fcad-core-dragon 2.1.2 → 2.2.0-beta.1

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/package.json CHANGED
@@ -1,33 +1,12 @@
1
1
  {
2
- "name": "fcad-core-dragon",
3
- "version": "2.1.2",
4
- "private": false,
5
- "type": "module",
6
- "main": "./src/main.js",
7
- "scripts": {
8
- "build": "vite build",
9
- "dev": "vite",
10
- "docs:build": "vitepress build documentation",
11
- "docs:dev": "vitepress dev documentation",
12
- "docs:preview": "vitepress preview documentation",
13
- "lintfix": "eslint --fix src",
14
- "lintreport": "eslint ./src",
15
- "preview": "vite preview",
16
- "reset": "rm -rf ./node_modules package-lock.json .cache dist && npm i && npm run watch",
17
- "test:unit": "vitest",
18
- "vue": "vue",
19
- "watch": "nodemon -e js,vue,html,json -x yalc publish --push",
20
- "format": "prettier --write src/",
21
- "test-ct": "playwright test -c playwright-ct.config.js"
22
- },
23
- "config": {
24
- "projname": ""
25
- },
26
2
  "browserslist": [
27
3
  "> 1%",
28
4
  "last 2 versions",
29
5
  "not dead"
30
6
  ],
7
+ "config": {
8
+ "projname": ""
9
+ },
31
10
  "dependencies": {
32
11
  "axios": "^1.6.8",
33
12
  "gsap": "^3.12.5",
@@ -44,23 +23,47 @@
44
23
  "@playwright/test": "^1.56.1",
45
24
  "@types/node": "^24.10.0",
46
25
  "@vitejs/plugin-vue": "^6.0.1",
26
+ "@vitest/coverage-v8": "^4.0.15",
47
27
  "@vue/eslint-config-prettier": "^10.2.0",
48
28
  "@vue/test-utils": "^2.4.6",
49
29
  "eslint": "^9.38.0",
50
30
  "eslint-plugin-cypress": "^5.1.0",
51
31
  "eslint-plugin-vue": "~10.3.0",
52
32
  "globals": "^16.4.0",
33
+ "happy-dom": "^20.0.10",
53
34
  "jsdom": "^25.0.1",
54
35
  "nodemon": "^3.1.0",
55
36
  "prettier": "^3.6.2",
56
37
  "sass-embedded": "^1.91.0",
57
38
  "vitepress": "^1.6.3",
58
- "vitest": "^3.2.4",
39
+ "vitest": "^4.0.15",
59
40
  "vue-i18n": "^11.1.12",
60
41
  "vue-router": "^4.4.5",
61
42
  "vuetify": "^3.10.10"
62
43
  },
63
44
  "engines": {
64
45
  "node": ">0.11.9"
65
- }
46
+ },
47
+ "main": "./src/main.js",
48
+ "name": "fcad-core-dragon",
49
+ "private": false,
50
+ "scripts": {
51
+ "build": "vite build",
52
+ "dev": "vite",
53
+ "docs:build": "vitepress build documentation",
54
+ "docs:dev": "vitepress dev documentation",
55
+ "docs:preview": "vitepress preview documentation",
56
+ "format": "prettier --write src/",
57
+ "lintfix": "eslint --fix src",
58
+ "lintreport": "eslint ./src",
59
+ "preview": "vite preview",
60
+ "reset": "rm -rf ./node_modules package-lock.json .cache dist && npm i && npm run watch",
61
+ "test-ct": "playwright test -c playwright-ct.config.js",
62
+ "test:unit": "vitest",
63
+ "test:unit:coverage": "vitest --coverage",
64
+ "vue": "vue",
65
+ "watch": "nodemon -e js,vue,html,json -x yalc publish --push"
66
+ },
67
+ "type": "module",
68
+ "version": "2.2.0-beta.1"
66
69
  }
