const assert = require('node:assert/strict'); const fs = require('node:fs'); const ts = require('typescript'); const { chromium } = require('playwright'); const output = ts.transpileModule(fs.readFileSync('assets/Script/coin_madness/GoldMinerService.ts', 'utf8'), { compilerOptions: { target: ts.ScriptTarget.ES2017, module: ts.ModuleKind.CommonJS } }).outputText; const exported = {}; new Function('require', 'exports', output)(() => ({}), exported); const service = Object.create(exported.default.prototype); service.ledger = { notices: {} }; for (const status of ['purchasable', 'unlocked']) { service.info = { status, progressWins: 4, tasks: [{ targetWins: 5, claimStatus: 'locked' }] }; assert.equal(service.hasBadge, false); service.info.progressWins = 5; assert.equal(service.hasBadge, true); service.info.tasks[0].claimStatus = 'claimed'; assert.equal(service.hasBadge, false); } service.info.tasks[0].claimStatus = 'pending_unlock'; assert.equal(service.hasBadge, true); service.info = null; service.ledger.notices = { old: 100 }; assert.equal(service.hasBadge, true); (async () => { const browser = await chromium.launch({ headless: true, channel: 'msedge' }); try { const page = await browser.newPage({ viewport: { width: 450, height: 800 } }); page.on('pageerror', error => console.error('Preview:', error.message)); await page.goto('http://127.0.0.1:' + (process.env.PORT || 8879)); await page.waitForFunction(() => window.demo && demo.ready); await page.evaluate(() => { const data = { serverNow: Date.now(), status: 'purchasable', state: { config: { periodId: 'layout-test', startsAt: Date.now() - 1000, endsAt: Date.now() + 3600000, priceCents: 300, stages: Array.from({ length: 5 }, (_, i) => ({ wins: i + 1, coins: 1000 })) }, totalWins: 0, paidAt: 0, claimed: Array(5).fill(false), stage: 0 } }; window.testService = { info: { ...data.state.config, status: data.status }, settlementNotice: 0, serverNow: Date.now(), hasBadge: false, readForDisplay: async () => data, refresh: async () => data, view: () => data, stopPolling() {} }; cc.director.getScene().children[0].getComponent('CoinMadnessHost').resumeSync(); }); await page.evaluate(() => { demo.open(); }); await page.waitForFunction(() => cc.director.getScene().children[0].getChildByName('CoinMadness')?.getComponent('CoinMadnessPanel')?.data); for (const price of [3, 68, 128, 3]) { const result = await page.evaluate(price => { const panel = demo.panel(), data = JSON.parse(JSON.stringify(panel.data)); data.state.config.priceCents = price * 100; data.state.config.stages.forEach((s, i) => { s.coins = i ? 9999 : 45000; }); data.state.paidAt = 0; data.status = 'purchasable'; panel.render(data); function bounds(digits) { const boxes = digits.node.children.filter(n => n.active).map(n => { const image = n.getChildByName('Image'); return { left: n.x - image.width / 2, right: n.x + image.width / 2 }; }); return { left: boxes[0].left, right: boxes[boxes.length - 1].right, separated: boxes.every((b, i) => !i || b.left >= boxes[i - 1].right) }; } const p = bounds(panel.priceDigits), total = bounds(panel.totalDigits); return { total, price: p, priceRight: panel.priceDigits.node.x + p.right, currencyLeft: panel.currency.x - panel.currency.width / 2, currencyRight: panel.currency.x + panel.currency.width / 2, buttonRight: panel.purchaseButton.width / 2, rewards: panel.content.children.filter(n => n.active).map(n => bounds(n.getChildByName('Reward').getComponent('CoinMadnessDigits'))) }; }, price); assert(result.total.separated && result.total.right - result.total.left <= 164.01); assert(result.price.separated && result.priceRight < result.currencyLeft); assert(result.currencyRight < result.buttonRight); assert(result.rewards.every(r => r.separated && r.right - r.left <= 170.01)); fs.mkdirSync('.codex_tmp/number-layout', { recursive: true }); await page.screenshot({ path: '.codex_tmp/number-layout/price-' + price + '.png' }); } console.log('PASS: 4/5-digit rewards, 1/2/3-digit prices, repeated layout and unpaid completion badge'); } finally { await browser.close(); } })().catch(error => { console.error(error); process.exitCode = 1; });