diff --git a/src/browserContext.ts b/src/browserContext.ts index 28da982edd..8ed4755da9 100644 --- a/src/browserContext.ts +++ b/src/browserContext.ts @@ -30,7 +30,7 @@ import { ProgressController } from './progress'; import { DebugController } from './debug/debugController'; import { LoggerSink } from './loggerSink'; -export interface BrowserContext { +export interface BrowserContext extends EventEmitter { setDefaultNavigationTimeout(timeout: number): void; setDefaultTimeout(timeout: number): void; pages(): Page[]; diff --git a/test/chromium-js-coverage.spec.js b/test/chromium-js-coverage.spec.ts similarity index 99% rename from test/chromium-js-coverage.spec.js rename to test/chromium-js-coverage.spec.ts index 3d34b9383f..26fe36c768 100644 --- a/test/chromium-js-coverage.spec.js +++ b/test/chromium-js-coverage.spec.ts @@ -14,6 +14,7 @@ * limitations under the License. */ +import utils from './utils'; const {FFOX, CHROMIUM, WEBKIT} = testOptions; it.skip(CHROMIUM)('should be missing', async function({page, server}) { diff --git a/test/chromium/oopif.spec.js b/test/chromium/oopif.spec.ts similarity index 92% rename from test/chromium/oopif.spec.js rename to test/chromium/oopif.spec.ts index 547cdab37e..0df33e73a3 100644 --- a/test/chromium/oopif.spec.js +++ b/test/chromium/oopif.spec.ts @@ -13,9 +13,17 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +import { Page, Browser, BrowserContext } from '../../types/types'; const {FFOX, CHROMIUM, WEBKIT, CHANNEL} = testOptions; +declare global { + interface FixtureState { + sppBrowser: Browser; + sppContext: BrowserContext; + sppPage: Page; + } +} registerFixture('sppBrowser', async ({browserType, defaultBrowserOptions}, test) => { const browser = await browserType.launch({ ...defaultBrowserOptions, @@ -76,13 +84,13 @@ it.skip(!CHROMIUM)('should handle remote -> local -> remote transitions', async expect(await page.frames()[1].evaluate(() => '' + location.href)).toBe(server.CROSS_PROCESS_PREFIX + '/grid.html'); await Promise.all([ page.frames()[1].waitForNavigation(), - page.evaluate(() => goLocal()), + page.evaluate('goLocal()'), ]); expect(await page.frames()[1].evaluate(() => '' + location.href)).toBe(server.PREFIX + '/grid.html'); expect(await countOOPIFs(browser)).toBe(0); await Promise.all([ page.frames()[1].waitForNavigation(), - page.evaluate(() => goRemote()), + page.evaluate('goRemote()'), ]); expect(await page.frames()[1].evaluate(() => '' + location.href)).toBe(server.CROSS_PROCESS_PREFIX + '/grid.html'); expect(await countOOPIFs(browser)).toBe(1); @@ -118,7 +126,7 @@ it.skip(!CHROMIUM)('should expose function', async({sppBrowser, sppPage, server} const oopif = page.frames()[1]; await page.exposeFunction('mul', (a, b) => a * b); const result = await oopif.evaluate(async function() { - return await mul(9, 4); + return await window['mul'](9, 4); }); expect(result).toBe(36); }); @@ -252,25 +260,25 @@ it.skip(!CHROMIUM)('should support exposeFunction', async function({sppBrowser, await page.goto(server.PREFIX + '/dynamic-oopif.html'); expect(await countOOPIFs(browser)).toBe(1); expect(page.frames().length).toBe(2); - expect(await page.frames()[0].evaluate(() => inc(3))).toBe(4); - expect(await page.frames()[1].evaluate(() => inc(4))).toBe(5); - expect(await page.frames()[0].evaluate(() => dec(3))).toBe(2); - expect(await page.frames()[1].evaluate(() => dec(4))).toBe(3); + expect(await page.frames()[0].evaluate(() => window['inc'](3))).toBe(4); + expect(await page.frames()[1].evaluate(() => window['inc'](4))).toBe(5); + expect(await page.frames()[0].evaluate(() => window['dec'](3))).toBe(2); + expect(await page.frames()[1].evaluate(() => window['dec'](4))).toBe(3); }); it.skip(!CHROMIUM)('should support addInitScript', async function({sppBrowser, sppContext, sppPage, server}) { const browser = sppBrowser; const context = sppContext; const page = sppPage; - await context.addInitScript(() => window.bar = 17); - await page.addInitScript(() => window.foo = 42); + await 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); expect(page.frames().length).toBe(2); - expect(await page.frames()[0].evaluate(() => window.foo)).toBe(42); - expect(await page.frames()[1].evaluate(() => window.foo)).toBe(42); - expect(await page.frames()[0].evaluate(() => window.bar)).toBe(17); - expect(await page.frames()[1].evaluate(() => window.bar)).toBe(17); + expect(await page.frames()[0].evaluate(() => window['foo'])).toBe(42); + expect(await page.frames()[1].evaluate(() => window['foo'])).toBe(42); + expect(await page.frames()[0].evaluate(() => window['bar'])).toBe(17); + expect(await page.frames()[1].evaluate(() => window['bar'])).toBe(17); }); // @see https://github.com/microsoft/playwright/issues/1240 it.skip(!CHROMIUM)('should click a button when it overlays oopif', async function({sppBrowser, sppPage, server}) { @@ -279,7 +287,7 @@ it.skip(!CHROMIUM)('should click a button when it overlays oopif', async functio await page.goto(server.PREFIX + '/button-overlay-oopif.html'); expect(await countOOPIFs(browser)).toBe(1); await page.click('button'); - expect(await page.evaluate(() => window.BUTTON_CLICKED)).toBe(true); + expect(await page.evaluate(() => window['BUTTON_CLICKED'])).toBe(true); }); it.skip(!CHROMIUM)('should report google.com frame with headful', async({browserType, defaultBrowserOptions, server}) => { @@ -325,7 +333,7 @@ it.skip(!CHROMIUM)('ElementHandle.boundingBox() should work', async function({sp await Promise.all([ page.frames()[1].waitForNavigation(), - page.evaluate(() => goLocal()), + page.evaluate('goLocal()'), ]); expect(await countOOPIFs(browser)).toBe(0); const handle2 = await page.frames()[1].$('.box:nth-of-type(13)'); @@ -346,9 +354,9 @@ it.skip(!CHROMIUM)('should click', async function({sppBrowser, sppPage, server}) expect(await countOOPIFs(browser)).toBe(1); const handle1 = await page.frames()[1].$('.box:nth-of-type(13)'); - await handle1.evaluate(div => div.addEventListener('click', () => window._clicked = true, false)); + await handle1.evaluate(div => div.addEventListener('click', () => window['_clicked'] = true, false)); await handle1.click(); - expect(await handle1.evaluate(() => window._clicked)).toBe(true); + expect(await handle1.evaluate(() => window['_clicked'])).toBe(true); }); async function countOOPIFs(browser) { diff --git a/test/chromium/tracing.spec.js b/test/chromium/tracing.spec.ts similarity index 61% rename from test/chromium/tracing.spec.js rename to test/chromium/tracing.spec.ts index 51c6f81c37..2346299003 100644 --- a/test/chromium/tracing.spec.js +++ b/test/chromium/tracing.spec.ts @@ -14,10 +14,15 @@ * limitations under the License. */ -const fs = require('fs'); -const path = require('path'); +import fs from 'fs'; +import path from 'path'; +import { ChromiumBrowser } from '../..'; const {FFOX, CHROMIUM, WEBKIT, OUTPUT_DIR, CHANNEL} = testOptions; - +declare global { + interface FixtureState { + outputFile: string; + } +} registerFixture('outputFile', async ({parallelIndex}, test) => { const outputFile = path.join(OUTPUT_DIR, `trace-${parallelIndex}.json`); await test(outputFile); @@ -26,48 +31,48 @@ registerFixture('outputFile', async ({parallelIndex}, test) => { }); it.skip(!CHROMIUM)('should output a trace', async({browser, page, server, outputFile}) => { - await browser.startTracing(page, {screenshots: true, path: outputFile}); + await (browser as ChromiumBrowser).startTracing(page, {screenshots: true, path: outputFile}); await page.goto(server.PREFIX + '/grid.html'); - await browser.stopTracing(); + await (browser as ChromiumBrowser).stopTracing(); expect(fs.existsSync(outputFile)).toBe(true); }); it.skip(!CHROMIUM)('should run with custom categories if provided', async({browser, page, outputFile}) => { - await browser.startTracing(page, {path: outputFile, categories: ['disabled-by-default-v8.cpu_profiler.hires']}); - await browser.stopTracing(); + await (browser as ChromiumBrowser).startTracing(page, {path: outputFile, categories: ['disabled-by-default-v8.cpu_profiler.hires']}); + await (browser as ChromiumBrowser).stopTracing(); const traceJson = JSON.parse(fs.readFileSync(outputFile).toString()); - expect(traceJson.metadata['trace-config']).toContain('disabled-by-default-v8.cpu_profiler.hires', 'Does not contain expected category'); + expect(traceJson.metadata['trace-config']).toContain('disabled-by-default-v8.cpu_profiler.hires'); }); it.skip(!CHROMIUM)('should throw if tracing on two pages', async({browser, page, outputFile}) => { - await browser.startTracing(page, {path: outputFile}); + await (browser as ChromiumBrowser).startTracing(page, {path: outputFile}); const newPage = await browser.newPage(); let error = null; - await browser.startTracing(newPage, {path: outputFile}).catch(e => error = e); + await (browser as ChromiumBrowser).startTracing(newPage, {path: outputFile}).catch(e => error = e); await newPage.close(); expect(error).toBeTruthy(); - await browser.stopTracing(); + await (browser as ChromiumBrowser).stopTracing(); }); it.skip(!CHROMIUM)('should return a buffer', async({browser, page, server, outputFile}) => { - await browser.startTracing(page, {screenshots: true, path: outputFile}); + await (browser as ChromiumBrowser).startTracing(page, {screenshots: true, path: outputFile}); await page.goto(server.PREFIX + '/grid.html'); - const trace = await browser.stopTracing(); + const trace = await (browser as ChromiumBrowser).stopTracing(); const buf = fs.readFileSync(outputFile); - expect(trace.toString()).toEqual(buf.toString(), 'Tracing buffer mismatch'); + expect(trace.toString()).toEqual(buf.toString()); }); it.skip(!CHROMIUM)('should work without options', async({browser, page, server}) => { - await browser.startTracing(page); + await (browser as ChromiumBrowser).startTracing(page); await page.goto(server.PREFIX + '/grid.html'); - const trace = await browser.stopTracing(); + const trace = await (browser as ChromiumBrowser).stopTracing(); expect(trace).toBeTruthy(); }); it.skip(!CHROMIUM)('should support a buffer without a path', async({browser, page, server}) => { - await browser.startTracing(page, {screenshots: true}); + await (browser as ChromiumBrowser).startTracing(page, {screenshots: true}); await page.goto(server.PREFIX + '/grid.html'); - const trace = await browser.stopTracing(); - expect(trace.toString()).toContain('screenshot', 'Does not contain screenshot'); + const trace = await (browser as ChromiumBrowser).stopTracing(); + expect(trace.toString()).toContain('screenshot'); }); diff --git a/test/click-react.spec.ts b/test/click-react.spec.ts new file mode 100644 index 0000000000..ec51d3e4d0 --- /dev/null +++ b/test/click-react.spec.ts @@ -0,0 +1,120 @@ +/** + * Copyright 2018 Google Inc. All rights reserved. + * Modifications copyright (c) Microsoft Corporation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import utils from './utils'; + +declare const renderComponent; +declare const e; +declare const MyButton; + +it.fail(true)('should report that selector does not match anymore', async ({page, server}) => { + await page.goto(server.PREFIX + '/react.html'); + await page.evaluate(() => { + renderComponent(e('div', {}, [e(MyButton, { name: 'button1' }), e(MyButton, { name: 'button2' })] )); + }); + const __testHookAfterStable = () => page.evaluate(() => { + window['counter'] = (window['counter'] || 0) + 1; + if (window['counter'] === 1) + renderComponent(e('div', {}, [e(MyButton, { name: 'button2' }), e(MyButton, { name: 'button1' })] )); + else + renderComponent(e('div', {}, [])); + }); + const error = await page.dblclick('text=button1', { __testHookAfterStable, timeout: 3000 } as any).catch(e => e); + expect(await page.evaluate('window.button1')).toBe(undefined); + expect(await page.evaluate('window.button2')).toBe(undefined); + expect(error.message).toContain('page.dblclick: Timeout 3000ms exceeded.'); + expect(error.message).toContain('element does not match the selector anymore'); +}); + +it.fail(true)('should not retarget the handle when element is recycled', async ({page, server}) => { + await page.goto(server.PREFIX + '/react.html'); + await page.evaluate(() => { + renderComponent(e('div', {}, [e(MyButton, { name: 'button1' }), e(MyButton, { name: 'button2', disabled: true })] )); + }); + const __testHookBeforeStable = () => page.evaluate(() => { + window['counter'] = (window['counter'] || 0) + 1; + if (window['counter'] === 1) + renderComponent(e('div', {}, [e(MyButton, { name: 'button2', disabled: true }), e(MyButton, { name: 'button1' })] )); + }); + const handle = await page.$('text=button1'); + const error = await handle.click({ __testHookBeforeStable, timeout: 3000 } as any).catch(e => e); + expect(await page.evaluate('window.button1')).toBe(undefined); + expect(await page.evaluate('window.button2')).toBe(undefined); + expect(error.message).toContain('elementHandle.click: Timeout 3000ms exceeded.'); + expect(error.message).toContain('element is disabled - waiting'); +}); + +it('should timeout when click opens alert', async({page, server}) => { + const dialogPromise = page.waitForEvent('dialog'); + await page.setContent(`
Click me
`); + const error = await page.click('div', { timeout: 3000 }).catch(e => e); + expect(error.message).toContain('page.click: Timeout 3000ms exceeded.'); + const dialog = await dialogPromise; + await dialog.dismiss(); +}); + +it.fail(true)('should retarget when element is recycled during hit testing', async ({page, server}) => { + await page.goto(server.PREFIX + '/react.html'); + await page.evaluate(() => { + renderComponent(e('div', {}, [e(MyButton, { name: 'button1' }), e(MyButton, { name: 'button2' })] )); + }); + const __testHookAfterStable = () => page.evaluate(() => { + window['counter'] = (window['counter'] || 0) + 1; + if (window['counter'] === 1) + renderComponent(e('div', {}, [e(MyButton, { name: 'button2' }), e(MyButton, { name: 'button1' })] )); + }); + await page.click('text=button1', { __testHookAfterStable } as any); + expect(await page.evaluate('window.button1')).toBe(true); + expect(await page.evaluate('window.button2')).toBe(undefined); +}); + +it.fail(true)('should retarget when element is recycled before enabled check', async ({page, server}) => { + await page.goto(server.PREFIX + '/react.html'); + await page.evaluate(() => { + renderComponent(e('div', {}, [e(MyButton, { name: 'button1' }), e(MyButton, { name: 'button2', disabled: true })] )); + }); + const __testHookBeforeStable = () => page.evaluate(() => { + window['counter'] = (window['counter'] || 0) + 1; + if (window['counter'] === 1) + renderComponent(e('div', {}, [e(MyButton, { name: 'button2', disabled: true }), e(MyButton, { name: 'button1' })] )); + }); + await page.click('text=button1', { __testHookBeforeStable } as any); + expect(await page.evaluate('window.button1')).toBe(true); + expect(await page.evaluate('window.button2')).toBe(undefined); +}); + +it('should not retarget when element changes on hover', async ({page, server}) => { + await page.goto(server.PREFIX + '/react.html'); + await page.evaluate(() => { + renderComponent(e('div', {}, [e(MyButton, { name: 'button1', renameOnHover: true }), e(MyButton, { name: 'button2' })] )); + }); + await page.click('text=button1'); + expect(await page.evaluate('window.button1')).toBe(true); + expect(await page.evaluate('window.button2')).toBe(undefined); +}); + +it('should not retarget when element is recycled on hover', async ({page, server}) => { + await page.goto(server.PREFIX + '/react.html'); + await page.evaluate(() => { + function shuffle() { + renderComponent(e('div', {}, [e(MyButton, { name: 'button2' }), e(MyButton, { name: 'button1' })] )); + } + renderComponent(e('div', {}, [e(MyButton, { name: 'button1', onHover: shuffle }), e(MyButton, { name: 'button2' })] )); + }); + await page.click('text=button1'); + expect(await page.evaluate('window.button1')).toBe(undefined); + expect(await page.evaluate('window.button2')).toBe(true); +}); diff --git a/test/click-timeout-1.spec.js b/test/click-timeout-1.spec.ts similarity index 89% rename from test/click-timeout-1.spec.js rename to test/click-timeout-1.spec.ts index 00e1321cd9..ac2634a555 100644 --- a/test/click-timeout-1.spec.js +++ b/test/click-timeout-1.spec.ts @@ -15,20 +15,21 @@ * limitations under the License. */ +import utils from './utils'; const {USES_HOOKS} = testOptions; it.skip(USES_HOOKS)('should avoid side effects after timeout', async({page, server}) => { await page.goto(server.PREFIX + '/input/button.html'); - const error = await page.click('button', { timeout: 2000, __testHookBeforePointerAction: () => new Promise(f => setTimeout(f, 2500))}).catch(e => e); + const error = await page.click('button', { timeout: 2000, __testHookBeforePointerAction: () => new Promise(f => setTimeout(f, 2500))} as any).catch(e => e); await page.waitForTimeout(5000); // Give it some time to click after the test hook is done waiting. - expect(await page.evaluate(() => result)).toBe('Was not clicked'); + expect(await page.evaluate('result')).toBe('Was not clicked'); expect(error.message).toContain('page.click: Timeout 2000ms exceeded.'); }); it('should timeout waiting for button to be enabled', async({page, server}) => { await page.setContent(''); const error = await page.click('text=Click target', { timeout: 3000 }).catch(e => e); - expect(await page.evaluate(() => window.__CLICKED)).toBe(undefined); + expect(await page.evaluate('window.__CLICKED')).toBe(undefined); expect(error.message).toContain('page.click: Timeout 3000ms exceeded.'); expect(error.message).toContain('element is disabled - waiting'); }); diff --git a/test/click-timeout-2.spec.js b/test/click-timeout-2.spec.ts similarity index 98% rename from test/click-timeout-2.spec.js rename to test/click-timeout-2.spec.ts index c0cc6d2194..857acec095 100644 --- a/test/click-timeout-2.spec.js +++ b/test/click-timeout-2.spec.ts @@ -15,6 +15,7 @@ * limitations under the License. */ +import utils from './utils'; const {USES_HOOKS} = testOptions; it('should timeout waiting for display:none to be gone', async({page, server}) => { diff --git a/test/click-timeout-3.spec.js b/test/click-timeout-3.spec.ts similarity index 92% rename from test/click-timeout-3.spec.js rename to test/click-timeout-3.spec.ts index 1fa192a576..d8636f4487 100644 --- a/test/click-timeout-3.spec.js +++ b/test/click-timeout-3.spec.ts @@ -15,6 +15,7 @@ * limitations under the License. */ +import utils from './utils'; const {USES_HOOKS} = testOptions; it.skip(USES_HOOKS)('should fail when element jumps during hit testing', async({page, server}) => { @@ -22,13 +23,13 @@ it.skip(USES_HOOKS)('should fail when element jumps during hit testing', async({ let clicked = false; const handle = await page.$('button'); const __testHookBeforeHitTarget = () => page.evaluate(() => { - const margin = parseInt(document.querySelector('button').style.marginLeft || 0) + 100; + const margin = parseInt(document.querySelector('button').style.marginLeft || '0') + 100; document.querySelector('button').style.marginLeft = margin + 'px'; }); - const promise = handle.click({ timeout: 5000, __testHookBeforeHitTarget }).then(() => clicked = true).catch(e => e); + const promise = handle.click({ timeout: 5000, __testHookBeforeHitTarget } as any).then(() => clicked = true).catch(e => e); const error = await promise; expect(clicked).toBe(false); - expect(await page.evaluate(() => window.clicked)).toBe(undefined); + expect(await page.evaluate('window.clicked')).toBe(undefined); expect(error.message).toContain('elementHandle.click: Timeout 5000ms exceeded.'); expect(error.message).toContain('element does not receive pointer events'); expect(error.message).toContain('retrying click action'); diff --git a/test/click-timeout-4.spec.js b/test/click-timeout-4.spec.ts similarity index 97% rename from test/click-timeout-4.spec.js rename to test/click-timeout-4.spec.ts index 7b83fa3fcb..7ce06aab6e 100644 --- a/test/click-timeout-4.spec.js +++ b/test/click-timeout-4.spec.ts @@ -14,6 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +import utils from './utils'; it('should timeout waiting for stable position', async({page, server}) => { await page.goto(server.PREFIX + '/input/button.html'); diff --git a/test/click-timeout-5.spec.js b/test/click-timeout-5.spec.js deleted file mode 100644 index 25e8b3ac09..0000000000 --- a/test/click-timeout-5.spec.js +++ /dev/null @@ -1,62 +0,0 @@ -/** - * Copyright 2018 Google Inc. All rights reserved. - * Modifications copyright (c) Microsoft Corporation. - * - * 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. - */ - -it.fail(true)('should report that selector does not match anymore', async ({page, server}) => { - await page.goto(server.PREFIX + '/react.html'); - await page.evaluate(() => { - renderComponent(e('div', {}, [e(MyButton, { name: 'button1' }), e(MyButton, { name: 'button2' })] )); - }); - const __testHookAfterStable = () => page.evaluate(() => { - window.counter = (window.counter || 0) + 1; - if (window.counter === 1) - renderComponent(e('div', {}, [e(MyButton, { name: 'button2' }), e(MyButton, { name: 'button1' })] )); - else - renderComponent(e('div', {}, [])); - }); - const error = await page.dblclick('text=button1', { __testHookAfterStable, timeout: 3000 }).catch(e => e); - expect(await page.evaluate(() => window.button1)).toBe(undefined); - expect(await page.evaluate(() => window.button2)).toBe(undefined); - expect(error.message).toContain('page.dblclick: Timeout 3000ms exceeded.'); - expect(error.message).toContain('element does not match the selector anymore'); -}); - -it.fail(true)('should not retarget the handle when element is recycled', async ({page, server}) => { - await page.goto(server.PREFIX + '/react.html'); - await page.evaluate(() => { - renderComponent(e('div', {}, [e(MyButton, { name: 'button1' }), e(MyButton, { name: 'button2', disabled: true })] )); - }); - const __testHookBeforeStable = () => page.evaluate(() => { - window.counter = (window.counter || 0) + 1; - if (window.counter === 1) - renderComponent(e('div', {}, [e(MyButton, { name: 'button2', disabled: true }), e(MyButton, { name: 'button1' })] )); - }); - const handle = await page.$('text=button1'); - const error = await handle.click({ __testHookBeforeStable, timeout: 3000 }).catch(e => e); - expect(await page.evaluate(() => window.button1)).toBe(undefined); - expect(await page.evaluate(() => window.button2)).toBe(undefined); - expect(error.message).toContain('elementHandle.click: Timeout 3000ms exceeded.'); - expect(error.message).toContain('element is disabled - waiting'); -}); - -it('should timeout when click opens alert', async({page, server}) => { - const dialogPromise = page.waitForEvent('dialog'); - await page.setContent(`
Click me
`); - const error = await page.click('div', { timeout: 3000 }).catch(e => e); - expect(error.message).toContain('page.click: Timeout 3000ms exceeded.'); - const dialog = await dialogPromise; - await dialog.dismiss(); -}); diff --git a/test/click.spec.js b/test/click.spec.ts similarity index 74% rename from test/click.spec.js rename to test/click.spec.ts index 3478434640..a4a4b4c886 100644 --- a/test/click.spec.js +++ b/test/click.spec.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -const utils = require('./utils'); +import utils from './utils'; const {FFOX, CHROMIUM, WEBKIT, HEADLESS, USES_HOOKS} = testOptions; async function giveItAChanceToClick(page) { @@ -26,7 +26,7 @@ async function giveItAChanceToClick(page) { it('should click the button', async({page, server}) => { await page.goto(server.PREFIX + '/input/button.html'); await page.click('button'); - expect(await page.evaluate(() => result)).toBe('Clicked'); + expect(await page.evaluate('result')).toBe('Clicked'); }); it('should click svg', async({page, server}) => { @@ -36,14 +36,14 @@ it('should click svg', async({page, server}) => { `); await page.click('circle'); - expect(await page.evaluate(() => window.__CLICKED)).toBe(42); + expect(await page.evaluate('__CLICKED')).toBe(42); }); it('should click the button if window.Node is removed', async({page, server}) => { await page.goto(server.PREFIX + '/input/button.html'); await page.evaluate(() => delete window.Node); await page.click('button'); - expect(await page.evaluate(() => result)).toBe('Clicked'); + expect(await page.evaluate('result')).toBe('Clicked'); }); // @see https://github.com/GoogleChrome/puppeteer/issues/4281 @@ -57,7 +57,7 @@ it('should click on a span with an inline element inside', async({page, server}) `); await page.click('span'); - expect(await page.evaluate(() => window.CLICKED)).toBe(42); + expect(await page.evaluate('CLICKED')).toBe(42); }); it('should not throw UnhandledPromiseRejection when page closes', async({browser, server}) => { @@ -75,7 +75,7 @@ it('should click the button after navigation ', async({page, server}) => { await page.click('button'); await page.goto(server.PREFIX + '/input/button.html'); await page.click('button'); - expect(await page.evaluate(() => result)).toBe('Clicked'); + expect(await page.evaluate('result')).toBe('Clicked'); }); it('should click the button after a cross origin navigation ', async({page, server}) => { @@ -83,7 +83,7 @@ it('should click the button after a cross origin navigation ', async({page, serv await page.click('button'); await page.goto(server.CROSS_PROCESS_PREFIX + '/input/button.html'); await page.click('button'); - expect(await page.evaluate(() => result)).toBe('Clicked'); + expect(await page.evaluate('result')).toBe('Clicked'); }); it('should click with disabled javascript', async({browser, server}) => { @@ -109,7 +109,7 @@ it('should click when one of inline box children is outside of viewport', async( woofdoggo `); await page.click('span'); - expect(await page.evaluate(() => window.CLICKED)).toBe(42); + expect(await page.evaluate('CLICKED')).toBe(42); }); it('should select the text by triple clicking', async({page, server}) => { @@ -150,7 +150,7 @@ it('should click offscreen buttons', async({page, server}) => { it('should waitFor visible when already visible', async({page, server}) => { await page.goto(server.PREFIX + '/input/button.html'); await page.click('button'); - expect(await page.evaluate(() => result)).toBe('Clicked'); + expect(await page.evaluate('result')).toBe('Clicked'); }); it('should not wait with force', async({page, server}) => { @@ -159,7 +159,7 @@ it('should not wait with force', async({page, server}) => { await page.$eval('button', b => b.style.display = 'none'); await page.click('button', { force: true }).catch(e => error = e); expect(error.message).toContain('Element is not visible'); - expect(await page.evaluate(() => result)).toBe('Was not clicked'); + expect(await page.evaluate('result')).toBe('Was not clicked'); }); it('should waitFor display:none to be gone', async({page, server}) => { @@ -168,12 +168,12 @@ it('should waitFor display:none to be gone', async({page, server}) => { await page.$eval('button', b => b.style.display = 'none'); const clicked = page.click('button', { timeout: 0 }).then(() => done = true); await giveItAChanceToClick(page); - expect(await page.evaluate(() => result)).toBe('Was not clicked'); + expect(await page.evaluate('result')).toBe('Was not clicked'); expect(done).toBe(false); await page.$eval('button', b => b.style.display = 'block'); await clicked; expect(done).toBe(true); - expect(await page.evaluate(() => result)).toBe('Clicked'); + expect(await page.evaluate('result')).toBe('Clicked'); }); it('should waitFor visibility:hidden to be gone', async({page, server}) => { @@ -182,12 +182,12 @@ it('should waitFor visibility:hidden to be gone', async({page, server}) => { await page.$eval('button', b => b.style.visibility = 'hidden'); const clicked = page.click('button', { timeout: 0 }).then(() => done = true); await giveItAChanceToClick(page); - expect(await page.evaluate(() => result)).toBe('Was not clicked'); + expect(await page.evaluate('result')).toBe('Was not clicked'); expect(done).toBe(false); await page.$eval('button', b => b.style.visibility = 'visible'); await clicked; expect(done).toBe(true); - expect(await page.evaluate(() => result)).toBe('Clicked'); + expect(await page.evaluate('result')).toBe('Clicked'); }); it('should waitFor visible when parent is hidden', async({page, server}) => { @@ -200,21 +200,21 @@ it('should waitFor visible when parent is hidden', async({page, server}) => { await page.$eval('button', b => b.parentElement.style.display = 'block'); await clicked; expect(done).toBe(true); - expect(await page.evaluate(() => result)).toBe('Clicked'); + expect(await page.evaluate('result')).toBe('Clicked'); }); it('should click wrapped links', async({page, server}) => { await page.goto(server.PREFIX + '/wrappedlink.html'); await page.click('a'); - expect(await page.evaluate(() => window.__clicked)).toBe(true); + expect(await page.evaluate('__clicked')).toBe(true); }); it('should click on checkbox input and toggle', async({page, server}) => { await page.goto(server.PREFIX + '/input/checkbox.html'); - expect(await page.evaluate(() => result.check)).toBe(null); + expect(await page.evaluate(() => window['result'].check)).toBe(null); await page.click('input#agree'); - expect(await page.evaluate(() => result.check)).toBe(true); - expect(await page.evaluate(() => result.events)).toEqual([ + expect(await page.evaluate(() => window['result'].check)).toBe(true); + expect(await page.evaluate(() => window['result'].events)).toEqual([ 'mouseover', 'mouseenter', 'mousemove', @@ -225,21 +225,21 @@ it('should click on checkbox input and toggle', async({page, server}) => { 'change', ]); await page.click('input#agree'); - expect(await page.evaluate(() => result.check)).toBe(false); + expect(await page.evaluate(() => window['result'].check)).toBe(false); }); it('should click on checkbox label and toggle', async({page, server}) => { await page.goto(server.PREFIX + '/input/checkbox.html'); - expect(await page.evaluate(() => result.check)).toBe(null); + expect(await page.evaluate(() => window['result'].check)).toBe(null); await page.click('label[for="agree"]'); - expect(await page.evaluate(() => result.check)).toBe(true); - expect(await page.evaluate(() => result.events)).toEqual([ + expect(await page.evaluate(() => window['result'].check)).toBe(true); + expect(await page.evaluate(() => window['result'].events)).toEqual([ 'click', 'input', 'change', ]); await page.click('label[for="agree"]'); - expect(await page.evaluate(() => result.check)).toBe(false); + expect(await page.evaluate(() => window['result'].check)).toBe(false); }); it('should not hang with touch-enabled viewports', async({browser, playwright}) => { @@ -264,10 +264,10 @@ it('should scroll and click the button', async({page, server}) => { it('should double click the button', async({page, server}) => { await page.goto(server.PREFIX + '/input/button.html'); await page.evaluate(() => { - window.double = false; + window['double'] = false; const button = document.querySelector('button'); button.addEventListener('dblclick', event => { - window.double = true; + window['double'] = true; }); }); await page.dblclick('button'); @@ -284,13 +284,13 @@ it('should click a partially obscured button', async({page, server}) => { button.style.left = '368px'; }); await page.click('button'); - expect(await page.evaluate(() => window.result)).toBe('Clicked'); + expect(await page.evaluate(() => window['result'])).toBe('Clicked'); }); it('should click a rotated button', async({page, server}) => { await page.goto(server.PREFIX + '/input/rotatedButton.html'); await page.click('button'); - expect(await page.evaluate(() => result)).toBe('Clicked'); + expect(await page.evaluate('result')).toBe('Clicked'); }); it('should fire contextmenu event on right click', async({page, server}) => { @@ -313,7 +313,7 @@ it('should click the button inside an iframe', async({page, server}) => { const frame = page.frames()[1]; const button = await frame.$('button'); await button.click(); - expect(await frame.evaluate(() => window.result)).toBe('Clicked'); + expect(await frame.evaluate(() => window['result'])).toBe('Clicked'); }); it.fail(CHROMIUM || WEBKIT)('should click the button with fixed position inside an iframe', async({page, server}) => { @@ -327,7 +327,7 @@ it.fail(CHROMIUM || WEBKIT)('should click the button with fixed position inside const frame = page.frames()[1]; await frame.$eval('button', button => button.style.setProperty('position', 'fixed')); await frame.click('button'); - expect(await frame.evaluate(() => window.result)).toBe('Clicked'); + expect(await frame.evaluate(() => window['result'])).toBe('Clicked'); }); it('should click the button with deviceScaleFactor set', async({browser, server}) => { @@ -339,7 +339,7 @@ it('should click the button with deviceScaleFactor set', async({browser, server} const frame = page.frames()[1]; const button = await frame.$('button'); await button.click(); - expect(await frame.evaluate(() => window.result)).toBe('Clicked'); + expect(await frame.evaluate(() => window['result'])).toBe('Clicked'); await context.close(); }); @@ -347,10 +347,10 @@ it('should click the button with px border with offset', async({page, server}) = await page.goto(server.PREFIX + '/input/button.html'); await page.$eval('button', button => button.style.borderWidth = '8px'); await page.click('button', { position: { x: 20, y: 10 } }); - expect(await page.evaluate(() => result)).toBe('Clicked'); + expect(await page.evaluate('result')).toBe('Clicked'); // Safari reports border-relative offsetX/offsetY. - expect(await page.evaluate(() => offsetX)).toBe(WEBKIT ? 20 + 8 : 20); - expect(await page.evaluate(() => offsetY)).toBe(WEBKIT ? 10 + 8 : 10); + expect(await page.evaluate('offsetX')).toBe(WEBKIT ? 20 + 8 : 20); + expect(await page.evaluate('offsetY')).toBe(WEBKIT ? 10 + 8 : 10); }); it('should click the button with em border with offset', async({page, server}) => { @@ -358,10 +358,10 @@ it('should click the button with em border with offset', async({page, server}) = await page.$eval('button', button => button.style.borderWidth = '2em'); await page.$eval('button', button => button.style.fontSize = '12px'); await page.click('button', { position: { x: 20, y: 10 } }); - expect(await page.evaluate(() => result)).toBe('Clicked'); + expect(await page.evaluate('result')).toBe('Clicked'); // Safari reports border-relative offsetX/offsetY. - expect(await page.evaluate(() => offsetX)).toBe(WEBKIT ? 12 * 2 + 20 : 20); - expect(await page.evaluate(() => offsetY)).toBe(WEBKIT ? 12 * 2 + 10 : 10); + expect(await page.evaluate('offsetX')).toBe(WEBKIT ? 12 * 2 + 20 : 20); + expect(await page.evaluate('offsetY')).toBe(WEBKIT ? 12 * 2 + 10 : 10); }); it('should click a very large button with offset', async({page, server}) => { @@ -369,10 +369,10 @@ it('should click a very large button with offset', async({page, server}) => { await page.$eval('button', button => button.style.borderWidth = '8px'); await page.$eval('button', button => button.style.height = button.style.width = '2000px'); await page.click('button', { position: { x: 1900, y: 1910 } }); - expect(await page.evaluate(() => window.result)).toBe('Clicked'); + expect(await page.evaluate(() => window['result'])).toBe('Clicked'); // Safari reports border-relative offsetX/offsetY. - expect(await page.evaluate(() => offsetX)).toBe(WEBKIT ? 1900 + 8 : 1900); - expect(await page.evaluate(() => offsetY)).toBe(WEBKIT ? 1910 + 8 : 1910); + expect(await page.evaluate('offsetX')).toBe(WEBKIT ? 1900 + 8 : 1900); + expect(await page.evaluate('offsetY')).toBe(WEBKIT ? 1910 + 8 : 1910); }); it('should click a button in scrolling container with offset', async({page, server}) => { @@ -389,10 +389,10 @@ it('should click a button in scrolling container with offset', async({page, serv button.style.borderWidth = '8px'; }); await page.click('button', { position: { x: 1900, y: 1910 } }); - expect(await page.evaluate(() => window.result)).toBe('Clicked'); + expect(await page.evaluate(() => window['result'])).toBe('Clicked'); // Safari reports border-relative offsetX/offsetY. - expect(await page.evaluate(() => offsetX)).toBe(WEBKIT ? 1900 + 8 : 1900); - expect(await page.evaluate(() => offsetY)).toBe(WEBKIT ? 1910 + 8 : 1910); + expect(await page.evaluate('offsetX')).toBe(WEBKIT ? 1900 + 8 : 1900); + expect(await page.evaluate('offsetY')).toBe(WEBKIT ? 1910 + 8 : 1910); }); it.skip(FFOX)('should click the button with offset with page scale', async({browser, server}) => { @@ -404,7 +404,7 @@ it.skip(FFOX)('should click the button with offset with page scale', async({brow document.body.style.margin = '0'; }); await page.click('button', { position: { x: 20, y: 10 } }); - expect(await page.evaluate(() => result)).toBe('Clicked'); + expect(await page.evaluate('result')).toBe('Clicked'); const round = x => Math.round(x + 0.01); let expected = { x: 28, y: 18 }; // 20;10 + 8px of border in each direction if (WEBKIT) { @@ -414,8 +414,8 @@ it.skip(FFOX)('should click the button with offset with page scale', async({brow // Headless Chromium rounds down during css -> dip -> css conversion. expected = { x: 27, y: 18 }; } - expect(round(await page.evaluate(() => pageX))).toBe(expected.x); - expect(round(await page.evaluate(() => pageY))).toBe(expected.y); + expect(round(await page.evaluate('pageX'))).toBe(expected.x); + expect(round(await page.evaluate('pageY'))).toBe(expected.y); await context.close(); }); @@ -433,9 +433,9 @@ it('should wait for stable position', async({page, server}) => { document.body.style.margin = '0'; }); await page.click('button'); - expect(await page.evaluate(() => window.result)).toBe('Clicked'); - expect(await page.evaluate(() => pageX)).toBe(300); - expect(await page.evaluate(() => pageY)).toBe(10); + expect(await page.evaluate(() => window['result'])).toBe('Clicked'); + expect(await page.evaluate('pageX')).toBe(300); + expect(await page.evaluate('pageY')).toBe(10); }); it('should wait for becoming hit target', async({page, server}) => { @@ -467,7 +467,7 @@ it('should wait for becoming hit target', async({page, server}) => { await page.$eval('.flyover', flyOver => flyOver.style.left = '200px'); await clickPromise; expect(clicked).toBe(true); - expect(await page.evaluate(() => window.result)).toBe('Clicked'); + expect(await page.evaluate(() => window['result'])).toBe('Clicked'); }); it('should fail when obscured and not waiting for hit target', async({page, server}) => { @@ -484,7 +484,7 @@ it('should fail when obscured and not waiting for hit target', async({page, serv document.body.appendChild(blocker); }); await button.click({ force: true }); - expect(await page.evaluate(() => window.result)).toBe('Was not clicked'); + expect(await page.evaluate(() => window['result'])).toBe('Was not clicked'); }); it('should wait for button to be enabled', async({page, server}) => { @@ -492,11 +492,11 @@ it('should wait for button to be enabled', async({page, server}) => { let done = false; const clickPromise = page.click('text=Click target').then(() => done = true); await giveItAChanceToClick(page); - expect(await page.evaluate(() => window.__CLICKED)).toBe(undefined); + expect(await page.evaluate('window.__CLICKED')).toBe(undefined); expect(done).toBe(false); await page.evaluate(() => document.querySelector('button').removeAttribute('disabled')); await clickPromise; - expect(await page.evaluate(() => window.__CLICKED)).toBe(true); + expect(await page.evaluate('__CLICKED')).toBe(true); }); it('should wait for input to be enabled', async({page, server}) => { @@ -504,11 +504,11 @@ it('should wait for input to be enabled', async({page, server}) => { let done = false; const clickPromise = page.click('input').then(() => done = true); await giveItAChanceToClick(page); - expect(await page.evaluate(() => window.__CLICKED)).toBe(undefined); + expect(await page.evaluate('window.__CLICKED')).toBe(undefined); expect(done).toBe(false); await page.evaluate(() => document.querySelector('input').removeAttribute('disabled')); await clickPromise; - expect(await page.evaluate(() => window.__CLICKED)).toBe(true); + expect(await page.evaluate('__CLICKED')).toBe(true); }); it('should wait for select to be enabled', async({page, server}) => { @@ -516,29 +516,29 @@ it('should wait for select to be enabled', async({page, server}) => { let done = false; const clickPromise = page.click('select').then(() => done = true); await giveItAChanceToClick(page); - expect(await page.evaluate(() => window.__CLICKED)).toBe(undefined); + expect(await page.evaluate('window.__CLICKED')).toBe(undefined); expect(done).toBe(false); await page.evaluate(() => document.querySelector('select').removeAttribute('disabled')); await clickPromise; - expect(await page.evaluate(() => window.__CLICKED)).toBe(true); + expect(await page.evaluate('__CLICKED')).toBe(true); }); it('should click disabled div', async({page, server}) => { await page.setContent('
Click target
'); await page.click('text=Click target'); - expect(await page.evaluate(() => window.__CLICKED)).toBe(true); + expect(await page.evaluate('__CLICKED')).toBe(true); }); it('should climb dom for inner label with pointer-events:none', async({page, server}) => { await page.setContent(''); await page.click('text=Click target'); - expect(await page.evaluate(() => window.__CLICKED)).toBe(true); + expect(await page.evaluate('__CLICKED')).toBe(true); }); it('should climb up to [role=button]', async({page, server}) => { await page.setContent('
Click target
'); await page.click('text=Click target'); - expect(await page.evaluate(() => window.__CLICKED)).toBe(true); + expect(await page.evaluate('__CLICKED')).toBe(true); }); it('should wait for BUTTON to be clickable when it has pointer-events:none', async({page, server}) => { @@ -546,11 +546,11 @@ it('should wait for BUTTON to be clickable when it has pointer-events:none', asy let done = false; const clickPromise = page.click('text=Click target').then(() => done = true); await giveItAChanceToClick(page); - expect(await page.evaluate(() => window.__CLICKED)).toBe(undefined); + expect(await page.evaluate('window.__CLICKED')).toBe(undefined); expect(done).toBe(false); await page.evaluate(() => document.querySelector('button').style.removeProperty('pointer-events')); await clickPromise; - expect(await page.evaluate(() => window.__CLICKED)).toBe(true); + expect(await page.evaluate('__CLICKED')).toBe(true); }); it('should wait for LABEL to be clickable when it has pointer-events:none', async({page, server}) => { @@ -558,28 +558,28 @@ it('should wait for LABEL to be clickable when it has pointer-events:none', asyn const clickPromise = page.click('text=Click target'); // Do a few roundtrips to the page. for (let i = 0; i < 5; ++i) - expect(await page.evaluate(() => window.__CLICKED)).toBe(undefined); + expect(await page.evaluate('window.__CLICKED')).toBe(undefined); // remove `pointer-events: none` css from button. await page.evaluate(() => document.querySelector('label').style.removeProperty('pointer-events')); await clickPromise; - expect(await page.evaluate(() => window.__CLICKED)).toBe(true); + expect(await page.evaluate('__CLICKED')).toBe(true); }); it('should update modifiers correctly', async({page, server}) => { await page.goto(server.PREFIX + '/input/button.html'); await page.click('button', { modifiers: ['Shift'] }); - expect(await page.evaluate(() => shiftKey)).toBe(true); + expect(await page.evaluate('shiftKey')).toBe(true); await page.click('button', { modifiers: [] }); - expect(await page.evaluate(() => shiftKey)).toBe(false); + expect(await page.evaluate('shiftKey')).toBe(false); await page.keyboard.down('Shift'); await page.click('button', { modifiers: [] }); - expect(await page.evaluate(() => shiftKey)).toBe(false); + expect(await page.evaluate('shiftKey')).toBe(false); await page.click('button'); - expect(await page.evaluate(() => shiftKey)).toBe(true); + expect(await page.evaluate('shiftKey')).toBe(true); await page.keyboard.up('Shift'); await page.click('button'); - expect(await page.evaluate(() => shiftKey)).toBe(false); + expect(await page.evaluate('shiftKey')).toBe(false); }); it('should click an offscreen element when scroll-behavior is smooth', async({page}) => { @@ -594,45 +594,45 @@ it('should click an offscreen element when scroll-behavior is smooth', async({pa it('should report nice error when element is detached and force-clicked', async({page, server}) => { await page.goto(server.PREFIX + '/input/animating-button.html'); - await page.evaluate(() => addButton()); + await page.evaluate('addButton()'); const handle = await page.$('button'); - await page.evaluate(() => stopButton(true)); + await page.evaluate('stopButton(true)'); const promise = handle.click({ force: true }).catch(e => e); const error = await promise; - expect(await page.evaluate(() => window.clicked)).toBe(undefined); + expect(await page.evaluate('window.clicked')).toBe(undefined); expect(error.message).toContain('Element is not attached to the DOM'); }); it('should fail when element detaches after animation', async({page, server}) => { await page.goto(server.PREFIX + '/input/animating-button.html'); - await page.evaluate(() => addButton()); + await page.evaluate('addButton()'); const handle = await page.$('button'); const promise = handle.click().catch(e => e); - await page.evaluate(() => stopButton(true)); + await page.evaluate('stopButton(true)'); const error = await promise; - expect(await page.evaluate(() => window.clicked)).toBe(undefined); + expect(await page.evaluate('window.clicked')).toBe(undefined); expect(error.message).toContain('Element is not attached to the DOM'); }); it('should retry when element detaches after animation', async({page, server}) => { await page.goto(server.PREFIX + '/input/animating-button.html'); - await page.evaluate(() => addButton()); + await page.evaluate('addButton()'); let clicked = false; const promise = page.click('button').then(() => clicked = true); expect(clicked).toBe(false); - expect(await page.evaluate(() => window.clicked)).toBe(undefined); - await page.evaluate(() => stopButton(true)); - await page.evaluate(() => addButton()); + expect(await page.evaluate('window.clicked')).toBe(undefined); + await page.evaluate('stopButton(true)'); + await page.evaluate('addButton()'); expect(clicked).toBe(false); - expect(await page.evaluate(() => window.clicked)).toBe(undefined); - await page.evaluate(() => stopButton(true)); - await page.evaluate(() => addButton()); + expect(await page.evaluate('window.clicked')).toBe(undefined); + await page.evaluate('stopButton(true)'); + await page.evaluate('addButton()'); expect(clicked).toBe(false); - expect(await page.evaluate(() => window.clicked)).toBe(undefined); - await page.evaluate(() => stopButton(false)); + expect(await page.evaluate('window.clicked')).toBe(undefined); + await page.evaluate('stopButton(false)'); await promise; expect(clicked).toBe(true); - expect(await page.evaluate(() => window.clicked)).toBe(true); + expect(await page.evaluate('clicked')).toBe(true); }); it('should retry when element is animating from outside the viewport', async({page, server}) => { @@ -660,7 +660,7 @@ it('should retry when element is animating from outside the viewport', async({pa const promise = handle.click(); await handle.evaluate(button => button.className = 'animated'); await promise; - expect(await page.evaluate(() => window.clicked)).toBe(true); + expect(await page.evaluate('clicked')).toBe(true); }); it('should fail when element is animating from outside the viewport with force', async({page, server}) => { @@ -688,7 +688,7 @@ it('should fail when element is animating from outside the viewport with force', const promise = handle.click({ force: true }).catch(e => e); await handle.evaluate(button => button.className = 'animated'); const error = await promise; - expect(await page.evaluate(() => window.clicked)).toBe(undefined); + expect(await page.evaluate('window.clicked')).toBe(undefined); expect(error.message).toContain('Element is outside of the viewport'); }); @@ -707,70 +707,17 @@ it('should dispatch microtasks in order', async({page, server}) => { document.body.appendChild(document.createElement('div')); }); button.addEventListener('mouseup', () => { - window.result = mutationCount; + window['result'] = mutationCount; }); `); await page.click('button'); - expect(await page.evaluate(() => window.result)).toBe(1); -}); - -it.fail(true)('should retarget when element is recycled during hit testing', async ({page, server}) => { - await page.goto(server.PREFIX + '/react.html'); - await page.evaluate(() => { - renderComponent(e('div', {}, [e(MyButton, { name: 'button1' }), e(MyButton, { name: 'button2' })] )); - }); - const __testHookAfterStable = () => page.evaluate(() => { - window.counter = (window.counter || 0) + 1; - if (window.counter === 1) - renderComponent(e('div', {}, [e(MyButton, { name: 'button2' }), e(MyButton, { name: 'button1' })] )); - }); - await page.click('text=button1', { __testHookAfterStable }); - expect(await page.evaluate(() => window.button1)).toBe(true); - expect(await page.evaluate(() => window.button2)).toBe(undefined); -}); - -it.fail(true)('should retarget when element is recycled before enabled check', async ({page, server}) => { - await page.goto(server.PREFIX + '/react.html'); - await page.evaluate(() => { - renderComponent(e('div', {}, [e(MyButton, { name: 'button1' }), e(MyButton, { name: 'button2', disabled: true })] )); - }); - const __testHookBeforeStable = () => page.evaluate(() => { - window.counter = (window.counter || 0) + 1; - if (window.counter === 1) - renderComponent(e('div', {}, [e(MyButton, { name: 'button2', disabled: true }), e(MyButton, { name: 'button1' })] )); - }); - await page.click('text=button1', { __testHookBeforeStable }); - expect(await page.evaluate(() => window.button1)).toBe(true); - expect(await page.evaluate(() => window.button2)).toBe(undefined); -}); - -it('should not retarget when element changes on hover', async ({page, server}) => { - await page.goto(server.PREFIX + '/react.html'); - await page.evaluate(() => { - renderComponent(e('div', {}, [e(MyButton, { name: 'button1', renameOnHover: true }), e(MyButton, { name: 'button2' })] )); - }); - await page.click('text=button1'); - expect(await page.evaluate(() => window.button1)).toBe(true); - expect(await page.evaluate(() => window.button2)).toBe(undefined); -}); - -it('should not retarget when element is recycled on hover', async ({page, server}) => { - await page.goto(server.PREFIX + '/react.html'); - await page.evaluate(() => { - function shuffle() { - renderComponent(e('div', {}, [e(MyButton, { name: 'button2' }), e(MyButton, { name: 'button1' })] )); - } - renderComponent(e('div', {}, [e(MyButton, { name: 'button1', onHover: shuffle }), e(MyButton, { name: 'button2' })] )); - }); - await page.click('text=button1'); - expect(await page.evaluate(() => window.button1)).toBe(undefined); - expect(await page.evaluate(() => window.button2)).toBe(true); + expect(await page.evaluate(() => window['result'])).toBe(1); }); it('should click the button when window.innerWidth is corrupted', async({page, server}) => { await page.goto(server.PREFIX + '/input/button.html'); - await page.evaluate(() => window.innerWidth = 0); + await page.evaluate(() => Object.defineProperty(window, 'innerWidth', {value: 0})); await page.click('button'); - expect(await page.evaluate(() => result)).toBe('Clicked'); + expect(await page.evaluate('result')).toBe('Clicked'); }); diff --git a/test/defaultbrowsercontext.spec.js b/test/defaultbrowsercontext.spec.ts similarity index 96% rename from test/defaultbrowsercontext.spec.js rename to test/defaultbrowsercontext.spec.ts index 27373f8db0..865423265b 100644 --- a/test/defaultbrowsercontext.spec.js +++ b/test/defaultbrowsercontext.spec.ts @@ -15,14 +15,20 @@ * limitations under the License. */ -const fs = require('fs'); -const path = require('path'); -const utils = require('./utils'); -const os = require('os'); - -const {mkdtempAsync, makeUserDataDir, removeUserDataDir} = utils; +import fs from 'fs'; +import path from 'path'; +import utils from './utils'; +import os from 'os'; +import { BrowserType, Browser, BrowserContext, Page } from '..'; +const {removeFolderAsync, mkdtempAsync, removeUserDataDir, makeUserDataDir} = utils; const {FFOX, MAC, CHROMIUM, WEBKIT, WIN, USES_HOOKS} = testOptions; +declare global { + interface FixtureState { + userDataDir: string; + launchPersistent: (options?: Parameters['launchPersistentContext']>[1]) => Promise<{context: BrowserContext, page: Page}>; + } +} registerFixture('userDataDir', async ({}, test) => { const userDataDir = await mkdtempAsync(path.join(os.tmpdir(), 'playwright_dev_profile-')); try { @@ -173,7 +179,7 @@ it('should support bypassCSP option', async ({server, launchPersistent}) => { const {page, context} = await launchPersistent({bypassCSP: true}); await page.goto(server.PREFIX + '/csp.html'); await page.addScriptTag({content: 'window.__injected = 42;'}); - expect(await page.evaluate(() => window.__injected)).toBe(42); + expect(await page.evaluate('__injected')).toBe(42); }); it('should support javascriptEnabled option', async ({server, launchPersistent}) => { diff --git a/test/download.spec.js b/test/download.spec.ts similarity index 97% rename from test/download.spec.js rename to test/download.spec.ts index d9aaa179c8..88d433725e 100644 --- a/test/download.spec.js +++ b/test/download.spec.ts @@ -14,14 +14,19 @@ * limitations under the License. */ -const fs = require('fs'); -const path = require('path'); -const util = require('util'); -const os = require('os'); -const {mkdtempAsync, removeFolderAsync} = require('./utils'); +import fs from 'fs'; +import path from 'path'; +import util from 'util'; +import os from 'os'; +import {mkdtempAsync, removeFolderAsync} from './utils'; const {FFOX, CHROMIUM, WEBKIT, HEADLESS} = testOptions; +declare global { + interface FixtureState { + persistentDirectory: string; + } +} registerFixture('persistentDirectory', async ({}, test) => { const persistentDirectory = await mkdtempAsync(path.join(os.tmpdir(), 'playwright-test-')); try { @@ -203,7 +208,7 @@ it('should report non-navigation downloads', async({browser, server}) => { it(`should report download path within page.on('download', …) handler for Files`, async({browser, server}) => { const page = await browser.newPage({ acceptDownloads: true }); - const onDownloadPath = new Promise((res) => { + const onDownloadPath = new Promise((res) => { page.on('download', dl => { dl.path().then(res); }); @@ -216,7 +221,7 @@ it(`should report download path within page.on('download', …) handler for File }) it(`should report download path within page.on('download', …) handler for Blobs`, async({browser, server}) => { const page = await browser.newPage({ acceptDownloads: true }); - const onDownloadPath = new Promise((res) => { + const onDownloadPath = new Promise((res) => { page.on('download', dl => { dl.path().then(res); }); diff --git a/test/downloads-path.spec.js b/test/downloads-path.spec.ts similarity index 92% rename from test/downloads-path.spec.js rename to test/downloads-path.spec.ts index 2807aeaf3a..74c7ccf0c8 100644 --- a/test/downloads-path.spec.js +++ b/test/downloads-path.spec.ts @@ -14,11 +14,19 @@ * limitations under the License. */ -const path = require('path'); -const fs = require('fs'); -const os = require('os'); -const {mkdtempAsync, removeFolderAsync} = require('./utils'); +import path from 'path'; +import fs from 'fs'; +import os from 'os'; +import {mkdtempAsync, removeFolderAsync} from './utils'; +import { Browser, BrowserContext } from '..'; +declare global { + interface FixtureState { + downloadsPath: string; + downloadsBrowser: Browser; + persistentDownloadsContext: BrowserContext; + } +} registerFixture('downloadsPath', async ({}, test) => { const downloadsPath = await mkdtempAsync(path.join(os.tmpdir(), 'playwright-test-')); try { @@ -66,7 +74,6 @@ registerFixture('persistentDownloadsContext', async ({server, browserType, defau await test(context); } finally { await context.close(); - await state.context.close(); await removeFolderAsync(userDataDir); } }); @@ -80,7 +87,7 @@ it('should keep downloadsPath folder', async({downloadsBrowser, downloadsPath, s ]); expect(download.url()).toBe(`${server.PREFIX}/download`); expect(download.suggestedFilename()).toBe(`file.txt`); - await download.path().catch(e => error = e); + await download.path().catch(e => void 0); await page.close(); await downloadsBrowser.close(); expect(fs.existsSync(downloadsPath)).toBeTruthy(); diff --git a/test/fixtures.spec.js b/test/fixtures.spec.ts similarity index 92% rename from test/fixtures.spec.js rename to test/fixtures.spec.ts index 60f1a5bfcc..b29fcb0ab2 100644 --- a/test/fixtures.spec.js +++ b/test/fixtures.spec.ts @@ -15,14 +15,21 @@ * limitations under the License. */ -const path = require('path'); -const {spawn, execSync} = require('child_process'); +import path from 'path'; +import {spawn, execSync} from 'child_process'; +import { BrowserType, Browser, LaunchOptions } from '..'; const {FFOX, CHROMIUM, WEBKIT, WIN, LINUX, HEADLESS} = testOptions; const playwrightPath = path.join(__dirname, '..'); class Wrapper { - constructor(browserType, defaultBrowserOptions, extraOptions) { + _output: Map; + _outputCallback: Map; + _browserType: BrowserType; + _child: import("child_process").ChildProcess; + _exitPromise: Promise; + _exitAndDisconnectPromise: Promise; + constructor(browserType: BrowserType, defaultBrowserOptions: LaunchOptions, extraOptions?: { stallOnClose: boolean; }) { this._output = new Map(); this._outputCallback = new Map(); @@ -91,6 +98,12 @@ class Wrapper { } } +declare global { + interface FixtureState { + wrapper: Wrapper; + stallingWrapper: Wrapper; + } +} registerFixture('wrapper', async ({browserType, defaultBrowserOptions}, test) => { const wrapper = new Wrapper(browserType, defaultBrowserOptions); await wrapper.connect(); diff --git a/test/tsconfig.json b/test/tsconfig.json index 90944c6e22..6ed2fc42d8 100644 --- a/test/tsconfig.json +++ b/test/tsconfig.json @@ -8,5 +8,5 @@ "strictNullChecks": false, "allowSyntheticDefaultImports": true, }, - "include": ["*.spec.js", "types.d.ts", "*.spec.ts"] + "include": ["**/*.spec.js", "types.d.ts", "**/*.spec.ts"] } \ No newline at end of file diff --git a/test/types.d.ts b/test/types.d.ts index 9c43704f74..aad38b51d4 100644 --- a/test/types.d.ts +++ b/test/types.d.ts @@ -1,8 +1,6 @@ type ServerResponse = import('http').ServerResponse; type IncomingMessage = import('http').IncomingMessage; -type Falsy = false|''|0|null|undefined; - type DescribeFunction = ((name: string, inner: () => void) => void) & {fail(condition: boolean): DescribeFunction}; type ItFunction = ((name: string, inner: (state: STATE) => Promise) => void) & { @@ -12,48 +10,20 @@ type ItFunction = ((name: string, inner: (state: STATE) => Promise) repeat(n: number): ItFunction; }; -type TestRunner = { - describe: DescribeFunction; - xdescribe: DescribeFunction; - fdescribe: DescribeFunction; - - it: ItFunction; - xit: ItFunction; - fit: ItFunction; - dit: ItFunction; - - beforeAll, beforeEach, afterAll, afterEach; -}; - -interface TestSetup { - testRunner: TestRunner; - product: 'Chromium'|'Firefox'|'WebKit'; - selectors: import('../index').Selectors; - playwrightPath; -} - -type TestState = { - server: TestServer; - httpsServer: TestServer; - sourceServer: TestServer; -}; - -type BrowserState = TestState & { +interface FixtureState { + parallelIndex: number; + http_server: {server: TestServer, httpsServer: TestServer}; + defaultBrowserOptions: import('../index').LaunchOptions; playwright: typeof import('../index'); browserType: import('../index').BrowserType; browser: import('../index').Browser; - browserServer: import('../index').BrowserServer; - defaultBrowserOptions: import('../index').LaunchOptions; -}; - -type PageState = BrowserState & { + toImpl: (rpcObject: any) => any; context: import('../index').BrowserContext; + server: TestServer; page: import('../index').Page; -}; -type ChromiumPageState = PageState & { - browser: import('../index').ChromiumBrowser; -}; - + httpsServer: TestServer; + browserServer: import('../index').BrowserServer; +} interface TestServer { enableHTTPCache(pathPrefix: string); @@ -72,16 +42,33 @@ interface TestServer { CROSS_PROCESS_PREFIX: string; EMPTY_PAGE: string; } +declare module '' { + module 'expect/build/types' { + interface Matchers { + toBeGolden(name: string): R; + } + } +} + + +declare const expect: typeof import('expect'); + declare const describe: DescribeFunction; declare const fdescribe: DescribeFunction; declare const xdescribe: DescribeFunction; -declare const expect: typeof import('expect'); -declare const it: ItFunction; -declare const fit: ItFunction; -declare const dit: ItFunction; -declare const xit: ItFunction; +declare const it: ItFunction; +declare const fit: ItFunction; +declare const dit: ItFunction; +declare const xit: ItFunction; +declare const beforeEach: (inner: (state: FixtureState) => Promise) => void; +declare const afterEach: (inner: (state: FixtureState) => Promise) => void; +declare const beforeAll: (inner: (state: FixtureState) => Promise) => void; +declare const afterAll: (inner: (state: FixtureState) => Promise) => void; + +declare const registerFixture: (name: T, inner: (state: FixtureState, test: (arg: FixtureState[T]) => Promise) => Promise) => void; + declare const browserType: import('../index').BrowserType; // global variables in assets