Skip to content

Added Node.js unit tests for core JavaScript utilities#636

Merged
khemarato merged 10 commits intobuddhist-uni:mainfrom
xr843:feat/node-tests
Mar 22, 2026
Merged

Added Node.js unit tests for core JavaScript utilities#636
khemarato merged 10 commits intobuddhist-uni:mainfrom
xr843:feat/node-tests

Conversation

@xr843
Copy link
Copy Markdown
Contributor

@xr843 xr843 commented Mar 18, 2026

Summary

  • Added unit tests for assets/js/utils.js covering utils.unaccented, UpdateQueryString, locationOf, sortedInsert, and the Ranges class (interval set data structure)
  • Added unit tests for assets/js/buggytrack.js covering the cyrb53 hash function (determinism, uniqueness, edge cases)
  • Added unit tests for pure functions extracted from assets/js/search_index.js: categoryName, getPositions, and resultMatched
  • Updated package.json to add a "test" script: node --test test/**/*.test.js

Zero new dependencies — uses only Node.js built-in node:test and node:assert modules, plus node:vm to load the browser-global JS files into a sandbox.

40 tests across 9 suites, all passing.

This was discussed and agreed to with the maintainer @khinsen-bot (Khemarato Bhikkhu).

Test plan

  • All 40 tests pass locally with npm test
  • Verify tests pass in CI (Node 25 via actions/setup-node@v4)

Co-Authored-By: Claude Opus 4.6 (1M context) [email protected]

🤖 Generated with Claude Code

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) <[email protected]>
@netlify
Copy link
Copy Markdown

netlify bot commented Mar 18, 2026

Deploy Preview for obu ready!

Name Link
🔨 Latest commit e5bd70e
🔍 Latest deploy log https://app.netlify.com/projects/obu/deploys/69bf6b55d60d9a0008675c08
😎 Deploy Preview https://deploy-preview-636--obu.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

Copy link
Copy Markdown
Collaborator

@khemarato khemarato left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some tests! Excellent!

package.json Outdated
},
"scripts": {}
"scripts": {
"test": "node --test test/**/*.test.js"
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's put these tests closer to the files they're testing in assets/js/

Also: make sure to exclude these js test files from the Jekyll build in _config

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in the latest commit. Moved test files to assets/js/tests/ and added the exclusion pattern in _config.yml to keep them out of the Jekyll build.

// Helper: bring vm-realm objects into the current realm for deepEqual
function toLocal(obj) { return JSON.parse(JSON.stringify(obj)); }

// search_index.js is a Jekyll/Liquid template, so we cannot load it
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's maybe run these tests after the Jekyll build process for now, so you can grab the real output js? Or alternatively we can split search_index.js into two files (js code and document data) as a first PR before these tests. But I am not a fan of trying to build a custom parser inside a test 😅

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed — the custom parser is brittle and not ideal. I see two viable paths:

  1. Split search_index.js into pure JS logic + Liquid data template — cleanest long-term, and tests can import the JS directly
  2. Run tests post-build against the generated output — simpler setup but couples tests to the build pipeline

I'd lean toward option 1 since it also improves the code structure. Happy to submit that as a preliminary PR. Which approach do you prefer?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good! I'll submit a preliminary PR to split search_index.js into pure JS logic (search_functions.js) + Liquid data template first. Once that's merged, I'll rebase this PR to:

  • Remove the extractFunction workaround and import the split file directly
  • Add html-validate as a devDependency for HTML fragment validation
  • Add the "finds the section with the most matches" test case for getBlurbForResult
  • Validate HTML output from categoryName, addMatchHighlights, and getBlurbForResult


describe('categoryName', () => {
it('returns correct HTML for known categories', () => {
assert.ok(categoryName('av').includes('Recording'));
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These are over-specified and trivial tests. Instead of testing that each category has the right name, let's just make sure they are valid html fragments. We can leave the exact names to the function itself.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good call. Fixed in the latest commit — now only validates that the return value contains a valid <i> icon element and is non-empty HTML, without asserting specific display names.

const result = { matchData: { metadata: {} } };
assert.equal(resultMatched(result, 'title'), false);
});
});
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's also add tests for addMatchHighlights and getBlurbForResult while we're here? (Nonblocking: can do in a follow-up PR if you like)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added tests for both addMatchHighlights and getBlurbForResult in the latest commit.

…rage

- 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) <[email protected]>
Copy link
Copy Markdown
Collaborator

