- Implemented a new test suite for production API verification using curl, ensuring all critical endpoints respond correctly and return valid JSON. - Added frontend integration tests to check for critical errors in the UI and verify key UI elements are present and functional. - Created test data for quotes and project calculations to facilitate testing. - Developed a script to check offers in the Ordrestyring system, including detailed task descriptions. - Added tests for generating PDFs from quote data, ensuring the output is valid and contains expected information. - Implemented a test script for the Røsevangen project, integrating with the Ordrestyring API and verifying database entries.
231 lines
7.9 KiB
JavaScript
231 lines
7.9 KiB
JavaScript
const { test, expect } = require('@playwright/test');
|
|
|
|
// Production URL
|
|
const BASE_URL = 'https://tilbudsgiveren.alw.dk';
|
|
|
|
test.describe('Tilbudgivern Production Verification', () => {
|
|
|
|
test('Frontend loads successfully', async ({ page }) => {
|
|
await page.goto(BASE_URL);
|
|
|
|
// Should load without errors
|
|
await expect(page).toHaveTitle(/Tilbudgivern/i);
|
|
|
|
// Should show main interface elements
|
|
await expect(page.locator('body')).toBeVisible();
|
|
|
|
// No critical JavaScript errors
|
|
const errors = [];
|
|
page.on('pageerror', error => errors.push(error.message));
|
|
await page.waitForTimeout(3000); // Wait for JS to load
|
|
expect(errors.filter(e => e.includes('Cannot read properties of undefined'))).toHaveLength(0);
|
|
});
|
|
|
|
test('SmartPackages API works correctly', async ({ request }) => {
|
|
const response = await request.get(`${BASE_URL}/api/smart-packages?limit=5`, {
|
|
headers: {
|
|
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'
|
|
}
|
|
});
|
|
|
|
expect(response.status()).toBe(200);
|
|
|
|
const data = await response.json();
|
|
expect(data.success).toBe(true);
|
|
expect(data.packages).toBeDefined();
|
|
expect(Array.isArray(data.packages)).toBe(true);
|
|
expect(data.packages.length).toBeGreaterThan(0);
|
|
|
|
// Verify package structure
|
|
const packageItem = data.packages[0];
|
|
expect(packageItem).toHaveProperty('id');
|
|
expect(packageItem).toHaveProperty('name');
|
|
expect(packageItem).toHaveProperty('category');
|
|
expect(packageItem).toHaveProperty('difficulty_level');
|
|
});
|
|
|
|
test('Categories API works correctly', async ({ request }) => {
|
|
const response = await request.get(`${BASE_URL}/api/smart-packages/categories`);
|
|
|
|
expect(response.status()).toBe(200);
|
|
|
|
const data = await response.json();
|
|
expect(data.success).toBe(true);
|
|
expect(data.categories).toBeDefined();
|
|
expect(Array.isArray(data.categories)).toBe(true);
|
|
expect(data.categories.length).toBeGreaterThan(0);
|
|
|
|
// Should contain expected categories
|
|
expect(data.categories).toContain('Tagarbejde');
|
|
});
|
|
|
|
test('Materials API works correctly', async ({ request }) => {
|
|
const response = await request.get(`${BASE_URL}/api/materials?limit=5`);
|
|
|
|
expect(response.status()).toBe(200);
|
|
|
|
const data = await response.json();
|
|
expect(data.success).toBe(true);
|
|
expect(data.materials).toBeDefined();
|
|
expect(Array.isArray(data.materials)).toBe(true);
|
|
expect(data.materials.length).toBeGreaterThan(0);
|
|
|
|
// Verify material structure
|
|
const material = data.materials[0];
|
|
expect(material).toHaveProperty('id');
|
|
expect(material).toHaveProperty('name');
|
|
expect(material).toHaveProperty('category');
|
|
expect(material).toHaveProperty('unit');
|
|
});
|
|
|
|
test('Employees API works correctly - Calendar Fix Verification', async ({ request }) => {
|
|
const response = await request.get(`${BASE_URL}/api/planning/employees`);
|
|
|
|
expect(response.status()).toBe(200);
|
|
|
|
const data = await response.json();
|
|
expect(data.success).toBe(true);
|
|
expect(data.employees).toBeDefined();
|
|
expect(Array.isArray(data.employees)).toBe(true);
|
|
|
|
// Critical fix verification: Should have active employees (not 0)
|
|
expect(data.count).toBeGreaterThan(0);
|
|
expect(data.employees.length).toBeGreaterThan(0);
|
|
expect(data.totalEmployees).toBeGreaterThan(0);
|
|
|
|
// Verify employee structure
|
|
if (data.employees.length > 0) {
|
|
const employee = data.employees[0];
|
|
expect(employee).toHaveProperty('id');
|
|
expect(employee).toHaveProperty('first_name');
|
|
expect(employee).toHaveProperty('last_name');
|
|
expect(employee).toHaveProperty('employee_type_title');
|
|
}
|
|
|
|
console.log(`✅ Employee Fix Verified: ${data.count} active employees out of ${data.totalEmployees} total`);
|
|
});
|
|
|
|
test('Planning Calendar API works correctly', async ({ request }) => {
|
|
const response = await request.get(`${BASE_URL}/api/planning/calendar`);
|
|
|
|
expect(response.status()).toBe(200);
|
|
|
|
const data = await response.json();
|
|
expect(data.success).toBe(true);
|
|
expect(data.data).toBeDefined();
|
|
expect(Array.isArray(data.data)).toBe(true);
|
|
|
|
// Should have calendar events
|
|
expect(data.data.length).toBeGreaterThan(0);
|
|
|
|
// Verify calendar event structure
|
|
const event = data.data[0];
|
|
expect(event).toHaveProperty('employee_id');
|
|
expect(event).toHaveProperty('start_time');
|
|
expect(event).toHaveProperty('first_name');
|
|
expect(event).toHaveProperty('last_name');
|
|
|
|
console.log(`✅ Calendar API Verified: ${data.data.length} calendar events loaded`);
|
|
});
|
|
|
|
test('Enhanced Roof Types API works correctly', async ({ request }) => {
|
|
const response = await request.get(`${BASE_URL}/api/enhanced/roof-types`);
|
|
|
|
expect(response.status()).toBe(200);
|
|
|
|
const data = await response.json();
|
|
expect(data.success).toBe(true);
|
|
expect(data.roofTypes).toBeDefined();
|
|
expect(Array.isArray(data.roofTypes)).toBe(true);
|
|
expect(data.roofTypes.length).toBeGreaterThan(0);
|
|
|
|
// Verify clean labels (no complexity suffix)
|
|
const roofType = data.roofTypes[0];
|
|
expect(roofType).toHaveProperty('value');
|
|
expect(roofType).toHaveProperty('label');
|
|
expect(roofType.label).not.toMatch(/\(kompleksitet/i); // No complexity suffix
|
|
|
|
console.log(`✅ Roof Types Verified: ${data.roofTypes.length} clean roof type labels`);
|
|
});
|
|
|
|
test('Health endpoint responds correctly', async ({ request }) => {
|
|
const response = await request.get(`${BASE_URL}/api/health`);
|
|
|
|
expect(response.status()).toBe(200);
|
|
|
|
const data = await response.json();
|
|
expect(data.status).toBe('ok');
|
|
expect(data.timestamp).toBeDefined();
|
|
});
|
|
|
|
test('Frontend UI Elements - Key Features Present', async ({ page }) => {
|
|
await page.goto(BASE_URL);
|
|
await page.waitForLoadState('networkidle');
|
|
|
|
// Wait for React app to fully load
|
|
await page.waitForTimeout(5000);
|
|
|
|
// Check for key UI elements (this may need adjustment based on actual UI)
|
|
const pageContent = await page.content();
|
|
|
|
// Should not show critical errors
|
|
expect(pageContent).not.toContain('Ingen materialer fundet');
|
|
expect(pageContent).not.toContain('Smart Package Management Service ikke initialiseret');
|
|
expect(pageContent).not.toContain('Failed to load categories');
|
|
|
|
console.log('✅ Frontend UI loaded without critical errors');
|
|
});
|
|
|
|
test('No 500/503 errors in recent requests', async ({ page }) => {
|
|
const responses = [];
|
|
|
|
page.on('response', response => {
|
|
responses.push({
|
|
url: response.url(),
|
|
status: response.status()
|
|
});
|
|
});
|
|
|
|
await page.goto(BASE_URL);
|
|
await page.waitForLoadState('networkidle');
|
|
await page.waitForTimeout(5000);
|
|
|
|
// Check for any 500/503 errors
|
|
const serverErrors = responses.filter(r => r.status >= 500);
|
|
|
|
if (serverErrors.length > 0) {
|
|
console.log('❌ Server errors found:', serverErrors);
|
|
}
|
|
|
|
expect(serverErrors).toHaveLength(0);
|
|
|
|
console.log(`✅ No server errors found in ${responses.length} requests`);
|
|
});
|
|
|
|
});
|
|
|
|
test.describe('API Performance Tests', () => {
|
|
|
|
test('All critical APIs respond within acceptable time', async ({ request }) => {
|
|
const endpoints = [
|
|
'/api/health',
|
|
'/api/smart-packages?limit=5',
|
|
'/api/smart-packages/categories',
|
|
'/api/materials?limit=5',
|
|
'/api/planning/employees',
|
|
'/api/enhanced/roof-types'
|
|
];
|
|
|
|
for (const endpoint of endpoints) {
|
|
const start = Date.now();
|
|
const response = await request.get(`${BASE_URL}${endpoint}`);
|
|
const duration = Date.now() - start;
|
|
|
|
expect(response.status()).toBe(200);
|
|
expect(duration).toBeLessThan(10000); // Should respond within 10 seconds
|
|
|
|
console.log(`✅ ${endpoint}: ${response.status()} in ${duration}ms`);
|
|
}
|
|
});
|
|
|
|
}); |