v0.16.3
This commit is contained in:
1 parent
f376f0a7b6
commit
8059e16d12
36 files changed
+1140
-193
No files matched your search
@@ -0,0 +1,95 @@
|
||||
// Verify contextual help for the constant and room heating Flow blocks.
|
||||
// Does not need a browser or a running HVAC controller.
|
||||
const assert = require('node:assert/strict');
|
||||
const fs = require('node:fs');
|
||||
const vm = require('node:vm');
|
||||
const path = require('node:path');
|
||||
const root = path.resolve(__dirname, '..');
|
||||
const translations = {};
|
||||
for (const language of ['pl', 'en']) {
|
||||
translations[language] = JSON.parse(fs.readFileSync(path.join(root, 'lang', `${language}.json`), 'utf8')).translations;
|
||||
}
|
||||
function localize(language, key, params = {}) {
|
||||
const result = translations[language][key];
|
||||
assert.ok(result, `missing translation ${language}:${key}`);
|
||||
return String(result).replace(/\{([a-zA-Z0-9_]+)\}/g, (_, name) => params[name] ?? `{${name}}`);
|
||||
}
|
||||
const app = {
|
||||
zones: [{ id: 'zone1', name: 'Pokój' }],
|
||||
devices: [], groups: [], flowDraft: null, flowSelectedNodeId: null,
|
||||
};
|
||||
const host = {
|
||||
innerHTML: '',
|
||||
expandedNode: null,
|
||||
classList: { toggle() {}, remove() {} },
|
||||
querySelector(selector) {
|
||||
return selector === '.flow-block-help' && this.expandedNode
|
||||
? { open: true, dataset: { flowHelpNode: this.expandedNode } } : null;
|
||||
},
|
||||
};
|
||||
const ctx = vm.createContext({
|
||||
app,
|
||||
tr: (key, params) => localize(ctx.language, key, params),
|
||||
language: 'pl',
|
||||
esc: value => String(value).replace(/&/g, '&').replace(/</g, '<').replace(/"/g, '"'),
|
||||
fmtTemp: value => `${Number(value).toFixed(1)}°C`,
|
||||
uiIcon: () => '',
|
||||
$: selector => selector === '#flowInspector' ? host : null,
|
||||
document: { addEventListener() {} }, window: { addEventListener() {} },
|
||||
});
|
||||
vm.runInContext(fs.readFileSync(path.join(root, 'web/js-dynamic/flows.js'), 'utf8'), ctx);
|
||||
const meta = vm.runInContext('FLOW_NODE_META', ctx);
|
||||
assert.equal(meta.constant.descriptionKey, 'flow.constantDescription');
|
||||
assert.equal(meta.zone_heat_cycle.descriptionKey, 'flow.simpleHeatDescription');
|
||||
|
||||
for (const language of ['pl', 'en']) {
|
||||
ctx.language = language;
|
||||
for (const file of fs.readdirSync(path.join(root, 'flows/simple-heating'))) {
|
||||
if (!file.endsWith('.flow.json')) continue;
|
||||
const json = JSON.parse(fs.readFileSync(path.join(root, 'flows/simple-heating', file), 'utf8'));
|
||||
const setting = json.flow.nodes.find(node => node.kind === 'zone_heat_cycle').config;
|
||||
const text = ctx.flowSimpleHeatHelp(setting, 'room', false);
|
||||
const night = setting.night_target;
|
||||
assert.ok(text.includes(`${night.toFixed(1)}°C`), `${language} ${file} missing room target`);
|
||||
assert.ok(text.includes(`${(night - 0.8).toFixed(1)}°C`), `${language} ${file} missing boost threshold`);
|
||||
assert.ok(text.includes(`${Math.floor(night - 1).toFixed(1)}°C`), `${language} ${file} missing standby`);
|
||||
assert.ok(text.includes(`${Math.floor(night) + 1}.0°C`), `${language} ${file} missing gentle heat`);
|
||||
assert.ok(text.includes(`${Math.ceil(night + 2)}.0°C`), `${language} ${file} missing boost`);
|
||||
assert.ok(text.includes(`${setting.day_min.toFixed(1)}°C`), `${language} ${file} missing daytime minimum`);
|
||||
assert.ok(text.includes('21.0°C') && text.includes('22.0°C'), `${language} ${file} missing daytime setpoints`);
|
||||
assert.ok(text.includes('19.2°C'), `${language} ${file} missing daytime immediate boost threshold`);
|
||||
assert.ok(text.includes(String(setting.day_off_after_minutes)), `${language} ${file} missing off timer`);
|
||||
assert.ok(text.includes(String(setting.boost_after_minutes)), `${language} ${file} missing boost timer`);
|
||||
assert.ok(text.includes(localize(language, 'flow.simpleHeatHelpSensors')));
|
||||
}
|
||||
assert.ok(ctx.flowSimpleHeatHelp({ night_target: 22.4, day_target: 21.3, day_min: 20.6, boost_after_minutes: 12, day_off_after_minutes: 45 }).includes('25.0°C'), 'help must recalculate for edited targets');
|
||||
}
|
||||
ctx.language = 'pl';
|
||||
function inspect(kind, config) {
|
||||
app.flowDraft = { nodes: [{ id: 'selected', kind, config }], edges: [] };
|
||||
app.flowSelectedNodeId = 'selected';
|
||||
ctx.renderFlowInspector();
|
||||
return host.innerHTML;
|
||||
}
|
||||
const constantHtml = inspect('constant', { value: true });
|
||||
assert.ok(constantHtml.includes(localize('pl', 'flow.constantDescription')));
|
||||
assert.ok(constantHtml.includes(localize('pl', 'flow.constantHelp')));
|
||||
assert.ok(constantHtml.includes('data-flow-config="value"'));
|
||||
const heat = { zone_id: 'zone1', day_target: 20, night_target: 20.6, day_min: 19.7, boost_after_minutes: 10, day_off_after_minutes: 30 };
|
||||
const simpleHtml = inspect('zone_heat_cycle', heat);
|
||||
assert.ok(simpleHtml.includes(localize('pl', 'flow.simpleHeatDescription')));
|
||||
assert.ok(simpleHtml.includes('<details class="flow-block-help"'));
|
||||
assert.ok(simpleHtml.includes(localize('pl', 'flow.simpleHeatHowItWorks')));
|
||||
assert.ok(simpleHtml.includes('19.0°C') && simpleHtml.includes('23.0°C'));
|
||||
assert.ok(simpleHtml.includes('data-flow-config="night_target"'));
|
||||
host.expandedNode = 'selected';
|
||||
assert.ok(inspect('zone_heat_cycle', heat).includes('data-flow-help-node="selected" open>'), 'expanded help should stay open when a user edits settings');
|
||||
const htmlFile = fs.readFileSync(path.join(root, 'web/index.html'), 'utf8');
|
||||
assert.ok(htmlFile.includes('data-i18n-title="flow.constantDescription"'));
|
||||
assert.ok(htmlFile.includes('data-i18n-title="flow.simpleHeatDescription"'));
|
||||
console.log('OK: 2 blocks, 3 rooms, PL/EN tips, dynamic temperature examples, inspector and expanded-state behavior');
|
||||
if (process.env.FLOW_HELP_PREVIEW_HTML) {
|
||||
host.expandedNode = 'selected';
|
||||
const content = inspect('zone_heat_cycle', heat);
|
||||
fs.writeFileSync(process.env.FLOW_HELP_PREVIEW_HTML, `<!doctype html><html lang="pl"><head><meta charset="utf-8"><link rel="stylesheet" href="file://${path.join(root, 'web/css/styles.css')}"><style>body{margin:0;padding:28px;background:var(--bg)} .flow-inspector{width:370px;max-height:none;box-sizing:border-box;border:1px solid var(--line-strong);border-radius:14px} .flow-inspector-head button{font-size:11px;padding:7px}</style></head><body><aside class="flow-inspector">${content}</aside></body></html>`);
|
||||
}
|
||||
Reference in new issue
Block a user