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()
+ })
+ })
+})