@debriefer/sources 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (194) hide show
  1. package/README.md +59 -0
  2. package/dist/__tests__/archives/chronicling-america.test.d.ts +8 -0
  3. package/dist/__tests__/archives/chronicling-america.test.d.ts.map +1 -0
  4. package/dist/__tests__/archives/chronicling-america.test.js +151 -0
  5. package/dist/__tests__/archives/chronicling-america.test.js.map +1 -0
  6. package/dist/__tests__/archives/europeana.test.d.ts +8 -0
  7. package/dist/__tests__/archives/europeana.test.d.ts.map +1 -0
  8. package/dist/__tests__/archives/europeana.test.js +200 -0
  9. package/dist/__tests__/archives/europeana.test.js.map +1 -0
  10. package/dist/__tests__/archives/internet-archive.test.d.ts +8 -0
  11. package/dist/__tests__/archives/internet-archive.test.d.ts.map +1 -0
  12. package/dist/__tests__/archives/internet-archive.test.js +189 -0
  13. package/dist/__tests__/archives/internet-archive.test.js.map +1 -0
  14. package/dist/__tests__/archives/trove.test.d.ts +8 -0
  15. package/dist/__tests__/archives/trove.test.d.ts.map +1 -0
  16. package/dist/__tests__/archives/trove.test.js +202 -0
  17. package/dist/__tests__/archives/trove.test.js.map +1 -0
  18. package/dist/__tests__/books/google-books.test.d.ts +8 -0
  19. package/dist/__tests__/books/google-books.test.d.ts.map +1 -0
  20. package/dist/__tests__/books/google-books.test.js +221 -0
  21. package/dist/__tests__/books/google-books.test.js.map +1 -0
  22. package/dist/__tests__/books/open-library.test.d.ts +8 -0
  23. package/dist/__tests__/books/open-library.test.d.ts.map +1 -0
  24. package/dist/__tests__/books/open-library.test.js +159 -0
  25. package/dist/__tests__/books/open-library.test.js.map +1 -0
  26. package/dist/__tests__/news/guardian.test.d.ts +9 -0
  27. package/dist/__tests__/news/guardian.test.d.ts.map +1 -0
  28. package/dist/__tests__/news/guardian.test.js +224 -0
  29. package/dist/__tests__/news/guardian.test.js.map +1 -0
  30. package/dist/__tests__/news/nytimes.test.d.ts +9 -0
  31. package/dist/__tests__/news/nytimes.test.d.ts.map +1 -0
  32. package/dist/__tests__/news/nytimes.test.js +271 -0
  33. package/dist/__tests__/news/nytimes.test.js.map +1 -0
  34. package/dist/__tests__/news/site-search-source.test.d.ts +9 -0
  35. package/dist/__tests__/news/site-search-source.test.d.ts.map +1 -0
  36. package/dist/__tests__/news/site-search-source.test.js +342 -0
  37. package/dist/__tests__/news/site-search-source.test.js.map +1 -0
  38. package/dist/__tests__/obituary/find-a-grave.test.d.ts +8 -0
  39. package/dist/__tests__/obituary/find-a-grave.test.d.ts.map +1 -0
  40. package/dist/__tests__/obituary/find-a-grave.test.js +238 -0
  41. package/dist/__tests__/obituary/find-a-grave.test.js.map +1 -0
  42. package/dist/__tests__/shared/duckduckgo-search.test.d.ts +9 -0
  43. package/dist/__tests__/shared/duckduckgo-search.test.d.ts.map +1 -0
  44. package/dist/__tests__/shared/duckduckgo-search.test.js +218 -0
  45. package/dist/__tests__/shared/duckduckgo-search.test.js.map +1 -0
  46. package/dist/__tests__/shared/fetch-page.test.d.ts +9 -0
  47. package/dist/__tests__/shared/fetch-page.test.d.ts.map +1 -0
  48. package/dist/__tests__/shared/fetch-page.test.js +281 -0
  49. package/dist/__tests__/shared/fetch-page.test.js.map +1 -0
  50. package/dist/__tests__/shared/html-utils.test.d.ts +2 -0
  51. package/dist/__tests__/shared/html-utils.test.d.ts.map +1 -0
  52. package/dist/__tests__/shared/html-utils.test.js +169 -0
  53. package/dist/__tests__/shared/html-utils.test.js.map +1 -0
  54. package/dist/__tests__/shared/readability-extract.test.d.ts +2 -0
  55. package/dist/__tests__/shared/readability-extract.test.d.ts.map +1 -0
  56. package/dist/__tests__/shared/readability-extract.test.js +107 -0
  57. package/dist/__tests__/shared/readability-extract.test.js.map +1 -0
  58. package/dist/__tests__/shared/sanitize-text.test.d.ts +2 -0
  59. package/dist/__tests__/shared/sanitize-text.test.d.ts.map +1 -0
  60. package/dist/__tests__/shared/sanitize-text.test.js +77 -0
  61. package/dist/__tests__/shared/sanitize-text.test.js.map +1 -0
  62. package/dist/__tests__/shared/search-utils.test.d.ts +2 -0
  63. package/dist/__tests__/shared/search-utils.test.d.ts.map +1 -0
  64. package/dist/__tests__/shared/search-utils.test.js +26 -0
  65. package/dist/__tests__/shared/search-utils.test.js.map +1 -0
  66. package/dist/__tests__/structured/wikidata.test.d.ts +9 -0
  67. package/dist/__tests__/structured/wikidata.test.d.ts.map +1 -0
  68. package/dist/__tests__/structured/wikidata.test.js +509 -0
  69. package/dist/__tests__/structured/wikidata.test.js.map +1 -0
  70. package/dist/__tests__/structured/wikipedia.test.d.ts +9 -0
  71. package/dist/__tests__/structured/wikipedia.test.d.ts.map +1 -0
  72. package/dist/__tests__/structured/wikipedia.test.js +643 -0
  73. package/dist/__tests__/structured/wikipedia.test.js.map +1 -0
  74. package/dist/__tests__/web-search/base.test.d.ts +9 -0
  75. package/dist/__tests__/web-search/base.test.d.ts.map +1 -0
  76. package/dist/__tests__/web-search/base.test.js +622 -0
  77. package/dist/__tests__/web-search/base.test.js.map +1 -0
  78. package/dist/__tests__/web-search/bing.test.d.ts +10 -0
  79. package/dist/__tests__/web-search/bing.test.d.ts.map +1 -0
  80. package/dist/__tests__/web-search/bing.test.js +277 -0
  81. package/dist/__tests__/web-search/bing.test.js.map +1 -0
  82. package/dist/__tests__/web-search/brave.test.d.ts +10 -0
  83. package/dist/__tests__/web-search/brave.test.d.ts.map +1 -0
  84. package/dist/__tests__/web-search/brave.test.js +264 -0
  85. package/dist/__tests__/web-search/brave.test.js.map +1 -0
  86. package/dist/__tests__/web-search/duckduckgo.test.d.ts +10 -0
  87. package/dist/__tests__/web-search/duckduckgo.test.d.ts.map +1 -0
  88. package/dist/__tests__/web-search/duckduckgo.test.js +107 -0
  89. package/dist/__tests__/web-search/duckduckgo.test.js.map +1 -0
  90. package/dist/__tests__/web-search/google.test.d.ts +9 -0
  91. package/dist/__tests__/web-search/google.test.d.ts.map +1 -0
  92. package/dist/__tests__/web-search/google.test.js +189 -0
  93. package/dist/__tests__/web-search/google.test.js.map +1 -0
  94. package/dist/archives/chronicling-america.d.ts +33 -0
  95. package/dist/archives/chronicling-america.d.ts.map +1 -0
  96. package/dist/archives/chronicling-america.js +85 -0
  97. package/dist/archives/chronicling-america.js.map +1 -0
  98. package/dist/archives/europeana.d.ts +37 -0
  99. package/dist/archives/europeana.d.ts.map +1 -0
  100. package/dist/archives/europeana.js +92 -0
  101. package/dist/archives/europeana.js.map +1 -0
  102. package/dist/archives/internet-archive.d.ts +32 -0
  103. package/dist/archives/internet-archive.d.ts.map +1 -0
  104. package/dist/archives/internet-archive.js +90 -0
  105. package/dist/archives/internet-archive.js.map +1 -0
  106. package/dist/archives/trove.d.ts +37 -0
  107. package/dist/archives/trove.d.ts.map +1 -0
  108. package/dist/archives/trove.js +97 -0
  109. package/dist/archives/trove.js.map +1 -0
  110. package/dist/books/google-books.d.ts +48 -0
  111. package/dist/books/google-books.d.ts.map +1 -0
  112. package/dist/books/google-books.js +111 -0
  113. package/dist/books/google-books.js.map +1 -0
  114. package/dist/books/open-library.d.ts +44 -0
  115. package/dist/books/open-library.d.ts.map +1 -0
  116. package/dist/books/open-library.js +103 -0
  117. package/dist/books/open-library.js.map +1 -0
  118. package/dist/index.d.ts +45 -0
  119. package/dist/index.d.ts.map +1 -0
  120. package/dist/index.js +35 -0
  121. package/dist/index.js.map +1 -0
  122. package/dist/news/guardian.d.ts +51 -0
  123. package/dist/news/guardian.d.ts.map +1 -0
  124. package/dist/news/guardian.js +131 -0
  125. package/dist/news/guardian.js.map +1 -0
  126. package/dist/news/nytimes.d.ts +27 -0
  127. package/dist/news/nytimes.d.ts.map +1 -0
  128. package/dist/news/nytimes.js +104 -0
  129. package/dist/news/nytimes.js.map +1 -0
  130. package/dist/news/site-search-source.d.ts +89 -0
  131. package/dist/news/site-search-source.d.ts.map +1 -0
  132. package/dist/news/site-search-source.js +182 -0
  133. package/dist/news/site-search-source.js.map +1 -0
  134. package/dist/news/sources.d.ts +52 -0
  135. package/dist/news/sources.d.ts.map +1 -0
  136. package/dist/news/sources.js +276 -0
  137. package/dist/news/sources.js.map +1 -0
  138. package/dist/obituary/find-a-grave.d.ts +43 -0
  139. package/dist/obituary/find-a-grave.d.ts.map +1 -0
  140. package/dist/obituary/find-a-grave.js +173 -0
  141. package/dist/obituary/find-a-grave.js.map +1 -0
  142. package/dist/shared/duckduckgo-search.d.ts +86 -0
  143. package/dist/shared/duckduckgo-search.d.ts.map +1 -0
  144. package/dist/shared/duckduckgo-search.js +218 -0
  145. package/dist/shared/duckduckgo-search.js.map +1 -0
  146. package/dist/shared/fetch-page.d.ts +50 -0
  147. package/dist/shared/fetch-page.d.ts.map +1 -0
  148. package/dist/shared/fetch-page.js +212 -0
  149. package/dist/shared/fetch-page.js.map +1 -0
  150. package/dist/shared/html-utils.d.ts +99 -0
  151. package/dist/shared/html-utils.d.ts.map +1 -0
  152. package/dist/shared/html-utils.js +246 -0
  153. package/dist/shared/html-utils.js.map +1 -0
  154. package/dist/shared/readability-extract.d.ts +33 -0
  155. package/dist/shared/readability-extract.d.ts.map +1 -0
  156. package/dist/shared/readability-extract.js +45 -0
  157. package/dist/shared/readability-extract.js.map +1 -0
  158. package/dist/shared/sanitize-text.d.ts +24 -0
  159. package/dist/shared/sanitize-text.d.ts.map +1 -0
  160. package/dist/shared/sanitize-text.js +49 -0
  161. package/dist/shared/sanitize-text.js.map +1 -0
  162. package/dist/shared/search-utils.d.ts +18 -0
  163. package/dist/shared/search-utils.d.ts.map +1 -0
  164. package/dist/shared/search-utils.js +20 -0
  165. package/dist/shared/search-utils.js.map +1 -0
  166. package/dist/structured/wikidata.d.ts +128 -0
  167. package/dist/structured/wikidata.d.ts.map +1 -0
  168. package/dist/structured/wikidata.js +361 -0
  169. package/dist/structured/wikidata.js.map +1 -0
  170. package/dist/structured/wikipedia.d.ts +184 -0
  171. package/dist/structured/wikipedia.d.ts.map +1 -0
  172. package/dist/structured/wikipedia.js +275 -0
  173. package/dist/structured/wikipedia.js.map +1 -0
  174. package/dist/web-search/base.d.ts +128 -0
  175. package/dist/web-search/base.d.ts.map +1 -0
  176. package/dist/web-search/base.js +251 -0
  177. package/dist/web-search/base.js.map +1 -0
  178. package/dist/web-search/bing.d.ts +21 -0
  179. package/dist/web-search/bing.d.ts.map +1 -0
  180. package/dist/web-search/bing.js +53 -0
  181. package/dist/web-search/bing.js.map +1 -0
  182. package/dist/web-search/brave.d.ts +21 -0
  183. package/dist/web-search/brave.d.ts.map +1 -0
  184. package/dist/web-search/brave.js +56 -0
  185. package/dist/web-search/brave.js.map +1 -0
  186. package/dist/web-search/duckduckgo.d.ts +15 -0
  187. package/dist/web-search/duckduckgo.d.ts.map +1 -0
  188. package/dist/web-search/duckduckgo.js +21 -0
  189. package/dist/web-search/duckduckgo.js.map +1 -0
  190. package/dist/web-search/google.d.ts +24 -0
  191. package/dist/web-search/google.d.ts.map +1 -0
  192. package/dist/web-search/google.js +48 -0
  193. package/dist/web-search/google.js.map +1 -0
  194. package/package.json +58 -0
