d5-testing-library 1.0.4 → 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.
@@ -1,7 +1,9 @@
1
1
  "use strict";
2
+ var __create = Object.create;
2
3
  var __defProp = Object.defineProperty;
3
4
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
5
  var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
5
7
  var __hasOwnProp = Object.prototype.hasOwnProperty;
6
8
  var __export = (target, all) => {
7
9
  for (var name in all)
@@ -15,6 +17,14 @@ var __copyProps = (to, from, except, desc) => {
15
17
  }
16
18
  return to;
17
19
  };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
18
28
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
29
 
20
30
  // src/index.ts
@@ -29,15 +39,36 @@ __export(src_exports, {
29
39
  SubsystemsPanel: () => SubsystemsPanel,
30
40
  TextEditor: () => TextEditor,
31
41
  byTestId: () => byTestId,
42
+ click: () => click,
32
43
  config: () => config,
33
44
  createDriver: () => createDriver,
34
45
  editorFactory: () => editorFactory,
35
46
  elementIsNotPresent: () => elementIsNotPresent,
36
- signIn: () => signIn
47
+ signIn: () => signIn,
48
+ waitElementIsVisible: () => waitElementIsVisible
37
49
  });
38
50
  module.exports = __toCommonJS(src_exports);
39
51
 
40
52
  // src/config.ts
53
+ var process = __toESM(require("process"));
54
+ var driversMap = /* @__PURE__ */ new Map();
55
+ var createConfigProxy = (config2) => {
56
+ return new Proxy(config2, {
57
+ get(_target, prop) {
58
+ if (prop === "driver") {
59
+ return driversMap.get(process.pid);
60
+ }
61
+ return Reflect.get(...arguments);
62
+ },
63
+ set(_target, prop, value) {
64
+ if (prop === "driver") {
65
+ driversMap.set(process.pid, value);
66
+ return true;
67
+ }
68
+ return Reflect.set(...arguments);
69
+ }
70
+ });
71
+ };
41
72
  var Config = class _Config {
42
73
  static instance;
43
74
  appConfig;
@@ -49,7 +80,7 @@ var Config = class _Config {
49
80
  }
50
81
  config = (cfg) => {
51
82
  if (!cfg)
52
- return this.appConfig;
83
+ return createConfigProxy(this.appConfig);
53
84
  this.appConfig = cfg;
54
85
  };
55
86
  };
@@ -98,7 +129,7 @@ var AbstractPage = class extends AbstractElement {
98
129
  // src/utils/locators.ts
99
130
  var import_selenium_webdriver = require("selenium-webdriver");
100
131
  var byTestId = (testId, element = "div") => {
101
- return import_selenium_webdriver.By.css(`${element}[data-testid=${testId}]`);
132
+ return import_selenium_webdriver.By.css(`${element}[data-testid="${testId}"]`);
102
133
  };
103
134
 
104
135
  // src/utils/createDriver.ts
@@ -120,6 +151,14 @@ var elementIsNotPresent = function elementIsNotPresent2(locator) {
120
151
  });
121
152
  };
122
153
 
154
+ // src/utils/waitElementIsVisible.ts
155
+ var import_selenium_webdriver4 = require("selenium-webdriver");
156
+ async function waitElementIsVisible({ element, timeout, errorMessage, driver }) {
157
+ driver = driver ?? config().driver;
158
+ const condition = import_selenium_webdriver4.until.elementIsVisible(element);
159
+ await driver.wait(condition.fn, timeout ?? 1e4, errorMessage ?? condition.description());
160
+ }
161
+
123
162
  // src/pageObjects/AbstractElementWithParent.ts
124
163
  var AbstractElementWithParent = class extends AbstractElement {
125
164
  getParentElement;
@@ -130,7 +169,23 @@ var AbstractElementWithParent = class extends AbstractElement {
130
169
  }
131
170
  async getElement() {
132
171
  const parentEl = await this.getParentElement();
133
- return parentEl.findElement(this.elementBy);
172
+ await waitElementIsVisible({
173
+ element: parentEl,
174
+ driver: this.driver
175
+ });
176
+ const el = await parentEl.findElement(this.elementBy);
177
+ await waitElementIsVisible({
178
+ element: el,
179
+ driver: this.driver
180
+ });
181
+ return el;
182
+ }
183
+ async getAttribute(attr) {
184
+ const el = await this.getElement();
185
+ return await el.getAttribute(attr);
186
+ }
187
+ async getClasses() {
188
+ return await this.getAttribute("class");
134
189
  }
135
190
  };
136
191
 
@@ -144,9 +199,9 @@ var SubsystemsPanelHeader = class extends AbstractElementWithParent {
144
199
  };
145
200
 
146
201
  // src/pageObjects/elements/SubsystemsPanel/SubsystemsPanel.ts
