Skip to content

Manual Triggers ​

Manually trigger browser tests from GitHub Actions UI or the gh CLI with configurable branch and test selection.

Why Manual Triggers? ​

Manual triggers are essential for:

  • QA Testing: Test specific feature branches before merge
  • Pre-merge Validation: Verify branch combinations work together
  • Debugging: Run specific test groups to isolate issues
  • Cross-repo Testing: Test frontend feature branch with backend develop

Triggering Methods ​

Both methods use the same workflow_dispatch configuration:

GitHub UI ​

  1. Go to your repository → Actions tab
  2. Select your browser tests workflow
  3. Click "Run workflow" button
  4. Fill in the inputs and click "Run workflow"

gh CLI ​

bash
# List available workflows
gh workflow list

# Trigger with default inputs
gh workflow run browser-tests.yml

# Trigger with specific inputs
gh workflow run browser-tests.yml \
  -f backend_branch=feature/payment \
  -f frontend_branch=develop \
  -f test_group=smoke

# Watch the run progress
gh run watch

Basic Configuration ​

Add workflow_dispatch to your existing workflow triggers:

yaml
name: Browser Tests

on:
  # Automatic triggers
  push:
    branches: [main, develop]
  pull_request:
    branches: [main, develop]

  # Manual trigger
  workflow_dispatch:

This enables the "Run workflow" button without any inputs.

Branch Selection ​

Single Repository ​

For monorepo setups, the workflow uses the branch you select in the UI:

yaml
on:
  workflow_dispatch:

jobs:
  browser-tests:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      # Uses the branch selected in the UI

Multi-Repository ​

For separate frontend/backend repos, add branch inputs:

yaml
on:
  push:
    branches: [main, develop]
  pull_request:
    branches: [main, develop]
  workflow_dispatch:
    inputs:
      backend_branch:
        description: 'Backend branch (empty = current ref)'
        required: false
        default: ''
      frontend_branch:
        description: 'Frontend branch'
        required: false
        default: 'develop'

jobs:
  browser-tests:
    runs-on: ubuntu-latest
    defaults:
      run:
        working-directory: backend

    steps:
      - name: Checkout API
        uses: actions/checkout@v4
        with:
          path: backend
          ref: ${{ inputs.backend_branch || github.ref }}

      - name: Checkout Frontend
        uses: actions/checkout@v4
        with:
          repository: your-org/frontend-repo
          path: frontend
          ref: ${{ inputs.frontend_branch || 'develop' }}

Fallback Logic

The expression inputs.backend_branch || github.ref uses the input if provided, otherwise falls back to the triggering ref. This makes the workflow work for both automatic and manual triggers.

Test Selection with Groups ​

Use Pest's --group feature to organize and selectively run tests.

Organizing Tests ​

Add groups to your tests:

php
// tests/Browser/LoginTest.php
test('user can login with valid credentials', function () {
    $this->bridge('/login')
        ->type('[data-testid="email"]', 'test@example.com')
        ->type('[data-testid="password"]', 'password')
        ->click('[data-testid="submit"]')
        ->assertPathContains('/dashboard');
})->group('smoke', 'auth');

test('user sees error with invalid credentials', function () {
    $this->bridge('/login')
        ->type('[data-testid="email"]', 'wrong@example.com')
        ->type('[data-testid="password"]', 'wrong')
        ->click('[data-testid="submit"]')
        ->assertSee('Invalid credentials');
})->group('regression', 'auth');

test('user can reset password', function () {
    // ...
})->group('critical', 'auth');

Common Group Names ​

GroupPurpose
smokeQuick sanity checks, run frequently
criticalCore business flows that must never break
regressionComprehensive tests, run before releases
authAuthentication-related tests
checkoutE-commerce checkout flow tests

Workflow Configuration ​

yaml
on:
  push:
    branches: [main, develop]
  pull_request:
    branches: [main, develop]
  workflow_dispatch:
    inputs:
      test_group:
        description: 'Test group to run'
        required: false
        default: 'all'
        type: choice
        options:
          - all
          - smoke
          - critical
          - regression

jobs:
  browser-tests:
    runs-on: ubuntu-latest
    defaults:
      run:
        working-directory: backend

    steps:
      # ... checkout and setup steps ...

      - name: Run browser tests
        run: |
          if [ "${{ inputs.test_group }}" == "all" ] || [ -z "${{ inputs.test_group }}" ]; then
            ./vendor/bin/pest tests/Browser
          else
            ./vendor/bin/pest tests/Browser --group=${{ inputs.test_group }}
          fi

Running Locally ​

Test your groups locally before CI:

bash
# Run all browser tests
./vendor/bin/pest tests/Browser

# Run only smoke tests
./vendor/bin/pest tests/Browser --group=smoke

# Run multiple groups
./vendor/bin/pest tests/Browser --group=smoke,critical

