@shopware-ag/dive 1.16.7 → 1.16.9

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shopware-ag/dive",
3
- "version": "1.16.7",
3
+ "version": "1.16.9",
4
4
  "description": "Shopware Spatial Framework",
5
5
  "type": "module",
6
6
  "main": "./build/dive.cjs",
@@ -1,30 +1,7 @@
1
- import { Vector3Like } from 'three';
2
- import {
3
- type COMLight,
4
- type COMModel,
5
- type COMPov,
6
- type COMPrimitive,
7
- } from '../../types';
8
-
9
- type SceneData = {
10
- name: string;
11
- mediaItem: null;
12
- backgroundColor: string;
13
- floorEnabled: boolean;
14
- floorColor: string;
15
- userCamera: {
16
- position: Vector3Like;
17
- target: Vector3Like;
18
- };
19
- spotmarks: object[];
20
- lights: COMLight[];
21
- objects: COMModel[];
22
- cameras: COMPov[];
23
- primitives: COMPrimitive[];
24
- };
1
+ import { DIVESceneData } from '../../../types';
25
2
 
26
3
  export default interface GET_ALL_SCENE_DATA {
27
4
  DESCRIPTION: 'Retrieves all current scene data.';
28
5
  PAYLOAD: object;
29
- RETURN: SceneData;
6
+ RETURN: DIVESceneData;
30
7
  }
@@ -7,4 +7,5 @@ export type COMLight = COMBaseEntity & {
7
7
  color: string | number;
8
8
  enabled: boolean;
9
9
  position?: Vector3Like;
10
+ rotation?: Vector3Like;
10
11
  };
package/src/dive.ts CHANGED
@@ -329,3 +329,4 @@ export * from './math/index.ts';
329
329
 
330
330
  export type * from './com/actions/index.ts';
331
331
  export type * from './com/types';
332
+ export type * from './types';
@@ -1,4 +1,10 @@
1
- import { BufferGeometry, Line, LineDashedMaterial, Vector3 } from 'three';
1
+ import {
2
+ BufferGeometry,
3
+ Line,
4
+ LineDashedMaterial,
5
+ Vector3,
6
+ Vector3Like,
7
+ } from 'three';
2
8
  import { DIVENode } from '../node/Node';
3
9
  import { type Object3D } from 'three';
4
10
  import { type DIVESceneObject } from '../types';
@@ -20,6 +26,15 @@ export class DIVEGroup extends DIVENode {
20
26
  this._lines = [];
21
27
  }
22
28
 
