@pie-element/hotspot 11.1.2-next.2 → 11.1.2

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 (234) hide show
  1. package/CHANGELOG.json +997 -0
  2. package/CHANGELOG.md +2220 -0
  3. package/LICENSE.md +5 -0
  4. package/README.md +1 -0
  5. package/configure/CHANGELOG.json +682 -0
  6. package/configure/CHANGELOG.md +1957 -0
  7. package/configure/lib/DeleteWidget.js +64 -0
  8. package/configure/lib/DeleteWidget.js.map +1 -0
  9. package/configure/lib/button.js +42 -0
  10. package/configure/lib/button.js.map +1 -0
  11. package/configure/lib/buttons/circle.js +33 -0
  12. package/configure/lib/buttons/circle.js.map +1 -0
  13. package/configure/lib/buttons/polygon.js +39 -0
  14. package/configure/lib/buttons/polygon.js.map +1 -0
  15. package/configure/lib/buttons/rectangle.js +39 -0
  16. package/configure/lib/buttons/rectangle.js.map +1 -0
  17. package/configure/lib/defaults.js +155 -0
  18. package/configure/lib/defaults.js.map +1 -0
  19. package/configure/lib/hotspot-circle.js +192 -0
  20. package/configure/lib/hotspot-circle.js.map +1 -0
  21. package/configure/lib/hotspot-container.js +320 -0
  22. package/configure/lib/hotspot-container.js.map +1 -0
  23. package/configure/lib/hotspot-drawable.js +519 -0
  24. package/configure/lib/hotspot-drawable.js.map +1 -0
  25. package/configure/lib/hotspot-palette.js +107 -0
  26. package/configure/lib/hotspot-palette.js.map +1 -0
  27. package/configure/lib/hotspot-polygon.js +293 -0
  28. package/configure/lib/hotspot-polygon.js.map +1 -0
  29. package/configure/lib/hotspot-rectangle.js +190 -0
  30. package/configure/lib/hotspot-rectangle.js.map +1 -0
  31. package/configure/lib/icons.js +7 -0
  32. package/configure/lib/icons.js.map +1 -0
  33. package/configure/lib/image-konva.js +66 -0
  34. package/configure/lib/image-konva.js.map +1 -0
  35. package/configure/lib/index.js +194 -0
  36. package/configure/lib/index.js.map +1 -0
  37. package/configure/lib/root.js +330 -0
  38. package/configure/lib/root.js.map +1 -0
  39. package/configure/lib/shapes/circle.js +84 -0
  40. package/configure/lib/shapes/circle.js.map +1 -0
  41. package/configure/lib/shapes/index.js +50 -0
  42. package/configure/lib/shapes/index.js.map +1 -0
  43. package/configure/lib/shapes/polygon.js +82 -0
  44. package/configure/lib/shapes/polygon.js.map +1 -0
  45. package/configure/lib/shapes/rectagle.js +84 -0
  46. package/configure/lib/shapes/rectagle.js.map +1 -0
  47. package/configure/lib/shapes/utils.js +21 -0
  48. package/configure/lib/shapes/utils.js.map +1 -0
  49. package/configure/lib/upload-control.js +41 -0
  50. package/configure/lib/upload-control.js.map +1 -0
  51. package/configure/lib/utils.js +185 -0
  52. package/configure/lib/utils.js.map +1 -0
  53. package/configure/package.json +26 -0
  54. package/configure/src/DeleteWidget.jsx +51 -0
  55. package/configure/src/__tests__/DeleteWidget.test.jsx +366 -0
  56. package/configure/src/__tests__/button.test.jsx +198 -0
  57. package/configure/src/__tests__/hotspot-circle.test.jsx +259 -0
  58. package/configure/src/__tests__/hotspot-container.test.js +366 -0
  59. package/configure/src/__tests__/hotspot-drawable.test.js +271 -0
  60. package/configure/src/__tests__/hotspot-palette.test.jsx +71 -0
  61. package/configure/src/__tests__/image-konva.test.jsx +226 -0
  62. package/configure/src/__tests__/index.test.js +329 -0
  63. package/configure/src/__tests__/root.test.js +400 -0
  64. package/configure/src/__tests__/utils.test.js +241 -0
  65. package/configure/src/button.jsx +35 -0
  66. package/configure/src/buttons/circle.jsx +18 -0
  67. package/configure/src/buttons/polygon.jsx +29 -0
  68. package/configure/src/buttons/rectangle.jsx +29 -0
  69. package/configure/src/defaults.js +109 -0
  70. package/configure/src/hotspot-circle.jsx +183 -0
  71. package/configure/src/hotspot-container.jsx +330 -0
  72. package/configure/src/hotspot-drawable.jsx +527 -0
  73. package/configure/src/hotspot-palette.jsx +90 -0
  74. package/configure/src/hotspot-polygon.jsx +294 -0
  75. package/configure/src/hotspot-rectangle.jsx +169 -0
  76. package/configure/src/icons.js +5 -0
  77. package/configure/src/image-konva.jsx +63 -0
  78. package/configure/src/index.js +208 -0
  79. package/configure/src/root.jsx +346 -0
  80. package/configure/src/shapes/circle.js +81 -0
  81. package/configure/src/shapes/index.js +4 -0
  82. package/configure/src/shapes/polygon.js +81 -0
  83. package/configure/src/shapes/rectagle.js +82 -0
  84. package/configure/src/shapes/utils.js +16 -0
  85. package/configure/src/upload-control.jsx +33 -0
  86. package/configure/src/utils.js +210 -0
  87. package/controller/CHANGELOG.json +362 -0
  88. package/controller/CHANGELOG.md +1304 -0
  89. package/controller/lib/defaults.js +33 -0
  90. package/controller/lib/defaults.js.map +1 -0
  91. package/controller/lib/index.js +341 -0
  92. package/controller/lib/index.js.map +1 -0
  93. package/controller/lib/utils.js +32 -0
  94. package/controller/lib/utils.js.map +1 -0
  95. package/controller/package.json +18 -0
  96. package/controller/src/__tests__/index.test.js +419 -0
  97. package/controller/src/__tests__/utils.test.js +5 -0
  98. package/controller/src/defaults.js +19 -0
  99. package/controller/src/index.js +328 -0
  100. package/controller/src/utils.js +29 -0
  101. package/docs/config-schema.json +2023 -0
  102. package/docs/config-schema.json.md +1495 -0
  103. package/docs/demo/config.js +8 -0
  104. package/docs/demo/generate.js +118 -0
  105. package/docs/demo/index.html +1 -0
  106. package/docs/demo/session.js +11 -0
  107. package/docs/pie-schema.json +1204 -0
  108. package/docs/pie-schema.json.md +851 -0
  109. package/lib/hotspot/circle.js +156 -0
  110. package/lib/hotspot/circle.js.map +1 -0
  111. package/lib/hotspot/container.js +206 -0
  112. package/lib/hotspot/container.js.map +1 -0
  113. package/lib/hotspot/icons.js +8 -0
  114. package/lib/hotspot/icons.js.map +1 -0
  115. package/lib/hotspot/image-konva-tooltip.js +86 -0
  116. package/lib/hotspot/image-konva-tooltip.js.map +1 -0
  117. package/lib/hotspot/index.js +163 -0
  118. package/lib/hotspot/index.js.map +1 -0
  119. package/lib/hotspot/polygon.js +203 -0
  120. package/lib/hotspot/polygon.js.map +1 -0
  121. package/lib/hotspot/rectangle.js +175 -0
  122. package/lib/hotspot/rectangle.js.map +1 -0
  123. package/lib/index.js +213 -0
  124. package/lib/index.js.map +1 -0
  125. package/lib/session-updater.js +42 -0
  126. package/lib/session-updater.js.map +1 -0
  127. package/package.json +18 -83
  128. package/src/__tests__/container.test.jsx +58 -0
  129. package/src/__tests__/index.test.js +123 -0
  130. package/src/__tests__/session-updater.test.jsx +69 -0
  131. package/src/hotspot/__tests__/circle.test.jsx +464 -0
  132. package/src/hotspot/__tests__/container.test.jsx +546 -0
  133. package/src/hotspot/__tests__/image-konva-tooltip.test.jsx +510 -0
  134. package/src/hotspot/__tests__/polygon.test.jsx +502 -0
  135. package/src/hotspot/__tests__/rectangle.test.jsx +418 -0
  136. package/src/hotspot/circle.jsx +152 -0
  137. package/src/hotspot/container.jsx +217 -0
  138. package/src/hotspot/icons.js +7 -0
  139. package/src/hotspot/image-konva-tooltip.jsx +76 -0
  140. package/src/hotspot/index.jsx +165 -0
  141. package/src/hotspot/polygon.jsx +195 -0
  142. package/src/hotspot/rectangle.jsx +171 -0
  143. package/src/index.js +226 -0
  144. package/src/session-updater.js +29 -0
  145. package/configure.js +0 -2
  146. package/controller.js +0 -1
  147. package/dist/author/DeleteWidget.d.ts +0 -38
  148. package/dist/author/DeleteWidget.js +0 -46
  149. package/dist/author/button.d.ts +0 -31
  150. package/dist/author/button.js +0 -27
  151. package/dist/author/buttons/circle.d.ts +0 -18
  152. package/dist/author/buttons/circle.js +0 -25
  153. package/dist/author/buttons/polygon.d.ts +0 -18
  154. package/dist/author/buttons/polygon.js +0 -36
  155. package/dist/author/buttons/rectangle.d.ts +0 -18
  156. package/dist/author/buttons/rectangle.js +0 -36
  157. package/dist/author/defaults.d.ts +0 -157
  158. package/dist/author/defaults.js +0 -119
  159. package/dist/author/hotspot-circle.d.ts +0 -21
  160. package/dist/author/hotspot-circle.js +0 -124
  161. package/dist/author/hotspot-container.d.ts +0 -29
  162. package/dist/author/hotspot-container.js +0 -210
  163. package/dist/author/hotspot-drawable.d.ts +0 -31
  164. package/dist/author/hotspot-drawable.js +0 -312
  165. package/dist/author/hotspot-palette.d.ts +0 -14
  166. package/dist/author/hotspot-palette.js +0 -72
  167. package/dist/author/hotspot-polygon.d.ts +0 -38
  168. package/dist/author/hotspot-polygon.js +0 -200
  169. package/dist/author/hotspot-rectangle.d.ts +0 -20
  170. package/dist/author/hotspot-rectangle.js +0 -119
  171. package/dist/author/icons.d.ts +0 -9
  172. package/dist/author/icons.js +0 -4
  173. package/dist/author/image-konva.d.ts +0 -19
  174. package/dist/author/image-konva.js +0 -49
  175. package/dist/author/index.d.ts +0 -52
  176. package/dist/author/index.js +0 -143
  177. package/dist/author/root.d.ts +0 -15
  178. package/dist/author/root.js +0 -215
  179. package/dist/author/shapes/circle.d.ts +0 -18
  180. package/dist/author/shapes/circle.js +0 -47
  181. package/dist/author/shapes/index.d.ts +0 -12
  182. package/dist/author/shapes/polygon.d.ts +0 -19
  183. package/dist/author/shapes/polygon.js +0 -51
  184. package/dist/author/shapes/rectagle.d.ts +0 -18
  185. package/dist/author/shapes/rectagle.js +0 -57
  186. package/dist/author/shapes/utils.d.ts +0 -19
  187. package/dist/author/shapes/utils.js +0 -16
  188. package/dist/author/upload-control.d.ts +0 -29
  189. package/dist/author/upload-control.js +0 -28
  190. package/dist/author/utils.d.ts +0 -24
  191. package/dist/author/utils.js +0 -83
  192. package/dist/browser/ReactKonva-DI5WIo8o.js +0 -19336
  193. package/dist/browser/ReactKonva-DI5WIo8o.js.map +0 -1
  194. package/dist/browser/author/index.js +0 -41646
  195. package/dist/browser/author/index.js.map +0 -1
  196. package/dist/browser/browser-CfnAFove.js +0 -219
  197. package/dist/browser/browser-CfnAFove.js.map +0 -1
  198. package/dist/browser/controller/index.js +0 -198
  199. package/dist/browser/controller/index.js.map +0 -1
  200. package/dist/browser/delivery/index.js +0 -2460
  201. package/dist/browser/delivery/index.js.map +0 -1
  202. package/dist/browser/dist-C78LDz6R.js +0 -96
  203. package/dist/browser/dist-C78LDz6R.js.map +0 -1
  204. package/dist/browser/hotspot.css +0 -2
  205. package/dist/controller/defaults.d.ts +0 -35
  206. package/dist/controller/defaults.js +0 -29
  207. package/dist/controller/index.d.ts +0 -22
  208. package/dist/controller/index.js +0 -154
  209. package/dist/controller/utils.d.ts +0 -10
  210. package/dist/controller/utils.js +0 -12
  211. package/dist/delivery/hotspot/circle.d.ts +0 -19
  212. package/dist/delivery/hotspot/circle.js +0 -100
  213. package/dist/delivery/hotspot/container.d.ts +0 -16
  214. package/dist/delivery/hotspot/container.js +0 -150
  215. package/dist/delivery/hotspot/icons.d.ts +0 -10
  216. package/dist/delivery/hotspot/icons.js +0 -4
  217. package/dist/delivery/hotspot/image-konva-tooltip.d.ts +0 -19
  218. package/dist/delivery/hotspot/image-konva-tooltip.js +0 -66
  219. package/dist/delivery/hotspot/index.d.ts +0 -17
  220. package/dist/delivery/hotspot/index.js +0 -114
  221. package/dist/delivery/hotspot/polygon.d.ts +0 -21
  222. package/dist/delivery/hotspot/polygon.js +0 -108
  223. package/dist/delivery/hotspot/rectangle.d.ts +0 -19
  224. package/dist/delivery/hotspot/rectangle.js +0 -104
  225. package/dist/delivery/index.d.ts +0 -20
  226. package/dist/delivery/index.js +0 -107
  227. package/dist/delivery/session-updater.d.ts +0 -10
  228. package/dist/delivery/session-updater.js +0 -14
  229. package/dist/index.d.ts +0 -1
  230. package/dist/index.iife.d.ts +0 -8
  231. package/dist/index.iife.js +0 -169
  232. package/dist/index.js +0 -2
  233. package/dist/runtime-support.d.ts +0 -12
  234. package/dist/runtime-support.js +0 -12
