import { fireEvent } from "@testing-library/react"; import { criarControle } from "./controle"; let alvo: HTMLDivElement; let travaDo: Element | null; beforeEach(() => { alvo = document.createElement("div"); document.body.append(alvo); travaDo = null; Object.defineProperty(document, "pointerLockElement", { configurable: true, get: () => travaDo, }); }); afterEach(() => { alvo.remove(); }); function travar() { travaDo = alvo; document.dispatchEvent(new Event("pointerlockchange")); } function mover(movementX: number, movementY: number) { const e = new MouseEvent("mousemove"); Object.defineProperty(e, "movementX", { value: movementX }); Object.defineProperty(e, "movementY", { value: movementY }); document.dispatchEvent(e); } describe("criarControle: teclado", () => { it("W e as setas movem; teclas opostas se anulam", () => { const c = criarControle(alvo); fireEvent.keyDown(window, { code: "KeyW" }); fireEvent.keyDown(window, { code: "ArrowRight" }); expect(c.ler().mover).toEqual([1, 1]); fireEvent.keyDown(window, { code: "KeyS" }); fireEvent.keyDown(window, { code: "KeyA" }); expect(c.ler().mover).toEqual([0, 0]); fireEvent.keyUp(window, { code: "KeyS" }); fireEvent.keyUp(window, { code: "KeyA" }); expect(c.ler().mover).toEqual([1, 1]); c.descartar(); }); it("soma o direcional de toque ao teclado", () => { const c = criarControle(alvo); c.definirMover(-1, 0.5); fireEvent.keyDown(window, { code: "KeyD" }); expect(c.ler().mover).toEqual([0, 0.5]); c.descartar(); }); it("1, 2 e 3 escolhem a arma", () => { const c = criarControle(alvo); expect(c.ler().arma).toBe(0); fireEvent.keyDown(window, { code: "Digit3" }); expect(c.ler().arma).toBe(2); fireEvent.keyDown(window, { code: "Digit2" }); expect(c.ler().arma).toBe(1); fireEvent.keyDown(window, { code: "Digit1" }); expect(c.ler().arma).toBe(0); c.descartar(); }); it("a roda troca para a próxima e para a anterior, em volta", () => { const c = criarControle(alvo); fireEvent.wheel(window, { deltaY: 100 }); expect(c.ler().arma).toBe(1); fireEvent.wheel(window, { deltaY: -100 }); fireEvent.wheel(window, { deltaY: -100 }); expect(c.ler().arma).toBe(2); fireEvent.wheel(window, { deltaY: 100 }); expect(c.ler().arma).toBe(0); c.descartar(); }); it("perder o foco da janela solta as teclas", () => { const c = criarControle(alvo); fireEvent.keyDown(window, { code: "KeyW" }); fireEvent.blur(window); expect(c.ler().mover).toEqual([0, 0]); c.descartar(); }); it("ignora tecla com o foco num campo de texto ou botão", () => { const c = criarControle(alvo); const campo = document.createElement("input"); const botao = document.createElement("button"); document.body.append(campo, botao); fireEvent.keyDown(campo, { code: "KeyW" }); fireEvent.keyDown(botao, { code: "Digit3" }); const entrada = c.ler(); expect(entrada.mover).toEqual([0, 0]); expect(entrada.arma).toBe(0); campo.remove(); botao.remove(); c.descartar(); }); }); describe("criarControle: mouse", () => { it("clicar no alvo pede a trava do ponteiro", () => { alvo.requestPointerLock = jest.fn(); const c = criarControle(alvo); fireEvent.click(alvo); expect(alvo.requestPointerLock).toHaveBeenCalledTimes(1); c.descartar(); }); it("clicar não falha onde não há trava do ponteiro", () => { const c = criarControle(alvo); expect(() => fireEvent.click(alvo)).not.toThrow(); c.descartar(); }); it("avisa quando a trava muda", () => { const aoMudarTrava = jest.fn(); const c = criarControle(alvo, { aoMudarTrava }); travar(); travaDo = null; document.dispatchEvent(new Event("pointerlockchange")); expect(aoMudarTrava.mock.calls).toEqual([[true], [false]]); c.descartar(); }); it("sem a trava, o mouse não gira nem atira", () => { const c = criarControle(alvo); mover(100, 100); fireEvent.mouseDown(document, { button: 0 }); const entrada = c.ler(); expect(entrada.olhar).toEqual([0, 0]); expect(entrada.atirar).toBe(false); c.descartar(); }); it("com a trava, soma o giro e o ler o zera", () => { const c = criarControle(alvo); travar(); mover(100, -50); mover(100, 0); const [rumo, inclinacao] = c.ler().olhar; expect(rumo).toBeCloseTo(-200 * 0.0022); expect(inclinacao).toBeCloseTo(50 * 0.0022); expect(c.ler().olhar).toEqual([0, 0]); c.descartar(); }); it("botão esquerdo pressionado atira; outro botão e soltar não", () => { const c = criarControle(alvo); travar(); fireEvent.mouseDown(document, { button: 2 }); expect(c.ler().atirar).toBe(false); fireEvent.mouseDown(document, { button: 0 }); expect(c.ler().atirar).toBe(true); fireEvent.mouseUp(document, { button: 2 }); expect(c.ler().atirar).toBe(true); fireEvent.mouseUp(document, { button: 0 }); expect(c.ler().atirar).toBe(false); c.descartar(); }); it("clique inteiro entre duas leituras ainda dá um tiro, e só um", () => { const c = criarControle(alvo); travar(); fireEvent.mouseDown(document, { button: 0 }); fireEvent.mouseUp(document, { button: 0 }); expect(c.ler().atirar).toBe(true); expect(c.ler().atirar).toBe(false); c.descartar(); }); }); describe("criarControle: toque, soltar e descartar", () => { it("junta o giro, o tiro e a arma vindos do toque", () => { const c = criarControle(alvo); c.somarOlhar(0.1, 0.2); c.somarOlhar(0.1, 0); c.definirAtirar(true); c.definirArma(2); expect(c.ler()).toEqual({ mover: [0, 0], olhar: [0.2, 0.2], atirar: true, arma: 2, }); c.descartar(); }); it("toque no botão de tiro entre duas leituras ainda dá um tiro, e só um", () => { const c = criarControle(alvo); c.definirAtirar(true); c.definirAtirar(false); expect(c.ler().atirar).toBe(true); expect(c.ler().atirar).toBe(false); c.descartar(); }); it("soltar zera movimento, tiro e giro, e mantém a arma", () => { const c = criarControle(alvo); travar(); fireEvent.keyDown(window, { code: "KeyW" }); fireEvent.mouseDown(document, { button: 0 }); c.definirMover(1, 1); c.definirAtirar(true); c.somarOlhar(1, 1); c.definirArma(1); c.soltar(); expect(c.ler()).toEqual({ mover: [0, 0], olhar: [0, 0], atirar: false, arma: 1, }); c.descartar(); }); it("descartar remove todos os ouvintes", () => { alvo.requestPointerLock = jest.fn(); const aoMudarTrava = jest.fn(); const c = criarControle(alvo, { aoMudarTrava }); travar(); aoMudarTrava.mockClear(); c.descartar(); fireEvent.keyDown(window, { code: "KeyW" }); fireEvent.keyDown(window, { code: "Digit3" }); fireEvent.wheel(window, { deltaY: 100 }); fireEvent.mouseDown(document, { button: 0 }); mover(100, 100); fireEvent.click(alvo); document.dispatchEvent(new Event("pointerlockchange")); expect(c.ler()).toEqual({ mover: [0, 0], olhar: [0, 0], atirar: false, arma: 0, }); expect(alvo.requestPointerLock).not.toHaveBeenCalled(); expect(aoMudarTrava).not.toHaveBeenCalled(); }); });