d5-testing-library 1.2.0 → 1.3.0
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/dist/cjs/d5-testing-library.cjs +462 -162
- package/dist/d5-testing-library.d.ts +150 -22
- package/dist/d5-testing-library.legacy-esm.js +455 -158
- package/dist/d5-testing-library.mjs +455 -158
- package/package.json +1 -1
|
@@ -38,11 +38,58 @@ function config(cfg) {
|
|
|
38
38
|
return configInstance.config(cfg);
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
+
// src/pageObjects/AbstractElement.ts
|
|
42
|
+
import { By } from "selenium-webdriver";
|
|
43
|
+
|
|
44
|
+
// src/utils/waitElementIsVisible.ts
|
|
45
|
+
import { until } from "selenium-webdriver";
|
|
46
|
+
|
|
47
|
+
// src/utils/wait.ts
|
|
48
|
+
async function wait(condition, driver, errorMessage, timeout) {
|
|
49
|
+
driver = driver ?? config().driver;
|
|
50
|
+
await driver.wait(condition, timeout ?? config().timeout, errorMessage);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// src/utils/waitElementIsVisible.ts
|
|
54
|
+
async function waitElementIsVisible({
|
|
55
|
+
element,
|
|
56
|
+
timeout,
|
|
57
|
+
errorMessage,
|
|
58
|
+
driver
|
|
59
|
+
}) {
|
|
60
|
+
driver = driver ?? config().driver;
|
|
61
|
+
const condition = until.elementIsVisible(element);
|
|
62
|
+
await wait(condition.fn, driver, errorMessage ?? condition.description(), timeout ?? config().timeout);
|
|
63
|
+
}
|
|
64
|
+
|
|
41
65
|
// src/pageObjects/AbstractElement.ts
|
|
42
66
|
var AbstractElement = class {
|
|
43
67
|
driver;
|
|
44
|
-
|
|
68
|
+
elementCssSelector;
|
|
69
|
+
constructor(driver, elementCssSelector) {
|
|
45
70
|
this.driver = driver || config().driver;
|
|
71
|
+
this.elementCssSelector = elementCssSelector;
|
|
72
|
+
}
|
|
73
|
+
getFullCssSelector() {
|
|
74
|
+
return this.elementCssSelector;
|
|
75
|
+
}
|
|
76
|
+
async getElement() {
|
|
77
|
+
return this.driver.findElement(By.css(this.getFullCssSelector()));
|
|
78
|
+
}
|
|
79
|
+
async getAttribute(attr) {
|
|
80
|
+
const element = await this.getElement();
|
|
81
|
+
await waitElementIsVisible({
|
|
82
|
+
element,
|
|
83
|
+
driver: this.driver,
|
|
84
|
+
errorMessage: `Element not found: ${this.getFullCssSelector()}`
|
|
85
|
+
});
|
|
86
|
+
return await element.getAttribute(attr);
|
|
87
|
+
}
|
|
88
|
+
async getClasses() {
|
|
89
|
+
return await this.getAttribute("class");
|
|
90
|
+
}
|
|
91
|
+
async getDataTestId() {
|
|
92
|
+
return await this.getAttribute("data-testid");
|
|
46
93
|
}
|
|
47
94
|
};
|
|
48
95
|
|
|
@@ -75,13 +122,56 @@ var AbstractPage = class extends AbstractElement {
|
|
|
75
122
|
}
|
|
76
123
|
};
|
|
77
124
|
|
|
125
|
+
// src/utils/actions.ts
|
|
126
|
+
var click = async (webElement, driver) => {
|
|
127
|
+
driver = driver ?? config().driver;
|
|
128
|
+
const actions = driver.actions({ async: true });
|
|
129
|
+
await actions.move({ origin: webElement }).click().perform();
|
|
130
|
+
};
|
|
131
|
+
|
|
78
132
|
// src/utils/locators.ts
|
|
79
|
-
import { By } from "selenium-webdriver";
|
|
133
|
+
import { By as By2 } from "selenium-webdriver";
|
|
80
134
|
var byTestIdCssSelector = (testId, element = "div") => {
|
|
81
135
|
return `${element}[data-testid="${testId}"]`;
|
|
82
136
|
};
|
|
83
137
|
var byTestId = (testId, element = "div") => {
|
|
84
|
-
return
|
|
138
|
+
return By2.css(byTestIdCssSelector(testId, element));
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
// src/pageObjects/pages/LoginPage.ts
|
|
142
|
+
var LOGIN_PATH = "/login";
|
|
143
|
+
var LoginPage = class extends AbstractPage {
|
|
144
|
+
usernameField = byTestId("loginField", "input");
|
|
145
|
+
passwordField = byTestId("passwordField", "input");
|
|
146
|
+
loginButton = byTestId("loginButton");
|
|
147
|
+
async locate() {
|
|
148
|
+
await super.locate();
|
|
149
|
+
const login = await this.driver.findElement(this.usernameField);
|
|
150
|
+
await waitElementIsVisible({
|
|
151
|
+
element: login,
|
|
152
|
+
driver: this.driver
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
get path() {
|
|
156
|
+
return LOGIN_PATH;
|
|
157
|
+
}
|
|
158
|
+
async enterUsername(username) {
|
|
159
|
+
await this.driver.findElement(this.usernameField).sendKeys(username);
|
|
160
|
+
}
|
|
161
|
+
async enterPassword(password) {
|
|
162
|
+
await this.driver.findElement(this.passwordField).sendKeys(password);
|
|
163
|
+
}
|
|
164
|
+
async clickLoginButton() {
|
|
165
|
+
await click(await this.driver.findElement(this.loginButton), this.driver);
|
|
166
|
+
}
|
|
167
|
+
async loginWith(username, password) {
|
|
168
|
+
await this.enterUsername(username);
|
|
169
|
+
await this.enterPassword(password);
|
|
170
|
+
await this.clickLoginButton();
|
|
171
|
+
}
|
|
172
|
+
async authorize() {
|
|
173
|
+
await this.loginWith(config().auth.login, config().auth.password);
|
|
174
|
+
}
|
|
85
175
|
};
|
|
86
176
|
|
|
87
177
|
// src/utils/createDriver.ts
|
|
@@ -103,36 +193,22 @@ var elementIsNotPresent = function elementIsNotPresent2(locator) {
|
|
|
103
193
|
});
|
|
104
194
|
};
|
|
105
195
|
|
|
106
|
-
// src/utils/
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
await
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
// src/utils/waitElementIsVisible.ts
|
|
116
|
-
async function waitElementIsVisible({
|
|
117
|
-
element,
|
|
118
|
-
timeout,
|
|
119
|
-
errorMessage,
|
|
120
|
-
driver
|
|
121
|
-
}) {
|
|
122
|
-
driver = driver ?? config().driver;
|
|
123
|
-
const condition = until.elementIsVisible(element);
|
|
124
|
-
await wait(condition.fn, driver, errorMessage ?? condition.description(), timeout ?? config().timeout);
|
|
196
|
+
// src/utils/signIn.ts
|
|
197
|
+
async function signIn(driver) {
|
|
198
|
+
const webDriver = driver ?? await createDriver();
|
|
199
|
+
config().driver = webDriver;
|
|
200
|
+
const loginPage = new LoginPage(webDriver);
|
|
201
|
+
await loginPage.locate();
|
|
202
|
+
await loginPage.authorize();
|
|
203
|
+
return webDriver;
|
|
125
204
|
}
|
|
126
205
|
|
|
127
206
|
// src/pageObjects/AbstractElementWithParent.ts
|
|
128
|
-
import { By as By3 } from "selenium-webdriver";
|
|
129
207
|
var AbstractElementWithParent = class extends AbstractElement {
|
|
130
|
-
elementCssSelector;
|
|
131
208
|
parentCssSelector;
|
|
132
209
|
constructor(parentCssSelector, elementCssSelector, driver) {
|
|
133
|
-
super(driver);
|
|
210
|
+
super(driver, elementCssSelector);
|
|
134
211
|
this.parentCssSelector = parentCssSelector;
|
|
135
|
-
this.elementCssSelector = elementCssSelector;
|
|
136
212
|
}
|
|
137
213
|
getFullCssSelector() {
|
|
138
214
|
if (!this.elementCssSelector) {
|
|
@@ -143,21 +219,6 @@ var AbstractElementWithParent = class extends AbstractElement {
|
|
|
143
219
|
}
|
|
144
220
|
return `${this.parentCssSelector} ${this.elementCssSelector}`;
|
|
145
221
|
}
|
|
146
|
-
async getElement() {
|
|
147
|
-
return this.driver.findElement(By3.css(this.getFullCssSelector()));
|
|
148
|
-
}
|
|
149
|
-
async getAttribute(attr) {
|
|
150
|
-
const element = await this.getElement();
|
|
151
|
-
await waitElementIsVisible({
|
|
152
|
-
element,
|
|
153
|
-
driver: this.driver,
|
|
154
|
-
errorMessage: `Element not found: ${this.getFullCssSelector()}`
|
|
155
|
-
});
|
|
156
|
-
return await element.getAttribute(attr);
|
|
157
|
-
}
|
|
158
|
-
async getClasses() {
|
|
159
|
-
return await this.getAttribute("class");
|
|
160
|
-
}
|
|
161
222
|
};
|
|
162
223
|
|
|
163
224
|
// src/pageObjects/elements/SubsystemsPanel/SubsystemsPanelHeader.ts
|
|
@@ -175,8 +236,10 @@ var SubsystemsPanel = class extends AbstractElement {
|
|
|
175
236
|
super(driver);
|
|
176
237
|
this.header = new SubsystemsPanelHeader("#aside", byTestIdCssSelector("subsystems-panel-title", "span"), this.driver);
|
|
177
238
|
}
|
|
178
|
-
headerTitle() {
|
|
179
|
-
|
|
239
|
+
async headerTitle() {
|
|
240
|
+
const title = await this.header.title();
|
|
241
|
+
await wait(async () => await title.getText() != "", this.driver);
|
|
242
|
+
return title.getText();
|
|
180
243
|
}
|
|
181
244
|
};
|
|
182
245
|
|
|
@@ -213,10 +276,10 @@ var AbstractForm = class extends AbstractPage {
|
|
|
213
276
|
*/
|
|
214
277
|
async exists() {
|
|
215
278
|
try {
|
|
216
|
-
await
|
|
279
|
+
let elements = await this.driver.findElements(this.containerBy);
|
|
280
|
+
return elements.length > 0;
|
|
281
|
+
} catch (error) {
|
|
217
282
|
return false;
|
|
218
|
-
} catch {
|
|
219
|
-
return true;
|
|
220
283
|
}
|
|
221
284
|
}
|
|
222
285
|
/**
|
|
@@ -231,7 +294,7 @@ var AbstractForm = class extends AbstractPage {
|
|
|
231
294
|
const titleEl2 = await this.driver.findElement(By5.css(`${this.popupContainerCssSelector} .popup-title`));
|
|
232
295
|
return titleEl2.getText();
|
|
233
296
|
}
|
|
234
|
-
let titleEl = await this.driver.findElement(By5.
|
|
297
|
+
let titleEl = await this.driver.findElement(By5.className("main-toolbar-form-title"));
|
|
235
298
|
return titleEl.getText();
|
|
236
299
|
}
|
|
237
300
|
};
|
|
@@ -239,6 +302,29 @@ var AbstractForm = class extends AbstractPage {
|
|
|
239
302
|
// src/pageObjects/elements/Forms/FormEdit.ts
|
|
240
303
|
import { By as By12 } from "selenium-webdriver";
|
|
241
304
|
|
|
305
|
+
// src/utils/createDataTestId.ts
|
|
306
|
+
function createDataTestId(formName, itemType, itemName) {
|
|
307
|
+
return `${formName}-${itemType}-${itemName}`;
|
|
308
|
+
}
|
|
309
|
+
function createFieldTestId(formName, itemName) {
|
|
310
|
+
return createDataTestId(formName, "form_field" /* FORM_FIELD */, itemName);
|
|
311
|
+
}
|
|
312
|
+
function createGroupTestId(formName, itemName) {
|
|
313
|
+
return createDataTestId(formName, "group" /* GROUP */, itemName);
|
|
314
|
+
}
|
|
315
|
+
function createFilterTestId(formName, itemName, layout) {
|
|
316
|
+
return createDataTestId(formName, "filter" /* FILTER */, itemName) + `-${layout}`;
|
|
317
|
+
}
|
|
318
|
+
function createDocFilterTestId(formName, itemName) {
|
|
319
|
+
return createFilterTestId(formName, itemName, "dock-panel-filter" /* DOCK_PANEL */);
|
|
320
|
+
}
|
|
321
|
+
function createLayoutFilterTestId(formName, itemName) {
|
|
322
|
+
return createFilterTestId(formName, itemName, "layout-filter" /* LAYOUT */);
|
|
323
|
+
}
|
|
324
|
+
function createTableId(formName) {
|
|
325
|
+
return `${"table" /* TABLE */}-${formName}`;
|
|
326
|
+
}
|
|
327
|
+
|
|
242
328
|
// src/pageObjects/elements/Editors/AbstractEditor.ts
|
|
243
329
|
import { By as By6, Key } from "selenium-webdriver";
|
|
244
330
|
|
|
@@ -431,6 +517,7 @@ var TAGS_SELECTOR = ".dx-tag .tag-text";
|
|
|
431
517
|
var DX_TAG_CONTAINER = "dx-tag-container";
|
|
432
518
|
var DX_TEXTEDITOR_EMPTY2 = "dx-texteditor-empty";
|
|
433
519
|
var BTN_OK_SELECTOR = ".dx-tagbox-popup-wrapper .dx-popup-bottom .dx-button.dx-popup-done";
|
|
520
|
+
var TAG_BOX_LIST_ITEM_SELECTOR = '.dx-tagbox-popup-wrapper .dx-list-item[aria-selected="true"]';
|
|
434
521
|
var TagBox = class extends AbstractDXEditorWithInput {
|
|
435
522
|
constructor(parentCssSelector, elementCssSelector, driver) {
|
|
436
523
|
super(parentCssSelector, elementCssSelector, driver);
|
|
@@ -445,21 +532,21 @@ var TagBox = class extends AbstractDXEditorWithInput {
|
|
|
445
532
|
}
|
|
446
533
|
await this.confirmSelection();
|
|
447
534
|
}
|
|
535
|
+
async waitOptionSelection() {
|
|
536
|
+
const option = await this.driver.findElement(By9.css(TAG_BOX_LIST_ITEM_SELECTOR));
|
|
537
|
+
await wait(() => option.isDisplayed(), this.driver);
|
|
538
|
+
}
|
|
448
539
|
async selectOption(inputEl, value, lastElement) {
|
|
449
540
|
await inputEl.sendKeys(value);
|
|
450
541
|
const optElement = await inputEl.findElement(By9.xpath(combinedValueXpath(value)));
|
|
451
542
|
await click(optElement, this.driver);
|
|
543
|
+
await this.waitOptionSelection();
|
|
452
544
|
if (value !== lastElement) {
|
|
453
545
|
await inputEl.clear();
|
|
454
546
|
}
|
|
455
547
|
}
|
|
456
548
|
async confirmSelection() {
|
|
457
|
-
const inputEl = await this.getInputElement();
|
|
458
|
-
const option = await inputEl.findElement(By9.xpath(XPathHelper.getRoleXpath(SELECT_BOX_ELEMENT_ROLE)));
|
|
459
549
|
const btnOk = await this.driver.findElement(By9.css(BTN_OK_SELECTOR));
|
|
460
|
-
await wait(async () => {
|
|
461
|
-
return await option.getAttribute("aria-selected") === "true";
|
|
462
|
-
}, this.driver);
|
|
463
550
|
await click(btnOk, this.driver);
|
|
464
551
|
}
|
|
465
552
|
async getTagBoxInputValue() {
|
|
@@ -607,25 +694,11 @@ var editorFactory = async (parentCssSelector, elementCssSelector, driver) => {
|
|
|
607
694
|
throw new Error("Unknown type of editor. Please write ticket to the support");
|
|
608
695
|
};
|
|
609
696
|
|
|
610
|
-
// src/
|
|
611
|
-
|
|
612
|
-
return `${formName}-${itemType}-${itemName}`;
|
|
613
|
-
}
|
|
614
|
-
function createFieldTestId(formName, itemName) {
|
|
615
|
-
return createDataTestId(formName, "form_field" /* FORM_FIELD */, itemName);
|
|
616
|
-
}
|
|
617
|
-
function createGroupTestId(formName, itemName) {
|
|
618
|
-
return createDataTestId(formName, "group" /* GROUP */, itemName);
|
|
619
|
-
}
|
|
620
|
-
function createTableId(formName) {
|
|
621
|
-
return `${"table" /* TABLE */}-${formName}`;
|
|
622
|
-
}
|
|
623
|
-
|
|
624
|
-
// src/pageObjects/elements/FormField.ts
|
|
625
|
-
var FormField = class extends AbstractElementWithParent {
|
|
697
|
+
// src/pageObjects/elements/AbstractEditor.ts
|
|
698
|
+
var AbstractEditor = class extends AbstractElementWithParent {
|
|
626
699
|
editor;
|
|
627
|
-
constructor(parentCssSelector,
|
|
628
|
-
super(parentCssSelector,
|
|
700
|
+
constructor(parentCssSelector, elementCssSelector, driver) {
|
|
701
|
+
super(parentCssSelector, elementCssSelector, driver);
|
|
629
702
|
}
|
|
630
703
|
async initEditor() {
|
|
631
704
|
if (!this.editor) {
|
|
@@ -678,6 +751,13 @@ var FormField = class extends AbstractElementWithParent {
|
|
|
678
751
|
}
|
|
679
752
|
};
|
|
680
753
|
|
|
754
|
+
// src/pageObjects/elements/FormField.ts
|
|
755
|
+
var FormField = class extends AbstractEditor {
|
|
756
|
+
constructor(parentCssSelector, formName, name, driver) {
|
|
757
|
+
super(parentCssSelector, byTestIdCssSelector(createFieldTestId(formName, name)), driver);
|
|
758
|
+
}
|
|
759
|
+
};
|
|
760
|
+
|
|
681
761
|
// src/pageObjects/elements/Groups/AbstractGroup.ts
|
|
682
762
|
var AbstractGroup = class extends AbstractElementWithParent {
|
|
683
763
|
async isDisplayed() {
|
|
@@ -779,9 +859,12 @@ var FormEdit = class extends AbstractForm {
|
|
|
779
859
|
};
|
|
780
860
|
|
|
781
861
|
// src/pageObjects/elements/Table/Table.ts
|
|
782
|
-
import { By as
|
|
862
|
+
import { By as By17 } from "selenium-webdriver";
|
|
783
863
|
|
|
784
864
|
// src/pageObjects/elements/Table/elements/Row.ts
|
|
865
|
+
import { By as By15 } from "selenium-webdriver";
|
|
866
|
+
|
|
867
|
+
// src/pageObjects/elements/Table/elements/Cell.ts
|
|
785
868
|
import { By as By14 } from "selenium-webdriver";
|
|
786
869
|
|
|
787
870
|
// src/pageObjects/elements/Table/utils.ts
|
|
@@ -791,6 +874,7 @@ function getCssRowByIndexSelector(index) {
|
|
|
791
874
|
}
|
|
792
875
|
var getColumnByIndexCssSelector = (columnIndex) => `td[aria-colindex="${transformInputIndex(columnIndex)}"]`;
|
|
793
876
|
var getColumnByCaptionCssSelector = (caption) => `td[role="columnheader"][aria-label="Column ${caption}"]`;
|
|
877
|
+
var getColumnByNameCssSelector = (name) => `td[role="columnheader"][data-fieldname="${name}"]`;
|
|
794
878
|
function getCssCellByIndexSelector(index) {
|
|
795
879
|
return `td[role="gridcell"][aria-colindex="${transformInputIndex(index)}"]`;
|
|
796
880
|
}
|
|
@@ -810,6 +894,10 @@ var Cell = class extends AbstractElementWithParent {
|
|
|
810
894
|
this._index = index;
|
|
811
895
|
this.elementCssSelector = getCssCellByIndexSelector(index);
|
|
812
896
|
}
|
|
897
|
+
async getBooleanValue(cellEl) {
|
|
898
|
+
const booleanEl = await cellEl.findElement(By14.className("boolean-widget"));
|
|
899
|
+
return await booleanEl.getAttribute("aria-checked") === "true" ? 1 : 0;
|
|
900
|
+
}
|
|
813
901
|
async getElement() {
|
|
814
902
|
if (this._index == null) {
|
|
815
903
|
this.index = await this.getCellIndex();
|
|
@@ -818,6 +906,10 @@ var Cell = class extends AbstractElementWithParent {
|
|
|
818
906
|
}
|
|
819
907
|
async getValue() {
|
|
820
908
|
const cellEl = await this.getElement();
|
|
909
|
+
const cellElClass = await cellEl.getAttribute("class");
|
|
910
|
+
if (cellElClass.includes("boolean-column")) {
|
|
911
|
+
return this.getBooleanValue(cellEl);
|
|
912
|
+
}
|
|
821
913
|
return cellEl.getText();
|
|
822
914
|
}
|
|
823
915
|
getIndex() {
|
|
@@ -831,22 +923,25 @@ var DX_ROW_EDIT_CLASS = "dx-edit-row";
|
|
|
831
923
|
var DX_DATA_ROW_CLASS = "dx-data-row";
|
|
832
924
|
var Row = class extends AbstractElementWithParent {
|
|
833
925
|
index;
|
|
834
|
-
|
|
835
|
-
constructor(parentCssSelector, index, driver,
|
|
926
|
+
getColumnBy;
|
|
927
|
+
constructor(parentCssSelector, index, driver, getColumnBy) {
|
|
836
928
|
super(parentCssSelector, getCssRowByIndexSelector(index), driver);
|
|
837
929
|
this.index = index;
|
|
838
|
-
this.
|
|
930
|
+
this.getColumnBy = getColumnBy;
|
|
839
931
|
}
|
|
840
|
-
_getCell(index,
|
|
932
|
+
_getCell({ index, name, title }) {
|
|
841
933
|
return new Cell(this.getFullCssSelector(), () => {
|
|
842
|
-
return this.
|
|
934
|
+
return this.getColumnBy({ title, name }).getIndex();
|
|
843
935
|
}, index, this.driver);
|
|
844
936
|
}
|
|
845
937
|
getCellByIndex(index) {
|
|
846
|
-
return this._getCell(index);
|
|
938
|
+
return this._getCell({ index });
|
|
847
939
|
}
|
|
848
|
-
getCell(
|
|
849
|
-
return this._getCell(
|
|
940
|
+
getCell(name) {
|
|
941
|
+
return this._getCell({ name });
|
|
942
|
+
}
|
|
943
|
+
getCellByTitle(title) {
|
|
944
|
+
return this._getCell({ title });
|
|
850
945
|
}
|
|
851
946
|
async select() {
|
|
852
947
|
const rowEl = await this.getElement();
|
|
@@ -864,7 +959,7 @@ var Row = class extends AbstractElementWithParent {
|
|
|
864
959
|
return this.index;
|
|
865
960
|
}
|
|
866
961
|
async getCells() {
|
|
867
|
-
const cells = await this.driver.findElements(
|
|
962
|
+
const cells = await this.driver.findElements(By15.css(`${this.getFullCssSelector()} td[role="gridcell"]`));
|
|
868
963
|
const result = [];
|
|
869
964
|
cells.forEach((_, index) => {
|
|
870
965
|
const newCell = this.getCellByIndex(index);
|
|
@@ -875,7 +970,7 @@ var Row = class extends AbstractElementWithParent {
|
|
|
875
970
|
async clickInlineButton(buttonClass) {
|
|
876
971
|
const FIXED_TABLE_CSS_SELECTOR = ".dx-fixed-columns .dx-datagrid-content.dx-datagrid-content-fixed";
|
|
877
972
|
const buttonCssSelector = `.${DX_DATA_ROW_CLASS}[aria-rowindex="${transformInputIndex(this.index)}"] .${buttonClass}`;
|
|
878
|
-
const button = await this.driver.findElement(
|
|
973
|
+
const button = await this.driver.findElement(By15.css(`${this.parentCssSelector} ${FIXED_TABLE_CSS_SELECTOR} ${buttonCssSelector}`));
|
|
879
974
|
return click(button, this.driver);
|
|
880
975
|
}
|
|
881
976
|
async checkEditingMode(isEditing, msg) {
|
|
@@ -898,15 +993,17 @@ var Row = class extends AbstractElementWithParent {
|
|
|
898
993
|
};
|
|
899
994
|
|
|
900
995
|
// src/pageObjects/elements/Table/elements/Column.ts
|
|
901
|
-
import { By as
|
|
996
|
+
import { By as By16 } from "selenium-webdriver";
|
|
902
997
|
var getHeaderCssSelector = () => {
|
|
903
998
|
return ".dx-datagrid-headers .dx-datagrid-content:not(.dx-datagrid-content-fixed)";
|
|
904
999
|
};
|
|
905
1000
|
var Column = class extends AbstractElementWithParent {
|
|
906
1001
|
_index;
|
|
907
1002
|
_caption;
|
|
908
|
-
|
|
1003
|
+
_name;
|
|
1004
|
+
constructor({ parentCssSelector, name, caption, index, driver }) {
|
|
909
1005
|
super(parentCssSelector, "", driver);
|
|
1006
|
+
this._name = name;
|
|
910
1007
|
this.index = index;
|
|
911
1008
|
this._caption = caption;
|
|
912
1009
|
}
|
|
@@ -921,19 +1018,33 @@ var Column = class extends AbstractElementWithParent {
|
|
|
921
1018
|
await this.getIndex();
|
|
922
1019
|
return super.getElement();
|
|
923
1020
|
}
|
|
924
|
-
async
|
|
925
|
-
const column = await this.driver.findElement(
|
|
1021
|
+
async getColumnHeaderIndexBy(cssSelector) {
|
|
1022
|
+
const column = await this.driver.findElement(By16.css(`${this.parentCssSelector} ${getHeaderCssSelector()} ${cssSelector}`));
|
|
926
1023
|
return transformOutputIndex(await column.getAttribute("aria-colindex"));
|
|
927
1024
|
}
|
|
1025
|
+
async getIndexByCaption() {
|
|
1026
|
+
return this.getColumnHeaderIndexBy(getColumnByCaptionCssSelector(this._caption));
|
|
1027
|
+
}
|
|
1028
|
+
async getIndexByName() {
|
|
1029
|
+
return this.getColumnHeaderIndexBy(getColumnByNameCssSelector(this._name));
|
|
1030
|
+
}
|
|
928
1031
|
async caption() {
|
|
929
1032
|
const el = await this.getElement();
|
|
930
1033
|
return el.getText();
|
|
931
1034
|
}
|
|
932
1035
|
async getIndex() {
|
|
933
|
-
if (this.index
|
|
1036
|
+
if (this.index != null) {
|
|
1037
|
+
return this.index;
|
|
1038
|
+
}
|
|
1039
|
+
if (this._caption != null) {
|
|
934
1040
|
this.index = await this.getIndexByCaption();
|
|
1041
|
+
return this.index;
|
|
935
1042
|
}
|
|
936
|
-
|
|
1043
|
+
if (this._name != null) {
|
|
1044
|
+
this.index = await this.getIndexByName();
|
|
1045
|
+
return this.index;
|
|
1046
|
+
}
|
|
1047
|
+
throw new Error("Cant get the index in the column");
|
|
937
1048
|
}
|
|
938
1049
|
};
|
|
939
1050
|
|
|
@@ -944,32 +1055,57 @@ var Table = class extends AbstractElementWithParent {
|
|
|
944
1055
|
super(parentCssSelector, `#${createTableId(formName)}`, driver);
|
|
945
1056
|
this.formName = formName;
|
|
946
1057
|
}
|
|
1058
|
+
getColumnBy = (args) => {
|
|
1059
|
+
if (args.title) {
|
|
1060
|
+
return this.getColumnByTitle(args.title);
|
|
1061
|
+
}
|
|
1062
|
+
return this.getColumnByName(args.name);
|
|
1063
|
+
};
|
|
947
1064
|
getRowByIndex(index) {
|
|
948
|
-
return new Row(this.getFullCssSelector(), index, this.driver, this.
|
|
1065
|
+
return new Row(this.getFullCssSelector(), index, this.driver, this.getColumnBy);
|
|
949
1066
|
}
|
|
950
1067
|
getColumnByIndex(index) {
|
|
951
|
-
return new Column(
|
|
1068
|
+
return new Column({
|
|
1069
|
+
parentCssSelector: this.getFullCssSelector(),
|
|
1070
|
+
driver: this.driver,
|
|
1071
|
+
index
|
|
1072
|
+
});
|
|
952
1073
|
}
|
|
953
|
-
|
|
954
|
-
return new Column(
|
|
1074
|
+
getColumnByTitle = (title) => {
|
|
1075
|
+
return new Column({
|
|
1076
|
+
parentCssSelector: this.getFullCssSelector(),
|
|
1077
|
+
caption: title,
|
|
1078
|
+
driver: this.driver
|
|
1079
|
+
});
|
|
1080
|
+
};
|
|
1081
|
+
getColumnByName = (name) => {
|
|
1082
|
+
return new Column({
|
|
1083
|
+
parentCssSelector: this.getFullCssSelector(),
|
|
1084
|
+
name,
|
|
1085
|
+
driver: this.driver
|
|
1086
|
+
});
|
|
955
1087
|
};
|
|
956
1088
|
selectRowByIndex(index) {
|
|
957
1089
|
return this.getRowByIndex(index).select();
|
|
958
1090
|
}
|
|
959
1091
|
async getRows() {
|
|
960
|
-
const rows = await this.driver.findElements(
|
|
1092
|
+
const rows = await this.driver.findElements(By17.css(`${this.getFullCssSelector()} .dx-row.dx-data-row`));
|
|
961
1093
|
const result = [];
|
|
962
1094
|
rows.forEach((_, index) => {
|
|
963
|
-
const newRow = new Row(this.getFullCssSelector(), index, this.driver, this.
|
|
1095
|
+
const newRow = new Row(this.getFullCssSelector(), index, this.driver, this.getColumnBy);
|
|
964
1096
|
result.push(newRow);
|
|
965
1097
|
});
|
|
966
1098
|
return result;
|
|
967
1099
|
}
|
|
968
1100
|
async getColumns() {
|
|
969
|
-
const columns = await this.driver.findElements(
|
|
1101
|
+
const columns = await this.driver.findElements(By17.css(`${this.getFullCssSelector()} ${getHeaderCssSelector()} td[role="columnheader"]`));
|
|
970
1102
|
const result = [];
|
|
971
1103
|
columns.forEach((_, index) => {
|
|
972
|
-
const newCol = new Column(
|
|
1104
|
+
const newCol = new Column({
|
|
1105
|
+
parentCssSelector: this.getFullCssSelector(),
|
|
1106
|
+
index,
|
|
1107
|
+
driver: this.driver
|
|
1108
|
+
});
|
|
973
1109
|
result.push(newCol);
|
|
974
1110
|
});
|
|
975
1111
|
return result;
|
|
@@ -989,19 +1125,16 @@ var Table = class extends AbstractElementWithParent {
|
|
|
989
1125
|
}
|
|
990
1126
|
};
|
|
991
1127
|
|
|
992
|
-
// src/pageObjects/elements/TableToolbar/
|
|
993
|
-
var
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
constructor(parentCssSelector, name, driver) {
|
|
997
|
-
super(parentCssSelector, `#${TOOLBAR_BUTTON_CLASS}-${name}`, driver);
|
|
998
|
-
this.name = name;
|
|
1128
|
+
// src/pageObjects/elements/TableToolbar/AbstractButton.ts
|
|
1129
|
+
var AbstractButton = class extends AbstractElementWithParent {
|
|
1130
|
+
constructor(parentCssSelector, elementCssSelector, driver) {
|
|
1131
|
+
super(parentCssSelector, elementCssSelector, driver);
|
|
999
1132
|
}
|
|
1000
1133
|
async click() {
|
|
1001
1134
|
const button = await this.getElement();
|
|
1002
1135
|
await wait(async () => {
|
|
1003
1136
|
return await button.isDisplayed() && !await this.isDisabled();
|
|
1004
|
-
}, this.driver,
|
|
1137
|
+
}, this.driver, this.buttonNotDisplayedError());
|
|
1005
1138
|
await click(button, this.driver);
|
|
1006
1139
|
}
|
|
1007
1140
|
async isDisabled() {
|
|
@@ -1010,14 +1143,207 @@ var TableToolbarButton = class extends AbstractElementWithParent {
|
|
|
1010
1143
|
}
|
|
1011
1144
|
};
|
|
1012
1145
|
|
|
1013
|
-
// src/pageObjects/elements/TableToolbar/
|
|
1146
|
+
// src/pageObjects/elements/TableToolbar/ToolbarGroupButton.ts
|
|
1147
|
+
var ToolbarGroupButton = class extends AbstractButton {
|
|
1148
|
+
name;
|
|
1149
|
+
constructor(parentCssSelector, name, driver) {
|
|
1150
|
+
super(parentCssSelector, ``, driver);
|
|
1151
|
+
this.name = name;
|
|
1152
|
+
}
|
|
1153
|
+
buttonNotDisplayedError() {
|
|
1154
|
+
return `Button ${this.name} cannot be clicked. It is not displayed or it is disabled`;
|
|
1155
|
+
}
|
|
1156
|
+
};
|
|
1157
|
+
|
|
1158
|
+
// src/pageObjects/elements/TableToolbar/ToolbarButton.ts
|
|
1159
|
+
var TOOLBAR_BUTTON_CLASS = "toolbar-button";
|
|
1160
|
+
var ToolbarButton = class extends AbstractButton {
|
|
1161
|
+
name;
|
|
1162
|
+
constructor(parentCssSelector, name, driver) {
|
|
1163
|
+
super(parentCssSelector, `#${TOOLBAR_BUTTON_CLASS}-${name}`, driver);
|
|
1164
|
+
this.name = name;
|
|
1165
|
+
}
|
|
1166
|
+
buttonNotDisplayedError() {
|
|
1167
|
+
return `Button ${this.name} cannot be clicked. It is not displayed or it is disabled`;
|
|
1168
|
+
}
|
|
1169
|
+
};
|
|
1170
|
+
|
|
1171
|
+
// src/pageObjects/elements/TableToolbar/ToolbarItem.ts
|
|
1172
|
+
var GROUP_BUTTON_CLASS = "toolbar-group-button";
|
|
1173
|
+
var toolbarItemSelector = (form, name) => byTestIdCssSelector(`ftb-${form}-${name}`);
|
|
1174
|
+
var ToolbarItem = class extends AbstractElementWithParent {
|
|
1175
|
+
_button;
|
|
1176
|
+
// private _formName: string;
|
|
1177
|
+
_itemName;
|
|
1178
|
+
constructor(parentCssSelector, formName, itemName, driver) {
|
|
1179
|
+
super(parentCssSelector, toolbarItemSelector(formName, itemName), driver);
|
|
1180
|
+
this._itemName = itemName;
|
|
1181
|
+
}
|
|
1182
|
+
async init() {
|
|
1183
|
+
if (this._button)
|
|
1184
|
+
return this._button;
|
|
1185
|
+
const classNames = await this.getClasses();
|
|
1186
|
+
if (classNames.includes(GROUP_BUTTON_CLASS)) {
|
|
1187
|
+
this._button = new ToolbarGroupButton(this.getFullCssSelector(), this._itemName, this.driver);
|
|
1188
|
+
return;
|
|
1189
|
+
}
|
|
1190
|
+
this._button = new ToolbarButton(this.parentCssSelector, this._itemName, this.driver);
|
|
1191
|
+
}
|
|
1192
|
+
async click() {
|
|
1193
|
+
await this.init();
|
|
1194
|
+
await this._button.click();
|
|
1195
|
+
}
|
|
1196
|
+
async isDisabled() {
|
|
1197
|
+
await this.init();
|
|
1198
|
+
return await this._button.isDisabled();
|
|
1199
|
+
}
|
|
1200
|
+
};
|
|
1201
|
+
|
|
1202
|
+
// src/pageObjects/elements/TableToolbar/FormToolbar.ts
|
|
1014
1203
|
var TABLE_TOOLBAR_CLASS = "d5-table-toolbar";
|
|
1015
|
-
var
|
|
1204
|
+
var FormToolbar = class extends AbstractElementWithParent {
|
|
1205
|
+
formName;
|
|
1016
1206
|
constructor(parentCssSelector, formName, driver) {
|
|
1017
1207
|
super(parentCssSelector, `#${TABLE_TOOLBAR_CLASS}-${formName}`, driver);
|
|
1208
|
+
this.formName = formName;
|
|
1018
1209
|
}
|
|
1019
1210
|
button(name) {
|
|
1020
|
-
return new
|
|
1211
|
+
return new ToolbarItem(this.getFullCssSelector(), this.formName, name, this.driver);
|
|
1212
|
+
}
|
|
1213
|
+
};
|
|
1214
|
+
|
|
1215
|
+
// src/pageObjects/elements/Filters/FormFilterField.ts
|
|
1216
|
+
var FormFilterField = class extends AbstractEditor {
|
|
1217
|
+
constructor(parentCssSelector, formName, name, driver) {
|
|
1218
|
+
super(parentCssSelector, byTestIdCssSelector(createLayoutFilterTestId(formName, name)), driver);
|
|
1219
|
+
}
|
|
1220
|
+
/**
|
|
1221
|
+
* Возвращает значение в поле
|
|
1222
|
+
* @example
|
|
1223
|
+
* expect(await form.formFilter('NumberFld').getValue()).toBe(10);
|
|
1224
|
+
* expect(await form.formFilter('TextFld').getValue()).toBe('TestValue');
|
|
1225
|
+
*/
|
|
1226
|
+
async getValue() {
|
|
1227
|
+
return super.getValue();
|
|
1228
|
+
}
|
|
1229
|
+
/**
|
|
1230
|
+
* Устанавливает значение в поле
|
|
1231
|
+
* @example
|
|
1232
|
+
* await form.formFilter('NumberFld').setValue(123);
|
|
1233
|
+
* await form.formFilter('TextFld').setValue('TestValue');
|
|
1234
|
+
*/
|
|
1235
|
+
async setValue(value) {
|
|
1236
|
+
return super.setValue(value);
|
|
1237
|
+
}
|
|
1238
|
+
/**
|
|
1239
|
+
* Очищает значение в поле
|
|
1240
|
+
* @example
|
|
1241
|
+
* await form.formFilter('NumberFld').clear();
|
|
1242
|
+
*/
|
|
1243
|
+
async clear() {
|
|
1244
|
+
return super.clear();
|
|
1245
|
+
}
|
|
1246
|
+
/**
|
|
1247
|
+
* Удаляет тег по переданному тексту
|
|
1248
|
+
* @example
|
|
1249
|
+
* await form.formFilter('TagBox').remove('test');
|
|
1250
|
+
*/
|
|
1251
|
+
async remove(itemText) {
|
|
1252
|
+
return super.remove(itemText);
|
|
1253
|
+
}
|
|
1254
|
+
};
|
|
1255
|
+
|
|
1256
|
+
// src/pageObjects/elements/Filters/FilterPanel.ts
|
|
1257
|
+
import { By as By18 } from "selenium-webdriver";
|
|
1258
|
+
|
|
1259
|
+
// src/pageObjects/elements/Filters/PanelFilterField.ts
|
|
1260
|
+
var PanelFilterField = class extends AbstractEditor {
|
|
1261
|
+
constructor(parentCssSelector, formName, name, driver) {
|
|
1262
|
+
super(parentCssSelector, byTestIdCssSelector(createDocFilterTestId(formName, name)), driver);
|
|
1263
|
+
}
|
|
1264
|
+
/**
|
|
1265
|
+
* Возвращает значение в поле
|
|
1266
|
+
* @example
|
|
1267
|
+
* expect(await form.filterPanel.filterField('NumberFld').getValue()).toBe(10);
|
|
1268
|
+
* expect(await form.filterPanel.filterField('TextFld').getValue()).toBe('TestValue');
|
|
1269
|
+
*/
|
|
1270
|
+
async getValue() {
|
|
1271
|
+
return super.getValue();
|
|
1272
|
+
}
|
|
1273
|
+
/**
|
|
1274
|
+
* Устанавливает значение в поле
|
|
1275
|
+
* @example
|
|
1276
|
+
* await form.filterPanel.filterField('NumberFld').setValue(123);
|
|
1277
|
+
* await form.filterPanel.filterField('TextFld').setValue('TestValue');
|
|
1278
|
+
*/
|
|
1279
|
+
async setValue(value) {
|
|
1280
|
+
return super.setValue(value);
|
|
1281
|
+
}
|
|
1282
|
+
/**
|
|
1283
|
+
* Очищает значение в поле
|
|
1284
|
+
* @example
|
|
1285
|
+
* await form.filterPanel.filterField('NumberFld').clear();
|
|
1286
|
+
*/
|
|
1287
|
+
async clear() {
|
|
1288
|
+
return super.clear();
|
|
1289
|
+
}
|
|
1290
|
+
/**
|
|
1291
|
+
* Удаляет тег по переданному тексту
|
|
1292
|
+
* @example
|
|
1293
|
+
* await form.filterPanel.filterField('TagBox').remove('test');
|
|
1294
|
+
*/
|
|
1295
|
+
async remove(itemText) {
|
|
1296
|
+
return super.remove(itemText);
|
|
1297
|
+
}
|
|
1298
|
+
};
|
|
1299
|
+
|
|
1300
|
+
// src/pageObjects/elements/Filters/FilterPanel.ts
|
|
1301
|
+
var drawerSelector = (formName) => `.secondary-drawer-side-panel.form-${formName}`;
|
|
1302
|
+
var FILTER_PANEL_SELECTOR = ".filter-panel";
|
|
1303
|
+
var CLOSE_SELECTOR = ".button-close";
|
|
1304
|
+
var FilterPanel = class extends AbstractElement {
|
|
1305
|
+
_formName;
|
|
1306
|
+
get filterPanelSelector() {
|
|
1307
|
+
return `${this.elementCssSelector} ${FILTER_PANEL_SELECTOR}`;
|
|
1308
|
+
}
|
|
1309
|
+
async filterPanelButtons() {
|
|
1310
|
+
const selector = `${this.elementCssSelector} ${FILTER_PANEL_SELECTOR} .filter-panel__buttons-panel .dx-button-text`;
|
|
1311
|
+
const el = await this.getElement();
|
|
1312
|
+
return el.findElements(By18.css(selector));
|
|
1313
|
+
}
|
|
1314
|
+
constructor(formName, driver) {
|
|
1315
|
+
super(driver, drawerSelector(formName));
|
|
1316
|
+
this._formName = formName;
|
|
1317
|
+
}
|
|
1318
|
+
/**
|
|
1319
|
+
* Возвращает фильтр на панели фильтрации
|
|
1320
|
+
* @returns {PanelFilterField}
|
|
1321
|
+
* @example
|
|
1322
|
+
* await form.filterPanel.filterField('NumberFld');
|
|
1323
|
+
*/
|
|
1324
|
+
filterField(name) {
|
|
1325
|
+
return new PanelFilterField(this.filterPanelSelector, this._formName, name, this.driver);
|
|
1326
|
+
}
|
|
1327
|
+
async apply() {
|
|
1328
|
+
const btn = await this.findButtonByTitle("\u0417\u0430\u0441\u0442\u043E\u0441\u0443\u0432\u0430\u0442\u0438");
|
|
1329
|
+
await click(btn, this.driver);
|
|
1330
|
+
}
|
|
1331
|
+
async findButtonByTitle(title) {
|
|
1332
|
+
const buttons = await this.filterPanelButtons();
|
|
1333
|
+
for (const btn of buttons) {
|
|
1334
|
+
if (await btn.getText() === title) {
|
|
1335
|
+
return btn;
|
|
1336
|
+
}
|
|
1337
|
+
}
|
|
1338
|
+
}
|
|
1339
|
+
async clearAll() {
|
|
1340
|
+
const btn = await this.findButtonByTitle("\u041E\u0447\u0438\u0441\u0442\u0438\u0442\u0438");
|
|
1341
|
+
await click(btn, this.driver);
|
|
1342
|
+
await this.driver.sleep(300);
|
|
1343
|
+
}
|
|
1344
|
+
async close() {
|
|
1345
|
+
const btn = await this.driver.findElement(By18.css(`${this.elementCssSelector} ${CLOSE_SELECTOR}`));
|
|
1346
|
+
await click(btn, this.driver);
|
|
1021
1347
|
}
|
|
1022
1348
|
};
|
|
1023
1349
|
|
|
@@ -1055,70 +1381,41 @@ var ListForm = class extends AbstractForm {
|
|
|
1055
1381
|
* expect(listForm.toolbarButton());
|
|
1056
1382
|
*/
|
|
1057
1383
|
toolbarButton(name) {
|
|
1058
|
-
const toolbar = new
|
|
1384
|
+
const toolbar = new FormToolbar(this.containerCssSelector, this.formName, this.driver);
|
|
1059
1385
|
return toolbar.button(name);
|
|
1060
1386
|
}
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
return webDriver;
|
|
1071
|
-
}
|
|
1072
|
-
|
|
1073
|
-
// src/utils/actions.ts
|
|
1074
|
-
var click = async (webElement, driver) => {
|
|
1075
|
-
driver = driver ?? config().driver;
|
|
1076
|
-
const actions = driver.actions({ async: true });
|
|
1077
|
-
await actions.move({ origin: webElement }).click().perform();
|
|
1078
|
-
};
|
|
1079
|
-
|
|
1080
|
-
// src/pageObjects/pages/LoginPage.ts
|
|
1081
|
-
var LOGIN_PATH = "/login";
|
|
1082
|
-
var LoginPage = class extends AbstractPage {
|
|
1083
|
-
usernameField = byTestId("loginField", "input");
|
|
1084
|
-
passwordField = byTestId("passwordField", "input");
|
|
1085
|
-
loginButton = byTestId("loginButton");
|
|
1086
|
-
async locate() {
|
|
1087
|
-
await super.locate();
|
|
1088
|
-
const login = await this.driver.findElement(this.usernameField);
|
|
1089
|
-
await waitElementIsVisible({
|
|
1090
|
-
element: login,
|
|
1091
|
-
driver: this.driver
|
|
1092
|
-
});
|
|
1093
|
-
}
|
|
1094
|
-
get path() {
|
|
1095
|
-
return LOGIN_PATH;
|
|
1096
|
-
}
|
|
1097
|
-
async enterUsername(username) {
|
|
1098
|
-
await this.driver.findElement(this.usernameField).sendKeys(username);
|
|
1099
|
-
}
|
|
1100
|
-
async enterPassword(password) {
|
|
1101
|
-
await this.driver.findElement(this.passwordField).sendKeys(password);
|
|
1102
|
-
}
|
|
1103
|
-
async clickLoginButton() {
|
|
1104
|
-
await click(await this.driver.findElement(this.loginButton), this.driver);
|
|
1105
|
-
}
|
|
1106
|
-
async loginWith(username, password) {
|
|
1107
|
-
await this.enterUsername(username);
|
|
1108
|
-
await this.enterPassword(password);
|
|
1109
|
-
await this.clickLoginButton();
|
|
1387
|
+
/**
|
|
1388
|
+
* Фильтр который расположен на форме
|
|
1389
|
+
* @example
|
|
1390
|
+
* const listForm = new MyForm();
|
|
1391
|
+
* //...
|
|
1392
|
+
* expect(listForm.formFilter());
|
|
1393
|
+
*/
|
|
1394
|
+
formFilter(name) {
|
|
1395
|
+
return new FormFilterField(this.containerCssSelector, this.formName, name, this.driver);
|
|
1110
1396
|
}
|
|
1111
|
-
|
|
1112
|
-
|
|
1397
|
+
/**
|
|
1398
|
+
* Модель панели фильтрации
|
|
1399
|
+
* @returns {FilterPanel}
|
|
1400
|
+
* @example
|
|
1401
|
+
* const listForm = new MyForm();
|
|
1402
|
+
* //...
|
|
1403
|
+
* expect(listForm.filterPanel());
|
|
1404
|
+
*/
|
|
1405
|
+
get filterPanel() {
|
|
1406
|
+
return new FilterPanel(this.formName, this.driver);
|
|
1113
1407
|
}
|
|
1114
1408
|
};
|
|
1115
1409
|
export {
|
|
1116
1410
|
AbstractPage,
|
|
1411
|
+
FilterPanel,
|
|
1117
1412
|
FormEdit,
|
|
1118
1413
|
FormField,
|
|
1414
|
+
FormFilterField,
|
|
1119
1415
|
ListForm,
|
|
1120
1416
|
LoginPage,
|
|
1121
1417
|
NumberEditor,
|
|
1418
|
+
PanelFilterField,
|
|
1122
1419
|
SubsystemsPanel,
|
|
1123
1420
|
TextEditor,
|
|
1124
1421
|
byTestId,
|