diff --git a/MMM-CalendarExtMiniMonth.js b/MMM-CalendarExtMiniMonth.js
index 1c19873..923a7db 100644
--- a/MMM-CalendarExtMiniMonth.js
+++ b/MMM-CalendarExtMiniMonth.js
@@ -179,6 +179,8 @@ Module.register('MMM-CalendarExtMiniMonth', {
}
return {
name: calendarName,
+ // Stored raw: never rendered via innerHTML today (see test/event-title-no-dom-sink.test.js).
+ // If a feature ever displays this, escape at the render sink, not here.
title: event.title,
startDate: startDate,
endDate: endDate,
diff --git a/package.json b/package.json
index f22003f..3804f75 100644
--- a/package.json
+++ b/package.json
@@ -9,7 +9,7 @@
"lint": "eslint && prettier . --check",
"lint:fix": "eslint --fix && prettier . --write",
"release": "commit-and-tag-version --config ./changelog.config.js",
- "test": "node --run lint"
+ "test": "node --run lint && node --test"
},
"repository": {
"type": "git",
diff --git a/test/event-title-no-dom-sink.test.js b/test/event-title-no-dom-sink.test.js
new file mode 100644
index 0000000..b792991
--- /dev/null
+++ b/test/event-title-no-dom-sink.test.js
@@ -0,0 +1,141 @@
+'use strict'
+
+const test = require('node:test')
+const assert = require('node:assert/strict')
+const path = require('node:path')
+
+function createFakeElement() {
+ return {
+ _id: '',
+ className: '',
+ style: {},
+ innerHTML: '',
+ children: [],
+ classList: {
+ add() {},
+ contains() {
+ return false
+ },
+ },
+ appendChild(child) {
+ this.children.push(child)
+ return child
+ },
+ get id() {
+ return this._id
+ },
+ set id(value) {
+ this._id = value
+ elementsById.set(value, this)
+ },
+ }
+}
+
+const elementsById = new Map()
+
+function serialize(el) {
+ let out = el.innerHTML || ''
+ for (const child of el.children) {
+ out += serialize(child)
+ }
+ return out
+}
+
+function loadModule() {
+ let captured = null
+ global.Module = {
+ register(name, definition) {
+ captured = definition
+ },
+ }
+ global.config = {}
+ global.document = {
+ createElement: createFakeElement,
+ getElementById(id) {
+ return elementsById.get(id) || null
+ },
+ body: createFakeElement(),
+ }
+
+ delete require.cache[path.join(__dirname, '..', 'MMM-CalendarExtMiniMonth.js')]
+ require('../MMM-CalendarExtMiniMonth.js')
+
+ const instance = Object.assign(Object.create(captured), captured)
+ instance.config = Object.assign({}, captured.defaults, { dynamicEventColor: null })
+ instance.start()
+ instance.updateDom = function () {}
+ instance.refreshScreen = function () {
+ this.updateDom()
+ }
+ return instance
+}
+
+function makeEvent(title) {
+ return {
+ calendarName: 'Test Calendar',
+ title: title,
+ startDate: Date.now(),
+ endDate: Date.now() + 60 * 60 * 1000,
+ }
+}
+
+test('normal event title is stored and rendered without error', () => {
+ const instance = loadModule()
+ instance.updateContentFromCalendarEvents([makeEvent('Team Meeting')])
+
+ assert.equal(instance.events.length, 1)
+ assert.equal(instance.events[0].title, 'Team Meeting')
+
+ const dom = instance.getDom()
+ assert.ok(dom.children.length > 0, 'expected rendered DOM to contain header/slot nodes')
+})
+
+test(''
+ instance.updateContentFromCalendarEvents([makeEvent(payload)])
+
+ assert.equal(instance.events[0].title, payload, 'title must be stored raw, not escaped/mangled')
+
+ const dom = instance.getDom()
+ const rendered = serialize(dom)
+ assert.ok(!rendered.includes('