fcad-core-dragon 2.2.0-beta.1 → 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 +10 -0
- package/artifacts/playwright-report/index.html +1 -1
- package/package.json +2 -1
- package/src/components/AppCompBranchButtons.vue +6 -3
- package/src/components/AppCompMenuItem.vue +2 -2
- package/src/components/AppCompNoteCredit.vue +17 -19
- package/src/components/AppCompPlayBarNext.vue +11 -3
- package/src/components/AppCompQuizRecall.vue +7 -13
- package/src/main.js +3 -2
- package/src/module/stores/appStore.js +0 -7
- package/src/plugins/i18n.js +1 -2
- package/src/shared/validators.js +8 -21
- 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 +142 -36
- package/tests/unit/AppCompPlayBarNext.spec.js +428 -0
- 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 +35 -33
- package/.vscode/extensions.json +0 -8
- package/.vscode/settings.json +0 -16
- package/tests/unit/AppCompQuizNext.spec.js +0 -112
|
@@ -1,58 +1,164 @@
|
|
|
1
1
|
import { mount } from '@vue/test-utils'
|
|
2
|
-
import
|
|
3
|
-
import {
|
|
2
|
+
import { beforeEach, describe, expect, vi, test, it } from 'vitest'
|
|
3
|
+
import { createTestingPinia } from '@pinia/testing'
|
|
4
4
|
|
|
5
|
-
const
|
|
6
|
-
{
|
|
7
|
-
id: 'nt_1',
|
|
8
|
-
text: `note 1`
|
|
9
|
-
},
|
|
5
|
+
const dummyPropsNotes = [
|
|
10
6
|
{
|
|
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
|
+
|
|
24
|
+
//Mock store appStore
|
|
25
|
+
vi.mock('@/module/stores/appStore', () => ({
|
|
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: [] }
|
|
33
|
+
})
|
|
34
|
+
}))
|
|
35
|
+
|
|
36
|
+
// Import of components must happen after mocks to prevent side effects
|
|
37
|
+
import AppCompNoteCredit from '../../src/components/AppCompNoteCredit.vue'
|
|
38
|
+
|
|
16
39
|
describe('AppCompNoteCredit', () => {
|
|
17
40
|
let wrapper = null
|
|
18
41
|
|
|
42
|
+
//Mounting componant
|
|
19
43
|
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
44
|
wrapper = mount(AppCompNoteCredit, {
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
'app-base-button': true,
|
|
40
|
-
'app-base-error-display': true
|
|
41
|
-
}
|
|
42
|
-
}
|
|
45
|
+
plugins: [createTestingPinia({ createSpy: vi.fn })],
|
|
46
|
+
global
|
|
43
47
|
})
|
|
44
48
|
})
|
|
45
49
|
|
|
46
|
-
test('It renders
|
|
50
|
+
test('It renders componant with notes and credits', async () => {
|
|
47
51
|
const wrapper = mount(AppCompNoteCredit)
|
|
48
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 () => {
|
|
73
|
+
const wrapper = mount(AppCompNoteCredit)
|
|
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)
|
|
49
83
|
expect(wrapper.exists()).toBe(true)
|
|
50
84
|
|
|
51
|
-
|
|
52
|
-
|
|
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)
|
|
53
92
|
})
|
|
54
93
|
|
|
55
|
-
test('It renders
|
|
56
|
-
|
|
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)
|
|
57
163
|
})
|
|
58
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
|
+
})
|
|
@@ -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
|
})
|