189 lines
7.7 KiB
JavaScript
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);
|
|
});
|