Chapters Testing locally

Chapter 11 of 13

Testing locally

No game runs offline: put your work behind a tunnel, play it in the Inspector's Test Room in any browser, and keep your tests offline.

The project create-camtable-addon makes (section 2: Quick start) is a complete reference: by default its game runs in host mode, its rules a reducer that the room host’s page runs with createHostAuthority, and with --engine the same reducer runs in engine mode, on a Worker. CamTable’s official games are built the same way.

There is no offline way to try a game. A game runs in a CamTable room, a room runs on CamTable’s server, and the server and the CamTable website reach an Addon only at an https address. So you put your work in progress behind a tunnel and try it in the Inspector, or in a real Discord call. What runs offline is your tests (below).

1. The Inspector, in any browser. The Inspector is a page of the CamTable website, https://camtable.app/inspector. Sign in with Discord, paste your Addon’s https manifest address (or open a link that carries it as ?url=), pick a game and a Play Option, and open a Test Room. It accepts any Addon’s manifest, however it was built, and needs no extension.

  • A Test Room is a real room on CamTable’s server, in both authority modes: the Lobby and Match Settings, the room’s limits, the Addon Blocklist and the action pipeline behave as in any room. It needs no paid Plan, is never listed, admits only you, and closes 30 minutes after it opens. You can have one open at a time.
  • You play every seat. The Inspector shows one game frame per seat, each with its own room connection, its own Match Player Id and a placeholder Video Tile, and each framed with the same sandbox the extension uses. The first seat is the room’s Host: in host mode its page decides every seat’s intents through createHostAuthority, as in a real room.
  • In engine mode the room calls your engine with CamTable’s real signature and its engine limits, and each request carries "test": true (section 8: Host mode and engine mode, “Test matches”).
  • The Inspector reports a manifest it cannot fetch, a field that fails the protocol’s schema, and an Addon on the blocklist, each with its own message.
  • The deployed Inspector accepts only https manifest addresses. A development build of the CamTable website also accepts a plain-http loopback address.

A game page opened directly in a browser tab, outside any room, says that it runs inside a CamTable room and links to the Inspector (NotInRoomError, section 6: The game page and the SDK).

2. pnpm dev in a scaffolded project. It starts the addon’s local server (the Vite server in host mode, the single Worker in engine mode), opens a temporary cloudflared quick tunnel to it (no account needed), and prints three addresses:

  Manifest URL  https://<random-words>.trycloudflare.com/manifest.json
  Inspector     https://camtable.app/inspector?url=<manifest url>
  Install link  https://camtable.app/install?url=<manifest url>

Open the Inspector link to play, or the Install Link in a browser with the CamTable extension to play in a real Discord call. The manifest is built for the tunnel’s address, so uiUrl and engine.url never say localhost, and in engine mode the Worker verifies signatures against the deployed CamTable server’s keys, because that server is the caller (a Test Room is one of its rooms). The address changes on every run. Deploy (section 9: Hosting) when you are done.

cloudflared is recommended, not required: its quick tunnel needs no account, is free, and puts no page of its own in front of your game page. Install it with brew install cloudflared, winget install --id Cloudflare.cloudflared, or Cloudflare’s apt repository; pnpm dev says so if it is missing.

Use your own tunnel. With another tunnel (ngrok, Tailscale Funnel, …), open it to a local port yourself and hand its https address to pnpm dev in ADDON_ORIGIN: PORT=4000 ADDON_ORIGIN=https://<your tunnel's address> pnpm dev. It then starts no cloudflared; it starts the local server on that port for that address (the template’s Vite server allows the Host of ADDON_ORIGIN), checks that the manifest answers through it and prints the same links. The scaffold starts no tunnel other than cloudflared. ngrok’s free plan shows browsers a warning page before your site, and in a room that page can take the place of your framed game page, so the game never appears.

Against a CamTable on your machine (CamTable’s own development): CAMTABLE_SITE points the links at another website, such as http://localhost:5190, and CAMTABLE_SERVER names the CamTable server whose rooms call your engine, such as http://localhost:8787, so the Worker checks signatures with that server’s published keys. With a ready loopback address in ADDON_ORIGIN, no tunnel is needed: the local server and a development build of the website both accept it.

pnpm test in the template covers what should never regress, with no network: every route validates against the protocol; the reducer’s replies (it is a pure function, so test it by calling it); a whole match played through the rules; and, in engine mode, that the Worker refuses an unsigned request and answers a correctly signed one. Extend tests/game.test.ts with your own rules.