Files
session-notify/tests/unit/storage.test.js
T

103 lines
4.1 KiB
JavaScript

// The browser-local preferences: the three trigger switches and the popup
// lifetime. Storage is foreign input: it can be absent, hold another version's
// shape, or refuse to write, and none of that may stop the plugin — nor may
// writing one preference ever clear another.
import { afterEach, test } from 'node:test'
import assert from 'node:assert/strict'
import {
STORAGE_KEY,
readStoredDismissMs,
readStoredKinds,
writeStoredDismissMs,
writeStoredKinds,
} from '../../src/client/core/storage.js'
import { TOAST_DURATION_MS } from '../../src/client/constants.js'
/** Install a storage double, optionally one that throws. */
function useStorage(behaviour) {
const data = new Map()
globalThis.localStorage = {
getItem: (key) => {
if (behaviour === 'read-throws') throw new Error('denied')
return data.has(key) ? data.get(key) : null
},
setItem: (key, value) => {
if (behaviour === 'write-throws') throw new Error('quota')
data.set(key, String(value))
},
}
return data
}
afterEach(() => {
delete globalThis.localStorage
})
test('everything is on, with the shipped popup lifetime, when this browser has no stored choice', () => {
useStorage()
assert.deepEqual(readStoredKinds(), { completion: true, approval: true, question: true })
assert.equal(readStoredDismissMs(), TOAST_DURATION_MS)
})
test('without storage at all the defaults still apply', () => {
assert.deepEqual(readStoredKinds(), { completion: true, approval: true, question: true })
assert.equal(readStoredDismissMs(), TOAST_DURATION_MS)
assert.doesNotThrow(() => writeStoredKinds({ completion: false, approval: true, question: true }))
assert.doesNotThrow(() => writeStoredDismissMs(0))
})
test('a stored switch survives a round trip, and unknown or broken values do not', () => {
const data = useStorage()
writeStoredKinds({ completion: false, approval: true, question: false })
assert.deepEqual(readStoredKinds(), { completion: false, approval: true, question: false })
data.set(STORAGE_KEY, '{ not json')
assert.deepEqual(readStoredKinds(), { completion: true, approval: true, question: true })
data.set(STORAGE_KEY, JSON.stringify({ completion: 'yes', approval: false }))
assert.deepEqual(readStoredKinds(), { completion: true, approval: false, question: true })
data.set(STORAGE_KEY, JSON.stringify({ somethingElse: false }))
assert.deepEqual(readStoredKinds(), { completion: true, approval: true, question: true })
})
test('the popup lifetime survives a round trip, including "never"', () => {
const data = useStorage()
writeStoredDismissMs(30000)
assert.equal(readStoredDismissMs(), 30000)
writeStoredDismissMs(0)
assert.equal(readStoredDismissMs(), 0, 'zero means the card waits for the user')
// A value this version does not offer is ignored rather than trusted.
data.set(STORAGE_KEY, JSON.stringify({ dismissAfterMs: 1234 }))
assert.equal(readStoredDismissMs(), TOAST_DURATION_MS)
data.set(STORAGE_KEY, JSON.stringify({ dismissAfterMs: -1 }))
assert.equal(readStoredDismissMs(), TOAST_DURATION_MS)
data.set(STORAGE_KEY, JSON.stringify({ dismissAfterMs: '6000' }))
assert.equal(readStoredDismissMs(), TOAST_DURATION_MS)
})
test('writing one preference keeps every other one', () => {
useStorage()
writeStoredKinds({ completion: false, approval: false, question: false })
writeStoredDismissMs(0)
assert.deepEqual(readStoredKinds(), { completion: false, approval: false, question: false })
assert.equal(readStoredDismissMs(), 0)
// …and the other way round: the switches do not reset the lifetime.
writeStoredKinds({ completion: true, approval: true, question: true })
assert.equal(readStoredDismissMs(), 0)
})
test('storage that refuses to be read or written never throws', () => {
useStorage('read-throws')
assert.deepEqual(readStoredKinds(), { completion: true, approval: true, question: true })
assert.equal(readStoredDismissMs(), TOAST_DURATION_MS)
useStorage('write-throws')
assert.doesNotThrow(() => writeStoredKinds({ completion: false, approval: false, question: false }))
assert.doesNotThrow(() => writeStoredDismissMs(3000))
})