test(e2e): role-shadowing specs in apps + scripts suites

Lifts loginAsUserToken + pageWithUserToken out of members.spec.ts into
fixtures/role-page.ts (third file that needs them). Adds shadowing
coverage: viewer member sees no New-app / Add-domain / Settings / Save
/ +Add-route, editor sees Save but no Delete header, and CodeMirror
renders contenteditable=false for viewers.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
MechaCat02
2026-05-28 19:40:09 +02:00
parent f694a6d504
commit b459b99fe9
4 changed files with 291 additions and 33 deletions

View File

@@ -1,14 +1,13 @@
import { expect, type Browser, type Page } from '@playwright/test';
import { expect } from '@playwright/test';
import { test } from '../fixtures/ids';
import { CleanupRegistry } from '../fixtures/cleanup';
import { adminApi } from '../fixtures/api';
import { loginAsUserToken, pageWithUserToken } from '../fixtures/role-page';
// Phase B5 — App Members. Setup creates one or two extra admin
// users via the API; tests drive the Members tab through the
// dashboard like a real app admin would.
const API_BASE = process.env.E2E_API_BASE ?? 'http://127.0.0.1:18080';
const cleanup = new CleanupRegistry();
test.afterEach(async () => {
await cleanup.run();
@@ -38,36 +37,6 @@ async function createMemberUser(username: string): Promise<string> {
}
}
async function loginAsUserToken(username: string, password: string): Promise<string> {
const probe = await (await import('@playwright/test')).request.newContext({
baseURL: API_BASE
});
try {
const res = await probe.post('/api/v1/admin/auth/login', {
data: { username, password },
headers: { 'content-type': 'application/json' }
});
expect(res.ok()).toBe(true);
return ((await res.json()) as { token: string }).token;
} finally {
await probe.dispose();
}
}
async function pageWithUserToken(browser: Browser, token: string): Promise<Page> {
const ctx = await browser.newContext({ storageState: undefined });
const page = await ctx.newPage();
// Seed localStorage on the right origin, then navigate normally.
await page.goto('/admin/login');
await page.evaluate(
([key, value]) => {
localStorage.setItem(key, value);
},
['picloud.admin.token', token]
);
return page;
}
test.describe('B5 app members', () => {
test('invite a member-role user, then remove them', async ({ page, uniqueSlug, uniqueUsername }) => {
const slug = uniqueSlug('mem');