Skip to content

Commit 899c6ef

Browse files
committed
chore: project configuration updates
- Update melos and pubspec configurations - Add project documentation and guidelines - Update renovate and prettier configurations
1 parent 8201682 commit 899c6ef

File tree

6 files changed

+217
-10
lines changed

6 files changed

+217
-10
lines changed

CLAUDE.md

Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
# CLAUDE.md
2+
3+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4+
5+
## Project Overview
6+
7+
Flutter Hooks Test is a testing utility library for Flutter hooks, inspired by React's `react-hooks-testing-library`. It provides a simple API to test custom hooks in isolation.
8+
9+
## Development Commands
10+
11+
### Essential Commands
12+
13+
```bash
14+
# Install dependencies
15+
melos get
16+
bun install
17+
18+
# Run tests
19+
melos test
20+
21+
# Run code analysis
22+
melos analyze
23+
24+
# Format code (Dart + Prettier)
25+
melos format
26+
27+
# Run all checks (analyze + format + test)
28+
melos analyze && melos format && melos test
29+
30+
# Run a single test file
31+
flutter test test/flutter_hooks_test_test.dart
32+
33+
# Run tests with coverage
34+
flutter test --coverage
35+
```
36+
37+
### Additional Commands
38+
39+
```bash
40+
# Upgrade dependencies
41+
melos upgrade
42+
43+
# Clean build artifacts
44+
melos clean
45+
46+
# Format with Prettier only
47+
bun run format
48+
49+
# Setup git hooks
50+
bun run prepare
51+
```
52+
53+
## Architecture and Code Structure
54+
55+
### Core API
56+
57+
The library exports a single file `lib/flutter_hooks_test.dart` containing:
58+
59+
1. **`buildHook<T, P>`** - Main function to test hooks
60+
61+
- Generic `T`: Return type of the hook
62+
- Generic `P`: Props type for parameterized hooks
63+
- Returns `_HookTestingAction<T>` with methods:
64+
- `current`: Access current hook value
65+
- `rebuild([props])`: Trigger rebuild with optional new props
66+
- `unmount()`: Unmount the hook
67+
68+
2. **`act`** - Wraps state changes to ensure proper Flutter test lifecycle
69+
- Similar to React's `act` function
70+
- Required when changing hook state
71+
72+
### Testing Pattern
73+
74+
```dart
75+
// Basic hook test structure
76+
final result = await buildHook((_) => useMyHook());
77+
await act(() => result.current.doSomething());
78+
expect(result.current.value, expectedValue);
79+
80+
// With wrapper widget
81+
final result = await buildHook(
82+
(_) => useMyHook(),
83+
wrapper: (child) => Provider(child: child),
84+
);
85+
```
86+
87+
### Internal Implementation
88+
89+
- Uses `TestWidgetsFlutterBinding` for test environment
90+
- Creates a minimal widget tree with `HookBuilder`
91+
- Manages completer-based async operations for mount/unmount
92+
- Preserves hook state across rebuilds using keys
93+
94+
## Testing Approach
95+
96+
- All tests go in `test/` directory
97+
- Example hooks in `test/hooks/` demonstrate testable patterns
98+
- Use `testWidgets` for widget-based tests
99+
- Use Mockito for mocking dependencies
100+
101+
## Code Quality
102+
103+
- Flutter lints are enforced via `analysis_options.yaml`
104+
- Example directory is excluded from analysis
105+
- Pre-commit hooks format code automatically
106+
- CI runs on Ubuntu with asdf version management
107+
108+
## Important Conventions
109+
110+
1. **Type Safety**: Always specify generic types when using `buildHook`
111+
2. **Act Wrapper**: Always wrap state changes in `act()`
112+
3. **Async Handling**: Most operations return Futures - use `await`
113+
4. **Testing**: Test both happy paths and edge cases (mount/unmount/rebuild)
114+
115+
## Version Requirements
116+
117+
- Dart SDK: `>=2.17.0 <4.0.0`
118+
- Flutter: `>=3.20.0`
119+
- Uses Flutter hooks `^0.21.2`
120+
121+
## Release Process
122+
123+
Releases are fully automated via GitHub Actions:
124+
125+
### Creating a Release
126+
127+
1. **Update version**: Update version in `pubspec.yaml`
128+
2. **Update changelog**: Run `git cliff --unreleased --tag v1.0.1 --output CHANGELOG.md`
129+
3. **Commit changes**: `git commit -am "chore(release): prepare for v1.0.1"`
130+
4. **Create tag**: `git tag v1.0.1`
131+
5. **Push**: `git push origin main --tags`
132+
133+
### Automated Release Steps
134+
135+
When a tag matching `v[0-9]+.[0-9]+.[0-9]+*` is pushed:
136+
137+
1. **CI Validation**: All tests, formatting, and analysis must pass
138+
2. **Dry Run**: Package publication is tested
139+
3. **Release Notes**: Auto-generated using git-cliff from conventional commits
140+
4. **GitHub Release**: Created with generated release notes
141+
5. **pub.dev Publication**: Package is published automatically
142+
143+
### Commit Convention
144+
145+
Use [Conventional Commits](https://www.conventionalcommits.org/) for automatic release note generation:
146+
147+
- `feat:` - New features
148+
- `fix:` - Bug fixes
149+
- `docs:` - Documentation changes
150+
- `test:` - Test improvements
151+
- `refactor:` - Code refactoring
152+
- `perf:` - Performance improvements

example/lib/main.dart

Lines changed: 45 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ void main() {
3838
testWidgets('Using flutter_hooks_test', (tester) async {
3939
// After
4040
var buildCount = 0;
41-
final result = await buildHook((_) {
41+
final result = await buildHook(() {
4242
buildCount++;
4343
return useUpdate();
4444
});
@@ -50,14 +50,14 @@ void main() {
5050
});
5151

5252
testWidgets('should rebuild after act()', (tester) async {
53-
final result = await buildHook((_) => useCounter(5));
53+
final result = await buildHook(() => useCounter(5));
5454
await act(() => result.current.inc());
5555
expect(result.current.value, 6);
5656
});
5757

5858
testWidgets('should unmount after unmount()', (tester) async {
5959
final effect = MockEffect();
60-
final result = await buildHook((_) => useMount(() => effect()));
60+
final result = await buildHook(() => useMount(() => effect()));
6161
verify(effect()).called(1);
6262
verifyNoMoreInteractions(effect);
6363
await result.unmount();
@@ -67,19 +67,58 @@ void main() {
6767

6868
testWidgets('should rebuild after rebuild()', (tester) async {
6969
final effect = MockEffect();
70-
final result = await buildHook((_) => useMount(() => effect()));
70+
final result = await buildHook(() => useMount(() => effect()));
7171
await result.rebuild();
7272
verify(effect()).called(1);
7373
verifyNoMoreInteractions(effect);
7474
});
7575

7676
testWidgets('should rebuild after rebuild() with parameter', (tester) async {
77-
final result = await buildHook(
77+
final result = await buildHookWithProps(
7878
(count) => useLatest(count),
7979
initialProps: 123,
8080
);
8181
expect(result.current, 123);
82-
await result.rebuild(456);
82+
await result.rebuildWithProps(456);
8383
expect(result.current, 456);
8484
});
85+
86+
testWidgets('should track build history with new API', (tester) async {
87+
final result = await buildHook(() => useCounter(0));
88+
89+
// Debug information
90+
result.debug();
91+
92+
// Initial build
93+
expect(result.buildCount, 1);
94+
expect(result.all.length, 1);
95+
expect(result.all.first.value, 0);
96+
97+
// After increment
98+
await act(() => result.current.inc());
99+
expect(result.buildCount, 2);
100+
expect(result.all.length, 2);
101+
expect(result.all.last.value, 1);
102+
});
103+
104+
testWidgets('should demonstrate waitFor utilities', (tester) async {
105+
final result = await buildHook(() => useCounter(0));
106+
107+
// Wait for initial condition
108+
await waitFor(() => result.current.value == 0);
109+
110+
// Increment and wait for change
111+
await act(() => result.current.inc());
112+
await waitFor(() => result.current.value == 1);
113+
114+
// Use extension method to wait for specific condition
115+
await act(() => result.current.inc());
116+
await act(() => result.current.inc());
117+
118+
final finalValue = await result.waitForValueToMatch(
119+
(counter) => counter.value >= 3,
120+
);
121+
122+
expect(finalValue.value, 3);
123+
});
85124
}

melos.yaml

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,13 @@ scripts:
1515

1616
analyze: melos exec -- flutter analyze .
1717

18-
format: melos exec -- dart format .
18+
format:
19+
run: |
20+
melos exec -- dart format .
21+
bunx prettier --write .
1922
2023
test:
2124
run: melos exec -- flutter test
25+
26+
clean:
27+
run: melos exec -- flutter clean

prettier.config.js

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,4 @@ export default {
99
trailingComma: 'all',
1010
arrowParens: 'always',
1111
endOfLine: 'lf',
12-
xmlWhitespaceSensitivity: 'ignore',
13-
plugins: ['@prettier/plugin-xml', 'prettier-plugin-packagejson'],
1412
};

pubspec.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,5 +20,5 @@ dependencies:
2020

2121
dev_dependencies:
2222
flutter_lints: ^6.0.0
23-
mockito: ^5.4.6
23+
mockito: ^5.4.4
2424
melos: ^6.3.3

renovate.json

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{
2+
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
3+
"extends": ["config:base"],
4+
"schedule": ["before 10am on monday"],
5+
"timezone": "Asia/Tokyo",
6+
"packageRules": [
7+
{
8+
"matchPackageNames": ["flutter", "dart"],
9+
"enabled": false
10+
}
11+
]
12+
}

0 commit comments

Comments
 (0)