UNPKG

9.36 kBJavaScriptView Raw
1"use strict";
2/**
3 * @module Core
4 */
5Object.defineProperty(exports, "__esModule", { value: true });
6exports.Group = void 0;
7/*
8 * japa
9 *
10 * (c) Harminder Virk <virk@adonisjs.com>
11 *
12 * For the full copyright and license information, please view the LICENSE
13 * file that was distributed with this source code.
14*/
15const Hook_1 = require("../Hook");
16const Test_1 = require("../Test");
17const Emitter_1 = require("../Emitter");
18const Contracts_1 = require("../Contracts");
19/**
20 * Group holds `n` number of tests to be executed. Groups also allows
21 * defining hooks to be called before and after each test and the
22 * group itself.
23 */
24class Group {
25 constructor(title, _resolveTestFn, _resolveHookFn, _options) {
26 this.title = title;
27 this._resolveTestFn = _resolveTestFn;
28 this._resolveHookFn = _resolveHookFn;
29 this._options = _options;
30 this._hooks = {
31 before: [],
32 after: [],
33 beforeEach: [],
34 afterEach: [],
35 };
36 /**
37 * The test error (if any)
38 */
39 this._error = null;
40 /**
41 * Has test been executed
42 */
43 this._completed = false;
44 /**
45 * An array of tests related to the group. They are mutated by the
46 * run method to filter and keep only the one's that matches
47 * the grep filter.
48 */
49 this._tests = [];
50 /**
51 * Storing whether the group has any failing tests or
52 * not.
53 */
54 this._hasFailingTests = false;
55 /**
56 * Is there a cherry picked test using the `only` property
57 * or not?
58 */
59 this._hasCherryPickedTest = false;
60 }
61 /**
62 * Returns a boolean telling if group or any of the tests inside
63 * the group has errors.
64 */
65 get hasErrors() {
66 return this._hasFailingTests || !!this._error;
67 }
68 /**
69 * Filter tests if grep value is defined
70 */
71 _filterTests() {
72 if (!this._options.grep) {
73 return;
74 }
75 const filteredTests = this._tests.filter((test) => this._options.grep.test(test.title));
76 this._tests = filteredTests;
77 }
78 /**
79 * Run a hook and if it raises error, then we will
80 * set the completed flag to true, along with the
81 * error.
82 */
83 async _runHook(fn) {
84 try {
85 await fn.run();
86 }
87 catch (error) {
88 this._completed = true;
89 this._error = error;
90 }
91 }
92 /**
93 * Runs a single test along side with it's hooks.
94 */
95 async _runTest(test) {
96 /**
97 * Run beforeEach hooks
98 */
99 for (let hook of this._hooks.beforeEach) {
100 if (this._completed) {
101 break;
102 }
103 await this._runHook(hook);
104 }
105 /**
106 * Return early if completed is set to true (happens when any hook throws error)
107 */
108 if (this._completed) {
109 return;
110 }
111 /**
112 * Otherwise run the test
113 */
114 await test.run();
115 /**
116 * Setting flag to true when any one test has failed. This helps
117 * in telling runner to exit process with the correct status.
118 */
119 const testFailed = test.toJSON().status === Contracts_1.ITestStatus.FAILED;
120 if (!this._hasFailingTests && testFailed) {
121 this._hasFailingTests = true;
122 }
123 /**
124 * Mark group as completed when bail is set to true and
125 * test has failed
126 */
127 if (this._options.bail && testFailed) {
128 this._completed = true;
129 return;
130 }
131 /**
132 * Run all after each hooks
133 */
134 for (let hook of this._hooks.afterEach) {
135 if (this._completed) {
136 break;
137 }
138 await this._runHook(hook);
139 }
140 }
141 /**
142 * Runs all the tests one by one and also executes
143 * the beforeEach and afterEach hooks
144 */
145 async _runTests() {
146 /**
147 * Run all the tests in sequence. If any hook beforeEach or afterEach
148 * hook fails, it will set `complete = true` and then we break out
149 * of the loop, since if hooks are failing, then there is no
150 * point is running tests.
151 */
152 for (let test of this._tests) {
153 if (this._completed) {
154 break;
155 }
156 await this._runTest(test);
157 }
158 }
159 /**
160 * Returns the JSON report for the group. The output of this
161 * method is emitted as an event.
162 */
163 toJSON() {
164 let status = Contracts_1.IGroupStatus.PENDING;
165 if (this._completed && this._error) {
166 status = Contracts_1.IGroupStatus.FAILED;
167 }
168 else if (this._completed) {
169 status = Contracts_1.IGroupStatus.PASSED;
170 }
171 return {
172 title: this.title,
173 status: status,
174 error: this._error,
175 };
176 }
177 /**
178 * Define timeout for all the tests inside the group. Still
179 * each test can override it's own timeout.
180 */
181 timeout(duration) {
182 if (typeof (duration) !== 'number') {
183 throw new Error('"group.timeout" expects a valid integer');
184 }
185 if (this._tests.length) {
186 throw new Error('group.timeout must be called before defining the tests');
187 }
188 this._timeout = duration;
189 return this;
190 }
191 /**
192 * Create a new test as part of this group.
193 */
194 test(title, callback, testOptions) {
195 if (!title.trim()) {
196 throw new Error('test title cannot be empty');
197 }
198 testOptions = Object.assign({
199 regression: false,
200 skip: false,
201 skipInCI: false,
202 runInCI: false,
203 }, testOptions);
204 /**
205 * Using group timeout as a priority over runner timeout
206 */
207 testOptions.timeout = this._timeout !== undefined ? this._timeout : this._options.timeout;
208 const test = new Test_1.Test(title, this._resolveTestFn, callback, testOptions);
209 /**
210 * Do not track test when a test has been cherry picked earlier
211 */
212 if (this._hasCherryPickedTest) {
213 return test;
214 }
215 /**
216 * Remove all existing tests, when a test has a `.only` property
217 * set to true.
218 */
219 if (testOptions.only === true) {
220 this._hasCherryPickedTest = true;
221 this._tests = [];
222 }
223 this._tests.push(test);
224 return test;
225 }
226 /**
227 * Add before hook to be executed before the group starts
228 * executing tests.
229 */
230 before(cb) {
231 if (typeof (cb) !== 'function') {
232 throw new Error('"group.before" expects callback to be a valid function');
233 }
234 this._hooks.before.push(new Hook_1.Hook(this._resolveHookFn, cb, 'before'));
235 return this;
236 }
237 /**
238 * Add after hook to be executed after the group has executed
239 * all the tests.
240 */
241 after(cb) {
242 if (typeof (cb) !== 'function') {
243 throw new Error('"group.after" expects callback to be a valid function');
244 }
245 this._hooks.after.push(new Hook_1.Hook(this._resolveHookFn, cb, 'after'));
246 return this;
247 }
248 /**
249 * Add before each hook to be execute before each test
250 */
251 beforeEach(cb) {
252 if (typeof (cb) !== 'function') {
253 throw new Error('"group.beforeEach" expects callback to be a valid function');
254 }
255 this._hooks.beforeEach.push(new Hook_1.Hook(this._resolveHookFn, cb, 'beforeEach'));
256 return this;
257 }
258 /**
259 * Add after each hook to be execute before each test
260 */
261 afterEach(cb) {
262 if (typeof (cb) !== 'function') {
263 throw new Error('"group.afterEach" expects callback to be a valid function');
264 }
265 this._hooks.afterEach.push(new Hook_1.Hook(this._resolveHookFn, cb, 'afterEach'));
266 return this;
267 }
268 /**
269 * Run the group with it's hooks and all tests. Shouldn't be called
270 * by the end user and Japa itself will call this method
271 */
272 async run() {
273 this._filterTests();
274 /**
275 * Return early when no tests are defined
276 */
277 if (!this._tests.length) {
278 this._completed = true;
279 return;
280 }
281 Emitter_1.emitter.emit(Contracts_1.IEvents.GROUPSTARTED, this.toJSON());
282 /**
283 * Run all before hooks for the group
284 */
285 for (let hook of this._hooks.before) {
286 if (this._completed) {
287 break;
288 }
289 await this._runHook(hook);
290 }
291 /**
292 * Run the tests, if complete flag is not set to true. It is
293 * set to true, when any before hooks fail
294 */
295 if (!this._completed) {
296 await this._runTests();
297 }
298 /**
299 * Run all after hooks
300 */
301 for (let hook of this._hooks.after) {
302 if (this._completed) {
303 break;
304 }
305 await this._runHook(hook);
306 }
307 this._completed = true;
308 Emitter_1.emitter.emit(Contracts_1.IEvents.GROUPCOMPLETED, this.toJSON());
309 }
310}
311exports.Group = Group;