pne-ui 4.3.0-rc.0 → 4.3.0-rc.2

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.
Files changed (47) hide show
  1. package/README.md +640 -2
  2. package/cjs/component/search-ui/SearchUI.d.ts +52 -19
  3. package/cjs/component/search-ui/SearchUI.js +80 -8
  4. package/cjs/component/search-ui/SearchUI.js.map +1 -1
  5. package/cjs/component/table/AbstractTable.d.ts +5 -0
  6. package/cjs/component/table/AbstractTable.js +16 -2
  7. package/cjs/component/table/AbstractTable.js.map +1 -1
  8. package/cjs/component/table/PneTablePaginationActions.js +12 -18
  9. package/cjs/component/table/PneTablePaginationActions.js.map +1 -1
  10. package/cjs/component/table/PneTableViewSelector.d.ts +30 -0
  11. package/cjs/component/table/PneTableViewSelector.js +81 -0
  12. package/cjs/component/table/PneTableViewSelector.js.map +1 -0
  13. package/cjs/component/table/useDelayedLoading.d.ts +1 -1
  14. package/cjs/component/table/useDelayedLoading.js +48 -20
  15. package/cjs/component/table/useDelayedLoading.js.map +1 -1
  16. package/cjs/component/table/useTable.d.ts +4 -0
  17. package/cjs/component/table/useTable.js +116 -32
  18. package/cjs/component/table/useTable.js.map +1 -1
  19. package/cjs/exports/search.d.ts +1 -1
  20. package/cjs/exports/search.js.map +1 -1
  21. package/cjs/exports/table.d.ts +1 -0
  22. package/cjs/exports/table.js +3 -1
  23. package/cjs/exports/table.js.map +1 -1
  24. package/docs/selenium-locators.md +4 -636
  25. package/esm/component/search-ui/SearchUI.d.ts +52 -19
  26. package/esm/component/search-ui/SearchUI.js +81 -9
  27. package/esm/component/search-ui/SearchUI.js.map +1 -1
  28. package/esm/component/table/AbstractTable.d.ts +5 -0
  29. package/esm/component/table/AbstractTable.js +16 -2
  30. package/esm/component/table/AbstractTable.js.map +1 -1
  31. package/esm/component/table/PneTablePaginationActions.js +13 -19
  32. package/esm/component/table/PneTablePaginationActions.js.map +1 -1
  33. package/esm/component/table/PneTableViewSelector.d.ts +30 -0
  34. package/esm/component/table/PneTableViewSelector.js +77 -0
  35. package/esm/component/table/PneTableViewSelector.js.map +1 -0
  36. package/esm/component/table/useDelayedLoading.d.ts +1 -1
  37. package/esm/component/table/useDelayedLoading.js +49 -21
  38. package/esm/component/table/useDelayedLoading.js.map +1 -1
  39. package/esm/component/table/useTable.d.ts +4 -0
  40. package/esm/component/table/useTable.js +116 -32
  41. package/esm/component/table/useTable.js.map +1 -1
  42. package/esm/exports/search.d.ts +1 -1
  43. package/esm/exports/search.js.map +1 -1
  44. package/esm/exports/table.d.ts +1 -0
  45. package/esm/exports/table.js +1 -0
  46. package/esm/exports/table.js.map +1 -1
  47. package/package.json +1 -2
package/README.md CHANGED
@@ -152,8 +152,646 @@ required textbox/text buttons и ищутся по role/name внутри scoped
152
152
  state. Не используйте MUI classes, SVG/path, DOM depth, array index, переведённый текст как технический ID или
153
153
  сгенерированный `aria-controls` ID.
154
154
 
