/**
 * Copyright 2017 Google Inc. All rights reserved.
 * Modifications copyright (c) Microsoft Corporation.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
import './base.fixture';

it('exposeBinding should work', async({browser}) => {
  const context = await browser.newContext();
  const page = await context.newPage();
  let bindingSource;
  await page.exposeBinding('add', (source, a, b) => {
    bindingSource = source;
    return a + b;
  });
  const result = await page.evaluate(async function() {
    return window['add'](5, 6);
  });
  expect(bindingSource.context).toBe(context);
  expect(bindingSource.page).toBe(page);
  expect(bindingSource.frame).toBe(page.mainFrame());
  expect(result).toEqual(11);
  await context.close();
});

it('should work', async({page, server}) => {
  await page.exposeFunction('compute', function(a, b) {
    return a * b;
  });
  const result = await page.evaluate(async function() {
    return await window['compute'](9, 4);
  });
  expect(result).toBe(36);
});

it('should work with handles and complex objects', async({page, server}) => {
  const fooHandle = await page.evaluateHandle(() => {
    window['fooValue'] = { bar: 2 };
    return window['fooValue'];
  });
  await page.exposeFunction('handle', () => {
    return [{ foo: fooHandle }];
  });
  const equals = await page.evaluate(async function() {
    const value = await window['handle']();
    const [{ foo }] = value;
    return foo === window['fooValue'];
  });
  expect(equals).toBe(true);
});

it('should throw exception in page context', async({page, server}) => {
  await page.exposeFunction('woof', function() {
    throw new Error('WOOF WOOF');
  });
  const {message, stack} = await page.evaluate(async() => {
    try {
      await window["woof"]();
    } catch (e) {
      return {message: e.message, stack: e.stack};
    }
  });
  expect(message).toBe('WOOF WOOF');
  expect(stack).toContain(__filename);
});

it('should support throwing "null"', async({page, server}) => {
  await page.exposeFunction('woof', function() {
    throw null;
  });
  const thrown = await page.evaluate(async() => {
    try {
      await window["woof"]();
    } catch (e) {
      return e;
    }
  });
  expect(thrown).toBe(null);
});

it('should be callable from-inside addInitScript', async({page, server}) => {
  let called = false;
  await page.exposeFunction('woof', function() {
    called = true;
  });
  await page.addInitScript(() => window["woof"]());
  await page.reload();
  expect(called).toBe(true);
});

it('should survive navigation', async({page, server}) => {
  await page.exposeFunction('compute', function(a, b) {
    return a * b;
  });

  await page.goto(server.EMPTY_PAGE);
  const result = await page.evaluate(async function() {
    return await window['compute'](9, 4);
  });
  expect(result).toBe(36);
});

it('should await returned promise', async({page, server}) => {
  await page.exposeFunction('compute', function(a, b) {
    return Promise.resolve(a * b);
  });

  const result = await page.evaluate(async function() {
    return await window['compute'](3, 5);
  });
  expect(result).toBe(15);
});

it('should work on frames', async({page, server}) => {
  await page.exposeFunction('compute', function(a, b) {
    return Promise.resolve(a * b);
  });

  await page.goto(server.PREFIX + '/frames/nested-frames.html');
  const frame = page.frames()[1];
  const result = await frame.evaluate(async function() {
    return await window['compute'](3, 5);
  });
  expect(result).toBe(15);
});

it('should work on frames before navigation', async({page, server}) => {
  await page.goto(server.PREFIX + '/frames/nested-frames.html');
  await page.exposeFunction('compute', function(a, b) {
    return Promise.resolve(a * b);
  });

  const frame = page.frames()[1];
  const result = await frame.evaluate(async function() {
    return await window['compute'](3, 5);
  });
  expect(result).toBe(15);
});

it('should work after cross origin navigation', async({page, server}) => {
  await page.goto(server.EMPTY_PAGE);
  await page.exposeFunction('compute', function(a, b) {
    return a * b;
  });

  await page.goto(server.CROSS_PROCESS_PREFIX + '/empty.html');
  const result = await page.evaluate(async function() {
    return await window['compute'](9, 4);
  });
  expect(result).toBe(36);
});

it('should work with complex objects', async({page, server}) => {
  await page.exposeFunction('complexObject', function(a, b) {
    return {x: a.x + b.x};
  });
  const result = await page.evaluate(async() => window['complexObject']({x: 5}, {x: 2}));
  expect(result.x).toBe(7);
});
