diff --git a/docs/src/test-api/class-testoptions.md b/docs/src/test-api/class-testoptions.md index 7b022addb1..7eb4a57cdc 100644 --- a/docs/src/test-api/class-testoptions.md +++ b/docs/src/test-api/class-testoptions.md @@ -190,7 +190,10 @@ Learn more about [various timeouts](../test-timeouts.md). ## property: TestOptions.screenshot * since: v1.10 -- type: <[Screenshot]<"off"|"on"|"only-on-failure">> +- type: <[Object]|[ScreenshotMode]<"off"|"on"||"only-on-failure">> + - `mode` <[ScreenshotMode]<"off"|"on"|"only-on-failure">> Automatic screenshot mode. + - `fullPage` ?<[boolean]> When true, takes a screenshot of the full scrollable page, instead of the currently visible viewport. Defaults to `false`. + - `omitBackground` ?<[boolean]> Hides default white background and allows capturing screenshots with transparency. Not applicable to `jpeg` images. Defaults to `false`. Whether to automatically capture a screenshot after each test. Defaults to `'off'`. * `'off'`: Do not capture screenshots. diff --git a/packages/playwright-test/src/index.ts b/packages/playwright-test/src/index.ts index 76fbee4e3c..8416ba556b 100644 --- a/packages/playwright-test/src/index.ts +++ b/packages/playwright-test/src/index.ts @@ -255,6 +255,8 @@ const playwrightFixtures: Fixtures = ({ if (debugMode()) testInfo.setTimeout(0); + const screenshotOptions = typeof screenshot !== 'string' ? { fullPage: screenshot.fullPage, omitBackground: screenshot.omitBackground } : undefined; + const screenshotMode = typeof screenshot === 'string' ? screenshot : screenshot.mode; const traceMode = normalizeTraceMode(trace); const defaultTraceOptions = { screenshots: true, snapshots: true, sources: true }; const traceOptions = typeof trace === 'string' ? defaultTraceOptions : { ...defaultTraceOptions, ...trace, mode: undefined }; @@ -342,7 +344,7 @@ const playwrightFixtures: Fixtures = ({ temporaryScreenshots.push(screenshotPath); // Pass caret=initial to avoid any evaluations that might slow down the screenshot // and let the page modify itself from the problematic state it had at the moment of failure. - await page.screenshot({ timeout: 5000, path: screenshotPath, caret: 'initial' }).catch(() => {}); + await page.screenshot({ ...screenshotOptions, timeout: 5000, path: screenshotPath, caret: 'initial' }).catch(() => {}); }; const screenshotOnTestFailure = async () => { @@ -354,7 +356,7 @@ const playwrightFixtures: Fixtures = ({ const onWillCloseContext = async (context: BrowserContext) => { await stopTracing(context.tracing); - if (screenshot === 'on' || screenshot === 'only-on-failure') { + if (screenshotMode === 'on' || screenshotMode === 'only-on-failure') { // Capture screenshot for now. We'll know whether we have to preserve them // after the test finishes. await Promise.all(context.pages().map(screenshotPage)); @@ -380,7 +382,7 @@ const playwrightFixtures: Fixtures = ({ const existingApiRequests: APIRequestContext[] = Array.from((playwright.request as any)._contexts as Set); await Promise.all(existingApiRequests.map(onDidCreateRequestContext)); } - if (screenshot === 'on' || screenshot === 'only-on-failure') + if (screenshotMode === 'on' || screenshotMode === 'only-on-failure') testInfoImpl._onTestFailureImmediateCallbacks.set(screenshotOnTestFailure, 'Screenshot on failure'); // 2. Run the test. @@ -389,7 +391,7 @@ const playwrightFixtures: Fixtures = ({ // 3. Determine whether we need the artifacts. const testFailed = testInfo.status !== testInfo.expectedStatus; const preserveTrace = captureTrace && (traceMode === 'on' || (testFailed && traceMode === 'retain-on-failure') || (traceMode === 'on-first-retry' && testInfo.retry === 1)); - const captureScreenshots = (screenshot === 'on' || (screenshot === 'only-on-failure' && testFailed)); + const captureScreenshots = screenshotMode === 'on' || (screenshotMode === 'only-on-failure' && testFailed); const traceAttachments: string[] = []; const addTraceAttachment = () => { @@ -446,7 +448,7 @@ const playwrightFixtures: Fixtures = ({ return; // Pass caret=initial to avoid any evaluations that might slow down the screenshot // and let the page modify itself from the problematic state it had at the moment of failure. - await page.screenshot({ timeout: 5000, path: addScreenshotAttachment(), caret: 'initial' }).catch(() => {}); + await page.screenshot({ ...screenshotOptions, timeout: 5000, path: addScreenshotAttachment(), caret: 'initial' }).catch(() => {}); })); } }).concat(leftoverApiRequests.map(async context => { diff --git a/packages/playwright-test/types/test.d.ts b/packages/playwright-test/types/test.d.ts index 523d059f84..36a3389d8e 100644 --- a/packages/playwright-test/types/test.d.ts +++ b/packages/playwright-test/types/test.d.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import type { APIRequestContext, Browser, BrowserContext, BrowserContextOptions, Page, LaunchOptions, ViewportSize, Geolocation, HTTPCredentials, Locator, APIResponse } from 'playwright-core'; +import type { APIRequestContext, Browser, BrowserContext, BrowserContextOptions, Page, LaunchOptions, ViewportSize, Geolocation, HTTPCredentials, Locator, APIResponse, PageScreenshotOptions } from 'playwright-core'; export * from 'playwright-core'; export type ReporterDescription = @@ -2965,7 +2965,7 @@ export interface PlaywrightWorkerOptions { * * Learn more about [automatic screenshots](https://playwright.dev/docs/test-configuration#automatic-screenshots). */ - screenshot: 'off' | 'on' | 'only-on-failure'; + screenshot: ScreenshotMode | { mode: ScreenshotMode } & Pick; /** * Whether to record trace for each test. Defaults to `'off'`. * - `'off'`: Do not record trace. @@ -2995,6 +2995,7 @@ export interface PlaywrightWorkerOptions { video: VideoMode | /** deprecated */ 'retry-with-video' | { mode: VideoMode, size?: ViewportSize }; } +export type ScreenshotMode = 'off' | 'on' | 'only-on-failure'; export type TraceMode = 'off' | 'on' | 'retain-on-failure' | 'on-first-retry'; export type VideoMode = 'off' | 'on' | 'retain-on-failure' | 'on-first-retry'; diff --git a/tests/page/pageTestApi.ts b/tests/page/pageTestApi.ts index cccde44901..5f315658fe 100644 --- a/tests/page/pageTestApi.ts +++ b/tests/page/pageTestApi.ts @@ -15,7 +15,7 @@ */ import type { Page, ViewportSize } from 'playwright-core'; -import type { VideoMode } from '@playwright/test'; +import type { PageScreenshotOptions, ScreenshotMode, VideoMode } from '@playwright/test'; export { expect } from '@playwright/test'; // Page test does not guarantee an isolated context, just a new page (because Android). @@ -26,6 +26,7 @@ export type PageTestFixtures = { export type PageWorkerFixtures = { headless: boolean; channel: string; + screenshot: ScreenshotMode | { mode: ScreenshotMode } & Pick; trace: 'off' | 'on' | 'retain-on-failure' | 'on-first-retry' | /** deprecated */ 'retry-with-trace'; video: VideoMode | { mode: VideoMode, size: ViewportSize }; browserName: 'chromium' | 'firefox' | 'webkit'; diff --git a/tests/playwright-test/__screenshots__/playwright.artifacts.spec.ts/screenshot-grid-fullpage.png b/tests/playwright-test/__screenshots__/playwright.artifacts.spec.ts/screenshot-grid-fullpage.png new file mode 100644 index 0000000000..0354694da1 Binary files /dev/null and b/tests/playwright-test/__screenshots__/playwright.artifacts.spec.ts/screenshot-grid-fullpage.png differ diff --git a/tests/playwright-test/playwright.artifacts.spec.ts b/tests/playwright-test/playwright.artifacts.spec.ts index c9e1a34f01..a3f01f74d2 100644 --- a/tests/playwright-test/playwright.artifacts.spec.ts +++ b/tests/playwright-test/playwright.artifacts.spec.ts @@ -189,6 +189,35 @@ test('should work with screenshot: only-on-failure', async ({ runInlineTest }, t ]); }); +test('should work with screenshot: only-on-failure & fullPage', async ({ runInlineTest, server }, testInfo) => { + const result = await runInlineTest({ + 'artifacts.spec.ts': ` + const { test } = pwt; + + test('should fail and take fullPage screenshots', async ({ page }) => { + await page.setViewportSize({ width: 500, height: 500 }); + await page.goto('${server.PREFIX}/grid.html'); + expect(1).toBe(2); + }); + `, + 'playwright.config.ts': ` + module.exports = { use: { screenshot: { mode: 'only-on-failure', fullPage: true } } }; + `, + }, { workers: 1 }); + expect(result.exitCode).toBe(1); + expect(result.passed).toBe(0); + expect(result.failed).toBe(1); + expect(listFiles(testInfo.outputPath('test-results'))).toEqual([ + 'artifacts-should-fail-and-take-fullPage-screenshots', + ' test-failed-1.png', + 'report.json', + ]); + const screenshotFailure = fs.readFileSync( + testInfo.outputPath('test-results', 'artifacts-should-fail-and-take-fullPage-screenshots', 'test-failed-1.png') + ); + expect.soft(screenshotFailure).toMatchSnapshot('screenshot-grid-fullpage.png'); +}); + test('should work with trace: on', async ({ runInlineTest }, testInfo) => { const result = await runInlineTest({ ...testFiles, diff --git a/utils/generate_types/overrides-test.d.ts b/utils/generate_types/overrides-test.d.ts index 1476d16d77..cd85aee175 100644 --- a/utils/generate_types/overrides-test.d.ts +++ b/utils/generate_types/overrides-test.d.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import type { APIRequestContext, Browser, BrowserContext, BrowserContextOptions, Page, LaunchOptions, ViewportSize, Geolocation, HTTPCredentials, Locator, APIResponse } from 'playwright-core'; +import type { APIRequestContext, Browser, BrowserContext, BrowserContextOptions, Page, LaunchOptions, ViewportSize, Geolocation, HTTPCredentials, Locator, APIResponse, PageScreenshotOptions } from 'playwright-core'; export * from 'playwright-core'; export type ReporterDescription = @@ -208,11 +208,12 @@ export interface PlaywrightWorkerOptions { channel: BrowserChannel | undefined; launchOptions: LaunchOptions; connectOptions: ConnectOptions | undefined; - screenshot: 'off' | 'on' | 'only-on-failure'; + screenshot: ScreenshotMode | { mode: ScreenshotMode } & Pick; trace: TraceMode | /** deprecated */ 'retry-with-trace' | { mode: TraceMode, snapshots?: boolean, screenshots?: boolean, sources?: boolean }; video: VideoMode | /** deprecated */ 'retry-with-video' | { mode: VideoMode, size?: ViewportSize }; } +export type ScreenshotMode = 'off' | 'on' | 'only-on-failure'; export type TraceMode = 'off' | 'on' | 'retain-on-failure' | 'on-first-retry'; export type VideoMode = 'off' | 'on' | 'retain-on-failure' | 'on-first-retry';