Refactor testing to use jest

The old system, which uses PhantomJS and Karma, prevents us from testing
i18n-enabled components.  Thus, we're refactoring the code to use
jest/enzyme/jsdom.  This should also make tests run faster.

Depends-On: I407743cbef8109455fa84ef901acee8d177ee2a4
Implements: blueprint tripleo-ui-replace-phantomjs
Co-Authored-By: Honza Pokorny <honza@redhat.com>
Closes-Bug: #1655026
Change-Id: Id6ad9898922c836bbda1c3c4c3147c137d301245
This commit is contained in:
Jiri Tomasek 2017-03-02 08:19:22 +01:00 committed by Honza Pokorny
parent dd3576941d
commit 06dee4582f
25 changed files with 95 additions and 170 deletions

View File

@ -0,0 +1,12 @@
// This is a custom Jest transformer turning style imports into empty objects.
// http://facebook.github.io/jest/docs/tutorial-webpack.html
module.exports = {
process() {
return 'module.exports = {};';
},
getCacheKey(fileData, filename) {
// The output is always the same.
return 'cssTransform';
},
};

View File

@ -0,0 +1,10 @@
const path = require('path');
// This is a custom Jest transformer turning file imports into filenames.
// http://facebook.github.io/jest/docs/tutorial-webpack.html
module.exports = {
process(src, filename) {
return 'module.exports = ' + JSON.stringify(path.basename(filename)) + ';';
}
};

View File

@ -1,45 +0,0 @@
var webpackConfig = require('./webpack.config.js');
module.exports = function(config) {
config.set({
// Add any browsers here
// browsers: ['PhantomJS', 'Chrome'], // if you have karma-chrome-launcher installed
browsers: ['PhantomJS'],
frameworks: ['jasmine'],
// The entry point for our test suite
basePath: 'src',
autoWatch: true,
files: ['webpack.tests.js'],
preprocessors: {
// Run this through webpack, and enable inline sourcemaps
'webpack.tests.js': ['webpack', 'sourcemap']
},
webpack: webpackConfig,
client: {
// log console output in our test console
captureConsole: true
},
reporters: ['dots', 'junit'],
singleRun: false, // exit after tests have completed
junitReporter: {
outputDir: '../',
outputFile: 'tests_results.xml',
suite: '', // suite will become the package name attribute in xml testsuite element
useBrowserName: false // add browser name to report and classes names
},
webpackMiddleware: {
noInfo: true
},
// Webpack takes a little while to compile -- this manifests as a really
// long load time while webpack blocks on serving the request.
browserNoActivityTimeout: 60000 // 60 seconds
});
};

View File

