summaryrefslogtreecommitdiff
path: root/test/search-url.test.ts
diff options
context:
space:
mode:
Diffstat (limited to 'test/search-url.test.ts')
-rw-r--r--test/search-url.test.ts97
1 files changed, 75 insertions, 22 deletions
diff --git a/test/search-url.test.ts b/test/search-url.test.ts
index 13f58d1..2425f76 100644
--- a/test/search-url.test.ts
+++ b/test/search-url.test.ts
@@ -1,35 +1,88 @@
import assert from "node:assert/strict";
import { test } from "node:test";
+import { RECENCY_VALUES, allEngines, getEngine, type Recency } from "../src/engines.ts";
import { buildSearchUrl } from "../src/search.ts";
-const paramsOf = (url: string): URLSearchParams => new URL(url).searchParams;
+const urlFor = (id: Parameters<typeof getEngine>[0], recency?: Recency) =>
+ new URL(buildSearchUrl(getEngine(id), { query: "sqlite wal mode", numResults: 7, recency }));
-test("a plain query asks for the Web tab in English", () => {
- const url = buildSearchUrl({ query: "typebox json schema", numResults: 10 });
- assert.equal(new URL(url).origin + new URL(url).pathname, "https://www.google.com/search");
- const p = paramsOf(url);
- assert.equal(p.get("q"), "typebox json schema");
- assert.equal(p.get("num"), "10");
- assert.equal(p.get("hl"), "en");
- assert.equal(p.get("udm"), "14");
- assert.equal(p.get("tbs"), null);
+test("google uses the Web tab, and swaps it for as_qdr when time-limited", () => {
+ const plain = urlFor("google");
+ assert.equal(plain.origin + plain.pathname, "https://www.google.com/search");
+ assert.equal(plain.searchParams.get("udm"), "14");
+ assert.equal(plain.searchParams.get("num"), "7");
+ assert.equal(plain.searchParams.get("as_qdr"), null);
+
+ const dated = urlFor("google", "month");
+ // Both findings in one assertion pair: tbs is never used, and udm must be
+ // dropped when a date filter is present or the page renders empty.
+ assert.equal(dated.searchParams.get("as_qdr"), "m");
+ assert.equal(dated.searchParams.get("udm"), null);
+ assert.equal(dated.searchParams.get("tbs"), null);
+});
+
+test("duckduckgo uses the no-JS html endpoint and df=", () => {
+ const plain = urlFor("duckduckgo");
+ assert.equal(plain.origin + plain.pathname, "https://html.duckduckgo.com/html/");
+ assert.equal(plain.searchParams.get("q"), "sqlite wal mode");
+ assert.equal(plain.searchParams.get("df"), null);
+
+ for (const [recency, code] of [
+ ["day", "d"],
+ ["week", "w"],
+ ["month", "m"],
+ ["year", "y"],
+ ] as const) {
+ assert.equal(urlFor("duckduckgo", recency).searchParams.get("df"), code, recency);
+ }
+});
+
+test("bing uses count=, and drops it when filtering because count cancels filters", () => {
+ const plain = urlFor("bing");
+ assert.equal(plain.origin + plain.pathname, "https://www.bing.com/search");
+ assert.equal(plain.searchParams.get("count"), "7");
+ assert.equal(plain.searchParams.get("filters"), null);
+
+ for (const [recency, code] of [
+ ["day", "ez1"],
+ ["week", "ez2"],
+ ["month", "ez3"],
+ ] as const) {
+ const dated = urlFor("bing", recency);
+ assert.equal(dated.searchParams.get("filters"), `ex1:"${code}"`, recency);
+ // Observed: sending count alongside filters silently returns unfiltered
+ // results that look entirely plausible. This assertion is the regression.
+ assert.equal(dated.searchParams.get("count"), null, `${recency}: count must be dropped`);
+ }
});
-test("recency uses as_qdr and drops udm — tbs and udm both render an empty page", () => {
- const cases = { day: "d", week: "w", month: "m", year: "y" } as const;
- for (const [recency, expected] of Object.entries(cases)) {
- const url = buildSearchUrl({ query: "q", numResults: 5, recency: recency as keyof typeof cases });
- const p = paramsOf(url);
- assert.equal(p.get("as_qdr"), expected, recency);
- assert.equal(p.get("tbs"), null, `${recency}: tbs must not be used`);
- assert.equal(p.get("udm"), null, `${recency}: udm must be dropped alongside a date filter`);
+test("brave is a plain query — it advertises no time filter at all", () => {
+ const plain = urlFor("brave");
+ assert.equal(plain.origin + plain.pathname, "https://search.brave.com/search");
+ assert.equal(plain.searchParams.get("q"), "sqlite wal mode");
+ assert.deepEqual(getEngine("brave").recency, {});
+});
+
+test("every advertised recency window maps to a non-empty parameter value", () => {
+ // Guards the failure this whole design exists to prevent: an engine claiming
+ // support for a window it then silently drops from the URL.
+ for (const engine of allEngines()) {
+ for (const recency of RECENCY_VALUES) {
+ const code = engine.recency[recency];
+ if (code === undefined) continue;
+ assert.ok(code.length > 0, `${engine.id}/${recency} maps to an empty value`);
+ const url = buildSearchUrl(engine, { query: "q", numResults: 5, recency });
+ assert.ok(url.includes(encodeURIComponent(code)) || url.includes(code), `${engine.id}/${recency} lost its value`);
+ }
}
});
-test("queries with characters that would break a URL are encoded", () => {
+test("queries with characters that would break a URL are encoded, on every engine", () => {
const query = 'site:example.com "exact phrase" a&b?c=d #frag +plus/slash';
- const url = buildSearchUrl({ query, numResults: 3 });
- assert.equal(paramsOf(url).get("q"), query);
- assert.ok(!url.includes(" "), "no raw spaces in the URL");
+ for (const engine of allEngines()) {
+ const url = buildSearchUrl(engine, { query, numResults: 3 });
+ assert.equal(new URL(url).searchParams.get("q"), query, engine.id);
+ assert.ok(!url.includes(" "), `${engine.id}: no raw spaces in the URL`);
+ }
});