persist trim across sessions

This commit is contained in:
Boof2015
2026-04-08 23:06:21 -04:00
parent 27f4b9610a
commit 3ebd6619dc
4 changed files with 226 additions and 4 deletions
+119 -1
View File
@@ -1,10 +1,18 @@
import assert from 'node:assert/strict'
import test from 'node:test'
import { audioCapture } from '../src/renderer/audio/AudioCapture'
import { useAudioStore } from '../src/renderer/stores/audioStore'
import {
loadAudioPreferences,
normalizeAudioPreferences,
useAudioStore,
} from '../src/renderer/stores/audioStore'
import { useUiStore } from '../src/renderer/stores/uiStore'
import type { CaptureBackendSupport } from '../src/types/capture'
type GlobalWithStorage = typeof globalThis & {
localStorage?: Storage
}
const initialAudioState = {
...useAudioStore.getState(),
}
@@ -13,6 +21,54 @@ const initialUiState = {
...useUiStore.getState(),
}
function installFakeLocalStorage(): {
getSetCount: () => number
getItem: (key: string) => string | null
restore: () => void
} {
const storage = new Map<string, string>()
let setCount = 0
const globalWithStorage = globalThis as GlobalWithStorage
const previousLocalStorage = globalWithStorage.localStorage
globalWithStorage.localStorage = {
getItem(key: string): string | null {
return storage.get(key) ?? null
},
setItem(key: string, value: string): void {
setCount += 1
storage.set(key, value)
},
removeItem(key: string): void {
storage.delete(key)
},
clear(): void {
storage.clear()
},
key(index: number): string | null {
return [...storage.keys()][index] ?? null
},
get length(): number {
return storage.size
},
} as Storage
return {
getSetCount: () => setCount,
getItem(key: string): string | null {
return storage.get(key) ?? null
},
restore(): void {
if (previousLocalStorage === undefined) {
delete globalWithStorage.localStorage
return
}
globalWithStorage.localStorage = previousLocalStorage
},
}
}
function createBackendSupport(available: boolean, reason: string | null): CaptureBackendSupport {
return {
nativeBackend: {
@@ -29,6 +85,7 @@ function createBackendSupport(available: boolean, reason: string | null): Captur
}
function resetStores(): void {
audioCapture.setInputGain(0)
useAudioStore.setState({
...initialAudioState,
systemSources: [],
@@ -68,6 +125,7 @@ function installAudioCaptureHarness(options: {
setCaptureMode: audioCapture.setCaptureMode,
setSelectedDeviceId: audioCapture.setSelectedDeviceId,
setSelectedSystemSourceId: audioCapture.setSelectedSystemSourceId,
setInputGain: audioCapture.setInputGain,
}
const calls = {
@@ -132,6 +190,7 @@ function installAudioCaptureHarness(options: {
audioCapture.setCaptureMode = originalMethods.setCaptureMode
audioCapture.setSelectedDeviceId = originalMethods.setSelectedDeviceId
audioCapture.setSelectedSystemSourceId = originalMethods.setSelectedSystemSourceId
audioCapture.setInputGain = originalMethods.setInputGain
void selectedDeviceId
void selectedSystemSourceId
@@ -140,6 +199,65 @@ function installAudioCaptureHarness(options: {
}
}
test('loadAudioPreferences falls back to 0 dB when storage is unavailable', () => {
assert.deepEqual(loadAudioPreferences(null), { inputGainDb: 0 })
})
test('loadAudioPreferences falls back to 0 dB when stored JSON is invalid', () => {
const storage = {
getItem: () => '{not valid json',
setItem: () => {},
}
assert.deepEqual(loadAudioPreferences(storage), { inputGainDb: 0 })
})
test('normalizeAudioPreferences clamps out-of-range trim values', () => {
assert.deepEqual(normalizeAudioPreferences({ inputGainDb: -18 }), { inputGainDb: -12 })
assert.deepEqual(normalizeAudioPreferences({ inputGainDb: 18 }), { inputGainDb: 12 })
})
test('normalizeAudioPreferences rounds trim values to 0.5 dB steps', () => {
assert.deepEqual(normalizeAudioPreferences({ inputGainDb: 6.24 }), { inputGainDb: 6 })
assert.deepEqual(normalizeAudioPreferences({ inputGainDb: 6.26 }), { inputGainDb: 6.5 })
})
test('audio store persists normalized trim values and forwards them to audioCapture', () => {
resetStores()
const fakeStorage = installFakeLocalStorage()
const originalSetInputGain = audioCapture.setInputGain
const forwardedValues: number[] = []
audioCapture.setInputGain = (db: number) => {
forwardedValues.push(db)
}
try {
useAudioStore.getState().setInputGain(6.26)
assert.equal(useAudioStore.getState().inputGainDb, 6.5)
assert.deepEqual(forwardedValues, [6.5])
assert.equal(fakeStorage.getItem('prism:audio'), JSON.stringify({ inputGainDb: 6.5 }))
assert.equal(fakeStorage.getSetCount(), 1)
useAudioStore.getState().setInputGain(6.49)
assert.deepEqual(forwardedValues, [6.5])
assert.equal(fakeStorage.getSetCount(), 1)
useAudioStore.getState().setInputGain(0)
assert.equal(useAudioStore.getState().inputGainDb, 0)
assert.deepEqual(forwardedValues, [6.5, 0])
assert.equal(fakeStorage.getItem('prism:audio'), JSON.stringify({ inputGainDb: 0 }))
assert.equal(fakeStorage.getSetCount(), 2)
} finally {
audioCapture.setInputGain = originalSetInputGain
fakeStorage.restore()
resetStores()
}
})
test('audio store auto-switches to device input when native system capture is unavailable on startup', async () => {
resetStores()
const harness = installAudioCaptureHarness({