Document Version: 1.1
Last Updated: January 2026
Status: Draft (2026 Proposal - Revised)
Authors: Nightscout Development Team
This proposal has been revised based on stakeholder interviews to align testing modernization with broader architectural goals. The original focus on migrating all client tests to Jest has been replaced with a leaner, three-track approach that:
- Gets tests running reliably with updated dependencies
- Separates pure logic from DOM code to enable faster, simpler testing
- Prepares for UI modernization without wasting effort on tests for code that will be replaced
Key insight: The current webpack bundle conflates pure logic (hashauth, statistics, data transforms) with DOM manipulation (jQuery, d3 rendering). Separating these concerns unlocks both testability and maintainability.
The following context informed the revised strategy:
| Question | Finding |
|---|---|
| What's driving modernization? | Increase development velocity for new features; potentially removing old ones and consolidating UI libraries |
| Database requirements? | Tests need to run against a database; current deps are outdated |
| UI library plans? | jQuery UI, d3, and other libraries may be consolidated or replaced |
| Critical client tests? | hashauth.test.js must continue working (security-critical) |
| Other client tests? | May be deferred since underlying UI code could be rewritten |
| Future architecture? | Server-side statistics API, possibly narrator-driven interface for agentic insulin delivery |
| Test harness security? | jsdom/Playwright must have strict network isolation to prevent unintended requests |
| Category | Count | Framework | Status |
|---|---|---|---|
| API/Server Tests | ~60 | mocha + supertest | Functional, needs updates |
| Client/UI Tests | 7 | mocha + benv/jsdom | Fragile, uses unmaintained deps |
| Disabled Tests | 1 | - | client.test.js.temporary_removed |
| File | Decision | Rationale |
|---|---|---|
hashauth.test.js |
Migrate | Security-critical, must keep working |
careportal.test.js |
Skip/Defer | UI code may be rewritten |
profileeditor.test.js |
Skip/Defer | Complex UI mocking, low ROI |
pluginbase.test.js |
Skip/Defer | Review after logic extraction |
admintools.test.js |
Skip/Defer | UI code may be rewritten |
reports.test.js |
Skip/Defer | Stats moving to server API |
adminnotifies.test.js |
Skip/Defer | Low priority |
The current bundle.app.js mixes:
- Pure logic (testable without DOM): hashauth crypto, statistics calculations, data transforms, unit conversions
- DOM manipulation (requires browser simulation): jQuery selectors, d3 rendering, event handlers, UI state
This conflation forces all client tests to load the entire bundle in a simulated browser, even when testing pure functions.
Duration: 2 weeks
Risk: Low
Goal: Get API tests green, migrate hashauth with secure harness
- Update mocha from 8.4.0 to 10.x
- Update supertest from 3.4.2 to 7.x
- Update nyc from 14.1.1 to 17.x
- Formalize database test fixture bootstrap
- Migrate
hashauth.test.jsto locked-down jsdom harness (see Network Isolation below) - Document and skip remaining client tests with rationale
- Verify CI pipeline passes
- Green CI run covering all API suites
- hashauth tests passing with secure jsdom harness
- Catalog of skipped legacy UI tests with documented rationale
- Security posture for test harness documented
Duration: 3 weeks (starts after T1 stabilizes)
Risk: Medium
Goal: Extract pure logic for fast, DOM-free testing
lib/
├── client/ # Existing - DOM-coupled code
│ ├── index.js
│ ├── careportal.js
│ └── ...
├── client-core/ # NEW - Pure logic, no DOM deps
│ ├── hashauth.js # Crypto/auth logic only
│ ├── statistics.js # Report calculations
│ ├── transforms.js # Data transformations
│ ├── units.js # Unit conversions
│ └── index.js
└── server/ # Existing server code
- Inventory client bundle modules: classify as "pure logic" vs "DOM layer"
- Extract pure logic to
lib/client-core/with no DOM dependencies - Add Mocha unit tests for extracted logic (no jsdom needed)
- Create thin adapter wrappers for DOM code that calls into client-core
- Update webpack config to expose client-core separately if needed
- Document dependency map showing remaining DOM-coupled modules
-
lib/client-core/contains extracted pure logic - 80% of extracted logic covered by Node-based tests
- Documented dependency map of remaining DOM-coupled modules
- Guidelines published for new code placement
Before (DOM-coupled):
// lib/client/hashauth.js
var hashauth = {
init: function(client, $) {
// Mixes auth logic with jQuery DOM manipulation
$('#login-btn').click(function() {
var token = hashauth.computeToken(password);
// ...
});
},
computeToken: function(password) {
// Pure crypto logic
}
};After (separated):
// lib/client-core/hashauth.js - Pure logic, testable without DOM
module.exports = {
computeToken: function(password, salt) { /* ... */ },
verifyToken: function(token, expected) { /* ... */ },
generateSalt: function() { /* ... */ }
};
// lib/client/hashauth-ui.js - Thin DOM wrapper
var core = require('../client-core/hashauth');
module.exports = {
init: function(client, $) {
$('#login-btn').click(function() {
var token = core.computeToken(password, salt);
// ...
});
}
};Duration: 4 weeks (starts mid-T2)
Risk: Medium
Goal: Technology decision and migration roadmap
-
Persona-Driven UX Goals
- Define user personas (patient, caregiver, clinician)
- Document key workflows and pain points
- Establish accessibility requirements
-
Technology Decision Matrix
Criteria jQuery (retain) React Svelte Vue Bundle size ? ? ? ? Team familiarity ? ? ? ? Accessibility tooling ? ? ? ? Mobile support ? ? ? ? Migration effort ? ? ? ? -
Server-Side Statistics API Contracts
- Define endpoints for report statistics
- Specify response formats
- Document caching strategy
-
Narrator/Agent Interface Requirements
- Define interaction patterns for voice/agentic control
- Specify accessibility requirements
- Document state management needs
-
Incremental Migration Roadmap
- Feature flag strategy for coexisting UI shells
- Prioritized list of components to migrate
- Rollback procedures
- Technology decision made and documented
- API contracts for server-side statistics defined
- Migration roadmap approved by stakeholders
- Definition of "done" for UI modernization established
The test harness must prevent unintended network requests. This is critical for security-related tests like hashauth.
// tests/fixtures/secure-jsdom.js
const { JSDOM, ResourceLoader } = require('jsdom');
class NoNetworkLoader extends ResourceLoader {
fetch(url) {
console.error(`BLOCKED: Attempted network request to ${url}`);
return Promise.reject(new Error(`Network requests disabled: ${url}`));
}
}
function createSecureDOM(html, options = {}) {
const dom = new JSDOM(html || '<!DOCTYPE html><html><body></body></html>', {
url: 'http://localhost',
resources: new NoNetworkLoader(),
runScripts: options.runScripts || 'outside-only',
pretendToBeVisual: true,
...options
});
// Block fetch API
dom.window.fetch = () => {
throw new Error('fetch() is disabled in tests');
};
// Block XMLHttpRequest
dom.window.XMLHttpRequest = class {
open() {}
send() { throw new Error('XMLHttpRequest is disabled in tests'); }
};
return dom;
}
module.exports = { createSecureDOM, NoNetworkLoader };const { createSecureDOM } = require('./fixtures/secure-jsdom');
describe('hashauth', function() {
let dom;
before(function() {
dom = createSecureDOM();
global.window = dom.window;
global.document = dom.window.document;
});
after(function() {
dom.window.close();
});
it('computes token correctly', function() {
// Test pure logic without network concerns
});
});-
Milestone Exit Reviews
- Each track requires stakeholder sign-off before proceeding
- Exit criteria must be met, not just "good enough"
-
Out-of-Scope Log
- Maintain explicit list of deferred items per milestone
- Review and reprioritize at each exit review
-
Change Control
- New UI feature ideas defer until Discovery completes
- No new UI module without corresponding test strategy
- Breaking changes require explicit approval
| In Scope | Out of Scope (for now) |
|---|---|
| API test updates | MongoDB driver upgrade |
| hashauth test migration | Full client test migration |
| Logic/DOM separation | Complete UI rewrite |
| UI Discovery process | UI implementation |
| Statistics API contracts | Statistics API implementation |
Track 1 (Testing Foundation)
│
└──► Track 2 (Logic/DOM Separation) ──► Enables fast pure-logic tests
│
└──► Track 3 (UI Discovery) ──► Informs technology choice
│
└──► Future: UI Implementation (separate proposal)
{
"devDependencies": {
"mocha": "^10.7.0",
"supertest": "^7.0.0",
"nyc": "^17.1.0"
}
}{
"dependencies": {
"jsdom": "^24.0.0"
}
}Note: benv removed; direct jsdom usage with secure harness.
- Jest migration (not needed with unified Mocha approach)
- Playwright (revisit after UI stabilizes)
{
"scripts": {
"test": "npm run test:api",
"test:api": "env-cmd -f ./my.test.env mocha --timeout 5000 --exit ./tests/*.test.js",
"test:core": "mocha --timeout 5000 ./tests/client-core/**/*.test.js",
"test:ci": "env-cmd -f ./tests/ci.test.env nyc --reporter=lcov mocha --timeout 5000 --exit ./tests/*.test.js",
"test:all": "npm run test:api && npm run test:core"
}
}| Track | Risk | Mitigation |
|---|---|---|
| T1 | Dependency updates break tests | Run incrementally, fix as needed |
| T1 | jsdom network isolation incomplete | Use NoNetworkLoader + override fetch/XHR |
| T2 | Difficult to separate logic from DOM | Start with clear wins (hashauth, statistics) |
| T2 | Breaks existing functionality | Maintain adapters, run existing tests |
| T3 | Scope creep during discovery | Strict exit criteria, out-of-scope log |
| T3 | Technology decision paralysis | Time-boxed evaluation, decision deadline |
- All API tests pass with updated dependencies
- hashauth tests pass with secure jsdom harness
- CI pipeline green
- Test execution under 5 minutes
-
lib/client-core/established with extracted modules - 80% coverage on extracted pure logic
- No regressions in existing functionality
- Clear guidelines for new code placement
- Technology decision documented
- Statistics API contracts defined
- Migration roadmap approved
- Stakeholder buy-in achieved
{
"devDependencies": {
"@types/tough-cookie": "^4.0.0",
"axios": "^0.21.1",
"babel-eslint": "^10.1.0",
"benv": "^3.3.0",
"csv-parse": "^4.12.0",
"env-cmd": "^10.1.0",
"eslint": "^7.19.0",
"eslint-plugin-security": "^1.4.0",
"eslint-webpack-plugin": "^2.7.0",
"mocha": "^8.4.0",
"nodemon": "^2.0.19",
"nyc": "^14.1.1",
"should": "^13.2.3",
"supertest": "^3.4.2",
"webpack-bundle-analyzer": "^4.5.0",
"webpack-dev-middleware": "^4.3.0",
"webpack-hot-middleware": "^2.25.2",
"xml2js": "^0.4.23"
},
"dependencies": {
"jsdom": "=11.11.0"
}
}Use this template during Track 2 to classify modules:
| Module | Type | DOM Dependencies | Extraction Complexity | Priority |
|---|---|---|---|---|
| hashauth.js | Mixed | jQuery, localStorage | Low | High |
| statistics.js | Pure | None | Low | High |
| careportal.js | DOM-heavy | jQuery, d3 | High | Low |
| ... |
For Track 3 Discovery phase:
-
Performance
- Initial bundle size
- Runtime performance
- Mobile device support
-
Developer Experience
- Learning curve for team
- Tooling quality
- Documentation
-
Accessibility
- ARIA support
- Screen reader compatibility
- Keyboard navigation
-
Migration Path
- Incremental adoption possible?
- jQuery interop
- Estimated effort
-
Long-term Viability
- Community size
- Corporate backing
- Release cadence
| Date | Version | Changes |
|---|---|---|
| Jan 2026 | 1.0 | Initial draft |
| Jan 2026 | 2.0 | Revised based on stakeholder interviews; three-track approach; added Logic/DOM separation; added UI Discovery track; added network isolation requirements; added scope guardrails |