diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index debb79ca4e..ad1b0c5ac7 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -41,10 +41,10 @@ jobs: env: BROWSER: ${{ matrix.browser }} DEBUG: "*,-pw:wrapped*" - - run: xvfb-run --auto-servernum -- bash -c "ulimit -c unlimited && npm run jest 2>>./testrun.log" + - run: xvfb-run --auto-servernum -- bash -c "ulimit -c unlimited && npm run jest -- --testTimeout=30000" + if: ${{ always() }} env: BROWSER: ${{ matrix.browser }} - DEBUG: "*,-pw:wrapped*" - uses: actions/upload-artifact@v1 if: failure() with: @@ -80,10 +80,10 @@ jobs: env: BROWSER: ${{ matrix.browser }} DEBUG: "*,-pw:wrapped*" - - run: npm run jest 2>>./${{ matrix.browser }}-mac-testrun.log + - run: npm run jest -- --testTimeout=30000 + if: ${{ always() }} env: BROWSER: ${{ matrix.browser }} - DEBUG: "*,-pw:wrapped*" - uses: actions/upload-artifact@v1 if: failure() with: @@ -115,11 +115,11 @@ jobs: env: BROWSER: ${{ matrix.browser }} DEBUG: "*,-pw:wrapped*" - - run: npm run jest 2>>./${{ matrix.browser }}-win-testrun.log + - run: npm run jest -- --testTimeout=30000 + if: ${{ always() }} shell: bash env: BROWSER: ${{ matrix.browser }} - DEBUG: "*,-pw:wrapped*" - uses: actions/upload-artifact@v1 if: failure() with: @@ -170,6 +170,21 @@ jobs: env: DEBUG: "*" HEADLESS: "false" + - run: xvfb-run --auto-servernum -- bash -c "ulimit -c unlimited && npm run jest -- --testTimeout=30000" + if: ${{ always() }} + env: + BROWSER: "chromium" + HEADLESS: "false" + - run: xvfb-run --auto-servernum -- bash -c "ulimit -c unlimited && npm run jest -- --testTimeout=30000" + if: ${{ always() }} + env: + BROWSER: "firefox" + HEADLESS: "false" + - run: xvfb-run --auto-servernum -- bash -c "ulimit -c unlimited && npm run jest -- --testTimeout=30000" + if: ${{ always() }} + env: + BROWSER: "webkit" + HEADLESS: "false" - uses: actions/upload-artifact@v1 if: failure() with: @@ -208,6 +223,11 @@ jobs: BROWSER: ${{ matrix.browser }} DEBUG: "*,-pw:wrapped*" PWCHANNEL: ${{ matrix.transport }} + - run: xvfb-run --auto-servernum -- bash -c "ulimit -c unlimited && npm run jest -- --testTimeout=30000" + if: ${{ always() }} + env: + BROWSER: ${{ matrix.browser }} + PWCHANNEL: ${{ matrix.transport }} - uses: actions/upload-artifact@v1 if: failure() with: diff --git a/jest.config.json b/jest.config.json index 81b19989be..355f4c1649 100644 --- a/jest.config.json +++ b/jest.config.json @@ -1,6 +1,9 @@ { - "rootDir": "./test/jest", - "testEnvironment": ".", - "testMatch": ["**/?(*.)spec.[jt]s"], - "testRunner": "jest-circus/runner" + "rootDir": "./test", + "testEnvironment": "./jest", + "testMatch": ["**/?(*.)jest.[jt]s"], + "testRunner": "jest-circus/runner", + "testTimeout": 10000, + "globalSetup": "./jest/setup.js", + "globalTeardown": "./jest/teardown.js" } diff --git a/package-lock.json b/package-lock.json index d6b0c24843..9192792945 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4872,6 +4872,12 @@ "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", "dev": true }, + "get-stdin": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-5.0.1.tgz", + "integrity": "sha1-Ei4WFZHiH/TFJTAwVpPyDmOTo5g=", + "dev": true + }, "get-stream": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.1.0.tgz", @@ -5008,6 +5014,12 @@ "define-properties": "^1.1.3" } }, + "glur": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/glur/-/glur-1.1.2.tgz", + "integrity": "sha1-8g6jbbEDv8KSNDkh8fkeg8NGdok=", + "dev": true + }, "got": { "version": "9.6.0", "resolved": "https://registry.npmjs.org/got/-/got-9.6.0.tgz", @@ -5067,6 +5079,23 @@ "har-schema": "^2.0.0" } }, + "has-ansi": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", + "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + } + } + }, "has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", @@ -6408,6 +6437,102 @@ } } }, + "jest-image-snapshot": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/jest-image-snapshot/-/jest-image-snapshot-4.0.2.tgz", + "integrity": "sha512-RqKGk0HbQrfw3E3dDuxjFBXqubvPWzjj3zFP6Z7auo48rC0FNpQ5jYXThQjhfy+fvgIgyHk3jPnDNgSiIuVVtw==", + "dev": true, + "requires": { + "chalk": "^1.1.3", + "get-stdin": "^5.0.1", + "glur": "^1.1.2", + "lodash": "^4.17.4", + "mkdirp": "^0.5.1", + "pixelmatch": "^5.1.0", + "pngjs": "^3.4.0", + "rimraf": "^2.6.2" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + }, + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true + }, + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "requires": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + } + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true + }, + "pixelmatch": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/pixelmatch/-/pixelmatch-5.2.1.tgz", + "integrity": "sha512-WjcAdYSnKrrdDdqTcVEY7aB7UhhwjYQKYhHiBXdJef0MOaQeYpUdQ+iVyBLa5YBKS8MPVPPMX7rpOByISLpeEQ==", + "dev": true, + "requires": { + "pngjs": "^4.0.1" + }, + "dependencies": { + "pngjs": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-4.0.1.tgz", + "integrity": "sha512-rf5+2/ioHeQxR6IxuYNYGFytUyG3lma/WW1nsmjeHlWwtb2aByla6dkVc8pmJ9nplzkTA0q2xx7mMWrOTqT4Gg==", + "dev": true + } + } + }, + "pngjs": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-3.4.0.tgz", + "integrity": "sha512-NCrCHhWmnQklfH4MtJMRjZ2a8c80qXeMlQMv2uVp9ISJMTt562SbGd6n2oq0PaPgKm7Z6pL9E2UlLIhC+SHL3w==", + "dev": true + }, + "rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true + } + } + }, "jest-jasmine2": { "version": "26.1.0", "resolved": "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-26.1.0.tgz", diff --git a/package.json b/package.json index 9163965180..5d8a7a4149 100644 --- a/package.json +++ b/package.json @@ -16,10 +16,6 @@ "ftest": "cross-env BROWSER=firefox node --unhandled-rejections=strict test/test.js", "wtest": "cross-env BROWSER=webkit node --unhandled-rejections=strict test/test.js", "test": "cross-env node --unhandled-rejections=strict test/test.js", - "cunit": "cross-env BROWSER=chromium node --unhandled-rejections=strict test/test.js", - "funit": "cross-env BROWSER=firefox node --unhandled-rejections=strict test/test.js", - "wunit": "cross-env BROWSER=webkit node --unhandled-rejections=strict test/test.js", - "unit": "cross-env node --unhandled-rejections=strict test/test.js", "ccoverage": "cross-env COVERAGE=true BROWSER=chromium node --unhandled-rejections=strict test/test.js", "fcoverage": "cross-env COVERAGE=true BROWSER=firefox node --unhandled-rejections=strict test/test.js", "wcoverage": "cross-env COVERAGE=true BROWSER=webkit node --unhandled-rejections=strict test/test.js", @@ -83,6 +79,7 @@ "formidable": "^1.2.1", "jest": "^26.1.0", "jest-circus": "^26.1.0", + "jest-image-snapshot": "^4.0.2", "ncp": "^2.0.0", "node-stream-zip": "^1.8.2", "pirates": "^4.0.1", diff --git a/src/common/utilityScriptSerializers.ts b/src/common/utilityScriptSerializers.ts index 43b5db5273..aa6cbcdd10 100644 --- a/src/common/utilityScriptSerializers.ts +++ b/src/common/utilityScriptSerializers.ts @@ -14,6 +14,14 @@ * limitations under the License. */ +function isRegExp(obj: any): obj is RegExp { + return obj instanceof RegExp || Object.prototype.toString.call(obj) === '[object RegExp]'; +} + +function isDate(obj: any): obj is Date { + return obj instanceof Date || Object.prototype.toString.call(obj) === '[object Date]'; +} + export function parseEvaluationResultValue(value: any, handles: any[] = []): any { if (value === undefined) return undefined; @@ -85,9 +93,9 @@ function serialize(value: any, jsHandleSerializer: (value: any) => { fallThrough } return `${error.name}: ${error.message}\n${error.stack}`; } - if (value instanceof Date) + if (isDate(value)) return { d: value.toJSON() }; - if (value instanceof RegExp) + if (isRegExp(value)) return { r: [ value.source, value.flags ] }; if (Array.isArray(value)) { diff --git a/test/environments.js b/test/environments.js index 7fc2b3c106..0478b8c971 100644 --- a/test/environments.js +++ b/test/environments.js @@ -180,7 +180,6 @@ class PlaywrightEnvironment { if (!this.expectExit) throw new Error(`Server closed with exitCode=${exitCode} signal=${signal}`); }); - process.on('exit', () => this._killProcess()); const transport = new Transport(this.spawnedProcess.stdin, this.spawnedProcess.stdout); connection.onmessage = message => transport.send(JSON.stringify(message)); transport.onmessage = message => connection.dispatch(JSON.parse(message)); @@ -213,20 +212,6 @@ class PlaywrightEnvironment { } delete state.playwright; } - - _killProcess() { - if (this.spawnedProcess && this.spawnedProcess.pid) { - this.expectExit = true; - try { - if (process.platform === 'win32') - childProcess.execSync(`taskkill /pid ${this.spawnedProcess.pid} /T /F`); - else - process.kill(-this.spawnedProcess.pid, 'SIGKILL'); - } catch (e) { - // the process might have already stopped - } - } - } } class BrowserTypeEnvironment { diff --git a/test/evaluation.spec.js b/test/evaluation.jest.js similarity index 97% rename from test/evaluation.spec.js rename to test/evaluation.jest.js index ab3960b807..f44818912d 100644 --- a/test/evaluation.spec.js +++ b/test/evaluation.jest.js @@ -17,7 +17,6 @@ const utils = require('./utils'); const path = require('path'); -const {FFOX, CHROMIUM, WEBKIT, USES_HOOKS, CHANNEL} = utils.testOptions(browserType); describe('Page.evaluate', function() { it('should work', async({page, server}) => { @@ -357,7 +356,7 @@ describe('Page.evaluate', function() { }); expect(result).toEqual([42]); }); - it.fail(WEBKIT)('should not throw an error when evaluation does a synchronous navigation and returns an object', async({page, server}) => { + (WEBKIT ? it.skip : it)('should not throw an error when evaluation does a synchronous navigation and returns an object', async({page, server}) => { // It is imporant to be on about:blank for sync reload. const result = await page.evaluate(() => { window.location.reload(); @@ -373,7 +372,7 @@ describe('Page.evaluate', function() { }); expect(result).toBe(undefined); }); - it.slow().skip(CHANNEL)('should transfer 100Mb of data from page to node.js', async({page, server}) => { + (CHANNEL ? it.skip : it)('should transfer 100Mb of data from page to node.js', async({page}) => { const a = await page.evaluate(() => Array(100 * 1024 * 1024 + 1).join('a')); expect(a.length).toBe(100 * 1024 * 1024); }); @@ -389,7 +388,7 @@ describe('Page.evaluate', function() { const result = await page.evaluate(() => ({abc: 123})); expect(result).toEqual({abc: 123}); }); - it.fail(FFOX)('should await promise from popup', async function({page, server}) { + (FFOX ? it.skip : it)('should await promise from popup', async ({page, server}) => { // Something is wrong about the way Firefox waits for the chained promise await page.goto(server.EMPTY_PAGE); const result = await page.evaluate(() => { @@ -574,14 +573,14 @@ describe('Frame.evaluate', function() { else expect(pageImpl._delegate._contextIdToContext.size).toBe(count); } - it.skip(USES_HOOKS)('should dispose context on navigation', async({page, server, toImpl}) => { + (USES_HOOKS ? it.skip : it)('should dispose context on navigation', async({page, server, toImpl}) => { await page.goto(server.PREFIX + '/frames/one-frame.html'); expect(page.frames().length).toBe(2); expectContexts(toImpl(page), 4); await page.goto(server.EMPTY_PAGE); expectContexts(toImpl(page), 2); }); - it.skip(USES_HOOKS)('should dispose context on cross-origin navigation', async({page, server, toImpl}) => { + (USES_HOOKS ? it.skip : it)('should dispose context on cross-origin navigation', async({page, server, toImpl}) => { await page.goto(server.PREFIX + '/frames/one-frame.html'); expect(page.frames().length).toBe(2); expectContexts(toImpl(page), 4); diff --git a/test/jest/fixtures.js b/test/jest/fixtures.js new file mode 100644 index 0000000000..fdc808cd13 --- /dev/null +++ b/test/jest/fixtures.js @@ -0,0 +1,132 @@ +/** + * Copyright Microsoft Corporation. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +const path = require('path'); +const childProcess = require('child_process'); + +const playwright = require('../../index'); +const { TestServer } = require('../../utils/testserver/'); +const { DispatcherConnection } = require('../../lib/rpc/server/dispatcher'); +const { Connection } = require('../../lib/rpc/client/connection'); +const { Transport } = require('../../lib/rpc/transport'); +const { PlaywrightDispatcher } = require('../../lib/rpc/server/playwrightDispatcher'); +const { setUseApiName } = require('../../lib/progress'); + +module.exports = function registerFixtures(global) { + + global.registerWorkerFixture('server', async ({}, test) => { + const assetsPath = path.join(__dirname, '..', 'assets'); + const cachedPath = path.join(__dirname, '..', 'assets', 'cached'); + + const port = 8907 + (process.env.JEST_WORKER_ID - 1) * 2; + const server = await TestServer.create(assetsPath, port); + server.enableHTTPCache(cachedPath); + server.PORT = port; + server.PREFIX = `http://localhost:${port}`; + server.CROSS_PROCESS_PREFIX = `http://127.0.0.1:${port}`; + server.EMPTY_PAGE = `http://localhost:${port}/empty.html`; + + const httpsPort = port + 1; + httpsServer = await TestServer.createHTTPS(assetsPath, httpsPort); + httpsServer.enableHTTPCache(cachedPath); + httpsServer.PORT = httpsPort; + httpsServer.PREFIX = `https://localhost:${httpsPort}`; + httpsServer.CROSS_PROCESS_PREFIX = `https://127.0.0.1:${httpsPort}`; + httpsServer.EMPTY_PAGE = `https://localhost:${httpsPort}/empty.html`; + + await test(server); + + await Promise.all([ + server.stop(), + httpsServer.stop(), + ]); + }); + + global.registerWorkerFixture('playwright', async({}, test) => { + if (process.env.PWCHANNEL) { + setUseApiName(false); + const connection = new Connection(); + let toImpl; + let spawnedProcess; + let expectExit; + if (process.env.PWCHANNEL === 'wire') { + spawnedProcess = childProcess.fork(path.join(__dirname, '..', '..', 'lib', 'rpc', 'server'), [], { + stdio: 'pipe', + detached: process.platform !== 'win32', + }); + spawnedProcess.once('exit', (exitCode, signal) => { + spawnedProcess = undefined; + if (!expectExit) + throw new Error(`Server closed with exitCode=${exitCode} signal=${signal}`); + }); + const transport = new Transport(spawnedProcess.stdin, spawnedProcess.stdout); + connection.onmessage = message => transport.send(JSON.stringify(message)); + transport.onmessage = message => connection.dispatch(JSON.parse(message)); + } else { + const dispatcherConnection = new DispatcherConnection(); + dispatcherConnection.onmessage = async message => { + setImmediate(() => connection.dispatch(message)); + }; + connection.onmessage = async message => { + const result = await dispatcherConnection.dispatch(message); + await new Promise(f => setImmediate(f)); + return result; + }; + new PlaywrightDispatcher(dispatcherConnection.rootDispatcher(), playwright); + toImpl = x => dispatcherConnection._dispatchers.get(x._guid)._object; + } + + const playwrightObject = await connection.waitForObjectWithKnownName('playwright'); + playwrightObject.toImpl = toImpl; + await test(playwrightObject); + + if (spawnedProcess) { + const exited = new Promise(f => spawnedProcess.once('exit', f)); + expectExit = true; + spawnedProcess.kill(); + await exited; + } + return; + } + playwright.toImpl = x => x; + await test(playwright); + }); + + global.registerFixture('toImpl', async ({playwright}, test) => { + await test(playwright.toImpl); + }); + + global.registerWorkerFixture('browserType', async ({playwright}, test) => { + await test(playwright[process.env.BROWSER || 'chromium']); + }); + + global.registerWorkerFixture('browser', async ({browserType}, test) => { + const browser = await browserType.launch({ headless: !!global.HEADLESS }); + await test(browser); + await browser.close(); + }); + + global.registerFixture('context', async ({browser}, test) => { + const context = await browser.newContext(); + await test(context); + await context.close(); + }); + + global.registerFixture('page', async ({context}, test) => { + const page = await context.newPage(); + await test(page); + }); +} diff --git a/test/jest/playwrightEnvironment.js b/test/jest/playwrightEnvironment.js index f9058fd012..889e2d8633 100644 --- a/test/jest/playwrightEnvironment.js +++ b/test/jest/playwrightEnvironment.js @@ -15,38 +15,26 @@ */ const NodeEnvironment = require('jest-environment-node'); -const path = require('path'); -const playwright = require('../../index'); +const registerFixtures = require('./fixtures'); class PlaywrightEnvironment extends NodeEnvironment { constructor(config, context) { super(config, context); this.fixturePool = new FixturePool(); + this.global.CHROMIUM = process.env.BROWSER === 'chromium' || !process.env.BROWSER; + this.global.FFOX = process.env.BROWSER === 'firefox'; + this.global.WEBKIT = process.env.BROWSER === 'webkit'; + this.global.USES_HOOKS = process.env.PWCHANNEL === 'wire'; + this.global.CHANNEL = !!process.env.PWCHANNEL; + this.global.HEADLESS = !!valueFromEnv('HEADLESS', true); this.global.registerFixture = (name, fn) => { this.fixturePool.registerFixture(name, 'test', fn); }; - this.global.registerWorkerFixture = (name, fn) => { this.fixturePool.registerFixture(name, 'worker', fn); }; - - this.global.registerWorkerFixture('browser', async (test) => { - const browser = await playwright[process.env.BROWSER || 'chromium'].launch(); - await test(browser); - await browser.close(); - }); - - this.global.registerFixture('context', async (browser, test) => { - const context = await browser.newContext(); - await test(context); - await context.close(); - }); - - this.global.registerFixture('page', async (context, test) => { - const page = await context.newPage(); - await test(page); - }); + registerFixtures(this.global); } async setup() { @@ -93,16 +81,20 @@ class Fixture { this.pool.instances.get(name).usages.add(this.name); } - const params = this.deps.map(n => this.pool.instances.get(n).value); - let setupFenceCallback; - const setupFence = new Promise(f => setupFenceCallback = f); + const params = {}; + for (const n of this.deps) + params[n] = this.pool.instances.get(n).value; + let setupFenceFulfill; + let setupFenceReject; + const setupFence = new Promise((f, r) => { setupFenceFulfill = f; setupFenceReject = r; }); const teardownFence = new Promise(f => this._teardownFenceCallback = f); - this._tearDownComplete = this.fn(...params, async value => { + this._tearDownComplete = this.fn(params, async value => { this.value = value; - setupFenceCallback(); + setupFenceFulfill(); await teardownFence; - }); + }).catch(e => setupFenceReject(e)); await setupFence; + this._setup = true; } async teardown() { @@ -115,7 +107,8 @@ class Fixture { continue; await fixture.teardown(); } - this._teardownFenceCallback(); + if (this._setup) + this._teardownFenceCallback(); await this._tearDownComplete; this.pool.instances.delete(this.name); } @@ -136,6 +129,8 @@ class FixturePool { if (fixture) return fixture; + if (!this.registrations.has(name)) + throw new Error('Unknown fixture: ' + name); const { scope, fn } = this.registrations.get(name); fixture = new Fixture(this, name, scope, fn); this.instances.set(name, fixture); @@ -154,7 +149,10 @@ class FixturePool { const names = fixtureParameterNames(fn); for (const name of names) await this.setupFixture(name); - await fn(...names.map(n => this.instances.get(n).value)); + const params = {}; + for (const n of names) + params[n] = this.instances.get(n).value; + await fn(params); } } @@ -163,14 +161,15 @@ exports.default = exports.getPlaywrightEnv(); function fixtureParameterNames(fn) { const text = fn.toString(); - const match = text.match(/async\ (.*) =>/); - if (!match) + const match = text.match(/async\s*\(\s*{\s*([^}]*)\s*}/); + if (!match || !match[1].trim()) return []; let signature = match[1]; - if (signature.startsWith('(') && signature.endsWith(')')) - signature = signature.substring(1, signature.length - 1); - if (!signature.trim()) - return []; - const result = signature.split(',').map(t => t.trim()); - return result.filter(s => s !== 'test'); + return signature.split(',').map(t => t.trim()); +} + +function valueFromEnv(name, defaultValue) { + if (!(name in process.env)) + return defaultValue; + return JSON.parse(process.env[name]); } diff --git a/test/jest/test.spec.js b/test/jest/setup.js similarity index 70% rename from test/jest/test.spec.js rename to test/jest/setup.js index a870fbb70e..18956a616e 100644 --- a/test/jest/test.spec.js +++ b/test/jest/setup.js @@ -14,12 +14,5 @@ * limitations under the License. */ -test('test 1', async (page, context) => { - expect(page.url()).toBe('about:blank'); - expect(page.context()).toEqual(context); -}); - -test('test 2', async (page, context) => { - expect(page.url()).toBe('about:blank'); - expect(page.context()).toEqual(context); -}); +module.exports = async function setup() { +}; diff --git a/test/jest/teardown.js b/test/jest/teardown.js new file mode 100644 index 0000000000..747281ceb3 --- /dev/null +++ b/test/jest/teardown.js @@ -0,0 +1,18 @@ +/** + * Copyright Microsoft Corporation. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +module.exports = async function teardown() { +}; diff --git a/test/test.config.js b/test/test.config.js index 2e7f723028..d7d3562275 100644 --- a/test/test.config.js +++ b/test/test.config.js @@ -81,7 +81,6 @@ module.exports = { './download.spec.js', './elementhandle.spec.js', './emulation.spec.js', - './evaluation.spec.js', './frame.spec.js', './focus.spec.js', './input.spec.js',