Your browser sucks, JavaScript disabled! Gunnar's console needs JavaScript and WebAssembly. Git over SSH on port 2222 does not.

nordisk/gunnar-ui

crates/gunnar-ui-server/src/anonymous/signup_page.rs

//! **The ONE sign-up page** (owner 2026-10-07: "go design it") — its contract,
//! held as the owner described it.
//!
//! * The LEFT half is the front page's, exactly: `.art` and its phone rule
//!   are login.html's own lines.
//! * The heading follows the situation: "Sign up for an account" (a visitor
//!   with no account here, whichever way they go — owner 2026-10-15), "Sign up
//!   for a new private Gunnar" (signed in, a new instance, ALWAYS), "Grow your
//!   account", "Grow <company>'s Gunnar"; the button says "Sign up" or "Upgrade".
//! * A visitor's two choices (owner 2026-10-15): LEFT and selected, "Account
//!   on <host>" — a list to pick from: the free account (read only), then the
//!   ONE box's plans with its prices; RIGHT, "Private Gunnar instance" — the
//!   server picture, its − / + and price, shown for it alone.
//! * ONE − / + for the whole setup; a lean picture of UI, SERVER and REPLICA
//!   inside a double dashed ring, captioned "Gunnar instance protected by
//!   VPN"; REPLICA is one constant.
//! * The front page's two offers open this page with their choice preselected.
//! * Thin: no price, no plan and no step name of the page's own — every one
//!   is monetize's answer.

use crate::auth::signup::signup_html;

fn page() -> &'static str {
    signup_html()
}

/// The CSS rules between two markers, one per `}`.
fn rules_between(html: &str, start: &str, end: &str) -> Vec<String> {
    let from = html.find(start).unwrap_or_else(|| panic!("no {start} marker")) + start.len();
    let to = from + html[from..].find(end).unwrap_or_else(|| panic!("no {end} marker"));
    html[from..to]
        .split_inclusive('}')
        .map(|r| r.trim().to_owned())
        .filter(|r| !r.is_empty() && !r.starts_with("/*"))
        .collect()
}

/// ★ **The left half is the front page's, byte for byte**: every rule of it is
/// a rule login.html carries, and the element is the same element.
#[test]
fn the_left_half_is_the_front_pages() {
    let login = crate::auth::routes::login_html();
    let mut held = 0;
    for (start, end) in [("/*@left-half*/", "/*@left-half-end*/"), ("/*@left-half-phone*/", "/*@left-half-phone-end*/")] {
        for rule in rules_between(page(), start, end) {
            assert!(login.contains(&rule), "the sign-up page's left half has drifted from login.html:\n{rule}");
            held += 1;
        }
    }
    assert!(held >= 3, "the backdrop, the column and the phone rule: {held}");
    for both in [r#"<div class="art" aria-hidden="true"></div>"#, r#"--bg: #f4f4f4;"#, r#"<meta name="color-scheme" content="light">"#] {
        assert!(page().contains(both) && login.contains(both), "{both}");
    }
}

