Security and quality
Test the store and validator
Move data rules into plain modules and test valid notes, malformed data, missing files, and round-trip persistence with Node.js.
12 minute lesson
The easiest Electron code to test is code that does not depend on Electron. This is why storage and validation live in plain modules.
Export validateNotes(), readNotes(), and writeNotes(). Write Node tests around temporary files:
const test = require('node:test')
const assert = require('node:assert/strict')
const fs = require('node:fs/promises')
const os = require('node:os')
const path = require('node:path')
test('notes survive a file round trip', async () => {
const folder = await fs.mkdtemp(path.join(os.tmpdir(), 'notes-'))
const file = path.join(folder, 'notes.json')
const notes = [{ id: '1', title: 'Trip', body: 'Pack light' }]
try {
await writeNotes(file, notes)
assert.deepEqual(await readNotes(file, validateNotes), notes)
} finally {
await fs.rm(folder, { recursive: true, force: true })
}
})
Add tests for a missing file, invalid JSON, more than 1,000 notes, an oversized title, and unexpected properties.
Assert the failure, not only that “something threw”:
assert.throws(
() => validateNotes([{ id: '1', title: 'x'.repeat(201), body: '' }]),
/Invalid note title/
)
Add a write-failure test with an invalid or unwritable destination appropriate to the test platform. Confirm the original notes file remains readable after a failed replacement.
Keep one manual checklist for window lifecycle, menu shortcuts, save dialog cancellation, and renderer error messages.
The remaining boundary needs an integration test through Electron: a renderer invokes IPC, main validates the sender and payload, and the bridge returns a plain result. Run that separately from fast Node tests.
Lesson completed