feat: introduce locator.viewportRatio (#19761)

References #8740
This commit is contained in:
Andrey Lushnikov 2023-01-05 10:49:32 -08:00 committed by GitHub
parent abe901d598
commit 3883799d68
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 87 additions and 0 deletions

View File

@ -1339,6 +1339,12 @@ When all steps combined have not finished during the specified [`option: timeout
### option: Locator.uncheck.trial = %%-input-trial-%% ### option: Locator.uncheck.trial = %%-input-trial-%%
* since: v1.14 * since: v1.14
## async method: Locator.viewportRatio
* since: v1.30
- returns: <[float]>
Returns the ratio of intersection between viewport and the element, according to the [intersection observer API](https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API).
## async method: Locator.waitFor ## async method: Locator.waitFor
* since: v1.16 * since: v1.16

View File

@ -257,6 +257,10 @@ export class Locator implements api.Locator {
return this._withElement((h, timeout) => h.scrollIntoViewIfNeeded({ ...options, timeout }), options.timeout); return this._withElement((h, timeout) => h.scrollIntoViewIfNeeded({ ...options, timeout }), options.timeout);
} }
async viewportRatio(): Promise<number> {
return (await this._frame._channel.viewportRatio({ selector: this._selector, strict: true })).value;
}
async selectOption(values: string | api.ElementHandle | SelectOption | string[] | api.ElementHandle[] | SelectOption[] | null, options: SelectOptionOptions = {}): Promise<string[]> { async selectOption(values: string | api.ElementHandle | SelectOption | string[] | api.ElementHandle[] | SelectOption[] | null, options: SelectOptionOptions = {}): Promise<string[]> {
return this._frame.selectOption(this._selector, values, { strict: true, ...options }); return this._frame.selectOption(this._selector, values, { strict: true, ...options });
} }

View File

@ -66,6 +66,7 @@ export const commandsWithTracingSnapshots = new Set([
'Frame.isEnabled', 'Frame.isEnabled',
'Frame.isHidden', 'Frame.isHidden',
'Frame.isVisible', 'Frame.isVisible',
'Frame.viewportRatio',
'Frame.isEditable', 'Frame.isEditable',
'Frame.press', 'Frame.press',
'Frame.selectOption', 'Frame.selectOption',

View File

@ -1425,6 +1425,13 @@ scheme.FrameIsVisibleParams = tObject({
scheme.FrameIsVisibleResult = tObject({ scheme.FrameIsVisibleResult = tObject({
value: tBoolean, value: tBoolean,
}); });
scheme.FrameViewportRatioParams = tObject({
selector: tString,
strict: tOptional(tBoolean),
});
scheme.FrameViewportRatioResult = tObject({
value: tNumber,
});
scheme.FrameIsEditableParams = tObject({ scheme.FrameIsEditableParams = tObject({
selector: tString, selector: tString,
strict: tOptional(tBoolean), strict: tOptional(tBoolean),

View File

@ -203,6 +203,10 @@ export class FrameDispatcher extends Dispatcher<Frame, channels.FrameChannel, Pa
return { value: await this._frame.isVisible(metadata, params.selector, params) }; return { value: await this._frame.isVisible(metadata, params.selector, params) };
} }
async viewportRatio(params: channels.FrameViewportRatioParams, metadata: CallMetadata): Promise<channels.FrameViewportRatioResult> {
return { value: await this._frame.viewportRatio(metadata, params.selector, params) };
}
async hover(params: channels.FrameHoverParams, metadata: CallMetadata): Promise<void> { async hover(params: channels.FrameHoverParams, metadata: CallMetadata): Promise<void> {
return await this._frame.hover(metadata, params.selector, params); return await this._frame.hover(metadata, params.selector, params);
} }

View File

@ -1318,6 +1318,31 @@ export class Frame extends SdkObject {
}, this._page._timeoutSettings.timeout({})); }, this._page._timeoutSettings.timeout({}));
} }
async viewportRatio(metadata: CallMetadata, selector: string, options: types.StrictOptions = {}): Promise<number> {
const controller = new ProgressController(metadata, this);
return controller.run(async progress => {
progress.log(` calculating viewport ratio of ${this._asLocator(selector)}`);
const resolved = await this._resolveInjectedForSelector(progress, selector, options);
if (!resolved)
return 0;
return await resolved.injected.evaluate(async (injected, { info }) => {
const element = injected.querySelector(info.parsed, document, info.strict);
if (!element)
return 0;
return await new Promise(resolve => {
const observer = new IntersectionObserver(entries => {
resolve(entries[0].intersectionRatio);
observer.disconnect();
});
observer.observe(element);
// Firefox doesn't call IntersectionObserver callback unless
// there are rafs.
requestAnimationFrame(() => {});
});
}, { info: resolved.info });
}, this._page._timeoutSettings.timeout({}));
}
async isHidden(metadata: CallMetadata, selector: string, options: types.StrictOptions = {}): Promise<boolean> { async isHidden(metadata: CallMetadata, selector: string, options: types.StrictOptions = {}): Promise<boolean> {
return !(await this.isVisible(metadata, selector, options)); return !(await this.isVisible(metadata, selector, options));
} }

View File

@ -11448,6 +11448,12 @@ export interface Locator {
trial?: boolean; trial?: boolean;
}): Promise<void>; }): Promise<void>;
/**
* Returns the ratio of intersection between viewport and the element, according to the
* [intersection observer API](https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API).
*/
viewportRatio(): Promise<number>;
/** /**
* Returns when element specified by locator satisfies the `state` option. * Returns when element specified by locator satisfies the `state` option.
* *

View File

@ -2196,6 +2196,7 @@ export interface FrameChannel extends FrameEventTarget, Channel {
isEnabled(params: FrameIsEnabledParams, metadata?: Metadata): Promise<FrameIsEnabledResult>; isEnabled(params: FrameIsEnabledParams, metadata?: Metadata): Promise<FrameIsEnabledResult>;
isHidden(params: FrameIsHiddenParams, metadata?: Metadata): Promise<FrameIsHiddenResult>; isHidden(params: FrameIsHiddenParams, metadata?: Metadata): Promise<FrameIsHiddenResult>;
isVisible(params: FrameIsVisibleParams, metadata?: Metadata): Promise<FrameIsVisibleResult>; isVisible(params: FrameIsVisibleParams, metadata?: Metadata): Promise<FrameIsVisibleResult>;
viewportRatio(params: FrameViewportRatioParams, metadata?: Metadata): Promise<FrameViewportRatioResult>;
isEditable(params: FrameIsEditableParams, metadata?: Metadata): Promise<FrameIsEditableResult>; isEditable(params: FrameIsEditableParams, metadata?: Metadata): Promise<FrameIsEditableResult>;
press(params: FramePressParams, metadata?: Metadata): Promise<FramePressResult>; press(params: FramePressParams, metadata?: Metadata): Promise<FramePressResult>;
querySelector(params: FrameQuerySelectorParams, metadata?: Metadata): Promise<FrameQuerySelectorResult>; querySelector(params: FrameQuerySelectorParams, metadata?: Metadata): Promise<FrameQuerySelectorResult>;
@ -2593,6 +2594,16 @@ export type FrameIsVisibleOptions = {
export type FrameIsVisibleResult = { export type FrameIsVisibleResult = {
value: boolean, value: boolean,
}; };
export type FrameViewportRatioParams = {
selector: string,
strict?: boolean,
};
export type FrameViewportRatioOptions = {
strict?: boolean,
};
export type FrameViewportRatioResult = {
value: number,
};
export type FrameIsEditableParams = { export type FrameIsEditableParams = {
selector: string, selector: string,
strict?: boolean, strict?: boolean,

View File

@ -1908,6 +1908,15 @@ Frame:
tracing: tracing:
snapshot: true snapshot: true
viewportRatio:
parameters:
selector: string
strict: boolean?
returns:
value: number
tracing:
snapshot: true
isEditable: isEditable:
parameters: parameters:
selector: string selector: string

View File

@ -154,3 +154,17 @@ it('locator.count should work with deleted Map in main world', async ({ page })
await page.locator('#searchResultTableDiv .x-grid3-row').count(); await page.locator('#searchResultTableDiv .x-grid3-row').count();
await expect(page.locator('#searchResultTableDiv .x-grid3-row')).toHaveCount(0); await expect(page.locator('#searchResultTableDiv .x-grid3-row')).toHaveCount(0);
}); });
it('locator.viewportRatio', async ({ page }) => {
await page.setContent(`
<style>body { padding: 0; margin: 0; } div { position: absolute; left: 0; top: 0 }</style>
<div id=fills-viewport style='width: 100vw; height: 100vh;'></div>
<div id=half-viewport style='width: 50vw; height: 100vh;'></div>
<div id=twice-viewport style='width: 200vw; height: 100vh;'></div>
<div id=off-viewport style='left:100vw; width: 100vw; height: 100vh;'></div>
`);
expect.soft(await page.locator('#fills-viewport').viewportRatio()).toBe(1);
expect.soft(await page.locator('#half-viewport').viewportRatio()).toBe(1);
expect.soft(await page.locator('#twice-viewport').viewportRatio()).toBe(0.5);
expect.soft(await page.locator('#off-viewport').viewportRatio()).toBe(0);
});