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
```
#### 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?
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 kMinInitialWindowWidth = 1200.0;
const kMinInitialWindowHeight = 800.0;
const kMinRequestEditorDetailsCardPaneSize = 300.0;
const kColorSchemeSeed = Colors.blue;
final kFontFamily = GoogleFonts.openSans().fontFamily;
@ -233,11 +234,13 @@ const kDefaultHttpMethod = HTTPVerb.get;
const kDefaultContentType = ContentType.json;
enum CodegenLanguage {
dartHttp("Dart (http)"),
kotlinOkHttp("Kotlin (OkHttp)");
dartHttp("Dart (http)", "dart", "dart"),
kotlinOkHttp("Kotlin (OkHttp)", "java", "kt");
const CodegenLanguage(this.label);
const CodegenLanguage(this.label, this.codeHighlightLang, this.ext);
final String label;
final String codeHighlightLang;
final String ext;
}
const JsonEncoder kEncoder = JsonEncoder.withIndent(' ');

View File

@ -2,3 +2,4 @@ export 'ui_utils.dart';
export 'convert_utils.dart';
export 'http_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,
this.content,
this.mimeType,
this.ext,
this.name,
this.showLabel = true,
});
final Uint8List? content;
final String? mimeType;
final String? ext;
final String? name;
final bool showLabel;
@ -129,10 +131,9 @@ class _SaveInDownloadsButtonState extends State<SaveInDownloadsButton> {
onPressed: (widget.content != null)
? () async {
var message = "";
var ext = getFileExtension(widget.mimeType);
var path = await getFileDownloadpath(
widget.name,
ext,
widget.ext ?? getFileExtension(widget.mimeType),
);
if (path != null) {
try {

View File

@ -135,48 +135,55 @@ class _ViewCodePaneState extends State<ViewCodePane> {
borderRadius: kBorderRadius8,
);
return Padding(
padding: kP10,
child: Column(
children: [
SizedBox(
height: kHeaderHeight,
child: Row(
children: [
Expanded(
child: Text(
"Code",
style: Theme.of(context).textTheme.titleMedium,
return LayoutBuilder(
builder: (BuildContext context, BoxConstraints constraints) {
var showLabel = showButtonLabelsInViewCodePane(
constraints.maxWidth,
);
return Padding(
padding: kP10,
child: Column(
children: [
SizedBox(
height: kHeaderHeight,
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(
builder: (BuildContext context, BoxConstraints constraints) {
var showLabel = false;
switch (widget.options.length) {
case 1:
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;
}
var showLabel = showButtonLabelsInBodySuccess(
widget.options.length,
constraints.maxWidth,
);
return Padding(
padding: kP10,
child: Column(

View File

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

View File

@ -1,19 +1,13 @@
import 'package:apidash/codegen/kotlin/pkg_okhttp.dart';
import 'package:apidash/models/models.dart' show KVRow, RequestModel;
import 'package:test/test.dart';
import 'package:apidash/consts.dart';
import 'request_models.dart';
void main() {
group('KotlinOkHttpCodeGen', () {
final kotlinOkHttpCodeGen = KotlinOkHttpCodeGen();
test('getCode returns valid code for GET request', () {
const requestModel = RequestModel(
url: 'https://jsonplaceholder.typicode.com/todos/1',
method: HTTPVerb.get,
id: '',
);
const expectedCode = """import okhttp3.MediaType.Companion.toMediaType
const expectedCode = r"""import okhttp3.MediaType.Companion.toMediaType
import okhttp3.MultipartBody
import okhttp3.OkHttpClient
import okhttp3.Request
@ -24,24 +18,17 @@ import java.util.concurrent.TimeUnit
val client = OkHttpClient()
val request = Request.Builder()
.url("https://jsonplaceholder.typicode.com/todos/1")
.url("https://api.foss42.com")
.build()
val response = client.newCall(request).execute()
println(response.body!!.string())
""";
expect(kotlinOkHttpCodeGen.getCode(requestModel), expectedCode);
expect(kotlinOkHttpCodeGen.getCode(requestModelGet1), expectedCode);
});
test('getCode returns valid code for POST request', () {
const requestModel = RequestModel(
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
const expectedCode = r"""import okhttp3.MediaType.Companion.toMediaType
import okhttp3.MultipartBody
import okhttp3.OkHttpClient
import okhttp3.Request
@ -52,27 +39,20 @@ import java.util.concurrent.TimeUnit
val client = OkHttpClient()
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()
.url("https://jsonplaceholder.typicode.com/posts")
.url("https://api.foss42.com/case/lower")
.post(body)
.build()
val response = client.newCall(request).execute()
println(response.body!!.string())
""";
expect(kotlinOkHttpCodeGen.getCode(requestModel), expectedCode);
expect(kotlinOkHttpCodeGen.getCode(requestModelPost1), expectedCode);
});
test('getCode returns valid code for DELETE request', () {
const requestModel = RequestModel(
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
const expectedCode = r"""import okhttp3.MediaType.Companion.toMediaType
import okhttp3.MultipartBody
import okhttp3.OkHttpClient
import okhttp3.Request
@ -83,7 +63,7 @@ import java.util.concurrent.TimeUnit
val client = OkHttpClient()
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()
.url("https://jsonplaceholder.typicode.com/posts/1")
.method("DELETE", body)
@ -92,15 +72,10 @@ val response = client.newCall(request).execute()
println(response.body!!.string())
""";
expect(kotlinOkHttpCodeGen.getCode(requestModel), expectedCode);
expect(kotlinOkHttpCodeGen.getCode(requestModelDelete1), expectedCode);
});
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
import okhttp3.MultipartBody
import okhttp3.OkHttpClient
@ -119,24 +94,12 @@ val response = client.newCall(request).execute()
println(response.body!!.string())
""";
expect(kotlinOkHttpCodeGen.getCode(requestModel), expectedCode);
expect(kotlinOkHttpCodeGen.getCode(requestModelHead1), expectedCode);
});
test(
'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
import okhttp3.MultipartBody
import okhttp3.OkHttpClient
@ -157,7 +120,7 @@ val response = client.newCall(request).execute()
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();
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),
findsOneWidget);
@ -97,7 +108,7 @@ void main() async {
);
await tester.pumpAndSettle();
expect(find.text('Code'), findsOneWidget);
expect(find.text('Dart (http)'), findsOneWidget);
expect(find.textContaining('Error Status Code', findRichText: true),
findsOneWidget);