diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 211eaf06..32f79e5c 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -154,4 +154,16 @@ ClientException with SocketException: Connection failed (OS Error: Operation not You can read more [here](https://docs.flutter.dev/platform-integration/macos/building#setting-up-entitlements) +### Android (Work in Progress) +Add the `multiDexEnabled true` line to the `defaultConfig` section at `android/app/build.gradle file` + +``` +android { + ... + defaultConfig { + ... + multiDexEnabled true + } +} +``` diff --git a/assets/completed.json b/assets/completed.json new file mode 100644 index 00000000..5326bc25 --- /dev/null +++ b/assets/completed.json @@ -0,0 +1 @@ +{"v":"5.1.16","fr":29.9700012207031,"ip":0,"op":30.0000012219251,"w":500,"h":500,"nm":"Comp 1","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[250,250,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-158,21],[-63,116],[162,-109]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.129411764706,0.8,0.223529411765,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":36,"ix":5},"lc":1,"lj":1,"ml":4,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":0,"s":[0],"e":[100]},{"t":20.0000008146167}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":-149.000006068894,"op":40.0000016292334,"st":-149.000006068894,"bm":0}],"markers":[]} \ No newline at end of file diff --git a/assets/saving.json b/assets/saving.json new file mode 100644 index 00000000..0c9ab2f1 --- /dev/null +++ b/assets/saving.json @@ -0,0 +1 @@ +{"v":"5.5.7","meta":{"g":"LottieFiles AE 0.1.20","a":"","k":"","d":"","tc":""},"fr":25,"ip":0,"op":31,"w":200,"h":200,"nm":"save","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Layer 4","parent":4,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[101.689,94.053,0],"ix":2},"a":{"a":0,"k":[101.689,94.053,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.25,0.25,0.25],"y":[1,1,1]},"o":{"x":[0.33,0.33,0.33],"y":[0,0,0]},"t":13,"s":[100,0,100]},{"i":{"x":[0.67,0.67,0.67],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":23,"s":[100,100,100]},{"i":{"x":[0.67,0.67,0.67],"y":[1,1,1]},"o":{"x":[0.75,0.75,0.75],"y":[0,0,0]},"t":60,"s":[100,100,100]},{"t":68,"s":[100,0,100]}],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[0.5,0.985],[-0.5,0.985],[-0.5,-0.985],[0.5,-0.985]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.20000000298,0.192156866193,0.196078434587,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[101.689,95.038],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":75,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Layer 3","parent":4,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[100.213,92.217,0],"ix":2},"a":{"a":0,"k":[100.213,92.217,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.25,0.25,0.25],"y":[1,1,1]},"o":{"x":[0.33,0.33,0.33],"y":[0,0,0]},"t":7,"s":[0,0,100]},{"i":{"x":[0.67,0.67,0.67],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":21,"s":[100,100,100]},{"i":{"x":[0.67,0.67,0.67],"y":[1,1,1]},"o":{"x":[0.75,0.75,0.75],"y":[0,0,0]},"t":61,"s":[100,100,100]},{"t":70,"s":[0,0,100]}],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0.276,0],[0,0],[0,0.276],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[-0.276,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0.276]],"v":[[3.623,3.021],[-3.623,3.021],[-4.123,2.521],[-4.123,-3.021],[-3.123,-3.021],[-3.123,2.021],[3.123,2.021],[3.123,-2.943],[4.123,-2.943],[4.123,2.521]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.20000000298,0.192156866193,0.196078434587,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[100.213,95.238],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":75,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"Layer 2","parent":4,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[100.213,109.234,0],"ix":2},"a":{"a":0,"k":[100.213,109.234,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.25,0.25,0.25],"y":[1,1,1]},"o":{"x":[0.33,0.33,0.33],"y":[0,0,0]},"t":4,"s":[0,0,100]},{"i":{"x":[0.67,0.67,0.67],"y":[1,1,1]},"o":{"x":[0.33,0.33,0.33],"y":[0,0,0]},"t":20,"s":[100,100,100]},{"i":{"x":[0.67,0.67,0.67],"y":[1,1,1]},"o":{"x":[0.75,0.75,0.75],"y":[0,0,0]},"t":62,"s":[100,100,100]},{"t":72,"s":[0,0,100]}],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0.37,0],[0,0],[0,-0.37],[0,0],[0,0],[0,0],[-0.921,0],[0,0],[0,-0.921]],"o":[[0,0],[0,0],[0,-0.37],[0,0],[-0.37,0],[0,0],[0,0],[0,0],[0,-0.921],[0,0],[0.921,0],[0,0]],"v":[[5.744,3.929],[4.744,3.929],[4.744,-2.259],[4.074,-2.929],[-4.074,-2.929],[-4.744,-2.259],[-4.744,3.929],[-5.744,3.929],[-5.744,-2.259],[-4.074,-3.929],[4.074,-3.929],[5.744,-2.259]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.20000000298,0.192156866193,0.196078434587,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[100.213,105.305],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":75,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[100.739,174.33,0],"ix":2},"a":{"a":0,"k":[100.213,109.734,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.25,0.25,0.25],"y":[1,1,1]},"o":{"x":[0.33,0.33,0.33],"y":[0,0,0]},"t":0,"s":[0,0,100]},{"i":{"x":[0.25,0.25,0.25],"y":[1,1,1]},"o":{"x":[0.33,0.33,0.33],"y":[0,0,0]},"t":12,"s":[830,830,100]},{"i":{"x":[0.67,0.67,0.67],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":19,"s":[815,815,100]},{"i":{"x":[0.67,0.67,0.67],"y":[1,1,1]},"o":{"x":[0.75,0.75,0.75],"y":[0,0,0]},"t":63,"s":[815,815,100]},{"t":74,"s":[0,0,100]}],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-8.522,8.036],[8.522,8.036],[8.522,-4.465],[5.185,-7.882],[-8.522,-8.03]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ind":1,"ty":"sh","ix":2,"ks":{"a":0,"k":{"i":[[0.276,0],[0,0],[0,0.276],[0,0],[-0.095,0.094],[-0.117,0.009],[0,0],[-0.093,-0.095],[0,0],[0,-0.13],[0,0]],"o":[[0,0],[-0.276,0],[0,0],[0,-0.134],[0.095,-0.094],[0,0],[0.133,0.001],[0,0],[0.091,0.093],[0,0],[0,0.276]],"v":[[9.022,9.036],[-9.022,9.036],[-9.522,8.536],[-9.522,-8.536],[-9.374,-8.891],[-9.017,-9.036],[5.403,-8.879],[5.756,-8.729],[9.38,-5.018],[9.522,-4.668],[9.522,8.536]],"c":true},"ix":2},"nm":"Path 2","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.20000000298,0.192156866193,0.196078434587,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[100.213,100.698],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":75,"st":0,"bm":0}],"markers":[]} \ No newline at end of file diff --git a/lib/codegen/codegen.dart b/lib/codegen/codegen.dart index 7c66b88f..f3f606ae 100644 --- a/lib/codegen/codegen.dart +++ b/lib/codegen/codegen.dart @@ -9,6 +9,7 @@ import 'php/guzzle.dart'; import 'python/http_client.dart'; import 'python/requests.dart'; import 'rust/actix.dart'; +import 'rust/curl_rust.dart'; import 'rust/reqwest.dart'; import 'rust/ureq.dart'; import 'js/axios.dart'; @@ -73,6 +74,8 @@ class Codegen { return PythonRequestsCodeGen().getCode(rM, boundary: boundary); case CodegenLanguage.rustActix: return RustActixCodeGen().getCode(rM, boundary: boundary); + case CodegenLanguage.rustCurl: + return RustCurlCodeGen().getCode(rM); case CodegenLanguage.rustReqwest: return RustReqwestCodeGen().getCode(rM); case CodegenLanguage.rustUreq: diff --git a/lib/codegen/rust/curl_rust.dart b/lib/codegen/rust/curl_rust.dart new file mode 100644 index 00000000..9299554f --- /dev/null +++ b/lib/codegen/rust/curl_rust.dart @@ -0,0 +1,147 @@ +import 'package:jinja/jinja.dart' as jj; +import 'package:apidash/utils/utils.dart' + show getValidRequestUri, requestModelToHARJsonRequest; +import 'package:apidash/models/models.dart' show RequestModel; +import 'package:apidash/consts.dart'; + +class RustCurlCodeGen { + final String kTemplateStart = """use curl::easy::Easy; +{% if hasJsonBody %}use serde_json::json; +{% endif %}{% if hasHeaders %}use curl::easy::List; +{% endif %} +fn main() { + let mut easy = Easy::new(); + let mut data = Vec::new(); + +"""; + + String kTemplateUrl = """ + easy.url("{{url}}").unwrap(); +"""; + + String kTemplateMethod = """ +{% if method == 'get' or method == 'post' or method == 'put' %} + easy.{{ method }}(true).unwrap(); +{% elif method == 'delete' %} + easy.custom_request("DELETE").unwrap(); +{% elif method == 'patch' %} + easy.custom_request("PATCH").unwrap(); +{% elif method == 'head' %} + easy.nobody(true).unwrap(); +{% endif %} + +"""; + + String kTemplateRawBody = """ + easy.post_fields_copy(r#"{{body}}"#.as_bytes()).unwrap(); + + +"""; + + String kTemplateJsonBody = """ + easy.post_fields_copy(json!({{body}}).to_string().as_bytes()).unwrap(); + + +"""; + + String kTemplateFormData = """ + let mut form = curl::easy::Form::new(); + {% for field in fields %} + form.part("{{field.name}}") + {% if field.type == "file" %}.file("{{field.value}}"){% else %}.contents(b"{{field.value}}"){% endif %} + .add().unwrap(); + {% endfor %} + easy.httppost(form).unwrap(); + """; + + String kTemplateHeader = """ + {% if headers %}let mut list = List::new();{% for header, value in headers %} + list.append("{{header}}: {{value}}").unwrap();{% endfor %} + easy.http_headers(list).unwrap(); + {% endif %} + +"""; + + final String kTemplateEnd = """ + { + let mut transfer = easy.transfer(); + transfer.write_function(|new_data| { + data.extend_from_slice(new_data); + Ok(new_data.len()) + }).unwrap(); + transfer.perform().unwrap(); + } + + let response_body = String::from_utf8_lossy(&data); + + println!("Response body: {}", response_body); + println!("Response code: {}", easy.response_code().unwrap()); +}"""; + + String? getCode(RequestModel requestModel) { + try { + String result = ""; + var requestBody = requestModel.requestBody; + + String url = requestModel.url; + + result += jj.Template(kTemplateStart).render({ + "hasJsonBody": requestModel.hasJsonData, + "hasHeaders": (requestModel.enabledRequestHeaders != null && + requestModel.enabledRequestHeaders!.isNotEmpty) || + (requestModel.hasJsonData || requestModel.hasTextData) + }); + + var rec = getValidRequestUri( + url, + requestModel.enabledRequestParams, + ); + + Uri? uri = rec.$1; + var harJson = + requestModelToHARJsonRequest(requestModel, useEnabled: true); + + var templateUrl = jj.Template(kTemplateUrl); + result += templateUrl.render({"url": harJson["url"]}); + + var methodTemplate = jj.Template(kTemplateMethod); + result += methodTemplate.render({"method": requestModel.method.name}); + + if (uri != null) { + if (requestModel.hasTextData) { + var templateBody = jj.Template(kTemplateRawBody); + result += templateBody.render({"body": requestBody}); + } else if (requestModel.hasJsonData) { + var templateBody = jj.Template(kTemplateJsonBody); + result += templateBody.render({"body": requestBody}); + } else if (requestModel.hasFormData) { + var templateFormData = jj.Template(kTemplateFormData); + result += templateFormData.render({ + "fields": requestModel.formDataMapList, + }); + } + + var headersList = requestModel.enabledRequestHeaders; + if (headersList != null || requestModel.hasBody) { + var headers = requestModel.enabledHeadersMap; + if (requestModel.hasJsonData || requestModel.hasTextData) { + headers.putIfAbsent(kHeaderContentType, + () => requestModel.requestBodyContentType.header); + } + if (headers.isNotEmpty) { + var templateHeader = jj.Template(kTemplateHeader); + result += templateHeader.render({ + "headers": headers, + }); + } + } + + result += kTemplateEnd; + } + + return result; + } catch (e) { + return null; + } + } +} diff --git a/lib/consts.dart b/lib/consts.dart index e3d2dc6b..01bc5593 100644 --- a/lib/consts.dart +++ b/lib/consts.dart @@ -10,6 +10,11 @@ const kGitUrl = "https://github.com/foss42/apidash"; const kIssueUrl = "$kGitUrl/issues"; const kDefaultUri = "api.apidash.dev"; +const kAssetIntroMd = "assets/intro.md"; +const kAssetSendingLottie = "assets/sending.json"; +const kAssetSavingLottie = "assets/saving.json"; +const kAssetSavedLottie = "assets/completed.json"; + final kIsMacOS = !kIsWeb && Platform.isMacOS; final kIsWindows = !kIsWeb && Platform.isWindows; final kIsLinux = !kIsWeb && Platform.isLinux; @@ -25,6 +30,7 @@ final kColorTransparentState = MaterialStateProperty.all(Colors.transparent); const kColorTransparent = Colors.transparent; const kColorWhite = Colors.white; +const kColorBlack = Colors.black; const kColorRed = Colors.red; final kColorLightDanger = Colors.red.withOpacity(0.9); const kColorDarkDanger = Color(0xffcf6679); @@ -47,6 +53,7 @@ final kCodeStyle = TextStyle( const kHintOpacity = 0.6; const kForegroundOpacity = 0.05; +const kOverlayBackgroundOpacity = 0.5; const kTextStyleButton = TextStyle(fontWeight: FontWeight.bold); const kTextStyleButtonSmall = TextStyle(fontSize: 12); @@ -79,6 +86,7 @@ const kPh20t40 = EdgeInsets.only( top: 40, ); const kPh60 = EdgeInsets.symmetric(horizontal: 60); +const kPh60v60 = EdgeInsets.symmetric(vertical: 60, horizontal: 60); const kP24CollectionPane = EdgeInsets.only( top: 24, left: 4.0, @@ -276,6 +284,7 @@ enum CodegenLanguage { pythonHttpClient("Python (http.client)", "python", "py"), rustActix("Rust (Actix Client)", "rust", "rs"), rustReqwest("Rust (reqwest)", "rust", "rs"), + rustCurl("Rust (curl-rust)", "rust", "rs"), rustUreq("Rust (ureq)", "rust", "rs"), javaOkHttp("Java (okhttp3)", "java", 'java'), javaAsyncHttpClient("Java (asynchttpclient)", "java", "java"), @@ -522,3 +531,5 @@ const kLabelBusy = "Busy"; const kLabelCopy = "Copy"; const kLabelSave = "Save"; const kLabelDownload = "Download"; +const kLabelSaving = "Saving"; +const kLabelSaved = "Saved"; diff --git a/lib/screens/home_page/collection_pane.dart b/lib/screens/home_page/collection_pane.dart index c70db3ad..fd0b518a 100644 --- a/lib/screens/home_page/collection_pane.dart +++ b/lib/screens/home_page/collection_pane.dart @@ -12,7 +12,7 @@ class CollectionPane extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { - var sm = ScaffoldMessenger.of(context); + final overlayWidget = OverlayWidgetTemplate(context: context); final collection = ref.watch(collectionStateNotifierProvider); final savingData = ref.watch(saveDataStateProvider); if (collection == null) { @@ -34,12 +34,18 @@ class CollectionPane extends ConsumerWidget { onPressed: savingData ? null : () async { + overlayWidget.show( + widget: + const SavingOverlay(saveCompleted: false)); + await ref .read(collectionStateNotifierProvider.notifier) .saveData(); - - sm.hideCurrentSnackBar(); - sm.showSnackBar(getSnackBar("Saved")); + overlayWidget.hide(); + overlayWidget.show( + widget: const SavingOverlay(saveCompleted: true)); + await Future.delayed(const Duration(seconds: 1)); + overlayWidget.hide(); }, icon: const Icon( Icons.save, diff --git a/lib/screens/home_page/editor_pane/details_card/request_pane/request_form_data.dart b/lib/screens/home_page/editor_pane/details_card/request_pane/request_form_data.dart index 5d518e22..b3d8f67c 100644 --- a/lib/screens/home_page/editor_pane/details_card/request_pane/request_form_data.dart +++ b/lib/screens/home_page/editor_pane/details_card/request_pane/request_form_data.dart @@ -17,22 +17,37 @@ class FormDataWidget extends ConsumerStatefulWidget { class _FormDataBodyState extends ConsumerState { late int seed; final random = Random.secure(); - late List rows; + late List formRows; + @override void initState() { super.initState(); seed = random.nextInt(kRandMax); } + void _onFieldChange(String selectedId) { + ref.read(collectionStateNotifierProvider.notifier).update( + selectedId, + requestFormDataList: formRows.sublist(0, formRows.length - 1), + ); + } + @override Widget build(BuildContext context) { final selectedId = ref.watch(selectedIdStateProvider); - var formRows = ref.read(selectedRequestModelProvider)?.requestFormDataList; - rows = - formRows == null || formRows.isEmpty ? [kFormDataEmptyModel] : formRows; + ref.watch(selectedRequestModelProvider + .select((value) => value?.requestFormDataList?.length)); + var rF = ref.read(selectedRequestModelProvider)?.requestFormDataList; + bool isFormDataEmpty = rF == null || rF.isEmpty; + List rows = (isFormDataEmpty) + ? [ + kFormDataEmptyModel, + ] + : rF; + formRows = isFormDataEmpty ? rows : rows + [kFormDataEmptyModel]; DaviModel daviModelRows = DaviModel( - rows: rows, + rows: formRows, columns: [ DaviColumn( cellPadding: kpsV5, @@ -40,25 +55,29 @@ class _FormDataBodyState extends ConsumerState { grow: 4, cellBuilder: (_, row) { int idx = row.index; + bool isLast = idx + 1 == formRows.length; return Theme( data: Theme.of(context), child: FormDataField( keyId: "$selectedId-$idx-form-v-$seed", - initialValue: rows[idx].name, + initialValue: formRows[idx].name, hintText: " Add Key", onChanged: (value) { - rows[idx] = rows[idx].copyWith( - name: value, - ); + formRows[idx] = formRows[idx].copyWith(name: value); + if (isLast) formRows.add(kFormDataEmptyModel); _onFieldChange(selectedId!); }, colorScheme: Theme.of(context).colorScheme, - formDataType: rows[idx].type, + formDataType: formRows[idx].type, onFormDataTypeChanged: (value) { - rows[idx] = rows[idx].copyWith( + bool hasChanged = formRows[idx].type != value; + formRows[idx] = formRows[idx].copyWith( type: value ?? FormDataType.text, ); - rows[idx] = rows[idx].copyWith(value: ""); + formRows[idx] = formRows[idx].copyWith(value: ""); + if (idx == formRows.length - 1 && hasChanged) { + formRows.add(kFormDataEmptyModel); + } setState(() {}); _onFieldChange(selectedId!); }, @@ -84,7 +103,8 @@ class _FormDataBodyState extends ConsumerState { cellPadding: kpsV5, cellBuilder: (_, row) { int idx = row.index; - return rows[idx].type == FormDataType.file + bool isLast = idx + 1 == formRows.length; + return formRows[idx].type == FormDataType.file ? Align( alignment: Alignment.centerLeft, child: Row( @@ -109,7 +129,7 @@ class _FormDataBodyState extends ConsumerState { if (pickedResult != null && pickedResult.files.isNotEmpty && pickedResult.files.first.path != null) { - rows[idx] = rows[idx].copyWith( + formRows[idx] = formRows[idx].copyWith( value: pickedResult.files.first.path!, ); setState(() {}); @@ -117,9 +137,9 @@ class _FormDataBodyState extends ConsumerState { } }, label: Text( - (rows[idx].type == FormDataType.file && - rows[idx].value.isNotEmpty) - ? rows[idx].value.toString() + (formRows[idx].type == FormDataType.file && + formRows[idx].value.isNotEmpty) + ? formRows[idx].value.toString() : "Select File", textAlign: TextAlign.center, overflow: TextOverflow.ellipsis, @@ -133,10 +153,11 @@ class _FormDataBodyState extends ConsumerState { ) : CellField( keyId: "$selectedId-$idx-form-v-$seed", - initialValue: rows[idx].value, + initialValue: formRows[idx].value, hintText: " Add Value", onChanged: (value) { - rows[idx] = rows[idx].copyWith(value: value); + formRows[idx] = formRows[idx].copyWith(value: value); + if (isLast) formRows.add(kFormDataEmptyModel); _onFieldChange(selectedId!); }, colorScheme: Theme.of(context).colorScheme, @@ -148,22 +169,24 @@ class _FormDataBodyState extends ConsumerState { pinStatus: PinStatus.none, width: 30, cellBuilder: (_, row) { + bool isLast = row.index + 1 == formRows.length; return InkWell( + onTap: isLast + ? null + : () { + seed = random.nextInt(kRandMax); + if (formRows.length == 2) { + setState(() { + formRows = [kFormDataEmptyModel]; + }); + } else { + formRows.removeAt(row.index); + } + _onFieldChange(selectedId!); + }, child: Theme.of(context).brightness == Brightness.dark ? kIconRemoveDark : kIconRemoveLight, - onTap: () { - seed = random.nextInt(kRandMax); - if (rows.length == 1) { - setState(() { - rows = [kFormDataEmptyModel]; - }); - } else { - rows.removeAt(row.index); - } - _onFieldChange(selectedId!); - setState(() {}); - }, ); }, ), @@ -194,9 +217,7 @@ class _FormDataBodyState extends ConsumerState { padding: const EdgeInsets.only(bottom: 30), child: ElevatedButton.icon( onPressed: () { - setState(() { - rows.add(kFormDataEmptyModel); - }); + formRows.add(kFormDataEmptyModel); _onFieldChange(selectedId!); }, icon: const Icon(Icons.add), @@ -210,11 +231,4 @@ class _FormDataBodyState extends ConsumerState { ], ); } - - void _onFieldChange(String selectedId) { - ref.read(collectionStateNotifierProvider.notifier).update( - selectedId, - requestFormDataList: rows, - ); - } } diff --git a/lib/screens/home_page/editor_pane/details_card/request_pane/request_headers.dart b/lib/screens/home_page/editor_pane/details_card/request_pane/request_headers.dart index 503355f8..e5b0df59 100644 --- a/lib/screens/home_page/editor_pane/details_card/request_pane/request_headers.dart +++ b/lib/screens/home_page/editor_pane/details_card/request_pane/request_headers.dart @@ -15,10 +15,10 @@ class EditRequestHeaders extends ConsumerStatefulWidget { } class EditRequestHeadersState extends ConsumerState { - final random = Random.secure(); - late List rows; - late List isRowEnabledList; late int seed; + final random = Random.secure(); + late List headerRows; + late List isRowEnabledList; @override void initState() { @@ -29,8 +29,9 @@ class EditRequestHeadersState extends ConsumerState { void _onFieldChange(String selectedId) { ref.read(collectionStateNotifierProvider.notifier).update( selectedId, - requestHeaders: rows, - isHeaderEnabledList: isRowEnabledList, + requestHeaders: headerRows.sublist(0, headerRows.length - 1), + isHeaderEnabledList: + isRowEnabledList.sublist(0, headerRows.length - 1), ); } @@ -40,32 +41,38 @@ class EditRequestHeadersState extends ConsumerState { ref.watch(selectedRequestModelProvider .select((value) => value?.requestHeaders?.length)); var rH = ref.read(selectedRequestModelProvider)?.requestHeaders; - rows = (rH == null || rH.isEmpty) + bool isHeadersEmpty = rH == null || rH.isEmpty; + List rows = (isHeadersEmpty) ? [ kNameValueEmptyModel, ] : rH; + headerRows = isHeadersEmpty ? rows : rows + [kNameValueEmptyModel]; isRowEnabledList = ref.read(selectedRequestModelProvider)?.isHeaderEnabledList ?? - List.filled(rows.length, true, growable: true); + List.filled(rH?.length ?? 0, true, growable: true); + isRowEnabledList.add(false); DaviModel model = DaviModel( - rows: rows, + rows: headerRows, columns: [ DaviColumn( name: 'Checkbox', width: 30, cellBuilder: (_, row) { int idx = row.index; + bool isLast = idx + 1 == headerRows.length; return CheckBox( keyId: "$selectedId-$idx-headers-c-$seed", value: isRowEnabledList[idx], - onChanged: (value) { - setState(() { - isRowEnabledList[idx] = value!; - }); - _onFieldChange(selectedId!); - }, + onChanged: isLast + ? null + : (value) { + setState(() { + isRowEnabledList[idx] = value!; + }); + _onFieldChange(selectedId!); + }, colorScheme: Theme.of(context).colorScheme, ); }, @@ -76,12 +83,18 @@ class EditRequestHeadersState extends ConsumerState { grow: 1, cellBuilder: (_, row) { int idx = row.index; + bool isLast = idx + 1 == headerRows.length; return HeaderField( keyId: "$selectedId-$idx-headers-k-$seed", - initialValue: rows[idx].name, + initialValue: headerRows[idx].name, hintText: "Add Header Name", onChanged: (value) { - rows[idx] = rows[idx].copyWith(name: value); + headerRows[idx] = headerRows[idx].copyWith(name: value); + if (isLast) { + isRowEnabledList[idx] = true; + headerRows.add(kNameValueEmptyModel); + isRowEnabledList.add(false); + } _onFieldChange(selectedId!); }, colorScheme: Theme.of(context).colorScheme, @@ -103,12 +116,18 @@ class EditRequestHeadersState extends ConsumerState { grow: 1, cellBuilder: (_, row) { int idx = row.index; + bool isLast = idx + 1 == headerRows.length; return CellField( keyId: "$selectedId-$idx-headers-v-$seed", - initialValue: rows[idx].value, + initialValue: headerRows[idx].value, hintText: " Add Header Value", onChanged: (value) { - rows[idx] = rows[idx].copyWith(value: value); + headerRows[idx] = headerRows[idx].copyWith(value: value); + if (isLast) { + isRowEnabledList[idx] = true; + headerRows.add(kNameValueEmptyModel); + isRowEnabledList.add(false); + } _onFieldChange(selectedId!); }, colorScheme: Theme.of(context).colorScheme, @@ -120,25 +139,28 @@ class EditRequestHeadersState extends ConsumerState { pinStatus: PinStatus.none, width: 30, cellBuilder: (_, row) { + bool isLast = row.index + 1 == headerRows.length; return InkWell( + onTap: isLast + ? null + : () { + seed = random.nextInt(kRandMax); + if (headerRows.length == 2) { + setState(() { + headerRows = [ + kNameValueEmptyModel, + ]; + isRowEnabledList = [false]; + }); + } else { + headerRows.removeAt(row.index); + isRowEnabledList.removeAt(row.index); + } + _onFieldChange(selectedId!); + }, child: Theme.of(context).brightness == Brightness.dark ? kIconRemoveDark : kIconRemoveLight, - onTap: () { - seed = random.nextInt(kRandMax); - if (rows.length == 1) { - setState(() { - rows = [ - kNameValueEmptyModel, - ]; - isRowEnabledList = [true]; - }); - } else { - rows.removeAt(row.index); - isRowEnabledList.removeAt(row.index); - } - _onFieldChange(selectedId!); - }, ); }, ), @@ -169,8 +191,8 @@ class EditRequestHeadersState extends ConsumerState { padding: const EdgeInsets.only(bottom: 30), child: ElevatedButton.icon( onPressed: () { - rows.add(kNameValueEmptyModel); - isRowEnabledList.add(true); + headerRows.add(kNameValueEmptyModel); + isRowEnabledList.add(false); _onFieldChange(selectedId!); }, icon: const Icon(Icons.add), diff --git a/lib/screens/home_page/editor_pane/details_card/request_pane/request_params.dart b/lib/screens/home_page/editor_pane/details_card/request_pane/request_params.dart index 6e3aca4b..319330ba 100644 --- a/lib/screens/home_page/editor_pane/details_card/request_pane/request_params.dart +++ b/lib/screens/home_page/editor_pane/details_card/request_pane/request_params.dart @@ -16,10 +16,10 @@ class EditRequestURLParams extends ConsumerStatefulWidget { } class EditRequestURLParamsState extends ConsumerState { - final random = Random.secure(); - late List rows; - late List isRowEnabledList; late int seed; + final random = Random.secure(); + late List paramRows; + late List isRowEnabledList; @override void initState() { @@ -30,8 +30,8 @@ class EditRequestURLParamsState extends ConsumerState { void _onFieldChange(String selectedId) { ref.read(collectionStateNotifierProvider.notifier).update( selectedId, - requestParams: rows, - isParamEnabledList: isRowEnabledList, + requestParams: paramRows.sublist(0, paramRows.length - 1), + isParamEnabledList: isRowEnabledList.sublist(0, paramRows.length - 1), ); } @@ -41,32 +41,38 @@ class EditRequestURLParamsState extends ConsumerState { ref.watch(selectedRequestModelProvider .select((value) => value?.requestParams?.length)); var rP = ref.read(selectedRequestModelProvider)?.requestParams; - rows = (rP == null || rP.isEmpty) + bool isParamsEmpty = rP == null || rP.isEmpty; + List rows = (isParamsEmpty) ? [ kNameValueEmptyModel, ] : rP; + paramRows = isParamsEmpty ? rows : rows + [kNameValueEmptyModel]; isRowEnabledList = ref.read(selectedRequestModelProvider)?.isParamEnabledList ?? - List.filled(rows.length, true, growable: true); + List.filled(rP?.length ?? 0, true, growable: true); + isRowEnabledList.add(false); DaviModel model = DaviModel( - rows: rows, + rows: paramRows, columns: [ DaviColumn( name: 'Checkbox', width: 30, cellBuilder: (_, row) { int idx = row.index; + bool isLast = idx + 1 == paramRows.length; return CheckBox( keyId: "$selectedId-$idx-params-c-$seed", value: isRowEnabledList[idx], - onChanged: (value) { - setState(() { - isRowEnabledList[idx] = value!; - }); - _onFieldChange(selectedId!); - }, + onChanged: isLast + ? null + : (value) { + setState(() { + isRowEnabledList[idx] = value!; + }); + _onFieldChange(selectedId!); + }, colorScheme: Theme.of(context).colorScheme, ); }, @@ -77,12 +83,18 @@ class EditRequestURLParamsState extends ConsumerState { grow: 1, cellBuilder: (_, row) { int idx = row.index; + bool isLast = idx + 1 == paramRows.length; return CellField( keyId: "$selectedId-$idx-params-k-$seed", - initialValue: rows[idx].name, + initialValue: paramRows[idx].name, hintText: "Add URL Parameter", onChanged: (value) { - rows[idx] = rows[idx].copyWith(name: value); + paramRows[idx] = paramRows[idx].copyWith(name: value); + if (isLast) { + isRowEnabledList[idx] = true; + paramRows.add(kNameValueEmptyModel); + isRowEnabledList.add(false); + } _onFieldChange(selectedId!); }, colorScheme: Theme.of(context).colorScheme, @@ -104,12 +116,18 @@ class EditRequestURLParamsState extends ConsumerState { grow: 1, cellBuilder: (_, row) { int idx = row.index; + bool isLast = idx + 1 == paramRows.length; return CellField( keyId: "$selectedId-$idx-params-v-$seed", - initialValue: rows[idx].value, + initialValue: paramRows[idx].value, hintText: "Add Value", onChanged: (value) { - rows[idx] = rows[idx].copyWith(value: value); + paramRows[idx] = paramRows[idx].copyWith(value: value); + if (isLast) { + isRowEnabledList[idx] = true; + paramRows.add(kNameValueEmptyModel); + isRowEnabledList.add(false); + } _onFieldChange(selectedId!); }, colorScheme: Theme.of(context).colorScheme, @@ -121,25 +139,28 @@ class EditRequestURLParamsState extends ConsumerState { pinStatus: PinStatus.none, width: 30, cellBuilder: (_, row) { + bool isLast = row.index + 1 == paramRows.length; return InkWell( + onTap: isLast + ? null + : () { + seed = random.nextInt(kRandMax); + if (paramRows.length == 2) { + setState(() { + paramRows = [ + kNameValueEmptyModel, + ]; + isRowEnabledList = [false]; + }); + } else { + paramRows.removeAt(row.index); + isRowEnabledList.removeAt(row.index); + } + _onFieldChange(selectedId!); + }, child: Theme.of(context).brightness == Brightness.dark ? kIconRemoveDark : kIconRemoveLight, - onTap: () { - seed = random.nextInt(kRandMax); - if (rows.length == 1) { - setState(() { - rows = [ - kNameValueEmptyModel, - ]; - isRowEnabledList = [true]; - }); - } else { - rows.removeAt(row.index); - isRowEnabledList.removeAt(row.index); - } - _onFieldChange(selectedId!); - }, ); }, ), @@ -170,8 +191,8 @@ class EditRequestURLParamsState extends ConsumerState { padding: const EdgeInsets.only(bottom: 30), child: ElevatedButton.icon( onPressed: () { - rows.add(kNameValueEmptyModel); - isRowEnabledList.add(true); + paramRows.add(kNameValueEmptyModel); + isRowEnabledList.add(false); _onFieldChange(selectedId!); }, icon: const Icon(Icons.add), diff --git a/lib/widgets/checkbox.dart b/lib/widgets/checkbox.dart index 22c3869f..53adf496 100644 --- a/lib/widgets/checkbox.dart +++ b/lib/widgets/checkbox.dart @@ -3,7 +3,7 @@ import 'package:flutter/material.dart'; class CheckBox extends StatelessWidget { final String keyId; final bool value; - final ValueChanged onChanged; + final ValueChanged? onChanged; final ColorScheme? colorScheme; const CheckBox({ super.key, diff --git a/lib/widgets/intro_message.dart b/lib/widgets/intro_message.dart index 7e7ff2e0..e4e4f49a 100644 --- a/lib/widgets/intro_message.dart +++ b/lib/widgets/intro_message.dart @@ -16,7 +16,7 @@ class IntroMessage extends StatelessWidget { late final String version; Future introData() async { - text = await rootBundle.loadString('assets/intro.md'); + text = await rootBundle.loadString(kAssetIntroMd); version = (await PackageInfo.fromPlatform()).version; } diff --git a/lib/widgets/overlay_widget.dart b/lib/widgets/overlay_widget.dart new file mode 100644 index 00000000..ed800521 --- /dev/null +++ b/lib/widgets/overlay_widget.dart @@ -0,0 +1,63 @@ +import 'package:flutter/material.dart'; +import 'package:lottie/lottie.dart'; +import '../consts.dart'; + +class OverlayWidgetTemplate { + OverlayEntry? _overlay; + BuildContext context; + OverlayState? _overlayState; + OverlayWidgetTemplate({required this.context}) { + _overlayState = Overlay.of(context); + } + + void show({required Widget widget}) { + if (_overlay == null) { + _overlay = OverlayEntry( + // replace with your own layout + builder: (context) => ColoredBox( + color: kColorBlack.withOpacity(kOverlayBackgroundOpacity), + child: widget), + ); + _overlayState!.insert(_overlay!); + } + } + + void hide() { + if (_overlay != null) { + _overlay?.remove(); + _overlay = null; + } + } +} + +class SavingOverlay extends StatelessWidget { + final bool saveCompleted; + const SavingOverlay({super.key, required this.saveCompleted}); + + @override + Widget build(BuildContext context) { + return Center( + child: Card( + child: Padding( + padding: kPh60v60, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Lottie.asset( + saveCompleted ? kAssetSavedLottie : kAssetSavingLottie, + width: 100, + height: 100), + kHSpacer20, + Text( + saveCompleted ? kLabelSaved : kLabelSaving, + style: const TextStyle( + fontSize: kDefaultFontSize, + ), + ) + ], + ), + ), + ), + ); + } +} diff --git a/lib/widgets/response_widgets.dart b/lib/widgets/response_widgets.dart index adf74985..e641b03d 100644 --- a/lib/widgets/response_widgets.dart +++ b/lib/widgets/response_widgets.dart @@ -42,7 +42,7 @@ class SendingWidget extends StatelessWidget { child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ - Lottie.asset("assets/sending.json"), + Lottie.asset(kAssetSendingLottie), ], ), ); diff --git a/lib/widgets/widgets.dart b/lib/widgets/widgets.dart index bb54b3ff..e4e842fb 100644 --- a/lib/widgets/widgets.dart +++ b/lib/widgets/widgets.dart @@ -13,6 +13,7 @@ export 'intro_message.dart'; export 'json_previewer.dart'; export 'markdown.dart'; export 'menus.dart'; +export 'overlay_widget.dart'; export 'previewer.dart'; export 'request_widgets.dart'; export 'response_widgets.dart'; diff --git a/test/codegen/rust_curl_codegen_test.dart b/test/codegen/rust_curl_codegen_test.dart new file mode 100644 index 00000000..badc18ba --- /dev/null +++ b/test/codegen/rust_curl_codegen_test.dart @@ -0,0 +1,992 @@ +import 'package:apidash/codegen/codegen.dart'; +import 'package:apidash/consts.dart'; +import 'package:test/test.dart'; +import '../request_models.dart'; + +void main() { + final codeGen = Codegen(); + + group('GET Request', () { + test('GET1', () { + const expectedCode = r""" +use curl::easy::Easy; + +fn main() { + let mut easy = Easy::new(); + let mut data = Vec::new(); + easy.url("https://api.apidash.dev").unwrap(); + easy.get(true).unwrap(); + + { + let mut transfer = easy.transfer(); + transfer.write_function(|new_data| { + data.extend_from_slice(new_data); + Ok(new_data.len()) + }).unwrap(); + transfer.perform().unwrap(); + } + + let response_body = String::from_utf8_lossy(&data); + + println!("Response body: {}", response_body); + println!("Response code: {}", easy.response_code().unwrap()); +}"""; + expect( + codeGen.getCode( + CodegenLanguage.rustCurl, requestModelGet1, "https"), + expectedCode); + }); + test('GET2', () { + const expectedCode = r""" +use curl::easy::Easy; + +fn main() { + let mut easy = Easy::new(); + let mut data = Vec::new(); + easy.url("https://api.apidash.dev/country/data?code=US").unwrap(); + easy.get(true).unwrap(); + + { + let mut transfer = easy.transfer(); + transfer.write_function(|new_data| { + data.extend_from_slice(new_data); + Ok(new_data.len()) + }).unwrap(); + transfer.perform().unwrap(); + } + + let response_body = String::from_utf8_lossy(&data); + + println!("Response body: {}", response_body); + println!("Response code: {}", easy.response_code().unwrap()); +}"""; + expect( + codeGen.getCode( + CodegenLanguage.rustCurl, requestModelGet2, "https"), + expectedCode); + }); + test('GET3', () { + const expectedCode = r""" +use curl::easy::Easy; + +fn main() { + let mut easy = Easy::new(); + let mut data = Vec::new(); + easy.url("https://api.apidash.dev/country/data?code=IND").unwrap(); + easy.get(true).unwrap(); + + { + let mut transfer = easy.transfer(); + transfer.write_function(|new_data| { + data.extend_from_slice(new_data); + Ok(new_data.len()) + }).unwrap(); + transfer.perform().unwrap(); + } + + let response_body = String::from_utf8_lossy(&data); + + println!("Response body: {}", response_body); + println!("Response code: {}", easy.response_code().unwrap()); +}"""; + expect( + codeGen.getCode( + CodegenLanguage.rustCurl, requestModelGet3, "https"), + expectedCode); + }); + test('GET4', () { + const expectedCode = r""" +use curl::easy::Easy; + +fn main() { + let mut easy = Easy::new(); + let mut data = Vec::new(); + easy.url("https://api.apidash.dev/humanize/social?num=8700000&digits=3&system=SS&add_space=true&trailing_zeros=true").unwrap(); + easy.get(true).unwrap(); + + { + let mut transfer = easy.transfer(); + transfer.write_function(|new_data| { + data.extend_from_slice(new_data); + Ok(new_data.len()) + }).unwrap(); + transfer.perform().unwrap(); + } + + let response_body = String::from_utf8_lossy(&data); + + println!("Response body: {}", response_body); + println!("Response code: {}", easy.response_code().unwrap()); +}"""; + expect( + codeGen.getCode( + CodegenLanguage.rustCurl, requestModelGet4, "https"), + expectedCode); + }); + test('GET5', () { + const expectedCode = r""" +use curl::easy::Easy; +use curl::easy::List; + +fn main() { + let mut easy = Easy::new(); + let mut data = Vec::new(); + easy.url("https://api.github.com/repos/foss42/apidash").unwrap(); + easy.get(true).unwrap(); + + let mut list = List::new(); + list.append("User-Agent: Test Agent").unwrap(); + easy.http_headers(list).unwrap(); + + { + let mut transfer = easy.transfer(); + transfer.write_function(|new_data| { + data.extend_from_slice(new_data); + Ok(new_data.len()) + }).unwrap(); + transfer.perform().unwrap(); + } + + let response_body = String::from_utf8_lossy(&data); + + println!("Response body: {}", response_body); + println!("Response code: {}", easy.response_code().unwrap()); +}"""; + expect( + codeGen.getCode( + CodegenLanguage.rustCurl, requestModelGet5, "https"), + expectedCode); + }); + test('GET6', () { + const expectedCode = r""" +use curl::easy::Easy; +use curl::easy::List; + +fn main() { + let mut easy = Easy::new(); + let mut data = Vec::new(); + easy.url("https://api.github.com/repos/foss42/apidash?raw=true").unwrap(); + easy.get(true).unwrap(); + + let mut list = List::new(); + list.append("User-Agent: Test Agent").unwrap(); + easy.http_headers(list).unwrap(); + + { + let mut transfer = easy.transfer(); + transfer.write_function(|new_data| { + data.extend_from_slice(new_data); + Ok(new_data.len()) + }).unwrap(); + transfer.perform().unwrap(); + } + + let response_body = String::from_utf8_lossy(&data); + + println!("Response body: {}", response_body); + println!("Response code: {}", easy.response_code().unwrap()); +}"""; + expect( + codeGen.getCode( + CodegenLanguage.rustCurl, requestModelGet6, "https"), + expectedCode); + }); + test('GET7', () { + const expectedCode = r""" +use curl::easy::Easy; + +fn main() { + let mut easy = Easy::new(); + let mut data = Vec::new(); + easy.url("https://api.apidash.dev").unwrap(); + easy.get(true).unwrap(); + + { + let mut transfer = easy.transfer(); + transfer.write_function(|new_data| { + data.extend_from_slice(new_data); + Ok(new_data.len()) + }).unwrap(); + transfer.perform().unwrap(); + } + + let response_body = String::from_utf8_lossy(&data); + + println!("Response body: {}", response_body); + println!("Response code: {}", easy.response_code().unwrap()); +}"""; + expect( + codeGen.getCode( + CodegenLanguage.rustCurl, requestModelGet7, "https"), + expectedCode); + }); + test('GET8', () { + const expectedCode = r""" +use curl::easy::Easy; +use curl::easy::List; + +fn main() { + let mut easy = Easy::new(); + let mut data = Vec::new(); + easy.url("https://api.github.com/repos/foss42/apidash?raw=true").unwrap(); + easy.get(true).unwrap(); + + let mut list = List::new(); + list.append("User-Agent: Test Agent").unwrap(); + easy.http_headers(list).unwrap(); + + { + let mut transfer = easy.transfer(); + transfer.write_function(|new_data| { + data.extend_from_slice(new_data); + Ok(new_data.len()) + }).unwrap(); + transfer.perform().unwrap(); + } + + let response_body = String::from_utf8_lossy(&data); + + println!("Response body: {}", response_body); + println!("Response code: {}", easy.response_code().unwrap()); +}"""; + expect( + codeGen.getCode( + CodegenLanguage.rustCurl, requestModelGet8, "https"), + expectedCode); + }); + test('GET9', () { + const expectedCode = r""" +use curl::easy::Easy; + +fn main() { + let mut easy = Easy::new(); + let mut data = Vec::new(); + easy.url("https://api.apidash.dev/humanize/social?num=8700000&add_space=true").unwrap(); + easy.get(true).unwrap(); + + { + let mut transfer = easy.transfer(); + transfer.write_function(|new_data| { + data.extend_from_slice(new_data); + Ok(new_data.len()) + }).unwrap(); + transfer.perform().unwrap(); + } + + let response_body = String::from_utf8_lossy(&data); + + println!("Response body: {}", response_body); + println!("Response code: {}", easy.response_code().unwrap()); +}"""; + expect( + codeGen.getCode( + CodegenLanguage.rustCurl, requestModelGet9, "https"), + expectedCode); + }); + test('GET10', () { + const expectedCode = r""" +use curl::easy::Easy; +use curl::easy::List; + +fn main() { + let mut easy = Easy::new(); + let mut data = Vec::new(); + easy.url("https://api.apidash.dev/humanize/social").unwrap(); + easy.get(true).unwrap(); + + let mut list = List::new(); + list.append("User-Agent: Test Agent").unwrap(); + easy.http_headers(list).unwrap(); + + { + let mut transfer = easy.transfer(); + transfer.write_function(|new_data| { + data.extend_from_slice(new_data); + Ok(new_data.len()) + }).unwrap(); + transfer.perform().unwrap(); + } + + let response_body = String::from_utf8_lossy(&data); + + println!("Response body: {}", response_body); + println!("Response code: {}", easy.response_code().unwrap()); +}"""; + expect( + codeGen.getCode( + CodegenLanguage.rustCurl, requestModelGet10, "https"), + expectedCode); + }); + test('GET11', () { + const expectedCode = r""" +use curl::easy::Easy; +use curl::easy::List; + +fn main() { + let mut easy = Easy::new(); + let mut data = Vec::new(); + easy.url("https://api.apidash.dev/humanize/social?num=8700000&digits=3").unwrap(); + easy.get(true).unwrap(); + + let mut list = List::new(); + list.append("User-Agent: Test Agent").unwrap(); + easy.http_headers(list).unwrap(); + + { + let mut transfer = easy.transfer(); + transfer.write_function(|new_data| { + data.extend_from_slice(new_data); + Ok(new_data.len()) + }).unwrap(); + transfer.perform().unwrap(); + } + + let response_body = String::from_utf8_lossy(&data); + + println!("Response body: {}", response_body); + println!("Response code: {}", easy.response_code().unwrap()); +}"""; + expect( + codeGen.getCode( + CodegenLanguage.rustCurl, requestModelGet11, "https"), + expectedCode); + }); + test('GET12', () { + const expectedCode = r""" +use curl::easy::Easy; + +fn main() { + let mut easy = Easy::new(); + let mut data = Vec::new(); + easy.url("https://api.apidash.dev/humanize/social").unwrap(); + easy.get(true).unwrap(); + + { + let mut transfer = easy.transfer(); + transfer.write_function(|new_data| { + data.extend_from_slice(new_data); + Ok(new_data.len()) + }).unwrap(); + transfer.perform().unwrap(); + } + + let response_body = String::from_utf8_lossy(&data); + + println!("Response body: {}", response_body); + println!("Response code: {}", easy.response_code().unwrap()); +}"""; + expect( + codeGen.getCode( + CodegenLanguage.rustCurl, requestModelGet12, "https"), + expectedCode); + }); + }); + + group('HEAD Request', () { + test('HEAD1', () { + const expectedCode = r""" +use curl::easy::Easy; + +fn main() { + let mut easy = Easy::new(); + let mut data = Vec::new(); + easy.url("https://api.apidash.dev").unwrap(); + easy.nobody(true).unwrap(); + + { + let mut transfer = easy.transfer(); + transfer.write_function(|new_data| { + data.extend_from_slice(new_data); + Ok(new_data.len()) + }).unwrap(); + transfer.perform().unwrap(); + } + + let response_body = String::from_utf8_lossy(&data); + + println!("Response body: {}", response_body); + println!("Response code: {}", easy.response_code().unwrap()); +}"""; + expect( + codeGen.getCode( + CodegenLanguage.rustCurl, requestModelHead1, "https"), + expectedCode); + }); + test('HEAD2', () { + const expectedCode = r""" +use curl::easy::Easy; + +fn main() { + let mut easy = Easy::new(); + let mut data = Vec::new(); + easy.url("https://api.apidash.dev").unwrap(); + easy.nobody(true).unwrap(); + + { + let mut transfer = easy.transfer(); + transfer.write_function(|new_data| { + data.extend_from_slice(new_data); + Ok(new_data.len()) + }).unwrap(); + transfer.perform().unwrap(); + } + + let response_body = String::from_utf8_lossy(&data); + + println!("Response body: {}", response_body); + println!("Response code: {}", easy.response_code().unwrap()); +}"""; + expect( + codeGen.getCode( + CodegenLanguage.rustCurl, requestModelHead2, "https"), + expectedCode); + }); + }); + + group('POST Request', () { + test('POST1', () { + const expectedCode = r""" +use curl::easy::Easy; +use curl::easy::List; + +fn main() { + let mut easy = Easy::new(); + let mut data = Vec::new(); + easy.url("https://api.apidash.dev/case/lower").unwrap(); + easy.post(true).unwrap(); + + easy.post_fields_copy(r#"{ +"text": "I LOVE Flutter" +}"#.as_bytes()).unwrap(); + + let mut list = List::new(); + list.append("Content-Type: text/plain").unwrap(); + easy.http_headers(list).unwrap(); + + { + let mut transfer = easy.transfer(); + transfer.write_function(|new_data| { + data.extend_from_slice(new_data); + Ok(new_data.len()) + }).unwrap(); + transfer.perform().unwrap(); + } + + let response_body = String::from_utf8_lossy(&data); + + println!("Response body: {}", response_body); + println!("Response code: {}", easy.response_code().unwrap()); +}"""; + expect( + codeGen.getCode( + CodegenLanguage.rustCurl, requestModelPost1, "https"), + expectedCode); + }); + test('POST2', () { + const expectedCode = r""" +use curl::easy::Easy; +use serde_json::json; +use curl::easy::List; + +fn main() { + let mut easy = Easy::new(); + let mut data = Vec::new(); + easy.url("https://api.apidash.dev/case/lower").unwrap(); + easy.post(true).unwrap(); + + easy.post_fields_copy(json!({ +"text": "I LOVE Flutter", +"flag": null, +"male": true, +"female": false, +"no": 1.2, +"arr": ["null", "true", "false", null] +}).to_string().as_bytes()).unwrap(); + + let mut list = List::new(); + list.append("Content-Type: application/json").unwrap(); + easy.http_headers(list).unwrap(); + + { + let mut transfer = easy.transfer(); + transfer.write_function(|new_data| { + data.extend_from_slice(new_data); + Ok(new_data.len()) + }).unwrap(); + transfer.perform().unwrap(); + } + + let response_body = String::from_utf8_lossy(&data); + + println!("Response body: {}", response_body); + println!("Response code: {}", easy.response_code().unwrap()); +}"""; + expect( + codeGen.getCode( + CodegenLanguage.rustCurl, requestModelPost2, "https"), + expectedCode); + }); + test('POST3', () { + const expectedCode = r""" +use curl::easy::Easy; +use serde_json::json; +use curl::easy::List; + +fn main() { + let mut easy = Easy::new(); + let mut data = Vec::new(); + easy.url("https://api.apidash.dev/case/lower").unwrap(); + easy.post(true).unwrap(); + + easy.post_fields_copy(json!({ +"text": "I LOVE Flutter" +}).to_string().as_bytes()).unwrap(); + + let mut list = List::new(); + list.append("User-Agent: Test Agent").unwrap(); + list.append("Content-Type: application/json").unwrap(); + easy.http_headers(list).unwrap(); + + { + let mut transfer = easy.transfer(); + transfer.write_function(|new_data| { + data.extend_from_slice(new_data); + Ok(new_data.len()) + }).unwrap(); + transfer.perform().unwrap(); + } + + let response_body = String::from_utf8_lossy(&data); + + println!("Response body: {}", response_body); + println!("Response code: {}", easy.response_code().unwrap()); +}"""; + expect( + codeGen.getCode( + CodegenLanguage.rustCurl, requestModelPost3, "https"), + expectedCode); + }); + test('POST4', () { + const expectedCode = r""" +use curl::easy::Easy; + +fn main() { + let mut easy = Easy::new(); + let mut data = Vec::new(); + easy.url("https://api.apidash.dev/io/form").unwrap(); + easy.post(true).unwrap(); + + let mut form = curl::easy::Form::new(); + + form.part("text") + .contents(b"API") + .add().unwrap(); + + form.part("sep") + .contents(b"|") + .add().unwrap(); + + form.part("times") + .contents(b"3") + .add().unwrap(); + + easy.httppost(form).unwrap(); + { + let mut transfer = easy.transfer(); + transfer.write_function(|new_data| { + data.extend_from_slice(new_data); + Ok(new_data.len()) + }).unwrap(); + transfer.perform().unwrap(); + } + + let response_body = String::from_utf8_lossy(&data); + + println!("Response body: {}", response_body); + println!("Response code: {}", easy.response_code().unwrap()); +}"""; + expect( + codeGen.getCode( + CodegenLanguage.rustCurl, requestModelPost4, "https"), + expectedCode); + }); + test('POST5', () { + const expectedCode = r""" +use curl::easy::Easy; +use curl::easy::List; + +fn main() { + let mut easy = Easy::new(); + let mut data = Vec::new(); + easy.url("https://api.apidash.dev/io/form").unwrap(); + easy.post(true).unwrap(); + + let mut form = curl::easy::Form::new(); + + form.part("text") + .contents(b"API") + .add().unwrap(); + + form.part("sep") + .contents(b"|") + .add().unwrap(); + + form.part("times") + .contents(b"3") + .add().unwrap(); + + easy.httppost(form).unwrap(); + let mut list = List::new(); + list.append("User-Agent: Test Agent").unwrap(); + easy.http_headers(list).unwrap(); + + { + let mut transfer = easy.transfer(); + transfer.write_function(|new_data| { + data.extend_from_slice(new_data); + Ok(new_data.len()) + }).unwrap(); + transfer.perform().unwrap(); + } + + let response_body = String::from_utf8_lossy(&data); + + println!("Response body: {}", response_body); + println!("Response code: {}", easy.response_code().unwrap()); +}"""; + expect( + codeGen.getCode( + CodegenLanguage.rustCurl, requestModelPost5, "https"), + expectedCode); + }); + test('POST6', () { + const expectedCode = r""" +use curl::easy::Easy; + +fn main() { + let mut easy = Easy::new(); + let mut data = Vec::new(); + easy.url("https://api.apidash.dev/io/img").unwrap(); + easy.post(true).unwrap(); + + let mut form = curl::easy::Form::new(); + + form.part("token") + .contents(b"xyz") + .add().unwrap(); + + form.part("imfile") + .file("/Documents/up/1.png") + .add().unwrap(); + + easy.httppost(form).unwrap(); + { + let mut transfer = easy.transfer(); + transfer.write_function(|new_data| { + data.extend_from_slice(new_data); + Ok(new_data.len()) + }).unwrap(); + transfer.perform().unwrap(); + } + + let response_body = String::from_utf8_lossy(&data); + + println!("Response body: {}", response_body); + println!("Response code: {}", easy.response_code().unwrap()); +}"""; + expect( + codeGen.getCode( + CodegenLanguage.rustCurl, requestModelPost6, "https"), + expectedCode); + }); + test('POST7', () { + const expectedCode = r""" +use curl::easy::Easy; + +fn main() { + let mut easy = Easy::new(); + let mut data = Vec::new(); + easy.url("https://api.apidash.dev/io/img").unwrap(); + easy.post(true).unwrap(); + + let mut form = curl::easy::Form::new(); + + form.part("token") + .contents(b"xyz") + .add().unwrap(); + + form.part("imfile") + .file("/Documents/up/1.png") + .add().unwrap(); + + easy.httppost(form).unwrap(); + { + let mut transfer = easy.transfer(); + transfer.write_function(|new_data| { + data.extend_from_slice(new_data); + Ok(new_data.len()) + }).unwrap(); + transfer.perform().unwrap(); + } + + let response_body = String::from_utf8_lossy(&data); + + println!("Response body: {}", response_body); + println!("Response code: {}", easy.response_code().unwrap()); +}"""; + expect( + codeGen.getCode( + CodegenLanguage.rustCurl, requestModelPost7, "https"), + expectedCode); + }); + test('POST8', () { + const expectedCode = r""" +use curl::easy::Easy; + +fn main() { + let mut easy = Easy::new(); + let mut data = Vec::new(); + easy.url("https://api.apidash.dev/io/form?size=2&len=3").unwrap(); + easy.post(true).unwrap(); + + let mut form = curl::easy::Form::new(); + + form.part("text") + .contents(b"API") + .add().unwrap(); + + form.part("sep") + .contents(b"|") + .add().unwrap(); + + form.part("times") + .contents(b"3") + .add().unwrap(); + + easy.httppost(form).unwrap(); + { + let mut transfer = easy.transfer(); + transfer.write_function(|new_data| { + data.extend_from_slice(new_data); + Ok(new_data.len()) + }).unwrap(); + transfer.perform().unwrap(); + } + + let response_body = String::from_utf8_lossy(&data); + + println!("Response body: {}", response_body); + println!("Response code: {}", easy.response_code().unwrap()); +}"""; + expect( + codeGen.getCode( + CodegenLanguage.rustCurl, requestModelPost8, "https"), + expectedCode); + }); + test('POST9', () { + const expectedCode = r""" +use curl::easy::Easy; +use curl::easy::List; + +fn main() { + let mut easy = Easy::new(); + let mut data = Vec::new(); + easy.url("https://api.apidash.dev/io/img?size=2&len=3").unwrap(); + easy.post(true).unwrap(); + + let mut form = curl::easy::Form::new(); + + form.part("token") + .contents(b"xyz") + .add().unwrap(); + + form.part("imfile") + .file("/Documents/up/1.png") + .add().unwrap(); + + easy.httppost(form).unwrap(); + let mut list = List::new(); + list.append("User-Agent: Test Agent").unwrap(); + list.append("Keep-Alive: true").unwrap(); + easy.http_headers(list).unwrap(); + + { + let mut transfer = easy.transfer(); + transfer.write_function(|new_data| { + data.extend_from_slice(new_data); + Ok(new_data.len()) + }).unwrap(); + transfer.perform().unwrap(); + } + + let response_body = String::from_utf8_lossy(&data); + + println!("Response body: {}", response_body); + println!("Response code: {}", easy.response_code().unwrap()); +}"""; + expect( + codeGen.getCode( + CodegenLanguage.rustCurl, requestModelPost9, "https"), + expectedCode); + }); + }); + + group('PUT Request', () { + test('PUT1', () { + const expectedCode = r""" +use curl::easy::Easy; +use serde_json::json; +use curl::easy::List; + +fn main() { + let mut easy = Easy::new(); + let mut data = Vec::new(); + easy.url("https://reqres.in/api/users/2").unwrap(); + easy.put(true).unwrap(); + + easy.post_fields_copy(json!({ +"name": "morpheus", +"job": "zion resident" +}).to_string().as_bytes()).unwrap(); + + let mut list = List::new(); + list.append("Content-Type: application/json").unwrap(); + easy.http_headers(list).unwrap(); + + { + let mut transfer = easy.transfer(); + transfer.write_function(|new_data| { + data.extend_from_slice(new_data); + Ok(new_data.len()) + }).unwrap(); + transfer.perform().unwrap(); + } + + let response_body = String::from_utf8_lossy(&data); + + println!("Response body: {}", response_body); + println!("Response code: {}", easy.response_code().unwrap()); +}"""; + expect( + codeGen.getCode( + CodegenLanguage.rustCurl, requestModelPut1, "https"), + expectedCode); + }); + }); + + group('PATCH Request', () { + test('PATCH1', () { + const expectedCode = r""" +use curl::easy::Easy; +use serde_json::json; +use curl::easy::List; + +fn main() { + let mut easy = Easy::new(); + let mut data = Vec::new(); + easy.url("https://reqres.in/api/users/2").unwrap(); + easy.custom_request("PATCH").unwrap(); + + easy.post_fields_copy(json!({ +"name": "marfeus", +"job": "accountant" +}).to_string().as_bytes()).unwrap(); + + let mut list = List::new(); + list.append("Content-Type: application/json").unwrap(); + easy.http_headers(list).unwrap(); + + { + let mut transfer = easy.transfer(); + transfer.write_function(|new_data| { + data.extend_from_slice(new_data); + Ok(new_data.len()) + }).unwrap(); + transfer.perform().unwrap(); + } + + let response_body = String::from_utf8_lossy(&data); + + println!("Response body: {}", response_body); + println!("Response code: {}", easy.response_code().unwrap()); +}"""; + expect( + codeGen.getCode( + CodegenLanguage.rustCurl, requestModelPatch1, "https"), + expectedCode); + }); + }); + + group('DELETE Request', () { + test('DELETE1', () { + const expectedCode = r""" +use curl::easy::Easy; + +fn main() { + let mut easy = Easy::new(); + let mut data = Vec::new(); + easy.url("https://reqres.in/api/users/2").unwrap(); + easy.custom_request("DELETE").unwrap(); + + { + let mut transfer = easy.transfer(); + transfer.write_function(|new_data| { + data.extend_from_slice(new_data); + Ok(new_data.len()) + }).unwrap(); + transfer.perform().unwrap(); + } + + let response_body = String::from_utf8_lossy(&data); + + println!("Response body: {}", response_body); + println!("Response code: {}", easy.response_code().unwrap()); +}"""; + expect( + codeGen.getCode( + CodegenLanguage.rustCurl, requestModelDelete1, "https"), + expectedCode); + }); + test('DELETE2', () { + const expectedCode = r""" +use curl::easy::Easy; +use serde_json::json; +use curl::easy::List; + +fn main() { + let mut easy = Easy::new(); + let mut data = Vec::new(); + easy.url("https://reqres.in/api/users/2").unwrap(); + easy.custom_request("DELETE").unwrap(); + + easy.post_fields_copy(json!({ +"name": "marfeus", +"job": "accountant" +}).to_string().as_bytes()).unwrap(); + + let mut list = List::new(); + list.append("Content-Type: application/json").unwrap(); + easy.http_headers(list).unwrap(); + + { + let mut transfer = easy.transfer(); + transfer.write_function(|new_data| { + data.extend_from_slice(new_data); + Ok(new_data.len()) + }).unwrap(); + transfer.perform().unwrap(); + } + + let response_body = String::from_utf8_lossy(&data); + + println!("Response body: {}", response_body); + println!("Response code: {}", easy.response_code().unwrap()); +}"""; + expect( + codeGen.getCode( + CodegenLanguage.rustCurl, requestModelDelete2, "https"), + expectedCode); + }); + }); + +} diff --git a/test/widgets/overlay_widget_test.dart b/test/widgets/overlay_widget_test.dart new file mode 100644 index 00000000..288c4798 --- /dev/null +++ b/test/widgets/overlay_widget_test.dart @@ -0,0 +1,58 @@ +import 'package:apidash/consts.dart'; +import 'package:apidash/widgets/overlay_widget.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:lottie/lottie.dart'; + +void main() { + testWidgets('OverlayWidgetTemplate Test', (WidgetTester tester) async { + late OverlayWidgetTemplate overlayWidget; + + await tester.pumpWidget( + MaterialApp( + home: Builder( + builder: (BuildContext context) { + overlayWidget = OverlayWidgetTemplate(context: context); + return Container(); // Return any widget here, as OverlayWidgetTemplate doesn't return a widget + }, + ), + ), + ); + + overlayWidget.show(widget: const Text('Test')); + await tester.pump(); + expect(find.text('Test'), findsOneWidget); + + overlayWidget.hide(); + await tester.pump(); + expect(find.text('Test'), findsNothing); + }); + + testWidgets('SavingOverlay Test', (WidgetTester tester) async { + await tester.pumpWidget( + const MaterialApp( + home: Scaffold( + body: SavingOverlay( + saveCompleted: false, + ), + ), + ), + ); + expect(find.byType(Card), findsOneWidget); + expect(find.byType(Lottie), findsOneWidget); + expect(find.text(kLabelSaving), findsOneWidget); + + await tester.pumpWidget( + const MaterialApp( + home: Scaffold( + body: SavingOverlay( + saveCompleted: true, + ), + ), + ), + ); + expect(find.byType(Card), findsOneWidget); + expect(find.byType(Lottie), findsOneWidget); + expect(find.text(kLabelSaved), findsOneWidget); + }); +}