fcad-core-dragon 2.1.2 → 2.2.0-beta.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 (33) hide show
  1. package/.gitlab-ci.yml +24 -42
  2. package/.vscode/settings.json +0 -30
  3. package/CHANGELOG +16 -0
  4. package/artifacts/playwright-report/index.html +85 -0
  5. package/package.json +29 -26
  6. package/src/components/AppBasePage.vue +3 -8
  7. package/src/components/AppCompAudio.vue +3 -3
  8. package/src/components/AppCompBranchButtons.vue +6 -3
  9. package/src/components/AppCompInputCheckBoxNx.vue +2 -1
  10. package/src/components/AppCompInputDropdownNx.vue +5 -5
  11. package/src/components/AppCompInputRadioNx.vue +2 -1
  12. package/src/components/AppCompInputTextNx.vue +0 -2
  13. package/src/components/AppCompInputTextTableNx.vue +4 -4
  14. package/src/components/AppCompInputTextToFillDropdownNx.vue +4 -6
  15. package/src/components/AppCompInputTextToFillNx.vue +1 -24
  16. package/src/components/AppCompNoteCredit.vue +9 -7
  17. package/src/components/AppCompPlayBarNext.vue +17 -11
  18. package/src/components/AppCompQuizRecall.vue +10 -6
  19. package/src/components/AppCompVideoPlayer.vue +2 -1
  20. package/src/main.js +20 -6
  21. package/src/plugins/i18n.js +8 -7
  22. package/tests/mocks/routes.mock.js +2 -0
  23. package/tests/unit/AppCompAudio.spec.js +139 -0
  24. package/tests/unit/AppCompBranchButtons.spec.js +172 -0
  25. package/tests/unit/AppCompCarousel.spec.js +62 -0
  26. package/tests/unit/AppCompNoteCredit.spec.js +49 -0
  27. package/tests/unit/AppCompVideoPlayer.spec.js +97 -102
  28. package/tests/unit/useQuiz.spec.js +72 -0
  29. package/tests/utility/colors.js +10 -0
  30. package/vitest.config.js +49 -23
  31. package/vitest.setup.js +70 -1
  32. package/junit-report.xml +0 -182
  33. package/tests/unit/AppCompQuizNext.spec.js +0 -114
@@ -1,104 +1,42 @@
1
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
- })
2
+ import { describe, it, test, expect, vi } from 'vitest'
3
+
4
+ //Mock store appStore
5
+ vi.mock('@/module/stores/appStore', () => ({
6
+ useAppStore: () => ({})
7
+ }))
8
+
9
+ let dummyProps = {
10
+ id: 'P01',
11
+ activityRef: 'A03',
12
+ title: 'Lecteurs médias',
13
+ type: 'pg_normal',
14
+ videosData: [
15
+ {
16
+ id: 'vid1',
17
+ mSources: [
18
+ {
19
+ type: 'mp4',
20
+ src: 'exemple_video.mp4'
21
+ }
22
+ ],
23
+ mSubtitles: [
24
+ {
25
+ label: 'Français',
26
+ src: 'exemple_soustitres.vtt',
27
+ srclang: 'fr'
28
+ }
29
+ ],
30
+ mPoster: 'video_poster.jpg',
31
+ mTranscript: 'exemple_transcript.html' // The file MUST be host in the public folder of the project
82
32
  }
83
- })
84
- })
33
+ ]
34
+ }
85
35
 
86
36
  import AppCompVideoPlayer from '@/components/AppCompVideoPlayer.vue'
87
- // import AppBaseErrorDisplay from '@/components/AppBaseErrorDisplay.vue'
88
37
  import { validateVideoData } from '@/shared/validators.js'
89
38
 
90
39
  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
