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) | |
|
||||
| [ChromeReleaseChannel](./puppeteer.chromereleasechannel.md) | |
|
||||
| [ConsoleMessageType](./puppeteer.consolemessagetype.md) | The supported types for console messages. |
|
||||
| [ElementFor](./puppeteer.elementfor.md) | |
|
||||
| [ErrorCode](./puppeteer.errorcode.md) | |
|
||||
| [EvaluateFunc](./puppeteer.evaluatefunc.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. |
|
||||
| [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. |
|
||||
| [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> |
|
||||
| [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> |
|
||||
|
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
|
||||
export declare type NodeFor<ComplexSelector extends string> =
|
||||
TypeSelectorOfComplexSelector<ComplexSelector> extends infer TypeSelector
|
||||
? TypeSelector extends keyof HTMLElementTagNameMap
|
||||
? HTMLElementTagNameMap[TypeSelector]
|
||||
: TypeSelector extends keyof SVGElementTagNameMap
|
||||
? SVGElementTagNameMap[TypeSelector]
|
||||
? TypeSelector extends
|
||||
| keyof HTMLElementTagNameMap
|
||||
| keyof SVGElementTagNameMap
|
||||
? ElementFor<TypeSelector>
|
||||
: Element
|
||||
: never;
|
||||
```
|
||||
|
||||
**References:** [ElementFor](./puppeteer.elementfor.md)
|
||||
|
@ -31,7 +31,7 @@ import {
|
||||
} from './JSHandle.js';
|
||||
import {Page, ScreenshotOptions} from '../api/Page.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 {debugError, isString} from './util.js';
|
||||
import {CDPPage} from './Page.js';
|
||||
@ -412,6 +412,37 @@ export class ElementHandle<
|
||||
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 {
|
||||
return this;
|
||||
}
|
||||
|
@ -57,6 +57,17 @@ export type InnerParams<T extends unknown[]> = {
|
||||
[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
|
||||
*/
|
||||
@ -69,10 +80,10 @@ export type EvaluateFunc<T extends unknown[]> = (
|
||||
*/
|
||||
export type NodeFor<ComplexSelector extends string> =
|
||||
TypeSelectorOfComplexSelector<ComplexSelector> extends infer TypeSelector
|
||||
? TypeSelector extends keyof HTMLElementTagNameMap
|
||||
? HTMLElementTagNameMap[TypeSelector]
|
||||
: TypeSelector extends keyof SVGElementTagNameMap
|
||||
? SVGElementTagNameMap[TypeSelector]
|
||||
? TypeSelector extends
|
||||
| keyof HTMLElementTagNameMap
|
||||
| keyof SVGElementTagNameMap
|
||||
? ElementFor<TypeSelector>
|
||||
: Element
|
||||
: never;
|
||||
|
||||
|
@ -605,4 +605,14 @@ describe('ElementHandle specs', function () {
|
||||
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