clone

one developer profile for links and private tcp services, reached peer to peer over iroh with no open ports

main
devsite / web / app.spec.ts
app.spec.ts 6.3K
1import { readFile } from 'node:fs/promises';2import { extname, join } from 'node:path';3import { expect, test, type Route } from '@playwright/test';45const webRoot = join(process.cwd(), 'web');67const contentTypes: Record<string, string> = {8 '.css': 'text/css',9 '.html': 'text/html',10 '.js': 'text/javascript',11 '.woff2': 'font/woff2',12};1314async function staticResponse(route: Route, pathname: string) {15 const relativePath = pathname === '/' || pathname.startsWith('/@')16 ? 'index.html' : pathname.replace(/^\//, '');17 if (relativePath.includes('..')) {18 await route.fulfill({ status: 404 });19 return;20 }21 const body = await readFile(join(webRoot, relativePath));22 await route.fulfill({23 body,24 contentType: contentTypes[extname(relativePath)] ?? 'application/octet-stream',25 });26}2728function sseEvent(data: string, event?: string, id?: string): string {29 return `${event ? `event: ${event}\n` : ''}${id ? `id: ${id}\n` : ''}`30 + `${data.split('\n').map((line) => `data: ${line}\n`).join('')}\n`;31}3233test('the signed-out home uses a font-independent wordmark', async ({ page }) => {34 await page.route('https://dev.site.test/**', async (route) => {35 const pathname = new URL(route.request().url()).pathname;36 if (pathname === '/api/me') await route.fulfill({ status: 204 });37 else await staticResponse(route, pathname);38 });3940 await page.goto('https://dev.site.test/');4142 await expect(page.getByRole('heading', { name: 'dev.site', exact: true })).toBeVisible();43 await expect(page.locator('.home-wordmark')).toBeVisible();44 await expect(page.locator('.home-wordmark path')).toHaveCount(1);45});4647test('an incoming share uses the Fixi UI endpoint', async ({ page }) => {48 let acceptRequests = 0;49 let removeRequests = 0;50 await page.route('https://dev.site.test/**', async (route) => {51 const request = route.request();52 const pathname = new URL(request.url()).pathname;53 const json = async (value: unknown) => route.fulfill({ json: value });5455 if (pathname === '/api/me') {56 await json({ account_id: 'acct_test', handle: 'alice' });57 } else if (pathname === '/api/resources') {58 await json({ resources: [] });59 } else if (pathname === '/api/share-invitations') {60 await json({61 shares: [{62 kind: 'link',63 name: 'runbook',64 owner_handle: 'bob',65 resource_id: 'res_test',66 status: 'pending',67 url: 'https://example.com/runbook',68 }],69 });70 } else if (pathname === '/api/machine-credentials') {71 await json({ credentials: [] });72 } else if (pathname === '/api/profile/settings') {73 await json({ private_only: false });74 } else if (pathname === '/ui/share-invitations/res_test/accept') {75 acceptRequests += 1;76 expect(request.method()).toBe('POST');77 expect(request.headers()['fx-request']).toBe('true');78 await route.fulfill({79 body: `<li><div><strong>runbook</strong><small>from @bob - link</small></div>80 <div class="dashboard-actions"><button class="secondary outline" type="button"81 fx-action="/ui/share-invitations/res_test" fx-method="DELETE"82 fx-target="#incoming-shares" fx-swap="innerHTML">Remove</button></div></li>`,83 contentType: 'text/html',84 });85 } else if (pathname === '/ui/share-invitations/res_test') {86 removeRequests += 1;87 expect(request.method()).toBe('DELETE');88 expect(request.headers()['fx-request']).toBe('true');89 await route.fulfill({90 body: '<li><small>No incoming shares.</small></li>',91 contentType: 'text/html',92 });93 } else {94 await staticResponse(route, pathname);95 }96 });9798 await page.goto('https://dev.site.test/');99 await expect(page.getByRole('heading', { name: '@alice' })).toBeVisible();100 await page.getByText('Incoming shares', { exact: true }).click();101 await page.getByRole('button', { name: 'Accept' }).click();102 await expect(page.getByRole('button', { name: 'Remove' })).toBeVisible();103 expect(acceptRequests).toBe(1);104105 await page.getByRole('button', { name: 'Remove' }).click();106107 await expect(page.getByText('No incoming shares.')).toBeVisible();108 expect(removeRequests).toBe(1);109});110111test('a profile hydrates and updates from one SSEXi response', async ({ page }) => {112 let streamRequests = 0;113 await page.route('https://dev.site.test/**', async (route) => {114 const pathname = new URL(route.request().url()).pathname;115 if (pathname === '/api/me') {116 await route.fulfill({ status: 204 });117 } else if (pathname === '/ui/profile/alice/stream') {118 streamRequests += 1;119 const themeEvent = JSON.stringify({ target: '#profile-theme', swap: 'textContent' });120 const initial = '<main class="container" id="main" data-profile-handle="alice" '121 + 'data-profile-scheme="dark"><article id="profile"><hgroup><h1>@alice</h1>'122 + '<p>1 site</p></hgroup><ul class="entries"><li class="entry" id="site-res_one" '123 + 'data-kind="link" data-visibility="public"><a href="https://one.example">one</a>'124 + '</li></ul></article></main>';125 const updated = '<main class="container" id="main" data-profile-handle="alice" '126 + 'data-profile-scheme="dark"><article id="profile"><hgroup><h1>@alice</h1>'127 + '<p>2 sites</p></hgroup><ul class="entries"><li class="entry" id="site-res_one" '128 + 'data-kind="link" data-visibility="public"><a href="https://one.example">one</a>'129 + '</li><li class="entry" id="site-res_two" data-kind="link" '130 + 'data-visibility="public"><a href="https://two.example">two</a></li></ul>'131 + '</article></main>';132 await route.fulfill({133 body: sseEvent(':root[data-profile="alice"] { --pico-primary: rebeccapurple; }', themeEvent, '1')134 + sseEvent(initial, undefined, '1')135 + sseEvent(updated, undefined, '2'),136 contentType: 'text/event-stream',137 });138 } else {139 await staticResponse(route, pathname);140 }141 });142143 await page.goto('https://dev.site.test/@alice');144 await expect(page.getByRole('heading', { name: '@alice' })).toBeVisible();145 await expect(page.getByText('2 sites')).toBeVisible();146 await expect(page.getByRole('link', { name: 'two' })).toBeVisible();147 await expect(page.locator('html[data-profile="alice"][data-theme="dark"]')).toBeAttached();148 expect(await page.locator('#profile-theme').textContent()).toContain('rebeccapurple');149 expect(streamRequests).toBe(1);150});