The GitHub Actions matrix strategy is a powerful feature that allows you to run the same job across multiple combinations of variables—like environments, operating systems, or software versions—without duplicating code. It's ideal for scenarios such as:

  • Testing across different browser environments
  • Running code against multiple versions of a language
  • Deploying across multiple platforms (e.g., Android and iOS)

By using a strategy.matrix section in your workflow, you can define all the variations you want to test or execute. GitHub will then automatically create a job for each combination. This not only helps keep workflows clean and scalable, but also provides a clear view of which combinations succeed or fail.

For example:

strategy:
  matrix:
    os: [ubuntu-latest, windows-latest]
    node: [16, 18]

This configuration will run four jobs in parallel:

  • Ubuntu + Node 16
  • Ubuntu + Node 18
  • Windows + Node 16
  • Windows + Node 18

Learn more in the official GitHub Docs

Cross-Browser UI Testing with GitHub Actions Matrix Strategy

When building UI automation workflows, one of the most common challenges is ensuring consistent functionality across different browsers. Initially, I handled this by creating separate GitHub Actions YAML files for each browser:

  • ui_chrome_pytest.yml
  • ui_firefox_pytest.yml

Each file had almost identical steps, with only minor differences:

🔍 Key Differences in the Old Files

ui_chrome_pytest.yml manually installs Google Chrome and ChromeDriver.

ui_firefox_pytest.yml uses the browser-actions/setup-firefox action for Firefox and GeckoDriver.

The pytest command uses --browser chrome in one and --browser firefox in the other.

Both upload the same artifacts, but the file names are not browser-specific (they would overwrite each other if run together).

This setup worked, but had several downsides:

  • ❌ Duplication of logic: Every change had to be applied in multiple files.
  • ❌ Error-prone: It’s easy to forget updating one file.
  • ❌ Scalability issues: Adding more browsers or platforms meant creating even more nearly-identical files.

The Solution: Matrix Strategy

So, to eliminate the duplication and better manage different browser setups, I leveraged the GitHub Actions matrix strategy. This approach allows each browser to be tested in parallel, using a single, unified workflow file.

To solve this, I created a new GitHub Actions workflow using a matrix strategy that dynamically switches between browsers:

ui_cross_browser_test.yml

name: UI Automation Tests

on:
  pull_request:
    branches:
      - main

jobs:
  build_and_test:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        browser: [chrome, firefox] # Matrix used to define browsers to test

    steps:
      - name: Checkout resume repository
        uses: actions/checkout@v4

      - name: Set up Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '22'

      - name: Install dependencies and build
        run: |
          yarn install
          yarn build

      - name: Start the application
        run: |
          yarn start &
        env:
          PORT: 3000

      - name: Set up Python
        uses: actions/setup-python@v5
        with:
          python-version: '3.12'

      # Chrome-specific installation
      - name: Install Chrome and ChromeDriver
        if: matrix.browser == 'chrome'
        run: |
          sudo apt update
          sudo apt install -y wget gnupg
          wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb
          sudo apt install -y ./google-chrome-stable_current_amd64.deb
          CHROMEDRIVER_VERSION=$(curl -sS https://chromedriver.storage.googleapis.com/LATEST_RELEASE)
          wget https://chromedriver.storage.googleapis.com/${CHROMEDRIVER_VERSION}/chromedriver_linux64.zip
          unzip chromedriver_linux64.zip
          sudo mv chromedriver /usr/local/bin/chromedriver
          sudo chmod +x /usr/local/bin/chromedriver

      # Firefox-specific setup using reusable action
      - name: Set up Firefox and GeckoDriver
        if: matrix.browser == 'firefox'
        uses: browser-actions/setup-firefox@latest

      - name: Clone the test repository
        run: |
          git clone https://github.com/serhatozdursun/serhatozdursun-ui-tests.git
        env:
          GITHUB_TOKEN: ${{ secrets.UI_TEST_TOKEN }}

      - name: Install Python dependencies
        working-directory: serhatozdursun-ui-tests
        run: |
          pip install -r requirements.txt

      # Matrix-aware pytest command
      - name: Run tests on ${{ matrix.browser }}
        working-directory: serhatozdursun-ui-tests
        id: run_tests
        run: |
          pytest --browser ${{ matrix.browser }} --base_url http://localhost:3000 --html=reports/html/report.html --junitxml=reports/report.xml

      - name: Upload HTML report
        if: always()
        uses: actions/upload-artifact@v4
        with:
          name: html-report-${{ matrix.browser }}
          path: serhatozdursun-ui-tests/reports/html
          retention-days: 5
        continue-on-error: true

      - name: Upload XML report
        if: always()
        uses: actions/upload-artifact@v4
        with:
          name: xml-report-${{ matrix.browser }}
          path: serhatozdursun-ui-tests/reports/report.xml
          retention-days: 5
        continue-on-error: true

View the full workflow file

Before vs After

Approach Pros Cons
Separate files Easy to isolate logic Repetitive, hard to scale
Matrix strategy Clean, DRY, scalable, browser-aware Slightly more complex YAML logic

Final Thoughts

Using GitHub Actions' matrix strategy is a game-changer for cross-browser testing. If you're managing separate workflows per browser, consolidating them will make your CI setup much more efficient.

Stay sharp, test smart! 🚀🧪

Connect with me on GitHub: serhatozdursun.com