@@ -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,366 @@
1
+ import { render } from '@testing-library/react';
2
+ import React from 'react';
3
+ import { Container } from '../hotspot-container';
4
+ import { getAllShapes, groupShapes } from '../utils';
5
+
6
+ jest.mock('react-konva', () => {
7
+ const React = require('react');
8
+ return {
9
+ Stage: ({ children, ...props }) => React.createElement('div', { 'data-testid': 'stage', ...props }, children),
10
+ Layer: ({ children, ...props }) => React.createElement('div', { 'data-testid': 'layer', ...props }, children),
11
+ Rect: (props) => React.createElement('div', { 'data-testid': 'rect', ...props }),
12
+ Circle: (props) => React.createElement('div', { 'data-testid': 'circle', ...props }),
13
+ Line: (props) => React.createElement('div', { 'data-testid': 'line', ...props }),
14
+ Group: ({ children, ...props }) => React.createElement('div', { 'data-testid': 'group', ...props }, children),
15
+ Image: (props) => React.createElement('div', { 'data-testid': 'image', ...props }),
16
+ };
17
+ });
18
+
19
+ const model = () => ({
20
+ imageUrl: 'https://cdn.fluence.net/image/0240eb1455ce4c4bb6180232347b6aef_W',
21
+ shapes: groupShapes(
22
+ getAllShapes({
23
+ rectangles: [
24
+ {
25
+ id: '0',
26
+ height: 140,
27
+ width: 130,
28
+ x: 1,
29
+ y: 1,
30
+ correct: true,
31
+ },
32
+ {
33
+ id: '1',
34
+ height: 140,
35
+ width: 130,
36
+ x: 140,
37
+ y: 1,
38
+ },
39
+ {
40
+ id: '2',
41
+ height: 140,
42
+ width: 130,
43
+ x: 280,
44
+ y: 1,
45
+ },
46
+ ],
47
+ polygons: [
48
+ {
49
+ id: '3',
50
+ points: [
51
+ { x: 1, y: 148 },
52
+ { x: 1, y: 288 },
53
+ { y: 288, x: 129 },
54
+ { y: 148, x: 129 },
55
+ ],
56
+ correct: true,
57
+ },
58
+ {
59
+ id: '4',
60
+ points: [
61
+ { y: 151, x: 141 },
62
+ { y: 289, x: 141 },
63
+ { y: 289, x: 269 },
64
+ { x: 269, y: 151 },
65
+ ],
66
+ correct: false,
67
+ },
68
+ {
69
+ id: '5',
70
+ points: [
71
+ { x: 279, y: 150 },
72
+ { x: 279, y: 289 },
73
+ { x: 407, y: 289 },
74
+ { x: 407, y: 150 },
75
+ ],
76
+ correct: false,
77
+ },
78
+ ],
79
+ circles: [
80
+ {
81
+ id: '6',
82
+ radius: 70,
83
+ x: 100,
84
+ y: 100,
85
+ correct: false,
86
+ },
87
+ {
88
+ id: '7',
89
+ radius: 30,
90
+ x: 200,
91
+ y: 150,
92
+ correct: true,
93
+ },
94
+ ],
95
+ }),
96
+ ),
97
+ dimensions: {
98
+ height: 291,
99
+ width: 410,
100
+ },
101
+ hotspotColor: 'rgba(137, 183, 244, 0.65)',
102
+ outlineColor: 'blue',
103
+ multipleCorrect: true,
104
+ });
105
+
106
+ describe('HotspotContainer', () => {
107
+ let w,
108
+ onImageUpload = jest.fn(),
109
+ onUpdateImageDimension = jest.fn(),
110
+ onDeleteShape = jest.fn(),
111
+ onUpdateShapes = jest.fn(),
112
+ initialModel = model();
113
+ beforeEach(() => {
114
+ w = (extras) => {
115
+ const props = {
116
+ classes: {},
117
+ dimensions: initialModel.dimensions,
118
+ imageUrl: initialModel.imageUrl,
119
+ multipleCorrect: initialModel.multipleCorrect,
120
+ hotspotColor: initialModel.hotspotColor,
121
+ outlineColor: initialModel.outlineColor,
122
+ onUpdateImageDimension: onUpdateImageDimension,
123
+ onUpdateShapes: onUpdateShapes,
124
+ onDeleteShape: onDeleteShape,
125
+ onImageUpload: onImageUpload,
126
+ shapes: initialModel.shapes,
127
+ ...extras,
128
+ };
129
+
130
+ return render(<Container {...props} />);
131
+ };
132
+ });
133
+
134
+ describe('logic', () => {
135
+ const createInstance = () => {
136
+ const props = {
137
+ classes: {},
138
+ dimensions: initialModel.dimensions,
139
+ imageUrl: initialModel.imageUrl,
140
+ multipleCorrect: initialModel.multipleCorrect,
141
+ hotspotColor: initialModel.hotspotColor,
142
+ outlineColor: initialModel.outlineColor,
143
+ onUpdateImageDimension: onUpdateImageDimension,
144
+ onUpdateShapes: onUpdateShapes,
145
+ onDeleteShape: onDeleteShape,
146
+ onImageUpload: onImageUpload,
147
+ shapes: initialModel.shapes,
148
+ };
149
+ const instance = new Container(props);
150
+
151
+ // Mock setState to execute callback immediately for testing
152
+ instance.setState = jest.fn((state, callback) => {
153
+ Object.assign(instance.state, typeof state === 'function' ? state(instance.state) : state);
154
+ if (callback) callback();
155
+ });
156
+
157
+ return instance;
158
+ };
159
+
160
+ let formattedShapes = [
161
+ {
162
+ id: '0',
163
+ height: 140,
164
+ width: 130,
165
+ x: 1,
166
+ y: 1,
167
+ correct: true,
168
+ group: 'rectangles',
169
+ index: 0,
170
+ },
171
+ {
172
+ id: '1',
173
+ height: 140,
174
+ width: 130,
175
+ x: 140,
176
+ y: 1,
177
+ group: 'rectangles',
178
+ index: 1,
179
+ },
180
+ {
181
+ id: '2',
182
+ height: 140,
183
+ width: 130,
184
+ x: 280,
185
+ y: 1,
186
+ group: 'rectangles',
187
+ index: 2,
188
+ },
189
+ {
190
+ id: '3',
191
+ points: [
192
+ { x: 1, y: 148 },
193
+ { x: 1, y: 288 },
194
+ { y: 288, x: 129 },
195
+ { y: 148, x: 129 },
196
+ ],
197
+ correct: true,
198
+ group: 'polygons',
199
+ index: 3,
200
+ },
201
+ {
202
+ id: '4',
203
+ points: [
204
+ { y: 151, x: 141 },
205
+ { y: 289, x: 141 },
206
+ { y: 289, x: 269 },
207
+ { x: 269, y: 151 },
208
+ ],
209
+ correct: false,
210
+ group: 'polygons',
211
+ index: 4,
212
+ },
213
+ {
214
+ id: '5',
215
+ points: [
216
+ { x: 279, y: 150 },
217
+ { x: 279, y: 289 },
218
+ { x: 407, y: 289 },
219
+ { x: 407, y: 150 },
220
+ ],
221
+ correct: false,
222
+ group: 'polygons',
223
+ index: 5,
224
+ },
225
+ {
226
+ id: '6',
227
+ radius: 70,
228
+ x: 100,
229
+ y: 100,
230
+ correct: false,
231
+ group: 'circles',
232
+ index: 6,
233
+ },
234
+ {
235
+ id: '7',
236
+ radius: 30,
237
+ x: 200,
238
+ y: 150,
239
+ correct: true,
240
+ group: 'circles',
241
+ index: 7,
242
+ },
243
+ ];
244
+
245
+ it('state will contain formatted shapes', () => {
246
+ const instance = createInstance();
247
+ expect(instance.state.shapes).toEqual(expect.arrayContaining(formattedShapes));
248
+ });
249
+
250
+ it('onUpdateShapes with new added shape', () => {
251
+ const instance = createInstance();
252
+ const newShape = {
253
+ id: '8',
254
+ height: 140,
255
+ width: 130,
256
+ x: 280,
257
+ y: 1,
258
+ group: 'rectangles',
259
+ };
260
+
261
+ instance.onUpdateShapes([...formattedShapes, newShape]);
262
+
263
+ expect(onUpdateShapes).toHaveBeenLastCalledWith({
264
+ rectangles: [
265
+ ...initialModel.shapes.rectangles,
266
+ {
267
+ id: '8',
268
+ height: 140,
269
+ width: 130,
270
+ x: 280,
271
+ y: 1,
272
+ },
273
+ ],
274
+ polygons: initialModel.shapes.polygons,
275
+ circles: initialModel.shapes.circles,
276
+ });
277
+ });
278
+
279
+ it('onDeleteShape by id', () => {
280
+ const instance = createInstance();
281
+ instance.onDeleteShape('8');
282
+ expect(onUpdateShapes).toHaveBeenCalledWith(
283
+ groupShapes([
284
+ { correct: true, group: 'rectangles', height: 140, id: '0', index: 0, width: 130, x: 1, y: 1 },
285
+ { group: 'rectangles', height: 140, id: '1', index: 1, width: 130, x: 140, y: 1 },
286
+ { group: 'rectangles', height: 140, id: '2', index: 2, width: 130, x: 280, y: 1 },
287
+ {
288
+ correct: true,
289
+ group: 'polygons',
290
+ id: '3',
291
+ index: 3,
292
+ points: [
293
+ { x: 1, y: 148 },
294
+ { x: 1, y: 288 },
295
+ { x: 129, y: 288 },
296
+ { x: 129, y: 148 },
297
+ ],
298
+ },
299
+ {
300
+ correct: false,
301
+ group: 'polygons',
302
+ id: '4',
303
+ index: 4,
304
+ points: [
305
+ { x: 141, y: 151 },
306
+ { x: 141, y: 289 },
307
+ { x: 269, y: 289 },
308
+ { x: 269, y: 151 },
309
+ ],
310
+ },
311
+ {
312
+ correct: false,
313
+ group: 'polygons',
314
+ id: '5',
315
+ index: 5,
316
+ points: [
317
+ { x: 279, y: 150 },
318
+ { x: 279, y: 289 },
319
+ { x: 407, y: 289 },
320
+ { x: 407, y: 150 },
321
+ ],
322
+ },
323
+ {
324
+ id: '6',
325
+ radius: 70,
326
+ x: 100,
327
+ y: 100,
328
+ correct: false,
329
+ group: 'circles',
330
+ index: 6,
331
+ },
332
+ {
333
+ id: '7',
334
+ radius: 30,
335
+ x: 200,
336
+ y: 150,
337
+ correct: true,
338
+ group: 'circles',
339
+ index: 7,
340
+ },
341
+ ]),
342
+ );
343
+ });
344
+
345
+ it('onUpdateShapes with no shapes', () => {
346
+ const instance = createInstance();
347
+ instance.onUpdateShapes([]);
348
+
349
+ expect(onUpdateShapes).toHaveBeenLastCalledWith({
350
+ rectangles: [],
351
+ polygons: [],
352
+ circles: [],
353
+ });
354
+ });
355
+
356
+ it('handleClearAll', () => {
357
+ const instance = createInstance();
358
+ instance.handleClearAll();
359
+ expect(onUpdateShapes).toHaveBeenLastCalledWith({
360
+ rectangles: [],
361
+ polygons: [],
362
+ circles: [],
363
+ });
364
+ });
365
+ });
366
+ });