import { describe, test, expect, beforeAll, afterAll } from 'bun:test'; import { chromium } from 'playwright'; import type { Browser, BrowserContext } from 'playwright'; import { BASE, DATA_DIR, ADMIN_PASS, setupTestEnv, spawnServer, killServer, login, seedRepo, getHeadCommit, gitOutput, } from './helpers.ts'; let browser: Browser; let server: Awaited>; beforeAll(async () => { await setupTestEnv(); server = await spawnServer(); browser = await chromium.launch(); }); afterAll(async () => { await browser.close(); await killServer(server); }); async function loggedInContext(username = 'admin', password = ADMIN_PASS) { const ctx = await browser.newContext(); const page = await ctx.newPage(); await login(page, username, password); await page.close(); return ctx; } // ─── File editing ───────────────────────────────────────────────────────────── describe('file editing', () => { let adminCtx: BrowserContext; beforeAll(async () => { adminCtx = await loggedInContext(); // Create a dedicated repo so edits don't interfere with other tests const page = await adminCtx.newPage(); try { await page.goto(`${BASE}/new`); await page.fill('[name=name]', 'edit-repo'); await page.click('form[action="/new"] button[type=submit]'); await page.waitForURL(`${BASE}/edit-repo`); } finally { await page.close(); } await seedRepo('edit-repo'); }); afterAll(async () => { await adminCtx.close(); }); test('Edit button appears on text file blob when viewing a branch as admin', async () => { const page = await adminCtx.newPage(); try { await page.goto(`${BASE}/edit-repo/blob/main/index.js`); const editBtn = page.locator('a[href*="/edit/main/index.js"]'); expect(await editBtn.isVisible()).toBe(true); expect(await editBtn.textContent()).toBe('Edit'); } finally { await page.close(); } }); test('Edit button does not appear when viewing a commit SHA', async () => { const sha = await getHeadCommit('edit-repo'); const page = await adminCtx.newPage(); try { await page.goto(`${BASE}/edit-repo/blob/${sha}/index.js`); expect(await page.locator('a[href*="/edit/"]').count()).toBe(0); } finally { await page.close(); } }); test('Edit button does not appear for unauthenticated visitors', async () => { const ctx = await browser.newContext(); const page = await ctx.newPage(); try { await page.goto(`${BASE}/edit-repo/blob/main/index.js`); expect(await page.locator('a[href*="/edit/main/"]').count()).toBe(0); } finally { await page.close(); await ctx.close(); } }); test('edit page loads with file content pre-filled', async () => { const page = await adminCtx.newPage(); try { await page.goto(`${BASE}/edit-repo/edit/main/index.js`); expect(await page.locator('.file-blob-name').textContent()).toBe('index.js'); const content = await page.locator('textarea[name=content]').inputValue(); expect(content).toContain('hello'); const msg = await page.locator('textarea[name=message]').inputValue(); expect(msg).toBe('Edited index.js'); } finally { await page.close(); } }); test('edit page shows which branch will be committed to', async () => { const page = await adminCtx.newPage(); try { await page.goto(`${BASE}/edit-repo/edit/main/index.js`); expect(await page.content()).toContain('main'); } finally { await page.close(); } }); test('edit page returns 404 for non-branch ref', async () => { const sha = await getHeadCommit('edit-repo'); const page = await adminCtx.newPage(); try { const resp = await page.request.get(`${BASE}/edit-repo/edit/${sha}/index.js`); expect(resp.status()).toBe(404); } finally { await page.close(); } }); test('submitting edit creates a new commit and redirects to blob view', async () => { const page = await adminCtx.newPage(); try { await page.goto(`${BASE}/edit-repo/edit/main/index.js`); await page.fill('textarea[name=content]', 'console.log("edited");\n'); await page.fill('textarea[name=message]', 'Update index.js via web editor'); await page.locator('.form-actions button[type=submit]').click(); await page.waitForURL(/\/edit-repo\/commit\/[0-9a-f]{40}/); // The commit detail view should show the commit message expect(await page.content()).toContain('Update index.js via web editor'); } finally { await page.close(); } }); test('edit commit has a gpgsig header (is signed)', async () => { const repoDir = `${process.cwd()}/${DATA_DIR}/repos/edit-repo.git`; const hash = gitOutput(['log', '--format=%H', '--grep=Update index.js via web editor', '-1'], repoDir); expect(hash).toBeTruthy(); const obj = gitOutput(['cat-file', '-p', hash], repoDir); expect(obj).toContain('gpgsig'); }); test('edit commit shows verified badge in commit log', async () => { const page = await adminCtx.newPage(); try { await page.goto(`${BASE}/edit-repo/commits/main`); const item = page.locator('.commit-item').filter({ hasText: 'Update index.js via web editor' }); expect(await item.locator('.sig-badge.verified').isVisible()).toBe(true); } finally { await page.close(); } }); test('GET edit page returns 404 for non-branch ref', async () => { const sha = await getHeadCommit('edit-repo'); const page = await adminCtx.newPage(); try { const resp = await page.request.get(`${BASE}/edit-repo/edit/${sha}/index.js`); expect(resp.status()).toBe(404); } finally { await page.close(); } }); });