147
- var import_selenium_webdriver4 = require("selenium-webdriver");
202
+ var import_selenium_webdriver5 = require("selenium-webdriver");
148
203
  var SubsystemsPanel = class extends AbstractElement {
149
- panelBy = import_selenium_webdriver4.By.id("aside");
204
+ panelBy = import_selenium_webdriver5.By.id("aside");
150
205
  header;
151
206
  constructor(driver) {
152
207
  super(driver);
@@ -161,7 +216,7 @@ var SubsystemsPanel = class extends AbstractElement {
161
216
  };
162
217
 
163
218
  // src/pageObjects/elements/Forms/AbstractForm.ts
164
- var import_selenium_webdriver5 = require("selenium-webdriver");
219
+ var import_selenium_webdriver6 = require("selenium-webdriver");
165
220
  var AbstractForm = class extends AbstractPage {
166
221
  containerBy;
167
222
  popupContainerBy;
@@ -206,20 +261,20 @@ var AbstractForm = class extends AbstractPage {
206
261
  async getTitleText() {
207
262
  if (await this.isModal()) {
208
263
  const popupEl = await this.driver.findElement(this.popupContainerBy);
209
- const titleEl2 = await popupEl.findElement(import_selenium_webdriver5.By.className("popup-title"));
264
+ const titleEl2 = await popupEl.findElement(import_selenium_webdriver6.By.className("popup-title"));
210
265
  return titleEl2.getText();
211
266
  }
212
267
  let container = await this.getContainerElement();
213
- let titleEl = await container.findElement(import_selenium_webdriver5.By.className("form-toolbar-name-container"));
268
+ let titleEl = await container.findElement(import_selenium_webdriver6.By.className("form-toolbar-name-container"));
214
269
  return titleEl.getText();
215
270
  }
216
271
  };
217
272
 
218
273
  // src/pageObjects/elements/Forms/FormEdit.ts
219
- var import_selenium_webdriver8 = require("selenium-webdriver");
274
+ var import_selenium_webdriver13 = require("selenium-webdriver");
220
275
 
221
276
  // src/pageObjects/elements/Editors/AbstractEditor.ts
222
- var import_selenium_webdriver6 = require("selenium-webdriver");
277
+ var import_selenium_webdriver7 = require("selenium-webdriver");
223
278
 
224
279
  // src/pageObjects/AbstractFormElement.ts
225
280
  var AbstractFormElement = class extends AbstractElementWithParent {
@@ -229,11 +284,23 @@ var AbstractFormElement = class extends AbstractElementWithParent {
229
284
  }
230
285
  };
231
286
 
287
+ // src/pageObjects/elements/Editors/ClearStrategy/InputClearStrategy.ts
288
+ var InputClearStrategy = class {
289
+ async clear(editor) {
290
+ const inputEl = await editor.getInputElement();
291
+ await inputEl.clear();
292
+ }
293
+ };
294
+
232
295
  // src/pageObjects/elements/Editors/AbstractEditor.ts
233
296
  var AbstractDXEditorWithInput = class extends AbstractFormElement {
297
+ clearStrategy = new InputClearStrategy();
298
+ setClearStrategy(strategy) {
299
+ this.clearStrategy = strategy;
300
+ }
234
301
  async getInputElement() {
235
302
  const controlEl = await this.getElement();
236
- return controlEl.findElement(import_selenium_webdriver6.By.className("dx-texteditor-input"));
303
+ return controlEl.findElement(import_selenium_webdriver7.By.className("dx-texteditor-input"));
237
304
  }
238
305
  async getInputValue() {
239
306
  const inputEl = await this.getInputElement();
@@ -241,13 +308,20 @@ var AbstractDXEditorWithInput = class extends AbstractFormElement {
241
308
  }
242
309
  async setInputValue(value) {
243
310
  const inputEl = await this.getInputElement();
244
- await inputEl.click();
245
- await inputEl.sendKeys(value, import_selenium_webdriver6.Key.ENTER);
311
+ await click(inputEl, this.driver);
312
+ await inputEl.sendKeys(value, import_selenium_webdriver7.Key.ENTER);
313
+ }
314
+ async clear() {
315
+ await this.clearStrategy.clear(this);
246
316
  }
247
317
  };
248
318
 
249
319
  // src/pageObjects/elements/Editors/TextEditor.ts
250
320
  var TextEditor = class extends AbstractDXEditorWithInput {
321
+ constructor(getParentElement, elementBy, driver) {
322
+ super(getParentElement, elementBy, driver);
323
+ this.setClearStrategy(new InputClearStrategy());
324
+ }
251
325
  getValue() {
252
326
  return this.getInputValue();
253
327
  }
@@ -258,6 +332,10 @@ var TextEditor = class extends AbstractDXEditorWithInput {
258
332
 
259
333
  // src/pageObjects/elements/Editors/NumberEditor.ts
260
334
  var NumberEditor = class extends AbstractDXEditorWithInput {
335
+ constructor(getParentElement, elementBy, driver) {
336
+ super(getParentElement, elementBy, driver);
337
+ this.setClearStrategy(new InputClearStrategy());
338
+ }
261
339
  async getValue() {
262
340
  const value = await super.getInputValue();
263
341
  return value == "" ? null : Number.parseFloat(value);
@@ -273,22 +351,22 @@ var CheckBox = class extends AbstractFormElement {
273
351
  const inputEl = await this.getElement();
274
352
  const result = await inputEl.getAttribute("aria-checked");
275
353
  if (result === "true")
276
- return true;
354
+ return 1;
277
355
  if (result === "false")
278
- return false;
279
- return void 0;
356
+ return 0;
357
+ return null;
280
358
  }
281
359
  async setValue(value) {
282
360
  const inputEl = await this.getElement();
283
361
  const currentValue = await this.getValue();
284
362
  if (currentValue !== value) {
285
- await inputEl.click();
363
+ await click(inputEl, this.driver);
286
364
  }
287
365
  }
288
366
  };
289
367
 
290
368
  // src/pageObjects/elements/Editors/SelectBoxEditor.ts
291
- var import_selenium_webdriver7 = require("selenium-webdriver");
369
+ var import_selenium_webdriver9 = require("selenium-webdriver");
292
370
 
293
371
  // src/utils/xpathHelper.ts
294
372
  var XPathHelper = class {
@@ -300,19 +378,53 @@ var XPathHelper = class {
300
378
  }
301
379
  };
302
380
 
381
+ // src/pageObjects/elements/Editors/ClearStrategy/ClearButtonStrategy.ts
382
+ var import_selenium_webdriver8 = require("selenium-webdriver");
383
+ var DX_TEXTEDITOR_EMPTY = "dx-texteditor-empty";
384
+ var DX_CLEAR_BUTTON_AREA = "dx-clear-button-area";
385
+ var ClearButtonStrategy = class {
386
+ editorContainerClass;
387
+ constructor(editorContainerClass) {
388
+ this.editorContainerClass = editorContainerClass;
389
+ }
390
+ async isEmptyEditorField(editor) {
391
+ const parentEl = await editor.getParentElement();
392
+ const editorContainer = await parentEl.findElement(import_selenium_webdriver8.By.className(this.editorContainerClass));
393
+ const classAttribute = await editorContainer.getAttribute("class");
394
+ return classAttribute.includes(DX_TEXTEDITOR_EMPTY);
395
+ }
396
+ async clear(editor) {
397
+ if (await this.isEmptyEditorField(editor)) {
398
+ throw new Error("This field is already empty");
399
+ }
400
+ const clearBtnSelector = `.${this.editorContainerClass} .${DX_CLEAR_BUTTON_AREA}`;
401
+ const parentEl = await editor.getParentElement();
402
+ const clearBtn = await parentEl.findElement(import_selenium_webdriver8.By.css(clearBtnSelector));
403
+ await click(clearBtn, editor.driver);
404
+ }
405
+ };
406
+
303
407
  // src/pageObjects/elements/Editors/SelectBoxEditor.ts
304
408
  var SELECT_BOX_OPTION_TEXT_CONTAINER_CLASS = "d5-multi-select-item";
305
409
  var SELECT_BOX_ELEMENT_ROLE = "option";
410
+ var combinedValueXpath = (value) => `${XPathHelper.getRoleXpath(SELECT_BOX_ELEMENT_ROLE)}${XPathHelper.getClassAndValueXpath(
411
+ SELECT_BOX_OPTION_TEXT_CONTAINER_CLASS,
412
+ value
413
+ )}`;
414
+ var DX_SELECTBOX = "dx-selectbox";
306
415
  var SelectBoxEditor = class extends AbstractDXEditorWithInput {
416
+ constructor(getParentElement, elementBy, driver) {
417
+ super(getParentElement, elementBy, driver);
418
+ this.setClearStrategy(new ClearButtonStrategy(DX_SELECTBOX));
419
+ }
307
420
  async setSelectInputValue(value) {
308
421
  const inputEl = await this.getInputElement();
309
- await inputEl.click();
310
- await inputEl.sendKeys(value, import_selenium_webdriver7.Key.ENTER);
311
- const combinedValueXpath = XPathHelper.getRoleXpath(SELECT_BOX_ELEMENT_ROLE) + XPathHelper.getClassAndValueXpath(SELECT_BOX_OPTION_TEXT_CONTAINER_CLASS, value);
422
+ await click(inputEl, this.driver);
423
+ await inputEl.sendKeys(value, import_selenium_webdriver9.Key.ENTER);
312
424
  await this.driver.wait(async () => {
313
425
  try {
314
- const optElement = await inputEl.findElement(import_selenium_webdriver7.By.xpath(combinedValueXpath));
315
- await optElement.click();
426
+ const optElement = await inputEl.findElement(import_selenium_webdriver9.By.xpath(combinedValueXpath(value)));
427
+ await click(optElement, this.driver);
316
428
  return true;
317
429
  } catch (e) {
318
430
  return false;
@@ -337,7 +449,7 @@ var SwitchEditor = class extends AbstractFormElement {
337
449
  return 1;
338
450
  if (result === "OFF")
339
451
  return 0;
340
- return void 0;
452
+ return null;
341
453
  }
342
454
  async setValue(value) {
343
455
  if (value !== 0 && value !== 1) {
@@ -346,7 +458,7 @@ var SwitchEditor = class extends AbstractFormElement {
346
458
  const inputEl = await this.getElement();
347
459
  const currentValue = await this.getValue();
348
460
  if (currentValue !== value) {
349
- await inputEl.click();
461
+ await click(inputEl, this.driver);
350
462
  await this.driver.wait(async () => {
351
463
  const updatedValue = await this.getValue();
352
464
  return updatedValue !== currentValue;
@@ -355,21 +467,205 @@ var SwitchEditor = class extends AbstractFormElement {
355
467
  }
356
468
  };
357
469
 
470
+ // src/pageObjects/elements/Editors/TagBox.ts
471
+ var import_selenium_webdriver10 = require("selenium-webdriver");
472
+ var TAGS_SELECTOR = ".dx-tag .tag-text";
473
+ var DX_TAG_CONTAINER = "dx-tag-container";
474
+ var DX_TAGBOX = "dx-tagbox";
475
+ var DX_TEXTEDITOR_EMPTY2 = "dx-texteditor-empty";
476
+ var BTN_OK_SELECTOR = ".dx-tagbox-popup-wrapper .dx-popup-bottom .dx-button.dx-popup-done";
477
+ var TagBox = class extends AbstractDXEditorWithInput {
478
+ constructor(getParentElement, elementBy, driver) {
479
+ super(getParentElement, elementBy, driver);
480
+ this.setClearStrategy(new ClearButtonStrategy(DX_TAGBOX));
481
+ }
482
+ async setTagBoxInputValue(values) {
483
+ const lastElement = values[values.length - 1];
484
+ const inputEl = await this.getInputElement();
485
+ await inputEl.click();
486
+ for (const value of values) {
487
+ await this.selectOption(inputEl, value, lastElement);
488
+ }
489
+ await this.confirmSelection();
490
+ }
491
+ async selectOption(inputEl, value, lastElement) {
492
+ await inputEl.sendKeys(value);
493
+ await this.driver.wait(async () => {
494
+ try {
495
+ const optElement = await inputEl.findElement(import_selenium_webdriver10.By.xpath(combinedValueXpath(value)));
496
+ await click(optElement, this.driver);
497
+ return true;
498
+ } catch (e) {
499
+ return false;
500
+ }
501
+ });
502
+ if (value !== lastElement) {
503
+ await inputEl.clear();
504
+ }
505
+ }
506
+ async confirmSelection() {
507
+ const inputEl = await this.getInputElement();
508
+ const option = await inputEl.findElement(import_selenium_webdriver10.By.xpath(XPathHelper.getRoleXpath(SELECT_BOX_ELEMENT_ROLE)));
509
+ const btnOk = await this.driver.findElement(import_selenium_webdriver10.By.css(BTN_OK_SELECTOR));
510
+ await this.driver.wait(async () => {
511
+ return await option.getAttribute("aria-selected") === "true";
512
+ });
513
+ await click(btnOk, this.driver);
514
+ }
515
+ async getTagBoxInputValue() {
516
+ if (await this.isTagBoxEmpty()) {
517
+ return [];
518
+ }
519
+ const parentEl = await this.getParentElement();
520
+ const inputEl = await parentEl.findElement(import_selenium_webdriver10.By.className(DX_TAG_CONTAINER));
521
+ const tags = await inputEl.findElements(import_selenium_webdriver10.By.css(TAGS_SELECTOR));
522
+ const values = [];
523
+ for (const tag of tags) {
524
+ const text = await tag.getText();
525
+ values.push(text);
526
+ }
527
+ return values;
528
+ }
529
+ async isTagBoxEmpty() {
530
+ const parentEl = await this.getParentElement();
531
+ const tagboxContainer = await parentEl.findElement(import_selenium_webdriver10.By.className(DX_TAGBOX));
532
+ const classAttribute = await tagboxContainer.getAttribute("class");
533
+ return classAttribute.includes(DX_TEXTEDITOR_EMPTY2);
534
+ }
535
+ async getValue() {
536
+ const value = await this.getTagBoxInputValue();
537
+ return value.length ? value : null;
538
+ }
539
+ async setValue(values) {
540
+ if (!Array.isArray(values)) {
541
+ throw new Error("The provided value is not an array");
542
+ }
543
+ await this.setTagBoxInputValue(values);
544
+ }
545
+ async remove(itemText) {
546
+ const tagValues = await this.getValue();
547
+ const tagExists = tagValues.includes(itemText);
548
+ if (!tagExists) {
549
+ throw new Error(`The tag with the text: ${itemText} was not found`);
550
+ }
551
+ const parentEl = await this.getParentElement();
552
+ const inputEl = await parentEl.findElement(import_selenium_webdriver10.By.className(DX_TAG_CONTAINER));
553
+ const clearBtnTestId = byTestId(`clear-button-${itemText}`);
554
+ const clearBtn = await inputEl.findElement(clearBtnTestId);
555
+ await click(clearBtn, this.driver);
556
+ }
557
+ };
558
+
559
+ // src/pageObjects/elements/Editors/ButtonGroup.ts
560
+ var import_selenium_webdriver11 = require("selenium-webdriver");
561
+ var ITEM_CLASS = "dx-buttongroup-item";
562
+ var SELECTED_CLASS = "dx-state-selected";
563
+ var ButtonGroup = class extends AbstractFormElement {
564
+ async getItems() {
565
+ return (await this.getElement()).findElements(import_selenium_webdriver11.By.className(ITEM_CLASS));
566
+ }
567
+ getDataValue(el) {
568
+ return el.getAttribute("data-value");
569
+ }
570
+ async getValue() {
571
+ const inputEl = await this.getElement();
572
+ const selectedEl = await inputEl.findElement(import_selenium_webdriver11.By.className(`${ITEM_CLASS} ${SELECTED_CLASS}`));
573
+ return this.getDataValue(selectedEl);
574
+ }
575
+ async setValue(value) {
576
+ const items = await this.getItems();
577
+ let buttonEl = void 0;
578
+ for (const item of items) {
579
+ const text = await this.getDataValue(item);
580
+ if (text == value) {
581
+ buttonEl = item;
582
+ break;
583
+ }
584
+ }
585
+ if (!buttonEl)
586
+ throw new Error(`Cannot find item in button group with text: ${value}`);
587
+ await click(buttonEl, this.driver);
588
+ }
589
+ };
590
+
591
+ // src/pageObjects/elements/Editors/BooleanSelector.ts
592
+ var BooleanSelector = class extends ButtonGroup {
593
+ // @ts-ignore
594
+ async getValue() {
595
+ const value = await super.getValue();
596
+ return value === "null" ? null : value === "1" ? 1 : 0;
597
+ }
598
+ // @ts-ignore
599
+ async setValue(value) {
600
+ const val = value == null ? "null" : value ? "1" : "0";
601
+ await super.setValue(val);
602
+ }
603
+ };
604
+
605
+ // src/pageObjects/elements/Editors/DateEditor.ts
606
+ var import_selenium_webdriver12 = require("selenium-webdriver");
607
+ var DateEditor = class extends AbstractDXEditorWithInput {
608
+ constructor(getParentElement, elementBy, driver) {
609
+ super(getParentElement, elementBy, driver);
610
+ this.setClearStrategy(new InputClearStrategy());
611
+ }
612
+ async getControlElement() {
613
+ const controlEl = await this.getElement();
614
+ return controlEl.findElement(import_selenium_webdriver12.By.className("dx-dropdowneditor-input-wrapper"));
615
+ }
616
+ async getDateElement() {
617
+ const controlEl = await this.getControlElement();
618
+ return controlEl.findElement(import_selenium_webdriver12.By.css("input[type=hidden]"));
619
+ }
620
+ async setDateValue(value) {
621
+ const dateEl = await this.getInputElement();
622
+ const stringDate = value.toJSON().split("T")[0];
623
+ const dateArr = stringDate.split("-");
624
+ const [year, month, day] = dateArr;
625
+ const keys = day + month + year;
626
+ await click(dateEl, this.driver);
627
+ const actions = this.driver.actions({ async: true });
628
+ await actions.move({ origin: dateEl }).sendKeys(import_selenium_webdriver12.Key.HOME).perform();
629
+ await actions.move({ origin: dateEl }).sendKeys(keys, import_selenium_webdriver12.Key.ENTER).perform();
630
+ }
631
+ async getDateValue() {
632
+ const inputEl = await this.getDateElement();
633
+ return inputEl.getAttribute("value");
634
+ }
635
+ async getValue() {
636
+ const value = await this.getDateValue();
637
+ return value == "" ? null : new Date(value);
638
+ }
639
+ setValue(value) {
640
+ if (typeof value === "object") {
641
+ return this.setDateValue(value);
642
+ }
643
+ throw new Error(`Value is incorrect. It should be Object`);
644
+ }
645
+ };
646
+
358
647
  // src/pageObjects/elements/Editors/editorFactory.ts
359
648
  var editorFactory = async (getParentElement, elementBy, driver) => {
360
649
  const elementInstance = new AbstractFormElement(getParentElement, elementBy, driver);
361
- const element = await elementInstance.getElement();
362
- const classSting = await element.getAttribute("class");
650
+ const classSting = await elementInstance.getClasses();
363
651
  if (classSting.includes("text-control"))
364
652
  return new TextEditor(getParentElement, elementBy, driver);
365
653
  if (classSting.includes("number-control"))
366
654
  return new NumberEditor(getParentElement, elementBy, driver);
367
655
  if (classSting.includes("check-box"))
368
656
  return new CheckBox(getParentElement, elementBy, driver);
657
+ if (classSting.includes("multi-select-control"))
658
+ return new TagBox(getParentElement, elementBy, driver);
369
659
  if (classSting.includes("select-control"))
370
660
  return new SelectBoxEditor(getParentElement, elementBy, driver);
371
661
  if (classSting.includes("switch-control"))
372
662
  return new SwitchEditor(getParentElement, elementBy, driver);
663
+ if (classSting.includes("date-control"))
664
+ return new DateEditor(getParentElement, elementBy, driver);
665
+ if (classSting.includes("buttons-group"))
666
+ return new BooleanSelector(getParentElement, elementBy, driver);
667
+ if (classSting.includes("button-group-field"))
668
+ return new ButtonGroup(getParentElement, elementBy, driver);
373
669
  throw new Error("Unknown type of editor. Please write ticket to the support");
374
670
  };
375
671
 
@@ -380,6 +676,9 @@ function createDataTestId(formName, itemType, itemName) {
380
676
  function createFieldTestId(formName, itemName) {
381
677
  return createDataTestId(formName, "form_field" /* FORM_FIELD */, itemName);
382
678
  }
679
+ function createGroupTestId(formName, itemName) {
680
+ return createDataTestId(formName, "group" /* GROUP */, itemName);
681
+ }
383
682
  function createTableId(formName) {
384
683
  return `${"table" /* TABLE */}-${formName}`;
385
684
  }
@@ -416,14 +715,82 @@ var FormField = class extends AbstractElementWithParent {
416
715
  await this.initEditor();
417
716
  return this.editor.setValue(value);
418
717
  }
718
+ /**
719
+ * Очищает значение в поле
720
+ * @example
721
+ * await formEdit.field('NumberFld').clear();
722
+ */
723
+ async clear() {
724
+ await this.initEditor();
725
+ if (!this.editor.clear) {
726
+ throw new Error("The clear method is not available for this field type.");
727
+ }
728
+ return this.editor.clear();
729
+ }
730
+ /**
731
+ * Удаляет тег по переданному тексту
732
+ * @example
733
+ * await formEdit.field('TagBox').remove('test');
734
+ */
735
+ async remove(itemText) {
736
+ await this.initEditor();
737
+ if (!this.editor.remove) {
738
+ throw new Error("This method is only available for the TagBox field.");
739
+ }
740
+ return this.editor.remove(itemText);
741
+ }
742
+ };
743
+
744
+ // src/pageObjects/elements/Groups/AbstractGroup.ts
745
+ var AbstractDXGroup = class extends AbstractFormElement {
746
+ };
747
+
748
+ // src/pageObjects/elements/Groups/Panel.ts
749
+ var Panel = class extends AbstractDXGroup {
750
+ async isDisplayed() {
751
+ const groupElement = await this.getElement();
752
+ return await groupElement.isDisplayed();
753
+ }
754
+ };
755
+
756
+ // src/pageObjects/elements/Groups/groupFactory.ts
757
+ var groupFactory = async (getParentElement, elementBy, driver) => {
758
+ const elementInstance = new AbstractFormElement(getParentElement, elementBy, driver);
759
+ const classSting = await elementInstance.getClasses();
760
+ if (classSting.includes("panel-group"))
761
+ return new Panel(getParentElement, elementBy, driver);
762
+ throw new Error("Unknown type of group. Please write ticket to the support");
763
+ };
764
+
765
+ // src/pageObjects/elements/FormGroup.ts
766
+ var FormGroup = class extends AbstractElementWithParent {
767
+ editor;
768
+ constructor(getParentElement, formName, name, driver) {
769
+ super(getParentElement, driver);
770
+ this.elementBy = byTestId(createGroupTestId(formName, name));
771
+ }
772
+ async initEditor() {
773
+ if (!this.editor) {
774
+ this.editor = await groupFactory(this.getParentElement, this.elementBy, this.driver);
775
+ }
776
+ }
777
+ /**
778
+ * Возвращает булевое значение доступна ли группа
779
+ * @example
780
+ * expect(await tableList.group('MainSuperPanel').isDisplayed();
781
+ */
782
+ async isDisplayed() {
783
+ await this.initEditor();
784
+ return this.editor.isDisplayed();
785
+ }
419
786
  };
420
787
 
421
788
  // src/pageObjects/elements/Forms/FormEdit.ts
422
789
  var FormEdit = class extends AbstractForm {
423
- saveButtonBy = import_selenium_webdriver8.By.id("button-save");
790
+ saveButtonBy = import_selenium_webdriver13.By.id("button-save");
424
791
  constructor(driver) {
425
792
  super(driver);
426
- this.popupContainerBy = import_selenium_webdriver8.By.className(`popup form-edit-${this.formName}`);
793
+ this.popupContainerBy = import_selenium_webdriver13.By.className(`popup form-edit-${this.formName}`);
427
794
  }
428
795
  /**
429
796
  * Находит поле формы редактирования по имени
@@ -435,6 +802,28 @@ var FormEdit = class extends AbstractForm {
435
802
  field(name) {
436
803
  return new FormField(this.getContainerElement, this.formName, name, this.driver);
437
804
  }
805
+ /**
806
+ * Проверяет не отключена ли кнопка Сохранить
807
+ * @example
808
+ * const formEdit = new MyForm();
809
+ * //...
810
+ * await formEdit.isSaveButtonDisabled();
811
+ */
812
+ async isSaveButtonDisabled() {
813
+ const saveButton = await this.driver.findElement(this.saveButtonBy);
814
+ const result = await saveButton.getAttribute("aria-disabled");
815
+ return result === "true";
816
+ }
817
+ /**
818
+ * Находит группу формы редактирования по имени
819
+ * @example
820
+ * const formEdit = new MyForm();
821
+ * //...
822
+ * expect(await formEdit.group('Name');
823
+ */
824
+ group(name) {
825
+ return new FormGroup(this.getContainerElement, this.formName, name, this.driver);
826
+ }
438
827
  /**
439
828
  * Нажатие на кнопку Сохранить
440
829
  * @example
@@ -443,41 +832,42 @@ var FormEdit = class extends AbstractForm {
443
832
  * await formEdit.saveButtonClick();
444
833
  */
445
834
  async saveButtonClick() {
446
- await this.driver.findElement(this.saveButtonBy).click();
835
+ const saveButton = await this.driver.findElement(this.saveButtonBy);
836
+ await this.driver.wait(async () => !await this.isSaveButtonDisabled(), void 0, "Button is disabled");
837
+ await click(saveButton, this.driver);
447
838
  }
448
839
  };
449
840
 
450
841
  // src/pageObjects/elements/Forms/ListForm.ts
451
- var import_selenium_webdriver14 = require("selenium-webdriver");
842
+ var import_selenium_webdriver19 = require("selenium-webdriver");
452
843
 
453
844
  // src/pageObjects/elements/Table/Table.ts
454
- var import_selenium_webdriver11 = require("selenium-webdriver");
845
+ var import_selenium_webdriver16 = require("selenium-webdriver");
455
846
 
456
847
  // src/pageObjects/elements/Table/utils.ts
457
- var import_selenium_webdriver9 = require("selenium-webdriver");
848
+ var import_selenium_webdriver14 = require("selenium-webdriver");
458
849
  var getRowPathByIndex = (index) => {
459
- return import_selenium_webdriver9.By.xpath(`//tr[@aria-rowindex="${transformInputIndex(index)}"]`);
850
+ return import_selenium_webdriver14.By.css(`tr[aria-rowindex="${transformInputIndex(index)}"]`);
460
851
  };
852
+ var getColumnByIndexCssSelector = (columnIndex) => `td[aria-colindex="${transformInputIndex(columnIndex)}"]`;
461
853
  var getColumnPathByIndex = (index) => {
462
- return import_selenium_webdriver9.By.xpath(`//td[@aria-colindex="${transformInputIndex(index)}"]`);
463
- };
464
- var getColumnPathByCaption = (caption) => {
465
- return import_selenium_webdriver9.By.xpath(`//td[@role="columnheader"][@aria-label="Column ${caption}"]`);
854
+ return import_selenium_webdriver14.By.css(getColumnByIndexCssSelector(index));
466
855
  };
856
+ var getColumnByCaptionCssSelector = (caption) => `td[role="columnheader"][aria-label="Column ${caption}"]`;
467
857
  var getCellPathByIndex = (index) => {
468
- return import_selenium_webdriver9.By.xpath(`//td[@role="gridcell"][@aria-colindex="${transformInputIndex(index)}"]`);
858
+ return import_selenium_webdriver14.By.css(`td[role="gridcell"][aria-colindex="${transformInputIndex(index)}"]`);
469
859
  };
470
860
  var transformInputIndex = (index) => index + 1;
471
861
  var transformOutputIndex = (index) => +index - 1;
472
862
 
473
863
  // src/pageObjects/elements/Table/elements/Row.ts
474
- var import_selenium_webdriver10 = require("selenium-webdriver");
864
+ var import_selenium_webdriver15 = require("selenium-webdriver");
475
865
 
476
866
  // src/pageObjects/elements/Table/elements/Cell.ts
477
867
  var Cell = class extends AbstractElementWithParent {
478
868
  index;
479
- constructor(getParentElement, index, driver) {
480
- super(getParentElement, driver);
869
+ constructor(rowEl, index, driver) {
870
+ super(() => Promise.resolve(rowEl), driver);
481
871
  this.index = index;
482
872
  this.elementBy = getCellPathByIndex(index);
483
873
  }
@@ -491,6 +881,9 @@ var Cell = class extends AbstractElementWithParent {
491
881
  };
492
882
 
493
883
  // src/pageObjects/elements/Table/elements/Row.ts
884
+ var DX_SELECTION_CLASS = "dx-selection";
885
+ var DX_ROW_EDIT_CLASS = "dx-edit-row";
886
+ var DX_DATA_ROW_CLASS = "dx-data-row";
494
887
  var Row = class extends AbstractElementWithParent {
495
888
  index;
496
889
  getColumnByCaption;
@@ -500,14 +893,11 @@ var Row = class extends AbstractElementWithParent {
500
893
  this.elementBy = getRowPathByIndex(index);
501
894
  this.getColumnByCaption = getColumnByCaption;
502
895
  }
503
- getRowElement = () => {
504
- return this.driver.findElement(this.elementBy);
505
- };
506
896
  async getCellByIndex(index) {
507
897
  const rowEl = await this.getElement();
508
898
  const cell = rowEl.findElement(getCellPathByIndex(index));
509
899
  if (cell) {
510
- return new Cell(this.getRowElement, index, this.driver);
900
+ return new Cell(rowEl, index, this.driver);
511
901
  }
512
902
  throw new Error(`Cannot find cell by index: ${index}`);
513
903
  }
@@ -521,7 +911,11 @@ var Row = class extends AbstractElementWithParent {
521
911
  }
522
912
  async select() {
523
913
  const rowEl = await this.getElement();
524
- await rowEl.click();
914
+ await click(rowEl, this.driver);
915
+ await this.driver.wait(async () => {
916
+ const classes = await rowEl.getAttribute("class");
917
+ return classes.includes(DX_SELECTION_CLASS);
918
+ }, 500, "Cannot select row");
525
919
  }
526
920
  async isSelected() {
527
921
  const rowEl = await this.getElement();
@@ -533,34 +927,38 @@ var Row = class extends AbstractElementWithParent {
533
927
  }
534
928
  async getCells() {
535
929
  const rowEl = await this.getElement();
536
- const cells = await rowEl.findElements(import_selenium_webdriver10.By.css(`td[role="gridcell"]`));
930
+ const cells = await rowEl.findElements(import_selenium_webdriver15.By.css(`td[role="gridcell"]`));
537
931
  const result = [];
538
932
  cells.forEach((_, index) => {
539
- const newCell = new Cell(this.getRowElement, index, this.driver);
933
+ const newCell = new Cell(rowEl, index, this.driver);
540
934
  result.push(newCell);
541
935
  });
542
936
  return result;
543
937
  }
544
- async clickInlineButton(rowClass, buttonClass) {
545
- const fixedTable = await this.getParentElement().findElement(
546
- import_selenium_webdriver10.By.css(".dx-fixed-columns .dx-datagrid-content.dx-datagrid-content-fixed")
938
+ async clickInlineButton(buttonClass) {
939
+ const FIXED_TABLE_CSS_SELECTOR = ".dx-fixed-columns .dx-datagrid-content.dx-datagrid-content-fixed";
940
+ const buttonCssSelector = `.${DX_DATA_ROW_CLASS}[aria-rowindex="${transformInputIndex(this.index)}"] .${buttonClass}`;
941
+ const button = (await this.getParentElement()).findElement(
942
+ import_selenium_webdriver15.By.css(`${FIXED_TABLE_CSS_SELECTOR} ${buttonCssSelector}`)
547
943
  );
548
- const rowEl = await fixedTable.findElement(import_selenium_webdriver10.By.className(rowClass));
549
- const button = await rowEl.findElement(import_selenium_webdriver10.By.className(buttonClass));
550
- if (button) {
551
- return button.click();
552
- }
553
- throw new Error(`Cannot find ${buttonClass} button`);
944
+ return click(button, this.driver);
945
+ }
946
+ async checkEditingMode(isEditing, msg) {
947
+ await this.driver.wait(async () => {
948
+ return (await this.getClasses()).includes(DX_ROW_EDIT_CLASS) === isEditing;
949
+ }, 2e3, msg);
554
950
  }
555
951
  async edit() {
556
- await this.select();
557
- return this.clickInlineButton("dx-selection", "edit");
952
+ await this.clickInlineButton("edit");
953
+ await this.checkEditingMode(true, "Row did not switch to edit mode");
558
954
  }
559
955
  async save() {
560
- return this.clickInlineButton("dx-edit-row", "save");
956
+ await this.clickInlineButton("save");
957
+ await this.checkEditingMode(false, "Row did not saved");
561
958
  }
562
959
  async cancel() {
563
- return this.clickInlineButton("dx-edit-row", "cancel");
960
+ await this.clickInlineButton("cancel");
961
+ await this.checkEditingMode(false, "Row did not switch to normal mode");
564
962
  }
565
963
  };
566
964
 
@@ -583,20 +981,21 @@ var Column = class extends AbstractElementWithParent {
583
981
 
584
982
  // src/pageObjects/elements/Table/Table.ts
585
983
  var Table = class extends AbstractElementWithParent {
984
+ formName;
586
985
  constructor(getParentElement, formName, driver) {
587
986
  super(getParentElement, driver);
588
- this.elementBy = import_selenium_webdriver11.By.id(createTableId(formName));
987
+ this.elementBy = import_selenium_webdriver16.By.id(createTableId(formName));
988
+ this.formName = formName;
589
989
  }
590
- async getHeaderElement() {
591
- const parentEl = await this.getElement();
592
- return parentEl.findElement(import_selenium_webdriver11.By.className("dx-header-row"));
990
+ getHeaderCssSelector() {
991
+ return ".dx-datagrid-headers .dx-datagrid-content:not(.dx-datagrid-content-fixed)";
593
992
  }
594
993
  async getDataGrid() {
595
994
  const parentEl = await this.getElement();
596
- return parentEl.findElement(import_selenium_webdriver11.By.css(`div[role="grid"]`));
995
+ return parentEl.findElement(import_selenium_webdriver16.By.css(`div[role="grid"]`));
597
996
  }
598
997
  getTableElement = () => {
599
- return this.driver.findElement(this.elementBy);
998
+ return this.getElement();
600
999
  };
601
1000
  async getRowByIndex(index) {
602
1001
  const gridContainer = await this.getDataGrid();
@@ -607,16 +1006,14 @@ var Table = class extends AbstractElementWithParent {
607
1006
  throw new Error(`Cannot find row by index: ${index}`);
608
1007
  }
609
1008
  async getColumnByIndex(index) {
610
- const headerContainer = await this.getHeaderElement();
611
- const column = await headerContainer.findElement(getColumnPathByIndex(index));
1009
+ const column = await (await this.getElement()).findElement(import_selenium_webdriver16.By.css(`${this.getHeaderCssSelector()} ${getColumnByIndexCssSelector(index)}`));
612
1010
  if (column) {
613
1011
  return new Column(this.getTableElement, index, this.driver);
614
1012
  }
615
1013
  throw new Error(`Cannot find column by index: ${index}`);
616
1014
  }
617
1015
  async getColumnByCaption(caption) {
618
- const headerContainer = await this.getHeaderElement();
619
- const column = await headerContainer.findElement(getColumnPathByCaption(caption));
1016
+ const column = await (await this.getElement()).findElement(import_selenium_webdriver16.By.css(`${this.getHeaderCssSelector()} ${getColumnByCaptionCssSelector(caption)}`));
620
1017
  if (column) {
621
1018
  const index = await column.getAttribute("aria-colindex");
622
1019
  return new Column(this.getTableElement, transformOutputIndex(index), this.driver);
@@ -633,7 +1030,7 @@ var Table = class extends AbstractElementWithParent {
633
1030
  }
634
1031
  async getRows() {
635
1032
  const gridContainer = await this.getDataGrid();
636
- const rows = await gridContainer.findElements(import_selenium_webdriver11.By.className("dx-row dx-data-row"));
1033
+ const rows = await gridContainer.findElements(import_selenium_webdriver16.By.className("dx-row dx-data-row"));
637
1034
  const result = [];
638
1035
  rows.forEach((_, index) => {
639
1036
  const newRow = new Row(this.getTableElement, index, this.driver, this.getColumnByCaption.bind(this));
@@ -642,8 +1039,7 @@ var Table = class extends AbstractElementWithParent {
642
1039
  return result;
643
1040
  }
644
1041
  async getColumns() {
645
- const headerContainer = await this.getHeaderElement();
646
- const columns = await headerContainer.findElements(import_selenium_webdriver11.By.css(`td[role="columnheader"]`));
1042
+ const columns = await (await this.getElement()).findElements(import_selenium_webdriver16.By.css(`${this.getHeaderCssSelector()} td[role="columnheader"]`));
647
1043
  const result = [];
648
1044
  columns.forEach((_, index) => {
649
1045
  const newCol = new Column(this.getTableElement, index, this.driver);
@@ -651,25 +1047,40 @@ var Table = class extends AbstractElementWithParent {
651
1047
  });
652
1048
  return result;
653
1049
  }
654
- async element() {
1050
+ element() {
655
1051
  return this.getElement();
656
1052
  }
1053
+ /**
1054
+ * Находит поле когда форма находится в состоянии редактирования
1055
+ * @example
1056
+ * const form = new MyForm();
1057
+ * //...
1058
+ * expect(await form.field('Name').getValue()).toBe('test value');
1059
+ */
1060
+ field(name) {
1061
+ return new FormField(this.getTableElement, this.formName, name, this.driver);
1062
+ }
657
1063
  };
658
1064
 
659
1065
  // src/pageObjects/elements/TableToolbar/TableToolbar.ts
660
- var import_selenium_webdriver13 = require("selenium-webdriver");
1066
+ var import_selenium_webdriver18 = require("selenium-webdriver");
661
1067
 
662
1068
  // src/pageObjects/elements/TableToolbar/TableToolbarButton.ts
663
- var import_selenium_webdriver12 = require("selenium-webdriver");
1069
+ var import_selenium_webdriver17 = require("selenium-webdriver");
664
1070
  var TOOLBAR_BUTTON_CLASS = "toolbar-button";
665
1071
  var TableToolbarButton = class extends AbstractElementWithParent {
1072
+ name;
666
1073
  constructor(getParentElement, name, driver) {
667
1074
  super(getParentElement, driver);
668
- this.elementBy = import_selenium_webdriver12.By.id(`${TOOLBAR_BUTTON_CLASS}-${name}`);
1075
+ this.name = name;
1076
+ this.elementBy = import_selenium_webdriver17.By.id(`${TOOLBAR_BUTTON_CLASS}-${name}`);
669
1077
  }
670
1078
  async click() {
671
1079
  const button = await this.getElement();
672
- return button.click();
1080
+ await this.driver.wait(async () => {
1081
+ return await button.isDisplayed() && !await this.isDisabled();
1082
+ }, 1e3, `Button ${this.name} cannot be clicked. It is not displayed or it is disabled`);
1083
+ return click(button, this.driver);
673
1084
  }
674
1085
  async isDisabled() {
675
1086
  const button = await this.getElement();
@@ -683,14 +1094,14 @@ var TABLE_TOOLBAR_CLASS = "d5-table-toolbar";
683
1094
  var TableToolbar = class extends AbstractElementWithParent {
684
1095
  constructor(getParentElement, formName, driver) {
685
1096
  super(getParentElement, driver);
686
- this.elementBy = import_selenium_webdriver13.By.id(`${TABLE_TOOLBAR_CLASS}-${formName}`);
1097
+ this.elementBy = import_selenium_webdriver18.By.id(`${TABLE_TOOLBAR_CLASS}-${formName}`);
687
1098
  }
688
1099
  getTableToolbarElement = () => {
689
1100
  return this.driver.findElement(this.elementBy);
690
1101
  };
691
1102
  async button(name) {
692
1103
  const toolbar = await this.getTableToolbarElement();
693
- const button = await toolbar.findElement(import_selenium_webdriver13.By.id(`${TOOLBAR_BUTTON_CLASS}-${name}`));
1104
+ const button = await toolbar.findElement(import_selenium_webdriver18.By.id(`${TOOLBAR_BUTTON_CLASS}-${name}`));
694
1105
  if (button) {
695
1106
  return new TableToolbarButton(this.getTableToolbarElement, name, this.driver);
696
1107
  }
@@ -702,7 +1113,7 @@ var TableToolbar = class extends AbstractElementWithParent {
702
1113
  var ListForm = class extends AbstractForm {
703
1114
  constructor(driver) {
704
1115
  super(driver);
705
- this.popupContainerBy = import_selenium_webdriver14.By.className(`popup form-inline-${this.formName}`);
1116
+ this.popupContainerBy = import_selenium_webdriver19.By.className(`popup form-inline-${this.formName}`);
706
1117
  }
707
1118
  /**
708
1119
  * Находит таблицу
@@ -714,6 +1125,16 @@ var ListForm = class extends AbstractForm {
714
1125
  table() {
715
1126
  return new Table(this.getContainerElement, this.formName, this.driver);
716
1127
  }
1128
+ /**
1129
+ * Находит группу на лист форме по имени
1130
+ * @example
1131
+ * const formEdit = new MyForm();
1132
+ * //...
1133
+ * expect(await formEdit.group('Name');
1134
+ */
1135
+ group(name) {
1136
+ return new FormGroup(this.getContainerElement, this.formName, name, this.driver);
1137
+ }
717
1138
  /**
718
1139
  * Находит таблицу
719
1140
  * @example
@@ -737,12 +1158,27 @@ async function signIn(driver) {
737
1158
  return webDriver;
738
1159
  }
739
1160
 
1161
+ // src/utils/actions.ts
1162
+ var click = async (webElement, driver) => {
1163
+ driver = driver ?? config().driver;
1164
+ const actions = driver.actions({ async: true });
1165
+ await actions.move({ origin: webElement }).click().perform();
1166
+ };
1167
+
740
1168
  // src/pageObjects/pages/LoginPage.ts
741
1169
  var LOGIN_PATH = "/login";
742
1170
  var LoginPage = class extends AbstractPage {
743
1171
  usernameField = byTestId("loginField", "input");
744
1172
  passwordField = byTestId("passwordField", "input");
745
1173
  loginButton = byTestId("loginButton");
1174
+ async locate() {
1175
+ await super.locate();
1176
+ const login = await this.driver.findElement(this.usernameField);
1177
+ await waitElementIsVisible({
1178
+ element: login,
1179
+ driver: this.driver
1180
+ });
1181
+ }
746
1182
  get path() {
747
1183
  return LOGIN_PATH;
748
1184
  }
@@ -753,7 +1189,7 @@ var LoginPage = class extends AbstractPage {
753
1189
  await this.driver.findElement(this.passwordField).sendKeys(password);
754
1190
  }
755
1191
  async clickLoginButton() {
756
- await this.driver.findElement(this.loginButton).click();
1192
+ await click(await this.driver.findElement(this.loginButton), this.driver);
757
1193
  }
758
1194
  async loginWith(username, password) {
759
1195
  await this.enterUsername(username);
@@ -775,9 +1211,11 @@ var LoginPage = class extends AbstractPage {
775
1211
  SubsystemsPanel,
776
1212
  TextEditor,
777
1213
  byTestId,
1214
+ click,
778
1215
  config,
779
1216
  createDriver,
780
1217
  editorFactory,
781
1218
  elementIsNotPresent,
782
- signIn
1219
+ signIn,
1220
+ waitElementIsVisible
783
1221
  });