feat(puppeteer-core): Infer element type from complex selector (#9253)

**What kind of change does this PR introduce?**

Better type inference.

**Did you add tests for your changes?**

~Not yet.~ Yes.

**If relevant, did you update the documentation?**

Not yet.

**Summary**

<!-- Explain the **motivation** for making this change. What existing
problem does the pull request solve? -->
<!-- Try to link to an open issue for more information. -->
Currently methods that return an element handle, i.e. `.$`,
`.waitForSelector` attempt to infer the node element type from the
selector string. However, this only works when the selector is an exact
match of the element tag, i.e. a selector `"a"` would be inferred as
`HTMLAnchorElement` . And not when the selector is complex, i.e.
selectors `"a#some-id"`, `div > a`, `a:nth-child(2)` would all fallback
on `Element`.

This is due to simply looking up the the selector in
`HTMLElementTagNameMap` and `SVGElementTagNameMap` without any attempt
to parse the selector string.

This PR is an attempt to do so.

**Does this PR introduce a breaking change?**

<!-- If this PR introduces a breaking change, please describe the impact
and a migration path for existing applications. -->
This could break existing incorrect assertions using the `as` keyword.

**Other information**

~This PR introduces a dependency on the `type-fest` package.~

This PR is far from complete (no tests, no docs). Put out early for
feedback and discussion.

Co-authored-by: Alex Rudenko <OrKoN@users.noreply.github.com>
This commit is contained in:
Joe Gomain Hoyes 2022-11-23 16:59:23 +07:00 committed by GitHub
parent 16784fc8cb
commit bef1061c06
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 1125 additions and 22 deletions

View File

@ -7,10 +7,12 @@ sidebar_label: NodeFor
#### Signature: #### Signature:
```typescript ```typescript
export declare type NodeFor<Selector extends string> = export declare type NodeFor<ComplexSelector extends string> =
Selector extends keyof HTMLElementTagNameMap TypeSelectorOfComplexSelector<ComplexSelector> extends infer TypeSelector
? HTMLElementTagNameMap[Selector] ? TypeSelector extends keyof HTMLElementTagNameMap
: Selector extends keyof SVGElementTagNameMap ? HTMLElementTagNameMap[TypeSelector]
? SVGElementTagNameMap[Selector] : TypeSelector extends keyof SVGElementTagNameMap
: Element; ? SVGElementTagNameMap[TypeSelector]
: Element
: never;
``` ```

View File

@ -67,9 +67,100 @@ export type EvaluateFunc<T extends unknown[]> = (
/** /**
* @public * @public
*/ */
export type NodeFor<Selector extends string> = export type NodeFor<ComplexSelector extends string> =
Selector extends keyof HTMLElementTagNameMap TypeSelectorOfComplexSelector<ComplexSelector> extends infer TypeSelector
? HTMLElementTagNameMap[Selector] ? TypeSelector extends keyof HTMLElementTagNameMap
: Selector extends keyof SVGElementTagNameMap ? HTMLElementTagNameMap[TypeSelector]
? SVGElementTagNameMap[Selector] : TypeSelector extends keyof SVGElementTagNameMap
: Element; ? SVGElementTagNameMap[TypeSelector]
: Element
: never;
type TypeSelectorOfComplexSelector<ComplexSelector extends string> =
CompoundSelectorsOfComplexSelector<ComplexSelector> extends infer CompoundSelectors
? CompoundSelectors extends NonEmptyReadonlyArray<string>
? Last<CompoundSelectors> extends infer LastCompoundSelector
? LastCompoundSelector extends string
? TypeSelectorOfCompoundSelector<LastCompoundSelector>
: never
: never
: unknown
: never;
type TypeSelectorOfCompoundSelector<CompoundSelector extends string> =
SplitWithDelemiters<
CompoundSelector,
BeginSubclassSelectorTokens
> extends infer CompoundSelectorTokens
? CompoundSelectorTokens extends [infer TypeSelector, ...any[]]
? TypeSelector extends ''
? unknown
: TypeSelector
: never
: never;
type Last<Arr extends NonEmptyReadonlyArray<unknown>> = Arr extends [
infer Head,
...infer Tail
]
? Tail extends NonEmptyReadonlyArray<unknown>
? Last<Tail>
: Head
: never;
type NonEmptyReadonlyArray<T> = [T, ...(readonly T[])];
type CompoundSelectorsOfComplexSelector<ComplexSelector extends string> =
SplitWithDelemiters<
ComplexSelector,
CombinatorTokens
> extends infer IntermediateTokens
? IntermediateTokens extends readonly string[]
? Drop<IntermediateTokens, ''>
: never
: never;
type SplitWithDelemiters<
Input extends string,
Delemiters extends readonly string[]
> = Delemiters extends [infer FirstDelemiter, ...infer RestDelemiters]
? FirstDelemiter extends string
? RestDelemiters extends readonly string[]
? FlatmapSplitWithDelemiters<Split<Input, FirstDelemiter>, RestDelemiters>
: never
: never
: [Input];
type BeginSubclassSelectorTokens = ['.', '#', '[', ':'];
type CombinatorTokens = [' ', '>', '+', '~', '|', '|'];
type Drop<Arr extends readonly unknown[], Remove> = Arr extends [
infer Head,
...infer Tail
]
? Head extends Remove
? Drop<Tail, Remove>
: [Head, ...Drop<Tail, Remove>]
: [];
type FlatmapSplitWithDelemiters<
Inputs extends readonly string[],
Delemiters extends readonly string[]
> = Inputs extends [infer FirstInput, ...infer RestInputs]
? FirstInput extends string
? RestInputs extends readonly string[]
? [
...SplitWithDelemiters<FirstInput, Delemiters>,
...FlatmapSplitWithDelemiters<RestInputs, Delemiters>
]
: never
: never
: [];
type Split<
Input extends string,
Delemiter extends string
> = Input extends `${infer Prefix}${Delemiter}${infer Suffix}`
? [Prefix, ...Split<Suffix, Delemiter>]
: [Input];

View File

@ -4,31 +4,897 @@ import {expectNotType, expectType} from 'tsd';
declare const handle: ElementHandle; declare const handle: ElementHandle;
{ {
{
{ {
expectType<ElementHandle<HTMLAnchorElement> | null>(await handle.$('a')); expectType<ElementHandle<HTMLAnchorElement> | null>(await handle.$('a'));
expectNotType<ElementHandle<Element> | null>(await handle.$('a')); expectNotType<ElementHandle<Element> | null>(await handle.$('a'));
} }
{
expectType<ElementHandle<HTMLAnchorElement> | null>(
await handle.$('a#id')
);
expectNotType<ElementHandle<Element> | null>(await handle.$('a#id'));
}
{
expectType<ElementHandle<HTMLAnchorElement> | null>(
await handle.$('a.class')
);
expectNotType<ElementHandle<Element> | null>(await handle.$('a.class'));
}
{
expectType<ElementHandle<HTMLAnchorElement> | null>(
await handle.$('a[attr=value]')
);
expectNotType<ElementHandle<Element> | null>(
await handle.$('a[attr=value]')
);
}
{
expectType<ElementHandle<HTMLAnchorElement> | null>(
await handle.$('a:psuedo-class')
);
expectNotType<ElementHandle<Element> | null>(
await handle.$('a:pseudo-class')
);
}
{
expectType<ElementHandle<HTMLAnchorElement> | null>(
await handle.$('a:func(arg)')
);
expectNotType<ElementHandle<Element> | null>(
await handle.$('a:func(arg)')
);
}
}
{
{ {
expectType<ElementHandle<HTMLDivElement> | null>(await handle.$('div')); expectType<ElementHandle<HTMLDivElement> | null>(await handle.$('div'));
expectNotType<ElementHandle<Element> | null>(await handle.$('div')); expectNotType<ElementHandle<Element> | null>(await handle.$('div'));
} }
{
expectType<ElementHandle<HTMLDivElement> | null>(
await handle.$('div#id')
);
expectNotType<ElementHandle<Element> | null>(await handle.$('div#id'));
}
{
expectType<ElementHandle<HTMLDivElement> | null>(
await handle.$('div.class')
);
expectNotType<ElementHandle<Element> | null>(await handle.$('div.class'));
}
{
expectType<ElementHandle<HTMLDivElement> | null>(
await handle.$('div[attr=value]')
);
expectNotType<ElementHandle<Element> | null>(
await handle.$('div[attr=value]')
);
}
{
expectType<ElementHandle<HTMLDivElement> | null>(
await handle.$('div:psuedo-class')
);
expectNotType<ElementHandle<Element> | null>(
await handle.$('div:pseudo-class')
);
}
{
expectType<ElementHandle<HTMLDivElement> | null>(
await handle.$('div:func(arg)')
);
expectNotType<ElementHandle<Element> | null>(
await handle.$('div:func(arg)')
);
}
}
{
{ {
expectType<ElementHandle<Element> | null>(await handle.$('some-custom')); expectType<ElementHandle<Element> | null>(await handle.$('some-custom'));
} }
{
expectType<ElementHandle<Element> | null>(
await handle.$('some-custom#id')
);
}
{
expectType<ElementHandle<Element> | null>(
await handle.$('some-custom.class')
);
}
{
expectType<ElementHandle<Element> | null>(
await handle.$('some-custom[attr=value]')
);
}
{
expectType<ElementHandle<Element> | null>(
await handle.$('some-custom:pseudo-class')
);
}
{
expectType<ElementHandle<Element> | null>(
await handle.$('some-custom:func(arg)')
);
}
}
{
{
expectType<ElementHandle<Element> | null>(await handle.$(''));
}
{
expectType<ElementHandle<Element> | null>(await handle.$('#id'));
}
{
expectType<ElementHandle<Element> | null>(await handle.$('.class'));
}
{
expectType<ElementHandle<Element> | null>(await handle.$('[attr=value]'));
}
{
expectType<ElementHandle<Element> | null>(
await handle.$(':pseudo-class')
);
}
{
expectType<ElementHandle<Element> | null>(await handle.$(':func(arg)'));
}
}
{
{
expectType<ElementHandle<HTMLAnchorElement> | null>(
await handle.$('div > a')
);
expectNotType<ElementHandle<Element> | null>(await handle.$('div > a'));
}
{
expectType<ElementHandle<HTMLAnchorElement> | null>(
await handle.$('div > a#id')
);
expectNotType<ElementHandle<Element> | null>(
await handle.$('div > a#id')
);
}
{
expectType<ElementHandle<HTMLAnchorElement> | null>(
await handle.$('div > a.class')
);
expectNotType<ElementHandle<Element> | null>(
await handle.$('div > a.class')
);
}
{
expectType<ElementHandle<HTMLAnchorElement> | null>(
await handle.$('div > a[attr=value]')
);
expectNotType<ElementHandle<Element> | null>(
await handle.$('div > a[attr=value]')
);
}
{
expectType<ElementHandle<HTMLAnchorElement> | null>(
await handle.$('div > a:psuedo-class')
);
expectNotType<ElementHandle<Element> | null>(
await handle.$('div > a:pseudo-class')
);
}
{
expectType<ElementHandle<HTMLAnchorElement> | null>(
await handle.$('div > a:func(arg)')
);
expectNotType<ElementHandle<Element> | null>(
await handle.$('div > a:func(arg)')
);
}
}
{
{
expectType<ElementHandle<HTMLDivElement> | null>(
await handle.$('div > div')
);
expectNotType<ElementHandle<Element> | null>(await handle.$('div > div'));
}
{
expectType<ElementHandle<HTMLDivElement> | null>(
await handle.$('div > div#id')
);
expectNotType<ElementHandle<Element> | null>(
await handle.$('div > div#id')
);
}
{
expectType<ElementHandle<HTMLDivElement> | null>(
await handle.$('div > div.class')
);
expectNotType<ElementHandle<Element> | null>(
await handle.$('div > div.class')
);
}
{
expectType<ElementHandle<HTMLDivElement> | null>(
await handle.$('div > div[attr=value]')
);
expectNotType<ElementHandle<Element> | null>(
await handle.$('div > div[attr=value]')
);
}
{
expectType<ElementHandle<HTMLDivElement> | null>(
await handle.$('div > div:psuedo-class')
);
expectNotType<ElementHandle<Element> | null>(
await handle.$('div > div:pseudo-class')
);
}
{
expectType<ElementHandle<HTMLDivElement> | null>(
await handle.$('div > div:func(arg)')
);
expectNotType<ElementHandle<Element> | null>(
await handle.$('div > div:func(arg)')
);
}
}
{
{
expectType<ElementHandle<Element> | null>(
await handle.$('div > some-custom')
);
}
{
expectType<ElementHandle<Element> | null>(
await handle.$('div > some-custom#id')
);
}
{
expectType<ElementHandle<Element> | null>(
await handle.$('div > some-custom.class')
);
}
{
expectType<ElementHandle<Element> | null>(
await handle.$('div > some-custom[attr=value]')
);
}
{
expectType<ElementHandle<Element> | null>(
await handle.$('div > some-custom:pseudo-class')
);
}
{
expectType<ElementHandle<Element> | null>(
await handle.$('div > some-custom:func(arg)')
);
}
}
{
{
expectType<ElementHandle<Element> | null>(await handle.$('div > #id'));
}
{
expectType<ElementHandle<Element> | null>(await handle.$('div > .class'));
}
{
expectType<ElementHandle<Element> | null>(
await handle.$('div > [attr=value]')
);
}
{
expectType<ElementHandle<Element> | null>(
await handle.$('div > :pseudo-class')
);
}
{
expectType<ElementHandle<Element> | null>(
await handle.$('div > :func(arg)')
);
}
}
{
{
expectType<ElementHandle<HTMLAnchorElement> | null>(
await handle.$('div > a')
);
expectNotType<ElementHandle<Element> | null>(await handle.$('div > a'));
}
{
expectType<ElementHandle<HTMLAnchorElement> | null>(
await handle.$('div > a#id')
);
expectNotType<ElementHandle<Element> | null>(
await handle.$('div > a#id')
);
}
{
expectType<ElementHandle<HTMLAnchorElement> | null>(
await handle.$('div > a.class')
);
expectNotType<ElementHandle<Element> | null>(
await handle.$('div > a.class')
);
}
{
expectType<ElementHandle<HTMLAnchorElement> | null>(
await handle.$('div > a[attr=value]')
);
expectNotType<ElementHandle<Element> | null>(
await handle.$('div > a[attr=value]')
);
}
{
expectType<ElementHandle<HTMLAnchorElement> | null>(
await handle.$('div > a:psuedo-class')
);
expectNotType<ElementHandle<Element> | null>(
await handle.$('div > a:pseudo-class')
);
}
{
expectType<ElementHandle<HTMLAnchorElement> | null>(
await handle.$('div > a:func(arg)')
);
expectNotType<ElementHandle<Element> | null>(
await handle.$('div > a:func(arg)')
);
}
}
{
{
expectType<ElementHandle<HTMLDivElement> | null>(
await handle.$('div > div')
);
expectNotType<ElementHandle<Element> | null>(await handle.$('div > div'));
}
{
expectType<ElementHandle<HTMLDivElement> | null>(
await handle.$('div > div#id')
);
expectNotType<ElementHandle<Element> | null>(
await handle.$('div > div#id')
);
}
{
expectType<ElementHandle<HTMLDivElement> | null>(
await handle.$('div > div.class')
);
expectNotType<ElementHandle<Element> | null>(
await handle.$('div > div.class')
);
}
{
expectType<ElementHandle<HTMLDivElement> | null>(
await handle.$('div > div[attr=value]')
);
expectNotType<ElementHandle<Element> | null>(
await handle.$('div > div[attr=value]')
);
}
{
expectType<ElementHandle<HTMLDivElement> | null>(
await handle.$('div > div:psuedo-class')
);
expectNotType<ElementHandle<Element> | null>(
await handle.$('div > div:pseudo-class')
);
}
{
expectType<ElementHandle<HTMLDivElement> | null>(
await handle.$('div > div:func(arg)')
);
expectNotType<ElementHandle<Element> | null>(
await handle.$('div > div:func(arg)')
);
}
}
{
{
expectType<ElementHandle<Element> | null>(
await handle.$('div > some-custom')
);
}
{
expectType<ElementHandle<Element> | null>(
await handle.$('div > some-custom#id')
);
}
{
expectType<ElementHandle<Element> | null>(
await handle.$('div > some-custom.class')
);
}
{
expectType<ElementHandle<Element> | null>(
await handle.$('div > some-custom[attr=value]')
);
}
{
expectType<ElementHandle<Element> | null>(
await handle.$('div > some-custom:pseudo-class')
);
}
{
expectType<ElementHandle<Element> | null>(
await handle.$('div > some-custom:func(arg)')
);
}
}
{
{
expectType<ElementHandle<Element> | null>(await handle.$('div > #id'));
}
{
expectType<ElementHandle<Element> | null>(await handle.$('div > .class'));
}
{
expectType<ElementHandle<Element> | null>(
await handle.$('div > [attr=value]')
);
}
{
expectType<ElementHandle<Element> | null>(
await handle.$('div > :pseudo-class')
);
}
{
expectType<ElementHandle<Element> | null>(
await handle.$('div > :func(arg)')
);
}
}
} }
{ {
{
{ {
expectType<Array<ElementHandle<HTMLAnchorElement>>>(await handle.$$('a')); expectType<Array<ElementHandle<HTMLAnchorElement>>>(await handle.$$('a'));
expectNotType<Array<ElementHandle<Element>>>(await handle.$$('a')); expectNotType<Array<ElementHandle<Element>>>(await handle.$$('a'));
} }
{
expectType<Array<ElementHandle<HTMLAnchorElement>>>(
await handle.$$('a#id')
);
expectNotType<Array<ElementHandle<Element>>>(await handle.$$('a#id'));
}
{
expectType<Array<ElementHandle<HTMLAnchorElement>>>(
await handle.$$('a.class')
);
expectNotType<Array<ElementHandle<Element>>>(await handle.$$('a.class'));
}
{
expectType<Array<ElementHandle<HTMLAnchorElement>>>(
await handle.$$('a[attr=value]')
);
expectNotType<Array<ElementHandle<Element>>>(
await handle.$$('a[attr=value]')
);
}
{
expectType<Array<ElementHandle<HTMLAnchorElement>>>(
await handle.$$('a:psuedo-class')
);
expectNotType<Array<ElementHandle<Element>>>(
await handle.$$('a:pseudo-class')
);
}
{
expectType<Array<ElementHandle<HTMLAnchorElement>>>(
await handle.$$('a:func(arg)')
);
expectNotType<Array<ElementHandle<Element>>>(
await handle.$$('a:func(arg)')
);
}
}
{
{ {
expectType<Array<ElementHandle<HTMLDivElement>>>(await handle.$$('div')); expectType<Array<ElementHandle<HTMLDivElement>>>(await handle.$$('div'));
expectNotType<Array<ElementHandle<Element>>>(await handle.$$('div')); expectNotType<Array<ElementHandle<Element>>>(await handle.$$('div'));
} }
{
expectType<Array<ElementHandle<HTMLDivElement>>>(
await handle.$$('div#id')
);
expectNotType<Array<ElementHandle<Element>>>(await handle.$$('div#id'));
}
{
expectType<Array<ElementHandle<HTMLDivElement>>>(
await handle.$$('div.class')
);
expectNotType<Array<ElementHandle<Element>>>(
await handle.$$('div.class')
);
}
{
expectType<Array<ElementHandle<HTMLDivElement>>>(
await handle.$$('div[attr=value]')
);
expectNotType<Array<ElementHandle<Element>>>(
await handle.$$('div[attr=value]')
);
}
{
expectType<Array<ElementHandle<HTMLDivElement>>>(
await handle.$$('div:psuedo-class')
);
expectNotType<Array<ElementHandle<Element>>>(
await handle.$$('div:pseudo-class')
);
}
{
expectType<Array<ElementHandle<HTMLDivElement>>>(
await handle.$$('div:func(arg)')
);
expectNotType<Array<ElementHandle<Element>>>(
await handle.$$('div:func(arg)')
);
}
}
{
{ {
expectType<Array<ElementHandle<Element>>>(await handle.$$('some-custom')); expectType<Array<ElementHandle<Element>>>(await handle.$$('some-custom'));
} }
{
expectType<Array<ElementHandle<Element>>>(
await handle.$$('some-custom#id')
);
}
{
expectType<Array<ElementHandle<Element>>>(
await handle.$$('some-custom.class')
);
}
{
expectType<Array<ElementHandle<Element>>>(
await handle.$$('some-custom[attr=value]')
);
}
{
expectType<Array<ElementHandle<Element>>>(
await handle.$$('some-custom:pseudo-class')
);
}
{
expectType<Array<ElementHandle<Element>>>(
await handle.$$('some-custom:func(arg)')
);
}
}
{
{
expectType<Array<ElementHandle<Element>>>(await handle.$$(''));
}
{
expectType<Array<ElementHandle<Element>>>(await handle.$$('#id'));
}
{
expectType<Array<ElementHandle<Element>>>(await handle.$$('.class'));
}
{
expectType<Array<ElementHandle<Element>>>(
await handle.$$('[attr=value]')
);
}
{
expectType<Array<ElementHandle<Element>>>(
await handle.$$(':pseudo-class')
);
}
{
expectType<Array<ElementHandle<Element>>>(await handle.$$(':func(arg)'));
}
}
{
{
expectType<Array<ElementHandle<HTMLAnchorElement>>>(
await handle.$$('div > a')
);
expectNotType<Array<ElementHandle<Element>>>(await handle.$$('div > a'));
}
{
expectType<Array<ElementHandle<HTMLAnchorElement>>>(
await handle.$$('div > a#id')
);
expectNotType<Array<ElementHandle<Element>>>(
await handle.$$('div > a#id')
);
}
{
expectType<Array<ElementHandle<HTMLAnchorElement>>>(
await handle.$$('div > a.class')
);
expectNotType<Array<ElementHandle<Element>>>(
await handle.$$('div > a.class')
);
}
{
expectType<Array<ElementHandle<HTMLAnchorElement>>>(
await handle.$$('div > a[attr=value]')
);
expectNotType<Array<ElementHandle<Element>>>(
await handle.$$('div > a[attr=value]')
);
}
{
expectType<Array<ElementHandle<HTMLAnchorElement>>>(
await handle.$$('div > a:psuedo-class')
);
expectNotType<Array<ElementHandle<Element>>>(
await handle.$$('div > a:pseudo-class')
);
}
{
expectType<Array<ElementHandle<HTMLAnchorElement>>>(
await handle.$$('div > a:func(arg)')
);
expectNotType<Array<ElementHandle<Element>>>(
await handle.$$('div > a:func(arg)')
);
}
}
{
{
expectType<Array<ElementHandle<HTMLDivElement>>>(
await handle.$$('div > div')
);
expectNotType<Array<ElementHandle<Element>>>(
await handle.$$('div > div')
);
}
{
expectType<Array<ElementHandle<HTMLDivElement>>>(
await handle.$$('div > div#id')
);
expectNotType<Array<ElementHandle<Element>>>(
await handle.$$('div > div#id')
);
}
{
expectType<Array<ElementHandle<HTMLDivElement>>>(
await handle.$$('div > div.class')
);
expectNotType<Array<ElementHandle<Element>>>(
await handle.$$('div > div.class')
);
}
{
expectType<Array<ElementHandle<HTMLDivElement>>>(
await handle.$$('div > div[attr=value]')
);
expectNotType<Array<ElementHandle<Element>>>(
await handle.$$('div > div[attr=value]')
);
}
{
expectType<Array<ElementHandle<HTMLDivElement>>>(
await handle.$$('div > div:psuedo-class')
);
expectNotType<Array<ElementHandle<Element>>>(
await handle.$$('div > div:pseudo-class')
);
}
{
expectType<Array<ElementHandle<HTMLDivElement>>>(
await handle.$$('div > div:func(arg)')
);
expectNotType<Array<ElementHandle<Element>>>(
await handle.$$('div > div:func(arg)')
);
}
}
{
{
expectType<Array<ElementHandle<Element>>>(
await handle.$$('div > some-custom')
);
}
{
expectType<Array<ElementHandle<Element>>>(
await handle.$$('div > some-custom#id')
);
}
{
expectType<Array<ElementHandle<Element>>>(
await handle.$$('div > some-custom.class')
);
}
{
expectType<Array<ElementHandle<Element>>>(
await handle.$$('div > some-custom[attr=value]')
);
}
{
expectType<Array<ElementHandle<Element>>>(
await handle.$$('div > some-custom:pseudo-class')
);
}
{
expectType<Array<ElementHandle<Element>>>(
await handle.$$('div > some-custom:func(arg)')
);
}
}
{
{
expectType<Array<ElementHandle<Element>>>(await handle.$$('div > #id'));
}
{
expectType<Array<ElementHandle<Element>>>(
await handle.$$('div > .class')
);
}
{
expectType<Array<ElementHandle<Element>>>(
await handle.$$('div > [attr=value]')
);
}
{
expectType<Array<ElementHandle<Element>>>(
await handle.$$('div > :pseudo-class')
);
}
{
expectType<Array<ElementHandle<Element>>>(
await handle.$$('div > :func(arg)')
);
}
}
{
{
expectType<Array<ElementHandle<HTMLAnchorElement>>>(
await handle.$$('div > a')
);
expectNotType<Array<ElementHandle<Element>>>(await handle.$$('div > a'));
}
{
expectType<Array<ElementHandle<HTMLAnchorElement>>>(
await handle.$$('div > a#id')
);
expectNotType<Array<ElementHandle<Element>>>(
await handle.$$('div > a#id')
);
}
{
expectType<Array<ElementHandle<HTMLAnchorElement>>>(
await handle.$$('div > a.class')
);
expectNotType<Array<ElementHandle<Element>>>(
await handle.$$('div > a.class')
);
}
{
expectType<Array<ElementHandle<HTMLAnchorElement>>>(
await handle.$$('div > a[attr=value]')
);
expectNotType<Array<ElementHandle<Element>>>(
await handle.$$('div > a[attr=value]')
);
}
{
expectType<Array<ElementHandle<HTMLAnchorElement>>>(
await handle.$$('div > a:psuedo-class')
);
expectNotType<Array<ElementHandle<Element>>>(
await handle.$$('div > a:pseudo-class')
);
}
{
expectType<Array<ElementHandle<HTMLAnchorElement>>>(
await handle.$$('div > a:func(arg)')
);
expectNotType<Array<ElementHandle<Element>>>(
await handle.$$('div > a:func(arg)')
);
}
}
{
{
expectType<Array<ElementHandle<HTMLDivElement>>>(
await handle.$$('div > div')
);
expectNotType<Array<ElementHandle<Element>>>(
await handle.$$('div > div')
);
}
{
expectType<Array<ElementHandle<HTMLDivElement>>>(
await handle.$$('div > div#id')
);
expectNotType<Array<ElementHandle<Element>>>(
await handle.$$('div > div#id')
);
}
{
expectType<Array<ElementHandle<HTMLDivElement>>>(
await handle.$$('div > div.class')
);
expectNotType<Array<ElementHandle<Element>>>(
await handle.$$('div > div.class')
);
}
{
expectType<Array<ElementHandle<HTMLDivElement>>>(
await handle.$$('div > div[attr=value]')
);
expectNotType<Array<ElementHandle<Element>>>(
await handle.$$('div > div[attr=value]')
);
}
{
expectType<Array<ElementHandle<HTMLDivElement>>>(
await handle.$$('div > div:psuedo-class')
);
expectNotType<Array<ElementHandle<Element>>>(
await handle.$$('div > div:pseudo-class')
);
}
{
expectType<Array<ElementHandle<HTMLDivElement>>>(
await handle.$$('div > div:func(arg)')
);
expectNotType<Array<ElementHandle<Element>>>(
await handle.$$('div > div:func(arg)')
);
}
}
{
{
expectType<Array<ElementHandle<Element>>>(
await handle.$$('div > some-custom')
);
}
{
expectType<Array<ElementHandle<Element>>>(
await handle.$$('div > some-custom#id')
);
}
{
expectType<Array<ElementHandle<Element>>>(
await handle.$$('div > some-custom.class')
);
}
{
expectType<Array<ElementHandle<Element>>>(
await handle.$$('div > some-custom[attr=value]')
);
}
{
expectType<Array<ElementHandle<Element>>>(
await handle.$$('div > some-custom:pseudo-class')
);
}
{
expectType<Array<ElementHandle<Element>>>(
await handle.$$('div > some-custom:func(arg)')
);
}
}
{
{
expectType<Array<ElementHandle<Element>>>(await handle.$$('div > #id'));
}
{
expectType<Array<ElementHandle<Element>>>(
await handle.$$('div > .class')
);
}
{
expectType<Array<ElementHandle<Element>>>(
await handle.$$('div > [attr=value]')
);
}
{
expectType<Array<ElementHandle<Element>>>(
await handle.$$('div > :pseudo-class')
);
}
{
expectType<Array<ElementHandle<Element>>>(
await handle.$$('div > :func(arg)')
);
}
}
} }
{ {

144
test-d/NodeFor.test-d.ts Normal file
View File

@ -0,0 +1,144 @@
import type {NodeFor} from 'puppeteer';
import {expectType, expectNotType} from 'tsd';
declare const nodeFor: <Selector extends string>(
selector: Selector
) => NodeFor<Selector>;
{
{
expectType<HTMLAnchorElement>(nodeFor('a'));
expectNotType<Element>(nodeFor('a'));
}
{
expectType<HTMLAnchorElement>(nodeFor('a#ignored'));
expectNotType<Element>(nodeFor('a#ignored'));
}
{
expectType<HTMLAnchorElement>(nodeFor('a.ignored'));
expectNotType<Element>(nodeFor('a.ignored'));
}
{
expectType<HTMLAnchorElement>(nodeFor('a[ignored'));
expectNotType<Element>(nodeFor('a[ignored'));
}
{
expectType<HTMLAnchorElement>(nodeFor('a:ignored'));
expectNotType<Element>(nodeFor('a:ignored'));
}
{
expectType<HTMLAnchorElement>(nodeFor('ignored a'));
expectNotType<Element>(nodeFor('ignored a'));
}
{
expectType<HTMLAnchorElement>(nodeFor('ignored a#ignored'));
expectNotType<Element>(nodeFor('ignored a#ignored'));
}
{
expectType<HTMLAnchorElement>(nodeFor('ignored a.ignored'));
expectNotType<Element>(nodeFor('ignored a.ignored'));
}
{
expectType<HTMLAnchorElement>(nodeFor('ignored a[ignored'));
expectNotType<Element>(nodeFor('ignored a[ignored'));
}
{
expectType<HTMLAnchorElement>(nodeFor('ignored a:ignored'));
expectNotType<Element>(nodeFor('ignored a:ignored'));
}
{
expectType<HTMLAnchorElement>(nodeFor('ignored > a'));
expectNotType<Element>(nodeFor('ignored > a'));
}
{
expectType<HTMLAnchorElement>(nodeFor('ignored > a#ignored'));
expectNotType<Element>(nodeFor('ignored > a#ignored'));
}
{
expectType<HTMLAnchorElement>(nodeFor('ignored > a.ignored'));
expectNotType<Element>(nodeFor('ignored > a.ignored'));
}
{
expectType<HTMLAnchorElement>(nodeFor('ignored > a[ignored'));
expectNotType<Element>(nodeFor('ignored > a[ignored'));
}
{
expectType<HTMLAnchorElement>(nodeFor('ignored > a:ignored'));
expectNotType<Element>(nodeFor('ignored > a:ignored'));
}
{
expectType<HTMLAnchorElement>(nodeFor('ignored + a'));
expectNotType<Element>(nodeFor('ignored + a'));
}
{
expectType<HTMLAnchorElement>(nodeFor('ignored ~ a'));
expectNotType<Element>(nodeFor('ignored ~ a'));
}
{
expectType<HTMLAnchorElement>(nodeFor('ignored | a'));
expectNotType<Element>(nodeFor('ignored | a'));
}
{
expectType<HTMLAnchorElement>(
nodeFor('ignored ignored > ignored + ignored | a#ignore')
);
expectNotType<Element>(
nodeFor('ignored ignored > ignored + ignored | a#ignore')
);
}
}
{
{
expectType<Element>(nodeFor(''));
}
{
expectType<Element>(nodeFor('#ignored'));
}
{
expectType<Element>(nodeFor('.ignored'));
}
{
expectType<Element>(nodeFor('[ignored'));
}
{
expectType<Element>(nodeFor(':ignored'));
}
{
expectType<Element>(nodeFor('ignored #ignored'));
}
{
expectType<Element>(nodeFor('ignored .ignored'));
}
{
expectType<Element>(nodeFor('ignored [ignored'));
}
{
expectType<Element>(nodeFor('ignored :ignored'));
}
{
expectType<Element>(nodeFor('ignored > #ignored'));
}
{
expectType<Element>(nodeFor('ignored > .ignored'));
}
{
expectType<Element>(nodeFor('ignored > [ignored'));
}
{
expectType<Element>(nodeFor('ignored > :ignored'));
}
{
expectType<Element>(nodeFor('ignored + #ignored'));
}
{
expectType<Element>(nodeFor('ignored ~ #ignored'));
}
{
expectType<Element>(nodeFor('ignored | #ignored'));
}
{
expectType<Element>(
nodeFor('ignored ignored > ignored ~ ignored + ignored | #ignored')
);
}
}