@pie-element/hotspot 10.0.0-next.42 → 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.
Files changed (54) hide show
  1. package/configure/lib/defaults.js +3 -0
  2. package/configure/lib/defaults.js.map +1 -1
  3. package/configure/lib/hotspot-circle.js +0 -1
  4. package/configure/lib/hotspot-circle.js.map +1 -1
  5. package/configure/lib/hotspot-drawable.js +5 -5
  6. package/configure/lib/hotspot-drawable.js.map +1 -1
  7. package/configure/lib/hotspot-polygon.js +1 -2
  8. package/configure/lib/hotspot-polygon.js.map +1 -1
  9. package/configure/lib/hotspot-rectangle.js +0 -1
  10. package/configure/lib/hotspot-rectangle.js.map +1 -1
  11. package/configure/lib/utils.js +2 -3
  12. package/configure/lib/utils.js.map +1 -1
  13. package/configure/package.json +6 -6
  14. package/configure/src/__tests__/DeleteWidget.test.jsx +366 -0
  15. package/configure/src/__tests__/button.test.jsx +198 -0
  16. package/configure/src/__tests__/hotspot-circle.test.jsx +259 -0
  17. package/configure/src/__tests__/hotspot-palette.test.jsx +71 -0
  18. package/configure/src/__tests__/image-konva.test.jsx +226 -0
  19. package/configure/src/defaults.js +1 -0
  20. package/configure/src/hotspot-circle.jsx +0 -1
  21. package/configure/src/hotspot-drawable.jsx +1 -1
  22. package/configure/src/hotspot-polygon.jsx +0 -1
  23. package/configure/src/hotspot-rectangle.jsx +0 -1
  24. package/configure/src/utils.js +1 -1
  25. package/controller/lib/index.js +2 -2
  26. package/controller/lib/index.js.map +1 -1
  27. package/controller/lib/utils.js +3 -5
  28. package/controller/lib/utils.js.map +1 -1
  29. package/controller/package.json +3 -3
  30. package/controller/src/index.js +1 -1
  31. package/controller/src/utils.js +1 -2
  32. package/lib/hotspot/circle.js +1 -2
  33. package/lib/hotspot/circle.js.map +1 -1
  34. package/lib/hotspot/polygon.js +0 -1
  35. package/lib/hotspot/polygon.js.map +1 -1
  36. package/lib/hotspot/rectangle.js +0 -1
  37. package/lib/hotspot/rectangle.js.map +1 -1
  38. package/package.json +12 -9
  39. package/src/hotspot/__tests__/circle.test.jsx +464 -0
  40. package/src/hotspot/__tests__/container.test.jsx +546 -0
  41. package/src/hotspot/__tests__/image-konva-tooltip.test.jsx +510 -0
  42. package/src/hotspot/__tests__/polygon.test.jsx +502 -0
  43. package/src/hotspot/__tests__/rectangle.test.jsx +418 -0
  44. package/src/hotspot/circle.jsx +0 -1
  45. package/src/hotspot/polygon.jsx +0 -1
  46. package/src/hotspot/rectangle.jsx +0 -1
  47. package/module/configure.js +0 -1
  48. package/module/controller.js +0 -4073
  49. package/module/demo.js +0 -143
  50. package/module/element.js +0 -1
  51. package/module/index.html +0 -21
  52. package/module/manifest.json +0 -14
  53. package/module/print-demo.js +0 -181
  54. package/module/print.html +0 -18
