d5-testing-library 1.0.5 → 1.2.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.
@@ -77,8 +77,11 @@ var AbstractPage = class extends AbstractElement {
77
77
 
78
78
  // src/utils/locators.ts
79
79
  import { By } from "selenium-webdriver";
80
+ var byTestIdCssSelector = (testId, element = "div") => {
81
+ return `${element}[data-testid="${testId}"]`;
82
+ };
80
83
  var byTestId = (testId, element = "div") => {
81
- return By.css(`${element}[data-testid=${testId}]`);
84
+ return By.css(byTestIdCssSelector(testId, element));
82
85
  };
83
86
 
84
87
  // src/utils/createDriver.ts
@@ -103,54 +106,91 @@ var elementIsNotPresent = function elementIsNotPresent2(locator) {
103
106
  // src/utils/waitElementIsVisible.ts
104
107
  import { until } from "selenium-webdriver";
105
108
 
109
+ // src/utils/wait.ts
110
+ async function wait(condition, driver, errorMessage, timeout) {
111
+ driver = driver ?? config().driver;
112
+ await driver.wait(condition, timeout ?? config().timeout, errorMessage);
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);
125
+ }
126
+
106
127
  // src/pageObjects/AbstractElementWithParent.ts
128
+ import { By as By3 } from "selenium-webdriver";
107
129
  var AbstractElementWithParent = class extends AbstractElement {
108
- getParentElement;
109
- elementBy;
110
- constructor(getParentElement, driver) {
130
+ elementCssSelector;
131
+ parentCssSelector;
132
+ constructor(parentCssSelector, elementCssSelector, driver) {
111
133
  super(driver);
112
- this.getParentElement = getParentElement;
134
+ this.parentCssSelector = parentCssSelector;
135
+ this.elementCssSelector = elementCssSelector;
136
+ }
137
+ getFullCssSelector() {
138
+ if (!this.elementCssSelector) {
139
+ return this.parentCssSelector;
140
+ }
141
+ if (!this.parentCssSelector) {
142
+ return this.elementCssSelector;
143
+ }
144
+ return `${this.parentCssSelector} ${this.elementCssSelector}`;
113
145
  }
114
146
  async getElement() {
115
- const parentEl = await this.getParentElement();
116
- return parentEl.findElement(this.elementBy);
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");
117
160
  }
118
161
  };
119
162
 
120
163
  // src/pageObjects/elements/SubsystemsPanel/SubsystemsPanelHeader.ts
164
+ import { By as By4 } from "selenium-webdriver";
121
165
  var SubsystemsPanelHeader = class extends AbstractElementWithParent {
122
- titleBy = byTestId("subsystems-panel-title", "span");
123
166
  async title() {
124
- const panel = await this.getParentElement();
125
- return panel.findElement(this.titleBy);
167
+ return this.driver.findElement(By4.css(this.getFullCssSelector()));
126
168
  }
127
169
  };
128
170
 
129
171
  // src/pageObjects/elements/SubsystemsPanel/SubsystemsPanel.ts
130
- import { By as By3 } from "selenium-webdriver";
131
172
  var SubsystemsPanel = class extends AbstractElement {
132
- panelBy = By3.id("aside");
133
173
  header;
134
174
  constructor(driver) {
135
175
  super(driver);
136
- this.header = new SubsystemsPanelHeader(this.panelElement, this.driver);
176
+ this.header = new SubsystemsPanelHeader("#aside", byTestIdCssSelector("subsystems-panel-title", "span"), this.driver);
137
177
  }
138
- panelElement = () => {
139
- return this.driver.findElement(this.panelBy);
140
- };
141
178
  headerTitle() {
142
179
  return this.header.title().then((el) => el.getText());
143
180
  }
144
181
  };
145
182
 
146
183
  // src/pageObjects/elements/Forms/AbstractForm.ts
147
- import { By as By4 } from "selenium-webdriver";
184
+ import { By as By5 } from "selenium-webdriver";
148
185
  var AbstractForm = class extends AbstractPage {
186
+ containerCssSelector = "";
149
187
  containerBy;
150
- popupContainerBy;
188
+ popupContainerCssSelector;
151
189
  constructor(driver) {
152
190
  super(driver);
153
- this.containerBy = byTestId(`form-content-wrapper-${this.formName}`);
191
+ const testId = `form-content-wrapper-${this.formName}`;
192
+ this.containerBy = byTestId(testId);
193
+ this.containerCssSelector = byTestIdCssSelector(testId);
154
194
  }
155
195
  async isModal() {
156
196
  const el = await this.getContainerElement();
@@ -173,7 +213,7 @@ var AbstractForm = class extends AbstractPage {
173
213
  */
174
214
  async exists() {
175
215
  try {
176
- await this.driver.wait(elementIsNotPresent(this.containerBy), 1e3);
216
+ await wait(elementIsNotPresent(this.containerBy), this.driver);
177
217
  return false;
178
218
  } catch {
179
219
  return true;
@@ -188,35 +228,39 @@ var AbstractForm = class extends AbstractPage {
188
228
  */
189
229
  async getTitleText() {
190
230
  if (await this.isModal()) {
191
- const popupEl = await this.driver.findElement(this.popupContainerBy);
192
- const titleEl2 = await popupEl.findElement(By4.className("popup-title"));
231
+ const titleEl2 = await this.driver.findElement(By5.css(`${this.popupContainerCssSelector} .popup-title`));
193
232
  return titleEl2.getText();
194
233
  }
195
- let container = await this.getContainerElement();
196
- let titleEl = await container.findElement(By4.className("form-toolbar-name-container"));
234
+ let titleEl = await this.driver.findElement(By5.css(`${this.containerCssSelector} .form-toolbar-name-container`));
197
235
  return titleEl.getText();
198
236
  }
199
237
  };
200
238
 
201
239
  // src/pageObjects/elements/Forms/FormEdit.ts
202
- import { By as By7 } from "selenium-webdriver";
240
+ import { By as By12 } from "selenium-webdriver";
203
241
 
204
242
  // src/pageObjects/elements/Editors/AbstractEditor.ts
205
- import { By as By5, Key } from "selenium-webdriver";
243
+ import { By as By6, Key } from "selenium-webdriver";
206
244
 
207
- // src/pageObjects/AbstractFormElement.ts
208
- var AbstractFormElement = class extends AbstractElementWithParent {
209
- constructor(getParentElement, elementBy, driver) {
210
- super(getParentElement, driver);
211
- this.elementBy = elementBy;
245
+ // src/pageObjects/elements/Editors/ClearStrategy/InputClearStrategy.ts
246
+ var InputClearStrategy = class {
247
+ async clear(editor) {
248
+ const inputEl = await editor.getInputElement();
249
+ await inputEl.clear();
212
250
  }
213
251
  };
214
252
 
215
253
  // src/pageObjects/elements/Editors/AbstractEditor.ts
216
- var AbstractDXEditorWithInput = class extends AbstractFormElement {
254
+ var AbstractDXEditorWithInput = class extends AbstractElementWithParent {
255
+ clearStrategy = new InputClearStrategy();
256
+ setClearStrategy(strategy) {
257
+ this.clearStrategy = strategy;
258
+ }
259
+ getInputCssSelector() {
260
+ return `${this.getFullCssSelector()} .dx-texteditor-input`;
261
+ }
217
262
  async getInputElement() {
218
- const controlEl = await this.getElement();
219
- return controlEl.findElement(By5.className("dx-texteditor-input"));
263
+ return this.driver.findElement(By6.css(this.getInputCssSelector()));
220
264
  }
221
265
  async getInputValue() {
222
266
  const inputEl = await this.getInputElement();
@@ -227,10 +271,17 @@ var AbstractDXEditorWithInput = class extends AbstractFormElement {
227
271
  await click(inputEl, this.driver);
228
272
  await inputEl.sendKeys(value, Key.ENTER);
229
273
  }
274
+ async clear() {
275
+ await this.clearStrategy.clear(this);
276
+ }
230
277
  };
231
278
 
232
279
  // src/pageObjects/elements/Editors/TextEditor.ts
233
280
  var TextEditor = class extends AbstractDXEditorWithInput {
281
+ constructor(parentCssSelector, elementCssSelector, driver) {
282
+ super(parentCssSelector, elementCssSelector, driver);
283
+ this.setClearStrategy(new InputClearStrategy());
284
+ }
234
285
  getValue() {
235
286
  return this.getInputValue();
236
287
  }
@@ -241,6 +292,10 @@ var TextEditor = class extends AbstractDXEditorWithInput {
241
292
 
242
293
  // src/pageObjects/elements/Editors/NumberEditor.ts
243
294
  var NumberEditor = class extends AbstractDXEditorWithInput {
295
+ constructor(parentCssSelector, elementCssSelector, driver) {
296
+ super(parentCssSelector, elementCssSelector, driver);
297
+ this.setClearStrategy(new InputClearStrategy());
298
+ }
244
299
  async getValue() {
245
300
  const value = await super.getInputValue();
246
301
  return value == "" ? null : Number.parseFloat(value);
@@ -250,16 +305,22 @@ var NumberEditor = class extends AbstractDXEditorWithInput {
250
305
  }
251
306
  };
252
307
 
308
+ // src/pageObjects/AbstractFormElement.ts
309
+ var AbstractFormElement = class extends AbstractElementWithParent {
310
+ constructor(parentCssSelector, elementCssSelector, driver) {
311
+ super(parentCssSelector, elementCssSelector, driver);
312
+ }
313
+ };
314
+
253
315
  // src/pageObjects/elements/Editors/CheckBox.ts
254
- var CheckBox = class extends AbstractFormElement {
316
+ var CheckBox = class extends AbstractElementWithParent {
255
317
  async getValue() {
256
- const inputEl = await this.getElement();
257
- const result = await inputEl.getAttribute("aria-checked");
318
+ const result = await this.getAttribute("aria-checked");
258
319
  if (result === "true")
259
- return true;
320
+ return 1;
260
321
  if (result === "false")
261
- return false;
262
- return void 0;
322
+ return 0;
323
+ return null;
263
324
  }
264
325
  async setValue(value) {
265
326
  const inputEl = await this.getElement();
@@ -271,7 +332,7 @@ var CheckBox = class extends AbstractFormElement {
271
332
  };
272
333
 
273
334
  // src/pageObjects/elements/Editors/SelectBoxEditor.ts
274
- import { By as By6, Key as Key2 } from "selenium-webdriver";
335
+ import { By as By8, Key as Key2 } from "selenium-webdriver";
275
336
 
276
337
  // src/utils/xpathHelper.ts
277
338
  var XPathHelper = class {
@@ -283,24 +344,50 @@ var XPathHelper = class {
283
344
  }
284
345
  };
285
346
 
347
+ // src/pageObjects/elements/Editors/ClearStrategy/ClearButtonStrategy.ts
348
+ import { By as By7 } from "selenium-webdriver";
349
+ var DX_TEXTEDITOR_EMPTY = "dx-texteditor-empty";
350
+ var DX_CLEAR_BUTTON_AREA = "dx-clear-button-area";
351
+ var ClearButtonStrategy = class {
352
+ editorContainerClass;
353
+ _driver;
354
+ constructor(editorContainerClass, driver) {
355
+ this.editorContainerClass = editorContainerClass;
356
+ this._driver = driver;
357
+ }
358
+ async isEmptyEditorField() {
359
+ const editorContainer = await this._driver.findElement(By7.css(this.editorContainerClass));
360
+ const classAttribute = await editorContainer.getAttribute("class");
361
+ return classAttribute.includes(DX_TEXTEDITOR_EMPTY);
362
+ }
363
+ async clear() {
364
+ if (await this.isEmptyEditorField()) {
365
+ throw new Error("This field is already empty");
366
+ }
367
+ const clearBtnSelector = `${this.editorContainerClass} .${DX_CLEAR_BUTTON_AREA}`;
368
+ const clearBtn = await this._driver.findElement(By7.css(clearBtnSelector));
369
+ await click(clearBtn, this._driver);
370
+ }
371
+ };
372
+
286
373
  // src/pageObjects/elements/Editors/SelectBoxEditor.ts
287
374
  var SELECT_BOX_OPTION_TEXT_CONTAINER_CLASS = "d5-multi-select-item";
288
375
  var SELECT_BOX_ELEMENT_ROLE = "option";
376
+ var combinedValueXpath = (value) => `${XPathHelper.getRoleXpath(SELECT_BOX_ELEMENT_ROLE)}${XPathHelper.getClassAndValueXpath(
377
+ SELECT_BOX_OPTION_TEXT_CONTAINER_CLASS,
378
+ value
379
+ )}`;
289
380
  var SelectBoxEditor = class extends AbstractDXEditorWithInput {
381
+ constructor(parentCssSelector, elementCssSelector, driver) {
382
+ super(parentCssSelector, elementCssSelector, driver);
383
+ this.setClearStrategy(new ClearButtonStrategy(this.getFullCssSelector(), this.driver));
384
+ }
290
385
  async setSelectInputValue(value) {
291
386
  const inputEl = await this.getInputElement();
292
387
  await click(inputEl, this.driver);
293
388
  await inputEl.sendKeys(value, Key2.ENTER);
294
- const combinedValueXpath = XPathHelper.getRoleXpath(SELECT_BOX_ELEMENT_ROLE) + XPathHelper.getClassAndValueXpath(SELECT_BOX_OPTION_TEXT_CONTAINER_CLASS, value);
295
- await this.driver.wait(async () => {
296
- try {
297
- const optElement = await inputEl.findElement(By6.xpath(combinedValueXpath));
298
- await click(optElement, this.driver);
299
- return true;
300
- } catch (e) {
301
- return false;
302
- }
303
- }, 2e3);
389
+ const optElement = await inputEl.findElement(By8.xpath(combinedValueXpath(value)));
390
+ await click(optElement, this.driver);
304
391
  }
305
392
  async getValue() {
306
393
  const value = await super.getInputValue();
@@ -312,15 +399,14 @@ var SelectBoxEditor = class extends AbstractDXEditorWithInput {
312
399
  };
313
400
 
314
401
  // src/pageObjects/elements/Editors/SwitchEditor.ts
315
- var SwitchEditor = class extends AbstractFormElement {
402
+ var SwitchEditor = class extends AbstractElementWithParent {
316
403
  async getValue() {
317
- const inputEl = await this.getElement();
318
- const result = await inputEl.getAttribute("aria-label");
404
+ const result = await this.getAttribute("aria-label");
319
405
  if (result === "ON")
320
406
  return 1;
321
407
  if (result === "OFF")
322
408
  return 0;
323
- return void 0;
409
+ return null;
324
410
  }
325
411
  async setValue(value) {
326
412
  if (value !== 0 && value !== 1) {
@@ -328,31 +414,196 @@ var SwitchEditor = class extends AbstractFormElement {
328
414
  }
329
415
  const inputEl = await this.getElement();
330
416
  const currentValue = await this.getValue();
331
- if (currentValue !== value) {
332
- await click(inputEl, this.driver);
333
- await this.driver.wait(async () => {
334
- const updatedValue = await this.getValue();
335
- return updatedValue !== currentValue;
336
- });
417
+ if (currentValue === value) {
418
+ return;
337
419
  }
420
+ await click(inputEl, this.driver);
421
+ await wait(async () => {
422
+ const updatedValue = await this.getValue();
423
+ return updatedValue !== currentValue;
424
+ }, this.driver);
425
+ }
426
+ };
427
+
428
+ // src/pageObjects/elements/Editors/TagBox.ts
429
+ import { By as By9 } from "selenium-webdriver";
430
+ var TAGS_SELECTOR = ".dx-tag .tag-text";
431
+ var DX_TAG_CONTAINER = "dx-tag-container";
432
+ var DX_TEXTEDITOR_EMPTY2 = "dx-texteditor-empty";
433
+ var BTN_OK_SELECTOR = ".dx-tagbox-popup-wrapper .dx-popup-bottom .dx-button.dx-popup-done";
434
+ var TagBox = class extends AbstractDXEditorWithInput {
435
+ constructor(parentCssSelector, elementCssSelector, driver) {
436
+ super(parentCssSelector, elementCssSelector, driver);
437
+ this.setClearStrategy(new ClearButtonStrategy(this.getFullCssSelector(), this.driver));
438
+ }
439
+ async setTagBoxInputValue(values) {
440
+ const lastElement = values[values.length - 1];
441
+ const inputEl = await this.getInputElement();
442
+ await inputEl.click();
443
+ for (const value of values) {
444
+ await this.selectOption(inputEl, value, lastElement);
445
+ }
446
+ await this.confirmSelection();
447
+ }
448
+ async selectOption(inputEl, value, lastElement) {
449
+ await inputEl.sendKeys(value);
450
+ const optElement = await inputEl.findElement(By9.xpath(combinedValueXpath(value)));
451
+ await click(optElement, this.driver);
452
+ if (value !== lastElement) {
453
+ await inputEl.clear();
454
+ }
455
+ }
456
+ async confirmSelection() {
457
+ const inputEl = await this.getInputElement();
458
+ const option = await inputEl.findElement(By9.xpath(XPathHelper.getRoleXpath(SELECT_BOX_ELEMENT_ROLE)));
459
+ 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
+ await click(btnOk, this.driver);
464
+ }
465
+ async getTagBoxInputValue() {
466
+ if (await this.isTagBoxEmpty()) {
467
+ return [];
468
+ }
469
+ const tags = await this.driver.findElements(By9.css(`${this.getFullCssSelector()} .${DX_TAG_CONTAINER} ${TAGS_SELECTOR}`));
470
+ const values = [];
471
+ for (const tag of tags) {
472
+ const text = await tag.getText();
473
+ values.push(text);
474
+ }
475
+ return values;
476
+ }
477
+ async isTagBoxEmpty() {
478
+ const classAttribute = await this.getClasses();
479
+ return classAttribute.includes(DX_TEXTEDITOR_EMPTY2);
480
+ }
481
+ async getValue() {
482
+ const value = await this.getTagBoxInputValue();
483
+ return value.length ? value : null;
484
+ }
485
+ async setValue(values) {
486
+ if (!Array.isArray(values)) {
487
+ throw new Error("The provided value is not an array");
488
+ }
489
+ await this.setTagBoxInputValue(values);
490
+ }
491
+ async remove(itemText) {
492
+ const tagValues = await this.getValue();
493
+ const tagExists = tagValues.includes(itemText);
494
+ if (!tagExists) {
495
+ throw new Error(`The tag with the text: ${itemText} was not found`);
496
+ }
497
+ const clearBtnTestId = byTestIdCssSelector(`clear-button-${itemText}`);
498
+ const clearBtn = await this.driver.findElement(By9.css(`${this.getFullCssSelector()} .${DX_TAG_CONTAINER} ${clearBtnTestId}`));
499
+ await click(clearBtn, this.driver);
500
+ }
501
+ };
502
+
503
+ // src/pageObjects/elements/Editors/ButtonGroup.ts
504
+ import { By as By10 } from "selenium-webdriver";
505
+ var ITEM_CLASS = "dx-buttongroup-item";
506
+ var SELECTED_CLASS = "dx-state-selected";
507
+ var ButtonGroup = class extends AbstractElementWithParent {
508
+ getItems() {
509
+ return this.driver.findElements(By10.css(`${this.getFullCssSelector()} .${ITEM_CLASS}`));
510
+ }
511
+ getDataValue(el) {
512
+ return el.getAttribute("data-value");
513
+ }
514
+ async getValue() {
515
+ const selectedEl = await this.driver.findElement(By10.css(`${this.getFullCssSelector()} .${ITEM_CLASS}.${SELECTED_CLASS}`));
516
+ return this.getDataValue(selectedEl);
517
+ }
518
+ async setValue(value) {
519
+ const items = await this.getItems();
520
+ let buttonEl = void 0;
521
+ for (const item of items) {
522
+ const text = await this.getDataValue(item);
523
+ if (text == value) {
524
+ buttonEl = item;
525
+ break;
526
+ }
527
+ }
528
+ if (!buttonEl)
529
+ throw new Error(`Cannot find item in button group with text: ${value}`);
530
+ await click(buttonEl, this.driver);
531
+ }
532
+ };
533
+
534
+ // src/pageObjects/elements/Editors/BooleanSelector.ts
535
+ var BooleanSelector = class extends ButtonGroup {
536
+ // @ts-ignore
537
+ async getValue() {
538
+ const value = await super.getValue();
539
+ return value === "null" ? null : value === "1" ? 1 : 0;
540
+ }
541
+ // @ts-ignore
542
+ async setValue(value) {
543
+ const val = value == null ? "null" : value ? "1" : "0";
544
+ await super.setValue(val);
545
+ }
546
+ };
547
+
548
+ // src/pageObjects/elements/Editors/DateEditor.ts
549
+ import { By as By11, Key as Key3 } from "selenium-webdriver";
550
+ var DateEditor = class extends AbstractDXEditorWithInput {
551
+ constructor(parentCssSelector, elementCssSelector, driver) {
552
+ super(parentCssSelector, elementCssSelector, driver);
553
+ this.setClearStrategy(new InputClearStrategy());
554
+ }
555
+ async getDateElement() {
556
+ return this.driver.findElement(By11.css(this.getFullCssSelector() + " .dx-dropdowneditor-input-wrapper input[type=hidden]"));
557
+ }
558
+ async setDateValue(value) {
559
+ const dateEl = await this.getInputElement();
560
+ const stringDate = value.toJSON().split("T")[0];
561
+ const dateArr = stringDate.split("-");
562
+ const [year, month, day] = dateArr;
563
+ const keys = day + month + year;
564
+ await click(dateEl, this.driver);
565
+ const actions = this.driver.actions({ async: true });
566
+ await actions.move({ origin: dateEl }).sendKeys(Key3.HOME).perform();
567
+ await actions.move({ origin: dateEl }).sendKeys(keys, Key3.ENTER).perform();
568
+ }
569
+ async getDateValue() {
570
+ const inputEl = await this.getDateElement();
571
+ return inputEl.getAttribute("value");
572
+ }
573
+ async getValue() {
574
+ const value = await this.getDateValue();
575
+ return value == "" ? null : new Date(value);
576
+ }
577
+ setValue(value) {
578
+ if (typeof value === "object") {
579
+ return this.setDateValue(value);
580
+ }
581
+ throw new Error(`Value is incorrect. It should be Object`);
338
582
  }
339
583
  };
340
584
 
341
585
  // src/pageObjects/elements/Editors/editorFactory.ts
342
- var editorFactory = async (getParentElement, elementBy, driver) => {
343
- const elementInstance = new AbstractFormElement(getParentElement, elementBy, driver);
344
- const element = await elementInstance.getElement();
345
- const classSting = await element.getAttribute("class");
586
+ var editorFactory = async (parentCssSelector, elementCssSelector, driver) => {
587
+ const elementInstance = new AbstractFormElement(parentCssSelector, elementCssSelector, driver);
588
+ const classSting = await elementInstance.getClasses();
346
589
  if (classSting.includes("text-control"))
347
- return new TextEditor(getParentElement, elementBy, driver);
590
+ return new TextEditor(parentCssSelector, elementCssSelector, driver);
348
591
  if (classSting.includes("number-control"))
349
- return new NumberEditor(getParentElement, elementBy, driver);
592
+ return new NumberEditor(parentCssSelector, elementCssSelector, driver);
350
593
  if (classSting.includes("check-box"))
351
- return new CheckBox(getParentElement, elementBy, driver);
594
+ return new CheckBox(parentCssSelector, elementCssSelector, driver);
595
+ if (classSting.includes("multi-select-control"))
596
+ return new TagBox(parentCssSelector, elementCssSelector, driver);
352
597
  if (classSting.includes("select-control"))
353
- return new SelectBoxEditor(getParentElement, elementBy, driver);
598
+ return new SelectBoxEditor(parentCssSelector, elementCssSelector, driver);
354
599
  if (classSting.includes("switch-control"))
355
- return new SwitchEditor(getParentElement, elementBy, driver);
600
+ return new SwitchEditor(parentCssSelector, elementCssSelector, driver);
601
+ if (classSting.includes("date-control"))
602
+ return new DateEditor(parentCssSelector, elementCssSelector, driver);
603
+ if (classSting.includes("buttons-group"))
604
+ return new BooleanSelector(parentCssSelector, elementCssSelector, driver);
605
+ if (classSting.includes("button-group-field"))
606
+ return new ButtonGroup(parentCssSelector, elementCssSelector, driver);
356
607
  throw new Error("Unknown type of editor. Please write ticket to the support");
357
608
  };
358
609
 
@@ -363,6 +614,9 @@ function createDataTestId(formName, itemType, itemName) {
363
614
  function createFieldTestId(formName, itemName) {
364
615
  return createDataTestId(formName, "form_field" /* FORM_FIELD */, itemName);
365
616
  }
617
+ function createGroupTestId(formName, itemName) {
618
+ return createDataTestId(formName, "group" /* GROUP */, itemName);
619
+ }
366
620
  function createTableId(formName) {
367
621
  return `${"table" /* TABLE */}-${formName}`;
368
622
  }
@@ -370,13 +624,12 @@ function createTableId(formName) {
370
624
  // src/pageObjects/elements/FormField.ts
371
625
  var FormField = class extends AbstractElementWithParent {
372
626
  editor;
373
- constructor(getParentElement, formName, name, driver) {
374
- super(getParentElement, driver);
375
- this.elementBy = byTestId(createFieldTestId(formName, name));
627
+ constructor(parentCssSelector, formName, name, driver) {
628
+ super(parentCssSelector, byTestIdCssSelector(createFieldTestId(formName, name)), driver);
376
629
  }
377
630
  async initEditor() {
378
631
  if (!this.editor) {
379
- this.editor = await editorFactory(this.getParentElement, this.elementBy, this.driver);
632
+ this.editor = await editorFactory(this.parentCssSelector, this.elementCssSelector, this.driver);
380
633
  }
381
634
  }
382
635
  /**
@@ -399,14 +652,85 @@ var FormField = class extends AbstractElementWithParent {
399
652
  await this.initEditor();
400
653
  return this.editor.setValue(value);
401
654
  }
655
+ /**
656
+ * Очищает значение в поле
657
+ * @example
658
+ * await formEdit.field('NumberFld').clear();
659
+ */
660
+ async clear() {
661
+ await this.initEditor();
662
+ if (!this.editor.clear) {
663
+ throw new Error("The clear method is not available for this field type.");
664
+ }
665
+ return this.editor.clear();
666
+ }
667
+ /**
668
+ * Удаляет тег по переданному тексту
669
+ * @example
670
+ * await formEdit.field('TagBox').remove('test');
671
+ */
672
+ async remove(itemText) {
673
+ await this.initEditor();
674
+ if (!this.editor.remove) {
675
+ throw new Error("This method is only available for the TagBox field.");
676
+ }
677
+ return this.editor.remove(itemText);
678
+ }
679
+ };
680
+
681
+ // src/pageObjects/elements/Groups/AbstractGroup.ts
682
+ var AbstractGroup = class extends AbstractElementWithParent {
683
+ async isDisplayed() {
684
+ const groupElement = await this.getElement();
685
+ return await groupElement.isDisplayed();
686
+ }
687
+ };
688
+
689
+ // src/pageObjects/elements/Groups/Panel.ts
690
+ var Panel = class extends AbstractGroup {
691
+ };
692
+
693
+ // src/pageObjects/elements/Groups/groupFactory.ts
694
+ var groupFactory = async (parentCssSelector, elementCssSelector, driver) => {
695
+ const elementInstance = new AbstractFormElement(parentCssSelector, elementCssSelector, driver);
696
+ const classSting = await elementInstance.getClasses();
697
+ if (classSting.includes("panel-group"))
698
+ return new Panel(parentCssSelector, elementCssSelector, driver);
699
+ throw new Error("Unknown type of group. Please write ticket to the support");
700
+ };
701
+
702
+ // src/pageObjects/elements/FormGroup.ts
703
+ var FormGroup = class extends AbstractElementWithParent {
704
+ editor;
705
+ constructor(parentCssSelector, formName, name, driver) {
706
+ super(parentCssSelector, byTestIdCssSelector(createGroupTestId(formName, name)), driver);
707
+ }
708
+ async initEditor() {
709
+ if (!this.editor) {
710
+ this.editor = await groupFactory(this.parentCssSelector, this.elementCssSelector, this.driver);
711
+ }
712
+ }
713
+ /**
714
+ * Возвращает булевое значение доступна ли группа
715
+ * @example
716
+ * expect(await tableList.group('MainSuperPanel').isDisplayed();
717
+ */
718
+ async isDisplayed() {
719
+ try {
720
+ await this.initEditor();
721
+ return this.editor.isDisplayed();
722
+ } catch {
723
+ return false;
724
+ }
725
+ }
402
726
  };
403
727
 
404
728
  // src/pageObjects/elements/Forms/FormEdit.ts
405
729
  var FormEdit = class extends AbstractForm {
406
- saveButtonBy = By7.id("button-save");
730
+ saveButtonBy = By12.id("button-save");
407
731
  constructor(driver) {
408
732
  super(driver);
409
- this.popupContainerBy = By7.className(`popup form-edit-${this.formName}`);
733
+ this.popupContainerCssSelector = `.popup .form-edit-${this.formName}`;
410
734
  }
411
735
  /**
412
736
  * Находит поле формы редактирования по имени
@@ -416,7 +740,29 @@ var FormEdit = class extends AbstractForm {
416
740
  * expect(await formEdit.field('Name').getValue()).toBe('test value');
417
741
  */
418
742
  field(name) {
419
- return new FormField(this.getContainerElement, this.formName, name, this.driver);
743
+ return new FormField(this.containerCssSelector, this.formName, name, this.driver);
744
+ }
745
+ /**
746
+ * Проверяет не отключена ли кнопка Сохранить
747
+ * @example
748
+ * const formEdit = new MyForm();
749
+ * //...
750
+ * await formEdit.isSaveButtonDisabled();
751
+ */
752
+ async isSaveButtonDisabled() {
753
+ const saveButton = await this.driver.findElement(this.saveButtonBy);
754
+ const result = await saveButton.getAttribute("aria-disabled");
755
+ return result === "true";
756
+ }
757
+ /**
758
+ * Находит группу формы редактирования по имени
759
+ * @example
760
+ * const formEdit = new MyForm();
761
+ * //...
762
+ * expect(await formEdit.group('Name');
763
+ */
764
+ group(name) {
765
+ return new FormGroup(this.containerCssSelector, this.formName, name, this.driver);
420
766
  }
421
767
  /**
422
768
  * Нажатие на кнопку Сохранить
@@ -426,145 +772,167 @@ var FormEdit = class extends AbstractForm {
426
772
  * await formEdit.saveButtonClick();
427
773
  */
428
774
  async saveButtonClick() {
429
- await click(await this.driver.findElement(this.saveButtonBy), this.driver);
775
+ const saveButton = await this.driver.findElement(this.saveButtonBy);
776
+ await wait(async () => !await this.isSaveButtonDisabled(), this.driver, "Button is disabled");
777
+ await click(saveButton, this.driver);
430
778
  }
431
779
  };
432
780
 
433
- // src/pageObjects/elements/Forms/ListForm.ts
434
- import { By as By13 } from "selenium-webdriver";
435
-
436
781
  // src/pageObjects/elements/Table/Table.ts
437
- import { By as By10 } from "selenium-webdriver";
782
+ import { By as By16 } from "selenium-webdriver";
783
+
784
+ // src/pageObjects/elements/Table/elements/Row.ts
785
+ import { By as By14 } from "selenium-webdriver";
438
786
 
439
787
  // src/pageObjects/elements/Table/utils.ts
440
- import { By as By8 } from "selenium-webdriver";
441
- var getRowPathByIndex = (index) => {
442
- return By8.css(`tr[aria-rowindex="${transformInputIndex(index)}"]`);
443
- };
444
- var getColumnPathByIndex = (index) => {
445
- return By8.css(`td[aria-colindex="${transformInputIndex(index)}"]`);
446
- };
447
- var getColumnPathByCaption = (caption) => {
448
- return By8.css(`td[role="columnheader"][aria-label="Column ${caption}"]`);
449
- };
450
- var getCellPathByIndex = (index) => {
451
- return By8.css(`td[role="gridcell"][aria-colindex="${transformInputIndex(index)}"]`);
452
- };
788
+ import { By as By13 } from "selenium-webdriver";
789
+ function getCssRowByIndexSelector(index) {
790
+ return `tr[aria-rowindex="${transformInputIndex(index)}"]`;
791
+ }
792
+ var getColumnByIndexCssSelector = (columnIndex) => `td[aria-colindex="${transformInputIndex(columnIndex)}"]`;
793
+ var getColumnByCaptionCssSelector = (caption) => `td[role="columnheader"][aria-label="Column ${caption}"]`;
794
+ function getCssCellByIndexSelector(index) {
795
+ return `td[role="gridcell"][aria-colindex="${transformInputIndex(index)}"]`;
796
+ }
453
797
  var transformInputIndex = (index) => index + 1;
454
798
  var transformOutputIndex = (index) => +index - 1;
455
799
 
456
- // src/pageObjects/elements/Table/elements/Row.ts
457
- import { By as By9 } from "selenium-webdriver";
458
-
459
800
  // src/pageObjects/elements/Table/elements/Cell.ts
460
801
  var Cell = class extends AbstractElementWithParent {
461
- index;
462
- constructor(rowEl, index, driver) {
463
- super(() => Promise.resolve(rowEl), driver);
464
- this.index = index;
465
- this.elementBy = getCellPathByIndex(index);
802
+ _index;
803
+ getCellIndex;
804
+ constructor(parentCssSelector, getCellIndex, index, driver) {
805
+ super(parentCssSelector, getCssCellByIndexSelector(index), driver);
806
+ this.getCellIndex = getCellIndex;
807
+ this._index = index;
808
+ }
809
+ set index(index) {
810
+ this._index = index;
811
+ this.elementCssSelector = getCssCellByIndexSelector(index);
812
+ }
813
+ async getElement() {
814
+ if (this._index == null) {
815
+ this.index = await this.getCellIndex();
816
+ }
817
+ return super.getElement();
466
818
  }
467
819
  async getValue() {
468
820
  const cellEl = await this.getElement();
469
821
  return cellEl.getText();
470
822
  }
471
823
  getIndex() {
472
- return this.index;
824
+ return this._index;
473
825
  }
474
826
  };
475
827
 
476
828
  // src/pageObjects/elements/Table/elements/Row.ts
477
829
  var DX_SELECTION_CLASS = "dx-selection";
478
830
  var DX_ROW_EDIT_CLASS = "dx-edit-row";
831
+ var DX_DATA_ROW_CLASS = "dx-data-row";
479
832
  var Row = class extends AbstractElementWithParent {
480
833
  index;
481
834
  getColumnByCaption;
482
- constructor(getParentElement, index, driver, getColumnByCaption) {
483
- super(getParentElement, driver);
835
+ constructor(parentCssSelector, index, driver, getColumnByCaption) {
836
+ super(parentCssSelector, getCssRowByIndexSelector(index), driver);
484
837
  this.index = index;
485
- this.elementBy = getRowPathByIndex(index);
486
838
  this.getColumnByCaption = getColumnByCaption;
487
839
  }
488
- async getCellByIndex(index) {
489
- const rowEl = await this.getElement();
490
- const cell = rowEl.findElement(getCellPathByIndex(index));
491
- if (cell) {
492
- return new Cell(rowEl, index, this.driver);
493
- }
494
- throw new Error(`Cannot find cell by index: ${index}`);
840
+ _getCell(index, caption) {
841
+ return new Cell(this.getFullCssSelector(), () => {
842
+ return this.getColumnByCaption(caption).getIndex();
843
+ }, index, this.driver);
495
844
  }
496
- async getCell(caption) {
497
- const column = await this.getColumnByCaption(caption);
498
- if (column) {
499
- const index = column.getIndex();
500
- return this.getCellByIndex(index);
501
- }
502
- throw new Error("Unknown caption of cell");
845
+ getCellByIndex(index) {
846
+ return this._getCell(index);
847
+ }
848
+ getCell(caption) {
849
+ return this._getCell(void 0, caption);
503
850
  }
504
851
  async select() {
505
852
  const rowEl = await this.getElement();
506
853
  await click(rowEl, this.driver);
507
- await this.driver.wait(async () => {
508
- const classes = await rowEl.getAttribute("class");
509
- if (!classes.includes(DX_SELECTION_CLASS))
510
- throw new Error("Cannot select row");
511
- return true;
512
- }, 500);
854
+ await wait(async () => {
855
+ const classes = await this.getClasses();
856
+ return classes.includes(DX_SELECTION_CLASS);
857
+ }, this.driver, "Cannot select row");
513
858
  }
514
859
  async isSelected() {
515
- const rowEl = await this.getElement();
516
- const result = await rowEl.getAttribute("aria-selected");
860
+ const result = await this.getAttribute("aria-selected");
517
861
  return result === "true";
518
862
  }
519
863
  getIndex() {
520
864
  return this.index;
521
865
  }
522
866
  async getCells() {
523
- const rowEl = await this.getElement();
524
- const cells = await rowEl.findElements(By9.css(`td[role="gridcell"]`));
867
+ const cells = await this.driver.findElements(By14.css(`${this.getFullCssSelector()} td[role="gridcell"]`));
525
868
  const result = [];
526
869
  cells.forEach((_, index) => {
527
- const newCell = new Cell(rowEl, index, this.driver);
870
+ const newCell = this.getCellByIndex(index);
528
871
  result.push(newCell);
529
872
  });
530
873
  return result;
531
874
  }
532
- async clickInlineButton(rowClass, buttonClass) {
533
- const fixedTable = (await this.getParentElement()).findElement(
534
- By9.css(".dx-fixed-columns .dx-datagrid-content.dx-datagrid-content-fixed")
535
- );
536
- const rowEl = await fixedTable.findElement(By9.className(rowClass));
537
- const button = await rowEl.findElement(By9.className(buttonClass));
538
- if (button) {
539
- return click(button, this.driver);
540
- }
541
- throw new Error(`Cannot find ${buttonClass} button`);
875
+ async clickInlineButton(buttonClass) {
876
+ const FIXED_TABLE_CSS_SELECTOR = ".dx-fixed-columns .dx-datagrid-content.dx-datagrid-content-fixed";
877
+ const buttonCssSelector = `.${DX_DATA_ROW_CLASS}[aria-rowindex="${transformInputIndex(this.index)}"] .${buttonClass}`;
878
+ const button = await this.driver.findElement(By14.css(`${this.parentCssSelector} ${FIXED_TABLE_CSS_SELECTOR} ${buttonCssSelector}`));
879
+ return click(button, this.driver);
880
+ }
881
+ async checkEditingMode(isEditing, msg) {
882
+ await wait(async () => {
883
+ return (await this.getClasses()).includes(DX_ROW_EDIT_CLASS) === isEditing;
884
+ }, this.driver, msg);
542
885
  }
543
886
  async edit() {
544
- await this.select();
545
- return this.clickInlineButton(DX_SELECTION_CLASS, "edit");
887
+ await this.clickInlineButton("edit");
888
+ await this.checkEditingMode(true, "Row did not switch to edit mode");
546
889
  }
547
890
  async save() {
548
- return this.clickInlineButton(DX_ROW_EDIT_CLASS, "save");
891
+ await this.clickInlineButton("save");
892
+ await this.checkEditingMode(false, "Row did not saved");
549
893
  }
550
894
  async cancel() {
551
- return this.clickInlineButton(DX_ROW_EDIT_CLASS, "cancel");
895
+ await this.clickInlineButton("cancel");
896
+ await this.checkEditingMode(false, "Row did not switch to normal mode");
552
897
  }
553
898
  };
554
899
 
555
900
  // src/pageObjects/elements/Table/elements/Column.ts
901
+ import { By as By15 } from "selenium-webdriver";
902
+ var getHeaderCssSelector = () => {
903
+ return ".dx-datagrid-headers .dx-datagrid-content:not(.dx-datagrid-content-fixed)";
904
+ };
556
905
  var Column = class extends AbstractElementWithParent {
557
- index;
558
- constructor(getParentElement, index, driver) {
559
- super(getParentElement, driver);
906
+ _index;
907
+ _caption;
908
+ constructor(parentCssSelector, caption, index, driver) {
909
+ super(parentCssSelector, "", driver);
560
910
  this.index = index;
561
- this.elementBy = getColumnPathByIndex(index);
911
+ this._caption = caption;
912
+ }
913
+ set index(index) {
914
+ this._index = index;
915
+ this.elementCssSelector = getColumnByIndexCssSelector(index);
916
+ }
917
+ get index() {
918
+ return this._index;
919
+ }
920
+ async getElement() {
921
+ await this.getIndex();
922
+ return super.getElement();
923
+ }
924
+ async getIndexByCaption() {
925
+ const column = await this.driver.findElement(By15.css(`${this.parentCssSelector} ${getHeaderCssSelector()} ${getColumnByCaptionCssSelector(this._caption)}`));
926
+ return transformOutputIndex(await column.getAttribute("aria-colindex"));
562
927
  }
563
928
  async caption() {
564
929
  const el = await this.getElement();
565
930
  return el.getText();
566
931
  }
567
- getIndex() {
932
+ async getIndex() {
933
+ if (this.index == null) {
934
+ this.index = await this.getIndexByCaption();
935
+ }
568
936
  return this.index;
569
937
  }
570
938
  };
@@ -572,71 +940,36 @@ var Column = class extends AbstractElementWithParent {
572
940
  // src/pageObjects/elements/Table/Table.ts
573
941
  var Table = class extends AbstractElementWithParent {
574
942
  formName;
575
- constructor(getParentElement, formName, driver) {
576
- super(getParentElement, driver);
577
- this.elementBy = By10.id(createTableId(formName));
943
+ constructor(parentCssSelector, formName, driver) {
944
+ super(parentCssSelector, `#${createTableId(formName)}`, driver);
578
945
  this.formName = formName;
579
946
  }
580
- async getHeaderElement() {
581
- const parentEl = await this.getElement();
582
- return parentEl.findElement(By10.className("dx-header-row"));
947
+ getRowByIndex(index) {
948
+ return new Row(this.getFullCssSelector(), index, this.driver, this.getColumnByCaption.bind(this));
583
949
  }
584
- async getDataGrid() {
585
- const parentEl = await this.getElement();
586
- return parentEl.findElement(By10.css(`div[role="grid"]`));
950
+ getColumnByIndex(index) {
951
+ return new Column(this.getFullCssSelector(), void 0, index, this.driver);
587
952
  }
588
- getTableElement = () => {
589
- return this.driver.findElement(this.elementBy);
953
+ getColumnByCaption = (caption) => {
954
+ return new Column(this.getFullCssSelector(), caption, void 0, this.driver);
590
955
  };
591
- async getRowByIndex(index) {
592
- const gridContainer = await this.getDataGrid();
593
- const row = await gridContainer.findElement(getRowPathByIndex(index));
594
- if (row) {
595
- return new Row(this.getTableElement, index, this.driver, this.getColumnByCaption.bind(this));
596
- }
597
- throw new Error(`Cannot find row by index: ${index}`);
598
- }
599
- async getColumnByIndex(index) {
600
- const headerContainer = await this.getHeaderElement();
601
- const column = await headerContainer.findElement(getColumnPathByIndex(index));
602
- if (column) {
603
- return new Column(this.getTableElement, index, this.driver);
604
- }
605
- throw new Error(`Cannot find column by index: ${index}`);
606
- }
607
- async getColumnByCaption(caption) {
608
- const headerContainer = await this.getHeaderElement();
609
- const column = await headerContainer.findElement(getColumnPathByCaption(caption));
610
- if (column) {
611
- const index = await column.getAttribute("aria-colindex");
612
- return new Column(this.getTableElement, transformOutputIndex(index), this.driver);
613
- }
614
- throw new Error(`Cannot find column by caption: ${caption}`);
615
- }
616
- async selectRowByIndex(index) {
617
- const row = await this.getRowByIndex(index);
618
- if (row) {
619
- await row.select();
620
- } else {
621
- throw new Error(`Cannot find row by index: ${index}`);
622
- }
956
+ selectRowByIndex(index) {
957
+ return this.getRowByIndex(index).select();
623
958
  }
624
959
  async getRows() {
625
- const gridContainer = await this.getDataGrid();
626
- const rows = await gridContainer.findElements(By10.className("dx-row dx-data-row"));
960
+ const rows = await this.driver.findElements(By16.css(`${this.getFullCssSelector()} .dx-row.dx-data-row`));
627
961
  const result = [];
628
962
  rows.forEach((_, index) => {
629
- const newRow = new Row(this.getTableElement, index, this.driver, this.getColumnByCaption.bind(this));
963
+ const newRow = new Row(this.getFullCssSelector(), index, this.driver, this.getColumnByCaption);
630
964
  result.push(newRow);
631
965
  });
632
966
  return result;
633
967
  }
634
968
  async getColumns() {
635
- const headerContainer = await this.getHeaderElement();
636
- const columns = await headerContainer.findElements(By10.css(`td[role="columnheader"]`));
969
+ const columns = await this.driver.findElements(By16.css(`${this.getFullCssSelector()} ${getHeaderCssSelector()} td[role="columnheader"]`));
637
970
  const result = [];
638
971
  columns.forEach((_, index) => {
639
- const newCol = new Column(this.getTableElement, index, this.driver);
972
+ const newCol = new Column(this.getFullCssSelector(), void 0, index, this.driver);
640
973
  result.push(newCol);
641
974
  });
642
975
  return result;
@@ -652,49 +985,39 @@ var Table = class extends AbstractElementWithParent {
652
985
  * expect(await form.field('Name').getValue()).toBe('test value');
653
986
  */
654
987
  field(name) {
655
- return new FormField(this.getElement, this.formName, name, this.driver);
988
+ return new FormField(this.getFullCssSelector(), this.formName, name, this.driver);
656
989
  }
657
990
  };
658
991
 
659
- // src/pageObjects/elements/TableToolbar/TableToolbar.ts
660
- import { By as By12 } from "selenium-webdriver";
661
-
662
992
  // src/pageObjects/elements/TableToolbar/TableToolbarButton.ts
663
- import { By as By11 } from "selenium-webdriver";
664
993
  var TOOLBAR_BUTTON_CLASS = "toolbar-button";
665
994
  var TableToolbarButton = class extends AbstractElementWithParent {
666
- constructor(getParentElement, name, driver) {
667
- super(getParentElement, driver);
668
- this.elementBy = By11.id(`${TOOLBAR_BUTTON_CLASS}-${name}`);
995
+ name;
996
+ constructor(parentCssSelector, name, driver) {
997
+ super(parentCssSelector, `#${TOOLBAR_BUTTON_CLASS}-${name}`, driver);
998
+ this.name = name;
669
999
  }
670
1000
  async click() {
671
1001
  const button = await this.getElement();
672
- return click(button, this.driver);
1002
+ await wait(async () => {
1003
+ return await button.isDisplayed() && !await this.isDisabled();
1004
+ }, this.driver, `Button ${this.name} cannot be clicked. It is not displayed or it is disabled`);
1005
+ await click(button, this.driver);
673
1006
  }
674
1007
  async isDisabled() {
675
1008
  const button = await this.getElement();
676
- const result = await button.getAttribute("aria-disabled");
677
- return result === "true";
1009
+ return await button.getCssValue("pointer-events") === "none";
678
1010
  }
679
1011
  };
680
1012
 
681
1013
  // src/pageObjects/elements/TableToolbar/TableToolbar.ts
682
1014
  var TABLE_TOOLBAR_CLASS = "d5-table-toolbar";
683
1015
  var TableToolbar = class extends AbstractElementWithParent {
684
- constructor(getParentElement, formName, driver) {
685
- super(getParentElement, driver);
686
- this.elementBy = By12.id(`${TABLE_TOOLBAR_CLASS}-${formName}`);
1016
+ constructor(parentCssSelector, formName, driver) {
1017
+ super(parentCssSelector, `#${TABLE_TOOLBAR_CLASS}-${formName}`, driver);
687
1018
  }
688
- getTableToolbarElement = () => {
689
- return this.driver.findElement(this.elementBy);
690
- };
691
- async button(name) {
692
- const toolbar = await this.getTableToolbarElement();
693
- const button = await toolbar.findElement(By12.id(`${TOOLBAR_BUTTON_CLASS}-${name}`));
694
- if (button) {
695
- return new TableToolbarButton(this.getTableToolbarElement, name, this.driver);
696
- }
697
- throw new Error(`Cannot find button by name: ${name}`);
1019
+ button(name) {
1020
+ return new TableToolbarButton(this.getFullCssSelector(), name, this.driver);
698
1021
  }
699
1022
  };
700
1023
 
@@ -702,7 +1025,7 @@ var TableToolbar = class extends AbstractElementWithParent {
702
1025
  var ListForm = class extends AbstractForm {
703
1026
  constructor(driver) {
704
1027
  super(driver);
705
- this.popupContainerBy = By13.className(`popup form-inline-${this.formName}`);
1028
+ this.popupContainerCssSelector = `.popup .form-inline-${this.formName}`;
706
1029
  }
707
1030
  /**
708
1031
  * Находит таблицу
@@ -712,7 +1035,17 @@ var ListForm = class extends AbstractForm {
712
1035
  * expect(listForm.table());
713
1036
  */
714
1037
  table() {
715
- return new Table(this.getContainerElement, this.formName, this.driver);
1038
+ return new Table(this.containerCssSelector, this.formName, this.driver);
1039
+ }
1040
+ /**
1041
+ * Находит группу на лист форме по имени
1042
+ * @example
1043
+ * const formEdit = new MyForm();
1044
+ * //...
1045
+ * expect(await formEdit.group('Name');
1046
+ */
1047
+ group(name) {
1048
+ return new FormGroup(this.containerCssSelector, this.formName, name, this.driver);
716
1049
  }
717
1050
  /**
718
1051
  * Находит таблицу
@@ -722,7 +1055,7 @@ var ListForm = class extends AbstractForm {
722
1055
  * expect(listForm.toolbarButton());
723
1056
  */
724
1057
  toolbarButton(name) {
725
- const toolbar = new TableToolbar(this.getContainerElement, this.formName, this.driver);
1058
+ const toolbar = new TableToolbar(this.containerCssSelector, this.formName, this.driver);
726
1059
  return toolbar.button(name);
727
1060
  }
728
1061
  };
@@ -750,6 +1083,14 @@ var LoginPage = class extends AbstractPage {
750
1083
  usernameField = byTestId("loginField", "input");
751
1084
  passwordField = byTestId("passwordField", "input");
752
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
+ }
753
1094
  get path() {
754
1095
  return LOGIN_PATH;
755
1096
  }
@@ -781,10 +1122,13 @@ export {
781
1122
  SubsystemsPanel,
782
1123
  TextEditor,
783
1124
  byTestId,
1125
+ byTestIdCssSelector,
784
1126
  click,
785
1127
  config,
786
1128
  createDriver,
787
1129
  editorFactory,
788
1130
  elementIsNotPresent,
789
- signIn
1131
+ signIn,
1132
+ wait,
1133
+ waitElementIsVisible
790
1134
  };