const { chromium } = require('playwright'); const fs = require('fs'); const path = require('path'); const base = process.env.REVIEW_URL; const password = process.env.REVIEW_PASSWORD; const shots = process.env.REVIEW_SCREENSHOTS; const siteRoot = process.env.REVIEW_SITE; const errors = []; function assert(condition, message) { if (!condition) throw new Error(message); } async function openPageList(browser) { const context = await browser.newContext({ viewport: { width: 1280, height: 900 } }); const page = await context.newPage(); page.on('pageerror', error => errors.push(error.message)); await page.goto(base + '/admin/index.php'); await page.locator('#username').fill('admin'); await page.locator('#password').fill(password); await Promise.all([page.waitForURL('**/admin/dashboard.php'), page.locator('form').first().locator('button[type=submit]').click()]); await page.locator('.sidebar-nav-item[data-tab="content"]').click(); await page.locator('#pageListBody .page-list-row').first().waitFor({ state: 'visible', timeout: 20000 }); return { context, page }; } const rows = page => page.locator('#pageListBody .page-list-row'); (async () => { const browser = await chromium.launch({ headless: true }); try { let { context, page } = await openPageList(browser); // The view language is German (the site's primary language): one row per page, each with its own German path await page.selectOption('#pageListLang', 'de'); assert(await rows(page).count() === 4, 'four pages, one row each: ' + await rows(page).count()); const slugs = await page.locator('#pageListBody .page-list-slug').allTextContents(); assert(slugs.includes('/leistungen') && slugs.includes('/ueber-uns') && !slugs.includes('/services'), 'the German paths are shown: ' + slugs); const leistungen = rows(page).filter({ hasText: 'Leistungen' }); // The English column offers "Edit" because the version exists under another path const editEnglish = leistungen.locator('.page-list-lang-btn'); assert(await editEnglish.count() === 1, '"Services" is the English version of "Leistungen", not a missing page'); await page.screenshot({ path: path.join(shots, 'page-list-de.png') }); await editEnglish.click(); await page.locator('#editorContainer').waitFor({ state: 'visible', timeout: 20000 }); assert(await page.locator('#langSelect').inputValue() === 'en' && await page.locator('#pageSelect').inputValue() === 'services', 'the English editor opens under its own path'); assert((await page.locator('#editorTitle').textContent()).trim() === 'Services', 'with the English title: ' + await page.locator('#editorTitle').textContent()); assert(page.url().includes('#page/en_services'), 'the editor link names the English version: ' + page.url()); assert((await page.locator('#editorViewBtn').getAttribute('href')) === '../en/services', 'the view link is the public English URL: ' + await page.locator('#editorViewBtn').getAttribute('href')); await page.screenshot({ path: path.join(shots, 'editor-en-services.png') }); // The language buttons follow the page: German leads to "leistungen", English back to "services" await page.selectOption('#langSelect', 'de'); assert(await page.locator('#pageSelect').inputValue() === 'leistungen', 'switching to German selects the German version: ' + await page.locator('#pageSelect').inputValue()); await page.locator('#topbarSelectors button', { hasText: /load|laden/i }).click(); await page.waitForFunction(() => document.getElementById('editorTitle').textContent.trim() === 'Leistungen', null, { timeout: 15000 }); await page.selectOption('#langSelect', 'en'); assert(await page.locator('#pageSelect').inputValue() === 'services', 'and back to English'); // A page that has only one version keeps its select value (nothing to translate to) await page.selectOption('#langSelect', 'de'); // Back in the list: the missing English version of "Über uns" is offered with the mapped path await page.evaluate(() => showPageList()); await page.locator('#pageListBody .page-list-row').first().waitFor({ state: 'visible' }); const about = rows(page).filter({ hasText: 'Über uns' }); // Columns beside German: English, Spanish (a third language the site has but no page uses yet) const create = about.locator('.page-list-cell-lang').nth(0).locator('.page-list-create-link'); assert(await create.count() === 1, 'a missing translation offers "create"'); await create.click(); await about.locator('.page-list-cell-lang').nth(0).locator('.page-list-lang-btn').waitFor({ state: 'visible', timeout: 15000 }); assert(fs.existsSync(path.join(siteRoot, 'content/pages/en_about-us.json')), 'the copy is stored under the path the mapping intends'); assert(!fs.existsSync(path.join(siteRoot, 'content/pages/en_ueber-uns.json')), 'not under the German path'); assert(await rows(page).count() === 4, 'creating the translation does not add a row'); await page.screenshot({ path: path.join(shots, 'page-list-after-create.png') }); // English view: same four pages, English paths, Edit leads to the German versions await page.selectOption('#pageListLang', 'en'); assert(await rows(page).count() === 4, 'four rows in English too'); const englishSlugs = await page.locator('#pageListBody .page-list-slug').allTextContents(); assert(englishSlugs.includes('/services') && englishSlugs.includes('/about-us') && !englishSlugs.includes('/leistungen'), 'the English paths are shown: ' + englishSlugs); await rows(page).filter({ hasText: 'Services' }).locator('.page-list-lang-btn').click(); await page.locator('#editorContainer').waitFor({ state: 'visible' }); assert(await page.locator('#langSelect').inputValue() === 'de' && await page.locator('#pageSelect').inputValue() === 'leistungen', 'the German version opens from the English list'); await context.close(); // A direct editor link works with the version's own path ({ context, page } = await openPageList(browser)); await page.goto(base + '/admin/dashboard.php#page/en_services'); await page.locator('#editorContainer').waitFor({ state: 'visible', timeout: 20000 }); assert(await page.locator('#langSelect').inputValue() === 'en' && await page.locator('#pageSelect').inputValue() === 'services', 'direct link: English version'); await page.goto(base + '/admin/dashboard.php#page/de_leistungen'); await page.reload(); await page.locator('#editorContainer').waitFor({ state: 'visible', timeout: 20000 }); assert(await page.locator('#langSelect').inputValue() === 'de' && await page.locator('#pageSelect').inputValue() === 'leistungen', 'direct link: German version'); assert(!errors.length, 'no script errors: ' + errors.join('; ')); await context.close(); console.log('page-list-check ok'); } finally { await browser.close(); } })().catch(error => { console.error(error); process.exit(1); });