test: exercise gui smoke actions

This commit is contained in:
mirivlad 2026-06-04 19:39:27 +08:00
parent cb6c06fdc5
commit 2284f893f8
1 changed files with 35 additions and 2 deletions

View File

@ -13,7 +13,7 @@ const OUT_DIR = process.env.GUI_SMOKE_OUT || path.join(os.tmpdir(), 'verstak-gui
const CHROMIUM = process.env.CHROMIUM_BIN || findChromium()
const HOST = '127.0.0.1'
const flowUnderTest = 'app loads -> first meaningful screen renders -> primary visible controls respond without runtime errors.'
const flowUnderTest = 'app loads -> first meaningful screen renders -> primary visible controls respond and mutate UI state without runtime errors.'
if (!CHROMIUM) {
fail('Chromium executable not found. Set CHROMIUM_BIN or install chromium.')
@ -138,6 +138,11 @@ async function runReadyScenario(cdp, url) {
await clickText(cdp, '.tab', 'Заметки')
await assertText(cdp, 'Smoke note', 'notes: existing note visible')
await clickText(cdp, '.btn', '+ Добавить заметку')
await waitForSelector(cdp, '.create-form input[type="text"]')
await setInputValue(cdp, '.create-form input[type="text"]', 'GUI smoke note')
await clickText(cdp, '.create-form .btn', 'Создать')
await assertText(cdp, 'GUI smoke note', 'notes: created note appears')
await clickText(cdp, '.tab', 'Файлы')
await waitForSelector(cdp, '.file-row')
@ -146,12 +151,21 @@ async function runReadyScenario(cdp, url) {
await waitForSelector(cdp, '.back-btn')
await assertEval(cdp, `document.querySelector('.back-btn')?.innerText.trim() === 'Назад'`, 'files: back button has one textual label')
await screenshot(cdp, 'files-folder.png')
await click(cdp, '.back-btn')
await assertText(cdp, 'brief.md', 'files: back button returns to parent folder')
await clickText(cdp, '.tab', 'Действия')
await assertText(cdp, 'Deploy smoke', 'actions: action card visible')
await clickText(cdp, '.tab', 'Журнал')
await assertText(cdp, 'Manual smoke entry', 'worklog: entry visible')
await clickText(cdp, '.worklog-toolbar .btn', 'Добавить запись')
await waitForSelector(cdp, '.modal-worklog')
await setInputValue(cdp, '.modal-worklog input[type="text"]', 'GUI smoke worklog')
await setInputValue(cdp, '.modal-worklog input[type="number"]', '15')
await clickText(cdp, '.modal-worklog .btn', 'Сохранить')
await waitForGone(cdp, '.modal-worklog')
await assertText(cdp, 'GUI smoke worklog', 'worklog: created entry appears')
await clickText(cdp, '.tab', 'Активность')
await assertText(cdp, 'Smoke activity', 'activity: per-node activity visible')
@ -173,9 +187,12 @@ async function runReadyScenario(cdp, url) {
await click(cdp, '.nav-add-btn')
await waitForSelector(cdp, '.modal-create')
await assertText(cdp, 'Создать элемент', 'create node: modal opens')
await clickText(cdp, '.template-card', 'Пустое дело')
await setInputValue(cdp, '.modal-create input[type="text"]', 'GUI Smoke Created')
await screenshot(cdp, 'create-node-modal.png')
await clickText(cdp, '.modal-actions .btn', 'Отмена')
await clickText(cdp, '.modal-actions .btn', 'Создать')
await waitForGone(cdp, '.modal-create')
await assertText(cdp, 'GUI Smoke Created', 'create node: created node appears')
await setViewport(cdp, 390, 844)
await navigate(cdp, url)
@ -263,6 +280,22 @@ async function clickText(cdp, selector, text) {
await sleep(200)
}
async function setInputValue(cdp, selector, value) {
const ok = await evalValue(cdp, `
(() => {
const el = document.querySelector(${JSON.stringify(selector)});
if (!el) return false;
el.focus();
el.value = ${JSON.stringify(value)};
el.dispatchEvent(new Event('input', { bubbles: true }));
el.dispatchEvent(new Event('change', { bubbles: true }));
return true;
})()
`)
if (!ok) throw new Error(`Input target not found: ${selector}`)
await sleep(100)
}
async function clickFolderOpenButton(cdp, name) {
const ok = await evalValue(cdp, `
(() => {