todos-contra-el-fuego-web/e2e/support/app.js
vjrj a3f2929ff7 test(e2e): browser suite for the flows the server tests cannot see
Playwright under e2e/, its own npm project so Meteor never sees the dependency
(.meteorignore keeps the directory out of the bundle — a config file at the app
root is otherwise eagerly loaded into the server and crashes it).

Covers what the phase asked for: sign up / log in, creating a zone from the map
and watching the union get painted on /subscriptions and /zones, removing it
again, commenting on a fire, and switching language. The zone spec is the flow
that froze staging on 2026-07-30 — it passes only if the server is still
answering DDP while the union is recomputed.

docker-compose.e2e.yml is a throwaway stack (Mongo on tmpfs, no named volume) so
the destructive seeds cannot be pointed at anything real by accident;
playwright.config.js refuses to start against the staging or production domains.

i18n.spec.js leaves a test.fixme on a real bug found while writing it: the
language chosen in the profile does not survive a page reload.
2026-08-01 18:44:09 +02:00

54 lines
2.3 KiB
JavaScript

// Shared helpers. Everything here drives the UI the way a person would: the
// point of this suite is the wiring between browser, DDP and Mongo, so reaching
// into Meteor.call() from the page would defeat it.
// A fresh address per test: the seed removes every e2e+* user, and reusing one
// across specs makes failures depend on the order they ran in.
export const newEmail = () => `e2e+${Date.now()}${Math.floor(Math.random() * 1000)}@test.com`;
export const signUp = async (page, email = newEmail()) => {
await page.goto('/signup');
await page.fill('input[name=firstName]', 'E2E');
await page.fill('input[name=lastName]', 'Tester');
await page.fill('input[name=emailAddress]', email);
await page.fill('input[name=password]', 'password');
await page.check('#tos');
await page.click('#signUpSubmit');
// Signing up lands on the zones page.
await page.waitForURL('**/subscriptions', { timeout: 30000 });
return email;
};
export const logIn = async (page, email, password = 'password') => {
await page.goto('/login');
await page.fill('input[name=emailAddress]', email);
await page.fill('input[name=password]', password);
await page.click('#loginSubmit');
};
// The map only settles once leaflet has laid out its panes and the tiles for the
// current viewport are in.
export const waitForMap = async (page) => {
await page.locator('.leaflet-container').first().waitFor({ state: 'visible', timeout: 60000 });
await page.waitForTimeout(1500);
};
// Creates a zone through the real flow: the button on the zones map, the
// selection map, and the subscribe button. This is the flow that froze staging.
export const addZoneThroughTheMap = async (page) => {
await page.goto('/subscriptions');
await waitForMap(page);
await page.getByRole('button', { name: 'Añadir zona' }).click();
await page.waitForURL('**/subscriptions/new', { timeout: 30000 });
await waitForMap(page);
const map = page.locator('.leaflet-container').first();
const box = await map.boundingBox();
// Click slightly off-centre: the marker moves there, which also proves the
// map is interactive and not a dead tile layer.
await page.mouse.click(box.x + (box.width / 2) + 20, box.y + (box.height / 2) + 20);
await page.waitForTimeout(1000);
await page.getByRole('button', { name: /Suscribirme/ }).click();
await page.waitForURL('**/subscriptions', { timeout: 60000 });
};