@shopware-ag/dive 1.16.2 → 1.16.4

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.2",
3
+ "version": "1.16.4",
4
4
  "description": "Shopware Spatial Framework",
5
5
  "type": "module",
6
6
  "main": "./build/dive.cjs",
@@ -292,6 +292,11 @@ export class DIVECommunication {
292
292
  );
293
293
  break;
294
294
  }
295
+ default: {
296
+ console.warn(
297
+ `DIVECommunication.PerformAction: has been executed with unknown Action type ${action}`,
298
+ );
299
+ }
295
300
  }
296
301
 
297
302
  this.dispatch(action, payload);
@@ -1,6 +1,6 @@
1
1
  import { DIVECommunication } from '../Communication';
2
2
  import '../types';
3
- import '../actions';
3
+ import { type Actions } from '../actions';
4
4
  import '../actions/camera/movecamera';
5
5
  import '../actions/camera/resetcamera';
6
6
  import '../actions/camera/setcameralayer';
@@ -933,4 +933,10 @@ describe('dive/communication/DIVECommunication', () => {
933
933
  });
934
934
  expect(result).toBe(url);
935
935
  });
936
+
937
+ it('should warn of action is of invalid type ', () => {
938
+ jest.spyOn(console, 'warn').mockImplementationOnce(() => {});
939
+ testCom.PerformAction('INVALID_ACTION' as keyof Actions, {});
940
+ expect(console.warn).toHaveBeenCalledTimes(1);
941
+ });
936
942
  });
