crates/holger-ui-server/tests/buyer_words.rs
//! ★ **Lane BUYFLOW-4: Holger's buy pages say buyer words only** (owner: customer-never-sees-caps,
//! one-monetize-only). The text a buyer sees on the sign-up, welcome and paid pages names none
//! of the operator's machinery. The list is
//! monetize-site-buy's `rules::OPERATOR_WORDS` (this crate builds from a lone clone and links
//! no sibling); the shared scripts' own sentences (buy.js, welcome.js, paid.js) are checked there. The footer's company
//! name is the seller's.
const OPERATOR_WORDS: &[&str] = &["lay", "lays", "laid", "laying", "estate", "estates", "guest", "guests", "operator", "operators", "cap", "caps", "monetize", "tenant", "tenants", "appliance", "vendor", "vendors", "ignalina"];
fn operator_words_in(words: &str) -> bool {
let w = words.to_lowercase();
w.contains("one box") || w.split(|c: char| !c.is_alphanumeric()).any(|t| OPERATOR_WORDS.contains(&t))
}
/// What of an HTML page a buyer reads: its text and its labels.
fn visible(html: &str) -> Vec<String> {
let mut s = html.to_owned();
for (open, close) in [("<!--", "-->"), ("<style", "</style>"), ("<script", "</script>")] {
while let Some(a) = s.find(open) {
let b = s[a..].find(close).map(|b| a + b + close.len()).unwrap_or(s.len());
s.replace_range(a..b, "\n");
}
}
let mut out = Vec::new();
for attr in ["aria-label=\"", "title=\"", "placeholder=\"", "alt=\""] {
for part in s.split(attr).skip(1) {
out.push(part.split('"').next().unwrap_or_default().to_owned());
}
}
let mut text = String::new();
let mut in_tag = false;
for c in s.chars() {
match c {
'<' => in_tag = true,
'>' => {
in_tag = false;
text.push('\n');
}
c if !in_tag => text.push(c),
_ => {}
}
}
out.extend(text.lines().map(str::trim).filter(|l| !l.is_empty()).map(str::to_owned));
out
}
/// The string literals of a script that read as a sentence (a space in them), comment lines
/// left out.
fn sentences(js: &str) -> Vec<String> {
let mut out = Vec::new();
for line in js.lines().filter(|l| !l.trim_start().starts_with("//") && !l.trim_start().starts_with('*')) {
let mut open: Option<char> = None;
let mut cur = String::new();
let mut prev = ' ';
let mut chars = line.chars().peekable();
while let Some(c) = chars.next() {
match open {
None if (c == '\'' || c == '"' || c == '`') && !(c == '\'' && prev.is_alphanumeric()) => {
open = Some(c);
cur.clear();
}
None if c == '/' && chars.peek() == Some(&'/') => break,
Some(_) if c == '\\' => {
chars.next();
}
Some(q) if c == q => {
if cur.contains(' ') {
out.push(cur.clone());
}
open = None;
}
Some(_) => cur.push(c),
None => {}
}
prev = c;
}
}
out
}
#[test]
fn holgers_buy_pages_name_no_operator_machinery() {
let mut bad = Vec::new();
for (name, page) in [("signup.html", include_str!("../../../assets/signup.html")), ("welcome.html", include_str!("../../../assets/welcome.html")), ("paid.html", include_str!("../../../assets/paid.html"))] {
for line in visible(page) {
if operator_words_in(&line.replace("Ignalina ApS", "")) {
bad.push(format!("{name}: {line}"));
}
}
}
assert!(bad.is_empty(), "a buyer reads the operator's words:\n{}", bad.join("\n"));
}
#[test]
fn the_readers_read_what_a_buyer_sees() {
assert_eq!(visible("<!-- the estate --><style>.estate{}</style><p title=\"An operator's cap\">Your instance</p><script>x('laid')</script>"), vec!["An operator's cap".to_owned(), "Your instance".to_owned()]);
assert_eq!(sentences("show('Your estate is laid', 'err'); // a lay"), vec!["Your estate is laid".to_owned()]);
assert!(operator_words_in("The operator's cap") && !operator_words_in("a capacity, capped"));
}