mirror of
https://github.com/hanskokx/arcane_framework.git
synced 2026-05-14 10:29:06 +02:00
[UNTESTED] Fixes notifiers and adds some additional methods. Adds tests.
Changes: // ArcaneEnvironment breaking: context.read<ArcaneEnvironment>() -> ArcaneEnvironment.of(context) breaking: context.read<ArcaneEnvironment>().state -> ArcaneEnvironment.of(context).environment; // Feature flag service added: reset() // Logging service added: registerInterface() added: unregisterInterfaces() added: unregisterAllInterfaces() // ArcaneReactiveTheme fixed: currentMode, dark, light now actually emit new values when changed added: getters for lightTheme, darkTheme, and systemTheme TODO: test systemTheme Signed-off-by: Hans Kokx <hans.d.kokx@gmail.com>
This commit is contained in:
@@ -0,0 +1,144 @@
|
||||
import "package:arcane_framework/arcane_framework.dart";
|
||||
import "package:flutter/material.dart";
|
||||
import "package:flutter_test/flutter_test.dart";
|
||||
import "package:mockito/annotations.dart";
|
||||
import "package:mockito/mockito.dart";
|
||||
|
||||
import "authentication_service_test.mocks.dart";
|
||||
|
||||
@GenerateMocks([
|
||||
ArcaneAuthInterface,
|
||||
ArcaneEnvironmentProvider,
|
||||
])
|
||||
void main() {
|
||||
late ArcaneAuthInterface mockInterface;
|
||||
|
||||
group("ArcaneAuthenticationService", () {
|
||||
setUp(() async {
|
||||
// Initialize mocks
|
||||
mockInterface = MockArcaneAuthInterface();
|
||||
|
||||
// Initialize the service
|
||||
await ArcaneAuthenticationService.I.reset();
|
||||
|
||||
// Set up default mock behaviors
|
||||
when(mockInterface.login(input: anyNamed("input"))).thenAnswer(
|
||||
(_) async => Result.ok(null),
|
||||
);
|
||||
when(mockInterface.logout()).thenAnswer(
|
||||
(_) async => Result.ok(null),
|
||||
);
|
||||
when(mockInterface.init()).thenAnswer(
|
||||
(_) async {},
|
||||
);
|
||||
|
||||
await ArcaneAuthenticationService.I.registerInterface(mockInterface);
|
||||
});
|
||||
|
||||
testWidgets("login with success", (WidgetTester tester) async {
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
home: ArcaneEnvironmentProvider(
|
||||
environment: Environment.normal,
|
||||
child: Builder(
|
||||
builder: (context) {
|
||||
return Container();
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
final result = await ArcaneAuthenticationService.I.login(
|
||||
input: {"username": "test"},
|
||||
);
|
||||
expect(result.isSuccess, true);
|
||||
expect(
|
||||
ArcaneAuthenticationService.I.status,
|
||||
equals(AuthenticationStatus.authenticated),
|
||||
);
|
||||
});
|
||||
|
||||
testWidgets("login with failure", (WidgetTester tester) async {
|
||||
// Reset the mock behavior for this specific test
|
||||
when(mockInterface.login(input: anyNamed("input")))
|
||||
.thenAnswer((_) async => Result.error("error"));
|
||||
|
||||
final result = await ArcaneAuthenticationService.I
|
||||
.login(input: {"username": "test"});
|
||||
expect(result.isFailure, true);
|
||||
expect(
|
||||
ArcaneAuthenticationService.I.status,
|
||||
equals(AuthenticationStatus.unauthenticated),
|
||||
);
|
||||
});
|
||||
|
||||
testWidgets("logout with success", (WidgetTester tester) async {
|
||||
ArcaneAuthenticationService.I.setAuthenticated();
|
||||
final result = await ArcaneAuthenticationService.I.logOut();
|
||||
expect(result.isSuccess, true);
|
||||
expect(
|
||||
ArcaneAuthenticationService.I.status,
|
||||
equals(AuthenticationStatus.unauthenticated),
|
||||
);
|
||||
});
|
||||
|
||||
testWidgets("setDebug enables debug mode", (WidgetTester tester) async {
|
||||
late BuildContext capturedContext;
|
||||
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
home: ArcaneEnvironmentProvider(
|
||||
child: Builder(
|
||||
builder: (context) {
|
||||
capturedContext = context;
|
||||
return Container();
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
await tester.pump();
|
||||
ArcaneEnvironment.of(capturedContext).enableDebugMode();
|
||||
await tester.pump();
|
||||
expect(
|
||||
ArcaneEnvironment.of(capturedContext).environment,
|
||||
equals(Environment.debug),
|
||||
);
|
||||
});
|
||||
|
||||
testWidgets("setNormal disables debug mode", (WidgetTester tester) async {
|
||||
late BuildContext capturedContext;
|
||||
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
home: ArcaneEnvironmentProvider(
|
||||
child: Builder(
|
||||
builder: (context) {
|
||||
capturedContext = context;
|
||||
return Container();
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
await tester.pump();
|
||||
ArcaneEnvironment.of(capturedContext).enableDebugMode();
|
||||
await tester.pump();
|
||||
|
||||
expect(
|
||||
ArcaneEnvironment.of(capturedContext).environment,
|
||||
equals(Environment.debug),
|
||||
);
|
||||
|
||||
ArcaneEnvironment.of(capturedContext).disableDebugMode();
|
||||
await tester.pump();
|
||||
|
||||
expect(
|
||||
ArcaneEnvironment.of(capturedContext).environment,
|
||||
equals(Environment.normal),
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,275 @@
|
||||
// Mocks generated by Mockito 5.4.5 from annotations
|
||||
// in arcane_framework/test/services/authentication/authentication_service_test.dart.
|
||||
// Do not manually edit this file.
|
||||
|
||||
// ignore_for_file: no_leading_underscores_for_library_prefixes
|
||||
import 'dart:async' as _i5;
|
||||
|
||||
import 'package:arcane_framework/arcane_framework.dart' as _i2;
|
||||
import 'package:flutter/foundation.dart' as _i4;
|
||||
import 'package:flutter/widgets.dart' as _i3;
|
||||
import 'package:mockito/mockito.dart' as _i1;
|
||||
import 'package:mockito/src/dummies.dart' as _i6;
|
||||
|
||||
// ignore_for_file: type=lint
|
||||
// ignore_for_file: avoid_redundant_argument_values
|
||||
// ignore_for_file: avoid_setters_without_getters
|
||||
// ignore_for_file: comment_references
|
||||
// ignore_for_file: deprecated_member_use
|
||||
// ignore_for_file: deprecated_member_use_from_same_package
|
||||
// ignore_for_file: implementation_imports
|
||||
// ignore_for_file: invalid_use_of_visible_for_testing_member
|
||||
// ignore_for_file: must_be_immutable
|
||||
// ignore_for_file: prefer_const_constructors
|
||||
// ignore_for_file: unnecessary_parenthesis
|
||||
// ignore_for_file: camel_case_types
|
||||
// ignore_for_file: subtype_of_sealed_class
|
||||
|
||||
class _FakeResult_0<T, E> extends _i1.SmartFake implements _i2.Result<T, E> {
|
||||
_FakeResult_0(Object parent, Invocation parentInvocation)
|
||||
: super(parent, parentInvocation);
|
||||
}
|
||||
|
||||
class _FakeWidget_1 extends _i1.SmartFake implements _i3.Widget {
|
||||
_FakeWidget_1(Object parent, Invocation parentInvocation)
|
||||
: super(parent, parentInvocation);
|
||||
|
||||
@override
|
||||
String toString({_i3.DiagnosticLevel? minLevel = _i3.DiagnosticLevel.info}) =>
|
||||
super.toString();
|
||||
}
|
||||
|
||||
class _FakeState_2<T extends _i3.StatefulWidget> extends _i1.SmartFake
|
||||
implements _i3.State<T> {
|
||||
_FakeState_2(Object parent, Invocation parentInvocation)
|
||||
: super(parent, parentInvocation);
|
||||
|
||||
@override
|
||||
String toString({_i3.DiagnosticLevel? minLevel = _i3.DiagnosticLevel.info}) =>
|
||||
super.toString();
|
||||
}
|
||||
|
||||
class _FakeStatefulElement_3 extends _i1.SmartFake
|
||||
implements _i3.StatefulElement {
|
||||
_FakeStatefulElement_3(Object parent, Invocation parentInvocation)
|
||||
: super(parent, parentInvocation);
|
||||
|
||||
@override
|
||||
String toString({_i3.DiagnosticLevel? minLevel = _i3.DiagnosticLevel.info}) =>
|
||||
super.toString();
|
||||
}
|
||||
|
||||
class _FakeDiagnosticsNode_4 extends _i1.SmartFake
|
||||
implements _i3.DiagnosticsNode {
|
||||
_FakeDiagnosticsNode_4(Object parent, Invocation parentInvocation)
|
||||
: super(parent, parentInvocation);
|
||||
|
||||
@override
|
||||
String toString({
|
||||
_i4.TextTreeConfiguration? parentConfiguration,
|
||||
_i3.DiagnosticLevel? minLevel = _i3.DiagnosticLevel.info,
|
||||
}) => super.toString();
|
||||
}
|
||||
|
||||
/// A class which mocks [ArcaneAuthInterface].
|
||||
///
|
||||
/// See the documentation for Mockito's code generation for more information.
|
||||
class MockArcaneAuthInterface extends _i1.Mock
|
||||
implements _i2.ArcaneAuthInterface {
|
||||
MockArcaneAuthInterface() {
|
||||
_i1.throwOnMissingStub(this);
|
||||
}
|
||||
|
||||
@override
|
||||
_i5.Future<bool> get isSignedIn =>
|
||||
(super.noSuchMethod(
|
||||
Invocation.getter(#isSignedIn),
|
||||
returnValue: _i5.Future<bool>.value(false),
|
||||
)
|
||||
as _i5.Future<bool>);
|
||||
|
||||
@override
|
||||
_i5.Future<void> init() =>
|
||||
(super.noSuchMethod(
|
||||
Invocation.method(#init, []),
|
||||
returnValue: _i5.Future<void>.value(),
|
||||
returnValueForMissingStub: _i5.Future<void>.value(),
|
||||
)
|
||||
as _i5.Future<void>);
|
||||
|
||||
@override
|
||||
_i5.Future<_i2.Result<void, String>> logout() =>
|
||||
(super.noSuchMethod(
|
||||
Invocation.method(#logout, []),
|
||||
returnValue: _i5.Future<_i2.Result<void, String>>.value(
|
||||
_FakeResult_0<void, String>(this, Invocation.method(#logout, [])),
|
||||
),
|
||||
)
|
||||
as _i5.Future<_i2.Result<void, String>>);
|
||||
|
||||
@override
|
||||
_i5.Future<_i2.Result<void, String>> login<T>({
|
||||
T? input,
|
||||
_i5.Future<void> Function()? onLoggedIn,
|
||||
}) =>
|
||||
(super.noSuchMethod(
|
||||
Invocation.method(#login, [], {
|
||||
#input: input,
|
||||
#onLoggedIn: onLoggedIn,
|
||||
}),
|
||||
returnValue: _i5.Future<_i2.Result<void, String>>.value(
|
||||
_FakeResult_0<void, String>(
|
||||
this,
|
||||
Invocation.method(#login, [], {
|
||||
#input: input,
|
||||
#onLoggedIn: onLoggedIn,
|
||||
}),
|
||||
),
|
||||
),
|
||||
)
|
||||
as _i5.Future<_i2.Result<void, String>>);
|
||||
}
|
||||
|
||||
/// A class which mocks [ArcaneEnvironmentProvider].
|
||||
///
|
||||
/// See the documentation for Mockito's code generation for more information.
|
||||
class MockArcaneEnvironmentProvider extends _i1.Mock
|
||||
implements _i2.ArcaneEnvironmentProvider {
|
||||
MockArcaneEnvironmentProvider() {
|
||||
_i1.throwOnMissingStub(this);
|
||||
}
|
||||
|
||||
@override
|
||||
_i3.Widget get child =>
|
||||
(super.noSuchMethod(
|
||||
Invocation.getter(#child),
|
||||
returnValue: _FakeWidget_1(this, Invocation.getter(#child)),
|
||||
)
|
||||
as _i3.Widget);
|
||||
|
||||
@override
|
||||
_i2.Environment get environment =>
|
||||
(super.noSuchMethod(
|
||||
Invocation.getter(#environment),
|
||||
returnValue: _i2.Environment.debug,
|
||||
)
|
||||
as _i2.Environment);
|
||||
|
||||
@override
|
||||
_i3.State<_i2.ArcaneEnvironmentProvider> createState() =>
|
||||
(super.noSuchMethod(
|
||||
Invocation.method(#createState, []),
|
||||
returnValue: _FakeState_2<_i2.ArcaneEnvironmentProvider>(
|
||||
this,
|
||||
Invocation.method(#createState, []),
|
||||
),
|
||||
)
|
||||
as _i3.State<_i2.ArcaneEnvironmentProvider>);
|
||||
|
||||
@override
|
||||
_i3.StatefulElement createElement() =>
|
||||
(super.noSuchMethod(
|
||||
Invocation.method(#createElement, []),
|
||||
returnValue: _FakeStatefulElement_3(
|
||||
this,
|
||||
Invocation.method(#createElement, []),
|
||||
),
|
||||
)
|
||||
as _i3.StatefulElement);
|
||||
|
||||
@override
|
||||
String toStringShort() =>
|
||||
(super.noSuchMethod(
|
||||
Invocation.method(#toStringShort, []),
|
||||
returnValue: _i6.dummyValue<String>(
|
||||
this,
|
||||
Invocation.method(#toStringShort, []),
|
||||
),
|
||||
)
|
||||
as String);
|
||||
|
||||
@override
|
||||
void debugFillProperties(_i4.DiagnosticPropertiesBuilder? properties) =>
|
||||
super.noSuchMethod(
|
||||
Invocation.method(#debugFillProperties, [properties]),
|
||||
returnValueForMissingStub: null,
|
||||
);
|
||||
|
||||
@override
|
||||
String toStringShallow({
|
||||
String? joiner = ', ',
|
||||
_i3.DiagnosticLevel? minLevel = _i3.DiagnosticLevel.debug,
|
||||
}) =>
|
||||
(super.noSuchMethod(
|
||||
Invocation.method(#toStringShallow, [], {
|
||||
#joiner: joiner,
|
||||
#minLevel: minLevel,
|
||||
}),
|
||||
returnValue: _i6.dummyValue<String>(
|
||||
this,
|
||||
Invocation.method(#toStringShallow, [], {
|
||||
#joiner: joiner,
|
||||
#minLevel: minLevel,
|
||||
}),
|
||||
),
|
||||
)
|
||||
as String);
|
||||
|
||||
@override
|
||||
String toStringDeep({
|
||||
String? prefixLineOne = '',
|
||||
String? prefixOtherLines,
|
||||
_i3.DiagnosticLevel? minLevel = _i3.DiagnosticLevel.debug,
|
||||
int? wrapWidth = 65,
|
||||
}) =>
|
||||
(super.noSuchMethod(
|
||||
Invocation.method(#toStringDeep, [], {
|
||||
#prefixLineOne: prefixLineOne,
|
||||
#prefixOtherLines: prefixOtherLines,
|
||||
#minLevel: minLevel,
|
||||
#wrapWidth: wrapWidth,
|
||||
}),
|
||||
returnValue: _i6.dummyValue<String>(
|
||||
this,
|
||||
Invocation.method(#toStringDeep, [], {
|
||||
#prefixLineOne: prefixLineOne,
|
||||
#prefixOtherLines: prefixOtherLines,
|
||||
#minLevel: minLevel,
|
||||
#wrapWidth: wrapWidth,
|
||||
}),
|
||||
),
|
||||
)
|
||||
as String);
|
||||
|
||||
@override
|
||||
_i3.DiagnosticsNode toDiagnosticsNode({
|
||||
String? name,
|
||||
_i4.DiagnosticsTreeStyle? style,
|
||||
}) =>
|
||||
(super.noSuchMethod(
|
||||
Invocation.method(#toDiagnosticsNode, [], {
|
||||
#name: name,
|
||||
#style: style,
|
||||
}),
|
||||
returnValue: _FakeDiagnosticsNode_4(
|
||||
this,
|
||||
Invocation.method(#toDiagnosticsNode, [], {
|
||||
#name: name,
|
||||
#style: style,
|
||||
}),
|
||||
),
|
||||
)
|
||||
as _i3.DiagnosticsNode);
|
||||
|
||||
@override
|
||||
List<_i3.DiagnosticsNode> debugDescribeChildren() =>
|
||||
(super.noSuchMethod(
|
||||
Invocation.method(#debugDescribeChildren, []),
|
||||
returnValue: <_i3.DiagnosticsNode>[],
|
||||
)
|
||||
as List<_i3.DiagnosticsNode>);
|
||||
|
||||
@override
|
||||
String toString({_i3.DiagnosticLevel? minLevel = _i3.DiagnosticLevel.info}) =>
|
||||
super.toString();
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
import "package:arcane_framework/arcane_framework.dart";
|
||||
import "package:flutter_test/flutter_test.dart";
|
||||
|
||||
void main() {
|
||||
group("ArcaneFeatureFlags", () {
|
||||
late ArcaneFeatureFlags featureFlags;
|
||||
|
||||
setUp(() {
|
||||
featureFlags = ArcaneFeatureFlags.I;
|
||||
});
|
||||
|
||||
test("singleton instance is consistent", () {
|
||||
expect(identical(ArcaneFeatureFlags.I, featureFlags), true);
|
||||
});
|
||||
|
||||
group("feature management", () {
|
||||
setUp(() {
|
||||
Arcane.features.reset();
|
||||
});
|
||||
test("enableFeature adds feature to enabled list", () {
|
||||
featureFlags.enableFeature(MockFeature.test);
|
||||
expect(featureFlags.enabledFeatures, contains(MockFeature.test));
|
||||
expect(featureFlags.isEnabled(MockFeature.test), true);
|
||||
});
|
||||
|
||||
test("disableFeature removes feature from enabled list", () {
|
||||
featureFlags.enableFeature(MockFeature.test);
|
||||
featureFlags.disableFeature(MockFeature.test);
|
||||
expect(featureFlags.enabledFeatures, isNot(contains(MockFeature.test)));
|
||||
expect(featureFlags.isDisabled(MockFeature.test), true);
|
||||
});
|
||||
|
||||
test("enabling already enabled feature has no effect", () {
|
||||
featureFlags.enableFeature(MockFeature.test);
|
||||
final initialCount = featureFlags.enabledFeatures.length;
|
||||
featureFlags.enableFeature(MockFeature.test);
|
||||
expect(featureFlags.enabledFeatures.length, equals(initialCount));
|
||||
});
|
||||
|
||||
test("disabling already disabled feature has no effect", () {
|
||||
final initialCount = featureFlags.enabledFeatures.length;
|
||||
featureFlags.disableFeature(MockFeature.test);
|
||||
expect(featureFlags.enabledFeatures.length, equals(initialCount));
|
||||
});
|
||||
});
|
||||
|
||||
group("notifications", () {
|
||||
test("enableFeature notifies listeners", () {
|
||||
var notified = false;
|
||||
featureFlags.addListener(() => notified = true);
|
||||
featureFlags.enableFeature(MockFeature.test);
|
||||
expect(notified, true);
|
||||
});
|
||||
|
||||
test("disableFeature notifies listeners", () {
|
||||
featureFlags.enableFeature(MockFeature.test);
|
||||
var notified = false;
|
||||
featureFlags.addListener(() => notified = true);
|
||||
featureFlags.disableFeature(MockFeature.test);
|
||||
expect(notified, true);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
enum MockFeature {
|
||||
test,
|
||||
another,
|
||||
}
|
||||
@@ -0,0 +1,203 @@
|
||||
import "package:arcane_framework/arcane_framework.dart";
|
||||
import "package:flutter_test/flutter_test.dart";
|
||||
import "package:mockito/annotations.dart";
|
||||
import "package:mockito/mockito.dart";
|
||||
|
||||
import "logging_service_test.mocks.dart";
|
||||
|
||||
class MyOtherLoggingInterface extends Mock implements MockLoggingInterface {}
|
||||
|
||||
@GenerateNiceMocks([
|
||||
MockSpec<LoggingInterface>(
|
||||
onMissingStub: OnMissingStub.returnDefault,
|
||||
),
|
||||
])
|
||||
void main() {
|
||||
final LoggingInterface myInterface = MockLoggingInterface();
|
||||
|
||||
setUp(() {
|
||||
Arcane.logger.reset();
|
||||
});
|
||||
|
||||
group("ArcaneLogger", () {
|
||||
group("interface management", () {
|
||||
test("registerInterfaces adds interfaces correctly", () async {
|
||||
await Arcane.logger.registerInterface(myInterface);
|
||||
|
||||
expect(
|
||||
Arcane.logger.interfaces,
|
||||
contains(isA<LoggingInterface>()),
|
||||
);
|
||||
});
|
||||
|
||||
test("registering an interface doesn't initialize it", () async {
|
||||
await Arcane.logger.registerInterface(myInterface);
|
||||
|
||||
expect(Arcane.logger.interfaces.first, isA<LoggingInterface>());
|
||||
|
||||
expect(myInterface.initialized, false);
|
||||
verifyNever(myInterface.init());
|
||||
});
|
||||
|
||||
test("registering an interface initializes the logger", () async {
|
||||
expect(Arcane.logger.initialized, false);
|
||||
|
||||
await Arcane.logger.registerInterface(myInterface);
|
||||
|
||||
expect(Arcane.logger.initialized, true);
|
||||
});
|
||||
|
||||
test("interfaces can be initialized through the logger", () async {
|
||||
await Arcane.logger.registerInterface(myInterface);
|
||||
|
||||
expect(Arcane.logger.interfaces.first.initialized, false);
|
||||
|
||||
await Arcane.logger.initializeInterfaces();
|
||||
|
||||
verify(Arcane.logger.interfaces.first.init()).called(1);
|
||||
});
|
||||
|
||||
test("multiple interfaces can be registered", () async {
|
||||
await Arcane.logger.registerInterfaces([
|
||||
MockLoggingInterface(),
|
||||
MyOtherLoggingInterface(),
|
||||
]);
|
||||
|
||||
expect(
|
||||
Arcane.logger.interfaces,
|
||||
contains(isA<MockLoggingInterface>()),
|
||||
);
|
||||
expect(
|
||||
Arcane.logger.interfaces,
|
||||
contains(isA<MyOtherLoggingInterface>()),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
group("persistent metadata", () {
|
||||
test("addPersistentMetadata adds metadata correctly", () {
|
||||
Arcane.logger.addPersistentMetadata({"test": "value"});
|
||||
expect(Arcane.logger.additionalMetadata["test"], equals("value"));
|
||||
});
|
||||
|
||||
test("removePersistentMetadata removes specific key", () {
|
||||
Arcane.logger.addPersistentMetadata({"test": "value", "keep": "this"});
|
||||
Arcane.logger.removePersistentMetadata("test");
|
||||
expect(Arcane.logger.additionalMetadata.containsKey("test"), false);
|
||||
expect(Arcane.logger.additionalMetadata["keep"], equals("this"));
|
||||
});
|
||||
|
||||
test("clearPersistentMetadata removes all metadata", () {
|
||||
Arcane.logger
|
||||
.addPersistentMetadata({"test": "value", "another": "value"});
|
||||
Arcane.logger.clearPersistentMetadata();
|
||||
expect(Arcane.logger.additionalMetadata.isEmpty, true);
|
||||
});
|
||||
});
|
||||
|
||||
group("logging messages", () {
|
||||
const String logMessage = "Test";
|
||||
|
||||
setUp(() async {
|
||||
await Arcane.logger.registerInterface(myInterface);
|
||||
});
|
||||
|
||||
test("logging a basic message works", () async {
|
||||
Arcane.log(logMessage);
|
||||
|
||||
verify(
|
||||
myInterface.log(
|
||||
logMessage,
|
||||
metadata: anyNamed("metadata"),
|
||||
level: anyNamed("level"),
|
||||
stackTrace: anyNamed("stackTrace"),
|
||||
extra: anyNamed("extra"),
|
||||
),
|
||||
).called(1);
|
||||
});
|
||||
|
||||
test("logging at a different level works", () async {
|
||||
Arcane.log(
|
||||
logMessage,
|
||||
level: Level.info,
|
||||
);
|
||||
|
||||
verify(
|
||||
myInterface.log(
|
||||
logMessage,
|
||||
metadata: anyNamed("metadata"),
|
||||
level: Level.info,
|
||||
stackTrace: anyNamed("stackTrace"),
|
||||
extra: anyNamed("extra"),
|
||||
),
|
||||
).called(1);
|
||||
|
||||
Arcane.log(
|
||||
logMessage,
|
||||
level: Level.warning,
|
||||
);
|
||||
|
||||
verify(
|
||||
myInterface.log(
|
||||
logMessage,
|
||||
metadata: anyNamed("metadata"),
|
||||
level: Level.warning,
|
||||
stackTrace: anyNamed("stackTrace"),
|
||||
extra: anyNamed("extra"),
|
||||
),
|
||||
).called(1);
|
||||
});
|
||||
|
||||
test("logging a stacktrace works", () async {
|
||||
final stackTrace = StackTrace.current;
|
||||
Arcane.log(logMessage, stackTrace: stackTrace);
|
||||
|
||||
verify(
|
||||
myInterface.log(
|
||||
logMessage,
|
||||
metadata: anyNamed("metadata"),
|
||||
level: anyNamed("level"),
|
||||
stackTrace: stackTrace,
|
||||
extra: anyNamed("extra"),
|
||||
),
|
||||
).called(1);
|
||||
});
|
||||
|
||||
test("logging an extra object works", () async {
|
||||
const bool extraObject = true;
|
||||
Arcane.log(
|
||||
logMessage,
|
||||
extra: extraObject,
|
||||
);
|
||||
|
||||
verify(
|
||||
myInterface.log(
|
||||
logMessage,
|
||||
metadata: anyNamed("metadata"),
|
||||
level: anyNamed("level"),
|
||||
stackTrace: anyNamed("stackTrace"),
|
||||
extra: extraObject,
|
||||
),
|
||||
).called(1);
|
||||
});
|
||||
|
||||
test("logging metadata works", () async {
|
||||
final Map<String, String> metadata = {"test": "value"};
|
||||
Arcane.log(
|
||||
logMessage,
|
||||
metadata: metadata,
|
||||
);
|
||||
|
||||
verify(
|
||||
myInterface.log(
|
||||
logMessage,
|
||||
metadata: metadata,
|
||||
level: anyNamed("level"),
|
||||
stackTrace: anyNamed("stackTrace"),
|
||||
extra: anyNamed("extra"),
|
||||
),
|
||||
).called(1);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
// Mocks generated by Mockito 5.4.5 from annotations
|
||||
// in arcane_framework/test/services/logging/logging_service_test.dart.
|
||||
// Do not manually edit this file.
|
||||
|
||||
// ignore_for_file: no_leading_underscores_for_library_prefixes
|
||||
import 'dart:async' as _i3;
|
||||
|
||||
import 'package:arcane_framework/src/services/logging/logging_service.dart'
|
||||
as _i2;
|
||||
import 'package:mockito/mockito.dart' as _i1;
|
||||
|
||||
// ignore_for_file: type=lint
|
||||
// ignore_for_file: avoid_redundant_argument_values
|
||||
// ignore_for_file: avoid_setters_without_getters
|
||||
// ignore_for_file: comment_references
|
||||
// ignore_for_file: deprecated_member_use
|
||||
// ignore_for_file: deprecated_member_use_from_same_package
|
||||
// ignore_for_file: implementation_imports
|
||||
// ignore_for_file: invalid_use_of_visible_for_testing_member
|
||||
// ignore_for_file: must_be_immutable
|
||||
// ignore_for_file: prefer_const_constructors
|
||||
// ignore_for_file: unnecessary_parenthesis
|
||||
// ignore_for_file: camel_case_types
|
||||
// ignore_for_file: subtype_of_sealed_class
|
||||
|
||||
/// A class which mocks [LoggingInterface].
|
||||
///
|
||||
/// See the documentation for Mockito's code generation for more information.
|
||||
class MockLoggingInterface extends _i1.Mock implements _i2.LoggingInterface {
|
||||
@override
|
||||
bool get initialized =>
|
||||
(super.noSuchMethod(
|
||||
Invocation.getter(#initialized),
|
||||
returnValue: false,
|
||||
returnValueForMissingStub: false,
|
||||
)
|
||||
as bool);
|
||||
|
||||
@override
|
||||
_i3.Future<_i2.LoggingInterface?> init() =>
|
||||
(super.noSuchMethod(
|
||||
Invocation.method(#init, []),
|
||||
returnValue: _i3.Future<_i2.LoggingInterface?>.value(),
|
||||
returnValueForMissingStub:
|
||||
_i3.Future<_i2.LoggingInterface?>.value(),
|
||||
)
|
||||
as _i3.Future<_i2.LoggingInterface?>);
|
||||
|
||||
@override
|
||||
void log(
|
||||
String? message, {
|
||||
Map<String, dynamic>? metadata,
|
||||
_i2.Level? level,
|
||||
StackTrace? stackTrace,
|
||||
Object? extra,
|
||||
}) => super.noSuchMethod(
|
||||
Invocation.method(
|
||||
#log,
|
||||
[message],
|
||||
{
|
||||
#metadata: metadata,
|
||||
#level: level,
|
||||
#stackTrace: stackTrace,
|
||||
#extra: extra,
|
||||
},
|
||||
),
|
||||
returnValueForMissingStub: null,
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,140 @@
|
||||
import "package:arcane_framework/arcane_framework.dart";
|
||||
import "package:flutter/material.dart";
|
||||
import "package:flutter_test/flutter_test.dart";
|
||||
|
||||
void main() {
|
||||
group("ArcaneReactiveTheme", () {
|
||||
late ArcaneReactiveTheme theme;
|
||||
|
||||
setUp(() {
|
||||
theme = ArcaneReactiveTheme.I;
|
||||
});
|
||||
|
||||
test("singleton instance is consistent", () {
|
||||
expect(identical(ArcaneReactiveTheme.I, theme), true);
|
||||
});
|
||||
|
||||
group("theme mode", () {
|
||||
test("initial mode is light", () {
|
||||
expect(theme.currentMode, equals(ThemeMode.light));
|
||||
});
|
||||
|
||||
test("switchTheme toggles between light and dark", () {
|
||||
expect(theme.currentMode, equals(ThemeMode.light));
|
||||
theme.switchTheme();
|
||||
expect(theme.currentMode, equals(ThemeMode.dark));
|
||||
theme.switchTheme();
|
||||
expect(theme.currentMode, equals(ThemeMode.light));
|
||||
});
|
||||
|
||||
test("switching theme notifies listeners", () {
|
||||
var notified = false;
|
||||
theme.addListener(() => notified = true);
|
||||
theme.switchTheme();
|
||||
expect(notified, true);
|
||||
});
|
||||
});
|
||||
|
||||
group("theme customization", () {
|
||||
test("setDarkTheme updates dark theme", () {
|
||||
final customTheme = ThemeData.dark().copyWith(
|
||||
primaryColor: Colors.purple,
|
||||
);
|
||||
theme.setDarkTheme(customTheme);
|
||||
expect(theme.dark.primaryColor, equals(Colors.purple));
|
||||
});
|
||||
|
||||
test("setLightTheme updates light theme", () {
|
||||
final customTheme = ThemeData.light().copyWith(
|
||||
primaryColor: Colors.orange,
|
||||
);
|
||||
theme.setLightTheme(customTheme);
|
||||
expect(theme.light.primaryColor, equals(Colors.orange));
|
||||
});
|
||||
|
||||
test("theme updates notify listeners", () {
|
||||
bool darkNotified = false;
|
||||
bool lightNotified = false;
|
||||
ThemeMode currentTheme = ThemeMode.system;
|
||||
|
||||
theme.darkTheme.addListener(() {
|
||||
darkNotified = true;
|
||||
});
|
||||
|
||||
theme.lightTheme.addListener(() {
|
||||
lightNotified = true;
|
||||
});
|
||||
|
||||
theme.addListener(() {
|
||||
currentTheme = theme.currentMode;
|
||||
});
|
||||
|
||||
expect(currentTheme, ThemeMode.system);
|
||||
|
||||
theme.setDarkTheme(ThemeData.dark());
|
||||
theme.setLightTheme(ThemeData.light());
|
||||
|
||||
expect(darkNotified, true);
|
||||
expect(lightNotified, true);
|
||||
|
||||
theme.switchTheme();
|
||||
expect(currentTheme, ThemeMode.light);
|
||||
|
||||
theme.switchTheme();
|
||||
expect(currentTheme, ThemeMode.dark);
|
||||
});
|
||||
});
|
||||
|
||||
group("system theme following", () {
|
||||
setUp(() {
|
||||
Arcane.theme.reset();
|
||||
});
|
||||
|
||||
testWidgets("followSystemTheme updates theme based on context brightness",
|
||||
(WidgetTester tester) async {
|
||||
// Create widgets with different brightness contexts
|
||||
await tester.pumpWidget(
|
||||
const MediaQuery(
|
||||
data: MediaQueryData(platformBrightness: Brightness.light),
|
||||
child: SizedBox(),
|
||||
),
|
||||
);
|
||||
|
||||
final BuildContext lightContext = tester.element(find.byType(SizedBox));
|
||||
theme.followSystemTheme(lightContext);
|
||||
|
||||
expect(theme.currentMode, equals(ThemeMode.light));
|
||||
|
||||
await tester.pumpWidget(
|
||||
const MediaQuery(
|
||||
data: MediaQueryData(platformBrightness: Brightness.dark),
|
||||
child: SizedBox(),
|
||||
),
|
||||
);
|
||||
|
||||
final BuildContext darkContext = tester.element(find.byType(SizedBox));
|
||||
|
||||
theme.followSystemTheme(darkContext);
|
||||
expect(theme.currentMode, equals(ThemeMode.dark));
|
||||
});
|
||||
|
||||
testWidgets("followSystemTheme only switches when needed",
|
||||
(WidgetTester tester) async {
|
||||
await tester.pumpWidget(
|
||||
const MediaQuery(
|
||||
data: MediaQueryData(platformBrightness: Brightness.light),
|
||||
child: SizedBox(),
|
||||
),
|
||||
);
|
||||
final BuildContext lightContext = tester.element(find.byType(SizedBox));
|
||||
|
||||
int switchCount = 0;
|
||||
theme.addListener(() => switchCount++);
|
||||
|
||||
// Already light, shouldn't switch
|
||||
theme.followSystemTheme(lightContext);
|
||||
expect(switchCount, equals(0));
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user