feat: add configurable Magic Notes and polish UI
Cross-platform packages / Validate source (push) Has been cancelled
Cross-platform packages / ${{ matrix.platform }} ${{ matrix.arch }} (arm64, linux, ubuntu-24.04-arm) (push) Has been cancelled
Cross-platform packages / ${{ matrix.platform }} ${{ matrix.arch }} (arm64, macos, macos-15) (push) Has been cancelled
Cross-platform packages / ${{ matrix.platform }} ${{ matrix.arch }} (arm64, windows, windows-2025) (push) Has been cancelled
Cross-platform packages / ${{ matrix.platform }} ${{ matrix.arch }} (x64, linux, ubuntu-24.04) (push) Has been cancelled
Cross-platform packages / ${{ matrix.platform }} ${{ matrix.arch }} (x64, macos, macos-15-intel) (push) Has been cancelled
Cross-platform packages / ${{ matrix.platform }} ${{ matrix.arch }} (x64, windows, windows-2025) (push) Has been cancelled
Cross-platform packages / Publish GitHub Release (push) Has been cancelled

This commit is contained in:
lofyer
2026-08-10 00:24:19 +08:00
parent 1cc969317d
commit b249df116a
29 changed files with 2440 additions and 296 deletions
+117 -23
View File
@@ -11,6 +11,7 @@ import { afterEach, describe, expect, it } from 'vitest'
import {
ApplicationSettingsStore,
applicationSettingsSchema,
applicationSettingsUpdateSchema,
defaultApplicationSettings
} from './application-settings-store'
@@ -51,18 +52,26 @@ describe('ApplicationSettingsStore', () => {
await expect(readdir(directory)).resolves.toEqual([])
})
it('persists only the versioned startup update preference', async () => {
it('persists versioned application preferences', async () => {
const { directory, filePath, store } = await createStore()
await expect(
store.update({ checkUpdatesOnStartup: false })
).resolves.toEqual({ checkUpdatesOnStartup: false })
store.update({
checkUpdatesOnStartup: false,
magicNotesEnabled: false
})
).resolves.toEqual({
checkUpdatesOnStartup: false,
magicNotesEnabled: false
})
await expect(store.get()).resolves.toEqual({
checkUpdatesOnStartup: false
checkUpdatesOnStartup: false,
magicNotesEnabled: false
})
expect(JSON.parse(await readFile(filePath, 'utf8'))).toEqual({
version: 1,
checkUpdatesOnStartup: false
version: 2,
checkUpdatesOnStartup: false,
magicNotesEnabled: false
})
expect(
(await readdir(directory)).filter((name) => name.endsWith('.tmp'))
@@ -73,38 +82,103 @@ describe('ApplicationSettingsStore', () => {
const { directory } = await createStore()
const filePath = join(directory, 'nested', 'application-settings.json')
const store = new ApplicationSettingsStore(filePath)
await store.update({ checkUpdatesOnStartup: false })
await store.update({
checkUpdatesOnStartup: false,
magicNotesEnabled: false
})
await expect(
new ApplicationSettingsStore(filePath).get()
).resolves.toEqual({ checkUpdatesOnStartup: false })
).resolves.toEqual({
checkUpdatesOnStartup: false,
magicNotesEnabled: false
})
})
it('strictly rejects unknown, missing, and mistyped input', async () => {
const { directory, store } = await createStore()
it.each([1, 2])(
'loads version %s settings missing the field with Magic Notes disabled',
async (version) => {
const { filePath, store } = await createStore()
await writeFile(
filePath,
JSON.stringify({
version,
checkUpdatesOnStartup: false
}),
'utf8'
)
await expect(store.get()).resolves.toEqual({
checkUpdatesOnStartup: false,
magicNotesEnabled: false
})
}
)
it('strictly rejects incomplete full settings', () => {
for (const input of [
{},
{ checkUpdatesOnStartup: 'true' },
{ checkUpdatesOnStartup: true, anotherSetting: true },
{
checkUpdatesOnStartup: true,
magicNotesEnabled: true,
anotherSetting: true
},
{ checkUpdatesOnStartup: true },
null
]) {
expect(applicationSettingsSchema.safeParse(input).success).toBe(
false
)
}
})
it('strictly rejects empty, unknown, and mistyped updates', async () => {
const { directory, store } = await createStore()
for (const input of [
{},
{ checkUpdatesOnStartup: 'true' },
{ anotherSetting: true },
null
]) {
expect(
applicationSettingsUpdateSchema.safeParse(input).success
).toBe(false)
await expect(store.update(input)).rejects.toThrow()
}
await expect(readdir(directory)).resolves.toEqual([])
})
it('merges partial updates without overwriting other settings', async () => {
const { store } = await createStore()
await store.update({ magicNotesEnabled: true })
await expect(
store.update({ checkUpdatesOnStartup: false })
).resolves.toEqual({
checkUpdatesOnStartup: false,
magicNotesEnabled: true
})
})
it.each([
'{not-json',
JSON.stringify({ version: 2, checkUpdatesOnStartup: false }),
JSON.stringify({
version: 1,
version: 3,
checkUpdatesOnStartup: false,
magicNotesEnabled: false
}),
JSON.stringify({
version: 2,
checkUpdatesOnStartup: false,
magicNotesEnabled: true,
injected: true
}),
JSON.stringify({ version: 1, checkUpdatesOnStartup: 'false' })
JSON.stringify({
version: 2,
checkUpdatesOnStartup: 'false',
magicNotesEnabled: true
})
])('isolates corrupt persisted data and restores defaults', async (data) => {
const { directory, filePath, store } = await createStore()
await writeFile(filePath, data, 'utf8')
@@ -142,28 +216,48 @@ describe('ApplicationSettingsStore', () => {
const { filePath, store } = await createStore()
await Promise.all([
store.update({ checkUpdatesOnStartup: false }),
store.update({ checkUpdatesOnStartup: true }),
store.update({ checkUpdatesOnStartup: false })
store.update({
checkUpdatesOnStartup: false,
magicNotesEnabled: true
}),
store.update({
checkUpdatesOnStartup: true,
magicNotesEnabled: false
}),
store.update({
checkUpdatesOnStartup: false,
magicNotesEnabled: false
})
])
await expect(store.get()).resolves.toEqual({
checkUpdatesOnStartup: false
checkUpdatesOnStartup: false,
magicNotesEnabled: false
})
expect(JSON.parse(await readFile(filePath, 'utf8'))).toEqual({
version: 1,
checkUpdatesOnStartup: false
version: 2,
checkUpdatesOnStartup: false,
magicNotesEnabled: false
})
})
it('continues accepting updates after a validation failure', async () => {
const { store } = await createStore()
await expect(
store.update({ checkUpdatesOnStartup: 'invalid' })
store.update({
checkUpdatesOnStartup: 'invalid',
magicNotesEnabled: true
})
).rejects.toThrow()
await expect(
store.update({ checkUpdatesOnStartup: false })
).resolves.toEqual({ checkUpdatesOnStartup: false })
store.update({
checkUpdatesOnStartup: false,
magicNotesEnabled: true
})
).resolves.toEqual({
checkUpdatesOnStartup: false,
magicNotesEnabled: true
})
})
})