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.
54 lines
2.3 KiB
JavaScript
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 });
|
|
};
|