fix(test): make use in config accept option values only (#8828)

Also include default options in FullConfig/FullProject.
Also make examples compile and add a test.
This commit is contained in:
Dmitry Gozman 2021-09-13 17:50:31 -07:00 committed by GitHub
parent ed34a67d4a
commit d9d2d809a2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 66 additions and 54 deletions

View File

@ -193,11 +193,11 @@ async function globalSetup(config: FullConfig) {
const { baseURL, storageState } = config.projects[0].use; const { baseURL, storageState } = config.projects[0].use;
const browser = await chromium.launch(); const browser = await chromium.launch();
const page = await browser.newPage(); const page = await browser.newPage();
await page.goto(baseURL); await page.goto(baseURL!);
await page.fill('input[name="user"]', 'user'); await page.fill('input[name="user"]', 'user');
await page.fill('input[name="password"]', 'password'); await page.fill('input[name="password"]', 'password');
await page.click('text=Sign in'); await page.click('text=Sign in');
await page.context().storageState({ path: storageState }); await page.context().storageState({ path: storageState as string });
await browser.close(); await browser.close();
} }

View File

@ -301,11 +301,11 @@ async function globalSetup(config: FullConfig) {
const { baseURL, storageState } = config.projects[0].use; const { baseURL, storageState } = config.projects[0].use;
const browser = await chromium.launch(); const browser = await chromium.launch();
const page = await browser.newPage(); const page = await browser.newPage();
await page.goto(baseURL); await page.goto(baseURL!);
await page.fill('input[name="user"]', 'user'); await page.fill('input[name="user"]', 'user');
await page.fill('input[name="password"]', 'password'); await page.fill('input[name="password"]', 'password');
await page.click('text=Sign in'); await page.click('text=Sign in');
await page.context().storageState({ path: storageState }); await page.context().storageState({ path: storageState as string });
await browser.close(); await browser.close();
} }

View File

