@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.
- package/README.md +59 -0
- package/dist/__tests__/archives/chronicling-america.test.d.ts +8 -0
- package/dist/__tests__/archives/chronicling-america.test.d.ts.map +1 -0
- package/dist/__tests__/archives/chronicling-america.test.js +151 -0
- package/dist/__tests__/archives/chronicling-america.test.js.map +1 -0
- package/dist/__tests__/archives/europeana.test.d.ts +8 -0
- package/dist/__tests__/archives/europeana.test.d.ts.map +1 -0
- package/dist/__tests__/archives/europeana.test.js +200 -0
- package/dist/__tests__/archives/europeana.test.js.map +1 -0
- package/dist/__tests__/archives/internet-archive.test.d.ts +8 -0
- package/dist/__tests__/archives/internet-archive.test.d.ts.map +1 -0
- package/dist/__tests__/archives/internet-archive.test.js +189 -0
- package/dist/__tests__/archives/internet-archive.test.js.map +1 -0
- package/dist/__tests__/archives/trove.test.d.ts +8 -0
- package/dist/__tests__/archives/trove.test.d.ts.map +1 -0
- package/dist/__tests__/archives/trove.test.js +202 -0
- package/dist/__tests__/archives/trove.test.js.map +1 -0
- package/dist/__tests__/books/google-books.test.d.ts +8 -0
- package/dist/__tests__/books/google-books.test.d.ts.map +1 -0
- package/dist/__tests__/books/google-books.test.js +221 -0
- package/dist/__tests__/books/google-books.test.js.map +1 -0
- package/dist/__tests__/books/open-library.test.d.ts +8 -0
- package/dist/__tests__/books/open-library.test.d.ts.map +1 -0
- package/dist/__tests__/books/open-library.test.js +159 -0
- package/dist/__tests__/books/open-library.test.js.map +1 -0
- package/dist/__tests__/news/guardian.test.d.ts +9 -0
- package/dist/__tests__/news/guardian.test.d.ts.map +1 -0
- package/dist/__tests__/news/guardian.test.js +224 -0
- package/dist/__tests__/news/guardian.test.js.map +1 -0
- package/dist/__tests__/news/nytimes.test.d.ts +9 -0
- package/dist/__tests__/news/nytimes.test.d.ts.map +1 -0
- package/dist/__tests__/news/nytimes.test.js +271 -0
- package/dist/__tests__/news/nytimes.test.js.map +1 -0
- package/dist/__tests__/news/site-search-source.test.d.ts +9 -0
- package/dist/__tests__/news/site-search-source.test.d.ts.map +1 -0
- package/dist/__tests__/news/site-search-source.test.js +342 -0
- package/dist/__tests__/news/site-search-source.test.js.map +1 -0
- package/dist/__tests__/obituary/find-a-grave.test.d.ts +8 -0
- package/dist/__tests__/obituary/find-a-grave.test.d.ts.map +1 -0
- package/dist/__tests__/obituary/find-a-grave.test.js +238 -0
- package/dist/__tests__/obituary/find-a-grave.test.js.map +1 -0
- package/dist/__tests__/shared/duckduckgo-search.test.d.ts +9 -0
- package/dist/__tests__/shared/duckduckgo-search.test.d.ts.map +1 -0
- package/dist/__tests__/shared/duckduckgo-search.test.js +218 -0
- package/dist/__tests__/shared/duckduckgo-search.test.js.map +1 -0
- package/dist/__tests__/shared/fetch-page.test.d.ts +9 -0
- package/dist/__tests__/shared/fetch-page.test.d.ts.map +1 -0
- package/dist/__tests__/shared/fetch-page.test.js +281 -0
- package/dist/__tests__/shared/fetch-page.test.js.map +1 -0
- package/dist/__tests__/shared/html-utils.test.d.ts +2 -0
- package/dist/__tests__/shared/html-utils.test.d.ts.map +1 -0
- package/dist/__tests__/shared/html-utils.test.js +169 -0
- package/dist/__tests__/shared/html-utils.test.js.map +1 -0
- package/dist/__tests__/shared/readability-extract.test.d.ts +2 -0
- package/dist/__tests__/shared/readability-extract.test.d.ts.map +1 -0
- package/dist/__tests__/shared/readability-extract.test.js +107 -0
- package/dist/__tests__/shared/readability-extract.test.js.map +1 -0
- package/dist/__tests__/shared/sanitize-text.test.d.ts +2 -0
- package/dist/__tests__/shared/sanitize-text.test.d.ts.map +1 -0
- package/dist/__tests__/shared/sanitize-text.test.js +77 -0
- package/dist/__tests__/shared/sanitize-text.test.js.map +1 -0
- package/dist/__tests__/shared/search-utils.test.d.ts +2 -0
- package/dist/__tests__/shared/search-utils.test.d.ts.map +1 -0
- package/dist/__tests__/shared/search-utils.test.js +26 -0
- package/dist/__tests__/shared/search-utils.test.js.map +1 -0
- package/dist/__tests__/structured/wikidata.test.d.ts +9 -0
- package/dist/__tests__/structured/wikidata.test.d.ts.map +1 -0
- package/dist/__tests__/structured/wikidata.test.js +509 -0
- package/dist/__tests__/structured/wikidata.test.js.map +1 -0
- package/dist/__tests__/structured/wikipedia.test.d.ts +9 -0
- package/dist/__tests__/structured/wikipedia.test.d.ts.map +1 -0
- package/dist/__tests__/structured/wikipedia.test.js +643 -0
- package/dist/__tests__/structured/wikipedia.test.js.map +1 -0
- package/dist/__tests__/web-search/base.test.d.ts +9 -0
- package/dist/__tests__/web-search/base.test.d.ts.map +1 -0
- package/dist/__tests__/web-search/base.test.js +622 -0
- package/dist/__tests__/web-search/base.test.js.map +1 -0
- package/dist/__tests__/web-search/bing.test.d.ts +10 -0
- package/dist/__tests__/web-search/bing.test.d.ts.map +1 -0
- package/dist/__tests__/web-search/bing.test.js +277 -0
- package/dist/__tests__/web-search/bing.test.js.map +1 -0
- package/dist/__tests__/web-search/brave.test.d.ts +10 -0
- package/dist/__tests__/web-search/brave.test.d.ts.map +1 -0
- package/dist/__tests__/web-search/brave.test.js +264 -0
- package/dist/__tests__/web-search/brave.test.js.map +1 -0
- package/dist/__tests__/web-search/duckduckgo.test.d.ts +10 -0
- package/dist/__tests__/web-search/duckduckgo.test.d.ts.map +1 -0
- package/dist/__tests__/web-search/duckduckgo.test.js +107 -0
- package/dist/__tests__/web-search/duckduckgo.test.js.map +1 -0
- package/dist/__tests__/web-search/google.test.d.ts +9 -0
- package/dist/__tests__/web-search/google.test.d.ts.map +1 -0
- package/dist/__tests__/web-search/google.test.js +189 -0
- package/dist/__tests__/web-search/google.test.js.map +1 -0
- package/dist/archives/chronicling-america.d.ts +33 -0
- package/dist/archives/chronicling-america.d.ts.map +1 -0
- package/dist/archives/chronicling-america.js +85 -0
- package/dist/archives/chronicling-america.js.map +1 -0
- package/dist/archives/europeana.d.ts +37 -0
- package/dist/archives/europeana.d.ts.map +1 -0
- package/dist/archives/europeana.js +92 -0
- package/dist/archives/europeana.js.map +1 -0
- package/dist/archives/internet-archive.d.ts +32 -0
- package/dist/archives/internet-archive.d.ts.map +1 -0
- package/dist/archives/internet-archive.js +90 -0
- package/dist/archives/internet-archive.js.map +1 -0
- package/dist/archives/trove.d.ts +37 -0
- package/dist/archives/trove.d.ts.map +1 -0
- package/dist/archives/trove.js +97 -0
- package/dist/archives/trove.js.map +1 -0
- package/dist/books/google-books.d.ts +48 -0
- package/dist/books/google-books.d.ts.map +1 -0
- package/dist/books/google-books.js +111 -0
- package/dist/books/google-books.js.map +1 -0
- package/dist/books/open-library.d.ts +44 -0
- package/dist/books/open-library.d.ts.map +1 -0
- package/dist/books/open-library.js +103 -0
- package/dist/books/open-library.js.map +1 -0
- package/dist/index.d.ts +45 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +35 -0
- package/dist/index.js.map +1 -0
- package/dist/news/guardian.d.ts +51 -0
- package/dist/news/guardian.d.ts.map +1 -0
- package/dist/news/guardian.js +131 -0
- package/dist/news/guardian.js.map +1 -0
- package/dist/news/nytimes.d.ts +27 -0
- package/dist/news/nytimes.d.ts.map +1 -0
- package/dist/news/nytimes.js +104 -0
- package/dist/news/nytimes.js.map +1 -0
- package/dist/news/site-search-source.d.ts +89 -0
- package/dist/news/site-search-source.d.ts.map +1 -0
- package/dist/news/site-search-source.js +182 -0
- package/dist/news/site-search-source.js.map +1 -0
- package/dist/news/sources.d.ts +52 -0
- package/dist/news/sources.d.ts.map +1 -0
- package/dist/news/sources.js +276 -0
- package/dist/news/sources.js.map +1 -0
- package/dist/obituary/find-a-grave.d.ts +43 -0
- package/dist/obituary/find-a-grave.d.ts.map +1 -0
- package/dist/obituary/find-a-grave.js +173 -0
- package/dist/obituary/find-a-grave.js.map +1 -0
- package/dist/shared/duckduckgo-search.d.ts +86 -0
- package/dist/shared/duckduckgo-search.d.ts.map +1 -0
- package/dist/shared/duckduckgo-search.js +218 -0
- package/dist/shared/duckduckgo-search.js.map +1 -0
- package/dist/shared/fetch-page.d.ts +50 -0
- package/dist/shared/fetch-page.d.ts.map +1 -0
- package/dist/shared/fetch-page.js +212 -0
- package/dist/shared/fetch-page.js.map +1 -0
- package/dist/shared/html-utils.d.ts +99 -0
- package/dist/shared/html-utils.d.ts.map +1 -0
- package/dist/shared/html-utils.js +246 -0
- package/dist/shared/html-utils.js.map +1 -0
- package/dist/shared/readability-extract.d.ts +33 -0
- package/dist/shared/readability-extract.d.ts.map +1 -0
- package/dist/shared/readability-extract.js +45 -0
- package/dist/shared/readability-extract.js.map +1 -0
- package/dist/shared/sanitize-text.d.ts +24 -0
- package/dist/shared/sanitize-text.d.ts.map +1 -0
- package/dist/shared/sanitize-text.js +49 -0
- package/dist/shared/sanitize-text.js.map +1 -0
- package/dist/shared/search-utils.d.ts +18 -0
- package/dist/shared/search-utils.d.ts.map +1 -0
- package/dist/shared/search-utils.js +20 -0
- package/dist/shared/search-utils.js.map +1 -0
- package/dist/structured/wikidata.d.ts +128 -0
- package/dist/structured/wikidata.d.ts.map +1 -0
- package/dist/structured/wikidata.js +361 -0
- package/dist/structured/wikidata.js.map +1 -0
- package/dist/structured/wikipedia.d.ts +184 -0
- package/dist/structured/wikipedia.d.ts.map +1 -0
- package/dist/structured/wikipedia.js +275 -0
- package/dist/structured/wikipedia.js.map +1 -0
- package/dist/web-search/base.d.ts +128 -0
- package/dist/web-search/base.d.ts.map +1 -0
- package/dist/web-search/base.js +251 -0
- package/dist/web-search/base.js.map +1 -0
- package/dist/web-search/bing.d.ts +21 -0
- package/dist/web-search/bing.d.ts.map +1 -0
- package/dist/web-search/bing.js +53 -0
- package/dist/web-search/bing.js.map +1 -0
- package/dist/web-search/brave.d.ts +21 -0
- package/dist/web-search/brave.d.ts.map +1 -0
- package/dist/web-search/brave.js +56 -0
- package/dist/web-search/brave.js.map +1 -0
- package/dist/web-search/duckduckgo.d.ts +15 -0
- package/dist/web-search/duckduckgo.d.ts.map +1 -0
- package/dist/web-search/duckduckgo.js +21 -0
- package/dist/web-search/duckduckgo.js.map +1 -0
- package/dist/web-search/google.d.ts +24 -0
- package/dist/web-search/google.d.ts.map +1 -0
- package/dist/web-search/google.js +48 -0
- package/dist/web-search/google.js.map +1 -0
- 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 @@
|
|
|
1
|
+
{"version":3,"file":"html-utils.test.d.ts","sourceRoot":"","sources":["../../../src/__tests__/shared/html-utils.test.ts"],"names":[],"mappings":""}
|