kanidm/server/testkit/tests/integration.rs

196 lines
6.1 KiB
Rust
Raw Normal View History

//! Integration tests using browser automation
/// Tries to handle closing the webdriver session if there's an error
#[allow(unused_macros)]
macro_rules! handle_error {
($client:ident, $e:expr, $msg:expr) => {
match $e {
Ok(e) => e,
Err(e) => {
$client.close().await.unwrap();
panic!("{:?}: {:?}", $msg, e);
}
}
};
}
/// Tries to get the webdriver client, trying the default chromedriver port if the default selenium port doesn't work
#[allow(dead_code)]
#[cfg(feature = "webdriver")]
async fn get_webdriver_client() -> fantoccini::Client {
use fantoccini::wd::Capabilities;
use serde_json::json;
// check if the env var "CI" is set
let in_ci = match std::env::var("CI") {
Ok(_) => true,
Err(_) => false,
};
if !in_ci {
match fantoccini::ClientBuilder::native()
.connect("http://localhost:4444")
.await
{
Ok(val) => val,
Err(_) => {
// trying the default chromedriver port
eprintln!("Couldn't connect on 4444, trying 9515");
fantoccini::ClientBuilder::new(hyper_tls::HttpsConnector::new())
.connect("http://localhost:9515")
.await
.unwrap()
}
}
} else {
println!("In CI setting headless and assuming Chrome");
let cap = json!({
"goog:chromeOptions" : {
"args" : ["--headless", "--no-sandbox", "--disable-gpu", "--disable-dev-shm-usage", "--window-size=1280,1024"]
}
});
let cap: Capabilities = serde_json::from_value(cap).unwrap();
fantoccini::ClientBuilder::new(hyper_tls::HttpsConnector::new())
.capabilities(cap)
.connect("http://localhost:9515")
.await
.unwrap()
}
}
#[kanidmd_testkit::test]
#[cfg(feature = "webdriver")]
async fn test_webdriver_user_login(rsclient: kanidm_client::KanidmClient) {
if !cfg!(feature = "webdriver") {
println!("Skipping test as webdriver feature is not enabled!");
return;
}
use fantoccini::elements::Element;
use fantoccini::Locator;
use kanidmd_testkit::*;
use std::time::Duration;
login_put_admin_idm_admins(&rsclient).await;
create_user_with_all_attrs(
&rsclient,
NOT_ADMIN_TEST_USERNAME,
Some(NOT_ADMIN_TEST_PASSWORD),
)
.await;
let c = get_webdriver_client().await;
handle_error!(c, c.goto(rsclient.get_url()).await, "Couldn't get URL");
println!("Waiting for page to load");
let mut wait_attempts = 0;
while wait_attempts < 10 {
tokio::time::sleep(tokio::time::Duration::from_micros(200)).await;
c.wait();
if c.find(Locator::Id("username")).await.is_ok() {
break;
}
wait_attempts += 1;
if wait_attempts > 10 {
panic!("Couldn't find username field after 10 attempts!");
}
}
let id = handle_error!(
c,
c.find(Locator::Id("username")).await,
"Couldn't find input id=username"
);
handle_error!(c, id.click().await, "Couldn't click the username input?");
handle_error!(
c,
id.send_keys(NOT_ADMIN_TEST_USERNAME).await,
"Couldn't type the password?"
);
let username_form = handle_error!(
c,
c.form(Locator::Id("login")).await,
"Coudln't find login form"
);
handle_error!(
c,
username_form.submit().await,
"Couldn't submit username-login form"
);
c.wait();
tokio::time::sleep(Duration::from_millis(300)).await;
let password_form = handle_error!(
c,
c.form(Locator::Id("login")).await,
"Coudln't find login form"
);
let id = handle_error!(
c,
c.find(Locator::Id("password")).await,
"Couldn't find input id=password"
);
handle_error!(c, id.click().await, "Couldn't click the username input?");
handle_error!(
c,
id.send_keys(NOT_ADMIN_TEST_PASSWORD).await,
"Couldn't type the password?"
);
handle_error!(
c,
password_form.submit().await,
"Couldn't submit password-login form"
);
c.wait();
// try clicking the nav links
let mut navlinks: Vec<Element> = vec![];
let mut navlinks_attempts = 0;
while navlinks.is_empty() {
navlinks = handle_error!(
c,
c.find_all(Locator::Css(".nav-link")).await,
"Couldn't find nav-link CSS items"
);
navlinks_attempts += 1;
tokio::time::sleep(Duration::from_millis(200)).await;
if navlinks_attempts > 10 {
panic!("Couldn't find navlinks after 2 seconds!");
}
}
println!("Found navlinks: {:?}", navlinks);
for link in navlinks {
println!("Clicking {:?}", link.text().await);
handle_error!(c, link.click().await, &format!("Couldn't click {:?}", link));
if let Ok(text) = link.text().await {
if text.to_lowercase() == "sign out" {
println!("looking for the sign out modal to click the cancel button...");
tokio::time::sleep(Duration::from_secs(1)).await;
println!("Found the sign out modal, clicking the cancel button");
// find the cancel button and click it
let buttons = handle_error!(
c,
c.find_all(Locator::Css(".btn")).await,
"Couldn't find CSS 'btn' items"
);
println!("Found the following buttons: {:?}", buttons);
for button in buttons {
if let Ok(text) = button.text().await {
if text == "Cancel" {
println!("Found the sign out cancel button, clicking it");
handle_error!(c, button.click().await, "Couldn't click cancel button");
break;
}
}
}
}
}
}
// tokio::time::sleep(Duration::from_millis(3000)).await;
}