@khanacademy/perseus 0.0.0-PR418-20230323233458

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 (449) hide show
  1. package/.babelrc.js +8 -0
  2. package/.eslintrc.js +11 -0
  3. package/CHANGELOG.md +367 -0
  4. package/README.md +5 -0
  5. package/dist/es/index.css +4071 -0
  6. package/dist/es/index.js +51108 -0
  7. package/dist/es/index.js.map +1 -0
  8. package/dist/index.css +4071 -0
  9. package/dist/index.d.ts +2 -0
  10. package/dist/index.js +51027 -0
  11. package/dist/index.js.flow +2 -0
  12. package/dist/index.js.map +1 -0
  13. package/dist/strings.js +150 -0
  14. package/package.json +95 -0
  15. package/src/__stories__/hints-renderer.stories.jsx +60 -0
  16. package/src/__stories__/item-renderer.stories.jsx +27 -0
  17. package/src/__stories__/server-item-renderer.stories.jsx +27 -0
  18. package/src/__testdata__/graphie_testdata.js +53 -0
  19. package/src/__testdata__/item-renderer_testdata.js +161 -0
  20. package/src/__testdata__/renderer_testdata.js +123 -0
  21. package/src/__tests__/__snapshots__/item-renderer_test.jsx.snap +232 -0
  22. package/src/__tests__/__snapshots__/perseus-markdown_test.jsx.snap +507 -0
  23. package/src/__tests__/__snapshots__/renderer_test.jsx.snap +585 -0
  24. package/src/__tests__/__snapshots__/server-item-renderer_test.jsx.snap +305 -0
  25. package/src/__tests__/a11y_test.jsx +304 -0
  26. package/src/__tests__/item-renderer_test.jsx +892 -0
  27. package/src/__tests__/jipt-paragraphs_test.js +34 -0
  28. package/src/__tests__/mock-asset-loading-widget.jsx +57 -0
  29. package/src/__tests__/mock-widget.jsx +35 -0
  30. package/src/__tests__/perseus-markdown_test.jsx +747 -0
  31. package/src/__tests__/renderability_test.jsx +481 -0
  32. package/src/__tests__/renderer-api_test.jsx +309 -0
  33. package/src/__tests__/renderer_test.jsx +1792 -0
  34. package/src/__tests__/server-item-renderer_test.jsx +516 -0
  35. package/src/__tests__/test-items/image-item.js +25 -0
  36. package/src/__tests__/test-items/input-number-1-item.js +27 -0
  37. package/src/__tests__/test-items/input-number-2-item.js +39 -0
  38. package/src/__tests__/test-items/table-item.js +30 -0
  39. package/src/__tests__/tex-wrangler_test.js +83 -0
  40. package/src/__tests__/util_test.js +41 -0
  41. package/src/a11y.js +46 -0
  42. package/src/all-widgets.js +9 -0
  43. package/src/article-renderer.jsx +214 -0
  44. package/src/asset-context.js +30 -0
  45. package/src/basic-widgets.js +13 -0
  46. package/src/components/__stories__/button-group.stories.jsx +61 -0
  47. package/src/components/__stories__/fixed-to-responsive.stories.jsx +132 -0
  48. package/src/components/__stories__/graph.stories.jsx +27 -0
  49. package/src/components/__stories__/graphie.stories.jsx +32 -0
  50. package/src/components/__stories__/hud.stories.jsx +36 -0
  51. package/src/components/__stories__/icon.stories.jsx +56 -0
  52. package/src/components/__stories__/image-loader.stories.jsx +60 -0
  53. package/src/components/__stories__/info-tip.stories.jsx +33 -0
  54. package/src/components/__stories__/inline-icon.stories.jsx +39 -0
  55. package/src/components/__stories__/input-with-examples.stories.jsx +52 -0
  56. package/src/components/__stories__/lint.stories.jsx +40 -0
  57. package/src/components/__stories__/math-input.stories.jsx +29 -0
  58. package/src/components/__stories__/math-output.stories.jsx +24 -0
  59. package/src/components/__stories__/multi-button-group.stories.jsx +69 -0
  60. package/src/components/__stories__/number-input.stories.jsx +42 -0
  61. package/src/components/__stories__/prop-check-box.stories.jsx +62 -0
  62. package/src/components/__stories__/range-input.stories.jsx +28 -0
  63. package/src/components/__stories__/simple-keypad-input.stories.jsx +28 -0
  64. package/src/components/__stories__/sortable.stories.jsx +73 -0
  65. package/src/components/__stories__/stub-tag-editor.stories.jsx +44 -0
  66. package/src/components/__stories__/svg-image.stories.jsx +71 -0
  67. package/src/components/__stories__/tex.stories.jsx +24 -0
  68. package/src/components/__stories__/text-buttons.stories.jsx +51 -0
  69. package/src/components/__stories__/text-input.stories.jsx +34 -0
  70. package/src/components/__stories__/text-list-editor.stories.jsx +33 -0
  71. package/src/components/__stories__/zoomable-tex.stories.jsx +39 -0
  72. package/src/components/__stories__/zoomable.stories.jsx +40 -0
  73. package/src/components/__testdata__/sorter_testdata.js +20 -0
  74. package/src/components/__tests__/__snapshots__/sortable_test.jsx.snap +79 -0
  75. package/src/components/__tests__/__snapshots__/sorter_test.jsx.snap +297 -0
  76. package/src/components/__tests__/number-input_test.jsx +112 -0
  77. package/src/components/__tests__/sortable_test.jsx +234 -0
  78. package/src/components/__tests__/sorter_test.jsx +111 -0
  79. package/src/components/__tests__/zoomable_test.jsx +388 -0
  80. package/src/components/button-group.jsx +131 -0
  81. package/src/components/constants.js +15 -0
  82. package/src/components/fixed-to-responsive.jsx +165 -0
  83. package/src/components/graph.jsx +413 -0
  84. package/src/components/graphie-classes.jsx +148 -0
  85. package/src/components/graphie-movables.jsx +342 -0
  86. package/src/components/graphie.jsx +353 -0
  87. package/src/components/highlighting/highlightable-content.jsx +245 -0
  88. package/src/components/highlighting/highlights.js +208 -0
  89. package/src/components/highlighting/ranges.js +299 -0
  90. package/src/components/highlighting/types.js +73 -0
  91. package/src/components/highlighting/ui/highlight-renderer.jsx +179 -0
  92. package/src/components/highlighting/ui/highlight-set-renderer.jsx +190 -0
  93. package/src/components/highlighting/ui/highlight-tooltip.jsx +143 -0
  94. package/src/components/highlighting/ui/highlighting-ui.jsx +163 -0
  95. package/src/components/highlighting/ui/selection-tracker.jsx +184 -0
  96. package/src/components/highlighting/ui/types.js +21 -0
  97. package/src/components/highlighting/ui/util.js +166 -0
  98. package/src/components/highlighting/word-indexer.jsx +155 -0
  99. package/src/components/hud.jsx +179 -0
  100. package/src/components/icon.jsx +149 -0
  101. package/src/components/image-loader.jsx +175 -0
  102. package/src/components/info-tip/info-tip.jsx +146 -0
  103. package/src/components/info-tip.jsx +38 -0
  104. package/src/components/inline-icon.jsx +81 -0
  105. package/src/components/input-with-examples.jsx +248 -0
  106. package/src/components/lint.jsx +455 -0
  107. package/src/components/math-input.jsx +293 -0
  108. package/src/components/math-output.jsx +128 -0
  109. package/src/components/multi-button-group.jsx +137 -0
  110. package/src/components/number-input.jsx +253 -0
  111. package/src/components/prop-check-box.jsx +80 -0
  112. package/src/components/range-input.jsx +61 -0
  113. package/src/components/simple-keypad-input.jsx +88 -0
  114. package/src/components/sortable.jsx +872 -0
  115. package/src/components/stub-tag-editor.jsx +54 -0
  116. package/src/components/svg-image.jsx +765 -0
  117. package/src/components/tex-buttons.jsx +320 -0
  118. package/src/components/tex.jsx +70 -0
  119. package/src/components/text-input.jsx +133 -0
  120. package/src/components/text-list-editor.jsx +157 -0
  121. package/src/components/tooltip.jsx +392 -0
  122. package/src/components/visibility-observer/__stories__/visibility-observer.stories.jsx +140 -0
  123. package/src/components/visibility-observer/get-scroll-parent.js +45 -0
  124. package/src/components/visibility-observer/visibility-observer.js +109 -0
  125. package/src/components/visibility-observer/visibility-observer.jsx +123 -0
  126. package/src/components/zoomable-tex.jsx +70 -0
  127. package/src/components/zoomable.jsx +294 -0
  128. package/src/components.js +17 -0
  129. package/src/definition-context.js +59 -0
  130. package/src/dependencies.js +22 -0
  131. package/src/error-boundary.jsx +48 -0
  132. package/src/extra-widgets.js +75 -0
  133. package/src/hint-renderer.jsx +155 -0
  134. package/src/hints-renderer.jsx +367 -0
  135. package/src/icon-paths.js +120 -0
  136. package/src/index.js +159 -0
  137. package/src/init.js +49 -0
  138. package/src/interaction-tracker.js +60 -0
  139. package/src/interactive2/__tests__/movable-mock.js +48 -0
  140. package/src/interactive2/__tests__/movable-point_test.js +146 -0
  141. package/src/interactive2/interactive-util.js +152 -0
  142. package/src/interactive2/movable-helper-methods.js +160 -0
  143. package/src/interactive2/movable-line-options.js +392 -0
  144. package/src/interactive2/movable-line.js +322 -0
  145. package/src/interactive2/movable-point-options.js +190 -0
  146. package/src/interactive2/movable-point.jsx +597 -0
  147. package/src/interactive2/movable-polygon-options.js +351 -0
  148. package/src/interactive2/movable-polygon.js +368 -0
  149. package/src/interactive2/movable.js +273 -0
  150. package/src/interactive2/objective_.js +79 -0
  151. package/src/interactive2/types.js +3 -0
  152. package/src/interactive2/wrapped-defaults.js +86 -0
  153. package/src/interactive2/wrapped-ellipse.js +86 -0
  154. package/src/interactive2/wrapped-line.js +109 -0
  155. package/src/interactive2/wrapped-path.js +47 -0
  156. package/src/interactive2.js +91 -0
  157. package/src/item-renderer.jsx +514 -0
  158. package/src/item-version.js +12 -0
  159. package/src/jipt-paragraphs.js +52 -0
  160. package/src/jquery.mobile.vmouse.js +546 -0
  161. package/src/keypad-context.js +33 -0
  162. package/src/loading-context.js +20 -0
  163. package/src/logging/log.js +107 -0
  164. package/src/logging/log_test.js +41 -0
  165. package/src/mixins/__tests__/widget-prop-denylist_test.js +23 -0
  166. package/src/mixins/changeable.jsx +115 -0
  167. package/src/mixins/editor-jsonify.jsx +13 -0
  168. package/src/mixins/provide-keypad.jsx +131 -0
  169. package/src/mixins/widget-jsonify-deprecated.jsx +23 -0
  170. package/src/mixins/widget-prop-denylist.js +49 -0
  171. package/src/multi-items/__stories__/multi-renderer.stories.js +88 -0
  172. package/src/multi-items/__testdata__/multi-renderer_testdata.js +174 -0
  173. package/src/multi-items/__tests__/__snapshots__/multi-renderer_test.js.snap +1220 -0
  174. package/src/multi-items/__tests__/items_test.js +245 -0
  175. package/src/multi-items/__tests__/multi-renderer_test.js +903 -0
  176. package/src/multi-items/__tests__/prop-type-builders_test.js +134 -0
  177. package/src/multi-items/__tests__/shapes_test.js +44 -0
  178. package/src/multi-items/__tests__/trees_test.js +316 -0
  179. package/src/multi-items/item-types.js +55 -0
  180. package/src/multi-items/items.js +176 -0
  181. package/src/multi-items/multi-renderer.jsx +543 -0
  182. package/src/multi-items/prop-type-builders.js +105 -0
  183. package/src/multi-items/shape-types.js +47 -0
  184. package/src/multi-items/shapes.js +53 -0
  185. package/src/multi-items/tree-types.js +42 -0
  186. package/src/multi-items/trees.js +345 -0
  187. package/src/multi-items.js +76 -0
  188. package/src/perseus-api.jsx +221 -0
  189. package/src/perseus-env.js +26 -0
  190. package/src/perseus-error.js +19 -0
  191. package/src/perseus-markdown.jsx +408 -0
  192. package/src/perseus-types.js +1475 -0
  193. package/src/question-paragraph.jsx +30 -0
  194. package/src/renderability.jsx +108 -0
  195. package/src/renderer.jsx +1937 -0
  196. package/src/server-item-renderer.jsx +467 -0
  197. package/src/sigfigs.jsx +492 -0
  198. package/src/styles/articles.less +747 -0
  199. package/src/styles/constants.js +96 -0
  200. package/src/styles/global-constants.js +344 -0
  201. package/src/styles/global-styles.js +307 -0
  202. package/src/styles/khan-exercise.css +1280 -0
  203. package/src/styles/media-queries.js +48 -0
  204. package/src/styles/perseus-renderer.less +742 -0
  205. package/src/styles/shared.js +132 -0
  206. package/src/styles/util.less +47 -0
  207. package/src/styles/variables.boostrap.less +301 -0
  208. package/src/styles/variables.less +55 -0
  209. package/src/styles/variables.shared.less +346 -0
  210. package/src/styles/widgets/categorizer.less +39 -0
  211. package/src/styles/widgets/dropdown.less +3 -0
  212. package/src/styles/widgets/expression.less +145 -0
  213. package/src/styles/widgets/graded-group.less +36 -0
  214. package/src/styles/widgets/grapher.less +11 -0
  215. package/src/styles/widgets/group.less +19 -0
  216. package/src/styles/widgets/image.less +29 -0
  217. package/src/styles/widgets/interactive-graph.less +61 -0
  218. package/src/styles/widgets/label-image.less +41 -0
  219. package/src/styles/widgets/matcher.less +8 -0
  220. package/src/styles/widgets/matrix.less +122 -0
  221. package/src/styles/widgets/measurer.less +20 -0
  222. package/src/styles/widgets/orderer.less +97 -0
  223. package/src/styles/widgets/passage.less +160 -0
  224. package/src/styles/widgets/plotter.less +29 -0
  225. package/src/styles/widgets/radio.less +56 -0
  226. package/src/styles/widgets/reaction-diagram.less +35 -0
  227. package/src/styles/widgets/sequence.less +5 -0
  228. package/src/styles/widgets/sortable.less +113 -0
  229. package/src/styles/widgets/table.less +56 -0
  230. package/src/styles/widgets/transformer.less +28 -0
  231. package/src/styles/widgets/unit.less +14 -0
  232. package/src/tex-wrangler.js +176 -0
  233. package/src/translation-linter.js +125 -0
  234. package/src/traversal.jsx +141 -0
  235. package/src/types.js +500 -0
  236. package/src/util/a11y.js +24 -0
  237. package/src/util/answer-types.js +861 -0
  238. package/src/util/answer-types_test.js +115 -0
  239. package/src/util/colors.js +94 -0
  240. package/src/util/fix-passage-refs.jsx +122 -0
  241. package/src/util/graph-utils.js +16 -0
  242. package/src/util/graphie.js +1586 -0
  243. package/src/util/interactive.js +4078 -0
  244. package/src/util/katex-preprocess.js +16 -0
  245. package/src/util/math.js +289 -0
  246. package/src/util/mobile-native-utils.js +14 -0
  247. package/src/util/mobile-native-utils_test.js +24 -0
  248. package/src/util/react-render.js +27 -0
  249. package/src/util/register-all-widgets-for-testing.js +12 -0
  250. package/src/util/scroll-utils.js +162 -0
  251. package/src/util/sizing-utils.js +46 -0
  252. package/src/util/tex.js +203 -0
  253. package/src/util/url-utils.js +36 -0
  254. package/src/util.js +984 -0
  255. package/src/version.js +11 -0
  256. package/src/widget-container.jsx +179 -0
  257. package/src/widgets/__stories__/categorizer.stories.jsx +15 -0
  258. package/src/widgets/__stories__/cs-program.stories.jsx +15 -0
  259. package/src/widgets/__stories__/definition.stories.jsx +108 -0
  260. package/src/widgets/__stories__/dropdown.stories.jsx +15 -0
  261. package/src/widgets/__stories__/explanation.stories.jsx +19 -0
  262. package/src/widgets/__stories__/expression.stories.jsx +26 -0
  263. package/src/widgets/__stories__/graded-group-set.stories.jsx +32 -0
  264. package/src/widgets/__stories__/graded-group.stories.jsx +32 -0
  265. package/src/widgets/__stories__/grapher.stories.jsx +47 -0
  266. package/src/widgets/__stories__/group.stories.jsx +15 -0
  267. package/src/widgets/__stories__/iframe.stories.jsx +15 -0
  268. package/src/widgets/__stories__/image.stories.jsx +62 -0
  269. package/src/widgets/__stories__/input-number.stories.jsx +102 -0
  270. package/src/widgets/__stories__/interaction.stories.jsx +15 -0
  271. package/src/widgets/__stories__/interactive-graph.stories.jsx +61 -0
  272. package/src/widgets/__stories__/matcher.stories.jsx +15 -0
  273. package/src/widgets/__stories__/matrix.stories.jsx +15 -0
  274. package/src/widgets/__stories__/number-line.stories.jsx +15 -0
  275. package/src/widgets/__stories__/numeric-input.stories.jsx +106 -0
  276. package/src/widgets/__stories__/on-screen-keypad.stories.jsx +118 -0
  277. package/src/widgets/__stories__/orderer.stories.jsx +15 -0
  278. package/src/widgets/__stories__/passage-ref.stories.jsx +23 -0
  279. package/src/widgets/__stories__/passage.stories.jsx +27 -0
  280. package/src/widgets/__stories__/radio.stories.jsx +90 -0
  281. package/src/widgets/__stories__/sorter.stories.jsx +15 -0
  282. package/src/widgets/__stories__/transformer.stories.jsx +15 -0
  283. package/src/widgets/__stories__/unit.stories.jsx +45 -0
  284. package/src/widgets/__stories__/video-transcript-link.stories.jsx +24 -0
  285. package/src/widgets/__stories__/video.stories.jsx +15 -0
  286. package/src/widgets/__testdata__/base-radio_testdata.js +22 -0
  287. package/src/widgets/__testdata__/categorizer_testdata.js +50 -0
  288. package/src/widgets/__testdata__/cs-program_testdata.js +28 -0
  289. package/src/widgets/__testdata__/dropdown_testdata.js +34 -0
  290. package/src/widgets/__testdata__/explanation_testdata.js +56 -0
  291. package/src/widgets/__testdata__/expression_testdata.js +95 -0
  292. package/src/widgets/__testdata__/graded-group-set_testdata.js +358 -0
  293. package/src/widgets/__testdata__/graded-group_testdata.js +79 -0
  294. package/src/widgets/__testdata__/grapher_testdata.js +376 -0
  295. package/src/widgets/__testdata__/group_testdata.js +156 -0
  296. package/src/widgets/__testdata__/iframe_testdata.js +36 -0
  297. package/src/widgets/__testdata__/image_testdata.js +38 -0
  298. package/src/widgets/__testdata__/input-number_testdata.js +73 -0
  299. package/src/widgets/__testdata__/interaction_testdata.js +264 -0
  300. package/src/widgets/__testdata__/interactive-graph_testdata.js +646 -0
  301. package/src/widgets/__testdata__/matcher_testdata.js +34 -0
  302. package/src/widgets/__testdata__/matrix_testdata.js +29 -0
  303. package/src/widgets/__testdata__/number-line_testdata.js +31 -0
  304. package/src/widgets/__testdata__/numeric-input_testdata.js +266 -0
  305. package/src/widgets/__testdata__/orderer_testdata.js +58 -0
  306. package/src/widgets/__testdata__/passage-ref_testdata.js +103 -0
  307. package/src/widgets/__testdata__/passage_testdata.js +96 -0
  308. package/src/widgets/__testdata__/radio_testdata.js +262 -0
  309. package/src/widgets/__testdata__/sorter_testdata.js +20 -0
  310. package/src/widgets/__testdata__/transformer_testdata.js +109 -0
  311. package/src/widgets/__testdata__/video_testdata.js +22 -0
  312. package/src/widgets/__tests__/__snapshots__/categorizer_test.jsx.snap +996 -0
  313. package/src/widgets/__tests__/__snapshots__/cs-program_test.jsx.snap +75 -0
  314. package/src/widgets/__tests__/__snapshots__/definition_test.js.snap +105 -0
  315. package/src/widgets/__tests__/__snapshots__/dropdown_test.jsx.snap +157 -0
  316. package/src/widgets/__tests__/__snapshots__/explanation_test.js.snap +473 -0
  317. package/src/widgets/__tests__/__snapshots__/expression_test.jsx.snap +38 -0
  318. package/src/widgets/__tests__/__snapshots__/graded-group-set-jipt_test.jsx.snap +757 -0
  319. package/src/widgets/__tests__/__snapshots__/graded-group-set_test.jsx.snap +320 -0
  320. package/src/widgets/__tests__/__snapshots__/graded-group_test.jsx.snap +865 -0
  321. package/src/widgets/__tests__/__snapshots__/grapher_test.jsx.snap +2897 -0
  322. package/src/widgets/__tests__/__snapshots__/group_test.jsx.snap +1274 -0
  323. package/src/widgets/__tests__/__snapshots__/iframe_test.jsx.snap +69 -0
  324. package/src/widgets/__tests__/__snapshots__/image_test.jsx.snap +404 -0
  325. package/src/widgets/__tests__/__snapshots__/input-number_test.jsx.snap +978 -0
  326. package/src/widgets/__tests__/__snapshots__/interaction_test.jsx.snap +311 -0
  327. package/src/widgets/__tests__/__snapshots__/interactive-graph_test.jsx.snap +3017 -0
  328. package/src/widgets/__tests__/__snapshots__/matcher_test.jsx.snap +1039 -0
  329. package/src/widgets/__tests__/__snapshots__/matrix_test.jsx.snap +522 -0
  330. package/src/widgets/__tests__/__snapshots__/number-line_test.jsx.snap +888 -0
  331. package/src/widgets/__tests__/__snapshots__/numeric-input_test.jsx.snap +488 -0
  332. package/src/widgets/__tests__/__snapshots__/orderer_test.jsx.snap +275 -0
  333. package/src/widgets/__tests__/__snapshots__/passage-ref_test.jsx.snap +446 -0
  334. package/src/widgets/__tests__/__snapshots__/passage_test.jsx.snap +1988 -0
  335. package/src/widgets/__tests__/__snapshots__/radio_test.jsx.snap +4505 -0
  336. package/src/widgets/__tests__/__snapshots__/video_test.jsx.snap +161 -0
  337. package/src/widgets/__tests__/categorizer_test.jsx +205 -0
  338. package/src/widgets/__tests__/cs-program_test.jsx +45 -0
  339. package/src/widgets/__tests__/definition_test.js +137 -0
  340. package/src/widgets/__tests__/dropdown_test.jsx +106 -0
  341. package/src/widgets/__tests__/explanation_test.js +199 -0
  342. package/src/widgets/__tests__/expression_test.jsx +439 -0
  343. package/src/widgets/__tests__/graded-group-set-jipt_test.jsx +32 -0
  344. package/src/widgets/__tests__/graded-group-set_test.jsx +290 -0
  345. package/src/widgets/__tests__/graded-group_test.jsx +233 -0
  346. package/src/widgets/__tests__/grapher.cypress.jsx +546 -0
  347. package/src/widgets/__tests__/grapher_test.jsx +35 -0
  348. package/src/widgets/__tests__/group_test.jsx +512 -0
  349. package/src/widgets/__tests__/iframe_test.jsx +47 -0
  350. package/src/widgets/__tests__/image_test.jsx +65 -0
  351. package/src/widgets/__tests__/input-number_test.jsx +360 -0
  352. package/src/widgets/__tests__/interaction_test.jsx +33 -0
  353. package/src/widgets/__tests__/interactive-graph.cypress.jsx +777 -0
  354. package/src/widgets/__tests__/interactive-graph_test.jsx +105 -0
  355. package/src/widgets/__tests__/matcher_test.jsx +157 -0
  356. package/src/widgets/__tests__/matrix_test.jsx +84 -0
  357. package/src/widgets/__tests__/number-line_test.jsx +81 -0
  358. package/src/widgets/__tests__/numeric-input_test.jsx +514 -0
  359. package/src/widgets/__tests__/orderer_test.jsx +75 -0
  360. package/src/widgets/__tests__/passage-ref_test.jsx +166 -0
  361. package/src/widgets/__tests__/passage_test.jsx +212 -0
  362. package/src/widgets/__tests__/radio/base-radio_test.jsx +443 -0
  363. package/src/widgets/__tests__/radio/choice-icon_test.jsx +91 -0
  364. package/src/widgets/__tests__/radio/choice_test.jsx +311 -0
  365. package/src/widgets/__tests__/radio/focus-ring_test.jsx +23 -0
  366. package/src/widgets/__tests__/radio/option-status_test.jsx +65 -0
  367. package/src/widgets/__tests__/radio_test.jsx +810 -0
  368. package/src/widgets/__tests__/renderQuestion.jsx +66 -0
  369. package/src/widgets/__tests__/unit_test.jsx +144 -0
  370. package/src/widgets/__tests__/video_test.jsx +55 -0
  371. package/src/widgets/categorizer.jsx +343 -0
  372. package/src/widgets/cs-program.jsx +227 -0
  373. package/src/widgets/definition.jsx +121 -0
  374. package/src/widgets/dropdown.jsx +141 -0
  375. package/src/widgets/explanation.jsx +315 -0
  376. package/src/widgets/expression.jsx +652 -0
  377. package/src/widgets/graded-group-answer-bar.jsx +152 -0
  378. package/src/widgets/graded-group-set.jsx +296 -0
  379. package/src/widgets/graded-group.jsx +453 -0
  380. package/src/widgets/grapher/util.jsx +806 -0
  381. package/src/widgets/grapher.jsx +663 -0
  382. package/src/widgets/group.jsx +195 -0
  383. package/src/widgets/iframe.jsx +198 -0
  384. package/src/widgets/image.jsx +310 -0
  385. package/src/widgets/input-number.jsx +515 -0
  386. package/src/widgets/interaction/arrow-picker.jsx +39 -0
  387. package/src/widgets/interaction/color-picker.jsx +69 -0
  388. package/src/widgets/interaction/dash-picker.jsx +46 -0
  389. package/src/widgets/interaction/element-container.jsx +97 -0
  390. package/src/widgets/interaction.jsx +742 -0
  391. package/src/widgets/interactive-graph.jsx +2820 -0
  392. package/src/widgets/label-image/__stories__/answer-choices.stories.jsx +93 -0
  393. package/src/widgets/label-image/__stories__/marker.stories.jsx +172 -0
  394. package/src/widgets/label-image/answer-choices.jsx +311 -0
  395. package/src/widgets/label-image/marker.jsx +365 -0
  396. package/src/widgets/label-image/types.js +33 -0
  397. package/src/widgets/label-image.jsx +841 -0
  398. package/src/widgets/label-image_test.js +840 -0
  399. package/src/widgets/lights-puzzle.jsx +356 -0
  400. package/src/widgets/matcher.jsx +274 -0
  401. package/src/widgets/matrix.jsx +632 -0
  402. package/src/widgets/measurer.jsx +215 -0
  403. package/src/widgets/molecule/__tests__/layout_test.jsx +159 -0
  404. package/src/widgets/molecule/__tests__/parser_test.jsx +142 -0
  405. package/src/widgets/molecule/molecule-drawing.jsx +175 -0
  406. package/src/widgets/molecule/molecule-layout.jsx +367 -0
  407. package/src/widgets/molecule/smiles-parser.jsx +300 -0
  408. package/src/widgets/molecule.jsx +172 -0
  409. package/src/widgets/number-line.jsx +858 -0
  410. package/src/widgets/numeric-input.jsx +553 -0
  411. package/src/widgets/orderer.jsx +756 -0
  412. package/src/widgets/passage/__tests__/passage-markdown_test.jsx +373 -0
  413. package/src/widgets/passage/passage-markdown.jsx +472 -0
  414. package/src/widgets/passage-ref-target.jsx +73 -0
  415. package/src/widgets/passage-ref.jsx +214 -0
  416. package/src/widgets/passage.jsx +576 -0
  417. package/src/widgets/plotter.jsx +1198 -0
  418. package/src/widgets/radio/__stories__/base-radio.stories.jsx +189 -0
  419. package/src/widgets/radio/__stories__/choice-icon.stories.jsx +166 -0
  420. package/src/widgets/radio/__stories__/choice-none-above.stories.jsx +41 -0
  421. package/src/widgets/radio/__stories__/choice.stories.jsx +138 -0
  422. package/src/widgets/radio/__stories__/focus-ring.stories.jsx +44 -0
  423. package/src/widgets/radio/__stories__/option-status.stories.jsx +104 -0
  424. package/src/widgets/radio/base-radio.jsx +494 -0
  425. package/src/widgets/radio/choice-a11y-text.js +62 -0
  426. package/src/widgets/radio/choice-icon/choice-icon.jsx +214 -0
  427. package/src/widgets/radio/choice-icon/cross-out-line.jsx +52 -0
  428. package/src/widgets/radio/choice-icon/shared-styles.js +16 -0
  429. package/src/widgets/radio/choice-none-above.jsx +63 -0
  430. package/src/widgets/radio/choice.jsx +382 -0
  431. package/src/widgets/radio/choice.jsx.fixture.js +46 -0
  432. package/src/widgets/radio/focus-ring.jsx +58 -0
  433. package/src/widgets/radio/option-status.jsx +98 -0
  434. package/src/widgets/radio/util.js +32 -0
  435. package/src/widgets/radio/util_test.js +19 -0
  436. package/src/widgets/radio/widget.jsx +515 -0
  437. package/src/widgets/radio.jsx +155 -0
  438. package/src/widgets/reaction-diagram.jsx +137 -0
  439. package/src/widgets/sequence.jsx +143 -0
  440. package/src/widgets/simulator.jsx +806 -0
  441. package/src/widgets/sorter.jsx +149 -0
  442. package/src/widgets/table.jsx +397 -0
  443. package/src/widgets/transformer.jsx +2876 -0
  444. package/src/widgets/unit.jsx +372 -0
  445. package/src/widgets/video-transcript-link.jsx +84 -0
  446. package/src/widgets/video-transcript-link_test.js +211 -0
  447. package/src/widgets/video.jsx +126 -0
  448. package/src/widgets.js +431 -0
  449. package/src/zoom.js +539 -0
