@windward/core 0.0.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 +13 -0
- package/.eslintrc.js +11 -0
- package/.prettierrc +4 -0
- package/README.md +86 -0
- package/babel.config.js +1 -0
- package/components/Content/Blocks/Accordion.vue +133 -0
- package/components/Content/Blocks/ClickableIcons.vue +98 -0
- package/components/Content/Blocks/Feedback.vue +323 -0
- package/components/Content/Blocks/FeedbackTemplates/FeedbackQuestionLikert.vue +95 -0
- package/components/Content/Blocks/FeedbackTemplates/FeedbackQuestionOpenResponse.vue +45 -0
- package/components/Content/Blocks/FeedbackTemplates/FeedbackQuestionTrueFalse.vue +55 -0
- package/components/Content/Blocks/Image.vue +116 -0
- package/components/Content/Blocks/Math.vue +87 -0
- package/components/Content/Blocks/Tab.vue +89 -0
- package/components/Content/Blocks/Table.vue +68 -0
- package/components/Content/Blocks/UserUpload/DisplayUserFilesTable.vue +157 -0
- package/components/Content/Blocks/UserUpload/ManageDataTableUserFiles.vue +68 -0
- package/components/Content/Blocks/UserUpload.vue +240 -0
- package/components/Content/Blocks/Video.vue +245 -0
- package/components/Navigation/Items/CourseGlossaryToolNav.vue +39 -0
- package/components/Navigation/Items/GlossaryNav.vue +32 -0
- package/components/Navigation/Items/UserUploadNav.vue +35 -0
- package/components/Settings/AccordionSettings.vue +153 -0
- package/components/Settings/ClickableIconsSettings.vue +189 -0
- package/components/Settings/FeedbackSettings.vue +92 -0
- package/components/Settings/ImageSettings.vue +124 -0
- package/components/Settings/MathSettings.vue +81 -0
- package/components/Settings/TabSettings.vue +139 -0
- package/components/Settings/TextEditorSettings.vue +249 -0
- package/components/Settings/UserUploadSettings.vue +151 -0
- package/components/Settings/VideoSettings.vue +699 -0
- package/components/utils/ContentViewer.vue +69 -0
- package/components/utils/MathExpressionEditor.vue +294 -0
- package/components/utils/MathLiveWrapper.vue +86 -0
- package/components/utils/TinyMCEWrapper.vue +103 -0
- package/components/utils/glossary/CourseGlossary.vue +284 -0
- package/components/utils/glossary/CourseGlossaryForm.vue +106 -0
- package/components/utils/glossary/GlossaryToolTip.vue +87 -0
- package/config/tinymce.config.js +136 -0
- package/helpers/GlossaryHelper.ts +137 -0
- package/helpers/GlossaryTerm.ts +31 -0
- package/helpers/MathHelper.ts +236 -0
- package/helpers/tinymce/plugin.ts +83 -0
- package/i18n/en-US/components/content/blocks/feedback.ts +28 -0
- package/i18n/en-US/components/content/blocks/image.ts +5 -0
- package/i18n/en-US/components/content/blocks/index.ts +15 -0
- package/i18n/en-US/components/content/blocks/tab.ts +4 -0
- package/i18n/en-US/components/content/blocks/table.ts +4 -0
- package/i18n/en-US/components/content/blocks/user_upload.ts +12 -0
- package/i18n/en-US/components/content/blocks/video.ts +53 -0
- package/i18n/en-US/components/content/index.ts +5 -0
- package/i18n/en-US/components/index.ts +12 -0
- package/i18n/en-US/components/navigation/image.ts +4 -0
- package/i18n/en-US/components/navigation/index.ts +7 -0
- package/i18n/en-US/components/navigation/user_upload.ts +3 -0
- package/i18n/en-US/components/settings/clickable_icon.ts +9 -0
- package/i18n/en-US/components/settings/image.ts +1 -0
- package/i18n/en-US/components/settings/index.ts +13 -0
- package/i18n/en-US/components/settings/text_editor.ts +7 -0
- package/i18n/en-US/components/settings/user_upload.ts +11 -0
- package/i18n/en-US/components/settings/video.ts +13 -0
- package/i18n/en-US/components/utils/index.ts +5 -0
- package/i18n/en-US/components/utils/tiny_mce_wrapper.ts +7 -0
- package/i18n/en-US/index.ts +16 -0
- package/i18n/en-US/modules/index.ts +5 -0
- package/i18n/en-US/pages/glossary.ts +7 -0
- package/i18n/en-US/pages/index.ts +7 -0
- package/i18n/en-US/pages/user_upload.ts +3 -0
- package/i18n/en-US/shared/content_blocks.ts +20 -0
- package/i18n/en-US/shared/index.ts +11 -0
- package/i18n/en-US/shared/menu.ts +3 -0
- package/i18n/en-US/shared/permission.ts +15 -0
- package/i18n/en-US/shared/settings.ts +15 -0
- package/index.js +15 -0
- package/jest.config.js +15 -0
- package/models/SurveyResult.ts +8 -0
- package/models/SurveyTemplate.ts +8 -0
- package/models/UserFileAsset.ts +12 -0
- package/package.json +48 -0
- package/pages/glossary.vue +31 -0
- package/pages/userUpload.vue +204 -0
- package/plugin.js +299 -0
- package/test/Components/Content/Blocks/Accordion.spec.js +21 -0
- package/test/Components/Content/Blocks/ClickableIcons.spec.js +21 -0
- package/test/Components/Content/Blocks/Feedback.spec.js +31 -0
- package/test/Components/Content/Blocks/FeedbackTemplates/FeedbackQuestionLikert.spec.js +23 -0
- package/test/Components/Content/Blocks/FeedbackTemplates/FeedbackQuestionOpenResponse.spec.js +23 -0
- package/test/Components/Content/Blocks/FeedbackTemplates/FeedbackQuestionTrueFalse.spec.js +23 -0
- package/test/Components/Content/Blocks/Image.spec.js +34 -0
- package/test/Components/Content/Blocks/Math.spec.js +34 -0
- package/test/Components/Content/Blocks/Tab.spec.js +20 -0
- package/test/Components/Content/Blocks/Table.spec.js +21 -0
- package/test/Components/Content/Blocks/UserUpload.spec.js +25 -0
- package/test/Components/Content/Blocks/Video.spec.js +112 -0
- package/test/Components/Settings/AccordionSettings.spec.js +45 -0
- package/test/Components/Settings/ClickableIconsSettings.spec.js +20 -0
- package/test/Components/Settings/FeedbackSettings.spec.js +20 -0
- package/test/Components/Settings/ImageSettings.spec.js +20 -0
- package/test/Components/Settings/MathSettings.spec.js +20 -0
- package/test/Components/Settings/TabSettings.spec.js +45 -0
- package/test/Components/Settings/UserUploadSettings.spec.js +20 -0
- package/test/__mocks__/componentsMock.js +83 -0
- package/test/__mocks__/contentBlockMock.js +119 -0
- package/test/__mocks__/contentSettingsMock.js +54 -0
- package/test/__mocks__/fileMock.js +1 -0
- package/test/__mocks__/helpersMock.js +53 -0
- package/test/__mocks__/modelMock.js +82 -0
- package/test/__mocks__/styleMock.js +1 -0
- package/test/helpers/GlossaryHelper.spec.js +227 -0
- package/test/helpers/MathHelper.spec.js +128 -0
- package/test/mocks.js +140 -0
- package/tsconfig.json +15 -0
- package/utils/index.js +18 -0
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { Model as mockModel } from 'vue-api-query'
|
|
2
|
+
|
|
3
|
+
import axios from 'axios'
|
|
4
|
+
jest.mock('axios')
|
|
5
|
+
mockModel.$http = axios
|
|
6
|
+
|
|
7
|
+
jest.mock('axios')
|
|
8
|
+
|
|
9
|
+
// Define any new model mocks here. The imports / mocks will be auto-generated below
|
|
10
|
+
const mockVirtualModels = [
|
|
11
|
+
{ path: '~/models/Enrollment', resource: 'enrollments' },
|
|
12
|
+
{ path: '~/models/ContentBlock', resource: 'blocks' },
|
|
13
|
+
{ path: '~/models/FileAsset', resource: 'file-assets' },
|
|
14
|
+
{ path: '~/models/UserFileAsset', resource: 'user-files' },
|
|
15
|
+
{ path: '~/models/Course', resource: 'courses' },
|
|
16
|
+
]
|
|
17
|
+
|
|
18
|
+
const mockModels = [
|
|
19
|
+
{ path: '../../models/UserFileAsset', resource: 'user-files' },
|
|
20
|
+
]
|
|
21
|
+
|
|
22
|
+
// DO NOT ALTER THE BELOW CODE
|
|
23
|
+
jest.mock('', () => {
|
|
24
|
+
return {
|
|
25
|
+
__esModule: true,
|
|
26
|
+
default: class Model {},
|
|
27
|
+
}
|
|
28
|
+
})
|
|
29
|
+
class mockBaseModel extends mockModel {
|
|
30
|
+
baseURL() {
|
|
31
|
+
return 'http://windwardapi.local'
|
|
32
|
+
}
|
|
33
|
+
request(config) {
|
|
34
|
+
return new Promise((resolve) => {
|
|
35
|
+
resolve({ data: {} })
|
|
36
|
+
})
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
jest.mock(
|
|
41
|
+
'~/models/Model',
|
|
42
|
+
() => {
|
|
43
|
+
return {
|
|
44
|
+
__esModule: true,
|
|
45
|
+
default: class Model extends mockBaseModel {},
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
{ virtual: true }
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
for (let mockI = 0; mockI < mockVirtualModels.length; mockI++) {
|
|
52
|
+
jest.mock(
|
|
53
|
+
mockVirtualModels[mockI].path,
|
|
54
|
+
() => {
|
|
55
|
+
return {
|
|
56
|
+
__esModule: true,
|
|
57
|
+
default: class Model extends mockBaseModel {
|
|
58
|
+
resource() {
|
|
59
|
+
return (
|
|
60
|
+
mockVirtualModels[mockI].resource ||
|
|
61
|
+
this.constructor.name
|
|
62
|
+
)
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
}
|
|
66
|
+
},
|
|
67
|
+
{ virtual: true }
|
|
68
|
+
)
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
for (let mockJ = 0; mockJ < mockModels.length; mockJ++) {
|
|
72
|
+
jest.mock(mockModels[mockJ].path, () => {
|
|
73
|
+
return {
|
|
74
|
+
__esModule: true,
|
|
75
|
+
default: class Model extends mockBaseModel {
|
|
76
|
+
resource() {
|
|
77
|
+
return mockModels[mockJ].resource || this.constructor.name
|
|
78
|
+
}
|
|
79
|
+
},
|
|
80
|
+
}
|
|
81
|
+
})
|
|
82
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
module.exports = {}
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
import GlossaryHelper from '../../helpers/GlossaryHelper'
|
|
2
|
+
import GlossaryTerm from '../../helpers/GlossaryTerm'
|
|
3
|
+
|
|
4
|
+
const contentHtml =
|
|
5
|
+
'<p>Voluptas est quis ut <strong>consequatur.</strong>' +
|
|
6
|
+
' Nesciunt et possimus minima unde ullam sed neque adipisci. Unde ullam ut reprehenderit cupiditate culpa et ratione est. ' +
|
|
7
|
+
'Exercitationem eum nobis nulla quis officia qui. Eos non soluta aperiam. Voluptate sit itaque vero. Non nesciunt ut nihil commodi.' +
|
|
8
|
+
' Et inventore hic aut.</p>'
|
|
9
|
+
const glossaryWord =
|
|
10
|
+
'<span class="glossary-word" aria-label="Glossary Term">Architecto</span>'
|
|
11
|
+
|
|
12
|
+
const glossaryWord2 =
|
|
13
|
+
'<span class="glossary-word" aria-label="Glossary Term">unverified</span>'
|
|
14
|
+
|
|
15
|
+
const unVerifiedGlossaryWordWithSpace =
|
|
16
|
+
'<span class="glossary-word" aria-label="Glossary Term"> Climate </span>'
|
|
17
|
+
const glossaryWord3 =
|
|
18
|
+
'<span class="glossary-word" aria-label="Glossary Term">Crisis change</span>'
|
|
19
|
+
const glossaryWordSpaceBefore =
|
|
20
|
+
'<span class="glossary-word" aria-label="Glossary Term"> Crisis change</span>'
|
|
21
|
+
const glossaryWordSpaceAfter =
|
|
22
|
+
'<span class="glossary-word" aria-label="Glossary Term">Crisis change </span>'
|
|
23
|
+
const glossaryWordSpaceBeforeAfter =
|
|
24
|
+
'<span class="glossary-word" aria-label="Glossary Term"> Crisis change </span>'
|
|
25
|
+
const glossary = [
|
|
26
|
+
{
|
|
27
|
+
term: 'veniam',
|
|
28
|
+
definition: 'venom is venomus',
|
|
29
|
+
alternate_forms: null,
|
|
30
|
+
related_term: null,
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
term: 'Architecto',
|
|
34
|
+
definition: 'Architecto is a word that means architect of the universe',
|
|
35
|
+
alternate_forms: null,
|
|
36
|
+
related_term: 'a,b,c,d',
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
term: 'Voluptatem',
|
|
40
|
+
definition:
|
|
41
|
+
'Contrary to popular belief, Lorem Ipsum is not simply random text. It has roots in a piece of ' +
|
|
42
|
+
'classical Latin literature from 45 BC, making it over 2000 years old. Richard McClintock,' +
|
|
43
|
+
' a Latin professor at Hampden-Sydney College in Virginia',
|
|
44
|
+
alternate_forms: null,
|
|
45
|
+
related_term: null,
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
term: 'crisis change',
|
|
49
|
+
definition: 'n/a',
|
|
50
|
+
alternate_forms: null,
|
|
51
|
+
related_term: null,
|
|
52
|
+
},
|
|
53
|
+
]
|
|
54
|
+
const glossaryRender =
|
|
55
|
+
'<p>Voluptas est quis ut <strong>consequatur.</strong> ' +
|
|
56
|
+
'Nesciunt et possimus minima unde ullam sed neque adipisci. Unde ullam ut reprehenderit cupiditate culpa et ratione est. ' +
|
|
57
|
+
'Exercitationem eum nobis nulla quis officia qui. Eos non soluta aperiam. Voluptate sit itaque vero. Non nesciunt ut nihil commodi.' +
|
|
58
|
+
' Et inventore hic aut.</p>' +
|
|
59
|
+
'<plugin-core-glossary-tool-tip>' +
|
|
60
|
+
'<template v-slot:term>Architecto</template>' +
|
|
61
|
+
'<template v-slot:definition>Architecto is a word that means architect of the universe</template>' +
|
|
62
|
+
'<template v-slot:related_terms>a,b,c,d</template>' +
|
|
63
|
+
'</plugin-core-glossary-tool-tip>'
|
|
64
|
+
const glossaryRenderMultiple =
|
|
65
|
+
'<plugin-core-glossary-tool-tip>' +
|
|
66
|
+
'<template v-slot:term>Architecto</template>' +
|
|
67
|
+
'<template v-slot:definition>Architecto is a word that means architect of the universe</template>' +
|
|
68
|
+
'<template v-slot:related_terms>a,b,c,d</template>' +
|
|
69
|
+
'</plugin-core-glossary-tool-tip>' +
|
|
70
|
+
'<p>Voluptas est quis ut <strong>consequatur.</strong> ' +
|
|
71
|
+
'Nesciunt et possimus minima unde ullam sed neque adipisci. Unde ullam ut reprehenderit cupiditate culpa et ratione est. ' +
|
|
72
|
+
'Exercitationem eum nobis nulla quis officia qui. Eos non soluta aperiam. Voluptate sit itaque vero. Non nesciunt ut nihil commodi.' +
|
|
73
|
+
' Et inventore hic aut.</p>' +
|
|
74
|
+
'<plugin-core-glossary-tool-tip>' +
|
|
75
|
+
'<template v-slot:term>Architecto</template>' +
|
|
76
|
+
'<template v-slot:definition>Architecto is a word that means architect of the universe</template>' +
|
|
77
|
+
'<template v-slot:related_terms>a,b,c,d</template>' +
|
|
78
|
+
'</plugin-core-glossary-tool-tip>'
|
|
79
|
+
|
|
80
|
+
const term = new GlossaryTerm(glossary[1])
|
|
81
|
+
const expectedToolTip =
|
|
82
|
+
'<plugin-core-glossary-tool-tip>' +
|
|
83
|
+
'<template v-slot:term>Architecto</template>' +
|
|
84
|
+
'<template v-slot:definition>Architecto is a word that means architect of the universe</template>' +
|
|
85
|
+
'<template v-slot:related_terms>a,b,c,d</template>' +
|
|
86
|
+
'</plugin-core-glossary-tool-tip>'
|
|
87
|
+
const glossaryRenderCapitalized =
|
|
88
|
+
'<p>Voluptas est quis ut <strong>consequatur.</strong> ' +
|
|
89
|
+
'Nesciunt et possimus minima unde ullam sed neque adipisci. Unde ullam ut reprehenderit cupiditate culpa et ratione est. ' +
|
|
90
|
+
'Exercitationem eum nobis nulla quis officia qui. Eos non soluta aperiam. Voluptate sit itaque vero. Non nesciunt ut nihil commodi.' +
|
|
91
|
+
' Et inventore hic aut.</p>' +
|
|
92
|
+
'<plugin-core-glossary-tool-tip>' +
|
|
93
|
+
'<template v-slot:term>Crisis change</template>' +
|
|
94
|
+
'<template v-slot:definition>n/a</template>' +
|
|
95
|
+
'</plugin-core-glossary-tool-tip>'
|
|
96
|
+
const glossaryRenderCapitalizedSpaceBefore =
|
|
97
|
+
'<p>Voluptas est quis ut <strong>consequatur.</strong> ' +
|
|
98
|
+
'Nesciunt et possimus minima unde ullam sed neque adipisci. Unde ullam ut reprehenderit cupiditate culpa et ratione est. ' +
|
|
99
|
+
'Exercitationem eum nobis nulla quis officia qui. Eos non soluta aperiam. Voluptate sit itaque vero. Non nesciunt ut nihil commodi.' +
|
|
100
|
+
' Et inventore hic aut.</p>' +
|
|
101
|
+
'<plugin-core-glossary-tool-tip>' +
|
|
102
|
+
'<template v-slot:term> Crisis change</template>' +
|
|
103
|
+
'<template v-slot:definition>n/a</template>' +
|
|
104
|
+
'</plugin-core-glossary-tool-tip>'
|
|
105
|
+
const glossaryRenderCapitalizedSpaceAfter =
|
|
106
|
+
'<p>Voluptas est quis ut <strong>consequatur.</strong> ' +
|
|
107
|
+
'Nesciunt et possimus minima unde ullam sed neque adipisci. Unde ullam ut reprehenderit cupiditate culpa et ratione est. ' +
|
|
108
|
+
'Exercitationem eum nobis nulla quis officia qui. Eos non soluta aperiam. Voluptate sit itaque vero. Non nesciunt ut nihil commodi.' +
|
|
109
|
+
' Et inventore hic aut.</p>' +
|
|
110
|
+
'<plugin-core-glossary-tool-tip>' +
|
|
111
|
+
'<template v-slot:term>Crisis change </template>' +
|
|
112
|
+
'<template v-slot:definition>n/a</template>' +
|
|
113
|
+
'</plugin-core-glossary-tool-tip>'
|
|
114
|
+
const glossaryRenderCapitalizedSpaceBeforeAfter =
|
|
115
|
+
'<p>Voluptas est quis ut <strong>consequatur.</strong> ' +
|
|
116
|
+
'Nesciunt et possimus minima unde ullam sed neque adipisci. Unde ullam ut reprehenderit cupiditate culpa et ratione est. ' +
|
|
117
|
+
'Exercitationem eum nobis nulla quis officia qui. Eos non soluta aperiam. Voluptate sit itaque vero. Non nesciunt ut nihil commodi.' +
|
|
118
|
+
' Et inventore hic aut.</p>' +
|
|
119
|
+
'<plugin-core-glossary-tool-tip>' +
|
|
120
|
+
'<template v-slot:term> Crisis change </template>' +
|
|
121
|
+
'<template v-slot:definition>n/a</template>' +
|
|
122
|
+
'</plugin-core-glossary-tool-tip>'
|
|
123
|
+
describe('GlossaryHelper ', () => {
|
|
124
|
+
test('detects text has glossary words', () => {
|
|
125
|
+
expect(GlossaryHelper.containsGlossaryTerms(contentHtml + glossaryWord))
|
|
126
|
+
})
|
|
127
|
+
test('detects text does not contain glossary', () => {
|
|
128
|
+
expect(GlossaryHelper.containsGlossaryTerms(contentHtml)).toBeFalsy()
|
|
129
|
+
})
|
|
130
|
+
test('can retrieve verified glossary terms', () => {
|
|
131
|
+
expect(
|
|
132
|
+
GlossaryHelper.getContentVerifiedGlossaryTerms(
|
|
133
|
+
glossaryWord + contentHtml + glossaryWord,
|
|
134
|
+
glossary
|
|
135
|
+
)
|
|
136
|
+
).toEqual([term])
|
|
137
|
+
})
|
|
138
|
+
|
|
139
|
+
test('can retrieve un-verified glossary terms', () => {
|
|
140
|
+
expect(
|
|
141
|
+
GlossaryHelper.getContentUnVerifiedGlossaryTerms(
|
|
142
|
+
glossaryWord2 + contentHtml + glossaryWord2,
|
|
143
|
+
glossary
|
|
144
|
+
)
|
|
145
|
+
).toEqual([new GlossaryTerm({ term: 'unverified', definition: 'n/a' })])
|
|
146
|
+
})
|
|
147
|
+
|
|
148
|
+
test('can retrieve un-verified glossary terms with space', () => {
|
|
149
|
+
expect(
|
|
150
|
+
GlossaryHelper.getContentUnVerifiedGlossaryTerms(
|
|
151
|
+
unVerifiedGlossaryWordWithSpace + contentHtml + glossaryWord2,
|
|
152
|
+
glossary
|
|
153
|
+
)
|
|
154
|
+
).toEqual([
|
|
155
|
+
new GlossaryTerm({ term: 'Climate', definition: 'n/a' }),
|
|
156
|
+
new GlossaryTerm({ term: 'unverified', definition: 'n/a' }),
|
|
157
|
+
])
|
|
158
|
+
})
|
|
159
|
+
|
|
160
|
+
test('can retrieve un-verified glossary terms even with verified terms present', () => {
|
|
161
|
+
expect(
|
|
162
|
+
GlossaryHelper.getContentUnVerifiedGlossaryTerms(
|
|
163
|
+
glossaryWord2 + contentHtml + glossaryWord,
|
|
164
|
+
glossary
|
|
165
|
+
)
|
|
166
|
+
).toEqual([new GlossaryTerm({ term: 'unverified', definition: 'n/a' })])
|
|
167
|
+
})
|
|
168
|
+
test('can render glossary tooltip component', () => {
|
|
169
|
+
expect(GlossaryHelper.makeToolTip(term, 'Architecto')).toEqual(
|
|
170
|
+
expectedToolTip
|
|
171
|
+
)
|
|
172
|
+
})
|
|
173
|
+
test('render html ', () => {
|
|
174
|
+
expect(
|
|
175
|
+
GlossaryHelper.renderGlossaryWordsHtml(contentHtml, glossary)
|
|
176
|
+
).toEqual(contentHtml)
|
|
177
|
+
})
|
|
178
|
+
|
|
179
|
+
test('injects glossary vue component once', () => {
|
|
180
|
+
expect(
|
|
181
|
+
GlossaryHelper.renderGlossaryWordsHtml(
|
|
182
|
+
contentHtml + glossaryWord,
|
|
183
|
+
glossary
|
|
184
|
+
)
|
|
185
|
+
).toEqual(glossaryRender)
|
|
186
|
+
})
|
|
187
|
+
test('injects glossary multiple times', () => {
|
|
188
|
+
expect(
|
|
189
|
+
GlossaryHelper.renderGlossaryWordsHtml(
|
|
190
|
+
glossaryWord + contentHtml + glossaryWord,
|
|
191
|
+
glossary
|
|
192
|
+
)
|
|
193
|
+
).toEqual(glossaryRenderMultiple)
|
|
194
|
+
})
|
|
195
|
+
test('can recognize text when capitalized', () => {
|
|
196
|
+
expect(
|
|
197
|
+
GlossaryHelper.renderGlossaryWordsHtml(
|
|
198
|
+
contentHtml + glossaryWord3,
|
|
199
|
+
glossary
|
|
200
|
+
)
|
|
201
|
+
).toEqual(glossaryRenderCapitalized)
|
|
202
|
+
})
|
|
203
|
+
test('can recognize text when capitalized with space before', () => {
|
|
204
|
+
expect(
|
|
205
|
+
GlossaryHelper.renderGlossaryWordsHtml(
|
|
206
|
+
contentHtml + glossaryWordSpaceBefore,
|
|
207
|
+
glossary
|
|
208
|
+
)
|
|
209
|
+
).toEqual(glossaryRenderCapitalizedSpaceBefore)
|
|
210
|
+
})
|
|
211
|
+
test('can recognize text when capitalized with space after', () => {
|
|
212
|
+
expect(
|
|
213
|
+
GlossaryHelper.renderGlossaryWordsHtml(
|
|
214
|
+
contentHtml + glossaryWordSpaceAfter,
|
|
215
|
+
glossary
|
|
216
|
+
)
|
|
217
|
+
).toEqual(glossaryRenderCapitalizedSpaceAfter)
|
|
218
|
+
})
|
|
219
|
+
test('can recognize text when capitalized with space before and after', () => {
|
|
220
|
+
expect(
|
|
221
|
+
GlossaryHelper.renderGlossaryWordsHtml(
|
|
222
|
+
contentHtml + glossaryWordSpaceBeforeAfter,
|
|
223
|
+
glossary
|
|
224
|
+
)
|
|
225
|
+
).toEqual(glossaryRenderCapitalizedSpaceBeforeAfter)
|
|
226
|
+
})
|
|
227
|
+
})
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import MathHelper from '@/helpers/MathHelper'
|
|
2
|
+
|
|
3
|
+
const text =
|
|
4
|
+
'Voluptas est quis ut consequatur.' +
|
|
5
|
+
' Nesciunt et possimus minima unde ullam sed neque adipisci. Unde ullam ut reprehenderit cupiditate culpa et ratione est. ' +
|
|
6
|
+
'Exercitationem eum nobis nulla quis officia qui. Eos non soluta aperiam. Voluptate sit itaque vero. Non nesciunt ut nihil commodi.' +
|
|
7
|
+
' Et inventore hic aut.'
|
|
8
|
+
const mathML =
|
|
9
|
+
'<math xmlns="http://www.w3.org/1998/Math/MathML"><mfrac><mn>1</mn><mn>2</mn></mfrac><mo>+</mo><mfrac><mn>3</mn><mn>4</mn></mfrac></math>'
|
|
10
|
+
const latexContent = '\\frac{1}{2}+\\frac{3}{4}'
|
|
11
|
+
const srEnhancedlatex =
|
|
12
|
+
'**' +
|
|
13
|
+
JSON.stringify({
|
|
14
|
+
latex: latexContent,
|
|
15
|
+
sr_text: ' half plus three quarter ',
|
|
16
|
+
}) +
|
|
17
|
+
'**'
|
|
18
|
+
const mathliveHtml = `<span tabindex="0" aria-label=" half plus three quarter "> <span tabindex="-1" ><span class="ML__mathlive"><span class="ML__strut" style="height:1.33em"></span><span class="ML__strut--bottom" style="height:2.01em;vertical-align:-0.68em"></span><span class="ML__base"><span><span class="mfrac"><span class="nulldelimiter"></span><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.33em"><span class="ML__center" style="top:-2.31em"><span class="pstrut" style="height:3em"></span><span><span class="ML__cmr">2</span></span></span><span style="top:-3.21em"><span class="pstrut" style="height:3em"></span><span class="ML__frac-line"></span></span><span class="ML__center" style="top:-3.67em"><span class="pstrut" style="height:3em"></span><span><span class="ML__cmr">1</span></span></span></span><span class="vlist-s"></span></span><span class="vlist-r"><span class="vlist" style="height:0.69em"></span></span></span><span class="nulldelimiter"></span></span><span class="ML__cmr" style="margin-left:0.23em">+</span><span class="mfrac" style="margin-left:0.23em"><span class="nulldelimiter"></span><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.33em"><span class="ML__center" style="top:-2.31em"><span class="pstrut" style="height:3em"></span><span><span class="ML__cmr">4</span></span></span><span style="top:-3.21em"><span class="pstrut" style="height:3em"></span><span class="ML__frac-line"></span></span><span class="ML__center" style="top:-3.67em"><span class="pstrut" style="height:3em"></span><span><span class="ML__cmr">3</span></span></span></span><span class="vlist-s"></span></span><span class="vlist-r"><span class="vlist" style="height:0.69em"></span></span></span><span class="nulldelimiter"></span></span></span></span></span></span></span>`
|
|
19
|
+
describe('MahtHelper ', () => {
|
|
20
|
+
test('detects text has mathml', () => {
|
|
21
|
+
expect(MathHelper.containsMathML(text + mathML)).toBeTruthy()
|
|
22
|
+
})
|
|
23
|
+
test('detects text does not contain mathml', () => {
|
|
24
|
+
expect(MathHelper.containsMathML(text)).toBeFalsy()
|
|
25
|
+
})
|
|
26
|
+
test('detects text has latexContent', () => {
|
|
27
|
+
expect(
|
|
28
|
+
MathHelper.containsLatex(text + '$$' + latexContent + '$$')
|
|
29
|
+
).toBeTruthy()
|
|
30
|
+
})
|
|
31
|
+
test('detects text does not contain latex', () => {
|
|
32
|
+
expect(MathHelper.containsLatex(text)).toBeFalsy()
|
|
33
|
+
})
|
|
34
|
+
test('detects text has SR enhanced latex ', () => {
|
|
35
|
+
expect(
|
|
36
|
+
MathHelper.containSREnhancedLatex(srEnhancedlatex + text)
|
|
37
|
+
).toBeTruthy()
|
|
38
|
+
})
|
|
39
|
+
test('detects text does not contain SR enhanced latex ', () => {
|
|
40
|
+
expect(MathHelper.containSREnhancedLatex(text)).toBeFalsy()
|
|
41
|
+
})
|
|
42
|
+
test('can parse latex string', () => {
|
|
43
|
+
expect(MathHelper.parseLatexString('$$' + latexContent + '$$')).toEqual(
|
|
44
|
+
latexContent
|
|
45
|
+
)
|
|
46
|
+
})
|
|
47
|
+
test('return empty string when latex not found', () => {
|
|
48
|
+
expect(MathHelper.parseLatexString(text)).toEqual('')
|
|
49
|
+
})
|
|
50
|
+
test('can parse SR enhanced latex string', () => {
|
|
51
|
+
expect(MathHelper.parseSREnhancedLatexString(srEnhancedlatex)).toEqual({
|
|
52
|
+
latex: latexContent,
|
|
53
|
+
sr_text: ' half plus three quarter ',
|
|
54
|
+
})
|
|
55
|
+
})
|
|
56
|
+
test('return empty object when SR enhanced latex string not found', () => {
|
|
57
|
+
expect(MathHelper.parseSREnhancedLatexString(text)).toEqual({})
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
test('can convert MathML to latex', () => {
|
|
61
|
+
expect(MathHelper.convertMathMLToLatex(mathML)).toEqual(latexContent)
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
test('can convert content MathML to latex', () => {
|
|
65
|
+
expect(MathHelper.convertContentMathMLtoLatex(mathML + text)).toEqual(
|
|
66
|
+
'$$' + latexContent + '$$' + text
|
|
67
|
+
)
|
|
68
|
+
})
|
|
69
|
+
test('can convert text latex to HTML', () => {
|
|
70
|
+
expect(
|
|
71
|
+
MathHelper.convertContentLatexToHtml(
|
|
72
|
+
'$$' + latexContent + '$$' + text
|
|
73
|
+
)
|
|
74
|
+
).toEqual(mathliveHtml + text)
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
test('can convert SR enhanced latex to HTML', () => {
|
|
78
|
+
expect(
|
|
79
|
+
MathHelper.convertContentSREnhancedLatexToHtml(
|
|
80
|
+
srEnhancedlatex + text
|
|
81
|
+
)
|
|
82
|
+
).toEqual(mathliveHtml + text)
|
|
83
|
+
})
|
|
84
|
+
test('can convert all math markup to HTML', () => {
|
|
85
|
+
expect(
|
|
86
|
+
MathHelper.convertMathContentToHtml(
|
|
87
|
+
'$$' +
|
|
88
|
+
latexContent +
|
|
89
|
+
'$$' +
|
|
90
|
+
text +
|
|
91
|
+
mathML +
|
|
92
|
+
text +
|
|
93
|
+
srEnhancedlatex
|
|
94
|
+
)
|
|
95
|
+
).toEqual(mathliveHtml + text + mathliveHtml + text + mathliveHtml)
|
|
96
|
+
})
|
|
97
|
+
|
|
98
|
+
test('detects content wrapped for tinymce', () => {
|
|
99
|
+
expect(
|
|
100
|
+
MathHelper.checkMathContentWrappedForTMCE(
|
|
101
|
+
`<span class ='windward-math-content'>` +
|
|
102
|
+
srEnhancedlatex +
|
|
103
|
+
`</span>` +
|
|
104
|
+
text +
|
|
105
|
+
latexContent
|
|
106
|
+
)
|
|
107
|
+
).toBeTruthy()
|
|
108
|
+
})
|
|
109
|
+
|
|
110
|
+
test('can wrap srEnhancedlatex in math span for tinymce', () => {
|
|
111
|
+
expect(MathHelper.wrapMathContentForTinyMCE(srEnhancedlatex)).toEqual(
|
|
112
|
+
`<span class ='windward-math-content'>` +
|
|
113
|
+
srEnhancedlatex +
|
|
114
|
+
`</span>`
|
|
115
|
+
)
|
|
116
|
+
})
|
|
117
|
+
test('can wrap latexContent in math span for tinymce', () => {
|
|
118
|
+
expect(
|
|
119
|
+
MathHelper.wrapMathContentForTinyMCE('$$' + latexContent + '$$')
|
|
120
|
+
).toEqual(
|
|
121
|
+
`<span class ='windward-math-content'>` +
|
|
122
|
+
'$$' +
|
|
123
|
+
latexContent +
|
|
124
|
+
'$$' +
|
|
125
|
+
`</span>`
|
|
126
|
+
)
|
|
127
|
+
})
|
|
128
|
+
})
|
package/test/mocks.js
ADDED
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import Vue from 'vue'
|
|
2
|
+
require('./__mocks__/modelMock')
|
|
3
|
+
require('./__mocks__/componentsMock')
|
|
4
|
+
require('./__mocks__/contentBlockMock')
|
|
5
|
+
require('./__mocks__/contentSettingsMock')
|
|
6
|
+
require('./__mocks__/helpersMock')
|
|
7
|
+
|
|
8
|
+
// Define filter mocks
|
|
9
|
+
Vue.filter('isoDate', (data) => data)
|
|
10
|
+
Vue.filter('humanDateTime', (data) => data)
|
|
11
|
+
Vue.filter('humanDate', (data) => data)
|
|
12
|
+
Vue.filter('humanTime', (data) => data)
|
|
13
|
+
Vue.filter('humanDuration', (data) => data)
|
|
14
|
+
Vue.filter('humanShortDuration', (data) => data)
|
|
15
|
+
Vue.filter('humanFilesize', (data) => data)
|
|
16
|
+
Vue.filter('formatNumber', (data) => data)
|
|
17
|
+
Vue.filter('shortUuid', (data) => data)
|
|
18
|
+
|
|
19
|
+
jest.mock(
|
|
20
|
+
'vuex',
|
|
21
|
+
() => {
|
|
22
|
+
return {
|
|
23
|
+
__esModule: true,
|
|
24
|
+
mapGetters(obj) {
|
|
25
|
+
const getters = {}
|
|
26
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
27
|
+
// Return a function getter since mapGetters only work in the computed section
|
|
28
|
+
getters[key] = () => {
|
|
29
|
+
return {
|
|
30
|
+
id: '00000000-0000-0000-0000-000000000000',
|
|
31
|
+
metadata: {},
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
// Just return an empty object
|
|
36
|
+
return getters
|
|
37
|
+
},
|
|
38
|
+
mapMutations(obj) {
|
|
39
|
+
const getters = {}
|
|
40
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
41
|
+
// Return a function getter since mapGetters only work in the computed section
|
|
42
|
+
getters[key] = () => {
|
|
43
|
+
return { id: '00000000-0000-0000-0000-000000000000' }
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
// Just return an empty object
|
|
47
|
+
return getters
|
|
48
|
+
},
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
{ virtual: true }
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
const mocks = {
|
|
55
|
+
window: {},
|
|
56
|
+
app: {},
|
|
57
|
+
$router: [],
|
|
58
|
+
$PluginService: { mounted: () => {} },
|
|
59
|
+
$Grading: {
|
|
60
|
+
getTypes() {
|
|
61
|
+
return [
|
|
62
|
+
{
|
|
63
|
+
id: '00000000-0000-0000-0000-000000000000',
|
|
64
|
+
name: 'numeric',
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
id: '00000000-0000-0000-0000-000000000001',
|
|
68
|
+
name: 'pass_fail',
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
id: '00000000-0000-0000-0000-000000000002',
|
|
72
|
+
name: 'text',
|
|
73
|
+
},
|
|
74
|
+
]
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
$eb: {
|
|
78
|
+
$emit: () => {},
|
|
79
|
+
},
|
|
80
|
+
$nuxt: {
|
|
81
|
+
$auth: {
|
|
82
|
+
user: {
|
|
83
|
+
email: 'email',
|
|
84
|
+
first_name: 'first_name',
|
|
85
|
+
last_name: 'last_name',
|
|
86
|
+
},
|
|
87
|
+
},
|
|
88
|
+
$store: {},
|
|
89
|
+
$on: () => {},
|
|
90
|
+
$emit: () => {},
|
|
91
|
+
},
|
|
92
|
+
$i18n: { locales: [] },
|
|
93
|
+
$t: (msg) => msg,
|
|
94
|
+
$tc: (msg, num) => msg + ' ' + num,
|
|
95
|
+
$store: {
|
|
96
|
+
state: {
|
|
97
|
+
tour: {
|
|
98
|
+
enabled: false,
|
|
99
|
+
},
|
|
100
|
+
organization: { id: 'test-id' },
|
|
101
|
+
course: { id: 'test-id' },
|
|
102
|
+
content: { id: 'test-id' },
|
|
103
|
+
userMenuItems: {},
|
|
104
|
+
role: {},
|
|
105
|
+
permissions: {},
|
|
106
|
+
context: '',
|
|
107
|
+
},
|
|
108
|
+
getters: {
|
|
109
|
+
'content/get': { id: '00000000-0000-0000-0000-000000000000' },
|
|
110
|
+
'context/get': { id: '00000000-0000-0000-0000-000000000000' },
|
|
111
|
+
'course/get': { id: '00000000-0000-0000-0000-000000000000' },
|
|
112
|
+
'organization/get': { id: '00000000-0000-0000-0000-000000000000' },
|
|
113
|
+
'permissions/get': { id: '00000000-0000-0000-0000-000000000000' },
|
|
114
|
+
'role/get': { id: '00000000-0000-0000-0000-000000000000' },
|
|
115
|
+
'userMenu/get': { id: '00000000-0000-0000-0000-000000000000' },
|
|
116
|
+
},
|
|
117
|
+
commit: () => {},
|
|
118
|
+
dispatch: () => {},
|
|
119
|
+
},
|
|
120
|
+
$tours: {
|
|
121
|
+
windwardTour: {
|
|
122
|
+
start: () => {},
|
|
123
|
+
},
|
|
124
|
+
},
|
|
125
|
+
$toast: {
|
|
126
|
+
info: () => {},
|
|
127
|
+
success: () => {},
|
|
128
|
+
show: () => {},
|
|
129
|
+
error: () => {},
|
|
130
|
+
},
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Copy some things around
|
|
134
|
+
mocks.$auth = mocks.$nuxt.$auth
|
|
135
|
+
mocks.$nuxt.$store = mocks.$store
|
|
136
|
+
mocks.window.$nuxt = mocks.$nuxt
|
|
137
|
+
mocks.app.store = mocks.$store
|
|
138
|
+
mocks.app.$eb = mocks.$eb
|
|
139
|
+
|
|
140
|
+
export const defaultMocks = mocks
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "es5",
|
|
4
|
+
"module": "commonjs",
|
|
5
|
+
"declaration": true,
|
|
6
|
+
"noImplicitAny": false,
|
|
7
|
+
"outDir": "./lib",
|
|
8
|
+
"strict": true,
|
|
9
|
+
"paths": {
|
|
10
|
+
"~/*": ["./*"],
|
|
11
|
+
"@/*": ["./*"]
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"exclude": ["node_modules", "**/__tests__/*"]
|
|
15
|
+
}
|
package/utils/index.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import MathHelper from '../helpers/MathHelper'
|
|
2
|
+
import ContentViewer from '../components/utils/ContentViewer.vue'
|
|
3
|
+
|
|
4
|
+
import MathLiveWrapper from '../components/utils/MathLiveWrapper.vue'
|
|
5
|
+
import TinyMCEWrapper from '../components/utils/TinyMCEWrapper.vue'
|
|
6
|
+
|
|
7
|
+
import MathExpressionEditor from '../components/utils/MathExpressionEditor'
|
|
8
|
+
import CourseGlossary from '../components/utils/glossary/CourseGlossary.vue'
|
|
9
|
+
import CourseGlossaryForm from '../components/utils/glossary/CourseGlossaryForm.vue'
|
|
10
|
+
export {
|
|
11
|
+
MathHelper,
|
|
12
|
+
ContentViewer,
|
|
13
|
+
MathExpressionEditor,
|
|
14
|
+
MathLiveWrapper,
|
|
15
|
+
TinyMCEWrapper,
|
|
16
|
+
CourseGlossary,
|
|
17
|
+
CourseGlossaryForm,
|
|
18
|
+
}
|