fcad-core-dragon 2.1.0 → 2.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.
Files changed (160) hide show
  1. package/.editorconfig +7 -7
  2. package/.gitlab-ci.yml +124 -0
  3. package/.prettierrc +11 -11
  4. package/.vscode/extensions.json +8 -8
  5. package/.vscode/settings.json +46 -16
  6. package/CHANGELOG +520 -520
  7. package/README.md +57 -57
  8. package/documentation/.vitepress/config.js +114 -114
  9. package/documentation/api-examples.md +49 -49
  10. package/documentation/composants/app-base-button.md +58 -58
  11. package/documentation/composants/app-base-error-display.md +59 -59
  12. package/documentation/composants/app-base-popover.md +68 -68
  13. package/documentation/composants/app-comp-audio.md +75 -75
  14. package/documentation/composants/app-comp-branch-buttons.md +111 -111
  15. package/documentation/composants/app-comp-button-progress.md +53 -53
  16. package/documentation/composants/app-comp-carousel.md +53 -53
  17. package/documentation/composants/app-comp-container.md +53 -53
  18. package/documentation/composants/app-comp-input-checkbox-next.md +42 -42
  19. package/documentation/composants/app-comp-input-dropdown-next.md +34 -34
  20. package/documentation/composants/app-comp-input-radio-next.md +39 -39
  21. package/documentation/composants/app-comp-input-text-next.md +35 -35
  22. package/documentation/composants/app-comp-input-text-table-next.md +34 -34
  23. package/documentation/composants/app-comp-input-text-to-fill-dropdown-next.md +53 -53
  24. package/documentation/composants/app-comp-input-text-to-fill-next.md +31 -31
  25. package/documentation/composants/app-comp-jauge.md +31 -31
  26. package/documentation/composants/app-comp-menu-item.md +55 -55
  27. package/documentation/composants/app-comp-menu.md +29 -29
  28. package/documentation/composants/app-comp-navigation.md +41 -41
  29. package/documentation/composants/app-comp-note-call.md +53 -53
  30. package/documentation/composants/app-comp-note-credit.md +53 -53
  31. package/documentation/composants/app-comp-play-bar-next.md +53 -53
  32. package/documentation/composants/app-comp-pop-up-next.md +93 -93
  33. package/documentation/composants/app-comp-quiz-next.md +235 -235
  34. package/documentation/composants/app-comp-quiz-recall.md +53 -53
  35. package/documentation/composants/app-comp-svg-next.md +53 -53
  36. package/documentation/composants/app-comp-table-of-content.md +50 -50
  37. package/documentation/composants/app-comp-video-player.md +82 -82
  38. package/documentation/composants.md +46 -46
  39. package/documentation/composants_critiques/ModelPageComposant.md +53 -53
  40. package/documentation/composants_critiques/app-base-module.md +43 -43
  41. package/documentation/composants_critiques/app-base-page.md +48 -48
  42. package/documentation/composants_critiques/app-base.md +311 -311
  43. package/documentation/composants_critiques/main.md +15 -15
  44. package/documentation/demarrage.md +50 -50
  45. package/documentation/deploiement.md +57 -57
  46. package/documentation/index.md +33 -33
  47. package/documentation/markdown-examples.md +85 -85
  48. package/documentation/public/vite.svg +14 -14
  49. package/documentation/public/vuejs.svg +1 -1
  50. package/documentation/public/vuetify.svg +5 -5
  51. package/eslint.config.js +60 -60
  52. package/junit-report.xml +182 -0
  53. package/package.json +66 -59
  54. package/playwright/index.html +12 -0
  55. package/playwright/index.js +21 -0
  56. package/playwright-ct.config.js +95 -0
  57. package/src/$locales/en.json +157 -157
  58. package/src/$locales/fr.json +120 -120
  59. package/src/assets/data/onboardingMessages.json +47 -47
  60. package/src/components/AppBase.vue +1171 -1169
  61. package/src/components/AppBaseButton.vue +90 -95
  62. package/src/components/AppBaseErrorDisplay.vue +438 -438
  63. package/src/components/AppBaseFlipCard.vue +84 -84
  64. package/src/components/AppBaseModule.vue +1639 -1634
  65. package/src/components/AppBasePage.vue +3 -2
  66. package/src/components/AppBasePopover.vue +41 -41
  67. package/src/components/AppBaseSkeleton.vue +66 -66
  68. package/src/components/AppCompAudio.vue +261 -256
  69. package/src/components/AppCompBranchButtons.vue +508 -508
  70. package/src/components/AppCompButtonProgress.vue +137 -132
  71. package/src/components/AppCompCarousel.vue +342 -336
  72. package/src/components/AppCompContainer.vue +29 -29
  73. package/src/components/AppCompInputCheckBoxNx.vue +325 -323
  74. package/src/components/AppCompInputDropdownNx.vue +302 -299
  75. package/src/components/AppCompInputRadioNx.vue +287 -284
  76. package/src/components/AppCompInputTextNx.vue +156 -153
  77. package/src/components/AppCompInputTextTableNx.vue +205 -202
  78. package/src/components/AppCompInputTextToFillDropdownNx.vue +343 -340
  79. package/src/components/AppCompInputTextToFillNx.vue +316 -313
  80. package/src/components/AppCompJauge.vue +81 -81
  81. package/src/components/AppCompMenu.vue +6 -2
  82. package/src/components/AppCompMenuItem.vue +246 -240
  83. package/src/components/AppCompNavigation.vue +977 -972
  84. package/src/components/AppCompNoteCall.vue +167 -161
  85. package/src/components/AppCompNoteCredit.vue +496 -491
  86. package/src/components/AppCompPlayBarNext.vue +2290 -2288
  87. package/src/components/AppCompPopUpNext.vue +508 -504
  88. package/src/components/AppCompQuizNext.vue +515 -510
  89. package/src/components/AppCompQuizRecall.vue +355 -350
  90. package/src/components/AppCompSVGNext.vue +346 -346
  91. package/src/components/AppCompSettingsMenu.vue +177 -172
  92. package/src/components/AppCompTableOfContent.vue +433 -427
  93. package/src/components/AppCompVideoPlayer.vue +377 -377
  94. package/src/components/AppCompViewDisplay.vue +6 -6
  95. package/src/components/BaseModule.vue +55 -55
  96. package/src/composables/useIdleDetector.js +56 -56
  97. package/src/composables/useQuiz.js +89 -89
  98. package/src/composables/useTimer.js +172 -172
  99. package/src/directives/nvdaFix.js +53 -53
  100. package/src/externalComps/ModuleView.vue +22 -22
  101. package/src/externalComps/SummaryView.vue +91 -91
  102. package/src/main.js +493 -476
  103. package/src/module/stores/appStore.js +960 -947
  104. package/src/module/xapi/ADL.js +520 -520
  105. package/src/module/xapi/Crypto/Hasher.js +241 -241
  106. package/src/module/xapi/Crypto/WordArray.js +278 -278
  107. package/src/module/xapi/Crypto/algorithms/BufferedBlockAlgorithm.js +103 -103
  108. package/src/module/xapi/Crypto/algorithms/C_algo.js +315 -315
  109. package/src/module/xapi/Crypto/algorithms/HMAC.js +9 -9
  110. package/src/module/xapi/Crypto/algorithms/SHA1.js +9 -9
  111. package/src/module/xapi/Crypto/encoders/Base.js +105 -105
  112. package/src/module/xapi/Crypto/encoders/Base64.js +99 -99
  113. package/src/module/xapi/Crypto/encoders/Hex.js +61 -61
  114. package/src/module/xapi/Crypto/encoders/Latin1.js +61 -61
  115. package/src/module/xapi/Crypto/encoders/Utf8.js +45 -45
  116. package/src/module/xapi/Crypto/index.js +53 -53
  117. package/src/module/xapi/Statement/activity.js +47 -47
  118. package/src/module/xapi/Statement/agent.js +55 -55
  119. package/src/module/xapi/Statement/group.js +26 -26
  120. package/src/module/xapi/Statement/index.js +259 -259
  121. package/src/module/xapi/Statement/statement.js +253 -253
  122. package/src/module/xapi/Statement/statementRef.js +23 -23
  123. package/src/module/xapi/Statement/substatement.js +22 -22
  124. package/src/module/xapi/Statement/verb.js +36 -36
  125. package/src/module/xapi/activitytypes.js +17 -17
  126. package/src/module/xapi/launch.js +157 -157
  127. package/src/module/xapi/utils.js +167 -167
  128. package/src/module/xapi/verbs.js +294 -294
  129. package/src/module/xapi/wrapper.js +1895 -1895
  130. package/src/module/xapi/xapiStatement.js +444 -444
  131. package/src/plugins/analytics.js +34 -34
  132. package/src/plugins/bus.js +12 -8
  133. package/src/plugins/gsap.js +17 -15
  134. package/src/plugins/helper.js +355 -358
  135. package/src/plugins/i18n.js +27 -26
  136. package/src/plugins/idb.js +227 -227
  137. package/src/plugins/save.js +37 -37
  138. package/src/plugins/scorm.js +287 -287
  139. package/src/plugins/xapi.js +11 -11
  140. package/src/public/index.html +33 -33
  141. package/src/router/index.js +57 -57
  142. package/src/router/routes.js +312 -312
  143. package/src/shared/generalfuncs.js +344 -344
  144. package/src/shared/validators.js +1018 -1018
  145. package/tests/component/AppBaseButton.spec.js +53 -0
  146. package/tests/component/pinia.spec.js +24 -0
  147. package/{src/components/tests__ → tests/unit}/AppBaseButton.spec.js +53 -53
  148. package/tests/unit/AppCompInputCheckBoxNx.spec.js +59 -0
  149. package/tests/unit/AppCompInputDropdownNx.spec.js +51 -0
  150. package/tests/unit/AppCompInputRadioNx.spec.js +59 -0
  151. package/tests/unit/AppCompInputTextNx.spec.js +44 -0
  152. package/tests/unit/AppCompInputTextTableNx.spec.js +77 -0
  153. package/tests/unit/AppCompInputTextToFillDropdownNx.spec.js +60 -0
  154. package/tests/unit/AppCompInputTextToFillNx.spec.js +45 -0
  155. package/tests/unit/AppCompQuizNext.spec.js +114 -0
  156. package/tests/unit/AppCompVideoPlayer.spec.js +177 -0
  157. package/{src/components/tests__ → tests/unit}/useTimer.spec.js +91 -91
  158. package/vitest.config.js +28 -19
  159. package/vitest.setup.js +28 -0
  160. package/src/components/AppBaseButton.test.js +0 -21
