patrol-e2e-testing

$npx mdskill add evanca/flutter-ai-rules/patrol-e2e-testing

Designs Flutter E2E tests for features, bugs, and native interactions.

  • Covers new user flows and captures regression tests for UI defects.
  • Integrates with Patrol 4.x CLI and Flutter project dependencies.
  • Executes deterministic tests locally and within continuous integration pipelines.
  • Delivers runnable test files in the patrol_test directory.

SKILL.md

.github/skills/patrol-e2e-testingView on GitHub ↗
---
name: patrol-e2e-testing
description: Generates and maintains end-to-end tests for Flutter apps using Patrol. Use when adding E2E coverage for new features, regression tests for UI bugs, or testing native interactions (permissions, system dialogs, deep links)
---

# Patrol E2E Testing Skill

This skill defines how to design, implement, and run end-to-end (E2E) tests using Patrol 4.x in Flutter projects.

It focuses on:

* Covering new feature flows with deterministic UI tests
* Converting reproducible UI bugs into regression tests
* Handling native OS interactions
* Running tests locally and in CI using correct Patrol CLI commands

## When to Use

Use this skill when:

* A new screen or user flow must be covered with E2E tests.
* A feature interacts with native components (permissions, notifications, system dialogs).
* A UI bug should be captured as a regression test.
* Cross-platform behavior (Android / iOS / Web) must be validated.

## Setup

For installation and project initialization, follow the official documentation:

[https://patrol.leancode.co/documentation#setup](https://patrol.leancode.co/documentation#setup)

Key Patrol conventions:

* `patrol` is added as a dev dependency.
* Tests live in `patrol_test/`.
* Test files must end with `_test.dart`.
* Tests are executed with `patrol test`.

## How to use this skill

Follow the steps below when implementing or updating Patrol tests.

### 1. Identify the user journey

Break the feature into:

* **Actions**: taps, scrolls, input, navigation, deep links.
* **Observable outcomes**: visible text, screen changes, enabled buttons, dialogs.

Rules:

* One test per primary (happy-path) journey.
* Separate tests for critical edge cases.
* Avoid combining unrelated flows in a single test.

### 2. Structure the Patrol test

Basic Patrol structure:

```dart
import 'package:flutter_test/flutter_test.dart';
import 'package:patrol/patrol.dart';

void main() {
  patrolTest(
    'user can log in successfully',
    ($) async {
      await $.pumpWidgetAndSettle(const MyApp());

      const email = String.fromEnvironment('E2E_EMAIL');
      const password = String.fromEnvironment('E2E_PASSWORD');

      await $(#emailField).enterText(email);
      await $(#passwordField).enterText(password);
      await $(#loginButton).tap();

      await $.waitUntilVisible($(#homeScreenTitle));

      expect($(#homeScreenTitle).text, equals('Welcome'));
    },
  );
}
```

Key concepts:

* Use `patrolTest()` instead of `testWidgets()`.
* `$` is the Patrol tester.
* Use `$(#keyName)` to find widgets by `Key`.
* Use explicit wait conditions (e.g., `waitUntilVisible`).

### 3. Handle native dialogs

For OS-level permission dialogs:

```dart
patrolTest('grants camera permission', ($) async {
  await $.pumpWidgetAndSettle(const MyApp());

  await $(#openCameraButton).tap();

  if (await $.native.isPermissionDialogVisible()) {
    await $.native.grantPermission();
  }

  await $.waitUntilVisible($(#cameraPreview));
});
```

Use native automation only when required by the feature.

### 4. Selector & interaction quick reference

**Finding widgets:**

```dart
$('some text')        // by text
$(TextField)          // by type
$(Icons.arrow_back)   // by icon
```

**Tapping:**

```dart
// Tap a widget containing a specific text label
await $(Container).$('click').tap();

// Tap a container that contains an ElevatedButton
await $(Container).containing(ElevatedButton).tap();

// Tap only the enabled ElevatedButton
await $(ElevatedButton)
    .which<ElevatedButton>(
      (b) => b.enabled,
    )
    .tap();
```

**Entering text:**

```dart
// Enter text into the second TextField on screen
await $(TextField).at(1).enterText('your input');
```

**Scrolling:**

```dart
await $(widget_you_want_to_scroll_to).scrollTo();
```

**Native interactions:**

```dart
// Grant permission while app is in use
await $.native.grantPermissionWhenInUse();

// Open notification shade and tap a notification by text
await $.native.openNotifications();
await $.native.tapOnNotificationBySelector(
  Selector(textContains: 'text'),
);
```

### 5. Running Patrol tests

Run all tests:

```bash
patrol test
```

Run a specific file with live reload (development mode):

```bash
patrol develop -t integration_test/my_test.dart
```

Run a specific file:

```bash
patrol test --target patrol_test/login_test.dart
```

Run on web:

```bash
patrol test --device chrome
```

Headless web (CI):

```bash
patrol test --device chrome --web-headless true
```

Filter by tags:

```bash
patrol test --tags android
```

### Output requirements when this skill is used

When applied, this skill must produce:

1. Patrol test(s) covering the specified feature.
2. Any required widget key additions.
3. Exact `patrol test` command(s) to execute locally.
4. Notes explaining stabilization decisions.

### Quality bar

A valid Patrol test must be:

* Deterministic (no arbitrary delays)
* Readable
* Minimal but complete
* Secret-safe (no hardcoded credentials)
* CI-ready

More from evanca/flutter-ai-rules

SkillDescription
architecture-feature-firstStructures Flutter apps using layered architecture (UI / Logic / Data) with feature-first file organization. Use when creating new features, designing the project structure, adding repositories/services/view models (or cubits/providers/notifiers), or wiring dependency injection. State management agnostic.
blocImplements Flutter state management using the bloc library (Bloc and Cubit). Use when creating new features, screens, or state management logic with bloc/cubit, modeling state, wiring Flutter widgets to blocs, or writing bloc/cubit unit tests.
dart-3-updatesApplies Dart 3 language features in Flutter/Dart code. Use when writing if-else or switch statements, creating new classes, or deciding between a data class and a record.
effective-dartApplies Effective Dart guidelines in Flutter/Dart code. Use when writing or reviewing Dart code for naming conventions, types, style, imports, file structure, usage patterns, documentation, testing, widgets, state management, or performance.
firebase-aiIntegrates Firebase AI Logic into Flutter apps. Use when setting up the firebase_ai plugin, calling Gemini models, handling AI service errors, or applying security and privacy considerations for AI features.
firebase-analyticsIntegrates Firebase Analytics into Flutter apps. Use when setting up analytics, logging events, setting user properties, or configuring event parameters.
firebase-app-checkIntegrates Firebase App Check into Flutter apps. Use when setting up App Check, selecting providers per platform, using debug providers during development, enabling enforcement, or applying App Check security best practices.
firebase-authIntegrates Firebase Authentication into Flutter apps. Use when setting up auth, managing auth state, implementing email/password or social sign-in, handling auth errors, managing users, or applying security best practices.
firebase-cloud-firestoreIntegrates Cloud Firestore into Flutter apps. Use when setting up Firestore, designing document/collection structure, reading and writing data, working with real-time listeners, designing for scale, or applying security rules.
firebase-cloud-functionsCalls Firebase Cloud Functions from Flutter apps. Use when setting up callable functions, passing data to functions, handling errors from function calls, optimizing performance, or testing with the Firebase Emulator Suite.