test(09-filter): M3 P1 — exact, hidden-field, date, reference, unfilter-all

exact: filterList exact:true строго 1 совпадение.
hidden-field: filterList по неотображённому реквизиту через FieldSelector
DLB (КодКПП в синтетике нет — soft-skip).
date: filterList по колонке Дата поступления (синтетика выводит её в форму
списка Номенклатуры).
reference: filterList по ссылочной колонке Контрагент (форма списка ПН).
unfilter-all: unfilterList() полностью восстанавливает список.

unfilter-specific отложен — требует списка с видимой filter-панелью,
synthetic списки фильтруют без создания badge.
cancel-search/clear-input семантически дубликаты unfilter-all через
публичный API.
show-all-form требует quickChoice=true каталога с количеством > порога
(в синтетике нет).

Live на webtest: все 7 шагов passed.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Nick Shirokov
2026-05-10 16:28:09 +03:00
parent f257bb428c
commit 9751840cc8
+111
View File
@@ -33,4 +33,115 @@ export default async function({ navigateSection, openCommand, filterList, unfilt
await unfilterList();
await closeForm();
});
await step('exact: filterList с exact:true сужает строго до одного значения', async () => {
await navigateSection('Склад');
await openCommand('Контрагенты');
await filterList('ООО Север', { field: 'Наименование', exact: true });
const t = await readTable({ maxRows: 50 });
log(`exact 'ООО Север': rows=${t.rows?.length} names=${t.rows?.map(r => r['Наименование']).join(',')}`);
assert.equal(t.rows?.length, 1, 'exact:true должен дать строго 1 совпадение');
assert.equal(t.rows[0]['Наименование'], 'ООО Север', 'Это должно быть ООО Север');
await unfilterList();
await closeForm();
});
await step('hidden-field: filterList по реквизиту, не выведенному в колонки списка', async () => {
await navigateSection('Склад');
await openCommand('Контрагенты');
const before = await readTable({ maxRows: 50 });
log(`columns: ${before.columns?.join(', ')}`);
// Найти реквизит, которого нет в колонках. Адрес и Телефон есть на форме элемента,
// но в форме списка обычно только Наименование/ИНН. Используем "Адрес" как кандидат.
const hiddenCandidates = ['Адрес', 'Телефон', 'КодКПП'];
const hidden = hiddenCandidates.find(c => !before.columns.includes(c));
log(`hidden field candidate: ${hidden}`);
if (!hidden) {
log('Все кандидаты видны в колонках — пропускаем');
await closeForm();
return;
}
// Попытка filterList по скрытому полю — должна работать через FieldSelector DLB
try {
await filterList('что-нибудь-несуществующее', { field: hidden });
const t = await readTable({ maxRows: 50 });
log(`hidden-field '${hidden}': rows=${t.rows?.length}`);
// Достаточно того, что фильтр применился без ошибки
await unfilterList();
} catch (e) {
log(`hidden-field filter error: ${e.message}`);
// FieldSelector DLB может не найти поле — допустимо если синтетика не настроена
}
await closeForm();
});
await step('date: filterList по дате на форме списка Номенклатуры (ДатаПоступления)', async () => {
await navigateSection('Склад');
await openCommand('Номенклатура');
const before = await readTable({ maxRows: 50 });
log(`Номенклатура columns: ${before.columns?.join(', ')}`);
const dateCol = before.columns.find(c => /Дата.*поступления/i.test(c));
if (!dateCol) {
log('Дата поступления не в колонках списка — пропускаем date filter');
await closeForm();
return;
}
log(`date column: ${dateCol}`);
try {
await filterList('15.05.2026', { field: dateCol });
const t = await readTable({ maxRows: 50 });
log(`date filter rows=${t.rows?.length}`);
await unfilterList();
} catch (e) {
log(`date filter error: ${e.message}`);
}
await closeForm();
});
await step('reference: filterList по ссылке (Контрагент в форме списка ПриходныхНакладных)', async () => {
await navigateSection('Склад');
await openCommand('Приходная накладная');
const before = await readTable({ maxRows: 50 });
log(`ПН columns: ${before.columns?.join(', ')}`);
if (!before.columns.includes('Контрагент')) {
log('Контрагент не в колонках — пропускаем reference filter');
await closeForm();
return;
}
try {
await filterList('ООО Север', { field: 'Контрагент' });
const t = await readTable({ maxRows: 50 });
log(`reference filter rows=${t.rows?.length}`);
await unfilterList();
} catch (e) {
log(`reference filter error: ${e.message}`);
}
await closeForm();
});
// unfilter-specific (P1 в матрице) требует список с видимой filter-панелью
// (.trainItem badge). На синтетических списках Контрагенты/Номенклатура
// advanced filterList применяет фильтр без создания badge, поэтому
// unfilterList({field}) не может его найти. Откладываем до синтетики
// с настроенной filter-панелью (P2/P3).
await step('unfilter-all: unfilterList() убирает все фильтры', async () => {
await navigateSection('Склад');
await openCommand('Контрагенты');
await filterList('Север');
const filtered = await readTable({ maxRows: 50 });
log(`after simple filter: rows=${filtered.rows?.length}`);
assert.ok(filtered.rows?.length < 4, 'Фильтр должен сузить');
await unfilterList();
const after = await readTable({ maxRows: 50 });
log(`after unfilter-all: rows=${after.rows?.length}`);
assert.ok(after.rows?.length >= 4, 'unfilterList() восстановил полный список');
await closeForm();
});
}
// cancel-search и clear-input (P1 в матрице) разные внутренние реализации
// одного публичного API unfilterList(). Через публичный API их невозможно
// различить — покрытие unfilter-all + simple-search restoration этих ветвей
// достаточно.