155
- Полная Selenium-документация, включая матрицу всех 31 критериев, detached portals, date pickers, grouping,
156
- transaction session status и все девять multiget-панелей: [docs/selenium-locators.md](docs/selenium-locators.md).
155
+ ## Справочник Selenium-якорей `pne-ui`
156
+
157
+ Документ предназначен для тестировщиков, которые пишут Selenium-автотесты. Здесь перечислены готовые
158
+ стабильные якоря `PneTable`, `SearchUI`, `SearchUIFilters`, всех 31 типов критериев и вынесенных в portal
159
+ панелей. Искать нужный элемент по JSX, структуре MUI или случайным классам не требуется.
160
+
161
+ Примеры ниже используют CSS selectors, поддерживаемые обычным Selenium WebDriver. Это справочник публичного
162
+ DOM-контракта, а не руководство по реализации компонентов библиотеки.
163
+
164
+ ### Быстрый старт
165
+
166
+ Тест всегда проходит три уровня:
167
+
168
+ 1. Находит scope экземпляра компонента.
169
+ 2. Внутри scope находит смысловой slot/action/control.
170
+ 3. Читает состояние из native DOM или ARIA, а не из отдельной test-only копии.
171
+
172
+ Три служебных атрибута имеют разные назначения:
173
+
174
+ | Атрибут | Назначение |
175
+ |---|---|
176
+ | `data-autotest` | Стабильное имя slot/action/control |
177
+ | `data-autotest-value` | Scope экземпляра или raw domain/enum ID |
178
+ | `data-autotest-criterion` | Raw `CriterionTypeEnum` владельца portal-контента |
179
+
180
+ Короткая запись, используемая дальше в документе:
181
+
182
+ ```text
183
+ slot/value
184
+ = [data-autotest="<slot>"][data-autotest-value="<value>"]
185
+
186
+ portal/scope + criterion
187
+ = [data-autotest="<portal>"][data-autotest-value="<scope>"][data-autotest-criterion="<criterion>"]
188
+ ```
189
+
190
+ `<scope>` в примерах ниже — стабильное имя конкретного экземпляра компонента на странице, например `orders`.
191
+ У нескольких таблиц scopes обязаны различаться. SearchUI и связанная таблица результатов используют общий
192
+ scope.
193
+
194
+ `data-autotest-value` не является универсальным полем состояния. В зависимости от якоря это scope либо raw
195
+ domain/enum ID. Введённый текст и состояния `checked/disabled` Selenium читает из настоящего DOM-свойства или
196
+ ARIA.
197
+
198
+ #### Пример scoped lookup
199
+
200
+ ```java
201
+ WebElement filters = driver.findElement(By.cssSelector(
202
+ "[data-autotest='search-filters'][data-autotest-value='orders']"
203
+ ));
204
+
205
+ WebElement status = filters.findElement(By.cssSelector(
206
+ "[data-autotest='criterion'][data-autotest-value='STATUS']"
207
+ ));
208
+
209
+ WebElement enabled = status.findElement(By.cssSelector(
210
+ "[data-autotest='criterion-option'][data-autotest-value='ENABLED']"
211
+ ));
212
+
213
+ enabled.click();
214
+ assertEquals("true", enabled.getAttribute("aria-pressed"));
215
+ ```
216
+
217
+ Эти lookup-операции удобно инкапсулировать в Selenium Page/Component Object. Selenium штатно поддерживает
218
+ поиск от найденного `WebElement`, поэтому внутренний selector не обязан быть глобально уникальным на всей
219
+ странице.
220
+
221
+ ### Как читать состояние
222
+
223
+ | Состояние | Источник истины в Selenium/DOM |
224
+ |---|---|
225
+ | Enabled/disabled native control | `element.isEnabled()`; native `disabled` присутствует только у disabled |
226
+ | Enabled/disabled MUI control с `role="combobox"` | `aria-disabled="true"` у disabled; у enabled атрибут отсутствует |
227
+ | Checkbox/radio/switch на native input | `element.isSelected()` или DOM property `checked` |
228
+ | Custom switch/checkbox | `aria-checked` |
229
+ | Toggle button | `aria-pressed` |
230
+ | Option | `aria-selected` |
231
+ | Открыт/закрыт trigger | `aria-expanded`; связь с popup — текущее `aria-controls` |
232
+ | Loading | `aria-busy` |
233
+ | Активная сортировка | `aria-sort="ascending|descending"` на semantic `<th>` |
234
+ | Текущая страница semantic Pagination | `aria-current="page"` |
235
+ | Значение input | DOM property `value` |
236
+
237
+ Не ожидайте `disabled="false"`: `disabled` является boolean HTML attribute. Если он присутствует, control
238
+ отключён независимо от текстового значения атрибута.
239
+
240
+ ### `PneTable`
241
+
242
+ #### Scope экземпляра
243
+
244
+ ```css
245
+ [data-autotest="table"][data-autotest-value="orders"]
246
+ ```
247
+
248
+ `PneTable` может повторяться на одной странице, поэтому для нового page contract значение `<scope>` является
249
+ обязательной частью локатора и должно быть уникальным для каждой логической таблицы. Конкретное значение
250
+ задаёт интеграция страницы через `autoTestId`; оно должно быть зафиксировано в тестовых данных/Page Object.
251
+ Пример `orders` ниже иллюстративный. Не заменяйте scope заголовком, текущим переводом, порядковым номером или
252
+ именем WhiteLabel.
253
+
254
+ Технически legacy-страница ещё может отрендерить только `[data-autotest="table"]` без value. Такой selector
255
+ не различает несколько таблиц: для страницы, входящей в автоматизацию, отсутствие согласованного scope нужно
256
+ фиксировать как пробел page-level контракта.
257
+
258
+ Таблица результатов SearchUI получает тот же scope, что SearchUI:
259
+
260
+ ```css
261
+ [data-autotest="table"][data-autotest-value="orders"]
262
+ ```
263
+
264
+ #### Внутренние элементы
265
+
266
+ | Элемент | Selector относительно table scope | Состояние |
267
+ |---|---|---|
268
+ | Верхняя пагинация | `[data-autotest="pagination"][data-autotest-value="top"]` | Native button `disabled`; `current-page` внутри |
269
+ | Нижняя пагинация | `[data-autotest="pagination"][data-autotest-value="bottom"]` | Native button `disabled`; `current-page` внутри |
270
+ | Пустой результат | `[data-autotest="empty-state"]` | Наличие существующей empty row |
271
+ | Загрузка | Semantic `table` | `aria-busy="true|false"` |
272
+ | Активная сортировка | `th[aria-sort="ascending"], th[aria-sort="descending"]` | Значение `aria-sort` |
273
+
274
+ Внутри каждого `pagination/top|bottom` уже существуют:
275
+
276
+ - `[data-autotest="first-page"]`, `[data-autotest="prev-page"]`, `[data-autotest="next-page"]` — actual native
277
+ buttons; доступность через `isEnabled()`;
278
+ - `[data-autotest="current-page"]` — отображаемый текущий диапазон/номер;
279
+ - `[data-autotest="page-sizes"][data-autotest-value="<current size>"]` — группа размеров и текущее raw value;
280
+ - `[data-autotest="page-size"][data-autotest-value="<raw size>"]` — конкретный вариант.
281
+
282
+ Отдельной last-page кнопки нет. Конец списка определяется disabled-состоянием `next-page`.
283
+
284
+ У библиотеки нет универсальных якорей business-строк и business-колонок: их identity определяется конкретной
285
+ страницей. Если странице нужны локаторы вида `row/<orderId>` или `cell/<columnKey>`, они должны быть описаны в
286
+ контракте этой страницы, а не угадываться по позиции строки или тексту ячейки.
287
+
288
+ ### SearchUI/SearchUIFilters
289
+
290
+ На текущих продуктовых страницах обычно присутствует один SearchUI/SearchUIFilters. Его scope задаётся
291
+ страницей или наследуется из стабильного `settingsContextName`. Если в DOM окажутся два экземпляра, они будут
292
+ иметь разные scopes.
293
+
294
+ Не ожидайте общего DOM-wrapper вокруг SearchUI. Фильтры и результаты — отдельные roots с одним scope:
295
+
296
+ ```css
297
+ [data-autotest="search-filters"][data-autotest-value="orders"]
298
+ [data-autotest="table"][data-autotest-value="orders"]
299
+ ```
300
+
301
+ Каждый критерий ищется внутри filter scope по raw enum:
302
+
303
+ ```css
304
+ [data-autotest="criterion"][data-autotest-value="STATUS"]
305
+ ```
306
+
307
+ #### Общие actions
308
+
309
+ Selectors ниже относительны к `search-filters/<scope>`.
310
+
311
+ | Action | Selector | Состояние/примечание |
312
+ |---|---|---|
313
+ | Показать/скрыть фильтры | `[data-autotest="toggle-filters"]` | Native button, `aria-expanded`, `aria-controls` |
314
+ | Очистить всё | `[data-autotest="clear-all"]` | Условно присутствует |
315
+ | Запустить поиск/refresh | `[data-autotest="run-search"]` | Native `disabled`/`isEnabled()` |
316
+ | Шаблоны | `[data-autotest="templates"]` | Native button, `aria-expanded`, `aria-controls` |
317
+ | Добавить фильтр | `[role="combobox"][data-autotest="add-filter"]` | Кликать actual combobox; `aria-expanded` |
318
+ | Очистить критерий | `[data-autotest="clear-criterion"]` | Native button внутри criterion root |
319
+ | Удалить критерий | `[data-autotest="remove-criterion"]` | Отсутствует у non-removable predefined criterion |
320
+
321
+ `clear-all`, templates, add-filter и отдельные criterion actions могут отсутствовать из-за config или текущего
322
+ состояния. Это условный UI, а не нарушение locator contract.
323
+
324
+ Внутри `add-filter-options/<scope>` конкретный доступный критерий сейчас выбирается как `[role="option"]` по
325
+ computed accessible name в фиксированной locale. Отдельного raw `data-autotest-value=<CriterionTypeEnum>` у
326
+ этих options пока нет; это явно известное исключение из raw-ID контракта.
327
+
328
+ #### Общие portals
329
+
330
+ MUI popover/modal/listbox может находиться вне DOM-поддерева `search-filters`. Такие roots ищутся от document по
331
+ тому же owner scope:
332
+
333
+ ```css
334
+ [data-autotest="templates-panel"][data-autotest-value="orders"]
335
+ [data-autotest="add-filter-options"][data-autotest-value="orders"]
336
+ [data-autotest="template-editor"][data-autotest-value="orders"]
337
+ ```
338
+
339
+ Внутри templates panel:
340
+
341
+ - строка: `[data-autotest="template-item"]`;
342
+ - применить конкретный шаблон: `button[data-autotest="select-template"][title="<template name>"]`;
343
+ - удалить: сначала найти строку выбранного шаблона, затем внутри неё
344
+ `button[data-autotest="remove-template"]`.
345
+
346
+ `template-editor/<scope>` является отдельным dialog portal, а не потомком `templates-panel`. Его close button:
347
+ `button[data-autotest="close-template-editor"]`.
348
+
349
+ Имя шаблона остаётся пользовательским значением/accessible name и не копируется в technical ID. Create,
350
+ Cancel и обычные поля формы ищутся внутри scoped dialog по native role/name.
351
+
352
+ #### Actions без отдельного `data-autotest`
353
+
354
+ Для нескольких стандартных dialog actions контрактом служат native button + computed accessible name внутри
355
+ уже найденного scoped dialog:
356
+
357
+ - Add filter: option нужного критерия внутри `add-filter-options/<scope>`;
358
+ - Template editor: Create, Cancel;
359
+ - Grouping: Save, Cancel;
360
+ - Multiget: Clear в selected column, Save, Cancel;
361
+ - Transaction Session Status: Close.
362
+
363
+ В Selenium 4 их можно находить без XPath по внутренней разметке:
364
+
365
+ ```java
366
+ static WebElement byAccessibleName(SearchContext scope, String css, String expectedName) {
367
+ return scope.findElements(By.cssSelector(css)).stream()
368
+ .filter(element -> expectedName.equals(element.getAccessibleName()))
369
+ .findFirst()
370
+ .orElseThrow();
371
+ }
372
+
373
+ WebElement save = byAccessibleName(dialog, "button, [role='button']", "Save");
374
+ WebElement status = byAccessibleName(addFilterListbox, "[role='option']", "Status");
375
+ ```
376
+
377
+ `expectedName` берётся из фиксированной locale тестового сценария. Это явно перечисленные locale-aware actions;
378
+ raw IDs критериев, options и entities по переведённому тексту не ищутся.
379
+
380
+ Для portal конкретного критерия используются все три owner attributes:
381
+
382
+ ```css
383
+ [data-autotest="criterion-project-currency-options"][data-autotest-value="orders"][data-autotest-criterion="PROJECT_CURRENCY"]
384
+ ```
385
+
386
+ Generated ID из `aria-controls` не хардкодируется; при необходимости он считывается у trigger после открытия
387
+ popup.
388
+
389
+ ### Матрица всех 31 критериев
390
+
391
+ В таблице указан meaningful control, который должен быть ровно один внутри соответствующего
392
+ `criterion/<CriterionTypeEnum>` root.
393
+
394
+ | `CriterionTypeEnum` | Primary selector внутри criterion root | Family |
395
+ |---|---|---|
396
+ | `EXACT` | `input[data-autotest="criterion-input"]` | Exact input |
397
+ | `ORDERS_SEARCH` | `button[role="combobox"][data-autotest="criterion-label"]` | Orders input |
398
+ | `CURRENCY` | `input[role="combobox"][data-autotest="criterion-collection"]` | Collection |
399
+ | `CUSTOMER_LEVEL` | `[role="combobox"][data-autotest="criterion-customer-level"]` | Dependent select |
400
+ | `THREE_D` | `[role="button"][data-autotest="criterion-option"]` | Enum buttons |
401
+ | `STATUS` | `[role="button"][data-autotest="criterion-option"]` | Enum buttons |
402
+ | `MERCHANT` | `button[data-autotest="criterion-multiget-trigger"]` | Multiget |
403
+ | `ENDPOINT` | `button[data-autotest="criterion-multiget-trigger"]` | Multiget |
404
+ | `RESELLER` | `button[data-autotest="criterion-multiget-trigger"]` | Multiget |
405
+ | `PROCESSOR` | `button[data-autotest="criterion-multiget-trigger"]` | Multiget |
406
+ | `MANAGER` | `button[data-autotest="criterion-multiget-trigger"]` | Multiget |
407
+ | `PROJECT` | `button[data-autotest="criterion-multiget-trigger"]` | Multiget |
408
+ | `COMPANY` | `button[data-autotest="criterion-multiget-trigger"]` | Multiget |
409
+ | `GATE` | `button[data-autotest="criterion-multiget-trigger"]` | Multiget |
410
+ | `DEALER` | `button[data-autotest="criterion-multiget-trigger"]` | Multiget |
411
+ | `DATE_RANGE` | `[role="combobox"][data-autotest="criterion-range-spec"]` | Date |
412
+ | `DATE_RANGE_ORDERS` | `[role="combobox"][data-autotest="criterion-order-date-type"]` | Date |
413
+ | `PROJECT_CURRENCY` | `[role="combobox"][data-autotest="criterion-project-currency"]` | Dependent select |
414
+ | `CARD_TYPES` | `input[role="combobox"][data-autotest="criterion-collection"]` | Collection |
415
+ | `COUNTRIES` | `input[role="combobox"][data-autotest="criterion-collection"]` | Collection |
416
+ | `GROUPING` | `button[data-autotest="criterion-grouping-groups"]` | Grouping dialog |
417
+ | `TRANSACTION_TYPES` | `input[role="combobox"][data-autotest="criterion-collection"]` | Collection |
418
+ | `TRANSACTION_STATUS` | `input[role="combobox"][data-autotest="criterion-collection"]` | Collection |
419
+ | `RECURRENCE_TYPE` | `input[role="combobox"][data-autotest="criterion-collection"]` | Collection |
420
+ | `RECURRENCE_STATUS` | `input[role="combobox"][data-autotest="criterion-collection"]` | Collection |
421
+ | `MFO_CONFIGURATION_TYPE` | `input[role="combobox"][data-autotest="criterion-collection"]` | Collection |
422
+ | `MARKER_TYPE` | `input[role="combobox"][data-autotest="criterion-collection"]` | Collection |
423
+ | `MARKER_STATUS` | `[role="button"][data-autotest="criterion-option"]` | Enum buttons |
424
+ | `PROCESSOR_LOG_ENTRY_TYPE` | `[role="combobox"][data-autotest="criterion-processor-log-entry-type"]` | Single select |
425
+ | `ERROR_CODE` | `input[role="combobox"][data-autotest="criterion-error-code"]` | Async autocomplete |
426
+ | `TRANSACTION_SESSION_STATUS` | `button[data-autotest="criterion-transaction-session-status"]` | Session dialog |
427
+
428
+ Ниже описаны controls и panels каждой family.
429
+
430
+ ### `EXACT`
431
+
432
+ Внутри `criterion/EXACT`:
433
+
434
+ | Элемент | Contract |
435
+ |---|---|
436
+ | Native input | `input[data-autotest="criterion-input"]`; текст читается из `.value` |
437
+ | Выбранное поле | `[role="combobox"][data-autotest="criterion-label"][data-autotest-value="<raw ExactCriterionSearchLabelEnum>"]` |
438
+ | Portal listbox | `criterion-label-options/<scope>` + `data-autotest-criterion="EXACT"` |
439
+ | Option | `[role="option"][data-autotest="criterion-label-option"][data-autotest-value="<raw label>"]` |
440
+
441
+ Selection option читается из `aria-selected`.
442
+
443
+ Raw values `ExactCriterionSearchLabelEnum`:
444
+
445
+ `ALL`, `NAME`, `DESCRIPTION`, `TAGS`, `IDENTIFIER`, `BEAN`, `END_POINT_GROUP_ID`, `ID`, `AMOUNT`,
446
+ `FINAL_CLEARING_DATE`, `MANAGER`, `SERIAL_NUMBER`, `INVOICE`, `CARD_FROM_RECURRENCE_NUMBER`, `FIRST_6`,
447
+ `LAST_4`, `FIRST_6_LAST_4`, `ORDER_IDENTIFIER`, `EMAIL`, `LOGIN`, `PRINCIPAL_DEALER`, `PRINCIPAL_MANAGER`,
448
+ `PRINCIPAL_MERCHANT`, `PRINCIPAL_RESELLER`, `PRINCIPAL_SUPERIOR`, `END_POINT_IDENTIFIER`,
449
+ `END_POINT_GROUP_IDENTIFIER`.
450
+
451
+ ### `ORDERS_SEARCH`
452
+
453
+ Внутри `criterion/ORDERS_SEARCH`:
454
+
455
+ - label trigger: `button[role="combobox"][data-autotest="criterion-label"]`;
456
+ - выбранный raw search label находится в `data-autotest-value` trigger;
457
+ - обычные, numeric, IP и masked inputs используют actual
458
+ `input[data-autotest="criterion-input"]`; значение читается из `.value`;
459
+ - country-вариант использует actual combobox `criterion-input/<raw country id>`.
460
+
461
+ Группированный label dialog:
462
+
463
+ ```css
464
+ [role="dialog"][data-autotest="criterion-label-options"][data-autotest-value="<scope>"][data-autotest-criterion="ORDERS_SEARCH"]
465
+ ```
466
+
467
+ Внутри него:
468
+
469
+ - семь native disclosure summaries: `criterion-label-group/<main|customer|source-card|destination-card|wire|card-present-api|mobile-api>`;
470
+ - 45 native radio inputs: `criterion-label-option/<raw ORDER_SEARCH_LABEL>`;
471
+ - expanded state группы: `aria-expanded` на `summary` и native `details.open`;
472
+ - выбранное поле: native radio `checked`/Selenium `isSelected()`.
473
+
474
+ Selectable raw labels по группам:
475
+
476
+ - `main`: `merchant_invoice_id`, `order_id`, `processor_order_id`, `purpose`, `transaction_amount`,
477
+ `session_token`, `batch_id`;
478
+ - `customer`: `customer_id`, `merchant_customer_identifier`, `customer_phone`, `customer_email`, `customer_ip`,
479
+ `customer_ip_country`, `customer_billing_country`;
480
+ - `source-card`: `source_bank_name`, `source_country`, `source_from_order_id`, `source_bin`,
481
+ `source_bin_range_from_order_id`, `source_last4`, `source_bin_last4`, `source_auth_code`, `source_arn`,
482
+ `source_rrn`, `source_card_holder`, `source_card_ref_id`;
483
+ - `destination-card`: `dest_bank_name`, `dest_country`, `dest_from_order_id`, `dest_bin`,
484
+ `dest_bin_range_from_order_id`, `dest_last4`, `dest_bin_last`, `dest_auth_code`, `dest_arn`, `dest_rrn`,
485
+ `dest_card_ref_id`;
486
+ - `wire`: `account_number`, `routing_number`;
487
+ - `card-present-api`: `reader_id`, `reader_key_serial_number`, `reader_device_serial_number`;
488
+ - `mobile-api`: `device_serial_number`, `phone_serial_number`, `phone_imei`.
489
+
490
+ Не проверяйте, что в dialog обязательно присутствуют все legacy values сохранённого фильтра. Десять значений
491
+ можно восстановить из сохранённого поиска, но нельзя выбрать в текущем dialog:
492
+ `customer_dna_id`, `registration_info_id`, `inn`, `mtcn`, `rebill`, `swift_number`, `webmoney_account`,
493
+ `yamoney_account`, `wire_account`, `card_number_hash_hash`. Для них trigger и input работают, но ни один radio
494
+ не будет выбран.
495
+
496
+ Country options portal:
497
+
498
+ - owner listbox: `criterion-input-options/<scope>` + `data-autotest-criterion="ORDERS_SEARCH"`;
499
+ - option: `criterion-input-option/<raw numeric country id>`;
500
+ - selection: `aria-selected`.
501
+
502
+ ### Enum buttons: `STATUS`, `THREE_D`, `MARKER_STATUS`
503
+
504
+ Каждый вариант является actual button:
505
+
506
+ ```css
507
+ [role="button"][data-autotest="criterion-option"][data-autotest-value="<raw enum>"]
508
+ ```
509
+
510
+ Состояние читается из `aria-pressed`.
511
+
512
+ | Criterion | Raw values |
513
+ |---|---|
514
+ | `STATUS` | `ANY`, `DISABLED`, `ENABLED` |
515
+ | `THREE_D` | `ANY`, `NO`, `YES` |
516
+ | `MARKER_STATUS` | `any`, `unprocessed`, `processed` |
517
+
518
+ Регистр raw value значим.
519
+
520
+ ### Collections
521
+
522
+ Один общий contract используется для:
523
+
524
+ - `CURRENCY`;
525
+ - `CARD_TYPES`;
526
+ - `COUNTRIES`;
527
+ - `TRANSACTION_TYPES`;
528
+ - `TRANSACTION_STATUS`;
529
+ - `RECURRENCE_TYPE`;
530
+ - `RECURRENCE_STATUS`;
531
+ - `MFO_CONFIGURATION_TYPE`;
532
+ - `MARKER_TYPE`.
533
+
534
+ Внутри criterion root:
535
+
536
+ - actual input: `input[role="combobox"][data-autotest="criterion-collection"]`;
537
+ - выбранные Chips: `criterion-collection-value/<raw entity id>`;
538
+ - synthetic All имеет literal value `all`, а не translated label.
539
+
540
+ Detached content:
541
+
542
+ | Элемент | Contract |
543
+ |---|---|
544
+ | Autocomplete paper | `criterion-collection-panel/<scope>` + owning criterion |
545
+ | Named listbox | `criterion-collection-options/<scope>` + owning criterion |
546
+ | Option | `criterion-collection-option/<raw entity id|all>` |
547
+
548
+ Option selection читается из `aria-selected`. Состояние All определяется raw `all`, а не сравнением количества
549
+ выбранных и доступных options.
550
+
551
+ ### `CUSTOMER_LEVEL`
552
+
553
+ - control: `criterion-customer-level/<raw selected level id>`;
554
+ - listbox: `criterion-customer-level-options/<scope>` + `data-autotest-criterion="CUSTOMER_LEVEL"`;
555
+ - option: `criterion-customer-level-option/<raw level id>`;
556
+ - loading: `aria-busy`;
557
+ - недоступность до выбора зависимостей/при loading: `aria-disabled="true"` на combobox;
558
+ - выбранный option: `aria-selected`.
559
+
560
+ Control может быть disabled или список может быть пустым, если не выбран ровно один Merchant либо provider не
561
+ вернул подходящие уровни. Это product state.
562
+
563
+ ### `PROJECT_CURRENCY`
564
+
565
+ - control: `criterion-project-currency/<raw currency id>`;
566
+ - listbox: `criterion-project-currency-options/<scope>` + owning criterion;
567
+ - option: `criterion-project-currency-option/<raw currency id>`;
568
+ - conversion checkbox: native
569
+ `input[data-autotest="criterion-project-currency-convert"]`; состояние через `isSelected()`/`checked`;
570
+ - loading: `aria-busy`; disabled selector: `aria-disabled="true"` на combobox.
571
+
572
+ Conversion checkbox остаётся отдельным native control и не становится disabled автоматически только из-за
573
+ недоступности currency combobox.
574
+
575
+ ### Date criteria
576
+
577
+ Оба date criteria имеют range-spec selector:
578
+
579
+ - control: `criterion-range-spec/<raw DateRangeSpecType>`;
580
+ - listbox: `criterion-range-spec-options/<scope>` + owning criterion;
581
+ - option: `criterion-range-spec-option/<raw DateRangeSpecType>`;
582
+ - option selection: `aria-selected`.
583
+
584
+ Raw `DateRangeSpecType`:
585
+
586
+ `EXACTLY`, `TODAY`, `YESTERDAY`, `THIS_WEEK`, `LAST_WEEK`, `THIS_MONTH`, `LAST_MONTH`, `DAYS_BEFORE`,
587
+ `HOURS_BEFORE`, `DATE_INDEPENDENT`.
588
+
589
+ Конкретная страница может разрешать только подмножество этих режимов, поэтому автотест не должен ожидать все
590
+ десять options без соответствующей фикстуры/config.
591
+
592
+ `DATE_RANGE_ORDERS` дополнительно имеет:
593
+
594
+ - control: `criterion-order-date-type/<raw order date type>`;
595
+ - listbox: `criterion-order-date-type-options/<scope>` + `data-autotest-criterion="DATE_RANGE_ORDERS"`;
596
+ - option: `criterion-order-date-type-option/<raw order date type>`.
597
+
598
+ Raw order date types: `SESSION_CREATED`, `SESSION_STATUS_CHANGED`, `TX_CREATED`, `BANK`, `TX_SETTLED`,
599
+ `TX_UNSETTLED`.
600
+
601
+ Зависимые от режима inputs:
602
+
603
+ | Режим | Contract |
604
+ |---|---|
605
+ | `DAYS_BEFORE`/`HOURS_BEFORE` | Native number input `criterion-before-count`; значение из `.value` |
606
+ | Exact date-only | Named composite group `criterion-date-range`; picker button `criterion-date-range-picker-toggle` |
607
+ | Exact date-time start | Composite `criterion-date-time-from`; button `criterion-date-time-from-picker-toggle` |
608
+ | Exact date-time end | Composite `criterion-date-time-to`; button `criterion-date-time-to-picker-toggle` |
609
+
610
+ Picker portal roots:
611
+
612
+ - `criterion-date-range-picker/<scope>`;
613
+ - `criterion-date-time-from-picker/<scope>`;
614
+ - `criterion-date-time-to-picker/<scope>`;
615
+ - каждый также получает `data-autotest-criterion` владельца.
616
+
617
+ Внутри picker:
618
+
619
+ - day gridcell: `criterion-date-option/<YYYY-MM-DD>`, selection через `aria-selected`;
620
+ - clock option: `[role="option"][data-autotest="criterion-time-option"]`; конкретное число берётся из option
621
+ content/accessible name внутри уже scoped picker, selection — из `aria-selected`.
622
+
623
+ Не используйте hidden serialized input date picker: контрактом являются visible composite sections и actual
624
+ picker controls.
625
+
626
+ ### `PROCESSOR_LOG_ENTRY_TYPE`
627
+
628
+ - control: `criterion-processor-log-entry-type/<raw numeric provider id>`;
629
+ - listbox: `criterion-processor-log-entry-type-options/<scope>` + owning criterion;
630
+ - option: `criterion-processor-log-entry-type-option/<raw numeric provider id>`;
631
+ - selection: `aria-selected`.
632
+
633
+ ### `ERROR_CODE`
634
+
635
+ - actual autocomplete input: `criterion-error-code/<raw committed choice id>`;
636
+ - текст запроса читается из `.value`, а не из test attribute;
637
+ - clear action: `criterion-error-code-clear`;
638
+ - paper: `criterion-error-code-panel/<scope>` + owning criterion;
639
+ - listbox: `criterion-error-code-options/<scope>` + owning criterion;
640
+ - option: `criterion-error-code-option/<raw choice id>`;
641
+ - loading: `aria-busy`; selection: `aria-selected`.
642
+
643
+ Одинаковые display labels не создают коллизию, потому что identity option — raw ID.
644
+
645
+ ### `GROUPING`
646
+
647
+ Inline controls:
648
+
649
+ - dialog trigger: `button[data-autotest="criterion-grouping-groups"]`;
650
+ - selected Chips: `criterion-grouping-value/<raw GroupingType>`;
651
+ - date-type control: `criterion-grouping-date-type/<raw date type>`;
652
+ - date-type listbox: `criterion-grouping-date-type-options/<scope>` + owning criterion;
653
+ - date-type option: `criterion-grouping-date-type-option/<raw date type>`.
654
+
655
+ Raw grouping date types: `MONTH`, `DAY`, `CLOSE_DAY`, `SETTLEMENT_DAY`, `SETTLEMENT_MONTH`.
656
+
657
+ Raw `GroupingType` values: `MERCHANT`, `MANAGER`, `PROJECT`, `CURRENCY`, `ENDPOINT`, `CARD_TYPE`, `GATE`,
658
+ `PROCESSOR`, `MID`, `COUNTERPARTY`, `PROJECT_CODE`, `DATE`, `MONTH`, `DAY`, `CLOSE_DAY`, `SETTLEMENT_DAY`,
659
+ `SETTLEMENT_MONTH`. Страница может передать только подмножество available types.
660
+
661
+ Detached dialog:
662
+
663
+ ```css
664
+ [role="dialog"][data-autotest="criterion-grouping-panel"][data-autotest-value="<scope>"][data-autotest-criterion="GROUPING"]
665
+ ```
666
+
667
+ Внутри dialog:
668
+
669
+ | Элемент | Contract |
670
+ |---|---|
671
+ | Available group | `criterion-grouping-available` |
672
+ | Selected group | `criterion-grouping-selected` |
673
+ | Add/remove row | `criterion-grouping-option/<raw GroupingType>`; actual native button |
674
+ | Search input | `criterion-grouping-search` |
675
+ | Conditional clear search | `criterion-grouping-search-clear` |
676
+ | Add all | `criterion-grouping-add-all` |
677
+ | Remove all | `criterion-grouping-remove-all` |
678
+
679
+ После переноса row из available в selected тот же raw ID сохраняется, а action/accessible name меняется.
680
+ Save/Cancel ищутся по native role/name внутри scoped dialog.
681
+
682
+ ### `TRANSACTION_SESSION_STATUS`
683
+
684
+ Inline:
685
+
686
+ - trigger: `criterion-transaction-session-status/<raw current group>`;
687
+ - current group Chip: `criterion-transaction-session-status-group-value/<raw group>`;
688
+ - selected status Chips: `criterion-transaction-session-status-value/<status.displayName>`;
689
+ - trigger state: `aria-expanded`, `aria-controls`, `aria-busy`.
690
+
691
+ Portal dialog:
692
+
693
+ - root: `criterion-transaction-session-status-panel/<scope>` +
694
+ `data-autotest-criterion="TRANSACTION_SESSION_STATUS"`;
695
+ - group combobox: `criterion-transaction-session-status-group/<raw group>`;
696
+ - group listbox: `criterion-transaction-session-status-group-options/<scope>` + owning criterion;
697
+ - group option: `criterion-transaction-session-status-group-option/<raw group>`;
698
+ - status checkbox input: `criterion-transaction-session-status-option/<status.displayName>`;
699
+ - checkbox state: native `checked`/Selenium `isSelected()`.
700
+
701
+ Список group/status приходит от provider и является динамическим: не проверяйте фиксированное число групп без
702
+ соответствующей фикстуры. `status.displayName` является backend identity статуса внутри группы; locator не
703
+ зависит от перевода или позиции в массиве. Изменения статусов применяются сразу, без отдельного Save.
704
+
705
+ Group listbox является отдельным portal и не находится внутри status dialog card. После открытия combobox его
706
+ нужно искать от `document` по owner scope и `data-autotest-criterion`, а не descendant-поиском от dialog.
707
+
708
+ ### Multiget: девять типов критериев
709
+
710
+ | Criterion | `LinkedEntityTypeEnum` | Only enabled control | Gate search labels |
711
+ |---|---|---|---|
712
+ | `PROJECT` | `PROJECT` | Да | Нет |
713
+ | `ENDPOINT` | `ENDPOINT` | Да | Нет |
714
+ | `GATE` | `GATE` | Да | Да |
715
+ | `PROCESSOR` | `PROCESSOR` | Да | Нет |
716
+ | `COMPANY` | `COMPANY` | Да | Нет |
717
+ | `MANAGER` | `MANAGER` | Нет | Нет |
718
+ | `MERCHANT` | `MERCHANT` | Да | Нет |
719
+ | `RESELLER` | `RESELLER` | Да | Нет |
720
+ | `DEALER` | `DEALER` | Нет | Нет |
721
+
722
+ #### Inline trigger и summary
723
+
724
+ - actual native button: `criterion-multiget-trigger/<NONE|ALL|SEARCH>`;
725
+ - selected/excluded summary Chip: `criterion-multiget-value/<raw numeric entity.id>`;
726
+ - open state: `aria-expanded`; portal link: `aria-controls`; popup type: `aria-haspopup="dialog"`.
727
+
728
+ Сохранённый фильтр может восстановить режим `SEARCH`, однако внутри dialog переключатели режима существуют
729
+ только для `NONE` и `ALL`. В таком восстановленном состоянии не ожидайте обязательный `aria-pressed="true"`
730
+ у одного из этих двух переключателей.
731
+
732
+ #### Owner-scoped dialog
733
+
734
+ ```css
735
+ [role="dialog"][data-autotest="criterion-multiget-panel"][data-autotest-value="<scope>"][data-autotest-criterion="<multiget CriterionTypeEnum>"]
736
+ ```
737
+
738
+ Внутри dialog:
739
+
740
+ | Элемент | Contract/state |
741
+ |---|---|
742
+ | Close | `criterion-multiget-close` |
743
+ | Include mode | `criterion-multiget-mode/NONE`, `aria-pressed` |
744
+ | Exclude mode | `criterion-multiget-mode/ALL`, `aria-pressed` |
745
+ | Only enabled | Native checkbox `criterion-multiget-only-enabled`; только для семи типов из таблицы |
746
+ | Search | Native input `criterion-multiget-search`; query из `.value` |
747
+ | Gate search field | `criterion-multiget-search-label/<all|mid|descriptor>`, `aria-pressed` |
748
+ | Available column | Named group `criterion-multiget-available`, loading через `aria-busy` |
749
+ | Selected/excluded column | Named group `criterion-multiget-selected` |
750
+ | Add entity | Native button `criterion-multiget-add/<raw numeric entity.id>` |
751
+ | Remove entity | Native button `criterion-multiget-remove/<raw numeric entity.id>` |
752
+
753
+ Одинаковый entity ID допустим в разных SearchUI scopes. Внутри одного dialog hidden duplicate может оставаться в
754
+ available column после выбора, поэтому add/remove всегда ищутся сначала относительно нужной колонки.
755
+
756
+ Clear в selected column, Save и Cancel ищутся по native role/name внутри scoped dialog. Pagination остаётся
757
+ semantic `nav`; текущая страница — `aria-current="page"`, unavailable controls — native disabled. Отдельные
758
+ anchors для этих стандартных действий отсутствуют.
759
+
760
+ ### Ожидания и асинхронный UI
761
+
762
+ Для async lists/pickers/modal используйте explicit waits на смысловое состояние:
763
+
764
+ - owner-scoped portal появился;
765
+ - trigger получил `aria-expanded="true"`;
766
+ - `aria-busy` стал `false`;
767
+ - ожидаемый raw option появился;
768
+ - после action изменился native/ARIA state или portal исчез.
769
+
770
+ Не используйте fixed sleeps. Не считайте empty result ошибкой locator, если provider действительно вернул
771
+ пустой список.
772
+
773
+ ### На чём не строить Selenium-локаторы
774
+
775
+ Не используйте как постоянную identity:
776
+
777
+ - MUI/Emotion class names (`Mui*`, `css-*`);
778
+ - `svg`, `path`, декоративную стрелку/иконку;
779
+ - DOM depth, `nth-child`, array index;
780
+ - translated visible text как технический ID raw-критерия, option или entity; явно перечисленные выше
781
+ role/name actions являются locale-aware исключением;
782
+ - generated React/MUI IDs из `aria-controls`;
783
+ - `PNE`, `Paynet`, WhiteLabel/product name;
784
+ - произвольное введённое или секретное значение как переиспользуемый технический ID; поиск заранее созданного
785
+ template fixture по его имени является test-data lookup, а не общей identity компонента;
786
+ - tag name как единственный признак (`//button`, `//div`).
787
+
788
+ Если элемент кликабельный, выбирайте якорь на actual meaningful control из таблиц выше, а не вложенную
789
+ декоративную иконку.
790
+
791
+ ### Где находится источник истины
792
+
793
+ Этот раздел README — основной реестр поддерживаемых Selenium-якорей библиотеки. Storybook используется только для
794
+ интерактивных примеров.
157
795
 
158
796
  ## OverlayHost
159
797