mirror of
https://github.com/microsoft/playwright.git
synced 2025-01-03 17:09:01 +03:00
test: migrate last tests to new folio (#6071)
This commit is contained in:
parent
f21f47889e
commit
4f7e7450e2
92
.github/workflows/tests.yml
vendored
92
.github/workflows/tests.yml
vendored
@ -14,6 +14,7 @@ env:
|
||||
# Force terminal colors. @see https://www.npmjs.com/package/colors
|
||||
FORCE_COLOR: 1
|
||||
FLAKINESS_CONNECTION_STRING: ${{ secrets.FLAKINESS_CONNECTION_STRING }}
|
||||
FOLIO_JSON_OUTPUT_NAME: "test-results/report.json"
|
||||
|
||||
jobs:
|
||||
test_linux:
|
||||
@ -38,17 +39,8 @@ 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 folio test/ --workers=1 --forbid-only --global-timeout=5400000 --retries=3 --reporter=dot,json"
|
||||
env:
|
||||
BROWSER: ${{ matrix.browser }}
|
||||
FOLIO_JSON_OUTPUT_NAME: "test-results/report-old.json"
|
||||
- run: xvfb-run --auto-servernum --server-args="-screen 0 1280x960x24" -- bash -c "ulimit -c unlimited && npm run folio -- ${{ matrix.browser }} --reporter=dot,json"
|
||||
env:
|
||||
FOLIO_JSON_OUTPUT_NAME: "test-results/report.json"
|
||||
# Checking coverage across two test suites is hard. Temporary disabled.
|
||||
# - run: node test/checkCoverage.js
|
||||
# env:
|
||||
# BROWSER: ${{ matrix.browser }}
|
||||
- run: xvfb-run --auto-servernum --server-args="-screen 0 1280x960x24" -- bash -c "ulimit -c unlimited && npm run test -- ${{ matrix.browser }} --reporter=dot,json"
|
||||
- run: node test/checkCoverage.js ${{ matrix.browser }}
|
||||
- run: ./utils/upload_flakiness_dashboard.sh ./test-results/report.json
|
||||
if: always() && github.repository == 'microsoft/playwright' && (github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/heads/release-'))
|
||||
- uses: actions/upload-artifact@v1
|
||||
@ -73,13 +65,7 @@ jobs:
|
||||
- run: npm ci
|
||||
- run: npm run build
|
||||
- run: node lib/cli/cli install-deps ${{ matrix.browser }} chromium
|
||||
- run: npx folio test/ --workers=1 --forbid-only --global-timeout=5400000 --retries=3 --reporter=dot,json
|
||||
env:
|
||||
BROWSER: ${{ matrix.browser }}
|
||||
FOLIO_JSON_OUTPUT_NAME: "test-results/report-old.json"
|
||||
- run: npm run folio -- ${{ matrix.browser }} --reporter=dot,json
|
||||
env:
|
||||
FOLIO_JSON_OUTPUT_NAME: "test-results/report.json"
|
||||
- run: npm run test -- ${{ matrix.browser }} --reporter=dot,json
|
||||
- run: ./utils/upload_flakiness_dashboard.sh ./test-results/report.json
|
||||
if: always() && github.repository == 'microsoft/playwright' && (github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/heads/release-'))
|
||||
- uses: actions/upload-artifact@v1
|
||||
@ -106,15 +92,8 @@ jobs:
|
||||
- run: npm ci
|
||||
- run: npm run build
|
||||
- run: node lib/cli/cli install-deps
|
||||
- run: npx folio test/ --workers=1 --forbid-only --global-timeout=5400000 --retries=3 --reporter=dot,json
|
||||
- run: npm run test -- ${{ matrix.browser }} --reporter=dot,json
|
||||
shell: bash
|
||||
env:
|
||||
BROWSER: ${{ matrix.browser }}
|
||||
FOLIO_JSON_OUTPUT_NAME: "test-results/report-old.json"
|
||||
- run: npm run folio -- ${{ matrix.browser }} --reporter=dot,json
|
||||
shell: bash
|
||||
env:
|
||||
FOLIO_JSON_OUTPUT_NAME: "test-results/report.json"
|
||||
- run: ./utils/upload_flakiness_dashboard.sh ./test-results/report.json
|
||||
if: always() && github.repository == 'microsoft/playwright' && (github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/heads/release-'))
|
||||
shell: bash
|
||||
@ -164,17 +143,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 folio test/ --workers=1 --forbid-only --global-timeout=5400000 --retries=3 --reporter=dot,json"
|
||||
if: ${{ always() }}
|
||||
env:
|
||||
BROWSER: ${{ matrix.browser }}
|
||||
HEADFUL: 1
|
||||
FOLIO_JSON_OUTPUT_NAME: "test-results/report-old.json"
|
||||
- run: xvfb-run --auto-servernum --server-args="-screen 0 1280x960x24" -- bash -c "ulimit -c unlimited && npm run folio -- ${{ matrix.browser }} --reporter=dot,json"
|
||||
- run: xvfb-run --auto-servernum --server-args="-screen 0 1280x960x24" -- bash -c "ulimit -c unlimited && npm run test -- ${{ matrix.browser }} --reporter=dot,json"
|
||||
if: ${{ always() }}
|
||||
env:
|
||||
HEADFUL: 1
|
||||
FOLIO_JSON_OUTPUT_NAME: "test-results/report.json"
|
||||
- run: ./utils/upload_flakiness_dashboard.sh ./test-results/report.json
|
||||
if: always() && github.repository == 'microsoft/playwright' && (github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/heads/release-'))
|
||||
- uses: actions/upload-artifact@v1
|
||||
@ -204,15 +176,9 @@ 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 folio test/ --workers=1 --forbid-only --global-timeout=5400000 --retries=3 --reporter=dot,json"
|
||||
env:
|
||||
BROWSER: "chromium"
|
||||
PWMODE: "${{ matrix.mode }}"
|
||||
FOLIO_JSON_OUTPUT_NAME: "test-results/report-old.json"
|
||||
- run: xvfb-run --auto-servernum --server-args="-screen 0 1280x960x24" -- bash -c "ulimit -c unlimited && npm run folio -- chromium --reporter=dot,json"
|
||||
- run: xvfb-run --auto-servernum --server-args="-screen 0 1280x960x24" -- bash -c "ulimit -c unlimited && npm run test -- chromium --reporter=dot,json"
|
||||
env:
|
||||
PWMODE: "${{ matrix.mode }}"
|
||||
FOLIO_JSON_OUTPUT_NAME: "test-results/report.json"
|
||||
- run: ./utils/upload_flakiness_dashboard.sh ./test-results/report.json
|
||||
if: always() && github.repository == 'microsoft/playwright' && (github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/heads/release-'))
|
||||
- uses: actions/upload-artifact@v1
|
||||
@ -242,14 +208,9 @@ 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 folio test/ --workers=1 --forbid-only --timeout=60000 --global-timeout=5400000 --retries=3 --reporter=dot,json -p video"
|
||||
env:
|
||||
BROWSER: ${{ matrix.browser }}
|
||||
FOLIO_JSON_OUTPUT_NAME: "test-results/report-old.json"
|
||||
- run: xvfb-run --auto-servernum --server-args="-screen 0 1280x960x24" -- bash -c "ulimit -c unlimited && npm run folio -- ${{ matrix.browser }} --reporter=dot,json"
|
||||
- run: xvfb-run --auto-servernum --server-args="-screen 0 1280x960x24" -- bash -c "ulimit -c unlimited && npm run test -- ${{ matrix.browser }} --reporter=dot,json"
|
||||
env:
|
||||
PWVIDEO: 1
|
||||
FOLIO_JSON_OUTPUT_NAME: "test-results/report.json"
|
||||
- run: ./utils/upload_flakiness_dashboard.sh ./test-results/report.json
|
||||
if: always() && github.repository == 'microsoft/playwright' && (github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/heads/release-'))
|
||||
- uses: actions/upload-artifact@v1
|
||||
@ -266,7 +227,6 @@ jobs:
|
||||
shard: [1, 2]
|
||||
runs-on: macos-10.15
|
||||
env:
|
||||
FOLIO_JSON_OUTPUT_NAME: "test-results/report.json"
|
||||
PW_ANDROID_TESTS: 1
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
@ -281,7 +241,7 @@ jobs:
|
||||
- name: Start Android Emulator
|
||||
run: utils/avd_start.sh
|
||||
- name: Run tests
|
||||
run: npm run build-folio && node tests/folio/cli.js --config=tests/config/android.config.ts --reporter=dot,json --shard=${{ matrix.shard }}/2
|
||||
run: npm run atest -- --reporter=dot,json --shard=${{ matrix.shard }}/2
|
||||
- run: ./utils/upload_flakiness_dashboard.sh ./test-results/report.json
|
||||
if: always() && github.repository == 'microsoft/playwright' && (github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/heads/release-'))
|
||||
- uses: actions/upload-artifact@v1
|
||||
@ -315,15 +275,9 @@ 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 folio test/ --workers=1 --forbid-only --timeout=60000 --global-timeout=5400000 --retries=3 --reporter=dot,json"
|
||||
env:
|
||||
BROWSER: "chromium"
|
||||
PW_CHROMIUM_CHANNEL: "chrome"
|
||||
FOLIO_JSON_OUTPUT_NAME: "test-results/report-old.json"
|
||||
- run: xvfb-run --auto-servernum --server-args="-screen 0 1280x960x24" -- bash -c "ulimit -c unlimited && npm run folio -- chromium --reporter=dot,json"
|
||||
- run: xvfb-run --auto-servernum --server-args="-screen 0 1280x960x24" -- bash -c "ulimit -c unlimited && npm run test -- chromium --reporter=dot,json"
|
||||
env:
|
||||
PW_CHROMIUM_CHANNEL: "chrome"
|
||||
FOLIO_JSON_OUTPUT_NAME: "test-results/report.json"
|
||||
- uses: actions/upload-artifact@v1
|
||||
if: ${{ always() }}
|
||||
with:
|
||||
@ -347,17 +301,10 @@ jobs:
|
||||
- run: npm run build
|
||||
# This only created problems, should we move ffmpeg back into npm?
|
||||
- run: node lib/cli/cli install ffmpeg
|
||||
- run: npx folio test/ --workers=1 --forbid-only --global-timeout=5400000 --retries=3 --reporter=dot,json
|
||||
shell: bash
|
||||
env:
|
||||
BROWSER: "chromium"
|
||||
PW_CHROMIUM_CHANNEL: "chrome"
|
||||
FOLIO_JSON_OUTPUT_NAME: "test-results/report-old.json"
|
||||
- run: npm run folio -- chromium --reporter=dot,json
|
||||
- run: npm run test -- chromium --reporter=dot,json
|
||||
shell: bash
|
||||
env:
|
||||
PW_CHROMIUM_CHANNEL: "chrome"
|
||||
FOLIO_JSON_OUTPUT_NAME: "test-results/report.json"
|
||||
- uses: actions/upload-artifact@v1
|
||||
if: ${{ always() }}
|
||||
with:
|
||||
@ -378,15 +325,9 @@ jobs:
|
||||
- run: npm run build
|
||||
# This only created problems, should we move ffmpeg back into npm?
|
||||
- run: node lib/cli/cli install ffmpeg
|
||||
- run: npx folio test/ --workers=1 --forbid-only --global-timeout=5400000 --retries=3 --reporter=dot,json
|
||||
env:
|
||||
BROWSER: "chromium"
|
||||
PW_CHROMIUM_CHANNEL: "chrome"
|
||||
FOLIO_JSON_OUTPUT_NAME: "test-results/report-old.json"
|
||||
- run: npm run folio -- chromium --reporter=dot,json
|
||||
- run: npm run test -- chromium --reporter=dot,json
|
||||
env:
|
||||
PW_CHROMIUM_CHANNEL: "chrome"
|
||||
FOLIO_JSON_OUTPUT_NAME: "test-results/report.json"
|
||||
- uses: actions/upload-artifact@v1
|
||||
if: ${{ always() }}
|
||||
with:
|
||||
@ -410,17 +351,10 @@ jobs:
|
||||
- run: npm run build
|
||||
# This only created problems, should we move ffmpeg back into npm?
|
||||
- run: node lib/cli/cli install ffmpeg
|
||||
- run: npx folio test/ --workers=1 --forbid-only --global-timeout=5400000 --retries=3 --reporter=dot,json
|
||||
shell: bash
|
||||
env:
|
||||
BROWSER: "chromium"
|
||||
PW_CHROMIUM_CHANNEL: "msedge"
|
||||
FOLIO_JSON_OUTPUT_NAME: "test-results/report-old.json"
|
||||
- run: npm run folio -- chromium --reporter=dot,json
|
||||
- run: npm run test -- chromium --reporter=dot,json
|
||||
shell: bash
|
||||
env:
|
||||
PW_CHROMIUM_CHANNEL: "msedge"
|
||||
FOLIO_JSON_OUTPUT_NAME: "test-results/report.json"
|
||||
- uses: actions/upload-artifact@v1
|
||||
if: ${{ always() }}
|
||||
with:
|
||||
|
13
package.json
13
package.json
@ -9,11 +9,11 @@
|
||||
"node": ">=10.17.0"
|
||||
},
|
||||
"scripts": {
|
||||
"ctest": "cross-env BROWSER=chromium folio test/",
|
||||
"ftest": "cross-env BROWSER=firefox folio test/",
|
||||
"wtest": "cross-env BROWSER=webkit folio test/",
|
||||
"atest": "cross-env BROWSER=chromium PW_ANDROID_TESTS=1 npx folio test/android --workers=1 --reporter=list",
|
||||
"test": "folio test/",
|
||||
"ctest": "npm run build-folio && node tests/folio/cli.js --config=tests/config/default.config.ts chromium",
|
||||
"ftest": "npm run build-folio && node tests/folio/cli.js --config=tests/config/default.config.ts firefox",
|
||||
"wtest": "npm run build-folio && node tests/folio/cli.js --config=tests/config/default.config.ts webkit",
|
||||
"atest": "npm run build-folio && node tests/folio/cli.js --config=tests/config/android.config.ts",
|
||||
"test": "npm run build-folio && node tests/folio/cli.js --config=tests/config/default.config.ts",
|
||||
"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",
|
||||
@ -29,8 +29,7 @@
|
||||
"build-android-driver": "./utils/build_android_driver.sh",
|
||||
"storybook": "start-storybook -p 6006 -s public",
|
||||
"build-storybook": "build-storybook -s public",
|
||||
"build-folio": "tsc -p ./tests/folio",
|
||||
"folio": "npm run build-folio && node tests/folio/cli.js --config=tests/config/default.config.ts"
|
||||
"build-folio": "tsc -p ./tests/folio"
|
||||
},
|
||||
"author": {
|
||||
"name": "Microsoft Corporation"
|
||||
|
@ -99,15 +99,15 @@ export class RecorderApp extends EventEmitter {
|
||||
'--window-size=600,600',
|
||||
'--window-position=1280,10',
|
||||
];
|
||||
if (isUnderTest())
|
||||
args.push(`--remote-debugging-port=0`);
|
||||
if (process.env.PW_RECORDER_PORT)
|
||||
args.push(`--remote-debugging-port=${process.env.PW_RECORDER_PORT}`);
|
||||
const context = await recorderPlaywright.chromium.launchPersistentContext(internalCallMetadata(), '', {
|
||||
channel: inspectedContext._browser.options.channel,
|
||||
sdkLanguage: inspectedContext._options.sdkLanguage,
|
||||
args,
|
||||
noDefaultViewport: true,
|
||||
headless: !!process.env.PWCLI_HEADLESS_FOR_TEST || (isUnderTest() && !inspectedContext._browser.options.headful),
|
||||
useWebSocket: isUnderTest()
|
||||
useWebSocket: !!process.env.PW_RECORDER_PORT
|
||||
});
|
||||
const controller = new ProgressController(internalCallMetadata(), context._browser);
|
||||
await controller.run(async progress => {
|
||||
|
@ -17,7 +17,7 @@ const path = require('path');
|
||||
const fs = require('fs');
|
||||
const {installCoverageHooks} = require('./coverage');
|
||||
|
||||
const browserName = process.env.BROWSER || 'chromium';
|
||||
const browserName = process.argv[2] || 'chromium';
|
||||
|
||||
let api = new Set(installCoverageHooks(browserName).coverage.keys());
|
||||
|
||||
@ -30,8 +30,21 @@ if (browserName === 'chromium') {
|
||||
|
||||
if (browserName !== 'chromium') {
|
||||
// we don't have CDPSession in non-chromium browsers
|
||||
api.delete('browser.newBrowserCDPSession');
|
||||
api.delete('browser.startTracing');
|
||||
api.delete('browser.stopTracing');
|
||||
api.delete('browserContext.backgroundPages');
|
||||
api.delete('browserContext.serviceWorkers');
|
||||
api.delete('browserContext.newCDPSession');
|
||||
api.delete('browserContext.emit("backgroundpage")');
|
||||
api.delete('browserContext.emit("serviceworker")');
|
||||
api.delete('cDPSession.send');
|
||||
api.delete('cDPSession.detach');
|
||||
api.delete('coverage.startJSCoverage');
|
||||
api.delete('coverage.stopJSCoverage');
|
||||
api.delete('coverage.startCSSCoverage');
|
||||
api.delete('coverage.stopCSSCoverage');
|
||||
api.delete('page.pdf');
|
||||
}
|
||||
|
||||
// Some permissions tests are disabled in webkit. See permissions.jest.js
|
||||
|
@ -15,8 +15,7 @@
|
||||
*/
|
||||
|
||||
import { expect } from './fixtures';
|
||||
import type { Frame, Page, BrowserContext } from '../index';
|
||||
import { chromium } from '../index';
|
||||
import type { Frame, Page } from '../index';
|
||||
|
||||
export async function attachFrame(page: Page, frameId: string, url: string): Promise<Frame> {
|
||||
const handle = await page.evaluateHandle(async ({ frameId, url }) => {
|
||||
@ -59,12 +58,3 @@ export function expectedSSLError(browserName: string): string {
|
||||
}
|
||||
return expectedSSLError;
|
||||
}
|
||||
|
||||
export async function recorderPageGetter(context: BrowserContext, toImpl: (x: any) => any) {
|
||||
while (!toImpl(context).recorderAppForTest)
|
||||
await new Promise(f => setTimeout(f, 100));
|
||||
const wsEndpoint = toImpl(context).recorderAppForTest.wsEndpoint;
|
||||
const browser = await chromium.connectOverCDP({ wsEndpoint });
|
||||
const c = browser.contexts()[0];
|
||||
return c.pages()[0] || await c.waitForEvent('page');
|
||||
}
|
||||
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
@ -14,21 +14,27 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { it, expect, describe } from '../fixtures';
|
||||
|
||||
import { test as pageTest, expect } from '../config/pageTest';
|
||||
import { test as playwrightTest } from '../config/playwrightTest';
|
||||
import http from 'http';
|
||||
|
||||
describe('chromium', (suite, { browserName }) => {
|
||||
suite.skip(browserName !== 'chromium');
|
||||
}, () => {
|
||||
it('should create a worker from a service worker', async ({page, server, context}) => {
|
||||
pageTest.describe('chromium', () => {
|
||||
pageTest.beforeEach(async ({ browserName }) => {
|
||||
pageTest.skip(browserName !== 'chromium');
|
||||
pageTest.skip(!!process.env.PW_ANDROID_TESTS);
|
||||
});
|
||||
|
||||
pageTest('should create a worker from a service worker', async ({page, server}) => {
|
||||
const [worker] = await Promise.all([
|
||||
context.waitForEvent('serviceworker'),
|
||||
page.context().waitForEvent('serviceworker'),
|
||||
page.goto(server.PREFIX + '/serviceworkers/empty/sw.html')
|
||||
]);
|
||||
expect(await worker.evaluate(() => self.toString())).toBe('[object ServiceWorkerGlobalScope]');
|
||||
});
|
||||
|
||||
it('serviceWorkers() should return current workers', async ({page, server, context}) => {
|
||||
pageTest('serviceWorkers() should return current workers', async ({page, server}) => {
|
||||
const context = page.context();
|
||||
const [worker1] = await Promise.all([
|
||||
context.waitForEvent('serviceworker'),
|
||||
page.goto(server.PREFIX + '/serviceworkers/empty/sw.html')
|
||||
@ -46,31 +52,17 @@ describe('chromium', (suite, { browserName }) => {
|
||||
expect(workers).toContain(worker2);
|
||||
});
|
||||
|
||||
it('should not create a worker from a shared worker', async ({page, server, context}) => {
|
||||
pageTest('should not create a worker from a shared worker', async ({page, server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
let serviceWorkerCreated;
|
||||
context.once('serviceworker', () => serviceWorkerCreated = true);
|
||||
page.context().once('serviceworker', () => serviceWorkerCreated = true);
|
||||
await page.evaluate(() => {
|
||||
new SharedWorker('data:text/javascript,console.log("hi")');
|
||||
});
|
||||
expect(serviceWorkerCreated).not.toBeTruthy();
|
||||
});
|
||||
|
||||
it('should close service worker together with the context', async ({browser, server}) => {
|
||||
const context = await browser.newContext();
|
||||
const page = await context.newPage();
|
||||
const [worker] = await Promise.all([
|
||||
context.waitForEvent('serviceworker'),
|
||||
page.goto(server.PREFIX + '/serviceworkers/empty/sw.html')
|
||||
]);
|
||||
const messages = [];
|
||||
context.on('close', () => messages.push('context'));
|
||||
worker.on('close', () => messages.push('worker'));
|
||||
await context.close();
|
||||
expect(messages.join('|')).toBe('worker|context');
|
||||
});
|
||||
|
||||
it('Page.route should work with intervention headers', async ({server, page}) => {
|
||||
pageTest('Page.route should work with intervention headers', async ({server, page}) => {
|
||||
server.setRoute('/intervention', (req, res) => res.end(`
|
||||
<script>
|
||||
document.write('<script src="${server.CROSS_PROCESS_PREFIX}/intervention.js">' + '</scr' + 'ipt>');
|
||||
@ -89,9 +81,31 @@ describe('chromium', (suite, { browserName }) => {
|
||||
// make it work with Edgium.
|
||||
expect(serverRequest.headers.intervention).toContain('feature/5718547946799104');
|
||||
});
|
||||
});
|
||||
|
||||
it('should connect to an existing cdp session', async ({browserType, testWorkerIndex, browserOptions }) => {
|
||||
const port = 9339 + testWorkerIndex;
|
||||
playwrightTest.describe('chromium', () => {
|
||||
playwrightTest.beforeEach(async ({ browserName }) => {
|
||||
playwrightTest.skip(browserName !== 'chromium');
|
||||
});
|
||||
|
||||
playwrightTest('should close service worker together with the context', async ({browserType, browserOptions, server}) => {
|
||||
const browser = await browserType.launch(browserOptions);
|
||||
const context = await browser.newContext();
|
||||
const page = await context.newPage();
|
||||
const [worker] = await Promise.all([
|
||||
context.waitForEvent('serviceworker'),
|
||||
page.goto(server.PREFIX + '/serviceworkers/empty/sw.html')
|
||||
]);
|
||||
const messages = [];
|
||||
context.on('close', () => messages.push('context'));
|
||||
worker.on('close', () => messages.push('worker'));
|
||||
await context.close();
|
||||
expect(messages.join('|')).toBe('worker|context');
|
||||
await browser.close();
|
||||
});
|
||||
|
||||
playwrightTest('should connect to an existing cdp session', async ({ browserType, browserOptions }, testInfo) => {
|
||||
const port = 9339 + testInfo.workerIndex;
|
||||
const browserServer = await browserType.launch({
|
||||
...browserOptions,
|
||||
args: ['--remote-debugging-port=' + port]
|
||||
@ -115,8 +129,8 @@ describe('chromium', (suite, { browserName }) => {
|
||||
}
|
||||
});
|
||||
|
||||
it('should connect to an existing cdp session twice', async ({browserType, testWorkerIndex, browserOptions, server }) => {
|
||||
const port = 9339 + testWorkerIndex;
|
||||
playwrightTest('should connect to an existing cdp session twice', async ({ browserType, browserOptions, server }, testInfo) => {
|
||||
const port = 9339 + testInfo.workerIndex;
|
||||
const browserServer = await browserType.launch({
|
||||
...browserOptions,
|
||||
args: ['--remote-debugging-port=' + port]
|
||||
@ -157,8 +171,8 @@ describe('chromium', (suite, { browserName }) => {
|
||||
}
|
||||
});
|
||||
|
||||
it('should connect to existing service workers', async ({browserType, testWorkerIndex, browserOptions, server}) => {
|
||||
const port = 9339 + testWorkerIndex;
|
||||
playwrightTest('should connect to existing service workers', async ({browserType, browserOptions, server}, testInfo) => {
|
||||
const port = 9339 + testInfo.workerIndex;
|
||||
const browserServer = await browserType.launch({
|
||||
...browserOptions,
|
||||
args: ['--remote-debugging-port=' + port]
|
@ -14,12 +14,14 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { it, expect, describe } from './fixtures';
|
||||
import { test as it, expect } from '../config/pageTest';
|
||||
|
||||
describe('CSS Coverage', (suite, { browserName }) => {
|
||||
suite.skip(browserName !== 'chromium');
|
||||
}, () => {
|
||||
it('should work', async function({browserType, page, server}) {
|
||||
it.describe('CSS Coverage', () => {
|
||||
it.beforeEach(async ({ browserName }) => {
|
||||
it.skip(browserName !== 'chromium');
|
||||
});
|
||||
|
||||
it('should work', async function({page, server}) {
|
||||
await page.coverage.startCSSCoverage();
|
||||
await page.goto(server.PREFIX + '/csscoverage/simple.html');
|
||||
const coverage = await page.coverage.stopCSSCoverage();
|
@ -14,11 +14,13 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { it, expect, describe } from './fixtures';
|
||||
import { test as it, expect } from '../config/pageTest';
|
||||
|
||||
it.describe('JS Coverage', () => {
|
||||
it.beforeEach(async ({ browserName }) => {
|
||||
it.skip(browserName !== 'chromium');
|
||||
});
|
||||
|
||||
describe('JS Coverage', (suite, { browserName }) => {
|
||||
suite.skip(browserName !== 'chromium');
|
||||
}, () => {
|
||||
it('should work', async function({page, server}) {
|
||||
await page.coverage.startJSCoverage();
|
||||
await page.goto(server.PREFIX + '/jscoverage/simple.html', { waitUntil: 'load' });
|
@ -13,31 +13,35 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { it, expect } from '../fixtures';
|
||||
|
||||
import { test as it, expect } from '../config/playwrightTest';
|
||||
import path from 'path';
|
||||
|
||||
it('should throw with remote-debugging-pipe argument', (test, { browserName, mode }) => {
|
||||
test.skip(mode !== 'default' || browserName !== 'chromium');
|
||||
}, async ({browserType, browserOptions}) => {
|
||||
it.beforeEach(async ({ browserName }) => {
|
||||
it.skip(browserName !== 'chromium');
|
||||
});
|
||||
|
||||
it('should throw with remote-debugging-pipe argument', async ({browserType, browserOptions, mode}) => {
|
||||
it.skip(mode !== 'default');
|
||||
|
||||
const options = Object.assign({}, browserOptions);
|
||||
options.args = ['--remote-debugging-pipe'].concat(options.args || []);
|
||||
const error = await browserType.launchServer(options).catch(e => e);
|
||||
expect(error.message).toContain('Playwright manages remote debugging connection itself');
|
||||
});
|
||||
|
||||
it('should not throw with remote-debugging-port argument', (test, { browserName, mode }) => {
|
||||
test.skip(mode !== 'default' || browserName !== 'chromium');
|
||||
}, async ({browserType, browserOptions}) => {
|
||||
it('should not throw with remote-debugging-port argument', async ({browserType, browserOptions, mode}) => {
|
||||
it.skip(mode !== 'default');
|
||||
|
||||
const options = Object.assign({}, browserOptions);
|
||||
options.args = ['--remote-debugging-port=0'].concat(options.args || []);
|
||||
const browser = await browserType.launchServer(options);
|
||||
await browser.close();
|
||||
});
|
||||
|
||||
it('should open devtools when "devtools: true" option is given', (test, { mode, browserName, platform}) => {
|
||||
test.skip(browserName !== 'chromium' || mode !== 'default' || platform === 'win32');
|
||||
}, async ({browserType, browserOptions}) => {
|
||||
it('should open devtools when "devtools: true" option is given', async ({browserType, browserOptions, mode, platform}) => {
|
||||
it.skip(mode !== 'default' || platform === 'win32');
|
||||
|
||||
let devtoolsCallback;
|
||||
const devtoolsPromise = new Promise(f => devtoolsCallback = f);
|
||||
const __testHookForDevTools = devtools => devtools.__testHookOnBinding = parsed => {
|
||||
@ -53,11 +57,9 @@ it('should open devtools when "devtools: true" option is given', (test, { mode,
|
||||
await browser.close();
|
||||
});
|
||||
|
||||
it('should return background pages', (test, { browserName }) => {
|
||||
test.skip(browserName !== 'chromium');
|
||||
}, async ({browserType, browserOptions, createUserDataDir}) => {
|
||||
it('should return background pages', async ({browserType, browserOptions, createUserDataDir}) => {
|
||||
const userDataDir = await createUserDataDir();
|
||||
const extensionPath = path.join(__dirname, '..', 'assets', 'simple-extension');
|
||||
const extensionPath = path.join(__dirname, '..', '..', 'test', 'assets', 'simple-extension');
|
||||
const extensionOptions = {...browserOptions,
|
||||
headless: false,
|
||||
args: [
|
||||
@ -76,11 +78,9 @@ it('should return background pages', (test, { browserName }) => {
|
||||
await context.close();
|
||||
});
|
||||
|
||||
it('should return background pages when recording video', (test, { browserName }) => {
|
||||
test.skip(browserName !== 'chromium');
|
||||
}, async ({browserType, testInfo, browserOptions, createUserDataDir}) => {
|
||||
it('should return background pages when recording video', async ({browserType, browserOptions, createUserDataDir}, testInfo) => {
|
||||
const userDataDir = await createUserDataDir();
|
||||
const extensionPath = path.join(__dirname, '..', 'assets', 'simple-extension');
|
||||
const extensionPath = path.join(__dirname, '..', '..', 'test', 'assets', 'simple-extension');
|
||||
const extensionOptions = {...browserOptions,
|
||||
headless: false,
|
||||
args: [
|
||||
@ -102,9 +102,7 @@ it('should return background pages when recording video', (test, { browserName }
|
||||
await context.close();
|
||||
});
|
||||
|
||||
it('should not create pages automatically', (test, { browserName }) => {
|
||||
test.skip(browserName !== 'chromium');
|
||||
}, async ({browserType, browserOptions}) => {
|
||||
it('should not create pages automatically', async ({browserType, browserOptions}) => {
|
||||
const browser = await browserType.launch(browserOptions);
|
||||
const browserSession = await browser.newBrowserCDPSession();
|
||||
const targets = [];
|
@ -14,30 +14,41 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { folio } from '../fixtures';
|
||||
import { test as it, expect } from '../config/playwrightTest';
|
||||
import type { Browser, Page } from '../../index';
|
||||
|
||||
const fixtures = folio.extend();
|
||||
fixtures.browser.override(async ({browserType, browserOptions}, run) => {
|
||||
const browser = await browserType.launch({
|
||||
...browserOptions,
|
||||
args: (browserOptions.args || []).concat(['--site-per-process'])
|
||||
it.describe('oopif', () => {
|
||||
let browser: Browser;
|
||||
let page: Page;
|
||||
|
||||
it.beforeEach(async ({ browserName, browserType, browserOptions }) => {
|
||||
it.skip(browserName !== 'chromium');
|
||||
|
||||
if (!browser) {
|
||||
browser = await browserType.launch({
|
||||
...browserOptions,
|
||||
args: (browserOptions.args || []).concat(['--site-per-process'])
|
||||
});
|
||||
}
|
||||
page = await browser.newPage();
|
||||
});
|
||||
await run(browser);
|
||||
await browser.close();
|
||||
});
|
||||
const { it, expect, describe } = fixtures.build();
|
||||
|
||||
describe('oopif', (suite, { browserName }) => {
|
||||
suite.skip(browserName !== 'chromium');
|
||||
}, () => {
|
||||
it('should report oopif frames', async function({browser, page, server}) {
|
||||
it.afterEach(async () => {
|
||||
await page.close();
|
||||
});
|
||||
|
||||
it.afterAll(async () => {
|
||||
await browser.close();
|
||||
});
|
||||
|
||||
it('should report oopif frames', async function({server}) {
|
||||
await page.goto(server.PREFIX + '/dynamic-oopif.html');
|
||||
expect(await countOOPIFs(browser)).toBe(1);
|
||||
expect(page.frames().length).toBe(2);
|
||||
expect(await page.frames()[1].evaluate(() => '' + location.href)).toBe(server.CROSS_PROCESS_PREFIX + '/grid.html');
|
||||
});
|
||||
|
||||
it('should handle oopif detach', async function({browser, page, server}) {
|
||||
it('should handle oopif detach', async function({server}) {
|
||||
await page.goto(server.PREFIX + '/dynamic-oopif.html');
|
||||
expect(await countOOPIFs(browser)).toBe(1);
|
||||
expect(page.frames().length).toBe(2);
|
||||
@ -50,7 +61,7 @@ describe('oopif', (suite, { browserName }) => {
|
||||
expect(detachedFrame).toBe(frame);
|
||||
});
|
||||
|
||||
it('should handle remote -> local -> remote transitions', async function({browser, page, server}) {
|
||||
it('should handle remote -> local -> remote transitions', async function({server}) {
|
||||
await page.goto(server.PREFIX + '/dynamic-oopif.html');
|
||||
expect(page.frames().length).toBe(2);
|
||||
expect(await countOOPIFs(browser)).toBe(1);
|
||||
@ -69,10 +80,9 @@ describe('oopif', (suite, { browserName }) => {
|
||||
expect(await countOOPIFs(browser)).toBe(1);
|
||||
});
|
||||
|
||||
it('should get the proper viewport', (test, { browserName }) => {
|
||||
test.fixme(browserName === 'chromium');
|
||||
test.skip(browserName !== 'chromium');
|
||||
}, async ({browser, page, server}) => {
|
||||
it('should get the proper viewport', async ({server}) => {
|
||||
it.fixme();
|
||||
|
||||
expect(page.viewportSize()).toEqual({width: 1280, height: 720});
|
||||
await page.goto(server.PREFIX + '/dynamic-oopif.html');
|
||||
expect(page.frames().length).toBe(2);
|
||||
@ -91,7 +101,7 @@ describe('oopif', (suite, { browserName }) => {
|
||||
expect(await oopif.evaluate(() => 'ontouchstart' in window)).toBe(false);
|
||||
});
|
||||
|
||||
it('should expose function', async ({browser, page, server}) => {
|
||||
it('should expose function', async ({server}) => {
|
||||
await page.goto(server.PREFIX + '/dynamic-oopif.html');
|
||||
expect(page.frames().length).toBe(2);
|
||||
expect(await countOOPIFs(browser)).toBe(1);
|
||||
@ -103,7 +113,7 @@ describe('oopif', (suite, { browserName }) => {
|
||||
expect(result).toBe(36);
|
||||
});
|
||||
|
||||
it('should emulate media', async ({browser, page, server}) => {
|
||||
it('should emulate media', async ({server}) => {
|
||||
await page.goto(server.PREFIX + '/dynamic-oopif.html');
|
||||
expect(page.frames().length).toBe(2);
|
||||
expect(await countOOPIFs(browser)).toBe(1);
|
||||
@ -113,17 +123,17 @@ describe('oopif', (suite, { browserName }) => {
|
||||
expect(await oopif.evaluate(() => matchMedia('(prefers-color-scheme: dark)').matches)).toBe(true);
|
||||
});
|
||||
|
||||
it('should emulate offline', async ({browser, page, context, server}) => {
|
||||
it('should emulate offline', async ({server}) => {
|
||||
await page.goto(server.PREFIX + '/dynamic-oopif.html');
|
||||
expect(page.frames().length).toBe(2);
|
||||
expect(await countOOPIFs(browser)).toBe(1);
|
||||
const oopif = page.frames()[1];
|
||||
expect(await oopif.evaluate(() => navigator.onLine)).toBe(true);
|
||||
await context.setOffline(true);
|
||||
await page.context().setOffline(true);
|
||||
expect(await oopif.evaluate(() => navigator.onLine)).toBe(false);
|
||||
});
|
||||
|
||||
it('should support context options', async ({browser, server, playwright}) => {
|
||||
it('should support context options', async ({server, playwright}) => {
|
||||
const iPhone = playwright.devices['iPhone 6'];
|
||||
const context = await browser.newContext({ ...iPhone, timezoneId: 'America/Jamaica', locale: 'fr-CH', userAgent: 'UA' });
|
||||
const page = await context.newPage();
|
||||
@ -145,7 +155,7 @@ describe('oopif', (suite, { browserName }) => {
|
||||
await context.close();
|
||||
});
|
||||
|
||||
it('should respect route', async ({browser, page, server}) => {
|
||||
it('should respect route', async ({server}) => {
|
||||
let intercepted = false;
|
||||
await page.route('**/digits/0.png', route => {
|
||||
intercepted = true;
|
||||
@ -157,7 +167,7 @@ describe('oopif', (suite, { browserName }) => {
|
||||
expect(intercepted).toBe(true);
|
||||
});
|
||||
|
||||
it('should take screenshot', async ({browser, page, server}) => {
|
||||
it('should take screenshot', async ({server}) => {
|
||||
await page.setViewportSize({width: 500, height: 500});
|
||||
await page.goto(server.PREFIX + '/dynamic-oopif.html');
|
||||
expect(page.frames().length).toBe(2);
|
||||
@ -165,13 +175,13 @@ describe('oopif', (suite, { browserName }) => {
|
||||
expect(await page.screenshot()).toMatchSnapshot('screenshot-oopif.png', { threshold: 0.3 });
|
||||
});
|
||||
|
||||
it('should load oopif iframes with subresources and route', async function({browser, page, server, context}) {
|
||||
it('should load oopif iframes with subresources and route', async function({server}) {
|
||||
await page.route('**/*', route => route.continue());
|
||||
await page.goto(server.PREFIX + '/dynamic-oopif.html');
|
||||
expect(await countOOPIFs(browser)).toBe(1);
|
||||
});
|
||||
|
||||
it('should report main requests', async function({browser, page, server}) {
|
||||
it('should report main requests', async function({server}) {
|
||||
const requestFrames = [];
|
||||
page.on('request', r => requestFrames.push(r.frame()));
|
||||
const finishedFrames = [];
|
||||
@ -209,8 +219,8 @@ describe('oopif', (suite, { browserName }) => {
|
||||
expect(finishedFrames[2]).toBe(grandChild);
|
||||
});
|
||||
|
||||
it('should support exposeFunction', async function({browser, context, page, server}) {
|
||||
await context.exposeFunction('dec', a => a - 1);
|
||||
it('should support exposeFunction', async function({server}) {
|
||||
await page.context().exposeFunction('dec', a => a - 1);
|
||||
await page.exposeFunction('inc', a => a + 1);
|
||||
await page.goto(server.PREFIX + '/dynamic-oopif.html');
|
||||
expect(await countOOPIFs(browser)).toBe(1);
|
||||
@ -221,8 +231,8 @@ describe('oopif', (suite, { browserName }) => {
|
||||
expect(await page.frames()[1].evaluate(() => window['dec'](4))).toBe(3);
|
||||
});
|
||||
|
||||
it('should support addInitScript', async function({browser, context, page, server}) {
|
||||
await context.addInitScript(() => window['bar'] = 17);
|
||||
it('should support addInitScript', async function({server}) {
|
||||
await page.context().addInitScript(() => window['bar'] = 17);
|
||||
await page.addInitScript(() => window['foo'] = 42);
|
||||
await page.goto(server.PREFIX + '/dynamic-oopif.html');
|
||||
expect(await countOOPIFs(browser)).toBe(1);
|
||||
@ -233,7 +243,7 @@ describe('oopif', (suite, { browserName }) => {
|
||||
expect(await page.frames()[1].evaluate(() => window['bar'])).toBe(17);
|
||||
});
|
||||
// @see https://github.com/microsoft/playwright/issues/1240
|
||||
it('should click a button when it overlays oopif', async function({browser, page, server}) {
|
||||
it('should click a button when it overlays oopif', async function({server}) {
|
||||
await page.goto(server.PREFIX + '/button-overlay-oopif.html');
|
||||
expect(await countOOPIFs(browser)).toBe(1);
|
||||
await page.click('button');
|
||||
@ -265,7 +275,7 @@ describe('oopif', (suite, { browserName }) => {
|
||||
await browser.close();
|
||||
});
|
||||
|
||||
it('ElementHandle.boundingBox() should work', async function({browser, page, server}) {
|
||||
it('ElementHandle.boundingBox() should work', async function({server}) {
|
||||
await page.goto(server.PREFIX + '/dynamic-oopif.html');
|
||||
await page.$eval('iframe', iframe => {
|
||||
iframe.style.width = '500px';
|
||||
@ -288,7 +298,7 @@ describe('oopif', (suite, { browserName }) => {
|
||||
expect(await handle2.boundingBox()).toEqual({ x: 100 + 42, y: 50 + 17, width: 50, height: 50 });
|
||||
});
|
||||
|
||||
it('should click', async function({browser, page, server}) {
|
||||
it('should click', async function({server}) {
|
||||
await page.goto(server.PREFIX + '/dynamic-oopif.html');
|
||||
await page.$eval('iframe', iframe => {
|
||||
iframe.style.width = '500px';
|
@ -13,11 +13,15 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { it, expect, describe } from '../fixtures';
|
||||
|
||||
describe('session', (suite, { browserName }) => {
|
||||
suite.skip(browserName !== 'chromium');
|
||||
}, () => {
|
||||
import { test as it, expect } from '../config/pageTest';
|
||||
import { test as browserTest } from '../config/browserTest';
|
||||
|
||||
it.describe('session', () => {
|
||||
it.beforeEach(async ({ browserName }) => {
|
||||
it.skip(browserName !== 'chromium');
|
||||
});
|
||||
|
||||
it('should work', async function({page}) {
|
||||
const client = await page.context().newCDPSession(page);
|
||||
|
||||
@ -88,8 +92,14 @@ describe('session', (suite, { browserName }) => {
|
||||
await client.send('ThisCommand.DoesNotExist');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('should not break page.close()', async function({browser}) {
|
||||
browserTest.describe('session', () => {
|
||||
browserTest.beforeEach(async ({ browserName }) => {
|
||||
browserTest.skip(browserName !== 'chromium');
|
||||
});
|
||||
|
||||
browserTest('should not break page.close()', async function({browser}) {
|
||||
const context = await browser.newContext();
|
||||
const page = await context.newPage();
|
||||
const session = await page.context().newCDPSession(page);
|
||||
@ -98,7 +108,7 @@ describe('session', (suite, { browserName }) => {
|
||||
await context.close();
|
||||
});
|
||||
|
||||
it('should detach when page closes', async function({browser}) {
|
||||
browserTest('should detach when page closes', async function({browser}) {
|
||||
const context = await browser.newContext();
|
||||
const page = await context.newPage();
|
||||
const session = await context.newCDPSession(page);
|
||||
@ -109,7 +119,7 @@ describe('session', (suite, { browserName }) => {
|
||||
await context.close();
|
||||
});
|
||||
|
||||
it('should work with newBrowserCDPSession', async function({browser}) {
|
||||
browserTest('should work with newBrowserCDPSession', async function({browser}) {
|
||||
const session = await browser.newBrowserCDPSession();
|
||||
|
||||
const version = await session.send('Browser.getVersion');
|
@ -14,40 +14,48 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { folio } from '../fixtures';
|
||||
import { test as it, expect } from '../config/browserTest';
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
const { it, expect, describe } = folio;
|
||||
|
||||
describe('tracing', (suite, { browserName }) => {
|
||||
suite.skip(browserName !== 'chromium');
|
||||
}, () => {
|
||||
it('should output a trace', async ({browser, page, server, testInfo}) => {
|
||||
it.describe('tracing', () => {
|
||||
it.beforeEach(async ({ browserName }) => {
|
||||
it.skip(browserName !== 'chromium');
|
||||
});
|
||||
|
||||
it('should output a trace', async ({browser, server}, testInfo) => {
|
||||
const page = await browser.newPage();
|
||||
const outputTraceFile = testInfo.outputPath(path.join(`trace.json`));
|
||||
await browser.startTracing(page, {screenshots: true, path: outputTraceFile});
|
||||
await page.goto(server.PREFIX + '/grid.html');
|
||||
await browser.stopTracing();
|
||||
expect(fs.existsSync(outputTraceFile)).toBe(true);
|
||||
await page.close();
|
||||
});
|
||||
|
||||
it('should create directories as needed', async ({browser, page, server, testInfo}) => {
|
||||
it('should create directories as needed', async ({browser, server}, testInfo) => {
|
||||
const page = await browser.newPage();
|
||||
const filePath = testInfo.outputPath(path.join('these', 'are', 'directories', 'trace.json'));
|
||||
await browser.startTracing(page, {screenshots: true, path: filePath});
|
||||
await page.goto(server.PREFIX + '/grid.html');
|
||||
await browser.stopTracing();
|
||||
expect(fs.existsSync(filePath)).toBe(true);
|
||||
await page.close();
|
||||
});
|
||||
|
||||
it('should run with custom categories if provided', async ({browser, page, testInfo}) => {
|
||||
it('should run with custom categories if provided', async ({browser}, testInfo) => {
|
||||
const page = await browser.newPage();
|
||||
const outputTraceFile = testInfo.outputPath(path.join(`trace.json`));
|
||||
await browser.startTracing(page, {path: outputTraceFile, categories: ['disabled-by-default-v8.cpu_profiler.hires']});
|
||||
await browser.stopTracing();
|
||||
|
||||
const traceJson = JSON.parse(fs.readFileSync(outputTraceFile).toString());
|
||||
expect(traceJson.metadata['trace-config']).toContain('disabled-by-default-v8.cpu_profiler.hires');
|
||||
await page.close();
|
||||
});
|
||||
|
||||
it('should throw if tracing on two pages', async ({browser, page, testInfo}) => {
|
||||
it('should throw if tracing on two pages', async ({browser}, testInfo) => {
|
||||
const page = await browser.newPage();
|
||||
const outputTraceFile = testInfo.outputPath(path.join(`trace.json`));
|
||||
await browser.startTracing(page, {path: outputTraceFile});
|
||||
const newPage = await browser.newPage();
|
||||
@ -56,28 +64,35 @@ describe('tracing', (suite, { browserName }) => {
|
||||
await newPage.close();
|
||||
expect(error).toBeTruthy();
|
||||
await browser.stopTracing();
|
||||
await page.close();
|
||||
});
|
||||
|
||||
it('should return a buffer', async ({browser, page, server, testInfo}) => {
|
||||
it('should return a buffer', async ({browser, server}, testInfo) => {
|
||||
const page = await browser.newPage();
|
||||
const outputTraceFile = testInfo.outputPath(path.join(`trace.json`));
|
||||
await browser.startTracing(page, {screenshots: true, path: outputTraceFile});
|
||||
await page.goto(server.PREFIX + '/grid.html');
|
||||
const trace = await browser.stopTracing();
|
||||
const buf = fs.readFileSync(outputTraceFile);
|
||||
expect(trace.toString()).toEqual(buf.toString());
|
||||
await page.close();
|
||||
});
|
||||
|
||||
it('should work without options', async ({browser, page, server}) => {
|
||||
it('should work without options', async ({browser, server}) => {
|
||||
const page = await browser.newPage();
|
||||
await browser.startTracing(page);
|
||||
await page.goto(server.PREFIX + '/grid.html');
|
||||
const trace = await browser.stopTracing();
|
||||
expect(trace).toBeTruthy();
|
||||
await page.close();
|
||||
});
|
||||
|
||||
it('should support a buffer without a path', async ({browser, page, server}) => {
|
||||
it('should support a buffer without a path', async ({browser, server}) => {
|
||||
const page = await browser.newPage();
|
||||
await browser.startTracing(page, {screenshots: true});
|
||||
await page.goto(server.PREFIX + '/grid.html');
|
||||
const trace = await browser.stopTracing();
|
||||
expect(trace.toString()).toContain('screenshot');
|
||||
await page.close();
|
||||
});
|
||||
});
|
@ -24,10 +24,10 @@ import { AndroidEnv, AndroidPageEnv } from './androidEnv';
|
||||
const config: Config = {
|
||||
testDir: path.join(__dirname, '..'),
|
||||
timeout: 120000,
|
||||
globalTimeout: 5400000,
|
||||
globalTimeout: 7200000,
|
||||
workers: 1,
|
||||
};
|
||||
if (process.env.CI) {
|
||||
config.workers = 1;
|
||||
config.forbidOnly = true;
|
||||
config.retries = 1; // Multiple retries are too slow on Android.
|
||||
}
|
||||
|
@ -58,7 +58,7 @@ class ServiceMode {
|
||||
private _serviceProcess: childProcess.ChildProcess;
|
||||
|
||||
async setup(workerInfo: WorkerInfo) {
|
||||
const port = 9407 + workerInfo.workerIndex * 2;
|
||||
const port = 10507 + workerInfo.workerIndex;
|
||||
this._serviceProcess = childProcess.fork(path.join(__dirname, '..', '..', 'lib', 'service.js'), [String(port)], {
|
||||
stdio: 'pipe'
|
||||
});
|
||||
@ -69,7 +69,6 @@ class ServiceMode {
|
||||
f();
|
||||
});
|
||||
});
|
||||
this._serviceProcess.unref();
|
||||
this._serviceProcess.on('exit', this._onExit);
|
||||
this._client = await PlaywrightClient.connect(`ws://localhost:${port}/ws`);
|
||||
this._playwrightObejct = this._client.playwright();
|
||||
|
@ -18,7 +18,7 @@ import type { Env, TestInfo, WorkerInfo } from '../folio/out';
|
||||
import { PageEnv } from './browserEnv';
|
||||
import { CLIMock, CLITestArgs, Recorder } from './cliTest';
|
||||
import * as http from 'http';
|
||||
import { recorderPageGetter } from '../../test/utils';
|
||||
import { chromium } from '../../index';
|
||||
|
||||
export class CLIEnv extends PageEnv implements Env<CLITestArgs> {
|
||||
private _server: http.Server | undefined;
|
||||
@ -29,8 +29,9 @@ export class CLIEnv extends PageEnv implements Env<CLITestArgs> {
|
||||
async beforeAll(workerInfo: WorkerInfo) {
|
||||
await super.beforeAll(workerInfo);
|
||||
|
||||
this._port = 9907 + workerInfo.workerIndex;
|
||||
this._port = 10907 + workerInfo.workerIndex * 2;
|
||||
this._server = http.createServer((req: http.IncomingMessage, res: http.ServerResponse) => this._handler(req, res)).listen(this._port);
|
||||
process.env.PW_RECORDER_PORT = String(this._port + 1);
|
||||
}
|
||||
|
||||
private _runCLI(args: string[]) {
|
||||
@ -41,6 +42,14 @@ export class CLIEnv extends PageEnv implements Env<CLITestArgs> {
|
||||
async beforeEach(testInfo: TestInfo) {
|
||||
const result = await super.beforeEach(testInfo);
|
||||
const { page, context, toImpl } = result;
|
||||
const recorderPageGetter = async () => {
|
||||
while (!toImpl(context).recorderAppForTest)
|
||||
await new Promise(f => setTimeout(f, 100));
|
||||
const wsEndpoint = toImpl(context).recorderAppForTest.wsEndpoint;
|
||||
const browser = await chromium.connectOverCDP({ wsEndpoint });
|
||||
const c = browser.contexts()[0];
|
||||
return c.pages()[0] || await c.waitForEvent('page');
|
||||
};
|
||||
return {
|
||||
...result,
|
||||
httpServer: {
|
||||
@ -50,9 +59,9 @@ export class CLIEnv extends PageEnv implements Env<CLITestArgs> {
|
||||
runCLI: this._runCLI.bind(this),
|
||||
openRecorder: async () => {
|
||||
await (page.context() as any)._enableRecorder({ language: 'javascript', startRecording: true });
|
||||
const recorderPage = await recorderPageGetter(context, toImpl);
|
||||
return new Recorder(page, recorderPage);
|
||||
return new Recorder(page, await recorderPageGetter());
|
||||
},
|
||||
recorderPageGetter,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -15,7 +15,7 @@
|
||||
*/
|
||||
|
||||
import { newTestType } from '../folio/out';
|
||||
import type { Page } from '../../index';
|
||||
import type { Page, BrowserContext } from '../../index';
|
||||
import type { ServerTestArgs } from './serverTest';
|
||||
import type { BrowserTestArgs } from './browserTest';
|
||||
import * as http from 'http';
|
||||
@ -31,7 +31,9 @@ interface CLIHTTPServer {
|
||||
|
||||
export type CLITestArgs = BrowserTestArgs & {
|
||||
page: Page;
|
||||
context: BrowserContext;
|
||||
httpServer: CLIHTTPServer;
|
||||
recorderPageGetter: () => Promise<Page>;
|
||||
openRecorder: () => Promise<Recorder>;
|
||||
runCLI: (args: string[]) => CLIMock;
|
||||
};
|
||||
|
@ -70,7 +70,8 @@ for (const browserName of browsers) {
|
||||
pageTest.runWith(browserName, serverEnv, new PageEnv(browserName, options), {});
|
||||
// TODO: get rid of contextTest if there isn't too many of them.
|
||||
contextTest.runWith(browserName, serverEnv, new PageEnv(browserName, options), {});
|
||||
cliTest.runWith(browserName, serverEnv, new CLIEnv(browserName, options), {});
|
||||
if (mode !== 'service')
|
||||
cliTest.runWith(browserName, serverEnv, new CLIEnv(browserName, options), {});
|
||||
if (browserName === 'chromium')
|
||||
electronTest.runWith(browserName, serverEnv, new ElectronEnv({ mode }));
|
||||
}
|
||||
|
@ -30,7 +30,7 @@ export class ServerEnv implements Env<ServerTestArgs> {
|
||||
const assetsPath = path.join(__dirname, '..', '..', 'test', 'assets');
|
||||
const cachedPath = path.join(__dirname, '..', '..', 'test', 'assets', 'cached');
|
||||
|
||||
const port = 8907 + workerInfo.workerIndex * 2;
|
||||
const port = 8907 + workerInfo.workerIndex * 3;
|
||||
this._server = await TestServer.create(assetsPath, port);
|
||||
this._server.enableHTTPCache(cachedPath);
|
||||
|
||||
@ -54,7 +54,7 @@ export class ServerEnv implements Env<ServerTestArgs> {
|
||||
].join('\r\n'));
|
||||
}
|
||||
});
|
||||
this._socksPort = 9107 + workerInfo.workerIndex * 2;
|
||||
this._socksPort = port + 2;
|
||||
this._socksServer.listen(this._socksPort, 'localhost');
|
||||
this._socksServer.useAuth(socks.auth.None());
|
||||
}
|
||||
|
@ -14,15 +14,13 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { folio } from './fixtures';
|
||||
const { it, expect, beforeEach, describe } = folio;
|
||||
|
||||
import { test as it, expect } from './config/browserTest';
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import util from 'util';
|
||||
|
||||
describe('download event', () => {
|
||||
beforeEach(async ({server}) => {
|
||||
it.describe('download event', () => {
|
||||
it.beforeEach(async ({server}) => {
|
||||
server.setRoute('/download', (req, res) => {
|
||||
res.setHeader('Content-Type', 'application/octet-stream');
|
||||
res.setHeader('Content-Disposition', 'attachment');
|
||||
@ -35,7 +33,8 @@ describe('download event', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('should report downloads with acceptDownloads: false', async ({page, server}) => {
|
||||
it('should report downloads with acceptDownloads: false', async ({browser, server}) => {
|
||||
const page = await browser.newPage();
|
||||
await page.setContent(`<a href="${server.PREFIX}/downloadWithFilename">download</a>`);
|
||||
const [ download ] = await Promise.all([
|
||||
page.waitForEvent('download'),
|
||||
@ -47,6 +46,7 @@ describe('download event', () => {
|
||||
await download.path().catch(e => error = e);
|
||||
expect(await download.failure()).toContain('acceptDownloads');
|
||||
expect(error.message).toContain('acceptDownloads: true');
|
||||
await page.close();
|
||||
});
|
||||
|
||||
it('should report downloads with acceptDownloads: true', async ({browser, server}) => {
|
||||
@ -62,10 +62,9 @@ describe('download event', () => {
|
||||
await page.close();
|
||||
});
|
||||
|
||||
it('should report proper download url when download is from download attribute', (test, {browserName}) => {
|
||||
// @see https://github.com/microsoft/playwright/issues/5537
|
||||
test.fixme(browserName === 'webkit');
|
||||
}, async ({browser, server}) => {
|
||||
it('should report proper download url when download is from download attribute', async ({browser, server, browserName}) => {
|
||||
it.fixme(browserName === 'webkit', '@see https://github.com/microsoft/playwright/issues/5537');
|
||||
|
||||
const page = await browser.newPage({ acceptDownloads: true });
|
||||
await page.goto(server.PREFIX + '/empty.html');
|
||||
await page.setContent(`<a href="${server.PREFIX}/chromium-linux.zip" download="foo.zip">download</a>`);
|
||||
@ -91,7 +90,7 @@ describe('download event', () => {
|
||||
await page.close();
|
||||
});
|
||||
|
||||
it('should save to user-specified path', async ({testInfo, browser, server}) => {
|
||||
it('should save to user-specified path', async ({browser, server}, testInfo) => {
|
||||
const page = await browser.newPage({ acceptDownloads: true });
|
||||
await page.setContent(`<a href="${server.PREFIX}/download">download</a>`);
|
||||
const [ download ] = await Promise.all([
|
||||
@ -105,7 +104,7 @@ describe('download event', () => {
|
||||
await page.close();
|
||||
});
|
||||
|
||||
it('should save to user-specified path without updating original path', async ({testInfo, browser, server}) => {
|
||||
it('should save to user-specified path without updating original path', async ({browser, server}, testInfo) => {
|
||||
const page = await browser.newPage({ acceptDownloads: true });
|
||||
await page.setContent(`<a href="${server.PREFIX}/download">download</a>`);
|
||||
const [ download ] = await Promise.all([
|
||||
@ -123,7 +122,7 @@ describe('download event', () => {
|
||||
await page.close();
|
||||
});
|
||||
|
||||
it('should save to two different paths with multiple saveAs calls', async ({testInfo, browser, server}) => {
|
||||
it('should save to two different paths with multiple saveAs calls', async ({browser, server}, testInfo) => {
|
||||
const page = await browser.newPage({ acceptDownloads: true });
|
||||
await page.setContent(`<a href="${server.PREFIX}/download">download</a>`);
|
||||
const [ download ] = await Promise.all([
|
||||
@ -142,7 +141,7 @@ describe('download event', () => {
|
||||
await page.close();
|
||||
});
|
||||
|
||||
it('should save to overwritten filepath', async ({testInfo, browser, server}) => {
|
||||
it('should save to overwritten filepath', async ({browser, server}, testInfo) => {
|
||||
const page = await browser.newPage({ acceptDownloads: true });
|
||||
await page.setContent(`<a href="${server.PREFIX}/download">download</a>`);
|
||||
const [ download ] = await Promise.all([
|
||||
@ -160,7 +159,7 @@ describe('download event', () => {
|
||||
await page.close();
|
||||
});
|
||||
|
||||
it('should create subdirectories when saving to non-existent user-specified path', async ({testInfo, browser, server}) => {
|
||||
it('should create subdirectories when saving to non-existent user-specified path', async ({browser, server}, testInfo) => {
|
||||
const page = await browser.newPage({ acceptDownloads: true });
|
||||
await page.setContent(`<a href="${server.PREFIX}/download">download</a>`);
|
||||
const [ download ] = await Promise.all([
|
||||
@ -174,7 +173,7 @@ describe('download event', () => {
|
||||
await page.close();
|
||||
});
|
||||
|
||||
it('should error when saving with downloads disabled', async ({testInfo, browser, server}) => {
|
||||
it('should error when saving with downloads disabled', async ({browser, server}, testInfo) => {
|
||||
const page = await browser.newPage({ acceptDownloads: false });
|
||||
await page.setContent(`<a href="${server.PREFIX}/download">download</a>`);
|
||||
const [ download ] = await Promise.all([
|
||||
@ -187,7 +186,7 @@ describe('download event', () => {
|
||||
await page.close();
|
||||
});
|
||||
|
||||
it('should error when saving after deletion', async ({testInfo, browser, server}) => {
|
||||
it('should error when saving after deletion', async ({browser, server}, testInfo) => {
|
||||
const page = await browser.newPage({ acceptDownloads: true });
|
||||
await page.setContent(`<a href="${server.PREFIX}/download">download</a>`);
|
||||
const [ download ] = await Promise.all([
|
||||
@ -235,6 +234,7 @@ describe('download event', () => {
|
||||
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 => {
|
||||
@ -248,9 +248,10 @@ describe('download event', () => {
|
||||
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}) => {
|
||||
|
||||
it('should report alt-click downloads', async ({browser, server, browserName}) => {
|
||||
it.fixme(browserName === 'firefox' || browserName === 'webkit');
|
||||
|
||||
// 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) => {
|
||||
@ -271,9 +272,9 @@ describe('download event', () => {
|
||||
await page.close();
|
||||
});
|
||||
|
||||
it('should report new window downloads', (test, { browserName, headful }) => {
|
||||
test.fixme(browserName === 'chromium' && headful);
|
||||
}, async ({browser, server}) => {
|
||||
it('should report new window downloads', async ({browser, server, browserName, headful}) => {
|
||||
it.fixme(browserName === 'chromium' && headful);
|
||||
|
||||
// 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
|
||||
@ -359,9 +360,9 @@ describe('download event', () => {
|
||||
expect(fs.existsSync(path.join(path1, '..'))).toBeFalsy();
|
||||
});
|
||||
|
||||
it('should close the context without awaiting the failed download', (test, { browserName }) => {
|
||||
test.skip(browserName !== 'chromium', 'Only Chromium downloads on alt-click');
|
||||
}, async ({browser, server, httpsServer, testInfo}) => {
|
||||
it('should close the context without awaiting the failed download', async ({browser, server, httpsServer, browserName}, testInfo) => {
|
||||
it.skip(browserName !== 'chromium', 'Only Chromium downloads on alt-click');
|
||||
|
||||
const page = await browser.newPage({ acceptDownloads: true });
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await page.setContent(`<a href="${httpsServer.PREFIX}/downloadWithFilename" download="file.txt">click me</a>`);
|
||||
@ -380,9 +381,9 @@ describe('download event', () => {
|
||||
expect(saveError.message).toContain('File deleted upon browser context closure.');
|
||||
});
|
||||
|
||||
it('should close the context without awaiting the download', (test, { browserName, platform }) => {
|
||||
test.skip(browserName === 'webkit' && platform === 'linux', 'WebKit on linux does not convert to the download immediately upon receiving headers');
|
||||
}, async ({browser, server, testInfo}) => {
|
||||
it('should close the context without awaiting the download', async ({browser, server, browserName, platform}, testInfo) => {
|
||||
it.skip(browserName === 'webkit' && platform === 'linux', 'WebKit on linux does not convert to the download immediately upon receiving headers');
|
||||
|
||||
server.setRoute('/downloadStall', (req, res) => {
|
||||
res.setHeader('Content-Type', 'application/octet-stream');
|
||||
res.setHeader('Content-Disposition', 'attachment; filename=file.txt');
|
@ -16,12 +16,15 @@
|
||||
*/
|
||||
|
||||
import path from 'path';
|
||||
import { it } from './fixtures';
|
||||
import { test as it } from './config/browserTest';
|
||||
|
||||
it('should load svg favicon with prefer-color-scheme', async ({contextFactory, server, browserName, browserChannel, headful}) => {
|
||||
it.skip(!headful && browserName !== 'firefox', 'headless browsers, except firefox, do not request favicons');
|
||||
it.skip(headful && browserName === 'webkit' && !browserChannel, 'playwright headful webkit does not have a favicon feature');
|
||||
|
||||
const context = await contextFactory();
|
||||
const page = await context.newPage();
|
||||
|
||||
it('should load svg favicon with prefer-color-scheme', (test, {browserName, browserChannel, headful}) => {
|
||||
test.skip(!headful && browserName !== 'firefox', 'headless browsers, except firefox, do not request favicons');
|
||||
test.skip(headful && browserName === 'webkit' && !browserChannel, 'playwright headful webkit does not have a favicon feature');
|
||||
}, async ({page, server}) => {
|
||||
// Browsers aggresively cache favicons, so force bust with the
|
||||
// `d` parameter to make iterating on this test more predictable and isolated.
|
||||
const favicon = `/favicon.svg?d=${Date.now()}`;
|
||||
@ -57,4 +60,6 @@ it('should load svg favicon with prefer-color-scheme', (test, {browserName, brow
|
||||
await page.waitForTimeout(500);
|
||||
// Text still being around ensures we haven't actually lost our browser to a crash.
|
||||
await page.waitForSelector('text=favicons');
|
||||
|
||||
await page.close();
|
||||
});
|
@ -13,11 +13,12 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { it, expect } from '../fixtures';
|
||||
|
||||
it('should pass firefox user preferences', (test, { browserName }) => {
|
||||
test.skip(browserName !== 'firefox');
|
||||
}, async ({browserType, browserOptions}) => {
|
||||
import { test as it, expect } from '../config/playwrightTest';
|
||||
|
||||
it('should pass firefox user preferences', async ({browserType, browserOptions, browserName}) => {
|
||||
it.skip(browserName !== 'firefox');
|
||||
|
||||
const browser = await browserType.launch({
|
||||
...browserOptions,
|
||||
firefoxUserPrefs: {
|
@ -55,10 +55,18 @@ function mergeEnvs(envs: any[]): any {
|
||||
}
|
||||
},
|
||||
afterAll: async (workerInfo: WorkerInfo) => {
|
||||
let error: Error | undefined;
|
||||
for (const env of backward) {
|
||||
if (env.afterAll)
|
||||
await env.afterAll(workerInfo);
|
||||
if (env.afterAll) {
|
||||
try {
|
||||
await env.afterAll(workerInfo);
|
||||
} catch (e) {
|
||||
error = error || e;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (error)
|
||||
throw error;
|
||||
},
|
||||
beforeEach: async (testInfo: TestInfo) => {
|
||||
let result = undefined;
|
||||
@ -71,10 +79,18 @@ function mergeEnvs(envs: any[]): any {
|
||||
return result;
|
||||
},
|
||||
afterEach: async (testInfo: TestInfo) => {
|
||||
let error: Error | undefined;
|
||||
for (const env of backward) {
|
||||
if (env.afterEach)
|
||||
await env.afterEach(testInfo);
|
||||
if (env.afterEach) {
|
||||
try {
|
||||
await env.afterEach(testInfo);
|
||||
} catch (e) {
|
||||
error = error || e;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (error)
|
||||
throw error;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
@ -14,28 +14,28 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Page } from '..';
|
||||
import { folio } from './fixtures';
|
||||
import { recorderPageGetter } from './utils';
|
||||
const { afterEach, it, describe, expect } = folio;
|
||||
import { Page } from '../index';
|
||||
import { test as it, expect } from './config/cliTest';
|
||||
|
||||
describe('pause', (suite, { mode }) => {
|
||||
suite.skip(mode !== 'default');
|
||||
}, () => {
|
||||
afterEach(async ({ context, toImpl }) => {
|
||||
it.describe('pause', () => {
|
||||
it.beforeEach(async ({ mode }) => {
|
||||
it.skip(mode !== 'default');
|
||||
});
|
||||
|
||||
it.afterEach(async ({ recorderPageGetter }) => {
|
||||
try {
|
||||
const recorderPage = await recorderPageGetter(context, toImpl);
|
||||
const recorderPage = await recorderPageGetter();
|
||||
recorderPage.click('[title=Resume]').catch(() => {});
|
||||
} catch (e) {
|
||||
// Some tests close context.
|
||||
}
|
||||
});
|
||||
|
||||
it('should pause and resume the script', async ({ page, context, toImpl }) => {
|
||||
it('should pause and resume the script', async ({ page, recorderPageGetter }) => {
|
||||
const scriptPromise = (async () => {
|
||||
await page.pause();
|
||||
})();
|
||||
const recorderPage = await recorderPageGetter(context, toImpl);
|
||||
const recorderPage = await recorderPageGetter();
|
||||
await recorderPage.click('[title=Resume]');
|
||||
await scriptPromise;
|
||||
});
|
||||
@ -52,33 +52,33 @@ describe('pause', (suite, { mode }) => {
|
||||
await scriptPromise;
|
||||
});
|
||||
|
||||
it('should pause after a navigation', async ({page, server, context, toImpl}) => {
|
||||
it('should pause after a navigation', async ({page, server, recorderPageGetter}) => {
|
||||
const scriptPromise = (async () => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await page.pause();
|
||||
})();
|
||||
const recorderPage = await recorderPageGetter(context, toImpl);
|
||||
const recorderPage = await recorderPageGetter();
|
||||
await recorderPage.click('[title=Resume]');
|
||||
await scriptPromise;
|
||||
});
|
||||
|
||||
it('should show source', async ({page, context, toImpl}) => {
|
||||
it('should show source', async ({page, recorderPageGetter}) => {
|
||||
const scriptPromise = (async () => {
|
||||
await page.pause();
|
||||
})();
|
||||
const recorderPage = await recorderPageGetter(context, toImpl);
|
||||
const recorderPage = await recorderPageGetter();
|
||||
const source = await recorderPage.textContent('.source-line-paused .source-code');
|
||||
expect(source).toContain('page.pause()');
|
||||
await recorderPage.click('[title=Resume]');
|
||||
await scriptPromise;
|
||||
});
|
||||
|
||||
it('should pause on next pause', async ({page, context, toImpl}) => {
|
||||
it('should pause on next pause', async ({page, recorderPageGetter}) => {
|
||||
const scriptPromise = (async () => {
|
||||
await page.pause(); // 1
|
||||
await page.pause(); // 2
|
||||
})();
|
||||
const recorderPage = await recorderPageGetter(context, toImpl);
|
||||
const recorderPage = await recorderPageGetter();
|
||||
const source = await recorderPage.textContent('.source-line-paused');
|
||||
expect(source).toContain('page.pause(); // 1');
|
||||
await recorderPage.click('[title=Resume]');
|
||||
@ -87,13 +87,13 @@ describe('pause', (suite, { mode }) => {
|
||||
await scriptPromise;
|
||||
});
|
||||
|
||||
it('should step', async ({page, context, toImpl}) => {
|
||||
it('should step', async ({page, recorderPageGetter}) => {
|
||||
await page.setContent('<button>Submit</button>');
|
||||
const scriptPromise = (async () => {
|
||||
await page.pause();
|
||||
await page.click('button');
|
||||
})();
|
||||
const recorderPage = await recorderPageGetter(context, toImpl);
|
||||
const recorderPage = await recorderPageGetter();
|
||||
const source = await recorderPage.textContent('.source-line-paused');
|
||||
expect(source).toContain('page.pause();');
|
||||
|
||||
@ -104,13 +104,13 @@ describe('pause', (suite, { mode }) => {
|
||||
await scriptPromise;
|
||||
});
|
||||
|
||||
it('should highlight pointer', async ({page, context, toImpl}) => {
|
||||
it('should highlight pointer', async ({page, recorderPageGetter}) => {
|
||||
await page.setContent('<button>Submit</button>');
|
||||
const scriptPromise = (async () => {
|
||||
await page.pause();
|
||||
await page.click('button');
|
||||
})();
|
||||
const recorderPage = await recorderPageGetter(context, toImpl);
|
||||
const recorderPage = await recorderPageGetter();
|
||||
await recorderPage.click('[title="Step over"]');
|
||||
|
||||
const point = await page.waitForSelector('x-pw-action-point');
|
||||
@ -130,28 +130,28 @@ describe('pause', (suite, { mode }) => {
|
||||
await scriptPromise;
|
||||
});
|
||||
|
||||
it('should skip input when resuming', async ({page, context, toImpl}) => {
|
||||
it('should skip input when resuming', async ({page, recorderPageGetter}) => {
|
||||
await page.setContent('<button>Submit</button>');
|
||||
const scriptPromise = (async () => {
|
||||
await page.pause();
|
||||
await page.click('button');
|
||||
await page.pause(); // 2
|
||||
})();
|
||||
const recorderPage = await recorderPageGetter(context, toImpl);
|
||||
const recorderPage = await recorderPageGetter();
|
||||
await recorderPage.click('[title="Resume"]');
|
||||
await recorderPage.waitForSelector('.source-line-paused:has-text("page.pause(); // 2")');
|
||||
await recorderPage.click('[title=Resume]');
|
||||
await scriptPromise;
|
||||
});
|
||||
|
||||
it('should populate log', async ({page, context, toImpl}) => {
|
||||
it('should populate log', async ({page, recorderPageGetter}) => {
|
||||
await page.setContent('<button>Submit</button>');
|
||||
const scriptPromise = (async () => {
|
||||
await page.pause();
|
||||
await page.click('button');
|
||||
await page.pause(); // 2
|
||||
})();
|
||||
const recorderPage = await recorderPageGetter(context, toImpl);
|
||||
const recorderPage = await recorderPageGetter();
|
||||
await recorderPage.click('[title="Resume"]');
|
||||
await recorderPage.waitForSelector('.source-line-paused:has-text("page.pause(); // 2")');
|
||||
expect(await sanitizeLog(recorderPage)).toEqual([
|
||||
@ -163,7 +163,7 @@ describe('pause', (suite, { mode }) => {
|
||||
await scriptPromise;
|
||||
});
|
||||
|
||||
it('should highlight waitForEvent', async ({page, context, toImpl}) => {
|
||||
it('should highlight waitForEvent', async ({page, recorderPageGetter}) => {
|
||||
await page.setContent('<button onclick="console.log(1)">Submit</button>');
|
||||
const scriptPromise = (async () => {
|
||||
await page.pause();
|
||||
@ -172,7 +172,7 @@ describe('pause', (suite, { mode }) => {
|
||||
page.click('button'),
|
||||
]);
|
||||
})();
|
||||
const recorderPage = await recorderPageGetter(context, toImpl);
|
||||
const recorderPage = await recorderPageGetter();
|
||||
await recorderPage.click('[title="Step over"]');
|
||||
await recorderPage.waitForSelector('.source-line-paused:has-text("page.click")');
|
||||
await recorderPage.waitForSelector('.source-line-running:has-text("page.waitForEvent")');
|
||||
@ -180,7 +180,7 @@ describe('pause', (suite, { mode }) => {
|
||||
await scriptPromise;
|
||||
});
|
||||
|
||||
it('should populate log with waitForEvent', async ({page, context, toImpl}) => {
|
||||
it('should populate log with waitForEvent', async ({page, recorderPageGetter}) => {
|
||||
await page.setContent('<button onclick="console.log(1)">Submit</button>');
|
||||
const scriptPromise = (async () => {
|
||||
await page.pause();
|
||||
@ -190,7 +190,7 @@ describe('pause', (suite, { mode }) => {
|
||||
]);
|
||||
await page.pause(); // 2
|
||||
})();
|
||||
const recorderPage = await recorderPageGetter(context, toImpl);
|
||||
const recorderPage = await recorderPageGetter();
|
||||
await recorderPage.click('[title="Resume"]');
|
||||
await recorderPage.waitForSelector('.source-line-paused:has-text("page.pause(); // 2")');
|
||||
expect(await sanitizeLog(recorderPage)).toEqual([
|
||||
@ -203,13 +203,13 @@ describe('pause', (suite, { mode }) => {
|
||||
await scriptPromise;
|
||||
});
|
||||
|
||||
it('should populate log with error', async ({page, context, toImpl}) => {
|
||||
it('should populate log with error', async ({page, recorderPageGetter}) => {
|
||||
await page.setContent('<button onclick="console.log(1)">Submit</button>');
|
||||
const scriptPromise = (async () => {
|
||||
await page.pause();
|
||||
await page.isChecked('button');
|
||||
})().catch(e => e);
|
||||
const recorderPage = await recorderPageGetter(context, toImpl);
|
||||
const recorderPage = await recorderPageGetter();
|
||||
await recorderPage.click('[title="Resume"]');
|
||||
await recorderPage.waitForSelector('.source-line-error');
|
||||
expect(await sanitizeLog(recorderPage)).toEqual([
|
||||
@ -223,7 +223,7 @@ describe('pause', (suite, { mode }) => {
|
||||
expect(error.message).toContain('Not a checkbox or radio button');
|
||||
});
|
||||
|
||||
it('should populate log with error in waitForEvent', async ({page, context, toImpl}) => {
|
||||
it('should populate log with error in waitForEvent', async ({page, recorderPageGetter}) => {
|
||||
await page.setContent('<button>Submit</button>');
|
||||
const scriptPromise = (async () => {
|
||||
await page.pause();
|
||||
@ -232,7 +232,7 @@ describe('pause', (suite, { mode }) => {
|
||||
page.click('button'),
|
||||
]);
|
||||
})().catch(() => {});
|
||||
const recorderPage = await recorderPageGetter(context, toImpl);
|
||||
const recorderPage = await recorderPageGetter();
|
||||
await recorderPage.click('[title="Step over"]');
|
||||
await recorderPage.waitForSelector('.source-line-paused:has-text("page.click")');
|
||||
await recorderPage.waitForSelector('.source-line-error:has-text("page.waitForEvent")');
|
||||
@ -247,36 +247,36 @@ describe('pause', (suite, { mode }) => {
|
||||
await scriptPromise;
|
||||
});
|
||||
|
||||
it('should pause on page close', async ({ page, context, toImpl }) => {
|
||||
it('should pause on page close', async ({ page, recorderPageGetter }) => {
|
||||
const scriptPromise = (async () => {
|
||||
await page.pause();
|
||||
await page.close();
|
||||
})();
|
||||
const recorderPage = await recorderPageGetter(context, toImpl);
|
||||
const recorderPage = await recorderPageGetter();
|
||||
await recorderPage.click('[title="Step over"]');
|
||||
await recorderPage.waitForSelector('.source-line-paused:has-text("page.close();")');
|
||||
await recorderPage.click('[title=Resume]');
|
||||
await scriptPromise;
|
||||
});
|
||||
|
||||
it('should pause on context close', async ({ page, context, toImpl }) => {
|
||||
it('should pause on context close', async ({ page, recorderPageGetter }) => {
|
||||
const scriptPromise = (async () => {
|
||||
await page.pause();
|
||||
await page.context().close();
|
||||
})();
|
||||
const recorderPage = await recorderPageGetter(context, toImpl);
|
||||
const recorderPage = await recorderPageGetter();
|
||||
await recorderPage.click('[title="Step over"]');
|
||||
await recorderPage.waitForSelector('.source-line-paused:has-text("page.context().close();")');
|
||||
await recorderPage.click('[title=Resume]');
|
||||
await scriptPromise;
|
||||
});
|
||||
|
||||
it('should highlight on explore', async ({ page, context, toImpl }) => {
|
||||
it('should highlight on explore', async ({ page, recorderPageGetter }) => {
|
||||
await page.setContent('<button>Submit</button>');
|
||||
const scriptPromise = (async () => {
|
||||
await page.pause();
|
||||
})();
|
||||
const recorderPage = await recorderPageGetter(context, toImpl);
|
||||
const recorderPage = await recorderPageGetter();
|
||||
const [element] = await Promise.all([
|
||||
page.waitForSelector('x-pw-highlight:visible'),
|
||||
recorderPage.fill('input[placeholder="Playwright Selector"]', 'text=Submit'),
|
@ -15,74 +15,102 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { it, expect, describe } from './fixtures';
|
||||
import { test as it, expect } from './config/browserTest';
|
||||
|
||||
function getPermission(page, name) {
|
||||
return page.evaluate(name => navigator.permissions.query({name}).then(result => result.state), name);
|
||||
}
|
||||
|
||||
describe('permissions', (suite, { browserName }) => {
|
||||
suite.skip(browserName === 'webkit');
|
||||
}, () => {
|
||||
it('should be prompt by default', async ({page, server, context}) => {
|
||||
// Permissions API is not implemented in WebKit (see https://developer.mozilla.org/en-US/docs/Web/API/Permissions_API)
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
expect(await getPermission(page, 'geolocation')).toBe('prompt');
|
||||
it.describe('permissions', () => {
|
||||
it.beforeEach(async ({ browserName }) => {
|
||||
it.skip(browserName === 'webkit', 'Permissions API is not implemented in WebKit (see https://developer.mozilla.org/en-US/docs/Web/API/Permissions_API)');
|
||||
});
|
||||
|
||||
it('should deny permission when not listed', async ({page, server, context}) => {
|
||||
it('should be prompt by default', async ({contextFactory, server}) => {
|
||||
const context = await contextFactory();
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
expect(await getPermission(page, 'geolocation')).toBe('prompt');
|
||||
await context.close();
|
||||
});
|
||||
|
||||
it('should deny permission when not listed', async ({contextFactory, server}) => {
|
||||
const context = await contextFactory();
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await context.grantPermissions([], { origin: server.EMPTY_PAGE });
|
||||
expect(await getPermission(page, 'geolocation')).toBe('denied');
|
||||
await context.close();
|
||||
});
|
||||
|
||||
it('should fail when bad permission is given', async ({page, server, context}) => {
|
||||
it('should fail when bad permission is given', async ({contextFactory, server}) => {
|
||||
const context = await contextFactory();
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
let error: Error;
|
||||
await context.grantPermissions(['foo'], { origin: server.EMPTY_PAGE }).catch(e => error = e);
|
||||
expect(error.message).toContain('Unknown permission: foo');
|
||||
await context.close();
|
||||
});
|
||||
|
||||
it('should grant geolocation permission when origin is listed', async ({page, server, context}) => {
|
||||
it('should grant geolocation permission when origin is listed', async ({contextFactory, server}) => {
|
||||
const context = await contextFactory();
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await context.grantPermissions(['geolocation'], { origin: server.EMPTY_PAGE });
|
||||
expect(await getPermission(page, 'geolocation')).toBe('granted');
|
||||
await context.close();
|
||||
});
|
||||
|
||||
it('should prompt for geolocation permission when origin is not listed', async ({page, server, context}) => {
|
||||
it('should prompt for geolocation permission when origin is not listed', async ({contextFactory, server}) => {
|
||||
const context = await contextFactory();
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await context.grantPermissions(['geolocation'], { origin: server.EMPTY_PAGE });
|
||||
await page.goto(server.EMPTY_PAGE.replace('localhost', '127.0.0.1'));
|
||||
expect(await getPermission(page, 'geolocation')).toBe('prompt');
|
||||
await context.close();
|
||||
});
|
||||
|
||||
it('should grant notifications permission when listed', async ({page, server, context}) => {
|
||||
it('should grant notifications permission when listed', async ({contextFactory, server}) => {
|
||||
const context = await contextFactory();
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await context.grantPermissions(['notifications'], { origin: server.EMPTY_PAGE });
|
||||
expect(await getPermission(page, 'notifications')).toBe('granted');
|
||||
await context.close();
|
||||
});
|
||||
|
||||
it('should accumulate when adding', async ({page, server, context}) => {
|
||||
it('should accumulate when adding', async ({contextFactory, server}) => {
|
||||
const context = await contextFactory();
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await context.grantPermissions(['geolocation']);
|
||||
await context.grantPermissions(['notifications']);
|
||||
expect(await getPermission(page, 'geolocation')).toBe('granted');
|
||||
expect(await getPermission(page, 'notifications')).toBe('granted');
|
||||
await context.close();
|
||||
});
|
||||
|
||||
it('should clear permissions', async ({page, server, context}) => {
|
||||
it('should clear permissions', async ({contextFactory, server}) => {
|
||||
const context = await contextFactory();
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await context.grantPermissions(['geolocation']);
|
||||
await context.clearPermissions();
|
||||
await context.grantPermissions(['notifications']);
|
||||
expect(await getPermission(page, 'geolocation')).not.toBe('granted');
|
||||
expect(await getPermission(page, 'notifications')).toBe('granted');
|
||||
await context.close();
|
||||
});
|
||||
|
||||
it('should grant permission when listed for all domains', async ({page, server, context}) => {
|
||||
it('should grant permission when listed for all domains', async ({contextFactory, server}) => {
|
||||
const context = await contextFactory();
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await context.grantPermissions(['geolocation']);
|
||||
expect(await getPermission(page, 'geolocation')).toBe('granted');
|
||||
await context.close();
|
||||
});
|
||||
|
||||
it('should grant permission when creating context', async ({server, browser}) => {
|
||||
@ -93,21 +121,23 @@ describe('permissions', (suite, { browserName }) => {
|
||||
await context.close();
|
||||
});
|
||||
|
||||
it('should reset permissions', async ({page, server, context}) => {
|
||||
it('should reset permissions', async ({contextFactory, server}) => {
|
||||
const context = await contextFactory();
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await context.grantPermissions(['geolocation'], { origin: server.EMPTY_PAGE });
|
||||
expect(await getPermission(page, 'geolocation')).toBe('granted');
|
||||
await context.clearPermissions();
|
||||
expect(await getPermission(page, 'geolocation')).toBe('prompt');
|
||||
await context.close();
|
||||
});
|
||||
|
||||
it('should trigger permission onchange', (test, { browserName, headful, platform }) => {
|
||||
test.fail(browserName === 'webkit');
|
||||
test.fail(browserName === 'chromium' && headful);
|
||||
}, async ({page, server, context}) => {
|
||||
// TODO: flaky
|
||||
// - Linux: https://github.com/microsoft/playwright/pull/1790/checks?check_run_id=587327883
|
||||
// - Win: https://ci.appveyor.com/project/aslushnikov/playwright/builds/32402536
|
||||
it('should trigger permission onchange', async ({contextFactory, server, browserName, headful}) => {
|
||||
it.fail(browserName === 'webkit');
|
||||
it.fail(browserName === 'chromium' && headful);
|
||||
|
||||
const context = await contextFactory();
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await page.evaluate(() => {
|
||||
window['events'] = [];
|
||||
@ -125,9 +155,12 @@ describe('permissions', (suite, { browserName }) => {
|
||||
expect(await page.evaluate(() => window['events'])).toEqual(['prompt', 'denied', 'granted']);
|
||||
await context.clearPermissions();
|
||||
expect(await page.evaluate(() => window['events'])).toEqual(['prompt', 'denied', 'granted', 'prompt']);
|
||||
await context.close();
|
||||
});
|
||||
|
||||
it('should isolate permissions between browser contexts', async ({page, server, context, browser}) => {
|
||||
it('should isolate permissions between browser contexts', async ({server, browser}) => {
|
||||
const context = await browser.newContext();
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
const otherContext = await browser.newContext();
|
||||
const otherPage = await otherContext.newPage();
|
||||
@ -144,13 +177,16 @@ describe('permissions', (suite, { browserName }) => {
|
||||
expect(await getPermission(page, 'geolocation')).toBe('prompt');
|
||||
expect(await getPermission(otherPage, 'geolocation')).toBe('granted');
|
||||
await otherContext.close();
|
||||
await context.close();
|
||||
});
|
||||
|
||||
it('should support clipboard read', (test, { browserName, headful }) => {
|
||||
test.fail(browserName === 'webkit');
|
||||
test.fail(browserName === 'firefox', 'No such permissions (requires flag) in Firefox');
|
||||
test.fixme(browserName === 'chromium' && headful);
|
||||
}, async ({page, server, context}) => {
|
||||
it('should support clipboard read', async ({contextFactory, server, browserName, headful}) => {
|
||||
it.fail(browserName === 'webkit');
|
||||
it.fail(browserName === 'firefox', 'No such permissions (requires flag) in Firefox');
|
||||
it.fixme(browserName === 'chromium' && headful);
|
||||
|
||||
const context = await contextFactory();
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
expect(await getPermission(page, 'clipboard-read')).toBe('prompt');
|
||||
let error;
|
||||
@ -159,5 +195,6 @@ describe('permissions', (suite, { browserName }) => {
|
||||
await context.grantPermissions(['clipboard-read']);
|
||||
expect(await getPermission(page, 'clipboard-read')).toBe('granted');
|
||||
await page.evaluate(() => navigator.clipboard.readText());
|
||||
await context.close();
|
||||
});
|
||||
});
|
@ -15,9 +15,11 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { expect, it } from './fixtures';
|
||||
import { test as it, expect } from './config/browserTest';
|
||||
|
||||
it('should work', async ({ page, server }) => {
|
||||
it('should work', async ({ contextFactory, server }) => {
|
||||
const context = await contextFactory();
|
||||
const page = await context.newPage();
|
||||
const [request] = await Promise.all([
|
||||
page.waitForEvent('requestfinished'),
|
||||
page.goto(server.EMPTY_PAGE)
|
||||
@ -28,9 +30,12 @@ it('should work', async ({ page, server }) => {
|
||||
expect(timing.responseStart).toBeGreaterThanOrEqual(timing.requestStart);
|
||||
expect(timing.responseEnd).toBeGreaterThanOrEqual(timing.responseStart);
|
||||
expect(timing.responseEnd).toBeLessThan(10000);
|
||||
await context.close();
|
||||
});
|
||||
|
||||
it('should work for subresource', async ({ page, server }) => {
|
||||
it('should work for subresource', async ({ contextFactory, server }) => {
|
||||
const context = await contextFactory();
|
||||
const page = await context.newPage();
|
||||
const requests = [];
|
||||
page.on('requestfinished', request => requests.push(request));
|
||||
await page.goto(server.PREFIX + '/one-style.html');
|
||||
@ -41,6 +46,7 @@ it('should work for subresource', async ({ page, server }) => {
|
||||
expect(timing.responseStart).toBeGreaterThan(timing.requestStart);
|
||||
expect(timing.responseEnd).toBeGreaterThanOrEqual(timing.responseStart);
|
||||
expect(timing.responseEnd).toBeLessThan(10000);
|
||||
await context.close();
|
||||
});
|
||||
|
||||
it('should work for SSL', async ({ browser, httpsServer }) => {
|
||||
@ -58,9 +64,11 @@ it('should work for SSL', async ({ browser, httpsServer }) => {
|
||||
await page.close();
|
||||
});
|
||||
|
||||
it('should work for redirect', (test, { browserName }) => {
|
||||
test.fixme(browserName === 'webkit', `In WebKit, redirects don't carry the timing info`);
|
||||
}, async ({ page, server }) => {
|
||||
it('should work for redirect', async ({ contextFactory, browserName, server }) => {
|
||||
it.fixme(browserName === 'webkit', `In WebKit, redirects don't carry the timing info`);
|
||||
|
||||
const context = await contextFactory();
|
||||
const page = await context.newPage();
|
||||
server.setRedirect('/foo.html', '/empty.html');
|
||||
const responses = [];
|
||||
page.on('response', response => responses.push(response));
|
||||
@ -84,6 +92,8 @@ it('should work for redirect', (test, { browserName }) => {
|
||||
expect(timing2.responseStart).toBeGreaterThan(timing2.requestStart);
|
||||
expect(timing2.responseEnd).toBeGreaterThanOrEqual(timing2.responseStart);
|
||||
expect(timing2.responseEnd).toBeLessThan(10000);
|
||||
|
||||
await context.close();
|
||||
});
|
||||
|
||||
function verifyTimingValue(value: number, previous: number) {
|
@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { it, expect, describe } from './fixtures';
|
||||
import { slowTest as it, expect } from './config/browserTest';
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import { spawnSync } from 'child_process';
|
||||
@ -151,15 +151,13 @@ function expectRedFrames(videoFile: string, size: { width: number, height: numbe
|
||||
}
|
||||
}
|
||||
|
||||
describe('screencast', suite => {
|
||||
suite.slow();
|
||||
}, () => {
|
||||
it.describe('screencast', () => {
|
||||
it('videoSize should require videosPath', async ({browser}) => {
|
||||
const error = await browser.newContext({ videoSize: { width: 100, height: 100 } }).catch(e => e);
|
||||
expect(error.message).toContain('"videoSize" option requires "videosPath" to be specified');
|
||||
});
|
||||
|
||||
it('should work with old options', async ({browser, testInfo}) => {
|
||||
it('should work with old options', async ({browser}, testInfo) => {
|
||||
const videosPath = testInfo.outputPath('');
|
||||
const size = { width: 450, height: 240 };
|
||||
const context = await browser.newContext({
|
||||
@ -182,7 +180,7 @@ describe('screencast', suite => {
|
||||
expect(error.message).toContain('recordVideo.dir: expected string, got undefined');
|
||||
});
|
||||
|
||||
it('should capture static page', async ({browser, testInfo}) => {
|
||||
it('should capture static page', async ({browser}, testInfo) => {
|
||||
const size = { width: 450, height: 240 };
|
||||
const context = await browser.newContext({
|
||||
recordVideo: {
|
||||
@ -201,7 +199,7 @@ describe('screencast', suite => {
|
||||
expectRedFrames(videoFile, size);
|
||||
});
|
||||
|
||||
it('should expose video path', async ({browser, testInfo}) => {
|
||||
it('should expose video path', async ({browser}, testInfo) => {
|
||||
const videosPath = testInfo.outputPath('');
|
||||
const size = { width: 320, height: 240 };
|
||||
const context = await browser.newContext({
|
||||
@ -219,7 +217,7 @@ describe('screencast', suite => {
|
||||
expect(fs.existsSync(path)).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should saveAs video', async ({browser, testInfo}) => {
|
||||
it('should saveAs video', async ({browser}, testInfo) => {
|
||||
const videosPath = testInfo.outputPath('');
|
||||
const size = { width: 320, height: 240 };
|
||||
const context = await browser.newContext({
|
||||
@ -239,7 +237,7 @@ describe('screencast', suite => {
|
||||
expect(fs.existsSync(saveAsPath)).toBeTruthy();
|
||||
});
|
||||
|
||||
it('saveAs should throw when no video frames', async ({browser, browserName, testInfo}) => {
|
||||
it('saveAs should throw when no video frames', async ({browser, browserName}, testInfo) => {
|
||||
const videosPath = testInfo.outputPath('');
|
||||
const size = { width: 320, height: 240 };
|
||||
const context = await browser.newContext({
|
||||
@ -269,7 +267,7 @@ describe('screencast', suite => {
|
||||
expect(error.message).toContain('Page did not produce any video frames');
|
||||
});
|
||||
|
||||
it('should delete video', async ({browser, testInfo}) => {
|
||||
it('should delete video', async ({browser}, testInfo) => {
|
||||
const videosPath = testInfo.outputPath('');
|
||||
const size = { width: 320, height: 240 };
|
||||
const context = await browser.newContext({
|
||||
@ -290,7 +288,7 @@ describe('screencast', suite => {
|
||||
expect(fs.existsSync(videoPath)).toBeFalsy();
|
||||
});
|
||||
|
||||
it('should expose video path blank page', async ({browser, testInfo}) => {
|
||||
it('should expose video path blank page', async ({browser}, testInfo) => {
|
||||
const videosPath = testInfo.outputPath('');
|
||||
const size = { width: 320, height: 240 };
|
||||
const context = await browser.newContext({
|
||||
@ -307,7 +305,7 @@ describe('screencast', suite => {
|
||||
expect(fs.existsSync(path)).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should expose video path blank popup', async ({browser, testInfo}) => {
|
||||
it('should expose video path blank popup', async ({browser}, testInfo) => {
|
||||
const videosPath = testInfo.outputPath('');
|
||||
const size = { width: 320, height: 240 };
|
||||
const context = await browser.newContext({
|
||||
@ -328,7 +326,7 @@ describe('screencast', suite => {
|
||||
expect(fs.existsSync(path)).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should capture navigation', async ({browser, server, testInfo}) => {
|
||||
it('should capture navigation', async ({browser, server}, testInfo) => {
|
||||
const context = await browser.newContext({
|
||||
recordVideo: {
|
||||
dir: testInfo.outputPath(''),
|
||||
@ -359,10 +357,10 @@ describe('screencast', suite => {
|
||||
}
|
||||
});
|
||||
|
||||
it('should capture css transformation', (test, { headful, browserName, platform }) => {
|
||||
test.fixme(headful, 'Fails on headful');
|
||||
test.fixme(browserName === 'webkit' && platform === 'win32', 'Fails on headful');
|
||||
}, async ({browser, server, testInfo}) => {
|
||||
it('should capture css transformation', async ({browser, server, headful, browserName, platform}, testInfo) => {
|
||||
it.fixme(headful, 'Fails on headful');
|
||||
it.fixme(browserName === 'webkit' && platform === 'win32', 'Fails on headful');
|
||||
|
||||
const size = { width: 320, height: 240 };
|
||||
// Set viewport equal to screencast frame size to avoid scaling.
|
||||
const context = await browser.newContext({
|
||||
@ -389,7 +387,7 @@ describe('screencast', suite => {
|
||||
}
|
||||
});
|
||||
|
||||
it('should work for popups', async ({browser, testInfo, server}) => {
|
||||
it('should work for popups', async ({browser, server}, testInfo) => {
|
||||
const videosPath = testInfo.outputPath('');
|
||||
const size = { width: 450, height: 240 };
|
||||
const context = await browser.newContext({
|
||||
@ -419,9 +417,9 @@ describe('screencast', suite => {
|
||||
expect(videoFiles.length).toBe(2);
|
||||
});
|
||||
|
||||
it('should scale frames down to the requested size ', (test, parameters) => {
|
||||
test.fixme(parameters.headful, 'Fails on headful');
|
||||
}, async ({browser, testInfo, server}) => {
|
||||
it('should scale frames down to the requested size ', async ({browser, server, headful}, testInfo) => {
|
||||
it.fixme(headful, 'Fails on headful');
|
||||
|
||||
const context = await browser.newContext({
|
||||
recordVideo: {
|
||||
dir: testInfo.outputPath(''),
|
||||
@ -467,7 +465,7 @@ describe('screencast', suite => {
|
||||
}
|
||||
});
|
||||
|
||||
it('should use viewport scaled down to fit into 800x800 as default size', async ({browser, testInfo}) => {
|
||||
it('should use viewport scaled down to fit into 800x800 as default size', async ({browser}, testInfo) => {
|
||||
const size = {width: 1600, height: 1200};
|
||||
const context = await browser.newContext({
|
||||
recordVideo: {
|
||||
@ -486,7 +484,7 @@ describe('screencast', suite => {
|
||||
expect(videoPlayer.videoHeight).toBe(600);
|
||||
});
|
||||
|
||||
it('should be 800x450 by default', async ({ browser, testInfo }) => {
|
||||
it('should be 800x450 by default', async ({ browser }, testInfo) => {
|
||||
const context = await browser.newContext({
|
||||
recordVideo: {
|
||||
dir: testInfo.outputPath(''),
|
||||
@ -503,9 +501,9 @@ describe('screencast', suite => {
|
||||
expect(videoPlayer.videoHeight).toBe(450);
|
||||
});
|
||||
|
||||
it('should be 800x600 with null viewport', (test, { headful, browserName }) => {
|
||||
test.fixme(browserName === 'firefox' && !headful, 'Fails in headless on bots');
|
||||
}, async ({ browser, testInfo }) => {
|
||||
it('should be 800x600 with null viewport', async ({ browser, headful, browserName }, testInfo) => {
|
||||
it.fixme(browserName === 'firefox' && !headful, 'Fails in headless on bots');
|
||||
|
||||
const context = await browser.newContext({
|
||||
recordVideo: {
|
||||
dir: testInfo.outputPath(''),
|
||||
@ -523,7 +521,7 @@ describe('screencast', suite => {
|
||||
expect(videoPlayer.videoHeight).toBe(600);
|
||||
});
|
||||
|
||||
it('should capture static page in persistent context', async ({launchPersistent, testInfo}) => {
|
||||
it('should capture static page in persistent context', async ({launchPersistent}, testInfo) => {
|
||||
const size = { width: 320, height: 240 };
|
||||
const { context, page } = await launchPersistent({
|
||||
recordVideo: {
|
||||
@ -551,9 +549,9 @@ describe('screencast', suite => {
|
||||
}
|
||||
});
|
||||
|
||||
it('should emulate an iphone', (test, { browserName }) => {
|
||||
test.skip(browserName === 'firefox', 'isMobile is not supported in Firefox');
|
||||
}, async ({contextFactory, playwright, contextOptions, testInfo}) => {
|
||||
it('should emulate an iphone', async ({contextFactory, playwright, contextOptions, browserName}, testInfo) => {
|
||||
it.skip(browserName === 'firefox', 'isMobile is not supported in Firefox');
|
||||
|
||||
const device = playwright.devices['iPhone 6'];
|
||||
const context = await contextFactory({
|
||||
...contextOptions,
|
@ -14,40 +14,45 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { folio } from './fixtures';
|
||||
import { test as it, expect } from './config/browserTest';
|
||||
import { InMemorySnapshotter } from '../lib/server/snapshot/inMemorySnapshotter';
|
||||
import { HttpServer } from '../lib/utils/httpServer';
|
||||
import { SnapshotServer } from '../lib/server/snapshot/snapshotServer';
|
||||
const { it, describe, expect, beforeEach, afterEach } = folio;
|
||||
import type { BrowserContext, Page } from '../index';
|
||||
|
||||
describe('snapshots', (suite, { mode }) => {
|
||||
suite.skip(mode !== 'default');
|
||||
}, () => {
|
||||
it.describe('snapshots', () => {
|
||||
let context: BrowserContext;
|
||||
let page: Page;
|
||||
let snapshotter: any;
|
||||
let httpServer: any;
|
||||
let snapshotPort: number;
|
||||
|
||||
beforeEach(async ({ context, toImpl, testWorkerIndex }) => {
|
||||
it.beforeEach(async ({ mode, toImpl, contextFactory }, testInfo) => {
|
||||
it.skip(mode !== 'default');
|
||||
|
||||
context = await contextFactory();
|
||||
page = await context.newPage();
|
||||
snapshotter = new InMemorySnapshotter(toImpl(context));
|
||||
await snapshotter.initialize();
|
||||
httpServer = new HttpServer();
|
||||
new SnapshotServer(httpServer, snapshotter);
|
||||
snapshotPort = 9700 + testWorkerIndex;
|
||||
snapshotPort = 9700 + testInfo.workerIndex;
|
||||
httpServer.start(snapshotPort);
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
it.afterEach(async () => {
|
||||
await snapshotter.dispose();
|
||||
httpServer.stop();
|
||||
await context.close();
|
||||
});
|
||||
|
||||
it('should collect snapshot', async ({ page, toImpl }) => {
|
||||
it('should collect snapshot', async ({ toImpl }) => {
|
||||
await page.setContent('<button>Hello</button>');
|
||||
const snapshot = await snapshotter.captureSnapshot(toImpl(page), 'snapshot');
|
||||
expect(distillSnapshot(snapshot)).toBe('<BUTTON>Hello</BUTTON>');
|
||||
});
|
||||
|
||||
it('should capture resources', async ({ page, toImpl, server }) => {
|
||||
it('should capture resources', async ({ toImpl, server }) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await page.route('**/style.css', route => {
|
||||
route.fulfill({ body: 'button { color: red; }', }).catch(() => {});
|
||||
@ -59,7 +64,7 @@ describe('snapshots', (suite, { mode }) => {
|
||||
expect(resources[cssHref]).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should collect multiple', async ({ page, toImpl }) => {
|
||||
it('should collect multiple', async ({ toImpl }) => {
|
||||
await page.setContent('<button>Hello</button>');
|
||||
const snapshots = [];
|
||||
snapshotter.on('snapshot', snapshot => snapshots.push(snapshot));
|
||||
@ -68,7 +73,7 @@ describe('snapshots', (suite, { mode }) => {
|
||||
expect(snapshots.length).toBe(2);
|
||||
});
|
||||
|
||||
it('should only collect on change', async ({ page }) => {
|
||||
it('should only collect on change', async ({}) => {
|
||||
await page.setContent('<button>Hello</button>');
|
||||
const snapshots = [];
|
||||
snapshotter.on('snapshot', snapshot => snapshots.push(snapshot));
|
||||
@ -83,7 +88,7 @@ describe('snapshots', (suite, { mode }) => {
|
||||
expect(snapshots.length).toBe(2);
|
||||
});
|
||||
|
||||
it('should respect inline CSSOM change', async ({ page }) => {
|
||||
it('should respect inline CSSOM change', async ({}) => {
|
||||
await page.setContent('<style>button { color: red; }</style><button>Hello</button>');
|
||||
const snapshots = [];
|
||||
snapshotter.on('snapshot', snapshot => snapshots.push(snapshot));
|
||||
@ -102,7 +107,7 @@ describe('snapshots', (suite, { mode }) => {
|
||||
expect(distillSnapshot(snapshots[1])).toBe('<style>button { color: blue; }</style><BUTTON>Hello</BUTTON>');
|
||||
});
|
||||
|
||||
it('should respect subresource CSSOM change', async ({ page, server }) => {
|
||||
it('should respect subresource CSSOM change', async ({ server }) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await page.route('**/style.css', route => {
|
||||
route.fulfill({ body: 'button { color: red; }', }).catch(() => {});
|
||||
@ -129,9 +134,9 @@ describe('snapshots', (suite, { mode }) => {
|
||||
expect(snapshotter.resourceContent(sha1).toString()).toBe('button { color: blue; }');
|
||||
});
|
||||
|
||||
it('should capture iframe', (test, { browserName }) => {
|
||||
test.skip(browserName === 'firefox');
|
||||
}, async ({ contextFactory, page, server, toImpl }) => {
|
||||
it('should capture iframe', async ({ contextFactory, server, toImpl, browserName }) => {
|
||||
it.skip(browserName === 'firefox');
|
||||
|
||||
await page.route('**/empty.html', route => {
|
||||
route.fulfill({
|
||||
body: '<iframe src="iframe.html"></iframe>',
|
||||
@ -170,7 +175,7 @@ describe('snapshots', (suite, { mode }) => {
|
||||
expect(await button.textContent()).toBe('Hello iframe');
|
||||
});
|
||||
|
||||
it('should capture snapshot target', async ({ page, toImpl }) => {
|
||||
it('should capture snapshot target', async ({ toImpl }) => {
|
||||
await page.setContent('<button>Hello</button><button>World</button>');
|
||||
{
|
||||
const handle = await page.$('text=Hello');
|
||||
@ -184,7 +189,7 @@ describe('snapshots', (suite, { mode }) => {
|
||||
}
|
||||
});
|
||||
|
||||
it('should collect on attribute change', async ({ page, toImpl }) => {
|
||||
it('should collect on attribute change', async ({ toImpl }) => {
|
||||
await page.setContent('<button>Hello</button>');
|
||||
{
|
||||
const snapshot = await snapshotter.captureSnapshot(toImpl(page), 'snapshot');
|
@ -13,21 +13,24 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { expect, folio } from './fixtures';
|
||||
import { ElementHandle } from '..';
|
||||
|
||||
import { test as it, expect } from './config/browserTest';
|
||||
import { ElementHandle, Page } from '../index';
|
||||
import type { ServerResponse } from 'http';
|
||||
|
||||
const fixtures = folio.extend();
|
||||
fixtures.page.override(async ({browser}, runTest) => {
|
||||
const page = await browser.newPage({
|
||||
let page: Page;
|
||||
|
||||
it.beforeEach(async ({browser}) => {
|
||||
page = await browser.newPage({
|
||||
hasTouch: true
|
||||
});
|
||||
await runTest(page);
|
||||
});
|
||||
|
||||
it.afterEach(async () => {
|
||||
await page.close();
|
||||
});
|
||||
const { it } = fixtures.build();
|
||||
|
||||
it('should send all of the correct events', async ({page}) => {
|
||||
it('should send all of the correct events', async ({}) => {
|
||||
await page.setContent(`
|
||||
<div id="a" style="background: lightblue; width: 50px; height: 50px">a</div>
|
||||
<div id="b" style="background: pink; width: 50px; height: 50px">b</div>
|
||||
@ -47,7 +50,7 @@ it('should send all of the correct events', async ({page}) => {
|
||||
]);
|
||||
});
|
||||
|
||||
it('should not send mouse events touchstart is canceled', async ({page}) => {
|
||||
it('should not send mouse events touchstart is canceled', async ({}) => {
|
||||
await page.setContent(`<div style="width: 50px; height: 50px; background: red">`);
|
||||
await page.evaluate(() => {
|
||||
// touchstart is not cancelable unless passive is false
|
||||
@ -63,7 +66,7 @@ it('should not send mouse events touchstart is canceled', async ({page}) => {
|
||||
]);
|
||||
});
|
||||
|
||||
it('should not send mouse events when touchend is canceled', async ({page}) => {
|
||||
it('should not send mouse events when touchend is canceled', async ({}) => {
|
||||
await page.setContent(`<div style="width: 50px; height: 50px; background: red">`);
|
||||
await page.evaluate(() => {
|
||||
document.addEventListener('touchend', t => t.preventDefault());
|
||||
@ -78,7 +81,7 @@ it('should not send mouse events when touchend is canceled', async ({page}) => {
|
||||
]);
|
||||
});
|
||||
|
||||
it('should wait for a navigation caused by a tap', async ({server, page}) => {
|
||||
it('should wait for a navigation caused by a tap', async ({server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await page.setContent(`
|
||||
<a href="/intercept-this.html">link</a>;
|
||||
@ -101,7 +104,7 @@ it('should wait for a navigation caused by a tap', async ({server, page}) => {
|
||||
expect(resolved).toBe(true);
|
||||
});
|
||||
|
||||
it('should work with modifiers', async ({page}) => {
|
||||
it('should work with modifiers', async ({}) => {
|
||||
await page.setContent('hello world');
|
||||
const altKeyPromise = page.evaluate(() => new Promise(resolve => {
|
||||
document.addEventListener('touchstart', event => {
|
||||
@ -116,7 +119,7 @@ it('should work with modifiers', async ({page}) => {
|
||||
expect(await altKeyPromise).toBe(true);
|
||||
});
|
||||
|
||||
it('should send well formed touch points', async ({page}) => {
|
||||
it('should send well formed touch points', async ({}) => {
|
||||
const promises = Promise.all([
|
||||
page.evaluate(() => new Promise(resolve => {
|
||||
document.addEventListener('touchstart', event => {
|
||||
@ -168,7 +171,7 @@ it('should send well formed touch points', async ({page}) => {
|
||||
expect(touchend).toEqual([]);
|
||||
});
|
||||
|
||||
it('should wait until an element is visible to tap it', async ({page}) => {
|
||||
it('should wait until an element is visible to tap it', async ({}) => {
|
||||
const div = await page.evaluateHandle(() => {
|
||||
const button = document.createElement('button');
|
||||
button.textContent = 'not clicked';
|
@ -15,7 +15,12 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { it, expect } from './fixtures';
|
||||
import { test as it, expect } from './config/pageTest';
|
||||
import { Server as WebSocketServer } from 'ws';
|
||||
|
||||
it.beforeEach(async () => {
|
||||
it.skip(!!process.env.PW_ANDROID_TESTS);
|
||||
});
|
||||
|
||||
it('should work', async ({ page, server }) => {
|
||||
const value = await page.evaluate(port => {
|
||||
@ -47,7 +52,7 @@ it('should emit close events', async ({ page, server }) => {
|
||||
expect(webSocket.isClosed()).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should emit frame events', async ({ page, server, isFirefox }) => {
|
||||
it('should emit frame events', async ({ page, server }) => {
|
||||
let socketClosed;
|
||||
const socketClosePromise = new Promise(f => socketClosed = f);
|
||||
const log = [];
|
||||
@ -69,7 +74,7 @@ it('should emit frame events', async ({ page, server, isFirefox }) => {
|
||||
expect(log.join(':')).toBe('close:open:received<incoming>:sent<outgoing>');
|
||||
});
|
||||
|
||||
it('should pass self as argument to close event', async ({ page, server, isFirefox }) => {
|
||||
it('should pass self as argument to close event', async ({ page, server }) => {
|
||||
let socketClosed;
|
||||
const socketClosePromise = new Promise(f => socketClosed = f);
|
||||
let webSocket;
|
||||
@ -170,9 +175,10 @@ it('should reject waitForEvent on page close', async ({page, server}) => {
|
||||
expect((await error).message).toContain('Page closed');
|
||||
});
|
||||
|
||||
it('should turn off when offline', test => {
|
||||
test.fixme();
|
||||
}, async ({page, webSocketServer}) => {
|
||||
it('should turn off when offline', async ({page}) => {
|
||||
it.fixme();
|
||||
|
||||
const webSocketServer = new WebSocketServer();
|
||||
const address = webSocketServer.address();
|
||||
const [socket, wsHandle] = await Promise.all([
|
||||
new Promise<import('ws')>(x => webSocketServer.once('connection', x)),
|
||||
@ -195,4 +201,5 @@ it('should turn off when offline', test => {
|
||||
await page.context().setOffline(true);
|
||||
await wsHandle.evaluate(ws => ws.send('if this arrives it failed'));
|
||||
expect(await result).toBe('successfully closed');
|
||||
await new Promise(x => webSocketServer.close(x));
|
||||
});
|
Loading…
Reference in New Issue
Block a user