fcad-core-dragon 2.2.0-beta.1 → 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.
- package/CHANGELOG +7 -0
- package/artifacts/playwright-report/index.html +1 -1
- package/package.json +1 -1
- package/src/components/AppCompBranchButtons.vue +6 -3
- package/src/components/AppCompNoteCredit.vue +9 -7
- package/src/components/AppCompPlayBarNext.vue +11 -3
- package/src/components/AppCompQuizRecall.vue +7 -13
- package/src/main.js +3 -2
- package/src/plugins/i18n.js +1 -2
- package/tests/mocks/routes.mock.js +2 -0
- package/tests/unit/AppCompAudio.spec.js +6 -1
- package/tests/unit/AppCompBranchButtons.spec.js +172 -0
- package/tests/unit/AppCompCarousel.spec.js +11 -3
- package/tests/unit/AppCompNoteCredit.spec.js +14 -23
- package/tests/unit/AppCompVideoPlayer.spec.js +6 -3
- package/tests/utility/colors.js +10 -0
- package/vitest.config.js +20 -8
- package/vitest.setup.js +30 -29
- package/tests/unit/AppCompQuizNext.spec.js +0 -112
package/CHANGELOG
CHANGED
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
2.2.0-beta.2(8 janvier 2026)
|
|
2
|
+
Correction du comportement de la playbar du lecteur vidéo (elle reste toujours visible, sauf en plein écran)
|
|
3
|
+
|
|
4
|
+
Correction de l’affichage du contenu de la section info (notes et sources) quand il n’y a que des notes ou que des sources
|
|
5
|
+
|
|
6
|
+
Correction du rappel de réponse ("skeleton" qui restait bloqué)
|
|
7
|
+
|
|
1
8
|
2.2.0-beta.1(19 décembre 2025) ***Changements majeurs*** main.js et i18n.js doivent être remplacés dans vos cours
|
|
2
9
|
Correction texte troué et texte troué listes déroulantes (fin de texte manquante)
|
|
3
10
|
|
|
@@ -82,4 +82,4 @@ Error generating stack: `+n.message+`
|
|
|
82
82
|
<div id='root'></div>
|
|
83
83
|
</body>
|
|
84
84
|
</html>
|
|
85
|
-
<script id="playwrightReportBase64" type="application/zip">data:application/zip;base64,
|
|
85
|
+
<script id="playwrightReportBase64" type="application/zip">data:application/zip;base64,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</script>
|
package/package.json
CHANGED
|
@@ -250,6 +250,7 @@ export default {
|
|
|
250
250
|
this.isCard = this.cards && Object.keys(this.cards).length != 0
|
|
251
251
|
this.idActivity = this.$router.currentRoute.value.meta.activity_ref
|
|
252
252
|
this.branchs = this.$router.currentRoute.value.meta.children
|
|
253
|
+
|
|
253
254
|
this.getbranchsData()
|
|
254
255
|
},
|
|
255
256
|
methods: {
|
|
@@ -271,7 +272,7 @@ export default {
|
|
|
271
272
|
branchData.progression = progression
|
|
272
273
|
if (isNaN(branchData.progression)) branchData.progression = 0
|
|
273
274
|
|
|
274
|
-
if (this.isCard) {
|
|
275
|
+
if (this.isCard && this.getMatchingElement(branchData.id)) {
|
|
275
276
|
const { title, text, imgFile, imgAlt, btnTitle } =
|
|
276
277
|
this.getMatchingElement(branchData.id)
|
|
277
278
|
|
|
@@ -476,12 +477,14 @@ export default {
|
|
|
476
477
|
* @return {Object} Matching element from
|
|
477
478
|
*/
|
|
478
479
|
getMatchingElement(id) {
|
|
480
|
+
let matchingBranch = null
|
|
479
481
|
if (this.isCustomButton) {
|
|
480
|
-
|
|
482
|
+
matchingBranch = this.customButtons.find((c) => c.brchName === id)
|
|
481
483
|
}
|
|
482
484
|
if (this.isCard) {
|
|
483
|
-
|
|
485
|
+
matchingBranch = this.cards.find((c) => c.brchName === id)
|
|
484
486
|
}
|
|
487
|
+
return matchingBranch ? matchingBranch : null
|
|
485
488
|
}
|
|
486
489
|
}
|
|
487
490
|
}
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
:error-text="`Vous avez une/des erreur(s) dans la création des notes/crédits.`"
|
|
27
27
|
></app-base-error-display>
|
|
28
28
|
<template v-else>
|
|
29
|
-
<div v-if="
|
|
29
|
+
<div v-if="hasNotes" class="ctn-note">
|
|
30
30
|
<p class="t-note">{{ $t('text.title_note') }}</p>
|
|
31
31
|
<div id="notes-list">
|
|
32
32
|
<template
|
|
@@ -62,7 +62,7 @@
|
|
|
62
62
|
</template>
|
|
63
63
|
</div>
|
|
64
64
|
</div>
|
|
65
|
-
<div v-if="
|
|
65
|
+
<div v-if="hasCredits" class="ctn-credit">
|
|
66
66
|
<p class="t-crdt">{{ $t('text.title_credit') }}</p>
|
|
67
67
|
|
|
68
68
|
<ul id="credits-list">
|
|
@@ -109,7 +109,13 @@ export default {
|
|
|
109
109
|
'getCurrentPage',
|
|
110
110
|
'getNotes',
|
|
111
111
|
'getCredits'
|
|
112
|
-
])
|
|
112
|
+
]),
|
|
113
|
+
hasNotes() {
|
|
114
|
+
return this.notes && this.notes.length
|
|
115
|
+
},
|
|
116
|
+
hasCredits() {
|
|
117
|
+
return this.credits && this.credits.length
|
|
118
|
+
}
|
|
113
119
|
},
|
|
114
120
|
created() {},
|
|
115
121
|
beforeUnmount() {
|
|
@@ -414,10 +420,6 @@ export default {
|
|
|
414
420
|
}
|
|
415
421
|
}
|
|
416
422
|
|
|
417
|
-
// .note-txt {
|
|
418
|
-
// pointer-events: none;
|
|
419
|
-
// }
|
|
420
|
-
|
|
421
423
|
.box-nc {
|
|
422
424
|
margin-top: 54px;
|
|
423
425
|
overflow-y: auto;
|
|
@@ -460,6 +460,7 @@ export default {
|
|
|
460
460
|
const timer = reactive(new Timer(id)) // Making Timer instance reactive to be able to track changes
|
|
461
461
|
const { t } = useI18n()
|
|
462
462
|
return { id, store, timer, t }
|
|
463
|
+
//return { id, store, timer }
|
|
463
464
|
},
|
|
464
465
|
data() {
|
|
465
466
|
return {
|
|
@@ -913,7 +914,7 @@ export default {
|
|
|
913
914
|
* @description - handle all the listeners for medias
|
|
914
915
|
*/
|
|
915
916
|
setMediaHandlers() {
|
|
916
|
-
if (this.mediaElement) {
|
|
917
|
+
if (this.mediaElement && this.mediaElement.addEventListener) {
|
|
917
918
|
//Prevent default on keys used for navigation in the player (prevent scrollbar to be trigger when changing volume)
|
|
918
919
|
this.pbContainer.addEventListener('keydown', this.keysPreventDefault)
|
|
919
920
|
//progressBar events
|
|
@@ -921,7 +922,7 @@ export default {
|
|
|
921
922
|
//window handlers for playbar progress
|
|
922
923
|
window.addEventListener('mousemove', this.progressWindowMove)
|
|
923
924
|
window.addEventListener('mouseup', this.progressWindowUp)
|
|
924
|
-
|
|
925
|
+
// console.log('⚠️--- TEST AppCompPlayBarNext---', this.mediaElement)
|
|
925
926
|
//Update data when media as a timeupdate/ended
|
|
926
927
|
this.mediaElement.addEventListener(
|
|
927
928
|
'timeupdate',
|
|
@@ -1580,7 +1581,10 @@ export default {
|
|
|
1580
1581
|
*/
|
|
1581
1582
|
setVideoHandlers() {
|
|
1582
1583
|
//Tooltip
|
|
1584
|
+
if (!this.progressArea || !this.progressArea.addEventListener) return
|
|
1583
1585
|
this.progressArea.addEventListener('mousemove', this.updateSeekTooltip)
|
|
1586
|
+
|
|
1587
|
+
if (!this.mediaContainer || !this.mediaContainer.addEventListener) return
|
|
1584
1588
|
//Fullscreen
|
|
1585
1589
|
this.mediaContainer.addEventListener(
|
|
1586
1590
|
'fullscreenchange',
|
|
@@ -1594,7 +1598,10 @@ export default {
|
|
|
1594
1598
|
*/
|
|
1595
1599
|
removeVideoHandlers() {
|
|
1596
1600
|
//Tooltip
|
|
1601
|
+
if (!this.progressArea || !this.progressArea.addEventListener) return
|
|
1597
1602
|
this.progressArea.removeEventListener('mousemove', this.updateSeekTooltip)
|
|
1603
|
+
if (!this.mediaContainer || !this.mediaContainer.removeEventListener)
|
|
1604
|
+
return
|
|
1598
1605
|
//Fullscreen
|
|
1599
1606
|
this.mediaContainer.removeEventListener(
|
|
1600
1607
|
'fullscreenchange',
|
|
@@ -1777,7 +1784,8 @@ export default {
|
|
|
1777
1784
|
*/
|
|
1778
1785
|
|
|
1779
1786
|
hideControls() {
|
|
1780
|
-
|
|
1787
|
+
//Always show playbar when paused or not in fullscreen mode
|
|
1788
|
+
if (this.mediaElement.paused || !document.fullscreenElement) return
|
|
1781
1789
|
|
|
1782
1790
|
this.hideTimer = setTimeout(() => {
|
|
1783
1791
|
this.showControlsValue = false
|
|
@@ -103,27 +103,15 @@ export default {
|
|
|
103
103
|
//Watch for user interaction change to get quizRecall answer
|
|
104
104
|
getUserInteraction: {
|
|
105
105
|
async handler(newValue) {
|
|
106
|
-
//console.log('???')
|
|
107
106
|
if (!this.getUserInteraction) return
|
|
108
|
-
//console.log('--------------------')
|
|
109
107
|
const { activityId, pageId } = this.quizRecallData
|
|
110
108
|
const interaction = this.getPageInteraction(
|
|
111
109
|
activityId,
|
|
112
110
|
pageId
|
|
113
111
|
).userInteraction
|
|
114
|
-
|
|
115
|
-
//console.log(interaction)
|
|
116
|
-
if (!interaction) return //{
|
|
117
|
-
// this.quizRecall.done == false
|
|
118
|
-
// }
|
|
119
|
-
|
|
120
112
|
//Get quizRecall answer
|
|
121
113
|
if (this.quizRecallData && this.quizData) {
|
|
122
|
-
//console.log('ici????')
|
|
123
114
|
await this.getQuizRecallAnswer(interaction)
|
|
124
|
-
} else {
|
|
125
|
-
//console.log('laaaaaa ???? ')
|
|
126
|
-
this.quizRecall.done == false
|
|
127
115
|
}
|
|
128
116
|
},
|
|
129
117
|
immediate: true,
|
|
@@ -216,6 +204,13 @@ export default {
|
|
|
216
204
|
//Get datas from quizRecallData and userData and add them to quizRecall object
|
|
217
205
|
async getQuizRecallAnswer(userData) {
|
|
218
206
|
await this.$nextTick() //wait for the DOM to update
|
|
207
|
+
//userData will be undefined if the page containing the target quiz was never visited
|
|
208
|
+
if (typeof userData === 'undefined') {
|
|
209
|
+
this.quizRecall.done = false //set quiz recall message (quiz not completed)
|
|
210
|
+
this.isReady = true //hide the skeleton
|
|
211
|
+
return
|
|
212
|
+
}
|
|
213
|
+
|
|
219
214
|
const { quizId, hypertext_done, hypertext_undone, title, titletag } =
|
|
220
215
|
this.quizRecallData
|
|
221
216
|
|
|
@@ -252,7 +247,6 @@ export default {
|
|
|
252
247
|
this.quizRecall.answer = quizAnswer.value[0].filled || ''
|
|
253
248
|
this.quizRecall.ennonce = this.quizData.ennonce
|
|
254
249
|
}
|
|
255
|
-
this.quizRecall
|
|
256
250
|
this.isReady = true //Set isReady to true to display the component
|
|
257
251
|
},
|
|
258
252
|
|
package/src/main.js
CHANGED
|
@@ -489,8 +489,9 @@ export default {
|
|
|
489
489
|
|
|
490
490
|
appStore.applicationSettings = settingsOptions
|
|
491
491
|
//=================================END SETTING PREFERENCES ====================================//
|
|
492
|
-
|
|
493
|
-
|
|
492
|
+
// const appi18b = options.i18n
|
|
493
|
+
// console.log('i18n options received in appBase plugin...', appi18b)
|
|
494
|
+
const i18n = initLocalisation(options.i18n)
|
|
494
495
|
app.use(i18n)
|
|
495
496
|
app.use(helper)
|
|
496
497
|
app.use(analytics)
|
package/src/plugins/i18n.js
CHANGED
|
@@ -7,7 +7,6 @@ import { createI18n } from 'vue-i18n'
|
|
|
7
7
|
*/
|
|
8
8
|
export default function initLocalisation(templateMessages) {
|
|
9
9
|
const i18n = createI18n({ legacy: false })
|
|
10
|
-
|
|
11
10
|
const thisLocales = import.meta.glob('../$locales/*.json', {
|
|
12
11
|
import: 'default',
|
|
13
12
|
eager: true
|
|
@@ -18,7 +17,7 @@ export default function initLocalisation(templateMessages) {
|
|
|
18
17
|
const locale = matched[1]
|
|
19
18
|
// Using lodash to deeply merge objects
|
|
20
19
|
const coreMessages = JSON.parse(JSON.stringify(thisLocales[path]))
|
|
21
|
-
const mergedMessages = _.merge(coreMessages, templateMessages)
|
|
20
|
+
const mergedMessages = _.merge(coreMessages, templateMessages[locale])
|
|
22
21
|
|
|
23
22
|
// Since merLocalMessage will do a shallow merge of the object. We will use setLocalMessage to replace the message with new message object
|
|
24
23
|
|
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
import { mount } from '@vue/test-utils'
|
|
2
|
-
import { describe, it, test, expect } from 'vitest'
|
|
2
|
+
import { describe, it, test, expect, vi } from 'vitest'
|
|
3
|
+
|
|
4
|
+
//Mock store appStore
|
|
5
|
+
vi.mock('@/module/stores/appStore', () => ({
|
|
6
|
+
useAppStore: () => ({})
|
|
7
|
+
}))
|
|
3
8
|
|
|
4
9
|
let dummyProps = {
|
|
5
10
|
id: 'P01',
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import { mount } from '@vue/test-utils'
|
|
2
|
+
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
|
3
|
+
import { createTestingPinia } from '@pinia/testing'
|
|
4
|
+
import { colors } from '../utility/colors'
|
|
5
|
+
|
|
6
|
+
//Mock store appStore
|
|
7
|
+
vi.mock('@/module/stores/appStore', () => ({
|
|
8
|
+
useAppStore: () => ({
|
|
9
|
+
getAllCompleted: { A01: [] }
|
|
10
|
+
})
|
|
11
|
+
}))
|
|
12
|
+
|
|
13
|
+
import AppCompBranchButtons from '@/components/AppCompBranchButtons.vue'
|
|
14
|
+
|
|
15
|
+
const dummyProps = {
|
|
16
|
+
cards: [
|
|
17
|
+
{
|
|
18
|
+
imgFile: 'https://loremflickr.com/500/500/kitten',
|
|
19
|
+
imgAlt: 'Test img 1',
|
|
20
|
+
title: 'banane',
|
|
21
|
+
text: 'Ceci est un embranchement factice, affiché sous la forme de carte.',
|
|
22
|
+
brchName: 'P02_E01'
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
imgFile: 'https://loremflickr.com/500/500/kitten',
|
|
26
|
+
imgAlt: 'Test img 2',
|
|
27
|
+
title: 'Titre embranchement 2',
|
|
28
|
+
text: 'Ceci est un embranchement factice, affiché sous la forme de carte, pour toto',
|
|
29
|
+
brchName: 'P02_E02'
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
imgFile: 'https://loremflickr.com/500/500/kitten',
|
|
33
|
+
imgAlt: 'Test img 3',
|
|
34
|
+
title: 'Titre embranchement 3',
|
|
35
|
+
text: 'Ceci est un embranchement factice, affiché sous la forme de carte, pour toto',
|
|
36
|
+
brchName: 'P02_E03'
|
|
37
|
+
}
|
|
38
|
+
],
|
|
39
|
+
customButtons: [
|
|
40
|
+
{ name: 'bouton_A02_P01_E01', brchName: 'P02_E01' },
|
|
41
|
+
{ name: 'bouton_A02_P01_E02', brchName: 'P02_E02' }
|
|
42
|
+
]
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
describe('AppCompBranchButtons.vue', () => {
|
|
46
|
+
let globalConfig
|
|
47
|
+
|
|
48
|
+
beforeEach(() => {
|
|
49
|
+
globalConfig = {
|
|
50
|
+
plugins: [createTestingPinia({ createSpy: vi.fn })],
|
|
51
|
+
mocks: {
|
|
52
|
+
$router: {
|
|
53
|
+
currentRoute: {
|
|
54
|
+
value: {
|
|
55
|
+
meta: {
|
|
56
|
+
activity_ref: 'A01',
|
|
57
|
+
children: [
|
|
58
|
+
{
|
|
59
|
+
_ref: 'P02_E01',
|
|
60
|
+
_path: 'branche-1',
|
|
61
|
+
_namedRoute: 'activite_3.page_2.branche_1'
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
_ref: 'P02_E02',
|
|
65
|
+
_path: 'branche-2',
|
|
66
|
+
_namedRoute: 'activite_3.page_2.branche_2'
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
_ref: 'P02_E03',
|
|
70
|
+
_path: 'branche-3',
|
|
71
|
+
_namedRoute: 'activite_3.page_2.branche_3'
|
|
72
|
+
}
|
|
73
|
+
]
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
vi.clearAllMocks()
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
it('Renders ErrorDisplay when cards has errors', async () => {
|
|
84
|
+
const wrapper = mount(AppCompBranchButtons, {
|
|
85
|
+
global: globalConfig,
|
|
86
|
+
props: {
|
|
87
|
+
cards: [dummyProps.cards[0], dummyProps.cards[1]] // Only 2 cards passed instead of 3
|
|
88
|
+
}
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
await wrapper.vm.$nextTick() // Must wait for DOM updates to display AppBaseErrorDisplay
|
|
92
|
+
|
|
93
|
+
if (wrapper.vm.hasErrors) {
|
|
94
|
+
const cards = wrapper.vm.cards
|
|
95
|
+
console.log(colors.green, `\n ✅ Current cards: ${JSON.stringify(cards)}`)
|
|
96
|
+
console.log(
|
|
97
|
+
'✅ wrapper.vm.hasErrors:',
|
|
98
|
+
wrapper.vm.hasErrors,
|
|
99
|
+
colors.reset
|
|
100
|
+
)
|
|
101
|
+
}
|
|
102
|
+
// Error should be displayed
|
|
103
|
+
expect(wrapper.vm.hasErrors.length).toBeGreaterThan(0)
|
|
104
|
+
expect(
|
|
105
|
+
wrapper.findComponent({ name: 'AppBaseErrorDisplay' }).exists()
|
|
106
|
+
).toBe(true)
|
|
107
|
+
})
|
|
108
|
+
|
|
109
|
+
it('Renders ErrorDisplay when customButtons has errors', async () => {
|
|
110
|
+
const wrapper = mount(AppCompBranchButtons, {
|
|
111
|
+
global: globalConfig,
|
|
112
|
+
props: {
|
|
113
|
+
customButtons: [
|
|
114
|
+
{ brchName: 'BRANCH_1' }, // missing required key 'name' here
|
|
115
|
+
{ name: 'bouton_A02_P01_E01', brchName: 'P02_E02' },
|
|
116
|
+
{ name: 'bouton_A02_P01_E01', brchName: 'P02_E03' }
|
|
117
|
+
]
|
|
118
|
+
}
|
|
119
|
+
})
|
|
120
|
+
await wrapper.vm.$nextTick() // Must wait for DOM updates to display AppBaseErrorDisplay
|
|
121
|
+
|
|
122
|
+
if (wrapper.vm.hasErrors) {
|
|
123
|
+
const el = wrapper.vm.customButtons[0]
|
|
124
|
+
console.log(colors.green, `\n ✅ Current cards: ${JSON.stringify(el)}`)
|
|
125
|
+
console.log(
|
|
126
|
+
'✅ wrapper.vm.hasErrors:',
|
|
127
|
+
wrapper.vm.hasErrors,
|
|
128
|
+
colors.reset
|
|
129
|
+
)
|
|
130
|
+
}
|
|
131
|
+
// hasError should have a message
|
|
132
|
+
expect(wrapper.vm.hasErrors.length).toBeGreaterThan(0)
|
|
133
|
+
// Error should be displayed
|
|
134
|
+
expect(
|
|
135
|
+
wrapper.findComponent({ name: 'AppBaseErrorDisplay' }).exists()
|
|
136
|
+
).toBe(true)
|
|
137
|
+
})
|
|
138
|
+
|
|
139
|
+
//=====================================================================
|
|
140
|
+
it('It renders the buttons when no errors', async () => {
|
|
141
|
+
const wrapper = mount(AppCompBranchButtons, {
|
|
142
|
+
global: {
|
|
143
|
+
...globalConfig
|
|
144
|
+
},
|
|
145
|
+
props: {
|
|
146
|
+
cards: dummyProps.cards
|
|
147
|
+
}
|
|
148
|
+
})
|
|
149
|
+
|
|
150
|
+
await wrapper.vm.$nextTick() // Must wait for DOM updates to display AppBaseErrorDisplay
|
|
151
|
+
|
|
152
|
+
if (wrapper.vm.hasErrors) {
|
|
153
|
+
console.log(colors.red)
|
|
154
|
+
console.log(
|
|
155
|
+
'\n wrapper.vm.hasErrors:',
|
|
156
|
+
wrapper.vm.hasErrors,
|
|
157
|
+
colors.reset
|
|
158
|
+
)
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// expect hasErrors to return false
|
|
162
|
+
expect(wrapper.vm.hasErrors).toBe(false)
|
|
163
|
+
//error display not to be in DOM
|
|
164
|
+
expect(
|
|
165
|
+
wrapper.findComponent({ name: 'AppBaseErrorDisplay' }).exists()
|
|
166
|
+
).not.toBe(true)
|
|
167
|
+
// element to rendered correctly in DOM
|
|
168
|
+
expect(wrapper.find('#branch-buttons-component').exists()).toBe(true)
|
|
169
|
+
//should display 3 buttons
|
|
170
|
+
expect(wrapper.findAll('.branch-btn').length).toBe(3)
|
|
171
|
+
})
|
|
172
|
+
})
|
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
import { mount } from '@vue/test-utils'
|
|
2
|
-
import
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
|
3
|
+
|
|
4
|
+
//Mock store appStore
|
|
5
|
+
vi.mock('@/module/stores/appStore', () => ({
|
|
6
|
+
useAppStore: () => ({
|
|
7
|
+
getAppConfigs: { lang: 'FR' }
|
|
8
|
+
})
|
|
9
|
+
}))
|
|
5
10
|
|
|
6
11
|
const dummyProps = {
|
|
7
12
|
slides: [
|
|
@@ -28,6 +33,9 @@ const dummyProps = {
|
|
|
28
33
|
name: 'Mon super carousel'
|
|
29
34
|
}
|
|
30
35
|
|
|
36
|
+
import AppCompCarousel from '@/components/AppCompCarousel.vue'
|
|
37
|
+
import AppBaseButton from '@/components/AppBaseButton.vue'
|
|
38
|
+
|
|
31
39
|
describe('AppCompCarousel.vue', () => {
|
|
32
40
|
let wrapper = null
|
|
33
41
|
beforeEach(() => {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { mount } from '@vue/test-utils'
|
|
2
|
-
import AppCompNoteCredit from '../../src/components/AppCompNoteCredit.vue'
|
|
3
2
|
import { beforeEach, describe, expect, vi, test } from 'vitest'
|
|
3
|
+
import { createTestingPinia } from '@pinia/testing'
|
|
4
4
|
|
|
5
5
|
const dummyProps = [
|
|
6
6
|
{
|
|
@@ -13,33 +13,24 @@ const dummyProps = [
|
|
|
13
13
|
}
|
|
14
14
|
]
|
|
15
15
|
|
|
16
|
+
//Mock store appStore
|
|
17
|
+
vi.mock('@/module/stores/appStore', () => ({
|
|
18
|
+
useAppStore: () => ({
|
|
19
|
+
getDataNoteCredit: dummyProps,
|
|
20
|
+
getAllActivities: 'A01',
|
|
21
|
+
getCurrentPage: 'P01'
|
|
22
|
+
})
|
|
23
|
+
}))
|
|
24
|
+
// Import of components must happen after mocks to prevent side effects
|
|
25
|
+
import AppCompNoteCredit from '../../src/components/AppCompNoteCredit.vue'
|
|
26
|
+
|
|
16
27
|
describe('AppCompNoteCredit', () => {
|
|
17
28
|
let wrapper = null
|
|
18
29
|
|
|
19
30
|
beforeEach(() => {
|
|
20
|
-
vi.mock('@/shared/generalfuncs.js', () => ({
|
|
21
|
-
fileAssets: { getActivities: () => [] }
|
|
22
|
-
}))
|
|
23
|
-
|
|
24
|
-
vi.mock('@/router/routes.js', () => ({
|
|
25
|
-
mappedFiles: []
|
|
26
|
-
}))
|
|
27
|
-
|
|
28
|
-
vi.mock('@/stores/myStore', () => ({
|
|
29
|
-
useMyStore: vi.fn(() => ({
|
|
30
|
-
getDataNoteCredit: dummyProps,
|
|
31
|
-
getAllActivities: 'A01',
|
|
32
|
-
getCurrentPage: 'P01'
|
|
33
|
-
}))
|
|
34
|
-
}))
|
|
35
|
-
|
|
36
31
|
wrapper = mount(AppCompNoteCredit, {
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
'app-base-button': true,
|
|
40
|
-
'app-base-error-display': true
|
|
41
|
-
}
|
|
42
|
-
}
|
|
32
|
+
plugins: [createTestingPinia({ createSpy: vi.fn })],
|
|
33
|
+
global
|
|
43
34
|
})
|
|
44
35
|
})
|
|
45
36
|
|
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
import { mount } from '@vue/test-utils'
|
|
2
|
-
import { describe, it, test, expect } from 'vitest'
|
|
2
|
+
import { describe, it, test, expect, vi } from 'vitest'
|
|
3
|
+
|
|
4
|
+
//Mock store appStore
|
|
5
|
+
vi.mock('@/module/stores/appStore', () => ({
|
|
6
|
+
useAppStore: () => ({})
|
|
7
|
+
}))
|
|
3
8
|
|
|
4
9
|
let dummyProps = {
|
|
5
10
|
id: 'P01',
|
|
@@ -99,8 +104,6 @@ describe('AppCompVideoPlayer', () => {
|
|
|
99
104
|
await wrapper.vm.$nextTick()
|
|
100
105
|
const sources = wrapper.findAll('source')
|
|
101
106
|
expect(sources.length).toBe(1)
|
|
102
|
-
|
|
103
|
-
// --- 3) valider les valeurs réelles rendues ---
|
|
104
107
|
expect(sources[0].attributes().src).toBe(videosData[0].mSources[0].src)
|
|
105
108
|
expect(sources[0].attributes().type).toBe('video/mp4')
|
|
106
109
|
})
|
package/vitest.config.js
CHANGED
|
@@ -13,30 +13,42 @@ export default defineConfig({
|
|
|
13
13
|
}
|
|
14
14
|
}
|
|
15
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
|
+
|
|
16
26
|
test: {
|
|
17
27
|
setupFiles: ['vitest.setup.js'],
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
},
|
|
28
|
+
environment: 'happy-dom',
|
|
29
|
+
|
|
21
30
|
server: {
|
|
22
31
|
deps: {
|
|
23
32
|
inline: ['vuetify']
|
|
24
33
|
}
|
|
25
34
|
},
|
|
26
|
-
|
|
35
|
+
|
|
27
36
|
exclude: [...configDefaults.exclude, 'tests/component/**', '**/_*.spec.js'],
|
|
37
|
+
|
|
28
38
|
root: fileURLToPath(new URL('./', import.meta.url)),
|
|
39
|
+
|
|
29
40
|
coverage: {
|
|
30
41
|
provider: 'v8',
|
|
31
42
|
reporter: ['text', 'json', 'cobertura'],
|
|
32
43
|
reportsDirectory: './coverage',
|
|
33
44
|
include: ['src/**/*.{js,vue}'],
|
|
34
45
|
exclude: ['tests/**/*'],
|
|
35
|
-
lines: 0,
|
|
36
|
-
branches: 0,
|
|
37
|
-
functions: 0,
|
|
38
|
-
statements: 0
|
|
46
|
+
lines: 0,
|
|
47
|
+
branches: 0,
|
|
48
|
+
functions: 0,
|
|
49
|
+
statements: 0
|
|
39
50
|
}
|
|
40
51
|
},
|
|
52
|
+
|
|
41
53
|
plugins: [vue()]
|
|
42
54
|
})
|
package/vitest.setup.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
//Mock Vuetify, vue-i18n and event bus
|
|
2
|
-
import { vi } from 'vitest'
|
|
2
|
+
import { vi, beforeEach } from 'vitest'
|
|
3
3
|
import { config } from '@vue/test-utils'
|
|
4
4
|
import { createI18n } from 'vue-i18n'
|
|
5
5
|
import { createVuetify } from 'vuetify'
|
|
@@ -8,6 +8,7 @@ import * as directives from 'vuetify/directives'
|
|
|
8
8
|
import * as frMessages from './src/$locales/fr.json'
|
|
9
9
|
import * as enMessages from './src/$locales/en.json'
|
|
10
10
|
import bus from './src/plugins/bus'
|
|
11
|
+
import { createPinia, setActivePinia } from 'pinia'
|
|
11
12
|
|
|
12
13
|
//create vuetify instance
|
|
13
14
|
const vuetify = createVuetify({
|
|
@@ -25,38 +26,20 @@ const i18n = createI18n({
|
|
|
25
26
|
}
|
|
26
27
|
})
|
|
27
28
|
|
|
28
|
-
// Suppress console warnings and errors during tests
|
|
29
29
|
vi.spyOn(console, 'warn').mockImplementation(() => {})
|
|
30
30
|
vi.spyOn(console, 'error').mockImplementation(() => {})
|
|
31
31
|
|
|
32
32
|
// Global mock of Router
|
|
33
33
|
// =========================================
|
|
34
|
-
vi.mock('
|
|
35
|
-
|
|
34
|
+
vi.mock('vue-router', () => ({
|
|
35
|
+
useRouter: () => ({
|
|
36
36
|
push: vi.fn(),
|
|
37
|
-
back: vi.fn()
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
// =========================================
|
|
44
|
-
vi.mock('@/module/stores/appStore', () => ({
|
|
45
|
-
useAppStore: () => ({
|
|
46
|
-
getAppConfigs: {
|
|
47
|
-
lang: 'fr'
|
|
48
|
-
},
|
|
49
|
-
|
|
50
|
-
getCurrentBrowser: 'Chrome',
|
|
51
|
-
|
|
52
|
-
getCurrentPage: {
|
|
53
|
-
id: 'P01',
|
|
54
|
-
activityRef: 'A03'
|
|
55
|
-
},
|
|
56
|
-
|
|
57
|
-
updateCurrentMediaElements: vi.fn(() => Promise.resolve()),
|
|
58
|
-
getPageInteraction: {},
|
|
59
|
-
getUserInteraction: {}
|
|
37
|
+
back: vi.fn()
|
|
38
|
+
}),
|
|
39
|
+
useRoute: () => ({
|
|
40
|
+
meta: {},
|
|
41
|
+
params: {},
|
|
42
|
+
query: {}
|
|
60
43
|
})
|
|
61
44
|
}))
|
|
62
45
|
|
|
@@ -67,13 +50,28 @@ vi.mock('@/shared/validators.js', () => ({
|
|
|
67
50
|
validateString: vi.fn(() => true),
|
|
68
51
|
validateNumber: vi.fn(() => true),
|
|
69
52
|
validateVideoData: vi.fn(() => []),
|
|
70
|
-
validateAudioData: vi.fn(() => [])
|
|
53
|
+
validateAudioData: vi.fn(() => []),
|
|
54
|
+
validateProp: vi.fn(() => [])
|
|
71
55
|
}))
|
|
72
56
|
|
|
73
57
|
//setup as global plugins used by all tests
|
|
74
|
-
config.global.plugins
|
|
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
|
+
})
|
|
75
72
|
|
|
76
73
|
// Global mock of components
|
|
74
|
+
//=========================================
|
|
77
75
|
config.global.stubs = {
|
|
78
76
|
AppBaseErrorDisplay: true,
|
|
79
77
|
AppBaseSkeleton: true,
|
|
@@ -92,5 +90,8 @@ config.global.mocks = {
|
|
|
92
90
|
$on: vi.fn(),
|
|
93
91
|
$emit: vi.fn(),
|
|
94
92
|
$off: vi.fn()
|
|
93
|
+
},
|
|
94
|
+
$helper: {
|
|
95
|
+
formatTime: vi.fn((t) => `00:${t}`)
|
|
95
96
|
}
|
|
96
97
|
}
|
|
@@ -1,112 +0,0 @@
|
|
|
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', async() => {
|
|
81
|
-
// const wrapper = mount(AppCompQuizNext, {
|
|
82
|
-
// props: {
|
|
83
|
-
// question: {
|
|
84
|
-
// id: 2,
|
|
85
|
-
// type: 'text'
|
|
86
|
-
// },
|
|
87
|
-
// modelValue: ''
|
|
88
|
-
// },
|
|
89
|
-
// global: { stubs }
|
|
90
|
-
// })
|
|
91
|
-
// expect(wrapper.find('.stub-text').exists()).toBe(true)
|
|
92
|
-
// })
|
|
93
|
-
|
|
94
|
-
// it('émet update:modelValue quand l’utilisateur répond', async () => {
|
|
95
|
-
// const wrapper = mount(AppCompQuizNext, {
|
|
96
|
-
// props: {
|
|
97
|
-
// question: { id: 1, type: 'text' },
|
|
98
|
-
// modelValue: ''
|
|
99
|
-
// },
|
|
100
|
-
// global: {
|
|
101
|
-
// stubs: {
|
|
102
|
-
// AppCompInputTextNx: {
|
|
103
|
-
// template: `<input @input="$emit('update:modelValue', 'hello')" />`
|
|
104
|
-
// }
|
|
105
|
-
// }
|
|
106
|
-
// }
|
|
107
|
-
// })
|
|
108
|
-
// await wrapper.find('input').trigger('input')
|
|
109
|
-
|
|
110
|
-
// expect(wrapper.emitted()['update:modelValue'][0]).toEqual(['hello'])
|
|
111
|
-
// })
|
|
112
|
-
})
|