@@ -0,0 +1,199 @@
1
+ // @flow
2
+ import {screen} from "@testing-library/react";
3
+ import userEvent from "@testing-library/user-event";
4
+
5
+ // TODO(FEI-3857): Include in jest setup so that we don't need to import it everywhere
6
+ import "@testing-library/jest-dom/extend-expect";
7
+
8
+ import {testDependencies} from "../../../../../testing/test-dependencies.js";
9
+ import * as Dependencies from "../../dependencies.js";
10
+ import {question1} from "../__testdata__/explanation_testdata.js";
11
+ import ExplanationWidgetExports from "../explanation.jsx";
12
+
13
+ import {renderQuestion} from "./renderQuestion.jsx";
14
+
15
+ import type {PerseusExplanationWidgetOptions} from "../../perseus-types.js";
16
+
17
+ describe("Explanation", function () {
18
+ beforeEach(() => {
19
+ // We mock out `console.error` because the explanation widget adds
20
+ // `javascript:void(0);` to the `onClick` handler which triggers an
21
+ // error in our test env.
22
+ jest.spyOn(console, "error").mockImplementation(() => {});
23
+
24
+ jest.spyOn(Dependencies, "getDependencies").mockReturnValue(
25
+ testDependencies,
26
+ );
27
+ });
28
+
29
+ it("should snapshot", () => {
30
+ // Arrange and Act
31
+ const {container} = renderQuestion(question1);
32
+
33
+ // Assert
34
+ expect(container).toMatchSnapshot("initial render");
35
+ });
36
+
37
+ it("should snapshot when expanded", () => {
38
+ // Arrange
39
+ const {container} = renderQuestion(question1);
40
+
41
+ // Act
42
+ userEvent.click(screen.getByRole("button"));
43
+
44
+ // Assert
45
+ expect(container).toMatchSnapshot("expanded");
46
+ });
47
+
48
+ it("should snapshot for mobile", () => {
49
+ // Arrange and Act
50
+ const {container} = renderQuestion(question1, {
51
+ isMobile: true,
52
+ });
53
+
54
+ // Assert
55
+ expect(container).toMatchSnapshot("initial render");
56
+ });
57
+
58
+ it("should snapshot for mobile when expanded", () => {
59
+ // Arrange
60
+ const {container} = renderQuestion(question1, {
61
+ isMobile: true,
62
+ });
63
+
64
+ // Act
65
+ userEvent.click(screen.getByRole("button"));
66
+
67
+ // Assert
68
+ expect(container).toMatchSnapshot("expanded");
69
+ });
70
+
71
+ it("should snapshot for article", () => {
72
+ // Arrange and Act
73
+ const {container} = renderQuestion(question1, {
74
+ isArticle: true,
75
+ });
76
+
77
+ // Assert
78
+ expect(container).toMatchSnapshot("initial render");
79
+ });
80
+
81
+ it("should snapshot for article when expanded", () => {
82
+ // Arrange
83
+ const {container} = renderQuestion(question1, {
84
+ isArticle: true,
85
+ });
86
+
87
+ // Act
88
+ userEvent.click(screen.getByRole("button"));
89
+
90
+ // Assert
91
+ expect(container).toMatchSnapshot("expanded");
92
+ });
93
+
94
+ it("should snapshot for article+mobile", () => {
95
+ // Arrange and Act
96
+ const {container} = renderQuestion(question1, {
97
+ isMobile: true,
98
+ isArticle: true,
99
+ });
100
+
101
+ // Assert
102
+ expect(container).toMatchSnapshot("initial render");
103
+ });
104
+
105
+ it("should snapshot for article+mobile when expanded", () => {
106
+ // Arrange
107
+ const {container} = renderQuestion(question1, {
108
+ isMobile: true,
109
+ isArticle: true,
110
+ });
111
+
112
+ // Act
113
+ userEvent.click(screen.getByRole("button"));
114
+
115
+ // Assert
116
+ expect(container).toMatchSnapshot("expanded");
117
+ });
118
+
119
+ it("can be expanded", function () {
120
+ // Arrange
121
+ renderQuestion(question1);
122
+
123
+ // Act
124
+ const expandLink = screen.getByRole("button", {expanded: false});
125
+ userEvent.click(expandLink);
126
+
127
+ // Assert
128
+ // get asserts if it doesn't find a single matching element
129
+ expect(screen.getByText("This is an explanation")).toBeVisible();
130
+ });
131
+
132
+ it("can be collapsed", function () {
133
+ // Arrange
134
+ renderQuestion(question1);
135
+
136
+ // Act
137
+ const expandLink = screen.getByRole("button", {expanded: false});
138
+ userEvent.click(expandLink); // expand and then
139
+ const collapseLink = screen.getByRole("button", {expanded: true});
140
+ userEvent.click(collapseLink); // collapse
141
+
142
+ // Assert
143
+ expect(screen.queryByText("This is an explanation")).toBeNull();
144
+ });
145
+
146
+ it("should return an empty object for getUserInput()", () => {
147
+ // Arrange
148
+ const {renderer} = renderQuestion(question1);
149
+
150
+ // Act
151
+ const userInput = renderer.getUserInput();
152
+
153
+ // Assert
154
+ expect(userInput).toMatchInlineSnapshot(`
155
+ [
156
+ {},
157
+ ]
158
+ `);
159
+ });
160
+
161
+ it("should return a zero score for simpleValidate()", () => {
162
+ // Arrange
163
+ const {renderer} = renderQuestion(question1);
164
+
165
+ // Act
166
+ const score = renderer.scoreWidgets();
167
+
168
+ // Assert
169
+ expect(score).toMatchInlineSnapshot(`
170
+ {
171
+ "explanation 1": {
172
+ "earned": 0,
173
+ "message": null,
174
+ "total": 0,
175
+ "type": "points",
176
+ },
177
+ }
178
+ `);
179
+ });
180
+
181
+ describe("static validate", () => {
182
+ it("should always return 0 points", () => {
183
+ const result = ExplanationWidgetExports.widget.validate(
184
+ {},
185
+ ((question1.widgets["explanation 1"]
186
+ .options: any): PerseusExplanationWidgetOptions),
187
+ );
188
+
189
+ expect(result).toMatchInlineSnapshot(`
190
+ {
191
+ "earned": 0,
192
+ "message": null,
193
+ "total": 0,
194
+ "type": "points",
195
+ }
196
+ `);
197
+ });
198
+ });
199
+ });
@@ -0,0 +1,439 @@
1
+ // @flow
2
+ import {screen, fireEvent} from "@testing-library/react";
3
+ import userEvent from "@testing-library/user-event";
4
+ import "@testing-library/jest-dom";
5
+
6
+ import {testDependencies} from "../../../../../testing/test-dependencies.js";
7
+ import * as Dependencies from "../../dependencies.js";
8
+ import {registerAllWidgetsForTesting} from "../../util/register-all-widgets-for-testing.js";
9
+ import {
10
+ expressionItem2,
11
+ expressionItem3,
12
+ expressionItem3Options,
13
+ } from "../__testdata__/expression_testdata.js";
14
+ import {Expression} from "../expression.jsx";
15
+
16
+ import {renderQuestion} from "./renderQuestion.jsx";
17
+
18
+ const assertComplete = (itemData, input, isCorrect) => {
19
+ const {renderer} = renderQuestion(itemData.question);
20
+ userEvent.type(screen.getByRole("textbox"), input);
21
+ const [_, score] = renderer.guessAndScore();
22
+ expect(score).toMatchObject({
23
+ type: "points",
24
+ earned: isCorrect ? 1 : 0,
25
+ });
26
+ };
27
+
28
+ const assertCorrect = (itemData, input) =>
29
+ assertComplete(itemData, input, true);
30
+ const assertIncorrect = (itemData, input) =>
31
+ assertComplete(itemData, input, false);
32
+
33
+ // TODO: actually Assert that message is being set on the score object.
34
+ const assertInvalid = (itemData, input, message?: string) => {
35
+ const {renderer} = renderQuestion(itemData.question);
36
+ if (input.length) {
37
+ userEvent.type(screen.getByRole("textbox"), input);
38
+ }
39
+ const [_, score] = renderer.guessAndScore();
40
+ expect(score).toMatchObject({type: "invalid"});
41
+ };
42
+
43
+ describe("Expression Widget", function () {
44
+ beforeEach(() => {
45
+ jest.spyOn(Dependencies, "getDependencies").mockReturnValue(
46
+ testDependencies,
47
+ );
48
+ registerAllWidgetsForTesting();
49
+ });
50
+
51
+ describe("grading", function () {
52
+ it("should not grade a thing that doesn't parse", function () {
53
+ assertInvalid(expressionItem2, "+++");
54
+ });
55
+
56
+ it("should not grade a thing that is empty", function () {
57
+ assertInvalid(expressionItem2, "");
58
+ });
59
+ });
60
+
61
+ describe("fallthrough", function () {
62
+ it("should grade answers which don't match anything as wrong", function () {
63
+ assertIncorrect(expressionItem2, "500");
64
+ });
65
+ });
66
+
67
+ describe("variables", function () {
68
+ it.each(["123-X", "X-123"])(
69
+ "should not grade answers that are correct except for the " +
70
+ "variable case",
71
+ function (input) {
72
+ assertInvalid(expressionItem2, input);
73
+ },
74
+ );
75
+
76
+ // This isn't necessarily the ideal behavior; it's just what was the
77
+ // easiest to implement
78
+ it(
79
+ "should not grade answers that have the wrong variable case, " +
80
+ "even if the answer has got other errors",
81
+ function () {
82
+ assertInvalid(expressionItem2, "123+X");
83
+ },
84
+ );
85
+
86
+ it.each(["123-y", "123-Y"])(
87
+ "should not not grade answers that use the wrong variable",
88
+ function (input) {
89
+ assertInvalid(expressionItem2, input);
90
+ },
91
+ );
92
+ });
93
+
94
+ describe("multiple answers", function () {
95
+ it.each(["x-123", "123-x"])(
96
+ "should recognize either of two possibilities",
97
+ (input) => {
98
+ assertCorrect(expressionItem2, input);
99
+ },
100
+ );
101
+
102
+ /* For the tests below, here are the three answer forms:
103
+ *
104
+ * considered: "ungraded"
105
+ * value: x+1
106
+ *
107
+ * considered: "incorrect"
108
+ * value: y+1
109
+ *
110
+ * considered: "correct"
111
+ * value: z+1
112
+ */
113
+
114
+ it("should match from top to bottom", function () {
115
+ assertInvalid(expressionItem3, "x+1");
116
+ });
117
+
118
+ it("should match from top to bottom (2)", function () {
119
+ assertIncorrect(expressionItem3, "y+1");
120
+ });
121
+
122
+ it("should match from top to bottom (3)", function () {
123
+ assertCorrect(expressionItem3, "z+1");
124
+ });
125
+
126
+ it.each([["X+1"], ["Y+1"], ["Z+1"]])(
127
+ "should give casing or variable name error only relative to the " +
128
+ "correct answer",
129
+ (input) => {
130
+ assertInvalid(expressionItem3, input);
131
+ },
132
+ );
133
+ });
134
+
135
+ describe("ungraded", function () {
136
+ it("should handle ungraded answers with no error callback", function () {
137
+ const err = Expression.validate("x+1", expressionItem3Options);
138
+ expect(err).toStrictEqual({message: "", type: "invalid"});
139
+ });
140
+ });
141
+
142
+ describe("invalid", function () {
143
+ it("should handle ungraded answers with no error callback", function () {
144
+ const err = Expression.validate("x+^1", expressionItem3Options);
145
+ expect(err).toStrictEqual({message: null, type: "invalid"});
146
+ });
147
+ });
148
+ });
149
+
150
+ describe("getOneCorrectAnswerFromRubric", () => {
151
+ beforeEach(() => {
152
+ jest.spyOn(Dependencies, "getDependencies").mockReturnValue(
153
+ testDependencies,
154
+ );
155
+ });
156
+
157
+ it("should return undefined if rubric.value is null/undefined", () => {
158
+ // Arrange
159
+ const rubric = {
160
+ answerForms: [],
161
+ buttonSets: [],
162
+ functions: [],
163
+ times: true,
164
+ };
165
+
166
+ // Act
167
+ const result = Expression.getOneCorrectAnswerFromRubric(rubric);
168
+
169
+ // Assert
170
+ expect(result).toBeUndefined();
171
+ });
172
+
173
+ it("returns a correct answer when there is one correct answer", () => {
174
+ // Arrange
175
+ const rubric = {
176
+ answerForms: [
177
+ {
178
+ value: "123",
179
+ form: true,
180
+ simplify: false,
181
+ considered: "correct",
182
+ },
183
+ ],
184
+ buttonSets: [],
185
+ functions: [],
186
+ times: true,
187
+ };
188
+
189
+ // Act
190
+ const result = Expression.getOneCorrectAnswerFromRubric(rubric);
191
+
192
+ // Assert
193
+ expect(result).toEqual("123");
194
+ });
195
+
196
+ it("returns the first correct answer when there are multiple correct answers", () => {
197
+ // Arrange
198
+ const rubric = {
199
+ answerForms: [
200
+ {
201
+ value: "123",
202
+ form: true,
203
+ simplify: false,
204
+ considered: "correct",
205
+ },
206
+ {
207
+ value: "456",
208
+ form: true,
209
+ simplify: false,
210
+ considered: "correct",
211
+ },
212
+ ],
213
+ buttonSets: [],
214
+ functions: [],
215
+ times: true,
216
+ };
217
+
218
+ // Act
219
+ const result = Expression.getOneCorrectAnswerFromRubric(rubric);
220
+
221
+ // Assert
222
+ expect(result).toEqual("123");
223
+ });
224
+ });
225
+
226
+ describe("focus state", () => {
227
+ beforeEach(() => {
228
+ jest.spyOn(Dependencies, "getDependencies").mockReturnValue(
229
+ testDependencies,
230
+ );
231
+ });
232
+
233
+ it("supports directly focusing", () => {
234
+ // Arrange
235
+ renderQuestion(expressionItem2.question);
236
+
237
+ // Act
238
+ const expressionInput = screen.getByRole("textbox");
239
+ expressionInput.focus();
240
+
241
+ // Assert
242
+ expect(expressionInput).toHaveFocus();
243
+ });
244
+
245
+ it("supports directly blurring", () => {
246
+ // Arrange
247
+ renderQuestion(expressionItem2.question);
248
+
249
+ // Act
250
+ const expressionInput = screen.getByRole("textbox");
251
+ expressionInput.focus();
252
+ expressionInput.blur();
253
+
254
+ // Assert
255
+ expect(expressionInput).not.toHaveFocus();
256
+ });
257
+
258
+ it("can be focused via a function", () => {
259
+ // arrange
260
+ const {renderer} = renderQuestion(expressionItem2.question);
261
+ const expression = renderer.findWidgets("expression 1")[0];
262
+
263
+ // act
264
+ expression.focusInputPath();
265
+
266
+ // Assert
267
+ const expressionInput = screen.getByRole("textbox");
268
+ expect(expressionInput).toHaveFocus();
269
+ });
270
+ });
271
+
272
+ describe("rendering", () => {
273
+ beforeEach(() => {
274
+ jest.spyOn(Dependencies, "getDependencies").mockReturnValue(
275
+ testDependencies,
276
+ );
277
+ });
278
+
279
+ it("supports mobile rendering", async () => {
280
+ // arrange and act
281
+ renderQuestion(expressionItem2.question, {
282
+ // Setting this triggers mobile rendering
283
+ // it would be nice if this was more clear in the code
284
+ customKeypad: true,
285
+ });
286
+
287
+ // Assert
288
+ const mobileInput = await screen.findByRole("textbox");
289
+ expect(mobileInput).toBeVisible();
290
+ });
291
+
292
+ it("static renders", () => {
293
+ // arrange and act
294
+ const {container} = renderQuestion(expressionItem2.question, {
295
+ staticRender: true,
296
+ });
297
+
298
+ // Assert
299
+ expect(container).toMatchSnapshot("static render");
300
+ });
301
+ });
302
+
303
+ describe("interaction", () => {
304
+ beforeEach(() => {
305
+ jest.spyOn(Dependencies, "getDependencies").mockReturnValue(
306
+ testDependencies,
307
+ );
308
+ });
309
+
310
+ it("sets input value directly", () => {
311
+ // arrange
312
+ const {renderer} = renderQuestion(expressionItem2.question);
313
+
314
+ // act
315
+ renderer.setInputValue(["expression 1"], "123-x", () => {});
316
+ const score = renderer.guessAndScore()[1];
317
+
318
+ // Assert
319
+ expect(score.type).toBe("points");
320
+ // Score.total doesn't exist if the input is invalid
321
+ // In this case we know that it'll be valid so we can assert directly
322
+ // $FlowFixMe[prop-missing]
323
+ expect(score.earned).toBe(score.total);
324
+ });
325
+
326
+ it("has a developer facility for inserting", () => {
327
+ // arrange
328
+ const {renderer} = renderQuestion(expressionItem2.question);
329
+ const expression = renderer.findWidgets("expression 1")[0];
330
+ expression.insert("x+1");
331
+
332
+ // act
333
+ const score = renderer.score();
334
+
335
+ // Assert
336
+ // Score.total doesn't exist if the input is invalid
337
+ // In this case we know that it'll be valid so we can assert directly
338
+ // $FlowFixMe[prop-missing]
339
+ expect(score.total).toBe(1);
340
+ });
341
+ });
342
+
343
+ describe("error tooltip", () => {
344
+ beforeEach(() => {
345
+ jest.spyOn(Dependencies, "getDependencies").mockReturnValue(
346
+ testDependencies,
347
+ );
348
+ });
349
+
350
+ it("shows on error", () => {
351
+ // Arrange
352
+ const {renderer} = renderQuestion(expressionItem2.question);
353
+ const expression = renderer.findWidgets("expression 1")[0];
354
+
355
+ // Act
356
+ expression.insert("x&&&&&^1");
357
+ renderer.guessAndScore();
358
+ jest.runOnlyPendingTimers();
359
+
360
+ // Assert
361
+ const errorMessage = screen.getByText(
362
+ "Sorry, I don't understand that!",
363
+ );
364
+ expect(errorMessage).not.toBeUndefined();
365
+ });
366
+
367
+ it("shows error text on mouse over", async () => {
368
+ // Arrange
369
+ const {renderer} = renderQuestion(expressionItem2.question);
370
+ const expression = renderer.findWidgets("expression 1")[0];
371
+
372
+ // Act
373
+ expression.insert("x&&&&&^1");
374
+ renderer.guessAndScore();
375
+ jest.runOnlyPendingTimers();
376
+
377
+ const tooltip = await screen.findByTestId("test-error-icon");
378
+
379
+ // eslint-disable-next-line testing-library/prefer-user-event
380
+ fireEvent.mouseEnter(tooltip);
381
+
382
+ // Assert
383
+ expect(
384
+ screen.getByText("Sorry, I don't understand that!"),
385
+ ).toBeVisible();
386
+ });
387
+ it("hides error text on mouse leave", async () => {
388
+ // Arrange
389
+ const {renderer} = renderQuestion(expressionItem2.question);
390
+ const expression = renderer.findWidgets("expression 1")[0];
391
+
392
+ expression.insert("x&&&&&^1");
393
+ renderer.guessAndScore();
394
+ jest.runOnlyPendingTimers();
395
+
396
+ const tooltip = await screen.findByTestId("test-error-icon");
397
+
398
+ // Act
399
+ // NOTE(Nicole): The existing perseus code requires explicit events
400
+ // eslint-disable-next-line testing-library/prefer-user-event
401
+ fireEvent.mouseEnter(tooltip);
402
+ // NOTE(Nicole): The existing perseus code requires explicit events
403
+ // eslint-disable-next-line testing-library/prefer-user-event
404
+ fireEvent.mouseLeave(tooltip);
405
+
406
+ // Assert
407
+ expect(
408
+ screen.getByText("Sorry, I don't understand that!"),
409
+ ).not.toBeVisible();
410
+ });
411
+
412
+ it("toggles error text on click", async () => {
413
+ // Arrange
414
+ const {renderer} = renderQuestion(expressionItem2.question);
415
+ const expression = renderer.findWidgets("expression 1")[0];
416
+
417
+ expression.insert("x&&&&&^1");
418
+ renderer.guessAndScore();
419
+ jest.runOnlyPendingTimers();
420
+
421
+ const tooltip = await screen.findByTestId("test-error-icon");
422
+
423
+ // Act & Assert
424
+ // NOTE(Nicole): The existing perseus code requires explicit events
425
+ // eslint-disable-next-line testing-library/prefer-user-event
426
+ fireEvent.click(tooltip);
427
+ expect(
428
+ screen.getByText("Sorry, I don't understand that!"),
429
+ ).toBeVisible();
430
+
431
+ // Act & Assert
432
+ // NOTE(Nicole): The existing perseus code requires explicit events
433
+ // eslint-disable-next-line testing-library/prefer-user-event
434
+ fireEvent.click(tooltip);
435
+ expect(
436
+ screen.getByText("Sorry, I don't understand that!"),
437
+ ).not.toBeVisible();
438
+ });
439
+ });
@@ -0,0 +1,32 @@
1
+ // @flow
2
+
3
+ /**
4
+ * These GradedGroupSet tests are specifically for the JIPT integration.
5
+ * This widget renders differently when JIPT is active to make translation
6
+ * easier for translators.
7
+ */
8
+
9
+ import {testDependencies} from "../../../../../testing/test-dependencies.js";
10
+ import * as Dependencies from "../../dependencies.js";
11
+ import {article1} from "../__testdata__/graded-group-set_testdata.js";
12
+
13
+ import {renderQuestion} from "./renderQuestion.jsx";
14
+
15
+ describe("graded-group-set", () => {
16
+ beforeEach(() => {
17
+ jest.spyOn(Dependencies, "getDependencies").mockReturnValue({
18
+ ...testDependencies,
19
+ JIPT: {
20
+ useJIPT: true,
21
+ },
22
+ });
23
+ });
24
+
25
+ it("should render all graded groups", () => {
26
+ // Arrange and Act
27
+ const {container} = renderQuestion(article1);
28
+
29
+ // Assert
30
+ expect(container).toMatchSnapshot();
31
+ });
32
+ });