bt-core-app 1.1.0 → 1.1.2

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bt-core-app",
3
- "version": "1.1.0",
3
+ "version": "1.1.2",
4
4
  "description": "Core app tools for some basic features like navigation, authentication, server apis, and cosmetics",
5
5
  "homepage": "https://github.com/BlitzItTech/bt-core",
6
6
  "bugs": {
@@ -0,0 +1,84 @@
1
+ import { describe, test, expect, afterAll, afterEach, beforeAll } from 'vitest'
2
+ import { createApi } from '../src/composables/api'
3
+ import { setupServer } from 'msw/node'
4
+ import { http, HttpResponse } from 'msw'
5
+
6
+ const handlers = [
7
+ http.get('https://test-api/get/1', () => {
8
+ return HttpResponse.json({
9
+ data: { test: 'a' },
10
+ count: 0,
11
+ filters: ['test']
12
+ })
13
+ }),
14
+ http.get('https://test-api/get/2', () => {
15
+ return new HttpResponse('not found', {
16
+ status: 304
17
+ })
18
+ }),
19
+ http.get('https://test-api/getAll', () => {
20
+ return HttpResponse.json({
21
+ data: [{ test: 'a' }, { test: 'b' }],
22
+ count: 2,
23
+ filters: ['test', 'test two']
24
+ })
25
+ }),
26
+ http.post('https://test-api/post', () => {
27
+ return HttpResponse.json({ data: { test: 'b' } })
28
+ }),
29
+ http.patch('https://test-api/patch', () => {
30
+ return HttpResponse.json({ data: { test: 'c' } })
31
+ })
32
+ ]
33
+
34
+ const server = setupServer(...handlers)
35
+
36
+ beforeAll(() => server.listen({ onUnhandledRequest: 'error' }))
37
+
38
+ afterAll(() => server.close())
39
+
40
+ afterEach(() => server.resetHandlers())
41
+
42
+ describe('default api', () => {
43
+ const api = createApi({
44
+ findPath: () => 'https://test-api/',
45
+ defaultThrowError: false
46
+ })
47
+
48
+ test('get', async () => {
49
+ const res = await api.get<any>({ additionalUrl: 'get', id: '1', nav: 'test' })
50
+ expect(res).not.toBeNull()
51
+ expect(res.data.test).toEqual('a')
52
+ expect(res.filters[0]).toEqual('test')
53
+ })
54
+
55
+ test('get fails', async () => {
56
+ const res = await api.get<any>({ additionalUrl: 'get', id: '2', nav: 'test' })
57
+ expect(res.status).toEqual(304)
58
+ })
59
+
60
+ test('getAll', async () => {
61
+ const res = await api.get<any>({ additionalUrl: 'getAll', nav: 'test' })
62
+ expect(res).not.toBeNull()
63
+ expect(res.data.length).toEqual(2)
64
+ expect(res.count).toEqual(2)
65
+ expect(res.filters[0]).toEqual('test')
66
+ expect(res.filters[1]).toEqual('test two')
67
+ })
68
+
69
+ test('post', async () => {
70
+ const res = await api.post<any>({ additionalUrl: 'post', nav: 'test', data: { data: 'a' }})
71
+ expect(res).not.toBeNull()
72
+ expect(res).toEqual({ data: { test: 'b' } })
73
+ })
74
+
75
+ test('patch', async () => {
76
+ const res = await api.patch<any>({ additionalUrl: 'patch', nav: 'test', data: { data: 'a' }})
77
+ expect(res).not.toBeNull()
78
+ expect(res).toEqual({ data: { test: 'c' } })
79
+ })
80
+ })
81
+
82
+ // describe('default api fails', () => {
83
+
84
+ // })
@@ -0,0 +1,31 @@
1
+ import { describe, expect, test } from 'vitest'
2
+ import { withSetup } from './utils'
3
+ import { useLocalDb } from '../src/composables/forage'
4
+
5
+ describe('foraging', () => {
6
+ const [db] = withSetup(() => useLocalDb())
7
+
8
+ test('local db exists', () => {
9
+ expect(db).not.toBeNull()
10
+ expect(db._config.name).toEqual('Db_undefined')
11
+ })
12
+
13
+ })
14
+
15
+ // describe('local cache', () => {
16
+ // const [cache] = withSetup(() => useLocalCache())
17
+
18
+ // test('save', async () => {
19
+ // expect(cache).not.toBeNull()
20
+
21
+ // await cache.saveAsync({ test: 'a' }, 'test-key')
22
+
23
+ // const res = await cache.getAsync('test-key')
24
+
25
+ // expect(res.test).toEqual('a')
26
+
27
+ // await cache.clearAsync()
28
+
29
+ // const none = await cache.getAsync('test-key')
30
+ // })
31
+ // })
@@ -0,0 +1,231 @@
1
+ import { nestedValue, toCompareString, hasSearch, deepSelect, containsSearch, copyItemByAlphabet, copyDeep, csvContains, toggleCSV, roundTo, isArrayOfLength, isLengthyArray, addWeekday, removeWeekday, appendUrl, getAreaAround, getAreaToLeft, getAreaToRight, getLocationLine, capitalizeWords, fromCamelCase, toCamelCase, weekdayValue, weekdayShortName, containsWeekday, validEmail } from '../src/composables/helpers'
2
+ import { describe, expect, test } from 'vitest'
3
+
4
+ describe("helpers", () => {
5
+
6
+ test('append url', () => {
7
+ expect(appendUrl('https://test.com/', '/test')).toEqual('https://test.com/test')
8
+ expect(appendUrl('https://test.com', 'test')).toEqual('https://test.com/test')
9
+ expect(appendUrl('https://test.com/', 'test')).toEqual('https://test.com/test')
10
+ expect(appendUrl('https://test.com', '/test')).toEqual('https://test.com/test')
11
+ expect(appendUrl('https://test.com///', '///test')).toEqual('https://test.com/test')
12
+ })
13
+
14
+ test('get area around', () => {
15
+ //starts with top left and goes anti-clockwise
16
+ expect(getAreaAround({ lat: 0, lng: 0 }, 1)).toEqual([{ lat: -1, lng: 1 },{ lat: -1, lng: -1 },{ lat: 1, lng: -1 },{ lat: 1, lng: 1 }])
17
+ expect(getAreaAround({ lat: 0, lng: 0 }, 2)).toEqual([{ lat: -2, lng: 2 },{ lat: -2, lng: -2 },{ lat: 2, lng: -2 },{ lat: 2, lng: 2 }])
18
+ expect(getAreaAround({ lat: 2, lng: 6 }, 1)).toEqual([{ lat: 1, lng: 7 },{ lat: 1, lng: 5 },{ lat: 3, lng: 5 },{ lat: 3, lng: 7 }])
19
+ })
20
+
21
+ test('get area to left', () => {
22
+ //starts with top left and goes anti-clockwise
23
+ expect(getAreaToLeft({ lat: 0, lng: 0 }, 1)).toEqual([{ lat: -2, lng: 1 },{ lat: -2, lng: -1 },{ lat: 0, lng: -1 },{ lat: 0, lng: 1 }])
24
+ expect(getAreaToLeft({ lat: 0, lng: 0 }, 2)).toEqual([{ lat: -4, lng: 2 },{ lat: -4, lng: -2 },{ lat: 0, lng: -2 },{ lat: 0, lng: 2 }])
25
+ })
26
+
27
+ test('get area to right', () => {
28
+ //starts with top left and goes anti-clockwise
29
+ expect(getAreaToRight({ lat: 0, lng: 0 }, 1)).toEqual([{ lat: 0, lng: 1 },{ lat: 0, lng: -1 },{ lat: 2, lng: -1 },{ lat: 2, lng: 1 }])
30
+ expect(getAreaToRight({ lat: 2, lng: 0 }, 2)).toEqual([{ lat: 2, lng: 2 },{ lat: 2, lng: -2 },{ lat: 6, lng: -2 },{ lat: 6, lng: 2 }])
31
+ })
32
+
33
+ test('get location line', () => {
34
+ expect(getLocationLine(null)).toEqual('')
35
+ expect(getLocationLine({ streetNumber: '9', streetName: 'Morgan St', suburb: 'Timboon', state: 'VIC', postcode: '3268' })).toEqual('9 Morgan St, Timboon VIC 3268')
36
+ })
37
+
38
+ test('from camel case', () => {
39
+ expect(fromCamelCase('testOneTwo')).toEqual('Test One Two')
40
+ expect(fromCamelCase('testOne Two')).toEqual('Test One Two')
41
+ expect(fromCamelCase('tesTOneTwo')).not.toEqual('Test One Two')
42
+ expect(fromCamelCase()).toBeUndefined()
43
+ })
44
+
45
+ test('to camel case', () => {
46
+ expect(toCamelCase(undefined)).toBeUndefined()
47
+ expect(toCamelCase({ Test: 't' })).not.toEqual({ Test: 't' })
48
+ expect(toCamelCase({ Test: 't' })).toEqual({ test: 't' })
49
+ })
50
+
51
+ test('capitalize', () => {
52
+ expect(capitalizeWords('test')).toEqual('Test')
53
+ expect(capitalizeWords('test one')).toEqual('Test One')
54
+ })
55
+
56
+ test('weekdays csv string sort value', () => {
57
+ expect(weekdayValue('sun, wed, sat')).toEqual(1)
58
+ expect(weekdayValue('wed, sat')).toEqual(4)
59
+ expect(weekdayValue('')).toEqual(8)
60
+ expect(weekdayValue()).toEqual(0)
61
+ expect(weekdayValue('sun, wed, sat, always')).toEqual(0)
62
+ expect(weekdayValue('Tuesday')).toEqual(3)
63
+ })
64
+
65
+ test('weekday short name conversion', () => {
66
+ expect(weekdayShortName('Sun, Mon, tuesday')).toEqual('sun,mon,tue')
67
+ expect(weekdayShortName('Sun, Mon, blah')).toEqual('sun,mon')
68
+ expect(weekdayShortName('Sun, Fri, Wedne')).toEqual('sun,fri')
69
+ expect(weekdayShortName('Sunday')).toEqual('sun')
70
+ })
71
+
72
+ test('contains weekday', () => {
73
+ expect(containsWeekday('sun', 'sun')).toEqual(true)
74
+ expect(containsWeekday('sun', 'Sunday')).toEqual(true)
75
+ expect(containsWeekday('sun,mon,tue, wed', 'Tuesday')).toEqual(true)
76
+ expect(containsWeekday('sun,mon,tuesday, wed', 'Tuesday')).toEqual(true)
77
+ expect(containsWeekday('sun,mon, wed', 'tue')).toEqual(false)
78
+ })
79
+
80
+ test('add weekday', () => {
81
+ expect(addWeekday(undefined, 'sun')).toEqual('sun')
82
+ expect(addWeekday(undefined, 'sun')).toEqual('sun')
83
+ expect(addWeekday('mon,sun', 'sun')).toEqual('sun,mon')
84
+ expect(addWeekday('mon,sun, always, m', 'sun')).toEqual('always,sun,mon')
85
+ })
86
+
87
+ test('remove weekday', () => {
88
+ expect(removeWeekday(undefined, 'sun')).toEqual(undefined)
89
+ expect(removeWeekday('sun', 'sun')).toEqual(undefined)
90
+ expect(removeWeekday('sun, thur, tue', 'sun')).toEqual('tue,thu')
91
+ expect(removeWeekday('always', 'sun')).toEqual('always')
92
+ })
93
+
94
+ test('is array of length', () => {
95
+ expect(isArrayOfLength([], 0)).toEqual(true)
96
+ expect(isArrayOfLength([1,1,1], 3)).toEqual(true)
97
+ expect(isArrayOfLength(undefined, 0)).toEqual(false)
98
+ expect(isArrayOfLength({}, 0)).toEqual(false)
99
+ })
100
+
101
+ test('is lengthy array', () => {
102
+ expect(isLengthyArray([])).toEqual(false)
103
+ expect(isLengthyArray(undefined)).toEqual(false)
104
+ expect(isLengthyArray([1])).toEqual(true)
105
+ expect(isLengthyArray([1,1])).toEqual(true)
106
+ expect(isLengthyArray([1,1], 1)).toEqual(true)
107
+ expect(isLengthyArray([1,1], 3)).toEqual(false)
108
+ })
109
+
110
+ test('rounding decimal places', () => {
111
+ expect(roundTo(1, 0)).toEqual(1)
112
+ expect(roundTo(1.1, 0)).toEqual(1)
113
+ expect(roundTo(1.6, 0)).toEqual(2)
114
+ expect(roundTo(2.22567, 3)).toEqual(2.226)
115
+ expect(roundTo(2.22547, 3)).toEqual(2.225)
116
+ })
117
+
118
+ test('toggle csv', () => {
119
+ expect(toggleCSV('one,two,three', 'two')).toEqual('one,three')
120
+ expect(toggleCSV('two', 'two')).toEqual(null)
121
+ })
122
+
123
+ test('csv contains', () => {
124
+ expect(csvContains('one,two,three', 'two')).toEqual(true)
125
+ expect(csvContains('one,two,three', 'three')).toEqual(true)
126
+ expect(csvContains('one,three', 'two')).toEqual(false)
127
+ expect(csvContains(undefined, 'two')).toEqual(false)
128
+ })
129
+
130
+ test('copy deep', () => {
131
+ expect(copyDeep(null)).toStrictEqual(null)
132
+ expect(copyDeep(undefined)).toStrictEqual(undefined)
133
+
134
+ let item = { test: { one: 'treat' } }
135
+ let copy = copyDeep(item)
136
+ item.test.one = 'other'
137
+
138
+ expect(item.test).not.toEqual(copy.test)
139
+ expect(item).not.toEqual(copy)
140
+ })
141
+
142
+ test('copy by property alphabet', () => {
143
+ expect(copyItemByAlphabet(null)).toStrictEqual(null)
144
+ expect(copyItemByAlphabet(undefined)).toStrictEqual(undefined)
145
+
146
+ let item = { test: { b: 'treat', a: 't' }, v: 'a', s: 'a', z: undefined }
147
+ //consistency
148
+ expect(JSON.stringify(copyItemByAlphabet(item))).toEqual(JSON.stringify(copyItemByAlphabet(item)))
149
+ expect(JSON.stringify(copyItemByAlphabet(item))).toEqual(JSON.stringify({ s: 'a', test: { a: 't', b: 'treat' }, v: 'a', z: undefined }))
150
+ expect(JSON.stringify(copyItemByAlphabet(item))).not.toEqual(JSON.stringify({ s: 'a', test: { b: 'treat', a: 't' }, v: 'a' }))
151
+ })
152
+
153
+ test('contains search', () => {
154
+ expect(containsSearch(undefined, undefined)).toEqual(true)
155
+ expect(containsSearch('a', undefined)).toEqual(true)
156
+ expect(containsSearch('aa', 'aa')).toEqual(true)
157
+ expect(containsSearch('aa', 'a')).toEqual(true)
158
+ expect(containsSearch('aa', 'b')).toEqual(false)
159
+ expect(containsSearch('Testing One Two Three', ' one')).toEqual(true)
160
+ expect(containsSearch('Testing One Two Three', ' one')).toEqual(false)
161
+ })
162
+
163
+ test('deep select', () => {
164
+ expect(deepSelect(null)).toEqual([])
165
+
166
+ let item = [
167
+ { a: 'a', b: [{ a: 'd' }] },
168
+ { a: 'b'},
169
+ { a: 'c' }
170
+ ]
171
+
172
+ expect(deepSelect(item, x => x.b).length).toEqual(4)
173
+
174
+ let ite = [{
175
+ b: [
176
+ { a: 'a', b: [{ a: 'b', b: [{ a: 'c' }] }]},
177
+ { a: 'd' },
178
+ { a: 'e', b: [{ a: 'f' }]}
179
+ ]
180
+ }]
181
+
182
+ expect(deepSelect(ite, x => x.b).length).toEqual(7)
183
+
184
+ let te = {
185
+ b: [
186
+ { a: 'a', b: [{ a: 'b', b: [{ a: 'c' }] }]},
187
+ { a: 'd' },
188
+ { a: 'e', b: [{ a: 'f' }]}
189
+ ]
190
+ }
191
+
192
+ //misses the parent
193
+ expect(deepSelect(te, x => x.b).length).toEqual(6)
194
+ })
195
+
196
+ test('has search', () => {
197
+ expect(hasSearch(undefined, 'test', ['a', 'b'])).toEqual(false)
198
+ expect(hasSearch('test', undefined, ['a', 'b'])).toEqual(true)
199
+ expect(hasSearch({ test: 'one two three', a: 'one' }, 'four', ['test', 'a'])).toEqual(false)
200
+ expect(hasSearch({ test: 'two', a: 'one' }, 'one', undefined)).toEqual(false)
201
+ expect(hasSearch({ test: 'one two three', a: 'one', b: 'two' }, 'three', ['a', 'b'])).toEqual(false)
202
+ expect(hasSearch({ test: 'one two three', a: 'one', b: 'two' }, 'three', ['a', 'b', 'test'])).toEqual(true)
203
+ expect(hasSearch({ test: 'one two three', a: 'one', b: 'two' }, 'one', ['b'])).toEqual(false)
204
+ expect(hasSearch({ test: 'one two three', a: 'one', b: 'two' }, 'one', ['a', 'b'])).toEqual(true)
205
+ })
206
+
207
+ test('to compare string', () => {
208
+ expect(toCompareString('one')).toEqual('one')
209
+ expect(toCompareString(undefined)).toStrictEqual(null)
210
+ expect(toCompareString('one two three')).toEqual('onetwothree')
211
+ })
212
+
213
+ test('nested value', () => {
214
+ let item = {
215
+ a: { b: 'b', c: { d: 'd' } },
216
+ b: 'str'
217
+ }
218
+
219
+ expect(nestedValue(item, 'b')).toEqual('str')
220
+ expect(nestedValue(item, 'a.c.d')).toEqual('d')
221
+ expect(nestedValue(item, 'a.b')).toEqual('b')
222
+ })
223
+
224
+ test('valid email', () => {
225
+ expect(validEmail('andrewderoon@gmail.com')).toEqual(true)
226
+ expect(validEmail('@gmail.com')).toEqual(false)
227
+ expect(validEmail('a@')).toEqual(false)
228
+ expect(validEmail('a@e')).toEqual(true)
229
+ expect(validEmail('andrewderoon.com')).toEqual(false)
230
+ })
231
+ })
@@ -0,0 +1,99 @@
1
+ import { createNavigation } from '../src/composables/navigation'
2
+ import { describe, expect, test } from 'vitest'
3
+
4
+ describe("navigation", () => {
5
+ const nav = createNavigation({
6
+ defaultCacheExpiryHours: 3,
7
+ navItems: [
8
+ {
9
+ name: 'test-nav'
10
+ },
11
+ {
12
+ name: 'navA',
13
+ background: 'b',
14
+ displayName: 'discs',
15
+ hideAppBar: true,
16
+ hideNavigation: true,
17
+ hesitate: true,
18
+ icon: 'mdi-plus',
19
+ path: '/testing/one',
20
+ children: [
21
+ {
22
+ name: 'navB',
23
+ cacheExpiryHours: 4,
24
+ children: [
25
+ {
26
+ archiveName: 'nav-archive',
27
+ aliases: ['nav-alias', 'nav-alia'],
28
+ name: 'navC',
29
+ path: '/test/alias'
30
+ }
31
+ ],
32
+ singleName: 'navone'
33
+ }
34
+ ]
35
+ }
36
+ ]
37
+ })
38
+
39
+ test('find archive name', () => {
40
+ expect(nav.findArchiveName('navC')).toEqual('nav-archive')
41
+ expect(nav.findArchiveName('navB')).toEqual(undefined)
42
+ expect(nav.findArchiveName('navD')).toEqual(undefined)
43
+ })
44
+
45
+ test('find cache length', () => {
46
+ expect(nav.findCacheHours('navC')).toEqual(3)
47
+ expect(nav.findCacheHours('navB')).toEqual(4)
48
+ })
49
+
50
+ test('find display', () => {
51
+ expect(nav.findDisplay('test-nav')).toEqual(undefined)
52
+ expect(nav.findDisplay('navA')).toEqual('discs')
53
+ })
54
+
55
+ test('find icon', () => {
56
+ expect(nav.findIcon('test-nav')).toEqual(undefined)
57
+ expect(nav.findIcon('navA')).toEqual('mdi-plus')
58
+ })
59
+
60
+ test('find item', () => {
61
+ expect(nav.findItem('test-nav')).not.toBeNull()
62
+ expect(nav.findItem('test-navs')).toBeNull()
63
+ expect(nav.findItem('test-nav')?.name).toEqual('test-nav')
64
+ expect(nav.findItem('nav-alias')?.name).toEqual('navC')
65
+ })
66
+
67
+ test('find store name', () => {
68
+ expect(nav.findStoreName('navA')).toEqual('navA')
69
+ expect(nav.findStoreName('navB')).toEqual('navB')
70
+ expect(nav.findStoreName('test-nav')).toEqual('test-nav')
71
+ })
72
+
73
+ test('find path', () => {
74
+ expect(nav.findPath('test-nav')).toEqual('')
75
+ expect(nav.findPath('navA')).toEqual('/testing/one')
76
+ expect(nav.findPath('nav-alias')).toEqual('/test/alias')
77
+ })
78
+
79
+ test('find single display', () => {
80
+ expect(nav.findSingleDisplay('navA')).toEqual('disc')
81
+ expect(nav.findSingleDisplay('navB')).toEqual('navone')
82
+ })
83
+
84
+ test('update nav props', () => {
85
+ nav.updateNavigationProperties('test-nav')
86
+
87
+ expect(nav.showAppBar.value).toEqual(true)
88
+ expect(nav.showAppNavigation.value).toEqual(true)
89
+ expect(nav.backgroundName.value).toEqual(undefined)
90
+ expect(nav.hesitate.value).toEqual(false)
91
+
92
+ nav.updateNavigationProperties('navA')
93
+
94
+ expect(nav.showAppBar.value).toEqual(false)
95
+ expect(nav.showAppNavigation.value).toEqual(false)
96
+ expect(nav.backgroundName.value).toEqual('b')
97
+ expect(nav.hesitate.value).toEqual(true)
98
+ })
99
+ })
@@ -0,0 +1,138 @@
1
+ import { setActivePinia, createPinia } from 'pinia'
2
+ import { createSessionStore, createWholeLastUpdateStore } from '../src/composables/stores'
3
+ import { createApi } from '../src/composables/api'
4
+ import { describe, test, expect, beforeEach, afterAll, afterEach, beforeAll } from 'vitest'
5
+ import { setupServer } from 'msw/node'
6
+ import { http, HttpResponse } from 'msw'
7
+ import { createApp } from 'vue'
8
+ import { withSetup } from './utils'
9
+
10
+ const handlers = [
11
+ http.get('https://api/get/1', () => {
12
+ return HttpResponse.json({
13
+ data: { test: 'g' }
14
+ })
15
+ }),
16
+ http.get('https://api/getAll', () => {
17
+ return HttpResponse.json({
18
+ data: [{ test: 'a', id: '1' }, { test: 'b', id: '2' }],
19
+ count: 2,
20
+ filters: ['test', 'test two']
21
+ })
22
+ }),
23
+ http.post('https://api/post', () => {
24
+ return HttpResponse.json({ data: { test: 'd', id: '11' } })
25
+ }),
26
+ http.patch('https://api/patch', () => {
27
+ return HttpResponse.json({ data: { test: 'e', rowVersion: 2 } })
28
+ })
29
+ ]
30
+
31
+ const server = setupServer(...handlers)
32
+
33
+ beforeAll(() => server.listen({ onUnhandledRequest: 'error' }))
34
+
35
+ afterAll(() => server.close())
36
+
37
+ afterEach(() => {
38
+ server.resetHandlers()
39
+ })
40
+
41
+ describe('use last update store no api no caching', () => {
42
+ const [api, app] = withSetup(() => createApi({
43
+ findPath: () => 'https://api/',
44
+ defaultThrowError: false
45
+ }))
46
+
47
+ const pinia = createPinia()
48
+ app.use(pinia)
49
+
50
+ setActivePinia(pinia)
51
+
52
+ const store = createWholeLastUpdateStore({
53
+ api: null,
54
+ storageMode: 'session',
55
+ storeName: 'whole'
56
+ })()
57
+
58
+ test('all session activity', async () => {
59
+
60
+ await store.post<any>({ data: { id: '1', test: 'a' }})
61
+ await store.post<any>({ data: { id: '2', test: 'b' }})
62
+ await store.post<any>({ data: { id: '3', test: 'c' }})
63
+
64
+ let postRes = await store.get<any>({ id: '1' })
65
+ expect(postRes.data).toEqual({ id: '1', test: 'a' })
66
+
67
+ await store.patch<any>({ data: { id: '1', test: 'd' }})
68
+ postRes = await store.get<any>({ id: '1' })
69
+ expect(postRes.data).toEqual({ id: '1', test: 'd' })
70
+
71
+ let getAll = await store.getAll<any>({})
72
+ expect(getAll.data.length).toEqual(3)
73
+ expect(getAll.count).toEqual(3)
74
+
75
+
76
+ await store.deleteItem({ data: { id: '1' }})
77
+ postRes = await store.get<any>({ id: '1' })
78
+ expect(postRes.data).toEqual(undefined)
79
+
80
+ getAll = await store.getAll<any>({})
81
+ expect(getAll.data.length).toEqual(2)
82
+ expect(getAll.count).toEqual(2)
83
+ })
84
+
85
+ })
86
+
87
+
88
+ describe('use last update store with api no caching', () => {
89
+ const [api, app] = withSetup(() => createApi({
90
+ findPath: () => 'https://api/',
91
+ defaultThrowError: false
92
+ }))
93
+
94
+ const pinia = createPinia()
95
+ app.use(pinia)
96
+ setActivePinia(pinia)
97
+
98
+ const store = createWholeLastUpdateStore({
99
+ storageMode: 'session',
100
+ api: api,
101
+ storeName: 'whole-api'
102
+ })()
103
+
104
+
105
+ test('get', async () => {
106
+ const res = await store.get<any>({ id: '1' })
107
+ expect(res.data).toEqual({ test: 'a', id: '1' })
108
+ let getAll = await store.getAll<any>({})
109
+ expect(getAll.data.length).toEqual(2)
110
+ expect(getAll.count).toEqual(2)
111
+ })
112
+
113
+
114
+ test('get all', async () => {
115
+ const res = await store.getAll<any>({})
116
+ expect(res).toEqual({
117
+ data: [{test: 'a', id: '1' },{test: 'b', id: '2' }],
118
+ count: 2,
119
+ filters: []
120
+ })
121
+ })
122
+
123
+ test('post', async () => {
124
+ const res = await store.post<any>({ data: { test: 'a', id: '11' }})
125
+ expect(res).not.toBeNull()
126
+
127
+ //gets api post return
128
+ let getRes = await store.get<any>({ id: '11' })
129
+ expect(getRes.data).toEqual({ test: 'd', id: '11' })
130
+ })
131
+
132
+ test('patch', async () => {
133
+ const res = await store.patch<any>({ data: { test: 'a', id: '1', rowVersion: 1 }})
134
+ let getRes = await store.get<any>({ id: '1' })
135
+ expect(getRes.data.rowVersion).toEqual(2)
136
+ })
137
+
138
+ })
@@ -0,0 +1,118 @@
1
+ import { setActivePinia, createPinia } from 'pinia'
2
+ import { createSessionStore, createWholeLastUpdateStore } from '../src/composables/stores'
3
+ import { createApi } from '../src/composables/api'
4
+ import { describe, test, expect, beforeEach, afterAll, afterEach, beforeAll } from 'vitest'
5
+ import { setupServer } from 'msw/node'
6
+ import { http, HttpResponse } from 'msw'
7
+ import { createApp } from 'vue'
8
+ import { withSetup } from './utils'
9
+
10
+ const handlers = [
11
+ http.get('https://test-api/get/1', () => {
12
+ return HttpResponse.json({
13
+ data: { test: 'a' }
14
+ })
15
+ }),
16
+ http.get('https://test-api/get/2', () => {
17
+ return new HttpResponse('not found', {
18
+ status: 304
19
+ })
20
+ }),
21
+ http.get('https://test-api/getAll', () => {
22
+ return HttpResponse.json({
23
+ data: [{ test: 'a' }, { test: 'b' }],
24
+ count: 2,
25
+ filters: ['test', 'test two']
26
+ })
27
+ }),
28
+ http.post('https://test-api/post', () => {
29
+ return HttpResponse.json({ data: { test: 'b' } })
30
+ }),
31
+ http.patch('https://test-api/patch', () => {
32
+ return HttpResponse.json({ data: { test: 'c', rowVersion: 2 } })
33
+ })
34
+ ]
35
+
36
+ const server = setupServer(...handlers)
37
+
38
+ beforeAll(() => server.listen({ onUnhandledRequest: 'error' }))
39
+
40
+ afterAll(() => server.close())
41
+
42
+ afterEach(() => {
43
+ server.resetHandlers()
44
+ })
45
+
46
+ describe('use session store with no api no caching', () => {
47
+ const [api, app] = withSetup(() => createApi())
48
+
49
+ const pinia = createPinia()
50
+ app.use(pinia)
51
+ setActivePinia(pinia)
52
+
53
+ const store = createSessionStore({
54
+ storageMode: 'session',
55
+ storeName: 'sesh'
56
+ })()
57
+
58
+ test('all session activity', async () => {
59
+ await store.post<any>({ data: { id: '1', test: 'a' }})
60
+ let postRes = await store.get<any>({ id: '1' })
61
+ expect(postRes.data).toEqual({ id: '1', test: 'a' })
62
+ await store.patch<any>({ nav: 'test', data: { id: '1', test: 'b' }})
63
+ postRes = await store.get<any>({ id: '1' })
64
+ expect(postRes.data).toEqual({ id: '1', test: 'b' })
65
+ await store.deleteItem({ id: '1' })
66
+ postRes = await store.get<any>({ id: '1' })
67
+ expect(postRes).toEqual(undefined)
68
+ })
69
+ })
70
+
71
+ describe('use session store with api no caching', () => {
72
+ const [api, app] = withSetup(() => createApi({
73
+ findPath: () => 'https://test-api/',
74
+ defaultThrowError: false
75
+ }))
76
+
77
+ const pinia = createPinia()
78
+ app.use(pinia)
79
+
80
+ setActivePinia(pinia)
81
+
82
+ const store = createSessionStore({
83
+ storageMode: 'session',
84
+ api: api,
85
+ storeName: 'test'
86
+ })()
87
+
88
+
89
+ test('get', async () => {
90
+ const res = await store.get<any>({ nav: 'test', id: '1' })
91
+ expect(res).toEqual({ data: { test: 'a' }})
92
+ //expect(store.searchMemory['test_no-user-id_1']).toEqual({ data: { test: 'a' }})
93
+ })
94
+
95
+ test('get all', async () => {
96
+ const res = await store.getAll<any>({ nav: 'test' })
97
+ expect(res).toEqual({
98
+ data: [{test: 'a' },{test: 'b'}],
99
+ count: 2,
100
+ filters: ['test', 'test two']
101
+ })
102
+ })
103
+
104
+ test('post', async () => {
105
+ const res = await store.post<any>({ nav: 'test', data: { test: 'a', id: '11' }})
106
+ expect(res).not.toBeNull()
107
+ //gets api post return
108
+ let getRes = await store.get<any>({ nav: 'test', id: '11' })
109
+ expect(getRes.data).toEqual({ test: 'b' })
110
+ })
111
+
112
+ test('patch', async () => {
113
+ const res = await store.patch<any>({ nav: 'test', data: { test: 'a', id: '1', rowVersion: 1 }})
114
+ let getRes = await store.get<any>({ nav: 'test', id: '1' })
115
+ expect(getRes.data.rowVersion).toEqual(2)
116
+ })
117
+
118
+ })
@@ -0,0 +1,29 @@
1
+ import { describe, expect, test } from "vitest"
2
+ import { withSetup } from './utils'
3
+ import { useTracker } from '../src/composables/track'
4
+ import { twiddleThumbs } from "../src/composables/helpers"
5
+
6
+ describe("track", () => {
7
+ test('tracker test', async () => {
8
+
9
+ const item = { test: 'a' }
10
+ const [{ asyncItem, isChanged, restartTracker }] = withSetup(() => useTracker(item))
11
+
12
+
13
+ expect(isChanged.value).toEqual(false)
14
+ asyncItem.value.test = 'b'
15
+ await twiddleThumbs(500)
16
+ expect(isChanged.value).toEqual(true)
17
+ asyncItem.value.test = 'a'
18
+ await twiddleThumbs(500)
19
+ expect(isChanged.value).toEqual(false)
20
+ asyncItem.value.test = 'b'
21
+ await twiddleThumbs(500)
22
+ expect(isChanged.value).toEqual(true)
23
+ restartTracker()
24
+ await twiddleThumbs(500)
25
+ expect(isChanged.value).toEqual(false)
26
+
27
+ })
28
+
29
+ })
package/test/utils.ts ADDED
@@ -0,0 +1,15 @@
1
+ import { createApp } from 'vue'
2
+
3
+ export function withSetup(composable) {
4
+ let result
5
+ const app = createApp({
6
+ setup() {
7
+ result = composable()
8
+ return () => {}
9
+ }
10
+ })
11
+
12
+ app.mount(document.createElement('div'))
13
+
14
+ return [result, app]
15
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,33 @@
1
+ {
2
+ "compilerOptions": {
3
+ "baseUrl": ".",
4
+ "target": "ES2020",
5
+ "useDefineForClassFields": true,
6
+ "module": "ESNext",
7
+ "lib": ["ES2020", "ES2021.String", "DOM", "DOM.Iterable"],
8
+ "skipLibCheck": true,
9
+
10
+ /* Bundler mode */
11
+ "moduleResolution": "bundler",
12
+ "allowImportingTsExtensions": true,
13
+ "resolveJsonModule": true,
14
+ "isolatedModules": true,
15
+ "noEmit": true,
16
+ "jsx": "preserve",
17
+
18
+ /* Linting */
19
+ "strict": true,
20
+ "noUnusedLocals": true,
21
+ "noUnusedParameters": true,
22
+ "noFallthroughCasesInSwitch": true,
23
+ "paths": {
24
+ "@/*": [
25
+ "src/*"
26
+ ]
27
+ },
28
+ "types": ["vitest/globals"]
29
+ },
30
+ "include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.vue"],
31
+ "references": [{ "path": "./tsconfig.node.json" }],
32
+ "exclude": ["node_modules"]
33
+ }
package/index.html DELETED
@@ -1,13 +0,0 @@
1
- <!doctype html>
2
- <html lang="en">
3
- <head>
4
- <meta charset="UTF-8" />
5
- <link rel="icon" type="image/svg+xml" href="/vite.svg" />
6
- <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
- <title>Vite + Vue + TS</title>
8
- </head>
9
- <body>
10
- <div id="app"></div>
11
- <script type="module" src="/src/main.ts"></script>
12
- </body>
13
- </html>
package/public/vite.svg DELETED
@@ -1 +0,0 @@
1
- <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>