test: add CitationDrawer tests for XSS prevention, group collapse, and tooltip clamping#200
test: add CitationDrawer tests for XSS prevention, group collapse, and tooltip clamping#200bensonwong wants to merge 9 commits intomainfrom
Conversation
…d tooltip clamping Add 24 new tests covering: - Proof image XSS prevention (SVG data URI, javascript: protocol, untrusted URLs) in both CitationDrawerItemComponent and SourceTooltip - Group collapse/expand state (toggle, independent per-group state, aria-expanded, chevron rotation, single-group no-header behavior) - Tooltip viewport clamping (centering transform, left/right overflow adjustment, resize listener cleanup) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
✅ Playwright Test ReportStatus: Tests passed 📊 Download Report & Snapshots (see Artifacts section) What's in the Visual SnapshotsThe gallery includes visual snapshots for:
Run ID: 21852911089 |
Code Review: PR #200Thank you for adding comprehensive test coverage for the CitationDrawer component. This PR addresses important testing gaps around security, accessibility, and edge cases. Here's my feedback: ✅ StrengthsSecurity Testing (Proof Image XSS Prevention)
Group Collapse/Expand Tests
Tooltip Viewport Clamping
|
The portal mock renders content at document level, not inside the RTL container. Use document.querySelectorAll instead of container.querySelectorAll to find group header buttons, matching how RTL's getByText/getByRole work. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Code Review for PR #200Thank you for adding comprehensive test coverage for the CitationDrawer! I've reviewed the code and have some important findings and suggestions. 🚨 Critical Issue: Tests Don't Match ImplementationThe group collapse/expand tests (lines 1209-1334) are testing functionality that doesn't exist in the current codebase:
These tests will fail because they're looking for Resolution Options:
✅ Excellent: XSS Prevention TestsThe security tests (lines 1119-1210) are comprehensive and valuable:
The implementation correctly uses:
Suggestion: Consider adding a test for mixed-case bypass attempts (e.g.,
|
| Category | Status | Notes |
|---|---|---|
| XSS Prevention Tests | ✅ Excellent | Comprehensive coverage |
| Group Collapse Tests | ❌ Will Fail | Testing non-existent functionality |
| Tooltip Clamping Tests | Timing issues, incomplete assertions | |
| Code Quality | ✅ Good | Clean, readable test code |
| Documentation | ✅ Good | Clear test descriptions |
🔧 Recommended Actions
Before merging:
- Fix or remove the group collapse tests - they will fail against the current implementation
- Verify all tests actually pass - run
bun test src/__tests__/CitationDrawer.test.tsxto confirm - Consider extracting
isValidProofImageSrcto a shared utility for better testability
Nice-to-haves:
- Add more specific assertions to viewport clamping tests
- Extract test helpers to avoid duplication
- Consider adding unit tests for
isValidProofImageSrcdirectly
Let me know if you'd like help addressing any of these issues!
Replace document.querySelectorAll with RTL's getAllByRole('button', { expanded })
to find group header buttons. This is more robust across different jsdom
implementations and matches how existing tests query elements.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Previous approaches using querySelectorAll and getAllByRole failed in CI's jsdom environment. Now uses RTL's getAllByText to find the source name text, then walks up the DOM to find the parent button with aria-expanded. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Pull Request Review: test: add CitationDrawer tests for XSS prevention, group collapse, and tooltip clampingOverall AssessmentStatus: ✅ Approved with minor suggestions This PR successfully addresses the leftover test coverage from PR #196 by adding comprehensive tests for three critical areas: XSS prevention, group collapse/expand functionality, and tooltip viewport clamping. The tests are well-structured, follow existing patterns, and provide valuable security and UX coverage. Strengths1. Security Testing (XSS Prevention) ⭐
2. Group Collapse/Expand Testing
3. Test Organization
4. Commit History
Code Quality ObservationsPositive Patterns
Minor Suggestions1. Test Duplication in XSS TestsThe XSS tests in CitationDrawerTrigger block and CitationDrawerItemComponent block test very similar scenarios. Consider adding a comment explaining why both are needed (testing different component paths), or extract shared test data to reduce duplication. 2. Mock Restoration in Viewport TestsThe viewport clamping tests modify global prototypes but could benefit from explicit cleanup in try-finally blocks to ensure cleanup even if assertions fail. 3. Magic NumbersConsider extracting magic numbers to named constants for clarity (VIEWPORT_WIDTH = 400, TOOLTIP_WIDTH = 180, etc.) Security AnalysisThe XSS prevention tests are critical and well-implemented. They validate the security boundary in isValidProofImageSrc function: ✅ Blocked Vectors:
✅ Allowed Sources:
This aligns with defense-in-depth principles and prevents common image-based XSS attacks. Test Coverage AnalysisNew Coverage:
This matches the PR description exactly. AccessibilityGood attention to accessibility:
RecommendationsHigh PriorityNone - the PR is ready to merge as-is. Nice to Have
Future Enhancements
ConclusionThis PR demonstrates excellent testing practices:
The iterative improvements shown in the commit history (fixing portal mock queries, switching to RTL patterns) show good problem-solving and CI/CD integration. Recommendation: Merge ✅ Great work addressing the test coverage gaps from #196! The XSS prevention tests are particularly valuable for maintaining security as the codebase evolves. |
The portal mock may not render content inside the RTL container in CI's jsdom. Switch from scoped getAllByText to screen.getAllByText which searches the entire document, and use screen.queryByText for all assertions in collapse/expand tests. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Code Review: PR #200Thank you for adding comprehensive test coverage for the CitationDrawer! I've reviewed the changes and identified several critical issues that need to be addressed. 🚨 Critical Issue: Tests Don't Match ImplementationThe tests are testing functionality that doesn't exist in the current codebase. Group Collapse/Expand Tests (Lines 221-362)The test suite includes extensive tests for group collapse/expand behavior:
However, the current
What This MeansThese 7 tests will fail because:
📝 Observations1. PR #196 Context MismatchPR #196's description mentions "collapsible section headers" with chevron and collapse behavior, and the diff from that PR shows Possible explanations:
2. Other Test Categories Look GoodThe XSS prevention tests (lines 1006-1217) are excellent:
The tooltip viewport clamping tests (lines 364-501) also look well-structured:
🔧 RecommendationsOption 1: Remove Collapse Tests (Quick Fix)If collapse functionality is not planned, remove the entire Option 2: Implement Collapse FunctionalityIf collapse is a desired feature, implement it based on the test expectations. Option 3: Verify Branch StateConfirm whether:
🎯 Additional ImprovementsTest Helper RobustnessThe
Test OrganizationThe XSS tests are split between two
Consider consolidating all XSS tests into a single ✅ What Works Well
SummaryBefore merging:
Please clarify the intended behavior and update accordingly. Happy to help with implementation if needed! |
Pull Request Review: CitationDrawer Test CoverageThis PR adds comprehensive test coverage for three critical areas of the CitationDrawer that were previously untested. The implementation is excellent with thorough edge case coverage and proper test structure. ✅ Strengths1. Excellent XSS Prevention Test CoverageThe proof image security tests are comprehensive and well-designed:
This is exemplary security test coverage. 🎯 2. Group Collapse/Expand State TestsThe state management tests cover all critical scenarios:
The 3. Tooltip Viewport Clamping TestsThe viewport clamping tests validate the
The 4. Code Quality
🐛 Issues Found1. Missing Test: CitationDrawerTrigger Viewport Clamping (Minor)Location: Lines 1382-1478 The tooltip viewport clamping tests only verify that the Current tests: // Tests only check that transform exists and contains 'translateX'
expect(tooltip.style.transform).toContain("translateX");Suggestion: Add assertions for the actual offset values: it("adjusts tooltip position when overflowing left edge", () => {
// ... setup code ...
const tooltip = trigger.querySelector("[data-testid='source-tooltip']") as HTMLElement;
// Should adjust by at least the overflow amount (20px left overflow + 8px margin)
expect(tooltip.style.transform).toMatch(/translateX\(calc\(-50% \+ \d+px\)\)/);
// Extract the pixel offset
const match = tooltip.style.transform.match(/\+ (\d+)px/);
expect(match).toBeTruthy();
const offset = Number.parseInt(match\![1], 10);
expect(offset).toBeGreaterThan(20); // Should push right by more than the 20px overflow
});Impact: Low - current tests still validate core functionality 2. Test Coverage Gap: Tooltip Clamping with Proof Image Loading (Low)Location: Lines 1382-1478 The tooltip clamping logic runs in Missing test scenario: it("re-clamps when proof image loads and changes tooltip dimensions", async () => {
const groups = [createGroup("TestSource")];
const { getByTestId } = render(<CitationDrawerTrigger citationGroups={groups} />);
const trigger = getByTestId("citation-drawer-trigger");
hoverFirstIcon(trigger);
const tooltip = trigger.querySelector("[data-testid='source-tooltip']") as HTMLElement;
const initialTransform = tooltip.style.transform;
// Simulate proof image load changing dimensions
const proofImg = tooltip.querySelector('img[alt="Verification proof"]');
if (proofImg) {
fireEvent.load(proofImg);
// Tooltip should recalculate position
// NOTE: This might fail because useLayoutEffect has empty deps\!
}
});Note: This test would likely expose a bug in the current implementation - the 3. Test Readability: Magic Numbers (Minor)Location: Lines 1407-1435 The mock return { left: -20, right: 160, top: 0, bottom: 50, width: 180, height: 50, x: -20, y: 0, toJSON: () => ({}) } as DOMRect;Suggestion: Add comments explaining the overflow scenario: // Tooltip is 180px wide, positioned -20px off left edge (20px overflow)
return {
left: -20, // 20px overflow on left
right: 160, // -20 + 180 = 160
width: 180, // Tooltip width
// ... rest
} as DOMRect;Impact: Very low - just improves maintainability 💡 Suggestions1. Consider Testing Error Boundaries (Optional Enhancement)If it("gracefully handles validation errors", () => {
const malformedItem = createItemWithProof("data:image/png;malformed");
// Should not throw
expect(() => {
render(<CitationDrawerItemComponent item={malformedItem} />);
}).not.toThrow();
});2. Test Group Collapse Animation Classes (Optional)The PR description mentions "chevron rotation" but the test only checks the it("animates chevron rotation on collapse/expand", () => {
// ... setup ...
const chevron = alphaHeader.querySelector("svg");
// Should have transition class for smooth animation
expect(chevron).toHaveClass("transition-transform");
expect(chevron).toHaveClass("rotate-90"); // Expanded
fireEvent.click(alphaHeader);
// Should remove rotation class (collapsed state)
expect(chevron).not.toHaveClass("rotate-90");
});3. Test Tooltip
|
| Area | Tests Added | Coverage |
|---|---|---|
| XSS Prevention | 13 tests | Excellent ✅ |
| Group Collapse/Expand | 7 tests | Excellent ✅ |
| Tooltip Viewport Clamping | 4 tests | Good ✅ |
| CitationDrawerTrigger Enhancements | 4 tests | Good ✅ |
| Total | 28 tests | Excellent ✅ |
✅ Recommendation
APPROVE
This PR provides excellent test coverage for critical security and UX features. The XSS prevention tests are particularly thorough and demonstrate strong security awareness. The group collapse/expand tests validate complex state management correctly. The viewport clamping tests cover the essential scenarios.
Blocking concerns: None
Optional improvements: See suggestions above (all low-priority)
This addresses the leftover PR review suggestions from #196 excellently. Great work! 🎉
Reviewed by: Claude Code (Sonnet 4.5)
Review Date: 2026-02-10
Find group headers by their unique citation count badge text (e.g. "2 citations") which only appears in SourceGroupHeader, not in citation items. This avoids the source name text ambiguity and is robust across jsdom implementations. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Code Review: PR #200Thank you for adding comprehensive test coverage for these critical security and UX features! Overall, this is excellent work. The tests are well-structured, thoroughly cover edge cases, and follow good testing practices. ✅ Strengths
🔍 Issues & Suggestions1. Missing Type Safety in Test Mocks (High Priority)The viewport clamping tests mock // Line ~1456
Element.prototype.getBoundingClientRect = function () {
if (this.getAttribute?.('data-testid') === 'source-tooltip') {
return { left: -20, right: 160, ... } as DOMRect;
}
return originalGetBCR.call(this);
};Issue: The returned object doesn't implement all DOMRect properties/methods. While Suggestion: Use a more complete mock: return {
left: -20,
right: 160,
top: 0,
bottom: 50,
width: 180,
height: 50,
x: -20,
y: 0,
toJSON: () => ({})
} as DOMRect;Or better yet, create a helper: function createMockDOMRect(overrides: Partial<DOMRect>): DOMRect {
return {
left: 0, right: 0, top: 0, bottom: 0,
width: 0, height: 0, x: 0, y: 0,
toJSON: () => ({}),
...overrides
} as DOMRect;
}2. Incomplete Viewport Clamping Assertions (Medium Priority)The viewport overflow tests verify that // Line ~1453 - Left overflow test
expect(tooltip.style.transform).toContain('translateX');
// Should verify the adjustment is positive (pushing right)Suggestion: Add specific assertions: // Left overflow: should have positive adjustment
const match = tooltip.style.transform.match(/translateX\(calc\(-50% \+ (-?\d+)px\)\)/);
expect(match).toBeTruthy();
const adjustment = Number.parseInt(match![1]);
expect(adjustment).toBeGreaterThan(0); // Pushed right
// Right overflow: should have negative adjustment
expect(adjustment).toBeLessThan(0); // Pushed left3. Test Cleanup Issues (Medium Priority)The viewport tests modify global state but cleanup could be more robust: // Line ~1448
Object.defineProperty(window, 'innerWidth', { value: 400, writable: true });
// ... test runs ...
Element.prototype.getBoundingClientRect = originalGetBCR; // Restored at endIssue: If a test throws, Suggestion: Use try/finally or afterEach: it('adjusts tooltip position when overflowing right edge', () => {
const originalGetBCR = Element.prototype.getBoundingClientRect;
const originalInnerWidth = window.innerWidth;
try {
Object.defineProperty(window, 'innerWidth', { value: 400, writable: true });
// ... test code ...
} finally {
Element.prototype.getBoundingClientRect = originalGetBCR;
Object.defineProperty(window, 'innerWidth', { value: originalInnerWidth, writable: true });
}
});4. Redundant Test Documentation (Low Priority)Some tests have verbose comments that restate what the test name already says: it('blocks SVG data URI proof images (XSS vector)', () => {
// ... test code ...
// No proof image should render ← Redundant
const proofImg = container.querySelector('img[alt="Verification proof"]');
expect(proofImg).not.toBeInTheDocument();
});The test name already communicates the intent. Comments are helpful for why something is done, not what. Suggestion: Remove redundant comments or make them explain the "why": // SVG data URIs can execute JavaScript via onload handlers and embedded scripts
const proofImg = container.querySelector('img[alt="Verification proof"]');
expect(proofImg).not.toBeInTheDocument();5. Test Data Duplication (Low Priority)Multiple tests create similar const createTestGroup = (overrides?: Partial<SourceCitationGroup>): SourceCitationGroup => ({
sourceName: 'TestSource',
sourceDomain: 'testsource.com',
citations: [{
citationKey: 'ts-0',
citation: { siteName: 'TestSource', title: 'Article 1' },
verification: { status: 'found' },
}],
additionalCount: 0,
...overrides,
});🛡️ SecurityExcellent security coverage. The XSS prevention tests cover all major attack vectors:
One additional test to consider: it('blocks data URI with incorrect MIME type claiming to be image', () => {
const item = createItemWithProof('data:text/html;base64,PHNjcmlwdD5hbGVydCgneHNzJyk8L3NjcmlwdD4=');
// Should block text/html even if it claims to be an image
});🎯 PerformanceNo performance concerns. Test execution should be fast since:
📊 Test CoverageBefore: 72 tests Coverage gaps filled:
📝 Minor Nits
✅ RecommendationsMust address (before merge):
Should address (nice to have):
Optional:
🎉 ConclusionThis PR significantly improves test coverage for critical security and UX features. The XSS prevention tests are particularly valuable. With the minor fixes above (especially #1 and #3), this will be ready to merge. Great work on the iterative CI fixes! The final solution using citation count badges is elegant and robust. Overall: ✅ Approve with minor changes |
Use tagName.toUpperCase() instead of comparing against "BUTTON" directly, since jsdom implementations may return lowercase tag names. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Code Review - PR #200Overall Assessment✅ Strong PR - This adds excellent test coverage for critical security features (XSS prevention), UI state management (group collapse), and responsive behavior (tooltip viewport clamping). The tests are well-structured, thorough, and follow existing patterns in the codebase. ✅ Strengths
🔍 Minor Suggestions1. Mock Cleanup in Viewport TestsThe viewport clamping tests modify describe("SourceTooltip viewport clamping", () => {
let originalGetBCR: typeof Element.prototype.getBoundingClientRect;
let originalInnerWidth: number;
beforeEach(() => {
originalGetBCR = Element.prototype.getBoundingClientRect;
originalInnerWidth = window.innerWidth;
});
afterEach(() => {
Element.prototype.getBoundingClientRect = originalGetBCR;
Object.defineProperty(window, "innerWidth", {
value: originalInnerWidth,
writable: true
});
cleanup();
});
// tests...
});Impact: Low - current approach works but this would be more defensive against test interference. 2. DOM Walking Null SafetyIn const findGroupHeaderByCount = (countText: string): HTMLElement | null => {
try {
const el = screen.getByText(countText);
let node: HTMLElement | null = el;
while (node) {
if (node.tagName.toUpperCase() === "BUTTON" && node.hasAttribute("aria-expanded")) {
return node;
}
node = node.parentElement;
}
} catch {
// Silently fail if text not found
}
return null;
};Current code is fine - this is just documenting the error handling strategy more explicitly. 3. Assertion Specificity in Viewport TestsThe viewport clamping tests verify it("adjusts tooltip position when overflowing left edge", () => {
// ... setup ...
const tooltip = trigger.querySelector("[data-testid='source-tooltip']") as HTMLElement;
const transform = tooltip.style.transform;
// Should have centering + positive offset to push right
expect(transform).toMatch(/translateX\(calc\(-50% \+ \d+px\)\)/);
// Or extract and verify offset is positive
const match = transform.match(/translateX\(calc\(-50% \+ (-?\d+)px\)\)/);
expect(match).toBeTruthy();
expect(Number(match\![1])).toBeGreaterThan(0);
});Current approach is acceptable - just verifying the property exists may be sufficient given the complexity of mocking layout. 📋 Additional Observations
Security Analysis ✅The XSS prevention tests verify critical attack vectors:
The implementation ( Performance Considerations ✅
Test Coverage Assessment ✅Previously Uncovered Areas Now Tested:
Remaining Gaps (not blockers for this PR):
Recommendation: ✅ APPROVEThis PR significantly improves test coverage for security-critical features and complex UI state management. The tests are well-written, thorough, and demonstrate careful debugging through CI environment differences. No blocking issues identified. Suggested follow-ups (optional, not blockers):
|
Code Review - PR #200Thank you for addressing the leftover test coverage from PR #196. This is a solid test suite addition with excellent coverage of security, UI behavior, and edge cases. Here's my detailed review: ✅ Strengths1. Security Testing (XSS Prevention)The XSS prevention tests are excellent and critical for security:
These tests directly validate the 2. Tooltip Viewport ClampingGreat testing of the
3. Test Quality
|
Remove DOM walking helper entirely. Click the citation count text (e.g. "2 citations") directly and rely on event bubbling to trigger the group header's onClick handler. Simplifies tests and eliminates jsdom compatibility issues with tagName/attribute queries. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
PR Review: Add CitationDrawer Tests for XSS Prevention, Group Collapse, and Tooltip ClampingThanks for adding these comprehensive tests! Overall, this is a solid test suite addition with good coverage of security concerns and UI behavior. Here's my detailed feedback: ✅ Strengths
|
…, and workflow Analyzed ~37 non-dependabot PRs (#200-#247) to identify recurring patterns in AI code review feedback, false positives, and coding issues. AGENTS.md additions: - Pre-submission checklist (build, test, size, exports, dedup) - Popover timing constants with "do not flag as race condition" guidance - CSS overflow rules for popovers (recurring in PRs #243, #244, #247) - SSR safety patterns - Testing rules and existing coverage catalog - PR description guidelines - Bundle size awareness CLAUDE.md additions: - Security utilities canonical locations (urlSafety, objectSafety, regexSafety, logSafety) - Security patterns section with concrete rules and code examples - Timing constants table in Interaction Behavior section - Type safety rules (discriminated unions, no unsafe casts, export verification) https://claude.ai/code/session_0157XtUZgvrxbD6diyJRXQAx
…klist (#249) * docs: add PR-derived agent instructions for security, timing, testing, and workflow Analyzed ~37 non-dependabot PRs (#200-#247) to identify recurring patterns in AI code review feedback, false positives, and coding issues. AGENTS.md additions: - Pre-submission checklist (build, test, size, exports, dedup) - Popover timing constants with "do not flag as race condition" guidance - CSS overflow rules for popovers (recurring in PRs #243, #244, #247) - SSR safety patterns - Testing rules and existing coverage catalog - PR description guidelines - Bundle size awareness CLAUDE.md additions: - Security utilities canonical locations (urlSafety, objectSafety, regexSafety, logSafety) - Security patterns section with concrete rules and code examples - Timing constants table in Interaction Behavior section - Type safety rules (discriminated unions, no unsafe casts, export verification) * docs: add lint/format fix as first step in pre-submission checklist Lint/format failures (Biome) are the most common CI failure. Add npm run check:fix as step 1 in the checklist and to the Commands quick-reference, with explicit guidance to run it before every commit.
Summary
Addresses the leftover PR review suggestion from #196 to add tests for three areas of the CitationDrawer that lacked coverage:
Proof image XSS prevention: Verifies that SVG data URIs (which can embed
<script>tags andonloadhandlers) are blocked byisValidProofImageSrcin bothCitationDrawerItemComponentandSourceTooltip. Also testsjavascript:protocol, untrusted HTTPS URLs, HTTP URLs, empty/whitespace strings, and confirms valid raster data URIs and trusted hosts are allowed.Tooltip viewport clamping: Tests the
useLayoutEffect-based clamping logic that adjusts tooltip position when it overflows the left or right viewport edge, verifies the default centering transform, and confirms theresizeevent listener is cleaned up on unmount.Changes
Test plan