126 lines
10 KiB
JavaScript
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; });
|