@khemarato khemarato left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed. Let's go ahead and split search_index.js first so we can run these tests before the build.

const content = 'a '.repeat(50) + 'MATCH' + ' b'.repeat(200);
const item = { title: 'Test', description: null, content: content };
const blurb = getBlurbForResult(result, item, [50]);
assert.ok(blurb.includes('MATCH') || blurb.includes('<strong>'));
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

or?

@xr843
Copy link
Copy Markdown
Contributor Author

xr843 commented Mar 19, 2026

Thanks for reviewing #638! Since you prefer keeping search_index.js as-is and testing manually, I'll update this PR to:

  • Remove the custom extractFunction parser for search_index.js tests
  • Keep the pure JS tests (buggytrack, search-functions, utils) that don't need the split
  • Add html-validate for HTML fragment validation as you suggested

Does that sound right, or would you prefer a different approach?

@khemarato
Copy link
Copy Markdown
Collaborator

Yep that sounds about right. You can also add tests for your new handleSearchMessage function.

xr843 and others added 2 commits March 20, 2026 07:13
…tests

Address review feedback:
- Remove extractFunction parser since search_functions.js now exists
  as a standalone file (after buddhist-uni#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) <[email protected]>
@xr843
Copy link
Copy Markdown
Contributor Author

xr843 commented Mar 19, 2026

Updated! Changes in the latest push:

  1. Removed extractFunction parser — now that Split search_index.js into pure JS logic and Liquid data template #637 merged and search_functions.js exists as a standalone file, the test loads it directly via vm.runInContext()
  2. Added handleSearchMessage tests (8 new test cases) covering:
    • Result object structure validation
    • Stop word detection (e.g. "the" is not prefixed with +)
    • + prefix logic for required matching
    • Preserving existing +/- prefixes
    • Fallback to unprefixed query when required query returns nothing
    • filterquery filtering
    • Pass-through of q/filterquery/qt fields

All 57 tests pass (12 suites, 0 failures).

Also merged upstream main to resolve the out-of-date branch.

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) <[email protected]>
@khemarato
Copy link
Copy Markdown
Collaborator

Ping: There are four unaddressed comments above, @xr843 No rush, just alerting you in case they dropped from your inbox. 😊

@xr843
Copy link
Copy Markdown
Contributor Author

xr843 commented Mar 21, 2026

@khemarato Done! Addressed all 4 comments:

  • Added html-validate for HTML fragment validation on addMatchHighlights and getBlurbForResult
  • Fixed the weak || assertion to check <strong>MATCH</strong> directly
  • Added "finds the section with the most matches" test case
  • Also fixed a wrong position offset in the test data

60 tests passing now. Let me know if anything else needs adjustment. Have a great day! 🙏

Copy link
Copy Markdown
Collaborator

@khemarato khemarato left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay. Two more nits and then this should be good. 😊

// Should pick the section with the most matches (MATCH, ME, INSTEAD) not the lone IGNORE
assert.ok(blurb.includes('MATCH'), 'Expected blurb to contain MATCH');
assert.ok(blurb.includes('INSTEAD'), 'Expected blurb to contain INSTEAD');
assert.ok(!blurb.includes('IGNORE'), 'Expected blurb to NOT contain IGNORE');
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

xr843 and others added 3 commits March 22, 2026 10:20
…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) <[email protected]>
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) <[email protected]>
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) <[email protected]>
Copy link
Copy Markdown
Collaborator

@khemarato khemarato left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🐛 Anumodana

var best_i = -1;
var best_n = 0;
for (var i in positions) {
for (var i = 0; i < positions.length; i++) {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Excellent! This is why I asked for the flipped test. I had a feeling there was a bug! 😁

@khemarato khemarato merged commit 33b7f68 into buddhist-uni:main Mar 22, 2026
4 checks passed
@xr843 xr843 deleted the feat/node-tests branch March 23, 2026 02:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants