Files
tilbudgivern/backend/routes/visualTestReports.js
alexpolo1 8773d96e94 feat: Add AI-Powered Visual Testing Suite using Selenium and OpenAI Vision API
- Implemented VisualAITester class for automated UI/UX testing
- Configured environment variables for API keys and paths
- Developed methods for browser setup, login, screenshot capture, and image encoding
- Integrated OpenAI Vision API for visual quality analysis with detailed feedback
- Created tests for various application pages including homepage, project flow, geometry form, and more
- Generated comprehensive HTML report summarizing test results and AI analysis
- Added functionality to copy AI prompts for automated issue resolution
2025-11-14 20:27:19 +00:00

208 lines
4.9 KiB
JavaScript

/**
* Visual AI Test Reports API
* Serve AI-generated visual test reports
*/
const express = require('express');
const router = express.Router();
const path = require('path');
const fs = require('fs').promises;
const REPORTS_DIR = path.join(__dirname, '../../test-results/visual-ai');
/**
* GET /api/visual-reports
* List all available visual test reports
*/
router.get('/', async (req, res) => {
try {
// Ensure directory exists
await fs.mkdir(REPORTS_DIR, { recursive: true });
// Read all files in directory
const files = await fs.readdir(REPORTS_DIR);
// Filter for HTML reports and get file stats
const reports = [];
for (const file of files) {
if (file.endsWith('.html') && file.includes('visual_ai_report')) {
const filePath = path.join(REPORTS_DIR, file);
const stats = await fs.stat(filePath);
// Extract timestamp from filename
const match = file.match(/visual_ai_report_(\d{8}_\d{6})\.html/);
const timestamp = match ? match[1] : 'unknown';
reports.push({
filename: file,
timestamp: timestamp,
created: stats.mtime,
size: stats.size,
url: `/api/visual-reports/${file}`
});
}
}
// Sort by creation time (newest first)
reports.sort((a, b) => b.created - a.created);
res.json({
success: true,
count: reports.length,
reports: reports
});
} catch (error) {
console.error('Error listing visual reports:', error);
res.status(500).json({
success: false,
error: error.message
});
}
});
/**
* GET /api/visual-reports/latest
* Get the latest visual test report
*/
router.get('/latest', async (req, res) => {
try {
const files = await fs.readdir(REPORTS_DIR);
// Filter for HTML reports
const htmlReports = files.filter(f =>
f.endsWith('.html') && f.includes('visual_ai_report')
);
if (htmlReports.length === 0) {
return res.status(404).json({
success: false,
error: 'No reports found'
});
}
// Get stats for each file and find the newest
let latestReport = null;
let latestTime = 0;
for (const file of htmlReports) {
const filePath = path.join(REPORTS_DIR, file);
const stats = await fs.stat(filePath);
if (stats.mtime > latestTime) {
latestTime = stats.mtime;
latestReport = file;
}
}
if (!latestReport) {
return res.status(404).json({
success: false,
error: 'No reports found'
});
}
// Redirect to the latest report
res.redirect(`/api/visual-reports/${latestReport}`);
} catch (error) {
console.error('Error getting latest report:', error);
res.status(500).json({
success: false,
error: error.message
});
}
});
/**
* GET /api/visual-reports/:filename
* Serve a specific visual test report
*/
router.get('/:filename', async (req, res) => {
try {
const filename = req.params.filename;
// Security: prevent directory traversal
if (filename.includes('..') || filename.includes('/')) {
return res.status(400).json({
success: false,
error: 'Invalid filename'
});
}
const filePath = path.join(REPORTS_DIR, filename);
// Check if file exists
try {
await fs.access(filePath);
} catch {
return res.status(404).json({
success: false,
error: 'Report not found'
});
}
// Serve HTML file
if (filename.endsWith('.html')) {
res.setHeader('Content-Type', 'text/html; charset=utf-8');
const content = await fs.readFile(filePath, 'utf-8');
res.send(content);
}
// Serve screenshots
else if (filename.endsWith('.png')) {
res.setHeader('Content-Type', 'image/png');
const content = await fs.readFile(filePath);
res.send(content);
}
else {
res.status(400).json({
success: false,
error: 'Unsupported file type'
});
}
} catch (error) {
console.error('Error serving report:', error);
res.status(500).json({
success: false,
error: error.message
});
}
});
/**
* DELETE /api/visual-reports/:filename
* Delete a specific report (optional)
*/
router.delete('/:filename', async (req, res) => {
try {
const filename = req.params.filename;
// Security check
if (filename.includes('..') || filename.includes('/')) {
return res.status(400).json({
success: false,
error: 'Invalid filename'
});
}
const filePath = path.join(REPORTS_DIR, filename);
await fs.unlink(filePath);
res.json({
success: true,
message: 'Report deleted',
filename: filename
});
} catch (error) {
console.error('Error deleting report:', error);
res.status(500).json({
success: false,
error: error.message
});
}
});
module.exports = router;