Fix false-positive search failures: real timeout + logged-in searches
The "has hits" searches produced false-positive failures in two ways:
- search() relied on Puppeteer's default 30s navigation timeout, which is
independent of KORAP_SEARCH_TIMEOUT, so complex queries on very large
corpora timed out at 30s even when the timeout was raised. The caller
timeout now flows into waitForNavigation and waitForFunction.
- "Logout works" ran before the searches: Mocha executes a suite's direct
it() tests before its nested describe() suites, so the top-level logout
test fired first and logged us out, leaving the searches to query the
(tiny) public corpus and report 0 hits. Logout is now wrapped in its own
suite declared after the searches, so it runs last; the searches' before
hook also asserts a required login actually succeeded.
Add an optional KORAP_VC env var to restrict the searches to a virtual
corpus (passed as cq), keeping complex queries fast enough to finish within
the timeout.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Change-Id: If72a0b9c36700ca1a546acfd4850ee447d586a38
diff --git a/lib/korap_rc.js b/lib/korap_rc.js
index 0bdf1af..e2d8a15 100644
--- a/lib/korap_rc.js
+++ b/lib/korap_rc.js
@@ -70,17 +70,38 @@
}
}
- async search(page, query) {
+ async search(page, query, options = {}) {
+ // Both the navigation and the post-navigation settle should honour the
+ // caller-supplied timeout. Without this, Puppeteer's default 30s
+ // navigation timeout silently caps the wait, so complex queries on very
+ // large corpora time out (false-positive failure) even when
+ // KORAP_SEARCH_TIMEOUT is raised. Default to 30s for backwards compat.
+ const timeout = options.timeout || 30000;
+ // Optional virtual corpus restriction (e.g. "pubDate in 2020"). When
+ // set, it is passed as the corpus query (cq) to narrow the search and
+ // keep it fast enough to finish within the timeout.
+ const vc = options.vc || "";
try {
- await page.waitForSelector("#q-field", { visible: true });
- const query_field = await page.$("#q-field");
- assert.notEqual(query_field, null, "Query field not found");
+ if (vc) {
+ // A VC can't be entered through the query field, so navigate to
+ // the search URL directly with q + cq. The session cookie is
+ // preserved, so the user stays logged in.
+ const url = new URL(this.korap_url);
+ url.searchParams.set('q', query);
+ url.searchParams.set('ql', 'poliqarp');
+ url.searchParams.set('cq', vc);
+ await page.goto(url.href, { waitUntil: 'domcontentloaded', timeout });
+ } else {
+ await page.waitForSelector("#q-field", { visible: true });
+ const query_field = await page.$("#q-field");
+ assert.notEqual(query_field, null, "Query field not found");
- await query_field.click({ clickCount: 3 });
- await page.keyboard.type(query);
- await page.keyboard.press("Enter");
+ await query_field.click({ clickCount: 3 });
+ await page.keyboard.type(query);
+ await page.keyboard.press("Enter");
- await page.waitForNavigation({ waitUntil: 'domcontentloaded' });
+ await page.waitForNavigation({ waitUntil: 'domcontentloaded', timeout });
+ }
// Wait until the results page has actually settled, then read the
// count immediately (no fixed sleep). Kalamar renders the precise
@@ -93,7 +114,7 @@
if (total && /\d/.test(total.textContent || '')) return true;
if (document.querySelectorAll('#search ol li').length > 0) return true;
return document.querySelector('#search .no-results, .no-results') !== null;
- }, { timeout: 15000, polling: 200 });
+ }, { timeout, polling: 200 });
const hits = await page.evaluate(() => {
const total = document.querySelector('#total-results');