mirror of
https://github.com/Nikolay-Shirokov/cc-1c-skills.git
synced 2026-06-10 16:14:54 +03:00
test(web-test): M3 P1 batch 1 — confirm-save-no/pending, more-menu, clear/ref-form, table checkbox/clear
02-crud: confirm-save-no (rollback при save:false), confirm-pending (closeForm() без решения возвращает confirmation), more-menu (clickElement 'Ещё' возвращает submenu). 03-fillfields: clear (Shift+F4 через пустое значение), reference-non-quickchoice (fillFields на quickChoice=false поле — method=dropdown через DLB; чистый form-path требует hasPick && !hasSelect, такого поля в синтетике нет). 04-selectvalue: clear (selectValue '' → Shift+F4). show-all-form отложен — требует quickChoice=true каталога с количеством > порога dropdown (в синтетике нет). 05-table: checkbox (fillTableRow с Boolean), clear (Shift+F4 на ref-ячейке + восстановление для последующего delete). Live на webtest: все шаги проходят. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -2,7 +2,7 @@ export const name = 'CRUD: открытие, чтение, закрытие с
|
||||
export const tags = ['crud', 'smoke'];
|
||||
export const timeout = 60000;
|
||||
|
||||
export default async function({ navigateSection, openCommand, clickElement, closeForm, readTable, fillField, getFormState, assert, step, log }) {
|
||||
export default async function({ navigateSection, openCommand, clickElement, closeForm, readTable, fillField, getFormState, getPage, assert, step, log }) {
|
||||
|
||||
await step('read: список Контрагентов отдаёт колонки/строки/total', async () => {
|
||||
await navigateSection('Склад');
|
||||
@@ -57,4 +57,52 @@ export default async function({ navigateSection, openCommand, clickElement, clos
|
||||
assert.equal(phoneField?.value, newPhone, 'Телефон должен сохраниться');
|
||||
await closeForm();
|
||||
});
|
||||
|
||||
await step('confirm-save-no: closeForm({save:false}) → изменения откатываются', async () => {
|
||||
await navigateSection('Склад');
|
||||
await openCommand('Контрагенты');
|
||||
await clickElement('ООО Восток', { dblclick: true });
|
||||
const before = await getFormState();
|
||||
const origPhone = before.fields?.find(f => f.name === 'Телефон')?.value;
|
||||
log(`origPhone='${origPhone}'`);
|
||||
await fillField('Телефон', '+7 (000) 000-00-00');
|
||||
const closed = await closeForm({ save: false });
|
||||
assert.ok(closed.closed, 'Форма должна закрыться через "Нет"');
|
||||
|
||||
await navigateSection('Склад');
|
||||
await openCommand('Контрагенты');
|
||||
await clickElement('ООО Восток', { dblclick: true });
|
||||
const state = await getFormState();
|
||||
const phone = state.fields?.find(f => f.name === 'Телефон')?.value;
|
||||
log(`Re-opened phone after save:false='${phone}'`);
|
||||
assert.equal(phone, origPhone, 'Телефон не должен измениться (save:false откатил)');
|
||||
await closeForm();
|
||||
});
|
||||
|
||||
await step('confirm-pending: closeForm() без решения → confirmation в state', async () => {
|
||||
await navigateSection('Склад');
|
||||
await openCommand('Контрагенты');
|
||||
await clickElement('ООО Север', { dblclick: true });
|
||||
await fillField('Телефон', '+7 (123) 456-78-90');
|
||||
const pending = await closeForm();
|
||||
log(`pending: closed=${pending.closed} confirmation=${JSON.stringify(pending.confirmation)}`);
|
||||
assert.ok(!pending.closed, 'Форма НЕ должна закрыться без решения');
|
||||
assert.ok(pending.confirmation, 'state.confirmation должен присутствовать');
|
||||
// Закрыть через явный отказ от сохранения
|
||||
await closeForm({ save: false });
|
||||
});
|
||||
|
||||
await step('more-menu: clickElement("Ещё") возвращает submenu[]', async () => {
|
||||
await navigateSection('Склад');
|
||||
await openCommand('Контрагенты');
|
||||
const r = await clickElement('Ещё');
|
||||
const items = r.submenu || [];
|
||||
log(`submenu items: ${items.length} sample=${items.slice(0, 5).join(', ')}`);
|
||||
assert.ok(Array.isArray(r.submenu), 'clickElement("Ещё") должен вернуть submenu[]');
|
||||
assert.ok(items.length >= 1, 'submenu не должен быть пустым');
|
||||
// Закрыть submenu
|
||||
const page = await getPage();
|
||||
await page.keyboard.press('Escape');
|
||||
await closeForm();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -52,6 +52,47 @@ export default async function({ navigateSection, openCommand, clickElement, fill
|
||||
await closeForm({ save: false });
|
||||
});
|
||||
|
||||
await step('clear: fillFields пустым значением очищает текстовое поле', async () => {
|
||||
await navigateSection('Склад');
|
||||
await openCommand('Контрагенты');
|
||||
await clickElement('ООО Север', { dblclick: true });
|
||||
const before = await getFormState();
|
||||
const phoneBefore = findField(before, 'Телефон')?.value;
|
||||
log(`phone before clear='${phoneBefore}'`);
|
||||
|
||||
const r = await fillFields({ 'Телефон': '' });
|
||||
log('clear method: ' + r.filled[0]?.method);
|
||||
assert.ok(r.filled[0]?.ok, 'clear должен вернуть ok=true');
|
||||
assert.equal(r.filled[0]?.method, 'clear', 'method должен быть clear (Shift+F4)');
|
||||
|
||||
const state = await getFormState();
|
||||
assert.equal(findField(state, 'Телефон')?.value, '', 'Телефон должен быть пустым');
|
||||
|
||||
await closeForm({ save: false });
|
||||
});
|
||||
|
||||
await step('reference-non-quickchoice: fillFields на Контрагент (quickChoice=false)', async () => {
|
||||
// Поле имеет DLB+CB → fillFields идёт через fillReferenceField (method=dropdown/typeahead).
|
||||
// Чистый method='form' путь требует поля без DLB (hasPick && !hasSelect) — в синтетике
|
||||
// такого поля нет, поэтому проверяем сам факт корректного заполнения через DLB.
|
||||
await navigateSection('Склад');
|
||||
await openCommand('Приходная накладная');
|
||||
await clickElement('Создать');
|
||||
|
||||
const r = await fillFields({ 'Контрагент': 'ООО Север' });
|
||||
log('reference method: ' + r.filled[0]?.method);
|
||||
assert.ok(r.filled[0]?.ok, 'fillFields на Контрагент должен сработать');
|
||||
assert.ok(['dropdown', 'typeahead', 'form'].includes(r.filled[0]?.method),
|
||||
`method=${r.filled[0]?.method} должен быть один из dropdown|typeahead|form`);
|
||||
|
||||
const state = await getFormState();
|
||||
const v = findField(state, 'Контрагент')?.value || '';
|
||||
log(`Контрагент value='${v}'`);
|
||||
assert.includes(v, 'Север', 'Контрагент должен содержать "Север"');
|
||||
|
||||
await closeForm({ save: false });
|
||||
});
|
||||
|
||||
await step('radio: КатегорияЦены (RadioButtons) через fillFields, СпособУчёта (Tumbler) через clickElement', async () => {
|
||||
// Tumbler-представление не парсится fillFields как radio-поле (см.
|
||||
// upload/web-test-bugs.md пункт 5). Но варианты тумблера видны в
|
||||
|
||||
@@ -37,4 +37,23 @@ export default async function({ navigateSection, openCommand, clickElement, sele
|
||||
|
||||
await closeForm({ save: false });
|
||||
});
|
||||
|
||||
await step('clear: selectValue с пустым search → Shift+F4', async () => {
|
||||
await navigateSection('Склад');
|
||||
await openCommand('Приходная накладная');
|
||||
await clickElement('Создать');
|
||||
|
||||
await selectValue('Организация', 'Альфа');
|
||||
const before = await selectValue('Организация', ''); // empty → clear
|
||||
const field = findField(before, 'Организация');
|
||||
log(`Организация after clear value='${field?.value}'`);
|
||||
assert.equal(field?.value, '', 'Организация должна быть очищена');
|
||||
|
||||
await closeForm({ save: false });
|
||||
});
|
||||
|
||||
}
|
||||
// show-all-form ветка (P1 в матрице) требует quickChoice=true каталога с
|
||||
// количеством > порога dropdown, чтобы появилась ссылка "Показать все".
|
||||
// В текущей синтетике такого каталога нет (Организации ~2 элемента, остальные
|
||||
// quickChoice=false). Откладывается до расширения синтетики.
|
||||
|
||||
@@ -48,6 +48,35 @@ export default async function({ navigateSection, openCommand, clickElement, fill
|
||||
assert.equal(t.rows[1]['Цена'], '150,00', 'Цена строки 1 = 150');
|
||||
});
|
||||
|
||||
await step('checkbox: переключить Согласовано в строке 1 через fillTableRow', async () => {
|
||||
const r = await fillTableRow(
|
||||
{ 'Согласовано': true },
|
||||
{ table: 'Товары', row: 1 }
|
||||
);
|
||||
log(`checkbox result: ${JSON.stringify(r.filled || r)}`);
|
||||
const t = await readTable({ table: 'Товары' });
|
||||
log(`row 1 Согласовано='${t.rows[1]['Согласовано']}'`);
|
||||
assert.equal(t.rows[1]['Согласовано'], 'true', 'Согласовано должно стать true');
|
||||
});
|
||||
|
||||
await step('clear: очистить ссылочную ячейку Номенклатура через fillTableRow с пустым значением', async () => {
|
||||
// Используем строку 0 (Товар 01)
|
||||
const r = await fillTableRow(
|
||||
{ 'Номенклатура': '' },
|
||||
{ table: 'Товары', row: 0 }
|
||||
);
|
||||
log(`clear result: ${JSON.stringify(r.filled || r)}`);
|
||||
const t = await readTable({ table: 'Товары' });
|
||||
log(`row 0 Номенклатура after clear='${t.rows[0]['Номенклатура']}'`);
|
||||
assert.equal(t.rows[0]['Номенклатура'], '', 'Номенклатура должна быть очищена (Shift+F4)');
|
||||
|
||||
// Восстанавливаем Товар 01 чтобы последующий delete мог работать с предсказуемым состоянием
|
||||
await fillTableRow(
|
||||
{ 'Номенклатура': 'Товар 01' },
|
||||
{ table: 'Товары', row: 0 }
|
||||
);
|
||||
});
|
||||
|
||||
await step('delete: удалить первую строку', async () => {
|
||||
await deleteTableRow(0, { table: 'Товары' });
|
||||
const t = await readTable({ table: 'Товары' });
|
||||
|
||||
Reference in New Issue
Block a user