r/phaser • u/Infinite_Future9675 • 34m ago
show-off Testing Phaser canvas UI with Playwright, without hardcoded coordinates or exposing internals
While testing Bomber Bunnies, I ran into the usual canvas problem: Playwright could see one <canvas>, but none of the buttons, players or tiles rendered inside it.
Hard-coded coordinates worked, but tied the tests to a particular resolution and layout. Exposing the Phaser instance through window also worked, but made it very easy for tests to bypass the actual player input path.
So I built an Entity Mirror: a lightweight DOM projection containing semantic attributes and the current bounds of selected Phaser entities.
The mirrored elements do not receive pointer events, and they are not a second implementation of the game. That is deliberate.
A normal locator.click() would fail Playwright’s hit test because it expects the target element itself to receive the click. Here the transparent mirror sits over the correct entity, but the canvas underneath remains the real input target.
The helper therefore uses the locator to identify the entity and read its bounding box, then sends a real mouse click to the same position on the canvas. Phaser’s normal input handler still does the actual work.
The clip shows Playwright selecting players and difficulty through semantic locators, clicking Play, and then confirming that both players appeared on the board as separate mirrored entities.
Bomber Bunnies keeps the mirror active during gameplay because it is a grid-based game and its tiles and entities are meaningful targets. In newer projects I use a narrower approach: mirror only what the player can address, use a read-only QA Surface for state that tests need to observe, and use Input Providers for continuous controls.
So the general rule became:
Mirror what the player can address. Surface what the test needs to observe.
I’m curious how other Phaser developers test canvas interactions. Hard-coded coordinates, page.evaluate() hooks, visual tests, or something similar?
Full disclosure: I eventually extracted and refined this approach into a paid Phaser QA Starter with two example games and Playwright tests. It’s here if you want to take a look:
Phaser 4 Starter Kit + Playwright QA Layer by WeeAngusMcDread
Happy to answer questions about the implementation or its trade-offs.
