/** * Lightweight `cc` module mock used by Jest to allow platform-agnostic * modules (`common/*`, `data/*`, some `logic/*`) to be unit-tested without * pulling in the full Cocos Creator engine. * * Anything that actually touches `cc.Node` / scene graph must **not** live * in a module that is directly tested by Jest; put it behind a thin facade * instead (see `common/StorageMgr.ts` which isolates `sys.localStorage`). */ class FakeEventTarget { private listeners = new Map void>>(); on(event: string, cb: (...args: unknown[]) => void): void { const list = this.listeners.get(event) ?? []; list.push(cb); this.listeners.set(event, list); } off(event: string, cb?: (...args: unknown[]) => void): void { if (!cb) { this.listeners.delete(event); return; } const list = this.listeners.get(event); if (!list) return; this.listeners.set( event, list.filter((fn) => fn !== cb) ); } emit(event: string, ...args: unknown[]): void { const list = this.listeners.get(event); if (!list) return; for (const fn of list) { fn(...args); } } } export const EventTarget = FakeEventTarget; export const _decorator = { ccclass: (_name?: string) => (target: unknown) => target as any, property: (_opts?: unknown) => (_target: unknown, _key: string) => {}, }; /** * Minimal stand-in for `cc.Node`. Only the surface actually exercised by * Scene Entry components is modelled so unit tests stay deterministic. */ export class Node { public name: string = ''; public active: boolean = true; public layer: number = 0; public isValid: boolean = true; public scale: Vec3 = new Vec3(1, 1, 1); constructor(name?: string) { this.name = name ?? ''; } public addChild(_child: Node): void {} public removeFromParent(): void {} public destroy(): void { this.isValid = false; } public on(_ev: string, _cb: (...args: unknown[]) => void, _target?: unknown): void {} public off(_ev: string, _cb?: (...args: unknown[]) => void): void {} public getComponent(_ctor: new (...args: unknown[]) => T): T | null { return null; } public addComponent(_ctor: new (...args: unknown[]) => T): T { return {} as T; } public setPosition(..._args: unknown[]): void {} public setScale(..._args: unknown[]): void {} public static EventType = { TOUCH_START: 'touch-start', TOUCH_MOVE: 'touch-move', TOUCH_END: 'touch-end', TOUCH_CANCEL: 'touch-cancel', }; } /** * Minimal stand-in for `cc.Component`. Provides `.node` so Scene Entry TS * types resolve. Runtime behaviour is injected by the Cocos Creator editor. */ export class Component { public node: Node = new Node(); public enabled: boolean = true; } export const director = { addPersistRootNode: (..._args: unknown[]) => {}, loadScene: (..._args: unknown[]) => {}, }; export const view = { setDesignResolutionSize: (..._args: unknown[]) => {}, }; export const screen = { orientation: 0, }; export const game = { frameRate: 60, }; export const sys = { localStorage: { _store: new Map(), getItem(key: string): string | null { return (sys.localStorage as any)._store.get(key) ?? null; }, setItem(key: string, value: string): void { (sys.localStorage as any)._store.set(key, value); }, removeItem(key: string): void { (sys.localStorage as any)._store.delete(key); }, clear(): void { (sys.localStorage as any)._store.clear(); }, }, }; export const settings = {}; export const Settings = {}; export const profiler = {}; // --------------------------------------------------------------------- // Additional stubs for types imported by Scene Entry components. All the // methods below are NO-OPs; the Cocos Creator editor injects the real // implementations at runtime. Having them declared here keeps the TS // language server happy and lets Jest exercise scene-entry unit tests. // --------------------------------------------------------------------- export class Color { public r: number; public g: number; public b: number; public a: number; constructor(r: number = 255, g: number = 255, b: number = 255, a: number = 255) { this.r = r; this.g = g; this.b = b; this.a = a; } public static WHITE = new Color(255, 255, 255, 255); public static BLACK = new Color(0, 0, 0, 255); } export class Vec3 { public x: number; public y: number; public z: number; constructor(x: number = 0, y: number = 0, z: number = 0) { this.x = x; this.y = y; this.z = z; } } export class UITransform { public width: number = 0; public height: number = 0; public setContentSize(_w: number, _h: number): void {} } export class Label { public string: string = ''; public fontSize: number = 24; public lineHeight: number = 28; public color: Color = Color.WHITE; public horizontalAlign: number = 0; public verticalAlign: number = 0; public useSystemFont: boolean = true; public enableWrapText: boolean = true; } export class Button { public transition: number = 0; public target: Node | null = null; public zoomScale: number = 1; public static Transition = { NONE: 0, COLOR: 1, SPRITE: 2, SCALE: 3 }; } export class Graphics { public fillColor: Color = Color.WHITE; public strokeColor: Color = Color.BLACK; public lineWidth: number = 1; public rect(_x: number, _y: number, _w: number, _h: number): void {} public fill(): void {} public stroke(): void {} } export class Sprite { public spriteFrame: unknown = null; public type: number = 0; public sizeMode: number = 0; public color: Color = Color.WHITE; public static Type = { SIMPLE: 0, SLICED: 1, TILED: 2, FILLED: 3 }; public static SizeMode = { CUSTOM: 0, TRIMMED: 1, RAW: 2 }; } export class Rect { public x: number; public y: number; public width: number; public height: number; constructor(x: number = 0, y: number = 0, width: number = 0, height: number = 0) { this.x = x; this.y = y; this.width = width; this.height = height; } } export class Size { public width: number; public height: number; constructor(width: number = 0, height: number = 0) { this.width = width; this.height = height; } } export class SpriteFrame { public texture: unknown = null; public rect: Rect = new Rect(); public originalSize: Size = new Size(); } export class Texture2D { public image: unknown = null; public width: number = 0; public height: number = 0; public static PixelFormat = { RGBA8888: 35 }; } export class ImageAsset { constructor(_opts?: unknown) {} } export class JsonAsset { public json: unknown = null; } export class Canvas {} export class EventTouch {} /** Global input event source (Cocos Creator 3.8). */ export const input = { on: (_ev: string, _cb: (...args: unknown[]) => void, _target?: unknown): void => {}, off: (_ev: string, _cb?: (...args: unknown[]) => void, _target?: unknown): void => {}, }; export const Input = { EventType: { TOUCH_START: 'touch-start', TOUCH_MOVE: 'touch-move', TOUCH_END: 'touch-end', TOUCH_CANCEL: 'touch-cancel', }, }; export const resources = { load: (_path: string, _type: unknown, _cb: (err: Error | null, asset: unknown) => void): void => {}, };