@ -74,12 +74,24 @@
"build": "webpack --bail",
"lint": "eslint --max-warnings 0 src",
"start": "webpack-dev-server --progress",
"test": "karma start --single-run",
"test:watch": "karma start",
"test": "jest",
"test:watch": "jest --watchAll",
"json2pot": "rip json2pot ./i18n/extracted-messages/**/*.json -o ./i18n/messages.pot",
"po2json": "rip po2json -m ./i18n/extracted-messages/**/*.json",
"docs": "cd docs && make html && cd -"
},
"jest": {
"testURL": "http://localhost",
"testMatch": ["**/__tests__/**/*.tests.js"],
"transform": {
"^.+\\.(js|jsx)$": "<rootDir>/node_modules/babel-jest",
"^.+\\.css$": "<rootDir>/config/jest/cssTransform.js",
"^(?!.*\\.(js|jsx|css|json)$)": "<rootDir>/config/jest/fileTransform.js"
},
"transformIgnorePatterns": [
"[/\\\\]node_modules[/\\\\].+\\.(js|jsx)$"
]
},
"repository": {
"type": "git",
"url": "https://github.com/openstack/tripleo-ui"

View File

@ -0,0 +1,5 @@
---
fixes:
- |
Fixes `bug 1655026 <https://bugs.launchpad.net/tripleo/+bug/1655026>`__
Replace karma-based tests with jest/enzyme; remove phantomjs dependency

View File

@ -8,7 +8,9 @@ import MistralApiService from '../../js/services/MistralApiService';
import MistralConstants from '../../js/constants/MistralConstants';
import { mockGetIntl } from './utils';
import { normalizeParameters } from '../../js/actions/ParametersActions';
import storage from '../mocks/storage';
window.localStorage = window.sessionStorage = storage;
// Use this to mock asynchronous functions which return a promise.
// The promise will immediately resolve with `data`.
@ -131,12 +133,10 @@ describe('ParametersActions', () => {
};
beforeEach(done => {
spyOn(ParametersActions, 'updateParametersPending');
spyOn(ParametersActions, 'updateParametersSuccess');
spyOn(ParametersActions, 'updateParametersFailed');
// Mock the service call.
spyOn(MistralApiService, 'runAction')
.and.callFake(createRejectingPromise(error));
jest.spyOn(ParametersActions, 'updateParametersPending');
jest.spyOn(ParametersActions, 'updateParametersSuccess');
jest.spyOn(ParametersActions, 'updateParametersFailed');
jest.spyOn(MistralApiService, 'runAction').mockImplementation(createRejectingPromise(error));
// Call the action creator and the resulting action.
// In this case, dispatch and getState are just empty placeHolders.
ParametersActions.updateParameters('overcloud', { foo: 'bar' })(

View File

@ -5,7 +5,9 @@ import MistralApiService from '../../js/services/MistralApiService';
import { mockGetIntl } from './utils';
import PlansActions from '../../js/actions/PlansActions';
import SwiftApiService from '../../js/services/SwiftApiService';
import storage from '../mocks/storage';
window.localStorage = window.sessionStorage = storage;
// Use these to mock asynchronous functions which return a promise.
// The promise will immediately resolve/reject with `data`.

View File

@ -0,0 +1,12 @@
export default {
getItem: function (key) {
return this[key];
},
setItem: function (key, value) {
this[key] = value;
},
removeItem: function(key) {
delete this[key];
return;
}
};

View File

@ -1,16 +1,9 @@
import matchers from 'jasmine-immutable-matchers';
import { CurrentPlanState } from '../../js/immutableRecords/currentPlan';
import currentPlanReducer from '../../js/reducers/currentPlanReducer';
import CurrentPlanActions from '../../js/actions/CurrentPlanActions';
describe('plansReducer state', () => {
beforeEach(() => {
jasmine.addMatchers(matchers);
});
describe('default state', () => {
let state;

View File

@ -1,14 +1,9 @@
import matchers from 'jasmine-immutable-matchers';
import { Map, Set } from 'immutable';
import NodesConstants from '../../js/constants/NodesConstants';
import nodesReducer from '../../js/reducers/nodesReducer';
describe('nodesReducer', () => {
beforeEach(() => {
jasmine.addMatchers(matchers);
});
const initialState = Map({
isFetching: false,
nodesInProgress: Set(),
@ -69,7 +64,7 @@ describe('nodesReducer', () => {
payload: 'uuid1'
};
const newState = nodesReducer(initialState, action);
expect(newState.get('nodesInProgress')).toEqualImmutable(Set(['uuid1']));
expect(newState.get('nodesInProgress')).toEqual(Set(['uuid1']));
});
it('should handle UPDATE_NODE_SUCCESS', () => {
@ -83,10 +78,10 @@ describe('nodesReducer', () => {
}
};
const newState = nodesReducer(updatedState, action);
expect(newState.get('all')).toEqualImmutable(
expect(newState.get('all')).toEqual(
updatedNodeState.get('all')
);
expect(newState.get('nodesInProgress')).toEqualImmutable(Set());
expect(newState.get('nodesInProgress')).toEqual(Set());
});
it('should handle UPDATE_NODE_FAILED', () => {
@ -95,8 +90,8 @@ describe('nodesReducer', () => {
payload: 'uuid1'
};
const newState = nodesReducer(updatedState, action);
expect(newState).toEqualImmutable(updatedState);
expect(newState.get('nodesInProgress')).toEqualImmutable(Set());
expect(newState).toEqual(updatedState);
expect(newState.get('nodesInProgress')).toEqual(Set());
});
it('should handle DELETE_NODE_SUCCESS', () => {
@ -106,7 +101,7 @@ describe('nodesReducer', () => {
};
const newState = nodesReducer(updatedState, action);
expect(newState.get('all').size).toEqual(1);
expect(newState.get('nodesInProgress')).toEqualImmutable(Set());
expect(newState.get('nodesInProgress')).toEqual(Set());
});
it('should handle DELETE_NODE_FAILED', () => {
@ -115,8 +110,8 @@ describe('nodesReducer', () => {
payload: 'uuid1'
};
const newState = nodesReducer(updatedState, action);
expect(newState).toEqualImmutable(updatedState);
expect(newState.get('nodesInProgress')).toEqualImmutable(Set());
expect(newState).toEqual(updatedState);
expect(newState.get('nodesInProgress')).toEqual(Set());
});
it('should handle ADD_NODES action', () => {
@ -133,6 +128,6 @@ describe('nodesReducer', () => {
payload: registeredNodes
};
const newState = nodesReducer(initialState, action);
expect(newState).toEqualImmutable(updatedState);
expect(newState).toEqual(updatedState);
});
});

View File

@ -1,4 +1,3 @@
import matchers from 'jasmine-immutable-matchers';
import { Map } from 'immutable';
import { Notification } from '../../js/immutableRecords/notifications';
@ -6,10 +5,6 @@ import NotificationConstants from '../../js/constants/NotificationConstants';
import notificationsReducer from '../../js/reducers/notificationsReducer';
describe('notificationsReducer', () => {
beforeEach(() => {
jasmine.addMatchers(matchers);
});
const initialState = Map({ all: Map() });
const testNotification = new Notification({
title: 'Title',
@ -27,7 +22,7 @@ describe('notificationsReducer', () => {
type: NotificationConstants.NOTIFY,
payload: testNotification
};
expect(notificationsReducer(initialState, action)).toEqualImmutable(
expect(notificationsReducer(initialState, action)).toEqual(
Map({
all: Map({
someId: testNotification
@ -46,7 +41,7 @@ describe('notificationsReducer', () => {
someId: testNotification
})
});
expect(notificationsReducer(testState, action)).toEqualImmutable(
expect(notificationsReducer(testState, action)).toEqual(
Map({
all: Map()
})
@ -63,7 +58,7 @@ describe('notificationsReducer', () => {
someId: testNotification
})
});
expect(notificationsReducer(testState, action)).toEqualImmutable(
expect(notificationsReducer(testState, action)).toEqual(
Map({
all: Map({
someId: new Notification({

View File

@ -1,4 +1,3 @@
import matchers from 'jasmine-immutable-matchers';
import { List, Map } from 'immutable';
import { ParametersDefaultState, Parameter } from '../../js/immutableRecords/parameters';
@ -45,10 +44,6 @@ const parametersActionPayload = {
};
describe('parametersReducer', () => {
beforeEach(() => {
jasmine.addMatchers(matchers);
});
describe('FETCH_PARAMETERS_PENDING', () => {
let state;
const action = {
@ -67,7 +62,7 @@ describe('parametersReducer', () => {
});
it('resets form', () => {
expect(state.form).toEqualImmutable(Map({
expect(state.form).toEqual(Map({
formErrors: List(),
formFieldErrors: Map()
}));
@ -93,7 +88,7 @@ describe('parametersReducer', () => {
});
it('resets form', () => {
expect(state.form).toEqualImmutable(Map({
expect(state.form).toEqual(Map({
formErrors: List(),
formFieldErrors: Map()
}));
@ -183,7 +178,7 @@ describe('parametersReducer', () => {
});
it('resets form', () => {
expect(state.form).toEqualImmutable(Map({
expect(state.form).toEqual(Map({
formErrors: List(),
formFieldErrors: Map()
}));

View File

@ -1,4 +1,3 @@
import matchers from 'jasmine-immutable-matchers';
import { List, Map } from 'immutable';
import { InitialPlanState, Plan, PlanFile } from '../../js/immutableRecords/plans';
@ -8,10 +7,6 @@ import plansReducer from '../../js/reducers/plansReducer';
describe('plansReducer state', () => {
beforeEach(() => {
jasmine.addMatchers(matchers);
});
describe('default state', () => {
let state;
@ -113,7 +108,7 @@ describe('plansReducer state', () => {
});
it('updates the plan records `files` attributes', () => {
expect(plan.get('files')).toEqualImmutable(Map({
expect(plan.get('files')).toEqual(Map({
'capabilities_map.yaml': new PlanFile({ name: 'capabilities_map.yaml' }),
'foo.yaml': new PlanFile({ name: 'foo.yaml' })
}));
@ -143,7 +138,7 @@ describe('plansReducer state', () => {
newState,
PlansActions.deletePlanSuccess('somecloud')
);
expect(newState.get('all')).toEqualImmutable(Map({
expect(newState.get('all')).toEqual(Map({
overcloud: new Plan({ name: 'overcloud' })
}));
});

View File

@ -1,4 +1,3 @@
import matchers from 'jasmine-immutable-matchers';
import { Map } from 'immutable';
import { Role } from '../../js/immutableRecords/roles';
@ -7,10 +6,6 @@ import RolesConstants from '../../js/constants/RolesConstants';
import rolesReducer from '../../js/reducers/rolesReducer';
describe('rolesReducer', () => {
beforeEach(() => {
jasmine.addMatchers(matchers);
});
const initialState = Map({
loaded: false,
isFetching: false,
@ -47,7 +42,7 @@ describe('rolesReducer', () => {
payload: ['Controller']
};
const newState = rolesReducer(initialState, action);
expect(newState.get('roles')).toEqualImmutable(
expect(newState.get('roles')).toEqual(
updatedState.get('roles')
);
expect(newState.get('loaded')).toEqual(true);

View File

@ -1,5 +1,4 @@
import { Map, OrderedMap } from 'immutable';
import matchers from 'jasmine-immutable-matchers';
import { StacksState, Stack } from '../../js/immutableRecords/stacks';
import StacksActions from '../../js/actions/StacksActions';
@ -7,11 +6,6 @@ import stacksReducer from '../../js/reducers/stacksReducer';
describe('stacksReducer state', () => {
beforeEach(() => {
jasmine.addMatchers(matchers);
});
describe('default state', () => {
let state;
@ -61,7 +55,7 @@ describe('stacksReducer state', () => {
});
it('sets stacks in state', () => {
expect(state.stacks).toEqualImmutable(Map({
expect(state.stacks).toEqual(Map({
overcloud: new Stack({
creation_time: undefined,
deletion_time: undefined,
@ -96,7 +90,7 @@ describe('stacksReducer state', () => {
});
it('sets stacks in state to an empty Map', () => {
expect(state.stacks).toEqualImmutable(Map());
expect(state.stacks).toEqual(Map());
});
});
});

View File

@ -1,4 +1,3 @@
import matchers from 'jasmine-immutable-matchers';
import { List, Map } from 'immutable';
import { Validation } from '../../js/immutableRecords/validations';
@ -6,10 +5,6 @@ import ValidationsConstants from '../../js/constants/ValidationsConstants';
import validationsReducer from '../../js/reducers/validationsReducer';
describe('validationsReducer', () => {
beforeEach(() => {
jasmine.addMatchers(matchers);
});
const initialState = Map({
validationsLoaded: false,
isFetching: false,
@ -76,7 +71,7 @@ describe('validationsReducer', () => {
}
};
const newState = validationsReducer(initialState, action);
expect(newState.get('validations')).toEqualImmutable(
expect(newState.get('validations')).toEqual(
updatedState.get('validations')
);
});

View File

@ -1,4 +1,3 @@
import matchers from 'jasmine-immutable-matchers';
import { Map, OrderedMap } from 'immutable';
import { WorkflowExecution } from '../../js/immutableRecords/workflowExecutions';
@ -6,11 +5,10 @@ import WorkflowExecutionsConstants from '../../js/constants/WorkflowExecutionsCo
import workflowExecutionsReducer from '../../js/reducers/workflowExecutionsReducer';
import MistralConstants from '../../js/constants/MistralConstants';
describe('workflowExecutionsReducer', () => {
beforeEach(() => {
jasmine.addMatchers(matchers);
});
const updatedAt = '1970-01-01T00:00:01Z';
const updatedAtEpoch = Date.parse(updatedAt);
describe('workflowExecutionsReducer', () => {
const initialState = Map({
executionsLoaded: false,
isFetching: false,
@ -35,7 +33,7 @@ describe('workflowExecutionsReducer', () => {
state: 'SUCCESS',
state_info: '',
task_execution_id: null,
updated_at: NaN,
updated_at: updatedAtEpoch,
workflow_id: 'f8b280bb-5ba2-486b-9384-ddd79300d987',
workflow_name: MistralConstants.VALIDATIONS_RUN
})
@ -69,14 +67,14 @@ describe('workflowExecutionsReducer', () => {
state: 'SUCCESS',
state_info: '',
task_execution_id: null,
updated_at: '2016-07-18 14:05:08',
updated_at: updatedAt,
workflow_id: 'f8b280bb-5ba2-486b-9384-ddd79300d987',
workflow_name: MistralConstants.VALIDATIONS_RUN
}
}
};
const newState = workflowExecutionsReducer(initialState, action);
expect(newState.get('executions')).toEqualImmutable(updatedState.get('executions'));
expect(newState.get('executions')).toEqual(updatedState.get('executions'));
});
it('should handle FETCH_WORKFLOW_EXECUTIONS_FAILED', () => {
@ -102,13 +100,13 @@ describe('workflowExecutionsReducer', () => {
state: 'SUCCESS',
state_info: '',
task_execution_id: null,
updated_at: '2016-07-18 14:05:08',
updated_at: updatedAt,
workflow_id: 'f8b280bb-5ba2-486b-9384-ddd79300d987',
workflow_name: MistralConstants.VALIDATIONS_RUN
}
};
const newState = workflowExecutionsReducer(initialState, action);
expect(newState.get('executions')).toEqualImmutable(updatedState.get('executions'));
expect(newState.get('executions')).toEqual(updatedState.get('executions'));
});
it('should handle UPDATE_WORKFLOW_EXECUTION_PENDING', () => {

View File

@ -1,15 +1,10 @@
import { List, Map, OrderedMap } from 'immutable';
import matchers from 'jasmine-immutable-matchers';
import * as selectors from '../../js/selectors/environmentConfiguration';
import { Environment,
EnvironmentConfigurationState } from '../../js/immutableRecords/environmentConfiguration';
describe('Environment Configuration selectors', () => {
beforeEach(() => {
jasmine.addMatchers(matchers);
});
const state = {
environmentConfiguration: new EnvironmentConfigurationState({
loaded: true,
@ -47,7 +42,7 @@ describe('Environment Configuration selectors', () => {
};
it('provides selector to get enabled environments', () => {
expect(selectors.getEnabledEnvironments(state)).toEqualImmutable(OrderedMap({
expect(selectors.getEnabledEnvironments(state)).toEqual(OrderedMap({
'environments/environment1.yaml': new Environment({
file: 'environments/environment1.yaml',
title: 'Environment1',
@ -62,7 +57,7 @@ describe('Environment Configuration selectors', () => {
it(`provides selector to get nested tree of Environment Configuration Topics,
Environment Groups and Environments`, () => {
expect(selectors.getTopicsTree(state)).toEqualImmutable(Map({
expect(selectors.getTopicsTree(state)).toEqual(Map({
Topic1: Map({
title: 'Topic1',
environment_groups: List([

View File

@ -1,14 +1,9 @@
import { fromJS, Map } from 'immutable';
import matchers from 'jasmine-immutable-matchers';
import * as selectors from '../../js/selectors/nodes';
import { Port } from '../../js/immutableRecords/nodes';
describe('Nodes selectors', () => {
beforeEach(() => {
jasmine.addMatchers(matchers);
});
const state = {
nodes: Map({
isFetching: false,

View File

@ -1,16 +1,10 @@
import { List, Map } from 'immutable';
import matchers from 'jasmine-immutable-matchers';
import * as selectors from '../../js/selectors/parameters';
// import * as rolesSelectors from '../../js/selectors/roles';
import { Resource, Parameter, ParametersDefaultState } from '../../js/immutableRecords/parameters';
import { Role } from '../../js/immutableRecords/roles';
describe(' validations selectors', () => {
beforeEach(() => {
jasmine.addMatchers(matchers);
});
const state = {
roles: Map({
roles: Map({

View File

@ -1,5 +1,4 @@
import { Map } from 'immutable';
import matchers from 'jasmine-immutable-matchers';
import { Plan } from '../../js/immutableRecords/plans';
import { getAllPlansButCurrent } from '../../js/selectors/plans';
@ -7,10 +6,6 @@ import { InitialPlanState } from '../../js/immutableRecords/plans';
import { CurrentPlanState } from '../../js/immutableRecords/currentPlan';
describe('plans selectors', () => {
beforeEach(() => {
jasmine.addMatchers(matchers);
});
describe('getAllPlansButCurrent()', () => {
const state = {
plans: new InitialPlanState({

View File

@ -1,14 +1,9 @@
import { List, Map, OrderedMap } from 'immutable';
import matchers from 'jasmine-immutable-matchers';
import * as selectors from '../../js/selectors/registerNodes';
import { NodeToRegister, IronicNode } from '../../js/immutableRecords/nodes';
describe('registerNodes selectors', () => {
beforeEach(() => {
jasmine.addMatchers(matchers);
});
const state = {
registerNodes: Map({
selectedNodeId: undefined,
@ -64,6 +59,6 @@ describe('registerNodes selectors', () => {
disk: undefined
})
});
expect(selectors.getIronicNodesfromNodesToRegister(state)).toEqualImmutable(expectedNodesList);
expect(selectors.getIronicNodesfromNodesToRegister(state)).toEqual(expectedNodesList);
});
});

View File

@ -1,5 +1,4 @@
import { Map } from 'immutable';
import matchers from 'jasmine-immutable-matchers';
import { getCurrentStackDeploymentInProgress,
getCurrentStack } from '../../js/selectors/stacks';
@ -7,10 +6,6 @@ import { CurrentPlanState } from '../../js/immutableRecords/currentPlan';
import { Stack, StacksState } from '../../js/immutableRecords/stacks';
describe('stacks selectors', () => {
beforeEach(() => {
jasmine.addMatchers(matchers);
});
describe('getCurrentStack()', () => {
const state = {
stacks: new StacksState({
@ -25,7 +20,7 @@ describe('stacks selectors', () => {
};
it('returns a stack based on the currentPlanName', () => {
expect(getCurrentStack(state)).toEqualImmutable(
expect(getCurrentStack(state)).toEqual(
Stack({ stack_name: 'overcloud', stack_status: 'CREATE_COMPLETE' })
);
});

View File

@ -1,5 +1,4 @@
import { List, Map, OrderedMap } from 'immutable';
import matchers from 'jasmine-immutable-matchers';
import * as selectors from '../../js/selectors/validations';
import { Validation } from '../../js/immutableRecords/validations';
@ -8,10 +7,6 @@ import { WorkflowExecution } from '../../js/immutableRecords/workflowExecutions'
import MistralConstants from '../../js/constants/MistralConstants';
describe(' validations selectors', () => {
beforeEach(() => {
jasmine.addMatchers(matchers);
});
const state = {
currentPlan: new CurrentPlanState({
conflict: undefined,
@ -98,7 +93,7 @@ describe(' validations selectors', () => {
it('provides selector to get validation executions for current plan', () => {
expect(selectors.getValidationExecutionsForCurrentPlan(state).size).toEqual(2);
expect(selectors.getValidationExecutionsForCurrentPlan(state))
.toEqualImmutable(state.executions.get('executions'));
.toEqual(state.executions.get('executions'));
});
it('provides selector to get validation combined with its results', () => {

View File

@ -1,7 +1,6 @@
import { getAuthTokenId, getServiceUrl } from '../../js/services/utils';
import { InitialLoginState } from '../../js/immutableRecords/login';
import { List, Map } from 'immutable';
import matchers from 'jasmine-immutable-matchers';
import store from '../../js/store';
describe('utility functions', () => {
@ -51,7 +50,6 @@ describe('utility functions', () => {
};
beforeEach(() => {
jasmine.addMatchers(matchers);
spyOn(store, 'getState').and.returnValue(appState);
});