40
  test('Validate received props', () => {
103
41
  validateVideoData.mockReturnValue([])
104
42
  const { videosData } = dummyProps
@@ -133,8 +71,7 @@ describe('AppCompVideoPlayer', () => {
133
71
  ])
134
72
 
135
73
  const wrapper = mount(AppCompVideoPlayer, {
136
- props: { vidData: { id: null } },
137
- global: { stubs, plugins: [createTestingPinia({ createSpy: vi.fn })] }
74
+ props: { vidData: { id: null } }
138
75
  })
139
76
 
140
77
  expect(
@@ -146,8 +83,7 @@ describe('AppCompVideoPlayer', () => {
146
83
  it('It renders HTML video Element', () => {
147
84
  validateVideoData.mockReturnValue([]) // no Error
148
85
  const wrapper = mount(AppCompVideoPlayer, {
149
- props: { vidData: { id: null } },
150
- global: { stubs }
86
+ props: { vidData: { id: null } }
151
87
  })
152
88
  expect(
153
89
  wrapper.findComponent({ name: 'AppBaseErrorDisplay' }).exists()
@@ -159,8 +95,7 @@ describe('AppCompVideoPlayer', () => {
159
95
  const { videosData } = dummyProps
160
96
  validateVideoData.mockReturnValue([]) // no Error
161
97
  const wrapper = mount(AppCompVideoPlayer, {
162
- props: { vidData: videosData[0] },
163
- global: { stubs }
98
+ props: { vidData: videosData[0] }
164
99
  })
165
100
 
166
101
  expect(wrapper.vm.vidSources).toEqual(videosData[0].mSources)
@@ -169,9 +104,69 @@ describe('AppCompVideoPlayer', () => {
169
104
  await wrapper.vm.$nextTick()
170
105
  const sources = wrapper.findAll('source')
171
106
  expect(sources.length).toBe(1)
172
-
173
- // --- 3) valider les valeurs réelles rendues ---
174
107
  expect(sources[0].attributes().src).toBe(videosData[0].mSources[0].src)
175
108
  expect(sources[0].attributes().type).toBe('video/mp4')
176
109
  })
110
+
111
+ it('computed $vidElement returns correct data structure', async () => {
112
+ const { videosData } = dummyProps
113
+ validateVideoData.mockReturnValue([])
114
+
115
+ const wrapper = mount(AppCompVideoPlayer, {
116
+ props: { vidData: videosData[0] }
117
+ })
118
+
119
+ // Simulate refs
120
+ const mockVideoRef = { tagName: 'VIDEO' }
121
+ const mockContainerRef = { className: '__media-container' }
122
+
123
+ wrapper.vm.$refs['m-video'] = mockVideoRef
124
+ wrapper.vm.$refs['$media-container'] = mockContainerRef
125
+
126
+ // Simulate refs isSet (video ready) to true
127
+ wrapper.vm.isSet = true
128
+ await wrapper.vm.$nextTick()
129
+
130
+ // get vidElement object
131
+ const obj = wrapper.vm.$vidElement
132
+ const id = wrapper.vm.$props.vidData.id
133
+
134
+ // expected data structure to be returned
135
+ const vidElement = {
136
+ id,
137
+ mType: 'video',
138
+ mElement: { id, localName: 'video' },
139
+ mMediaContainer: {
140
+ id: `video_${id}`,
141
+ ...mockContainerRef,
142
+ localName: 'section'
143
+ },
144
+ mTranscript: 'exemple_transcript.html',
145
+ mSubtitles: [
146
+ {
147
+ label: 'Français',
148
+ src: 'exemple_soustitres.vtt',
149
+ srclang: 'fr'
150
+ }
151
+ ]
152
+ }
153
+ // validate returned object
154
+ expect(obj.id).toEqual(vidElement.id)
155
+ expect(obj.mType).toBe(vidElement.mType)
156
+ expect(obj.mElement.id).toBe(vidElement.mElement.id)
157
+ expect(obj.mElement.tagName.toLowerCase()).toBe(
158
+ vidElement.mElement.localName
159
+ )
160
+ expect(obj.mMediaContainer.tagName.toLowerCase()).toBe(
161
+ vidElement.mMediaContainer.localName
162
+ )
163
+ expect(obj.mMediaContainer.id).toBe(vidElement.mMediaContainer.id)
164
+ expect(
165
+ obj.mMediaContainer.classList.contains(
166
+ vidElement.mMediaContainer.className
167
+ )
168
+ ).toBe(true)
169
+ expect(obj.mTranscript).toBe(vidElement.mTranscript)
170
+ expect(obj.mSubtitles).toEqual(vidElement.mSubtitles)
171
+ })
177
172
  })
@@ -0,0 +1,72 @@
1
+ import { describe, it, expect, vi } from 'vitest'
2
+ import { useQuiz } from '../../src/composables/useQuiz.js'
3
+
4
+ // Mock de useI18n pour éviter les erreurs liées à $t()
5
+ vi.mock('vue-i18n', () => ({
6
+ useI18n: () => ({ t: (key) => key })
7
+ }))
8
+
9
+ describe('useQuiz composable', () => {
10
+ const { shuffleArray, addRetroStyle, resetRetroStyle, retroType } = useQuiz()
11
+
12
+ it('shuffleArray should shuffle array without losing elements', () => {
13
+ const arr = [1, 2, 3, 4]
14
+ const shuffled = shuffleArray(arr)
15
+ expect(shuffled).toHaveLength(arr.length)
16
+ expect(shuffled.sort()).toEqual(arr.sort())
17
+ })
18
+
19
+ it('addRetroStyle should return correct classes and messages for correct answers', () => {
20
+ const solution = [{ id: 1 }, { id: 2 }]
21
+ const reponse = [{ correct: true }, { correct: true }]
22
+ const result = addRetroStyle(solution, reponse, reponse.length)
23
+ expect(result.classRetro).toEqual(['goodAnswer', 'goodAnswer'])
24
+ expect(result.mesA11y).toEqual([
25
+ 'quizState.goodAnswer',
26
+ 'quizState.goodAnswer'
27
+ ])
28
+ })
29
+
30
+ it('addRetroStyle should return neutral style when solution is null', () => {
31
+ const result = addRetroStyle(null, [], 0)
32
+ expect(result.classRetro).toEqual(['NeutralAnswer'])
33
+ expect(result.mesA11y).toEqual(['quizState.neutralAnswer'])
34
+ })
35
+
36
+ it('retroType should return retro_positive when all answers are correct', () => {
37
+ const solution = [{ id: 1 }, { id: 2 }]
38
+ const reponse = [{ correct: true }, { correct: true }]
39
+ expect(retroType(solution, reponse)).toBe('retro_positive')
40
+ })
41
+
42
+ it('retroType should return retro_negative when some answers are wrong', () => {
43
+ const solution = ['Données primaires', 'Données secondaires']
44
+ const reponse = [
45
+ {
46
+ selected: 'Données primaires',
47
+ correct: true
48
+ },
49
+ {
50
+ selected: 'Données primaires',
51
+ correct: false
52
+ }
53
+ ]
54
+ expect(retroType(solution, reponse)).toBe('retro_negative')
55
+ })
56
+
57
+ it('retroType should return retro_neutre when solution is null', () => {
58
+ expect(retroType(null, [])).toBe('retro_neutre')
59
+ })
60
+
61
+ it('resetRetroStyle should clear arrays and emit event', () => {
62
+ const fakeBus = { $emit: vi.fn() }
63
+ const fakeEl = {}
64
+ const context = { $bus: fakeBus, $el: fakeEl }
65
+ const arr1 = [1, 2]
66
+ const arr2 = ['a']
67
+ resetRetroStyle.call(context, [arr1, arr2])
68
+ expect(arr1.length).toBe(0)
69
+ expect(arr2.length).toBe(0)
70
+ expect(fakeBus.$emit).toHaveBeenCalledWith('hide-retro', fakeEl, false)
71
+ })
72
+ })
@@ -0,0 +1,10 @@
1
+ export const colors = {
2
+ red: '\x1b[31m',
3
+ green: '\x1b[32m',
4
+ yellow: '\x1b[33m',
5
+ blue: '\x1b[34m',
6
+ magenta: '\x1b[35m',
7
+ cyan: '\x1b[36m',
8
+ white: '\x1b[37m',
9
+ reset: '\x1b[0m'
10
+ }
package/vitest.config.js CHANGED
@@ -1,28 +1,54 @@
1
1
  /**
2
- * @vitest-environment jsdom
2
+ * @vitest-environment happy-dom
3
3
  */
4
4
  import { fileURLToPath, URL } from 'node:url'
5
- import { mergeConfig, defineConfig, configDefaults } from 'vitest/config'
5
+ import { defineConfig, configDefaults } from 'vitest/config'
6
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))
7
+
8
+ export default defineConfig({
9
+ css: {
10
+ preprocessorOptions: {
11
+ scss: {
12
+ api: 'modern'
13
+ }
14
+ }
15
+ },
16
+
17
+ resolve: {
18
+ alias: {
19
+ '@': fileURLToPath(new URL('./src', import.meta.url)),
20
+ '@/router/routes.js': fileURLToPath(
21
+ new URL('./tests/mocks/routes.mock.js', import.meta.url)
22
+ )
23
+ }
24
+ },
25
+
26
+ test: {
27
+ setupFiles: ['vitest.setup.js'],
28
+ environment: 'happy-dom',
29
+
30
+ server: {
31
+ deps: {
32
+ inline: ['vuetify']
33
+ }
25
34
  },
26
- plugins: [vue()]
27
- })
28
- )
35
+
36
+ exclude: [...configDefaults.exclude, 'tests/component/**', '**/_*.spec.js'],
37
+
38
+ root: fileURLToPath(new URL('./', import.meta.url)),
39
+
40
+ coverage: {
41
+ provider: 'v8',
42
+ reporter: ['text', 'json', 'cobertura'],
43
+ reportsDirectory: './coverage',
44
+ include: ['src/**/*.{js,vue}'],
45
+ exclude: ['tests/**/*'],
46
+ lines: 0,
47
+ branches: 0,
48
+ functions: 0,
49
+ statements: 0
50
+ }
51
+ },
52
+
53
+ plugins: [vue()]
54
+ })
package/vitest.setup.js CHANGED
@@ -1,4 +1,5 @@
1
1
  //Mock Vuetify, vue-i18n and event bus
2
+ import { vi, beforeEach } from 'vitest'
2
3
  import { config } from '@vue/test-utils'
3
4
  import { createI18n } from 'vue-i18n'
4
5
  import { createVuetify } from 'vuetify'
@@ -7,6 +8,7 @@ import * as directives from 'vuetify/directives'
7
8
  import * as frMessages from './src/$locales/fr.json'
8
9
  import * as enMessages from './src/$locales/en.json'
9
10
  import bus from './src/plugins/bus'
11
+ import { createPinia, setActivePinia } from 'pinia'
10
12
 
11
13
  //create vuetify instance
12
14
  const vuetify = createVuetify({
@@ -24,5 +26,72 @@ const i18n = createI18n({
24
26
  }
25
27
  })
26
28
 
29
+ vi.spyOn(console, 'warn').mockImplementation(() => {})
30
+ vi.spyOn(console, 'error').mockImplementation(() => {})
31
+
32
+ // Global mock of Router
33
+ // =========================================
34
+ vi.mock('vue-router', () => ({
35
+ useRouter: () => ({
36
+ push: vi.fn(),
37
+ back: vi.fn()
38
+ }),
39
+ useRoute: () => ({
40
+ meta: {},
41
+ params: {},
42
+ query: {}
43
+ })
44
+ }))
45
+
46
+ // Global mock of validators
47
+ // =========================================
48
+ vi.mock('@/shared/validators.js', () => ({
49
+ validateObjType: vi.fn(() => true),
50
+ validateString: vi.fn(() => true),
51
+ validateNumber: vi.fn(() => true),
52
+ validateVideoData: vi.fn(() => []),
53
+ validateAudioData: vi.fn(() => []),
54
+ validateProp: vi.fn(() => [])
55
+ }))
56
+
27
57
  //setup as global plugins used by all tests
28
- config.global.plugins = [i18n, vuetify, bus]
58
+ config.global.plugins.push(i18n, vuetify, bus)
59
+
60
+ //Setup a new pinia instance before each test
61
+ //=========================================
62
+ //NOTE:
63
+ // Do NOT use createTestingPinia() globally
64
+ // Pinia mocking must be done per-test using createTestingPinia()
65
+ // to avoid hidden side effects and false-positive tests.
66
+ // https://pinia.vuejs.org/cookbook/testing.html#using-the-testing-plugin
67
+ // https://pinia.vuejs.org/cookbook/testing.html
68
+
69
+ beforeEach(() => {
70
+ setActivePinia(createPinia())
71
+ })
72
+
73
+ // Global mock of components
74
+ //=========================================
75
+ config.global.stubs = {
76
+ AppBaseErrorDisplay: true,
77
+ AppBaseSkeleton: true,
78
+ AppBaseButton: true,
79
+ AppCompPlayBarNext: true
80
+ }
81
+
82
+ config.global.mocks = {
83
+ $route: {
84
+ meta: {
85
+ activity_ref: 'A03',
86
+ id: 'P01'
87
+ }
88
+ },
89
+ $bus: {
90
+ $on: vi.fn(),
91
+ $emit: vi.fn(),
92
+ $off: vi.fn()
93
+ },
94
+ $helper: {
95
+ formatTime: vi.fn((t) => `00:${t}`)
96
+ }
97
+ }