fcad-core-dragon 2.1.1 → 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/.editorconfig +7 -7
- package/.gitlab-ci.yml +106 -0
- package/.prettierrc +11 -11
- package/.vscode/extensions.json +8 -8
- package/.vscode/settings.json +16 -16
- package/CHANGELOG +529 -520
- package/README.md +57 -57
- package/artifacts/playwright-report/index.html +85 -0
- package/documentation/.vitepress/config.js +114 -114
- package/documentation/api-examples.md +49 -49
- package/documentation/composants/app-base-button.md +58 -58
- package/documentation/composants/app-base-error-display.md +59 -59
- package/documentation/composants/app-base-popover.md +68 -68
- package/documentation/composants/app-comp-audio.md +75 -75
- package/documentation/composants/app-comp-branch-buttons.md +111 -111
- package/documentation/composants/app-comp-button-progress.md +53 -53
- package/documentation/composants/app-comp-carousel.md +53 -53
- package/documentation/composants/app-comp-container.md +53 -53
- package/documentation/composants/app-comp-input-checkbox-next.md +42 -42
- package/documentation/composants/app-comp-input-dropdown-next.md +34 -34
- package/documentation/composants/app-comp-input-radio-next.md +39 -39
- package/documentation/composants/app-comp-input-text-next.md +35 -35
- package/documentation/composants/app-comp-input-text-table-next.md +34 -34
- package/documentation/composants/app-comp-input-text-to-fill-dropdown-next.md +53 -53
- package/documentation/composants/app-comp-input-text-to-fill-next.md +31 -31
- package/documentation/composants/app-comp-jauge.md +31 -31
- package/documentation/composants/app-comp-menu-item.md +55 -55
- package/documentation/composants/app-comp-menu.md +29 -29
- package/documentation/composants/app-comp-navigation.md +41 -41
- package/documentation/composants/app-comp-note-call.md +53 -53
- package/documentation/composants/app-comp-note-credit.md +53 -53
- package/documentation/composants/app-comp-play-bar-next.md +53 -53
- package/documentation/composants/app-comp-pop-up-next.md +93 -93
- package/documentation/composants/app-comp-quiz-next.md +235 -235
- package/documentation/composants/app-comp-quiz-recall.md +53 -53
- package/documentation/composants/app-comp-svg-next.md +53 -53
- package/documentation/composants/app-comp-table-of-content.md +50 -50
- package/documentation/composants/app-comp-video-player.md +82 -82
- package/documentation/composants.md +46 -46
- package/documentation/composants_critiques/ModelPageComposant.md +53 -53
- package/documentation/composants_critiques/app-base-module.md +43 -43
- package/documentation/composants_critiques/app-base-page.md +48 -48
- package/documentation/composants_critiques/app-base.md +311 -311
- package/documentation/composants_critiques/main.md +15 -15
- package/documentation/demarrage.md +50 -50
- package/documentation/deploiement.md +57 -57
- package/documentation/index.md +33 -33
- package/documentation/markdown-examples.md +85 -85
- package/documentation/public/vite.svg +14 -14
- package/documentation/public/vuejs.svg +1 -1
- package/documentation/public/vuetify.svg +5 -5
- package/eslint.config.js +60 -60
- package/package.json +69 -59
- package/playwright/index.html +12 -0
- package/playwright/index.js +21 -0
- package/playwright-ct.config.js +95 -0
- package/src/$locales/en.json +157 -157
- package/src/$locales/fr.json +120 -120
- package/src/assets/data/onboardingMessages.json +47 -47
- package/src/components/AppBase.vue +1171 -1169
- package/src/components/AppBaseButton.vue +90 -95
- package/src/components/AppBaseErrorDisplay.vue +438 -438
- package/src/components/AppBaseFlipCard.vue +84 -84
- package/src/components/AppBaseModule.vue +1639 -1634
- package/src/components/AppBasePage.vue +862 -866
- package/src/components/AppBasePopover.vue +41 -41
- package/src/components/AppBaseSkeleton.vue +66 -66
- package/src/components/AppCompAudio.vue +261 -256
- package/src/components/AppCompBranchButtons.vue +508 -508
- package/src/components/AppCompButtonProgress.vue +137 -132
- package/src/components/AppCompCarousel.vue +342 -336
- package/src/components/AppCompContainer.vue +29 -29
- package/src/components/AppCompInputCheckBoxNx.vue +326 -323
- package/src/components/AppCompInputDropdownNx.vue +302 -299
- package/src/components/AppCompInputRadioNx.vue +288 -284
- package/src/components/AppCompInputTextNx.vue +154 -153
- package/src/components/AppCompInputTextTableNx.vue +205 -202
- package/src/components/AppCompInputTextToFillDropdownNx.vue +341 -340
- package/src/components/AppCompInputTextToFillNx.vue +293 -313
- package/src/components/AppCompJauge.vue +81 -81
- package/src/components/AppCompMenu.vue +6 -1
- package/src/components/AppCompMenuItem.vue +246 -240
- package/src/components/AppCompNavigation.vue +977 -972
- package/src/components/AppCompNoteCall.vue +167 -161
- package/src/components/AppCompNoteCredit.vue +496 -491
- package/src/components/AppCompPlayBarNext.vue +2288 -2288
- package/src/components/AppCompPopUpNext.vue +508 -504
- package/src/components/AppCompQuizNext.vue +515 -510
- package/src/components/AppCompQuizRecall.vue +365 -350
- package/src/components/AppCompSVGNext.vue +346 -346
- package/src/components/AppCompSettingsMenu.vue +177 -172
- package/src/components/AppCompTableOfContent.vue +433 -427
- package/src/components/AppCompVideoPlayer.vue +378 -377
- package/src/components/AppCompViewDisplay.vue +6 -6
- package/src/components/BaseModule.vue +55 -55
- package/src/composables/useIdleDetector.js +56 -56
- package/src/composables/useQuiz.js +89 -89
- package/src/composables/useTimer.js +172 -172
- package/src/directives/nvdaFix.js +53 -53
- package/src/externalComps/ModuleView.vue +22 -22
- package/src/externalComps/SummaryView.vue +91 -91
- package/src/main.js +506 -476
- package/src/module/stores/appStore.js +960 -947
- package/src/module/xapi/ADL.js +520 -520
- package/src/module/xapi/Crypto/Hasher.js +241 -241
- package/src/module/xapi/Crypto/WordArray.js +278 -278
- package/src/module/xapi/Crypto/algorithms/BufferedBlockAlgorithm.js +103 -103
- package/src/module/xapi/Crypto/algorithms/C_algo.js +315 -315
- package/src/module/xapi/Crypto/algorithms/HMAC.js +9 -9
- package/src/module/xapi/Crypto/algorithms/SHA1.js +9 -9
- package/src/module/xapi/Crypto/encoders/Base.js +105 -105
- package/src/module/xapi/Crypto/encoders/Base64.js +99 -99
- package/src/module/xapi/Crypto/encoders/Hex.js +61 -61
- package/src/module/xapi/Crypto/encoders/Latin1.js +61 -61
- package/src/module/xapi/Crypto/encoders/Utf8.js +45 -45
- package/src/module/xapi/Crypto/index.js +53 -53
- package/src/module/xapi/Statement/activity.js +47 -47
- package/src/module/xapi/Statement/agent.js +55 -55
- package/src/module/xapi/Statement/group.js +26 -26
- package/src/module/xapi/Statement/index.js +259 -259
- package/src/module/xapi/Statement/statement.js +253 -253
- package/src/module/xapi/Statement/statementRef.js +23 -23
- package/src/module/xapi/Statement/substatement.js +22 -22
- package/src/module/xapi/Statement/verb.js +36 -36
- package/src/module/xapi/activitytypes.js +17 -17
- package/src/module/xapi/launch.js +157 -157
- package/src/module/xapi/utils.js +167 -167
- package/src/module/xapi/verbs.js +294 -294
- package/src/module/xapi/wrapper.js +1895 -1895
- package/src/module/xapi/xapiStatement.js +444 -444
- package/src/plugins/analytics.js +34 -34
- package/src/plugins/bus.js +12 -8
- package/src/plugins/gsap.js +17 -17
- package/src/plugins/helper.js +355 -358
- package/src/plugins/i18n.js +29 -26
- package/src/plugins/idb.js +227 -227
- package/src/plugins/save.js +37 -37
- package/src/plugins/scorm.js +287 -287
- package/src/plugins/xapi.js +11 -11
- package/src/public/index.html +33 -33
- package/src/router/index.js +57 -57
- package/src/router/routes.js +312 -312
- package/src/shared/generalfuncs.js +344 -344
- package/src/shared/validators.js +1018 -1018
- package/tests/component/AppBaseButton.spec.js +53 -0
- package/tests/component/pinia.spec.js +24 -0
- package/{src/components/tests__ → tests/unit}/AppBaseButton.spec.js +53 -53
- package/tests/unit/AppCompAudio.spec.js +134 -0
- package/tests/unit/AppCompCarousel.spec.js +54 -0
- package/tests/unit/AppCompInputCheckBoxNx.spec.js +59 -0
- package/tests/unit/AppCompInputDropdownNx.spec.js +51 -0
- package/tests/unit/AppCompInputRadioNx.spec.js +59 -0
- package/tests/unit/AppCompInputTextNx.spec.js +44 -0
- package/tests/unit/AppCompInputTextTableNx.spec.js +77 -0
- package/tests/unit/AppCompInputTextToFillDropdownNx.spec.js +60 -0
- package/tests/unit/AppCompInputTextToFillNx.spec.js +45 -0
- package/tests/unit/AppCompNoteCredit.spec.js +58 -0
- package/tests/unit/AppCompQuizNext.spec.js +112 -0
- package/tests/unit/AppCompVideoPlayer.spec.js +169 -0
- package/tests/unit/useQuiz.spec.js +72 -0
- package/{src/components/tests__ → tests/unit}/useTimer.spec.js +91 -91
- package/vitest.config.js +42 -19
- package/vitest.setup.js +96 -0
- package/src/components/AppBaseButton.test.js +0 -21
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { mount } from '@vue/test-utils'
|
|
2
|
+
import AppCompInputTextToFillDropdownNx from '@/components/AppCompInputTextToFillDropdownNx.vue'
|
|
3
|
+
import { beforeEach, describe, expect, test } from 'vitest'
|
|
4
|
+
import { VSelect } from 'vuetify/components/VSelect'
|
|
5
|
+
|
|
6
|
+
const dummyProps = {
|
|
7
|
+
modelValue: ['rouge', 'rouge', 'jaune', 'jaune', 'vert', 'magenta'],
|
|
8
|
+
inputData: [
|
|
9
|
+
{
|
|
10
|
+
un: ['rouge', 'vert', 'bleu', 'jaune', 'magenta', 'cyan']
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
deux: ['rouge', 'vert', 'bleu', 'jaune', 'magenta', 'cyan']
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
trois: ['rouge', 'vert', 'bleu', 'jaune', 'magenta', 'cyan']
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
quatre: ['rouge', 'vert', 'bleu', 'jaune', 'magenta', 'cyan']
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
cinq: ['rouge', 'vert', 'bleu', 'jaune', 'magenta', 'cyan']
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
six: ['rouge', 'vert', 'bleu', 'jaune', 'magenta', 'cyan']
|
|
26
|
+
}
|
|
27
|
+
],
|
|
28
|
+
textBase:
|
|
29
|
+
'$%un%$ Bacon ipsum dolor amet ground round shank turkey buffalo flank, doner pork chop pork belly ham hock corned beef. Sirloin bresaola kielbasa pig flank $%deux%$ landjaeger biltong porchetta. Pork belly prosciutto sirloin shoulder, leberkas bresaola chicken pastrami pork ground round alcatra chislic ham. Prosciutto corned beef filet mignon chislic $%trois%$ pancetta salami bresaola chicken. Chislic kielbasa alcatra, porchetta cow hamburger $%quatre%$ Lorem Ipsum Dolor $%cinq%$ cupim chuck sirloin turkey spare ribs jowl. Spare ribs swine alcatra picanha beef corned beef tenderloin bresaola shankle. Chislic jowl frankfurter flank leberkas filet mignon ground round strip steak.$%six%$',
|
|
30
|
+
solution: null,
|
|
31
|
+
id: 'A02_P02_Q4'
|
|
32
|
+
}
|
|
33
|
+
describe('AppCompInputTextToFillDropdownNx.vue', () => {
|
|
34
|
+
let wrapper = null
|
|
35
|
+
beforeEach(() => {
|
|
36
|
+
wrapper = mount(AppCompInputTextToFillDropdownNx, {
|
|
37
|
+
props: dummyProps
|
|
38
|
+
})
|
|
39
|
+
})
|
|
40
|
+
test(`renders a list of vuetify v-select components`, () => {
|
|
41
|
+
expect(wrapper.findAllComponents(VSelect).length).toBe(
|
|
42
|
+
dummyProps.inputData.length
|
|
43
|
+
)
|
|
44
|
+
})
|
|
45
|
+
test(`has the right initial data to display (modelValue prop)`, () => {
|
|
46
|
+
expect(wrapper.vm.inputsValue).toStrictEqual(dummyProps.modelValue)
|
|
47
|
+
})
|
|
48
|
+
test('emits enable-submit and update:modelValue events on input change', () => {
|
|
49
|
+
wrapper.vm.inputsValue = [
|
|
50
|
+
'jaune',
|
|
51
|
+
'rouge',
|
|
52
|
+
'rouge',
|
|
53
|
+
'jaune',
|
|
54
|
+
'vert',
|
|
55
|
+
'magenta'
|
|
56
|
+
]
|
|
57
|
+
expect(wrapper.emitted()).toHaveProperty('enable-submit')
|
|
58
|
+
expect(wrapper.emitted()).toHaveProperty('update:modelValue')
|
|
59
|
+
})
|
|
60
|
+
})
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { mount } from '@vue/test-utils'
|
|
2
|
+
import AppCompInputTextToFillNx from '@/components/AppCompInputTextToFillNx.vue'
|
|
3
|
+
import { beforeEach, describe, expect, test } from 'vitest'
|
|
4
|
+
import { VTextField } from 'vuetify/components/VTextField'
|
|
5
|
+
|
|
6
|
+
const dummyProps = {
|
|
7
|
+
modelValue: ['Hello', 'world', 'lorem', 'ipsum', 'good', 'bye'],
|
|
8
|
+
textBase:
|
|
9
|
+
'$%un%$ Bacon ipsum dolor amet groun doner pork. Sirloin bresaola kielbasa pig flank $%deux%$ landjaege. Pork belly prosciutto sirloin shoulder, pork ground round alcatra chislic ham. Prosciutto corned chislic $%trois%$ pancetta salami bresaola chicken. Chislic, porchetta cow hamburger $%quatre%$ $%cinq%$ cupim chuck sirloin turkey spare ribs jowl. Spare ribs swine alcatra picanha beef corned beef tenderloin bresaola shankle. Chislic jowl frankfurter flank leberkas filet mignon ground round strip steak.$%six%$',
|
|
10
|
+
inputData: null,
|
|
11
|
+
solution: null,
|
|
12
|
+
id: 'A02_P02_Q4'
|
|
13
|
+
}
|
|
14
|
+
describe('AppCompInputTextToFillNx.vue', () => {
|
|
15
|
+
let wrapper = null
|
|
16
|
+
|
|
17
|
+
beforeEach(() => {
|
|
18
|
+
wrapper = mount(AppCompInputTextToFillNx, {
|
|
19
|
+
props: dummyProps
|
|
20
|
+
})
|
|
21
|
+
})
|
|
22
|
+
test(`renders a paragraph with multiple vuetify v-text-field`, () => {
|
|
23
|
+
const textfields = wrapper.findAllComponents(VTextField)
|
|
24
|
+
expect(textfields.length).toBeGreaterThan(0)
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
test(`displays the user saved answers`, () => {
|
|
28
|
+
const textfields = wrapper.findAll('input[type="text"]')
|
|
29
|
+
textfields.forEach((textfield, index) => {
|
|
30
|
+
expect(textfield.element.value).toBe(dummyProps.modelValue[index])
|
|
31
|
+
})
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
test('accepts some input text', async () => {
|
|
35
|
+
const textfield = wrapper.find('input[type="text"]')
|
|
36
|
+
await textfield.setValue('new value')
|
|
37
|
+
expect(textfield.element.value).toBe('new value')
|
|
38
|
+
})
|
|
39
|
+
test('emits enable-submit and update:modelValue events on input change', async () => {
|
|
40
|
+
const textfield = wrapper.find('input[type="text"]')
|
|
41
|
+
await textfield.setValue('new value')
|
|
42
|
+
expect(wrapper.emitted()).toHaveProperty('enable-submit')
|
|
43
|
+
expect(wrapper.emitted()).toHaveProperty('update:modelValue')
|
|
44
|
+
})
|
|
45
|
+
})
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { mount } from '@vue/test-utils'
|
|
2
|
+
import AppCompNoteCredit from '../../src/components/AppCompNoteCredit.vue'
|
|
3
|
+
import { beforeEach, describe, expect, vi, test } from 'vitest'
|
|
4
|
+
|
|
5
|
+
const dummyProps = [
|
|
6
|
+
{
|
|
7
|
+
id: 'nt_1',
|
|
8
|
+
text: `note 1`
|
|
9
|
+
},
|
|
10
|
+
{
|
|
11
|
+
id: 'nt_2',
|
|
12
|
+
text: `note 2`
|
|
13
|
+
}
|
|
14
|
+
]
|
|
15
|
+
|
|
16
|
+
describe('AppCompNoteCredit', () => {
|
|
17
|
+
let wrapper = null
|
|
18
|
+
|
|
19
|
+
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
|
+
wrapper = mount(AppCompNoteCredit, {
|
|
37
|
+
global: {
|
|
38
|
+
stubs: {
|
|
39
|
+
'app-base-button': true,
|
|
40
|
+
'app-base-error-display': true
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
})
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
test('It renders a the pop-up', () => {
|
|
47
|
+
const wrapper = mount(AppCompNoteCredit)
|
|
48
|
+
|
|
49
|
+
expect(wrapper.exists()).toBe(true)
|
|
50
|
+
|
|
51
|
+
const div = wrapper.find('#noteCredit')
|
|
52
|
+
expect(div.exists()).toBe(true)
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
test('It renders a list of ${dummyProps.length}', () => {
|
|
56
|
+
expect(wrapper.findAll('div').length).toBe(dummyProps.length)
|
|
57
|
+
})
|
|
58
|
+
})
|
|
@@ -0,0 +1,112 @@
|
|
|
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
|
+
})
|
|
@@ -0,0 +1,169 @@
|
|
|
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
|
+
videosData: [
|
|
10
|
+
{
|
|
11
|
+
id: 'vid1',
|
|
12
|
+
mSources: [
|
|
13
|
+
{
|
|
14
|
+
type: 'mp4',
|
|
15
|
+
src: 'exemple_video.mp4'
|
|
16
|
+
}
|
|
17
|
+
],
|
|
18
|
+
mSubtitles: [
|
|
19
|
+
{
|
|
20
|
+
label: 'Français',
|
|
21
|
+
src: 'exemple_soustitres.vtt',
|
|
22
|
+
srclang: 'fr'
|
|
23
|
+
}
|
|
24
|
+
],
|
|
25
|
+
mPoster: 'video_poster.jpg',
|
|
26
|
+
mTranscript: 'exemple_transcript.html' // The file MUST be host in the public folder of the project
|
|
27
|
+
}
|
|
28
|
+
]
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
import AppCompVideoPlayer from '@/components/AppCompVideoPlayer.vue'
|
|
32
|
+
import { validateVideoData } from '@/shared/validators.js'
|
|
33
|
+
|
|
34
|
+
describe('AppCompVideoPlayer', () => {
|
|
35
|
+
test('Validate received props', () => {
|
|
36
|
+
validateVideoData.mockReturnValue([])
|
|
37
|
+
const { videosData } = dummyProps
|
|
38
|
+
mount(AppCompVideoPlayer, {
|
|
39
|
+
props: { vidData: videosData[0] }
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
expect(validateVideoData).toHaveBeenCalledWith({
|
|
43
|
+
id: 'vid1',
|
|
44
|
+
mSources: [
|
|
45
|
+
{
|
|
46
|
+
type: 'mp4',
|
|
47
|
+
src: 'exemple_video.mp4'
|
|
48
|
+
}
|
|
49
|
+
],
|
|
50
|
+
mSubtitles: [
|
|
51
|
+
{
|
|
52
|
+
label: 'Français',
|
|
53
|
+
src: 'exemple_soustitres.vtt',
|
|
54
|
+
srclang: 'fr'
|
|
55
|
+
}
|
|
56
|
+
],
|
|
57
|
+
mPoster: 'video_poster.jpg',
|
|
58
|
+
mTranscript: 'exemple_transcript.html' // The file MUST be host in the public folder of the project
|
|
59
|
+
})
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
it('renders ErrorDisplay When validator returns errors', () => {
|
|
63
|
+
// simulate invalid props
|
|
64
|
+
validateVideoData.mockReturnValue([
|
|
65
|
+
'WARNING!>>> VIDEO: 💥 Invalid declaration for video element'
|
|
66
|
+
])
|
|
67
|
+
|
|
68
|
+
const wrapper = mount(AppCompVideoPlayer, {
|
|
69
|
+
props: { vidData: { id: null } }
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
expect(
|
|
73
|
+
wrapper.findComponent({ name: 'AppBaseErrorDisplay' }).exists()
|
|
74
|
+
).toBe(true)
|
|
75
|
+
expect(wrapper.find('video').exists()).toBe(false)
|
|
76
|
+
})
|
|
77
|
+
|
|
78
|
+
it('It renders HTML video Element', () => {
|
|
79
|
+
validateVideoData.mockReturnValue([]) // no Error
|
|
80
|
+
const wrapper = mount(AppCompVideoPlayer, {
|
|
81
|
+
props: { vidData: { id: null } }
|
|
82
|
+
})
|
|
83
|
+
expect(
|
|
84
|
+
wrapper.findComponent({ name: 'AppBaseErrorDisplay' }).exists()
|
|
85
|
+
).toBe(false)
|
|
86
|
+
expect(wrapper.find('video').exists()).toBe(true)
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
it('Video Element has correct sources', async () => {
|
|
90
|
+
const { videosData } = dummyProps
|
|
91
|
+
validateVideoData.mockReturnValue([]) // no Error
|
|
92
|
+
const wrapper = mount(AppCompVideoPlayer, {
|
|
93
|
+
props: { vidData: videosData[0] }
|
|
94
|
+
})
|
|
95
|
+
|
|
96
|
+
expect(wrapper.vm.vidSources).toEqual(videosData[0].mSources)
|
|
97
|
+
|
|
98
|
+
//sources are rendered in the DOM
|
|
99
|
+
await wrapper.vm.$nextTick()
|
|
100
|
+
const sources = wrapper.findAll('source')
|
|
101
|
+
expect(sources.length).toBe(1)
|
|
102
|
+
|
|
103
|
+
// --- 3) valider les valeurs réelles rendues ---
|
|
104
|
+
expect(sources[0].attributes().src).toBe(videosData[0].mSources[0].src)
|
|
105
|
+
expect(sources[0].attributes().type).toBe('video/mp4')
|
|
106
|
+
})
|
|
107
|
+
|
|
108
|
+
it('computed $vidElement returns correct data structure', async () => {
|
|
109
|
+
const { videosData } = dummyProps
|
|
110
|
+
validateVideoData.mockReturnValue([])
|
|
111
|
+
|
|
112
|
+
const wrapper = mount(AppCompVideoPlayer, {
|
|
113
|
+
props: { vidData: videosData[0] }
|
|
114
|
+
})
|
|
115
|
+
|
|
116
|
+
// Simulate refs
|
|
117
|
+
const mockVideoRef = { tagName: 'VIDEO' }
|
|
118
|
+
const mockContainerRef = { className: '__media-container' }
|
|
119
|
+
|
|
120
|
+
wrapper.vm.$refs['m-video'] = mockVideoRef
|
|
121
|
+
wrapper.vm.$refs['$media-container'] = mockContainerRef
|
|
122
|
+
|
|
123
|
+
// Simulate refs isSet (video ready) to true
|
|
124
|
+
wrapper.vm.isSet = true
|
|
125
|
+
await wrapper.vm.$nextTick()
|
|
126
|
+
|
|
127
|
+
// get vidElement object
|
|
128
|
+
const obj = wrapper.vm.$vidElement
|
|
129
|
+
const id = wrapper.vm.$props.vidData.id
|
|
130
|
+
|
|
131
|
+
// expected data structure to be returned
|
|
132
|
+
const vidElement = {
|
|
133
|
+
id,
|
|
134
|
+
mType: 'video',
|
|
135
|
+
mElement: { id, localName: 'video' },
|
|
136
|
+
mMediaContainer: {
|
|
137
|
+
id: `video_${id}`,
|
|
138
|
+
...mockContainerRef,
|
|
139
|
+
localName: 'section'
|
|
140
|
+
},
|
|
141
|
+
mTranscript: 'exemple_transcript.html',
|
|
142
|
+
mSubtitles: [
|
|
143
|
+
{
|
|
144
|
+
label: 'Français',
|
|
145
|
+
src: 'exemple_soustitres.vtt',
|
|
146
|
+
srclang: 'fr'
|
|
147
|
+
}
|
|
148
|
+
]
|
|
149
|
+
}
|
|
150
|
+
// validate returned object
|
|
151
|
+
expect(obj.id).toEqual(vidElement.id)
|
|
152
|
+
expect(obj.mType).toBe(vidElement.mType)
|
|
153
|
+
expect(obj.mElement.id).toBe(vidElement.mElement.id)
|
|
154
|
+
expect(obj.mElement.tagName.toLowerCase()).toBe(
|
|
155
|
+
vidElement.mElement.localName
|
|
156
|
+
)
|
|
157
|
+
expect(obj.mMediaContainer.tagName.toLowerCase()).toBe(
|
|
158
|
+
vidElement.mMediaContainer.localName
|
|
159
|
+
)
|
|
160
|
+
expect(obj.mMediaContainer.id).toBe(vidElement.mMediaContainer.id)
|
|
161
|
+
expect(
|
|
162
|
+
obj.mMediaContainer.classList.contains(
|
|
163
|
+
vidElement.mMediaContainer.className
|
|
164
|
+
)
|
|
165
|
+
).toBe(true)
|
|
166
|
+
expect(obj.mTranscript).toBe(vidElement.mTranscript)
|
|
167
|
+
expect(obj.mSubtitles).toEqual(vidElement.mSubtitles)
|
|
168
|
+
})
|
|
169
|
+
})
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { describe, it, expect, vi } from 'vitest'
|
|
2
|
+
import { useQuiz } from '../../src/composables/useQuiz.js'
|
|
3
|
+
|
|
4
|
+
// Mock de useI18n pour éviter les erreurs liées à $t()
|
|
5
|
+
vi.mock('vue-i18n', () => ({
|
|
6
|
+
useI18n: () => ({ t: (key) => key })
|
|
7
|
+
}))
|
|
8
|
+
|
|
9
|
+
describe('useQuiz composable', () => {
|
|
10
|
+
const { shuffleArray, addRetroStyle, resetRetroStyle, retroType } = useQuiz()
|
|
11
|
+
|
|
12
|
+
it('shuffleArray should shuffle array without losing elements', () => {
|
|
13
|
+
const arr = [1, 2, 3, 4]
|
|
14
|
+
const shuffled = shuffleArray(arr)
|
|
15
|
+
expect(shuffled).toHaveLength(arr.length)
|
|
16
|
+
expect(shuffled.sort()).toEqual(arr.sort())
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
it('addRetroStyle should return correct classes and messages for correct answers', () => {
|
|
20
|
+
const solution = [{ id: 1 }, { id: 2 }]
|
|
21
|
+
const reponse = [{ correct: true }, { correct: true }]
|
|
22
|
+
const result = addRetroStyle(solution, reponse, reponse.length)
|
|
23
|
+
expect(result.classRetro).toEqual(['goodAnswer', 'goodAnswer'])
|
|
24
|
+
expect(result.mesA11y).toEqual([
|
|
25
|
+
'quizState.goodAnswer',
|
|
26
|
+
'quizState.goodAnswer'
|
|
27
|
+
])
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
it('addRetroStyle should return neutral style when solution is null', () => {
|
|
31
|
+
const result = addRetroStyle(null, [], 0)
|
|
32
|
+
expect(result.classRetro).toEqual(['NeutralAnswer'])
|
|
33
|
+
expect(result.mesA11y).toEqual(['quizState.neutralAnswer'])
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
it('retroType should return retro_positive when all answers are correct', () => {
|
|
37
|
+
const solution = [{ id: 1 }, { id: 2 }]
|
|
38
|
+
const reponse = [{ correct: true }, { correct: true }]
|
|
39
|
+
expect(retroType(solution, reponse)).toBe('retro_positive')
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
it('retroType should return retro_negative when some answers are wrong', () => {
|
|
43
|
+
const solution = ['Données primaires', 'Données secondaires']
|
|
44
|
+
const reponse = [
|
|
45
|
+
{
|
|
46
|
+
selected: 'Données primaires',
|
|
47
|
+
correct: true
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
selected: 'Données primaires',
|
|
51
|
+
correct: false
|
|
52
|
+
}
|
|
53
|
+
]
|
|
54
|
+
expect(retroType(solution, reponse)).toBe('retro_negative')
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
it('retroType should return retro_neutre when solution is null', () => {
|
|
58
|
+
expect(retroType(null, [])).toBe('retro_neutre')
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
it('resetRetroStyle should clear arrays and emit event', () => {
|
|
62
|
+
const fakeBus = { $emit: vi.fn() }
|
|
63
|
+
const fakeEl = {}
|
|
64
|
+
const context = { $bus: fakeBus, $el: fakeEl }
|
|
65
|
+
const arr1 = [1, 2]
|
|
66
|
+
const arr2 = ['a']
|
|
67
|
+
resetRetroStyle.call(context, [arr1, arr2])
|
|
68
|
+
expect(arr1.length).toBe(0)
|
|
69
|
+
expect(arr2.length).toBe(0)
|
|
70
|
+
expect(fakeBus.$emit).toHaveBeenCalledWith('hide-retro', fakeEl, false)
|
|
71
|
+
})
|
|
72
|
+
})
|