Initial commit of the Flutter Cursor Generator project, including the core generator tool, project brief schema, example project setup, and CI configuration. Added README documentation outlining repository structure, quick start guide, and detailed descriptions of features and architecture pillars.

This commit is contained in:
2026-05-12 22:29:55 +05:30
commit 6dfb9a8aa5
72 changed files with 4542 additions and 0 deletions
@@ -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
@@ -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