pickle_parser 1.1.2 copy "pickle_parser: ^1.1.2" to clipboard
pickle_parser: ^1.1.2 copied to clipboard

A Flutter package for parsing pickle files and executing Cucumber steps in widget tests.

Pickle Parser #

A comprehensive Flutter package for parsing pickle/Gherkin files and executing Cucumber steps in widget tests. This package allows you to write human-readable test scenarios using Gherkin syntax and automatically execute them as Flutter widget tests.

📋 Table of Contents #

🔍 Overview #

The Pickle Parser package bridges the gap between business-readable Gherkin scenarios and Flutter widget tests. It automatically parses pickle/feature files and executes corresponding test actions, making it easier to maintain test scenarios and improve collaboration between developers and non-technical stakeholders.

✨ Features #

  • 🥒 Full Gherkin Support: Parse and execute Given/When/Then steps
  • 🎯 Multiple Element Selectors: Find widgets by key, type, text, or icon
  • 🔍 Advanced Text Matching: Supports exact, contains, startsWith, endsWith, and regex patterns
  • 💫 Rich Interactions: Tap, long press, double tap, swipe, scroll, and keyboard input
  • ⌨️ Text Input: Support for various text field interactions
  • 🕒 Wait Strategies: Time-based waits and conditional waiting
  • 📱 Navigation Support: Back navigation and dialog dismissal
  • 🎨 Gesture Support: Swipe, scroll, refresh, and custom gestures
  • 📖 Clear Error Messages: Detailed logging and error reporting
  • 🛠️ CLI Tools: Validate feature files and generate test skeletons
  • 🔧 Custom Steps: Register your own step implementations for app-specific actions

📦 Installation #

Add pickle_parser to your pubspec.yaml file:

dev_dependencies:
  flutter_test:
    sdk: flutter
  pickle_parser: ^1.1.0

Then run:

flutter pub get

🛠️ CLI Tools #

The package includes powerful command-line tools for validating feature files and generating test skeletons, making it easier to maintain and debug your Gherkin scenarios.

Installation #

The CLI tools are included with the package. After installing pickle_parser, you can run them directly:

# Run from your project directory
dart run pickle_parser:cli --help

Or run the tool file directly:

# Navigate to the package location and run
dart tool/cli/bin/pickle_parser_cli.dart --help

CLI Commands #

Validate Feature Files

# Validate all feature files in default directory (assets/features)
dart run pickle_parser:cli --validate

# Validate with verbose output
dart run pickle_parser:cli --validate --verbose

# Validate specific directory
dart run pickle_parser:cli --validate --input assets/features

Generate Test Skeletons

# Generate test files from feature files
dart run pickle_parser:cli --generate

# Specify input and output directories
dart run pickle_parser:cli --generate --input assets/features --output test/integration

# Generate with verbose output
dart run pickle_parser:cli --generate --verbose

Combined Operations

# Validate and generate in one command
dart run pickle_parser:cli --validate --generate --verbose

# Full workflow with custom directories
dart run pickle_parser:cli --validate --generate --input assets/scenarios --output test/generated --verbose

CLI Features #

  • Feature File Validation: Checks syntax and supported step patterns
  • 🏗️ Test Skeleton Generation: Creates ready-to-use test files
  • 📊 Detailed Reporting: Shows validation results and statistics
  • 🎯 Smart Error Detection: Identifies unsupported steps and syntax issues
  • 📁 Batch Processing: Handles multiple feature files at once
  • 🔍 Step Pattern Recognition: Validates against all supported Gherkin patterns
  • 📋 Summary Statistics: Reports total files, steps, and validation status

CLI Usage Examples #

Basic Validation

$ dart run pickle_parser:cli --validate
🔍 Validating feature files in: assets/features
📁 Found 3 feature file(s)
📊 Validation Summary:
  ✅ Valid files: 3/3
  📋 Total steps: 45
  🎉 All feature files are valid!

Validation with Errors

$ dart run pickle_parser:cli --validate --verbose
🔍 Validating feature files in: assets/features
📁 Found 2 feature file(s)

📄 Validating: login.feature
  ✅ Valid (12 steps)

📄 Validating: checkout.feature
  ❌ Invalid:
    - Line 15: Unsupported step - "I click the mysterious button"
    - Line 23: Missing Feature declaration

📊 Validation Summary:
  ✅ Valid files: 1/2
  📋 Total steps: 12
  ❌ Errors found: 2

Test Generation

$ dart run pickle_parser:cli --generate --verbose
🏗️ Generating test skeletons from: assets/features to: test/generated
📁 Created output directory: test/generated
  ✅ Generated: login_test.dart
  ✅ Generated: checkout_test.dart

📊 Generation Summary:
  ✅ Generated 2 test file(s)
  📁 Output directory: test/generated

💡 Next steps:
  1. Review generated test files
  2. Add imports for your app widgets
  3. Customize the test setup as needed
  4. Run: flutter test test/generated

🔗 Dependencies #

Required Dependencies #

  • Flutter SDK: >=2.19.5 <4.0.0
  • flutter: Core Flutter framework
  • flutter_test: Flutter testing framework

