@wandelbots/wandelbots-js-react-components 2.26.0-pr.feature-update-to-react-19.361.3244daa → 2.26.0-pr.feature-update-to-react-19.361.ec4e097
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/components/robots/RobotAnimator.test.d.ts +2 -0
- package/dist/components/robots/RobotAnimator.test.d.ts.map +1 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/test/setup.d.ts +1 -1
- package/package.json +1 -1
- package/src/components/robots/RobotAnimator.test.tsx +349 -0
- package/src/components/robots/RobotAnimator.tsx +1 -1
- package/src/components/robots/RobotAnimator.unit.test.tsx +49 -36
- package/src/test/setup.ts +14 -12
package/dist/test/setup.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import
|
|
1
|
+
import "@testing-library/jest-dom";
|
|
2
2
|
//# sourceMappingURL=setup.d.ts.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wandelbots/wandelbots-js-react-components",
|
|
3
|
-
"version": "2.26.0-pr.feature-update-to-react-19.361.
|
|
3
|
+
"version": "2.26.0-pr.feature-update-to-react-19.361.ec4e097",
|
|
4
4
|
"description": "React UI toolkit for building applications on top of the Wandelbots platform",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -0,0 +1,349 @@
|
|
|
1
|
+
import { Canvas } from "@react-three/fiber"
|
|
2
|
+
import { act, render, waitFor } from "@testing-library/react"
|
|
3
|
+
import type {
|
|
4
|
+
DHParameter,
|
|
5
|
+
MotionGroupStateResponse,
|
|
6
|
+
} from "@wandelbots/nova-api/v1"
|
|
7
|
+
import React from "react"
|
|
8
|
+
import { Object3D } from "three"
|
|
9
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"
|
|
10
|
+
import RobotAnimator from "./RobotAnimator"
|
|
11
|
+
|
|
12
|
+
// Mock the dependencies
|
|
13
|
+
vi.mock("../utils/hooks", () => ({
|
|
14
|
+
useAutorun: vi.fn(),
|
|
15
|
+
}))
|
|
16
|
+
|
|
17
|
+
vi.mock("./robotModelLogic", () => ({
|
|
18
|
+
collectJoints: vi.fn(),
|
|
19
|
+
}))
|
|
20
|
+
|
|
21
|
+
vi.mock("@react-spring/three", () => ({
|
|
22
|
+
Globals: {
|
|
23
|
+
assign: vi.fn(),
|
|
24
|
+
},
|
|
25
|
+
useSpring: vi.fn(),
|
|
26
|
+
}))
|
|
27
|
+
|
|
28
|
+
vi.mock("@react-three/fiber", async () => {
|
|
29
|
+
const actual = await vi.importActual("@react-three/fiber")
|
|
30
|
+
return {
|
|
31
|
+
...actual,
|
|
32
|
+
useThree: () => ({
|
|
33
|
+
invalidate: vi.fn(),
|
|
34
|
+
}),
|
|
35
|
+
}
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
import { useSpring } from "@react-spring/three"
|
|
39
|
+
import { useAutorun } from "../utils/hooks"
|
|
40
|
+
import { collectJoints } from "./robotModelLogic"
|
|
41
|
+
|
|
42
|
+
const mockUseAutorun = useAutorun as any
|
|
43
|
+
const mockCollectJoints = collectJoints as any
|
|
44
|
+
const mockUseSpring = useSpring as any
|
|
45
|
+
|
|
46
|
+
describe("RobotAnimator", () => {
|
|
47
|
+
const mockMotionState: MotionGroupStateResponse = {
|
|
48
|
+
state: {
|
|
49
|
+
joint_position: {
|
|
50
|
+
joints: [0, 0.5, 1, 1.5, 2, 2.5],
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
} as any
|
|
54
|
+
|
|
55
|
+
const mockDhParameters: DHParameter[] = [
|
|
56
|
+
{ theta: 0, reverse_rotation_direction: false },
|
|
57
|
+
{ theta: 0.1, reverse_rotation_direction: true },
|
|
58
|
+
{ theta: 0.2, reverse_rotation_direction: false },
|
|
59
|
+
{ theta: 0.3, reverse_rotation_direction: true },
|
|
60
|
+
{ theta: 0.4, reverse_rotation_direction: false },
|
|
61
|
+
{ theta: 0.5, reverse_rotation_direction: true },
|
|
62
|
+
]
|
|
63
|
+
|
|
64
|
+
let mockJointObjects: Object3D[]
|
|
65
|
+
let mockAxisValues: any
|
|
66
|
+
let mockSetSpring: any
|
|
67
|
+
let mockInvalidate: any
|
|
68
|
+
let mockSpringConfig: any
|
|
69
|
+
let mockAutorunCallback: any
|
|
70
|
+
|
|
71
|
+
beforeEach(() => {
|
|
72
|
+
// Create mock joint objects
|
|
73
|
+
mockJointObjects = Array.from({ length: 6 }, (_, i) => {
|
|
74
|
+
const obj = new Object3D()
|
|
75
|
+
obj.name = `Joint_J${i + 1}`
|
|
76
|
+
obj.rotation.y = 0
|
|
77
|
+
return obj
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
// Mock axis values object that acts like spring values
|
|
81
|
+
mockAxisValues = {}
|
|
82
|
+
for (let i = 0; i < 6; i++) {
|
|
83
|
+
mockAxisValues[i] = {
|
|
84
|
+
get: vi
|
|
85
|
+
.fn()
|
|
86
|
+
.mockReturnValue(mockMotionState.state.joint_position.joints[i]),
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
mockSetSpring = {
|
|
91
|
+
start: vi.fn(),
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
mockInvalidate = vi.fn()
|
|
95
|
+
|
|
96
|
+
// Mock spring config that will be returned by useSpring
|
|
97
|
+
mockSpringConfig = {
|
|
98
|
+
onChange: vi.fn(),
|
|
99
|
+
onResolve: vi.fn(),
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Mock collectJoints to return our mock joint objects
|
|
103
|
+
mockCollectJoints.mockReturnValue(mockJointObjects)
|
|
104
|
+
|
|
105
|
+
// Mock useSpring to return our mock values and accept a factory function
|
|
106
|
+
mockUseSpring.mockImplementation((factory: any) => {
|
|
107
|
+
const config = factory()
|
|
108
|
+
mockSpringConfig.onChange = config.onChange
|
|
109
|
+
mockSpringConfig.onResolve = config.onResolve
|
|
110
|
+
return [mockAxisValues, mockSetSpring]
|
|
111
|
+
})
|
|
112
|
+
|
|
113
|
+
// Mock useAutorun to store the callback
|
|
114
|
+
mockUseAutorun.mockImplementation((callback: any) => {
|
|
115
|
+
mockAutorunCallback = callback
|
|
116
|
+
})
|
|
117
|
+
})
|
|
118
|
+
|
|
119
|
+
afterEach(() => {
|
|
120
|
+
vi.clearAllMocks()
|
|
121
|
+
})
|
|
122
|
+
|
|
123
|
+
const renderRobotAnimator = (
|
|
124
|
+
props: Partial<React.ComponentProps<typeof RobotAnimator>> = {},
|
|
125
|
+
) => {
|
|
126
|
+
const defaultProps = {
|
|
127
|
+
rapidlyChangingMotionState: mockMotionState,
|
|
128
|
+
dhParameters: mockDhParameters,
|
|
129
|
+
children: <mesh />,
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
return render(
|
|
133
|
+
<Canvas>
|
|
134
|
+
<RobotAnimator {...defaultProps} {...props} />
|
|
135
|
+
</Canvas>,
|
|
136
|
+
)
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
it("should render without crashing", () => {
|
|
140
|
+
expect(() => renderRobotAnimator()).not.toThrow()
|
|
141
|
+
})
|
|
142
|
+
|
|
143
|
+
it("should initialize joint objects when group is set", async () => {
|
|
144
|
+
const { container } = renderRobotAnimator()
|
|
145
|
+
|
|
146
|
+
// Wait for the component to render and set up
|
|
147
|
+
await waitFor(() => {
|
|
148
|
+
expect(mockCollectJoints).toHaveBeenCalled()
|
|
149
|
+
})
|
|
150
|
+
})
|
|
151
|
+
|
|
152
|
+
it("should call onRotationChanged when provided", async () => {
|
|
153
|
+
const mockOnRotationChanged = vi.fn()
|
|
154
|
+
|
|
155
|
+
renderRobotAnimator({ onRotationChanged: mockOnRotationChanged })
|
|
156
|
+
|
|
157
|
+
// Wait for initialization
|
|
158
|
+
await waitFor(() => {
|
|
159
|
+
expect(mockCollectJoints).toHaveBeenCalled()
|
|
160
|
+
})
|
|
161
|
+
|
|
162
|
+
// Simulate spring onChange callback
|
|
163
|
+
await act(async () => {
|
|
164
|
+
if (mockSpringConfig.onChange) {
|
|
165
|
+
mockSpringConfig.onChange()
|
|
166
|
+
}
|
|
167
|
+
})
|
|
168
|
+
|
|
169
|
+
expect(mockOnRotationChanged).toHaveBeenCalledWith(
|
|
170
|
+
mockJointObjects,
|
|
171
|
+
expect.arrayContaining([0, 0.5, 1, 1.5, 2, 2.5]),
|
|
172
|
+
)
|
|
173
|
+
})
|
|
174
|
+
|
|
175
|
+
it("should update joint rotations when onRotationChanged is not provided", async () => {
|
|
176
|
+
renderRobotAnimator()
|
|
177
|
+
|
|
178
|
+
// Wait for initialization
|
|
179
|
+
await waitFor(() => {
|
|
180
|
+
expect(mockCollectJoints).toHaveBeenCalled()
|
|
181
|
+
})
|
|
182
|
+
|
|
183
|
+
await act(async () => {
|
|
184
|
+
// Simulate spring animation callback
|
|
185
|
+
if (mockSpringConfig.onChange) {
|
|
186
|
+
mockSpringConfig.onChange()
|
|
187
|
+
}
|
|
188
|
+
})
|
|
189
|
+
|
|
190
|
+
// Check that joint rotations were updated according to DH parameters
|
|
191
|
+
mockJointObjects.forEach((joint, index) => {
|
|
192
|
+
const dhParam = mockDhParameters[index]
|
|
193
|
+
const rotationSign = dhParam.reverse_rotation_direction ? -1 : 1
|
|
194
|
+
const expectedRotation =
|
|
195
|
+
rotationSign * mockMotionState.state.joint_position.joints[index]! +
|
|
196
|
+
dhParam.theta!
|
|
197
|
+
|
|
198
|
+
expect(joint.rotation.y).toBe(expectedRotation)
|
|
199
|
+
})
|
|
200
|
+
})
|
|
201
|
+
|
|
202
|
+
it("should not update rotations when not initialized", async () => {
|
|
203
|
+
// Mock collectJoints to return empty array (not initialized)
|
|
204
|
+
mockCollectJoints.mockReturnValue([])
|
|
205
|
+
|
|
206
|
+
renderRobotAnimator()
|
|
207
|
+
|
|
208
|
+
await act(async () => {
|
|
209
|
+
// Simulate spring animation callback
|
|
210
|
+
if (mockSpringConfig.onChange) {
|
|
211
|
+
mockSpringConfig.onChange()
|
|
212
|
+
}
|
|
213
|
+
})
|
|
214
|
+
|
|
215
|
+
// Verify that joint rotations were not modified
|
|
216
|
+
mockJointObjects.forEach((joint) => {
|
|
217
|
+
expect(joint.rotation.y).toBe(0)
|
|
218
|
+
})
|
|
219
|
+
})
|
|
220
|
+
|
|
221
|
+
it("should handle spring updates correctly", async () => {
|
|
222
|
+
renderRobotAnimator()
|
|
223
|
+
|
|
224
|
+
const newValues = [1, 2, 3, 4, 5, 6]
|
|
225
|
+
|
|
226
|
+
// Wait for initialization
|
|
227
|
+
await waitFor(() => {
|
|
228
|
+
expect(mockCollectJoints).toHaveBeenCalled()
|
|
229
|
+
})
|
|
230
|
+
|
|
231
|
+
await act(async () => {
|
|
232
|
+
// Simulate motion state update by calling the autorun callback
|
|
233
|
+
if (mockAutorunCallback) {
|
|
234
|
+
mockAutorunCallback()
|
|
235
|
+
}
|
|
236
|
+
})
|
|
237
|
+
|
|
238
|
+
expect(mockSetSpring.start).toHaveBeenCalledWith(
|
|
239
|
+
expect.objectContaining({
|
|
240
|
+
0: 0,
|
|
241
|
+
1: 0.5,
|
|
242
|
+
2: 1,
|
|
243
|
+
3: 1.5,
|
|
244
|
+
4: 2,
|
|
245
|
+
5: 2.5,
|
|
246
|
+
}),
|
|
247
|
+
)
|
|
248
|
+
})
|
|
249
|
+
|
|
250
|
+
it("should handle group reference being null", async () => {
|
|
251
|
+
renderRobotAnimator()
|
|
252
|
+
|
|
253
|
+
// This should not throw an error
|
|
254
|
+
await act(async () => {
|
|
255
|
+
// Component should handle null group reference gracefully
|
|
256
|
+
})
|
|
257
|
+
|
|
258
|
+
expect(mockCollectJoints).toHaveBeenCalled()
|
|
259
|
+
})
|
|
260
|
+
|
|
261
|
+
it("should call invalidate when updating rotations", async () => {
|
|
262
|
+
renderRobotAnimator()
|
|
263
|
+
|
|
264
|
+
// Wait for initialization
|
|
265
|
+
await waitFor(() => {
|
|
266
|
+
expect(mockCollectJoints).toHaveBeenCalled()
|
|
267
|
+
})
|
|
268
|
+
|
|
269
|
+
await act(async () => {
|
|
270
|
+
// Simulate spring animation callback
|
|
271
|
+
if (mockSpringConfig.onChange) {
|
|
272
|
+
mockSpringConfig.onChange()
|
|
273
|
+
}
|
|
274
|
+
})
|
|
275
|
+
|
|
276
|
+
// Note: invalidate is called from useThree hook which is mocked
|
|
277
|
+
// The actual invalidate call would happen in the real component
|
|
278
|
+
expect(mockSpringConfig.onChange).toHaveBeenCalled()
|
|
279
|
+
})
|
|
280
|
+
|
|
281
|
+
it("should handle onResolve callback", async () => {
|
|
282
|
+
const mockOnRotationChanged = vi.fn()
|
|
283
|
+
renderRobotAnimator({ onRotationChanged: mockOnRotationChanged })
|
|
284
|
+
|
|
285
|
+
// Wait for initialization
|
|
286
|
+
await waitFor(() => {
|
|
287
|
+
expect(mockCollectJoints).toHaveBeenCalled()
|
|
288
|
+
})
|
|
289
|
+
|
|
290
|
+
await act(async () => {
|
|
291
|
+
// Simulate spring onResolve callback
|
|
292
|
+
if (mockSpringConfig.onResolve) {
|
|
293
|
+
mockSpringConfig.onResolve()
|
|
294
|
+
}
|
|
295
|
+
})
|
|
296
|
+
|
|
297
|
+
expect(mockOnRotationChanged).toHaveBeenCalled()
|
|
298
|
+
})
|
|
299
|
+
|
|
300
|
+
it("should filter out undefined joint values", async () => {
|
|
301
|
+
const motionStateWithUndefined = {
|
|
302
|
+
state: {
|
|
303
|
+
joint_position: {
|
|
304
|
+
joints: [1, undefined, 3, undefined, 5, 6],
|
|
305
|
+
},
|
|
306
|
+
},
|
|
307
|
+
} as any
|
|
308
|
+
|
|
309
|
+
renderRobotAnimator({
|
|
310
|
+
rapidlyChangingMotionState: motionStateWithUndefined,
|
|
311
|
+
})
|
|
312
|
+
|
|
313
|
+
// Wait for initialization
|
|
314
|
+
await waitFor(() => {
|
|
315
|
+
expect(mockCollectJoints).toHaveBeenCalled()
|
|
316
|
+
})
|
|
317
|
+
|
|
318
|
+
await act(async () => {
|
|
319
|
+
if (mockAutorunCallback) {
|
|
320
|
+
mockAutorunCallback()
|
|
321
|
+
}
|
|
322
|
+
})
|
|
323
|
+
|
|
324
|
+
// Should call setSpring.start with filtered values
|
|
325
|
+
expect(mockSetSpring.start).toHaveBeenCalledWith(
|
|
326
|
+
expect.objectContaining({
|
|
327
|
+
0: 1,
|
|
328
|
+
1: 3,
|
|
329
|
+
2: 5,
|
|
330
|
+
3: 6,
|
|
331
|
+
}),
|
|
332
|
+
)
|
|
333
|
+
})
|
|
334
|
+
|
|
335
|
+
it("should handle empty joint objects gracefully", async () => {
|
|
336
|
+
mockCollectJoints.mockReturnValue([])
|
|
337
|
+
|
|
338
|
+
const { container } = renderRobotAnimator()
|
|
339
|
+
|
|
340
|
+
await act(async () => {
|
|
341
|
+
if (mockAutorunCallback) {
|
|
342
|
+
mockAutorunCallback()
|
|
343
|
+
}
|
|
344
|
+
})
|
|
345
|
+
|
|
346
|
+
// Should not throw and should not call setSpring.start
|
|
347
|
+
expect(mockSetSpring.start).not.toHaveBeenCalled()
|
|
348
|
+
})
|
|
349
|
+
})
|
|
@@ -4,7 +4,7 @@ import type {
|
|
|
4
4
|
DHParameter,
|
|
5
5
|
MotionGroupStateResponse,
|
|
6
6
|
} from "@wandelbots/nova-api/v1"
|
|
7
|
-
import React, { useEffect, useRef, useState
|
|
7
|
+
import React, { useCallback, useEffect, useRef, useState } from "react"
|
|
8
8
|
import type { Group, Object3D } from "three"
|
|
9
9
|
import { useAutorun } from "../utils/hooks"
|
|
10
10
|
import { collectJoints } from "./robotModelLogic"
|
|
@@ -1,6 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type {
|
|
2
|
+
DHParameter,
|
|
3
|
+
MotionGroupStateResponse,
|
|
4
|
+
} from "@wandelbots/nova-api/v1"
|
|
2
5
|
import { Object3D } from "three"
|
|
3
|
-
import
|
|
6
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"
|
|
4
7
|
|
|
5
8
|
// Import the component for testing
|
|
6
9
|
import RobotAnimator from "./RobotAnimator"
|
|
@@ -27,9 +30,9 @@ vi.mock("@react-three/fiber", () => ({
|
|
|
27
30
|
}),
|
|
28
31
|
}))
|
|
29
32
|
|
|
33
|
+
import { useSpring } from "@react-spring/three"
|
|
30
34
|
import { useAutorun } from "../utils/hooks"
|
|
31
35
|
import { collectJoints } from "./robotModelLogic"
|
|
32
|
-
import { useSpring } from "@react-spring/three"
|
|
33
36
|
|
|
34
37
|
const mockUseAutorun = useAutorun as any
|
|
35
38
|
const mockCollectJoints = collectJoints as any
|
|
@@ -72,7 +75,9 @@ describe("RobotAnimator Logic Tests", () => {
|
|
|
72
75
|
mockAxisValues = {}
|
|
73
76
|
for (let i = 0; i < 6; i++) {
|
|
74
77
|
mockAxisValues[i] = {
|
|
75
|
-
get: vi
|
|
78
|
+
get: vi
|
|
79
|
+
.fn()
|
|
80
|
+
.mockReturnValue(mockMotionState.state.joint_position.joints[i]),
|
|
76
81
|
}
|
|
77
82
|
}
|
|
78
83
|
|
|
@@ -123,7 +128,7 @@ describe("RobotAnimator Logic Tests", () => {
|
|
|
123
128
|
const dhParam = dhParameters[index]
|
|
124
129
|
const rotationOffset = dhParam.theta || 0
|
|
125
130
|
const rotationSign = dhParam.reverse_rotation_direction ? -1 : 1
|
|
126
|
-
|
|
131
|
+
|
|
127
132
|
joint.rotation.y = rotationSign * jointValues[index]! + rotationOffset
|
|
128
133
|
})
|
|
129
134
|
|
|
@@ -140,7 +145,7 @@ describe("RobotAnimator Logic Tests", () => {
|
|
|
140
145
|
// Test the filtering logic that removes undefined values
|
|
141
146
|
const inputJoints = [1, undefined, 3, undefined, 5, 6]
|
|
142
147
|
const filteredJoints = inputJoints.filter((item) => item !== undefined)
|
|
143
|
-
|
|
148
|
+
|
|
144
149
|
expect(filteredJoints).toEqual([1, 3, 5, 6])
|
|
145
150
|
expect(filteredJoints.length).toBe(4)
|
|
146
151
|
})
|
|
@@ -148,25 +153,25 @@ describe("RobotAnimator Logic Tests", () => {
|
|
|
148
153
|
it("should test DH parameter application", () => {
|
|
149
154
|
// Test various DH parameter combinations
|
|
150
155
|
const testCases = [
|
|
151
|
-
{
|
|
152
|
-
jointValue: 1.0,
|
|
156
|
+
{
|
|
157
|
+
jointValue: 1.0,
|
|
153
158
|
dhParam: { theta: 0, reverse_rotation_direction: false },
|
|
154
|
-
expected: 1.0
|
|
159
|
+
expected: 1.0,
|
|
155
160
|
},
|
|
156
|
-
{
|
|
157
|
-
jointValue: 1.0,
|
|
161
|
+
{
|
|
162
|
+
jointValue: 1.0,
|
|
158
163
|
dhParam: { theta: 0.5, reverse_rotation_direction: false },
|
|
159
|
-
expected: 1.5
|
|
164
|
+
expected: 1.5,
|
|
160
165
|
},
|
|
161
|
-
{
|
|
162
|
-
jointValue: 1.0,
|
|
166
|
+
{
|
|
167
|
+
jointValue: 1.0,
|
|
163
168
|
dhParam: { theta: 0, reverse_rotation_direction: true },
|
|
164
|
-
expected: -1.0
|
|
169
|
+
expected: -1.0,
|
|
165
170
|
},
|
|
166
|
-
{
|
|
167
|
-
jointValue: 1.0,
|
|
171
|
+
{
|
|
172
|
+
jointValue: 1.0,
|
|
168
173
|
dhParam: { theta: 0.5, reverse_rotation_direction: true },
|
|
169
|
-
expected: -0.5
|
|
174
|
+
expected: -0.5,
|
|
170
175
|
},
|
|
171
176
|
]
|
|
172
177
|
|
|
@@ -174,9 +179,9 @@ describe("RobotAnimator Logic Tests", () => {
|
|
|
174
179
|
const joint = new Object3D()
|
|
175
180
|
const rotationOffset = dhParam.theta || 0
|
|
176
181
|
const rotationSign = dhParam.reverse_rotation_direction ? -1 : 1
|
|
177
|
-
|
|
182
|
+
|
|
178
183
|
joint.rotation.y = rotationSign * jointValue + rotationOffset
|
|
179
|
-
|
|
184
|
+
|
|
180
185
|
expect(joint.rotation.y).toBeCloseTo(expected, 5)
|
|
181
186
|
})
|
|
182
187
|
})
|
|
@@ -203,10 +208,11 @@ describe("RobotAnimator Logic Tests", () => {
|
|
|
203
208
|
it("should test empty joint array handling", () => {
|
|
204
209
|
// Test behavior when no joints are available
|
|
205
210
|
const emptyJoints: Object3D[] = []
|
|
206
|
-
|
|
211
|
+
|
|
207
212
|
// This simulates the isInitialized check in the component
|
|
208
|
-
const isInitialized =
|
|
209
|
-
|
|
213
|
+
const isInitialized =
|
|
214
|
+
emptyJoints.length > 0 && emptyJoints.every((obj) => obj != null)
|
|
215
|
+
|
|
210
216
|
expect(isInitialized).toBe(false)
|
|
211
217
|
})
|
|
212
218
|
|
|
@@ -214,10 +220,12 @@ describe("RobotAnimator Logic Tests", () => {
|
|
|
214
220
|
// Test the joint object validation logic
|
|
215
221
|
const validJoints = [new Object3D(), new Object3D(), new Object3D()]
|
|
216
222
|
const invalidJoints = [new Object3D(), null, new Object3D()]
|
|
217
|
-
|
|
218
|
-
const isValidJointsInitialized =
|
|
219
|
-
|
|
220
|
-
|
|
223
|
+
|
|
224
|
+
const isValidJointsInitialized =
|
|
225
|
+
validJoints.length > 0 && validJoints.every((obj) => obj != null)
|
|
226
|
+
const isInvalidJointsInitialized =
|
|
227
|
+
invalidJoints.length > 0 && invalidJoints.every((obj) => obj != null)
|
|
228
|
+
|
|
221
229
|
expect(isValidJointsInitialized).toBe(true)
|
|
222
230
|
expect(isInvalidJointsInitialized).toBe(false)
|
|
223
231
|
})
|
|
@@ -227,7 +235,9 @@ describe("RobotAnimator Logic Tests", () => {
|
|
|
227
235
|
expect(mockMotionState.state).toBeDefined()
|
|
228
236
|
expect(mockMotionState.state.joint_position).toBeDefined()
|
|
229
237
|
expect(mockMotionState.state.joint_position.joints).toBeDefined()
|
|
230
|
-
expect(Array.isArray(mockMotionState.state.joint_position.joints)).toBe(
|
|
238
|
+
expect(Array.isArray(mockMotionState.state.joint_position.joints)).toBe(
|
|
239
|
+
true,
|
|
240
|
+
)
|
|
231
241
|
expect(mockMotionState.state.joint_position.joints.length).toBe(6)
|
|
232
242
|
})
|
|
233
243
|
|
|
@@ -245,10 +255,10 @@ describe("RobotAnimator Logic Tests", () => {
|
|
|
245
255
|
const mockCallback = vi.fn()
|
|
246
256
|
const testJoints = [new Object3D(), new Object3D()]
|
|
247
257
|
const testValues = [1.0, 2.0]
|
|
248
|
-
|
|
258
|
+
|
|
249
259
|
// Simulate calling the callback
|
|
250
260
|
mockCallback(testJoints, testValues)
|
|
251
|
-
|
|
261
|
+
|
|
252
262
|
expect(mockCallback).toHaveBeenCalledWith(testJoints, testValues)
|
|
253
263
|
expect(mockCallback).toHaveBeenCalledTimes(1)
|
|
254
264
|
})
|
|
@@ -257,29 +267,32 @@ describe("RobotAnimator Logic Tests", () => {
|
|
|
257
267
|
// Test the initialization state logic that prevents race conditions
|
|
258
268
|
let isInitialized = false
|
|
259
269
|
const joints: Object3D[] = []
|
|
260
|
-
|
|
270
|
+
|
|
261
271
|
// Simulate setGroupRef logic
|
|
262
272
|
function simulateSetGroupRef(group: any) {
|
|
263
273
|
if (!group) {
|
|
264
274
|
isInitialized = false
|
|
265
275
|
return
|
|
266
276
|
}
|
|
267
|
-
|
|
277
|
+
|
|
268
278
|
// Simulate collectJoints
|
|
269
279
|
const collectedJoints = group ? [new Object3D(), new Object3D()] : []
|
|
270
|
-
|
|
280
|
+
|
|
271
281
|
// Only mark as initialized if we have valid joint objects
|
|
272
|
-
if (
|
|
282
|
+
if (
|
|
283
|
+
collectedJoints.length > 0 &&
|
|
284
|
+
collectedJoints.every((obj) => obj != null)
|
|
285
|
+
) {
|
|
273
286
|
isInitialized = true
|
|
274
287
|
} else {
|
|
275
288
|
isInitialized = false
|
|
276
289
|
}
|
|
277
290
|
}
|
|
278
|
-
|
|
291
|
+
|
|
279
292
|
// Test null group
|
|
280
293
|
simulateSetGroupRef(null)
|
|
281
294
|
expect(isInitialized).toBe(false)
|
|
282
|
-
|
|
295
|
+
|
|
283
296
|
// Test valid group
|
|
284
297
|
simulateSetGroupRef({ type: "Group" })
|
|
285
298
|
expect(isInitialized).toBe(true)
|
package/src/test/setup.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { beforeAll, vi } from
|
|
1
|
+
import "@testing-library/jest-dom"
|
|
2
|
+
import { beforeAll, vi } from "vitest"
|
|
3
3
|
|
|
4
4
|
// Mock ResizeObserver
|
|
5
5
|
global.ResizeObserver = vi.fn().mockImplementation(() => ({
|
|
@@ -16,9 +16,9 @@ global.IntersectionObserver = vi.fn().mockImplementation(() => ({
|
|
|
16
16
|
}))
|
|
17
17
|
|
|
18
18
|
// Mock matchMedia
|
|
19
|
-
Object.defineProperty(window,
|
|
19
|
+
Object.defineProperty(window, "matchMedia", {
|
|
20
20
|
writable: true,
|
|
21
|
-
value: vi.fn().mockImplementation(query => ({
|
|
21
|
+
value: vi.fn().mockImplementation((query) => ({
|
|
22
22
|
matches: false,
|
|
23
23
|
media: query,
|
|
24
24
|
onchange: null,
|
|
@@ -95,15 +95,17 @@ const mockWebGLContext = {
|
|
|
95
95
|
TRUE: 1,
|
|
96
96
|
}
|
|
97
97
|
|
|
98
|
-
HTMLCanvasElement.prototype.getContext = vi
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
}
|
|
98
|
+
HTMLCanvasElement.prototype.getContext = vi
|
|
99
|
+
.fn()
|
|
100
|
+
.mockImplementation((contextType) => {
|
|
101
|
+
if (contextType === "webgl" || contextType === "experimental-webgl") {
|
|
102
|
+
return mockWebGLContext
|
|
103
|
+
}
|
|
104
|
+
return null
|
|
105
|
+
})
|
|
104
106
|
|
|
105
107
|
beforeAll(() => {
|
|
106
108
|
// Suppress console errors in tests
|
|
107
|
-
vi.spyOn(console,
|
|
108
|
-
vi.spyOn(console,
|
|
109
|
+
vi.spyOn(console, "error").mockImplementation(() => {})
|
|
110
|
+
vi.spyOn(console, "warn").mockImplementation(() => {})
|
|
109
111
|
})
|