Merge branch 'add-ui-tests' of https://github.com/sixtusagbo/apidash into add-ui-tests

This commit is contained in:
Sixtus Agbo
2024-03-29 00:39:25 +01:00
15 changed files with 363 additions and 23 deletions

View File

@ -157,7 +157,16 @@ Here is the complete list of mimetypes that can be directly previewed in API Das
| File Type | Mimetype | Extension | Comment | | File Type | Mimetype | Extension | Comment |
| --------- | -------------------------- | ----------------- | -------- | | --------- | -------------------------- | ----------------- | -------- |
| PDF | `application/pdf` | `.pdf` | | | PDF | `application/pdf` | `.pdf` | |
| CSV | `text/csv` | `.csv` | Can be improved | | Video | `video/mp4` | `.mp4` | |
| Video | `video/webm` | `.webm` | |
| Video | `video/x-ms-wmv` | `.wmv` | |
| Video | `video/x-ms-asf` | `.wmv` | |
| Video | `video/avi` | `.avi` | |
| Video | `video/msvideo` | `.avi` | |
| Video | `video/x-msvideo` | `.avi` | |
| Video | `video/quicktime` | `.mov` | |
| Video | `video/x-quicktime` | `.mov` | |
| Video | `video/x-matroska` | `.mkv` | |
| Image | `image/apng` | `.apng` | Animated | | Image | `image/apng` | `.apng` | Animated |
| Image | `image/avif` | `.avif` | | | Image | `image/avif` | `.avif` | |
| Image | `image/bmp` | `.bmp` | | | Image | `image/bmp` | `.bmp` | |
@ -188,6 +197,7 @@ Here is the complete list of mimetypes that can be directly previewed in API Das
| Audio | `audio/x-m4a` | `.m4a` | | | Audio | `audio/x-m4a` | `.m4a` | |
| Audio | `audio/wav` | `.wav` | | | Audio | `audio/wav` | `.wav` | |
| Audio | `audio/wave` | `.wav` | | | Audio | `audio/wave` | `.wav` | |
| CSV | `text/csv` | `.csv` | Can be improved |
We welcome PRs to add support for previewing other multimedia mimetypes. Please go ahead and raise an issue so that we can discuss the approach. We welcome PRs to add support for previewing other multimedia mimetypes. Please go ahead and raise an issue so that we can discuss the approach.
We are adding support for other mimetypes with each release. But, if you are looking for any particular mimetype support, please go ahead and open an issue. We will prioritize it's addition. We are adding support for other mimetypes with each release. But, if you are looking for any particular mimetype support, please go ahead and open an issue. We will prioritize it's addition.

View File

