1/** 2 * Copyright 2018 Google Inc. All rights reserved. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17import expect from 'expect'; 18import { 19 getTestState, 20 setupTestPageAndContextHooks, 21 setupTestBrowserHooks, 22 itFailsFirefox, 23} from './mocha-utils'; // eslint-disable-line import/extensions 24import utils from './utils.js'; 25 26describe('Page.click', function () { 27 setupTestBrowserHooks(); 28 setupTestPageAndContextHooks(); 29 it('should click the button', async () => { 30 const { page, server } = getTestState(); 31 32 await page.goto(server.PREFIX + '/input/button.html'); 33 await page.click('button'); 34 expect(await page.evaluate(() => globalThis.result)).toBe('Clicked'); 35 }); 36 it('should click svg', async () => { 37 const { page } = getTestState(); 38 39 await page.setContent(` 40 <svg height="100" width="100"> 41 <circle onclick="javascript:window.__CLICKED=42" cx="50" cy="50" r="40" stroke="black" stroke-width="3" fill="red" /> 42 </svg> 43 `); 44 await page.click('circle'); 45 expect(await page.evaluate(() => globalThis.__CLICKED)).toBe(42); 46 }); 47 it( 48 'should click the button if window.Node is removed', 49 async () => { 50 const { page, server } = getTestState(); 51 52 await page.goto(server.PREFIX + '/input/button.html'); 53 await page.evaluate(() => delete window.Node); 54 await page.click('button'); 55 expect(await page.evaluate(() => globalThis.result)).toBe('Clicked'); 56 } 57 ); 58 // @see https://github.com/puppeteer/puppeteer/issues/4281 59 it('should click on a span with an inline element inside', async () => { 60 const { page } = getTestState(); 61 62 await page.setContent(` 63 <style> 64 span::before { 65 content: 'q'; 66 } 67 </style> 68 <span onclick='javascript:window.CLICKED=42'></span> 69 `); 70 await page.click('span'); 71 expect(await page.evaluate(() => globalThis.CLICKED)).toBe(42); 72 }); 73 it('should not throw UnhandledPromiseRejection when page closes', async () => { 74 const { page } = getTestState(); 75 76 const newPage = await page.browser().newPage(); 77 await Promise.all([newPage.close(), newPage.mouse.click(1, 2)]).catch( 78 () => {} 79 ); 80 }); 81 it('should click the button after navigation ', async () => { 82 const { page, server } = getTestState(); 83 84 await page.goto(server.PREFIX + '/input/button.html'); 85 await page.click('button'); 86 await page.goto(server.PREFIX + '/input/button.html'); 87 await page.click('button'); 88 expect(await page.evaluate(() => globalThis.result)).toBe('Clicked'); 89 }); 90 it('should click with disabled javascript', async () => { 91 const { page, server } = getTestState(); 92 93 await page.setJavaScriptEnabled(false); 94 await page.goto(server.PREFIX + '/wrappedlink.html'); 95 await Promise.all([page.click('a'), page.waitForNavigation()]); 96 expect(page.url()).toBe(server.PREFIX + '/wrappedlink.html#clicked'); 97 }); 98 it('should click when one of inline box children is outside of viewport', async () => { 99 const { page } = getTestState(); 100 101 await page.setContent(` 102 <style> 103 i { 104 position: absolute; 105 top: -1000px; 106 } 107 </style> 108 <span onclick='javascript:window.CLICKED = 42;'><i>woof</i><b>doggo</b></span> 109 `); 110 await page.click('span'); 111 expect(await page.evaluate(() => globalThis.CLICKED)).toBe(42); 112 }); 113 it('should select the text by triple clicking', async () => { 114 const { page, server } = getTestState(); 115 116 await page.goto(server.PREFIX + '/input/textarea.html'); 117 await page.focus('textarea'); 118 const text = 119 "This is the text that we are going to try to select. Let's see how it goes."; 120 await page.keyboard.type(text); 121 await page.click('textarea'); 122 await page.click('textarea', { clickCount: 2 }); 123 await page.click('textarea', { clickCount: 3 }); 124 expect( 125 await page.evaluate(() => { 126 const textarea = document.querySelector('textarea'); 127 return textarea.value.substring( 128 textarea.selectionStart, 129 textarea.selectionEnd 130 ); 131 }) 132 ).toBe(text); 133 }); 134 it('should click offscreen buttons', async () => { 135 const { page, server } = getTestState(); 136 137 await page.goto(server.PREFIX + '/offscreenbuttons.html'); 138 const messages = []; 139 page.on('console', (msg) => messages.push(msg.text())); 140 for (let i = 0; i < 11; ++i) { 141 // We might've scrolled to click a button - reset to (0, 0). 142 await page.evaluate(() => window.scrollTo(0, 0)); 143 await page.click(`#btn${i}`); 144 } 145 expect(messages).toEqual([ 146 'button #0 clicked', 147 'button #1 clicked', 148 'button #2 clicked', 149 'button #3 clicked', 150 'button #4 clicked', 151 'button #5 clicked', 152 'button #6 clicked', 153 'button #7 clicked', 154 'button #8 clicked', 155 'button #9 clicked', 156 'button #10 clicked', 157 ]); 158 }); 159 160 it('should click wrapped links', async () => { 161 const { page, server } = getTestState(); 162 163 await page.goto(server.PREFIX + '/wrappedlink.html'); 164 await page.click('a'); 165 expect(await page.evaluate(() => globalThis.__clicked)).toBe(true); 166 }); 167 168 it('should click on checkbox input and toggle', async () => { 169 const { page, server } = getTestState(); 170 171 await page.goto(server.PREFIX + '/input/checkbox.html'); 172 expect(await page.evaluate(() => globalThis.result.check)).toBe(null); 173 await page.click('input#agree'); 174 expect(await page.evaluate(() => globalThis.result.check)).toBe(true); 175 expect(await page.evaluate(() => globalThis.result.events)).toEqual([ 176 'mouseover', 177 'mouseenter', 178 'mousemove', 179 'mousedown', 180 'mouseup', 181 'click', 182 'input', 183 'change', 184 ]); 185 await page.click('input#agree'); 186 expect(await page.evaluate(() => globalThis.result.check)).toBe(false); 187 }); 188 189 it('should click on checkbox label and toggle', async () => { 190 const { page, server } = getTestState(); 191 192 await page.goto(server.PREFIX + '/input/checkbox.html'); 193 expect(await page.evaluate(() => globalThis.result.check)).toBe(null); 194 await page.click('label[for="agree"]'); 195 expect(await page.evaluate(() => globalThis.result.check)).toBe(true); 196 expect(await page.evaluate(() => globalThis.result.events)).toEqual([ 197 'click', 198 'input', 199 'change', 200 ]); 201 await page.click('label[for="agree"]'); 202 expect(await page.evaluate(() => globalThis.result.check)).toBe(false); 203 }); 204 205 it('should fail to click a missing button', async () => { 206 const { page, server } = getTestState(); 207 208 await page.goto(server.PREFIX + '/input/button.html'); 209 let error = null; 210 await page 211 .click('button.does-not-exist') 212 .catch((error_) => (error = error_)); 213 expect(error.message).toBe( 214 'No node found for selector: button.does-not-exist' 215 ); 216 }); 217 // @see https://github.com/puppeteer/puppeteer/issues/161 218 it('should not hang with touch-enabled viewports', async () => { 219 const { page, puppeteer } = getTestState(); 220 221 await page.setViewport(puppeteer.devices['iPhone 6'].viewport); 222 await page.mouse.down(); 223 await page.mouse.move(100, 10); 224 await page.mouse.up(); 225 }); 226 it('should scroll and click the button', async () => { 227 const { page, server } = getTestState(); 228 229 await page.goto(server.PREFIX + '/input/scrollable.html'); 230 await page.click('#button-5'); 231 expect( 232 await page.evaluate(() => document.querySelector('#button-5').textContent) 233 ).toBe('clicked'); 234 await page.click('#button-80'); 235 expect( 236 await page.evaluate( 237 () => document.querySelector('#button-80').textContent 238 ) 239 ).toBe('clicked'); 240 }); 241 // See https://github.com/puppeteer/puppeteer/issues/7175 242 it('should double click the button', async () => { 243 const { page, server } = getTestState(); 244 245 await page.goto(server.PREFIX + '/input/button.html'); 246 await page.evaluate(() => { 247 globalThis.double = false; 248 const button = document.querySelector('button'); 249 button.addEventListener('dblclick', () => { 250 globalThis.double = true; 251 }); 252 }); 253 const button = await page.$('button'); 254 await button.click({ clickCount: 2 }); 255 expect(await page.evaluate('double')).toBe(true); 256 expect(await page.evaluate('result')).toBe('Clicked'); 257 }); 258 it('should click a partially obscured button', async () => { 259 const { page, server } = getTestState(); 260 261 await page.goto(server.PREFIX + '/input/button.html'); 262 await page.evaluate(() => { 263 const button = document.querySelector('button'); 264 button.textContent = 'Some really long text that will go offscreen'; 265 button.style.position = 'absolute'; 266 button.style.left = '368px'; 267 }); 268 await page.click('button'); 269 expect(await page.evaluate(() => globalThis.result)).toBe('Clicked'); 270 }); 271 it('should click a rotated button', async () => { 272 const { page, server } = getTestState(); 273 274 await page.goto(server.PREFIX + '/input/rotatedButton.html'); 275 await page.click('button'); 276 expect(await page.evaluate(() => globalThis.result)).toBe('Clicked'); 277 }); 278 it('should fire contextmenu event on right click', async () => { 279 const { page, server } = getTestState(); 280 281 await page.goto(server.PREFIX + '/input/scrollable.html'); 282 await page.click('#button-8', { button: 'right' }); 283 expect( 284 await page.evaluate(() => document.querySelector('#button-8').textContent) 285 ).toBe('context menu'); 286 }); 287 // @see https://github.com/puppeteer/puppeteer/issues/206 288 it('should click links which cause navigation', async () => { 289 const { page, server } = getTestState(); 290 291 await page.setContent(`<a href="${server.EMPTY_PAGE}">empty.html</a>`); 292 // This await should not hang. 293 await page.click('a'); 294 }); 295 it('should click the button inside an iframe', async () => { 296 const { page, server } = getTestState(); 297 298 await page.goto(server.EMPTY_PAGE); 299 await page.setContent('<div style="width:100px;height:100px">spacer</div>'); 300 await utils.attachFrame( 301 page, 302 'button-test', 303 server.PREFIX + '/input/button.html' 304 ); 305 const frame = page.frames()[1]; 306 const button = await frame.$('button'); 307 await button.click(); 308 expect(await frame.evaluate(() => globalThis.result)).toBe('Clicked'); 309 }); 310 // @see https://github.com/puppeteer/puppeteer/issues/4110 311 xit('should click the button with fixed position inside an iframe', async () => { 312 const { page, server } = getTestState(); 313 314 await page.goto(server.EMPTY_PAGE); 315 await page.setViewport({ width: 500, height: 500 }); 316 await page.setContent( 317 '<div style="width:100px;height:2000px">spacer</div>' 318 ); 319 await utils.attachFrame( 320 page, 321 'button-test', 322 server.CROSS_PROCESS_PREFIX + '/input/button.html' 323 ); 324 const frame = page.frames()[1]; 325 await frame.$eval('button', (button: HTMLElement) => 326 button.style.setProperty('position', 'fixed') 327 ); 328 await frame.click('button'); 329 expect(await frame.evaluate(() => globalThis.result)).toBe('Clicked'); 330 }); 331 it( 332 'should click the button with deviceScaleFactor set', 333 async () => { 334 const { page, server } = getTestState(); 335 336 await page.setViewport({ width: 400, height: 400, deviceScaleFactor: 5 }); 337 expect(await page.evaluate(() => window.devicePixelRatio)).toBe(5); 338 await page.setContent( 339 '<div style="width:100px;height:100px">spacer</div>' 340 ); 341 await utils.attachFrame( 342 page, 343 'button-test', 344 server.PREFIX + '/input/button.html' 345 ); 346 const frame = page.frames()[1]; 347 const button = await frame.$('button'); 348 await button.click(); 349 expect(await frame.evaluate(() => globalThis.result)).toBe('Clicked'); 350 } 351 ); 352}); 353