[CI] Collect coverage from frontend during integration tests (#1472)

This change will allow us to collect frontend code coverage from our Selenium based integration tests.

Given that the frontend is embedded into the Go binary and the integration tests run with a compiled binary in Docker this poses some issues with the instrumented code and the ability for it to run in this manner. To fix this we need to relax Authelia's CSP for the integration tests. This is achieved by setting the env variable `ENVIRONMENT` to `dev`.
This commit is contained in:
Amir Zarrinkafsh 2020-11-19 12:50:34 +11:00 committed by GitHub
parent ec0af02aa3
commit 6db5455762
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 84 additions and 11 deletions

View File

@ -10,16 +10,11 @@ if [[ $BUILDKITE_PULL_REQUEST != "false" ]]; then
fi
if [[ ! $BUILDKITE_BRANCH =~ ^(v.*) ]] && [[ $BUILDKITE_COMMAND_EXIT_STATUS == 0 ]]; then
if [[ $BUILDKITE_LABEL == ":hammer_and_wrench: Unit Test" ]]; then
if [[ $BUILDKITE_LABEL == ":hammer_and_wrench: Unit Test" ]] || [[ $BUILDKITE_LABEL =~ ":selenium:" ]]; then
echo "--- :codecov: Upload coverage reports"
bash <(curl -s --connect-timeout 10 --retry 10 --retry-max-time 0 https://codecov.io/bash) -v -Z -c -f "coverage.txt" -F backend
bash <(curl -s --connect-timeout 10 --retry 10 --retry-max-time 0 https://codecov.io/bash) -v -Z -c -F frontend
fi
if [[ $BUILDKITE_LABEL =~ ":selenium:" ]]; then
echo "--- :codecov: Upload coverage reports"
bash <(curl -s --connect-timeout 10 --retry 10 --retry-max-time 0 https://codecov.io/bash) -v -Z -c -f "coverage.txt" -F backend
fi
fi
if [[ $BUILDKITE_LABEL =~ ":selenium:" ]] || [[ $BUILDKITE_LABEL =~ ":docker: Build Image" ]]; then

View File

@ -7,7 +7,7 @@ WORKDIR /node/src/app
COPY web .
# Install the dependencies and build
RUN yarn install --frozen-lockfile && INLINE_RUNTIME_CHUNK=false yarn build
RUN yarn install --frozen-lockfile && INLINE_RUNTIME_CHUNK=false yarn coverage
# =======================================
# ===== Build image for the backend =====

View File

@ -138,6 +138,21 @@ func runSuiteSetupTeardown(command string, suite string) error {
s := suites.GlobalRegistry.Get(selectedSuite)
if command == "teardown" {
if _, err := os.Stat("../../web/.nyc_output"); err == nil {
log.Infof("Generating frontend coverage reports for suite %s...", suite)
cmd := utils.Command("yarn", "report")
cmd.Dir = "web"
cmd.Env = os.Environ()
err := cmd.Run()
if err != nil {
log.Fatal(err)
}
}
}
cmd := utils.CommandWithStdout("go", "run", "cmd/authelia-suites/main.go", command, selectedSuite)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr

3
internal/server/const.go Normal file
View File

@ -0,0 +1,3 @@
package server
const dev = "dev"

View File

@ -3,6 +3,7 @@ package server
import (
"fmt"
"io/ioutil"
"os"
"text/template"
"github.com/valyala/fasthttp"
@ -36,7 +37,12 @@ func ServeIndex(publicDir, base, rememberMe, resetPassword string) fasthttp.Requ
nonce := utils.RandomString(32, alphaNumericRunes)
ctx.SetContentType("text/html; charset=utf-8")
if os.Getenv("ENVIRONMENT") == dev {
ctx.Response.Header.Add("Content-Security-Policy", fmt.Sprintf("default-src 'self' 'unsafe-eval'; object-src 'none'; style-src 'self' 'nonce-%s'", nonce))
} else {
ctx.Response.Header.Add("Content-Security-Policy", fmt.Sprintf("default-src 'self'; object-src 'none'; style-src 'self' 'nonce-%s'", nonce))
}
err := tmpl.Execute(ctx.Response.BodyWriter(), struct{ Base, CSPNonce, RememberMe, ResetPassword string }{Base: base, CSPNonce: nonce, RememberMe: rememberMe, ResetPassword: resetPassword})
if err != nil {

View File

@ -99,7 +99,7 @@ func StartServer(configuration schema.Configuration, providers middlewares.Provi
// Configure DUO api endpoint only if configuration exists.
if configuration.DuoAPI != nil {
var duoAPI duo.API
if os.Getenv("ENVIRONMENT") == "dev" {
if os.Getenv("ENVIRONMENT") == dev {
duoAPI = duo.NewDuoAPI(duoapi.NewDuoApi(
configuration.DuoAPI.IntegrationKey,
configuration.DuoAPI.SecretKey,

View File

@ -24,6 +24,7 @@ authentication_backend:
groups_filter: (&(member={dn})(objectclass=groupOfNames))
group_name_attribute: cn
mail_attribute: mail
display_name_attribute: displayName
user: cn=admin,dc=example,dc=com
password: password

View File

@ -22,6 +22,7 @@ authentication_backend:
groups_filter: (&(member={dn})(objectclass=groupOfNames))
group_name_attribute: cn
mail_attribute: mail
display_name_attribute: displayName
user: cn=admin,dc=example,dc=com
access_control:

View File

@ -40,6 +40,8 @@ spec:
value: /app/secrets/session
- name: AUTHELIA_STORAGE_MYSQL_PASSWORD_FILE
value: /app/secrets/sql_password
- name: ENVIRONMENT
value: dev
volumes:
- name: config-volume
configMap:

View File

@ -2,12 +2,15 @@ package suites
import (
"context"
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"os"
"strconv"
"strings"
"testing"
"time"
"github.com/stretchr/testify/require"
"github.com/tebeka/selenium"
@ -82,8 +85,31 @@ func StartWebDriver() (*WebDriverSession, error) {
// Stop stop the selenium session.
func (wds *WebDriverSession) Stop() error {
err := wds.WebDriver.Quit()
var coverage map[string]interface{}
coverageDir := "../../web/.nyc_output"
time := time.Now()
resp, err := wds.WebDriver.ExecuteScriptRaw("return JSON.stringify(window.__coverage__)", nil)
if err != nil {
return err
}
err = json.Unmarshal(resp, &coverage)
if err != nil {
return err
}
coverageData := fmt.Sprintf("%s", coverage["value"])
_ = os.MkdirAll(coverageDir, 0775)
err = ioutil.WriteFile(fmt.Sprintf("%s/coverage-%d.json", coverageDir, time.Unix()), []byte(coverageData), 0664) //nolint:gosec
if err != nil {
return err
}
err = wds.WebDriver.Quit()
if err != nil {
return err
}

5
web/craco.config.js Normal file
View File

@ -0,0 +1,5 @@
module.exports = {
babel: {
plugins: [ "babel-plugin-istanbul" ]
}
};

View File

@ -3,6 +3,7 @@
"version": "0.1.0",
"private": true,
"dependencies": {
"@craco/craco": "^5.8.0",
"@fortawesome/fontawesome-svg-core": "^1.2.32",
"@fortawesome/free-regular-svg-icons": "^5.15.1",
"@fortawesome/free-solid-svg-icons": "^5.15.1",
@ -40,9 +41,11 @@
"u2f-api": "^1.1.1"
},
"scripts": {
"start": "react-scripts start",
"start": "craco start",
"build": "react-scripts build",
"coverage": "craco build",
"test": "react-scripts test --coverage --no-cache",
"report": "nyc report -r clover -r json -r lcov -r text",
"eject": "react-scripts eject"
},
"eslintConfig": {

View File

@ -1159,6 +1159,15 @@
exec-sh "^0.3.2"
minimist "^1.2.0"
"@craco/craco@^5.8.0":
version "5.8.0"
resolved "https://registry.yarnpkg.com/@craco/craco/-/craco-5.8.0.tgz#2a0f551290a5eab353b615de4d7093dd83785777"
integrity sha512-4rhusETLD7rJ195GxOK9VmVdv/VD4jawFxc9hcQ9TrZ3/9ny+qwc0uW+08qu9GYwEF9Eb9meSeSvpWjaqdDr1Q==
dependencies:
cross-spawn "^7.0.0"
lodash "^4.17.15"
webpack-merge "^4.2.2"
"@csstools/convert-colors@^1.4.0":
version "1.4.0"
resolved "https://registry.yarnpkg.com/@csstools/convert-colors/-/convert-colors-1.4.0.tgz#ad495dc41b12e75d588c6db8b9834f08fa131eb7"
@ -11979,6 +11988,13 @@ webpack-manifest-plugin@2.2.0:
object.entries "^1.1.0"
tapable "^1.0.0"
webpack-merge@^4.2.2:
version "4.2.2"
resolved "https://registry.yarnpkg.com/webpack-merge/-/webpack-merge-4.2.2.tgz#a27c52ea783d1398afd2087f547d7b9d2f43634d"
integrity sha512-TUE1UGoTX2Cd42j3krGYqObZbOD+xF7u28WB7tfUordytSjbWTIjK/8V0amkBfTYN4/pB/GIDlJZZ657BGG19g==
dependencies:
lodash "^4.17.15"
webpack-sources@^1.1.0, webpack-sources@^1.3.0, webpack-sources@^1.4.0, webpack-sources@^1.4.1, webpack-sources@^1.4.3:
version "1.4.3"
resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-1.4.3.tgz#eedd8ec0b928fbf1cbfe994e22d2d890f330a933"