1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
|
/* eslint-disable jasmine/no-global-setup */
import $ from 'jquery';
import 'vendor/jasmine-jquery';
import '~/commons';
import Vue from 'vue';
import VueResource from 'vue-resource';
import Translate from '~/vue_shared/translate';
import { getDefaultAdapter } from '~/lib/utils/axios_utils';
import { FIXTURES_PATH, TEST_HOST } from './test_constants';
import customMatchers from './matchers';
const isHeadlessChrome = /\bHeadlessChrome\//.test(navigator.userAgent);
Vue.config.devtools = !isHeadlessChrome;
Vue.config.productionTip = false;
let hasVueWarnings = false;
Vue.config.warnHandler = (msg, vm, trace) => {
hasVueWarnings = true;
fail(`${msg}${trace}`);
};
let hasVueErrors = false;
Vue.config.errorHandler = function(err) {
hasVueErrors = true;
fail(err);
};
Vue.use(VueResource);
Vue.use(Translate);
// enable test fixtures
jasmine.getFixtures().fixturesPath = FIXTURES_PATH;
jasmine.getJSONFixtures().fixturesPath = FIXTURES_PATH;
beforeAll(() => jasmine.addMatchers(customMatchers));
// globalize common libraries
window.$ = window.jQuery = $;
// stub expected globals
window.gl = window.gl || {};
window.gl.TEST_HOST = TEST_HOST;
window.gon = window.gon || {};
window.gon.test_env = true;
gon.relative_url_root = '';
let hasUnhandledPromiseRejections = false;
window.addEventListener('unhandledrejection', event => {
hasUnhandledPromiseRejections = true;
console.error('Unhandled promise rejection:');
console.error(event.reason.stack || event.reason);
});
// HACK: Chrome 59 disconnects if there are too many synchronous tests in a row
// because it appears to lock up the thread that communicates to Karma's socket
// This async beforeEach gets called on every spec and releases the JS thread long
// enough for the socket to continue to communicate.
// The downside is that it creates a minor performance penalty in the time it takes
// to run our unit tests.
beforeEach(done => done());
const builtinVueHttpInterceptors = Vue.http.interceptors.slice();
beforeEach(() => {
// restore interceptors so we have no remaining ones from previous tests
Vue.http.interceptors = builtinVueHttpInterceptors.slice();
});
const axiosDefaultAdapter = getDefaultAdapter();
let testFiles = process.env.TEST_FILES || [];
if (testFiles.length > 0) {
testFiles = testFiles.map(path => path.replace(/^spec\/javascripts\//, '').replace(/\.js$/, ''));
console.log(`Running only tests matching: ${testFiles}`);
} else {
console.log('Running all tests');
}
// render all of our tests
const testsContext = require.context('.', true, /_spec$/);
testsContext.keys().forEach(function(path) {
try {
if (testFiles.length === 0 || testFiles.some(p => path.includes(p))) {
testsContext(path);
}
} catch (err) {
console.error('[ERROR] Unable to load spec: ', path);
describe('Test bundle', function() {
it(`includes '${path}'`, function() {
expect(err).toBeNull();
});
});
}
});
describe('test errors', () => {
beforeAll(done => {
if (hasUnhandledPromiseRejections || hasVueWarnings || hasVueErrors) {
setTimeout(done, 1000);
} else {
done();
}
});
it('has no unhandled Promise rejections', () => {
expect(hasUnhandledPromiseRejections).toBe(false);
});
it('has no Vue warnings', () => {
expect(hasVueWarnings).toBe(false);
});
it('has no Vue error', () => {
expect(hasVueErrors).toBe(false);
});
it('restores axios adapter after mocking', () => {
if (getDefaultAdapter() !== axiosDefaultAdapter) {
fail('axios adapter is not restored! Did you forget a restore() on MockAdapter?');
}
});
});
// if we're generating coverage reports, make sure to include all files so
// that we can catch files with 0% coverage
// see: https://github.com/deepsweet/istanbul-instrumenter-loader/issues/15
if (process.env.BABEL_ENV === 'coverage') {
// exempt these files from the coverage report
const troubleMakers = [
'./blob_edit/blob_bundle.js',
'./boards/components/modal/empty_state.js',
'./boards/components/modal/footer.js',
'./boards/components/modal/header.js',
'./cycle_analytics/cycle_analytics_bundle.js',
'./cycle_analytics/components/stage_plan_component.js',
'./cycle_analytics/components/stage_staging_component.js',
'./cycle_analytics/components/stage_test_component.js',
'./commit/pipelines/pipelines_bundle.js',
'./diff_notes/diff_notes_bundle.js',
'./diff_notes/components/jump_to_discussion.js',
'./diff_notes/components/resolve_count.js',
'./dispatcher.js',
'./environments/environments_bundle.js',
'./graphs/graphs_bundle.js',
'./issuable/time_tracking/time_tracking_bundle.js',
'./main.js',
'./merge_conflicts/merge_conflicts_bundle.js',
'./merge_conflicts/components/inline_conflict_lines.js',
'./merge_conflicts/components/parallel_conflict_lines.js',
'./monitoring/monitoring_bundle.js',
'./network/network_bundle.js',
'./network/branch_graph.js',
'./profile/profile_bundle.js',
'./protected_branches/protected_branches_bundle.js',
'./snippet/snippet_bundle.js',
'./terminal/terminal_bundle.js',
'./users/users_bundle.js',
'./issue_show/index.js',
];
describe('Uncovered files', function() {
const sourceFiles = require.context('~', true, /\.js$/);
$.holdReady(true);
sourceFiles.keys().forEach(function(path) {
// ignore if there is a matching spec file
if (testsContext.keys().indexOf(`${path.replace(/\.js$/, '')}_spec`) > -1) {
return;
}
it(`includes '${path}'`, function() {
try {
sourceFiles(path);
} catch (err) {
if (troubleMakers.indexOf(path) === -1) {
expect(err).toBeNull();
}
}
});
});
});
}
|