67 lines
5.7 KiB
JavaScript
67 lines
5.7 KiB
JavaScript
const fs = require('fs'), assert = require('node:assert/strict'), { createHash } = require('crypto');
|
|
const { chromium, expect } = require('@playwright/test');
|
|
(async () => {
|
|
const phase = process.argv[2], portal = JSON.parse(fs.readFileSync('tmp/portal195-private.json'));
|
|
const state = JSON.parse(fs.readFileSync('tmp/state239-public.json'));
|
|
const browser = await chromium.launch({ channel: 'msedge', headless: true });
|
|
try {
|
|
const page = await browser.newPage({ viewport: { width: 1440, height: 1000 } }), errors = [], receipts = [];
|
|
page.on('pageerror', e => errors.push(e.message));
|
|
page.on('response', r => { if (new URL(r.url()).pathname.startsWith('/api/backups') && r.request().method() !== 'GET') receipts.push({ method: r.request().method(), path: new URL(r.url()).pathname, status: r.status() }); });
|
|
await page.goto('http://localhost:3000/backups');
|
|
await page.getByLabel('Email', { exact: true }).fill(portal.email);
|
|
await page.getByLabel('Пароль', { exact: true }).fill(portal.password);
|
|
await page.getByRole('button', { name: 'Войти', exact: true }).click();
|
|
await expect(page.getByRole('heading', { name: 'Резервные копии', exact: true })).toBeVisible({ timeout: 30000 });
|
|
if (phase === 'configure') {
|
|
const s = JSON.parse(fs.readFileSync('tmp/s3-ui239-private.json'));
|
|
const paused = await page.evaluate(async () => {
|
|
const r = await fetch('/api/backups/schedule', { method: 'PUT', headers: { 'Content-Type': 'application/json', Authorization: 'Bearer ' + localStorage.getItem('mmapp_token') }, body: JSON.stringify({ enabled: false }) });
|
|
return { status: r.status, body: await r.json() };
|
|
});
|
|
assert.equal(paused.status, 200); assert.equal(paused.body.enabled, false);
|
|
await page.getByRole('tab', { name: 'Настройки', exact: true }).click();
|
|
await page.getByRole('button', { name: 'S3', exact: true }).click();
|
|
for (const [label, value] of Object.entries({ Endpoint: s.endpoint, Region: s.region, Bucket: s.bucket, Prefix: state.prefix, 'Access key': s.accessKey, 'Secret key': s.secretKey })) await page.getByLabel(label, { exact: true }).fill(value);
|
|
const path = page.getByRole('switch', { name: 'Path-style', exact: true });
|
|
if (await path.getAttribute('aria-checked') === 'true') await path.click();
|
|
const saved = page.waitForResponse(r => new URL(r.url()).pathname === '/api/backups/storage' && r.request().method() === 'PUT');
|
|
await page.getByRole('button', { name: 'Сохранить хранилище', exact: true }).click();
|
|
assert.equal((await saved).status(), 200);
|
|
await expect(page.getByLabel('Secret key', { exact: true })).toHaveValue('');
|
|
const tested = page.waitForResponse(r => new URL(r.url()).pathname === '/api/backups/storage/test' && r.request().method() === 'POST');
|
|
await page.getByRole('button', { name: 'Проверить', exact: true }).click();
|
|
assert.equal((await tested).status(), 200);
|
|
} else {
|
|
await page.getByRole('button', { name: 'Обновить', exact: true }).click();
|
|
await expect(page.getByText(state.filename, { exact: true })).toBeVisible();
|
|
if (phase === 'restore') {
|
|
const download = page.waitForEvent('download');
|
|
await page.getByRole('button', { name: 'Скачать ' + state.filename, exact: true }).click();
|
|
const d = await download; assert.equal(d.suggestedFilename(), state.filename);
|
|
const bytes = fs.readFileSync(await d.path());
|
|
const expected = Buffer.from(`# Own limited UI restore 239\n/interface bridge add name=${state.bridge} comment=mm-ui239\n`);
|
|
assert.deepEqual(bytes, expected);
|
|
await page.getByRole('button', { name: 'Восстановить ' + state.filename, exact: true }).click();
|
|
const dialog = page.getByRole('alertdialog');
|
|
await expect(dialog).toContainText(state.filename);
|
|
const restored = page.waitForResponse(r => new URL(r.url()).pathname === '/api/backups/' + state.id + '/restore' && r.request().method() === 'POST');
|
|
await dialog.getByRole('button', { name: 'Восстановить', exact: true }).click();
|
|
const r = await restored; assert.equal(r.status(), 200);
|
|
const result = await r.json(); assert.equal(result.backupId, state.id); assert.equal(result.serverId, state.serverId);
|
|
await expect(dialog).toBeHidden();
|
|
fs.writeFileSync('docs/ux-ui/acceptance/s3-ui239-browser.json', JSON.stringify({ date: '2026-10-14', block: '12.5.239', realPortalLogin: true, noMocks: true, passed: true, downloadBytes: bytes.length, downloadSha256: createHash('sha256').update(bytes).digest('hex'), restoreStatus: 200, restoreReceiptMatched: true, receipts, pageErrors: errors, fullConfigurationApplied: false }, null, 2) + '\n');
|
|
} else if (phase === 'delete') {
|
|
await page.getByRole('button', { name: 'Удалить ' + state.filename, exact: true }).click();
|
|
const deleted = page.waitForResponse(r => new URL(r.url()).pathname === '/api/backups/' + state.id && r.request().method() === 'DELETE');
|
|
await page.getByRole('alertdialog').getByRole('button', { name: 'Удалить', exact: true }).click();
|
|
assert.equal((await deleted).status(), 204); await expect(page.getByRole('alertdialog')).toBeHidden();
|
|
await expect(page.getByText(state.filename, { exact: true })).toHaveCount(0);
|
|
const p = 'docs/ux-ui/acceptance/s3-ui239-browser.json', report = JSON.parse(fs.readFileSync(p));
|
|
Object.assign(report, { deleteStatus: 204, ownRowRemovedThroughUi: true, deleteReceipts: receipts }); fs.writeFileSync(p, JSON.stringify(report, null, 2) + '\n');
|
|
} else throw Error('Unsupported phase');
|
|
}
|
|
assert.deepEqual(errors, []); console.log('S3 UI239 ' + phase + ' passed');
|
|
} finally { await browser.close(); }
|
|
})().catch(e => { console.error(e.message); process.exitCode = 1; });
|