/** * Tests for followup.js — FollowUpResearch.getResearchIdFromPage. * * Extracts the parent research ID from one of four fallback sources: * 1. URL path segment (/results/) * 2. URL query param (?research_id=) * 3. DOM data-research-id attribute * 4. window.currentResearchId * * Each test isolates exactly one source so the precedence order doesn't * have to be re-derived from the test setup. */ let FollowUpResearch; beforeAll(async () => { // followup.js auto-constructs an instance and binds a DOMContentLoaded // listener that fetches /static/templates/followup_modal.html. The // DOMContentLoaded event has already fired in happy-dom by the time // import settles, so the listener never runs — but stub fetch // defensively in case ordering changes. globalThis.fetch = vi.fn(() => Promise.resolve({ ok: false, status: 404, text: () => Promise.resolve('') }) ); await import('@js/followup.js'); FollowUpResearch = window.FollowUpResearch; }); function setLocation(pathname, search = '') { Object.defineProperty(window, 'location', { configurable: true, writable: true, value: { pathname, search, hash: '', host: 'localhost', protocol: 'http:' }, }); } describe('FollowUpResearch.getResearchIdFromPage', () => { afterEach(() => { document.body.innerHTML = ''; delete window.currentResearchId; }); it('extracts the id from /results/ in the URL path', () => { setLocation('/results/abc-123-def'); const fr = new FollowUpResearch(); expect(fr.getResearchIdFromPage()).toBe('abc-123-def'); }); it('falls through to the query string when the path does not match', () => { setLocation('/somewhere-else', '?research_id=xyz-789'); const fr = new FollowUpResearch(); expect(fr.getResearchIdFromPage()).toBe('xyz-789'); }); it('falls through to a [data-research-id] DOM attribute', () => { setLocation('/somewhere-else', ''); const el = document.createElement('div'); el.dataset.researchId = 'data-id-456'; document.body.appendChild(el); const fr = new FollowUpResearch(); expect(fr.getResearchIdFromPage()).toBe('data-id-456'); }); it('falls through to window.currentResearchId as the last resort', () => { setLocation('/somewhere-else', ''); window.currentResearchId = 'window-id-999'; const fr = new FollowUpResearch(); expect(fr.getResearchIdFromPage()).toBe('window-id-999'); }); it('returns null when none of the four sources have a value', () => { setLocation('/somewhere-else', ''); // No DOM element, no window.currentResearchId. const fr = new FollowUpResearch(); expect(fr.getResearchIdFromPage()).toBeNull(); }); it('prefers the URL path over the query string (precedence smoke test)', () => { setLocation('/results/from-path', '?research_id=from-query'); const fr = new FollowUpResearch(); expect(fr.getResearchIdFromPage()).toBe('from-path'); }); });