use super::{publish, server_mode, zip_of};
use crate::adapters::chrome::testing::{self, eval, until};
fn doc(text: &str) -> Vec<u8> {
zip_of(&[
(
"devpage.yml",
"kind: doc\ntitle: Rollout\nchapters:\n - rollout.md\n",
),
("rollout.md", &format!("# Rollout\n\n{text}\n")),
])
}
async fn start(name: &str) -> String {
testing::serve(server_mode(name)).await
}
const TEXT: &str = "document.getElementById('devpage-page').textContent";
const MENU: &str = "document.querySelector('#devpage-versions summary')?.textContent";
#[tokio::test(flavor = "multi_thread")]
#[ignore = "drives Chrome"]
async fn a_page_open_at_the_link_swaps_in_each_new_version() {
let base = start("live").await;
let id = publish(&base, None, doc("The first plan.")).await;
let browser = testing::browser().await;
let page = testing::open(&browser, &format!("{base}/d/{id}/")).await;
until(&page, &format!("{MENU} === 'Version 1 · latest'")).await;
assert!(
eval::<String>(&page, TEXT)
.await
.contains("The first plan.")
);
let at: String = eval(&page, "location.pathname").await;
assert_eq!(at, format!("/d/{id}/devpage.yml"));
eval::<bool>(&page, "window.sameLoad = true").await;
publish(&base, Some(&id), doc("The second plan.")).await;
until(&page, &format!("{TEXT}.includes('The second plan.')")).await;
until(&page, &format!("{MENU} === 'Version 2 · latest'")).await;
assert_eq!(eval::<String>(&page, "location.pathname").await, at);
assert!(
eval::<bool>(&page, "window.sameLoad === true").await,
"swapped, not reloaded"
);
publish(&base, Some(&id), doc("The third plan.")).await;
until(&page, &format!("{TEXT}.includes('The third plan.')")).await;
assert!(
eval::<bool>(&page, "!document.getElementById('devpage-version-banner')").await,
"the latest has no banner"
);
drop(browser);
}
#[tokio::test(flavor = "multi_thread")]
#[ignore = "drives Chrome"]
async fn an_old_version_says_so_never_changes_and_lists_every_version() {
let base = start("old").await;
let id = publish(&base, None, doc("The first plan.")).await;
publish(&base, Some(&id), doc("The second plan.")).await;
let browser = testing::browser().await;
let page = testing::open(&browser, &format!("{base}/d/{id}/v/1/")).await;
until(&page, "!!document.getElementById('devpage-version-banner')").await;
let banner: String = eval(
&page,
"document.getElementById('devpage-version-banner').textContent",
)
.await;
assert_eq!(
banner,
"You are reading version 1; the latest is version 2."
);
let latest: String = eval(
&page,
"new URL(document.querySelector('#devpage-version-banner a').href).pathname",
)
.await;
assert_eq!(latest, format!("/d/{id}/"));
assert_eq!(eval::<String>(&page, MENU).await, "Version 1");
let listed: Vec<String> = eval(
&page,
"[...document.querySelectorAll('.version-menu a')].map((a) => new URL(a.href).pathname)",
)
.await;
assert_eq!(listed, [format!("/d/{id}/v/2/"), format!("/d/{id}/v/1/")]);
let current: String = eval(
&page,
"document.querySelector('.version-menu a[aria-current]').textContent",
)
.await;
assert_eq!(current, "Version 1");
let latest = testing::open(&browser, &format!("{base}/d/{id}/")).await;
until(&latest, &format!("{MENU} === 'Version 2 · latest'")).await;
publish(&base, Some(&id), doc("The third plan.")).await;
until(&latest, &format!("{TEXT}.includes('The third plan.')")).await;
let text: String = eval(&page, TEXT).await;
assert!(text.contains("The first plan."), "{text}");
drop(browser);
}
#[tokio::test(flavor = "multi_thread")]
#[ignore = "drives Chrome"]
async fn a_docs_download_button_saves_its_pdf_printed_once() {
let base = start("pdf").await;
let id = publish(&base, None, doc("The plan, on paper.")).await;
let browser = testing::browser().await;
let page = testing::open(&browser, &format!("{base}/d/{id}/")).await;
until(&page, "!!document.querySelector('.version-download')").await;
assert_eq!(
eval::<String>(
&page,
"document.querySelector('.version-download').textContent"
)
.await,
"Download PDF"
);
let href: String = eval(
&page,
"new URL(document.querySelector('.version-download').href).pathname",
)
.await;
assert_eq!(href, format!("/d/{id}/v/1/download"));
drop(browser);
let first = std::time::Instant::now();
let response = reqwest::get(format!("{base}{href}")).await.unwrap();
assert_eq!(response.status(), 200);
assert_eq!(response.headers()["content-type"], "application/pdf");
assert_eq!(
response.headers()["content-disposition"],
"attachment; filename=\"Rollout.pdf\""
);
let pdf = response.bytes().await.unwrap();
assert!(pdf.starts_with(b"%PDF"), "a PDF");
let printed = first.elapsed();
let second = std::time::Instant::now();
let again = reqwest::get(format!("{base}{href}"))
.await
.unwrap()
.bytes()
.await
.unwrap();
assert_eq!(again, pdf, "the same PDF, from the cache");
assert!(
second.elapsed() * 5 < printed,
"read, not printed again: {:?} after {printed:?}",
second.elapsed()
);
}
#[tokio::test(flavor = "multi_thread")]
#[ignore = "drives Chrome"]
async fn the_banner_and_the_version_bar_cover_nothing() {
use chromiumoxide::cdp::browser_protocol::emulation::SetDeviceMetricsOverrideParams;
let base = start("cover").await;
let id = publish(&base, None, doc("The first plan.")).await;
publish(&base, Some(&id), doc("The second plan.")).await;
let mut files = vec![(
"devpage.yml".to_string(),
"kind: site\ntitle: Long site\nroot: README.md\n".to_string(),
)];
let mut readme = "# Home\n\n## Topics\n\n".to_string();
for i in 1..=40 {
readme += &format!("- [Page {i}](p{i}.md) — page {i}\n");
files.push((format!("p{i}.md"), format!("# Page {i}\n")));
}
files.push(("README.md".into(), readme));
let files: Vec<(&str, &str)> = files
.iter()
.map(|(a, b)| (a.as_str(), b.as_str()))
.collect();
let site = publish(&base, None, zip_of(&files)).await;
publish(&base, Some(&site), zip_of(&files)).await;
let browser = testing::browser().await;
const SHOWN: &str = "!!document.querySelector('#devpage-versions summary')
&& !!document.getElementById('devpage-version-banner')";
for (width, height) in [(1400, 900), (800, 600)] {
let page = testing::open(&browser, &format!("{base}/d/{id}/v/1/")).await;
page.execute(SetDeviceMetricsOverrideParams::new(
width, height, 1.0, false,
))
.await
.unwrap();
until(&page, SHOWN).await;
let gap: f64 = eval(
&page,
"document.getElementById('devpage-page').getBoundingClientRect().top
- document.getElementById('devpage-version-banner').getBoundingClientRect().bottom",
)
.await;
assert!(
gap >= 0.0,
"at {width}px the banner covers the page by {}px",
-gap
);
page.close().await.ok();
}
let page = testing::open(&browser, &format!("{base}/d/{site}/v/1/")).await;
page.execute(SetDeviceMetricsOverrideParams::new(1400, 700, 1.0, false))
.await
.unwrap();
until(&page, SHOWN).await;
let gap: f64 = eval(
&page,
"(() => { const nav = document.getElementById('devpage-nav');
nav.scrollTop = nav.scrollHeight;
const links = nav.querySelectorAll('a');
return document.getElementById('devpage-versions').getBoundingClientRect().top
- links[links.length - 1].getBoundingClientRect().bottom; })()",
)
.await;
assert!(
gap >= 0.0,
"the version bar covers the sidebar's last link by {}px",
-gap
);
page.close().await.ok();
drop(browser);
}