diff --git a/packages/disco/lib/src/models/overrides/provider_argument_override.dart b/packages/disco/lib/src/models/overrides/provider_argument_override.dart index 77dfbca..7300e58 100644 --- a/packages/disco/lib/src/models/overrides/provider_argument_override.dart +++ b/packages/disco/lib/src/models/overrides/provider_argument_override.dart @@ -1,28 +1,67 @@ part of '../../disco_internal.dart'; +sealed class _ArgProviderOverrideType {} + +// TODO (manuel-plavsic): I would get rid of this (and remove sealed class) +final class _ArgProviderOverrideWithValue + extends _ArgProviderOverrideType { + _ArgProviderOverrideWithValue._(this.argument, this.value, this.debugName); + + final A argument; + + final T value; + + /// {@macro Provider.debugName} + final String? debugName; +} + +final class _ArgProviderOverrideWithProvider + extends _ArgProviderOverrideType { + _ArgProviderOverrideWithProvider._(this.mockProvider); + final ArgProvider mockProvider; +} + /// Override that, if inserted into the widget tree, takes precedence over -/// [_argProvider]. +/// [_originalArgProvider]. @immutable class ArgProviderOverride extends Override { - ArgProviderOverride._(this._argProvider, T value, {this.debugName}) - : _value = value, + ArgProviderOverride._withValue( + this._originalArgProvider, + A arg, + T value, + String? debugName, + ) : _overrideType = _ArgProviderOverrideWithValue._( + arg, + value, + debugName, + ), + super._(); + + ArgProviderOverride._withArgProvider( + this._originalArgProvider, + ArgProvider mockProvider, + ) : _overrideType = _ArgProviderOverrideWithProvider._(mockProvider), super._(); /// The reference of the argument provider to override. - final ArgProvider _argProvider; + final ArgProvider _originalArgProvider; /// The overridden value. - final T _value; + final _ArgProviderOverrideType _overrideType; // Utils leveraged by ProviderScope ----------------------------------------- /// Given an argument, creates a [Provider] with that argument. /// This method is used internally by [ProviderScope]. - Provider _generateIntermediateProvider() => Provider( - (_) => _value, - lazy: false, - ); - - /// {@macro Provider.debugName} - final String? debugName; + Provider _generateIntermediateProvider() => switch (_overrideType) { + // TODO (manuel-plavsic): I would get rid of this + _ArgProviderOverrideWithValue(:final T value) => Provider( + (_) => value, + lazy: false, + ), + // TODO (manuel-plavsic): I would keep only this part below (and remove pattern matching) + // TODO (manuel-plavsic): this requires a major change in ProviderScope: a new layer of intermediate providers should be added (this time of type ArgProvider, not just Provider) + _ArgProviderOverrideWithProvider(:final ArgProvider mockProvider) => + mockProvider, + }; } diff --git a/packages/disco/lib/src/models/overrides/provider_override.dart b/packages/disco/lib/src/models/overrides/provider_override.dart index 166f908..c05ee0d 100644 --- a/packages/disco/lib/src/models/overrides/provider_override.dart +++ b/packages/disco/lib/src/models/overrides/provider_override.dart @@ -1,26 +1,57 @@ part of '../../disco_internal.dart'; +sealed class _ProviderOverrideType {} + +// TODO (manuel-plavsic): I would get rid of this (and remove sealed class) +final class _ProviderOverrideWithValue + extends _ProviderOverrideType { + _ProviderOverrideWithValue._(this.value, this.debugName); + final T value; + + /// {@macro Provider.debugName} + final String? debugName; +} + +final class _ProviderOverrideWithProvider + extends _ProviderOverrideType { + _ProviderOverrideWithProvider._(this.mockProvider); + final Provider mockProvider; +} + /// Override that, if inserted into the widget tree, takes precedence over -/// [_provider]. +/// [_originalProvider]. @immutable class ProviderOverride extends Override { - ProviderOverride._( - this._provider, + ProviderOverride._withValue( + this._originalProvider, T value, - ) : _value = value, + String? debugName, + ) : _overrideType = _ProviderOverrideWithValue._(value, debugName), + super._(); + + ProviderOverride._withProvider( + this._originalProvider, + Provider mockProvider, + ) : _overrideType = _ProviderOverrideWithProvider._(mockProvider), super._(); /// The reference of the provider to override. - final Provider _provider; + final Provider _originalProvider; - final T _value; + final _ProviderOverrideType _overrideType; // Utils leveraged by ProviderScope ----------------------------------------- /// Creates a [Provider]. /// This method is used internally by [ProviderScope]. - Provider _generateIntermediateProvider() => Provider( - (_) => _value, - lazy: false, - ); + Provider _generateIntermediateProvider() => switch (_overrideType) { + // TODO (manuel-plavsic): I would get rid of this + _ProviderOverrideWithValue(:final T value) => Provider( + (_) => value, + lazy: false, + ), + // TODO (manuel-plavsic): I would keep only this (and remove pattern matching) + _ProviderOverrideWithProvider(:final Provider mockProvider) => + mockProvider, + }; } diff --git a/packages/disco/lib/src/models/providers/provider.dart b/packages/disco/lib/src/models/providers/provider.dart index c1ffeaa..6af6b76 100644 --- a/packages/disco/lib/src/models/providers/provider.dart +++ b/packages/disco/lib/src/models/providers/provider.dart @@ -81,8 +81,17 @@ class Provider extends InstantiableProvider { /// [ProviderScopeOverride]. /// {@endtemplate} @visibleForTesting - ProviderOverride overrideWithValue(T value) => - ProviderOverride._(this, value); + ProviderOverride overrideWithValue(T value, {String? debugName}) => + ProviderOverride._withValue(this, value, debugName); + + /// {@template Provider.overrideWithValue} + /// It creates an override of this provider to be passed to + /// [ProviderScopeOverride]. + /// {@endtemplate} + @visibleForTesting + ProviderOverride overrideWithProvider( + Provider override, + ) => ProviderOverride._withProvider(this, override); // DI methods --------------------------------------------------------------- diff --git a/packages/disco/lib/src/models/providers/provider_argument.dart b/packages/disco/lib/src/models/providers/provider_argument.dart index c64d52e..4e54d72 100644 --- a/packages/disco/lib/src/models/providers/provider_argument.dart +++ b/packages/disco/lib/src/models/providers/provider_argument.dart @@ -38,8 +38,16 @@ class ArgProvider { /// {@macro Provider.overrideWithValue} @visibleForTesting - ArgProviderOverride overrideWithValue(T value) => - ArgProviderOverride._(this, value, debugName: debugName); + ArgProviderOverride overrideWithValue( + A arg, + T value, { + String? debugName, + }) => ArgProviderOverride._withValue(this, arg, value, debugName); + + @visibleForTesting + ArgProviderOverride overrideWithProvider( + ArgProvider override, + ) => ArgProviderOverride._withArgProvider(this, override); // --- // DI methods diff --git a/packages/disco/lib/src/widgets/provider_scope.dart b/packages/disco/lib/src/widgets/provider_scope.dart index 287409d..f219a4d 100644 --- a/packages/disco/lib/src/widgets/provider_scope.dart +++ b/packages/disco/lib/src/widgets/provider_scope.dart @@ -355,7 +355,7 @@ class ProviderScopeState extends State { // check if there are multiple providers of the same type final ids = []; for (final override in providerOverrides) { - final id = override._provider; // the instance of the provider + final id = override._originalProvider; // the instance of the provider if (ids.contains(id)) { throw MultipleProviderOverrideOfSameInstance(); } @@ -367,7 +367,7 @@ class ProviderScopeState extends State { ); for (final override in providerOverrides) { - final id = override._provider; + final id = override._originalProvider; allProvidersInScope[id] = override._generateIntermediateProvider(); @@ -388,7 +388,8 @@ class ProviderScopeState extends State { // check if there are multiple providers of the same type final ids = []; for (final override in argProviderOverrides) { - final id = override._argProvider; // the instance of the provider + final id = + override._originalArgProvider; // the instance of the provider if (ids.contains(id)) { throw MultipleProviderOverrideOfSameInstance(); } @@ -400,7 +401,7 @@ class ProviderScopeState extends State { ); for (final override in argProviderOverrides) { - final id = override._argProvider; + final id = override._originalArgProvider; allArgProvidersInScope[id] = override._generateIntermediateProvider(); diff --git a/packages/disco/test/disco_test.dart b/packages/disco/test/disco_test.dart index 5b90b53..631c0b7 100644 --- a/packages/disco/test/disco_test.dart +++ b/packages/disco/test/disco_test.dart @@ -698,6 +698,36 @@ void main() { expect(textFinder('number: 1'), findsOneWidget); }); + testWidgets('''ProviderScopeOverride should override providers''', ( + tester, + ) async { + final numberProvider = Provider((_) => 0); + final mockNumberProvider = Provider((_) => 9); + await tester.pumpWidget( + ProviderScopeOverride( + overrides: [ + numberProvider.overrideWithProvider(mockNumberProvider), + ], + child: MaterialApp( + home: ProviderScope( + providers: [ + numberProvider, + ], + child: Builder( + builder: (context) { + final number = numberProvider.of(context); + return Text(number.toString()); + }, + ), + ), + ), + ), + ); + expect(find.text('9'), findsOneWidget); + }); + + // TODO(manuel): add other test (check also that debug is different, etc.) + testWidgets('''ProviderScopeOverride should override providers''', ( tester, ) async { @@ -729,10 +759,12 @@ void main() { tester, ) async { final numberProvider = Provider.withArgument((_, int arg) => arg); + final mockNumberProvider = Provider.withArgument((_, int arg) => 4); await tester.pumpWidget( ProviderScopeOverride( overrides: [ - numberProvider.overrideWithValue(16), + // numberProvider.overrideWithValue(1, 16), + numberProvider.overrideWithProvider(mockNumberProvider), ], child: MaterialApp( home: ProviderScope( @@ -864,8 +896,8 @@ void main() { home: Scaffold( body: ProviderScopeOverride( overrides: [ - numberProvider.overrideWithValue(1), - numberProvider.overrideWithValue(2), + numberProvider.overrideWithValue(0, 1), + numberProvider.overrideWithValue(1, 2), ], child: Builder( builder: (context) {