| 1 | // Tests for the signup-first join page's pure decision helpers (prejoin.js). |
| 2 | import test from "node:test"; |
| 3 | import assert from "node:assert/strict"; |
| 4 | import { |
| 5 | PREJOIN_STASH_KEY, offerPrejoin, prejoinStash, readPrejoinReturn, homeSigninURL, |
| 6 | } from "../web/static/prejoin.js"; |
| 7 | |
| 8 | const HOME = "https://home.example.com"; |
| 9 | const base = { kind: "join", homeOrigin: HOME, embed: false, native: false, signedUp: false }; |
| 10 | |
| 11 | test("offerPrejoin: fresh join/setup kinds with a home server", () => { |
| 12 | assert.equal(offerPrejoin(base), true); |
| 13 | assert.equal(offerPrejoin({ ...base, kind: "setup" }), true); |
| 14 | }); |
| 15 | |
| 16 | test("offerPrejoin: never for established or unsupported contexts", () => { |
| 17 | for (const kind of ["device", "adopt", "adopt-admin"]) { |
| 18 | assert.equal(offerPrejoin({ ...base, kind }), false); |
| 19 | } |
| 20 | assert.equal(offerPrejoin({ ...base, homeOrigin: "" }), false); |
| 21 | assert.equal(offerPrejoin({ ...base, homeOrigin: "__HOME_ORIGIN__" }), false); |
| 22 | assert.equal(offerPrejoin({ ...base, embed: true }), false); |
| 23 | assert.equal(offerPrejoin({ ...base, native: true }), false); |
| 24 | assert.equal(offerPrejoin({ ...base, signedUp: true }), false); |
| 25 | }); |
| 26 | |
| 27 | test("round trip: stash then read back on a genuine return", () => { |
| 28 | const now = 1_000_000; |
| 29 | const stored = prejoinStash({ frag: "e1.SECRET~Ann~bkey", nonce: "n0nce", now }); |
| 30 | const r = readPrejoinReturn({ search: "?su=n0nce", storedJSON: stored, now: now + 5000 }); |
| 31 | assert.deepEqual(r, { signedUp: true, frag: "e1.SECRET~Ann~bkey" }); |
| 32 | }); |
| 33 | |
| 34 | test("readPrejoinReturn: no marker, forged nonce, stale stash, clock skew", () => { |
| 35 | const now = 1_000_000; |
| 36 | const stored = prejoinStash({ frag: "f", nonce: "good", now }); |
| 37 | assert.deepEqual(readPrejoinReturn({ search: "", storedJSON: stored, now }), |
| 38 | { signedUp: false, frag: "" }); |
| 39 | assert.deepEqual(readPrejoinReturn({ search: "?su=evil", storedJSON: stored, now }), |
| 40 | { signedUp: false, frag: "" }); |
| 41 | assert.deepEqual(readPrejoinReturn({ search: "?su=good", storedJSON: stored, now: now + 61 * 60 * 1000 }), |
| 42 | { signedUp: false, frag: "" }); |
| 43 | assert.deepEqual(readPrejoinReturn({ search: "?su=good", storedJSON: stored, now: now - 1000 }), |
| 44 | { signedUp: false, frag: "" }); |
| 45 | assert.deepEqual(readPrejoinReturn({ search: "?su=good", storedJSON: "not json", now }), |
| 46 | { signedUp: false, frag: "" }); |
| 47 | assert.deepEqual(readPrejoinReturn({ search: "?su=good", storedJSON: "", now }), |
| 48 | { signedUp: false, frag: "" }); |
| 49 | }); |
| 50 | |
| 51 | test("homeSigninURL: return URL rides the fragment, encoded, no invite secret", () => { |
| 52 | const url = homeSigninURL(HOME, "https://chat.example.com", "/join/abc", "n+once"); |
| 53 | assert.equal(url, |
| 54 | HOME + "/#joinret=" + encodeURIComponent("https://chat.example.com/join/abc?su=" + encodeURIComponent("n+once"))); |
| 55 | assert.equal(homeSigninURL(HOME + "/", "https://c.example.com", "/join/x", "n"), |
| 56 | HOME + "/#joinret=" + encodeURIComponent("https://c.example.com/join/x?su=n")); |
| 57 | }); |
| 58 | |
| 59 | test("stash key is stable", () => assert.equal(PREJOIN_STASH_KEY, "lchat_prejoin")); |
| 60 | |