Переглянути джерело

[web] Experiment with code coverage (#28029)

* Update test script to allow coverage to be recorded

* Collect coverage in CI

* [web] integrate coverage report with Jenkins

* [web] add coverage report for acceptance tests

* Use --all flag in vitest coverage

* Use --all flag in acceptance tests

* Switch vitest to use istanbul

* Bump versions of vitest plugins and update package-lock.json

* [web] use a dedicated parameter for coverage in unit/acceptance tests

* [web] temporarily generate HTML report and archive artifacts

* [web] do not run acceptance tests as root in CI

* [web] increase hook timeout in vitest when collecting coverage

* [web] exclude packageMapping from coverage report

* [web] use relative paths in clover reports

* [web] reduce concurrency for unit tests in CI

* [web] keep source code forever on builds

* Revert "[web] temporarily generate HTML report and archive artifacts"

This reverts commit 4c9db571

* [web] archive all the raw coverage report files

* [monorepo] update build scripts for web

* [monorepo] update package-lock.json after merge

* [web] remove spurious change

* [web] merge Jenkins parameter sections

* [web] increase hookTimeout when collecting coverage report

---------

Co-authored-by: Jakob Ackermann <jakob.ackermann@overleaf.com>
GitOrigin-RevId: 9f1ec6d38a7b037267737bec3b0bc53a0ac844af
Andrew Rumble 6 місяців тому
батько
коміт
b2d4687c52

Різницю між файлами не показано, бо вона завелика
+ 762 - 99
package-lock.json


+ 3 - 0
services/web/Dockerfile

@@ -81,6 +81,9 @@ COPY tools/migrations/ /overleaf/tools/migrations/
 # Build the latex parser
 RUN cd /overleaf/services/web && npm run 'lezer-latex:generate'
 
+# The module folders are owned as root. The node user cannot create them. Create them in advance.
+RUN cd /overleaf/services/web && make create_module_Makefiles
+
 USER node
 
 

+ 8 - 0
services/web/Makefile

@@ -32,6 +32,7 @@ MODULE_DIRS := $(shell find modules -mindepth 1 -maxdepth 1 -type d -not -name '
 MODULE_MAKEFILES := $(MODULE_DIRS:=/Makefile)
 MODULE_NAME=$(shell basename $(MODULE))
 
+create_module_Makefiles: $(MODULE_MAKEFILES)
 $(MODULE_MAKEFILES): Makefile.module
 	cp Makefile.module $@ || diff Makefile.module $@
 
@@ -424,6 +425,13 @@ test_acceptance_modules: $(TEST_ACCEPTANCE_MODULES_MERGED_VARIANTS)
 # CI tests
 #
 
+rebase_coverage_reports:
+	sed -i 's_path="/overleaf/_path="_' data/coverage/*/clover.xml
+
+# Create the bundle with full paths inside the monorepo to make it easier to organize them later.
+bundle_coverage_reports:
+	cd ../../ && tar -czf services/web/data/coverage.tar.gz services/web/data/coverage/*/clover.xml
+
 ci:
 	MOCHA_ARGS="--reporter tap" \
 	$(MAKE) test

+ 12 - 0
services/web/bin/test_acceptance_run_dir

@@ -0,0 +1,12 @@
+#!/bin/bash
+
+declare -a mocha_prefix=()
+
+if [[ "$COVERAGE_ACCEPTANCE_TESTS" == "true" ]]; then
+  name=$(echo "$BASE_CONFIG $OVERLEAF_CONFIG" "$@" | sha256sum)
+  dir=data/coverage/mocha-acceptance-${name}
+  echo "Enabling coverage. Writing into ${dir}"
+  mocha_prefix+=("c8" "--reporter=clover" "--all" "--include={app.mjs,app/**/*.{mjs,js},modules/*/index.mjs,modules/*/app/**/*.{mjs,js}}" "--exclude=app/src/Features/Metadata/packageMapping.mjs" "--report-dir=${dir}")
+fi
+
+"${mocha_prefix[@]}" mocha --recursive --timeout 25000 --grep="$MOCHA_GREP" --require test/acceptance/bootstrap.js "$@"

+ 0 - 1
services/web/bin/test_unit_run_dir

@@ -14,4 +14,3 @@ fi
 echo "Running unit tests in directory: $*"
 
 npm run test:unit -- "${vitest_args[@]}"
-

+ 0 - 0
services/web/data/coverage/.gitignore


+ 7 - 2
services/web/docker-compose.ci.yml

@@ -12,6 +12,7 @@ services:
     user: node
     volumes:
       - ../../bin/shared/wait_for_it:/overleaf/bin/shared/wait_for_it
+      - ./data/coverage:/overleaf/services/web/data/coverage
       - ./data/reports:/overleaf/services/web/data/reports
     entrypoint: /overleaf/bin/shared/wait_for_it mongo:27017 --timeout=60 --
     command: npm run test:unit:app
@@ -27,6 +28,8 @@ services:
       NODE_OPTIONS: "--unhandled-rejections=strict"
       REDIS_HOST: redis_test
       VITEST_NO_CACHE: true
+      COVERAGE_UNIT_TESTS:
+      MONOREPO:
     depends_on:
       - redis_test
       - mongo
@@ -47,21 +50,23 @@ services:
       BASE_CONFIG:
       OVERLEAF_CONFIG:
       REDIS_HOST: redis_test
+      COVERAGE_ACCEPTANCE_TESTS:
+      MONOREPO:
     extra_hosts:
       - "www.overleaf.test:127.0.0.1"
     volumes:
       - ../../bin/shared/wait_for_it:/overleaf/bin/shared/wait_for_it
+      - ./data/coverage:/overleaf/services/web/data/coverage
       - ./data/reports:/overleaf/services/web/data/reports
     entrypoint: /overleaf/bin/shared/wait_for_it mongo:27017 --timeout=60 --
     command: npm run test:acceptance:app
-    user: root
     depends_on:
       - redis_test
       - mongo
       - saml
       - ldap
     tmpfs:
-      - /overleaf/services/web/data
+      - /overleaf/services/web/data:uid=1000,gid=1000
 
   test_frontend:
     build:

+ 6 - 2
services/web/docker-compose.yml

@@ -18,11 +18,13 @@ services:
       CI:
       MODULE_NAME:
       JUNIT_ROOT_SUITE_NAME:
-      MOCHA_GREP: ${MOCHA_GREP}
+      COVERAGE_UNIT_TESTS: ${COVERAGE_UNIT_TESTS:-}
+      MOCHA_GREP: ${MOCHA_GREP:-}
       LOG_LEVEL: ${LOG_LEVEL:-}
       NODE_ENV: test
       NODE_OPTIONS: "--unhandled-rejections=strict"
       REDIS_HOST: redis_test
+      MONOREPO:
     entrypoint: /overleaf/bin/shared/wait_for_it mongo:27017 --timeout=60 --
     command: npm run --silent test:unit:app
     user: node
@@ -47,11 +49,13 @@ services:
       CI:
       MODULE_NAME:
       JUNIT_ROOT_SUITE_NAME:
-      MOCHA_GREP: ${MOCHA_GREP}
+      COVERAGE_ACCEPTANCE_TESTS: ${COVERAGE_ACCEPTANCE_TESTS:-}
+      MOCHA_GREP: ${MOCHA_GREP:-}
       LOG_LEVEL: ${LOG_LEVEL:-}
       MONGO_SERVER_SELECTION_TIMEOUT: 600000
       MONGO_SOCKET_TIMEOUT: 300000
       REDIS_HOST: redis_test
+      MONOREPO:
       # OVERLEAF_ALLOW_ANONYMOUS_READ_AND_WRITE_SHARING: 'true'
 
     extra_hosts:

+ 3 - 2
services/web/package.json

@@ -7,7 +7,7 @@
     "public": "./public"
   },
   "scripts": {
-    "test:acceptance:run_dir": "mocha --recursive --timeout 25000 --grep=$MOCHA_GREP --require test/acceptance/bootstrap.js",
+    "test:acceptance:run_dir": "bin/test_acceptance_run_dir",
     "test:acceptance:app": "npm run test:acceptance:run_dir -- test/acceptance/src",
     "test:unit:run_dir": "bin/test_unit_run_dir",
     "test:unit:all": "npm run test:unit:run_dir -- test/unit/src modules/*/test/unit/src",
@@ -273,7 +273,8 @@
     "@uppy/react": "^3.2.1",
     "@uppy/utils": "^5.7.0",
     "@uppy/xhr-upload": "^3.6.0",
-    "@vitest/eslint-plugin": "1.1.44",
+    "@vitest/coverage-istanbul": "^4.0.17",
+    "@vitest/eslint-plugin": "1.6.6",
     "@writefull/core": "^1.27.27",
     "@writefull/ui": "^1.27.27",
     "@writefull/utils": "^1.27.27",

+ 20 - 0
services/web/vitest.config.js

@@ -1,8 +1,11 @@
 const { defineConfig } = require('vitest/config')
 
+const COVERAGE_ENABLED = process.env.COVERAGE_UNIT_TESTS === 'true'
+
 let reporterOptions = {}
 if (process.env.CI && process.env.JUNIT_ROOT_SUITE_NAME) {
   reporterOptions = {
+    maxWorkers: '50%',
     reporters: [
       'default',
       [
@@ -15,6 +18,7 @@ if (process.env.CI && process.env.JUNIT_ROOT_SUITE_NAME) {
     outputFile: 'data/reports/junit-vitest.xml',
   }
 }
+
 module.exports = defineConfig({
   test: {
     setupFiles: ['./test/unit/bootstrap.mjs'],
@@ -53,5 +57,21 @@ module.exports = defineConfig({
       },
     ],
     ...reporterOptions,
+    hookTimeout: COVERAGE_ENABLED ? 20_000 : 10_000,
+    coverage: {
+      enabled: COVERAGE_ENABLED,
+      // Add 'sequential' / 'parallel' to the folder
+      reportsDirectory: `data/coverage/esm-unit-${(process.env.JUNIT_ROOT_SUITE_NAME || 'all').split(' ').pop()}`,
+      include: [
+        'app.mjs',
+        'app/**/*.{js,mjs}',
+        'modules/*/index.mjs',
+        'modules/*/app/src/**/*.{js,mjs}',
+      ],
+      exclude: ['app/src/Features/Metadata/packageMapping.mjs'],
+      provider: 'istanbul',
+      reporters: ['console-details', 'clover'],
+      all: true,
+    },
   },
 })

Деякі файли не було показано, через те що забагато файлів було змінено