Complete Example ​

Full workflow with branch selection and test groups:

yaml
name: Browser Tests

on:
  push:
    branches: [main, develop]
  pull_request:
    branches: [main, develop]
  workflow_dispatch:
    inputs:
      backend_branch:
        description: 'Backend branch (empty = current ref)'
        required: false
        default: ''
      frontend_branch:
        description: 'Frontend branch'
        required: false
        default: 'develop'
      test_group:
        description: 'Test group to run'
        required: false
        default: 'all'
        type: choice
        options:
          - all
          - smoke
          - critical
          - regression

jobs:
  browser-tests:
    runs-on: ubuntu-latest
    defaults:
      run:
        working-directory: backend

    steps:
      - name: Checkout API
        uses: actions/checkout@v4
        with:
          path: backend
          ref: ${{ inputs.backend_branch || github.ref }}

      - name: Checkout Frontend
        uses: actions/checkout@v4
        with:
          repository: your-org/frontend-repo
          path: frontend
          ref: ${{ inputs.frontend_branch || 'develop' }}

      - name: Setup PHP
        uses: shivammathur/setup-php@v2
        with:
          php-version: '8.3'
          extensions: dom, curl, libxml, mbstring, zip, pdo_sqlite

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: 'lts/*'

      - name: Install Composer dependencies
        run: composer install --no-interaction --prefer-dist

      - name: Install npm dependencies
        run: npm ci

      - name: Install Playwright browsers
        run: npx playwright install --with-deps chromium

      - name: Install frontend dependencies
        working-directory: frontend
        run: npm ci

      - name: Prepare Laravel
        run: |
          cp .env.example .env
          php artisan key:generate
          touch database/database.sqlite
          php artisan migrate --force

      - name: Run browser tests
        run: |
          if [ "${{ inputs.test_group }}" == "all" ] || [ -z "${{ inputs.test_group }}" ]; then
            ./vendor/bin/pest tests/Browser
          else
            ./vendor/bin/pest tests/Browser --group=${{ inputs.test_group }}
          fi

      - name: Upload artifacts on failure
        if: failure()
        uses: actions/upload-artifact@v4
        with:
          name: browser-artifacts
          path: |
            backend/tests/Browser/screenshots/
            backend/storage/logs/
          retention-days: 7

Tips & Best Practices ​

Using --filter for Debugging ​

For quick debugging of a specific test:

bash
# Via gh CLI
gh workflow run browser-tests.yml -f test_filter="user can login"

# In workflow
- name: Run browser tests
  run: ./vendor/bin/pest tests/Browser --filter="${{ inputs.test_filter }}"

Combining with Matrix ​

Run manual tests against multiple PHP versions:

yaml
on:
  workflow_dispatch:
    inputs:
      php_version:
        description: 'PHP version'
        required: false
        default: '8.3'
        type: choice
        options:
          - '8.2'
          - '8.3'
          - '8.4'

jobs:
  browser-tests:
    runs-on: ubuntu-latest
    steps:
      - name: Setup PHP
        uses: shivammathur/setup-php@v2
        with:
          php-version: ${{ inputs.php_version }}

Notifications ​

Add Slack notification on completion:

yaml
- name: Notify Slack
  if: always()
  uses: 8398a7/action-slack@v3
  with:
    status: ${{ job.status }}
    fields: repo,message,commit,author,action,eventName,ref,workflow
  env:
    SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK }}

Required vs Optional Inputs ​

  • Use required: false with sensible default for flexibility
  • Use required: true when the value must be explicitly chosen
  • Use type: choice for predefined options (prevents typos)
  • Use type: string for free-form input (branch names)

Troubleshooting ​

Checkout Fails: "The process '/usr/bin/git' failed" ​

Symptom: Workflow fails at checkout step with git exit code 1

Common causes:

  1. Wrong branch name: Some repos use main, others use master

    yaml
    # Check your repo's default branch and use it as default
    inputs:
      backend_branch:
        default: 'master'  # or 'main' depending on your repo
  2. Private repository: Need a PAT (Personal Access Token)

    yaml
    - uses: actions/checkout@v4
      with:
        repository: your-org/private-repo
        token: ${{ secrets.REPO_ACCESS_TOKEN }}

Composer Install Fails: "Package requires PHP >= 8.4" ​

Symptom: composer install fails with PHP version requirement errors

Cause: Some packages (especially Symfony 8.x) require PHP 8.4+

Solution: Update the PHP version in your workflow:

yaml
- name: Setup PHP
  uses: shivammathur/setup-php@v2
  with:
    php-version: '8.4'  # Match your composer.lock requirements

Check Your Requirements

Run composer show --locked | grep symfony locally to see which Symfony version your project uses. Symfony 8.x requires PHP 8.4+.

Released under the MIT License.