forked from GoogleChrome/lighthouse
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathinputs.js
118 lines (105 loc) · 3.62 KB
/
inputs.js
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
/**
* @license
* Copyright 2020 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
/* global getNodeDetails */
import BaseGatherer from '../base-gatherer.js';
import {pageFunctions} from '../../lib/page-functions.js';
/* eslint-env browser, node */
/**
* @return {LH.Artifacts['Inputs']}
*/
/* c8 ignore start */
function collectElements() {
/** @type {LH.Artifacts.InputElement[]} */
const inputArtifacts = [];
/** @type {Map<HTMLFormElement, LH.Artifacts.FormElement>} */
const formElToArtifact = new Map();
/** @type {Map<HTMLLabelElement, LH.Artifacts.LabelElement>} */
const labelElToArtifact = new Map();
/** @type {HTMLFormElement[]} */
// @ts-expect-error - put into scope via stringification
const formEls = getElementsInDocument('form'); // eslint-disable-line no-undef
for (const formEl of formEls) {
formElToArtifact.set(formEl, {
id: formEl.id,
name: formEl.name,
autocomplete: formEl.autocomplete,
// @ts-expect-error - getNodeDetails put into scope via stringification
node: getNodeDetails(formEl),
});
}
/** @type {HTMLLabelElement[]} */
// @ts-expect-error - put into scope via stringification
const labelEls = getElementsInDocument('label'); // eslint-disable-line no-undef
for (const labelEl of labelEls) {
labelElToArtifact.set(labelEl, {
for: labelEl.htmlFor,
// @ts-expect-error - getNodeDetails put into scope via stringification
node: getNodeDetails(labelEl),
});
}
/** @type {HTMLInputElement[]} */
// @ts-expect-error - put into scope via stringification
const inputEls = getElementsInDocument('textarea, input, select'); // eslint-disable-line no-undef
for (const inputEl of inputEls) {
// If the input element is in a form (either because an ancestor element is <form> or the
// form= attribute is associated with a <form> element's id), this will be set.
const parentFormEl = inputEl.form;
const parentFormIndex = parentFormEl ?
[...formElToArtifact.keys()].indexOf(parentFormEl) :
undefined;
const labelIndices = [...(inputEl.labels || [])].map((labelEl) => {
return [...labelElToArtifact.keys()].indexOf(labelEl);
});
let preventsPaste;
if (!inputEl.readOnly) {
preventsPaste = !inputEl.dispatchEvent(new ClipboardEvent('paste', {cancelable: true}));
}
inputArtifacts.push({
parentFormIndex,
labelIndices,
id: inputEl.id,
name: inputEl.name,
type: inputEl.type,
placeholder: inputEl instanceof HTMLSelectElement ? undefined : inputEl.placeholder,
autocomplete: {
property: inputEl.autocomplete,
attribute: inputEl.getAttribute('autocomplete'),
// Requires `--enable-features=AutofillShowTypePredictions`.
prediction: inputEl.getAttribute('autofill-prediction'),
},
preventsPaste,
// @ts-expect-error - getNodeDetails put into scope via stringification
node: getNodeDetails(inputEl),
});
}
return {
inputs: inputArtifacts,
forms: [...formElToArtifact.values()],
labels: [...labelElToArtifact.values()],
};
}
/* c8 ignore stop */
class Inputs extends BaseGatherer {
/** @type {LH.Gatherer.GathererMeta} */
meta = {
supportedModes: ['snapshot', 'navigation'],
};
/**
* @param {LH.Gatherer.Context} passContext
* @return {Promise<LH.Artifacts['Inputs']>}
*/
async getArtifact(passContext) {
return passContext.driver.executionContext.evaluate(collectElements, {
args: [],
useIsolation: true,
deps: [
pageFunctions.getElementsInDocument,
pageFunctions.getNodeDetails,
],
});
}
}
export default Inputs;