d5-testing-library 1.8.0-alpha.8 → 1.8.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.
@@ -8,18 +8,20 @@ import {
8
8
  byTestId,
9
9
  byTestIdCssSelector,
10
10
  click,
11
+ contextMenu,
11
12
  createDocFilterTestId,
12
13
  createHeaderFilterTestId,
13
14
  createLayoutFilterTestId,
14
15
  createTableId,
15
16
  fixElementStaleError,
16
- wait,
17
- waitElementIsClickable,
18
- waitElementIsVisible
19
- } from "./chunk-K6JEWF3Y.mjs";
17
+ safeWait,
18
+ staleElementAction,
19
+ waitElementIsClickable_default,
20
+ waitElementIsVisible_default
21
+ } from "./chunk-32KSAOFJ.mjs";
20
22
 
21
23
  // src/pageObjects/elements/Table/Table.ts
22
- import { By as By5 } from "selenium-webdriver";
24
+ import { By as By6 } from "selenium-webdriver";
23
25
 
24
26
  // src/pageObjects/elements/Table/elements/Row.ts
25
27
  import { By as By3 } from "selenium-webdriver";
@@ -45,7 +47,7 @@ var transformOutputIndex = (index) => +index - 1;
45
47
  var Cell = class extends AbstractElementWithParent {
46
48
  _index;
47
49
  getCellIndex;
48
- constructor(parentCssSelector, getCellIndex, index, driver) {
50
+ constructor(parentCssSelector, getCellIndex, driver, index) {
49
51
  super(parentCssSelector, getCssCellByIndexSelector(index), driver);
50
52
  this.getCellIndex = getCellIndex;
51
53
  this._index = index;
@@ -65,7 +67,30 @@ var Cell = class extends AbstractElementWithParent {
65
67
  for (const tag of tags) {
66
68
  values.push(await tag.getAttribute("textContent"));
67
69
  }
68
- return values;
70
+ return values.length ? values : null;
71
+ }
72
+ async getNumberValue(cellEl) {
73
+ const value = (await cellEl.getText()).trim();
74
+ return value == "" ? null : Number.parseFloat(value);
75
+ }
76
+ async getDateValue(cellEl, cellElClass) {
77
+ const value = (await cellEl.getText()).trim();
78
+ 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));
89
+ }
90
+ } catch (error) {
91
+ console.error(`Error parsing date value "${value}":`, error);
92
+ return null;
93
+ }
69
94
  }
