Merge branch 'foss42:main' into mobileDashboardFeature

This commit is contained in:
Kirollos Nayrouz
2023-07-23 16:02:31 +03:00
committed by GitHub
14 changed files with 250 additions and 109 deletions

View File

@ -151,6 +151,20 @@ To view the coverage report in the browser for further analysis, execute:
open coverage/html/index.html open coverage/html/index.html
``` ```
#### Testing a single file
To run tests specified in a single file, execute the following command:
```
flutter test --enable-experiment=records <file_path>.dart
```
Example:
```
flutter test --enable-experiment=records test/widgets/codegen_previewer_test.dart
```
### How to add a new package to pubspec.yaml? ### How to add a new package to pubspec.yaml?
Instead of copy pasting from pub.dev, it is recommended that you use `flutter pub add package_name` to add a new package to `pubspec.yaml`. You can read more [here](https://docs.flutter.dev/packages-and-plugins/using-packages#adding-a-package-dependency-to-an-app-using-flutter-pub-add). Instead of copy pasting from pub.dev, it is recommended that you use `flutter pub add package_name` to add a new package to `pubspec.yaml`. You can read more [here](https://docs.flutter.dev/packages-and-plugins/using-packages#adding-a-package-dependency-to-an-app-using-flutter-pub-add).

View File

@ -32,6 +32,7 @@ const kWindowTitle = "API Dash";
const kMinWindowSize = Size(900, 600); const kMinWindowSize = Size(900, 600);
const kMinInitialWindowWidth = 1200.0; const kMinInitialWindowWidth = 1200.0;
const kMinInitialWindowHeight = 800.0; const kMinInitialWindowHeight = 800.0;
const kMinRequestEditorDetailsCardPaneSize = 300.0;
const kColorSchemeSeed = Colors.blue; const kColorSchemeSeed = Colors.blue;
final kFontFamily = GoogleFonts.openSans().fontFamily; final kFontFamily = GoogleFonts.openSans().fontFamily;
@ -233,11 +234,13 @@ const kDefaultHttpMethod = HTTPVerb.get;
const kDefaultContentType = ContentType.json; const kDefaultContentType = ContentType.json;
enum CodegenLanguage { enum CodegenLanguage {
dartHttp("Dart (http)"), dartHttp("Dart (http)", "dart", "dart"),
kotlinOkHttp("Kotlin (OkHttp)"); kotlinOkHttp("Kotlin (OkHttp)", "java", "kt");
const CodegenLanguage(this.label); const CodegenLanguage(this.label, this.codeHighlightLang, this.ext);
final String label; final String label;
final String codeHighlightLang;
final String ext;
} }
const JsonEncoder kEncoder = JsonEncoder.withIndent(' '); const JsonEncoder kEncoder = JsonEncoder.withIndent(' ');

View File

@ -2,3 +2,4 @@ export 'ui_utils.dart';
export 'convert_utils.dart'; export 'convert_utils.dart';
export 'http_utils.dart'; export 'http_utils.dart';
export 'file_utils.dart'; export 'file_utils.dart';
export 'window_utils.dart';

View File

@ -0,0 +1,18 @@
bool showButtonLabelsInBodySuccess(int options, double maxWidth) {
switch (options) {
case 0:
return true;
case 1:
return (maxWidth < 300) ? false : true;
case 2:
return (maxWidth < 400) ? false : true;
case 3:
return (maxWidth < 500) ? false : true;
default:
return false;
}
}
bool showButtonLabelsInViewCodePane(double maxWidth) {
return (maxWidth < 400) ? false : true;
}

View File

@ -104,12 +104,14 @@ class SaveInDownloadsButton extends StatefulWidget {
super.key, super.key,
this.content, this.content,
this.mimeType, this.mimeType,
this.ext,
this.name, this.name,
this.showLabel = true, this.showLabel = true,
}); });
final Uint8List? content; final Uint8List? content;
final String? mimeType; final String? mimeType;
final String? ext;
final String? name; final String? name;
final bool showLabel; final bool showLabel;
@ -129,10 +131,9 @@ class _SaveInDownloadsButtonState extends State<SaveInDownloadsButton> {
onPressed: (widget.content != null) onPressed: (widget.content != null)
? () async { ? () async {
var message = ""; var message = "";
var ext = getFileExtension(widget.mimeType);
var path = await getFileDownloadpath( var path = await getFileDownloadpath(
widget.name, widget.name,
ext, widget.ext ?? getFileExtension(widget.mimeType),
); );
if (path != null) { if (path != null) {
try { try {

View File

@ -135,48 +135,55 @@ class _ViewCodePaneState extends State<ViewCodePane> {
borderRadius: kBorderRadius8, borderRadius: kBorderRadius8,
); );
return Padding( return LayoutBuilder(
padding: kP10, builder: (BuildContext context, BoxConstraints constraints) {
child: Column( var showLabel = showButtonLabelsInViewCodePane(
children: [ constraints.maxWidth,
SizedBox( );
height: kHeaderHeight, return Padding(
child: Row( padding: kP10,
children: [ child: Column(
Expanded( children: [
child: Text( SizedBox(
"Code", height: kHeaderHeight,
style: Theme.of(context).textTheme.titleMedium, child: Row(
children: [
Expanded(
child: DropdownButtonCodegenLanguage(
codegenLanguage: widget.codegenLanguage,
onChanged: widget.onChangedCodegenLanguage,
),
),
CopyButton(
toCopy: widget.code,
showLabel: showLabel,
),
SaveInDownloadsButton(
content: stringToBytes(widget.code),
ext: widget.codegenLanguage.ext,
showLabel: showLabel,
)
],
),
),
kVSpacer10,
Expanded(
child: Container(
width: double.maxFinite,
padding: kP8,
decoration: textContainerdecoration,
child: CodeGenPreviewer(
code: widget.code,
theme: codeTheme,
language: widget.codegenLanguage.codeHighlightLang,
textStyle: kCodeStyle,
), ),
), ),
DropdownButtonCodegenLanguage(
codegenLanguage: widget.codegenLanguage,
onChanged: widget.onChangedCodegenLanguage,
),
CopyButton(toCopy: widget.code),
SaveInDownloadsButton(
content: stringToBytes(widget.code),
mimeType: "application/vnd.dart",
)
],
),
),
kVSpacer10,
Expanded(
child: Container(
width: double.maxFinite,
padding: kP8,
decoration: textContainerdecoration,
child: CodeGenPreviewer(
code: widget.code,
theme: codeTheme,
language: 'dart',
textStyle: kCodeStyle,
), ),
), ],
), ),
], );
), },
); );
} }
} }

View File

@ -400,18 +400,10 @@ class _BodySuccessState extends State<BodySuccess> {
return LayoutBuilder( return LayoutBuilder(
builder: (BuildContext context, BoxConstraints constraints) { builder: (BuildContext context, BoxConstraints constraints) {
var showLabel = false; var showLabel = showButtonLabelsInBodySuccess(
switch (widget.options.length) { widget.options.length,
case 1: constraints.maxWidth,
showLabel = (constraints.maxWidth < 300) ? false : true; );
break;
case 2:
showLabel = (constraints.maxWidth < 400) ? false : true;
break;
case 3:
showLabel = (constraints.maxWidth < 500) ? false : true;
break;
}
return Padding( return Padding(
padding: kP10, padding: kP10,
child: Column( child: Column(

View File

@ -76,8 +76,8 @@ class EqualSplitView extends StatefulWidget {
class _EqualSplitViewState extends State<EqualSplitView> { class _EqualSplitViewState extends State<EqualSplitView> {
final MultiSplitViewController _controller = MultiSplitViewController( final MultiSplitViewController _controller = MultiSplitViewController(
areas: [ areas: [
Area(minimalSize: 300), Area(minimalSize: kMinRequestEditorDetailsCardPaneSize),
Area(minimalSize: 300), Area(minimalSize: kMinRequestEditorDetailsCardPaneSize),
], ],
); );

View File

@ -1,19 +1,13 @@
import 'package:apidash/codegen/kotlin/pkg_okhttp.dart'; import 'package:apidash/codegen/kotlin/pkg_okhttp.dart';
import 'package:apidash/models/models.dart' show KVRow, RequestModel;
import 'package:test/test.dart'; import 'package:test/test.dart';
import 'package:apidash/consts.dart'; import 'request_models.dart';
void main() { void main() {
group('KotlinOkHttpCodeGen', () { group('KotlinOkHttpCodeGen', () {
final kotlinOkHttpCodeGen = KotlinOkHttpCodeGen(); final kotlinOkHttpCodeGen = KotlinOkHttpCodeGen();
test('getCode returns valid code for GET request', () { test('getCode returns valid code for GET request', () {
const requestModel = RequestModel( const expectedCode = r"""import okhttp3.MediaType.Companion.toMediaType
url: 'https://jsonplaceholder.typicode.com/todos/1',
method: HTTPVerb.get,
id: '',
);
const expectedCode = """import okhttp3.MediaType.Companion.toMediaType
import okhttp3.MultipartBody import okhttp3.MultipartBody
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import okhttp3.Request import okhttp3.Request
@ -24,24 +18,17 @@ import java.util.concurrent.TimeUnit
val client = OkHttpClient() val client = OkHttpClient()
val request = Request.Builder() val request = Request.Builder()
.url("https://jsonplaceholder.typicode.com/todos/1") .url("https://api.foss42.com")
.build() .build()
val response = client.newCall(request).execute() val response = client.newCall(request).execute()
println(response.body!!.string()) println(response.body!!.string())
"""; """;
expect(kotlinOkHttpCodeGen.getCode(requestModel), expectedCode); expect(kotlinOkHttpCodeGen.getCode(requestModelGet1), expectedCode);
}); });
test('getCode returns valid code for POST request', () { test('getCode returns valid code for POST request', () {
const requestModel = RequestModel( const expectedCode = r"""import okhttp3.MediaType.Companion.toMediaType
url: 'https://jsonplaceholder.typicode.com/posts',
method: HTTPVerb.post,
requestBody: '{"title": "foo","body": "bar","userId": 1}',
requestBodyContentType: ContentType.json,
id: '1',
);
const expectedCode = """import okhttp3.MediaType.Companion.toMediaType
import okhttp3.MultipartBody import okhttp3.MultipartBody
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import okhttp3.Request import okhttp3.Request
@ -52,27 +39,20 @@ import java.util.concurrent.TimeUnit
val client = OkHttpClient() val client = OkHttpClient()
val mediaType = "application/json".toMediaType() val mediaType = "application/json".toMediaType()
val body = "{\"title\": \"foo\",\"body\": \"bar\",\"userId\": 1}".toRequestBody(mediaType) val body = "{"text": "IS Upper"}".toRequestBody(mediaType)
val request = Request.Builder() val request = Request.Builder()
.url("https://jsonplaceholder.typicode.com/posts") .url("https://api.foss42.com/case/lower")
.post(body) .post(body)
.build() .build()
val response = client.newCall(request).execute() val response = client.newCall(request).execute()
println(response.body!!.string()) println(response.body!!.string())
"""; """;
expect(kotlinOkHttpCodeGen.getCode(requestModel), expectedCode); expect(kotlinOkHttpCodeGen.getCode(requestModelPost1), expectedCode);
}); });
test('getCode returns valid code for DELETE request', () { test('getCode returns valid code for DELETE request', () {
const requestModel = RequestModel( const expectedCode = r"""import okhttp3.MediaType.Companion.toMediaType
url: 'https://jsonplaceholder.typicode.com/posts/1',
method: HTTPVerb.delete,
requestBody: '{"title": "foo","body": "bar","userId": 1}',
requestBodyContentType: ContentType.json,
id: '1',
);
const expectedCode = """import okhttp3.MediaType.Companion.toMediaType
import okhttp3.MultipartBody import okhttp3.MultipartBody
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import okhttp3.Request import okhttp3.Request
@ -83,7 +63,7 @@ import java.util.concurrent.TimeUnit
val client = OkHttpClient() val client = OkHttpClient()
val mediaType = "application/json".toMediaType() val mediaType = "application/json".toMediaType()
val body = "{\"title\": \"foo\",\"body\": \"bar\",\"userId\": 1}".toRequestBody(mediaType) val body = "{"title": "foo","body": "bar","userId": 1}".toRequestBody(mediaType)
val request = Request.Builder() val request = Request.Builder()
.url("https://jsonplaceholder.typicode.com/posts/1") .url("https://jsonplaceholder.typicode.com/posts/1")
.method("DELETE", body) .method("DELETE", body)
@ -92,15 +72,10 @@ val response = client.newCall(request).execute()
println(response.body!!.string()) println(response.body!!.string())
"""; """;
expect(kotlinOkHttpCodeGen.getCode(requestModel), expectedCode); expect(kotlinOkHttpCodeGen.getCode(requestModelDelete1), expectedCode);
}); });
test('getCode returns valid code for HEAD request', () { test('getCode returns valid code for HEAD request', () {
const requestModel = RequestModel(
url: 'https://jsonplaceholder.typicode.com/posts/1',
method: HTTPVerb.head,
id: '1',
);
const expectedCode = """import okhttp3.MediaType.Companion.toMediaType const expectedCode = """import okhttp3.MediaType.Companion.toMediaType
import okhttp3.MultipartBody import okhttp3.MultipartBody
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
@ -119,24 +94,12 @@ val response = client.newCall(request).execute()
println(response.body!!.string()) println(response.body!!.string())
"""; """;
expect(kotlinOkHttpCodeGen.getCode(requestModel), expectedCode); expect(kotlinOkHttpCodeGen.getCode(requestModelHead1), expectedCode);
}); });
test( test(
'getCode returns valid code for requests with headers and query parameters', 'getCode returns valid code for requests with headers and query parameters',
() { () {
const requestModel = RequestModel(
url: 'https://jsonplaceholder.typicode.com/posts',
method: HTTPVerb.get,
requestParams: [
KVRow('userId', 1),
],
requestHeaders: [
KVRow('Custom-Header-1', 'Value-1'),
KVRow('Custom-Header-2', 'Value-2')
],
id: '1',
);
const expectedCode = """import okhttp3.MediaType.Companion.toMediaType const expectedCode = """import okhttp3.MediaType.Companion.toMediaType
import okhttp3.MultipartBody import okhttp3.MultipartBody
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
@ -157,7 +120,7 @@ val response = client.newCall(request).execute()
println(response.body!!.string()) println(response.body!!.string())
"""; """;
expect(kotlinOkHttpCodeGen.getCode(requestModel), expectedCode); expect(kotlinOkHttpCodeGen.getCode(requestModelGet2), expectedCode);
}); });
}); });
} }

View File

@ -0,0 +1,48 @@
import 'package:apidash/models/models.dart' show KVRow, RequestModel;
import 'package:apidash/consts.dart';
/// Basic GET request model
const requestModelGet1 = RequestModel(
url: 'https://api.foss42.com',
method: HTTPVerb.get,
id: '',
);
/// GET request model with headers and query params
const requestModelGet2 = RequestModel(
url: 'https://jsonplaceholder.typicode.com/posts',
method: HTTPVerb.get,
requestParams: [
KVRow('userId', 1),
],
requestHeaders: [
KVRow('Custom-Header-1', 'Value-1'),
KVRow('Custom-Header-2', 'Value-2')
],
id: '1',
);
/// Basic HEAD request model
const requestModelHead1 = RequestModel(
url: 'https://jsonplaceholder.typicode.com/posts/1',
method: HTTPVerb.head,
id: '1',
);
/// Basic POST request model
const requestModelPost1 = RequestModel(
url: 'https://api.foss42.com/case/lower',
method: HTTPVerb.post,
requestBody: '{"text": "IS Upper"}',
requestBodyContentType: ContentType.json,
id: '1',
);
/// Basic DELETE request model
const requestModelDelete1 = RequestModel(
url: 'https://jsonplaceholder.typicode.com/posts/1',
method: HTTPVerb.delete,
requestBody: '{"title": "foo","body": "bar","userId": 1}',
requestBodyContentType: ContentType.json,
id: '1',
);

View File

@ -0,0 +1,17 @@
import 'package:test/test.dart';
import 'package:apidash/utils/file_utils.dart';
void main() {
group(
"Testing x function",
() {
/*test('Test case 2', () {
expect(showButtonLabelsInViewCodePane(350), false);
});
test('Test case 3', () {
expect(showButtonLabelsInViewCodePane(450), true);
});*/
},
);
}

View File

@ -0,0 +1,62 @@
import 'package:test/test.dart';
import 'package:apidash/utils/window_utils.dart';
void main() {
group(
"Testing showButtonLabelsInBodySuccess function",
() {
test('Test case 1 options 0', () {
expect(showButtonLabelsInBodySuccess(0, 300), true);
});
test('Test case 2 options 0', () {
expect(showButtonLabelsInBodySuccess(0, 500), true);
});
test('Test case 1 options 1', () {
expect(showButtonLabelsInBodySuccess(1, 250), false);
});
test('Test case 2 options 1', () {
expect(showButtonLabelsInBodySuccess(1, 350), true);
});
test('Test case 1 options 2', () {
expect(showButtonLabelsInBodySuccess(2, 250), false);
});
test('Test case 2 options 2', () {
expect(showButtonLabelsInBodySuccess(2, 350), false);
});
test('Test case 3 options 2', () {
expect(showButtonLabelsInBodySuccess(2, 450), true);
});
test('Test case 1 options 3', () {
expect(showButtonLabelsInBodySuccess(3, 350), false);
});
test('Test case 2 options 3', () {
expect(showButtonLabelsInBodySuccess(3, 450), false);
});
test('Test case 3 options 3', () {
expect(showButtonLabelsInBodySuccess(3, 550), true);
});
},
);
group(
"Testing showButtonLabelsInViewCodePane function",
() {
test('Test case 2', () {
expect(showButtonLabelsInViewCodePane(350), false);
});
test('Test case 3', () {
expect(showButtonLabelsInViewCodePane(450), true);
});
},
);
}

4
test/widget_test.dart Normal file
View File

@ -0,0 +1,4 @@
import 'package:apidash/main.dart';
import 'package:flutter_test/flutter_test.dart';
void main() {}

View File

@ -69,7 +69,18 @@ void main() async {
); );
await tester.pumpAndSettle(); await tester.pumpAndSettle();
expect(find.text('Code'), findsOneWidget); expect(find.byType(DropdownButton<CodegenLanguage>), findsOneWidget);
expect(
(tester.widget(find.byType(DropdownButton<CodegenLanguage>))
as DropdownButton)
.value,
equals(CodegenLanguage.dartHttp));
await tester.tap(find.text('Dart (http)'));
await tester.pump();
await tester.pump(const Duration(seconds: 1));
expect(find.text('Kotlin (OkHttp)'), findsWidgets);
expect(find.textContaining('Error Status Code', findRichText: true), expect(find.textContaining('Error Status Code', findRichText: true),
findsOneWidget); findsOneWidget);
@ -97,7 +108,7 @@ void main() async {
); );
await tester.pumpAndSettle(); await tester.pumpAndSettle();
expect(find.text('Code'), findsOneWidget); expect(find.text('Dart (http)'), findsOneWidget);
expect(find.textContaining('Error Status Code', findRichText: true), expect(find.textContaining('Error Status Code', findRichText: true),
findsOneWidget); findsOneWidget);