Skip to content

Commit

Permalink
test(react-components): added test for few hooks (#4767)
Browse files Browse the repository at this point in the history
* added useCameraNavigation and use3dModels test files

* type error fix
  • Loading branch information
pramodcog authored Sep 19, 2024
1 parent 2fbfa6c commit 754c82b
Show file tree
Hide file tree
Showing 8 changed files with 310 additions and 15 deletions.
8 changes: 6 additions & 2 deletions react-components/tests/unit-tests/fixtures/cadModel.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,21 @@
import { type CogniteCadModel } from '@cognite/reveal';
import { Mock } from 'moq.ts';
import { Matrix4 } from 'three';
import { It, Mock } from 'moq.ts';
import { Box3, Matrix4, Vector3 } from 'three';

export const cadModelOptions = {
modelId: 123,
revisionId: 456
};

export const nodeBoundingBox = new Box3(new Vector3(1, 1, 1), new Vector3(2, 2, 2));

export const cadMock = new Mock<CogniteCadModel>()
.setup((p) => p.modelId)
.returns(cadModelOptions.modelId)
.setup((p) => p.revisionId)
.returns(cadModelOptions.revisionId)
.setup((p) => p.getModelTransformation())
.returns(new Matrix4())
.setup(async (p) => await p.getBoundingBoxesByNodeIds(It.IsAny()))
.returns(Promise.resolve([nodeBoundingBox]))
.object();
23 changes: 12 additions & 11 deletions react-components/tests/unit-tests/fixtures/cameraManager.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,20 @@
import { CameraManager, CameraManagerEventType, CameraState } from '@cognite/reveal';
import { type CameraManager, type CameraManagerEventType, type CameraState } from '@cognite/reveal';
import { remove } from 'lodash';
import { Mock } from 'moq.ts';
import { Vector3 } from 'three';
import { type Box3, type Vector3 } from 'three';

import { vi, Mock as viMock } from 'vitest';
import { vi, type Mock as viMock } from 'vitest';

export const cameraManagerGlobalCameraEvents: Record<
CameraManagerEventType,
viMock<[Vector3, Vector3], void>[]
Array<viMock<[Vector3, Vector3], void>>
> = {
cameraChange: [],
cameraStop: []
};

const cameraManagerGlobalCurrentCameraState: CameraState = {};
export const fitCameraToBoundingBoxMock = vi.fn<[Box3], void>();

export const cameraManagerMock = new Mock<CameraManager>()
.setup((p) => p.on)
Expand All @@ -31,14 +32,14 @@ export const cameraManagerMock = new Mock<CameraManager>()
.returns(({ position, target }) => {
cameraManagerGlobalCurrentCameraState.position = position;
cameraManagerGlobalCurrentCameraState.target = target;
setTimeout(
() =>
cameraManagerGlobalCameraEvents.cameraStop.forEach((callback) =>
callback(position!, target!)
),
50
);
setTimeout(() => {
cameraManagerGlobalCameraEvents.cameraStop.forEach((callback) => {
callback(position!, target!);
});
}, 50);
})
.setup((p) => p.getCameraState())
.returns(cameraManagerGlobalCurrentCameraState as Required<CameraState>)
.setup((p) => p.fitCameraToBoundingBox)
.returns(fitCameraToBoundingBoxMock)
.object();
68 changes: 68 additions & 0 deletions react-components/tests/unit-tests/fixtures/fdmNodeCache.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import { Mock } from 'moq.ts';
import { type FdmNodeCache } from '../../../src/components/CacheProvider/FdmNodeCache';
import { type FdmNodeCacheContent } from '../../../src/components/CacheProvider/NodeCacheProvider';
import { type DmsUniqueIdentifier } from '../../../src/data-providers/FdmSDK';
import { type TypedReveal3DModel } from '../../../src/components/Reveal3DResources/types';
import { type Node3D } from '@cognite/sdk';
import {
type FdmCadConnection,
type FdmConnectionWithNode
} from '../../../src/components/CacheProvider/types';

const fdmNodeCacheMock = new Mock<FdmNodeCache>()
.setup((instance) => instance.getAllMappingExternalIds)
.returns(
async (
modelRevisionIds: Array<{ modelId: number; revisionId: number }>,
_fetchViews: boolean
) => {
return new Map(
modelRevisionIds.map(({ modelId, revisionId }) => [
`${modelId}/${revisionId}`,
[
{
connection: {
instance: { space: 'space', externalId: 'id' },
modelId,
revisionId,
treeIndex: 1
} satisfies FdmCadConnection,
cadNode: {
id: 1,
treeIndex: 1,
parentId: 0,
depth: 0,
name: 'node-name',
subtreeSize: 1
} satisfies Node3D
} satisfies FdmConnectionWithNode
]
])
);
}
)
.setup((instance) => instance.getClosestParentDataPromises)
.returns((modelId: number, revisionId: number, treeIndex: number) => {
return {
modelId,
revisionId,
treeIndex,
data: `data-for-${modelId}-${revisionId}-${treeIndex}`,
cadAndFdmNodesPromise: Promise.resolve(undefined),
viewsPromise: Promise.resolve([])
};
})
.setup((instance) => instance.getMappingsForFdmInstances)
.returns(async (fdmAssetExternalIds: DmsUniqueIdentifier[], models: TypedReveal3DModel[]) => {
return models.map((model) => ({
modelId: model.modelId,
revisionId: model.revisionId,
mappings: new Map(fdmAssetExternalIds.map((id) => [JSON.stringify(id), [] as Node3D[]]))
}));
});

const fdmNodeCacheContentMock: FdmNodeCacheContent = {
cache: fdmNodeCacheMock.object()
};

export { fdmNodeCacheContentMock };
2 changes: 1 addition & 1 deletion react-components/tests/unit-tests/fixtures/image360.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Image360Collection } from '@cognite/reveal';
import { type Image360Collection } from '@cognite/reveal';
import { Mock } from 'moq.ts';

