d5-testing-library 1.0.5 → 1.1.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.
@@ -78,7 +78,7 @@ var AbstractPage = class extends AbstractElement {
78
78
  // src/utils/locators.ts
79
79
  import { By } from "selenium-webdriver";
80
80
  var byTestId = (testId, element = "div") => {
81
- return By.css(`${element}[data-testid=${testId}]`);
81
+ return By.css(`${element}[data-testid="${testId}"]`);
82
82
  };
83
83
 
84
84
  // src/utils/createDriver.ts
@@ -102,6 +102,11 @@ var elementIsNotPresent = function elementIsNotPresent2(locator) {
102
102
 
103
103
  // src/utils/waitElementIsVisible.ts
104
104
  import { until } from "selenium-webdriver";
105
+ async function waitElementIsVisible({ element, timeout, errorMessage, driver }) {
106
+ driver = driver ?? config().driver;
107
+ const condition = until.elementIsVisible(element);
108
+ await driver.wait(condition.fn, timeout ?? 1e4, errorMessage ?? condition.description());
109
+ }
105
110
 
106
111
  // src/pageObjects/AbstractElementWithParent.ts
107
112
  var AbstractElementWithParent = class extends AbstractElement {
@@ -113,7 +118,23 @@ var AbstractElementWithParent = class extends AbstractElement {
113
118
  }
114
119
  async getElement() {
115
120
  const parentEl = await this.getParentElement();
116
- return parentEl.findElement(this.elementBy);
121
+ await waitElementIsVisible({
122
+ element: parentEl,
123
+ driver: this.driver
124
+ });
125
+ const el = await parentEl.findElement(this.elementBy);
126
+ await waitElementIsVisible({
127
+ element: el,
128
+ driver: this.driver
129
+ });
130
+ return el;
131
+ }
132
+ async getAttribute(attr) {
133
+ const el = await this.getElement();
134
+ return await el.getAttribute(attr);
135
+ }
136
+ async getClasses() {
137
+ return await this.getAttribute("class");
117
138
  }
118
139
  };
119
140
 
@@ -199,7 +220,7 @@ var AbstractForm = class extends AbstractPage {
199
220
  };
200
221
 
201
222
  // src/pageObjects/elements/Forms/FormEdit.ts
202
- import { By as By7 } from "selenium-webdriver";
223
+ import { By as By11 } from "selenium-webdriver";
203
224
 
204
225
  // src/pageObjects/elements/Editors/AbstractEditor.ts
205
226
  import { By as By5, Key } from "selenium-webdriver";
@@ -212,8 +233,20 @@ var AbstractFormElement = class extends AbstractElementWithParent {
212
233
  }
213
234
  };
214
235
 
236
+ // src/pageObjects/elements/Editors/ClearStrategy/InputClearStrategy.ts
237
+ var InputClearStrategy = class {
238
+ async clear(editor) {
239
+ const inputEl = await editor.getInputElement();
240
+ await inputEl.clear();
241
+ }
242
+ };
243
+
215
244
  // src/pageObjects/elements/Editors/AbstractEditor.ts
216
245
  var AbstractDXEditorWithInput = class extends AbstractFormElement {
246
+ clearStrategy = new InputClearStrategy();
247
+ setClearStrategy(strategy) {
248
+ this.clearStrategy = strategy;
249
+ }
217
250
  async getInputElement() {
218
251
  const controlEl = await this.getElement();
219
252
  return controlEl.findElement(By5.className("dx-texteditor-input"));
@@ -227,10 +260,17 @@ var AbstractDXEditorWithInput = class extends AbstractFormElement {
227
260
  await click(inputEl, this.driver);
228
261
  await inputEl.sendKeys(value, Key.ENTER);
229
262
  }
263
+ async clear() {
264
+ await this.clearStrategy.clear(this);
265
+ }
230
266
  };
231
267
 
232
268
  // src/pageObjects/elements/Editors/TextEditor.ts
233
269
  var TextEditor = class extends AbstractDXEditorWithInput {
270
+ constructor(getParentElement, elementBy, driver) {
271
+ super(getParentElement, elementBy, driver);
272
+ this.setClearStrategy(new InputClearStrategy());
273
+ }
234
274
  getValue() {
235
275
  return this.getInputValue();
236
276
  }
@@ -241,6 +281,10 @@ var TextEditor = class extends AbstractDXEditorWithInput {
241
281
 
242
282
  // src/pageObjects/elements/Editors/NumberEditor.ts
243
283
  var NumberEditor = class extends AbstractDXEditorWithInput {
284
+ constructor(getParentElement, elementBy, driver) {
285
+ super(getParentElement, elementBy, driver);
286
+ this.setClearStrategy(new InputClearStrategy());
287
+ }
244
288
  async getValue() {
245
289
  const value = await super.getInputValue();
246
290
  return value == "" ? null : Number.parseFloat(value);
@@ -256,10 +300,10 @@ var CheckBox = class extends AbstractFormElement {
256
300
  const inputEl = await this.getElement();
257
301
  const result = await inputEl.getAttribute("aria-checked");
258
302
  if (result === "true")
259
- return true;
303
+ return 1;
260
304
  if (result === "false")
261
- return false;
262
- return void 0;
305
+ return 0;
306
+ return null;
263
307
  }
264
308
  async setValue(value) {
265
309
  const inputEl = await this.getElement();
@@ -271,7 +315,7 @@ var CheckBox = class extends AbstractFormElement {
271
315
  };
272
316
 
273
317
  // src/pageObjects/elements/Editors/SelectBoxEditor.ts
274
- import { By as By6, Key as Key2 } from "selenium-webdriver";
318
+ import { By as By7, Key as Key2 } from "selenium-webdriver";
275
319
 
276
320
  // src/utils/xpathHelper.ts
277
321
  var XPathHelper = class {
@@ -283,18 +327,52 @@ var XPathHelper = class {
283
327
  }
284
328
  };
285
329
 
330
+ // src/pageObjects/elements/Editors/ClearStrategy/ClearButtonStrategy.ts
331
+ import { By as By6 } from "selenium-webdriver";
332
+ var DX_TEXTEDITOR_EMPTY = "dx-texteditor-empty";
333
+ var DX_CLEAR_BUTTON_AREA = "dx-clear-button-area";
334
+ var ClearButtonStrategy = class {
335
+ editorContainerClass;
336
+ constructor(editorContainerClass) {
337
+ this.editorContainerClass = editorContainerClass;
338
+ }
339
+ async isEmptyEditorField(editor) {
340
+ const parentEl = await editor.getParentElement();
341
+ const editorContainer = await parentEl.findElement(By6.className(this.editorContainerClass));
342
+ const classAttribute = await editorContainer.getAttribute("class");
343
+ return classAttribute.includes(DX_TEXTEDITOR_EMPTY);
344
+ }
345
+ async clear(editor) {
346
+ if (await this.isEmptyEditorField(editor)) {
347
+ throw new Error("This field is already empty");
348
+ }
349
+ const clearBtnSelector = `.${this.editorContainerClass} .${DX_CLEAR_BUTTON_AREA}`;
350
+ const parentEl = await editor.getParentElement();
351
+ const clearBtn = await parentEl.findElement(By6.css(clearBtnSelector));
352
+ await click(clearBtn, editor.driver);
353
+ }
354
+ };
355
+
286
356
  // src/pageObjects/elements/Editors/SelectBoxEditor.ts
287
357
  var SELECT_BOX_OPTION_TEXT_CONTAINER_CLASS = "d5-multi-select-item";
288
358
  var SELECT_BOX_ELEMENT_ROLE = "option";
359
+ var combinedValueXpath = (value) => `${XPathHelper.getRoleXpath(SELECT_BOX_ELEMENT_ROLE)}${XPathHelper.getClassAndValueXpath(
360
+ SELECT_BOX_OPTION_TEXT_CONTAINER_CLASS,
361
+ value
362
+ )}`;
363
+ var DX_SELECTBOX = "dx-selectbox";
289
364
  var SelectBoxEditor = class extends AbstractDXEditorWithInput {
365
+ constructor(getParentElement, elementBy, driver) {
366
+ super(getParentElement, elementBy, driver);
367
+ this.setClearStrategy(new ClearButtonStrategy(DX_SELECTBOX));
368
+ }
290
369
  async setSelectInputValue(value) {
291
370
  const inputEl = await this.getInputElement();
292
371
  await click(inputEl, this.driver);
293
372
  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
373
  await this.driver.wait(async () => {
296
374
  try {
297
- const optElement = await inputEl.findElement(By6.xpath(combinedValueXpath));
375
+ const optElement = await inputEl.findElement(By7.xpath(combinedValueXpath(value)));
298
376
  await click(optElement, this.driver);
299
377
  return true;
300
378
  } catch (e) {
@@ -320,7 +398,7 @@ var SwitchEditor = class extends AbstractFormElement {
320
398
  return 1;
321
399
  if (result === "OFF")
322
400
  return 0;
323
- return void 0;
401
+ return null;
324
402
  }
325
403
  async setValue(value) {
326
404
  if (value !== 0 && value !== 1) {
@@ -338,21 +416,205 @@ var SwitchEditor = class extends AbstractFormElement {
338
416
  }
339
417
  };
340
418
 
419
+ // src/pageObjects/elements/Editors/TagBox.ts
420
+ import { By as By8 } from "selenium-webdriver";
421
+ var TAGS_SELECTOR = ".dx-tag .tag-text";
422
+ var DX_TAG_CONTAINER = "dx-tag-container";
423
+ var DX_TAGBOX = "dx-tagbox";
424
+ var DX_TEXTEDITOR_EMPTY2 = "dx-texteditor-empty";
425
+ var BTN_OK_SELECTOR = ".dx-tagbox-popup-wrapper .dx-popup-bottom .dx-button.dx-popup-done";
426
+ var TagBox = class extends AbstractDXEditorWithInput {
427
+ constructor(getParentElement, elementBy, driver) {
428
+ super(getParentElement, elementBy, driver);
429
+ this.setClearStrategy(new ClearButtonStrategy(DX_TAGBOX));
430
+ }
431
+ async setTagBoxInputValue(values) {
432
+ const lastElement = values[values.length - 1];
433
+ const inputEl = await this.getInputElement();
434
+ await inputEl.click();
435
+ for (const value of values) {
436
+ await this.selectOption(inputEl, value, lastElement);
437
+ }
438
+ await this.confirmSelection();
439
+ }
440
+ async selectOption(inputEl, value, lastElement) {
441
+ await inputEl.sendKeys(value);
442
+ await this.driver.wait(async () => {
443
+ try {
444
+ const optElement = await inputEl.findElement(By8.xpath(combinedValueXpath(value)));
445
+ await click(optElement, this.driver);
446
+ return true;
447
+ } catch (e) {
448
+ return false;
449
+ }
450
+ });
451
+ if (value !== lastElement) {
452
+ await inputEl.clear();
453
+ }
454
+ }
455
+ async confirmSelection() {
456
+ const inputEl = await this.getInputElement();
457
+ const option = await inputEl.findElement(By8.xpath(XPathHelper.getRoleXpath(SELECT_BOX_ELEMENT_ROLE)));
458
+ const btnOk = await this.driver.findElement(By8.css(BTN_OK_SELECTOR));
459
+ await this.driver.wait(async () => {
460
+ return await option.getAttribute("aria-selected") === "true";
461
+ });
462
+ await click(btnOk, this.driver);
463
+ }
464
+ async getTagBoxInputValue() {
465
+ if (await this.isTagBoxEmpty()) {
466
+ return [];
467
+ }
468
+ const parentEl = await this.getParentElement();
469
+ const inputEl = await parentEl.findElement(By8.className(DX_TAG_CONTAINER));
470
+ const tags = await inputEl.findElements(By8.css(TAGS_SELECTOR));
471
+ const values = [];
472
+ for (const tag of tags) {
473
+ const text = await tag.getText();
474
+ values.push(text);
475
+ }
476
+ return values;
477
+ }
478
+ async isTagBoxEmpty() {
479
+ const parentEl = await this.getParentElement();
480
+ const tagboxContainer = await parentEl.findElement(By8.className(DX_TAGBOX));
481
+ const classAttribute = await tagboxContainer.getAttribute("class");
482
+ return classAttribute.includes(DX_TEXTEDITOR_EMPTY2);
483
+ }
484
+ async getValue() {
485
+ const value = await this.getTagBoxInputValue();
486
+ return value.length ? value : null;
487
+ }
488
+ async setValue(values) {
489
+ if (!Array.isArray(values)) {
490
+ throw new Error("The provided value is not an array");
491
+ }
492
+ await this.setTagBoxInputValue(values);
493
+ }
494
+ async remove(itemText) {
495
+ const tagValues = await this.getValue();
496
+ const tagExists = tagValues.includes(itemText);
497
+ if (!tagExists) {
498
+ throw new Error(`The tag with the text: ${itemText} was not found`);
499
+ }
500
+ const parentEl = await this.getParentElement();
501
+ const inputEl = await parentEl.findElement(By8.className(DX_TAG_CONTAINER));
502
+ const clearBtnTestId = byTestId(`clear-button-${itemText}`);
503
+ const clearBtn = await inputEl.findElement(clearBtnTestId);
504
+ await click(clearBtn, this.driver);
505
+ }
506
+ };
507
+
508
+ // src/pageObjects/elements/Editors/ButtonGroup.ts
509
+ import { By as By9 } from "selenium-webdriver";
510
+ var ITEM_CLASS = "dx-buttongroup-item";
511
+ var SELECTED_CLASS = "dx-state-selected";
512
+ var ButtonGroup = class extends AbstractFormElement {
513
+ async getItems() {
514
+ return (await this.getElement()).findElements(By9.className(ITEM_CLASS));
515
+ }
516
+ getDataValue(el) {
517
+ return el.getAttribute("data-value");
518
+ }
519
+ async getValue() {
520
+ const inputEl = await this.getElement();
521
+ const selectedEl = await inputEl.findElement(By9.className(`${ITEM_CLASS} ${SELECTED_CLASS}`));
522
+ return this.getDataValue(selectedEl);
523
+ }
524
+ async setValue(value) {
525
+ const items = await this.getItems();
526
+ let buttonEl = void 0;
527
+ for (const item of items) {
528
+ const text = await this.getDataValue(item);
529
+ if (text == value) {
530
+ buttonEl = item;
531
+ break;
532
+ }
533
+ }
534
+ if (!buttonEl)
535
+ throw new Error(`Cannot find item in button group with text: ${value}`);
536
+ await click(buttonEl, this.driver);
537
+ }
538
+ };
539
+
540
+ // src/pageObjects/elements/Editors/BooleanSelector.ts
541
+ var BooleanSelector = class extends ButtonGroup {
542
+ // @ts-ignore
543
+ async getValue() {
544
+ const value = await super.getValue();
545
+ return value === "null" ? null : value === "1" ? 1 : 0;
546
+ }
547
+ // @ts-ignore
548
+ async setValue(value) {
549
+ const val = value == null ? "null" : value ? "1" : "0";
550
+ await super.setValue(val);
551
+ }
552
+ };
553
+
554
+ // src/pageObjects/elements/Editors/DateEditor.ts
555
+ import { By as By10, Key as Key3 } from "selenium-webdriver";
556
+ var DateEditor = class extends AbstractDXEditorWithInput {
557
+ constructor(getParentElement, elementBy, driver) {
558
+ super(getParentElement, elementBy, driver);
559
+ this.setClearStrategy(new InputClearStrategy());
560
+ }
561
+ async getControlElement() {
562
+ const controlEl = await this.getElement();
563
+ return controlEl.findElement(By10.className("dx-dropdowneditor-input-wrapper"));
564
+ }
565
+ async getDateElement() {
566
+ const controlEl = await this.getControlElement();
567
+ return controlEl.findElement(By10.css("input[type=hidden]"));
568
+ }
569
+ async setDateValue(value) {
570
+ const dateEl = await this.getInputElement();
571
+ const stringDate = value.toJSON().split("T")[0];
572
+ const dateArr = stringDate.split("-");
573
+ const [year, month, day] = dateArr;
574
+ const keys = day + month + year;
575
+ await click(dateEl, this.driver);
576
+ const actions = this.driver.actions({ async: true });
577
+ await actions.move({ origin: dateEl }).sendKeys(Key3.HOME).perform();
578
+ await actions.move({ origin: dateEl }).sendKeys(keys, Key3.ENTER).perform();
579
+ }
580
+ async getDateValue() {
581
+ const inputEl = await this.getDateElement();
582
+ return inputEl.getAttribute("value");
583
+ }
584
+ async getValue() {
585
+ const value = await this.getDateValue();
586
+ return value == "" ? null : new Date(value);
587
+ }
588
+ setValue(value) {
589
+ if (typeof value === "object") {
590
+ return this.setDateValue(value);
591
+ }
592
+ throw new Error(`Value is incorrect. It should be Object`);
593
+ }
594
+ };
595
+
341
596
  // src/pageObjects/elements/Editors/editorFactory.ts
342
597
  var editorFactory = async (getParentElement, elementBy, driver) => {
343
598
  const elementInstance = new AbstractFormElement(getParentElement, elementBy, driver);
344
- const element = await elementInstance.getElement();
345
- const classSting = await element.getAttribute("class");
599
+ const classSting = await elementInstance.getClasses();
346
600
  if (classSting.includes("text-control"))
347
601
  return new TextEditor(getParentElement, elementBy, driver);
348
602
  if (classSting.includes("number-control"))
349
603
  return new NumberEditor(getParentElement, elementBy, driver);
350
604
  if (classSting.includes("check-box"))
351
605
  return new CheckBox(getParentElement, elementBy, driver);
606
+ if (classSting.includes("multi-select-control"))
607
+ return new TagBox(getParentElement, elementBy, driver);
352
608
  if (classSting.includes("select-control"))
353
609
  return new SelectBoxEditor(getParentElement, elementBy, driver);
354
610
  if (classSting.includes("switch-control"))
355
611
  return new SwitchEditor(getParentElement, elementBy, driver);
612
+ if (classSting.includes("date-control"))
613
+ return new DateEditor(getParentElement, elementBy, driver);
614
+ if (classSting.includes("buttons-group"))
615
+ return new BooleanSelector(getParentElement, elementBy, driver);
616
+ if (classSting.includes("button-group-field"))
617
+ return new ButtonGroup(getParentElement, elementBy, driver);
356
618
  throw new Error("Unknown type of editor. Please write ticket to the support");
357
619
  };
358
620
 
@@ -363,6 +625,9 @@ function createDataTestId(formName, itemType, itemName) {
363
625
  function createFieldTestId(formName, itemName) {
364
626
  return createDataTestId(formName, "form_field" /* FORM_FIELD */, itemName);
365
627
  }
628
+ function createGroupTestId(formName, itemName) {
629
+ return createDataTestId(formName, "group" /* GROUP */, itemName);
630
+ }
366
631
  function createTableId(formName) {
367
632
  return `${"table" /* TABLE */}-${formName}`;
368
633
  }
@@ -399,14 +664,82 @@ var FormField = class extends AbstractElementWithParent {
399
664
  await this.initEditor();
400
665
  return this.editor.setValue(value);
401
666
  }
667
+ /**
668
+ * Очищает значение в поле
669
+ * @example
670
+ * await formEdit.field('NumberFld').clear();
671
+ */
672
+ async clear() {
673
+ await this.initEditor();
674
+ if (!this.editor.clear) {
675
+ throw new Error("The clear method is not available for this field type.");
676
+ }
677
+ return this.editor.clear();
678
+ }
679
+ /**
680
+ * Удаляет тег по переданному тексту
681
+ * @example
682
+ * await formEdit.field('TagBox').remove('test');
683
+ */
684
+ async remove(itemText) {
685
+ await this.initEditor();
686
+ if (!this.editor.remove) {
687
+ throw new Error("This method is only available for the TagBox field.");
688
+ }
689
+ return this.editor.remove(itemText);
690
+ }
691
+ };
692
+
693
+ // src/pageObjects/elements/Groups/AbstractGroup.ts
694
+ var AbstractDXGroup = class extends AbstractFormElement {
695
+ };
696
+
697
+ // src/pageObjects/elements/Groups/Panel.ts
698
+ var Panel = class extends AbstractDXGroup {
699
+ async isDisplayed() {
700
+ const groupElement = await this.getElement();
701
+ return await groupElement.isDisplayed();
702
+ }
703
+ };
704
+
705
+ // src/pageObjects/elements/Groups/groupFactory.ts
706
+ var groupFactory = async (getParentElement, elementBy, driver) => {
707
+ const elementInstance = new AbstractFormElement(getParentElement, elementBy, driver);
708
+ const classSting = await elementInstance.getClasses();
709
+ if (classSting.includes("panel-group"))
710
+ return new Panel(getParentElement, elementBy, driver);
711
+ throw new Error("Unknown type of group. Please write ticket to the support");
712
+ };
713
+
714
+ // src/pageObjects/elements/FormGroup.ts
715
+ var FormGroup = class extends AbstractElementWithParent {
716
+ editor;
717
+ constructor(getParentElement, formName, name, driver) {
718
+ super(getParentElement, driver);
719
+ this.elementBy = byTestId(createGroupTestId(formName, name));
720
+ }
721
+ async initEditor() {
722
+ if (!this.editor) {
723
+ this.editor = await groupFactory(this.getParentElement, this.elementBy, this.driver);
724
+ }
725
+ }
726
+ /**
727
+ * Возвращает булевое значение доступна ли группа
728
+ * @example
729
+ * expect(await tableList.group('MainSuperPanel').isDisplayed();
730
+ */
731
+ async isDisplayed() {
732
+ await this.initEditor();
733
+ return this.editor.isDisplayed();
734
+ }
402
735
  };
403
736
 
404
737
  // src/pageObjects/elements/Forms/FormEdit.ts
405
738
  var FormEdit = class extends AbstractForm {
406
- saveButtonBy = By7.id("button-save");
739
+ saveButtonBy = By11.id("button-save");
407
740
  constructor(driver) {
408
741
  super(driver);
409
- this.popupContainerBy = By7.className(`popup form-edit-${this.formName}`);
742
+ this.popupContainerBy = By11.className(`popup form-edit-${this.formName}`);
410
743
  }
411
744
  /**
412
745
  * Находит поле формы редактирования по имени
@@ -418,6 +751,28 @@ var FormEdit = class extends AbstractForm {
418
751
  field(name) {
419
752
  return new FormField(this.getContainerElement, this.formName, name, this.driver);
420
753
  }
754
+ /**
755
+ * Проверяет не отключена ли кнопка Сохранить
756
+ * @example
757
+ * const formEdit = new MyForm();
758
+ * //...
759
+ * await formEdit.isSaveButtonDisabled();
760
+ */
761
+ async isSaveButtonDisabled() {
762
+ const saveButton = await this.driver.findElement(this.saveButtonBy);
763
+ const result = await saveButton.getAttribute("aria-disabled");
764
+ return result === "true";
765
+ }
766
+ /**
767
+ * Находит группу формы редактирования по имени
768
+ * @example
769
+ * const formEdit = new MyForm();
770
+ * //...
771
+ * expect(await formEdit.group('Name');
772
+ */
773
+ group(name) {
774
+ return new FormGroup(this.getContainerElement, this.formName, name, this.driver);
775
+ }
421
776
  /**
422
777
  * Нажатие на кнопку Сохранить
423
778
  * @example
@@ -426,35 +781,36 @@ var FormEdit = class extends AbstractForm {
426
781
  * await formEdit.saveButtonClick();
427
782
  */
428
783
  async saveButtonClick() {
429
- await click(await this.driver.findElement(this.saveButtonBy), this.driver);
784
+ const saveButton = await this.driver.findElement(this.saveButtonBy);
785
+ await this.driver.wait(async () => !await this.isSaveButtonDisabled(), void 0, "Button is disabled");
786
+ await click(saveButton, this.driver);
430
787
  }
431
788
  };
432
789
 
433
790
  // src/pageObjects/elements/Forms/ListForm.ts
434
- import { By as By13 } from "selenium-webdriver";
791
+ import { By as By17 } from "selenium-webdriver";
435
792
 
436
793
  // src/pageObjects/elements/Table/Table.ts
437
- import { By as By10 } from "selenium-webdriver";
794
+ import { By as By14 } from "selenium-webdriver";
438
795
 
439
796
  // src/pageObjects/elements/Table/utils.ts
440
- import { By as By8 } from "selenium-webdriver";
797
+ import { By as By12 } from "selenium-webdriver";
441
798
  var getRowPathByIndex = (index) => {
442
- return By8.css(`tr[aria-rowindex="${transformInputIndex(index)}"]`);
799
+ return By12.css(`tr[aria-rowindex="${transformInputIndex(index)}"]`);
443
800
  };
801
+ var getColumnByIndexCssSelector = (columnIndex) => `td[aria-colindex="${transformInputIndex(columnIndex)}"]`;
444
802
  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}"]`);
803
+ return By12.css(getColumnByIndexCssSelector(index));
449
804
  };
805
+ var getColumnByCaptionCssSelector = (caption) => `td[role="columnheader"][aria-label="Column ${caption}"]`;
450
806
  var getCellPathByIndex = (index) => {
451
- return By8.css(`td[role="gridcell"][aria-colindex="${transformInputIndex(index)}"]`);
807
+ return By12.css(`td[role="gridcell"][aria-colindex="${transformInputIndex(index)}"]`);
452
808
  };
453
809
  var transformInputIndex = (index) => index + 1;
454
810
  var transformOutputIndex = (index) => +index - 1;
455
811
 
456
812
  // src/pageObjects/elements/Table/elements/Row.ts
457
- import { By as By9 } from "selenium-webdriver";
813
+ import { By as By13 } from "selenium-webdriver";
458
814
 
459
815
  // src/pageObjects/elements/Table/elements/Cell.ts
460
816
  var Cell = class extends AbstractElementWithParent {
@@ -476,6 +832,7 @@ var Cell = class extends AbstractElementWithParent {
476
832
  // src/pageObjects/elements/Table/elements/Row.ts
477
833
  var DX_SELECTION_CLASS = "dx-selection";
478
834
  var DX_ROW_EDIT_CLASS = "dx-edit-row";
835
+ var DX_DATA_ROW_CLASS = "dx-data-row";
479
836
  var Row = class extends AbstractElementWithParent {
480
837
  index;
481
838
  getColumnByCaption;
@@ -506,10 +863,8 @@ var Row = class extends AbstractElementWithParent {
506
863
  await click(rowEl, this.driver);
507
864
  await this.driver.wait(async () => {
508
865
  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);
866
+ return classes.includes(DX_SELECTION_CLASS);
867
+ }, 500, "Cannot select row");
513
868
  }
514
869
  async isSelected() {
515
870
  const rowEl = await this.getElement();
@@ -521,7 +876,7 @@ var Row = class extends AbstractElementWithParent {
521
876
  }
522
877
  async getCells() {
523
878
  const rowEl = await this.getElement();
524
- const cells = await rowEl.findElements(By9.css(`td[role="gridcell"]`));
879
+ const cells = await rowEl.findElements(By13.css(`td[role="gridcell"]`));
525
880
  const result = [];
526
881
  cells.forEach((_, index) => {
527
882
  const newCell = new Cell(rowEl, index, this.driver);
@@ -529,26 +884,30 @@ var Row = class extends AbstractElementWithParent {
529
884
  });
530
885
  return result;
531
886
  }
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")
887
+ async clickInlineButton(buttonClass) {
888
+ const FIXED_TABLE_CSS_SELECTOR = ".dx-fixed-columns .dx-datagrid-content.dx-datagrid-content-fixed";
889
+ const buttonCssSelector = `.${DX_DATA_ROW_CLASS}[aria-rowindex="${transformInputIndex(this.index)}"] .${buttonClass}`;
890
+ const button = (await this.getParentElement()).findElement(
891
+ By13.css(`${FIXED_TABLE_CSS_SELECTOR} ${buttonCssSelector}`)
535
892
  );
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`);
893
+ return click(button, this.driver);
894
+ }
895
+ async checkEditingMode(isEditing, msg) {
896
+ await this.driver.wait(async () => {
897
+ return (await this.getClasses()).includes(DX_ROW_EDIT_CLASS) === isEditing;
898
+ }, 2e3, msg);
542
899
  }
543
900
  async edit() {
544
- await this.select();
545
- return this.clickInlineButton(DX_SELECTION_CLASS, "edit");
901
+ await this.clickInlineButton("edit");
902
+ await this.checkEditingMode(true, "Row did not switch to edit mode");
546
903
  }
547
904
  async save() {
548
- return this.clickInlineButton(DX_ROW_EDIT_CLASS, "save");
905
+ await this.clickInlineButton("save");
906
+ await this.checkEditingMode(false, "Row did not saved");
549
907
  }
550
908
  async cancel() {
551
- return this.clickInlineButton(DX_ROW_EDIT_CLASS, "cancel");
909
+ await this.clickInlineButton("cancel");
910
+ await this.checkEditingMode(false, "Row did not switch to normal mode");
552
911
  }
553
912
  };
554
913
 
@@ -574,19 +933,18 @@ var Table = class extends AbstractElementWithParent {
574
933
  formName;
575
934
  constructor(getParentElement, formName, driver) {
576
935
  super(getParentElement, driver);
577
- this.elementBy = By10.id(createTableId(formName));
936
+ this.elementBy = By14.id(createTableId(formName));
578
937
  this.formName = formName;
579
938
  }
580
- async getHeaderElement() {
581
- const parentEl = await this.getElement();
582
- return parentEl.findElement(By10.className("dx-header-row"));
939
+ getHeaderCssSelector() {
940
+ return ".dx-datagrid-headers .dx-datagrid-content:not(.dx-datagrid-content-fixed)";
583
941
  }
584
942
  async getDataGrid() {
585
943
  const parentEl = await this.getElement();
586
- return parentEl.findElement(By10.css(`div[role="grid"]`));
944
+ return parentEl.findElement(By14.css(`div[role="grid"]`));
587
945
  }
588
946
  getTableElement = () => {
589
- return this.driver.findElement(this.elementBy);
947
+ return this.getElement();
590
948
  };
591
949
  async getRowByIndex(index) {
592
950
  const gridContainer = await this.getDataGrid();
@@ -597,16 +955,14 @@ var Table = class extends AbstractElementWithParent {
597
955
  throw new Error(`Cannot find row by index: ${index}`);
598
956
  }
599
957
  async getColumnByIndex(index) {
600
- const headerContainer = await this.getHeaderElement();
601
- const column = await headerContainer.findElement(getColumnPathByIndex(index));
958
+ const column = await (await this.getElement()).findElement(By14.css(`${this.getHeaderCssSelector()} ${getColumnByIndexCssSelector(index)}`));
602
959
  if (column) {
603
960
  return new Column(this.getTableElement, index, this.driver);
604
961
  }
605
962
  throw new Error(`Cannot find column by index: ${index}`);
606
963
  }
607
964
  async getColumnByCaption(caption) {
608
- const headerContainer = await this.getHeaderElement();
609
- const column = await headerContainer.findElement(getColumnPathByCaption(caption));
965
+ const column = await (await this.getElement()).findElement(By14.css(`${this.getHeaderCssSelector()} ${getColumnByCaptionCssSelector(caption)}`));
610
966
  if (column) {
611
967
  const index = await column.getAttribute("aria-colindex");
612
968
  return new Column(this.getTableElement, transformOutputIndex(index), this.driver);
@@ -623,7 +979,7 @@ var Table = class extends AbstractElementWithParent {
623
979
  }
624
980
  async getRows() {
625
981
  const gridContainer = await this.getDataGrid();
626
- const rows = await gridContainer.findElements(By10.className("dx-row dx-data-row"));
982
+ const rows = await gridContainer.findElements(By14.className("dx-row dx-data-row"));
627
983
  const result = [];
628
984
  rows.forEach((_, index) => {
629
985
  const newRow = new Row(this.getTableElement, index, this.driver, this.getColumnByCaption.bind(this));
@@ -632,8 +988,7 @@ var Table = class extends AbstractElementWithParent {
632
988
  return result;
633
989
  }
634
990
  async getColumns() {
635
- const headerContainer = await this.getHeaderElement();
636
- const columns = await headerContainer.findElements(By10.css(`td[role="columnheader"]`));
991
+ const columns = await (await this.getElement()).findElements(By14.css(`${this.getHeaderCssSelector()} td[role="columnheader"]`));
637
992
  const result = [];
638
993
  columns.forEach((_, index) => {
639
994
  const newCol = new Column(this.getTableElement, index, this.driver);
@@ -652,23 +1007,28 @@ var Table = class extends AbstractElementWithParent {
652
1007
  * expect(await form.field('Name').getValue()).toBe('test value');
653
1008
  */
654
1009
  field(name) {
655
- return new FormField(this.getElement, this.formName, name, this.driver);
1010
+ return new FormField(this.getTableElement, this.formName, name, this.driver);
656
1011
  }
657
1012
  };
658
1013
 
659
1014
  // src/pageObjects/elements/TableToolbar/TableToolbar.ts
660
- import { By as By12 } from "selenium-webdriver";
1015
+ import { By as By16 } from "selenium-webdriver";
661
1016
 
662
1017
  // src/pageObjects/elements/TableToolbar/TableToolbarButton.ts
663
- import { By as By11 } from "selenium-webdriver";
1018
+ import { By as By15 } from "selenium-webdriver";
664
1019
  var TOOLBAR_BUTTON_CLASS = "toolbar-button";
665
1020
  var TableToolbarButton = class extends AbstractElementWithParent {
1021
+ name;
666
1022
  constructor(getParentElement, name, driver) {
667
1023
  super(getParentElement, driver);
668
- this.elementBy = By11.id(`${TOOLBAR_BUTTON_CLASS}-${name}`);
1024
+ this.name = name;
1025
+ this.elementBy = By15.id(`${TOOLBAR_BUTTON_CLASS}-${name}`);
669
1026
  }
670
1027
  async click() {
671
1028
  const button = await this.getElement();
1029
+ await this.driver.wait(async () => {
1030
+ return await button.isDisplayed() && !await this.isDisabled();
1031
+ }, 1e3, `Button ${this.name} cannot be clicked. It is not displayed or it is disabled`);
672
1032
  return click(button, this.driver);
673
1033
  }
674
1034
  async isDisabled() {
@@ -683,14 +1043,14 @@ var TABLE_TOOLBAR_CLASS = "d5-table-toolbar";
683
1043
  var TableToolbar = class extends AbstractElementWithParent {
684
1044
  constructor(getParentElement, formName, driver) {
685
1045
  super(getParentElement, driver);
686
- this.elementBy = By12.id(`${TABLE_TOOLBAR_CLASS}-${formName}`);
1046
+ this.elementBy = By16.id(`${TABLE_TOOLBAR_CLASS}-${formName}`);
687
1047
  }
688
1048
  getTableToolbarElement = () => {
689
1049
  return this.driver.findElement(this.elementBy);
690
1050
  };
691
1051
  async button(name) {
692
1052
  const toolbar = await this.getTableToolbarElement();
693
- const button = await toolbar.findElement(By12.id(`${TOOLBAR_BUTTON_CLASS}-${name}`));
1053
+ const button = await toolbar.findElement(By16.id(`${TOOLBAR_BUTTON_CLASS}-${name}`));
694
1054
  if (button) {
695
1055
  return new TableToolbarButton(this.getTableToolbarElement, name, this.driver);
696
1056
  }
@@ -702,7 +1062,7 @@ var TableToolbar = class extends AbstractElementWithParent {
702
1062
  var ListForm = class extends AbstractForm {
703
1063
  constructor(driver) {
704
1064
  super(driver);
705
- this.popupContainerBy = By13.className(`popup form-inline-${this.formName}`);
1065
+ this.popupContainerBy = By17.className(`popup form-inline-${this.formName}`);
706
1066
  }
707
1067
  /**
708
1068
  * Находит таблицу
@@ -714,6 +1074,16 @@ var ListForm = class extends AbstractForm {
714
1074
  table() {
715
1075
  return new Table(this.getContainerElement, this.formName, this.driver);
716
1076
  }
1077
+ /**
1078
+ * Находит группу на лист форме по имени
1079
+ * @example
1080
+ * const formEdit = new MyForm();
1081
+ * //...
1082
+ * expect(await formEdit.group('Name');
1083
+ */
1084
+ group(name) {
1085
+ return new FormGroup(this.getContainerElement, this.formName, name, this.driver);
1086
+ }
717
1087
  /**
718
1088
  * Находит таблицу
719
1089
  * @example
@@ -750,6 +1120,14 @@ var LoginPage = class extends AbstractPage {
750
1120
  usernameField = byTestId("loginField", "input");
751
1121
  passwordField = byTestId("passwordField", "input");
752
1122
  loginButton = byTestId("loginButton");
1123
+ async locate() {
1124
+ await super.locate();
1125
+ const login = await this.driver.findElement(this.usernameField);
1126
+ await waitElementIsVisible({
1127
+ element: login,
1128
+ driver: this.driver
1129
+ });
1130
+ }
753
1131
  get path() {
754
1132
  return LOGIN_PATH;
755
1133
  }
@@ -786,5 +1164,6 @@ export {
786
1164
  createDriver,
787
1165
  editorFactory,
788
1166
  elementIsNotPresent,
789
- signIn
1167
+ signIn,
1168
+ waitElementIsVisible
790
1169
  };