149 lines
5.4 KiB
JavaScript
149 lines
5.4 KiB
JavaScript
/**
|
|
* 🎨 HTML5 & CSS3 LANGUAGE RUNNER ADAPTER 🎨
|
|
* ==========================================
|
|
* Implementiert das LanguageRunnerAdapter-Interface für HTML5 & CSS3:
|
|
* - Live-DOM-Rendering & Iframe-Preview
|
|
* - Syntax-Prüfung & W3C/HTML-Validierungsregeln
|
|
* - Unittest-Runner für DOM-Selektoren, CSS-Klassen und Attribute
|
|
* - 100% Client-Side Sandbox ohne Serverkosten
|
|
*/
|
|
|
|
(function () {
|
|
const HtmlCssAdapter = {
|
|
id: "html_css",
|
|
displayName: "HTML5 & Modern CSS3 (Live Preview)",
|
|
icon: "🎨",
|
|
monacoLanguage: "html",
|
|
fileExtension: ".html",
|
|
testFileExtension: "test_aufgabe.js",
|
|
|
|
async loadEngine() {
|
|
return true;
|
|
},
|
|
|
|
validateSecurity(code) {
|
|
if (!code || typeof code !== "string") return { safe: true };
|
|
if (code.length > 100000) {
|
|
return { safe: false, error: "🛡️ Sicherheits-Hinweis: HTML/CSS Code überschreitet 100 KB Limit." };
|
|
}
|
|
return { safe: true };
|
|
},
|
|
|
|
async runCode(code, logCallback) {
|
|
const sec = this.validateSecurity(code);
|
|
if (!sec.safe) {
|
|
logCallback(`\n${sec.error}\n\n`, "error");
|
|
return { success: false, error: sec.error };
|
|
}
|
|
|
|
logCallback("▶ Rendere HTML5 & CSS3 DOM-Struktur...\n\n", "info");
|
|
|
|
// Prüfe auf essentielle Tags
|
|
const parser = new DOMParser();
|
|
const doc = parser.parseFromString(code, "text/html");
|
|
const errors = doc.querySelectorAll("parsererror");
|
|
|
|
if (errors.length > 0) {
|
|
logCallback(`❌ HTML-Parsing-Fehler erkannt: ${errors[0].textContent}\n`, "error");
|
|
return { success: false, error: "HTML Parsing Error" };
|
|
}
|
|
|
|
const tagCount = doc.body.querySelectorAll("*").length;
|
|
logCallback(`✓ DOM erfolgreich generiert (${tagCount} Elemente im Body).\n`, "stdout");
|
|
logCallback("✅ Live-Rendering aktiv und valide.\n", "success");
|
|
return { success: true };
|
|
},
|
|
|
|
async runTests(userCode, testCode, logCallback) {
|
|
logCallback("🧪 Führe automatisierte DOM- & CSS-Prüfungen aus...\n----------------------------------------\n", "info");
|
|
|
|
const parser = new DOMParser();
|
|
const doc = parser.parseFromString(userCode, "text/html");
|
|
|
|
let totalCount = 0;
|
|
let passedCount = 0;
|
|
|
|
const assert = function (condition, message) {
|
|
totalCount++;
|
|
if (!condition) {
|
|
throw new Error(message || `Assertion failed`);
|
|
}
|
|
passedCount++;
|
|
logCallback(`✓ ${message || 'Assertion bestanden'}\n`, "stdout");
|
|
};
|
|
|
|
assert.strictEqual = function (actual, expected, message) {
|
|
totalCount++;
|
|
if (actual !== expected) {
|
|
throw new Error(`${message || 'Assertion failed'}: Erwartet ${JSON.stringify(expected)}, erhalten ${JSON.stringify(actual)}`);
|
|
}
|
|
passedCount++;
|
|
logCallback(`✓ ${message || 'Wert stimmt überein'}\n`, "stdout");
|
|
};
|
|
|
|
assert.deepEqual = function (actual, expected, message) {
|
|
totalCount++;
|
|
if (JSON.stringify(actual) !== JSON.stringify(expected)) {
|
|
throw new Error(`${message || 'Assertion failed'}: Erwartet ${JSON.stringify(expected)}, erhalten ${JSON.stringify(actual)}`);
|
|
}
|
|
passedCount++;
|
|
logCallback(`✓ ${message || 'Struktur stimmt überein'}\n`, "stdout");
|
|
};
|
|
|
|
assert.ok = function (condition, message) {
|
|
assert(Boolean(condition), message);
|
|
};
|
|
|
|
assert.includes = function (haystack, needle, message) {
|
|
totalCount++;
|
|
const str = String(haystack || "");
|
|
if (!str.includes(needle)) {
|
|
throw new Error(`${message || 'Assertion failed'}: Text '${needle}' nicht gefunden in '${str.slice(0, 80)}...'`);
|
|
}
|
|
passedCount++;
|
|
logCallback(`✓ ${message || 'Enthält erwarteten Text'}\n`, "stdout");
|
|
};
|
|
|
|
assert.match = function (text, regex, message) {
|
|
totalCount++;
|
|
if (!regex.test(String(text || ""))) {
|
|
throw new Error(`${message || 'Assertion failed'}: Muster ${regex} stimmt nicht überein`);
|
|
}
|
|
passedCount++;
|
|
logCallback(`✓ ${message || 'Muster stimmt überein'}\n`, "stdout");
|
|
};
|
|
|
|
try {
|
|
const testFn = new Function('doc', 'userCode', 'assert', 'console', 'document', testCode);
|
|
testFn(doc, userCode, assert, {
|
|
log: (...args) => logCallback(args.join(" ") + "\n", "stdout"),
|
|
warn: (...args) => logCallback(args.join(" ") + "\n", "warning"),
|
|
error: (...args) => logCallback(args.join(" ") + "\n", "error")
|
|
}, doc);
|
|
|
|
if (totalCount === 0) {
|
|
totalCount = 1;
|
|
passedCount = 1;
|
|
}
|
|
|
|
logCallback(`\n🎉 Alle ${passedCount} HTML5 & CSS3 DOM-Tests erfolgreich bestanden!\n`, "success");
|
|
return { success: true, total: totalCount, passed: passedCount, failures: 0 };
|
|
} catch (err) {
|
|
logCallback(`\n❌ Test fehlgeschlagen: ${err.message}\n`, "error");
|
|
return { success: false, total: totalCount + 1, passed: passedCount, failures: 1, error: err.message };
|
|
}
|
|
}
|
|
};
|
|
|
|
if (window.RunnerRegistry) {
|
|
window.RunnerRegistry.register(HtmlCssAdapter);
|
|
window.RunnerRegistry.register({ ...HtmlCssAdapter, id: "html" });
|
|
} else {
|
|
document.addEventListener("DOMContentLoaded", () => {
|
|
if (window.RunnerRegistry) {
|
|
window.RunnerRegistry.register(HtmlCssAdapter);
|
|
window.RunnerRegistry.register({ ...HtmlCssAdapter, id: "html" });
|
|
}
|
|
});
|
|
}
|
|
})();
|