-
Notifications
You must be signed in to change notification settings - Fork 0
/
pageUtils.ts
122 lines (99 loc) · 3.76 KB
/
pageUtils.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
import {SelectorChain} from 'leak-detect-inject';
import type {BoundingBox, BrowserContext, ElementHandle, Frame, Page} from 'puppeteer';
import {stripHash} from './utils';
import {PageVars} from './FieldsCollector';
import {getFrameStack, unwrapHandle} from './puppeteerUtils';
/**
* Close pages in `context` not in `keep`
* @returns Closed pages
*/
export async function closeExtraPages(context: BrowserContext, keep: Set<Page>): Promise<Page[]> {
return Promise.all((await context.pages()).filter(page => !keep.has(page))
.map(page => page.close({runBeforeUnload: false}).then(() => page)));
}
/** Get a string which should uniquely identify an element across pages */
export function getElemIdentifier(elem: ElementAttrs | ElementInfo): string {
const attrs = 'attrs' in elem ? elem.attrs : elem;
return `${stripHash(attrs.frameStack[0]!)} ${selectorStr(attrs.selectorChain)}`;
}
export function selectorStr(selectorChain: SelectorChain): string {
return selectorChain.join('>>>');
}
export async function getElementInfoFromAttrs(attrs: ElementAttrs, frame: Frame): Promise<ElementInfo | null> {
const handle = (await getElementBySelectorChain(attrs.selectorChain, frame))?.elem;
return handle ? {handle, attrs} : null;
}
export async function getElementBySelectorChain(selector: SelectorChain, frame: Frame):
Promise<{ elem: ElementHandle, unique: boolean } | null> {
return await unwrapHandle(await frame.evaluateHandle(
(selector: SelectorChain) => window[PageVars.INJECTED].getElementBySelectorChain(selector), selector));
}
export function formSelectorChain(handle: ElementHandle): Promise<SelectorChain> {
return handle.evaluate(el => window[PageVars.INJECTED].formSelectorChain(el));
}
export async function getElementAttrs(handle: ElementHandle): Promise<ElementAttrs> {
const inView = await handle.isIntersectingViewport();
const boundingBox = await handle.boundingBox();
const elAttrsPartial = await handle.evaluate(el => {
const form = (el as Element & { form?: HTMLFormElement }).form;
return {
id: el.id,
tagName: el.nodeName,
class: el.className,
innerText: el instanceof HTMLElement ? el.innerText : el.textContent ?? '',
name: el.getAttribute('name'),
type: el.getAttribute('type'),
href: el.getAttribute('href'),
ariaLabel: el.ariaLabel,
placeholder: el.getAttribute('placeholder'),
form: form ? window[PageVars.INJECTED].formSelectorChain(form) : null,
onTop: window[PageVars.INJECTED].isOnTop(el),
visible: window[PageVars.INJECTED].isVisible(el),
selectorChain: window[PageVars.INJECTED].formSelectorChain(el),
};
});
return {
...elAttrsPartial,
frameStack: getFrameStack(handle.executionContext().frame()!).map(f => f.url()),
inView,
boundingBox,
time: Date.now(),
};
}
export interface ElementAttrs {
/** URLs starting with the bottom frame, going up */
frameStack: string[];
id: string;
tagName: string;
class: string;
innerText: string;
name: string | null;
type: string | null;
href: string | null;
ariaLabel: string | null;
placeholder: string | null;
form: SelectorChain | null;
onTop: boolean;
inView: boolean;
visible: boolean;
boundingBox: BoundingBox | null;
selectorChain: SelectorChain;
time: number;
}
export interface FathomElementAttrs extends ElementAttrs {
score: number;
}
export interface FieldElementAttrs extends ElementAttrs {
fieldType: FieldType;
filled?: boolean;
submitted?: boolean;
}
export interface LinkElementAttrs extends ElementAttrs {
linkMatchType: LinkMatchType;
}
export type FieldType = 'email' | 'password';
export type LinkMatchType = 'exact' | 'loose' | 'coords';
export interface ElementInfo<AttrsType extends ElementAttrs = ElementAttrs, ElementType extends Element = Element> {
handle: ElementHandle<ElementType>;
attrs: AttrsType;
}