@pie-lib/math-input-mathlive 1.0.0
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/README.md +119 -0
- package/lib/__tests__/index.test.js +118 -0
- package/lib/__tests__/index.test.js.map +1 -0
- package/lib/__tests__/latex-bridge.test.js +186 -0
- package/lib/__tests__/latex-bridge.test.js.map +1 -0
- package/lib/__tests__/mathlive-instance.test.js +137 -0
- package/lib/__tests__/mathlive-instance.test.js.map +1 -0
- package/lib/horizontal-keypad.js +101 -0
- package/lib/horizontal-keypad.js.map +1 -0
- package/lib/index.js +143 -0
- package/lib/index.js.map +1 -0
- package/lib/keypad/index.js +365 -0
- package/lib/keypad/index.js.map +1 -0
- package/lib/keypad/keys-layout.js +22 -0
- package/lib/keypad/keys-layout.js.map +1 -0
- package/lib/keys/basic-operators.js +33 -0
- package/lib/keys/basic-operators.js.map +1 -0
- package/lib/keys/chars.js +12 -0
- package/lib/keys/chars.js.map +1 -0
- package/lib/keys/comparison.js +39 -0
- package/lib/keys/comparison.js.map +1 -0
- package/lib/keys/constants.js +37 -0
- package/lib/keys/constants.js.map +1 -0
- package/lib/keys/digits.js +46 -0
- package/lib/keys/digits.js.map +1 -0
- package/lib/keys/edit.js +14 -0
- package/lib/keys/edit.js.map +1 -0
- package/lib/keys/exponent.js +30 -0
- package/lib/keys/exponent.js.map +1 -0
- package/lib/keys/fractions.js +29 -0
- package/lib/keys/fractions.js.map +1 -0
- package/lib/keys/geometry.js +140 -0
- package/lib/keys/geometry.js.map +1 -0
- package/lib/keys/grades.js +259 -0
- package/lib/keys/grades.js.map +1 -0
- package/lib/keys/index.js +35 -0
- package/lib/keys/index.js.map +1 -0
- package/lib/keys/log.js +27 -0
- package/lib/keys/log.js.map +1 -0
- package/lib/keys/logic.js +19 -0
- package/lib/keys/logic.js.map +1 -0
- package/lib/keys/matrices.js +19 -0
- package/lib/keys/matrices.js.map +1 -0
- package/lib/keys/misc.js +62 -0
- package/lib/keys/misc.js.map +1 -0
- package/lib/keys/navigation.js +20 -0
- package/lib/keys/navigation.js.map +1 -0
- package/lib/keys/operators.js +15 -0
- package/lib/keys/operators.js.map +1 -0
- package/lib/keys/statistics.js +40 -0
- package/lib/keys/statistics.js.map +1 -0
- package/lib/keys/sub-sup.js +19 -0
- package/lib/keys/sub-sup.js.map +1 -0
- package/lib/keys/trigonometry.js +45 -0
- package/lib/keys/trigonometry.js.map +1 -0
- package/lib/keys/utils.js +87 -0
- package/lib/keys/utils.js.map +1 -0
- package/lib/keys/vars.js +24 -0
- package/lib/keys/vars.js.map +1 -0
- package/lib/latex-bridge.js +195 -0
- package/lib/latex-bridge.js.map +1 -0
- package/lib/math-input.js +143 -0
- package/lib/math-input.js.map +1 -0
- package/lib/mathlive-instance.js +319 -0
- package/lib/mathlive-instance.js.map +1 -0
- package/lib/mf/common-styles.js +111 -0
- package/lib/mf/common-styles.js.map +1 -0
- package/lib/mf/index.js +72 -0
- package/lib/mf/index.js.map +1 -0
- package/lib/mf/input.js +266 -0
- package/lib/mf/input.js.map +1 -0
- package/lib/mf/static.js +396 -0
- package/lib/mf/static.js.map +1 -0
- package/package.json +31 -0
- package/src/__tests__/index.test.jsx +109 -0
- package/src/__tests__/latex-bridge.test.js +151 -0
- package/src/__tests__/mathlive-instance.test.js +130 -0
- package/src/horizontal-keypad.jsx +73 -0
- package/src/index.jsx +63 -0
- package/src/keypad/index.jsx +335 -0
- package/src/keypad/keys-layout.js +16 -0
- package/src/keys/basic-operators.js +32 -0
- package/src/keys/chars.js +5 -0
- package/src/keys/comparison.js +28 -0
- package/src/keys/constants.js +35 -0
- package/src/keys/digits.js +40 -0
- package/src/keys/edit.js +3 -0
- package/src/keys/exponent.js +28 -0
- package/src/keys/fractions.js +26 -0
- package/src/keys/geometry.js +144 -0
- package/src/keys/grades.js +367 -0
- package/src/keys/index.js +20 -0
- package/src/keys/log.js +22 -0
- package/src/keys/logic.js +15 -0
- package/src/keys/matrices.js +15 -0
- package/src/keys/misc.js +65 -0
- package/src/keys/navigation.js +8 -0
- package/src/keys/operators.js +10 -0
- package/src/keys/statistics.js +38 -0
- package/src/keys/sub-sup.js +15 -0
- package/src/keys/trigonometry.js +15 -0
- package/src/keys/utils.js +66 -0
- package/src/keys/vars.js +19 -0
- package/src/latex-bridge.js +190 -0
- package/src/math-input.jsx +115 -0
- package/src/mathlive-instance.js +286 -0
- package/src/mf/common-styles.js +108 -0
- package/src/mf/index.js +26 -0
- package/src/mf/input.jsx +223 -0
- package/src/mf/static.jsx +358 -0
package/README.md
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
# @pie-lib/math-input-mathlive
|
|
2
|
+
|
|
3
|
+
MathLive-backed math input. A drop-in alternative to `@pie-lib/math-input`, which
|
|
4
|
+
is built on the `@pie-framework/mathquill` fork.
|
|
5
|
+
|
|
6
|
+
Findings that motivated this package: [`../math-input/SPIKE-mathlive.md`](../math-input/SPIKE-mathlive.md).
|
|
7
|
+
|
|
8
|
+
## Migrating
|
|
9
|
+
|
|
10
|
+
The export surface mirrors `@pie-lib/math-input`, so in most cases only the
|
|
11
|
+
import changes:
|
|
12
|
+
|
|
13
|
+
```diff
|
|
14
|
+
-import { mq, keys, keysForGrade, HorizontalKeypad, registerEmbed, applyStaticMath } from '@pie-lib/math-input';
|
|
15
|
+
+import { mq, keys, keysForGrade, HorizontalKeypad, registerEmbed, applyStaticMath } from '@pie-lib/math-input-mathlive';
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
The field wrappers are exported as `mf`, with `mq` kept as an alias so existing
|
|
19
|
+
`mq.Input` / `mq.Static` call sites keep working.
|
|
20
|
+
|
|
21
|
+
### Behaviour differences
|
|
22
|
+
|
|
23
|
+
| | `@pie-lib/math-input` | this package |
|
|
24
|
+
|---|---|---|
|
|
25
|
+
| `mq.Input` | MathQuill `MathField` | `<math-field>` |
|
|
26
|
+
| `mq.Static` (display) | one `MQ.StaticMath` per instance | `convertLatexToMarkup`, no live instance |
|
|
27
|
+
| `mq.Static` (answer blocks) | `innerFields` | prompts API (`getPrompts`/`getPromptValue`) |
|
|
28
|
+
| `updateSpans()` | patched MathQuill DOM | **no-op** (kept for compatibility) |
|
|
29
|
+
| `registerEmbed` | MathQuill embed plugin | macro + `\htmlData` shim, see below |
|
|
30
|
+
| Loading | synchronous | **async** - `await loadMathLive()` before `applyStaticMath` / `latexToMarkup` |
|
|
31
|
+
|
|
32
|
+
### Content model is preserved
|
|
33
|
+
|
|
34
|
+
Stored latex keeps its MathQuill-era form, so items authored with either
|
|
35
|
+
implementation stay readable and `@pie-lib/math-rendering` needs no change.
|
|
36
|
+
Translation happens at the package boundary:
|
|
37
|
+
|
|
38
|
+
| stored | in MathLive |
|
|
39
|
+
|---|---|
|
|
40
|
+
| `\embed{newLine}[]` | `\displaylines{… \\ …}` |
|
|
41
|
+
| `\MathQuillMathField[id]{}` | `\placeholder[id]{}` |
|
|
42
|
+
|
|
43
|
+
`toMathLive()` / `fromMathLive()` are exported if you need the conversion directly.
|
|
44
|
+
|
|
45
|
+
### `registerEmbed`
|
|
46
|
+
|
|
47
|
+
Same signature, different mechanism - MathLive has no embed plugin system, so an
|
|
48
|
+
embed becomes a macro that expands to real LaTeX, tagged with
|
|
49
|
+
`\htmlData{embed=<name>}` so CSS can target `[data-embed="<name>"]` the way it
|
|
50
|
+
used to target the embed's `htmlString`.
|
|
51
|
+
|
|
52
|
+
```js
|
|
53
|
+
registerEmbed('answerBlock', () => ({
|
|
54
|
+
latex: () => '\\embed{answerBlock}[]',
|
|
55
|
+
mathliveLatex: '\\placeholder{}', // optional: control the expansion
|
|
56
|
+
}));
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
`newLine` and `answerBlock` are handled natively. Any other name falls back to a
|
|
60
|
+
`\placeholder{}` and logs a warning.
|
|
61
|
+
|
|
62
|
+
**Limitation:** MathQuill's `htmlString` could inject an arbitrary DOM subtree.
|
|
63
|
+
`\htmlData` only decorates MathLive's own output, so embeds that mounted a custom
|
|
64
|
+
widget do not port and need a redesign (`mf.getElementInfo()` exposes per-element
|
|
65
|
+
`bounds`/`id`/`data` if you need to overlay something).
|
|
66
|
+
|
|
67
|
+
## Notes
|
|
68
|
+
|
|
69
|
+
- **Browser only.** MathLive registers custom elements; `loadMathLive()` returns
|
|
70
|
+
`undefined` during SSR and every consumer degrades gracefully. Import the
|
|
71
|
+
package client-side (see `packages/demo/pages/math-input-mathlive.js`).
|
|
72
|
+
- **Bundle.** The editor is ~227KB gzip plus ~296KB of KaTeX fonts, so it is
|
|
73
|
+
loaded lazily via dynamic `import()`. Render-only consumers can use
|
|
74
|
+
`mathlive/ssr` (~111KB gzip) through `latexToMarkup`.
|
|
75
|
+
- **Fonts: import MathLive's stylesheet once.** This is the recommended way -
|
|
76
|
+
no copied files, no CDN:
|
|
77
|
+
|
|
78
|
+
```js
|
|
79
|
+
import 'mathlive/fonts.css';
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
The stylesheet's relative `url(fonts/*.woff2)` paths are resolved by your
|
|
83
|
+
bundler, so the fonts are emitted with the build and content-hashed. It also
|
|
84
|
+
sets `--ML__static-fonts`, which makes MathLive skip font fetching entirely.
|
|
85
|
+
|
|
86
|
+
This matters because MathLive's default `fontsDirectory` is the relative
|
|
87
|
+
`'./fonts/'`, which it resolves against its own script URL - empty under a
|
|
88
|
+
bundler, so `loadFonts` throws
|
|
89
|
+
`Failed to construct 'URL': Invalid base URL`. Importing the stylesheet avoids
|
|
90
|
+
that code path completely.
|
|
91
|
+
|
|
92
|
+
*Bundler caveat:* a `url-loader`/`file-loader` rule matching `woff2` will
|
|
93
|
+
corrupt these fonts (it emits a JS module under a `.woff2` name). Exclude
|
|
94
|
+
mathlive from such rules - see `packages/demo/next.config.js`.
|
|
95
|
+
|
|
96
|
+
**Alternative** - serve the fonts yourself (a static path, or a CDN):
|
|
97
|
+
|
|
98
|
+
```js
|
|
99
|
+
import { configureFonts } from '@pie-lib/math-input-mathlive';
|
|
100
|
+
|
|
101
|
+
configureFonts('/mathlive-fonts'); // before loadMathLive()
|
|
102
|
+
configureFonts('https://unpkg.com/mathlive@0.110.0/dist/fonts');
|
|
103
|
+
configureFonts(null); // system fonts
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
Paths are resolved to absolute URLs against `document.baseURI`. If you neither
|
|
107
|
+
import the stylesheet nor call `configureFonts`, the package disables font
|
|
108
|
+
loading rather than leaving MathLive's crashing default in place.
|
|
109
|
+
- **Custom macros.** `PIE_MACROS` covers the pie-specific commands the MathQuill
|
|
110
|
+
fork understood (`\parallelogram`, `\longdiv`, `\napprox`, …) and is kept in
|
|
111
|
+
sync with the MathJax macros in `@pie-lib/math-rendering`.
|
|
112
|
+
|
|
113
|
+
## Development
|
|
114
|
+
|
|
115
|
+
```bash
|
|
116
|
+
nvm use --delete-prefix v22 # mathlive needs node >= 18
|
|
117
|
+
./node_modules/.bin/jest packages/math-input-mathlive/src
|
|
118
|
+
./node_modules/.bin/next dev packages/demo # then /math-input-mathlive
|
|
119
|
+
```
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
|
|
4
|
+
var _typeof2 = _interopRequireDefault(require("@babel/runtime/helpers/typeof"));
|
|
5
|
+
var _react = _interopRequireDefault(require("react"));
|
|
6
|
+
var _react2 = require("@testing-library/react");
|
|
7
|
+
var _index = require("../index");
|
|
8
|
+
describe('math-input-mathlive public API', function () {
|
|
9
|
+
it('mirrors the @pie-lib/math-input export surface', function () {
|
|
10
|
+
expect((0, _typeof2["default"])(_index.keysForGrade)).toBe('function');
|
|
11
|
+
expect((0, _typeof2["default"])(_index.addBrackets)).toBe('function');
|
|
12
|
+
expect((0, _typeof2["default"])(_index.removeBrackets)).toBe('function');
|
|
13
|
+
expect((0, _typeof2["default"])(_index.registerEmbed)).toBe('function');
|
|
14
|
+
expect((0, _typeof2["default"])(_index.applyStaticMath)).toBe('function');
|
|
15
|
+
expect((0, _typeof2["default"])(_index.updateSpans)).toBe('function');
|
|
16
|
+
expect(_index.keys).toBeDefined();
|
|
17
|
+
expect(_index.keys.baseSet).toBeDefined();
|
|
18
|
+
});
|
|
19
|
+
it('exports the field wrappers under both mf and mq', function () {
|
|
20
|
+
expect(_index.mf.Input).toBeDefined();
|
|
21
|
+
expect(_index.mf.Static).toBeDefined();
|
|
22
|
+
// `mq` alias lets a consumer swap packages by changing only the import.
|
|
23
|
+
expect(_index.mq).toBe(_index.mf);
|
|
24
|
+
});
|
|
25
|
+
it('updateSpans is a safe no-op', function () {
|
|
26
|
+
expect(function () {
|
|
27
|
+
return (0, _index.updateSpans)();
|
|
28
|
+
}).not.toThrow();
|
|
29
|
+
});
|
|
30
|
+
it('CommonMqStyles keeps the shape consumers destructure', function () {
|
|
31
|
+
// math-toolbar does:
|
|
32
|
+
// const { commonMqFontStyles, commonMqKeyboardStyles, longdivStyles, supsubStyles }
|
|
33
|
+
// = mq.CommonMqStyles;
|
|
34
|
+
var _mf$CommonMqStyles = _index.mf.CommonMqStyles,
|
|
35
|
+
commonMqFontStyles = _mf$CommonMqStyles.commonMqFontStyles,
|
|
36
|
+
commonMqKeyboardStyles = _mf$CommonMqStyles.commonMqKeyboardStyles,
|
|
37
|
+
longdivStyles = _mf$CommonMqStyles.longdivStyles,
|
|
38
|
+
supsubStyles = _mf$CommonMqStyles.supsubStyles;
|
|
39
|
+
[commonMqFontStyles, commonMqKeyboardStyles, longdivStyles, supsubStyles].forEach(function (s) {
|
|
40
|
+
expect(s).toBeDefined();
|
|
41
|
+
expect((0, _typeof2["default"])(s)).toBe('object');
|
|
42
|
+
});
|
|
43
|
+
});
|
|
44
|
+
it('bracket helpers round-trip', function () {
|
|
45
|
+
expect((0, _index.addBrackets)('x')).toEqual('\\(x\\)');
|
|
46
|
+
expect((0, _index.removeBrackets)('\\(x\\)')).toEqual('x');
|
|
47
|
+
expect((0, _index.removeBrackets)((0, _index.addBrackets)('\\frac{1}{2}'))).toEqual('\\frac{1}{2}');
|
|
48
|
+
});
|
|
49
|
+
it('keysForGrade returns a 5-row keyset', function () {
|
|
50
|
+
var set = (0, _index.keysForGrade)('geometry');
|
|
51
|
+
expect(Array.isArray(set)).toBe(true);
|
|
52
|
+
expect(set.length).toBe(5);
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
describe('rendering', function () {
|
|
56
|
+
// MathLive is a browser-only custom element; loadMathLive() resolves async and
|
|
57
|
+
// is a no-op-ish under jsdom. These assert the components mount without
|
|
58
|
+
// throwing, which is what regressed most often in the MathQuill version.
|
|
59
|
+
it('KeyPad renders without a live MathLive instance', function () {
|
|
60
|
+
var _render = (0, _react2.render)(/*#__PURE__*/_react["default"].createElement(_index.KeyPad, {
|
|
61
|
+
onPress: function onPress() {}
|
|
62
|
+
})),
|
|
63
|
+
container = _render.container;
|
|
64
|
+
expect(container.querySelectorAll('button').length).toBeGreaterThan(0);
|
|
65
|
+
});
|
|
66
|
+
it('KeyPad falls back to raw latex for labels before MathLive loads', function () {
|
|
67
|
+
var _render2 = (0, _react2.render)(/*#__PURE__*/_react["default"].createElement(_index.KeyPad, {
|
|
68
|
+
onPress: function onPress() {},
|
|
69
|
+
baseSet: [[{
|
|
70
|
+
name: 'pi',
|
|
71
|
+
latex: '\\pi',
|
|
72
|
+
category: 'constants'
|
|
73
|
+
}]]
|
|
74
|
+
})),
|
|
75
|
+
container = _render2.container;
|
|
76
|
+
expect(container.textContent).toContain('\\pi');
|
|
77
|
+
});
|
|
78
|
+
it('KeyPad fires onPress with the key definition', function () {
|
|
79
|
+
var onPress = jest.fn();
|
|
80
|
+
var key = {
|
|
81
|
+
name: 'x',
|
|
82
|
+
label: 'x',
|
|
83
|
+
write: 'x',
|
|
84
|
+
category: 'vars'
|
|
85
|
+
};
|
|
86
|
+
var _render3 = (0, _react2.render)(/*#__PURE__*/_react["default"].createElement(_index.KeyPad, {
|
|
87
|
+
onPress: onPress,
|
|
88
|
+
baseSet: [[key]]
|
|
89
|
+
})),
|
|
90
|
+
container = _render3.container;
|
|
91
|
+
container.querySelector('button').click();
|
|
92
|
+
expect(onPress).toHaveBeenCalledWith(key);
|
|
93
|
+
});
|
|
94
|
+
it('MathInput mounts', function () {
|
|
95
|
+
expect(function () {
|
|
96
|
+
return (0, _react2.render)(/*#__PURE__*/_react["default"].createElement(_index.MathInput, {
|
|
97
|
+
latex: "\\\\frac{1}{2}",
|
|
98
|
+
onChange: function onChange() {}
|
|
99
|
+
}));
|
|
100
|
+
}).not.toThrow();
|
|
101
|
+
});
|
|
102
|
+
it('HorizontalKeypad mounts and reports the legacy {value,type} model', function () {
|
|
103
|
+
var onClick = jest.fn();
|
|
104
|
+
expect(function () {
|
|
105
|
+
return (0, _react2.render)(/*#__PURE__*/_react["default"].createElement(_index.HorizontalKeypad, {
|
|
106
|
+
onClick: onClick
|
|
107
|
+
}));
|
|
108
|
+
}).not.toThrow();
|
|
109
|
+
});
|
|
110
|
+
it('Static renders display-mode latex without a mathfield', function () {
|
|
111
|
+
expect(function () {
|
|
112
|
+
return (0, _react2.render)(/*#__PURE__*/_react["default"].createElement(_index.mf.Static, {
|
|
113
|
+
latex: "\\\\frac{1}{2}"
|
|
114
|
+
}));
|
|
115
|
+
}).not.toThrow();
|
|
116
|
+
});
|
|
117
|
+
});
|
|
118
|
+
//# sourceMappingURL=index.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.test.js","names":["_react","_interopRequireDefault","require","_react2","_index","describe","it","expect","_typeof2","keysForGrade","toBe","addBrackets","removeBrackets","registerEmbed","applyStaticMath","updateSpans","keys","toBeDefined","baseSet","mf","Input","Static","mq","not","toThrow","_mf$CommonMqStyles","CommonMqStyles","commonMqFontStyles","commonMqKeyboardStyles","longdivStyles","supsubStyles","forEach","s","toEqual","set","Array","isArray","length","_render","render","createElement","KeyPad","onPress","container","querySelectorAll","toBeGreaterThan","_render2","name","latex","category","textContent","toContain","jest","fn","key","label","write","_render3","querySelector","click","toHaveBeenCalledWith","MathInput","onChange","onClick","HorizontalKeypad"],"sources":["../../src/__tests__/index.test.jsx"],"sourcesContent":["import React from 'react';\nimport { render } from '@testing-library/react';\n\nimport {\n keys,\n keysForGrade,\n addBrackets,\n removeBrackets,\n registerEmbed,\n applyStaticMath,\n updateSpans,\n mf,\n mq,\n MathInput,\n HorizontalKeypad,\n KeyPad,\n} from '../index';\n\ndescribe('math-input-mathlive public API', () => {\n it('mirrors the @pie-lib/math-input export surface', () => {\n expect(typeof keysForGrade).toBe('function');\n expect(typeof addBrackets).toBe('function');\n expect(typeof removeBrackets).toBe('function');\n expect(typeof registerEmbed).toBe('function');\n expect(typeof applyStaticMath).toBe('function');\n expect(typeof updateSpans).toBe('function');\n expect(keys).toBeDefined();\n expect(keys.baseSet).toBeDefined();\n });\n\n it('exports the field wrappers under both mf and mq', () => {\n expect(mf.Input).toBeDefined();\n expect(mf.Static).toBeDefined();\n // `mq` alias lets a consumer swap packages by changing only the import.\n expect(mq).toBe(mf);\n });\n\n it('updateSpans is a safe no-op', () => {\n expect(() => updateSpans()).not.toThrow();\n });\n\n it('CommonMqStyles keeps the shape consumers destructure', () => {\n // math-toolbar does:\n // const { commonMqFontStyles, commonMqKeyboardStyles, longdivStyles, supsubStyles }\n // = mq.CommonMqStyles;\n const { commonMqFontStyles, commonMqKeyboardStyles, longdivStyles, supsubStyles } = mf.CommonMqStyles;\n\n [commonMqFontStyles, commonMqKeyboardStyles, longdivStyles, supsubStyles].forEach((s) => {\n expect(s).toBeDefined();\n expect(typeof s).toBe('object');\n });\n });\n\n it('bracket helpers round-trip', () => {\n expect(addBrackets('x')).toEqual('\\\\(x\\\\)');\n expect(removeBrackets('\\\\(x\\\\)')).toEqual('x');\n expect(removeBrackets(addBrackets('\\\\frac{1}{2}'))).toEqual('\\\\frac{1}{2}');\n });\n\n it('keysForGrade returns a 5-row keyset', () => {\n const set = keysForGrade('geometry');\n\n expect(Array.isArray(set)).toBe(true);\n expect(set.length).toBe(5);\n });\n});\n\ndescribe('rendering', () => {\n // MathLive is a browser-only custom element; loadMathLive() resolves async and\n // is a no-op-ish under jsdom. These assert the components mount without\n // throwing, which is what regressed most often in the MathQuill version.\n it('KeyPad renders without a live MathLive instance', () => {\n const { container } = render(<KeyPad onPress={() => {}} />);\n\n expect(container.querySelectorAll('button').length).toBeGreaterThan(0);\n });\n\n it('KeyPad falls back to raw latex for labels before MathLive loads', () => {\n const { container } = render(\n <KeyPad onPress={() => {}} baseSet={[[{ name: 'pi', latex: '\\\\pi', category: 'constants' }]]} />,\n );\n\n expect(container.textContent).toContain('\\\\pi');\n });\n\n it('KeyPad fires onPress with the key definition', () => {\n const onPress = jest.fn();\n const key = { name: 'x', label: 'x', write: 'x', category: 'vars' };\n const { container } = render(<KeyPad onPress={onPress} baseSet={[[key]]} />);\n\n container.querySelector('button').click();\n\n expect(onPress).toHaveBeenCalledWith(key);\n });\n\n it('MathInput mounts', () => {\n expect(() => render(<MathInput latex=\"\\\\frac{1}{2}\" onChange={() => {}} />)).not.toThrow();\n });\n\n it('HorizontalKeypad mounts and reports the legacy {value,type} model', () => {\n const onClick = jest.fn();\n\n expect(() => render(<HorizontalKeypad onClick={onClick} />)).not.toThrow();\n });\n\n it('Static renders display-mode latex without a mathfield', () => {\n expect(() => render(<mf.Static latex=\"\\\\frac{1}{2}\" />)).not.toThrow();\n });\n});\n"],"mappings":"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","ignoreList":[]}
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
|
|
4
|
+
var _typeof2 = _interopRequireDefault(require("@babel/runtime/helpers/typeof"));
|
|
5
|
+
var _latexBridge = require("../latex-bridge");
|
|
6
|
+
describe('latex-bridge', function () {
|
|
7
|
+
describe('toMathLive', function () {
|
|
8
|
+
it('passes plain latex through', function () {
|
|
9
|
+
expect((0, _latexBridge.toMathLive)('\\frac{1}{2}')).toEqual('\\frac{1}{2}');
|
|
10
|
+
});
|
|
11
|
+
it('handles empty/nullish', function () {
|
|
12
|
+
expect((0, _latexBridge.toMathLive)('')).toEqual('');
|
|
13
|
+
expect((0, _latexBridge.toMathLive)(undefined)).toEqual('');
|
|
14
|
+
expect((0, _latexBridge.toMathLive)(null)).toEqual('');
|
|
15
|
+
});
|
|
16
|
+
it('converts a newLine embed to a multiline environment', function () {
|
|
17
|
+
expect((0, _latexBridge.toMathLive)("a".concat(_latexBridge.NEWLINE_EMBED, "b"))).toEqual('\\displaylines{a \\\\ b}');
|
|
18
|
+
});
|
|
19
|
+
it('converts multiple newLine embeds', function () {
|
|
20
|
+
expect((0, _latexBridge.toMathLive)("a".concat(_latexBridge.NEWLINE_EMBED, "b").concat(_latexBridge.NEWLINE_EMBED, "c"))).toEqual('\\displaylines{a \\\\ b \\\\ c}');
|
|
21
|
+
});
|
|
22
|
+
it('converts MathQuill answer blocks to placeholders', function () {
|
|
23
|
+
expect((0, _latexBridge.toMathLive)('x=\\MathQuillMathField[r1]{}')).toEqual('x=\\placeholder[r1]{}');
|
|
24
|
+
});
|
|
25
|
+
it('preserves answer block content', function () {
|
|
26
|
+
expect((0, _latexBridge.toMathLive)('\\MathQuillMathField[r2]{7}')).toEqual('\\placeholder[r2]{7}');
|
|
27
|
+
});
|
|
28
|
+
it('defaults an unnamed answer block id', function () {
|
|
29
|
+
expect((0, _latexBridge.toMathLive)('\\MathQuillMathField[]{}')).toEqual("\\placeholder[".concat(_latexBridge.DEFAULT_FIELD_ID, "]{}"));
|
|
30
|
+
});
|
|
31
|
+
it('handles answer blocks and newlines together', function () {
|
|
32
|
+
expect((0, _latexBridge.toMathLive)("\\MathQuillMathField[r1]{}".concat(_latexBridge.NEWLINE_EMBED, "x"))).toEqual('\\displaylines{\\placeholder[r1]{} \\\\ x}');
|
|
33
|
+
});
|
|
34
|
+
});
|
|
35
|
+
describe('fromMathLive', function () {
|
|
36
|
+
it('passes plain latex through', function () {
|
|
37
|
+
expect((0, _latexBridge.fromMathLive)('\\frac{1}{2}')).toEqual('\\frac{1}{2}');
|
|
38
|
+
});
|
|
39
|
+
it('unwraps displaylines back to newLine embeds', function () {
|
|
40
|
+
expect((0, _latexBridge.fromMathLive)('\\displaylines{a \\\\ b}')).toEqual("a".concat(_latexBridge.NEWLINE_EMBED, "b"));
|
|
41
|
+
});
|
|
42
|
+
it('converts placeholders back to MathQuill answer blocks', function () {
|
|
43
|
+
expect((0, _latexBridge.fromMathLive)('x=\\placeholder[r1]{}')).toEqual('x=\\MathQuillMathField[r1]{}');
|
|
44
|
+
});
|
|
45
|
+
it('round-trips a newline document', function () {
|
|
46
|
+
var stored = "a".concat(_latexBridge.NEWLINE_EMBED, "b").concat(_latexBridge.NEWLINE_EMBED, "c");
|
|
47
|
+
expect((0, _latexBridge.fromMathLive)((0, _latexBridge.toMathLive)(stored))).toEqual(stored);
|
|
48
|
+
});
|
|
49
|
+
it('round-trips an answer-block document', function () {
|
|
50
|
+
var stored = 'x=\\MathQuillMathField[r1]{}+\\MathQuillMathField[r2]{3}';
|
|
51
|
+
expect((0, _latexBridge.fromMathLive)((0, _latexBridge.toMathLive)(stored))).toEqual(stored);
|
|
52
|
+
});
|
|
53
|
+
it('round-trips answer blocks combined with newlines', function () {
|
|
54
|
+
var stored = "\\MathQuillMathField[r1]{}".concat(_latexBridge.NEWLINE_EMBED, "y");
|
|
55
|
+
expect((0, _latexBridge.fromMathLive)((0, _latexBridge.toMathLive)(stored))).toEqual(stored);
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
describe('fieldIds', function () {
|
|
59
|
+
it('returns ids in document order', function () {
|
|
60
|
+
expect((0, _latexBridge.fieldIds)('\\MathQuillMathField[r1]{}+\\MathQuillMathField[r2]{}')).toEqual(['r1', 'r2']);
|
|
61
|
+
});
|
|
62
|
+
it('returns [] when there are none', function () {
|
|
63
|
+
expect((0, _latexBridge.fieldIds)('x+y')).toEqual([]);
|
|
64
|
+
expect((0, _latexBridge.fieldIds)(undefined)).toEqual([]);
|
|
65
|
+
});
|
|
66
|
+
it('is repeatable (regex lastIndex is reset)', function () {
|
|
67
|
+
var latex = '\\MathQuillMathField[r1]{}';
|
|
68
|
+
expect((0, _latexBridge.fieldIds)(latex)).toEqual(['r1']);
|
|
69
|
+
expect((0, _latexBridge.fieldIds)(latex)).toEqual(['r1']);
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
describe('keyToAction', function () {
|
|
73
|
+
it('prefers latex when there is no command', function () {
|
|
74
|
+
expect((0, _latexBridge.keyToAction)({
|
|
75
|
+
latex: '\\pi',
|
|
76
|
+
write: 'p'
|
|
77
|
+
})).toEqual({
|
|
78
|
+
type: 'insert',
|
|
79
|
+
value: '\\pi'
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
it('uses write', function () {
|
|
83
|
+
expect((0, _latexBridge.keyToAction)({
|
|
84
|
+
write: 'x'
|
|
85
|
+
})).toEqual({
|
|
86
|
+
type: 'insert',
|
|
87
|
+
value: 'x'
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
it('expands commands that take arguments into placeholders', function () {
|
|
91
|
+
expect((0, _latexBridge.keyToAction)({
|
|
92
|
+
command: '\\frac'
|
|
93
|
+
})).toEqual({
|
|
94
|
+
type: 'insert',
|
|
95
|
+
value: '\\frac{#?}{#?}'
|
|
96
|
+
});
|
|
97
|
+
expect((0, _latexBridge.keyToAction)({
|
|
98
|
+
command: '\\sqrt'
|
|
99
|
+
})).toEqual({
|
|
100
|
+
type: 'insert',
|
|
101
|
+
value: '\\sqrt{#?}'
|
|
102
|
+
});
|
|
103
|
+
expect((0, _latexBridge.keyToAction)({
|
|
104
|
+
command: '^'
|
|
105
|
+
})).toEqual({
|
|
106
|
+
type: 'insert',
|
|
107
|
+
value: '^{#?}'
|
|
108
|
+
});
|
|
109
|
+
});
|
|
110
|
+
it('passes argument-less commands through', function () {
|
|
111
|
+
expect((0, _latexBridge.keyToAction)({
|
|
112
|
+
command: '\\pi'
|
|
113
|
+
})).toEqual({
|
|
114
|
+
type: 'insert',
|
|
115
|
+
value: '\\pi'
|
|
116
|
+
});
|
|
117
|
+
});
|
|
118
|
+
it('concatenates array commands (MathQuill cmd() applied them in sequence)', function () {
|
|
119
|
+
// Measured Angle
|
|
120
|
+
expect((0, _latexBridge.keyToAction)({
|
|
121
|
+
command: ['m', '\\angle']
|
|
122
|
+
})).toEqual({
|
|
123
|
+
type: 'insert',
|
|
124
|
+
value: 'm\\angle'
|
|
125
|
+
});
|
|
126
|
+
// log base n - the `_` part must still expand to a placeholder
|
|
127
|
+
expect((0, _latexBridge.keyToAction)({
|
|
128
|
+
command: ['\\log', '_']
|
|
129
|
+
})).toEqual({
|
|
130
|
+
type: 'insert',
|
|
131
|
+
value: '\\log_{#?}'
|
|
132
|
+
});
|
|
133
|
+
});
|
|
134
|
+
it('always returns a string value for insert actions', function () {
|
|
135
|
+
[{
|
|
136
|
+
command: ['m', '\\angle']
|
|
137
|
+
}, {
|
|
138
|
+
command: '\\frac'
|
|
139
|
+
}, {
|
|
140
|
+
write: 'x'
|
|
141
|
+
}, {
|
|
142
|
+
latex: '\\pi'
|
|
143
|
+
}].forEach(function (k) {
|
|
144
|
+
expect((0, _typeof2["default"])((0, _latexBridge.keyToAction)(k).value)).toBe('string');
|
|
145
|
+
});
|
|
146
|
+
});
|
|
147
|
+
it('maps keystrokes to MathLive selectors', function () {
|
|
148
|
+
expect((0, _latexBridge.keyToAction)({
|
|
149
|
+
keystroke: 'Left'
|
|
150
|
+
})).toEqual({
|
|
151
|
+
type: 'command',
|
|
152
|
+
value: 'moveToPreviousChar'
|
|
153
|
+
});
|
|
154
|
+
expect((0, _latexBridge.keyToAction)({
|
|
155
|
+
keystroke: 'Right'
|
|
156
|
+
})).toEqual({
|
|
157
|
+
type: 'command',
|
|
158
|
+
value: 'moveToNextChar'
|
|
159
|
+
});
|
|
160
|
+
expect((0, _latexBridge.keyToAction)({
|
|
161
|
+
keystroke: 'Backspace'
|
|
162
|
+
})).toEqual({
|
|
163
|
+
type: 'command',
|
|
164
|
+
value: 'deleteBackward'
|
|
165
|
+
});
|
|
166
|
+
});
|
|
167
|
+
it('returns undefined for unknown/empty input', function () {
|
|
168
|
+
expect((0, _latexBridge.keyToAction)(undefined)).toBeUndefined();
|
|
169
|
+
expect((0, _latexBridge.keyToAction)({})).toBeUndefined();
|
|
170
|
+
expect((0, _latexBridge.keyToAction)({
|
|
171
|
+
keystroke: 'Nope'
|
|
172
|
+
})).toBeUndefined();
|
|
173
|
+
});
|
|
174
|
+
it('latex wins over command only when command is absent', function () {
|
|
175
|
+
// a key with both should use the command path (matches MathInput.keypadPress)
|
|
176
|
+
expect((0, _latexBridge.keyToAction)({
|
|
177
|
+
latex: '\\frac{}{}',
|
|
178
|
+
command: '\\frac'
|
|
179
|
+
})).toEqual({
|
|
180
|
+
type: 'insert',
|
|
181
|
+
value: '\\frac{#?}{#?}'
|
|
182
|
+
});
|
|
183
|
+
});
|
|
184
|
+
});
|
|
185
|
+
});
|
|
186
|
+
//# sourceMappingURL=latex-bridge.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"latex-bridge.test.js","names":["_latexBridge","require","describe","it","expect","toMathLive","toEqual","undefined","concat","NEWLINE_EMBED","DEFAULT_FIELD_ID","fromMathLive","stored","fieldIds","latex","keyToAction","write","type","value","command","forEach","k","_typeof2","toBe","keystroke","toBeUndefined"],"sources":["../../src/__tests__/latex-bridge.test.js"],"sourcesContent":["import {\n toMathLive,\n fromMathLive,\n fieldIds,\n keyToAction,\n NEWLINE_EMBED,\n DEFAULT_FIELD_ID,\n} from '../latex-bridge';\n\ndescribe('latex-bridge', () => {\n describe('toMathLive', () => {\n it('passes plain latex through', () => {\n expect(toMathLive('\\\\frac{1}{2}')).toEqual('\\\\frac{1}{2}');\n });\n\n it('handles empty/nullish', () => {\n expect(toMathLive('')).toEqual('');\n expect(toMathLive(undefined)).toEqual('');\n expect(toMathLive(null)).toEqual('');\n });\n\n it('converts a newLine embed to a multiline environment', () => {\n expect(toMathLive(`a${NEWLINE_EMBED}b`)).toEqual('\\\\displaylines{a \\\\\\\\ b}');\n });\n\n it('converts multiple newLine embeds', () => {\n expect(toMathLive(`a${NEWLINE_EMBED}b${NEWLINE_EMBED}c`)).toEqual('\\\\displaylines{a \\\\\\\\ b \\\\\\\\ c}');\n });\n\n it('converts MathQuill answer blocks to placeholders', () => {\n expect(toMathLive('x=\\\\MathQuillMathField[r1]{}')).toEqual('x=\\\\placeholder[r1]{}');\n });\n\n it('preserves answer block content', () => {\n expect(toMathLive('\\\\MathQuillMathField[r2]{7}')).toEqual('\\\\placeholder[r2]{7}');\n });\n\n it('defaults an unnamed answer block id', () => {\n expect(toMathLive('\\\\MathQuillMathField[]{}')).toEqual(`\\\\placeholder[${DEFAULT_FIELD_ID}]{}`);\n });\n\n it('handles answer blocks and newlines together', () => {\n expect(toMathLive(`\\\\MathQuillMathField[r1]{}${NEWLINE_EMBED}x`)).toEqual(\n '\\\\displaylines{\\\\placeholder[r1]{} \\\\\\\\ x}',\n );\n });\n });\n\n describe('fromMathLive', () => {\n it('passes plain latex through', () => {\n expect(fromMathLive('\\\\frac{1}{2}')).toEqual('\\\\frac{1}{2}');\n });\n\n it('unwraps displaylines back to newLine embeds', () => {\n expect(fromMathLive('\\\\displaylines{a \\\\\\\\ b}')).toEqual(`a${NEWLINE_EMBED}b`);\n });\n\n it('converts placeholders back to MathQuill answer blocks', () => {\n expect(fromMathLive('x=\\\\placeholder[r1]{}')).toEqual('x=\\\\MathQuillMathField[r1]{}');\n });\n\n it('round-trips a newline document', () => {\n const stored = `a${NEWLINE_EMBED}b${NEWLINE_EMBED}c`;\n\n expect(fromMathLive(toMathLive(stored))).toEqual(stored);\n });\n\n it('round-trips an answer-block document', () => {\n const stored = 'x=\\\\MathQuillMathField[r1]{}+\\\\MathQuillMathField[r2]{3}';\n\n expect(fromMathLive(toMathLive(stored))).toEqual(stored);\n });\n\n it('round-trips answer blocks combined with newlines', () => {\n const stored = `\\\\MathQuillMathField[r1]{}${NEWLINE_EMBED}y`;\n\n expect(fromMathLive(toMathLive(stored))).toEqual(stored);\n });\n });\n\n describe('fieldIds', () => {\n it('returns ids in document order', () => {\n expect(fieldIds('\\\\MathQuillMathField[r1]{}+\\\\MathQuillMathField[r2]{}')).toEqual(['r1', 'r2']);\n });\n\n it('returns [] when there are none', () => {\n expect(fieldIds('x+y')).toEqual([]);\n expect(fieldIds(undefined)).toEqual([]);\n });\n\n it('is repeatable (regex lastIndex is reset)', () => {\n const latex = '\\\\MathQuillMathField[r1]{}';\n\n expect(fieldIds(latex)).toEqual(['r1']);\n expect(fieldIds(latex)).toEqual(['r1']);\n });\n });\n\n describe('keyToAction', () => {\n it('prefers latex when there is no command', () => {\n expect(keyToAction({ latex: '\\\\pi', write: 'p' })).toEqual({ type: 'insert', value: '\\\\pi' });\n });\n\n it('uses write', () => {\n expect(keyToAction({ write: 'x' })).toEqual({ type: 'insert', value: 'x' });\n });\n\n it('expands commands that take arguments into placeholders', () => {\n expect(keyToAction({ command: '\\\\frac' })).toEqual({ type: 'insert', value: '\\\\frac{#?}{#?}' });\n expect(keyToAction({ command: '\\\\sqrt' })).toEqual({ type: 'insert', value: '\\\\sqrt{#?}' });\n expect(keyToAction({ command: '^' })).toEqual({ type: 'insert', value: '^{#?}' });\n });\n\n it('passes argument-less commands through', () => {\n expect(keyToAction({ command: '\\\\pi' })).toEqual({ type: 'insert', value: '\\\\pi' });\n });\n\n it('concatenates array commands (MathQuill cmd() applied them in sequence)', () => {\n // Measured Angle\n expect(keyToAction({ command: ['m', '\\\\angle'] })).toEqual({ type: 'insert', value: 'm\\\\angle' });\n // log base n - the `_` part must still expand to a placeholder\n expect(keyToAction({ command: ['\\\\log', '_'] })).toEqual({ type: 'insert', value: '\\\\log_{#?}' });\n });\n\n it('always returns a string value for insert actions', () => {\n [{ command: ['m', '\\\\angle'] }, { command: '\\\\frac' }, { write: 'x' }, { latex: '\\\\pi' }].forEach((k) => {\n expect(typeof keyToAction(k).value).toBe('string');\n });\n });\n\n it('maps keystrokes to MathLive selectors', () => {\n expect(keyToAction({ keystroke: 'Left' })).toEqual({ type: 'command', value: 'moveToPreviousChar' });\n expect(keyToAction({ keystroke: 'Right' })).toEqual({ type: 'command', value: 'moveToNextChar' });\n expect(keyToAction({ keystroke: 'Backspace' })).toEqual({ type: 'command', value: 'deleteBackward' });\n });\n\n it('returns undefined for unknown/empty input', () => {\n expect(keyToAction(undefined)).toBeUndefined();\n expect(keyToAction({})).toBeUndefined();\n expect(keyToAction({ keystroke: 'Nope' })).toBeUndefined();\n });\n\n it('latex wins over command only when command is absent', () => {\n // a key with both should use the command path (matches MathInput.keypadPress)\n expect(keyToAction({ latex: '\\\\frac{}{}', command: '\\\\frac' })).toEqual({\n type: 'insert',\n value: '\\\\frac{#?}{#?}',\n });\n });\n });\n});\n"],"mappings":"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","ignoreList":[]}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
|
|
4
|
+
var _typeof2 = _interopRequireDefault(require("@babel/runtime/helpers/typeof"));
|
|
5
|
+
describe('mathlive-instance', function () {
|
|
6
|
+
var instance;
|
|
7
|
+
beforeEach(function () {
|
|
8
|
+
jest.resetModules();
|
|
9
|
+
// eslint-disable-next-line global-require
|
|
10
|
+
instance = require('../mathlive-instance');
|
|
11
|
+
instance.__resetEmbeds();
|
|
12
|
+
});
|
|
13
|
+
describe('registerEmbed', function () {
|
|
14
|
+
it('registers a known embed and exposes it as a macro', function () {
|
|
15
|
+
instance.registerEmbed('answerBlock', function () {
|
|
16
|
+
return {
|
|
17
|
+
htmlString: '<div class="answerBlock"></div>',
|
|
18
|
+
latex: function latex() {
|
|
19
|
+
return '\\embed{answerBlock}[]';
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
});
|
|
23
|
+
expect(instance.registeredEmbeds()).toEqual(['answerBlock']);
|
|
24
|
+
// The macro tags output with data-embed so CSS can target it, replacing
|
|
25
|
+
// MathQuill's htmlString hook.
|
|
26
|
+
expect(instance.getMacros().answerBlock).toEqual('\\htmlData{embed=answerBlock}{\\placeholder{}}');
|
|
27
|
+
});
|
|
28
|
+
it('registers newLine as a harmless macro (it is handled by latex-bridge)', function () {
|
|
29
|
+
instance.registerEmbed('newLine', function () {
|
|
30
|
+
return {
|
|
31
|
+
latex: function latex() {
|
|
32
|
+
return '\\embed{newLine}[]';
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
});
|
|
36
|
+
expect(instance.getMacros().newLine).toEqual('\\htmlData{embed=newLine}{}');
|
|
37
|
+
});
|
|
38
|
+
it('falls back to a placeholder for an unknown embed', function () {
|
|
39
|
+
instance.registerEmbed('somethingCustom', function () {
|
|
40
|
+
return {
|
|
41
|
+
htmlString: '<b>x</b>'
|
|
42
|
+
};
|
|
43
|
+
});
|
|
44
|
+
expect(instance.getMacros().somethingCustom).toEqual('\\htmlData{embed=somethingCustom}{\\placeholder{}}');
|
|
45
|
+
});
|
|
46
|
+
it('honours an explicit mathliveLatex from the factory', function () {
|
|
47
|
+
instance.registerEmbed('custom', function () {
|
|
48
|
+
return {
|
|
49
|
+
mathliveLatex: '\\alpha'
|
|
50
|
+
};
|
|
51
|
+
});
|
|
52
|
+
expect(instance.getMacros().custom).toEqual('\\htmlData{embed=custom}{\\alpha}');
|
|
53
|
+
});
|
|
54
|
+
it('does not throw when the factory throws', function () {
|
|
55
|
+
expect(function () {
|
|
56
|
+
return instance.registerEmbed('bad', function () {
|
|
57
|
+
throw new Error('boom');
|
|
58
|
+
});
|
|
59
|
+
}).not.toThrow();
|
|
60
|
+
expect(instance.registeredEmbeds()).toEqual(['bad']);
|
|
61
|
+
});
|
|
62
|
+
it('ignores bad arguments', function () {
|
|
63
|
+
instance.registerEmbed(undefined, function () {
|
|
64
|
+
return {};
|
|
65
|
+
});
|
|
66
|
+
instance.registerEmbed('noFactory', undefined);
|
|
67
|
+
expect(instance.registeredEmbeds()).toEqual([]);
|
|
68
|
+
});
|
|
69
|
+
it('always includes the pie macro set', function () {
|
|
70
|
+
var macros = instance.getMacros();
|
|
71
|
+
expect(macros.parallelogram).toBeDefined();
|
|
72
|
+
expect(macros.longdiv).toBeDefined();
|
|
73
|
+
expect(macros.napprox).toEqual('\\not\\approx');
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
describe('applyStaticMath', function () {
|
|
77
|
+
it('returns undefined without an element', function () {
|
|
78
|
+
expect(instance.applyStaticMath(null, 'x')).toBeUndefined();
|
|
79
|
+
});
|
|
80
|
+
it('returns undefined when MathLive has not loaded', function () {
|
|
81
|
+
var el = document.createElement('div');
|
|
82
|
+
expect(instance.applyStaticMath(el, 'x')).toBeUndefined();
|
|
83
|
+
});
|
|
84
|
+
});
|
|
85
|
+
describe('configureFonts', function () {
|
|
86
|
+
// MathLive's default fontsDirectory is the relative './fonts/', which it
|
|
87
|
+
// resolves against its own (empty, when bundled) script url and throws
|
|
88
|
+
// "Invalid base URL" from loadFonts. Paths must become absolute.
|
|
89
|
+
it('is exported and callable before MathLive loads', function () {
|
|
90
|
+
expect((0, _typeof2["default"])(instance.configureFonts)).toBe('function');
|
|
91
|
+
expect(function () {
|
|
92
|
+
return instance.configureFonts('/mathlive-fonts');
|
|
93
|
+
}).not.toThrow();
|
|
94
|
+
});
|
|
95
|
+
it('accepts null to disable font loading', function () {
|
|
96
|
+
expect(function () {
|
|
97
|
+
return instance.configureFonts(null);
|
|
98
|
+
}).not.toThrow();
|
|
99
|
+
});
|
|
100
|
+
it('applies an absolute url to an already-loaded MathfieldElement', function () {
|
|
101
|
+
var MathfieldElement = {};
|
|
102
|
+
|
|
103
|
+
// simulate a loaded instance
|
|
104
|
+
instance.__setMathLiveForTest({
|
|
105
|
+
MathfieldElement: MathfieldElement
|
|
106
|
+
});
|
|
107
|
+
instance.configureFonts('/mathlive-fonts');
|
|
108
|
+
expect(MathfieldElement.fontsDirectory).toEqual("".concat(document.baseURI.replace(/\/$/, ''), "/mathlive-fonts"));
|
|
109
|
+
expect(MathfieldElement.fontsDirectory.startsWith('http')).toBe(true);
|
|
110
|
+
});
|
|
111
|
+
it('leaves a fully-qualified url untouched', function () {
|
|
112
|
+
var MathfieldElement = {};
|
|
113
|
+
instance.__setMathLiveForTest({
|
|
114
|
+
MathfieldElement: MathfieldElement
|
|
115
|
+
});
|
|
116
|
+
instance.configureFonts('https://cdn.example.com/fonts');
|
|
117
|
+
expect(MathfieldElement.fontsDirectory).toEqual('https://cdn.example.com/fonts');
|
|
118
|
+
});
|
|
119
|
+
it('passes null through rather than resolving it', function () {
|
|
120
|
+
var MathfieldElement = {};
|
|
121
|
+
instance.__setMathLiveForTest({
|
|
122
|
+
MathfieldElement: MathfieldElement
|
|
123
|
+
});
|
|
124
|
+
instance.configureFonts(null);
|
|
125
|
+
expect(MathfieldElement.fontsDirectory).toBeNull();
|
|
126
|
+
});
|
|
127
|
+
});
|
|
128
|
+
describe('latexToMarkup', function () {
|
|
129
|
+
it('returns empty string when MathLive is not loaded', function () {
|
|
130
|
+
expect(instance.latexToMarkup('\\pi')).toEqual('');
|
|
131
|
+
});
|
|
132
|
+
it('returns empty string for empty latex', function () {
|
|
133
|
+
expect(instance.latexToMarkup('')).toEqual('');
|
|
134
|
+
});
|
|
135
|
+
});
|
|
136
|
+
});
|
|
137
|
+
//# sourceMappingURL=mathlive-instance.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mathlive-instance.test.js","names":["describe","instance","beforeEach","jest","resetModules","require","__resetEmbeds","it","registerEmbed","htmlString","latex","expect","registeredEmbeds","toEqual","getMacros","answerBlock","newLine","somethingCustom","mathliveLatex","custom","Error","not","toThrow","undefined","macros","parallelogram","toBeDefined","longdiv","napprox","applyStaticMath","toBeUndefined","el","document","createElement","_typeof2","configureFonts","toBe","MathfieldElement","__setMathLiveForTest","fontsDirectory","concat","baseURI","replace","startsWith","toBeNull","latexToMarkup"],"sources":["../../src/__tests__/mathlive-instance.test.js"],"sourcesContent":["describe('mathlive-instance', () => {\n let instance;\n\n beforeEach(() => {\n jest.resetModules();\n // eslint-disable-next-line global-require\n instance = require('../mathlive-instance');\n instance.__resetEmbeds();\n });\n\n describe('registerEmbed', () => {\n it('registers a known embed and exposes it as a macro', () => {\n instance.registerEmbed('answerBlock', () => ({\n htmlString: '<div class=\"answerBlock\"></div>',\n latex: () => '\\\\embed{answerBlock}[]',\n }));\n\n expect(instance.registeredEmbeds()).toEqual(['answerBlock']);\n // The macro tags output with data-embed so CSS can target it, replacing\n // MathQuill's htmlString hook.\n expect(instance.getMacros().answerBlock).toEqual('\\\\htmlData{embed=answerBlock}{\\\\placeholder{}}');\n });\n\n it('registers newLine as a harmless macro (it is handled by latex-bridge)', () => {\n instance.registerEmbed('newLine', () => ({ latex: () => '\\\\embed{newLine}[]' }));\n\n expect(instance.getMacros().newLine).toEqual('\\\\htmlData{embed=newLine}{}');\n });\n\n it('falls back to a placeholder for an unknown embed', () => {\n instance.registerEmbed('somethingCustom', () => ({ htmlString: '<b>x</b>' }));\n\n expect(instance.getMacros().somethingCustom).toEqual('\\\\htmlData{embed=somethingCustom}{\\\\placeholder{}}');\n });\n\n it('honours an explicit mathliveLatex from the factory', () => {\n instance.registerEmbed('custom', () => ({ mathliveLatex: '\\\\alpha' }));\n\n expect(instance.getMacros().custom).toEqual('\\\\htmlData{embed=custom}{\\\\alpha}');\n });\n\n it('does not throw when the factory throws', () => {\n expect(() => instance.registerEmbed('bad', () => {\n throw new Error('boom');\n })).not.toThrow();\n\n expect(instance.registeredEmbeds()).toEqual(['bad']);\n });\n\n it('ignores bad arguments', () => {\n instance.registerEmbed(undefined, () => ({}));\n instance.registerEmbed('noFactory', undefined);\n\n expect(instance.registeredEmbeds()).toEqual([]);\n });\n\n it('always includes the pie macro set', () => {\n const macros = instance.getMacros();\n\n expect(macros.parallelogram).toBeDefined();\n expect(macros.longdiv).toBeDefined();\n expect(macros.napprox).toEqual('\\\\not\\\\approx');\n });\n });\n\n describe('applyStaticMath', () => {\n it('returns undefined without an element', () => {\n expect(instance.applyStaticMath(null, 'x')).toBeUndefined();\n });\n\n it('returns undefined when MathLive has not loaded', () => {\n const el = document.createElement('div');\n\n expect(instance.applyStaticMath(el, 'x')).toBeUndefined();\n });\n });\n\n describe('configureFonts', () => {\n // MathLive's default fontsDirectory is the relative './fonts/', which it\n // resolves against its own (empty, when bundled) script url and throws\n // \"Invalid base URL\" from loadFonts. Paths must become absolute.\n it('is exported and callable before MathLive loads', () => {\n expect(typeof instance.configureFonts).toBe('function');\n expect(() => instance.configureFonts('/mathlive-fonts')).not.toThrow();\n });\n\n it('accepts null to disable font loading', () => {\n expect(() => instance.configureFonts(null)).not.toThrow();\n });\n\n it('applies an absolute url to an already-loaded MathfieldElement', () => {\n const MathfieldElement = {};\n\n // simulate a loaded instance\n instance.__setMathLiveForTest({ MathfieldElement });\n instance.configureFonts('/mathlive-fonts');\n\n expect(MathfieldElement.fontsDirectory).toEqual(`${document.baseURI.replace(/\\/$/, '')}/mathlive-fonts`);\n expect(MathfieldElement.fontsDirectory.startsWith('http')).toBe(true);\n });\n\n it('leaves a fully-qualified url untouched', () => {\n const MathfieldElement = {};\n\n instance.__setMathLiveForTest({ MathfieldElement });\n instance.configureFonts('https://cdn.example.com/fonts');\n\n expect(MathfieldElement.fontsDirectory).toEqual('https://cdn.example.com/fonts');\n });\n\n it('passes null through rather than resolving it', () => {\n const MathfieldElement = {};\n\n instance.__setMathLiveForTest({ MathfieldElement });\n instance.configureFonts(null);\n\n expect(MathfieldElement.fontsDirectory).toBeNull();\n });\n });\n\n describe('latexToMarkup', () => {\n it('returns empty string when MathLive is not loaded', () => {\n expect(instance.latexToMarkup('\\\\pi')).toEqual('');\n });\n\n it('returns empty string for empty latex', () => {\n expect(instance.latexToMarkup('')).toEqual('');\n });\n });\n});\n"],"mappings":"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","ignoreList":[]}
|