mirror of
https://github.com/hanzoai/playwright.git
synced 2026-09-24 17:01:53 +00:00
239 lines
8.2 KiB
TypeScript
239 lines
8.2 KiB
TypeScript
/**
|
|
* Copyright Microsoft Corporation. All rights reserved.
|
|
*
|
|
* 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 { test, expect } from './playwright-test-fixtures';
|
|
|
|
// Given '%%begin:<name>' and '%%end:<name>' lines, returns pairs from the
|
|
// `conflicts` list that were running at the same time.
|
|
function conflictingOverlaps(lines: string[], conflicts: [string, string][]): [string, string][] {
|
|
const running = new Set<string>();
|
|
const overlaps: [string, string][] = [];
|
|
for (const line of lines) {
|
|
const [kind, name] = line.split(':');
|
|
if (kind === 'begin') {
|
|
for (const [x, y] of conflicts) {
|
|
if ((name === x && running.has(y)) || (name === y && running.has(x)))
|
|
overlaps.push([x, y]);
|
|
}
|
|
running.add(name);
|
|
} else if (kind === 'end') {
|
|
running.delete(name);
|
|
}
|
|
}
|
|
return overlaps;
|
|
}
|
|
|
|
const lockedTest = (name: string, delay: number, lock?: string | string[]) => `
|
|
test('${name}'${lock !== undefined ? `, { lock: ${JSON.stringify(lock)} }` : ''}, async () => {
|
|
console.log('\\n%%begin:${name}');
|
|
await new Promise(f => setTimeout(f, ${delay}));
|
|
console.log('\\n%%end:${name}');
|
|
});
|
|
`;
|
|
|
|
test('should not run tests with the same lock at the same time', async ({ runInlineTest }) => {
|
|
const result = await runInlineTest({
|
|
'playwright.config.ts': `
|
|
module.exports = { fullyParallel: true };
|
|
`,
|
|
'a.test.ts': `
|
|
import { test } from '@playwright/test';
|
|
${lockedTest('test1', 1000, 'shared')}
|
|
`,
|
|
'b.test.ts': `
|
|
import { test } from '@playwright/test';
|
|
${lockedTest('test2', 1000, 'shared')}
|
|
`,
|
|
}, { workers: 2 });
|
|
expect(result.exitCode).toBe(0);
|
|
expect(result.passed).toBe(2);
|
|
expect(conflictingOverlaps(result.outputLines, [['test1', 'test2']])).toEqual([]);
|
|
});
|
|
|
|
test('should run tests with different locks at the same time', async ({ runInlineTest }) => {
|
|
const result = await runInlineTest({
|
|
'playwright.config.ts': `
|
|
module.exports = { fullyParallel: true };
|
|
`,
|
|
'helper.ts': `
|
|
import fs from 'fs';
|
|
import path from 'path';
|
|
export async function signalAndWait(signal: string, waitFor: string) {
|
|
fs.mkdirSync(process.env.SIGNAL_DIR, { recursive: true });
|
|
fs.writeFileSync(path.join(process.env.SIGNAL_DIR, signal), '');
|
|
while (!fs.existsSync(path.join(process.env.SIGNAL_DIR, waitFor)))
|
|
await new Promise(f => setTimeout(f, 100));
|
|
}
|
|
`,
|
|
'a.test.ts': `
|
|
import { test } from '@playwright/test';
|
|
import { signalAndWait } from './helper';
|
|
test('test1', { lock: 'lock-a' }, async () => {
|
|
// Only finishes when both tests run at the same time.
|
|
await signalAndWait('a.txt', 'b.txt');
|
|
});
|
|
`,
|
|
'b.test.ts': `
|
|
import { test } from '@playwright/test';
|
|
import { signalAndWait } from './helper';
|
|
test('test2', { lock: 'lock-b' }, async () => {
|
|
await signalAndWait('b.txt', 'a.txt');
|
|
});
|
|
`,
|
|
}, { workers: 2 }, { SIGNAL_DIR: test.info().outputDir });
|
|
expect(result.exitCode).toBe(0);
|
|
expect(result.passed).toBe(2);
|
|
});
|
|
|
|
test('should not run tests with the same lock from different projects at the same time', async ({ runInlineTest }) => {
|
|
const result = await runInlineTest({
|
|
'playwright.config.ts': `
|
|
module.exports = {
|
|
projects: [
|
|
{ name: 'project1' },
|
|
{ name: 'project2' },
|
|
],
|
|
};
|
|
`,
|
|
'a.test.ts': `
|
|
import { test } from '@playwright/test';
|
|
test('test1', { lock: 'shared' }, async ({}, testInfo) => {
|
|
console.log('\\n%%begin:' + testInfo.project.name);
|
|
await new Promise(f => setTimeout(f, 1000));
|
|
console.log('\\n%%end:' + testInfo.project.name);
|
|
});
|
|
`,
|
|
}, { workers: 2 });
|
|
expect(result.exitCode).toBe(0);
|
|
expect(result.passed).toBe(2);
|
|
expect(conflictingOverlaps(result.outputLines, [['project1', 'project2']])).toEqual([]);
|
|
});
|
|
|
|
test('should support locks declared on a describe group', async ({ runInlineTest }) => {
|
|
const result = await runInlineTest({
|
|
'playwright.config.ts': `
|
|
module.exports = { fullyParallel: true };
|
|
`,
|
|
'a.test.ts': `
|
|
import { test } from '@playwright/test';
|
|
test.describe('locked suite', { lock: 'shared' }, () => {
|
|
${lockedTest('test1', 1000)}
|
|
${lockedTest('test2', 1000)}
|
|
});
|
|
`,
|
|
}, { workers: 2 });
|
|
expect(result.exitCode).toBe(0);
|
|
expect(result.passed).toBe(2);
|
|
expect(conflictingOverlaps(result.outputLines, [['test1', 'test2']])).toEqual([]);
|
|
});
|
|
|
|
test('should support multiple locks on a single test', async ({ runInlineTest }) => {
|
|
const result = await runInlineTest({
|
|
'playwright.config.ts': `
|
|
module.exports = { fullyParallel: true };
|
|
`,
|
|
'a.test.ts': `
|
|
import { test } from '@playwright/test';
|
|
${lockedTest('test1', 1000, ['lock-a', 'lock-b'])}
|
|
`,
|
|
'b.test.ts': `
|
|
import { test } from '@playwright/test';
|
|
${lockedTest('test2', 1000, 'lock-a')}
|
|
`,
|
|
'c.test.ts': `
|
|
import { test } from '@playwright/test';
|
|
${lockedTest('test3', 1000, 'lock-b')}
|
|
`,
|
|
}, { workers: 3 });
|
|
expect(result.exitCode).toBe(0);
|
|
expect(result.passed).toBe(3);
|
|
expect(conflictingOverlaps(result.outputLines, [['test1', 'test2'], ['test1', 'test3']])).toEqual([]);
|
|
});
|
|
|
|
test('should hold the lock for the whole file group in default mode', async ({ runInlineTest }) => {
|
|
const result = await runInlineTest({
|
|
'a.test.ts': `
|
|
import { test } from '@playwright/test';
|
|
${lockedTest('a1', 500, 'shared')}
|
|
${lockedTest('a2', 500)}
|
|
`,
|
|
'b.test.ts': `
|
|
import { test } from '@playwright/test';
|
|
${lockedTest('b1', 1000, 'shared')}
|
|
`,
|
|
}, { workers: 2 });
|
|
expect(result.exitCode).toBe(0);
|
|
expect(result.passed).toBe(3);
|
|
// The lock declared on a1 covers the whole file, including a2.
|
|
expect(conflictingOverlaps(result.outputLines, [['a1', 'b1'], ['a2', 'b1']])).toEqual([]);
|
|
});
|
|
|
|
test('should respect locks on tests from a parallel suite with beforeAll hooks', async ({ runInlineTest }) => {
|
|
const result = await runInlineTest({
|
|
'playwright.config.ts': `
|
|
module.exports = { fullyParallel: true };
|
|
`,
|
|
'a.test.ts': `
|
|
import { test } from '@playwright/test';
|
|
test.beforeAll(() => {
|
|
console.log('\\n%%beforeAll');
|
|
});
|
|
test('plain1', async () => {});
|
|
test('plain2', async () => {});
|
|
${lockedTest('test1', 1000, 'shared')}
|
|
`,
|
|
'b.test.ts': `
|
|
import { test } from '@playwright/test';
|
|
${lockedTest('test2', 1000, 'shared')}
|
|
`,
|
|
}, { workers: 2 });
|
|
expect(result.exitCode).toBe(0);
|
|
expect(result.passed).toBe(4);
|
|
expect(result.output).toContain('%%beforeAll');
|
|
expect(conflictingOverlaps(result.outputLines, [['test1', 'test2']])).toEqual([]);
|
|
});
|
|
|
|
test('should not count waiting for a lock towards the test timeout', async ({ runInlineTest }) => {
|
|
const result = await runInlineTest({
|
|
'playwright.config.ts': `
|
|
module.exports = { timeout: 3000 };
|
|
`,
|
|
'a.test.ts': `
|
|
import { test } from '@playwright/test';
|
|
${lockedTest('test1', 2000, 'shared')}
|
|
`,
|
|
'b.test.ts': `
|
|
import { test } from '@playwright/test';
|
|
${lockedTest('test2', 2000, 'shared')}
|
|
`,
|
|
}, { workers: 2 });
|
|
// Together the tests exceed the 3000ms timeout; waiting for the lock is not test time.
|
|
expect(result.exitCode).toBe(0);
|
|
expect(result.passed).toBe(2);
|
|
expect(conflictingOverlaps(result.outputLines, [['test1', 'test2']])).toEqual([]);
|
|
});
|
|
|
|
test('should validate lock in test details', async ({ runInlineTest }) => {
|
|
const result = await runInlineTest({
|
|
'a.test.ts': `
|
|
import { test } from '@playwright/test';
|
|
test('test1', { lock: 42 }, async () => {});
|
|
`,
|
|
});
|
|
expect(result.exitCode).toBe(1);
|
|
expect(result.output).toContain('details.lock');
|
|
});
|