@@ -0,0 +1,259 @@
1
+ import React from 'react';
2
+ import { render, fireEvent } from '@testing-library/react';
3
+ import Konva from 'konva';
4
+ import CircleComponent from '../hotspot-circle';
5
+
6
+ Konva.isBrowser = false;
7
+
8
+ jest.mock('react-konva', () => {
9
+ const React = require('react');
10
+ return {
11
+ Circle: React.forwardRef(({ onClick, onTap, onMouseEnter, onMouseLeave, onDragStart, onDragEnd, onTransformStart, onTransformEnd, ...props }, ref) => {
12
+ const handleClick = (e) => {
13
+ if (onClick) onClick(e);
14
+ if (onTap) onTap(e);
15
+ };
16
+ return React.createElement('div', {
17
+ ref,
18
+ 'data-testid': 'circle',
19
+ onClick: handleClick,
20
+ onMouseEnter,
21
+ onMouseLeave,
22
+ ...props,
23
+ });
24
+ }),
25
+ Group: ({ children, onMouseEnter, onMouseLeave, ...props }) =>
26
+ React.createElement('div', { 'data-testid': 'group', onMouseEnter, onMouseLeave, ...props }, children),
27
+ Transformer: React.forwardRef(({ borderStroke, ...props }, ref) => {
28
+ return React.createElement('div', { ref, 'data-testid': 'transformer', 'data-border-stroke': borderStroke, ...props });
29
+ }),
30
+ };
31
+ });
32
+
33
+ jest.mock('../DeleteWidget', () => {
34
+ return function DeleteWidget({ id, handleWidgetClick }) {
35
+ return (
36
+ <div
37
+ data-testid="delete-widget"
38
+ data-id={id}
39
+ onClick={() => handleWidgetClick(id)}
40
+ />
41
+ );
42
+ };
43
+ });
44
+
45
+ describe('CircleComponent', () => {
46
+ let defaultProps;
47
+
48
+ beforeEach(() => {
49
+ defaultProps = {
50
+ id: 'circle1',
51
+ x: 100,
52
+ y: 150,
53
+ radius: 50,
54
+ hotspotColor: '#FF0000',
55
+ selectedHotspotColor: '#00FF00',
56
+ outlineColor: '#0000FF',
57
+ hoverOutlineColor: '#FFFF00',
58
+ correct: false,
59
+ isDrawing: false,
60
+ onClick: jest.fn(),
61
+ onDeleteShape: jest.fn(),
62
+ onDragEnd: jest.fn(),
63
+ strokeWidth: 5,
64
+ };
65
+
66
+ document.body.style.cursor = 'default';
67
+ });
68
+
69
+ afterEach(() => {
70
+ document.body.style.cursor = 'default';
71
+ });
72
+
73
+ describe('rendering', () => {
74
+ it('should render without crashing', () => {
75
+ const { container } = render(<CircleComponent {...defaultProps} />);
76
+ expect(container).toBeTruthy();
77
+ });
78
+
79
+ it('should render Group and Circle', () => {
80
+ const { getByTestId } = render(<CircleComponent {...defaultProps} />);
81
+ expect(getByTestId('group')).toBeInTheDocument();
82
+ expect(getByTestId('circle')).toBeInTheDocument();
83
+ });
84
+
85
+ it('should render with correct radius', () => {
86
+ const { getByTestId } = render(<CircleComponent {...defaultProps} radius={75} />);
87
+ const circle = getByTestId('circle');
88
+ expect(circle).toHaveAttribute('radius', '75');
89
+ });
90
+
91
+ it('should use minimum radius of 5 for invalid radius', () => {
92
+ const { getByTestId } = render(<CircleComponent {...defaultProps} radius={0} />);
93
+ const circle = getByTestId('circle');
94
+ expect(circle).toHaveAttribute('radius', '5');
95
+ });
96
+
97
+ it('should use minimum radius of 5 for NaN radius', () => {
98
+ const { getByTestId } = render(<CircleComponent {...defaultProps} radius={NaN} />);
99
+ const circle = getByTestId('circle');
100
+ expect(circle).toHaveAttribute('radius', '5');
101
+ });
102
+
103
+ it('should use minimum radius of 5 for negative radius', () => {
104
+ const { getByTestId } = render(<CircleComponent {...defaultProps} radius={-10} />);
105
+ const circle = getByTestId('circle');
106
+ expect(circle).toHaveAttribute('radius', '5');
107
+ });
108
+
109
+ it('should render with hotspot color when not correct', () => {
110
+ const { getByTestId } = render(<CircleComponent {...defaultProps} correct={false} />);
111
+ const circle = getByTestId('circle');
112
+ expect(circle).toHaveAttribute('fill', '#FF0000');
113
+ });
114
+
115
+ it('should render with selected color when correct', () => {
116
+ const { getByTestId } = render(<CircleComponent {...defaultProps} correct={true} />);
117
+ const circle = getByTestId('circle');
118
+ expect(circle).toHaveAttribute('fill', '#00FF00');
119
+ });
120
+ });
121
+
122
+ describe('interactions', () => {
123
+ it('should call onClick when clicked', () => {
124
+ const onClick = jest.fn();
125
+ const { getByTestId } = render(<CircleComponent {...defaultProps} onClick={onClick} />);
126
+ const circle = getByTestId('circle');
127
+
128
+ fireEvent.click(circle);
129
+
130
+ expect(onClick).toHaveBeenCalledWith('circle1');
131
+ });
132
+
133
+ it('should not call onClick when radius is 0 and isDrawing', () => {
134
+ const onClick = jest.fn();
135
+ const { getByTestId } = render(
136
+ <CircleComponent {...defaultProps} radius={0} isDrawing={true} onClick={onClick} />
137
+ );
138
+ const circle = getByTestId('circle');
139
+
140
+ fireEvent.click(circle);
141
+
142
+ expect(onClick).not.toHaveBeenCalled();
143
+ });
144
+ });
145
+
146
+ describe('hover state', () => {
147
+ it('should not show Transformer when not hovered', () => {
148
+ const { queryByTestId } = render(<CircleComponent {...defaultProps} />);
149
+
150
+ expect(queryByTestId('transformer')).not.toBeInTheDocument();
151
+ });
152
+ });
153
+
154
+ describe('drag functionality', () => {
155
+ it('should call onDragEnd with new position', () => {
156
+ const onDragEnd = jest.fn();
157
+ const { getByTestId } = render(
158
+ <CircleComponent {...defaultProps} onDragEnd={onDragEnd} />
159
+ );
160
+ const circle = getByTestId('circle');
161
+
162
+ const mockEvent = {
163
+ target: {
164
+ x: () => 200,
165
+ y: () => 250,
166
+ },
167
+ };
168
+
169
+ if (circle.onDragEnd) {
170
+ circle.onDragEnd(mockEvent);
171
+ }
172
+ });
173
+ });
174
+
175
+ describe('resize functionality', () => {
176
+ it('should handle resize with minimum radius constraint', () => {
177
+ const onDragEnd = jest.fn();
178
+ const { getByTestId } = render(
179
+ <CircleComponent {...defaultProps} onDragEnd={onDragEnd} />
180
+ );
181
+
182
+ // This tests the component's ability to handle transforms
183
+ // In actual usage, the transformer would modify the node's scale
184
+ const transformer = getByTestId('group');
185
+ expect(transformer).toBeInTheDocument();
186
+ });
187
+ });
188
+
189
+ describe('edge cases', () => {
190
+ it('should handle very large radius', () => {
191
+ const { getByTestId } = render(<CircleComponent {...defaultProps} radius={500} />);
192
+ const circle = getByTestId('circle');
193
+ expect(circle).toHaveAttribute('radius', '500');
194
+ });
195
+
196
+ it('should handle position at origin', () => {
197
+ const { getByTestId } = render(<CircleComponent {...defaultProps} x={0} y={0} />);
198
+ const circle = getByTestId('circle');
199
+ expect(circle).toHaveAttribute('x', '0');
200
+ expect(circle).toHaveAttribute('y', '0');
201
+ });
202
+
203
+ it('should handle negative positions', () => {
204
+ const { getByTestId } = render(<CircleComponent {...defaultProps} x={-50} y={-75} />);
205
+ const circle = getByTestId('circle');
206
+ expect(circle).toHaveAttribute('x', '-50');
207
+ expect(circle).toHaveAttribute('y', '-75');
208
+ });
209
+
210
+ it('should use default correct value of false', () => {
211
+ const { getByTestId } = render(<CircleComponent {...defaultProps} correct={undefined} />);
212
+ const circle = getByTestId('circle');
213
+ expect(circle).toHaveAttribute('fill', '#FF0000');
214
+ });
215
+
216
+ it('should handle missing selectedHotspotColor', () => {
217
+ const { getByTestId } = render(
218
+ <CircleComponent {...defaultProps} correct={true} selectedHotspotColor={undefined} />
219
+ );
220
+ const circle = getByTestId('circle');
221
+ // Should fall back to hotspotColor
222
+ expect(circle).toHaveAttribute('fill', '#FF0000');
223
+ });
224
+ });
225
+
226
+ describe('prop updates', () => {
227
+ it('should update radius when prop changes', () => {
228
+ const { getByTestId, rerender } = render(<CircleComponent {...defaultProps} radius={50} />);
229
+ let circle = getByTestId('circle');
230
+ expect(circle).toHaveAttribute('radius', '50');
231
+
232
+ rerender(<CircleComponent {...defaultProps} radius={75} />);
233
+ circle = getByTestId('circle');
234
+ expect(circle).toHaveAttribute('radius', '75');
235
+ });
236
+
237
+ it('should update color when correct prop changes', () => {
238
+ const { getByTestId, rerender } = render(<CircleComponent {...defaultProps} correct={false} />);
239
+ let circle = getByTestId('circle');
240
+ expect(circle).toHaveAttribute('fill', '#FF0000');
241
+
242
+ rerender(<CircleComponent {...defaultProps} correct={true} />);
243
+ circle = getByTestId('circle');
244
+ expect(circle).toHaveAttribute('fill', '#00FF00');
245
+ });
246
+
247
+ it('should update position when props change', () => {
248
+ const { getByTestId, rerender } = render(<CircleComponent {...defaultProps} x={100} y={150} />);
249
+ let circle = getByTestId('circle');
250
+ expect(circle).toHaveAttribute('x', '100');
251
+ expect(circle).toHaveAttribute('y', '150');
252
+
253
+ rerender(<CircleComponent {...defaultProps} x={200} y={250} />);
254
+ circle = getByTestId('circle');
255
+ expect(circle).toHaveAttribute('x', '200');
256
+ expect(circle).toHaveAttribute('y', '250');
257
+ });
258
+ });
259
+ });
@@ -0,0 +1,71 @@
1
+ import React from 'react';
2
+ import { render, fireEvent } from '@testing-library/react';
3
+ import Palette from '../hotspot-palette';
4
+
5
+ describe('Palette', () => {
6
+ let defaultProps;
7
+
8
+ beforeEach(() => {
9
+ defaultProps = {
10
+ hotspotColor: '#FF0000',
11
+ outlineColor: '#0000FF',
12
+ hotspotList: ['#FF0000', '#00FF00', '#0000FF', '#FFFF00'],
13
+ outlineList: ['#000000', '#0000FF', '#FF0000', '#00FF00'],
14
+ onHotspotColorChange: jest.fn(),
15
+ onOutlineColorChange: jest.fn(),
16
+ };
17
+ });
18
+
19
+ describe('rendering', () => {
20
+ it('should render without crashing', () => {
21
+ const { container } = render(<Palette {...defaultProps} />);
22
+ expect(container).toBeTruthy();
23
+ });
24
+ });
25
+
26
+ describe('edge cases', () => {
27
+ it('should handle empty hotspot list gracefully', () => {
28
+ const { container } = render(<Palette {...defaultProps} hotspotList={[]} />);
29
+ expect(container).toBeTruthy();
30
+ });
31
+
32
+ it('should handle empty outline list gracefully', () => {
33
+ const { container } = render(<Palette {...defaultProps} outlineList={[]} />);
34
+ expect(container).toBeTruthy();
35
+ });
36
+ });
37
+
38
+ describe('onChange handler', () => {
39
+ it('should create onChange handler for hotspot', () => {
40
+ const component = new Palette(defaultProps);
41
+ const handler = component.onChange('hotspot');
42
+ expect(typeof handler).toBe('function');
43
+ });
44
+
45
+ it('should create onChange handler for outline', () => {
46
+ const component = new Palette(defaultProps);
47
+ const handler = component.onChange('outline');
48
+ expect(typeof handler).toBe('function');
49
+ });
50
+
51
+ it('should call onHotspotColorChange when hotspot handler is invoked', () => {
52
+ const onHotspotColorChange = jest.fn();
53
+ const component = new Palette({ ...defaultProps, onHotspotColorChange });
54
+ const handler = component.onChange('hotspot');
55
+
56
+ handler({ target: { value: '#00FF00' } });
57
+
58
+ expect(onHotspotColorChange).toHaveBeenCalledWith('#00FF00');
59
+ });
60
+
61
+ it('should call onOutlineColorChange when outline handler is invoked', () => {
62
+ const onOutlineColorChange = jest.fn();
63
+ const component = new Palette({ ...defaultProps, onOutlineColorChange });
64
+ const handler = component.onChange('outline');
65
+
66
+ handler({ target: { value: '#FF0000' } });
67
+
68
+ expect(onOutlineColorChange).toHaveBeenCalledWith('#FF0000');
69
+ });
70
+ });
71
+ });
@@ -0,0 +1,226 @@
1
+ import React from 'react';
2
+ import { render, waitFor } from '@testing-library/react';
3
+ import Konva from 'konva';
4
+ import ImageComponent from '../image-konva';
5
+
6
+ Konva.isBrowser = false;
7
+
8
+ jest.mock('react-konva', () => {
9
+ const React = require('react');
10
+ return {
11
+ Image: (props) => React.createElement('div', { 'data-testid': 'image', ...props }),
12
+ };
13
+ });
14
+
15
+ describe('ImageComponent', () => {
16
+ let defaultProps;
17
+
18
+ beforeEach(() => {
19
+ defaultProps = {
20
+ src: 'test-image.png',
21
+ x: 100,
22
+ y: 150,
23
+ };
24
+ });
25
+
26
+ describe('rendering', () => {
27
+ it('should render without crashing', () => {
28
+ const { container } = render(<ImageComponent {...defaultProps} />);
29
+ expect(container).toBeTruthy();
30
+ });
31
+
32
+ it('should render Image component', () => {
33
+ const { getByTestId } = render(<ImageComponent {...defaultProps} />);
34
+ expect(getByTestId('image')).toBeInTheDocument();
35
+ });
36
+
37
+ it('should render with correct position', () => {
38
+ const { getByTestId } = render(<ImageComponent {...defaultProps} x={200} y={250} />);
39
+ const image = getByTestId('image');
40
+ expect(image).toHaveAttribute('x', '200');
41
+ expect(image).toHaveAttribute('y', '250');
42
+ });
43
+
44
+ it('should render with fixed dimensions', () => {
45
+ const { getByTestId } = render(<ImageComponent {...defaultProps} />);
46
+ const image = getByTestId('image');
47
+ expect(image).toHaveAttribute('width', '20');
48
+ expect(image).toHaveAttribute('height', '20');
49
+ });
50
+
51
+ it('should render at origin (0, 0)', () => {
52
+ const { getByTestId } = render(<ImageComponent {...defaultProps} x={0} y={0} />);
53
+ const image = getByTestId('image');
54
+ expect(image).toHaveAttribute('x', '0');
55
+ expect(image).toHaveAttribute('y', '0');
56
+ });
57
+ });
58
+
59
+ describe('image loading', () => {
60
+ it('should load image on mount', async () => {
61
+ const { getByTestId } = render(<ImageComponent {...defaultProps} />);
62
+
63
+ const image = getByTestId('image');
64
+ expect(image).toBeInTheDocument();
65
+ });
66
+
67
+ it('should call loadImage on mount', () => {
68
+ const { container } = render(<ImageComponent {...defaultProps} src="custom-image.jpg" />);
69
+
70
+ expect(container).toBeTruthy();
71
+ });
72
+ });
73
+
74
+ describe('component lifecycle', () => {
75
+ it('should reload image when src changes', async () => {
76
+ const { rerender, container } = render(<ImageComponent {...defaultProps} src="image1.png" />);
77
+
78
+ expect(container).toBeTruthy();
79
+
80
+ rerender(<ImageComponent {...defaultProps} src="image2.png" />);
81
+
82
+ expect(container).toBeTruthy();
83
+ });
84
+
85
+ it('should not reload image when src stays the same', async () => {
86
+ const { rerender, getByTestId } = render(<ImageComponent {...defaultProps} src="same-image.png" />);
87
+
88
+ const image = getByTestId('image');
89
+ expect(image).toBeInTheDocument();
90
+
91
+ rerender(<ImageComponent {...defaultProps} src="same-image.png" x={200} y={250} />);
92
+
93
+ const updatedImage = getByTestId('image');
94
+ expect(updatedImage).toHaveAttribute('x', '200');
95
+ expect(updatedImage).toHaveAttribute('y', '250');
96
+ });
97
+
98
+ it('should clean up event listener on unmount', () => {
99
+ const { unmount } = render(<ImageComponent {...defaultProps} />);
100
+
101
+ expect(() => {
102
+ unmount();
103
+ }).not.toThrow();
104
+ });
105
+
106
+ it('should handle multiple mount/unmount cycles', () => {
107
+ const { unmount } = render(<ImageComponent {...defaultProps} />);
108
+ unmount();
109
+
110
+ expect(() => {
111
+ render(<ImageComponent {...defaultProps} />);
112
+ }).not.toThrow();
113
+ });
114
+ });
115
+
116
+ describe('position updates', () => {
117
+ it('should update x position', () => {
118
+ const { getByTestId, rerender } = render(<ImageComponent {...defaultProps} x={100} />);
119
+ let image = getByTestId('image');
120
+ expect(image).toHaveAttribute('x', '100');
121
+
122
+ rerender(<ImageComponent {...defaultProps} x={200} />);
123
+ image = getByTestId('image');
124
+ expect(image).toHaveAttribute('x', '200');
125
+ });
126
+
127
+ it('should update y position', () => {
128
+ const { getByTestId, rerender } = render(<ImageComponent {...defaultProps} y={150} />);
129
+ let image = getByTestId('image');
130
+ expect(image).toHaveAttribute('y', '150');
131
+
132
+ rerender(<ImageComponent {...defaultProps} y={250} />);
133
+ image = getByTestId('image');
134
+ expect(image).toHaveAttribute('y', '250');
135
+ });
136
+
137
+ it('should update both x and y positions', () => {
138
+ const { getByTestId, rerender } = render(<ImageComponent {...defaultProps} x={100} y={150} />);
139
+ let image = getByTestId('image');
140
+ expect(image).toHaveAttribute('x', '100');
141
+ expect(image).toHaveAttribute('y', '150');
142
+
143
+ rerender(<ImageComponent {...defaultProps} x={300} y={400} />);
144
+ image = getByTestId('image');
145
+ expect(image).toHaveAttribute('x', '300');
146
+ expect(image).toHaveAttribute('y', '400');
147
+ });
148
+ });
149
+
150
+ describe('edge cases', () => {
151
+ it('should handle negative positions', () => {
152
+ const { getByTestId } = render(<ImageComponent {...defaultProps} x={-50} y={-75} />);
153
+ const image = getByTestId('image');
154
+ expect(image).toHaveAttribute('x', '-50');
155
+ expect(image).toHaveAttribute('y', '-75');
156
+ });
157
+
158
+ it('should handle very large positions', () => {
159
+ const { getByTestId } = render(<ImageComponent {...defaultProps} x={10000} y={20000} />);
160
+ const image = getByTestId('image');
161
+ expect(image).toHaveAttribute('x', '10000');
162
+ expect(image).toHaveAttribute('y', '20000');
163
+ });
164
+
165
+ it('should handle data URI as src', () => {
166
+ const dataUri = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==';
167
+ const { container } = render(<ImageComponent {...defaultProps} src={dataUri} />);
168
+
169
+ expect(container).toBeTruthy();
170
+ });
171
+
172
+ it('should handle SVG as src', () => {
173
+ const { container } = render(<ImageComponent {...defaultProps} src="icon.svg" />);
174
+ expect(container).toBeTruthy();
175
+ });
176
+
177
+ it('should handle empty src', () => {
178
+ const { container } = render(<ImageComponent {...defaultProps} src="" />);
179
+ expect(container).toBeTruthy();
180
+ });
181
+
182
+ it('should handle URL with query parameters', () => {
183
+ const srcWithParams = 'image.png?width=100&height=100';
184
+ const { container } = render(<ImageComponent {...defaultProps} src={srcWithParams} />);
185
+
186
+ expect(container).toBeTruthy();
187
+ });
188
+
189
+ it('should handle relative paths', () => {
190
+ const { container } = render(<ImageComponent {...defaultProps} src="./images/icon.png" />);
191
+ expect(container).toBeTruthy();
192
+ });
193
+
194
+ it('should handle absolute paths', () => {
195
+ const { container } = render(<ImageComponent {...defaultProps} src="/assets/icon.png" />);
196
+ expect(container).toBeTruthy();
197
+ });
198
+ });
199
+
200
+ describe('image state', () => {
201
+ it('should initially render without image loaded', () => {
202
+ const { getByTestId } = render(<ImageComponent {...defaultProps} />);
203
+ const image = getByTestId('image');
204
+ expect(image).toBeInTheDocument();
205
+ });
206
+
207
+ it('should render component regardless of image load state', () => {
208
+ const { getByTestId } = render(<ImageComponent {...defaultProps} />);
209
+ const image = getByTestId('image');
210
+ expect(image).toBeInTheDocument();
211
+ });
212
+ });
213
+
214
+ describe('error handling', () => {
215
+ it('should handle image load error gracefully', () => {
216
+ const { container } = render(<ImageComponent {...defaultProps} src="nonexistent.png" />);
217
+
218
+ const imgElements = document.querySelectorAll('img[src="nonexistent.png"]');
219
+ imgElements.forEach(img => {
220
+ img.dispatchEvent(new Event('error'));
221
+ });
222
+
223
+ expect(container).toBeTruthy();
224
+ });
225
+ });
226
+ });
@@ -20,6 +20,7 @@ export default {
20
20
  },
