New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(ObjectHandles): Implement Object Handles #943
Conversation
docs/api.md
Outdated
If the function, passed to the `page.object`, returns a [Promise], then `page.object` would wait for the promise to resolve and return it's value. | ||
|
||
```js | ||
const aHandle = await page.object(() => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: () => Promise.resolve(window)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
docs/api.md
Outdated
@@ -109,21 +110,44 @@ | |||
+ [frame.$eval(selector, pageFunction[, ...args])](#frameevalselector-pagefunction-args) | |||
+ [frame.addScriptTag(url)](#frameaddscripttagurl) | |||
+ [frame.childFrames()](#framechildframes) | |||
+ [frame.context()](#framecontext) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
executionContext
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
docs/api.md
Outdated
@@ -55,6 +55,7 @@ | |||
+ [page.keyboard](#pagekeyboard) | |||
+ [page.mainFrame()](#pagemainframe) | |||
+ [page.mouse](#pagemouse) | |||
+ [page.object(pageFunction, ...args)](#pageobjectpagefunction-args) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
evaluateObject
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
docs/api.md
Outdated
+ [frame.evaluate(pageFunction, ...args)](#frameevaluatepagefunction-args) | ||
+ [frame.injectFile(filePath)](#frameinjectfilefilepath) | ||
+ [frame.isDetached()](#frameisdetached) | ||
+ [frame.name()](#framename) | ||
+ [frame.object(pageFunction, ...args)](#frameobjectpagefunction-args) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Drop it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
DOne.
docs/api.md
Outdated
* [class: ObjectHandle](#class-objecthandle) | ||
+ [objectHandle.asArray()](#objecthandleasarray) | ||
+ [objectHandle.asElement()](#objecthandleaselement) | ||
+ [objectHandle.asJSON()](#objecthandleasjson) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
jsonValue
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
docs/api.md
Outdated
+ [objectHandle.asArray()](#objecthandleasarray) | ||
+ [objectHandle.asElement()](#objecthandleaselement) | ||
+ [objectHandle.asJSON()](#objecthandleasjson) | ||
+ [objectHandle.asObject()](#objecthandleasobject) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
getProperties(own) ? Returns Map.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
docs/api.md
Outdated
* [class: ElementHandle](#class-elementhandle) | ||
+ [elementHandle.asArray()](#elementhandleasarray) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Return null for everything not array. Don't assign non-indexed property.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Killed it.
docs/api.md
Outdated
+ [executionContext.evaluate(pageFunction, ...args)](#executioncontextevaluatepagefunction-args) | ||
+ [executionContext.object(pageFunction, ...args)](#executioncontextobjectpagefunction-args) | ||
* [class: ObjectHandle](#class-objecthandle) | ||
+ [objectHandle.asArray()](#objecthandleasarray) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use getProperties instead.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
docs/api.md
Outdated
* [class: ExecutionContext](#class-executioncontext) | ||
+ [executionContext.evaluate(pageFunction, ...args)](#executioncontextevaluatepagefunction-args) | ||
+ [executionContext.object(pageFunction, ...args)](#executioncontextobjectpagefunction-args) | ||
* [class: ObjectHandle](#class-objecthandle) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Handle
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As discussed offline, renamed into JSHandle
docs/api.md
Outdated
+ [objectHandle.asObject()](#objecthandleasobject) | ||
+ [objectHandle.context()](#objecthandlecontext) | ||
+ [objectHandle.dispose()](#objecthandledispose) | ||
+ [objectHandle.get(propertyName)](#objecthandlegetpropertyname) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
getProperty
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
docs/api.md
Outdated
+ [objectHandle.dispose()](#objecthandledispose) | ||
+ [objectHandle.get(propertyName)](#objecthandlegetpropertyname) | ||
+ [objectHandle.toString()](#objecthandletostring) | ||
+ [objectHandle.type()](#objecthandletype) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove type, I would add evaluate.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Killed type; as per offline discussion, holding off from adding .evaluate in this patch until its necessary
docs/api.md
Outdated
- `...args` <...[Serializable]|[ObjectHandle]> Arguments to pass to `pageFunction` | ||
- returns: <[Promise]<[ObjectHandle]>> Resolves to the return value of `pageFunction` | ||
|
||
If the function, passed to the `page.object`, returns a [Promise], then `page.object` would wait for the promise to resolve and return it's value. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
its value
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
docs/api.md
Outdated
If the function, passed to the `page.object`, returns a [Promise], then `page.object` would wait for the promise to resolve and return it's value. | ||
|
||
```js | ||
const aHandle = await page.object(() => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ditto
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
docs/api.md
Outdated
|
||
Returns either `null` or the object handle itself, if the object handle is an instance of [ElementHandle]. | ||
|
||
#### objectHandle.asJSON() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd expect this method to return a string
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Renamed into objectHandle.jsonValue()
@@ -59,7 +59,7 @@ class MDOutline { | |||
let actualText = element.firstChild.textContent; | |||
let angleIndex = actualText.indexOf('<'); | |||
let spaceIndex = actualText.indexOf(' '); | |||
angleIndex = angleIndex === -1 ? angleText.length : angleIndex; | |||
angleIndex = angleIndex === -1 ? actualText.length : angleIndex; | |||
spaceIndex = spaceIndex === -1 ? spaceIndex.length : spaceIndex + 1; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
spaceIndex === -1 ? spaceIndex.length
looks sketchy.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, done.
ObjectHandle -> JSHandle |
974a350
to
64fcfba
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
addressed all comments
docs/api.md
Outdated
@@ -55,6 +55,7 @@ | |||
+ [page.keyboard](#pagekeyboard) | |||
+ [page.mainFrame()](#pagemainframe) | |||
+ [page.mouse](#pagemouse) | |||
+ [page.object(pageFunction, ...args)](#pageobjectpagefunction-args) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
docs/api.md
Outdated
@@ -109,21 +110,44 @@ | |||
+ [frame.$eval(selector, pageFunction[, ...args])](#frameevalselector-pagefunction-args) | |||
+ [frame.addScriptTag(url)](#frameaddscripttagurl) | |||
+ [frame.childFrames()](#framechildframes) | |||
+ [frame.context()](#framecontext) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
docs/api.md
Outdated
+ [frame.evaluate(pageFunction, ...args)](#frameevaluatepagefunction-args) | ||
+ [frame.injectFile(filePath)](#frameinjectfilefilepath) | ||
+ [frame.isDetached()](#frameisdetached) | ||
+ [frame.name()](#framename) | ||
+ [frame.object(pageFunction, ...args)](#frameobjectpagefunction-args) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
DOne.
docs/api.md
Outdated
* [class: ExecutionContext](#class-executioncontext) | ||
+ [executionContext.evaluate(pageFunction, ...args)](#executioncontextevaluatepagefunction-args) | ||
+ [executionContext.object(pageFunction, ...args)](#executioncontextobjectpagefunction-args) | ||
* [class: ObjectHandle](#class-objecthandle) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As discussed offline, renamed into JSHandle
docs/api.md
Outdated
* [class: ObjectHandle](#class-objecthandle) | ||
+ [objectHandle.asArray()](#objecthandleasarray) | ||
+ [objectHandle.asElement()](#objecthandleaselement) | ||
+ [objectHandle.asJSON()](#objecthandleasjson) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
@@ -59,7 +59,7 @@ class MDOutline { | |||
let actualText = element.firstChild.textContent; | |||
let angleIndex = actualText.indexOf('<'); | |||
let spaceIndex = actualText.indexOf(' '); | |||
angleIndex = angleIndex === -1 ? angleText.length : angleIndex; | |||
angleIndex = angleIndex === -1 ? actualText.length : angleIndex; | |||
spaceIndex = spaceIndex === -1 ? spaceIndex.length : spaceIndex + 1; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, done.
docs/api.md
Outdated
|
||
Returns either `null` or the object handle itself, if the object handle is an instance of [ElementHandle]. | ||
|
||
#### objectHandle.asJSON() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Renamed into objectHandle.jsonValue()
docs/api.md
Outdated
If the function, passed to the `page.object`, returns a [Promise], then `page.object` would wait for the promise to resolve and return it's value. | ||
|
||
```js | ||
const aHandle = await page.object(() => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
docs/api.md
Outdated
If the function, passed to the `page.object`, returns a [Promise], then `page.object` would wait for the promise to resolve and return it's value. | ||
|
||
```js | ||
const aHandle = await page.object(() => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
docs/api.md
Outdated
- `...args` <...[Serializable]|[ObjectHandle]> Arguments to pass to `pageFunction` | ||
- returns: <[Promise]<[ObjectHandle]>> Resolves to the return value of `pageFunction` | ||
|
||
If the function, passed to the `page.object`, returns a [Promise], then `page.object` would wait for the promise to resolve and return it's value. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lgtm
This patch: - introduces ExecutionContext class that incapsulates javascript execution context. An examples of execution contexts are workers and frames - introduces ObjectHandle that holds a references to the javascript object in ExecutionContext - inherits ElementHandle from ObjectHandle Fixes puppeteer#382.
64fcfba
to
a557255
Compare
docs/api.md
Outdated
A string can also be passed in instead of a function. | ||
|
||
```js | ||
const aHandle = await page.evaluateHandle('document'); // Handle on the 'document'. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Handle of the 'document'
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Went on with "for".
docs/api.md
Outdated
@@ -1314,8 +1365,129 @@ puppeteer.launch().then(async browser => { | |||
}); | |||
``` | |||
|
|||
### class: ExecutionContext | |||
|
|||
The class represents a context for javascript execution. Examples of javascript contexts are: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: JavaScript
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
docs/api.md
Outdated
### class: ExecutionContext | ||
|
||
The class represents a context for javascript execution. Examples of javascript contexts are: | ||
- each frame has a separate execution context |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
docs/api.md
Outdated
|
||
The class represents a context for javascript execution. Examples of javascript contexts are: | ||
- each frame has a separate execution context | ||
- all kind of workers have their own contexts |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
docs/api.md
Outdated
console.log(await executionContext.evaluate('1 + 2')); // prints "3" | ||
``` | ||
|
||
[JSHandle] instances could be passed as arguments to the `frame.evaluate`: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
could can
Let's try to use a consistent tense.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Replaced here for now. Probably will replace everywhere later.
async getProperties() { | ||
const response = await this._client.send('Runtime.getProperties', { | ||
objectId: this._remoteObject.objectId, | ||
ownProperties: true |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do we want only enumerable and "own" properties? Feels arbitrary.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Non-enumerable might clash with names.
ownProperties is a good call, I removed this flag and added a test.
await helper.releaseObject(this._client, this._remoteObject); | ||
} | ||
|
||
/** |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
lib/ElementHandle.js
Outdated
if (!element.ownerDocument.contains(element)) | ||
return null; | ||
if (element.nodeType !== HTMLElement.ELEMENT_NODE) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why are we making element handles for nodes?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's no good way to differentiate for now; as discussed offline, I'll make them throw for elementHandle methods for now.
const context = this._contextIdToContext.get(contextId); | ||
console.assert(context, 'INTERNAL ERROR: missing context with id = ' + contextId); | ||
if (remoteObject.subtype === 'node') | ||
return new ElementHandle(context, this._client, remoteObject, this._mouse, this._touchscreen); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we check if it is an element here, instead of just a node?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can't: it requires either an async hop to the page, or breaking change to the protocol. We'd be very unhappy with both.
@@ -59,8 +59,8 @@ class MDOutline { | |||
let actualText = element.firstChild.textContent; | |||
let angleIndex = actualText.indexOf('<'); | |||
let spaceIndex = actualText.indexOf(' '); | |||
angleIndex = angleIndex === -1 ? angleText.length : angleIndex; | |||
spaceIndex = spaceIndex === -1 ? spaceIndex.length : spaceIndex + 1; | |||
angleIndex = angleIndex === -1 ? actualText.length : angleIndex; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What does this fix?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's no such thing as 'angleText'. This fixes a typo.
test/test.js
Outdated
it('should work for TextNodes', SX(async function() { | ||
await page.goto(PREFIX + '/input/button.html'); | ||
const buttonTextNode = await page.evaluateHandle(() => document.querySelector('button').firstChild); | ||
await buttonTextNode.click('button'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
element.click doesn't take a string.
This lies about clicking the text node.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
good call about a string. Regarding clicking text node: i switched to throwing in case of text nodes as we discussed offline.
const [frame1, frame2] = page.frames(); | ||
expect(frame1.executionContext()).toBeTruthy(); | ||
expect(frame2.executionContext()).toBeTruthy(); | ||
expect(frame1.executionContext() !== frame2.executionContext()).toBeTruthy(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we evaluate something just so this test makes me feel safer?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure thing; done.
This patch:
execution context. Examples of execution contexts are workers and
frames
object in ExecutionContext
Fixes #382.