Added Jest for testing both front and backend

This commit is contained in:
Jori Lallo 2016-09-09 01:35:39 -07:00
parent f4d1e62c13
commit 458735f341
21 changed files with 370 additions and 18 deletions

View File

@ -22,9 +22,14 @@
}
}
},
"env": {
"jest": true,
},
"globals": {
__DEV__: true,
SLACK_KEY: true,
SLACK_REDIRECT_URI: true,
afterAll: true
},
}

2
__mocks__/console.js Normal file
View File

@ -0,0 +1,2 @@
// Mock for node-uuid
global.console.warn = () => {};

7
__mocks__/ctx.js Normal file
View File

@ -0,0 +1,7 @@
const ctx = {
cache: {
set: () => {},
},
};
export default ctx;

1
__mocks__/fileMock.js Normal file
View File

@ -0,0 +1 @@
export default '';

2
__mocks__/styleMock.js Normal file
View File

@ -0,0 +1,2 @@
import idObj from 'identity-obj-proxy';
export default idObj;

1
__mocks__/window.js Normal file
View File

@ -0,0 +1 @@
window.matchMedia = (data) => data;

View File

@ -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>);
});

View File

@ -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>
`;

View File

@ -0,0 +1,10 @@
import renderer from 'react-test-renderer';
const snap = (children) => {
const component = renderer.create(children);
expect(component).toMatchSnapshot();
};
export {
snap,
};

View File

@ -1,7 +1,6 @@
{
"name": "BeautifulAtlas",
"version": "0.0.1",
"description": "For writing",
"private": true,
"main": "index.js",
"scripts": {
"clean": "rimraf dist",
@ -13,7 +12,29 @@
"lint": "eslint frontend",
"deploy": "git push heroku master",
"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": {
"node": "6.x"
@ -22,12 +43,6 @@
"type": "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": {
"babel-core": "6.13.2",
"babel-eslint": "6.1.2",
@ -99,10 +114,10 @@
"query-string": "^4.2.2",
"querystring": "0.2.0",
"randomstring": "1.1.5",
"react": "15.1.0",
"react-codemirror": "0.2.5",
"react": "15.3.1",
"react-codemirror": "0.2.6",
"react-dom": "15.1.0",
"react-dropzone": "3.3.2",
"react-dropzone": "3.6.0",
"react-helmet": "3.1.0",
"react-keydown": "^1.6.1",
"react-router": "2.5.1",
@ -125,11 +140,16 @@
"webpack": "1.13.2"
},
"devDependencies": {
"babel-jest": "^15.0.0",
"fetch-test-server": "^1.1.0",
"fsevents": "1.0.14",
"identity-obj-proxy": "^3.0.0",
"ignore-loader": "0.1.1",
"jest-cli": "^15.1.1",
"koa-webpack-dev-middleware": "1.2.0",
"koa-webpack-hot-middleware": "1.0.3",
"node-dev": "3.1.0",
"nodemon": "1.9.1"
"nodemon": "1.9.1",
"react-test-renderer": "^15.3.1"
}
}

10
server/.jest-config Normal file
View File

@ -0,0 +1,10 @@
{
"verbose": true,
"testPathDirs": [
"<rootDir>/server"
],
"setupFiles": [
"<rootDir>/__mocks__/console.js",
"./server/test/helper.js"
]
}

View File

@ -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"
}
}
`;

View File

@ -10,8 +10,6 @@ export default function auth({ require = true } = {}) {
return async function authMiddleware(ctx, next) {
let token;
console.log(ctx.body);
const authorizationHeader = ctx.request.get('authorization');
if (authorizationHeader) {
const parts = authorizationHeader.split(' ');

37
server/api/user.test.js Normal file
View File

@ -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();
});

View File

@ -2,7 +2,7 @@ import crypto from 'crypto';
import {
DataTypes,
sequelize,
encryptedFields
encryptedFields,
} from '../sequelize';
import JWT from 'jsonwebtoken';

View File

@ -1,5 +1,5 @@
import Sequelize from 'sequelize';
import _orderBy from 'lodash.orderby';
import _ from 'lodash';
import { Document, Atlas, User, Revision } from './models';
export function presentUser(ctx, user) {
@ -123,7 +123,7 @@ export function presentCollection(ctx, collection, includeRecentDocuments=false)
includeCollaborators: true,
}));
}));
data.recentDocuments = _orderBy(recentDocuments, ['updatedAt'], ['desc']);
data.recentDocuments = _.orderBy(recentDocuments, ['updatedAt'], ['desc']);
}
resolve(data);

View File

@ -0,0 +1,8 @@
exports[`test presents a user 1`] = `
Object {
"avatarUrl": "http://example.com/avatar.png",
"id": "123",
"name": "Test User",
"username": "testuser"
}
`;

15
server/presenters/user.js Normal file
View File

@ -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;

View File

@ -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();
});

30
server/test/helper.js Normal file
View File

@ -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();

29
server/test/support.js Normal file
View File

@ -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,
};