@angular/cdk 21.0.0-next.9 → 21.0.0-rc.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/_adev_assets/cdk_drag_drop.json +13 -12
- package/_adev_assets/cdk_testing.json +9 -9
- package/_adev_assets/cdk_testing_protractor.json +1 -1
- package/_adev_assets/cdk_testing_selenium_webdriver.json +1 -1
- package/_adev_assets/cdk_testing_testbed.json +1 -1
- package/fesm2022/_a11y-module-chunk.mjs +755 -869
- package/fesm2022/_a11y-module-chunk.mjs.map +1 -1
- package/fesm2022/_activedescendant-key-manager-chunk.mjs +8 -8
- package/fesm2022/_activedescendant-key-manager-chunk.mjs.map +1 -1
- package/fesm2022/_array-chunk.mjs +1 -1
- package/fesm2022/_array-chunk.mjs.map +1 -1
- package/fesm2022/_breakpoints-observer-chunk.mjs +149 -152
- package/fesm2022/_breakpoints-observer-chunk.mjs.map +1 -1
- package/fesm2022/_css-pixel-value-chunk.mjs +4 -5
- package/fesm2022/_css-pixel-value-chunk.mjs.map +1 -1
- package/fesm2022/_data-source-chunk.mjs +2 -8
- package/fesm2022/_data-source-chunk.mjs.map +1 -1
- package/fesm2022/_directionality-chunk.mjs +54 -54
- package/fesm2022/_directionality-chunk.mjs.map +1 -1
- package/fesm2022/_dispose-view-repeater-strategy-chunk.mjs +25 -36
- package/fesm2022/_dispose-view-repeater-strategy-chunk.mjs.map +1 -1
- package/fesm2022/_element-chunk.mjs +6 -17
- package/fesm2022/_element-chunk.mjs.map +1 -1
- package/fesm2022/_fake-event-detection-chunk.mjs +3 -17
- package/fesm2022/_fake-event-detection-chunk.mjs.map +1 -1
- package/fesm2022/_focus-key-manager-chunk.mjs +10 -14
- package/fesm2022/_focus-key-manager-chunk.mjs.map +1 -1
- package/fesm2022/_focus-monitor-chunk.mjs +376 -566
- package/fesm2022/_focus-monitor-chunk.mjs.map +1 -1
- package/fesm2022/_id-generator-chunk.mjs +37 -27
- package/fesm2022/_id-generator-chunk.mjs.map +1 -1
- package/fesm2022/_keycodes-chunk.mjs +9 -9
- package/fesm2022/_keycodes-chunk.mjs.map +1 -1
- package/fesm2022/_list-key-manager-chunk.mjs +248 -336
- package/fesm2022/_list-key-manager-chunk.mjs.map +1 -1
- package/fesm2022/_overlay-module-chunk.mjs +2535 -2947
- package/fesm2022/_overlay-module-chunk.mjs.map +1 -1
- package/fesm2022/_passive-listeners-chunk.mjs +10 -22
- package/fesm2022/_passive-listeners-chunk.mjs.map +1 -1
- package/fesm2022/_platform-chunk.mjs +42 -65
- package/fesm2022/_platform-chunk.mjs.map +1 -1
- package/fesm2022/_recycle-view-repeater-strategy-chunk.mjs +78 -134
- package/fesm2022/_recycle-view-repeater-strategy-chunk.mjs.map +1 -1
- package/fesm2022/_scrolling-chunk.mjs +44 -85
- package/fesm2022/_scrolling-chunk.mjs.map +1 -1
- package/fesm2022/_selection-model-chunk.mjs +138 -209
- package/fesm2022/_selection-model-chunk.mjs.map +1 -1
- package/fesm2022/_shadow-dom-chunk.mjs +21 -35
- package/fesm2022/_shadow-dom-chunk.mjs.map +1 -1
- package/fesm2022/_style-loader-chunk.mjs +50 -37
- package/fesm2022/_style-loader-chunk.mjs.map +1 -1
- package/fesm2022/_test-environment-chunk.mjs +2 -14
- package/fesm2022/_test-environment-chunk.mjs.map +1 -1
- package/fesm2022/_tree-key-manager-chunk.mjs +229 -308
- package/fesm2022/_tree-key-manager-chunk.mjs.map +1 -1
- package/fesm2022/_typeahead-chunk.mjs +52 -74
- package/fesm2022/_typeahead-chunk.mjs.map +1 -1
- package/fesm2022/_unique-selection-dispatcher-chunk.mjs +43 -40
- package/fesm2022/_unique-selection-dispatcher-chunk.mjs.map +1 -1
- package/fesm2022/a11y.mjs +351 -449
- package/fesm2022/a11y.mjs.map +1 -1
- package/fesm2022/accordion.mjs +254 -192
- package/fesm2022/accordion.mjs.map +1 -1
- package/fesm2022/bidi.mjs +121 -64
- package/fesm2022/bidi.mjs.map +1 -1
- package/fesm2022/cdk.mjs +1 -2
- package/fesm2022/cdk.mjs.map +1 -1
- package/fesm2022/clipboard.mjs +208 -186
- package/fesm2022/clipboard.mjs.map +1 -1
- package/fesm2022/coercion-private.mjs +4 -8
- package/fesm2022/coercion-private.mjs.map +1 -1
- package/fesm2022/coercion.mjs +11 -29
- package/fesm2022/coercion.mjs.map +1 -1
- package/fesm2022/dialog.mjs +660 -808
- package/fesm2022/dialog.mjs.map +1 -1
- package/fesm2022/drag-drop.mjs +3347 -4286
- package/fesm2022/drag-drop.mjs.map +1 -1
- package/fesm2022/keycodes.mjs +4 -8
- package/fesm2022/keycodes.mjs.map +1 -1
- package/fesm2022/layout.mjs +44 -26
- package/fesm2022/layout.mjs.map +1 -1
- package/fesm2022/listbox.mjs +841 -895
- package/fesm2022/listbox.mjs.map +1 -1
- package/fesm2022/menu.mjs +1942 -1858
- package/fesm2022/menu.mjs.map +1 -1
- package/fesm2022/observers-private.mjs +88 -106
- package/fesm2022/observers-private.mjs.map +1 -1
- package/fesm2022/observers.mjs +262 -184
- package/fesm2022/observers.mjs.map +1 -1
- package/fesm2022/overlay.mjs +72 -68
- package/fesm2022/overlay.mjs.map +1 -1
- package/fesm2022/platform.mjs +43 -54
- package/fesm2022/platform.mjs.map +1 -1
- package/fesm2022/portal.mjs +402 -560
- package/fesm2022/portal.mjs.map +1 -1
- package/fesm2022/private.mjs +38 -10
- package/fesm2022/private.mjs.map +1 -1
- package/fesm2022/scrolling.mjs +1323 -1400
- package/fesm2022/scrolling.mjs.map +1 -1
- package/fesm2022/stepper.mjs +758 -590
- package/fesm2022/stepper.mjs.map +1 -1
- package/fesm2022/table.mjs +2327 -2319
- package/fesm2022/table.mjs.map +1 -1
- package/fesm2022/testing-selenium-webdriver.mjs +252 -325
- package/fesm2022/testing-selenium-webdriver.mjs.map +1 -1
- package/fesm2022/testing-testbed.mjs +592 -709
- package/fesm2022/testing-testbed.mjs.map +1 -1
- package/fesm2022/testing.mjs +368 -889
- package/fesm2022/testing.mjs.map +1 -1
- package/fesm2022/text-field.mjs +459 -388
- package/fesm2022/text-field.mjs.map +1 -1
- package/fesm2022/tree.mjs +1483 -1731
- package/fesm2022/tree.mjs.map +1 -1
- package/overlay/_index.scss +30 -0
- package/overlay-prebuilt.css +1 -1
- package/package.json +1 -1
- package/schematics/ng-add/index.js +1 -1
- package/types/_overlay-module-chunk.d.ts +69 -7
- package/types/_portal-directives-chunk.d.ts +2 -18
- package/types/a11y.d.ts +3 -1
- package/types/accordion.d.ts +3 -1
- package/types/dialog.d.ts +1 -1
- package/types/overlay.d.ts +1 -1
- package/types/portal.d.ts +1 -1
|
@@ -2,351 +2,278 @@ import * as webdriver from 'selenium-webdriver';
|
|
|
2
2
|
import { TestKey, getNoKeysSpecifiedError, _getTextWithExcludedElements, HarnessEnvironment } from './testing.mjs';
|
|
3
3
|
import 'rxjs';
|
|
4
4
|
|
|
5
|
-
/**
|
|
6
|
-
* Maps the `TestKey` constants to WebDriver's `webdriver.Key` constants.
|
|
7
|
-
* See https://github.com/SeleniumHQ/selenium/blob/trunk/javascript/webdriver/key.js#L29
|
|
8
|
-
*/
|
|
9
5
|
const seleniumWebDriverKeyMap = {
|
|
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
|
-
|
|
6
|
+
[TestKey.BACKSPACE]: webdriver.Key.BACK_SPACE,
|
|
7
|
+
[TestKey.TAB]: webdriver.Key.TAB,
|
|
8
|
+
[TestKey.ENTER]: webdriver.Key.ENTER,
|
|
9
|
+
[TestKey.SHIFT]: webdriver.Key.SHIFT,
|
|
10
|
+
[TestKey.CONTROL]: webdriver.Key.CONTROL,
|
|
11
|
+
[TestKey.ALT]: webdriver.Key.ALT,
|
|
12
|
+
[TestKey.ESCAPE]: webdriver.Key.ESCAPE,
|
|
13
|
+
[TestKey.PAGE_UP]: webdriver.Key.PAGE_UP,
|
|
14
|
+
[TestKey.PAGE_DOWN]: webdriver.Key.PAGE_DOWN,
|
|
15
|
+
[TestKey.END]: webdriver.Key.END,
|
|
16
|
+
[TestKey.HOME]: webdriver.Key.HOME,
|
|
17
|
+
[TestKey.LEFT_ARROW]: webdriver.Key.ARROW_LEFT,
|
|
18
|
+
[TestKey.UP_ARROW]: webdriver.Key.ARROW_UP,
|
|
19
|
+
[TestKey.RIGHT_ARROW]: webdriver.Key.ARROW_RIGHT,
|
|
20
|
+
[TestKey.DOWN_ARROW]: webdriver.Key.ARROW_DOWN,
|
|
21
|
+
[TestKey.INSERT]: webdriver.Key.INSERT,
|
|
22
|
+
[TestKey.DELETE]: webdriver.Key.DELETE,
|
|
23
|
+
[TestKey.F1]: webdriver.Key.F1,
|
|
24
|
+
[TestKey.F2]: webdriver.Key.F2,
|
|
25
|
+
[TestKey.F3]: webdriver.Key.F3,
|
|
26
|
+
[TestKey.F4]: webdriver.Key.F4,
|
|
27
|
+
[TestKey.F5]: webdriver.Key.F5,
|
|
28
|
+
[TestKey.F6]: webdriver.Key.F6,
|
|
29
|
+
[TestKey.F7]: webdriver.Key.F7,
|
|
30
|
+
[TestKey.F8]: webdriver.Key.F8,
|
|
31
|
+
[TestKey.F9]: webdriver.Key.F9,
|
|
32
|
+
[TestKey.F10]: webdriver.Key.F10,
|
|
33
|
+
[TestKey.F11]: webdriver.Key.F11,
|
|
34
|
+
[TestKey.F12]: webdriver.Key.F12,
|
|
35
|
+
[TestKey.META]: webdriver.Key.META,
|
|
36
|
+
[TestKey.COMMA]: ','
|
|
41
37
|
};
|
|
42
|
-
/** Gets a list of WebDriver `Key`s for the given `ModifierKeys`. */
|
|
43
38
|
function getSeleniumWebDriverModifierKeys(modifiers) {
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
39
|
+
const result = [];
|
|
40
|
+
if (modifiers.control) {
|
|
41
|
+
result.push(webdriver.Key.CONTROL);
|
|
42
|
+
}
|
|
43
|
+
if (modifiers.alt) {
|
|
44
|
+
result.push(webdriver.Key.ALT);
|
|
45
|
+
}
|
|
46
|
+
if (modifiers.shift) {
|
|
47
|
+
result.push(webdriver.Key.SHIFT);
|
|
48
|
+
}
|
|
49
|
+
if (modifiers.meta) {
|
|
50
|
+
result.push(webdriver.Key.META);
|
|
51
|
+
}
|
|
52
|
+
return result;
|
|
58
53
|
}
|
|
59
54
|
|
|
60
|
-
/** A `TestElement` implementation for WebDriver. */
|
|
61
55
|
class SeleniumWebDriverElement {
|
|
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
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
/** Sets the value of a property of an input. */
|
|
183
|
-
async setInputValue(newValue) {
|
|
184
|
-
await this._executeScript((element, value) => (element.value = value), this.element(), newValue);
|
|
185
|
-
await this._stabilize();
|
|
186
|
-
}
|
|
187
|
-
/** Selects the options at the specified indexes inside of a native `select` element. */
|
|
188
|
-
async selectOptions(...optionIndexes) {
|
|
189
|
-
await this._stabilize();
|
|
190
|
-
const options = await this.element().findElements(webdriver.By.css('option'));
|
|
191
|
-
const indexes = new Set(optionIndexes); // Convert to a set to remove duplicates.
|
|
192
|
-
if (options.length && indexes.size) {
|
|
193
|
-
// Reset the value so all the selected states are cleared. We can
|
|
194
|
-
// reuse the input-specific method since the logic is the same.
|
|
195
|
-
await this.setInputValue('');
|
|
196
|
-
for (let i = 0; i < options.length; i++) {
|
|
197
|
-
if (indexes.has(i)) {
|
|
198
|
-
// We have to hold the control key while clicking on options so that multiple can be
|
|
199
|
-
// selected in multi-selection mode. The key doesn't do anything for single selection.
|
|
200
|
-
await this._actions().keyDown(webdriver.Key.CONTROL).perform();
|
|
201
|
-
await options[i].click();
|
|
202
|
-
await this._actions().keyUp(webdriver.Key.CONTROL).perform();
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
await this._stabilize();
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
/** Checks whether this element matches the given selector. */
|
|
209
|
-
async matchesSelector(selector) {
|
|
210
|
-
await this._stabilize();
|
|
211
|
-
return this._executeScript((element, s) => (Element.prototype.matches || Element.prototype.msMatchesSelector).call(element, s), this.element(), selector);
|
|
212
|
-
}
|
|
213
|
-
/** Checks whether the element is focused. */
|
|
214
|
-
async isFocused() {
|
|
215
|
-
await this._stabilize();
|
|
216
|
-
return webdriver.WebElement.equals(this.element(), this.element().getDriver().switchTo().activeElement());
|
|
217
|
-
}
|
|
218
|
-
/**
|
|
219
|
-
* Dispatches an event with a particular name.
|
|
220
|
-
* @param name Name of the event to be dispatched.
|
|
221
|
-
*/
|
|
222
|
-
async dispatchEvent(name, data) {
|
|
223
|
-
await this._executeScript(dispatchEvent, name, this.element(), data);
|
|
224
|
-
await this._stabilize();
|
|
225
|
-
}
|
|
226
|
-
/** Gets the webdriver action sequence. */
|
|
227
|
-
_actions() {
|
|
228
|
-
return this.element().getDriver().actions();
|
|
229
|
-
}
|
|
230
|
-
/** Executes a function in the browser. */
|
|
231
|
-
async _executeScript(script, ...var_args) {
|
|
232
|
-
return this.element()
|
|
233
|
-
.getDriver()
|
|
234
|
-
.executeScript(script, ...var_args);
|
|
235
|
-
}
|
|
236
|
-
/** Dispatches all the events that are part of a click event sequence. */
|
|
237
|
-
async _dispatchClickEventSequence(args, button) {
|
|
238
|
-
let modifiers = {};
|
|
239
|
-
if (args.length && typeof args[args.length - 1] === 'object') {
|
|
240
|
-
modifiers = args.pop();
|
|
241
|
-
}
|
|
242
|
-
const modifierKeys = getSeleniumWebDriverModifierKeys(modifiers);
|
|
243
|
-
// Omitting the offset argument to mouseMove results in clicking the center.
|
|
244
|
-
// This is the default behavior we want, so we use an empty array of offsetArgs if
|
|
245
|
-
// no args remain after popping the modifiers from the args passed to this function.
|
|
246
|
-
const offsetArgs = (args.length === 2 ? [{ x: args[0], y: args[1] }] : []);
|
|
247
|
-
let actions = this._actions().mouseMove(this.element(), ...offsetArgs);
|
|
248
|
-
for (const modifierKey of modifierKeys) {
|
|
249
|
-
actions = actions.keyDown(modifierKey);
|
|
250
|
-
}
|
|
251
|
-
actions = actions.click(button);
|
|
252
|
-
for (const modifierKey of modifierKeys) {
|
|
253
|
-
actions = actions.keyUp(modifierKey);
|
|
56
|
+
element;
|
|
57
|
+
_stabilize;
|
|
58
|
+
constructor(element, _stabilize) {
|
|
59
|
+
this.element = element;
|
|
60
|
+
this._stabilize = _stabilize;
|
|
61
|
+
}
|
|
62
|
+
async blur() {
|
|
63
|
+
await this._executeScript(element => element.blur(), this.element());
|
|
64
|
+
await this._stabilize();
|
|
65
|
+
}
|
|
66
|
+
async clear() {
|
|
67
|
+
await this.element().clear();
|
|
68
|
+
await this._stabilize();
|
|
69
|
+
}
|
|
70
|
+
async click(...args) {
|
|
71
|
+
await this._dispatchClickEventSequence(args, webdriver.Button.LEFT);
|
|
72
|
+
await this._stabilize();
|
|
73
|
+
}
|
|
74
|
+
async rightClick(...args) {
|
|
75
|
+
await this._dispatchClickEventSequence(args, webdriver.Button.RIGHT);
|
|
76
|
+
await this._stabilize();
|
|
77
|
+
}
|
|
78
|
+
async focus() {
|
|
79
|
+
await this._executeScript(element => element.focus(), this.element());
|
|
80
|
+
await this._stabilize();
|
|
81
|
+
}
|
|
82
|
+
async getCssValue(property) {
|
|
83
|
+
await this._stabilize();
|
|
84
|
+
return this.element().getCssValue(property);
|
|
85
|
+
}
|
|
86
|
+
async hover() {
|
|
87
|
+
await this._actions().mouseMove(this.element()).perform();
|
|
88
|
+
await this._stabilize();
|
|
89
|
+
}
|
|
90
|
+
async mouseAway() {
|
|
91
|
+
await this._actions().mouseMove(this.element(), {
|
|
92
|
+
x: -1,
|
|
93
|
+
y: -1
|
|
94
|
+
}).perform();
|
|
95
|
+
await this._stabilize();
|
|
96
|
+
}
|
|
97
|
+
async sendKeys(...modifiersAndKeys) {
|
|
98
|
+
const first = modifiersAndKeys[0];
|
|
99
|
+
let modifiers;
|
|
100
|
+
let rest;
|
|
101
|
+
if (first !== undefined && typeof first !== 'string' && typeof first !== 'number') {
|
|
102
|
+
modifiers = first;
|
|
103
|
+
rest = modifiersAndKeys.slice(1);
|
|
104
|
+
} else {
|
|
105
|
+
modifiers = {};
|
|
106
|
+
rest = modifiersAndKeys;
|
|
107
|
+
}
|
|
108
|
+
const modifierKeys = getSeleniumWebDriverModifierKeys(modifiers);
|
|
109
|
+
const keys = rest.map(k => typeof k === 'string' ? k.split('') : [seleniumWebDriverKeyMap[k]]).reduce((arr, k) => arr.concat(k), []).map(k => modifierKeys.length > 0 ? webdriver.Key.chord(...modifierKeys, k) : k);
|
|
110
|
+
if (keys.length === 0) {
|
|
111
|
+
throw getNoKeysSpecifiedError();
|
|
112
|
+
}
|
|
113
|
+
await this.element().sendKeys(...keys);
|
|
114
|
+
await this._stabilize();
|
|
115
|
+
}
|
|
116
|
+
async text(options) {
|
|
117
|
+
await this._stabilize();
|
|
118
|
+
if (options?.exclude) {
|
|
119
|
+
return this._executeScript(_getTextWithExcludedElements, this.element(), options.exclude);
|
|
120
|
+
}
|
|
121
|
+
return this._executeScript(element => (element.textContent || '').trim(), this.element());
|
|
122
|
+
}
|
|
123
|
+
async setContenteditableValue(value) {
|
|
124
|
+
const contenteditableAttr = await this.getAttribute('contenteditable');
|
|
125
|
+
if (contenteditableAttr !== '' && contenteditableAttr !== 'true' && contenteditableAttr !== 'plaintext-only') {
|
|
126
|
+
throw new Error('setContenteditableValue can only be called on a `contenteditable` element.');
|
|
127
|
+
}
|
|
128
|
+
await this._stabilize();
|
|
129
|
+
return this._executeScript((element, valueToSet) => element.textContent = valueToSet, this.element(), value);
|
|
130
|
+
}
|
|
131
|
+
async getAttribute(name) {
|
|
132
|
+
await this._stabilize();
|
|
133
|
+
return this._executeScript((element, attribute) => element.getAttribute(attribute), this.element(), name);
|
|
134
|
+
}
|
|
135
|
+
async hasClass(name) {
|
|
136
|
+
await this._stabilize();
|
|
137
|
+
const classes = (await this.getAttribute('class')) || '';
|
|
138
|
+
return new Set(classes.split(/\s+/).filter(c => c)).has(name);
|
|
139
|
+
}
|
|
140
|
+
async getDimensions() {
|
|
141
|
+
await this._stabilize();
|
|
142
|
+
const {
|
|
143
|
+
width,
|
|
144
|
+
height
|
|
145
|
+
} = await this.element().getSize();
|
|
146
|
+
const {
|
|
147
|
+
x: left,
|
|
148
|
+
y: top
|
|
149
|
+
} = await this.element().getLocation();
|
|
150
|
+
return {
|
|
151
|
+
width,
|
|
152
|
+
height,
|
|
153
|
+
left,
|
|
154
|
+
top
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
async getProperty(name) {
|
|
158
|
+
await this._stabilize();
|
|
159
|
+
return this._executeScript((element, property) => element[property], this.element(), name);
|
|
160
|
+
}
|
|
161
|
+
async setInputValue(newValue) {
|
|
162
|
+
await this._executeScript((element, value) => element.value = value, this.element(), newValue);
|
|
163
|
+
await this._stabilize();
|
|
164
|
+
}
|
|
165
|
+
async selectOptions(...optionIndexes) {
|
|
166
|
+
await this._stabilize();
|
|
167
|
+
const options = await this.element().findElements(webdriver.By.css('option'));
|
|
168
|
+
const indexes = new Set(optionIndexes);
|
|
169
|
+
if (options.length && indexes.size) {
|
|
170
|
+
await this.setInputValue('');
|
|
171
|
+
for (let i = 0; i < options.length; i++) {
|
|
172
|
+
if (indexes.has(i)) {
|
|
173
|
+
await this._actions().keyDown(webdriver.Key.CONTROL).perform();
|
|
174
|
+
await options[i].click();
|
|
175
|
+
await this._actions().keyUp(webdriver.Key.CONTROL).perform();
|
|
254
176
|
}
|
|
255
|
-
|
|
256
|
-
|
|
177
|
+
}
|
|
178
|
+
await this._stabilize();
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
async matchesSelector(selector) {
|
|
182
|
+
await this._stabilize();
|
|
183
|
+
return this._executeScript((element, s) => (Element.prototype.matches || Element.prototype.msMatchesSelector).call(element, s), this.element(), selector);
|
|
184
|
+
}
|
|
185
|
+
async isFocused() {
|
|
186
|
+
await this._stabilize();
|
|
187
|
+
return webdriver.WebElement.equals(this.element(), this.element().getDriver().switchTo().activeElement());
|
|
188
|
+
}
|
|
189
|
+
async dispatchEvent(name, data) {
|
|
190
|
+
await this._executeScript(dispatchEvent, name, this.element(), data);
|
|
191
|
+
await this._stabilize();
|
|
192
|
+
}
|
|
193
|
+
_actions() {
|
|
194
|
+
return this.element().getDriver().actions();
|
|
195
|
+
}
|
|
196
|
+
async _executeScript(script, ...var_args) {
|
|
197
|
+
return this.element().getDriver().executeScript(script, ...var_args);
|
|
198
|
+
}
|
|
199
|
+
async _dispatchClickEventSequence(args, button) {
|
|
200
|
+
let modifiers = {};
|
|
201
|
+
if (args.length && typeof args[args.length - 1] === 'object') {
|
|
202
|
+
modifiers = args.pop();
|
|
203
|
+
}
|
|
204
|
+
const modifierKeys = getSeleniumWebDriverModifierKeys(modifiers);
|
|
205
|
+
const offsetArgs = args.length === 2 ? [{
|
|
206
|
+
x: args[0],
|
|
207
|
+
y: args[1]
|
|
208
|
+
}] : [];
|
|
209
|
+
let actions = this._actions().mouseMove(this.element(), ...offsetArgs);
|
|
210
|
+
for (const modifierKey of modifierKeys) {
|
|
211
|
+
actions = actions.keyDown(modifierKey);
|
|
212
|
+
}
|
|
213
|
+
actions = actions.click(button);
|
|
214
|
+
for (const modifierKey of modifierKeys) {
|
|
215
|
+
actions = actions.keyUp(modifierKey);
|
|
216
|
+
}
|
|
217
|
+
await actions.perform();
|
|
218
|
+
}
|
|
257
219
|
}
|
|
258
|
-
/**
|
|
259
|
-
* Dispatches an event with a particular name and data to an element. Note that this needs to be a
|
|
260
|
-
* pure function, because it gets stringified by WebDriver and is executed inside the browser.
|
|
261
|
-
*/
|
|
262
220
|
function dispatchEvent(name, element, data) {
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
element.dispatchEvent(event);
|
|
221
|
+
const event = document.createEvent('Event');
|
|
222
|
+
event.initEvent(name);
|
|
223
|
+
Object.assign(event, data || {});
|
|
224
|
+
element.dispatchEvent(event);
|
|
268
225
|
}
|
|
269
226
|
|
|
270
|
-
/** The default environment options. */
|
|
271
227
|
const defaultEnvironmentOptions = {
|
|
272
|
-
|
|
228
|
+
queryFn: async (selector, root) => root().findElements(webdriver.By.css(selector))
|
|
273
229
|
};
|
|
274
|
-
/**
|
|
275
|
-
* This function is meant to be executed in the browser. It taps into the hooks exposed by Angular
|
|
276
|
-
* and invokes the specified `callback` when the application is stable (no more pending tasks).
|
|
277
|
-
*/
|
|
278
230
|
function whenStable(callback) {
|
|
279
|
-
|
|
231
|
+
Promise.all(window.frameworkStabilizers.map(stabilizer => new Promise(stabilizer))).then(callback);
|
|
280
232
|
}
|
|
281
|
-
/**
|
|
282
|
-
* This function is meant to be executed in the browser. It checks whether the Angular framework has
|
|
283
|
-
* bootstrapped yet.
|
|
284
|
-
*/
|
|
285
233
|
function isBootstrapped() {
|
|
286
|
-
|
|
234
|
+
return !!window.frameworkStabilizers;
|
|
287
235
|
}
|
|
288
|
-
/** Waits for angular to be ready after the page load. */
|
|
289
236
|
async function waitForAngularReady(wd) {
|
|
290
|
-
|
|
291
|
-
|
|
237
|
+
await wd.wait(() => wd.executeScript(isBootstrapped));
|
|
238
|
+
await wd.executeAsyncScript(whenStable);
|
|
292
239
|
}
|
|
293
|
-
/** A `HarnessEnvironment` implementation for WebDriver. */
|
|
294
240
|
class SeleniumWebDriverHarnessEnvironment extends HarnessEnvironment {
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
}
|
|
332
|
-
/** Creates a `TestElement` from a raw element. */
|
|
333
|
-
createTestElement(element) {
|
|
334
|
-
return new SeleniumWebDriverElement(element, this._stabilizeCallback);
|
|
335
|
-
}
|
|
336
|
-
/** Creates a `HarnessLoader` rooted at the given raw element. */
|
|
337
|
-
createEnvironment(element) {
|
|
338
|
-
return new SeleniumWebDriverHarnessEnvironment(element, this._options);
|
|
339
|
-
}
|
|
340
|
-
// Note: This seems to be working, though we may need to re-evaluate if we encounter issues with
|
|
341
|
-
// stale element references. `() => Promise<webdriver.WebElement[]>` seems like a more correct
|
|
342
|
-
// return type, though supporting it would require changes to the public harness API.
|
|
343
|
-
/**
|
|
344
|
-
* Gets a list of all elements matching the given selector under this environment's root element.
|
|
345
|
-
*/
|
|
346
|
-
async getAllRawElements(selector) {
|
|
347
|
-
const els = await this._options.queryFn(selector, this.rawRootElement);
|
|
348
|
-
return els.map((x) => () => x);
|
|
349
|
-
}
|
|
241
|
+
_options;
|
|
242
|
+
_stabilizeCallback;
|
|
243
|
+
constructor(rawRootElement, options) {
|
|
244
|
+
super(rawRootElement);
|
|
245
|
+
this._options = {
|
|
246
|
+
...defaultEnvironmentOptions,
|
|
247
|
+
...options
|
|
248
|
+
};
|
|
249
|
+
this._stabilizeCallback = () => this.forceStabilize();
|
|
250
|
+
}
|
|
251
|
+
static getNativeElement(el) {
|
|
252
|
+
if (el instanceof SeleniumWebDriverElement) {
|
|
253
|
+
return el.element();
|
|
254
|
+
}
|
|
255
|
+
throw Error('This TestElement was not created by the WebDriverHarnessEnvironment');
|
|
256
|
+
}
|
|
257
|
+
static loader(driver, options) {
|
|
258
|
+
return new SeleniumWebDriverHarnessEnvironment(() => driver.findElement(webdriver.By.css('body')), options);
|
|
259
|
+
}
|
|
260
|
+
async forceStabilize() {
|
|
261
|
+
await this.rawRootElement().getDriver().executeAsyncScript(whenStable);
|
|
262
|
+
}
|
|
263
|
+
async waitForTasksOutsideAngular() {}
|
|
264
|
+
getDocumentRoot() {
|
|
265
|
+
return () => this.rawRootElement().getDriver().findElement(webdriver.By.css('body'));
|
|
266
|
+
}
|
|
267
|
+
createTestElement(element) {
|
|
268
|
+
return new SeleniumWebDriverElement(element, this._stabilizeCallback);
|
|
269
|
+
}
|
|
270
|
+
createEnvironment(element) {
|
|
271
|
+
return new SeleniumWebDriverHarnessEnvironment(element, this._options);
|
|
272
|
+
}
|
|
273
|
+
async getAllRawElements(selector) {
|
|
274
|
+
const els = await this._options.queryFn(selector, this.rawRootElement);
|
|
275
|
+
return els.map(x => () => x);
|
|
276
|
+
}
|
|
350
277
|
}
|
|
351
278
|
|
|
352
279
|
export { SeleniumWebDriverElement, SeleniumWebDriverHarnessEnvironment, waitForAngularReady };
|