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.
- package/.editorconfig +7 -7
- package/.gitlab-ci.yml +124 -0
- package/.prettierrc +11 -11
- package/.vscode/extensions.json +8 -8
- package/.vscode/settings.json +46 -16
- package/CHANGELOG +520 -520
- package/README.md +57 -57
- package/documentation/.vitepress/config.js +114 -114
- package/documentation/api-examples.md +49 -49
- package/documentation/composants/app-base-button.md +58 -58
- package/documentation/composants/app-base-error-display.md +59 -59
- package/documentation/composants/app-base-popover.md +68 -68
- package/documentation/composants/app-comp-audio.md +75 -75
- package/documentation/composants/app-comp-branch-buttons.md +111 -111
- package/documentation/composants/app-comp-button-progress.md +53 -53
- package/documentation/composants/app-comp-carousel.md +53 -53
- package/documentation/composants/app-comp-container.md +53 -53
- package/documentation/composants/app-comp-input-checkbox-next.md +42 -42
- package/documentation/composants/app-comp-input-dropdown-next.md +34 -34
- package/documentation/composants/app-comp-input-radio-next.md +39 -39
- package/documentation/composants/app-comp-input-text-next.md +35 -35
- package/documentation/composants/app-comp-input-text-table-next.md +34 -34
- package/documentation/composants/app-comp-input-text-to-fill-dropdown-next.md +53 -53
- package/documentation/composants/app-comp-input-text-to-fill-next.md +31 -31
- package/documentation/composants/app-comp-jauge.md +31 -31
- package/documentation/composants/app-comp-menu-item.md +55 -55
- package/documentation/composants/app-comp-menu.md +29 -29
- package/documentation/composants/app-comp-navigation.md +41 -41
- package/documentation/composants/app-comp-note-call.md +53 -53
- package/documentation/composants/app-comp-note-credit.md +53 -53
- package/documentation/composants/app-comp-play-bar-next.md +53 -53
- package/documentation/composants/app-comp-pop-up-next.md +93 -93
- package/documentation/composants/app-comp-quiz-next.md +235 -235
- package/documentation/composants/app-comp-quiz-recall.md +53 -53
- package/documentation/composants/app-comp-svg-next.md +53 -53
- package/documentation/composants/app-comp-table-of-content.md +50 -50
- package/documentation/composants/app-comp-video-player.md +82 -82
- package/documentation/composants.md +46 -46
- package/documentation/composants_critiques/ModelPageComposant.md +53 -53
- package/documentation/composants_critiques/app-base-module.md +43 -43
- package/documentation/composants_critiques/app-base-page.md +48 -48
- package/documentation/composants_critiques/app-base.md +311 -311
- package/documentation/composants_critiques/main.md +15 -15
- package/documentation/demarrage.md +50 -50
- package/documentation/deploiement.md +57 -57
- package/documentation/index.md +33 -33
- package/documentation/markdown-examples.md +85 -85
- package/documentation/public/vite.svg +14 -14
- package/documentation/public/vuejs.svg +1 -1
- package/documentation/public/vuetify.svg +5 -5
- package/eslint.config.js +60 -60
- package/junit-report.xml +182 -0
- package/package.json +66 -59
- package/playwright/index.html +12 -0
- package/playwright/index.js +21 -0
- package/playwright-ct.config.js +95 -0
- package/src/$locales/en.json +157 -157
- package/src/$locales/fr.json +120 -120
- package/src/assets/data/onboardingMessages.json +47 -47
- package/src/components/AppBase.vue +1171 -1169
- package/src/components/AppBaseButton.vue +90 -95
- package/src/components/AppBaseErrorDisplay.vue +438 -438
- package/src/components/AppBaseFlipCard.vue +84 -84
- package/src/components/AppBaseModule.vue +1639 -1634
- package/src/components/AppBasePage.vue +3 -2
- package/src/components/AppBasePopover.vue +41 -41
- package/src/components/AppBaseSkeleton.vue +66 -66
- package/src/components/AppCompAudio.vue +261 -256
- package/src/components/AppCompBranchButtons.vue +508 -508
- package/src/components/AppCompButtonProgress.vue +137 -132
- package/src/components/AppCompCarousel.vue +342 -336
- package/src/components/AppCompContainer.vue +29 -29
- package/src/components/AppCompInputCheckBoxNx.vue +325 -323
- package/src/components/AppCompInputDropdownNx.vue +302 -299
- package/src/components/AppCompInputRadioNx.vue +287 -284
- package/src/components/AppCompInputTextNx.vue +156 -153
- package/src/components/AppCompInputTextTableNx.vue +205 -202
- package/src/components/AppCompInputTextToFillDropdownNx.vue +343 -340
- package/src/components/AppCompInputTextToFillNx.vue +316 -313
- package/src/components/AppCompJauge.vue +81 -81
- package/src/components/AppCompMenu.vue +6 -2
- package/src/components/AppCompMenuItem.vue +246 -240
- package/src/components/AppCompNavigation.vue +977 -972
- package/src/components/AppCompNoteCall.vue +167 -161
- package/src/components/AppCompNoteCredit.vue +496 -491
- package/src/components/AppCompPlayBarNext.vue +2290 -2288
- package/src/components/AppCompPopUpNext.vue +508 -504
- package/src/components/AppCompQuizNext.vue +515 -510
- package/src/components/AppCompQuizRecall.vue +355 -350
- package/src/components/AppCompSVGNext.vue +346 -346
- package/src/components/AppCompSettingsMenu.vue +177 -172
- package/src/components/AppCompTableOfContent.vue +433 -427
- package/src/components/AppCompVideoPlayer.vue +377 -377
- package/src/components/AppCompViewDisplay.vue +6 -6
- package/src/components/BaseModule.vue +55 -55
- package/src/composables/useIdleDetector.js +56 -56
- package/src/composables/useQuiz.js +89 -89
- package/src/composables/useTimer.js +172 -172
- package/src/directives/nvdaFix.js +53 -53
- package/src/externalComps/ModuleView.vue +22 -22
- package/src/externalComps/SummaryView.vue +91 -91
- package/src/main.js +493 -476
- package/src/module/stores/appStore.js +960 -947
- package/src/module/xapi/ADL.js +520 -520
- package/src/module/xapi/Crypto/Hasher.js +241 -241
- package/src/module/xapi/Crypto/WordArray.js +278 -278
- package/src/module/xapi/Crypto/algorithms/BufferedBlockAlgorithm.js +103 -103
- package/src/module/xapi/Crypto/algorithms/C_algo.js +315 -315
- package/src/module/xapi/Crypto/algorithms/HMAC.js +9 -9
- package/src/module/xapi/Crypto/algorithms/SHA1.js +9 -9
- package/src/module/xapi/Crypto/encoders/Base.js +105 -105
- package/src/module/xapi/Crypto/encoders/Base64.js +99 -99
- package/src/module/xapi/Crypto/encoders/Hex.js +61 -61
- package/src/module/xapi/Crypto/encoders/Latin1.js +61 -61
- package/src/module/xapi/Crypto/encoders/Utf8.js +45 -45
- package/src/module/xapi/Crypto/index.js +53 -53
- package/src/module/xapi/Statement/activity.js +47 -47
- package/src/module/xapi/Statement/agent.js +55 -55
- package/src/module/xapi/Statement/group.js +26 -26
- package/src/module/xapi/Statement/index.js +259 -259
- package/src/module/xapi/Statement/statement.js +253 -253
- package/src/module/xapi/Statement/statementRef.js +23 -23
- package/src/module/xapi/Statement/substatement.js +22 -22
- package/src/module/xapi/Statement/verb.js +36 -36
- package/src/module/xapi/activitytypes.js +17 -17
- package/src/module/xapi/launch.js +157 -157
- package/src/module/xapi/utils.js +167 -167
- package/src/module/xapi/verbs.js +294 -294
- package/src/module/xapi/wrapper.js +1895 -1895
- package/src/module/xapi/xapiStatement.js +444 -444
- package/src/plugins/analytics.js +34 -34
- package/src/plugins/bus.js +12 -8
- package/src/plugins/gsap.js +17 -15
- package/src/plugins/helper.js +355 -358
- package/src/plugins/i18n.js +27 -26
- package/src/plugins/idb.js +227 -227
- package/src/plugins/save.js +37 -37
- package/src/plugins/scorm.js +287 -287
- package/src/plugins/xapi.js +11 -11
- package/src/public/index.html +33 -33
- package/src/router/index.js +57 -57
- package/src/router/routes.js +312 -312
- package/src/shared/generalfuncs.js +344 -344
- package/src/shared/validators.js +1018 -1018
- package/tests/component/AppBaseButton.spec.js +53 -0
- package/tests/component/pinia.spec.js +24 -0
- package/{src/components/tests__ → tests/unit}/AppBaseButton.spec.js +53 -53
- package/tests/unit/AppCompInputCheckBoxNx.spec.js +59 -0
- package/tests/unit/AppCompInputDropdownNx.spec.js +51 -0
- package/tests/unit/AppCompInputRadioNx.spec.js +59 -0
- package/tests/unit/AppCompInputTextNx.spec.js +44 -0
- package/tests/unit/AppCompInputTextTableNx.spec.js +77 -0
- package/tests/unit/AppCompInputTextToFillDropdownNx.spec.js +60 -0
- package/tests/unit/AppCompInputTextToFillNx.spec.js +45 -0
- package/tests/unit/AppCompQuizNext.spec.js +114 -0
- package/tests/unit/AppCompVideoPlayer.spec.js +177 -0
- package/{src/components/tests__ → tests/unit}/useTimer.spec.js +91 -91
- package/vitest.config.js +28 -19
- package/vitest.setup.js +28 -0
- 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
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
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
|
+
)
|
package/vitest.setup.js
ADDED
|
@@ -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
|
-
})
|