mod common;
use axum::http::StatusCode;
use common::*;
use serde_json::json;
use what_core::server::create_router;
#[tokio::test]
async fn delete_not_blocked_by_edit_form_validation() {
let proj = TestProject::new();
proj.add_page(
"edit.html",
r##"<form w-validate action="/w-action/notes/1?w-redirect=/done"><input name="title" w-required></form>
<form method="post" action="/w-action/notes/1?w-action=delete&w-redirect=/done"><button>del</button></form>"##,
);
proj.add_page("done.html", "<h1>done</h1>");
let config = open_collections_config(&["notes"]);
let (_dir, state) = proj.build_state_with_config(config);
state.store.create("notes", json!({ "title": "X" })).await.unwrap();
let router = create_router(state);
get(&router, "/edit").await;
let resp = post_form_with_headers(
&router,
"/w-action/notes/1?w-action=delete&w-redirect=/done",
"",
vec![("referer", "/edit")],
)
.await;
assert_eq!(resp.status, StatusCode::SEE_OTHER);
assert_eq!(
resp.location(),
Some("/done"),
"delete should proceed, not be rejected as a validation bypass"
);
}
#[tokio::test]
async fn form_w_validate_injects_hidden_field() {
let proj = TestProject::new();
proj.add_page(
"form.html",
r#"<form w-validate action="/w-action/users?w-redirect=/form">
<input type="text" name="email" w-required w-type="email">
<input type="submit" value="Submit">
</form>"#,
);
let (_dir, state) = proj.build_state();
let router = create_router(state);
let resp = get(&router, "/form").await;
assert_eq!(resp.status, StatusCode::OK);
resp.assert_contains(r#"name="w-rules""#);
resp.assert_contains(r#"type="hidden""#);
}
#[tokio::test]
async fn form_without_w_validate_no_hidden_field() {
let proj = TestProject::new();
proj.add_page(
"form.html",
r#"<form action="/w-action/users?w-redirect=/form">
<input type="text" name="email" w-required>
<input type="submit" value="Submit">
</form>"#,
);
let (_dir, state) = proj.build_state();
let router = create_router(state);
let resp = get(&router, "/form").await;
assert_eq!(resp.status, StatusCode::OK);
resp.assert_not_contains(r#"name="w-rules""#);
}
#[tokio::test]
async fn validation_redirect_on_failure() {
let proj = TestProject::new();
proj.add_page(
"form.html",
r##"<form w-validate action="/w-action/users?w-redirect=/success">
<input type="text" name="email" w-required w-type="email">
<input type="submit" value="Submit">
</form>
<p>#errors.email|default:"no-error"#</p>"##,
);
proj.add_page("success.html", "<h1>Success</h1>");
let (_dir, state) = proj.build_state();
let router = create_router(state);
let form_resp = get(&router, "/form").await;
let body = &form_resp.body;
let token_start =
body.find(r#"name="w-rules" value=""#).unwrap() + r#"name="w-rules" value=""#.len();
let token_end = body[token_start..].find('"').unwrap() + token_start;
let token = &body[token_start..token_end];
let form_data = format!("email=&w-rules={}", token);
let resp = post_form_with_headers(
&router,
"/w-action/users?w-redirect=/success",
&form_data,
vec![("referer", "/form")],
)
.await;
assert_eq!(resp.status, StatusCode::SEE_OTHER);
assert_eq!(resp.location(), Some("/form"));
}
#[tokio::test]
async fn validation_passes_on_valid_data() {
let proj = TestProject::new();
proj.add_page(
"form.html",
r#"<form w-validate action="/w-action/users?w-redirect=/success">
<input type="text" name="email" w-required w-type="email">
<input type="submit">
</form>"#,
);
proj.add_page("success.html", "<h1>Success</h1>");
let (_dir, state) = proj.build_state();
let router = create_router(state);
let form_resp = get(&router, "/form").await;
let body = &form_resp.body;
let token_start =
body.find(r#"name="w-rules" value=""#).unwrap() + r#"name="w-rules" value=""#.len();
let token_end = body[token_start..].find('"').unwrap() + token_start;
let token = &body[token_start..token_end];
let form_data = format!("email=test@example.com&w-rules={}", token);
let resp = post_form(&router, "/w-action/users?w-redirect=/success", &form_data).await;
assert_eq!(resp.status, StatusCode::SEE_OTHER);
assert_eq!(resp.location(), Some("/success"));
}
#[tokio::test]
async fn validation_errors_in_flash() {
let proj = TestProject::new();
proj.add_page(
"form.html",
r##"<form w-validate action="/w-action/users?w-redirect=/success">
<input type="text" name="name" w-required>
<input type="submit">
</form>
<p>error: #errors.name|default:"none"#</p>
<p>has: #has_errors#</p>"##,
);
let (_dir, state) = proj.build_state();
let router = create_router(state);
let form_resp = get(&router, "/form").await;
let body = &form_resp.body;
let token_start =
body.find(r#"name="w-rules" value=""#).unwrap() + r#"name="w-rules" value=""#.len();
let token_end = body[token_start..].find('"').unwrap() + token_start;
let token = &body[token_start..token_end];
let form_data = format!("name=&w-rules={}", token);
let resp = post_form_with_headers(
&router,
"/w-action/users?w-redirect=/success",
&form_data,
vec![("referer", "/form")],
)
.await;
let cookie = resp.set_cookie().expect("should set session cookie");
let resp = get_with_headers(&router, "/form", vec![("cookie", cookie)]).await;
resp.assert_contains("name is required");
resp.assert_contains("has: true");
}