Skip to content

Commit

Permalink
feat(3D Knowledge Graph): improve unit tests
Browse files Browse the repository at this point in the history
  • Loading branch information
haweston authored and mumanity committed May 26, 2023
1 parent ef5c71c commit 11cd450
Show file tree
Hide file tree
Showing 11 changed files with 569 additions and 327 deletions.
265 changes: 107 additions & 158 deletions packages/scene-composer/src/components/SceneComposerInternal.spec.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import * as React from 'react';
import renderer, { act } from 'react-test-renderer';
import { cleanup, renderHook } from '@testing-library/react-hooks';
import str2ab from 'string-to-arraybuffer';
import flushPromises from 'flush-promises';
import { Object3D, Event, Mesh, MeshBasicMaterial, Color } from 'three';

import { SceneComposerInternal, SceneComposerApi, useSceneComposerApi, COMPOSER_FEATURES, setFeatureConfig } from '..';
import * as SceneLayoutComponents from '../layouts/SceneLayout';
import { invalidTestScenes, testScenes } from '../../tests/testData';
import { testScenes } from '../../tests/testData';

jest.mock('../layouts/StaticLayout', () => ({
StaticLayout: 'StaticLayout',
Expand All @@ -23,11 +24,71 @@ jest.mock('resize-observer-polyfill', () => {
return ResizeObserver;
});

const object3D = new Object3D<Event>();
const redColor = new Color('red');
const blueColor = new Color('blue');
const mesh = new Mesh(undefined, new MeshBasicMaterial({ color: redColor }));
object3D.children.push(mesh);

const nodeMap = [
{
name: 'Water Tank',
ref: 'waterTankRef',
components: [
{
ref: 'bindComponentRef',
type: 'DataBinding',
valueDataBindings: [
{
valueDataBinding: {
dataBindingContext: {
entityId: 'WaterTank',
},
},
},
],
},
],
},
{
name: 'FlowMeter',
ref: 'flowMeterRef',
components: [
{
ref: 'fakeComponent',
type: 'Camera',
valueDataBindings: [],
},
],
},
];

jest.mock('../store', () => {
const originalModule = jest.requireActual('../store');
return {
...originalModule,
useStore: jest.fn(() => {
return {
...originalModule.useStore(),
getState: jest.fn(() => {
return {
...originalModule.useStore().getState(),
document: {
nodeMap: nodeMap,
},
getObject3DBySceneNodeRef: jest.fn(() => object3D),
};
}),
};
}),
};
});

function createSceneLoaderMock(sceneContent: string) {
return {
getSceneUri: () => Promise.resolve('file://test.json'),
getSceneUrl: () => Promise.resolve('file://test.json'),
getSceneObject: (uri: string) => Promise.resolve(str2ab(sceneContent)),
getSceneObject: () => Promise.resolve(str2ab(sceneContent)),
};
}

Expand All @@ -37,177 +98,41 @@ describe('SceneComposerInternal', () => {
setFeatureConfig({ [COMPOSER_FEATURES.DataBinding]: true });
});

it('should render correctly with an empty scene in editing mode', async () => {
let container;
await act(async () => {
container = renderer.create(
<SceneComposerInternal config={{ mode: 'Editing' }} sceneLoader={createSceneLoaderMock('')} />,
);

await flushPromises();
});

// shows the scene hierarchy browser
expect(container).toMatchSnapshot();
});

it('should render correctly with an empty scene in viewing mode', async () => {
let container;
await act(async () => {
container = renderer.create(
<SceneComposerInternal config={{ mode: 'Viewing' }} sceneLoader={createSceneLoaderMock('')} />,
);

await flushPromises();
});

// shows the scene hierarchy browser
expect(container).toMatchSnapshot();
});

it('should render correctly with a valid scene in editing mode', async () => {
let container;
await act(async () => {
container = renderer.create(
<SceneComposerInternal config={{ mode: 'Editing' }} sceneLoader={createSceneLoaderMock(testScenes.scene1)} />,
);

await flushPromises();
});

// shows the scene hierarchy browser
expect(container).toMatchSnapshot();
});

it('should render warning when minor version is newer', async () => {
let container;
await act(async () => {
container = renderer.create(
<SceneComposerInternal
config={{ mode: 'Editing' }}
sceneLoader={createSceneLoaderMock(invalidTestScenes.unsupportedMinorVersionScene)}
/>,
);

await flushPromises();
});

expect(container).toMatchSnapshot();
});

it('should render error when major version is newer', async () => {
let container;
await act(async () => {
container = renderer.create(
<SceneComposerInternal
config={{ mode: 'Editing' }}
sceneLoader={createSceneLoaderMock(invalidTestScenes.unsupportedMajorVersion)}
/>,
);

await flushPromises();
});

expect(container).toMatchSnapshot();
});

it('should render error when specVersion is invalid', async () => {
let container;
await act(async () => {
container = renderer.create(
<SceneComposerInternal
config={{ mode: 'Editing' }}
sceneLoader={createSceneLoaderMock(invalidTestScenes.invalidSpecVersionScene)}
/>,
);

await flushPromises();
});

expect(container).toMatchSnapshot();
});

it('should support rendering multiple valid scenes', async () => {
let container;
await act(async () => {
container = renderer.create(
<div>
<SceneComposerInternal config={{ mode: 'Editing' }} sceneLoader={createSceneLoaderMock(testScenes.scene1)} />
<SceneComposerInternal config={{ mode: 'Editing' }} sceneLoader={createSceneLoaderMock(testScenes.scene2)} />
</div>,
);

await flushPromises();
});
describe('useSceneComposerApi', () => {
it('should return an api object', async () => {
let sut: SceneComposerApi | null = null;

// verify that 2 different scenes are rendered
expect(container).toMatchSnapshot();
});
const TestComponent = () => {
const sceneComposerId = 'test';
sut = useSceneComposerApi('test');

it('should render both valid and invalid scene correctly', async () => {
let container;
await act(async () => {
container = renderer.create(
<div>
return (
<SceneComposerInternal
sceneComposerId={sceneComposerId}
config={{ mode: 'Editing' }}
sceneLoader={createSceneLoaderMock(invalidTestScenes.invalidJson)}
sceneLoader={createSceneLoaderMock(testScenes.scene1)}
/>
<SceneComposerInternal config={{ mode: 'Editing' }} sceneLoader={createSceneLoaderMock(testScenes.scene1)} />
</div>,
);

await flushPromises();
});

expect(container).toMatchSnapshot();
});
);
};

it('should render a default error view when loading a bad scene content', async () => {
let container;
await act(async () => {
container = renderer.create(
<SceneComposerInternal
config={{ mode: 'Editing' }}
sceneLoader={createSceneLoaderMock(invalidTestScenes.invalidJson)}
/>,
);

await flushPromises();
});
await act(async () => {
renderer.create(<TestComponent />);

expect(container).toMatchSnapshot();
});
await flushPromises();
});

it('should render a default error view when unknown error happens', async () => {
const mocked = jest.spyOn(SceneLayoutComponents, 'SceneLayout').mockImplementation(() => {
throw new Error('failed to render');
expect(sut).toHaveProperty('setCameraTarget');
});

const container = renderer.create(
<SceneComposerInternal config={{ mode: 'Editing' }} sceneLoader={createSceneLoaderMock('')} />,
);

await flushPromises();

expect(container).toMatchSnapshot();

mocked.mockRestore();
});

describe('useSceneComposerApi', () => {
it('should return an api object', async () => {
let sut: SceneComposerApi | null = null;

it('should highlight and clear a scene node', async () => {
const TestComponent = () => {
const sceneComposerId = 'test';
sut = useSceneComposerApi('test');

return (
<SceneComposerInternal
sceneComposerId={sceneComposerId}
config={{ mode: 'Editing' }}
sceneLoader={createSceneLoaderMock(testScenes.scene1)}
sceneLoader={createSceneLoaderMock(testScenes.waterTank)}
/>
);
};
Expand All @@ -218,7 +143,31 @@ describe('SceneComposerInternal', () => {
await flushPromises();
});

expect(sut).toHaveProperty('setCameraTarget');
const composerApi = renderHook(() => useSceneComposerApi('test')).result.current;

act(() => {
composerApi.highlights([
{
dataBindingContext: {
entityId: 'WaterTank',
},
style: {
color: 'blue',
},
},
]);
});
expect(mesh.material.color.getHex()).toBe(blueColor.getHex());
act(() => {
composerApi.clearHighlights([
{
entityId: 'WaterTank',
},
]);
});
expect(mesh.material.color.getHex()).toBe(redColor.getHex());

cleanup();
});
});
});

0 comments on commit 11cd450

Please sign in to comment.