playwright/tests/playwright-test/global-scripts.spec.ts
Yury Semikhatsky 730a197c80
feat: config.globalScripts (#20062)
Introduce config.globalScripts. Tests from the matching files will run
before all projects. We'll only allow beforeAll/afterAll instead of
tests in such files (next PR).

Global scripts are executed as part of 'Global Scripts' project which is
not present in FullConfig.projects but may be referenced by
corresponding global setup Suites.

Signed-off-by: Yury Semikhatsky <yurys@chromium.org>
Co-authored-by: Dmitry Gozman <dgozman@gmail.com>
2023-01-12 13:02:54 -08:00

226 lines
6.6 KiB
TypeScript

/**
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import path from 'path';
import { test, expect } from './playwright-test-fixtures';
type Timeline = { titlePath: string[], event: 'begin' | 'end' }[];
function formatTimeline(timeline: Timeline) {
return timeline.map(e => `${e.titlePath.slice(1).join(' > ')} [${e.event}]`).join('\n');
}
function formatFileNames(timeline: Timeline) {
return timeline.map(e => e.titlePath[2]).join('\n');
}
function fileNames(timeline: Timeline) {
const fileNames = Array.from(new Set(timeline.map(({ titlePath }) => {
const name = titlePath[2];
const index = name.lastIndexOf(path.sep);
if (index === -1)
return name;
return name.slice(index + 1);
})).keys());
fileNames.sort();
return fileNames;
}
function expectFilesRunBefore(timeline: Timeline, before: string[], after: string[]) {
const fileBegin = name => {
const index = timeline.findIndex(({ titlePath }) => titlePath[2] === name);
expect(index, `cannot find ${name} in\n${formatFileNames(timeline)}`).not.toBe(-1);
return index;
};
const fileEnd = name => {
// There is no Array.findLastIndex in Node < 18.
let index = -1;
for (index = timeline.length - 1; index >= 0; index--) {
if (timeline[index].titlePath[2] === name)
break;
}
expect(index, `cannot find ${name} in\n${formatFileNames(timeline)}`).not.toBe(-1);
return index;
};
for (const b of before) {
const bEnd = fileEnd(b);
for (const a of after) {
const aBegin = fileBegin(a);
expect(bEnd < aBegin, `'${b}' expected to finish before ${a}, actual order:\n${formatTimeline(timeline)}`).toBeTruthy();
}
}
}
test('should work for one project', async ({ runGroups }, testInfo) => {
const files = {
'playwright.config.ts': `
module.exports = {
globalScripts: /.*global.ts/,
projects: [
{
name: 'p1',
testMatch: /.*.test.ts/,
},
]
};`,
'a.test.ts': `
const { test } = pwt;
test('test1', async () => { });
test('test2', async () => { });
`,
'global.ts': `
const { test } = pwt;
test('setup1', async () => { });
test('setup2', async () => { });
`,
};
const { exitCode, passed, timeline } = await runGroups(files);
expect(exitCode).toBe(0);
expect(passed).toBe(4);
expect(formatTimeline(timeline)).toEqual(`Global Scripts > global.ts > setup1 [begin]
Global Scripts > global.ts > setup1 [end]
Global Scripts > global.ts > setup2 [begin]
Global Scripts > global.ts > setup2 [end]
p1 > a.test.ts > test1 [begin]
p1 > a.test.ts > test1 [end]
p1 > a.test.ts > test2 [begin]
p1 > a.test.ts > test2 [end]`);
});
test('should work for several projects', async ({ runGroups }, testInfo) => {
const files = {
'playwright.config.ts': `
module.exports = {
globalScripts: /.*global.ts/,
projects: [
{
name: 'p1',
testMatch: /.*a.test.ts/,
},
{
name: 'p2',
testMatch: /.*b.test.ts/,
},
]
};`,
'a.test.ts': `
const { test } = pwt;
test('test1', async () => { });
test('test2', async () => { });
`,
'b.test.ts': `
const { test } = pwt;
test('test1', async () => { });
test('test2', async () => { });
`,
'global.ts': `
const { test } = pwt;
test('setup1', async () => { });
test('setup2', async () => { });
`,
};
const { exitCode, passed, timeline } = await runGroups(files);
expect(exitCode).toBe(0);
expect(passed).toBe(6);
expectFilesRunBefore(timeline, [`global.ts`], [`a.test.ts`, `b.test.ts`]);
});
test('should skip tests if global setup fails', async ({ runGroups }, testInfo) => {
const files = {
'playwright.config.ts': `
module.exports = {
globalScripts: /.*global.ts/,
projects: [
{
name: 'p1',
testMatch: /.*a.test.ts/,
},
{
name: 'p2',
testMatch: /.*b.test.ts/,
},
]
};`,
'a.test.ts': `
const { test } = pwt;
test('test1', async () => { });
test('test2', async () => { });
`,
'b.test.ts': `
const { test } = pwt;
test('test1', async () => { });
`,
'global.ts': `
const { test, expect } = pwt;
test('setup1', async () => { });
test('setup2', async () => { expect(1).toBe(2) });
`,
};
const { exitCode, passed, skipped } = await runGroups(files);
expect(exitCode).toBe(1);
expect(passed).toBe(1);
expect(skipped).toBe(3);
});
test('should run setup in each project shard', async ({ runGroups }, testInfo) => {
const files = {
'playwright.config.ts': `
module.exports = {
globalScripts: /.*global.ts/,
projects: [
{
name: 'p1',
},
]
};`,
'a.test.ts': `
const { test } = pwt;
test('test1', async () => { });
test('test2', async () => { });
test('test3', async () => { });
test('test4', async () => { });
`,
'b.test.ts': `
const { test } = pwt;
test('test1', async () => { });
test('test2', async () => { });
`,
'global.ts': `
const { test, expect } = pwt;
test('setup1', async () => { });
test('setup2', async () => { });
`,
};
{ // Shard 1/2
const { exitCode, passed, timeline, output } = await runGroups(files, { shard: '1/2' });
expect(output).toContain('Running 6 tests using 1 worker, shard 1 of 2');
expect(fileNames(timeline)).toEqual(['a.test.ts', 'global.ts']);
expectFilesRunBefore(timeline, [`global.ts`], [`a.test.ts`]);
expect(exitCode).toBe(0);
expect(passed).toBe(6);
}
{ // Shard 2/2
const { exitCode, passed, timeline, output } = await runGroups(files, { shard: '2/2' });
expect(output).toContain('Running 4 tests using 1 worker, shard 2 of 2');
expect(fileNames(timeline)).toEqual(['b.test.ts', 'global.ts']);
expectFilesRunBefore(timeline, [`global.ts`], [`b.test.ts`]);
expect(exitCode).toBe(0);
expect(passed).toBe(4);
}
});