Files
tilbudgivern/tests/lars-planning-final-test.spec.js

241 lines
9.0 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(() => document.body.textContent.length > 200, { timeout: 10000 }).catch(() => {});
}
// ============================================================
// PLANNING DASHBOARD
// ============================================================
test('FLOW 7 - Planlægningsdashboard', async ({ page }) => {
test.setTimeout(90000);
console.log('\n=== FLOW 7: PLANLÆGNINGSDASHBOARD ===');
await loginToApp(page);
await waitForApp(page);
// Click Planning nav button
const planningBtn = page.getByRole('button', { name: /planlæ/i });
const hasPlanning = await planningBtn.isVisible({ timeout: 5000 }).catch(() => false);
console.log(`[PLAN] Planning button visible: ${hasPlanning}`);
if (hasPlanning) {
const btnText = await planningBtn.textContent();
console.log(`[PLAN] Button text: "${btnText?.trim()}"`);
await planningBtn.click();
await page.waitForTimeout(2000);
await waitForApp(page);
}
await screenshot(page, '07_01_planning_page');
const bodyText = await page.textContent('body');
console.log('[PLAN] Planning page content:', bodyText.substring(0, 3000));
// Calendar/planning keywords
const calKeywords = ['januar', 'februar', 'marts', 'april', 'maj', 'juni',
'juli', 'august', 'september', 'oktober', 'november', 'december',
'mandag', 'tirsdag', 'onsdag', 'torsdag', 'fredag', 'lørdag', 'søndag',
'uge', 'kalender', 'opgave', 'projekt', 'ordre', 'tilbud', 'planlagt'];
console.log('[PLAN] Keywords found:');
for (const kw of calKeywords) {
if (bodyText.toLowerCase().includes(kw.toLowerCase())) {
console.log(` ✓ "${kw}"`);
}
}
// Count calendar cells/days
const dayCells = await page.locator('td, [class*="day"], [class*="Day"], [class*="cell"]').all();
console.log(`[PLAN] Day cells: ${dayCells.length}`);
// Find week numbers
const weekNumbers = await page.locator('[class*="week"], [class*="Week"]').all();
console.log(`[PLAN] Week elements: ${weekNumbers.length}`);
// Check for projects in calendar
const calItems = await page.locator('[class*="event"], [class*="Event"], [class*="task"], [class*="project"]').all();
console.log(`[PLAN] Calendar items/events: ${calItems.length}`);
// Find navigation buttons
const navBtns = await page.locator('button:visible').all();
console.log('[PLAN] All buttons on planning page:');
for (const btn of navBtns) {
const text = await btn.textContent();
if (text?.trim()) console.log(` "${text.trim()}"`);
}
// Check for headings
const headings = await page.locator('h1, h2, h3, h4').all();
for (const h of headings) {
const text = await h.textContent();
console.log(`[PLAN] Heading: "${text?.trim()}"`);
}
await screenshot(page, '07_02_planning_detail');
console.log('[FLOW 7] DONE');
expect(true).toBe(true);
});
// ============================================================
// FINAL REVIEW + EXISTING PROJECT
// ============================================================
test('FLOW 5 - Final Review og eksisterende projekter', async ({ page }) => {
test.setTimeout(90000);
console.log('\n=== FLOW 5: FINAL REVIEW ===');
await loginToApp(page);
await waitForApp(page);
// Navigate to project flow first
const projektFlowBtn = page.getByRole('button', { name: /projekt flow/i });
if (await projektFlowBtn.isVisible({ timeout: 5000 }).catch(() => false)) {
await projektFlowBtn.click();
await page.waitForTimeout(1000);
await waitForApp(page);
}
// Click "Eksisterende" to see existing projects
const eksisterendeBtn = page.getByRole('button', { name: /eksisterende/i });
const hasEks = await eksisterendeBtn.isVisible({ timeout: 5000 }).catch(() => false);
console.log(`[REVIEW] Eksisterende button: ${hasEks}`);
if (hasEks) {
await eksisterendeBtn.click();
await page.waitForTimeout(2000);
await waitForApp(page);
await screenshot(page, '05_01_existing_projects');
const projContent = await page.textContent('body');
console.log('[REVIEW] Existing projects:', projContent.substring(0, 2000));
// Find project items
const projItems = await page.locator('[class*="project"], [class*="Project"]').filter({ hasNot: page.locator('nav') }).all();
console.log(`[REVIEW] Project items: ${projItems.length}`);
// Find clickable project rows
const projLinks = await page.locator('li, tr, [role="listitem"], [class*="item"]').filter({ hasText: /kr|tilbud|tagpap|tag/i }).all();
console.log(`[REVIEW] Project-like rows: ${projLinks.length}`);
// Click on first project to open it
if (projItems.length > 0) {
await projItems[0].click();
await page.waitForTimeout(2000);
await waitForApp(page);
await screenshot(page, '05_02_project_opened');
}
// Look for Final Review step
const finalReviewStep = page.locator('[class*="step"]').filter({ hasText: /final review/i });
if (await finalReviewStep.isVisible({ timeout: 3000 }).catch(() => false)) {
const stepClass = await finalReviewStep.getAttribute('class');
const isDisabled = stepClass?.includes('disabled');
console.log(`[REVIEW] Final Review step visible, disabled=${isDisabled}`);
if (!isDisabled) {
await finalReviewStep.click();
await page.waitForTimeout(2000);
await waitForApp(page);
await screenshot(page, '05_03_final_review');
const reviewContent = await page.textContent('body');
console.log('[REVIEW] Final Review content:', reviewContent.substring(0, 3000));
// Price keywords
const priceContent = reviewContent.match(/\d+[\.,]\d+\s*kr|kr\.?\s*\d+[\.,]?\d*/g) || [];
console.log('[REVIEW] Price values found:', priceContent.slice(0, 10));
// Action buttons
const actionBtns = await page.locator('button:visible').all();
console.log('[REVIEW] Action buttons:');
for (const btn of actionBtns) {
const text = await btn.textContent();
const disabled = await btn.isDisabled();
if (text?.trim()) console.log(` "${text.trim()}" disabled=${disabled}`);
}
}
}
}
await screenshot(page, '05_04_final_state');
console.log('[FLOW 5] DONE');
expect(true).toBe(true);
});
// ============================================================
// AI BUDGET SECTION
// ============================================================
test('FLOW 6 - AI Budget sektion', async ({ page }) => {
test.setTimeout(90000);
console.log('\n=== FLOW 6: AI BUDGET ===');
await loginToApp(page);
await waitForApp(page);
// Click AI Budget nav item
const aiBudgetBtn = page.getByRole('button', { name: /ai budget/i });
const hasAiBudget = await aiBudgetBtn.isVisible({ timeout: 5000 }).catch(() => false);
console.log(`[AI] AI Budget button: ${hasAiBudget}`);
if (hasAiBudget) {
await aiBudgetBtn.click();
await page.waitForTimeout(2000);
await waitForApp(page);
await screenshot(page, '06_01_ai_budget_page');
const content = await page.textContent('body');
console.log('[AI] AI Budget content:', content.substring(0, 3000));
const headings = await page.locator('h1, h2, h3, h4').all();
for (const h of headings) {
const text = await h.textContent();
console.log(`[AI] Heading: "${text?.trim()}"`);
}
const buttons = await page.locator('button:visible').all();
console.log('[AI] Buttons:');
for (const btn of buttons) {
const text = await btn.textContent();
if (text?.trim()) console.log(` "${text.trim()}"`);
}
}
await screenshot(page, '06_02_ai_budget_final');
console.log('[FLOW 6] DONE');
expect(true).toBe(true);
});