Skip to content

Commit 33b7f68

Browse files
xr843claude
andauthored
Added Node.js unit tests for core JavaScript utilities (#636)
* Added Node.js unit tests for core JavaScript utilities Added tests for utils.js (unaccented, UpdateQueryString, locationOf, sortedInsert, Ranges) and buggytrack.js (cyrb53) using Node's built-in test runner. Zero new dependencies. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Address review feedback: relocate tests, improve assertions, add coverage - Moved test files from test/ to assets/js/tests/ (closer to source) - Added assets/js/tests/ to _config.yml exclude list - Updated categoryName tests to validate HTML structure instead of specific names - Added tests for addMatchHighlights and getBlurbForResult - Added inline comments explaining why vm.createContext needs explicit globals Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * refactor: load search_functions.js directly, add handleSearchMessage tests Address review feedback: - Remove extractFunction parser since search_functions.js now exists as a standalone file (after #637 merged) - Load search_functions.js directly via vm sandbox - Add handleSearchMessage tests covering: stop word handling, +prefix logic, fallback search, filterquery filtering, and result structure validation Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * test: add +-, --, -+ prefix combination tests for handleSearchMessage Address review feedback from khemarato: add test cases covering all prefix combinations to verify existing +/- prefixes are preserved. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * test: address review feedback — add html-validate and missing test cases * fix(test): add HTML validation in categoryName loop + flip assertion order Address review nits: - Add assertValidHtml() for every category in the knownCategories loop - Flip assertion order in "finds the section with the most matches" to catch incorrect greedy implementations by checking the negative first Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(search): use numeric for-loop instead of for-in on positions array for (var i in positions) yields string keys ("0", "1", ...), causing j = i + 1 to produce "01" (string concatenation) instead of 1 (numeric addition). This made the inner while-loop skip positions[1] for i=0 and exit immediately for all other starting indices, so the algorithm always selected the first match's window regardless of where the densest cluster was. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * test(search): flip test data to catch greedy blurb selection Place the lone IGNORE match before the cluster (MATCH, ME, INSTEAD) so that a greedy algorithm picking the first match would fail. Also add assertValidHtml() in the categoryName loop per review. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent bfe89ad commit 33b7f68

7 files changed

Lines changed: 928 additions & 31 deletions

File tree

‎_config.yml‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ exclude:
8888
- scripts/
8989
- .github/
9090
- .obsidian/
91+
- assets/js/tests/
9192

9293
permalink: pretty
9394

‎assets/js/search_functions.js‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ function getBlurbForResult(result, item, positions) {
6767
// Calculate the best section of the content to blurb
6868
var best_i = -1;
6969
var best_n = 0;
70-
for (var i in positions) {
70+
for (var i = 0; i < positions.length; i++) {
7171
var n = 1;
7272
var j = i+1;
7373
while (j < positions.length) {

‎assets/js/tests/buggytrack.test.js‎

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
const { describe, it } = require('node:test');
2+
const assert = require('node:assert/strict');
3+
const vm = require('node:vm');
4+
const fs = require('node:fs');
5+
const path = require('node:path');
6+
7+
// Load only the cyrb53 function from buggytrack.js
8+
// The file also creates a BuggyTracker that needs `document`, so we
9+
// provide minimal DOM stubs to let the file evaluate without errors.
10+
const src = fs.readFileSync(
11+
path.join(__dirname, '..', 'buggytrack.js'),
12+
'utf-8'
13+
);
14+
15+
// vm.createContext() creates a bare sandbox with no built-in globals.
16+
// Unlike the main Node.js runtime, Math, Array, etc. must be provided explicitly.
17+
const sandbox = {
18+
Math,
19+
console,
20+
localStorage: {
21+
getItem() { return null; },
22+
setItem() {},
23+
},
24+
window: { WEBSITE_SECTION: 'test' },
25+
gtag() {},
26+
document: {
27+
createElement() {
28+
return { href: '', host: '', hostname: '', pathname: '' };
29+
},
30+
location: { host: 'example.com', hostname: 'example.com', pathname: '/', search: '', href: 'https://example.com/' },
31+
addEventListener() {},
32+
referrer: '',
33+
cookie: '',
34+
},
35+
};
36+
// BuggyTracker references `document` as a bare global
37+
sandbox.self = sandbox;
38+
vm.createContext(sandbox);
39+
vm.runInContext(src + '\nthis.cyrb53 = cyrb53;\n', sandbox);
40+
41+
const { cyrb53 } = sandbox;
42+
43+
describe('cyrb53', () => {
44+
it('returns a number', () => {
45+
assert.equal(typeof cyrb53('hello'), 'number');
46+
});
47+
48+
it('is deterministic (same input gives same output)', () => {
49+
const a = cyrb53('test string');
50+
const b = cyrb53('test string');
51+
assert.equal(a, b);
52+
});
53+
54+
it('produces different hashes for different inputs', () => {
55+
const a = cyrb53('hello');
56+
const b = cyrb53('world');
57+
assert.notEqual(a, b);
58+
});
59+
60+
it('handles empty string', () => {
61+
const h = cyrb53('');
62+
assert.equal(typeof h, 'number');
63+
assert.ok(Number.isFinite(h));
64+
});
65+
66+
it('handles unicode strings', () => {
67+
const h = cyrb53('āgama nibbāna');
68+
assert.equal(typeof h, 'number');
69+
assert.ok(Number.isFinite(h));
70+
});
71+
72+
it('returns non-negative values', () => {
73+
for (const s of ['a', 'bb', 'ccc', '', 'test123']) {
74+
assert.ok(cyrb53(s) >= 0, `Expected non-negative hash for "${s}"`);
75+
}
76+
});
77+
});

0 commit comments

Comments
 (0)