metaowl 0.4.1 → 0.6.0
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/CHANGELOG.md +50 -0
- package/README.md +267 -2
- package/build/runtime/bin/metaowl-build.js +10 -0
- package/{bin → build/runtime/bin}/metaowl-create.js +96 -177
- package/build/runtime/bin/metaowl-dev.js +10 -0
- package/build/runtime/bin/metaowl-generate.js +231 -0
- package/build/runtime/bin/metaowl-lint.js +58 -0
- package/build/runtime/bin/utils.js +68 -0
- package/build/runtime/index.js +144 -0
- package/build/runtime/modules/app-mounter.js +73 -0
- package/build/runtime/modules/auto-import.js +140 -0
- package/build/runtime/modules/cache.js +49 -0
- package/build/runtime/modules/composables.js +353 -0
- package/build/runtime/modules/constants.js +38 -0
- package/build/runtime/modules/error-boundary.js +116 -0
- package/build/runtime/modules/fetch.js +31 -0
- package/build/runtime/modules/file-router.js +207 -0
- package/build/runtime/modules/fonts.js +172 -0
- package/build/runtime/modules/forms.js +193 -0
- package/build/runtime/modules/i18n.js +180 -0
- package/build/runtime/modules/image.js +175 -0
- package/build/runtime/modules/layouts.js +214 -0
- package/build/runtime/modules/link.js +141 -0
- package/build/runtime/modules/meta.js +117 -0
- package/build/runtime/modules/odoo-rpc.js +265 -0
- package/build/runtime/modules/pwa.js +272 -0
- package/build/runtime/modules/router.js +384 -0
- package/build/runtime/modules/seo.js +186 -0
- package/build/runtime/modules/store.js +198 -0
- package/build/runtime/modules/templates-manager.js +52 -0
- package/build/runtime/modules/test-utils.js +238 -0
- package/build/runtime/vite/plugin.js +197 -0
- package/eslint.js +29 -0
- package/package.json +45 -27
- package/CONTRIBUTING.md +0 -49
- package/bin/metaowl-build.js +0 -12
- package/bin/metaowl-dev.js +0 -12
- package/bin/metaowl-generate.js +0 -339
- package/bin/metaowl-lint.js +0 -71
- package/bin/utils.js +0 -82
- package/eslint.config.js +0 -3
- package/index.js +0 -328
- package/modules/app-mounter.js +0 -104
- package/modules/auto-import.js +0 -225
- package/modules/cache.js +0 -59
- package/modules/composables.js +0 -600
- package/modules/error-boundary.js +0 -228
- package/modules/fetch.js +0 -51
- package/modules/file-router.js +0 -478
- package/modules/forms.js +0 -353
- package/modules/i18n.js +0 -333
- package/modules/layouts.js +0 -431
- package/modules/link.js +0 -255
- package/modules/meta.js +0 -119
- package/modules/odoo-rpc.js +0 -511
- package/modules/pwa.js +0 -515
- package/modules/router.js +0 -769
- package/modules/seo.js +0 -501
- package/modules/store.js +0 -409
- package/modules/templates-manager.js +0 -89
- package/modules/test-utils.js +0 -532
- package/test/auto-import.test.js +0 -110
- package/test/cache.test.js +0 -55
- package/test/composables.test.js +0 -103
- package/test/dynamic-routes.test.js +0 -469
- package/test/error-boundary.test.js +0 -126
- package/test/fetch.test.js +0 -100
- package/test/file-router.test.js +0 -55
- package/test/forms.test.js +0 -203
- package/test/i18n.test.js +0 -188
- package/test/layouts.test.js +0 -395
- package/test/link.test.js +0 -189
- package/test/meta.test.js +0 -146
- package/test/odoo-rpc.test.js +0 -547
- package/test/pwa.test.js +0 -154
- package/test/router-guards.test.js +0 -229
- package/test/router.test.js +0 -77
- package/test/seo.test.js +0 -353
- package/test/store.test.js +0 -476
- package/test/templates-manager.test.js +0 -83
- package/test/test-utils.test.js +0 -314
- package/vite/plugin.js +0 -290
- package/vitest.config.js +0 -8
package/test/pwa.test.js
DELETED
|
@@ -1,154 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from 'vitest'
|
|
2
|
-
import {
|
|
3
|
-
generateManifest,
|
|
4
|
-
isOnline,
|
|
5
|
-
subscribeToConnectivity,
|
|
6
|
-
sync,
|
|
7
|
-
showNotification,
|
|
8
|
-
cache,
|
|
9
|
-
checkCapabilities,
|
|
10
|
-
PWA
|
|
11
|
-
} from '../modules/pwa.js'
|
|
12
|
-
|
|
13
|
-
describe('PWA', () => {
|
|
14
|
-
describe('Exports', () => {
|
|
15
|
-
it('should export all functions', () => {
|
|
16
|
-
expect(typeof generateManifest).toBe('function')
|
|
17
|
-
expect(typeof isOnline).toBe('function')
|
|
18
|
-
expect(typeof subscribeToConnectivity).toBe('function')
|
|
19
|
-
expect(typeof sync).toBe('function')
|
|
20
|
-
expect(typeof showNotification).toBe('function')
|
|
21
|
-
expect(typeof cache).toBe('object')
|
|
22
|
-
expect(typeof checkCapabilities).toBe('function')
|
|
23
|
-
})
|
|
24
|
-
|
|
25
|
-
it('should export PWA namespace', () => {
|
|
26
|
-
expect(PWA.generateManifest).toBe(generateManifest)
|
|
27
|
-
expect(PWA.isOnline).toBe(isOnline)
|
|
28
|
-
expect(PWA.cache).toBe(cache)
|
|
29
|
-
})
|
|
30
|
-
})
|
|
31
|
-
|
|
32
|
-
describe('generateManifest', () => {
|
|
33
|
-
it('should generate basic manifest', () => {
|
|
34
|
-
const manifest = generateManifest({
|
|
35
|
-
name: 'My App',
|
|
36
|
-
shortName: 'MyApp'
|
|
37
|
-
})
|
|
38
|
-
|
|
39
|
-
expect(manifest.name).toBe('My App')
|
|
40
|
-
expect(manifest.short_name).toBe('MyApp')
|
|
41
|
-
expect(manifest.display).toBe('standalone')
|
|
42
|
-
expect(manifest.theme_color).toBe('#000000')
|
|
43
|
-
})
|
|
44
|
-
|
|
45
|
-
it('should include optional fields', () => {
|
|
46
|
-
const manifest = generateManifest({
|
|
47
|
-
name: 'Full App',
|
|
48
|
-
shortName: 'Full',
|
|
49
|
-
description: 'A complete app',
|
|
50
|
-
startUrl: '/home',
|
|
51
|
-
display: 'fullscreen',
|
|
52
|
-
themeColor: '#007bff',
|
|
53
|
-
backgroundColor: '#ffffff',
|
|
54
|
-
scope: '/app',
|
|
55
|
-
icons: [
|
|
56
|
-
{ src: '/icon.png', sizes: '192x192', type: 'image/png' }
|
|
57
|
-
]
|
|
58
|
-
})
|
|
59
|
-
|
|
60
|
-
expect(manifest.description).toBe('A complete app')
|
|
61
|
-
expect(manifest.start_url).toBe('/home')
|
|
62
|
-
expect(manifest.display).toBe('fullscreen')
|
|
63
|
-
expect(manifest.theme_color).toBe('#007bff')
|
|
64
|
-
expect(manifest.background_color).toBe('#ffffff')
|
|
65
|
-
expect(manifest.scope).toBe('/app')
|
|
66
|
-
expect(manifest.icons).toHaveLength(1)
|
|
67
|
-
})
|
|
68
|
-
|
|
69
|
-
it('should use default values', () => {
|
|
70
|
-
const manifest = generateManifest({
|
|
71
|
-
name: 'Minimal'
|
|
72
|
-
})
|
|
73
|
-
|
|
74
|
-
expect(manifest.display).toBe('standalone')
|
|
75
|
-
expect(manifest.theme_color).toBe('#000000')
|
|
76
|
-
expect(manifest.start_url).toBe('./')
|
|
77
|
-
})
|
|
78
|
-
|
|
79
|
-
it('should include orientation', () => {
|
|
80
|
-
const manifest = generateManifest({ name: 'Test' })
|
|
81
|
-
expect(manifest.orientation).toBe('any')
|
|
82
|
-
})
|
|
83
|
-
})
|
|
84
|
-
|
|
85
|
-
describe('isOnline', () => {
|
|
86
|
-
it('should return navigator.onLine', () => {
|
|
87
|
-
const result = isOnline()
|
|
88
|
-
expect(typeof result).toBe('boolean')
|
|
89
|
-
})
|
|
90
|
-
})
|
|
91
|
-
|
|
92
|
-
describe('subscribeToConnectivity', () => {
|
|
93
|
-
it('should return unsubscribe function', () => {
|
|
94
|
-
const unsubscribe = subscribeToConnectivity({})
|
|
95
|
-
expect(typeof unsubscribe).toBe('function')
|
|
96
|
-
|
|
97
|
-
// Cleanup
|
|
98
|
-
unsubscribe()
|
|
99
|
-
})
|
|
100
|
-
|
|
101
|
-
it('should not throw without callbacks', () => {
|
|
102
|
-
const unsubscribe = subscribeToConnectivity({})
|
|
103
|
-
expect(typeof unsubscribe).toBe('function')
|
|
104
|
-
unsubscribe()
|
|
105
|
-
})
|
|
106
|
-
})
|
|
107
|
-
|
|
108
|
-
describe('sync', () => {
|
|
109
|
-
it('should return false without service worker', async () => {
|
|
110
|
-
const result = await sync('test-tag')
|
|
111
|
-
expect(result).toBe(false)
|
|
112
|
-
})
|
|
113
|
-
})
|
|
114
|
-
|
|
115
|
-
describe('showNotification', () => {
|
|
116
|
-
it('should not throw', async () => {
|
|
117
|
-
await showNotification('Test', { body: 'Body' })
|
|
118
|
-
// Should complete without error
|
|
119
|
-
})
|
|
120
|
-
})
|
|
121
|
-
|
|
122
|
-
describe('cache', () => {
|
|
123
|
-
it('should have cache methods', () => {
|
|
124
|
-
expect(typeof cache.add).toBe('function')
|
|
125
|
-
expect(typeof cache.remove).toBe('function')
|
|
126
|
-
expect(typeof cache.clear).toBe('function')
|
|
127
|
-
expect(typeof cache.info).toBe('function')
|
|
128
|
-
})
|
|
129
|
-
|
|
130
|
-
it('should return empty array for info without caches', async () => {
|
|
131
|
-
const info = await cache.info()
|
|
132
|
-
expect(Array.isArray(info)).toBe(true)
|
|
133
|
-
})
|
|
134
|
-
})
|
|
135
|
-
|
|
136
|
-
describe('checkCapabilities', () => {
|
|
137
|
-
it('should return capability object', () => {
|
|
138
|
-
const caps = checkCapabilities()
|
|
139
|
-
|
|
140
|
-
expect(typeof caps.serviceWorker).toBe('boolean')
|
|
141
|
-
expect(typeof caps.push).toBe('boolean')
|
|
142
|
-
expect(typeof caps.notifications).toBe('boolean')
|
|
143
|
-
expect(typeof caps.backgroundSync).toBe('boolean')
|
|
144
|
-
expect(typeof caps.persistentStorage).toBe('boolean')
|
|
145
|
-
expect(typeof caps.addToHomeScreen).toBe('boolean')
|
|
146
|
-
expect(typeof caps.offline).toBe('boolean')
|
|
147
|
-
})
|
|
148
|
-
|
|
149
|
-
it('should detect service worker support', () => {
|
|
150
|
-
const caps = checkCapabilities()
|
|
151
|
-
expect(caps.serviceWorker).toBeDefined()
|
|
152
|
-
})
|
|
153
|
-
})
|
|
154
|
-
})
|
|
@@ -1,229 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest'
|
|
2
|
-
import {
|
|
3
|
-
processRoutes,
|
|
4
|
-
beforeEach as beforeEachGuard,
|
|
5
|
-
afterEach as afterEachHook,
|
|
6
|
-
getCurrentRoute,
|
|
7
|
-
getPreviousRoute,
|
|
8
|
-
isNavigating,
|
|
9
|
-
cancelNavigation,
|
|
10
|
-
navigate,
|
|
11
|
-
push,
|
|
12
|
-
replace,
|
|
13
|
-
back,
|
|
14
|
-
forward,
|
|
15
|
-
go,
|
|
16
|
-
router,
|
|
17
|
-
resetRouter
|
|
18
|
-
} from '../modules/router.js'
|
|
19
|
-
|
|
20
|
-
// Mock document.location
|
|
21
|
-
describe('Router Guards', () => {
|
|
22
|
-
let originalLocation
|
|
23
|
-
let mockRoutes
|
|
24
|
-
|
|
25
|
-
beforeEach(() => {
|
|
26
|
-
// Save original location
|
|
27
|
-
originalLocation = window.location
|
|
28
|
-
|
|
29
|
-
// Create a shared pathname that both window.location and document.location will use
|
|
30
|
-
let currentPathname = '/'
|
|
31
|
-
let currentSearch = ''
|
|
32
|
-
|
|
33
|
-
const mockLocation = {
|
|
34
|
-
get pathname() { return currentPathname },
|
|
35
|
-
set pathname(value) { currentPathname = value },
|
|
36
|
-
get search() { return currentSearch },
|
|
37
|
-
set search(value) { currentSearch = value },
|
|
38
|
-
get href() { return `http://localhost${currentPathname}${currentSearch}` },
|
|
39
|
-
replace: vi.fn(),
|
|
40
|
-
assign: vi.fn()
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
// Mock location
|
|
44
|
-
delete window.location
|
|
45
|
-
window.location = mockLocation
|
|
46
|
-
|
|
47
|
-
// Also mock document.location for router (use same object reference)
|
|
48
|
-
document.location = mockLocation
|
|
49
|
-
|
|
50
|
-
// Mock history
|
|
51
|
-
window.history = {
|
|
52
|
-
back: vi.fn(),
|
|
53
|
-
forward: vi.fn(),
|
|
54
|
-
go: vi.fn()
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
// Reset router state
|
|
58
|
-
vi.clearAllMocks()
|
|
59
|
-
resetRouter()
|
|
60
|
-
|
|
61
|
-
// Define test routes
|
|
62
|
-
mockRoutes = [
|
|
63
|
-
{ name: 'index', path: ['/'], component: class Index {} },
|
|
64
|
-
{ name: 'about', path: ['/about'], component: class About {} },
|
|
65
|
-
{ name: 'user', path: ['/user/:id'], component: class User {} },
|
|
66
|
-
{
|
|
67
|
-
name: 'admin',
|
|
68
|
-
path: ['/admin'],
|
|
69
|
-
component: class Admin {},
|
|
70
|
-
meta: { requiresAuth: true },
|
|
71
|
-
beforeEnter: null
|
|
72
|
-
},
|
|
73
|
-
{ name: 'login', path: ['/login'], component: class Login {} }
|
|
74
|
-
]
|
|
75
|
-
})
|
|
76
|
-
|
|
77
|
-
afterEach(() => {
|
|
78
|
-
// Restore original location
|
|
79
|
-
window.location = originalLocation
|
|
80
|
-
})
|
|
81
|
-
|
|
82
|
-
describe('beforeEach guards', () => {
|
|
83
|
-
it('provides to, from, and next to guard', async () => {
|
|
84
|
-
const guard = vi.fn((to, from, next) => {
|
|
85
|
-
expect(to).toHaveProperty('name')
|
|
86
|
-
expect(to).toHaveProperty('path')
|
|
87
|
-
expect(to).toHaveProperty('fullPath')
|
|
88
|
-
expect(to).toHaveProperty('meta')
|
|
89
|
-
expect(typeof next).toBe('function')
|
|
90
|
-
next()
|
|
91
|
-
})
|
|
92
|
-
|
|
93
|
-
beforeEachGuard(guard)
|
|
94
|
-
window.location.pathname = '/about'
|
|
95
|
-
|
|
96
|
-
await processRoutes(mockRoutes)
|
|
97
|
-
|
|
98
|
-
expect(guard).toHaveBeenCalled()
|
|
99
|
-
})
|
|
100
|
-
|
|
101
|
-
it('blocks navigation with next(false)', async () => {
|
|
102
|
-
const guard = vi.fn((to, from, next) => next(false))
|
|
103
|
-
|
|
104
|
-
beforeEachGuard(guard)
|
|
105
|
-
window.location.pathname = '/about'
|
|
106
|
-
|
|
107
|
-
await expect(processRoutes(mockRoutes)).rejects.toThrow('Navigation cancelled')
|
|
108
|
-
})
|
|
109
|
-
|
|
110
|
-
it('allows returning false directly from guard', async () => {
|
|
111
|
-
const guard = vi.fn(() => false)
|
|
112
|
-
|
|
113
|
-
beforeEachGuard(guard)
|
|
114
|
-
window.location.pathname = '/about'
|
|
115
|
-
|
|
116
|
-
await expect(processRoutes(mockRoutes)).rejects.toThrow('Navigation cancelled')
|
|
117
|
-
})
|
|
118
|
-
|
|
119
|
-
it('handles errors in guards', async () => {
|
|
120
|
-
const guard = vi.fn(() => {
|
|
121
|
-
throw new Error('Guard error')
|
|
122
|
-
})
|
|
123
|
-
|
|
124
|
-
beforeEachGuard(guard)
|
|
125
|
-
window.location.pathname = '/about'
|
|
126
|
-
|
|
127
|
-
await expect(processRoutes(mockRoutes)).rejects.toThrow('Guard error')
|
|
128
|
-
})
|
|
129
|
-
|
|
130
|
-
it('removes guard when unsubscribe is called', async () => {
|
|
131
|
-
const guard = vi.fn((to, from, next) => next())
|
|
132
|
-
|
|
133
|
-
const unsubscribe = beforeEachGuard(guard)
|
|
134
|
-
unsubscribe()
|
|
135
|
-
|
|
136
|
-
window.location.pathname = '/about'
|
|
137
|
-
await processRoutes(mockRoutes)
|
|
138
|
-
|
|
139
|
-
// Guard should not be called after unsubscribe
|
|
140
|
-
expect(guard).not.toHaveBeenCalled()
|
|
141
|
-
})
|
|
142
|
-
|
|
143
|
-
it('calls multiple guards in order', async () => {
|
|
144
|
-
const order = []
|
|
145
|
-
|
|
146
|
-
beforeEachGuard((to, from, next) => {
|
|
147
|
-
order.push(1)
|
|
148
|
-
next()
|
|
149
|
-
})
|
|
150
|
-
|
|
151
|
-
beforeEachGuard((to, from, next) => {
|
|
152
|
-
order.push(2)
|
|
153
|
-
next()
|
|
154
|
-
})
|
|
155
|
-
|
|
156
|
-
window.location.pathname = '/about'
|
|
157
|
-
await processRoutes(mockRoutes)
|
|
158
|
-
|
|
159
|
-
expect(order).toEqual([1, 2])
|
|
160
|
-
})
|
|
161
|
-
})
|
|
162
|
-
|
|
163
|
-
describe('afterEach hooks', () => {
|
|
164
|
-
it('provides to and from to hook', async () => {
|
|
165
|
-
const hook = vi.fn((to, from) => {
|
|
166
|
-
expect(to).toHaveProperty('name')
|
|
167
|
-
expect(from).toBeNull() // First navigation
|
|
168
|
-
})
|
|
169
|
-
|
|
170
|
-
afterEachHook(hook)
|
|
171
|
-
window.location.pathname = '/about'
|
|
172
|
-
|
|
173
|
-
await processRoutes(mockRoutes)
|
|
174
|
-
|
|
175
|
-
expect(hook).toHaveBeenCalled()
|
|
176
|
-
})
|
|
177
|
-
|
|
178
|
-
it('removes hook when unsubscribe is called', async () => {
|
|
179
|
-
const hook = vi.fn()
|
|
180
|
-
|
|
181
|
-
const unsubscribe = afterEachHook(hook)
|
|
182
|
-
unsubscribe()
|
|
183
|
-
|
|
184
|
-
window.location.pathname = '/about'
|
|
185
|
-
await processRoutes(mockRoutes)
|
|
186
|
-
|
|
187
|
-
expect(hook).not.toHaveBeenCalled()
|
|
188
|
-
})
|
|
189
|
-
})
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
describe('route state tracking', () => {
|
|
194
|
-
it('exposes beforeEach method', () => {
|
|
195
|
-
expect(router.beforeEach).toBe(beforeEachGuard)
|
|
196
|
-
})
|
|
197
|
-
|
|
198
|
-
it('exposes afterEach method', () => {
|
|
199
|
-
expect(router.afterEach).toBe(afterEachHook)
|
|
200
|
-
})
|
|
201
|
-
|
|
202
|
-
it('exposes isNavigating getter', () => {
|
|
203
|
-
expect(typeof router.isNavigating).toBe('boolean')
|
|
204
|
-
})
|
|
205
|
-
|
|
206
|
-
it('exposes navigation methods', () => {
|
|
207
|
-
expect(typeof router.push).toBe('function')
|
|
208
|
-
expect(typeof router.replace).toBe('function')
|
|
209
|
-
expect(typeof router.back).toBe('function')
|
|
210
|
-
expect(typeof router.forward).toBe('function')
|
|
211
|
-
expect(typeof router.go).toBe('function')
|
|
212
|
-
})
|
|
213
|
-
|
|
214
|
-
it('back calls history.back', () => {
|
|
215
|
-
back()
|
|
216
|
-
expect(window.history.back).toHaveBeenCalled()
|
|
217
|
-
})
|
|
218
|
-
|
|
219
|
-
it('forward calls history.forward', () => {
|
|
220
|
-
forward()
|
|
221
|
-
expect(window.history.forward).toHaveBeenCalled()
|
|
222
|
-
})
|
|
223
|
-
|
|
224
|
-
it('go calls history.go', () => {
|
|
225
|
-
go(-2)
|
|
226
|
-
expect(window.history.go).toHaveBeenCalledWith(-2)
|
|
227
|
-
})
|
|
228
|
-
})
|
|
229
|
-
})
|
package/test/router.test.js
DELETED
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, beforeEach } from 'vitest'
|
|
2
|
-
import { processRoutes } from '../modules/router.js'
|
|
3
|
-
|
|
4
|
-
const Comp = function Page() {}
|
|
5
|
-
|
|
6
|
-
function makeRoute(path) {
|
|
7
|
-
return { name: 'page', path: [path], component: Comp }
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
// Stub document.location for each test
|
|
11
|
-
function setPath(pathname) {
|
|
12
|
-
Object.defineProperty(globalThis, 'document', {
|
|
13
|
-
value: { location: { pathname } },
|
|
14
|
-
writable: true,
|
|
15
|
-
configurable: true
|
|
16
|
-
})
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
beforeEach(() => {
|
|
20
|
-
setPath('/')
|
|
21
|
-
})
|
|
22
|
-
|
|
23
|
-
describe('processRoutes', () => {
|
|
24
|
-
it('resolves a matching route for /', async () => {
|
|
25
|
-
setPath('/')
|
|
26
|
-
const routes = [makeRoute('/')]
|
|
27
|
-
const result = await processRoutes(routes)
|
|
28
|
-
expect(result).toHaveLength(1)
|
|
29
|
-
expect(result[0].component).toBe(Comp)
|
|
30
|
-
})
|
|
31
|
-
|
|
32
|
-
it('resolves /index.html as the index route', async () => {
|
|
33
|
-
setPath('/index.html')
|
|
34
|
-
const routes = [makeRoute('/')]
|
|
35
|
-
const result = await processRoutes(routes)
|
|
36
|
-
expect(result[0].component).toBe(Comp)
|
|
37
|
-
})
|
|
38
|
-
|
|
39
|
-
it('resolves /about via trailing slash variant', async () => {
|
|
40
|
-
setPath('/about/')
|
|
41
|
-
const routes = [makeRoute('/about')]
|
|
42
|
-
const result = await processRoutes(routes)
|
|
43
|
-
expect(result[0].component).toBe(Comp)
|
|
44
|
-
})
|
|
45
|
-
|
|
46
|
-
it('resolves /about.html variant', async () => {
|
|
47
|
-
setPath('/about.html')
|
|
48
|
-
const routes = [makeRoute('/about')]
|
|
49
|
-
const result = await processRoutes(routes)
|
|
50
|
-
expect(result[0].component).toBe(Comp)
|
|
51
|
-
})
|
|
52
|
-
|
|
53
|
-
it('resolves /about/index.html variant', async () => {
|
|
54
|
-
setPath('/about/index.html')
|
|
55
|
-
const routes = [makeRoute('/about')]
|
|
56
|
-
const result = await processRoutes(routes)
|
|
57
|
-
expect(result[0].component).toBe(Comp)
|
|
58
|
-
})
|
|
59
|
-
|
|
60
|
-
it('throws when no route matches', async () => {
|
|
61
|
-
setPath('/not-found')
|
|
62
|
-
const routes = [makeRoute('/')]
|
|
63
|
-
await expect(processRoutes(routes)).rejects.toThrow('No route found')
|
|
64
|
-
})
|
|
65
|
-
|
|
66
|
-
it('does not duplicate SSG paths on repeated calls', async () => {
|
|
67
|
-
setPath('/')
|
|
68
|
-
const route = makeRoute('/')
|
|
69
|
-
await processRoutes([route])
|
|
70
|
-
const countBefore = route.path.length
|
|
71
|
-
setPath('/index.html')
|
|
72
|
-
await processRoutes([route])
|
|
73
|
-
// /index.html should still only appear once
|
|
74
|
-
const indexHtmlCount = route.path.filter(p => p === '/index.html').length
|
|
75
|
-
expect(indexHtmlCount).toBe(1)
|
|
76
|
-
})
|
|
77
|
-
})
|