use crate::core::config::ResolvedCrateConfig;
use crate::core::ir::{EnumDef, TypeDef};
use crate::e2e::config::E2eConfig;
use crate::e2e::fixture::Fixture;
use anyhow::{Context, Result};
pub(super) fn render_snippet_body(
fixture: &Fixture,
e2e_config: &E2eConfig,
config: &ResolvedCrateConfig,
type_defs: &[TypeDef],
enums: &[EnumDef],
) -> Result<String> {
if fixture.is_http_test() {
return render_http_snippet(fixture);
}
let mut fixture_without_assertions = fixture.clone();
fixture_without_assertions.assertions.clear();
let bridge_class = config.dart_bridge_class_name();
let first_class_map = super::values::build_dart_first_class_map(type_defs, enums, e2e_config);
let mut test_case = String::new();
super::test_case::render_test_case(
&mut test_case,
&fixture_without_assertions,
super::test_case::DartTestCaseContext {
e2e_config,
lang: "dart",
bridge_class: &bridge_class,
dart_first_class_map: &first_class_map,
adapters: &config.adapters,
config,
type_defs,
enums,
native_typed_dtos: true,
},
);
let statements = extract_test_statements(&test_case)
.with_context(|| format!("extracting Dart snippet body for fixture `{}`", fixture.id))?;
let package = e2e_config
.resolve_package("dart")
.and_then(|value| value.name)
.unwrap_or_else(|| config.dart_pubspec_name());
let module = config.name.replace('-', "_");
let needs_json = statements
.iter()
.any(|statement| statement.contains("jsonDecode(") || statement.contains("jsonEncode("));
let needs_io = statements.iter().any(|statement| statement.contains("File("));
Ok(crate::e2e::template_env::render(
"dart/snippet_body.jinja",
minijinja::context! {
package => package, module => module, statements => statements, needs_json => needs_json,
needs_io => needs_io,
},
))
}
fn render_http_snippet(fixture: &Fixture) -> Result<String> {
let http = fixture.http.as_ref().expect("HTTP fixture checked by caller");
let plan = crate::e2e::codegen::client::http_call::plan_request(http);
let mut headers = plan.headers;
if let Some(content_type) = &plan.content_type
&& !headers.keys().any(|name| name.eq_ignore_ascii_case("content-type"))
{
headers.insert("content-type".into(), content_type.clone());
}
if !http.request.cookies.is_empty() {
headers.insert(
"cookie".into(),
http.request
.cookies
.iter()
.map(|(key, value)| format!("{key}={value}"))
.collect::<Vec<_>>()
.join("; "),
);
}
let raw_body = plan.body.as_ref().is_some_and(|body| {
matches!(body, serde_json::Value::String(_))
&& plan
.content_type
.as_deref()
.is_some_and(crate::e2e::codegen::client::is_raw_text_content_type)
});
Ok(crate::e2e::template_env::render(
"dart/http_snippet.jinja",
minijinja::context! {
method => http.request.method.to_uppercase(),
path => format!("/fixtures/{}{}", fixture.id, http.request.path),
headers => headers.iter().map(|(key, value)| minijinja::context! {
key => super::values::escape_dart(key), value => super::values::escape_dart(value),
}).collect::<Vec<_>>(),
body_json => plan.body.as_ref().map(serde_json::to_string).transpose()?,
raw_body => raw_body,
},
))
}
fn extract_test_statements(rendered: &str) -> Option<Vec<String>> {
let lines: Vec<&str> = rendered.lines().collect();
let start = lines.iter().position(|line| line.trim_start().starts_with("test("))? + 1;
let end = lines.iter().rposition(|line| line.trim() == "});")?;
Some(
lines[start..end]
.iter()
.map(|line| line.strip_prefix(" ").unwrap_or(line).to_string())
.collect(),
)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn extracts_only_the_native_test_body() {
let rendered = " test('sample', () async {\n final value = await Api.load();\n });\n";
assert_eq!(
extract_test_statements(rendered),
Some(vec!["final value = await Api.load();".to_string()])
);
}
#[test]
fn renders_native_call_without_test_harness() {
let fixture = Fixture {
id: "sample".into(),
description: "Sample".into(),
..Fixture::default()
};
let mut e2e = E2eConfig::default();
e2e.call.function = "load_document".into();
let body = render_snippet_body(&fixture, &e2e, &ResolvedCrateConfig::default(), &[], &[]).unwrap();
assert!(body.contains("loadDocument()"));
assert!(body.contains("Future<void> main() async"));
assert!(!body.contains("test("));
assert!(!body.contains("expect("));
}
#[test]
fn renders_http_request_without_test_harness_assertions() {
let fixture: Fixture = serde_json::from_value(serde_json::json!({
"id": "create_item", "description": "Create item", "input": null,
"http": {
"handler": {"route": "/items", "method": "POST"},
"request": {"method": "POST", "path": "/items", "body": {"name": "sample"}},
"expected_response": {"status_code": 201}
}
}))
.unwrap();
let body = render_snippet_body(
&fixture,
&E2eConfig::default(),
&ResolvedCrateConfig::default(),
&[],
&[],
)
.unwrap();
assert!(body.contains("HttpClient"));
assert!(body.contains("/fixtures/create_item/items"));
assert!(body.contains("request.write(jsonEncode(jsonDecode"));
assert!(!body.contains("expect("));
}
}