What's Included #

The package automatically provides:

  • Gherkin step parsing and execution
  • Widget finding capabilities
  • Gesture simulation
  • Text input handling
  • Wait and timing utilities

Asset Configuration #

To use pickle files, add them to your pubspec.yaml:

flutter:
  assets:
    - assets/features/
    - test/features/

🚀 Quick Start #

1. Create a Feature File #

Create a .feature file in your assets folder:

# assets/features/login.feature
Feature: User Login

  Scenario: Successful login
    When I see Welcome to MyApp
    When I enter john@example.com in field with key:email_field
    When I enter password123 in field with key:password_field
    When I tap key:login_button
    Then I see Dashboard
    Then I do not see Login Failed

2. Create a Test File #

// test/login_test.dart
import 'package:flutter_test/flutter_test.dart';
import 'package:pickle_parser/pickle_parser.dart';
import 'package:myapp/main.dart'; // Your app

void main() {
  testWidgets('Login flow from pickle file', (WidgetTester tester) async {
    // Build your app
    await tester.pumpWidget(MyApp());

    // Execute the pickle file
    await pickleParser(tester, 'assets/features/login.feature');
  });
}

📝 Supported Gherkin Syntax #

Keywords #

  • Given - Setup steps (currently treated as When steps)
  • When - Action steps
  • Then - Assertion steps
  • # - Comments (ignored)

Step Structure #

When I [action] [element]
Then I [assertion] [element]

🎯 Element Selectors #

The package supports multiple ways to find widgets:

1. By Key #

When I tap key:submit_button
Then I see key:success_message

2. By Widget Type #

When I tap type:ElevatedButton
Then I see type:Text

Supported Widget Types:

  • Text, TextField, TextFormField
  • ElevatedButton, TextButton, OutlinedButton
  • Container, Column, Row, Expanded
  • AppBar, Scaffold, Material
  • Icon, IconButton, FloatingActionButton
  • ListView, GridView, Card
  • Checkbox, Switch, Slider, Radio
  • DropdownButton, AlertDialog, SnackBar
  • Drawer, BottomSheet, Tab, TabBar
  • NavigationBar, BottomNavigationBar
  • RefreshIndicator, CupertinoNavigationBar

3. By Text Content #

# Exact match (default)
When I tap Submit
Then I see Welcome!

# Enhanced text matching
When I tap contains:Submit
Then I see startsWith:Welcome
When I see endsWith:Message
Then I see regex:^Error.*occurred$

Text Selector Formats:

  • text:exact - Exact text match (explicit)
  • contains:partial - Text contains substring
  • startsWith:prefix - Text starts with prefix
  • endsWith:suffix - Text ends with suffix
  • regex:pattern - Text matches regex pattern

4. By Icon #

When I tap icon:add
Then I see icon:check

Supported Icons:

  • add, delete, close, menu

🔧 Custom Steps #

The package supports registering custom step implementations for app-specific actions. This allows you to extend the built-in step library with your own business logic.

How Custom Steps Work #

  1. Priority: Custom steps are checked first, before built-in implementations
  2. Fallback: If a custom step returns false or throws an error, built-in steps are tried
  3. Flexibility: Support for exact matching, regex patterns, and templates

Registration Methods #

Exact Text Matching

Register a step handler for exact text matches:

import 'package:pickle_parser/pickle_parser.dart';

// Register before running tests
registerCustomStep(
  'I login with default credentials',
  (step, tester) async {
    await tester.enterText(find.byKey(Key('username')), 'test@example.com');
    await tester.enterText(find.byKey(Key('password')), 'password123');
    await tester.tap(find.byKey(Key('login_button')));
    await tester.pumpAndSettle();
    return true; // Step handled successfully
  },
);

Pattern Matching with Regex

Register steps that match regex patterns:

registerCustomStepPattern(
  RegExp(r'I wait for (\d+) milliseconds'),
  (step, tester) async {
    final match = RegExp(r'I wait for (\d+) milliseconds').firstMatch(step);
    if (match != null) {
      final ms = int.parse(match.group(1)!);
      await tester.pump(Duration(milliseconds: ms));
      return true;
    }
    return false;
  },
);

Template Matching

Register steps using simple templates with placeholders:

registerCustomStepTemplate(
  'I wait for {} seconds and then tap {}',
  (step, tester) async {
    // Parse step manually to extract values
    final parts = step.split(' ');
    final seconds = int.parse(parts[3]);
    final elementKey = parts[7];

    await Future.delayed(Duration(seconds: seconds));
    await tester.tap(find.byKey(Key(elementKey)));
    return true;
  },
);

Advanced Custom Steps #

Complex Business Logic

registerCustomStep(
  'I complete the checkout process',
  (step, tester) async {
    // Multi-step business process
    await tester.enterText(find.byKey(Key('credit_card')), '4111111111111111');
    await tester.enterText(find.byKey(Key('expiry')), '12/25');
    await tester.enterText(find.byKey(Key('cvv')), '123');

    await tester.tap(find.byKey(Key('submit_payment')));
    await tester.pumpAndSettle();

    // Verify success
    expect(find.text('Payment Successful'), findsOneWidget);
    return true;
  },
);

