2021-07-22 21:37:20 -07:00
|
|
|
/**
|
|
|
|
* Copyright (c) Microsoft Corporation.
|
|
|
|
*
|
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
* you may not use this file except in compliance with the License.
|
|
|
|
* You may obtain a copy of the License at
|
|
|
|
*
|
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
*
|
|
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
* See the License for the specific language governing permissions and
|
|
|
|
* limitations under the License.
|
|
|
|
*/
|
|
|
|
|
2022-04-06 13:57:14 -08:00
|
|
|
import type * as structs from '../../types/structs';
|
|
|
|
import type * as api from '../../types/types';
|
2022-09-20 18:41:51 -07:00
|
|
|
import type * as channels from '@protocol/channels';
|
2021-07-22 21:37:20 -07:00
|
|
|
import * as util from 'util';
|
2023-09-16 14:24:10 -07:00
|
|
|
import { asLocator, isString, monotonicTime } from '../utils';
|
2021-07-22 21:37:20 -07:00
|
|
|
import { ElementHandle } from './elementHandle';
|
2022-04-06 13:57:14 -08:00
|
|
|
import type { Frame } from './frame';
|
|
|
|
import type { FilePayload, FrameExpectOptions, Rect, SelectOption, SelectOptionOptions, TimeoutOptions } from './types';
|
2021-09-24 11:06:30 -07:00
|
|
|
import { parseResult, serializeArgument } from './jsHandle';
|
2022-11-07 09:06:13 -08:00
|
|
|
import { escapeForTextSelector } from '../utils/isomorphic/stringUtils';
|
|
|
|
import type { ByRoleOptions } from '../utils/isomorphic/locatorUtils';
|
|
|
|
import { getByAltTextSelector, getByLabelSelector, getByPlaceholderSelector, getByRoleSelector, getByTestIdSelector, getByTextSelector, getByTitleSelector } from '../utils/isomorphic/locatorUtils';
|
2021-07-22 21:37:20 -07:00
|
|
|
|
2022-05-03 10:33:33 +01:00
|
|
|
export type LocatorOptions = {
|
|
|
|
hasText?: string | RegExp;
|
2023-04-05 14:13:28 -07:00
|
|
|
hasNotText?: string | RegExp;
|
2022-05-03 10:33:33 +01:00
|
|
|
has?: Locator;
|
2023-04-05 12:45:46 -07:00
|
|
|
hasNot?: Locator;
|
2022-05-03 10:33:33 +01:00
|
|
|
};
|
|
|
|
|
2021-07-22 21:37:20 -07:00
|
|
|
export class Locator implements api.Locator {
|
2022-02-15 08:05:05 -07:00
|
|
|
_frame: Frame;
|
|
|
|
_selector: string;
|
2021-07-22 21:37:20 -07:00
|
|
|
|
2022-05-03 10:33:33 +01:00
|
|
|
constructor(frame: Frame, selector: string, options?: LocatorOptions) {
|
2021-07-22 21:37:20 -07:00
|
|
|
this._frame = frame;
|
|
|
|
this._selector = selector;
|
2021-12-14 15:37:31 -08:00
|
|
|
|
2022-10-21 16:29:45 -07:00
|
|
|
if (options?.hasText)
|
|
|
|
this._selector += ` >> internal:has-text=${escapeForTextSelector(options.hasText, false)}`;
|
2022-02-02 16:55:50 -08:00
|
|
|
|
2023-04-05 14:13:28 -07:00
|
|
|
if (options?.hasNotText)
|
|
|
|
this._selector += ` >> internal:has-not-text=${escapeForTextSelector(options.hasNotText, false)}`;
|
|
|
|
|
2022-05-12 18:50:19 +01:00
|
|
|
if (options?.has) {
|
|
|
|
const locator = options.has;
|
2022-05-03 10:33:33 +01:00
|
|
|
if (locator._frame !== frame)
|
2022-05-12 18:50:19 +01:00
|
|
|
throw new Error(`Inner "has" locator must belong to the same frame.`);
|
2022-10-05 08:45:10 -07:00
|
|
|
this._selector += ` >> internal:has=` + JSON.stringify(locator._selector);
|
2022-05-03 10:33:33 +01:00
|
|
|
}
|
2023-04-05 12:45:46 -07:00
|
|
|
|
|
|
|
if (options?.hasNot) {
|
|
|
|
const locator = options.hasNot;
|
|
|
|
if (locator._frame !== frame)
|
|
|
|
throw new Error(`Inner "hasNot" locator must belong to the same frame.`);
|
|
|
|
this._selector += ` >> internal:has-not=` + JSON.stringify(locator._selector);
|
|
|
|
}
|
2021-07-22 21:37:20 -07:00
|
|
|
}
|
|
|
|
|
2021-08-05 17:13:46 +02:00
|
|
|
private async _withElement<R>(task: (handle: ElementHandle<SVGElement | HTMLElement>, timeout?: number) => Promise<R>, timeout?: number): Promise<R> {
|
|
|
|
timeout = this._frame.page()._timeoutSettings.timeout({ timeout });
|
2021-07-22 21:37:20 -07:00
|
|
|
const deadline = timeout ? monotonicTime() + timeout : 0;
|
2021-10-21 15:10:47 -08:00
|
|
|
|
2023-12-07 20:13:35 -08:00
|
|
|
return await this._frame._wrapApiCall<R>(async () => {
|
2021-11-19 16:28:11 -08:00
|
|
|
const result = await this._frame._channel.waitForSelector({ selector: this._selector, strict: true, state: 'attached', timeout });
|
2021-10-21 15:10:47 -08:00
|
|
|
const handle = ElementHandle.fromNullable(result.element) as ElementHandle<SVGElement | HTMLElement> | null;
|
|
|
|
if (!handle)
|
|
|
|
throw new Error(`Could not resolve ${this._selector} to DOM Element`);
|
|
|
|
try {
|
|
|
|
return await task(handle, deadline ? deadline - monotonicTime() : 0);
|
|
|
|
} finally {
|
|
|
|
await handle.dispose();
|
|
|
|
}
|
|
|
|
});
|
2021-07-22 21:37:20 -07:00
|
|
|
}
|
|
|
|
|
2022-01-26 07:58:58 -08:00
|
|
|
page() {
|
|
|
|
return this._frame.page();
|
|
|
|
}
|
|
|
|
|
2021-07-22 21:37:20 -07:00
|
|
|
async boundingBox(options?: TimeoutOptions): Promise<Rect | null> {
|
2023-12-07 20:13:35 -08:00
|
|
|
return await this._withElement(h => h.boundingBox(), options?.timeout);
|
2021-07-22 21:37:20 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
async check(options: channels.ElementHandleCheckOptions = {}) {
|
2023-12-07 20:13:35 -08:00
|
|
|
return await this._frame.check(this._selector, { strict: true, ...options });
|
2021-07-22 21:37:20 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
async click(options: channels.ElementHandleClickOptions = {}): Promise<void> {
|
2023-12-07 20:13:35 -08:00
|
|
|
return await this._frame.click(this._selector, { strict: true, ...options });
|
2021-07-22 21:37:20 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
async dblclick(options: channels.ElementHandleDblclickOptions = {}): Promise<void> {
|
2023-12-07 20:13:35 -08:00
|
|
|
return await this._frame.dblclick(this._selector, { strict: true, ...options });
|
2021-07-22 21:37:20 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
async dispatchEvent(type: string, eventInit: Object = {}, options?: TimeoutOptions) {
|
2023-12-07 20:13:35 -08:00
|
|
|
return await this._frame.dispatchEvent(this._selector, type, eventInit, { strict: true, ...options });
|
2021-07-22 21:37:20 -07:00
|
|
|
}
|
|
|
|
|
2021-11-22 14:27:26 -05:00
|
|
|
async dragTo(target: Locator, options: channels.FrameDragAndDropOptions = {}) {
|
2023-12-07 20:13:35 -08:00
|
|
|
return await this._frame.dragAndDrop(this._selector, target._selector, {
|
2021-11-22 14:27:26 -05:00
|
|
|
strict: true,
|
2022-01-18 17:51:15 +01:00
|
|
|
...options,
|
2021-11-22 14:27:26 -05:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2021-07-22 21:37:20 -07:00
|
|
|
async evaluate<R, Arg>(pageFunction: structs.PageFunctionOn<SVGElement | HTMLElement, Arg, R>, arg?: Arg, options?: TimeoutOptions): Promise<R> {
|
2023-12-07 20:13:35 -08:00
|
|
|
return await this._withElement(h => h.evaluate(pageFunction, arg), options?.timeout);
|
2021-07-22 21:37:20 -07:00
|
|
|
}
|
|
|
|
|
2021-08-03 22:32:39 +02:00
|
|
|
async evaluateAll<R, Arg>(pageFunction: structs.PageFunctionOn<Element[], Arg, R>, arg?: Arg): Promise<R> {
|
2023-12-07 20:13:35 -08:00
|
|
|
return await this._frame.$$eval(this._selector, pageFunction, arg);
|
2021-07-22 21:37:20 -07:00
|
|
|
}
|
|
|
|
|
2021-08-03 22:32:39 +02:00
|
|
|
async evaluateHandle<R, Arg>(pageFunction: structs.PageFunctionOn<any, Arg, R>, arg?: Arg, options?: TimeoutOptions): Promise<structs.SmartHandle<R>> {
|
2023-12-07 20:13:35 -08:00
|
|
|
return await this._withElement(h => h.evaluateHandle(pageFunction, arg), options?.timeout);
|
2021-07-22 21:37:20 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
async fill(value: string, options: channels.ElementHandleFillOptions = {}): Promise<void> {
|
2023-12-07 20:13:35 -08:00
|
|
|
return await this._frame.fill(this._selector, value, { strict: true, ...options });
|
2021-07-22 21:37:20 -07:00
|
|
|
}
|
|
|
|
|
2022-10-25 21:56:11 +02:00
|
|
|
async clear(options: channels.ElementHandleFillOptions = {}): Promise<void> {
|
2023-12-07 20:13:35 -08:00
|
|
|
return await this.fill('', options);
|
2022-10-25 21:56:11 +02:00
|
|
|
}
|
|
|
|
|
2022-01-12 07:37:48 -08:00
|
|
|
async _highlight() {
|
2022-03-01 13:56:21 -08:00
|
|
|
// VS Code extension uses this one, keep it for now.
|
2023-12-07 20:13:35 -08:00
|
|
|
return await this._frame._highlight(this._selector);
|
2022-03-01 13:56:21 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
async highlight() {
|
2023-12-07 20:13:35 -08:00
|
|
|
return await this._frame._highlight(this._selector);
|
2022-01-12 07:37:48 -08:00
|
|
|
}
|
|
|
|
|
2023-03-03 14:50:53 -08:00
|
|
|
locator(selectorOrLocator: string | Locator, options?: LocatorOptions): Locator {
|
|
|
|
if (isString(selectorOrLocator))
|
|
|
|
return new Locator(this._frame, this._selector + ' >> ' + selectorOrLocator, options);
|
|
|
|
if (selectorOrLocator._frame !== this._frame)
|
|
|
|
throw new Error(`Locators must belong to the same frame.`);
|
2023-07-14 12:21:45 -07:00
|
|
|
return new Locator(this._frame, this._selector + ' >> internal:chain=' + JSON.stringify(selectorOrLocator._selector), options);
|
2021-12-03 09:27:06 -08:00
|
|
|
}
|
|
|
|
|
2022-12-13 08:43:13 -08:00
|
|
|
getByTestId(testId: string | RegExp): Locator {
|
2022-11-08 12:04:43 -08:00
|
|
|
return this.locator(getByTestIdSelector(testIdAttributeName(), testId));
|
2022-09-27 20:06:07 -08:00
|
|
|
}
|
|
|
|
|
2022-09-29 20:45:44 -08:00
|
|
|
getByAltText(text: string | RegExp, options?: { exact?: boolean }): Locator {
|
2022-10-05 11:00:01 -08:00
|
|
|
return this.locator(getByAltTextSelector(text, options));
|
2022-09-29 20:45:44 -08:00
|
|
|
}
|
|
|
|
|
2022-10-04 09:29:26 -08:00
|
|
|
getByLabel(text: string | RegExp, options?: { exact?: boolean }): Locator {
|
2022-10-05 11:00:01 -08:00
|
|
|
return this.locator(getByLabelSelector(text, options));
|
2022-09-29 10:06:58 -08:00
|
|
|
}
|
|
|
|
|
2022-10-04 09:29:26 -08:00
|
|
|
getByPlaceholder(text: string | RegExp, options?: { exact?: boolean }): Locator {
|
2022-10-05 11:00:01 -08:00
|
|
|
return this.locator(getByPlaceholderSelector(text, options));
|
2022-09-29 17:12:49 -08:00
|
|
|
}
|
|
|
|
|
2022-09-27 15:13:56 -08:00
|
|
|
getByText(text: string | RegExp, options?: { exact?: boolean }): Locator {
|
2022-10-05 11:00:01 -08:00
|
|
|
return this.locator(getByTextSelector(text, options));
|
2022-09-27 15:13:56 -08:00
|
|
|
}
|
|
|
|
|
2022-09-29 20:45:44 -08:00
|
|
|
getByTitle(text: string | RegExp, options?: { exact?: boolean }): Locator {
|
2022-10-05 11:00:01 -08:00
|
|
|
return this.locator(getByTitleSelector(text, options));
|
2022-09-29 20:45:44 -08:00
|
|
|
}
|
|
|
|
|
2022-09-27 15:13:56 -08:00
|
|
|
getByRole(role: string, options: ByRoleOptions = {}): Locator {
|
2022-10-05 11:00:01 -08:00
|
|
|
return this.locator(getByRoleSelector(role, options));
|
2022-09-27 15:13:56 -08:00
|
|
|
}
|
|
|
|
|
2021-11-08 09:58:24 -08:00
|
|
|
frameLocator(selector: string): FrameLocator {
|
|
|
|
return new FrameLocator(this._frame, this._selector + ' >> ' + selector);
|
|
|
|
}
|
|
|
|
|
2023-03-30 14:41:30 -07:00
|
|
|
filter(options?: LocatorOptions): Locator {
|
|
|
|
return new Locator(this._frame, this._selector, options);
|
2022-04-25 20:06:18 +01:00
|
|
|
}
|
|
|
|
|
2021-07-27 15:27:36 -07:00
|
|
|
async elementHandle(options?: TimeoutOptions): Promise<ElementHandle<SVGElement | HTMLElement>> {
|
2021-08-03 22:32:39 +02:00
|
|
|
return await this._frame.waitForSelector(this._selector, { strict: true, state: 'attached', ...options })!;
|
2021-07-22 21:37:20 -07:00
|
|
|
}
|
|
|
|
|
2021-07-27 15:27:36 -07:00
|
|
|
async elementHandles(): Promise<api.ElementHandle<SVGElement | HTMLElement>[]> {
|
2023-12-07 20:13:35 -08:00
|
|
|
return await this._frame.$$(this._selector);
|
2021-07-27 15:27:36 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
first(): Locator {
|
2021-08-11 11:06:09 -07:00
|
|
|
return new Locator(this._frame, this._selector + ' >> nth=0');
|
2021-07-27 15:58:18 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
last(): Locator {
|
2021-08-11 11:06:09 -07:00
|
|
|
return new Locator(this._frame, this._selector + ` >> nth=-1`);
|
2021-07-27 15:58:18 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
nth(index: number): Locator {
|
2021-08-11 11:06:09 -07:00
|
|
|
return new Locator(this._frame, this._selector + ` >> nth=${index}`);
|
2021-07-27 15:27:36 -07:00
|
|
|
}
|
|
|
|
|
2023-05-05 11:14:01 -07:00
|
|
|
and(locator: Locator): Locator {
|
|
|
|
if (locator._frame !== this._frame)
|
|
|
|
throw new Error(`Locators must belong to the same frame.`);
|
|
|
|
return new Locator(this._frame, this._selector + ` >> internal:and=` + JSON.stringify(locator._selector));
|
|
|
|
}
|
|
|
|
|
2023-03-22 15:28:59 -07:00
|
|
|
or(locator: Locator): Locator {
|
|
|
|
if (locator._frame !== this._frame)
|
|
|
|
throw new Error(`Locators must belong to the same frame.`);
|
|
|
|
return new Locator(this._frame, this._selector + ` >> internal:or=` + JSON.stringify(locator._selector));
|
|
|
|
}
|
|
|
|
|
2021-07-22 21:37:20 -07:00
|
|
|
async focus(options?: TimeoutOptions): Promise<void> {
|
2023-12-07 20:13:35 -08:00
|
|
|
return await this._frame.focus(this._selector, { strict: true, ...options });
|
2021-07-27 15:27:36 -07:00
|
|
|
}
|
|
|
|
|
2022-10-25 06:10:40 -07:00
|
|
|
async blur(options?: TimeoutOptions): Promise<void> {
|
|
|
|
await this._frame._channel.blur({ selector: this._selector, strict: true, ...options });
|
|
|
|
}
|
|
|
|
|
2021-07-27 15:27:36 -07:00
|
|
|
async count(): Promise<number> {
|
2023-12-07 20:13:35 -08:00
|
|
|
return await this._frame._queryCount(this._selector);
|
2021-07-22 21:37:20 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
async getAttribute(name: string, options?: TimeoutOptions): Promise<string | null> {
|
2023-12-07 20:13:35 -08:00
|
|
|
return await this._frame.getAttribute(this._selector, name, { strict: true, ...options });
|
2021-07-22 21:37:20 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
async hover(options: channels.ElementHandleHoverOptions = {}): Promise<void> {
|
2023-12-07 20:13:35 -08:00
|
|
|
return await this._frame.hover(this._selector, { strict: true, ...options });
|
2021-07-22 21:37:20 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
async innerHTML(options?: TimeoutOptions): Promise<string> {
|
2023-12-07 20:13:35 -08:00
|
|
|
return await this._frame.innerHTML(this._selector, { strict: true, ...options });
|
2021-07-22 21:37:20 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
async innerText(options?: TimeoutOptions): Promise<string> {
|
2023-12-07 20:13:35 -08:00
|
|
|
return await this._frame.innerText(this._selector, { strict: true, ...options });
|
2021-07-22 21:37:20 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
async inputValue(options?: TimeoutOptions): Promise<string> {
|
2023-12-07 20:13:35 -08:00
|
|
|
return await this._frame.inputValue(this._selector, { strict: true, ...options });
|
2021-07-22 21:37:20 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
async isChecked(options?: TimeoutOptions): Promise<boolean> {
|
2023-12-07 20:13:35 -08:00
|
|
|
return await this._frame.isChecked(this._selector, { strict: true, ...options });
|
2021-07-22 21:37:20 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
async isDisabled(options?: TimeoutOptions): Promise<boolean> {
|
2023-12-07 20:13:35 -08:00
|
|
|
return await this._frame.isDisabled(this._selector, { strict: true, ...options });
|
2021-07-22 21:37:20 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
async isEditable(options?: TimeoutOptions): Promise<boolean> {
|
2023-12-07 20:13:35 -08:00
|
|
|
return await this._frame.isEditable(this._selector, { strict: true, ...options });
|
2021-07-22 21:37:20 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
async isEnabled(options?: TimeoutOptions): Promise<boolean> {
|
2023-12-07 20:13:35 -08:00
|
|
|
return await this._frame.isEnabled(this._selector, { strict: true, ...options });
|
2021-07-22 21:37:20 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
async isHidden(options?: TimeoutOptions): Promise<boolean> {
|
2023-12-07 20:13:35 -08:00
|
|
|
return await this._frame.isHidden(this._selector, { strict: true, ...options });
|
2021-07-22 21:37:20 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
async isVisible(options?: TimeoutOptions): Promise<boolean> {
|
2023-12-07 20:13:35 -08:00
|
|
|
return await this._frame.isVisible(this._selector, { strict: true, ...options });
|
2021-07-22 21:37:20 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
async press(key: string, options: channels.ElementHandlePressOptions = {}): Promise<void> {
|
2023-12-07 20:13:35 -08:00
|
|
|
return await this._frame.press(this._selector, key, { strict: true, ...options });
|
2021-07-22 21:37:20 -07:00
|
|
|
}
|
|
|
|
|
2022-02-15 08:05:05 -07:00
|
|
|
async screenshot(options: Omit<channels.ElementHandleScreenshotOptions, 'mask'> & { path?: string, mask?: Locator[] } = {}): Promise<Buffer> {
|
2023-12-07 20:13:35 -08:00
|
|
|
return await this._withElement((h, timeout) => h.screenshot({ ...options, timeout }), options.timeout);
|
2021-07-22 21:37:20 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
async scrollIntoViewIfNeeded(options: channels.ElementHandleScrollIntoViewIfNeededOptions = {}) {
|
2023-12-07 20:13:35 -08:00
|
|
|
return await this._withElement((h, timeout) => h.scrollIntoViewIfNeeded({ ...options, timeout }), options.timeout);
|
2021-07-22 21:37:20 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
async selectOption(values: string | api.ElementHandle | SelectOption | string[] | api.ElementHandle[] | SelectOption[] | null, options: SelectOptionOptions = {}): Promise<string[]> {
|
2023-12-07 20:13:35 -08:00
|
|
|
return await this._frame.selectOption(this._selector, values, { strict: true, ...options });
|
2021-07-22 21:37:20 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
async selectText(options: channels.ElementHandleSelectTextOptions = {}): Promise<void> {
|
2023-12-07 20:13:35 -08:00
|
|
|
return await this._withElement((h, timeout) => h.selectText({ ...options, timeout }), options.timeout);
|
2021-07-22 21:37:20 -07:00
|
|
|
}
|
|
|
|
|
2021-08-29 20:00:40 -07:00
|
|
|
async setChecked(checked: boolean, options?: channels.ElementHandleCheckOptions) {
|
|
|
|
if (checked)
|
|
|
|
await this.check(options);
|
|
|
|
else
|
|
|
|
await this.uncheck(options);
|
|
|
|
}
|
|
|
|
|
2021-07-22 21:37:20 -07:00
|
|
|
async setInputFiles(files: string | FilePayload | string[] | FilePayload[], options: channels.ElementHandleSetInputFilesOptions = {}) {
|
2023-12-07 20:13:35 -08:00
|
|
|
return await this._frame.setInputFiles(this._selector, files, { strict: true, ...options });
|
2021-07-22 21:37:20 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
async tap(options: channels.ElementHandleTapOptions = {}): Promise<void> {
|
2023-12-07 20:13:35 -08:00
|
|
|
return await this._frame.tap(this._selector, { strict: true, ...options });
|
2021-07-22 21:37:20 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
async textContent(options?: TimeoutOptions): Promise<string | null> {
|
2023-12-07 20:13:35 -08:00
|
|
|
return await this._frame.textContent(this._selector, { strict: true, ...options });
|
2021-07-22 21:37:20 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
async type(text: string, options: channels.ElementHandleTypeOptions = {}): Promise<void> {
|
2023-12-07 20:13:35 -08:00
|
|
|
return await this._frame.type(this._selector, text, { strict: true, ...options });
|
2021-07-22 21:37:20 -07:00
|
|
|
}
|
|
|
|
|
2023-08-22 15:21:00 -07:00
|
|
|
async pressSequentially(text: string, options: channels.ElementHandleTypeOptions = {}): Promise<void> {
|
2023-12-07 20:13:35 -08:00
|
|
|
return await this.type(text, options);
|
2023-08-22 15:21:00 -07:00
|
|
|
}
|
|
|
|
|
2021-07-22 21:37:20 -07:00
|
|
|
async uncheck(options: channels.ElementHandleUncheckOptions = {}) {
|
2023-12-07 20:13:35 -08:00
|
|
|
return await this._frame.uncheck(this._selector, { strict: true, ...options });
|
2021-07-22 21:37:20 -07:00
|
|
|
}
|
|
|
|
|
2022-12-14 16:42:50 -08:00
|
|
|
async all(): Promise<Locator[]> {
|
|
|
|
return new Array(await this.count()).fill(0).map((e, i) => this.nth(i));
|
|
|
|
}
|
|
|
|
|
2021-07-29 14:09:35 -07:00
|
|
|
async allInnerTexts(): Promise<string[]> {
|
2023-12-07 20:13:35 -08:00
|
|
|
return await this._frame.$$eval(this._selector, ee => ee.map(e => (e as HTMLElement).innerText));
|
2021-07-29 14:09:35 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
async allTextContents(): Promise<string[]> {
|
2023-12-07 20:13:35 -08:00
|
|
|
return await this._frame.$$eval(this._selector, ee => ee.map(e => e.textContent || ''));
|
2021-07-29 14:09:35 -07:00
|
|
|
}
|
|
|
|
|
2021-09-28 13:57:11 -07:00
|
|
|
waitFor(options: channels.FrameWaitForSelectorOptions & { state: 'attached' | 'visible' }): Promise<void>;
|
|
|
|
waitFor(options?: channels.FrameWaitForSelectorOptions): Promise<void>;
|
|
|
|
async waitFor(options?: channels.FrameWaitForSelectorOptions): Promise<void> {
|
2021-11-19 16:28:11 -08:00
|
|
|
await this._frame._channel.waitForSelector({ selector: this._selector, strict: true, omitReturnValue: true, ...options });
|
2021-09-28 13:57:11 -07:00
|
|
|
}
|
|
|
|
|
2023-02-21 14:15:11 -08:00
|
|
|
async _expect(expression: string, options: Omit<FrameExpectOptions, 'expectedValue'> & { expectedValue?: any }): Promise<{ matches: boolean, received?: any, log?: string[], timedOut?: boolean }> {
|
|
|
|
const params: channels.FrameExpectParams = { selector: this._selector, expression, ...options, isNot: !!options.isNot };
|
|
|
|
params.expectedValue = serializeArgument(options.expectedValue);
|
|
|
|
const result = (await this._frame._channel.expect(params));
|
|
|
|
if (result.received !== undefined)
|
|
|
|
result.received = parseResult(result.received);
|
|
|
|
return result;
|
2021-09-23 16:46:46 -07:00
|
|
|
}
|
|
|
|
|
2021-11-16 15:42:35 -08:00
|
|
|
[util.inspect.custom]() {
|
2021-07-22 21:37:20 -07:00
|
|
|
return this.toString();
|
|
|
|
}
|
|
|
|
|
|
|
|
toString() {
|
2023-11-16 16:31:34 -08:00
|
|
|
return asLocator('javascript', this._selector);
|
2021-07-22 21:37:20 -07:00
|
|
|
}
|
|
|
|
}
|
2021-11-08 09:58:24 -08:00
|
|
|
|
|
|
|
export class FrameLocator implements api.FrameLocator {
|
|
|
|
private _frame: Frame;
|
2021-11-09 14:14:20 -08:00
|
|
|
private _frameSelector: string;
|
2021-11-08 09:58:24 -08:00
|
|
|
|
|
|
|
constructor(frame: Frame, selector: string) {
|
|
|
|
this._frame = frame;
|
2021-11-09 14:14:20 -08:00
|
|
|
this._frameSelector = selector;
|
2021-11-08 09:58:24 -08:00
|
|
|
}
|
|
|
|
|
2023-03-03 14:50:53 -08:00
|
|
|
locator(selectorOrLocator: string | Locator, options?: LocatorOptions): Locator {
|
|
|
|
if (isString(selectorOrLocator))
|
|
|
|
return new Locator(this._frame, this._frameSelector + ' >> internal:control=enter-frame >> ' + selectorOrLocator, options);
|
|
|
|
if (selectorOrLocator._frame !== this._frame)
|
|
|
|
throw new Error(`Locators must belong to the same frame.`);
|
|
|
|
return new Locator(this._frame, this._frameSelector + ' >> internal:control=enter-frame >> ' + selectorOrLocator._selector, options);
|
2021-11-08 09:58:24 -08:00
|
|
|
}
|
|
|
|
|
2022-12-13 08:43:13 -08:00
|
|
|
getByTestId(testId: string | RegExp): Locator {
|
2022-11-08 12:04:43 -08:00
|
|
|
return this.locator(getByTestIdSelector(testIdAttributeName(), testId));
|
2022-09-27 20:06:07 -08:00
|
|
|
}
|
|
|
|
|
2022-09-29 20:45:44 -08:00
|
|
|
getByAltText(text: string | RegExp, options?: { exact?: boolean }): Locator {
|
2022-10-05 11:00:01 -08:00
|
|
|
return this.locator(getByAltTextSelector(text, options));
|
2022-09-29 20:45:44 -08:00
|
|
|
}
|
|
|
|
|
2022-10-04 09:29:26 -08:00
|
|
|
getByLabel(text: string | RegExp, options?: { exact?: boolean }): Locator {
|
2022-10-05 11:00:01 -08:00
|
|
|
return this.locator(getByLabelSelector(text, options));
|
2022-09-29 10:06:58 -08:00
|
|
|
}
|
2022-09-29 17:12:49 -08:00
|
|
|
|
2022-10-04 09:29:26 -08:00
|
|
|
getByPlaceholder(text: string | RegExp, options?: { exact?: boolean }): Locator {
|
2022-10-05 11:00:01 -08:00
|
|
|
return this.locator(getByPlaceholderSelector(text, options));
|
2022-09-29 17:12:49 -08:00
|
|
|
}
|
|
|
|
|
2022-09-27 15:13:56 -08:00
|
|
|
getByText(text: string | RegExp, options?: { exact?: boolean }): Locator {
|
2022-10-05 11:00:01 -08:00
|
|
|
return this.locator(getByTextSelector(text, options));
|
2022-09-27 15:13:56 -08:00
|
|
|
}
|
|
|
|
|
2022-09-29 20:45:44 -08:00
|
|
|
getByTitle(text: string | RegExp, options?: { exact?: boolean }): Locator {
|
2022-10-05 11:00:01 -08:00
|
|
|
return this.locator(getByTitleSelector(text, options));
|
2022-09-29 20:45:44 -08:00
|
|
|
}
|
|
|
|
|
2022-09-27 15:13:56 -08:00
|
|
|
getByRole(role: string, options: ByRoleOptions = {}): Locator {
|
2022-10-05 11:00:01 -08:00
|
|
|
return this.locator(getByRoleSelector(role, options));
|
2022-09-27 15:13:56 -08:00
|
|
|
}
|
|
|
|
|
2021-11-08 09:58:24 -08:00
|
|
|
frameLocator(selector: string): FrameLocator {
|
2022-10-05 08:45:10 -07:00
|
|
|
return new FrameLocator(this._frame, this._frameSelector + ' >> internal:control=enter-frame >> ' + selector);
|
2021-11-09 14:14:20 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
first(): FrameLocator {
|
|
|
|
return new FrameLocator(this._frame, this._frameSelector + ' >> nth=0');
|
|
|
|
}
|
|
|
|
|
|
|
|
last(): FrameLocator {
|
|
|
|
return new FrameLocator(this._frame, this._frameSelector + ` >> nth=-1`);
|
|
|
|
}
|
|
|
|
|
|
|
|
nth(index: number): FrameLocator {
|
|
|
|
return new FrameLocator(this._frame, this._frameSelector + ` >> nth=${index}`);
|
2021-11-08 09:58:24 -08:00
|
|
|
}
|
|
|
|
}
|
2022-10-05 11:00:01 -08:00
|
|
|
|
2022-11-08 12:04:43 -08:00
|
|
|
let _testIdAttributeName: string = 'data-testid';
|
|
|
|
|
|
|
|
export function testIdAttributeName(): string {
|
|
|
|
return _testIdAttributeName;
|
|
|
|
}
|
2022-10-05 11:00:01 -08:00
|
|
|
|
|
|
|
export function setTestIdAttribute(attributeName: string) {
|
2022-11-08 12:04:43 -08:00
|
|
|
_testIdAttributeName = attributeName;
|
2022-10-05 11:00:01 -08:00
|
|
|
}
|