@@ -0,0 +1,218 @@
1
+ /**
2
+ * Tests for the DuckDuckGo HTML search utility.
3
+ *
4
+ * Mocks the global `fetch` function to avoid real HTTP calls.
5
+ * Tests CAPTCHA detection, URL cleaning, HTML result extraction,
6
+ * domain filtering, and the full search function.
7
+ */
8
+ import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
9
+ import { isDuckDuckGoCaptcha, cleanDuckDuckGoUrl, extractUrlsFromDuckDuckGoHtml, searchDuckDuckGo, } from "../../shared/duckduckgo-search.js";
10
+ // ============================================================================
11
+ // Mocks
12
+ // ============================================================================
13
+ const mockFetch = vi.fn();
14
+ beforeEach(() => {
15
+ mockFetch.mockReset();
16
+ vi.stubGlobal("fetch", mockFetch);
17
+ });
18
+ afterEach(() => {
19
+ vi.restoreAllMocks();
20
+ });
21
+ // ============================================================================
22
+ // Test Helpers
23
+ // ============================================================================
24
+ function makeOkResponse(body) {
25
+ return {
26
+ ok: true,
27
+ status: 200,
28
+ statusText: "OK",
29
+ text: async () => body,
30
+ headers: new Headers({ "content-length": String(body.length) }),
31
+ };
32
+ }
33
+ function makeErrorResponse(status, body = "") {
34
+ return {
35
+ ok: false,
36
+ status,
37
+ statusText: "Error",
38
+ text: async () => body,
39
+ headers: new Headers({ "content-length": String(body.length) }),
40
+ };
41
+ }
42
+ // ============================================================================
43
+ // isDuckDuckGoCaptcha
44
+ // ============================================================================
45
+ describe("isDuckDuckGoCaptcha", () => {
46
+ it("detects anomaly-modal as CAPTCHA", () => {
47
+ const html = '<html><body><div id="anomaly-modal">Please solve</div></body></html>';
48
+ expect(isDuckDuckGoCaptcha(html)).toBe(true);
49
+ });
50
+ it("detects 'bots use DuckDuckGo too' as CAPTCHA", () => {
51
+ const html = "<html><body><p>It seems like bots use DuckDuckGo too. Please try again.</p></body></html>";
52
+ expect(isDuckDuckGoCaptcha(html)).toBe(true);
53
+ });
54
+ it("returns false for normal HTML", () => {
55
+ const html = '<html><body><div class="results">Normal search results here</div></body></html>';
56
+ expect(isDuckDuckGoCaptcha(html)).toBe(false);
57
+ });
58
+ });
59
+ // ============================================================================
60
+ // cleanDuckDuckGoUrl
61
+ // ============================================================================
62
+ describe("cleanDuckDuckGoUrl", () => {
63
+ it("decodes DDG redirect URLs with uddg parameter", () => {
64
+ const ddgUrl = "//duckduckgo.com/l/?uddg=https%3A%2F%2Fwww.example.com%2Fpage%3Fid%3D1&rut=abc123";
65
+ expect(cleanDuckDuckGoUrl(ddgUrl)).toBe("https://www.example.com/page?id=1");
66
+ });
67
+ it("handles protocol-relative URLs by prepending https:", () => {
68
+ const url = "//www.example.com/some/path";
69
+ expect(cleanDuckDuckGoUrl(url)).toBe("https://www.example.com/some/path");
70
+ });
71
+ it("returns normal URLs unchanged", () => {
72
+ const url = "https://www.example.com/page";
73
+ expect(cleanDuckDuckGoUrl(url)).toBe("https://www.example.com/page");
74
+ });
75
+ });
76
+ // ============================================================================
77
+ // extractUrlsFromDuckDuckGoHtml
78
+ // ============================================================================
79
+ describe("extractUrlsFromDuckDuckGoHtml", () => {
80
+ it("extracts URLs, titles, and snippets from result HTML", () => {
81
+ const html = `
82
+ <div class="result">
83
+ <a class="result__url" href="https://www.example.com/page1"></a>
84
+ <a class="result__a" href="https://www.example.com/page1">Example Page Title</a>
85
+ <a class="result__snippet">This is the snippet for the first result.</a>
86
+ </div>
87
+ <div class="result">
88
+ <a class="result__url" href="https://www.example.com/page2"></a>
89
+ <a class="result__a" href="https://www.example.com/page2">Second Result</a>
90
+ <a class="result__snippet">Snippet for second result.</a>
91
+ </div>
92
+ `;
93
+ const results = extractUrlsFromDuckDuckGoHtml(html);
94
+ expect(results).toHaveLength(2);
95
+ expect(results[0].url).toBe("https://www.example.com/page1");
96
+ expect(results[0].title).toBe("Example Page Title");
97
+ expect(results[0].snippet).toBe("This is the snippet for the first result.");
98
+ expect(results[1].url).toBe("https://www.example.com/page2");
99
+ expect(results[1].title).toBe("Second Result");
100
+ });
101
+ it("filters results by domain when domainFilter is specified", () => {
102
+ const html = `
103
+ <div class="result">
104
+ <a class="result__url" href="https://www.nytimes.com/article"></a>
105
+ <a class="result__a" href="https://www.nytimes.com/article">NYT Article</a>
106
+ <a class="result__snippet">A NYT article snippet.</a>
107
+ </div>
108
+ <div class="result">
109
+ <a class="result__url" href="https://www.example.com/page"></a>
110
+ <a class="result__a" href="https://www.example.com/page">Other Site</a>
111
+ <a class="result__snippet">Not from NYT.</a>
112
+ </div>
113
+ `;
114
+ const results = extractUrlsFromDuckDuckGoHtml(html, "nytimes.com");
115
+ expect(results).toHaveLength(1);
116
+ expect(results[0].url).toBe("https://www.nytimes.com/article");
117
+ expect(results[0].title).toBe("NYT Article");
118
+ });
119
+ it("rejects domain spoofing (evilnytimes.com does not match nytimes.com)", () => {
120
+ const html = `
121
+ <div class="result">
122
+ <a class="result__url" href="https://evilnytimes.com/article"></a>
123
+ <a class="result__a" href="https://evilnytimes.com/article">Fake NYT</a>
124
+ <a class="result__snippet">Spoofed domain.</a>
125
+ </div>
126
+ <div class="result">
127
+ <a class="result__url" href="https://www.nytimes.com/real"></a>
128
+ <a class="result__a" href="https://www.nytimes.com/real">Real NYT</a>
129
+ <a class="result__snippet">Real article.</a>
130
+ </div>
131
+ `;
132
+ const results = extractUrlsFromDuckDuckGoHtml(html, "nytimes.com");
133
+ expect(results).toHaveLength(1);
134
+ expect(results[0].url).toBe("https://www.nytimes.com/real");
135
+ });
136
+ it("cleans DDG redirect URLs in results", () => {
137
+ const html = `
138
+ <div class="result">
139
+ <a class="result__url" href="//duckduckgo.com/l/?uddg=https%3A%2F%2Fwww.example.com%2Fpage&rut=abc"></a>
140
+ <a class="result__a" href="//duckduckgo.com/l/?uddg=https%3A%2F%2Fwww.example.com%2Fpage&rut=abc">Example</a>
141
+ <a class="result__snippet">A snippet.</a>
142
+ </div>
143
+ `;
144
+ const results = extractUrlsFromDuckDuckGoHtml(html);
145
+ expect(results).toHaveLength(1);
146
+ expect(results[0].url).toBe("https://www.example.com/page");
147
+ });
148
+ it("returns empty array for HTML with no results", () => {
149
+ const html = "<html><body><p>No results found</p></body></html>";
150
+ const results = extractUrlsFromDuckDuckGoHtml(html);
151
+ expect(results).toEqual([]);
152
+ });
153
+ it("falls back to result__a hrefs when no result__url matches found", () => {
154
+ const html = `
155
+ <div class="result">
156
+ <a class="result__a" href="https://www.example.com/fallback">Fallback Title</a>
157
+ <a class="result__snippet">Fallback snippet.</a>
158
+ </div>
159
+ `;
160
+ const results = extractUrlsFromDuckDuckGoHtml(html);
161
+ expect(results).toHaveLength(1);
162
+ expect(results[0].url).toBe("https://www.example.com/fallback");
163
+ expect(results[0].title).toBe("Fallback Title");
164
+ });
165
+ });
166
+ // ============================================================================
167
+ // searchDuckDuckGo
168
+ // ============================================================================
169
+ describe("searchDuckDuckGo", () => {
170
+ it("returns search results on successful fetch", async () => {
171
+ const html = `
172
+ <div class="result">
173
+ <a class="result__url" href="https://www.example.com/page"></a>
174
+ <a class="result__a" href="https://www.example.com/page">Example Page</a>
175
+ <a class="result__snippet">A snippet about the page.</a>
176
+ </div>
177
+ `;
178
+ mockFetch.mockResolvedValueOnce(makeOkResponse(html));
179
+ const results = await searchDuckDuckGo({ query: "test query" });
180
+ expect(results).toHaveLength(1);
181
+ expect(results[0].url).toBe("https://www.example.com/page");
182
+ expect(results[0].title).toBe("Example Page");
183
+ // Verify the URL was called correctly
184
+ const [url] = mockFetch.mock.calls[0];
185
+ expect(url).toContain("html.duckduckgo.com/html/");
186
+ expect(url).toContain("q=test%20query");
187
+ });
188
+ it("prepends site: to query when domainFilter is set", async () => {
189
+ const html = `
190
+ <div class="result">
191
+ <a class="result__url" href="https://www.nytimes.com/article"></a>
192
+ <a class="result__a" href="https://www.nytimes.com/article">NYT Article</a>
193
+ <a class="result__snippet">Snippet.</a>
194
+ </div>
195
+ `;
196
+ mockFetch.mockResolvedValueOnce(makeOkResponse(html));
197
+ await searchDuckDuckGo({ query: "test query", domainFilter: "nytimes.com" });
198
+ const [url] = mockFetch.mock.calls[0];
199
+ expect(url).toContain("q=site%3Anytimes.com%20test%20query");
200
+ });
201
+ it("returns empty array on CAPTCHA response", async () => {
202
+ const captchaHtml = '<html><body><div id="anomaly-modal">CAPTCHA</div></body></html>';
203
+ mockFetch.mockResolvedValueOnce(makeOkResponse(captchaHtml));
204
+ const results = await searchDuckDuckGo({ query: "test" });
205
+ expect(results).toEqual([]);
206
+ });
207
+ it("returns empty array on fetch error", async () => {
208
+ mockFetch.mockRejectedValueOnce(new TypeError("Failed to fetch"));
209
+ const results = await searchDuckDuckGo({ query: "test" });
210
+ expect(results).toEqual([]);
211
+ });
212
+ it("returns empty array on non-OK response", async () => {
213
+ mockFetch.mockResolvedValueOnce(makeErrorResponse(503));
214
+ const results = await searchDuckDuckGo({ query: "test" });
215
+ expect(results).toEqual([]);
216
+ });
217
+ });
218
+ //# sourceMappingURL=duckduckgo-search.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"duckduckgo-search.test.js","sourceRoot":"","sources":["../../../src/__tests__/shared/duckduckgo-search.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Tests for the fetch-page utility.
3
+ *
4
+ * Mocks the global `fetch` function to avoid real HTTP calls.
5
+ * Tests direct fetching, browser-like headers, block detection,
6
+ * archive.org fallback, error handling, and abort signal support.
7
+ */
8
+ export {};
9
+ //# sourceMappingURL=fetch-page.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fetch-page.test.d.ts","sourceRoot":"","sources":["../../../src/__tests__/shared/fetch-page.test.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG"}
@@ -0,0 +1,281 @@
1
+ /**
2
+ * Tests for the fetch-page utility.
3
+ *
4
+ * Mocks the global `fetch` function to avoid real HTTP calls.
5
+ * Tests direct fetching, browser-like headers, block detection,
6
+ * archive.org fallback, error handling, and abort signal support.
7
+ */
8
+ import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
9
+ import { fetchPage } from "../../shared/fetch-page.js";
10
+ // ============================================================================
11
+ // Mocks
12
+ // ============================================================================
13
+ const mockFetch = vi.fn();
14
+ beforeEach(() => {
15
+ mockFetch.mockReset();
16
+ vi.stubGlobal("fetch", mockFetch);
17
+ });
18
+ afterEach(() => {
19
+ vi.restoreAllMocks();
20
+ });
21
+ // ============================================================================
22
+ // Test Helpers
23
+ // ============================================================================
24
+ function makeOkResponse(body, status = 200) {
25
+ return {
26
+ ok: true,
27
+ status,
28
+ statusText: "OK",
29
+ text: async () => body,
30
+ headers: new Headers({ "content-length": String(body.length) }),
31
+ };
32
+ }
33
+ function makeErrorResponse(status, body = "") {
34
+ return {
35
+ ok: false,
36
+ status,
37
+ statusText: status === 403 ? "Forbidden" : status === 429 ? "Too Many Requests" : "Error",
38
+ text: async () => body,
39
+ headers: new Headers({ "content-length": String(body.length) }),
40
+ };
41
+ }
42
+ // ============================================================================
43
+ // Direct Fetch Success
44
+ // ============================================================================
45
+ describe("fetchPage", () => {
46
+ describe("direct fetch success", () => {
47
+ it("returns content from a successful direct fetch", async () => {
48
+ const html = "<html><body><h1>Hello World</h1></body></html>";
49
+ mockFetch.mockResolvedValueOnce(makeOkResponse(html));
50
+ const result = await fetchPage({ url: "https://example.com/page" });
51
+ expect(result.content).toBe(html);
52
+ expect(result.url).toBe("https://example.com/page");
53
+ expect(result.fetchMethod).toBe("direct");
54
+ expect(result.error).toBeUndefined();
55
+ });
56
+ });
57
+ // ============================================================================
58
+ // Browser-like Headers
59
+ // ============================================================================
60
+ describe("browser-like headers", () => {
61
+ it("sends browser-like headers on direct fetch", async () => {
62
+ mockFetch.mockResolvedValueOnce(makeOkResponse("<html></html>"));
63
+ await fetchPage({ url: "https://example.com" });
64
+ expect(mockFetch).toHaveBeenCalledTimes(1);
65
+ const [, options] = mockFetch.mock.calls[0];
66
+ const headers = options.headers;
67
+ // Should have a Chrome-like User-Agent
68
+ expect(headers["User-Agent"]).toContain("Chrome");
69
+ // Should accept text/html
70
+ expect(headers["Accept"]).toContain("text/html");
71
+ // Should include Accept-Language
72
+ expect(headers["Accept-Language"]).toBeDefined();
73
+ });
74
+ });
75
+ // ============================================================================
76
+ // Custom User-Agent
77
+ // ============================================================================
78
+ describe("custom User-Agent", () => {
79
+ it("uses the provided userAgent instead of the default", async () => {
80
+ mockFetch.mockResolvedValueOnce(makeOkResponse("<html></html>"));
81
+ await fetchPage({ url: "https://example.com", userAgent: "MyBot/1.0" });
82
+ const [, options] = mockFetch.mock.calls[0];
83
+ const headers = options.headers;
84
+ expect(headers["User-Agent"]).toBe("MyBot/1.0");
85
+ });
86
+ });
87
+ // ============================================================================
88
+ // Hard Block → Archive.org Fallback
89
+ // ============================================================================
90
+ describe("hard block fallback", () => {
91
+ it("falls back to archive.org on HTTP 403", async () => {
92
+ const archiveHtml = "<html><body>Archived content</body></html>";
93
+ mockFetch
94
+ .mockResolvedValueOnce(makeErrorResponse(403))
95
+ .mockResolvedValueOnce(makeOkResponse(archiveHtml));
96
+ const result = await fetchPage({ url: "https://blocked-site.com/page" });
97
+ expect(result.fetchMethod).toBe("archive.org");
98
+ expect(result.content).toBe(archiveHtml);
99
+ // The archive URL should be used
100
+ expect(result.url).toContain("web.archive.org");
101
+ expect(result.url).toContain("https://blocked-site.com/page");
102
+ });
103
+ it("falls back to archive.org on HTTP 429", async () => {
104
+ const archiveHtml = "<html><body>Archived version</body></html>";
105
+ mockFetch
106
+ .mockResolvedValueOnce(makeErrorResponse(429))
107
+ .mockResolvedValueOnce(makeOkResponse(archiveHtml));
108
+ const result = await fetchPage({ url: "https://rate-limited.com/page" });
109
+ expect(result.fetchMethod).toBe("archive.org");
110
+ expect(result.content).toBe(archiveHtml);
111
+ });
112
+ it("falls back to archive.org on HTTP 401", async () => {
113
+ mockFetch
114
+ .mockResolvedValueOnce(makeErrorResponse(401))
115
+ .mockResolvedValueOnce(makeOkResponse("<html>archived</html>"));
116
+ const result = await fetchPage({ url: "https://auth-required.com" });
117
+ expect(result.fetchMethod).toBe("archive.org");
118
+ });
119
+ it("falls back to archive.org on HTTP 451", async () => {
120
+ mockFetch
121
+ .mockResolvedValueOnce(makeErrorResponse(451))
122
+ .mockResolvedValueOnce(makeOkResponse("<html>archived</html>"));
123
+ const result = await fetchPage({ url: "https://legal-block.com" });
124
+ expect(result.fetchMethod).toBe("archive.org");
125
+ });
126
+ });
127
+ // ============================================================================
128
+ // Soft Block → Archive.org Fallback
129
+ // ============================================================================
130
+ describe("soft block fallback", () => {
131
+ it("detects captcha in body and falls back to archive.org", async () => {
132
+ const captchaBody = '<html><body><div class="captcha">Please complete the CAPTCHA to continue</div></body></html>';
133
+ const archiveHtml = "<html><body>Real content from archive</body></html>";
134
+ mockFetch
135
+ .mockResolvedValueOnce(makeOkResponse(captchaBody))
136
+ .mockResolvedValueOnce(makeOkResponse(archiveHtml));
137
+ const result = await fetchPage({ url: "https://captcha-site.com/page" });
138
+ expect(result.fetchMethod).toBe("archive.org");
139
+ expect(result.content).toBe(archiveHtml);
140
+ });
141
+ it("detects 'access denied' in body and falls back", async () => {
142
+ const blockedBody = "<html><body><h1>Access Denied</h1><p>You do not have permission.</p></body></html>";
143
+ mockFetch
144
+ .mockResolvedValueOnce(makeOkResponse(blockedBody))
145
+ .mockResolvedValueOnce(makeOkResponse("<html>archived</html>"));
146
+ const result = await fetchPage({ url: "https://denied-site.com" });
147
+ expect(result.fetchMethod).toBe("archive.org");
148
+ });
149
+ it("detects cloudflare challenge and falls back", async () => {
150
+ const cfBody = "<html><head><title>Just a moment...</title></head><body>DDoS protection by Cloudflare</body></html>";
151
+ mockFetch
152
+ .mockResolvedValueOnce(makeOkResponse(cfBody))
153
+ .mockResolvedValueOnce(makeOkResponse("<html>archived</html>"));
154
+ const result = await fetchPage({ url: "https://cf-protected.com" });
155
+ expect(result.fetchMethod).toBe("archive.org");
156
+ });
157
+ });
158
+ // ============================================================================
159
+ // Large Page with Block Words — NOT Treated as Blocked
160
+ // ============================================================================
161
+ describe("large page with block words", () => {
162
+ it("does NOT treat a large page (>50KB) with block words as blocked", async () => {
163
+ // Create a body larger than 50KB that contains a soft-block word
164
+ const padding = "x".repeat(60_000);
165
+ const largeBody = `<html><body>${padding} captcha appears in article text</body></html>`;
166
+ mockFetch.mockResolvedValueOnce(makeOkResponse(largeBody));
167
+ const result = await fetchPage({ url: "https://big-site.com/article" });
168
+ // Should be treated as direct success, not a soft block
169
+ expect(result.fetchMethod).toBe("direct");
170
+ expect(result.content).toBe(largeBody);
171
+ // Archive should NOT have been attempted
172
+ expect(mockFetch).toHaveBeenCalledTimes(1);
173
+ });
174
+ });
175
+ // ============================================================================
176
+ // Both Direct and Archive Fail → none
177
+ // ============================================================================
178
+ describe("both direct and archive fail", () => {
179
+ it("returns none when both direct and archive.org fail", async () => {
180
+ mockFetch
181
+ .mockResolvedValueOnce(makeErrorResponse(403))
182
+ .mockResolvedValueOnce(makeErrorResponse(404));
183
+ const result = await fetchPage({ url: "https://dead-site.com/page" });
184
+ expect(result.fetchMethod).toBe("none");
185
+ expect(result.content).toBe("");
186
+ expect(result.error).toBeDefined();
187
+ });
188
+ });
189
+ // ============================================================================
190
+ // archiveFallback=false Skips Archive
191
+ // ============================================================================
192
+ describe("archiveFallback disabled", () => {
193
+ it("skips archive.org when archiveFallback is false", async () => {
194
+ mockFetch.mockResolvedValueOnce(makeErrorResponse(403));
195
+ const result = await fetchPage({
196
+ url: "https://blocked-site.com/page",
197
+ archiveFallback: false,
198
+ });
199
+ expect(result.fetchMethod).toBe("none");
200
+ expect(result.content).toBe("");
201
+ // Should NOT have attempted archive.org fetch
202
+ expect(mockFetch).toHaveBeenCalledTimes(1);
203
+ });
204
+ });
205
+ // ============================================================================
206
+ // Non-Blocking HTTP Errors → none Without Archive
207
+ // ============================================================================
208
+ describe("non-blocking HTTP errors", () => {
209
+ it("returns none on 404 without attempting archive.org", async () => {
210
+ mockFetch.mockResolvedValueOnce(makeErrorResponse(404));
211
+ const result = await fetchPage({ url: "https://example.com/missing-page" });
212
+ expect(result.fetchMethod).toBe("none");
213
+ expect(result.content).toBe("");
214
+ expect(result.error).toContain("404");
215
+ // Should NOT have tried archive.org
216
+ expect(mockFetch).toHaveBeenCalledTimes(1);
217
+ });
218
+ it("returns none on 500 without attempting archive.org", async () => {
219
+ mockFetch.mockResolvedValueOnce(makeErrorResponse(500));
220
+ const result = await fetchPage({ url: "https://example.com/error-page" });
221
+ expect(result.fetchMethod).toBe("none");
222
+ expect(result.content).toBe("");
223
+ expect(mockFetch).toHaveBeenCalledTimes(1);
224
+ });
225
+ });
226
+ // ============================================================================
227
+ // Abort Signal Handling
228
+ // ============================================================================
229
+ describe("abort signal handling", () => {
230
+ it("passes the signal to fetch and returns none on abort", async () => {
231
+ const controller = new AbortController();
232
+ controller.abort();
233
+ mockFetch.mockRejectedValueOnce(new DOMException("The operation was aborted.", "AbortError"));
234
+ const result = await fetchPage({
235
+ url: "https://example.com",
236
+ signal: controller.signal,
237
+ });
238
+ expect(result.fetchMethod).toBe("none");
239
+ expect(result.content).toBe("");
240
+ expect(result.error).toBeDefined();
241
+ // Should not attempt archive.org on abort
242
+ expect(mockFetch).toHaveBeenCalledTimes(1);
243
+ });
244
+ it("combines caller signal with timeout", async () => {
245
+ mockFetch.mockResolvedValueOnce(makeOkResponse("<html>ok</html>"));
246
+ const controller = new AbortController();
247
+ await fetchPage({
248
+ url: "https://example.com",
249
+ signal: controller.signal,
250
+ timeoutMs: 5000,
251
+ });
252
+ const [, options] = mockFetch.mock.calls[0];
253
+ // A signal should have been passed to fetch
254
+ expect(options.signal).toBeDefined();
255
+ });
256
+ });
257
+ // ============================================================================
258
+ // Network Error → Archive Fallback
259
+ // ============================================================================
260
+ describe("network error fallback", () => {
261
+ it("falls back to archive.org on network error", async () => {
262
+ const archiveHtml = "<html><body>Archived version</body></html>";
263
+ mockFetch
264
+ .mockRejectedValueOnce(new TypeError("Failed to fetch"))
265
+ .mockResolvedValueOnce(makeOkResponse(archiveHtml));
266
+ const result = await fetchPage({ url: "https://unreachable-site.com/page" });
267
+ expect(result.fetchMethod).toBe("archive.org");
268
+ expect(result.content).toBe(archiveHtml);
269
+ });
270
+ it("returns none when network error occurs and archive also fails", async () => {
271
+ mockFetch
272
+ .mockRejectedValueOnce(new TypeError("Failed to fetch"))
273
+ .mockRejectedValueOnce(new TypeError("Failed to fetch"));
274
+ const result = await fetchPage({ url: "https://unreachable-site.com/page" });
275
+ expect(result.fetchMethod).toBe("none");
276
+ expect(result.content).toBe("");
277
+ expect(result.error).toBeDefined();
278
+ });
279
+ });
280
+ });
281
+ //# sourceMappingURL=fetch-page.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fetch-page.test.js","sourceRoot":"","sources":["../../../src/__tests__/shared/fetch-page.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=html-utils.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"html-utils.test.d.ts","sourceRoot":"","sources":["../../../src/__tests__/shared/html-utils.test.ts"],"names":[],"mappings":""}