/** * @license * Copyright 2018 Google Inc. * SPDX-License-Identifier: Apache-2.0 */ import expect from 'expect'; import {JSHandle} from 'puppeteer-core/internal/api/JSHandle.js'; import { asyncDisposeSymbol, disposeSymbol, } from 'puppeteer-core/internal/util/disposable.js'; import sinon from 'sinon'; import {getTestState, setupTestBrowserHooks} from './mocha-utils.js'; describe('JSHandle', function () { setupTestBrowserHooks(); describe('Page.evaluateHandle', function () { it('should work', async () => { const {page} = await getTestState(); using windowHandle = await page.evaluateHandle(() => { return window; }); expect(windowHandle).toBeTruthy(); }); it('should return the RemoteObject', async () => { const {page} = await getTestState(); using windowHandle = await page.evaluateHandle(() => { return window; }); expect(windowHandle.remoteObject()).toBeTruthy(); }); it('should accept object handle as an argument', async () => { const {page} = await getTestState(); using navigatorHandle = await page.evaluateHandle(() => { return navigator; }); const text = await page.evaluate(e => { return e.userAgent; }, navigatorHandle); expect(text).toContain('Mozilla'); }); it('should accept object handle to primitive types', async () => { const {page} = await getTestState(); using aHandle = await page.evaluateHandle(() => { return 5; }); const isFive = await page.evaluate(e => { return Object.is(e, 5); }, aHandle); expect(isFive).toBeTruthy(); }); it('should warn about recursive objects', async () => { const {page} = await getTestState(); const test: {obj?: unknown} = {}; test.obj = test; let error!: Error; await page .evaluateHandle(opts => { return opts; }, test) .catch(error_ => { return (error = error_); }); expect(error.message).toContain('Recursive objects are not allowed.'); }); it('should accept object handle to unserializable value', async () => { const {page} = await getTestState(); using aHandle = await page.evaluateHandle(() => { return Infinity; }); expect( await page.evaluate(e => { return Object.is(e, Infinity); }, aHandle) ).toBe(true); }); it('should use the same JS wrappers', async () => { const {page} = await getTestState(); using aHandle = await page.evaluateHandle(() => { (globalThis as any).FOO = 123; return window; }); expect( await page.evaluate(e => { return (e as any).FOO; }, aHandle) ).toBe(123); }); }); describe('JSHandle.getProperty', function () { it('should work', async () => { const {page} = await getTestState(); using aHandle = await page.evaluateHandle(() => { return { one: 1, two: 2, three: 3, }; }); using twoHandle = await aHandle.getProperty('two'); expect(await twoHandle.jsonValue()).toEqual(2); }); }); describe('JSHandle.jsonValue', function () { it('should work', async () => { const {page} = await getTestState(); using aHandle = await page.evaluateHandle(() => { return {foo: 'bar'}; }); const json = await aHandle.jsonValue(); expect(json).toEqual({foo: 'bar'}); }); it('works with jsonValues that are not objects', async () => { const {page} = await getTestState(); using aHandle = await page.evaluateHandle(() => { return ['a', 'b']; }); const json = await aHandle.jsonValue(); expect(json).toEqual(['a', 'b']); }); it('works with jsonValues that are primitives', async () => { const {page} = await getTestState(); using aHandle = await page.evaluateHandle(() => { return 'foo'; }); expect(await aHandle.jsonValue()).toEqual('foo'); using bHandle = await page.evaluateHandle(() => { return undefined; }); expect(await bHandle.jsonValue()).toEqual(undefined); }); it('should work with dates', async () => { const {page} = await getTestState(); using dateHandle = await page.evaluateHandle(() => { return new Date('2017-09-26T00:00:00.000Z'); }); const date = await dateHandle.jsonValue(); expect(date).toBeInstanceOf(Date); expect(date.toISOString()).toEqual('2017-09-26T00:00:00.000Z'); }); it('should not throw for circular objects', async () => { const {page} = await getTestState(); using handle = await page.evaluateHandle(() => { const t: {t?: unknown; g: number} = {g: 1}; t.t = t; return t; }); await handle.jsonValue(); }); }); describe('JSHandle.getProperties', function () { it('should work', async () => { const {page} = await getTestState(); using aHandle = await page.evaluateHandle(() => { return { foo: 'bar', }; }); const properties = await aHandle.getProperties(); using foo = properties.get('foo')!; expect(foo).toBeTruthy(); expect(await foo.jsonValue()).toBe('bar'); }); it('should return even non-own properties', async () => { const {page} = await getTestState(); using aHandle = await page.evaluateHandle(() => { class A { a: string; constructor() { this.a = '1'; } } class B extends A { b: string; constructor() { super(); this.b = '2'; } } return new B(); }); const properties = await aHandle.getProperties(); expect(await properties.get('a')!.jsonValue()).toBe('1'); expect(await properties.get('b')!.jsonValue()).toBe('2'); }); }); describe('JSHandle.asElement', function () { it('should work', async () => { const {page} = await getTestState(); using aHandle = await page.evaluateHandle(() => { return document.body; }); using element = aHandle.asElement(); expect(element).toBeTruthy(); }); it('should return null for non-elements', async () => { const {page} = await getTestState(); using aHandle = await page.evaluateHandle(() => { return 2; }); using element = aHandle.asElement(); expect(element).toBeFalsy(); }); it('should return ElementHandle for TextNodes', async () => { const {page} = await getTestState(); await page.setContent('