Warning, /firebird/firebird-ng/src/app/utils/three.utils.spec.ts is written in an unsupported language. File is not indexed.
0001 import type { Mock } from "vitest";
0002 import { walkObject3DNodes, findObject3DNodes } from './three.utils';
0003 import * as THREE from 'three';
0004 import { isColorable, getColorOrDefault } from './three.utils';
0005
0006 describe('walkObject3dNodes', () => {
0007 let root: THREE.Object3D;
0008 let callback: Mock;
0009
0010 beforeEach(() => {
0011 // Create a simple scene graph: root -> child -> grandchild
0012 root = new THREE.Object3D();
0013 root.name = 'root';
0014
0015 const child = new THREE.Object3D();
0016 child.name = 'child';
0017 root.add(child);
0018
0019 const grandchild = new THREE.Object3D();
0020 grandchild.name = 'grandchild';
0021 child.add(grandchild);
0022
0023 // Setup callback spy
0024 callback = vi.fn();
0025 });
0026
0027 it('should invoke callback for each node when no pattern is given', () => {
0028 walkObject3DNodes(root, callback);
0029 expect(vi.mocked(callback).mock.calls.length).toBe(3);
0030 expect(vi.mocked(callback).mock.calls[0]).toEqual([root, 'root', 0]);
0031 expect(vi.mocked(callback).mock.calls[1]).toEqual([root.children[0], 'root/child', 1]);
0032 expect(vi.mocked(callback).mock.calls[2]).toEqual([root.children[0].children[0], 'root/child/grandchild', 2]);
0033 });
0034
0035 it('should respect the maxLevel parameter', () => {
0036 walkObject3DNodes(root, callback, { maxLevel: 1 });
0037 expect(vi.mocked(callback).mock.calls.length).toBe(2);
0038 });
0039
0040 it('should correctly match nodes when a pattern is given', () => {
0041 const pattern = 'root/child';
0042 walkObject3DNodes(root, callback, { pattern: pattern });
0043 expect(callback).toHaveBeenCalledWith(expect.objectContaining({ name: 'child' }), 'root/child', 1);
0044 });
0045
0046 it('should return the correct number of processed nodes', () => {
0047 const processed = walkObject3DNodes(root, callback);
0048 expect(processed).toBe(3); // Including root, child, grandchild
0049 });
0050
0051 it('should not invoke callback for non-matching pattern', () => {
0052 const pattern = 'nonexistent';
0053 walkObject3DNodes(root, callback, { pattern: pattern });
0054 expect(callback).not.toHaveBeenCalled();
0055 });
0056 });
0057
0058
0059 describe('Material color functions', () => {
0060 describe('isColorable', () => {
0061 it('should return true if material has a color property', () => {
0062 const colorableMaterial = { color: new THREE.Color(255, 0, 0) };
0063 expect(isColorable(colorableMaterial)).toBe(true);
0064 });
0065
0066 it('should return false if material does not have a color property', () => {
0067 const nonColorableMaterial = { noColor: true };
0068 expect(isColorable(nonColorableMaterial)).toBe(false);
0069 });
0070 });
0071
0072 describe('getColorOrDefault', () => {
0073 it('should return the material color if material is colorable', () => {
0074 const colorableMaterial = { color: new THREE.Color(255, 0, 0) };
0075 const defaultColor = new THREE.Color(0, 0, 0);
0076 expect(getColorOrDefault(colorableMaterial, defaultColor)).toEqual(colorableMaterial.color);
0077 });
0078
0079 it('should return the default color if material is not colorable', () => {
0080 const nonColorableMaterial = { noColor: true };
0081 const defaultColor = new THREE.Color(0, 0, 0);
0082 expect(getColorOrDefault(nonColorableMaterial, defaultColor)).toEqual(defaultColor);
0083 });
0084 });
0085 });
0086
0087 describe('findObject3DNodes', () => {
0088 let root: THREE.Object3D, child1: THREE.Object3D, child2: THREE.Object3D, subchild1: THREE.Object3D, subchild2: THREE.Object3D;
0089
0090 beforeEach(() => {
0091 // Setup a mock hierarchy of THREE.Object3D nodes
0092 root = new THREE.Object3D();
0093 root.name = 'root';
0094
0095 child1 = new THREE.Object3D();
0096 child1.name = 'child1';
0097 root.add(child1);
0098
0099 child2 = new THREE.Mesh(new THREE.BoxGeometry(1, 1, 1));
0100 child2.name = 'child2';
0101 root.add(child2);
0102
0103 subchild1 = new THREE.Object3D();
0104 subchild1.name = 'subchild1';
0105 child1.add(subchild1);
0106
0107 subchild2 = new THREE.Mesh(new THREE.BoxGeometry(1, 1, 1));
0108 subchild2.name = 'match';
0109 child2.add(subchild2);
0110 });
0111
0112 it('should return all nodes when no pattern or type is provided', () => {
0113 const results = findObject3DNodes(root, '');
0114 expect(results.nodes.length).toBe(5);
0115 expect(results.deepestLevel).toBe(2);
0116 expect(results.totalWalked).toBe(5);
0117 });
0118
0119 it('should handle no matches found', () => {
0120 const results = findObject3DNodes(root, 'nonexistent');
0121 expect(results.nodes.length).toBe(0);
0122 });
0123
0124 it('should match nodes based on a pattern', () => {
0125 const results = findObject3DNodes(root, '**/match');
0126 expect(results.nodes.length).toBe(1);
0127 expect(results.nodes[0]).toBe(subchild2);
0128 expect(results.fullPaths[0]).toBe('root/child2/match');
0129 });
0130
0131 it('should filter nodes based on type', () => {
0132 const results = findObject3DNodes(root, '', 'Mesh');
0133 expect(results.nodes.every(node => node instanceof THREE.Mesh)).toBe(true);
0134 expect(results.nodes.length).toBe(2); // Only child2 and subchild2 are Meshes
0135 });
0136
0137 it('should respect the maxLevel parameter', () => {
0138 const results = findObject3DNodes(root, '', '', 1);
0139 expect(results.deepestLevel).toBe(1);
0140 expect(results.totalWalked).toBe(3); // root, child1, child2
0141 });
0142
0143 it('should throw an error for invalid parentNode', () => {
0144 expect(() => {
0145 findObject3DNodes(null, '');
0146 }).toThrow();
0147 });
0148 });