@playwright/test
Advanced tools
Comparing version 0.1111.0 to 0.1112.0-alpha1
@@ -16,181 +16,234 @@ /** | ||
*/ | ||
import type { Browser, BrowserContext, BrowserContextOptions, BrowserType, LaunchOptions, Page } from 'playwright'; | ||
export { expect, config } from 'folio'; | ||
declare type PlaywrightParameters = { | ||
browserName: 'chromium' | 'firefox' | 'webkit'; | ||
headful: boolean; | ||
platform: 'win32' | 'linux' | 'darwin'; | ||
screenshotOnFailure: boolean; | ||
slowMo: number; | ||
video: boolean; | ||
import { Browser, BrowserContext, BrowserContextOptions, Page, LaunchOptions, ViewportSize, Geolocation, HTTPCredentials } from 'playwright'; | ||
import * as folio from 'folio'; | ||
/** | ||
* The name of the browser supported by Playwright. | ||
*/ | ||
export declare type BrowserName = 'chromium' | 'firefox' | 'webkit'; | ||
/** | ||
* Browser channel name. Used to run tests in different browser flavors, | ||
* for example Google Chrome Beta, or Microsoft Edge Stable. | ||
* @see BrowserContextOptions | ||
*/ | ||
export declare type BrowserChannel = Exclude<LaunchOptions['channel'], undefined>; | ||
/** | ||
* Emulates `'prefers-colors-scheme'` media feature, | ||
* supported values are `'light'`, `'dark'`, `'no-preference'`. | ||
* @see BrowserContextOptions | ||
*/ | ||
export declare type ColorScheme = Exclude<BrowserContextOptions['colorScheme'], undefined>; | ||
/** | ||
* An object containing additional HTTP headers to be sent with every request. All header values must be strings. | ||
* @see BrowserContextOptions | ||
*/ | ||
export declare type ExtraHTTPHeaders = Exclude<BrowserContextOptions['extraHTTPHeaders'], undefined>; | ||
/** | ||
* Proxy settings available for all tests, or individually per test. | ||
* @see BrowserContextOptions | ||
*/ | ||
export declare type Proxy = Exclude<BrowserContextOptions['proxy'], undefined>; | ||
/** | ||
* Storage state for the test. | ||
* @see BrowserContextOptions | ||
*/ | ||
export declare type StorageState = Exclude<BrowserContextOptions['storageState'], undefined>; | ||
/** | ||
* Options available to configure browser launch. | ||
* - Set options in config: | ||
* ```js | ||
* use: { browserName: 'webkit' } | ||
* ``` | ||
* - Set options in test file: | ||
* ```js | ||
* test.use({ browserName: 'webkit' }) | ||
* ``` | ||
* | ||
* Available as arguments to the test function and all hooks (beforeEach, afterEach, beforeAll, afterAll). | ||
*/ | ||
export declare type PlaywrightWorkerOptions = { | ||
/** | ||
* Name of the browser (`chromium`, `firefox`, `webkit`) that runs tests. | ||
*/ | ||
browserName: BrowserName; | ||
/** | ||
* Whether to run browser in headless mode. Takes priority over `launchOptions`. | ||
* @see LaunchOptions | ||
*/ | ||
headless: boolean | undefined; | ||
/** | ||
* Browser distribution channel. Takes priority over `launchOptions`. | ||
* @see LaunchOptions | ||
*/ | ||
channel: BrowserChannel | undefined; | ||
/** | ||
* Options used to launch the browser. Other options above (e.g. `headless`) take priority. | ||
* @see LaunchOptions | ||
*/ | ||
launchOptions: LaunchOptions; | ||
}; | ||
declare type PlaywrightWorkerFixtures = { | ||
/** | ||
* Options available to configure each test. | ||
* - Set options in config: | ||
* ```js | ||
* use: { video: 'on' } | ||
* ``` | ||
* - Set options in test file: | ||
* ```js | ||
* test.use({ video: 'on' }) | ||
* ``` | ||
* | ||
* Available as arguments to the test function and beforeEach/afterEach hooks. | ||
*/ | ||
export declare type PlaywrightTestOptions = { | ||
/** | ||
* Whether to capture a screenshot after each test, off by default. | ||
* - `off`: Do not capture screenshots. | ||
* - `on`: Capture screenshot after each test. | ||
* - `only-on-failure`: Capture screenshot after each test failure. | ||
*/ | ||
screenshot: 'off' | 'on' | 'only-on-failure'; | ||
/** | ||
* Whether to record video for each test, off by default. | ||
* - `off`: Do not record video. | ||
* - `on`: Record video for each test. | ||
* - `retain-on-failure`: Record video for each test, but remove all videos from successful test runs. | ||
* - `retry-with-video`: Record video only when retrying a test. | ||
*/ | ||
video: 'off' | 'on' | 'retain-on-failure' | 'retry-with-video'; | ||
/** | ||
* Whether to automatically download all the attachments. Takes priority over `contextOptions`. | ||
* @see BrowserContextOptions | ||
*/ | ||
acceptDownloads: boolean | undefined; | ||
/** | ||
* Toggles bypassing page's Content-Security-Policy. Takes priority over `contextOptions`. | ||
* @see BrowserContextOptions | ||
*/ | ||
bypassCSP: boolean | undefined; | ||
/** | ||
* Emulates `'prefers-colors-scheme'` media feature, supported values are `'light'`, `'dark'`, `'no-preference'`. | ||
* @see BrowserContextOptions | ||
*/ | ||
colorScheme: ColorScheme | undefined; | ||
/** | ||
* Specify device scale factor (can be thought of as dpr). Defaults to `1`. | ||
* @see BrowserContextOptions | ||
*/ | ||
deviceScaleFactor: number | undefined; | ||
/** | ||
* An object containing additional HTTP headers to be sent with every request. All header values must be strings. | ||
* @see BrowserContextOptions | ||
*/ | ||
extraHTTPHeaders: ExtraHTTPHeaders | undefined; | ||
/** | ||
* Context geolocation. Takes priority over `contextOptions`. | ||
* @see BrowserContextOptions | ||
*/ | ||
geolocation: Geolocation | undefined; | ||
/** | ||
* Specifies if viewport supports touch events. Takes priority over `contextOptions`. | ||
* @see BrowserContextOptions | ||
*/ | ||
hasTouch: boolean | undefined; | ||
/** | ||
* Credentials for [HTTP authentication](https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication). | ||
* @see BrowserContextOptions | ||
*/ | ||
httpCredentials: HTTPCredentials | undefined; | ||
/** | ||
* Whether to ignore HTTPS errors during navigation. Takes priority over `contextOptions`. | ||
* @see BrowserContextOptions | ||
*/ | ||
ignoreHTTPSErrors: boolean | undefined; | ||
/** | ||
* Whether the `meta viewport` tag is taken into account and touch events are enabled. Not supported in Firefox. | ||
* @see BrowserContextOptions | ||
*/ | ||
isMobile: boolean | undefined; | ||
/** | ||
* Whether or not to enable JavaScript in the context. Defaults to `true`. | ||
* @see BrowserContextOptions | ||
*/ | ||
javaScriptEnabled: boolean | undefined; | ||
/** | ||
* User locale, for example `en-GB`, `de-DE`, etc. Takes priority over `contextOptions`. | ||
* @see BrowserContextOptions | ||
*/ | ||
locale: string | undefined; | ||
/** | ||
* Whether to emulate network being offline. | ||
* @see BrowserContextOptions | ||
*/ | ||
offline: boolean | undefined; | ||
/** | ||
* A list of permissions to grant to all pages in this context. Takes priority over `contextOptions`. | ||
* @see BrowserContextOptions | ||
*/ | ||
permissions: string[] | undefined; | ||
/** | ||
* Proxy setting used for all pages in the test. Takes priority over `contextOptions`. | ||
* @see BrowserContextOptions | ||
*/ | ||
proxy: Proxy | undefined; | ||
/** | ||
* Populates context with given storage state. Takes priority over `contextOptions`. | ||
* @see BrowserContextOptions | ||
*/ | ||
storageState: StorageState | undefined; | ||
/** | ||
* Changes the timezone of the context. Takes priority over `contextOptions`. | ||
* @see BrowserContextOptions | ||
*/ | ||
timezoneId: string | undefined; | ||
/** | ||
* Specific user agent to use in this context. | ||
* @see BrowserContextOptions | ||
*/ | ||
userAgent: string | undefined; | ||
/** | ||
* Viewport used for all pages in the test. Takes priority over `contextOptions`. | ||
* @see BrowserContextOptions | ||
*/ | ||
viewport: ViewportSize | undefined; | ||
/** | ||
* Options used to create the context. Other options above (e.g. `viewport`) take priority. | ||
* @see BrowserContextOptions | ||
*/ | ||
contextOptions: BrowserContextOptions; | ||
}; | ||
/** | ||
* Arguments available to the test function and all hooks (beforeEach, afterEach, beforeAll, afterAll). | ||
*/ | ||
export declare type PlaywrightWorkerArgs = { | ||
/** | ||
* The Playwright instance. | ||
*/ | ||
playwright: typeof import('playwright'); | ||
browserType: BrowserType<Browser>; | ||
browserOptions: LaunchOptions; | ||
/** | ||
* Browser instance, shared between multiple tests. | ||
*/ | ||
browser: Browser; | ||
isChromium: boolean; | ||
isFirefox: boolean; | ||
isWebKit: boolean; | ||
isWindows: boolean; | ||
isMac: boolean; | ||
isLinux: boolean; | ||
}; | ||
declare type PlaywrightTestFixtures = { | ||
contextOptions: BrowserContextOptions; | ||
contextFactory: (options?: BrowserContextOptions) => Promise<BrowserContext>; | ||
/** | ||
* Arguments available to the test function and beforeEach/afterEach hooks. | ||
*/ | ||
export declare type PlaywrightTestArgs = { | ||
/** | ||
* BrowserContext instance, created fresh for each test. | ||
*/ | ||
context: BrowserContext; | ||
/** | ||
* Page instance, created fresh for each test. | ||
*/ | ||
page: Page; | ||
}; | ||
export declare const folio: import("folio").Folio<{ | ||
testInfo: import("folio").TestInfo; | ||
testParametersPathSegment: string; | ||
} & PlaywrightTestFixtures, { | ||
testWorkerIndex: number; | ||
} & PlaywrightWorkerFixtures, PlaywrightParameters>; | ||
export declare const it: { | ||
(name: string, inner: (fixtures: PlaywrightParameters & { | ||
testWorkerIndex: number; | ||
} & PlaywrightWorkerFixtures & { | ||
testInfo: import("folio").TestInfo; | ||
testParametersPathSegment: string; | ||
} & PlaywrightTestFixtures) => void | Promise<void>): void; | ||
(name: string, modifierFn: (modifier: import("folio/out/testModifier").TestModifier, parameters: PlaywrightParameters) => any, inner: (fixtures: PlaywrightParameters & { | ||
testWorkerIndex: number; | ||
} & PlaywrightWorkerFixtures & { | ||
testInfo: import("folio").TestInfo; | ||
testParametersPathSegment: string; | ||
} & PlaywrightTestFixtures) => void | Promise<void>): void; | ||
} & { | ||
only: { | ||
(name: string, inner: (fixtures: PlaywrightParameters & { | ||
testWorkerIndex: number; | ||
} & PlaywrightWorkerFixtures & { | ||
testInfo: import("folio").TestInfo; | ||
testParametersPathSegment: string; | ||
} & PlaywrightTestFixtures) => void | Promise<void>): void; | ||
(name: string, modifierFn: (modifier: import("folio/out/testModifier").TestModifier, parameters: PlaywrightParameters) => any, inner: (fixtures: PlaywrightParameters & { | ||
testWorkerIndex: number; | ||
} & PlaywrightWorkerFixtures & { | ||
testInfo: import("folio").TestInfo; | ||
testParametersPathSegment: string; | ||
} & PlaywrightTestFixtures) => void | Promise<void>): void; | ||
}; | ||
skip: { | ||
(name: string, inner: (fixtures: PlaywrightParameters & { | ||
testWorkerIndex: number; | ||
} & PlaywrightWorkerFixtures & { | ||
testInfo: import("folio").TestInfo; | ||
testParametersPathSegment: string; | ||
} & PlaywrightTestFixtures) => void | Promise<void>): void; | ||
(name: string, modifierFn: (modifier: import("folio/out/testModifier").TestModifier, parameters: PlaywrightParameters) => any, inner: (fixtures: PlaywrightParameters & { | ||
testWorkerIndex: number; | ||
} & PlaywrightWorkerFixtures & { | ||
testInfo: import("folio").TestInfo; | ||
testParametersPathSegment: string; | ||
} & PlaywrightTestFixtures) => void | Promise<void>): void; | ||
}; | ||
}; | ||
export declare const fit: { | ||
(name: string, inner: (fixtures: PlaywrightParameters & { | ||
testWorkerIndex: number; | ||
} & PlaywrightWorkerFixtures & { | ||
testInfo: import("folio").TestInfo; | ||
testParametersPathSegment: string; | ||
} & PlaywrightTestFixtures) => void | Promise<void>): void; | ||
(name: string, modifierFn: (modifier: import("folio/out/testModifier").TestModifier, parameters: PlaywrightParameters) => any, inner: (fixtures: PlaywrightParameters & { | ||
testWorkerIndex: number; | ||
} & PlaywrightWorkerFixtures & { | ||
testInfo: import("folio").TestInfo; | ||
testParametersPathSegment: string; | ||
} & PlaywrightTestFixtures) => void | Promise<void>): void; | ||
}; | ||
export declare const xit: { | ||
(name: string, inner: (fixtures: PlaywrightParameters & { | ||
testWorkerIndex: number; | ||
} & PlaywrightWorkerFixtures & { | ||
testInfo: import("folio").TestInfo; | ||
testParametersPathSegment: string; | ||
} & PlaywrightTestFixtures) => void | Promise<void>): void; | ||
(name: string, modifierFn: (modifier: import("folio/out/testModifier").TestModifier, parameters: PlaywrightParameters) => any, inner: (fixtures: PlaywrightParameters & { | ||
testWorkerIndex: number; | ||
} & PlaywrightWorkerFixtures & { | ||
testInfo: import("folio").TestInfo; | ||
testParametersPathSegment: string; | ||
} & PlaywrightTestFixtures) => void | Promise<void>): void; | ||
}; | ||
export declare const test: { | ||
(name: string, inner: (fixtures: PlaywrightParameters & { | ||
testWorkerIndex: number; | ||
} & PlaywrightWorkerFixtures & { | ||
testInfo: import("folio").TestInfo; | ||
testParametersPathSegment: string; | ||
} & PlaywrightTestFixtures) => void | Promise<void>): void; | ||
(name: string, modifierFn: (modifier: import("folio/out/testModifier").TestModifier, parameters: PlaywrightParameters) => any, inner: (fixtures: PlaywrightParameters & { | ||
testWorkerIndex: number; | ||
} & PlaywrightWorkerFixtures & { | ||
testInfo: import("folio").TestInfo; | ||
testParametersPathSegment: string; | ||
} & PlaywrightTestFixtures) => void | Promise<void>): void; | ||
} & { | ||
only: { | ||
(name: string, inner: (fixtures: PlaywrightParameters & { | ||
testWorkerIndex: number; | ||
} & PlaywrightWorkerFixtures & { | ||
testInfo: import("folio").TestInfo; | ||
testParametersPathSegment: string; | ||
} & PlaywrightTestFixtures) => void | Promise<void>): void; | ||
(name: string, modifierFn: (modifier: import("folio/out/testModifier").TestModifier, parameters: PlaywrightParameters) => any, inner: (fixtures: PlaywrightParameters & { | ||
testWorkerIndex: number; | ||
} & PlaywrightWorkerFixtures & { | ||
testInfo: import("folio").TestInfo; | ||
testParametersPathSegment: string; | ||
} & PlaywrightTestFixtures) => void | Promise<void>): void; | ||
}; | ||
skip: { | ||
(name: string, inner: (fixtures: PlaywrightParameters & { | ||
testWorkerIndex: number; | ||
} & PlaywrightWorkerFixtures & { | ||
testInfo: import("folio").TestInfo; | ||
testParametersPathSegment: string; | ||
} & PlaywrightTestFixtures) => void | Promise<void>): void; | ||
(name: string, modifierFn: (modifier: import("folio/out/testModifier").TestModifier, parameters: PlaywrightParameters) => any, inner: (fixtures: PlaywrightParameters & { | ||
testWorkerIndex: number; | ||
} & PlaywrightWorkerFixtures & { | ||
testInfo: import("folio").TestInfo; | ||
testParametersPathSegment: string; | ||
} & PlaywrightTestFixtures) => void | Promise<void>): void; | ||
}; | ||
}; | ||
export declare const describe: { | ||
(name: string, inner: () => void): void; | ||
(name: string, modifierFn: (modifier: import("folio/out/testModifier").TestModifier, parameters: PlaywrightParameters) => any, inner: () => void): void; | ||
} & { | ||
only: { | ||
(name: string, inner: () => void): void; | ||
(name: string, modifierFn: (modifier: import("folio/out/testModifier").TestModifier, parameters: PlaywrightParameters) => any, inner: () => void): void; | ||
}; | ||
skip: { | ||
(name: string, inner: () => void): void; | ||
(name: string, modifierFn: (modifier: import("folio/out/testModifier").TestModifier, parameters: PlaywrightParameters) => any, inner: () => void): void; | ||
}; | ||
}; | ||
export declare const beforeEach: (inner: (fixtures: PlaywrightParameters & { | ||
testWorkerIndex: number; | ||
} & PlaywrightWorkerFixtures & { | ||
testInfo: import("folio").TestInfo; | ||
testParametersPathSegment: string; | ||
} & PlaywrightTestFixtures) => Promise<void>) => void; | ||
export declare const afterEach: (inner: (fixtures: PlaywrightParameters & { | ||
testWorkerIndex: number; | ||
} & PlaywrightWorkerFixtures & { | ||
testInfo: import("folio").TestInfo; | ||
testParametersPathSegment: string; | ||
} & PlaywrightTestFixtures) => Promise<void>) => void; | ||
export declare const beforeAll: (inner: (fixtures: { | ||
testWorkerIndex: number; | ||
} & PlaywrightWorkerFixtures) => Promise<void>) => void; | ||
export declare const afterAll: (inner: (fixtures: { | ||
testWorkerIndex: number; | ||
} & PlaywrightWorkerFixtures) => Promise<void>) => void; | ||
/** | ||
* These tests are executed in Playwright environment that launches the browser | ||
* and provides a fresh page to each test. | ||
*/ | ||
export declare const test: folio.TestType<PlaywrightTestArgs & PlaywrightTestOptions, PlaywrightWorkerArgs & PlaywrightWorkerOptions>; | ||
export * from 'folio'; | ||
export default test; | ||
export declare type PlaywrightTestProject<TestArgs = {}, WorkerArgs = {}> = folio.Project<PlaywrightTestOptions & TestArgs, PlaywrightWorkerOptions & WorkerArgs>; | ||
export declare type PlaywrightTestConfig<TestArgs = {}, WorkerArgs = {}> = folio.Config<PlaywrightTestOptions & TestArgs, PlaywrightWorkerOptions & WorkerArgs>; | ||
//# sourceMappingURL=index.d.ts.map |
251
out/index.js
@@ -17,109 +17,154 @@ "use strict"; | ||
*/ | ||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); | ||
}) : (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
o[k2] = m[k]; | ||
})); | ||
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { | ||
Object.defineProperty(o, "default", { enumerable: true, value: v }); | ||
}) : function(o, v) { | ||
o["default"] = v; | ||
}); | ||
var __importStar = (this && this.__importStar) || function (mod) { | ||
if (mod && mod.__esModule) return mod; | ||
var result = {}; | ||
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); | ||
__setModuleDefault(result, mod); | ||
return result; | ||
}; | ||
var __exportStar = (this && this.__exportStar) || function(m, exports) { | ||
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p); | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.afterAll = exports.beforeAll = exports.afterEach = exports.beforeEach = exports.describe = exports.test = exports.xit = exports.fit = exports.it = exports.folio = exports.config = exports.expect = void 0; | ||
const folio_1 = require("folio"); | ||
var folio_2 = require("folio"); | ||
Object.defineProperty(exports, "expect", { enumerable: true, get: function () { return folio_2.expect; } }); | ||
Object.defineProperty(exports, "config", { enumerable: true, get: function () { return folio_2.config; } }); | ||
// Test timeout for e2e tests is 30 seconds. | ||
folio_1.config.timeout = 30000; | ||
const fixtures = folio_1.folio.extend(); | ||
fixtures.browserName.initParameter('Browser type name', (process.env.BROWSER || 'chromium')); | ||
fixtures.headful.initParameter('Whether to run tests headless or headful', process.env.HEADFUL ? true : false); | ||
fixtures.platform.initParameter('Operating system', process.platform); | ||
fixtures.screenshotOnFailure.initParameter('Generate screenshot on failure', false); | ||
fixtures.slowMo.initParameter('Slows down Playwright operations by the specified amount of milliseconds', 0); | ||
fixtures.video.initParameter('Record videos while running tests', false); | ||
fixtures.browserOptions.init(async ({ headful, slowMo }, run) => { | ||
await run({ | ||
handleSIGINT: false, | ||
slowMo, | ||
headless: !headful, | ||
}); | ||
}, { scope: 'worker' }); | ||
fixtures.playwright.init(async ({}, run) => { | ||
const playwright = require('playwright'); | ||
await run(playwright); | ||
}, { scope: 'worker' }); | ||
fixtures.browserType.init(async ({ playwright, browserName }, run) => { | ||
const browserType = playwright[browserName]; | ||
await run(browserType); | ||
}, { scope: 'worker' }); | ||
fixtures.browser.init(async ({ browserType, browserOptions }, run) => { | ||
const browser = await browserType.launch(browserOptions); | ||
await run(browser); | ||
await browser.close(); | ||
}, { scope: 'worker' }); | ||
fixtures.isChromium.init(async ({ browserName }, run) => { | ||
await run(browserName === 'chromium'); | ||
}, { scope: 'worker' }); | ||
fixtures.isFirefox.init(async ({ browserName }, run) => { | ||
await run(browserName === 'firefox'); | ||
}, { scope: 'worker' }); | ||
fixtures.isWebKit.init(async ({ browserName }, run) => { | ||
await run(browserName === 'webkit'); | ||
}, { scope: 'worker' }); | ||
fixtures.isWindows.init(async ({ platform }, run) => { | ||
await run(platform === 'win32'); | ||
}, { scope: 'worker' }); | ||
fixtures.isMac.init(async ({ platform }, run) => { | ||
await run(platform === 'darwin'); | ||
}, { scope: 'worker' }); | ||
fixtures.isLinux.init(async ({ platform }, run) => { | ||
await run(platform === 'linux'); | ||
}, { scope: 'worker' }); | ||
fixtures.contextOptions.init(async ({ video, testInfo }, run) => { | ||
if (video) { | ||
await run({ | ||
videosPath: testInfo.outputPath(''), | ||
}); | ||
} | ||
else { | ||
await run({}); | ||
} | ||
}); | ||
fixtures.contextFactory.init(async ({ browser, contextOptions, testInfo, screenshotOnFailure }, run) => { | ||
const contexts = []; | ||
async function contextFactory(options = {}) { | ||
const context = await browser.newContext({ ...contextOptions, ...options }); | ||
contexts.push(context); | ||
return context; | ||
} | ||
await run(contextFactory); | ||
if (screenshotOnFailure && (testInfo.status !== testInfo.expectedStatus)) { | ||
let ordinal = 0; | ||
for (const context of contexts) { | ||
for (const page of context.pages()) | ||
await page.screenshot({ timeout: 5000, path: testInfo.outputPath(`test-failed-${++ordinal}.png`) }); | ||
exports.test = void 0; | ||
const folio = __importStar(require("folio")); | ||
const fs = __importStar(require("fs")); | ||
const util = __importStar(require("util")); | ||
const headedOption = folio.registerCLIOption('headed', 'Run tests in headed browsers (default: headless)', { type: 'boolean' }); | ||
/** | ||
* These tests are executed in Playwright environment that launches the browser | ||
* and provides a fresh page to each test. | ||
*/ | ||
exports.test = folio.test.extend({ | ||
browserName: ['chromium', { scope: 'worker' }], | ||
playwright: [require('playwright'), { scope: 'worker' }], | ||
headless: [undefined, { scope: 'worker' }], | ||
channel: [undefined, { scope: 'worker' }], | ||
launchOptions: [{}, { scope: 'worker' }], | ||
browser: [async ({ playwright, browserName, headless, channel, launchOptions }, use) => { | ||
const options = { | ||
handleSIGINT: false, | ||
...launchOptions, | ||
}; | ||
if (headless !== undefined) | ||
options.headless = headless; | ||
if (channel !== undefined) | ||
options.channel = channel; | ||
if (headedOption.value) | ||
options.headless = false; | ||
const browser = await playwright[browserName].launch(options); | ||
await use(browser); | ||
await browser.close(); | ||
}, { scope: 'worker' }], | ||
screenshot: 'off', | ||
video: 'off', | ||
acceptDownloads: undefined, | ||
bypassCSP: undefined, | ||
colorScheme: undefined, | ||
deviceScaleFactor: undefined, | ||
extraHTTPHeaders: undefined, | ||
geolocation: undefined, | ||
hasTouch: undefined, | ||
httpCredentials: undefined, | ||
ignoreHTTPSErrors: undefined, | ||
isMobile: undefined, | ||
javaScriptEnabled: undefined, | ||
locale: undefined, | ||
offline: undefined, | ||
permissions: undefined, | ||
proxy: undefined, | ||
storageState: undefined, | ||
timezoneId: undefined, | ||
userAgent: undefined, | ||
viewport: undefined, | ||
contextOptions: {}, | ||
context: async ({ browserName, browser, screenshot, video, acceptDownloads, bypassCSP, colorScheme, deviceScaleFactor, extraHTTPHeaders, hasTouch, geolocation, httpCredentials, ignoreHTTPSErrors, isMobile, javaScriptEnabled, locale, offline, permissions, proxy, storageState, viewport, timezoneId, userAgent, contextOptions }, use, testInfo) => { | ||
testInfo.snapshotPathSegment = browserName + '-' + process.platform; | ||
if (process.env.PWDEBUG) | ||
testInfo.setTimeout(0); | ||
const recordVideo = video === 'on' || video === 'retain-on-failure' || | ||
(video === 'retry-with-video' && !!testInfo.retry); | ||
const options = { | ||
recordVideo: recordVideo ? { dir: testInfo.outputPath('') } : undefined, | ||
...contextOptions, | ||
}; | ||
if (acceptDownloads !== undefined) | ||
options.acceptDownloads = acceptDownloads; | ||
if (bypassCSP !== undefined) | ||
options.bypassCSP = bypassCSP; | ||
if (colorScheme !== undefined) | ||
options.colorScheme = colorScheme; | ||
if (deviceScaleFactor !== undefined) | ||
options.deviceScaleFactor = deviceScaleFactor; | ||
if (extraHTTPHeaders !== undefined) | ||
options.extraHTTPHeaders = extraHTTPHeaders; | ||
if (geolocation !== undefined) | ||
options.geolocation = geolocation; | ||
if (hasTouch !== undefined) | ||
options.hasTouch = hasTouch; | ||
if (httpCredentials !== undefined) | ||
options.httpCredentials = httpCredentials; | ||
if (ignoreHTTPSErrors !== undefined) | ||
options.ignoreHTTPSErrors = ignoreHTTPSErrors; | ||
if (isMobile !== undefined) | ||
options.isMobile = isMobile; | ||
if (javaScriptEnabled !== undefined) | ||
options.javaScriptEnabled = javaScriptEnabled; | ||
if (locale !== undefined) | ||
options.locale = locale; | ||
if (offline !== undefined) | ||
options.offline = offline; | ||
if (permissions !== undefined) | ||
options.permissions = permissions; | ||
if (proxy !== undefined) | ||
options.proxy = proxy; | ||
if (storageState !== undefined) | ||
options.storageState = storageState; | ||
if (timezoneId !== undefined) | ||
options.timezoneId = timezoneId; | ||
if (userAgent !== undefined) | ||
options.userAgent = userAgent; | ||
if (viewport !== undefined) | ||
options.viewport = viewport; | ||
const context = await browser.newContext(options); | ||
const allPages = []; | ||
context.on('page', page => allPages.push(page)); | ||
await use(context); | ||
const testFailed = testInfo.status !== testInfo.expectedStatus; | ||
if (screenshot === 'on' || (screenshot === 'only-on-failure' && testFailed)) { | ||
await Promise.all(allPages.map((page, index) => { | ||
const screenshotPath = testInfo.outputPath(`test-${testFailed ? 'failed' : 'finished'}-${++index}.png`); | ||
return page.screenshot({ timeout: 5000, path: screenshotPath }).catch(e => { }); | ||
})); | ||
} | ||
} | ||
for (const context of contexts) | ||
await context.close(); | ||
const deleteVideos = video === 'retain-on-failure' && !testFailed; | ||
if (deleteVideos) { | ||
await Promise.all(allPages.map(async (page) => { | ||
const video = page.video(); | ||
if (!video) | ||
return; | ||
const videoPath = await video.path(); | ||
await util.promisify(fs.unlink)(videoPath).catch(e => { }); | ||
})); | ||
} | ||
}, | ||
page: async ({ context }, use) => { | ||
await use(await context.newPage()); | ||
}, | ||
}); | ||
fixtures.context.init(async ({ contextFactory }, run) => { | ||
const context = await contextFactory(); | ||
await run(context); | ||
// Context factory is taking care of closing the context, | ||
// so that it could capture a screenshot on failure. | ||
}); | ||
fixtures.page.init(async ({ context }, run) => { | ||
// Always create page off context so that they matched. | ||
await run(await context.newPage()); | ||
// Context fixture is taking care of closing the page. | ||
}); | ||
fixtures.testParametersPathSegment.override(async ({ browserName, platform }, run) => { | ||
await run(browserName + '-' + platform); | ||
}); | ||
exports.folio = fixtures.build(); | ||
exports.it = exports.folio.it; | ||
exports.fit = exports.folio.fit; | ||
exports.xit = exports.folio.xit; | ||
exports.test = exports.folio.test; | ||
exports.describe = exports.folio.describe; | ||
exports.beforeEach = exports.folio.beforeEach; | ||
exports.afterEach = exports.folio.afterEach; | ||
exports.beforeAll = exports.folio.beforeAll; | ||
exports.afterAll = exports.folio.afterAll; | ||
// If browser is not specified, we are running tests against all three browsers. | ||
exports.folio.generateParametrizedTests('browserName', process.env.BROWSER ? [process.env.BROWSER] : ['chromium', 'webkit', 'firefox']); | ||
__exportStar(require("folio"), exports); | ||
exports.default = exports.test; | ||
//# sourceMappingURL=index.js.map |
{ | ||
"name": "@playwright/test", | ||
"version": "0.1111.0", | ||
"version": "0.1112.0-alpha1", | ||
"license": "Apache-2.0", | ||
@@ -16,4 +16,4 @@ "author": { | ||
"prepare": "npm run build", | ||
"prepublishOnly": "rm tsconfig.tsbuildinfo && rm -rf out && npm run build", | ||
"test": "folio test/" | ||
"prepublishOnly": "rm -rf out && npm run build", | ||
"test": "folio -c test/" | ||
}, | ||
@@ -24,3 +24,3 @@ "repository": "github:Microsoft/playwright-test", | ||
"dependencies": { | ||
"folio": "=0.3.18", | ||
"folio": "=0.4.0-alpha17", | ||
"playwright": "=1.11.1", | ||
@@ -27,0 +27,0 @@ "rimraf": "^3.0.2" |
477
README.md
@@ -5,3 +5,3 @@ # ⚠️ This project is not ready for production. Stay tuned! ⚠️ | ||
Zero config cross-browser end-to-end testing for web apps. Browser automation with [Playwright](https://playwright.dev), Jest-like assertions and built-in support for TypeScript. | ||
Cross-browser end-to-end testing for web apps. Browser automation with [Playwright](https://playwright.dev), Jest-like assertions and built-in support for TypeScript. | ||
@@ -13,3 +13,5 @@ Playwright test runner is **available in preview** and minor breaking changes could happen. We welcome your feedback to shape this towards 1.0. | ||
- [Write a test](#write-a-test) | ||
- [Run the test](#run-the-test) | ||
- [Tests and assertions syntax](#tests-and-assertions-syntax) | ||
- [Write a configuration file](#write-a-configuration-file) | ||
- [Run the test suite](#run-the-test-suite) | ||
- [Examples](#examples) | ||
@@ -21,4 +23,3 @@ - [Multiple pages](#multiple-pages) | ||
- [Configuration](#configuration) | ||
- [Modify context options](#modify-context-options) | ||
- [Modify launch options](#modify-launch-options) | ||
- [Modify options](#modify-options) | ||
- [Skip tests with annotations](#skip-tests-with-annotations) | ||
@@ -37,8 +38,8 @@ - [Export JUnit report](#export-junit-report) | ||
Create `foo.spec.ts` to define your test. The test function uses the [`page`][page] argument for browser automation. | ||
Create `tests/foo.spec.ts` to define your test. The test function uses the [`page`][page] argument for browser automation. | ||
```js | ||
import { it, expect } from "@playwright/test"; | ||
```ts | ||
import { test, expect } from "@playwright/test"; | ||
it("is a basic test with the page", async ({ page }) => { | ||
test("is a basic test with the page", async ({ page }) => { | ||
await page.goto("https://playwright.dev/"); | ||
@@ -50,58 +51,107 @@ const name = await page.innerText(".navbar__title"); | ||
#### Default arguments | ||
The test runner provides browser primitives as arguments to your test functions. Test functions can use one or more of these arguments. | ||
- `page`: Instance of [Page][page]. Each test gets a new isolated page to run the test. | ||
- `context`: Instance of [BrowserContext][browser-context]. Each test gets a new isolated context to run the test. The `page` object belongs to this context. | ||
- `contextOptions`: Default options passed to context creation. Learn [how to modify them](#modify-context-options). | ||
- `browser`: Instance of [Browser][browser]. Browsers are shared across tests to optimize resources. Each worker process gets a browser instance. | ||
- `browserOptions`: Default options passed to browser/launch creation. | ||
- `context`: Instance of [BrowserContext][browser-context]. Each test gets a new isolated context to run the test. The `page` object belongs to this context. Learn [how to configure](#modify-options) context creation. | ||
- `browser`: Instance of [Browser][browser]. Browsers are shared across tests to optimize resources. Learn [how to configure](#modify-options) browser launch. | ||
- `browserName`: The name of the browser currently running the test. Either `chromium`, `firefox` or `webkit`. | ||
#### Specs and assertions | ||
You can now run the test using the underlying [Folio] command line: | ||
- Use `it` and `describe` to write test functions. Run a single test with `it.only` and skip a test with `it.skip`. | ||
- For assertions, use the [`expect` API](https://jestjs.io/docs/en/expect). | ||
```sh | ||
# Assuming that test files are in the tests directory. | ||
npx folio -c tests | ||
``` | ||
### Tests and assertions syntax | ||
- Use `test` to write test functions. Run a single test with `test.only` and skip a test with `test.skip`. | ||
- Use `test.describe` to group related tests together. | ||
- Use `test.beforeAll` and `test.afterAll` hooks to set up and tear down resources shared between tests. | ||
- Use `test.beforeEach` and `test.afterEach` hooks to set up and tear down resources for each test individually. | ||
- For assertions, use the [`expect` API](https://jestjs.io/docs/expect). | ||
```js | ||
const { it, describe } = require("@playwright/test"); | ||
const { test, expect } = require("@playwright/test"); | ||
describe("feature foo", () => { | ||
it("is working correctly", async ({ page }) => { | ||
// Test function | ||
test.describe("feature foo", () => { | ||
test.beforeEach(async ({ page }) => { | ||
// Go to the starting url before each test. | ||
await page.goto("https://my.start.url/feature-foo"); | ||
}); | ||
test("is working correctly", async ({ page }) => { | ||
// Assertions use the expect API. | ||
expect(page.url()).toBe("https://my.start.url/feature-foo"); | ||
}); | ||
}); | ||
``` | ||
### Run the test | ||
### Write a configuration file | ||
Tests can be run on single or multiple browsers and with flags to generate screenshot on test failures. | ||
Create `config.ts` to configure your tests: specify browser launch options, run tests in multiple browsers and much more. Here is an example configuration that runs every test in Chromium, Firefox and WebKit. | ||
```ts | ||
import { PlaywrightTestProject } from "@playwright/test"; | ||
const projects: PlaywrightTestProject[] = [ | ||
{ | ||
name: 'chromium', | ||
use: { | ||
browserName: 'chromium', | ||
headless: true, | ||
viewport: { width: 1280, height: 720 }, | ||
}, | ||
}, | ||
{ | ||
name: 'webkit', | ||
use: { | ||
browserName: 'webkit', | ||
headless: true, | ||
viewport: { width: 1280, height: 720 }, | ||
}, | ||
}, | ||
{ | ||
name: 'firefox', | ||
use: { | ||
browserName: 'firefox', | ||
headless: true, | ||
viewport: { width: 1280, height: 720 }, | ||
}, | ||
} | ||
]; | ||
module.exports = { | ||
timeout: 30000, // Each test is given 30 seconds. | ||
projects, | ||
}; | ||
``` | ||
### Run the test suite | ||
Tests can be run in single or multiple browsers, in parallel or sequentially. | ||
```sh | ||
# Run all tests across Chromium, Firefox and WebKit | ||
npx folio | ||
$ npx folio --config=config.ts | ||
# Run tests on a single browser | ||
npx folio --param browserName=chromium | ||
$ npx folio --config=config.ts --project=chromium | ||
# Run all tests in headful mode | ||
npx folio --param headful | ||
# Run tests in parallel | ||
$ npx folio --config=config.ts --workers=5 | ||
# Run tests with slowMo (slows down Playwright operations by n milliseconds) | ||
npx folio --param slowMo=100 | ||
# Run tests sequentially | ||
$ npx folio --config=config.ts --workers=1 | ||
# Save screenshots on failure in test-results directory | ||
npx folio --param screenshotOnFailure | ||
# Retry failing tests | ||
$ npx folio --config=config.ts --retries=2 | ||
# Record videos | ||
npx folio --param video | ||
# Retry test failures | ||
npx folio --retries 3 | ||
# See all options | ||
npx folio --help | ||
$ npx folio --help | ||
``` | ||
Test runner CLI can be customized with [Folio parameters][folio-parameters]. | ||
Refer to the [command line documentation][folio-cli] for all options. | ||
@@ -115,3 +165,3 @@ #### Configure NPM scripts | ||
"scripts": { | ||
"test": "npx folio --param screenshotOnFailure" | ||
"test": "npx folio --config=config.ts" | ||
} | ||
@@ -130,5 +180,5 @@ } | ||
```js | ||
import { it } from "@playwright/test"; | ||
import { test } from "@playwright/test"; | ||
it("tests on multiple web pages", async ({ context }) => { | ||
test("tests on multiple web pages", async ({ context }) => { | ||
const pageFoo = await context.newPage(); | ||
@@ -142,20 +192,44 @@ const pageBar = await context.newPage(); | ||
The `contextOptions` fixture defines default options used for context creation. This fixture can be overriden to configure mobile emulation in the default `context`. | ||
`use` section in the configuration file can be used to configure mobile emulation in the default `context`. | ||
```js | ||
import { folio } from "@playwright/test"; | ||
import { devices } from "playwright"; | ||
```diff | ||
// config.ts | ||
import { PlaywrightTestProject } from "@playwright/test"; | ||
+ import { devices } from "playwright"; | ||
const fixtures = folio.extend(); | ||
fixtures.contextOptions.override(async ({ contextOptions }, runTest) => { | ||
await runTest({ | ||
...contextOptions, | ||
...devices["iPhone 11"] | ||
}); | ||
}); | ||
const { it, describe, extend } = fixtures.build(); | ||
const projects: PlaywrightTestProject[] = [ | ||
{ | ||
name: 'chromium', | ||
use: { | ||
browserName: 'chromium', | ||
headless: true, | ||
- viewport: { width: 1280, height: 720 }, | ||
+ ...devices["Pixel 2"], | ||
}, | ||
}, | ||
it("uses mobile emulation", async ({ context }) => { | ||
// Test function | ||
}); | ||
{ | ||
name: 'webkit', | ||
use: { | ||
browserName: 'webkit', | ||
headless: true, | ||
- viewport: { width: 1280, height: 720 }, | ||
+ ...devices["iPhone 11"], | ||
}, | ||
}, | ||
{ | ||
name: 'firefox', | ||
use: { | ||
browserName: 'firefox', | ||
headless: true, | ||
viewport: { width: 1280, height: 720 }, | ||
}, | ||
} | ||
]; | ||
module.exports = { | ||
timeout: 30000, // Each test is given 30 seconds. | ||
projects, | ||
}; | ||
``` | ||
@@ -165,31 +239,19 @@ | ||
Define a custom argument that mocks networks call for a browser context. | ||
Define a custom route that mocks network calls for a browser context. | ||
```js | ||
// In fixtures.ts | ||
import { folio as base } from "@playwright/test"; | ||
import { BrowserContext } from "playwright"; | ||
// In foo.spec.ts | ||
import { test, expect } from "@playwright/test"; | ||
// Extend base fixtures with a new test-level fixture | ||
const fixtures = base.extend<{ mockedContext: BrowserContext }>(); | ||
fixtures.mockedContext.init(async ({ context }, runTest) => { | ||
// Modify existing `context` fixture to add a route | ||
context.route(/.css/, route => route.abort()); | ||
// Pass fixture to test functions | ||
runTest(context); | ||
test.beforeEach(async ({ context }) => { | ||
// Block any css requests for each test in this file. | ||
await context.route(/.css/, route => route.abort()); | ||
}); | ||
export folio = fixtures.build(); | ||
``` | ||
test("loads page without css", async ({ page }) => { | ||
// Alternatively, block any png requests just for this test. | ||
await page.route(/.png/, route => route.abort()); | ||
```js | ||
// In foo.spec.ts | ||
import { folio } from "./fixtures"; | ||
const { it, expect } = folio; | ||
it("loads pages without css requests", async ({ mockedContext }) => { | ||
const page = await mockedContext.newPage(); | ||
// Test function code. | ||
await page.goto("https://stackoverflow.com"); | ||
// Test function code | ||
}); | ||
@@ -203,8 +265,8 @@ ``` | ||
```js | ||
import { it, expect } from "@playwright/test"; | ||
import { test, expect } from "@playwright/test"; | ||
it("compares page screenshot", async ({ page, browserName }) => { | ||
test("compares page screenshot", async ({ page }) => { | ||
await page.goto("https://stackoverflow.com"); | ||
const screenshot = await page.screenshot(); | ||
expect(screenshot).toMatchSnapshot(`test-${browserName}.png`, { threshold: 0.2 }); | ||
expect(screenshot).toMatchSnapshot(`test.png`, { threshold: 0.2 }); | ||
}); | ||
@@ -224,144 +286,102 @@ ``` | ||
### Modify context options | ||
### Modify options | ||
You can modify the built-in fixtures. This example modifies the default `contextOptions` with a custom viewport size. | ||
You can modify browser launch options, context creation options and testing options either globally in the configuration file, or locally in the test file. | ||
**Step 1**: Create a new file (say `test/fixtures.ts`) which contains our modifications. | ||
Playwright test runner is based on the [Folio] framework, so it supports any configuration available in Folio, and adds a lot of Playwright-specific options. | ||
```ts | ||
// test/fixtures.ts | ||
import { folio as baseFolio } from "@playwright/test"; | ||
#### Globally in the configuration file | ||
const builder = baseFolio.extend(); | ||
You can specify different options for each browser using projects in the configuration file. Below is an example that changes some global testing options, and Chromium browser configuration. | ||
// Fixture modifications go here | ||
const folio = builder.build(); | ||
``` | ||
**Step 2**: Override the existing `contextOptions` fixture to configure viewport size and HTTPS error handling. | ||
```diff | ||
// test/fixtures.ts | ||
import { folio as baseFolio } from "@playwright/test"; | ||
+ import { BrowserContextOptions } from "playwright"; | ||
// config.ts | ||
import { PlaywrightTestProject } from "@playwright/test"; | ||
const builder = baseFolio.extend(); | ||
const projects: PlaywrightTestProject[] = [ | ||
{ | ||
name: 'chromium', | ||
use: { | ||
browserName: 'chromium', | ||
- headless: true, | ||
- viewport: { width: 1280, height: 720 }, | ||
+ | ||
+ // Launch options: | ||
+ headless: false, | ||
+ slowMo: 50, | ||
+ | ||
+ // Context options: | ||
+ viewport: { width: 800, height: 600 }, | ||
+ ignoreHTTPSErrors: true, | ||
+ | ||
+ // Testing options: | ||
+ video: 'retain-on-failure', }, | ||
}, | ||
+ builder.contextOptions.override(async ({ contextOptions }, runTest) => { | ||
+ const modifiedOptions: BrowserContextOptions = { | ||
+ ...contextOptions, // default options | ||
+ viewport: { width: 1440, height: 900 }, | ||
+ ignoreHTTPSErrors: true | ||
+ } | ||
+ await runTest(modifiedOptions); | ||
+ }); | ||
{ | ||
name: 'webkit', | ||
use: { | ||
browserName: 'webkit', | ||
headless: true, | ||
viewport: { width: 1280, height: 720 }, | ||
}, | ||
}, | ||
const folio = builder.build(); | ||
``` | ||
**Step 3**: Export `it` and other helpers from the modified fixtures. In your test files, import the modified fixture. | ||
```diff | ||
// test/fixtures.ts | ||
import { folio as baseFolio } from "@playwright/test"; | ||
import { BrowserContextOptions } from "playwright"; | ||
const builder = baseFolio.extend(); | ||
builder.contextOptions.override(async ({ contextOptions }, runTest) => { | ||
const modifiedOptions: BrowserContextOptions = { | ||
...contextOptions, // default options | ||
viewport: { width: 1440, height: 900 } | ||
ignoreHTTPSErrors: true | ||
{ | ||
name: 'firefox', | ||
use: { | ||
browserName: 'firefox', | ||
headless: true, | ||
viewport: { width: 1280, height: 720 }, | ||
}, | ||
} | ||
await runTest(modifiedOptions); | ||
}); | ||
]; | ||
const folio = builder.build(); | ||
+ export const it = folio.it; | ||
+ export const expect = folio.expect; | ||
module.exports = { | ||
- timeout: 30000, // Each test is given 30 seconds. | ||
+ timeout: 90000, // Each test is given 90 seconds. | ||
+ retries: 2, // Failing tests will be retried at most two times. | ||
projects, | ||
}; | ||
``` | ||
```ts | ||
// test/index.spec.ts | ||
import { it, expect } from "./fixtures"; | ||
#### Locally in the test file | ||
// Test functions go here | ||
it("should have modified viewport", async ({ context }) => { | ||
// ... | ||
}); | ||
``` | ||
With `test.use()` you can override some options for a file, or a `describe` block. | ||
### Modify launch options | ||
You can modify the built-in fixtures. This example modifies the default `browserOptions` with a slowMo. | ||
**Step 1**: Create a new file (say `test/fixtures.ts`) which contains our modifications. | ||
```ts | ||
// test/fixtures.ts | ||
import { folio as baseFolio } from "@playwright/test"; | ||
// my.spec.ts | ||
import { test, expect } from "@playwright/test"; | ||
const builder = baseFolio.extend(); | ||
// Run tests in this file with portrait-like viewport. | ||
test.use({ viewport: { width: 600, height: 900 } }); | ||
// Fixture modifications go here | ||
const folio = builder.build(); | ||
test('my test', async ({ page }) => { | ||
// Test code goes here. | ||
}); | ||
``` | ||
**Step 2**: Override the existing `browserOptions` fixture to configure the slowMo. | ||
#### Available options | ||
```diff | ||
// test/fixtures.ts | ||
import { folio as baseFolio } from "@playwright/test"; | ||
+ import { LaunchOptions } from "playwright"; | ||
See the full list of launch options in [`browserType.launch()`](https://playwright.dev/docs/api/class-browsertype#browsertypelaunchoptions) documentation. | ||
const builder = baseFolio.extend(); | ||
See the full list of context options in [`browser.newContext()`](https://playwright.dev/docs/api/class-browser#browsernewcontextoptions) documentation. | ||
+ builder.browserOptions.override(async ({ browserOptions }, runTest) => { | ||
+ const modifiedOptions: LaunchOptions = { | ||
+ ...browserOptions, // Default options | ||
+ slowMo: 50, | ||
+ } | ||
+ await runTest(modifiedOptions); | ||
+ }); | ||
Available testing options: | ||
- `screenshot: 'off' | 'on' | 'only-on-failure'` - Whether to capture a screenshot after each test, off by default. | ||
- `off` - Do not capture screenshots. | ||
- `on` - Capture screenshot after each test. | ||
- `only-on-failure` - Capture screenshot after each test failure. | ||
- `video: 'off' | 'on' | 'retain-on-failure' | 'retry-with-video'` - Whether to record video for each test, off by default. | ||
- `off` - Do not record video. | ||
- `on` - Record video for each test. | ||
- `retain-on-failure` - Record video for each test, but remove all videos from successful test runs. | ||
- `retry-with-video` - Record video only when retrying a test. | ||
const folio = builder.build(); | ||
``` | ||
Most notable testing options from [Folio documentation][folio]: | ||
- `retries: number` - Each failing test will be retried up to the certain number of times. | ||
- `testDir: string` - Directory where test runner should search for test files. | ||
- `timeout: number` - Timeout in milliseconds for each test. | ||
- `workers: number` - The maximum number of worker processes to run in parallel. | ||
**Step 3**: Export `it` and other helpers from the modified fixtures. In your test files, import the modified fixture. | ||
```diff | ||
// test/fixtures.ts | ||
import { folio as baseFolio } from "@playwright/test"; | ||
import { LaunchOptions } from "playwright"; | ||
const builder = baseFolio.extend(); | ||
builder.browserOptions.override(async ({ browserOptions }, runTest) => { | ||
const modifiedOptions: LaunchOptions = { | ||
...browserOptions, // default | ||
slowMo: 50, | ||
} | ||
await runTest(modifiedOptions); | ||
}); | ||
const folio = builder.build(); | ||
+ export const it = folio.it; | ||
+ export const expect = folio.expect; | ||
``` | ||
```ts | ||
// test/index.spec.ts | ||
import { it, expect } from "./fixtures"; | ||
// Test functions go here | ||
it("should have the slow mo", async ({ context }) => { | ||
// ... | ||
}); | ||
``` | ||
### Skip tests with annotations | ||
@@ -372,5 +392,4 @@ | ||
```js | ||
it("should be skipped on firefox", (test, { browserName }) => { | ||
test.skip(browserName === "firefox", "optional description for the skip") | ||
}, async ({ page, browserName }) => { | ||
test("should be skipped on firefox", async ({ page, browserName }) => { | ||
test.skip(browserName === "firefox", "optional description for the skip"); | ||
// Test function | ||
@@ -384,18 +403,45 @@ }); | ||
```sh | ||
# Specify output file as an environment variable | ||
# Linux/macOS | ||
export FOLIO_JUNIT_OUTPUT_NAME=junit.xml | ||
# Windows | ||
set FOLIO_JUNIT_OUTPUT_NAME=junit.xml | ||
```diff | ||
// config.ts | ||
import { PlaywrightTestProject } from "@playwright/test"; | ||
# Use junit and CLI reporters | ||
npx folio --reporter=junit,line | ||
const projects: PlaywrightTestProject[] = [ | ||
{ | ||
name: 'chromium', | ||
use: { | ||
browserName: 'chromium', | ||
headless: true, | ||
viewport: { width: 1280, height: 720 }, | ||
}, | ||
}, | ||
# See all supported reporters | ||
npx folio --help | ||
{ | ||
name: 'webkit', | ||
use: { | ||
browserName: 'webkit', | ||
headless: true, | ||
viewport: { width: 1280, height: 720 }, | ||
}, | ||
}, | ||
{ | ||
name: 'firefox', | ||
use: { | ||
browserName: 'firefox', | ||
headless: true, | ||
viewport: { width: 1280, height: 720 }, | ||
}, | ||
} | ||
]; | ||
module.exports = { | ||
timeout: 30000, // Each test is given 30 seconds. | ||
+ reporter: [ | ||
+ 'list', // Terminal output | ||
+ { name: 'junit', outputFile: 'report.xml' }, // And junit report | ||
+ ], | ||
projects, | ||
}; | ||
``` | ||
[browser-opts]: https://playwright.dev/docs/api/class-browsertype#browsertypelaunchoptions | ||
[context-opts]: https://playwright.dev/docs/api/class-browser#browsernewcontextoptions | ||
[multi-page]: https://playwright.dev/docs/multi-pages | ||
@@ -405,3 +451,4 @@ [browser]: https://playwright.dev/docs/api/class-browser | ||
[page]: https://playwright.dev/docs/api/class-page | ||
[folio]: https://github.com/microsoft/folio | ||
[folio-annotations]: https://github.com/microsoft/folio#annotations | ||
[folio-parameters]: https://github.com/microsoft/folio#parameters | ||
[folio-cli]: https://github.com/microsoft/folio#command-line |
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 1 instance in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 2 instances in 1 package
41652
416
441
3
1
Updatedfolio@=0.4.0-alpha17