Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions .github/workflows/release-checks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,39 @@

- name: Publish dry run
run: dart pub publish --dry-run

integration-smoke:
name: Android Integration Smoke
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Enable KVM
run: |
echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' \
| sudo tee /etc/udev/rules.d/99-kvm4all.rules
sudo udevadm control --reload-rules
sudo udevadm trigger --name-match=kvm

- name: Install Flutter
run: |
curl -fsSL "https://storage.googleapis.com/flutter_infra_release/releases/stable/linux/flutter_linux_${FLUTTER_VERSION}-stable.tar.xz" -o "$RUNNER_TEMP/flutter.tar.xz"
tar -xf "$RUNNER_TEMP/flutter.tar.xz" -C "$RUNNER_TEMP"
echo "$RUNNER_TEMP/flutter/bin" >> $GITHUB_PATH

- name: Install package dependencies
run: flutter pub get

- name: Install example dependencies
working-directory: example
run: flutter pub get

- name: Run Android integration smoke test
uses: reactivecircus/android-emulator-runner@v2
with:
api-level: 34
arch: x86_64
profile: pixel_6
script: |
cd example
flutter test integration_test/app_smoke_test.dart -d emulator-5554
Comment on lines +48 to +81

Check warning

Code scanning / CodeQL

Workflow does not contain permissions Medium

Actions job or workflow does not limit the permissions of the GITHUB_TOKEN. Consider setting an explicit permissions block, using the following as a minimal starting point: {contents: read}

Copilot Autofix

AI 8 days ago

In general, the fix is to explicitly declare permissions for the workflow or individual jobs so the GITHUB_TOKEN is restricted to the least privileges required. Since both validate and integration-smoke jobs only need to read repository contents (for actions/checkout) and do not perform any write operations, the minimal appropriate permission is contents: read.

The best fix without changing existing functionality is to add a workflow-level permissions block near the top of .github/workflows/release-checks.yml, applying to all jobs that do not declare their own permissions. Add:

permissions:
  contents: read

right after the on: block (for example, after line 4–5). This preserves all current behavior while ensuring the GITHUB_TOKEN is restricted. No imports or additional methods are needed; this is purely a YAML configuration change.

Suggested changeset 1
.github/workflows/release-checks.yml

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/.github/workflows/release-checks.yml b/.github/workflows/release-checks.yml
--- a/.github/workflows/release-checks.yml
+++ b/.github/workflows/release-checks.yml
@@ -3,6 +3,9 @@
 on:
   workflow_call:
 
+permissions:
+  contents: read
+
 env:
   FLUTTER_VERSION: 3.27.4
 
EOF
@@ -3,6 +3,9 @@
on:
workflow_call:

permissions:
contents: read

env:
FLUTTER_VERSION: 3.27.4

Copilot is powered by AI and may make mistakes. Always verify output.
4 changes: 4 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ These instructions apply to the whole repository.
- Prefer these checks after code changes:
- `flutter analyze --no-fatal-infos`
- `flutter test`
- For runtime regression work, prefer the example smoke test:
- `cd example && flutter test integration_test/app_smoke_test.dart -d <DEVICE_ID>`
- If you only changed package Dart code, still consider analyzer impact on `example/`.
- If you change CI behavior, check `.github/workflows/validate.yml`.
- If you change publish behavior, check `.github/workflows/publish.yml`.
Expand All @@ -49,6 +51,8 @@ These instructions apply to the whole repository.
- Keep the example app buildable and analyzable.
- Prefer minimal demo-oriented fixes in `example/`; avoid production-grade abstractions there unless requested.
- If a dependency API deprecates, update the example usage when it is easy and low-risk.
- Android example smoke coverage exists in `example/integration_test/app_smoke_test.dart`; keep it stable when changing example navigation or consent flow.
- iOS runtime smoke coverage is not automated yet; do not claim parity unless it was explicitly tested.

## Analyzer Hygiene

Expand Down
100 changes: 84 additions & 16 deletions INSTRUCTIONS.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,34 +36,73 @@ cd example && flutter pub get && cd ..

