import { chromium } from 'playwright' function evalEq(text) { const m = String(text || '').match(/(-?\d+)\s*([+\-×÷])\s*(-?\d+)/) if (!m) return null const a = Number(m[1]) const b = Number(m[3]) const op = m[2] if (op === '+') return a + b if (op === '-') return a - b if (op === '×') return a * b if (op === '÷') return a / b return null } const browser = await chromium.launch({ headless: true, channel: process.env.PW_CHANNEL || 'msedge' }) const page = await browser.newPage({ viewport: { width: 1280, height: 800 } }) await page.addInitScript(() => { localStorage.setItem('matematika_lang', 'sl') localStorage.setItem('matematika_settings', JSON.stringify({ name: 'Test', stevilo_racunov: 2, max_number: 20, sestevanje: true, odstevanje: false, mnozenje: false, deljenje: false, za_mnozenje: [2], za_deljenje: [2], difficulty: 'easy' })) }) const url = process.env.SMOKE_URL || 'http://127.0.0.1:4173/' await page.goto(url, { waitUntil: 'domcontentloaded' }) const continueBtn = page.getByRole('button', { name: 'Nadaljuj' }) await continueBtn.waitFor({ state: 'visible', timeout: 120000 }) await page.waitForFunction(() => { const btn = [...document.querySelectorAll('button')].find((b) => b.textContent.trim() === 'Nadaljuj') return btn && !btn.disabled }, null, { timeout: 120000 }) await continueBtn.click() await page.getByRole('button', { name: 'IGRAJ' }).first().click() await page.getByRole('button', { name: 'IGRAJ' }).last().click() await page.locator('.answer-tile').first().waitFor({ timeout: 15000 }) async function currentEq() { return page.locator('.font-extrabold.leading-tight').innerText() } async function clickTile(expected) { const tiles = page.locator('.answer-tile') const n = await tiles.count() for (let i = 0; i < n; i += 1) { const label = (await tiles.nth(i).innerText()).replace(/[^\d-]/g, '') if (Number(label) === expected) { await tiles.nth(i).click() return } } throw new Error(`No tile for ${expected}`) } async function answerCurrent({ wrongFirst = false } = {}) { const eq = await currentEq() const expected = evalEq(eq) if (expected === null) throw new Error(`Could not parse equation: ${eq}`) if (wrongFirst) { const tiles = page.locator('.answer-tile:not([disabled])') const n = await tiles.count() for (let i = 0; i < n; i += 1) { const label = (await tiles.nth(i).innerText()).replace(/[^\d-]/g, '') if (Number(label) !== expected) { await tiles.nth(i).click() await page.getByText(/Narobe/i).first().waitFor({ timeout: 3000 }) await page.waitForTimeout(400) break } } } await clickTile(expected) return eq } async function exitToSettings() { await page.getByRole('button', { name: 'Izhod' }).click() await page.locator('.fixed.inset-0').getByRole('button', { name: 'Izhod' }).click() await page.getByRole('button', { name: 'IGRAJ' }).first().waitFor({ timeout: 8000 }) } async function startFromSettings() { await page.getByRole('button', { name: 'IGRAJ' }).first().click() await page.getByRole('button', { name: 'IGRAJ' }).last().click() } const first = await answerCurrent() await page.getByText(/Bravo/i).first().waitFor({ timeout: 5000 }) await page.waitForTimeout(2400) const second = await answerCurrent() await page.getByRole('button', { name: 'Začni ponovno' }).waitFor({ timeout: 8000 }) await page.getByRole('button', { name: 'Začni ponovno' }).click() await page.locator('.answer-tile').first().waitFor({ timeout: 8000 }) const afterRestart = await currentEq() if (!/[=+]/.test(afterRestart)) { throw new Error(`Restart did not show a new question: ${afterRestart}`) } if (await page.getByRole('button', { name: 'Začni ponovno' }).isVisible().catch(() => false)) { throw new Error('Restart immediately showed game over') } const easyTiles = await page.locator('.answer-tile').count() if (easyTiles !== 4) throw new Error(`Easy should have 4 tiles, got ${easyTiles}`) await exitToSettings() await page.getByRole('button', { name: 'Srednje' }).click() await startFromSettings() await page.locator('.answer-tile').first().waitFor({ timeout: 15000 }) const mediumTiles = await page.locator('.answer-tile').count() if (mediumTiles !== 6) throw new Error(`Medium should have 6 tiles, got ${mediumTiles}`) await answerCurrent({ wrongFirst: true }) await page.getByText(/Bravo/i).first().waitFor({ timeout: 5000 }) await page.waitForTimeout(2400) await answerCurrent() await page.getByRole('button', { name: 'Začni ponovno' }).waitFor({ timeout: 8000 }) await page.getByRole('button', { name: 'Nastavitve' }).click() await page.getByRole('button', { name: 'IGRAJ' }).first().waitFor({ timeout: 8000 }) await page.getByRole('button', { name: 'Težko' }).click() await startFromSettings() await page.getByRole('button', { name: 'OK' }).waitFor({ timeout: 15000 }) if (await page.locator('.answer-tile').count()) { throw new Error('Hard should not show choice tiles') } if (await page.getByRole('button', { name: '±' }).count()) { throw new Error('± should be hidden when negatives are off') } if (await page.getByRole('button', { name: 'OK' }).isEnabled()) { throw new Error('OK should stay disabled on empty keypad') } const hardEq = await currentEq() const hardExpected = evalEq(hardEq) if (hardExpected === null) throw new Error(`Could not parse hard equation: ${hardEq}`) const digits = String(Math.abs(hardExpected)) for (const d of digits) { await page.getByRole('button', { name: d, exact: true }).click() } if (!(await page.getByRole('button', { name: 'OK' }).isEnabled())) { throw new Error('OK should enable after entering digits') } await page.getByRole('button', { name: 'OK' }).click() await page.getByText(/Bravo/i).first().waitFor({ timeout: 5000 }) await page.waitForTimeout(2400) const hardEq2 = await currentEq() const hardExpected2 = evalEq(hardEq2) if (hardExpected2 === null) throw new Error(`Could not parse hard equation 2: ${hardEq2}`) for (const d of String(Math.abs(hardExpected2))) { await page.getByRole('button', { name: d, exact: true }).click() } await page.getByRole('button', { name: 'OK' }).click() await page.getByRole('button', { name: 'Nastavitve' }).waitFor({ timeout: 8000 }) await page.setViewportSize({ width: 390, height: 844 }) await page.getByRole('button', { name: 'Nastavitve' }).click() await page.getByRole('button', { name: 'Lahko' }).click() await startFromSettings() await page.locator('.answer-tile').first().waitFor({ timeout: 15000 }) const mobileTiles = await page.locator('.answer-tile').count() if (mobileTiles !== 4) throw new Error(`Mobile easy should have 4 tiles, got ${mobileTiles}`) const tileBox = await page.locator('.answer-tile').first().boundingBox() if (!tileBox || tileBox.width < 80 || tileBox.height < 40) { throw new Error(`Mobile tiles too small: ${JSON.stringify(tileBox)}`) } if (tileBox.x + tileBox.width > 390) { throw new Error('Mobile tile overflows the viewport') } const eqBox = await page.locator('.font-extrabold.leading-tight').boundingBox() if (eqBox && eqBox.x + eqBox.width > 390) { throw new Error('Equation overflows the mobile viewport') } console.log('SMOKE_OK', { first, second, afterRestart, easyTiles, mediumTiles, hardEq, mobileTiles, tileBox }) await browser.close()