Add core domain layer: schema, store, sanitizer, placeholders, settings
- Folder-based GuideStore with atomic writes, trash/restore, duplicate, substep reparenting, folders/favorites, working-image crop/reset - Allowlist HTML sanitizer applied on every store write - Placeholder scopes (guide > global > system) and collection - Persisted app settings with deep default merge - 16 workflow tests exercising real on-disk round-trips Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,63 @@
|
||||
'use strict';
|
||||
|
||||
const test = require('node:test');
|
||||
const assert = require('node:assert/strict');
|
||||
|
||||
const {
|
||||
systemPlaceholders, resolveScopes, expandPlaceholders,
|
||||
listPlaceholders, collectGuidePlaceholders,
|
||||
} = require('../../core/placeholders');
|
||||
const { createGuide, createStep } = require('../../core/schema');
|
||||
const { Settings, DEFAULT_SETTINGS } = require('../../core/settings');
|
||||
const { makeTmpDir, rmrf } = require('./helpers');
|
||||
|
||||
test('placeholder expansion respects guide > global > system precedence', () => {
|
||||
const guide = createGuide({ title: 'Install VPN', placeholders: { Author: 'Alice' } });
|
||||
const system = systemPlaceholders(guide, { now: new Date(2026, 5, 10, 9, 5), stepCount: 4 });
|
||||
const values = resolveScopes({ guide, globals: { Author: 'Bob', Company: 'Acme' }, system });
|
||||
|
||||
const out = expandPlaceholders(
|
||||
'Guide [[Guide_Title]] by [[Author]] at [[Company]] on [[Date]] ([[Step_Count]] steps)',
|
||||
values
|
||||
);
|
||||
assert.equal(out, 'Guide Install VPN by Alice at Acme on 2026-06-10 (4 steps)');
|
||||
});
|
||||
|
||||
test('unknown placeholders stay visible instead of disappearing', () => {
|
||||
const out = expandPlaceholders('Hello [[Nobody]] and [[Author]]', { Author: 'A' });
|
||||
assert.equal(out, 'Hello [[Nobody]] and A');
|
||||
});
|
||||
|
||||
test('placeholders used across a guide are collected from every surface', () => {
|
||||
const guide = createGuide({ title: '[[Product]] setup', descriptionHtml: '<p>[[Company]]</p>' });
|
||||
const steps = [
|
||||
createStep({ title: 'Login as [[Author]]' }),
|
||||
createStep({
|
||||
textBlocks: [{ title: 'Warning [[Severity]]', descriptionHtml: '<p>[[Company]]</p>' }],
|
||||
annotations: [{ type: 'text', x: 0, y: 0, w: 0.1, h: 0.1, text: 'See [[Doc_Ref]]' }],
|
||||
}),
|
||||
];
|
||||
assert.deepEqual(
|
||||
collectGuidePlaceholders(guide, steps),
|
||||
['Author', 'Company', 'Doc_Ref', 'Product', 'Severity']
|
||||
);
|
||||
assert.deepEqual(listPlaceholders('no tokens here'), []);
|
||||
});
|
||||
|
||||
test('settings persist, deep-merge with defaults, and store global placeholders', (t) => {
|
||||
const dir = makeTmpDir('settings');
|
||||
t.after(() => rmrf(dir));
|
||||
|
||||
const s1 = new Settings(dir);
|
||||
assert.equal(s1.get('appearance'), DEFAULT_SETTINGS.appearance);
|
||||
s1.set('appearance', 'dark');
|
||||
s1.set('capture.delayMs', 1500);
|
||||
s1.setGlobalPlaceholders({ Company: 'Acme', Author: 'Tyler' });
|
||||
|
||||
// A fresh instance reads back the changed values merged over defaults.
|
||||
const s2 = new Settings(dir);
|
||||
assert.equal(s2.get('appearance'), 'dark');
|
||||
assert.equal(s2.get('capture.delayMs'), 1500);
|
||||
assert.equal(s2.get('capture.clickMarker'), DEFAULT_SETTINGS.capture.clickMarker);
|
||||
assert.deepEqual(s2.getGlobalPlaceholders(), { Company: 'Acme', Author: 'Tyler' });
|
||||
});
|
||||
Reference in New Issue
Block a user