【CircleCI2.0】JavaScript に対応するように設定

## はじめに

**CircleCI で JavaScript を扱える**ように設定しました。
今まで問題なく動作していた `CircleCI` のテストでしたが、[あること](##原因)をしてから通らなくなったので対処方法を記していきます。

今までの設定ファイルは[こちら](https://qiita.com/suzu12/items/2322b7ba14155371a949)です。
## 目次

- 環境

- 問題
- 原因
    - RSpecで`js: true`を使用
    - 今回 **ローカル環境**と **CircleCI環境** をあわせるために行った変更
    
- 解決策
    - `spec/support/capybara.rb`
        - 変更前
        - 変更後
    - `spec/rails_helper.rb`
    - `Dockerfile`
    - `.circleci/config.yml`
    - `.circleci/config.yml 全体`
    - `config/database.yml.ci`

- まとめ
- おわりに
- 参考文献

## 環境

- Ruby: 2.6.6
- Bundler: 2.0.2
- MySQL: 8.0
- CircleCI: 2.0
- yarnでjQuery( JavaScript )を使用。( ※ gemとyarnでの使用方法の違いでテストの結果に影響でることが考えられるため。 )

## 問題

```ruby
SocketError: 
Failed to open TCP connection to chrome:4444 
(getaddrinfo: No address associated with hostname)
# chrome:4444へのTCP接続のオープンに失敗
```

```ruby
SocketError:　getaddrinfo: No address associated with hostname
# ホスト名に関連付けられたアドレスはありません
```

## 原因

#### RSpecでjs: trueを使用

RSpecで **JavaScriptのテストを実装** したところ**ローカル環境で通っていたテストがCircleCIの自動テスト**では通りませんでした。
結論として、**ローカル環境とCircleCIの環境の差異が原因**かと思います。
ローカルではchromeコンテナを立ち上げて `System Spec` を通していましたが、 `CircleCI の設定ファイル` でも設定が必要なのでエラーが起きていました。
#### 今回ローカル環境とCircleCI環境をあわせるために行った変更

- **chromeコンテナ から Dockerfileで chrome** を追加するようにしたこと
- **Chrome使用時のURLを指定していた**ので削除したこと
- `CircleCI の設定ファイル` で **Chrome の追加**と **background で使用**するようにしたこと

## 解決策

#### spec/support/capybara.rb

大幅に変更したように見えますが、`url` を指定していないというのがポイントです。

```ruby:変更前
require 'capybara/rspec'
require 'selenium-webdriver'

module CapybaraSupport
  Capybara.javascript_driver = :selenium_chrome_headless
  Capybara.default_driver    = :selenium_chrome_headless
  Capybara.register_driver :selenium_chrome_headless do |app|
    url = 'http://chrome:4444/wd/hub'
    caps = ::Selenium::WebDriver::Remote::Capabilities.chrome(
      'goog:chromeOptions' => {
        'args' => [
          'no-sandbox',
          'headless',
          'disable-gpu',
          'window-size=1680,1050'
        ]
      }
    )
    Capybara::Selenium::Driver.new(app, browser: :chrome, url: url, desired_capabilities: caps)
  end
```

```ruby:変更後
require 'capybara/rspec'
require 'selenium-webdriver'

Capybara.register_driver :selenium_chrome_headless do |app|
  options = ::Selenium::WebDriver::Chrome::Options.new

  options.add_argument('--headless')
  options.add_argument('--no-sandbox')
  options.add_argument('--disable-dev-shm-usage')
  options.add_argument('--window-size=1400,1400')

  driver = Capybara::Selenium::Driver.new(app, browser: :chrome, options: options)
end

Capybara.javascript_driver = :selenium_chrome_headless
```

#### spec/rails_helper.rb

```ruby
RSpec.configure do |config|
  config.before(:each, type: :system) do
    driven_by :rack_test
  end

  config.before(:each, type: :system, js: true) do
    driven_by :selenium_chrome_headless
  end
end
```

#### Dockerfile
**ローカル環境のテストで Chrome を使用**したいので `Dockerfile`で **Chromeの追加** をしています。
この記述を `RUN mkdir /myapp` の前に書きました。

```ruby
RUN apt-get update && apt-get install -y unzip && \
    CHROME_DRIVER_VERSION=`curl -sS chromedriver.storage.googleapis.com/LATEST_RELEASE` && \
    wget -N http://chromedriver.storage.googleapis.com/$CHROME_DRIVER_VERSION/chromedriver_linux64.zip -P ~/ && \
    unzip ~/chromedriver_linux64.zip -d ~/ && \
    rm ~/chromedriver_linux64.zip && \
    chown root:root ~/chromedriver && \
    chmod 755 ~/chromedriver && \
    mv ~/chromedriver /usr/bin/chromedriver && \
    sh -c 'wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key add -' && \
    sh -c 'echo "deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main" >> /etc/apt/sources.list.d/google-chrome.list' && \
    apt-get update && apt-get install -y google-chrome-stable

# ルート直下にmyappという名前で作業ディレクトリを作成（コンテナ内のアプリケーションディレクトリ）
RUN mkdir /myapp
WORKDIR /myapp
```

#### .circleci/config.yml

CircleCIの設定ファイル。**Chromeを追加** することで **ローカル環境との差異** をなくすようにしました。

```ruby
    steps:
      - run:
          name: Chrome Driver Install
          command: |
            curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add -
            echo "deb https://dl.yarnpkg.com/debian/ stable main" | sudo tee /etc/apt/sources.list.d/yarn.list
            sudo apt-get update && sudo apt-get install -y unzip
            wget -N http://chromedriver.storage.googleapis.com/87.0.4280.88/chromedriver_linux64.zip -P ~/
            unzip ~/chromedriver_linux64.zip -d ~/
            rm ~/chromedriver_linux64.zip
            sudo chown root:root ~/chromedriver
            sudo chmod 755 ~/chromedriver
            sudo mv ~/chromedriver /usr/bin/chromedriver
            sh -c 'wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | sudo apt-key add -'
            sudo sh -c 'echo "deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main" >> /etc/apt/sources.list.d/google-chrome.list'
            sudo apt-get update && sudo apt-get install -y google-chrome-stable
          background: true
```

### 2021/11/25追記


@tomoya128 さんからのコメントよりこの部分は以下のように記述できるとお聞きしました。

```ruby
    steps:
      - run:
          command: chromedriver
          background: true
```

また、`Ruby 2.5` 以上を使用している場合は、`webdrivers(gem)` を利用すると簡単に設定できるようです。
試してみてはどうでしょうか。

https://github.com/titusfortner/webdrivers/wiki/Continuous-Integration-configuration#circle-ci

#### .circleci/config.yml 全体

RSpecを動かすところで `no_output_timeout: 15m` と記述しています。
こちらはなくともテストには直接関係ないので削除しても大丈夫です。

なにも設定しなければ**標準で10分の制限時間**があり、それを過ぎると**タイムアウトエラー**になってしまうので念の為付けております。テストの数とかにもよると思いますが、実際にはほんの数分で終わるので不要です。

```ruby
version: 2

jobs:
  build:
    docker:
      - image: circleci/ruby:2.6.6-node-browsers
        environment:
          - BUNDLER_VERSION: 2.0.2
          - RAILS_ENV: 'test'
      - image: circleci/mysql:8.0
        command: [--default-authentication-plugin=mysql_native_password]
        environment:
          - MYSQL_USER: root
          - MYSQL_DB: ci_test
          - MYSQL_ROOT_HOST: "127.0.0.1"

    working_directory: ~/myapp

    steps:
      - run:
          name: Chrome Driver Install
          command: |
            curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add -
            echo "deb https://dl.yarnpkg.com/debian/ stable main" | sudo tee /etc/apt/sources.list.d/yarn.list
            sudo apt-get update && sudo apt-get install -y unzip
            wget -N http://chromedriver.storage.googleapis.com/87.0.4280.88/chromedriver_linux64.zip -P ~/
            unzip ~/chromedriver_linux64.zip -d ~/
            rm ~/chromedriver_linux64.zip
            sudo chown root:root ~/chromedriver
            sudo chmod 755 ~/chromedriver
            sudo mv ~/chromedriver /usr/bin/chromedriver
            sh -c 'wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | sudo apt-key add -'
            sudo sh -c 'echo "deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main" >> /etc/apt/sources.list.d/google-chrome.list'
            sudo apt-get update && sudo apt-get install -y google-chrome-stable
          background: true

      - checkout

      - restore_cache:
          keys:
            - v1-dependencies-{{ checksum "Gemfile.lock" }}
            - v1-dependencies-

      - run:
          name: install dependencies
          command: |
            gem install bundler -v 2.0.2
            bundle install --jobs=4 --retry=3 --path vendor/bundle

      - save_cache:
          key: v1-dependencies-{{ checksum "Gemfile.lock" }}
          paths:
            - ./vendor/bundle

      - run: mv config/database.yml.ci config/database.yml
      - run: yarn install
      - run: bundle exec rake db:create
      - run: bundle exec rake db:schema:load

      - run:
          name: run tests
          command: |
            mkdir /tmp/test-results
            TEST_FILES="$(circleci tests glob "spec/**/*_spec.rb" | \
              circleci tests split --split-by=timings)"
            bundle exec rspec \
              --format RspecJunitFormatter \
              --out /tmp/test-results/rspec.xml \
              --format progress \
              $TEST_FILES
          no_output_timeout: 15m

      - store_test_results:
          path: /tmp/test-results
      - store_artifacts:
          path: /tmp/test-results
          destination: test-results
```

#### config/database.yml.ci

```ruby
test:
  adapter: mysql2
  encoding: utf8
  pool: 5
  username: 'root'
  port: 3306
  host: '127.0.0.1'
  database: ci_test
```

## まとめ

- `Dockerfile` と `CircleCIの設定ファイル` で **Chromeを追加**
- ローカルで通用してもCircleCI上で失敗することがある。
- ありがたいことに記事（情報）がたくさんあるので、その中で自分がどの問題に直面しているのかを理解することが大事。

## おわりに

[前回の記事](https://qiita.com/suzu12/items/2322b7ba14155371a949)に変更前の `CircleCIの設定ファイル` があります。（厳密に言えば少しリファクタなどしています）
**JavaScript適用前** ですが、 `CircleCI` についてまとめていますのでよろしければご覧ください。

## 参考文献

- [CircleCIでChromeDriverを立ち上げる](https://qiita.com/h-hiroki/items/bfe5357144189b7cb922)
- [RailsのSystemTest（headless chrome）をDocker上で動かす](https://qiita.com/t-koshi/items/97adab8628d201e85d10)
- [【Rails6.0×MySQL8.0×RSpec×Circle CI】CI/CD自動化で出たエラー集](https://qiita.com/shun0211/items/fdf7c962dbf95e0a55fa)
- [CircleCI2.0をRailsアプリに導入してみた](https://qiita.com/suzu12/items/2322b7ba14155371a949)