Files
tilbudgivern/tests/lars-geometry-test.spec.js

189 lines
7.7 KiB
JavaScript

// @ts-check
const { test, expect } = require('@playwright/test');
const path = require('path');
const fs = require('fs');
const BASE_URL = process.env.PLAYWRIGHT_BASE_URL || 'https://tilbudsgiveren.alw.dk';
const SCREENSHOT_DIR = '/mnt/HC_Volume_103713257/tilbudgivern/screenshots/lars-test';
if (!fs.existsSync(SCREENSHOT_DIR)) {
fs.mkdirSync(SCREENSHOT_DIR, { recursive: true });
}
async function screenshot(page, name) {
const filePath = path.join(SCREENSHOT_DIR, `${name}.png`);
await page.screenshot({ path: filePath, fullPage: true });
console.log(`[SS] ${name}.png`);
}
async function loginToApp(page) {
await page.goto(BASE_URL + '/', { waitUntil: 'domcontentloaded', timeout: 30000 });
await page.waitForTimeout(2000);
const hasPassword = await page.locator('input[type="password"]').isVisible({ timeout: 3000 }).catch(() => false);
if (!hasPassword) return true;
await page.locator('input[type="text"]').first().fill('toemrer');
await page.locator('input[type="password"]').first().fill('tilbud2024');
const loginBtn = page.getByRole('button', { name: /log ind/i });
if (await loginBtn.isVisible({ timeout: 3000 }).catch(() => false)) {
await loginBtn.click();
} else {
await page.keyboard.press('Enter');
}
await page.waitForTimeout(3000);
await page.waitForLoadState('networkidle', { timeout: 20000 }).catch(() => {});
return true;
}
async function waitForApp(page) {
await page.waitForTimeout(1500);
await page.waitForLoadState('networkidle', { timeout: 10000 }).catch(() => {});
await page.waitForFunction(() => {
const body = document.body.textContent || '';
return body.length > 200;
}, { timeout: 10000 }).catch(() => {});
}
// ============================================================
// GEOMETRY STEP DETAILED TEST
// ============================================================
test('FLOW 3 - Geometri step i detaljer', async ({ page }) => {
test.setTimeout(120000);
console.log('\n=== FLOW 3: GEOMETRI ===');
await loginToApp(page);
await waitForApp(page);
// Create project to reach geometry
const nytBtn = page.getByRole('button', { name: /nyt projekt/i });
if (await nytBtn.isVisible({ timeout: 5000 }).catch(() => false)) {
await nytBtn.click();
await page.waitForTimeout(500);
}
const projName = page.locator('#field-projectName');
if (await projName.isVisible({ timeout: 3000 }).catch(() => false)) await projName.fill('Lars Geometri Test');
const custName = page.locator('#field-customerName');
if (await custName.isVisible({ timeout: 2000 }).catch(() => false)) await custName.fill('Søren Jensen');
const email = page.locator('#field-customerEmail');
if (await email.isVisible({ timeout: 2000 }).catch(() => false)) await email.fill('soren@test.dk');
const desc = page.locator('#field-projectDescription');
if (await desc.isVisible({ timeout: 2000 }).catch(() => false)) await desc.fill('Sadeltag 180 m2 teglsten');
const gemBtn = page.getByRole('button', { name: /gem projekt/i });
if (await gemBtn.isVisible({ timeout: 3000 }).catch(() => false)) {
await gemBtn.click();
await page.waitForTimeout(4000);
await waitForApp(page);
}
await screenshot(page, '03_01_geometry_step');
const bodyText = await page.textContent('body');
console.log('[GEOM] Page text:', bodyText.substring(0, 2000));
// Find all numeric inputs
const numInputs = await page.locator('input[type="number"]:visible').all();
console.log(`[GEOM] Numeric inputs: ${numInputs.length}`);
for (let i = 0; i < numInputs.length; i++) {
const input = numInputs[i];
const id = await input.getAttribute('id');
const placeholder = await input.getAttribute('placeholder');
const min = await input.getAttribute('min');
const max = await input.getAttribute('max');
const step = await input.getAttribute('step');
// Find parent label
const parentLabel = await input.evaluate(el => {
const label = el.closest('div')?.querySelector('label');
return label ? label.textContent : '';
});
console.log(` [${i}] id="${id}" placeholder="${placeholder}" min=${min} max=${max} step=${step} label="${parentLabel?.trim()}"`);
}
// Check for roof type buttons
const roofTypeChips = await page.locator('button, .MuiChip-root, [role="button"]').filter({ hasText: /sadeltag|fladtag|pult|kviste|mansard|valmtag|køben/i }).all();
console.log(`[GEOM] Roof type selector buttons: ${roofTypeChips.length}`);
for (const chip of roofTypeChips) {
const text = await chip.textContent();
const selected = await chip.getAttribute('aria-selected');
const classes = await chip.getAttribute('class');
console.log(` Roof type: "${text?.trim()}" aria-selected=${selected}`);
}
// Check SVG visualization
const svgs = await page.locator('svg').all();
console.log(`[GEOM] SVG elements: ${svgs.length}`);
for (const svg of svgs) {
const w = await svg.getAttribute('width');
const h = await svg.getAttribute('height');
const viewBox = await svg.getAttribute('viewBox');
console.log(` SVG: ${w}x${h} viewBox="${viewBox}"`);
}
// Fill in dimensions - realistic for sadeltag
if (numInputs.length >= 1) {
await numInputs[0].triple_click ? numInputs[0].triple_click() : await numInputs[0].click({ clickCount: 3 });
await numInputs[0].fill('12'); // Bredde 12m
console.log('[GEOM] Set input[0] = 12 (bredde)');
}
if (numInputs.length >= 2) {
await numInputs[1].click({ clickCount: 3 });
await numInputs[1].fill('15'); // Længde 15m
console.log('[GEOM] Set input[1] = 15 (længde)');
}
if (numInputs.length >= 3) {
await numInputs[2].click({ clickCount: 3 });
await numInputs[2].fill('30'); // Hældning 30°
console.log('[GEOM] Set input[2] = 30 (hældning)');
}
await screenshot(page, '03_02_dimensions_filled');
// Check for tagmateriale selector
const tagmaterialeLabels = await page.locator('label, [class*="label"]').filter({ hasText: /tagmateriale|materiale/i }).all();
console.log(`[GEOM] Tagmateriale labels: ${tagmaterialeLabels.length}`);
// Check calculation button
const calcBtn = page.getByRole('button', { name: /beregn|gem nu|opdater|beregn areal/i });
const hasCalc = await calcBtn.isVisible({ timeout: 3000 }).catch(() => false);
console.log(`[GEOM] Calculation button: ${hasCalc}`);
if (hasCalc) {
const calcText = await calcBtn.textContent();
console.log(`[GEOM] Calc button text: "${calcText?.trim()}"`);
await calcBtn.click();
await page.waitForTimeout(5000);
await waitForApp(page);
}
await screenshot(page, '03_03_after_calculation');
const afterCalcText = await page.textContent('body');
console.log('[GEOM] After calc text:', afterCalcText.substring(0, 2000));
// Find area results
const m2Matches = afterCalcText.match(/\d+[,.]?\d*\s*m[²2]/g) || [];
console.log('[GEOM] m² results found:', m2Matches);
// Find "Fortsæt" button
const fortsetBtn = page.getByRole('button', { name: /fortsæt|næste|spring over tagberegning/i });
const hasFortset = await fortsetBtn.isVisible({ timeout: 5000 }).catch(() => false);
console.log(`[GEOM] Fortsæt button: ${hasFortset}`);
if (hasFortset) {
const fortsetText = await fortsetBtn.textContent();
const isEnabled = await fortsetBtn.isEnabled();
console.log(`[GEOM] Fortsæt text: "${fortsetText?.trim()}" enabled=${isEnabled}`);
}
// Check for error messages / validation
const errorMsgs = await page.locator('[class*="error"], [class*="Error"], [role="alert"]').all();
console.log(`[GEOM] Error messages: ${errorMsgs.length}`);
for (const err of errorMsgs) {
const text = await err.textContent();
console.log(` Error: "${text?.trim()}"`);
}
await screenshot(page, '03_04_geometry_final');
console.log('[FLOW 3] DONE');
expect(true).toBe(true);
});