diff --git a/.eslintrc.js b/.eslintrc.js index d902880a..bdb925af 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -144,7 +144,7 @@ module.exports = { // Keeps comments formatted. 'rulesdir/prettier-comments': 'error', // Enforces clean up of used resources. - 'rulesdir/use-using': 'off', + 'rulesdir/use-using': 'error', // Brackets keep code readable. curly: ['error', 'all'], // Brackets keep code readable and `return` intentions clear. diff --git a/packages/puppeteer-core/src/api/ElementHandle.ts b/packages/puppeteer-core/src/api/ElementHandle.ts index 1f5244ec..0a804cd2 100644 --- a/packages/puppeteer-core/src/api/ElementHandle.ts +++ b/packages/puppeteer-core/src/api/ElementHandle.ts @@ -28,11 +28,7 @@ import { NodeFor, } from '../common/types.js'; import {KeyInput} from '../common/USKeyboardLayout.js'; -import { - debugError, - isString, - withSourcePuppeteerURLIfNone, -} from '../common/util.js'; +import {isString, withSourcePuppeteerURLIfNone} from '../common/util.js'; import {assert} from '../util/assert.js'; import {AsyncIterableUtil} from '../util/AsyncIterableUtil.js'; @@ -336,15 +332,13 @@ export abstract class ElementHandle< ...args: Params ): Promise>> { pageFunction = withSourcePuppeteerURLIfNone(this.$eval.name, pageFunction); - const elementHandle = await this.$(selector); + using elementHandle = await this.$(selector); if (!elementHandle) { throw new Error( `Error: failed to find element matching selector "${selector}"` ); } - const result = await elementHandle.evaluate(pageFunction, ...args); - await elementHandle.dispose(); - return result; + return await elementHandle.evaluate(pageFunction, ...args); } /** @@ -394,7 +388,7 @@ export abstract class ElementHandle< ): Promise>> { pageFunction = withSourcePuppeteerURLIfNone(this.$$eval.name, pageFunction); const results = await this.$$(selector); - const elements = await this.evaluateHandle( + using elements = await this.evaluateHandle( (_, ...elements) => { return elements; }, @@ -406,7 +400,6 @@ export abstract class ElementHandle< return results.dispose(); }), ]); - await elements.dispose(); return result; } @@ -480,21 +473,17 @@ export abstract class ElementHandle< } async #checkVisibility(visibility: boolean): Promise { - const element = await this.frame.isolatedRealm().adoptHandle(this); - try { - return await this.frame.isolatedRealm().evaluate( - async (PuppeteerUtil, element, visibility) => { - return Boolean(PuppeteerUtil.checkVisibility(element, visibility)); - }, - LazyArg.create(context => { - return context.puppeteerUtil; - }), - element, - visibility - ); - } finally { - await element.dispose(); - } + using element = await this.frame.isolatedRealm().adoptHandle(this); + return await this.frame.isolatedRealm().evaluate( + async (PuppeteerUtil, element, visibility) => { + return Boolean(PuppeteerUtil.checkVisibility(element, visibility)); + }, + LazyArg.create(context => { + return context.puppeteerUtil; + }), + element, + visibility + ); } /** @@ -630,7 +619,8 @@ export abstract class ElementHandle< * Returns the middle point within an element unless a specific offset is provided. */ async clickablePoint(offset?: Offset): Promise { - const box = await this.#clickableBox(); + using adoptedThis = await this.frame.isolatedRealm().adoptHandle(this); + const box = await adoptedThis.#clickableBox(); if (!box) { throw new Error('Node is either not clickable or not an Element'); } @@ -903,8 +893,7 @@ export abstract class ElementHandle< } async #clickableBox(): Promise { - const adoptedThis = await this.frame.isolatedRealm().adoptHandle(this); - const boxes = await adoptedThis.evaluate(element => { + const boxes = await this.evaluate(element => { if (!(element instanceof Element)) { return null; } @@ -912,46 +901,44 @@ export abstract class ElementHandle< return {x: rect.x, y: rect.y, width: rect.width, height: rect.height}; }); }); - void adoptedThis.dispose().catch(debugError); if (!boxes?.length) { return null; } await this.#intersectBoundingBoxesWithFrame(boxes); - let frame: Frame | null | undefined = this.frame; - let element: HandleFor | null | undefined; - while ((element = await frame?.frameElement())) { - try { - element = await element.frame.isolatedRealm().transferHandle(element); - const parentBox = await element.evaluate(element => { - // Element is not visible. - if (element.getClientRects().length === 0) { - return null; - } - const rect = element.getBoundingClientRect(); - const style = window.getComputedStyle(element); - return { - left: - rect.left + - parseInt(style.paddingLeft, 10) + - parseInt(style.borderLeftWidth, 10), - top: - rect.top + - parseInt(style.paddingTop, 10) + - parseInt(style.borderTopWidth, 10), - }; - }); - if (!parentBox) { + let frame = this.frame; + let parentFrame: Frame | null | undefined; + while ((parentFrame = frame?.parentFrame())) { + using handle = await frame.frameElement(); + if (!handle) { + throw new Error('Unsupported frame type'); + } + const parentBox = await handle.evaluate(element => { + // Element is not visible. + if (element.getClientRects().length === 0) { return null; } - for (const box of boxes) { - box.x += parentBox.left; - box.y += parentBox.top; - } - await element.#intersectBoundingBoxesWithFrame(boxes); - frame = frame?.parentFrame(); - } finally { - void element.dispose().catch(debugError); + const rect = element.getBoundingClientRect(); + const style = window.getComputedStyle(element); + return { + left: + rect.left + + parseInt(style.paddingLeft, 10) + + parseInt(style.borderLeftWidth, 10), + top: + rect.top + + parseInt(style.paddingTop, 10) + + parseInt(style.borderTopWidth, 10), + }; + }); + if (!parentBox) { + return null; } + for (const box of boxes) { + box.x += parentBox.left; + box.y += parentBox.top; + } + await handle.#intersectBoundingBoxesWithFrame(boxes); + frame = parentFrame; } const box = boxes.find(box => { return box.width >= 1 && box.height >= 1; @@ -986,7 +973,7 @@ export abstract class ElementHandle< * or `null` if the element is not visible. */ async boundingBox(): Promise { - const adoptedThis = await this.frame.isolatedRealm().adoptHandle(this); + using adoptedThis = await this.frame.isolatedRealm().adoptHandle(this); const box = await adoptedThis.evaluate(element => { if (!(element instanceof Element)) { return null; @@ -998,11 +985,10 @@ export abstract class ElementHandle< const rect = element.getBoundingClientRect(); return {x: rect.x, y: rect.y, width: rect.width, height: rect.height}; }); - void adoptedThis.dispose().catch(debugError); if (!box) { return null; } - const offset = await this.#getTopLeftCornerOfFrame(); + const offset = await adoptedThis.#getTopLeftCornerOfFrame(); if (!offset) { return null; } @@ -1023,80 +1009,81 @@ export abstract class ElementHandle< * Each Point is an object `{x, y}`. Box points are sorted clock-wise. */ async boxModel(): Promise { - const adoptedThis = await this.frame.isolatedRealm().adoptHandle(this); - const model = await adoptedThis.evaluate(element => { - if (!(element instanceof Element)) { - return null; - } - // Element is not visible. - if (element.getClientRects().length === 0) { - return null; - } - const rect = element.getBoundingClientRect(); - const style = window.getComputedStyle(element); - const offsets = { - padding: { - left: parseInt(style.paddingLeft, 10), - top: parseInt(style.paddingTop, 10), - right: parseInt(style.paddingRight, 10), - bottom: parseInt(style.paddingBottom, 10), - }, - margin: { - left: -parseInt(style.marginLeft, 10), - top: -parseInt(style.marginTop, 10), - right: -parseInt(style.marginRight, 10), - bottom: -parseInt(style.marginBottom, 10), - }, - border: { - left: parseInt(style.borderLeft, 10), - top: parseInt(style.borderTop, 10), - right: parseInt(style.borderRight, 10), - bottom: parseInt(style.borderBottom, 10), - }, - }; - const border: Quad = [ - {x: rect.left, y: rect.top}, - {x: rect.left + rect.width, y: rect.top}, - {x: rect.left + rect.width, y: rect.top + rect.bottom}, - {x: rect.left, y: rect.top + rect.bottom}, - ]; - const padding = transformQuadWithOffsets(border, offsets.border); - const content = transformQuadWithOffsets(padding, offsets.padding); - const margin = transformQuadWithOffsets(border, offsets.margin); - return { - content, - padding, - border, - margin, - width: rect.width, - height: rect.height, - }; - - function transformQuadWithOffsets( - quad: Quad, - offsets: {top: number; left: number; right: number; bottom: number} - ): Quad { - return [ - { - x: quad[0].x + offsets.left, - y: quad[0].y + offsets.top, + const model = await (async () => { + using adoptedThis = await this.frame.isolatedRealm().adoptHandle(this); + return await adoptedThis.evaluate(element => { + if (!(element instanceof Element)) { + return null; + } + // Element is not visible. + if (element.getClientRects().length === 0) { + return null; + } + const rect = element.getBoundingClientRect(); + const style = window.getComputedStyle(element); + const offsets = { + padding: { + left: parseInt(style.paddingLeft, 10), + top: parseInt(style.paddingTop, 10), + right: parseInt(style.paddingRight, 10), + bottom: parseInt(style.paddingBottom, 10), }, - { - x: quad[1].x - offsets.right, - y: quad[1].y + offsets.top, + margin: { + left: -parseInt(style.marginLeft, 10), + top: -parseInt(style.marginTop, 10), + right: -parseInt(style.marginRight, 10), + bottom: -parseInt(style.marginBottom, 10), }, - { - x: quad[2].x - offsets.right, - y: quad[2].y - offsets.bottom, - }, - { - x: quad[3].x + offsets.left, - y: quad[3].y - offsets.bottom, + border: { + left: parseInt(style.borderLeft, 10), + top: parseInt(style.borderTop, 10), + right: parseInt(style.borderRight, 10), + bottom: parseInt(style.borderBottom, 10), }, + }; + const border: Quad = [ + {x: rect.left, y: rect.top}, + {x: rect.left + rect.width, y: rect.top}, + {x: rect.left + rect.width, y: rect.top + rect.bottom}, + {x: rect.left, y: rect.top + rect.bottom}, ]; - } - }); - void adoptedThis.dispose().catch(debugError); + const padding = transformQuadWithOffsets(border, offsets.border); + const content = transformQuadWithOffsets(padding, offsets.padding); + const margin = transformQuadWithOffsets(border, offsets.margin); + return { + content, + padding, + border, + margin, + width: rect.width, + height: rect.height, + }; + + function transformQuadWithOffsets( + quad: Quad, + offsets: {top: number; left: number; right: number; bottom: number} + ): Quad { + return [ + { + x: quad[0].x + offsets.left, + y: quad[0].y + offsets.top, + }, + { + x: quad[1].x - offsets.right, + y: quad[1].y + offsets.top, + }, + { + x: quad[2].x - offsets.right, + y: quad[2].y - offsets.bottom, + }, + { + x: quad[3].x + offsets.left, + y: quad[3].y - offsets.bottom, + }, + ]; + } + }); + })(); if (!model) { return null; } @@ -1120,38 +1107,37 @@ export abstract class ElementHandle< async #getTopLeftCornerOfFrame() { const point = {x: 0, y: 0}; - let frame: Frame | null | undefined = this.frame; - let element: HandleFor | null | undefined; - while ((element = await frame?.frameElement())) { - try { - element = await element.frame.isolatedRealm().transferHandle(element); - const parentBox = await element.evaluate(element => { - // Element is not visible. - if (element.getClientRects().length === 0) { - return null; - } - const rect = element.getBoundingClientRect(); - const style = window.getComputedStyle(element); - return { - left: - rect.left + - parseInt(style.paddingLeft, 10) + - parseInt(style.borderLeftWidth, 10), - top: - rect.top + - parseInt(style.paddingTop, 10) + - parseInt(style.borderTopWidth, 10), - }; - }); - if (!parentBox) { + let frame = this.frame; + let parentFrame: Frame | null | undefined; + while ((parentFrame = frame?.parentFrame())) { + using handle = await frame.frameElement(); + if (!handle) { + throw new Error('Unsupported frame type'); + } + const parentBox = await handle.evaluate(element => { + // Element is not visible. + if (element.getClientRects().length === 0) { return null; } - point.x += parentBox.left; - point.y += parentBox.top; - frame = frame?.parentFrame(); - } finally { - void element.dispose().catch(debugError); + const rect = element.getBoundingClientRect(); + const style = window.getComputedStyle(element); + return { + left: + rect.left + + parseInt(style.paddingLeft, 10) + + parseInt(style.borderLeftWidth, 10), + top: + rect.top + + parseInt(style.paddingTop, 10) + + parseInt(style.borderTopWidth, 10), + }; + }); + if (!parentBox) { + return null; } + point.x += parentBox.left; + point.y += parentBox.top; + frame = parentFrame; } return point; } @@ -1173,17 +1159,15 @@ export abstract class ElementHandle< * @internal */ protected async assertConnectedElement(): Promise { - const error = await this.evaluate( - async (element): Promise => { - if (!element.isConnected) { - return 'Node is detached from document'; - } - if (element.nodeType !== Node.ELEMENT_NODE) { - return 'Node is not of type HTMLElement'; - } - return; + const error = await this.evaluate(async element => { + if (!element.isConnected) { + return 'Node is detached from document'; } - ); + if (element.nodeType !== Node.ELEMENT_NODE) { + return 'Node is not of type HTMLElement'; + } + return; + }); if (error) { throw new Error(error); @@ -1216,20 +1200,16 @@ export abstract class ElementHandle< */ async isIntersectingViewport( this: ElementHandle, - options?: { + options: { threshold?: number; - } + } = {} ): Promise { await this.assertConnectedElement(); - - const {threshold = 0} = options ?? {}; - const svgHandle = await this.#asSVGElementHandle(this); - const intersectionTarget: ElementHandle = svgHandle - ? await this.#getOwnerSVGElement(svgHandle) - : this; - - try { - return await intersectionTarget.evaluate(async (element, threshold) => { + // eslint-disable-next-line rulesdir/use-using -- Returns `this`. + const handle = await this.#asSVGElementHandle(); + using target = handle && (await handle.#getOwnerSVGElement()); + return await ((target ?? this) as ElementHandle).evaluate( + async (element, threshold) => { const visibleRatio = await new Promise(resolve => { const observer = new IntersectionObserver(entries => { resolve(entries[0]!.intersectionRatio); @@ -1238,12 +1218,9 @@ export abstract class ElementHandle< observer.observe(element); }); return threshold === 1 ? visibleRatio === 1 : visibleRatio > threshold; - }, threshold); - } finally { - if (intersectionTarget !== this) { - await intersectionTarget.dispose(); - } - } + }, + options.threshold ?? 0 + ); } /** @@ -1251,8 +1228,9 @@ export abstract class ElementHandle< * or by calling element.scrollIntoView. */ async scrollIntoView(this: ElementHandle): Promise { - await this.assertConnectedElement(); - await this.evaluate(async (element): Promise => { + using adoptedThis = await this.frame.isolatedRealm().adoptHandle(this); + await adoptedThis.assertConnectedElement(); + await adoptedThis.evaluate(async (element): Promise => { element.scrollIntoView({ block: 'center', inline: 'center', @@ -1266,24 +1244,24 @@ export abstract class ElementHandle< * etc.). */ async #asSVGElementHandle( - handle: ElementHandle + this: ElementHandle ): Promise | null> { if ( - await handle.evaluate(element => { + await this.evaluate(element => { return element instanceof SVGElement; }) ) { - return handle as ElementHandle; + return this as ElementHandle; } else { return null; } } async #getOwnerSVGElement( - handle: ElementHandle + this: ElementHandle ): Promise> { // SVGSVGElement.ownerSVGElement === null. - return await handle.evaluateHandle(element => { + return await this.evaluateHandle(element => { if (element instanceof SVGSVGElement) { return element; } diff --git a/packages/puppeteer-core/src/api/Frame.ts b/packages/puppeteer-core/src/api/Frame.ts index 98575c72..e7889dd1 100644 --- a/packages/puppeteer-core/src/api/Frame.ts +++ b/packages/puppeteer-core/src/api/Frame.ts @@ -37,12 +37,12 @@ import { InnerLazyParams, NodeFor, } from '../common/types.js'; -import {debugError, importFSPromises} from '../common/util.js'; +import {importFSPromises} from '../common/util.js'; import {TaskManager} from '../common/WaitTask.js'; import {KeyboardTypeOptions} from './Input.js'; import {JSHandle} from './JSHandle.js'; -import {Locator, FunctionLocator, NodeLocator} from './locators/locators.js'; +import {FunctionLocator, Locator, NodeLocator} from './locators/locators.js'; /** * @internal @@ -389,15 +389,14 @@ export class Frame extends EventEmitter { if (!parentFrame) { return null; } - const list = await parentFrame.isolatedRealm().evaluateHandle(() => { + using list = await parentFrame.isolatedRealm().evaluateHandle(() => { return document.querySelectorAll('iframe'); }); - for await (const iframe of transposeIterableHandle(list)) { + for await (using iframe of transposeIterableHandle(list)) { const frame = await iframe.contentFrame(); if (frame._id === this._id) { - return iframe; + return iframe.move(); } - void iframe.dispose().catch(debugError); } return null; } diff --git a/packages/puppeteer-core/src/api/locators/Locator.ts b/packages/puppeteer-core/src/api/locators/Locator.ts index e4b7e670..ef0f2d34 100644 --- a/packages/puppeteer-core/src/api/locators/Locator.ts +++ b/packages/puppeteer-core/src/api/locators/Locator.ts @@ -692,12 +692,8 @@ export abstract class Locator extends EventEmitter { * @public */ async wait(options?: Readonly): Promise { - const handle = await this.waitHandle(options); - try { - return await handle.jsonValue(); - } finally { - void handle.dispose().catch(debugError); - } + using handle = await this.waitHandle(options); + return await handle.jsonValue(); } /** diff --git a/packages/puppeteer-core/src/common/Binding.ts b/packages/puppeteer-core/src/common/Binding.ts index 01268bbe..3cb20638 100644 --- a/packages/puppeteer-core/src/common/Binding.ts +++ b/packages/puppeteer-core/src/common/Binding.ts @@ -32,11 +32,11 @@ export class Binding { args: unknown[], isTrivial: boolean ): Promise { - const garbage = []; + const stack = new DisposableStack(); try { if (!isTrivial) { // Getting non-trivial arguments. - const handles = await context.evaluateHandle( + using handles = await context.evaluateHandle( (name, seq) => { // @ts-expect-error Code is evaluated in a different context. return globalThis[name].args.get(seq); @@ -44,25 +44,21 @@ export class Binding { this.#name, id ); - try { - const properties = await handles.getProperties(); - for (const [index, handle] of properties) { - // This is not straight-forward since some arguments can stringify, but - // aren't plain objects so add subtypes when the use-case arises. - if (index in args) { - switch (handle.remoteObject().subtype) { - case 'node': - args[+index] = handle; - break; - default: - garbage.push(handle.dispose()); - } - } else { - garbage.push(handle.dispose()); + const properties = await handles.getProperties(); + for (const [index, handle] of properties) { + // This is not straight-forward since some arguments can stringify, but + // aren't plain objects so add subtypes when the use-case arises. + if (index in args) { + switch (handle.remoteObject().subtype) { + case 'node': + args[+index] = handle; + break; + default: + stack.use(handle); } + } else { + stack.use(handle); } - } finally { - await handles.dispose(); } } @@ -80,7 +76,7 @@ export class Binding { for (const arg of args) { if (arg instanceof JSHandle) { - garbage.push(arg.dispose()); + stack.use(arg); } } } catch (error) { @@ -116,8 +112,6 @@ export class Binding { ) .catch(debugError); } - } finally { - await Promise.all(garbage); } } } diff --git a/packages/puppeteer-core/src/common/HandleIterator.ts b/packages/puppeteer-core/src/common/HandleIterator.ts index d5df382f..25a2e988 100644 --- a/packages/puppeteer-core/src/common/HandleIterator.ts +++ b/packages/puppeteer-core/src/common/HandleIterator.ts @@ -31,7 +31,7 @@ async function* fastTransposeIteratorHandle( iterator: JSHandle>, size: number ) { - const array = await iterator.evaluateHandle(async (iterator, size) => { + using array = await iterator.evaluateHandle(async (iterator, size) => { const results = []; while (results.length < size) { const result = await iterator.next(); @@ -43,8 +43,14 @@ async function* fastTransposeIteratorHandle( return results; }, size); const properties = (await array.getProperties()) as Map>; - await array.dispose(); - yield* properties.values(); + const handles = properties.values(); + using stack = new DisposableStack(); + stack.defer(() => { + for (using handle of handles) { + handle[Symbol.dispose](); + } + }); + yield* handles; return properties.size === 0; } @@ -57,12 +63,8 @@ async function* transposeIteratorHandle( iterator: JSHandle> ) { let size = DEFAULT_BATCH_SIZE; - try { - while (!(yield* fastTransposeIteratorHandle(iterator, size))) { - size <<= 1; - } - } finally { - await iterator.dispose(); + while (!(yield* fastTransposeIteratorHandle(iterator, size))) { + size <<= 1; } } @@ -74,11 +76,10 @@ type AwaitableIterator = Iterator | AsyncIterator; export async function* transposeIterableHandle( handle: JSHandle> ): AsyncIterableIterator> { - yield* transposeIteratorHandle( - await handle.evaluateHandle(iterable => { - return (async function* () { - yield* iterable; - })(); - }) - ); + using generatorHandle = await handle.evaluateHandle(iterable => { + return (async function* () { + yield* iterable; + })(); + }); + yield* transposeIteratorHandle(generatorHandle); } diff --git a/packages/puppeteer-core/src/common/IsolatedWorld.ts b/packages/puppeteer-core/src/common/IsolatedWorld.ts index ff4d6faf..0a64aac3 100644 --- a/packages/puppeteer-core/src/common/IsolatedWorld.ts +++ b/packages/puppeteer-core/src/common/IsolatedWorld.ts @@ -104,7 +104,6 @@ export interface IsolatedWorldChart { */ export class IsolatedWorld implements Realm { #frame: Frame; - #document?: ElementHandle; #context = Deferred.create(); #detached = false; @@ -149,7 +148,6 @@ export class IsolatedWorld implements Realm { } clearContext(): void { - this.#document = undefined; this.#context = Deferred.create(); } @@ -216,31 +214,27 @@ export class IsolatedWorld implements Realm { async $( selector: Selector ): Promise> | null> { - const document = await this.document(); - return document.$(selector); + using document = await this.document(); + return await document.$(selector); } async $$( selector: Selector ): Promise>>> { - const document = await this.document(); - return document.$$(selector); + using document = await this.document(); + return await document.$$(selector); } async document(): Promise> { - if (this.#document) { - return this.#document; - } - const context = await this.executionContext(); - this.#document = await context.evaluateHandle(() => { + // TODO(#10813): Implement document caching. + return await this.evaluateHandle(() => { return document; }); - return this.#document; } async $x(expression: string): Promise>> { - const document = await this.document(); - return document.$x(expression); + using document = await this.document(); + return await document.$x(expression); } async $eval< @@ -256,8 +250,8 @@ export class IsolatedWorld implements Realm { ...args: Params ): Promise>> { pageFunction = withSourcePuppeteerURLIfNone(this.$eval.name, pageFunction); - const document = await this.document(); - return document.$eval(selector, pageFunction, ...args); + using document = await this.document(); + return await document.$eval(selector, pageFunction, ...args); } async $$eval< @@ -273,8 +267,8 @@ export class IsolatedWorld implements Realm { ...args: Params ): Promise>> { pageFunction = withSourcePuppeteerURLIfNone(this.$$eval.name, pageFunction); - const document = await this.document(); - return document.$$eval(selector, pageFunction, ...args); + using document = await this.document(); + return await document.$$eval(selector, pageFunction, ...args); } async content(): Promise { @@ -315,39 +309,34 @@ export class IsolatedWorld implements Realm { selector: string, options?: Readonly ): Promise { - const handle = await this.$(selector); + using handle = await this.$(selector); assert(handle, `No element found for selector: ${selector}`); await handle.click(options); await handle.dispose(); } async focus(selector: string): Promise { - const handle = await this.$(selector); + using handle = await this.$(selector); assert(handle, `No element found for selector: ${selector}`); await handle.focus(); - await handle.dispose(); } async hover(selector: string): Promise { - const handle = await this.$(selector); + using handle = await this.$(selector); assert(handle, `No element found for selector: ${selector}`); await handle.hover(); - await handle.dispose(); } async select(selector: string, ...values: string[]): Promise { - const handle = await this.$(selector); + using handle = await this.$(selector); assert(handle, `No element found for selector: ${selector}`); - const result = await handle.select(...values); - await handle.dispose(); - return result; + return await handle.select(...values); } async tap(selector: string): Promise { - const handle = await this.$(selector); + using handle = await this.$(selector); assert(handle, `No element found for selector: ${selector}`); await handle.tap(); - await handle.dispose(); } async type( @@ -355,10 +344,9 @@ export class IsolatedWorld implements Realm { text: string, options?: Readonly ): Promise { - const handle = await this.$(selector); + using handle = await this.$(selector); assert(handle, `No element found for selector: ${selector}`); await handle.type(text, options); - await handle.dispose(); } // If multiple waitFor are set up asynchronously, we need to wait for the diff --git a/packages/puppeteer-core/src/common/Page.ts b/packages/puppeteer-core/src/common/Page.ts index 85ea18e9..ba94fac4 100644 --- a/packages/puppeteer-core/src/common/Page.ts +++ b/packages/puppeteer-core/src/common/Page.ts @@ -433,11 +433,11 @@ export class CDPPage extends Page { assert(frame, 'This should never happen.'); // This is guaranteed to be an HTMLInputElement handle by the event. - const handle = (await frame.worlds[MAIN_WORLD].adoptBackendNode( + using handle = (await frame.worlds[MAIN_WORLD].adoptBackendNode( event.backendNodeId )) as ElementHandle; - const fileChooser = new FileChooser(handle, event); + const fileChooser = new FileChooser(handle.move(), event); for (const promise of this.#fileChooserDeferreds) { promise.resolve(fileChooser); } @@ -885,6 +885,8 @@ export class CDPPage extends Page { return; } const textTokens = []; + // eslint-disable-next-line max-len -- The comment is long. + // eslint-disable-next-line rulesdir/use-using -- These are not owned by this function. for (const arg of args) { const remoteObject = arg.remoteObject(); if (remoteObject.objectId) { diff --git a/packages/puppeteer-core/src/common/QueryHandler.ts b/packages/puppeteer-core/src/common/QueryHandler.ts index 5e4d8c70..b04259e7 100644 --- a/packages/puppeteer-core/src/common/QueryHandler.ts +++ b/packages/puppeteer-core/src/common/QueryHandler.ts @@ -107,7 +107,7 @@ export class QueryHandler { selector: string ): AwaitableIterable> { element.assertElementHasWorld(); - const handle = await element.evaluateHandle( + using handle = await element.evaluateHandle( this._querySelectorAll, selector, LazyArg.create(context => { @@ -127,7 +127,7 @@ export class QueryHandler { selector: string ): Promise | null> { element.assertElementHasWorld(); - const result = await element.evaluateHandle( + using result = await element.evaluateHandle( this._querySelector, selector, LazyArg.create(context => { @@ -135,10 +135,9 @@ export class QueryHandler { }) ); if (!(result instanceof ElementHandle)) { - await result.dispose(); return null; } - return result; + return result.move(); } /** @@ -153,21 +152,22 @@ export class QueryHandler { selector: string, options: WaitForSelectorOptions ): Promise | null> { - let frame: Frame; - let element: ElementHandle | undefined; - if (!(elementOrFrame instanceof ElementHandle)) { - frame = elementOrFrame; - } else { + let frame!: Frame; + using element = await (async () => { + if (!(elementOrFrame instanceof ElementHandle)) { + frame = elementOrFrame; + return; + } frame = elementOrFrame.frame; - element = await frame.isolatedRealm().adoptHandle(elementOrFrame); - } + return await frame.isolatedRealm().adoptHandle(elementOrFrame); + })(); const {visible = false, hidden = false, timeout, signal} = options; try { signal?.throwIfAborted(); - const handle = await frame.isolatedRealm().waitForFunction( + using handle = await frame.isolatedRealm().waitForFunction( async (PuppeteerUtil, query, selector, root, visible) => { const querySelector = PuppeteerUtil.createFunction( query @@ -195,15 +195,13 @@ export class QueryHandler { ); if (signal?.aborted) { - await handle.dispose(); throw signal.reason; } if (!(handle instanceof ElementHandle)) { - await handle.dispose(); return null; } - return frame.mainRealm().transferHandle(handle); + return await frame.mainRealm().transferHandle(handle); } catch (error) { if (!isErrorLike(error)) { throw error; @@ -213,10 +211,6 @@ export class QueryHandler { } error.message = `Waiting for selector \`${selector}\` failed: ${error.message}`; throw error; - } finally { - if (element) { - await element.dispose(); - } } } } diff --git a/packages/puppeteer-core/src/common/bidi/ElementHandle.ts b/packages/puppeteer-core/src/common/bidi/ElementHandle.ts index a8ccd3ae..5bf2e34a 100644 --- a/packages/puppeteer-core/src/common/bidi/ElementHandle.ts +++ b/packages/puppeteer-core/src/common/bidi/ElementHandle.ts @@ -20,7 +20,6 @@ import { AutofillData, ElementHandle as BaseElementHandle, } from '../../api/ElementHandle.js'; -import {debugError} from '../util.js'; import {Frame} from './Frame.js'; import {JSHandle as BidiJSHandle, JSHandle} from './JSHandle.js'; @@ -86,15 +85,13 @@ export class ElementHandle< this: ElementHandle ): Promise; override async contentFrame(): Promise { - const adoptedThis = await this.frame.isolatedRealm().adoptHandle(this); - const handle = (await adoptedThis.evaluateHandle(element => { + using adoptedThis = await this.frame.isolatedRealm().adoptHandle(this); + using handle = (await adoptedThis.evaluateHandle(element => { if (element instanceof HTMLIFrameElement) { return element.contentWindow; } return; })) as BidiJSHandle; - void handle.dispose().catch(debugError); - void adoptedThis.dispose().catch(debugError); const value = handle.remoteValue(); if (value.type === 'window') { return this.frame.page().frame(value.value.context); diff --git a/packages/puppeteer-core/src/common/bidi/JSHandle.ts b/packages/puppeteer-core/src/common/bidi/JSHandle.ts index f0cd61b2..6d3f62b4 100644 --- a/packages/puppeteer-core/src/common/bidi/JSHandle.ts +++ b/packages/puppeteer-core/src/common/bidi/JSHandle.ts @@ -106,9 +106,9 @@ export class JSHandle extends BaseJSHandle { ); for (const [key, value] of Object.entries(keys)) { - const handle = results[key as any]; + using handle = results[key as any]; if (handle) { - map.set(value, handle); + map.set(value, handle.move()); } } diff --git a/packages/puppeteer-core/src/common/bidi/Realm.ts b/packages/puppeteer-core/src/common/bidi/Realm.ts index 3137d580..16c7e62a 100644 --- a/packages/puppeteer-core/src/common/bidi/Realm.ts +++ b/packages/puppeteer-core/src/common/bidi/Realm.ts @@ -56,8 +56,7 @@ export class Realm extends EventEmitter { const promise = this.internalPuppeteerUtil; this.internalPuppeteerUtil = undefined; try { - const util = await promise; - await util?.dispose(); + await (await promise)?.dispose(); } catch (error) { debugError(error); } diff --git a/packages/puppeteer-core/src/common/bidi/Sandbox.ts b/packages/puppeteer-core/src/common/bidi/Sandbox.ts index 558c0e9d..9ab9dcd4 100644 --- a/packages/puppeteer-core/src/common/bidi/Sandbox.ts +++ b/packages/puppeteer-core/src/common/bidi/Sandbox.ts @@ -96,8 +96,7 @@ export class Sandbox implements RealmBase { } async document(): Promise> { - // TODO(jrandolf): We should try to cache this because we need to dispose - // this when it's unused. + // TODO(#10813): Implement document caching. return await this.#realm.evaluateHandle(() => { return document; }); @@ -106,15 +105,15 @@ export class Sandbox implements RealmBase { async $( selector: Selector ): Promise> | null> { - const document = await this.document(); - return document.$(selector); + using document = await this.document(); + return await document.$(selector); } async $$( selector: Selector ): Promise>>> { - const document = await this.document(); - return document.$$(selector); + using document = await this.document(); + return await document.$$(selector); } async $eval< @@ -130,8 +129,8 @@ export class Sandbox implements RealmBase { ...args: Params ): Promise>> { pageFunction = withSourcePuppeteerURLIfNone(this.$eval.name, pageFunction); - const document = await this.document(); - return document.$eval(selector, pageFunction, ...args); + using document = await this.document(); + return await document.$eval(selector, pageFunction, ...args); } async $$eval< @@ -147,13 +146,13 @@ export class Sandbox implements RealmBase { ...args: Params ): Promise>> { pageFunction = withSourcePuppeteerURLIfNone(this.$$eval.name, pageFunction); - const document = await this.document(); - return document.$$eval(selector, pageFunction, ...args); + using document = await this.document(); + return await document.$$eval(selector, pageFunction, ...args); } async $x(expression: string): Promise>> { - const document = await this.document(); - return document.$x(expression); + using document = await this.document(); + return await document.$x(expression); } async evaluateHandle< @@ -249,39 +248,34 @@ export class Sandbox implements RealmBase { selector: string, options?: Readonly ): Promise { - const handle = await this.$(selector); + using handle = await this.$(selector); assert(handle, `No element found for selector: ${selector}`); await handle.click(options); - await handle.dispose(); } async focus(selector: string): Promise { - const handle = await this.$(selector); + using handle = await this.$(selector); assert(handle, `No element found for selector: ${selector}`); await handle.focus(); - await handle.dispose(); } async hover(selector: string): Promise { - const handle = await this.$(selector); + using handle = await this.$(selector); assert(handle, `No element found for selector: ${selector}`); await handle.hover(); - await handle.dispose(); } async select(selector: string, ...values: string[]): Promise { - const handle = await this.$(selector); + using handle = await this.$(selector); assert(handle, `No element found for selector: ${selector}`); const result = await handle.select(...values); - await handle.dispose(); return result; } async tap(selector: string): Promise { - const handle = await this.$(selector); + using handle = await this.$(selector); assert(handle, `No element found for selector: ${selector}`); await handle.tap(); - await handle.dispose(); } async type( @@ -289,9 +283,8 @@ export class Sandbox implements RealmBase { text: string, options?: Readonly ): Promise { - const handle = await this.$(selector); + using handle = await this.$(selector); assert(handle, `No element found for selector: ${selector}`); await handle.type(text, options); - await handle.dispose(); } } diff --git a/packages/puppeteer-core/src/common/bidi/Serializer.ts b/packages/puppeteer-core/src/common/bidi/Serializer.ts index 0adfbb1e..ca2c9529 100644 --- a/packages/puppeteer-core/src/common/bidi/Serializer.ts +++ b/packages/puppeteer-core/src/common/bidi/Serializer.ts @@ -149,6 +149,7 @@ export class BidiSerializer { if (arg instanceof LazyArg) { arg = await arg.get(context); } + // eslint-disable-next-line rulesdir/use-using -- We want this to continue living. const objectHandle = arg && (arg instanceof JSHandle || arg instanceof ElementHandle) ? arg diff --git a/test/src/accessibility.spec.ts b/test/src/accessibility.spec.ts index bbe0f6a6..051ef170 100644 --- a/test/src/accessibility.spec.ts +++ b/test/src/accessibility.spec.ts @@ -200,7 +200,7 @@ describe('Accessibility', function () { async function getAccessibleName(page: any, element: any) { return (await page.accessibility.snapshot({root: element})).name; } - const button = await page.$('button'); + using button = await page.$('button'); expect(await getAccessibleName(page, button)).toEqual('Show'); await button?.click(); await page.waitForSelector('aria/Hide'); @@ -483,7 +483,7 @@ describe('Accessibility', function () { await page.setContent(``); - const button = (await page.$('button'))!; + using button = (await page.$('button'))!; expect(await page.accessibility.snapshot({root: button})).toEqual({ role: 'button', name: 'My Button', @@ -494,7 +494,7 @@ describe('Accessibility', function () { await page.setContent(``); - const input = (await page.$('input'))!; + using input = (await page.$('input'))!; expect(await page.accessibility.snapshot({root: input})).toEqual({ role: 'textbox', name: 'My Input', @@ -512,7 +512,7 @@ describe('Accessibility', function () { `); - const menu = (await page.$('div[role="menu"]'))!; + using menu = (await page.$('div[role="menu"]'))!; expect(await page.accessibility.snapshot({root: menu})).toEqual({ role: 'menu', name: 'My Menu', @@ -528,7 +528,7 @@ describe('Accessibility', function () { const {page} = await getTestState(); await page.setContent(``); - const button = (await page.$('button'))!; + using button = (await page.$('button'))!; await page.$eval('button', button => { return button.remove(); }); @@ -538,7 +538,7 @@ describe('Accessibility', function () { const {page} = await getTestState(); await page.setContent(`
`); - const div = (await page.$('div'))!; + using div = (await page.$('div'))!; expect(await page.accessibility.snapshot({root: div})).toEqual(null); expect( await page.accessibility.snapshot({ diff --git a/test/src/ariaqueryhandler.spec.ts b/test/src/ariaqueryhandler.spec.ts index e5d01e83..fb52d227 100644 --- a/test/src/ariaqueryhandler.spec.ts +++ b/test/src/ariaqueryhandler.spec.ts @@ -39,49 +39,67 @@ describe('AriaQueryHandler', () => { }); expect(id).toBe('btn'); }; - let button = await page.$( - 'aria/Submit button and some spaces[role="button"]' - ); - await expectFound(button); - button = await page.$( - "aria/Submit button and some spaces[role='button']" - ); - await expectFound(button); - button = await page.$( + { + using button = await page.$( + 'aria/Submit button and some spaces[role="button"]' + ); + await expectFound(button); + } + { + using button = await page.$( + "aria/Submit button and some spaces[role='button']" + ); + await expectFound(button); + } + using button = await page.$( 'aria/ Submit button and some spaces[role="button"]' ); await expectFound(button); - button = await page.$( - 'aria/Submit button and some spaces [role="button"]' - ); - await expectFound(button); - button = await page.$( - 'aria/Submit button and some spaces [ role = "button" ] ' - ); - await expectFound(button); - button = await page.$( - 'aria/[role="button"]Submit button and some spaces' - ); - await expectFound(button); - button = await page.$( - 'aria/Submit button [role="button"]and some spaces' - ); - await expectFound(button); - button = await page.$( - 'aria/[name=" Submit button and some spaces"][role="button"]' - ); - await expectFound(button); - button = await page.$( - "aria/[name=' Submit button and some spaces'][role='button']" - ); - await expectFound(button); - button = await page.$( - 'aria/ignored[name="Submit button and some spaces"][role="button"]' - ); - await expectFound(button); - await expect(page.$('aria/smth[smth="true"]')).rejects.toThrow( - 'Unknown aria attribute "smth" in selector' - ); + { + using button = await page.$( + 'aria/Submit button and some spaces [role="button"]' + ); + await expectFound(button); + } + { + using button = await page.$( + 'aria/Submit button and some spaces [ role = "button" ] ' + ); + await expectFound(button); + } + { + using button = await page.$( + 'aria/[role="button"]Submit button and some spaces' + ); + await expectFound(button); + } + { + using button = await page.$( + 'aria/Submit button [role="button"]and some spaces' + ); + await expectFound(button); + } + { + using button = await page.$( + 'aria/[name=" Submit button and some spaces"][role="button"]' + ); + await expectFound(button); + } + { + using button = await page.$( + "aria/[name=' Submit button and some spaces'][role='button']" + ); + await expectFound(button); + } + { + using button = await page.$( + 'aria/ignored[name="Submit button and some spaces"][role="button"]' + ); + await expectFound(button); + await expect(page.$('aria/smth[smth="true"]')).rejects.toThrow( + 'Unknown aria attribute "smth" in selector' + ); + } }); }); @@ -91,7 +109,7 @@ describe('AriaQueryHandler', () => { await page.setContent( '
' ); - const button = (await page.$( + using button = (await page.$( 'aria/[role="button"]' )) as ElementHandle; const id = await button!.evaluate(button => { @@ -105,7 +123,7 @@ describe('AriaQueryHandler', () => { await page.setContent( '
' ); - const button = (await page.$( + using button = (await page.$( 'aria/Submit[role="button"]' )) as ElementHandle; const id = await button!.evaluate(button => { @@ -122,7 +140,7 @@ describe('AriaQueryHandler', () => { ` ); - const div = (await page.$( + using div = (await page.$( 'aria/menu div' )) as ElementHandle; const id = await div!.evaluate(div => { @@ -139,7 +157,7 @@ describe('AriaQueryHandler', () => { ` ); - const menu = (await page.$( + using menu = (await page.$( 'aria/menu-label1' )) as ElementHandle; const id = await menu!.evaluate(div => { @@ -156,7 +174,7 @@ describe('AriaQueryHandler', () => { ` ); - const menu = (await page.$( + using menu = (await page.$( 'aria/menu-label2' )) as ElementHandle; const id = await menu!.evaluate(div => { @@ -230,7 +248,7 @@ describe('AriaQueryHandler', () => { await page.evaluate(() => { return (document.body.innerHTML = `
`); }); - const element = (await page.$('div'))!; + using element = (await page.$('div'))!; await element!.waitForSelector('aria/test'); }); @@ -299,7 +317,7 @@ describe('AriaQueryHandler', () => { const watchdog = frame.waitForSelector('aria/[role="heading"]'); await frame.evaluate(addElement, 'br'); await frame.evaluate(addElement, 'h1'); - const elementHandle = (await watchdog)!; + using elementHandle = (await watchdog)!; const tagName = await ( await elementHandle.getProperty('tagName') ).jsonValue(); @@ -328,7 +346,7 @@ describe('AriaQueryHandler', () => { const watchdog = page.waitForSelector('aria/[role="button"]'); await otherFrame!.evaluate(addElement, 'button'); await page.evaluate(addElement, 'button'); - const elementHandle = await watchdog; + using elementHandle = await watchdog; expect(elementHandle!.frame).toBe(page.mainFrame()); }); @@ -344,7 +362,7 @@ describe('AriaQueryHandler', () => { ); await frame1!.evaluate(addElement, 'button'); await frame2!.evaluate(addElement, 'button'); - const elementHandle = await waitForSelectorPromise; + using elementHandle = await waitForSelectorPromise; expect(elementHandle!.frame).toBe(frame2); }); @@ -517,7 +535,7 @@ describe('AriaQueryHandler', () => { it('should return null if waiting to hide non-existing element', async () => { const {page} = await getTestState(); - const handle = await page.waitForSelector('aria/non-existing', { + using handle = await page.waitForSelector('aria/non-existing', { hidden: true, }); expect(handle).toBe(null); diff --git a/test/src/autofill.spec.ts b/test/src/autofill.spec.ts index 7c80c73b..848b9005 100644 --- a/test/src/autofill.spec.ts +++ b/test/src/autofill.spec.ts @@ -24,7 +24,7 @@ describe('Autofill', function () { it('should fill out a credit card', async () => { const {page, server} = await getTestState(); await page.goto(server.PREFIX + '/credit-card.html'); - const name = await page.waitForSelector('#name'); + using name = await page.waitForSelector('#name'); await name!.autofill({ creditCard: { number: '4444444444444444', diff --git a/test/src/click.spec.ts b/test/src/click.spec.ts index abc68c8f..8379bb13 100644 --- a/test/src/click.spec.ts +++ b/test/src/click.spec.ts @@ -117,7 +117,7 @@ describe('Page.click', function () { await page.setJavaScriptEnabled(false); await page.goto(server.PREFIX + '/wrappedlink.html'); - const body = await page.waitForSelector('body'); + using body = await page.waitForSelector('body'); await body!.evaluate(el => { el.style.paddingTop = '3000px'; }); @@ -332,7 +332,7 @@ describe('Page.click', function () { (globalThis as any).double = true; }); }); - const button = (await page.$('button'))!; + using button = (await page.$('button'))!; await button!.click({count: 2}); expect(await page.evaluate('double')).toBe(true); expect(await page.evaluate('result')).toBe('Clicked'); @@ -428,7 +428,7 @@ describe('Page.click', function () { server.PREFIX + '/input/button.html' ); const frame = page.frames()[1]; - const button = await frame!.$('button'); + using button = await frame!.$('button'); await button!.click(); expect( await frame!.evaluate(() => { @@ -477,7 +477,7 @@ describe('Page.click', function () { server.PREFIX + '/input/button.html' ); const frame = page.frames()[1]; - const button = await frame!.$('button'); + using button = await frame!.$('button'); await button!.click(); expect( await frame!.evaluate(() => { diff --git a/test/src/drag-and-drop.spec.ts b/test/src/drag-and-drop.spec.ts index c64947c9..103cee81 100644 --- a/test/src/drag-and-drop.spec.ts +++ b/test/src/drag-and-drop.spec.ts @@ -25,7 +25,7 @@ describe('Input.drag', function () { const {page, server} = await getTestState(); await page.goto(server.PREFIX + '/input/drag-and-drop.html'); - const draggable = (await page.$('#drag'))!; + using draggable = (await page.$('#drag'))!; try { await draggable!.drag({x: 1, y: 1}); @@ -42,7 +42,7 @@ describe('Input.drag', function () { expect(page.isDragInterceptionEnabled()).toBe(false); await page.setDragInterception(true); expect(page.isDragInterceptionEnabled()).toBe(true); - const draggable = (await page.$('#drag'))!; + using draggable = (await page.$('#drag'))!; const data = await draggable.drag({x: 1, y: 1}); expect(data.items).toHaveLength(1); @@ -59,9 +59,9 @@ describe('Input.drag', function () { expect(page.isDragInterceptionEnabled()).toBe(false); await page.setDragInterception(true); expect(page.isDragInterceptionEnabled()).toBe(true); - const draggable = (await page.$('#drag'))!; + using draggable = (await page.$('#drag'))!; const data = await draggable.drag({x: 1, y: 1}); - const dropzone = (await page.$('#drop'))!; + using dropzone = (await page.$('#drop'))!; await dropzone.dragEnter(data); expect( @@ -82,9 +82,9 @@ describe('Input.drag', function () { expect(page.isDragInterceptionEnabled()).toBe(false); await page.setDragInterception(true); expect(page.isDragInterceptionEnabled()).toBe(true); - const draggable = (await page.$('#drag'))!; + using draggable = (await page.$('#drag'))!; const data = await draggable.drag({x: 1, y: 1}); - const dropzone = (await page.$('#drop'))!; + using dropzone = (await page.$('#drop'))!; await dropzone.dragEnter(data); await dropzone.dragOver(data); @@ -111,8 +111,8 @@ describe('Input.drag', function () { expect(page.isDragInterceptionEnabled()).toBe(false); await page.setDragInterception(true); expect(page.isDragInterceptionEnabled()).toBe(true); - const draggable = (await page.$('#drag'))!; - const dropzone = (await page.$('#drop'))!; + using draggable = (await page.$('#drag'))!; + using dropzone = (await page.$('#drop'))!; const data = await draggable.drag({x: 1, y: 1}); await dropzone.dragEnter(data); await dropzone.dragOver(data); @@ -146,8 +146,8 @@ describe('Input.drag', function () { expect(page.isDragInterceptionEnabled()).toBe(false); await page.setDragInterception(true); expect(page.isDragInterceptionEnabled()).toBe(true); - const draggable = (await page.$('#drag'))!; - const dropzone = (await page.$('#drop'))!; + using draggable = (await page.$('#drag'))!; + using dropzone = (await page.$('#drop'))!; await draggable.dragAndDrop(dropzone); expect( @@ -178,7 +178,7 @@ describe('Input.drag', function () { expect(page.isDragInterceptionEnabled()).toBe(false); await page.setDragInterception(true); expect(page.isDragInterceptionEnabled()).toBe(true); - const draggable = (await page.$('#drag'))!; + using draggable = (await page.$('#drag'))!; await draggable.drag({x: 1, y: 1}); await page.setDragInterception(false); diff --git a/test/src/elementhandle.spec.ts b/test/src/elementhandle.spec.ts index 77dc1448..4e7f3955 100644 --- a/test/src/elementhandle.spec.ts +++ b/test/src/elementhandle.spec.ts @@ -35,7 +35,7 @@ describe('ElementHandle specs', function () { await page.setViewport({width: 500, height: 500}); await page.goto(server.PREFIX + '/grid.html'); - const elementHandle = (await page.$('.box:nth-of-type(13)'))!; + using elementHandle = (await page.$('.box:nth-of-type(13)'))!; const box = await elementHandle.boundingBox(); expect(box).toEqual({x: 100, y: 50, width: 50, height: 50}); }); @@ -45,7 +45,7 @@ describe('ElementHandle specs', function () { await page.setViewport({width: 500, height: 500}); await page.goto(server.PREFIX + '/frames/nested-frames.html'); const nestedFrame = page.frames()[1]!.childFrames()[1]!; - const elementHandle = (await nestedFrame.$('div'))!; + using elementHandle = (await nestedFrame.$('div'))!; const box = await elementHandle.boundingBox(); if (isChrome) { expect(box).toEqual({x: 28, y: 182, width: 264, height: 18}); @@ -57,7 +57,7 @@ describe('ElementHandle specs', function () { const {page} = await getTestState(); await page.setContent('
hi
'); - const element = (await page.$('div'))!; + using element = (await page.$('div'))!; expect(await element.boundingBox()).toBe(null); }); it('should force a layout', async () => { @@ -67,7 +67,7 @@ describe('ElementHandle specs', function () { await page.setContent( '
hello
' ); - const elementHandle = (await page.$('div'))!; + using elementHandle = (await page.$('div'))!; await page.evaluate((element: HTMLElement) => { return (element.style.height = '200px'); }, elementHandle); @@ -82,7 +82,7 @@ describe('ElementHandle specs', function () { `); - const element = (await page.$( + using element = (await page.$( '#therect' )) as ElementHandle; const pptrBoundingBox = await element.boundingBox(); @@ -111,7 +111,7 @@ describe('ElementHandle specs', function () { // Step 2: Add div and position it absolutely inside frame. const frame = page.frames()[1]!; - const divHandle = ( + using divHandle = ( await frame.evaluateHandle(() => { const div = document.createElement('div'); document.body.appendChild(div); @@ -154,7 +154,7 @@ describe('ElementHandle specs', function () { const {page} = await getTestState(); await page.setContent('
hi
'); - const element = (await page.$('div'))!; + using element = (await page.$('div'))!; expect(await element.boxModel()).toBe(null); }); }); @@ -165,7 +165,7 @@ describe('ElementHandle specs', function () { await page.goto(server.EMPTY_PAGE); await attachFrame(page, 'frame1', server.EMPTY_PAGE); - const elementHandle = (await page.$('#frame1'))!; + using elementHandle = (await page.$('#frame1'))!; const frame = await elementHandle.contentFrame(); expect(frame).toBe(page.frames()[1]); }); @@ -175,7 +175,7 @@ describe('ElementHandle specs', function () { it('should work', async () => { const {page} = await getTestState(); await page.setContent('
text
'); - const element = (await page.waitForSelector('div'))!; + using element = (await page.waitForSelector('div'))!; await expect(element.isVisible()).resolves.toBeFalsy(); await expect(element.isHidden()).resolves.toBeTruthy(); await element.evaluate(e => { @@ -191,7 +191,7 @@ describe('ElementHandle specs', function () { const {page, server} = await getTestState(); await page.goto(server.PREFIX + '/input/button.html'); - const button = (await page.$('button'))!; + using button = (await page.$('button'))!; await button.click(); expect( await page.evaluate(() => { @@ -219,7 +219,7 @@ describe('ElementHandle specs', function () { }); }); - const divHandle = (await page.$('div'))!; + using divHandle = (await page.$('div'))!; await divHandle.click(); await divHandle.click({ offset: { @@ -237,7 +237,7 @@ describe('ElementHandle specs', function () { const {page, server} = await getTestState(); await page.goto(server.PREFIX + '/shadow.html'); - const buttonHandle = await page.evaluateHandle(() => { + using buttonHandle = await page.evaluateHandle(() => { // @ts-expect-error button is expected to be in the page's scope. return button as HTMLButtonElement; }); @@ -253,7 +253,7 @@ describe('ElementHandle specs', function () { const {page, server} = await getTestState(); await page.goto(server.PREFIX + '/input/button.html'); - const buttonTextNode = await page.evaluateHandle(() => { + using buttonTextNode = await page.evaluateHandle(() => { return document.querySelector('button')!.firstChild as HTMLElement; }); let error!: Error; @@ -269,7 +269,7 @@ describe('ElementHandle specs', function () { const {page, server} = await getTestState(); await page.goto(server.PREFIX + '/input/button.html'); - const button = (await page.$('button'))!; + using button = (await page.$('button'))!; await page.evaluate((button: HTMLElement) => { return button.remove(); }, button); @@ -286,7 +286,7 @@ describe('ElementHandle specs', function () { const {page, server} = await getTestState(); await page.goto(server.PREFIX + '/input/button.html'); - const button = (await page.$('button'))!; + using button = (await page.$('button'))!; await page.evaluate((button: HTMLElement) => { return (button.style.display = 'none'); }, button); @@ -302,7 +302,7 @@ describe('ElementHandle specs', function () { const {page, server} = await getTestState(); await page.goto(server.PREFIX + '/input/button.html'); - const button = (await page.$('button'))!; + using button = (await page.$('button'))!; await page.evaluate((button: HTMLElement) => { return (button.parentElement!.style.display = 'none'); }, button); @@ -318,7 +318,7 @@ describe('ElementHandle specs', function () { const {page} = await getTestState(); await page.setContent('hello
goodbye'); - const br = (await page.$('br'))!; + using br = (await page.$('br'))!; const error = await br.click().catch(error_ => { return error_; }); @@ -345,7 +345,7 @@ describe('ElementHandle specs', function () { return window.requestAnimationFrame(resolve); }); }); - const divHandle = (await page.$('div'))!; + using divHandle = (await page.$('div'))!; expect(await divHandle.clickablePoint()).toEqual({ x: 45 + 60, // margin + middle point offset y: 45 + 30, // margin + middle point offset @@ -367,25 +367,25 @@ describe('ElementHandle specs', function () { await page.setContent( '' ); - const handle = await page.locator('button').waitHandle(); + using handle = await page.locator('button').waitHandle(); await expect(handle.clickablePoint()).rejects.toBeInstanceOf(Error); await page.setContent( '' ); - const handle2 = await page.locator('button').waitHandle(); + using handle2 = await page.locator('button').waitHandle(); await expect(handle2.clickablePoint()).rejects.toBeInstanceOf(Error); await page.setContent( '' ); - const handle3 = await page.locator('button').waitHandle(); + using handle3 = await page.locator('button').waitHandle(); await expect(handle3.clickablePoint()).rejects.toBeInstanceOf(Error); await page.setContent( '' ); - const handle4 = await page.locator('button').waitHandle(); + using handle4 = await page.locator('button').waitHandle(); await expect(handle4.clickablePoint()).rejects.toBeInstanceOf(Error); }); @@ -399,7 +399,7 @@ describe('ElementHandle specs', function () { return frame.name() === 'frame'; }); - const handle = await frame.locator('button').waitHandle(); + using handle = await frame.locator('button').waitHandle(); await expect(handle.clickablePoint()).rejects.toBeInstanceOf(Error); await page.setContent( @@ -409,7 +409,7 @@ describe('ElementHandle specs', function () { return frame.name() === 'frame2'; }); - const handle2 = await frame2.locator('button').waitHandle(); + using handle2 = await frame2.locator('button').waitHandle(); await expect(handle2.clickablePoint()).rejects.toBeInstanceOf(Error); }); @@ -428,7 +428,7 @@ describe('ElementHandle specs', function () { }); }); const frame = page.frames()[1]!; - const divHandle = (await frame.$('div'))!; + using divHandle = (await frame.$('div'))!; expect(await divHandle.clickablePoint()).toEqual({ x: 20 + 45 + 60, // iframe pos + margin + middle point offset y: 20 + 45 + 30, // iframe pos + margin + middle point offset @@ -455,7 +455,7 @@ describe('ElementHandle specs', function () { await page.setContent( '
bar2
Foo1
' ); - let element = (await waitFor)!; + using element = (await waitFor)!; if (element instanceof Error) { throw element; } @@ -467,13 +467,13 @@ describe('ElementHandle specs', function () { await element.evaluate(el => { el.innerHTML = '
bar1
'; }); - element = (await innerWaitFor)!; - if (element instanceof Error) { - throw element; + using element2 = (await innerWaitFor)!; + if (element2 instanceof Error) { + throw element2; } - expect(element).toBeDefined(); + expect(element2).toBeDefined(); expect( - await element.evaluate(el => { + await element2.evaluate(el => { return (el as HTMLElement).innerText; }) ).toStrictEqual('bar1'); @@ -496,12 +496,12 @@ describe('ElementHandle specs', function () { ` ); - const el1 = (await page.waitForSelector( + using el1 = (await page.waitForSelector( '#el1' )) as ElementHandle; for (const path of ['//div', './/div']) { - const e = (await el1.waitForXPath( + using e = (await el1.waitForXPath( path )) as ElementHandle; expect( @@ -518,7 +518,7 @@ describe('ElementHandle specs', function () { const {page, server} = await getTestState(); await page.goto(server.PREFIX + '/input/scrollable.html'); - const button = (await page.$('#button-6'))!; + using button = (await page.$('#button-6'))!; await button.hover(); expect( await page.evaluate(() => { @@ -533,7 +533,7 @@ describe('ElementHandle specs', function () { const {page, server} = await getTestState(); async function getVisibilityForButton(selector: string) { - const button = (await page.$(selector))!; + using button = (await page.$(selector))!; return await button.isIntersectingViewport(); } @@ -557,7 +557,7 @@ describe('ElementHandle specs', function () { await page.goto(server.PREFIX + '/offscreenbuttons.html'); // a button almost cannot be seen // sometimes we expect to return false by isIntersectingViewport1 - const button = (await page.$('#btn11'))!; + using button = (await page.$('#btn11'))!; expect( await button.isIntersectingViewport({ threshold: 0.001, @@ -570,7 +570,7 @@ describe('ElementHandle specs', function () { await page.goto(server.PREFIX + '/offscreenbuttons.html'); // a button almost cannot be seen // sometimes we expect to return false by isIntersectingViewport1 - const button = (await page.$('#btn0'))!; + using button = (await page.$('#btn0'))!; expect( await button.isIntersectingViewport({ threshold: 1, @@ -615,7 +615,7 @@ describe('ElementHandle specs', function () { const [invisibleCircle, invisibleSvg] = await Promise.all([ page.$('div circle'), - await page.$('div svg'), + page.$('div svg'), ]); // Firefox seems slow when using `isIntersectingViewport` @@ -661,7 +661,7 @@ describe('ElementHandle specs', function () { return document.querySelector(`[id="${selector}"]`); }, }); - const element = (await page.$( + using element = (await page.$( 'getById/foo' )) as ElementHandle; expect( @@ -780,7 +780,7 @@ describe('ElementHandle specs', function () { await page.setContent( '
bar2
Foo1
' ); - let element = (await waitFor)!; + using element = (await waitFor)!; if (element instanceof Error) { throw element; } @@ -796,13 +796,13 @@ describe('ElementHandle specs', function () { el.innerHTML = '
bar1
'; }); - element = (await innerWaitFor)!; - if (element instanceof Error) { - throw element; + using element2 = (await innerWaitFor)!; + if (element2 instanceof Error) { + throw element2; } - expect(element).toBeDefined(); + expect(element2).toBeDefined(); expect( - await element.evaluate(el => { + await element2.evaluate(el => { return el.innerText; }) ).toStrictEqual('bar1'); @@ -847,7 +847,7 @@ describe('ElementHandle specs', function () { }, }); - const element = (await page.$('getByClass/foo'))!; + using element = (await page.$('getByClass/foo'))!; expect(element).toBeDefined(); const elements = await page.$$('getByClass/foo'); @@ -893,7 +893,7 @@ describe('ElementHandle specs', function () { }, }); - const element = (await page.$( + using element = (await page.$( 'getById/foo' )) as ElementHandle; expect( @@ -908,8 +908,8 @@ describe('ElementHandle specs', function () { it('should work', async () => { const {page} = await getTestState(); await page.setContent('
Foo1
'); - const element = await page.$('.foo'); - const div = await element?.toElement('div'); + using element = await page.$('.foo'); + using div = await element?.toElement('div'); expect(div).toBeDefined(); }); }); @@ -917,7 +917,7 @@ describe('ElementHandle specs', function () { describe('ElementHandle[Symbol.dispose]', () => { it('should work', async () => { const {page} = await getTestState(); - const handle = await page.evaluateHandle('document'); + using handle = await page.evaluateHandle('document'); const spy = sinon.spy(handle, Symbol.dispose); { using _ = handle; @@ -931,7 +931,7 @@ describe('ElementHandle specs', function () { describe('ElementHandle[Symbol.asyncDispose]', () => { it('should work', async () => { const {page} = await getTestState(); - const handle = await page.evaluateHandle('document'); + using handle = await page.evaluateHandle('document'); const spy = sinon.spy(handle, Symbol.asyncDispose); { await using _ = handle; @@ -945,7 +945,7 @@ describe('ElementHandle specs', function () { describe('ElementHandle.move', () => { it('should work', async () => { const {page} = await getTestState(); - const handle = await page.evaluateHandle('document'); + using handle = await page.evaluateHandle('document'); const spy = sinon.spy(handle, Symbol.dispose); { using _ = handle; diff --git a/test/src/emulation.spec.ts b/test/src/emulation.spec.ts index 8a0ba6eb..3fd9a156 100644 --- a/test/src/emulation.spec.ts +++ b/test/src/emulation.spec.ts @@ -167,7 +167,7 @@ describe('Emulation', () => { await page.emulate(iPhone); await page.goto(server.PREFIX + '/input/button.html'); - const button = (await page.$('button'))!; + using button = (await page.$('button'))!; await page.evaluate((button: HTMLElement) => { return (button.style.marginTop = '200px'); }, button); diff --git a/test/src/evaluation.spec.ts b/test/src/evaluation.spec.ts index 2cee6da5..c67095c1 100644 --- a/test/src/evaluation.spec.ts +++ b/test/src/evaluation.spec.ts @@ -367,7 +367,7 @@ describe('Evaluation specs', function () { const {page} = await getTestState(); await page.setContent('
42
'); - const element = (await page.$('section'))!; + using element = (await page.$('section'))!; const text = await page.evaluate(e => { return e.textContent; }, element); @@ -377,8 +377,9 @@ describe('Evaluation specs', function () { const {page} = await getTestState(); await page.setContent('
39
'); - const element = (await page.$('section'))!; + using element = (await page.$('section'))!; expect(element).toBeTruthy(); + // We want to dispose early. await element.dispose(); let error!: Error; await page @@ -394,7 +395,7 @@ describe('Evaluation specs', function () { const {page, server} = await getTestState(); await attachFrame(page, 'frame1', server.EMPTY_PAGE); - const bodyHandle = await page.frames()[1]!.$('body'); + using bodyHandle = await page.frames()[1]!.$('body'); let error!: Error; await page .evaluate(body => { diff --git a/test/src/frame.spec.ts b/test/src/frame.spec.ts index 5c7a1db0..faf7abbb 100644 --- a/test/src/frame.spec.ts +++ b/test/src/frame.spec.ts @@ -73,7 +73,7 @@ describe('Frame specs', function () { await page.goto(server.EMPTY_PAGE); const mainFrame = page.mainFrame(); - const windowHandle = await mainFrame.evaluateHandle(() => { + using windowHandle = await mainFrame.evaluateHandle(() => { return window; }); expect(windowHandle).toBeTruthy(); diff --git a/test/src/idle_override.spec.ts b/test/src/idle_override.spec.ts index 0bd27df5..bd941307 100644 --- a/test/src/idle_override.spec.ts +++ b/test/src/idle_override.spec.ts @@ -24,7 +24,7 @@ describe('Emulate idle state', () => { setupTestBrowserHooks(); async function getIdleState(page: Page) { - const stateElement = (await page.$('#state')) as ElementHandle; + using stateElement = (await page.$('#state')) as ElementHandle; return await page.evaluate(element => { return element.innerText; }, stateElement); diff --git a/test/src/input.spec.ts b/test/src/input.spec.ts index 875d3c30..07c2f011 100644 --- a/test/src/input.spec.ts +++ b/test/src/input.spec.ts @@ -33,7 +33,7 @@ describe('input tests', function () { await page.goto(server.PREFIX + '/input/fileupload.html'); const filePath = path.relative(process.cwd(), FILE_TO_UPLOAD); - const input = (await page.$('input'))!; + using input = (await page.$('input'))!; await page.evaluate((e: HTMLElement) => { (globalThis as any)._inputEvents = []; e.addEventListener('change', ev => { diff --git a/test/src/jshandle.spec.ts b/test/src/jshandle.spec.ts index 6d0ce33f..b8ca4fd2 100644 --- a/test/src/jshandle.spec.ts +++ b/test/src/jshandle.spec.ts @@ -27,7 +27,7 @@ describe('JSHandle', function () { it('should work', async () => { const {page} = await getTestState(); - const windowHandle = await page.evaluateHandle(() => { + using windowHandle = await page.evaluateHandle(() => { return window; }); expect(windowHandle).toBeTruthy(); @@ -35,7 +35,7 @@ describe('JSHandle', function () { it('should return the RemoteObject', async () => { const {page} = await getTestState(); - const windowHandle = await page.evaluateHandle(() => { + using windowHandle = await page.evaluateHandle(() => { return window; }); expect(windowHandle.remoteObject()).toBeTruthy(); @@ -43,7 +43,7 @@ describe('JSHandle', function () { it('should accept object handle as an argument', async () => { const {page} = await getTestState(); - const navigatorHandle = await page.evaluateHandle(() => { + using navigatorHandle = await page.evaluateHandle(() => { return navigator; }); const text = await page.evaluate(e => { @@ -54,7 +54,7 @@ describe('JSHandle', function () { it('should accept object handle to primitive types', async () => { const {page} = await getTestState(); - const aHandle = await page.evaluateHandle(() => { + using aHandle = await page.evaluateHandle(() => { return 5; }); const isFive = await page.evaluate(e => { @@ -80,7 +80,7 @@ describe('JSHandle', function () { it('should accept object handle to unserializable value', async () => { const {page} = await getTestState(); - const aHandle = await page.evaluateHandle(() => { + using aHandle = await page.evaluateHandle(() => { return Infinity; }); expect( @@ -92,7 +92,7 @@ describe('JSHandle', function () { it('should use the same JS wrappers', async () => { const {page} = await getTestState(); - const aHandle = await page.evaluateHandle(() => { + using aHandle = await page.evaluateHandle(() => { (globalThis as any).FOO = 123; return window; }); @@ -108,14 +108,14 @@ describe('JSHandle', function () { it('should work', async () => { const {page} = await getTestState(); - const aHandle = await page.evaluateHandle(() => { + using aHandle = await page.evaluateHandle(() => { return { one: 1, two: 2, three: 3, }; }); - const twoHandle = await aHandle.getProperty('two'); + using twoHandle = await aHandle.getProperty('two'); expect(await twoHandle.jsonValue()).toEqual(2); }); }); @@ -124,7 +124,7 @@ describe('JSHandle', function () { it('should work', async () => { const {page} = await getTestState(); - const aHandle = await page.evaluateHandle(() => { + using aHandle = await page.evaluateHandle(() => { return {foo: 'bar'}; }); const json = await aHandle.jsonValue(); @@ -134,7 +134,7 @@ describe('JSHandle', function () { it('works with jsonValues that are not objects', async () => { const {page} = await getTestState(); - const aHandle = await page.evaluateHandle(() => { + using aHandle = await page.evaluateHandle(() => { return ['a', 'b']; }); const json = await aHandle.jsonValue(); @@ -144,12 +144,12 @@ describe('JSHandle', function () { it('works with jsonValues that are primitives', async () => { const {page} = await getTestState(); - const aHandle = await page.evaluateHandle(() => { + using aHandle = await page.evaluateHandle(() => { return 'foo'; }); expect(await aHandle.jsonValue()).toEqual('foo'); - const bHandle = await page.evaluateHandle(() => { + using bHandle = await page.evaluateHandle(() => { return undefined; }); expect(await bHandle.jsonValue()).toEqual(undefined); @@ -158,7 +158,7 @@ describe('JSHandle', function () { it('should work with dates', async () => { const {page} = await getTestState(); - const dateHandle = await page.evaluateHandle(() => { + using dateHandle = await page.evaluateHandle(() => { return new Date('2017-09-26T00:00:00.000Z'); }); const date = await dateHandle.jsonValue(); @@ -168,7 +168,7 @@ describe('JSHandle', function () { it('should not throw for circular objects', async () => { const {page} = await getTestState(); - const handle = await page.evaluateHandle(() => { + using handle = await page.evaluateHandle(() => { const t: {t?: unknown; g: number} = {g: 1}; t.t = t; return t; @@ -181,20 +181,20 @@ describe('JSHandle', function () { it('should work', async () => { const {page} = await getTestState(); - const aHandle = await page.evaluateHandle(() => { + using aHandle = await page.evaluateHandle(() => { return { foo: 'bar', }; }); const properties = await aHandle.getProperties(); - const foo = properties.get('foo')!; + 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(); - const aHandle = await page.evaluateHandle(() => { + using aHandle = await page.evaluateHandle(() => { class A { a: string; constructor() { @@ -220,29 +220,29 @@ describe('JSHandle', function () { it('should work', async () => { const {page} = await getTestState(); - const aHandle = await page.evaluateHandle(() => { + using aHandle = await page.evaluateHandle(() => { return document.body; }); - const element = aHandle.asElement(); + using element = aHandle.asElement(); expect(element).toBeTruthy(); }); it('should return null for non-elements', async () => { const {page} = await getTestState(); - const aHandle = await page.evaluateHandle(() => { + using aHandle = await page.evaluateHandle(() => { return 2; }); - const element = aHandle.asElement(); + using element = aHandle.asElement(); expect(element).toBeFalsy(); }); it('should return ElementHandle for TextNodes', async () => { const {page} = await getTestState(); await page.setContent('
ee!
'); - const aHandle = await page.evaluateHandle(() => { + using aHandle = await page.evaluateHandle(() => { return document.querySelector('div')!.firstChild; }); - const element = aHandle.asElement(); + using element = aHandle.asElement(); expect(element).toBeTruthy(); expect( await page.evaluate(e => { @@ -256,11 +256,11 @@ describe('JSHandle', function () { it('should work for primitives', async () => { const {page} = await getTestState(); - const numberHandle = await page.evaluateHandle(() => { + using numberHandle = await page.evaluateHandle(() => { return 2; }); expect(numberHandle.toString()).toBe('JSHandle:2'); - const stringHandle = await page.evaluateHandle(() => { + using stringHandle = await page.evaluateHandle(() => { return 'a'; }); expect(stringHandle.toString()).toBe('JSHandle:a'); @@ -268,7 +268,7 @@ describe('JSHandle', function () { it('should work for complicated objects', async () => { const {page} = await getTestState(); - const aHandle = await page.evaluateHandle(() => { + using aHandle = await page.evaluateHandle(() => { return window; }); expect(aHandle.toString()).atLeastOneToContain([ @@ -337,7 +337,7 @@ describe('JSHandle', function () { describe('JSHandle[Symbol.dispose]', () => { it('should work', async () => { const {page} = await getTestState(); - const handle = await page.evaluateHandle('new Set()'); + using handle = await page.evaluateHandle('new Set()'); const spy = sinon.spy(handle, Symbol.dispose); { using _ = handle; @@ -351,7 +351,7 @@ describe('JSHandle', function () { describe('JSHandle[Symbol.asyncDispose]', () => { it('should work', async () => { const {page} = await getTestState(); - const handle = await page.evaluateHandle('new Set()'); + using handle = await page.evaluateHandle('new Set()'); const spy = sinon.spy(handle, Symbol.asyncDispose); { await using _ = handle; @@ -365,7 +365,7 @@ describe('JSHandle', function () { describe('JSHandle.move', () => { it('should work', async () => { const {page} = await getTestState(); - const handle = await page.evaluateHandle('new Set()'); + using handle = await page.evaluateHandle('new Set()'); const spy = sinon.spy(handle, Symbol.dispose); { using _ = handle; diff --git a/test/src/keyboard.spec.ts b/test/src/keyboard.spec.ts index be6e8136..ae0aaa63 100644 --- a/test/src/keyboard.spec.ts +++ b/test/src/keyboard.spec.ts @@ -119,7 +119,7 @@ describe('Keyboard', function () { const {page, server} = await getTestState(); await page.goto(server.PREFIX + '/input/textarea.html'); - const textarea = (await page.$('textarea'))!; + using textarea = (await page.$('textarea'))!; await textarea.press('a'); expect( await page.evaluate(() => { @@ -148,7 +148,7 @@ describe('Keyboard', function () { const {page, server} = await getTestState(); await page.goto(server.PREFIX + '/input/textarea.html'); - const textarea = (await page.$('textarea'))!; + using textarea = (await page.$('textarea'))!; await textarea.press('a', {text: 'ё'}); expect( await page.evaluate(() => { @@ -444,7 +444,7 @@ describe('Keyboard', function () { true ); }); - const textarea = (await page.$('textarea'))!; + using textarea = (await page.$('textarea'))!; await textarea.press('Digit5'); expect(await page.evaluate('keyLocation')).toBe(0); @@ -490,7 +490,7 @@ describe('Keyboard', function () { server.PREFIX + '/input/textarea.html' ); const frame = page.frames()[1]!; - const textarea = (await frame.$('textarea'))!; + using textarea = (await frame.$('textarea'))!; await textarea.type('👹 Tokyo street Japan 🇯🇵'); expect( await frame.$eval('textarea', textarea => { diff --git a/test/src/launcher.spec.ts b/test/src/launcher.spec.ts index 613defb7..2a5fe9eb 100644 --- a/test/src/launcher.spec.ts +++ b/test/src/launcher.spec.ts @@ -863,10 +863,9 @@ describe('Launcher specs', function () { return page.url() === server.EMPTY_PAGE; })!; await pageTwo.reload(); - const bodyHandle = await pageTwo.waitForSelector('body', { + using _ = await pageTwo.waitForSelector('body', { timeout: 10000, }); - await bodyHandle!.dispose(); await browserTwo.close(); } finally { await close(); diff --git a/test/src/locator.spec.ts b/test/src/locator.spec.ts index 48ea8471..26b2e352 100644 --- a/test/src/locator.spec.ts +++ b/test/src/locator.spec.ts @@ -42,7 +42,7 @@ describe('Locator', function () { willClick = true; }) .click(); - const button = await page.$('button'); + using button = await page.$('button'); const text = await button?.evaluate(el => { return el.innerText; }); @@ -69,7 +69,7 @@ describe('Locator', function () { willClick = true; }) .click(); - const button = await page.$('button'); + using button = await page.$('button'); const text = await button?.evaluate(el => { return el.innerText; }); @@ -92,7 +92,7 @@ describe('Locator', function () { willClick = true; }) .click(); - const button = await page.$('button'); + using button = await page.$('button'); const text = await button?.evaluate(el => { return el.innerText; }); @@ -114,7 +114,7 @@ describe('Locator', function () { clicked = true; }) .click(); - const button = await page.$('button'); + using button = await page.$('button'); const text = await button?.evaluate(el => { return el.innerText; }); @@ -130,7 +130,7 @@ describe('Locator', function () { `); await page.locator('button').click(); - const button = await page.$('button'); + using button = await page.$('button'); const text = await button?.evaluate(el => { return el.innerText; }); @@ -144,7 +144,7 @@ describe('Locator', function () { await page.setContent(` `); - const button = await page.$('button'); + using button = await page.$('button'); const result = page .locator('button') .click() @@ -177,7 +177,7 @@ describe('Locator', function () { await page.setContent(` `); - const button = await page.$('button'); + using button = await page.$('button'); const result = page.locator('button').click(); expect( await button?.evaluate(el => { @@ -202,7 +202,7 @@ describe('Locator', function () { await page.setContent(` `); - const button = await page.$('button'); + using button = await page.$('button'); const result = page.locator('button').click(); expect( await button?.evaluate(el => { @@ -308,7 +308,7 @@ describe('Locator', function () { willClick = true; }) .click(); - const button = await frame.$('button'); + using button = await frame.$('button'); const text = await button?.evaluate(el => { return el.innerText; }); @@ -332,7 +332,7 @@ describe('Locator', function () { hovered = true; }) .hover(); - const button = await page.$('button'); + using button = await page.$('button'); const text = await button?.evaluate(el => { return el.innerText; }); @@ -361,7 +361,7 @@ describe('Locator', function () { scrollTop: 500, scrollLeft: 500, }); - const scrollable = await page.$('div'); + using scrollable = await page.$('div'); const scroll = await scrollable?.evaluate(el => { return el.scrollTop + ' ' + el.scrollLeft; }); @@ -435,7 +435,7 @@ describe('Locator', function () { await page.setContent(` `); - const input = await page.$('input'); + using input = await page.$('input'); const result = page.locator('input').fill('test'); expect( await input?.evaluate(el => { diff --git a/test/src/mouse.spec.ts b/test/src/mouse.spec.ts index a1cc6bac..7a598fbd 100644 --- a/test/src/mouse.spec.ts +++ b/test/src/mouse.spec.ts @@ -212,7 +212,7 @@ describe('Mouse', function () { const {page, server} = await getTestState(); await page.goto(server.PREFIX + '/input/wheel.html'); - const elem = (await page.$('div'))!; + using elem = (await page.$('div'))!; const boundingBoxBefore = (await elem.boundingBox())!; expect(boundingBoxBefore).toMatchObject({ width: 115, diff --git a/test/src/oopif.spec.ts b/test/src/oopif.spec.ts index e430b5fe..2611780c 100644 --- a/test/src/oopif.spec.ts +++ b/test/src/oopif.spec.ts @@ -378,7 +378,7 @@ describeWithDebugLogs('OOPIF', function () { button.innerText = 'click'; document.body.appendChild(button); }); - const button = (await frame.waitForSelector('#test-button', { + using button = (await frame.waitForSelector('#test-button', { visible: true, }))!; const result = await button.clickablePoint(); diff --git a/test/src/page.spec.ts b/test/src/page.spec.ts index 6bb16c74..145efada 100644 --- a/test/src/page.spec.ts +++ b/test/src/page.spec.ts @@ -530,7 +530,7 @@ describe('Page', function () { const {page} = await getTestState(); // Create a custom class - const classHandle = await page.evaluateHandle(() => { + using classHandle = await page.evaluateHandle(() => { return class CustomClass {}; }); @@ -541,10 +541,10 @@ describe('Page', function () { }, classHandle); // Validate only one has been added. - const prototypeHandle = await page.evaluateHandle(CustomClass => { + using prototypeHandle = await page.evaluateHandle(CustomClass => { return CustomClass.prototype; }, classHandle); - const objectsHandle = await page.queryObjects(prototypeHandle); + using objectsHandle = await page.queryObjects(prototypeHandle); await expect( page.evaluate(objects => { return objects.length; @@ -564,7 +564,7 @@ describe('Page', function () { await page.goto(server.EMPTY_PAGE); // Create a custom class - const classHandle = await page.evaluateHandle(() => { + using classHandle = await page.evaluateHandle(() => { return class CustomClass {}; }); @@ -575,10 +575,10 @@ describe('Page', function () { }, classHandle); // Validate only one has been added. - const prototypeHandle = await page.evaluateHandle(CustomClass => { + using prototypeHandle = await page.evaluateHandle(CustomClass => { return CustomClass.prototype; }, classHandle); - const objectsHandle = await page.queryObjects(prototypeHandle); + using objectsHandle = await page.queryObjects(prototypeHandle); await expect( page.evaluate(objects => { return objects.length; @@ -596,9 +596,10 @@ describe('Page', function () { it('should fail for disposed handles', async () => { const {page} = await getTestState(); - const prototypeHandle = await page.evaluateHandle(() => { + using prototypeHandle = await page.evaluateHandle(() => { return HTMLBodyElement.prototype; }); + // We want to dispose early. await prototypeHandle.dispose(); let error!: Error; await page.queryObjects(prototypeHandle).catch(error_ => { @@ -609,7 +610,7 @@ describe('Page', function () { it('should fail primitive values as prototypes', async () => { const {page} = await getTestState(); - const prototypeHandle = await page.evaluateHandle(() => { + using prototypeHandle = await page.evaluateHandle(() => { return 42; }); let error!: Error; @@ -1160,7 +1161,7 @@ describe('Page', function () { await page.goto(server.PREFIX + '/abort-request.html'); - const element = await page.$(`#abort`); + using element = await page.$(`#abort`); await element!.click(); let error = false; @@ -1733,7 +1734,7 @@ describe('Page', function () { const {page, server} = await getTestState(); await page.goto(server.EMPTY_PAGE); - const scriptHandle = await page.addScriptTag({url: '/injectedfile.js'}); + using scriptHandle = await page.addScriptTag({url: '/injectedfile.js'}); expect(scriptHandle.asElement()).not.toBeNull(); expect( await page.evaluate(() => { @@ -1811,7 +1812,7 @@ describe('Page', function () { const {page, server} = await getTestState(); await page.goto(server.EMPTY_PAGE); - const scriptHandle = await page.addScriptTag({ + using scriptHandle = await page.addScriptTag({ path: path.join(__dirname, '../assets/injectedfile.js'), }); expect(scriptHandle.asElement()).not.toBeNull(); @@ -1839,7 +1840,7 @@ describe('Page', function () { const {page, server} = await getTestState(); await page.goto(server.EMPTY_PAGE); - const scriptHandle = await page.addScriptTag({ + using scriptHandle = await page.addScriptTag({ content: 'window.__injected = 35;', }); expect(scriptHandle.asElement()).not.toBeNull(); @@ -1907,7 +1908,7 @@ describe('Page', function () { const {page, server} = await getTestState(); await page.goto(server.EMPTY_PAGE); - const styleHandle = await page.addStyleTag({url: '/injectedstyle.css'}); + using styleHandle = await page.addStyleTag({url: '/injectedstyle.css'}); expect(styleHandle.asElement()).not.toBeNull(); expect( await page.evaluate( @@ -1937,7 +1938,7 @@ describe('Page', function () { const {page, server} = await getTestState(); await page.goto(server.EMPTY_PAGE); - const styleHandle = await page.addStyleTag({ + using styleHandle = await page.addStyleTag({ path: path.join(__dirname, '../assets/injectedstyle.css'), }); expect(styleHandle.asElement()).not.toBeNull(); @@ -1955,7 +1956,7 @@ describe('Page', function () { await page.addStyleTag({ path: path.join(__dirname, '../assets/injectedstyle.css'), }); - const styleHandle = (await page.$('style'))!; + using styleHandle = (await page.$('style'))!; const styleContent = await page.evaluate((style: HTMLStyleElement) => { return style.innerHTML; }, styleHandle); @@ -1966,7 +1967,7 @@ describe('Page', function () { const {page, server} = await getTestState(); await page.goto(server.EMPTY_PAGE); - const styleHandle = await page.addStyleTag({ + using styleHandle = await page.addStyleTag({ content: 'body { background-color: green; }', }); expect(styleHandle.asElement()).not.toBeNull(); diff --git a/test/src/prerender.spec.ts b/test/src/prerender.spec.ts index 83c11916..307b146c 100644 --- a/test/src/prerender.spec.ts +++ b/test/src/prerender.spec.ts @@ -25,10 +25,10 @@ describe('Prerender', function () { const {page, server} = await getTestState(); await page.goto(server.PREFIX + '/prerender/index.html'); - const button = await page.waitForSelector('button'); + using button = await page.waitForSelector('button'); await button?.click(); - const link = await page.waitForSelector('a'); + using link = await page.waitForSelector('a'); await Promise.all([page.waitForNavigation(), link?.click()]); expect( await page.evaluate(() => { @@ -41,7 +41,7 @@ describe('Prerender', function () { const {page, server} = await getTestState(); await page.goto(server.PREFIX + '/prerender/index.html'); - const button = await page.waitForSelector('button'); + using button = await page.waitForSelector('button'); await button?.click(); await page.goto(server.PREFIX + '/prerender/target.html'); @@ -57,11 +57,11 @@ describe('Prerender', function () { const {page, server} = await getTestState(); await page.goto(server.PREFIX + '/prerender/index.html'); - const button = await page.waitForSelector('button'); + using button = await page.waitForSelector('button'); await button?.click(); const mainFrame = page.mainFrame(); - const link = await mainFrame.waitForSelector('a'); + using link = await mainFrame.waitForSelector('a'); await Promise.all([mainFrame.waitForNavigation(), link?.click()]); expect(mainFrame).toBe(page.mainFrame()); expect( @@ -76,7 +76,7 @@ describe('Prerender', function () { const {page, server} = await getTestState(); await page.goto(server.PREFIX + '/prerender/index.html'); - const button = await page.waitForSelector('button'); + using button = await page.waitForSelector('button'); await button?.click(); const mainFrame = page.mainFrame(); @@ -100,10 +100,10 @@ describe('Prerender', function () { }); await page.goto(server.PREFIX + '/prerender/index.html'); - const button = await page.waitForSelector('button'); + using button = await page.waitForSelector('button'); await button?.click(); const mainFrame = page.mainFrame(); - const link = await mainFrame.waitForSelector('a'); + using link = await mainFrame.waitForSelector('a'); await Promise.all([mainFrame.waitForNavigation(), link?.click()]); expect(mainFrame).toBe(page.mainFrame()); expect( @@ -138,9 +138,9 @@ describe('Prerender', function () { height: 400, }); await page.goto(server.PREFIX + '/prerender/index.html'); - const button = await page.waitForSelector('button'); + using button = await page.waitForSelector('button'); await button?.click(); - const link = await page.waitForSelector('a'); + using link = await page.waitForSelector('a'); await Promise.all([page.waitForNavigation(), link?.click()]); const result = await page.evaluate(() => { return { diff --git a/test/src/queryhandler.spec.ts b/test/src/queryhandler.spec.ts index 649d26e8..b78004af 100644 --- a/test/src/queryhandler.spec.ts +++ b/test/src/queryhandler.spec.ts @@ -46,7 +46,7 @@ describe('Query handler tests', function () { } it('should find first element in shadow', async () => { const {page} = await setUpPage(); - const div = (await page.$('pierce/.foo')) as ElementHandle; + using div = (await page.$('pierce/.foo')) as ElementHandle; const text = await div.evaluate(element => { return element.textContent; }); @@ -68,8 +68,8 @@ describe('Query handler tests', function () { }); it('should find first child element', async () => { const {page} = await setUpPage(); - const parentElement = (await page.$('html > div'))!; - const childElement = (await parentElement.$( + using parentElement = (await page.$('html > div'))!; + using childElement = (await parentElement.$( 'pierce/div' )) as ElementHandle; const text = await childElement.evaluate(element => { @@ -79,7 +79,7 @@ describe('Query handler tests', function () { }); it('should find all child elements', async () => { const {page} = await setUpPage(); - const parentElement = (await page.$('html > div'))!; + using parentElement = (await page.$('html > div'))!; const childElements = (await parentElement.$$('pierce/div')) as Array< ElementHandle >; @@ -115,7 +115,7 @@ describe('Query handler tests', function () { await page.setContent('
a
a
'); - const element = await page.$('text/a'); + using element = await page.$('text/a'); expect( await element?.evaluate(e => { return e.id; @@ -145,7 +145,7 @@ describe('Query handler tests', function () { document.body.append(div); }); - const element = await page.$('text/a'); + using element = await page.$('text/a'); expect( await element?.evaluate(e => { return e.textContent; @@ -157,7 +157,7 @@ describe('Query handler tests', function () { await page.setContent('
a
b
'); - const element = await page.$('text/a'); + using element = await page.$('text/a'); expect( await element?.evaluate(e => { return e.textContent; @@ -169,7 +169,7 @@ describe('Query handler tests', function () { await page.setContent(''); - const element = (await page.$( + using element = (await page.$( 'text/a' )) as ElementHandle; expect( @@ -190,7 +190,7 @@ describe('Query handler tests', function () { await page.setContent('
a b
'); - const element = await page.$('text/a b'); + using element = await page.$('text/a b'); expect( await element?.evaluate(e => { return e.textContent; @@ -203,8 +203,8 @@ describe('Query handler tests', function () { await page.setContent( '
text
text
' ); - const div = (await page.$('#target1')) as ElementHandle; - const input = (await page.$( + using div = (await page.$('#target1')) as ElementHandle; + using input = (await page.$( '#target2' )) as ElementHandle; @@ -271,7 +271,7 @@ describe('Query handler tests', function () { await page.setContent('
a
'); - const elementHandle = (await page.$('div'))!; + using elementHandle = (await page.$('div'))!; expect(await elementHandle.$(`text/a`)).toBeTruthy(); expect(await elementHandle.$$(`text/a`)).toHaveLength(1); }); @@ -281,7 +281,7 @@ describe('Query handler tests', function () { await page.setContent('
'); - const elementHandle = (await page.$('div'))!; + using elementHandle = (await page.$('div'))!; expect(await elementHandle.$(`text/a`)).toBeFalsy(); expect(await elementHandle.$$(`text/a`)).toHaveLength(0); }); @@ -313,7 +313,7 @@ describe('Query handler tests', function () { await page.setContent('
a
'); - const element = await page.$('xpath/html/body/div'); + using element = await page.$('xpath/html/body/div'); expect( await element?.evaluate(e => { return e.textContent === 'a'; @@ -335,7 +335,7 @@ describe('Query handler tests', function () { await page.setContent('
a
'); - const elementHandle = (await page.$('div'))!; + using elementHandle = (await page.$('div'))!; expect(await elementHandle.$(`xpath/span`)).toBeTruthy(); expect(await elementHandle.$$(`xpath/span`)).toHaveLength(1); }); @@ -345,7 +345,7 @@ describe('Query handler tests', function () { await page.setContent('
a
'); - const elementHandle = (await page.$('div'))!; + using elementHandle = (await page.$('div'))!; expect(await elementHandle.$(`xpath/span`)).toBeFalsy(); expect(await elementHandle.$$(`xpath/span`)).toHaveLength(0); }); @@ -360,7 +360,7 @@ describe('Query handler tests', function () { it('should work with CSS selectors', async () => { const {server, page} = await getTestState(); await page.goto(`${server.PREFIX}/p-selectors.html`); - const element = await page.$('div > button'); + using element = await page.$('div > button'); assert(element, 'Could not find element'); expect( await element.evaluate(element => { @@ -383,7 +383,7 @@ describe('Query handler tests', function () { const {server, page} = await getTestState(); await page.goto(`${server.PREFIX}/p-selectors.html`); { - const element = await page.$('div >>>> div'); + using element = await page.$('div >>>> div'); assert(element, 'Could not find element'); expect( await element.evaluate(element => { @@ -423,7 +423,7 @@ describe('Query handler tests', function () { it('should work with text selectors', async () => { const {server, page} = await getTestState(); await page.goto(`${server.PREFIX}/p-selectors.html`); - const element = await page.$('div ::-p-text(world)'); + using element = await page.$('div ::-p-text(world)'); assert(element, 'Could not find element'); expect( await element.evaluate(element => { @@ -435,7 +435,7 @@ describe('Query handler tests', function () { it('should work ARIA selectors', async () => { const {server, page} = await getTestState(); await page.goto(`${server.PREFIX}/p-selectors.html`); - const element = await page.$('div ::-p-aria(world)'); + using element = await page.$('div ::-p-aria(world)'); assert(element, 'Could not find element'); expect( await element.evaluate(element => { @@ -447,7 +447,7 @@ describe('Query handler tests', function () { it('should work for ARIA selectors in multiple isolated worlds', async () => { const {server, page} = await getTestState(); await page.goto(`${server.PREFIX}/p-selectors.html`); - let element = await page.waitForSelector('::-p-aria(world)'); + using element = await page.waitForSelector('::-p-aria(world)'); assert(element, 'Could not find element'); expect( await element.evaluate(element => { @@ -456,10 +456,10 @@ describe('Query handler tests', function () { ).toBeTruthy(); // $ would add ARIA query handler to the main world. await element.$('::-p-aria(world)'); - element = await page.waitForSelector('::-p-aria(world)'); - assert(element, 'Could not find element'); + using element2 = await page.waitForSelector('::-p-aria(world)'); + assert(element2, 'Could not find element'); expect( - await element.evaluate(element => { + await element2.evaluate(element => { return element.id === 'b'; }) ).toBeTruthy(); @@ -468,7 +468,7 @@ describe('Query handler tests', function () { it('should work ARIA selectors with role', async () => { const {server, page} = await getTestState(); await page.goto(`${server.PREFIX}/p-selectors.html`); - const element = await page.$('::-p-aria(world[role="button"])'); + using element = await page.$('::-p-aria(world[role="button"])'); assert(element, 'Could not find element'); expect( await element.evaluate(element => { @@ -480,7 +480,7 @@ describe('Query handler tests', function () { it('should work ARIA selectors with name and role', async () => { const {server, page} = await getTestState(); await page.goto(`${server.PREFIX}/p-selectors.html`); - const element = await page.$('::-p-aria([name="world"][role="button"])'); + using element = await page.$('::-p-aria([name="world"][role="button"])'); assert(element, 'Could not find element'); expect( await element.evaluate(element => { @@ -492,7 +492,7 @@ describe('Query handler tests', function () { it('should work XPath selectors', async () => { const {server, page} = await getTestState(); await page.goto(`${server.PREFIX}/p-selectors.html`); - const element = await page.$('div ::-p-xpath(//button)'); + using element = await page.$('div ::-p-xpath(//button)'); assert(element, 'Could not find element'); expect( await element.evaluate(element => { @@ -510,7 +510,7 @@ describe('Query handler tests', function () { const {server, page} = await getTestState(); await page.goto(`${server.PREFIX}/p-selectors.html`); - const element = await page.$('::-p-div'); + using element = await page.$('::-p-div'); assert(element, 'Could not find element'); expect( await element.evaluate(element => { @@ -533,7 +533,7 @@ describe('Query handler tests', function () { }); { - const element = await page.$('::-p-div(true)'); + using element = await page.$('::-p-div(true)'); assert(element, 'Could not find element'); expect( await element.evaluate(element => { @@ -542,7 +542,7 @@ describe('Query handler tests', function () { ).toBeTruthy(); } { - const element = await page.$('::-p-div("true")'); + using element = await page.$('::-p-div("true")'); assert(element, 'Could not find element'); expect( await element.evaluate(element => { @@ -551,7 +551,7 @@ describe('Query handler tests', function () { ).toBeTruthy(); } { - const element = await page.$("::-p-div('true')"); + using element = await page.$("::-p-div('true')"); assert(element, 'Could not find element'); expect( await element.evaluate(element => { @@ -560,7 +560,7 @@ describe('Query handler tests', function () { ).toBeTruthy(); } { - const element = await page.$('::-p-div'); + using element = await page.$('::-p-div'); assert(element, 'Could not find element'); expect( await element.evaluate(element => { @@ -573,14 +573,13 @@ describe('Query handler tests', function () { it('should work with :hover', async () => { const {server, page} = await getTestState(); await page.goto(`${server.PREFIX}/p-selectors.html`); - let button = await page.$('div ::-p-text(world)'); + using button = await page.$('div ::-p-text(world)'); assert(button, 'Could not find element'); await button.hover(); - await button.dispose(); - button = await page.$('div ::-p-text(world):hover'); - assert(button, 'Could not find element'); - const value = await button.evaluate(span => { + using button2 = await page.$('div ::-p-text(world):hover'); + assert(button2, 'Could not find element'); + const value = await button2.evaluate(span => { return {textContent: span.textContent, tagName: span.tagName}; }); expect(value).toMatchObject({textContent: 'world', tagName: 'BUTTON'}); @@ -643,22 +642,22 @@ describe('Query handler tests', function () { it('should handle escapes', async () => { const {server, page} = await getTestState(); await page.goto(`${server.PREFIX}/p-selectors.html`); - let element = await page.$( + using element = await page.$( ':scope >>> ::-p-text(My name is Jun \\(pronounced like "June"\\))' ); expect(element).toBeTruthy(); - element = await page.$( + using element2 = await page.$( ':scope >>> ::-p-text("My name is Jun (pronounced like \\"June\\")")' ); - expect(element).toBeTruthy(); - element = await page.$( + expect(element2).toBeTruthy(); + using element3 = await page.$( ':scope >>> ::-p-text(My name is Jun \\(pronounced like "June"\\)")' ); - expect(element).toBeFalsy(); - element = await page.$( + expect(element3).toBeFalsy(); + using element4 = await page.$( ':scope >>> ::-p-text("My name is Jun \\(pronounced like "June"\\))' ); - expect(element).toBeFalsy(); + expect(element4).toBeFalsy(); }); }); }); diff --git a/test/src/queryselector.spec.ts b/test/src/queryselector.spec.ts index 98496921..9f7b4dca 100644 --- a/test/src/queryselector.spec.ts +++ b/test/src/queryselector.spec.ts @@ -49,7 +49,7 @@ describe('querySelector', function () { const {page} = await getTestState(); await page.setContent('
hello
world
'); - const divHandle = (await page.$('div'))!; + using divHandle = (await page.$('div'))!; const text = await page.$eval( 'section', (e, div) => { @@ -111,7 +111,7 @@ describe('querySelector', function () { await page.setContent( '
2
2
1
3
' ); - const divHandle = (await page.$('div'))!; + using divHandle = (await page.$('div'))!; const sum = await page.$$eval( 'section', (sections, div) => { @@ -152,13 +152,13 @@ describe('querySelector', function () { const {page} = await getTestState(); await page.setContent('
test
'); - const element = (await page.$('section'))!; + using element = (await page.$('section'))!; expect(element).toBeTruthy(); }); it('should return null for non-existing element', async () => { const {page} = await getTestState(); - const element = (await page.$('non-existing-element'))!; + using element = (await page.$('non-existing-element'))!; expect(element).toBe(null); }); }); @@ -218,9 +218,9 @@ describe('querySelector', function () { await page.setContent( '
A
' ); - const html = (await page.$('html'))!; - const second = (await html.$('.second'))!; - const inner = await second.$('.inner'); + using html = (await page.$('html'))!; + using second = (await html.$('.second'))!; + using inner = await second.$('.inner'); const content = await page.evaluate(e => { return e?.textContent; }, inner); @@ -233,8 +233,8 @@ describe('querySelector', function () { await page.setContent( '
B
' ); - const html = (await page.$('html'))!; - const second = await html.$('.third'); + using html = (await page.$('html'))!; + using second = await html.$('.third'); expect(second).toBe(null); }); }); @@ -245,7 +245,7 @@ describe('querySelector', function () { await page.setContent( '
10
' ); - const tweet = (await page.$('.tweet'))!; + using tweet = (await page.$('.tweet'))!; const content = await tweet.$eval('.like', node => { return (node as HTMLElement).innerText; }); @@ -258,7 +258,7 @@ describe('querySelector', function () { const htmlContent = '
not-a-child-div
a-child-div
'; await page.setContent(htmlContent); - const elementHandle = (await page.$('#myId'))!; + using elementHandle = (await page.$('#myId'))!; const content = await elementHandle.$eval('.a', node => { return (node as HTMLElement).innerText; }); @@ -271,7 +271,7 @@ describe('querySelector', function () { const htmlContent = '
not-a-child-div
'; await page.setContent(htmlContent); - const elementHandle = (await page.$('#myId'))!; + using elementHandle = (await page.$('#myId'))!; const errorMessage = await elementHandle .$eval('.a', node => { return (node as HTMLElement).innerText; @@ -291,7 +291,7 @@ describe('querySelector', function () { await page.setContent( '
' ); - const tweet = (await page.$('.tweet'))!; + using tweet = (await page.$('.tweet'))!; const content = await tweet.$$eval('.like', nodes => { return (nodes as HTMLElement[]).map(n => { return n.innerText; @@ -306,7 +306,7 @@ describe('querySelector', function () { const htmlContent = '
not-a-child-div
a1-child-div
a2-child-div
'; await page.setContent(htmlContent); - const elementHandle = (await page.$('#myId'))!; + using elementHandle = (await page.$('#myId'))!; const content = await elementHandle.$$eval('.a', nodes => { return (nodes as HTMLElement[]).map(n => { return n.innerText; @@ -321,7 +321,7 @@ describe('querySelector', function () { const htmlContent = '
not-a-child-div
'; await page.setContent(htmlContent); - const elementHandle = (await page.$('#myId'))!; + using elementHandle = (await page.$('#myId'))!; const nodesLength = await elementHandle.$$eval('.a', nodes => { return nodes.length; }); @@ -336,7 +336,7 @@ describe('querySelector', function () { await page.setContent( '
A

B
' ); - const html = (await page.$('html'))!; + using html = (await page.$('html'))!; const elements = await html.$$('div'); expect(elements).toHaveLength(2); const promises = elements.map(element => { @@ -353,7 +353,7 @@ describe('querySelector', function () { await page.setContent( 'A
B' ); - const html = (await page.$('html'))!; + using html = (await page.$('html'))!; const elements = await html.$$('div'); expect(elements).toHaveLength(0); }); @@ -367,7 +367,7 @@ describe('querySelector', function () { await page.setContent( '
A
' ); - const html = (await page.$('html'))!; + using html = (await page.$('html'))!; const second = await html.$x(`./body/div[contains(@class, 'second')]`); const inner = await second[0]!.$x(`./div[contains(@class, 'inner')]`); const content = await page.evaluate(e => { @@ -382,7 +382,7 @@ describe('querySelector', function () { await page.setContent( '
B
' ); - const html = (await page.$('html'))!; + using html = (await page.$('html'))!; const second = await html.$x(`/div[contains(@class, 'third')]`); expect(second).toEqual([]); }); @@ -411,7 +411,7 @@ describe('querySelector', function () { await page.setContent( '
A

B
' ); - const html = (await page.$('html'))!; + using html = (await page.$('html'))!; const elements = await html.$$('allArray/div'); expect(elements).toHaveLength(2); const promises = elements.map(element => { @@ -428,7 +428,7 @@ describe('querySelector', function () { await page.setContent( 'A
B' ); - const html = (await page.$('html'))!; + using html = (await page.$('html'))!; const elements = await html.$$('allArray/div'); expect(elements).toHaveLength(0); }); @@ -463,7 +463,7 @@ describe('querySelector', function () { await page.setContent( '
2
2
1
3
' ); - const divHandle = (await page.$('div'))!; + using divHandle = (await page.$('div'))!; const sum = await page.$$eval( 'allArray/section', (sections, div) => { diff --git a/test/src/requestinterception-experimental.spec.ts b/test/src/requestinterception-experimental.spec.ts index e7153440..2c3c5afa 100644 --- a/test/src/requestinterception-experimental.spec.ts +++ b/test/src/requestinterception-experimental.spec.ts @@ -921,7 +921,7 @@ describe('request interception', function () { return (img.onload = fulfill); }); }, server.PREFIX); - const img = (await page.$('img'))!; + using img = (await page.$('img'))!; expect(await img.screenshot()).toBeGolden('mock-binary-response.png'); }); it('should stringify intercepted request response headers', async () => { diff --git a/test/src/requestinterception.spec.ts b/test/src/requestinterception.spec.ts index 398fc5b1..04ede17d 100644 --- a/test/src/requestinterception.spec.ts +++ b/test/src/requestinterception.spec.ts @@ -868,7 +868,7 @@ describe('request interception', function () { return (img.onload = fulfill); }); }, server.PREFIX); - const img = (await page.$('img'))!; + using img = (await page.$('img'))!; expect(await img.screenshot()).toBeGolden('mock-binary-response.png'); }); it('should stringify intercepted request response headers', async () => { diff --git a/test/src/screenshot.spec.ts b/test/src/screenshot.spec.ts index 8a4acde7..d5941bc5 100644 --- a/test/src/screenshot.spec.ts +++ b/test/src/screenshot.spec.ts @@ -14,6 +14,8 @@ * limitations under the License. */ +import assert from 'assert'; + import expect from 'expect'; import {getTestState, launch, setupTestBrowserHooks} from './mocha-utils.js'; @@ -213,7 +215,7 @@ describe('Screenshots', function () { await page.evaluate(() => { return window.scrollBy(50, 100); }); - const elementHandle = (await page.$('.box:nth-of-type(3)'))!; + using elementHandle = (await page.$('.box:nth-of-type(3)'))!; const screenshot = await elementHandle.screenshot(); expect(screenshot).toBeGolden('screenshot-element-bounding-box.png'); }); @@ -231,7 +233,8 @@ describe('Screenshots', function () { await page.evaluate(() => { return window.scrollBy(50, 100); }); - const elementHandle = (await page.$('.box:nth-of-type(3)'))!; + using elementHandle = await page.$('.box:nth-of-type(3)'); + assert(elementHandle); const screenshot = await elementHandle.screenshot(); expect(screenshot).toBeTruthy(); } finally { @@ -253,7 +256,7 @@ describe('Screenshots', function () {
`); - const elementHandle = (await page.$('div'))!; + using elementHandle = (await page.$('div'))!; const screenshot = await elementHandle.screenshot(); expect(screenshot).toBeGolden('screenshot-element-padding-border.png'); }); @@ -277,7 +280,7 @@ describe('Screenshots', function () {
`); - const elementHandle = (await page.$('div.to-screenshot'))!; + using elementHandle = (await page.$('div.to-screenshot'))!; const screenshot = await elementHandle.screenshot(); expect(screenshot).toBeGolden( 'screenshot-element-larger-than-viewport.png' @@ -313,7 +316,7 @@ describe('Screenshots', function () {
`); - const elementHandle = (await page.$('div.to-screenshot'))!; + using elementHandle = (await page.$('div.to-screenshot'))!; const screenshot = await elementHandle.screenshot(); expect(screenshot).toBeGolden( 'screenshot-element-scrolled-into-view.png' @@ -330,7 +333,7 @@ describe('Screenshots', function () { height: 100px; background: green; transform: rotateZ(200deg);"> 
`); - const elementHandle = (await page.$('div'))!; + using elementHandle = (await page.$('div'))!; const screenshot = await elementHandle.screenshot(); expect(screenshot).toBeGolden('screenshot-element-rotate.png'); }); @@ -338,7 +341,7 @@ describe('Screenshots', function () { const {page} = await getTestState(); await page.setContent('

remove this

'); - const elementHandle = (await page.$('h1'))!; + using elementHandle = (await page.$('h1'))!; await page.evaluate((element: HTMLElement) => { return element.remove(); }, elementHandle); @@ -353,7 +356,7 @@ describe('Screenshots', function () { const {page} = await getTestState(); await page.setContent('
'); - const div = (await page.$('div'))!; + using div = (await page.$('div'))!; const error = await div.screenshot().catch(error_ => { return error_; }); @@ -365,7 +368,7 @@ describe('Screenshots', function () { await page.setContent( '
' ); - const elementHandle = (await page.$('div'))!; + using elementHandle = (await page.$('div'))!; const screenshot = await elementHandle.screenshot(); expect(screenshot).toBeGolden('screenshot-element-fractional.png'); }); @@ -375,7 +378,7 @@ describe('Screenshots', function () { await page.setContent( '
' ); - const elementHandle = (await page.$('div'))!; + using elementHandle = (await page.$('div'))!; const screenshot = await elementHandle.screenshot(); expect(screenshot).toBeGolden('screenshot-element-fractional-offset.png'); }); diff --git a/test/src/stacktrace.spec.ts b/test/src/stacktrace.spec.ts index 78be1b13..d3e0a057 100644 --- a/test/src/stacktrace.spec.ts +++ b/test/src/stacktrace.spec.ts @@ -84,7 +84,7 @@ describe('Stack trace', function () { it('should work with contiguous evaluation', async () => { const {page} = await getTestState(); - const thrower = await page.evaluateHandle(() => { + using thrower = await page.evaluateHandle(() => { return () => { throw new Error('Test'); }; diff --git a/test/src/touchscreen.spec.ts b/test/src/touchscreen.spec.ts index b2fe6a9a..749edecf 100644 --- a/test/src/touchscreen.spec.ts +++ b/test/src/touchscreen.spec.ts @@ -40,7 +40,7 @@ describe('Touchscreen', function () { const iPhone = KnownDevices['iPhone 6']!; await page.emulate(iPhone); await page.goto(server.PREFIX + '/input/touches.html'); - const button = (await page.$('button'))!; + using button = (await page.$('button'))!; await button.tap(); expect( await page.evaluate(() => { @@ -54,7 +54,7 @@ describe('Touchscreen', function () { const iPhone = KnownDevices['iPhone 6']!; await page.emulate(iPhone); await page.goto(server.PREFIX + '/input/touches-move.html'); - const touch = (await page.$('#touch'))!; + using touch = (await page.$('#touch'))!; const touchObj = (await touch.boundingBox()) as BoundingBox; await page.touchscreen.touchStart(touchObj.x, touchObj.y); const movePosx = 100; diff --git a/test/src/utils.ts b/test/src/utils.ts index 5d9bf035..36564630 100644 --- a/test/src/utils.ts +++ b/test/src/utils.ts @@ -73,7 +73,7 @@ export const attachFrame = async ( frameId: string, url: string ): Promise => { - const handle = await pageOrFrame.evaluateHandle(attachFrame, frameId, url); + using handle = await pageOrFrame.evaluateHandle(attachFrame, frameId, url); return (await handle.asElement()?.contentFrame()) ?? undefined; async function attachFrame(frameId: string, url: string) { diff --git a/test/src/waittask.spec.ts b/test/src/waittask.spec.ts index e0605509..2ea379c3 100644 --- a/test/src/waittask.spec.ts +++ b/test/src/waittask.spec.ts @@ -221,11 +221,11 @@ describe('waittask specs', function () { const {page} = await getTestState(); await page.setContent('
'); - const div = (await page.$('div'))!; + using div = (await page.$('div'))!; let resolved = false; const waitForFunction = page .waitForFunction( - (element: Element) => { + element => { return element.localName === 'div' && !element.parentElement; }, {}, @@ -432,7 +432,7 @@ describe('waittask specs', function () { const watchdog = frame.waitForSelector('div'); await frame.evaluate(addElement, 'br'); await frame.evaluate(addElement, 'div'); - const eHandle = (await watchdog)!; + using eHandle = (await watchdog)!; const tagName = await (await eHandle.getProperty('tagName')).jsonValue(); expect(tagName).toBe('DIV'); }); @@ -459,7 +459,7 @@ describe('waittask specs', function () { const watchdog = page.waitForSelector('div'); await otherFrame.evaluate(addElement, 'div'); await page.evaluate(addElement, 'div'); - const eHandle = await watchdog; + using eHandle = await watchdog; expect(eHandle?.frame).toBe(page.mainFrame()); }); @@ -473,7 +473,7 @@ describe('waittask specs', function () { const waitForSelectorPromise = frame2.waitForSelector('div'); await frame1.evaluate(addElement, 'div'); await frame2.evaluate(addElement, 'div'); - const eHandle = await waitForSelectorPromise; + using eHandle = await waitForSelectorPromise; expect(eHandle?.frame).toBe(frame2); }); @@ -513,7 +513,7 @@ describe('waittask specs', function () { const promise = page.waitForSelector('div', {visible: true}); await page.setContent('
text
'); - const element = await page.evaluateHandle(() => { + using element = await page.evaluateHandle(() => { return document.getElementsByTagName('div')[0]!; }); await expect( @@ -529,7 +529,7 @@ describe('waittask specs', function () { const promise = page.waitForSelector('div', {visible: true}); await page.setContent('
text
'); - const element = await page.evaluateHandle(() => { + using element = await page.evaluateHandle(() => { return document.getElementsByTagName('div')[0]!; }); await expect( @@ -551,7 +551,7 @@ describe('waittask specs', function () { const promise = page.waitForSelector('div', {visible: true}); await page.setContent('
text
'); - const element = await page.evaluateHandle(() => { + using element = await page.evaluateHandle(() => { return document.getElementsByTagName('div')[0]!; }); await expect( @@ -578,7 +578,7 @@ describe('waittask specs', function () { await page.setContent( `
hi
` ); - const element = await page.evaluateHandle(() => { + using element = await page.evaluateHandle(() => { return document.getElementsByTagName('div')[0]!; }); await expect( @@ -600,7 +600,7 @@ describe('waittask specs', function () { const promise = page.waitForSelector('div', {hidden: true}); await page.setContent(`
text
`); - const element = await page.evaluateHandle(() => { + using element = await page.evaluateHandle(() => { return document.getElementsByTagName('div')[0]!; }); await expect( @@ -616,7 +616,7 @@ describe('waittask specs', function () { const promise = page.waitForSelector('div', {hidden: true}); await page.setContent(`
text
`); - const element = await page.evaluateHandle(() => { + using element = await page.evaluateHandle(() => { return document.getElementsByTagName('div')[0]!; }); await expect( @@ -632,7 +632,7 @@ describe('waittask specs', function () { const promise = page.waitForSelector('div', {hidden: true}); await page.setContent('
text
'); - const element = await page.evaluateHandle(() => { + using element = await page.evaluateHandle(() => { return document.getElementsByTagName('div')[0]!; }); await expect( @@ -648,7 +648,7 @@ describe('waittask specs', function () { const promise = page.waitForSelector('div', {hidden: true}); await page.setContent(`
text
`); - const element = await page.evaluateHandle(() => { + using element = await page.evaluateHandle(() => { return document.getElementsByTagName('div')[0]!; }); await expect( @@ -662,7 +662,7 @@ describe('waittask specs', function () { it('should return null if waiting to hide non-existing element', async () => { const {page} = await getTestState(); - const handle = await page.waitForSelector('non-existing', { + using handle = await page.waitForSelector('non-existing', { hidden: true, }); expect(handle).toBe(null); @@ -779,7 +779,7 @@ describe('waittask specs', function () { const waitForXPathPromise = frame2.waitForXPath('//div'); await frame1.evaluate(addElement, 'div'); await frame2.evaluate(addElement, 'div'); - const eHandle = await waitForXPathPromise; + using eHandle = await waitForXPathPromise; expect(eHandle?.frame).toBe(frame2); }); it('should throw when frame is detached', async () => { @@ -823,7 +823,7 @@ describe('waittask specs', function () { it('hidden should return null if the element is not found', async () => { const {page} = await getTestState(); - const waitForXPath = await page.waitForXPath('//div', {hidden: true}); + using waitForXPath = await page.waitForXPath('//div', {hidden: true}); expect(waitForXPath).toBe(null); }); @@ -832,7 +832,7 @@ describe('waittask specs', function () { await page.setContent(`
text
`); - const waitForXPath = await page.waitForXPath('//div', {hidden: true}); + using waitForXPath = await page.waitForXPath('//div', {hidden: true}); expect(waitForXPath).toBeInstanceOf(ElementHandle); }); @@ -854,7 +854,7 @@ describe('waittask specs', function () { const {page} = await getTestState(); await page.setContent(`
some text
`); - const text = await page.waitForXPath('//div/text()'); + using text = await page.waitForXPath('//div/text()'); expect(await (await text!.getProperty('nodeType')!).jsonValue()).toBe( 3 /* Node.TEXT_NODE */ ); diff --git a/test/src/worker.spec.ts b/test/src/worker.spec.ts index 1f766829..21dddb37 100644 --- a/test/src/worker.spec.ts +++ b/test/src/worker.spec.ts @@ -47,11 +47,11 @@ describe('Workers', function () { const {page} = await getTestState(); const workerCreatedPromise = waitEvent(page, 'workercreated'); - const workerObj = await page.evaluateHandle(() => { + using workerObj = await page.evaluateHandle(() => { return new Worker('data:text/javascript,1'); }); const worker = await workerCreatedPromise; - const workerThisObj = await worker.evaluateHandle(() => { + using workerThisObj = await worker.evaluateHandle(() => { return this; }); const workerDestroyedPromise = waitEvent(page, 'workerdestroyed');