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 }))); const activityTitle = await page.evaluate(() => demo.data.state.config.title); assert.ok(labels.some(l => l.text === activityTitle && l.width > 0)); assert.ok(labels.every(l => !l.text || l.width > 0)); 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 label = cc.director.getScene().getComponentsInChildren(cc.Label).find(l => l.string === '领取'); for (let i = 0; i < 2; i++) label.node.parent.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), 0); await page.screenshot({ path: out + '/next-stage.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); 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(); }); await late.evaluate(() => { demo.open(); }); await late.waitForTimeout(50); await late.evaluate(() => cc.director.getScene().getComponentsInChildren(cc.Component).find(c => c.constructor.name === 'CoinMadnessHost' || cc.js.getClassName(c) === 'CoinMadnessHost').destroy()); await late.waitForTimeout(300); 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; });