開始測試前,讓我們在元件中新增一些對外屬性,方便進行測試吧。( ´ ▽ ` )ノ
src\components\wrapper-physics\wrapper-physics.vue
...
<script setup lang="ts">
...
// #region Methods
defineExpose({
  ...
  bodyMap,
  bodyInfoMap,
});
// #endregion Methods
</script>
src\components\wrapper-physics\wrapper-physics-body.vue
...
<script setup lang="ts">
...
defineExpose({
  id,
  info,
});
</script>
新增第一個測試案例吧。
src\components\wrapper-physics\wrapper-physics.spec.ts
import { mount } from '@vue/test-utils';
import { test, expect } from 'vitest';
import WrapperPhysics from './wrapper-physics.vue';
test('沒有任何子元素時 bodyMap 為空', () => {
  const wrapper = mount(WrapperPhysics);
  expect(wrapper).toBeDefined();
  const bodyMap = wrapper.vm.bodyMap;
  expect(bodyMap.size).toBe(0);
})
 RERUN  src/components/wrapper-physics/wrapper-physics.spec.ts x3
 ✓ src/components/wrapper-physics/wrapper-physics.spec.ts (2)
   ✓ 沒有任何子元素時 bodyMap 為空
順利通過。
讓我們加入更多註冊物體邏輯吧。◝( •ω• )◟
src\components\wrapper-physics\wrapper-physics.spec.ts
import { mount } from '@vue/test-utils';
import { test, expect } from 'vitest';
import { h } from 'vue';
import WrapperPhysics from './wrapper-physics.vue';
import WrapperPhysicsBody from './wrapper-physics-body.vue';
test('沒有任何子元素時 bodyMap 為空', () => {
  const wrapper = mount(WrapperPhysics);
  expect(wrapper).toBeDefined();
  const bodyMap = wrapper.vm.bodyMap;
  expect(bodyMap.size).toBe(0);
})
test('內部僅有非 body 元件子元素時 bodyMap 為空', () => {
  const wrapper = mount(WrapperPhysics, {
    slots: {
      default: '<div>安安</div>',
    },
  });
  expect(wrapper).toBeDefined();
  const bodyMap = wrapper.vm.bodyMap;
  expect(bodyMap.size).toBe(0);
})
test('bodyMap 數量必須相符 body 元件數量', () => {
  const wrapper = mount(WrapperPhysics, {
    slots: {
      default: [
        h(WrapperPhysicsBody),
        h(WrapperPhysicsBody),
      ],
    },
  });
  expect(wrapper).toBeDefined();
  const bodyMap = wrapper.vm.bodyMap;
  expect(bodyMap.size).toBe(2);
})
最後加入物理模擬相關測試。
src\components\wrapper-physics\wrapper-physics.spec.ts
...
test('start 後物體會移動', async () => {
  const wrapper = mount(WrapperPhysics, {
    slots: {
      default: [
        h(WrapperPhysicsBody),
        h(WrapperPhysicsBody),
      ],
    },
  });
  // 等待 bodyInfo 更新
  await delay(50);
  const bodyInfoMap = wrapper.vm.bodyInfoMap;
  bodyInfoMap.forEach((info) => {
    expect(info.offsetX).toBe(0);
    expect(info.offsetY).toBe(0);
  })
  wrapper.vm.start();
  await delay(500);
  bodyInfoMap.forEach((info) => {
    expect(info.offsetX).not.toBe(0);
    expect(info.offsetY).not.toBe(0);
  })
})
test('reset 會清空 bodyInfoMap', async () => {
  const wrapper = mount(WrapperPhysics, {
    props: {
      immediate: true,
    },
    slots: {
      default: [
        h(WrapperPhysicsBody),
        h(WrapperPhysicsBody),
      ],
    },
  });
  const bodyInfoMap = wrapper.vm.bodyInfoMap;
  await delay(500);
  expect(bodyInfoMap.size).toBe(2);
  wrapper.vm.reset();
  expect(bodyInfoMap.size).toBe(0);
})
test('isStatic 的物體完全不會移動', async () => {
  const wrapper = mount(WrapperPhysics, {
    props: {
      immediate: true,
    },
    slots: {
      default: [
        h(WrapperPhysicsBody, { isStatic: true }),
        h(WrapperPhysicsBody),
        h(WrapperPhysicsBody, { isStatic: true }),
      ],
    },
  });
  const bodyMap = wrapper.vm.bodyMap;
  const bodyInfoMap = wrapper.vm.bodyInfoMap;
  await delay(500);
  /** 所有靜止物體的 ID */
  const staticIds = [...bodyMap.entries()]
    .filter(([id, body]) => body.isStatic)
    .map(([id]) => id);
  [...bodyInfoMap.entries()].forEach(([id, info]) => {
    if (staticIds.includes(id)) {
      expect(info.offsetX).toBe(0);
      expect(info.offsetY).toBe(0);
    } else {
      expect(info.offsetX).not.toBe(0);
      expect(info.offsetY).not.toBe(0);
    }
  })
})
順利通過!✧*。٩(ˊᗜˋ*)و✧*。
 ✓ src/components/wrapper-physics/wrapper-physics.spec.ts (6) 1739ms
   ✓ 沒有任何子元素時 bodyMap 為空
   ✓ 內部僅有非 body 元件子元素時 bodyMap 為空
   ✓ bodyMap 數量必須相符 body 元件數量
   ✓ start 後物體會移動 579ms
   ✓ reset 會清空 bodyInfoMap 516ms
   ✓ isStatic 的物體完全不會移動 505ms
 Test Files  1 passed (1)
      Tests  6 passed (6)
   Start at  23:22:48
   Duration  2.12s
以上程式碼已同步至 GitLab,大家可以前往下載: