بنقرة واحدة
test-loading-state
Test isWaiting() behavior using Completers to verify loading states work correctly
التثبيت باستخدام Codex أو Claude انسخ هذا Prompt والصقه في Codex أو Claude أو مساعد آخر ليراجع صفحة Skill ويثبّتها لك.
القائمة
Test isWaiting() behavior using Completers to verify loading states work correctly
التثبيت باستخدام Codex أو Claude انسخ هذا Prompt والصقه في Codex أو Claude أو مساعد آخر ليراجع صفحة Skill ويثبّتها لك.
استنادا إلى تصنيف SOC المهني
Add custom error handling with catchError to a mix() call for logging, error conversion, or suppression
Add internet connectivity checking before executing a Cubit method with optional retry
Add debounce to delay method execution until after a period of inactivity for search or validation
Add EffectQueue to state for triggering multiple sequential one-time UI effects
Add Effect fields to state class for one-time UI notifications like snackbars, navigation, or form clearing
Add error state handling to widgets using context.isFailed() and context.getException()
| name | test-loading-state |
| description | Test isWaiting() behavior using Completers to verify loading states work correctly |
This skill tests isWaiting() behavior in Cubit methods.
Tests that:
isWaiting() returns true while operation is in progressisWaiting() returns false after operation completesUse Completer to control when async operations complete:
import 'dart:async';
import 'package:bloc_superpowers/bloc_superpowers.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:mocktail/mocktail.dart';
void main() {
late UserCubit cubit;
late MockApi mockApi;
setUp(() {
Superpowers.clear();
mockApi = MockApi();
cubit = UserCubit(api: mockApi);
});
tearDown(() => cubit.close());
test('isWaiting is true during operation', () async {
// Create a completer to control when the future completes
final completer = Completer<User>();
// Mock returns a future we control
when(() => mockApi.getUser()).thenAnswer((_) => completer.future);
// Start the operation
cubit.loadUser();
// Allow async to process
await Future.delayed(Duration.zero);
// Verify loading state is true
expect(Superpowers.isWaiting(UserCubit), isTrue);
// Complete the operation
completer.complete(User(name: 'John'));
// Allow async to process
await Future.delayed(Duration.zero);
// Verify loading state is false
expect(Superpowers.isWaiting(UserCubit), isFalse);
});
}
test('loading state pattern', () async {
final completer = Completer<Data>();
when(() => mockApi.getData()).thenAnswer((_) => completer.future);
// 1. Start operation
cubit.loadData();
await Future.delayed(Duration.zero);
// 2. Assert loading is true
expect(Superpowers.isWaiting(key), isTrue);
// 3. Complete operation
completer.complete(data);
await Future.delayed(Duration.zero);
// 4. Assert loading is false
expect(Superpowers.isWaiting(key), isFalse);
});
// Cubit uses: key: this (becomes UserCubit)
test('loading with type key', () async {
final completer = Completer<User>();
when(() => mockApi.getUser()).thenAnswer((_) => completer.future);
cubit.loadUser();
await Future.delayed(Duration.zero);
expect(Superpowers.isWaiting(UserCubit), isTrue);
completer.complete(User(name: 'John'));
await Future.delayed(Duration.zero);
expect(Superpowers.isWaiting(UserCubit), isFalse);
});
// Cubit uses: key: UserAction.load
test('loading with enum key', () async {
final completer = Completer<User>();
when(() => mockApi.getUser()).thenAnswer((_) => completer.future);
cubit.loadUser();
await Future.delayed(Duration.zero);
expect(Superpowers.isWaiting(UserAction.load), isTrue);
completer.complete(User(name: 'John'));
await Future.delayed(Duration.zero);
expect(Superpowers.isWaiting(UserAction.load), isFalse);
});
// Cubit uses: key: (DeleteItem, itemId)
test('loading with composite key', () async {
final completer = Completer<void>();
when(() => mockApi.deleteItem('123')).thenAnswer((_) => completer.future);
cubit.deleteItem('123');
await Future.delayed(Duration.zero);
// Only this specific item is loading
expect(Superpowers.isWaiting((DeleteItem, '123')), isTrue);
expect(Superpowers.isWaiting((DeleteItem, '456')), isFalse);
completer.complete();
await Future.delayed(Duration.zero);
expect(Superpowers.isWaiting((DeleteItem, '123')), isFalse);
});
test('multiple operations have independent loading states', () async {
final loadCompleter = Completer<User>();
final saveCompleter = Completer<void>();
when(() => mockApi.getUser()).thenAnswer((_) => loadCompleter.future);
when(() => mockApi.saveUser(any())).thenAnswer((_) => saveCompleter.future);
// Start both operations
cubit.loadUser();
cubit.saveUser(User(name: 'John'));
await Future.delayed(Duration.zero);
// Both are loading
expect(Superpowers.isWaiting(UserAction.load), isTrue);
expect(Superpowers.isWaiting(UserAction.save), isTrue);
// Complete load
loadCompleter.complete(User(name: 'John'));
await Future.delayed(Duration.zero);
// Load done, save still loading
expect(Superpowers.isWaiting(UserAction.load), isFalse);
expect(Superpowers.isWaiting(UserAction.save), isTrue);
// Complete save
saveCompleter.complete();
await Future.delayed(Duration.zero);
// Both done
expect(Superpowers.isWaiting(UserAction.load), isFalse);
expect(Superpowers.isWaiting(UserAction.save), isFalse);
});
test('loading state clears on error', () async {
when(() => mockApi.getUser()).thenThrow(Exception('Error'));
cubit.loadUser();
await Future.delayed(Duration.zero);
// Loading should be false (operation completed, with error)
expect(Superpowers.isWaiting(UserCubit), isFalse);
// Error state should be true
expect(Superpowers.isFailed(UserCubit), isTrue);
});
import 'dart:async';
import 'package:bloc_superpowers/bloc_superpowers.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:mocktail/mocktail.dart';
class MockApi extends Mock implements Api {}
void main() {
late ProductCubit cubit;
late MockApi mockApi;
setUp(() {
Superpowers.clear();
mockApi = MockApi();
cubit = ProductCubit(api: mockApi);
});
tearDown(() => cubit.close());
group('loading state', () {
test('isWaiting is true while loading products', () async {
final completer = Completer<List<Product>>();
when(() => mockApi.getProducts()).thenAnswer((_) => completer.future);
cubit.loadProducts();
await Future.delayed(Duration.zero);
expect(Superpowers.isWaiting(ProductCubit), isTrue);
completer.complete([Product(id: '1', name: 'Widget')]);
await Future.delayed(Duration.zero);
expect(Superpowers.isWaiting(ProductCubit), isFalse);
});
test('per-item loading for delete', () async {
final completer = Completer<void>();
when(() => mockApi.deleteProduct('1')).thenAnswer((_) => completer.future);
cubit.deleteProduct('1');
await Future.delayed(Duration.zero);
expect(Superpowers.isWaiting((DeleteProduct, '1')), isTrue);
expect(Superpowers.isWaiting((DeleteProduct, '2')), isFalse);
completer.complete();
await Future.delayed(Duration.zero);
expect(Superpowers.isWaiting((DeleteProduct, '1')), isFalse);
});
test('loading clears on error', () async {
when(() => mockApi.getProducts()).thenThrow(Exception('Error'));
cubit.loadProducts();
await Future.delayed(Duration.zero);
expect(Superpowers.isWaiting(ProductCubit), isFalse);
expect(Superpowers.isFailed(ProductCubit), isTrue);
});
});
}
Completer to control async timingFuture.delayed(Duration.zero) to let async processSuperpowers.isWaiting(key) static method in testsmix() callSuperpowers.clear() in setUp