4 changed files with 162 additions and 1 deletions
@ -0,0 +1,109 @@ |
|||||
|
import { beforeEach, describe, expect, it, vi } from "vitest"; |
||||
|
import { SceneType } from "@/enums/SceneType"; |
||||
|
import type { IBaseScene } from "@/scene/types"; |
||||
|
|
||||
|
class MockContainer { |
||||
|
visible = true; |
||||
|
label = ""; |
||||
|
_isHolderLast?: boolean; |
||||
|
children: MockContainer[] = []; |
||||
|
destroyed = false; |
||||
|
|
||||
|
addChild(child: MockContainer): MockContainer { |
||||
|
this.children.push(child); |
||||
|
return child; |
||||
|
} |
||||
|
|
||||
|
removeChild(child: MockContainer): MockContainer { |
||||
|
this.children = this.children.filter((item) => item !== child); |
||||
|
return child; |
||||
|
} |
||||
|
|
||||
|
destroy(): void { |
||||
|
this.destroyed = true; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
vi.mock("pixi.js", () => ({ |
||||
|
Container: MockContainer, |
||||
|
})); |
||||
|
|
||||
|
vi.mock("@/core/Game", () => { |
||||
|
class MockGame { |
||||
|
private static instance: MockGame; |
||||
|
readonly stage = new MockContainer(); |
||||
|
|
||||
|
static getInstance(): MockGame { |
||||
|
if (!MockGame.instance) { |
||||
|
MockGame.instance = new MockGame(); |
||||
|
} |
||||
|
return MockGame.instance; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return { |
||||
|
default: MockGame, |
||||
|
}; |
||||
|
}); |
||||
|
|
||||
|
function createScene( |
||||
|
name: string, |
||||
|
type: SceneType = SceneType.Normal, |
||||
|
onLoad?: () => Promise<void> | void |
||||
|
): IBaseScene { |
||||
|
const stage = new MockContainer(); |
||||
|
stage.label = name; |
||||
|
|
||||
|
return { |
||||
|
name, |
||||
|
type, |
||||
|
stage: stage as never, |
||||
|
_assetsLoaded: true, |
||||
|
_layoutDone: true, |
||||
|
changeScene: () => {}, |
||||
|
onLoad, |
||||
|
onUnLoad: async () => {}, |
||||
|
loadBundle: async () => {}, |
||||
|
unLoadBundle: async () => {}, |
||||
|
layout: async () => {}, |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
describe("SceneManager rollback consistency", () => { |
||||
|
beforeEach(async () => { |
||||
|
vi.resetModules(); |
||||
|
const module = await import("@/scene/SceneManager"); |
||||
|
(module.default as unknown as { instance?: unknown }).instance = undefined; |
||||
|
}); |
||||
|
|
||||
|
it("restores previous scene visibility and mount when transition fails", async () => { |
||||
|
const module = await import("@/scene/SceneManager"); |
||||
|
const SceneManager = module.default; |
||||
|
|
||||
|
const manager = SceneManager.getInstance(); |
||||
|
const previous = createScene("previous"); |
||||
|
const target = createScene("target", SceneType.Normal, async () => { |
||||
|
throw new Error("target onLoad failed"); |
||||
|
}); |
||||
|
|
||||
|
manager.registerScene(previous); |
||||
|
manager.registerScene(target); |
||||
|
await manager.initScene("previous"); |
||||
|
|
||||
|
expect(previous.stage.visible).toBe(true); |
||||
|
expect(manager.currentScene).toBe(previous); |
||||
|
|
||||
|
await expect( |
||||
|
manager.changeScene("target", { isHolderLast: true }) |
||||
|
).rejects.toThrowError("target onLoad failed"); |
||||
|
|
||||
|
expect(manager.currentScene).toBe(previous); |
||||
|
expect(previous.stage.visible).toBe(true); |
||||
|
expect(manager["game"].stage.children.includes(previous.stage as never)).toBe( |
||||
|
true |
||||
|
); |
||||
|
expect(manager["game"].stage.children.includes(target.stage as never)).toBe( |
||||
|
false |
||||
|
); |
||||
|
}); |
||||
|
}); |
||||
Loading…
Reference in new issue