Release: develop -> main - #31
Merged
Merged
Conversation
* Add invite and promo landing pages. * Add Playwright visuals for spent invite and promo landings. * Add English Playwright visuals for invalid invite and promo landings. * Add English Playwright visuals for spent invite and promo landings. * Add English Playwright visuals for unavailable invite and promo landings. * Add Playwright visuals for invite and promo landings with no code. * Add English Playwright visuals for invite and promo with no code. * Add loading mock and Playwright visuals for invite and promo landings. * Add English Playwright visuals for invite and promo loading states. * Add Android Playwright visuals for invite and promo loading states. * Add Android Playwright visuals for invalid invite and promo landings. * Add Android Playwright visuals for unavailable invite and promo landings. * Add Android Playwright visuals for spent invite and promo landings. * Add Android Playwright visuals for invite and promo with no code. * Add fallback mock and Playwright visuals for nameless invite and promo landings. * Add English Playwright visuals for nameless invite and promo landings. * Add Android Playwright visuals for nameless invite and promo landings. * Add English Android Playwright visuals for invite and promo loading states. * Add English Android Playwright visuals for invalid invite and promo landings. * Add English Android Playwright visuals for unavailable invite and promo landings. * Pin referral lookup name fields * test(web): refresh visual baselines * fix(referral): harden invite/promo landing per review EN: Escape reflected referral codes at the crawler HTML text sinks and use a function replacer so a $ in the code is never a backreference; treat a plain-text "Cannot GET" 404 as unavailable (read the body as text, not JSON); render the promo action text verbatim (do not trim the displayed value); drop the Android debug-keystore fingerprint from the production Digital Asset Links; widen the English visual matrix to tablet and mobile with regenerated baselines. DE: Reflektierte Referral-Codes an den Crawler-HTML-Textstellen escapen und per Funktions-Replacer einsetzen (ein $ im Code ist nie mehr eine Backreference); einen Klartext-404 "Cannot GET" als unavailable behandeln (Body als Text statt JSON lesen); Promo-Aktionstext 1:1 anzeigen (Anzeige nicht trimmen); den Android-Debug-Keystore-Fingerprint aus den Prod-assetlinks entfernen; die englische Visual-Matrix auf Tablet und Mobile erweitern (Baselines neu erzeugt). * test(web): widen remaining English visual views to tablet and mobile EN: Extend the loading/invalid/unavailable English invite and promo views to tablet-chromium and mobile-safari (completing the language x device x state matrix) and add the regenerated baselines. DE: Die englischen loading/invalid/unavailable-Views fuer Invite und Promo auf tablet-chromium und mobile-safari erweitern (Matrix Sprache x Geraet x Zustand vollstaendig) und die neu erzeugten Baselines ergaenzen. * fix(referral): address review findings on the landing hardening EN: Treat only a non-2xx text body as a message so a 200 with a non-JSON body (a misconfigured proxy/CDN) falls through to the safe "unavailable" state instead of a false-positive success page; add escaping tests for the image-alt and description share sinks and a browser-mirror escaping test; add an e2e regression for the 200-non-JSON case; document the deliberate escape-contract difference between the server and browser replaceTaggedAttr mirrors; drop the now-single debug fingerprint sentence from the README; and remove a stray "(orchestrator)" suffix from three test names. DE: Nur einen non-2xx-Textbody als Message behandeln, damit ein 200 mit Nicht-JSON-Body (fehlkonfigurierter Proxy/CDN) in den sicheren "unavailable"-Zustand faellt statt in eine falsch-positive Erfolgsseite; Escaping-Tests fuer die image-alt- und description-Share-Sinks sowie einen Browser-Mirror-Escaping-Test ergaenzen; eine e2e-Regression fuer den 200-Nicht-JSON-Fall hinzufuegen; die bewusste Escape-Vertragsdifferenz zwischen Server- und Browser-replaceTaggedAttr-Mirror dokumentieren; den nun einzelnen Debug-Fingerprint-Satz aus dem README entfernen; und ein verirrtes "(orchestrator)"-Suffix aus drei Testnamen loeschen. * test(referral): strengthen the landing-hardening regression tests EN: Make the escaping assertions per-sink (count the escaped code once per og/twitter/meta sink instead of a single global check) so a per-sink regression is caught; add the missing quote-escape assertion and image-alt coverage to the browser-mirror test; and assert the stubbed route was actually hit in the 200-non-JSON e2e test so it cannot pass via the generic network-error path. DE: Die Escaping-Assertions sink-spezifisch machen (den escapten Code je og/twitter/meta-Sink einmal zaehlen statt einer globalen Pruefung), damit eine Regression je Sink auffaellt; die fehlende Quote-Escape-Assertion und image-alt-Abdeckung im Browser-Mirror-Test ergaenzen; und im 200-Nicht-JSON-e2e pruefen, dass die gestubbte Route wirklich getroffen wurde, damit er nicht ueber den generischen Netzwerkfehler-Pfad besteht. * fix(invite): stop folding issued codes and foreign paths onto a code Three ways a wrong referrer could be credited: - unwrapNestedCode matched `invite/` anywhere, so an issued code containing it (`PREFIXINVITE/AB12CD`) collapsed onto its tail. Codes may legitimately contain a slash and are not character-validated, so this hit honest users. Anchored at start or slash, on both the browser and the function mirror. - codeFromReferralHostInPath accepted our host from any segment of a foreign path, so `example.com/r/realunit.app/invite/ATTACKER` yielded ATTACKER. Restricted to the marked viewer forms that need it: /amp/s/<host>, /c/s/<host> and /https/<host>. - The function-side landing matcher was unbounded, so `evilrealunit.app` contained our host as a substring and passed. Two tests asserted the first and second behaviours as intended and were corrected; the attack case is now a regression test. * test(coverage): gate the code the Pages Function actually runs coverage.include only listed public/js/lib, so the 100% badge was earned by a browser-side mirror whose two HTML transformers are referenced from tests only, while functions/lib/itunes-banner.js — the module the middleware imports — was measured not at all. It sits at 98.98% lines / 88% branches; that is now a ratchet, not a blind spot. Adds tests for three paths this uncovered (bare store link, unparseable lang, fragment-only code) and drops a dead scheme-less retry in the host-in-path unwrap. * test(invite): assert values where the suite only asserted shape Four places passed without checking the thing under test: three parser fallbacks accepted any non-empty string, the cap test accepted any 32-character output, the Play-referrer expectation was built from the same production helper it verifies, and a setAttribute stub discarded the value it was called with. * docs: describe the architecture the repo actually has Three ground rules contradicted the code they govern: public/ does not ship verbatim for invite/promo (the middleware rewrites those bytes), "no external resources of any kind (…, fetch)" sits next to the API lookup the CSP explicitly allows, and the visual matrix listed three families while VIEWS carries six across 72 views. * fix(invite): hand the app the kind the API confirmed The page switched its copy on result.state but every hand-off kept using the kind from the URL path, so /invite/<a promo code> showed the promo text while the deeplink, the smart app banner and the Play install referrer still said invite= — the app then processed it as the wrong programme. The regression test fails without the fix. * fix(invite): cap the code before the final strip and never split a surrogate Capping ran after the trailing-punctuation strip, so a cut landing on a separator left it in place (…C. instead of …C). A cut through a surrogate pair left a lone surrogate, which makes encodeURIComponent throw when the code goes into the lookup URL and the deeplink. The 32-character cap itself stays: the API's sanitizeReferralCode and the app fold identically, so rejecting here instead would put the three out of step. --------- Co-authored-by: Jonny Luca <320529100+JonnyLuca@users.noreply.github.com> Co-authored-by: TaprootFreakAI <315477232+TaprootFreakAI@users.noreply.github.com>
* test(account-merge): cover empty otp, 404, DeadLetter and Failed jobs The confirmation page already handles these API states; the suite now opens each one the way a mail link would. * test(account-merge): match sibling assertions and printWidth Empty OTP also asserts the loading state is hidden. DeadLetter counts the confirm request like the existing Failed case. * test(account-merge): count the job GET on a Failed poll A regression that skipped polling would still show unavailable. The test now requires the confirm and the job request. * test(account-merge): cover DeadLetter after a pending job poll A ticket that is Pending on confirm and DeadLetter on the first job GET must show unavailable and must have issued the job request. --------- Co-authored-by: TaprootFreakAI <315477232+TaprootFreakAI@users.noreply.github.com>
TaprootFreak
approved these changes
Sep 9, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Automatic Release PR
This PR was automatically created after changes were pushed to develop.
Commits: 1 new commit(s)
Checklist