From 0f8ebec49e4629689c47b528dd5f3c90e7f40c17 Mon Sep 17 00:00:00 2001 From: "Ivan Mar (sOkam!)" <7308253+heysokam@users.noreply.github.com> Date: Fri, 31 May 2024 07:37:23 +0200 Subject: [PATCH] [IR-2055] Unit Test - Camera (#10247) * tst: Add `CameraSystem.test.tsx` stub file * chg: Remove `deprecated` tag from System.reactor (as per request) * tst: UnitTest skeleton for `CameraEntityState` with event sourcing * tst: Fix `CameraComponent` assert on the wrong entity * tst: Renamed the `CameraEntityState` to be more descriptive * tst: Unit Tests for `SpectateEntityState` from `SpectateSystem.tsx` --- packages/ecs/src/SystemFunctions.ts | 1 - .../src/camera/systems/CameraSystem.test.tsx | 116 +++++++++++++ .../camera/systems/SpectateSystem.test.tsx | 152 ++++++++++++++++++ 3 files changed, 268 insertions(+), 1 deletion(-) create mode 100755 packages/spatial/src/camera/systems/CameraSystem.test.tsx create mode 100644 packages/spatial/src/camera/systems/SpectateSystem.test.tsx diff --git a/packages/ecs/src/SystemFunctions.ts b/packages/ecs/src/SystemFunctions.ts index 6af3755097..b98ceb20ee 100755 --- a/packages/ecs/src/SystemFunctions.ts +++ b/packages/ecs/src/SystemFunctions.ts @@ -62,7 +62,6 @@ export interface System { * Defaults to 'variable'. */ timeStep: number | 'variable' - /** @deprecated use defineState reactor instead */ reactor?: FC insert?: InsertSystem preSystems: SystemUUID[] diff --git a/packages/spatial/src/camera/systems/CameraSystem.test.tsx b/packages/spatial/src/camera/systems/CameraSystem.test.tsx new file mode 100755 index 0000000000..17944236bd --- /dev/null +++ b/packages/spatial/src/camera/systems/CameraSystem.test.tsx @@ -0,0 +1,116 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import { act, render } from '@testing-library/react' +import assert from 'assert' +import React from 'react' + +import { NetworkId } from '@etherealengine/common/src/interfaces/NetworkId' +import { UserID } from '@etherealengine/common/src/schema.type.module' +import { + Engine, + SystemDefinitions, + UUIDComponent, + UndefinedEntity, + createEntity, + destroyEngine, + getComponent, + hasComponent, + removeEntity, + setComponent +} from '@etherealengine/ecs' +import { PeerID, applyIncomingActions, dispatchAction } from '@etherealengine/hyperflux' +import { + Network, + NetworkPeerFunctions, + NetworkState, + NetworkTopics, + NetworkWorldUserStateSystem +} from '@etherealengine/network' +import { createMockNetwork } from '../../../../network/tests/createMockNetwork' +import { createEngine } from '../../initializeEngine' +import { CameraActions } from '../CameraState' +import { CameraComponent } from '../components/CameraComponent' + +describe('CameraSystem', async () => { + let viewerEntity = UndefinedEntity + + describe('CameraEntityState', async () => { + beforeEach(async () => { + createEngine() + createMockNetwork() + Engine.instance.store.defaultDispatchDelay = () => 0 + viewerEntity = createEntity() + setComponent(viewerEntity, UUIDComponent, UUIDComponent.generateUUID()) + }) + + afterEach(() => { + removeEntity(viewerEntity) + return destroyEngine() + }) + + const NetworkWorldUserStateSystemReactor = SystemDefinitions.get(NetworkWorldUserStateSystem)!.reactor! + const tag = + + it('should create a camera entity and apply a CameraComponent to that entity', async () => { + const hostUserId = 'world' as UserID + const userId = 'user id' as UserID + const peerID = Engine.instance.store.peerID + const peerID2 = 'peer id 2' as PeerID + const CameraUUID = UUIDComponent.generateUUID() + + Engine.instance.userID = userId + const network: Network = NetworkState.worldNetwork + + NetworkPeerFunctions.createPeer(network, peerID, 0, hostUserId, 0) + NetworkPeerFunctions.createPeer(network, peerID2, 1, userId, 1) + const objNetId = 3 as NetworkId + + const { rerender, unmount } = render(tag) + await act(() => rerender(tag)) + + dispatchAction( + CameraActions.spawnCamera({ + parentUUID: getComponent(viewerEntity, UUIDComponent), + entityUUID: CameraUUID, + ownerID: network.hostUserID, // from host + networkId: objNetId, + $topic: NetworkTopics.world, + $peer: Engine.instance.store.peerID + }) + ) + applyIncomingActions() + + const cameraEntity = UUIDComponent.getEntityByUUID(CameraUUID) + assert.ok(cameraEntity, "The spawnCamera Action didn't create an entity.") + assert.ok( + hasComponent(cameraEntity, CameraComponent), + "The spawnCamera Action didn't apply the CameraComponent to the entity" + ) + + unmount() + }) + }) +}) diff --git a/packages/spatial/src/camera/systems/SpectateSystem.test.tsx b/packages/spatial/src/camera/systems/SpectateSystem.test.tsx new file mode 100644 index 0000000000..bf97a59c0f --- /dev/null +++ b/packages/spatial/src/camera/systems/SpectateSystem.test.tsx @@ -0,0 +1,152 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import { act, render } from '@testing-library/react' +import assert from 'assert' +import React from 'react' + +import { UserID } from '@etherealengine/common/src/schema.type.module' +import { + Engine, + SystemDefinitions, + UUIDComponent, + UndefinedEntity, + createEntity, + destroyEngine, + removeEntity, + setComponent +} from '@etherealengine/ecs' +import { PeerID, applyIncomingActions, dispatchAction, getState } from '@etherealengine/hyperflux' +import { + Network, + NetworkPeerFunctions, + NetworkState, + NetworkTopics, + NetworkWorldUserStateSystem +} from '@etherealengine/network' +import { createMockNetwork } from '../../../../network/tests/createMockNetwork' +import { createEngine } from '../../initializeEngine' +import { SpectateActions, SpectateEntityState } from './SpectateSystem' + +describe('SpectateSystem', async () => { + let viewerEntity = UndefinedEntity + + describe('SpectateEntityState', async () => { + beforeEach(async () => { + createEngine() + createMockNetwork() + Engine.instance.store.defaultDispatchDelay = () => 0 + viewerEntity = createEntity() + setComponent(viewerEntity, UUIDComponent, UUIDComponent.generateUUID()) + }) + + afterEach(() => { + removeEntity(viewerEntity) + return destroyEngine() + }) + + const NetworkWorldUserStateSystemReactor = SystemDefinitions.get(NetworkWorldUserStateSystem)!.reactor! + const tag = + + it('should start spectating an entity when the `spectateEntity` action is dispatched', async () => { + const hostUserId = 'world' as UserID + const userId = 'user id' as UserID + const peerID = Engine.instance.store.peerID + const peerID2 = 'peer id 2' as PeerID + const peerID3 = 'peer id 3' as PeerID + const spectatorID = 'spectator id' as UserID + + Engine.instance.userID = userId + const network: Network = NetworkState.worldNetwork + + NetworkPeerFunctions.createPeer(network, peerID, 0, hostUserId, 0) + NetworkPeerFunctions.createPeer(network, peerID2, 1, userId, 1) + NetworkPeerFunctions.createPeer(network, peerID3, 2, userId, 2) + + const { rerender, unmount } = render(tag) + await act(() => rerender(tag)) + + dispatchAction( + SpectateActions.spectateEntity({ + spectatorUserID: spectatorID, + spectatingUserID: userId, + $topic: NetworkTopics.world, + $peer: Engine.instance.store.peerID + }) + ) + applyIncomingActions() + const state = getState(SpectateEntityState)[spectatorID] + assert.notEqual(state, undefined, "The spectator's SpectateEntityState should not be undefined after `getState`") + assert.equal(state.spectating, userId, 'The spectator is not spectating the correct userID') + + unmount() + }) + + it('should stop spectating an entity when the `exitSpectate` action is dispatched', async () => { + const hostUserId = 'world' as UserID + const userId = 'user id' as UserID + const peerID = Engine.instance.store.peerID + const peerID2 = 'peer id 2' as PeerID + const peerID3 = 'peer id 3' as PeerID + const spectatorID = 'spectator id' as UserID + + Engine.instance.userID = userId + const network: Network = NetworkState.worldNetwork + + NetworkPeerFunctions.createPeer(network, peerID, 0, hostUserId, 0) + NetworkPeerFunctions.createPeer(network, peerID2, 1, userId, 1) + NetworkPeerFunctions.createPeer(network, peerID3, 2, userId, 2) + + const { rerender, unmount } = render(tag) + await act(() => rerender(tag)) + + dispatchAction( + SpectateActions.spectateEntity({ + spectatorUserID: spectatorID, + spectatingUserID: userId, + $topic: NetworkTopics.world, + $peer: Engine.instance.store.peerID + }) + ) + applyIncomingActions() + const before = getState(SpectateEntityState)[spectatorID] + assert.notEqual(before, undefined, "The spectator's SpectateEntityState should not be undefined after `getState`") + assert.equal(before.spectating, userId, 'The spectator is not spectating the correct userID') + + dispatchAction( + SpectateActions.exitSpectate({ + spectatorUserID: spectatorID, + $topic: NetworkTopics.world, + $peer: Engine.instance.store.peerID + }) + ) + applyIncomingActions() + const after = getState(SpectateEntityState)[spectatorID] + assert.equal(after, undefined, "The spectator's SpectateEntityState should be undefined after exitSpectate") + + unmount() + }) + }) +})