Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ease Testing Forms w/LocalForm #1591

Open
SergioBenitez opened this issue Mar 25, 2021 · 7 comments
Open

Ease Testing Forms w/LocalForm #1591

SergioBenitez opened this issue Mar 25, 2021 · 7 comments
Assignees
Labels
enhancement A minor feature request help wanted Contributions to this issue are needed
Milestone

Comments

@SergioBenitez
Copy link
Member

SergioBenitez commented Mar 25, 2021

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 { /* .. */ }
@SergioBenitez SergioBenitez added enhancement A minor feature request help wanted Contributions to this issue are needed labels Mar 25, 2021
@SergioBenitez SergioBenitez added this to the 0.6.0 milestone Mar 25, 2021
@ELD
Copy link
Member

ELD commented Mar 26, 2021

I'll go ahead and take this one on if no one else is 🙂

@SergioBenitez
Copy link
Member Author

It's yours!

@sabirmgd

This comment has been minimized.

@jebrosen

This comment has been minimized.

@sabirmgd

This comment has been minimized.

@jebrosen

This comment has been minimized.

@RuboGubo
Copy link

Would it not make more sense to create a macro that implements a ToForm trait to serialise a struct to a string encoded form? I feel like that would be a common use case, given that most functions will serialise it back to a struct immediately.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement A minor feature request help wanted Contributions to this issue are needed
Projects
Status: Ready
Development

No branches or pull requests

5 participants