diff --git a/test/defaultbrowsercontext-1.spec.ts b/test/defaultbrowsercontext-1.spec.ts new file mode 100644 index 0000000000..f0e4694a7e --- /dev/null +++ b/test/defaultbrowsercontext-1.spec.ts @@ -0,0 +1,189 @@ +/** + * Copyright 2017 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 { options } from './playwright.fixtures'; +import fs from 'fs'; +import utils from './utils'; + +it('context.cookies() should work', async ({server, launchPersistent}) => { + const {page, context} = await launchPersistent(); + await page.goto(server.EMPTY_PAGE); + const documentCookie = await page.evaluate(() => { + document.cookie = 'username=John Doe'; + return document.cookie; + }); + expect(documentCookie).toBe('username=John Doe'); + expect(await page.context().cookies()).toEqual([{ + name: 'username', + value: 'John Doe', + domain: 'localhost', + path: '/', + expires: -1, + httpOnly: false, + secure: false, + sameSite: 'None', + }]); +}); + +it('context.addCookies() should work', async ({server, launchPersistent}) => { + const {page, context} = await launchPersistent(); + await page.goto(server.EMPTY_PAGE); + await page.context().addCookies([{ + url: server.EMPTY_PAGE, + name: 'username', + value: 'John Doe' + }]); + expect(await page.evaluate(() => document.cookie)).toBe('username=John Doe'); + expect(await page.context().cookies()).toEqual([{ + name: 'username', + value: 'John Doe', + domain: 'localhost', + path: '/', + expires: -1, + httpOnly: false, + secure: false, + sameSite: 'None', + }]); +}); + +it('context.clearCookies() should work', async ({server, launchPersistent}) => { + const {page, context} = await launchPersistent(); + await page.goto(server.EMPTY_PAGE); + await page.context().addCookies([{ + url: server.EMPTY_PAGE, + name: 'cookie1', + value: '1' + }, { + url: server.EMPTY_PAGE, + name: 'cookie2', + value: '2' + }]); + expect(await page.evaluate('document.cookie')).toBe('cookie1=1; cookie2=2'); + await page.context().clearCookies(); + await page.reload(); + expect(await page.context().cookies([])).toEqual([]); + expect(await page.evaluate('document.cookie')).toBe(''); +}); + +it('should(not) block third party cookies', async ({server, launchPersistent}) => { + const {page, context} = await launchPersistent(); + await page.goto(server.EMPTY_PAGE); + await page.evaluate(src => { + let fulfill; + const promise = new Promise(x => fulfill = x); + const iframe = document.createElement('iframe'); + document.body.appendChild(iframe); + iframe.onload = fulfill; + iframe.src = src; + return promise; + }, server.CROSS_PROCESS_PREFIX + '/grid.html'); + const documentCookie = await page.frames()[1].evaluate(() => { + document.cookie = 'username=John Doe'; + return document.cookie; + }); + await page.waitForTimeout(2000); + const allowsThirdParty = options.CHROMIUM || options.FIREFOX; + expect(documentCookie).toBe(allowsThirdParty ? 'username=John Doe' : ''); + const cookies = await context.cookies(server.CROSS_PROCESS_PREFIX + '/grid.html'); + if (allowsThirdParty) { + expect(cookies).toEqual([ + { + "domain": "127.0.0.1", + "expires": -1, + "httpOnly": false, + "name": "username", + "path": "/", + "sameSite": "None", + "secure": false, + "value": "John Doe" + } + ]); + } else { + expect(cookies).toEqual([]); + } +}); + +it('should support viewport option', async ({launchPersistent}) => { + const {page, context} = await launchPersistent({viewport: { width: 456, height: 789 }}); + await utils.verifyViewport(page, 456, 789); + const page2 = await context.newPage(); + await utils.verifyViewport(page2, 456, 789); +}); + +it('should support deviceScaleFactor option', async ({launchPersistent}) => { + const {page, context} = await launchPersistent({deviceScaleFactor: 3}); + expect(await page.evaluate('window.devicePixelRatio')).toBe(3); +}); + +it('should support userAgent option', async ({server, launchPersistent}) => { + const {page, context} = await launchPersistent({userAgent: 'foobar'}); + expect(await page.evaluate(() => navigator.userAgent)).toBe('foobar'); + const [request] = await Promise.all([ + server.waitForRequest('/empty.html'), + page.goto(server.EMPTY_PAGE), + ]); + expect(request.headers['user-agent']).toBe('foobar'); +}); + +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('__injected')).toBe(42); +}); + +it('should support javascriptEnabled option', async ({launchPersistent}) => { + const {page, context} = await launchPersistent({javaScriptEnabled: false}); + await page.goto('data:text/html, '); + let error = null; + await page.evaluate('something').catch(e => error = e); + if (options.WEBKIT) + expect(error.message).toContain('Can\'t find variable: something'); + else + expect(error.message).toContain('something is not defined'); +}); + +it('should support httpCredentials option', async ({server, launchPersistent}) => { + const {page, context} = await launchPersistent({httpCredentials: { username: 'user', password: 'pass' }}); + server.setAuth('/playground.html', 'user', 'pass'); + const response = await page.goto(server.PREFIX + '/playground.html'); + expect(response.status()).toBe(200); +}); + +it('should support offline option', async ({server, launchPersistent}) => { + const {page, context} = await launchPersistent({offline: true}); + const error = await page.goto(server.EMPTY_PAGE).catch(e => e); + expect(error).toBeTruthy(); +}); + +it.skip(true)('should support acceptDownloads option', async ({server, launchPersistent}) => { + // TODO: unskip once we support downloads in persistent context. + const {page, context} = await launchPersistent({acceptDownloads: true}); + server.setRoute('/download', (req, res) => { + res.setHeader('Content-Type', 'application/octet-stream'); + res.setHeader('Content-Disposition', 'attachment'); + res.end(`Hello world`); + }); + await page.setContent(`download`); + const [ download ] = await Promise.all([ + page.waitForEvent('download'), + page.click('a') + ]); + const path = await download.path(); + expect(fs.existsSync(path)).toBeTruthy(); + expect(fs.readFileSync(path).toString()).toBe('Hello world'); +}); diff --git a/test/defaultbrowsercontext.spec.ts b/test/defaultbrowsercontext-2.spec.ts similarity index 59% rename from test/defaultbrowsercontext.spec.ts rename to test/defaultbrowsercontext-2.spec.ts index d808ad24df..b2636ed36b 100644 --- a/test/defaultbrowsercontext.spec.ts +++ b/test/defaultbrowsercontext-2.spec.ts @@ -16,201 +16,10 @@ */ import { options } from './playwright.fixtures'; -import { registerFixture } from '../test-runner'; import fs from 'fs'; import utils from './utils'; -import { BrowserType, Browser, BrowserContext, Page } from '..'; const { removeUserDataDir, makeUserDataDir } = utils; -declare global { - interface TestState { - launchPersistent: (options?: Parameters['launchPersistentContext']>[1]) => Promise<{context: BrowserContext, page: Page}>; - } -} - -registerFixture('launchPersistent', async ({tmpDir, defaultBrowserOptions, browserType}, test) => { - let context; - async function launchPersistent(options) { - if (context) - throw new Error('can only launch one persitent context'); - context = await browserType.launchPersistentContext(tmpDir, {...defaultBrowserOptions, ...options}); - const page = context.pages()[0]; - return {context, page}; - } - await test(launchPersistent); - if (context) - await context.close(); -}); - -it('context.cookies() should work', async ({server, launchPersistent}) => { - const {page, context} = await launchPersistent(); - await page.goto(server.EMPTY_PAGE); - const documentCookie = await page.evaluate(() => { - document.cookie = 'username=John Doe'; - return document.cookie; - }); - expect(documentCookie).toBe('username=John Doe'); - expect(await page.context().cookies()).toEqual([{ - name: 'username', - value: 'John Doe', - domain: 'localhost', - path: '/', - expires: -1, - httpOnly: false, - secure: false, - sameSite: 'None', - }]); -}); - -it('context.addCookies() should work', async ({server, launchPersistent}) => { - const {page, context} = await launchPersistent(); - await page.goto(server.EMPTY_PAGE); - await page.context().addCookies([{ - url: server.EMPTY_PAGE, - name: 'username', - value: 'John Doe' - }]); - expect(await page.evaluate(() => document.cookie)).toBe('username=John Doe'); - expect(await page.context().cookies()).toEqual([{ - name: 'username', - value: 'John Doe', - domain: 'localhost', - path: '/', - expires: -1, - httpOnly: false, - secure: false, - sameSite: 'None', - }]); -}); - -it('context.clearCookies() should work', async ({server, launchPersistent}) => { - const {page, context} = await launchPersistent(); - await page.goto(server.EMPTY_PAGE); - await page.context().addCookies([{ - url: server.EMPTY_PAGE, - name: 'cookie1', - value: '1' - }, { - url: server.EMPTY_PAGE, - name: 'cookie2', - value: '2' - }]); - expect(await page.evaluate('document.cookie')).toBe('cookie1=1; cookie2=2'); - await page.context().clearCookies(); - await page.reload(); - expect(await page.context().cookies([])).toEqual([]); - expect(await page.evaluate('document.cookie')).toBe(''); -}); - -it('should(not) block third party cookies', async ({server, launchPersistent}) => { - const {page, context} = await launchPersistent(); - await page.goto(server.EMPTY_PAGE); - await page.evaluate(src => { - let fulfill; - const promise = new Promise(x => fulfill = x); - const iframe = document.createElement('iframe'); - document.body.appendChild(iframe); - iframe.onload = fulfill; - iframe.src = src; - return promise; - }, server.CROSS_PROCESS_PREFIX + '/grid.html'); - const documentCookie = await page.frames()[1].evaluate(() => { - document.cookie = 'username=John Doe'; - return document.cookie; - }); - await page.waitForTimeout(2000); - const allowsThirdParty = options.CHROMIUM || options.FIREFOX; - expect(documentCookie).toBe(allowsThirdParty ? 'username=John Doe' : ''); - const cookies = await context.cookies(server.CROSS_PROCESS_PREFIX + '/grid.html'); - if (allowsThirdParty) { - expect(cookies).toEqual([ - { - "domain": "127.0.0.1", - "expires": -1, - "httpOnly": false, - "name": "username", - "path": "/", - "sameSite": "None", - "secure": false, - "value": "John Doe" - } - ]); - } else { - expect(cookies).toEqual([]); - } -}); - -it('should support viewport option', async ({launchPersistent}) => { - const {page, context} = await launchPersistent({viewport: { width: 456, height: 789 }}); - await utils.verifyViewport(page, 456, 789); - const page2 = await context.newPage(); - await utils.verifyViewport(page2, 456, 789); -}); - -it('should support deviceScaleFactor option', async ({launchPersistent}) => { - const {page, context} = await launchPersistent({deviceScaleFactor: 3}); - expect(await page.evaluate('window.devicePixelRatio')).toBe(3); -}); - -it('should support userAgent option', async ({server, launchPersistent}) => { - const {page, context} = await launchPersistent({userAgent: 'foobar'}); - expect(await page.evaluate(() => navigator.userAgent)).toBe('foobar'); - const [request] = await Promise.all([ - server.waitForRequest('/empty.html'), - page.goto(server.EMPTY_PAGE), - ]); - expect(request.headers['user-agent']).toBe('foobar'); -}); - -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('__injected')).toBe(42); -}); - -it('should support javascriptEnabled option', async ({launchPersistent}) => { - const {page, context} = await launchPersistent({javaScriptEnabled: false}); - await page.goto('data:text/html, '); - let error = null; - await page.evaluate('something').catch(e => error = e); - if (options.WEBKIT) - expect(error.message).toContain('Can\'t find variable: something'); - else - expect(error.message).toContain('something is not defined'); -}); - -it('should support httpCredentials option', async ({server, launchPersistent}) => { - const {page, context} = await launchPersistent({httpCredentials: { username: 'user', password: 'pass' }}); - server.setAuth('/playground.html', 'user', 'pass'); - const response = await page.goto(server.PREFIX + '/playground.html'); - expect(response.status()).toBe(200); -}); - -it('should support offline option', async ({server, launchPersistent}) => { - const {page, context} = await launchPersistent({offline: true}); - const error = await page.goto(server.EMPTY_PAGE).catch(e => e); - expect(error).toBeTruthy(); -}); - -it.skip(true)('should support acceptDownloads option', async ({server, launchPersistent}) => { - // TODO: unskip once we support downloads in persistent context. - const {page, context} = await launchPersistent({acceptDownloads: true}); - server.setRoute('/download', (req, res) => { - res.setHeader('Content-Type', 'application/octet-stream'); - res.setHeader('Content-Disposition', 'attachment'); - res.end(`Hello world`); - }); - await page.setContent(`download`); - const [ download ] = await Promise.all([ - page.waitForEvent('download'), - page.click('a') - ]); - const path = await download.path(); - expect(fs.existsSync(path)).toBeTruthy(); - expect(fs.readFileSync(path).toString()).toBe('Hello world'); -}); - it('should support hasTouch option', async ({server, launchPersistent}) => { const {page, context} = await launchPersistent({hasTouch: true}); await page.goto(server.PREFIX + '/mobile.html'); diff --git a/test/playwright.fixtures.ts b/test/playwright.fixtures.ts index 9595dd232a..3b58e454cc 100644 --- a/test/playwright.fixtures.ts +++ b/test/playwright.fixtures.ts @@ -53,6 +53,7 @@ declare global { page: Page; httpsServer: TestServer; browserServer: BrowserServer; + launchPersistent: (options?: Parameters['launchPersistentContext']>[1]) => Promise<{context: BrowserContext, page: Page}>; } interface FixtureParameters { browserName: string; @@ -199,6 +200,20 @@ registerFixture('page', async ({context}, runTest, config, test) => { } }); +registerFixture('launchPersistent', async ({tmpDir, defaultBrowserOptions, browserType}, test) => { + let context; + async function launchPersistent(options) { + if (context) + throw new Error('can only launch one persitent context'); + context = await browserType.launchPersistentContext(tmpDir, {...defaultBrowserOptions, ...options}); + const page = context.pages()[0]; + return {context, page}; + } + await test(launchPersistent); + if (context) + await context.close(); +}); + registerFixture('server', async ({httpService}, test) => { httpService.server.reset(); await test(httpService.server); diff --git a/test/wait-for-selector-1.spec.ts b/test/wait-for-selector-1.spec.ts new file mode 100644 index 0000000000..0e0e5b660c --- /dev/null +++ b/test/wait-for-selector-1.spec.ts @@ -0,0 +1,228 @@ +/** + * 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 './playwright.fixtures'; +import utils from './utils'; + +async function giveItTimeToLog(frame) { + await frame.evaluate(() => new Promise(f => requestAnimationFrame(() => requestAnimationFrame(f)))); + await frame.evaluate(() => new Promise(f => requestAnimationFrame(() => requestAnimationFrame(f)))); +} + +const addElement = tag => document.body.appendChild(document.createElement(tag)); + +it('should throw on waitFor', async({page, server}) => { + await page.goto(server.EMPTY_PAGE); + let error; + await page.waitForSelector('*', { waitFor: 'attached' } as any).catch(e => error = e); + expect(error.message).toContain('options.waitFor is not supported, did you mean options.state?'); +}); + +it('should tolerate waitFor=visible', async({page, server}) => { + await page.goto(server.EMPTY_PAGE); + let error = false; + await page.waitForSelector('*', { waitFor: 'visible' } as any).catch(() => error = true); + expect(error).toBe(false); +}); + +it('should immediately resolve promise if node exists', async({page, server}) => { + await page.goto(server.EMPTY_PAGE); + const frame = page.mainFrame(); + await frame.waitForSelector('*'); + await frame.evaluate(addElement, 'div'); + await frame.waitForSelector('div', { state: 'attached'}); +}); + +it('elementHandle.waitForSelector should immediately resolve if node exists', async({page}) => { + await page.setContent(`extra
target
`); + const div = await page.$('div'); + const span = await div.waitForSelector('span', { state: 'attached' }); + expect(await span.evaluate(e => e.textContent)).toBe('target'); +}); + +it('elementHandle.waitForSelector should wait', async({page}) => { + await page.setContent(`
`); + const div = await page.$('div'); + const promise = div.waitForSelector('span', { state: 'attached' }); + await div.evaluate(div => div.innerHTML = 'target'); + const span = await promise; + expect(await span.evaluate(e => e.textContent)).toBe('target'); +}); + +it('elementHandle.waitForSelector should timeout', async({page}) => { + await page.setContent(`
`); + const div = await page.$('div'); + const error = await div.waitForSelector('span', { timeout: 100 }).catch(e => e); + expect(error.message).toContain('Timeout 100ms exceeded.'); +}); + +it('elementHandle.waitForSelector should throw on navigation', async({page, server}) => { + await page.setContent(`
`); + const div = await page.$('div'); + const promise = div.waitForSelector('span').catch(e => e); + // Give it some time before navigating. + for (let i = 0; i < 10; i++) + await page.evaluate(() => 1); + await page.goto(server.EMPTY_PAGE); + const error = await promise; + expect(error.message).toContain('Execution context was destroyed, most likely because of a navigation'); +}); + +it('should work with removed MutationObserver', async({page, server}) => { + await page.evaluate(() => delete window.MutationObserver); + const [handle] = await Promise.all([ + page.waitForSelector('.zombo'), + page.setContent(`
anything
`), + ]); + expect(await page.evaluate(x => x.textContent, handle)).toBe('anything'); +}); + +it('should resolve promise when node is added', async({page, server}) => { + await page.goto(server.EMPTY_PAGE); + const frame = page.mainFrame(); + const watchdog = frame.waitForSelector('div', { state: 'attached' }); + await frame.evaluate(addElement, 'br'); + await frame.evaluate(addElement, 'div'); + const eHandle = await watchdog; + const tagName = await eHandle.getProperty('tagName').then(e => e.jsonValue()); + expect(tagName).toBe('DIV'); +}); + +it('should report logs while waiting for visible', async({page, server}) => { + await page.goto(server.EMPTY_PAGE); + const frame = page.mainFrame(); + const watchdog = frame.waitForSelector('div', { timeout: 5000 }); + + await frame.evaluate(() => { + const div = document.createElement('div'); + div.className = 'foo bar'; + div.id = 'mydiv'; + div.setAttribute('style', 'display: none'); + div.setAttribute('foo', '123456789012345678901234567890123456789012345678901234567890'); + div.textContent = 'abcdefghijklmnopqrstuvwyxzabcdefghijklmnopqrstuvwyxzabcdefghijklmnopqrstuvwyxz'; + document.body.appendChild(div); + }); + await giveItTimeToLog(frame); + + await frame.evaluate(() => document.querySelector('div').remove()); + await giveItTimeToLog(frame); + + await frame.evaluate(() => { + const div = document.createElement('div'); + div.className = 'another'; + div.style.display = 'none'; + document.body.appendChild(div); + }); + await giveItTimeToLog(frame); + + const error = await watchdog.catch(e => e); + expect(error.message).toContain(`frame.waitForSelector: Timeout 5000ms exceeded.`); + expect(error.message).toContain(`waiting for selector "div" to be visible`); + expect(error.message).toContain(`selector resolved to hidden
`); +}); + +it('should report logs while waiting for hidden', async({page, server}) => { + await page.goto(server.EMPTY_PAGE); + const frame = page.mainFrame(); + await frame.evaluate(() => { + const div = document.createElement('div'); + div.className = 'foo bar'; + div.id = 'mydiv'; + div.textContent = 'hello'; + document.body.appendChild(div); + }); + + const watchdog = frame.waitForSelector('div', { state: 'hidden', timeout: 5000 }); + await giveItTimeToLog(frame); + + await frame.evaluate(() => { + document.querySelector('div').remove(); + const div = document.createElement('div'); + div.className = 'another'; + div.textContent = 'hello'; + document.body.appendChild(div); + }); + await giveItTimeToLog(frame); + + const error = await watchdog.catch(e => e); + expect(error.message).toContain(`frame.waitForSelector: Timeout 5000ms exceeded.`); + expect(error.message).toContain(`waiting for selector "div" to be hidden`); + expect(error.message).toContain(`selector resolved to visible
hello
`); + expect(error.message).toContain(`selector resolved to visible
hello
`); +}); + +it('should resolve promise when node is added in shadow dom', async({page, server}) => { + await page.goto(server.EMPTY_PAGE); + const watchdog = page.waitForSelector('span'); + await page.evaluate(() => { + const div = document.createElement('div'); + div.attachShadow({mode: 'open'}); + document.body.appendChild(div); + }); + await page.evaluate(() => new Promise(f => setTimeout(f, 100))); + await page.evaluate(() => { + const span = document.createElement('span'); + span.textContent = 'Hello from shadow'; + document.querySelector('div').shadowRoot.appendChild(span); + }); + const handle = await watchdog; + expect(await handle.evaluate(e => e.textContent)).toBe('Hello from shadow'); +}); + +it('should work when node is added through innerHTML', async({page, server}) => { + await page.goto(server.EMPTY_PAGE); + const watchdog = page.waitForSelector('h3 div', { state: 'attached'}); + await page.evaluate(addElement, 'span'); + await page.evaluate(() => document.querySelector('span').innerHTML = '

'); + await watchdog; +}); + +it('page.waitForSelector is shortcut for main frame', async({page, server}) => { + await page.goto(server.EMPTY_PAGE); + await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE); + const otherFrame = page.frames()[1]; + const watchdog = page.waitForSelector('div', { state: 'attached' }); + await otherFrame.evaluate(addElement, 'div'); + await page.evaluate(addElement, 'div'); + const eHandle = await watchdog; + expect(await eHandle.ownerFrame()).toBe(page.mainFrame()); +}); + +it('should run in specified frame', async({page, server}) => { + await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE); + await utils.attachFrame(page, 'frame2', server.EMPTY_PAGE); + const frame1 = page.frames()[1]; + const frame2 = page.frames()[2]; + const waitForSelectorPromise = frame2.waitForSelector('div', { state: 'attached' }); + await frame1.evaluate(addElement, 'div'); + await frame2.evaluate(addElement, 'div'); + const eHandle = await waitForSelectorPromise; + expect(await eHandle.ownerFrame()).toBe(frame2); +}); + +it('should throw when frame is detached', async({page, server}) => { + await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE); + const frame = page.frames()[1]; + let waitError = null; + const waitPromise = frame.waitForSelector('.box').catch(e => waitError = e); + await utils.detachFrame(page, 'frame1'); + await waitPromise; + expect(waitError).toBeTruthy(); + expect(waitError.message).toContain('waitForFunction failed: frame got detached.'); +}); diff --git a/test/wait-for-selector.spec.ts b/test/wait-for-selector-2.spec.ts similarity index 58% rename from test/wait-for-selector.spec.ts rename to test/wait-for-selector-2.spec.ts index fd97ce1532..2476defa97 100644 --- a/test/wait-for-selector.spec.ts +++ b/test/wait-for-selector-2.spec.ts @@ -25,208 +25,6 @@ async function giveItTimeToLog(frame) { const addElement = tag => document.body.appendChild(document.createElement(tag)); -it('should throw on waitFor', async({page, server}) => { - await page.goto(server.EMPTY_PAGE); - let error; - await page.waitForSelector('*', { waitFor: 'attached' } as any).catch(e => error = e); - expect(error.message).toContain('options.waitFor is not supported, did you mean options.state?'); -}); - -it('should tolerate waitFor=visible', async({page, server}) => { - await page.goto(server.EMPTY_PAGE); - let error = false; - await page.waitForSelector('*', { waitFor: 'visible' } as any).catch(() => error = true); - expect(error).toBe(false); -}); - -it('should immediately resolve promise if node exists', async({page, server}) => { - await page.goto(server.EMPTY_PAGE); - const frame = page.mainFrame(); - await frame.waitForSelector('*'); - await frame.evaluate(addElement, 'div'); - await frame.waitForSelector('div', { state: 'attached'}); -}); - -it('elementHandle.waitForSelector should immediately resolve if node exists', async({page}) => { - await page.setContent(`extra
target
`); - const div = await page.$('div'); - const span = await div.waitForSelector('span', { state: 'attached' }); - expect(await span.evaluate(e => e.textContent)).toBe('target'); -}); - -it('elementHandle.waitForSelector should wait', async({page}) => { - await page.setContent(`
`); - const div = await page.$('div'); - const promise = div.waitForSelector('span', { state: 'attached' }); - await div.evaluate(div => div.innerHTML = 'target'); - const span = await promise; - expect(await span.evaluate(e => e.textContent)).toBe('target'); -}); - -it('elementHandle.waitForSelector should timeout', async({page}) => { - await page.setContent(`
`); - const div = await page.$('div'); - const error = await div.waitForSelector('span', { timeout: 100 }).catch(e => e); - expect(error.message).toContain('Timeout 100ms exceeded.'); -}); - -it('elementHandle.waitForSelector should throw on navigation', async({page, server}) => { - await page.setContent(`
`); - const div = await page.$('div'); - const promise = div.waitForSelector('span').catch(e => e); - // Give it some time before navigating. - for (let i = 0; i < 10; i++) - await page.evaluate(() => 1); - await page.goto(server.EMPTY_PAGE); - const error = await promise; - expect(error.message).toContain('Execution context was destroyed, most likely because of a navigation'); -}); - -it('should work with removed MutationObserver', async({page, server}) => { - await page.evaluate(() => delete window.MutationObserver); - const [handle] = await Promise.all([ - page.waitForSelector('.zombo'), - page.setContent(`
anything
`), - ]); - expect(await page.evaluate(x => x.textContent, handle)).toBe('anything'); -}); - -it('should resolve promise when node is added', async({page, server}) => { - await page.goto(server.EMPTY_PAGE); - const frame = page.mainFrame(); - const watchdog = frame.waitForSelector('div', { state: 'attached' }); - await frame.evaluate(addElement, 'br'); - await frame.evaluate(addElement, 'div'); - const eHandle = await watchdog; - const tagName = await eHandle.getProperty('tagName').then(e => e.jsonValue()); - expect(tagName).toBe('DIV'); -}); - -it('should report logs while waiting for visible', async({page, server}) => { - await page.goto(server.EMPTY_PAGE); - const frame = page.mainFrame(); - const watchdog = frame.waitForSelector('div', { timeout: 5000 }); - - await frame.evaluate(() => { - const div = document.createElement('div'); - div.className = 'foo bar'; - div.id = 'mydiv'; - div.setAttribute('style', 'display: none'); - div.setAttribute('foo', '123456789012345678901234567890123456789012345678901234567890'); - div.textContent = 'abcdefghijklmnopqrstuvwyxzabcdefghijklmnopqrstuvwyxzabcdefghijklmnopqrstuvwyxz'; - document.body.appendChild(div); - }); - await giveItTimeToLog(frame); - - await frame.evaluate(() => document.querySelector('div').remove()); - await giveItTimeToLog(frame); - - await frame.evaluate(() => { - const div = document.createElement('div'); - div.className = 'another'; - div.style.display = 'none'; - document.body.appendChild(div); - }); - await giveItTimeToLog(frame); - - const error = await watchdog.catch(e => e); - expect(error.message).toContain(`frame.waitForSelector: Timeout 5000ms exceeded.`); - expect(error.message).toContain(`waiting for selector "div" to be visible`); - expect(error.message).toContain(`selector resolved to hidden
`); -}); - -it('should report logs while waiting for hidden', async({page, server}) => { - await page.goto(server.EMPTY_PAGE); - const frame = page.mainFrame(); - await frame.evaluate(() => { - const div = document.createElement('div'); - div.className = 'foo bar'; - div.id = 'mydiv'; - div.textContent = 'hello'; - document.body.appendChild(div); - }); - - const watchdog = frame.waitForSelector('div', { state: 'hidden', timeout: 5000 }); - await giveItTimeToLog(frame); - - await frame.evaluate(() => { - document.querySelector('div').remove(); - const div = document.createElement('div'); - div.className = 'another'; - div.textContent = 'hello'; - document.body.appendChild(div); - }); - await giveItTimeToLog(frame); - - const error = await watchdog.catch(e => e); - expect(error.message).toContain(`frame.waitForSelector: Timeout 5000ms exceeded.`); - expect(error.message).toContain(`waiting for selector "div" to be hidden`); - expect(error.message).toContain(`selector resolved to visible
hello
`); - expect(error.message).toContain(`selector resolved to visible
hello
`); -}); - -it('should resolve promise when node is added in shadow dom', async({page, server}) => { - await page.goto(server.EMPTY_PAGE); - const watchdog = page.waitForSelector('span'); - await page.evaluate(() => { - const div = document.createElement('div'); - div.attachShadow({mode: 'open'}); - document.body.appendChild(div); - }); - await page.evaluate(() => new Promise(f => setTimeout(f, 100))); - await page.evaluate(() => { - const span = document.createElement('span'); - span.textContent = 'Hello from shadow'; - document.querySelector('div').shadowRoot.appendChild(span); - }); - const handle = await watchdog; - expect(await handle.evaluate(e => e.textContent)).toBe('Hello from shadow'); -}); - -it('should work when node is added through innerHTML', async({page, server}) => { - await page.goto(server.EMPTY_PAGE); - const watchdog = page.waitForSelector('h3 div', { state: 'attached'}); - await page.evaluate(addElement, 'span'); - await page.evaluate(() => document.querySelector('span').innerHTML = '

'); - await watchdog; -}); - -it('page.waitForSelector is shortcut for main frame', async({page, server}) => { - await page.goto(server.EMPTY_PAGE); - await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE); - const otherFrame = page.frames()[1]; - const watchdog = page.waitForSelector('div', { state: 'attached' }); - await otherFrame.evaluate(addElement, 'div'); - await page.evaluate(addElement, 'div'); - const eHandle = await watchdog; - expect(await eHandle.ownerFrame()).toBe(page.mainFrame()); -}); - -it('should run in specified frame', async({page, server}) => { - await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE); - await utils.attachFrame(page, 'frame2', server.EMPTY_PAGE); - const frame1 = page.frames()[1]; - const frame2 = page.frames()[2]; - const waitForSelectorPromise = frame2.waitForSelector('div', { state: 'attached' }); - await frame1.evaluate(addElement, 'div'); - await frame2.evaluate(addElement, 'div'); - const eHandle = await waitForSelectorPromise; - expect(await eHandle.ownerFrame()).toBe(frame2); -}); - -it('should throw when frame is detached', async({page, server}) => { - await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE); - const frame = page.frames()[1]; - let waitError = null; - const waitPromise = frame.waitForSelector('.box').catch(e => waitError = e); - await utils.detachFrame(page, 'frame1'); - await waitPromise; - expect(waitError).toBeTruthy(); - expect(waitError.message).toContain('waitForFunction failed: frame got detached.'); -}); - it('should survive cross-process navigation', async({page, server}) => { let boxFound = false; const waitForSelector = page.waitForSelector('.box').then(() => boxFound = true);