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 utils from './utils.js'; 18import expect from 'expect'; 19import { 20 getTestState, 21 setupTestBrowserHooks, 22 setupTestPageAndContextHooks, 23 itFailsFirefox, 24 describeFailsFirefox, 25} from './mocha-utils'; // eslint-disable-line import/extensions 26import os from 'os'; 27 28describe('navigation', function () { 29 setupTestBrowserHooks(); 30 setupTestPageAndContextHooks(); 31 describe('Page.goto', function () { 32 it('should work', async () => { 33 const { page, server } = getTestState(); 34 35 await page.goto(server.EMPTY_PAGE); 36 expect(page.url()).toBe(server.EMPTY_PAGE); 37 }); 38 it('should work with anchor navigation', async () => { 39 const { page, server } = getTestState(); 40 41 await page.goto(server.EMPTY_PAGE); 42 expect(page.url()).toBe(server.EMPTY_PAGE); 43 await page.goto(server.EMPTY_PAGE + '#foo'); 44 expect(page.url()).toBe(server.EMPTY_PAGE + '#foo'); 45 await page.goto(server.EMPTY_PAGE + '#bar'); 46 expect(page.url()).toBe(server.EMPTY_PAGE + '#bar'); 47 }); 48 it('should work with redirects', async () => { 49 const { page, server } = getTestState(); 50 51 server.setRedirect('/redirect/1.html', '/redirect/2.html'); 52 server.setRedirect('/redirect/2.html', '/empty.html'); 53 await page.goto(server.PREFIX + '/redirect/1.html'); 54 expect(page.url()).toBe(server.EMPTY_PAGE); 55 }); 56 it('should navigate to about:blank', async () => { 57 const { page } = getTestState(); 58 59 const response = await page.goto('about:blank'); 60 expect(response).toBe(null); 61 }); 62 it('should return response when page changes its URL after load', async () => { 63 const { page, server } = getTestState(); 64 65 const response = await page.goto(server.PREFIX + '/historyapi.html'); 66 expect(response.status()).toBe(200); 67 }); 68 it('should work with subframes return 204', async () => { 69 const { page, server } = getTestState(); 70 71 server.setRoute('/frames/frame.html', (req, res) => { 72 res.statusCode = 204; 73 res.end(); 74 }); 75 let error = null; 76 await page 77 .goto(server.PREFIX + '/frames/one-frame.html') 78 .catch((error_) => (error = error_)); 79 expect(error).toBe(null); 80 }); 81 it('should fail when server returns 204', async () => { 82 const { page, server, isChrome } = getTestState(); 83 84 server.setRoute('/empty.html', (req, res) => { 85 res.statusCode = 204; 86 res.end(); 87 }); 88 let error = null; 89 await page.goto(server.EMPTY_PAGE).catch((error_) => (error = error_)); 90 expect(error).not.toBe(null); 91 if (isChrome) expect(error.message).toContain('net::ERR_ABORTED'); 92 else expect(error.message).toContain('NS_BINDING_ABORTED'); 93 }); 94 it('should navigate to empty page with domcontentloaded', async () => { 95 const { page, server } = getTestState(); 96 97 const response = await page.goto(server.EMPTY_PAGE, { 98 waitUntil: 'domcontentloaded', 99 }); 100 expect(response.status()).toBe(200); 101 }); 102 it('should work when page calls history API in beforeunload', async () => { 103 const { page, server } = getTestState(); 104 105 await page.goto(server.EMPTY_PAGE); 106 await page.evaluate(() => { 107 window.addEventListener( 108 'beforeunload', 109 () => history.replaceState(null, 'initial', window.location.href), 110 false 111 ); 112 }); 113 const response = await page.goto(server.PREFIX + '/grid.html'); 114 expect(response.status()).toBe(200); 115 }); 116 it( 117 'should navigate to empty page with networkidle0', 118 async () => { 119 const { page, server } = getTestState(); 120 121 const response = await page.goto(server.EMPTY_PAGE, { 122 waitUntil: 'networkidle0', 123 }); 124 expect(response.status()).toBe(200); 125 } 126 ); 127 it( 128 'should navigate to empty page with networkidle2', 129 async () => { 130 const { page, server } = getTestState(); 131 132 const response = await page.goto(server.EMPTY_PAGE, { 133 waitUntil: 'networkidle2', 134 }); 135 expect(response.status()).toBe(200); 136 } 137 ); 138 it('should fail when navigating to bad url', async () => { 139 const { page, isChrome } = getTestState(); 140 141 let error = null; 142 await page.goto('asdfasdf').catch((error_) => (error = error_)); 143 if (isChrome) 144 expect(error.message).toContain('Cannot navigate to invalid URL'); 145 else expect(error.message).toContain('Invalid url'); 146 }); 147 148 /* If you are running this on pre-Catalina versions of macOS this will fail locally. 149 /* Mac OSX Catalina outputs a different message than other platforms. 150 * See https://support.google.com/chrome/thread/18125056?hl=en for details. 151 * If you're running pre-Catalina Mac OSX this test will fail locally. 152 */ 153 const EXPECTED_SSL_CERT_MESSAGE = 154 os.platform() === 'darwin' 155 ? 'net::ERR_CERT_INVALID' 156 : 'net::ERR_CERT_AUTHORITY_INVALID'; 157 158 it('should fail when navigating to bad SSL', async () => { 159 const { page, httpsServer, isChrome } = getTestState(); 160 161 // Make sure that network events do not emit 'undefined'. 162 // @see https://crbug.com/750469 163 const requests = []; 164 page.on('request', () => requests.push('request')); 165 page.on('requestfinished', () => requests.push('requestfinished')); 166 page.on('requestfailed', () => requests.push('requestfailed')); 167 168 let error = null; 169 await page 170 .goto(httpsServer.EMPTY_PAGE) 171 .catch((error_) => (error = error_)); 172 if (isChrome) expect(error.message).toContain(EXPECTED_SSL_CERT_MESSAGE); 173 else expect(error.message).toContain('SSL_ERROR_UNKNOWN'); 174 175 expect(requests.length).toBe(2); 176 expect(requests[0]).toBe('request'); 177 expect(requests[1]).toBe('requestfailed'); 178 }); 179 it('should fail when navigating to bad SSL after redirects', async () => { 180 const { page, server, httpsServer, isChrome } = getTestState(); 181 182 server.setRedirect('/redirect/1.html', '/redirect/2.html'); 183 server.setRedirect('/redirect/2.html', '/empty.html'); 184 let error = null; 185 await page 186 .goto(httpsServer.PREFIX + '/redirect/1.html') 187 .catch((error_) => (error = error_)); 188 if (isChrome) expect(error.message).toContain(EXPECTED_SSL_CERT_MESSAGE); 189 else expect(error.message).toContain('SSL_ERROR_UNKNOWN'); 190 }); 191 it('should throw if networkidle is passed as an option', async () => { 192 const { page, server } = getTestState(); 193 194 let error = null; 195 await page 196 // @ts-expect-error purposefully passing an old option 197 .goto(server.EMPTY_PAGE, { waitUntil: 'networkidle' }) 198 .catch((error_) => (error = error_)); 199 expect(error.message).toContain( 200 '"networkidle" option is no longer supported' 201 ); 202 }); 203 it('should fail when main resources failed to load', async () => { 204 const { page, isChrome } = getTestState(); 205 206 let error = null; 207 await page 208 .goto('http://localhost:44123/non-existing-url') 209 .catch((error_) => (error = error_)); 210 if (isChrome) 211 expect(error.message).toContain('net::ERR_CONNECTION_REFUSED'); 212 else expect(error.message).toContain('NS_ERROR_CONNECTION_REFUSED'); 213 }); 214 it('should fail when exceeding maximum navigation timeout', async () => { 215 const { page, server, puppeteer } = getTestState(); 216 217 // Hang for request to the empty.html 218 server.setRoute('/empty.html', () => {}); 219 let error = null; 220 await page 221 .goto(server.PREFIX + '/empty.html', { timeout: 1 }) 222 .catch((error_) => (error = error_)); 223 expect(error.message).toContain('Navigation timeout of 1 ms exceeded'); 224 expect(error).toBeInstanceOf(puppeteer.errors.TimeoutError); 225 }); 226 it('should fail when exceeding default maximum navigation timeout', async () => { 227 const { page, server, puppeteer } = getTestState(); 228 229 // Hang for request to the empty.html 230 server.setRoute('/empty.html', () => {}); 231 let error = null; 232 page.setDefaultNavigationTimeout(1); 233 await page 234 .goto(server.PREFIX + '/empty.html') 235 .catch((error_) => (error = error_)); 236 expect(error.message).toContain('Navigation timeout of 1 ms exceeded'); 237 expect(error).toBeInstanceOf(puppeteer.errors.TimeoutError); 238 }); 239 it('should fail when exceeding default maximum timeout', async () => { 240 const { page, server, puppeteer } = getTestState(); 241 242 // Hang for request to the empty.html 243 server.setRoute('/empty.html', () => {}); 244 let error = null; 245 page.setDefaultTimeout(1); 246 await page 247 .goto(server.PREFIX + '/empty.html') 248 .catch((error_) => (error = error_)); 249 expect(error.message).toContain('Navigation timeout of 1 ms exceeded'); 250 expect(error).toBeInstanceOf(puppeteer.errors.TimeoutError); 251 }); 252 it('should prioritize default navigation timeout over default timeout', async () => { 253 const { page, server, puppeteer } = getTestState(); 254 255 // Hang for request to the empty.html 256 server.setRoute('/empty.html', () => {}); 257 let error = null; 258 page.setDefaultTimeout(0); 259 page.setDefaultNavigationTimeout(1); 260 await page 261 .goto(server.PREFIX + '/empty.html') 262 .catch((error_) => (error = error_)); 263 expect(error.message).toContain('Navigation timeout of 1 ms exceeded'); 264 expect(error).toBeInstanceOf(puppeteer.errors.TimeoutError); 265 }); 266 it('should disable timeout when its set to 0', async () => { 267 const { page, server } = getTestState(); 268 269 let error = null; 270 let loaded = false; 271 page.once('load', () => (loaded = true)); 272 await page 273 .goto(server.PREFIX + '/grid.html', { timeout: 0, waitUntil: ['load'] }) 274 .catch((error_) => (error = error_)); 275 expect(error).toBe(null); 276 expect(loaded).toBe(true); 277 }); 278 it('should work when navigating to valid url', async () => { 279 const { page, server } = getTestState(); 280 281 const response = await page.goto(server.EMPTY_PAGE); 282 expect(response.ok()).toBe(true); 283 }); 284 it('should work when navigating to data url', async () => { 285 const { page } = getTestState(); 286 287 const response = await page.goto('data:text/html,hello'); 288 expect(response.ok()).toBe(true); 289 }); 290 it('should work when navigating to 404', async () => { 291 const { page, server } = getTestState(); 292 293 const response = await page.goto(server.PREFIX + '/not-found'); 294 expect(response.ok()).toBe(false); 295 expect(response.status()).toBe(404); 296 }); 297 it('should return last response in redirect chain', async () => { 298 const { page, server } = getTestState(); 299 300 server.setRedirect('/redirect/1.html', '/redirect/2.html'); 301 server.setRedirect('/redirect/2.html', '/redirect/3.html'); 302 server.setRedirect('/redirect/3.html', server.EMPTY_PAGE); 303 const response = await page.goto(server.PREFIX + '/redirect/1.html'); 304 expect(response.ok()).toBe(true); 305 expect(response.url()).toBe(server.EMPTY_PAGE); 306 }); 307 it( 308 'should wait for network idle to succeed navigation', 309 async () => { 310 const { page, server } = getTestState(); 311 312 let responses = []; 313 // Hold on to a bunch of requests without answering. 314 server.setRoute('/fetch-request-a.js', (req, res) => 315 responses.push(res) 316 ); 317 server.setRoute('/fetch-request-b.js', (req, res) => 318 responses.push(res) 319 ); 320 server.setRoute('/fetch-request-c.js', (req, res) => 321 responses.push(res) 322 ); 323 server.setRoute('/fetch-request-d.js', (req, res) => 324 responses.push(res) 325 ); 326 const initialFetchResourcesRequested = Promise.all([ 327 server.waitForRequest('/fetch-request-a.js'), 328 server.waitForRequest('/fetch-request-b.js'), 329 server.waitForRequest('/fetch-request-c.js'), 330 ]); 331 const secondFetchResourceRequested = server.waitForRequest( 332 '/fetch-request-d.js' 333 ); 334 335 // Navigate to a page which loads immediately and then does a bunch of 336 // requests via javascript's fetch method. 337 const navigationPromise = page.goto( 338 server.PREFIX + '/networkidle.html', 339 { 340 waitUntil: 'networkidle0', 341 } 342 ); 343 // Track when the navigation gets completed. 344 let navigationFinished = false; 345 navigationPromise.then(() => (navigationFinished = true)); 346 347 // Wait for the page's 'load' event. 348 await new Promise((fulfill) => page.once('load', fulfill)); 349 expect(navigationFinished).toBe(false); 350 351 // Wait for the initial three resources to be requested. 352 await initialFetchResourcesRequested; 353 354 // Expect navigation still to be not finished. 355 expect(navigationFinished).toBe(false); 356 357 // Respond to initial requests. 358 for (const response of responses) { 359 response.statusCode = 404; 360 response.end(`File not found`); 361 } 362 363 // Reset responses array 364 responses = []; 365 366 // Wait for the second round to be requested. 367 await secondFetchResourceRequested; 368 // Expect navigation still to be not finished. 369 expect(navigationFinished).toBe(false); 370 371 // Respond to requests. 372 for (const response of responses) { 373 response.statusCode = 404; 374 response.end(`File not found`); 375 } 376 377 const response = await navigationPromise; 378 // Expect navigation to succeed. 379 expect(response.ok()).toBe(true); 380 } 381 ); 382 it('should not leak listeners during navigation', async () => { 383 const { page, server } = getTestState(); 384 385 let warning = null; 386 const warningHandler = (w) => (warning = w); 387 process.on('warning', warningHandler); 388 for (let i = 0; i < 20; ++i) await page.goto(server.EMPTY_PAGE); 389 process.removeListener('warning', warningHandler); 390 expect(warning).toBe(null); 391 }); 392 it('should not leak listeners during bad navigation', async () => { 393 const { page } = getTestState(); 394 395 let warning = null; 396 const warningHandler = (w) => (warning = w); 397 process.on('warning', warningHandler); 398 for (let i = 0; i < 20; ++i) 399 await page.goto('asdf').catch(() => { 400 /* swallow navigation error */ 401 }); 402 process.removeListener('warning', warningHandler); 403 expect(warning).toBe(null); 404 }); 405 it('should not leak listeners during navigation of 11 pages', async () => { 406 const { context, server } = getTestState(); 407 408 let warning = null; 409 const warningHandler = (w) => (warning = w); 410 process.on('warning', warningHandler); 411 await Promise.all( 412 [...Array(20)].map(async () => { 413 const page = await context.newPage(); 414 await page.goto(server.EMPTY_PAGE); 415 await page.close(); 416 }) 417 ); 418 process.removeListener('warning', warningHandler); 419 expect(warning).toBe(null); 420 }); 421 it( 422 'should navigate to dataURL and fire dataURL requests', 423 async () => { 424 const { page } = getTestState(); 425 426 const requests = []; 427 page.on( 428 'request', 429 (request) => !utils.isFavicon(request) && requests.push(request) 430 ); 431 const dataURL = 'data:text/html,<div>yo</div>'; 432 const response = await page.goto(dataURL); 433 expect(response.status()).toBe(200); 434 expect(requests.length).toBe(1); 435 expect(requests[0].url()).toBe(dataURL); 436 } 437 ); 438 it( 439 'should navigate to URL with hash and fire requests without hash', 440 async () => { 441 const { page, server } = getTestState(); 442 443 const requests = []; 444 page.on( 445 'request', 446 (request) => !utils.isFavicon(request) && requests.push(request) 447 ); 448 const response = await page.goto(server.EMPTY_PAGE + '#hash'); 449 expect(response.status()).toBe(200); 450 expect(response.url()).toBe(server.EMPTY_PAGE); 451 expect(requests.length).toBe(1); 452 expect(requests[0].url()).toBe(server.EMPTY_PAGE); 453 } 454 ); 455 it('should work with self requesting page', async () => { 456 const { page, server } = getTestState(); 457 458 const response = await page.goto(server.PREFIX + '/self-request.html'); 459 expect(response.status()).toBe(200); 460 expect(response.url()).toContain('self-request.html'); 461 }); 462 it('should fail when navigating and show the url at the error message', async () => { 463 const { page, httpsServer } = getTestState(); 464 465 const url = httpsServer.PREFIX + '/redirect/1.html'; 466 let error = null; 467 try { 468 await page.goto(url); 469 } catch (error_) { 470 error = error_; 471 } 472 expect(error.message).toContain(url); 473 }); 474 it('should send referer', async () => { 475 const { page, server } = getTestState(); 476 477 const [request1, request2] = await Promise.all([ 478 server.waitForRequest('/grid.html'), 479 server.waitForRequest('/digits/1.png'), 480 page.goto(server.PREFIX + '/grid.html', { 481 referer: 'http://google.com/', 482 }), 483 ]); 484 expect(request1.headers['referer']).toBe('http://google.com/'); 485 // Make sure subresources do not inherit referer. 486 expect(request2.headers['referer']).toBe(server.PREFIX + '/grid.html'); 487 }); 488 }); 489 490 describe('Page.waitForNavigation', function () { 491 it('should work', async () => { 492 const { page, server } = getTestState(); 493 494 await page.goto(server.EMPTY_PAGE); 495 const [response] = await Promise.all([ 496 page.waitForNavigation(), 497 page.evaluate( 498 (url: string) => (window.location.href = url), 499 server.PREFIX + '/grid.html' 500 ), 501 ]); 502 expect(response.ok()).toBe(true); 503 expect(response.url()).toContain('grid.html'); 504 }); 505 it('should work with both domcontentloaded and load', async () => { 506 const { page, server } = getTestState(); 507 508 let response = null; 509 server.setRoute('/one-style.css', (req, res) => (response = res)); 510 const navigationPromise = page.goto(server.PREFIX + '/one-style.html'); 511 const domContentLoadedPromise = page.waitForNavigation({ 512 waitUntil: 'domcontentloaded', 513 }); 514 515 let bothFired = false; 516 const bothFiredPromise = page 517 .waitForNavigation({ 518 waitUntil: ['load', 'domcontentloaded'], 519 }) 520 .then(() => (bothFired = true)); 521 522 await server.waitForRequest('/one-style.css'); 523 await domContentLoadedPromise; 524 expect(bothFired).toBe(false); 525 response.end(); 526 await bothFiredPromise; 527 await navigationPromise; 528 }); 529 it('should work with clicking on anchor links', async () => { 530 const { page, server } = getTestState(); 531 532 await page.goto(server.EMPTY_PAGE); 533 await page.setContent(`<a href='#foobar'>foobar</a>`); 534 const [response] = await Promise.all([ 535 page.waitForNavigation(), 536 page.click('a'), 537 ]); 538 expect(response).toBe(null); 539 expect(page.url()).toBe(server.EMPTY_PAGE + '#foobar'); 540 }); 541 it('should work with history.pushState()', async () => { 542 const { page, server } = getTestState(); 543 544 await page.goto(server.EMPTY_PAGE); 545 await page.setContent(` 546 <a onclick='javascript:pushState()'>SPA</a> 547 <script> 548 function pushState() { history.pushState({}, '', 'wow.html') } 549 </script> 550 `); 551 const [response] = await Promise.all([ 552 page.waitForNavigation(), 553 page.click('a'), 554 ]); 555 expect(response).toBe(null); 556 expect(page.url()).toBe(server.PREFIX + '/wow.html'); 557 }); 558 it('should work with history.replaceState()', async () => { 559 const { page, server } = getTestState(); 560 561 await page.goto(server.EMPTY_PAGE); 562 await page.setContent(` 563 <a onclick='javascript:replaceState()'>SPA</a> 564 <script> 565 function replaceState() { history.replaceState({}, '', '/replaced.html') } 566 </script> 567 `); 568 const [response] = await Promise.all([ 569 page.waitForNavigation(), 570 page.click('a'), 571 ]); 572 expect(response).toBe(null); 573 expect(page.url()).toBe(server.PREFIX + '/replaced.html'); 574 }); 575 it( 576 'should work with DOM history.back()/history.forward()', 577 async () => { 578 const { page, server } = getTestState(); 579 580 await page.goto(server.EMPTY_PAGE); 581 await page.setContent(` 582 <a id=back onclick='javascript:goBack()'>back</a> 583 <a id=forward onclick='javascript:goForward()'>forward</a> 584 <script> 585 function goBack() { history.back(); } 586 function goForward() { history.forward(); } 587 history.pushState({}, '', '/first.html'); 588 history.pushState({}, '', '/second.html'); 589 </script> 590 `); 591 expect(page.url()).toBe(server.PREFIX + '/second.html'); 592 const [backResponse] = await Promise.all([ 593 page.waitForNavigation(), 594 page.click('a#back'), 595 ]); 596 expect(backResponse).toBe(null); 597 expect(page.url()).toBe(server.PREFIX + '/first.html'); 598 const [forwardResponse] = await Promise.all([ 599 page.waitForNavigation(), 600 page.click('a#forward'), 601 ]); 602 expect(forwardResponse).toBe(null); 603 expect(page.url()).toBe(server.PREFIX + '/second.html'); 604 } 605 ); 606 it( 607 'should work when subframe issues window.stop()', 608 async () => { 609 const { page, server } = getTestState(); 610 611 server.setRoute('/frames/style.css', () => {}); 612 const navigationPromise = page.goto( 613 server.PREFIX + '/frames/one-frame.html' 614 ); 615 const frame = await utils.waitEvent(page, 'frameattached'); 616 await new Promise<void>((fulfill) => { 617 page.on('framenavigated', (f) => { 618 if (f === frame) fulfill(); 619 }); 620 }); 621 await Promise.all([ 622 frame.evaluate(() => window.stop()), 623 navigationPromise, 624 ]); 625 } 626 ); 627 }); 628 629 describe('Page.goBack', function () { 630 it('should work', async () => { 631 const { page, server } = getTestState(); 632 633 await page.goto(server.EMPTY_PAGE); 634 await page.goto(server.PREFIX + '/grid.html'); 635 636 let response = await page.goBack(); 637 expect(response.ok()).toBe(true); 638 expect(response.url()).toContain(server.EMPTY_PAGE); 639 640 response = await page.goForward(); 641 expect(response.ok()).toBe(true); 642 expect(response.url()).toContain('/grid.html'); 643 644 response = await page.goForward(); 645 expect(response).toBe(null); 646 }); 647 it('should work with HistoryAPI', async () => { 648 const { page, server } = getTestState(); 649 650 await page.goto(server.EMPTY_PAGE); 651 await page.evaluate(() => { 652 history.pushState({}, '', '/first.html'); 653 history.pushState({}, '', '/second.html'); 654 }); 655 expect(page.url()).toBe(server.PREFIX + '/second.html'); 656 657 await page.goBack(); 658 expect(page.url()).toBe(server.PREFIX + '/first.html'); 659 await page.goBack(); 660 expect(page.url()).toBe(server.EMPTY_PAGE); 661 await page.goForward(); 662 expect(page.url()).toBe(server.PREFIX + '/first.html'); 663 }); 664 }); 665 666 describe('Frame.goto', function () { 667 it('should navigate subframes', async () => { 668 const { page, server } = getTestState(); 669 670 await page.goto(server.PREFIX + '/frames/one-frame.html'); 671 expect(page.frames()[0].url()).toContain('/frames/one-frame.html'); 672 expect(page.frames()[1].url()).toContain('/frames/frame.html'); 673 674 const response = await page.frames()[1].goto(server.EMPTY_PAGE); 675 expect(response.ok()).toBe(true); 676 expect(response.frame()).toBe(page.frames()[1]); 677 }); 678 it('should reject when frame detaches', async () => { 679 const { page, server } = getTestState(); 680 681 await page.goto(server.PREFIX + '/frames/one-frame.html'); 682 683 server.setRoute('/empty.html', () => {}); 684 const navigationPromise = page 685 .frames()[1] 686 .goto(server.EMPTY_PAGE) 687 .catch((error_) => error_); 688 await server.waitForRequest('/empty.html'); 689 690 await page.$eval('iframe', (frame) => frame.remove()); 691 const error = await navigationPromise; 692 expect(error.message).toBe('Navigating frame was detached'); 693 }); 694 it('should return matching responses', async () => { 695 const { page, server } = getTestState(); 696 697 // Disable cache: otherwise, chromium will cache similar requests. 698 await page.setCacheEnabled(false); 699 await page.goto(server.EMPTY_PAGE); 700 // Attach three frames. 701 const frames = await Promise.all([ 702 utils.attachFrame(page, 'frame1', server.EMPTY_PAGE), 703 utils.attachFrame(page, 'frame2', server.EMPTY_PAGE), 704 utils.attachFrame(page, 'frame3', server.EMPTY_PAGE), 705 ]); 706 // Navigate all frames to the same URL. 707 const serverResponses = []; 708 server.setRoute('/one-style.html', (req, res) => 709 serverResponses.push(res) 710 ); 711 const navigations = []; 712 for (let i = 0; i < 3; ++i) { 713 navigations.push(frames[i].goto(server.PREFIX + '/one-style.html')); 714 await server.waitForRequest('/one-style.html'); 715 } 716 // Respond from server out-of-order. 717 const serverResponseTexts = ['AAA', 'BBB', 'CCC']; 718 for (const i of [1, 2, 0]) { 719 serverResponses[i].end(serverResponseTexts[i]); 720 const response = await navigations[i]; 721 expect(response.frame()).toBe(frames[i]); 722 expect(await response.text()).toBe(serverResponseTexts[i]); 723 } 724 }); 725 }); 726 727 describe('Frame.waitForNavigation', function () { 728 it('should work', async () => { 729 const { page, server } = getTestState(); 730 731 await page.goto(server.PREFIX + '/frames/one-frame.html'); 732 const frame = page.frames()[1]; 733 const [response] = await Promise.all([ 734 frame.waitForNavigation(), 735 frame.evaluate( 736 (url: string) => (window.location.href = url), 737 server.PREFIX + '/grid.html' 738 ), 739 ]); 740 expect(response.ok()).toBe(true); 741 expect(response.url()).toContain('grid.html'); 742 expect(response.frame()).toBe(frame); 743 expect(page.url()).toContain('/frames/one-frame.html'); 744 }); 745 it('should fail when frame detaches', async () => { 746 const { page, server } = getTestState(); 747 748 await page.goto(server.PREFIX + '/frames/one-frame.html'); 749 const frame = page.frames()[1]; 750 751 server.setRoute('/empty.html', () => {}); 752 let error = null; 753 const navigationPromise = frame 754 .waitForNavigation() 755 .catch((error_) => (error = error_)); 756 await Promise.all([ 757 server.waitForRequest('/empty.html'), 758 frame.evaluate(() => ((window as any).location = '/empty.html')), 759 ]); 760 await page.$eval('iframe', (frame) => frame.remove()); 761 await navigationPromise; 762 expect(error.message).toBe('Navigating frame was detached'); 763 }); 764 }); 765 766 describe('Page.reload', function () { 767 it('should work', async () => { 768 const { page, server } = getTestState(); 769 770 await page.goto(server.EMPTY_PAGE); 771 await page.evaluate(() => (globalThis._foo = 10)); 772 await page.reload(); 773 expect(await page.evaluate(() => globalThis._foo)).toBe(undefined); 774 }); 775 }); 776}); 777