diff --git a/docs/api/puppeteer.locator.md b/docs/api/puppeteer.locator.md index 96574f90..e90ddfd5 100644 --- a/docs/api/puppeteer.locator.md +++ b/docs/api/puppeteer.locator.md @@ -37,3 +37,4 @@ export declare abstract class Locator extends EventEmitter | [setVisibility(this, visibility)](./puppeteer.locator.setvisibility.md) | | | | [setWaitForEnabled(this, value)](./puppeteer.locator.setwaitforenabled.md) | | | | [setWaitForStableBoundingBox(this, value)](./puppeteer.locator.setwaitforstableboundingbox.md) | | | +| [wait(options)](./puppeteer.locator.wait.md) | |

Waits for the locator to get the serialized value from the page.

Note this requires the value to be JSON-serializable.

| diff --git a/docs/api/puppeteer.locator.wait.md b/docs/api/puppeteer.locator.wait.md new file mode 100644 index 00000000..e601dab8 --- /dev/null +++ b/docs/api/puppeteer.locator.wait.md @@ -0,0 +1,27 @@ +--- +sidebar_label: Locator.wait +--- + +# Locator.wait() method + +Waits for the locator to get the serialized value from the page. + +Note this requires the value to be JSON-serializable. + +#### Signature: + +```typescript +class Locator { + wait(options?: Readonly): Promise; +} +``` + +## Parameters + +| Parameter | Type | Description | +| --------- | ------------------------------------------------------------- | ------------ | +| options | Readonly<[ActionOptions](./puppeteer.actionoptions.md)> | _(Optional)_ | + +**Returns:** + +Promise<T> diff --git a/packages/puppeteer-core/src/api/locators/Locator.ts b/packages/puppeteer-core/src/api/locators/Locator.ts index 94d268e0..cbbf2e87 100644 --- a/packages/puppeteer-core/src/api/locators/Locator.ts +++ b/packages/puppeteer-core/src/api/locators/Locator.ts @@ -635,6 +635,26 @@ export abstract class Locator extends EventEmitter { */ abstract _wait(options?: Readonly): Observable>; + /** + * Waits for the locator to get the serialized value from the page. + * + * Note this requires the value to be JSON-serializable. + * + * @public + */ + async wait(options?: Readonly): Promise { + const handle = await firstValueFrom( + this._wait(options).pipe( + this.operators.retryAndRaceWithSignalAndTimer(options?.signal) + ) + ); + try { + return await handle.jsonValue(); + } finally { + void handle.dispose().catch(debugError); + } + } + /** * Creates an expectation that is evaluated against located values. * diff --git a/packages/puppeteer-core/src/common/bidi/JSHandle.ts b/packages/puppeteer-core/src/common/bidi/JSHandle.ts index 81baabb8..4fcba7c4 100644 --- a/packages/puppeteer-core/src/common/bidi/JSHandle.ts +++ b/packages/puppeteer-core/src/common/bidi/JSHandle.ts @@ -115,12 +115,9 @@ export class JSHandle extends BaseJSHandle { } override async jsonValue(): Promise { - const value = BidiSerializer.deserialize(this.#remoteValue); - - if (this.#remoteValue.type !== 'undefined' && value === undefined) { - throw new Error('Could not serialize referenced object'); - } - return value; + return await this.evaluate(value => { + return value; + }); } override asElement(): ElementHandle | null { diff --git a/test/TestExpectations.json b/test/TestExpectations.json index 2f06babe..03dc1e9f 100644 --- a/test/TestExpectations.json +++ b/test/TestExpectations.json @@ -23,6 +23,12 @@ "parameters": ["webDriverBiDi"], "expectations": ["PASS"] }, + { + "testIdPattern": "[jshandle.spec] JSHandle JSHandle.jsonValue should not throw for circular objects", + "platforms": ["darwin", "linux", "win32"], + "parameters": ["cdp"], + "expectations": ["FAIL"] + }, { "testIdPattern": "[elementhandle.spec] ElementHandle specs Custom queries *", "platforms": ["darwin", "linux", "win32"], diff --git a/test/src/evaluation.spec.ts b/test/src/evaluation.spec.ts index a7296965..2cee6da5 100644 --- a/test/src/evaluation.spec.ts +++ b/test/src/evaluation.spec.ts @@ -345,24 +345,6 @@ describe('Evaluation specs', function () { }); expect(result).toBe(undefined); }); - it('should be able to throw a tricky error', async () => { - const {page} = await getTestState(); - - const windowHandle = await page.evaluateHandle(() => { - return window; - }); - const errorText = await windowHandle.jsonValue().catch(error_ => { - return error_.message; - }); - const error = await page - .evaluate(errorText => { - throw new Error(errorText); - }, errorText) - .catch(error_ => { - return error_; - }); - expect(error.message).toContain(errorText); - }); it('should accept a string', async () => { const {page} = await getTestState(); diff --git a/test/src/jshandle.spec.ts b/test/src/jshandle.spec.ts index c3697827..35040bad 100644 --- a/test/src/jshandle.spec.ts +++ b/test/src/jshandle.spec.ts @@ -163,7 +163,7 @@ describe('JSHandle', function () { expect(date).toBeInstanceOf(Date); expect(date.toISOString()).toEqual('2017-09-26T00:00:00.000Z'); }); - it('should throw for circular objects', async () => { + it('should not throw for circular objects', async () => { const {page} = await getTestState(); const handle = await page.evaluateHandle(() => { @@ -171,11 +171,7 @@ describe('JSHandle', function () { t.t = t; return t; }); - let error!: Error; - await handle.jsonValue().catch(error_ => { - return (error = error_); - }); - expect(error.message).toContain('Could not serialize referenced object'); + await handle.jsonValue(); }); }); diff --git a/test/src/locator.spec.ts b/test/src/locator.spec.ts index ee3c0274..42c7a77a 100644 --- a/test/src/locator.spec.ts +++ b/test/src/locator.spec.ts @@ -569,4 +569,21 @@ describe('Locator', function () { } }); }); + + describe('Locator.prototype.wait', () => { + it('should work', async () => { + const {page} = await getTestState(); + page.setContent(` + + `); + // This shouldn't throw. + await page.locator('div').wait(); + }); + }); });