120 lines
6.6 KiB
TypeScript
120 lines
6.6 KiB
TypeScript
import { test, expect } from '@playwright/test';
|
|
|
|
test('切换历史资源后忽略延迟列表和差异响应', async ({ page }) => {
|
|
await page.goto('/');
|
|
const session = await (await page.request.get('/api/v1/session')).json();
|
|
const headers = { 'X-CSRF-Token': session.csrfToken };
|
|
const ids: string[] = [];
|
|
let releaseHistory!: () => void, releaseDiff!: () => void;
|
|
const historyGate = new Promise<void>(resolve => { releaseHistory = resolve; });
|
|
const diffGate = new Promise<void>(resolve => { releaseDiff = resolve; });
|
|
let firstHistory = true, historyHeld = false, diffHeld = false;
|
|
try {
|
|
for (const [name, port] of [['历史时序 A', 24681], ['历史时序 B', 24682]] as const) {
|
|
const result = await page.request.post('/api/v1/proxies', { headers, data: {
|
|
name, type: 'mysql', listenHost: '127.0.0.1', listenPort: port,
|
|
gatewayAccounts: [{ id: 'history', username: 'history', password: 'isolated-history-password' }],
|
|
} });
|
|
expect(result.ok()).toBeTruthy();
|
|
ids.push((await result.json()).config.id);
|
|
}
|
|
// Hold selected responses explicitly to reproduce out-of-order network delivery.
|
|
await page.route('**/api/v1/configuration/history?*', async route => {
|
|
const id = new URL(route.request().url()).searchParams.get('id');
|
|
if (id === ids[0] && firstHistory) {
|
|
firstHistory = false; historyHeld = true; await historyGate;
|
|
}
|
|
await route.fulfill({ json: [{ revision: id === ids[0] ? 11 : 22, at: 'fixture-history' }] });
|
|
});
|
|
await page.route('**/api/v1/configuration/diff?*', async route => {
|
|
const id = new URL(route.request().url()).searchParams.get('id');
|
|
if (id === ids[0]) { diffHeld = true; await diffGate; }
|
|
await route.fulfill({ json: { changes: [{ path: '/name', before: 'current', after: id === ids[0] ? 'old-A' : 'old-B' }] } });
|
|
});
|
|
await page.reload();
|
|
await page.getByRole('navigation').getByRole('button', { name: '配置管理', exact: true }).click();
|
|
const choose = async (name: string) => {
|
|
await page.locator('.el-select').filter({ has: page.getByRole('combobox', { name: '历史资源', exact: true }) }).click();
|
|
await page.getByRole('option', { name, exact: true }).click();
|
|
};
|
|
const history = page.locator('[aria-label="配置历史列表"]');
|
|
await choose('历史时序 A');
|
|
await expect.poll(() => historyHeld).toBe(true);
|
|
await choose('历史时序 B');
|
|
await expect(history.getByRole('cell', { name: '22', exact: true })).toBeVisible();
|
|
const delayedHistory = page.waitForResponse(r => r.url().includes('/configuration/history?') && r.url().includes(ids[0]));
|
|
releaseHistory(); await delayedHistory;
|
|
await page.evaluate(() => new Promise(requestAnimationFrame));
|
|
await expect(history.getByRole('cell', { name: '11', exact: true })).toHaveCount(0);
|
|
await choose('历史时序 A');
|
|
await history.getByRole('button', { name: '查看差异 / 恢复', exact: true }).click();
|
|
await expect.poll(() => diffHeld).toBe(true);
|
|
await choose('历史时序 B');
|
|
await history.getByRole('button', { name: '查看差异 / 恢复', exact: true }).click();
|
|
const dialog = page.getByRole('dialog', { name: '配置变更对比', exact: true });
|
|
await expect(dialog).toContainText('历史时序 B');
|
|
await expect(dialog).toContainText('old-B');
|
|
const delayedDiff = page.waitForResponse(r => r.url().includes('/configuration/diff?') && r.url().includes(ids[0]));
|
|
releaseDiff(); await delayedDiff;
|
|
await page.evaluate(() => new Promise(requestAnimationFrame));
|
|
await expect(dialog).toContainText('历史时序 B');
|
|
await expect(dialog).not.toContainText('old-A');
|
|
await dialog.getByRole('button', { name: '恢复此版本', exact: true }).click();
|
|
const confirm = page.getByRole('dialog', { name: '恢复版本', exact: true });
|
|
await expect(confirm).toContainText('历史时序 B');
|
|
await expect(confirm).toContainText('22');
|
|
await confirm.getByRole('button', { name: /取消|Cancel/ }).click();
|
|
} finally {
|
|
releaseHistory(); releaseDiff();
|
|
await page.unrouteAll({ behavior: 'wait' }).catch(() => {});
|
|
for (const id of ids) await page.request.delete(`/api/v1/proxies/${id}`, { headers }).catch(() => {});
|
|
}
|
|
});
|
|
|
|
test('监控自动刷新保留间隔草稿并避免重叠请求', async ({ page }) => {
|
|
await page.clock.install();
|
|
let requests = 0, release!: () => void;
|
|
const gate = new Promise<void>(resolve => { release = resolve; });
|
|
await page.route('**/api/v1/monitoring', async route => {
|
|
requests++;
|
|
if (requests === 2) await gate;
|
|
await route.fulfill({ json: { gateways: [], health: [], intervalSeconds: 30 } });
|
|
});
|
|
try {
|
|
await page.goto('/');
|
|
await page.getByRole('navigation').getByRole('button', { name: '监控运维', exact: true }).click();
|
|
const interval = page.getByRole('spinbutton', { name: '健康检查间隔秒', exact: true });
|
|
await expect(interval).toHaveValue('30');
|
|
await interval.fill('60'); await interval.press('Tab');
|
|
await page.clock.runFor(5000);
|
|
await expect.poll(() => requests).toBe(2);
|
|
await page.clock.runFor(10000);
|
|
expect(requests).toBe(2);
|
|
const refreshed = page.waitForResponse(r => r.url().endsWith('/api/v1/monitoring'));
|
|
release(); await refreshed;
|
|
await expect(interval).toHaveValue('60');
|
|
await expect(page.getByText('(未保存)', { exact: true })).toBeVisible();
|
|
await interval.fill('5'); await interval.press('Tab');
|
|
await expect(page.getByRole('button', { name: '保存检查间隔', exact: true })).toBeDisabled();
|
|
// Pause only browser refresh; it must not send a setting that stops health checks.
|
|
const refreshSwitch = page.locator('.el-switch').filter({ has: page.getByRole('switch', { name: '自动刷新监控', exact: true }) });
|
|
await refreshSwitch.click();
|
|
await page.clock.runFor(10000);
|
|
expect(requests).toBe(2);
|
|
await refreshSwitch.click();
|
|
await page.evaluate(() => Object.defineProperty(document, 'visibilityState', { configurable: true, value: 'hidden' }));
|
|
await page.clock.runFor(10000);
|
|
expect(requests).toBe(2);
|
|
await page.evaluate(() => {
|
|
Object.defineProperty(document, 'visibilityState', { configurable: true, value: 'visible' });
|
|
document.dispatchEvent(new Event('visibilitychange'));
|
|
});
|
|
await expect.poll(() => requests).toBe(3);
|
|
await page.getByRole('navigation').getByRole('button', { name: '代理管理', exact: true }).click();
|
|
await page.clock.runFor(10000);
|
|
expect(requests).toBe(3);
|
|
} finally {
|
|
release(); await page.unrouteAll({ behavior: 'wait' }).catch(() => {});
|
|
}
|
|
});
|