tests/a11y/global-setup.js
Rama main · réplica de lectura del repositorio original.
// Signs in as the seeded superadmin, adds the content the seed leaves out (wiki
// pages, an own gist), and resolves every {placeholder} in routes.js to a real
// path on this server. Discovery instead of hard-coded names keeps the gate
// working when the seed's output changes.
import { chromium } from '@playwright/test';
import fs from 'node:fs';
import { baseURL } from './playwright.config.js';
export const ADMIN = { email: 'admin@example.test', username: 'siteadmin', password: 'cloudzilla-seed' };
export const WIKI_PAGES = ['Home', 'Architecture', 'Contributing'];
export default async function globalSetup() {
fs.mkdirSync('.cache', { recursive: true });
const browser = await chromium.launch();
const page = await browser.newPage({ baseURL });
await page.goto('/login');
await page.getByLabel('Email').fill(ADMIN.email);
await page.getByLabel('Password', { exact: true }).fill(ADMIN.password);
await Promise.all([page.waitForURL(u => !u.pathname.startsWith('/login')), page.getByRole('button', { name: 'Sign in' }).click()]);
await page.context().storageState({ path: '.cache/admin.json' });
const csrf = (await page.context().cookies()).find(c => c.name === 'csrf_token')?.value ?? '';
const api = (method, path, opts = {}) =>
page.request.fetch(path, { method, headers: { 'X-CSRF-Token': csrf, ...opts.headers }, ...opts }).then(async res => {
if (!res.ok() && res.status() !== 303) throw new Error(`${method} ${path}: ${res.status()} ${await res.text()}`);
return res;
});
async function links(path, pattern) {
const res = await page.goto(path);
if (!res.ok()) return [];
const hrefs = await page.locator('main a[href]').evaluateAll(as => as.map(a => a.getAttribute('href')));
return hrefs.filter(h => pattern.test(h.split(/[?#]/)[0])).map(h => h.split('#')[0]);
}
async function first(paths, pattern) {
for (const p of paths) {
const [hit] = await links(p, pattern);
if (hit) return hit;
}
throw new Error(`no link matching ${pattern} on ${paths.join(', ')}`);
}
let repos = await (await api('GET', '/api/repos/')).json();
const ownRepo = `/${ADMIN.username}/a11y-fixture`;
if (!repos.some(r => r.owner_name === ADMIN.username && r.name === 'a11y-fixture')) {
await api('POST', '/api/repos/', { data: { name: 'a11y-fixture', add_readme: true, private: false } });
repos = await (await api('GET', '/api/repos/')).json();
}
const orgCheck = await page.request.get('/api/orgs/a11y-fixture-org');
if (orgCheck.status() === 404) {
await api('POST', '/api/orgs/', { data: { name: 'a11y-fixture-org', display_name: 'A11y fixture' } });
} else if (!orgCheck.ok()) throw new Error(`Cannot inspect fixture organization: ${orgCheck.status()}`);
for (const slug of WIKI_PAGES) {
await api('POST', `/api/repos${ownRepo}/wiki/${slug}`, {
form: { content: `# ${slug}\n\nSee [the README](../) and [${WIKI_PAGES[0]}](${WIKI_PAGES[0]}).`, message: `Add ${slug}` },
maxRedirects: 0,
});
}
await page.goto('/gists');
const existingGist = page.locator('li[data-name="a11y fixture siteadmin"] > a').first();
let gistPath = await existingGist.count() ? await existingGist.getAttribute('href') : null;
if (!gistPath) {
const gist = await (await api('POST', '/api/gists', {
data: { description: 'a11y fixture', public: true, files: [{ filename: 'main.go', content: 'package main\n\nfunc main() {}\n' }] },
})).json();
gistPath = `/gists/${gist.id}`;
}
// The busiest public repo carries the issue, PR and discussion pages.
const publicRepos = repos.filter(r => !r.private && !r.is_archived && r.name !== 'a11y-fixture').map(r => `/${r.owner_name}/${r.name}`);
const counts = await Promise.all(
publicRepos.map(async p => (await (await api('GET', `/api/repos${p}/issues/`)).json()).length),
);
const busy = publicRepos.map((p, i) => [p, counts[i]]).sort((a, b) => b[1] - a[1]).map(([p]) => p);
const repo = busy[0];
const each = suffix => busy.map(p => p + suffix);
const num = s => new RegExp(`^/[^/]+/[^/]+/${s}/\\d+$`);
const fixtures = {
me: ADMIN.username,
repo,
ownRepo,
wiki: WIKI_PAGES[0],
gist: gistPath,
blob: await first([repo], /^\/[^/]+\/[^/]+\/blob\/.+/),
commit: await first([`${repo}/commits`], /^\/[^/]+\/[^/]+\/commit\/[0-9a-f]{7,}$/),
issue: await first([`${repo}/issues`], num('issues')),
closedIssue: await first(each('/issues?state=closed'), num('issues')),
milestone: await first(each('/milestones'), num('milestones')),
pull: await first(each('/pulls'), num('pulls')),
discussion: await first(each('/discussions'), num('discussions')),
project: await first(each('/projects'), num('projects')),
release: await first(each('/releases'), /^\/[^/]+\/[^/]+\/releases\/tag\/.+$/),
org: 'a11y-fixture-org',
user: repos.find(r => !r.org_id && r.owner_name !== ADMIN.username).owner_name,
};
const repoOf = path => path.split('/').slice(0, 3).join('/');
Object.assign(fixtures, {
blame: fixtures.blob.replace('/blob/', '/blame/'),
milestoneRepo: repoOf(fixtures.milestone),
discussionRepo: repoOf(fixtures.discussion),
projectRepo: repoOf(fixtures.project),
releaseRepo: repoOf(fixtures.release),
});
fs.writeFileSync('.cache/fixtures.json', JSON.stringify(fixtures, null, 2));
await browser.close();
}