fix(supplier): manage-project.js robustness — conditional type/tab clicks + diag dump
Найдено при Task 4 live-smoke form-канала: - type-select и вкладка «Список» кликались безусловно → re-click уже-активного значения ремоунтит Element UI tab-pane (textarea детачится). Теперь кликаем только при реальной смене значения + waitForTimeout после смены типа. - defensive: проверка непустого textarea после fill content. - diag: на status!=OK дамп фактически отправленного rt-project-save body в stderr.
This commit is contained in:
@@ -124,14 +124,24 @@ async function fillForm(page, dto) {
|
||||
'"Ретро сайты" / "Ретро звонки" are not supported in Tier-2 form channel.',
|
||||
);
|
||||
}
|
||||
// Кликаем input чтобы открыть dropdown
|
||||
await fieldByFor(page, 'type').locator('.el-select input.el-input__inner').click();
|
||||
// Dropdown рендерится снаружи формы в body — ждём его появления
|
||||
const dropdownOption = page.locator('.el-select-dropdown__item', {
|
||||
hasText: new RegExp(`^${signalLabel}$`),
|
||||
});
|
||||
await dropdownOption.waitFor({ state: 'visible', timeout: TIMEOUT_MS });
|
||||
await dropdownOption.click();
|
||||
// Тип меняем ТОЛЬКО если текущее значение ≠ нужное. Смена типа ремоунтит
|
||||
// content tab-pane (Сайты/Звонки/СМС — разные поля сбора) → если сразу
|
||||
// после type-select заполнять content, fill попадёт в detached textarea
|
||||
// (Vue ещё не закончил ре-рендер) → rt-project-save уходит с пустым
|
||||
// `content` → портал «Введите домены». Verified live 2026-05-19.
|
||||
const typeInput = fieldByFor(page, 'type').locator('.el-select input.el-input__inner');
|
||||
const currentType = (await typeInput.inputValue().catch(() => '')).trim();
|
||||
if (currentType !== signalLabel) {
|
||||
await typeInput.click();
|
||||
// Dropdown рендерится снаружи формы в body — ждём его появления
|
||||
const dropdownOption = page.locator('.el-select-dropdown__item', {
|
||||
hasText: new RegExp(`^${signalLabel}$`),
|
||||
});
|
||||
await dropdownOption.waitFor({ state: 'visible', timeout: TIMEOUT_MS });
|
||||
await dropdownOption.click();
|
||||
// Ждём, пока Vue завершит ре-рендер content tab-pane после смены типа.
|
||||
await page.waitForTimeout(1000);
|
||||
}
|
||||
|
||||
// --- 7. Regions (label for="regions") — SKIP, gap зафиксирован в JSDoc ---
|
||||
// DTO несёт int[] id; форма требует имена. Mapping не реализован для MVP.
|
||||
@@ -154,13 +164,30 @@ async function fillForm(page, dto) {
|
||||
: null;
|
||||
if (contentLines) {
|
||||
const contentField = fieldByFor(page, 'content');
|
||||
// Убедиться что вкладка «Список» активна (она default, но на всякий случай)
|
||||
const listTab = contentField.locator('.el-tabs__item', { hasText: 'Список' });
|
||||
const listTabCount = await listTab.count();
|
||||
if (listTabCount > 0) {
|
||||
await listTab.click();
|
||||
// Вкладка «Список» — default active. Кликаем ТОЛЬКО если она НЕ активна:
|
||||
// клик по вкладке Element UI ремоунтит tab-pane → textarea детачится,
|
||||
// и последующий .fill() гонится с ре-рендером (домены теряются →
|
||||
// rt-project-save уходит с пустым `content` → портал «Введите домены»).
|
||||
// Verified live 2026-05-19: re-click активной вкладки ломал save.
|
||||
const listTab = contentField.locator('.el-tabs__item', { hasText: 'Список' }).first();
|
||||
if ((await listTab.count()) > 0) {
|
||||
const tabClass = (await listTab.getAttribute('class')) || '';
|
||||
if (!tabClass.includes('is-active')) {
|
||||
await listTab.click();
|
||||
await contentField.locator('textarea.el-textarea__inner')
|
||||
.waitFor({ state: 'visible', timeout: TIMEOUT_MS });
|
||||
}
|
||||
}
|
||||
const contentTa = contentField.locator('textarea.el-textarea__inner');
|
||||
await contentTa.fill(contentLines);
|
||||
// Defensive: убедиться, что значение действительно осело в textarea
|
||||
// (если поле детачнулось ре-рендером — fill уйдёт в пустоту).
|
||||
const filledValue = await contentTa.inputValue();
|
||||
if (filledValue.trim() === '') {
|
||||
throw new Error(
|
||||
'Content textarea empty after fill — likely tab/type re-render race; domains lost',
|
||||
);
|
||||
}
|
||||
await contentField.locator('textarea.el-textarea__inner').fill(contentLines);
|
||||
}
|
||||
|
||||
// --- 10. Limit (label for="limit") ---
|
||||
@@ -215,6 +242,9 @@ async function createOp(page, args) {
|
||||
|
||||
const body = await saveResponse.json();
|
||||
if (body.status !== 'OK') {
|
||||
// DIAG: дамп фактически отправленного тела — для расследования "Введите домены"
|
||||
const sentBody = saveResponse.request().postData();
|
||||
process.stderr.write(JSON.stringify({ diag_sent_body: sentBody }) + '\n');
|
||||
throw new Error(`Portal rejected save: ${body.message || 'unknown error'}`);
|
||||
}
|
||||
const externalId = String(body.id ?? '');
|
||||
|
||||
Reference in New Issue
Block a user