@ -15,7 +15,6 @@
*/ */
import { Locator, Page } from '../../..'; import { Locator, Page } from '../../..';
import { PlaywrightTestOptions} from '../../../types/test';
import { constructURLBasedOnBaseURL } from '../../utils/utils'; import { constructURLBasedOnBaseURL } from '../../utils/utils';
import { currentTestInfo } from '../globals'; import { currentTestInfo } from '../globals';
import type { Expect } from '../types'; import type { Expect } from '../types';
@ -240,7 +239,7 @@ export function toHaveURL(
const testInfo = currentTestInfo(); const testInfo = currentTestInfo();
if (!testInfo) if (!testInfo)
throw new Error(`toHaveURL must be called during the test`); throw new Error(`toHaveURL must be called during the test`);
const baseURL = (testInfo.project.use as PlaywrightTestOptions)?.baseURL; const baseURL = testInfo.project.use.baseURL;
return toMatchText.call(this, 'toHaveURL', page, 'Page', async () => { return toMatchText.call(this, 'toHaveURL', page, 'Page', async () => {
return page.url(); return page.url();

View File

@ -15,3 +15,5 @@
*/ */
export * from '../../../types/test'; export * from '../../../types/test';
export * from '../../../types/types';
export { default } from '../../../types/test';

View File

@ -238,45 +238,54 @@ test('globalSetup should allow requiring a package from node_modules', async ({
expect(results[0].status).toBe('passed'); expect(results[0].status).toBe('passed');
}); });
test('globalSetup should work for auth', async ({ runInlineTest }) => { const authFiles = {
const result = await runInlineTest({ 'playwright.config.ts': `
'playwright.config.ts': ` const config: pwt.PlaywrightTestConfig = {
module.exports = { globalSetup: require.resolve('./auth'),
globalSetup: require.resolve('./auth.js'), use: {
use: { baseURL: 'https://www.example.com',
baseURL: 'https://www.example.com', storageState: 'state.json',
storageState: 'state.json', },
}, };
}; export default config;
`, `,
'auth.js': ` 'auth.ts': `
module.exports = async config => { async function globalSetup(config: pwt.FullConfig) {
const { baseURL, storageState } = config.projects[0].use; const { baseURL, storageState } = config.projects[0].use;
const browser = await pwt.chromium.launch(); const browser = await pwt.chromium.launch();
const page = await browser.newPage(); const page = await browser.newPage();
await page.route('**/*', route => { await page.route('**/*', route => {
route.fulfill({ body: '<html></html>' }).catch(() => {}); route.fulfill({ body: '<html></html>' }).catch(() => {});
});
await page.goto(baseURL);
await page.evaluate(() => {
localStorage['name'] = 'value';
});
await page.context().storageState({ path: storageState });
await browser.close();
};
`,
'a.test.js': `
const { test } = pwt;
test('should have storage state', async ({ page }) => {
await page.route('**/*', route => {
route.fulfill({ body: '<html></html>' }).catch(() => {});
});
await page.goto('/');
const value = await page.evaluate(() => localStorage['name']);
expect(value).toBe('value');
}); });
`, await page.goto(baseURL!);
}); await page.evaluate(() => {
localStorage['name'] = 'value';
});
await page.context().storageState({ path: storageState as string });
await browser.close();
};
export default globalSetup;
`,
'a.test.ts': `
const { test } = pwt;
test('should have storage state', async ({ page }) => {
await page.route('**/*', route => {
route.fulfill({ body: '<html></html>' }).catch(() => {});
});
await page.goto('/');
const value = await page.evaluate(() => localStorage['name']);
expect(value).toBe('value');
});
`,
};
test('globalSetup should work for auth', async ({ runInlineTest }) => {
const result = await runInlineTest(authFiles);
expect(result.exitCode).toBe(0); expect(result.exitCode).toBe(0);
expect(result.passed).toBe(1); expect(result.passed).toBe(1);
}); });
test('globalSetup auth should compile', async ({runTSC}) => {
const result = await runTSC(authFiles);
expect(result.exitCode).toBe(0);
});

11
types/test.d.ts vendored
View File

@ -35,6 +35,7 @@ export type PreserveOutput = 'always' | 'never' | 'failures-only';
export type UpdateSnapshots = 'all' | 'none' | 'missing'; export type UpdateSnapshots = 'all' | 'none' | 'missing';
type FixtureDefine<TestArgs extends KeyValue = {}, WorkerArgs extends KeyValue = {}> = { test: TestType<TestArgs, WorkerArgs>, fixtures: Fixtures<{}, {}, TestArgs, WorkerArgs> }; type FixtureDefine<TestArgs extends KeyValue = {}, WorkerArgs extends KeyValue = {}> = { test: TestType<TestArgs, WorkerArgs>, fixtures: Fixtures<{}, {}, TestArgs, WorkerArgs> };
type UseOptions<TestArgs, WorkerArgs> = { [K in keyof WorkerArgs]?: WorkerArgs[K] } & { [K in keyof TestArgs]?: TestArgs[K] };
type ExpectSettings = { type ExpectSettings = {
// Default timeout for async expect matchers in milliseconds, defaults to 5000ms. // Default timeout for async expect matchers in milliseconds, defaults to 5000ms.
@ -305,10 +306,10 @@ export interface Project<TestArgs = {}, WorkerArgs = {}> extends TestProject {
* ``` * ```
* *
*/ */
use?: Fixtures<{}, {}, TestArgs, WorkerArgs>; use?: UseOptions<TestArgs, WorkerArgs>;
} }
export type FullProject<TestArgs = {}, WorkerArgs = {}> = Required<Project<TestArgs, WorkerArgs>>; export type FullProject<TestArgs = {}, WorkerArgs = {}> = Required<Project<PlaywrightTestOptions & TestArgs, PlaywrightWorkerOptions & WorkerArgs>>;
export type WebServerConfig = { export type WebServerConfig = {
/** /**
@ -611,7 +612,7 @@ export interface Config<TestArgs = {}, WorkerArgs = {}> extends TestConfig {
* ``` * ```
* *
*/ */
use?: Fixtures<{}, {}, TestArgs, WorkerArgs>; use?: UseOptions<TestArgs, WorkerArgs>;
} }
/** /**
@ -636,7 +637,7 @@ export interface Config<TestArgs = {}, WorkerArgs = {}> extends TestConfig {
* ``` * ```
* *
*/ */
export interface FullConfig { export interface FullConfig<TestArgs = {}, WorkerArgs = {}> {
/** /**
* Whether to exit with an error if any tests or groups are marked as * Whether to exit with an error if any tests or groups are marked as
* [test.only(title, testFunction)](https://playwright.dev/docs/api/class-test#test-only) or * [test.only(title, testFunction)](https://playwright.dev/docs/api/class-test#test-only) or
@ -706,7 +707,7 @@ export interface FullConfig {
/** /**
* Playwright Test supports running multiple test projects at the same time. See [TestProject] for more information. * Playwright Test supports running multiple test projects at the same time. See [TestProject] for more information.
*/ */
projects: FullProject[]; projects: FullProject<TestArgs, WorkerArgs>[];
/** /**
* The list of reporters to use. Each reporter can be: * The list of reporters to use. Each reporter can be:
* - A builtin reporter name like `'list'` or `'json'`. * - A builtin reporter name like `'list'` or `'json'`.

View File

@ -34,6 +34,7 @@ export type PreserveOutput = 'always' | 'never' | 'failures-only';
export type UpdateSnapshots = 'all' | 'none' | 'missing'; export type UpdateSnapshots = 'all' | 'none' | 'missing';
type FixtureDefine<TestArgs extends KeyValue = {}, WorkerArgs extends KeyValue = {}> = { test: TestType<TestArgs, WorkerArgs>, fixtures: Fixtures<{}, {}, TestArgs, WorkerArgs> }; type FixtureDefine<TestArgs extends KeyValue = {}, WorkerArgs extends KeyValue = {}> = { test: TestType<TestArgs, WorkerArgs>, fixtures: Fixtures<{}, {}, TestArgs, WorkerArgs> };
type UseOptions<TestArgs, WorkerArgs> = { [K in keyof WorkerArgs]?: WorkerArgs[K] } & { [K in keyof TestArgs]?: TestArgs[K] };
type ExpectSettings = { type ExpectSettings = {
// Default timeout for async expect matchers in milliseconds, defaults to 5000ms. // Default timeout for async expect matchers in milliseconds, defaults to 5000ms.
@ -59,10 +60,10 @@ interface TestProject {
export interface Project<TestArgs = {}, WorkerArgs = {}> extends TestProject { export interface Project<TestArgs = {}, WorkerArgs = {}> extends TestProject {
define?: FixtureDefine | FixtureDefine[]; define?: FixtureDefine | FixtureDefine[];
use?: Fixtures<{}, {}, TestArgs, WorkerArgs>; use?: UseOptions<TestArgs, WorkerArgs>;
} }
export type FullProject<TestArgs = {}, WorkerArgs = {}> = Required<Project<TestArgs, WorkerArgs>>; export type FullProject<TestArgs = {}, WorkerArgs = {}> = Required<Project<PlaywrightTestOptions & TestArgs, PlaywrightWorkerOptions & WorkerArgs>>;
export type WebServerConfig = { export type WebServerConfig = {
/** /**
@ -129,10 +130,10 @@ interface TestConfig {
export interface Config<TestArgs = {}, WorkerArgs = {}> extends TestConfig { export interface Config<TestArgs = {}, WorkerArgs = {}> extends TestConfig {
projects?: Project<TestArgs, WorkerArgs>[]; projects?: Project<TestArgs, WorkerArgs>[];
define?: FixtureDefine | FixtureDefine[]; define?: FixtureDefine | FixtureDefine[];
use?: Fixtures<{}, {}, TestArgs, WorkerArgs>; use?: UseOptions<TestArgs, WorkerArgs>;
} }
export interface FullConfig { export interface FullConfig<TestArgs = {}, WorkerArgs = {}> {
forbidOnly: boolean; forbidOnly: boolean;
globalSetup: string | null; globalSetup: string | null;
globalTeardown: string | null; globalTeardown: string | null;
@ -141,7 +142,7 @@ export interface FullConfig {
grepInvert: RegExp | RegExp[] | null; grepInvert: RegExp | RegExp[] | null;
maxFailures: number; maxFailures: number;
preserveOutput: PreserveOutput; preserveOutput: PreserveOutput;
projects: FullProject[]; projects: FullProject<TestArgs, WorkerArgs>[];
reporter: ReporterDescription[]; reporter: ReporterDescription[];
reportSlowTests: ReportSlowTests; reportSlowTests: ReportSlowTests;
rootDir: string; rootDir: string;