import { describe, test, expect, beforeAll, afterAll } from 'bun:test'; import { chromium } from 'playwright'; import type { Browser, BrowserContext } from 'playwright'; import { BASE, ADMIN_PASS, setupTestEnv, spawnServer, killServer, login, } 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; } // ─── Repo sorting and pinning ───────────────────────────────────────────────── describe('repo sorting and pinning', () => { let adminCtx: BrowserContext; beforeAll(async () => { adminCtx = await loggedInContext(); // Create two repos with predictable names: sort-aaa (created first/older), // sort-zzz (created second/newer). This lets us verify both name order and // creation-time order independently. // Also create my-repo which is navigated to in one test. const page = await adminCtx.newPage(); try { await page.goto(`${BASE}/new`); await page.fill('[name=name]', 'my-repo'); await page.click('form[action="/new"] button[type=submit]'); await page.waitForURL(`${BASE}/my-repo`); await page.goto(`${BASE}/new`); await page.fill('[name=name]', 'sort-aaa'); await page.click('form[action="/new"] button[type=submit]'); await page.waitForURL(`${BASE}/sort-aaa`); await page.goto(`${BASE}/new`); await page.fill('[name=name]', 'sort-zzz'); await page.click('form[action="/new"] button[type=submit]'); await page.waitForURL(`${BASE}/sort-zzz`); } finally { await page.close(); } }); afterAll(async () => { await adminCtx.close(); }); test('sort dropdown is visible on repo list page', async () => { const page = await adminCtx.newPage(); try { await page.goto(BASE); const options = await page.locator('.repo-sort-select option').allTextContents(); expect(options.some(t => t.includes('Newest'))).toBe(true); expect(options.some(t => t.includes('Name'))).toBe(true); } finally { await page.close(); } }); test('newest option is selected by default', async () => { // Use a fresh context to ensure no repo_sort cookie is set. const ctx = await browser.newContext(); const page = await ctx.newPage(); try { await login(page, 'admin', ADMIN_PASS); await page.goto(BASE); expect(await page.locator('.repo-sort-select').inputValue()).toBe('created'); } finally { await ctx.close(); } }); test('Go button is hidden with JS and works without JS', async () => { const ctx = await browser.newContext({ javaScriptEnabled: false }); const page = await ctx.newPage(); try { await login(page, 'admin', ADMIN_PASS); await page.goto(BASE); // Go button visible without JS expect(await page.locator('form[action="/sort"] button[type=submit]').isVisible()).toBe(true); // Select name sort and submit via Go button await page.locator('.repo-sort-select').selectOption('name'); await page.locator('form[action="/sort"] button[type=submit]').click(); await page.waitForURL(BASE + '/'); expect(await page.locator('.repo-sort-select').inputValue()).toBe('name'); } finally { await ctx.close(); } }); test('selecting name sort sets cookie and persists on next visit', async () => { const page = await adminCtx.newPage(); try { await page.goto(BASE); await Promise.all([ page.waitForURL(BASE + '/'), page.locator('.repo-sort-select').selectOption('name'), ]); expect(await page.locator('.repo-sort-select').inputValue()).toBe('name'); // Navigate away and back to confirm cookie persists await page.goto(`${BASE}/my-repo`); await page.goto(BASE); expect(await page.locator('.repo-sort-select').inputValue()).toBe('name'); } finally { // Reset cookie so subsequent tests start from the default sort. await adminCtx.addCookies([{ name: 'repo_sort', value: 'created', domain: 'localhost', path: '/' }]); await page.close(); } }); test('default sort shows newer repo before older repo', async () => { const page = await adminCtx.newPage(); try { await adminCtx.addCookies([{ name: 'repo_sort', value: 'created', domain: 'localhost', path: '/' }]); await page.goto(`${BASE}/?q=sort-`); const names = await page.locator('.repo-name').allTextContents(); expect(names.indexOf('sort-zzz')).toBeLessThan(names.indexOf('sort-aaa')); } finally { await page.close(); } }); test('name sort shows repos in alphabetical order', async () => { const page = await adminCtx.newPage(); try { await adminCtx.addCookies([{ name: 'repo_sort', value: 'name', domain: 'localhost', path: '/' }]); await page.goto(`${BASE}/?q=sort-`); const names = await page.locator('.repo-name').allTextContents(); expect(names.indexOf('sort-aaa')).toBeLessThan(names.indexOf('sort-zzz')); } finally { await adminCtx.addCookies([{ name: 'repo_sort', value: 'created', domain: 'localhost', path: '/' }]); await page.close(); } }); test('pinning a repo shows pinned badge on list page', async () => { const page = await adminCtx.newPage(); try { await page.goto(`${BASE}/sort-aaa/settings`); await page.check('[name=is_pinned]'); await page.click('form[action$="/settings"] button[type=submit]'); expect(await page.locator('.form-success').isVisible()).toBe(true); await page.goto(BASE); const card = page.locator('.repo-card').filter({ hasText: 'sort-aaa' }); expect(await card.locator('.badge-pinned').isVisible()).toBe(true); } finally { await page.close(); } }); test('pinned repo appears before unpinned repos regardless of creation order', async () => { const page = await adminCtx.newPage(); try { // sort-aaa is pinned; default (newest) sort would normally show sort-zzz // first since it's newer — but pinned repos float to the top. await page.goto(`${BASE}/?q=sort-`); const names = await page.locator('.repo-name').allTextContents(); expect(names.indexOf('sort-aaa')).toBeLessThan(names.indexOf('sort-zzz')); } finally { await page.close(); } }); test('unpinning a repo removes the pinned badge', async () => { const page = await adminCtx.newPage(); try { await page.goto(`${BASE}/sort-aaa/settings`); await page.uncheck('[name=is_pinned]'); await page.click('form[action$="/settings"] button[type=submit]'); expect(await page.locator('.form-success').isVisible()).toBe(true); await page.goto(BASE); const card = page.locator('.repo-card').filter({ hasText: 'sort-aaa' }); expect(await card.locator('.badge-pinned').count()).toBe(0); } finally { await page.close(); } }); });