Add simplified DOM dump generation and improve linter comment handling

This commit is contained in:
seb
2025-07-17 06:00:56 +02:00
parent a492223e45
commit f44b66b152
5 changed files with 271 additions and 8 deletions

View File

@@ -856,6 +856,10 @@ class TestExecutor {
const beautifiedHtml = this.beautifyHtml(html);
await fs.writeFile(path.join(dumpDir, 'dom.html'), beautifiedHtml);
// Create simplified DOM dump for test development
const simplifiedDom = await this.createSimplifiedDom();
await fs.writeFile(path.join(dumpDir, 'simplified-dom.html'), simplifiedDom);
// Create screenshot with viewport lock (if enabled)
if (this.enableScreenshots) {
if (!this.headless) {
@@ -891,6 +895,136 @@ class TestExecutor {
}
}
async createSimplifiedDom() {
// Extract simplified DOM structure with only essential elements and attributes
const simplifiedStructure = await this.page.evaluate(() => {
// Elements we want to keep
const keepElements = ['button', 'input', 'textarea', 'span', 'a'];
// Attributes we want to keep
const keepAttributes = ['id', 'class', 'name', 'value', 'type'];
function processElement(element) {
const tagName = element.tagName.toLowerCase();
// Only process elements we want to keep
if (!keepElements.includes(tagName)) {
return null;
}
// Create simplified element representation
const simplified = {
tag: tagName,
attributes: {},
text: '',
children: []
};
// Extract relevant attributes
keepAttributes.forEach(attr => {
if (element.hasAttribute(attr)) {
const value = element.getAttribute(attr);
if (value && value.trim()) {
simplified.attributes[attr] = value.trim();
}
}
});
// Get all text content from this element and its descendants
// This captures text even when it's nested in elements we don't keep
const allTextContent = element.textContent.trim();
if (allTextContent) {
simplified.text = allTextContent;
}
// Process children recursively
Array.from(element.children).forEach(child => {
const processedChild = processElement(child);
if (processedChild) {
simplified.children.push(processedChild);
}
});
return simplified;
}
function processContainer(container) {
const results = [];
Array.from(container.children).forEach(child => {
const processed = processElement(child);
if (processed) {
results.push(processed);
} else {
// If the element itself isn't kept, still check its children
results.push(...processContainer(child));
}
});
return results;
}
// Start from body and extract all relevant elements
return processContainer(document.body);
});
// Convert to readable HTML format
function renderElement(element, indent = 0) {
const spaces = ' '.repeat(indent);
let html = `${spaces}<${element.tag}`;
// Add attributes
Object.entries(element.attributes).forEach(([key, value]) => {
html += ` ${key}="${value}"`;
});
if (element.children.length === 0 && !element.text) {
html += ' />';
return html;
}
html += '>';
// Add text content
if (element.text) {
html += element.text;
}
// Add children
if (element.children.length > 0) {
if (element.text) html += '\n';
element.children.forEach(child => {
if (element.text) html += '\n';
html += renderElement(child, indent + 1);
});
if (element.text) html += '\n' + spaces;
}
html += `</${element.tag}>`;
return html;
}
// Generate HTML header and structure
let html = '<!DOCTYPE html>\n<html>\n<head>\n <title>Simplified DOM - Test Development Helper</title>\n <style>\n';
html += ' body { font-family: Arial, sans-serif; margin: 20px; }\n';
html += ' .info { background: #f0f0f0; padding: 10px; border-radius: 5px; margin-bottom: 20px; }\n';
html += ' pre { background: #f8f8f8; padding: 15px; border-radius: 5px; overflow-x: auto; }\n';
html += ' </style>\n</head>\n<body>\n';
html += ' <pre>\n';
// Add the simplified structure
if (simplifiedStructure.length === 0) {
html += '<!-- No interactive elements found -->\n';
} else {
simplifiedStructure.forEach(element => {
html += renderElement(element) + '\n';
});
}
html += ' </pre>\n</body>\n</html>';
return html;
}
beautifyHtml(html) {
let beautified = html;

View File

@@ -120,16 +120,13 @@ class TestLinter {
this.info = [];
this.filename = filename;
// Remove multi-line comments first (like the actual parser does)
const contentWithoutMultiLineComments = this.removeMultiLineComments(content);
// Parse content into lines with line numbers
const lines = contentWithoutMultiLineComments.split('\n');
const cleanedLines = this.preprocessLines(lines);
// Parse content into lines with original line numbers preserved
const originalLines = content.split('\n');
const cleanedLines = this.preprocessLinesWithComments(originalLines);
// Run all rules
for (const rule of this.rules) {
rule(cleanedLines, lines);
rule(cleanedLines, originalLines);
}
return {
@@ -141,6 +138,66 @@ class TestLinter {
};
}
preprocessLinesWithComments(lines) {
const result = [];
let inMultiLineComment = false;
let commentDepth = 0;
for (let i = 0; i < lines.length; i++) {
const line = lines[i];
let cleanLine = line;
// Handle multi-line comments line by line
let lineInComment = inMultiLineComment;
// Check for multi-line comment start/end on this line
let tempLine = cleanLine;
let tempInComment = inMultiLineComment;
let tempDepth = commentDepth;
// Process the line character by character to handle multi-line comments
let processedLine = '';
for (let j = 0; j < tempLine.length; j++) {
const char = tempLine[j];
const nextChar = j < tempLine.length - 1 ? tempLine[j + 1] : '';
if (!tempInComment && char === '/' && nextChar === '*') {
tempInComment = true;
tempDepth = 1;
j++; // Skip the '*'
} else if (tempInComment && char === '/' && nextChar === '*') {
tempDepth++;
j++; // Skip the '*'
} else if (tempInComment && char === '*' && nextChar === '/') {
tempDepth--;
if (tempDepth === 0) {
tempInComment = false;
}
j++; // Skip the '/'
} else if (!tempInComment) {
processedLine += char;
}
}
// Update state for next line
inMultiLineComment = tempInComment;
commentDepth = tempDepth;
cleanLine = processedLine;
// Remove single line comments (but not inside quotes)
cleanLine = this.removeLineComments(cleanLine);
result.push({
original: line,
cleaned: cleanLine.trim(),
lineNumber: i + 1
});
}
return result.filter(line => line.cleaned.length > 0);
}
preprocessLines(lines) {
const result = [];