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 fs from 'fs'; 18import path from 'path'; 19import utils from './utils.js'; 20import expect from 'expect'; 21import { 22 getTestState, 23 setupTestBrowserHooks, 24 setupTestPageAndContextHooks, 25 describeFailsFirefox, 26} from './mocha-utils'; // eslint-disable-line import/extensions 27 28describe('request interception', function () { 29 setupTestBrowserHooks(); 30 setupTestPageAndContextHooks(); 31 describe('Page.setRequestInterception', function () { 32 it('should intercept', async () => { 33 const { page, server } = getTestState(); 34 35 await page.setRequestInterception(true); 36 page.on('request', (request) => { 37 if (utils.isFavicon(request)) { 38 request.continue(); 39 return; 40 } 41 expect(request.url()).toContain('empty.html'); 42 expect(request.headers()['user-agent']).toBeTruthy(); 43 expect(request.method()).toBe('GET'); 44 expect(request.postData()).toBe(undefined); 45 expect(request.isNavigationRequest()).toBe(true); 46 expect(request.resourceType()).toBe('document'); 47 expect(request.frame() === page.mainFrame()).toBe(true); 48 expect(request.frame().url()).toBe('about:blank'); 49 request.continue(); 50 }); 51 const response = await page.goto(server.EMPTY_PAGE); 52 expect(response.ok()).toBe(true); 53 expect(response.remoteAddress().port).toBe(server.PORT); 54 }); 55 it('should work when POST is redirected with 302', async () => { 56 const { page, server } = getTestState(); 57 58 server.setRedirect('/rredirect', '/empty.html'); 59 await page.goto(server.EMPTY_PAGE); 60 await page.setRequestInterception(true); 61 page.on('request', (request) => request.continue()); 62 await page.setContent(` 63 <form action='/rredirect' method='post'> 64 <input type="hidden" id="foo" name="foo" value="FOOBAR"> 65 </form> 66 `); 67 await Promise.all([ 68 page.$eval('form', (form: HTMLFormElement) => form.submit()), 69 page.waitForNavigation(), 70 ]); 71 }); 72 // @see https://github.com/puppeteer/puppeteer/issues/3973 73 it('should work when header manipulation headers with redirect', async () => { 74 const { page, server } = getTestState(); 75 76 server.setRedirect('/rrredirect', '/empty.html'); 77 await page.setRequestInterception(true); 78 page.on('request', (request) => { 79 const headers = Object.assign({}, request.headers(), { 80 foo: 'bar', 81 }); 82 request.continue({ headers }); 83 }); 84 await page.goto(server.PREFIX + '/rrredirect'); 85 }); 86 // @see https://github.com/puppeteer/puppeteer/issues/4743 87 it('should be able to remove headers', async () => { 88 const { page, server } = getTestState(); 89 90 await page.setRequestInterception(true); 91 page.on('request', (request) => { 92 const headers = Object.assign({}, request.headers(), { 93 foo: 'bar', 94 origin: undefined, // remove "origin" header 95 }); 96 request.continue({ headers }); 97 }); 98 99 const [serverRequest] = await Promise.all([ 100 server.waitForRequest('/empty.html'), 101 page.goto(server.PREFIX + '/empty.html'), 102 ]); 103 104 expect(serverRequest.headers.origin).toBe(undefined); 105 }); 106 it('should contain referer header', async () => { 107 const { page, server } = getTestState(); 108 109 await page.setRequestInterception(true); 110 const requests = []; 111 page.on('request', (request) => { 112 if (!utils.isFavicon(request)) requests.push(request); 113 request.continue(); 114 }); 115 await page.goto(server.PREFIX + '/one-style.html'); 116 expect(requests[1].url()).toContain('/one-style.css'); 117 expect(requests[1].headers().referer).toContain('/one-style.html'); 118 }); 119 it('should properly return navigation response when URL has cookies', async () => { 120 const { page, server } = getTestState(); 121 122 // Setup cookie. 123 await page.goto(server.EMPTY_PAGE); 124 await page.setCookie({ name: 'foo', value: 'bar' }); 125 126 // Setup request interception. 127 await page.setRequestInterception(true); 128 page.on('request', (request) => request.continue()); 129 const response = await page.reload(); 130 expect(response.status()).toBe(200); 131 }); 132 it('should stop intercepting', async () => { 133 const { page, server } = getTestState(); 134 135 await page.setRequestInterception(true); 136 page.once('request', (request) => request.continue()); 137 await page.goto(server.EMPTY_PAGE); 138 await page.setRequestInterception(false); 139 await page.goto(server.EMPTY_PAGE); 140 }); 141 it('should show custom HTTP headers', async () => { 142 const { page, server } = getTestState(); 143 144 await page.setExtraHTTPHeaders({ 145 foo: 'bar', 146 }); 147 await page.setRequestInterception(true); 148 page.on('request', (request) => { 149 expect(request.headers()['foo']).toBe('bar'); 150 request.continue(); 151 }); 152 const response = await page.goto(server.EMPTY_PAGE); 153 expect(response.ok()).toBe(true); 154 }); 155 // @see https://github.com/puppeteer/puppeteer/issues/4337 156 it('should work with redirect inside sync XHR', async () => { 157 const { page, server } = getTestState(); 158 159 await page.goto(server.EMPTY_PAGE); 160 server.setRedirect('/logo.png', '/pptr.png'); 161 await page.setRequestInterception(true); 162 page.on('request', (request) => request.continue()); 163 const status = await page.evaluate(async () => { 164 const request = new XMLHttpRequest(); 165 request.open('GET', '/logo.png', false); // `false` makes the request synchronous 166 request.send(null); 167 return request.status; 168 }); 169 expect(status).toBe(200); 170 }); 171 it('should work with custom referer headers', async () => { 172 const { page, server } = getTestState(); 173 174 await page.setExtraHTTPHeaders({ referer: server.EMPTY_PAGE }); 175 await page.setRequestInterception(true); 176 page.on('request', (request) => { 177 expect(request.headers()['referer']).toBe(server.EMPTY_PAGE); 178 request.continue(); 179 }); 180 const response = await page.goto(server.EMPTY_PAGE); 181 expect(response.ok()).toBe(true); 182 }); 183 it('should be abortable', async () => { 184 const { page, server } = getTestState(); 185 186 await page.setRequestInterception(true); 187 page.on('request', (request) => { 188 if (request.url().endsWith('.css')) request.abort(); 189 else request.continue(); 190 }); 191 let failedRequests = 0; 192 page.on('requestfailed', () => ++failedRequests); 193 const response = await page.goto(server.PREFIX + '/one-style.html'); 194 expect(response.ok()).toBe(true); 195 expect(response.request().failure()).toBe(null); 196 expect(failedRequests).toBe(1); 197 }); 198 it('should be abortable with custom error codes', async () => { 199 const { page, server } = getTestState(); 200 201 await page.setRequestInterception(true); 202 page.on('request', (request) => { 203 request.abort('internetdisconnected'); 204 }); 205 let failedRequest = null; 206 page.on('requestfailed', (request) => (failedRequest = request)); 207 await page.goto(server.EMPTY_PAGE).catch(() => {}); 208 expect(failedRequest).toBeTruthy(); 209 expect(failedRequest.failure().errorText).toBe( 210 'net::ERR_INTERNET_DISCONNECTED' 211 ); 212 }); 213 it('should send referer', async () => { 214 const { page, server } = getTestState(); 215 216 await page.setExtraHTTPHeaders({ 217 referer: 'http://google.com/', 218 }); 219 await page.setRequestInterception(true); 220 page.on('request', (request) => request.continue()); 221 const [request] = await Promise.all([ 222 server.waitForRequest('/grid.html'), 223 page.goto(server.PREFIX + '/grid.html'), 224 ]); 225 expect(request.headers['referer']).toBe('http://google.com/'); 226 }); 227 it('should fail navigation when aborting main resource', async () => { 228 const { page, server, isChrome } = getTestState(); 229 230 await page.setRequestInterception(true); 231 page.on('request', (request) => request.abort()); 232 let error = null; 233 await page.goto(server.EMPTY_PAGE).catch((error_) => (error = error_)); 234 expect(error).toBeTruthy(); 235 if (isChrome) expect(error.message).toContain('net::ERR_FAILED'); 236 else expect(error.message).toContain('NS_ERROR_FAILURE'); 237 }); 238 it('should work with redirects', async () => { 239 const { page, server } = getTestState(); 240 241 await page.setRequestInterception(true); 242 const requests = []; 243 page.on('request', (request) => { 244 request.continue(); 245 requests.push(request); 246 }); 247 server.setRedirect( 248 '/non-existing-page.html', 249 '/non-existing-page-2.html' 250 ); 251 server.setRedirect( 252 '/non-existing-page-2.html', 253 '/non-existing-page-3.html' 254 ); 255 server.setRedirect( 256 '/non-existing-page-3.html', 257 '/non-existing-page-4.html' 258 ); 259 server.setRedirect('/non-existing-page-4.html', '/empty.html'); 260 const response = await page.goto( 261 server.PREFIX + '/non-existing-page.html' 262 ); 263 expect(response.status()).toBe(200); 264 expect(response.url()).toContain('empty.html'); 265 expect(requests.length).toBe(5); 266 expect(requests[2].resourceType()).toBe('document'); 267 // Check redirect chain 268 const redirectChain = response.request().redirectChain(); 269 expect(redirectChain.length).toBe(4); 270 expect(redirectChain[0].url()).toContain('/non-existing-page.html'); 271 expect(redirectChain[2].url()).toContain('/non-existing-page-3.html'); 272 for (let i = 0; i < redirectChain.length; ++i) { 273 const request = redirectChain[i]; 274 expect(request.isNavigationRequest()).toBe(true); 275 expect(request.redirectChain().indexOf(request)).toBe(i); 276 } 277 }); 278 it('should work with redirects for subresources', async () => { 279 const { page, server } = getTestState(); 280 281 await page.setRequestInterception(true); 282 const requests = []; 283 page.on('request', (request) => { 284 request.continue(); 285 if (!utils.isFavicon(request)) requests.push(request); 286 }); 287 server.setRedirect('/one-style.css', '/two-style.css'); 288 server.setRedirect('/two-style.css', '/three-style.css'); 289 server.setRedirect('/three-style.css', '/four-style.css'); 290 server.setRoute('/four-style.css', (req, res) => 291 res.end('body {box-sizing: border-box; }') 292 ); 293 294 const response = await page.goto(server.PREFIX + '/one-style.html'); 295 expect(response.status()).toBe(200); 296 expect(response.url()).toContain('one-style.html'); 297 expect(requests.length).toBe(5); 298 expect(requests[0].resourceType()).toBe('document'); 299 expect(requests[1].resourceType()).toBe('stylesheet'); 300 // Check redirect chain 301 const redirectChain = requests[1].redirectChain(); 302 expect(redirectChain.length).toBe(3); 303 expect(redirectChain[0].url()).toContain('/one-style.css'); 304 expect(redirectChain[2].url()).toContain('/three-style.css'); 305 }); 306 it('should be able to abort redirects', async () => { 307 const { page, server, isChrome } = getTestState(); 308 309 await page.setRequestInterception(true); 310 server.setRedirect('/non-existing.json', '/non-existing-2.json'); 311 server.setRedirect('/non-existing-2.json', '/simple.html'); 312 page.on('request', (request) => { 313 if (request.url().includes('non-existing-2')) request.abort(); 314 else request.continue(); 315 }); 316 await page.goto(server.EMPTY_PAGE); 317 const result = await page.evaluate(async () => { 318 try { 319 await fetch('/non-existing.json'); 320 } catch (error) { 321 return error.message; 322 } 323 }); 324 if (isChrome) expect(result).toContain('Failed to fetch'); 325 else expect(result).toContain('NetworkError'); 326 }); 327 it('should work with equal requests', async () => { 328 const { page, server } = getTestState(); 329 330 await page.goto(server.EMPTY_PAGE); 331 let responseCount = 1; 332 server.setRoute('/zzz', (req, res) => res.end(responseCount++ * 11 + '')); 333 await page.setRequestInterception(true); 334 335 let spinner = false; 336 // Cancel 2nd request. 337 page.on('request', (request) => { 338 if (utils.isFavicon(request)) { 339 request.continue(); 340 return; 341 } 342 spinner ? request.abort() : request.continue(); 343 spinner = !spinner; 344 }); 345 const results = await page.evaluate(() => 346 Promise.all([ 347 fetch('/zzz') 348 .then((response) => response.text()) 349 .catch(() => 'FAILED'), 350 fetch('/zzz') 351 .then((response) => response.text()) 352 .catch(() => 'FAILED'), 353 fetch('/zzz') 354 .then((response) => response.text()) 355 .catch(() => 'FAILED'), 356 ]) 357 ); 358 expect(results).toEqual(['11', 'FAILED', '22']); 359 }); 360 it('should navigate to dataURL and fire dataURL requests', async () => { 361 const { page } = getTestState(); 362 363 await page.setRequestInterception(true); 364 const requests = []; 365 page.on('request', (request) => { 366 requests.push(request); 367 request.continue(); 368 }); 369 const dataURL = 'data:text/html,<div>yo</div>'; 370 const response = await page.goto(dataURL); 371 expect(response.status()).toBe(200); 372 expect(requests.length).toBe(1); 373 expect(requests[0].url()).toBe(dataURL); 374 }); 375 it('should be able to fetch dataURL and fire dataURL requests', async () => { 376 const { page, server } = getTestState(); 377 378 await page.goto(server.EMPTY_PAGE); 379 await page.setRequestInterception(true); 380 const requests = []; 381 page.on('request', (request) => { 382 requests.push(request); 383 request.continue(); 384 }); 385 const dataURL = 'data:text/html,<div>yo</div>'; 386 const text = await page.evaluate( 387 (url: string) => fetch(url).then((r) => r.text()), 388 dataURL 389 ); 390 expect(text).toBe('<div>yo</div>'); 391 expect(requests.length).toBe(1); 392 expect(requests[0].url()).toBe(dataURL); 393 }); 394 it('should navigate to URL with hash and fire requests without hash', async () => { 395 const { page, server } = getTestState(); 396 397 await page.setRequestInterception(true); 398 const requests = []; 399 page.on('request', (request) => { 400 requests.push(request); 401 request.continue(); 402 }); 403 const response = await page.goto(server.EMPTY_PAGE + '#hash'); 404 expect(response.status()).toBe(200); 405 expect(response.url()).toBe(server.EMPTY_PAGE); 406 expect(requests.length).toBe(1); 407 expect(requests[0].url()).toBe(server.EMPTY_PAGE); 408 }); 409 it('should work with encoded server', async () => { 410 const { page, server } = getTestState(); 411 412 // The requestWillBeSent will report encoded URL, whereas interception will 413 // report URL as-is. @see crbug.com/759388 414 await page.setRequestInterception(true); 415 page.on('request', (request) => request.continue()); 416 const response = await page.goto( 417 server.PREFIX + '/some nonexisting page' 418 ); 419 expect(response.status()).toBe(404); 420 }); 421 it('should work with badly encoded server', async () => { 422 const { page, server } = getTestState(); 423 424 await page.setRequestInterception(true); 425 server.setRoute('/malformed?rnd=%911', (req, res) => res.end()); 426 page.on('request', (request) => request.continue()); 427 const response = await page.goto(server.PREFIX + '/malformed?rnd=%911'); 428 expect(response.status()).toBe(200); 429 }); 430 it('should work with encoded server - 2', async () => { 431 const { page, server } = getTestState(); 432 433 // The requestWillBeSent will report URL as-is, whereas interception will 434 // report encoded URL for stylesheet. @see crbug.com/759388 435 await page.setRequestInterception(true); 436 const requests = []; 437 page.on('request', (request) => { 438 request.continue(); 439 requests.push(request); 440 }); 441 const response = await page.goto( 442 `data:text/html,<link rel="stylesheet" href="${server.PREFIX}/fonts?helvetica|arial"/>` 443 ); 444 expect(response.status()).toBe(200); 445 expect(requests.length).toBe(2); 446 expect(requests[1].response().status()).toBe(404); 447 }); 448 it('should not throw "Invalid Interception Id" if the request was cancelled', async () => { 449 const { page, server } = getTestState(); 450 451 await page.setContent('<iframe></iframe>'); 452 await page.setRequestInterception(true); 453 let request = null; 454 page.on('request', async (r) => (request = r)); 455 page.$eval( 456 'iframe', 457 (frame: HTMLIFrameElement, url: string) => (frame.src = url), 458 server.EMPTY_PAGE 459 ), 460 // Wait for request interception. 461 await utils.waitEvent(page, 'request'); 462 // Delete frame to cause request to be canceled. 463 await page.$eval('iframe', (frame) => frame.remove()); 464 let error = null; 465 await request.continue().catch((error_) => (error = error_)); 466 expect(error).toBe(null); 467 }); 468 it('should throw if interception is not enabled', async () => { 469 const { page, server } = getTestState(); 470 471 let error = null; 472 page.on('request', async (request) => { 473 try { 474 await request.continue(); 475 } catch (error_) { 476 error = error_; 477 } 478 }); 479 await page.goto(server.EMPTY_PAGE); 480 expect(error.message).toContain('Request Interception is not enabled'); 481 }); 482 it('should work with file URLs', async () => { 483 const { page } = getTestState(); 484 485 await page.setRequestInterception(true); 486 const urls = new Set(); 487 page.on('request', (request) => { 488 urls.add(request.url().split('/').pop()); 489 request.continue(); 490 }); 491 await page.goto( 492 pathToFileURL(path.join(__dirname, 'assets', 'one-style.html')) 493 ); 494 expect(urls.size).toBe(2); 495 expect(urls.has('one-style.html')).toBe(true); 496 expect(urls.has('one-style.css')).toBe(true); 497 }); 498 it('should not cache if cache disabled', async () => { 499 const { page, server } = getTestState(); 500 501 // Load and re-load to make sure it's cached. 502 await page.goto(server.PREFIX + '/cached/one-style.html'); 503 504 await page.setRequestInterception(true); 505 await page.setCacheEnabled(false); 506 page.on('request', (request) => request.continue()); 507 508 const cached = []; 509 page.on('requestservedfromcache', (r) => cached.push(r)); 510 511 await page.reload(); 512 expect(cached.length).toBe(0); 513 }); 514 it('should cache if cache enabled', async () => { 515 const { page, server } = getTestState(); 516 517 // Load and re-load to make sure it's cached. 518 await page.goto(server.PREFIX + '/cached/one-style.html'); 519 520 await page.setRequestInterception(true); 521 await page.setCacheEnabled(true); 522 page.on('request', (request) => request.continue()); 523 524 const cached = []; 525 page.on('requestservedfromcache', (r) => cached.push(r)); 526 527 await page.reload(); 528 expect(cached.length).toBe(1); 529 }); 530 it('should load fonts if cache enabled', async () => { 531 const { page, server } = getTestState(); 532 533 await page.setRequestInterception(true); 534 await page.setCacheEnabled(true); 535 page.on('request', (request) => request.continue()); 536 537 await page.goto(server.PREFIX + '/cached/one-style-font.html'); 538 await page.waitForResponse((r) => r.url().endsWith('/one-style.woff')); 539 }); 540 }); 541 542 describe('Request.continue', function () { 543 it('should work', async () => { 544 const { page, server } = getTestState(); 545 546 await page.setRequestInterception(true); 547 page.on('request', (request) => request.continue()); 548 await page.goto(server.EMPTY_PAGE); 549 }); 550 it('should amend HTTP headers', async () => { 551 const { page, server } = getTestState(); 552 553 await page.setRequestInterception(true); 554 page.on('request', (request) => { 555 const headers = Object.assign({}, request.headers()); 556 headers['FOO'] = 'bar'; 557 request.continue({ headers }); 558 }); 559 await page.goto(server.EMPTY_PAGE); 560 const [request] = await Promise.all([ 561 server.waitForRequest('/sleep.zzz'), 562 page.evaluate(() => fetch('/sleep.zzz')), 563 ]); 564 expect(request.headers['foo']).toBe('bar'); 565 }); 566 it('should redirect in a way non-observable to page', async () => { 567 const { page, server } = getTestState(); 568 569 await page.setRequestInterception(true); 570 page.on('request', (request) => { 571 const redirectURL = request.url().includes('/empty.html') 572 ? server.PREFIX + '/consolelog.html' 573 : undefined; 574 request.continue({ url: redirectURL }); 575 }); 576 let consoleMessage = null; 577 page.on('console', (msg) => (consoleMessage = msg)); 578 await page.goto(server.EMPTY_PAGE); 579 expect(page.url()).toBe(server.EMPTY_PAGE); 580 expect(consoleMessage.text()).toBe('yellow'); 581 }); 582 it('should amend method', async () => { 583 const { page, server } = getTestState(); 584 585 await page.goto(server.EMPTY_PAGE); 586 587 await page.setRequestInterception(true); 588 page.on('request', (request) => { 589 request.continue({ method: 'POST' }); 590 }); 591 const [request] = await Promise.all([ 592 server.waitForRequest('/sleep.zzz'), 593 page.evaluate(() => fetch('/sleep.zzz')), 594 ]); 595 expect(request.method).toBe('POST'); 596 }); 597 it('should amend post data', async () => { 598 const { page, server } = getTestState(); 599 600 await page.goto(server.EMPTY_PAGE); 601 602 await page.setRequestInterception(true); 603 page.on('request', (request) => { 604 request.continue({ postData: 'doggo' }); 605 }); 606 const [serverRequest] = await Promise.all([ 607 server.waitForRequest('/sleep.zzz'), 608 page.evaluate(() => 609 fetch('/sleep.zzz', { method: 'POST', body: 'birdy' }) 610 ), 611 ]); 612 expect(await serverRequest.postBody).toBe('doggo'); 613 }); 614 it('should amend both post data and method on navigation', async () => { 615 const { page, server } = getTestState(); 616 617 await page.setRequestInterception(true); 618 page.on('request', (request) => { 619 request.continue({ method: 'POST', postData: 'doggo' }); 620 }); 621 const [serverRequest] = await Promise.all([ 622 server.waitForRequest('/empty.html'), 623 page.goto(server.EMPTY_PAGE), 624 ]); 625 expect(serverRequest.method).toBe('POST'); 626 expect(await serverRequest.postBody).toBe('doggo'); 627 }); 628 }); 629 630 describe('Request.respond', function () { 631 it('should work', async () => { 632 const { page, server } = getTestState(); 633 634 await page.setRequestInterception(true); 635 page.on('request', (request) => { 636 request.respond({ 637 status: 201, 638 headers: { 639 foo: 'bar', 640 }, 641 body: 'Yo, page!', 642 }); 643 }); 644 const response = await page.goto(server.EMPTY_PAGE); 645 expect(response.status()).toBe(201); 646 expect(response.headers().foo).toBe('bar'); 647 expect(await page.evaluate(() => document.body.textContent)).toBe( 648 'Yo, page!' 649 ); 650 }); 651 it('should work with status code 422', async () => { 652 const { page, server } = getTestState(); 653 654 await page.setRequestInterception(true); 655 page.on('request', (request) => { 656 request.respond({ 657 status: 422, 658 body: 'Yo, page!', 659 }); 660 }); 661 const response = await page.goto(server.EMPTY_PAGE); 662 expect(response.status()).toBe(422); 663 expect(response.statusText()).toBe('Unprocessable Entity'); 664 expect(await page.evaluate(() => document.body.textContent)).toBe( 665 'Yo, page!' 666 ); 667 }); 668 it('should redirect', async () => { 669 const { page, server } = getTestState(); 670 671 await page.setRequestInterception(true); 672 page.on('request', (request) => { 673 if (!request.url().includes('rrredirect')) { 674 request.continue(); 675 return; 676 } 677 request.respond({ 678 status: 302, 679 headers: { 680 location: server.EMPTY_PAGE, 681 }, 682 }); 683 }); 684 const response = await page.goto(server.PREFIX + '/rrredirect'); 685 expect(response.request().redirectChain().length).toBe(1); 686 expect(response.request().redirectChain()[0].url()).toBe( 687 server.PREFIX + '/rrredirect' 688 ); 689 expect(response.url()).toBe(server.EMPTY_PAGE); 690 }); 691 it('should allow mocking binary responses', async () => { 692 const { page, server } = getTestState(); 693 694 await page.setRequestInterception(true); 695 page.on('request', (request) => { 696 const imageBuffer = fs.readFileSync( 697 path.join(__dirname, 'assets', 'pptr.png') 698 ); 699 request.respond({ 700 contentType: 'image/png', 701 body: imageBuffer, 702 }); 703 }); 704 await page.evaluate((PREFIX) => { 705 const img = document.createElement('img'); 706 img.src = PREFIX + '/does-not-exist.png'; 707 document.body.appendChild(img); 708 return new Promise((fulfill) => (img.onload = fulfill)); 709 }, server.PREFIX); 710 const img = await page.$('img'); 711 expect(await img.screenshot()).toBeGolden('mock-binary-response.png'); 712 }); 713 it('should stringify intercepted request response headers', async () => { 714 const { page, server } = getTestState(); 715 716 await page.setRequestInterception(true); 717 page.on('request', (request) => { 718 request.respond({ 719 status: 200, 720 headers: { 721 foo: true, 722 }, 723 body: 'Yo, page!', 724 }); 725 }); 726 const response = await page.goto(server.EMPTY_PAGE); 727 expect(response.status()).toBe(200); 728 const headers = response.headers(); 729 expect(headers.foo).toBe('true'); 730 expect(await page.evaluate(() => document.body.textContent)).toBe( 731 'Yo, page!' 732 ); 733 }); 734 }); 735}); 736 737/** 738 * @param {string} path 739 * @returns {string} 740 */ 741function pathToFileURL(path) { 742 let pathName = path.replace(/\\/g, '/'); 743 // Windows drive letter must be prefixed with a slash. 744 if (!pathName.startsWith('/')) pathName = '/' + pathName; 745 return 'file://' + pathName; 746} 747