// Adding a model in the Models card has to reach every picker that lists models. // It used to refresh the default-model dropdown alone, so a newly added model // was invisible in the Clinical Assistant, review-model and image pickers until // the page was reloaded — which is not obvious, and looks like the add failed. const test = require('node:test'); const assert = require('node:assert/strict'); const fs = require('node:fs'); const path = require('node:path'); const read = f => fs.readFileSync(path.join(__dirname, '..', f), 'utf8'); test('every mutation of the model roster announces it', () => { const admin = read('public/js/admin.js'); assert.match(admin, /function announceModelsChanged\(\)/); assert.match(admin, /new CustomEvent\('models-changed'\)/); // Add, remove, clear-all and enable/disable each change what is selectable. const announced = (admin.match(/announceModelsChanged\(\);/g) || []).length; assert.ok(announced >= 4, 'expected an announcement on add, remove, clear and toggle; found ' + announced); for (const [label, marker] of [ ['add-discovered', "config/models/add-discovered"], ['remove custom', "config/models/custom/"], ['clear all', "config/models/clear-all"], ['toggle', "config/models/toggle"] ]) { const at = admin.indexOf(marker); assert.ok(at > -1, label + ' call site missing'); // The announcement lives inside that call's success branch. assert.ok(admin.slice(at, at + 900).includes('announceModelsChanged()'), label + ' does not announce the change'); } }); test('the pickers listen, and re-run a loader that is otherwise once-per-visit', () => { const assistant = read('public/js/admin/clinicalAssistant.js'); assert.match(assistant, /document\.addEventListener\('models-changed'/); // Its guard must be cleared, or the listener fires and the loader returns early. assert.match(assistant, /configState = 'idle';\s*\n\s*loadAssistantAdmin\(\);/); assert.match(assistant, /if \(configState === 'loading'\) return;/, 'no reload mid-flight'); const images = read('public/js/admin/imageSettings.js'); assert.match(images, /document\.addEventListener\('models-changed'/); assert.match(images, /loaded = false;\s*\n\s*load\(\);/); assert.match(images, /if \(loading\) return;/, 'no reload mid-flight'); }); test('every model picker listens, not only the two in the admin cards', () => { // The point is that a model added in Admin is selectable wherever models are // chosen — the per-tab selectors and My Resources included, not just the // admin's own dropdowns. const app = read('public/js/app.js'); assert.match(app, /document\.addEventListener\('models-changed'/); assert.match(app, /function loadModelList\(\)/, 'the boot fetch is reusable, not inline'); const resources = read('public/js/myResources.js'); assert.match(resources, /document\.addEventListener\('models-changed'/); assert.match(resources, /if \(inited\) loadOptions\(\);/, 'nothing to refresh before the tab has initialised'); }); test('a refresh never moves someone off the model they picked', () => { // These selects are rebuilt while a form may be half filled in. const app = read('public/js/app.js'); assert.match(app, /var chosen = sel\.value;[\s\S]{0,260}sel\.value = chosen;/); const resources = read('public/js/myResources.js'); assert.match(resources, /var chosen = select\.value;[\s\S]{0,420}select\.value = chosen;/); }); test('the toast no longer tells people to go and set a default', () => { // It used to say "now select it as default and click Set Default", which was // advice for the one dropdown that did refresh. const admin = read('public/js/admin.js'); assert.doesNotMatch(admin, /now select it as default and click Set Default/); assert.match(admin, /selectable everywhere models are chosen/); });