import { chromium } from "playwright"; import fs from "node:fs/promises"; import path from "node:path"; const targetUrl = "https://create.onlineacademy.nl/8602e048-d7ee-4764-aafb-43412d0f65f3/edit?idsVersion=6"; const email = process.env.OA_EMAIL; const password = process.env.OA_PASSWORD; if (!email || !password) { throw new Error("Set OA_EMAIL and OA_PASSWORD before running this script."); } const outDir = path.resolve("artifacts/onlineacademy"); await fs.mkdir(outDir, { recursive: true }); const browser = await chromium.launch({ headless: false, slowMo: 150, }); const context = await browser.newContext({ viewport: { width: 1440, height: 960 }, }); const page = await context.newPage(); const requests = []; const payloads = {}; page.on("response", async (response) => { const request = response.request(); const resourceType = request.resourceType(); if (!["document", "fetch", "xhr"].includes(resourceType)) { return; } const url = response.url(); if (!url.includes("onlineacademy.nl")) { return; } requests.push({ method: request.method(), resourceType, status: response.status(), url, }); if (response.status() !== 200) { return; } if ( url.includes("/authoring/v1.0/contents/course/") || url.includes("/authoring/v1.0/contents/version?") ) { try { payloads[url] = await response.json(); } catch { payloads[url] = { parseError: true }; } } }); async function shot(name) { await page.screenshot({ path: path.join(outDir, name), fullPage: true, }); } async function saveState(name) { await fs.writeFile(path.join(outDir, name), await page.content(), "utf8"); } await page.goto(targetUrl, { waitUntil: "domcontentloaded" }); await page.waitForLoadState("networkidle").catch(() => {}); await shot("01-open.png"); await saveState("01-open.html"); const emailField = page.locator('input[type="email"], input[name="email"]'); if ((await emailField.count()) > 0) { await emailField.first().fill(email); const passwordField = page.locator('input[type="password"]'); await passwordField.first().fill(password); await shot("02-login-filled.png"); const submitButton = page.locator( 'button[type="submit"], button:has-text("Inloggen"), button:has-text("Log in")' ); await submitButton.first().click(); await page.waitForURL((url) => !url.href.startsWith("https://identity.onlineacademy.nl"), { timeout: 10000, }).catch(async () => { await passwordField.first().press("Enter").catch(() => {}); await page.waitForURL( (url) => !url.href.startsWith("https://identity.onlineacademy.nl"), { timeout: 10000 } ); }); await page.waitForLoadState("networkidle").catch(() => {}); } await page.waitForTimeout(3000); await shot("03-after-login.png"); await saveState("03-after-login.html"); const summary = await page.evaluate(() => { const text = (selector) => Array.from(document.querySelectorAll(selector)) .map((node) => node.textContent?.trim()) .filter(Boolean); const attrs = (selector, attr) => Array.from(document.querySelectorAll(selector)) .map((node) => node.getAttribute(attr)) .filter(Boolean); return { url: location.href, title: document.title, loginError: text(".input__error-message, .toast-manager, [role='alert']"), chapterButtons: text('[data-testid="page-button"]'), chapterTitles: attrs('.page__title input', 'placeholder'), leftActions: text(".sidebar-left-actions .button__text"), blockGroups: text(".dragblock-group__title"), blockLabels: text(".content-block__text"), blockTypes: attrs(".content-block[data-type]", "data-type"), settingsTabs: text('[role="tab"]'), }; }); await fs.writeFile( path.join(outDir, "summary.json"), JSON.stringify(summary, null, 2), "utf8" ); await fs.writeFile( path.join(outDir, "requests.json"), JSON.stringify(requests, null, 2), "utf8" ); await fs.writeFile( path.join(outDir, "payloads.json"), JSON.stringify(payloads, null, 2), "utf8" ); console.log(JSON.stringify(summary, null, 2)); console.log(JSON.stringify(requests, null, 2)); console.log(JSON.stringify(Object.keys(payloads), null, 2)); console.log(`Artifacts saved in ${outDir}`); await page.waitForTimeout(5000); await browser.close();