kopia lustrzana https://github.com/Tldraw/Tldraw
179 wiersze
4.3 KiB
TypeScript
179 wiersze
4.3 KiB
TypeScript
import { BaseRecord, IdOf, RecordId } from '../BaseRecord'
|
|
import { createRecordType } from '../RecordType'
|
|
import { SerializedStore, Store } from '../Store'
|
|
import { StoreSchema } from '../StoreSchema'
|
|
|
|
interface Book extends BaseRecord<'book', RecordId<Book>> {
|
|
title: string
|
|
author: IdOf<Author>
|
|
numPages: number
|
|
}
|
|
|
|
const Book = createRecordType<Book>('book', {
|
|
validator: {
|
|
validate(value) {
|
|
const book = value as Book
|
|
if (!book.id.startsWith('book:')) throw Error()
|
|
if (book.typeName !== 'book') throw Error()
|
|
if (typeof book.title !== 'string') throw Error()
|
|
if (!Number.isFinite(book.numPages)) throw Error()
|
|
if (book.numPages < 0) throw Error()
|
|
return book
|
|
},
|
|
},
|
|
scope: 'document',
|
|
})
|
|
|
|
interface Author extends BaseRecord<'author', RecordId<Author>> {
|
|
name: string
|
|
isPseudonym: boolean
|
|
}
|
|
|
|
const Author = createRecordType<Author>('author', {
|
|
validator: {
|
|
validate(value) {
|
|
const author = value as Author
|
|
if (author.typeName !== 'author') throw Error()
|
|
if (!author.id.startsWith('author:')) throw Error()
|
|
if (typeof author.name !== 'string') throw Error()
|
|
if (typeof author.isPseudonym !== 'boolean') throw Error()
|
|
return author
|
|
},
|
|
},
|
|
scope: 'document',
|
|
}).withDefaultProperties(() => ({
|
|
isPseudonym: false,
|
|
}))
|
|
|
|
const schema = StoreSchema.create<Book | Author>({
|
|
book: Book,
|
|
author: Author,
|
|
})
|
|
|
|
describe('Store with validation', () => {
|
|
let store: Store<Book | Author>
|
|
|
|
beforeEach(() => {
|
|
store = new Store({ schema, props: {} })
|
|
})
|
|
|
|
it('Accepts valid records and rejects invalid records', () => {
|
|
store.put([Author.create({ name: 'J.R.R Tolkein', id: Author.createId('tolkein') })])
|
|
|
|
expect(store.query.records('author').get()).toEqual([
|
|
{ id: 'author:tolkein', typeName: 'author', name: 'J.R.R Tolkein', isPseudonym: false },
|
|
])
|
|
|
|
expect(() => {
|
|
store.put([
|
|
{
|
|
id: Book.createId('the-hobbit'),
|
|
typeName: 'book',
|
|
title: 'The Hobbit',
|
|
numPages: -1, // <---- Invalid!
|
|
author: Author.createId('tolkein'),
|
|
},
|
|
])
|
|
}).toThrow()
|
|
|
|
expect(store.query.records('book').get()).toEqual([])
|
|
})
|
|
})
|
|
|
|
describe('Validating initial data', () => {
|
|
let snapshot: SerializedStore<Book | Author>
|
|
|
|
beforeEach(() => {
|
|
const authorId = Author.createId('tolkein')
|
|
const authorRecord = Author.create({ name: 'J.R.R Tolkein', id: authorId })
|
|
const bookId = Book.createId('the-hobbit')
|
|
const bookRecord = Book.create({
|
|
title: 'The Hobbit',
|
|
numPages: 300,
|
|
author: authorId,
|
|
id: bookId,
|
|
})
|
|
|
|
snapshot = {
|
|
[authorId]: authorRecord,
|
|
[bookId]: bookRecord,
|
|
}
|
|
})
|
|
|
|
it('Validates initial data', () => {
|
|
expect(() => {
|
|
new Store<Book | Author>({ schema, initialData: snapshot, props: {} })
|
|
}).not.toThrow()
|
|
|
|
expect(() => {
|
|
// @ts-expect-error
|
|
snapshot[0].name = 4
|
|
|
|
new Store<Book | Author>({ schema, initialData: snapshot, props: {} })
|
|
}).toThrow()
|
|
})
|
|
})
|
|
|
|
describe('Create & update validations', () => {
|
|
const authorId = Author.createId('tolkein')
|
|
const bookId = Book.createId('the-hobbit')
|
|
const initialAuthor = Author.create({ name: 'J.R.R Tolkein', id: authorId })
|
|
const invalidBook = Book.create({
|
|
// @ts-expect-error - deliberately invalid data
|
|
title: 4,
|
|
numPages: 300,
|
|
author: authorId,
|
|
id: bookId,
|
|
})
|
|
const validBook = Book.create({
|
|
title: 'The Hobbit',
|
|
numPages: 300,
|
|
author: authorId,
|
|
id: bookId,
|
|
})
|
|
|
|
it('Prevents creating a store with invalid records', () => {
|
|
expect(
|
|
() =>
|
|
new Store<Book | Author>({
|
|
schema,
|
|
initialData: { [bookId]: invalidBook, [authorId]: initialAuthor },
|
|
props: {},
|
|
})
|
|
).toThrow()
|
|
})
|
|
|
|
it('Prevents updating invalid records to a store', () => {
|
|
const store = new Store<Book | Author>({
|
|
schema,
|
|
initialData: { [bookId]: validBook, [authorId]: initialAuthor },
|
|
props: {},
|
|
})
|
|
|
|
expect(() => {
|
|
store.put([invalidBook])
|
|
}).toThrow()
|
|
})
|
|
|
|
it('Prevents adding invalid records to a store', () => {
|
|
const newAuthorId = Author.createId('shearing')
|
|
const store = new Store<Book | Author>({
|
|
schema,
|
|
initialData: { [bookId]: validBook, [authorId]: initialAuthor },
|
|
props: {},
|
|
})
|
|
|
|
expect(() => {
|
|
store.put([
|
|
Author.create({
|
|
// @ts-expect-error - deliberately invalid data
|
|
name: 5,
|
|
id: newAuthorId,
|
|
}),
|
|
])
|
|
}).toThrow()
|
|
|
|
expect(store.get(newAuthorId)).toBeUndefined()
|
|
})
|
|
})
|