laravel / dusk

Laravel Dusk provides simple end-to-end testing and browser automation.
https://laravel.com/docs/dusk
MIT License
1.87k stars 320 forks source link

Github Actions: Chrome failed to start: exited abnormally. (unknown error: DevToolsActivePort file doesn't exist) #910

Closed kyranb closed 3 years ago

kyranb commented 3 years ago

Description:

Using these steps: https://laravel.com/docs/8.x/dusk#running-tests-on-github-actions

Results in the following error message below.

Some others have mentioned this issue here:

https://github.com/laravel/dusk/issues/768

https://stackoverflow.com/questions/56278372/dusk-test-for-browser-devtoolsactiveport-file-doesnt-exist

https://stackoverflow.com/questions/49837939/laravel-dusk-chrome-driver-timeout

1) Tests\Browser\LoginTest::test_login_page_loads
Facebook\WebDriver\Exception\UnknownErrorException: unknown error: Chrome failed to start: exited abnormally.
  (unknown error: DevToolsActivePort file doesn't exist)
  (The process started from chrome location /usr/bin/google-chrome is no longer running, so ChromeDriver is assuming that Chrome has crashed.)

Steps To Reproduce:

Running php artisan dusk in Github actions.

crynobone commented 3 years ago

Can you share your GitHub Action yaml?

driesvints commented 3 years ago

Closing this issue because it's inactive, already solved, old or not relevant anymore. Feel to open up a new issue if you're still experiencing this.

kyranb commented 3 years ago

Sure here is the yaml file

name: Test
on:
  push:
    branches: ["**"]
    paths: ["src/**"]
  pull_request:
    branches: ["**"]
    paths: ["src/**"]

  # Allows you to run this workflow manually from the Actions tab
  workflow_dispatch:

jobs:
  laravel-tests:
    runs-on: ubuntu-latest
    defaults:
      run:
        working-directory: "src"
    timeout-minutes: 30
    env:
      APP_ENV: testing
      APP_URL: http://app.example.test
      APP_DOMAIN: app.example.test
      DB_HOST: 127.0.0.1
      DB_DATABASE: example_ci
      DB_USERNAME: username
      DB_PASSWORD: password
      AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY }}
      AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET }}
      STRIPE_KEY: ${{ secrets.STRIPE_KEY }}
      STRIPE_SECRET: ${{ secrets.STRIPE_SECRET }}
      STRIPE_WEBHOOK_SECRET: ${{ secrets.STRIPE_WEBHOOK_SECRET }}
      NOVA_USERNAME: ${{ secrets.NOVA_USERNAME }}
      NOVA_PASSWORD: ${{ secrets.NOVA_PASSWORD }}
    services:
      mysql:
        image: mysql:8
        env:
          MYSQL_ALLOW_EMPTY_PASSWORD: false
          MYSQL_ROOT_PASSWORD: password
          MYSQL_DATABASE: example_ci
        ## map the "external" 33306 port with the "internal" 3306
        ports:
          - 3306/tcp
        # Set health checks to wait until mysql database has started (it takes some seconds to start)
        options: >-
          --health-cmd="mysqladmin ping"
          --health-interval=10s
          --health-timeout=5s
          --health-retries=3
      redis:
        image: redis
        ports:
          - 6379/tcp
        options: --health-cmd="redis-cli ping" --health-interval=10s --health-timeout=5s --health-retries=3

    strategy:
      fail-fast: false
      matrix:
        php-versions: ["7.4"] # To add 8.0
        dependency-stability: [ prefer-stable ]

    name: Test P${{ matrix.php-versions }} - L${{ matrix.laravel }} - ${{ matrix.dependency-stability }} - ${{ matrix.operating-system}}

    steps:
      - name: Checkout
        uses: actions/checkout@v2
      - name: Setup Node.js
        uses: actions/setup-node@v1
        with:
          node-version: '16.x'
      - name: Cache node_modules directory
        uses: actions/cache@v2
        id: node_modules-cache
        with:
          path: node_modules
          key: ${{ runner.OS }}-build-${{ hashFiles('**/package.json') }}-${{ hashFiles('**/package-lock.json') }}
      - name: Install NPM packages
        if: steps.node_modules-cache.outputs.cache-hit != 'true' || true
        run: npm ci
      - name: Build frontend
        run: npm run production
      - name: Install PHP versions
        uses: shivammathur/setup-php@v2
        with:
          php-version: ${{ matrix.php-versions }}
          extensions: mbstring, dom, fileinfo, mysql, redis
          tools: composer:v2, phpunit, cs2pr, phpstan, php-cs-fixer
          coverage: none
      - name: Get Composer Cache Directory 2
        id: composer-cache
        run: |
          echo "::set-output name=dir::$(composer config cache-files-dir)"
      - uses: actions/cache@v2
        id: actions-cache
        with:
          path: ${{ steps.composer-cache.outputs.dir }}
          key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
          restore-keys: |
            ${{ runner.os }}-composer-
      - name: Cache PHP dependencies
        uses: actions/cache@v2
        id: vendor-cache
        with:
          path: vendor
          key: ${{ runner.OS }}-build-${{ hashFiles('**/composer.lock') }}      
      - name: Copy .env
        run: php -r "file_exists('.env') || copy('.env.ci', '.env');"
      - name: Authenticate laravel nova
        run: composer config http-basic.nova.laravel.com ${NOVA_USERNAME} ${NOVA_PASSWORD}
      - name: Install Dependencies
        #if: steps.vendor-cache.outputs.cache-hit != 'true'
        run: composer install --no-ansi --no-interaction --no-scripts --no-progress --prefer-dist --optimize-autoloader
      - name: Generate key
        run: php artisan key:generate
      - name: Set Directory Permissions
        run: chmod -R 777 storage bootstrap/cache
      - name: Cache Views
        run: php artisan view:cache
      - name: Cache Routes
        run: php artisan route:cache
      - name: Cache Routes
        run: php artisan config:clear

      - name: Run Migrations
      # Set environment
        env:
          DB_PORT: ${{ job.services.mysql.ports['3306'] }}
          REDIS_PORT: ${{ job.services.redis.ports['6379'] }}

        run: php artisan migrate --force --seed -v

      - name: Show dir
        run: pwd
      - name: PHP Version
        run: php --version

      # Code quality
      - name: Setup Problem Matchers for PHP
        run: echo "::add-matcher::${{ runner.tool_cache }}/php.json"
      - name: Setup Problem Matchers for PHPUnit
        run: echo "::add-matcher::${{ runner.tool_cache }}/phpunit.json"
      - name: Execute tests (Unit and Feature tests) via PHPUnit
      # Set environment
        env:
          DB_PORT: ${{ job.services.mysql.ports['3306'] }}
          REDIS_PORT: ${{ job.services.redis.ports['6379'] }}

        # run: vendor/bin/phpunit --testdox --verbose
        run:  php artisan test --parallel --verbose

      - name: Execute Code Sniffer via phpcs
        run: vendor/bin/phpcs --standard=PSR12 app
        continue-on-error: true

      - name: Execute Code Static Analysis (PHP Stan + Larastan)
        run: vendor/bin/phpstan analyse app -c ./vendor/nunomaduro/larastan/extension.neon  --level=4 --no-progress
        continue-on-error: true

      - name: Add hosts to /etc/hosts
        run: |
          sudo echo "127.0.0.1 app.example.test" | sudo tee -a /etc/hosts

      - name: Upgrade Chrome Driver
        run: php artisan dusk:chrome-driver `/opt/google/chrome/chrome --version | cut -d " " -f3 | cut -d "." -f1`
      - name: Start Chrome Driver
        run: ./vendor/laravel/dusk/bin/chromedriver-linux &
      - name: Run Laravel Server
        run: php artisan serve --no-reload &
      - name: Run Dusk Tests
        env:
          APP_ENV: testing
          APP_URL: http://app.example.test:8000
          APP_DOMAIN: app.example.test
          DB_PORT: ${{ job.services.mysql.ports['3306'] }}
          REDIS_PORT: ${{ job.services.redis.ports['6379'] }}
        run: php artisan dusk

