Skip to content

Ease Testing Forms w/LocalForm #1591

Open
@SergioBenitez

Description

@SergioBenitez

Testing forms in Rocket is currently an entirely manual process:

let client = Client::tracked(rocket).unwrap();

let response = client.post("/")
    .header(ContentType::Form)
    .body("field=value&is+it=a+cat%3F")
    .dispatch();

With the addition of multipart form support, form testing verbosity is even further exacerbated:

let client = Client::tracked(rocket).unwrap();

let ct = "multipart/form-data; boundary=X-BOUNDARY"
    .parse::<ContentType>()
    .unwrap();

let body = &[
    "--X-BOUNDARY",
    r#"Content-Disposition: form-data; name="names[]""#,
    "",
    "abcd",
    "--X-BOUNDARY",
    r#"Content-Disposition: form-data; name="names[]""#,
    "",
    "123",
    "--X-BOUNDARY",
    r#"Content-Disposition: form-data; name="file"; filename="foo.txt""#,
    "Content-Type: text/plain",
    "",
    "hi there",
    "--X-BOUNDARY--",
    "",
].join("\r\n");

let response = client.post("/")
    .header(ct)
    .body(body)
    .dispatch();

Rocket should make testing applications with forms a much simpler task.

Here's how we might hope testing the above forms would look:

let client = Client::tracked(rocket).unwrap();
let response = client.post("/")
    .form(&[("field", "value"), ("is it", "a cat?")])
    .dispatch();

let client = Client::tracked(rocket).unwrap();
let response = client.post("/")
    .form(LocalForm::new()
        .field("names[]", "abcd")
        .field("names[]", "123")
        .file("foo.txt", ContentType::Plain, "hi there"))
    .dispatch();

An API enabling this might resemble:

impl Client {
    pub fn form<F: Into<LocalForm>>(&mut self, form: F) -> &mut Self {
        let form = form.into();
        self.set_header(form.content_type());
        self.set_body(form.body_data());
        self
    }
}

struct LocalForm { /* .. */ }

impl LocalForm {
    pub fn new() -> Self;

    /// A percent-decoded `name` and `value`.
    pub fn field<'v, N, V>(mut self, name: N, value: V) -> Self
        where N: Into<NameBuf<'v>>, V: AsRef<str>;

    /// Adds all of the `fields` to `self`.
    pub fn fields<'v, I, F>(mut self, fields: I) -> Self
        where I: Iterator<Item = F>, F: Into<ValueField<'v>>;

    /// A percent-encoded `name` and `value`.
    pub fn raw_field(mut self, name: &'v RawStr, value: &'v RawStr) -> Self;

    /// Adds a field for the file with name `file_name`, content-type `ct`, and
    /// file contents `data`.
    pub fn file<'v, N, V>(mut self, file_name: N, ct: ContentType, data: V) -> Self
        where N: Into<Option<&'v str>>, V: AsRef<[u8]>;

    /// Add a data field with content-type `ct` and binary `data`.
    pub fn data<'v, V>(mut self, ct: ContentType, data: V) -> Self
        where V: AsRef<[u8]>;

    /// The full content-type for this form.
    pub fn content_type(&self) -> ContentType;

    /// The full body data for this form.
    pub fn body_data(&self) -> Vec<u8>;
}

impl<'v, F: Into<ValueField<'v>>, I: Iterator<Item = F>> From<I> for LocalForm { /* .. */ }

Metadata

Metadata

Labels

enhancementA minor feature requesthelp wantedContributions to this issue are needed

Type

No type

Projects

Status

Ready

Relationships

None yet

Development

No branches or pull requests

Issue actions