mirror of
https://github.com/foss42/apidash.git
synced 2025-05-29 12:59:58 +08:00
Merge branch 'foss42:main' into mobileDashboardFeature
This commit is contained in:
@ -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).
|
||||
|
@ -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(' ');
|
||||
|
@ -2,3 +2,4 @@ export 'ui_utils.dart';
|
||||
export 'convert_utils.dart';
|
||||
export 'http_utils.dart';
|
||||
export 'file_utils.dart';
|
||||
export 'window_utils.dart';
|
||||
|
18
lib/utils/window_utils.dart
Normal file
18
lib/utils/window_utils.dart
Normal 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;
|
||||
}
|
@ -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 {
|
||||
|
@ -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,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -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(
|
||||
|
@ -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),
|
||||
],
|
||||
);
|
||||
|
||||
|
@ -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);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
48
test/codegen/request_models.dart
Normal file
48
test/codegen/request_models.dart
Normal 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',
|
||||
);
|
17
test/utils/file_utils_test.dart
Normal file
17
test/utils/file_utils_test.dart
Normal 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);
|
||||
});*/
|
||||
},
|
||||
);
|
||||
}
|
62
test/utils/window_utils_test.dart
Normal file
62
test/utils/window_utils_test.dart
Normal 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
4
test/widget_test.dart
Normal file
@ -0,0 +1,4 @@
|
||||
import 'package:apidash/main.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
void main() {}
|
@ -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);
|
||||
|
Reference in New Issue
Block a user