## 2. Running Tests

**All unit tests:**
This repository currently has three test layers.

### 2.1 Package unit test suite

Run the full package-level Dart test suite:
```bash
flutter test
```

**Specific test file:**
Purpose:
- Validates the public Dart API of the plugin
- Verifies method-channel calls and serialized arguments
- Covers WebTrackingController behavior without requiring a real device
- Fastest regression signal; should be run after any Dart or API change

Main files in this suite:

| File | Description | Purpose |
|---|---|---|
| `test/plugin_mappintelligence_test.dart` | Method-channel focused unit tests for `PluginMappintelligence` public APIs | Ensures plugin methods call the correct native channel methods, pass the expected arguments, and keep version sync with `pubspec.yaml` |
| `test/web_tracking_controller_test.dart` | Unit tests for `WebTrackingController` using fake WebView platform classes | Protects the WebView/session-linking integration: callback forwarding, EverID injection, JavaScript message handling, and failure-path behavior |

Run an individual package test file:
```bash
flutter test test/web_tracking_controller_test.dart
flutter test test/plugin_mappintelligence_test.dart
flutter test test/web_tracking_controller_test.dart
```

**With verbose output:**
Useful targeted runs:
```bash
flutter test --name "version sync"
flutter test --reporter expanded
```

**What the tests cover:**
`version sync` specifically verifies that `pubspec.yaml` matches the hardcoded plugin version used in `lib/plugin_mappintelligence.dart`.

| File | Coverage |
|---|---|
| `test/plugin_mappintelligence_test.dart` | All public API methods, channel argument verification, version sync between `pubspec.yaml` and source code |
| `test/web_tracking_controller_test.dart` | NavigationDelegate forwarding, EverID injection ordering, onLoad success/failure behavior, JavaScript channel dispatch |
### 2.2 Example app integration smoke test

**Version sync test** — verifies `pubspec.yaml` version matches the hardcoded string in `lib/plugin_mappintelligence.dart`:
Run the example app smoke test on a device or emulator:
```bash
flutter test --name "version sync"
cd example
flutter test integration_test/app_smoke_test.dart -d <DEVICE_ID>
```

Main file in this suite:

| File | Description | Purpose |
|---|---|---|
| `example/integration_test/app_smoke_test.dart` | Runtime smoke test for the real example application | Verifies the app launches, consent flow works, navigation renders correctly, and the plugin is registered in a real app environment |

Purpose:
- Exercises the plugin from a real Flutter app instead of mocked unit-only code
- Catches runtime integration regressions that unit tests cannot see
- Intended as a smoke test, not full feature automation

Current scope:
- Android smoke coverage is automated in CI
- iOS runtime smoke coverage is not automated yet

### 2.3 Example widget test

The example app also contains:

| File | Description | Purpose |
|---|---|---|
| `example/test/widget_test.dart` | Legacy example widget test | Currently low-value and not the main regression signal; the integration smoke test is more important for runtime coverage |

---

## 3. Running the Example App
Expand All @@ -80,6 +119,16 @@ The example app demonstrates all tracking features:
- Exception tracking
- Form tracking

For runtime regression coverage, the example app also contains an Android smoke test:
```bash
cd example
flutter test integration_test/app_smoke_test.dart -d <DEVICE_ID>
```

Current scope:
- Android smoke coverage is automated in CI
- iOS runtime smoke coverage is not automated yet

---

