| 
									
										
										
										
											2020-10-19 10:07:33 -07:00
										 |  |  | /** | 
					
						
							|  |  |  |  * 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. | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2021-04-05 09:18:56 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-03-25 15:05:50 -08:00
										 |  |  | import { contextTest as it, expect } from '../config/browserTest'; | 
					
						
							| 
									
										
										
										
											2022-04-06 13:57:14 -08:00
										 |  |  | import type { ElementHandle } from 'playwright-core'; | 
					
						
							| 
									
										
										
										
											2020-10-19 10:07:33 -07:00
										 |  |  | import type { ServerResponse } from 'http'; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-16 19:58:26 -07:00
										 |  |  | it.use({ hasTouch: true }); | 
					
						
							| 
									
										
										
										
											2020-10-19 10:07:33 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-03-10 19:42:52 +01:00
										 |  |  | it('should send all of the correct events @smoke', async ({ page }) => { | 
					
						
							| 
									
										
										
										
											2020-10-19 10:07:33 -07:00
										 |  |  |   await page.setContent(`
 | 
					
						
							|  |  |  |   <div id="a" style="background: lightblue; width: 50px; height: 50px">a</div> | 
					
						
							|  |  |  |   <div id="b" style="background: pink; width: 50px; height: 50px">b</div> | 
					
						
							|  |  |  | `);
 | 
					
						
							|  |  |  |   await page.tap('#a'); | 
					
						
							|  |  |  |   const eventsHandle = await trackEvents(await page.$('#b')); | 
					
						
							|  |  |  |   await page.tap('#b'); | 
					
						
							| 
									
										
										
										
											2023-01-30 09:53:28 +01:00
										 |  |  |   // webkit doesn't send pointerenter or pointerleave or mouseout
 | 
					
						
							| 
									
										
										
										
											2020-10-19 10:07:33 -07:00
										 |  |  |   expect(await eventsHandle.jsonValue()).toEqual([ | 
					
						
							|  |  |  |     'pointerover',  'pointerenter', | 
					
						
							|  |  |  |     'pointerdown',  'touchstart', | 
					
						
							|  |  |  |     'pointerup',    'pointerout', | 
					
						
							|  |  |  |     'pointerleave', 'touchend', | 
					
						
							|  |  |  |     'mouseover',    'mouseenter', | 
					
						
							|  |  |  |     'mousemove',    'mousedown', | 
					
						
							|  |  |  |     'mouseup',      'click', | 
					
						
							|  |  |  |   ]); | 
					
						
							|  |  |  | }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-30 13:26:13 -07:00
										 |  |  | it('trial run should not tap', async ({ page }) => { | 
					
						
							| 
									
										
										
										
											2021-04-21 12:22:19 -07:00
										 |  |  |   await page.setContent(`
 | 
					
						
							|  |  |  |     <div id="a" style="background: lightblue; width: 50px; height: 50px">a</div> | 
					
						
							|  |  |  |     <div id="b" style="background: pink; width: 50px; height: 50px">b</div> | 
					
						
							|  |  |  |   `);
 | 
					
						
							|  |  |  |   await page.tap('#a'); | 
					
						
							|  |  |  |   const eventsHandle = await trackEvents(await page.$('#b')); | 
					
						
							|  |  |  |   await page.tap('#b', { trial: true }); | 
					
						
							| 
									
										
										
										
											2022-03-08 16:42:39 -08:00
										 |  |  |   const expected = ['pointerover', 'pointerenter', 'pointerout', 'pointerleave']; | 
					
						
							| 
									
										
										
										
											2021-11-05 17:31:28 -07:00
										 |  |  |   expect(await eventsHandle.jsonValue()).toEqual(expected); | 
					
						
							| 
									
										
										
										
											2021-04-21 12:22:19 -07:00
										 |  |  | }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-30 13:26:13 -07:00
										 |  |  | it('should not send mouse events touchstart is canceled', async ({ page }) => { | 
					
						
							| 
									
										
										
										
											2020-11-16 10:14:37 -08:00
										 |  |  |   await page.setContent(`<div style="width: 50px; height: 50px; background: red">`); | 
					
						
							| 
									
										
										
										
											2020-10-19 10:07:33 -07:00
										 |  |  |   await page.evaluate(() => { | 
					
						
							|  |  |  |     // touchstart is not cancelable unless passive is false
 | 
					
						
							| 
									
										
										
										
											2021-09-27 18:58:08 +02:00
										 |  |  |     document.addEventListener('touchstart', t => t.preventDefault(), { passive: false }); | 
					
						
							| 
									
										
										
										
											2020-10-19 10:07:33 -07:00
										 |  |  |   }); | 
					
						
							| 
									
										
										
										
											2020-11-16 10:14:37 -08:00
										 |  |  |   const eventsHandle = await trackEvents(await page.$('div')); | 
					
						
							|  |  |  |   await page.tap('div'); | 
					
						
							| 
									
										
										
										
											2020-10-19 10:07:33 -07:00
										 |  |  |   expect(await eventsHandle.jsonValue()).toEqual([ | 
					
						
							|  |  |  |     'pointerover',  'pointerenter', | 
					
						
							|  |  |  |     'pointerdown',  'touchstart', | 
					
						
							|  |  |  |     'pointerup',    'pointerout', | 
					
						
							|  |  |  |     'pointerleave', 'touchend', | 
					
						
							|  |  |  |   ]); | 
					
						
							|  |  |  | }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-30 13:26:13 -07:00
										 |  |  | it('should not send mouse events when touchend is canceled', async ({ page }) => { | 
					
						
							| 
									
										
										
										
											2020-11-16 10:14:37 -08:00
										 |  |  |   await page.setContent(`<div style="width: 50px; height: 50px; background: red">`); | 
					
						
							| 
									
										
										
										
											2020-10-19 10:07:33 -07:00
										 |  |  |   await page.evaluate(() => { | 
					
						
							|  |  |  |     document.addEventListener('touchend', t => t.preventDefault()); | 
					
						
							|  |  |  |   }); | 
					
						
							| 
									
										
										
										
											2020-11-16 10:14:37 -08:00
										 |  |  |   const eventsHandle = await trackEvents(await page.$('div')); | 
					
						
							|  |  |  |   await page.tap('div'); | 
					
						
							| 
									
										
										
										
											2020-10-19 10:07:33 -07:00
										 |  |  |   expect(await eventsHandle.jsonValue()).toEqual([ | 
					
						
							|  |  |  |     'pointerover',  'pointerenter', | 
					
						
							|  |  |  |     'pointerdown',  'touchstart', | 
					
						
							|  |  |  |     'pointerup',    'pointerout', | 
					
						
							|  |  |  |     'pointerleave', 'touchend', | 
					
						
							|  |  |  |   ]); | 
					
						
							|  |  |  | }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-27 18:58:08 +02:00
										 |  |  | it('should wait for a navigation caused by a tap', async ({ page, server }) => { | 
					
						
							| 
									
										
										
										
											2020-10-19 10:07:33 -07:00
										 |  |  |   await page.goto(server.EMPTY_PAGE); | 
					
						
							|  |  |  |   await page.setContent(`
 | 
					
						
							|  |  |  |   <a href="/intercept-this.html">link</a>; | 
					
						
							|  |  |  | `);
 | 
					
						
							|  |  |  |   const responsePromise = new Promise<ServerResponse>(resolve => { | 
					
						
							|  |  |  |     server.setRoute('/intercept-this.html', (handler, response) => { | 
					
						
							|  |  |  |       resolve(response); | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  |   let resolved = false; | 
					
						
							|  |  |  |   const tapPromise = page.tap('a').then(() => resolved = true); | 
					
						
							|  |  |  |   const response = await responsePromise; | 
					
						
							| 
									
										
										
										
											2023-01-30 09:53:28 +01:00
										 |  |  |   // make sure the tap doesn't resolve too early
 | 
					
						
							| 
									
										
										
										
											2020-10-19 10:07:33 -07:00
										 |  |  |   await new Promise(x => setTimeout(x, 100)); | 
					
						
							|  |  |  |   expect(resolved).toBe(false); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   response.end('foo'); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   await tapPromise; | 
					
						
							|  |  |  |   expect(resolved).toBe(true); | 
					
						
							|  |  |  | }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-30 13:26:13 -07:00
										 |  |  | it('should work with modifiers', async ({ page  }) => { | 
					
						
							| 
									
										
										
										
											2020-10-19 10:07:33 -07:00
										 |  |  |   await page.setContent('hello world'); | 
					
						
							|  |  |  |   const altKeyPromise = page.evaluate(() => new Promise(resolve => { | 
					
						
							|  |  |  |     document.addEventListener('touchstart', event => { | 
					
						
							|  |  |  |       resolve(event.altKey); | 
					
						
							| 
									
										
										
										
											2021-09-27 18:58:08 +02:00
										 |  |  |     }, { passive: false }); | 
					
						
							| 
									
										
										
										
											2020-10-19 10:07:33 -07:00
										 |  |  |   })); | 
					
						
							|  |  |  |   // make sure the evals hit the page
 | 
					
						
							|  |  |  |   await page.evaluate(() => void 0); | 
					
						
							|  |  |  |   await page.tap('body', { | 
					
						
							|  |  |  |     modifiers: ['Alt'] | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  |   expect(await altKeyPromise).toBe(true); | 
					
						
							|  |  |  | }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-30 13:26:13 -07:00
										 |  |  | it('should send well formed touch points', async ({ page }) => { | 
					
						
							| 
									
										
										
										
											2020-10-19 10:07:33 -07:00
										 |  |  |   const promises = Promise.all([ | 
					
						
							|  |  |  |     page.evaluate(() => new Promise(resolve => { | 
					
						
							|  |  |  |       document.addEventListener('touchstart', event => { | 
					
						
							|  |  |  |         resolve([...event.touches].map(t => ({ | 
					
						
							|  |  |  |           identifier: t.identifier, | 
					
						
							|  |  |  |           clientX: t.clientX, | 
					
						
							|  |  |  |           clientY: t.clientY, | 
					
						
							|  |  |  |           pageX: t.pageX, | 
					
						
							|  |  |  |           pageY: t.pageY, | 
					
						
							|  |  |  |           radiusX: 'radiusX' in t ? t.radiusX : t['webkitRadiusX'], | 
					
						
							|  |  |  |           radiusY: 'radiusY' in t ? t.radiusY : t['webkitRadiusY'], | 
					
						
							|  |  |  |           rotationAngle: 'rotationAngle' in t ? t.rotationAngle : t['webkitRotationAngle'], | 
					
						
							|  |  |  |           force: 'force' in t ? t.force : t['webkitForce'], | 
					
						
							|  |  |  |         }))); | 
					
						
							|  |  |  |       }, false); | 
					
						
							|  |  |  |     })), | 
					
						
							|  |  |  |     page.evaluate(() => new Promise(resolve => { | 
					
						
							|  |  |  |       document.addEventListener('touchend', event => { | 
					
						
							|  |  |  |         resolve([...event.touches].map(t => ({ | 
					
						
							|  |  |  |           identifier: t.identifier, | 
					
						
							|  |  |  |           clientX: t.clientX, | 
					
						
							|  |  |  |           clientY: t.clientY, | 
					
						
							|  |  |  |           pageX: t.pageX, | 
					
						
							|  |  |  |           pageY: t.pageY, | 
					
						
							|  |  |  |           radiusX: 'radiusX' in t ? t.radiusX : t['webkitRadiusX'], | 
					
						
							|  |  |  |           radiusY: 'radiusY' in t ? t.radiusY : t['webkitRadiusY'], | 
					
						
							|  |  |  |           rotationAngle: 'rotationAngle' in t ? t.rotationAngle : t['webkitRotationAngle'], | 
					
						
							|  |  |  |           force: 'force' in t ? t.force : t['webkitForce'], | 
					
						
							|  |  |  |         }))); | 
					
						
							|  |  |  |       }, false); | 
					
						
							|  |  |  |     })), | 
					
						
							|  |  |  |   ]); | 
					
						
							|  |  |  |   // make sure the evals hit the page
 | 
					
						
							|  |  |  |   await page.evaluate(() => void 0); | 
					
						
							|  |  |  |   await page.touchscreen.tap(40, 60); | 
					
						
							|  |  |  |   const [touchstart, touchend] = await promises; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   expect(touchstart).toEqual([{ | 
					
						
							|  |  |  |     clientX: 40, | 
					
						
							|  |  |  |     clientY: 60, | 
					
						
							|  |  |  |     force: 1, | 
					
						
							|  |  |  |     identifier: 0, | 
					
						
							|  |  |  |     pageX: 40, | 
					
						
							|  |  |  |     pageY: 60, | 
					
						
							|  |  |  |     radiusX: 1, | 
					
						
							|  |  |  |     radiusY: 1, | 
					
						
							|  |  |  |     rotationAngle: 0, | 
					
						
							|  |  |  |   }]); | 
					
						
							|  |  |  |   expect(touchend).toEqual([]); | 
					
						
							|  |  |  | }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-30 13:26:13 -07:00
										 |  |  | it('should wait until an element is visible to tap it', async ({ page }) => { | 
					
						
							| 
									
										
										
										
											2020-10-19 10:07:33 -07:00
										 |  |  |   const div = await page.evaluateHandle(() => { | 
					
						
							|  |  |  |     const button = document.createElement('button'); | 
					
						
							|  |  |  |     button.textContent = 'not clicked'; | 
					
						
							|  |  |  |     document.body.appendChild(button); | 
					
						
							|  |  |  |     button.style.display = 'none'; | 
					
						
							|  |  |  |     return button; | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  |   const tapPromise = div.tap(); | 
					
						
							|  |  |  |   await div.evaluate(div => div.onclick = () => div.textContent = 'clicked'); | 
					
						
							|  |  |  |   await div.evaluate(div => div.style.display = 'block'); | 
					
						
							|  |  |  |   await tapPromise; | 
					
						
							|  |  |  |   expect(await div.textContent()).toBe('clicked'); | 
					
						
							|  |  |  | }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | async function trackEvents(target: ElementHandle) { | 
					
						
							|  |  |  |   const eventsHandle = await target.evaluateHandle(target => { | 
					
						
							|  |  |  |     const events: string[] = []; | 
					
						
							|  |  |  |     for (const event of [ | 
					
						
							|  |  |  |       'mousedown', 'mouseenter', 'mouseleave', 'mousemove', 'mouseout', 'mouseover', 'mouseup', 'click', | 
					
						
							|  |  |  |       'pointercancel', 'pointerdown', 'pointerenter', 'pointerleave', 'pointermove', 'pointerout', 'pointerover', 'pointerup', | 
					
						
							|  |  |  |       'touchstart', 'touchend', 'touchmove', 'touchcancel', | 
					
						
							|  |  |  |     ]) | 
					
						
							|  |  |  |       target.addEventListener(event, () => events.push(event), false); | 
					
						
							|  |  |  |     return events; | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  |   return eventsHandle; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2021-08-04 20:11:17 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | it.describe('locators', () => { | 
					
						
							|  |  |  |   it('should send all of the correct events', async ({ page }) => { | 
					
						
							|  |  |  |     await page.setContent(`
 | 
					
						
							|  |  |  |       <div id="a" style="background: lightblue; width: 50px; height: 50px">a</div> | 
					
						
							|  |  |  |       <div id="b" style="background: pink; width: 50px; height: 50px">b</div> | 
					
						
							|  |  |  |     `);
 | 
					
						
							|  |  |  |     await page.locator('#a').tap(); | 
					
						
							|  |  |  |     await page.locator('#b').tap(); | 
					
						
							|  |  |  |   }); | 
					
						
							| 
									
										
										
										
											2021-12-29 19:51:28 -07:00
										 |  |  | }); |