CodeIssuesDiscussionsWikiPull RequestsProjectsCommitsActionsReleasesContributorsPulse● GatesSecuritySettingsDeploymentsPipelineInsightsAgents✨ Explain✨ Ask AI✨ Workspace✨ Spec✨ Tests▓ Debt Map✨ NL Search🏛 Archaeology
Blame · Line-by-line history
sse-client.test.ts
Each line is annotated with the commit that last touched it. Click any SHA to jump to that commit and see the surrounding change.
| febd4f0 | 1 | import { describe, it, expect } from "bun:test"; |
| b584e52 | 2 | import { |
| 3 | liveSubscribeScript, | |
| 4 | liveCommentBannerScript, | |
| 5 | } from "../lib/sse-client"; | |
| febd4f0 | 6 | |
| 7 | describe("liveSubscribeScript", () => { | |
| 8 | it("returns a string containing EventSource and the topic path", () => { | |
| 9 | const js = liveSubscribeScript({ | |
| 10 | topic: "repo:abc", | |
| 11 | targetElementId: "live-feed", | |
| 12 | }); | |
| 13 | ||
| 14 | expect(typeof js).toBe("string"); | |
| 15 | expect(js).toContain("EventSource"); | |
| 16 | // topic is JSON-encoded then concatenated with the /live-events/ prefix | |
| 17 | // at runtime, so both must appear in the emitted script. | |
| 18 | expect(js).toContain("/live-events/"); | |
| 19 | expect(js).toContain('"repo:abc"'); | |
| 20 | // targetElementId must also be JSON-escaped into the snippet. | |
| 21 | expect(js).toContain('"live-feed"'); | |
| 22 | }); | |
| 23 | ||
| 24 | it("JSON-escapes bad topic strings to prevent </script> injection", () => { | |
| 25 | const malicious = '</script><script>alert(1)</script>'; | |
| 26 | const js = liveSubscribeScript({ | |
| 27 | topic: malicious, | |
| 28 | targetElementId: "feed", | |
| 29 | }); | |
| 30 | ||
| 31 | // Raw closing-tag sequence MUST NOT appear anywhere in the output — | |
| 32 | // JSON.stringify escapes `<` when emitted for HTML, but we additionally | |
| 33 | // verify the literal bad sequence is absent. | |
| 34 | expect(js).not.toContain("</script>"); | |
| 35 | // Unescaped alert call (in the exact bad form) must not appear. | |
| 36 | expect(js).not.toContain("<script>alert(1)"); | |
| 37 | // The topic should still be represented (escaped) so the subscription | |
| 38 | // remains functional — at minimum the inner `alert(1)` literal is there | |
| 39 | // as an escaped JSON string, but the HTML-breakout is gone. | |
| 40 | expect(js).toContain("EventSource"); | |
| 41 | }); | |
| 42 | }); | |
| b584e52 | 43 | |
| 44 | describe("liveCommentBannerScript", () => { | |
| 45 | it("emits a self-invoking IIFE that opens an EventSource", () => { | |
| 46 | const js = liveCommentBannerScript({ | |
| 47 | topic: "repo:r1:issue:7", | |
| 48 | bannerElementId: "live-comment-banner", | |
| 49 | }); | |
| 50 | expect(typeof js).toBe("string"); | |
| 51 | expect(js).toContain("EventSource"); | |
| 52 | expect(js).toContain("/live-events/"); | |
| 53 | expect(js).toContain('"repo:r1:issue:7"'); | |
| 54 | expect(js).toContain('"live-comment-banner"'); | |
| 55 | // Counter increment + show contract. | |
| 56 | expect(js).toContain("n++"); | |
| 57 | expect(js).toContain("js-live-count"); | |
| 58 | expect(js).toContain("js-live-link"); | |
| 59 | }); | |
| 60 | ||
| 61 | it("escapes topic strings to prevent </script> injection", () => { | |
| 62 | const malicious = '</script><script>alert(1)</script>'; | |
| 63 | const js = liveCommentBannerScript({ | |
| 64 | topic: malicious, | |
| 65 | bannerElementId: "banner", | |
| 66 | }); | |
| 67 | expect(js).not.toContain("</script>"); | |
| 68 | expect(js).not.toContain("<script>alert(1)"); | |
| 69 | expect(js).toContain("EventSource"); | |
| 70 | }); | |
| 71 | ||
| 72 | it("uses the current location for the reload link", () => { | |
| 73 | const js = liveCommentBannerScript({ | |
| 74 | topic: "repo:r1:pr:9", | |
| 75 | bannerElementId: "banner", | |
| 76 | }); | |
| 77 | // Must reference window.location so a click reloads the same page. | |
| 78 | expect(js).toContain("window.location"); | |
| 79 | }); | |
| 80 | }); |