// SPDX-License-Identifier: AGPL-3.0-or-later // The owner's 2026-10-21 features in a browser: // - Settings → Numbering: the users range starts at 1, so Next free suggests 1. // - Sounds & music: three files play in upload order; "Move … up" reorders them; Shuffle. // - Reports: answered, talk time, outside minutes (in and out), and the voip.ms balance tile // (a trunk without API login says what to add). // Calls for Reports go straight into the database (docker exec psql). // Runs after m1-t4.mjs. Usage: // PG_CONTAINER=selfphoneng-postgres-1 PG_DB= node scripts/e2e/numbers-music-reports.mjs \ // import assert from 'node:assert/strict'; import { spawnSync } from 'node:child_process'; import { launch } from './cdp.mjs'; const [base, email, pw, shotsDir] = process.argv.slice(2); const login = await fetch(`${base}/api/auth/login`, { method: 'POST', headers: { 'content-type': 'application/json', 'x-requested-with': 'selfphoneng' }, body: JSON.stringify({ email, password: pw }), }); const cookie = login.headers .getSetCookie() .map((c) => c.split(';')[0]) .join('; '); const api = async (method, url, body, type = 'application/json') => { const r = await fetch(`${base}${url}`, { method, headers: { cookie, 'x-requested-with': 'selfphoneng', ...(body && { 'content-type': type }) }, body: body && type === 'application/json' ? JSON.stringify(body) : body, }); const json = await r.json(); assert.ok(r.ok, `${method} ${url}: ${JSON.stringify(json)}`); return json; }; /** One second of a sine tone, 8 kHz mono 16-bit PCM. */ const tone = (hz) => { const n = 8000; const b = Buffer.alloc(44 + n * 2); b.write('RIFF', 0); b.writeUInt32LE(36 + n * 2, 4); b.write('WAVEfmt ', 8); b.writeUInt32LE(16, 16); b.writeUInt16LE(1, 20); b.writeUInt16LE(1, 22); b.writeUInt32LE(8000, 24); b.writeUInt32LE(16000, 28); b.writeUInt16LE(2, 32); b.writeUInt16LE(16, 34); b.write('data', 36); b.writeUInt32LE(n * 2, 40); for (let i = 0; i < n; i++) b.writeInt16LE(Math.round(8000 * Math.sin((2 * Math.PI * hz * i) / 8000)), 44 + i * 2); return b; }; const psql = (sql) => { const r = spawnSync( 'docker', [ 'exec', process.env.PG_CONTAINER, 'psql', '-U', 'selfphoneng', '-d', process.env.PG_DB, '-c', sql, ], { encoding: 'utf8' }, ); assert.equal(r.status, 0, r.stderr); }; const text = (s) => `document.body.innerText.includes(${JSON.stringify(s)})`; /** Types into an input found by its aria-label (the range grid has no