29
+ public SetPosition(position: Vector3Like): void {
30
+ super.SetPosition(position);
31
+ this._members.forEach((member) => {
32
+ if ('isDIVENode' in member) {
33
+ (member as DIVENode).onMove();
34
+ }
35
+ });
36
+ }
37
+
23
38
  public SetLinesVisibility(visible: boolean, object?: Object3D): void {
24
39
  if (!object) {
25
40
  this._lines.forEach((line) => {
@@ -1,4 +1,6 @@
1
+ import { type Vector3Like } from 'three';
1
2
  import { DIVECommunication } from '../../com/Communication';
3
+ import { type DIVENode } from '../../node/Node';
2
4
  import { DIVEGroup } from '../Group';
3
5
 
4
6
  jest.mock('../../com/Communication.ts', () => {
@@ -107,4 +109,99 @@ describe('dive/group/DIVEGroup', () => {
107
109
  jest.spyOn(DIVECommunication, 'get').mockReturnValueOnce(undefined);
108
110
  expect(() => group.onMove()).not.toThrow();
109
111
  });
112
+
113
+ it('should call onMove on members with isDIVENode', () => {
114
+ // Create mock members
115
+ const diveNode1: DIVENode = {
116
+ isDIVENode: true,
117
+ onMove: jest.fn(),
118
+ } as unknown as DIVENode;
119
+
120
+ const diveNode2: DIVENode = {
121
+ isDIVENode: true,
122
+ onMove: jest.fn(),
123
+ } as unknown as DIVENode;
124
+
125
+ const member1: DIVENode = {
126
+ // Define other properties/methods if necessary
127
+ } as unknown as DIVENode;
128
+
129
+ const member2: DIVENode = {
130
+ // Define other properties/methods if necessary
131
+ } as unknown as DIVENode;
132
+
133
+ // Assign the _members array (assuming it's protected or public for testing)
134
+ // If _members is private, you might need to use a different approach or modify the class for testability
135
+ (group as any)._members = [
136
+ diveNode1,
137
+ member1,
138
+ diveNode2,
139
+ member2,
140
+ ];
141
+
142
+ const position: Vector3Like = { x: 4, y: 5, z: 6 };
143
+ group.SetPosition(position);
144
+
145
+ // Check that onMove was called on diveNode1 and diveNode2
146
+ expect(diveNode1.onMove).toHaveBeenCalled();
147
+ expect(diveNode2.onMove).toHaveBeenCalled();
148
+
149
+ // Ensure onMove was not called on other members
150
+ // Since member1 and member2 don't have onMove, there's nothing to assert here
151
+ // If members have onMove, you should mock and verify they are not called
152
+ });
153
+
154
+ it('should not call onMove on members without isDIVENode', () => {
155
+ // Create mock members without isDIVENode
156
+ const member1: DIVENode = {
157
+ // Define other properties/methods if necessary
158
+ } as unknown as DIVENode;
159
+
160
+ const member2: DIVENode = {
161
+ // Define other properties/methods if necessary
162
+ } as unknown as DIVENode;
163
+
164
+ // Assign the _members array
165
+ (group as any)._members = [
166
+ member1,
167
+ member2,
168
+ ];
169
+
170
+ const position: Vector3Like = { x: 7, y: 8, z: 9 };
171
+ group.SetPosition(position);
172
+
173
+ // Since members do not have onMove, there's nothing to assert
174
+ // If members have onMove as optional, you can spy on them to ensure they're not called
175
+ });
176
+
177
+ it('should handle an empty _members array without errors', () => {
178
+ // Assign an empty _members array
179
+ (group as any)._members = [];
180
+
181
+ const position: Vector3Like = { x: 10, y: 11, z: 12 };
182
+ expect(() => group.SetPosition(position)).not.toThrow();
183
+ });
184
+
185
+ it('should handle _members with mixed types correctly', () => {
186
+ // Create mixed members
187
+ const diveNode: DIVENode = {
188
+ isDIVENode: true,
189
+ onMove: jest.fn(),
190
+ } as unknown as DIVENode;
191
+
192
+ const member: DIVENode = {
193
+ // Define other properties/methods if necessary
194
+ } as unknown as DIVENode;
195
+
196
+ (group as any)._members = [
197
+ diveNode,
198
+ member,
199
+ ];
200
+
201
+ const position: Vector3Like = { x: 13, y: 14, z: 15 };
202
+ group.SetPosition(position);
203
+
204
+ // Ensure onMove is called only on diveNode
205
+ expect(diveNode.onMove).toHaveBeenCalled();
206
+ });
110
207
  });
@@ -0,0 +1,179 @@
1
+ import degToRad from '../degToRad';
2
+ import { MathUtils } from 'three';
3
+
4
+ // Mock the 'three' module, specifically MathUtils.degToRad
5
+ jest.mock('three', () => ({
6
+ MathUtils: {
7
+ degToRad: jest.fn(),
8
+ },
9
+ }));
10
+
11
+ // Type assertion for the mocked MathUtils.degToRad
12
+ const mockedDegToRad = MathUtils.degToRad as jest.Mock;
13
+
14
+ /**
15
+ * Test Suite for degToRad Function
16
+ */
17
+ describe('degToRad', () => {
18
+ beforeEach(() => {
19
+ // Clear all previous mock calls and implementations before each test
20
+ mockedDegToRad.mockClear();
21
+ });
22
+
23
+ it('should convert 0 degrees to 0 radians', () => {
24
+ // Arrange
25
+ mockedDegToRad.mockReturnValue(0);
26
+
27
+ // Act
28
+ const result = degToRad(0);
29
+
30
+ // Assert
31
+ expect(MathUtils.degToRad).toHaveBeenCalledWith(0);
32
+ expect(result).toBe(0);
33
+ });
34
+
35
+ it('should convert 180 degrees to π radians', () => {
36
+ // Arrange
37
+ const degrees = 180;
38
+ const radians = Math.PI;
39
+ mockedDegToRad.mockReturnValue(radians);
40
+
41
+ // Act
42
+ const result = degToRad(degrees);
43
+
44
+ // Assert
45
+ expect(MathUtils.degToRad).toHaveBeenCalledWith(degrees);
46
+ expect(result).toBe(radians);
47
+ });
48
+
49
+ it('should convert 360 degrees to 2π radians', () => {
50
+ // Arrange
51
+ const degrees = 360;
52
+ const radians = 2 * Math.PI;
53
+ mockedDegToRad.mockReturnValue(radians);
54
+
55
+ // Act
56
+ const result = degToRad(degrees);
57
+
58
+ // Assert
59
+ expect(MathUtils.degToRad).toHaveBeenCalledWith(degrees);
60
+ expect(result).toBe(radians);
61
+ });
62
+
63
+ it('should convert 90 degrees to π/2 radians', () => {
64
+ // Arrange
65
+ const degrees = 90;
66
+ const radians = Math.PI / 2;
67
+ mockedDegToRad.mockReturnValue(radians);
68
+
69
+ // Act
70
+ const result = degToRad(degrees);
71
+
72
+ // Assert
73
+ expect(MathUtils.degToRad).toHaveBeenCalledWith(degrees);
74
+ expect(result).toBe(radians);
75
+ });
76
+
77
+ it('should convert 45 degrees to π/4 radians', () => {
78
+ // Arrange
79
+ const degrees = 45;
80
+ const radians = Math.PI / 4;
81
+ mockedDegToRad.mockReturnValue(radians);
82
+
83
+ // Act
84
+ const result = degToRad(degrees);
85
+
86
+ // Assert
87
+ expect(MathUtils.degToRad).toHaveBeenCalledWith(degrees);
88
+ expect(result).toBe(radians);
89
+ });
90
+
91
+ it('should handle multiple calls with different degrees', () => {
92
+ // Arrange
93
+ const degrees1 = 30;
94
+ const radians1 = Math.PI / 6;
95
+ const degrees2 = 60;
96
+ const radians2 = Math.PI / 3;
97
+ const degrees3 = 120;
98
+ const radians3 = (2 * Math.PI) / 3;
99
+
100
+ mockedDegToRad
101
+ .mockReturnValueOnce(radians1)
102
+ .mockReturnValueOnce(radians2)
103
+ .mockReturnValueOnce(radians3);
104
+
105
+ // Act
106
+ const result1 = degToRad(degrees1);
107
+ const result2 = degToRad(degrees2);
108
+ const result3 = degToRad(degrees3);
109
+
110
+ // Assert
111
+ expect(MathUtils.degToRad).toHaveBeenNthCalledWith(1, degrees1);
112
+ expect(MathUtils.degToRad).toHaveBeenNthCalledWith(2, degrees2);
113
+ expect(MathUtils.degToRad).toHaveBeenNthCalledWith(3, degrees3);
114
+
115
+ expect(result1).toBe(radians1);
116
+ expect(result2).toBe(radians2);
117
+ expect(result3).toBe(radians3);
118
+ });
119
+
120
+ it('should handle edge case of degrees just below 360', () => {
121
+ // Arrange
122
+ const degrees = 359.999;
123
+ const radians = MathUtils.degToRad(degrees);
124
+ mockedDegToRad.mockReturnValue(radians);
125
+
126
+ // Act
127
+ const result = degToRad(degrees);
128
+
129
+ // Assert
130
+ expect(MathUtils.degToRad).toHaveBeenCalledWith(degrees);
131
+ expect(result).toBe(radians);
132
+ });
133
+
134
+ it('should not allow negative degrees', () => {
135
+ // Arrange
136
+ const degrees = -45;
137
+ const radians = MathUtils.degToRad(degrees);
138
+ mockedDegToRad.mockReturnValue(radians);
139
+
140
+ // Act
141
+ const result = degToRad(degrees);
142
+
143
+ // Assert
144
+ expect(MathUtils.degToRad).toHaveBeenCalledWith(degrees);
145
+ expect(result).toBe(radians);
146
+ // Depending on implementation, you might want to expect an error
147
+ // or handle negative degrees differently. Adjust assertions accordingly.
148
+ });
149
+
150
+ it('should handle undefined degrees gracefully', () => {
151
+ // Arrange
152
+ const degrees = undefined;
153
+ // Since the function expects a number, this might throw an error or pass undefined
154
+ mockedDegToRad.mockReturnValue(NaN);
155
+
156
+ // Act
157
+ // @ts-ignore: Testing undefined input
158
+ const result = degToRad(degrees);
159
+
160
+ // Assert
161
+ expect(MathUtils.degToRad).toHaveBeenCalledWith(degrees);
162
+ expect(result).toBe(NaN);
163
+ });
164
+
165
+ it('should handle null degrees gracefully', () => {
166
+ // Arrange
167
+ const degrees = null;
168
+ // Depending on implementation, this might throw an error or pass null
169
+ mockedDegToRad.mockReturnValue(NaN);
170
+
171
+ // Act
172
+ // @ts-ignore: Testing null input
173
+ const result = degToRad(degrees);
174
+
175
+ // Assert
176
+ expect(MathUtils.degToRad).toHaveBeenCalledWith(degrees);
177
+ expect(result).toBe(NaN);
178
+ });
179
+ });
@@ -0,0 +1,5 @@
1
+ import { MathUtils } from 'three';
2
+
3
+ export default function degToRad(degrees: number): number {
4
+ return MathUtils.degToRad(degrees);
5
+ }
package/src/math/index.ts CHANGED
@@ -4,6 +4,8 @@ import roundExp from './round/roundExp.ts';
4
4
  import signedAngleTo from './signedAngleTo/signedAngleTo.ts';
5
5
  import toFixedExp from './toFixed/toFixedExp.ts';
6
6
  import truncateExp from './truncate/truncateExp.ts';
7
+ import radToDeg from './radToDeg/radToDeg.ts';
8
+ import degToRad from './degToRad/degToRad.ts';
7
9
 
8
10
  export const DIVEMath: {
9
11
  ceilExp: typeof ceilExp;
@@ -12,6 +14,8 @@ export const DIVEMath: {
12
14
  toFixedExp: typeof toFixedExp;
13
15
  truncateExp: typeof truncateExp;
14
16
  signedAngleTo: typeof signedAngleTo;
17
+ radToDeg: typeof radToDeg;
18
+ degToRad: typeof degToRad;
15
19
  } = {
16
20
  ceilExp,
17
21
  floorExp,
@@ -19,4 +23,6 @@ export const DIVEMath: {
19
23
  toFixedExp,
20
24
  truncateExp,
21
25
  signedAngleTo,
26
+ radToDeg,
27
+ degToRad,
22
28
  };
@@ -0,0 +1,162 @@
1
+ import radToDeg from '../radToDeg';
2
+ import { MathUtils } from 'three';
3
+
4
+ // Mock the 'three' module, specifically MathUtils.radToDeg
5
+ jest.mock('three', () => ({
6
+ MathUtils: {
7
+ radToDeg: jest.fn(),
8
+ },
9
+ }));
10
+
11
+ // Type assertion for the mocked MathUtils.radToDeg
12
+ const mockedRadToDeg = MathUtils.radToDeg as jest.Mock;
13
+
14
+ /**
15
+ * Test Suite for radToDeg Function
16
+ */
17
+ describe('radToDeg', () => {
18
+ beforeEach(() => {
19
+ // Clear all previous mock calls and implementations before each test
20
+ mockedRadToDeg.mockClear();
21
+ });
22
+
23
+ it('should convert 0 radians to 0 degrees', () => {
24
+ // Arrange
25
+ mockedRadToDeg.mockReturnValue(0);
26
+
27
+ // Act
28
+ const result = radToDeg(0);
29
+
30
+ // Assert
31
+ expect(MathUtils.radToDeg).toHaveBeenCalledWith(0);
32
+ expect(result).toBe(0);
33
+ });
34
+
35
+ it('should convert π radians to 180 degrees', () => {
36
+ // Arrange
37
+ const pi = Math.PI;
38
+ mockedRadToDeg.mockReturnValue(180);
39
+
40
+ // Act
41
+ const result = radToDeg(pi);
42
+
43
+ // Assert
44
+ expect(MathUtils.radToDeg).toHaveBeenCalledWith(pi);
45
+ expect(result).toBe(180);
46
+ });
47
+
48
+ it('should convert 2π radians to 0 degrees (wrapped)', () => {
49
+ // Arrange
50
+ const twoPi = 2 * Math.PI;
51
+ mockedRadToDeg.mockReturnValue(360);
52
+
53
+ // Act
54
+ const result = radToDeg(twoPi);
55
+
56
+ // Assert
57
+ expect(MathUtils.radToDeg).toHaveBeenCalledWith(twoPi);
58
+ expect(result).toBe(0);
59
+ });
60
+
61
+ it('should convert 3π/2 radians to 270 degrees', () => {
62
+ // Arrange
63
+ const threePiOver2 = (3 * Math.PI) / 2;
64
+ mockedRadToDeg.mockReturnValue(270);
65
+
66
+ // Act
67
+ const result = radToDeg(threePiOver2);
68
+
69
+ // Assert
70
+ expect(MathUtils.radToDeg).toHaveBeenCalledWith(threePiOver2);
71
+ expect(result).toBe(270);
72
+ });
73
+
74
+ it('should handle angles greater than 2π radians correctly', () => {
75
+ // Arrange
76
+ const sevenPi = 7 * Math.PI; // 1260 degrees
77
+ const expectedDegrees = (1260 + 360) % 360; // 180 degrees
78
+ mockedRadToDeg.mockReturnValue(1260);
79
+
80
+ // Act
81
+ const result = radToDeg(sevenPi);
82
+
83
+ // Assert
84
+ expect(MathUtils.radToDeg).toHaveBeenCalledWith(sevenPi);
85
+ expect(result).toBe(expectedDegrees);
86
+ });
87
+
88
+ it('should handle fractional radians correctly', () => {
89
+ // Arrange
90
+ const fractionalRadians = Math.PI / 4; // 45 degrees
91
+ mockedRadToDeg.mockReturnValue(45);
92
+
93
+ // Act
94
+ const result = radToDeg(fractionalRadians);
95
+
96
+ // Assert
97
+ expect(MathUtils.radToDeg).toHaveBeenCalledWith(fractionalRadians);
98
+ expect(result).toBe(45);
99
+ });
100
+
101
+ it('should wrap negative angles correctly', () => {
102
+ // Since the function does not handle negative inputs, this test ensures it behaves as expected
103
+ // However, based on initial requirements, negative degrees are not allowed and inputs are non-negative
104
+ // This test is optional and based on how you want the function to behave
105
+ const negativeRadians = -Math.PI / 2; // -90 degrees
106
+ mockedRadToDeg.mockReturnValue(-90);
107
+ const expectedDegrees = (-90 + 360) % 360; // 270 degrees
108
+
109
+ const result = radToDeg(negativeRadians);
110
+
111
+ expect(MathUtils.radToDeg).toHaveBeenCalledWith(negativeRadians);
112
+ expect(result).toBe(expectedDegrees);
113
+ });
114
+
115
+ it('should handle multiple full rotations correctly', () => {
116
+ // Arrange
117
+ const multipleRotations = 5 * 2 * Math.PI; // 360 * 5 = 1800 degrees
118
+ const expectedDegrees = (1800 + 360) % 360; // 0 degrees
119
+ mockedRadToDeg.mockReturnValue(1800);
120
+
121
+ // Act
122
+ const result = radToDeg(multipleRotations);
123
+
124
+ // Assert
125
+ expect(MathUtils.radToDeg).toHaveBeenCalledWith(multipleRotations);
126
+ expect(result).toBe(0);
127
+ });
128
+
129
+ it('should handle mixed angles', () => {
130
+ // Arrange
131
+ const angles = [
132
+ Math.PI / 6,
133
+ Math.PI,
134
+ (5 * Math.PI) / 3,
135
+ ];
136
+ const mockReturns = [
137
+ 30,
138
+ 180,
139
+ 300,
140
+ ];
141
+ const expectedDegrees = [
142
+ (30 + 360) % 360,
143
+ (180 + 360) % 360,
144
+ (300 + 360) % 360,
145
+ ]; // [30, 180, 300]
146
+
147
+ mockedRadToDeg
148
+ .mockReturnValueOnce(mockReturns[0])
149
+ .mockReturnValueOnce(mockReturns[1])
150
+ .mockReturnValueOnce(mockReturns[2]);
151
+
152
+ // Act & Assert for each angle
153
+ angles.forEach((angle, index) => {
154
+ const result = radToDeg(angle);
155
+ expect(MathUtils.radToDeg).toHaveBeenNthCalledWith(
156
+ index + 1,
157
+ angle,
158
+ );
159
+ expect(result).toBe(expectedDegrees[index]);
160
+ });
161
+ });
162
+ });
@@ -0,0 +1,5 @@
1
+ import { MathUtils } from 'three';
2
+
3
+ export default function radToDeg(radians: number): number {
4
+ return (MathUtils.radToDeg(radians) + 360) % 360;
5
+ }
@@ -4,6 +4,7 @@ import type { GLTF } from 'three/examples/jsm/Addons.js';
4
4
  import { findSceneRecursive } from '../helper/findSceneRecursive/findSceneRecursive';
5
5
  import { type COMMaterial } from '../com/types';
6
6
  import { DIVENode } from '../node/Node';
7
+ import { DIVECommunication } from '../com/Communication';
7
8
 
8
9
  /**
9
10
  * A basic model class.
@@ -23,6 +24,7 @@ export class DIVEModel extends DIVENode {
23
24
 
24
25
  public SetModel(gltf: GLTF): void {
25
26
  this.clear();
27
+ this._boundingBox.makeEmpty();
26
28
 
27
29
  gltf.scene.traverse((child) => {
28
30
  child.castShadow = true;
@@ -110,11 +112,25 @@ export class DIVEModel extends DIVENode {
110
112
  }
111
113
 
112
114
  public PlaceOnFloor(): void {
113
- const oldPos = this.position.clone();
114
- this.position.y = -this._boundingBox.min.y * this.scale.y;
115
- if (this.position.y === oldPos.y) return;
116
-
117
- this.onMove();
115
+ // calculate and temporary save world position
116
+ const worldPos = this.getWorldPosition(this._positionWorldBuffer);
117
+ const oldWorldPos = worldPos.clone();
118
+
119
+ // calculate the bottom center of the bounding box and set it to world posion
120
+ worldPos.y = -this._boundingBox.min.y * this.scale.y;
121
+
122
+ // skip any action when the position did not change
123
+ if (worldPos.y === oldWorldPos.y) return;
124
+
125
+ DIVECommunication.get(this.userData.id)?.PerformAction(
126
+ 'UPDATE_OBJECT',
127
+ {
128
+ id: this.userData.id,
129
+ position: worldPos,
130
+ rotation: this.rotation,
131
+ scale: this.scale,
132
+ },
133
+ );
118
134
  }
119
135
 
120
136
  public DropIt(): void {
@@ -11,12 +11,17 @@ import {
11
11
  Color,
12
12
  } from 'three';
13
13
  import { type COMMaterial } from '../../com/types';
14
+ import { isTypeOnlyImportOrExportDeclaration } from 'typescript';
14
15
 
15
16
  const intersectObjectsMock = jest.fn();
16
17
 
17
18
  jest.mock('three', () => {
18
19
  return {
19
- Vector3: jest.fn(function (x: number, y: number, z: number) {
20
+ Vector3: jest.fn(function (
21
+ x: number = 0,
22
+ y: number = 0,
23
+ z: number = 0,
24
+ ) {
20
25
  this.x = x;
21
26
  this.y = y;
22
27
  this.z = z;
@@ -107,6 +112,9 @@ jest.mock('three', () => {
107
112
  this.traverse = jest.fn((callback) => {
108
113
  callback(this.children[0]);
109
114
  });
115
+ this.getWorldPosition = jest.fn(() => {
116
+ return this.position.clone();
117
+ });
110
118
  return this;
111
119
  }),
112
120
  Box3: jest.fn(function () {
@@ -116,6 +124,7 @@ jest.mock('three', () => {
116
124
  return new Vector3(0, 0, 0);
117
125
  });
118
126
  this.expandByObject = jest.fn();
127
+ this.makeEmpty = jest.fn();
119
128
 
120
129
  return this;
121
130
  }),
@@ -226,16 +235,37 @@ describe('dive/model/DIVEModel', () => {
226
235
  });
227
236
 
228
237
  it('should place on floor', () => {
238
+ const com = DIVECommunication.get('id')!;
239
+ const spyPerformAction = jest.spyOn(com, 'PerformAction');
240
+
229
241
  model.userData.id = 'something';
230
242
 
231
- const spy = jest.spyOn(model, 'onMove').mockImplementation(() => {});
243
+ model['_boundingBox'] = {
244
+ min: new Vector3(0, -1, 0),
245
+ } as unknown as Box3;
232
246
 
233
247
  expect(() => model.PlaceOnFloor()).not.toThrow();
234
- expect(spy).toHaveBeenCalledTimes(1);
248
+ expect(spyPerformAction).toHaveBeenCalledWith(
249
+ 'UPDATE_OBJECT',
250
+ expect.objectContaining({
251
+ position: expect.objectContaining({
252
+ y: 1,
253
+ }),
254
+ }),
255
+ );
235
256
 
257
+ // skip any action when the position did not change
258
+ spyPerformAction.mockClear();
259
+ model.position.y = 1;
260
+ expect(() => model.PlaceOnFloor()).not.toThrow();
261
+ expect(spyPerformAction).not.toHaveBeenCalled();
262
+
263
+ // mock that the communication is not available
264
+ spyPerformAction.mockClear();
236
265
  jest.spyOn(DIVECommunication, 'get').mockReturnValueOnce(undefined);
266
+ model.position.y = 0;
237
267
  expect(() => model.PlaceOnFloor()).not.toThrow();
238
- expect(spy).toHaveBeenCalledTimes(1);
268
+ expect(spyPerformAction).not.toHaveBeenCalled();
239
269
  });
240
270
 
241
271
  it('should drop it', () => {