muya 2.1.1 → 2.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/cjs/index.js +1 -1
- package/esm/create-state.js +1 -1
- package/esm/create.js +1 -1
- package/esm/scheduler.js +1 -1
- package/esm/select.js +1 -1
- package/esm/sqlite/__tests__/create-sqlite.test.js +1 -0
- package/esm/sqlite/__tests__/map-deque.test.js +1 -0
- package/esm/sqlite/__tests__/table.test.js +1 -0
- package/esm/sqlite/__tests__/use-sqlite.test.js +1 -0
- package/esm/sqlite/create-sqlite.js +1 -0
- package/esm/sqlite/select-sql.js +1 -0
- package/esm/sqlite/table/backend.js +1 -0
- package/esm/sqlite/table/bun-backend.js +1 -0
- package/esm/sqlite/table/map-deque.js +1 -0
- package/esm/sqlite/table/table.js +10 -0
- package/esm/sqlite/table/table.types.js +0 -0
- package/esm/sqlite/table/where.js +1 -0
- package/esm/sqlite/use-sqlite.js +1 -0
- package/esm/utils/common.js +1 -1
- package/package.json +1 -1
- package/src/__tests__/scheduler.test.tsx +2 -2
- package/src/create-state.ts +3 -2
- package/src/create.ts +22 -24
- package/src/scheduler.ts +15 -7
- package/src/select.ts +15 -17
- package/src/sqlite/__tests__/create-sqlite.test.ts +81 -0
- package/src/sqlite/__tests__/map-deque.test.ts +61 -0
- package/src/sqlite/__tests__/table.test.ts +142 -0
- package/src/sqlite/__tests__/use-sqlite.test.ts +274 -0
- package/src/sqlite/create-sqlite.ts +273 -0
- package/src/sqlite/select-sql.ts +55 -0
- package/src/sqlite/table/backend.ts +21 -0
- package/src/sqlite/table/bun-backend.ts +38 -0
- package/src/sqlite/table/map-deque.ts +29 -0
- package/src/sqlite/table/table.ts +200 -0
- package/src/sqlite/table/table.types.ts +55 -0
- package/src/sqlite/table/where.ts +267 -0
- package/src/sqlite/use-sqlite.ts +70 -0
- package/src/types.ts +1 -0
- package/src/utils/common.ts +6 -2
- package/types/create.d.ts +3 -3
- package/types/scheduler.d.ts +12 -3
- package/types/sqlite/create-sqlite.d.ts +28 -0
- package/types/sqlite/select-sql.d.ts +14 -0
- package/types/sqlite/table/backend.d.ts +20 -0
- package/types/sqlite/table/bun-backend.d.ts +2 -0
- package/types/sqlite/table/map-deque.d.ts +5 -0
- package/types/sqlite/table/table.d.ts +3 -0
- package/types/sqlite/table/table.types.d.ts +52 -0
- package/types/sqlite/table/where.d.ts +32 -0
- package/types/sqlite/use-sqlite.d.ts +15 -0
- package/types/types.d.ts +1 -0
- package/types/utils/common.d.ts +2 -2
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { MapDeque } from '../table/map-deque'
|
|
2
|
+
|
|
3
|
+
describe('MapDeque', () => {
|
|
4
|
+
it('should throw if maxSize <= 0', () => {
|
|
5
|
+
expect(() => new MapDeque(0)).toThrow(RangeError)
|
|
6
|
+
expect(() => new MapDeque(-1)).toThrow(RangeError)
|
|
7
|
+
})
|
|
8
|
+
|
|
9
|
+
it('should add items up to maxSize', () => {
|
|
10
|
+
const deque = new MapDeque<string, number>(2)
|
|
11
|
+
deque.set('a', 1)
|
|
12
|
+
deque.set('b', 2)
|
|
13
|
+
expect(deque.size).toBe(2)
|
|
14
|
+
expect(deque.get('a')).toBe(1)
|
|
15
|
+
expect(deque.get('b')).toBe(2)
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
it('should evict the oldest item when maxSize is exceeded', () => {
|
|
19
|
+
const deque = new MapDeque<string, number>(2)
|
|
20
|
+
deque.set('a', 1)
|
|
21
|
+
deque.set('b', 2)
|
|
22
|
+
deque.set('c', 3)
|
|
23
|
+
expect(deque.size).toBe(2)
|
|
24
|
+
expect(deque.has('a')).toBe(false)
|
|
25
|
+
expect(deque.get('b')).toBe(2)
|
|
26
|
+
expect(deque.get('c')).toBe(3)
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
it('should update value if key already exists and not evict', () => {
|
|
30
|
+
const deque = new MapDeque<string, number>(2)
|
|
31
|
+
deque.set('a', 1)
|
|
32
|
+
deque.set('b', 2)
|
|
33
|
+
// eslint-disable-next-line sonarjs/no-element-overwrite
|
|
34
|
+
deque.set('a', 42)
|
|
35
|
+
expect(deque.size).toBe(2)
|
|
36
|
+
expect(deque.get('a')).toBe(42)
|
|
37
|
+
expect(deque.get('b')).toBe(2)
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
it('should work with initial entries', () => {
|
|
41
|
+
const entries: Array<[string, number]> = [
|
|
42
|
+
['x', 10],
|
|
43
|
+
['y', 20],
|
|
44
|
+
]
|
|
45
|
+
const deque = new MapDeque<string, number>(3, entries)
|
|
46
|
+
expect(deque.size).toBe(2)
|
|
47
|
+
expect(deque.get('x')).toBe(10)
|
|
48
|
+
expect(deque.get('y')).toBe(20)
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
it('should evict in insertion order, not key order', () => {
|
|
52
|
+
const deque = new MapDeque<string, number>(2)
|
|
53
|
+
deque.set('b', 1)
|
|
54
|
+
deque.set('a', 2)
|
|
55
|
+
deque.set('c', 3)
|
|
56
|
+
expect(deque.size).toBe(2)
|
|
57
|
+
expect(deque.has('b')).toBe(false)
|
|
58
|
+
expect(deque.has('a')).toBe(true)
|
|
59
|
+
expect(deque.has('c')).toBe(true)
|
|
60
|
+
})
|
|
61
|
+
})
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-shadow */
|
|
2
|
+
/* eslint-disable no-shadow */
|
|
3
|
+
/* eslint-disable sonarjs/pseudo-random */
|
|
4
|
+
// /* eslint-disable sonarjs/no-unused-vars */
|
|
5
|
+
// /* eslint-disable unicorn/prevent-abbreviations */
|
|
6
|
+
|
|
7
|
+
import { bunMemoryBackend } from '../table/bun-backend'
|
|
8
|
+
import { createTable } from '../table/table'
|
|
9
|
+
|
|
10
|
+
interface Person {
|
|
11
|
+
name: string
|
|
12
|
+
age: number
|
|
13
|
+
city: string
|
|
14
|
+
}
|
|
15
|
+
describe('table', () => {
|
|
16
|
+
let backend = bunMemoryBackend()
|
|
17
|
+
let table: ReturnType<typeof createTable<Person>> extends Promise<infer T> ? T : never
|
|
18
|
+
|
|
19
|
+
beforeEach(async () => {
|
|
20
|
+
backend = bunMemoryBackend()
|
|
21
|
+
table = await createTable<Person>({
|
|
22
|
+
backend,
|
|
23
|
+
tableName: 'TestTable',
|
|
24
|
+
key: 'name',
|
|
25
|
+
})
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
it('should set and get items', async () => {
|
|
29
|
+
const mutation = await table.set({ name: 'Alice', age: 30, city: 'Paris' })
|
|
30
|
+
expect(mutation.key).toBe('Alice')
|
|
31
|
+
expect(mutation.op).toBe('insert')
|
|
32
|
+
const result = await table.get('Alice')
|
|
33
|
+
expect(result).toEqual({ name: 'Alice', age: 30, city: 'Paris' })
|
|
34
|
+
|
|
35
|
+
const updateMutation = await table.set({ name: 'Alice', age: 31, city: 'Paris' })
|
|
36
|
+
expect(updateMutation.key).toBe('Alice')
|
|
37
|
+
expect(updateMutation.op).toBe('update')
|
|
38
|
+
const updatedResult = await table.get('Alice')
|
|
39
|
+
expect(updatedResult).toEqual({ name: 'Alice', age: 31, city: 'Paris' })
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
it('should count items and count with where', async () => {
|
|
43
|
+
await table.set({ name: 'Alice', age: 30, city: 'Paris' })
|
|
44
|
+
await table.set({ name: 'Bob', age: 25, city: 'London' })
|
|
45
|
+
expect(await table.count()).toBe(2)
|
|
46
|
+
expect(await table.count({ where: { city: 'Paris' } })).toBe(1)
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
it('should search with ordering, limit and offset', async () => {
|
|
50
|
+
const people: Person[] = [
|
|
51
|
+
{ name: 'Alice', age: 30, city: 'Paris' },
|
|
52
|
+
{ name: 'Bob', age: 25, city: 'London' },
|
|
53
|
+
{ name: 'Carol', age: 35, city: 'Berlin' },
|
|
54
|
+
]
|
|
55
|
+
for (const p of people) {
|
|
56
|
+
await table.set(p)
|
|
57
|
+
}
|
|
58
|
+
// sort by age ascending
|
|
59
|
+
const asc = [] as Person[]
|
|
60
|
+
for await (const p of table.search({ sorBy: 'age', order: 'asc' })) asc.push(p)
|
|
61
|
+
expect(asc.map((p) => p.name)).toEqual(['Bob', 'Alice', 'Carol'])
|
|
62
|
+
// limit and offset
|
|
63
|
+
const limited = [] as Person[]
|
|
64
|
+
for await (const p of table.search({ sorBy: 'age', order: 'asc', limit: 2 })) limited.push(p)
|
|
65
|
+
expect(limited.map((p) => p.name)).toEqual(['Bob', 'Alice'])
|
|
66
|
+
const offsetted = [] as Person[]
|
|
67
|
+
for await (const p of table.search({ sorBy: 'age', order: 'asc', offset: 1, limit: 2 })) offsetted.push(p)
|
|
68
|
+
expect(offsetted.map((p) => p.name)).toEqual(['Alice', 'Carol'])
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
it('should deleteBy where clause', async () => {
|
|
72
|
+
await table.set({ name: 'Dave', age: 40, city: 'NY' })
|
|
73
|
+
await table.set({ name: 'Eve', age: 45, city: 'NY' })
|
|
74
|
+
await table.set({ name: 'Frank', age: 50, city: 'LA' })
|
|
75
|
+
expect(await table.count()).toBe(3)
|
|
76
|
+
await table.deleteBy({ city: 'NY' })
|
|
77
|
+
expect(await table.count()).toBe(1)
|
|
78
|
+
expect(await table.get('Frank')).toEqual({ name: 'Frank', age: 50, city: 'LA' })
|
|
79
|
+
expect(await table.get('Dave')).toBeUndefined()
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
it('should use selector in get and search', async () => {
|
|
83
|
+
await table.set({ name: 'Gary', age: 60, city: 'SF' })
|
|
84
|
+
// selector in get
|
|
85
|
+
const ageOnly = await table.get('Gary', ({ age }) => age)
|
|
86
|
+
expect(ageOnly).toBe(60)
|
|
87
|
+
// selector in search
|
|
88
|
+
const cities: string[] = []
|
|
89
|
+
for await (const city of table.search({ select: ({ city }) => city })) cities.push(city)
|
|
90
|
+
expect(cities).toEqual(['SF'])
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
it('should delete items by key', async () => {
|
|
94
|
+
await table.set({ name: 'Helen', age: 28, city: 'Rome' })
|
|
95
|
+
expect(await table.get('Helen')).toBeDefined()
|
|
96
|
+
await table.delete('Helen')
|
|
97
|
+
expect(await table.get('Helen')).toBeUndefined()
|
|
98
|
+
})
|
|
99
|
+
it('should test search with 1000 items', async () => {
|
|
100
|
+
const people: Person[] = []
|
|
101
|
+
for (let index = 0; index < 1000; index++) {
|
|
102
|
+
people.push({
|
|
103
|
+
name: `Person${index}`,
|
|
104
|
+
age: Math.floor(Math.random() * 100),
|
|
105
|
+
city: 'City' + (index % 10),
|
|
106
|
+
})
|
|
107
|
+
}
|
|
108
|
+
for (const p of people) {
|
|
109
|
+
await table.set(p)
|
|
110
|
+
}
|
|
111
|
+
const results: Person[] = []
|
|
112
|
+
for await (const person of table.search({ sorBy: 'age', order: 'asc', limit: 100 })) {
|
|
113
|
+
results.push(person)
|
|
114
|
+
}
|
|
115
|
+
expect(results.length).toBe(100)
|
|
116
|
+
})
|
|
117
|
+
|
|
118
|
+
it('should handle operations on an empty table', async () => {
|
|
119
|
+
expect(await table.count()).toBe(0)
|
|
120
|
+
expect(await table.get('NonExistent')).toBeUndefined()
|
|
121
|
+
const results: Person[] = []
|
|
122
|
+
for await (const person of table.search({ sorBy: 'age', order: 'asc' })) {
|
|
123
|
+
results.push(person)
|
|
124
|
+
}
|
|
125
|
+
expect(results.length).toBe(0)
|
|
126
|
+
})
|
|
127
|
+
|
|
128
|
+
it('should handle duplicate keys gracefully', async () => {
|
|
129
|
+
await table.set({ name: 'Alice', age: 30, city: 'Paris' })
|
|
130
|
+
await table.set({ name: 'Alice', age: 35, city: 'Berlin' })
|
|
131
|
+
const result = await table.get('Alice')
|
|
132
|
+
expect(result).toEqual({ name: 'Alice', age: 35, city: 'Berlin' })
|
|
133
|
+
})
|
|
134
|
+
|
|
135
|
+
it('should handle edge cases in selectors', async () => {
|
|
136
|
+
await table.set({ name: 'Charlie', age: 40, city: 'NY' })
|
|
137
|
+
const nullSelector = await table.get('Charlie', () => null)
|
|
138
|
+
expect(nullSelector).toBeNull()
|
|
139
|
+
const undefinedSelector = await table.get('Charlie', () => void 0)
|
|
140
|
+
expect(undefinedSelector).toBeUndefined()
|
|
141
|
+
})
|
|
142
|
+
})
|
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
import { act, renderHook } from '@testing-library/react-hooks'
|
|
2
|
+
import { createSqliteState } from '../create-sqlite'
|
|
3
|
+
import { useSqliteValue } from '../use-sqlite'
|
|
4
|
+
import { waitFor } from '@testing-library/react'
|
|
5
|
+
import { bunMemoryBackend } from '../table/bun-backend'
|
|
6
|
+
import { useState } from 'react'
|
|
7
|
+
import { DEFAULT_STEP_SIZE } from '../table/table'
|
|
8
|
+
|
|
9
|
+
const backend = bunMemoryBackend()
|
|
10
|
+
interface Person {
|
|
11
|
+
id: string
|
|
12
|
+
name: string
|
|
13
|
+
age: number
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
describe('use-sqlite-state', () => {
|
|
17
|
+
it('should get basic value states', async () => {
|
|
18
|
+
const sql = createSqliteState<Person>({ backend, tableName: 'State1', key: 'id' })
|
|
19
|
+
let reRenders = 0
|
|
20
|
+
const { result } = renderHook(() => {
|
|
21
|
+
reRenders++
|
|
22
|
+
return useSqliteValue(sql, {}, [])
|
|
23
|
+
})
|
|
24
|
+
// expect(result.current).toEqual([])
|
|
25
|
+
|
|
26
|
+
expect(reRenders).toBe(1)
|
|
27
|
+
|
|
28
|
+
act(() => {
|
|
29
|
+
sql.set({ id: '1', name: 'Alice', age: 30 })
|
|
30
|
+
})
|
|
31
|
+
await waitFor(() => {
|
|
32
|
+
expect(result.current[0]).toEqual([{ id: '1', name: 'Alice', age: 30 }])
|
|
33
|
+
expect(reRenders).toBe(3)
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
act(() => {
|
|
37
|
+
sql.set({ id: '1', name: 'Alice2', age: 30 })
|
|
38
|
+
})
|
|
39
|
+
await waitFor(() => {
|
|
40
|
+
expect(result.current[0]).toEqual([{ id: '1', name: 'Alice2', age: 30 }])
|
|
41
|
+
expect(reRenders).toBe(4)
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
// delete item
|
|
45
|
+
act(() => {
|
|
46
|
+
sql.delete('1')
|
|
47
|
+
})
|
|
48
|
+
await waitFor(() => {
|
|
49
|
+
expect(result.current[0]).toEqual([])
|
|
50
|
+
expect(reRenders).toBe(5)
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
// add two items
|
|
54
|
+
act(() => {
|
|
55
|
+
sql.set({ id: '1', name: 'Alice', age: 30 })
|
|
56
|
+
sql.set({ id: '2', name: 'Bob', age: 25 })
|
|
57
|
+
})
|
|
58
|
+
await waitFor(() => {
|
|
59
|
+
expect(result.current[0].length).toBe(2)
|
|
60
|
+
expect(reRenders).toBe(6)
|
|
61
|
+
})
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
it('should use where clause changed via state', async () => {
|
|
65
|
+
const sql = createSqliteState<Person>({ backend, tableName: 'State2', key: 'id' })
|
|
66
|
+
await sql.batchSet([
|
|
67
|
+
{ id: '1', name: 'Alice', age: 30 },
|
|
68
|
+
{ id: '2', name: 'Bob', age: 25 },
|
|
69
|
+
{ id: '3', name: 'Carol', age: 40 },
|
|
70
|
+
])
|
|
71
|
+
let reRenders = 0
|
|
72
|
+
const { result } = renderHook(() => {
|
|
73
|
+
reRenders++
|
|
74
|
+
const [minAge, setMinAge] = useState(20)
|
|
75
|
+
return [useSqliteValue(sql, { where: { age: { gt: minAge } }, sorBy: 'age' }, [minAge]), setMinAge] as const
|
|
76
|
+
})
|
|
77
|
+
|
|
78
|
+
await waitFor(() => {
|
|
79
|
+
expect(result.current[0][0].map((p) => p.name)).toEqual(['Bob', 'Alice', 'Carol'])
|
|
80
|
+
expect(reRenders).toBe(2)
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
// // change minAge to 29
|
|
84
|
+
act(() => {
|
|
85
|
+
result.current[1](29)
|
|
86
|
+
})
|
|
87
|
+
await waitFor(() => {
|
|
88
|
+
expect(result.current[0][0].map((p) => p.name)).toEqual(['Alice', 'Carol'])
|
|
89
|
+
expect(reRenders).toBe(4)
|
|
90
|
+
})
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
it('should support like in where clause and update results', async () => {
|
|
94
|
+
const sql = createSqliteState<Person>({ backend, tableName: 'State3', key: 'id' })
|
|
95
|
+
await sql.batchSet([
|
|
96
|
+
{ id: '1', name: 'Alice', age: 30 },
|
|
97
|
+
{ id: '2', name: 'Alicia', age: 25 },
|
|
98
|
+
{ id: '3', name: 'Bob', age: 40 },
|
|
99
|
+
])
|
|
100
|
+
let reRenders = 0
|
|
101
|
+
const { result, rerender } = renderHook(
|
|
102
|
+
({ like }) => {
|
|
103
|
+
reRenders++
|
|
104
|
+
return useSqliteValue(sql, { where: { name: { like } } }, [like])
|
|
105
|
+
},
|
|
106
|
+
{ initialProps: { like: '%Ali%' } },
|
|
107
|
+
)
|
|
108
|
+
await waitFor(() => {
|
|
109
|
+
expect(result.current[0].map((p) => p.name)).toEqual(['Alice', 'Alicia'])
|
|
110
|
+
})
|
|
111
|
+
act(() => {
|
|
112
|
+
rerender({ like: '%Bob%' })
|
|
113
|
+
})
|
|
114
|
+
await waitFor(() => {
|
|
115
|
+
expect(result.current[0].map((p) => p.name)).toEqual(['Bob'])
|
|
116
|
+
})
|
|
117
|
+
expect(reRenders).toBeGreaterThanOrEqual(2)
|
|
118
|
+
})
|
|
119
|
+
|
|
120
|
+
it('should update results when changing order and limit options', async () => {
|
|
121
|
+
const sql = createSqliteState<Person>({ backend, tableName: 'State4', key: 'id' })
|
|
122
|
+
await sql.batchSet([
|
|
123
|
+
{ id: '1', name: 'Alice', age: 30 },
|
|
124
|
+
{ id: '2', name: 'Bob', age: 25 },
|
|
125
|
+
{ id: '3', name: 'Carol', age: 40 },
|
|
126
|
+
])
|
|
127
|
+
const { result, rerender } = renderHook(
|
|
128
|
+
({ order, limit }) => useSqliteValue(sql, { sorBy: 'age', order, limit }, [order, limit]),
|
|
129
|
+
{ initialProps: { order: 'asc' as 'asc' | 'desc', limit: 2 } },
|
|
130
|
+
)
|
|
131
|
+
await waitFor(() => {
|
|
132
|
+
expect(result.current[0].map((p) => p.name)).toEqual(['Bob', 'Alice'])
|
|
133
|
+
})
|
|
134
|
+
act(() => {
|
|
135
|
+
rerender({ order: 'desc', limit: 2 })
|
|
136
|
+
})
|
|
137
|
+
await waitFor(() => {
|
|
138
|
+
expect(result.current[0].map((p) => p.name)).toEqual(['Carol', 'Alice'])
|
|
139
|
+
})
|
|
140
|
+
act(() => {
|
|
141
|
+
rerender({ order: 'desc', limit: 1 })
|
|
142
|
+
})
|
|
143
|
+
await waitFor(() => {
|
|
144
|
+
expect(result.current[0].map((p) => p.name)).toEqual(['Carol'])
|
|
145
|
+
})
|
|
146
|
+
})
|
|
147
|
+
|
|
148
|
+
it('should support actions.next and actions.refresh', async () => {
|
|
149
|
+
const sql = createSqliteState<Person>({ backend, tableName: 'State5', key: 'id' })
|
|
150
|
+
await sql.batchSet([
|
|
151
|
+
{ id: '1', name: 'Alice', age: 30 },
|
|
152
|
+
{ id: '2', name: 'Bob', age: 25 },
|
|
153
|
+
])
|
|
154
|
+
const { result } = renderHook(() => useSqliteValue(sql, {}, []))
|
|
155
|
+
// actions.next and actions.refresh should be functions
|
|
156
|
+
await waitFor(() => {
|
|
157
|
+
expect(typeof result.current[1].next).toBe('function')
|
|
158
|
+
expect(typeof result.current[1].reset).toBe('function')
|
|
159
|
+
expect(result.current[1].reset()).resolves.toBeUndefined()
|
|
160
|
+
expect(result.current[1].next()).resolves.toBeFalsy()
|
|
161
|
+
})
|
|
162
|
+
})
|
|
163
|
+
it('should handle thousands of records', async () => {
|
|
164
|
+
const sql = createSqliteState<Person>({ backend, tableName: 'State6', key: 'id' })
|
|
165
|
+
const people: Person[] = []
|
|
166
|
+
const ITEMS_COUNT = 1000
|
|
167
|
+
for (let index = 1; index <= ITEMS_COUNT; index++) {
|
|
168
|
+
people.push({ id: index.toString(), name: `Person${index}`, age: 20 + (index % 50) })
|
|
169
|
+
}
|
|
170
|
+
await sql.batchSet(people)
|
|
171
|
+
const { result } = renderHook(() => useSqliteValue(sql, {}, []))
|
|
172
|
+
await waitFor(() => {
|
|
173
|
+
expect(result.current[0].length).toBe(DEFAULT_STEP_SIZE)
|
|
174
|
+
})
|
|
175
|
+
|
|
176
|
+
// loop until we have all ITEMS_COUNT items
|
|
177
|
+
for (let index = 0; index < ITEMS_COUNT / DEFAULT_STEP_SIZE; index++) {
|
|
178
|
+
act(() => {
|
|
179
|
+
result.current[1].next()
|
|
180
|
+
})
|
|
181
|
+
await waitFor(() => {
|
|
182
|
+
expect(result.current[0].length).toBe(Math.min(DEFAULT_STEP_SIZE * (index + 2), ITEMS_COUNT))
|
|
183
|
+
})
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
act(() => {
|
|
187
|
+
result.current[1].reset()
|
|
188
|
+
})
|
|
189
|
+
await waitFor(() => {
|
|
190
|
+
expect(result.current[0].length).toBe(DEFAULT_STEP_SIZE)
|
|
191
|
+
})
|
|
192
|
+
})
|
|
193
|
+
|
|
194
|
+
it('should handle thousands of records with single update', async () => {
|
|
195
|
+
const sql = createSqliteState<Person>({ backend, tableName: 'State6', key: 'id' })
|
|
196
|
+
const people: Person[] = []
|
|
197
|
+
const ITEMS_COUNT = 10_000
|
|
198
|
+
const stepSize = 5000
|
|
199
|
+
for (let index = 1; index <= ITEMS_COUNT; index++) {
|
|
200
|
+
people.push({ id: index.toString(), name: `Person${index}`, age: 20 + (index % 50) })
|
|
201
|
+
}
|
|
202
|
+
await sql.batchSet(people)
|
|
203
|
+
let reRenders = 0
|
|
204
|
+
const { result } = renderHook(() => {
|
|
205
|
+
reRenders++
|
|
206
|
+
return useSqliteValue(sql, { stepSize }, [])
|
|
207
|
+
})
|
|
208
|
+
await waitFor(() => {
|
|
209
|
+
expect(reRenders).toBe(2)
|
|
210
|
+
expect(result.current[0].length).toBe(stepSize)
|
|
211
|
+
})
|
|
212
|
+
|
|
213
|
+
act(() => {
|
|
214
|
+
for (let index = 0; index < ITEMS_COUNT / stepSize; index++) {
|
|
215
|
+
result.current[1].next()
|
|
216
|
+
}
|
|
217
|
+
})
|
|
218
|
+
|
|
219
|
+
await waitFor(() => {
|
|
220
|
+
expect(reRenders).toBe(4)
|
|
221
|
+
expect(result.current[0].length).toBe(ITEMS_COUNT)
|
|
222
|
+
})
|
|
223
|
+
|
|
224
|
+
act(() => {
|
|
225
|
+
result.current[1].reset()
|
|
226
|
+
})
|
|
227
|
+
await waitFor(() => {
|
|
228
|
+
expect(reRenders).toBe(5)
|
|
229
|
+
expect(result.current[0].length).toBe(stepSize)
|
|
230
|
+
})
|
|
231
|
+
})
|
|
232
|
+
it('should change ordering', async () => {
|
|
233
|
+
const sql = createSqliteState<Person>({ backend, tableName: 'State7', key: 'id', indexes: ['age'] })
|
|
234
|
+
const people: Person[] = []
|
|
235
|
+
for (let index = 1; index <= 100; index++) {
|
|
236
|
+
people.push({ id: index.toString(), name: `Person${index}`, age: 20 + (index % 50) })
|
|
237
|
+
}
|
|
238
|
+
await sql.batchSet(people)
|
|
239
|
+
const { result, rerender } = renderHook(({ order }) => useSqliteValue(sql, { sorBy: 'age', order }, [order]), {
|
|
240
|
+
initialProps: { order: 'asc' as 'asc' | 'desc' },
|
|
241
|
+
})
|
|
242
|
+
await waitFor(() => {
|
|
243
|
+
expect(result.current[0][0].age).toBe(20)
|
|
244
|
+
})
|
|
245
|
+
act(() => {
|
|
246
|
+
rerender({ order: 'desc' })
|
|
247
|
+
})
|
|
248
|
+
await waitFor(() => {
|
|
249
|
+
expect(result.current[0][0].age).toBe(69)
|
|
250
|
+
})
|
|
251
|
+
})
|
|
252
|
+
|
|
253
|
+
it('should support selector in options', async () => {
|
|
254
|
+
const sql = createSqliteState<Person>({ backend, tableName: 'State8', key: 'id' })
|
|
255
|
+
await sql.batchSet([
|
|
256
|
+
{ id: '1', name: 'Alice', age: 30 },
|
|
257
|
+
{ id: '2', name: 'Bob', age: 25 },
|
|
258
|
+
{ id: '3', name: 'Carol', age: 40 },
|
|
259
|
+
])
|
|
260
|
+
const { result } = renderHook(() =>
|
|
261
|
+
useSqliteValue(
|
|
262
|
+
sql,
|
|
263
|
+
{
|
|
264
|
+
sorBy: 'age',
|
|
265
|
+
select: (d) => d.name,
|
|
266
|
+
},
|
|
267
|
+
[],
|
|
268
|
+
),
|
|
269
|
+
)
|
|
270
|
+
await waitFor(() => {
|
|
271
|
+
expect(result.current[0]).toEqual(['Bob', 'Alice', 'Carol'])
|
|
272
|
+
})
|
|
273
|
+
})
|
|
274
|
+
})
|