diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 5476e798dc..2a87d08c7a 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -37,10 +37,10 @@ jobs: # XVFB-RUN merges both STDOUT and STDERR, whereas we need only STDERR # Wrap `npm run` in a subshell to redirect STDERR to file. # Enable core dumps in the subshell. - - run: xvfb-run --auto-servernum --server-args="-screen 0 1280x960x24" -- bash -c "ulimit -c unlimited && npx test-runner test/ --jobs=1 --forbid-only --timeout=30000 --global-timeout=5400000 --retries=3 --reporter=dot,json && npm run coverage" + - run: xvfb-run --auto-servernum --server-args="-screen 0 1280x960x24" -- bash -c "ulimit -c unlimited && npx folio test/ --workers=1 --forbid-only --timeout=30000 --global-timeout=5400000 --retries=3 --reporter=dot,json && npm run coverage" env: BROWSER: ${{ matrix.browser }} - PWRUNNER_JSON_REPORT: "test-results/report.json" + FOLIO_JSON_OUTPUT_NAME: "test-results/report.json" - uses: actions/upload-artifact@v1 if: always() with: @@ -63,10 +63,10 @@ jobs: - uses: microsoft/playwright-github-action@v1 - run: npm ci - run: npm run build - - run: npx test-runner test/ --jobs=1 --forbid-only --timeout=30000 --global-timeout=5400000 --retries=3 --reporter=dot,json --shard=${{ matrix.shard }}/2 + - run: npx folio test/ --workers=1 --forbid-only --timeout=30000 --global-timeout=5400000 --retries=3 --reporter=dot,json --shard=${{ matrix.shard }}/2 env: BROWSER: ${{ matrix.browser }} - PWRUNNER_JSON_REPORT: "test-results/report.json" + FOLIO_JSON_OUTPUT_NAME: "test-results/report.json" - uses: actions/upload-artifact@v1 if: ${{ always() }} with: @@ -91,11 +91,11 @@ jobs: - uses: microsoft/playwright-github-action@v1 - run: npm ci - run: npm run build - - run: npx test-runner test/ --jobs=1 --forbid-only --timeout=30000 --global-timeout=5400000 --retries=3 --reporter=dot,json + - run: npx folio test/ --workers=1 --forbid-only --timeout=30000 --global-timeout=5400000 --retries=3 --reporter=dot,json shell: bash env: BROWSER: ${{ matrix.browser }} - PWRUNNER_JSON_REPORT: "test-results/report.json" + FOLIO_JSON_OUTPUT_NAME: "test-results/report.json" - uses: actions/upload-artifact@v1 if: ${{ always() }} with: @@ -142,12 +142,12 @@ jobs: # XVFB-RUN merges both STDOUT and STDERR, whereas we need only STDERR # Wrap `npm run` in a subshell to redirect STDERR to file. # Enable core dumps in the subshell. - - run: xvfb-run --auto-servernum --server-args="-screen 0 1280x960x24" -- bash -c "ulimit -c unlimited && npx test-runner test/ --jobs=1 --forbid-only --timeout=30000 --global-timeout=5400000 --retries=3 --reporter=dot,json" + - run: xvfb-run --auto-servernum --server-args="-screen 0 1280x960x24" -- bash -c "ulimit -c unlimited && npx folio test/ --workers=1 --forbid-only --timeout=30000 --global-timeout=5400000 --retries=3 --reporter=dot,json" if: ${{ always() }} env: BROWSER: ${{ matrix.browser }} HEADLESS: "false" - PWRUNNER_JSON_REPORT: "test-results/report.json" + FOLIO_JSON_OUTPUT_NAME: "test-results/report.json" - uses: actions/upload-artifact@v1 if: ${{ always() }} with: @@ -175,11 +175,11 @@ jobs: # XVFB-RUN merges both STDOUT and STDERR, whereas we need only STDERR # Wrap `npm run` in a subshell to redirect STDERR to file. # Enable core dumps in the subshell. - - run: xvfb-run --auto-servernum --server-args="-screen 0 1280x960x24" -- bash -c "ulimit -c unlimited && npx test-runner test/ --jobs=1 --forbid-only --timeout=30000 --global-timeout=5400000 --retries=3 --reporter=dot,json" + - run: xvfb-run --auto-servernum --server-args="-screen 0 1280x960x24" -- bash -c "ulimit -c unlimited && npx folio test/ --workers=1 --forbid-only --timeout=30000 --global-timeout=5400000 --retries=3 --reporter=dot,json" env: BROWSER: ${{ matrix.browser }} PWWIRE: true - PWRUNNER_JSON_REPORT: "test-results/report.json" + FOLIO_JSON_OUTPUT_NAME: "test-results/report.json" - uses: actions/upload-artifact@v1 if: ${{ always() }} with: @@ -207,11 +207,11 @@ jobs: # XVFB-RUN merges both STDOUT and STDERR, whereas we need only STDERR # Wrap `npm run` in a subshell to redirect STDERR to file. # Enable core dumps in the subshell. - - run: xvfb-run --auto-servernum --server-args="-screen 0 1280x960x24" -- bash -c "ulimit -c unlimited && npx test-runner test/ --jobs=1 --forbid-only --timeout=60000 --global-timeout=5400000 --retries=3 --reporter=dot,json" + - run: xvfb-run --auto-servernum --server-args="-screen 0 1280x960x24" -- bash -c "ulimit -c unlimited && npx folio test/ --workers=1 --forbid-only --timeout=60000 --global-timeout=5400000 --retries=3 --reporter=dot,json" env: BROWSER: ${{ matrix.browser }} TRACING: true - PWRUNNER_JSON_REPORT: "test-results/report.json" + FOLIO_JSON_OUTPUT_NAME: "test-results/report.json" - uses: actions/upload-artifact@v1 if: ${{ always() }} with: diff --git a/package-lock.json b/package-lock.json index a06cb307df..a852af0a6d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1159,33 +1159,6 @@ } } }, - "@playwright/test-runner": { - "version": "0.9.22", - "resolved": "https://registry.npmjs.org/@playwright/test-runner/-/test-runner-0.9.22.tgz", - "integrity": "sha512-U1RcwMUcL2dBKc4pa7zb0s43jPNRySeXV7QxTT4F7uiyVQp6VLxH/1CU36MM9iiBR43X6rGSdsjmD3FDzc3piw==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.10.4", - "@babel/core": "^7.11.4", - "@babel/plugin-proposal-class-properties": "^7.10.4", - "@babel/preset-env": "^7.11.0", - "@babel/preset-typescript": "^7.10.4", - "colors": "^1.4.0", - "commander": "^6.1.0", - "debug": "^4.1.5", - "expect": "^26.4.2", - "jpeg-js": "^0.4.2", - "micromatch": "^4.0.2", - "ms": "^2.1.2", - "pirates": "^4.0.1", - "pixelmatch": "^5.2.1", - "pngjs": "^5.0.0", - "pretty-format": "^26.4.2", - "rimraf": "^3.0.2", - "source-map-support": "^0.5.19", - "stack-utils": "^2.0.2" - } - }, "@sindresorhus/is": { "version": "0.14.0", "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz", @@ -1345,9 +1318,9 @@ } }, "@types/yargs": { - "version": "15.0.7", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.7.tgz", - "integrity": "sha512-Gf4u3EjaPNcC9cTu4/j2oN14nSVhr8PQ+BvBcBQHAhDZfl0bVIiLgvnRXv/dn58XhTm9UXvBpvJpDlwV65QxOA==", + "version": "15.0.8", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.8.tgz", + "integrity": "sha512-b0BYzFUzBpOhPjpl1wtAHU994jBeKF4TKVlT7ssFv44T617XNcPdRoG4AzHLVshLzlrF7i3lTelH7UbuNYV58Q==", "dev": true, "requires": { "@types/yargs-parser": "*" @@ -2225,9 +2198,9 @@ "dev": true }, "caniuse-lite": { - "version": "1.0.30001144", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001144.tgz", - "integrity": "sha512-4GQTEWNMnVZVOFG3BK0xvGeaDAtiPAbG2N8yuMXuXzx/c2Vd4XoMPO8+E918zeXn5IF0FRVtGShBfkfQea2wHQ==", + "version": "1.0.30001148", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001148.tgz", + "integrity": "sha512-E66qcd0KMKZHNJQt9hiLZGE3J4zuTqE1OnU53miEVtylFbwOEmeA5OsRu90noZful+XGSQOni1aT2tiqu/9yYw==", "dev": true }, "chalk": { @@ -2858,9 +2831,9 @@ } }, "electron-to-chromium": { - "version": "1.3.577", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.577.tgz", - "integrity": "sha512-dSb64JQSFif/pD8mpVAgSFkbVi6YHbK6JeEziwNNmXlr/Ne2rZtseFK5SM7JoWSLf6gP0gVvRGi4/2ZRhSX/rA==", + "version": "1.3.578", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.578.tgz", + "integrity": "sha512-z4gU6dA1CbBJsAErW5swTGAaU2TBzc2mPAonJb00zqW1rOraDo2zfBMDRvaz9cVic+0JEZiYbHWPw/fTaZlG2Q==", "dev": true }, "elliptic": { @@ -3296,9 +3269,9 @@ } }, "expect": { - "version": "26.5.2", - "resolved": "https://registry.npmjs.org/expect/-/expect-26.5.2.tgz", - "integrity": "sha512-ccTGrXZd8DZCcvCz4htGXTkd/LOoy6OEtiDS38x3/VVf6E4AQL0QoeksBiw7BtGR5xDNiRYPB8GN6pfbuTOi7w==", + "version": "26.5.3", + "resolved": "https://registry.npmjs.org/expect/-/expect-26.5.3.tgz", + "integrity": "sha512-kkpOhGRWGOr+TEFUnYAjfGvv35bfP+OlPtqPIJpOCR9DVtv8QV+p8zG0Edqafh80fsjeE+7RBcVUq1xApnYglw==", "dev": true, "requires": { "@jest/types": "^26.5.2", @@ -3676,6 +3649,33 @@ "readable-stream": "^2.3.6" } }, + "folio": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/folio/-/folio-0.3.6.tgz", + "integrity": "sha512-LPi0B9HHxdqCAvwgZOdcmPufJX4PjWbS2VN1QbN3mzapMoM1j+OI30YV5fU3e+4krJn50rKuki5WL6gdRIcJlQ==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.10.4", + "@babel/core": "^7.11.4", + "@babel/plugin-proposal-class-properties": "^7.10.4", + "@babel/preset-env": "^7.11.0", + "@babel/preset-typescript": "^7.10.4", + "colors": "^1.4.0", + "commander": "^6.1.0", + "debug": "^4.1.5", + "expect": "^26.4.2", + "jpeg-js": "^0.4.2", + "micromatch": "^4.0.2", + "ms": "^2.1.2", + "pirates": "^4.0.1", + "pixelmatch": "^5.2.1", + "pngjs": "^5.0.0", + "pretty-format": "^26.4.2", + "rimraf": "^3.0.2", + "source-map-support": "^0.5.19", + "stack-utils": "^2.0.2" + } + }, "for-in": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", diff --git a/package.json b/package.json index a49e4fe97d..0e97ee6d93 100644 --- a/package.json +++ b/package.json @@ -9,15 +9,15 @@ "node": ">=10.17.0" }, "scripts": { - "ctest": "cross-env BROWSER=chromium test-runner test/", - "ftest": "cross-env BROWSER=firefox test-runner test/", - "wtest": "cross-env BROWSER=webkit test-runner test/", - "test": "test-runner test/", + "ctest": "cross-env BROWSER=chromium folio test/", + "ftest": "cross-env BROWSER=firefox folio test/", + "wtest": "cross-env BROWSER=webkit folio test/", + "test": "folio test/", "eslint": "[ \"$CI\" = true ] && eslint --quiet -f codeframe --ext js,ts . || eslint --ext js,ts .", "tsc": "tsc -p .", "tsc-installer": "tsc -p ./src/install/tsconfig.json", "doc": "node utils/doclint/cli.js", - "test-infra": "test-runner utils/doclint/check_public_api/test/test.js && test-runner utils/doclint/preprocessor/test.js", + "test-infra": "folio utils/doclint/check_public_api/test/test.js && folio utils/doclint/preprocessor/test.js", "lint": "npm run eslint && npm run tsc && npm run doc && npm run check-deps && npm run generate-channels && npm run test-types && npm run test-infra", "clean": "rimraf lib && rimraf types", "prepare": "node install-from-github.js", @@ -50,7 +50,6 @@ "ws": "^7.3.1" }, "devDependencies": { - "@playwright/test-runner": "0.9.22", "@types/debug": "^4.1.5", "@types/extract-zip": "^1.6.2", "@types/mime": "^2.0.3", @@ -69,6 +68,7 @@ "electron": "^9.2.1", "eslint": "^7.7.0", "eslint-plugin-notice": "^0.9.10", + "folio": "=0.3.6", "formidable": "^1.2.2", "ncp": "^2.0.0", "node-stream-zip": "^1.11.3", diff --git a/test/browsertype-connect.spec.ts b/test/browsertype-connect.spec.ts index ed1d044381..2af2fae252 100644 --- a/test/browsertype-connect.spec.ts +++ b/test/browsertype-connect.spec.ts @@ -15,9 +15,9 @@ * limitations under the License. */ -import { serverFixtures } from './remoteServer.fixture'; +import { folio } from './remoteServer.fixture'; import * as fs from 'fs'; -const { it, expect, describe } = serverFixtures; +const { it, expect, describe } = folio; describe('connect', (suite, { wire }) => { suite.skip(wire); diff --git a/test/channels.spec.ts b/test/channels.spec.ts index 54ca034330..b1bdd0eec8 100644 --- a/test/channels.spec.ts +++ b/test/channels.spec.ts @@ -16,26 +16,20 @@ */ import domain from 'domain'; -import { fixtures as baseFixtures } from './fixtures'; +import { folio } from './fixtures'; import type { ChromiumBrowser } from '..'; -type DomainFixtures = { - domain: any; -}; - -const fixtures = baseFixtures.defineWorkerFixtures({ - domain: async ({ }, test) => { - const local = domain.create(); - local.run(() => { }); - let err; - local.on('error', e => err = e); - await test(null); - if (err) - throw err; - } +const fixtures = folio.extend<{ domain: any }, {}>(); +fixtures.domain.initWorker(async ({ }, run) => { + const local = domain.create(); + local.run(() => { }); + let err; + local.on('error', e => err = e); + await run(null); + if (err) + throw err; }); - -const { it, expect } = fixtures; +const { it, expect } = fixtures.build(); it('should work', async ({browser}) => { expect(!!browser['_connection']).toBeTruthy(); diff --git a/test/chromium/oopif.spec.ts b/test/chromium/oopif.spec.ts index fdcbfcbd98..ca930d3968 100644 --- a/test/chromium/oopif.spec.ts +++ b/test/chromium/oopif.spec.ts @@ -14,20 +14,18 @@ * limitations under the License. */ -import { fixtures as playwrightFixtures } from '../fixtures'; +import { folio } from '../fixtures'; -const fixtures = playwrightFixtures.overrideWorkerFixtures({ - browser: async ({browserType, defaultBrowserOptions}, test) => { - const browser = await browserType.launch({ - ...defaultBrowserOptions, - args: (defaultBrowserOptions.args || []).concat(['--site-per-process']) - }); - await test(browser); - await browser.close(); - } +const fixtures = folio.extend(); +fixtures.browser.overrideWorker(async ({browserType, defaultBrowserOptions}, run) => { + const browser = await browserType.launch({ + ...defaultBrowserOptions, + args: (defaultBrowserOptions.args || []).concat(['--site-per-process']) + }); + await run(browser); + await browser.close(); }); - -const { it, expect, describe } = fixtures; +const { it, expect, describe } = fixtures.build(); describe('oopif', (suite, { browserName }) => { suite.skip(browserName !== 'chromium'); diff --git a/test/chromium/tracing.spec.ts b/test/chromium/tracing.spec.ts index dc107597f2..e97aa365a9 100644 --- a/test/chromium/tracing.spec.ts +++ b/test/chromium/tracing.spec.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { fixtures as playwrightFixtures } from '../fixtures'; +import { folio } from '../fixtures'; import fs from 'fs'; import path from 'path'; import type { ChromiumBrowser } from '../..'; @@ -22,13 +22,11 @@ import type { ChromiumBrowser } from '../..'; type TestState = { outputTraceFile: string; }; -const fixtures = playwrightFixtures.defineTestFixtures({ - outputTraceFile: async ({ testInfo }, test) => { - await test(testInfo.outputPath(path.join(`trace.json`))); - } +const fixtures = folio.extend<{}, TestState>(); +fixtures.outputTraceFile.initTest(async ({ testInfo }, run) => { + await run(testInfo.outputPath(path.join(`trace.json`))); }); - -const { it, expect, describe } = fixtures; +const { it, expect, describe } = fixtures.build(); describe('oopif', (suite, { browserName }) => { suite.skip(browserName !== 'chromium'); diff --git a/test/download.spec.ts b/test/download.spec.ts index fcf6070160..8b1169ab1e 100644 --- a/test/download.spec.ts +++ b/test/download.spec.ts @@ -14,353 +14,355 @@ * limitations under the License. */ -import { serverFixtures } from './remoteServer.fixture'; -const { it, expect, beforeEach } = serverFixtures; +import { folio } from './remoteServer.fixture'; +const { it, expect, beforeEach, describe } = folio; import fs from 'fs'; import path from 'path'; import util from 'util'; -beforeEach(async ({server}) => { - server.setRoute('/download', (req, res) => { - res.setHeader('Content-Type', 'application/octet-stream'); - res.setHeader('Content-Disposition', 'attachment'); - res.end(`Hello world`); - }); - server.setRoute('/downloadWithFilename', (req, res) => { - res.setHeader('Content-Type', 'application/octet-stream'); - res.setHeader('Content-Disposition', 'attachment; filename=file.txt'); - res.end(`Hello world`); - }); -}); - -it('should report downloads with acceptDownloads: false', async ({page, server}) => { - await page.setContent(`download`); - const [ download ] = await Promise.all([ - page.waitForEvent('download'), - page.click('a') - ]); - let error; - expect(download.url()).toBe(`${server.PREFIX}/downloadWithFilename`); - expect(download.suggestedFilename()).toBe(`file.txt`); - await download.path().catch(e => error = e); - expect(await download.failure()).toContain('acceptDownloads'); - expect(error.message).toContain('acceptDownloads: true'); -}); - -it('should report downloads with acceptDownloads: true', async ({browser, server}) => { - const page = await browser.newPage({ acceptDownloads: true }); - await page.setContent(`download`); - const [ download ] = await Promise.all([ - page.waitForEvent('download'), - page.click('a') - ]); - const path = await download.path(); - expect(fs.existsSync(path)).toBeTruthy(); - expect(fs.readFileSync(path).toString()).toBe('Hello world'); - await page.close(); -}); - -it('should save to user-specified path', async ({testInfo, browser, server}) => { - const page = await browser.newPage({ acceptDownloads: true }); - await page.setContent(`download`); - const [ download ] = await Promise.all([ - page.waitForEvent('download'), - page.click('a') - ]); - const userPath = testInfo.outputPath('download.txt'); - await download.saveAs(userPath); - expect(fs.existsSync(userPath)).toBeTruthy(); - expect(fs.readFileSync(userPath).toString()).toBe('Hello world'); - await page.close(); -}); - -it('should save to user-specified path without updating original path', async ({testInfo, browser, server}) => { - const page = await browser.newPage({ acceptDownloads: true }); - await page.setContent(`download`); - const [ download ] = await Promise.all([ - page.waitForEvent('download'), - page.click('a') - ]); - const userPath = testInfo.outputPath('download.txt'); - await download.saveAs(userPath); - expect(fs.existsSync(userPath)).toBeTruthy(); - expect(fs.readFileSync(userPath).toString()).toBe('Hello world'); - - const originalPath = await download.path(); - expect(fs.existsSync(originalPath)).toBeTruthy(); - expect(fs.readFileSync(originalPath).toString()).toBe('Hello world'); - await page.close(); -}); - -it('should save to two different paths with multiple saveAs calls', async ({testInfo, browser, server}) => { - const page = await browser.newPage({ acceptDownloads: true }); - await page.setContent(`download`); - const [ download ] = await Promise.all([ - page.waitForEvent('download'), - page.click('a') - ]); - const userPath = testInfo.outputPath('download.txt'); - await download.saveAs(userPath); - expect(fs.existsSync(userPath)).toBeTruthy(); - expect(fs.readFileSync(userPath).toString()).toBe('Hello world'); - - const anotherUserPath = testInfo.outputPath('download (2).txt'); - await download.saveAs(anotherUserPath); - expect(fs.existsSync(anotherUserPath)).toBeTruthy(); - expect(fs.readFileSync(anotherUserPath).toString()).toBe('Hello world'); - await page.close(); -}); - -it('should save to overwritten filepath', async ({testInfo, browser, server}) => { - const page = await browser.newPage({ acceptDownloads: true }); - await page.setContent(`download`); - const [ download ] = await Promise.all([ - page.waitForEvent('download'), - page.click('a') - ]); - const dir = testInfo.outputPath('downloads'); - const userPath = path.join(dir, 'download.txt'); - await download.saveAs(userPath); - expect((await util.promisify(fs.readdir)(dir)).length).toBe(1); - await download.saveAs(userPath); - expect((await util.promisify(fs.readdir)(dir)).length).toBe(1); - expect(fs.existsSync(userPath)).toBeTruthy(); - expect(fs.readFileSync(userPath).toString()).toBe('Hello world'); - await page.close(); -}); - -it('should create subdirectories when saving to non-existent user-specified path', async ({testInfo, browser, server}) => { - const page = await browser.newPage({ acceptDownloads: true }); - await page.setContent(`download`); - const [ download ] = await Promise.all([ - page.waitForEvent('download'), - page.click('a') - ]); - const nestedPath = testInfo.outputPath(path.join('these', 'are', 'directories', 'download.txt')); - await download.saveAs(nestedPath); - expect(fs.existsSync(nestedPath)).toBeTruthy(); - expect(fs.readFileSync(nestedPath).toString()).toBe('Hello world'); - await page.close(); -}); - -it('should save when connected remotely', (test, { wire }) => { - test.skip(wire); -}, async ({testInfo, server, browserType, remoteServer}) => { - const browser = await browserType.connect({ wsEndpoint: remoteServer.wsEndpoint() }); - const page = await browser.newPage({ acceptDownloads: true }); - await page.setContent(`download`); - const [ download ] = await Promise.all([ - page.waitForEvent('download'), - page.click('a') - ]); - const nestedPath = testInfo.outputPath(path.join('these', 'are', 'directories', 'download.txt')); - await download.saveAs(nestedPath); - expect(fs.existsSync(nestedPath)).toBeTruthy(); - expect(fs.readFileSync(nestedPath).toString()).toBe('Hello world'); - const error = await download.path().catch(e => e); - expect(error.message).toContain('Path is not available when using browserType.connect(). Use download.saveAs() to save a local copy.'); - await browser.close(); -}); - -it('should error when saving with downloads disabled', async ({testInfo, browser, server}) => { - const page = await browser.newPage({ acceptDownloads: false }); - await page.setContent(`download`); - const [ download ] = await Promise.all([ - page.waitForEvent('download'), - page.click('a') - ]); - const userPath = testInfo.outputPath('download.txt'); - const { message } = await download.saveAs(userPath).catch(e => e); - expect(message).toContain('Pass { acceptDownloads: true } when you are creating your browser context'); - await page.close(); -}); - -it('should error when saving after deletion', async ({testInfo, browser, server}) => { - const page = await browser.newPage({ acceptDownloads: true }); - await page.setContent(`download`); - const [ download ] = await Promise.all([ - page.waitForEvent('download'), - page.click('a') - ]); - const userPath = testInfo.outputPath('download.txt'); - await download.delete(); - const { message } = await download.saveAs(userPath).catch(e => e); - expect(message).toContain('Download already deleted. Save before deleting.'); - await page.close(); -}); - -it('should error when saving after deletion when connected remotely', (test, { wire }) => { - test.skip(wire); -}, async ({testInfo, server, browserType, remoteServer}) => { - const browser = await browserType.connect({ wsEndpoint: remoteServer.wsEndpoint() }); - const page = await browser.newPage({ acceptDownloads: true }); - await page.setContent(`download`); - const [ download ] = await Promise.all([ - page.waitForEvent('download'), - page.click('a') - ]); - const userPath = testInfo.outputPath('download.txt'); - await download.delete(); - const { message } = await download.saveAs(userPath).catch(e => e); - expect(message).toContain('Download already deleted. Save before deleting.'); - await browser.close(); -}); - -it('should report non-navigation downloads', async ({browser, server}) => { - // Mac WebKit embedder does not download in this case, although Safari does. - server.setRoute('/download', (req, res) => { - res.setHeader('Content-Type', 'application/octet-stream'); - res.end(`Hello world`); - }); - - const page = await browser.newPage({ acceptDownloads: true }); - await page.goto(server.EMPTY_PAGE); - await page.setContent(`download`); - const [ download ] = await Promise.all([ - page.waitForEvent('download'), - page.click('a') - ]); - expect(download.suggestedFilename()).toBe(`file.txt`); - const path = await download.path(); - expect(fs.existsSync(path)).toBeTruthy(); - expect(fs.readFileSync(path).toString()).toBe('Hello world'); - await page.close(); -}); - -it(`should report download path within page.on('download', …) handler for Files`, async ({browser, server}) => { - const page = await browser.newPage({ acceptDownloads: true }); - const onDownloadPath = new Promise(res => { - page.on('download', dl => { - dl.path().then(res); +describe('download event', () => { + beforeEach(async ({server}) => { + server.setRoute('/download', (req, res) => { + res.setHeader('Content-Type', 'application/octet-stream'); + res.setHeader('Content-Disposition', 'attachment'); + res.end(`Hello world`); + }); + server.setRoute('/downloadWithFilename', (req, res) => { + res.setHeader('Content-Type', 'application/octet-stream'); + res.setHeader('Content-Disposition', 'attachment; filename=file.txt'); + res.end(`Hello world`); }); }); - await page.setContent(`download`); - await page.click('a'); - const path = await onDownloadPath; - expect(fs.readFileSync(path).toString()).toBe('Hello world'); - await page.close(); -}); -it(`should report download path within page.on('download', …) handler for Blobs`, async ({browser, server}) => { - const page = await browser.newPage({ acceptDownloads: true }); - const onDownloadPath = new Promise(res => { - page.on('download', dl => { - dl.path().then(res); + + it('should report downloads with acceptDownloads: false', async ({page, server}) => { + await page.setContent(`download`); + const [ download ] = await Promise.all([ + page.waitForEvent('download'), + page.click('a') + ]); + let error; + expect(download.url()).toBe(`${server.PREFIX}/downloadWithFilename`); + expect(download.suggestedFilename()).toBe(`file.txt`); + await download.path().catch(e => error = e); + expect(await download.failure()).toContain('acceptDownloads'); + expect(error.message).toContain('acceptDownloads: true'); + }); + + it('should report downloads with acceptDownloads: true', async ({browser, server}) => { + const page = await browser.newPage({ acceptDownloads: true }); + await page.setContent(`download`); + const [ download ] = await Promise.all([ + page.waitForEvent('download'), + page.click('a') + ]); + const path = await download.path(); + expect(fs.existsSync(path)).toBeTruthy(); + expect(fs.readFileSync(path).toString()).toBe('Hello world'); + await page.close(); + }); + + it('should save to user-specified path', async ({testInfo, browser, server}) => { + const page = await browser.newPage({ acceptDownloads: true }); + await page.setContent(`download`); + const [ download ] = await Promise.all([ + page.waitForEvent('download'), + page.click('a') + ]); + const userPath = testInfo.outputPath('download.txt'); + await download.saveAs(userPath); + expect(fs.existsSync(userPath)).toBeTruthy(); + expect(fs.readFileSync(userPath).toString()).toBe('Hello world'); + await page.close(); + }); + + it('should save to user-specified path without updating original path', async ({testInfo, browser, server}) => { + const page = await browser.newPage({ acceptDownloads: true }); + await page.setContent(`download`); + const [ download ] = await Promise.all([ + page.waitForEvent('download'), + page.click('a') + ]); + const userPath = testInfo.outputPath('download.txt'); + await download.saveAs(userPath); + expect(fs.existsSync(userPath)).toBeTruthy(); + expect(fs.readFileSync(userPath).toString()).toBe('Hello world'); + + const originalPath = await download.path(); + expect(fs.existsSync(originalPath)).toBeTruthy(); + expect(fs.readFileSync(originalPath).toString()).toBe('Hello world'); + await page.close(); + }); + + it('should save to two different paths with multiple saveAs calls', async ({testInfo, browser, server}) => { + const page = await browser.newPage({ acceptDownloads: true }); + await page.setContent(`download`); + const [ download ] = await Promise.all([ + page.waitForEvent('download'), + page.click('a') + ]); + const userPath = testInfo.outputPath('download.txt'); + await download.saveAs(userPath); + expect(fs.existsSync(userPath)).toBeTruthy(); + expect(fs.readFileSync(userPath).toString()).toBe('Hello world'); + + const anotherUserPath = testInfo.outputPath('download (2).txt'); + await download.saveAs(anotherUserPath); + expect(fs.existsSync(anotherUserPath)).toBeTruthy(); + expect(fs.readFileSync(anotherUserPath).toString()).toBe('Hello world'); + await page.close(); + }); + + it('should save to overwritten filepath', async ({testInfo, browser, server}) => { + const page = await browser.newPage({ acceptDownloads: true }); + await page.setContent(`download`); + const [ download ] = await Promise.all([ + page.waitForEvent('download'), + page.click('a') + ]); + const dir = testInfo.outputPath('downloads'); + const userPath = path.join(dir, 'download.txt'); + await download.saveAs(userPath); + expect((await util.promisify(fs.readdir)(dir)).length).toBe(1); + await download.saveAs(userPath); + expect((await util.promisify(fs.readdir)(dir)).length).toBe(1); + expect(fs.existsSync(userPath)).toBeTruthy(); + expect(fs.readFileSync(userPath).toString()).toBe('Hello world'); + await page.close(); + }); + + it('should create subdirectories when saving to non-existent user-specified path', async ({testInfo, browser, server}) => { + const page = await browser.newPage({ acceptDownloads: true }); + await page.setContent(`download`); + const [ download ] = await Promise.all([ + page.waitForEvent('download'), + page.click('a') + ]); + const nestedPath = testInfo.outputPath(path.join('these', 'are', 'directories', 'download.txt')); + await download.saveAs(nestedPath); + expect(fs.existsSync(nestedPath)).toBeTruthy(); + expect(fs.readFileSync(nestedPath).toString()).toBe('Hello world'); + await page.close(); + }); + + it('should save when connected remotely', (test, { wire }) => { + test.skip(wire); + }, async ({testInfo, server, browserType, remoteServer}) => { + const browser = await browserType.connect({ wsEndpoint: remoteServer.wsEndpoint() }); + const page = await browser.newPage({ acceptDownloads: true }); + await page.setContent(`download`); + const [ download ] = await Promise.all([ + page.waitForEvent('download'), + page.click('a') + ]); + const nestedPath = testInfo.outputPath(path.join('these', 'are', 'directories', 'download.txt')); + await download.saveAs(nestedPath); + expect(fs.existsSync(nestedPath)).toBeTruthy(); + expect(fs.readFileSync(nestedPath).toString()).toBe('Hello world'); + const error = await download.path().catch(e => e); + expect(error.message).toContain('Path is not available when using browserType.connect(). Use download.saveAs() to save a local copy.'); + await browser.close(); + }); + + it('should error when saving with downloads disabled', async ({testInfo, browser, server}) => { + const page = await browser.newPage({ acceptDownloads: false }); + await page.setContent(`download`); + const [ download ] = await Promise.all([ + page.waitForEvent('download'), + page.click('a') + ]); + const userPath = testInfo.outputPath('download.txt'); + const { message } = await download.saveAs(userPath).catch(e => e); + expect(message).toContain('Pass { acceptDownloads: true } when you are creating your browser context'); + await page.close(); + }); + + it('should error when saving after deletion', async ({testInfo, browser, server}) => { + const page = await browser.newPage({ acceptDownloads: true }); + await page.setContent(`download`); + const [ download ] = await Promise.all([ + page.waitForEvent('download'), + page.click('a') + ]); + const userPath = testInfo.outputPath('download.txt'); + await download.delete(); + const { message } = await download.saveAs(userPath).catch(e => e); + expect(message).toContain('Download already deleted. Save before deleting.'); + await page.close(); + }); + + it('should error when saving after deletion when connected remotely', (test, { wire }) => { + test.skip(wire); + }, async ({testInfo, server, browserType, remoteServer}) => { + const browser = await browserType.connect({ wsEndpoint: remoteServer.wsEndpoint() }); + const page = await browser.newPage({ acceptDownloads: true }); + await page.setContent(`download`); + const [ download ] = await Promise.all([ + page.waitForEvent('download'), + page.click('a') + ]); + const userPath = testInfo.outputPath('download.txt'); + await download.delete(); + const { message } = await download.saveAs(userPath).catch(e => e); + expect(message).toContain('Download already deleted. Save before deleting.'); + await browser.close(); + }); + + it('should report non-navigation downloads', async ({browser, server}) => { + // Mac WebKit embedder does not download in this case, although Safari does. + server.setRoute('/download', (req, res) => { + res.setHeader('Content-Type', 'application/octet-stream'); + res.end(`Hello world`); }); - }); - await page.goto(server.PREFIX + '/download-blob.html'); - await page.click('a'); - const path = await onDownloadPath; - expect(fs.readFileSync(path).toString()).toBe('Hello world'); - await page.close(); -}); -it('should report alt-click downloads', (test, { browserName }) => { - test.fixme(browserName === 'firefox' || browserName === 'webkit'); -}, async ({browser, server}) => { - // Firefox does not download on alt-click by default. - // Our WebKit embedder does not download on alt-click, although Safari does. - server.setRoute('/download', (req, res) => { - res.setHeader('Content-Type', 'application/octet-stream'); - res.end(`Hello world`); + + const page = await browser.newPage({ acceptDownloads: true }); + await page.goto(server.EMPTY_PAGE); + await page.setContent(`download`); + const [ download ] = await Promise.all([ + page.waitForEvent('download'), + page.click('a') + ]); + expect(download.suggestedFilename()).toBe(`file.txt`); + const path = await download.path(); + expect(fs.existsSync(path)).toBeTruthy(); + expect(fs.readFileSync(path).toString()).toBe('Hello world'); + await page.close(); }); - const page = await browser.newPage({ acceptDownloads: true }); - await page.goto(server.EMPTY_PAGE); - await page.setContent(`download`); - const [ download ] = await Promise.all([ - page.waitForEvent('download'), - page.click('a', { modifiers: ['Alt']}) - ]); - const path = await download.path(); - expect(fs.existsSync(path)).toBeTruthy(); - expect(fs.readFileSync(path).toString()).toBe('Hello world'); - await page.close(); -}); + it(`should report download path within page.on('download', …) handler for Files`, async ({browser, server}) => { + const page = await browser.newPage({ acceptDownloads: true }); + const onDownloadPath = new Promise(res => { + page.on('download', dl => { + dl.path().then(res); + }); + }); + await page.setContent(`download`); + await page.click('a'); + const path = await onDownloadPath; + expect(fs.readFileSync(path).toString()).toBe('Hello world'); + await page.close(); + }); + it(`should report download path within page.on('download', …) handler for Blobs`, async ({browser, server}) => { + const page = await browser.newPage({ acceptDownloads: true }); + const onDownloadPath = new Promise(res => { + page.on('download', dl => { + dl.path().then(res); + }); + }); + await page.goto(server.PREFIX + '/download-blob.html'); + await page.click('a'); + const path = await onDownloadPath; + expect(fs.readFileSync(path).toString()).toBe('Hello world'); + await page.close(); + }); + it('should report alt-click downloads', (test, { browserName }) => { + test.fixme(browserName === 'firefox' || browserName === 'webkit'); + }, async ({browser, server}) => { + // Firefox does not download on alt-click by default. + // Our WebKit embedder does not download on alt-click, although Safari does. + server.setRoute('/download', (req, res) => { + res.setHeader('Content-Type', 'application/octet-stream'); + res.end(`Hello world`); + }); -it('should report new window downloads', (test, { browserName, headful }) => { - test.fixme(browserName === 'chromium' && headful); -}, async ({browser, server}) => { - // TODO: - the test fails in headful Chromium as the popup page gets closed along - // with the session before download completed event arrives. - // - WebKit doesn't close the popup page - const page = await browser.newPage({ acceptDownloads: true }); - await page.setContent(`download`); - const [ download ] = await Promise.all([ - page.waitForEvent('download'), - page.click('a') - ]); - const path = await download.path(); - expect(fs.existsSync(path)).toBeTruthy(); - await page.close(); -}); + const page = await browser.newPage({ acceptDownloads: true }); + await page.goto(server.EMPTY_PAGE); + await page.setContent(`download`); + const [ download ] = await Promise.all([ + page.waitForEvent('download'), + page.click('a', { modifiers: ['Alt']}) + ]); + const path = await download.path(); + expect(fs.existsSync(path)).toBeTruthy(); + expect(fs.readFileSync(path).toString()).toBe('Hello world'); + await page.close(); + }); -it('should delete file', async ({browser, server}) => { - const page = await browser.newPage({ acceptDownloads: true }); - await page.setContent(`download`); - const [ download ] = await Promise.all([ - page.waitForEvent('download'), - page.click('a') - ]); - const path = await download.path(); - expect(fs.existsSync(path)).toBeTruthy(); - await download.delete(); - expect(fs.existsSync(path)).toBeFalsy(); - await page.close(); -}); + it('should report new window downloads', (test, { browserName, headful }) => { + test.fixme(browserName === 'chromium' && headful); + }, async ({browser, server}) => { + // TODO: - the test fails in headful Chromium as the popup page gets closed along + // with the session before download completed event arrives. + // - WebKit doesn't close the popup page + const page = await browser.newPage({ acceptDownloads: true }); + await page.setContent(`download`); + const [ download ] = await Promise.all([ + page.waitForEvent('download'), + page.click('a') + ]); + const path = await download.path(); + expect(fs.existsSync(path)).toBeTruthy(); + await page.close(); + }); -it('should expose stream', async ({browser, server}) => { - const page = await browser.newPage({ acceptDownloads: true }); - await page.setContent(`download`); - const [ download ] = await Promise.all([ - page.waitForEvent('download'), - page.click('a') - ]); - const stream = await download.createReadStream(); - let content = ''; - stream.on('data', data => content += data.toString()); - await new Promise(f => stream.on('end', f)); - expect(content).toBe('Hello world'); - await page.close(); -}); + it('should delete file', async ({browser, server}) => { + const page = await browser.newPage({ acceptDownloads: true }); + await page.setContent(`download`); + const [ download ] = await Promise.all([ + page.waitForEvent('download'), + page.click('a') + ]); + const path = await download.path(); + expect(fs.existsSync(path)).toBeTruthy(); + await download.delete(); + expect(fs.existsSync(path)).toBeFalsy(); + await page.close(); + }); -it('should delete downloads on context destruction', async ({browser, server}) => { - const page = await browser.newPage({ acceptDownloads: true }); - await page.setContent(`download`); - const [ download1 ] = await Promise.all([ - page.waitForEvent('download'), - page.click('a') - ]); - const [ download2 ] = await Promise.all([ - page.waitForEvent('download'), - page.click('a') - ]); - const path1 = await download1.path(); - const path2 = await download2.path(); - expect(fs.existsSync(path1)).toBeTruthy(); - expect(fs.existsSync(path2)).toBeTruthy(); - await page.context().close(); - expect(fs.existsSync(path1)).toBeFalsy(); - expect(fs.existsSync(path2)).toBeFalsy(); -}); + it('should expose stream', async ({browser, server}) => { + const page = await browser.newPage({ acceptDownloads: true }); + await page.setContent(`download`); + const [ download ] = await Promise.all([ + page.waitForEvent('download'), + page.click('a') + ]); + const stream = await download.createReadStream(); + let content = ''; + stream.on('data', data => content += data.toString()); + await new Promise(f => stream.on('end', f)); + expect(content).toBe('Hello world'); + await page.close(); + }); -it('should delete downloads on browser gone', async ({ server, browserType, defaultBrowserOptions }) => { - const browser = await browserType.launch(defaultBrowserOptions); - const page = await browser.newPage({ acceptDownloads: true }); - await page.setContent(`download`); - const [ download1 ] = await Promise.all([ - page.waitForEvent('download'), - page.click('a') - ]); - const [ download2 ] = await Promise.all([ - page.waitForEvent('download'), - page.click('a') - ]); - const path1 = await download1.path(); - const path2 = await download2.path(); - expect(fs.existsSync(path1)).toBeTruthy(); - expect(fs.existsSync(path2)).toBeTruthy(); - await browser.close(); - expect(fs.existsSync(path1)).toBeFalsy(); - expect(fs.existsSync(path2)).toBeFalsy(); - expect(fs.existsSync(path.join(path1, '..'))).toBeFalsy(); + it('should delete downloads on context destruction', async ({browser, server}) => { + const page = await browser.newPage({ acceptDownloads: true }); + await page.setContent(`download`); + const [ download1 ] = await Promise.all([ + page.waitForEvent('download'), + page.click('a') + ]); + const [ download2 ] = await Promise.all([ + page.waitForEvent('download'), + page.click('a') + ]); + const path1 = await download1.path(); + const path2 = await download2.path(); + expect(fs.existsSync(path1)).toBeTruthy(); + expect(fs.existsSync(path2)).toBeTruthy(); + await page.context().close(); + expect(fs.existsSync(path1)).toBeFalsy(); + expect(fs.existsSync(path2)).toBeFalsy(); + }); + + it('should delete downloads on browser gone', async ({ server, browserType, defaultBrowserOptions }) => { + const browser = await browserType.launch(defaultBrowserOptions); + const page = await browser.newPage({ acceptDownloads: true }); + await page.setContent(`download`); + const [ download1 ] = await Promise.all([ + page.waitForEvent('download'), + page.click('a') + ]); + const [ download2 ] = await Promise.all([ + page.waitForEvent('download'), + page.click('a') + ]); + const path1 = await download1.path(); + const path2 = await download2.path(); + expect(fs.existsSync(path1)).toBeTruthy(); + expect(fs.existsSync(path2)).toBeTruthy(); + await browser.close(); + expect(fs.existsSync(path1)).toBeFalsy(); + expect(fs.existsSync(path2)).toBeFalsy(); + expect(fs.existsSync(path.join(path1, '..'))).toBeFalsy(); + }); }); diff --git a/test/downloads-path.spec.ts b/test/downloads-path.spec.ts index 2d461bf720..17948d4910 100644 --- a/test/downloads-path.spec.ts +++ b/test/downloads-path.spec.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { fixtures as baseFixtures } from './fixtures'; +import { folio } from './fixtures'; import fs from 'fs'; import type { Browser, BrowserContext } from '..'; @@ -23,40 +23,40 @@ type TestState = { downloadsBrowser: Browser; persistentDownloadsContext: BrowserContext; }; -const fixtures = baseFixtures.defineTestFixtures({ - downloadsBrowser: async ({ server, browserType, defaultBrowserOptions, testInfo }, test) => { - server.setRoute('/download', (req, res) => { - res.setHeader('Content-Type', 'application/octet-stream'); - res.setHeader('Content-Disposition', 'attachment; filename=file.txt'); - res.end(`Hello world`); - }); - const browser = await browserType.launch({ - ...defaultBrowserOptions, - downloadsPath: testInfo.outputPath(''), - }); - await test(browser); - await browser.close(); - }, +const fixtures = folio.extend<{}, TestState>(); - persistentDownloadsContext: async ({ server, launchPersistent, testInfo }, test) => { - server.setRoute('/download', (req, res) => { - res.setHeader('Content-Type', 'application/octet-stream'); - res.setHeader('Content-Disposition', 'attachment; filename=file.txt'); - res.end(`Hello world`); - }); - const { context, page } = await launchPersistent( - { - downloadsPath: testInfo.outputPath(''), - acceptDownloads: true - } - ); - await page.setContent(`download`); - await test(context); - await context.close(); - }, +fixtures.downloadsBrowser.initTest(async ({ server, browserType, defaultBrowserOptions, testInfo }, test) => { + server.setRoute('/download', (req, res) => { + res.setHeader('Content-Type', 'application/octet-stream'); + res.setHeader('Content-Disposition', 'attachment; filename=file.txt'); + res.end(`Hello world`); + }); + const browser = await browserType.launch({ + ...defaultBrowserOptions, + downloadsPath: testInfo.outputPath(''), + }); + await test(browser); + await browser.close(); }); -const { it, expect } = fixtures; +fixtures.persistentDownloadsContext.initTest(async ({ server, launchPersistent, testInfo }, test) => { + server.setRoute('/download', (req, res) => { + res.setHeader('Content-Type', 'application/octet-stream'); + res.setHeader('Content-Disposition', 'attachment; filename=file.txt'); + res.end(`Hello world`); + }); + const { context, page } = await launchPersistent( + { + downloadsPath: testInfo.outputPath(''), + acceptDownloads: true + } + ); + await page.setContent(`download`); + await test(context); + await context.close(); +}); + +const { it, expect } = fixtures.build(); it('should keep downloadsPath folder', async ({downloadsBrowser, testInfo, server}) => { const page = await downloadsBrowser.newPage(); diff --git a/test/electron/electron-app.spec.ts b/test/electron/electron-app.spec.ts index 845cab4cda..f770116b9d 100644 --- a/test/electron/electron-app.spec.ts +++ b/test/electron/electron-app.spec.ts @@ -14,8 +14,8 @@ * limitations under the License. */ -import { electronFixtures } from './electron.fixture'; -const { it, expect, describe } = electronFixtures; +import { folio } from './electron.fixture'; +const { it, expect, describe } = folio; import path from 'path'; const electronName = process.platform === 'win32' ? 'electron.cmd' : 'electron'; diff --git a/test/electron/electron-window.spec.ts b/test/electron/electron-window.spec.ts index 8f8d26595c..d0737c7fb0 100644 --- a/test/electron/electron-window.spec.ts +++ b/test/electron/electron-window.spec.ts @@ -14,8 +14,8 @@ * limitations under the License. */ -import { electronFixtures } from './electron.fixture'; -const { it, expect, describe } = electronFixtures; +import { folio } from './electron.fixture'; +const { it, expect, describe } = folio; describe('electron window', (suite, { browserName }) => { suite.skip(browserName !== 'chromium'); diff --git a/test/electron/electron.fixture.ts b/test/electron/electron.fixture.ts index 4685e7a3aa..b555a401f7 100644 --- a/test/electron/electron.fixture.ts +++ b/test/electron/electron.fixture.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { fixtures as baseFixtures } from '../fixtures'; +import { folio as base } from '../fixtures'; import type { ElectronApplication, ElectronLauncher, ElectronPage } from '../../electron-types'; import path from 'path'; @@ -24,24 +24,25 @@ type TestState = { application: ElectronApplication; window: ElectronPage; }; +const fixtures = base.extend<{}, TestState>(); -export const electronFixtures = baseFixtures.defineTestFixtures({ - application: async ({ playwright }, test) => { - const electronPath = path.join(__dirname, '..', '..', 'node_modules', '.bin', electronName); - const application = await playwright.electron.launch(electronPath, { - args: [path.join(__dirname, 'testApp.js')], - }); - await test(application); - await application.close(); - }, - - window: async ({ application }, test) => { - const page = await application.newBrowserWindow({ width: 800, height: 600 }); - await test(page); - await page.close(); - }, +fixtures.application.initTest(async ({ playwright }, run) => { + const electronPath = path.join(__dirname, '..', '..', 'node_modules', '.bin', electronName); + const application = await playwright.electron.launch(electronPath, { + args: [path.join(__dirname, 'testApp.js')], + }); + await run(application); + await application.close(); }); +fixtures.window.initTest(async ({ application }, run) => { + const page = await application.newBrowserWindow({ width: 800, height: 600 }); + await run(page); + await page.close(); +}); + +export const folio = fixtures.build(); + declare module '../../index' { const electron: ElectronLauncher; } diff --git a/test/fixtures.spec.ts b/test/fixtures.spec.ts index b55333b86f..4985998ae1 100644 --- a/test/fixtures.spec.ts +++ b/test/fixtures.spec.ts @@ -15,35 +15,33 @@ * limitations under the License. */ -import { serverFixtures } from './remoteServer.fixture'; +import { folio, RemoteServer } from './remoteServer.fixture'; import { execSync } from 'child_process'; import path from 'path'; -import { RemoteServer } from './remoteServer.fixture'; -export type FixturesFixtures = { +type FixturesFixtures = { connectedRemoteServer: RemoteServer; stallingConnectedRemoteServer: RemoteServer; }; +const fixtures = folio.extend<{}, FixturesFixtures>(); -const fixturesFixtures = serverFixtures.defineTestFixtures({ - connectedRemoteServer: async ({browserType, remoteServer, server}, test) => { - const browser = await browserType.connect({ wsEndpoint: remoteServer.wsEndpoint() }); - const page = await browser.newPage(); - await page.goto(server.EMPTY_PAGE); - await test(remoteServer); - await browser.close(); - }, - - stallingConnectedRemoteServer: async ({browserType, stallingRemoteServer, server}, test) => { - const browser = await browserType.connect({ wsEndpoint: stallingRemoteServer.wsEndpoint() }); - const page = await browser.newPage(); - await page.goto(server.EMPTY_PAGE); - await test(stallingRemoteServer); - await browser.close(); - }, +fixtures.connectedRemoteServer.initTest(async ({browserType, remoteServer, server}, run) => { + const browser = await browserType.connect({ wsEndpoint: remoteServer.wsEndpoint() }); + const page = await browser.newPage(); + await page.goto(server.EMPTY_PAGE); + await run(remoteServer); + await browser.close(); }); -const { it, describe, expect } = fixturesFixtures; +fixtures.stallingConnectedRemoteServer.initTest(async ({browserType, stallingRemoteServer, server}, run) => { + const browser = await browserType.connect({ wsEndpoint: stallingRemoteServer.wsEndpoint() }); + const page = await browser.newPage(); + await page.goto(server.EMPTY_PAGE); + await run(stallingRemoteServer); + await browser.close(); +}); + +const { it, describe, expect } = fixtures.build(); it('should close the browser when the node process closes', test => { test.slow(); diff --git a/test/fixtures.ts b/test/fixtures.ts index e4109bc0f0..407b3fd882 100644 --- a/test/fixtures.ts +++ b/test/fixtures.ts @@ -24,10 +24,9 @@ import type { Browser, BrowserContext, BrowserType, Page } from '../index'; import { Connection } from '../lib/client/connection'; import { Transport } from '../lib/protocol/transport'; import { installCoverageHooks } from './coverage'; -import { fixtures as httpFixtures } from './http.fixtures'; -import { fixtures as implFixtures } from './impl.fixtures'; -import { fixtures as playwrightFixtures } from './playwright.fixtures'; -export { expect, config } from '@playwright/test-runner'; +import { folio as httpFolio } from './http.fixtures'; +import { folio as playwrightFolio } from './playwright.fixtures'; +export { expect, config } from 'folio'; const removeFolderAsync = util.promisify(require('rimraf')); const mkdtempAsync = util.promisify(fs.mkdtemp); @@ -41,118 +40,126 @@ const getExecutablePath = browserName => { return process.env.WKPATH; }; -type AllTestFixtures = { +type WireParameters = { + wire: boolean; +}; +type WorkerFixtures = { + toImpl: (rpcObject: any) => any; +}; +type TestFixtures = { createUserDataDir: () => Promise; launchPersistent: (options?: Parameters['launchPersistentContext']>[1]) => Promise<{ context: BrowserContext, page: Page }>; }; -export const fixtures = playwrightFixtures - .union(httpFixtures) - .union(implFixtures) - .defineParameter('wire', 'Wire testing mode', !!process.env.PWWIRE || false) - .defineTestFixtures({ - createUserDataDir: async ({ }, runTest) => { - const dirs: string[] = []; - async function createUserDataDir() { - // We do not put user data dir in testOutputPath, - // because we do not want to upload them as test result artifacts. - // - // Additionally, it is impossible to upload user data dir after test run: - // - Firefox removes lock file later, presumably from another watchdog process? - // - WebKit has circular symlinks that makes CI go crazy. - const dir = await mkdtempAsync(path.join(os.tmpdir(), 'playwright-test-')); - dirs.push(dir); - return dir; - } - await runTest(createUserDataDir); - await Promise.all(dirs.map(dir => removeFolderAsync(dir).catch(e => { }))); - }, +const fixtures = playwrightFolio.union(httpFolio).extend(); - launchPersistent: async ({ createUserDataDir, defaultBrowserOptions, browserType }, test) => { - let context; - async function launchPersistent(options) { - if (context) - throw new Error('can only launch one persitent context'); - const userDataDir = await createUserDataDir(); - context = await browserType.launchPersistentContext(userDataDir, { ...defaultBrowserOptions, ...options }); - const page = context.pages()[0]; - return { context, page }; - } - await test(launchPersistent); - if (context) - await context.close(); - }, - }) - .overrideWorkerFixtures({ - defaultBrowserOptions: async ({ browserName, headful, slowMo }, runTest) => { - const executablePath = getExecutablePath(browserName); - if (executablePath) - console.error(`Using executable at ${executablePath}`); - await runTest({ - executablePath, - handleSIGINT: false, - slowMo, - headless: !headful, - }); - }, +fixtures.wire.initParameter('Wire testing mode', !!process.env.PWWIRE); - playwright: async ({ browserName, testWorkerIndex, platform, wire }, runTest) => { - assert(platform); // Depend on platform to generate all tests. - const { coverage, uninstall } = installCoverageHooks(browserName); - if (wire) { - require('../lib/utils/utils').setUnderTest(); - const connection = new Connection(); - const spawnedProcess = childProcess.fork(path.join(__dirname, '..', 'lib', 'driver.js'), ['serve'], { - stdio: 'pipe', - detached: true, - }); - spawnedProcess.unref(); - const onExit = (exitCode, signal) => { - throw new Error(`Server closed with exitCode=${exitCode} signal=${signal}`); - }; - spawnedProcess.on('exit', onExit); - const transport = new Transport(spawnedProcess.stdin, spawnedProcess.stdout); - connection.onmessage = message => transport.send(JSON.stringify(message)); - transport.onmessage = message => connection.dispatch(JSON.parse(message)); - const playwrightObject = await connection.waitForObjectWithKnownName('Playwright'); - await runTest(playwrightObject); - spawnedProcess.removeListener('exit', onExit); - spawnedProcess.stdin.destroy(); - spawnedProcess.stdout.destroy(); - spawnedProcess.stderr.destroy(); - await teardownCoverage(); - } else { - const playwright = require('../index'); - await runTest(playwright); - await teardownCoverage(); - } +fixtures.createUserDataDir.initTest(async ({ }, run) => { + const dirs: string[] = []; + async function createUserDataDir() { + // We do not put user data dir in testOutputPath, + // because we do not want to upload them as test result artifacts. + // + // Additionally, it is impossible to upload user data dir after test run: + // - Firefox removes lock file later, presumably from another watchdog process? + // - WebKit has circular symlinks that makes CI go crazy. + const dir = await mkdtempAsync(path.join(os.tmpdir(), 'playwright-test-')); + dirs.push(dir); + return dir; + } + await run(createUserDataDir); + await Promise.all(dirs.map(dir => removeFolderAsync(dir).catch(e => { }))); +}); - async function teardownCoverage() { - uninstall(); - const coveragePath = path.join(__dirname, 'coverage-report', testWorkerIndex + '.json'); - const coverageJSON = [...coverage.keys()].filter(key => coverage.get(key)); - await fs.promises.mkdir(path.dirname(coveragePath), { recursive: true }); - await fs.promises.writeFile(coveragePath, JSON.stringify(coverageJSON, undefined, 2), 'utf8'); - } - }, - }) - .overrideTestFixtures({ - testParametersPathSegment: async ({ browserName }, runTest) => { - await runTest(browserName); - } +fixtures.launchPersistent.initTest(async ({ createUserDataDir, defaultBrowserOptions, browserType }, run) => { + let context; + async function launchPersistent(options) { + if (context) + throw new Error('can only launch one persitent context'); + const userDataDir = await createUserDataDir(); + context = await browserType.launchPersistentContext(userDataDir, { ...defaultBrowserOptions, ...options }); + const page = context.pages()[0]; + return { context, page }; + } + await run(launchPersistent); + if (context) + await context.close(); +}); + +fixtures.defaultBrowserOptions.overrideWorker(async ({ browserName, headful, slowMo }, run) => { + const executablePath = getExecutablePath(browserName); + if (executablePath) + console.error(`Using executable at ${executablePath}`); + await run({ + executablePath, + handleSIGINT: false, + slowMo, + headless: !headful, + }); +}); + +fixtures.playwright.overrideWorker(async ({ browserName, testWorkerIndex, platform, wire }, run) => { + assert(platform); // Depend on platform to generate all tests. + const { coverage, uninstall } = installCoverageHooks(browserName); + if (wire) { + require('../lib/utils/utils').setUnderTest(); + const connection = new Connection(); + const spawnedProcess = childProcess.fork(path.join(__dirname, '..', 'lib', 'driver.js'), ['serve'], { + stdio: 'pipe', + detached: true, }); + spawnedProcess.unref(); + const onExit = (exitCode, signal) => { + throw new Error(`Server closed with exitCode=${exitCode} signal=${signal}`); + }; + spawnedProcess.on('exit', onExit); + const transport = new Transport(spawnedProcess.stdin, spawnedProcess.stdout); + connection.onmessage = message => transport.send(JSON.stringify(message)); + transport.onmessage = message => connection.dispatch(JSON.parse(message)); + const playwrightObject = await connection.waitForObjectWithKnownName('Playwright'); + await run(playwrightObject); + spawnedProcess.removeListener('exit', onExit); + spawnedProcess.stdin.destroy(); + spawnedProcess.stdout.destroy(); + spawnedProcess.stderr.destroy(); + await teardownCoverage(); + } else { + const playwright = require('../index'); + await run(playwright); + await teardownCoverage(); + } -fixtures.generateParametrizedTests( + async function teardownCoverage() { + uninstall(); + const coveragePath = path.join(__dirname, 'coverage-report', testWorkerIndex + '.json'); + const coverageJSON = [...coverage.keys()].filter(key => coverage.get(key)); + await fs.promises.mkdir(path.dirname(coveragePath), { recursive: true }); + await fs.promises.writeFile(coveragePath, JSON.stringify(coverageJSON, undefined, 2), 'utf8'); + } +}); + +fixtures.toImpl.initWorker(async ({ playwright }, run) => { + await run((playwright as any)._toImpl); +}); + +fixtures.testParametersPathSegment.overrideTest(async ({ browserName }, run) => { + await run(browserName); +}); + +export const folio = fixtures.build(); + +folio.generateParametrizedTests( 'platform', process.env.PWTESTREPORT ? ['win32', 'darwin', 'linux'] : [process.platform as ('win32' | 'linux' | 'darwin')]); -export const it = fixtures.it; -export const fit = fixtures.fit; -export const xit = fixtures.xit; -export const describe = fixtures.describe; -export const fdescribe = fixtures.fdescribe; -export const xdescribe = fixtures.xdescribe; -export const beforeEach = fixtures.beforeEach; -export const afterEach = fixtures.afterEach; -export const beforeAll = fixtures.beforeAll; -export const afterAll = fixtures.afterAll; +export const it = folio.it; +export const fit = folio.fit; +export const xit = folio.xit; +export const describe = folio.describe; +export const fdescribe = folio.fdescribe; +export const xdescribe = folio.xdescribe; +export const beforeEach = folio.beforeEach; +export const afterEach = folio.afterEach; +export const beforeAll = folio.beforeAll; +export const afterAll = folio.afterAll; diff --git a/test/http.fixtures.ts b/test/http.fixtures.ts index bebddc8a39..cc5f38b6a8 100644 --- a/test/http.fixtures.ts +++ b/test/http.fixtures.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { fixtures as baseFixtures } from '@playwright/test-runner'; +import { folio as base } from 'folio'; import path from 'path'; import { TestServer } from '../utils/testserver'; @@ -28,40 +28,39 @@ type HttpTestFixtures = { httpsServer: TestServer; }; -export const fixtures = baseFixtures - .defineWorkerFixtures({ - httpService: async ({ testWorkerIndex }, test) => { - const assetsPath = path.join(__dirname, 'assets'); - const cachedPath = path.join(__dirname, 'assets', 'cached'); +const fixtures = base.extend(); +fixtures.httpService.initWorker(async ({ testWorkerIndex }, test) => { + const assetsPath = path.join(__dirname, 'assets'); + const cachedPath = path.join(__dirname, 'assets', 'cached'); - const port = 8907 + testWorkerIndex * 2; - const server = await TestServer.create(assetsPath, port); - server.enableHTTPCache(cachedPath); + const port = 8907 + testWorkerIndex * 2; + const server = await TestServer.create(assetsPath, port); + server.enableHTTPCache(cachedPath); - const httpsPort = port + 1; - const httpsServer = await TestServer.createHTTPS(assetsPath, httpsPort); - httpsServer.enableHTTPCache(cachedPath); + const httpsPort = port + 1; + const httpsServer = await TestServer.createHTTPS(assetsPath, httpsPort); + httpsServer.enableHTTPCache(cachedPath); - await test({ server, httpsServer }); + await test({ server, httpsServer }); - await Promise.all([ - server.stop(), - httpsServer.stop(), - ]); - }, + await Promise.all([ + server.stop(), + httpsServer.stop(), + ]); +}); - asset: async ({ }, test) => { - await test(p => path.join(__dirname, `assets`, p)); - }, - }) - .defineTestFixtures({ - server: async ({ httpService }, test) => { - httpService.server.reset(); - await test(httpService.server); - }, +fixtures.asset.initWorker(async ({ }, test) => { + await test(p => path.join(__dirname, `assets`, p)); +}); - httpsServer: async ({ httpService }, test) => { - httpService.httpsServer.reset(); - await test(httpService.httpsServer); - }, - }); +fixtures.server.initTest(async ({ httpService }, test) => { + httpService.server.reset(); + await test(httpService.server); +}); + +fixtures.httpsServer.initTest(async ({ httpService }, test) => { + httpService.httpsServer.reset(); + await test(httpService.httpsServer); +}); + +export const folio = fixtures.build(); diff --git a/test/impl.fixtures.ts b/test/impl.fixtures.ts deleted file mode 100644 index 293570a5d5..0000000000 --- a/test/impl.fixtures.ts +++ /dev/null @@ -1,28 +0,0 @@ -/** - * Copyright Microsoft Corporation. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { fixtures as playwrightFixtures } from './playwright.fixtures'; - -type ImplWorkerFixtures = { - toImpl: (rpcObject: any) => any; -}; - -export const fixtures = playwrightFixtures - .defineWorkerFixtures({ - toImpl: async ({ playwright }, test) => { - await test((playwright as any)._toImpl); - } - }); diff --git a/test/playwright.fixtures.ts b/test/playwright.fixtures.ts index 662a3249d0..d422e95688 100644 --- a/test/playwright.fixtures.ts +++ b/test/playwright.fixtures.ts @@ -14,10 +14,29 @@ * limitations under the License. */ -import { config, fixtures as baseFixtures } from '@playwright/test-runner'; +import { config, folio as base } from 'folio'; import type { Browser, BrowserContext, BrowserContextOptions, BrowserType, LaunchOptions, Page } from '../index'; import * as path from 'path'; +// Parameters ------------------------------------------------------------------ +// ... these can be used to run tests in different modes. + +type PlaywrightParameters = { + // Browser type name. + browserName: 'chromium' | 'firefox' | 'webkit'; + // Whether to run tests headless or headful. + headful: boolean; + // Operating system. + platform: 'win32' | 'linux' | 'darwin'; + // Generate screenshot on failure. + screenshotOnFailure: boolean; + // Slows down Playwright operations by the specified amount of milliseconds. + slowMo: number; + // Whether to record the execution trace. + trace: boolean; +}; + + // Worker fixture declarations ------------------------------------------------- // ... these live as long as the worker process. @@ -58,115 +77,114 @@ type PlaywrightTestFixtures = { page: Page; }; -export const fixtures = baseFixtures - .defineParameter('browserName', 'Browser type name', (process.env.BROWSER || 'chromium') as 'chromium' | 'firefox' | 'webkit') - .defineParameter('headful', 'Whether to run tests headless or headful', process.env.HEADFUL ? true : false) - .defineParameter('platform', 'Operating system', process.platform as ('win32' | 'linux' | 'darwin')) - .defineParameter('screenshotOnFailure', 'Generate screenshot on failure', false) - .defineParameter('slowMo', 'Slows down Playwright operations by the specified amount of milliseconds', 0) - .defineParameter('trace', 'Whether to record the execution trace', !!process.env.TRACING || false) - .defineWorkerFixtures({ - defaultBrowserOptions: async ({ headful, slowMo }, runTest) => { - await runTest({ - handleSIGINT: false, - slowMo, - headless: !headful, - }); - }, +const fixtures = base.extend(); +fixtures.browserName.initParameter('Browser type name', (process.env.BROWSER || 'chromium') as 'chromium' | 'firefox' | 'webkit'); +fixtures.headful.initParameter('Whether to run tests headless or headful', process.env.HEADFUL ? true : false); +fixtures.platform.initParameter('Operating system', process.platform as ('win32' | 'linux' | 'darwin')); +fixtures.screenshotOnFailure.initParameter('Generate screenshot on failure', false); +fixtures.slowMo.initParameter('Slows down Playwright operations by the specified amount of milliseconds', 0); +fixtures.trace.initParameter('Whether to record the execution trace', !!process.env.TRACING); - playwright: async ({ }, runTest) => { - const playwright = require('../index'); - await runTest(playwright); - }, +fixtures.defaultBrowserOptions.initWorker(async ({ headful, slowMo }, run) => { + await run({ + handleSIGINT: false, + slowMo, + headless: !headful, + }); +}); - browserType: async ({ playwright, browserName }, runTest) => { - const browserType = (playwright as any)[browserName]; - await runTest(browserType); - }, +fixtures.playwright.initWorker(async ({ }, run) => { + const playwright = require('../index'); + await run(playwright); +}); - browser: async ({ browserType, defaultBrowserOptions }, runTest) => { - const browser = await browserType.launch(defaultBrowserOptions); - await runTest(browser); - await browser.close(); - }, +fixtures.browserType.initWorker(async ({ playwright, browserName }, run) => { + const browserType = (playwright as any)[browserName]; + await run(browserType); +}); - isChromium: async ({ browserName }, runTest) => { - await runTest(browserName === 'chromium'); - }, +fixtures.browser.initWorker(async ({ browserType, defaultBrowserOptions }, run) => { + const browser = await browserType.launch(defaultBrowserOptions); + await run(browser); + await browser.close(); +}); - isFirefox: async ({ browserName }, runTest) => { - await runTest(browserName === 'firefox'); - }, +fixtures.isChromium.initWorker(async ({ browserName }, run) => { + await run(browserName === 'chromium'); +}); - isWebKit: async ({ browserName }, runTest) => { - await runTest(browserName === 'webkit'); - }, +fixtures.isFirefox.initWorker(async ({ browserName }, run) => { + await run(browserName === 'firefox'); +}); - isWindows: async ({ platform }, test) => { - await test(platform === 'win32'); - }, +fixtures.isWebKit.initWorker(async ({ browserName }, run) => { + await run(browserName === 'webkit'); +}); - isMac: async ({ platform }, test) => { - await test(platform === 'darwin'); - }, +fixtures.isWindows.initWorker(async ({ platform }, run) => { + await run(platform === 'win32'); +}); - isLinux: async ({ platform }, test) => { - await test(platform === 'linux'); - }, - }) - .defineTestFixtures({ - defaultContextOptions: async ({ trace, testInfo }, runTest) => { - if (trace || testInfo.retry) { - await runTest({ - _traceResourcesPath: path.join(config.outputDir, 'trace-resources'), - _tracePath: testInfo.outputPath('playwright.trace'), - videosPath: testInfo.outputPath(''), - } as any); - } else { - await runTest({}); - } - }, +fixtures.isMac.initWorker(async ({ platform }, run) => { + await run(platform === 'darwin'); +}); - contextFactory: async ({ browser, defaultContextOptions, testInfo, screenshotOnFailure }, runTest) => { - const contexts: BrowserContext[] = []; - async function contextFactory(options: BrowserContextOptions = {}) { - const context = await browser.newContext({ ...defaultContextOptions, ...options }); - contexts.push(context); - return context; - } - await runTest(contextFactory); +fixtures.isLinux.initWorker(async ({ platform }, run) => { + await run(platform === 'linux'); +}); - 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` }); - } - } - for (const context of contexts) - await context.close(); - }, +fixtures.defaultContextOptions.initTest(async ({ trace, testInfo }, run) => { + if (trace || testInfo.retry) { + await run({ + _traceResourcesPath: path.join(config.outputDir, 'trace-resources'), + _tracePath: testInfo.outputPath('playwright.trace'), + videosPath: testInfo.outputPath(''), + } as any); + } else { + await run({}); + } +}); - context: async ({ contextFactory }, runTest) => { - const context = await contextFactory(); - await runTest(context); - // Context factory is taking care of closing the context, - // so that it could capture a screenshot on failure. - }, +fixtures.contextFactory.initTest(async ({ browser, defaultContextOptions, testInfo, screenshotOnFailure }, run) => { + const contexts: BrowserContext[] = []; + async function contextFactory(options: BrowserContextOptions = {}) { + const context = await browser.newContext({ ...defaultContextOptions, ...options }); + contexts.push(context); + return context; + } + await run(contextFactory); - page: async ({ context }, runTest) => { - // Always create page off context so that they matched. - await runTest(await context.newPage()); - // Context fixture is taking care of closing the page. - }, - }) - .overrideTestFixtures({ - testParametersPathSegment: async ({ browserName, platform }, runTest) => { - await runTest(browserName + '-' + platform); - } - }); + 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` }); + } + } + for (const context of contexts) + await context.close(); +}); + +fixtures.context.initTest(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.initTest(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.overrideTest(async ({ browserName, platform }, run) => { + await run(browserName + '-' + platform); +}); + +export const folio = fixtures.build(); // If browser is not specified, we are running tests against all three browsers. -fixtures.generateParametrizedTests( +folio.generateParametrizedTests( 'browserName', process.env.BROWSER ? [process.env.BROWSER] as any : ['chromium', 'webkit', 'firefox']); diff --git a/test/remoteServer.fixture.ts b/test/remoteServer.fixture.ts index 3d0bb83dd4..b5b4c571bd 100644 --- a/test/remoteServer.fixture.ts +++ b/test/remoteServer.fixture.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { fixtures as baseFixtures } from './fixtures'; +import { folio as base } from './fixtures'; import path from 'path'; import { spawn } from 'child_process'; @@ -24,23 +24,24 @@ type ServerFixtures = { remoteServer: RemoteServer; stallingRemoteServer: RemoteServer; }; +const fixtures = base.extend<{}, ServerFixtures>(); -export const serverFixtures = baseFixtures.defineTestFixtures({ - remoteServer: async ({ browserType, defaultBrowserOptions }, test) => { - const remoteServer = new RemoteServer(); - await remoteServer._start(browserType, defaultBrowserOptions); - await test(remoteServer); - await remoteServer.close(); - }, - - stallingRemoteServer: async ({ browserType, defaultBrowserOptions }, test) => { - const remoteServer = new RemoteServer(); - await remoteServer._start(browserType, defaultBrowserOptions, { stallOnClose: true }); - await test(remoteServer); - await remoteServer.close(); - }, +fixtures.remoteServer.initTest(async ({ browserType, defaultBrowserOptions }, run) => { + const remoteServer = new RemoteServer(); + await remoteServer._start(browserType, defaultBrowserOptions); + await run(remoteServer); + await remoteServer.close(); }); +fixtures.stallingRemoteServer.initTest(async ({ browserType, defaultBrowserOptions }, run) => { + const remoteServer = new RemoteServer(); + await remoteServer._start(browserType, defaultBrowserOptions, { stallOnClose: true }); + await run(remoteServer); + await remoteServer.close(); +}); + +export const folio = fixtures.build(); + const playwrightPath = path.join(__dirname, '..'); export class RemoteServer { diff --git a/test/screencast.spec.ts b/test/screencast.spec.ts index cec6f1e1c6..d1a0da0aaf 100644 --- a/test/screencast.spec.ts +++ b/test/screencast.spec.ts @@ -14,14 +14,12 @@ * limitations under the License. */ -import { fixtures } from './fixtures'; +import { it, expect, describe } from './fixtures'; import fs from 'fs'; import path from 'path'; import { spawnSync } from 'child_process'; import { PNG } from 'pngjs'; -const { it, expect, describe } = fixtures; - let ffmpegName = ''; if (process.platform === 'win32') ffmpegName = process.arch === 'ia32' ? 'ffmpeg-win32' : 'ffmpeg-win64'; diff --git a/utils/doclint/check_public_api/test/test.js b/utils/doclint/check_public_api/test/test.js index f081289c5b..8edf7ba376 100644 --- a/utils/doclint/check_public_api/test/test.js +++ b/utils/doclint/check_public_api/test/test.js @@ -21,17 +21,16 @@ const checkPublicAPI = require('..'); const Source = require('../../Source'); const mdBuilder = require('../MDBuilder'); const jsBuilder = require('../JSBuilder'); -const { fixtures } = require('@playwright/test-runner'); -const { defineWorkerFixtures, describe, it, expect } = fixtures; +const { folio } = require('folio'); -defineWorkerFixtures({ - page: async({}, test) => { - const browser = await playwright.chromium.launch(); - const page = await browser.newPage(); - await test(page); - await browser.close(); - } +const fixtures = folio.extend(); +fixtures.setWorkerFixture('page', async({}, test) => { + const browser = await playwright.chromium.launch(); + const page = await browser.newPage(); + await test(page); + await browser.close(); }); +const { describe, it, expect } = fixtures.build(); describe('checkPublicAPI', function() { testLint('diff-classes'); diff --git a/utils/doclint/preprocessor/test.js b/utils/doclint/preprocessor/test.js index 95bc1a9ce8..a4a8eac55d 100644 --- a/utils/doclint/preprocessor/test.js +++ b/utils/doclint/preprocessor/test.js @@ -16,8 +16,8 @@ const {runCommands} = require('.'); const Source = require('../Source'); -const { fixtures } = require('@playwright/test-runner'); -const { describe, it, expect } = fixtures; +const { folio } = require('folio'); +const { describe, it, expect } = folio; describe('runCommands', function() { const OPTIONS_REL = {