mirror of
https://github.com/microsoft/playwright.git
synced 2024-09-20 00:41:16 +03:00
chore: roll folio to 0.3.6 (#4110)
This commit is contained in:
parent
80ed407033
commit
46b14bc740
24
.github/workflows/tests.yml
vendored
24
.github/workflows/tests.yml
vendored
@ -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:
|
||||
|
78
package-lock.json
generated
78
package-lock.json
generated
@ -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",
|
||||
|
12
package.json
12
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",
|
||||
|
@ -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);
|
||||
|
@ -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<DomainFixtures>({
|
||||
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();
|
||||
|
@ -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');
|
||||
|
@ -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<TestState>({
|
||||
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');
|
||||
|
@ -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(`<a href="${server.PREFIX}/downloadWithFilename">download</a>`);
|
||||
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(`<a href="${server.PREFIX}/download">download</a>`);
|
||||
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(`<a href="${server.PREFIX}/download">download</a>`);
|
||||
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(`<a href="${server.PREFIX}/download">download</a>`);
|
||||
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(`<a href="${server.PREFIX}/download">download</a>`);
|
||||
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(`<a href="${server.PREFIX}/download">download</a>`);
|
||||
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(`<a href="${server.PREFIX}/download">download</a>`);
|
||||
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(`<a href="${server.PREFIX}/download">download</a>`);
|
||||
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(`<a href="${server.PREFIX}/download">download</a>`);
|
||||
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(`<a href="${server.PREFIX}/download">download</a>`);
|
||||
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(`<a href="${server.PREFIX}/download">download</a>`);
|
||||
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(`<a download="file.txt" href="${server.PREFIX}/download">download</a>`);
|
||||
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<string>(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(`<a href="${server.PREFIX}/download">download</a>`);
|
||||
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<string>(res => {
|
||||
page.on('download', dl => {
|
||||
dl.path().then(res);
|
||||
|
||||
it('should report downloads with acceptDownloads: false', async ({page, server}) => {
|
||||
await page.setContent(`<a href="${server.PREFIX}/downloadWithFilename">download</a>`);
|
||||
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(`<a href="${server.PREFIX}/download">download</a>`);
|
||||
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(`<a href="${server.PREFIX}/download">download</a>`);
|
||||
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(`<a href="${server.PREFIX}/download">download</a>`);
|
||||
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(`<a href="${server.PREFIX}/download">download</a>`);
|
||||
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(`<a href="${server.PREFIX}/download">download</a>`);
|
||||
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(`<a href="${server.PREFIX}/download">download</a>`);
|
||||
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(`<a href="${server.PREFIX}/download">download</a>`);
|
||||
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(`<a href="${server.PREFIX}/download">download</a>`);
|
||||
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(`<a href="${server.PREFIX}/download">download</a>`);
|
||||
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(`<a href="${server.PREFIX}/download">download</a>`);
|
||||
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(`<a download="file.txt" href="${server.PREFIX}/download">download</a>`);
|
||||
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(`<a href="${server.PREFIX}/download">download</a>`);
|
||||
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<string>(res => {
|
||||
page.on('download', dl => {
|
||||
dl.path().then(res);
|
||||
});
|
||||
});
|
||||
await page.setContent(`<a href="${server.PREFIX}/download">download</a>`);
|
||||
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<string>(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(`<a target=_blank href="${server.PREFIX}/download">download</a>`);
|
||||
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(`<a href="${server.PREFIX}/download">download</a>`);
|
||||
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(`<a href="${server.PREFIX}/download">download</a>`);
|
||||
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(`<a target=_blank href="${server.PREFIX}/download">download</a>`);
|
||||
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(`<a href="${server.PREFIX}/download">download</a>`);
|
||||
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(`<a href="${server.PREFIX}/download">download</a>`);
|
||||
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(`<a href="${server.PREFIX}/download">download</a>`);
|
||||
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(`<a href="${server.PREFIX}/download">download</a>`);
|
||||
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(`<a href="${server.PREFIX}/download">download</a>`);
|
||||
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(`<a href="${server.PREFIX}/download">download</a>`);
|
||||
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(`<a href="${server.PREFIX}/download">download</a>`);
|
||||
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();
|
||||
});
|
||||
});
|
||||
|
@ -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<TestState>({
|
||||
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(`<a href="${server.PREFIX}/download">download</a>`);
|
||||
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(`<a href="${server.PREFIX}/download">download</a>`);
|
||||
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();
|
||||
|
@ -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';
|
||||
|
@ -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');
|
||||
|
@ -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<TestState>({
|
||||
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;
|
||||
}
|
||||
|
@ -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<FixturesFixtures>({
|
||||
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();
|
||||
|
221
test/fixtures.ts
221
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<string>;
|
||||
launchPersistent: (options?: Parameters<BrowserType<Browser>['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<AllTestFixtures>({
|
||||
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<WorkerFixtures, TestFixtures, WireParameters>();
|
||||
|
||||
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;
|
||||
|
@ -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<HttpWorkerFixtures>({
|
||||
httpService: async ({ testWorkerIndex }, test) => {
|
||||
const assetsPath = path.join(__dirname, 'assets');
|
||||
const cachedPath = path.join(__dirname, 'assets', 'cached');
|
||||
const fixtures = base.extend<HttpWorkerFixtures, HttpTestFixtures>();
|
||||
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<HttpTestFixtures>({
|
||||
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();
|
||||
|
@ -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<ImplWorkerFixtures>({
|
||||
toImpl: async ({ playwright }, test) => {
|
||||
await test((playwright as any)._toImpl);
|
||||
}
|
||||
});
|
@ -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<PlaywrightWorkerFixtures>({
|
||||
defaultBrowserOptions: async ({ headful, slowMo }, runTest) => {
|
||||
await runTest({
|
||||
handleSIGINT: false,
|
||||
slowMo,
|
||||
headless: !headful,
|
||||
});
|
||||
},
|
||||
const fixtures = base.extend<PlaywrightWorkerFixtures, PlaywrightTestFixtures, PlaywrightParameters>();
|
||||
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<PlaywrightTestFixtures>({
|
||||
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']);
|
||||
|
@ -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<ServerFixtures>({
|
||||
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 {
|
||||
|
@ -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';
|
||||
|
@ -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');
|
||||
|
@ -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 = {
|
||||
|
Loading…
Reference in New Issue
Block a user