@@ -1,5 +1,7 @@
1
+ import { COMGeometryType } from './COMGeometryType';
2
+
1
3
  export type COMGeometry = {
2
- name: string;
4
+ name: COMGeometryType;
3
5
  width: number;
4
6
  height: number;
5
7
  depth: number;
@@ -0,0 +1,9 @@
1
+ export type COMGeometryType =
2
+ | 'cylinder'
3
+ | 'sphere'
4
+ | 'pyramid'
5
+ | 'cube'
6
+ | 'box'
7
+ | 'cone'
8
+ | 'wall'
9
+ | 'plane';
@@ -7,6 +7,7 @@ import { type COMGeometry } from './COMGeometry';
7
7
  import { type COMMaterial } from './COMMaterial';
8
8
  import { type COMGroup } from './COMGroup';
9
9
  import { type COMEntityType } from './COMEntityType';
10
+ import { type COMGeometryType } from './COMGeometryType';
10
11
 
11
12
  export type {
12
13
  COMEntity,
@@ -18,4 +19,5 @@ export type {
18
19
  COMMaterial,
19
20
  COMGroup,
20
21
  COMEntityType,
22
+ COMGeometryType,
21
23
  };
package/src/io/IO.ts CHANGED
@@ -18,63 +18,40 @@ export class DIVEIO {
18
18
  type: FileType,
19
19
  url: string,
20
20
  ): Promise<DIVESceneFileType[FileType] | null> {
21
- return this._importFromURL(type, url)
22
-
23
- .catch((error) => {
24
- console.error(error);
25
-
26
- return null;
27
- });
28
- }
29
-
30
- public Export<FileType extends keyof DIVESceneFileType>(
31
- type: FileType,
32
- ): Promise<string | null> {
33
- return this._exportToURL(type)
34
-
35
- .catch((error) => {
36
- console.error(error);
37
-
38
- return null;
39
- });
40
- }
41
-
42
- private _importFromURL<FileType extends keyof DIVESceneFileType>(
43
- type: FileType,
44
- url: string,
45
- ): Promise<DIVESceneFileType[FileType]> {
46
21
  switch (type) {
47
22
  case 'glb': {
48
- return this._gltfIO.Import(url);
23
+ return this._gltfIO.Import(url).catch((error) => {
24
+ console.error(error);
25
+ return null;
26
+ });
49
27
  }
50
28
 
51
29
  default: {
52
- return Promise.reject('Unsupported file type: ' + type);
30
+ console.error('DIVEIO.Import: Unsupported file type: ' + type);
31
+ return Promise.reject();
53
32
  }
54
33
  }
55
34
  }
56
35
 
57
- private _exportToURL<FileType extends keyof DIVESceneFileType>(
36
+ public Export<FileType extends keyof DIVESceneFileType>(
58
37
  type: FileType,
59
- ): Promise<string> {
38
+ ): Promise<string | null> {
60
39
  switch (type) {
61
40
  case 'glb': {
62
- return new Promise((resolve, reject) => {
63
- this._gltfIO
64
- .Export(this._scene, true, true)
65
-
66
- .then((data) => {
67
- resolve(this._createBlobURL(data));
68
- })
69
-
70
- .catch((error) => {
71
- reject(error);
72
- });
73
- });
41
+ return this._gltfIO
42
+ .Export(this._scene, true, true)
43
+ .then((data) => {
44
+ return this._createBlobURL(data);
45
+ })
46
+ .catch((error) => {
47
+ console.error(error);
48
+ return null;
49
+ });
74
50
  }
75
51
 
76
52
  default: {
77
- return Promise.reject('Unsupported file type: ' + type);
53
+ console.error('DIVEIO.Export: Unsupported file type: ' + type);
54
+ return Promise.reject();
78
55
  }
79
56
  }
80
57
  }
@@ -58,15 +58,24 @@ describe('dive/io/DIVEIO', () => {
58
58
  expect(result).toStrictEqual(mockGLTF);
59
59
  });
60
60
 
61
- it('should reject when importing with unsupported file type', async () => {
61
+ it('should handle rejection from gltf io import', async () => {
62
62
  jest.spyOn(console, 'error').mockImplementationOnce(() => {});
63
-
64
- const result = await testIO.Import(
65
- 'unsupported file type' as 'glb',
66
- 'test.glb',
63
+ jest.spyOn(testIO['_gltfIO'], 'Import').mockRejectedValueOnce(
64
+ 'THIS_IS_A_TEST_ERROR',
67
65
  );
68
66
 
69
- expect(result).toStrictEqual(null);
67
+ expect(testIO.Import('glb', 'test.glb')).resolves.toEqual(null);
68
+ });
69
+
70
+ it('should reject when importing with unsupported file type', async () => {
71
+ const spy = jest
72
+ .spyOn(console, 'error')
73
+ .mockImplementationOnce(() => {});
74
+
75
+ expect(
76
+ testIO.Import('THIS_IS_NOT_A_VALID_FILE_TYPE' as 'glb', 'test.glb'),
77
+ ).rejects.not.toBeDefined();
78
+ expect(spy).toHaveBeenCalled();
70
79
  });
71
80
 
72
81
  it('should export to URL', async () => {
@@ -80,20 +89,28 @@ describe('dive/io/DIVEIO', () => {
80
89
 
81
90
  const result = await testIO.Export('glb');
82
91
  expect(result).toBeDefined();
92
+ expect(console.error).not.toHaveBeenCalled();
83
93
  });
84
94
 
85
- it('should handle rejection from gltf io', async () => {
86
- jest.spyOn(console, 'error').mockImplementationOnce(() => {});
87
- jest.spyOn(testIO['_gltfIO'], 'Export').mockRejectedValueOnce('Error');
95
+ it('should handle rejection from gltf io export', async () => {
96
+ const spy = jest
97
+ .spyOn(console, 'error')
98
+ .mockImplementationOnce(() => {});
99
+ jest.spyOn(testIO['_gltfIO'], 'Export').mockRejectedValueOnce(
100
+ 'THIS_IS_A_TEST_ERROR',
101
+ );
88
102
 
89
- const result = await testIO.Export('glb');
90
- expect(result).toBeDefined();
103
+ expect(testIO.Export('glb')).resolves.toEqual(null);
91
104
  });
92
105
 
93
106
  it('should reject when exporting with unsupported file type', async () => {
94
- jest.spyOn(console, 'error').mockImplementationOnce(() => {});
95
-
96
- const result = await testIO.Export('unsupported file type' as 'glb');
97
- expect(result).toStrictEqual(null);
107
+ const spy = jest
108
+ .spyOn(console, 'error')
109
+ .mockImplementationOnce(() => {});
110
+
111
+ expect(
112
+ testIO.Export('THIS_IS_NOT_A_VALID_FILE_TYPE' as 'glb'),
113
+ ).rejects.not.toBeDefined();
114
+ expect(spy).toHaveBeenCalled();
98
115
  });
99
116
  });
@@ -43,7 +43,10 @@ export class DIVEPrimitive extends DIVENode {
43
43
  }
44
44
 
45
45
  public SetGeometry(geometry: COMGeometry): void {
46
- this._mesh.geometry = this.assembleGeometry(geometry);
46
+ const geo = this.assembleGeometry(geometry);
47
+ if (!geo) return;
48
+
49
+ this._mesh.geometry = geo;
47
50
  this._boundingBox.setFromObject(this._mesh);
48
51
  }
49
52
 
@@ -168,7 +171,7 @@ export class DIVEPrimitive extends DIVENode {
168
171
  }
169
172
  }
170
173
 
171
- private assembleGeometry(geometry: COMGeometry): BufferGeometry {
174
+ private assembleGeometry(geometry: COMGeometry): BufferGeometry | null {
172
175
  switch (geometry.name.toLowerCase()) {
173
176
  case 'cylinder':
174
177
  return this.createCylinderGeometry(geometry);
@@ -176,6 +179,7 @@ export class DIVEPrimitive extends DIVENode {
176
179
  return this.createSphereGeometry(geometry);
177
180
  case 'pyramid':
178
181
  return this.createPyramidGeometry(geometry);
182
+ case 'cube':
179
183
  case 'box':
180
184
  return this.createBoxGeometry(geometry);
181
185
  case 'cone':
@@ -186,10 +190,10 @@ export class DIVEPrimitive extends DIVENode {
186
190
  return this.createPlaneGeometry(geometry);
187
191
  default: {
188
192
  console.warn(
189
- 'DIVEPrimitive: Invalid geometry type:',
193
+ 'DIVEPrimitive.assembleGeometry: Invalid geometry type:',
190
194
  geometry.name.toLowerCase(),
191
195
  );
192
- return new BufferGeometry();
196
+ return null;
193
197
  }
194
198
  }
195
199
  }
@@ -8,7 +8,11 @@ import {
8
8
  type MeshStandardMaterial,
9
9
  } from 'three';
10
10
  import type { DIVEScene } from '../../scene/Scene';
11
- import { type COMMaterial, type COMGeometry } from '../../com/types';
11
+ import {
12
+ type COMMaterial,
13
+ type COMGeometry,
14
+ type COMGeometryType,
15
+ } from '../../com/types';
12
16
 
13
17
  const intersectObjectsMock = jest.fn();
14
18
 
@@ -219,10 +223,21 @@ describe('dive/primitive/DIVEPrimitive', () => {
219
223
  });
220
224
 
221
225
  it('should set geometry', () => {
226
+ jest.spyOn(console, 'warn');
227
+ const geometry = {
228
+ name: 'cube' as COMGeometryType,
229
+ } as COMGeometry;
230
+ expect(() => primitive.SetGeometry(geometry)).not.toThrow();
231
+ expect(console.warn).not.toHaveBeenCalled();
232
+ });
233
+
234
+ it('should warn when geometry is invalid', () => {
235
+ jest.spyOn(console, 'warn').mockImplementation(() => {});
222
236
  const geometry = {
223
- name: 'geometry',
237
+ name: 'INVALID' as COMGeometryType,
224
238
  } as COMGeometry;
225
239
  expect(() => primitive.SetGeometry(geometry)).not.toThrow();
240
+ expect(console.warn).toHaveBeenCalled();
226
241
  });
227
242
 
228
243
  it('should place on floor', () => {
@@ -10,7 +10,6 @@ const test_uuid = 'test-uuid';
10
10
 
11
11
  const mock_render = jest.fn();
12
12
  const mock_setSize = jest.fn();
13
- const mock_setAnimationLoop = jest.fn();
14
13
 
15
14
  jest.mock('three', () => {
16
15
  return {
@@ -27,7 +26,9 @@ jest.mock('three', () => {
27
26
  this.setSize = mock_setSize;
28
27
  this.setPixelRatio = jest.fn();
29
28
  this.render = mock_render;
30
- this.setAnimationLoop = mock_setAnimationLoop;
29
+ this.setAnimationLoop = jest.fn((callback: (() => void) | null) => {
30
+ if (callback) callback();
31
+ });
31
32
  this.shadowMap = {
32
33
  enabled: false,
33
34
  };
@@ -81,6 +81,11 @@ export class DIVERoot extends Object3D {
81
81
  this.updateGroup(object);
82
82
  break;
83
83
  }
84
+ default: {
85
+ console.warn(
86
+ `DIVERoot.AddSceneObject: Unknown entity type: ${object.entityType}`,
87
+ );
88
+ }
84
89
  }
85
90
  }
86
91
 
@@ -107,6 +112,11 @@ export class DIVERoot extends Object3D {
107
112
  this.updateGroup(object);
108
113
  break;
109
114
  }
115
+ default: {
116
+ console.warn(
117
+ `DIVERoot.UpdateSceneObject: Unknown entity type: ${object.entityType}`,
118
+ );
119
+ }
110
120
  }
111
121
  }
112
122
 
@@ -133,6 +143,11 @@ export class DIVERoot extends Object3D {
133
143
  this.deleteGroup(object);
134
144
  break;
135
145
  }
146
+ default: {
147
+ console.warn(
148
+ `DIVERoot.DeleteSceneObject: Unknown entity type: ${object.entityType}`,
149
+ );
150
+ }
136
151
  }
137
152
  }
138
153
 
@@ -147,6 +162,11 @@ export class DIVERoot extends Object3D {
147
162
  this.placeOnFloor(object);
148
163
  break;
149
164
  }
165
+ default: {
166
+ console.warn(
167
+ `DIVERoot.PlaceOnFloor: Unknown entity type: ${object.entityType}`,
168
+ );
169
+ }
150
170
  }
151
171
  }
152
172
 
@@ -6,7 +6,8 @@ import {
6
6
  type COMPov,
7
7
  type COMEntity,
8
8
  type COMGeometry,
9
- COMGroup,
9
+ type COMGroup,
10
+ type COMEntityType,
10
11
  } from '../../../com/types';
11
12
  import { type DIVEScene } from '../../Scene';
12
13
  import { DIVECommunication } from '../../../com/Communication';
@@ -928,4 +929,56 @@ describe('DIVE/scene/root/DIVERoot', () => {
928
929
  } as COMPrimitive),
929
930
  ).not.toThrow();
930
931
  });
932
+
933
+ it('should warn if entity type is invalid while adding object', () => {
934
+ const spy = jest.spyOn(console, 'warn').mockImplementation(() => {});
935
+ expect(() =>
936
+ root.AddSceneObject({
937
+ id: 'id',
938
+ name: 'entity',
939
+ entityType: 'INVALID' as COMEntityType,
940
+ visible: true,
941
+ } as COMEntity),
942
+ ).not.toThrow();
943
+ expect(spy).toHaveBeenCalled();
944
+ });
945
+
946
+ it('should warn if entity type is invalid while updating object', () => {
947
+ const spy = jest.spyOn(console, 'warn').mockImplementation(() => {});
948
+ expect(() =>
949
+ root.UpdateSceneObject({
950
+ id: 'id',
951
+ name: 'entity',
952
+ entityType: 'INVALID' as COMEntityType,
953
+ visible: true,
954
+ } as COMEntity),
955
+ ).not.toThrow();
956
+ expect(spy).toHaveBeenCalled();
957
+ });
958
+
959
+ it('should warn if entity type is invalid while deleting object', () => {
960
+ const spy = jest.spyOn(console, 'warn').mockImplementation(() => {});
961
+ expect(() =>
962
+ root.DeleteSceneObject({
963
+ id: 'id',
964
+ name: 'entity',
965
+ entityType: 'INVALID' as COMEntityType,
966
+ visible: true,
967
+ } as COMEntity),
968
+ ).not.toThrow();
969
+ expect(spy).toHaveBeenCalled();
970
+ });
971
+
972
+ it('should warn if entity type is invalid while placing on floor', () => {
973
+ const spy = jest.spyOn(console, 'warn').mockImplementation(() => {});
974
+ expect(() =>
975
+ root.PlaceOnFloor({
976
+ id: 'id',
977
+ name: 'entity',
978
+ entityType: 'INVALID' as COMEntityType,
979
+ visible: true,
980
+ } as COMEntity),
981
+ ).not.toThrow();
982
+ expect(spy).toHaveBeenCalled();
983
+ });
931
984
  });
@@ -95,15 +95,23 @@ export abstract class DIVEBaseTool {
95
95
 
96
96
  public onPointerDown(e: PointerEvent): void {
97
97
  switch (e.button) {
98
- case 0:
98
+ case 0: {
99
99
  this._pointerPrimaryDown = true;
100
100
  break;
101
- case 1:
101
+ }
102
+ case 1: {
102
103
  this._pointerMiddleDown = true;
103
104
  break;
104
- case 2:
105
+ }
106
+ case 2: {
105
107
  this._pointerSecondaryDown = true;
106
108
  break;
109
+ }
110
+ default: {
111
+ console.warn(
112
+ 'DIVEBaseTool.onPointerDown: Unknown button: ' + e.button,
113
+ );
114
+ }
107
115
  }
108
116
 
109
117
  this._lastPointerDown.copy(this._pointer);
@@ -66,7 +66,7 @@ export default class DIVEToolbox {
66
66
  break;
67
67
  }
68
68
  default: {
69
- throw new Error(`ToolBox.UseTool: Unknown tool: ${tool}`);
69
+ console.warn(`DIVEToolBox.UseTool: Unknown tool: ${tool}`);
70
70
  }
71
71
  }
72
72
  }
@@ -1,7 +1,7 @@
1
1
  import { DIVEBaseTool } from '../BaseTool';
2
2
  import type DIVEOrbitControls from '../../controls/OrbitControls';
3
3
  import type { DIVEScene } from '../../scene/Scene';
4
- import { type Object3D, type Vector3 } from 'three';
4
+ import { type Intersection, type Object3D, type Vector3 } from 'three';
5
5
  import { type DIVEHoverable } from '../../interface/Hoverable';
6
6
  import { type DIVEDraggable } from '../../interface/Draggable';
7
7
 
@@ -60,8 +60,36 @@ describe('dive/toolbox/DIVEBaseTool', () => {
60
60
 
61
61
  it('should raycast', () => {
62
62
  const toolBox = new abstractWrapper(mockScene, mockController);
63
+ const spy = jest
64
+ .spyOn(toolBox['_raycaster'], 'intersectObjects')
65
+ .mockImplementationOnce(() => {
66
+ return [
67
+ {
68
+ object: {
69
+ visible: true,
70
+ },
71
+ } as unknown as Intersection,
72
+ ];
73
+ });
63
74
  expect(() => toolBox['raycast']()).not.toThrow();
75
+ expect(spy).toHaveBeenCalled();
76
+ });
77
+
78
+ it('should raycast with selection of objects', () => {
79
+ const toolBox = new abstractWrapper(mockScene, mockController);
80
+ const spy = jest
81
+ .spyOn(toolBox['_raycaster'], 'intersectObjects')
82
+ .mockImplementationOnce(() => {
83
+ return [
84
+ {
85
+ object: {
86
+ visible: true,
87
+ },
88
+ } as unknown as Intersection,
89
+ ];
90
+ });
64
91
  expect(() => toolBox['raycast']([])).not.toThrow();
92
+ expect(spy).toHaveBeenCalled();
65
93
  });
66
94
 
67
95
  it('should return correct pointerAnyDown', () => {
@@ -96,13 +124,21 @@ describe('dive/toolbox/DIVEBaseTool', () => {
96
124
  expect(() =>
97
125
  toolBox.onPointerDown({ button: 0 } as PointerEvent),
98
126
  ).not.toThrow();
127
+
99
128
  expect(() =>
100
129
  toolBox.onPointerDown({ button: 1 } as PointerEvent),
101
130
  ).not.toThrow();
131
+
102
132
  expect(() =>
103
133
  toolBox.onPointerDown({ button: 2 } as PointerEvent),
104
134
  ).not.toThrow();
105
135
 
136
+ const spy = jest.spyOn(console, 'warn').mockImplementation();
137
+ expect(() =>
138
+ toolBox.onPointerDown({ button: 666 } as PointerEvent),
139
+ ).not.toThrow();
140
+ expect(spy).toHaveBeenCalled();
141
+
106
142
  toolBox['_intersects'] = [
107
143
  {
108
144
  distance: 1,
@@ -6,21 +6,6 @@ import type { DIVEScene } from '../../scene/Scene';
6
6
  * @jest-environment jsdom
7
7
  */
8
8
 
9
- const mock_addEventListener = jest.fn();
10
- const mock_removeEventListener = jest.fn();
11
-
12
- const mock_Canvas = {
13
- width: 0,
14
- height: 0,
15
- addEventListener: mock_addEventListener,
16
- getContext: jest.fn(),
17
- removeEventListener: mock_removeEventListener,
18
- clientWidth: 0,
19
- clientHeight: 0,
20
- offsetLeft: 0,
21
- offsetTop: 0,
22
- };
23
-
24
9
  jest.mock('../select/SelectTool.ts', () => {
25
10
  return {
26
11
  DIVESelectTool: jest.fn(function () {
@@ -38,7 +23,21 @@ jest.mock('../select/SelectTool.ts', () => {
38
23
  });
39
24
 
40
25
  const mockController = {
41
- domElement: mock_Canvas,
26
+ domElement: {
27
+ width: 0,
28
+ height: 0,
29
+ addEventListener: jest.fn((type, callback) => {
30
+ callback();
31
+ }),
32
+ getContext: jest.fn(),
33
+ removeEventListener: jest.fn((type, callback) => {
34
+ callback();
35
+ }),
36
+ clientWidth: 0,
37
+ clientHeight: 0,
38
+ offsetLeft: 0,
39
+ offsetTop: 0,
40
+ },
42
41
  object: {},
43
42
  } as unknown as DIVEOrbitControls;
44
43
 
@@ -55,14 +54,15 @@ describe('dive/toolbox/DIVEToolBox', () => {
55
54
  it('should dispose', () => {
56
55
  const toolBox = new DIVEToolbox({} as DIVEScene, mockController);
57
56
  toolBox.Dispose();
58
- expect(mock_removeEventListener).toHaveBeenCalled();
59
57
  });
60
58
 
61
59
  it('should throw with incorrect tool', () => {
60
+ const spy = jest.spyOn(console, 'warn').mockImplementation();
62
61
  const toolBox = new DIVEToolbox({} as DIVEScene, mockController);
63
62
  expect(() =>
64
63
  toolBox.UseTool('not a real tool' as unknown as ToolType),
65
- ).toThrow();
64
+ ).not.toThrow();
65
+ expect(spy).toHaveBeenCalled();
66
66
  });
67
67
 
68
68
  it('should use no tool', () => {