SOURCE / PINNED RELEASE
Made of little things.
Napcraft
- Release
- ee47ac6a25df…
- Author-recorded commit
- 410dea87e109…
- License
- LICENSE
- Author’s source reference
- nostr://npub182jczunncwe0jn6frpqwq3e0qjws7yqqnc3auccqv9nte2dnd63scjm4rf/wss%3A%2F%2Fgit.napplet.soy%2F/n-52f9e22f5ce
Archive hash verified: 9e6e0cabb18d0d46…. The source-to-build association is the author’s claim; it has not been independently rebuilt.
import {
chromium,
executable,
frameOf,
connectTestIdentity,
approveLocalPrompts,
fitFrame,
} from './browser-support.mjs';
import { readFile, writeFile, mkdir } from 'node:fs/promises';
import assert from 'node:assert/strict';
const browser = await chromium.launch({
executablePath: executable,
headless: true,
});
const backend = JSON.parse(
await readFile('.napplet-space/soy-backend.json', 'utf8'),
);
const results = [];
const check = (name, value) => {
assert.ok(value, name);
results.push(name);
console.log('PASS', name);
};
try {
const players = [];
for (let i = 0; i < 2; i++) {
const context = await browser.newContext({
viewport: { width: 1200, height: 900 },
});
const page = await context.newPage();
await page.goto(process.env.NAPCRAFT_URL || 'http://127.0.0.1:51696/');
const frame = await frameOf(page);
const stop = approveLocalPrompts(page);
players.push({ page, frame, stop });
}
const [a, b] = players;
await connectTestIdentity(a.page);
await a.frame.locator('#create').click();
await a.frame.locator('#name').fill('Cloudberry Club');
await a.frame.locator('#confirmCreate').click();
await a.frame.locator('#share').waitFor({ state: 'visible' });
await a.frame.locator('#share').click();
const code = await a.frame.locator('#shareCode').inputValue();
await a.frame.locator('#close').click();
const [release, instance] = JSON.parse(
Buffer.from(code.slice(4), 'base64url'),
);
const target = {
module: { napplet: backend.napplet, name: 'worlds' },
release,
instance,
};
await b.frame.locator('#join').click();
await b.frame.locator('#code').fill(code);
await b.frame.locator('#confirmJoin').click();
await b.frame.locator('#share').waitFor({ state: 'visible' });
check('independent guest joins persisted world', true);
await b.frame.locator('#place').click();
await a.frame.waitForFunction(
() => document.querySelector('canvas').dataset.edits === '1',
);
check('guest placement visible to creator', true);
await b.page.waitForTimeout(900);
await b.frame.locator('#remove').click();
await a.frame.waitForFunction(
() => document.querySelector('canvas').dataset.edits === '2',
);
check('guest removal visible to creator', true);
const invoke = (frame, intent) =>
frame.evaluate(
async (args) =>
window.napplet.cvm.registry.call(
'soy.backends.v1',
'soy_backend_invoke',
args,
),
intent,
);
const make = (operation, input) => ({
target,
operation,
input,
requestId: crypto.randomUUID(),
expiresAt: Math.floor(Date.now() / 1000) + 240,
});
const unauthorized = await invoke(
b.frame,
make('setLocked', { locked: true }),
);
check(
'guest cannot change ownership controls',
unauthorized.isError === true,
);
await a.frame.locator('#share').click();
await a.frame.locator('#lock').click();
await a.page.waitForTimeout(1300);
await b.frame.waitForFunction(() =>
document.querySelector('#saveStatus').textContent.includes('paused'),
);
check('verified owner pauses building', true);
await a.frame.locator('#share').click();
await a.frame.locator('#lock').click();
await b.frame.waitForFunction(
() => !document.querySelector('#saveStatus').textContent.includes('paused'),
);
check('verified owner resumes building', true);
let intent = make('editBlock', { x: 9, y: 3, z: 10, expected: 0, block: 5 });
let first = await invoke(b.frame, intent);
for (let i = 0; i < 3 && first.isError; i++) {
await b.page.waitForTimeout(300);
intent = make('editBlock', { x: 9, y: 3, z: 10, expected: 0, block: 5 });
first = await invoke(b.frame, intent);
}
assert.ok(!first.isError, JSON.stringify(first));
const second = await invoke(b.frame, intent);
check(
'retry returns same committed revision',
second.structuredContent.revision === first.structuredContent.revision,
);
const stale = await invoke(
b.frame,
make('editBlock', { x: 9, y: 3, z: 10, expected: 0, block: 4 }),
);
check('stale same-cell edit is rejected', stale.isError === true);
const race = await Promise.all([
invoke(
a.frame,
make('editBlock', { x: 9, y: 4, z: 10, expected: 0, block: 4 }),
),
invoke(
b.frame,
make('editBlock', { x: 9, y: 4, z: 10, expected: 0, block: 6 }),
),
]);
check(
'simultaneous same-cell edits have exactly one winner',
race.filter((r) => !r.isError).length === 1,
);
await a.frame.waitForFunction(
() => document.querySelector('canvas').dataset.edits === '4',
);
await writeFile(
'.napplet-space/persistence-world.json',
JSON.stringify({ code, edits: 4, target }),
);
await Promise.all(players.map((p) => p.page.reload()));
b.frame = await frameOf(b.page);
await b.frame.locator('#join').click();
await b.frame.locator('#code').fill(code);
await b.frame.locator('#confirmJoin').click();
await b.frame.waitForFunction(
() => document.querySelector('canvas').dataset.edits === '4',
);
check('world survives both players leaving and reopening', true);
await fitFrame(b.page, 1200, 750);
await mkdir('.napplet-space/promotion', { recursive: true });
await b.page
.locator('#stage iframe')
.screenshot({ path: '.napplet-space/promotion/shared-world.png' });
for (const p of players) p.stop();
} finally {
await writeFile(
'.napplet-space/shared-world-results.json',
JSON.stringify(results, null, 2),
);
await browser.close();
}
