@pie-lib/math-input-mathlive 1.0.7 → 1.0.8
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/lib/__tests__/composites.test.js +255 -0
- package/lib/__tests__/composites.test.js.map +1 -0
- package/lib/__tests__/keypad-render.test.js +206 -0
- package/lib/__tests__/keypad-render.test.js.map +1 -0
- package/lib/__tests__/mount-race.test.js +194 -0
- package/lib/__tests__/mount-race.test.js.map +1 -0
- package/lib/__tests__/static-prompts.test.js +282 -0
- package/lib/__tests__/static-prompts.test.js.map +1 -0
- package/lib/mathlive-instance.js +13 -0
- package/lib/mathlive-instance.js.map +1 -1
- package/lib/mf/input.js +18 -5
- package/lib/mf/input.js.map +1 -1
- package/lib/mf/static.js +17 -4
- package/lib/mf/static.js.map +1 -1
- package/package.json +1 -1
- package/src/__tests__/composites.test.jsx +220 -0
- package/src/__tests__/keypad-render.test.jsx +123 -0
- package/src/__tests__/mount-race.test.jsx +129 -0
- package/src/__tests__/static-prompts.test.jsx +251 -0
- package/src/mathlive-instance.js +12 -0
- package/src/mf/input.jsx +19 -5
- package/src/mf/static.jsx +20 -4
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { render } from '@testing-library/react';
|
|
3
|
+
|
|
4
|
+
import MathInput from '../math-input';
|
|
5
|
+
import HorizontalKeypad from '../horizontal-keypad';
|
|
6
|
+
import Input from '../mf/input';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* The pieces that sit between the keypad and the field: how a pressed key is
|
|
10
|
+
* routed into the field, and the legacy `{value,type}` shape math-toolbar
|
|
11
|
+
* consumes.
|
|
12
|
+
*/
|
|
13
|
+
describe('MathInput.keypadPress routing', () => {
|
|
14
|
+
const withInput = () => {
|
|
15
|
+
const mi = new MathInput({});
|
|
16
|
+
const calls = { write: [], keystroke: [] };
|
|
17
|
+
|
|
18
|
+
mi.input = {
|
|
19
|
+
write: (v) => calls.write.push(v),
|
|
20
|
+
keystroke: (v) => calls.keystroke.push(v),
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
return { mi, calls };
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
it('routes an insert action to write()', () => {
|
|
27
|
+
const { mi, calls } = withInput();
|
|
28
|
+
|
|
29
|
+
mi.keypadPress({ latex: '\\pi' });
|
|
30
|
+
|
|
31
|
+
expect(calls.write).toEqual(['\\pi']);
|
|
32
|
+
expect(calls.keystroke).toEqual([]);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it('expands an argument-taking command before writing', () => {
|
|
36
|
+
const { mi, calls } = withInput();
|
|
37
|
+
|
|
38
|
+
mi.keypadPress({ command: '\\frac' });
|
|
39
|
+
|
|
40
|
+
expect(calls.write).toEqual(['\\frac{#?}{#?}']);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it('routes a cursor keystroke to keystroke()', () => {
|
|
44
|
+
const { mi, calls } = withInput();
|
|
45
|
+
|
|
46
|
+
mi.keypadPress({ keystroke: 'Left' });
|
|
47
|
+
|
|
48
|
+
expect(calls.keystroke).toEqual(['moveToPreviousChar']);
|
|
49
|
+
expect(calls.write).toEqual([]);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it('ignores keys with no action', () => {
|
|
53
|
+
const { mi, calls } = withInput();
|
|
54
|
+
|
|
55
|
+
mi.keypadPress({});
|
|
56
|
+
mi.keypadPress({ keystroke: 'Nonsense' });
|
|
57
|
+
|
|
58
|
+
expect(calls.write).toEqual([]);
|
|
59
|
+
expect(calls.keystroke).toEqual([]);
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it('is a no-op before the field ref exists', () => {
|
|
63
|
+
const mi = new MathInput({});
|
|
64
|
+
|
|
65
|
+
expect(() => mi.keypadPress({ latex: '\\pi' })).not.toThrow();
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it('changeLatex only fires onChange when the value differs', () => {
|
|
69
|
+
const onChange = jest.fn();
|
|
70
|
+
const mi = new MathInput({ latex: 'x', onChange });
|
|
71
|
+
|
|
72
|
+
mi.changeLatex('x');
|
|
73
|
+
expect(onChange).not.toHaveBeenCalled();
|
|
74
|
+
|
|
75
|
+
mi.changeLatex('y');
|
|
76
|
+
expect(onChange).toHaveBeenCalledWith('y');
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
it('tracks focus state', () => {
|
|
80
|
+
const mi = new MathInput({});
|
|
81
|
+
|
|
82
|
+
mi.setState = (s) => Object.assign(mi.state, s);
|
|
83
|
+
mi.inputFocus();
|
|
84
|
+
expect(mi.state.focused).toBe(true);
|
|
85
|
+
|
|
86
|
+
mi.inputBlur();
|
|
87
|
+
expect(mi.state.focused).toBe(false);
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
describe('HorizontalKeypad legacy {value,type} model', () => {
|
|
92
|
+
const pressed = (key) => {
|
|
93
|
+
const onClick = jest.fn();
|
|
94
|
+
const hk = new HorizontalKeypad({ onClick });
|
|
95
|
+
|
|
96
|
+
hk.keypadPress(key);
|
|
97
|
+
|
|
98
|
+
return onClick.mock.calls[0][0];
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
it('maps a command to {value, type: command}', () => {
|
|
102
|
+
expect(pressed({ command: '\\frac' })).toEqual({ value: '\\frac', type: 'command' });
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
it('maps a write to {value}', () => {
|
|
106
|
+
expect(pressed({ write: 'x' })).toEqual({ value: 'x' });
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
it('maps a keystroke to {type: cursor}', () => {
|
|
110
|
+
expect(pressed({ keystroke: 'Left' })).toEqual({ type: 'cursor', value: 'Left' });
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
it('renders without throwing', () => {
|
|
114
|
+
expect(() => render(<HorizontalKeypad onClick={() => {}} />)).not.toThrow();
|
|
115
|
+
});
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
describe('Input: controlled-value sync and events', () => {
|
|
119
|
+
const withField = () => {
|
|
120
|
+
const input = new Input({});
|
|
121
|
+
|
|
122
|
+
input.mathField = {
|
|
123
|
+
value: '',
|
|
124
|
+
getValue: () => input.mathField.value,
|
|
125
|
+
insert(v) {
|
|
126
|
+
input.mathField.value += v;
|
|
127
|
+
},
|
|
128
|
+
executeCommand() {},
|
|
129
|
+
focus() {},
|
|
130
|
+
blur() {},
|
|
131
|
+
addEventListener() {},
|
|
132
|
+
removeEventListener() {},
|
|
133
|
+
remove() {},
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
return input;
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
it('emits stored-form latex on edit', () => {
|
|
140
|
+
const onChange = jest.fn();
|
|
141
|
+
const input = withField();
|
|
142
|
+
|
|
143
|
+
input.props = { onChange };
|
|
144
|
+
input.mathField.value = '\\longdiv{\\placeholder{}}';
|
|
145
|
+
input.onInputEdit();
|
|
146
|
+
|
|
147
|
+
// \placeholder is MathLive-internal and must not reach the model
|
|
148
|
+
expect(onChange).toHaveBeenCalledWith('\\longdiv{}');
|
|
149
|
+
expect(input.lastEmitted).toEqual('\\longdiv{}');
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
it('does not push a prop echo back into the field', () => {
|
|
153
|
+
const input = withField();
|
|
154
|
+
|
|
155
|
+
input.mathField.value = 'x';
|
|
156
|
+
input.lastEmitted = 'x';
|
|
157
|
+
input.props = { latex: 'x' };
|
|
158
|
+
|
|
159
|
+
input.componentDidUpdate({ latex: '' });
|
|
160
|
+
|
|
161
|
+
expect(input.mathField.value).toEqual('x');
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
it('pushes a genuinely new latex prop into the field', () => {
|
|
165
|
+
const input = withField();
|
|
166
|
+
|
|
167
|
+
input.mathField.value = 'x';
|
|
168
|
+
input.lastEmitted = 'x';
|
|
169
|
+
input.props = { latex: 'y' };
|
|
170
|
+
|
|
171
|
+
input.componentDidUpdate({ latex: 'x' });
|
|
172
|
+
|
|
173
|
+
expect(input.mathField.value).toEqual('y');
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
it('bridges stored latex on the way in', () => {
|
|
177
|
+
const input = withField();
|
|
178
|
+
|
|
179
|
+
input.props = { latex: '\\longdiv{5}' };
|
|
180
|
+
input.componentDidUpdate({ latex: '' });
|
|
181
|
+
|
|
182
|
+
// must become native \enclose so the content stays editable
|
|
183
|
+
expect(input.mathField.value).toEqual('\\enclose{longdiv}{5}');
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
it('toggles readOnly', () => {
|
|
187
|
+
const input = withField();
|
|
188
|
+
|
|
189
|
+
input.props = { readOnly: true };
|
|
190
|
+
input.componentDidUpdate({ readOnly: false });
|
|
191
|
+
|
|
192
|
+
expect(input.mathField.readOnly).toBe(true);
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
it('clear() empties the field', () => {
|
|
196
|
+
const input = withField();
|
|
197
|
+
|
|
198
|
+
input.mathField.value = 'abc';
|
|
199
|
+
expect(input.clear()).toEqual('');
|
|
200
|
+
expect(input.mathField.value).toEqual('');
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
it('suppresses Enter so it cannot insert a line break', () => {
|
|
204
|
+
const input = withField();
|
|
205
|
+
const preventDefault = jest.fn();
|
|
206
|
+
|
|
207
|
+
input.onKeyDown({ key: 'Enter', preventDefault });
|
|
208
|
+
expect(preventDefault).toHaveBeenCalled();
|
|
209
|
+
|
|
210
|
+
preventDefault.mockClear();
|
|
211
|
+
input.onKeyDown({ key: 'a', preventDefault });
|
|
212
|
+
expect(preventDefault).not.toHaveBeenCalled();
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
it('componentDidUpdate is safe before the field exists', () => {
|
|
216
|
+
const input = new Input({ latex: 'x' });
|
|
217
|
+
|
|
218
|
+
expect(() => input.componentDidUpdate({ latex: '' })).not.toThrow();
|
|
219
|
+
});
|
|
220
|
+
});
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { render } from '@testing-library/react';
|
|
3
|
+
|
|
4
|
+
import KeyPad, { LatexLabel } from '../keypad';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Keypad behaviour that is new in this package: labels are static markup from
|
|
8
|
+
* convertLatexToMarkup (the MathQuill version created a live StaticMath per
|
|
9
|
+
* button), with a raw-latex fallback when the engine is unavailable.
|
|
10
|
+
*/
|
|
11
|
+
describe('LatexLabel', () => {
|
|
12
|
+
it('falls back to raw latex when MathLive is unavailable', () => {
|
|
13
|
+
// jsdom cannot load MathLive, so latexToMarkup returns '' - the button must
|
|
14
|
+
// still show something rather than render blank.
|
|
15
|
+
const { container } = render(<LatexLabel latex={'\\pi'} />);
|
|
16
|
+
|
|
17
|
+
expect(container.textContent).toContain('\\pi');
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it('re-renders when the latex prop changes', () => {
|
|
21
|
+
const { container, rerender } = render(<LatexLabel latex={'\\pi'} />);
|
|
22
|
+
|
|
23
|
+
rerender(<LatexLabel latex={'\\theta'} />);
|
|
24
|
+
|
|
25
|
+
expect(container.textContent).toContain('\\theta');
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it('does not warn more than once about a missing engine', async () => {
|
|
29
|
+
LatexLabel.warned = false;
|
|
30
|
+
|
|
31
|
+
const a = new LatexLabel({ latex: '\\pi' });
|
|
32
|
+
const b = new LatexLabel({ latex: '\\theta' });
|
|
33
|
+
|
|
34
|
+
a.state = { markup: '' };
|
|
35
|
+
b.state = { markup: '' };
|
|
36
|
+
a.setState = () => {};
|
|
37
|
+
b.setState = () => {};
|
|
38
|
+
|
|
39
|
+
await a.componentDidMount();
|
|
40
|
+
await b.componentDidMount();
|
|
41
|
+
|
|
42
|
+
expect(LatexLabel.warned).toBe(true);
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
describe('KeyPad', () => {
|
|
47
|
+
const key = (over = {}) => ({ name: 'x', label: 'x', write: 'x', category: 'vars', ...over });
|
|
48
|
+
|
|
49
|
+
it('renders a button per key', () => {
|
|
50
|
+
const { container } = render(<KeyPad onPress={() => {}} baseSet={[[key(), key({ name: 'y', label: 'y' })]]} />);
|
|
51
|
+
|
|
52
|
+
expect(container.querySelectorAll('button').length).toBe(2);
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it('calls onPress with the whole key definition', () => {
|
|
56
|
+
const onPress = jest.fn();
|
|
57
|
+
const k = key();
|
|
58
|
+
const { container } = render(<KeyPad onPress={onPress} baseSet={[[k]]} />);
|
|
59
|
+
|
|
60
|
+
container.querySelector('button').click();
|
|
61
|
+
|
|
62
|
+
expect(onPress).toHaveBeenCalledWith(k);
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it('renders a latex key through LatexLabel', () => {
|
|
66
|
+
const { container } = render(<KeyPad onPress={() => {}} baseSet={[[{ name: 'pi', latex: '\\pi' }]]} />);
|
|
67
|
+
|
|
68
|
+
expect(container.textContent).toContain('\\pi');
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it('disables the decimal separators when noDecimal is set', () => {
|
|
72
|
+
const dot = key({ name: '.', label: '.', write: '.' });
|
|
73
|
+
const comma = key({ name: ',', label: ',', write: ',' });
|
|
74
|
+
const { container } = render(<KeyPad onPress={() => {}} noDecimal baseSet={[[dot, comma]]} />);
|
|
75
|
+
|
|
76
|
+
const disabled = Array.from(container.querySelectorAll('button')).filter((b) => b.disabled);
|
|
77
|
+
|
|
78
|
+
expect(disabled.length).toBe(2);
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
it('leaves the separators enabled otherwise', () => {
|
|
82
|
+
const dot = key({ name: '.', label: '.', write: '.' });
|
|
83
|
+
const { container } = render(<KeyPad onPress={() => {}} baseSet={[[dot]]} />);
|
|
84
|
+
|
|
85
|
+
expect(container.querySelector('button').disabled).toBe(false);
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it('renders a placeholder for empty grid slots', () => {
|
|
89
|
+
expect(() => render(<KeyPad onPress={() => {}} baseSet={[[null]]} />)).not.toThrow();
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
it('notifies setKeypadInteraction on pointer interaction', () => {
|
|
93
|
+
const setKeypadInteraction = jest.fn();
|
|
94
|
+
const { container } = render(
|
|
95
|
+
<KeyPad onPress={() => {}} setKeypadInteraction={setKeypadInteraction} baseSet={[[key()]]} />,
|
|
96
|
+
);
|
|
97
|
+
|
|
98
|
+
container.firstChild.dispatchEvent(new MouseEvent('mousedown', { bubbles: true }));
|
|
99
|
+
|
|
100
|
+
expect(setKeypadInteraction).toHaveBeenCalledWith(true);
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
it('uses the mode as a class so mode-specific styling applies', () => {
|
|
104
|
+
const { container } = render(<KeyPad onPress={() => {}} mode="language" baseSet={[[key()]]} />);
|
|
105
|
+
|
|
106
|
+
expect(container.firstChild.className).toContain('language');
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
it('drops the base set for the modes that supply their own keys', () => {
|
|
110
|
+
const { container } = render(
|
|
111
|
+
<KeyPad onPress={() => {}} mode="language" baseSet={[[key(), key()]]} additionalKeys={[[key()]]} />,
|
|
112
|
+
);
|
|
113
|
+
|
|
114
|
+
// base set ignored, only the additional key renders
|
|
115
|
+
expect(container.querySelectorAll('button').length).toBe(1);
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
it('unmounts without throwing', () => {
|
|
119
|
+
const { unmount } = render(<KeyPad onPress={() => {}} controlledKeypadMode baseSet={[[key()]]} />);
|
|
120
|
+
|
|
121
|
+
expect(() => unmount()).not.toThrow();
|
|
122
|
+
});
|
|
123
|
+
});
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MathLive loads asynchronously, so `componentDidMount` resumes after an await
|
|
3
|
+
* before appending a <math-field>. Two orderings must both hold:
|
|
4
|
+
*
|
|
5
|
+
* - StrictMode dev double-mount (mount -> unmount -> mount on the SAME
|
|
6
|
+
* instance) must end up with exactly ONE field. A boolean "unmounted" flag
|
|
7
|
+
* latches here and blocks the remount entirely - which is why this uses a
|
|
8
|
+
* generation counter.
|
|
9
|
+
* - A genuine unmount while the load is in flight must append nothing.
|
|
10
|
+
*
|
|
11
|
+
* MathLive cannot load under jsdom, so it is mocked with a minimal stub in
|
|
12
|
+
* order to actually count appended fields.
|
|
13
|
+
*/
|
|
14
|
+
jest.mock('../mathlive-instance', () => {
|
|
15
|
+
// Must be a real DOM node: the component uses replaceChildren(), which
|
|
16
|
+
// rejects anything that is not a Node. Returning an element from the
|
|
17
|
+
// constructor replaces `this`, so callers get a genuine <math-field>.
|
|
18
|
+
class FakeMathfieldElement {
|
|
19
|
+
constructor() {
|
|
20
|
+
// `global` rather than `document`: jest forbids out-of-scope variables in
|
|
21
|
+
// a mock factory, and `global` is on its allow-list.
|
|
22
|
+
const el = global.document.createElement('math-field');
|
|
23
|
+
|
|
24
|
+
el.value = '';
|
|
25
|
+
el.getValue = () => el.value;
|
|
26
|
+
|
|
27
|
+
return el;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return {
|
|
32
|
+
loadMathLive: () => Promise.resolve({ MathfieldElement: FakeMathfieldElement }),
|
|
33
|
+
getMacros: () => ({}),
|
|
34
|
+
// must mirror the real export, otherwise the component sets `undefined`
|
|
35
|
+
MATH_MODE_SPACE: '\\ ',
|
|
36
|
+
latexToMarkup: (l) => `<span>${l}</span>`,
|
|
37
|
+
};
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
// eslint-disable-next-line import/first
|
|
41
|
+
import Input from '../mf/input';
|
|
42
|
+
|
|
43
|
+
const holderFor = (cmp) => {
|
|
44
|
+
const el = document.createElement('span');
|
|
45
|
+
|
|
46
|
+
cmp.holderRef = { current: el };
|
|
47
|
+
|
|
48
|
+
return el;
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
const fieldCount = (el) => Array.from(el.childNodes).filter((n) => n.tagName === 'MATH-FIELD').length;
|
|
52
|
+
|
|
53
|
+
describe('async mount race', () => {
|
|
54
|
+
it('StrictMode mount -> unmount -> mount yields exactly one field', async () => {
|
|
55
|
+
const input = new Input({});
|
|
56
|
+
const el = holderFor(input);
|
|
57
|
+
|
|
58
|
+
const first = input.componentDidMount();
|
|
59
|
+
|
|
60
|
+
input.componentWillUnmount();
|
|
61
|
+
|
|
62
|
+
const second = input.componentDidMount();
|
|
63
|
+
|
|
64
|
+
await Promise.all([first, second]);
|
|
65
|
+
|
|
66
|
+
expect(fieldCount(el)).toBe(1);
|
|
67
|
+
expect(input.mathField).toBeDefined();
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it('appends nothing when unmounted while the load is in flight', async () => {
|
|
71
|
+
const input = new Input({});
|
|
72
|
+
const el = holderFor(input);
|
|
73
|
+
|
|
74
|
+
const pending = input.componentDidMount();
|
|
75
|
+
|
|
76
|
+
input.componentWillUnmount();
|
|
77
|
+
|
|
78
|
+
await pending;
|
|
79
|
+
|
|
80
|
+
expect(fieldCount(el)).toBe(0);
|
|
81
|
+
expect(input.mathField).toBeUndefined();
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
it('a normal single mount creates the field', async () => {
|
|
85
|
+
const input = new Input({});
|
|
86
|
+
const el = holderFor(input);
|
|
87
|
+
|
|
88
|
+
await input.componentDidMount();
|
|
89
|
+
|
|
90
|
+
expect(fieldCount(el)).toBe(1);
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
it('repeated mounts never accumulate fields', async () => {
|
|
94
|
+
const input = new Input({});
|
|
95
|
+
const el = holderFor(input);
|
|
96
|
+
|
|
97
|
+
await input.componentDidMount();
|
|
98
|
+
await input.componentDidMount();
|
|
99
|
+
await input.componentDidMount();
|
|
100
|
+
|
|
101
|
+
expect(fieldCount(el)).toBe(1);
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
// MathLive's mathModeSpace defaults to '', which makes the spacebar a no-op.
|
|
105
|
+
it('configures the spacebar so a space can be typed', async () => {
|
|
106
|
+
const input = new Input({});
|
|
107
|
+
|
|
108
|
+
holderFor(input);
|
|
109
|
+
await input.componentDidMount();
|
|
110
|
+
|
|
111
|
+
// a LaTeX control space - what MathQuill produced, and MathJax understands
|
|
112
|
+
expect(input.mathField.mathModeSpace).toEqual('\\ ');
|
|
113
|
+
expect(input.mathField.mathModeSpace).not.toEqual('');
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
it('reports itself through innerRef on mount and clears it on unmount', async () => {
|
|
117
|
+
const innerRef = jest.fn();
|
|
118
|
+
const input = new Input({ innerRef });
|
|
119
|
+
|
|
120
|
+
holderFor(input);
|
|
121
|
+
await input.componentDidMount();
|
|
122
|
+
|
|
123
|
+
expect(innerRef).toHaveBeenCalledWith(input);
|
|
124
|
+
|
|
125
|
+
input.componentWillUnmount();
|
|
126
|
+
|
|
127
|
+
expect(innerRef).toHaveBeenLastCalledWith(null);
|
|
128
|
+
});
|
|
129
|
+
});
|