/** * 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 { contextTest as it, expect } from '../config/browserTest'; import type { ElementHandle } from 'playwright-core'; declare const renderComponent; declare const e; declare const MaterialUI; it('should block all events when hit target is wrong', async ({ page, server }) => { await page.goto(server.PREFIX + '/input/button.html'); await page.evaluate(() => { const blocker = document.createElement('div'); blocker.style.position = 'absolute'; blocker.style.width = '400px'; blocker.style.height = '400px'; blocker.style.left = '0'; blocker.style.top = '0'; document.body.appendChild(blocker); const allEvents = []; (window as any).allEvents = allEvents; for (const name of ['mousedown', 'mouseup', 'click', 'dblclick', 'auxclick', 'contextmenu', 'pointerdown', 'pointerup']) { window.addEventListener(name, e => allEvents.push(e.type)); blocker.addEventListener(name, e => allEvents.push(e.type)); } }); const error = await page.click('button', { timeout: 1000 }).catch(e => e); expect(error.message).toContain('page.click: Timeout 1000ms exceeded.'); // Give it some time, just in case. await page.waitForTimeout(1000); const allEvents = await page.evaluate(() => (window as any).allEvents); expect(allEvents).toEqual([]); }); it('should block click when mousedown fails', async ({ page, server }) => { await page.goto(server.PREFIX + '/input/button.html'); await page.$eval('button', button => { button.addEventListener('mousemove', () => { button.style.marginLeft = '100px'; }); const allEvents = []; (window as any).allEvents = allEvents; for (const name of ['mousemove', 'mousedown', 'mouseup', 'click', 'dblclick', 'auxclick', 'contextmenu', 'pointerdown', 'pointerup']) button.addEventListener(name, e => allEvents.push(e.type)); }); await page.click('button'); expect(await page.evaluate('result')).toBe('Clicked'); const allEvents = await page.evaluate(() => (window as any).allEvents); expect(allEvents).toEqual([ // First attempt failed. 'mousemove', // Second attempt succeeded. 'mousemove', 'pointerdown', 'mousedown', 'pointerup', 'mouseup', 'click', ]); }); it('should click when element detaches in mousedown', async ({ page, server }) => { await page.goto(server.PREFIX + '/input/button.html'); await page.$eval('button', button => { button.addEventListener('mousedown', () => { (window as any).result = 'Mousedown'; button.remove(); }); }); await page.click('button', { timeout: 1000 }); expect(await page.evaluate('result')).toBe('Mousedown'); }); it('should block all events when hit target is wrong and element detaches', async ({ page, server }) => { await page.goto(server.PREFIX + '/input/button.html'); await page.$eval('button', button => { const blocker = document.createElement('div'); blocker.style.position = 'absolute'; blocker.style.width = '400px'; blocker.style.height = '400px'; blocker.style.left = '0'; blocker.style.top = '0'; document.body.appendChild(blocker); window.addEventListener('mousemove', () => button.remove()); const allEvents = []; (window as any).allEvents = allEvents; for (const name of ['mousedown', 'mouseup', 'click', 'dblclick', 'auxclick', 'contextmenu', 'pointerdown', 'pointerup']) { window.addEventListener(name, e => allEvents.push(e.type)); blocker.addEventListener(name, e => allEvents.push(e.type)); } }); const error = await page.click('button', { timeout: 1000 }).catch(e => e); expect(error.message).toContain('page.click: Timeout 1000ms exceeded.'); // Give it some time, just in case. await page.waitForTimeout(1000); const allEvents = await page.evaluate(() => (window as any).allEvents); expect(allEvents).toEqual([]); }); it('should not block programmatic events', async ({ page, server }) => { await page.goto(server.PREFIX + '/input/button.html'); await page.$eval('button', button => { button.addEventListener('mousemove', () => { button.style.marginLeft = '100px'; button.dispatchEvent(new MouseEvent('click')); }); const allEvents = []; (window as any).allEvents = allEvents; button.addEventListener('click', e => { if (!e.isTrusted) allEvents.push(e.type); }); }); await page.click('button'); expect(await page.evaluate('result')).toBe('Clicked'); const allEvents = await page.evaluate(() => (window as any).allEvents); // We should get one programmatic click on each attempt. expect(allEvents).toEqual([ 'click', 'click', ]); }); it('should click the button again after document.write', async ({ page, server }) => { await page.goto(server.PREFIX + '/input/button.html'); await page.click('button'); expect(await page.evaluate('result')).toBe('Clicked'); await page.evaluate(() => { document.open(); document.write(''); document.close(); }); await page.click('button'); expect(await page.evaluate('result2')).toBe(true); }); it('should work with mui select', async ({ page, server }) => { await page.goto(server.PREFIX + '/mui.html'); await page.evaluate(() => { renderComponent(e(MaterialUI.FormControl, { fullWidth: true }, [ e(MaterialUI.InputLabel, { id: 'demo-simple-select-label' }, ['Age']), e(MaterialUI.Select, { labelId: 'demo-simple-select-label', id: 'demo-simple-select', value: 10, label: 'Age', }, [ e(MaterialUI.MenuItem, { value: 10 }, ['Ten']), e(MaterialUI.MenuItem, { value: 20 }, ['Twenty']), e(MaterialUI.MenuItem, { value: 30 }, ['Thirty']), ]), ])); }); await page.click('div.MuiFormControl-root:has-text("Age")'); await expect(page.locator('text=Thirty')).toBeVisible(); }); it('should work with drag and drop that moves the element under cursor', async ({ page, server }) => { await page.goto(server.PREFIX + '/input/drag-n-drop-manual.html'); await page.dragAndDrop('#from', '#to'); await expect(page.locator('#to')).toHaveText('Dropped'); }); it('should work with block inside inline', async ({ page, server }) => { await page.goto(server.EMPTY_PAGE); await page.setContent(`
Romimine
`); await page.locator('#target').click(); expect(await page.evaluate('window._clicked')).toBe(true); }); it('should work with block-block-block inside inline-inline', async ({ page, server }) => { await page.goto(server.EMPTY_PAGE); await page.setContent(`

Romimine

`); await page.locator('#target').click(); await expect(page).toHaveURL(server.EMPTY_PAGE + '#yay'); }); it('should work with block inside inline in shadow dom', async ({ page, server }) => { await page.goto(server.EMPTY_PAGE); await page.setContent(`
`); await page.locator('#target').click(); expect(await page.evaluate('window._clicked')).toBe(true); }); it('should not click iframe overlaying the target', async ({ page, server }) => { await page.goto(server.EMPTY_PAGE); await page.setContent(`
`); const error = await page.click('text=click-me', { timeout: 1000 }).catch(e => e); expect(await page.evaluate('window._clicked')).toBe(undefined); expect(error.message).toContain(`
outer
">
PINK OVERLAY
`); const target = page.frameLocator('iframe').frameLocator('iframe').locator('text=inner'); const error = await target.click({ timeout: 1000 }).catch(e => e); expect(await page.evaluate('window._clicked')).toBe(undefined); expect(error.message).toContain(`
PINK OVERLAY
intercepts pointer events`); await page.locator('text=overlay').evaluate(e => e.style.display = 'none'); await target.click(); expect(await page.evaluate('window._clicked')).toBe(3); }); it('should click into frame inside closed shadow root', async ({ page, server }) => { await page.goto(server.EMPTY_PAGE); await page.setContent(`
`); const frame = page.frame({ name: 'myframe' }); await frame.locator('text=click me').click(); expect(await page.evaluate('window.__clicked')).toBe(true); }); it('should click an element inside closed shadow root', async ({ page, server }) => { await page.goto(server.EMPTY_PAGE); await page.setContent(`
`); const handle = await page.evaluateHandle('window.__target'); await (handle as any as ElementHandle).click(); expect(await page.evaluate('window.__clicked')).toBe(true); }); it('should detect overlay from another shadow root', async ({ page, server }) => { await page.goto(server.EMPTY_PAGE); await page.setContent(`
`); const error = await page.locator('#container1 >> text=click me').click({ timeout: 2000 }).catch(e => e); expect(error.message).toContain(`
intercepts pointer events`); }); it('should detect overlayed element in a transformed iframe', async ({ page }) => { await page.setContent(` `); const locator = page.frameLocator('iframe').locator('div'); const error = await locator.click({ timeout: 2000 }).catch(e => e); expect(error.message).toContain('
Overlay
intercepts pointer events'); }); it('should click in iframe with padding', async ({ page }) => { await page.setContent(` `); const locator = page.frameLocator('iframe').locator('#target'); await locator.click(); expect(await page.evaluate('window._clicked')).toBe(true); }); it('should click in iframe with padding 2', async ({ page }) => { await page.setContent(` `); const locator = page.frameLocator('iframe').locator('#target'); await locator.click(); expect(await page.evaluate('window._clicked')).toBe(true); }); it('should click in custom element', async ({ page }) => { await page.setContent(` `); await page.locator('input').click(); expect(await page.evaluate('window.__clicked')).toBe(true); });