Files

86 lines
6.2 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
'use strict';
const assert = require('node:assert/strict');
const fs = require('node:fs'), os = require('node:os'), path = require('node:path');
const { PeeringState } = require('../peering/state');
module.exports = async function testPeerUI(url, cookie) {
const { chromium } = require('playwright');
const browser = await chromium.launch({ channel: process.env.QA_BROWSER_CHANNEL || 'msedge', headless: true });
const context = await browser.newContext({ viewport: { width: 1280, height: 900 } });
const [name, ...value] = cookie.split('=');
await context.addCookies([{ name, value: value.join('='), url }]);
const page = await context.newPage(), errors = [];
page.on('pageerror', error => errors.push(error.message));
try {
await page.goto(url + '/peers.html#admin');
await page.getByRole('heading', { name: 'Known peers' }).waitFor();
assert.ok(await page.getByRole('link', { name: 'Peer catalogs' }).count() >= 1);
const state = await page.evaluate(() => fetch('/api/admin/peering').then(response => response.json()));
assert.equal(state.identity?.name, 'ComposeQA');
await page.getByRole('button', { name: 'Add a peer' }).click();
await page.getByRole('heading', { name: 'Add a peer' }).waitFor();
await page.getByLabel('This server’s reachable peer gateway URL').fill('http://localhost:8080');
await page.getByRole('button', { name: 'Create 48-hour invitation' }).click();
assert.match(await page.getByLabel('This server’s reachable peer gateway URL').evaluate(el => el.validationMessage), /HTTPS peer gateway origin/);
await page.getByLabel('This server’s reachable peer gateway URL').fill('https://peer-test.example:8093');
await page.getByRole('button', { name: 'Create 48-hour invitation' }).click();
const outgoing = page.getByLabel('Invitation to send');
await page.waitForFunction(() => document.querySelector('textarea[aria-label="Invitation to send"]')?.value.includes('fingerprint'));
const invitation = JSON.parse(await outgoing.inputValue());
assert.equal(invitation.url, 'https://peer-test.example:8093');
assert.match(invitation.name, /^[A-Za-z0-9]{1,25}$/);
assert.ok(Date.parse(invitation.expiresAt) > Date.now());
await page.getByLabel('Invitation received').fill('{bad json');
await page.getByRole('button', { name: 'Import invitation' }).click();
await page.getByText('That is not valid invitation JSON.').waitFor();
const peerDir = fs.mkdtempSync(path.join(os.tmpdir(), 'mc-peer-ui-'));
let peerId;
try {
if (process.platform === 'win32' && !process.env.OPENSSL_BIN) process.env.OPENSSL_BIN = 'C:\\Program Files\\Git\\usr\\bin\\openssl.exe';
const peer = new PeeringState(peerDir);
peer.generateIdentity('UIFixturePeer');
const fixtureInvitation = peer.createInvitation('https://fixture-peer.example:8093');
await page.getByLabel('Invitation received').fill(JSON.stringify(fixtureInvitation));
await page.getByRole('button', { name: 'Import invitation' }).click();
await page.getByRole('heading', { name: '<UIFixturePeer>', exact: true }).waitFor();
const apiState = await page.evaluate(() => fetch('/api/peering').then(response => response.json()));
peerId = apiState.peers.find(item => item.alias === 'UIFixturePeer').id;
await page.locator('.peer-page-tabs').getByRole('link', { name: 'Peer catalogs' }).click();
await page.getByRole('heading', { name: 'Peer catalogs' }).waitFor();
if (!await page.getByRole('button', { name: 'Select visible' }).count()) {
console.error('Peer catalog page content:', await page.locator('main').innerText());
console.error('Peer catalog JS errors:', errors);
}
await page.getByLabel('Select peer').selectOption(peerId);
assert.equal(await page.getByText('Setup is incomplete', { exact: false }).count(), 0, 'Sharing can be prepared before pairing');
await page.getByRole('button', { name: 'Select visible' }).click();
await page.getByRole('button', { name: 'Save approvals' }).click();
await page.getByText('Approved titles updated.').waitFor();
const saved = await page.evaluate(id => fetch('/api/admin/peering').then(r => r.json()).then(s => s.peers.find(p => p.id === id).shares.length), peerId);
assert.equal(saved, 3, 'Bulk approval saves the selected local catalog');
assert.equal(await page.getByRole('button', { name: 'Browse peer catalog' }).count(), 1);
await page.getByRole('button', { name: 'Browse peer catalog' }).click();
await page.getByText('Setup is incomplete', { exact: false }).waitFor();
await page.setViewportSize({ width: 390, height: 844 });
assert.ok(await page.evaluate(() => document.documentElement.scrollWidth <= innerWidth), 'Peer catalog should fit mobile width');
} finally { fs.rmSync(peerDir, { recursive: true, force: true }); }
assert.equal(errors.length, 0, `Browser errors: ${errors.join('; ')}`);
const created = await page.evaluate(async () => (await fetch('/api/admin/user', { method: 'POST', headers: { 'content-type': 'application/json' },
body: JSON.stringify({ name: 'peer-reader-qa', password: 'disposable-reader-test', admin: false }) })).status);
assert.equal(created, 200);
await page.evaluate(() => fetch('/api/logout', { method: 'POST', headers: { 'content-type': 'application/json' }, body: '{}' }));
await page.evaluate(() => fetch('/api/login', { method: 'POST', headers: { 'content-type': 'application/json' },
body: JSON.stringify({ name: 'peer-reader-qa', password: 'disposable-reader-test' }) }));
await page.goto(url + '/peer-catalog.html');
await page.getByText('Peer catalogs are available to administrators only.').waitFor();
const denied = await page.evaluate(async () => Promise.all(['/api/admin/peering', '/api/peering', '/api/peering/catalog?peer=x', '/api/peering/cover?peer=x&manga=x']
.map(async endpoint => (await fetch(endpoint)).status)));
assert.deepEqual(denied, [403, 403, 403, 403], 'Every peering API remains admin-only');
await page.goto(url + '/');
await page.getByRole('link', { name: 'Peer catalogs' }).waitFor({ state: 'detached' });
assert.equal(errors.length, 0, `Browser errors: ${errors.join('; ')}`);
} finally { await browser.close(); }
};