Visual Studio CodeのDev Containerを使ったAngular開発でヘッドレスChromeを使ってテストを実施する

### TL;DR
- Angularのテストの練習中に問題発生
- Visual Studio CodeのDev ContainerでAngularの開発環境を構築した際にKarmaでエラー
- Chromeもコンテナ内に入れてヘッドレス環境を構築することにした
- 設定項目で何箇所か注意する点があったので記事を書いた

### 環境
- Windows10
- Docker Desktop 2.2.0.3
- docker-composeはDocker Desktopに同梱
- visual studio code 1.42.1[拡張機能Remote - Containers使用]
- Angular 9

### 完成したリポジトリ
https://github.com/MegaBlackLabel/angular-unittest-practice

### ファイル
Node.jsのDocker環境とAngular-cliで作った環境のうちポイントのファイルを記載。

```dockerfile:Dockerfile
#-------------------------------------------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See https://go.microsoft.com/fwlink/?linkid=2090316 for license information.
#-------------------------------------------------------------------------------------------------------------

# For information on the contents of the container image below, see following Dockerfile:
# https://github.com/microsoft/vscode-dev-containers/tree/v0.43.0/containers/javascript-node-12/.devcontainer/Dockerfile
FROM mcr.microsoft.com/vscode/devcontainers/javascript-node:0-12

# The image referenced above includes a non-root user with sudo access. Add 
# the "remoteUser" property to devcontainer.json to use it. On Linux, the container 
# user's GID/UIDs will be updated to match your local UID/GID when using the image
# or dockerFile property. Update USER_UID/USER_GID below if you are using the
# dockerComposeFile property or want the image itself to start with different ID
# values. See https://aka.ms/vscode-remote/containers/non-root-user for details.
ARG USER_UID=1000
ARG USER_GID=$USER_UID
ARG USERNAME=node

# [Optional] Update UID/GID if needed
RUN if [ "$USER_GID" != "1000" ] || [ "$USER_UID" != "1000" ]; then \
        groupmod --gid $USER_GID $USERNAME \
        && usermod --uid $USER_UID --gid $USER_GID $USERNAME \
        && chown -R $USER_UID:$USER_GID /home/$USERNAME; \
    fi

# *************************************************************
# * Uncomment this section to use RUN instructions to install *
# * any needed dependencies after executing "apt-get update". *
# * See https://docs.docker.com/engine/reference/builder/#run *
# *************************************************************
ENV DEBIAN_FRONTEND=noninteractive
RUN apt-get update \
    && apt-get install -y --no-install-recommends chromium \
    # Clean up
    && apt-get autoremove -y \
    && apt-get clean -y \
    && rm -rf /var/lib/apt/lists/*
ENV DEBIAN_FRONTEND=dialog
ENV CHROME_BIN=chromium

USER $USER_UID

COPY docker-entrypoint.sh ./
COPY ./frontend ./

RUN npm install -g @angular/cli

WORKDIR /frontend

EXPOSE 4200

ENTRYPOINT ["/docker-entrypoint.sh"]
```

- DockerfileではDevContainer用にnodeユーザを追加
- ヘッドレスChromeを使うためchromiumをインストール。環境変数CHROME_BINを追加

```javascript:karma.conf.js
// Karma configuration file, see link for more information
// https://karma-runner.github.io/1.0/config/configuration-file.html

module.exports = function (config) {
  config.set({
    basePath: '',
    frameworks: ['jasmine', '@angular-devkit/build-angular'],
    plugins: [
      require('karma-jasmine'),
      require('karma-chrome-launcher'),
      require('karma-jasmine-html-reporter'),
      require('karma-coverage-istanbul-reporter'),
      require('@angular-devkit/build-angular/plugins/karma')
    ],
    client: {
      clearContext: false // leave Jasmine Spec Runner output visible in browser
    },
    coverageIstanbulReporter: {
      dir: require('path').join(__dirname, './coverage/frontend'),
      reports: ['html', 'lcovonly', 'text-summary'],
      fixWebpackSourcePaths: true
    },
    reporters: ['progress', 'kjhtml'],
    port: 9876,
    colors: true,
    logLevel: config.LOG_INFO,
    autoWatch: true,
    browsers: ['ChromeHeadlessNoSandbox'],
    customLaunchers: {
      ChromeHeadlessNoSandbox: {
        base: 'ChromeHeadless',
        flags: ['--no-sandbox']
      }
    },
    singleRun: false,
    restartOnFileChange: true
  });
};
```

- customLaunchersの項目追加。「ChromeHeadlessNoSandbox」としてChromeHeadlessで--no-sandboxとする
- browsersを追加したChromeHeadlessNoSandboxに変更

### ng test実行結果
```shell:実行結果
node@13fd2b410b71:/frontend$ yarn test
yarn run v1.22.4
$ ng test --watch=false
10% building 2/2 modules 0 active17 03 2020 07:54:26.175:INFO [karma-server]: Karma v4.3.0 server started at http://0.0.0.0:9876/
17 03 2020 07:54:26.178:INFO [launcher]: Launching browsers ChromeHeadlessNoSandbox with concurrency unlimited
17 03 2020 07:54:26.195:INFO [launcher]: Starting browser ChromeHeadless
17 03 2020 07:54:34.990:INFO [HeadlessChrome 73.0.3683 (Linux 0.0.0)]: Connected on socket rFmsBL9G_zLc7S4aAAAA with id 68373234
HeadlessChrome 73.0.3683 (Linux 0.0.0): Executed 3 of 3 SUCCESS (0.795 secs / 0.682 secs)
TOTAL: 3 SUCCESS
TOTAL: 3 SUCCESS
Done in 24.36s.
```

### まとめ
ホスト環境に依存せずにKarmaのテストが可能になりました。このままCIも回せるようになるのでヘッドレス環境万歳。