70
95
  async getElement() {
71
96
  if (this._index == null) {
@@ -76,11 +101,12 @@ var Cell = class extends AbstractElementWithParent {
76
101
  async getValue() {
77
102
  const cellEl = await this.getElement();
78
103
  const cellElClass = await cellEl.getAttribute("class");
79
- if (cellElClass.includes("boolean-column" /* Boolean */))
80
- return this.getBooleanValue(cellEl);
81
- if (cellElClass.includes("tags-column" /* Tags */))
82
- return this.getTagsValue(cellEl);
83
- return cellEl.getText();
104
+ if (cellElClass.includes("boolean-column" /* Boolean */)) return this.getBooleanValue(cellEl);
105
+ if (cellElClass.includes("tags-column" /* Tags */)) return this.getTagsValue(cellEl);
106
+ if (cellElClass.includes("number-column" /* Number */)) return this.getNumberValue(cellEl);
107
+ if (cellElClass.includes("date-column" /* Date */)) return this.getDateValue(cellEl, cellElClass);
108
+ const cellText = await cellEl.getText();
109
+ return cellText == "" ? null : cellText;
84
110
  }
85
111
  getIndex() {
86
112
  return this._index;
@@ -95,6 +121,13 @@ var Row = class extends AbstractElementWithParent {
95
121
  index;
96
122
  getColumnBy;
97
123
  fixedTableContentCssSelector;
124
+ /**
125
+ * @param parentCssSelector
126
+ * @param fixedTableContentCssSelector
127
+ * @param index починається з 0 (в getCssRowByIndexSelector додається +1)
128
+ * @param driver
129
+ * @param getColumnBy
130
+ */
98
131
  constructor(parentCssSelector, fixedTableContentCssSelector, index, driver, getColumnBy) {
99
132
  super(parentCssSelector, getCssRowByIndexSelector(index), driver);
100
133
  this.index = index;
@@ -107,8 +140,8 @@ var Row = class extends AbstractElementWithParent {
107
140
  () => {
108
141
  return this.getColumnBy({ title, name }).getIndex();
109
142
  },
110
- index,
111
- this.driver
143
+ this.driver,
144
+ index
112
145
  );
113
146
  }
114
147
  getCellByIndex(index) {
@@ -121,9 +154,11 @@ var Row = class extends AbstractElementWithParent {
121
154
  return this._getCell({ title });
122
155
  }
123
156
  async select() {
124
- const rowEl = await this.getElement();
125
- await click(rowEl, this.driver);
126
- await wait(
157
+ await staleElementAction(async () => {
158
+ const rowEl = await this.getElement();
159
+ await click(rowEl, this.driver);
160
+ });
161
+ await safeWait(
127
162
  async () => {
128
163
  const classes = await this.getClasses();
129
164
  return classes.includes(DX_SELECTION_CLASS);
@@ -152,14 +187,14 @@ var Row = class extends AbstractElementWithParent {
152
187
  const buttonCssSelector = `.${DX_DATA_ROW_CLASS}[aria-rowindex="${transformInputIndex(
153
188
  this.index
154
189
  )}"] .${buttonClass}`;
155
- const button = await fixElementStaleError(
156
- By3.css(`${this.parentCssSelector} ${this.fixedTableContentCssSelector} ${buttonCssSelector}`),
157
- this.driver
158
- );
159
- return click(button, this.driver);
190
+ const by = By3.css(`${this.parentCssSelector} ${this.fixedTableContentCssSelector} ${buttonCssSelector}`);
191
+ await staleElementAction(async () => {
192
+ const button = await fixElementStaleError(by, this.driver);
193
+ await click(button, this.driver);
194
+ });
160
195
  }
161
196
  async checkEditingMode(isEditing, msg) {
162
- await wait(
197
+ await safeWait(
163
198
  async () => {
164
199
  return (await this.getClasses()).includes(DX_ROW_EDIT_CLASS) === isEditing;
165
200
  },
@@ -236,16 +271,158 @@ var Column = class extends AbstractElementWithParent {
236
271
  }
237
272
  };
238
273
 
274
+ // src/utils/typesUtil.ts
275
+ import { isObject } from "selenium-webdriver/lib/util";
276
+ var types = {
277
+ "[object Array]": "array",
278
+ "[object Date]": "date",
279
+ "[object Object]": "object",
280
+ "[object String]": "string",
281
+ "[object Null]": "null"
282
+ };
283
+ var type = function(object) {
284
+ const typeOfObject = Object.prototype.toString.call(object);
285
+ return typeof object === "object" ? types[typeOfObject] || "object" : typeof object;
286
+ };
287
+ var isArray = function(value) {
288
+ return type(value) === "array";
289
+ };
290
+ var isString = function(object) {
291
+ return typeof object === "string";
292
+ };
293
+
294
+ // src/pageObjects/elements/AbstractDropDownMenuList.ts
295
+ var AbstractDropDownMenuList = class extends AbstractElementWithParent {
296
+ buttonNotDisplayedError(name) {
297
+ return `Menu button ${name} cannot be clicked. It is not displayed or it is disabled`;
298
+ }
299
+ async clickItemBy(errorMessage, by) {
300
+ await waitElementIsClickable_default({
301
+ errorMessage,
302
+ driver: this.driver,
303
+ by
304
+ });
305
+ const menuItemElement = await fixElementStaleError(by, this.driver);
306
+ await click(menuItemElement, this.driver);
307
+ }
308
+ async getElementByPath(path, getBy, returnLastElement = false) {
309
+ let pathList = isString(path) ? [path] : path;
310
+ let result = null;
311
+ let index = 1;
312
+ for (let text of pathList) {
313
+ if (!returnLastElement || index < pathList.length) {
314
+ await this.clickItemBy(this.buttonNotDisplayedError(text), getBy(text));
315
+ } else {
316
+ result = await fixElementStaleError(getBy(text), this.driver);
317
+ }
318
+ index++;
319
+ }
320
+ return result;
321
+ }
322
+ async clickButton(name) {
323
+ await this.getElementByPath(name, this.getButtonNameBy.bind(this));
324
+ }
325
+ async clickButtonByTitle(title) {
326
+ await this.getElementByPath(title, this.getButtonTitleBy.bind(this));
327
+ }
328
+ getMenuItemElementByTitle(title) {
329
+ return this.getElementByPath(title, this.getButtonTitleBy.bind(this), true);
330
+ }
331
+ getMenuItemElementByName(name) {
332
+ return this.getElementByPath(name, this.getButtonNameBy.bind(this), true);
333
+ }
334
+ isVisible() {
335
+ return Promise.resolve(true);
336
+ }
337
+ };
338
+
339
+ // src/pageObjects/elements/DXContextMenuList.ts
340
+ import { By as By5 } from "selenium-webdriver";
341
+
342
+ // src/utils/cssToXPath.ts
343
+ function cssClassesToXPath(classNames) {
344
+ const classNamesList = classNames.split(".").filter((className) => !!className);
345
+ const chained = classNamesList.map((className) => `contains(@class, "${className}")`).join(" and ");
346
+ return `[${chained}]`;
347
+ }
348
+
349
+ // src/pageObjects/elements/DXContextMenuList.ts
350
+ var CONTEXT_MENU_CLASS = "dx-context-menu";
351
+ var CONTEXT_MENU_ITEM_TEXT_CLASS = "dx-menu-item-text";
352
+ var DXContextMenuList = class extends AbstractDropDownMenuList {
353
+ /**
354
+ * @param parentCssSelector
355
+ * @param classNames - Список класів. Починається з крапки
356
+ * @param driver
357
+ */
358
+ constructor(parentCssSelector, classNames, driver) {
359
+ super(parentCssSelector, `.${CONTEXT_MENU_CLASS}${classNames}`, driver);
360
+ }
361
+ getButtonTitleBy(title) {
362
+ const buttonByTextXpath = (text) => `//span[contains(@class, "${CONTEXT_MENU_ITEM_TEXT_CLASS}") and contains(text(), "${text}")]`;
363
+ const xpath = "//div" + cssClassesToXPath(this.elementCssSelector) + buttonByTextXpath(title);
364
+ return By5.xpath(xpath);
365
+ }
366
+ async isVisible() {
367
+ try {
368
+ await waitElementIsVisible_default({
369
+ element: await this.driver.findElement(By5.css(`.dx-overlay-wrapper .dx-contextmenu`)),
370
+ driver: this.driver,
371
+ errorMessage: "Cannot find dropdown element"
372
+ });
373
+ } catch {
374
+ return false;
375
+ }
376
+ }
377
+ };
378
+
379
+ // src/pageObjects/elements/Table/elements/ContextMenu.ts
380
+ var ContextMenu = class extends DXContextMenuList {
381
+ _formName;
382
+ _initFn;
383
+ constructor(cssClass, formName, driver) {
384
+ super("", cssClass, driver);
385
+ this._formName = formName;
386
+ }
387
+ setInit(cb) {
388
+ this._initFn = cb;
389
+ return this;
390
+ }
391
+ getButtonNameBy(name) {
392
+ return byTestId(`cm-${this._formName}-${name}`);
393
+ }
394
+ async clickButtonByTitle(title) {
395
+ await this._initFn?.();
396
+ await super.clickButtonByTitle(title);
397
+ }
398
+ async clickButton(name) {
399
+ await this._initFn?.();
400
+ await super.clickButton(name);
401
+ }
402
+ };
403
+
239
404
  // src/pageObjects/elements/Table/Table.ts
240
405
  var Table = class extends AbstractElementWithParent {
241
406
  formName;
242
407
  headerCssSelector;
408
+ contextMenuCssSelector;
243
409
  fixedTableContentCssSelector;
244
- constructor(parentCssSelector, headerCssSelector, fixedTableContentCssSelector, formName, driver) {
410
+ tableContentCssSelector;
411
+ constructor({
412
+ parentCssSelector,
413
+ contextMenuCssSelector,
414
+ tableContentCssSelector,
415
+ headerCssSelector,
416
+ fixedTableContentCssSelector,
417
+ formName,
418
+ driver
419
+ }) {
245
420
  super(parentCssSelector, `#${createTableId(formName)}`, driver);
246
421
  this.formName = formName;
247
422
  this.headerCssSelector = headerCssSelector;
423
+ this.contextMenuCssSelector = contextMenuCssSelector;
248
424
  this.fixedTableContentCssSelector = fixedTableContentCssSelector;
425
+ this.tableContentCssSelector = tableContentCssSelector;
249
426
  }
250
427
  getColumnBy = (args) => {
251
428
  if (args.title) {
@@ -253,10 +430,18 @@ var Table = class extends AbstractElementWithParent {
253
430
  }
254
431
  return this.getColumnByName(args.name);
255
432
  };
433
+ /**
434
+ * Отримати рядок по індексу.
435
+ * @param index починається з 0
436
+ */
256
437
  getRowByIndex(index) {
257
438
  return new Row(this.getFullCssSelector(), this.fixedTableContentCssSelector, index, this.driver, this.getColumnBy);
258
439
  }
440
+ /**
441
+ * @param index починається з 0
442
+ */
259
443
  getColumnByIndex(index) {
444
+ this.getRowByIndex(index);
260
445
  return new Column({
261
446
  parentCssSelector: this.getFullCssSelector(),
262
447
  driver: this.driver,
@@ -279,11 +464,16 @@ var Table = class extends AbstractElementWithParent {
279
464
  driver: this.driver
280
465
  });
281
466
  };
467
+ /**
468
+ * Вибрати рядок по індексу.
469
+ * @param index починається з 0
470
+ */
282
471
  selectRowByIndex(index) {
283
472
  return this.getRowByIndex(index).select();
284
473
  }
285
474
  async getRows() {
286
- const rows = await this.driver.findElements(By5.css(`${this.getFullCssSelector()} .dx-row.dx-data-row`));
475
+ const rowsFullCssSelector = `${this.getFullCssSelector()} ${this.tableContentCssSelector} .dx-row.dx-data-row`;
476
+ const rows = await this.driver.findElements(By6.css(rowsFullCssSelector));
287
477
  const result = [];
288
478
  rows.forEach((_, index) => {
289
479
  const newRow = new Row(
@@ -299,7 +489,7 @@ var Table = class extends AbstractElementWithParent {
299
489
  }
300
490
  async getColumns() {
301
491
  const columns = await this.driver.findElements(
302
- By5.css(`${this.getFullCssSelector()} ${this.headerCssSelector} td[role="columnheader"]`)
492
+ By6.css(`${this.getFullCssSelector()} ${this.headerCssSelector} td[role="columnheader"] .header-template-text`)
303
493
  );
304
494
  const result = [];
305
495
  columns.forEach((_, index) => {
@@ -325,102 +515,227 @@ var Table = class extends AbstractElementWithParent {
325
515
  field(name) {
326
516
  return new FormField(this.getFullCssSelector(), this.formName, name, this.driver);
327
517
  }
518
+ /**
519
+ * Возвращает список строк которые выделены
520
+ * @returns Row[]
521
+ * @example
522
+ * const form = new MyForm();
523
+ * //...
524
+ * await form.getSelectedRows();
525
+ */
526
+ async getSelectedRows() {
527
+ const rows = await this.getRows();
528
+ let result = [];
529
+ for (const row of rows) {
530
+ if (await row.isSelected()) {
531
+ }
532
+ result.push(row);
533
+ }
534
+ return result;
535
+ }
536
+ /**
537
+ * Вызывает контекстное меню в области с данными.
538
+ * @param fieldName - имя поля
539
+ * @param [rowIndex] - индекс строки. Если значение не передано, то по умолчанию контекстное меню будет вызвано на первой выделенной строке.
540
+ * @throws {Error} Если нет выделенных строк, то выбросит исключение.
541
+ * @example
542
+ * const form = new MyForm();
543
+ * //...
544
+ * await form.contextMenu('FieldName').clickButton('OpenFormButton');
545
+ * // or
546
+ * await form.contextMenu('FieldName', 3).clickButton(['Group', 'ButtonInGroup']);
547
+ */
548
+ contextMenu(fieldName, rowIndex) {
549
+ return new ContextMenu(this.contextMenuCssSelector, this.formName, this.driver).setInit(async () => {
550
+ let row;
551
+ if (rowIndex != null) {
552
+ row = this.getRowByIndex(rowIndex);
553
+ } else {
554
+ [row] = await this.getSelectedRows();
555
+ }
556
+ if (!row)
557
+ throw new Error("No row available row for context menu");
558
+ const cellElement = await row.getCell(fieldName).getElement();
559
+ await contextMenu(cellElement, this.driver);
560
+ });
561
+ }
328
562
  };
329
563
 
330
564
  // src/pageObjects/elements/TableToolbar/ToolbarGroupButton.ts
331
- var ToolbarGroupButton = class extends AbstractButton {
565
+ import { By as By8 } from "selenium-webdriver";
566
+
567
+ // src/pageObjects/elements/TableToolbar/ToolbarButton.ts
568
+ import { By as By7 } from "selenium-webdriver";
569
+ var TOOLBAR_BUTTON_CLASS = "toolbar-button";
570
+ var TOOLBAR_BUTTON_TEXT_CLASS = "dx-button-text";
571
+ var getToolbarButtonSelector = (name) => `#${TOOLBAR_BUTTON_CLASS}-${name}`;
572
+ var getToolbarButtonTextSelector = (name) => `${getToolbarButtonSelector(name)} .${TOOLBAR_BUTTON_TEXT_CLASS}`;
573
+ var ToolbarButton = class extends AbstractButton {
332
574
  name;
333
575
  constructor(parentCssSelector, name, driver) {
334
- super(parentCssSelector, ``, driver);
576
+ super(parentCssSelector, getToolbarButtonSelector(name), driver);
335
577
  this.name = name;
336
578
  }
337
579
  buttonNotDisplayedError() {
338
580
  return `Button ${this.name} cannot be clicked. It is not displayed or it is disabled`;
339
581
  }
582
+ async getTitle() {
583
+ try {
584
+ const buttonTextContainer = await this.driver.findElement(By7.css(getToolbarButtonTextSelector(this.name)));
585
+ return await buttonTextContainer.getText();
586
+ } catch (_) {
587
+ return null;
588
+ }
589
+ }
340
590
  };
341
591
 
342
- // src/pageObjects/elements/TableToolbar/ToolbarButton.ts
343
- var TOOLBAR_BUTTON_CLASS = "toolbar-button";
344
- var ToolbarButton = class extends AbstractButton {
592
+ // src/pageObjects/elements/TableToolbar/ToolbarGroupButton.ts
593
+ var ToolbarGroupButton = class extends AbstractButton {
345
594
  name;
346
595
  constructor(parentCssSelector, name, driver) {
347
- super(parentCssSelector, `#${TOOLBAR_BUTTON_CLASS}-${name}`, driver);
596
+ super(parentCssSelector, ``, driver);
348
597
  this.name = name;
349
598
  }
350
599
  buttonNotDisplayedError() {
351
600
  return `Button ${this.name} cannot be clicked. It is not displayed or it is disabled`;
352
601
  }
602
+ async getTitle() {
603
+ try {
604
+ const buttonTextContainer = await this.driver.findElement(By8.css(getToolbarButtonTextSelector(this.name)));
605
+ return await buttonTextContainer.getText();
606
+ } catch (_) {
607
+ return null;
608
+ }
609
+ }
353
610
  };
354
611
 
355
- // src/pageObjects/elements/TableToolbar/ToolbarMenu.ts
356
- import { By as By6 } from "selenium-webdriver";
357
-
358
- // src/utils/typesUtil.ts
359
- var types = {
360
- "[object Array]": "array",
361
- "[object Date]": "date",
362
- "[object Object]": "object",
363
- "[object String]": "string",
364
- "[object Null]": "null"
365
- };
366
- var type = function(object) {
367
- const typeOfObject = Object.prototype.toString.call(object);
368
- return typeof object === "object" ? types[typeOfObject] || "object" : typeof object;
369
- };
370
- var isArray = function(value) {
371
- return type(value) === "array";
612
+ // src/pageObjects/elements/AbstractDropDownMenuButton.ts
613
+ var AbstractDropDownMenuButton = class extends AbstractElementWithParent {
614
+ _list;
615
+ constructor(parentCssSelector, elementCssSelector, list, driver) {
616
+ super(parentCssSelector, elementCssSelector, driver);
617
+ this._list = list;
618
+ }
619
+ async openDropdownMenu() {
620
+ const dropdown = await this.getElement();
621
+ await click(dropdown, this.driver);
622
+ await this._list.isVisible();
623
+ }
624
+ async clickButton(name) {
625
+ await this.openDropdownMenu();
626
+ await this._list.clickButton(name);
627
+ }
628
+ async clickButtonByTitle(title) {
629
+ await this.openDropdownMenu();
630
+ await this._list.clickButtonByTitle(title);
631
+ }
632
+ async getMenuItemElementByTitle(title) {
633
+ await this.openDropdownMenu();
634
+ return await this._list.getMenuItemElementByTitle(title);
635
+ }
636
+ async getMenuItemElementByName(name) {
637
+ await this.openDropdownMenu();
638
+ return await this._list.getMenuItemElementByName(name);
639
+ }
372
640
  };
373
- var isString = function(object) {
374
- return typeof object === "string";
641
+
642
+ // src/pageObjects/elements/TableToolbar/ToolbarMenuItem.ts
643
+ var ToolbarMenuItem = class {
644
+ getMenuItem;
645
+ name;
646
+ constructor(getMenuItem, name) {
647
+ this.getMenuItem = getMenuItem;
648
+ this.name = name;
649
+ }
650
+ async click() {
651
+ const menuItemElement = await this.getMenuItem(this.name);
652
+ await menuItemElement.click();
653
+ }
654
+ async getTitle() {
655
+ try {
656
+ const menuItemElement = await this.getMenuItem(this.name);
657
+ return await menuItemElement.getText();
658
+ } catch (_) {
659
+ return null;
660
+ }
661
+ }
662
+ async isDisabled() {
663
+ const menuItemElement = await this.getMenuItem(this.name);
664
+ return await menuItemElement.getCssValue("pointer-events") === "none";
665
+ }
666
+ async isDisplayed() {
667
+ try {
668
+ const menuItemElement = await this.getMenuItem(this.name);
669
+ return await menuItemElement.isDisplayed();
670
+ } catch (_) {
671
+ return false;
672
+ }
673
+ }
375
674
  };
376
675
 
377
676
  // src/pageObjects/elements/TableToolbar/ToolbarMenu.ts
378
677
  var SPINDOWN_CLASS = ".dx-icon-spindown";
379
678
  var buttonGroupItemSelector = (formName, name) => byTestIdCssSelector(`ftb-${formName}-${name}`);
380
679
  var getFullCssSelector = (formName, name) => `${buttonGroupItemSelector(formName, name)} ${SPINDOWN_CLASS}`;
381
- var buttonByTextXpath = (text) => `.//span[contains(@class, "dx-menu-item-text") and contains(text(), "${text}")]`;
382
- var ToolbarMenu = class extends AbstractElementWithParent {
680
+ var ToolbarMenuList = class extends DXContextMenuList {
383
681
  _formName;
384
- constructor(parentCssSelector, formName, name, driver) {
385
- super(parentCssSelector, getFullCssSelector(formName, name), driver);
682
+ constructor(parentCssSelector, elementCssSelector, formName, driver) {
683
+ super(parentCssSelector, elementCssSelector, driver);
386
684
  this._formName = formName;
387
685
  }
388
- async openDropdownMenu() {
389
- const dropdown = await this.getElement();
390
- await click(dropdown, this.driver);
391
- }
392
- getCssSelectorToButton(name) {
686
+ getButtonNameBy(name) {
393
687
  return byTestId(`ftb-${this._formName}-${name}`);
394
688
  }
395
- buttonNotDisplayedError(name) {
396
- return `Toolbar menu button ${name} cannot be clicked. It is not displayed or it is disabled`;
397
- }
398
- async goToButton(name, by) {
399
- const toolbarMenuItem = await fixElementStaleError(by, this.driver);
400
- await waitElementIsClickable({
401
- element: toolbarMenuItem,
402
- errorMessage: this.buttonNotDisplayedError(name),
403
- driver: this.driver
404
- });
405
- await click(toolbarMenuItem, this.driver);
689
+ };
690
+ var ToolbarMenu = class extends AbstractDropDownMenuButton {
691
+ constructor(parentCssSelector, formName, name, driver) {
692
+ const fullSelector = getFullCssSelector(formName, name);
693
+ const list = new ToolbarMenuList(parentCssSelector, ".toolbar-dropdown-context-menu", formName, driver);
694
+ super(parentCssSelector, fullSelector, list, driver);
406
695
  }
407
- async clickButton(name) {
408
- await this.openDropdownMenu();
696
+ checkNameValue(name) {
409
697
  if (!isString(name) && !isArray(name))
410
698
  throw new Error(`"name" must be a string or array of string`);
411
- let lName = isString(name) ? [name] : name;
412
- for (let n of lName) {
413
- await this.goToButton(n, this.getCssSelectorToButton(n));
414
- }
415
699
  }
416
- async clickButtonByTitle(title) {
417
- await this.openDropdownMenu();
700
+ checkTitleValue(title) {
418
701
  if (!isString(title) && !isArray(title))
419
702
  throw new Error(`"title" must be a string or array of string`);
420
- let lTitle = isString(title) ? [title] : title;
421
- for (let t of lTitle) {
422
- await this.goToButton(t, By6.xpath(buttonByTextXpath(t)));
423
- }
703
+ }
704
+ buttonByName(name) {
705
+ this.checkNameValue(name);
706
+ return new ToolbarMenuItem(this.getMenuItemElementByName.bind(this), name);
707
+ }
708
+ buttonByTitle(title) {
709
+ this.checkTitleValue(title);
710
+ return new ToolbarMenuItem(this.getMenuItemElementByTitle.bind(this), title);
711
+ }
712
+ async buttonIsDisplayed(name) {
713
+ const button = this.buttonByName(name);
714
+ return await button.isDisplayed();
715
+ }
716
+ async buttonByTitleIsDisplayed(title) {
717
+ const button = this.buttonByTitle(title);
718
+ return await button.isDisplayed();
719
+ }
720
+ async buttonTitle(name) {
721
+ const button = this.buttonByName(name);
722
+ return await button.getTitle();
723
+ }
724
+ async buttonIsDisabled(name) {
725
+ const button = this.buttonByName(name);
726
+ return await button.isDisabled();
727
+ }
728
+ async buttonByTitleIsDisabled(title) {
729
+ const button = this.buttonByTitle(title);
730
+ return await button.isDisabled();
731
+ }
732
+ async clickButton(name) {
733
+ const button = this.buttonByName(name);
734
+ await button.click();
735
+ }
736
+ async clickButtonByTitle(title) {
737
+ const button = this.buttonByTitle(title);
738
+ await button.click();
424
739
  }
425
740
  };
426
741
 
@@ -437,8 +752,7 @@ var ToolbarItem = class extends AbstractElementWithParent {
437
752
  this._formName = formName;
438
753
  }
439
754
  async init() {
440
- if (this._button)
441
- return this._button;
755
+ if (this._button) return this._button;
442
756
  const classNames = await this.getClasses();
443
757
  if (classNames.includes(GROUP_BUTTON_CLASS)) {
444
758
  this._button = new ToolbarGroupButton(this.getFullCssSelector(), this._itemName, this.driver);
@@ -462,6 +776,10 @@ var ToolbarItem = class extends AbstractElementWithParent {
462
776
  return false;
463
777
  }
464
778
  }
779
+ async getTitle() {
780
+ await this.init();
781
+ return await this._button.getTitle();
782
+ }
465
783
  get menu() {
466
784
  return new ToolbarMenu(this.parentCssSelector, this._formName, this._itemName, this.driver);
467
785
  }
@@ -481,7 +799,7 @@ var FormToolbar = class extends AbstractElementWithParent {
481
799
  };
482
800
 
483
801
  // src/pageObjects/elements/Filters/FilterOperation.ts
484
- import { By as By7 } from "selenium-webdriver";
802
+ import { By as By9 } from "selenium-webdriver";
485
803
  var FILTER_OPERATIONS = [
486
804
  "=",
487
805
  "<>",
@@ -509,16 +827,16 @@ var FilterOperation = class extends AbstractElementWithParent {
509
827
  }
510
828
  async initOperationWidget() {
511
829
  const widgetButtonEl = await this.getElement();
512
- await waitElementIsClickable({
830
+ await waitElementIsClickable_default({
513
831
  element: widgetButtonEl,
514
832
  errorMessage: "Widget operation is not clickable",
515
833
  driver: this.driver
516
834
  });
517
- const clickableElement = await widgetButtonEl.findElement(By7.className("dx-icon"));
835
+ const clickableElement = await widgetButtonEl.findElement(By9.className("dx-icon"));
518
836
  await click(clickableElement, this.driver);
519
837
  try {
520
- const widgetPopupEl = await this.driver.findElement(By7.css(`${byTestIdCssSelector(`${OPERATIONS_WIDGET_POPUP}-${this._testId}`)}`));
521
- await waitElementIsVisible({
838
+ const widgetPopupEl = await this.driver.findElement(By9.css(`${byTestIdCssSelector(`${OPERATIONS_WIDGET_POPUP}-${this._testId}`)}`));
839
+ await waitElementIsVisible_default({
522
840
  element: widgetPopupEl,
523
841
  errorMessage: `Operation widget popup is not visible`,
524
842
  driver: this.driver
@@ -532,13 +850,12 @@ var FilterOperation = class extends AbstractElementWithParent {
532
850
  throw new Error(`Operation with name "${operationName}" is not correct.`);
533
851
  }
534
852
  await this.initOperationWidget();
535
- const operationEl = await fixElementStaleError(By7.css(`${byTestIdCssSelector(`${OPERATIONS_WIDGET_POPUP}-${this._testId}`)} ${getWidgetInPopupSelector(operationName)}`), this.driver);
853
+ const operationEl = await fixElementStaleError(By9.css(`${byTestIdCssSelector(`${OPERATIONS_WIDGET_POPUP}-${this._testId}`)} ${getWidgetInPopupSelector(operationName)}`), this.driver);
536
854
  await click(operationEl, this.driver);
537
- await wait(
855
+ await safeWait(
538
856
  async () => {
539
857
  const currOperationName = await this.getAttribute("data-operation");
540
- if (operationName === "isblank")
541
- return true;
858
+ if (operationName === "isblank") return true;
542
859
  return currOperationName === operationName;
543
860
  },
544
861
  this.driver,
@@ -547,15 +864,14 @@ var FilterOperation = class extends AbstractElementWithParent {
547
864
  }
548
865
  async getOperation() {
549
866
  const result = await this.getAttribute("data-operation");
550
- if (!result)
551
- throw new Error("Operation not found");
867
+ if (!result) throw new Error("Operation not found");
552
868
  return result;
553
869
  }
554
870
  async setIsBlank(value) {
555
871
  const currValue = await this.getIsBlank();
556
872
  if (currValue !== value) {
557
873
  await this.setOperation("isblank");
558
- await wait(
874
+ await safeWait(
559
875
  async () => {
560
876
  const currIsBlank = await this.getIsBlank();
561
877
  return currIsBlank === value;
@@ -627,7 +943,8 @@ var FormFilterField = class extends AbstractEditor {
627
943
  async initOperation() {
628
944
  return new FilterOperation({
629
945
  parentCssSelector: this.parentCssSelector,
630
- filterTestID: createLayoutFilterTestId(this.formName, this.name)
946
+ filterTestID: createLayoutFilterTestId(this.formName, this.name),
947
+ driver: this.driver
631
948
  });
632
949
  }
633
950
  /**
@@ -669,7 +986,7 @@ var FormFilterField = class extends AbstractEditor {
669
986
  };
670
987
 
671
988
  // src/pageObjects/elements/Filters/FilterPanel.ts
672
- import { By as By8 } from "selenium-webdriver";
989
+ import { By as By10 } from "selenium-webdriver";
673
990
 
674
991
  // src/pageObjects/elements/Filters/PanelFilterField.ts
675
992
  var PanelFilterField = class extends AbstractEditor {
@@ -727,7 +1044,8 @@ var PanelFilterField = class extends AbstractEditor {
727
1044
  async initOperation() {
728
1045
  return new FilterOperation({
729
1046
  parentCssSelector: this.parentCssSelector,
730
- filterTestID: createDocFilterTestId(this.formName, this.name)
1047
+ filterTestID: createDocFilterTestId(this.formName, this.name),
1048
+ driver: this.driver
731
1049
  });
732
1050
  }
733
1051
  /**
@@ -780,7 +1098,7 @@ var FilterPanel = class extends AbstractElement {
780
1098
  async filterPanelButtons() {
781
1099
  const selector = `${this.elementCssSelector} ${FILTER_PANEL_SELECTOR} .filter-panel__buttons-panel .dx-button-text`;
782
1100
  const el = await this.getElement();
783
- return el.findElements(By8.css(selector));
1101
+ return el.findElements(By10.css(selector));
784
1102
  }
785
1103
  constructor(formName, driver) {
786
1104
  super(driver, drawerSelector(formName));
@@ -813,13 +1131,13 @@ var FilterPanel = class extends AbstractElement {
813
1131
  await this.driver.sleep(300);
814
1132
  }
815
1133
  async close() {
816
- const btn = await this.driver.findElement(By8.css(`${this.elementCssSelector} ${CLOSE_SELECTOR}`));
1134
+ const btn = await this.driver.findElement(By10.css(`${this.elementCssSelector} ${CLOSE_SELECTOR}`));
817
1135
  await click(btn, this.driver);
818
1136
  }
819
1137
  };
820
1138
 
821
1139
  // src/pageObjects/elements/Filters/ColumnFilter.ts
822
- import { By as By9 } from "selenium-webdriver";
1140
+ import { By as By11 } from "selenium-webdriver";
823
1141
 
824
1142
  // src/pageObjects/elements/Filters/HeaderFilterField.ts
825
1143
  var HeaderFilterField = class extends AbstractEditor {
@@ -859,11 +1177,11 @@ var ColumnFilter = class extends AbstractElement {
859
1177
  }
860
1178
  async getApplyButton() {
861
1179
  const selector = `${HEADER_FILTER_CONTAINER} ${HEADER_FILTER_BUTTONS} ${APPLY_SELECTOR}`;
862
- return this.driver.findElement(By9.css(selector));
1180
+ return this.driver.findElement(By11.css(selector));
863
1181
  }
864
1182
  async getCancelButton() {
865
1183
  const selector = `${HEADER_FILTER_CONTAINER} ${HEADER_FILTER_BUTTONS} ${CANCEL_SELECTOR}`;
866
- return this.driver.findElement(By9.css(selector));
1184
+ return this.driver.findElement(By11.css(selector));
867
1185
  }
868
1186
  async apply() {
869
1187
  const btn = await this.getApplyButton();
@@ -877,9 +1195,9 @@ var ColumnFilter = class extends AbstractElement {
877
1195
  const columnEl = await this._column.getElement();
878
1196
  const actions = this.driver.actions({ async: true });
879
1197
  await actions.move({ origin: columnEl }).perform();
880
- const elements = await columnEl.findElements(By9.css(HEADER_FILTER_ICON));
1198
+ const elements = await columnEl.findElements(By11.css(HEADER_FILTER_ICON));
881
1199
  let headerFilterIcon = null;
882
- await wait(
1200
+ await safeWait(
883
1201
  async () => {
884
1202
  let result = false;
885
1203
  for (let element of elements) {
@@ -894,12 +1212,11 @@ var ColumnFilter = class extends AbstractElement {
894
1212
  this.driver,
895
1213
  `HeaderFilter "${this._name}" is not visible`
896
1214
  );
897
- if (!headerFilterIcon)
898
- throw new Error(`HeaderFilter "${this._name}" is not visible`);
899
- await headerFilterIcon.click();
1215
+ if (!headerFilterIcon) throw new Error(`HeaderFilter "${this._name}" is not visible`);
1216
+ await click(headerFilterIcon, this.driver);
900
1217
  const filter = new HeaderFilterField("", this._formName, this._name, this.driver);
901
1218
  const filterEl = await filter.getElement();
902
- await waitElementIsVisible({
1219
+ await waitElementIsVisible_default({
903
1220
  element: filterEl,
904
1221
  driver: this.driver,
905
1222
  errorMessage: `Header filter is not visible`
@@ -927,16 +1244,18 @@ var ColumnFilter = class extends AbstractElement {
927
1244
  */
928
1245
  async setValue(value, operation, isBlank) {
929
1246
  const filter = await this.initFilter();
930
- const inputEl = await filter.getElement();
931
- await inputEl.click();
932
1247
  const filterOperation = new FilterOperation({
933
1248
  parentCssSelector: HEADER_FILTER_CONTAINER,
934
- filterTestID: createHeaderFilterTestId(this._formName, this._name)
1249
+ filterTestID: createHeaderFilterTestId(this._formName, this._name),
1250
+ driver: this.driver
935
1251
  });
1252
+ const inputEl = await filter.getElement();
936
1253
  if (typeof operation === "string") {
1254
+ await inputEl.click();
937
1255
  await filterOperation.setOperation(operation);
938
1256
  }
939
1257
  if (typeof isBlank === "boolean") {
1258
+ await inputEl.click();
940
1259
  await filterOperation.setIsBlank(isBlank);
941
1260
  }
942
1261
  await filter.setValue(value);
@@ -971,7 +1290,8 @@ var ColumnFilter = class extends AbstractElement {
971
1290
  await inputEl.click();
972
1291
  return new FilterOperation({
973
1292
  parentCssSelector: HEADER_FILTER_CONTAINER,
974
- filterTestID: createHeaderFilterTestId(this._formName, this._name)
1293
+ filterTestID: createHeaderFilterTestId(this._formName, this._name),
1294
+ driver: this.driver
975
1295
  });
976
1296
  }
977
1297
  /**
@@ -1016,15 +1336,21 @@ var ColumnFilter = class extends AbstractElement {
1016
1336
 
1017
1337
  // src/pageObjects/elements/Forms/ListForm.ts
1018
1338
  var DATAGRID_HEADER_CSS_SELECTOR = ".dx-datagrid-headers .dx-datagrid-content:not(.dx-datagrid-content-fixed)";
1339
+ var DATAGRID_CONTENT_CSS_SELECTOR = ".dx-datagrid-content:not(.dx-datagrid-content-fixed)";
1019
1340
  var FIXED_DATAGRID_CONTENT_CSS_SELECTOR = ".dx-fixed-columns .dx-datagrid-content.dx-datagrid-content-fixed";
1341
+ var CONTEXT_MENU_CSS_SELECTOR = ".dx-datagrid";
1020
1342
  var ListForm = class extends Form {
1021
1343
  headerCssSelector;
1022
1344
  fixedTableContentCssSelector;
1345
+ tableContentCssSelector;
1346
+ contextMenuCssSelector;
1023
1347
  constructor(driver) {
1024
1348
  super(driver);
1025
1349
  this.popupContainerCssSelector = `.popup.form-inline-${this.formName}`;
1026
1350
  this.headerCssSelector = DATAGRID_HEADER_CSS_SELECTOR;
1027
1351
  this.fixedTableContentCssSelector = FIXED_DATAGRID_CONTENT_CSS_SELECTOR;
1352
+ this.tableContentCssSelector = DATAGRID_CONTENT_CSS_SELECTOR;
1353
+ this.contextMenuCssSelector = CONTEXT_MENU_CSS_SELECTOR;
1028
1354
  }
1029
1355
  /**
1030
1356
  * Находит таблицу
@@ -1035,11 +1361,15 @@ var ListForm = class extends Form {
1035
1361
  */
1036
1362
  table() {
1037
1363
  return new Table(
1038
- this.containerCssSelector,
1039
- this.headerCssSelector,
1040
- this.fixedTableContentCssSelector,
1041
- this.formName,
1042
- this.driver
1364
+ {
1365
+ parentCssSelector: this.containerCssSelector,
1366
+ headerCssSelector: this.headerCssSelector,
1367
+ fixedTableContentCssSelector: this.fixedTableContentCssSelector,
1368
+ formName: this.formName,
1369
+ driver: this.driver,
1370
+ contextMenuCssSelector: this.contextMenuCssSelector,
1371
+ tableContentCssSelector: this.tableContentCssSelector
1372
+ }
1043
1373
  );
1044
1374
  }
1045
1375
  /**