Files
Enelix-EMS/services/license/changes/20261006-heat-pump-header/tests/browser-smoke.mjs
T
2026-10-06 14:35:17 +00:00

94 lines
6.8 KiB
JavaScript

import assert from 'node:assert/strict';
import fs from 'node:fs/promises';
import path from 'node:path';
import { createRequire } from 'node:module';
import { publicWizardCatalog, normalizeSystemConfiguration, recommendLicenses } from '../portal-domain.mjs';
import { publicCatalog } from '../stripe.mjs';
const root = path.resolve(path.dirname(new URL(import.meta.url).pathname), '..');
const require = createRequire(process.env.BROWSER_PACKAGE || import.meta.url);
const { chromium } = require('playwright');
const options = { headless: true };
if (process.env.BROWSER_EXECUTABLE) {
options.executablePath = process.env.BROWSER_EXECUTABLE;
const loaded = require('@sparticuz/chromium');
const standalone = loaded.default || loaded;
options.args = standalone.args.filter(arg => !['--single-process', '--disable-web-security', '--allow-running-insecure-content', '--disable-site-isolation-trials'].includes(arg) && !arg.startsWith('--disable-features='));
}
const browser = await chromium.launch(options);
let plants = [];
let submitted;
const errors = [];
const json = (route, value) => route.fulfill({ contentType: 'application/json', body: JSON.stringify(value) });
try {
const page = await browser.newPage({ viewport: { width: 1440, height: 1000 }, locale: 'de-CH' });
page.on('pageerror', error => errors.push(error.message));
await page.route('**/*', async route => {
const url = new URL(route.request().url());
if (url.pathname === '/api/catalog') return json(route, { items: publicCatalog(), vatRate: 8.1 });
if (url.pathname === '/api/wizard-catalog') return json(route, publicWizardCatalog());
if (url.pathname === '/api/session') return json(route, { authenticated: true, user: { name: 'Testkonto', email: 'portal-test@example.invalid' }, organization: { type: 'personal', role: 'owner', name: 'Testbereich' }, organizations: [] });
if (url.pathname === '/healthz') return json(route, { stripe: 'not_configured' });
if (url.pathname === '/api/plants') return json(route, { plants });
if (url.pathname === '/api/orders') return json(route, { orders: [] });
if (url.pathname === '/api/configurations') {
submitted = normalizeSystemConfiguration(route.request().postDataJSON());
const recommendation = recommendLicenses(submitted);
plants = [{ id: 'test-plant', name: submitted.name, location: submitted.location, type: submitted.siteType, manager_variant: 'manager_standard', configuration_id: 'test-config', configuration_json: JSON.stringify(submitted), recommendation_json: JSON.stringify(recommendation) }];
return json(route, { plant: plants[0], configurationId: 'test-config', packageReady: false, purchaseProposal: { required: true } });
}
const file = url.pathname === '/' ? 'index.html' : url.pathname.slice(1);
if (!['index.html', 'styles.css', 'docs-portal.css', 'app.js'].includes(file)) return route.fulfill({ status: 404, body: 'Not found' });
return route.fulfill({ contentType: file.endsWith('.js') ? 'text/javascript' : file.endsWith('.css') ? 'text/css' : 'text/html', body: await fs.readFile(path.join(root, 'public', file)) });
});
await page.goto('http://portal.test/');
await page.locator('#portalApp:not(.hidden)').waitFor();
for (const width of [1440, 768, 390, 320]) {
await page.setViewportSize({ width, height: 1000 });
const layout = await page.locator('.topbar').evaluate(element => {
const nodes = [...element.querySelectorAll('.brand, .header-links a, .account-button')].map(node => {
const box = node.getBoundingClientRect(); return { x: box.x, right: box.right, top: box.top, bottom: box.bottom };
});
return { nodes, viewport: innerWidth, width: document.documentElement.scrollWidth };
});
assert.ok(layout.nodes.every(box => box.x >= 0 && box.right <= width), JSON.stringify({ width, layout }));
for (let i = 1; i < layout.nodes.length; i++) assert.ok(layout.nodes[i - 1].right <= layout.nodes[i].x, JSON.stringify({ width, layout }));
assert.equal(await page.locator('.sidebar a[href^="/docs/"]').count(), 0);
await page.screenshot({ path: path.join(root, `header-${width}.png`), clip: { x: 0, y: 0, width, height: 180 } });
}
await page.setViewportSize({ width: 1440, height: 1000 });
await page.locator('[data-open-new-license]').first().click();
await page.locator('#newLicenseDialog [data-goto="configurator"]').click();
await page.locator('#systemForm [name="name"]').fill('Testanlage');
await page.locator('#systemForm [name="location"]').fill('Teststandort');
for (let i = 0; i < 7; i++) await page.locator('#wizardNext').click();
assert.equal(await page.locator('#wizardStepTitle').textContent(), 'Wärmepumpe');
assert.equal(await page.locator('.wizard-progress li').evaluateAll(nodes => nodes.every(node => node.scrollWidth <= node.clientWidth)), true);
await page.locator('[name="heatPumpQuantity"]').fill('1');
const editor = page.locator('[data-device-editor="heat_pump"]');
await editor.locator('[data-device-property="Kontaktart"]').selectOption('1');
for (const [field, value] of Object.entries({ Kontakt1VariableID: 10001, Kontakt2VariableID: 10002, IstleistungVariableID: 10003, Nennleistung: 4200, Mindestlaufzeit: 1500 })) {
await editor.locator(`[data-device-property="${field}"]`).fill(String(value));
}
await editor.locator('[data-device-property="Rueckmeldungsart"]').selectOption('1');
await page.evaluate(() => window.scrollTo(0, 0));
await page.screenshot({ path: path.join(root, 'wizard-desktop.png'), fullPage: true });
await page.setViewportSize({ width: 390, height: 844 });
await page.screenshot({ path: path.join(root, 'wizard-mobile.png'), fullPage: true });
await page.locator('#wizardNext').click();
await page.locator('#wizardNext').click();
assert.match(await page.locator('#wizardReview').textContent(), /Wärmepumpe/);
await page.locator('#wizardSubmit').click();
await page.locator('#view-licenses.active').waitFor();
assert.equal(submitted.devices.heat_pump.items[0].properties.Mindestlaufzeit, 1500);
assert.equal(submitted.devices.heat_pump.items[0].properties.Kontaktart, 1);
assert.equal(await page.locator('.module-row[data-key="heat_pump"] .module-check').isChecked(), true);
assert.match(await page.locator('#orderItems').textContent(), /Wärmepumpe/);
await page.locator('[data-configure-current]').click();
for (let i = 0; i < 7; i++) await page.locator('#wizardNext').click();
assert.equal(await editor.locator('[data-device-property="Kontakt1VariableID"]').inputValue(), '10001');
assert.equal(await editor.locator('[data-device-property="Mindestlaufzeit"]').inputValue(), '1500');
assert.deepEqual(errors, []);
console.log(JSON.stringify({ ok: true, headerWidths: [1440, 768, 390, 320], wizard: 'configure, save, license proposal, reopen', browserErrors: errors }));
} finally { await browser.close(); }