mirror of
https://github.com/microsoft/playwright.git
synced 2025-06-26 21:40:17 +00:00
test: replace sendSIGINTAfter with interactWithTestRunner (#24411)
This way we can send multiple SIGINTs in tests.
This commit is contained in:
parent
9d0bba9c99
commit
ed14bf2103
@ -204,8 +204,8 @@ export class TestChildProcess {
|
|||||||
throw new Error(`Process received signal: ${r.signal}`);
|
throw new Error(`Process received signal: ${r.signal}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
async waitForOutput(substring: string) {
|
async waitForOutput(substring: string, count = 1) {
|
||||||
while (!stripAnsi(this.output).includes(substring))
|
while (countTimes(stripAnsi(this.output), substring) < count)
|
||||||
await new Promise<void>(f => this._outputCallbacks.add(f));
|
await new Promise<void>(f => this._outputCallbacks.add(f));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -275,3 +275,15 @@ export const commonFixtures: Fixtures<CommonFixtures, CommonWorkerFixtures> = {
|
|||||||
token.canceled = true;
|
token.canceled = true;
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export function countTimes(s: string, sub: string): number {
|
||||||
|
let result = 0;
|
||||||
|
for (let index = 0; index !== -1;) {
|
||||||
|
index = s.indexOf(sub, index);
|
||||||
|
if (index !== -1) {
|
||||||
|
result++;
|
||||||
|
index += sub.length;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|||||||
@ -15,7 +15,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import { test, expect } from './playwright-test-fixtures';
|
import { test, expect, parseTestRunnerOutput } from './playwright-test-fixtures';
|
||||||
|
|
||||||
test('should be able to call expect.extend in config', async ({ runInlineTest }) => {
|
test('should be able to call expect.extend in config', async ({ runInlineTest }) => {
|
||||||
const result = await runInlineTest({
|
const result = await runInlineTest({
|
||||||
@ -588,10 +588,10 @@ test('should print pending operations for toHaveText', async ({ runInlineTest })
|
|||||||
expect(output).toContain('waiting for locator(\'no-such-thing\')');
|
expect(output).toContain('waiting for locator(\'no-such-thing\')');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should print expected/received on Ctrl+C', async ({ runInlineTest }) => {
|
test('should print expected/received on Ctrl+C', async ({ interactWithTestRunner }) => {
|
||||||
test.skip(process.platform === 'win32', 'No sending SIGINT on Windows');
|
test.skip(process.platform === 'win32', 'No sending SIGINT on Windows');
|
||||||
|
|
||||||
const result = await runInlineTest({
|
const testProcess = await interactWithTestRunner({
|
||||||
'a.test.ts': `
|
'a.test.ts': `
|
||||||
import { test, expect } from '@playwright/test';
|
import { test, expect } from '@playwright/test';
|
||||||
|
|
||||||
@ -603,8 +603,13 @@ test('should print expected/received on Ctrl+C', async ({ runInlineTest }) => {
|
|||||||
await promise;
|
await promise;
|
||||||
});
|
});
|
||||||
`,
|
`,
|
||||||
}, { workers: 1 }, {}, { sendSIGINTAfter: 1 });
|
}, { workers: 1 });
|
||||||
expect(result.exitCode).toBe(130);
|
await testProcess.waitForOutput('%%SEND-SIGINT%%');
|
||||||
|
process.kill(testProcess.process.pid!, 'SIGINT');
|
||||||
|
const { exitCode } = await testProcess.exited;
|
||||||
|
expect(exitCode).toBe(130);
|
||||||
|
|
||||||
|
const result = parseTestRunnerOutput(testProcess.output);
|
||||||
expect(result.passed).toBe(0);
|
expect(result.passed).toBe(0);
|
||||||
expect(result.interrupted).toBe(1);
|
expect(result.interrupted).toBe(1);
|
||||||
expect(result.output).toContain('Expected string: "Text 2"');
|
expect(result.output).toContain('Expected string: "Text 2"');
|
||||||
|
|||||||
@ -26,8 +26,9 @@ import { serverFixtures } from '../config/serverFixtures';
|
|||||||
import type { TestInfo } from './stable-test-runner';
|
import type { TestInfo } from './stable-test-runner';
|
||||||
import { expect } from './stable-test-runner';
|
import { expect } from './stable-test-runner';
|
||||||
import { test as base } from './stable-test-runner';
|
import { test as base } from './stable-test-runner';
|
||||||
|
export { countTimes } from '../config/commonFixtures';
|
||||||
|
|
||||||
export type CliRunResult = {
|
type CliRunResult = {
|
||||||
exitCode: number,
|
exitCode: number,
|
||||||
output: string,
|
output: string,
|
||||||
};
|
};
|
||||||
@ -92,13 +93,7 @@ const configFile = (baseDir: string, files: Files): string | undefined => {
|
|||||||
return undefined;
|
return undefined;
|
||||||
};
|
};
|
||||||
|
|
||||||
async function runPlaywrightTest(childProcess: CommonFixtures['childProcess'], baseDir: string, params: any, env: NodeJS.ProcessEnv, options: RunOptions, files: Files, mergeReports: (reportFolder: string, env?: NodeJS.ProcessEnv, options?: RunOptions) => Promise<CliRunResult>, useIntermediateMergeReport: boolean): Promise<RunResult> {
|
function startPlaywrightTest(childProcess: CommonFixtures['childProcess'], baseDir: string, params: any, env: NodeJS.ProcessEnv, options: RunOptions): TestChildProcess {
|
||||||
let reporter;
|
|
||||||
if (useIntermediateMergeReport) {
|
|
||||||
reporter = params.reporter;
|
|
||||||
params.reporter = 'blob';
|
|
||||||
}
|
|
||||||
|
|
||||||
const paramList: string[] = [];
|
const paramList: string[] = [];
|
||||||
for (const key of Object.keys(params)) {
|
for (const key of Object.keys(params)) {
|
||||||
for (const value of Array.isArray(params[key]) ? params[key] : [params[key]]) {
|
for (const value of Array.isArray(params[key]) ? params[key] : [params[key]]) {
|
||||||
@ -106,7 +101,6 @@ async function runPlaywrightTest(childProcess: CommonFixtures['childProcess'], b
|
|||||||
paramList.push(params[key] === true ? `${k}` : `${k}=${value}`);
|
paramList.push(params[key] === true ? `${k}` : `${k}=${value}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const reportFile = path.join(baseDir, 'report.json');
|
|
||||||
const args = ['test'];
|
const args = ['test'];
|
||||||
args.push(
|
args.push(
|
||||||
'--workers=2',
|
'--workers=2',
|
||||||
@ -114,14 +108,27 @@ async function runPlaywrightTest(childProcess: CommonFixtures['childProcess'], b
|
|||||||
);
|
);
|
||||||
if (options.additionalArgs)
|
if (options.additionalArgs)
|
||||||
args.push(...options.additionalArgs);
|
args.push(...options.additionalArgs);
|
||||||
|
return childProcess({
|
||||||
|
command: ['node', cliEntrypoint, ...args],
|
||||||
|
env: cleanEnv(env),
|
||||||
|
cwd: options.cwd ? path.resolve(baseDir, options.cwd) : baseDir,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const cwd = options.cwd ? path.resolve(baseDir, options.cwd) : baseDir;
|
async function runPlaywrightTest(childProcess: CommonFixtures['childProcess'], baseDir: string, params: any, env: NodeJS.ProcessEnv, options: RunOptions, files: Files, mergeReports: (reportFolder: string, env?: NodeJS.ProcessEnv, options?: RunOptions) => Promise<CliRunResult>, useIntermediateMergeReport: boolean): Promise<RunResult> {
|
||||||
// eslint-disable-next-line prefer-const
|
let reporter;
|
||||||
let { exitCode, output } = await runPlaywrightCommand(childProcess, cwd, args, {
|
if (useIntermediateMergeReport) {
|
||||||
|
reporter = params.reporter;
|
||||||
|
params.reporter = 'blob';
|
||||||
|
}
|
||||||
|
const reportFile = path.join(baseDir, 'report.json');
|
||||||
|
const testProcess = startPlaywrightTest(childProcess, baseDir, params, {
|
||||||
PW_TEST_REPORTER: path.join(__dirname, '../../packages/playwright-test/lib/reporters/json.js'),
|
PW_TEST_REPORTER: path.join(__dirname, '../../packages/playwright-test/lib/reporters/json.js'),
|
||||||
PLAYWRIGHT_JSON_OUTPUT_NAME: reportFile,
|
PLAYWRIGHT_JSON_OUTPUT_NAME: reportFile,
|
||||||
...env,
|
...env,
|
||||||
}, options.sendSIGINTAfter);
|
}, options);
|
||||||
|
const { exitCode } = await testProcess.exited;
|
||||||
|
let output = testProcess.output.toString();
|
||||||
|
|
||||||
if (useIntermediateMergeReport) {
|
if (useIntermediateMergeReport) {
|
||||||
const additionalArgs = [];
|
const additionalArgs = [];
|
||||||
@ -130,25 +137,14 @@ async function runPlaywrightTest(childProcess: CommonFixtures['childProcess'], b
|
|||||||
const config = configFile(baseDir, files);
|
const config = configFile(baseDir, files);
|
||||||
if (config)
|
if (config)
|
||||||
additionalArgs.push('--config', config);
|
additionalArgs.push('--config', config);
|
||||||
|
const cwd = options.cwd ? path.resolve(baseDir, options.cwd) : baseDir;
|
||||||
const mergeResult = await mergeReports('blob-report', env, { cwd, additionalArgs });
|
const mergeResult = await mergeReports('blob-report', env, { cwd, additionalArgs });
|
||||||
expect(mergeResult.exitCode).toBe(0);
|
expect(mergeResult.exitCode).toBe(0);
|
||||||
output = mergeResult.output;
|
output = mergeResult.output;
|
||||||
}
|
}
|
||||||
|
|
||||||
const summary = (re: RegExp) => {
|
const parsed = parseTestRunnerOutput(output);
|
||||||
let result = 0;
|
|
||||||
let match = re.exec(output);
|
|
||||||
while (match) {
|
|
||||||
result += (+match[1]);
|
|
||||||
match = re.exec(output);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
};
|
|
||||||
const passed = summary(/(\d+) passed/g);
|
|
||||||
const failed = summary(/(\d+) failed/g);
|
|
||||||
const flaky = summary(/(\d+) flaky/g);
|
|
||||||
const skipped = summary(/(\d+) skipped/g);
|
|
||||||
const interrupted = summary(/(\d+) interrupted/g);
|
|
||||||
let report;
|
let report;
|
||||||
try {
|
try {
|
||||||
report = JSON.parse(fs.readFileSync(reportFile).toString());
|
report = JSON.parse(fs.readFileSync(reportFile).toString());
|
||||||
@ -171,66 +167,23 @@ async function runPlaywrightTest(childProcess: CommonFixtures['childProcess'], b
|
|||||||
if (report)
|
if (report)
|
||||||
visitSuites(report.suites);
|
visitSuites(report.suites);
|
||||||
|
|
||||||
const strippedOutput = stripAnsi(output);
|
|
||||||
return {
|
return {
|
||||||
|
...parsed,
|
||||||
exitCode,
|
exitCode,
|
||||||
output: strippedOutput,
|
|
||||||
outputLines: strippedOutput.split('\n').filter(line => line.startsWith('%%')).map(line => line.substring(2).trim()),
|
|
||||||
rawOutput: output,
|
rawOutput: output,
|
||||||
passed,
|
|
||||||
failed,
|
|
||||||
flaky,
|
|
||||||
skipped,
|
|
||||||
interrupted,
|
|
||||||
report,
|
report,
|
||||||
results,
|
results,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
async function runPlaywrightListFiles(childProcess: CommonFixtures['childProcess'], baseDir: string, env: NodeJS.ProcessEnv): Promise<{ output: string, exitCode: number }> {
|
async function runPlaywrightListFiles(childProcess: CommonFixtures['childProcess'], baseDir: string, env: NodeJS.ProcessEnv): Promise<{ output: string, exitCode: number }> {
|
||||||
const reportFile = path.join(baseDir, 'report.json');
|
|
||||||
// eslint-disable-next-line prefer-const
|
|
||||||
let { exitCode, output } = await runPlaywrightCommand(childProcess, baseDir, ['list-files'], {
|
|
||||||
PW_TEST_REPORTER: path.join(__dirname, '../../packages/playwright-test/lib/reporters/json.js'),
|
|
||||||
PLAYWRIGHT_JSON_OUTPUT_NAME: reportFile,
|
|
||||||
...env,
|
|
||||||
});
|
|
||||||
return { exitCode, output };
|
|
||||||
}
|
|
||||||
|
|
||||||
function watchPlaywrightTest(childProcess: CommonFixtures['childProcess'], baseDir: string, env: NodeJS.ProcessEnv, options: RunOptions): TestChildProcess {
|
|
||||||
const args = ['test', '--workers=2'];
|
|
||||||
if (options.additionalArgs)
|
|
||||||
args.push(...options.additionalArgs);
|
|
||||||
const cwd = options.cwd ? path.resolve(baseDir, options.cwd) : baseDir;
|
|
||||||
|
|
||||||
const command = ['node', cliEntrypoint];
|
|
||||||
command.push(...args);
|
|
||||||
const testProcess = childProcess({
|
const testProcess = childProcess({
|
||||||
command,
|
command: ['node', cliEntrypoint, 'list-files'],
|
||||||
env: cleanEnv({ PWTEST_WATCH: '1', ...env }),
|
|
||||||
cwd,
|
|
||||||
});
|
|
||||||
return testProcess;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function runPlaywrightCommand(childProcess: CommonFixtures['childProcess'], cwd: string, commandWithArguments: string[], env: NodeJS.ProcessEnv, sendSIGINTAfter?: number): Promise<CliRunResult> {
|
|
||||||
const command = ['node', cliEntrypoint];
|
|
||||||
command.push(...commandWithArguments);
|
|
||||||
const testProcess = childProcess({
|
|
||||||
command,
|
|
||||||
env: cleanEnv(env),
|
env: cleanEnv(env),
|
||||||
cwd,
|
cwd: baseDir,
|
||||||
});
|
});
|
||||||
let didSendSigint = false;
|
|
||||||
testProcess.onOutput = () => {
|
|
||||||
if (sendSIGINTAfter && !didSendSigint && countTimes(testProcess.output, '%%SEND-SIGINT%%') >= sendSIGINTAfter) {
|
|
||||||
didSendSigint = true;
|
|
||||||
process.kill(testProcess.process.pid!, 'SIGINT');
|
|
||||||
}
|
|
||||||
};
|
|
||||||
const { exitCode } = await testProcess.exited;
|
const { exitCode } = await testProcess.exited;
|
||||||
return { exitCode, output: testProcess.output.toString() };
|
return { exitCode, output: testProcess.output };
|
||||||
}
|
}
|
||||||
|
|
||||||
export function cleanEnv(env: NodeJS.ProcessEnv): NodeJS.ProcessEnv {
|
export function cleanEnv(env: NodeJS.ProcessEnv): NodeJS.ProcessEnv {
|
||||||
@ -261,7 +214,6 @@ export function cleanEnv(env: NodeJS.ProcessEnv): NodeJS.ProcessEnv {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export type RunOptions = {
|
export type RunOptions = {
|
||||||
sendSIGINTAfter?: number;
|
|
||||||
additionalArgs?: string[];
|
additionalArgs?: string[];
|
||||||
cwd?: string,
|
cwd?: string,
|
||||||
};
|
};
|
||||||
@ -271,6 +223,7 @@ type Fixtures = {
|
|||||||
runInlineTest: (files: Files, params?: Params, env?: NodeJS.ProcessEnv, options?: RunOptions) => Promise<RunResult>;
|
runInlineTest: (files: Files, params?: Params, env?: NodeJS.ProcessEnv, options?: RunOptions) => Promise<RunResult>;
|
||||||
runListFiles: (files: Files) => Promise<{ output: string, exitCode: number }>;
|
runListFiles: (files: Files) => Promise<{ output: string, exitCode: number }>;
|
||||||
runWatchTest: (files: Files, env?: NodeJS.ProcessEnv, options?: RunOptions) => Promise<TestChildProcess>;
|
runWatchTest: (files: Files, env?: NodeJS.ProcessEnv, options?: RunOptions) => Promise<TestChildProcess>;
|
||||||
|
interactWithTestRunner: (files: Files, params?: Params, env?: NodeJS.ProcessEnv, options?: RunOptions) => Promise<TestChildProcess>;
|
||||||
runTSC: (files: Files) => Promise<TSCResult>;
|
runTSC: (files: Files) => Promise<TSCResult>;
|
||||||
mergeReports: (reportFolder: string, env?: NodeJS.ProcessEnv, options?: RunOptions) => Promise<CliRunResult>
|
mergeReports: (reportFolder: string, env?: NodeJS.ProcessEnv, options?: RunOptions) => Promise<CliRunResult>
|
||||||
useIntermediateMergeReport: boolean;
|
useIntermediateMergeReport: boolean;
|
||||||
@ -310,12 +263,16 @@ export const test = base
|
|||||||
await rimraf(cacheDir);
|
await rimraf(cacheDir);
|
||||||
},
|
},
|
||||||
|
|
||||||
runWatchTest: async ({ childProcess }, use, testInfo: TestInfo) => {
|
runWatchTest: async ({ interactWithTestRunner }, use, testInfo: TestInfo) => {
|
||||||
|
await use((files, env, options) => interactWithTestRunner(files, {}, { ...env, PWTEST_WATCH: '1' }, options));
|
||||||
|
},
|
||||||
|
|
||||||
|
interactWithTestRunner: async ({ childProcess }, use, testInfo: TestInfo) => {
|
||||||
const cacheDir = await fs.promises.mkdtemp(path.join(os.tmpdir(), 'playwright-test-cache-'));
|
const cacheDir = await fs.promises.mkdtemp(path.join(os.tmpdir(), 'playwright-test-cache-'));
|
||||||
let testProcess: TestChildProcess | undefined;
|
let testProcess: TestChildProcess | undefined;
|
||||||
await use(async (files: Files, env: NodeJS.ProcessEnv = {}, options: RunOptions = {}) => {
|
await use(async (files: Files, params: Params = {}, env: NodeJS.ProcessEnv = {}, options: RunOptions = {}) => {
|
||||||
const baseDir = await writeFiles(testInfo, files, true);
|
const baseDir = await writeFiles(testInfo, files, true);
|
||||||
testProcess = watchPlaywrightTest(childProcess, baseDir, { ...env, PWTEST_CACHE_DIR: cacheDir }, options);
|
testProcess = startPlaywrightTest(childProcess, baseDir, params, { ...env, PWTEST_CACHE_DIR: cacheDir }, options);
|
||||||
return testProcess;
|
return testProcess;
|
||||||
});
|
});
|
||||||
await testProcess?.kill();
|
await testProcess?.kill();
|
||||||
@ -388,18 +345,6 @@ export function stripAnsi(str: string): string {
|
|||||||
return str.replace(asciiRegex, '');
|
return str.replace(asciiRegex, '');
|
||||||
}
|
}
|
||||||
|
|
||||||
export function countTimes(s: string, sub: string): number {
|
|
||||||
let result = 0;
|
|
||||||
for (let index = 0; index !== -1;) {
|
|
||||||
index = s.indexOf(sub, index);
|
|
||||||
if (index !== -1) {
|
|
||||||
result++;
|
|
||||||
index += sub.length;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function createImage(width: number, height: number, r: number = 0, g: number = 0, b: number = 0, a: number = 255): Buffer {
|
export function createImage(width: number, height: number, r: number = 0, g: number = 0, b: number = 0, a: number = 255): Buffer {
|
||||||
const image = new PNG({ width, height });
|
const image = new PNG({ width, height });
|
||||||
// Make both images red.
|
// Make both images red.
|
||||||
@ -446,3 +391,32 @@ export function expectTestHelper(result: RunResult) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function parseTestRunnerOutput(output: string) {
|
||||||
|
const summary = (re: RegExp) => {
|
||||||
|
let result = 0;
|
||||||
|
let match = re.exec(output);
|
||||||
|
while (match) {
|
||||||
|
result += (+match[1]);
|
||||||
|
match = re.exec(output);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
const passed = summary(/(\d+) passed/g);
|
||||||
|
const failed = summary(/(\d+) failed/g);
|
||||||
|
const flaky = summary(/(\d+) flaky/g);
|
||||||
|
const skipped = summary(/(\d+) skipped/g);
|
||||||
|
const interrupted = summary(/(\d+) interrupted/g);
|
||||||
|
|
||||||
|
const strippedOutput = stripAnsi(output);
|
||||||
|
return {
|
||||||
|
output: strippedOutput,
|
||||||
|
outputLines: strippedOutput.split('\n').filter(line => line.startsWith('%%')).map(line => line.substring(2).trim()),
|
||||||
|
rawOutput: output,
|
||||||
|
passed,
|
||||||
|
failed,
|
||||||
|
flaky,
|
||||||
|
skipped,
|
||||||
|
interrupted,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|||||||
@ -14,7 +14,7 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { test, expect } from './playwright-test-fixtures';
|
import { test, expect, parseTestRunnerOutput } from './playwright-test-fixtures';
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import { spawnSync } from 'child_process';
|
import { spawnSync } from 'child_process';
|
||||||
@ -431,10 +431,10 @@ test('should throw when using page in beforeAll', async ({ runInlineTest }) => {
|
|||||||
expect(result.output).toContain(`Error: "context" and "page" fixtures are not supported in "beforeAll"`);
|
expect(result.output).toContain(`Error: "context" and "page" fixtures are not supported in "beforeAll"`);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should report click error on sigint', async ({ runInlineTest }) => {
|
test('should report click error on sigint', async ({ interactWithTestRunner }) => {
|
||||||
test.skip(process.platform === 'win32', 'No sending SIGINT on Windows');
|
test.skip(process.platform === 'win32', 'No sending SIGINT on Windows');
|
||||||
|
|
||||||
const result = await runInlineTest({
|
const testProcess = await interactWithTestRunner({
|
||||||
'a.test.ts': `
|
'a.test.ts': `
|
||||||
import { test, expect } from '@playwright/test';
|
import { test, expect } from '@playwright/test';
|
||||||
test('timedout', async ({ page }) => {
|
test('timedout', async ({ page }) => {
|
||||||
@ -445,9 +445,13 @@ test('should report click error on sigint', async ({ runInlineTest }) => {
|
|||||||
await promise;
|
await promise;
|
||||||
});
|
});
|
||||||
`,
|
`,
|
||||||
}, { workers: 1 }, {}, { sendSIGINTAfter: 1 });
|
}, { workers: 1 });
|
||||||
|
await testProcess.waitForOutput('%%SEND-SIGINT%%');
|
||||||
|
process.kill(testProcess.process.pid!, 'SIGINT');
|
||||||
|
const { exitCode } = await testProcess.exited;
|
||||||
|
expect(exitCode).toBe(130);
|
||||||
|
|
||||||
expect(result.exitCode).toBe(130);
|
const result = parseTestRunnerOutput(testProcess.output);
|
||||||
expect(result.passed).toBe(0);
|
expect(result.passed).toBe(0);
|
||||||
expect(result.failed).toBe(0);
|
expect(result.failed).toBe(0);
|
||||||
expect(result.interrupted).toBe(1);
|
expect(result.interrupted).toBe(1);
|
||||||
|
|||||||
@ -13,8 +13,10 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import fs from 'fs';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import { test, expect } from './playwright-test-fixtures';
|
import { test, expect, parseTestRunnerOutput } from './playwright-test-fixtures';
|
||||||
|
|
||||||
test('it should not allow multiple tests with the same name per suite', async ({ runInlineTest }) => {
|
test('it should not allow multiple tests with the same name per suite', async ({ runInlineTest }) => {
|
||||||
const result = await runInlineTest({
|
const result = await runInlineTest({
|
||||||
@ -93,10 +95,10 @@ test('should continue with other tests after worker process suddenly exits', asy
|
|||||||
expect(result.output).toContain('Internal error: worker process exited unexpectedly');
|
expect(result.output).toContain('Internal error: worker process exited unexpectedly');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('sigint should stop workers', async ({ runInlineTest }) => {
|
test('sigint should stop workers', async ({ interactWithTestRunner }) => {
|
||||||
test.skip(process.platform === 'win32', 'No sending SIGINT on Windows');
|
test.skip(process.platform === 'win32', 'No sending SIGINT on Windows');
|
||||||
|
|
||||||
const result = await runInlineTest({
|
const testProcess = await interactWithTestRunner({
|
||||||
'a.spec.js': `
|
'a.spec.js': `
|
||||||
import { test, expect } from '@playwright/test';
|
import { test, expect } from '@playwright/test';
|
||||||
test('interrupted1', async () => {
|
test('interrupted1', async () => {
|
||||||
@ -117,8 +119,16 @@ test('sigint should stop workers', async ({ runInlineTest }) => {
|
|||||||
console.log('\\n%%skipped2');
|
console.log('\\n%%skipped2');
|
||||||
});
|
});
|
||||||
`,
|
`,
|
||||||
}, { 'workers': 2, 'reporter': 'line,json' }, {}, { sendSIGINTAfter: 2 });
|
}, { 'workers': 2, 'reporter': 'line,json' }, {
|
||||||
expect(result.exitCode).toBe(130);
|
PW_TEST_REPORTER: path.join(__dirname, '../../packages/playwright-test/lib/reporters/json.js'),
|
||||||
|
PLAYWRIGHT_JSON_OUTPUT_NAME: 'report.json',
|
||||||
|
});
|
||||||
|
await testProcess.waitForOutput('%%SEND-SIGINT%%', 2);
|
||||||
|
process.kill(testProcess.process.pid!, 'SIGINT');
|
||||||
|
const { exitCode } = await testProcess.exited;
|
||||||
|
expect(exitCode).toBe(130);
|
||||||
|
|
||||||
|
const result = parseTestRunnerOutput(testProcess.output);
|
||||||
expect(result.passed).toBe(0);
|
expect(result.passed).toBe(0);
|
||||||
expect(result.failed).toBe(0);
|
expect(result.failed).toBe(0);
|
||||||
expect(result.skipped).toBe(2);
|
expect(result.skipped).toBe(2);
|
||||||
@ -130,11 +140,12 @@ test('sigint should stop workers', async ({ runInlineTest }) => {
|
|||||||
expect(result.output).toContain('Test was interrupted.');
|
expect(result.output).toContain('Test was interrupted.');
|
||||||
expect(result.output).not.toContain('Test timeout of');
|
expect(result.output).not.toContain('Test timeout of');
|
||||||
|
|
||||||
const interrupted2 = result.report.suites[1].specs[0];
|
const report = JSON.parse(fs.readFileSync(test.info().outputPath('report.json'), 'utf8'));
|
||||||
|
const interrupted2 = report.suites[1].specs[0];
|
||||||
expect(interrupted2.title).toBe('interrupted2');
|
expect(interrupted2.title).toBe('interrupted2');
|
||||||
expect(interrupted2.tests[0].results[0].workerIndex === 0 || interrupted2.tests[0].results[0].workerIndex === 1).toBe(true);
|
expect(interrupted2.tests[0].results[0].workerIndex === 0 || interrupted2.tests[0].results[0].workerIndex === 1).toBe(true);
|
||||||
|
|
||||||
const skipped2 = result.report.suites[1].specs[1];
|
const skipped2 = report.suites[1].specs[1];
|
||||||
expect(skipped2.title).toBe('skipped2');
|
expect(skipped2.title).toBe('skipped2');
|
||||||
expect(skipped2.tests[0].results[0].workerIndex).toBe(-1);
|
expect(skipped2.tests[0].results[0].workerIndex).toBe(-1);
|
||||||
});
|
});
|
||||||
@ -169,10 +180,10 @@ test('should use the first occurring error when an unhandled exception was throw
|
|||||||
expect(result.report.suites[0].specs[0].tests[0].results[0].error!.message).toBe('first error');
|
expect(result.report.suites[0].specs[0].tests[0].results[0].error!.message).toBe('first error');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('worker interrupt should report errors', async ({ runInlineTest }) => {
|
test('worker interrupt should report errors', async ({ interactWithTestRunner }) => {
|
||||||
test.skip(process.platform === 'win32', 'No sending SIGINT on Windows');
|
test.skip(process.platform === 'win32', 'No sending SIGINT on Windows');
|
||||||
|
|
||||||
const result = await runInlineTest({
|
const testProcess = await interactWithTestRunner({
|
||||||
'a.spec.ts': `
|
'a.spec.ts': `
|
||||||
import { test as base, expect } from '@playwright/test';
|
import { test as base, expect } from '@playwright/test';
|
||||||
const test = base.extend({
|
const test = base.extend({
|
||||||
@ -187,8 +198,13 @@ test('worker interrupt should report errors', async ({ runInlineTest }) => {
|
|||||||
await throwOnTeardown;
|
await throwOnTeardown;
|
||||||
});
|
});
|
||||||
`,
|
`,
|
||||||
}, {}, {}, { sendSIGINTAfter: 1 });
|
});
|
||||||
expect(result.exitCode).toBe(130);
|
await testProcess.waitForOutput('%%SEND-SIGINT%%');
|
||||||
|
process.kill(testProcess.process.pid!, 'SIGINT');
|
||||||
|
const { exitCode } = await testProcess.exited;
|
||||||
|
expect(exitCode).toBe(130);
|
||||||
|
|
||||||
|
const result = parseTestRunnerOutput(testProcess.output);
|
||||||
expect(result.passed).toBe(0);
|
expect(result.passed).toBe(0);
|
||||||
expect(result.failed).toBe(0);
|
expect(result.failed).toBe(0);
|
||||||
expect(result.interrupted).toBe(1);
|
expect(result.interrupted).toBe(1);
|
||||||
@ -334,10 +350,10 @@ test('should not hang if test suites in worker are inconsistent with runner', as
|
|||||||
expect(result.report.suites[0].specs[1].tests[0].results[0].error!.message).toBe('Test(s) not found in the worker process. Make sure test titles do not change:\nproject-name > a.spec.js > Test 1 - bar\nproject-name > a.spec.js > Test 2 - baz');
|
expect(result.report.suites[0].specs[1].tests[0].results[0].error!.message).toBe('Test(s) not found in the worker process. Make sure test titles do not change:\nproject-name > a.spec.js > Test 1 - bar\nproject-name > a.spec.js > Test 2 - baz');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('sigint should stop global setup', async ({ runInlineTest }) => {
|
test('sigint should stop global setup', async ({ interactWithTestRunner }) => {
|
||||||
test.skip(process.platform === 'win32', 'No sending SIGINT on Windows');
|
test.skip(process.platform === 'win32', 'No sending SIGINT on Windows');
|
||||||
|
|
||||||
const result = await runInlineTest({
|
const testProcess = await interactWithTestRunner({
|
||||||
'playwright.config.ts': `
|
'playwright.config.ts': `
|
||||||
module.exports = {
|
module.exports = {
|
||||||
globalSetup: './globalSetup',
|
globalSetup: './globalSetup',
|
||||||
@ -360,18 +376,22 @@ test('sigint should stop global setup', async ({ runInlineTest }) => {
|
|||||||
import { test, expect } from '@playwright/test';
|
import { test, expect } from '@playwright/test';
|
||||||
test('test', async () => { });
|
test('test', async () => { });
|
||||||
`,
|
`,
|
||||||
}, { 'workers': 1 }, {}, { sendSIGINTAfter: 1 });
|
}, { 'workers': 1 });
|
||||||
expect(result.exitCode).toBe(130);
|
await testProcess.waitForOutput('%%SEND-SIGINT%%');
|
||||||
|
process.kill(testProcess.process.pid!, 'SIGINT');
|
||||||
|
const { exitCode } = await testProcess.exited;
|
||||||
|
expect(exitCode).toBe(130);
|
||||||
|
|
||||||
|
const result = parseTestRunnerOutput(testProcess.output);
|
||||||
expect(result.passed).toBe(0);
|
expect(result.passed).toBe(0);
|
||||||
const output = result.output;
|
expect(result.output).toContain('Global setup');
|
||||||
expect(output).toContain('Global setup');
|
expect(result.output).not.toContain('Global teardown');
|
||||||
expect(output).not.toContain('Global teardown');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test('sigint should stop plugins', async ({ runInlineTest }) => {
|
test('sigint should stop plugins', async ({ interactWithTestRunner }) => {
|
||||||
test.skip(process.platform === 'win32', 'No sending SIGINT on Windows');
|
test.skip(process.platform === 'win32', 'No sending SIGINT on Windows');
|
||||||
|
|
||||||
const result = await runInlineTest({
|
const testProcess = await interactWithTestRunner({
|
||||||
'playwright.config.ts': `
|
'playwright.config.ts': `
|
||||||
const _plugins = [];
|
const _plugins = [];
|
||||||
_plugins.push(() => ({
|
_plugins.push(() => ({
|
||||||
@ -403,21 +423,25 @@ test('sigint should stop plugins', async ({ runInlineTest }) => {
|
|||||||
console.log('testing!');
|
console.log('testing!');
|
||||||
});
|
});
|
||||||
`,
|
`,
|
||||||
}, { 'workers': 1 }, {}, { sendSIGINTAfter: 1 });
|
}, { 'workers': 1 });
|
||||||
expect(result.exitCode).toBe(130);
|
await testProcess.waitForOutput('%%SEND-SIGINT%%');
|
||||||
|
process.kill(testProcess.process.pid!, 'SIGINT');
|
||||||
|
const { exitCode } = await testProcess.exited;
|
||||||
|
expect(exitCode).toBe(130);
|
||||||
|
|
||||||
|
const result = parseTestRunnerOutput(testProcess.output);
|
||||||
expect(result.passed).toBe(0);
|
expect(result.passed).toBe(0);
|
||||||
const output = result.output;
|
expect(result.output).toContain('Plugin1 setup');
|
||||||
expect(output).toContain('Plugin1 setup');
|
expect(result.output).toContain('Plugin1 teardown');
|
||||||
expect(output).toContain('Plugin1 teardown');
|
expect(result.output).not.toContain('Plugin2 setup');
|
||||||
expect(output).not.toContain('Plugin2 setup');
|
expect(result.output).not.toContain('Plugin2 teardown');
|
||||||
expect(output).not.toContain('Plugin2 teardown');
|
expect(result.output).not.toContain('testing!');
|
||||||
expect(output).not.toContain('testing!');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test('sigint should stop plugins 2', async ({ runInlineTest }) => {
|
test('sigint should stop plugins 2', async ({ interactWithTestRunner }) => {
|
||||||
test.skip(process.platform === 'win32', 'No sending SIGINT on Windows');
|
test.skip(process.platform === 'win32', 'No sending SIGINT on Windows');
|
||||||
|
|
||||||
const result = await runInlineTest({
|
const testProcess = await interactWithTestRunner({
|
||||||
'playwright.config.ts': `
|
'playwright.config.ts': `
|
||||||
const _plugins = [];
|
const _plugins = [];
|
||||||
_plugins.push(() => ({
|
_plugins.push(() => ({
|
||||||
@ -447,15 +471,19 @@ test('sigint should stop plugins 2', async ({ runInlineTest }) => {
|
|||||||
console.log('testing!');
|
console.log('testing!');
|
||||||
});
|
});
|
||||||
`,
|
`,
|
||||||
}, { 'workers': 1 }, {}, { sendSIGINTAfter: 1 });
|
}, { 'workers': 1 });
|
||||||
expect(result.exitCode).toBe(130);
|
await testProcess.waitForOutput('%%SEND-SIGINT%%');
|
||||||
|
process.kill(testProcess.process.pid!, 'SIGINT');
|
||||||
|
const { exitCode } = await testProcess.exited;
|
||||||
|
expect(exitCode).toBe(130);
|
||||||
|
|
||||||
|
const result = parseTestRunnerOutput(testProcess.output);
|
||||||
expect(result.passed).toBe(0);
|
expect(result.passed).toBe(0);
|
||||||
const output = result.output;
|
expect(result.output).toContain('Plugin1 setup');
|
||||||
expect(output).toContain('Plugin1 setup');
|
expect(result.output).toContain('Plugin2 setup');
|
||||||
expect(output).toContain('Plugin2 setup');
|
expect(result.output).toContain('Plugin1 teardown');
|
||||||
expect(output).toContain('Plugin1 teardown');
|
expect(result.output).toContain('Plugin2 teardown');
|
||||||
expect(output).toContain('Plugin2 teardown');
|
expect(result.output).not.toContain('testing!');
|
||||||
expect(output).not.toContain('testing!');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should not crash with duplicate titles and .only', async ({ runInlineTest }) => {
|
test('should not crash with duplicate titles and .only', async ({ runInlineTest }) => {
|
||||||
|
|||||||
@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
import type http from 'http';
|
import type http from 'http';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import { test, expect } from './playwright-test-fixtures';
|
import { test, expect, parseTestRunnerOutput } from './playwright-test-fixtures';
|
||||||
import { createHttpServer } from '../../packages/playwright-core/lib/utils/network';
|
import { createHttpServer } from '../../packages/playwright-core/lib/utils/network';
|
||||||
|
|
||||||
const SIMPLE_SERVER_PATH = path.join(__dirname, 'assets', 'simple-server.js');
|
const SIMPLE_SERVER_PATH = path.join(__dirname, 'assets', 'simple-server.js');
|
||||||
@ -705,10 +705,10 @@ test('should be able to ignore "stderr"', async ({ runInlineTest }, { workerInde
|
|||||||
expect(result.output).not.toContain('error from server');
|
expect(result.output).not.toContain('error from server');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should forward stdout when set to "pipe" before server is ready', async ({ runInlineTest }, { workerIndex }) => {
|
test('should forward stdout when set to "pipe" before server is ready', async ({ interactWithTestRunner }) => {
|
||||||
test.skip(process.platform === 'win32', 'No sending SIGINT on Windows');
|
test.skip(process.platform === 'win32', 'No sending SIGINT on Windows');
|
||||||
|
|
||||||
const result = await runInlineTest({
|
const testProcess = await interactWithTestRunner({
|
||||||
'web-server.js': `
|
'web-server.js': `
|
||||||
console.log('output from server');
|
console.log('output from server');
|
||||||
console.log('\\n%%SEND-SIGINT%%');
|
console.log('\\n%%SEND-SIGINT%%');
|
||||||
@ -728,7 +728,12 @@ test('should forward stdout when set to "pipe" before server is ready', async ({
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
`,
|
`,
|
||||||
}, { workers: 1 }, {}, { sendSIGINTAfter: 1 });
|
}, { workers: 1 });
|
||||||
|
await testProcess.waitForOutput('%%SEND-SIGINT%%');
|
||||||
|
process.kill(testProcess.process.pid!, 'SIGINT');
|
||||||
|
await testProcess.exited;
|
||||||
|
|
||||||
|
const result = parseTestRunnerOutput(testProcess.output);
|
||||||
expect(result.passed).toBe(0);
|
expect(result.passed).toBe(0);
|
||||||
expect(result.output).toContain('[WebServer] output from server');
|
expect(result.output).toContain('[WebServer] output from server');
|
||||||
expect(result.output).not.toContain('Timed out waiting 3000ms');
|
expect(result.output).not.toContain('Timed out waiting 3000ms');
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user