@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.
Files changed (113) hide show
  1. package/.editorconfig +13 -0
  2. package/.eslintrc.js +11 -0
  3. package/.prettierrc +4 -0
  4. package/README.md +86 -0
  5. package/babel.config.js +1 -0
  6. package/components/Content/Blocks/Accordion.vue +133 -0
  7. package/components/Content/Blocks/ClickableIcons.vue +98 -0
  8. package/components/Content/Blocks/Feedback.vue +323 -0
  9. package/components/Content/Blocks/FeedbackTemplates/FeedbackQuestionLikert.vue +95 -0
  10. package/components/Content/Blocks/FeedbackTemplates/FeedbackQuestionOpenResponse.vue +45 -0
  11. package/components/Content/Blocks/FeedbackTemplates/FeedbackQuestionTrueFalse.vue +55 -0
  12. package/components/Content/Blocks/Image.vue +116 -0
  13. package/components/Content/Blocks/Math.vue +87 -0
  14. package/components/Content/Blocks/Tab.vue +89 -0
  15. package/components/Content/Blocks/Table.vue +68 -0
  16. package/components/Content/Blocks/UserUpload/DisplayUserFilesTable.vue +157 -0
  17. package/components/Content/Blocks/UserUpload/ManageDataTableUserFiles.vue +68 -0
  18. package/components/Content/Blocks/UserUpload.vue +240 -0
  19. package/components/Content/Blocks/Video.vue +245 -0
  20. package/components/Navigation/Items/CourseGlossaryToolNav.vue +39 -0
  21. package/components/Navigation/Items/GlossaryNav.vue +32 -0
  22. package/components/Navigation/Items/UserUploadNav.vue +35 -0
  23. package/components/Settings/AccordionSettings.vue +153 -0
  24. package/components/Settings/ClickableIconsSettings.vue +189 -0
  25. package/components/Settings/FeedbackSettings.vue +92 -0
  26. package/components/Settings/ImageSettings.vue +124 -0
  27. package/components/Settings/MathSettings.vue +81 -0
  28. package/components/Settings/TabSettings.vue +139 -0
  29. package/components/Settings/TextEditorSettings.vue +249 -0
  30. package/components/Settings/UserUploadSettings.vue +151 -0
  31. package/components/Settings/VideoSettings.vue +699 -0
  32. package/components/utils/ContentViewer.vue +69 -0
  33. package/components/utils/MathExpressionEditor.vue +294 -0
  34. package/components/utils/MathLiveWrapper.vue +86 -0
  35. package/components/utils/TinyMCEWrapper.vue +103 -0
  36. package/components/utils/glossary/CourseGlossary.vue +284 -0
  37. package/components/utils/glossary/CourseGlossaryForm.vue +106 -0
  38. package/components/utils/glossary/GlossaryToolTip.vue +87 -0
  39. package/config/tinymce.config.js +136 -0
  40. package/helpers/GlossaryHelper.ts +137 -0
  41. package/helpers/GlossaryTerm.ts +31 -0
  42. package/helpers/MathHelper.ts +236 -0
  43. package/helpers/tinymce/plugin.ts +83 -0
  44. package/i18n/en-US/components/content/blocks/feedback.ts +28 -0
  45. package/i18n/en-US/components/content/blocks/image.ts +5 -0
  46. package/i18n/en-US/components/content/blocks/index.ts +15 -0
  47. package/i18n/en-US/components/content/blocks/tab.ts +4 -0
  48. package/i18n/en-US/components/content/blocks/table.ts +4 -0
  49. package/i18n/en-US/components/content/blocks/user_upload.ts +12 -0
  50. package/i18n/en-US/components/content/blocks/video.ts +53 -0
  51. package/i18n/en-US/components/content/index.ts +5 -0
  52. package/i18n/en-US/components/index.ts +12 -0
  53. package/i18n/en-US/components/navigation/image.ts +4 -0
  54. package/i18n/en-US/components/navigation/index.ts +7 -0
  55. package/i18n/en-US/components/navigation/user_upload.ts +3 -0
  56. package/i18n/en-US/components/settings/clickable_icon.ts +9 -0
  57. package/i18n/en-US/components/settings/image.ts +1 -0
  58. package/i18n/en-US/components/settings/index.ts +13 -0
  59. package/i18n/en-US/components/settings/text_editor.ts +7 -0
  60. package/i18n/en-US/components/settings/user_upload.ts +11 -0
  61. package/i18n/en-US/components/settings/video.ts +13 -0
  62. package/i18n/en-US/components/utils/index.ts +5 -0
  63. package/i18n/en-US/components/utils/tiny_mce_wrapper.ts +7 -0
  64. package/i18n/en-US/index.ts +16 -0
  65. package/i18n/en-US/modules/index.ts +5 -0
  66. package/i18n/en-US/pages/glossary.ts +7 -0
  67. package/i18n/en-US/pages/index.ts +7 -0
  68. package/i18n/en-US/pages/user_upload.ts +3 -0
  69. package/i18n/en-US/shared/content_blocks.ts +20 -0
  70. package/i18n/en-US/shared/index.ts +11 -0
  71. package/i18n/en-US/shared/menu.ts +3 -0
  72. package/i18n/en-US/shared/permission.ts +15 -0
  73. package/i18n/en-US/shared/settings.ts +15 -0
  74. package/index.js +15 -0
  75. package/jest.config.js +15 -0
  76. package/models/SurveyResult.ts +8 -0
  77. package/models/SurveyTemplate.ts +8 -0
  78. package/models/UserFileAsset.ts +12 -0
  79. package/package.json +48 -0
  80. package/pages/glossary.vue +31 -0
  81. package/pages/userUpload.vue +204 -0
  82. package/plugin.js +299 -0
  83. package/test/Components/Content/Blocks/Accordion.spec.js +21 -0
  84. package/test/Components/Content/Blocks/ClickableIcons.spec.js +21 -0
  85. package/test/Components/Content/Blocks/Feedback.spec.js +31 -0
  86. package/test/Components/Content/Blocks/FeedbackTemplates/FeedbackQuestionLikert.spec.js +23 -0
  87. package/test/Components/Content/Blocks/FeedbackTemplates/FeedbackQuestionOpenResponse.spec.js +23 -0
  88. package/test/Components/Content/Blocks/FeedbackTemplates/FeedbackQuestionTrueFalse.spec.js +23 -0
  89. package/test/Components/Content/Blocks/Image.spec.js +34 -0
  90. package/test/Components/Content/Blocks/Math.spec.js +34 -0
  91. package/test/Components/Content/Blocks/Tab.spec.js +20 -0
  92. package/test/Components/Content/Blocks/Table.spec.js +21 -0
  93. package/test/Components/Content/Blocks/UserUpload.spec.js +25 -0
  94. package/test/Components/Content/Blocks/Video.spec.js +112 -0
  95. package/test/Components/Settings/AccordionSettings.spec.js +45 -0
  96. package/test/Components/Settings/ClickableIconsSettings.spec.js +20 -0
  97. package/test/Components/Settings/FeedbackSettings.spec.js +20 -0
  98. package/test/Components/Settings/ImageSettings.spec.js +20 -0
  99. package/test/Components/Settings/MathSettings.spec.js +20 -0
  100. package/test/Components/Settings/TabSettings.spec.js +45 -0
  101. package/test/Components/Settings/UserUploadSettings.spec.js +20 -0
  102. package/test/__mocks__/componentsMock.js +83 -0
  103. package/test/__mocks__/contentBlockMock.js +119 -0
  104. package/test/__mocks__/contentSettingsMock.js +54 -0
  105. package/test/__mocks__/fileMock.js +1 -0
  106. package/test/__mocks__/helpersMock.js +53 -0
  107. package/test/__mocks__/modelMock.js +82 -0
  108. package/test/__mocks__/styleMock.js +1 -0
  109. package/test/helpers/GlossaryHelper.spec.js +227 -0
  110. package/test/helpers/MathHelper.spec.js +128 -0
  111. package/test/mocks.js +140 -0
  112. package/tsconfig.json +15 -0
  113. 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
+ }