diff --git a/.github/workflows/webviz.yml b/.github/workflows/webviz.yml index 5a43af313..3fc974c86 100644 --- a/.github/workflows/webviz.yml +++ b/.github/workflows/webviz.yml @@ -1,116 +1,133 @@ name: webviz on: - push: - pull_request: - branches: - - main - release: - types: - - published + push: + pull_request: + branches: + - main + release: + types: + - published jobs: - frontend: - runs-on: ubuntu-latest - - steps: - - name: 📖 Checkout commit locally - uses: actions/checkout@v3 - - - uses: actions/setup-node@v3 - with: - node-version: 18 - cache: npm - cache-dependency-path: frontend/package-lock.json - - - name: ℹī¸ Node and npm versions - run: | - node -v - npm -v - - - name: đŸ“Ļ Install build dependencies - working-directory: ./frontend - run: | - npm ci --include=dev - - - name: ℹī¸ Installed npm packages (depth = 0) - working-directory: ./frontend - run: npm list --depth=0 - - - name: 🏗ī¸ Build JavaScript bundle - working-directory: ./frontend - run: npm run build - - - name: đŸ•ĩī¸ Check code style, linting & dependencies - working-directory: ./frontend - run: | - npm run validate - - - name: 🤖 Run tests - working-directory: ./frontend - run: | - npm run test - - - name: đŸ•ĩī¸ Check auto-generated frontend code is in sync with backend - run: | - docker build -f backend.Dockerfile -t backend:latest . - CONTAINER_ID=$(docker run --detach -p 5000:5000 --env UVICORN_PORT=5000 --env UVICORN_ENTRYPOINT=src.backend.primary.main:app --env WEBVIZ_CLIENT_SECRET=0 --env WEBVIZ_SMDA_SUBSCRIPTION_KEY=0 --env WEBVIZ_SMDA_RESOURCE_SCOPE=0 backend:latest) - sleep 5 # Ensure the backend server is up and running exposing /openapi.json - npm run generate-api --prefix ./frontend - docker stop $CONTAINER_ID - git diff --exit-code ./frontend/src/api || exit 1 - - backend: - runs-on: ubuntu-latest - - steps: - - name: 📖 Checkout commit locally - uses: actions/checkout@v3 - - - name: 🐍 Set up Python - uses: actions/setup-python@v4 - with: - python-version: "3.11" - cache: pip - - - name: đŸ“Ļ Install poetry and dependencies - working-directory: ./backend - run: | - pip install --upgrade pip - pip install poetry - poetry config virtualenvs.create false - poetry lock --check --no-update # Check lock file is consistent with pyproject.toml - poetry install --with dev - - - name: đŸ•ĩī¸ Check code style & linting - working-directory: ./backend - run: | - black --check src/ tests/ - pylint src/ tests/ - bandit --recursive src/ - mypy src/ tests/ - - - name: 🤖 Run tests - working-directory: ./backend - env: - WEBVIZ_CLIENT_SECRET: 0 - WEBVIZ_SMDA_SUBSCRIPTION_KEY: 0 - WEBVIZ_SMDA_RESOURCE_SCOPE: 0 - run: | - pytest ./tests/unit - - build_docker_images: - runs-on: ubuntu-latest - - steps: - - name: 📖 Checkout commit locally - uses: actions/checkout@v3 - - - uses: actions/setup-node@v3 - with: - node-version: 18 - - - name: đŸŗ Verify Docker images build - run: | - docker build -f frontend-prod.Dockerfile . - docker build -f backend.Dockerfile . + frontend: + runs-on: ubuntu-latest + + steps: + - name: 📖 Checkout commit locally + uses: actions/checkout@v3 + + - uses: actions/setup-node@v3 + with: + node-version: 18 + cache: npm + cache-dependency-path: frontend/package-lock.json + + - name: ℹī¸ Node and npm versions + run: | + node -v + npm -v + + - name: đŸ“Ļ Install build dependencies + working-directory: ./frontend + run: | + npm ci --include=dev + + - name: ℹī¸ Installed npm packages (depth = 0) + working-directory: ./frontend + run: npm list --depth=0 + + - name: 🏗ī¸ Build JavaScript bundle + working-directory: ./frontend + run: npm run build + + - name: đŸ•ĩī¸ Check code style, linting & dependencies + working-directory: ./frontend + run: | + npm run validate + + - name: Install Playwright Browsers + working-directory: ./frontend + run: | + npx playwright install --with-deps + + - name: 🤖 Run unit tests + working-directory: ./frontend + run: | + npm run test:unit + + - name: 🤖 Run component tests + working-directory: ./frontend + run: | + npm run test:ct + + - uses: actions/upload-artifact@v3 + if: always() + with: + name: playwright-report + path: playwright-report/ + retention-days: 30 + + - name: đŸ•ĩī¸ Check auto-generated frontend code is in sync with backend + run: | + docker build -f backend.Dockerfile -t backend:latest . + CONTAINER_ID=$(docker run --detach -p 5000:5000 --env UVICORN_PORT=5000 --env UVICORN_ENTRYPOINT=src.backend.primary.main:app --env WEBVIZ_CLIENT_SECRET=0 --env WEBVIZ_SMDA_SUBSCRIPTION_KEY=0 --env WEBVIZ_SMDA_RESOURCE_SCOPE=0 backend:latest) + sleep 5 # Ensure the backend server is up and running exposing /openapi.json + npm run generate-api --prefix ./frontend + docker stop $CONTAINER_ID + git diff --exit-code ./frontend/src/api || exit 1 + + backend: + runs-on: ubuntu-latest + + steps: + - name: 📖 Checkout commit locally + uses: actions/checkout@v3 + + - name: 🐍 Set up Python + uses: actions/setup-python@v4 + with: + python-version: "3.11" + cache: pip + + - name: đŸ“Ļ Install poetry and dependencies + working-directory: ./backend + run: | + pip install --upgrade pip + pip install poetry + poetry config virtualenvs.create false + poetry lock --check --no-update # Check lock file is consistent with pyproject.toml + poetry install --with dev + + - name: đŸ•ĩī¸ Check code style & linting + working-directory: ./backend + run: | + black --check src/ tests/ + pylint src/ tests/ + bandit --recursive src/ + mypy src/ tests/ + + - name: 🤖 Run tests + working-directory: ./backend + env: + WEBVIZ_CLIENT_SECRET: 0 + WEBVIZ_SMDA_SUBSCRIPTION_KEY: 0 + WEBVIZ_SMDA_RESOURCE_SCOPE: 0 + run: | + pytest ./tests/unit + + build_docker_images: + runs-on: ubuntu-latest + + steps: + - name: 📖 Checkout commit locally + uses: actions/checkout@v3 + + - uses: actions/setup-node@v3 + with: + node-version: 18 + + - name: đŸŗ Verify Docker images build + run: | + docker build -f frontend-prod.Dockerfile . + docker build -f backend.Dockerfile . diff --git a/frontend/nyc.config.cjs b/frontend/nyc.config.cjs index 15381c134..b2d066418 100644 --- a/frontend/nyc.config.cjs +++ b/frontend/nyc.config.cjs @@ -1,11 +1,16 @@ module.exports = { + extends: "@istanbuljs/nyc-config-typescript", /* include all TypeScript sources from folder "src", excluding type definitions and test files */ cwd: ".", - include: ["src/**"], - extension: [".ts", ".tsx"], - + include: ["./src/lib/components/**/*.tsx"], + extension: [".tsx"], + exclude: ["**/*.d.ts", "**/index.ts"], + /* instrument all files, not just the ones touched by the test cases */ all: true, - + + cache: false, + "check-coverage": true, + reporter: ["html", "lcov"], - }; \ No newline at end of file +}; diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 0c2ba5308..beb5305e4 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -29,6 +29,7 @@ "devDependencies": { "@babel/eslint-parser": "^7.19.1", "@babel/preset-react": "^7.18.6", + "@istanbuljs/nyc-config-typescript": "^1.0.2", "@playwright/experimental-ct-react": "^1.39.0", "@playwright/test": "^1.39.0", "@trivago/prettier-plugin-sort-imports": "^4.0.0", @@ -44,6 +45,7 @@ "@typescript-eslint/eslint-plugin": "^5.59.6", "@typescript-eslint/parser": "^5.59.6", "@vitejs/plugin-react": "^4.0.0", + "@vitest/coverage-istanbul": "^0.34.6", "autoprefixer": "^10.4.13", "dependency-cruiser": "^13.1.0", "eslint": "^8.40.0", @@ -66,7 +68,8 @@ "vite": "^4.3.9", "vite-plugin-checker": "^0.6.0", "vite-plugin-glsl": "^1.1.2", - "vite-plugin-istanbul": "^5.0.0" + "vite-plugin-istanbul": "^5.0.0", + "vitest": "^0.34.6" } }, "node_modules/@aashutoshrathi/word-wrap": { @@ -1591,6 +1594,21 @@ "node": ">=8" } }, + "node_modules/@istanbuljs/nyc-config-typescript": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@istanbuljs/nyc-config-typescript/-/nyc-config-typescript-1.0.2.tgz", + "integrity": "sha512-iKGIyMoyJuFnJRSVTZ78POIRvNnwZaWIf8vG4ZS3rQq58MMDrqEX2nnzx0R28V2X8JvmKYiqY9FP2hlJsm8A0w==", + "dev": true, + "dependencies": { + "@istanbuljs/schema": "^0.1.2" + }, + "engines": { + "node": ">=8" + }, + "peerDependencies": { + "nyc": ">=15" + } + }, "node_modules/@istanbuljs/schema": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", @@ -4119,6 +4137,21 @@ "@babel/types": "^7.20.7" } }, + "node_modules/@types/chai": { + "version": "4.3.9", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.9.tgz", + "integrity": "sha512-69TtiDzu0bcmKQv3yg1Zx409/Kd7r0b5F1PfpYJfSHzLGtB53547V4u+9iqKYsTu/O2ai6KTb0TInNpvuQ3qmg==", + "dev": true + }, + "node_modules/@types/chai-subset": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/@types/chai-subset/-/chai-subset-1.3.4.tgz", + "integrity": "sha512-CCWNXrJYSUIojZ1149ksLl3AN9cmZ5djf+yUoVVV+NuYrtydItQVlL2ZDqyC6M6O9LWRnVf8yYDxbXHO2TfQZg==", + "dev": true, + "dependencies": { + "@types/chai": "*" + } + }, "node_modules/@types/culori": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/@types/culori/-/culori-2.0.0.tgz", @@ -4669,6 +4702,171 @@ "vite": "^4.2.0" } }, + "node_modules/@vitest/coverage-istanbul": { + "version": "0.34.6", + "resolved": "https://registry.npmjs.org/@vitest/coverage-istanbul/-/coverage-istanbul-0.34.6.tgz", + "integrity": "sha512-5KaBNZPDSk2ybavC3rZ1pWGniw7sJ5usuwVGRUYzJwiBfWvnLpuUer7bjw7qUCRGdKJXrBgb/Dsgif9rkwMX/A==", + "dev": true, + "dependencies": { + "istanbul-lib-coverage": "^3.2.0", + "istanbul-lib-instrument": "^6.0.0", + "istanbul-lib-report": "^3.0.1", + "istanbul-lib-source-maps": "^4.0.1", + "istanbul-reports": "^3.1.5", + "picocolors": "^1.0.0", + "test-exclude": "^6.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "vitest": ">=0.32.0 <1" + } + }, + "node_modules/@vitest/coverage-istanbul/node_modules/istanbul-lib-instrument": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.1.tgz", + "integrity": "sha512-EAMEJBsYuyyztxMxW3g7ugGPkrZsV57v0Hmv3mm1uQsmB+QnZuepg731CRaIgeUVSdmsTngOkSnauNF8p7FIhA==", + "dev": true, + "dependencies": { + "@babel/core": "^7.12.3", + "@babel/parser": "^7.14.7", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@vitest/coverage-istanbul/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@vitest/coverage-istanbul/node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@vitest/coverage-istanbul/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/@vitest/expect": { + "version": "0.34.6", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-0.34.6.tgz", + "integrity": "sha512-QUzKpUQRc1qC7qdGo7rMK3AkETI7w18gTCUrsNnyjjJKYiuUB9+TQK3QnR1unhCnWRC0AbKv2omLGQDF/mIjOw==", + "dev": true, + "dependencies": { + "@vitest/spy": "0.34.6", + "@vitest/utils": "0.34.6", + "chai": "^4.3.10" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/runner": { + "version": "0.34.6", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-0.34.6.tgz", + "integrity": "sha512-1CUQgtJSLF47NnhN+F9X2ycxUP0kLHQ/JWvNHbeBfwW8CzEGgeskzNnHDyv1ieKTltuR6sdIHV+nmR6kPxQqzQ==", + "dev": true, + "dependencies": { + "@vitest/utils": "0.34.6", + "p-limit": "^4.0.0", + "pathe": "^1.1.1" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/runner/node_modules/p-limit": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-4.0.0.tgz", + "integrity": "sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^1.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@vitest/runner/node_modules/yocto-queue": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.0.0.tgz", + "integrity": "sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==", + "dev": true, + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@vitest/snapshot": { + "version": "0.34.6", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-0.34.6.tgz", + "integrity": "sha512-B3OZqYn6k4VaN011D+ve+AA4whM4QkcwcrwaKwAbyyvS/NB1hCWjFIBQxAQQSQir9/RtyAAGuq+4RJmbn2dH4w==", + "dev": true, + "dependencies": { + "magic-string": "^0.30.1", + "pathe": "^1.1.1", + "pretty-format": "^29.5.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/spy": { + "version": "0.34.6", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-0.34.6.tgz", + "integrity": "sha512-xaCvneSaeBw/cz8ySmF7ZwGvL0lBjfvqc1LpQ/vcdHEvpLn3Ff1vAvjw+CoGn0802l++5L/pxb7whwcWAw+DUQ==", + "dev": true, + "dependencies": { + "tinyspy": "^2.1.1" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/utils": { + "version": "0.34.6", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-0.34.6.tgz", + "integrity": "sha512-IG5aDD8S6zlvloDsnzHw0Ut5xczlF+kv2BOTo+iXfPr54Yhi5qbVOgGB1hZaVq4iJ4C/MZ2J0y15IlsV/ZcI0A==", + "dev": true, + "dependencies": { + "diff-sequences": "^29.4.3", + "loupe": "^2.3.6", + "pretty-format": "^29.5.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, "node_modules/@vivaxy/png": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/@vivaxy/png/-/png-1.3.0.tgz", @@ -5076,6 +5274,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/assertion-error": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", + "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", + "dev": true, + "engines": { + "node": "*" + } + }, "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", @@ -5442,6 +5649,15 @@ "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" }, + "node_modules/cac": { + "version": "6.7.14", + "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", + "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/caching-transform": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/caching-transform/-/caching-transform-4.0.0.tgz", @@ -5579,6 +5795,24 @@ "colorbrewer": "1.0.0" } }, + "node_modules/chai": { + "version": "4.3.10", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.10.tgz", + "integrity": "sha512-0UXG04VuVbruMUYbJ6JctvH0YnC/4q3/AkT18q4NaITo91CUm0liMS9VqzT9vZhVQ/1eqPanMWjBM+Juhfb/9g==", + "dev": true, + "dependencies": { + "assertion-error": "^1.1.0", + "check-error": "^1.0.3", + "deep-eql": "^4.1.3", + "get-func-name": "^2.0.2", + "loupe": "^2.3.6", + "pathval": "^1.1.1", + "type-detect": "^4.0.8" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/chalk": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", @@ -5609,6 +5843,18 @@ "node": ">=10" } }, + "node_modules/check-error": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.3.tgz", + "integrity": "sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==", + "dev": true, + "dependencies": { + "get-func-name": "^2.0.2" + }, + "engines": { + "node": "*" + } + }, "node_modules/chokidar": { "version": "3.5.3", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", @@ -6639,6 +6885,18 @@ } } }, + "node_modules/deep-eql": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.3.tgz", + "integrity": "sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw==", + "dev": true, + "dependencies": { + "type-detect": "^4.0.0" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/deep-is": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", @@ -8468,6 +8726,15 @@ "integrity": "sha512-LnpfLf/TNzr9zVOGiIY6aKCz8EKuXmlYNV7CM2pUjBa/B+c2I15tS7KLySep75+FuerJdmArvJLcsAXWEy2H0A==", "peer": true }, + "node_modules/get-func-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz", + "integrity": "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==", + "dev": true, + "engines": { + "node": "*" + } + }, "node_modules/get-intrinsic": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.1.tgz", @@ -11597,6 +11864,12 @@ "node": ">=6" } }, + "node_modules/jsonc-parser": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.0.tgz", + "integrity": "sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==", + "dev": true + }, "node_modules/jsonfile": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", @@ -11687,6 +11960,18 @@ "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==" }, + "node_modules/local-pkg": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/local-pkg/-/local-pkg-0.4.3.tgz", + "integrity": "sha512-SFppqq5p42fe2qcZQqqEOiVRXl+WCP1MdT6k7BDEW1j++sp5fIY+/fdRQitvKgB5BrBcmrs5m/L0v2FrU5MY1g==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, "node_modules/locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", @@ -11920,6 +12205,15 @@ "loose-envify": "cli.js" } }, + "node_modules/loupe": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.7.tgz", + "integrity": "sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==", + "dev": true, + "dependencies": { + "get-func-name": "^2.0.1" + } + }, "node_modules/lru-cache": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", @@ -11928,6 +12222,18 @@ "yallist": "^3.0.2" } }, + "node_modules/magic-string": { + "version": "0.30.5", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.5.tgz", + "integrity": "sha512-7xlpfBaQaP/T6Vh8MO/EqXSW5En6INHEvEXQiuff7Gku0PWjU3uf6w/j9o7O+SpB5fOAkrI5HeoNgwjEO0pFsA==", + "dev": true, + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.4.15" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/make-dir": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", @@ -12195,6 +12501,18 @@ "npm": ">= 3" } }, + "node_modules/mlly": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.4.2.tgz", + "integrity": "sha512-i/Ykufi2t1EZ6NaPLdfnZk2AX8cs0d+mTzVKuPfqPKPatxLApaBoxJQ9x1/uckXtrS/U5oisPMDkNs0yQTaBRg==", + "dev": true, + "dependencies": { + "acorn": "^8.10.0", + "pathe": "^1.1.1", + "pkg-types": "^1.0.3", + "ufo": "^1.3.0" + } + }, "node_modules/moment": { "version": "2.29.4", "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz", @@ -13186,6 +13504,21 @@ "node": ">=8" } }, + "node_modules/pathe": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.1.tgz", + "integrity": "sha512-d+RQGp0MAYTIaDBIMmOfMwz3E+LOZnxx1HZd5R18mmCZY0QBlK0LDZfPc8FW8Ed2DlvsuE6PRjroDY+wg4+j/Q==", + "dev": true + }, + "node_modules/pathval": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", + "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", + "dev": true, + "engines": { + "node": "*" + } + }, "node_modules/pbf": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/pbf/-/pbf-3.2.1.tgz", @@ -13307,6 +13640,17 @@ "node": ">=8" } }, + "node_modules/pkg-types": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.0.3.tgz", + "integrity": "sha512-nN7pYi0AQqJnoLPC9eHFQ8AcyaixBUOwvqc5TDnIKCMEE6I0y8P7OKA7fPexsXGCGxQDl/cmrLAp26LhcwxZ4A==", + "dev": true, + "dependencies": { + "jsonc-parser": "^3.2.0", + "mlly": "^1.2.0", + "pathe": "^1.1.0" + } + }, "node_modules/playwright": { "version": "1.39.0", "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.39.0.tgz", @@ -14605,6 +14949,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/siginfo": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", + "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", + "dev": true + }, "node_modules/signal-exit": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", @@ -14803,6 +15153,12 @@ "node": ">=8" } }, + "node_modules/stackback": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", + "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", + "dev": true + }, "node_modules/static-eval": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/static-eval/-/static-eval-2.1.0.tgz", @@ -14812,6 +15168,12 @@ "escodegen": "^1.11.1" } }, + "node_modules/std-env": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.4.3.tgz", + "integrity": "sha512-f9aPhy8fYBuMN+sNfakZV18U39PbalgjXG3lLB9WkaYTxijru61wb57V9wxxNthXM5Sd88ETBWi29qLAsHO52Q==", + "dev": true + }, "node_modules/stream-parser": { "version": "0.3.1", "resolved": "https://registry.npmjs.org/stream-parser/-/stream-parser-0.3.1.tgz", @@ -15063,6 +15425,18 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/strip-literal": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/strip-literal/-/strip-literal-1.3.0.tgz", + "integrity": "sha512-PugKzOsyXpArk0yWmUwqOZecSO0GH0bPoctLcqNDH9J04pVW3lflYE0ujElBGTloevcxF5MofAOZ7C5l2b+wLg==", + "dev": true, + "dependencies": { + "acorn": "^8.10.0" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, "node_modules/strnum": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.0.5.tgz", @@ -15417,17 +15791,41 @@ "integrity": "sha512-AD5ih2NlSssTCwsMznbvwMZpJ1cbhkGd2uueNxzv2jDlEeZdU04JQfRnggJQ8DrcVBGjAsCKwFBbDlVNtEMlzw==", "dev": true }, + "node_modules/tinybench": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.5.1.tgz", + "integrity": "sha512-65NKvSuAVDP/n4CqH+a9w2kTlLReS9vhsAP06MWx+/89nMinJyB2icyl58RIcqCmIggpojIGeuJGhjU1aGMBSg==", + "dev": true + }, "node_modules/tinycolor2": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/tinycolor2/-/tinycolor2-1.6.0.tgz", "integrity": "sha512-XPaBkWQJdsf3pLKJV9p4qN/S+fm2Oj8AIPo1BTUhg5oxkvm9+SVEGFdhyOz7tTdUTfvxMiAs4sp6/eZO2Ew+pw==" }, + "node_modules/tinypool": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-0.7.0.tgz", + "integrity": "sha512-zSYNUlYSMhJ6Zdou4cJwo/p7w5nmAH17GRfU/ui3ctvjXFErXXkruT4MWW6poDeXgCaIBlGLrfU6TbTXxyGMww==", + "dev": true, + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/tinyqueue": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/tinyqueue/-/tinyqueue-2.0.3.tgz", "integrity": "sha512-ppJZNDuKGgxzkHihX8v9v9G5f+18gzaTfrukGrq6ueg0lmH4nqVnA2IPG0AEH3jKEk2GRJCUhDoqpoiw3PHLBA==", "peer": true }, + "node_modules/tinyspy": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-2.2.0.tgz", + "integrity": "sha512-d2eda04AN/cPOR89F7Xv5bK/jrQEhmcLFe6HFldoeO9AJtps+fqEnh486vnT/8y4bw38pSyxDcTCAq+Ks2aJTg==", + "dev": true, + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/tmpl": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", @@ -15890,6 +16288,12 @@ "node": ">=4.2.0" } }, + "node_modules/ufo": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.3.1.tgz", + "integrity": "sha512-uY/99gMLIOlJPwATcMVYfqDSxUR9//AUcgZMzwfSTJPDKzA1S8mX4VLqa+fiAtveraQUBCz4FFcwVZBGbwBXIw==", + "dev": true + }, "node_modules/uglify-js": { "version": "3.17.4", "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.17.4.tgz", @@ -16083,6 +16487,29 @@ } } }, + "node_modules/vite-node": { + "version": "0.34.6", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-0.34.6.tgz", + "integrity": "sha512-nlBMJ9x6n7/Amaz6F3zJ97EBwR2FkzhBRxF5e+jE6LA3yi6Wtc2lyTij1OnDMIr34v5g/tVQtsVAzhT0jc5ygA==", + "dev": true, + "dependencies": { + "cac": "^6.7.14", + "debug": "^4.3.4", + "mlly": "^1.4.0", + "pathe": "^1.1.1", + "picocolors": "^1.0.0", + "vite": "^3.0.0 || ^4.0.0 || ^5.0.0-0" + }, + "bin": { + "vite-node": "vite-node.mjs" + }, + "engines": { + "node": ">=v14.18.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, "node_modules/vite-plugin-checker": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/vite-plugin-checker/-/vite-plugin-checker-0.6.1.tgz", @@ -16327,6 +16754,83 @@ "vite": ">=2.9.1 <= 5" } }, + "node_modules/vitest": { + "version": "0.34.6", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-0.34.6.tgz", + "integrity": "sha512-+5CALsOvbNKnS+ZHMXtuUC7nL8/7F1F2DnHGjSsszX8zCjWSSviphCb/NuS9Nzf4Q03KyyDRBAXhF/8lffME4Q==", + "dev": true, + "dependencies": { + "@types/chai": "^4.3.5", + "@types/chai-subset": "^1.3.3", + "@types/node": "*", + "@vitest/expect": "0.34.6", + "@vitest/runner": "0.34.6", + "@vitest/snapshot": "0.34.6", + "@vitest/spy": "0.34.6", + "@vitest/utils": "0.34.6", + "acorn": "^8.9.0", + "acorn-walk": "^8.2.0", + "cac": "^6.7.14", + "chai": "^4.3.10", + "debug": "^4.3.4", + "local-pkg": "^0.4.3", + "magic-string": "^0.30.1", + "pathe": "^1.1.1", + "picocolors": "^1.0.0", + "std-env": "^3.3.3", + "strip-literal": "^1.0.1", + "tinybench": "^2.5.0", + "tinypool": "^0.7.0", + "vite": "^3.1.0 || ^4.0.0 || ^5.0.0-0", + "vite-node": "0.34.6", + "why-is-node-running": "^2.2.2" + }, + "bin": { + "vitest": "vitest.mjs" + }, + "engines": { + "node": ">=v14.18.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@edge-runtime/vm": "*", + "@vitest/browser": "*", + "@vitest/ui": "*", + "happy-dom": "*", + "jsdom": "*", + "playwright": "*", + "safaridriver": "*", + "webdriverio": "*" + }, + "peerDependenciesMeta": { + "@edge-runtime/vm": { + "optional": true + }, + "@vitest/browser": { + "optional": true + }, + "@vitest/ui": { + "optional": true + }, + "happy-dom": { + "optional": true + }, + "jsdom": { + "optional": true + }, + "playwright": { + "optional": true + }, + "safaridriver": { + "optional": true + }, + "webdriverio": { + "optional": true + } + } + }, "node_modules/vscode-jsonrpc": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-6.0.0.tgz", @@ -16525,6 +17029,22 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/why-is-node-running": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.2.2.tgz", + "integrity": "sha512-6tSwToZxTOcotxHeA+qGCq1mVzKR3CwcJGmVcY+QE8SHy6TnpFnh8PAvPNHYr7EcuVeG0QSMxtYCuO1ta/G/oA==", + "dev": true, + "dependencies": { + "siginfo": "^2.0.0", + "stackback": "0.0.2" + }, + "bin": { + "why-is-node-running": "cli.js" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/word-wrap": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", diff --git a/frontend/package.json b/frontend/package.json index 70a880992..922e6f776 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -12,9 +12,11 @@ "lint": "eslint 'src/**/*.+(ts|tsx|js|jsx|json)' 'tests/**/*.+(ts|tsx|js|jsx|json)' --max-warnings=0", "depscheck": "dependency-cruise src", "validate": "npm run typecheck && npm run lint && npm run depscheck", - "test:unit": "nyc node -r source-map-support/register ./node_modules/@playwright/test/cli.js test --config=tests/unit/_playwright.config.ts", - "test:e2e": "nyc node -r source-map-support/register ./node_modules/@playwright/test/cli.js test --config=tests/e2e/_playwright.config.ts", - "test:ct": "playwright test -c tests/ct/_playwright.config.ts" + "test:unit": "vitest run --coverage", + "test:e2e": "playwright test --config=tests/e2e/_playwright.config.ts", + "test:e2e:ui": "playwright test --config=tests/e2e/_playwright.config.ts -- --ui", + "test:ct": "playwright test -c tests/ct/_playwright.config.ts", + "test:ct:ui": "playwright test --config=tests/ct/_playwright.config.ts -- --ui" }, "dependencies": { "@headlessui/react": "^1.7.8", @@ -38,6 +40,7 @@ "devDependencies": { "@babel/eslint-parser": "^7.19.1", "@babel/preset-react": "^7.18.6", + "@istanbuljs/nyc-config-typescript": "^1.0.2", "@playwright/experimental-ct-react": "^1.39.0", "@playwright/test": "^1.39.0", "@trivago/prettier-plugin-sort-imports": "^4.0.0", @@ -53,6 +56,7 @@ "@typescript-eslint/eslint-plugin": "^5.59.6", "@typescript-eslint/parser": "^5.59.6", "@vitejs/plugin-react": "^4.0.0", + "@vitest/coverage-istanbul": "^0.34.6", "autoprefixer": "^10.4.13", "dependency-cruiser": "^13.1.0", "eslint": "^8.40.0", @@ -75,6 +79,7 @@ "vite": "^4.3.9", "vite-plugin-checker": "^0.6.0", "vite-plugin-glsl": "^1.1.2", - "vite-plugin-istanbul": "^5.0.0" + "vite-plugin-istanbul": "^5.0.0", + "vitest": "^0.34.6" } } diff --git a/frontend/tests/README.md b/frontend/tests/README.md new file mode 100644 index 000000000..649684218 --- /dev/null +++ b/frontend/tests/README.md @@ -0,0 +1,66 @@ +# Testing in Webviz + +## Installation + +In order to run `e2e` or `component` tests, you might need to install the required browser executables for `Playwright`. + +```bash +npx playwright install --with-deps +``` + +If this command does not work, try to install all dependencies manually: + +```bash +sudo apt install libopenjp2-7 libflite1 gstreamer1.0-libav +``` + +## Unit tests + +Unit tests are performed using `vitest`. All code (classes, functions, etc.) that is not depending on the GUI (i.e. all files that are not components or hooks) should be covered by a unit test. + +### How to write unit tests +https://vitest.dev/guide/#writing-tests + +### Where to place unit tests +All unit tests have to be placed in the `tests/unit/` folder. + +### Additional information +A check for test coverage is automatically performed using `istanbul / nyc`. The results are written to `coverage/unit`. + +## e2e tests + +*NOTE:* e2e tests are currently not performed due to missing test authentication towards our backend services + +End-to-end tests are performed using `Playwright`. Each module author is encouraged to write one or more e2e tests for their respective module. + +### How to write e2e tests +https://playwright.dev/docs/writing-tests + +### Where to place e2e tests +All e2e tests have to be placed in the `tests/e2e/` folder. + +### Additional information +You can run e2e tests in `ui` mode by running `npm run test:e2e:ui`. + +You can also generate e2e test dynamically by using `Playwright`'s test generator. + +Start the Webviz docker container on your local computer as usual and then run: + +```bash +npx playwright codegen http://localhost:8080 +``` + +Read more: https://playwright.dev/docs/codegen-intro + +## Component tests + +Component tests are performed using `Playwright`. Each author of a generic component (i.e. placed in the `src/lib/components/` folder) is encouraged to write one or more component tests for their respective component. + +### How to write component tests +https://playwright.dev/docs/test-components + +### Where to place component tests +All component tests have to be placed in the `tests/ct/` folder. + +### Additional information +Unfortunately, `Playwright` does not yet provide `coverage` for component tests. However, it would be beneficial to check how many components actually do have a related test such that we can improve our code base. This is a feature that should be implemented as soon as it becomes easier available. \ No newline at end of file diff --git a/frontend/tests/e2e/signIn.test.ts b/frontend/tests/e2e/signIn.test.ts index a7b8b31ae..8df2e27c3 100644 --- a/frontend/tests/e2e/signIn.test.ts +++ b/frontend/tests/e2e/signIn.test.ts @@ -4,7 +4,7 @@ test("Test sign in", async ({ page }) => { await page.goto("http://localhost:8080/"); const redirectUri = encodeURIComponent(new URL("api/auth-callback", page.url()).toString()); const urlRegEx = new RegExp( - `^https://login\\.microsoftonline\\.com/3aa4a235-b6e2-48d5-9195-7fcf05b459b0/oauth2/v2\\.0/authorize\\?client_id=[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}&response_type=code&redirect_uri=${redirectUri}&scope=User\\.Read\\+User\\.ReadBasic\\.All` + `^https://login\\.microsoftonline\\.com/3aa4a235-b6e2-48d5-9195-7fcf05b459b0/oauth2/v2\\.0/authorize\\?client_id=[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}&response_type=code&redirect_uri=${redirectUri}` ); await page.getByRole("button", { name: "Sign in" }).click(); await expect(page.url()).toEqual(expect.stringMatching(urlRegEx)); diff --git a/frontend/tests/unit/EnsembleParameters.test.ts b/frontend/tests/unit/EnsembleParameters.test.ts index 4021c74d9..f1d80c681 100644 --- a/frontend/tests/unit/EnsembleParameters.test.ts +++ b/frontend/tests/unit/EnsembleParameters.test.ts @@ -1,8 +1,7 @@ import { EnsembleParameters, Parameter, ParameterIdent, ParameterType } from "@framework/EnsembleParameters"; import { MinMax } from "@lib/utils/MinMax"; -import { expect } from "@playwright/test"; -import { test } from "./_baseFixtures"; +import { describe, expect, test } from "vitest"; // prettier-ignore const PARAM_ARR: Parameter[] = [ @@ -15,7 +14,7 @@ const PARAM_ARR: Parameter[] = [ {type: ParameterType.DISCRETE, name: "dparam_B", groupName: null, description: "descB", isConstant: false, realizations: [1,2,3], values: ["A", "B", "C"]}, ]; -test.describe("EnsembleParameters tests", () => { +describe("EnsembleParameters tests", () => { test("Get list of parameter idents", () => { const ensParams = new EnsembleParameters(PARAM_ARR); { @@ -100,7 +99,7 @@ test.describe("EnsembleParameters tests", () => { }); }); -test.describe("ParameterIdent tests", () => { +describe("ParameterIdent tests", () => { test("Conversion to/from string", () => { { const identStr = ParameterIdent.fromNameAndGroup("aName", "aGroup").toString(); diff --git a/frontend/tests/unit/EnsembleSet.test.ts b/frontend/tests/unit/EnsembleSet.test.ts index 81dcbe865..24945c84f 100644 --- a/frontend/tests/unit/EnsembleSet.test.ts +++ b/frontend/tests/unit/EnsembleSet.test.ts @@ -1,9 +1,8 @@ import { Ensemble } from "@framework/Ensemble"; import { EnsembleIdent } from "@framework/EnsembleIdent"; import { EnsembleSet } from "@framework/EnsembleSet"; -import { expect } from "@playwright/test"; -import { test } from "./_baseFixtures"; +import { describe, expect, test } from "vitest"; const ensembleArr = [ new Ensemble("11111111-aaaa-4444-aaaa-aaaaaaaaaaaa", "case1", "ens1", [], [], null), @@ -11,7 +10,7 @@ const ensembleArr = [ new Ensemble("22222222-aaaa-4444-aaaa-aaaaaaaaaaaa", "case2", "ens1", [], [], null), ]; -test.describe("EnsembleSet tests", () => { +describe("EnsembleSet tests", () => { test("access empty EnsembleSet", () => { const ensSet = new EnsembleSet([]); expect(ensSet.hasAnyEnsembles()).toBe(false); diff --git a/frontend/tests/unit/MinMax.test.ts b/frontend/tests/unit/MinMax.test.ts index 2863775f3..6e6849feb 100644 --- a/frontend/tests/unit/MinMax.test.ts +++ b/frontend/tests/unit/MinMax.test.ts @@ -1,9 +1,8 @@ import { MinMax } from "@lib/utils/MinMax"; -import { expect } from "@playwright/test"; -import { test } from "./_baseFixtures"; +import { describe, expect, test } from "vitest"; -test.describe("MinMax tests", () => { +describe("MinMax tests", () => { test("Check validity of MinMax instances", () => { expect(new MinMax(0, 1).isValid()).toBe(true); expect(new MinMax(-1, -1).isValid()).toBe(true); diff --git a/frontend/tests/unit/ParameterListFilterUtils.test.ts b/frontend/tests/unit/ParameterListFilterUtils.test.ts index e112f682d..c22867a53 100644 --- a/frontend/tests/unit/ParameterListFilterUtils.test.ts +++ b/frontend/tests/unit/ParameterListFilterUtils.test.ts @@ -6,9 +6,8 @@ import { getParametersMatchingSelectedNodes, } from "@framework/components/ParameterListFilter/private-utils/smartNodeSelectorUtils"; import { TreeDataNode } from "@lib/components/SmartNodeSelector"; -import { expect } from "@playwright/test"; -import { test } from "./_baseFixtures"; +import { describe, expect, test } from "vitest"; const CONTINUOUS_PARAMETER: ContinuousParameter = { type: ParameterType.CONTINUOUS, @@ -42,7 +41,7 @@ const DISCRETE_PARAMETER: DiscreteParameter = { values: [10, 11, 12], }; -test.describe("Test of utility functions for ParameterListFilter", () => { +describe("Test of utility functions for ParameterListFilter", () => { test("Test create and add node", () => { const myTestList: TreeDataNode[] = []; const newNode = createAndAddNode(myTestList, "my node"); diff --git a/frontend/tests/unit/SimulationTimeSeriesMatrixUtils.test.ts b/frontend/tests/unit/SimulationTimeSeriesMatrixUtils.test.ts index 73a0ee2b0..b13948557 100644 --- a/frontend/tests/unit/SimulationTimeSeriesMatrixUtils.test.ts +++ b/frontend/tests/unit/SimulationTimeSeriesMatrixUtils.test.ts @@ -1,10 +1,9 @@ import { scaleHexColorLightness } from "@modules/SimulationTimeSeriesMatrix/utils/colorUtils"; import { joinStringArrayToHumanReadableString } from "@modules/SimulationTimeSeriesMatrix/utils/stringUtils"; -import { expect } from "@playwright/test"; -import { test } from "./_baseFixtures"; +import { describe, expect, test } from "vitest"; -test.describe("Test of utility functions for SimulationTimeSeriesMatrix module", () => { +describe("Test of utility functions for SimulationTimeSeriesMatrix module", () => { test("Test join string array to human readable string", () => { expect(joinStringArrayToHumanReadableString(["a", "b", "c"])).toBe("a, b and c"); expect(joinStringArrayToHumanReadableString(["a"])).toBe("a"); diff --git a/frontend/tests/unit/_baseFixtures.ts b/frontend/tests/unit/_baseFixtures.ts deleted file mode 100644 index 6f5de27d5..000000000 --- a/frontend/tests/unit/_baseFixtures.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { test as baseTest } from "@playwright/test"; - -import * as crypto from "crypto"; -import * as fs from "fs"; -import * as path from "path"; - -const istanbulCLIOutput = path.join(process.cwd(), ".nyc_output"); - -export function generateUUID(): string { - return crypto.randomBytes(16).toString("hex"); -} - -export const test = baseTest.extend({ - context: async ({ context }, use) => { - await context.addInitScript(() => - window.addEventListener("beforeunload", () => - (window as any).collectIstanbulCoverage(JSON.stringify((window as any).__coverage__)) - ) - ); - await fs.promises.mkdir(istanbulCLIOutput, { recursive: true }); - await context.exposeFunction("collectIstanbulCoverage", (coverageJSON: string) => { - if (coverageJSON) - fs.writeFileSync( - path.join(istanbulCLIOutput, `playwright_coverage_${generateUUID()}.json`), - coverageJSON - ); - }); - await use(context); - for (const page of context.pages()) { - await page.evaluate(() => - (window as any).collectIstanbulCoverage(JSON.stringify((window as any).__coverage__)) - ); - } - }, -}); - -export const expect = test.expect; diff --git a/frontend/tests/unit/reservoirSimulationStringUtils.test.ts b/frontend/tests/unit/reservoirSimulationStringUtils.test.ts index 8c8a74305..257f380f7 100644 --- a/frontend/tests/unit/reservoirSimulationStringUtils.test.ts +++ b/frontend/tests/unit/reservoirSimulationStringUtils.test.ts @@ -1,9 +1,8 @@ import { simulationUnitReformat, simulationVectorDescription } from "@modules/_shared/reservoirSimulationStringUtils"; -import { expect } from "@playwright/test"; -import { test } from "./_baseFixtures"; +import { describe, expect, test } from "vitest"; -test.describe("Reservoir Simulation string utils tests", () => { +describe("Reservoir Simulation string utils tests", () => { test("Test simulationVectorDescription", () => { expect(simulationVectorDescription("INVALID_VECTOR")).toEqual("INVALID_VECTOR"); expect(simulationVectorDescription("WOPR:A1")).toEqual("Oil Production Rate, well A1"); diff --git a/frontend/tests/unit/stateStore.test.ts b/frontend/tests/unit/stateStore.test.ts index 67fa0140a..9d6dc178b 100644 --- a/frontend/tests/unit/stateStore.test.ts +++ b/frontend/tests/unit/stateStore.test.ts @@ -1,7 +1,6 @@ import { StateStore } from "@framework/StateStore"; -import { expect } from "@playwright/test"; -import { test } from "./_baseFixtures"; +import { describe, expect, test } from "vitest"; type TestState = { value: string; @@ -17,7 +16,7 @@ type TestState = { }[]; }; -test.describe("StateStore", () => { +describe("StateStore", () => { test("Can set and get value", () => { const store = new StateStore({ value: "first", diff --git a/frontend/tests/unit/timestampUtils.test.ts b/frontend/tests/unit/timestampUtils.test.ts index cabe53ab1..02c2e5dfc 100644 --- a/frontend/tests/unit/timestampUtils.test.ts +++ b/frontend/tests/unit/timestampUtils.test.ts @@ -1,11 +1,10 @@ import { hasTime, hasTimezone } from "@framework/utils/timestampUtils"; import { isoStringToTimestampUtcMs } from "@framework/utils/timestampUtils"; import { timestampUtcMsToCompactIsoString, timestampUtcMsToIsoString } from "@framework/utils/timestampUtils"; -import { expect } from "@playwright/test"; -import { test } from "./_baseFixtures"; +import { describe, expect, test } from "vitest"; -test.describe("TimestampUtils tests", () => { +describe("TimestampUtils tests", () => { test("Check if ISO 8601 string contains time", () => { expect(hasTime("2018-01-01")).toBe(false); expect(hasTime("2018-01-01T")).toBe(false); diff --git a/frontend/tsconfig.json b/frontend/tsconfig.json index c2f171c4d..88e0d4b98 100644 --- a/frontend/tsconfig.json +++ b/frontend/tsconfig.json @@ -18,10 +18,6 @@ }, "include": [ "src", - "tests", - "tests/unit/state-store.test.ts", - "tests/unit/.playwright.config.ts", - "tests/e2e/.playwright.config.ts" ], "references": [{ "path": "./tsconfig.node.json" }], "extends": "./aliases.json" diff --git a/frontend/vitest.config.ts b/frontend/vitest.config.ts new file mode 100644 index 000000000..e459fa064 --- /dev/null +++ b/frontend/vitest.config.ts @@ -0,0 +1,31 @@ +// vitest.config.ts +import path from "path"; +import { defineConfig } from "vitest/config"; + +import aliases from "./aliases.json"; + +export default defineConfig({ + test: { + coverage: { + include: ["src/**/*"], + extension: [".ts"], + provider: "istanbul", // or 'v8' + reportsDirectory: "./coverage/unit/", + exclude: ["**/api/**", "**/assets/**", "**/templates/**"], + all: true, + }, + include: ["./tests/unit/**/*.test.ts"], + }, + resolve: { + alias: Object.keys(aliases.compilerOptions.paths).reduce( + (prev, current) => ({ + ...prev, + [current.replace("/*", "")]: path.resolve( + __dirname, + aliases.compilerOptions.paths[current][0].replace("/*", "") + ), + }), + {} + ), + }, +});