# ABTasty QA Assistant

This package allows QA teams and developers to easily test campaigns, force variations, and verify flag values in real-time within their Flutter applications. [using-abtasty-qa-assistant](https://docs.abtasty.com/server-side/integrations/using-abtasty-qa-assistant "mention")

### Table of Contents

* Overview
* Features
* Prerequisites
* Installation
* Quick Start
* Detailed Integration
  * Initialize Flagship SDK
  * Setup QA Assistant
  * Display Overlay Button
  * Live Flag Updates
* Key Features
* Best Practices
* Troubleshooting
* Additional Resources

***

### Overview

The ABTasty QA Assistant provides an intuitive in-app interface for testing A/B campaigns, feature flags, and variations during development and QA phases. It seamlessly integrates with the Flagship Flutter SDK to provide real-time flag updates and comprehensive campaign management.

#### ⚠️ Production Warning

{% hint style="warning" %}
**Do not ship this package to production!** The QA Assistant is intended for development and QA testing only.
{% endhint %}

To prevent accidental inclusion in production, wrap the QA Assistant initialization with a debug check:

```dart
import 'package:flutter/foundation.dart';

void _toggleQAAssistant() {
  if (kDebugMode) {  // Only available in debug mode
    if (_qaAssistant == null) {
      _qaAssistant = ABTastyQAAssistant(...);
      _qaAssistant?.showOverlayButton(context);
    }
  }
}
```

***

### Features

* 🎯 **Campaign Management** - View, test, and force variations for A/B tests and feature flags
* 🔄 **Live Flag Updates** - Real-time flag value changes when forcing variations
* 📊 **Allocation Viewer** - Check traffic distribution across variations
* 🎨 **Overlay Button** - Floating button for easy access during testing
* 🔍 **Targeting Inspection** - Verify targeting rules and conditions
* 📈 **Event Tracking** - Monitor all events sent to Flagship
* 🧪 **Variation Testing** - Force specific variations to test different experiences

***

### Prerequisites

Before integrating the ABTasty QA Assistant, ensure you have:

* Flutter SDK installed (>=3.6.1)
* An ABTasty account with:
  * Environment ID
  * API Key
* The Flutter [Flagship SDK v4.3.0](https://docs.abtasty.com/server-side/sdks/flutter/flutter) installed in your project

***

### Installation

#### Step 1: Add Dependencies

Add both the Flagship SDK and QA Assistant to your `pubspec.yaml`:

```yaml
dependencies:
  flutter:
    sdk: flutter
    
  # Flagship SDK
  flagship: ^4.3.0
  
  # ABTasty QA Assistant
  abtasty_qa_assistant: ^1.0.0
```

#### Step 2: Install Packages

Run the following command:

```bash
flutter pub get
```

***

### Quick Start

Get up and running in 5 minutes with this complete example:

#### Step 1: Replace Your Credentials

Replace these placeholders with your actual values:

* `YOUR_ENVIRONMENT_ID` - Your Flagship environment ID
* `YOUR_API_KEY` - Your Flagship API key

#### Step 2: Copy & Paste This Code

```dart
import 'package:flutter/material.dart';
import 'package:flagship/flagship.dart';
import 'package:abtasty_qa_assistant/abtasty_qa_assistant.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'ABTasty QA Demo',
      home: const HomePage(),
    );
  }
}

class HomePage extends StatefulWidget {
  const HomePage({super.key});

  @override
  State<HomePage> createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  ABTastyQAAssistant? _qaAssistant;
  String _flagValue = "Loading...";

  @override
  void initState() {
    super.initState();
    _initFlagship();
  }

  Future<void> _initFlagship() async {
    // 1. Initialize Flagship
    await Flagship.start(
      "YOUR_ENVIRONMENT_ID",
      "YOUR_API_KEY",
    );

    // 2. Create a visitor
    Flagship.newVisitor(
      visitorId: "user_123",
      hasConsented: true,
    ).withContext({
      "isQA": true,
    }).build();

    // 3. Fetch flags
    await Flagship.getCurrentVisitor()?.fetchFlags();

    // 4. Setup live updates
    Flagship.getCurrentVisitor()?.onFlagUpdate = (changedKeys) {
      print('🔔 Flags updated: $changedKeys');
      _updateFlagValue();
    };

    _updateFlagValue();
  }

  void _updateFlagValue() {
    setState(() {
      _flagValue = Flagship.getCurrentVisitor()
          ?.getFlag("btnTitle")
          .value("Default Button") ?? "No value";
    });
  }

  void _toggleQA() {
    if (_qaAssistant == null) {
      _qaAssistant = ABTastyQAAssistant(
        "YOUR_ENVIRONMENT_ID",
        "YOUR_API_KEY",
        onClose: () {
          print('QA Assistant closed');
        },
      );
      _qaAssistant?.showOverlayButton(context);
    } else {
      _qaAssistant?.hideOverlayButton();
      _qaAssistant?.dispose();
      _qaAssistant = null;
    }
    setState(() {});
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('ABTasty QA Demo'),
        leading: IconButton(
          icon: Icon(
            _qaAssistant?.isOverlayVisible ?? false
                ? Icons.visibility_off
                : Icons.bug_report,
          ),
          onPressed: _toggleQA,
          tooltip: 'Toggle QA Assistant',
        ),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            const Text(
              'Flag Value:',
              style: TextStyle(fontSize: 18),
            ),
            const SizedBox(height: 8),
            Text(
              _flagValue,
              style: const TextStyle(
                fontSize: 24,
                fontWeight: FontWeight.bold,
                color: Colors.blue,
              ),
            ),
            const SizedBox(height: 32),
            ElevatedButton(
              onPressed: _toggleQA,
              child: Text(
                _qaAssistant?.isOverlayVisible ?? false
                    ? 'Hide QA Assistant'
                    : 'Show QA Assistant',
              ),
            ),
          ],
        ),
      ),
    );
  }

  @override
  void dispose() {
    Flagship.getCurrentVisitor()?.onFlagUpdate = null;
    _qaAssistant?.dispose();
    super.dispose();
  }
}
```

#### Step 3: Run Your App

```bash
flutter run
```

#### Step 4: Use the QA Assistant

1. **Tap the bug icon** in the AppBar (top-left)
2. **The QA overlay button appears** in the bottom-right corner
3. **Tap the overlay button** to open the QA Assistant dialog
4. **Browse campaigns** and force different variations
5. **Watch the flag value update live** when you select variations

***

### Detailed Integration

#### 1. Initialize Flagship SDK

First, initialize the Flagship SDK in your app:

```dart
Future<void> _initFlagship() async {
  try {
    // Start Flagship SDK
    await Flagship.start(
      "YOUR_ENVIRONMENT_ID",
      "YOUR_API_KEY",
    );

    // Create a visitor with context
    Flagship.newVisitor(
      visitorId: "user_123",
      hasConsented: true,
    ).withContext({
      "isQA": true,
      "country": "FR",
      "isVip": false,
      // Add your custom context
    }).build();

    print("✅ Flagship SDK initialized");

    // Fetch flags from the server
    await Flagship.getCurrentVisitor()?.fetchFlags();
    
  } catch (e) {
    print("❌ Error initializing Flagship: $e");
  }
}
```

#### 2. Setup QA Assistant

Initialize the QA Assistant with your credentials:

```dart
void _initializeQAAssistant() {
  _qaAssistant = ABTastyQAAssistant(
    "YOUR_ENVIRONMENT_ID",
    "YOUR_API_KEY",
    onClose: () {
      // Called when QA Assistant dialog is closed
      print('🔄 QA Assistant closed, refreshing flags...');
      _refreshFlags();
    },
  );
  
  print('✅ QA Assistant initialized');
}
```

#### 3. Display Overlay Button

Show the floating overlay button in your app:

```dart
void _showQAAssistant() {
  _initializeQAAssistant();
  _qaAssistant?.showOverlayButton(context);
}

void _hideQAAssistant() {
  _qaAssistant?.hideOverlayButton();
  _qaAssistant?.dispose();
  _qaAssistant = null;
}

void _toggleQAAssistant() {
  if (_qaAssistant?.isOverlayVisible ?? false) {
    _hideQAAssistant();
  } else {
    _showQAAssistant();
  }
  setState(() {});
}
```

**Add a button in your AppBar:**

```dart
AppBar(
  title: const Text('My App'),
  leading: IconButton(
    icon: Icon(
      _qaAssistant?.isOverlayVisible ?? false
          ? Icons.visibility_off
          : Icons.bug_report,
    ),
    onPressed: _toggleQAAssistant,
    tooltip: 'Toggle QA Assistant',
  ),
)
```

#### 4. Live Flag Updates

Enable live flag updates to automatically refresh your UI when flags are modified through the QA Assistant:

```dart
void _setupFlagUpdateListener() {
  final visitor = Flagship.getCurrentVisitor();
  if (visitor != null) {
    visitor.onFlagUpdate = (changedKeys) {
      print('🔔 Live update received for flags: $changedKeys');
      
      // Show notification
      if (mounted) {
        ScaffoldMessenger.of(context).showSnackBar(
          SnackBar(
            content: Text('🔄 Live update: ${changedKeys.join(", ")}'),
            backgroundColor: Colors.orange,
            duration: const Duration(seconds: 2),
          ),
        );
      }
      
      // Update your UI with new flag values
      _updateFlagValues();
    };
    
    print('✅ Flag update listener registered');
  }
}

void _updateFlagValues() {
  final visitor = Flagship.getCurrentVisitor();
  if (visitor != null) {
    setState(() {
      // Get updated flag values
      final buttonTitle = visitor.getFlag("btnTitle").value("Default");
      final buttonColor = visitor.getFlag("btnColor").value("blue");
      
      print("Updated flag values:");
      print("  btnTitle: $buttonTitle");
      print("  btnColor: $buttonColor");
    });
  }
}
```

**Important:** Setup the listener AFTER creating the visitor:

```dart
Future<void> _initFlagship() async {
  await Flagship.start("ENV_ID", "API_KEY");
  
  Flagship.newVisitor(visitorId: "user_123", hasConsented: true)
    .withContext({"isQA": true})
    .build();
    
  await Flagship.getCurrentVisitor()?.fetchFlags();
  
  // ✅ Setup listener after visitor is created
  _setupFlagUpdateListener();
}
```

***

### Key Features

#### 🎯 Campaign Management

The QA Assistant provides a comprehensive interface to:

* **View all campaigns** - See active, inactive, and forced campaigns (A/B tests, toggles, personalizations)
* **View variations** - Explore different variations for each campaign with all associated flags
* **Force variations** - Override the default allocation and test specific variations
* **Reset to original** - Return to the production variation with a single tap
* **View allocations** - Check traffic distribution across variations
* **Check targeting** - Verify targeting rules and conditions

{% hint style="info" %}
See [using-abtasty-qa-assistant](https://docs.abtasty.com/server-side/integrations/using-abtasty-qa-assistant "mention") for more information
{% endhint %}

#### 🔄 Live Flag Updates

Real-time flag value changes when you force variations through the QA Assistant:

```dart
// Setup listener for automatic UI updates
Flagship.getCurrentVisitor()?.onFlagUpdate = (changedKeys) {
  print('🔔 Flags updated: $changedKeys');
  setState(() {
    // Refresh your UI with new flag values
  });
};
```

#### 📊 Comprehensive Views

* **Variations Tab** - See all flags and their values for each variation
* **Allocation Tab** - View traffic distribution percentages
* **Targeting Tab** - Check targeting rules and audience criteria
* **Dashboard Tab** - See campaign state and status
* **Events Tab** - Monitor events sent to Flagship in real-time

#### 🔍 Context Management

* **View current context** - See all context key-value pairs for the visitor
* **Modify context** - Update context values for testing different targeting scenarios

***

### Best Practices

#### 1. Initialize in Debug Mode Only

Only enable the QA Assistant in debug/staging environments:

```dart
import 'package:flutter/foundation.dart';

void _initQAAssistant() {
  if (kDebugMode || _isStagingEnvironment) {
    _qaAssistant = ABTastyQAAssistant(
      "YOUR_ENVIRONMENT_ID",
      "YOUR_API_KEY",
    );
  }
}
```

#### 2. Always Dispose Properly

Clean up resources when the widget is disposed:

```dart
@override
void dispose() {
  final visitor = Flagship.getCurrentVisitor();
  if (visitor != null) {
    visitor.onFlagUpdate = null;  // Remove listener
  }
  
  _qaAssistant?.hideOverlayButton();
  _qaAssistant?.dispose();
  
  super.dispose();
}
```

### Troubleshooting

#### Issue: Overlay button not showing

**Solution:** Make sure you've called `showOverlayButton(context)` with a valid `BuildContext`:

```dart
_qaAssistant?.showOverlayButton(context);
```

#### Issue: Flags not updating after forcing a variation

**Solution:** Ensure the flag update listener is properly registered:

```dart
// Setup listener AFTER creating visitor
_setupFlagUpdateListener();
```

Also verify that `onFlagUpdate` callback is set:

```dart
Flagship.getCurrentVisitor()?.onFlagUpdate = (changedKeys) {
  _updateFlagValues();
};
```

#### Issue: "Allocation has been bypassed" message

This is expected when you force a campaign. The allocation is bypassed because you've manually selected a variation instead of letting the normal traffic allocation decide. This is working as intended.

#### Issue: QA Assistant not compiling in production build

Make sure to wrap QA Assistant code in debug checks:

```dart
import 'package:flutter/foundation.dart';

if (kDebugMode) {
  // QA Assistant code
}
```

{% hint style="info" %}
[Consider using build flavors to completely exclude the package from production builds.](#user-content-fn-1)[^1]
{% endhint %}

***

### Additional Resources

* [Flagship Flutter SDK Documentation](https://docs.developers.flagship.io/docs/flutter)
* [ABTasty Platform Documentation](https://doc.abtasty.com/)
* [Example Application](#example-application)
* [GitHub Repository](https://github.com/flagship-io/abtasty-qa-assistant-flutter)
* [Report Issues](https://github.com/flagship-io/abtasty-qa-assistant-flutter/issues)

***

### Example Application

A complete demo application is included in the `example/` directory. To run it:

```bash
cd example
flutter run
```

The example demonstrates:

* Complete Flagship SDK integration
* QA Assistant setup
* Live flag updates
* Multiple campaign types
* Context management

[^1]:
