d5-testing-library 2.0.0-alpha.1 → 2.0.0-alpha.10

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.
@@ -76,11 +76,14 @@ var init_AbstractElement = __esm({
76
76
  joinRelativeCssSelectors(selectors) {
77
77
  return selectors.join(" ");
78
78
  }
79
- getLocator() {
79
+ syncLocator() {
80
80
  return this.page.locator(`css=${this.getFullCssSelector()}`);
81
81
  }
82
+ getLocator() {
83
+ return this.syncLocator();
84
+ }
82
85
  async getAttribute(attr) {
83
- const locator = this.getLocator();
86
+ const locator = this.syncLocator();
84
87
  if (await locator.isVisible()) {
85
88
  return locator.getAttribute(attr);
86
89
  }
@@ -329,7 +332,7 @@ var init_AbstractEditor = __esm({
329
332
  async setInputValue(value) {
330
333
  const input = this.getInputLocator();
331
334
  await input.click();
332
- await input.fill(value + "");
335
+ await input.pressSequentially(value + "");
333
336
  await input.press("Enter");
334
337
  }
335
338
  async clear() {
@@ -403,6 +406,7 @@ var init_types = __esm({
403
406
  ControlClass2["BooleanSelector"] = "buttons-group";
404
407
  ControlClass2["ButtonGroup"] = "button-group-field";
405
408
  ControlClass2["HeaderFilterDictionary"] = "header-filter-dictionary";
409
+ ControlClass2["FileUploader"] = "file-uploader";
406
410
  return ControlClass2;
407
411
  })(ControlClass || {});
408
412
  }
@@ -453,7 +457,7 @@ var init_xpathHelper = __esm({
453
457
  "use strict";
454
458
  XPathHelper = class {
455
459
  static getClassAndValueXpath(cssClass, value) {
456
- return `div[contains(@class, '${cssClass}') and contains(.,'${value}')]`;
460
+ return `div[contains(@class, '${cssClass}') and .='${value}']`;
457
461
  }
458
462
  };
459
463
  }
@@ -552,7 +556,7 @@ var init_SelectBoxEditor = __esm({
552
556
  const inputEl = this.getInputLocator();
553
557
  await inputEl.click();
554
558
  try {
555
- await inputEl.fill(value);
559
+ await inputEl.pressSequentially(value + "");
556
560
  await inputEl.press("Enter");
557
561
  await dropDownOptionClick(value, this.page, false);
558
562
  } catch {
@@ -655,12 +659,12 @@ var init_TagBox = __esm({
655
659
  try {
656
660
  await dropDownOptionClick(value, this.page, false);
657
661
  } catch {
658
- await input.fill(value);
662
+ await input.pressSequentially(value + "");
659
663
  await dropDownOptionClick(value, this.page, true);
660
664
  }
661
665
  await this.waitOptionSelection(value);
662
666
  if (value !== lastElement) {
663
- await input.fill("");
667
+ await input.pressSequentially("");
664
668
  }
665
669
  }
666
670
  async confirmSelection() {
@@ -1057,7 +1061,6 @@ var init_SingleDateEditor = __esm({
1057
1061
  init_BaseDateEditor();
1058
1062
  init_utils();
1059
1063
  init_ClearButtonStrategy();
1060
- init_src();
1061
1064
  SingleDateEditor = class extends BaseDateEditor {
1062
1065
  constructor(parentCssSelector, elementCssSelector, page) {
1063
1066
  super(parentCssSelector, elementCssSelector, page);
@@ -1080,11 +1083,7 @@ var init_SingleDateEditor = __esm({
1080
1083
  }
1081
1084
  async setValue(value) {
1082
1085
  if (value instanceof Date) {
1083
- await wait(async () => {
1084
- await this.setDateValue(value);
1085
- const updatedValue = await this.getValue();
1086
- return updatedValue.toDateString() === value.toDateString();
1087
- }, this.page);
1086
+ await this.setDateValue(value);
1088
1087
  } else {
1089
1088
  throw new Error(`Value is incorrect. It should be a Date object`);
1090
1089
  }
@@ -1180,7 +1179,7 @@ var init_DateEditor = __esm({
1180
1179
  });
1181
1180
 
1182
1181
  // src/pageObjects/elements/Editors/HeaderFilterDictionary.ts
1183
- var TAGS_SELECTOR2, DX_TAG_CONTAINER2, DX_TEXTEDITOR_EMPTY3, CUSTOM_OPTION_LIST_SELECTOR, SELECT_BOX_OPTION_TEXT_CONTAINER_CLASS2, TAG_BOX_LIST_ITEM_SELECTOR, combinedOptionHeaderFilterDictValueXpath, dropDownHeaderFilterDictionaryOptionClick, HeaderFilterDictionary;
1182
+ var TAGS_SELECTOR2, DX_TAG_CONTAINER2, DX_TEXTEDITOR_EMPTY3, CUSTOM_OPTION_LIST_SELECTOR, SELECT_BOX_OPTION_TEXT_CONTAINER_CLASS2, TAG_BOX_LIST_ITEM_SELECTOR, dropDownHeaderFilterDictionaryOptionClick, HeaderFilterDictionary;
1184
1183
  var init_HeaderFilterDictionary = __esm({
1185
1184
  "src/pageObjects/elements/Editors/HeaderFilterDictionary.ts"() {
1186
1185
  "use strict";
@@ -1193,12 +1192,11 @@ var init_HeaderFilterDictionary = __esm({
1193
1192
  CUSTOM_OPTION_LIST_SELECTOR = "custom-list";
1194
1193
  SELECT_BOX_OPTION_TEXT_CONTAINER_CLASS2 = "d5-multi-select-item";
1195
1194
  TAG_BOX_LIST_ITEM_SELECTOR = '.dx-overlay-wrapper .dx-list-item[aria-selected="true"]';
1196
- combinedOptionHeaderFilterDictValueXpath = (value) => `//div[contains(@class, '${SELECT_BOX_OPTION_TEXT_CONTAINER_CLASS2}') and contains(text(), '${value}')]`;
1197
1195
  dropDownHeaderFilterDictionaryOptionClick = async (optionValue, page, dataTEST = "", delay = true) => {
1198
1196
  const calendarPopoverSelector = byTestIdCssSelector(`${dataTEST}`);
1199
1197
  const calendarPopover = page.locator(calendarPopoverSelector);
1200
1198
  const customOptionList = calendarPopover.locator(`.${CUSTOM_OPTION_LIST_SELECTOR}`);
1201
- const optElement = customOptionList.locator(`xpath=${combinedOptionHeaderFilterDictValueXpath(optionValue)}`);
1199
+ const optElement = customOptionList.locator(`.${SELECT_BOX_OPTION_TEXT_CONTAINER_CLASS2}`, { hasText: optionValue });
1202
1200
  try {
1203
1201
  await optElement.waitFor({ state: "visible", timeout: delay ? void 0 : 600 });
1204
1202
  await optElement.click();
@@ -1222,28 +1220,28 @@ var init_HeaderFilterDictionary = __esm({
1222
1220
  await this.selectOption(inputEl, value, lastElement);
1223
1221
  }
1224
1222
  }
1225
- async waitOptionSelection() {
1226
- const selectedItem = this.page.locator(TAG_BOX_LIST_ITEM_SELECTOR);
1227
- await selectedItem.waitFor({ state: "visible" });
1223
+ async waitOptionSelection(value) {
1224
+ await this.page.locator(`${TAG_BOX_LIST_ITEM_SELECTOR}`).filter({ hasText: value }).first().waitFor({ state: "visible" });
1228
1225
  }
1229
1226
  async selectOption(inputEl, value, lastElement) {
1230
1227
  try {
1231
- await inputEl.fill(value);
1228
+ await inputEl.pressSequentially(value + "");
1232
1229
  const dataTEST = await this.getDataTestId();
1233
1230
  await dropDownHeaderFilterDictionaryOptionClick(value, this.page, dataTEST);
1234
1231
  if (value !== lastElement) {
1235
- await inputEl.fill("");
1232
+ await inputEl.pressSequentially("");
1236
1233
  }
1237
1234
  } catch (error) {
1238
1235
  throw error;
1239
1236
  }
1240
- await this.waitOptionSelection();
1237
+ await this.waitOptionSelection(value);
1241
1238
  }
1242
1239
  async getHeaderFilterDictionaryInputValue() {
1243
1240
  if (await this.isHeaderFilterDictionaryEmpty()) {
1244
1241
  return [];
1245
1242
  }
1246
1243
  const tags = this.page.locator(`${this.getFullCssSelector()} .${DX_TAG_CONTAINER2} ${TAGS_SELECTOR2}`);
1244
+ await tags.first().waitFor({ state: "visible" });
1247
1245
  const count = await tags.count();
1248
1246
  const values = [];
1249
1247
  for (let i = 0; i < count; i++) {
@@ -1269,6 +1267,125 @@ var init_HeaderFilterDictionary = __esm({
1269
1267
  }
1270
1268
  });
1271
1269
 
1270
+ // src/pageObjects/elements/Editors/FileUploader.ts
1271
+ var FILE_NAME_SELECTOR, FILE_SIZE_ATTRIBUTE, FILE_DATE_ATTRIBUTE, FILE_TYPE_ATTRIBUTE, SELECT_FILE_BUTTON_SELECTOR, DOWNLOAD_FILE_BUTTON_SELECTOR, METADATA_SELECTOR, CLEAR_BUTTON_SELECTOR, FileUploader;
1272
+ var init_FileUploader = __esm({
1273
+ "src/pageObjects/elements/Editors/FileUploader.ts"() {
1274
+ "use strict";
1275
+ init_AbstractElementWithParent();
1276
+ FILE_NAME_SELECTOR = ".file-text";
1277
+ FILE_SIZE_ATTRIBUTE = "data-filesize";
1278
+ FILE_DATE_ATTRIBUTE = "data-moddate";
1279
+ FILE_TYPE_ATTRIBUTE = "data-filetype";
1280
+ SELECT_FILE_BUTTON_SELECTOR = ".select-file-button";
1281
+ DOWNLOAD_FILE_BUTTON_SELECTOR = ".download-file-button";
1282
+ METADATA_SELECTOR = ".file-text-file-info-wrapper";
1283
+ CLEAR_BUTTON_SELECTOR = ".clear-button";
1284
+ FileUploader = class extends AbstractElementWithParent {
1285
+ constructor(parentCssSelector, elementCssSelector, page) {
1286
+ super(parentCssSelector, elementCssSelector, page);
1287
+ }
1288
+ /**
1289
+ * Returns the metadata container element for the uploaded file.
1290
+ * Returns null when metadata is not rendered or not visible.
1291
+ */
1292
+ async getMedataElement() {
1293
+ const root = this.getFullCssSelector();
1294
+ const locator = this.page.locator(`${root} ${METADATA_SELECTOR}`);
1295
+ if (!await locator.isVisible()) return null;
1296
+ return locator;
1297
+ }
1298
+ /**
1299
+ * Returns the name of the uploaded file, or null if not present.
1300
+ */
1301
+ async getFileName() {
1302
+ const root = this.getFullCssSelector();
1303
+ const locator = this.page.locator(`${root} ${FILE_NAME_SELECTOR}`);
1304
+ if (!await locator.isVisible()) return null;
1305
+ const text = await locator.innerText();
1306
+ return text?.trim() || null;
1307
+ }
1308
+ /**
1309
+ * Returns the size of the uploaded file, or null if not present.
1310
+ */
1311
+ async getFileSize() {
1312
+ const element = await this.getMedataElement();
1313
+ if (!element) return null;
1314
+ const result = (await element.getAttribute(FILE_SIZE_ATTRIBUTE))?.trim();
1315
+ return isNaN(+result) ? null : +result;
1316
+ }
1317
+ /**
1318
+ * Returns the upload/modification date of the file, or null if not present.
1319
+ */
1320
+ async getFileDate() {
1321
+ const element = await this.getMedataElement();
1322
+ if (!element) return null;
1323
+ const fileModDate = (await element.getAttribute(FILE_DATE_ATTRIBUTE))?.trim();
1324
+ return fileModDate ? new Date(fileModDate) : null;
1325
+ }
1326
+ /**
1327
+ * Returns the MIME/content type of the uploaded file from metadata.
1328
+ * Returns null when the type attribute is absent or empty.
1329
+ */
1330
+ async getFileType() {
1331
+ const element = await this.getMedataElement();
1332
+ if (!element) return null;
1333
+ const fileType = (await element.getAttribute(FILE_TYPE_ATTRIBUTE))?.trim();
1334
+ return fileType || null;
1335
+ }
1336
+ /**
1337
+ * Returns current file information: name, size and upload/modification date.
1338
+ * Returns null if no file is uploaded.
1339
+ */
1340
+ async getValue() {
1341
+ const name = await this.getFileName();
1342
+ const size = await this.getFileSize();
1343
+ const date = await this.getFileDate();
1344
+ const type2 = await this.getFileType();
1345
+ if (!name && !size && !date && !type2) return null;
1346
+ return { FileName: name, FileSize: size, FileType: type2, ModDate: date };
1347
+ }
1348
+ /**
1349
+ * Uploads a file by clicking the select-file-button and providing the file path.
1350
+ * @param filePath - Absolute or relative path to the file on disk.
1351
+ */
1352
+ async setValue(filePath) {
1353
+ const root = this.getFullCssSelector();
1354
+ const fileChooserPromise = this.page.waitForEvent("filechooser");
1355
+ await this.page.locator(`${root} ${SELECT_FILE_BUTTON_SELECTOR}`).click();
1356
+ const fileChooser = await fileChooserPromise;
1357
+ await fileChooser.setFiles(filePath);
1358
+ }
1359
+ /**
1360
+ * Downloads the currently uploaded file by clicking the download-file-button.
1361
+ * Returns the suggested file name of the downloaded file.
1362
+ */
1363
+ async download() {
1364
+ const root = this.getFullCssSelector();
1365
+ const buttonLocator = this.page.locator(
1366
+ `${root} ${DOWNLOAD_FILE_BUTTON_SELECTOR}`
1367
+ );
1368
+ if (await buttonLocator.isHidden()) return "";
1369
+ const downloadPromise = this.page.waitForEvent("download");
1370
+ await buttonLocator.click();
1371
+ const download = await downloadPromise;
1372
+ return download.suggestedFilename();
1373
+ }
1374
+ /**
1375
+ * Removes the currently uploaded file by clicking the delete-file-button.
1376
+ * Does nothing if no file is uploaded.
1377
+ */
1378
+ async clear() {
1379
+ if (await this.getValue() === null) return;
1380
+ const root = this.getFullCssSelector();
1381
+ const deleteButton = this.page.locator(`${root} ${CLEAR_BUTTON_SELECTOR}`);
1382
+ if (await deleteButton.isHidden()) return;
1383
+ await deleteButton.click();
1384
+ }
1385
+ };
1386
+ }
1387
+ });
1388
+
1272
1389
  // src/pageObjects/elements/Editors/editorFactory.ts
1273
1390
  var editorFactory;
1274
1391
  var init_editorFactory = __esm({
@@ -1286,6 +1403,7 @@ var init_editorFactory = __esm({
1286
1403
  init_ButtonGroup();
1287
1404
  init_DateEditor();
1288
1405
  init_HeaderFilterDictionary();
1406
+ init_FileUploader();
1289
1407
  editorFactory = async (parentCssSelector, elementCssSelector, page) => {
1290
1408
  const elementInstance = new AbstractFormElement(parentCssSelector, elementCssSelector, page);
1291
1409
  const classString = await elementInstance.getClasses();
@@ -1306,6 +1424,8 @@ var init_editorFactory = __esm({
1306
1424
  return new ButtonGroup(parentCssSelector, elementCssSelector, page);
1307
1425
  if (classString.includes("header-filter-dictionary" /* HeaderFilterDictionary */))
1308
1426
  return new HeaderFilterDictionary(parentCssSelector, elementCssSelector, page);
1427
+ if (classString.includes("file-uploader" /* FileUploader */))
1428
+ return new FileUploader(parentCssSelector, elementCssSelector, page);
1309
1429
  throw new Error("Unknown type of editor. Please write ticket to the support");
1310
1430
  };
1311
1431
  }
@@ -1385,6 +1505,15 @@ function createSubSystemTestId(itemName) {
1385
1505
  function createEditorButtonTestId(itemName) {
1386
1506
  return `${"editor-button" /* EDITOR_BUTTON */}-${itemName}`;
1387
1507
  }
1508
+ function createLabelFieldTestId(formName, itemName) {
1509
+ return `${"label" /* LABEL */}-${createFieldTestId(formName, itemName)}`;
1510
+ }
1511
+ function createLabelDocFilterTestId(formName, itemName) {
1512
+ return `${"label" /* LABEL */}-${createDocFilterTestId(formName, itemName)}`;
1513
+ }
1514
+ function createLabelLayoutFilterTestId(formName, itemName) {
1515
+ return `${"label" /* LABEL */}-${createLayoutFilterTestId(formName, itemName)}`;
1516
+ }
1388
1517
  var init_createDataTestId = __esm({
1389
1518
  "src/utils/createDataTestId.ts"() {
1390
1519
  "use strict";
@@ -1863,7 +1992,7 @@ var init_AbstractForm = __esm({
1863
1992
  async exists() {
1864
1993
  try {
1865
1994
  const container = this.page.locator(this.containerCssSelector);
1866
- await waitElementIsVisible_default({ locator: container });
1995
+ await waitElementIsVisible_default({ locator: container, timeout: 3e3 });
1867
1996
  return true;
1868
1997
  } catch (error) {
1869
1998
  return false;
@@ -1996,14 +2125,14 @@ var init_Cell = __esm({
1996
2125
  }
1997
2126
  return getDate(value);
1998
2127
  }
1999
- async element() {
2128
+ async getLocator() {
2000
2129
  if (this._index == null) {
2001
2130
  this.index = await this.getCellIndex();
2002
2131
  }
2003
- return this.getLocator();
2132
+ return this.syncLocator();
2004
2133
  }
2005
2134
  async getValue() {
2006
- const cellEl = await this.element();
2135
+ const cellEl = await this.getLocator();
2007
2136
  const cellElClass = await cellEl.getAttribute("class") ?? "";
2008
2137
  if (cellElClass.includes("boolean-column" /* Boolean */)) return this.getBooleanValue(cellEl);
2009
2138
  if (cellElClass.includes("tags-column" /* Tags */)) return this.getTagsValue(cellEl);
@@ -2151,9 +2280,9 @@ var init_Column = __esm({
2151
2280
  constructor({ parentCssSelector, headerCssSelector, name, caption, index, page }) {
2152
2281
  super(parentCssSelector, "", page);
2153
2282
  this._name = name;
2283
+ this.headerCssSelector = headerCssSelector;
2154
2284
  this.index = index;
2155
2285
  this._caption = caption;
2156
- this.headerCssSelector = headerCssSelector;
2157
2286
  }
2158
2287
  set index(index) {
2159
2288
  this._index = index;
@@ -2162,9 +2291,9 @@ var init_Column = __esm({
2162
2291
  get index() {
2163
2292
  return this._index;
2164
2293
  }
2165
- async element() {
2294
+ async getLocator() {
2166
2295
  await this.getIndex();
2167
- return this.getLocator();
2296
+ return this.syncLocator();
2168
2297
  }
2169
2298
  async getColumnHeaderIndexBy(cssSelector) {
2170
2299
  const column = this.page.locator(`${this.parentCssSelector} ${this.headerCssSelector} ${cssSelector}`);
@@ -2177,7 +2306,7 @@ var init_Column = __esm({
2177
2306
  return this.getColumnHeaderIndexBy(getColumnByNameCssSelector(this._name));
2178
2307
  }
2179
2308
  async caption() {
2180
- const el = await this.element();
2309
+ const el = await this.getLocator();
2181
2310
  const text = await el.innerText();
2182
2311
  return text?.trim() ?? null;
2183
2312
  }
@@ -2230,6 +2359,25 @@ var init_EditorButton = __esm({
2230
2359
  }
2231
2360
  });
2232
2361
 
2362
+ // src/pageObjects/elements/EditorTitle.ts
2363
+ var CAPTION_TEXT_CLASS, EditorTitle;
2364
+ var init_EditorTitle = __esm({
2365
+ "src/pageObjects/elements/EditorTitle.ts"() {
2366
+ "use strict";
2367
+ init_AbstractElementWithParent();
2368
+ CAPTION_TEXT_CLASS = " .caption-text";
2369
+ EditorTitle = class extends AbstractElementWithParent {
2370
+ constructor({ parentCssSelector, elementCssSelector, page }) {
2371
+ super(parentCssSelector, elementCssSelector, page);
2372
+ }
2373
+ async getTitle() {
2374
+ const captionElement = this.getLocator().locator(CAPTION_TEXT_CLASS);
2375
+ return captionElement.innerText();
2376
+ }
2377
+ };
2378
+ }
2379
+ });
2380
+
2233
2381
  // src/pageObjects/elements/AbstractEditor.ts
2234
2382
  var AbstractEditor;
2235
2383
  var init_AbstractEditor2 = __esm({
@@ -2238,10 +2386,13 @@ var init_AbstractEditor2 = __esm({
2238
2386
  init_AbstractElementWithParent();
2239
2387
  init_Editors();
2240
2388
  init_EditorButton();
2389
+ init_EditorTitle();
2241
2390
  AbstractEditor = class extends AbstractElementWithParent {
2242
2391
  editor;
2243
- constructor(parentCssSelector, elementCssSelector, page) {
2392
+ _titleCssSelector;
2393
+ constructor({ parentCssSelector, elementCssSelector, titleCssSelector, page }) {
2244
2394
  super(parentCssSelector, elementCssSelector, page);
2395
+ this._titleCssSelector = titleCssSelector;
2245
2396
  }
2246
2397
  async initEditor() {
2247
2398
  if (!this.editor) {
@@ -2337,6 +2488,32 @@ var init_AbstractEditor2 = __esm({
2337
2488
  button(name) {
2338
2489
  return new EditorButton(this.getFullCssSelector(), name, this.page);
2339
2490
  }
2491
+ /**
2492
+ * Повертає загловок едітора
2493
+ * @example
2494
+ * expect(await formEdit.field('NumberFld').title()).toBe('Title1');
2495
+ * expect(await formEdit.field('TextFld').title()).toBe('Title2`);
2496
+ */
2497
+ async title() {
2498
+ return new EditorTitle({
2499
+ elementCssSelector: this._titleCssSelector,
2500
+ parentCssSelector: this.parentCssSelector,
2501
+ page: this.page
2502
+ }).getTitle();
2503
+ }
2504
+ /**
2505
+ * Вивантажує файл і повертає назву файла. Доступний тільки для поля FileUploader.
2506
+ * @example
2507
+ * const fileName = await formEdit.field('FileField').downloadFile();
2508
+ * expect(filename()).toBe('file.pdf');
2509
+ */
2510
+ async download() {
2511
+ await this.initEditor();
2512
+ if (!this.editor.download) {
2513
+ throw new Error("download is only available for the FileUploader field type.");
2514
+ }
2515
+ return this.editor.download();
2516
+ }
2340
2517
  };
2341
2518
  }
2342
2519
  });
@@ -2351,7 +2528,12 @@ var init_FormField = __esm({
2351
2528
  init_AbstractEditor2();
2352
2529
  FormField = class extends AbstractEditor {
2353
2530
  constructor(parentCssSelector, formName, name, page) {
2354
- super(parentCssSelector, byTestIdCssSelector(createFieldTestId(formName, name)), page);
2531
+ super({
2532
+ parentCssSelector,
2533
+ elementCssSelector: byTestIdCssSelector(createFieldTestId(formName, name)),
2534
+ titleCssSelector: byTestIdCssSelector(createLabelFieldTestId(formName, name)),
2535
+ page
2536
+ });
2355
2537
  }
2356
2538
  };
2357
2539
  }
@@ -2525,6 +2707,7 @@ var init_Table = __esm({
2525
2707
  this.getRowByIndex(index);
2526
2708
  return new Column({
2527
2709
  parentCssSelector: this.getFullCssSelector(),
2710
+ headerCssSelector: this.headerCssSelector,
2528
2711
  page: this.page,
2529
2712
  index
2530
2713
  });
@@ -2637,7 +2820,7 @@ var init_Table = __esm({
2637
2820
  [row] = await this.getSelectedRows();
2638
2821
  }
2639
2822
  if (!row) throw new Error("No row available row for context menu");
2640
- const cellElement = await row.getCell(fieldName).element();
2823
+ const cellElement = await row.getCell(fieldName).getLocator();
2641
2824
  await cellElement.click({ button: "right" });
2642
2825
  });
2643
2826
  }
@@ -2696,10 +2879,14 @@ var init_Table = __esm({
2696
2879
  * console.log('Таблиця містить 5 рядків');
2697
2880
  */
2698
2881
  async waitRowsCount(count) {
2699
- await wait(async () => {
2700
- const rowsCount = await this.getRowElements().count();
2701
- return rowsCount === count;
2702
- }, this.page, `Rows count not equal: ${count}`);
2882
+ await wait(
2883
+ async () => {
2884
+ const rowsCount = await this.getRowElements().count();
2885
+ return rowsCount === count;
2886
+ },
2887
+ this.page,
2888
+ `Rows count not equal: ${count}`
2889
+ );
2703
2890
  }
2704
2891
  };
2705
2892
  }
@@ -2951,6 +3138,7 @@ var init_ToolbarItem = __esm({
2951
3138
  init_locators();
2952
3139
  init_ToolbarMenu();
2953
3140
  init_waitElementIsVisible();
3141
+ init_wait();
2954
3142
  GROUP_BUTTON_CLASS = "toolbar-group-button";
2955
3143
  toolbarItemSelector = (form, name) => byTestIdCssSelector(`ftb-${form}-${name}`);
2956
3144
  ToolbarItem = class extends AbstractElementWithParent {
@@ -2974,7 +3162,10 @@ var init_ToolbarItem = __esm({
2974
3162
  async click() {
2975
3163
  try {
2976
3164
  await this.init();
2977
- await waitElementIsVisible_default({ locator: this._button.getLocator() });
3165
+ await wait(async () => {
3166
+ const element = this._button.getLocator();
3167
+ return await element.isEnabled() && await element.isVisible();
3168
+ }, this.page);
2978
3169
  await this._button.click();
2979
3170
  } catch (error) {
2980
3171
  throw new Error(`Cannot click on toolbar item: ${this._itemName}`);
@@ -3149,7 +3340,12 @@ var init_FormFilterField = __esm({
3149
3340
  formName;
3150
3341
  name;
3151
3342
  constructor(parentCssSelector, formName, name, page) {
3152
- super(parentCssSelector, byTestIdCssSelector(createLayoutFilterTestId(formName, name)), page);
3343
+ super({
3344
+ parentCssSelector,
3345
+ elementCssSelector: byTestIdCssSelector(createLayoutFilterTestId(formName, name)),
3346
+ titleCssSelector: byTestIdCssSelector(createLabelLayoutFilterTestId(formName, name)),
3347
+ page
3348
+ });
3153
3349
  this.formName = formName;
3154
3350
  this.name = name;
3155
3351
  }
@@ -3263,7 +3459,12 @@ var init_PanelFilterField = __esm({
3263
3459
  formName;
3264
3460
  name;
3265
3461
  constructor(parentCssSelector, formName, name, page) {
3266
- super(parentCssSelector, byTestIdCssSelector(createDocFilterTestId(formName, name)), page);
3462
+ super({
3463
+ parentCssSelector,
3464
+ elementCssSelector: byTestIdCssSelector(createDocFilterTestId(formName, name)),
3465
+ titleCssSelector: byTestIdCssSelector(createLabelDocFilterTestId(formName, name)),
3466
+ page
3467
+ });
3267
3468
  this.formName = formName;
3268
3469
  this.name = name;
3269
3470
  }
@@ -3431,7 +3632,12 @@ var init_HeaderFilterField = __esm({
3431
3632
  init_AbstractEditor2();
3432
3633
  HeaderFilterField = class extends AbstractEditor {
3433
3634
  constructor(parentCssSelector, formName, name, page) {
3434
- super(parentCssSelector, byTestIdCssSelector(createHeaderFilterTestId(formName, name)), page);
3635
+ super({
3636
+ parentCssSelector,
3637
+ elementCssSelector: byTestIdCssSelector(createHeaderFilterTestId(formName, name)),
3638
+ titleCssSelector: "",
3639
+ page
3640
+ });
3435
3641
  }
3436
3642
  async getValue() {
3437
3643
  return super.getValue();
@@ -3493,7 +3699,7 @@ var init_ColumnFilter = __esm({
3493
3699
  await this.getCancelButton().click();
3494
3700
  }
3495
3701
  async initFilter() {
3496
- const columnEl = await this._column.element();
3702
+ const columnEl = await this._column.getLocator();
3497
3703
  await columnEl.hover();
3498
3704
  const headerFilterIcon = columnEl.locator(HEADER_FILTER_ICON);
3499
3705
  await headerFilterIcon.waitFor({ state: "visible" });
@@ -1,4 +1,4 @@
1
- import { Page, Locator } from '@playwright/test';
1
+ import { Locator, Page } from '@playwright/test';
2
2
  import * as playwright_core from 'playwright-core';
3
3
 
4
4
  type IConfig = {
@@ -9,13 +9,14 @@ type IConfig = {
9
9
  };
10
10
  declare function config(cfg?: IConfig): IConfig;
11
11
 
12
- declare abstract class AbstractElement {
12
+ declare abstract class AbstractElement<TLocator extends Locator | Promise<Locator> = Locator> {
13
13
  page: Page;
14
14
  protected elementCssSelector: string;
15
15
  protected constructor(page: Page, elementCssSelector?: string);
16
16
  protected getFullCssSelector(): string;
17
17
  protected joinRelativeCssSelectors(selectors: string[]): string;
18
- getLocator(): Locator;
18
+ protected syncLocator(): Locator;
19
+ getLocator(): TLocator;
19
20
  getAttribute(attr: string): Promise<string>;
20
21
  getClasses(): Promise<string>;
21
22
  getDataTestId(): Promise<string>;
@@ -95,6 +96,7 @@ interface Editor {
95
96
  clear?(): Promise<void>;
96
97
  isReadonly?(): Promise<boolean>;
97
98
  isDisabled?(): Promise<boolean>;
99
+ download?(): Promise<string>;
98
100
  }
99
101
  declare enum ControlClass {
100
102
  Text = "text-control",
@@ -108,14 +110,15 @@ declare enum ControlClass {
108
110
  SingleDateRange = "single-date-range-control",
109
111
  BooleanSelector = "buttons-group",
110
112
  ButtonGroup = "button-group-field",
111
- HeaderFilterDictionary = "header-filter-dictionary"
113
+ HeaderFilterDictionary = "header-filter-dictionary",
114
+ FileUploader = "file-uploader"
112
115
  }
113
116
 
114
117
  interface ClearStrategy {
115
118
  clear(editor: Editor): Promise<void>;
116
119
  }
117
120
 
118
- declare abstract class AbstractElementWithParent extends AbstractElement {
121
+ declare abstract class AbstractElementWithParent<TLocator extends Locator | Promise<Locator> = Locator> extends AbstractElement<TLocator> {
119
122
  protected parentCssSelector: string;
120
123
  constructor(parentCssSelector: string, elementCssSelector: string, page: Page);
121
124
  protected getFullCssSelector(): string;
@@ -347,7 +350,13 @@ declare class EditorButton extends AbstractElementWithParent {
347
350
 
348
351
  declare class AbstractEditor extends AbstractElementWithParent implements Editor {
349
352
  protected editor: Editor;
350
- constructor(parentCssSelector: string, elementCssSelector: string, page: Page);
353
+ protected _titleCssSelector: string;
354
+ constructor({ parentCssSelector, elementCssSelector, titleCssSelector, page }: {
355
+ parentCssSelector: string;
356
+ elementCssSelector: string;
357
+ titleCssSelector: string;
358
+ page: Page;
359
+ });
351
360
  protected initEditor(): Promise<void>;
352
361
  /**
353
362
  * Возвращает значение в поле
@@ -404,6 +413,20 @@ declare class AbstractEditor extends AbstractElementWithParent implements Editor
404
413
  * await form.field('Name').button('editorButton).click()
405
414
  */
406
415
  button(name: string): EditorButton;
416
+ /**
417
+ * Повертає загловок едітора
418
+ * @example
419
+ * expect(await formEdit.field('NumberFld').title()).toBe('Title1');
420
+ * expect(await formEdit.field('TextFld').title()).toBe('Title2`);
421
+ */
422
+ title(): Promise<string>;
423
+ /**
424
+ * Вивантажує файл і повертає назву файла. Доступний тільки для поля FileUploader.
425
+ * @example
426
+ * const fileName = await formEdit.field('FileField').downloadFile();
427
+ * expect(filename()).toBe('file.pdf');
428
+ */
429
+ download(): Promise<string>;
407
430
  }
408
431
 
409
432
  declare class FormField extends AbstractEditor {
@@ -500,7 +523,7 @@ declare class ToolbarItem extends AbstractElementWithParent implements Button {
500
523
  get menu(): ToolbarMenu;
501
524
  }
502
525
 
503
- declare class Cell extends AbstractElementWithParent {
526
+ declare class Cell extends AbstractElementWithParent<Promise<Locator>> {
504
527
  private _index;
505
528
  private readonly getCellIndex;
506
529
  constructor(parentCssSelector: string, getCellIndex: () => Promise<number>, page: Page, index?: number);
@@ -509,7 +532,7 @@ declare class Cell extends AbstractElementWithParent {
509
532
  private getTagsValue;
510
533
  private getNumberValue;
511
534
  private getDateValue;
512
- element(): Promise<Locator>;
535
+ getLocator(): Promise<Locator>;
513
536
  getValue(): Promise<string | number | 1 | 0 | string[] | Date | Date[] | null>;
514
537
  getIndex(): number;
515
538
  }
@@ -522,7 +545,7 @@ interface ConstructorParams$1 {
522
545
  index?: number;
523
546
  page: Page;
524
547
  }
525
- declare class Column extends AbstractElementWithParent {
548
+ declare class Column extends AbstractElementWithParent<Promise<Locator>> {
526
549
  private _index;
527
550
  private _caption;
528
551
  private _name;
@@ -530,7 +553,7 @@ declare class Column extends AbstractElementWithParent {
530
553
  constructor({ parentCssSelector, headerCssSelector, name, caption, index, page }: ConstructorParams$1);
531
554
  private set index(value);
532
555
  private get index();
533
- element(): Promise<Locator>;
556
+ getLocator(): Promise<Locator>;
534
557
  private getColumnHeaderIndexBy;
535
558
  private getIndexByCaption;
536
559
  private getIndexByName;