@userfrosting/sprinkle-core 6.0.0-beta.7 → 6.0.0-rc.1

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.
Files changed (67) hide show
  1. package/README.md +27 -46
  2. package/dist/Page401Unauthorized-D_EoXicK.js +11 -0
  3. package/dist/Page403Forbidden-C96BgRtx.js +11 -0
  4. package/dist/Page404NotFound-CIs-2Fy2.js +11 -0
  5. package/dist/PageError-Sk-QAv9A.js +11 -0
  6. package/dist/_plugin-vue_export-helper-CHgC5LLL.js +9 -0
  7. package/dist/composables/index.d.ts +4 -0
  8. package/dist/composables/useAxiosInterceptor.d.ts +10 -0
  9. package/dist/composables/useCsrf.d.ts +18 -0
  10. package/dist/composables/useRuleSchemaAdapter.d.ts +7 -0
  11. package/dist/composables/useSprunjer.d.ts +2 -0
  12. package/dist/composables.js +151 -0
  13. package/dist/index.d.ts +18 -0
  14. package/dist/index.js +15 -0
  15. package/{app/assets/interfaces/ApiResponse.ts → dist/interfaces/ApiResponse.d.ts} +5 -6
  16. package/{app/assets/interfaces/DictionaryApi.ts → dist/interfaces/DictionaryApi.d.ts} +9 -11
  17. package/dist/interfaces/alerts.d.ts +8 -0
  18. package/{app/assets/interfaces/common.ts → dist/interfaces/common.d.ts} +1 -1
  19. package/dist/interfaces/index.d.ts +13 -0
  20. package/{app/assets/interfaces/severity.ts → dist/interfaces/severity.d.ts} +9 -9
  21. package/dist/interfaces/sprunjer.d.ts +51 -0
  22. package/{app/assets/interfaces/sprunjerApi.ts → dist/interfaces/sprunjerApi.d.ts} +12 -15
  23. package/dist/interfaces.js +5 -0
  24. package/dist/routes/index.d.ts +16 -0
  25. package/dist/routes.js +41 -0
  26. package/dist/severity-DwLpzIij.js +4 -0
  27. package/{app/assets/stores/Helpers/PluralRules.ts → dist/stores/Helpers/PluralRules.d.ts} +17 -114
  28. package/dist/stores/index.d.ts +4 -0
  29. package/dist/stores/useAlertsStore.d.ts +29 -0
  30. package/dist/stores/useConfigStore.d.ts +11 -0
  31. package/dist/stores/usePageMeta.d.ts +51 -0
  32. package/dist/stores/useTranslator.d.ts +66 -0
  33. package/dist/stores.js +7 -0
  34. package/dist/useAlertsStore-Ca6nXz8C.js +179 -0
  35. package/dist/useAxiosInterceptor-DcOpTLHG.js +68 -0
  36. package/dist/views/Page401Unauthorized.vue.d.ts +2 -0
  37. package/dist/views/Page403Forbidden.vue.d.ts +2 -0
  38. package/dist/views/Page404NotFound.vue.d.ts +2 -0
  39. package/dist/views/PageError.vue.d.ts +2 -0
  40. package/package.json +37 -9
  41. package/app/assets/composables/index.ts +0 -4
  42. package/app/assets/composables/useAxiosInterceptor.ts +0 -30
  43. package/app/assets/composables/useCsrf.ts +0 -129
  44. package/app/assets/composables/useRuleSchemaAdapter.ts +0 -205
  45. package/app/assets/composables/useSprunjer.ts +0 -188
  46. package/app/assets/index.d.ts +0 -8
  47. package/app/assets/index.ts +0 -40
  48. package/app/assets/interfaces/alerts.ts +0 -16
  49. package/app/assets/interfaces/index.ts +0 -30
  50. package/app/assets/interfaces/sprunjer.ts +0 -60
  51. package/app/assets/routes/index.ts +0 -44
  52. package/app/assets/stores/index.ts +0 -4
  53. package/app/assets/stores/useAlertsStore.ts +0 -30
  54. package/app/assets/stores/useConfigStore.ts +0 -30
  55. package/app/assets/stores/usePageMeta.ts +0 -114
  56. package/app/assets/stores/useTranslator.ts +0 -293
  57. package/app/assets/tests/composables/useCsrf.test.ts +0 -212
  58. package/app/assets/tests/composables/useRuleSchemaAdapter.test.ts +0 -657
  59. package/app/assets/tests/interfaces/alerts.test.ts +0 -43
  60. package/app/assets/tests/plugin.test.ts +0 -29
  61. package/app/assets/tests/stores/Helpers/PluralRules.test.ts +0 -440
  62. package/app/assets/tests/stores/config.test.ts +0 -42
  63. package/app/assets/tests/stores/useTranslator.test.ts +0 -373
  64. package/app/assets/views/Page401Unauthorized.vue +0 -3
  65. package/app/assets/views/Page403Forbidden.vue +0 -3
  66. package/app/assets/views/Page404NotFound.vue +0 -3
  67. package/app/assets/views/PageError.vue +0 -3
