ai-engineering-project / static /test_citation_rendering.html
GitHub Action
Clean deployment without binary files
f884e6e
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Citation Rendering Test</title>
<style>
body {
font-family: Arial, sans-serif;
max-width: 800px;
margin: 20px auto;
padding: 20px;
background-color: #f5f5f5;
}
.test-case {
background: white;
padding: 20px;
margin-bottom: 20px;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
h2 {
color: #333;
margin-top: 0;
}
.result {
padding: 15px;
background: #f8f9fa;
border-left: 4px solid #007bff;
margin-top: 10px;
}
.inline-source-citation {
color: #667eea;
background: #eef2ff;
padding: 0.125rem 0.375rem;
border-radius: 4px;
cursor: pointer;
transition: all 0.2s;
font-weight: 500;
border: 1px solid #c7d2fe;
display: inline-block;
text-decoration: none;
margin: 0 0.125rem;
}
.inline-source-citation:hover {
background: #ddd6fe;
border-color: #a78bfa;
color: #7c3aed;
}
.error {
color: #dc3545;
background: #f8d7da;
padding: 10px;
border-radius: 4px;
margin-top: 10px;
}
.success {
color: #28a745;
background: #d4edda;
padding: 10px;
border-radius: 4px;
margin-top: 10px;
}
</style>
</head>
<body>
<h1>Source Citation Rendering Test</h1>
<div class="test-case">
<h2>Test 1: Placeholder Generation</h2>
<div id="test1-input">
Input: "Based on the PTO Policy [Source: pto_policy.md], you can take time off."
</div>
<div class="result" id="test1-result"></div>
</div>
<div class="test-case">
<h2>Test 2: Multiple Citations</h2>
<div id="test2-input">
Input: "According to [Source: pto_policy.md] and [Source: remote_work_policy.md], employees have flexibility."
</div>
<div class="result" id="test2-result"></div>
</div>
<div class="test-case">
<h2>Test 3: HTML Escaping</h2>
<div id="test3-input">
Input: "The policy [Source: &lt;script&gt;alert('xss')&lt;/script&gt;.md] is secure."
</div>
<div class="result" id="test3-result"></div>
</div>
<script>
// Simplified version of the citation functions for testing
class CitationRenderer {
constructor() {
this.citationPlaceholders = [];
}
escapeHtml(text) {
const div = document.createElement('div');
div.textContent = text;
return div.innerHTML;
}
prepareInlineCitations(text, sources) {
const citationPattern = /\[Source:\s*([^\]]+)\]/g;
const sourceMap = new Map();
sources.forEach(source => {
const filename = source.filename || source.document || source.title;
if (filename) {
sourceMap.set(filename.trim(), source.id || source.filename || source.document);
}
});
this.citationPlaceholders = [];
const processedText = text.replace(citationPattern, (match, filename) => {
const trimmedFilename = filename.trim();
const sourceId = sourceMap.get(trimmedFilename) || trimmedFilename;
const placeholderIndex = this.citationPlaceholders.length;
this.citationPlaceholders.push({
sourceId: sourceId,
sourceName: trimmedFilename
});
return `__CITATION_${placeholderIndex}__`;
});
return processedText;
}
replaceCitationPlaceholders(html) {
if (!this.citationPlaceholders || this.citationPlaceholders.length === 0) {
return html;
}
let result = html;
this.citationPlaceholders.forEach((citation, index) => {
const placeholder = `__CITATION_${index}__`;
const escapedSourceId = this.escapeHtml(citation.sourceId);
const escapedSourceName = this.escapeHtml(citation.sourceName);
const citationHtml = `<span class="inline-source-citation" role="button" tabindex="0" data-source-id="${escapedSourceId}" data-source-name="${escapedSourceName}" title="Click to view source: ${escapedSourceName}" aria-label="View source: ${escapedSourceName}">[Source: ${escapedSourceName}]</span>`;
result = result.replace(placeholder, citationHtml);
});
return result;
}
// Simplified markdown formatter (just escapes HTML)
formatMarkdown(text) {
return this.escapeHtml(text);
}
renderMessage(text, sources) {
const textWithPlaceholders = this.prepareInlineCitations(text, sources);
const escapedMarkdown = this.formatMarkdown(textWithPlaceholders);
return this.replaceCitationPlaceholders(escapedMarkdown);
}
}
// Run tests
const renderer = new CitationRenderer();
// Test 1
const sources1 = [
{ filename: 'pto_policy.md', id: 'pto_policy.md' }
];
const result1 = renderer.renderMessage(
"Based on the PTO Policy [Source: pto_policy.md], you can take time off.",
sources1
);
document.getElementById('test1-result').innerHTML = result1;
if (result1.includes('<span class="inline-source-citation"')) {
document.getElementById('test1-result').insertAdjacentHTML('beforeend',
'<div class="success">✓ Citation rendered correctly as HTML</div>');
} else {
document.getElementById('test1-result').insertAdjacentHTML('beforeend',
'<div class="error">✗ Citation not rendered as HTML</div>');
}
// Test 2
const sources2 = [
{ filename: 'pto_policy.md', id: 'pto_policy.md' },
{ filename: 'remote_work_policy.md', id: 'remote_work_policy.md' }
];
const result2 = renderer.renderMessage(
"According to [Source: pto_policy.md] and [Source: remote_work_policy.md], employees have flexibility.",
sources2
);
document.getElementById('test2-result').innerHTML = result2;
const citationCount = (result2.match(/inline-source-citation/g) || []).length;
if (citationCount === 2) {
document.getElementById('test2-result').insertAdjacentHTML('beforeend',
'<div class="success">✓ Multiple citations rendered correctly</div>');
} else {
document.getElementById('test2-result').insertAdjacentHTML('beforeend',
`<div class="error">✗ Expected 2 citations, found ${citationCount}</div>`);
}
// Test 3 - XSS prevention
const sources3 = [];
const result3 = renderer.renderMessage(
"The policy [Source: <script>alert('xss')</script>.md] is secure.",
sources3
);
document.getElementById('test3-result').innerHTML = result3;
if (!result3.includes('<script>') && result3.includes('&lt;script&gt;')) {
document.getElementById('test3-result').insertAdjacentHTML('beforeend',
'<div class="success">✓ HTML properly escaped, XSS prevented</div>');
} else {
document.getElementById('test3-result').insertAdjacentHTML('beforeend',
'<div class="error">✗ XSS vulnerability detected!</div>');
}
// Add click handlers to all citations
document.querySelectorAll('.inline-source-citation').forEach(citation => {
citation.addEventListener('click', function() {
alert('Source clicked: ' + this.dataset.sourceName);
});
});
</script>
</body>
</html>