const { chromium } = require(process.env.PLAYWRIGHT_MODULE || 'playwright'); const assert = require('node:assert/strict'); const fs = require('node:fs'); const out = '.codex_tmp/coinmadness-preview'; fs.mkdirSync(out, { recursive: true }); (async () => { const browser = await chromium.launch({ headless: true, channel: process.env.BROWSER_CHANNEL || 'msedge' }); try { const page = await browser.newPage({ viewport: { width: 450, height: 800 } }); const errors = []; page.on('pageerror', error => errors.push(error.message)); await page.goto('http://127.0.0.1:' + (process.env.PORT || 8765)); await page.waitForFunction(() => window.demo?.ready); const before = await page.evaluate(() => demo.snapshot()); assert.equal(before.bundle, false); assert.equal(before.panels, 0); await page.evaluate(() => demo.open()); await page.waitForTimeout(150); assert.equal((await page.evaluate(() => demo.snapshot())).panels, 1); const labels = await page.evaluate(() => cc.director.getScene().getComponentsInChildren(cc.Label).map(l => ({ text: l.string, width: l.node.width }))); assert.ok(await page.evaluate(() => !!demo.panel().node.getChildByName('Card').getChildByName('title').getComponent(cc.Sprite).spriteFrame)); assert.ok(labels.every(l => !l.text || l.width > 0)); assert.ok(await page.evaluate(() => demo.panel().node.getChildByName('Card') .getComponentsInChildren(cc.Label).every(label => !/[0-9]/.test(label.string))), 'All digits must use image glyphs'); assert.ok(await page.evaluate(() => ['timer_bg', 'close'].every(name => demo.panel().node.getChildByName('Card').getChildByName(name).getComponent(cc.Sprite).spriteFrame))); await page.screenshot({ path: out + '/unpaid.png' }); await page.evaluate(() => demo.action('purchase',{periodId:demo.data.state.config.periodId})); await page.waitForTimeout(300); for(let i=0;i<2;i++){await page.evaluate(()=>demo.action('simulate_win'));await page.waitForTimeout(300);} await page.waitForTimeout(100); await page.screenshot({ path: out + '/claimable.png' }); await page.evaluate(() => { const row = demo.panel().content.children[0]; for (let i = 0; i < 2; i++) row.emit(cc.Node.EventType.TOUCH_END, { stopPropagation() {} }); }); await page.waitForFunction(() => demo.data.state.stage === 1 && !demo.data.pending); assert.equal(await page.evaluate(() => cc.fx.GameConfig.GM_INFO.coin), 1000); assert.equal(await page.evaluate(() => demo.data.mockBalance), 1150); assert.equal(await page.evaluate(() => demo.data.state.progress), 2); await page.screenshot({ path: out + '/next-stage.png' }); for (let i = 0; i < 3; i++) { await page.evaluate(() => demo.action('simulate_win')); await page.waitForTimeout(300); } await page.screenshot({ path: out + '/art-ready.png' }); // Repeated refreshes must destroy old row/label nodes rather than only detaching them. const count = () => cc.director.getScene().getComponentsInChildren(cc.Label).length; const initialLabels = await page.evaluate(count); for (let i = 0; i < 5; i++) { await page.evaluate(() => demo.action('read')); await page.waitForTimeout(300); } assert.equal(await page.evaluate(count), initialLabels); assert.ok(await page.evaluate(() => demo.panel().countdown.frames.length === 10)); for (let i = 0; i < 10; i++) { await page.evaluate(() => demo.close()); await page.evaluate(() => demo.open()); } await page.evaluate(() => demo.close()); const after = await page.evaluate(() => demo.snapshot()); assert.deepEqual(after, before); assert.deepEqual(errors, []); // Destroy the host while a prefab request is in flight, as when leaving HomeScene. const late = await browser.newPage({ viewport: { width: 450, height: 800 } }); late.on('pageerror', error => errors.push(error.message)); await late.goto('http://127.0.0.1:' + (process.env.PORT || 8765)); await late.waitForFunction(() => window.demo?.ready); await late.route('**/coin_madness/import/**', async route => { await new Promise(r => setTimeout(r, 150)); await route.continue(); }); const loadingTexture = late.waitForRequest('**/coin_madness/import/**'); await late.evaluate(() => { demo.open(); }); await loadingTexture; await late.evaluate(() => cc.director.getScene().getComponentsInChildren(cc.Component).find(c => c.constructor.name === 'CoinMadnessHost' || cc.js.getClassName(c) === 'CoinMadnessHost').destroy()); await late.waitForFunction(() => !demo.snapshot().bundle && demo.snapshot().panels === 0, null, { timeout: 5000 }); assert.equal((await late.evaluate(() => demo.snapshot())).bundle, false); assert.equal((await late.evaluate(() => demo.snapshot())).panels, 0); assert.deepEqual(errors, []); console.log(JSON.stringify({ passed: true, before, after, cycles: 10, lateLoadReleased: true, doubleClickClaimCoins: 150, screenshots: out }, null, 2)); } finally { await browser.close(); } })().catch(error => { console.error(error); process.exitCode = 1; });