Skip to main content

TestClient

Struct TestClient 

Source
pub struct TestClient<'a> { /* private fields */ }
Expand description

Sends requests carrying cookies and tenant.

Implementations§

Source§

impl<'a> TestClient<'a>

Source

pub fn new(router: &'a Router) -> Self

Source

pub fn panel(&self, panel: &PanelHandle) -> Self

Resolves resource’s URLs in panel, the handle the router’s builder gives through panel_handle.

let builder = Router::builder().discover().with(db).panel(panel)?;
let admin = builder.panel_handle("admin").expect("mounted above");
let router = builder.build();
let client = tablo_test::TestClient::new(&router).panel(&admin);
Source

pub fn resource<R: Resource>(&self) -> ResourceClient<'_, 'a, R>

Typed requests to R, whose URLs and form keys come from the panel’s own declaration.

§Panics

When no panel is set.

Source

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

Attaches a cookie to every request; a later value for the same name replaces the earlier one.

Source

pub fn cookies(&self, cookies: &[(String, String)]) -> Self

Attaches every (name, value) pair, replacing earlier values for the same name.

Source

pub fn language(&self, languages: &str) -> Self

Sends Accept-Language: {languages} with every request, as a browser preferring them does: "fr-CA, en;q=0.5".

Source

pub fn csrf(&self, token: &str) -> Self

Attach the CSRF cookie the form’s csrf_token field must match.

Source

pub async fn sign_in(&self, db: &Db, panel: &str, user: &impl PanelUser) -> Self

Signs user in to the panel Panel::new(panel) mounts, as a successful login does, and attaches the session cookie; no password is hashed or checked.

The session row goes into db, the router’s database, keyed to PanelUser::user_id: each request loads the user back through the panel’s Authenticator::find_by_id, so a custom authenticator works as the shipped one does.

let admin = tablo_core::auth::create_admin(&mut db, "admin@example.com", "secret", "Admin")
    .await
    .expect("seed the admin");
let client = TestClient::new(router).sign_in(&db, "admin", &admin).await;
§Panics

When the session row cannot be written, e.g. the database does not register AuthSession.

Source

pub async fn log_in( &self, panel: &str, email: &str, password: &str, next: &str, ) -> (Self, Response<Body>)

Signs in through the login form of the panel Panel::new(panel) mounts, as a browser does: loads the page, posts email and password with the page’s CSRF token, and keeps the cookies both responses set. Returns the client and the login response, which redirects on success and re-renders the form on a refusal.

next is the page to land on after signing in, "" for the panel’s home. Prefer sign_in unless the test is about the login itself.

§Panics

When the login page renders no csrf_token input: the panel’s authentication is off.

Source

pub fn tenant(&self, tenant: TenantId) -> Self

Acts for tenant, as a browser that switched to it from the tenant menu: a signed-in user who is not one of its members acts for their own tenant instead, so a cross-tenant test reaches only what a browser would. With nobody signed in, such as on a panel with auth disabled, the request acts for tenant.

Source

pub async fn get(&self, uri: &str) -> Response<Body>

Source

pub async fn head(&self, uri: &str) -> Response<Body>

Source

pub async fn post_form(&self, uri: &str, body: String) -> Response<Body>

Source

pub async fn submit(&self, uri: &str, body: &str) -> Response<Body>

Posts the urlencoded body as a panel form does: with the CSRF token in both the cookie and the csrf_token field, the one csrf attached, else a fresh one.

Source

pub async fn submit_multipart( &self, uri: &str, parts: &[(&str, Option<&str>, &str)], ) -> Response<Body>

Posts multipart_body’s parts as a panel form with a file input does: with the CSRF token in both the cookie and a csrf_token part, as submit picks it.

Source

pub async fn post_multipart( &self, uri: &str, boundary: &str, body: String, ) -> Response<Body>

Source

pub async fn call_procedure(&self, path: &str, args: &str) -> Response<Body>

Calls the runtime procedure served at path with args, the JSON array of its arguments, as the browser’s runtime does: a JSON POST that answers with the JSON of its result.

let response = client
    .call_procedure(
        "/_topcoat/runtime/procedures/tablo-notifications/mark",
        &format!(r#"["{id}", true]"#),
    )
    .await;
assert_eq!(response.status(), 200);

Trait Implementations§

Source§

impl<'a> Clone for TestClient<'a>

Source§

fn clone(&self) -> Self

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more

Auto Trait Implementations§

§

impl<'a> !RefUnwindSafe for TestClient<'a>

§

impl<'a> !UnwindSafe for TestClient<'a>

§

impl<'a> Forget for TestClient<'a>

§

impl<'a> Freeze for TestClient<'a>

§

impl<'a> Move for TestClient<'a>

§

impl<'a> Send for TestClient<'a>

§

impl<'a> Sync for TestClient<'a>

§

impl<'a> Unpin for TestClient<'a>

§

impl<'a> UnsafeUnpin for TestClient<'a>

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
Source§

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

Source§

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

Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> ErasedDestructor for T
where T: 'static,

Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self> ⓘ

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self> ⓘ

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
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.

Source§

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> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

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

Source§

type Error = !

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

fn try_from(value: U) -> Result<T, !>

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<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V

Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self> ⓘ
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self> ⓘ

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more