Back to Black Site
SOURCE / PINNED RELEASE

Made of little things.

Black Site

Release
666bc7c74ebd…
Author-recorded commit
05e95af79676…
License
LICENSE
Author’s source reference
nostr://npub12lm3rxsva8kfempyjkxupt09e02n3fezurkyadyqfejdc5e0r2kscqty6r/wss%3A%2F%2Fgit.napplet.soy%2F/n-d2225ddc391

Archive hash verified: 1544e072e245c4a8…. The source-to-build association is the author’s claim; it has not been independently rebuilt.

tests/player-ui.mjs
// Exercise the built app in an opaque-origin frame with controlled host responses.
import assert from 'node:assert/strict';
import { readFileSync } from 'node:fs';
import { createRequire } from 'node:module';
const requireHarness = createRequire(import.meta.resolve('@napplet/conformance-cli/package.json'));
const { chromium } = requireHarness('playwright');
const html = readFileSync(new URL('../dist/index.html', import.meta.url), 'utf8');
const png = 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mP8/x8AAwMCAO+aV1sAAAAASUVORK5CYII=';
const browser = await chromium.launch({ args: ['--no-sandbox'] });
try {
  for (const mode of ['avatar', 'no-resource', 'denied', 'stale-avatar', 'no-identity']) {
    const page = await browser.newPage({ viewport: { width: 900, height: 600 } });
    const errors = [];
    page.on('pageerror', error => errors.push(error.message));
    const fixture = `<script>
      const mode = ${JSON.stringify(mode)};
      window.napplet = {};
      window.fixtureClosed = false;
      window.fixtureReads = 0;
      window.fixtureRevoked = 0;
      const revoke = URL.revokeObjectURL.bind(URL);
      URL.revokeObjectURL = url => { window.fixtureRevoked++; revoke(url); };
      if (mode !== 'no-identity') {
        window.napplet.identity = {
          getPublicKey: async () => 'a'.repeat(64),
          onChanged: callback => { window.fixtureChange = callback; return { close: () => { window.fixtureClosed = true; } }; }
        };
        window.napplet.common = { getProfile: async key => ({ ok: true, pubkey: key, profile: {
          display_name: key === 'a'.repeat(64) ? 'Alice Player' : 'Bob Player', picture: 'https://example.com/avatar.png'
        } }) };
      }
      if (mode !== 'no-resource') window.napplet.resource = { bytes: async () => {
        window.fixtureReads++;
        if (mode === 'denied') throw new Error('blocked-by-policy');
        if (mode === 'stale-avatar') await new Promise(resolve => { window.fixtureResolveAvatar = resolve; });
        return new Blob([Uint8Array.from(atob(${JSON.stringify(png)}), c => c.charCodeAt(0))], { type: 'image/png' });
      } };
    </script>`;
    await page.setContent('<iframe sandbox="allow-scripts" style="position:fixed;inset:0;border:0;width:100%;height:100%"></iframe>');
    await page.locator('iframe').evaluate((iframe, source) => { iframe.srcdoc = source; }, html.replace('<head>', '<head>' + fixture));
    const frame = await (await page.locator('iframe').elementHandle()).contentFrame();
    await frame.locator('#menu.on').waitFor();
    const label = mode === 'no-identity' ? 'Guest player' : 'Alice Player';
    await frame.waitForFunction(text => document.querySelector('.player-label')?.textContent === text, label);
    assert.equal(await frame.locator('#menu .hint').count(), 0);
    assert.ok(await frame.locator('#currentUser').evaluate(el => el.getBoundingClientRect().width) >= 420);
    assert.doesNotMatch(await frame.locator('#currentUser').innerText(), /nostr|refresh/i);
    if (mode === 'avatar') {
      await frame.locator('#currentUser img').waitFor({ state: 'attached' });
      assert.equal(await frame.locator('#currentUser img').evaluate(img => img.complete && img.naturalWidth > 0), true);
      assert.match(await frame.locator('#currentUser img').getAttribute('src'), /^blob:/);
      assert.equal(await frame.evaluate(() => window.fixtureReads), 1);
    } else {
      assert.equal(await frame.locator('#currentUser img').count(), 0);
    }
    await frame.locator('#currentUser').click();
    await frame.locator('#playerAccount.on').waitFor();
    assert.doesNotMatch(await frame.locator('#playerAccount').innerText(), /nostr|pubkey|refresh|host/i);
    await frame.waitForFunction(() => document.querySelector('#playerAccount .sync-warning'));
    assert.match(await frame.locator('#playerAccount .sync-warning:visible').innerText(), /runtime.*features needed for sync/);
    assert.equal(await frame.locator('#playerAccount .armory-sync button:visible').count(), 0, 'unsupported runtimes show no sync actions');
    await page.keyboard.press('Escape');
    await frame.locator('#menu.on').waitFor();
    await frame.locator('#helpButton').click();
    await frame.locator('#howToPlay.on').waitFor();
    assert.match(await frame.locator('#howToPlay').innerText(), /LEFT CLICK/);
    await frame.locator('#howToPlay [data-go=menu]').click();
    if (mode !== 'no-identity') {
      await frame.evaluate(() => window.fixtureChange(''));
      if (mode === 'stale-avatar') await frame.evaluate(() => window.fixtureResolveAvatar());
      await frame.waitForFunction(() => document.querySelector('.player-label').textContent === 'Guest player');
      assert.equal(await frame.locator('#currentUser img').count(), 0);
      if (mode === 'avatar') assert.equal(await frame.evaluate(() => window.fixtureRevoked), 1);
      await frame.evaluate(() => window.dispatchEvent(new Event('pagehide')));
      assert.equal(await frame.evaluate(() => window.fixtureClosed), true);
    }
    assert.deepEqual(errors, []);
    console.log(`PASS player UI: ${mode}`);
    await page.close();
  }
} finally { await browser.close(); }