test: reconnexion même identité — VERT (défaut résolu), retrait de @wip

Le scénario passe désormais (broker réel, 5 steps) : une page fraîche pour la
MÊME identité, sur le wallet persistant (nouveau login → session verifier
fraîche), relit son événement sur l'accueil, sa participation et un count
autoritatif de 1.

Résolution mesurée (investigation opus répétée) : le read à froid de l'index de
scope public est un LAG DE SYNC borné, pas un gap permanent — le doc se liste dès
la 1re tentative, l'accueil converge en ~1 s. Il fallait deux choses :
- les fix lib open-repo + anti-fork de compte (branche fix/session-rehydration-
  on-login, dans node_modules) ;
- et surtout que le TEST attende la convergence : les 3 assertions de la page
  fraîche POLLENT maintenant (jusqu'à ~15 s) au lieu de lire une seule fois — un
  read unique course la fenêtre de premier-open/sync et flakait. L'UI réelle est
  réactive, donc ce polling reflète le vrai comportement utilisateur.

Devient un test @data permanent (retrait de @wip).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Sylvain Duchesne
2026-07-08 13:14:22 +02:00
parent 005c052bc6
commit c07150cb27
2 changed files with 26 additions and 7 deletions
@@ -1,5 +1,5 @@
# language: fr
@EVENT @priority-1 @data @wip
@EVENT @priority-1 @data
Fonctionnalité: Reconnexion d'une même identité sur le wallet persistant
En tant qu'utilisateur qui, sur le MÊME wallet physique, ouvre une PAGE FRAÎCHE
(nouveau login broker, session fraîche) sous le MÊME identifiant qu'avant,
@@ -52,21 +52,34 @@ When('une page fraîche pour la MÊME identité A recharge sur le même wallet',
(this as any).recoFreshFrame = freshFrame;
});
Then('l\'événement {string} est sur l\'accueil de la page fraîche A', async function (this: FestipodWorld, title: string) {
// The cold-start read is a BOUNDED SYNC-LAG (measured: the just-created public
// doc lists on the first cold attempt, home titles converge at ~1s), and the real
// UI is reactive — so these assertions POLL for convergence (up to ~15s) instead
// of reading once. A single-shot read races the first-open/sync window and would
// flake. This mirrors how the app's own steps already poll participation.
Then('l\'événement {string} est sur l\'accueil de la page fraîche A', { timeout: 60000 }, async function (this: FestipodWorld, title: string) {
const freshFrame = (this as any).recoFreshFrame;
const onHome = await freshFrame.evaluate((title: string) => {
const onHome = await freshFrame.evaluate(async (title: string) => {
const td = (window as any).__testData;
for (let i = 0; i < 30; i++) {
if (td.homeEventTitles().includes(title)) return true;
await new Promise(r => setTimeout(r, 500));
}
return td.homeEventTitles().includes(title);
}, title);
expect(onHome, `"${title}" MUST appear on the fresh A page's home (getUserEvents(A) after reconnect)`).to.be.true;
});
Then('la page fraîche A est participante de l\'événement {string}', async function (this: FestipodWorld, title: string) {
Then('la page fraîche A est participante de l\'événement {string}', { timeout: 60000 }, async function (this: FestipodWorld, title: string) {
const freshFrame = (this as any).recoFreshFrame;
const eventId = (this as any).isoEventId;
const isPart = await freshFrame.evaluate(async ({ eventId }: { eventId: string }) => {
const td = (window as any).__testData;
const aId = await td.ensureCurrentUser();
for (let i = 0; i < 30; i++) {
if (td.isParticipating(eventId, aId)) return true;
await new Promise(r => setTimeout(r, 500));
}
return td.isParticipating(eventId, aId);
}, { eventId });
expect(isPart, `The fresh A page MUST read its own participation in "${title}" after reconnect`).to.be.true;
@@ -75,10 +88,16 @@ Then('la page fraîche A est participante de l\'événement {string}', async fun
Then('le compte autoritatif de participation de A à l\'événement {string} est {int}', { timeout: 60000 }, async function (this: FestipodWorld, _title: string, expected: number) {
const freshFrame = (this as any).recoFreshFrame;
const eventId = (this as any).isoEventId;
const count = await freshFrame.evaluate(async ({ eventId }: { eventId: string }) => {
const count = await freshFrame.evaluate(async ({ eventId, expected }: { eventId: string; expected: number }) => {
const td = (window as any).__testData;
const aId = await td.ensureCurrentUser();
return td.authParticipationCount(eventId, aId);
}, { eventId });
let last = -1;
for (let i = 0; i < 30; i++) {
last = await td.authParticipationCount(eventId, aId);
if (last === expected) return last;
await new Promise(r => setTimeout(r, 500));
}
return last;
}, { eventId, expected });
expect(count, `Authoritative broker count of A's participation must be ${expected} after reconnect`).to.equal(expected);
});