fix(locator.count): do not touch main workd when computing count (#11256)
This commit is contained in:
parent
71a8da9c88
commit
dc07fa6da6
|
|
@ -213,6 +213,10 @@ export class Frame extends ChannelOwner<channels.FrameChannel> implements api.Fr
|
||||||
return result.elements.map(e => ElementHandle.from(e) as ElementHandle<SVGElement | HTMLElement>);
|
return result.elements.map(e => ElementHandle.from(e) as ElementHandle<SVGElement | HTMLElement>);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async _queryCount(selector: string): Promise<number> {
|
||||||
|
return (await this._channel.queryCount({ selector })).value;
|
||||||
|
}
|
||||||
|
|
||||||
async content(): Promise<string> {
|
async content(): Promise<string> {
|
||||||
return (await this._channel.content()).value;
|
return (await this._channel.content()).value;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -135,7 +135,7 @@ export class Locator implements api.Locator {
|
||||||
}
|
}
|
||||||
|
|
||||||
async count(): Promise<number> {
|
async count(): Promise<number> {
|
||||||
return this.evaluateAll(ee => ee.length);
|
return this._frame._queryCount(this._selector);
|
||||||
}
|
}
|
||||||
|
|
||||||
async getAttribute(name: string, options?: TimeoutOptions): Promise<string | null> {
|
async getAttribute(name: string, options?: TimeoutOptions): Promise<string | null> {
|
||||||
|
|
|
||||||
|
|
@ -100,6 +100,10 @@ export class FrameDispatcher extends Dispatcher<Frame, channels.FrameChannel> im
|
||||||
return { elements: elements.map(e => ElementHandleDispatcher.from(this._scope, e)) };
|
return { elements: elements.map(e => ElementHandleDispatcher.from(this._scope, e)) };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async queryCount(params: channels.FrameQueryCountParams): Promise<channels.FrameQueryCountResult> {
|
||||||
|
return { value: await this._frame.queryCount(params.selector) };
|
||||||
|
}
|
||||||
|
|
||||||
async content(): Promise<channels.FrameContentResult> {
|
async content(): Promise<channels.FrameContentResult> {
|
||||||
return { value: await this._frame.content() };
|
return { value: await this._frame.content() };
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1794,6 +1794,7 @@ export interface FrameChannel extends FrameEventTarget, Channel {
|
||||||
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>;
|
||||||
querySelectorAll(params: FrameQuerySelectorAllParams, metadata?: Metadata): Promise<FrameQuerySelectorAllResult>;
|
querySelectorAll(params: FrameQuerySelectorAllParams, metadata?: Metadata): Promise<FrameQuerySelectorAllResult>;
|
||||||
|
queryCount(params: FrameQueryCountParams, metadata?: Metadata): Promise<FrameQueryCountResult>;
|
||||||
selectOption(params: FrameSelectOptionParams, metadata?: Metadata): Promise<FrameSelectOptionResult>;
|
selectOption(params: FrameSelectOptionParams, metadata?: Metadata): Promise<FrameSelectOptionResult>;
|
||||||
setContent(params: FrameSetContentParams, metadata?: Metadata): Promise<FrameSetContentResult>;
|
setContent(params: FrameSetContentParams, metadata?: Metadata): Promise<FrameSetContentResult>;
|
||||||
setInputFiles(params: FrameSetInputFilesParams, metadata?: Metadata): Promise<FrameSetInputFilesResult>;
|
setInputFiles(params: FrameSetInputFilesParams, metadata?: Metadata): Promise<FrameSetInputFilesResult>;
|
||||||
|
|
@ -2210,6 +2211,15 @@ export type FrameQuerySelectorAllOptions = {
|
||||||
export type FrameQuerySelectorAllResult = {
|
export type FrameQuerySelectorAllResult = {
|
||||||
elements: ElementHandleChannel[],
|
elements: ElementHandleChannel[],
|
||||||
};
|
};
|
||||||
|
export type FrameQueryCountParams = {
|
||||||
|
selector: string,
|
||||||
|
};
|
||||||
|
export type FrameQueryCountOptions = {
|
||||||
|
|
||||||
|
};
|
||||||
|
export type FrameQueryCountResult = {
|
||||||
|
value: number,
|
||||||
|
};
|
||||||
export type FrameSelectOptionParams = {
|
export type FrameSelectOptionParams = {
|
||||||
selector: string,
|
selector: string,
|
||||||
strict?: boolean,
|
strict?: boolean,
|
||||||
|
|
|
||||||
|
|
@ -1640,6 +1640,12 @@ Frame:
|
||||||
type: array
|
type: array
|
||||||
items: ElementHandle
|
items: ElementHandle
|
||||||
|
|
||||||
|
queryCount:
|
||||||
|
parameters:
|
||||||
|
selector: string
|
||||||
|
returns:
|
||||||
|
value: number
|
||||||
|
|
||||||
selectOption:
|
selectOption:
|
||||||
parameters:
|
parameters:
|
||||||
selector: string
|
selector: string
|
||||||
|
|
|
||||||
|
|
@ -826,6 +826,9 @@ export function createScheme(tChannel: (name: string) => Validator): Scheme {
|
||||||
scheme.FrameQuerySelectorAllParams = tObject({
|
scheme.FrameQuerySelectorAllParams = tObject({
|
||||||
selector: tString,
|
selector: tString,
|
||||||
});
|
});
|
||||||
|
scheme.FrameQueryCountParams = tObject({
|
||||||
|
selector: tString,
|
||||||
|
});
|
||||||
scheme.FrameSelectOptionParams = tObject({
|
scheme.FrameSelectOptionParams = tObject({
|
||||||
selector: tString,
|
selector: tString,
|
||||||
strict: tOptional(tBoolean),
|
strict: tOptional(tBoolean),
|
||||||
|
|
|
||||||
|
|
@ -797,7 +797,7 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> {
|
||||||
throw new Error(`Error: failed to find frame for selector "${selector}"`);
|
throw new Error(`Error: failed to find frame for selector "${selector}"`);
|
||||||
const { frame, info } = pair;
|
const { frame, info } = pair;
|
||||||
// If we end up in the same frame => use the scope again, line above was noop.
|
// If we end up in the same frame => use the scope again, line above was noop.
|
||||||
const arrayHandle = await this._page.selectors._queryArray(frame, info, this._frame === frame ? this : undefined);
|
const arrayHandle = await this._page.selectors._queryArrayInMainWorld(frame, info, this._frame === frame ? this : undefined);
|
||||||
const result = await arrayHandle.evaluateExpressionAndWaitForSignals(expression, isFunction, true, arg);
|
const result = await arrayHandle.evaluateExpressionAndWaitForSignals(expression, isFunction, true, arg);
|
||||||
arrayHandle.dispose();
|
arrayHandle.dispose();
|
||||||
return result;
|
return result;
|
||||||
|
|
|
||||||
|
|
@ -780,7 +780,7 @@ export class Frame extends SdkObject {
|
||||||
const pair = await this.resolveFrameForSelectorNoWait(selector, {});
|
const pair = await this.resolveFrameForSelectorNoWait(selector, {});
|
||||||
if (!pair)
|
if (!pair)
|
||||||
throw new Error(`Error: failed to find frame for selector "${selector}"`);
|
throw new Error(`Error: failed to find frame for selector "${selector}"`);
|
||||||
const arrayHandle = await this._page.selectors._queryArray(pair.frame, pair.info);
|
const arrayHandle = await this._page.selectors._queryArrayInMainWorld(pair.frame, pair.info);
|
||||||
const result = await arrayHandle.evaluateExpressionAndWaitForSignals(expression, isFunction, true, arg);
|
const result = await arrayHandle.evaluateExpressionAndWaitForSignals(expression, isFunction, true, arg);
|
||||||
arrayHandle.dispose();
|
arrayHandle.dispose();
|
||||||
return result;
|
return result;
|
||||||
|
|
@ -793,6 +793,13 @@ export class Frame extends SdkObject {
|
||||||
return this._page.selectors._queryAll(pair.frame, pair.info, undefined, true /* adoptToMain */);
|
return this._page.selectors._queryAll(pair.frame, pair.info, undefined, true /* adoptToMain */);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async queryCount(selector: string): Promise<number> {
|
||||||
|
const pair = await this.resolveFrameForSelectorNoWait(selector);
|
||||||
|
if (!pair)
|
||||||
|
throw new Error(`Error: failed to find frame for selector "${selector}"`);
|
||||||
|
return await this._page.selectors._queryCount(pair.frame, pair.info);
|
||||||
|
}
|
||||||
|
|
||||||
async content(): Promise<string> {
|
async content(): Promise<string> {
|
||||||
try {
|
try {
|
||||||
const context = await this._utilityContext();
|
const context = await this._utilityContext();
|
||||||
|
|
@ -1536,7 +1543,7 @@ export class Frame extends SdkObject {
|
||||||
return { frame, info: this._page.parseSelector(frameChunks[frameChunks.length - 1], options) };
|
return { frame, info: this._page.parseSelector(frameChunks[frameChunks.length - 1], options) };
|
||||||
}
|
}
|
||||||
|
|
||||||
async resolveFrameForSelectorNoWait(selector: string, options: types.StrictOptions & types.TimeoutOptions, scope?: dom.ElementHandle): Promise<SelectorInFrame | null> {
|
async resolveFrameForSelectorNoWait(selector: string, options: types.StrictOptions & types.TimeoutOptions = {}, scope?: dom.ElementHandle): Promise<SelectorInFrame | null> {
|
||||||
let frame: Frame | null = this;
|
let frame: Frame | null = this;
|
||||||
const frameChunks = splitSelectorByFrame(selector);
|
const frameChunks = splitSelectorByFrame(selector);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -83,7 +83,7 @@ export class Selectors {
|
||||||
return this._adoptIfNeeded(elementHandle, mainContext);
|
return this._adoptIfNeeded(elementHandle, mainContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
async _queryArray(frame: frames.Frame, info: SelectorInfo, scope?: dom.ElementHandle): Promise<js.JSHandle<Element[]>> {
|
async _queryArrayInMainWorld(frame: frames.Frame, info: SelectorInfo, scope?: dom.ElementHandle): Promise<js.JSHandle<Element[]>> {
|
||||||
const context = await frame._mainContext();
|
const context = await frame._mainContext();
|
||||||
const injectedScript = await context.injectedScript();
|
const injectedScript = await context.injectedScript();
|
||||||
const arrayHandle = await injectedScript.evaluateHandle((injected, { parsed, scope }) => {
|
const arrayHandle = await injectedScript.evaluateHandle((injected, { parsed, scope }) => {
|
||||||
|
|
@ -92,6 +92,14 @@ export class Selectors {
|
||||||
return arrayHandle;
|
return arrayHandle;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async _queryCount(frame: frames.Frame, info: SelectorInfo, scope?: dom.ElementHandle): Promise<number> {
|
||||||
|
const context = await frame._utilityContext();
|
||||||
|
const injectedScript = await context.injectedScript();
|
||||||
|
return await injectedScript.evaluate((injected, { parsed, scope }) => {
|
||||||
|
return injected.querySelectorAll(parsed, scope || document).length;
|
||||||
|
}, { parsed: info.parsed, scope });
|
||||||
|
}
|
||||||
|
|
||||||
async _queryAll(frame: frames.Frame, selector: SelectorInfo, scope?: dom.ElementHandle, adoptToMain?: boolean): Promise<dom.ElementHandle<Element>[]> {
|
async _queryAll(frame: frames.Frame, selector: SelectorInfo, scope?: dom.ElementHandle, adoptToMain?: boolean): Promise<dom.ElementHandle<Element>[]> {
|
||||||
const info = typeof selector === 'string' ? frame._page.parseSelector(selector) : selector;
|
const info = typeof selector === 'string' ? frame._page.parseSelector(selector) : selector;
|
||||||
const context = await frame._context(info.world);
|
const context = await frame._context(info.world);
|
||||||
|
|
|
||||||
|
|
@ -113,3 +113,10 @@ it('should combine visible with other selectors', async ({ page }) => {
|
||||||
await expect(locator).toHaveText('visible data2');
|
await expect(locator).toHaveText('visible data2');
|
||||||
await expect(page.locator('.item >> visible=true >> text=data3')).toHaveText('visible data3');
|
await expect(page.locator('.item >> visible=true >> text=data3')).toHaveText('visible data3');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('locator.count should work with deleted Map in main world', async ({ page }) => {
|
||||||
|
it.info().annotations.push({ type: 'issue', description: 'https://github.com/microsoft/playwright/issues/11254' });
|
||||||
|
await page.evaluate('Map = 1');
|
||||||
|
await page.locator('#searchResultTableDiv .x-grid3-row').count();
|
||||||
|
await expect(page.locator('#searchResultTableDiv .x-grid3-row')).toHaveCount(0);
|
||||||
|
});
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue