Files
MatchMaster/tools/check-shared-popup-backdrop-render.cjs

126 lines
10 KiB
JavaScript

// Standalone Creator pixel regression. All browser requests are served from local files;
// no preview server, game login, or activity API is used.
// Optional: COCOS_CREATOR_PATH, PLAYWRIGHT_MODULE, PNGJS_MODULE, BROWSER_CHANNEL.
const assert = require('node:assert/strict');
const fs = require('node:fs');
const path = require('node:path');
const ts = require('typescript');
const { chromium } = require(process.env.PLAYWRIGHT_MODULE || 'playwright');
const { PNG } = require(process.env.PNGJS_MODULE || 'pngjs');
const homeScene = JSON.parse(fs.readFileSync('assets/Scene/HomeScene.fire'));
const authoredLoading = homeScene.find(n => n.__type__ === 'cc.Node' && n._name === 'Loading' && homeScene[n._parent?.__id__]?._name === 'Canvas');
const artwork = node => ({ name: node._name, x: node._trs.array[0], y: node._trs.array[1], opacity: node._opacity,
width: node._contentSize.width, height: node._contentSize.height,
components: node._components.map(ref => homeScene[ref.__id__]).filter(c => ['cc.Sprite','cc.Label'].includes(c.__type__)),
children: node._children.map(ref => artwork(homeScene[ref.__id__])) });
const loadingArtwork = authoredLoading._children.map(ref => homeScene[ref.__id__]).filter(n => n._name !== 'New Sprite(Splash)').map(artwork);
(async () => {
const browser = await chromium.launch({ headless: true, channel: process.env.BROWSER_CHANNEL || 'msedge' });
try {
const page = await browser.newPage({ viewport: { width: 360, height: 640 } });
const errors = [];
page.on('pageerror', error => { errors.push(error.message); if (errors.length < 6) console.error('PAGE ERROR', error.message); });
page.on('console', message => { if (message.type() === 'error') console.error('BROWSER', message.text()); });
const creator = path.join(process.env.COCOS_CREATOR_PATH || 'C:/ProgramData/cocos/editors/Creator/2.4.15', 'resources');
const resident = (name, dependency) => {
const file = 'assets/Script/' + name + '.ts';
const uuid = JSON.parse(fs.readFileSync(file + '.meta')).uuid;
const hex = uuid.replaceAll('-', ''), alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
const id = hex.slice(0,5) + hex.slice(5).match(/.{3}/g).map(s => alphabet[parseInt(s,16)>>6] + alphabet[parseInt(s,16)&63]).join('');
const code = ts.transpileModule(fs.readFileSync(file,'utf8'), {compilerOptions:{target:1,module:1,experimentalDecorators:true}}).outputText;
return '(function(){var exports={};function require(){return '+dependency+';}cc._RF.push({},'+JSON.stringify(id)+','+JSON.stringify(name)+');'+code+';cc._RF.pop();return exports;})()';
};
await page.route('http://127.0.0.1:8765/**', route => {
const url = new URL(route.request().url()).pathname.replace(/^\/assets\//, '/');
if (url === '/host.js') return route.fulfill({contentType:'text/javascript', body:'var animationExports='+resident('LoadingCatAnimation','null')+';var backdropExports='+resident('SharedPopupBackdrop','animationExports')+';window.fixtureBackdropSource='+resident('PopupBackdropSource','backdropExports')+';'});
const file = url === '/engine.js' ? path.join(creator,'engine/bin/cocos2d-js-for-preview.js')
: url.startsWith('/internal/') ? path.join(creator,'cocos2d-x/simulator/win32/assets',url)
: path.join('library',url.replace(/^\/native\//, '/imports/'));
if (!fs.existsSync(file)) { console.error('MISSING',url); return route.fulfill({status:404,body:'missing'}); }
return route.fulfill({path:file});
});
await page.route('http://127.0.0.1:8765/backdrop-check', route => route.fulfill({
contentType: 'text/html; charset=utf-8', body: `<html><meta charset="utf-8"><style>html,body{margin:0}canvas{display:block}</style><canvas id="GameCanvas"></canvas>
<script src="/engine.js"></script><script src="/host.js"></script><script>
cc.assetManager.init({importBase:'/imports',nativeBase:'/native'});
cc.assetManager.loadBundle('/internal',error=>{if(error)throw error;
cc.game.run({id:'GameCanvas',debugMode:1,frameRate:30,renderMode:2,groupList:['default'],collisionMatrix:[[true]]},()=>{
cc.view.setDesignResolutionSize(1080,1920,cc.ResolutionPolicy.SHOW_ALL);
const scene=new cc.Scene(),canvas=new cc.Node('Canvas');scene.addChild(canvas);canvas.setContentSize(1080,1920);
canvas.addComponent(cc.Canvas).designResolution=cc.size(1080,1920);cc.director.runScene(scene);
const white=new cc.Node('White');canvas.addChild(white);const g=white.addComponent(cc.Graphics);
g.fillColor=cc.Color.WHITE;g.rect(-540,-960,1080,1920);g.fill();
const texture=new cc.Texture2D();texture.initWithData(new Uint8Array([255,255,255,255]),cc.Texture2D.PixelFormat.RGBA8888,1,1);
const frame=new cc.SpriteFrame(texture);
const add=(name,z,opacity)=>{const root=new cc.Node(name);canvas.addChild(root,z);
const mask=new cc.Node('Mask');root.addChild(mask);mask.color=cc.Color.BLACK;mask.opacity=opacity;
const sprite=mask.addComponent(cc.Sprite);sprite.spriteFrame=frame;sprite.sizeMode=cc.Sprite.SizeMode.CUSTOM;mask.setContentSize(1080,1920);
mask.addComponent(window.fixtureBackdropSource.default);return {root,mask,sprite};};
window.check={canvas,add,owner:{},loading:add('Loading',32767,200)};
const assets=[];
const makeArtwork=data=>{
const node=new cc.Node(data.name);node.setPosition(data.x,data.y);node.opacity=data.opacity;node.setContentSize(data.width,data.height);
data.components.forEach(c=>{
if(c.__type__==='cc.Label'){const label=node.addComponent(cc.Label);label.string=c._string;label.fontSize=c._fontSize;label.lineHeight=c._lineHeight;}
if(c.__type__==='cc.Sprite'){const sprite=node.addComponent(cc.Sprite);assets.push(new Promise((resolve,reject)=>cc.assetManager.loadAny(c._spriteFrame.__uuid__,(error,frame)=>{if(error)return reject(error);sprite.spriteFrame=frame;resolve();})));}
});
data.children.forEach(child=>node.addChild(makeArtwork(child)));return node;
};
${JSON.stringify(loadingArtwork)}.forEach(data=>check.loading.root.addChild(makeArtwork(data)));
cc.director.once(cc.Director.EVENT_AFTER_DRAW,()=>{
Promise.all(assets).then(()=>{
check.manager=canvas.getComponent('SharedPopupBackdrop');
check.manager.hold(check.owner,()=>{});window.ready=true;
});
});
});});</script></html>`
}));
await page.goto('http://127.0.0.1:8765/backdrop-check');
await page.waitForFunction(() => window.ready, null, { timeout: 15000 });
const results = [];
async function verify(stage, expected = 55) {
await page.evaluate(() => new Promise(resolve => cc.director.once(cc.Director.EVENT_AFTER_DRAW, resolve)));
const png = PNG.sync.read(await page.screenshot());
const offset = (30 * png.width + 30) * 4;
const rgb = Array.from(png.data.slice(offset, offset + 3));
results.push({ stage, rgb });
assert.ok(rgb.every(c => Math.abs(c - expected) <= 1), stage + ': ' + rgb);
assert.equal(await page.evaluate(() => Object.values(check).filter(x => x && x.sprite).some(x => x.root.activeInHierarchy && x.sprite.enabled)), false);
}
await verify('loading');
await page.evaluate(() => { check.loading.root.active = false; });
await verify('bridge');
await page.waitForTimeout(900);
await verify('bridge with cat loading');
const loadingState = await page.evaluate(() => {
const content=check.manager.message;
return { active:content.active, children:content.children.map(n=>n.name), text:content.getChildByName('New Label').getComponent(cc.Label).string,
y:content.getChildByName('cat').y, runId:content.__loadingCatAnimationState.runId };
});
assert.equal(loadingState.active,true);
assert.equal(loadingState.text,'加载中,请稍等喵~');
assert.equal(loadingState.children.includes('Mask'),false);
assert.equal(loadingState.children.includes('New Sprite(Splash)'),false);
await page.waitForTimeout(230);
const after = await page.evaluate(() => ({y:check.manager.message.getChildByName('cat').y,runId:check.manager.message.__loadingCatAnimationState.runId}));
assert.notEqual(after.y,loadingState.y,'cat actually animates in Creator');
assert.equal(after.runId,loadingState.runId,'animation must not restart every frame');
if (process.env.LOADING_SCREENSHOT) await page.screenshot({path:process.env.LOADING_SCREENSHOT});
await page.evaluate(() => { check.main = check.add('CoinMadness',32766,175); });
await verify('activity');
assert.equal(await page.evaluate(()=>check.manager.message.active),false);
await page.evaluate(() => { check.loading.root.active = true; });
await verify('loading above activity');
await page.evaluate(() => { check.loading.root.active = false; check.help = check.add('Help',32767,190); });
await verify('help above activity');
await page.evaluate(() => { check.help.root.active = false; });
await verify('activity restored');
await page.evaluate(() => { check.main.root.active = false; check.manager.release(check.owner); });
await page.waitForTimeout(250);
await verify('closed',255);
assert.deepEqual(errors,[]);
console.log(JSON.stringify({passed:true,renderer:'Cocos Creator 2.4.15 WebGL',results}));
} finally { await browser.close(); }
})().catch(error => { console.error(error); process.exitCode = 1; });