export const image360Options = {
Expand Down
2 changes: 1 addition & 1 deletion react-components/tests/unit-tests/fixtures/pointCloud.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { CognitePointCloudModel } from '@cognite/reveal';
import { type CognitePointCloudModel } from '@cognite/reveal';
import { Mock } from 'moq.ts';
import { Matrix4 } from 'three';

Expand Down
6 changes: 6 additions & 0 deletions react-components/tests/unit-tests/fixtures/viewer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ const domElement = document.createElement('div').appendChild(document.createElem
export const viewerModelsMock = vi.fn<[], CogniteModel[]>();
export const viewerRemoveModelsMock = vi.fn<[CogniteModel], void>();
export const viewerImage360CollectionsMock = vi.fn<[], Image360Collection[]>();
export const fitCameraToVisualSceneBoundingBoxMock = vi.fn<[number?], void>();
export const fitCameraToModelsMock = vi.fn<[CogniteModel[], number?, boolean?], void>();

export const viewerMock = new Mock<Cognite3DViewer>()
.setup((viewer) => {
Expand All @@ -25,4 +27,8 @@ export const viewerMock = new Mock<Cognite3DViewer>()
.returns(viewerRemoveModelsMock)
.setup((p) => p.cameraManager)
.returns(cameraManagerMock)
.setup((p) => p.fitCameraToVisualSceneBoundingBox)
.returns(fitCameraToVisualSceneBoundingBoxMock)
.setup((p) => p.fitCameraToModels)
.returns(fitCameraToModelsMock)
.object();
75 changes: 75 additions & 0 deletions react-components/tests/unit-tests/hooks/use3dModels.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import { describe, expect, test, vi, beforeEach, beforeAll, afterAll } from 'vitest';
import { use3dModels } from '../../../src/hooks/use3dModels';
import { CogniteCadModel, type CogniteModel } from '@cognite/reveal';
import { renderHook } from '@testing-library/react';

import { viewerMock, viewerModelsMock } from '../fixtures/viewer';
import { cadMock, cadModelOptions } from '../fixtures/cadModel';
import { Mock } from 'moq.ts';
import { Matrix4 } from 'three';

const mockResourceCount = { reveal3DResourcesCount: 2 };

vi.mock('../../../src/components/RevealCanvas/ViewerContext', () => ({
useReveal: () => viewerMock
}));

vi.mock('../../../src/components/Reveal3DResources/Reveal3DResourcesInfoContext', () => ({
useReveal3DResourcesCount: () => mockResourceCount
}));

describe('use3dModels', () => {
beforeEach(() => {
vi.resetAllMocks();
});

beforeAll(() => {
vi.useFakeTimers();
});

afterAll(() => {
vi.useRealTimers();
});

test('returns models from viewer', () => {
const mockModels: CogniteModel[] = [cadMock, cadMock];
viewerModelsMock.mockReturnValue(mockModels);

const { result } = renderHook(() => use3dModels());

expect(result.current).toEqual(mockModels);
});

test('updates models when viewer or resourceCount changes', () => {
const mockModels: CogniteModel[] = [cadMock, cadMock];
viewerModelsMock.mockReturnValue(mockModels);

const { result, rerender } = renderHook(() => use3dModels());

expect(result.current).toEqual(mockModels);

const newCadModelOptions = {
modelId: 987,
revisionId: 654
};

const newCadMock = new Mock<CogniteCadModel>()
.setup((p) => p.modelId)
.returns(newCadModelOptions.modelId)
.setup((p) => p.revisionId)
.returns(newCadModelOptions.revisionId)
.setup((p) => p.getModelTransformation())
.returns(new Matrix4())
.object();

const newMockModels: CogniteModel[] = [newCadMock, newCadMock];
const newMockResourceCount = { reveal3DResourcesCount: 3 };

viewerModelsMock.mockReturnValue(newMockModels);
mockResourceCount.reveal3DResourcesCount = newMockResourceCount.reveal3DResourcesCount;

rerender();

expect(result.current).toEqual(newMockModels);
});
});
141 changes: 141 additions & 0 deletions react-components/tests/unit-tests/hooks/useCameraNavigation.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
import { describe, expect, test, vi, beforeEach, beforeAll, afterAll } from 'vitest';
import { useCameraNavigation } from '../../../src/hooks/useCameraNavigation';
import { viewerMock, viewerModelsMock } from '../fixtures/viewer';
import { cadMock, nodeBoundingBox } from '../fixtures/cadModel';
import { act, renderHook } from '@testing-library/react';
import { fdmNodeCacheContentMock } from '../fixtures/fdmNodeCache';
import { Vector3 } from 'three';

vi.mock('../../../src/components/RevealCanvas/ViewerContext', () => ({
useReveal: () => viewerMock
}));

vi.mock('../../../src/components/CacheProvider/NodeCacheProvider', () => ({
useFdmNodeCache: () => fdmNodeCacheContentMock
}));

describe('useCameraNavigation', () => {
beforeEach(() => {
vi.resetAllMocks();
viewerMock.cameraManager.setCameraState = vi.fn();
viewerMock.cameraManager.fitCameraToBoundingBox = vi.fn();
});

beforeAll(() => {
vi.useFakeTimers();
});

afterAll(() => {
vi.useRealTimers();
});

test('fitCameraToVisualSceneBoundingBox calls viewer method', () => {
const { result } = renderHook(() => useCameraNavigation());

act(() => {
result.current.fitCameraToVisualSceneBoundingBox(1000);
});

expect(viewerMock.fitCameraToVisualSceneBoundingBox).toHaveBeenCalledWith(1000);
});

test('fitCameraToAllModels calls viewer method with models', () => {
const mockModels = [cadMock, cadMock];
viewerModelsMock.mockReturnValue(mockModels);

const { result } = renderHook(() => useCameraNavigation());

act(() => {
result.current.fitCameraToAllModels(1000);
});

expect(viewerMock.fitCameraToModels).toHaveBeenCalledWith(mockModels, 1000, true);
});

test('fitCameraToModelNodes calls viewer method with bounding box', async () => {
const mockModels = [cadMock, cadMock];
viewerModelsMock.mockReturnValue(mockModels);

const { result } = renderHook(() => useCameraNavigation());
const fitCameraToModelNodesSpy = vi.spyOn(result.current, 'fitCameraToModelNodes');

await act(async () => {
await result.current.fitCameraToModelNodes(456, [1, 2]);
});

expect(fitCameraToModelNodesSpy).toHaveBeenCalledWith(456, [1, 2]);

expect(viewerMock.cameraManager.fitCameraToBoundingBox).toHaveBeenCalledWith(nodeBoundingBox);
});

test('fitCameraToModelNode calls fitCameraToModelNodes with single node', async () => {
const mockModels = [cadMock, cadMock];
viewerModelsMock.mockReturnValue(mockModels);

const { result } = renderHook(() => useCameraNavigation());
const fitCameraToModelNodeSpy = vi.spyOn(result.current, 'fitCameraToModelNode');

await act(async () => {
await result.current.fitCameraToModelNode(456, 1);
});

expect(fitCameraToModelNodeSpy).toHaveBeenCalledWith(456, 1);
});

test('fitCameraToInstances calls fitCameraToModelNodes with node ids', async () => {
const mockModels = [cadMock, cadMock];
viewerModelsMock.mockReturnValue(mockModels);
const mockMappings = {
revisionId: 456,
mappings: new Map([['model1', [{ id: 1 }, { id: 2 }]]])
};

fdmNodeCacheContentMock.cache.getMappingsForFdmInstances = vi
.fn()
.mockResolvedValue([mockMappings]);

const { result } = renderHook(() => useCameraNavigation());

await act(async () => {
await result.current.fitCameraToInstances([{ externalId: 'ext1', space: 'space1' }]);
});

expect(viewerMock.cameraManager.fitCameraToBoundingBox).toHaveBeenCalled();
});

test('fitCameraToInstance calls fitCameraToInstances with single instance', async () => {
const mockModels = [cadMock, cadMock];
const mockMappings = {
revisionId: 456,
mappings: new Map([['model1', [{ id: 1 }]]])
};
viewerModelsMock.mockReturnValue(mockModels);

fdmNodeCacheContentMock.cache.getMappingsForFdmInstances = vi
.fn()
.mockResolvedValue([mockMappings]);

const { result } = renderHook(() => useCameraNavigation());

await act(async () => {
await result.current.fitCameraToInstance('ext1', 'space1');
});

expect(viewerMock.cameraManager.fitCameraToBoundingBox).toHaveBeenCalled();
});

test('fitCameraToState calls viewer method with camera state', () => {
const mockCameraState = {
position: new Vector3(0, 0, 0),
target: new Vector3(1, 1, 1)
};

const { result } = renderHook(() => useCameraNavigation());

act(() => {
result.current.fitCameraToState(mockCameraState);
});

expect(viewerMock.cameraManager.setCameraState).toHaveBeenCalledWith(mockCameraState);
});
});

0 comments on commit 754c82b

Please sign in to comment.