OverviewFilesCommitsIssuesDocsReleasesPeers

favela-web / test / static-assets.test.mjs

66 lines · javascript ref:
// The browser scripts survive bundling — and stay in step with the files they were generated from.
//
// ⛔ THE BUG THIS GUARDS. lib/static.js serves web/static/ by reading the filesystem. That is correct on a
// server and impossible inside a bundle: bare-pack packs the JS module GRAPH, and these files are
// imported by nothing. Measured on the handset 2026-08-18 — /static/compose.js and /static/actions.js
// both 404 — so image attach, like, repost, follow, drafts and reporting had never worked on the phone
// since the WebView pivot. The markup arrived "for free" from favela-web; the behaviour did not.
//
// The generated module is the copy that reaches the phone. If it drifts from web/static/, the phone
// silently serves STALE JAVASCRIPT while the server serves the new file — a divergence that would be
// invisible from either side. So the drift check IS the test: a stale generated file must be a red
// suite, never a mystery on a handset.
//
// Runs under node (not bare) like the rest of the suite — which is possible precisely because the
// generated file is a plain module with no bare-* imports. lib/static.js itself cannot be imported here
// (`Bare is not defined`), so the fallback wiring is verified by these bytes plus a live fetch.
import { readFileSync, readdirSync, statSync } from 'node:fs'
import { join, dirname, relative } from 'node:path'
import { fileURLToPath } from 'node:url'
import { STATIC_ASSETS } from '../web/static-assets.js'

const ROOT = join(dirname(fileURLToPath(import.meta.url)), '..')
const STATIC_DIR = join(ROOT, 'web', 'static')

let pass = 0
const ok = (c, m) => { if (!c) { console.error('FAIL:', m); process.exit(1) } console.log('  ok -', m); pass++ }

const ALLOWED = new Set(['.js', '.css', '.json', '.map'])
const onDisk = []
const walk = (d) => {
  for (const name of readdirSync(d).sort()) {
    const abs = join(d, name)
    if (statSync(abs).isDirectory()) { walk(abs); continue }
    const dot = name.lastIndexOf('.')
    if (dot >= 0 && ALLOWED.has(name.slice(dot))) onDisk.push(relative(STATIC_DIR, abs).split('\\').join('/'))
  }
}
walk(STATIC_DIR)

ok(onDisk.length > 0, `web/static holds ${onDisk.length} servable files`)

// Every servable file is bundled. A file added to web/static and not regenerated is a 404 on every
// bundled surface while working perfectly on the developer's machine — the exact shape of the bug.
for (const rel of onDisk) {
  ok(Object.prototype.hasOwnProperty.call(STATIC_ASSETS, rel), `${rel} is in the bundle`)
}

// …and byte-identical, or the phone runs different code from the server.
for (const rel of onDisk) {
  const disk = readFileSync(join(STATIC_DIR, rel), 'utf8')
  ok(STATIC_ASSETS[rel] === disk, `${rel} is byte-identical to web/static/${rel}`)
}

// No stale entries: a file deleted from web/static must not keep being served from the bundle.
for (const key of Object.keys(STATIC_ASSETS)) {
  ok(onDisk.includes(key), `${key} still exists on disk (no orphan in the bundle)`)
}

// The two that the handset proved were missing, named explicitly — a regression here is not abstract.
ok((STATIC_ASSETS['compose.js'] || '').includes('img-controls'),
  'compose.js carries the picker reveal the phone never received')
ok((STATIC_ASSETS['actions.js'] || '').length > 0,
  'actions.js — like/repost/follow — is bundled')

console.log(`\nSTATIC-ASSETS OK: ${pass} assertions`)