feat(ui): run deps in UI mode if dep projects are checked (#24245)

This commit is contained in:
Pavel Feldman 2023-07-15 15:11:31 -07:00 committed by GitHub
parent d92db9a513
commit 49c1f9eb02
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 111 additions and 15 deletions

View file

@ -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);
} }
} }

View file

@ -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.

View file

@ -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');

View file

@ -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;

View file

@ -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
View file