This version of the documentation is outdated, and features documented here may work differently now. You can see the latest stable version of the docs here.
Fantoccini Basics
Now that you know a bit more about how Perseus tests work, it's time to go through how to write them!
Remember, you're controlling an actual browser, so you basically have everything available to you that a user can do (mostly). You can even take screenshots! All this is achieved with Fantoccini, which you can learn more about here. For now though, here's a quick tutorial on the basics, using this example:
use fantoccini::{Client, Locator};
use perseus::wait_for_checkpoint;
#[perseus::test]
async fn main(c: &mut Client) -> Result<(), fantoccini::error::CmdError> {
c.goto("http://localhost:8080").await?;
wait_for_checkpoint!("begin", 0, c);
let url = c.current_url().await?;
assert!(url.as_ref().starts_with("http://localhost:8080"));
// The greeting was passed through using build state
wait_for_checkpoint!("initial_state_present", 0, c);
wait_for_checkpoint!("page_visible", 0, c);
let greeting = c.find(Locator::Css("p")).await?.text().await?;
assert_eq!(greeting, "Hello World!");
// For some reason, retrieving the inner HTML or text of a `<title>` doens't work
let title = c.find(Locator::Css("title")).await?.html(false).await?;
assert!(title.contains("Index Page"));
// Go to `/about`
c.find(Locator::Id("about-link")).await?.click().await?;
let url = c.current_url().await?;
assert!(url.as_ref().starts_with("http://localhost:8080/about"));
wait_for_checkpoint!("initial_state_not_present", 0, c);
wait_for_checkpoint!("page_visible", 1, c);
// Make sure the hardcoded text there exists
let text = c.find(Locator::Css("p")).await?.text().await?;
assert_eq!(text, "About.");
let title = c.find(Locator::Css("title")).await?.html(false).await?;
assert!(title.contains("About Page"));
// Make sure we get initial state if we refresh
c.refresh().await?;
wait_for_checkpoint!("initial_state_present", 0, c);
Ok(())
}
Going to a Page
You can trivially go to a page of your app by running c.goto("...")
. The above example ensures that the URL is valid, but you shouldn't have to do this unless you're testing a page that automatically redirects the user. Also, if you're using i18n, don't worry about testing automatic locale redirection, we've already done that for you!
Once you've arrived at a page, you should wait for the router_entry
(this example uses begin
because it tests internal parts of Perseus) checkpoint, which will be reached when Perseus has decided what to do with your app. If you're testing particular page logic, you should wait instead for page_visible
, which will be reached when the user could see content on your page, and then for page_interactive
, which will be reached when the page is completely ready. Remember though, you only need to wait for the checkpoints that you actually use (e.g. you don't need to wait for page_visible
and page_interactive
if you're not doing anything in between).
Finding an Element
You can find an element easily by using Fantoccini's Locator
enum
. This has two options, Id
or Css
. The former will find an element by its HTML id
, and the latter will use a CSS selector (here's a list of them). In the above example, we've used Locator::Css("p")
to get all paragraph elements, and then we've plugged that into c.find()
to get the first one. Then, we can get its innerText
with .text()
and assert that is what we want it to be.
Caveats
As you may have noticed above, asserting on the contents of a <title>
is extremely unintuitive, as it requires using .html(false)
(meaning include the element tag itself) and asserting against that. For some reason, neither .html(true)
nor .text()
return anything. There's a tracking issue for this here.
Miscellaneous
For full documentation of how Fantoccini works, see its API documentation here.