/** * @jest-environment jsdom */ const fs = require("fs"); const path = require("path"); describe("calculate() row223 Ellbow-Rotation Tests", () => { let calculate; beforeEach(() => { // DOM erzeugen document.body.innerHTML = ` `; // Fetch mocken - wird pro Test konfiguriert global.fetch = jest.fn(); // Modul erst JETZT laden (DOM existiert) ({ calculate } = require("../public/calculateActions.js")); }); /** * Hilfsfunktion: Erstellt ein CSV mit definierten row223.x_mm und x226 Wert * @param {number} row223_x_mm - x_mm Wert für row223 (id=223) * @param {number} x226_base - Basis-x_mm Wert für x226-Punkt (wird durch mehrere Punkte erreicht) */ function createTestCSV(row223_x_mm, x226_base) { const csvContent = `id,x_mm,y_mm,z_mm,roll_deg,pitch_deg,seen_by 222,${x226_base},120,65,0,0,3 226,${x226_base},115,61,0,0,3 229,${x226_base - 90},110,55,0,0,3 223,${row223_x_mm},125,62,0,0,3 `; return csvContent; } /** * Berechnet den erwarteten Winkel basierend auf Formel aus calculateActions.js * angleRad = Math.asin(dx / 35) * angleDeg = 90 - angleRad * 180 / Math.PI */ function calculateExpectedAngle(row223_x_mm, x226) { const dx = row223_x_mm - x226; if (Math.abs(dx) > 35) { return null; // Math.asin() erlaubt nur Werte zwischen -1 und 1 } const angleRad = Math.asin(dx / 35); const angleDeg = 90 - angleRad * 180 / Math.PI; return { angleRad, angleDeg, dx }; } // Test 1: row223 mit x_mm = x226 (dx = 0, erwarteter Winkel = 90°) test("row223.x_mm = x226 sollte Winkel von 90° ergeben", async () => { const x226_base = 100; const row223_x_mm = x226_base; // dx = 0 const csvContent = createTestCSV(row223_x_mm, x226_base); global.fetch.mockResolvedValueOnce({ ok: true, headers: { get: () => "text/csv" }, text: async () => csvContent }); const result = await calculate(); // xCount sollte > 2 sein (wir haben 4 Zeilen) expect(result.status).toBe("ok"); // Überprüfe, ob die elbowRotationAFromX223 Berechnung vorhanden ist const elbowRotationCalc = result.calculations.find( c => c.type === "elbowRotationAFromX223" ); expect(elbowRotationCalc).toBeDefined(); expect(elbowRotationCalc.output.angleDeg).toBeCloseTo(90, 1); expect(elbowRotationCalc.output.angleRad).toBeCloseTo(0, 4); }); // Test 2: row223 mit x_mm = x226 + 17.5 (dx = 17.5, erwarteter Winkel = 45°) test("row223.x_mm = x226 + 17.5 sollte Winkel von 45° ergeben", async () => { const x226_base = 100; const row223_x_mm = x226_base + 17.5; // dx = 17.5 const csvContent = createTestCSV(row223_x_mm, x226_base); global.fetch.mockResolvedValueOnce({ ok: true, headers: { get: () => "text/csv" }, text: async () => csvContent }); const result = await calculate(); const expected = calculateExpectedAngle(row223_x_mm, x226_base); expect(result.status).toBe("ok"); const elbowRotationCalc = result.calculations.find( c => c.type === "elbowRotationAFromX223" ); expect(elbowRotationCalc).toBeDefined(); expect(elbowRotationCalc.output.angleDeg).toBeCloseTo(expected.angleDeg, 1); expect(elbowRotationCalc.output.angleRad).toBeCloseTo(expected.angleRad, 3); }); // Test 3: row223 mit x_mm = x226 + 35 (dx = 35, erwarteter Winkel = 0°) test("row223.x_mm = x226 + 35 sollte Winkel von 0° ergeben", async () => { const x226_base = 100; const row223_x_mm = x226_base + 35; // dx = 35, asin(1) = π/2 const csvContent = createTestCSV(row223_x_mm, x226_base); global.fetch.mockResolvedValueOnce({ ok: true, headers: { get: () => "text/csv" }, text: async () => csvContent }); const result = await calculate(); const expected = calculateExpectedAngle(row223_x_mm, x226_base); expect(result.status).toBe("ok"); const elbowRotationCalc = result.calculations.find( c => c.type === "elbowRotationAFromX223" ); expect(elbowRotationCalc).toBeDefined(); expect(elbowRotationCalc.output.angleDeg).toBeCloseTo(expected.angleDeg, 1); expect(elbowRotationCalc.output.angleRad).toBeCloseTo(expected.angleRad, 3); }); // Test 4: row223 mit negativem dx (x_mm = x226 - 17.5, erwarteter Winkel = 135°) test("row223.x_mm = x226 - 17.5 sollte Winkel von 135° ergeben", async () => { const x226_base = 100; const row223_x_mm = x226_base - 17.5; // dx = -17.5 const csvContent = createTestCSV(row223_x_mm, x226_base); global.fetch.mockResolvedValueOnce({ ok: true, headers: { get: () => "text/csv" }, text: async () => csvContent }); const result = await calculate(); const expected = calculateExpectedAngle(row223_x_mm, x226_base); expect(result.status).toBe("ok"); const elbowRotationCalc = result.calculations.find( c => c.type === "elbowRotationAFromX223" ); expect(elbowRotationCalc).toBeDefined(); expect(elbowRotationCalc.output.angleDeg).toBeCloseTo(expected.angleDeg, 1); expect(elbowRotationCalc.output.angleRad).toBeCloseTo(expected.angleRad, 3); }); // Test 5: row223 mit x_mm = x226 - 35 (dx = -35, erwarteter Winkel = 180°) test("row223.x_mm = x226 - 35 sollte Winkel von 180° ergeben", async () => { const x226_base = 100; const row223_x_mm = x226_base - 35; // dx = -35, asin(-1) = -π/2 const csvContent = createTestCSV(row223_x_mm, x226_base); global.fetch.mockResolvedValueOnce({ ok: true, headers: { get: () => "text/csv" }, text: async () => csvContent }); const result = await calculate(); const expected = calculateExpectedAngle(row223_x_mm, x226_base); expect(result.status).toBe("ok"); const elbowRotationCalc = result.calculations.find( c => c.type === "elbowRotationAFromX223" ); expect(elbowRotationCalc).toBeDefined(); expect(elbowRotationCalc.output.angleDeg).toBeCloseTo(expected.angleDeg, 1); expect(elbowRotationCalc.output.angleRad).toBeCloseTo(expected.angleRad, 3); }); // Test 6: xCount > 2 Bedingung wird erfüllt test("xCount muss > 2 sein, damit elbowRotationAFromX223 berechnet wird", async () => { const x226_base = 100; const row223_x_mm = x226_base + 10; const csvContent = createTestCSV(row223_x_mm, x226_base); global.fetch.mockResolvedValueOnce({ ok: true, headers: { get: () => "text/csv" }, text: async () => csvContent }); const result = await calculate(); // Überprüfe, dass die Berechnung stattgefunden hat const elbowXEstimate = result.calculations.find( c => c.type === "elbowXEstimate" ); const elbowRotationCalc = result.calculations.find( c => c.type === "elbowRotationAFromX223" ); expect(elbowXEstimate).toBeDefined(); // xCount wurde berechnet expect(elbowRotationCalc).toBeDefined(); // xCount > 2 und row223 existiert }); // Test 7: Parametrisierter Test mit verschiedenen Winkeln test.each([ { dx: 0, expectedDeg: 90, desc: "dx=0" }, { dx: 5, expectedDeg: 90 - Math.asin(5 / 35) * 180 / Math.PI, desc: "dx=5" }, { dx: 10, expectedDeg: 90 - Math.asin(10 / 35) * 180 / Math.PI, desc: "dx=10" }, { dx: -5, expectedDeg: 90 - Math.asin(-5 / 35) * 180 / Math.PI, desc: "dx=-5" }, { dx: -10, expectedDeg: 90 - Math.asin(-10 / 35) * 180 / Math.PI, desc: "dx=-10" }, ])( "Verschiedene dx Werte sollten korrekte Winkel ergeben ($desc)", async ({ dx, expectedDeg }) => { const x226_base = 100; const row223_x_mm = x226_base + dx; const csvContent = createTestCSV(row223_x_mm, x226_base); global.fetch.mockResolvedValueOnce({ ok: true, headers: { get: () => "text/csv" }, text: async () => csvContent }); const result = await calculate(); const elbowRotationCalc = result.calculations.find( c => c.type === "elbowRotationAFromX223" ); expect(elbowRotationCalc).toBeDefined(); expect(elbowRotationCalc.output.angleDeg).toBeCloseTo(expectedDeg, 1); } ); // Test 8: row223 Input wird korrekt in Output dokumentiert test("row223.x_mm wird korrekt in Berechnung dokumentiert", async () => { const x226_base = 100; const row223_x_mm = 115; const csvContent = createTestCSV(row223_x_mm, x226_base); global.fetch.mockResolvedValueOnce({ ok: true, headers: { get: () => "text/csv" }, text: async () => csvContent }); const result = await calculate(); const elbowRotationCalc = result.calculations.find( c => c.type === "elbowRotationAFromX223" ); expect(elbowRotationCalc).toBeDefined(); expect(elbowRotationCalc.input.x223).toBe(row223_x_mm); expect(elbowRotationCalc.input.x226).toBe(x226_base); }); });