mirror of
https://github.com/puppeteer/puppeteer
synced 2024-06-14 14:02:48 +00:00
feat(JSHandles): introduce JSHandles (#943)
This patch: - introduces ExecutionContext class that incapsulates javascript execution context. An examples of execution contexts are workers and frames - introduces JSHandle that holds a references to the javascript object in ExecutionContext - inherits ElementHandle from JSHandle Fixes #382.
This commit is contained in:
162
test/test.js
162
test/test.js
@@ -265,9 +265,10 @@ describe('Page', function() {
|
||||
const result = await page.evaluate((a, b) => Object.is(a, undefined) && Object.is(b, 'foo'), undefined, 'foo');
|
||||
expect(result).toBe(true);
|
||||
}));
|
||||
it('should not fail for window object', SX(async function() {
|
||||
const result = await page.evaluate(() => window);
|
||||
expect(result).toBe('Window');
|
||||
it('should fail for window object', SX(async function() {
|
||||
let error = null;
|
||||
await page.evaluate(() => window).catch(e => error = e);
|
||||
expect(error.message).toContain('Converting circular structure to JSON');
|
||||
}));
|
||||
it('should accept a string', SX(async function() {
|
||||
const result = await page.evaluate('1 + 2');
|
||||
@@ -294,7 +295,7 @@ describe('Page', function() {
|
||||
await element.dispose();
|
||||
let error = null;
|
||||
await page.evaluate(e => e.textContent, element).catch(e => error = e);
|
||||
expect(error.message).toContain('ElementHandle is disposed');
|
||||
expect(error.message).toContain('JSHandle is disposed');
|
||||
}));
|
||||
it('should throw if elementHandles are from other frames', SX(async function() {
|
||||
const FrameUtils = require('./frame-utils');
|
||||
@@ -303,7 +304,117 @@ describe('Page', function() {
|
||||
let error = null;
|
||||
await page.evaluate(body => body.innerHTML, bodyHandle).catch(e => error = e);
|
||||
expect(error).toBeTruthy();
|
||||
expect(error.message).toContain('ElementHandles passed as arguments should belong');
|
||||
expect(error.message).toContain('JSHandles can be evaluated only in the context they were created');
|
||||
}));
|
||||
it('should accept object handle as an argument', SX(async function() {
|
||||
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', SX(async function() {
|
||||
const aHandle = await page.evaluateHandle(() => 5);
|
||||
const isFive = await page.evaluate(e => Object.is(e, 5), aHandle);
|
||||
expect(isFive).toBeTruthy();
|
||||
}));
|
||||
});
|
||||
|
||||
describe('Page.evaluateHandle', function() {
|
||||
it('should work', SX(async function() {
|
||||
const windowHandle = await page.evaluateHandle(() => window);
|
||||
expect(windowHandle).toBeTruthy();
|
||||
}));
|
||||
});
|
||||
|
||||
describe('JSHandle.getProperty', function() {
|
||||
it('should work', SX(async function() {
|
||||
const aHandle = await page.evaluateHandle(() => ({
|
||||
one: 1,
|
||||
two: 2,
|
||||
three: 3
|
||||
}));
|
||||
const twoHandle = await aHandle.getProperty('two');
|
||||
expect(await twoHandle.jsonValue()).toEqual(2);
|
||||
}));
|
||||
});
|
||||
|
||||
describe('JSHandle.jsonValue', function() {
|
||||
it('should work', SX(async function() {
|
||||
const aHandle = await page.evaluateHandle(() => ({foo: 'bar'}));
|
||||
const json = await aHandle.jsonValue();
|
||||
expect(json).toEqual({foo: 'bar'});
|
||||
}));
|
||||
it('should work with dates', SX(async function() {
|
||||
const dateHandle = await page.evaluateHandle(() => new Date('2017-09-26T00:00:00.000Z'));
|
||||
const json = await dateHandle.jsonValue();
|
||||
expect(json).toBe('2017-09-26T00:00:00.000Z');
|
||||
}));
|
||||
it('should throw for circular objects', SX(async function() {
|
||||
const windowHandle = await page.evaluateHandle('window');
|
||||
let error = null;
|
||||
await windowHandle.jsonValue().catch(e => error = e);
|
||||
expect(error.message).toContain('Converting circular structure to JSON');
|
||||
}));
|
||||
});
|
||||
|
||||
describe('JSHandle.getProperties', function() {
|
||||
it('should work', SX(async function() {
|
||||
const aHandle = await page.evaluateHandle(() => ({
|
||||
foo: 'bar'
|
||||
}));
|
||||
const properties = await aHandle.getProperties();
|
||||
const foo = properties.get('foo');
|
||||
expect(foo).toBeTruthy();
|
||||
expect(await foo.jsonValue()).toBe('bar');
|
||||
}));
|
||||
it('should return even non-own properties', SX(async function() {
|
||||
const aHandle = await page.evaluateHandle(() => {
|
||||
class A {
|
||||
constructor() {
|
||||
this.a = '1';
|
||||
}
|
||||
}
|
||||
class B extends A {
|
||||
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', SX(async function() {
|
||||
const aHandle = await page.evaluateHandle(() => document.body);
|
||||
const element = aHandle.asElement();
|
||||
expect(element).toBeTruthy();
|
||||
}));
|
||||
it('should return null for non-elements', SX(async function() {
|
||||
const aHandle = await page.evaluateHandle(() => 2);
|
||||
const element = aHandle.asElement();
|
||||
expect(element).toBeFalsy();
|
||||
}));
|
||||
it('should return ElementHandle for TextNodes', SX(async function() {
|
||||
await page.setContent('<div>ee!</div>');
|
||||
const aHandle = await page.evaluateHandle(() => document.querySelector('div').firstChild);
|
||||
const element = aHandle.asElement();
|
||||
expect(element).toBeTruthy();
|
||||
expect(await page.evaluate(e => e.nodeType === HTMLElement.TEXT_NODE, element));
|
||||
}));
|
||||
});
|
||||
|
||||
describe('JSHandle.toString', function() {
|
||||
it('should work for primitives', SX(async function() {
|
||||
const aHandle = await page.evaluateHandle(() => 2);
|
||||
expect(aHandle.toString()).toBe('2');
|
||||
}));
|
||||
it('should work for complicated objects', SX(async function() {
|
||||
const aHandle = await page.evaluateHandle(() => window);
|
||||
expect(aHandle.toString()).toBe('JSHandle@object');
|
||||
}));
|
||||
});
|
||||
|
||||
@@ -322,6 +433,30 @@ describe('Page', function() {
|
||||
}));
|
||||
});
|
||||
|
||||
describe('Frame.context', function() {
|
||||
const FrameUtils = require('./frame-utils');
|
||||
it('should work', SX(async function() {
|
||||
await page.goto(EMPTY_PAGE);
|
||||
await FrameUtils.attachFrame(page, 'frame1', EMPTY_PAGE);
|
||||
expect(page.frames().length).toBe(2);
|
||||
const [frame1, frame2] = page.frames();
|
||||
expect(frame1.executionContext()).toBeTruthy();
|
||||
expect(frame2.executionContext()).toBeTruthy();
|
||||
expect(frame1.executionContext() !== frame2.executionContext()).toBeTruthy();
|
||||
|
||||
await Promise.all([
|
||||
frame1.executionContext().evaluate(() => window.a = 1),
|
||||
frame2.executionContext().evaluate(() => window.a = 2)
|
||||
]);
|
||||
const [a1, a2] = await Promise.all([
|
||||
frame1.executionContext().evaluate(() => window.a),
|
||||
frame2.executionContext().evaluate(() => window.a)
|
||||
]);
|
||||
expect(a1).toBe(1);
|
||||
expect(a2).toBe(2);
|
||||
}));
|
||||
});
|
||||
|
||||
describe('Frame.evaluate', function() {
|
||||
const FrameUtils = require('./frame-utils');
|
||||
it('should have different execution contexts', SX(async function() {
|
||||
@@ -1241,9 +1376,24 @@ describe('Page', function() {
|
||||
it('should work', SX(async function() {
|
||||
await page.goto(PREFIX + '/input/button.html');
|
||||
const button = await page.$('button');
|
||||
await button.click('button');
|
||||
await button.click();
|
||||
expect(await page.evaluate(() => result)).toBe('Clicked');
|
||||
}));
|
||||
it('should work for TextNodes', SX(async function() {
|
||||
await page.goto(PREFIX + '/input/button.html');
|
||||
const buttonTextNode = await page.evaluateHandle(() => document.querySelector('button').firstChild);
|
||||
let error = null;
|
||||
await buttonTextNode.click().catch(err => error = err);
|
||||
expect(error.message).toBe('Node is not of type HTMLElement');
|
||||
}));
|
||||
it('should throw for detached nodes', SX(async function() {
|
||||
await page.goto(PREFIX + '/input/button.html');
|
||||
const button = await page.$('button');
|
||||
await page.evaluate(button => button.remove(), button);
|
||||
let error = null;
|
||||
await button.click().catch(err => error = err);
|
||||
expect(error.message).toBe('Node is detached from document');
|
||||
}));
|
||||
});
|
||||
|
||||
describe('ElementHandle.hover', function() {
|
||||
|
||||
Reference in New Issue
Block a user