chore: update README and CLI usage for cursor_gen, version bump to 1.0.1
- Changed CLI usage instructions from `dart run cursor_gen` to `cursor_gen` for global activation. - Updated project-brief.yaml example and README to reflect new command usage. - Added app_context section in project-brief.yaml for theme variants and RBAC roles. - Fixed bundled template resolution for local and global installs to prevent 'Template not found' errors. - Version bump to 1.0.1 with corresponding updates in CHANGELOG and pubspec.yaml.
This commit is contained in:
@@ -0,0 +1,60 @@
|
||||
---
|
||||
description: "BLoC testing conventions for {{PROJECT_NAME}}"
|
||||
alwaysApply: false
|
||||
---
|
||||
|
||||
# BLoC Testing Standards — {{PROJECT_NAME}}
|
||||
|
||||
## Test pattern (bloc_test)
|
||||
```dart
|
||||
// {{TEST_PATTERN}}
|
||||
|
||||
void main() {
|
||||
late AuthBloc authBloc;
|
||||
late MockAuthRepository mockRepo;
|
||||
|
||||
setUp(() {
|
||||
mockRepo = MockAuthRepository();
|
||||
authBloc = AuthBloc(repository: mockRepo);
|
||||
});
|
||||
|
||||
tearDown(() => authBloc.close());
|
||||
|
||||
group('AuthBloc', () {
|
||||
blocTest<AuthBloc, AuthState>(
|
||||
'emits [Loading, Authenticated] when login succeeds',
|
||||
build: () {
|
||||
when(() => mockRepo.login(any(), any()))
|
||||
.thenAnswer((_) async => const Right(User(id: '1', email: 'test@test.com')));
|
||||
return authBloc;
|
||||
},
|
||||
act: (bloc) => bloc.add(const AuthLoginRequested(email: 'test@test.com', password: 'pass')),
|
||||
expect: () => [
|
||||
const AuthLoading(),
|
||||
isA<AuthAuthenticated>(),
|
||||
],
|
||||
);
|
||||
|
||||
blocTest<AuthBloc, AuthState>(
|
||||
'emits [Loading, Failure] when login fails',
|
||||
build: () {
|
||||
when(() => mockRepo.login(any(), any()))
|
||||
.thenAnswer((_) async => const Left(AuthError('Invalid credentials')));
|
||||
return authBloc;
|
||||
},
|
||||
act: (bloc) => bloc.add(const AuthLoginRequested(email: 'bad', password: 'bad')),
|
||||
expect: () => [
|
||||
const AuthLoading(),
|
||||
const AuthFailure('Invalid credentials'),
|
||||
],
|
||||
);
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
## Rules
|
||||
- Use `mocktail` for mocking — never `mockito`
|
||||
- Every BLoC test file: `test/features/[feature]/[feature]_bloc_test.dart`
|
||||
- Coverage requirement: all state transitions must be tested
|
||||
- Use `Given/When/Then` naming in test descriptions
|
||||
- Test error paths as thoroughly as success paths
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
---
|
||||
description: "Patrol E2E testing conventions for {{PROJECT_NAME}}"
|
||||
alwaysApply: false
|
||||
---
|
||||
|
||||
# Patrol E2E Testing — {{PROJECT_NAME}}
|
||||
|
||||
## Test structure
|
||||
```dart
|
||||
void main() {
|
||||
patrolTest('User can complete checkout flow', ($) async {
|
||||
await $.pumpWidgetAndSettle(const App());
|
||||
|
||||
// Login
|
||||
await $(LoginScreen).waitUntilVisible();
|
||||
await $(#emailField).enterText('test@example.com');
|
||||
await $(#passwordField).enterText('password123');
|
||||
await $('Sign In').tap();
|
||||
|
||||
// Add to cart
|
||||
await $(ProductCard).at(0).tap();
|
||||
await $('Add to Cart').tap();
|
||||
|
||||
// Checkout
|
||||
await $('Cart').tap();
|
||||
await $('Checkout').tap();
|
||||
await $(CheckoutSuccessScreen).waitUntilVisible();
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
## Rules
|
||||
- E2E tests in `integration_test/` — separate from unit tests
|
||||
- Use `patrolTest` not `testWidgets` for E2E scenarios
|
||||
- Tag tests with `@Tags(['slow'])` so CI can skip on PRs
|
||||
- Run against real emulators/simulators, not mocked environments
|
||||
- Test on minimum supported OS version for each platform
|
||||
@@ -0,0 +1,40 @@
|
||||
---
|
||||
description: "GetX testing conventions for {{PROJECT_NAME}}"
|
||||
alwaysApply: false
|
||||
---
|
||||
|
||||
# GetX Testing Standards — {{PROJECT_NAME}}
|
||||
|
||||
## Test pattern
|
||||
```dart
|
||||
void main() {
|
||||
late ProductsController controller;
|
||||
late MockProductRepository mockRepo;
|
||||
|
||||
setUp(() {
|
||||
mockRepo = MockProductRepository();
|
||||
Get.testMode = true;
|
||||
controller = Get.put(ProductsController(mockRepo));
|
||||
});
|
||||
|
||||
tearDown(() => Get.deleteAll());
|
||||
|
||||
test('loads products on init', () async {
|
||||
when(() => mockRepo.getProducts()).thenAnswer((_) async => [fakeProduct]);
|
||||
await controller.fetchProducts();
|
||||
expect(controller.products, [fakeProduct]);
|
||||
expect(controller.isLoading.value, false);
|
||||
});
|
||||
|
||||
testWidgets('ProductsView shows shimmer while loading', (tester) async {
|
||||
controller.isLoading.value = true;
|
||||
await tester.pumpWidget(GetMaterialApp(home: ProductsView()));
|
||||
expect(find.byType(ProductShimmer), findsOneWidget);
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
## Rules
|
||||
- Use `Get.testMode = true` in setUp
|
||||
- Always call `Get.deleteAll()` in tearDown
|
||||
- Wrap widget tests in `GetMaterialApp`, not `MaterialApp`
|
||||
@@ -0,0 +1,56 @@
|
||||
---
|
||||
description: "Riverpod testing conventions for {{PROJECT_NAME}}"
|
||||
alwaysApply: false
|
||||
---
|
||||
|
||||
# Riverpod Testing Standards — {{PROJECT_NAME}}
|
||||
|
||||
## Test pattern
|
||||
```dart
|
||||
// {{TEST_PATTERN}}
|
||||
|
||||
void main() {
|
||||
late ProviderContainer container;
|
||||
late MockProductRepository mockRepo;
|
||||
|
||||
setUp(() {
|
||||
mockRepo = MockProductRepository();
|
||||
container = ProviderContainer(overrides: [
|
||||
productRepositoryProvider.overrideWithValue(mockRepo),
|
||||
]);
|
||||
});
|
||||
|
||||
tearDown(() => container.dispose()); // ALWAYS dispose
|
||||
|
||||
test('ProductsNotifier loads products successfully', () async {
|
||||
when(() => mockRepo.getProducts()).thenAnswer((_) async => [fakeProduct]);
|
||||
|
||||
final notifier = container.read(productsProvider.notifier);
|
||||
await notifier.loadProducts();
|
||||
|
||||
final state = container.read(productsProvider);
|
||||
expect(state, isA<AsyncData<List<Product>>>());
|
||||
expect(state.value, [fakeProduct]);
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
## Widget tests with Riverpod
|
||||
```dart
|
||||
testWidgets('ProductScreen shows shimmer while loading', (tester) async {
|
||||
await tester.pumpWidget(
|
||||
ProviderScope(
|
||||
overrides: [
|
||||
productsProvider.overrideWith((ref) => const AsyncLoading()),
|
||||
],
|
||||
child: const MaterialApp(home: ProductScreen()),
|
||||
),
|
||||
);
|
||||
expect(find.byType(ProductShimmer), findsOneWidget);
|
||||
});
|
||||
```
|
||||
|
||||
## Rules
|
||||
- **Never** use a real `ProviderScope` in unit tests — always use `ProviderContainer` with overrides
|
||||
- `addTearDown(container.dispose)` in every test that creates a container
|
||||
- Test all three `AsyncValue` states: loading, data, error
|
||||
Reference in New Issue
Block a user