Custom Assertions

registerCustomStepPattern(
  RegExp(r'I verify that (.+) contains (.+)'),
  (step, tester) async {
    final match = RegExp(r'I verify that (.+) contains (.+)').firstMatch(step);
    if (match != null) {
      final elementName = match.group(1)!;
      final expectedText = match.group(2)!;

      Finder finder = elementName.startsWith('key:')
          ? find.byKey(Key(elementName.substring(4)))
          : find.text(elementName);

      final widget = tester.widget(finder);
      String actualText = '';

      if (widget is Text) {
        actualText = widget.data ?? '';
      } else if (widget is TextField) {
        actualText = (widget as dynamic).controller?.text ?? '';
      }

      if (!actualText.contains(expectedText)) {
        throw TestFailure('Text "$actualText" does not contain "$expectedText"');
      }

      return true;
    }
    return false;
  },
);

Using the Registry Directly #

For more control, use the CustomStepRegistry directly:

void setupCustomSteps() {
  final registry = CustomStepRegistry();

  // Register multiple patterns
  registry.registerPattern(RegExp(r'I scroll to (.+)'), myScrollHandler);
  registry.registerExact('I perform cleanup', myCleanupHandler);

  // Check registration
  print('Registered ${registry.handlerCount} custom steps');

  // Clear all (useful for testing)
  registry.clear();
}

Custom Step Handler Signature #

typedef CustomStepHandler = Future<bool> Function(String step, WidgetTester tester);

Parameters:

  • step: The full step text including Gherkin keyword
  • tester: The WidgetTester instance for widget interactions

Return Value:

  • true: Step was handled successfully
  • false: Step couldn't be handled, try built-in steps
  • Exception: Step failed with error

Best Practices #

1. Return false for Unhandled Cases

registerCustomStepPattern(
  RegExp(r'I wait for (\d+) seconds'),
  (step, tester) async {
    final match = RegExp(r'I wait for (\d+) seconds').firstMatch(step);
    if (match == null) {
      return false; // Let built-in handlers try
    }

    final seconds = int.tryParse(match.group(1)!);
    if (seconds == null) {
      return false; // Invalid format, let others handle
    }

    await Future.delayed(Duration(seconds: seconds));
    return true;
  },
);

2. Use Descriptive Step Names

// Good - clear and specific
registerCustomStep('I login as admin user', adminLoginHandler);
registerCustomStep('I verify shopping cart total', cartTotalHandler);

// Avoid - too generic
registerCustomStep('I do something', genericHandler);

3. Setup in Test Suite

void main() {
  setUpAll(() {
    // Register custom steps before all tests
    registerCustomStep('I setup test data', setupTestDataHandler);
    registerCustomStep('I cleanup test data', cleanupTestDataHandler);
  });

  tearDownAll(() {
    // Clean up if needed
    CustomStepRegistry().clear();
  });

  testWidgets('My test', (tester) async {
    // Use custom steps in feature files or directly
    await getCucumberStepTestCode('When I setup test data', tester);
    // ... test logic ...
    await getCucumberStepTestCode('When I cleanup test data', tester);
  });
}

4. Error Handling

registerCustomStep(
  'I verify complex state',
  (step, tester) async {
    try {
      // Complex verification logic
      await verifyComplexState(tester);
      return true;
    } catch (e) {
      // Log error and let built-in steps try, or rethrow if critical
      print('Custom step failed: $e');
      return false; // Or rethrow if you want the test to fail
    }
  },
);

Feature File Examples #

With custom steps registered, you can use them in feature files:

Feature: Shopping Cart

  Scenario: Complete purchase
    Given I am on the product page
    When I add item to cart
    And I login with default credentials          # Custom step
    And I complete the checkout process           # Custom step
    Then I verify that receipt contains "Success" # Custom step
    And I cleanup test data                       # Custom step

Custom Steps in CLI Validation #

The CLI tool recognizes custom steps when they are registered:

# Run validation with custom steps
dart run pickle_parser:cli --validate

# The validator will check:
# 1. Built-in step patterns
# 2. Registered custom step patterns
# 3. Report any unmatched steps

Note: For CLI validation to recognize custom steps, you need to register them in a setup file that the CLI can access.

☕ Support This Project #

If you find this package helpful and want to support its development, consider buying me a coffee! Your support helps maintain and improve this open-source project.

Buy Me A Coffee

📄 License #

This project is licensed under the MIT License - see the LICENSE file for details.


Need help? Check the example directory for complete working examples or create an issue on GitHub.

1
likes
160
points
83
downloads

Publisher

unverified uploader

Weekly Downloads

A Flutter package for parsing pickle files and executing Cucumber steps in widget tests.

Repository (GitHub)

Topics

#testing #cucumber #gherkin #bdd #flutter-testing

Documentation

API reference

License

MIT (license)

Dependencies

args, flutter, flutter_test, path

More

Packages that depend on pickle_parser