@fluentui-react-native/tester 0.129.6 → 0.129.7
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/CHANGELOG.json +73 -1
- package/CHANGELOG.md +17 -2
- package/lib/E2E/src/FocusZone/consts.d.ts +3 -2
- package/lib/E2E/src/FocusZone/consts.d.ts.map +1 -1
- package/lib/E2E/src/FocusZone/consts.js.map +1 -1
- package/lib/E2E/src/FocusZone/pages/FocusZonePageObject.d.ts +28 -0
- package/lib/E2E/src/FocusZone/pages/FocusZonePageObject.d.ts.map +1 -0
- package/lib/E2E/src/FocusZone/pages/FocusZonePageObject.js +189 -0
- package/lib/E2E/src/FocusZone/pages/FocusZonePageObject.js.map +1 -0
- package/lib/E2E/src/common/BasePage.d.ts +67 -0
- package/lib/E2E/src/common/BasePage.d.ts.map +1 -0
- package/lib/E2E/src/common/BasePage.js +654 -0
- package/lib/E2E/src/common/BasePage.js.map +1 -0
- package/lib/E2E/src/common/consts.d.ts.map +1 -1
- package/lib/E2E/src/common/consts.js.map +1 -1
- package/lib/fluent-tester/src/TestComponents/Button/ButtonSizeTestSection.d.ts.map +1 -1
- package/lib/fluent-tester/src/TestComponents/Button/ButtonSizeTestSection.js +2 -1
- package/lib/fluent-tester/src/TestComponents/Button/ButtonSizeTestSection.js.map +1 -1
- package/lib/fluent-tester/src/TestComponents/Button/ButtonTest.d.ts.map +1 -1
- package/lib/fluent-tester/src/TestComponents/Button/ButtonTest.js +4 -2
- package/lib/fluent-tester/src/TestComponents/Button/ButtonTest.js.map +1 -1
- package/lib/fluent-tester/src/TestComponents/Button/ButtonVariantTestSection.android.d.ts +1 -2
- package/lib/fluent-tester/src/TestComponents/Button/ButtonVariantTestSection.android.d.ts.map +1 -1
- package/lib/fluent-tester/src/TestComponents/Button/ButtonVariantTestSection.android.js +1 -22
- package/lib/fluent-tester/src/TestComponents/Button/ButtonVariantTestSection.android.js.map +1 -1
- package/lib/fluent-tester/src/TestComponents/Button/ButtonVariantTestSection.ios.d.ts +2 -0
- package/lib/fluent-tester/src/TestComponents/Button/ButtonVariantTestSection.ios.d.ts.map +1 -0
- package/lib/fluent-tester/src/TestComponents/Button/ButtonVariantTestSection.ios.js +2 -0
- package/lib/fluent-tester/src/TestComponents/Button/ButtonVariantTestSection.ios.js.map +1 -0
- package/lib/fluent-tester/src/TestComponents/Button/ButtonVariantTestSection.mobile.d.ts +3 -0
- package/lib/fluent-tester/src/TestComponents/Button/ButtonVariantTestSection.mobile.d.ts.map +1 -0
- package/lib/fluent-tester/src/TestComponents/Button/ButtonVariantTestSection.mobile.js +23 -0
- package/lib/fluent-tester/src/TestComponents/Button/ButtonVariantTestSection.mobile.js.map +1 -0
- package/lib/fluent-tester/src/TestComponents/FocusZone/FocusZoneE2ETest.d.ts +1 -1
- package/lib/fluent-tester/src/TestComponents/FocusZone/FocusZoneE2ETest.d.ts.map +1 -1
- package/lib/fluent-tester/src/TestComponents/FocusZone/FocusZoneE2ETest.js +5 -5
- package/lib/fluent-tester/src/TestComponents/FocusZone/FocusZoneE2ETest.js.map +1 -1
- package/lib-commonjs/E2E/src/FocusZone/consts.d.ts +3 -2
- package/lib-commonjs/E2E/src/FocusZone/consts.d.ts.map +1 -1
- package/lib-commonjs/E2E/src/FocusZone/consts.js.map +1 -1
- package/lib-commonjs/E2E/src/FocusZone/pages/FocusZonePageObject.d.ts +28 -0
- package/lib-commonjs/E2E/src/FocusZone/pages/FocusZonePageObject.d.ts.map +1 -0
- package/lib-commonjs/E2E/src/FocusZone/pages/FocusZonePageObject.js +191 -0
- package/lib-commonjs/E2E/src/FocusZone/pages/FocusZonePageObject.js.map +1 -0
- package/lib-commonjs/E2E/src/common/BasePage.d.ts +67 -0
- package/lib-commonjs/E2E/src/common/BasePage.d.ts.map +1 -0
- package/lib-commonjs/E2E/src/common/BasePage.js +658 -0
- package/lib-commonjs/E2E/src/common/BasePage.js.map +1 -0
- package/lib-commonjs/E2E/src/common/consts.d.ts.map +1 -1
- package/lib-commonjs/E2E/src/common/consts.js.map +1 -1
- package/lib-commonjs/fluent-tester/src/TestComponents/Button/ButtonSizeTestSection.d.ts.map +1 -1
- package/lib-commonjs/fluent-tester/src/TestComponents/Button/ButtonSizeTestSection.js +2 -1
- package/lib-commonjs/fluent-tester/src/TestComponents/Button/ButtonSizeTestSection.js.map +1 -1
- package/lib-commonjs/fluent-tester/src/TestComponents/Button/ButtonTest.d.ts.map +1 -1
- package/lib-commonjs/fluent-tester/src/TestComponents/Button/ButtonTest.js +4 -2
- package/lib-commonjs/fluent-tester/src/TestComponents/Button/ButtonTest.js.map +1 -1
- package/lib-commonjs/fluent-tester/src/TestComponents/Button/ButtonVariantTestSection.android.d.ts +1 -2
- package/lib-commonjs/fluent-tester/src/TestComponents/Button/ButtonVariantTestSection.android.d.ts.map +1 -1
- package/lib-commonjs/fluent-tester/src/TestComponents/Button/ButtonVariantTestSection.android.js +2 -24
- package/lib-commonjs/fluent-tester/src/TestComponents/Button/ButtonVariantTestSection.android.js.map +1 -1
- package/lib-commonjs/fluent-tester/src/TestComponents/Button/ButtonVariantTestSection.ios.d.ts +2 -0
- package/lib-commonjs/fluent-tester/src/TestComponents/Button/ButtonVariantTestSection.ios.d.ts.map +1 -0
- package/lib-commonjs/fluent-tester/src/TestComponents/Button/ButtonVariantTestSection.ios.js +6 -0
- package/lib-commonjs/fluent-tester/src/TestComponents/Button/ButtonVariantTestSection.ios.js.map +1 -0
- package/lib-commonjs/fluent-tester/src/TestComponents/Button/ButtonVariantTestSection.mobile.d.ts +3 -0
- package/lib-commonjs/fluent-tester/src/TestComponents/Button/ButtonVariantTestSection.mobile.d.ts.map +1 -0
- package/lib-commonjs/fluent-tester/src/TestComponents/Button/ButtonVariantTestSection.mobile.js +28 -0
- package/lib-commonjs/fluent-tester/src/TestComponents/Button/ButtonVariantTestSection.mobile.js.map +1 -0
- package/lib-commonjs/fluent-tester/src/TestComponents/FocusZone/FocusZoneE2ETest.d.ts +1 -1
- package/lib-commonjs/fluent-tester/src/TestComponents/FocusZone/FocusZoneE2ETest.d.ts.map +1 -1
- package/lib-commonjs/fluent-tester/src/TestComponents/FocusZone/FocusZoneE2ETest.js +14 -14
- package/lib-commonjs/fluent-tester/src/TestComponents/FocusZone/FocusZoneE2ETest.js.map +1 -1
- package/package.json +8 -8
- package/src/TestComponents/Button/ButtonSizeTestSection.tsx +3 -1
- package/src/TestComponents/Button/ButtonTest.tsx +4 -2
- package/src/TestComponents/Button/ButtonVariantTestSection.android.tsx +1 -45
- package/src/TestComponents/Button/ButtonVariantTestSection.ios.tsx +1 -0
- package/src/TestComponents/Button/ButtonVariantTestSection.mobile.tsx +45 -0
- package/src/TestComponents/FocusZone/FocusZoneE2ETest.tsx +15 -14
- package/tsconfig.json +2 -1
|
@@ -0,0 +1,654 @@
|
|
|
1
|
+
import { __awaiter, __generator } from "tslib";
|
|
2
|
+
import { ROOT_VIEW, TESTPAGE_CONTENT_SCROLLVIEWER } from './consts';
|
|
3
|
+
import { attributeToEnumName, TESTPAGE_BUTTONS_SCROLLVIEWER } from './consts';
|
|
4
|
+
var DUMMY_CHAR = '';
|
|
5
|
+
// The E2ETEST_PLATFORM environment variable should be set in the beforeSession hook in the wdio.conf file for the respective platform
|
|
6
|
+
var PLATFORM = process.env['E2ETEST_PLATFORM'];
|
|
7
|
+
export var COMPONENT_SCROLL_COORDINATES = { x: -0, y: -100 }; // These are the offsets. Y is negative because we want the touch to move up (and thus it scrolls down)
|
|
8
|
+
var rootView = null;
|
|
9
|
+
/* Win32/UWP-Specific Selector. We use this to get elements on the test page */
|
|
10
|
+
export function By(identifier) {
|
|
11
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
12
|
+
return __generator(this, function (_a) {
|
|
13
|
+
switch (_a.label) {
|
|
14
|
+
case 0:
|
|
15
|
+
if (!(PLATFORM === 'windows')) return [3 /*break*/, 2];
|
|
16
|
+
return [4 /*yield*/, $('~' + identifier)];
|
|
17
|
+
case 1:
|
|
18
|
+
// For some reason, the rootView node is never put into the element tree on the UWP tester. Remove this when fixed.
|
|
19
|
+
return [2 /*return*/, _a.sent()];
|
|
20
|
+
case 2: return [4 /*yield*/, QueryWithChaining(identifier)];
|
|
21
|
+
case 3: return [2 /*return*/, _a.sent()];
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
function QueryWithChaining(identifier) {
|
|
27
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
28
|
+
var selector, queryResult;
|
|
29
|
+
return __generator(this, function (_a) {
|
|
30
|
+
switch (_a.label) {
|
|
31
|
+
case 0:
|
|
32
|
+
if (!(rootView === null)) return [3 /*break*/, 2];
|
|
33
|
+
return [4 /*yield*/, $('~' + ROOT_VIEW)];
|
|
34
|
+
case 1:
|
|
35
|
+
// Most of the elements we're searching for will be children of this rootView node.
|
|
36
|
+
rootView = _a.sent();
|
|
37
|
+
_a.label = 2;
|
|
38
|
+
case 2:
|
|
39
|
+
selector = '~' + identifier;
|
|
40
|
+
return [4 /*yield*/, rootView.$(selector)];
|
|
41
|
+
case 3:
|
|
42
|
+
queryResult = _a.sent();
|
|
43
|
+
if (!queryResult.error) return [3 /*break*/, 5];
|
|
44
|
+
return [4 /*yield*/, $(selector)];
|
|
45
|
+
case 4:
|
|
46
|
+
// In some cases, such as opened ContextualMenu items, the element nodes are not children of the rootView node, meaning we need to start our search from the top of the tree.
|
|
47
|
+
queryResult = _a.sent();
|
|
48
|
+
_a.label = 5;
|
|
49
|
+
case 5: return [2 /*return*/, queryResult];
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
/****************************** IMPORTANT! PLEASE READ! **************************************************
|
|
55
|
+
* Every component's page object extends this. We can assume each test page will interact with at least
|
|
56
|
+
* two UI elements, so we'll add integration for two UI elements in this file (See *Getters* section below).
|
|
57
|
+
* Every additional UI element that a test page wants to interact with should be handled in that respective
|
|
58
|
+
* component's page object.
|
|
59
|
+
*********************************************************************************************************/
|
|
60
|
+
var BasePage = /** @class */ (function () {
|
|
61
|
+
function BasePage() {
|
|
62
|
+
// Default timeout to wait until page is displayed (10s)
|
|
63
|
+
this.waitForUiEvent = 25000;
|
|
64
|
+
if (PLATFORM) {
|
|
65
|
+
this.platform = PLATFORM;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
/******************************************************************/
|
|
69
|
+
/**************** UI Element Interaction Methods ******************/
|
|
70
|
+
/******************************************************************/
|
|
71
|
+
/**
|
|
72
|
+
* Checks to see if an element attribute is strictly equal to an expected value the user passes in.
|
|
73
|
+
* The advantage to this over testing using .isEqual in a spec is that this throws a detailed error if
|
|
74
|
+
* the expected and actual values don't match. This should be called for attribute tests in specs. */
|
|
75
|
+
BasePage.prototype.compareAttribute = function (element, attribute, expectedValue) {
|
|
76
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
77
|
+
var el, actualValue, _a, _b, _c;
|
|
78
|
+
return __generator(this, function (_d) {
|
|
79
|
+
switch (_d.label) {
|
|
80
|
+
case 0: return [4 /*yield*/, element];
|
|
81
|
+
case 1:
|
|
82
|
+
el = _d.sent();
|
|
83
|
+
return [4 /*yield*/, el.getAttribute(attribute)];
|
|
84
|
+
case 2:
|
|
85
|
+
actualValue = _d.sent();
|
|
86
|
+
if (!(expectedValue !== actualValue)) return [3 /*break*/, 4];
|
|
87
|
+
_a = Error.bind;
|
|
88
|
+
_c = (_b = "On ".concat(this._pageName, ", a test component with a testID = '")).concat;
|
|
89
|
+
return [4 /*yield*/, el.getAttribute("AutomationId" /* Attribute.TestID */)];
|
|
90
|
+
case 3: throw new (_a.apply(Error, [void 0, _c.apply(_b, [_d.sent(), "' should have attribute, "]).concat(attributeToEnumName[attribute], " (which maps to windows attribute '").concat(attribute, "' property on the element), equal to '").concat(expectedValue, "'. Instead, ").concat(attributeToEnumName[attribute], " is equal to '").concat(actualValue, "'.")]))();
|
|
91
|
+
case 4: return [2 /*return*/, true];
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
};
|
|
96
|
+
BasePage.prototype.getAccessibilityRole = function () {
|
|
97
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
98
|
+
var _a;
|
|
99
|
+
return __generator(this, function (_b) {
|
|
100
|
+
switch (_b.label) {
|
|
101
|
+
case 0:
|
|
102
|
+
_a = this.getElementAttribute;
|
|
103
|
+
return [4 /*yield*/, this._primaryComponent];
|
|
104
|
+
case 1: return [4 /*yield*/, _a.apply(this, [_b.sent(), "ControlType" /* Attribute.AccessibilityRole */])];
|
|
105
|
+
case 2: return [2 /*return*/, _b.sent()];
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
});
|
|
109
|
+
};
|
|
110
|
+
/* Gets the accessibility label of an UI element given the selector */
|
|
111
|
+
BasePage.prototype.getAccessibilityLabel = function (componentSelector) {
|
|
112
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
113
|
+
var _a, _b, _c;
|
|
114
|
+
return __generator(this, function (_d) {
|
|
115
|
+
switch (_d.label) {
|
|
116
|
+
case 0:
|
|
117
|
+
_a = componentSelector;
|
|
118
|
+
switch (_a) {
|
|
119
|
+
case 'Primary': return [3 /*break*/, 1];
|
|
120
|
+
case 'Secondary': return [3 /*break*/, 4];
|
|
121
|
+
}
|
|
122
|
+
return [3 /*break*/, 7];
|
|
123
|
+
case 1:
|
|
124
|
+
_b = this.getElementAttribute;
|
|
125
|
+
return [4 /*yield*/, this._primaryComponent];
|
|
126
|
+
case 2: return [4 /*yield*/, _b.apply(this, [_d.sent(), "Name" /* Attribute.AccessibilityLabel */])];
|
|
127
|
+
case 3: return [2 /*return*/, _d.sent()];
|
|
128
|
+
case 4:
|
|
129
|
+
_c = this.getElementAttribute;
|
|
130
|
+
return [4 /*yield*/, this._secondaryComponent];
|
|
131
|
+
case 5: return [4 /*yield*/, _c.apply(this, [_d.sent(), "Name" /* Attribute.AccessibilityLabel */])];
|
|
132
|
+
case 6: return [2 /*return*/, _d.sent()];
|
|
133
|
+
case 7: return [2 /*return*/];
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
});
|
|
137
|
+
};
|
|
138
|
+
/* Returns true if the test page has loaded. To determine if it's loaded, each test page has a specific UI element we attempt to locate.
|
|
139
|
+
* If this UI element is located, we know the page as loaded correctly. The UI element we look for is a Text component that contains
|
|
140
|
+
* the title of the page (this._testPage returns that UI element) */
|
|
141
|
+
BasePage.prototype.isPageLoaded = function () {
|
|
142
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
143
|
+
var onPage, _a;
|
|
144
|
+
return __generator(this, function (_b) {
|
|
145
|
+
switch (_b.label) {
|
|
146
|
+
case 0: return [4 /*yield*/, this._testPage];
|
|
147
|
+
case 1: return [4 /*yield*/, (_b.sent()).isDisplayed()];
|
|
148
|
+
case 2:
|
|
149
|
+
_a = (_b.sent());
|
|
150
|
+
if (_a) return [3 /*break*/, 5];
|
|
151
|
+
return [4 /*yield*/, this._primaryComponent];
|
|
152
|
+
case 3: return [4 /*yield*/, (_b.sent()).isDisplayed()];
|
|
153
|
+
case 4:
|
|
154
|
+
_a = (_b.sent());
|
|
155
|
+
_b.label = 5;
|
|
156
|
+
case 5:
|
|
157
|
+
onPage = _a;
|
|
158
|
+
return [2 /*return*/, onPage];
|
|
159
|
+
}
|
|
160
|
+
});
|
|
161
|
+
});
|
|
162
|
+
};
|
|
163
|
+
/* Returns true if the test page's button is displayed (the button that navigates to each test page) */
|
|
164
|
+
BasePage.prototype.isButtonInView = function () {
|
|
165
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
166
|
+
return __generator(this, function (_a) {
|
|
167
|
+
switch (_a.label) {
|
|
168
|
+
case 0: return [4 /*yield*/, this._pageButton];
|
|
169
|
+
case 1: return [4 /*yield*/, (_a.sent()).isDisplayed()];
|
|
170
|
+
case 2: return [2 /*return*/, _a.sent()];
|
|
171
|
+
}
|
|
172
|
+
});
|
|
173
|
+
});
|
|
174
|
+
};
|
|
175
|
+
BasePage.prototype.clickComponent = function () {
|
|
176
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
177
|
+
return __generator(this, function (_a) {
|
|
178
|
+
switch (_a.label) {
|
|
179
|
+
case 0: return [4 /*yield*/, this._primaryComponent];
|
|
180
|
+
case 1: return [4 /*yield*/, (_a.sent()).click()];
|
|
181
|
+
case 2:
|
|
182
|
+
_a.sent();
|
|
183
|
+
return [2 /*return*/];
|
|
184
|
+
}
|
|
185
|
+
});
|
|
186
|
+
});
|
|
187
|
+
};
|
|
188
|
+
/* The goal of click() and sendKeys() is to be generally used across all pageobjects to reduce code repetition in similar methods. */
|
|
189
|
+
BasePage.prototype.click = function (element) {
|
|
190
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
191
|
+
return __generator(this, function (_a) {
|
|
192
|
+
switch (_a.label) {
|
|
193
|
+
case 0: return [4 /*yield*/, element];
|
|
194
|
+
case 1: return [4 /*yield*/, (_a.sent()).click()];
|
|
195
|
+
case 2:
|
|
196
|
+
_a.sent();
|
|
197
|
+
return [2 /*return*/];
|
|
198
|
+
}
|
|
199
|
+
});
|
|
200
|
+
});
|
|
201
|
+
};
|
|
202
|
+
BasePage.prototype.sendKeys = function (element, keys) {
|
|
203
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
204
|
+
return __generator(this, function (_a) {
|
|
205
|
+
switch (_a.label) {
|
|
206
|
+
case 0: return [4 /*yield*/, element];
|
|
207
|
+
case 1: return [4 /*yield*/, (_a.sent()).addValue(keys)];
|
|
208
|
+
case 2:
|
|
209
|
+
_a.sent();
|
|
210
|
+
return [2 /*return*/];
|
|
211
|
+
}
|
|
212
|
+
});
|
|
213
|
+
});
|
|
214
|
+
};
|
|
215
|
+
BasePage.prototype.getElementAttribute = function (element, attribute) {
|
|
216
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
217
|
+
return __generator(this, function (_a) {
|
|
218
|
+
switch (_a.label) {
|
|
219
|
+
case 0: return [4 /*yield*/, element.getAttribute(attribute)];
|
|
220
|
+
case 1: return [2 /*return*/, _a.sent()];
|
|
221
|
+
}
|
|
222
|
+
});
|
|
223
|
+
});
|
|
224
|
+
};
|
|
225
|
+
/* Scrolls until the desired test page's button is displayed. We use the scroll viewer UI element as the point to start scrolling.
|
|
226
|
+
* We use a negative number as the Y-coordinate because that enables us to scroll downwards */
|
|
227
|
+
BasePage.prototype.mobileScrollToComponentButton = function () {
|
|
228
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
229
|
+
var errorMsg, _a;
|
|
230
|
+
var _this = this;
|
|
231
|
+
return __generator(this, function (_b) {
|
|
232
|
+
switch (_b.label) {
|
|
233
|
+
case 0: return [4 /*yield*/, this._pageButton];
|
|
234
|
+
case 1: return [4 /*yield*/, (_b.sent()).isDisplayed()];
|
|
235
|
+
case 2:
|
|
236
|
+
if (_b.sent()) {
|
|
237
|
+
return [2 /*return*/];
|
|
238
|
+
}
|
|
239
|
+
errorMsg = 'Could not scroll to the ' + this._pageName + "'s Button. Please see Pipeline artifacts for more debugging information.";
|
|
240
|
+
_a = this.platform;
|
|
241
|
+
switch (_a) {
|
|
242
|
+
case 'ios': return [3 /*break*/, 3];
|
|
243
|
+
case 'android': return [3 /*break*/, 5];
|
|
244
|
+
}
|
|
245
|
+
return [3 /*break*/, 5];
|
|
246
|
+
case 3: return [4 /*yield*/, browser.waitUntil(function () { return __awaiter(_this, void 0, void 0, function () {
|
|
247
|
+
return __generator(this, function (_a) {
|
|
248
|
+
switch (_a.label) {
|
|
249
|
+
case 0: return [4 /*yield*/, driver.execute('mobile: scroll', { direction: 'down' })];
|
|
250
|
+
case 1:
|
|
251
|
+
_a.sent();
|
|
252
|
+
return [4 /*yield*/, this._pageButton];
|
|
253
|
+
case 2: return [4 /*yield*/, (_a.sent()).isDisplayed()];
|
|
254
|
+
case 3: return [2 /*return*/, _a.sent()];
|
|
255
|
+
}
|
|
256
|
+
});
|
|
257
|
+
}); }, {
|
|
258
|
+
timeout: this.waitForUiEvent,
|
|
259
|
+
timeoutMsg: errorMsg,
|
|
260
|
+
})];
|
|
261
|
+
case 4:
|
|
262
|
+
_b.sent();
|
|
263
|
+
return [3 /*break*/, 7];
|
|
264
|
+
case 5:
|
|
265
|
+
/* 'mobile: scroll' which is used for iOS, does not support direction option on Android.
|
|
266
|
+
* Instead, we use the UiScrollable class to scroll down to the desired view based on its 'description' (accessibilityLabel).
|
|
267
|
+
* The first selector tells which container to scroll in, and the other selector tells which component to scroll to. */
|
|
268
|
+
return [4 /*yield*/, browser.waitUntil(function () { return __awaiter(_this, void 0, void 0, function () {
|
|
269
|
+
var buttonElementSelector, pageButton;
|
|
270
|
+
return __generator(this, function (_a) {
|
|
271
|
+
switch (_a.label) {
|
|
272
|
+
case 0:
|
|
273
|
+
buttonElementSelector = "new UiScrollable(new UiSelector().description(\"".concat(TESTPAGE_BUTTONS_SCROLLVIEWER, "\").scrollable(true)).setMaxSearchSwipes(10).setAsVerticalList().scrollIntoView(new UiSelector().description(\"").concat(this._pageButtonName, "\"))");
|
|
274
|
+
return [4 /*yield*/, $("android=".concat(buttonElementSelector))];
|
|
275
|
+
case 1:
|
|
276
|
+
pageButton = _a.sent();
|
|
277
|
+
return [4 /*yield*/, pageButton.isDisplayed()];
|
|
278
|
+
case 2: return [2 /*return*/, _a.sent()];
|
|
279
|
+
}
|
|
280
|
+
});
|
|
281
|
+
}); }, {
|
|
282
|
+
timeout: this.waitForUiEvent,
|
|
283
|
+
timeoutMsg: errorMsg,
|
|
284
|
+
})];
|
|
285
|
+
case 6:
|
|
286
|
+
/* 'mobile: scroll' which is used for iOS, does not support direction option on Android.
|
|
287
|
+
* Instead, we use the UiScrollable class to scroll down to the desired view based on its 'description' (accessibilityLabel).
|
|
288
|
+
* The first selector tells which container to scroll in, and the other selector tells which component to scroll to. */
|
|
289
|
+
_b.sent();
|
|
290
|
+
return [3 /*break*/, 7];
|
|
291
|
+
case 7: return [2 /*return*/];
|
|
292
|
+
}
|
|
293
|
+
});
|
|
294
|
+
});
|
|
295
|
+
};
|
|
296
|
+
/* Waits for the test page to load. If the test page doesn't load before the timeout, it causes the test to fail. */
|
|
297
|
+
BasePage.prototype.waitForPageDisplayed = function (timeout) {
|
|
298
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
299
|
+
var _this = this;
|
|
300
|
+
return __generator(this, function (_a) {
|
|
301
|
+
switch (_a.label) {
|
|
302
|
+
case 0: return [4 /*yield*/, browser.waitUntil(function () { return __awaiter(_this, void 0, void 0, function () { return __generator(this, function (_a) {
|
|
303
|
+
switch (_a.label) {
|
|
304
|
+
case 0: return [4 /*yield*/, this.isPageLoaded()];
|
|
305
|
+
case 1: return [2 /*return*/, _a.sent()];
|
|
306
|
+
}
|
|
307
|
+
}); }); }, {
|
|
308
|
+
timeout: timeout !== null && timeout !== void 0 ? timeout : this.waitForUiEvent,
|
|
309
|
+
timeoutMsg: this._pageName + ' did not render correctly. Please see /errorShots for more information.',
|
|
310
|
+
interval: 1500,
|
|
311
|
+
})];
|
|
312
|
+
case 1:
|
|
313
|
+
_a.sent();
|
|
314
|
+
return [2 /*return*/];
|
|
315
|
+
}
|
|
316
|
+
});
|
|
317
|
+
});
|
|
318
|
+
};
|
|
319
|
+
/* Waits for the test page's button to be displayed. If the button doesn't load before the timeout, it causes the test to fail. */
|
|
320
|
+
BasePage.prototype.waitForButtonDisplayed = function (timeout) {
|
|
321
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
322
|
+
var _this = this;
|
|
323
|
+
return __generator(this, function (_a) {
|
|
324
|
+
switch (_a.label) {
|
|
325
|
+
case 0: return [4 /*yield*/, browser.waitUntil(function () { return __awaiter(_this, void 0, void 0, function () { return __generator(this, function (_a) {
|
|
326
|
+
switch (_a.label) {
|
|
327
|
+
case 0: return [4 /*yield*/, this.isButtonInView()];
|
|
328
|
+
case 1: return [2 /*return*/, _a.sent()];
|
|
329
|
+
}
|
|
330
|
+
}); }); }, {
|
|
331
|
+
timeout: timeout !== null && timeout !== void 0 ? timeout : this.waitForUiEvent,
|
|
332
|
+
timeoutMsg: 'Could not find the button to navigate to ' + this._pageName + '. Please see /errorShots for more information.',
|
|
333
|
+
interval: 1500,
|
|
334
|
+
})];
|
|
335
|
+
case 1:
|
|
336
|
+
_a.sent();
|
|
337
|
+
return [2 /*return*/];
|
|
338
|
+
}
|
|
339
|
+
});
|
|
340
|
+
});
|
|
341
|
+
};
|
|
342
|
+
/* @deprecated, only use `scrollToTestElement()` instead */
|
|
343
|
+
BasePage.prototype.waitForPrimaryElementDisplayed = function (timeout) {
|
|
344
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
345
|
+
var _this = this;
|
|
346
|
+
return __generator(this, function (_a) {
|
|
347
|
+
switch (_a.label) {
|
|
348
|
+
case 0:
|
|
349
|
+
console.warn('`waitForPrimaryElementDisplayed` is deprecated. Only use `scrollToTestElement` in your spec to improve performance.');
|
|
350
|
+
return [4 /*yield*/, browser.waitUntil(function () { return __awaiter(_this, void 0, void 0, function () { return __generator(this, function (_a) {
|
|
351
|
+
switch (_a.label) {
|
|
352
|
+
case 0: return [4 /*yield*/, this._primaryComponent];
|
|
353
|
+
case 1: return [4 /*yield*/, (_a.sent()).isDisplayed()];
|
|
354
|
+
case 2: return [2 /*return*/, _a.sent()];
|
|
355
|
+
}
|
|
356
|
+
}); }); }, {
|
|
357
|
+
timeout: timeout !== null && timeout !== void 0 ? timeout : this.waitForUiEvent,
|
|
358
|
+
timeoutMsg: 'The UI element for testing did not display correctly. Please see /errorShots of the first failed test for more information.',
|
|
359
|
+
interval: 1500,
|
|
360
|
+
})];
|
|
361
|
+
case 1:
|
|
362
|
+
_a.sent();
|
|
363
|
+
return [2 /*return*/];
|
|
364
|
+
}
|
|
365
|
+
});
|
|
366
|
+
});
|
|
367
|
+
};
|
|
368
|
+
/* Scrolls to the specified or primary UI test element until it is displayed. */
|
|
369
|
+
BasePage.prototype.scrollToTestElement = function (component) {
|
|
370
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
371
|
+
var ComponentToScrollTo, _a, FocusButton, scrollDownKeys;
|
|
372
|
+
var _this = this;
|
|
373
|
+
return __generator(this, function (_b) {
|
|
374
|
+
switch (_b.label) {
|
|
375
|
+
case 0:
|
|
376
|
+
if (!(component !== null && component !== void 0)) return [3 /*break*/, 1];
|
|
377
|
+
_a = component;
|
|
378
|
+
return [3 /*break*/, 3];
|
|
379
|
+
case 1: return [4 /*yield*/, this._primaryComponent];
|
|
380
|
+
case 2:
|
|
381
|
+
_a = (_b.sent());
|
|
382
|
+
_b.label = 3;
|
|
383
|
+
case 3:
|
|
384
|
+
ComponentToScrollTo = _a;
|
|
385
|
+
return [4 /*yield*/, ComponentToScrollTo.isDisplayed()];
|
|
386
|
+
case 4:
|
|
387
|
+
if (_b.sent()) {
|
|
388
|
+
return [2 /*return*/];
|
|
389
|
+
}
|
|
390
|
+
return [4 /*yield*/, By('Focus_Button')];
|
|
391
|
+
case 5:
|
|
392
|
+
FocusButton = _b.sent();
|
|
393
|
+
scrollDownKeys = ["\uE00F" /* Keys.PAGE_DOWN */];
|
|
394
|
+
return [4 /*yield*/, browser.waitUntil(function () { return __awaiter(_this, void 0, void 0, function () {
|
|
395
|
+
return __generator(this, function (_a) {
|
|
396
|
+
switch (_a.label) {
|
|
397
|
+
case 0: return [4 /*yield*/, FocusButton.addValue(scrollDownKeys)];
|
|
398
|
+
case 1:
|
|
399
|
+
_a.sent();
|
|
400
|
+
scrollDownKeys.push("\uE00F" /* Keys.PAGE_DOWN */);
|
|
401
|
+
return [4 /*yield*/, ComponentToScrollTo.isDisplayed()];
|
|
402
|
+
case 2: return [2 /*return*/, _a.sent()];
|
|
403
|
+
}
|
|
404
|
+
});
|
|
405
|
+
}); }, {
|
|
406
|
+
timeout: 30000,
|
|
407
|
+
timeoutMsg: 'Could not scroll to the ' +
|
|
408
|
+
this._pageName +
|
|
409
|
+
"'s main test element. Please see Pipeline artifacts for more debugging information.",
|
|
410
|
+
})];
|
|
411
|
+
case 6:
|
|
412
|
+
_b.sent();
|
|
413
|
+
// We have this extra scroll here to ensure the whole component is visible.
|
|
414
|
+
return [4 /*yield*/, FocusButton.addValue(scrollDownKeys)];
|
|
415
|
+
case 7:
|
|
416
|
+
// We have this extra scroll here to ensure the whole component is visible.
|
|
417
|
+
_b.sent();
|
|
418
|
+
return [2 /*return*/];
|
|
419
|
+
}
|
|
420
|
+
});
|
|
421
|
+
});
|
|
422
|
+
};
|
|
423
|
+
/* Scrolls to the specified or primary UI test element until it is displayed. */
|
|
424
|
+
BasePage.prototype.mobileScrollToTestElement = function (componentIdentifier) {
|
|
425
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
426
|
+
var componentToScrollTo, errorMsg, _a;
|
|
427
|
+
var _this = this;
|
|
428
|
+
return __generator(this, function (_b) {
|
|
429
|
+
switch (_b.label) {
|
|
430
|
+
case 0:
|
|
431
|
+
componentToScrollTo = componentIdentifier !== null && componentIdentifier !== void 0 ? componentIdentifier : this._primaryComponentName;
|
|
432
|
+
return [4 /*yield*/, By(componentToScrollTo)];
|
|
433
|
+
case 1: return [4 /*yield*/, (_b.sent()).isDisplayed()];
|
|
434
|
+
case 2:
|
|
435
|
+
if (_b.sent()) {
|
|
436
|
+
return [2 /*return*/];
|
|
437
|
+
}
|
|
438
|
+
errorMsg = 'Could not scroll to the ' + componentToScrollTo + '. Please see Pipeline artifacts for more debugging information.';
|
|
439
|
+
_a = this.platform;
|
|
440
|
+
switch (_a) {
|
|
441
|
+
case 'ios': return [3 /*break*/, 3];
|
|
442
|
+
case 'android': return [3 /*break*/, 5];
|
|
443
|
+
}
|
|
444
|
+
return [3 /*break*/, 7];
|
|
445
|
+
case 3: return [4 /*yield*/, browser.waitUntil(function () { return __awaiter(_this, void 0, void 0, function () {
|
|
446
|
+
return __generator(this, function (_a) {
|
|
447
|
+
switch (_a.label) {
|
|
448
|
+
case 0: return [4 /*yield*/, driver.execute('mobile: scroll', { direction: 'down' })];
|
|
449
|
+
case 1:
|
|
450
|
+
_a.sent();
|
|
451
|
+
return [4 /*yield*/, By(componentToScrollTo)];
|
|
452
|
+
case 2: return [4 /*yield*/, (_a.sent()).isDisplayed()];
|
|
453
|
+
case 3: return [2 /*return*/, _a.sent()];
|
|
454
|
+
}
|
|
455
|
+
});
|
|
456
|
+
}); }, {
|
|
457
|
+
timeout: this.waitForUiEvent,
|
|
458
|
+
timeoutMsg: errorMsg,
|
|
459
|
+
})];
|
|
460
|
+
case 4:
|
|
461
|
+
_b.sent();
|
|
462
|
+
return [3 /*break*/, 7];
|
|
463
|
+
case 5:
|
|
464
|
+
/* 'mobile: scroll' which is used for iOS, does not support direction option on Android.
|
|
465
|
+
* Instead, we use the UiScrollable class to scroll down to the desired view based on its 'description' (accessibilityLabel).
|
|
466
|
+
* The first selector tells which container to scroll in, and the other selector tells which component to scroll to. */
|
|
467
|
+
return [4 /*yield*/, browser.waitUntil(function () { return __awaiter(_this, void 0, void 0, function () {
|
|
468
|
+
var componentSelector, component;
|
|
469
|
+
return __generator(this, function (_a) {
|
|
470
|
+
switch (_a.label) {
|
|
471
|
+
case 0:
|
|
472
|
+
componentSelector = "new UiScrollable(new UiSelector().description(\"".concat(TESTPAGE_CONTENT_SCROLLVIEWER, "\").scrollable(true)).setMaxSearchSwipes(10).scrollIntoView(new UiSelector().description(\"").concat(componentIdentifier, "\"))");
|
|
473
|
+
return [4 /*yield*/, $("android=".concat(componentSelector))];
|
|
474
|
+
case 1:
|
|
475
|
+
component = _a.sent();
|
|
476
|
+
return [4 /*yield*/, component.isDisplayed()];
|
|
477
|
+
case 2: return [2 /*return*/, _a.sent()];
|
|
478
|
+
}
|
|
479
|
+
});
|
|
480
|
+
}); }, {
|
|
481
|
+
timeout: this.waitForUiEvent,
|
|
482
|
+
timeoutMsg: errorMsg,
|
|
483
|
+
})];
|
|
484
|
+
case 6:
|
|
485
|
+
/* 'mobile: scroll' which is used for iOS, does not support direction option on Android.
|
|
486
|
+
* Instead, we use the UiScrollable class to scroll down to the desired view based on its 'description' (accessibilityLabel).
|
|
487
|
+
* The first selector tells which container to scroll in, and the other selector tells which component to scroll to. */
|
|
488
|
+
_b.sent();
|
|
489
|
+
return [3 /*break*/, 7];
|
|
490
|
+
case 7: return [2 /*return*/];
|
|
491
|
+
}
|
|
492
|
+
});
|
|
493
|
+
});
|
|
494
|
+
};
|
|
495
|
+
/* A method that allows the caller to pass in a condition. A wrapper for waitUntil(). Once testing becomes more extensive,
|
|
496
|
+
* this will allow cleaner code within all the Page Objects. */
|
|
497
|
+
BasePage.prototype.waitForCondition = function (condition, errorMsg, timeout, interval) {
|
|
498
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
499
|
+
var _this = this;
|
|
500
|
+
return __generator(this, function (_a) {
|
|
501
|
+
switch (_a.label) {
|
|
502
|
+
case 0: return [4 /*yield*/, browser.waitUntil(function () { return __awaiter(_this, void 0, void 0, function () { return __generator(this, function (_a) {
|
|
503
|
+
switch (_a.label) {
|
|
504
|
+
case 0: return [4 /*yield*/, condition()];
|
|
505
|
+
case 1: return [2 /*return*/, _a.sent()];
|
|
506
|
+
}
|
|
507
|
+
}); }); }, {
|
|
508
|
+
timeout: timeout !== null && timeout !== void 0 ? timeout : this.waitForUiEvent,
|
|
509
|
+
timeoutMsg: errorMsg !== null && errorMsg !== void 0 ? errorMsg : 'Error. Please see /errorShots and logs for more information.',
|
|
510
|
+
interval: interval !== null && interval !== void 0 ? interval : 1000,
|
|
511
|
+
})];
|
|
512
|
+
case 1:
|
|
513
|
+
_a.sent();
|
|
514
|
+
return [2 /*return*/];
|
|
515
|
+
}
|
|
516
|
+
});
|
|
517
|
+
});
|
|
518
|
+
};
|
|
519
|
+
/* FUNCTIONALITY: Determines if an Assert has fired. True if yes; false otherwise
|
|
520
|
+
*
|
|
521
|
+
* WHY/HOW: Unfortunately, some Asserts don't take focus away from the test app. Because of this, the test execution is able
|
|
522
|
+
* to continue without realizing an assert as popped up (unless the assert is a crashing one).
|
|
523
|
+
*
|
|
524
|
+
* In order to detect an assert, this function gets the number of window handles currently open within the test app.
|
|
525
|
+
* Only one instance SHOULD be open at a time (the app itself). If another instance of the app is open, we know an assert dialogue
|
|
526
|
+
* has popped up. With this information, we know to fail the test.
|
|
527
|
+
*
|
|
528
|
+
* Additionally, you might think it's logical to simply place this in the afterEach() hook so it's called after every test (rather than duplicating this call in every test).
|
|
529
|
+
* Unfortunately, afterEach() is designed for setup/teardown - not for determining if a test should fail or not.
|
|
530
|
+
* */
|
|
531
|
+
BasePage.prototype.didAssertPopup = function () {
|
|
532
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
533
|
+
var windowHandles;
|
|
534
|
+
return __generator(this, function (_a) {
|
|
535
|
+
switch (_a.label) {
|
|
536
|
+
case 0:
|
|
537
|
+
if (!(PLATFORM === 'android')) return [3 /*break*/, 2];
|
|
538
|
+
return [4 /*yield*/, this.isPageLoaded()];
|
|
539
|
+
case 1: return [2 /*return*/, !(_a.sent())];
|
|
540
|
+
case 2: return [4 /*yield*/, browser.getWindowHandles()];
|
|
541
|
+
case 3:
|
|
542
|
+
windowHandles = _a.sent();
|
|
543
|
+
return [2 /*return*/, windowHandles.length > 1];
|
|
544
|
+
}
|
|
545
|
+
});
|
|
546
|
+
});
|
|
547
|
+
};
|
|
548
|
+
Object.defineProperty(BasePage.prototype, "_testPage", {
|
|
549
|
+
/*****************************************/
|
|
550
|
+
/**************** Getters ****************/
|
|
551
|
+
/*****************************************/
|
|
552
|
+
/* IMPORTANT:
|
|
553
|
+
* Each PageObject class that extends this class is expected to override these values.
|
|
554
|
+
* This is necessary because each page will have different IDs for their respective UI elements.
|
|
555
|
+
* DUMMY_CHAR is just a placeholder.
|
|
556
|
+
*/
|
|
557
|
+
// Returns: UI Element
|
|
558
|
+
// The Text component on each test page containing the title of that page. We can use this to determine if a test page has loaded correctly.
|
|
559
|
+
get: function () {
|
|
560
|
+
return By(this._pageName);
|
|
561
|
+
},
|
|
562
|
+
enumerable: false,
|
|
563
|
+
configurable: true
|
|
564
|
+
});
|
|
565
|
+
Object.defineProperty(BasePage.prototype, "_primaryComponent", {
|
|
566
|
+
// Returns: UI Element
|
|
567
|
+
// The primary UI element used for testing on the given test page.
|
|
568
|
+
get: function () {
|
|
569
|
+
return By(this._primaryComponentName);
|
|
570
|
+
},
|
|
571
|
+
enumerable: false,
|
|
572
|
+
configurable: true
|
|
573
|
+
});
|
|
574
|
+
Object.defineProperty(BasePage.prototype, "_secondaryComponent", {
|
|
575
|
+
// Returns: UI Element
|
|
576
|
+
// The secondary UI element used for testing on the given test page. Often times, we'll want to set a
|
|
577
|
+
// prop on one component, and not set it on another to verify certain behaviors. This is why we have this secondary component.
|
|
578
|
+
get: function () {
|
|
579
|
+
return By(this._secondaryComponentName);
|
|
580
|
+
},
|
|
581
|
+
enumerable: false,
|
|
582
|
+
configurable: true
|
|
583
|
+
});
|
|
584
|
+
Object.defineProperty(BasePage.prototype, "_pageButton", {
|
|
585
|
+
// Returns: UI Element
|
|
586
|
+
// The button that navigates you to the component's test page.
|
|
587
|
+
get: function () {
|
|
588
|
+
return By(this._pageButtonName);
|
|
589
|
+
},
|
|
590
|
+
enumerable: false,
|
|
591
|
+
configurable: true
|
|
592
|
+
});
|
|
593
|
+
Object.defineProperty(BasePage.prototype, "_primaryComponentName", {
|
|
594
|
+
// Returns: String
|
|
595
|
+
// Returns the identifier of the primary UI element used for testing on the given test page.
|
|
596
|
+
get: function () {
|
|
597
|
+
console.warn('Please verify whether or not your page object should implement _primaryComponentName.');
|
|
598
|
+
return DUMMY_CHAR;
|
|
599
|
+
},
|
|
600
|
+
enumerable: false,
|
|
601
|
+
configurable: true
|
|
602
|
+
});
|
|
603
|
+
Object.defineProperty(BasePage.prototype, "_secondaryComponentName", {
|
|
604
|
+
// Returns: String
|
|
605
|
+
// Returns the identifier of the secondary UI element used for testing on the given test page. Often times, we'll want to set a
|
|
606
|
+
// prop on one component, and not set it on another to verify certain behaviors. This is why we have this secondary component.
|
|
607
|
+
get: function () {
|
|
608
|
+
console.warn('Please verify whether or not your page object should implement _secondaryComponentName.');
|
|
609
|
+
return DUMMY_CHAR;
|
|
610
|
+
},
|
|
611
|
+
enumerable: false,
|
|
612
|
+
configurable: true
|
|
613
|
+
});
|
|
614
|
+
Object.defineProperty(BasePage.prototype, "_testPageButtonScrollViewer", {
|
|
615
|
+
// The scrollviewer containing the list of buttons to navigate to each test page
|
|
616
|
+
get: function () {
|
|
617
|
+
return By(TESTPAGE_BUTTONS_SCROLLVIEWER);
|
|
618
|
+
},
|
|
619
|
+
enumerable: false,
|
|
620
|
+
configurable: true
|
|
621
|
+
});
|
|
622
|
+
Object.defineProperty(BasePage.prototype, "ERRORMESSAGE_SUFFIX", {
|
|
623
|
+
/****************** Error Messages ******************/
|
|
624
|
+
get: function () {
|
|
625
|
+
return 'Please review logs and error screenshots for more information.';
|
|
626
|
+
},
|
|
627
|
+
enumerable: false,
|
|
628
|
+
configurable: true
|
|
629
|
+
});
|
|
630
|
+
Object.defineProperty(BasePage.prototype, "ERRORMESSAGE_APPLOAD", {
|
|
631
|
+
get: function () {
|
|
632
|
+
return 'The FluentTester app did not load correctly. ' + this.ERRORMESSAGE_SUFFIX;
|
|
633
|
+
},
|
|
634
|
+
enumerable: false,
|
|
635
|
+
configurable: true
|
|
636
|
+
});
|
|
637
|
+
Object.defineProperty(BasePage.prototype, "ERRORMESSAGE_PAGELOAD", {
|
|
638
|
+
get: function () {
|
|
639
|
+
return 'The ' + this._pageName + ' test page did not load correctly. ' + this.ERRORMESSAGE_SUFFIX;
|
|
640
|
+
},
|
|
641
|
+
enumerable: false,
|
|
642
|
+
configurable: true
|
|
643
|
+
});
|
|
644
|
+
Object.defineProperty(BasePage.prototype, "ERRORMESSAGE_ASSERT", {
|
|
645
|
+
get: function () {
|
|
646
|
+
return 'An assert popped up. ' + this.ERRORMESSAGE_SUFFIX;
|
|
647
|
+
},
|
|
648
|
+
enumerable: false,
|
|
649
|
+
configurable: true
|
|
650
|
+
});
|
|
651
|
+
return BasePage;
|
|
652
|
+
}());
|
|
653
|
+
export { BasePage };
|
|
654
|
+
//# sourceMappingURL=BasePage.js.map
|