mirror of
https://github.com/puppeteer/puppeteer
synced 2024-06-14 14:02:48 +00:00
feat: add element validation (#9352)
This PR adds a method to ElementHandle that validates the tag type of that handle and returns it. Fixed: #8579, #9280
This commit is contained in:
parent
e3e9cc622a
commit
c7a063a152
@ -146,6 +146,7 @@ sidebar_label: API
|
|||||||
| [Awaitable](./puppeteer.awaitable.md) | |
|
| [Awaitable](./puppeteer.awaitable.md) | |
|
||||||
| [ChromeReleaseChannel](./puppeteer.chromereleasechannel.md) | |
|
| [ChromeReleaseChannel](./puppeteer.chromereleasechannel.md) | |
|
||||||
| [ConsoleMessageType](./puppeteer.consolemessagetype.md) | The supported types for console messages. |
|
| [ConsoleMessageType](./puppeteer.consolemessagetype.md) | The supported types for console messages. |
|
||||||
|
| [ElementFor](./puppeteer.elementfor.md) | |
|
||||||
| [ErrorCode](./puppeteer.errorcode.md) | |
|
| [ErrorCode](./puppeteer.errorcode.md) | |
|
||||||
| [EvaluateFunc](./puppeteer.evaluatefunc.md) | |
|
| [EvaluateFunc](./puppeteer.evaluatefunc.md) | |
|
||||||
| [EventType](./puppeteer.eventtype.md) | |
|
| [EventType](./puppeteer.eventtype.md) | |
|
||||||
|
17
docs/api/puppeteer.elementfor.md
Normal file
17
docs/api/puppeteer.elementfor.md
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
---
|
||||||
|
sidebar_label: ElementFor
|
||||||
|
---
|
||||||
|
|
||||||
|
# ElementFor type
|
||||||
|
|
||||||
|
#### Signature:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
export declare type ElementFor<
|
||||||
|
TagName extends keyof HTMLElementTagNameMap | keyof SVGElementTagNameMap
|
||||||
|
> = TagName extends keyof HTMLElementTagNameMap
|
||||||
|
? HTMLElementTagNameMap[TagName]
|
||||||
|
: TagName extends keyof SVGElementTagNameMap
|
||||||
|
? SVGElementTagNameMap[TagName]
|
||||||
|
: never;
|
||||||
|
```
|
@ -72,6 +72,7 @@ The constructor for this class is marked as internal. Third-party code should no
|
|||||||
| [screenshot(this, options)](./puppeteer.elementhandle.screenshot.md) | | This method scrolls element into view if needed, and then uses [Page.screenshot()](./puppeteer.page.screenshot.md) to take a screenshot of the element. If the element is detached from DOM, the method throws an error. |
|
| [screenshot(this, options)](./puppeteer.elementhandle.screenshot.md) | | This method scrolls element into view if needed, and then uses [Page.screenshot()](./puppeteer.page.screenshot.md) to take a screenshot of the element. If the element is detached from DOM, the method throws an error. |
|
||||||
| [select(values)](./puppeteer.elementhandle.select.md) | | Triggers a <code>change</code> and <code>input</code> event once all the provided options have been selected. If there's no <code><select></code> element matching <code>selector</code>, the method throws an error. |
|
| [select(values)](./puppeteer.elementhandle.select.md) | | Triggers a <code>change</code> and <code>input</code> event once all the provided options have been selected. If there's no <code><select></code> element matching <code>selector</code>, the method throws an error. |
|
||||||
| [tap(this)](./puppeteer.elementhandle.tap.md) | | This method scrolls element into view if needed, and then uses [Touchscreen.tap()](./puppeteer.touchscreen.tap.md) to tap in the center of the element. If the element is detached from DOM, the method throws an error. |
|
| [tap(this)](./puppeteer.elementhandle.tap.md) | | This method scrolls element into view if needed, and then uses [Touchscreen.tap()](./puppeteer.touchscreen.tap.md) to tap in the center of the element. If the element is detached from DOM, the method throws an error. |
|
||||||
|
| [toElement(tagName)](./puppeteer.elementhandle.toelement.md) | | Converts the current handle to the given element type. |
|
||||||
| [type(text, options)](./puppeteer.elementhandle.type.md) | | <p>Focuses the element, and then sends a <code>keydown</code>, <code>keypress</code>/<code>input</code>, and <code>keyup</code> event for each character in the text.</p><p>To press a special key, like <code>Control</code> or <code>ArrowDown</code>, use [ElementHandle.press()](./puppeteer.elementhandle.press.md).</p> |
|
| [type(text, options)](./puppeteer.elementhandle.type.md) | | <p>Focuses the element, and then sends a <code>keydown</code>, <code>keypress</code>/<code>input</code>, and <code>keyup</code> event for each character in the text.</p><p>To press a special key, like <code>Control</code> or <code>ArrowDown</code>, use [ElementHandle.press()](./puppeteer.elementhandle.press.md).</p> |
|
||||||
| [uploadFile(this, filePaths)](./puppeteer.elementhandle.uploadfile.md) | | This method expects <code>elementHandle</code> to point to an [input element](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input). |
|
| [uploadFile(this, filePaths)](./puppeteer.elementhandle.uploadfile.md) | | This method expects <code>elementHandle</code> to point to an [input element](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input). |
|
||||||
| [waitForSelector(selector, options)](./puppeteer.elementhandle.waitforselector.md) | | <p>Wait for an element matching the given selector to appear in the current element.</p><p>Unlike [Frame.waitForSelector()](./puppeteer.frame.waitforselector.md), this method does not work across navigations or if the element is detached from DOM.</p> |
|
| [waitForSelector(selector, options)](./puppeteer.elementhandle.waitforselector.md) | | <p>Wait for an element matching the given selector to appear in the current element.</p><p>Unlike [Frame.waitForSelector()](./puppeteer.frame.waitforselector.md), this method does not work across navigations or if the element is detached from DOM.</p> |
|
||||||
|
39
docs/api/puppeteer.elementhandle.toelement.md
Normal file
39
docs/api/puppeteer.elementhandle.toelement.md
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
---
|
||||||
|
sidebar_label: ElementHandle.toElement
|
||||||
|
---
|
||||||
|
|
||||||
|
# ElementHandle.toElement() method
|
||||||
|
|
||||||
|
Converts the current handle to the given element type.
|
||||||
|
|
||||||
|
#### Signature:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
class ElementHandle {
|
||||||
|
toElement<K extends keyof HTMLElementTagNameMap | keyof SVGElementTagNameMap>(
|
||||||
|
tagName: K
|
||||||
|
): Promise<HandleFor<ElementFor<K>>>;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Parameters
|
||||||
|
|
||||||
|
| Parameter | Type | Description |
|
||||||
|
| --------- | ---- | ----------------------------------------- |
|
||||||
|
| tagName | K | The tag name of the desired element type. |
|
||||||
|
|
||||||
|
**Returns:**
|
||||||
|
|
||||||
|
Promise<[HandleFor](./puppeteer.handlefor.md)<[ElementFor](./puppeteer.elementfor.md)<K>>>
|
||||||
|
|
||||||
|
## Exceptions
|
||||||
|
|
||||||
|
An error if the handle does not match. **The handle will not be automatically disposed.**
|
||||||
|
|
||||||
|
## Example
|
||||||
|
|
||||||
|
```ts
|
||||||
|
const element: ElementHandle<Element> = await page.$('.class-name-of-anchor');
|
||||||
|
// DO NOT DISPOSE `element`, this will be always be the same handle.
|
||||||
|
const anchor: ElementHandle<HTMLAnchorElement> = await element.toElement('a');
|
||||||
|
```
|
@ -9,10 +9,12 @@ sidebar_label: NodeFor
|
|||||||
```typescript
|
```typescript
|
||||||
export declare type NodeFor<ComplexSelector extends string> =
|
export declare type NodeFor<ComplexSelector extends string> =
|
||||||
TypeSelectorOfComplexSelector<ComplexSelector> extends infer TypeSelector
|
TypeSelectorOfComplexSelector<ComplexSelector> extends infer TypeSelector
|
||||||
? TypeSelector extends keyof HTMLElementTagNameMap
|
? TypeSelector extends
|
||||||
? HTMLElementTagNameMap[TypeSelector]
|
| keyof HTMLElementTagNameMap
|
||||||
: TypeSelector extends keyof SVGElementTagNameMap
|
| keyof SVGElementTagNameMap
|
||||||
? SVGElementTagNameMap[TypeSelector]
|
? ElementFor<TypeSelector>
|
||||||
: Element
|
: Element
|
||||||
: never;
|
: never;
|
||||||
```
|
```
|
||||||
|
|
||||||
|
**References:** [ElementFor](./puppeteer.elementfor.md)
|
||||||
|
@ -31,7 +31,7 @@ import {
|
|||||||
} from './JSHandle.js';
|
} from './JSHandle.js';
|
||||||
import {Page, ScreenshotOptions} from '../api/Page.js';
|
import {Page, ScreenshotOptions} from '../api/Page.js';
|
||||||
import {getQueryHandlerAndSelector} from './QueryHandler.js';
|
import {getQueryHandlerAndSelector} from './QueryHandler.js';
|
||||||
import {EvaluateFunc, HandleFor, NodeFor} from './types.js';
|
import {ElementFor, EvaluateFunc, HandleFor, NodeFor} from './types.js';
|
||||||
import {KeyInput} from './USKeyboardLayout.js';
|
import {KeyInput} from './USKeyboardLayout.js';
|
||||||
import {debugError, isString} from './util.js';
|
import {debugError, isString} from './util.js';
|
||||||
import {CDPPage} from './Page.js';
|
import {CDPPage} from './Page.js';
|
||||||
@ -412,6 +412,37 @@ export class ElementHandle<
|
|||||||
return this.waitForSelector(`xpath/${xpath}`, options);
|
return this.waitForSelector(`xpath/${xpath}`, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts the current handle to the given element type.
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
*
|
||||||
|
* ```ts
|
||||||
|
* const element: ElementHandle<Element> = await page.$(
|
||||||
|
* '.class-name-of-anchor'
|
||||||
|
* );
|
||||||
|
* // DO NOT DISPOSE `element`, this will be always be the same handle.
|
||||||
|
* const anchor: ElementHandle<HTMLAnchorElement> = await element.toElement(
|
||||||
|
* 'a'
|
||||||
|
* );
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* @param tagName - The tag name of the desired element type.
|
||||||
|
* @throws An error if the handle does not match. **The handle will not be
|
||||||
|
* automatically disposed.**
|
||||||
|
*/
|
||||||
|
async toElement<
|
||||||
|
K extends keyof HTMLElementTagNameMap | keyof SVGElementTagNameMap
|
||||||
|
>(tagName: K): Promise<HandleFor<ElementFor<K>>> {
|
||||||
|
const isMatchingTagName = await this.evaluate((node, tagName) => {
|
||||||
|
return node.nodeName === tagName.toUpperCase();
|
||||||
|
}, tagName);
|
||||||
|
if (!isMatchingTagName) {
|
||||||
|
throw new Error(`Element is not a(n) \`${tagName}\` element`);
|
||||||
|
}
|
||||||
|
return this as unknown as HandleFor<ElementFor<K>>;
|
||||||
|
}
|
||||||
|
|
||||||
override asElement(): ElementHandle<ElementType> | null {
|
override asElement(): ElementHandle<ElementType> | null {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
@ -57,6 +57,17 @@ export type InnerParams<T extends unknown[]> = {
|
|||||||
[K in keyof T]: FlattenHandle<T[K]>;
|
[K in keyof T]: FlattenHandle<T[K]>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
export type ElementFor<
|
||||||
|
TagName extends keyof HTMLElementTagNameMap | keyof SVGElementTagNameMap
|
||||||
|
> = TagName extends keyof HTMLElementTagNameMap
|
||||||
|
? HTMLElementTagNameMap[TagName]
|
||||||
|
: TagName extends keyof SVGElementTagNameMap
|
||||||
|
? SVGElementTagNameMap[TagName]
|
||||||
|
: never;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @public
|
* @public
|
||||||
*/
|
*/
|
||||||
@ -69,10 +80,10 @@ export type EvaluateFunc<T extends unknown[]> = (
|
|||||||
*/
|
*/
|
||||||
export type NodeFor<ComplexSelector extends string> =
|
export type NodeFor<ComplexSelector extends string> =
|
||||||
TypeSelectorOfComplexSelector<ComplexSelector> extends infer TypeSelector
|
TypeSelectorOfComplexSelector<ComplexSelector> extends infer TypeSelector
|
||||||
? TypeSelector extends keyof HTMLElementTagNameMap
|
? TypeSelector extends
|
||||||
? HTMLElementTagNameMap[TypeSelector]
|
| keyof HTMLElementTagNameMap
|
||||||
: TypeSelector extends keyof SVGElementTagNameMap
|
| keyof SVGElementTagNameMap
|
||||||
? SVGElementTagNameMap[TypeSelector]
|
? ElementFor<TypeSelector>
|
||||||
: Element
|
: Element
|
||||||
: never;
|
: never;
|
||||||
|
|
||||||
|
@ -605,4 +605,14 @@ describe('ElementHandle specs', function () {
|
|||||||
expect(txtContents).toBe('textcontent');
|
expect(txtContents).toBe('textcontent');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('Element.toElement', () => {
|
||||||
|
it('should work', async () => {
|
||||||
|
const {page} = getTestState();
|
||||||
|
await page.setContent('<div class="foo">Foo1</div>');
|
||||||
|
const element = await page.$('.foo');
|
||||||
|
const div = await element?.toElement('div');
|
||||||
|
expect(div).toBeDefined();
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
Loading…
Reference in New Issue
Block a user