@playwright/test
Advanced tools
Comparing version 0.1101.0 to 0.1102.0-alpha1
@@ -16,53 +16,181 @@ /** | ||
*/ | ||
import { Browser, BrowserContext, BrowserContextOptions, Page, LaunchOptions } from 'playwright'; | ||
import * as folio from 'folio'; | ||
export * from 'folio'; | ||
export { BrowserContextOptions, LaunchOptions } from 'playwright'; | ||
export declare type BrowserName = 'chromium' | 'firefox' | 'webkit'; | ||
export declare type PlaywrightTestArgs = { | ||
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; | ||
}; | ||
declare type PlaywrightWorkerFixtures = { | ||
playwright: typeof import('playwright'); | ||
browserName: BrowserName; | ||
browserType: BrowserType<Browser>; | ||
browserOptions: LaunchOptions; | ||
browser: Browser; | ||
isChromium: boolean; | ||
isFirefox: boolean; | ||
isWebKit: boolean; | ||
isWindows: boolean; | ||
isMac: boolean; | ||
isLinux: boolean; | ||
}; | ||
declare type PlaywrightTestFixtures = { | ||
contextOptions: BrowserContextOptions; | ||
contextFactory: (options?: BrowserContextOptions) => Promise<BrowserContext>; | ||
context: BrowserContext; | ||
page: Page; | ||
}; | ||
export declare type PlaywrightOptions = LaunchOptions & BrowserContextOptions & { | ||
screenshot?: 'off' | 'on' | 'only-on-failure'; | ||
video?: 'off' | 'on' | 'retain-on-failure' | 'retry-with-video'; | ||
snapshotPathSegment?: string; | ||
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 class PlaywrightEnv implements folio.Env<PlaywrightTestArgs> { | ||
private _playwright; | ||
private _browserName; | ||
private _options; | ||
private _browser; | ||
private _context; | ||
private _page; | ||
private _allPages; | ||
constructor(browserName: BrowserName, options?: PlaywrightOptions); | ||
beforeAll(): Promise<void>; | ||
beforeEach(testInfo: folio.TestInfo): Promise<{ | ||
playwright: typeof import("playwright"); | ||
browserName: "chromium" | "firefox" | "webkit"; | ||
browser: Browser; | ||
context: BrowserContext; | ||
page: Page; | ||
}>; | ||
afterEach(testInfo: folio.TestInfo): Promise<void>; | ||
afterAll(): Promise<void>; | ||
} | ||
export declare class ChromiumEnv extends PlaywrightEnv { | ||
constructor(options?: PlaywrightOptions); | ||
} | ||
export declare class FirefoxEnv extends PlaywrightEnv { | ||
constructor(options?: PlaywrightOptions); | ||
} | ||
export declare class WebKitEnv extends PlaywrightEnv { | ||
constructor(options?: PlaywrightOptions); | ||
} | ||
declare type PlaywrightTestOptions = { | ||
contextOptions?: BrowserContextOptions; | ||
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 function newTestType<TestArgs = {}, TestOptions = {}>(): folio.TestType<TestArgs & PlaywrightTestArgs, TestOptions & PlaywrightTestOptions>; | ||
export declare const test: folio.TestType<PlaywrightTestArgs, PlaywrightTestOptions>; | ||
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; | ||
//# sourceMappingURL=index.d.ts.map |
215
out/index.js
@@ -17,122 +17,109 @@ "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.test = exports.newTestType = exports.WebKitEnv = exports.FirefoxEnv = exports.ChromiumEnv = exports.PlaywrightEnv = void 0; | ||
const folio = __importStar(require("folio")); | ||
const fs = __importStar(require("fs")); | ||
const util = __importStar(require("util")); | ||
__exportStar(require("folio"), exports); | ||
class PlaywrightEnv { | ||
constructor(browserName, options = {}) { | ||
this._allPages = []; | ||
this._browserName = browserName; | ||
this._options = options; | ||
} | ||
async beforeAll() { | ||
this._playwright = require('playwright'); | ||
this._browser = await this._playwright[this._browserName].launch({ | ||
...this._options, | ||
handleSIGINT: false, | ||
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(''), | ||
}); | ||
} | ||
async beforeEach(testInfo) { | ||
const options = testInfo.testOptions; | ||
const recordVideo = this._options.video === 'on' || this._options.video === 'retain-on-failure' || | ||
(this._options.video === 'retry-with-video' && !!testInfo.retry); | ||
this._context = await this._browser.newContext({ | ||
recordVideo: recordVideo ? { dir: testInfo.outputPath('') } : undefined, | ||
...this._options, | ||
...options.contextOptions | ||
}); | ||
this._allPages = []; | ||
this._context.on('page', page => this._allPages.push(page)); | ||
this._page = await this._context.newPage(); | ||
testInfo.snapshotPathSegment = this._options.snapshotPathSegment || (this._browserName + '-' + process.platform); | ||
return { | ||
playwright: this._playwright, | ||
browserName: this._browserName, | ||
browser: this._browser, | ||
context: this._context, | ||
page: this._page, | ||
}; | ||
else { | ||
await run({}); | ||
} | ||
async afterEach(testInfo) { | ||
const testFailed = testInfo.status !== testInfo.expectedStatus; | ||
if (this._context) { | ||
if (this._options.screenshot === 'on' || (this._options.screenshot === 'only-on-failure' && testFailed)) { | ||
await Promise.all(this._context.pages().map((page, index) => { | ||
const screenshotPath = testInfo.outputPath(`test-${testFailed ? 'failed' : 'finished'}-${++index}.png`); | ||
return page.screenshot({ timeout: 5000, path: screenshotPath }).catch(e => { }); | ||
})); | ||
} | ||
await this._context.close(); | ||
}); | ||
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`) }); | ||
} | ||
const deleteVideos = this._options.video === 'retain-on-failure' && !testFailed; | ||
if (deleteVideos) { | ||
await Promise.all(this._allPages.map(async (page) => { | ||
const video = page.video(); | ||
if (!video) | ||
return; | ||
const videoPath = await video.path(); | ||
await util.promisify(fs.unlink)(videoPath).catch(e => { }); | ||
})); | ||
} | ||
this._allPages = []; | ||
this._context = undefined; | ||
this._page = undefined; | ||
} | ||
async afterAll() { | ||
if (this._browser) | ||
await this._browser.close(); | ||
this._browser = undefined; | ||
} | ||
} | ||
exports.PlaywrightEnv = PlaywrightEnv; | ||
// Environment that runs tests in Chromium. | ||
class ChromiumEnv extends PlaywrightEnv { | ||
constructor(options = {}) { | ||
super('chromium', options); | ||
} | ||
} | ||
exports.ChromiumEnv = ChromiumEnv; | ||
// Environment that runs tests in Firefox. | ||
class FirefoxEnv extends PlaywrightEnv { | ||
constructor(options = {}) { | ||
super('firefox', options); | ||
} | ||
} | ||
exports.FirefoxEnv = FirefoxEnv; | ||
// Environment that runs tests in WebKit. | ||
class WebKitEnv extends PlaywrightEnv { | ||
constructor(options = {}) { | ||
super('webkit', options); | ||
} | ||
} | ||
exports.WebKitEnv = WebKitEnv; | ||
function newTestType() { | ||
return folio.newTestType(); | ||
} | ||
exports.newTestType = newTestType; | ||
exports.test = newTestType(); | ||
for (const context of contexts) | ||
await context.close(); | ||
}); | ||
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']); | ||
//# sourceMappingURL=index.js.map |
{ | ||
"name": "@playwright/test", | ||
"version": "0.1101.0", | ||
"version": "0.1102.0-alpha1", | ||
"license": "Apache-2.0", | ||
@@ -17,3 +17,3 @@ "author": { | ||
"prepublishOnly": "rm tsconfig.tsbuildinfo && rm -rf out && npm run build", | ||
"test": "folio --config=test/simple/config.ts && folio --config=test/complex/config.ts" | ||
"test": "folio test/" | ||
}, | ||
@@ -24,3 +24,3 @@ "repository": "github:Microsoft/playwright-test", | ||
"dependencies": { | ||
"folio": "=0.3.23-alpha", | ||
"folio": "=0.3.18", | ||
"playwright": "=1.10.0", | ||
@@ -27,0 +27,0 @@ "rimraf": "^3.0.2" |
414
README.md
@@ -0,4 +1,6 @@ | ||
# ⚠️ This project is not ready for production. Stay tuned! ⚠️ | ||
# 🎭 Playwright test runner [![npm version](https://img.shields.io/npm/v/@playwright/test.svg?style=flat)](https://www.npmjs.com/package/@playwright/test) | ||
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. | ||
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. | ||
@@ -10,3 +12,2 @@ 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) | ||
- [Write a configuration file](#write-a-configuration-file) | ||
- [Run the test](#run-the-test) | ||
@@ -19,5 +20,5 @@ - [Examples](#examples) | ||
- [Configuration](#configuration) | ||
- [Modify options](#modify-options) | ||
- [Modify context options](#modify-context-options) | ||
- [Modify launch options](#modify-launch-options) | ||
- [Skip tests with annotations](#skip-tests-with-annotations) | ||
- [Modify context options for a single test](#modify-context-options-for-a-single-test) | ||
- [Export JUnit report](#export-junit-report) | ||
@@ -37,6 +38,6 @@ | ||
```ts | ||
import { test, expect } from "@playwright/test"; | ||
```js | ||
import { it, expect } from "@playwright/test"; | ||
test("is a basic test with the page", async ({ page }) => { | ||
it("is a basic test with the page", async ({ page }) => { | ||
await page.goto("https://playwright.dev/"); | ||
@@ -48,30 +49,25 @@ 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. 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`. | ||
- `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. | ||
### Write a configuration file | ||
#### Specs and assertions | ||
Create `config.ts` to configure your tests. Here is an example configuration that runs every test in Chromium, Firefox and WebKit. | ||
- 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). | ||
```ts | ||
import { ChromiumEnv, FirefoxEnv, WebKitEnv, test, setConfig } from "@playwright/test"; | ||
```js | ||
const { it, describe } = require("@playwright/test"); | ||
setConfig({ | ||
testDir: __dirname, // Search for tests in this directory. | ||
timeout: 30000, // Each test is given 30 seconds. | ||
describe("feature foo", () => { | ||
it("is working correctly", async ({ page }) => { | ||
// Test function | ||
}); | ||
}); | ||
const options = { | ||
headless: true, // Run tests in headless browsers. | ||
viewport: { width: 1280, height: 720 }, | ||
}; | ||
// Run tests in three browsers. | ||
test.runWith(new ChromiumEnv(options), { tag: 'chromium' }); | ||
test.runWith(new FirefoxEnv(options), { tag: 'firefox' }); | ||
test.runWith(new WebKitEnv(options), { tag: 'webkit' }); | ||
``` | ||
@@ -81,25 +77,31 @@ | ||
Tests can be run in single or multiple browsers, in parallel or sequentially. | ||
Tests can be run on single or multiple browsers and with flags to generate screenshot on test failures. | ||
```sh | ||
# Run all tests across Chromium, Firefox and WebKit | ||
$ npx folio --config=config.ts | ||
npx folio | ||
# Run tests on a single browser | ||
$ npx folio --config=config.ts --tag=chromium | ||
npx folio --param browserName=chromium | ||
# Run tests in parallel | ||
$ npx folio --config=config.ts --workers=5 | ||
# Run all tests in headful mode | ||
npx folio --param headful | ||
# Run tests sequentially | ||
$ npx folio --config=config.ts --workers=1 | ||
# Run tests with slowMo (slows down Playwright operations by n milliseconds) | ||
npx folio --param slowMo=100 | ||
# Retry failing tests | ||
$ npx folio --config=config.ts --retries=2 | ||
# Save screenshots on failure in test-results directory | ||
npx folio --param screenshotOnFailure | ||
# Record videos | ||
npx folio --param video | ||
# Retry test failures | ||
npx folio --retries 3 | ||
# See all options | ||
$ npx folio --help | ||
npx folio --help | ||
``` | ||
Refer to the [command line documentation][folio-cli] for all options. | ||
Test runner CLI can be customized with [Folio parameters][folio-parameters]. | ||
@@ -113,3 +115,3 @@ #### Configure NPM scripts | ||
"scripts": { | ||
"test": "npx folio --config=config.ts" | ||
"test": "npx folio --param screenshotOnFailure" | ||
} | ||
@@ -119,26 +121,2 @@ } | ||
### 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 { test, expect } = require("@playwright/test"); | ||
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"); | ||
}); | ||
}); | ||
``` | ||
----------- | ||
@@ -153,5 +131,5 @@ | ||
```js | ||
import { test } from "@playwright/test"; | ||
import { it } from "@playwright/test"; | ||
test("tests on multiple web pages", async ({ context }) => { | ||
it("tests on multiple web pages", async ({ context }) => { | ||
const pageFoo = await context.newPage(); | ||
@@ -165,24 +143,20 @@ const pageBar = await context.newPage(); | ||
`options` in the configuration file can be used to configure mobile emulation in the default `context`. | ||
The `contextOptions` fixture defines default options used for context creation. This fixture can be overriden to configure mobile emulation in the default `context`. | ||
```diff | ||
// config.ts | ||
import { ChromiumEnv, FirefoxEnv, WebKitEnv, test, setConfig } from "@playwright/test"; | ||
+ import { devices } from "playwright"; | ||
```js | ||
import { folio } from "@playwright/test"; | ||
import { devices } from "playwright"; | ||
setConfig({ | ||
testDir: __dirname, // Search for tests in this directory. | ||
timeout: 30000, // Each test is given 30 seconds. | ||
const fixtures = folio.extend(); | ||
fixtures.contextOptions.override(async ({ contextOptions }, runTest) => { | ||
await runTest({ | ||
...contextOptions, | ||
...devices["iPhone 11"] | ||
}); | ||
}); | ||
const { it, describe, extend } = fixtures.build(); | ||
const options = { | ||
headless: true, // Run tests in headless browsers. | ||
- viewport: { width: 1280, height: 720 }, | ||
+ ...devices["iPhone 11"], | ||
}; | ||
// Run tests in three browsers. | ||
test.runWith(new ChromiumEnv(options), { tag: 'chromium' }); | ||
test.runWith(new FirefoxEnv(options), { tag: 'firefox' }); | ||
test.runWith(new WebKitEnv(options), { tag: 'webkit' }); | ||
it("uses mobile emulation", async ({ context }) => { | ||
// Test function | ||
}); | ||
``` | ||
@@ -192,19 +166,31 @@ | ||
Define a custom route that mocks network calls for a browser context. | ||
Define a custom argument that mocks networks call for a browser context. | ||
```js | ||
// In foo.spec.ts | ||
import { test, expect } from "@playwright/test"; | ||
// In fixtures.ts | ||
import { folio as base } from "@playwright/test"; | ||
import { BrowserContext } from "playwright"; | ||
test.beforeEach(async ({ context }) => { | ||
// Block any css requests for each test in this file. | ||
await context.route(/.css/, route => route.abort()); | ||
// 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("loads page without css", async ({ page }) => { | ||
// Alternatively, block any png requests just for this test. | ||
await page.route(/.png/, route => route.abort()); | ||
export folio = fixtures.build(); | ||
``` | ||
// Test function code. | ||
```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(); | ||
await page.goto("https://stackoverflow.com"); | ||
// Test function code | ||
}); | ||
@@ -218,8 +204,8 @@ ``` | ||
```js | ||
import { test, expect } from "@playwright/test"; | ||
import { it, expect } from "@playwright/test"; | ||
test("compares page screenshot", async ({ page }) => { | ||
it("compares page screenshot", async ({ page, browserName }) => { | ||
await page.goto("https://stackoverflow.com"); | ||
const screenshot = await page.screenshot(); | ||
expect(screenshot).toMatchSnapshot(`test.png`, { threshold: 0.2 }); | ||
expect(screenshot).toMatchSnapshot(`test-${browserName}.png`, { threshold: 0.2 }); | ||
}); | ||
@@ -239,108 +225,152 @@ ``` | ||
### Modify options | ||
### Modify context options | ||
You can modify browser launch options, context creation options and testing options in the configuration file. | ||
You can modify the built-in fixtures. This example modifies the default `contextOptions` with a custom viewport size. | ||
**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"; | ||
const builder = baseFolio.extend(); | ||
// Fixture modifications go here | ||
const folio = builder.build(); | ||
``` | ||
**Step 2**: Override the existing `contextOptions` fixture to configure viewport size and HTTPS error handling. | ||
```diff | ||
// config.ts | ||
import { ChromiumEnv, FirefoxEnv, WebKitEnv, test, setConfig } from "@playwright/test"; | ||
// test/fixtures.ts | ||
import { folio as baseFolio } from "@playwright/test"; | ||
+ import { BrowserContextOptions } from "playwright"; | ||
setConfig({ | ||
testDir: __dirname, // Search for tests in this directory. | ||
- 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. | ||
const builder = baseFolio.extend(); | ||
+ builder.contextOptions.override(async ({ contextOptions }, runTest) => { | ||
+ const modifiedOptions: BrowserContextOptions = { | ||
+ ...contextOptions, // default options | ||
+ viewport: { width: 1440, height: 900 }, | ||
+ ignoreHTTPSErrors: true | ||
+ } | ||
+ await runTest(modifiedOptions); | ||
+ }); | ||
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 | ||
} | ||
await runTest(modifiedOptions); | ||
}); | ||
const options = { | ||
- headless: true, // Run tests in headless browsers. | ||
- 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', | ||
}; | ||
const folio = builder.build(); | ||
// Run tests in three browsers. | ||
test.runWith(new ChromiumEnv(options), { tag: 'chromium' }); | ||
test.runWith(new FirefoxEnv(options), { tag: 'firefox' }); | ||
test.runWith(new WebKitEnv(options), { tag: 'webkit' }); | ||
+ export const it = folio.it; | ||
+ export const expect = folio.expect; | ||
``` | ||
See the full list of launch options in [`browserType.launch()`](https://playwright.dev/docs/api/class-browsertype#browsertypelaunchoptions) documentation. | ||
```ts | ||
// test/index.spec.ts | ||
import { it, expect } from "./fixtures"; | ||
See the full list of context options in [`browser.newContext()`](https://playwright.dev/docs/api/class-browser#browsernewcontextoptions) documentation. | ||
// Test functions go here | ||
it("should have modified viewport", async ({ context }) => { | ||
// ... | ||
}); | ||
``` | ||
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. | ||
### Modify launch options | ||
Most notable `setConfig` options, see the full list in [Folio documentation](https://github.com/microsoft/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. | ||
You can modify the built-in fixtures. This example modifies the default `browserOptions` with a slowMo. | ||
#### Browser-specific options | ||
**Step 1**: Create a new file (say `test/fixtures.ts`) which contains our modifications. | ||
You can specify different options for each browser when creating a corresponding environment. | ||
```ts | ||
// test/fixtures.ts | ||
import { folio as baseFolio } from "@playwright/test"; | ||
const builder = baseFolio.extend(); | ||
// Fixture modifications go here | ||
const folio = builder.build(); | ||
``` | ||
**Step 2**: Override the existing `browserOptions` fixture to configure the slowMo. | ||
```diff | ||
// config.ts | ||
import { ChromiumEnv, FirefoxEnv, WebKitEnv, test, setConfig } from "@playwright/test"; | ||
// test/fixtures.ts | ||
import { folio as baseFolio } from "@playwright/test"; | ||
+ import { LaunchOptions } from "playwright"; | ||
setConfig({ | ||
testDir: __dirname, // Search for tests in this directory. | ||
timeout: 30000, // Each test is given 30 seconds. | ||
}); | ||
const builder = baseFolio.extend(); | ||
const options = { | ||
headless: true, // Run tests in headless browsers. | ||
viewport: { width: 1280, height: 720 }, | ||
}; | ||
+ builder.browserOptions.override(async ({ browserOptions }, runTest) => { | ||
+ const modifiedOptions: LaunchOptions = { | ||
+ ...browserOptions, // Default options | ||
+ slowMo: 50, | ||
+ } | ||
+ await runTest(modifiedOptions); | ||
+ }); | ||
// Run tests in three browsers. | ||
test.runWith(new ChromiumEnv(options), { tag: 'chromium' }); | ||
test.runWith(new FirefoxEnv(options), { tag: 'firefox' }); | ||
- test.runWith(new WebKitEnv(options), { tag: 'webkit' }); | ||
+ test.runWith(new WebKitEnv({ | ||
+ ...options, | ||
+ viewport: { width: 800, height: 600 }, // Use different viewport for WebKit. | ||
+ }), { tag: 'webkit' }); | ||
const folio = builder.build(); | ||
``` | ||
### Skip tests with annotations | ||
**Step 3**: Export `it` and other helpers from the modified fixtures. In your test files, import the modified fixture. | ||
The Playwright test runner can annotate tests to skip under certain parameters. This is enabled by [Folio annotations][folio-annotations]. | ||
```diff | ||
// test/fixtures.ts | ||
import { folio as baseFolio } from "@playwright/test"; | ||
import { LaunchOptions } from "playwright"; | ||
```js | ||
test("should be skipped on firefox", async ({ page, browserName }) => { | ||
test.skip(browserName === "firefox", "optional description for the skip"); | ||
// Test function | ||
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; | ||
``` | ||
### Modify context options for a single test | ||
```ts | ||
// test/index.spec.ts | ||
import { it, expect } from "./fixtures"; | ||
Pass a second parameter that has `contextOptions` property to the `test` function: | ||
// Test functions go here | ||
it("should have the slow mo", async ({ context }) => { | ||
// ... | ||
}); | ||
``` | ||
### Skip tests with annotations | ||
The Playwright test runner can annotate tests to skip under certain parameters. This is enabled by [Folio annotations][folio-annotations]. | ||
```js | ||
const options = { | ||
contextOptions: { | ||
ignoreHTTPSErrors: true, | ||
} | ||
}; | ||
test("no https errors in this test", options, async ({ page }) => { | ||
it("should be skipped on firefox", (test, { browserName }) => { | ||
test.skip(browserName === "firefox", "optional description for the skip") | ||
}, async ({ page, browserName }) => { | ||
// Test function | ||
@@ -354,30 +384,18 @@ }); | ||
```diff | ||
// config.ts | ||
import { ChromiumEnv, FirefoxEnv, WebKitEnv, test, setConfig } from "@playwright/test"; | ||
+ import { reporters, setReporters } from "@playwright/test"; | ||
```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 | ||
setConfig({ | ||
testDir: __dirname, // Search for tests in this directory. | ||
timeout: 30000, // Each test is given 30 seconds. | ||
}); | ||
# Use junit and CLI reporters | ||
npx folio --reporter=junit,line | ||
+ setReporters([ | ||
+ // Report to the terminal with "line" reporter. | ||
+ new reporters.line(), | ||
+ // Additionally, output a JUnit XML file. | ||
+ new reporters.junit({ outputFile: 'junit.xml' }), | ||
+ ]); | ||
const options = { | ||
headless: true, // Run tests in headless browsers. | ||
viewport: { width: 1280, height: 720 }, | ||
}; | ||
// Run tests in three browsers. | ||
test.runWith(new ChromiumEnv(options), { tag: 'chromium' }); | ||
test.runWith(new FirefoxEnv(options), { tag: 'firefox' }); | ||
test.runWith(new WebKitEnv(options), { tag: 'webkit' }); | ||
# See all supported reporters | ||
npx folio --help | ||
``` | ||
[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 | ||
@@ -388,2 +406,2 @@ [browser]: https://playwright.dev/docs/api/class-browser | ||
[folio-annotations]: https://github.com/microsoft/folio#annotations | ||
[folio-cli]: https://github.com/microsoft/folio#command-line | ||
[folio-parameters]: https://github.com/microsoft/folio#parameters |
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 2 instances in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
38425
317
394
5
1
Updatedfolio@=0.3.18