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

nordisk/holger-ui

browser-tests/tests/open_bundle.rs

//! ★ A28 M13: **the open bundle speaks no money**, checked on the BUILT bundles, beside
//! holger-ui's source guard (`tests/open_bundle_speaks_no_money.rs`, which judges the literals).
//!
//! * the files: the open `dist/` carries none of the paid half's pages, the metered one all;
//! * the module: the open wasm holds none of the paid console's words, the metered wasm every
//!   one (so the check sees what it looks for);
//! * the browser: the open console, served as its files, draws no "My plan" tab, and `/plan`
//!   is no page of it.

use std::net::SocketAddr;
use std::path::{Path, PathBuf};

use holger_ui_browser_tests::browser::Browser;
use holger_ui_browser_tests::bundle::{dist, dist_open};

/// The paid half's pages: the metered page copies them, the open page does not.
const PAID_FILES: [&str; 3] = ["signup.html", "welcome.html", "paid.html"];

/// Words only the paid console renders, each a literal of a `metered` item: `pages::plan`,
/// `crate::purchase`, the topbar's buy link, "More room" and the admin's payments card.
const PAID_WORDS: [&str; 6] = ["My plan", "Buy a plan", "Past purchases", "Resume payment", "Buy more room", "Stripe webhook"];

fn holds(hay: &[u8], needle: &str) -> bool {
    hay.windows(needle.len()).any(|w| w == needle.as_bytes())
}

/// The bundle's wasm module, by trunk's suffix or wasm-bindgen's name.
fn module(dir: &Path) -> Vec<u8> {
    let path = std::fs::read_dir(dir).unwrap().flatten().map(|e| e.path()).find(|p| p.to_string_lossy().ends_with("_bg.wasm")).unwrap_or_else(|| panic!("no wasm in {}", dir.display()));
    std::fs::read(path).unwrap()
}

#[test]
fn the_open_bundle_carries_no_paid_page_and_no_paid_word() {
    let (open, paid) = (dist_open(), dist());
    for f in PAID_FILES {
        assert!(!open.join(f).exists(), "the OPEN bundle carries {f}");
        assert!(paid.join(f).is_file(), "the metered bundle lacks {f}");
    }
    assert!(open.join("login.html").is_file() && open.join("index.html").is_file(), "the open bundle is a console");
    let (o, m) = (module(open), module(paid));
    for w in PAID_WORDS {
        assert!(!holds(&o, w), "the OPEN console's module holds {w:?}");
        assert!(holds(&m, w), "the metered module lacks {w:?}, so this check would see nothing");
    }
}

/// Serves a bundle's files as a static host does: the file at its path, else `index.html`.
async fn serve(dir: PathBuf) -> SocketAddr {
    use axum::http::{header, StatusCode, Uri};
    use axum::response::IntoResponse;
    let app = axum::Router::new().fallback(move |uri: Uri| {
        let dir = dir.clone();
        async move {
            let rel = uri.path().trim_start_matches('/');
            let file = Some(dir.join(rel)).filter(|p| !rel.is_empty() && p.is_file()).unwrap_or_else(|| dir.join("index.html"));
            let kind = match file.extension().and_then(|e| e.to_str()) {
                Some("js") => "text/javascript",
                Some("wasm") => "application/wasm",
                Some("css") => "text/css",
                Some("webp") => "image/webp",
                _ => "text/html; charset=utf-8",
            };
            match std::fs::read(&file) {
                Ok(bytes) => ([(header::CONTENT_TYPE, kind)], bytes).into_response(),
                Err(_) => StatusCode::NOT_FOUND.into_response(),
            }
        }
    });
    let listener = tokio::net::TcpListener::bind("127.0.0.1:0").await.unwrap();
    let addr = listener.local_addr().unwrap();
    tokio::spawn(async move { axum::serve(listener, app).await.unwrap() });
    addr
}

#[tokio::test(flavor = "multi_thread", worker_threads = 4)]
async fn the_open_console_draws_no_plan_and_has_no_plan_page() {
    let addr = serve(dist_open().to_path_buf()).await;
    let browser = Browser::launch().await.expect("chromium starts");
    let tab = browser.tab().await.unwrap();

    tab.open(&format!("http://{addr}/mine")).await.unwrap();
    // HOL-UI-FIX: the person's own pages are the Account entry's tabs (once a menu entry "Mine").
    tab.until("the Account tabs", "document.querySelectorAll('nav[aria-label=\"Account\"] a').length > 0").await;
    let tabs = tab.js("[...document.querySelectorAll('nav[aria-label=\"Account\"] a')].map(a => a.textContent.trim())").await.unwrap();
    assert_eq!(tabs, serde_json::json!(["Account", "My repositories", "My tokens"]), "the open console's Account tabs");

    tab.open(&format!("http://{addr}/plan")).await.unwrap();
    tab.until("/plan is no page", "document.body.innerText.includes('no such page')").await;
    let text = tab.text().await;
    for w in PAID_WORDS {
        assert!(!text.contains(w), "the open console draws {w:?}: {text}");
    }
}