feat(notes): add note model and storage layer
This commit is contained in:
1 parent
51584886b3
commit
c64c5d408c
3 files changed
+95
No files matched your search
@@ -0,0 +1,60 @@
|
||||
import { beforeEach, describe, expect, it } from 'vitest'
|
||||
import { loadNotes, saveNotes } from './notesStorage'
|
||||
import type { Note } from '../types'
|
||||
|
||||
const STORAGE_KEY = 'notes'
|
||||
|
||||
describe('notesStorage', () => {
|
||||
beforeEach(() => {
|
||||
localStorage.clear()
|
||||
})
|
||||
|
||||
it('loads an empty array when nothing is stored', () => {
|
||||
expect(loadNotes()).toEqual([])
|
||||
})
|
||||
|
||||
it('saves and loads notes', () => {
|
||||
const notes: Note[] = [
|
||||
{ id: 1, title: 'Courses', content: 'Lait, oeufs' },
|
||||
{ id: 2, title: 'Pense-bête', content: 'Rappeler le plombier' },
|
||||
]
|
||||
|
||||
saveNotes(notes)
|
||||
|
||||
expect(JSON.parse(localStorage.getItem(STORAGE_KEY)!)).toEqual(notes)
|
||||
expect(loadNotes()).toEqual(notes)
|
||||
})
|
||||
|
||||
it('returns an empty array when the stored JSON is invalid', () => {
|
||||
localStorage.setItem(STORAGE_KEY, 'not json {')
|
||||
expect(loadNotes()).toEqual([])
|
||||
})
|
||||
|
||||
it('returns an empty array when the stored value is not an array', () => {
|
||||
localStorage.setItem(STORAGE_KEY, JSON.stringify({ id: 1, title: 'x', content: 'y' }))
|
||||
expect(loadNotes()).toEqual([])
|
||||
})
|
||||
|
||||
it('filters out entries that do not match the Note shape', () => {
|
||||
localStorage.setItem(
|
||||
STORAGE_KEY,
|
||||
JSON.stringify([
|
||||
{ id: 1, title: 'ok', content: 'ok' },
|
||||
{ id: '2', title: 'bad id', content: 'x' },
|
||||
{ title: 'missing id', content: 'x' },
|
||||
'nope',
|
||||
]),
|
||||
)
|
||||
|
||||
expect(loadNotes()).toEqual([{ id: 1, title: 'ok', content: 'ok' }])
|
||||
})
|
||||
|
||||
it('overwrites previously stored notes', () => {
|
||||
saveNotes([{ id: 1, title: 'A', content: 'a' }])
|
||||
const updated: Note[] = [{ id: 2, title: 'B', content: 'b' }]
|
||||
|
||||
saveNotes(updated)
|
||||
|
||||
expect(loadNotes()).toEqual(updated)
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,30 @@
|
||||
import type { Note } from '../types'
|
||||
|
||||
const STORAGE_KEY = 'notes'
|
||||
|
||||
export function loadNotes(): Note[] {
|
||||
const raw = localStorage.getItem(STORAGE_KEY)
|
||||
if (raw === null) return []
|
||||
|
||||
try {
|
||||
const parsed: unknown = JSON.parse(raw)
|
||||
if (!Array.isArray(parsed)) return []
|
||||
return parsed.filter(isNote)
|
||||
} catch {
|
||||
return []
|
||||
}
|
||||
}
|
||||
|
||||
export function saveNotes(notes: Note[]): void {
|
||||
localStorage.setItem(STORAGE_KEY, JSON.stringify(notes))
|
||||
}
|
||||
|
||||
function isNote(value: unknown): value is Note {
|
||||
if (typeof value !== 'object' || value === null) return false
|
||||
const candidate = value as Record<string, unknown>
|
||||
return (
|
||||
typeof candidate.id === 'number' &&
|
||||
typeof candidate.title === 'string' &&
|
||||
typeof candidate.content === 'string'
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
export interface Note {
|
||||
id: number
|
||||
title: string
|
||||
content: string
|
||||
}
|
||||
Reference in new issue
Block a user