feat(ui): run deps in UI mode if dep projects are checked (#24245)
This commit is contained in:
parent
d92db9a513
commit
49c1f9eb02
|
|
@ -32,7 +32,7 @@ import { dependenciesForTestFile } from '../transform/compilationCache';
|
||||||
import { sourceMapSupport } from '../utilsBundle';
|
import { sourceMapSupport } from '../utilsBundle';
|
||||||
import type { RawSourceMap } from 'source-map';
|
import type { RawSourceMap } from 'source-map';
|
||||||
|
|
||||||
export async function collectProjectsAndTestFiles(testRun: TestRun, additionalFileMatcher: Matcher | undefined) {
|
export async function collectProjectsAndTestFiles(testRun: TestRun, doNotRunTestsOutsideProjectFilter: boolean, additionalFileMatcher: Matcher | undefined) {
|
||||||
const config = testRun.config;
|
const config = testRun.config;
|
||||||
const fsCache = new Map();
|
const fsCache = new Map();
|
||||||
const sourceMapCache = new Map();
|
const sourceMapCache = new Map();
|
||||||
|
|
@ -40,7 +40,8 @@ export async function collectProjectsAndTestFiles(testRun: TestRun, additionalFi
|
||||||
|
|
||||||
// First collect all files for the projects in the command line, don't apply any file filters.
|
// First collect all files for the projects in the command line, don't apply any file filters.
|
||||||
const allFilesForProject = new Map<FullProjectInternal, string[]>();
|
const allFilesForProject = new Map<FullProjectInternal, string[]>();
|
||||||
for (const project of filterProjects(config.projects, config.cliProjectFilter)) {
|
const filteredProjects = filterProjects(config.projects, config.cliProjectFilter);
|
||||||
|
for (const project of filteredProjects) {
|
||||||
const files = await collectFilesForProject(project, fsCache);
|
const files = await collectFilesForProject(project, fsCache);
|
||||||
allFilesForProject.set(project, files);
|
allFilesForProject.set(project, files);
|
||||||
}
|
}
|
||||||
|
|
@ -68,7 +69,8 @@ export async function collectProjectsAndTestFiles(testRun: TestRun, additionalFi
|
||||||
for (const [project, type] of projectClosure) {
|
for (const [project, type] of projectClosure) {
|
||||||
if (type === 'dependency') {
|
if (type === 'dependency') {
|
||||||
filesToRunByProject.delete(project);
|
filesToRunByProject.delete(project);
|
||||||
const files = allFilesForProject.get(project) || await collectFilesForProject(project, fsCache);
|
const treatProjectAsEmpty = doNotRunTestsOutsideProjectFilter && !filteredProjects.includes(project);
|
||||||
|
const files = treatProjectAsEmpty ? [] : allFilesForProject.get(project) || await collectFilesForProject(project, fsCache);
|
||||||
filesToRunByProject.set(project, files);
|
filesToRunByProject.set(project, files);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -76,7 +76,7 @@ export function createTaskRunnerForWatchSetup(config: FullConfigInternal, report
|
||||||
|
|
||||||
export function createTaskRunnerForWatch(config: FullConfigInternal, reporter: ReporterV2, additionalFileMatcher?: Matcher): TaskRunner<TestRun> {
|
export function createTaskRunnerForWatch(config: FullConfigInternal, reporter: ReporterV2, additionalFileMatcher?: Matcher): TaskRunner<TestRun> {
|
||||||
const taskRunner = new TaskRunner<TestRun>(reporter, 0);
|
const taskRunner = new TaskRunner<TestRun>(reporter, 0);
|
||||||
taskRunner.addTask('load tests', createLoadTask('out-of-process', { filterOnly: true, failOnLoadErrors: false, additionalFileMatcher }));
|
taskRunner.addTask('load tests', createLoadTask('out-of-process', { filterOnly: true, failOnLoadErrors: false, doNotRunTestsOutsideProjectFilter: true, additionalFileMatcher }));
|
||||||
addRunTasks(taskRunner, config);
|
addRunTasks(taskRunner, config);
|
||||||
return taskRunner;
|
return taskRunner;
|
||||||
}
|
}
|
||||||
|
|
@ -171,9 +171,9 @@ function createRemoveOutputDirsTask(): Task<TestRun> {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function createLoadTask(mode: 'out-of-process' | 'in-process', options: { filterOnly: boolean, failOnLoadErrors: boolean, additionalFileMatcher?: Matcher }): Task<TestRun> {
|
function createLoadTask(mode: 'out-of-process' | 'in-process', options: { filterOnly: boolean, failOnLoadErrors: boolean, doNotRunTestsOutsideProjectFilter?: boolean, additionalFileMatcher?: Matcher }): Task<TestRun> {
|
||||||
return async (testRun, errors, softErrors) => {
|
return async (testRun, errors, softErrors) => {
|
||||||
await collectProjectsAndTestFiles(testRun, options.additionalFileMatcher);
|
await collectProjectsAndTestFiles(testRun, !!options.doNotRunTestsOutsideProjectFilter, options.additionalFileMatcher);
|
||||||
await loadFileSuites(testRun, mode, options.failOnLoadErrors ? errors : softErrors);
|
await loadFileSuites(testRun, mode, options.failOnLoadErrors ? errors : softErrors);
|
||||||
testRun.rootSuite = await createRootSuite(testRun, options.failOnLoadErrors ? errors : softErrors, !!options.filterOnly);
|
testRun.rootSuite = await createRootSuite(testRun, options.failOnLoadErrors ? errors : softErrors, !!options.filterOnly);
|
||||||
// Fail when no tests.
|
// Fail when no tests.
|
||||||
|
|
|
||||||
|
|
@ -45,10 +45,6 @@ class UIMode {
|
||||||
process.env.PW_LIVE_TRACE_STACKS = '1';
|
process.env.PW_LIVE_TRACE_STACKS = '1';
|
||||||
config.cliListOnly = false;
|
config.cliListOnly = false;
|
||||||
config.cliPassWithNoTests = true;
|
config.cliPassWithNoTests = true;
|
||||||
for (const project of config.projects) {
|
|
||||||
project.deps = [];
|
|
||||||
project.teardown = undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const p of config.projects) {
|
for (const p of config.projects) {
|
||||||
p.project.retries = 0;
|
p.project.retries = 0;
|
||||||
|
|
@ -153,7 +149,7 @@ class UIMode {
|
||||||
if (method === 'list')
|
if (method === 'list')
|
||||||
await this._listTests();
|
await this._listTests();
|
||||||
if (method === 'run')
|
if (method === 'run')
|
||||||
await this._runTests(params.testIds);
|
await this._runTests(params.testIds, params.projects);
|
||||||
}
|
}
|
||||||
|
|
||||||
private _dispatchEvent(method: string, params?: any) {
|
private _dispatchEvent(method: string, params?: any) {
|
||||||
|
|
@ -178,11 +174,12 @@ class UIMode {
|
||||||
this._globalWatcher.update([...projectDirs], false);
|
this._globalWatcher.update([...projectDirs], false);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _runTests(testIds: string[]) {
|
private async _runTests(testIds: string[], projects: string[]) {
|
||||||
await this._stopTests();
|
await this._stopTests();
|
||||||
|
|
||||||
const testIdSet = testIds ? new Set<string>(testIds) : null;
|
const testIdSet = testIds ? new Set<string>(testIds) : null;
|
||||||
this._config.cliListOnly = false;
|
this._config.cliListOnly = false;
|
||||||
|
this._config.cliProjectFilter = projects.length ? projects : undefined;
|
||||||
this._config.testIdMatcher = id => !testIdSet || testIdSet.has(id);
|
this._config.testIdMatcher = id => !testIdSet || testIdSet.has(id);
|
||||||
|
|
||||||
const reporters = await createReporters(this._config, 'ui');
|
const reporters = await createReporters(this._config, 'ui');
|
||||||
|
|
|
||||||
|
|
@ -153,7 +153,7 @@ export const UIModeView: React.FC<{}> = ({
|
||||||
setProgress({ total: testIds.size, passed: 0, failed: 0, skipped: 0 });
|
setProgress({ total: testIds.size, passed: 0, failed: 0, skipped: 0 });
|
||||||
setRunningState({ testIds });
|
setRunningState({ testIds });
|
||||||
|
|
||||||
await sendMessage('run', { testIds: [...testIds] });
|
await sendMessage('run', { testIds: [...testIds], projects: [...projectFilters].filter(([_, v]) => v).map(([p]) => p) });
|
||||||
// Clear pending tests in case of interrupt.
|
// Clear pending tests in case of interrupt.
|
||||||
for (const test of testModel.rootSuite?.allTests() || []) {
|
for (const test of testModel.rootSuite?.allTests() || []) {
|
||||||
if (test.results[0]?.duration === -1)
|
if (test.results[0]?.duration === -1)
|
||||||
|
|
@ -162,7 +162,7 @@ export const UIModeView: React.FC<{}> = ({
|
||||||
setTestModel({ ...testModel });
|
setTestModel({ ...testModel });
|
||||||
setRunningState(undefined);
|
setRunningState(undefined);
|
||||||
});
|
});
|
||||||
}, [runningState, testModel]);
|
}, [projectFilters, runningState, testModel]);
|
||||||
|
|
||||||
const isRunningTest = !!runningState;
|
const isRunningTest = !!runningState;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { test, expect, retries } from './ui-mode-fixtures';
|
import { test, expect, retries, dumpTestTree } from './ui-mode-fixtures';
|
||||||
|
|
||||||
test.describe.configure({ mode: 'parallel', retries });
|
test.describe.configure({ mode: 'parallel', retries });
|
||||||
|
|
||||||
|
|
@ -76,3 +76,100 @@ test('should teardown on sigint', async ({ runUITest }) => {
|
||||||
'from-global-teardown',
|
'from-global-teardown',
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const testsWithSetup = {
|
||||||
|
'playwright.config.ts': `
|
||||||
|
import { defineConfig } from '@playwright/test';
|
||||||
|
export default defineConfig({
|
||||||
|
projects: [
|
||||||
|
{ name: 'setup', teardown: 'teardown', testMatch: 'setup.ts' },
|
||||||
|
{ name: 'test', testMatch: 'test.ts', dependencies: ['setup'] },
|
||||||
|
{ name: 'teardown', testMatch: 'teardown.ts' },
|
||||||
|
]
|
||||||
|
});
|
||||||
|
`,
|
||||||
|
'setup.ts': `
|
||||||
|
import { test, expect } from '@playwright/test';
|
||||||
|
test('setup', async ({}) => {
|
||||||
|
console.log('from-setup');
|
||||||
|
});
|
||||||
|
`,
|
||||||
|
'test.ts': `
|
||||||
|
import { test, expect } from '@playwright/test';
|
||||||
|
test('test', async ({}) => {
|
||||||
|
console.log('from-test');
|
||||||
|
});
|
||||||
|
`,
|
||||||
|
'teardown.ts': `
|
||||||
|
import { test, expect } from '@playwright/test';
|
||||||
|
test('teardown', async ({}) => {
|
||||||
|
console.log('from-teardown');
|
||||||
|
});
|
||||||
|
`,
|
||||||
|
};
|
||||||
|
|
||||||
|
test('should run setup and teardown projects (1)', async ({ runUITest }) => {
|
||||||
|
const { page } = await runUITest(testsWithSetup);
|
||||||
|
await page.getByText('Status:').click();
|
||||||
|
await page.getByLabel('setup').setChecked(false);
|
||||||
|
await page.getByLabel('teardown').setChecked(false);
|
||||||
|
await page.getByLabel('test').setChecked(false);
|
||||||
|
|
||||||
|
await page.getByTitle('Run all').click();
|
||||||
|
|
||||||
|
await expect.poll(dumpTestTree(page)).toBe(`
|
||||||
|
▼ ✅ setup.ts
|
||||||
|
✅ setup
|
||||||
|
▼ ✅ teardown.ts
|
||||||
|
✅ teardown
|
||||||
|
▼ ✅ test.ts
|
||||||
|
✅ test
|
||||||
|
`);
|
||||||
|
|
||||||
|
await page.getByTitle('Toggle output').click();
|
||||||
|
await expect(page.getByTestId('output')).toContainText(`from-setup`);
|
||||||
|
await expect(page.getByTestId('output')).toContainText(`from-test`);
|
||||||
|
await expect(page.getByTestId('output')).toContainText(`from-teardown`);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should run setup and teardown projects (2)', async ({ runUITest }) => {
|
||||||
|
const { page } = await runUITest(testsWithSetup);
|
||||||
|
await page.getByText('Status:').click();
|
||||||
|
await page.getByLabel('setup').setChecked(false);
|
||||||
|
await page.getByLabel('teardown').setChecked(true);
|
||||||
|
await page.getByLabel('test').setChecked(true);
|
||||||
|
|
||||||
|
await page.getByTitle('Run all').click();
|
||||||
|
|
||||||
|
await expect.poll(dumpTestTree(page)).toBe(`
|
||||||
|
▼ ✅ teardown.ts
|
||||||
|
✅ teardown
|
||||||
|
▼ ✅ test.ts
|
||||||
|
✅ test
|
||||||
|
`);
|
||||||
|
|
||||||
|
await page.getByTitle('Toggle output').click();
|
||||||
|
await expect(page.getByTestId('output')).toContainText(`from-test`);
|
||||||
|
await expect(page.getByTestId('output')).toContainText(`from-teardown`);
|
||||||
|
await expect(page.getByTestId('output')).not.toContainText(`from-setup`);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should run setup and teardown projects (3)', async ({ runUITest }) => {
|
||||||
|
const { page } = await runUITest(testsWithSetup);
|
||||||
|
await page.getByText('Status:').click();
|
||||||
|
await page.getByLabel('setup').setChecked(false);
|
||||||
|
await page.getByLabel('teardown').setChecked(false);
|
||||||
|
await page.getByLabel('test').setChecked(true);
|
||||||
|
|
||||||
|
await page.getByTitle('Run all').click();
|
||||||
|
|
||||||
|
await expect.poll(dumpTestTree(page)).toBe(`
|
||||||
|
▼ ✅ test.ts
|
||||||
|
✅ test
|
||||||
|
`);
|
||||||
|
|
||||||
|
await page.getByTitle('Toggle output').click();
|
||||||
|
await expect(page.getByTestId('output')).toContainText(`from-test`);
|
||||||
|
await expect(page.getByTestId('output')).not.toContainText(`from-setup`);
|
||||||
|
await expect(page.getByTestId('output')).not.toContainText(`from-teardown`);
|
||||||
|
});
|
||||||
|
|
|
||||||
0
utils/limits.sh
Normal file → Executable file
0
utils/limits.sh
Normal file → Executable file
Loading…
Reference in a new issue