Skip to main content

Client

Struct Client 

Source
pub struct Client { /* private fields */ }
Expand description

An HTTP client for request tests, with a cookie jar, like a browser tab (Rails’ integration session).

Every request carries Sec-Fetch-Site: same-origin, as a browser’s same-site form does, so Ocre’s cross-site request check passes; use Client::cross_site to test the check. Cookies from Set-Cookie are sent back on the next requests, so a sign-in, the session and flash messages carry over. Redirects are not followed automatically: assert on them, then call Client::follow_redirect. Two clients are two independent visitors (Rails’ open_session).

§Examples

use ocre::testing::Client;

let mut client = Client::new();
client.get("/up").assert_status(200).assert_contains("OK");

Implementations§

Source§

impl Client

Source

pub fn new() -> Self

A client for the server ocre test --e2e started (TEST_URL).

§Panics

When TEST_URL is not set: the test was run with plain cargo test -- --ignored.

§Examples
let mut client = ocre::testing::Client::new();
client.get("/").assert_success();
Source

pub fn with_base_url(base: &str) -> Self

A client for the app at base (http://localhost:8787), e.g. a running ocre dev.

§Examples
let mut client = ocre::testing::Client::with_base_url("http://localhost:8787");
client.get("/up").assert_status(200);
Source

pub fn header(self, name: &str, value: &str) -> Self

Sends name: value with every request (Rails’ headers:); replaces an earlier value of the same header.

§Examples
let mut client = ocre::testing::Client::new().header("Accept-Language", "fr");
client.get("/").assert_contains("Bienvenue");
Source

pub fn cross_site(self) -> Self

Requests as a form posted from another site would (Sec-Fetch-Site: cross-site): Ocre’s CSRF protection answers 403 to unsafe methods.

§Examples
let mut client = ocre::testing::Client::new().cross_site();
client.post("/posts", &[("title", "x")]).assert_status(403);
Source

pub fn htmx(self) -> Self

Requests as htmx does (HX-Request: true), Rails’ xhr: true.

§Examples
let mut client = ocre::testing::Client::new().htmx();
client.get("/posts").assert_not_contains("<html");
Source

pub fn get(&mut self, path: &str) -> Response

GET path.

§Examples
ocre::testing::Client::new().get("/posts?page=2").assert_status(200);
Source

pub fn post(&mut self, path: &str, form: &impl Serialize) -> Response

POST path with an application/x-www-form-urlencoded body, as an HTML form sends it.

form is anything serde_urlencoded takes: a slice of (name, value) pairs, a factory’s form(), a struct deriving Serialize, or &() for an empty body.

§Panics

When form is not a flat list of fields.

§Examples
let mut client = ocre::testing::Client::new();
client.post("/posts", &[("title", "Hello"), ("body", "World")]).assert_status(303);
client.post("/posts/1/delete", &()).assert_redirect_to("/posts");
Source

pub fn post_json(&mut self, path: &str, value: &impl Serialize) -> Response

POST path with value as a JSON body.

§Examples
let mut client = ocre::testing::Client::new();
let created = client.post_json("/api/posts", &ocre::serde_json::json!({"title": "Hello"}));
assert_eq!(created.assert_status(201).json::<ocre::serde_json::Value>()["title"], "Hello");
Source

pub fn patch_json(&mut self, path: &str, value: &impl Serialize) -> Response

PATCH path with value as a JSON body.

§Examples
let mut client = ocre::testing::Client::new();
client.patch_json("/api/posts/1", &ocre::serde_json::json!({"title": "New"})).assert_status(200);
Source

pub fn put_json(&mut self, path: &str, value: &impl Serialize) -> Response

PUT path with value as a JSON body.

§Examples
let mut client = ocre::testing::Client::new();
client.put_json("/api/settings", &ocre::serde_json::json!({"theme": "dark"})).assert_success();
Source

pub fn delete(&mut self, path: &str) -> Response

DELETE path.

§Examples
ocre::testing::Client::new().delete("/api/posts/1").assert_status(204);
Source

pub fn request( &mut self, method: &str, path: &str, body: Option<(&str, Vec<u8>)>, ) -> Response

Sends method path with an optional (content type, body): any method, any body (multipart uploads, raw bytes).

§Panics

When the server cannot be reached.

§Examples
let mut client = ocre::testing::Client::new();
client.request("HEAD", "/up", None).assert_status(200);
client.request("POST", "/api/raw", Some(("text/plain", b"bytes".to_vec()))).assert_success();
Source

pub fn follow_redirect(&mut self, response: &Response) -> Response

Follows the redirect response answered: GET of its Location (Rails’ follow_redirect!).

§Panics

When response has no Location header.

§Examples
let mut client = ocre::testing::Client::new();
let created = client.post("/posts", &[("title", "Hello"), ("body", "World")]);
client.follow_redirect(&created).assert_contains("Post was successfully created.");
Source

pub fn cookie(&self, name: &str) -> Option<&str>

The cookie name as the server set it (percent-encoded), if the jar has it.

§Examples
let mut client = ocre::testing::Client::new();
client.get("/");
assert!(client.cookie("_ocre_session").is_none());

Sets a cookie sent with the next requests, as if the server had set it.

§Examples
let mut client = ocre::testing::Client::new();
client.set_cookie("locale", "fr");
Source

pub fn session(&self) -> Map<String, Value>

The session data (Rails’ session in tests), decrypted with the app’s SECRET_KEY_BASE (the environment variable, else .dev.vars): empty without a session cookie. Flash messages set for the next request are under _flash: Client::flash reads them.

§Panics

When there is a session cookie but no SECRET_KEY_BASE, or the cookie does not decrypt with it.

§Examples
let mut client = ocre::testing::Client::new();
client.post("/session", &[("email", "ada@example.com"), ("password", "secret")]);
assert!(client.session().contains_key("user_id"));
Source

pub fn flash(&self, kind: &str) -> Option<String>

The flash message of kind (notice, alert…) set by the last request for the next page (Rails’ flash[:notice] after an action).

§Examples
let mut client = ocre::testing::Client::new();
client.post("/posts", &[("title", "Hello"), ("body", "World")]);
assert_eq!(client.flash("notice").as_deref(), Some("Post was successfully created."));
Source

pub fn deliveries(&mut self) -> Vec<Email>

Emails the app sent with MAIL_ADAPTER = "log" (Rails’ ActionMailer::Base.deliveries), oldest first: the last 20, kept by the Worker instance. Reads GET /ocre/dev/mailers/sent.json, which exists when routes() merges ocre::mail::dev_routes (ocre g mailer adds it).

§Panics

When the endpoint does not answer a JSON list.

§Examples
let mut client = ocre::testing::Client::new();
let before = client.deliveries().len();
client.post("/passwords", &[("email", "ada@example.com")]);
let sent = client.deliveries();
assert_eq!(sent.len(), before + 1);
assert_eq!(sent.last().unwrap().to, ["ada@example.com"]);
Source

pub fn broadcasts(&mut self) -> Vec<Broadcast>

Messages the app broadcast to realtime channels (Rails’ assert_broadcasts), oldest first: the last 50, kept by the Worker instance. Reads GET /ocre/dev/realtime/sent.json, which exists when routes() merges ocre::realtime::dev_routes() (ocre g scaffold ... --realtime adds it).

§Panics

When the endpoint does not answer a JSON list.

§Examples
let mut client = ocre::testing::Client::new();
client.post("/posts", &[("title", "Live"), ("body", "b")]);
let last = client.broadcasts().pop().unwrap();
assert_eq!(last.channel, "posts");
assert!(last.message.contains("Live"));
Source

pub fn jobs(&mut self) -> Jobs

Jobs the app enqueued and ran (Rails’ assert_enqueued_with, assert_performed_jobs): the last 50 of each, oldest first, kept by the Worker instance. Reads GET /ocre/dev/jobs.json, which the first ocre g job merges into routes() (ocre::jobs::dev_routes()). Local queues deliver within a second or so: wait for a run with eventually.

§Panics

When the endpoint does not answer the expected JSON.

§Examples
use ocre::testing::{Client, eventually};

let mut client = Client::new();
client.post("/signups", &[("email", "ada@example.com")]);
let jobs = client.jobs();
assert_eq!(jobs.enqueued.last().unwrap().name(), Some("send_welcome"));
eventually(|| client.jobs().performed.iter().any(|run| run.job == "send_welcome" && run.outcome == "done").then_some(()));
Source

pub fn receive_email( &mut self, from: &str, to: &str, subject: &str, body: &str, ) -> Response

Delivers an email to the app’s mailbox (ocre g mailbox), as Cloudflare Email Routing would (Rails’ receive_inbound_email_from_mail): a plain-text message posted to the local server’s email endpoint (POST /cdn-cgi/local/email?from=&to=), which runs the Worker’s email event. The response says whether the mailbox accepted it.

§Examples
let mut client = ocre::testing::Client::new();
client.receive_email("ada@example.com", "support@example.com", "Help", "My order is late.").assert_success();

Trait Implementations§

Source§

impl Debug for Client

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
§

impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
where ST: ?Sized, DT: ?Sized,

§

impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

§

impl<T> Read<Exclusive, BecauseExclusive> for T
where T: ?Sized,

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<S, T> Upcast<T> for S
where T: UpcastFrom<S> + ?Sized, S: ?Sized,

Source§

fn upcast(&self) -> &T
where Self: ErasableGeneric, T: Sized + ErasableGeneric<Repr = Self::Repr>,

Perform a zero-cost type-safe upcast to a wider ref type within the Wasm bindgen generics type system. Read more
Source§

fn upcast_into(self) -> T
where Self: Sized + ErasableGeneric, T: Sized + ErasableGeneric<Repr = Self::Repr>,

Perform a zero-cost type-safe upcast to a wider type within the Wasm bindgen generics type system. Read more
§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

§

fn vzip(self) -> V