Repository navigation
Expand file tree
/
Copy pathHeftCommandLineParser.test.ts
More file actions
140 lines (122 loc) · 5.3 KB
/
Copy pathHeftCommandLineParser.test.ts
File metadata and controls
140 lines (122 loc) · 5.3 KB
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
// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
// See LICENSE in the project root for license information.
import * as childProcess from 'node:child_process';
import * as fs from 'node:fs';
import * as os from 'node:os';
import * as path from 'node:path';
jest.setTimeout(30_000);
const HEFT_START_PATH: string = path.resolve(__dirname, '../start.js');
// A project with one task. The task sets process.exitCode to FIXTURE_EXIT_CODE in the Heft process, the
// way a test that Jest runs in band can, and then fails if FIXTURE_FAIL is set.
const FIXTURE_FILES: Record<string, string> = {
'package.json': JSON.stringify({ name: 'heft-exit-code-fixture', version: '1.0.0', private: true }),
'heft-plugin.json': JSON.stringify({
taskPlugins: [{ pluginName: 'exit-code-fixture-plugin', entryPoint: './plugin.js' }]
}),
'config/heft.json': JSON.stringify({
phasesByName: {
test: { tasksByName: { fixture: { taskPlugin: { pluginPackage: 'heft-exit-code-fixture' } } } }
}
}),
'plugin.js': `module.exports = class {
apply(taskSession) {
taskSession.hooks.run.tapPromise('exit-code-fixture-plugin', async () => {
process.exitCode = Number(process.env.FIXTURE_EXIT_CODE);
if (process.env.FIXTURE_FAIL) {
throw new Error('The fixture task failed on purpose');
}
});
}
};
`
};
// The same project, but its heft.json declares an alias with the name of the "test" phase's action, so Heft fails
// before any task runs, with an error that nothing has reported yet.
const ALIAS_CLASH_FIXTURE_FILES: Record<string, string> = {
...FIXTURE_FILES,
'config/heft.json': JSON.stringify({
...JSON.parse(FIXTURE_FILES['config/heft.json']),
aliasesByName: { test: { actionName: 'test' } }
})
};
function createFixtureFolder(files: Record<string, string>): string {
const fixtureFolderPath: string = fs.mkdtempSync(path.join(os.tmpdir(), 'heft-exit-code-'));
for (const [relativePath, contents] of Object.entries(files)) {
const filePath: string = path.join(fixtureFolderPath, relativePath);
fs.mkdirSync(path.dirname(filePath), { recursive: true });
fs.writeFileSync(filePath, contents);
}
return fixtureFolderPath;
}
interface IHeftResult {
exitCode: number | undefined;
output: string;
}
describe('HeftCommandLineParser', () => {
let fixtureFolderPath: string;
let aliasClashFixtureFolderPath: string;
beforeAll(() => {
fixtureFolderPath = createFixtureFolder(FIXTURE_FILES);
aliasClashFixtureFolderPath = createFixtureFolder(ALIAS_CLASH_FIXTURE_FILES);
});
afterAll(() => {
fs.rmSync(fixtureFolderPath, { recursive: true, force: true });
fs.rmSync(aliasClashFixtureFolderPath, { recursive: true, force: true });
});
async function runHeftTestAsync(
taskExitCode: number,
taskFails: boolean,
folderPath: string = fixtureFolderPath
): Promise<IHeftResult> {
const env: NodeJS.ProcessEnv = { ...process.env, FIXTURE_EXIT_CODE: String(taskExitCode) };
delete env.FIXTURE_FAIL;
delete env._RUSH_REPORTER_CHILD_FD;
delete env._RUSH_REPORTER_CHILD_ACK_FD;
if (taskFails) {
env.FIXTURE_FAIL = '1';
}
const child: childProcess.ChildProcess = childProcess.spawn(process.execPath, [HEFT_START_PATH, 'test'], {
cwd: folderPath,
env,
stdio: ['ignore', 'pipe', 'pipe']
});
let output: string = '';
const appendOutput: (chunk: string) => void = (chunk: string) => {
output += chunk;
};
child.stdout?.setEncoding('utf8').on('data', appendOutput);
child.stderr?.setEncoding('utf8').on('data', appendOutput);
const exitCode: number | null = await new Promise((resolve, reject) => {
child.once('error', reject);
child.once('close', resolve);
});
return { exitCode: exitCode ?? undefined, output };
}
it('exits with 1 when a task fails after code in the Heft process set process.exitCode to 0', async () => {
const { exitCode, output }: IHeftResult = await runHeftTestAsync(0, true);
expect(output).toContain('The fixture task failed on purpose');
expect(exitCode).toBe(1);
});
it('exits with 1 when a task fails after code in the Heft process set a negative process.exitCode', async () => {
const { exitCode, output }: IHeftResult = await runHeftTestAsync(-1, true);
expect(output).toContain('The fixture task failed on purpose');
expect(exitCode).toBe(1);
});
it('keeps a positive process.exitCode when a task fails', async () => {
const { exitCode, output }: IHeftResult = await runHeftTestAsync(3, true);
expect(output).toContain('The fixture task failed on purpose');
expect(exitCode).toBe(3);
});
it('exits with 0 when the task succeeds after code in the Heft process set process.exitCode to 0', async () => {
const { exitCode, output }: IHeftResult = await runHeftTestAsync(0, false);
expect(output).not.toContain('The fixture task failed on purpose');
expect(exitCode).toBe(0);
});
it('writes an error that nothing reported before it exits, and exits with 1', async () => {
const { exitCode, output }: IHeftResult = await runHeftTestAsync(0, false, aliasClashFixtureFolderPath);
expect(output).toContain(
'The alias "test" specified in heft.json cannot be used because an action with that name already exists.'
);
expect(exitCode).toBe(1);
});
});