Search code examples
dockerdocker-composecontinuous-integrationgithub-actionsplaywright

Running Playwright in GitHub Actions with other Docker Containers


TL;DR

I am trying to run Playwright in my GitHub Actions workflow that involves multiple containers, including MongoDB and Firebase. Connections and Playwright tracing not working.

Problem

With the code below, I am unable to run the tests in the CI pipeline (I think) because the containers are failing to connect. The error in GitHub Actions implies the tests are not starting: Stalling out in the setup code and also Running 0 tests using 0 workers.

When I try to see what might be happening at startup via the Playwright Trace, the trace file is invalid. The error when attempting to upload Playwright Trace:

Could not load trace from setup-trace.zip. Make sure to upload a valid Playwright trace.

Playwright may require the mcr.microsoft.com/playwright:v1.31.1-focal container, but if I run it in that container, docker is not available for other steps.

Error in GitHub Actions

Run PLAYWRIGHT_SERVER="http://localhost:3000" npx playwright test

Running 0 tests using 0 workers

page.fill: Timeout 30000ms exceeded.
=========================== logs ===========================
waiting for locator('input[name="email"]')
============================================================

   at signup-page.ts:23

  21 |
  22 |   async signUp({ email, password }: UserInfo, ensureSuccess?: boolean) {
> 23 |     await this.page.fill('input[name="email"]', email);
     |                     ^
  24 |     await this.page.fill('input[name="password"]', password);
  25 |     await this.page.fill('input[name="repeatPassword"]', password);
  26 |     await this.page.click('button[type="submit"]');

    at SignupPage.signUp (/home/runner/work/<REPOSITORY>/e2e/signup-page.ts:23:21)
    at initApplicant01 (/home/runner/work/<REPOSITORY>/e2e/global-setup.ts:31:20)
    at globalSetup (/home/runner/work/<REPOSITORY>/e2e/global-setup.ts:66:7)

Error: Process completed with exit code 1.

Code

.github/workflows/build.yml:

name: Build

on:
  push:
    branches: [ main ]
  pull_request:
    branches: [ main ]

jobs:
  build_job:
    name: Build Dev
    runs-on: ubuntu-latest
    permissions:
      contents: 'read'
      id-token: 'write'
    outputs:
      release_tag_dev: ${{ steps.get-tag-dev.outputs.release_tag }}
    steps:
    - name: Setup
      uses: actions/checkout@v3
    - uses: actions/setup-node@v3
    - run: npm install
    - run: npm run build

    - name: Build and upload
      uses: docker/setup-buildx-action@v2
    - uses: docker/build-push-action@v4
      with:
        context: .
        tags: specific-build:latest
        outputs: type=docker,dest=./repository-image.tar
    - uses: actions/upload-artifact@v3
      with:
        name: repository-artifact
        path: ./repository-image.tar

    - name: Get Tag
      id: get-tag-dev
      run: echo "release_tag=$(date +%s%3N)-$(git rev-parse --short=8 HEAD)-prod" >> $GITHUB_OUTPUT

  test_job:
    name: Run Tests
    needs: [ build_job ]
    runs-on: ubuntu-latest
    permissions:
      contents: 'read'
      id-token: 'write'
    # container: mcr.microsoft.com/playwright:v1.31.1-focal
    steps:
    - uses: actions/checkout@v3

    - name: Download Repository TAR
      id: auth
      uses: google-github-actions/auth@v0
      with:
        token_format: access_token
        workload_identity_provider: <CREDENTIALS>
        service_account: <ACCOUNT>
        access_token_lifetime: 300s
    - uses: docker/login-action@v2
      with:
        registry: us-west1-docker.pkg.dev
        username: <USERNAME>
        password: ${{ steps.auth.outputs.access_token }}
    - uses: actions/download-artifact@v3
      with:
        name: repository-artifact
    - run: docker load --input ./repository-image.tar
    - run: RELEASE_TAG=${{ needs.build_job.outputs.release_tag_dev }} docker compose up -d

    - name: Run Tests
      uses: actions/setup-node@v3
    - run: npm ci --cache .npm --prefer-offline
    - run: npx playwright install
    - run: npx playwright test  
    # Everything below is just to view the Playwright Trace      
      continue-on-error: true
      env:
          PLAYWRIGHT_SERVER: "http://localhost:3000"
    - uses: actions/upload-artifact@v3
      with:
        name: upload-trace
        path: ./e2e/artifacts/setup-trace.zip

docker-compose.yml:

version: "1"

services:
  mongodb:
    image: "mongo"
    ports:
      - "27017:27017"
    command: mongod --bind_ip 0.0.0.0

  firebase:
    image: <PRIVATE_IMAGE>
    ports:
      - "4000:4000"
      - "4400:4400"
      - "4500:4500"
      - "9099:9099"
      - "9199:9199"
    command: --project=<REPOSITORY> emulators:start

  <REPOSITORY_IN_TEST>:
    image: "specific-build:latest"
    ports:
      - "3000:3000"
    environment:
      - DB_CONNECTION_STRING=mongodb://<PRIVATE_STRING>
      - DISABLE_DB_SSL=true
      - CORS_ORIGINS=http://localhost:3000
      - FIREBASE_AUTH_EMULATOR_HOST=firebase:9099
      - NEXT_PUBLIC_FIREBASE_AUTH_EMULATOR_HOST=firebase:9099
      - NEXT_PUBLIC_SENTRY_ENVIRONMENT=ci
      - <PLAID, SENTRY, and OTHER CONTEXT>

Solution

  • I resolved this. A few things were misconfigured:

    1. build_job in build.yml needed NEXT_PUBLIC_FIREBASE_AUTH_EMULATOR_HOST to be localhost:9099 to match mongo in docker-compose.yml
    2. mongo in docker-compose.yml needed both MONGO_INITDB_ROOT_USERNAME and MONGO_INITDB_ROOT_PASSWORD values
    3. <REPOSITORY_IN_TEST> in docker-compose.yml needed NEXT_PUBLIC_FIREBASE_AUTH_EMULATOR_HOST to be localhost:9099 instead of firebaseL:9099 to match build_job in build.yml

    The correct versions of the files are as follows:

    .github/workflows/build.yml:

    name: Build
    
    on:
      push:
        branches: [ main ]
      pull_request:
        branches: [ main ]
    
    jobs:
      build_job:
        name: Build Dev
        runs-on: ubuntu-latest
        permissions:
          contents: 'read'
          id-token: 'write'
        env:
          NEXT_PUBLIC_FIREBASE_AUTH_EMULATOR_HOST: localhost:9099
        outputs:
          release_tag_dev: ${{ steps.get-tag-dev.outputs.release_tag }}
        steps:
        - name: Setup
          uses: actions/checkout@v3
        - uses: actions/setup-node@v3
        - run: npm install
        - run: npm run build
    
        - name: Build and upload
          uses: docker/setup-buildx-action@v2
        - uses: docker/build-push-action@v4
          with:
            context: .
            tags: specific-build:latest
            outputs: type=docker,dest=./repository-image.tar
        - uses: actions/upload-artifact@v3
          with:
            name: repository-artifact
            path: ./repository-image.tar
    
        - name: Get Tag
          id: get-tag-dev
          run: echo "release_tag=$(date +%s%3N)-$(git rev-parse --short=8 HEAD)-prod" >> $GITHUB_OUTPUT
    
      test_job:
        name: Run Tests
        needs: [ build_job ]
        runs-on: ubuntu-latest
        environment: dev
        permissions:
          contents: 'read'
          id-token: 'write'
        steps:
        - uses: actions/checkout@v3
    
        - name: Download Repository TAR
          id: auth
          uses: google-github-actions/auth@v0
          with:
            token_format: access_token
            workload_identity_provider: <CREDENTIALS>
            service_account: <ACCOUNT>
            access_token_lifetime: 300s
        - uses: docker/login-action@v2
          with:
            registry: us-west1-docker.pkg.dev
            username: <USERNAME>
            password: ${{ steps.auth.outputs.access_token }}
        - uses: actions/download-artifact@v3
          with:
            name: repository-artifact
        - run: docker load --input ./repository-image.tar
        - run: RELEASE_TAG=${{ needs.build_job.outputs.release_tag_dev }} docker compose up -d
    
        - name: Run Tests
          uses: actions/setup-node@v3
        - run: npm ci --cache .npm --prefer-offline
        - run: npx playwright install
        - run: npx playwright test
          env:
              PLAYWRIGHT_SERVER: "http://localhost:3000"
    

    docker-compose.yml:

    version: "1"
    
    services:
      mongodb:
        image: "mongo"
        environment:
          MONGO_INITDB_ROOT_USERNAME: <FOUND IN DB_CONNECTION_STRING BELOW>
          MONGO_INITDB_ROOT_PASSWORD: <FOUND IN DB_CONNECTION_STRING BELOW>
        ports:
          - "27017:27017"
        command: mongod --bind_ip 0.0.0.0
    
      firebase:
        image: <PRIVATE_IMAGE>
        ports:
          - "4000:4000"
          - "4400:4400"
          - "4500:4500"
          - "9099:9099"
          - "9199:9199"
        command: --project=<REPOSITORY> emulators:start
    
      <REPOSITORY_IN_TEST>:
        image: "specific-build:latest"
        ports:
          - "3000:3000"
        environment:
          - DB_CONNECTION_STRING=mongodb://<PRIVATE_STRING>
          - DISABLE_DB_SSL=true
          - CORS_ORIGINS=http://localhost:3000
          - FIREBASE_AUTH_EMULATOR_HOST=firebase:9099
          - NEXT_PUBLIC_FIREBASE_AUTH_EMULATOR_HOST=localhost:9099
          - NEXT_PUBLIC_SENTRY_ENVIRONMENT=ci
          - <PLAID, SENTRY, and OTHER CONTEXT>