21
21
  configuration: {
22
22
  baseInputConfiguration: {
23
+ h3: { disabled: true },
23
24
  audio: { disabled: false },
24
25
  video: { disabled: false },
25
26
  image: { disabled: false },
@@ -109,7 +109,6 @@ class CircleComponent extends React.Component {
109
109
  onTransformEnd={this.onResizeEnd}
110
110
  x={x}
111
111
  y={y}
112
- opacity={0.5}
113
112
  cursor="pointer"
114
113
  />
115
114
 
@@ -1,7 +1,7 @@
1
1
  import React from 'react';
2
2
  import PropTypes from 'prop-types';
3
3
  import { Layer, Stage } from 'react-konva';
4
- import cloneDeep from 'lodash/cloneDeep';
4
+ import { cloneDeep } from 'lodash-es';
5
5
  import { styled } from '@mui/material/styles';
6
6
 
7
7
  import Rectangle from './hotspot-rectangle';
@@ -232,7 +232,6 @@ class PolComponent extends React.Component {
232
232
  onDragEnd={(e) => this.handleOnDragEnd(e, true)}
233
233
  x={x}
234
234
  y={y}
235
- opacity={0.5}
236
235
  />
237
236
 
238
237
  {showPoints &&
@@ -119,7 +119,6 @@ class RectComponent extends React.Component {
119
119
  onTransformEnd={this.onResizeEnd}
120
120
  x={x}
121
121
  y={y}
122
- opacity={0.5}
123
122
  cursor="pointer"
124
123
  />
125
124
  {!this.state.isDragging && this.state.hovered && (
@@ -1,4 +1,4 @@
1
- import cloneDeep from 'lodash/cloneDeep';
1
+ import { cloneDeep } from 'lodash-es';
2
2
  import { SHAPE_GROUPS } from './shapes';
3
3
 
4
4
  const updateImageDimensions = (initialDim, nextDim, keepAspectRatio, resizeType) => {
@@ -10,7 +10,7 @@ exports.normalize = void 0;
10
10
  exports.outcome = outcome;
11
11
  exports.validate = void 0;
12
12
  var _debug = _interopRequireDefault(require("debug"));
13
- var _isEmpty = _interopRequireDefault(require("lodash/isEmpty"));
13
+ var _lodashEs = require("lodash-es");
14
14
  var _controllerUtils = require("@pie-lib/controller-utils");
15
15
  var _utils = require("./utils");
16
16
  var _defaults = _interopRequireDefault(require("./defaults"));
@@ -160,7 +160,7 @@ const getScore = (config, session, env = {}) => {
160
160
  function outcome(config, session, env = {}) {
161
161
  return new Promise(resolve => {
162
162
  log('outcome...');
163
- if (!session || (0, _isEmpty.default)(session)) {
163
+ if (!session || (0, _lodashEs.isEmpty)(session)) {
164
164
  resolve({
165
165
  score: 0,
166
166
  empty: true