Add scoped peer access and reversible serving controls
This commit is contained in:
1 parent
6c994113c6
commit
492477f86a
7 files changed
+1081
No files matched your search
@@ -0,0 +1,72 @@
|
||||
'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');
|
||||
await page.getByRole('heading', { name: 'Peer catalogs', exact: true }).waitFor();
|
||||
let state = await page.evaluate(() => fetch('/api/admin/peering').then(response => response.json()));
|
||||
if (!state.identity) {
|
||||
await page.getByRole('button', { name: 'Generate local certificate' }).click();
|
||||
await page.getByText('Identity generated').waitFor();
|
||||
state = await page.evaluate(() => fetch('/api/admin/peering').then(response => response.json()));
|
||||
}
|
||||
assert.match(state.identity.fingerprint, /^[0-9a-f]{64}$/);
|
||||
await page.getByLabel('Reachable peer-gateway URL').fill('http://localhost:8080');
|
||||
await page.getByRole('button', { name: 'Create 48-hour invitation' }).click();
|
||||
assert.match(await page.getByLabel('Reachable peer-gateway URL').evaluate(el => el.validationMessage), /HTTPS peer-gateway origin/);
|
||||
await page.getByLabel('Reachable peer-gateway URL').fill('https://peer-test.example:8093');
|
||||
await page.getByRole('button', { name: 'Create 48-hour invitation' }).click();
|
||||
await page.waitForFunction(() => document.querySelector('textarea[aria-label="Invitation to exchange"]')?.value.includes('fingerprint'));
|
||||
const invitation = JSON.parse(await page.getByLabel('Invitation to exchange').inputValue());
|
||||
assert.equal(invitation.url, 'https://peer-test.example:8093');
|
||||
assert.equal(invitation.fingerprint, state.identity.fingerprint);
|
||||
await page.getByLabel('Other server invitation').fill('{bad json');
|
||||
await page.getByRole('button', { name: 'Import invitation' }).click();
|
||||
await page.getByText('Invalid invitation JSON').waitFor();
|
||||
const peerDir = fs.mkdtempSync(path.join(os.tmpdir(), 'mc-peer-ui-'));
|
||||
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('Other server invitation').fill(JSON.stringify(fixtureInvitation));
|
||||
await page.getByRole('button', { name: 'Import invitation' }).click();
|
||||
await page.getByRole('heading', { name: 'UI fixture peer' }).waitFor();
|
||||
const card = page.locator('.peer-card').filter({ has: page.getByRole('heading', { name: 'UI fixture peer' }) });
|
||||
await card.getByText('Transfer limits').click();
|
||||
await card.getByText('Approved manga (0)').click();
|
||||
await card.getByText('Access grant').click();
|
||||
await card.getByLabel('Moon Garden').check();
|
||||
await card.getByText('Approved manga (1)').waitFor();
|
||||
assert.equal(await card.locator('details[open]').count(), 3, 'Saving a share must preserve all expanded sections');
|
||||
} finally { fs.rmSync(peerDir, { recursive: true, force: true }); }
|
||||
assert.equal(errors.length, 0, `Browser errors: ${errors.join('; ')}`);
|
||||
await page.setViewportSize({ width: 390, height: 844 });
|
||||
assert.ok(await page.evaluate(() => document.documentElement.scrollWidth <= innerWidth), 'Peering page overflows on mobile');
|
||||
// An ordinary reader may browse, but cannot operate certificate controls.
|
||||
const created = await page.evaluate(async () => {
|
||||
const response = 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 }) });
|
||||
return response.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.reload();
|
||||
await page.getByRole('heading', { name: 'Peer catalogs', exact: true }).waitFor();
|
||||
assert.equal(await page.getByRole('heading', { name: 'Server identity & invitations' }).count(), 0);
|
||||
assert.equal((await page.evaluate(() => fetch('/api/admin/peering').then(response => response.status))), 403);
|
||||
assert.equal(errors.length, 0, `Browser errors: ${errors.join('; ')}`);
|
||||
} finally { await browser.close(); }
|
||||
};
|
||||
Reference in new issue
Block a user