76 lines
4.7 KiB
JavaScript
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();
|
|
}
|