/// ★ **The heading follows the situation, and "a new" is always a new
/// instance.** The four headings and the two buttons, in the owner's words.
#[test]
fn the_heading_follows_the_situation() {
    let html = page();
    assert!(html.contains("const FIRST_ACCOUNT_HEADING = 'Sign up for an account';"));
    assert!(html.contains(r#"<h1 id="heading">Sign up for an account</h1>"#), "the heading before the script runs");
    assert!(html.contains("const words = (firstAccount() ? FIRST_ACCOUNT_HEADING\n    : page.mode === 'grow-account' ? growWords() : HEADINGS[page.mode]).replace('{company}', company);"), "a visitor's heading is the account's");
    for (mode, words) in [
        ("private", "New private Gunnar"),
        ("grow-account", "Grow your account"),
        ("grow-instance", "Grow {company}’s Gunnar"),
    ] {
        assert!(html.contains(&format!("'{mode}': '{words}'")), "the heading for {mode} is not {words:?}");
    }
    // ★ "Pay <amount>" on the private choice (owner 2026-10-22): the step's amount as monetize
    // words it (`pay_words`); the other buttons as they were.
    assert!(html.contains("const BUTTONS = { 'private': 'Pay {amount}', 'account': 'Pay {amount}', 'grow-account': 'Upgrade', 'grow-instance': 'Upgrade' };"));
    // ★ The owner's standing buy rule (2026-10-23, A14 audit): a PAID plan's button says "Pay
    // <amount>", never "Sign up"; the free account keeps its own words.
    assert!(html.contains("if (step && step.id !== FREE_ACCOUNT.id) return BUTTONS.account.replace('{amount}', amountOf(step.month));"), "a paid plan row says Pay <amount>");
    assert!(html.contains("return FIRST_ACCOUNT_BUTTON.replace('{host}', location.hostname);"), "the free account keeps its words");
    assert!(!html.contains(">Sign up</button>"), "never Sign up on the button");
    assert!(html.contains("return (step && step.pay) ? BUTTONS.private.replace('{amount}', step.pay) : 'Pay';"), "the amount is the step's");
    assert!(html.contains("pay: s.pay_words"), "monetize's words for the amount");
    assert!(html.contains("if (!page.busy) go.textContent = buttonWords();"), "the button follows the − / +");
    // Signed in, the choice is "New instance" or a growth — and the new one
    // is the private heading, never an increase.
    assert!(html.contains("if (s.private_url) out.push({ mode: 'private', words: TAB_PRIVATE });"), "BUY-ONE-2: New private instance, signed in too");
    // A visitor's pill (owner 2026-10-15): "Account on <host>", the host the
    // page was served from. The "Private Gunnar instance" pill beside it is
    // HIDDEN since 2026-10-18 (owner, wide audience): behind a flag that is off.
    assert!(html.contains("out.push({ mode: 'account', words: 'Account on ' + location.hostname });"), "the account pill");
    assert!(html.contains("const OFFER_PRIVATE_TO_VISITORS = true;"), "the visitor's private pill is offered (owner 2026-10-21)");
    assert!(html.contains("if (OFFER_PRIVATE_TO_VISITORS && s.private_url) out.push({ mode: 'private', words: TAB_PRIVATE });"));
    // No plan from monetize (`/auth/status` account_offer), no account pill.
    assert!(html.contains("if (s.account_offer && s.buy_url && s.buy_url.startsWith('/signup')) out.push({ mode: 'account',"));
    assert!(!html.contains("'10GB/month on '") && !html.contains("'/month on '"), "the old account pill");
    // ★ With no `?plan=`, nothing is preselected (ALICA-2, owner 2026-10-26).
    assert!(html.contains("             : '';") && html.contains("if (!want && chosen.length >= 2) {"), "nothing is preselected without ?plan=");
    // The account ladder reads what the server sends: no total_words, no reason.
    let account = &html[html.find("} else if (mode === 'account') {").expect("the account ladder")..];
    let account = &account[..account.find("} else if (mode === 'grow-account') {").expect("its end")];
    assert!(!account.contains("p.total_words") && !account.contains("p.reason"), "dead reads in the account ladder: {account}");
    assert!(html.contains("'account': FIRST_ACCOUNT_HEADING,"), "the account heading changed");
    // A sold instance's private Gunnar is its provider's page: followed, and
    // only an https address on another site is.
    assert!(html.contains("if (mode === 'private' && elsewhere((page.status || {}).private_url))"));
    assert!(html.contains("url.startsWith('https://') && new URL(url).origin !== location.origin"));
    // The signed-in account is filled in and locked.
    assert!(html.contains("acct.readOnly = true;"));
    // Every order names its owner (the ownership contract): the account name
    // is a required field, and the page checks it before anything is sent.
    assert!(html.contains(r#"spellcheck="false" maxlength="32" placeholder="e.g. alice" required>"#), "the account name is not marked required");
    assert!(html.contains("!RULE.test(account)"));
}

/// **ONE − and ONE + for the whole setup.**
#[test]
fn one_minus_and_one_plus_for_the_whole_setup() {
    let html = page();
    assert_eq!(html.matches(r#"id="minus""#).count(), 1);
    assert_eq!(html.matches(r#"id="plus""#).count(), 1);
    assert_eq!(html.matches(r#"<button type="button" id="minus" aria-label="Smaller">−</button>"#).count(), 1);
    assert_eq!(html.matches(r#"<button type="button" id="plus" aria-label="Bigger">+</button>"#).count(), 1);
    // The floor is the server's: the − stops at it.
    assert!(html.contains("$('minus').disabled = page.at <= page.floor;"));
}

/// ★ **The picture**: three hosts, a double patterned ring, the caption — and
/// REPLICA is one constant (the owner wrote REPLIKA; changing it is one line).
#[test]
fn the_picture_is_three_hosts_in_a_double_ring_with_the_caption() {
    let html = page();
    assert_eq!(html.matches("<svg").count(), 1, "one inline svg");
    for id in ["ring-outer", "ring-inner", "box-front", "box-main", "box-twin", "pic-caption"] {
        assert!(html.contains(&format!(r#"id="{id}""#)), "{id}");
    }
    assert!(html.contains(".estate .ring.outer { stroke-dasharray:") && html.contains(".estate .ring.inner { stroke-dasharray:"), "two dashed strokes");
    assert!(html.contains(">Gunnar instance protected by VPN</text>"));
    assert!(html.contains("const HOST_LABELS = { front: 'UI', main: 'SERVER', twin: 'REPLICA' };"));
    assert_eq!(html.matches("'REPLICA'").count(), 1, "REPLICA is one constant");
    // About a sixth of the screen's height.
    assert!(html.contains("height: clamp(120px, 16.6vh, 190px)"));
}

/// ★ **The page holds no price, no plan and no step of its own**: Mini …
/// Gigantic, the cores and the euros are monetize's answers.
#[test]
fn the_page_holds_no_price_no_plan_and_no_step_of_its_own() {
    let html = page();
    for money in ["EUR", "DKK", "SEK", "€", "price_minor", "* 1.2", "1.20"] {
        assert!(!html.contains(money), "the page carries {money:?}");
    }
    for plan in ["STARTER-", "xCPU", "HIMEM"] {
        assert!(!html.contains(plan), "the page names a plan: {plan:?}");
    }
    let words: Vec<&str> = html.split(|c: char| !c.is_ascii_alphanumeric()).collect();
    for step in ["Mini", "Small", "Medium", "Large", "Huge", "Gigantic"] {
        assert!(!words.contains(&step), "the page names a step of its own: {step:?}");
    }
    // What it renders, it renders from the server's fields.
    for field in ["s.resources_words", "s.monthly_words", "s.total_words", "s.reason", "p.price_words", "o.price_words"] {
        assert!(html.contains(field), "{field}");
    }
}

/// **The front page's ONE paid link opens this page** (owner 2026-10-08:
/// "Sign up for paid service", to /signup, where the visitor chooses) —
/// `?plan=` is still read once, for the console's own links that preselect.
#[test]
fn the_page_reads_its_preselected_plan_from_the_front_pages_links() {
    let html = page();
    assert!(html.contains("const asked = (q.get('plan') || '').toLowerCase();"));
    assert!(html.contains("asked === '10gb'"));
    #[cfg(feature = "metered")]
    {
        let login = crate::auth::routes::login_html();
        // LOGIN-1 (owner 2026-10-27): /login is the login; the front page's links lead here.
        assert!(!login.contains("paid-link") && !login.contains("Sign up account"), "the login offers no sign-up");
        assert!(!login.contains("Buy 10GB/month") && !login.contains("Buy a private Gunnar instance"), "the two old offers");
    }
}

/// ★ **The account's − / + steps through SIZES of ONE currency** (test2,
/// owner 2026-10-08: on /signup?plan=10gb every + changed the CURRENCY). The
/// server decides the market — from the browser's languages, else its
/// default — and answers one market's plans; the page renders them and
/// sends that market back with the order. It guesses no market of its own.
#[test]
fn the_account_ladder_is_one_market_and_the_servers() {
    let html = page();
    for own in ["navigator.language", "marketOf", "'DK'", "'FI'", "'SE'"] {
        assert!(!html.contains(own), "the page decides the market itself: {own}");
    }
    assert!(html.contains("fetch('/auth/signup/ladder', { credentials: 'same-origin' })"), "the ladder is asked with no market of the page's");
    assert!(html.contains("page.market = j.market || '';"), "the server's market is kept");
    // ST-10: the priced checkout is the account's own (`buy-bundle`), priced in the market
    // the server reads from the same browser; the page sends none of its own.
    assert!(!html.contains("market: page.market"), "the page names a market of its own to an order");
}

/// ★ **No configuration sentence, and no private choice where none is sold**
/// (test2, owner 2026-10-08: "this monetize sells no private estates
/// ([estate_orders] is absent)" on the page, and no − / +). The private
/// choice is drawn only from /auth/status's private door, which the server
/// gives only where private Gunnars are sold; the page names no setting.
#[test]
fn the_private_choice_is_the_servers_and_the_page_names_no_setting() {
    let html = page();
    assert!(html.contains("if (OFFER_PRIVATE_TO_VISITORS && s.private_url) out.push({ mode: 'private', words: TAB_PRIVATE });"));
    assert!(html.contains("if (s.private_url) out.push({ mode: 'private', words: TAB_PRIVATE });"));
    assert!(html.contains("const TAB_PRIVATE = 'New private instance';"), "BUY-ONE-2's tab name");
    for config in ["estate_orders", "monetize.toml", "[signup", "sells no private"] {
        assert!(!html.contains(config), "the page carries a configuration word: {config}");
    }
}

/// **Light, one inline script, every sentence built as text** (the CSP hashes
/// the one script; nothing is parsed as markup).
#[test]
fn the_page_is_light_carries_one_script_and_writes_no_markup() {
    let html = page();
    assert!(html.contains(r#"<html lang="en" data-theme="light">"#));
    assert!(!html.contains("prefers-color-scheme: dark"));
    assert_eq!(crate::security::inline_script_hashes(html).len(), 1);
    assert!(!html.contains("innerHTML"));
    assert!(!html.contains("insertAdjacentHTML"));
    // Only an https payment page is followed.
    assert!(html.contains("url.startsWith('https://')"));
    // The username rule, mirrored from monetize_pay::signup::USERNAME_RULE.
    assert!(html.contains("/^[a-z0-9][a-z0-9._-]{1,31}$/"));
}

/// ★ **A visitor buying a private Gunnar pays ONCE** (owner 2026-10-22, "ja! så måste det
/// vara!"): no free account first on the private choice — the account name and the e-mail
/// typed once go with the order straight to its payment, and the account is made when it is
/// paid. The free account (and its card check) is the account choice's alone.
#[test]
fn a_visitor_buying_a_private_gunnar_pays_once() {
    let html = page();
    assert!(html.contains("return page.mode === 'account' && !(s.authenticated && s.user);"), "only the account choice makes the account first");
    assert!(html.contains("const PRIVATE_VISITOR_LINE = 'You pay once. Your account at {host} is made with the payment, and its sign-in link is mailed to you: there you see and grow what you bought.';"));
    assert!(html.contains(": { company, email, account, domain, front_plan: step.sizes.front_plan, main_plan: step.sizes.main_plan, main_data_gib: step.sizes.main_data_gib });"), "the visitor's order names the new account and its e-mail");
    assert!(!html.contains("'/signup?plan=private' }"), "never back to the private choice through a free account");
    assert!(html.contains("plan: 'gunnar/account', next: '/' }"), "the free account goes on to the console");
    assert!(html.contains("'Your {host} account shows everything you have bought and lets you add more."), "what the account is (owner 2026-10-27)");
    assert_eq!(html.matches("'Free account'").count(), 1, "the free account is one row of the owner's words");
    let lower = html.to_lowercase();
    for never in ["free plan", "0 gb", "0 b ", "no room"] {
        assert!(!lower.contains(never), "the page says {never:?}");
    }
}

/// ★ **Grow from "Your purchases" names the instance** (one shop, owner
/// 2026-10-07): `?plan=grow&instance=<company>` opens the grow of THAT
/// instance (the instance rule, read once), signed in on the shop — not the
/// account's room.
#[test]
fn grow_from_your_purchases_names_the_instance() {
    let html = page();
    assert!(html.contains("const asked_instance = (q.get('instance') || '').toLowerCase();"), "read once");
    assert!(html.contains("COMPANY_RULE.test(asked_instance)"), "the instance rule");
    assert!(html.contains("asked === 'grow' ? ((page.status.instance || page.instance) ? 'grow-instance' : 'grow-account')"), "grow of the instance named");
}

/// ★ **Right after paying, the page says a mail is on its way** (owner
/// 2026-10-11, from a live purchase): "Payment received. We've sent a sign-in
/// link to <masked email>. Open it to register your security key." and a
/// [Send again] that asks the estate for a new link (S10,
/// `/auth/private/sign-in-again`). Before the instance is ready the link is
/// not sent yet, and the page says WHEN it will be — never a mail that has
/// not gone. The address is the buyer's own, typed on this page before the
/// payment: only its MASKED form is kept (this tab's sessionStorage), and
/// nothing is sent anywhere it was not sent before.
#[test]
fn after_paying_the_page_says_where_the_sign_in_link_went() {
    let page = page();
    let watch = &page[page.find("async function watch(company)").expect("the order's watch")..];
    let watch = &watch[..watch.find("\n}\n").expect("its end")];
    assert!(watch.contains("'Payment received. We’ve sent a sign-in link to ' + mailed + '. Open it to register your security key.'"), "the ready line: {watch}");
    assert!(watch.contains("'Payment received. When ' + company + ' is ready we’ll send a sign-in link to ' + mailed + '.'"), "the before-ready line: {watch}");
    assert!(watch.contains("sendAgain(company)"), "no [Send again] after paying: {watch}");
    let again = &page[page.find("function sendAgain(company)").expect("the one Send again")..];
    let again = &again[..again.find("\n}\n").expect("its end")];
    assert!(again.contains("'/auth/private/sign-in-again'") && again.contains("'Send again'"), "{again}");
    // Only the masked address is kept, and only in this tab.
    assert!(page.contains("function maskEmail(address)"), "no mask");
    assert!(page.contains("sessionStorage.setItem(MAILED_KEY, maskEmail(email))"), "the address is kept unmasked, or not at all");
    assert!(!page.contains("localStorage.setItem(MAILED_KEY"), "the address outlives the tab");
}

/// ★ ST-10 (owner 2026-10-11: he LIKED account-first): **the hosted 10 GB plan makes the
/// account first too** — the same page flow as the private choice: the account name and an
/// e-mail, "Create your account at <host>", the account made with no payment (the shop's
/// account plan), the passkey, and back on THIS choice signed in, where the priced checkout
/// is bought on that account (its own order ticket: `buy-bundle`). The page starts no
/// priced checkout for a visitor with no account: no money ever waits for a name.
#[test]
fn the_hosted_plan_makes_the_account_first_too() {
    let html = page();
    assert!(html.contains("return page.mode === 'account' && !(s.authenticated && s.user);"), "the account choice is account-first (the private one pays once, 2026-10-22)");
    // ★ B (owner 2026-10-15): a plan row pays ONCE, its own checkout; the account follows the payment.
    assert!(html.contains("{ username: account, email, plan: step.id, next: '/' }"), "a plan row is its own checkout");
    assert!(html.contains("asked === '10gb' ? (page.status.authenticated ? 'grow-account' : 'account')"), "signed in, 10gb is the account's own priced checkout");
    assert!(html.contains("post('/api/account/subscription/buy-bundle', { bundle: step.id })"), "the priced checkout on the account");
    assert!(html.contains("'account': 'Your {host} account shows everything you have bought and lets you add more. You pay only for the storage you choose.'"), "what comes after, on the 10 GB choice (owner 2026-10-27)");
    // The address the link goes to, kept MASKED in this tab for the welcome page.
    let first = &html[html.find("if (firstAccount()) {").expect("the account step")..];
    let first = &first[..first.find("} else if").expect("its end")];
    assert!(first.contains("sessionStorage.setItem(MAILED_KEY, maskEmail(email))"), "{first}");
}

/// ★ **TERMINATE, no backup at all** (owner 2026-10-12; "ta ingen backup alls", 2026-10-22):
/// beside [Stop renewing], a [Terminate <company>] that says terminating ends the instance for
/// good — its machines, disks and records go — asks the admin to type the name back, and
/// posts it to `/auth/private/terminate` with no backup word at all (monetize A32-27: the wire
/// carries none). No backup is offered, asked or taken; the button stays disabled until the name typed matches.
#[test]
fn the_instance_pane_terminates_without_a_backup_after_the_name_typed_back() {
    let page = page();
    let pane = &page[page.find("function terminating(company)").expect("the terminate pane")..];
    let pane = &pane[..pane.find("\n}\n").expect("its end")];
    assert!(pane.contains("'Terminating ' + company + ' ends it for good: its machines, disks and records go.'"), "{pane}");
    assert_eq!(pane.matches("'/auth/private/terminate'").count(), 1, "one request, the termination itself: {pane}");
    assert!(pane.contains("{ company, confirm: typed.value.trim() }"), "no backup word on the wire: {pane}");
    for gone in ["radio", "Take a backup", "without a backup", "offered first", "'take'", "backup:"] {
        assert!(!pane.contains(gone), "{gone:?} is gone (no backup at all): {pane}");
    }
    assert!(pane.contains("typed.value.trim() !== company"), "the name typed back: {pane}");
    assert!(pane.contains("go.textContent = 'Terminate ' + company;"), "{pane}");
    let renew = &page[page.find("function renewing(company)").expect("the renew pane")..];
    assert!(renew[..renew.find("\n}\n").unwrap()].contains("terminating(company)"), "the button sits with Stop renewing");
}

/// ★ **The account is a list to pick from; the server picture is a private
/// Gunnar's** (owner 2026-10-15). The free account (read only) first, in the
/// owner's words, once; then every other plan the ONE box sells, named and priced by it —
/// the period read from its `period_days`. The picture, the − / + and the
/// price line are drawn for an instance alone.
#[test]
fn the_account_picks_from_a_list_and_the_picture_is_the_instances() {
    let html = page();
    assert!(html.contains(r#"<div class="plans" id="plans" role="radiogroup" aria-label="Account plans" hidden></div>"#));
    assert!(html.contains("const FREE_ACCOUNT = { id: 'gunnar/account', name: 'Free account', price: 'Free',"));
    assert!(html.contains("note: 'Free: clone only. Pushing needs a paid plan.' };") && html.contains("'account-free': 'A card check at Stripe (0 kr) proves you are a person.',") && html.contains("const BETA_LINE = 'Beta: we replicate your data, but cannot guarantee it.';"), "the free row, its card check and the beta line in the owner's short words (BUY-ONE-2, 2026-10-27)");
    // The ONE box's own rows: its name, its price words, its period. A ONE box that lists the
    // free account itself is not shown a second free row under this page's own (monetize
    // 52e1eaa0): every other plan it sells is a row, in its order.
    assert!(html.contains("const sold = (j.plans || []).filter(p => p.id !== FREE_ACCOUNT.id).map(p => ({ id: p.id, name: planName(p), res: p.resources_words, month: p.price_words,"));
    assert!(html.contains("steps = [{ id: FREE_ACCOUNT.id, name: FREE_ACCOUNT.name, res: '', month: FREE_ACCOUNT.price, total: '', note: FREE_ACCOUNT.note, ok: true, why: '' }].concat(sold);"), "the free account first, once");
    assert!(html.contains("const months = Math.round(Number(p.period_days || 0) / 30);"));
    assert!(html.contains("start = sold.length ? 1 : 0;"), "the first plan sold is not the one chosen");
    // The picture is an instance's; the account's − / + and price are its list.
    assert!(html.contains("$('estate').hidden = !(page.mode === 'private' || page.mode === 'grow-instance');"));
    assert!(html.contains("$('size').hidden = coming || list || page.steps.length === 0;"));
    assert!(html.contains("$('price').hidden = coming || list;"));
    assert!(html.contains(".estate[hidden] { display: none; }") && html.contains(".plans[hidden] { display: none; }"));
    // The free row and a private Gunnar make the free account; a plan row pays for its own (B).
    assert!(html.contains("const planRow = page.mode === 'account' && !freeOnly;"));
    assert!(html.contains("sessionStorage.setItem(CARD_CHECK_KEY, (check && !planRow) ? '1' : '')"), "a plan row's welcome page says Payment received");
}

/// ★ **The free account's card check** (owner 2026-10-15): monetize answers the account's
/// start with Stripe's page (an https checkout, followed like any) instead of this console's
/// welcome page; the page says nothing is charged, and tells the welcome page (this tab only)
/// that nothing was paid — so it never says "Payment received" for it.
#[test]
fn the_free_account_goes_to_stripes_card_check_and_back() {
    let html = page();
    assert!(html.contains("const check = url ? '' : safeCheckout(j.url);"), "a Stripe page is followed as monetize answered it");
    assert!(html.contains("const CARD_CHECK_GOING = 'Taking you to Stripe to check your card. Nothing is charged.';"));
    let welcome = crate::auth::signup::welcome_html();
    assert!(welcome.contains("const CARD_CHECK_KEY = 'gunnar_card_check';") && html.contains("const CARD_CHECK_KEY = 'gunnar_card_check';"), "one key, both pages");
    assert!(welcome.contains("const paid = !session.startsWith('acct_') && !cardChecked();"), "no \"Payment received\" after a card check");
}

/// ★ **The account's own growth says what the Account page's button says**
/// (owner 2026-10-17: "the word on the account button = the heading = the
/// tab"): a FREE account (monetize's plan state `none`) reads "Buy a plan" in
/// the heading and on its tab; an account with a plan "Grow your account".
/// The state is monetize's window, read once before the tabs are drawn and
/// handed to the ladder (one read, one ticket).
#[test]
fn a_free_account_buys_a_plan_and_a_planned_one_grows() {
    let html = page();
    assert!(html.contains("const BUY_A_PLAN = 'Buy a plan';"), "the free account's words");
    assert!(html.contains("function growWords() { return page.planned === false ? BUY_A_PLAN : HEADINGS['grow-account']; }"), "one place decides the words");
    assert!(html.contains("if (s.buy_url) out.push({ mode: 'grow-account', words: TAB_ACCOUNT_SIGNED_IN });") && html.contains("const TAB_ACCOUNT_SIGNED_IN = 'Grow your plan';"), "the tab (BUY-ONE-2: the same tab, \"Grow your plan\")");
    assert!(html.contains(": page.mode === 'grow-account' ? growWords() : HEADINGS[page.mode]"), "the heading");
    assert!(html.contains("page.planned = !!(w.plan && w.plan.state && w.plan.state !== 'none');"), "monetize's state decides");
    assert!(html.contains("const j = page.window || await getJson('/api/account/subscription/bundles');"), "the ladder reuses the one read");
}

// ★ Registering the passkey signs the person in at once (owner 2026-10-21), with that
// passkey only, and a device that will not gets the Sign in button: held in the browser
// (RA2-17), `passkeys_at_gunnar::first_touch` and its `sign_in_after`.

/// ★ **The e-mail is not changed in the buy** (owner 2026-10-22: "man skall inte kunna byta
/// email"): signed in, the private choice has NO e-mail field — the order is the account's and
/// monetize mails the account's own address; the page sends none. A visitor types it once, as
/// the new account's. The account line is short: "<name> (you)".
#[test]
fn signed_in_the_private_order_asks_no_email_and_names_the_account_short() {
    let html = page();
    assert!(html.contains("reveal($('f-email'), (m === 'private' || m === 'account') && !signedIn);"), "no e-mail field signed in");
    assert!(html.contains("? { company, domain, front_plan: step.sizes.front_plan, main_plan: step.sizes.main_plan, main_data_gib: step.sizes.main_data_gib }"), "signed in, the order sends no e-mail and no name");
    // ★ The account line "<name> (you)" on EVERY choice signed in (owner 2026-10-23, A14 audit).
    assert!(html.contains("$('account-hint').textContent = s.user + ' (you)';"), "the short account line");
    assert!(html.contains("$('f-account').hidden = !(signedIn || m === 'private' || m === 'account' || m === 'grow-account');"), "on every choice");
    assert!(html.contains("reveal($('form'), !(m === 'grow-instance' && !signedIn));"), "grow-instance too");
    for gone in ["EMAIL_WHY", "PRIVATE_EMAIL_KEY", "keptPrivateEmail", "signed in — the new instance is yours", "E-mail for the “ready” mail"] {
        assert!(!html.contains(gone), "{gone} is still on the page");
    }
}

/// ★ **"Manage <company>" beside "Grow <company>'s Gunnar"** (owner 2026-10-22): a signed-in
/// account that owns an instance reaches the page that renews, stops renewing and terminates
/// it (`/signup?plan=private&renew=<company>`, [`renewing`]) from the choice row — not only from
/// the renewal mail's link. The instance is the one /auth/status (or `?instance=`) already
/// names; no instance, no Manage. It is a link, never a mode of this page.
#[test]
fn a_signed_in_owner_of_an_instance_gets_manage_beside_grow() {
    let html = page();
    assert!(html.contains("const MANAGE = 'Manage {company}';"), "the owner's word, in one place");
    let at = html.find("function choices()").expect("choices()");
    let choices = &html[at..at + html[at..].find("\n}\n").expect("its end")];
    let grow = choices.find("mode: 'grow-instance'").expect("the grow choice");
    let manage = choices.find("mode: 'manage'").expect("a signed-in owner of an instance gets Manage");
    assert!(manage > grow, "Manage sits beside Grow: {choices}");
    let signed_in = &choices[choices.find("if (grows)").expect("only when the account owns an instance")..];
    assert!(signed_in[..signed_in.find("\n    }").expect("the block's end")].contains("mode: 'manage'"), "Manage only with an instance: {choices}");
    assert!(choices.contains("href: manageUrl(grows)"), "{choices}");
    assert!(
        html.contains("function manageUrl(company) { return SIGNUP_PRIVATE_RENEW + encodeURIComponent(company); }"),
        "the renew pane's own address"
    );
    assert!(html.contains("const SIGNUP_PRIVATE_RENEW = '/signup?plan=private&renew=';"));
    assert_eq!(
        format!("{}&renew=", crate::auth::routes::SIGNUP_PRIVATE),
        "/signup?plan=private&renew=",
        "the same address Your purchases' row gets from the server"
    );
    // Drawn as a link; never a mode the page preselects or chooses.
    let draw = &html[html.find("function drawChoices(list)").expect("drawChoices")..];
    let draw = &draw[..draw.find("\n}\n").unwrap()];
    assert!(draw.contains("if (c.href)") && draw.contains("a.href = c.href;"), "{draw}");
    assert!(html.contains("const here = list.filter(c => !c.href && "), "a link is never preselected");
    assert!(html.contains(".choice button, .choice a {"), "the link looks like its neighbours");
}