d5-testing-library 1.9.0-alpha.3 → 1.9.0-alpha.33

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.
@@ -17,14 +17,15 @@ import {
17
17
  safeWait,
18
18
  staleElementAction,
19
19
  waitElementIsClickable_default,
20
- waitElementIsVisible_default
21
- } from "./chunk-ROHBQLRX.mjs";
20
+ waitElementIsVisible_default,
21
+ withCustomTimeouts_default
22
+ } from "./chunk-RPSLW2WB.mjs";
22
23
 
23
24
  // src/pageObjects/elements/Table/Table.ts
24
25
  import { By as By6 } from "selenium-webdriver";
25
26
 
26
27
  // src/pageObjects/elements/Table/elements/Row.ts
27
- import { By as By3 } from "selenium-webdriver";
28
+ import { By as By3, Key } from "selenium-webdriver";
28
29
 
29
30
  // src/pageObjects/elements/Table/elements/Cell.ts
30
31
  import { By as By2 } from "selenium-webdriver";
@@ -76,21 +77,30 @@ var Cell = class extends AbstractElementWithParent {
76
77
  async getDateValue(cellEl, cellElClass) {
77
78
  const value = (await cellEl.getText()).trim();
78
79
  if (value === "") return null;
79
- const isWithTime = cellElClass.includes("with-time");
80
- try {
81
- if (isWithTime) {
82
- const [datePart, timePart] = value.split(", ");
83
- const [day, month, year] = datePart.split(".").map(Number);
84
- const [hours, minutes] = timePart.split(":").map(Number);
85
- return new Date(Date.UTC(year, month - 1, day, hours, minutes));
86
- } else {
87
- const [day, month, year] = value.split(".").map(Number);
88
- return new Date(Date.UTC(year, month - 1, day));
80
+ const getDate = (stringDate) => {
81
+ const isWithTime = cellElClass.includes("with-time");
82
+ try {
83
+ if (isWithTime) {
84
+ const [datePart, timePart] = stringDate.split(", ");
85
+ const [day, month, year] = datePart.split(".").map(Number);
86
+ const [hours, minutes] = timePart.split(":").map(Number);
87
+ return new Date(year, month - 1, day, hours, minutes, 0, 0);
88
+ } else {
89
+ const [day, month, year] = stringDate.split(".").map(Number);
90
+ return new Date(Date.UTC(year, month - 1, day));
91
+ }
92
+ } catch (error) {
93
+ console.error(`Error parsing date value "${stringDate}":`, error);
94
+ return null;
89
95
  }
90
- } catch (error) {
91
- console.error(`Error parsing date value "${value}":`, error);
92
- return null;
96
+ };
97
+ const periodValue = value.split(" - ");
98
+ if (periodValue.length > 1) {
99
+ const dateFrom = getDate(periodValue[0]);
100
+ const dateTo = getDate(periodValue[1]);
101
+ return [dateFrom, dateTo];
93
102
  }
103
+ return getDate(value);
94
104
  }
95
105
  async getElement() {
96
106
  if (this._index == null) {
@@ -117,6 +127,7 @@ var Cell = class extends AbstractElementWithParent {
117
127
  var DX_SELECTION_CLASS = "dx-selection";
118
128
  var DX_ROW_EDIT_CLASS = "dx-edit-row";
119
129
  var DX_DATA_ROW_CLASS = "dx-data-row";
130
+ var FIRST_CLICKED_CELL_SELECTOR = 'td[aria-colindex="1"]';
120
131
  var Row = class extends AbstractElementWithParent {
121
132
  index;
122
133
  getColumnBy;
@@ -153,10 +164,20 @@ var Row = class extends AbstractElementWithParent {
153
164
  getCellByTitle(title) {
154
165
  return this._getCell({ title });
155
166
  }
156
- async select() {
167
+ async select(preserve = false) {
157
168
  await staleElementAction(async () => {
158
169
  const rowEl = await this.getElement();
159
- await click(rowEl, this.driver);
170
+ const isSelected = (await this.getClasses()).includes(DX_SELECTION_CLASS);
171
+ const clickedElement = await rowEl.findElement(By3.css(FIRST_CLICKED_CELL_SELECTOR));
172
+ if (isSelected && !preserve) {
173
+ await this.driver.actions({ async: true }).keyUp(Key.CONTROL).perform();
174
+ }
175
+ if (preserve && !isSelected) {
176
+ await this.driver.actions({ async: true }).keyDown(Key.CONTROL).perform();
177
+ await click(clickedElement, this.driver);
178
+ return;
179
+ }
180
+ await click(clickedElement, this.driver);
160
181
  });
161
182
  await safeWait(
162
183
  async () => {
@@ -366,7 +387,7 @@ var DXContextMenuList = class extends AbstractDropDownMenuList {
366
387
  async isVisible() {
367
388
  try {
368
389
  await waitElementIsVisible_default({
369
- element: await this.driver.findElement(By5.css(`.dx-overlay-wrapper .dx-contextmenu`)),
390
+ element: await this.driver.findElement(By5.css(`.dx-overlay-wrapper .dx-context-menu`)),
370
391
  driver: this.driver,
371
392
  errorMessage: "Cannot find dropdown element"
372
393
  });
@@ -467,13 +488,32 @@ var Table = class extends AbstractElementWithParent {
467
488
  /**
468
489
  * Вибрати рядок по індексу.
469
490
  * @param index починається з 0
491
+ * @param preserve boolean який изначає, чи повинні раніше вибрані рядки залишатися вибраними
470
492
  */
471
- selectRowByIndex(index) {
472
- return this.getRowByIndex(index).select();
493
+ selectRowByIndex(index, preserve = false) {
494
+ return this.getRowByIndex(index).select(preserve);
473
495
  }
474
- async getRows() {
496
+ /**
497
+ * Вибрати масив рядків по індексу.
498
+ * @param indexes починається з 0
499
+ */
500
+ async selectRowsByIndexes(indexes) {
501
+ for (const index of indexes) {
502
+ await this.selectRowByIndex(index, true);
503
+ }
504
+ }
505
+ getRowElements(timeout) {
475
506
  const rowsFullCssSelector = `${this.getFullCssSelector()} ${this.tableContentCssSelector} .dx-row.dx-data-row`;
476
- const rows = await this.driver.findElements(By6.css(rowsFullCssSelector));
507
+ return withCustomTimeouts_default({
508
+ driver: this.driver,
509
+ timeout,
510
+ callback: () => {
511
+ return this.driver.findElements(By6.css(rowsFullCssSelector));
512
+ }
513
+ });
514
+ }
515
+ async getRows() {
516
+ const rows = await this.getRowElements();
477
517
  const result = [];
478
518
  rows.forEach((_, index) => {
479
519
  const newRow = new Row(
@@ -559,6 +599,72 @@ var Table = class extends AbstractElementWithParent {
559
599
  await contextMenu(cellElement, this.driver);
560
600
  });
561
601
  }
602
+ /**
603
+ * Отримує значення комірки таблиці за ім'ям комірки та індексом рядка.
604
+ *
605
+ * @param {string} cellName - Ім'я комірки (поля) в таблиці
606
+ * @param {number} rowIndex - Індекс рядка (починається з 0)
607
+ * @throws {Error} Викидає помилку, якщо rowIndex не вказано
608
+ * @returns {Promise<string>} Повертає значення комірки
609
+ *
610
+ * @example
611
+ * const table = new Table(driver);
612
+ *
613
+ * // Отримання значення комірки 'Name' в першому рядку (індекс 0)
614
+ * const cellValue = await table.getCellValueByCellName('Name', 0);
615
+ * expect(cellValue).toBe('Очікуване значення');
616
+ */
617
+ getCellValueByCellName(cellName, rowIndex) {
618
+ if (rowIndex == null) {
619
+ throw new Error("Error: getCellValue(): rowIndex is required");
620
+ }
621
+ const row = this.getRowByIndex(rowIndex);
622
+ return row.getCell(cellName).getValue();
623
+ }
624
+ /**
625
+ * Перевіряє, чи є таблиця порожньою (не містить жодного рядка з даними).
626
+ *
627
+ * @returns {Promise<boolean>} Повертає true, якщо таблиця порожня, false - якщо містить рядки
628
+ *
629
+ * @example
630
+ * const table = new Table(driver);
631
+ *
632
+ * // Перевірка чи порожня таблиця
633
+ * const empty = await table.isEmpty();
634
+ * expect(empty).toBe(true);
635
+ */
636
+ async isEmpty() {
637
+ try {
638
+ await this.waitRowsCount(0);
639
+ return true;
640
+ } catch {
641
+ return false;
642
+ }
643
+ }
644
+ /**
645
+ * Очікує, поки кількість рядків у таблиці не стане рівною заданому значенню.
646
+ * Використовується для синхронізації тестів з динамічним завантаженням даних.
647
+ *
648
+ * @param {number} count - Очікувана кількість рядків у таблиці
649
+ * @returns {Promise<void>} Повертає Promise, який виконується, коли кількість рядків відповідає очікуваній
650
+ * @throws {Error} Викидає помилку, якщо очікування перевищує тайм-аут
651
+ *
652
+ * @example
653
+ * const table = new Table(driver);
654
+ *
655
+ * // Очікування, поки таблиця не стане порожньою
656
+ * await table.waitRowsCount(0);
657
+ *
658
+ * // Очікування, поки в таблиці не з'явиться 5 рядків
659
+ * await table.waitRowsCount(5);
660
+ * console.log('Таблиця містить 5 рядків');
661
+ */
662
+ async waitRowsCount(count) {
663
+ await safeWait(async () => {
664
+ const rows = await this.getRowElements(1e3);
665
+ return rows.length === count;
666
+ }, this.driver, `Rows count not equal: ${count}`);
667
+ }
562
668
  };
563
669
 
564
670
  // src/pageObjects/elements/TableToolbar/ToolbarGroupButton.ts
@@ -570,6 +676,7 @@ var TOOLBAR_BUTTON_CLASS = "toolbar-button";
570
676
  var TOOLBAR_BUTTON_TEXT_CLASS = "dx-button-text";
571
677
  var getToolbarButtonSelector = (name) => `#${TOOLBAR_BUTTON_CLASS}-${name}`;
572
678
  var getToolbarButtonTextSelector = (name) => `${getToolbarButtonSelector(name)} .${TOOLBAR_BUTTON_TEXT_CLASS}`;
679
+ var getToolbarButtonTextByTestIdSelector = (testId) => `${byTestIdCssSelector(testId)} .${TOOLBAR_BUTTON_TEXT_CLASS}`;
573
680
  var ToolbarButton = class extends AbstractButton {
574
681
  name;
575
682
  constructor(parentCssSelector, name, driver) {
@@ -601,7 +708,8 @@ var ToolbarGroupButton = class extends AbstractButton {
601
708
  }
602
709
  async getTitle() {
603
710
  try {
604
- const buttonTextContainer = await this.driver.findElement(By8.css(getToolbarButtonTextSelector(this.name)));
711
+ const buttonTestId = await this.getDataTestId();
712
+ const buttonTextContainer = await this.driver.findElement(By8.css(getToolbarButtonTextByTestIdSelector(buttonTestId)));
605
713
  return await buttonTextContainer.getText();
606
714
  } catch (_) {
607
715
  return null;
@@ -776,6 +884,14 @@ var ToolbarItem = class extends AbstractElementWithParent {
776
884
  return false;
777
885
  }
778
886
  }
887
+ async isPressed() {
888
+ try {
889
+ await this.init();
890
+ return this._button.isPressed();
891
+ } catch (error) {
892
+ return false;
893
+ }
894
+ }
779
895
  async getTitle() {
780
896
  await this.init();
781
897
  return await this._button.getTitle();
@@ -983,6 +1099,12 @@ var FormFilterField = class extends AbstractEditor {
983
1099
  const filterOperation = await this.initOperation();
984
1100
  return await filterOperation.getIsBlank();
985
1101
  }
1102
+ async isReadonly() {
1103
+ return super.isReadonly();
1104
+ }
1105
+ async isDisabled() {
1106
+ return super.isDisabled();
1107
+ }
986
1108
  };
987
1109
 
988
1110
  // src/pageObjects/elements/Filters/FilterPanel.ts
@@ -1084,6 +1206,12 @@ var PanelFilterField = class extends AbstractEditor {
1084
1206
  const filterOperation = await this.initOperation();
1085
1207
  return await filterOperation.getIsBlank();
1086
1208
  }
1209
+ async isReadonly() {
1210
+ return super.isReadonly();
1211
+ }
1212
+ async isDisabled() {
1213
+ return super.isDisabled();
1214
+ }
1087
1215
  };
1088
1216
 
1089
1217
  // src/pageObjects/elements/Filters/FilterPanel.ts
@@ -1156,12 +1284,18 @@ var HeaderFilterField = class extends AbstractEditor {
1156
1284
  async remove(itemText) {
1157
1285
  return super.remove(itemText);
1158
1286
  }
1287
+ async isReadonly() {
1288
+ return super.isReadonly();
1289
+ }
1290
+ async isDisabled() {
1291
+ return super.isDisabled();
1292
+ }
1159
1293
  };
1160
1294
 
1161
1295
  // src/pageObjects/elements/Filters/ColumnFilter.ts
1162
1296
  var APPLY_SELECTOR = ".apply-button";
1163
1297
  var CANCEL_SELECTOR = ".cancel-button";
1164
- var HEADER_FILTER_ICON = ".dx-column-indicators .dx-header-filter";
1298
+ var HEADER_FILTER_ICON = ".indicators-container .filter-icon";
1165
1299
  var HEADER_FILTER_CONTAINER = ".header-filter";
1166
1300
  var HEADER_FILTER_BUTTONS = ".header-filter-buttons";
1167
1301
  var ColumnFilter = class extends AbstractElement {
@@ -1195,24 +1329,15 @@ var ColumnFilter = class extends AbstractElement {
1195
1329
  const columnEl = await this._column.getElement();
1196
1330
  const actions = this.driver.actions({ async: true });
1197
1331
  await actions.move({ origin: columnEl }).perform();
1198
- const elements = await columnEl.findElements(By11.css(HEADER_FILTER_ICON));
1199
- let headerFilterIcon = null;
1332
+ const headerFilterIconBy = By11.css(HEADER_FILTER_ICON);
1333
+ let headerFilterIcon = await columnEl.findElement(headerFilterIconBy);
1200
1334
  await safeWait(
1201
- async () => {
1202
- let result = false;
1203
- for (let element of elements) {
1204
- if (await element.isDisplayed()) {
1205
- headerFilterIcon = element;
1206
- result = true;
1207
- break;
1208
- }
1209
- }
1210
- return result;
1335
+ () => {
1336
+ return headerFilterIcon.isDisplayed();
1211
1337
  },
1212
1338
  this.driver,
1213
1339
  `HeaderFilter "${this._name}" is not visible`
1214
1340
  );
1215
- if (!headerFilterIcon) throw new Error(`HeaderFilter "${this._name}" is not visible`);
1216
1341
  await click(headerFilterIcon, this.driver);
1217
1342
  const filter = new HeaderFilterField("", this._formName, this._name, this.driver);
1218
1343
  const filterEl = await filter.getElement();
@@ -1332,6 +1457,18 @@ var ColumnFilter = class extends AbstractElement {
1332
1457
  const filterOperation = await this.initOperation();
1333
1458
  return await filterOperation.getIsBlank();
1334
1459
  }
1460
+ async isReadonly() {
1461
+ const filter = await this.initFilter();
1462
+ const result = await filter.isReadonly();
1463
+ await this.cancel();
1464
+ return result;
1465
+ }
1466
+ async isDisabled() {
1467
+ const filter = await this.initFilter();
1468
+ const result = await filter.isDisabled();
1469
+ await this.cancel();
1470
+ return result;
1471
+ }
1335
1472
  };
1336
1473
 
1337
1474
  // src/pageObjects/elements/Forms/ListForm.ts