Back to Napcraft
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.

tests/shared-world.mjs
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();
}