Added Jest for testing both front and backend
This commit is contained in:
parent
f4d1e62c13
commit
458735f341
|
@ -22,9 +22,14 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"env": {
|
||||||
|
"jest": true,
|
||||||
|
},
|
||||||
"globals": {
|
"globals": {
|
||||||
__DEV__: true,
|
__DEV__: true,
|
||||||
SLACK_KEY: true,
|
SLACK_KEY: true,
|
||||||
SLACK_REDIRECT_URI: true,
|
SLACK_REDIRECT_URI: true,
|
||||||
|
|
||||||
|
afterAll: true
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
// Mock for node-uuid
|
||||||
|
global.console.warn = () => {};
|
|
@ -0,0 +1,7 @@
|
||||||
|
const ctx = {
|
||||||
|
cache: {
|
||||||
|
set: () => {},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ctx;
|
|
@ -0,0 +1 @@
|
||||||
|
export default '';
|
|
@ -0,0 +1,2 @@
|
||||||
|
import idObj from 'identity-obj-proxy';
|
||||||
|
export default idObj;
|
|
@ -0,0 +1 @@
|
||||||
|
window.matchMedia = (data) => data;
|
|
@ -0,0 +1,29 @@
|
||||||
|
/* eslint-disable */
|
||||||
|
import React from 'react';
|
||||||
|
import { snap } from 'utils/testUtils';
|
||||||
|
|
||||||
|
import Alert from '.';
|
||||||
|
|
||||||
|
test('renders default as info', () => {
|
||||||
|
snap(<Alert>default</Alert>);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('renders success', () => {
|
||||||
|
snap(<Alert success>success</Alert>);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('renders info', () => {
|
||||||
|
snap(<Alert info>info</Alert>);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('renders warning', () => {
|
||||||
|
snap(<Alert warning>warning</Alert>);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('renders danger', () => {
|
||||||
|
snap(<Alert danger>danger</Alert>);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('renders offline', () => {
|
||||||
|
snap(<Alert offline>offline</Alert>);
|
||||||
|
});
|
|
@ -0,0 +1,113 @@
|
||||||
|
exports[`test renders danger 1`] = `
|
||||||
|
<div
|
||||||
|
className="Flex container danger"
|
||||||
|
style={
|
||||||
|
Object {
|
||||||
|
"WebkitAlignItems": "center",
|
||||||
|
"WebkitJustifyContent": "center",
|
||||||
|
"alignItems": "center",
|
||||||
|
"boxSizing": "border-box",
|
||||||
|
"display": "flex",
|
||||||
|
"justifyContent": "center",
|
||||||
|
"msAlignItems": "center",
|
||||||
|
"msJustifyContent": "center"
|
||||||
|
}
|
||||||
|
}>
|
||||||
|
danger
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`test renders default as info 1`] = `
|
||||||
|
<div
|
||||||
|
className="Flex container info"
|
||||||
|
style={
|
||||||
|
Object {
|
||||||
|
"WebkitAlignItems": "center",
|
||||||
|
"WebkitJustifyContent": "center",
|
||||||
|
"alignItems": "center",
|
||||||
|
"boxSizing": "border-box",
|
||||||
|
"display": "flex",
|
||||||
|
"justifyContent": "center",
|
||||||
|
"msAlignItems": "center",
|
||||||
|
"msJustifyContent": "center"
|
||||||
|
}
|
||||||
|
}>
|
||||||
|
default
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`test renders info 1`] = `
|
||||||
|
<div
|
||||||
|
className="Flex container info"
|
||||||
|
style={
|
||||||
|
Object {
|
||||||
|
"WebkitAlignItems": "center",
|
||||||
|
"WebkitJustifyContent": "center",
|
||||||
|
"alignItems": "center",
|
||||||
|
"boxSizing": "border-box",
|
||||||
|
"display": "flex",
|
||||||
|
"justifyContent": "center",
|
||||||
|
"msAlignItems": "center",
|
||||||
|
"msJustifyContent": "center"
|
||||||
|
}
|
||||||
|
}>
|
||||||
|
info
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`test renders offline 1`] = `
|
||||||
|
<div
|
||||||
|
className="Flex container offline"
|
||||||
|
style={
|
||||||
|
Object {
|
||||||
|
"WebkitAlignItems": "center",
|
||||||
|
"WebkitJustifyContent": "center",
|
||||||
|
"alignItems": "center",
|
||||||
|
"boxSizing": "border-box",
|
||||||
|
"display": "flex",
|
||||||
|
"justifyContent": "center",
|
||||||
|
"msAlignItems": "center",
|
||||||
|
"msJustifyContent": "center"
|
||||||
|
}
|
||||||
|
}>
|
||||||
|
offline
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`test renders success 1`] = `
|
||||||
|
<div
|
||||||
|
className="Flex container success"
|
||||||
|
style={
|
||||||
|
Object {
|
||||||
|
"WebkitAlignItems": "center",
|
||||||
|
"WebkitJustifyContent": "center",
|
||||||
|
"alignItems": "center",
|
||||||
|
"boxSizing": "border-box",
|
||||||
|
"display": "flex",
|
||||||
|
"justifyContent": "center",
|
||||||
|
"msAlignItems": "center",
|
||||||
|
"msJustifyContent": "center"
|
||||||
|
}
|
||||||
|
}>
|
||||||
|
success
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`test renders warning 1`] = `
|
||||||
|
<div
|
||||||
|
className="Flex container warning"
|
||||||
|
style={
|
||||||
|
Object {
|
||||||
|
"WebkitAlignItems": "center",
|
||||||
|
"WebkitJustifyContent": "center",
|
||||||
|
"alignItems": "center",
|
||||||
|
"boxSizing": "border-box",
|
||||||
|
"display": "flex",
|
||||||
|
"justifyContent": "center",
|
||||||
|
"msAlignItems": "center",
|
||||||
|
"msJustifyContent": "center"
|
||||||
|
}
|
||||||
|
}>
|
||||||
|
warning
|
||||||
|
</div>
|
||||||
|
`;
|
|
@ -0,0 +1,10 @@
|
||||||
|
import renderer from 'react-test-renderer';
|
||||||
|
|
||||||
|
const snap = (children) => {
|
||||||
|
const component = renderer.create(children);
|
||||||
|
expect(component).toMatchSnapshot();
|
||||||
|
};
|
||||||
|
|
||||||
|
export {
|
||||||
|
snap,
|
||||||
|
};
|
46
package.json
46
package.json
|
@ -1,7 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "BeautifulAtlas",
|
"name": "BeautifulAtlas",
|
||||||
"version": "0.0.1",
|
"private": true,
|
||||||
"description": "For writing",
|
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"clean": "rimraf dist",
|
"clean": "rimraf dist",
|
||||||
|
@ -13,7 +12,29 @@
|
||||||
"lint": "eslint frontend",
|
"lint": "eslint frontend",
|
||||||
"deploy": "git push heroku master",
|
"deploy": "git push heroku master",
|
||||||
"heroku-postbuild": "npm run build && npm run sequelize db:migrate",
|
"heroku-postbuild": "npm run build && npm run sequelize db:migrate",
|
||||||
"sequelize": "./node_modules/.bin/sequelize"
|
"sequelize": "./node_modules/.bin/sequelize",
|
||||||
|
"test": "npm run test:frontend && npm run test:server",
|
||||||
|
"test:frontend": "jest",
|
||||||
|
"test:watch": "jest --watch",
|
||||||
|
"test:server": "jest --config=server/.jest-config"
|
||||||
|
},
|
||||||
|
"jest": {
|
||||||
|
"verbose": false,
|
||||||
|
"testPathDirs": [
|
||||||
|
"frontend"
|
||||||
|
],
|
||||||
|
"moduleNameMapper": {
|
||||||
|
"^.*[.](s?css|css)$": "<rootDir>/__mocks__/styleMock.js",
|
||||||
|
"^.*[.](gif|ttf|eot|svg)$": "<rootDir>/__test__/fileMock.js"
|
||||||
|
},
|
||||||
|
"moduleFileExtensions": ["js", "jsx", "json"],
|
||||||
|
"moduleDirectories": ["node_modules", "server"],
|
||||||
|
"modulePaths": [
|
||||||
|
"frontend"
|
||||||
|
],
|
||||||
|
"setupFiles": [
|
||||||
|
"<rootDir>/__mocks__/window.js"
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "6.x"
|
"node": "6.x"
|
||||||
|
@ -22,12 +43,6 @@
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "git+ssh://git@github.com/jorilallo/atlas.git"
|
"url": "git+ssh://git@github.com/jorilallo/atlas.git"
|
||||||
},
|
},
|
||||||
"author": "Jori Lallo",
|
|
||||||
"license": "ISC",
|
|
||||||
"bugs": {
|
|
||||||
"url": "https://github.com/jorilallo/atlas/issues"
|
|
||||||
},
|
|
||||||
"homepage": "https://github.com/jorilallo/atlas#readme",
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"babel-core": "6.13.2",
|
"babel-core": "6.13.2",
|
||||||
"babel-eslint": "6.1.2",
|
"babel-eslint": "6.1.2",
|
||||||
|
@ -99,10 +114,10 @@
|
||||||
"query-string": "^4.2.2",
|
"query-string": "^4.2.2",
|
||||||
"querystring": "0.2.0",
|
"querystring": "0.2.0",
|
||||||
"randomstring": "1.1.5",
|
"randomstring": "1.1.5",
|
||||||
"react": "15.1.0",
|
"react": "15.3.1",
|
||||||
"react-codemirror": "0.2.5",
|
"react-codemirror": "0.2.6",
|
||||||
"react-dom": "15.1.0",
|
"react-dom": "15.1.0",
|
||||||
"react-dropzone": "3.3.2",
|
"react-dropzone": "3.6.0",
|
||||||
"react-helmet": "3.1.0",
|
"react-helmet": "3.1.0",
|
||||||
"react-keydown": "^1.6.1",
|
"react-keydown": "^1.6.1",
|
||||||
"react-router": "2.5.1",
|
"react-router": "2.5.1",
|
||||||
|
@ -125,11 +140,16 @@
|
||||||
"webpack": "1.13.2"
|
"webpack": "1.13.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"babel-jest": "^15.0.0",
|
||||||
|
"fetch-test-server": "^1.1.0",
|
||||||
"fsevents": "1.0.14",
|
"fsevents": "1.0.14",
|
||||||
|
"identity-obj-proxy": "^3.0.0",
|
||||||
"ignore-loader": "0.1.1",
|
"ignore-loader": "0.1.1",
|
||||||
|
"jest-cli": "^15.1.1",
|
||||||
"koa-webpack-dev-middleware": "1.2.0",
|
"koa-webpack-dev-middleware": "1.2.0",
|
||||||
"koa-webpack-hot-middleware": "1.0.3",
|
"koa-webpack-hot-middleware": "1.0.3",
|
||||||
"node-dev": "3.1.0",
|
"node-dev": "3.1.0",
|
||||||
"nodemon": "1.9.1"
|
"nodemon": "1.9.1",
|
||||||
|
"react-test-renderer": "^15.3.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
{
|
||||||
|
"verbose": true,
|
||||||
|
"testPathDirs": [
|
||||||
|
"<rootDir>/server"
|
||||||
|
],
|
||||||
|
"setupFiles": [
|
||||||
|
"<rootDir>/__mocks__/console.js",
|
||||||
|
"./server/test/helper.js"
|
||||||
|
]
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
exports[`test should require authentication 1`] = `
|
||||||
|
Object {
|
||||||
|
"message": "Authentication required"
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`test should return known user 1`] = `
|
||||||
|
Object {
|
||||||
|
"data": Object {
|
||||||
|
"avatarUrl": "http://example.com/avatar.png",
|
||||||
|
"id": "86fde1d4-0050-428f-9f0b-0bf77f8bdf61",
|
||||||
|
"name": "User 1",
|
||||||
|
"username": "user1"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
|
@ -10,8 +10,6 @@ export default function auth({ require = true } = {}) {
|
||||||
return async function authMiddleware(ctx, next) {
|
return async function authMiddleware(ctx, next) {
|
||||||
let token;
|
let token;
|
||||||
|
|
||||||
console.log(ctx.body);
|
|
||||||
|
|
||||||
const authorizationHeader = ctx.request.get('authorization');
|
const authorizationHeader = ctx.request.get('authorization');
|
||||||
if (authorizationHeader) {
|
if (authorizationHeader) {
|
||||||
const parts = authorizationHeader.split(' ');
|
const parts = authorizationHeader.split(' ');
|
||||||
|
|
|
@ -0,0 +1,37 @@
|
||||||
|
import TestServer from 'fetch-test-server';
|
||||||
|
|
||||||
|
import app from '..';
|
||||||
|
import { User } from '../models';
|
||||||
|
|
||||||
|
import { flushdb, seed, sequelize } from '../test/support';
|
||||||
|
|
||||||
|
const server = new TestServer(app.callback());
|
||||||
|
|
||||||
|
beforeEach(seed);
|
||||||
|
afterEach(flushdb);
|
||||||
|
afterAll(() => server.close());
|
||||||
|
afterAll(() => sequelize.close());
|
||||||
|
|
||||||
|
it('should return known user', async () => {
|
||||||
|
const user = await User.findOne({
|
||||||
|
where: {
|
||||||
|
email: 'user1@example.com',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const res = await server.post('/api/user.info', {
|
||||||
|
body: { token: user.getJwtToken() },
|
||||||
|
});
|
||||||
|
const body = await res.json();
|
||||||
|
|
||||||
|
expect(res.status).toEqual(200);
|
||||||
|
expect(body).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should require authentication', async () => {
|
||||||
|
const res = await server.post('/api/user.info');
|
||||||
|
const body = await res.json();
|
||||||
|
|
||||||
|
expect(res.status).toEqual(401);
|
||||||
|
expect(body).toMatchSnapshot();
|
||||||
|
});
|
|
@ -2,7 +2,7 @@ import crypto from 'crypto';
|
||||||
import {
|
import {
|
||||||
DataTypes,
|
DataTypes,
|
||||||
sequelize,
|
sequelize,
|
||||||
encryptedFields
|
encryptedFields,
|
||||||
} from '../sequelize';
|
} from '../sequelize';
|
||||||
|
|
||||||
import JWT from 'jsonwebtoken';
|
import JWT from 'jsonwebtoken';
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import Sequelize from 'sequelize';
|
import Sequelize from 'sequelize';
|
||||||
import _orderBy from 'lodash.orderby';
|
import _ from 'lodash';
|
||||||
import { Document, Atlas, User, Revision } from './models';
|
import { Document, Atlas, User, Revision } from './models';
|
||||||
|
|
||||||
export function presentUser(ctx, user) {
|
export function presentUser(ctx, user) {
|
||||||
|
@ -123,7 +123,7 @@ export function presentCollection(ctx, collection, includeRecentDocuments=false)
|
||||||
includeCollaborators: true,
|
includeCollaborators: true,
|
||||||
}));
|
}));
|
||||||
}));
|
}));
|
||||||
data.recentDocuments = _orderBy(recentDocuments, ['updatedAt'], ['desc']);
|
data.recentDocuments = _.orderBy(recentDocuments, ['updatedAt'], ['desc']);
|
||||||
}
|
}
|
||||||
|
|
||||||
resolve(data);
|
resolve(data);
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
exports[`test presents a user 1`] = `
|
||||||
|
Object {
|
||||||
|
"avatarUrl": "http://example.com/avatar.png",
|
||||||
|
"id": "123",
|
||||||
|
"name": "Test User",
|
||||||
|
"username": "testuser"
|
||||||
|
}
|
||||||
|
`;
|
|
@ -0,0 +1,15 @@
|
||||||
|
const presentUser = (ctx, user) => {
|
||||||
|
ctx.cache.set(user.id, user);
|
||||||
|
|
||||||
|
return new Promise(async (resolve, _reject) => {
|
||||||
|
const data = {
|
||||||
|
id: user.id,
|
||||||
|
name: user.name,
|
||||||
|
username: user.username,
|
||||||
|
avatarUrl: user.slackData.image_192,
|
||||||
|
};
|
||||||
|
resolve(data);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export default presentUser;
|
|
@ -0,0 +1,19 @@
|
||||||
|
import presentUser from './user';
|
||||||
|
|
||||||
|
import ctx from '../../__mocks__/ctx';
|
||||||
|
|
||||||
|
it('presents a user', async () => {
|
||||||
|
const user = await presentUser(
|
||||||
|
ctx,
|
||||||
|
{
|
||||||
|
id: '123',
|
||||||
|
name: 'Test User',
|
||||||
|
username: 'testuser',
|
||||||
|
slackData: {
|
||||||
|
image_192: 'http://example.com/avatar.png',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(user).toMatchSnapshot();
|
||||||
|
});
|
|
@ -0,0 +1,30 @@
|
||||||
|
require('localenv');
|
||||||
|
|
||||||
|
// test environment variables
|
||||||
|
process.env.DATABASE_URL = process.env.DATABASE_URL_TEST;
|
||||||
|
process.env.NODE_ENV = 'test';
|
||||||
|
|
||||||
|
const Sequelize = require('sequelize');
|
||||||
|
const sequelize = require('../sequelize').sequelize;
|
||||||
|
const Umzug = require('umzug');
|
||||||
|
|
||||||
|
const queryInterface = sequelize.getQueryInterface();
|
||||||
|
|
||||||
|
function runMigrations() {
|
||||||
|
const umzug = new Umzug({
|
||||||
|
storage: 'sequelize',
|
||||||
|
storageOptions: {
|
||||||
|
sequelize,
|
||||||
|
},
|
||||||
|
migrations: {
|
||||||
|
params: [queryInterface, Sequelize],
|
||||||
|
path: './server/migrations',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
umzug.up()
|
||||||
|
.then(() => {
|
||||||
|
sequelize.close();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
runMigrations();
|
|
@ -0,0 +1,29 @@
|
||||||
|
import { User } from '../models';
|
||||||
|
import { sequelize } from '../sequelize';
|
||||||
|
|
||||||
|
export function flushdb() {
|
||||||
|
const sql = sequelize.getQueryInterface();
|
||||||
|
const tables = Object.keys(sequelize.models).map((model) =>
|
||||||
|
sql.quoteTable(sequelize.models[model].getTableName()));
|
||||||
|
const query = `TRUNCATE ${tables.join(', ')} CASCADE`;
|
||||||
|
|
||||||
|
return sequelize.query(query);
|
||||||
|
}
|
||||||
|
|
||||||
|
const seed = async () => {
|
||||||
|
await User.create({
|
||||||
|
id: '86fde1d4-0050-428f-9f0b-0bf77f8bdf61',
|
||||||
|
email: 'user1@example.com',
|
||||||
|
username: 'user1',
|
||||||
|
name: 'User 1',
|
||||||
|
slackId: '123',
|
||||||
|
slackData: {
|
||||||
|
image_192: 'http://example.com/avatar.png',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export {
|
||||||
|
seed,
|
||||||
|
sequelize,
|
||||||
|
};
|
Reference in New Issue