## 4. Making a Release
Expand Down Expand Up @@ -114,19 +163,26 @@ flutter test --name "version sync"
flutter test
```

### Step 6 — Dry run
### Step 6 — Run example smoke test
```bash
cd example
flutter test integration_test/app_smoke_test.dart -d <DEVICE_ID>
cd ..
```

### Step 7 — Dry run
```bash
dart pub publish --dry-run
```

### Step 7 — Commit and push to `main`
### Step 8 — Commit and push to `main`
```bash
git add -A
git commit -m "chore: release <VERSION>"
git push origin main
```

The `validate.yml` GitHub Actions workflow runs automatically on every push to `main` — it verifies formatting, analysis, tests, and a dry run. Wait for it to pass before proceeding to publish.
The `validate.yml` GitHub Actions workflow runs on pull requests to `main`. It calls the shared release checks workflow, which verifies analysis, unit tests, Android integration smoke coverage, and a publish dry run. Wait for it to pass before proceeding to publish.

---

Expand Down Expand Up @@ -166,8 +222,9 @@ git push origin v<VERSION>

This automatically triggers the **Publish to pub.dev** workflow which:
1. Runs formatting check, analysis, and all tests
2. Publishes the package to pub.dev via OIDC
3. Creates a GitHub Release with the tag name and changelog notes extracted from `CHANGELOG.md`
2. Runs the Android example integration smoke test
3. Publishes the package to pub.dev via OIDC
4. Creates a GitHub Release with the tag name and changelog notes extracted from `CHANGELOG.md`

Monitor the run at:
`https://github.com/mapp-digital/Mapp-Intelligence-Flutter-Tracking/actions`
Expand All @@ -188,6 +245,11 @@ flutter analyze --no-fatal-infos
# Tests
flutter test

# Android integration smoke
cd example
flutter test integration_test/app_smoke_test.dart -d <DEVICE_ID>
cd ..

# Dry run — validates package structure without publishing
dart pub publish --dry-run
```
Expand All @@ -213,5 +275,11 @@ The version heading in `CHANGELOG.md` must match exactly `## <version>` with no
### OIDC authentication fails in CI
Verify the pub.dev admin setup in Section 5 is complete and the repository name matches exactly: `mapp-digital/Mapp-Intelligence-Flutter-Tracking`.

### Android smoke test fails in CI
The shared release checks workflow runs `example/integration_test/app_smoke_test.dart` on an Android emulator. Check:
- emulator boot failures in the `integration-smoke` job
- example dependency resolution
- UI text or navigation changes in the example app that invalidate the smoke test assertions

### Publish workflow triggers but GitHub Release is not created
The `github-release` job requires the `publish` job to succeed first. Check the Actions run log for errors in the publish step.
50 changes: 50 additions & 0 deletions example/integration_test/app_smoke_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:integration_test/integration_test.dart';
import 'package:plugin_mappintelligence_example/main.dart' as app;

Future<void> _pumpUntilVisible(
WidgetTester tester,
Finder finder, {
Duration timeout = const Duration(seconds: 20),
Duration step = const Duration(milliseconds: 250),
}) async {
final maxTicks = timeout.inMilliseconds ~/ step.inMilliseconds;
for (var i = 0; i < maxTicks; i++) {
await tester.pump(step);
if (finder.evaluate().isNotEmpty) {
return;
}
}
fail('Timed out waiting for expected widget to appear.');
}

void main() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();

testWidgets('example app launches and basic navigation works',
(WidgetTester tester) async {
app.main();

final consentDialog = find.text('User Tracking');
await _pumpUntilVisible(tester, consentDialog);
expect(consentDialog, findsOneWidget);

await tester.tap(find.text('Ok'));
await tester.pumpAndSettle(const Duration(seconds: 1));

expect(find.text('Mapp Intelligence Demo'), findsOneWidget);
expect(find.text('Page Tracking'), findsOneWidget);
expect(find.text('Webview'), findsOneWidget);

await tester.tap(find.text('Page Tracking'));
await tester.pumpAndSettle(const Duration(seconds: 1));

expect(find.text('Track Page'), findsOneWidget);
expect(find.text('Track Custom Page'), findsOneWidget);

await tester.pageBack();
await tester.pumpAndSettle(const Duration(seconds: 1));

expect(find.text('Mapp Intelligence Demo'), findsOneWidget);
});
}
2 changes: 2 additions & 0 deletions example/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ dev_dependencies:
video_player: ^2.1.1
flutter_test:
sdk: flutter
integration_test:
sdk: flutter

# For information on the generic Dart part of this file, see the
# following page: https://dart.dev/tools/pub/pubspec
Expand Down
Loading
Loading