@pie-lib/render-ui 7.0.2 → 8.0.0-next.20261002094057
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/dist/_virtual/_rolldown/runtime.js +11 -0
- package/dist/assets/enableAudioAutoplayImage.d.ts +10 -0
- package/dist/assets/enableAudioAutoplayImage.js +4 -0
- package/dist/collapsible/index.d.ts +31 -0
- package/dist/collapsible/index.js +53 -0
- package/dist/color.d.ts +128 -0
- package/dist/color.js +132 -0
- package/dist/feedback.d.ts +20 -0
- package/dist/feedback.js +65 -0
- package/dist/has-media.d.ts +9 -0
- package/dist/has-media.js +10 -0
- package/dist/has-text.d.ts +9 -0
- package/dist/has-text.js +10 -0
- package/dist/html-and-math.d.ts +22 -0
- package/dist/html-and-math.js +25 -0
- package/dist/index.d.ts +26 -0
- package/dist/index.js +18 -0
- package/dist/inline-menu.d.ts +33 -0
- package/dist/inline-menu.js +29 -0
- package/dist/input-container.d.ts +23 -0
- package/dist/input-container.js +39 -0
- package/dist/preview-layout.d.ts +24 -0
- package/dist/preview-layout.js +37 -0
- package/dist/preview-prompt.d.ts +39 -0
- package/dist/preview-prompt.js +153 -0
- package/dist/purpose.d.ts +18 -0
- package/dist/purpose.js +11 -0
- package/dist/readable.d.ts +18 -0
- package/dist/readable.js +11 -0
- package/dist/response-indicators.d.ts +121 -0
- package/dist/response-indicators.js +62 -0
- package/{src/transform-headings.js → dist/transform-headings.d.ts} +9 -30
- package/dist/transform-headings.js +15 -0
- package/dist/ui-layout.d.ts +31 -0
- package/dist/ui-layout.js +55 -0
- package/dist/withUndoReset.d.ts +47 -0
- package/dist/withUndoReset.js +84 -0
- package/package.json +29 -20
- package/CHANGELOG.json +0 -1
- package/CHANGELOG.md +0 -1184
- package/LICENSE.md +0 -5
- package/lib/assets/enableAudioAutoplayImage.js +0 -8
- package/lib/assets/enableAudioAutoplayImage.js.map +0 -1
- package/lib/collapsible/index.js +0 -105
- package/lib/collapsible/index.js.map +0 -1
- package/lib/color.js +0 -253
- package/lib/color.js.map +0 -1
- package/lib/feedback.js +0 -112
- package/lib/feedback.js.map +0 -1
- package/lib/has-media.js +0 -22
- package/lib/has-media.js.map +0 -1
- package/lib/has-text.js +0 -22
- package/lib/has-text.js.map +0 -1
- package/lib/html-and-math.js +0 -54
- package/lib/html-and-math.js.map +0 -1
- package/lib/index.js +0 -113
- package/lib/index.js.map +0 -1
- package/lib/input-container.js +0 -59
- package/lib/input-container.js.map +0 -1
- package/lib/preview-layout.js +0 -65
- package/lib/preview-layout.js.map +0 -1
- package/lib/preview-prompt.js +0 -316
- package/lib/preview-prompt.js.map +0 -1
- package/lib/purpose.js +0 -22
- package/lib/purpose.js.map +0 -1
- package/lib/readable.js +0 -22
- package/lib/readable.js.map +0 -1
- package/lib/response-indicators.js +0 -102
- package/lib/response-indicators.js.map +0 -1
- package/lib/transform-headings.js +0 -53
- package/lib/transform-headings.js.map +0 -1
- package/lib/ui-layout.js +0 -125
- package/lib/ui-layout.js.map +0 -1
- package/lib/withUndoReset.js +0 -135
- package/lib/withUndoReset.js.map +0 -1
- package/src/__tests__/color.test.js +0 -259
- package/src/__tests__/feedback.test.jsx +0 -279
- package/src/__tests__/has-media.test.js +0 -19
- package/src/__tests__/has-text.test.js +0 -20
- package/src/__tests__/html-and-math.test.js +0 -36
- package/src/__tests__/input-container.test.jsx +0 -328
- package/src/__tests__/preview-layout.test.jsx +0 -349
- package/src/__tests__/preview-prompt.test.jsx +0 -464
- package/src/__tests__/purpose.test.jsx +0 -51
- package/src/__tests__/readable.test.jsx +0 -69
- package/src/__tests__/response-indicators.test.jsx +0 -111
- package/src/__tests__/ui-layout.test.jsx +0 -52
- package/src/__tests__/withUndoReset.test.jsx +0 -176
- package/src/assets/enableAudioAutoplayImage.js +0 -1
- package/src/collapsible/__tests__/index.test.jsx +0 -39
- package/src/collapsible/index.jsx +0 -64
- package/src/color.js +0 -151
- package/src/feedback.jsx +0 -84
- package/src/has-media.js +0 -16
- package/src/has-text.js +0 -18
- package/src/html-and-math.js +0 -21
- package/src/index.js +0 -35
- package/src/input-container.jsx +0 -47
- package/src/preview-layout.jsx +0 -41
- package/src/preview-prompt.jsx +0 -300
- package/src/purpose.jsx +0 -17
- package/src/readable.jsx +0 -19
- package/src/response-indicators.jsx +0 -82
- package/src/ui-layout.jsx +0 -96
- package/src/withUndoReset.jsx +0 -114
|
@@ -1,464 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import { render, screen } from '@testing-library/react';
|
|
3
|
-
import userEvent from '@testing-library/user-event';
|
|
4
|
-
import PreviewPrompt from '../preview-prompt';
|
|
5
|
-
import * as color from '../color';
|
|
6
|
-
|
|
7
|
-
jest.mock('@pie-lib/math-rendering', () => ({
|
|
8
|
-
renderMath: jest.fn(),
|
|
9
|
-
}));
|
|
10
|
-
|
|
11
|
-
const { renderMath } = require('@pie-lib/math-rendering');
|
|
12
|
-
|
|
13
|
-
describe('Prompt without Custom tag', () => {
|
|
14
|
-
const defaultProps = {
|
|
15
|
-
classes: {},
|
|
16
|
-
prompt:
|
|
17
|
-
'Which of these northern European countries are EU members? <math><mstack><msrow><mn>111</mn></msrow><msline/></mstack></math>',
|
|
18
|
-
tagName: '',
|
|
19
|
-
className: '',
|
|
20
|
-
};
|
|
21
|
-
|
|
22
|
-
describe('default rendering with markup', () => {
|
|
23
|
-
it('renders the prompt text', () => {
|
|
24
|
-
render(<PreviewPrompt {...defaultProps} />);
|
|
25
|
-
expect(screen.getByText(/Which of these northern European countries are EU members/)).toBeInTheDocument();
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
it('does not have prompt class by default', () => {
|
|
29
|
-
const { container } = render(<PreviewPrompt {...defaultProps} />);
|
|
30
|
-
expect(container.firstChild).not.toHaveClass('prompt');
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
it('renders math markup', () => {
|
|
34
|
-
const { container } = render(<PreviewPrompt {...defaultProps} />);
|
|
35
|
-
// Math may be rendered but transformed by the math rendering library
|
|
36
|
-
// Just check that the prompt text is rendered
|
|
37
|
-
expect(screen.getByText(/Which of these northern European countries are EU members/)).toBeInTheDocument();
|
|
38
|
-
});
|
|
39
|
-
});
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
describe('Prompt with Custom tag', () => {
|
|
43
|
-
const defaultProps = {
|
|
44
|
-
classes: {},
|
|
45
|
-
prompt:
|
|
46
|
-
'Which of these northern European countries are EU members? <math><mstack><msrow><mn>111</mn></msrow><msline/></mstack></math>',
|
|
47
|
-
tagName: 'span',
|
|
48
|
-
className: 'prompt',
|
|
49
|
-
};
|
|
50
|
-
|
|
51
|
-
describe('renders with custom tag "span" correctly', () => {
|
|
52
|
-
it('renders with custom className', () => {
|
|
53
|
-
const { container } = render(<PreviewPrompt {...defaultProps} />);
|
|
54
|
-
expect(container.firstChild).toHaveClass('prompt');
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
it('renders as span element when tagName is specified', () => {
|
|
58
|
-
const { container } = render(<PreviewPrompt {...defaultProps} />);
|
|
59
|
-
expect(container.firstChild.tagName.toLowerCase()).toBe('span');
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
it('renders the prompt text', () => {
|
|
63
|
-
render(<PreviewPrompt {...defaultProps} />);
|
|
64
|
-
expect(screen.getByText(/Which of these northern European countries are EU members/)).toBeInTheDocument();
|
|
65
|
-
});
|
|
66
|
-
});
|
|
67
|
-
});
|
|
68
|
-
|
|
69
|
-
describe('PreviewPrompt - Extended Tests', () => {
|
|
70
|
-
beforeEach(() => {
|
|
71
|
-
jest.clearAllMocks();
|
|
72
|
-
document.body.innerHTML = '';
|
|
73
|
-
});
|
|
74
|
-
|
|
75
|
-
describe('tagName variations', () => {
|
|
76
|
-
it('should render as legend when tagName is "legend"', () => {
|
|
77
|
-
const { container } = render(<PreviewPrompt prompt="Legend text" tagName="legend" />);
|
|
78
|
-
expect(container.firstChild.tagName.toLowerCase()).toBe('legend');
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
it('should render as div by default when no tagName', () => {
|
|
82
|
-
const { container } = render(<PreviewPrompt prompt="Default text" />);
|
|
83
|
-
expect(container.firstChild.tagName.toLowerCase()).toBe('div');
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
it('should apply legend class when tagName is "legend"', () => {
|
|
87
|
-
const { container } = render(<PreviewPrompt prompt="Legend text" tagName="legend" />);
|
|
88
|
-
expect(container.firstChild).toHaveClass('legend');
|
|
89
|
-
});
|
|
90
|
-
|
|
91
|
-
it('should render as p tag when specified', () => {
|
|
92
|
-
const { container } = render(<PreviewPrompt prompt="Paragraph text" tagName="p" />);
|
|
93
|
-
expect(container.firstChild.tagName.toLowerCase()).toBe('p');
|
|
94
|
-
});
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
describe('className handling', () => {
|
|
98
|
-
it('should apply single className', () => {
|
|
99
|
-
const { container } = render(<PreviewPrompt prompt="Test" className="custom-class" />);
|
|
100
|
-
expect(container.firstChild).toHaveClass('custom-class');
|
|
101
|
-
});
|
|
102
|
-
|
|
103
|
-
it('should apply defaultClassName', () => {
|
|
104
|
-
const { container } = render(<PreviewPrompt prompt="Test" defaultClassName="default-class" />);
|
|
105
|
-
expect(container.firstChild).toHaveClass('default-class');
|
|
106
|
-
});
|
|
107
|
-
|
|
108
|
-
it('should combine className and defaultClassName', () => {
|
|
109
|
-
const { container } = render(<PreviewPrompt prompt="Test" className="custom" defaultClassName="default" />);
|
|
110
|
-
expect(container.firstChild).toHaveClass('custom');
|
|
111
|
-
expect(container.firstChild).toHaveClass('default');
|
|
112
|
-
});
|
|
113
|
-
|
|
114
|
-
it('should add legend class when tagName is legend', () => {
|
|
115
|
-
const { container } = render(<PreviewPrompt prompt="Test" tagName="legend" className="custom" />);
|
|
116
|
-
expect(container.firstChild).toHaveClass('custom');
|
|
117
|
-
expect(container.firstChild).toHaveClass('legend');
|
|
118
|
-
});
|
|
119
|
-
});
|
|
120
|
-
|
|
121
|
-
describe('onClick handler', () => {
|
|
122
|
-
it('should call onClick when clicked', async () => {
|
|
123
|
-
const onClick = jest.fn();
|
|
124
|
-
const user = userEvent.setup();
|
|
125
|
-
|
|
126
|
-
render(<PreviewPrompt prompt="Clickable text" onClick={onClick} />);
|
|
127
|
-
|
|
128
|
-
await user.click(screen.getByText('Clickable text'));
|
|
129
|
-
expect(onClick).toHaveBeenCalledTimes(1);
|
|
130
|
-
});
|
|
131
|
-
|
|
132
|
-
it('should work without onClick handler', async () => {
|
|
133
|
-
const user = userEvent.setup();
|
|
134
|
-
|
|
135
|
-
render(<PreviewPrompt prompt="Text without handler" />);
|
|
136
|
-
|
|
137
|
-
await user.click(screen.getByText('Text without handler'));
|
|
138
|
-
});
|
|
139
|
-
|
|
140
|
-
it('should call onClick multiple times', async () => {
|
|
141
|
-
const onClick = jest.fn();
|
|
142
|
-
const user = userEvent.setup();
|
|
143
|
-
|
|
144
|
-
render(<PreviewPrompt prompt="Multi-click text" onClick={onClick} />);
|
|
145
|
-
|
|
146
|
-
await user.click(screen.getByText('Multi-click text'));
|
|
147
|
-
await user.click(screen.getByText('Multi-click text'));
|
|
148
|
-
await user.click(screen.getByText('Multi-click text'));
|
|
149
|
-
|
|
150
|
-
expect(onClick).toHaveBeenCalledTimes(3);
|
|
151
|
-
});
|
|
152
|
-
});
|
|
153
|
-
|
|
154
|
-
describe('newline replacement', () => {
|
|
155
|
-
it('should replace \\embed{newLine}[] with \\newline', () => {
|
|
156
|
-
const promptWithNewline = 'Line 1\\embed{newLine}[]Line 2';
|
|
157
|
-
const { container } = render(<PreviewPrompt prompt={promptWithNewline} />);
|
|
158
|
-
|
|
159
|
-
expect(container.innerHTML).toContain('\\newline');
|
|
160
|
-
expect(container.innerHTML).not.toContain('\\embed{newLine}[]');
|
|
161
|
-
});
|
|
162
|
-
|
|
163
|
-
it('should replace multiple newlines', () => {
|
|
164
|
-
const promptWithNewlines = 'A\\embed{newLine}[]B\\embed{newLine}[]C';
|
|
165
|
-
const { container } = render(<PreviewPrompt prompt={promptWithNewlines} />);
|
|
166
|
-
|
|
167
|
-
const newlineCount = (container.innerHTML.match(/\\newline/g) || []).length;
|
|
168
|
-
expect(newlineCount).toBe(2);
|
|
169
|
-
});
|
|
170
|
-
|
|
171
|
-
it('should handle prompt without newlines', () => {
|
|
172
|
-
const normalPrompt = 'Normal text without newlines';
|
|
173
|
-
render(<PreviewPrompt prompt={normalPrompt} />);
|
|
174
|
-
|
|
175
|
-
expect(screen.getByText('Normal text without newlines')).toBeInTheDocument();
|
|
176
|
-
});
|
|
177
|
-
});
|
|
178
|
-
|
|
179
|
-
describe('audio handling', () => {
|
|
180
|
-
it('should parse audio tag and add source element', () => {
|
|
181
|
-
const promptWithAudio = '<audio src="test.mp3"></audio>';
|
|
182
|
-
render(<PreviewPrompt prompt={promptWithAudio} />);
|
|
183
|
-
|
|
184
|
-
const audio = document.querySelector('audio');
|
|
185
|
-
expect(audio).toBeInTheDocument();
|
|
186
|
-
expect(audio).toHaveAttribute('id', 'pie-prompt-audio-player');
|
|
187
|
-
|
|
188
|
-
const source = audio.querySelector('source');
|
|
189
|
-
expect(source).toBeInTheDocument();
|
|
190
|
-
expect(source).toHaveAttribute('src', 'test.mp3');
|
|
191
|
-
expect(source).toHaveAttribute('type', 'audio/mp3');
|
|
192
|
-
});
|
|
193
|
-
|
|
194
|
-
it('should remove src attribute from audio tag after adding source', () => {
|
|
195
|
-
const promptWithAudio = '<audio src="test.mp3"></audio>';
|
|
196
|
-
render(<PreviewPrompt prompt={promptWithAudio} />);
|
|
197
|
-
|
|
198
|
-
const audio = document.querySelector('audio');
|
|
199
|
-
expect(audio).not.toHaveAttribute('src');
|
|
200
|
-
});
|
|
201
|
-
|
|
202
|
-
it('should handle custom audio button', () => {
|
|
203
|
-
const customAudioButton = {
|
|
204
|
-
playImage: 'play.png',
|
|
205
|
-
pauseImage: 'pause.png',
|
|
206
|
-
};
|
|
207
|
-
const promptWithAudio = '<audio src="test.mp3"></audio>';
|
|
208
|
-
|
|
209
|
-
render(<PreviewPrompt prompt={promptWithAudio} customAudioButton={customAudioButton} />);
|
|
210
|
-
|
|
211
|
-
const audio = document.getElementById('pie-prompt-audio-player');
|
|
212
|
-
expect(audio).toHaveStyle({ display: 'none' });
|
|
213
|
-
|
|
214
|
-
const playButton = document.getElementById('play-audio-button');
|
|
215
|
-
expect(playButton).toBeInTheDocument();
|
|
216
|
-
});
|
|
217
|
-
|
|
218
|
-
it('should not add custom button when customAudioButton is not provided', () => {
|
|
219
|
-
const promptWithAudio = '<audio src="test.mp3"></audio>';
|
|
220
|
-
render(<PreviewPrompt prompt={promptWithAudio} />);
|
|
221
|
-
|
|
222
|
-
const playButton = document.getElementById('play-audio-button');
|
|
223
|
-
expect(playButton).not.toBeInTheDocument();
|
|
224
|
-
});
|
|
225
|
-
});
|
|
226
|
-
|
|
227
|
-
describe('image alignment', () => {
|
|
228
|
-
it('should align images with alignment attribute', () => {
|
|
229
|
-
const promptWithImage = '<img src="test.jpg" alignment="center" />';
|
|
230
|
-
render(<PreviewPrompt prompt={promptWithImage} />);
|
|
231
|
-
|
|
232
|
-
const images = document.querySelectorAll('img');
|
|
233
|
-
expect(images.length).toBeGreaterThan(0);
|
|
234
|
-
});
|
|
235
|
-
|
|
236
|
-
it('should handle images without alignment attribute', () => {
|
|
237
|
-
const promptWithImage = '<img src="test.jpg" />';
|
|
238
|
-
render(<PreviewPrompt prompt={promptWithImage} />);
|
|
239
|
-
|
|
240
|
-
const img = document.querySelector('img');
|
|
241
|
-
expect(img).toBeInTheDocument();
|
|
242
|
-
});
|
|
243
|
-
});
|
|
244
|
-
|
|
245
|
-
describe('edge cases', () => {
|
|
246
|
-
it('should handle empty prompt', () => {
|
|
247
|
-
const { container } = render(<PreviewPrompt prompt="" />);
|
|
248
|
-
expect(container.firstChild).toBeInTheDocument();
|
|
249
|
-
});
|
|
250
|
-
|
|
251
|
-
it('should handle undefined prompt', () => {
|
|
252
|
-
const { container } = render(<PreviewPrompt prompt={undefined} />);
|
|
253
|
-
expect(container.firstChild).toBeInTheDocument();
|
|
254
|
-
});
|
|
255
|
-
|
|
256
|
-
it('should handle null prompt', () => {
|
|
257
|
-
const { container } = render(<PreviewPrompt prompt={null} />);
|
|
258
|
-
expect(container.firstChild).toBeInTheDocument();
|
|
259
|
-
});
|
|
260
|
-
|
|
261
|
-
it('should handle very long prompt', () => {
|
|
262
|
-
const longPrompt = 'A'.repeat(10000);
|
|
263
|
-
render(<PreviewPrompt prompt={longPrompt} />);
|
|
264
|
-
expect(screen.getByText(longPrompt)).toBeInTheDocument();
|
|
265
|
-
});
|
|
266
|
-
|
|
267
|
-
it('should handle prompt with special HTML characters', () => {
|
|
268
|
-
const specialPrompt = '<div>Test & "quotes" \' apostrophe</div>';
|
|
269
|
-
render(<PreviewPrompt prompt={specialPrompt} />);
|
|
270
|
-
expect(screen.getByText(/Test/)).toBeInTheDocument();
|
|
271
|
-
});
|
|
272
|
-
|
|
273
|
-
it('should handle prompt with script tags safely', () => {
|
|
274
|
-
const scriptPrompt = '<script>alert("XSS")</script>Safe text';
|
|
275
|
-
const { container } = render(<PreviewPrompt prompt={scriptPrompt} />);
|
|
276
|
-
expect(container).toBeInTheDocument();
|
|
277
|
-
});
|
|
278
|
-
});
|
|
279
|
-
|
|
280
|
-
describe('dangerouslySetInnerHTML', () => {
|
|
281
|
-
it('should render HTML using dangerouslySetInnerHTML', () => {
|
|
282
|
-
const htmlPrompt = '<strong>Bold</strong> and <em>italic</em>';
|
|
283
|
-
render(<PreviewPrompt prompt={htmlPrompt} />);
|
|
284
|
-
|
|
285
|
-
expect(screen.getByText('Bold')).toBeInTheDocument();
|
|
286
|
-
expect(screen.getByText(/italic/)).toBeInTheDocument();
|
|
287
|
-
});
|
|
288
|
-
|
|
289
|
-
it('should render complex nested HTML', () => {
|
|
290
|
-
const complexPrompt = `
|
|
291
|
-
<div>
|
|
292
|
-
<h3>Title</h3>
|
|
293
|
-
<p>Paragraph 1</p>
|
|
294
|
-
<ul>
|
|
295
|
-
<li>Item 1</li>
|
|
296
|
-
<li>Item 2</li>
|
|
297
|
-
</ul>
|
|
298
|
-
</div>
|
|
299
|
-
`;
|
|
300
|
-
render(<PreviewPrompt prompt={complexPrompt} />);
|
|
301
|
-
|
|
302
|
-
expect(screen.getByText('Title')).toBeInTheDocument();
|
|
303
|
-
expect(screen.getByText('Paragraph 1')).toBeInTheDocument();
|
|
304
|
-
expect(screen.getByText('Item 1')).toBeInTheDocument();
|
|
305
|
-
expect(screen.getByText('Item 2')).toBeInTheDocument();
|
|
306
|
-
});
|
|
307
|
-
|
|
308
|
-
it('should render tables', () => {
|
|
309
|
-
const tablePrompt = `
|
|
310
|
-
<table>
|
|
311
|
-
<tbody>
|
|
312
|
-
<tr><th>Header</th><td>Data</td></tr>
|
|
313
|
-
</tbody>
|
|
314
|
-
</table>
|
|
315
|
-
`;
|
|
316
|
-
render(<PreviewPrompt prompt={tablePrompt} />);
|
|
317
|
-
|
|
318
|
-
expect(screen.getByText('Header')).toBeInTheDocument();
|
|
319
|
-
expect(screen.getByText('Data')).toBeInTheDocument();
|
|
320
|
-
});
|
|
321
|
-
|
|
322
|
-
// Asserted against the CSS emotion emitted rather than through toHaveStyle: jsdom
|
|
323
|
-
// does not resolve custom properties, so it normalises `var()` away. Emotion
|
|
324
|
-
// inserts through the CSSOM, so the <style> tags themselves carry no text.
|
|
325
|
-
describe('authored table painting', () => {
|
|
326
|
-
const emittedCss = () =>
|
|
327
|
-
Array.from(document.querySelectorAll('style'))
|
|
328
|
-
.flatMap((tag) => Array.from(tag.sheet?.cssRules || []))
|
|
329
|
-
.map((rule) => rule.cssText)
|
|
330
|
-
.join('\n');
|
|
331
|
-
|
|
332
|
-
it('paints a bordered table flat, from the ink token', () => {
|
|
333
|
-
render(<PreviewPrompt prompt={'<div><table border="1"><tbody><tr><td>A</td></tr></tbody></table></div>'} />);
|
|
334
|
-
|
|
335
|
-
const css = emittedCss();
|
|
336
|
-
expect(css).toContain('table[border]');
|
|
337
|
-
expect(css).toContain('border-style: solid');
|
|
338
|
-
expect(css).toContain(`border-color: ${color.tableGrid()}`);
|
|
339
|
-
});
|
|
340
|
-
|
|
341
|
-
it('takes the stripe from a scheme-following fill, not a fixed near-white one', () => {
|
|
342
|
-
render(<PreviewPrompt prompt={'<table><tbody><tr><td>A</td></tr></tbody></table>'} />);
|
|
343
|
-
|
|
344
|
-
const css = emittedCss();
|
|
345
|
-
expect(css).toContain(`background-color: ${color.tableStripe()}`);
|
|
346
|
-
// #f6f8fa still appears as the var() chain's last resort, so this checks that
|
|
347
|
-
// nothing paints it bare - along with the `color: black` it used to be paired with.
|
|
348
|
-
expect(css).not.toContain('background-color: #f6f8fa');
|
|
349
|
-
expect(css).not.toContain('color: rgb(0, 0, 0)');
|
|
350
|
-
});
|
|
351
|
-
});
|
|
352
|
-
});
|
|
353
|
-
|
|
354
|
-
describe('component lifecycle', () => {
|
|
355
|
-
it('should cleanup on unmount', () => {
|
|
356
|
-
const { unmount } = render(<PreviewPrompt prompt="Test" />);
|
|
357
|
-
unmount();
|
|
358
|
-
// Should not throw errors on unmount
|
|
359
|
-
});
|
|
360
|
-
|
|
361
|
-
it('should update when props change', () => {
|
|
362
|
-
const { rerender } = render(<PreviewPrompt prompt="Initial" />);
|
|
363
|
-
rerender(<PreviewPrompt prompt="Updated" />);
|
|
364
|
-
|
|
365
|
-
expect(screen.getByText('Updated')).toBeInTheDocument();
|
|
366
|
-
expect(screen.queryByText('Initial')).not.toBeInTheDocument();
|
|
367
|
-
});
|
|
368
|
-
|
|
369
|
-
it('should handle rapid prop changes', () => {
|
|
370
|
-
const { rerender } = render(<PreviewPrompt prompt="V1" />);
|
|
371
|
-
rerender(<PreviewPrompt prompt="V2" />);
|
|
372
|
-
rerender(<PreviewPrompt prompt="V3" />);
|
|
373
|
-
rerender(<PreviewPrompt prompt="V4" />);
|
|
374
|
-
|
|
375
|
-
expect(screen.getByText('V4')).toBeInTheDocument();
|
|
376
|
-
});
|
|
377
|
-
});
|
|
378
|
-
|
|
379
|
-
describe('accessibility', () => {
|
|
380
|
-
it('should mark the container with the stable preview-prompt class', () => {
|
|
381
|
-
const { container } = render(<PreviewPrompt prompt="Accessible text" />);
|
|
382
|
-
expect(container.firstChild).toHaveClass('preview-prompt');
|
|
383
|
-
});
|
|
384
|
-
|
|
385
|
-
it('should not emit an id on the container', () => {
|
|
386
|
-
const { container } = render(<PreviewPrompt prompt="Accessible text" />);
|
|
387
|
-
expect(container.firstChild).not.toHaveAttribute('id');
|
|
388
|
-
});
|
|
389
|
-
|
|
390
|
-
it('should leave no duplicate ids when a page renders many prompts', () => {
|
|
391
|
-
const { container } = render(
|
|
392
|
-
<div>
|
|
393
|
-
<PreviewPrompt prompt="Item 1" />
|
|
394
|
-
<PreviewPrompt prompt="Item 2" />
|
|
395
|
-
<PreviewPrompt prompt="Item 3" />
|
|
396
|
-
<PreviewPrompt prompt="Item 4" />
|
|
397
|
-
<PreviewPrompt prompt="Item 5" />
|
|
398
|
-
</div>,
|
|
399
|
-
);
|
|
400
|
-
|
|
401
|
-
const ids = Array.from(container.querySelectorAll('[id]')).map((el) => el.id);
|
|
402
|
-
|
|
403
|
-
expect(ids).toEqual(Array.from(new Set(ids)));
|
|
404
|
-
});
|
|
405
|
-
|
|
406
|
-
it('should preserve semantic HTML', () => {
|
|
407
|
-
const semanticPrompt = '<strong>Important:</strong> Read carefully';
|
|
408
|
-
render(<PreviewPrompt prompt={semanticPrompt} />);
|
|
409
|
-
|
|
410
|
-
const strong = document.querySelector('strong');
|
|
411
|
-
expect(strong).toBeInTheDocument();
|
|
412
|
-
expect(strong).toHaveTextContent('Important:');
|
|
413
|
-
});
|
|
414
|
-
});
|
|
415
|
-
|
|
416
|
-
describe('multiple instances', () => {
|
|
417
|
-
it('should handle multiple PreviewPrompt components', () => {
|
|
418
|
-
const { container } = render(
|
|
419
|
-
<div>
|
|
420
|
-
<PreviewPrompt prompt="First prompt" />
|
|
421
|
-
<PreviewPrompt prompt="Second prompt" />
|
|
422
|
-
<PreviewPrompt prompt="Third prompt" />
|
|
423
|
-
</div>,
|
|
424
|
-
);
|
|
425
|
-
|
|
426
|
-
expect(screen.getByText('First prompt')).toBeInTheDocument();
|
|
427
|
-
expect(screen.getByText('Second prompt')).toBeInTheDocument();
|
|
428
|
-
expect(screen.getByText('Third prompt')).toBeInTheDocument();
|
|
429
|
-
});
|
|
430
|
-
|
|
431
|
-
it('should typeset each instance with its own node rather than the first prompt on the page', () => {
|
|
432
|
-
renderMath.mockClear();
|
|
433
|
-
|
|
434
|
-
render(
|
|
435
|
-
<div>
|
|
436
|
-
<PreviewPrompt prompt="First prompt" />
|
|
437
|
-
<PreviewPrompt prompt="Second prompt" />
|
|
438
|
-
<PreviewPrompt prompt="Third prompt" />
|
|
439
|
-
</div>,
|
|
440
|
-
);
|
|
441
|
-
|
|
442
|
-
const typesetNodes = renderMath.mock.calls.map(([node]) => node);
|
|
443
|
-
|
|
444
|
-
expect(typesetNodes).toHaveLength(3);
|
|
445
|
-
expect(new Set(typesetNodes).size).toBe(3);
|
|
446
|
-
expect(typesetNodes.map((node) => node.textContent)).toEqual(['First prompt', 'Second prompt', 'Third prompt']);
|
|
447
|
-
});
|
|
448
|
-
|
|
449
|
-
it('should align images within each instance independently', () => {
|
|
450
|
-
const { container } = render(
|
|
451
|
-
<div>
|
|
452
|
-
<PreviewPrompt prompt='<img src="a.png" alignment="center" />' />
|
|
453
|
-
<PreviewPrompt prompt='<img src="b.png" alignment="right" />' />
|
|
454
|
-
</div>,
|
|
455
|
-
);
|
|
456
|
-
|
|
457
|
-
const wrappers = container.querySelectorAll('.preview-prompt > div');
|
|
458
|
-
|
|
459
|
-
expect(wrappers).toHaveLength(2);
|
|
460
|
-
expect(wrappers[0].style.justifyContent).toBe('center');
|
|
461
|
-
expect(wrappers[1].style.justifyContent).toBe('flex-end');
|
|
462
|
-
});
|
|
463
|
-
});
|
|
464
|
-
});
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import { render, screen } from '@testing-library/react';
|
|
3
|
-
import Purpose from '../purpose';
|
|
4
|
-
|
|
5
|
-
describe('Purpose', () => {
|
|
6
|
-
describe('rendering', () => {
|
|
7
|
-
it('renders child with data-pie-purpose attribute even when purpose prop is not provided', () => {
|
|
8
|
-
const { container } = render(
|
|
9
|
-
<Purpose>
|
|
10
|
-
<div>text</div>
|
|
11
|
-
</Purpose>,
|
|
12
|
-
);
|
|
13
|
-
|
|
14
|
-
expect(screen.getByText('text')).toBeInTheDocument();
|
|
15
|
-
const div = container.querySelector('div');
|
|
16
|
-
// When purpose is undefined, React sets the attribute as data-pie-purpose="undefined"
|
|
17
|
-
// This is expected behavior - the component always adds the attribute
|
|
18
|
-
expect(div).toBeInTheDocument();
|
|
19
|
-
});
|
|
20
|
-
|
|
21
|
-
it('renders child with data-pie-purpose="passage"', () => {
|
|
22
|
-
const { container } = render(
|
|
23
|
-
<Purpose purpose="passage">
|
|
24
|
-
<div>text</div>
|
|
25
|
-
</Purpose>,
|
|
26
|
-
);
|
|
27
|
-
|
|
28
|
-
expect(screen.getByText('text')).toBeInTheDocument();
|
|
29
|
-
const div = container.querySelector('div');
|
|
30
|
-
expect(div).toHaveAttribute('data-pie-purpose', 'passage');
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
it('renders multiple children with data-pie-purpose attribute', () => {
|
|
34
|
-
const { container } = render(
|
|
35
|
-
<Purpose purpose="something">
|
|
36
|
-
<div>
|
|
37
|
-
<div>text1</div>
|
|
38
|
-
<div>text2</div>
|
|
39
|
-
</div>
|
|
40
|
-
</Purpose>,
|
|
41
|
-
);
|
|
42
|
-
|
|
43
|
-
expect(screen.getByText('text1')).toBeInTheDocument();
|
|
44
|
-
expect(screen.getByText('text2')).toBeInTheDocument();
|
|
45
|
-
expect(screen.queryByText('text3')).not.toBeInTheDocument();
|
|
46
|
-
|
|
47
|
-
const parentDiv = container.querySelector('div');
|
|
48
|
-
expect(parentDiv).toHaveAttribute('data-pie-purpose', 'something');
|
|
49
|
-
});
|
|
50
|
-
});
|
|
51
|
-
});
|
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import { render, screen } from '@testing-library/react';
|
|
3
|
-
import Readable from '../readable';
|
|
4
|
-
|
|
5
|
-
describe('Readable', () => {
|
|
6
|
-
describe('rendering', () => {
|
|
7
|
-
it('renders child with data-pie-readable attribute set to true', () => {
|
|
8
|
-
const { container } = render(
|
|
9
|
-
<Readable>
|
|
10
|
-
<div>text</div>
|
|
11
|
-
</Readable>,
|
|
12
|
-
);
|
|
13
|
-
|
|
14
|
-
expect(screen.getByText('text')).toBeInTheDocument();
|
|
15
|
-
const div = container.querySelector('div');
|
|
16
|
-
expect(div).toHaveAttribute('data-pie-readable', 'true');
|
|
17
|
-
});
|
|
18
|
-
|
|
19
|
-
it('renders multiple children with data-pie-readable attribute', () => {
|
|
20
|
-
const { container } = render(
|
|
21
|
-
<Readable>
|
|
22
|
-
<div>
|
|
23
|
-
<div>text1</div>
|
|
24
|
-
<div>text2</div>
|
|
25
|
-
</div>
|
|
26
|
-
</Readable>,
|
|
27
|
-
);
|
|
28
|
-
|
|
29
|
-
expect(screen.getByText('text1')).toBeInTheDocument();
|
|
30
|
-
expect(screen.getByText('text2')).toBeInTheDocument();
|
|
31
|
-
expect(screen.queryByText('text3')).not.toBeInTheDocument();
|
|
32
|
-
|
|
33
|
-
const parentDiv = container.querySelector('div');
|
|
34
|
-
expect(parentDiv).toHaveAttribute('data-pie-readable', 'true');
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
it('renders with data-pie-readable set to false when false prop is provided', () => {
|
|
38
|
-
const { container } = render(
|
|
39
|
-
<Readable false>
|
|
40
|
-
<div>
|
|
41
|
-
<div>text1</div>
|
|
42
|
-
<div>text2</div>
|
|
43
|
-
</div>
|
|
44
|
-
</Readable>,
|
|
45
|
-
);
|
|
46
|
-
|
|
47
|
-
expect(screen.getByText('text1')).toBeInTheDocument();
|
|
48
|
-
expect(screen.getByText('text2')).toBeInTheDocument();
|
|
49
|
-
expect(screen.queryByText('text3')).not.toBeInTheDocument();
|
|
50
|
-
|
|
51
|
-
const parentDiv = container.querySelector('div');
|
|
52
|
-
expect(parentDiv).toHaveAttribute('data-pie-readable', 'false');
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
it('renders with data-pie-readable set to false when false={true}', () => {
|
|
56
|
-
const { container } = render(
|
|
57
|
-
<Readable false={true}>
|
|
58
|
-
<div>
|
|
59
|
-
<div>text1</div>
|
|
60
|
-
<div>text2</div>
|
|
61
|
-
</div>
|
|
62
|
-
</Readable>,
|
|
63
|
-
);
|
|
64
|
-
|
|
65
|
-
const parentDiv = container.querySelector('div');
|
|
66
|
-
expect(parentDiv).toHaveAttribute('data-pie-readable', 'false');
|
|
67
|
-
});
|
|
68
|
-
});
|
|
69
|
-
});
|