Skip to content

Commit 873dd26

Browse files
fix: set test state failed if error is thrown in before hook (#160)
--------- Co-authored-by: Danielku15 <[email protected]>
1 parent f491294 commit 873dd26

File tree

9 files changed

+259
-2
lines changed

9 files changed

+259
-2
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ This extension automatically discovers and works with the `.mocharc.js/cjs/yaml/
2323
- `syntax` Parse the file and try to extract the tests from the syntax tree.
2424
- The `extractTimeout` limiting how long the extraction of tests for a single file is allowed to take.
2525
- The `test` and `suite` identifiers the process extracts. Defaults to `["it", "test"]` and `["describe", "suite"]` respectively, covering Mocha's common interfaces.
26+
- The `hooks` identifiers to avoid Mocha executing stuff on test discovery. Defaults to `["before", "after", "beforeEach", "afterEach"]`.
2627

2728
- `mocha-vscode.debugOptions`: options, normally found in the launch.json, to pass when debugging the extension. See [the docs](https://code.visualstudio.com/docs/nodejs/nodejs-debugging#_launch-configuration-attributes) for a complete list of options.
2829

package.json

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@
3838
"title": "Mocha for VS Code",
3939
"properties": {
4040
"mocha-vscode.extractSettings": {
41-
"markdownDescription": "Configures how tests get extracted. You can configure:\n\n- The `extractWith` mode, that specifies if tests are extracted.\n - `evaluation-cjs` (default) Translate the test file to CommonJS and evaluate it with all dependencies mocked.\n - `evaluation-cjs-full` Translate the test file to CommonJS and fully evaluate it with all dependencies.\n - `syntax` Parse the file and try to extract the tests from the syntax tree.\n- The `extractTimeout` limiting how long the extraction of tests for a single file is allowed to take.\n- The `test` and `suite` identifiers the process extracts. Defaults to `[\"it\", \"test\"]` and `[\"describe\", \"suite\"]` respectively, covering Mocha's common interfaces.\n\n- `mocha-vscode.debugOptions`: options, normally found in the launch.json, to pass when debugging the extension. See [the docs](https://code.visualstudio.com/docs/nodejs/nodejs-debugging#_launch-configuration-attributes) for a complete list of options.",
41+
"markdownDescription": "Configures how tests get extracted. You can configure:\n\n- The `extractWith` mode, that specifies if tests are extracted.\n - `evaluation-cjs` (default) Translate the test file to CommonJS and evaluate it with all dependencies mocked.\n - `evaluation-cjs-full` Translate the test file to CommonJS and fully evaluate it with all dependencies.\n - `syntax` Parse the file and try to extract the tests from the syntax tree.\n- The `extractTimeout` limiting how long the extraction of tests for a single file is allowed to take.\n- The `test` and `suite` identifiers the process extracts. Defaults to `[\"it\", \"test\"]` and `[\"describe\", \"suite\"]` respectively, covering Mocha's common interfaces.\n- The `hooks` identifiers to avoid Mocha executing stuff on test discovery. Defaults to `[\"before\", \"after\", \"beforeEach\", \"afterEach\"]`\n\n- `mocha-vscode.debugOptions`: options, normally found in the launch.json, to pass when debugging the extension. See [the docs](https://code.visualstudio.com/docs/nodejs/nodejs-debugging#_launch-configuration-attributes) for a complete list of options.",
4242
"type": "object",
4343
"properties": {
4444
"suite": {
@@ -53,6 +53,12 @@
5353
"type": "string"
5454
}
5555
},
56+
"hooks": {
57+
"type": "array",
58+
"items": {
59+
"type": "string"
60+
}
61+
},
5662
"extractWith": {
5763
"type": "string",
5864
"enum": [
@@ -74,6 +80,12 @@
7480
"it",
7581
"test"
7682
],
83+
"hooks": [
84+
"before",
85+
"after",
86+
"beforeEach",
87+
"afterEach"
88+
],
7789
"extractWith": "evaluation-cjs",
7890
"extractTimeout": 10000
7991
},

src/constants.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ export const configFilePattern = '**/.mocharc.{js,cjs,yaml,yml,json,jsonc}';
1616
export const defaultTestSymbols: IExtensionSettings = {
1717
suite: ['describe', 'suite'],
1818
test: ['it', 'test'],
19+
hooks: ['before', 'after', 'beforeEach', 'afterEach'],
1920
extractWith: 'evaluation-cjs',
2021
extractTimeout: 10_000,
2122
};

src/discoverer/evaluate.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,8 @@ export class EvaluationTestDiscoverer implements ITestDiscoverer {
203203
return suiteFunction;
204204
} else if (symbols.value.test.includes(prop as string)) {
205205
return testFunction;
206+
} else if (symbols.value.hooks.includes(prop as string)) {
207+
return placeholder();
206208
} else if (prop in target) {
207209
return target[prop]; // top-level `var` defined get set on the contextObj
208210
} else if (prop in globalThis && !replacedGlobals.has(prop as string)) {

src/discoverer/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ export interface IParsedNode {
2222
export interface IExtensionSettings {
2323
suite: readonly string[];
2424
test: readonly string[];
25+
hooks: readonly string[];
2526
extractWith: 'syntax' | 'evaluation-cjs' | 'evaluation-cjs-full';
2627
extractTimeout: number;
2728
}

src/runner.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -510,7 +510,16 @@ class CompiledFileTests {
510510
for (const item of items) {
511511
let candidate: vscode.TestItem | undefined = item;
512512
for (let i = 0; i < path.length && candidate; i++) {
513-
candidate = candidate.children.get(path[i]);
513+
const pathPart = path[i];
514+
if (
515+
pathPart.startsWith('"before all" hook') ||
516+
pathPart.startsWith('"before each" hook') ||
517+
pathPart.startsWith('"after all" hook') ||
518+
pathPart.startsWith('"after each" hook')
519+
) {
520+
break;
521+
}
522+
candidate = candidate.children.get(pathPart);
514523
}
515524
if (candidate !== undefined) {
516525
return candidate;
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
/**
2+
* Copyright (C) Daniel Kuschny (Danielku15) and contributors.
3+
* Copyright (C) Microsoft Corporation. All rights reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style
6+
* license that can be found in the LICENSE file or at
7+
* https://opensource.org/licenses/MIT.
8+
*/
9+
10+
import { expect } from 'chai';
11+
import * as vscode from 'vscode';
12+
import { captureTestRun, expectTestTree, getController } from '../util';
13+
14+
describe('with-hooks', () => {
15+
it('discovers tests', async () => {
16+
const c = await getController();
17+
18+
expectTestTree(c, [
19+
[
20+
'hello.test.js',
21+
[
22+
['with beforeAll hook', [['addition'], ['failing'], ['subtraction']]],
23+
['with beforeEach hook', [['addition'], ['failing'], ['subtraction']]],
24+
[
25+
'with broken after hook (suite must be failed)',
26+
[['addition'], ['failing'], ['subtraction']],
27+
],
28+
[
29+
'with broken afterEach hook (suite must be failed)',
30+
[['addition (success)'], ['failing (skipped)'], ['subtraction (skipped)']],
31+
],
32+
[
33+
'with broken before hook (suite must be failed)',
34+
[['addition (skipped)'], ['failing (skipped)'], ['subtraction (skipped)']],
35+
],
36+
[
37+
'with broken beforeEach hook (suite must be failed)',
38+
[['addition (skipped)'], ['failing (skipped)'], ['subtraction (skipped)']],
39+
],
40+
],
41+
],
42+
]);
43+
});
44+
45+
it('runs tests', async () => {
46+
const c = await getController();
47+
const profiles = c.profiles;
48+
expect(profiles).to.have.lengthOf(2);
49+
50+
const run = await captureTestRun(
51+
c,
52+
new vscode.TestRunRequest(
53+
undefined,
54+
undefined,
55+
profiles.find((p) => p.kind === vscode.TestRunProfileKind.Run),
56+
),
57+
);
58+
59+
run.expectStates({
60+
'hello.test.js/with beforeAll hook/addition': ['enqueued', 'started', 'passed'],
61+
'hello.test.js/with beforeAll hook/subtraction': ['enqueued', 'started', 'passed'],
62+
'hello.test.js/with beforeAll hook/failing': ['enqueued', 'started', 'failed'],
63+
'hello.test.js/with beforeEach hook/addition': ['enqueued', 'started', 'passed'],
64+
'hello.test.js/with beforeEach hook/subtraction': ['enqueued', 'started', 'passed'],
65+
'hello.test.js/with beforeEach hook/failing': ['enqueued', 'started', 'failed'],
66+
'hello.test.js/with broken before hook (suite must be failed)/addition (skipped)': [
67+
'enqueued',
68+
'skipped',
69+
],
70+
'hello.test.js/with broken before hook (suite must be failed)/subtraction (skipped)': [
71+
'enqueued',
72+
'skipped',
73+
],
74+
'hello.test.js/with broken before hook (suite must be failed)/failing (skipped)': [
75+
'enqueued',
76+
'skipped',
77+
],
78+
'hello.test.js/with broken beforeEach hook (suite must be failed)/addition (skipped)': [
79+
'enqueued',
80+
'started',
81+
'skipped',
82+
],
83+
'hello.test.js/with broken beforeEach hook (suite must be failed)/subtraction (skipped)': [
84+
'enqueued',
85+
'skipped',
86+
],
87+
'hello.test.js/with broken beforeEach hook (suite must be failed)/failing (skipped)': [
88+
'enqueued',
89+
'skipped',
90+
],
91+
'hello.test.js/with broken after hook (suite must be failed)/addition': [
92+
'enqueued',
93+
'started',
94+
'passed',
95+
],
96+
'hello.test.js/with broken after hook (suite must be failed)/subtraction': [
97+
'enqueued',
98+
'started',
99+
'passed',
100+
],
101+
'hello.test.js/with broken after hook (suite must be failed)/failing': [
102+
'enqueued',
103+
'started',
104+
'failed',
105+
],
106+
'hello.test.js/with broken afterEach hook (suite must be failed)/addition (success)': [
107+
'enqueued',
108+
'started',
109+
'passed',
110+
],
111+
'hello.test.js/with broken afterEach hook (suite must be failed)/subtraction (skipped)': [
112+
'enqueued',
113+
'skipped',
114+
],
115+
'hello.test.js/with broken afterEach hook (suite must be failed)/failing (skipped)': [
116+
'enqueued',
117+
'skipped',
118+
],
119+
'hello.test.js/with broken before hook (suite must be failed)': ['failed'],
120+
'hello.test.js/with broken beforeEach hook (suite must be failed)': ['failed'],
121+
'hello.test.js/with broken after hook (suite must be failed)': ['failed'],
122+
'hello.test.js/with broken afterEach hook (suite must be failed)': ['failed'],
123+
});
124+
});
125+
});
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
module.exports = {
2+
spec: '**/*.test.js'
3+
};
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
const { strictEqual } = require('node:assert');
2+
3+
describe('with beforeAll hook', () => {
4+
before(() => {
5+
console.log('Before hook executed once!');
6+
});
7+
8+
it('addition', async () => {
9+
strictEqual(1 + 1, 2);
10+
});
11+
12+
it('subtraction', async () => {
13+
strictEqual(1 - 1, 0);
14+
});
15+
it('failing', async () => {
16+
strictEqual(1 * 1, 0);
17+
});
18+
});
19+
20+
describe('with beforeEach hook', () => {
21+
beforeEach(() => {
22+
console.log('BeforeEach hook executed every time!');
23+
});
24+
25+
it('addition', async () => {
26+
strictEqual(1 + 1, 2);
27+
});
28+
29+
it('subtraction', async () => {
30+
strictEqual(1 - 1, 0);
31+
});
32+
it('failing', async () => {
33+
strictEqual(1 * 1, 0);
34+
});
35+
});
36+
37+
describe('with broken before hook (suite must be failed)', () => {
38+
before(() => {
39+
throw new Error('Before hook is broken!!!');
40+
});
41+
42+
it('addition (skipped)', async () => {
43+
strictEqual(1 + 1, 2);
44+
});
45+
46+
it('subtraction (skipped)', async () => {
47+
strictEqual(1 - 1, 0);
48+
});
49+
it('failing (skipped)', async () => {
50+
strictEqual(1 * 1, 0);
51+
});
52+
});
53+
54+
describe('with broken beforeEach hook (suite must be failed)', () => {
55+
beforeEach(() => {
56+
throw new Error('BeforeEach hook is broken!!!');
57+
});
58+
59+
it('addition (skipped)', async () => {
60+
strictEqual(1 + 1, 2);
61+
});
62+
63+
it('subtraction (skipped)', async () => {
64+
strictEqual(1 - 1, 0);
65+
});
66+
it('failing (skipped)', async () => {
67+
strictEqual(1 * 1, 0);
68+
});
69+
});
70+
71+
describe('with broken after hook (suite must be failed)', () => {
72+
after(() => {
73+
throw new Error('After hook is broken!!!');
74+
});
75+
76+
it('addition', async () => {
77+
strictEqual(1 + 1, 2);
78+
});
79+
80+
it('subtraction', async () => {
81+
strictEqual(1 - 1, 0);
82+
});
83+
it('failing', async () => {
84+
strictEqual(1 * 1, 0);
85+
});
86+
});
87+
88+
describe('with broken afterEach hook (suite must be failed)', () => {
89+
afterEach(() => {
90+
throw new Error('After each hook is broken!!!');
91+
});
92+
93+
it('addition (success)', async () => {
94+
strictEqual(1 + 1, 2);
95+
});
96+
97+
it('subtraction (skipped)', async () => {
98+
strictEqual(1 - 1, 0);
99+
});
100+
it('failing (skipped)', async () => {
101+
strictEqual(1 * 1, 0);
102+
});
103+
});

0 commit comments

Comments
 (0)