Files
SystemSimulationApp/tests/manual/system_simulation_skill.mjs
T
2026-09-18 01:40:58 +08:00

76 lines
4.7 KiB
JavaScript

// Run against a separately started backend serving the current frontend build.
// No mocked catalog or simulation; a fresh browser context protects user state.
import { chromium } from '../../frontend/node_modules/playwright/index.mjs';
import fs from 'node:fs/promises';
import path from 'node:path';
import assert from 'node:assert/strict';
const [baseURL, projectPath, output] = process.argv.slice(2);
assert(baseURL && projectPath && output, 'Usage: node tests/manual/system_simulation_skill.mjs BASE_URL PROJECT OUTPUT');
const project = JSON.parse(await fs.readFile(projectPath, 'utf8'));
await fs.mkdir(output, { recursive: true });
const browser = await chromium.launch({ headless: true,
...(process.env.PLAYWRIGHT_CHANNEL ? { channel: process.env.PLAYWRIGHT_CHANNEL } : {}),
...(process.env.PLAYWRIGHT_EXECUTABLE_PATH ? { executablePath: process.env.PLAYWRIGHT_EXECUTABLE_PATH } : {}),
});
const page = await browser.newPage({ viewport: { width: 1440, height: 1000 }, acceptDownloads: true });
const errors = [];
page.on('pageerror', error => errors.push(String(error)));
page.setDefaultTimeout(15000);
try {
await page.goto(baseURL);
await page.getByRole('button', { name: '运行仿真', exact: true }).waitFor();
await page.locator('input[type="file"][accept*=".json"]').setInputFiles(path.resolve(projectPath));
const canvasNodes = page.locator('.flow-canvas .react-flow__node');
await page.waitForFunction(count => document.querySelectorAll('.flow-canvas .react-flow__node').length === count, project.nodes.length);
assert.equal(await canvasNodes.count(), project.nodes.length);
for (const node of project.nodes) {
assert.equal(await page.locator(`.flow-canvas .react-flow__node[data-id="${node.id}"]`).count(), 1);
}
await page.getByRole('button', { name: '检查模型', exact: true }).click();
const expand = page.getByRole('button', { name: '展开仿真控制台', exact: true });
if (await expand.count()) await expand.click();
await page.getByRole('button', { name: '生成系统 XML', exact: true }).click();
const xml = await page.getByLabel('生成的系统 XML', { exact: true }).last().innerText();
assert.match(xml, /schemaVersion="3"/);
assert.match(xml, /name="p0" value="250000"/);
assert.match(xml, /name="T0" value="293.15"/);
await fs.writeFile(path.join(output, 'browser.xml'), xml);
const responsePromise = page.waitForResponse(response => response.url().endsWith('/api/system-xml/simulate-stream'), { timeout: 120000 });
await page.getByRole('button', { name: '运行仿真', exact: true }).click();
const response = await responsePromise;
assert(response.ok(), await response.text());
const events = (await response.text()).trim().split('\n').map(line => JSON.parse(line));
const resultEvent = events.find(event => event.event === 'result');
assert(resultEvent?.result?.success, JSON.stringify(events.slice(-3)));
const result = resultEvent.result;
assert.equal(result.simulatedUntil, project.simulation.t_stop);
assert(Object.values(result.series).every(values => values.every(Number.isFinite)));
const pressure = result.variables.find(variable => variable.componentId === 'tank' && variable.quantity === 'pressure');
assert(pressure && result.series[pressure.key]?.length > 1);
assert(result.series[pressure.key].every(value => Math.abs(value - 250000) < 1e-6));
await fs.writeFile(path.join(output, 'result.json'), JSON.stringify(result));
const downloadPromise = page.waitForEvent('download');
await page.getByRole('button', { name: '导出工程 JSON', exact: true }).click();
await (await downloadPromise).saveAs(path.join(output, 'roundtrip.json'));
const exported = JSON.parse(await fs.readFile(path.join(output, 'roundtrip.json'), 'utf8'));
assert.equal(exported.projectSchemaVersion, 2);
assert.equal(exported.nodes.length, project.nodes.length);
assert.equal(exported.edges.length, project.edges.length);
assert.deepEqual(errors, []);
await page.screenshot({ path: path.join(output, 'acceptance.png'), fullPage: true });
const summary = { browser: browser.version(), components: exported.nodes.length,
connections: exported.edges.length, simulatedUntil: result.simulatedUntil,
sampleCount: result.series.time.length, pressureKey: pressure.key,
browserImport: 'passed', browserExport: 'passed', simulation: 'passed', errors };
await fs.writeFile(path.join(output, 'summary.json'), JSON.stringify(summary, null, 2));
console.log(JSON.stringify(summary));
} catch (error) {
await fs.writeFile(path.join(output, 'failure.txt'), `${error.stack}\n${await page.locator('body').innerText()}`);
await page.screenshot({ path: path.join(output, 'failure.png'), fullPage: true });
throw error;
} finally {
await browser.close();
}