diablo2-web/tests/challenger-m2-stress.test.ts

169 lines
5.7 KiB
TypeScript

/**
* Adversarial Challenger Stress Testing Suite for Milestone 2 Left-Click & Shift Controls
* (Solution Stress Testing Playbook: Differential Fuzzing, Boundary Values, Invariant Checks)
*/
import { describe, expect, it } from 'vitest'
import { HudManager } from '../src/client/ui/hud-manager.ts'
import { GameEngine, type GameEngineOptions, type WorldMapProvider } from '../src/server/engine/engine.ts'
import { WaypointNetwork } from '../src/common/world/portal.ts'
import { screenToWorld, facingOf } from '../src/common/world/scene-links-nav.ts'
import { getSkillRange, getSkillMana } from '../src/common/skills/weapon-combat-meta.ts'
class MockWindow extends EventTarget {
innerWidth = 1920
innerHeight = 1080
devicePixelRatio = 1
}
class MockCanvas extends EventTarget {
clientWidth = 1920
clientHeight = 1080
width = 1920
height = 1080
style = { pointerEvents: 'none' }
rect = { left: 0, top: 0, width: 1920, height: 1080 }
getBoundingClientRect() {
return this.rect
}
getContext() {
return null
}
}
class SyntheticKeyboardEvent extends Event {
readonly code: string
readonly key: string
constructor(type: string, init: { code: string; key: string }) {
super(type, { cancelable: true })
this.code = init.code
this.key = init.key
}
}
class SyntheticPointerEvent extends Event {
readonly clientX: number
readonly clientY: number
readonly button: number
readonly buttons: number
readonly shiftKey: boolean
private _defaultPrevented = false
constructor(type: string, init: { clientX: number; clientY: number; button?: number; buttons?: number; shiftKey?: boolean; cancelable?: boolean }) {
super(type, { cancelable: init.cancelable ?? true })
this.clientX = init.clientX
this.clientY = init.clientY
this.button = init.button ?? 0
this.buttons = init.buttons !== undefined ? init.buttons : (1 << (init.button ?? 0))
this.shiftKey = init.shiftKey ?? false
}
override get defaultPrevented(): boolean {
return this._defaultPrevented
}
override preventDefault() {
super.preventDefault()
this._defaultPrevented = true
}
}
const mockWin = new MockWindow()
// @ts-ignore
globalThis.window = mockWin
// @ts-ignore
globalThis.HTMLInputElement = class HTMLInputElement {}
// @ts-ignore
globalThis.HTMLSelectElement = class HTMLSelectElement {}
describe('Challenger Stress Tests: Milestone 2 Left-Click & Shift Controls', () => {
describe('1. screenToWorld projection with varying camera zoom levels and DPRs', () => {
it('exact round-trip invertibility across zoom levels [0.4x..8.0x] and DPRs [1.0..4.0]', () => {
const zooms = [0.4, 0.5, 0.75, 1.0, 1.25, 1.5, 2.0, 3.0, 4.0, 6.0, 8.0]
const dprs = [1.0, 1.25, 1.333, 1.5, 2.0, 2.5, 3.0, 4.0]
const viewports = [
{ width: 800, height: 600, left: 0, top: 0 },
{ width: 1280, height: 720, left: 10, top: 20 },
{ width: 1920, height: 1080, left: 100, top: 50 },
{ width: 2560, height: 1440, left: 0, top: 0 },
{ width: 3440, height: 1440, left: 50, top: 0 }, // ultrawide
]
let testIterations = 0
for (const zoom of zooms) {
for (const dpr of dprs) {
for (const vp of viewports) {
const rect = { left: vp.left, top: vp.top, width: vp.width, height: vp.height }
const canvas = {
width: Math.round(vp.width * dpr),
height: Math.round(vp.height * dpr),
}
const camX = 1500
const camY = 1200
// Test 20 random points per configuration
for (let i = 0; i < 20; i++) {
testIterations++
const originalWorldX = 500 + (i * 97) % 2000
const originalWorldY = 400 + (i * 113) % 2000
// Forward projection to screen client coordinates:
const scaleX = canvas.width > 0 ? rect.width / canvas.width : 1
const scaleY = canvas.height > 0 ? rect.height / canvas.height : 1
const clientX = rect.left + rect.width * 0.5 + (originalWorldX - camX) * zoom * scaleX
const clientY = rect.top + rect.height * 0.5 + (originalWorldY - camY) * zoom * scaleY
// Invert back via screenToWorld:
const unprojected = screenToWorld({
clientX,
clientY,
rect,
canvas,
camX,
camY,
cameraZoom: zoom,
})
expect(Math.abs(unprojected.x - originalWorldX)).toBeLessThan(1e-4)
expect(Math.abs(unprojected.y - originalWorldY)).toBeLessThan(1e-4)
}
}
}
}
expect(testIterations).toBe(zooms.length * dprs.length * viewports.length * 20)
})
it('center of viewport strictly projects to camera focal position regardless of zoom or DPR', () => {
const dprs = [1.0, 1.5, 2.0, 3.0]
const zooms = [0.4, 1.0, 2.5, 8.0]
for (const dpr of dprs) {
for (const zoom of zooms) {
const rect = { left: 80, top: 40, width: 1600, height: 900 }
const canvas = { width: Math.round(1600 * dpr), height: Math.round(900 * dpr) }
const camX = 777.5
const camY = 888.25
const centerClientX = rect.left + rect.width * 0.5 // 80 + 800 = 880
const centerClientY = rect.top + rect.height * 0.5 // 40 + 450 = 490
const world = screenToWorld({
clientX: centerClientX,
clientY: centerClientY,
rect,
canvas,
camX,
camY,
cameraZoom: zoom,
})
expect(world.x).toBeCloseTo(camX, 5)
expect(world.y).toBeCloseTo(camY, 5)
}
}
})
})
})