@@ -11,7 +11,6 @@
11
11
  <div class="ctn">
12
12
  Activity info: Route : {{ $route.path }}
13
13
  <br />
14
-
15
14
  Activity id : {{ getDebugModeInfo.id }}
16
15
  <br />
17
16
  Nombre de page : {{ getDebugModeInfo.size }}
@@ -96,6 +95,7 @@ export default {
96
95
  }
97
96
  },
98
97
  setup(props) {
98
+ //console.log('dans setup')
99
99
  const store = useAppStore()
100
100
  const { activityRef, id: pageID, type: pageType } = props.pageData
101
101
  const { t } = useI18n()
@@ -167,35 +167,29 @@ export default {
167
167
  getModuleInfo() {
168
168
  return this.store.getModuleInfo
169
169
  },
170
-
171
170
  getAllActivities() {
172
171
  return this.store.getAllActivities()
173
172
  },
174
173
  getConnectionInfo() {
175
174
  return this.store.getConnectionInfo
176
175
  },
177
-
178
176
  getAnchorsForActivity() {
179
177
  return this.store.getAnchorsForActivity()
180
178
  },
181
179
  getBifChoice() {
182
180
  return this.store.getBifChoice
183
181
  },
184
-
185
182
  isBranchingPage() {
186
183
  return this.$route.meta.type === 'branching' && this.type !== 'pg_branch'
187
184
  },
188
-
189
185
  //================================================
190
186
  settingsOptions() {
191
187
  return this.settingsOptionsELPlus
192
188
  },
193
-
194
189
  settingsSelected() {
195
190
  const setting = this.getApplicationSettings
196
191
  return setting
197
192
  },
198
-
199
193
  errorPage() {
200
194
  let err = false
201
195
  if (import.meta.env.DEV) {
@@ -380,7 +374,8 @@ export default {
380
374
  /**
381
375
  * Observe changes in the number of poperties to dispatch updates in the userdata in the Store
382
376
  */
383
-
377
+ // console.log('ici')
378
+ //console.log(this.userInteraction)
384
379
  if (newValue && Object.entries(this.userInteraction).length) {
385
380
  await this.store.updateUserMetaData({
386
381
  activityRef: this.pageData.activityRef,
@@ -107,11 +107,11 @@ export default {
107
107
  ...mapState(useAppStore, ['getCurrentBrowser', 'getCurrentPage']),
108
108
  $audElement() {
109
109
  if (!this.isSet) return null
110
+ const { id, mTranscript } = this.audData
110
111
  return {
111
- id: this.id,
112
- mTranscript: null,
112
+ id,
113
+ mTranscript,
113
114
  mType: 'audio',
114
- mSubtitles: null,
115
115
  mElement: this.$refs['m-audio']
116
116
  }
117
117
  },
@@ -168,7 +168,8 @@ export default {
168
168
  )
169
169
  const { errorList, errorInConsole } = errors
170
170
 
171
- if (errorList.length || errorInConsole.length) return errors
171
+ if (!errorList || !errorInConsole) return null
172
+ return errors
172
173
  }
173
174
 
174
175
  return errors
@@ -29,6 +29,7 @@
29
29
  :items="getItemOptions(index)"
30
30
  :open-text="`${$t('message.dropdown_list')}_${singleDropdown.ennonce}`"
31
31
  :aria-describedby="`${id}_${singleDropdown.id}-msg-erreur`"
32
+ menu-icon="mdi-chevron-down"
32
33
  @update:model-value="onSelectUpdate($event, index)"
33
34
  ></v-select>
34
35
 
@@ -169,11 +170,10 @@ export default {
169
170
  null,
170
171
  `liste déroulante #${i + 1}`
171
172
  )
172
-
173
- if (e.errorInConsole.length && e.errorList.length) {
174
- errors.errorInConsole.push(e.errorInConsole[0])
175
- errors.errorList.push(e.errorList[0])
176
- }
173
+ if (!e || !e.errorList || !e.errorInConsole) return null
174
+ const { errorList, errorInConsole } = e
175
+ if (errorInConsole.length) errors.errorInConsole.push(errorInConsole[0])
176
+ if (errorList.length) errors.errorList.push(errorList[0])
177
177
  }
178
178
 
179
179
  return errors
@@ -197,7 +197,8 @@ export default {
197
197
  )
198
198
  const { errorList, errorInConsole } = errors
199
199
 
200
- if (errorList.length || errorInConsole.length) return errors
200
+ if (!errorList || !errorInConsole) return null
201
+ return errors
201
202
  }
202
203
 
203
204
  return errors
@@ -99,9 +99,7 @@ export default {
99
99
  methods: {
100
100
  validateInputData() {
101
101
  let errors = null //array for errors dectected
102
-
103
102
  if (this.inputData != undefined) return errors
104
-
105
103
  return errors
106
104
  },
107
105
  /**
@@ -129,10 +129,10 @@ export default {
129
129
  `texte tableau #${i + 1}`
130
130
  )
131
131
 
132
- if (e.errorInConsole.length && e.errorList.length) {
133
- errors.errorInConsole.push(e.errorInConsole[0])
134
- errors.errorList.push(e.errorList[0])
135
- }
132
+ if (!e || !e.errorList || !e.errorInConsole) return null
133
+ const { errorList, errorInConsole } = e
134
+ if (errorInConsole.length) errors.errorInConsole.push(errorInConsole[0])
135
+ if (errorList.length) errors.errorList.push(errorList[0])
136
136
  }
137
137
 
138
138
  return errors
@@ -34,6 +34,7 @@
34
34
  :items="getItemOptions(textInput.index)"
35
35
  :aria-describedby="`${id}_${textInput.id}-msg-erreur`"
36
36
  :aria-labelledby="`${id}_${textInput.id}-label`"
37
+ menu-icon="mdi-chevron-down"
37
38
  @update:model-value="onSelectUpdate($event, textInput.index)"
38
39
  />
39
40
  <span
@@ -173,7 +174,7 @@ export default {
173
174
  let splittedText = str.split(regex)
174
175
  let listInput = []
175
176
 
176
- for (let i = 0; i < splittedText.length - 1; i++) {
177
+ for (let i = 0; i < splittedText.length; i++) {
177
178
  listInput.push({
178
179
  id: `text-${i}`,
179
180
  type: 'text',
@@ -183,9 +184,10 @@ export default {
183
184
  if (i < splittedText.length - 1)
184
185
  listInput.push({ id: `select-${i}`, type: 'select', index: i })
185
186
  }
187
+
186
188
  let lI = listInput.findLast((element) => element.type == 'select')
187
- this.inputCount = lI.index + 1
188
189
 
190
+ this.inputCount = lI.index + 1
189
191
  this.inputElements = listInput
190
192
  },
191
193
  /**
@@ -299,14 +301,11 @@ fieldset {
299
301
  div.texteatrou {
300
302
  display: inline !important;
301
303
 
302
-
303
304
  .cnt-input {
304
305
  display: inline;
305
306
  .v-input {
306
307
  display: inline-block !important;
307
308
 
308
-
309
-
310
309
  :deep(.v-input__control),
311
310
  :deep(.v-field) {
312
311
  width: 240px !important;
@@ -339,5 +338,4 @@ div.texteatrou {
339
338
  }
340
339
  }
341
340
  }
342
-
343
341
  </style>
@@ -132,29 +132,6 @@ export default {
132
132
  mounted() {},
133
133
 
134
134
  methods: {
135
- /**
136
- * @description validate the raw data received by the component to render is view
137
- * @returns {Object} errors - errorList: to display in view and errorConsole, to be displayed in console
138
- */
139
- validateInputData() {
140
- let errors = null //array for errors dectected
141
- let stringType = ['id', 'value']
142
-
143
- if (!this.inputData.length) return errors
144
- for (let i = 0; i < this.inputData.length; i++) {
145
- errors = validateObjType(
146
- this.inputData[i],
147
- { stringType },
148
- null,
149
- `choix_reponse #${i + 1}`
150
- )
151
- const { errorList, errorInConsole } = errors
152
-
153
- if (errorList.length || errorInConsole.length) return errors
154
- }
155
-
156
- return errors
157
- },
158
135
  /**
159
136
  * @description create the object to genate the text and inputs
160
137
  * @param {String} str the text with holes to fill
@@ -165,7 +142,7 @@ export default {
165
142
  let splittedText = str.split(regex)
166
143
  let listInput = []
167
144
 
168
- for (let i = 0; i < splittedText.length - 1; i++) {
145
+ for (let i = 0; i < splittedText.length; i++) {
169
146
  listInput.push({
170
147
  id: `text-${i}`,
171
148
  type: 'text',
@@ -1628,7 +1628,7 @@ export default {
1628
1628
  */
1629
1629
  toggleFullScreen() {
1630
1630
  const fullscreenElement = this.mediaContainer
1631
- console.log(fullscreenElement)
1631
+ //console.log(fullscreenElement)
1632
1632
 
1633
1633
  if (document.fullscreenElement) {
1634
1634
  // exitFullscreen is only available on the Document object.
@@ -1795,12 +1795,10 @@ export default {
1795
1795
  </script>
1796
1796
  <style lang="scss" scoped>
1797
1797
  .pb-container.video {
1798
- position: absolute;
1799
1798
  height: 100%;
1800
1799
  width: 100%;
1801
1800
  justify-content: center;
1802
1801
  display: flex;
1803
- margin-bottom: 78px;
1804
1802
  }
1805
1803
 
1806
1804
  .playback-button {
@@ -1808,7 +1806,7 @@ export default {
1808
1806
  bottom: 0;
1809
1807
  left: 0;
1810
1808
  width: 100%;
1811
- height: 94%;
1809
+ height: 100%;
1812
1810
  display: flex;
1813
1811
  flex-flow: column wrap;
1814
1812
  justify-content: center;
@@ -1837,8 +1835,6 @@ export default {
1837
1835
  width: 100%;
1838
1836
  display: flex;
1839
1837
  flex-direction: column;
1840
- position: absolute;
1841
- bottom: -78px;
1842
1838
  opacity: 0;
1843
1839
  z-index: 2;
1844
1840
 
@@ -2260,8 +2256,10 @@ export default {
2260
2256
 
2261
2257
  .FS {
2262
2258
  &:fullscreen {
2263
- .pb-wrapper {
2264
- bottom: -45px;
2259
+ .pb-container {
2260
+ position: absolute;
2261
+ bottom: 0;
2262
+ height: 78px;
2265
2263
  }
2266
2264
  }
2267
2265
  }
@@ -103,19 +103,26 @@ export default {
103
103
  //Watch for user interaction change to get quizRecall answer
104
104
  getUserInteraction: {
105
105
  async handler(newValue) {
106
+ //console.log('???')
106
107
  if (!this.getUserInteraction) return
107
-
108
+ //console.log('--------------------')
108
109
  const { activityId, pageId } = this.quizRecallData
109
110
  const interaction = this.getPageInteraction(
110
111
  activityId,
111
112
  pageId
112
113
  ).userInteraction
113
114
 
114
- if (!interaction) return
115
+ //console.log(interaction)
116
+ if (!interaction) return //{
117
+ // this.quizRecall.done == false
118
+ // }
119
+
115
120
  //Get quizRecall answer
116
121
  if (this.quizRecallData && this.quizData) {
122
+ //console.log('ici????')
117
123
  await this.getQuizRecallAnswer(interaction)
118
124
  } else {
125
+ //console.log('laaaaaa ???? ')
119
126
  this.quizRecall.done == false
120
127
  }
121
128
  },
@@ -216,6 +223,7 @@ export default {
216
223
  if (hypertext_done) {
217
224
  this.quizRecall.hypertext_done = hypertext_done
218
225
  }
226
+
219
227
  if (hypertext_undone) {
220
228
  this.quizRecall.hypertext_undone = hypertext_undone
221
229
  }
@@ -231,6 +239,7 @@ export default {
231
239
  }
232
240
  }
233
241
  //Get the quiz answers from userData
242
+
234
243
  const answers = userData.quizAnswers || {}
235
244
  let quizAnswer = answers[quizId] || null
236
245
 
@@ -301,6 +310,7 @@ export default {
301
310
  )
302
311
  this.hasError.push(msgErr)
303
312
  }
313
+
304
314
  //Validate if all required properties are present in quizRecallData
305
315
  if (missingRequired.length > 0) {
306
316
  let msgErr = `QuizRecallData missing required ${missingRequired} property`
@@ -282,6 +282,7 @@ export default {
282
282
  resizeVideo(size, container) {
283
283
  let defaultSize = 100
284
284
  if (size == 'sm') defaultSize = 68
285
+
285
286
  //const videoElement = document.querySelector('.__media-container')
286
287
  const videoElement = this.$vidElement.mMediaContainer
287
288
  setTimeout(() => {
@@ -304,7 +305,7 @@ $widthVideo: 100%;
304
305
 
305
306
  .app-video-player {
306
307
  display: flex;
307
- flex-direction: row;
308
+ flex-direction: column;
308
309
  flex-wrap: wrap;
309
310
  position: relative;
310
311
  align-items: center;
package/src/main.js CHANGED
@@ -28,7 +28,7 @@ import GsapPlugin from './plugins/gsap'
28
28
  import eventBus from './plugins/bus'
29
29
  import helper from './plugins/helper'
30
30
  import analytics from './plugins/analytics'
31
- import mergeLocales from './plugins/i18n'
31
+ import initLocalisation from './plugins/i18n'
32
32
  import { scormPlugin } from './plugins/scorm'
33
33
  import { xapiPlugin } from './plugins/xapi'
34
34
  import { $idb } from './plugins/idb'
@@ -41,19 +41,33 @@ import VueSafeTeleport from 'vue-safe-teleport'
41
41
  import { FocusTrap } from 'focus-trap-vue'
42
42
  import nvdaFix from './directives/nvdaFix.js'
43
43
  import pckg from '../package.json'
44
+ import fcadRouter from './router/index.js'
45
+ import '@mdi/font/css/materialdesignicons.css'
46
+ import 'vuetify/styles'
47
+ import { createVuetify } from 'vuetify'
48
+ import { fr, en } from 'vuetify/locale'
49
+
44
50
  const pinia = createPinia()
45
51
 
46
52
  export default {
47
53
  install(app, options) {
54
+ app.use(fcadRouter)
48
55
  app.use(pinia)
49
56
  app.use(scormPlugin)
50
57
  app.use(GsapPlugin)
51
58
  app.use(xapiPlugin)
52
59
  app.use(VueSafeTeleport)
53
60
  app.component('FocusTrap', FocusTrap)
54
-
55
61
  app.use(eventBus)
56
62
  app.use($idb)
63
+ const vuetify = createVuetify({
64
+ locale: {
65
+ locale: 'fr',
66
+ fallback: 'en',
67
+ messages: { fr, en }
68
+ }
69
+ })
70
+ app.use(vuetify)
57
71
 
58
72
  app.component('AppBase', AppBase)
59
73
  app.component('AppBaseButton', AppBaseButton)
@@ -332,7 +346,6 @@ export default {
332
346
 
333
347
  let server = remote ? specification : 'local'
334
348
  //falback to idb when scorm and origin is localhost
335
- //falback to idb when scorm and origin is localhost
336
349
  if (
337
350
  (specification == 'scorm' && window.location.hostname == 'localhost') ||
338
351
  !window.location.hostname.includes('cegepadistance.ca')
@@ -477,8 +490,8 @@ export default {
477
490
  appStore.applicationSettings = settingsOptions
478
491
  //=================================END SETTING PREFERENCES ====================================//
479
492
 
480
- //mergeLocales
481
- mergeLocales(options.i18n.global)
493
+ const i18n = initLocalisation()
494
+ app.use(i18n)
482
495
  app.use(helper)
483
496
  app.use(analytics)
484
497
 
@@ -1,10 +1,13 @@
1
1
  import _ from 'lodash'
2
+ import { createI18n } from 'vue-i18n'
2
3
  /**
3
4
  * Merge locales message in project.
4
5
  * @summary To merge the locales dictionnary of this library with the locales that user will create
5
- * @param {Object} i18n - vue-i18n internalization object.
6
+ * @param {Object} templateMessages - messages (JSON) received from the template
6
7
  */
7
- export default function mergeLocales(i18n) {
8
+ export default function initLocalisation(templateMessages) {
9
+ const i18n = createI18n({ legacy: false })
10
+
8
11
  const thisLocales = import.meta.glob('../$locales/*.json', {
9
12
  import: 'default',
10
13
  eager: true
@@ -15,13 +18,12 @@ export default function mergeLocales(i18n) {
15
18
  const locale = matched[1]
16
19
  // Using lodash to deeply merge objects
17
20
  const coreMessages = JSON.parse(JSON.stringify(thisLocales[path]))
18
- const templateMessages = JSON.parse(
19
- JSON.stringify(i18n.messages[locale] || i18n.messages.value[locale]) //cover both legacy mode and non legacy mode of vue-i18n
20
- )
21
21
  const mergedMessages = _.merge(coreMessages, templateMessages)
22
22
 
23
23
  // Since merLocalMessage will do a shallow merge of the object. We will use setLocalMessage to replace the message with new message object
24
- i18n.setLocaleMessage(locale, mergedMessages)
24
+
25
+ i18n.global.setLocaleMessage(locale, mergedMessages)
25
26
  }
26
27
  }
28
+ return i18n
27
29
  }
@@ -0,0 +1,134 @@
1
+ import { mount } from '@vue/test-utils'
2
+ import { describe, it, test, expect } from 'vitest'
3
+
4
+ let dummyProps = {
5
+ id: 'P01',
6
+ activityRef: 'A03',
7
+ title: 'Lecteurs médias',
8
+ type: 'pg_normal',
9
+ audiosData: [
10
+ {
11
+ id: 'aud1',
12
+ mTitle: 'Et si... Annie Ernaux nous parlait',
13
+ mSources: [
14
+ {
15
+ type: 'mp3',
16
+ src: ' exemple_audio.mp3'
17
+ }
18
+ ],
19
+ mPoster: 'audio_poster.png',
20
+ mAlt: "Portrait de l'autrice Annie Ernaux",
21
+ mTranscript:
22
+ '<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>'
23
+ }
24
+ ]
25
+ }
26
+
27
+ import AppCompAudioPlayer from '@/components/AppCompAudio.vue'
28
+ import { validateAudioData } from '@/shared/validators.js'
29
+
30
+ describe('AppCompAudioPlayer', () => {
31
+ test('Validate received props', () => {
32
+ validateAudioData.mockReturnValue([])
33
+ const { audiosData } = dummyProps
34
+ mount(AppCompAudioPlayer, {
35
+ props: { audData: audiosData[0] }
36
+ })
37
+
38
+ expect(validateAudioData).toHaveBeenCalledWith({
39
+ id: 'aud1',
40
+ mTitle: 'Et si... Annie Ernaux nous parlait',
41
+ mSources: [
42
+ {
43
+ type: 'mp3',
44
+ src: ' exemple_audio.mp3'
45
+ }
46
+ ],
47
+ mPoster: 'audio_poster.png',
48
+ mAlt: "Portrait de l'autrice Annie Ernaux",
49
+ mTranscript:
50
+ '<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>'
51
+ })
52
+ })
53
+
54
+ it('renders ErrorDisplay When validator returns errors', () => {
55
+ // simulate invalid props
56
+ validateAudioData.mockReturnValue([
57
+ 'WARNING!>>> audio: 💥 Invalid declaration for audio element'
58
+ ])
59
+
60
+ const wrapper = mount(AppCompAudioPlayer, {
61
+ props: { audData: { id: null } }
62
+ })
63
+
64
+ expect(
65
+ wrapper.findComponent({ name: 'AppBaseErrorDisplay' }).exists()
66
+ ).toBe(true)
67
+ expect(wrapper.find('audio').exists()).toBe(false)
68
+ })
69
+
70
+ it('It renders HTML audio Element', () => {
71
+ validateAudioData.mockReturnValue([]) // no Error
72
+ const wrapper = mount(AppCompAudioPlayer, {
73
+ props: { audData: { id: null } }
74
+ })
75
+ expect(
76
+ wrapper.findComponent({ name: 'AppBaseErrorDisplay' }).exists()
77
+ ).toBe(false)
78
+ expect(wrapper.find('audio').exists()).toBe(true)
79
+ })
80
+
81
+ it('audio Element has correct sources', async () => {
82
+ const { audiosData } = dummyProps
83
+
84
+ validateAudioData.mockReturnValue([]) // no Error
85
+ const wrapper = mount(AppCompAudioPlayer, {
86
+ props: { audData: audiosData[0] }
87
+ })
88
+
89
+ expect(wrapper.vm.mSources).toEqual(audiosData[0].mSources)
90
+
91
+ //sources are rendered in the DOM
92
+ await wrapper.vm.$nextTick()
93
+ const sources = wrapper.findAll('source')
94
+ expect(sources.length).toBe(1)
95
+
96
+ // --- 3) valider les valeurs réelles rendues ---
97
+ expect(sources[0].attributes().src).toBe(audiosData[0].mSources[0].src)
98
+ expect(sources[0].attributes().type).toBe('audio/mp3')
99
+ })
100
+
101
+ it('computed $auElement returns correct data structure', async () => {
102
+ validateAudioData.mockReturnValue([])
103
+ const { audiosData } = dummyProps
104
+ const wrapper = mount(AppCompAudioPlayer, {
105
+ props: { audData: audiosData[0] }
106
+ })
107
+ // Simulate refs
108
+ const mockaudioRef = { tagName: 'audio' }
109
+ const mockContainerRef = { className: '__media-container' }
110
+ wrapper.vm.$refs['m-audio'] = mockaudioRef
111
+ wrapper.vm.$refs['$media-container'] = mockContainerRef
112
+ // Simulate refs isSet (audio ready) to true
113
+ wrapper.vm.isSet = true
114
+ await wrapper.vm.$nextTick()
115
+ // get audElement object
116
+ const obj = wrapper.vm.$audElement
117
+ const id = wrapper.vm.$props.audData.id
118
+ // expected data structure to be returned
119
+ const audElement = {
120
+ id,
121
+ mType: 'audio',
122
+ mElement: { localName: 'audio' },
123
+ mTranscript:
124
+ '<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>'
125
+ }
126
+
127
+ expect(obj.id).toEqual(audElement.id)
128
+ expect(obj.mType).toBe(audElement.mType)
129
+ expect(obj.mElement.tagName.toLowerCase()).toBe(
130
+ audElement.mElement.localName
131
+ )
132
+ expect(obj.mTranscript).toBe(audElement.mTranscript)
133
+ })
134
+ })