156 lines
4.0 KiB
JavaScript
156 lines
4.0 KiB
JavaScript
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest'
|
|
import { startAutoLock, stopAutoLock } from '../../../src/lib/stores/security.svelte.js'
|
|
import { app } from '../../../src/lib/stores/app.svelte.js'
|
|
import { settings } from '../../../src/lib/stores/settings.svelte.js'
|
|
|
|
// Reset singleton state before each test
|
|
function resetState() {
|
|
app.isUnlocked = false
|
|
app.encryptionKey = null
|
|
app.salt = null
|
|
settings.autoLockMinutes = 5
|
|
settings.lockOnTabSwitch = true
|
|
}
|
|
|
|
beforeEach(() => {
|
|
resetState()
|
|
stopAutoLock()
|
|
})
|
|
|
|
afterEach(() => {
|
|
stopAutoLock()
|
|
resetState()
|
|
})
|
|
|
|
describe('startAutoLock / stopAutoLock', () => {
|
|
it('should register and clean up activity listeners', () => {
|
|
// startAutoLock registers event listeners
|
|
startAutoLock()
|
|
|
|
// stopAutoLock should remove them
|
|
stopAutoLock()
|
|
|
|
// No error means cleanup worked
|
|
expect(true).toBe(true)
|
|
})
|
|
|
|
it('should register and clean up visibility listener', () => {
|
|
startAutoLock()
|
|
stopAutoLock()
|
|
expect(true).toBe(true)
|
|
})
|
|
|
|
it('should register and clean up beforeunload listener', () => {
|
|
startAutoLock()
|
|
stopAutoLock()
|
|
expect(true).toBe(true)
|
|
})
|
|
|
|
it('should not throw when called multiple times', () => {
|
|
startAutoLock()
|
|
startAutoLock()
|
|
stopAutoLock()
|
|
stopAutoLock()
|
|
expect(true).toBe(true)
|
|
})
|
|
})
|
|
|
|
describe('auto-lock behavior', () => {
|
|
it('should lock vault after inactivity timeout', async () => {
|
|
vi.useFakeTimers()
|
|
|
|
settings.autoLockMinutes = 1
|
|
app.isUnlocked = true
|
|
app.encryptionKey = { mock: 'key' }
|
|
|
|
startAutoLock()
|
|
|
|
// Wait for the timer (1 minute = 60000ms)
|
|
vi.advanceTimersByTime(60000)
|
|
|
|
expect(app.isUnlocked).toBe(false)
|
|
expect(app.encryptionKey).toBe(null)
|
|
|
|
vi.useRealTimers()
|
|
})
|
|
|
|
it('should lock on tab switch when lockOnTabSwitch is true', async () => {
|
|
app.isUnlocked = true
|
|
app.encryptionKey = { mock: 'key' }
|
|
settings.lockOnTabSwitch = true
|
|
|
|
startAutoLock()
|
|
|
|
// Simulate visibility change
|
|
Object.defineProperty(document, 'hidden', { value: true, writable: true, configurable: true })
|
|
document.dispatchEvent(new Event('visibilitychange'))
|
|
|
|
expect(app.isUnlocked).toBe(false)
|
|
expect(app.encryptionKey).toBe(null)
|
|
})
|
|
|
|
it('should not lock on tab switch when lockOnTabSwitch is false', async () => {
|
|
app.isUnlocked = true
|
|
app.encryptionKey = { mock: 'key' }
|
|
settings.lockOnTabSwitch = false
|
|
|
|
startAutoLock()
|
|
|
|
Object.defineProperty(document, 'hidden', { value: true, writable: true, configurable: true })
|
|
document.dispatchEvent(new Event('visibilitychange'))
|
|
|
|
expect(app.isUnlocked).toBe(true)
|
|
expect(app.encryptionKey).toEqual({ mock: 'key' })
|
|
})
|
|
|
|
it('should clear key on beforeunload', async () => {
|
|
app.isUnlocked = true
|
|
app.encryptionKey = { mock: 'key' }
|
|
|
|
startAutoLock()
|
|
|
|
window.dispatchEvent(new Event('beforeunload'))
|
|
|
|
expect(app.encryptionKey).toBe(null)
|
|
})
|
|
|
|
it('should reset timer on user activity', async () => {
|
|
vi.useFakeTimers()
|
|
|
|
settings.autoLockMinutes = 1
|
|
app.isUnlocked = true
|
|
app.encryptionKey = { mock: 'key' }
|
|
|
|
startAutoLock()
|
|
|
|
// Tick 45 seconds — not enough to trigger
|
|
vi.advanceTimersByTime(45000)
|
|
expect(app.isUnlocked).toBe(true)
|
|
|
|
// Simulate user activity (keydown resets timer)
|
|
window.dispatchEvent(new Event('keydown'))
|
|
|
|
// Tick another 45 seconds — should still be unlocked (timer was reset)
|
|
vi.advanceTimersByTime(45000)
|
|
expect(app.isUnlocked).toBe(true)
|
|
|
|
// Tick the remaining 15+ seconds
|
|
vi.advanceTimersByTime(20000)
|
|
expect(app.isUnlocked).toBe(false)
|
|
|
|
vi.useRealTimers()
|
|
})
|
|
|
|
it('should not lock when already locked', async () => {
|
|
app.isUnlocked = false
|
|
settings.lockOnTabSwitch = true
|
|
|
|
startAutoLock()
|
|
|
|
Object.defineProperty(document, 'hidden', { value: true, writable: true, configurable: true })
|
|
document.dispatchEvent(new Event('visibilitychange'))
|
|
|
|
expect(app.isUnlocked).toBe(false)
|
|
})
|
|
})
|