# Upload logs
      - name: Upload Laravel Logs
        if: failure()
        uses: actions/upload-artifact@v2
        with:
          retention-days: 3
          name: logs
          path: src/storage/logs
      - name: Upload Screenshots
        if: failure()
        uses: actions/upload-artifact@v2
        with:
          name: screenshots
          path: /tests/Browser/screenshots
      - name: Upload Console Logs
        if: failure()
        uses: actions/upload-artifact@v2
        with:
          name: console
          path: /tests/Browser/console
kyranb commented 3 years ago

See here for someone else having the same issue too:

https://stackoverflow.com/questions/68678351/laravel-dusk-chrome-browser-unable-to-lunch

crynobone commented 3 years ago
APP_URL: http://app.example.test:8000
APP_DOMAIN: app.example.test

I am not sure how GitHub Action can use app.example.test and point it to php artisan serve, which normally would default to http://localhost:8000

crynobone commented 3 years ago

See here for someone else having the same issue too:

https://stackoverflow.com/questions/68678351/laravel-dusk-chrome-browser-unable-to-lunch

This seems unrelated, CI environment would require --headless to be enabled by default since there's no GUI to work with and dusk command should already be checking for this, https://github.com/laravel/dusk/blob/6.x/src/Console/DuskCommand.php#L139-L141

kyranb commented 3 years ago

Here's another example of someone mentioning that they had to use --disable-gpu to get Chrome to work with github actions:

https://www.reddit.com/r/laravel/comments/gike2u/github_actions_dusk/fqrlwku/

kyranb commented 3 years ago
APP_URL: http://app.example.test:8000
APP_DOMAIN: app.example.test

I am not sure how GitHub Action can use app.example.test and point it to php artisan serve, which normally would default to http://localhost:8000

This step here adds the host app.example.test to the hosts file /etc/hosts

- name: Add hosts to /etc/hosts
        run: |
          sudo echo "127.0.0.1 app.example.test" | sudo tee -a /etc/hosts

Unless there is another workaround for apps that require a hostname (that serve multiple domains from the one app and can't just use localhost)?

kyranb commented 3 years ago

@driesvints Can this be re-opened?

driesvints commented 3 years ago

We're not going to revisit this at the time. If you feel the docs can be improved then feel free to send in a PR 👍

njoguamos commented 2 years ago

I had the same issue. The problem was that I had disable headless option in the DuskTestCase file

<?php

//....

abstract class DuskTestCase extends BaseTestCase
{
    //....

    protected function driver()
    {
        $options = (new ChromeOptions)->addArguments(collect([
            '--window-size=1920,1080',
        ])->unless($this->hasHeadlessDisabled(), function ($items) {
            return $items->merge([
                '--disable-gpu',
                '--headless', //<-- THIS OPTION HAS TO BE ENABLED
            ]);
        })->all());

        ///....
    }
}