diff --git a/js/nostr/client.js b/js/nostr/client.js index c9abd471..5dda8a0b 100644 --- a/js/nostr/client.js +++ b/js/nostr/client.js @@ -2417,6 +2417,10 @@ export class NostrClient { return this.signerManager.ensureActiveSignerForPubkey(pubkey); } + async derivePrivateKeyFromSecret(secret) { + return this.signerManager.derivePrivateKeyFromSecret(secret); + } + async loginWithExtension(options) { // Check if ensureExtensionPermissions was overridden on this instance (e.g. by tests). // Only proxy when the own property differs from the prototype method to avoid recursion. diff --git a/scripts/agent/simple-relay.mjs b/scripts/agent/simple-relay.mjs index 2a847271..dd208e76 100644 --- a/scripts/agent/simple-relay.mjs +++ b/scripts/agent/simple-relay.mjs @@ -194,6 +194,11 @@ export function startRelay(port = 8888, options = {}) { return { close: () => { return new Promise((resolve) => { + // Force close all connected clients + for (const client of wss.clients) { + client.terminate(); + } + const closeHttp = httpServer ? new Promise((r) => httpServer.close(r)) : Promise.resolve(); diff --git a/scripts/check-sw-compat.mjs b/scripts/check-sw-compat.mjs index c16e8481..bac49bce 100644 --- a/scripts/check-sw-compat.mjs +++ b/scripts/check-sw-compat.mjs @@ -13,24 +13,29 @@ const CRITICAL_PATH_PATTERNS = [ ]; function getChangedFilesAgainstHeadParent() { - const hasHeadParent = execSync('git rev-parse --verify --quiet HEAD^', { - encoding: 'utf8', - stdio: ['ignore', 'pipe', 'ignore'] - }).trim(); + try { + const hasHeadParent = execSync('git rev-parse --verify --quiet HEAD^', { + encoding: 'utf8', + stdio: ['ignore', 'pipe', 'ignore'] + }).trim(); - if (!hasHeadParent) { + if (!hasHeadParent) { + return []; + } + + const output = execSync('git diff-tree --no-commit-id --name-only -r HEAD', { + encoding: 'utf8' + }).trim(); + + if (!output) { + return []; + } + + return output.split('\n').map((value) => value.trim()).filter(Boolean); + } catch (error) { + console.warn('[sw-compat] Skipping check: unable to determine HEAD^ or git history unavailable.'); return []; } - - const output = execSync('git diff-tree --no-commit-id --name-only -r HEAD', { - encoding: 'utf8' - }).trim(); - - if (!output) { - return []; - } - - return output.split('\n').map((value) => value.trim()).filter(Boolean); } function getCriticalPathDiff() { diff --git a/tests/e2e/helpers/bitvidTestFixture.ts b/tests/e2e/helpers/bitvidTestFixture.ts index c83e9e6a..467531a7 100644 --- a/tests/e2e/helpers/bitvidTestFixture.ts +++ b/tests/e2e/helpers/bitvidTestFixture.ts @@ -33,10 +33,7 @@ const TEST_PRIVATE_KEY_HEX = const TEST_SK = Uint8Array.from(Buffer.from(TEST_PRIVATE_KEY_HEX, "hex")); const TEST_PK = getPublicKey(TEST_SK); -const RELAY_PORT = 8877; -const RELAY_HTTP_PORT = 8878; -const RELAY_WS_URL = `ws://127.0.0.1:${RELAY_PORT}`; -const RELAY_HTTP_URL = `http://127.0.0.1:${RELAY_HTTP_PORT}`; +const BASE_RELAY_PORT = 8877; export interface TestVideoEvent { title: string; @@ -85,8 +82,8 @@ function createVideoEvent(video: TestVideoEvent) { /** * Seed an event into the relay via HTTP API. */ -async function seedEventViaHttp(event: any) { - const resp = await fetch(`${RELAY_HTTP_URL}/seed`, { +async function seedEventViaHttp(event: any, httpUrl: string) { + const resp = await fetch(`${httpUrl}/seed`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(event), @@ -100,8 +97,8 @@ async function seedEventViaHttp(event: any) { /** * Clear all events from the relay. */ -async function clearRelayEvents() { - const resp = await fetch(`${RELAY_HTTP_URL}/events`, { method: "DELETE" }); +async function clearRelayEvents(httpUrl: string) { + const resp = await fetch(`${httpUrl}/events`, { method: "DELETE" }); if (!resp.ok) { throw new Error(`Failed to clear events: ${resp.status}`); } @@ -110,9 +107,9 @@ async function clearRelayEvents() { /** * Navigate to bitvid with test mode and relay overrides enabled. */ -async function gotoWithTestMode(page: Page, path = "/") { +async function gotoWithTestMode(page: Page, relayUrl: string, path = "/") { const separator = path.includes("?") ? "&" : "?"; - const testUrl = `${path}${separator}__test__=1&__testRelays__=${encodeURIComponent(RELAY_WS_URL)}`; + const testUrl = `${path}${separator}__test__=1&__testRelays__=${encodeURIComponent(relayUrl)}`; // Set up localStorage before navigation await page.addInitScript(() => { @@ -163,8 +160,16 @@ type BitvidFixtures = { export const test = base.extend({ relay: [ - async ({}, use) => { - const relayInstance = startRelay(RELAY_PORT, { httpPort: RELAY_HTTP_PORT }); + async ({}, use, testInfo) => { + // Avoid port collisions when running in parallel workers + const port = BASE_RELAY_PORT + (testInfo.workerIndex * 2); + const httpPort = port + 1; + + const relayInstance = startRelay(port, { httpPort }); + // Attach the dynamic URLs to the relay instance for downstream fixtures + (relayInstance as any).wsUrl = `ws://127.0.0.1:${port}`; + (relayInstance as any).httpUrl = `http://127.0.0.1:${httpPort}`; + await use(relayInstance); await relayInstance.close(); }, @@ -174,25 +179,25 @@ export const test = base.extend({ seedEvent: async ({ relay }, use) => { await use(async (video: TestVideoEvent) => { const event = createVideoEvent(video); - return seedEventViaHttp(event); + return seedEventViaHttp(event, (relay as any).httpUrl); }); }, seedRawEvent: async ({ relay }, use) => { await use(async (event: any) => { - return seedEventViaHttp(event); + return seedEventViaHttp(event, (relay as any).httpUrl); }); }, clearRelay: async ({ relay }, use) => { await use(async () => { - await clearRelayEvents(); + await clearRelayEvents((relay as any).httpUrl); }); }, - gotoApp: async ({ page }, use) => { + gotoApp: async ({ page, relay }, use) => { await use(async (path = "/") => { - await gotoWithTestMode(page, path); + await gotoWithTestMode(page, (relay as any).wsUrl, path); }); }, @@ -204,7 +209,9 @@ export const test = base.extend({ testPubkey: TEST_PK, testPrivateKey: TEST_PRIVATE_KEY_HEX, - relayUrl: RELAY_WS_URL, + relayUrl: async ({ relay }, use) => { + await use((relay as any).wsUrl); + }, }); export { expect };