refactor: playwright and tests in one folder

This commit is contained in:
ISA
2025-08-14 12:06:20 +02:00
parent bb68327604
commit 87cbdca79c
12 changed files with 136 additions and 180 deletions

View File

@@ -6,6 +6,6 @@ NEXT_PUBLIC_USE_MOCK_BACKEND_LOOP_START=false
NEXT_PUBLIC_EXPORT_STATIC=false NEXT_PUBLIC_EXPORT_STATIC=false
NEXT_PUBLIC_USE_CGI=false NEXT_PUBLIC_USE_CGI=false
# App-Versionsnummer # App-Versionsnummer
NEXT_PUBLIC_APP_VERSION=1.6.717 NEXT_PUBLIC_APP_VERSION=1.6.718
NEXT_PUBLIC_CPL_MODE=json # json (Entwicklungsumgebung) oder jsSimulatedProd (CPL ->CGI-Interface-Simulator) oder production (CPL-> CGI-Interface Platzhalter) NEXT_PUBLIC_CPL_MODE=json # json (Entwicklungsumgebung) oder jsSimulatedProd (CPL ->CGI-Interface-Simulator) oder production (CPL-> CGI-Interface Platzhalter)

View File

@@ -5,5 +5,5 @@ NEXT_PUBLIC_CPL_API_PATH=/CPL
NEXT_PUBLIC_EXPORT_STATIC=true NEXT_PUBLIC_EXPORT_STATIC=true
NEXT_PUBLIC_USE_CGI=true NEXT_PUBLIC_USE_CGI=true
# App-Versionsnummer # App-Versionsnummer
NEXT_PUBLIC_APP_VERSION=1.6.717 NEXT_PUBLIC_APP_VERSION=1.6.718
NEXT_PUBLIC_CPL_MODE=production NEXT_PUBLIC_CPL_MODE=production

3
.gitignore vendored
View File

@@ -14,6 +14,9 @@
/playwright-report/ /playwright-report/
/blob-report/ /blob-report/
/playwright/.cache/ /playwright/.cache/
playwright/report/
playwright/test-results/
playwright/.cache/
# next.js # next.js
/.next/ /.next/

View File

@@ -1,3 +1,8 @@
## [1.6.718] 2025-08-14
- Feat: Analogeingänge (Messwerteingänge) Modal
---
## [1.6.717] 2025-08-14 ## [1.6.717] 2025-08-14
- feat: close button and maximize modal - feat: close button and maximize modal

View File

