@pie-element/hotspot 10.0.0-next.43 → 10.1.0-next.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/configure/lib/defaults.js +3 -0
- package/configure/lib/defaults.js.map +1 -1
- package/configure/lib/hotspot-circle.js +0 -1
- package/configure/lib/hotspot-circle.js.map +1 -1
- package/configure/lib/hotspot-drawable.js +5 -5
- package/configure/lib/hotspot-drawable.js.map +1 -1
- package/configure/lib/hotspot-polygon.js +1 -2
- package/configure/lib/hotspot-polygon.js.map +1 -1
- package/configure/lib/hotspot-rectangle.js +0 -1
- package/configure/lib/hotspot-rectangle.js.map +1 -1
- package/configure/lib/utils.js +2 -3
- package/configure/lib/utils.js.map +1 -1
- package/configure/package.json +6 -6
- package/configure/src/__tests__/DeleteWidget.test.jsx +366 -0
- package/configure/src/__tests__/button.test.jsx +198 -0
- package/configure/src/__tests__/hotspot-circle.test.jsx +259 -0
- package/configure/src/__tests__/hotspot-palette.test.jsx +71 -0
- package/configure/src/__tests__/image-konva.test.jsx +226 -0
- package/configure/src/defaults.js +1 -0
- package/configure/src/hotspot-circle.jsx +0 -1
- package/configure/src/hotspot-drawable.jsx +1 -1
- package/configure/src/hotspot-polygon.jsx +0 -1
- package/configure/src/hotspot-rectangle.jsx +0 -1
- package/configure/src/utils.js +1 -1
- package/controller/lib/index.js +2 -2
- package/controller/lib/index.js.map +1 -1
- package/controller/lib/utils.js +3 -5
- package/controller/lib/utils.js.map +1 -1
- package/controller/package.json +3 -3
- package/controller/src/index.js +1 -1
- package/controller/src/utils.js +1 -2
- package/lib/hotspot/circle.js +1 -2
- package/lib/hotspot/circle.js.map +1 -1
- package/lib/hotspot/polygon.js +0 -1
- package/lib/hotspot/polygon.js.map +1 -1
- package/lib/hotspot/rectangle.js +0 -1
- package/lib/hotspot/rectangle.js.map +1 -1
- package/package.json +10 -7
- package/src/hotspot/__tests__/circle.test.jsx +464 -0
- package/src/hotspot/__tests__/container.test.jsx +546 -0
- package/src/hotspot/__tests__/image-konva-tooltip.test.jsx +510 -0
- package/src/hotspot/__tests__/polygon.test.jsx +502 -0
- package/src/hotspot/__tests__/rectangle.test.jsx +418 -0
- package/src/hotspot/circle.jsx +0 -1
- package/src/hotspot/polygon.jsx +0 -1
- package/src/hotspot/rectangle.jsx +0 -1
|
@@ -0,0 +1,546 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { render, fireEvent } from '@testing-library/react';
|
|
3
|
+
import Konva from 'konva';
|
|
4
|
+
import { Container } from '../container';
|
|
5
|
+
|
|
6
|
+
Konva.isBrowser = false;
|
|
7
|
+
|
|
8
|
+
jest.mock('react-konva', () => {
|
|
9
|
+
const React = require('react');
|
|
10
|
+
return {
|
|
11
|
+
Stage: ({ children, ...props }) => React.createElement('div', { 'data-testid': 'stage', ...props }, children),
|
|
12
|
+
Layer: ({ children, ...props }) => React.createElement('div', { 'data-testid': 'layer', ...props }, children),
|
|
13
|
+
};
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
jest.mock('../rectangle', () => {
|
|
17
|
+
return function Rectangle(props) {
|
|
18
|
+
return (
|
|
19
|
+
<div
|
|
20
|
+
data-testid={`rectangle-${props.id}`}
|
|
21
|
+
data-selected={props.selected}
|
|
22
|
+
data-iscorrect={props.isCorrect}
|
|
23
|
+
data-disabled={props.disabled}
|
|
24
|
+
onClick={() => props.onClick({ id: props.id, selected: !props.selected })}
|
|
25
|
+
/>
|
|
26
|
+
);
|
|
27
|
+
};
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
jest.mock('../polygon', () => {
|
|
31
|
+
return function Polygon(props) {
|
|
32
|
+
return (
|
|
33
|
+
<div
|
|
34
|
+
data-testid={`polygon-${props.id}`}
|
|
35
|
+
data-selected={props.selected}
|
|
36
|
+
data-iscorrect={props.isCorrect}
|
|
37
|
+
data-disabled={props.disabled}
|
|
38
|
+
onClick={() => props.onClick({ id: props.id, selected: !props.selected })}
|
|
39
|
+
/>
|
|
40
|
+
);
|
|
41
|
+
};
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
jest.mock('../circle', () => {
|
|
45
|
+
return function Circle(props) {
|
|
46
|
+
return (
|
|
47
|
+
<div
|
|
48
|
+
data-testid={`circle-${props.id}`}
|
|
49
|
+
data-selected={props.selected}
|
|
50
|
+
data-iscorrect={props.isCorrect}
|
|
51
|
+
data-disabled={props.disabled}
|
|
52
|
+
onClick={() => props.onClick({ id: props.id, selected: !props.selected })}
|
|
53
|
+
/>
|
|
54
|
+
);
|
|
55
|
+
};
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
describe('Container', () => {
|
|
59
|
+
let defaultProps;
|
|
60
|
+
|
|
61
|
+
beforeEach(() => {
|
|
62
|
+
defaultProps = {
|
|
63
|
+
dimensions: { width: 800, height: 600 },
|
|
64
|
+
disabled: false,
|
|
65
|
+
hotspotColor: '#FF0000',
|
|
66
|
+
hoverOutlineColor: '#FFFF00',
|
|
67
|
+
selectedHotspotColor: '#00FF00',
|
|
68
|
+
imageUrl: 'http://example.com/image.png',
|
|
69
|
+
isEvaluateMode: false,
|
|
70
|
+
onSelectChoice: jest.fn(),
|
|
71
|
+
outlineColor: '#0000FF',
|
|
72
|
+
session: { answers: [] },
|
|
73
|
+
shapes: {
|
|
74
|
+
rectangles: [],
|
|
75
|
+
polygons: [],
|
|
76
|
+
circles: [],
|
|
77
|
+
},
|
|
78
|
+
strokeWidth: 5,
|
|
79
|
+
scale: 1,
|
|
80
|
+
showCorrect: false,
|
|
81
|
+
};
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
describe('rendering', () => {
|
|
85
|
+
it('should render without crashing', () => {
|
|
86
|
+
const { container } = render(<Container {...defaultProps} />);
|
|
87
|
+
expect(container).toBeTruthy();
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
it('should render image when imageUrl is provided', () => {
|
|
91
|
+
const { getByAltText } = render(<Container {...defaultProps} />);
|
|
92
|
+
const image = getByAltText('hotspot-image');
|
|
93
|
+
expect(image).toBeInTheDocument();
|
|
94
|
+
expect(image).toHaveAttribute('src', 'http://example.com/image.png');
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
it('should not render image when imageUrl is not provided', () => {
|
|
98
|
+
const { queryByAltText } = render(<Container {...defaultProps} imageUrl="" />);
|
|
99
|
+
expect(queryByAltText('hotspot-image')).not.toBeInTheDocument();
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
it('should render Stage with correct dimensions', () => {
|
|
103
|
+
const { getByTestId } = render(<Container {...defaultProps} />);
|
|
104
|
+
const stage = getByTestId('stage');
|
|
105
|
+
expect(stage).toHaveAttribute('height', '605'); // 600 + strokeWidth
|
|
106
|
+
expect(stage).toHaveAttribute('width', '805'); // 800 + strokeWidth
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
it('should apply scale to dimensions', () => {
|
|
110
|
+
const { getByTestId } = render(<Container {...defaultProps} scale={2} />);
|
|
111
|
+
const stage = getByTestId('stage');
|
|
112
|
+
expect(stage).toHaveAttribute('height', '1205'); // (600 * 2) + strokeWidth
|
|
113
|
+
expect(stage).toHaveAttribute('width', '1605'); // (800 * 2) + strokeWidth
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
it('should render Layer inside Stage', () => {
|
|
117
|
+
const { getByTestId } = render(<Container {...defaultProps} />);
|
|
118
|
+
const layer = getByTestId('layer');
|
|
119
|
+
expect(layer).toBeInTheDocument();
|
|
120
|
+
});
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
describe('rectangle shapes', () => {
|
|
124
|
+
it('should render rectangles', () => {
|
|
125
|
+
const props = {
|
|
126
|
+
...defaultProps,
|
|
127
|
+
shapes: {
|
|
128
|
+
rectangles: [
|
|
129
|
+
{ id: 'rect1', x: 10, y: 20, width: 100, height: 80, correct: false },
|
|
130
|
+
{ id: 'rect2', x: 150, y: 200, width: 120, height: 90, correct: false },
|
|
131
|
+
],
|
|
132
|
+
polygons: [],
|
|
133
|
+
circles: [],
|
|
134
|
+
},
|
|
135
|
+
};
|
|
136
|
+
const { getByTestId } = render(<Container {...props} />);
|
|
137
|
+
|
|
138
|
+
expect(getByTestId('rectangle-rect1')).toBeInTheDocument();
|
|
139
|
+
expect(getByTestId('rectangle-rect2')).toBeInTheDocument();
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
it('should mark rectangle as selected when in session answers', () => {
|
|
143
|
+
const props = {
|
|
144
|
+
...defaultProps,
|
|
145
|
+
session: { answers: [{ id: 'rect1' }] },
|
|
146
|
+
shapes: {
|
|
147
|
+
rectangles: [
|
|
148
|
+
{ id: 'rect1', x: 10, y: 20, width: 100, height: 80, correct: false },
|
|
149
|
+
],
|
|
150
|
+
polygons: [],
|
|
151
|
+
circles: [],
|
|
152
|
+
},
|
|
153
|
+
};
|
|
154
|
+
const { getByTestId } = render(<Container {...props} />);
|
|
155
|
+
|
|
156
|
+
const rect = getByTestId('rectangle-rect1');
|
|
157
|
+
expect(rect).toHaveAttribute('data-selected', 'true');
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
it('should call onSelectChoice when rectangle is clicked', () => {
|
|
161
|
+
const onSelectChoice = jest.fn();
|
|
162
|
+
const props = {
|
|
163
|
+
...defaultProps,
|
|
164
|
+
onSelectChoice,
|
|
165
|
+
shapes: {
|
|
166
|
+
rectangles: [
|
|
167
|
+
{ id: 'rect1', x: 10, y: 20, width: 100, height: 80, correct: false },
|
|
168
|
+
],
|
|
169
|
+
polygons: [],
|
|
170
|
+
circles: [],
|
|
171
|
+
},
|
|
172
|
+
};
|
|
173
|
+
const { getByTestId } = render(<Container {...props} />);
|
|
174
|
+
|
|
175
|
+
const rect = getByTestId('rectangle-rect1');
|
|
176
|
+
fireEvent.click(rect);
|
|
177
|
+
|
|
178
|
+
expect(onSelectChoice).toHaveBeenCalledWith({ id: 'rect1', selected: true });
|
|
179
|
+
});
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
describe('polygon shapes', () => {
|
|
183
|
+
it('should render polygons', () => {
|
|
184
|
+
const props = {
|
|
185
|
+
...defaultProps,
|
|
186
|
+
shapes: {
|
|
187
|
+
rectangles: [],
|
|
188
|
+
polygons: [
|
|
189
|
+
{
|
|
190
|
+
id: 'poly1',
|
|
191
|
+
points: [{ x: 10, y: 10 }, { x: 100, y: 10 }, { x: 50, y: 100 }],
|
|
192
|
+
correct: false,
|
|
193
|
+
},
|
|
194
|
+
],
|
|
195
|
+
circles: [],
|
|
196
|
+
},
|
|
197
|
+
};
|
|
198
|
+
const { getByTestId } = render(<Container {...props} />);
|
|
199
|
+
|
|
200
|
+
expect(getByTestId('polygon-poly1')).toBeInTheDocument();
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
it('should mark polygon as selected when in session answers', () => {
|
|
204
|
+
const props = {
|
|
205
|
+
...defaultProps,
|
|
206
|
+
session: { answers: [{ id: 'poly1' }] },
|
|
207
|
+
shapes: {
|
|
208
|
+
rectangles: [],
|
|
209
|
+
polygons: [
|
|
210
|
+
{
|
|
211
|
+
id: 'poly1',
|
|
212
|
+
points: [{ x: 10, y: 10 }, { x: 100, y: 10 }, { x: 50, y: 100 }],
|
|
213
|
+
correct: false,
|
|
214
|
+
},
|
|
215
|
+
],
|
|
216
|
+
circles: [],
|
|
217
|
+
},
|
|
218
|
+
};
|
|
219
|
+
const { getByTestId } = render(<Container {...props} />);
|
|
220
|
+
|
|
221
|
+
const polygon = getByTestId('polygon-poly1');
|
|
222
|
+
expect(polygon).toHaveAttribute('data-selected', 'true');
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
it('should call onSelectChoice when polygon is clicked', () => {
|
|
226
|
+
const onSelectChoice = jest.fn();
|
|
227
|
+
const props = {
|
|
228
|
+
...defaultProps,
|
|
229
|
+
onSelectChoice,
|
|
230
|
+
shapes: {
|
|
231
|
+
rectangles: [],
|
|
232
|
+
polygons: [
|
|
233
|
+
{
|
|
234
|
+
id: 'poly1',
|
|
235
|
+
points: [{ x: 10, y: 10 }, { x: 100, y: 10 }, { x: 50, y: 100 }],
|
|
236
|
+
correct: false,
|
|
237
|
+
},
|
|
238
|
+
],
|
|
239
|
+
circles: [],
|
|
240
|
+
},
|
|
241
|
+
};
|
|
242
|
+
const { getByTestId } = render(<Container {...props} />);
|
|
243
|
+
|
|
244
|
+
const polygon = getByTestId('polygon-poly1');
|
|
245
|
+
fireEvent.click(polygon);
|
|
246
|
+
|
|
247
|
+
expect(onSelectChoice).toHaveBeenCalledWith({ id: 'poly1', selected: true });
|
|
248
|
+
});
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
describe('circle shapes', () => {
|
|
252
|
+
it('should render circles', () => {
|
|
253
|
+
const props = {
|
|
254
|
+
...defaultProps,
|
|
255
|
+
shapes: {
|
|
256
|
+
rectangles: [],
|
|
257
|
+
polygons: [],
|
|
258
|
+
circles: [
|
|
259
|
+
{ id: 'circle1', x: 100, y: 100, radius: 50, correct: false },
|
|
260
|
+
],
|
|
261
|
+
},
|
|
262
|
+
};
|
|
263
|
+
const { getByTestId } = render(<Container {...props} />);
|
|
264
|
+
|
|
265
|
+
expect(getByTestId('circle-circle1')).toBeInTheDocument();
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
it('should mark circle as selected when in session answers', () => {
|
|
269
|
+
const props = {
|
|
270
|
+
...defaultProps,
|
|
271
|
+
session: { answers: [{ id: 'circle1' }] },
|
|
272
|
+
shapes: {
|
|
273
|
+
rectangles: [],
|
|
274
|
+
polygons: [],
|
|
275
|
+
circles: [
|
|
276
|
+
{ id: 'circle1', x: 100, y: 100, radius: 50, correct: false },
|
|
277
|
+
],
|
|
278
|
+
},
|
|
279
|
+
};
|
|
280
|
+
const { getByTestId } = render(<Container {...props} />);
|
|
281
|
+
|
|
282
|
+
const circle = getByTestId('circle-circle1');
|
|
283
|
+
expect(circle).toHaveAttribute('data-selected', 'true');
|
|
284
|
+
});
|
|
285
|
+
|
|
286
|
+
it('should call onSelectChoice when circle is clicked', () => {
|
|
287
|
+
const onSelectChoice = jest.fn();
|
|
288
|
+
const props = {
|
|
289
|
+
...defaultProps,
|
|
290
|
+
onSelectChoice,
|
|
291
|
+
shapes: {
|
|
292
|
+
rectangles: [],
|
|
293
|
+
polygons: [],
|
|
294
|
+
circles: [
|
|
295
|
+
{ id: 'circle1', x: 100, y: 100, radius: 50, correct: false },
|
|
296
|
+
],
|
|
297
|
+
},
|
|
298
|
+
};
|
|
299
|
+
const { getByTestId } = render(<Container {...props} />);
|
|
300
|
+
|
|
301
|
+
const circle = getByTestId('circle-circle1');
|
|
302
|
+
fireEvent.click(circle);
|
|
303
|
+
|
|
304
|
+
expect(onSelectChoice).toHaveBeenCalledWith({ id: 'circle1', selected: true });
|
|
305
|
+
});
|
|
306
|
+
});
|
|
307
|
+
|
|
308
|
+
describe('evaluate mode', () => {
|
|
309
|
+
it('should show correctness when in evaluate mode', () => {
|
|
310
|
+
const props = {
|
|
311
|
+
...defaultProps,
|
|
312
|
+
isEvaluateMode: true,
|
|
313
|
+
session: { answers: [{ id: 'rect1' }] },
|
|
314
|
+
shapes: {
|
|
315
|
+
rectangles: [
|
|
316
|
+
{ id: 'rect1', x: 10, y: 20, width: 100, height: 80, correct: true },
|
|
317
|
+
],
|
|
318
|
+
polygons: [],
|
|
319
|
+
circles: [],
|
|
320
|
+
},
|
|
321
|
+
};
|
|
322
|
+
const { getByTestId } = render(<Container {...props} />);
|
|
323
|
+
|
|
324
|
+
const rect = getByTestId('rectangle-rect1');
|
|
325
|
+
expect(rect).toHaveAttribute('data-iscorrect', 'true');
|
|
326
|
+
});
|
|
327
|
+
|
|
328
|
+
it('should show incorrect when selected but not correct', () => {
|
|
329
|
+
const props = {
|
|
330
|
+
...defaultProps,
|
|
331
|
+
isEvaluateMode: true,
|
|
332
|
+
session: { answers: [{ id: 'rect1' }] },
|
|
333
|
+
shapes: {
|
|
334
|
+
rectangles: [
|
|
335
|
+
{ id: 'rect1', x: 10, y: 20, width: 100, height: 80, correct: false },
|
|
336
|
+
],
|
|
337
|
+
polygons: [],
|
|
338
|
+
circles: [],
|
|
339
|
+
},
|
|
340
|
+
};
|
|
341
|
+
const { getByTestId } = render(<Container {...props} />);
|
|
342
|
+
|
|
343
|
+
const rect = getByTestId('rectangle-rect1');
|
|
344
|
+
expect(rect).toHaveAttribute('data-iscorrect', 'false');
|
|
345
|
+
});
|
|
346
|
+
|
|
347
|
+
it('should show incorrect when not selected but correct', () => {
|
|
348
|
+
const props = {
|
|
349
|
+
...defaultProps,
|
|
350
|
+
isEvaluateMode: true,
|
|
351
|
+
session: { answers: [] },
|
|
352
|
+
shapes: {
|
|
353
|
+
rectangles: [
|
|
354
|
+
{ id: 'rect1', x: 10, y: 20, width: 100, height: 80, correct: true },
|
|
355
|
+
],
|
|
356
|
+
polygons: [],
|
|
357
|
+
circles: [],
|
|
358
|
+
},
|
|
359
|
+
};
|
|
360
|
+
const { getByTestId } = render(<Container {...props} />);
|
|
361
|
+
|
|
362
|
+
const rect = getByTestId('rectangle-rect1');
|
|
363
|
+
expect(rect).toHaveAttribute('data-iscorrect', 'false');
|
|
364
|
+
});
|
|
365
|
+
|
|
366
|
+
it('should show correct when not selected and not correct', () => {
|
|
367
|
+
const props = {
|
|
368
|
+
...defaultProps,
|
|
369
|
+
isEvaluateMode: true,
|
|
370
|
+
session: { answers: [] },
|
|
371
|
+
shapes: {
|
|
372
|
+
rectangles: [
|
|
373
|
+
{ id: 'rect1', x: 10, y: 20, width: 100, height: 80, correct: false },
|
|
374
|
+
],
|
|
375
|
+
polygons: [],
|
|
376
|
+
circles: [],
|
|
377
|
+
},
|
|
378
|
+
};
|
|
379
|
+
const { getByTestId } = render(<Container {...props} />);
|
|
380
|
+
|
|
381
|
+
const rect = getByTestId('rectangle-rect1');
|
|
382
|
+
expect(rect).toHaveAttribute('data-iscorrect', 'true');
|
|
383
|
+
});
|
|
384
|
+
});
|
|
385
|
+
|
|
386
|
+
describe('disabled state', () => {
|
|
387
|
+
it('should pass disabled prop to shapes', () => {
|
|
388
|
+
const props = {
|
|
389
|
+
...defaultProps,
|
|
390
|
+
disabled: true,
|
|
391
|
+
shapes: {
|
|
392
|
+
rectangles: [
|
|
393
|
+
{ id: 'rect1', x: 10, y: 20, width: 100, height: 80, correct: false },
|
|
394
|
+
],
|
|
395
|
+
polygons: [],
|
|
396
|
+
circles: [],
|
|
397
|
+
},
|
|
398
|
+
};
|
|
399
|
+
const { getByTestId } = render(<Container {...props} />);
|
|
400
|
+
|
|
401
|
+
const rect = getByTestId('rectangle-rect1');
|
|
402
|
+
expect(rect).toHaveAttribute('data-disabled', 'true');
|
|
403
|
+
});
|
|
404
|
+
});
|
|
405
|
+
|
|
406
|
+
describe('mixed shapes', () => {
|
|
407
|
+
it('should render all types of shapes together', () => {
|
|
408
|
+
const props = {
|
|
409
|
+
...defaultProps,
|
|
410
|
+
shapes: {
|
|
411
|
+
rectangles: [
|
|
412
|
+
{ id: 'rect1', x: 10, y: 20, width: 100, height: 80, correct: false },
|
|
413
|
+
],
|
|
414
|
+
polygons: [
|
|
415
|
+
{
|
|
416
|
+
id: 'poly1',
|
|
417
|
+
points: [{ x: 10, y: 10 }, { x: 100, y: 10 }, { x: 50, y: 100 }],
|
|
418
|
+
correct: false,
|
|
419
|
+
},
|
|
420
|
+
],
|
|
421
|
+
circles: [
|
|
422
|
+
{ id: 'circle1', x: 100, y: 100, radius: 50, correct: false },
|
|
423
|
+
],
|
|
424
|
+
},
|
|
425
|
+
};
|
|
426
|
+
const { getByTestId } = render(<Container {...props} />);
|
|
427
|
+
|
|
428
|
+
expect(getByTestId('rectangle-rect1')).toBeInTheDocument();
|
|
429
|
+
expect(getByTestId('polygon-poly1')).toBeInTheDocument();
|
|
430
|
+
expect(getByTestId('circle-circle1')).toBeInTheDocument();
|
|
431
|
+
});
|
|
432
|
+
|
|
433
|
+
it('should handle multiple selections across different shape types', () => {
|
|
434
|
+
const props = {
|
|
435
|
+
...defaultProps,
|
|
436
|
+
session: { answers: [{ id: 'rect1' }, { id: 'poly1' }, { id: 'circle1' }] },
|
|
437
|
+
shapes: {
|
|
438
|
+
rectangles: [
|
|
439
|
+
{ id: 'rect1', x: 10, y: 20, width: 100, height: 80, correct: false },
|
|
440
|
+
],
|
|
441
|
+
polygons: [
|
|
442
|
+
{
|
|
443
|
+
id: 'poly1',
|
|
444
|
+
points: [{ x: 10, y: 10 }, { x: 100, y: 10 }, { x: 50, y: 100 }],
|
|
445
|
+
correct: false,
|
|
446
|
+
},
|
|
447
|
+
],
|
|
448
|
+
circles: [
|
|
449
|
+
{ id: 'circle1', x: 100, y: 100, radius: 50, correct: false },
|
|
450
|
+
],
|
|
451
|
+
},
|
|
452
|
+
};
|
|
453
|
+
const { getByTestId } = render(<Container {...props} />);
|
|
454
|
+
|
|
455
|
+
expect(getByTestId('rectangle-rect1')).toHaveAttribute('data-selected', 'true');
|
|
456
|
+
expect(getByTestId('polygon-poly1')).toHaveAttribute('data-selected', 'true');
|
|
457
|
+
expect(getByTestId('circle-circle1')).toHaveAttribute('data-selected', 'true');
|
|
458
|
+
});
|
|
459
|
+
});
|
|
460
|
+
|
|
461
|
+
describe('showCorrect mode', () => {
|
|
462
|
+
it('should pass showCorrect prop to shapes in evaluate mode', () => {
|
|
463
|
+
const props = {
|
|
464
|
+
...defaultProps,
|
|
465
|
+
isEvaluateMode: true,
|
|
466
|
+
showCorrect: true,
|
|
467
|
+
shapes: {
|
|
468
|
+
rectangles: [
|
|
469
|
+
{ id: 'rect1', x: 10, y: 20, width: 100, height: 80, correct: true },
|
|
470
|
+
],
|
|
471
|
+
polygons: [],
|
|
472
|
+
circles: [],
|
|
473
|
+
},
|
|
474
|
+
};
|
|
475
|
+
const { container } = render(<Container {...props} />);
|
|
476
|
+
expect(container).toBeTruthy();
|
|
477
|
+
});
|
|
478
|
+
});
|
|
479
|
+
|
|
480
|
+
describe('getEvaluateText', () => {
|
|
481
|
+
it('should return correct text for correctly selected', () => {
|
|
482
|
+
const component = new Container(defaultProps);
|
|
483
|
+
const text = component.getEvaluateText(true, true);
|
|
484
|
+
expect(text).toBe('Correctly\nselected');
|
|
485
|
+
});
|
|
486
|
+
|
|
487
|
+
it('should return correct text for incorrectly selected', () => {
|
|
488
|
+
const component = new Container(defaultProps);
|
|
489
|
+
const text = component.getEvaluateText(false, true);
|
|
490
|
+
expect(text).toBe('Should not have\nbeen selected');
|
|
491
|
+
});
|
|
492
|
+
|
|
493
|
+
it('should return correct text for should have been selected', () => {
|
|
494
|
+
const component = new Container(defaultProps);
|
|
495
|
+
const text = component.getEvaluateText(true, false);
|
|
496
|
+
expect(text).toBe('Should have\nbeen selected');
|
|
497
|
+
});
|
|
498
|
+
|
|
499
|
+
it('should return null for correctly not selected', () => {
|
|
500
|
+
const component = new Container(defaultProps);
|
|
501
|
+
const text = component.getEvaluateText(false, false);
|
|
502
|
+
expect(text).toBeNull();
|
|
503
|
+
});
|
|
504
|
+
});
|
|
505
|
+
|
|
506
|
+
describe('correctness calculation', () => {
|
|
507
|
+
it('should calculate correctness properly', () => {
|
|
508
|
+
const component = new Container(defaultProps);
|
|
509
|
+
|
|
510
|
+
expect(component.correctness(true, true)).toBe(true);
|
|
511
|
+
expect(component.correctness(true, false)).toBe(false);
|
|
512
|
+
expect(component.correctness(false, true)).toBe(false);
|
|
513
|
+
expect(component.correctness(false, false)).toBe(true);
|
|
514
|
+
});
|
|
515
|
+
});
|
|
516
|
+
|
|
517
|
+
describe('edge cases', () => {
|
|
518
|
+
it('should handle empty shapes object', () => {
|
|
519
|
+
const props = {
|
|
520
|
+
...defaultProps,
|
|
521
|
+
shapes: {},
|
|
522
|
+
};
|
|
523
|
+
const { container } = render(<Container {...props} />);
|
|
524
|
+
expect(container).toBeTruthy();
|
|
525
|
+
});
|
|
526
|
+
|
|
527
|
+
it('should handle null imageUrl', () => {
|
|
528
|
+
const props = {
|
|
529
|
+
...defaultProps,
|
|
530
|
+
imageUrl: null,
|
|
531
|
+
};
|
|
532
|
+
const { queryByAltText } = render(<Container {...props} />);
|
|
533
|
+
expect(queryByAltText('hotspot-image')).not.toBeInTheDocument();
|
|
534
|
+
});
|
|
535
|
+
|
|
536
|
+
it('should handle default scale value', () => {
|
|
537
|
+
const props = {
|
|
538
|
+
...defaultProps,
|
|
539
|
+
scale: undefined,
|
|
540
|
+
};
|
|
541
|
+
const { getByTestId } = render(<Container {...props} />);
|
|
542
|
+
const stage = getByTestId('stage');
|
|
543
|
+
expect(stage).toBeInTheDocument();
|
|
544
|
+
});
|
|
545
|
+
});
|
|
546
|
+
});
|