- 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
208 lines
4.9 KiB
JavaScript
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;
|