@@ -1,6 +1,7 @@
module.exports = { module.exports = {
testEnvironment: "jest-environment-jsdom", testEnvironment: "jest-environment-jsdom",
setupFilesAfterEnv: ["<rootDir>/jest.setup.ts"], setupFilesAfterEnv: ["<rootDir>/jest.setup.ts"],
testPathIgnorePatterns: ["/node_modules/", "/playwright/"],
moduleNameMapper: { moduleNameMapper: {
"\\.(css|less|scss|sass)$": "identity-obj-proxy", "\\.(css|less|scss|sass)$": "identity-obj-proxy",
"^bootstrap-icons/font/bootstrap-icons.css$": "^bootstrap-icons/font/bootstrap-icons.css$":

4
package-lock.json generated
View File

@@ -1,12 +1,12 @@
{ {
"name": "cpl-v4", "name": "cpl-v4",
"version": "1.6.717", "version": "1.6.718",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "cpl-v4", "name": "cpl-v4",
"version": "1.6.717", "version": "1.6.718",
"dependencies": { "dependencies": {
"@fontsource/roboto": "^5.1.0", "@fontsource/roboto": "^5.1.0",
"@headlessui/react": "^2.2.4", "@headlessui/react": "^2.2.4",

View File

@@ -1,6 +1,6 @@
{ {
"name": "cpl-v4", "name": "cpl-v4",
"version": "1.6.717", "version": "1.6.718",
"private": true, "private": true,
"scripts": { "scripts": {
"dev": "next dev", "dev": "next dev",
@@ -15,7 +15,8 @@
"test:e2e": "playwright test", "test:e2e": "playwright test",
"test:e2e:ui": "playwright test --ui", "test:e2e:ui": "playwright test --ui",
"test:e2e:debug": "playwright test --debug", "test:e2e:debug": "playwright test --debug",
"test:e2e:report": "playwright show-report", "test:e2e:report": "playwright show-report playwright/report",
"test:e2e:clean": "rimraf playwright/report playwright/test-results playwright/.cache blob-report test-results playwright-report",
"prepare": "husky install", "prepare": "husky install",
"bump-version": "node ./scripts/bumpVersion.js", "bump-version": "node ./scripts/bumpVersion.js",
"mocks:cable": "node ./mocks/scripts/fetchCableData.mjs --insecure", "mocks:cable": "node ./mocks/scripts/fetchCableData.mjs --insecure",

View File

@@ -4,7 +4,7 @@ import { defineConfig, devices } from "@playwright/test";
* @see https://playwright.dev/docs/test-configuration * @see https://playwright.dev/docs/test-configuration
*/ */
export default defineConfig({ export default defineConfig({
testDir: "./tests", testDir: "./playwright/tests",
/* Run tests in files in parallel */ /* Run tests in files in parallel */
fullyParallel: true, fullyParallel: true,
/* Fail the build on CI if you accidentally left test.only in the source code. */ /* Fail the build on CI if you accidentally left test.only in the source code. */
@@ -14,7 +14,11 @@ export default defineConfig({
/* Opt out of parallel tests on CI. */ /* Opt out of parallel tests on CI. */
workers: process.env.CI ? 1 : undefined, workers: process.env.CI ? 1 : undefined,
/* Reporter to use. See https://playwright.dev/docs/test-reporters */ /* Reporter to use. See https://playwright.dev/docs/test-reporters */
reporter: "html", // Write HTML reports to a single folder under ./playwright/report
reporter: [["html", { outputFolder: "playwright/report" }]],
/* Where to put test artifacts (screenshots, videos, traces, etc.) */
outputDir: "playwright/test-results",
/* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */ /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
use: { use: {
/* Base URL to use in actions like `await page.goto('/')`. */ /* Base URL to use in actions like `await page.goto('/')`. */

View File

@@ -0,0 +1,114 @@
import { test, expect } from "@playwright/test";
/*
Centralized location for Playwright specs.
(Originally moved from tests/analogInputs.spec.ts)
*/
test("test", async ({ page }) => {
await page.goto("http://localhost:3000/analogInputs");
await expect(
page.getByRole("heading", { name: "Messwerteingänge" }).nth(1)
).toBeVisible();
await expect(page.getByRole("cell", { name: "Eingang" })).toBeVisible();
await expect(page.getByRole("cell", { name: "Messwert" })).toBeVisible();
await expect(page.getByRole("cell", { name: "Einheit" })).toBeVisible();
await expect(page.getByRole("cell", { name: "Bezeichnung" })).toBeVisible();
await expect(page.getByRole("cell", { name: "Einstellungen" })).toBeVisible();
await expect(
page.getByRole("cell", { name: "Messkurve", exact: true })
).toBeVisible();
await expect(page.getByText("1", { exact: true })).toBeVisible();
await expect(page.getByText("2", { exact: true })).toBeVisible();
await expect(page.getByText("3", { exact: true })).toBeVisible();
await expect(
page.getByRole("cell", { name: "4", exact: true }).locator("path")
).toBeVisible();
await expect(
page.getByRole("cell", { name: "5", exact: true })
).toBeVisible();
await expect(page.getByText("6", { exact: true })).toBeVisible();
await expect(
page.getByRole("cell", { name: "7", exact: true })
).toBeVisible();
await expect(
page.getByRole("cell", { name: "8", exact: true })
).toBeVisible();
await expect(page.locator(".border.p-2.text-center").first()).toBeVisible();
await expect(
page
.getByRole("row", { name: "2 5.67 °C Temperatur" })
.getByRole("button")
.first()
).toBeVisible();
await expect(page.locator("tr:nth-child(3) > td:nth-child(5)")).toBeVisible();
await expect(
page
.getByRole("row", { name: "0.01 V AE 4 Messkurve anzeigen" })
.getByRole("button")
.first()
).toBeVisible();
await expect(
page
.getByRole("row", { name: "8 -0.00 mA AE 8 Messkurve" })
.getByLabel("Messkurve anzeigen")
).toBeVisible();
await page.getByRole("cell", { name: "1", exact: true }).click();
await page.locator(".border.p-2.text-center").first().click();
await expect(
page.getByRole("heading", { name: "Einstellungen Messwerteingang" })
).toBeVisible();
await expect(page.getByText("Bezeichnung:")).toBeVisible();
await expect(page.getByText("Offset:")).toBeVisible();
await expect(page.getByText("Faktor:")).toBeVisible();
await expect(page.getByText("Einheit:")).toBeVisible();
await expect(page.getByText("Speicherintervall:")).toBeVisible();
await expect(page.getByRole("button", { name: "Speichern" })).toBeVisible();
await expect(
page.getByRole("button", { name: "Modal schließen" })
).toBeVisible();
await expect(
page.getByText(
"Einstellungen Messwerteingang 1Bezeichnung:Offset:Faktor:Einheit:"
)
).toBeVisible();
await page.getByRole("button", { name: "Modal schließen" }).click();
await page
.getByRole("row", { name: "1 126.63 V AE 1 Messkurve" })
.getByLabel("Messkurve anzeigen")
.click();
await expect(
page.getByText(
"Messkurve Messwerteingang 1Eingang 1VonBisAlle MesswerteDaten laden"
)
).toBeVisible();
await expect(
page.getByRole("heading", { name: "Messkurve Messwerteingang" })
).toBeVisible();
await expect(page.getByText("Eingang 1VonBisAlle")).toBeVisible();
await expect(page.getByRole("button", { name: "Daten laden" })).toBeVisible();
await expect(
page.getByRole("button", { name: "Alle Messwerte " })
).toBeVisible();
await expect(page.getByText("Von")).toBeVisible();
await expect(page.getByText("Bis")).toBeVisible();
await expect(page.locator("div").filter({ hasText: /^Von$/ })).toBeVisible();
await expect(
page.locator("div").filter({ hasText: /^Von$/ }).getByRole("textbox")
).toBeVisible();
await expect(page.locator("div").filter({ hasText: /^Bis$/ })).toBeVisible();
await expect(
page.locator("div").filter({ hasText: /^Bis$/ }).getByRole("textbox")
).toBeVisible();
await expect(page.getByRole("img")).toBeVisible();
await page.getByRole("button", { name: "Alle Messwerte " }).click();
await page.getByRole("option", { name: "Stündlich" }).click();
await page.getByRole("button", { name: "Stündlich " }).click();
await page.getByRole("option", { name: "Täglich" }).click();
await page.getByRole("button", { name: "Fullscreen" }).click();
await page.getByRole("button", { name: "Exit fullscreen" }).click();
await expect(page.getByRole("button", { name: "Fullscreen" })).toBeVisible();
await expect(
page.getByRole("button", { name: "Modal schließen" })
).toBeVisible();
await page.getByRole("button", { name: "Modal schließen" }).click();
});

View File

@@ -1,94 +0,0 @@
import { test, expect, Page, Locator } from "@playwright/test";
// Helpers
function cardByCableName(page: Page, name: string): Locator {
// Find a module card that contains the visible cable name text
return page.locator("div.relative.bg-gray-300", { hasText: name }).first();
}
function slotOverlayIn(card: Locator): Locator {
// Slot overlay is the only element with absolute inset-0 inside the card
return card.locator("div.absolute.inset-0");
}
async function setDeviceEvents(
page: Page,
{ ksx, ksy, ksz }: { ksx?: number[]; ksy?: number[]; ksz?: number[] }
) {
await page.evaluate(
({ ksx, ksy, ksz }) => {
interface W {
loopMeasurementEvent?: number[];
tdrMeasurementEvent?: number[];
alignmentEvent?: number[];
}
const w = window as unknown as W;
if (ksx) w.loopMeasurementEvent = ksx;
if (ksy) w.tdrMeasurementEvent = ksy;
if (ksz) w.alignmentEvent = ksz;
},
{ ksx, ksy, ksz }
);
}
// Notes:
// - On /kabelueberwachung, the global overlay is hidden and each module shows its own overlay when active.
// - DeviceEventsBridge polls the window arrays every 2 seconds.
// - Dev mode serves /api/cpl/kabelueberwachungAPIHandler which initializes those arrays from mocks.
test.describe("Kabelüberwachung per-slot overlays", () => {
test.beforeEach(async ({ page }) => {
await page.goto("http://localhost:3000/kabelueberwachung");
// Ensure page has rendered modules
await expect(page.getByText("Rack 1")).toBeVisible();
// Wait a moment for initial device arrays and first poll
await page.waitForTimeout(2500);
});
test("only active slot is blocked; others remain usable", async ({
page,
}) => {
const card1 = cardByCableName(page, "Kabel 1"); // slot 1 (index 0) has events in mock
const card2 = cardByCableName(page, "Kabel 2"); // slot 2 (index 1) is inactive in mock
// 1) Initial: overlay visible on Kabel 1, not on Kabel 2
await expect(slotOverlayIn(card1)).toBeVisible();
await expect(slotOverlayIn(card2)).toHaveCount(0);
// 2) Interact with Kabel 2 while Kabel 1 is blocked
await expect(card2.getByRole("button", { name: "ISO" })).toBeVisible();
await card2.getByRole("button", { name: "ISO" }).click();
// ISO modal should open
await expect(page.getByText("Isolationswiderstand")).toBeVisible();
// Close modal with Escape
await page.keyboard.press("Escape");
await expect(page.getByText("Isolationswiderstand")).toHaveCount(0);
// 3) Dynamically switch overlay from Kabel 1 to Kabel 2
const zero = new Array(32).fill(0);
const ksx = zero.slice();
ksx[1] = 1; // activate Schleife for slot 2
await setDeviceEvents(page, { ksx });
// Wait for bridge poll and UI update
await page.waitForTimeout(2500);
await expect(slotOverlayIn(card1)).toHaveCount(0);
await expect(slotOverlayIn(card2)).toBeVisible();
// Percentage text should show in the overlay (e.g., "12%")
await expect(slotOverlayIn(card2).getByText(/%$/)).toBeVisible();
});
test("global overlay is not shown on kabelueberwachung page", async ({
page,
}) => {
// A full-screen overlay would be fixed inset-0 at document level; ensure none
const globalOverlay = page.locator(
'div.fixed.inset-0:has-text("Bitte warten…")'
);
await expect(globalOverlay).toHaveCount(0);
});
});

View File

@@ -1,78 +0,0 @@
import { test, expect, Page } from "@playwright/test";
// Simple visual smoke test for /kabelueberwachung
// Creates / compares a full-page screenshot (baseline generated on first run with --update-snapshots)
// To update baseline: npx playwright test tests/kabelueberwachung-visual.spec.ts --update-snapshots
// To run in UI mode: npx playwright test --ui
// Helper to stabilize dynamic UI before screenshot
test.describe("Kabelüberwachung Visual", () => {
test("test", async ({ page }) => {
// Globales Auto-Highlight für jeden Klick (nur lokale Debug-Hilfe)
await page.addInitScript(() => {
interface HighlightFlagWindow extends Window {
__PW_CLICK_HIGHLIGHT__?: boolean;
}
const w = window as HighlightFlagWindow;
if (w.__PW_CLICK_HIGHLIGHT__) return; // einmalig
w.__PW_CLICK_HIGHLIGHT__ = true;
document.addEventListener(
"click",
(ev) => {
const el = ev.target as HTMLElement | null;
if (!el || !(el instanceof HTMLElement)) return;
const prev = el.style.outline;
el.style.outline = "3px solid #ff00aa";
setTimeout(() => {
el.style.outline = prev;
}, 600);
},
true // capture, damit nichts das Event vorher stoppt
);
});
await page.goto("http://localhost:3000/kabelueberwachung");
await page
.locator(".bg-littwin-blue.text-white.text-\\[0\\.625rem\\]")
.first()
.click();
//warte 1 Sekunde
await page.waitForTimeout(2000);
await page.getByRole("button", { name: "Daten laden" }).click();
await page.waitForTimeout(2000);
await page.getByRole("button", { name: "Messkurve" }).click();
await page.waitForTimeout(2000);
await page.getByRole("option", { name: "Meldungen" }).click();
await page.waitForTimeout(2000);
await page.getByRole("button", { name: "" }).click();
await page.waitForTimeout(2000);
page.once("dialog", (dialog) => {
console.log(`Dialog message: ${dialog.message()}`);
dialog.dismiss().catch(() => {});
});
await page.waitForTimeout(2000);
await page.locator(".flex > button:nth-child(2)").first().click();
await page.waitForTimeout(2000);
await page.getByRole("button", { name: "Messkurve" }).click();
await page.waitForTimeout(2000);
await page.getByRole("option", { name: "Meldungen" }).click();
await page.waitForTimeout(2000);
await page.getByRole("button", { name: "" }).click();
await page.waitForTimeout(2000);
await page
.locator(".bg-littwin-blue.text-white.cursor-pointer")
.first()
.click();
await page.waitForTimeout(2000);
await page.getByRole("button", { name: "Messkurve" }).click();
await page.waitForTimeout(2000);
await page.getByRole("option", { name: "Meldungen" }).click();
await page.waitForTimeout(2000);
await page.getByRole("button", { name: "" }).click();
await page.waitForTimeout(2000);
});
});
//zum ausführen
// npx playwright test kabelueberwachung-visual.spec.ts --project=chromium --headed
//zum aufzeichnen
// npx playwright codegen http://localhost:3000/kabelueberwachung --channel=chrome