@ -57,7 +57,7 @@ class _AppState extends ConsumerState<App> with WindowListener {
bool isPreventClose = await windowManager.isPreventClose(); bool isPreventClose = await windowManager.isPreventClose();
if (isPreventClose) { if (isPreventClose) {
if (ref.watch( if (ref.watch(
settingsProvider.select((value) => value.promptBeforeClosing))) { settingsProvider.select((value) => value.promptBeforeClosing)) && ref.watch(hasUnsavedChangesProvider)) {
showDialog( showDialog(
context: context, context: context,
builder: (_) => AlertDialog( builder: (_) => AlertDialog(

View File

@ -21,7 +21,7 @@ final kIsLinux = !kIsWeb && Platform.isLinux;
final kIsApple = !kIsWeb && (Platform.isIOS || Platform.isMacOS); final kIsApple = !kIsWeb && (Platform.isIOS || Platform.isMacOS);
final kIsDesktop = final kIsDesktop =
!kIsWeb && (Platform.isMacOS || Platform.isWindows || Platform.isLinux); !kIsWeb && (Platform.isMacOS || Platform.isWindows || Platform.isLinux);
final kIsRunningTests = Platform.environment.containsKey('FLUTTER_TEST');
final kIsIOS = !kIsWeb && Platform.isIOS; final kIsIOS = !kIsWeb && Platform.isIOS;
final kIsAndroid = !kIsWeb && Platform.isAndroid; final kIsAndroid = !kIsWeb && Platform.isAndroid;
final kIsMobile = !kIsWeb && (Platform.isIOS || Platform.isAndroid); final kIsMobile = !kIsWeb && (Platform.isIOS || Platform.isAndroid);
@ -403,7 +403,7 @@ const Map<String, Map<String, List<ResponseBodyView>>>
kSubTypeDefaultViewOptions: kPreviewBodyViewOptions, kSubTypeDefaultViewOptions: kPreviewBodyViewOptions,
}, },
kTypeVideo: { kTypeVideo: {
kSubTypeDefaultViewOptions: kNoBodyViewOptions, kSubTypeDefaultViewOptions: kPreviewBodyViewOptions,
}, },
kTypeText: { kTypeText: {
kSubTypeDefaultViewOptions: kRawBodyViewOptions, kSubTypeDefaultViewOptions: kRawBodyViewOptions,
@ -512,6 +512,9 @@ const kMimeTypeRaiseIssue =
const kUnexpectedRaiseIssue = const kUnexpectedRaiseIssue =
"\nIf the behaviour is unexpected, please raise an issue in API Dash GitHub repo so that we can resolve it."; "\nIf the behaviour is unexpected, please raise an issue in API Dash GitHub repo so that we can resolve it.";
const kVideoError =
"There seems to be an issue playing this video. Please raise an issue in API Dash GitHub repo so that we can resolve it.";
const kImageError = const kImageError =
"There seems to be an issue rendering this image. Please raise an issue in API Dash GitHub repo so that we can resolve it."; "There seems to be an issue rendering this image. Please raise an issue in API Dash GitHub repo so that we can resolve it.";

View File

@ -31,6 +31,7 @@ class RequestModel {
this.message, this.message,
this.responseModel, this.responseModel,
this.isWorking = false, this.isWorking = false,
this.sendingTime,
}); });
final String id; final String id;
@ -50,6 +51,7 @@ class RequestModel {
final String? message; final String? message;
final ResponseModel? responseModel; final ResponseModel? responseModel;
final bool isWorking; final bool isWorking;
final DateTime? sendingTime;
List<NameValueModel>? get enabledRequestHeaders => List<NameValueModel>? get enabledRequestHeaders =>
getEnabledRows(requestHeaders, isHeaderEnabledList); getEnabledRows(requestHeaders, isHeaderEnabledList);
@ -135,6 +137,7 @@ class RequestModel {
String? message, String? message,
ResponseModel? responseModel, ResponseModel? responseModel,
bool? isWorking, bool? isWorking,
DateTime? sendingTime,
}) { }) {
var headers = requestHeaders ?? this.requestHeaders; var headers = requestHeaders ?? this.requestHeaders;
var params = requestParams ?? this.requestParams; var params = requestParams ?? this.requestParams;
@ -160,6 +163,7 @@ class RequestModel {
message: message ?? this.message, message: message ?? this.message,
responseModel: responseModel ?? this.responseModel, responseModel: responseModel ?? this.responseModel,
isWorking: isWorking ?? this.isWorking, isWorking: isWorking ?? this.isWorking,
sendingTime: sendingTime ?? this.sendingTime,
); );
} }

View File

@ -66,6 +66,7 @@ class CollectionStateNotifier
.read(requestSequenceProvider.notifier) .read(requestSequenceProvider.notifier)
.update((state) => [id, ...state]); .update((state) => [id, ...state]);
ref.read(selectedIdStateProvider.notifier).state = newRequestModel.id; ref.read(selectedIdStateProvider.notifier).state = newRequestModel.id;
ref.read(hasUnsavedChangesProvider.notifier).state = true;
} }
void reorder(int oldIdx, int newIdx) { void reorder(int oldIdx, int newIdx) {
@ -73,6 +74,7 @@ class CollectionStateNotifier
final itemId = itemIds.removeAt(oldIdx); final itemId = itemIds.removeAt(oldIdx);
itemIds.insert(newIdx, itemId); itemIds.insert(newIdx, itemId);
ref.read(requestSequenceProvider.notifier).state = [...itemIds]; ref.read(requestSequenceProvider.notifier).state = [...itemIds];
ref.read(hasUnsavedChangesProvider.notifier).state = true;
} }
void remove(String id) { void remove(String id) {
@ -95,6 +97,7 @@ class CollectionStateNotifier
var map = {...state!}; var map = {...state!};
map.remove(id); map.remove(id);
state = map; state = map;
ref.read(hasUnsavedChangesProvider.notifier).state = true;
} }
void clearResponse(String? id) { void clearResponse(String? id) {
@ -108,6 +111,7 @@ class CollectionStateNotifier
var map = {...state!}; var map = {...state!};
map[id] = newModel; map[id] = newModel;
state = map; state = map;
ref.read(hasUnsavedChangesProvider.notifier).state = true;
} }
void duplicate(String id) { void duplicate(String id) {
@ -127,6 +131,7 @@ class CollectionStateNotifier
ref.read(requestSequenceProvider.notifier).state = [...itemIds]; ref.read(requestSequenceProvider.notifier).state = [...itemIds];
ref.read(selectedIdStateProvider.notifier).state = newId; ref.read(selectedIdStateProvider.notifier).state = newId;
ref.read(hasUnsavedChangesProvider.notifier).state = true;
} }
void update( void update(
@ -168,6 +173,7 @@ class CollectionStateNotifier
var map = {...state!}; var map = {...state!};
map[id] = newModel; map[id] = newModel;
state = map; state = map;
ref.read(hasUnsavedChangesProvider.notifier).state = true;
} }
Future<void> sendRequest(String id) async { Future<void> sendRequest(String id) async {
@ -182,7 +188,10 @@ class CollectionStateNotifier
// set current model's isWorking to true and update state // set current model's isWorking to true and update state
var map = {...state!}; var map = {...state!};
map[id] = requestModel.copyWith(isWorking: true); map[id] = requestModel.copyWith(
isWorking: true,
sendingTime: DateTime.now(),
);
state = map; state = map;
(http.Response?, Duration?, String?)? responseRec = await request( (http.Response?, Duration?, String?)? responseRec = await request(
@ -214,6 +223,7 @@ class CollectionStateNotifier
map = {...state!}; map = {...state!};
map[id] = newRequestModel; map[id] = newRequestModel;
state = map; state = map;
ref.read(hasUnsavedChangesProvider.notifier).state = true;
} }
Future<void> clearData() async { Future<void> clearData() async {
@ -223,6 +233,7 @@ class CollectionStateNotifier
ref.read(clearDataStateProvider.notifier).state = false; ref.read(clearDataStateProvider.notifier).state = false;
ref.read(requestSequenceProvider.notifier).state = []; ref.read(requestSequenceProvider.notifier).state = [];
state = {}; state = {};
ref.read(hasUnsavedChangesProvider.notifier).state = true;
} }
bool loadData() { bool loadData() {
@ -263,6 +274,7 @@ class CollectionStateNotifier
} }
await hiveHandler.removeUnused(); await hiveHandler.removeUnused();
ref.read(saveDataStateProvider.notifier).state = false; ref.read(saveDataStateProvider.notifier).state = false;
ref.read(hasUnsavedChangesProvider.notifier).state = false;
} }
Future<Map<String, dynamic>> exportDataToHAR() async { Future<Map<String, dynamic>> exportDataToHAR() async {

View File

@ -6,6 +6,8 @@ final selectedIdEditStateProvider = StateProvider<String?>((ref) => null);
final codePaneVisibleStateProvider = StateProvider<bool>((ref) => false); final codePaneVisibleStateProvider = StateProvider<bool>((ref) => false);
final saveDataStateProvider = StateProvider<bool>((ref) => false); final saveDataStateProvider = StateProvider<bool>((ref) => false);
final clearDataStateProvider = StateProvider<bool>((ref) => false); final clearDataStateProvider = StateProvider<bool>((ref) => false);
final hasUnsavedChangesProvider = StateProvider<bool>((ref) => false);
// final nameTextFieldControllerProvider = // final nameTextFieldControllerProvider =
// StateProvider.autoDispose<TextEditingController>((ref) { // StateProvider.autoDispose<TextEditingController>((ref) {
// TextEditingController controller = TextEditingController(text: ""); // TextEditingController controller = TextEditingController(text: "");

View File

@ -15,6 +15,7 @@ class CollectionPane extends ConsumerWidget {
final overlayWidget = OverlayWidgetTemplate(context: context); final overlayWidget = OverlayWidgetTemplate(context: context);
final collection = ref.watch(collectionStateNotifierProvider); final collection = ref.watch(collectionStateNotifierProvider);
final savingData = ref.watch(saveDataStateProvider); final savingData = ref.watch(saveDataStateProvider);
final hasUnsavedChanges = ref.watch(hasUnsavedChangesProvider);
if (collection == null) { if (collection == null) {
return const Center( return const Center(
child: CircularProgressIndicator(), child: CircularProgressIndicator(),
@ -31,7 +32,7 @@ class CollectionPane extends ConsumerWidget {
alignment: WrapAlignment.spaceBetween, alignment: WrapAlignment.spaceBetween,
children: [ children: [
TextButton.icon( TextButton.icon(
onPressed: savingData onPressed: (savingData || !hasUnsavedChanges)
? null ? null
: () async { : () async {
overlayWidget.show( overlayWidget.show(

View File

@ -12,12 +12,17 @@ class ResponsePane extends ConsumerWidget {
final isWorking = ref.watch( final isWorking = ref.watch(
selectedRequestModelProvider.select((value) => value?.isWorking)) ?? selectedRequestModelProvider.select((value) => value?.isWorking)) ??
false; false;
final startSendingTime = ref.watch(
selectedRequestModelProvider.select((value) => value?.sendingTime));
final responseStatus = ref.watch( final responseStatus = ref.watch(
selectedRequestModelProvider.select((value) => value?.responseStatus)); selectedRequestModelProvider.select((value) => value?.responseStatus));
final message = ref final message = ref
.watch(selectedRequestModelProvider.select((value) => value?.message)); .watch(selectedRequestModelProvider.select((value) => value?.message));
if (isWorking) { if (isWorking) {
return const SendingWidget(); return SendingWidget(
startSendingTime: startSendingTime,
);
} }
if (responseStatus == null) { if (responseStatus == null) {
return const NotSentWidget(); return const NotSentWidget();

View File

@ -8,6 +8,7 @@ import 'error_message.dart';
import 'uint8_audio_player.dart'; import 'uint8_audio_player.dart';
import 'json_previewer.dart'; import 'json_previewer.dart';
import 'csv_previewer.dart'; import 'csv_previewer.dart';
import 'video_previewer.dart';
import '../consts.dart'; import '../consts.dart';
class Previewer extends StatefulWidget { class Previewer extends StatefulWidget {
@ -86,7 +87,12 @@ class _PreviewerState extends State<Previewer> {
return CsvPreviewer(body: widget.body); return CsvPreviewer(body: widget.body);
} }
if (widget.type == kTypeVideo) { if (widget.type == kTypeVideo) {
// TODO: Video Player try {
var preview = VideoPreviewer(videoBytes: widget.bytes);
return preview;
} catch (e) {
return const ErrorMessage(message: kVideoError);
}
} }
String message = widget.hasRaw String message = widget.hasRaw
? "$kMimeTypeRawRaiseIssueStart${widget.type}/${widget.subtype}$kMimeTypeRaiseIssue" ? "$kMimeTypeRawRaiseIssueStart${widget.type}/${widget.subtype}$kMimeTypeRaiseIssue"

View File

@ -1,3 +1,4 @@
import 'dart:async';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:http_parser/http_parser.dart'; import 'package:http_parser/http_parser.dart';
@ -33,18 +34,80 @@ class NotSentWidget extends StatelessWidget {
} }
} }
class SendingWidget extends StatelessWidget { class SendingWidget extends StatefulWidget {
const SendingWidget({super.key}); final DateTime? startSendingTime;
const SendingWidget({
super.key,
required this.startSendingTime,
});
@override
State<SendingWidget> createState() => _SendingWidgetState();
}
class _SendingWidgetState extends State<SendingWidget> {
int _millisecondsElapsed = 0;
Timer? _timer;
@override
void initState() {
super.initState();
if (widget.startSendingTime != null) {
_millisecondsElapsed =
(DateTime.now().difference(widget.startSendingTime!).inMilliseconds ~/
100) *
100;
_timer = Timer.periodic(const Duration(milliseconds: 100), _updateTimer);
}
}
void _updateTimer(Timer timer) {
setState(() {
_millisecondsElapsed += 100;
});
}
@override
void dispose() {
if (_timer != null && _timer!.isActive) _timer?.cancel();
super.dispose();
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Center( return Stack(
child: Column( children: [
mainAxisAlignment: MainAxisAlignment.center, Center(
children: [ child: Lottie.asset(kAssetSendingLottie),
Lottie.asset(kAssetSendingLottie), ),
], Padding(
), padding: kPh20t40,
child: Visibility(
visible: _millisecondsElapsed >= 0,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(
Icons.alarm,
color: Theme.of(context).colorScheme.onSurfaceVariant,
),
const SizedBox(
width: 10,
),
Text(
'Time elapsed: ${humanizeDuration(Duration(milliseconds: _millisecondsElapsed))}',
textAlign: TextAlign.center,
overflow: TextOverflow.fade,
softWrap: false,
style: kTextStyleButton.copyWith(
color: Theme.of(context).colorScheme.onSurfaceVariant,
),
),
],
),
),
),
],
); );
} }
} }

View File

@ -0,0 +1,147 @@
import 'dart:io';
import 'dart:typed_data';
import 'package:apidash/consts.dart';
import 'package:fvp/fvp.dart' as fvp;
import 'package:flutter/material.dart';
import 'package:flutter/foundation.dart';
import 'package:video_player/video_player.dart';
import 'package:path_provider/path_provider.dart';
class VideoPreviewer extends StatefulWidget {
const VideoPreviewer({
super.key,
required this.videoBytes,
});
final Uint8List videoBytes;
@override
State<VideoPreviewer> createState() => _VideoPreviewerState();
}
class _VideoPreviewerState extends State<VideoPreviewer> {
VideoPlayerController? _videoController;
bool _isPlaying = false;
File? _tempVideoFile;
bool _showControls = false;
@override
void initState() {
super.initState();
registerWithAllPlatforms();
_initializeVideoPlayer();
}
void registerWithAllPlatforms() {
try {
fvp.registerWith();
} catch (e) {
// pass
}
}
void _initializeVideoPlayer() async {
final tempDir = await getTemporaryDirectory();
_tempVideoFile = File(
'${tempDir.path}/temp_video_${DateTime.now().millisecondsSinceEpoch}');
try {
await _tempVideoFile?.writeAsBytes(widget.videoBytes);
_videoController = VideoPlayerController.file(_tempVideoFile!)
..initialize().then((_) {
if (mounted) {
setState(() {
_videoController!.play();
_videoController!.setLooping(true);
});
}
});
} catch (e) {
return;
}
}
@override
Widget build(BuildContext context) {
final iconColor = Theme.of(context).iconTheme.color;
final progressBarColors = VideoProgressColors(
playedColor: iconColor!,
bufferedColor: iconColor.withOpacity(0.5),
backgroundColor: iconColor.withOpacity(0.3),
);
return Scaffold(
body: MouseRegion(
onEnter: (_) => setState(() => _showControls = true),
onExit: (_) => setState(() => _showControls = false),
child: Stack(
children: [
Center(
child: _videoController?.value.isInitialized == true
? AspectRatio(
aspectRatio: _videoController!.value.aspectRatio,
child: VideoPlayer(_videoController!),
)
: const CircularProgressIndicator(),
),
Positioned(
left: 0,
right: 0,
bottom: 0,
child: _videoController?.value.isInitialized == true
? SizedBox(
height: 50.0,
child: VideoProgressIndicator(
_videoController!,
allowScrubbing: true,
padding: const EdgeInsets.all(20),
colors: progressBarColors,
),
)
: Container(height: 0),
),
if (_showControls)
Center(
child: GestureDetector(
onTap: () {
if (_videoController!.value.isPlaying) {
_videoController!.pause();
} else {
_videoController!.play();
}
setState(() {
_isPlaying = !_isPlaying;
});
},
child: Container(
color: Colors.transparent,
child: Icon(
_isPlaying ? Icons.play_arrow : Icons.pause,
size: 64,
color: iconColor,
),
),
),
),
],
),
),
);
}
@override
void dispose() {
_videoController?.pause();
_videoController?.dispose();
if (!kIsRunningTests) {
Future.delayed(const Duration(seconds: 1), () async {
try {
if (_tempVideoFile != null) {
await _tempVideoFile!.delete();
}
} catch (e) {
return;
}
});
}
super.dispose();
}
}

View File

@ -225,6 +225,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.0.3" version: "3.0.3"
csslib:
dependency: transitive
description:
name: csslib
sha256: "706b5707578e0c1b4b7550f64078f0a0f19dec3f50a178ffae7006b0a9ca58fb"
url: "https://pub.dev"
source: hosted
version: "1.0.0"
csv: csv:
dependency: "direct main" dependency: "direct main"
description: description:
@ -504,6 +512,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.2.0" version: "3.2.0"
fvp:
dependency: "direct main"
description:
name: fvp
sha256: "995328479ba4641da6760ddc84a168db157a3b9db4f0417fa68713d99344a146"
url: "https://pub.dev"
source: hosted
version: "0.14.0"
glob: glob:
dependency: transitive dependency: transitive
description: description:
@ -552,6 +568,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.1.0" version: "1.1.0"
html:
dependency: transitive
description:
name: html
sha256: "3a7812d5bcd2894edf53dfaf8cd640876cf6cef50a8f238745c8b8120ea74d3a"
url: "https://pub.dev"
source: hosted
version: "0.15.4"
html_unescape: html_unescape:
dependency: transitive dependency: transitive
description: description:
@ -1350,6 +1374,46 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.1.4" version: "2.1.4"
video_player:
dependency: "direct main"
description:
name: video_player
sha256: afc65f4b8bcb2c188f64a591f84fb471f4f2e19fc607c65fd8d2f8fedb3dec23
url: "https://pub.dev"
source: hosted
version: "2.8.3"
video_player_android:
dependency: transitive
description:
name: video_player_android
sha256: "4dd9b8b86d70d65eecf3dcabfcdfbb9c9115d244d022654aba49a00336d540c2"
url: "https://pub.dev"
source: hosted
version: "2.4.12"
video_player_avfoundation:
dependency: transitive
description:
name: video_player_avfoundation
sha256: "309e3962795e761be010869bae65c0b0e45b5230c5cee1bec72197ca7db040ed"
url: "https://pub.dev"
source: hosted
version: "2.5.6"
video_player_platform_interface:
dependency: "direct main"
description:
name: video_player_platform_interface
sha256: "236454725fafcacf98f0f39af0d7c7ab2ce84762e3b63f2cbb3ef9a7e0550bc6"
url: "https://pub.dev"
source: hosted
version: "6.2.2"
video_player_web:
dependency: transitive
description:
name: video_player_web
sha256: "41245cef5ef29c4585dbabcbcbe9b209e34376642c7576cabf11b4ad9289d6e4"
url: "https://pub.dev"
source: hosted
version: "2.3.0"
vm_service: vm_service:
dependency: transitive dependency: transitive
description: description:

View File

@ -44,6 +44,9 @@ dependencies:
package_info_plus: ^5.0.1 package_info_plus: ^5.0.1
flutter_typeahead: ^5.2.0 flutter_typeahead: ^5.2.0
provider: ^6.1.2 provider: ^6.1.2
fvp: ^0.14.0
video_player: ^2.3.2
video_player_platform_interface: ^6.2.2
json_data_explorer: json_data_explorer:
git: git:
url: https://github.com/foss42/json_data_explorer.git url: https://github.com/foss42/json_data_explorer.git

View File

@ -5,6 +5,7 @@ import 'package:apidash/consts.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:printing/printing.dart' show PdfPreview; import 'package:printing/printing.dart' show PdfPreview;
import 'package:flutter_svg/flutter_svg.dart' show SvgPicture; import 'package:flutter_svg/flutter_svg.dart' show SvgPicture;
import 'package:apidash/widgets/video_previewer.dart';
import '../test_consts.dart'; import '../test_consts.dart';
void main() { void main() {
@ -63,10 +64,7 @@ void main() {
), ),
), ),
); );
expect(find.byType(VideoPreviewer), findsOneWidget);
expect(
find.text("${kMimeTypeRaiseIssueStart}video/H264$kMimeTypeRaiseIssue"),
findsOneWidget);
}); });
testWidgets('Testing when type/subtype is model/step+xml', (tester) async { testWidgets('Testing when type/subtype is model/step+xml', (tester) async {

View File

@ -10,13 +10,15 @@ import 'package:apidash/models/models.dart';
import '../test_consts.dart'; import '../test_consts.dart';
void main() { void main() {
testWidgets('Testing Sending Widget', (tester) async { testWidgets('Testing Sending Widget Without Timer', (tester) async {
await tester.pumpWidget( await tester.pumpWidget(
MaterialApp( MaterialApp(
title: 'Send', title: 'Send',
theme: kThemeDataDark, theme: kThemeDataDark,
home: const Scaffold( home: const Scaffold(
body: SendingWidget(), body: SendingWidget(
startSendingTime: null,
),
), ),
), ),
); );
@ -24,6 +26,26 @@ void main() {
expect(find.byType(Lottie), findsOneWidget); expect(find.byType(Lottie), findsOneWidget);
}); });
testWidgets('Testing Sending Widget With Timer', (tester) async {
await tester.pumpWidget(
MaterialApp(
title: 'Send',
theme: kThemeDataDark,
home: Scaffold(
body: SendingWidget(
startSendingTime: DateTime.now(),
),
),
),
);
expect(find.text('Time elapsed: 0 ms'), findsOneWidget);
expect(find.byType(Lottie), findsOneWidget);
await tester.pump(const Duration(seconds: 1));
expect(find.text('Time elapsed: 1.00 s'), findsOneWidget);
});
testWidgets('Testing Not Sent Widget', (tester) async { testWidgets('Testing Not Sent Widget', (tester) async {
await tester.pumpWidget( await tester.pumpWidget(
const MaterialApp( const MaterialApp(