fcad-core-dragon 2.2.0-beta.2 → 2.2.0
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 +3 -0
- package/artifacts/playwright-report/index.html +1 -1
- package/package.json +2 -1
- package/src/components/AppCompMenuItem.vue +2 -2
- package/src/components/AppCompNoteCredit.vue +9 -13
- package/src/module/stores/appStore.js +0 -7
- package/src/shared/validators.js +8 -21
- package/tests/unit/AppCompNoteCredit.spec.js +132 -17
- package/tests/unit/AppCompPlayBarNext.spec.js +428 -0
- package/vitest.setup.js +6 -5
- package/.vscode/extensions.json +0 -8
- package/.vscode/settings.json +0 -16
package/CHANGELOG
CHANGED
|
@@ -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
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
"@pinia/testing": "^1.0.3",
|
|
22
22
|
"@playwright/experimental-ct-vue": "^1.56.1",
|
|
23
23
|
"@playwright/test": "^1.56.1",
|
|
24
|
+
"@testing-library/jest-dom": "^6.9.1",
|
|
24
25
|
"@types/node": "^24.10.0",
|
|
25
26
|
"@vitejs/plugin-vue": "^6.0.1",
|
|
26
27
|
"@vitest/coverage-v8": "^4.0.15",
|
|
@@ -65,5 +66,5 @@
|
|
|
65
66
|
"watch": "nodemon -e js,vue,html,json -x yalc publish --push"
|
|
66
67
|
},
|
|
67
68
|
"type": "module",
|
|
68
|
-
"version": "2.2.0
|
|
69
|
+
"version": "2.2.0"
|
|
69
70
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<!--
|
|
2
|
-
@ Description: This component is used to display and create the
|
|
3
|
-
@ What it does: Goes
|
|
2
|
+
@ Description: This component is used to display and create the links for all the activities in module.
|
|
3
|
+
@ What it does: Goes through all the activities in the router and create a card that navigate to the corresponding activity. Display the title and subtitle enter in menu.json. Must be used with AppCompTableOfContent and AppCompMenu.
|
|
4
4
|
-->
|
|
5
5
|
<template>
|
|
6
6
|
<v-row v-if="activities.length" class="box-msa">
|
|
@@ -70,6 +70,7 @@
|
|
|
70
70
|
v-for="(credit, index) of credits"
|
|
71
71
|
:key="`credit_${index + 1}`"
|
|
72
72
|
:ref="`#nt_${index + 1}`"
|
|
73
|
+
class="item-credits"
|
|
73
74
|
v-html="credit"
|
|
74
75
|
></li>
|
|
75
76
|
</ul>
|
|
@@ -79,7 +80,7 @@
|
|
|
79
80
|
</div>
|
|
80
81
|
</template>
|
|
81
82
|
<script>
|
|
82
|
-
import {
|
|
83
|
+
import { mapActions } from 'pinia'
|
|
83
84
|
import { useAppStore } from '../module/stores/appStore'
|
|
84
85
|
import { validateObjType } from '../shared/validators'
|
|
85
86
|
import { useI18n } from 'vue-i18n'
|
|
@@ -99,17 +100,11 @@ export default {
|
|
|
99
100
|
shouldDeactivate: false,
|
|
100
101
|
errorData: [],
|
|
101
102
|
notes: null,
|
|
102
|
-
credits: null
|
|
103
|
+
credits: null,
|
|
104
|
+
test: null
|
|
103
105
|
}
|
|
104
106
|
},
|
|
105
107
|
computed: {
|
|
106
|
-
...mapState(useAppStore, [
|
|
107
|
-
'getDataNoteCredit',
|
|
108
|
-
'getAllActivities',
|
|
109
|
-
'getCurrentPage',
|
|
110
|
-
'getNotes',
|
|
111
|
-
'getCredits'
|
|
112
|
-
]),
|
|
113
108
|
hasNotes() {
|
|
114
109
|
return this.notes && this.notes.length
|
|
115
110
|
},
|
|
@@ -156,7 +151,6 @@ export default {
|
|
|
156
151
|
//Add all the note to the temp array
|
|
157
152
|
formatedNotes.push(...Object.values(g)[0])
|
|
158
153
|
})
|
|
159
|
-
|
|
160
154
|
return formatedNotes
|
|
161
155
|
},
|
|
162
156
|
|
|
@@ -174,6 +168,7 @@ export default {
|
|
|
174
168
|
},
|
|
175
169
|
setPageNotesAndCredits(data) {
|
|
176
170
|
const { notes, credits } = data
|
|
171
|
+
|
|
177
172
|
this.notes = this.getPageNotes(notes)
|
|
178
173
|
this.credits = this.getPageCredits(credits)
|
|
179
174
|
},
|
|
@@ -186,7 +181,6 @@ export default {
|
|
|
186
181
|
validateData(ctx, data) {
|
|
187
182
|
let errConsole = []
|
|
188
183
|
if (this.errorData.length) this.errorData = [] //reset the error tracker
|
|
189
|
-
|
|
190
184
|
switch (ctx) {
|
|
191
185
|
case 'note': {
|
|
192
186
|
if (data.constructor !== Object) {
|
|
@@ -197,7 +191,6 @@ export default {
|
|
|
197
191
|
`Unexpected definition the note. Expecting Object but received ${typeof data}`
|
|
198
192
|
)
|
|
199
193
|
}
|
|
200
|
-
|
|
201
194
|
let stringType = ['id', 'text'] //expected attribute in the note declaration
|
|
202
195
|
|
|
203
196
|
let { errorInConsole, errorList } = validateObjType(
|
|
@@ -208,7 +201,6 @@ export default {
|
|
|
208
201
|
null,
|
|
209
202
|
`Note ${data.details}`
|
|
210
203
|
)
|
|
211
|
-
|
|
212
204
|
if (errorInConsole.length) errConsole.push(...errorInConsole)
|
|
213
205
|
if (errorList.length) this.errorData.push(...errorList)
|
|
214
206
|
break
|
|
@@ -359,6 +351,10 @@ export default {
|
|
|
359
351
|
const elTarget = widgetContent.namedItem(ref) // Target current button
|
|
360
352
|
const pageRef = elTarget.getAttribute('pageref')
|
|
361
353
|
|
|
354
|
+
console.log('widgetContent :', widgetContent)
|
|
355
|
+
console.log('elTarget :', elTarget)
|
|
356
|
+
console.log('pageRef :', pageRef)
|
|
357
|
+
|
|
362
358
|
if (!this.sideBarIsOpen) this.prevNote = null //reset previous when sidebar is opened
|
|
363
359
|
|
|
364
360
|
let prevPageref = this.prevNote
|
|
@@ -27,7 +27,6 @@ export const useAppStore = defineStore('$appStore', {
|
|
|
27
27
|
userMetaData: {},
|
|
28
28
|
appConfigs: null,
|
|
29
29
|
menuSetting: null,
|
|
30
|
-
dataNc: {},
|
|
31
30
|
wigIsOpen: false,
|
|
32
31
|
popIsOpen: false,
|
|
33
32
|
sideBIsOpen: false,
|
|
@@ -596,9 +595,6 @@ export const useAppStore = defineStore('$appStore', {
|
|
|
596
595
|
getDataFromServer: (state) => {
|
|
597
596
|
return state.dataFromServer
|
|
598
597
|
},
|
|
599
|
-
getDataNoteCredit: (state) => {
|
|
600
|
-
return state.dataNc
|
|
601
|
-
},
|
|
602
598
|
getWidgetOpen(state) {
|
|
603
599
|
return state.wigIsOpen
|
|
604
600
|
},
|
|
@@ -801,9 +797,6 @@ export const useAppStore = defineStore('$appStore', {
|
|
|
801
797
|
)
|
|
802
798
|
this.currentSection = data
|
|
803
799
|
},
|
|
804
|
-
updateCurrentnoteCredit(data) {
|
|
805
|
-
this.dataNc = data
|
|
806
|
-
},
|
|
807
800
|
/**
|
|
808
801
|
* @description: Save user interaction on a page to store
|
|
809
802
|
* USe to Update existing userMetadata
|
package/src/shared/validators.js
CHANGED
|
@@ -955,18 +955,9 @@ const validateQuizData = (data) => {
|
|
|
955
955
|
if (hasError) return errors
|
|
956
956
|
|
|
957
957
|
//validate Containt for retroaction
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
// if(data.solution !=null){
|
|
961
|
-
// console.log('ici')
|
|
962
|
-
// }else{
|
|
963
|
-
// console.log('la')
|
|
964
|
-
// }
|
|
965
|
-
|
|
966
958
|
switch (true) {
|
|
967
959
|
case data.retroaction != null:
|
|
968
960
|
objectType = ['retro_positive', 'retro_negative', 'retro_neutre']
|
|
969
|
-
|
|
970
961
|
|
|
971
962
|
errors = validateObjType(
|
|
972
963
|
data.retroaction,
|
|
@@ -983,19 +974,15 @@ const validateQuizData = (data) => {
|
|
|
983
974
|
// Validate containt of retroaction attributes
|
|
984
975
|
stringType = ['title', 'hypertext']
|
|
985
976
|
|
|
986
|
-
|
|
987
977
|
for (const key of Object.keys(data.retroaction)) {
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
null,
|
|
997
|
-
key
|
|
998
|
-
)
|
|
978
|
+
if (key == 'retro_neutre') continue //skip
|
|
979
|
+
|
|
980
|
+
errors = validateObjType(
|
|
981
|
+
data.retroaction[key],
|
|
982
|
+
{ stringType },
|
|
983
|
+
null,
|
|
984
|
+
key
|
|
985
|
+
)
|
|
999
986
|
|
|
1000
987
|
hasError = errors.errorInConsole.length || errors.errorList.length
|
|
1001
988
|
|
|
@@ -1,32 +1,45 @@
|
|
|
1
1
|
import { mount } from '@vue/test-utils'
|
|
2
|
-
import { beforeEach, describe, expect, vi, test } from 'vitest'
|
|
2
|
+
import { beforeEach, describe, expect, vi, test, it } from 'vitest'
|
|
3
3
|
import { createTestingPinia } from '@pinia/testing'
|
|
4
4
|
|
|
5
|
-
const
|
|
5
|
+
const dummyPropsNotes = [
|
|
6
6
|
{
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
7
|
+
P01: [
|
|
8
|
+
{
|
|
9
|
+
id: 'nt_1',
|
|
10
|
+
text: 'note 1',
|
|
11
|
+
page_ref: 'P01'
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
id: 'nt_2',
|
|
15
|
+
text: 'note 2',
|
|
16
|
+
page_ref: 'P01'
|
|
17
|
+
}
|
|
18
|
+
]
|
|
13
19
|
}
|
|
14
20
|
]
|
|
15
21
|
|
|
22
|
+
const dummyPropsCredits = ['credits 1', 'credits 2', 'credits 3']
|
|
23
|
+
|
|
16
24
|
//Mock store appStore
|
|
17
25
|
vi.mock('@/module/stores/appStore', () => ({
|
|
18
|
-
useAppStore: () => ({
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
26
|
+
useAppStore: () => ({ updateWidgetOpen: vi.fn() })
|
|
27
|
+
}))
|
|
28
|
+
|
|
29
|
+
//Mock Error handeling
|
|
30
|
+
vi.mock('@/shared/validators.js', () => ({
|
|
31
|
+
validateObjType: vi.fn(() => {
|
|
32
|
+
return { errorInConsole: [], errorList: [] }
|
|
22
33
|
})
|
|
23
34
|
}))
|
|
35
|
+
|
|
24
36
|
// Import of components must happen after mocks to prevent side effects
|
|
25
37
|
import AppCompNoteCredit from '../../src/components/AppCompNoteCredit.vue'
|
|
26
38
|
|
|
27
39
|
describe('AppCompNoteCredit', () => {
|
|
28
40
|
let wrapper = null
|
|
29
41
|
|
|
42
|
+
//Mounting componant
|
|
30
43
|
beforeEach(() => {
|
|
31
44
|
wrapper = mount(AppCompNoteCredit, {
|
|
32
45
|
plugins: [createTestingPinia({ createSpy: vi.fn })],
|
|
@@ -34,16 +47,118 @@ describe('AppCompNoteCredit', () => {
|
|
|
34
47
|
})
|
|
35
48
|
})
|
|
36
49
|
|
|
37
|
-
test('It renders
|
|
50
|
+
test('It renders componant with notes and credits', async () => {
|
|
51
|
+
const wrapper = mount(AppCompNoteCredit)
|
|
52
|
+
|
|
53
|
+
//Give information
|
|
54
|
+
const d = {
|
|
55
|
+
type: 'noteCredit',
|
|
56
|
+
content: { notes: dummyPropsNotes, credits: dummyPropsCredits }
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
//Filling the componant
|
|
60
|
+
await wrapper.vm.onToggleWidget(d)
|
|
61
|
+
expect(wrapper.exists()).toBe(true)
|
|
62
|
+
|
|
63
|
+
//Find all the information that was renders
|
|
64
|
+
const ctnNotes = wrapper.find('#notes-list')
|
|
65
|
+
const ctnCredits = wrapper.find('#credits-list')
|
|
66
|
+
|
|
67
|
+
//Expect test result
|
|
68
|
+
expect(ctnNotes.exists()).toBe(true)
|
|
69
|
+
expect(ctnCredits.exists()).toBe(true)
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
test('It renders componant with only notes', async () => {
|
|
38
73
|
const wrapper = mount(AppCompNoteCredit)
|
|
39
74
|
|
|
75
|
+
//Give information
|
|
76
|
+
const d = {
|
|
77
|
+
type: 'noteCredit',
|
|
78
|
+
content: { notes: dummyPropsNotes, credits: null }
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
//Filling the componant
|
|
82
|
+
await wrapper.vm.onToggleWidget(d)
|
|
40
83
|
expect(wrapper.exists()).toBe(true)
|
|
41
84
|
|
|
42
|
-
|
|
43
|
-
|
|
85
|
+
//Find all the information that was renders
|
|
86
|
+
const divNotes = wrapper.find('#notes-list')
|
|
87
|
+
const divCredits = wrapper.find('#credits-list')
|
|
88
|
+
|
|
89
|
+
//Expect test result
|
|
90
|
+
expect(divNotes.exists()).toBe(true)
|
|
91
|
+
expect(divCredits.exists()).toBe(false)
|
|
44
92
|
})
|
|
45
93
|
|
|
46
|
-
test('It renders
|
|
47
|
-
|
|
94
|
+
test('It renders componant with only credits', async () => {
|
|
95
|
+
const wrapper = mount(AppCompNoteCredit)
|
|
96
|
+
|
|
97
|
+
//Give information
|
|
98
|
+
const d = {
|
|
99
|
+
type: 'noteCredit',
|
|
100
|
+
content: { notes: null, credits: dummyPropsCredits }
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
//Filling the componant
|
|
104
|
+
await wrapper.vm.onToggleWidget(d)
|
|
105
|
+
expect(wrapper.exists()).toBe(true)
|
|
106
|
+
|
|
107
|
+
//Find all the information that was renders
|
|
108
|
+
const divNotes = wrapper.find('#notes-list')
|
|
109
|
+
const divCredits = wrapper.find('#credits-list')
|
|
110
|
+
|
|
111
|
+
//Expect test result
|
|
112
|
+
expect(divNotes.exists()).toBe(false)
|
|
113
|
+
expect(divCredits.exists()).toBe(true)
|
|
114
|
+
})
|
|
115
|
+
|
|
116
|
+
test('It renders a list of present notes and credits', async () => {
|
|
117
|
+
const wrapper = mount(AppCompNoteCredit)
|
|
118
|
+
|
|
119
|
+
//Give information
|
|
120
|
+
const d = {
|
|
121
|
+
type: 'noteCredit',
|
|
122
|
+
content: { notes: dummyPropsNotes, credits: dummyPropsCredits }
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
//Filling the componant
|
|
126
|
+
await wrapper.vm.onToggleWidget(d)
|
|
127
|
+
|
|
128
|
+
//Find all the information that was renders
|
|
129
|
+
const notes = Object.values(dummyPropsNotes[0])
|
|
130
|
+
|
|
131
|
+
//Expect test result
|
|
132
|
+
expect(wrapper.findAll('.note-item').length).toBe(notes[0].length)
|
|
133
|
+
expect(wrapper.findAll('.item-credits').length).toBe(
|
|
134
|
+
dummyPropsCredits.length
|
|
135
|
+
)
|
|
136
|
+
})
|
|
137
|
+
|
|
138
|
+
test('It render all the rigth information in button data', async () => {
|
|
139
|
+
//Give information
|
|
140
|
+
const d = {
|
|
141
|
+
type: 'noteCredit',
|
|
142
|
+
content: { notes: dummyPropsNotes }
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
//Filling the componant
|
|
146
|
+
await wrapper.vm.onToggleWidget(d)
|
|
147
|
+
|
|
148
|
+
//Find all the information that was renders
|
|
149
|
+
const notes = Object.values(dummyPropsNotes[0])
|
|
150
|
+
const note = notes[0][0]
|
|
151
|
+
const div = wrapper.find(`#nt_${note.page_ref}__${note.id.substring(3)}`)
|
|
152
|
+
|
|
153
|
+
//Expect test result
|
|
154
|
+
expect(div.attributes('note-ref')).toBe(
|
|
155
|
+
`rnt_${note.page_ref}__${note.id.substring(3)}`
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
expect(div.attributes('data-ref')).toBe(
|
|
159
|
+
`rnt_${note.page_ref}__${note.id.substring(3)}`
|
|
160
|
+
)
|
|
161
|
+
|
|
162
|
+
expect(div.attributes('pageref')).toBe(note.page_ref)
|
|
48
163
|
})
|
|
49
164
|
})
|
|
@@ -0,0 +1,428 @@
|
|
|
1
|
+
import { mount } from '@vue/test-utils'
|
|
2
|
+
import { beforeEach, afterAll, describe, expect, it, vi } from 'vitest'
|
|
3
|
+
import { createTestingPinia } from '@pinia/testing'
|
|
4
|
+
|
|
5
|
+
//TODO:
|
|
6
|
+
// 1- test pour valider que le composant rend le bon player selon le type de media (audio/video)
|
|
7
|
+
// 1b- verfifier recuperation du bon media media data(getCUrrentPage.mElements[i])
|
|
8
|
+
// 2- test pour valider le trigger des evenemets:
|
|
9
|
+
// - play & pause (toggglePlay): OK
|
|
10
|
+
// - ended (mediaEnded): OK
|
|
11
|
+
// - volume change (updateVolumeLevel): OK
|
|
12
|
+
// - mute toggle (toggleMute): OK
|
|
13
|
+
// - fullscreen toggle (toggleFullscreen): A FAIRE
|
|
14
|
+
// 3- test pour valider l'affichage du transcript :OK
|
|
15
|
+
|
|
16
|
+
//Helper to create a mock of media container with event listeners
|
|
17
|
+
const makeMediaContainer = () => {
|
|
18
|
+
const listeners = new Map()
|
|
19
|
+
return {
|
|
20
|
+
addEventListener: vi.fn((name, fn) => listeners.set(name, fn)),
|
|
21
|
+
removeEventListener: vi.fn((name) => listeners.delete(name)),
|
|
22
|
+
requestFullscreen: vi.fn(async () => {}),
|
|
23
|
+
dispatchEvent: (name, payload) => listeners.get(name)?.(payload)
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
//Helper to create a mock of media element with event listeners and methods
|
|
28
|
+
const makeMediaElement = (id, type, duration) => {
|
|
29
|
+
const listeners = new Map()
|
|
30
|
+
const el = {
|
|
31
|
+
id,
|
|
32
|
+
tagName: type.toUpperCase(),
|
|
33
|
+
duration,
|
|
34
|
+
currentTime: 0,
|
|
35
|
+
volume: 0.5,
|
|
36
|
+
muted: false,
|
|
37
|
+
paused: true,
|
|
38
|
+
play: vi.fn(async () => {
|
|
39
|
+
el.paused = false
|
|
40
|
+
}),
|
|
41
|
+
pause: vi.fn(() => {
|
|
42
|
+
el.paused = true
|
|
43
|
+
}),
|
|
44
|
+
addEventListener: vi.fn((name, fn) => listeners.set(name, fn)),
|
|
45
|
+
removeEventListener: vi.fn((name) => listeners.delete(name)),
|
|
46
|
+
dispatchEvent: (name, payload) => listeners.get(name)?.(payload)
|
|
47
|
+
}
|
|
48
|
+
return el
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
let defineMediaToPlay = (type, duration) => {
|
|
52
|
+
if (type === 'video') {
|
|
53
|
+
const id = 'vid1'
|
|
54
|
+
return {
|
|
55
|
+
id,
|
|
56
|
+
mElement: makeMediaElement(id, type, duration || 20),
|
|
57
|
+
mMediaContainer: makeMediaContainer(),
|
|
58
|
+
mSubtitles: {},
|
|
59
|
+
mTranscript: 'exemple_transcript.html',
|
|
60
|
+
mType: 'video'
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (type === 'audio') {
|
|
65
|
+
const id = 'aud1'
|
|
66
|
+
return {
|
|
67
|
+
id,
|
|
68
|
+
mElement: makeMediaElement(id, type, duration || 30),
|
|
69
|
+
mTranscript:
|
|
70
|
+
'<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.</p>',
|
|
71
|
+
mType: 'audio'
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
//Helper to return the sctructure of an HTMLElement
|
|
77
|
+
const getStructure = (el) => ({
|
|
78
|
+
tag: el && el.tagName ? el.tagName.toLowerCase() : null,
|
|
79
|
+
class: el.className || null,
|
|
80
|
+
children:
|
|
81
|
+
el.children.length > 0 ? [...el.children].map((c) => getStructure(c)) : []
|
|
82
|
+
})
|
|
83
|
+
|
|
84
|
+
const mockedStore = {
|
|
85
|
+
getCurrentBrowser: () => 'chrome',
|
|
86
|
+
getPageInteraction: vi.fn(() => ({
|
|
87
|
+
userActivation: {
|
|
88
|
+
mediasViewed: []
|
|
89
|
+
}
|
|
90
|
+
})),
|
|
91
|
+
getUserInteraction: () => ({}),
|
|
92
|
+
getModuleInfo: { id: 'MOD-1' },
|
|
93
|
+
getCurrentPage: {
|
|
94
|
+
id: 'P01',
|
|
95
|
+
activityRef: 'A03',
|
|
96
|
+
title: 'Lecteurs médias',
|
|
97
|
+
type: 'pg_normal',
|
|
98
|
+
audiosData: [
|
|
99
|
+
{
|
|
100
|
+
id: 'aud1',
|
|
101
|
+
aSources: [{ type: 'mp3', src: '/audios/aud1.mp3' }],
|
|
102
|
+
aTranscript: 'exemple_transcript.html',
|
|
103
|
+
mSources: [{ src: '/audios/aud1.mp3' }],
|
|
104
|
+
mTitle: 'Audio 1'
|
|
105
|
+
}
|
|
106
|
+
],
|
|
107
|
+
videosData: [
|
|
108
|
+
{
|
|
109
|
+
id: 'vid1',
|
|
110
|
+
mSources: [{ src: '/videos/vid1.mp4' }],
|
|
111
|
+
mTitle: 'Video 1'
|
|
112
|
+
}
|
|
113
|
+
]
|
|
114
|
+
},
|
|
115
|
+
getMedidaMuted: () => false,
|
|
116
|
+
getMediaSubTitles: () => false,
|
|
117
|
+
getCurrentMediaDuration: () => 20,
|
|
118
|
+
getMediaPlaybarValues: (key) => {
|
|
119
|
+
if (key === 'volume') return 0.5
|
|
120
|
+
return null
|
|
121
|
+
},
|
|
122
|
+
setMediaPlaybarValues: vi.fn(),
|
|
123
|
+
updateCurrentMediaElements: vi.fn(),
|
|
124
|
+
setMediaMuted: vi.fn(),
|
|
125
|
+
setMediaSubTitles: vi.fn()
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/*
|
|
129
|
+
* Helper to flush all pending promises
|
|
130
|
+
* Useful when mocking axios calls or other async operations
|
|
131
|
+
* This ensures that all microtasks are completed before proceeding with assertions
|
|
132
|
+
*/
|
|
133
|
+
const flush = async () => {
|
|
134
|
+
await Promise.resolve()
|
|
135
|
+
await Promise.resolve()
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
//Mock store appStore
|
|
139
|
+
vi.mock('@/module/stores/appStore', () => ({
|
|
140
|
+
useAppStore: () => mockedStore
|
|
141
|
+
}))
|
|
142
|
+
|
|
143
|
+
//Mock axios
|
|
144
|
+
vi.mock('axios', () => ({
|
|
145
|
+
default: {
|
|
146
|
+
get: vi.fn(async () => ({
|
|
147
|
+
data: {
|
|
148
|
+
// component calls: res.data.text()
|
|
149
|
+
text: async () => '<div id="mock-transcript">Transcript OK</div>'
|
|
150
|
+
}
|
|
151
|
+
}))
|
|
152
|
+
}
|
|
153
|
+
}))
|
|
154
|
+
|
|
155
|
+
import AppCompPlayBarNext from '@/components/AppCompPlayBarNext.vue'
|
|
156
|
+
import axios from 'axios'
|
|
157
|
+
|
|
158
|
+
describe('AppCompPlayBarNext', () => {
|
|
159
|
+
let globalConfig
|
|
160
|
+
let playBarContainer
|
|
161
|
+
let wrapper
|
|
162
|
+
const mockContainerRef = { className: 'pb-container' }
|
|
163
|
+
let $bus
|
|
164
|
+
let $analytics
|
|
165
|
+
|
|
166
|
+
beforeEach(() => {
|
|
167
|
+
vi.clearAllMocks()
|
|
168
|
+
$bus = {
|
|
169
|
+
$emit: vi.fn(),
|
|
170
|
+
$on: vi.fn(),
|
|
171
|
+
$off: vi.fn()
|
|
172
|
+
}
|
|
173
|
+
$analytics = {
|
|
174
|
+
sendEvent: vi.fn()
|
|
175
|
+
}
|
|
176
|
+
globalConfig = {
|
|
177
|
+
plugins: [createTestingPinia({ createSpy: vi.fn })],
|
|
178
|
+
provide: {
|
|
179
|
+
userInteraction: {}
|
|
180
|
+
},
|
|
181
|
+
stubs: {
|
|
182
|
+
// Replace custom button component with a real <button>
|
|
183
|
+
'app-base-button': {
|
|
184
|
+
template: '<button><slot /></button>'
|
|
185
|
+
}
|
|
186
|
+
},
|
|
187
|
+
mocks: {
|
|
188
|
+
$router: {
|
|
189
|
+
currentRoute: {
|
|
190
|
+
value: {
|
|
191
|
+
meta: {
|
|
192
|
+
activity_ref: 'A03',
|
|
193
|
+
id: 'P01'
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
},
|
|
198
|
+
$bus,
|
|
199
|
+
$analytics,
|
|
200
|
+
computed: {
|
|
201
|
+
mediaDuration: vi.fn(() => 20)
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
})
|
|
206
|
+
|
|
207
|
+
afterAll(() => {
|
|
208
|
+
wrapper.unmount()
|
|
209
|
+
})
|
|
210
|
+
|
|
211
|
+
describe('It renders the correct HtmlElement', () => {
|
|
212
|
+
it('Renders the correct player instance (id = plyr_<mediaId>) for video', async () => {
|
|
213
|
+
const mediaType = 'video'
|
|
214
|
+
wrapper = mount(AppCompPlayBarNext, {
|
|
215
|
+
props: {
|
|
216
|
+
mediaToPlay: defineMediaToPlay(mediaType)
|
|
217
|
+
}
|
|
218
|
+
})
|
|
219
|
+
|
|
220
|
+
await wrapper.vm.$nextTick()
|
|
221
|
+
//Should render a div with class pb-container, div with class video and id plyr_vid1
|
|
222
|
+
playBarContainer = wrapper.find(`.pb-container`)
|
|
223
|
+
const playBarElement = playBarContainer.find(`.${mediaType}`)
|
|
224
|
+
expect(playBarElement.exists()).toBe(true)
|
|
225
|
+
expect(wrapper.vm.id).toBe('plyr_vid1')
|
|
226
|
+
})
|
|
227
|
+
|
|
228
|
+
it('Renders a the correct player instance (id = plyr_<mediaId>) for audio', async () => {
|
|
229
|
+
const mediaType = 'audio'
|
|
230
|
+
wrapper = mount(AppCompPlayBarNext, {
|
|
231
|
+
props: {
|
|
232
|
+
mediaToPlay: defineMediaToPlay(mediaType)
|
|
233
|
+
}
|
|
234
|
+
})
|
|
235
|
+
|
|
236
|
+
await wrapper.vm.$nextTick()
|
|
237
|
+
//Should render a div with class pb-container div with class video and id if plyr_aud1
|
|
238
|
+
playBarContainer = wrapper.find(`.pb-container`)
|
|
239
|
+
const playBarElement = playBarContainer.find(`.${mediaType}`)
|
|
240
|
+
expect(playBarElement.exists()).toBe(true)
|
|
241
|
+
expect(wrapper.vm.id).toBe('plyr_aud1')
|
|
242
|
+
})
|
|
243
|
+
})
|
|
244
|
+
|
|
245
|
+
describe('It initializes correctly', async () => {
|
|
246
|
+
it('Calls updateCurrentMediaElements() on mount', () => {
|
|
247
|
+
wrapper = mount(AppCompPlayBarNext, {
|
|
248
|
+
props: {
|
|
249
|
+
mediaToPlay: defineMediaToPlay('audio')
|
|
250
|
+
}
|
|
251
|
+
})
|
|
252
|
+
|
|
253
|
+
expect(mockedStore.updateCurrentMediaElements).toHaveBeenCalledTimes(1)
|
|
254
|
+
expect(mockedStore.updateCurrentMediaElements).toHaveBeenCalledWith(
|
|
255
|
+
expect.objectContaining({ id: wrapper.vm.mediaToPlay.id })
|
|
256
|
+
)
|
|
257
|
+
})
|
|
258
|
+
|
|
259
|
+
it('Video loads transcript on mount (setTranscript -> axios.get) when mTranscript exists', async () => {
|
|
260
|
+
wrapper = mount(AppCompPlayBarNext, {
|
|
261
|
+
props: {
|
|
262
|
+
mediaToPlay: defineMediaToPlay('video')
|
|
263
|
+
}
|
|
264
|
+
})
|
|
265
|
+
await flush() // wait for all pending promises to resolve before making assertions
|
|
266
|
+
expect(axios.get).toHaveBeenCalledTimes(1)
|
|
267
|
+
// when mTranscript has no '/', component builds "./<file>"
|
|
268
|
+
// expect component to normalize the path
|
|
269
|
+
expect(axios.get).toHaveBeenCalledWith(
|
|
270
|
+
`./${wrapper.vm.mediaToPlay.mTranscript}`,
|
|
271
|
+
expect.any(Object)
|
|
272
|
+
)
|
|
273
|
+
await wrapper.vm.$nextTick()
|
|
274
|
+
expect(wrapper.vm.transcriptToShow).toContain('mock-transcript')
|
|
275
|
+
})
|
|
276
|
+
})
|
|
277
|
+
|
|
278
|
+
describe('Controls media events with user interaction on:', () => {
|
|
279
|
+
it('PLAY: (togglePlay), emits xAPI + GA play tracking ', async () => {
|
|
280
|
+
await flush()
|
|
281
|
+
const mediaType = 'video'
|
|
282
|
+
wrapper = await mount(AppCompPlayBarNext, {
|
|
283
|
+
props: {
|
|
284
|
+
mediaToPlay: defineMediaToPlay(mediaType)
|
|
285
|
+
},
|
|
286
|
+
global: { ...globalConfig }
|
|
287
|
+
})
|
|
288
|
+
await flush()
|
|
289
|
+
const mediaID = wrapper.vm.mediaToPlay.id
|
|
290
|
+
expect(wrapper.vm.mediaRawData.id).toBe(mediaID)
|
|
291
|
+
expect(wrapper.vm.mediaToPlay.mElement.paused).toBe(true)
|
|
292
|
+
|
|
293
|
+
//Trigger play event call togglePlay()
|
|
294
|
+
await wrapper.vm.togglePlay()
|
|
295
|
+
expect(wrapper.vm.mediaToPlay.mElement.play).toHaveBeenCalledTimes(1)
|
|
296
|
+
expect(wrapper.vm.mediaToPlay.mElement.paused).toBe(false)
|
|
297
|
+
await flush()
|
|
298
|
+
|
|
299
|
+
//Check that xAPI and GA events were sent
|
|
300
|
+
expect($bus.$emit).toHaveBeenCalledWith(
|
|
301
|
+
'send-xapi-statement',
|
|
302
|
+
expect.objectContaining({ verb: 'played' })
|
|
303
|
+
)
|
|
304
|
+
|
|
305
|
+
const url = mediaType == 'audio' ? '/audios/aud1.mp3' : '/videos/vid1.mp4'
|
|
306
|
+
|
|
307
|
+
expect($analytics.sendEvent).toHaveBeenCalledWith(
|
|
308
|
+
`fcad_${mediaType}_play`,
|
|
309
|
+
expect.objectContaining({ url })
|
|
310
|
+
)
|
|
311
|
+
})
|
|
312
|
+
|
|
313
|
+
it('STOP/END : mediaEnded(), set canReplay(), media-viewed event and emits xAPI + GA event ', async () => {
|
|
314
|
+
const mediaType = 'audio'
|
|
315
|
+
wrapper = await mount(AppCompPlayBarNext, {
|
|
316
|
+
props: {
|
|
317
|
+
mediaToPlay: defineMediaToPlay(mediaType)
|
|
318
|
+
},
|
|
319
|
+
global: { ...globalConfig }
|
|
320
|
+
})
|
|
321
|
+
await flush()
|
|
322
|
+
const mediaID = wrapper.vm.mediaToPlay.id
|
|
323
|
+
expect(wrapper.vm.mediaRawData.id).toBe(mediaID)
|
|
324
|
+
expect(wrapper.vm.mediaToPlay.mElement.paused).toBe(true)
|
|
325
|
+
|
|
326
|
+
//Trigger play event and ended event
|
|
327
|
+
await wrapper.vm.togglePlay()
|
|
328
|
+
expect(wrapper.vm.isPlaying).toBe(true)
|
|
329
|
+
wrapper.vm.mediaEnded()
|
|
330
|
+
await flush()
|
|
331
|
+
//Assert media ended events & states
|
|
332
|
+
expect(wrapper.vm.mediaToPlay.mElement.pause).toHaveBeenCalledTimes(1)
|
|
333
|
+
expect(wrapper.vm.isPlaying).toBe(false)
|
|
334
|
+
expect(wrapper.vm.canReplay).toBe(true)
|
|
335
|
+
//Assert media viewed recorded in store
|
|
336
|
+
expect($bus.$emit).toHaveBeenCalledWith('media-viewed', mediaID)
|
|
337
|
+
//Check that xAPI and GA events were sent
|
|
338
|
+
expect($bus.$emit).toHaveBeenCalledWith(
|
|
339
|
+
'send-xapi-statement',
|
|
340
|
+
expect.objectContaining({ verb: 'completed' })
|
|
341
|
+
)
|
|
342
|
+
const url = mediaType == 'audio' ? '/audios/aud1.mp3' : '/videos/vid1.mp4'
|
|
343
|
+
expect($analytics.sendEvent).toHaveBeenCalledWith(
|
|
344
|
+
`fcad_${mediaType}_viewed`,
|
|
345
|
+
expect.objectContaining({ url })
|
|
346
|
+
)
|
|
347
|
+
})
|
|
348
|
+
|
|
349
|
+
it('VOLUME/MUTE: updateVolumeLevel() persists to store, toggleMute updates store muted state', async () => {
|
|
350
|
+
const mediaType = 'audio'
|
|
351
|
+
wrapper = await mount(AppCompPlayBarNext, {
|
|
352
|
+
props: {
|
|
353
|
+
mediaToPlay: defineMediaToPlay(mediaType)
|
|
354
|
+
},
|
|
355
|
+
global: { ...globalConfig }
|
|
356
|
+
})
|
|
357
|
+
await flush()
|
|
358
|
+
|
|
359
|
+
// updateVolumeLevel is called on mount; ensure store called at least once
|
|
360
|
+
expect(mockedStore.setMediaPlaybarValues).toHaveBeenCalled()
|
|
361
|
+
//Trigger mute action by calling toggleMute()
|
|
362
|
+
wrapper.vm.toggleMute()
|
|
363
|
+
await flush()
|
|
364
|
+
|
|
365
|
+
expect(mockedStore.setMediaMuted).toHaveBeenCalled()
|
|
366
|
+
expect(typeof wrapper.vm.muted).toBe('boolean')
|
|
367
|
+
})
|
|
368
|
+
|
|
369
|
+
it('SHOW TRANSCRIPT- toggleViewTranscript(e) emits resize-video(sm) and opens sidebar', async () => {
|
|
370
|
+
const mediaType = 'video'
|
|
371
|
+
wrapper = await mount(AppCompPlayBarNext, {
|
|
372
|
+
props: {
|
|
373
|
+
mediaToPlay: defineMediaToPlay(mediaType)
|
|
374
|
+
},
|
|
375
|
+
global: { ...globalConfig }
|
|
376
|
+
})
|
|
377
|
+
await flush()
|
|
378
|
+
//Assert trancript initial state
|
|
379
|
+
expect(wrapper.vm.transcriptEnabled).toBe(false) //disabled
|
|
380
|
+
expect(wrapper.vm.transcriptToShow).toBeTruthy()
|
|
381
|
+
//Trigger view transcript
|
|
382
|
+
wrapper.vm.toggleViewTranscript(true) //open transcript
|
|
383
|
+
expect(wrapper.vm.transcriptEnabled).toBe(true) //transcript enabled
|
|
384
|
+
await flush()
|
|
385
|
+
//Assert emits event video-transcript-toggle with correct value
|
|
386
|
+
expect($bus.$emit).toHaveBeenCalledWith(
|
|
387
|
+
'video-transcript-toggle',
|
|
388
|
+
expect.objectContaining({ id: 'vid1' }),
|
|
389
|
+
true
|
|
390
|
+
)
|
|
391
|
+
//Assert emits resize-video(sm) and open-sidebar
|
|
392
|
+
const emits = wrapper.emitted('resize-video') || []
|
|
393
|
+
if (emits[0]) {
|
|
394
|
+
expect(emits[0]).toEqual(['sm'])
|
|
395
|
+
expect($bus.$emit).toHaveBeenCalledWith(
|
|
396
|
+
'open-sidebar',
|
|
397
|
+
expect.objectContaining({ ctx: 'ctxTranscript' })
|
|
398
|
+
)
|
|
399
|
+
}
|
|
400
|
+
})
|
|
401
|
+
|
|
402
|
+
it('HIDE TRANSCRIPT- toggleViewTranscript(e), closes sidebar and emits resize-video(lg)', async () => {
|
|
403
|
+
const mediaType = 'video'
|
|
404
|
+
wrapper = await mount(AppCompPlayBarNext, {
|
|
405
|
+
props: {
|
|
406
|
+
mediaToPlay: defineMediaToPlay(mediaType)
|
|
407
|
+
},
|
|
408
|
+
global: { ...globalConfig }
|
|
409
|
+
})
|
|
410
|
+
await flush()
|
|
411
|
+
//Assert transcript exists
|
|
412
|
+
expect(wrapper.vm.transcriptToShow).toBeTruthy()
|
|
413
|
+
wrapper.vm.toggleViewTranscript(true) // open
|
|
414
|
+
await flush()
|
|
415
|
+
wrapper.vm.toggleViewTranscript(true) // close
|
|
416
|
+
//Assert transcript disabled
|
|
417
|
+
expect(wrapper.vm.transcriptEnabled).toBe(false)
|
|
418
|
+
|
|
419
|
+
await flush()
|
|
420
|
+
//Assert emits resize-video(lg) and close-sidebar
|
|
421
|
+
const emits = wrapper.emitted('resize-video') || []
|
|
422
|
+
expect(emits[0]).toEqual(['sm'])
|
|
423
|
+
expect(emits[1]).toEqual(['lg'])
|
|
424
|
+
|
|
425
|
+
expect($bus.$emit).toHaveBeenCalledWith('close-sidebar', 'ctxTranscript')
|
|
426
|
+
})
|
|
427
|
+
})
|
|
428
|
+
})
|
package/vitest.setup.js
CHANGED
|
@@ -7,6 +7,7 @@ import * as components from 'vuetify/components'
|
|
|
7
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
|
+
import '@testing-library/jest-dom/vitest'
|
|
10
11
|
import bus from './src/plugins/bus'
|
|
11
12
|
import { createPinia, setActivePinia } from 'pinia'
|
|
12
13
|
|
|
@@ -86,11 +87,11 @@ config.global.mocks = {
|
|
|
86
87
|
id: 'P01'
|
|
87
88
|
}
|
|
88
89
|
},
|
|
89
|
-
$bus: {
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
},
|
|
90
|
+
// $bus: {
|
|
91
|
+
// $on: vi.fn(),
|
|
92
|
+
// $emit: vi.fn(),
|
|
93
|
+
// $off: vi.fn()
|
|
94
|
+
// },
|
|
94
95
|
$helper: {
|
|
95
96
|
formatTime: vi.fn((t) => `00:${t}`)
|
|
96
97
|
}
|
package/.vscode/extensions.json
DELETED
package/.vscode/settings.json
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"explorer.fileNesting.enabled": true,
|
|
3
|
-
"explorer.fileNesting.patterns": {
|
|
4
|
-
"tsconfig.json": "tsconfig.*.json, env.d.ts",
|
|
5
|
-
"vite.config.*": "jsconfig*, vitest.config.*, cypress.config.*, playwright.config.*",
|
|
6
|
-
"package.json": "package-lock.json, pnpm*, .yarnrc*, yarn*, .eslint*, eslint*, .oxlint*, oxlint*, .prettier*, prettier*, .editorconfig"
|
|
7
|
-
},
|
|
8
|
-
"editor.codeActionsOnSave": {
|
|
9
|
-
"source.fixAll": "explicit"
|
|
10
|
-
},
|
|
11
|
-
"editor.formatOnSave": true,
|
|
12
|
-
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
|
13
|
-
"[vue]": {
|
|
14
|
-
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
|
15
|
-
}
|
|
16
|
-
}
|