126 lines
4.2 KiB
TypeScript
126 lines
4.2 KiB
TypeScript
import { readFileSync } from 'node:fs'
|
|
import { resolve } from 'node:path'
|
|
import { afterEach, describe, expect, it, vi } from 'vitest'
|
|
import { MISSILE_METAS } from '../src/render/missiles-meta.ts'
|
|
import type { SpriteRenderer } from '../src/render/renderer.ts'
|
|
import { loadItemsAtlas } from '../src/scene/frontend/ground-item-renderer.ts'
|
|
import {
|
|
drawExplosion,
|
|
drawMissileProjectile,
|
|
loadMissileArtMap,
|
|
loadOverlayArtMap,
|
|
} from '../src/scene/frontend/missile-overlay-renderer.ts'
|
|
|
|
describe('P0 #530: Missile Art Fail-Fast & Removal of Procedural drawSolid / fireexplode Fallbacks', () => {
|
|
afterEach(() => {
|
|
vi.unstubAllGlobals()
|
|
})
|
|
|
|
it('includes canonical MISSILE_METAS alias keys for frozenorbbolt, frozenorbnova, icestormimpactvar02, and hydra_turret', () => {
|
|
expect(MISSILE_METAS.frozenorbbolt).toBeDefined()
|
|
expect(MISSILE_METAS.frozenorbbolt?.name).toBe('icebolt')
|
|
expect(MISSILE_METAS.frozenorbnova).toBeDefined()
|
|
expect(MISSILE_METAS.frozenorbnova?.name).toBe('icebolt')
|
|
expect(MISSILE_METAS.icestormimpactvar02).toBeDefined()
|
|
expect(MISSILE_METAS.icestormimpactvar02?.name).toBe('blizzardexplode3')
|
|
expect(MISSILE_METAS.hydra_turret).toBeDefined()
|
|
expect(MISSILE_METAS.hydra_turret?.name).toBe('hydra')
|
|
})
|
|
|
|
it('fails fast with a descriptive Error in loadMissileArtMap when fetch returns HTTP 404', async () => {
|
|
const errorSpy = vi.spyOn(console, 'error').mockImplementation(() => {})
|
|
vi.stubGlobal(
|
|
'fetch',
|
|
vi.fn(async () => ({
|
|
ok: false,
|
|
status: 404,
|
|
})),
|
|
)
|
|
vi.stubGlobal('createImageBitmap', vi.fn())
|
|
|
|
const dummyRenderer = { addAtlas: vi.fn() } as unknown as SpriteRenderer
|
|
await expect(loadMissileArtMap(dummyRenderer, '/packs')).rejects.toThrow(
|
|
/\[loadMissileArtMap\] Failed to load .*HTTP 404/,
|
|
)
|
|
expect(errorSpy).toHaveBeenCalled()
|
|
errorSpy.mockRestore()
|
|
})
|
|
|
|
it('fails fast with a descriptive Error in loadOverlayArtMap when fetch returns HTTP 500', async () => {
|
|
const errorSpy = vi.spyOn(console, 'error').mockImplementation(() => {})
|
|
vi.stubGlobal(
|
|
'fetch',
|
|
vi.fn(async () => ({
|
|
ok: false,
|
|
status: 500,
|
|
})),
|
|
)
|
|
vi.stubGlobal('createImageBitmap', vi.fn())
|
|
|
|
const dummyRenderer = { addAtlas: vi.fn() } as unknown as SpriteRenderer
|
|
await expect(loadOverlayArtMap(dummyRenderer, '/packs')).rejects.toThrow(
|
|
/\[loadOverlayArtMap\] Failed to load .*HTTP 500/,
|
|
)
|
|
expect(errorSpy).toHaveBeenCalled()
|
|
errorSpy.mockRestore()
|
|
})
|
|
|
|
it('fails fast with a descriptive Error in loadItemsAtlas when all candidate URLs return HTTP 404', async () => {
|
|
const errorSpy = vi.spyOn(console, 'error').mockImplementation(() => {})
|
|
vi.stubGlobal(
|
|
'fetch',
|
|
vi.fn(async () => ({
|
|
ok: false,
|
|
status: 404,
|
|
})),
|
|
)
|
|
vi.stubGlobal('createImageBitmap', vi.fn())
|
|
|
|
const dummyRenderer = { addAtlas: vi.fn() } as unknown as SpriteRenderer
|
|
await expect(loadItemsAtlas(dummyRenderer, '/packs')).rejects.toThrow(
|
|
/\[loadItemsAtlas\] Failed to load items-atlas\.png from all candidates/,
|
|
)
|
|
expect(errorSpy).toHaveBeenCalled()
|
|
errorSpy.mockRestore()
|
|
})
|
|
|
|
it('never calls renderer.drawSolid in drawMissileProjectile or drawExplosion when art is undefined', () => {
|
|
const drawSolid = vi.fn()
|
|
const draw = vi.fn()
|
|
const mockRenderer = { draw, drawSolid } as unknown as SpriteRenderer
|
|
|
|
drawMissileProjectile(mockRenderer, {
|
|
skillId: '36',
|
|
missileType: 'firebolt',
|
|
x: 100,
|
|
y: 100,
|
|
vx: 5,
|
|
vy: 5,
|
|
damage: 10,
|
|
ttl: 20,
|
|
fromPlayer: true,
|
|
})
|
|
|
|
drawExplosion(mockRenderer, {
|
|
x: 120,
|
|
y: 120,
|
|
radius: 40,
|
|
frame: 2,
|
|
maxFrames: 16,
|
|
missileType: 'iceexplode',
|
|
})
|
|
|
|
expect(draw).not.toHaveBeenCalled()
|
|
expect(drawSolid).not.toHaveBeenCalled()
|
|
})
|
|
|
|
it('contains no borrowed fireexplode fallback or 8x8 yellow drawSolid in world-renderer.ts', () => {
|
|
const worldRendererSrc = readFileSync(
|
|
resolve(process.cwd(), 'src/scene/frontend/world-renderer.ts'),
|
|
'utf8',
|
|
)
|
|
expect(worldRendererSrc).not.toContain("missileArtMap.get('fireexplode')")
|
|
expect(worldRendererSrc).not.toContain('[0.9, 0.9, 0.2, 1]')
|
|
})
|
|
})
|