SOURCE / PINNED RELEASE
Made of little things.
Impact Yard
- Release
- 09fe6aebec3c…
- Author-recorded commit
- 0696a4211733…
- License
- LICENSE
- Author’s source reference
- nostr://npub1n8ga89w8h6tvwamxusfyzexw8gjy84yxu9rxgnmk955cxtml4ujswzxydd/wss%3A%2F%2Fgit.napplet.soy%2F/n-0b3d7b60562
Archive hash verified: 823f5de0d2677ef2…. The source-to-build association is the author’s claim; it has not been independently rebuilt.
import assert from 'node:assert/strict';
import { createRequire } from 'node:module';
import { mkdir, readFile, writeFile } from 'node:fs/promises';
const require = createRequire(import.meta.resolve('@napplet/conformance-cli/package.json'));
const { chromium } = require('playwright');
const outDir = '/tmp/impact-yard-checks';
await mkdir(outDir, { recursive: true });
const browser = await chromium.launch({ headless: true });
const report = { errors: [], checks: [], frames: [] };
try {
const page = await browser.newPage({ viewport: { width: 1440, height: 1000 }, deviceScaleFactor: 1 });
page.on('pageerror', error => report.errors.push(error.message));
await page.goto(process.env.SIM_PREVIEW_URL || 'http://127.0.0.1:4184/');
const iframe = page.locator('iframe'); await iframe.waitFor();
const app = page.frames().find(frame => frame !== page.mainFrame());
await app.locator('#scene').waitFor();
await page.waitForTimeout(800);
assert.equal(await app.locator('#retry').isVisible(), false);
assert.equal(await app.locator('#structure-count').innerText(), '7');
assert.equal(await app.locator('.structure').count(), 7);
assert.match(await app.locator('#prop-total').innerText(), /157/);
for (const name of ['Domino run', 'Timber tower', 'Smash arch']) assert.ok((await app.locator('#structure-list').innerText()).includes(name));
report.checks.push({ structures: 7, movablePieces: 157 });
const injected = await app.evaluate(() => Object.keys(window.napplet ?? {}));
report.checks.push({ host: 'allow-scripts', injected });
await app.locator('#camera').click(); await page.waitForTimeout(200);
assert.equal(await app.locator('#camera-label').innerText(), 'Orbit camera');
await page.reload(); await page.waitForTimeout(1200);
const restored = page.frames().find(frame => frame !== page.mainFrame());
assert.equal(await restored.locator('#camera-label').innerText(), 'Orbit camera');
report.checks.push({ cameraPersistence: await restored.locator('#camera-label').innerText() });
// Subsequent checks use the reloaded runtime frame.
const game = restored;
await game.locator('#rebuild').click();
await game.locator('#scene').click({ position: { x: 700, y: 400 } });
await page.keyboard.down('w');
await page.waitForTimeout(1600);
const speed = Number(await game.locator('#speed').innerText()); assert.ok(speed > 5, `accelerating: ${speed}`);
await page.keyboard.up('w');
await page.keyboard.down('Space'); await page.waitForTimeout(1500); await page.keyboard.up('Space');
const braked = Number(await game.locator('#speed').innerText()); assert.ok(braked < speed, `braking: ${braked}`);
report.checks.push({ acceleration: speed, braking: braked });
await game.locator('#pause').click();
assert.equal(await game.locator('#paused-notice').isVisible(), true);
await game.locator('#resume').click();
assert.equal(await game.locator('#paused-notice').isVisible(), false);
await game.locator('#slow').click(); assert.equal(await game.locator('#slow').getAttribute('aria-pressed'), 'true');
await game.locator('#slow').click();
await game.locator('#rebuild').click();
await game.locator('#scene').click({ position: { x: 700, y: 400 } });
await page.keyboard.down('w'); await page.waitForTimeout(6000); await page.keyboard.up('w');
const displaced = Number(await game.locator('#displaced').innerText());
assert.ok(displaced > 0, `collision displaced ${displaced}`);
report.checks.push({ collisionDisplaced: displaced });
await iframe.screenshot({ path: `${outDir}/collision.png` });
await game.locator('#rebuild').click(); await page.waitForTimeout(600);
assert.equal(Number(await game.locator('#displaced').innerText()), 0);
await game.locator('#camera').click(); await game.locator('#camera').click();
report.checks.push({ renderFps: await game.evaluate(() => new Promise(resolve => {
let start = performance.now(), frames = 0;
function tick(now) { frames++; if (now - start > 1000) resolve(Math.round(frames * 1000 / (now - start))); else requestAnimationFrame(tick); }
requestAnimationFrame(tick);
})) });
// Keep the actual runtime alive while resizing its frame; state must survive.
for (const [themeName, colors] of [['dark', { background: '#182420', text: '#edf2e9', primary: '#e4ac6b' }], ['light', { background: '#f5f3e9', text: '#28352c', primary: '#367c66' }]]) {
// Exercise the installed runtime's documented theme push from the parent frame.
await iframe.evaluate((el, colors) => el.contentWindow.postMessage({ type: 'theme.changed', theme: { colors } }, '*'), colors);
await page.waitForTimeout(150);
assert.equal(await game.evaluate(() => document.documentElement.style.getPropertyValue('--bg')), colors.background);
for (const [width, height] of [[200, 160], [320, 560], [900, 600], [2400, 1200]]) {
await page.setViewportSize({ width: Math.max(width, 480), height: height + 140 });
await iframe.evaluate((el, size) => { el.style.width = `${size[0]}px`; el.style.height = `${size[1]}px`; el.style.minHeight = '0'; el.style.minWidth = '0'; el.style.flex = 'none'; }, [width, height]);
await page.waitForTimeout(250);
const layout = await game.evaluate(() => {
const root = document.documentElement;
const controls = [...document.querySelectorAll('button')].filter(el => el.getClientRects().length > 0).map(el => {
const r = el.getBoundingClientRect(); return { label: el.getAttribute('aria-label') || el.textContent, x: r.x, y: r.y, right: r.right, bottom: r.bottom };
});
return { w: innerWidth, h: innerHeight, scrollW: root.scrollWidth, scrollH: root.scrollHeight, controls, bg: getComputedStyle(root).backgroundColor };
});
assert.equal(layout.w, width); assert.equal(layout.h, height);
assert.ok(layout.scrollW <= width && layout.scrollH <= height, `overflow at ${width}×${height}`);
for (const control of layout.controls) assert.ok(control.x >= 0 && control.y >= 0 && control.right <= width + 1 && control.bottom <= height + 1, `clipped ${control.label} at ${width}×${height}`);
report.frames.push({ themeName, ...layout });
await iframe.screenshot({ path: `${outDir}/${themeName}-${width}x${height}.png` });
}
}
await page.setViewportSize({ width: 480, height: 700 });
await iframe.evaluate(el => { el.style.width = '320px'; el.style.height = '560px'; });
await game.locator('#rebuild').click();
const pedal = await game.getByRole('button', { name: 'Accelerate', exact: true }).boundingBox();
await page.mouse.move(pedal.x + pedal.width / 2, pedal.y + pedal.height / 2);
await page.mouse.down(); await page.waitForTimeout(1200); await page.mouse.up();
assert.ok(Number(await game.locator('#speed').innerText()) > 5);
assert.equal(await game.locator('.pressed').count(), 0);
report.checks.push({ pointerDriving: 'accelerates and releases' });
// Drive toward both new side obstacles to inspect their actual rendered meshes.
await page.setViewportSize({ width: 1440, height: 1000 });
await iframe.evaluate(el => { el.style.width = '1440px'; el.style.height = '860px'; });
for (const [direction, name] of [['a', 'domino-run'], ['d', 'timber-tower']]) {
await game.locator('#rebuild').click();
await game.locator('#scene').click({ position: { x: 700, y: 400 } });
await page.keyboard.down('w'); await page.keyboard.down(direction);
await page.waitForTimeout(1000); await page.keyboard.up(direction);
await page.waitForTimeout(400); await page.keyboard.up('w');
await game.locator('#pause').click();
await game.locator('#camera').click(); await game.locator('#camera').click();
await iframe.screenshot({ path: `${outDir}/${name}.png` });
await game.locator('#camera').click(); await game.locator('#pause').click();
}
const bare = await browser.newPage({ viewport: { width: 900, height: 600 } });
bare.on('pageerror', error => report.errors.push(error.message));
await bare.setContent('<iframe sandbox="allow-scripts" style="width:900px;height:600px;border:0"></iframe>');
await bare.locator('iframe').evaluate((el, html) => { el.srcdoc = html; }, await readFile(new URL('../dist/index.html', import.meta.url), 'utf8'));
await bare.waitForTimeout(1200);
const offline = bare.frames().find(frame => frame !== bare.mainFrame());
assert.equal(await offline.locator('#retry').isVisible(), false);
await offline.locator('#camera').click();
assert.equal(await offline.locator('#camera-label').innerText(), 'Orbit camera');
report.checks.push({ optionalDomainsAbsent: 'game and camera work' });
assert.deepEqual(report.errors, []);
await writeFile(`${outDir}/report.json`, JSON.stringify(report, null, 2));
console.log(JSON.stringify({ errors: report.errors, checks: report.checks, frames: report.frames.map(f => `${f.themeName} ${f.w}×${f.h}`), report: `${outDir}/report.json` }, null, 2));
} finally { await browser.close(); }