@@ -1,373 +0,0 @@
1
- import { describe, expect, beforeEach, test, vi } from 'vitest'
2
- import { setActivePinia, createPinia } from 'pinia'
3
- import { DateTime, Settings } from 'luxon'
4
- import axios from 'axios'
5
- import { useTranslator } from '../../stores/useTranslator'
6
- import type { DictionaryConfig, DictionaryEntries, DictionaryResponse } from '../../interfaces'
7
-
8
- const testDictionaryConfig: DictionaryConfig = {
9
- name: 'English',
10
- regional: 'English',
11
- authors: ['UserFrosting'],
12
- plural_rule: 1,
13
- dates: 'en-US'
14
- }
15
-
16
- const testDictionaryConfigFr: DictionaryConfig = {
17
- name: 'French',
18
- regional: 'Français',
19
- authors: ['Malou'],
20
- plural_rule: 1,
21
- dates: 'fr-FR'
22
- }
23
-
24
- const testDictionaryEntries: DictionaryEntries = {
25
- YES: 'Yes',
26
- NO: 'No',
27
- WELCOME_TO: 'Welcome to {{title}}!',
28
- 'ERROR.@TRANSLATION': 'The Error',
29
- 'ERROR.TITLE': "We've sensed a great disturbance in the Force.",
30
- USERNAME: 'Username',
31
- 'COLOR.BLACK': 'black',
32
- 'COLOR.RED': 'red',
33
- 'COLOR.WHITE': 'white',
34
- 'COLOR.0': 'colors',
35
- 'COLOR.1': 'color',
36
- 'COLOR.2': 'colors',
37
- 'X_CARS.0': 'no cars',
38
- 'X_CARS.1': 'a car',
39
- 'X_CARS.2': '{{plural}} cars',
40
- 'X_HUNGRY_CATS.@PLURAL': 'num',
41
- 'X_HUNGRY_CATS.1': '{{num}} hungry cat',
42
- 'X_HUNGRY_CATS.2': '{{num}} hungry cats',
43
- X_FOO: '{{plural}}x foos',
44
- 'CAR.1': 'car',
45
- 'CAR.2': 'cars',
46
- 'CAR.GAS': 'gas',
47
- 'CAR.EV.@TRANSLATION': 'electric',
48
- 'CAR.EV.FULL': 'full electric',
49
- 'CAR.FULL_MODEL': '{{make}} {{model}} {{year}}',
50
- 'MY_CARS.@TRANSLATION': 'My cars',
51
- 'MY_CARS.1': 'I have a {{type}} {{&CAR}}',
52
- 'MY_CARS.2': 'I have {{plural}} {{type}} {{&CAR}}',
53
- TEST_LIMIT: 'Your test must be between {{min}} and {{max}} potatoes.',
54
- MIN: 'minimum',
55
- 'X_RULES.0': 'no rules',
56
- 'X_RULES.1': '{{plural}} rule', // No plural form for 2
57
- 'X_BANANAS.0': 'no bananas', // No plural form for 1, 2, etc.
58
- 'X_DOGS.5': 'five dogs',
59
- 'X_DOGS.101': '101 Dalmatians',
60
- 'X_DOGS.1000': 'An island of dogs'
61
- }
62
-
63
- const testDictionary: DictionaryResponse = {
64
- identifier: 'en_US',
65
- config: testDictionaryConfig,
66
- dictionary: testDictionaryEntries
67
- }
68
-
69
- const testDictionaryFr: DictionaryResponse = {
70
- identifier: 'fr_FR',
71
- config: testDictionaryConfigFr,
72
- dictionary: testDictionaryEntries
73
- }
74
-
75
- describe('API Tests', async () => {
76
- test('API should be saved', async () => {
77
- // Arrange
78
- setActivePinia(createPinia())
79
- const translator = useTranslator()
80
- const { translate, load } = translator
81
- const response = { data: testDictionary }
82
- vi.spyOn(axios, 'get').mockResolvedValue(response as any)
83
-
84
- // Assert initial state
85
- expect(translator.identifier).toBe('')
86
- expect(translator.config).toEqual({
87
- name: '',
88
- regional: '',
89
- authors: [],
90
- plural_rule: 0,
91
- dates: ''
92
- })
93
-
94
- // Act
95
- await load()
96
-
97
- // Assert config data
98
- expect(axios.get).toHaveBeenCalledWith('/api/dictionary')
99
- expect(translator.config).toEqual(testDictionaryConfig)
100
- expect(translator.identifier).toBe('en_US')
101
-
102
- // Assert basic translate method
103
- expect(translate('YES')).toBe('Yes')
104
- expect(translate('NO')).toBe('No')
105
- expect(translate('WELCOME_TO')).toBe('Welcome to {{title}}!')
106
- })
107
- })
108
-
109
- describe('Translator Tests', () => {
110
- beforeEach(async () => {
111
- // Arrange
112
- setActivePinia(createPinia())
113
- const translator = useTranslator()
114
- const { load } = translator
115
- const response = { data: testDictionary }
116
- vi.spyOn(axios, 'get').mockResolvedValue(response as any)
117
-
118
- // Act
119
- await load()
120
- })
121
-
122
- test('Should handle basic translation', () => {
123
- const { translate } = useTranslator()
124
- expect(translate('USERNAME')).toBe('Username')
125
- })
126
-
127
- test('Should handle key not existing', () => {
128
- const { translate } = useTranslator()
129
- expect(translate('NOT_EXIST')).toBe('NOT_EXIST')
130
- expect(translate('NOT EXIST')).toBe('NOT EXIST')
131
- })
132
-
133
- test('Should handle @TRANSLATION', () => {
134
- const { translate } = useTranslator()
135
- expect(translate('ERROR')).toBe('The Error')
136
- expect(translate('ERROR.TITLE')).toBe("We've sensed a great disturbance in the Force.")
137
- })
138
-
139
- test('Should handle placeholders', () => {
140
- const { translate } = useTranslator()
141
- expect(translate('WELCOME_TO')).toBe('Welcome to {{title}}!') // Placeholder not replaced
142
- expect(translate('WELCOME_TO', { title: 'UserFrosting' })).toBe('Welcome to UserFrosting!')
143
- expect(translate('WELCOME_TO', { bar: 'UserFrosting' })).toBe('Welcome to {{title}}!') // Wrong key
144
- expect(translate('WELCOME_TO', 'UserFrosting')).toBe('Welcome to {{title}}!') // Key not provided
145
- expect(translate('WELCOME_TO', { title: 'UserFrosting', foo: 'bar' })).toBe(
146
- 'Welcome to UserFrosting!'
147
- ) // Extra key not used
148
- })
149
-
150
- test('Should handle basic plurals', () => {
151
- const { translate } = useTranslator()
152
- expect(translate('X_CARS', 0)).toBe('no cars')
153
- expect(translate('X_CARS', 1)).toBe('a car')
154
- expect(translate('X_CARS', 2)).toBe('2 cars')
155
- expect(translate('X_CARS', 5)).toBe('5 cars')
156
- })
157
-
158
- test('Should handle pluralization with custom plural key', () => {
159
- const { translate } = useTranslator()
160
- expect(translate('X_HUNGRY_CATS', { num: 0 })).toBe('0 hungry cats')
161
- expect(translate('X_HUNGRY_CATS', { num: 1 })).toBe('1 hungry cat')
162
- expect(translate('X_HUNGRY_CATS', { num: 2 })).toBe('2 hungry cats')
163
- expect(translate('X_HUNGRY_CATS', { num: 5 })).toBe('5 hungry cats')
164
-
165
- // Custom key can also be omitted in the placeholder if it's the only
166
- // placeholder even with custom plural key
167
- expect(translate('X_HUNGRY_CATS', 5)).toBe('5 hungry cats')
168
-
169
- // Test missing pluralization and placeholder (default to 1)
170
- expect(translate('X_HUNGRY_CATS')).toBe('1 hungry cat')
171
- })
172
-
173
- test('Should handle plurals default, when no placeholder', () => {
174
- const { translate } = useTranslator()
175
- expect(translate('X_CARS')).toBe('a car')
176
- })
177
-
178
- test('Should handle Key With No Plural', () => {
179
- const { translate } = useTranslator()
180
- expect(translate('USERNAME', 123)).toBe('Username') // USERNAME has no placeholders
181
- expect(translate('X_FOO')).toBe('{{plural}}x foos') // 'X_FOO' doesn't have children, so it's not treated as a "pluralize-able" string
182
- expect(translate('X_FOO', { plural: 1 })).toBe('1x foos') // Replace {{plural}} with 1
183
- expect(translate('X_FOO', 1)).toBe('1x foos') // Replace {{plural}} with 1, without specifying the key
184
- expect(translate('X_FOO', 123)).toBe('123x foos') // Replace {{plural}} with 123
185
- })
186
-
187
- test('Should handle plurals for different plural rules', () => {
188
- const { translate } = useTranslator()
189
-
190
- // English plural rule is 1
191
- expect(translate('COLOR', 0)).toBe('colors')
192
- expect(translate('COLOR', 1)).toBe('color')
193
- expect(translate('COLOR', 2)).toBe('colors')
194
- expect(translate('COLOR', 3)).toBe('colors')
195
-
196
- // Same as above, but with a custom plural key (french)
197
- // Note "0" is plural (colors) in english, singular (couleur) in french !
198
- // TODO : Implement plurals & load custom locale
199
- // expect(translate('COLOR', 0)).toBe('colors')
200
- // expect(translate('COLOR', 1)).toBe('color')
201
- // expect(translate('COLOR', 2)).toBe('colors')
202
- // expect(translate('COLOR', 3)).toBe('colors')
203
- })
204
-
205
- test('Should handle a simple replacement when the key is not defined in dictionary', () => {
206
- const { translate } = useTranslator()
207
- expect(translate('You are {{status}}', { status: 'dumb' })).toBe('You are dumb')
208
- })
209
-
210
- test('Should handle @TRANSLATION when the key have plural rules', () => {
211
- const { translate } = useTranslator()
212
- expect(translate('MY_CARS')).toBe('My cars') // @TRANSLATION is used, not the 1 rule
213
- expect(translate('MY_CARS', 1)).toBe('I have a {{type}} car')
214
- expect(translate('MY_CARS', 2)).toBe('I have 2 {{type}} cars')
215
- })
216
-
217
- test('Should handle complex placeholders', () => {
218
- const { translate } = useTranslator()
219
- expect(translate('MY_CARS', { type: 'car' })).toBe('My cars')
220
- expect(translate('MY_CARS', { type: 'gaz', plural: 1 })).toBe('I have a gaz car')
221
- expect(translate('MY_CARS', { type: 'gaz', plural: 2 })).toBe('I have 2 gaz cars')
222
- expect(translate('MY_CARS', { type: '&CAR.GAS', plural: 2 })).toBe('I have 2 gas cars')
223
- expect(translate('MY_CARS', { type: '&CAR.EV', plural: 2 })).toBe('I have 2 electric cars')
224
- expect(translate('MY_CARS', { type: '&CAR.EV.FULL', plural: 1 })).toBe(
225
- 'I have a full electric car'
226
- )
227
- expect(
228
- translate('MY_CARS', {
229
- type: '&CAR.FULL_MODEL',
230
- plural: 5,
231
- make: 'Toyota',
232
- model: 'Camry',
233
- year: 2022
234
- })
235
- ).toBe('I have 5 Toyota Camry 2022 cars')
236
- })
237
-
238
- // Test basic placeholder replacement using int as placeholder value (So they don't try to translate "min" and "max")
239
- // We don't want to end up with "Your test must be between _minimum_ and 200 potatoes"
240
- test('Should handle placeholder not overwritten by other key', () => {
241
- const { translate } = useTranslator()
242
- expect(translate('TEST_LIMIT', { min: 4, max: 200 })).toBe(
243
- 'Your test must be between 4 and 200 potatoes.'
244
- )
245
- })
246
-
247
- // 2 will return singular as the plural is not defined
248
- test('Should handle pluralization with no rules', () => {
249
- const { translate } = useTranslator()
250
- expect(translate('X_RULES', 0)).toBe('no rules')
251
- expect(translate('X_RULES', 1)).toBe('1 rule')
252
- expect(translate('X_RULES', 2)).toBe('2 rule')
253
-
254
- // X_BANANAS
255
- expect(translate('X_BANANAS', 0)).toBe('no bananas')
256
- expect(translate('X_BANANAS', 1)).toBe('no bananas')
257
- expect(translate('X_BANANAS', 2)).toBe('no bananas')
258
- expect(translate('X_BANANAS', 5)).toBe('no bananas')
259
- })
260
-
261
- // The keys are int, but don't follow the rules. It will fallback to the literal key
262
- test("Should handle plurals who doesn't follow the rules", () => {
263
- const { translate } = useTranslator()
264
- expect(translate('X_DOGS')).toBe('X_DOGS')
265
- expect(translate('X_DOGS', 0)).toBe('X_DOGS')
266
- expect(translate('X_DOGS', 1)).toBe('X_DOGS')
267
- expect(translate('X_DOGS', 2)).toBe('X_DOGS') // No plural rules found
268
- expect(translate('X_DOGS', 5)).toBe('five dogs') // This one is hardcoded and will fallback as normal string key
269
- expect(translate('X_DOGS', 101)).toBe('101 Dalmatians') // Same here
270
- expect(translate('X_DOGS', 102)).toBe('X_DOGS') // This one is not hardcoded
271
- expect(translate('X_DOGS', 1000)).toBe('An island of dogs') // Still fallback, if the key is a string representing and INT
272
- })
273
- })
274
-
275
- describe('Date Tests', async () => {
276
- test('translateDate should return the correct values in English', async () => {
277
- // Arrange
278
- setActivePinia(createPinia())
279
- const translator = useTranslator()
280
- const { translateDate, load, getDateTime } = translator
281
- const response = { data: testDictionary }
282
- vi.spyOn(axios, 'get').mockResolvedValue(response as any)
283
- await load()
284
-
285
- // Assert basic translate method
286
- // Force the default timezone, so the test is consistent regardless of
287
- // the timezone of the runner
288
- Settings.defaultZone = 'America/New_York'
289
- expect(translateDate('2025-02-02T14:42:12.000000Z')).toBe('Sun, Feb 2, 2025, 9:42 AM')
290
- expect(translateDate('2025-02-02T14:42:12.000000Z', 'DDD')).toBe('February 2, 2025')
291
- expect(translateDate('2025-02-02T14:42:12.000000Z', DateTime.DATETIME_MED)).toBe(
292
- 'Feb 2, 2025, 9:42 AM'
293
- )
294
-
295
- // Test get the Datetime object
296
- expect(getDateTime('2025-02-02T14:42:12.000000Z').monthLong).toBe('February')
297
- })
298
-
299
- test('translateDate should return the correct values in French', async () => {
300
- // Arrange
301
- setActivePinia(createPinia())
302
- const translator = useTranslator()
303
- const { translateDate, load, getDateTime } = translator
304
- const response = { data: testDictionaryFr }
305
- vi.spyOn(axios, 'get').mockResolvedValue(response as any)
306
- await load()
307
-
308
- // Assert basic translate method
309
- // Force the default timezone, so the test is consistent regardless of
310
- // the timezone of the runner
311
- Settings.defaultZone = 'America/New_York'
312
- expect(translateDate('2025-02-02T14:42:12.000000Z')).toBe('dim. 2 févr. 2025, 09:42')
313
- expect(translateDate('2025-02-02T14:42:12.000000Z', 'DDD')).toBe('2 février 2025')
314
- expect(translateDate('2025-02-02T14:42:12.000000Z', DateTime.DATETIME_MED)).toBe(
315
- '2 févr. 2025, 09:42'
316
- )
317
-
318
- // Test get the Datetime object
319
- expect(getDateTime('2025-02-02T14:42:12.000000Z').monthLong).toBe('février')
320
- })
321
- })
322
-
323
- describe('Plural Form Tests', () => {
324
- beforeEach(async () => {
325
- // Arrange
326
- setActivePinia(createPinia())
327
- const translator = useTranslator()
328
- const { load } = translator
329
- const response = { data: testDictionary }
330
- vi.spyOn(axios, 'get').mockResolvedValue(response as any)
331
-
332
- // Act
333
- await load()
334
- })
335
-
336
- test('Should return correct plural form for rule 1', () => {
337
- const { getPluralForm } = useTranslator()
338
- expect(getPluralForm(0, 1)).toBe(2)
339
- expect(getPluralForm(1, 1)).toBe(1)
340
- expect(getPluralForm(2, 1)).toBe(2)
341
- expect(getPluralForm(20, 1)).toBe(2)
342
- })
343
-
344
- test('Should return correct plural form for rule 2', () => {
345
- const { getPluralForm } = useTranslator()
346
- expect(getPluralForm(0, 2)).toBe(1) // French = zero is singular
347
- expect(getPluralForm(1, 2)).toBe(1)
348
- expect(getPluralForm(2, 2)).toBe(2)
349
- expect(getPluralForm(20, 2)).toBe(2)
350
- })
351
-
352
- test('Should throw error for invalid rule', () => {
353
- const { getPluralForm } = useTranslator()
354
- expect(() => getPluralForm(1, 16)).toThrow('The rule number 16 must be between 0 and 15')
355
- })
356
-
357
- test('Should use default rule if forceRule is not provided', () => {
358
- const { getPluralForm } = useTranslator()
359
- expect(getPluralForm(0)).toBe(2)
360
- expect(getPluralForm(1)).toBe(1)
361
- expect(getPluralForm(2)).toBe(2)
362
- })
363
-
364
- test('Should used the specified rule if forceRule is provided', () => {
365
- const { getPluralForm } = useTranslator()
366
- expect(getPluralForm(0, 1)).toBe(2) // English
367
- expect(getPluralForm(0, 2)).toBe(1) // French
368
- expect(getPluralForm(5, 1)).toBe(2) // English
369
- expect(getPluralForm(5, 8)).toBe(3) // Slavic
370
- })
371
- })
372
-
373
- // TODO : Test each plural rules
@@ -1,3 +0,0 @@
1
- <template>
2
- <UFErrorPage errorCode="401" />
3
- </template>
@@ -1,3 +0,0 @@
1
- <template>
2
- <UFErrorPage errorCode="403" />
3
- </template>
@@ -1,3 +0,0 @@
1
- <template>
2
- <UFErrorPage errorCode="404" />
3
- </template>
@@ -1,3 +0,0 @@
1
- <template>
2
- <UFErrorPage />
3
- </template>