From 3ecb901e3e77d5fffb4cdf555d6c97ef1d05c2bd Mon Sep 17 00:00:00 2001 From: sh <37271604+shumvgolove@users.noreply.github.com> Date: Mon, 2 Mar 2026 16:22:36 +0000 Subject: [PATCH] xftp-web: fix build and Playwright test failures (#1720) --- xftp-web/playwright.config.ts | 2 ++ xftp-web/test/page.spec.ts | 21 ++++++++++++++------- xftp-web/vite.config.ts | 14 +++++--------- xftp-web/web/main.ts | 11 ++++++----- 4 files changed, 27 insertions(+), 21 deletions(-) diff --git a/xftp-web/playwright.config.ts b/xftp-web/playwright.config.ts index bdfd579f8..5077ba1f8 100644 --- a/xftp-web/playwright.config.ts +++ b/xftp-web/playwright.config.ts @@ -8,10 +8,12 @@ export default defineConfig({ ignoreHTTPSErrors: true, launchOptions: { // --ignore-certificate-errors makes fetch() accept self-signed certs + // --disable-dev-shm-usage avoids crashes in Docker (default /dev/shm is 64MB) args: [ '--ignore-certificate-errors', '--ignore-certificate-errors-spki-list', '--allow-insecure-localhost', + '--disable-dev-shm-usage', ] } }, diff --git a/xftp-web/test/page.spec.ts b/xftp-web/test/page.spec.ts index 95d0cc376..3f0bba111 100644 --- a/xftp-web/test/page.spec.ts +++ b/xftp-web/test/page.spec.ts @@ -13,7 +13,8 @@ test.describe('Upload Flow', () => { await uploadPage.expectDropZoneVisible() await uploadPage.selectTextFile('picker-test.txt', 'test content ' + Date.now()) - await uploadPage.waitForEncrypting() + // Small files (< 100KB) skip "Encrypting" and go directly to "Uploading" + // await uploadPage.waitForEncrypting() await uploadPage.waitForUploading() const link = await uploadPage.waitForShareLink() @@ -96,14 +97,20 @@ test.describe('Upload Flow', () => { // ───────────────────────────────────────────────────────────────────────────── test.describe('Download Flow', () => { - test('download shows error for malformed hash', async ({downloadPage}) => { - await downloadPage.goto('#not-valid-base64!!!') - await downloadPage.expectInitialError(/[Ii]nvalid|corrupted/) - await downloadPage.expectDownloadButtonNotVisible() + test('non-XFTP anchor hash shows upload page', async ({page}) => { + await page.goto('http://localhost:4173#hello-world') + await expect(page.locator('#drop-zone')).toBeVisible() }) - test('download shows error for invalid structure', async ({downloadPage}) => { - await downloadPage.goto('#AAAA') + test('short non-XFTP hash shows upload page', async ({page}) => { + await page.goto('http://localhost:4173#AAAA') + await expect(page.locator('#drop-zone')).toBeVisible() + }) + + test('corrupted XFTP hash shows error', async ({downloadPage}) => { + // Long base64url string that looks like XFTP but is not valid DEFLATE data + const corrupted = 'A'.repeat(100) + await downloadPage.goto('#' + corrupted) await downloadPage.expectInitialError(/[Ii]nvalid|corrupted/) }) diff --git a/xftp-web/vite.config.ts b/xftp-web/vite.config.ts index 13b3caa83..694f1a949 100644 --- a/xftp-web/vite.config.ts +++ b/xftp-web/vite.config.ts @@ -49,19 +49,15 @@ function getFingerprint(): string { .replace(/\+/g, '-').replace(/\//g, '_') } -// Plugin to inject __XFTP_SERVERS__ lazily (reads PORT_FILE written by test/runSetup.ts) +// Plugin to inject __XFTP_SERVERS__ via Vite define (reads PORT_FILE written by test/runSetup.ts) function xftpServersPlugin(): Plugin { - let serverAddr: string | null = null const fp = getFingerprint() return { name: 'xftp-servers-define', - transform(code, _id) { - if (!code.includes('__XFTP_SERVERS__')) return null - if (!serverAddr) { - const port = readFileSync(PORT_FILE, 'utf-8').trim() - serverAddr = `xftp://${fp}@localhost:${port}` - } - return code.replace(/__XFTP_SERVERS__/g, JSON.stringify([serverAddr])) + config() { + const port = readFileSync(PORT_FILE, 'utf-8').trim() + const serverAddr = `xftp://${fp}@localhost:${port}` + return {define: {__XFTP_SERVERS__: JSON.stringify([serverAddr])}} } } } diff --git a/xftp-web/web/main.ts b/xftp-web/web/main.ts index 41b18b067..246b45d02 100644 --- a/xftp-web/web/main.ts +++ b/xftp-web/web/main.ts @@ -1,7 +1,6 @@ import sodium from 'libsodium-wrappers-sumo' import {initUpload} from './upload.js' import {initDownload} from './download.js' -import {decodeDescriptionURI} from '../src/agent.js' import {t} from './i18n.js' function getAppElement(): HTMLElement | null { @@ -20,22 +19,24 @@ async function main() { if (!app?.hasAttribute('data-no-hashchange')) { window.addEventListener('hashchange', () => { const hash = window.location.hash.slice(1) - if (!hash || isXFTPHash(hash)) initApp() + if (!hash || looksLikeXFTPHash(hash)) initApp() }) } await wasmReady app?.dispatchEvent(new CustomEvent('xftp:ready', {bubbles: true})) } -function isXFTPHash(hash: string): boolean { - try { decodeDescriptionURI(hash); return true } catch { return false } +// XFTP hashes are base64url-encoded DEFLATE data, always long. +// Matches the index.html inline script heuristic (length > 50). +function looksLikeXFTPHash(hash: string): boolean { + return hash.length > 50 && /^[A-Za-z0-9_=-]+$/.test(hash) } function initApp() { const app = getAppElement()! const hash = window.location.hash.slice(1) - if (hash && isXFTPHash(hash)) { + if (hash && looksLikeXFTPHash(hash)) { initDownload(app, hash) } else { initUpload(app)