'use strict'; const assert = require('node:assert/strict'); module.exports = async function (url, cookie) { const { chromium } = require('playwright'); const browser = await chromium.launch({ channel: process.env.QA_BROWSER_CHANNEL || 'msedge', headless: true }); const context = await browser.newContext({ viewport: { width: 390, height: 844 }, isMobile: true, hasTouch: true }); const [name, ...value] = cookie.split('='); await context.addCookies([{ name, value: value.join('='), url }]); try { const api = async path => { const response = await context.request.get(url + path); assert.equal(response.ok(), true, await response.text()); return response.json(); }; const inventory = await api('/api/library'); const pick = async title => { const manga = inventory.mangas.find(item => item.title === title); const detail = await api('/api/manga?m=' + encodeURIComponent(manga.id)); const chapter = detail.chapters[0]; return { manga, chapter, data: await api('/api/chapter?m=' + encodeURIComponent(manga.id) + '&c=' + encodeURIComponent(chapter.id)) }; }; const strip = await pick('Moon Garden'); const printed = await pick('Before the Rain'); assert.equal(strip.data.direction, 'ttb'); assert.equal(printed.data.direction, 'ltr'); const page = await context.newPage(); await page.goto(url + '/read.html?m=' + encodeURIComponent(strip.manga.id) + '&c=' + encodeURIComponent(strip.chapter.id)); await page.waitForFunction(() => document.querySelectorAll('#canvas img').length === 4); const widths = await page.locator('#canvas img').evaluateAll(images => images.map(img => Math.round(img.getBoundingClientRect().width))); assert.equal(widths.length, 4); assert.ok(widths.every(width => Math.abs(width - widths[1]) <= 1), `Flow widths differ: ${widths.join(', ')}`); console.log('PASS: mobile flow gives the landscape credits card the same width as detected webtoon strips; printed manual LTR remains LTR'); } finally { await context.close(); await browser.close(); } };