<commit_before>/**
 * Copyright 2017 Google Inc. All rights reserved.
 *
 * 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.
 */
const fs = require('fs');
const path = require('path');
const utils = require('./utils');
const {waitEvent, getPDFPages, cssPixelsToInches} = require('./utils');

module.exports.addTests = function({testRunner, expect, puppeteer, DeviceDescriptors, headless}) {
  const {describe, xdescribe, fdescribe} = testRunner;
  const {it, fit, xit} = testRunner;
  const {beforeAll, beforeEach, afterAll, afterEach} = testRunner;
  const iPhone = DeviceDescriptors['iPhone 6'];
  const iPhoneLandscape = DeviceDescriptors['iPhone 6 landscape'];

  describe('Page.close', function() {
    it('should reject all promises when page is closed', async({browser}) => {
      const newPage = await browser.newPage();
      const neverResolves = newPage.evaluate(() => new Promise(r => {}));
      newPage.close();
      let error = null;
      await neverResolves.catch(e => error = e);
      expect(error.message).toContain('Protocol error');
    });
    it('should not be visible in browser.pages', async({browser}) => {
      const newPage = await browser.newPage();
      expect(await browser.pages()).toContain(newPage);
      await newPage.close();
      expect(await browser.pages()).not.toContain(newPage);
    });
  });

  describe('Page.Events.error', function() {
    it('should throw when page crashes', async({page}) => {
      let error = null;
      page.on('error', err => error = err);
      page.goto('chrome://crash').catch(e => {});
      await waitEvent(page, 'error');
      expect(error.message).toBe('Page crashed!');
    });
  });

  describe('Page.evaluate', function() {
    it('should work', async({page, server}) => {
      const result = await page.evaluate(() => 7 * 3);
      expect(result).toBe(21);
    });
    it('should throw when evaluation triggers reload', async({page, server}) => {
      let error = null;
      await page.evaluate(() => {
        location.reload();
        return new Promise(resolve => {
          setTimeout(() => resolve(1), 0);
        });
      }).catch(e => error = e);
      expect(error.message).toContain('Protocol error');
    });
    it('should await promise', async({page, server}) => {
      const result = await page.evaluate(() => Promise.resolve(8 * 7));
      expect(result).toBe(56);
    });
    it('should work right after framenavigated', async({page, server}) => {
      let frameEvaluation = null;
      page.on('framenavigated', async frame => {
        frameEvaluation = frame.evaluate(() => 6 * 7);
      });
      await page.goto(server.EMPTY_PAGE);
      expect(await frameEvaluation).toBe(42);
    });
    it('should work from-inside an exposed function', async({page, server}) => {
      // Setup inpage callback, which calls Page.evaluate
      await page.exposeFunction('callController', async function(a, b) {
        return await page.evaluate((a, b) => a * b, a, b);
      });
      const result = await page.evaluate(async function() {
        return await callController(9, 3);
      });
      expect(result).toBe(27);
    });
    it('should reject promise with exception', async({page, server}) => {
      let error = null;
      await page.evaluate(() => not.existing.object.property).catch(e => error = e);
      expect(error).toBeTruthy();
      expect(error.message).toContain('not is not defined');
    });
    it('should return complex objects', async({page, server}) => {
      const object = {foo: 'bar!'};
      const result = await page.evaluate(a => a, object);
      expect(result).not.toBe(object);
      expect(result).toEqual(object);
    });
    it('should return NaN', async({page, server}) => {
      const result = await page.evaluate(() => NaN);
      expect(Object.is(result, NaN)).toBe(true);
    });
    it('should return -0', async({page, server}) => {
      const result = await page.evaluate(() => -0);
      expect(Object.is(result, -0)).toBe(true);
    });
    it('should return Infinity', async({page, server}) => {
      const result = await page.evaluate(() => Infinity);
      expect(Object.is(result, Infinity)).toBe(true);
    });
    it('should return -Infinity', async({page, server}) => {
      const result = await page.evaluate(() => -Infinity);
      expect(Object.is(result, -Infinity)).toBe(true);
    });
    it('should accept "undefined" as one of multiple parameters', async({page, server}) => {
      const result = await page.evaluate((a, b) => Object.is(a, undefined) && Object.is(b, 'foo'), undefined, 'foo');
      expect(result).toBe(true);
    });
    it('should properly serialize null fields', async({page}) => {
      expect(await page.evaluate(() => ({a: undefined}))).toEqual({});
    });
    it('should return undefined for non-serializable objects', async({page, server}) => {
      expect(await page.evaluate(() => window)).toBe(undefined);
      expect(await page.evaluate(() => [Symbol('foo4')])).toBe(undefined);
    });
    it('should fail for circular object', async({page, server}) => {
      const result = await page.evaluate(() => {
        const a = {};
        const b = {a};
        a.b = b;
        return a;
      });
      expect(result).toBe(undefined);
    });
    it('should accept a string', async({page, server}) => {
      const result = await page.evaluate('1 + 2');
      expect(result).toBe(3);
    });
    it('should accept a string with semi colons', async({page, server}) => {
      const result = await page.evaluate('1 + 5;');
      expect(result).toBe(6);
    });
    it('should accept a string with comments', async({page, server}) => {
      const result = await page.evaluate('2 + 5;\n// do some math!');
      expect(result).toBe(7);
    });
    it('should accept element handle as an argument', async({page, server}) => {
      await page.setContent('<section>42</section>');
      const element = await page.$('section');
      const text = await page.evaluate(e => e.textContent, element);
      expect(text).toBe('42');
    });
    it('should throw if underlying element was disposed', async({page, server}) => {
      await page.setContent('<section>39</section>');
      const element = await page.$('section');
      expect(element).toBeTruthy();
      await element.dispose();
      let error = null;
      await page.evaluate(e => e.textContent, element).catch(e => error = e);
      expect(error.message).toContain('JSHandle is disposed');
    });
    it('should throw if elementHandles are from other frames', async({page, server}) => {
      await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE);
      const bodyHandle = await page.frames()[1].$('body');
      let error = null;
      await page.evaluate(body => body.innerHTML, bodyHandle).catch(e => error = e);
      expect(error).toBeTruthy();
      expect(error.message).toContain('JSHandles can be evaluated only in the context they were created');
    });
    it('should accept object handle as an argument', async({page, server}) => {
      const navigatorHandle = await page.evaluateHandle(() => navigator);
      const text = await page.evaluate(e => e.userAgent, navigatorHandle);
      expect(text).toContain('Mozilla');
    });
    it('should accept object handle to primitive types', async({page, server}) => {
      const aHandle = await page.evaluateHandle(() => 5);
      const isFive = await page.evaluate(e => Object.is(e, 5), aHandle);
      expect(isFive).toBeTruthy();
    });
  });

  describe('Page.setOfflineMode', function() {
    it('should work', async({page, server}) => {
      await page.setOfflineMode(true);
      let error = null;
      await page.goto(server.EMPTY_PAGE).catch(e => error = e);
      expect(error).toBeTruthy();
      await page.setOfflineMode(false);
      const response = await page.reload();
      expect(response.status()).toBe(200);
    });
    it('should emulate navigator.onLine', async({page, server}) => {
      expect(await page.evaluate(() => window.navigator.onLine)).toBe(true);
      await page.setOfflineMode(true);
      expect(await page.evaluate(() => window.navigator.onLine)).toBe(false);
      await page.setOfflineMode(false);
      expect(await page.evaluate(() => window.navigator.onLine)).toBe(true);
    });
  });

  describe('Page.evaluateHandle', function() {
    it('should work', async({page, server}) => {
      const windowHandle = await page.evaluateHandle(() => window);
      expect(windowHandle).toBeTruthy();
    });
  });

  describe('ExecutionContext.queryObjects', function() {
    it('should work', async({page, server}) => {
      // Instantiate an object
      await page.evaluate(() => window.set = new Set(['hello', 'world']));
      const prototypeHandle = await page.evaluateHandle(() => Set.prototype);
      const objectsHandle = await page.queryObjects(prototypeHandle);
      const count = await page.evaluate(objects => objects.length, objectsHandle);
      expect(count).toBe(1);
      const values = await page.evaluate(objects => Array.from(objects[0].values()), objectsHandle);
      expect(values).toEqual(['hello', 'world']);
    });
    it('should fail for disposed handles', async({page, server}) => {
      const prototypeHandle = await page.evaluateHandle(() => HTMLBodyElement.prototype);
      await prototypeHandle.dispose();
      let error = null;
      await page.queryObjects(prototypeHandle).catch(e => error = e);
      expect(error.message).toBe('Prototype JSHandle is disposed!');
    });
    it('should fail primitive values as prototypes', async({page, server}) => {
      const prototypeHandle = await page.evaluateHandle(() => 42);
      let error = null;
      await page.queryObjects(prototypeHandle).catch(e => error = e);
      expect(error.message).toBe('Prototype JSHandle must not be referencing primitive value');
    });
  });

  describe('Page.waitFor', function() {
    it('should wait for selector', async({page, server}) => {
      let found = false;
      const waitFor = page.waitFor('div').then(() => found = true);
      await page.goto(server.EMPTY_PAGE);
      expect(found).toBe(false);
      await page.goto(server.PREFIX + '/grid.html');
      await waitFor;
      expect(found).toBe(true);
    });
    it('should wait for an xpath', async({page, server}) => {
      let found = false;
      const waitFor = page.waitFor('//div').then(() => found = true);
      await page.goto(server.EMPTY_PAGE);
      expect(found).toBe(false);
      await page.goto(server.PREFIX + '/grid.html');
      await waitFor;
      expect(found).toBe(true);
    });
    it('should not allow you to select an element with single slash xpath', async({page, server}) => {
      await page.setContent(`<div>some text</div>`);
      let error = null;
      await page.waitFor('/html/body/div').catch(e => error = e);
      expect(error).toBeTruthy();
    });
    it('should timeout', async({page, server}) => {
      const startTime = Date.now();
      const timeout = 42;
      await page.waitFor(timeout);
      expect(Date.now() - startTime).not.toBeLessThan(timeout / 2);
    });
    it('should wait for predicate', async({page, server}) => {
      const watchdog = page.waitFor(() => window.innerWidth < 100);
      page.setViewport({width: 10, height: 10});
      await watchdog;
    });
    it('should throw when unknown type', async({page, server}) => {
      let error = null;
      await page.waitFor({foo: 'bar'}).catch(e => error = e);
      expect(error.message).toContain('Unsupported target type');
    });
    it('should wait for predicate with arguments', async({page, server}) => {
      await page.waitFor((arg1, arg2) => arg1 !== arg2, {}, 1, 2);
    });
  });

  describe('Page.Events.Console', function() {
    it('should work', async({page, server}) => {
      let message = null;
      page.once('console', m => message = m);
      await Promise.all([
        page.evaluate(() => console.log('hello', 5, {foo: 'bar'})),
        waitEvent(page, 'console')
      ]);
      expect(message.text()).toEqual('hello 5 JSHandle@object');
      expect(message.type()).toEqual('log');
      expect(await message.args()[0].jsonValue()).toEqual('hello');
      expect(await message.args()[1].jsonValue()).toEqual(5);
      expect(await message.args()[2].jsonValue()).toEqual({foo: 'bar'});
    });
    it('should work for different console API calls', async({page, server}) => {
      const messages = [];
      page.on('console', msg => messages.push(msg));
      // All console events will be reported before `page.evaluate` is finished.
      await page.evaluate(() => {
        // A pair of time/timeEnd generates only one Console API call.
        console.time('calling console.time');
        console.timeEnd('calling console.time');
        console.trace('calling console.trace');
        console.dir('calling console.dir');
        console.warn('calling console.warn');
        console.error('calling console.error');
        console.log(Promise.resolve('should not wait until resolved!'));
      });
      expect(messages.map(msg => msg.type())).toEqual([
        'timeEnd', 'trace', 'dir', 'warning', 'error', 'log'
      ]);
      expect(messages[0].text()).toContain('calling console.time');
      expect(messages.slice(1).map(msg => msg.text())).toEqual([
        'calling console.trace',
        'calling console.dir',
        'calling console.warn',
        'calling console.error',
        'JSHandle@promise',
      ]);
    });
    it('should not fail for window object', async({page, server}) => {
      let message = null;
      page.once('console', msg => message = msg);
      await Promise.all([
        page.evaluate(() => console.error(window)),
        waitEvent(page, 'console')
      ]);
      expect(message.text()).toBe('JSHandle@object');
    });
  });

  describe('Page.Events.DOMContentLoaded', function() {
    it('should fire when expected', async({page, server}) => {
      page.goto('about:blank');
      await waitEvent(page, 'domcontentloaded');
    });
  });

  describe('Page.metrics', function() {
    it('should get metrics from a page', async({page, server}) => {
      await page.goto('about:blank');
      const metrics = await page.metrics();
      checkMetrics(metrics);
    });
    it('metrics event fired on console.timeStamp', async({page, server}) => {
      const metricsPromise = new Promise(fulfill => page.once('metrics', fulfill));
      await page.evaluate(() => console.timeStamp('test42'));
      const metrics = await metricsPromise;
      expect(metrics.title).toBe('test42');
      checkMetrics(metrics.metrics);
    });
    function checkMetrics(metrics) {
      const metricsToCheck = new Set([
        'Timestamp',
        'Documents',
        'Frames',
        'JSEventListeners',
        'Nodes',
        'LayoutCount',
        'RecalcStyleCount',
        'LayoutDuration',
        'RecalcStyleDuration',
        'ScriptDuration',
        'TaskDuration',
        'JSHeapUsedSize',
        'JSHeapTotalSize',
      ]);
      for (const name in metrics) {
        expect(metricsToCheck.has(name)).toBeTruthy();
        expect(metrics[name]).toBeGreaterThanOrEqual(0);
        metricsToCheck.delete(name);
      }
      expect(metricsToCheck.size).toBe(0);
    }
  });

  describe('Page.goto', function() {
    it('should navigate to about:blank', async({page, server}) => {
      const response = await page.goto('about:blank');
      expect(response).toBe(null);
    });
    it('should work with subframes return 204', async({page, server}) => {
      server.setRoute('/frames/frame.html', (req, res) => {
        res.statusCode = 204;
        res.end();
      });
      await page.goto(server.PREFIX + '/frames/one-frame.html');
    });
    it('should navigate to empty page with domcontentloaded', async({page, server}) => {
      const response = await page.goto(server.EMPTY_PAGE, {waitUntil: 'domcontentloaded'});
      expect(response.status()).toBe(200);
      expect(response.securityDetails()).toBe(null);
    });
    it('should navigate to empty page with networkidle0', async({page, server}) => {
      const response = await page.goto(server.EMPTY_PAGE, {waitUntil: 'networkidle0'});
      expect(response.status()).toBe(200);
    });
    it('should navigate to empty page with networkidle2', async({page, server}) => {
      const response = await page.goto(server.EMPTY_PAGE, {waitUntil: 'networkidle2'});
      expect(response.status()).toBe(200);
    });
    it('should fail when navigating to bad url', async({page, server}) => {
      let error = null;
      await page.goto('asdfasdf').catch(e => error = e);
      expect(error.message).toContain('Cannot navigate to invalid URL');
    });
    it('should fail when navigating to bad SSL', async({page, httpsServer}) => {
      // Make sure that network events do not emit 'undefined'.
      // @see https://crbug.com/750469<commit_msg>test: verify Page.goto fails when server returns 204 (#2452)<commit_after>/**
 * Copyright 2017 Google Inc. All rights reserved.
 *
 * 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.
 */
const fs = require('fs');
const path = require('path');
const utils = require('./utils');
const {waitEvent, getPDFPages, cssPixelsToInches} = require('./utils');

module.exports.addTests = function({testRunner, expect, puppeteer, DeviceDescriptors, headless}) {
  const {describe, xdescribe, fdescribe} = testRunner;
  const {it, fit, xit} = testRunner;
  const {beforeAll, beforeEach, afterAll, afterEach} = testRunner;
  const iPhone = DeviceDescriptors['iPhone 6'];
  const iPhoneLandscape = DeviceDescriptors['iPhone 6 landscape'];

  describe('Page.close', function() {
    it('should reject all promises when page is closed', async({browser}) => {
      const newPage = await browser.newPage();
      const neverResolves = newPage.evaluate(() => new Promise(r => {}));
      newPage.close();
      let error = null;
      await neverResolves.catch(e => error = e);
      expect(error.message).toContain('Protocol error');
    });
    it('should not be visible in browser.pages', async({browser}) => {
      const newPage = await browser.newPage();
      expect(await browser.pages()).toContain(newPage);
      await newPage.close();
      expect(await browser.pages()).not.toContain(newPage);
    });
  });

  describe('Page.Events.error', function() {
    it('should throw when page crashes', async({page}) => {
      let error = null;
      page.on('error', err => error = err);
      page.goto('chrome://crash').catch(e => {});
      await waitEvent(page, 'error');
      expect(error.message).toBe('Page crashed!');
    });
  });

  describe('Page.evaluate', function() {
    it('should work', async({page, server}) => {
      const result = await page.evaluate(() => 7 * 3);
      expect(result).toBe(21);
    });
    it('should throw when evaluation triggers reload', async({page, server}) => {
      let error = null;
      await page.evaluate(() => {
        location.reload();
        return new Promise(resolve => {
          setTimeout(() => resolve(1), 0);
        });
      }).catch(e => error = e);
      expect(error.message).toContain('Protocol error');
    });
    it('should await promise', async({page, server}) => {
      const result = await page.evaluate(() => Promise.resolve(8 * 7));
      expect(result).toBe(56);
    });
    it('should work right after framenavigated', async({page, server}) => {
      let frameEvaluation = null;
      page.on('framenavigated', async frame => {
        frameEvaluation = frame.evaluate(() => 6 * 7);
      });
      await page.goto(server.EMPTY_PAGE);
      expect(await frameEvaluation).toBe(42);
    });
    it('should work from-inside an exposed function', async({page, server}) => {
      // Setup inpage callback, which calls Page.evaluate
      await page.exposeFunction('callController', async function(a, b) {
        return await page.evaluate((a, b) => a * b, a, b);
      });
      const result = await page.evaluate(async function() {
        return await callController(9, 3);
      });
      expect(result).toBe(27);
    });
    it('should reject promise with exception', async({page, server}) => {
      let error = null;
      await page.evaluate(() => not.existing.object.property).catch(e => error = e);
      expect(error).toBeTruthy();
      expect(error.message).toContain('not is not defined');
    });
    it('should return complex objects', async({page, server}) => {
      const object = {foo: 'bar!'};
      const result = await page.evaluate(a => a, object);
      expect(result).not.toBe(object);
      expect(result).toEqual(object);
    });
    it('should return NaN', async({page, server}) => {
      const result = await page.evaluate(() => NaN);
      expect(Object.is(result, NaN)).toBe(true);
    });
    it('should return -0', async({page, server}) => {
      const result = await page.evaluate(() => -0);
      expect(Object.is(result, -0)).toBe(true);
    });
    it('should return Infinity', async({page, server}) => {
      const result = await page.evaluate(() => Infinity);
      expect(Object.is(result, Infinity)).toBe(true);
    });
    it('should return -Infinity', async({page, server}) => {
      const result = await page.evaluate(() => -Infinity);
      expect(Object.is(result, -Infinity)).toBe(true);
    });
    it('should accept "undefined" as one of multiple parameters', async({page, server}) => {
      const result = await page.evaluate((a, b) => Object.is(a, undefined) && Object.is(b, 'foo'), undefined, 'foo');
      expect(result).toBe(true);
    });
    it('should properly serialize null fields', async({page}) => {
      expect(await page.evaluate(() => ({a: undefined}))).toEqual({});
    });
    it('should return undefined for non-serializable objects', async({page, server}) => {
      expect(await page.evaluate(() => window)).toBe(undefined);
      expect(await page.evaluate(() => [Symbol('foo4')])).toBe(undefined);
    });
    it('should fail for circular object', async({page, server}) => {
      const result = await page.evaluate(() => {
        const a = {};
        const b = {a};
        a.b = b;
        return a;
      });
      expect(result).toBe(undefined);
    });
    it('should accept a string', async({page, server}) => {
      const result = await page.evaluate('1 + 2');
      expect(result).toBe(3);
    });
    it('should accept a string with semi colons', async({page, server}) => {
      const result = await page.evaluate('1 + 5;');
      expect(result).toBe(6);
    });
    it('should accept a string with comments', async({page, server}) => {
      const result = await page.evaluate('2 + 5;\n// do some math!');
      expect(result).toBe(7);
    });
    it('should accept element handle as an argument', async({page, server}) => {
      await page.setContent('<section>42</section>');
      const element = await page.$('section');
      const text = await page.evaluate(e => e.textContent, element);
      expect(text).toBe('42');
    });
    it('should throw if underlying element was disposed', async({page, server}) => {
      await page.setContent('<section>39</section>');
      const element = await page.$('section');
      expect(element).toBeTruthy();
      await element.dispose();
      let error = null;
      await page.evaluate(e => e.textContent, element).catch(e => error = e);
      expect(error.message).toContain('JSHandle is disposed');
    });
    it('should throw if elementHandles are from other frames', async({page, server}) => {
      await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE);
      const bodyHandle = await page.frames()[1].$('body');
      let error = null;
      await page.evaluate(body => body.innerHTML, bodyHandle).catch(e => error = e);
      expect(error).toBeTruthy();
      expect(error.message).toContain('JSHandles can be evaluated only in the context they were created');
    });
    it('should accept object handle as an argument', async({page, server}) => {
      const navigatorHandle = await page.evaluateHandle(() => navigator);
      const text = await page.evaluate(e => e.userAgent, navigatorHandle);
      expect(text).toContain('Mozilla');
    });
    it('should accept object handle to primitive types', async({page, server}) => {
      const aHandle = await page.evaluateHandle(() => 5);
      const isFive = await page.evaluate(e => Object.is(e, 5), aHandle);
      expect(isFive).toBeTruthy();
    });
  });

  describe('Page.setOfflineMode', function() {
    it('should work', async({page, server}) => {
      await page.setOfflineMode(true);
      let error = null;
      await page.goto(server.EMPTY_PAGE).catch(e => error = e);
      expect(error).toBeTruthy();
      await page.setOfflineMode(false);
      const response = await page.reload();
      expect(response.status()).toBe(200);
    });
    it('should emulate navigator.onLine', async({page, server}) => {
      expect(await page.evaluate(() => window.navigator.onLine)).toBe(true);
      await page.setOfflineMode(true);
      expect(await page.evaluate(() => window.navigator.onLine)).toBe(false);
      await page.setOfflineMode(false);
      expect(await page.evaluate(() => window.navigator.onLine)).toBe(true);
    });
  });

  describe('Page.evaluateHandle', function() {
    it('should work', async({page, server}) => {
      const windowHandle = await page.evaluateHandle(() => window);
      expect(windowHandle).toBeTruthy();
    });
  });

  describe('ExecutionContext.queryObjects', function() {
    it('should work', async({page, server}) => {
      // Instantiate an object
      await page.evaluate(() => window.set = new Set(['hello', 'world']));
      const prototypeHandle = await page.evaluateHandle(() => Set.prototype);
      const objectsHandle = await page.queryObjects(prototypeHandle);
      const count = await page.evaluate(objects => objects.length, objectsHandle);
      expect(count).toBe(1);
      const values = await page.evaluate(objects => Array.from(objects[0].values()), objectsHandle);
      expect(values).toEqual(['hello', 'world']);
    });
    it('should fail for disposed handles', async({page, server}) => {
      const prototypeHandle = await page.evaluateHandle(() => HTMLBodyElement.prototype);
      await prototypeHandle.dispose();
      let error = null;
      await page.queryObjects(prototypeHandle).catch(e => error = e);
      expect(error.message).toBe('Prototype JSHandle is disposed!');
    });
    it('should fail primitive values as prototypes', async({page, server}) => {
      const prototypeHandle = await page.evaluateHandle(() => 42);
      let error = null;
      await page.queryObjects(prototypeHandle).catch(e => error = e);
      expect(error.message).toBe('Prototype JSHandle must not be referencing primitive value');
    });
  });

  describe('Page.waitFor', function() {
    it('should wait for selector', async({page, server}) => {
      let found = false;
      const waitFor = page.waitFor('div').then(() => found = true);
      await page.goto(server.EMPTY_PAGE);
      expect(found).toBe(false);
      await page.goto(server.PREFIX + '/grid.html');
      await waitFor;
      expect(found).toBe(true);
    });
    it('should wait for an xpath', async({page, server}) => {
      let found = false;
      const waitFor = page.waitFor('//div').then(() => found = true);
      await page.goto(server.EMPTY_PAGE);
      expect(found).toBe(false);
      await page.goto(server.PREFIX + '/grid.html');
      await waitFor;
      expect(found).toBe(true);
    });
    it('should not allow you to select an element with single slash xpath', async({page, server}) => {
      await page.setContent(`<div>some text</div>`);
      let error = null;
      await page.waitFor('/html/body/div').catch(e => error = e);
      expect(error).toBeTruthy();
    });
    it('should timeout', async({page, server}) => {
      const startTime = Date.now();
      const timeout = 42;
      await page.waitFor(timeout);
      expect(Date.now() - startTime).not.toBeLessThan(timeout / 2);
    });
    it('should wait for predicate', async({page, server}) => {
      const watchdog = page.waitFor(() => window.innerWidth < 100);
      page.setViewport({width: 10, height: 10});
      await watchdog;
    });
    it('should throw when unknown type', async({page, server}) => {
      let error = null;
      await page.waitFor({foo: 'bar'}).catch(e => error = e);
      expect(error.message).toContain('Unsupported target type');
    });
    it('should wait for predicate with arguments', async({page, server}) => {
      await page.waitFor((arg1, arg2) => arg1 !== arg2, {}, 1, 2);
    });
  });

  describe('Page.Events.Console', function() {
    it('should work', async({page, server}) => {
      let message = null;
      page.once('console', m => message = m);
      await Promise.all([
        page.evaluate(() => console.log('hello', 5, {foo: 'bar'})),
        waitEvent(page, 'console')
      ]);
      expect(message.text()).toEqual('hello 5 JSHandle@object');
      expect(message.type()).toEqual('log');
      expect(await message.args()[0].jsonValue()).toEqual('hello');
      expect(await message.args()[1].jsonValue()).toEqual(5);
      expect(await message.args()[2].jsonValue()).toEqual({foo: 'bar'});
    });
    it('should work for different console API calls', async({page, server}) => {
      const messages = [];
      page.on('console', msg => messages.push(msg));
      // All console events will be reported before `page.evaluate` is finished.
      await page.evaluate(() => {
        // A pair of time/timeEnd generates only one Console API call.
        console.time('calling console.time');
        console.timeEnd('calling console.time');
        console.trace('calling console.trace');
        console.dir('calling console.dir');
        console.warn('calling console.warn');
        console.error('calling console.error');
        console.log(Promise.resolve('should not wait until resolved!'));
      });
      expect(messages.map(msg => msg.type())).toEqual([
        'timeEnd', 'trace', 'dir', 'warning', 'error', 'log'
      ]);
      expect(messages[0].text()).toContain('calling console.time');
      expect(messages.slice(1).map(msg => msg.text())).toEqual([
        'calling console.trace',
        'calling console.dir',
        'calling console.warn',
        'calling console.error',
        'JSHandle@promise',
      ]);
    });
    it('should not fail for window object', async({page, server}) => {
      let message = null;
      page.once('console', msg => message = msg);
      await Promise.all([
        page.evaluate(() => console.error(window)),
        waitEvent(page, 'console')
      ]);
      expect(message.text()).toBe('JSHandle@object');
    });
  });

  describe('Page.Events.DOMContentLoaded', function() {
    it('should fire when expected', async({page, server}) => {
      page.goto('about:blank');
      await waitEvent(page, 'domcontentloaded');
    });
  });

  describe('Page.metrics', function() {
    it('should get metrics from a page', async({page, server}) => {
      await page.goto('about:blank');
      const metrics = await page.metrics();
      checkMetrics(metrics);
    });
    it('metrics event fired on console.timeStamp', async({page, server}) => {
      const metricsPromise = new Promise(fulfill => page.once('metrics', fulfill));
      await page.evaluate(() => console.timeStamp('test42'));
      const metrics = await metricsPromise;
      expect(metrics.title).toBe('test42');
      checkMetrics(metrics.metrics);
    });
    function checkMetrics(metrics) {
      const metricsToCheck = new Set([
        'Timestamp',
        'Documents',
        'Frames',
        'JSEventListeners',
        'Nodes',
        'LayoutCount',
        'RecalcStyleCount',
        'LayoutDuration',
        'RecalcStyleDuration',
        'ScriptDuration',
        'TaskDuration',
        'JSHeapUsedSize',
        'JSHeapTotalSize',
      ]);
      for (const name in metrics) {
        expect(metricsToCheck.has(name)).toBeTruthy();
        expect(metrics[name]).toBeGreaterThanOrEqual(0);
        metricsToCheck.delete(name);
      }
      expect(metricsToCheck.size).toBe(0);
    }
  });

  describe('Page.goto', function() {
    it('should navigate to about:blank', async({page, server}) => {
      const response = await page.goto('about:blank');
      expect(response).toBe(null);
    });
    it('should work with subframes return 204', async({page, server}) => {
      server.setRoute('/frames/frame.html', (req, res) => {
        res.statusCode = 204;
        res.end();
      });
      await page.goto(server.PREFIX + '/frames/one-frame.html');
    });
    it('should fail when server returns 204', async({page, server}) => {
      server.setRoute('/empty.html', (req, res) => {
        res.statusCode = 204;
        res.end();
      });
      let error = null;
      await page.goto(server.EMPTY_PAGE).catch(e => error = e);
      expect(error).not.toBe(null);
      expect(error.message).toContain('net::ERR_ABORTED');
    });
    it('should navigate to empty page with domcontentloaded', async({page, server}) => {
      const response = await page.goto(server.EMPTY_PAGE, {waitUntil: 'domcontentloaded'});
      expect(response.status()).toBe(200);
      expect(response.securityDetails()).toBe(null);
    });
    it('should navigate to empty page with networkidle0', async({page, server}) => {
      const response = await page.goto(server.EMPTY_PAGE, {waitUntil: 'networkidle0'});
      expect(response.status()).toBe(200);
    });
    it('should navigate to empty page with networkidle2', async({page, server}) => {
      const response = await page.goto(server.EMPTY_PAGE, {waitUntil: 'networkidle2'});
      expect(response.status()).toBe(200);
    });
    it('should fail when navigating to bad url', async({page, server}) => {
      let error = null;
      await page.goto('asdfasdf').catch(e => error = e);
      expect(error.message).toContain('Cannot navigate to invalid URL');
    });
    it('should fail when navigating to bad SSL', async({page, httpsServer}) => {
      // Make sure that network events do not emit 'undefined'.
      // @see https://crbug.com/750469
{'fraction_non_alphanumeric': 0.16775147088082312, 'fraction_numerical': 0.006793053463320354, 'mean_word_length': 3.1980670564371794, 'pattern_counts': {'":': 0, '<': 17, '<?xml version=': 0, '>': 254, 'https://': 2, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4762852', 'n_tokens_mistral': 10141, 'n_tokens_neox': 10029, 'n_words': 2530}