瀏覽代碼

feat(core): useCamera unit tests

alvarosabu 2 年之前
父節點
當前提交
9889d56e81

+ 4 - 0
packages/tres/src/App.vue

@@ -1,8 +1,12 @@
 <script setup lang="ts">
 import { useTweakPane } from '@tresjs/cientos'
 import TheEvents from '/@/components/TheEvents.vue'
+import { CameraType, useCamera } from '.'
 
 useTweakPane()
+const { createCamera, activeCamera } = useCamera()
+createCamera(CameraType.Orthographic)
+console.log('activeCamera', activeCamera)
 </script>
 
 <template>

+ 10 - 6
packages/tres/src/core/useCamera/index.ts

@@ -32,8 +32,9 @@ export interface OrthographicCameraOptions {
 interface UseCameraReturn {
   activeCamera: ComputedRef<Camera>
   createCamera: (cameraType?: CameraType, options?: PerspectiveCameraOptions | OrthographicCameraOptions) => Camera
-  updateCamera: () => void
+  updateCurrentCamera: () => void
   pushCamera: (camera: Camera) => void
+  clearCameras: () => void
 }
 
 const state: CameraState = {
@@ -72,14 +73,12 @@ export function useCamera(): UseCameraReturn {
       camera = new OrthographicCamera(left, right, top, bottom, near, far)
       state.cameras.push(camera as OrthographicCamera)
     }
-
-    state.cameras.push(camera)
     return camera
   }
 
   const activeCamera = computed(() => state.cameras[0])
 
-  function updateCamera() {
+  function updateCurrentCamera() {
     if (activeCamera.value instanceof PerspectiveCamera && aspectRatio) {
       activeCamera.value.aspect = aspectRatio.value
     }
@@ -99,13 +98,18 @@ export function useCamera(): UseCameraReturn {
     camera.updateProjectionMatrix()
   }
 
+  function clearCameras() {
+    state.cameras = []
+  }
+
   if (aspectRatio) {
-    watch(aspectRatio, updateCamera)
+    watch(aspectRatio, updateCurrentCamera)
   }
   return {
     activeCamera,
     createCamera,
-    updateCamera,
+    updateCurrentCamera,
     pushCamera,
+    clearCameras,
   }
 }

+ 104 - 0
packages/tres/src/core/useCamera/useCamera.test.ts

@@ -0,0 +1,104 @@
+import { computed, provide } from 'vue'
+import { OrthographicCamera, PerspectiveCamera } from 'three'
+import { describe, test, expect, beforeEach, vi, afterEach } from 'vitest'
+import { withSetup } from '/@/utils/test-utils'
+import { CameraType, useCamera } from '.'
+
+const [composable, app] = withSetup(() => useCamera())
+const aspectRatio = computed(() => 1)
+app.provide('aspect-ratio', aspectRatio)
+
+describe('useCamera', () => {
+  beforeEach(() => {
+    const { clearCameras } = composable
+  })
+  afterEach(() => {
+    composable.clearCameras()
+    app.unmount()
+  })
+  describe('createCamera', () => {
+    test('should create a camera', () => {
+      const { createCamera } = composable
+      const camera = createCamera(CameraType.Perspective)
+      expect(camera).toBeDefined()
+    })
+    test('should create a perspective camera', () => {
+      const { createCamera } = composable
+      const camera = createCamera(CameraType.Perspective)
+      expect(camera.type).toBe('PerspectiveCamera')
+    })
+    test('should create a perspective camera with default options', () => {
+      const { createCamera } = composable
+      const camera = createCamera(CameraType.Perspective)
+      expect((camera as PerspectiveCamera).fov).toBe(45)
+      expect((camera as PerspectiveCamera).near).toBe(0.1)
+      expect((camera as PerspectiveCamera).far).toBe(1000)
+    })
+    test('should create a perspective camera with custom options', () => {
+      const { createCamera } = composable
+      const camera = createCamera(CameraType.Perspective, {
+        fov: 60,
+        near: 1,
+        far: 100,
+      })
+      expect((camera as PerspectiveCamera).fov).toBe(60)
+      expect((camera as PerspectiveCamera).near).toBe(1)
+      expect((camera as PerspectiveCamera).far).toBe(100)
+    })
+    test('should create an orthographic camera', () => {
+      const { createCamera } = composable
+      const camera = createCamera(CameraType.Orthographic)
+      expect(camera.type).toBe('OrthographicCamera')
+    })
+    test('should create an orthographic camera with default options', () => {
+      const { createCamera } = composable
+      const camera = createCamera(CameraType.Orthographic)
+      expect((camera as OrthographicCamera).near).toBe(0.1)
+      expect((camera as OrthographicCamera).far).toBe(1000)
+      expect((camera as OrthographicCamera).left).toBe(-100)
+      expect((camera as OrthographicCamera).right).toBe(100)
+      expect((camera as OrthographicCamera).top).toBe(100)
+      expect((camera as OrthographicCamera).bottom).toBe(-100)
+    })
+
+    test('should create an orthographic camera with custom options', () => {
+      const { createCamera } = composable
+      const camera = createCamera(CameraType.Orthographic, {
+        near: 1,
+        far: 100,
+        left: -50,
+        right: 50,
+        top: 50,
+        bottom: -50,
+      })
+      expect((camera as OrthographicCamera).near).toBe(1)
+      expect((camera as OrthographicCamera).far).toBe(100)
+      expect((camera as OrthographicCamera).left).toBe(-50)
+      expect((camera as OrthographicCamera).right).toBe(50)
+      expect((camera as OrthographicCamera).top).toBe(50)
+      expect((camera as OrthographicCamera).bottom).toBe(-50)
+    })
+  })
+  describe('activeCamera', () => {
+    test('should return the first camera', () => {
+      const { createCamera, activeCamera } = composable
+      createCamera(CameraType.Perspective)
+      expect(activeCamera.value.type).toBe('PerspectiveCamera')
+    })
+    test('should return the first camera if used more than once', () => {
+      const { createCamera, activeCamera, state } = composable
+      createCamera(CameraType.Orthographic)
+      createCamera(CameraType.Perspective)
+      expect(activeCamera.value.type).toBe('OrthographicCamera')
+    })
+  })
+  describe('updateCurrentCamera', () => {
+    test('should update the current camera with aspect ratio change', () => {
+      const { activeCamera, createCamera, updateCurrentCamera } = composable
+      createCamera(CameraType.Perspective)
+      const updateProjectionMatrix = vi.spyOn(activeCamera.value, 'updateProjectionMatrix')
+      updateCurrentCamera()
+      expect(updateProjectionMatrix).toHaveBeenCalled()
+    })
+  })
+})

+ 16 - 0
packages/tres/src/utils/test-utils.ts

@@ -0,0 +1,16 @@
+import { createApp } from 'vue'
+
+export function withSetup(composable) {
+  let result
+  const app = createApp({
+    setup() {
+      result = composable()
+      // suppress missing template warning
+      return () => {}
+    },
+  })
+  app.mount(document.createElement('div'))
+  // return the result and the app instance
+  // for testing provide / unmount
+  return [result, app]
+}

+ 6 - 2
packages/tres/tsconfig.node.json

@@ -1,9 +1,13 @@
 {
   "compilerOptions": {
+    "baseUrl": ".",
     "composite": true,
     "module": "ESNext",
     "moduleResolution": "Node",
-    "allowSyntheticDefaultImports": true
+    "allowSyntheticDefaultImports": true,
+    "paths": {
+      "/@/*": ["src/*"]
+    }
   },
-  "include": ["vite.config.ts"]
+  "include": ["vite.config.ts", "src/**/*.test.ts"]
 }

+ 3 - 0
packages/tres/vite.config.ts

@@ -43,6 +43,9 @@ export default defineConfig({
     environment: 'happy-dom',
     globals: true,
     threads: false,
+    alias: {
+      '/@': resolve(__dirname, './src'),
+    },
   },
   build: {
     lib: {