Skip to content

Commit

Permalink
fix: Fix defects by state finding helpers and improve code coverage
Browse files Browse the repository at this point in the history
  • Loading branch information
rlperez committed Apr 21, 2022
1 parent 137299a commit 692ee25
Show file tree
Hide file tree
Showing 9 changed files with 214 additions and 45 deletions.
20 changes: 17 additions & 3 deletions lib/providers/color_provider.dart
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,24 @@ class ColorProvider extends DeviceProvider {
@override
String get displayName => deviceDetail?.displayName ?? _DEFAULT_DISPLAY_NAME;

ColorTrait? get colorTrait => trait<ColorTrait>() as ColorTrait?;
ColorTrait? get getColorTrait {
final color = trait<ColorTrait>();
if (color is ColorTrait) {
return color;
} else {
return null;
}
}

GHSBColorValueInput? get getColorState {
final color = getColorTrait?.stateWhereType<HSBColor>();
if (color is HSBColor) {
return color.value;
} else {
return null;
}
}

GHSBColorValueInput? get getColorState =>
colorTrait?.stateWhereType<HSBColor>().value as GHSBColorValueInput?;

Future<void> setColorAction(HSBColor color) {
return performAction<HSBColor>(color, () => getColorState,
Expand Down
29 changes: 14 additions & 15 deletions lib/traits/color_widget.dart
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,7 @@ class ColorWidget extends StatelessWidget {
),
SizedBox(height: 10),
Center(
child: SizedBox(
width: 100,
height: 100,
child: _mainIcon())),
child: SizedBox(width: 100, height: 100, child: _mainIcon())),
SizedBox(height: 10),
_stateRow(context),
]);
Expand Down Expand Up @@ -77,17 +74,19 @@ class ColorWidget extends StatelessWidget {
),
IconButton(
onPressed: () {
SlidePicker(
pickerColor: pickedColor.toColor(),
onColorChanged: (color) {
final calculatedColor = HSVColor.fromColor(color);
_colorProvider.setColorAction(HSBColor(
calculatedColor.hue.toInt(),
(calculatedColor.saturation * 100).toInt(),
(calculatedColor.value * 100).toInt()));
},
colorModel: ColorModel.hsv,
);
showDialog(
context: context,
builder: (context) => SlidePicker(
pickerColor: pickedColor.toColor(),
onColorChanged: (color) {
final calculatedColor = HSVColor.fromColor(color);
_colorProvider.setColorAction(HSBColor(
calculatedColor.hue.toInt(),
(calculatedColor.saturation * 100).toInt(),
(calculatedColor.value * 100).toInt()));
},
colorModel: ColorModel.hsv,
));
},
icon: Icon(
BootstrapIcons.pencil,
Expand Down
36 changes: 25 additions & 11 deletions test/mixins/color_testing.dart
Original file line number Diff line number Diff line change
Expand Up @@ -22,32 +22,46 @@ mixin ColorTesting {
when(mockColorProvider.isPerformingAction).thenReturn(isPerformingAction);

when(mockColorProvider.deviceDetail).thenReturn(device);
when(mockColorProvider.getColorState).thenReturn(device.colorState.value);
when(mockColorProvider.getColorState)
.thenReturn(device.colorTrait?.color.value);
when(mockColorProvider.setColorAction(any))
.thenAnswer((_) => Future.value(null));

final colorTrait = device.traits.firstWhere((trait) => trait is ColorTrait,
orElse: () => UnknownTrait(device.displayName));
if (colorTrait is UnknownTrait) {
when(mockColorProvider.getColorTrait).thenReturn(null);
} else {
when(mockColorProvider.getColorTrait)
.thenReturn(colorTrait as ColorTrait);
}

return mockColorProvider;
}
}

class TestColorDevice extends Device {
late final HSBColor colorState;
TestColorDevice(Device device, {HSBColor? colorState})
: this.colorState = colorState ?? HSBColor(130, 50, 50),
super(
late final ColorTrait? colorTrait;
TestColorDevice(Device device, {this.colorTrait})
: super(
device.id,
device.displayName,
device.description,
device.manufacturerName,
device.model,
device.serialNumber,
device.createdAt,
device.updatedAt, [
ColorTrait(colorState ?? HSBColor(130, 50, 50)),
...device.traits.where((t) => t.runtimeType != ColorTrait)
]);
device.updatedAt,
[
colorTrait,
...device.traits.where((t) => t.runtimeType != ColorTrait)
].whereType<Trait>().toList());

TestColorDevice withColor(HSBColor colorState) {
return TestColorDevice(this, colorState: colorState);
TestColorDevice withColor(HSBColor? colorState) {
if (colorState == null) {
return TestColorDevice(this, colorTrait: null);
} else {
return TestColorDevice(this, colorTrait: ColorTrait(colorState));
}
}
}
2 changes: 1 addition & 1 deletion test/providers/brightness_provider_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ void main() {
final GetDeviceDetailsMethod mockDeviceDetailsMethod =
brightnessTest.getMockDeviceDetailsMethod(request, deviceId);

BrightnessProvider brightnessProvider = await BrightnessProvider(
final brightnessProvider = await BrightnessProvider(
request, deviceId,
getDetails: mockDeviceDetailsMethod);

Expand Down
66 changes: 59 additions & 7 deletions test/providers/color_provider_test.dart
Original file line number Diff line number Diff line change
@@ -1,18 +1,70 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:mockito/annotations.dart';
import 'package:mockito/mockito.dart';
import 'package:yonomi_device_widgets/providers/color_provider.dart';
import 'package:yonomi_platform_sdk/third_party/yonomi_graphql_schema/schema.docs.schema.gql.dart';
import 'package:yonomi_platform_sdk/yonomi-sdk.dart';

import '../mixins/color_testing.dart';
import '../mixins/device_testing.dart';
import 'color_provider_test.mocks.dart';

class BatteryLevelProviderTest with DeviceTesting, ColorTesting {}
class GetDeviceDetailsMethod extends Mock {
Future<Device> call(Request request, String id);
}

class SendSetColorActionMethod extends Mock {
Future<void> call(Request request, String id, HSBColor hsbColor);
}

@GenerateMocks([GetDeviceDetailsMethod, SendSetColorActionMethod])
void main() {
final colorTest = ColorProviderTest();
final defaultColorDevice = TestColorDevice(
colorTest
.device(id: 'aDeviceId', traits: [ColorTrait(HSBColor(130, 50, 50))]),
colorTrait: ColorTrait(HSBColor(130, 50, 50)));
;

group('For ColorProvider', () {
test("""When loading device data, we are notified that it is loading
through isLoading.""", () async {});
test("""After successfully loading device data, should be in idle state
when done.""", () async {});
test("""When an error occurs loading device data, we are notified
an error occurred using isInErrorState and get
an error message with getErrorMessage.""", () async {});
when done.""", () async {
Request request = Request("", {});
String deviceId = 'aDeviceId';

final GetDeviceDetailsMethod mockDeviceDetailsMethod =
colorTest.getMockDeviceDetailsMethod(request, deviceId);

final colorProvider = await ColorProvider(request, deviceId,
getDetails: mockDeviceDetailsMethod);

expect(colorProvider.isLoading, equals(false),
reason: 'is in loading state');
expect(colorProvider.isBusy, equals(false), reason: 'is in busy state');
expect(colorProvider.isInErrorState, equals(false),
reason: 'is in error state');
expect(colorProvider.displayName, defaultColorDevice.displayName);
expect(colorProvider.deviceDetail?.id, equals(deviceId));
expect(colorProvider.getColorTrait, isA<ColorTrait>());
expect(colorProvider.getColorState, isA<GHSBColorValueInput>());
});

});
}

class ColorProviderTest with DeviceTesting, ColorTesting {
MockGetDeviceDetailsMethod getMockDeviceDetailsMethod(
Request request, String deviceId) {
MockGetDeviceDetailsMethod mockDeviceDetailsMethod =
MockGetDeviceDetailsMethod();

final device = TestColorDevice(
this.device(id: deviceId, traits: [ColorTrait(HSBColor(130, 50, 50))]),
colorTrait: ColorTrait(HSBColor(130, 50, 50)));

when(mockDeviceDetailsMethod.call(request, deviceId))
.thenAnswer((_) => Future.value(device));

return mockDeviceDetailsMethod;
}
}
57 changes: 57 additions & 0 deletions test/providers/color_provider_test.mocks.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// Mocks generated by Mockito 5.0.15 from annotations
// in yonomi_device_widgets/test/providers/color_provider_test.dart.
// Do not manually edit this file.

import 'dart:async' as _i4;

import 'package:mockito/mockito.dart' as _i1;
import 'package:yonomi_platform_sdk/yonomi-sdk.dart' as _i2;

import 'color_provider_test.dart' as _i3;

// ignore_for_file: avoid_redundant_argument_values
// ignore_for_file: avoid_setters_without_getters
// ignore_for_file: comment_references
// ignore_for_file: implementation_imports
// ignore_for_file: invalid_use_of_visible_for_testing_member
// ignore_for_file: prefer_const_constructors
// ignore_for_file: unnecessary_parenthesis

class _FakeDevice_0 extends _i1.Fake implements _i2.Device {}

/// A class which mocks [GetDeviceDetailsMethod].
///
/// See the documentation for Mockito's code generation for more information.
class MockGetDeviceDetailsMethod extends _i1.Mock
implements _i3.GetDeviceDetailsMethod {
MockGetDeviceDetailsMethod() {
_i1.throwOnMissingStub(this);
}

@override
_i4.Future<_i2.Device> call(_i2.Request? request, String? id) =>
(super.noSuchMethod(Invocation.method(#call, [request, id]),
returnValue: Future<_i2.Device>.value(_FakeDevice_0()))
as _i4.Future<_i2.Device>);
@override
String toString() => super.toString();
}

/// A class which mocks [SendSetColorActionMethod].
///
/// See the documentation for Mockito's code generation for more information.
class MockSendSetColorActionMethod extends _i1.Mock
implements _i3.SendSetColorActionMethod {
MockSendSetColorActionMethod() {
_i1.throwOnMissingStub(this);
}

@override
_i4.Future<void> call(
_i2.Request? request, String? id, _i2.HSBColor? hsbColor) =>
(super.noSuchMethod(Invocation.method(#call, [request, id, hsbColor]),
returnValue: Future<void>.value(),
returnValueForMissingStub: Future<void>.value()) as _i4.Future<void>);
@override
String toString() => super.toString();
}
33 changes: 31 additions & 2 deletions test/traits/color_widget_test.dart
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import 'package:bootstrap_icons/bootstrap_icons.dart';
import 'package:flutter/material.dart';
import 'package:flutter_colorpicker/flutter_colorpicker.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:yonomi_device_widgets/assets/traits/unknown_item_icon.dart';
import 'package:mockito/mockito.dart';
import 'package:yonomi_device_widgets/providers/color_provider.dart';
import 'package:yonomi_device_widgets/traits/color_widget.dart';
import 'package:yonomi_platform_sdk/yonomi-sdk.dart';
Expand All @@ -20,7 +21,8 @@ class ColorWidgetTest with DeviceTesting, ColorTesting {
main() {
final test = ColorWidgetTest();
final colorDevice =
TestColorDevice(test.device(), colorState: HSBColor(130, 50, 50));
TestColorDevice(test.device(),
colorTrait: ColorTrait(HSBColor(130, 50, 50)));

testWidgets('When loading, should show CircularProgressIndicator ',
(WidgetTester tester) async {
Expand Down Expand Up @@ -52,5 +54,32 @@ main() {
await tester.pumpWidget(test.createMaterialApp(mockColorProvider));

expect(find.byIcon(BootstrapIcons.box), findsOneWidget);
expect(mockColorProvider.displayName, colorDevice.displayName);
expect(mockColorProvider.getColorTrait,
colorDevice.traits.firstWhere((t) => t is ColorTrait));
});

testWidgets(
'When widget color state value is null, should call provider for color',
(WidgetTester tester) async {
final mockColorProvider =
test.mockColorProvider(colorDevice.withColor(null));
await tester.pumpWidget(test.createMaterialApp(mockColorProvider));
verify(mockColorProvider.getColorState).called(1);
});

testWidgets('When edit clicked, should show color picker',
(WidgetTester tester) async {
final mockColorProvider = test.mockColorProvider(colorDevice);
await tester.pumpWidget(test.createMaterialApp(mockColorProvider));

await tester.tap(find.byIcon(BootstrapIcons.pencil));
await tester.pumpAndSettle(const Duration(seconds: 1));
expect(find.byType(SlidePicker), findsOneWidget);

await tester.drag(find.byType(ColorPickerSlider).first, Offset(100, 0));
await tester.pumpAndSettle(const Duration(seconds: 1));

verify(mockColorProvider.setColorAction(any)).called(3);
});
}
14 changes: 9 additions & 5 deletions test/traits/detail_screen_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -170,9 +170,8 @@ void main() {

testWidgets('When loading, should show CircularProgressIndicator ',
(WidgetTester tester) async {
await tester
.pumpWidget(test.createDetailScreenWhenLoading(Request('', {}), ""));

final materialApp = test.createDetailScreenWhenLoading(Request('', {}), "");
await tester.pumpWidget(materialApp);
expect(find.byType(CircularProgressIndicator), findsOneWidget);
});

Expand Down Expand Up @@ -383,9 +382,14 @@ void main() {

testWidgets('Detail screen returns a multiprovider',
(WidgetTester tester) async {
await tester.pumpWidget(test.createDetailScreenWidgetForTraits(
[], Request('', {}), testedDeviceId));

final detailScreen =
DetailScreen(request: Request('', {}), deviceId: 'deviceId');
expect(
detailScreen.build(MockBuildContext()), isInstanceOf<MultiProvider>());
final multiProvider =
detailScreen.build(MockBuildContext()) as MultiProvider;

expect(multiProvider, isInstanceOf<MultiProvider>());
});
}
2 changes: 1 addition & 1 deletion test/traits/slim/color_slim_widget_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ class ColorSlimWidgetTest with DeviceTesting, ColorTesting {
void main() {
final test = ColorSlimWidgetTest();
final colorDevice =
TestColorDevice(test.device(), colorState: HSBColor(130, 50, 50));
TestColorDevice(test.device(), colorTrait: ColorTrait(HSBColor(130, 50, 50)));

testWidgets('When loading, should show CircularProgressIndicator ',
(WidgetTester tester) async {
Expand Down

0 comments on commit 692ee25

Please sign in to comment.