From 8da083e91e12fdf7b34470f8bc01ae43daa83ee5 Mon Sep 17 00:00:00 2001 From: Kelani Tolulope Date: Thu, 16 Nov 2023 15:43:03 +0100 Subject: [PATCH] Update github workflow with CY tests --- .github/workflows/assets/client/config.js | 4 + .../assets/client/vue.config-builder.js | 171 +++++++++++++ .github/workflows/assets/docker-compose.yaml | 59 +++++ .github/workflows/test-e2e.yml | 238 ++++++++++++++++++ 4 files changed, 472 insertions(+) create mode 100644 .github/workflows/assets/client/config.js create mode 100644 .github/workflows/assets/client/vue.config-builder.js create mode 100644 .github/workflows/assets/docker-compose.yaml create mode 100644 .github/workflows/test-e2e.yml diff --git a/.github/workflows/assets/client/config.js b/.github/workflows/assets/client/config.js new file mode 100644 index 000000000..5a1ef8564 --- /dev/null +++ b/.github/workflows/assets/client/config.js @@ -0,0 +1,4 @@ +// Corteza API location +window.CortezaAPI = '//localhost:8888/api' + +window.i18nPseudoModeEnabled = false diff --git a/.github/workflows/assets/client/vue.config-builder.js b/.github/workflows/assets/client/vue.config-builder.js new file mode 100644 index 000000000..003589aa5 --- /dev/null +++ b/.github/workflows/assets/client/vue.config-builder.js @@ -0,0 +1,171 @@ +var webpack = require('webpack') +var exec = require('child_process').execSync +var path = require('path') +var Vue = require('vue') + +module.exports = ({ appFlavour, appLabel, version = process.env.BUILD_VERSION, theme, packageAlias, root = path.resolve('.'), env = process.env.NODE_ENV }) => { + const isDevelopment = (env === 'development') + const isTest = (env === 'test') + + if (isTest) { + Vue.config.devtools = false + Vue.config.productionTip = false + } + + if (isDevelopment) { + Vue.config.devtools = true + Vue.config.performance = true + } + + const optimization = isTest ? {} : { + usedExports: true, + runtimeChunk: 'single', + splitChunks: { + cacheGroups: { + vendor: { + test: /[\\/]node_modules[\\/]/, + name: 'vendors', + chunks: 'all', + }, + }, + }, + } + + return { + publicPath: './', + lintOnSave: true, + runtimeCompiler: true, + + configureWebpack: { + // other webpack options to merge in ... + + // Make sure webpack is not too nosy + // and tries to tinker around linked packages + resolve: { symlinks: false }, + + plugins: [ + new webpack.DefinePlugin({ + FLAVOUR: JSON.stringify(appFlavour), + WEBAPP: JSON.stringify(appLabel), + VERSION: JSON.stringify(version || ('' + exec('git describe --always --tags')).trim()), + BUILD_TIME: JSON.stringify((new Date()).toISOString()), + }), + ], + + optimization, + }, + + chainWebpack: config => { + // https://cli.vuejs.org/guide/troubleshooting.html#symbolic-links-in-node-modules + config.resolve.symlinks(false) + + // Remove css extraction issues + // https://github.com/vuejs/vue-cli/issues/3771#issuecomment-526228100 + config.plugin('friendly-errors').tap(args => { + const vueCli3Transformer = args[0].additionalTransformers[0] + args[0].additionalTransformers = [ + vueCli3Transformer, + error => { + const regexp = /\[mini-css-extract-plugin\]/ + if (regexp.test(error.message)) return {} + return error + }, + ] + return args + }) + + // Do not copy config files (deployment procedure will do that) + config.plugins.has('copy') && config.plugin('copy').tap(options => { + options[0][0].ignore.push('config*js') + options[0][0].ignore.push('*gitignore') + return options + }) + + // Aliasing full package name instead of '@' so we do + // not break imports on apps that import this code + config.resolve.alias.delete('@') + if (packageAlias) { + config.resolve.alias.set(packageAlias, root) + } + + if (isTest) { + const scssRule = config.module.rule('scss') + scssRule.uses.clear() + scssRule + .use('null-loader') + .loader('null-loader') + } + + const scssNormal = config.module.rule('scss').oneOf('normal') + + scssNormal.use('sass-loader') + .loader('sass-loader') + .tap(options => ({ + ...options, + sourceMap: true, + })) + + // Load CSS assets according to their location + scssNormal.use('resolve-url-loader') + .loader('resolve-url-loader').options({ + keepQuery: true, + removeCR: true, + root: path.join(root, 'src/themes', theme), + }) + .before('sass-loader') + }, + + devServer: { + host: '0.0.0.0', + https: false, + port: 8080, + public: 'http://127.0.0.1:8080', + hot: false, + + proxy: { + '^/custom.css': { + target: fetchBaseUrl(), + }, + }, + + watchOptions: { + ignored: [ + // Do not watch for changes under node_modules + // (exception is node_modules/@cortezaproject) + /node_modules([\\]+|\/)+(?!@cortezaproject)/, + ], + aggregateTimeout: 200, + poll: 1000, + }, + }, + + css: { + sourceMap: isDevelopment, + loaderOptions: { + sass: {}, + }, + }, + } +} + +function fetchBaseUrl () { + var fs = require('fs') + var window = {} + + const fileContents = + fs.existsSync('public/config.js') + ? fs.readFileSync('public/config.js', 'utf-8') + : '' + + try { + // eslint-disable-next-line no-eval + eval(fileContents) + + const u = window.CortezaAPI || '' + const ur = new URL(u.startsWith('//') ? `http:${u}` : u) + + return `${ur.protocol}//${ur.host}/` + } catch (e) { + return '/' + } +} diff --git a/.github/workflows/assets/docker-compose.yaml b/.github/workflows/assets/docker-compose.yaml new file mode 100644 index 000000000..3ff06b037 --- /dev/null +++ b/.github/workflows/assets/docker-compose.yaml @@ -0,0 +1,59 @@ +version: "3.5" + +services: + backend: + network_mode: host + image: golang:latest + volumes: + - "${PWD}:/corteza" + entrypoint: | + sh -c " + git config --global --add safe.directory /corteza && + go run /corteza/server/cmd/corteza/main.go serve-api + " + working_dir: /corteza/server + environment: + - DB_DSN=postgres://corteza:root@localhost:5432/corteza_cy_test?sslmode=disable + - HTTP_ADDR=:8888 + - HTTP_ENABLE_DEBUG_ROUTE=true + - HTTP_WEBAPP_ENABLED=true + - ENVIRONMENT=dev + healthcheck: + test: curl --silent --fail-early --fail http://localhost:8888/healthcheck || exit 1 + interval: 30s + timeout: 30s + retries: 3 + start_period: 60s + + postgres: + network_mode: host + image: postgres:15 + environment: + POSTGRES_USER: corteza + POSTGRES_PASSWORD: root + POSTGRES_DB: corteza_cy_test + healthcheck: + test: [ "CMD-SHELL", "pg_isready -d $${POSTGRES_DB} -U $${POSTGRES_USER}" ] + interval: 10s + timeout: 5s + retries: 5 + + client: + network_mode: host + image: node:16.20-alpine3.16 + volumes: + - "${PWD}:/corteza" + working_dir: /corteza + environment: + BUILD_VERSION: 2023.9.1-6-g3ad38a1be + healthcheck: + test: wget --no-verbose --tries=1 --spider http://localhost:8080 || exit 1 + interval: 30s + timeout: 30s + retries: 3 + start_period: 60s + + deploy: + resources: + limits: + memory: 2g diff --git a/.github/workflows/test-e2e.yml b/.github/workflows/test-e2e.yml new file mode 100644 index 000000000..1cc84139f --- /dev/null +++ b/.github/workflows/test-e2e.yml @@ -0,0 +1,238 @@ +name: "E2e test on main branch push" + +# These jobs can be tested with nektos/act tool +# https://github.com/nektos/act +# +# Look for "!env.ACT" in the DRY_RUN expression below + +on: + push: + branches: + - '202[2-9].[39].x' + +env: + BUILD_OS: linux + BUILD_ARCH: amd64 + + GO_VERSION: 1.19 + GOFLAGS: -mod=readonly + + NODE_VERSION: 16 + CYPRESS_VERSION: 13.5.0 + CYPRESS_HOST: http://localhost:8888 + CYPRESS_BASE_URL: http://localhost:8888 + CYPRESS_ADMIN_URL: http://localhost:8080 + CYPRESS_COMPOSE_URL: http://localhost:8081 + CYPRESS_WORKFLOW_URL: http://localhost:8082 + CYPRESS_REPORTER_URL: http://localhost:8083 + CYPRESS_ONE_URL: http://localhost:8086 + CYPRESS_PRIVACY_URL: http://localhost:8087 + CYPRESS_USER_EMAIL: cypress@test.com + CYPRESS_USER_PASSWORD: cypress123 + CYPRESS_USER_PASSWORD_NEW: newcypress123 + CYPRESS_USER_EMAIL_NEW: permissions@email.com + CYPRESS_USER_DPO: dpo@email.com + CYPRESS_USER_DPO_PASSWORD: dpo@email.com + +jobs: + server-client-setup: + name: server and client test + strategy: + matrix: + client: + - { port: 8081, name: compose } + - { port: 8080, name: admin } + - { port: 8082, name: workflow } + - { port: 8083, name: reporter } + - { port: 8086, name: one } + # - { port: 8087, name: privacy } + runs-on: ubuntu-20.04 + steps: + - name: Setup environment + env: + RELEASE_CRUST_SFTP_KEY: ${{ secrets.RELEASE_CRUST_SFTP_KEY }} + RELEASE_CRUST_SFTP_URI: ${{ secrets.RELEASE_CRUST_SFTP_URI }} + CLIENT_NAME: ${{ matrix.client.name }} + run: | + echo "SCREENSHOTS_GEN_FOLDER=$(date +"%Y%m%d%H%M%S")_$CLIENT_NAME" >> $GITHUB_ENV && \ + echo "TMP_KEY=$(mktemp)" >> $GITHUB_ENV + echo $SCREENSHOTS_GEN_FOLDER + + - name: Create crust key + env: + RELEASE_CRUST_SFTP_KEY: ${{ secrets.RELEASE_CRUST_SFTP_KEY }} + RELEASE_CRUST_KEY_FILE: ${{ env.TMP_KEY }} + run: | + echo ${RELEASE_CRUST_SFTP_KEY} | base64 -d > ${RELEASE_CRUST_KEY_FILE} && \ + echo ${RELEASE_CRUST_SFTP_KEY} > /tmp/foo + + - name: Make folder for uploaded screenshots + env: + RELEASE_CRUST_SFTP_URI: ${{ secrets.RELEASE_CRUST_SFTP_URI }} + RELEASE_CRUST_KEY_FILE: ${{ env.TMP_KEY }} + SCREENSHOTS_GEN_FOLDER: ${{ env.SCREENSHOTS_GEN_FOLDER }} + run: | + printf '%s\n' 'cd screenshots' "mkdir ${SCREENSHOTS_GEN_FOLDER}" | sftp -o "StrictHostKeyChecking no" -i ${RELEASE_CRUST_KEY_FILE} ${RELEASE_CRUST_SFTP_URI} + + - uses: actions/setup-node@v3 + with: { node-version: "${{ env.NODE_VERSION }}" } + + - uses: actions/setup-go@v3 + with: { go-version: "${{ env.GO_VERSION }}" } + + - name: Setup YARN + run: npm install -g yarn @vue/cli-service + + - name: Checkout corteza repo + uses: actions/checkout@v3 + with: + path: corteza + token: ${{ secrets.GITHUB_TOKEN }} + ref: ${{ github.ref_name }} + + - name: Copy server language files + working-directory: corteza/server/pkg/locale + run: make src/en + + - name: Copy assets + working-directory: corteza + env: + CLIENT_NAME: ${{ matrix.client.name }} + run: | + mv ./.github/workflows/assets/docker-compose.yaml ./ + cp .github/workflows/assets/client/vue.config-builder.js client/web/${CLIENT_NAME}/public/vue.config-builder.js + cp .github/workflows/assets/client/config.js client/web/${CLIENT_NAME}/public/config.js + + - name: Run server and db + working-directory: corteza + run: | + docker-compose run -d --name backend backend + docker-compose run -d --name postgres postgres + + - name: Build js + working-directory: corteza/lib/js + run: | + yarn install + yarn build + + - name: Build vue + working-directory: corteza/lib/vue + run: | + yarn install + yarn build + + - name: Run ${{ matrix.client.name }} + working-directory: corteza + # re-link the packages for each app + env: + CLIENT_NAME: ${{ matrix.client.name }} + CLIENT_PORT: ${{ matrix.client.port }} + run: | + docker-compose \ + run -d \ + --name "${CLIENT_NAME}" \ + --use-aliases \ + client \ + sh -c "ls && cd /corteza/lib/js && yarn link && \ + cd /corteza/lib/vue && yarn link && \ + cd /corteza/client/web/${CLIENT_NAME} && \ + yarn link @cortezaproject/corteza-vue && \ + yarn link @cortezaproject/corteza-js && \ + yarn install && \ + yarn serve --port ${CLIENT_PORT}" + + - name: Clone CY repo + uses: actions/checkout@v3 + with: + repository: cortezaproject/corteza-e2e-cypress + token: ${{ secrets.GITHUB_TOKEN }} + ref: ${{ github.ref_name }} + path: corteza-e2e-cypress + + # github actions do not support docker-compose --wait + - name: Wait for postgres healthcheck + run: timeout 180s sh -c 'until docker ps | grep postgres | grep -q healthy; do echo "Waiting for container to be healthy..."; sleep 1; done' + + - name: Wait for ${{ matrix.client.name }} healthcheck + env: + CLIENT_NAME: ${{ matrix.client.name }} + run: | + timeout 180s sh -c 'until docker ps | grep ${CLIENT_NAME} | grep -q healthy; do echo "Waiting for container to be healthy..."; sleep 1; done' + + - name: Wait for backend healthcheck + run: timeout 180s sh -c 'until docker ps | grep backend | grep -q healthy; do echo "Waiting for container to be healthy..."; sleep 1; done' + + - name: Run CY tests for ${{ matrix.client.name }} + env: + CLIENT_NAME: ${{ matrix.client.name }} + CLIENT_PORT: ${{ matrix.client.port }} + working-directory: corteza-e2e-cypress + run: | + yarn install + + docker-compose run \ + --entrypoint="bash -c \" \ + cypress run --spec cypress/e2e/basic-functionalities/server/Create_user.cy.js --browser chrome \ + \"" \ + cypress + + docker-compose run \ + --entrypoint="bash -c \" \ + cypress run --spec cypress/e2e/basic-functionalities/${CLIENT_NAME}/index.cy.js --browser chrome \ + \"" \ + cypress + + docker-compose run \ + --entrypoint="bash -c \" \ + cypress run --spec cypress/e2e/basic-functionalities/topbar/${CLIENT_NAME}/index.cy.js --browser chrome \ + \"" \ + cypress + + - name: Upload screenshots + if: failure() + env: + RELEASE_CRUST_SFTP_URI: ${{ secrets.RELEASE_CRUST_SFTP_URI }} + RELEASE_CRUST_KEY_FILE: ${{ env.TMP_KEY }} + SCREENSHOTS_GEN_FOLDER: ${{ env.SCREENSHOTS_GEN_FOLDER }} + run: | + printf '%s\n' "cd screenshots/${SCREENSHOTS_GEN_FOLDER}" 'put -r corteza-e2e-cypress/cypress/screenshots/*' | sftp -q -o "StrictHostKeyChecking no" -i ${RELEASE_CRUST_KEY_FILE} ${RELEASE_CRUST_SFTP_URI} + + notify-failure: + needs: [ server-client-setup ] + runs-on: ubuntu-20.04 + if: failure() + steps: + - name: Send message on failed testing results + uses: fadenb/matrix-chat-message@v0.0.6 + env: + E2E_REPORTS_USER: ${{ secrets.E2E_REPORTS_USER }} + E2E_REPORTS_PASS: ${{ secrets.E2E_REPORTS_PASS }} + with: + homeserver: ${{ secrets.MATRIX_HOME_SERVER }} + token: ${{ secrets.MATRIX_ACCESS_TOKEN }} + channel: ${{ secrets.MATRIX_ROOM_ID }} + message: | + ❌ E2e tests did not pass, screenshots provided + + 🔗 https://${{ secrets.E2E_REPORTS_USER }}:${{ secrets.E2E_REPORTS_PASS }}@releases.cortezaproject.org/e2e-reports/ + + 📷 https://${{ secrets.E2E_REPORTS_USER }}:${{ secrets.E2E_REPORTS_PASS }}@releases.cortezaproject.org/e2e-reports/screenshots/${{ env.SCREENSHOTS_GEN_FOLDER }}/ + + notify-success: + needs: [ server-client-setup ] + runs-on: ubuntu-20.04 + if: success() + steps: + - name: Send message on successful testing results + uses: fadenb/matrix-chat-message@v0.0.6 + env: + E2E_REPORTS_USER: ${{ secrets.E2E_REPORTS_USER }} + E2E_REPORTS_PASS: ${{ secrets.E2E_REPORTS_PASS }} + with: + homeserver: ${{ secrets.MATRIX_HOME_SERVER }} + token: ${{ secrets.MATRIX_ACCESS_TOKEN }} + channel: ${{ secrets.MATRIX_ROOM_ID }} + message: | + ✅ E2e tests passed 🙌 🎉 + + 🔗 https://${{ secrets.E2E_REPORTS_USER }}:${{ secrets.E2E_REPORTS_PASS }}@releases.cortezaproject.org/e2e-reports/