From ff22793f7c727c82b3cdbbf953788d9b511ad45d Mon Sep 17 00:00:00 2001
From: Kipstz <140314732+Kipstz@users.noreply.github.com>
Date: Tue, 5 Aug 2025 04:48:11 +0200
Subject: [PATCH] Add auto-upgrade buildings feature with middle mouse click
(#1597)
## Description:
This PR implements a new feature allowing automatic upgrade of the
nearest building using the middle mouse button. This feature greatly
simplifies the upgrade process that previously required a right-click +
building recreation.
## Please complete the following:
- [x] I have added screenshots for all UI updates
- [x] I process any text displayed to the user through translateText()
and I've added it to the en.json file
- [x] I have added relevant tests to the test directory
- [x] I confirm I have thoroughly tested these changes and take full
responsibility for any bugs introduced
- [x] I have read and accepted the CLA agreement (only required once).
## Please put your Discord username so you can be contacted if a bug or
regression is found:
Kipstzz
---------
Co-authored-by: Scott Anderson <662325+scottanderson@users.noreply.github.com>
---
resources/lang/en.json | 1 +
src/client/ClientGameRunner.ts | 73 ++++++
src/client/HelpModal.ts | 8 +
src/client/InputHandler.ts | 23 ++
tests/AutoUpgrade.test.ts | 155 +++++++++++++
tests/InputHandler.test.ts | 400 +++++++++++++++++++++++++++++++++
6 files changed, 660 insertions(+)
create mode 100644 tests/AutoUpgrade.test.ts
create mode 100644 tests/InputHandler.test.ts
diff --git a/resources/lang/en.json b/resources/lang/en.json
index df37a229f..e738a8d67 100644
--- a/resources/lang/en.json
+++ b/resources/lang/en.json
@@ -43,6 +43,7 @@
"action_move_camera": "Move camera",
"action_ratio_change": "Decrease/Increase attack ratio",
"action_reset_gfx": "Reset graphics",
+ "action_auto_upgrade": "Auto-upgrade nearest building",
"ui_section": "Game UI",
"ui_leaderboard": "Leaderboard",
"ui_your_team": "Your team:",
diff --git a/src/client/ClientGameRunner.ts b/src/client/ClientGameRunner.ts
index b0c3150d8..6d073fd30 100644
--- a/src/client/ClientGameRunner.ts
+++ b/src/client/ClientGameRunner.ts
@@ -26,6 +26,7 @@ import { loadTerrainMap, TerrainMapData } from "../core/game/TerrainMapLoader";
import { UserSettings } from "../core/game/UserSettings";
import { WorkerClient } from "../core/worker/WorkerClient";
import {
+ AutoUpgradeEvent,
DoBoatAttackEvent,
DoGroundAttackEvent,
InputHandler,
@@ -40,6 +41,7 @@ import {
SendBoatAttackIntentEvent,
SendHashEvent,
SendSpawnIntentEvent,
+ SendUpgradeStructureIntentEvent,
Transport,
} from "./Transport";
import { createCanvas } from "./Utils";
@@ -248,6 +250,7 @@ export class ClientGameRunner {
}, 20000);
this.eventBus.on(MouseUpEvent, this.inputEvent.bind(this));
this.eventBus.on(MouseMoveEvent, this.onMouseMove.bind(this));
+ this.eventBus.on(AutoUpgradeEvent, this.autoUpgradeEvent.bind(this));
this.eventBus.on(
DoBoatAttackEvent,
this.doBoatAttackUnderCursor.bind(this),
@@ -424,6 +427,76 @@ export class ClientGameRunner {
});
}
+ private autoUpgradeEvent(event: AutoUpgradeEvent) {
+ if (!this.isActive) {
+ return;
+ }
+
+ const cell = this.renderer.transformHandler.screenToWorldCoordinates(
+ event.x,
+ event.y,
+ );
+ if (!this.gameView.isValidCoord(cell.x, cell.y)) {
+ return;
+ }
+
+ const tile = this.gameView.ref(cell.x, cell.y);
+
+ if (this.myPlayer === null) {
+ const myPlayer = this.gameView.playerByClientID(this.lobby.clientID);
+ if (myPlayer === null) return;
+ this.myPlayer = myPlayer;
+ }
+
+ if (this.gameView.inSpawnPhase()) {
+ return;
+ }
+
+ this.findAndUpgradeNearestBuilding(tile);
+ }
+
+ private findAndUpgradeNearestBuilding(clickedTile: TileRef) {
+ this.myPlayer!.actions(clickedTile).then((actions) => {
+ const upgradeUnits: {
+ unitId: number;
+ unitType: UnitType;
+ distance: number;
+ }[] = [];
+
+ for (const bu of actions.buildableUnits) {
+ if (bu.canUpgrade !== false) {
+ const existingUnit = this.gameView
+ .units()
+ .find((unit) => unit.id() === bu.canUpgrade);
+ if (existingUnit) {
+ const distance = this.gameView.manhattanDist(
+ clickedTile,
+ existingUnit.tile(),
+ );
+
+ upgradeUnits.push({
+ unitId: bu.canUpgrade,
+ unitType: bu.type,
+ distance: distance,
+ });
+ }
+ }
+ }
+
+ if (upgradeUnits.length > 0) {
+ upgradeUnits.sort((a, b) => a.distance - b.distance);
+ const bestUpgrade = upgradeUnits[0];
+
+ this.eventBus.emit(
+ new SendUpgradeStructureIntentEvent(
+ bestUpgrade.unitId,
+ bestUpgrade.unitType,
+ ),
+ );
+ }
+ });
+ }
+
private doBoatAttackUnderCursor(): void {
const tile = this.getTileUnderCursor();
if (tile === null) {
diff --git a/src/client/HelpModal.ts b/src/client/HelpModal.ts
index 97e74be6d..2c9e33cbb 100644
--- a/src/client/HelpModal.ts
+++ b/src/client/HelpModal.ts
@@ -138,6 +138,14 @@ export class HelpModal extends LitElement {
${translateText("help_modal.action_reset_gfx")} |
+
+ |
+
+ |
+ ${translateText("help_modal.action_auto_upgrade")} |
+
diff --git a/src/client/InputHandler.ts b/src/client/InputHandler.ts
index f089df82f..3031858df 100644
--- a/src/client/InputHandler.ts
+++ b/src/client/InputHandler.ts
@@ -107,6 +107,13 @@ export class CenterCameraEvent implements GameEvent {
constructor() {}
}
+export class AutoUpgradeEvent implements GameEvent {
+ constructor(
+ public readonly x: number,
+ public readonly y: number,
+ ) {}
+}
+
export class InputHandler {
private lastPointerX: number = 0;
private lastPointerY: number = 0;
@@ -325,6 +332,12 @@ export class InputHandler {
}
private onPointerDown(event: PointerEvent) {
+ if (event.button === 1) {
+ event.preventDefault();
+ this.eventBus.emit(new AutoUpgradeEvent(event.clientX, event.clientY));
+ return;
+ }
+
if (event.button > 0) {
return;
}
@@ -346,6 +359,11 @@ export class InputHandler {
}
onPointerUp(event: PointerEvent) {
+ if (event.button === 1) {
+ event.preventDefault();
+ return;
+ }
+
if (event.button > 0) {
return;
}
@@ -398,6 +416,11 @@ export class InputHandler {
}
private onPointerMove(event: PointerEvent) {
+ if (event.button === 1) {
+ event.preventDefault();
+ return;
+ }
+
if (event.button > 0) {
return;
}
diff --git a/tests/AutoUpgrade.test.ts b/tests/AutoUpgrade.test.ts
new file mode 100644
index 000000000..126929098
--- /dev/null
+++ b/tests/AutoUpgrade.test.ts
@@ -0,0 +1,155 @@
+/**
+ * @jest-environment jsdom
+ */
+import { AutoUpgradeEvent } from "../src/client/InputHandler";
+import { EventBus } from "../src/core/EventBus";
+
+describe("AutoUpgrade Feature", () => {
+ let eventBus: EventBus;
+
+ beforeEach(() => {
+ eventBus = new EventBus();
+ });
+
+ describe("AutoUpgradeEvent", () => {
+ test("should create AutoUpgradeEvent with correct coordinates", () => {
+ const event = new AutoUpgradeEvent(100, 200);
+ expect(event.x).toBe(100);
+ expect(event.y).toBe(200);
+ });
+
+ test("should emit AutoUpgradeEvent when created", () => {
+ const mockEmit = jest.spyOn(eventBus, "emit");
+
+ const event = new AutoUpgradeEvent(150, 250);
+ eventBus.emit(event);
+
+ expect(mockEmit).toHaveBeenCalledWith(event);
+ expect(mockEmit).toHaveBeenCalledWith(
+ expect.objectContaining({
+ x: 150,
+ y: 250,
+ }),
+ );
+ });
+ });
+
+ describe("AutoUpgradeEvent Integration", () => {
+ test("should handle multiple AutoUpgradeEvents", () => {
+ const mockEmit = jest.spyOn(eventBus, "emit");
+
+ const event1 = new AutoUpgradeEvent(100, 200);
+ const event2 = new AutoUpgradeEvent(300, 400);
+
+ eventBus.emit(event1);
+ eventBus.emit(event2);
+
+ expect(mockEmit).toHaveBeenCalledTimes(2);
+ expect(mockEmit).toHaveBeenNthCalledWith(1, event1);
+ expect(mockEmit).toHaveBeenNthCalledWith(2, event2);
+ });
+
+ test("should handle AutoUpgradeEvent with zero coordinates", () => {
+ const event = new AutoUpgradeEvent(0, 0);
+ expect(event.x).toBe(0);
+ expect(event.y).toBe(0);
+ });
+
+ test("should handle AutoUpgradeEvent with negative coordinates", () => {
+ const event = new AutoUpgradeEvent(-100, -200);
+ expect(event.x).toBe(-100);
+ expect(event.y).toBe(-200);
+ });
+
+ test("should handle AutoUpgradeEvent with decimal coordinates", () => {
+ const event = new AutoUpgradeEvent(100.5, 200.7);
+ expect(event.x).toBe(100.5);
+ expect(event.y).toBe(200.7);
+ });
+ });
+
+ describe("AutoUpgradeEvent Event Bus Integration", () => {
+ test("should allow event listeners to subscribe to AutoUpgradeEvent", () => {
+ const mockListener = jest.fn();
+ const event = new AutoUpgradeEvent(100, 200);
+
+ eventBus.on(AutoUpgradeEvent, mockListener);
+ eventBus.emit(event);
+
+ expect(mockListener).toHaveBeenCalledWith(event);
+ });
+
+ test("should allow multiple listeners for AutoUpgradeEvent", () => {
+ const mockListener1 = jest.fn();
+ const mockListener2 = jest.fn();
+ const event = new AutoUpgradeEvent(100, 200);
+
+ eventBus.on(AutoUpgradeEvent, mockListener1);
+ eventBus.on(AutoUpgradeEvent, mockListener2);
+ eventBus.emit(event);
+
+ expect(mockListener1).toHaveBeenCalledWith(event);
+ expect(mockListener2).toHaveBeenCalledWith(event);
+ });
+
+ test("should not call unsubscribed listeners", () => {
+ const mockListener = jest.fn();
+ const event = new AutoUpgradeEvent(100, 200);
+
+ eventBus.on(AutoUpgradeEvent, mockListener);
+ eventBus.off(AutoUpgradeEvent, mockListener);
+ eventBus.emit(event);
+
+ expect(mockListener).not.toHaveBeenCalled();
+ });
+ });
+
+ describe("AutoUpgradeEvent Edge Cases", () => {
+ test("should handle very large coordinates", () => {
+ const event = new AutoUpgradeEvent(
+ Number.MAX_SAFE_INTEGER,
+ Number.MAX_SAFE_INTEGER,
+ );
+ expect(event.x).toBe(Number.MAX_SAFE_INTEGER);
+ expect(event.y).toBe(Number.MAX_SAFE_INTEGER);
+ });
+
+ test("should handle very small coordinates", () => {
+ const event = new AutoUpgradeEvent(
+ Number.MIN_SAFE_INTEGER,
+ Number.MIN_SAFE_INTEGER,
+ );
+ expect(event.x).toBe(Number.MIN_SAFE_INTEGER);
+ expect(event.y).toBe(Number.MIN_SAFE_INTEGER);
+ });
+
+ test("should handle NaN coordinates", () => {
+ const event = new AutoUpgradeEvent(NaN, NaN);
+ expect(isNaN(event.x)).toBe(true);
+ expect(isNaN(event.y)).toBe(true);
+ });
+
+ test("should handle Infinity coordinates", () => {
+ const event = new AutoUpgradeEvent(Infinity, -Infinity);
+ expect(event.x).toBe(Infinity);
+ expect(event.y).toBe(-Infinity);
+ });
+ });
+
+ describe("AutoUpgradeEvent Serialization", () => {
+ test("should maintain coordinate precision", () => {
+ const event = new AutoUpgradeEvent(100.123456789, 200.987654321);
+ expect(event.x).toBe(100.123456789);
+ expect(event.y).toBe(200.987654321);
+ });
+
+ test("should handle string conversion", () => {
+ const event = new AutoUpgradeEvent(100, 200);
+ const eventString = JSON.stringify(event);
+ const parsedEvent = JSON.parse(eventString);
+
+ expect(parsedEvent.x).toBe(100);
+ expect(parsedEvent.y).toBe(200);
+ });
+ });
+});
diff --git a/tests/InputHandler.test.ts b/tests/InputHandler.test.ts
new file mode 100644
index 000000000..82050eaa0
--- /dev/null
+++ b/tests/InputHandler.test.ts
@@ -0,0 +1,400 @@
+/**
+ * @jest-environment jsdom
+ */
+import { AutoUpgradeEvent, InputHandler } from "../src/client/InputHandler";
+import { EventBus } from "../src/core/EventBus";
+
+class MockPointerEvent {
+ button: number;
+ clientX: number;
+ clientY: number;
+ pointerId: number;
+ type: string;
+ preventDefault: () => void;
+
+ constructor(type: string, init: any) {
+ this.type = type;
+ this.button = init.button;
+ this.clientX = init.clientX;
+ this.clientY = init.clientY;
+ this.pointerId = init.pointerId;
+ this.preventDefault = jest.fn();
+ }
+}
+
+global.PointerEvent = MockPointerEvent as any;
+
+describe("InputHandler AutoUpgrade", () => {
+ let inputHandler: InputHandler;
+ let eventBus: EventBus;
+ let mockCanvas: HTMLCanvasElement;
+
+ beforeEach(() => {
+ mockCanvas = document.createElement("canvas");
+ mockCanvas.width = 800;
+ mockCanvas.height = 600;
+
+ eventBus = new EventBus();
+
+ inputHandler = new InputHandler(mockCanvas, eventBus);
+ });
+
+ describe("Middle Mouse Button Handling", () => {
+ test("should emit AutoUpgradeEvent on middle mouse button press", () => {
+ const mockEmit = jest.spyOn(eventBus, "emit");
+
+ const pointerEvent = new PointerEvent("pointerdown", {
+ button: 1,
+ clientX: 150,
+ clientY: 250,
+ pointerId: 1,
+ });
+
+ inputHandler["onPointerDown"](pointerEvent);
+
+ expect(mockEmit).toHaveBeenCalledWith(
+ expect.objectContaining({
+ x: 150,
+ y: 250,
+ }),
+ );
+ });
+
+ test("should emit MouseDownEvent on left mouse button press instead of AutoUpgradeEvent", () => {
+ const mockEmit = jest.spyOn(eventBus, "emit");
+
+ const pointerEvent = new PointerEvent("pointerdown", {
+ button: 0,
+ clientX: 150,
+ clientY: 250,
+ pointerId: 1,
+ });
+
+ inputHandler["onPointerDown"](pointerEvent);
+
+ expect(mockEmit).toHaveBeenCalledWith(
+ expect.objectContaining({
+ x: 150,
+ y: 250,
+ }),
+ );
+
+ const calls = mockEmit.mock.calls;
+ const lastCall = calls[calls.length - 1];
+ expect(lastCall[0]).not.toBeInstanceOf(AutoUpgradeEvent);
+ });
+
+ test("should not emit AutoUpgradeEvent on right mouse button press", () => {
+ const mockEmit = jest.spyOn(eventBus, "emit");
+
+ const pointerEvent = new PointerEvent("pointerdown", {
+ button: 2,
+ clientX: 150,
+ clientY: 250,
+ pointerId: 1,
+ });
+
+ inputHandler["onPointerDown"](pointerEvent);
+
+ expect(mockEmit).not.toHaveBeenCalledWith(
+ expect.objectContaining({
+ x: 150,
+ y: 250,
+ }),
+ );
+ });
+
+ test("should handle multiple middle mouse button presses", () => {
+ const mockEmit = jest.spyOn(eventBus, "emit");
+
+ const pointerEvent1 = new PointerEvent("pointerdown", {
+ button: 1,
+ clientX: 100,
+ clientY: 200,
+ pointerId: 1,
+ });
+ inputHandler["onPointerDown"](pointerEvent1);
+
+ const pointerEvent2 = new PointerEvent("pointerdown", {
+ button: 1,
+ clientX: 300,
+ clientY: 400,
+ pointerId: 2,
+ });
+ inputHandler["onPointerDown"](pointerEvent2);
+
+ expect(mockEmit).toHaveBeenCalledTimes(2);
+ expect(mockEmit).toHaveBeenNthCalledWith(
+ 1,
+ expect.objectContaining({
+ x: 100,
+ y: 200,
+ }),
+ );
+ expect(mockEmit).toHaveBeenNthCalledWith(
+ 2,
+ expect.objectContaining({
+ x: 300,
+ y: 400,
+ }),
+ );
+ });
+
+ test("should handle middle mouse button press with zero coordinates", () => {
+ const mockEmit = jest.spyOn(eventBus, "emit");
+
+ const pointerEvent = new PointerEvent("pointerdown", {
+ button: 1,
+ clientX: 0,
+ clientY: 0,
+ pointerId: 1,
+ });
+
+ inputHandler["onPointerDown"](pointerEvent);
+
+ expect(mockEmit).toHaveBeenCalledWith(
+ expect.objectContaining({
+ x: 0,
+ y: 0,
+ }),
+ );
+ });
+
+ test("should handle middle mouse button press with negative coordinates", () => {
+ const mockEmit = jest.spyOn(eventBus, "emit");
+
+ const pointerEvent = new PointerEvent("pointerdown", {
+ button: 1,
+ clientX: -100,
+ clientY: -200,
+ pointerId: 1,
+ });
+
+ inputHandler["onPointerDown"](pointerEvent);
+
+ expect(mockEmit).toHaveBeenCalledWith(
+ expect.objectContaining({
+ x: -100,
+ y: -200,
+ }),
+ );
+ });
+
+ test("should handle middle mouse button press with decimal coordinates", () => {
+ const mockEmit = jest.spyOn(eventBus, "emit");
+
+ const pointerEvent = new PointerEvent("pointerdown", {
+ button: 1,
+ clientX: 100.5,
+ clientY: 200.7,
+ pointerId: 1,
+ });
+
+ inputHandler["onPointerDown"](pointerEvent);
+
+ expect(mockEmit).toHaveBeenCalledWith(
+ expect.objectContaining({
+ x: 100.5,
+ y: 200.7,
+ }),
+ );
+ });
+ });
+
+ describe("Pointer Event Handling", () => {
+ test("should handle pointer events with different pointer IDs", () => {
+ const mockEmit = jest.spyOn(eventBus, "emit");
+
+ const pointerEvent1 = new PointerEvent("pointerdown", {
+ button: 1,
+ clientX: 100,
+ clientY: 200,
+ pointerId: 1,
+ });
+ inputHandler["onPointerDown"](pointerEvent1);
+
+ const pointerEvent2 = new PointerEvent("pointerdown", {
+ button: 1,
+ clientX: 300,
+ clientY: 400,
+ pointerId: 2,
+ });
+ inputHandler["onPointerDown"](pointerEvent2);
+
+ expect(mockEmit).toHaveBeenCalledTimes(2);
+ });
+
+ test("should handle pointer events with same pointer ID", () => {
+ const mockEmit = jest.spyOn(eventBus, "emit");
+
+ const pointerEvent1 = new PointerEvent("pointerdown", {
+ button: 1,
+ clientX: 100,
+ clientY: 200,
+ pointerId: 1,
+ });
+ inputHandler["onPointerDown"](pointerEvent1);
+
+ const pointerEvent2 = new PointerEvent("pointerdown", {
+ button: 1,
+ clientX: 300,
+ clientY: 400,
+ pointerId: 1,
+ });
+ inputHandler["onPointerDown"](pointerEvent2);
+
+ expect(mockEmit).toHaveBeenCalledTimes(2);
+ });
+ });
+
+ describe("Edge Cases", () => {
+ test("should handle very large coordinates", () => {
+ const mockEmit = jest.spyOn(eventBus, "emit");
+
+ const pointerEvent = new PointerEvent("pointerdown", {
+ button: 1,
+ clientX: Number.MAX_SAFE_INTEGER,
+ clientY: Number.MAX_SAFE_INTEGER,
+ pointerId: 1,
+ });
+
+ inputHandler["onPointerDown"](pointerEvent);
+
+ expect(mockEmit).toHaveBeenCalledWith(
+ expect.objectContaining({
+ x: Number.MAX_SAFE_INTEGER,
+ y: Number.MAX_SAFE_INTEGER,
+ }),
+ );
+ });
+
+ test("should handle very small coordinates", () => {
+ const mockEmit = jest.spyOn(eventBus, "emit");
+
+ const pointerEvent = new PointerEvent("pointerdown", {
+ button: 1,
+ clientX: Number.MIN_SAFE_INTEGER,
+ clientY: Number.MIN_SAFE_INTEGER,
+ pointerId: 1,
+ });
+
+ inputHandler["onPointerDown"](pointerEvent);
+
+ expect(mockEmit).toHaveBeenCalledWith(
+ expect.objectContaining({
+ x: Number.MIN_SAFE_INTEGER,
+ y: Number.MIN_SAFE_INTEGER,
+ }),
+ );
+ });
+
+ test("should handle NaN coordinates", () => {
+ const mockEmit = jest.spyOn(eventBus, "emit");
+
+ const pointerEvent = new PointerEvent("pointerdown", {
+ button: 1,
+ clientX: NaN,
+ clientY: NaN,
+ pointerId: 1,
+ });
+
+ inputHandler["onPointerDown"](pointerEvent);
+
+ expect(mockEmit).toHaveBeenCalledWith(
+ expect.objectContaining({
+ x: NaN,
+ y: NaN,
+ }),
+ );
+ });
+
+ test("should handle Infinity coordinates", () => {
+ const mockEmit = jest.spyOn(eventBus, "emit");
+
+ const pointerEvent = new PointerEvent("pointerdown", {
+ button: 1,
+ clientX: Infinity,
+ clientY: -Infinity,
+ pointerId: 1,
+ });
+
+ inputHandler["onPointerDown"](pointerEvent);
+
+ expect(mockEmit).toHaveBeenCalledWith(
+ expect.objectContaining({
+ x: Infinity,
+ y: -Infinity,
+ }),
+ );
+ });
+ });
+
+ describe("Integration with Event Bus", () => {
+ test("should allow event listeners to receive AutoUpgradeEvents", () => {
+ const mockListener = jest.fn();
+
+ eventBus.on(AutoUpgradeEvent, mockListener);
+
+ const pointerEvent = new PointerEvent("pointerdown", {
+ button: 1,
+ clientX: 150,
+ clientY: 250,
+ pointerId: 1,
+ });
+ inputHandler["onPointerDown"](pointerEvent);
+
+ expect(mockListener).toHaveBeenCalledWith(
+ expect.objectContaining({
+ x: 150,
+ y: 250,
+ }),
+ );
+ });
+
+ test("should allow multiple listeners for AutoUpgradeEvent", () => {
+ const mockListener1 = jest.fn();
+ const mockListener2 = jest.fn();
+
+ eventBus.on(AutoUpgradeEvent, mockListener1);
+ eventBus.on(AutoUpgradeEvent, mockListener2);
+
+ const pointerEvent = new PointerEvent("pointerdown", {
+ button: 1,
+ clientX: 150,
+ clientY: 250,
+ pointerId: 1,
+ });
+ inputHandler["onPointerDown"](pointerEvent);
+
+ expect(mockListener1).toHaveBeenCalledWith(
+ expect.objectContaining({
+ x: 150,
+ y: 250,
+ }),
+ );
+ expect(mockListener2).toHaveBeenCalledWith(
+ expect.objectContaining({
+ x: 150,
+ y: 250,
+ }),
+ );
+ });
+
+ test("should not call unsubscribed listeners", () => {
+ const mockListener = jest.fn();
+
+ eventBus.on(AutoUpgradeEvent, mockListener);
+ eventBus.off(AutoUpgradeEvent, mockListener);
+
+ const pointerEvent = new PointerEvent("pointerdown", {
+ button: 1,
+ clientX: 150,
+ clientY: 250,
+ pointerId: 1,
+ });
+ inputHandler["onPointerDown"](pointerEvent);
+
+ expect(mockListener).not.toHaveBeenCalled();
+ });
+ });
+});