@@ -0,0 +1,114 @@
1
+ import { mount } from '@vue/test-utils'
2
+ import { beforeEach, describe, it, expect, vi } from 'vitest'
3
+ import { createTestingPinia } from '@pinia/testing'
4
+
5
+ // Les mocks doivent être "hoistés" (déclarés avant l'import du composant)
6
+ beforeEach(() => {
7
+ vi.mock('@/shared/generalfuncs.js', () => ({
8
+ fileAssets: { getActivities: () => [] }
9
+ }))
10
+ vi.mock('@/router/routes.js', () => ({
11
+ mappedFiles: []
12
+ }))
13
+ vi.mock('@/router/index.js', () => ({
14
+ default: { push: vi.fn(), currentRoute: { value: {} } }
15
+ }))
16
+ vi.mock('@/shared/validators.js', () => ({
17
+ validateObjType: () => true,
18
+ validateString: () => true,
19
+ validateQuizData: () => ({ errorInConsole: [] }),
20
+ validateNumber: () => true
21
+ }))
22
+ })
23
+
24
+ import AppCompQuizNext from '@/components/AppCompQuizNext.vue'
25
+
26
+ describe('AppCompQuizNext', () => {
27
+ const stubs = {
28
+ AppCompInputRadio: {
29
+ template: '<div class="stub-radio"></div>',
30
+ props: ['modelValue', 'inputData', 'id', 'solution']
31
+ },
32
+ AppCompInputTextNx: {
33
+ template: '<input class="stub-text" />',
34
+ props: ['modelValue']
35
+ },
36
+ AppBaseErrorDisplay: {
37
+ template: '<div class="stub-error"></div>',
38
+ props: {
39
+ /* ... (props omises pour la clarté) ... */
40
+ }
41
+ },
42
+ AppBaseSkeleton: { template: '<div class="stub-skeleton"></div>' },
43
+ AppBaseButton: { template: '<button class="stub-button"></button>' }
44
+ }
45
+
46
+ it('rend un input radio si la question est de type choix_unique', () => {
47
+ const quizData = {
48
+ id: 1,
49
+ type_question: 'choix_unique',
50
+ ennonce: 'Quel est la deuxieme lettre de alphabet',
51
+ mode: ['A', 'B', 'C'],
52
+ solution: ['B']
53
+ }
54
+
55
+ const wrapper = mount(AppCompQuizNext, {
56
+ props: {
57
+ modelValue: ['A', 'B', 'C'],
58
+ consigne: false,
59
+ shuffleAnswers: false,
60
+ quizData: quizData // Passez la variable ici
61
+ },
62
+ global: {
63
+ stubs,
64
+ plugins: [createTestingPinia({ createSpy: vi.fn })],
65
+ provide: {
66
+ userInteraction: {
67
+ recordAnswer: vi.fn(),
68
+ hasInteracted: vi.fn(),
69
+ quizAnswers: {
70
+ [quizData.id]: { value: ['A'], total_attempts: 0 }
71
+ }
72
+ }
73
+ }
74
+ }
75
+ })
76
+
77
+ expect(wrapper.find('.stub-radio').exists()).toBe(true)
78
+ })
79
+
80
+ // it('rend un input texte si la question est de type text', () => {
81
+ // const wrapper = mount(AppCompQuizNext, {
82
+ // props: {
83
+ // question: {
84
+ // id: 2,
85
+ // type: 'text'
86
+ // },
87
+ // modelValue: ''
88
+ // },
89
+ // global: { stubs }
90
+ // })
91
+
92
+ // expect(wrapper.find('.stub-text').exists()).toBe(true)
93
+ // })
94
+
95
+ // it('émet update:modelValue quand l’utilisateur répond', async () => {
96
+ // const wrapper = mount(AppCompQuizNext, {
97
+ // props: {
98
+ // question: { id: 1, type: 'text' },
99
+ // modelValue: ''
100
+ // },
101
+ // global: {
102
+ // stubs: {
103
+ // AppCompInputTextNx: {
104
+ // template: `<input @input="$emit('update:modelValue', 'hello')" />`
105
+ // }
106
+ // }
107
+ // }
108
+ // })
109
+
110
+ // await wrapper.find('input').trigger('input')
111
+
112
+ // expect(wrapper.emitted()['update:modelValue'][0]).toEqual(['hello'])
113
+ // })
114
+ })
@@ -0,0 +1,177 @@
1
+ import { mount } from '@vue/test-utils'
2
+ import { beforeEach, describe, it, test, expect, vi } from 'vitest'
3
+ import { createTestingPinia } from '@pinia/testing'
4
+
5
+ let dummyProps = {}
6
+
7
+ //TODO: Logic de test pour le composant videos
8
+ // 1- valider que composant gerer bien les props reçu-OK
9
+ // 2- valider que le composant produit une balise HTML video en cas de success -OK
10
+ // 3- valider le rendu du composant en cas d'erreur (errorDysplay) -OK
11
+ // 4- Valider que le validateur de video active bien sur les datas reçu -OK
12
+ // 5- valider certaine methode clée fonctionne
13
+
14
+ beforeEach(() => {
15
+ dummyProps = {
16
+ id: 'P01',
17
+ activityRef: 'A03',
18
+ title: 'Lecteurs médias',
19
+ type: 'pg_normal',
20
+ videosData: [
21
+ {
22
+ id: 'vid1',
23
+ mSources: [
24
+ {
25
+ type: 'mp4',
26
+ src: 'exemple_video.mp4'
27
+ }
28
+ ],
29
+ mSubtitles: [
30
+ {
31
+ label: 'Français',
32
+ src: 'exemple_soustitres.vtt',
33
+ srclang: 'fr'
34
+ }
35
+ ],
36
+ mPoster: 'video_poster.jpg',
37
+ mTranscript: 'exemple_transcript.html' // The file MUST be host in the public folder of the project
38
+ }
39
+ ]
40
+ }
41
+ vi.mock('@/shared/generalfuncs.js', () => ({
42
+ fileAssets: { getActivities: () => [] }
43
+ }))
44
+
45
+ vi.mock('@/shared/validators.js', () => ({
46
+ validateObjType: () => true,
47
+ validateString: () => true,
48
+ validateVideoData: vi.fn(),
49
+ validateNumber: () => true
50
+ }))
51
+
52
+ vi.mock('@/module/stores/appStore', () => {
53
+ return {
54
+ useAppStore: () => ({
55
+ getCurrentPage: {
56
+ id: 'P01',
57
+ activityRef: 'A01',
58
+ videosData: [
59
+ {
60
+ id: 'vid1',
61
+ mSources: [
62
+ {
63
+ type: 'mp4',
64
+ src: ''
65
+ }
66
+ ],
67
+ mSubtitles: [
68
+ {
69
+ label: 'Français',
70
+ src: '',
71
+ srclang: 'fr'
72
+ }
73
+ ],
74
+ mPoster: '',
75
+ mTranscript: 'exemple_transcript.html' // The file MUST be host in the public folder of the project
76
+ }
77
+ ]
78
+ },
79
+ getPageInteraction: () => ({ ...dummyProps }),
80
+ getUserInteraction: () => ({ ...dummyProps })
81
+ })
82
+ }
83
+ })
84
+ })
85
+
86
+ import AppCompVideoPlayer from '@/components/AppCompVideoPlayer.vue'
87
+ // import AppBaseErrorDisplay from '@/components/AppBaseErrorDisplay.vue'
88
+ import { validateVideoData } from '@/shared/validators.js'
89
+
90
+ describe('AppCompVideoPlayer', () => {
91
+ const stubs = {
92
+ AppBaseErrorDisplay: {
93
+ name: 'AppBaseErrorDisplay',
94
+ template: '<div class="stub-error"></div>',
95
+ props: {
96
+ /* ... (props omises pour la clarté) ... */
97
+ }
98
+ },
99
+ AppBaseSkeleton: { template: '<div class="stub-skeleton"></div>' },
100
+ AppBaseButton: { template: '<button class="stub-button"></button>' }
101
+ }
102
+ test('Validate received props', () => {
103
+ validateVideoData.mockReturnValue([])
104
+ const { videosData } = dummyProps
105
+ mount(AppCompVideoPlayer, {
106
+ props: { vidData: videosData[0] }
107
+ })
108
+
109
+ expect(validateVideoData).toHaveBeenCalledWith({
110
+ id: 'vid1',
111
+ mSources: [
112
+ {
113
+ type: 'mp4',
114
+ src: 'exemple_video.mp4'
115
+ }
116
+ ],
117
+ mSubtitles: [
118
+ {
119
+ label: 'Français',
120
+ src: 'exemple_soustitres.vtt',
121
+ srclang: 'fr'
122
+ }
123
+ ],
124
+ mPoster: 'video_poster.jpg',
125
+ mTranscript: 'exemple_transcript.html' // The file MUST be host in the public folder of the project
126
+ })
127
+ })
128
+
129
+ it('renders ErrorDisplay When validator returns errors', () => {
130
+ // simulate invalid props
131
+ validateVideoData.mockReturnValue([
132
+ 'WARNING!>>> VIDEO: 💥 Invalid declaration for video element'
133
+ ])
134
+
135
+ const wrapper = mount(AppCompVideoPlayer, {
136
+ props: { vidData: { id: null } },
137
+ global: { stubs, plugins: [createTestingPinia({ createSpy: vi.fn })] }
138
+ })
139
+
140
+ expect(
141
+ wrapper.findComponent({ name: 'AppBaseErrorDisplay' }).exists()
142
+ ).toBe(true)
143
+ expect(wrapper.find('video').exists()).toBe(false)
144
+ })
145
+
146
+ it('It renders HTML video Element', () => {
147
+ validateVideoData.mockReturnValue([]) // no Error
148
+ const wrapper = mount(AppCompVideoPlayer, {
149
+ props: { vidData: { id: null } },
150
+ global: { stubs }
151
+ })
152
+ expect(
153
+ wrapper.findComponent({ name: 'AppBaseErrorDisplay' }).exists()
154
+ ).toBe(false)
155
+ expect(wrapper.find('video').exists()).toBe(true)
156
+ })
157
+
158
+ it('Video Element has correct sources', async () => {
159
+ const { videosData } = dummyProps
160
+ validateVideoData.mockReturnValue([]) // no Error
161
+ const wrapper = mount(AppCompVideoPlayer, {
162
+ props: { vidData: videosData[0] },
163
+ global: { stubs }
164
+ })
165
+
166
+ expect(wrapper.vm.vidSources).toEqual(videosData[0].mSources)
167
+
168
+ //sources are rendered in the DOM
169
+ await wrapper.vm.$nextTick()
170
+ const sources = wrapper.findAll('source')
171
+ expect(sources.length).toBe(1)
172
+
173
+ // --- 3) valider les valeurs réelles rendues ---
174
+ expect(sources[0].attributes().src).toBe(videosData[0].mSources[0].src)
175
+ expect(sources[0].attributes().type).toBe('video/mp4')
176
+ })
177
+ })
@@ -1,91 +1,91 @@
1
- import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
2
- import { Timer } from '../../composables/useTimer'
3
-
4
- // vitest vi utility ref: https://vitest.dev/api/vi.html
5
- // vi fakeTimers ref: https://vitest.dev/api/vi.html#vi-usefaketimers
6
-
7
- describe('Timer class', () => {
8
- let timer
9
-
10
- beforeEach(() => {
11
- Timer.timers.clear() // Clear existing timers before each test
12
- timer = new Timer()
13
- vi.useFakeTimers() // Enable fake timers before each test
14
- })
15
-
16
- afterEach(() => {
17
- timer.destroy() // destroy created timer after each test
18
- vi.useRealTimers() // Restore real timers after each test
19
- })
20
-
21
- it('should create a Timer instance with a unique ID', () => {
22
- const timer2 = new Timer() //create a second instance
23
- expect(timer2.getTimerID()).not.toBe(timer.getTimerID())
24
- expect(Timer.timers.size).toBe(2) // Two timers should be registered
25
- })
26
-
27
- it('should return a timer instance by it ID', () => {
28
- const timer2 = new Timer('timer22') //create a second instance with custom ID
29
- expect(timer.getTimer('timer22')).toBeInstanceOf(Timer)
30
- })
31
-
32
- it('should throw error when a timer instance doesn`t exist', () => {
33
- const id = 'noExistingTimer'
34
- expect(() => timer.getTimer(id)).toThrowError(
35
- `Timer with ID ${id} does not exist.`
36
- )
37
- })
38
-
39
- it('should initialize timer at 0 seconds', () => {
40
- expect(timer.getTime()).toBe(0)
41
- })
42
-
43
- it('should start the timer and increment time every second', () => {
44
- timer.start()
45
- expect(timer.getTimerState()).toBe('started')
46
-
47
- vi.advanceTimersByTime(5000) // Fast-forward 3 seconds
48
- expect(timer.getTime()).toBe(5) // Should be approximately 5 seconds
49
- })
50
-
51
- it('should display time in correct format when converting to ISOformats', () => {
52
- timer.start()
53
- expect(timer.getTimerState()).toBe('started')
54
-
55
- vi.advanceTimersByTime(65000) // Fast-forward 3 seconds
56
-
57
- const fullISOFormat = '1970-01-01T00:01:05.000Z'
58
- const isoTimeStr = '00:01:05'
59
-
60
- expect(timer.getTime()).toBe(65) // Should be approximately 1 min 5 seconds (00:01:05)
61
- expect(timer.formatToISOString(timer.getTime())).toBe(fullISOFormat)
62
- expect(timer.ISOTimeParser(timer.getTime())).toBe(isoTimeStr)
63
- })
64
-
65
- it('should pause the timer', () => {
66
- timer.start()
67
- vi.advanceTimersByTime(2000)
68
- timer.pause()
69
- const current = timer.getTime()
70
-
71
- // Advance time but timer should stay paused
72
- vi.advanceTimersByTime(3000)
73
- expect(timer.getTime()).toBe(current) // about 2 seconds
74
- expect(timer.ISOTimeParser(timer.getTime())).toBe('00:00:02')
75
- })
76
-
77
- it('should reset the timer to 0 when timer stops', () => {
78
- timer.start()
79
- vi.advanceTimersByTime(4000)
80
- timer.stop()
81
- expect(timer.getTime()).toBe(0)
82
- expect(timer.getTimerState()).toBe('stopped')
83
- })
84
-
85
- it('should destroy timer', () => {
86
- const id = timer.getTimerID()
87
- timer.destroy()
88
- expect(Timer.timers.get(id)).toBeUndefined()
89
- expect(Timer.timers.size).toBe(0)
90
- })
91
- })
1
+ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
2
+ import { Timer } from '../../src/composables/useTimer.js'
3
+
4
+ // vitest vi utility ref: https://vitest.dev/api/vi.html
5
+ // vi fakeTimers ref: https://vitest.dev/api/vi.html#vi-usefaketimers
6
+
7
+ describe('Timer class', () => {
8
+ let timer
9
+
10
+ beforeEach(() => {
11
+ Timer.timers.clear() // Clear existing timers before each test
12
+ timer = new Timer()
13
+ vi.useFakeTimers() // Enable fake timers before each test
14
+ })
15
+
16
+ afterEach(() => {
17
+ timer.destroy() // destroy created timer after each test
18
+ vi.useRealTimers() // Restore real timers after each test
19
+ })
20
+
21
+ it('should create a Timer instance with a unique ID', () => {
22
+ const timer2 = new Timer() //create a second instance
23
+ expect(timer2.getTimerID()).not.toBe(timer.getTimerID())
24
+ expect(Timer.timers.size).toBe(2) // Two timers should be registered
25
+ })
26
+
27
+ it('should return a timer instance by it ID', () => {
28
+ const timer2 = new Timer('timer22') //create a second instance with custom ID
29
+ expect(timer.getTimer('timer22')).toBeInstanceOf(Timer)
30
+ })
31
+
32
+ it('should throw error when a timer instance doesn`t exist', () => {
33
+ const id = 'noExistingTimer'
34
+ expect(() => timer.getTimer(id)).toThrowError(
35
+ `Timer with ID ${id} does not exist.`
36
+ )
37
+ })
38
+
39
+ it('should initialize timer at 0 seconds', () => {
40
+ expect(timer.getTime()).toBe(0)
41
+ })
42
+
43
+ it('should start the timer and increment time every second', () => {
44
+ timer.start()
45
+ expect(timer.getTimerState()).toBe('started')
46
+
47
+ vi.advanceTimersByTime(5000) // Fast-forward 3 seconds
48
+ expect(timer.getTime()).toBe(5) // Should be approximately 5 seconds
49
+ })
50
+
51
+ it('should display time in correct format when converting to ISOformats', () => {
52
+ timer.start()
53
+ expect(timer.getTimerState()).toBe('started')
54
+
55
+ vi.advanceTimersByTime(65000) // Fast-forward 3 seconds
56
+
57
+ const fullISOFormat = '1970-01-01T00:01:05.000Z'
58
+ const isoTimeStr = '00:01:05'
59
+
60
+ expect(timer.getTime()).toBe(65) // Should be approximately 1 min 5 seconds (00:01:05)
61
+ expect(timer.formatToISOString(timer.getTime())).toBe(fullISOFormat)
62
+ expect(timer.ISOTimeParser(timer.getTime())).toBe(isoTimeStr)
63
+ })
64
+
65
+ it('should pause the timer', () => {
66
+ timer.start()
67
+ vi.advanceTimersByTime(2000)
68
+ timer.pause()
69
+ const current = timer.getTime()
70
+
71
+ // Advance time but timer should stay paused
72
+ vi.advanceTimersByTime(3000)
73
+ expect(timer.getTime()).toBe(current) // about 2 seconds
74
+ expect(timer.ISOTimeParser(timer.getTime())).toBe('00:00:02')
75
+ })
76
+
77
+ it('should reset the timer to 0 when timer stops', () => {
78
+ timer.start()
79
+ vi.advanceTimersByTime(4000)
80
+ timer.stop()
81
+ expect(timer.getTime()).toBe(0)
82
+ expect(timer.getTimerState()).toBe('stopped')
83
+ })
84
+
85
+ it('should destroy timer', () => {
86
+ const id = timer.getTimerID()
87
+ timer.destroy()
88
+ expect(Timer.timers.get(id)).toBeUndefined()
89
+ expect(Timer.timers.size).toBe(0)
90
+ })
91
+ })
package/vitest.config.js CHANGED
@@ -1,19 +1,28 @@
1
- /**
2
- * @vitest-environment jsdom
3
- */
4
- import { fileURLToPath, URL } from 'node:url'
5
- import { mergeConfig, defineConfig, configDefaults } from 'vitest/config'
6
- import vue from '@vitejs/plugin-vue'
7
- import viteConfig from './vitest.config'
8
-
9
- export default mergeConfig(
10
- viteConfig,
11
- defineConfig({
12
- test: {
13
- environment: 'jsdom',
14
- exclude: [...configDefaults.exclude, 'e2e/**'],
15
- root: fileURLToPath(new URL('./', import.meta.url))
16
- },
17
- plugins: [vue()]
18
- })
19
- )
1
+ /**
2
+ * @vitest-environment jsdom
3
+ */
4
+ import { fileURLToPath, URL } from 'node:url'
5
+ import { mergeConfig, defineConfig, configDefaults } from 'vitest/config'
6
+ import vue from '@vitejs/plugin-vue'
7
+ import viteConfig from './vitest.config'
8
+
9
+ export default mergeConfig(
10
+ viteConfig,
11
+ defineConfig({
12
+ test: {
13
+ setupFiles: ['vitest.setup.js'],
14
+ alias: {
15
+ '@': fileURLToPath(new URL('./src', import.meta.url))
16
+ },
17
+ server: {
18
+ deps: {
19
+ inline: ['vuetify']
20
+ }
21
+ },
22
+ environment: 'jsdom',
23
+ exclude: [...configDefaults.exclude, 'tests/component/**', '**/_*.spec.js'],
24
+ root: fileURLToPath(new URL('./', import.meta.url))
25
+ },
26
+ plugins: [vue()]
27
+ })
28
+ )
@@ -0,0 +1,28 @@
1
+ //Mock Vuetify, vue-i18n and event bus
2
+ import { config } from '@vue/test-utils'
3
+ import { createI18n } from 'vue-i18n'
4
+ import { createVuetify } from 'vuetify'
5
+ import * as components from 'vuetify/components'
6
+ import * as directives from 'vuetify/directives'
7
+ import * as frMessages from './src/$locales/fr.json'
8
+ import * as enMessages from './src/$locales/en.json'
9
+ import bus from './src/plugins/bus'
10
+
11
+ //create vuetify instance
12
+ const vuetify = createVuetify({
13
+ components,
14
+ directives
15
+ })
16
+
17
+ //load messages from core
18
+ const i18n = createI18n({
19
+ legacy: false,
20
+ locale: 'fr',
21
+ messages: {
22
+ fr: frMessages,
23
+ en: enMessages
24
+ }
25
+ })
26
+
27
+ //setup as global plugins used by all tests
28
+ config.global.plugins = [i18n, vuetify, bus]
@@ -1,21 +0,0 @@
1
- import { describe, it, expect } from 'vitest'
2
- import { mount } from '@vue/test-utils'
3
- import AppBaseButton from './AppBaseButton.vue'
4
-
5
- const defaultProps = { isDisabled: false, type: 'button' }
6
-
7
- describe('AppBaseButton Tests', () => {
8
- it('should render', () => {
9
- const wrapper = mount(AppBaseButton, {
10
- props: defaultProps
11
- })
12
- expect(wrapper.find('button').exists()).toBeTruthy()
13
- })
14
- it('should fire click event', async () => {
15
- const wrapper = mount(AppBaseButton, {
16
- props: defaultProps
17
- })
18
- wrapper.find('button').trigger('click')
19
- expect(wrapper.emitted()).toHaveProperty('click')
20
- })
21
- })