diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f9fab3e1a..7149bc798 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -3,7 +3,7 @@ name: CI on: push: branches: - - '**' + - "**" pull_request: release: types: [created] @@ -19,22 +19,22 @@ jobs: docs: name: Docs Build runs-on: ubuntu-latest - + steps: - name: Checkout repository uses: actions/checkout@v4 - + - name: Set up Node.js uses: actions/setup-node@v4 with: node-version: 20 cache: npm cache-dependency-path: apps/docs/package-lock.json - + - name: Install docs dependencies working-directory: apps/docs run: npm ci - + - name: Build docs working-directory: apps/docs run: npm run docs:build @@ -83,7 +83,7 @@ jobs: run: npm run build data-management: - name: Data Management App + name: Data Management App 80% Coverage runs-on: ubuntu-latest steps: @@ -111,7 +111,6 @@ jobs: working-directory: apps/data-management run: | { - echo "VITE_APP_VERSION=ci" echo "VITE_APP_GOOGLE_MAPS_API_KEY=" echo "VITE_APP_GOOGLE_MAPS_MAP_ID=" echo "VITE_APP_PROXY_BASE_URL=http://127.0.0.1:8000" @@ -127,6 +126,60 @@ jobs: working-directory: apps/data-management run: npm run build + qc-app: + name: QC App Coverage + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Set up Node.js + uses: actions/setup-node@v4 + with: + node-version: 22 + cache: npm + cache-dependency-path: | + apps/qc-app/package-lock.json + packages/qc-utils/package-lock.json + packages/hydroserver-ts/package-lock.json + + # The QC app consumes @hydroserver/client as a local `file:` link and + # builds it from source, so the client's own dependencies (e.g. p-limit) + # must be installed for typecheck/build to resolve them. Mirrors the + # data-management job. + - name: Install TypeScript client dependencies + working-directory: packages/hydroserver-ts + run: npm ci + + - name: Install QC utils dependencies + working-directory: packages/qc-utils + run: npm ci --workspaces=false + + - name: Build QC utils + working-directory: packages/qc-utils + run: npm run build + + - name: Install QC app dependencies + working-directory: apps/qc-app + run: npm ci --workspaces=false + + - name: Install local QC utils package + working-directory: apps/qc-app + run: npm install ../../packages/qc-utils --no-save --package-lock=false --workspaces=false + + - name: Run QC app typecheck + working-directory: apps/qc-app + run: npm run typecheck + + - name: Run QC app coverage + working-directory: apps/qc-app + run: npm run coverage + + - name: Build QC app + working-directory: apps/qc-app + run: npm run build + hydroserverpy: name: Python Client runs-on: ubuntu-latest @@ -219,8 +272,8 @@ jobs: SMTP_URL: smtp://127.0.0.1:1025 run: python -m pytest - e2e: - name: End-to-End Tests + e2e-data-management: + name: Data Management E2E runs-on: ubuntu-latest env: E2E_DATABASE_URL: postgresql://hsdbadmin:admin@127.0.0.1:5432/hydroserver_e2e_${{ github.run_id }}_${{ github.run_attempt }} @@ -269,7 +322,6 @@ jobs: cache-dependency-path: | apps/data-management/package-lock.json packages/hydroserver-ts/package-lock.json - tests/e2e/package-lock.json - name: Install API dependencies run: | @@ -285,27 +337,84 @@ jobs: working-directory: apps/data-management run: npm ci - - name: Install E2E dependencies - working-directory: tests/e2e - run: npm ci - - name: Install Playwright browser - working-directory: tests/e2e + working-directory: apps/data-management run: npx playwright install --with-deps chromium - - name: Run Playwright E2E suite + - name: Run data-management Playwright E2E suite + working-directory: apps/data-management env: E2E_PYTHON: python - run: ./scripts/e2e test + run: npm run e2e + + - name: Upload Playwright report + if: always() + uses: actions/upload-artifact@v4 + with: + name: playwright-report-data-management + path: | + apps/data-management/playwright-report + apps/data-management/test-results + if-no-files-found: ignore + + e2e-qc-app: + name: QC App E2E + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Set up Node.js + uses: actions/setup-node@v4 + with: + node-version: 22 + cache: npm + cache-dependency-path: | + apps/qc-app/package-lock.json + packages/qc-utils/package-lock.json + packages/hydroserver-ts/package-lock.json + + # The QC app builds @hydroserver/client from source via a local `file:` + # link, so the client's own dependencies must be present for the e2e + # build/serve to resolve them. `--workspaces=false` keeps each install + # scoped to its own package-lock.json instead of the workspace root. + - name: Install TypeScript client dependencies + working-directory: packages/hydroserver-ts + run: npm ci + + - name: Install QC utils dependencies + working-directory: packages/qc-utils + run: npm ci --workspaces=false + + - name: Build QC utils + working-directory: packages/qc-utils + run: npm run build + + - name: Install QC app dependencies + working-directory: apps/qc-app + run: npm ci --workspaces=false + + - name: Install local QC utils package + working-directory: apps/qc-app + run: npm install ../../packages/qc-utils --no-save --package-lock=false --workspaces=false + + - name: Install Playwright browsers + working-directory: apps/qc-app + run: npx playwright install --with-deps chromium firefox + + - name: Run QC app Playwright E2E suite + working-directory: apps/qc-app + run: npm run e2e - name: Upload Playwright report if: always() uses: actions/upload-artifact@v4 with: - name: playwright-report + name: playwright-report-qc-app path: | - tests/e2e/playwright-report - tests/e2e/test-results + apps/qc-app/playwright-report + apps/qc-app/test-results if-no-files-found: ignore trigger-release: @@ -317,10 +426,20 @@ jobs: github.event.release.prerelease || (!contains(needs.*.result, 'failure') && !contains(needs.*.result, 'cancelled')) ) - needs: [docs, hydroserver-ts, data-management, hydroserverpy, api, e2e] + needs: + [ + docs, + hydroserver-ts, + data-management, + qc-app, + hydroserverpy, + api, + e2e-data-management, + e2e-qc-app, + ] permissions: actions: write - + steps: - name: Trigger release workflow uses: actions/github-script@v7 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index de416159f..22b10dbc6 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -40,13 +40,6 @@ jobs: working-directory: apps/data-management run: npm ci - - name: Configure environment variables - working-directory: apps/data-management - run: | - cat << EOF > .env - VITE_APP_VERSION=${{ inputs.tag }} - EOF - - name: Build working-directory: apps/data-management run: npm run django-build @@ -61,6 +54,64 @@ jobs: name: data-management-app-${{ inputs.tag }} path: apps/data-management/data-management-app-${{ inputs.tag }}.zip + build-quality-control-app: + name: Build Quality Control App + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + ref: ${{ inputs.tag }} + + - name: Set up Node 22.x + uses: actions/setup-node@v4 + with: + node-version: 22.x + cache: npm + cache-dependency-path: | + apps/qc-app/package-lock.json + packages/hydroserver-ts/package-lock.json + packages/qc-utils/package-lock.json + + - name: Install TypeScript client dependencies + working-directory: packages/hydroserver-ts + run: npm ci + + - name: Install QC utils dependencies + working-directory: packages/qc-utils + run: npm ci + + - name: Build QC utils + working-directory: packages/qc-utils + run: npm run build + + - name: Install dependencies + working-directory: apps/qc-app + run: npm ci + + - name: Configure environment variables + working-directory: apps/qc-app + run: | + cat << EOF > .env + VITE_APP_ROUTE=/qc/ + EOF + + - name: Build + working-directory: apps/qc-app + run: npm run django-build + + - name: Create ZIP archive + working-directory: apps/qc-app + run: zip -r qc-app-${{ inputs.tag }}.zip dist + + - name: Upload build artifact + uses: actions/upload-artifact@v4 + with: + name: qc-app-${{ inputs.tag }} + path: apps/qc-app/qc-app-${{ inputs.tag }}.zip + + publish-hydroserverpy: name: Publish hydroserverpy to PyPI runs-on: ubuntu-latest @@ -117,7 +168,7 @@ jobs: build-and-push-docker-image: name: Build and Push Docker Image to GHCR runs-on: ubuntu-latest - needs: [build-data-management-app] + needs: [build-data-management-app, build-quality-control-app] steps: - name: Checkout repository @@ -131,9 +182,18 @@ jobs: name: data-management-app-${{ inputs.tag }} path: . - - name: Rename artifact for Docker build + - name: Rename data management artifact for Docker build run: mv data-management-app-${{ inputs.tag }}.zip data_mgmt_app.zip + - name: Download quality control app artifact + uses: actions/download-artifact@v4 + with: + name: qc-app-${{ inputs.tag }} + path: . + + - name: Rename quality control artifact for Docker build + run: mv qc-app-${{ inputs.tag }}.zip qc_app.zip + - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 000000000..e96b4aedf --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,249 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "Start Data Management App", + "type": "shell", + "command": "npm run dev", + "options": { + "cwd": "${workspaceFolder}/apps/data-management", + "env": { + "VITE_APP_PROXY_BASE_URL": "http://127.0.0.1:8000", + "VITE_HYDROSERVER_CLIENT_LOCAL": "1", + "VITE_HYDROSERVER_CLIENT_PATH": "${workspaceFolder}/packages/hydroserver-ts/src" + } + }, + "presentation": { + "group": "dev-environment", + "panel": "dedicated", + "reveal": "always" + }, + "problemMatcher": [] + }, + { + "label": "Start QC App", + "type": "shell", + "command": "npm run dev:qc", + "options": { + "cwd": "${workspaceFolder}", + "env": { + "VITE_APP_DISABLE_COOP": "1" + } + }, + "presentation": { + "group": "dev-environment", + "panel": "dedicated", + "reveal": "always" + }, + "problemMatcher": [] + }, + { + "label": "Start Data Management App (Playground)", + "type": "shell", + "command": "npm run dev", + "options": { + "cwd": "${workspaceFolder}/apps/data-management", + "env": { + "VITE_APP_PROXY_BASE_URL": "https://playground.hydroserver.org", + "VITE_HYDROSERVER_CLIENT_LOCAL": "1", + "VITE_HYDROSERVER_CLIENT_PATH": "${workspaceFolder}/packages/hydroserver-ts/src" + } + }, + "presentation": { + "group": "playground", + "panel": "dedicated", + "reveal": "always" + }, + "problemMatcher": [] + }, + { + "label": "Start QC App (Playground)", + "type": "shell", + "command": "npm run dev:qc", + "options": { + "cwd": "${workspaceFolder}", + "env": { + "VITE_APP_DISABLE_COOP": "1" + } + }, + "presentation": { + "group": "playground", + "panel": "dedicated", + "reveal": "always" + }, + "problemMatcher": [] + }, + { + "label": "Start Data Management App (Utah Water Rights Dev)", + "type": "shell", + "command": "npm run dev", + "options": { + "cwd": "${workspaceFolder}/apps/data-management", + "env": { + "VITE_APP_PROXY_BASE_URL": "https://hydroserverdev.waterrights.utah.gov", + "VITE_HYDROSERVER_CLIENT_LOCAL": "1", + "VITE_HYDROSERVER_CLIENT_PATH": "${workspaceFolder}/packages/hydroserver-ts/src" + } + }, + "presentation": { + "group": "utah-water-rights-dev", + "panel": "dedicated", + "reveal": "always" + }, + "problemMatcher": [] + }, + { + "label": "Start QC App (Utah Water Rights Dev)", + "type": "shell", + "command": "npm run dev:qc", + "options": { + "cwd": "${workspaceFolder}", + "env": { + "VITE_APP_PROXY_BASE_URL": "https://hydroserverdev.waterrights.utah.gov", + "VITE_APP_DISABLE_COOP": "1" + } + }, + "presentation": { + "group": "utah-water-rights-dev", + "panel": "dedicated", + "reveal": "always" + }, + "problemMatcher": [] + }, + { + "label": "Start Data Management App (Utah Water Rights Test)", + "type": "shell", + "command": "npm run dev", + "options": { + "cwd": "${workspaceFolder}/apps/data-management", + "env": { + "VITE_APP_PROXY_BASE_URL": "https://hydroservertest.waterrights.utah.gov", + "VITE_HYDROSERVER_CLIENT_LOCAL": "1", + "VITE_HYDROSERVER_CLIENT_PATH": "${workspaceFolder}/packages/hydroserver-ts/src" + } + }, + "presentation": { + "group": "utah-water-rights-test", + "panel": "dedicated", + "reveal": "always" + }, + "problemMatcher": [] + }, + { + "label": "Start QC App (Utah Water Rights Test)", + "type": "shell", + "command": "npm run dev:qc", + "options": { + "cwd": "${workspaceFolder}", + "env": { + "VITE_APP_PROXY_BASE_URL": "https://hydroservertest.waterrights.utah.gov", + "VITE_APP_DISABLE_COOP": "1" + } + }, + "presentation": { + "group": "utah-water-rights-test", + "panel": "dedicated", + "reveal": "always" + }, + "problemMatcher": [] + }, + { + "label": "Start Backend", + "type": "shell", + "command": "${workspaceFolder}/scripts/dev-api-command manage.py runserver 0.0.0.0:8000", + "options": { + "cwd": "${workspaceFolder}/django" + }, + "presentation": { + "group": "dev-environment", + "panel": "dedicated", + "reveal": "always" + }, + "problemMatcher": [] + }, + { + "label": "Start Redis", + "type": "shell", + "command": "if [ \"$(docker inspect -f '{{.State.Running}}' hydroserver-redis 2>/dev/null)\" = \"true\" ]; then echo \"Redis already running\"; elif docker inspect -f '{{.State.Running}}' hydroserver-redis >/dev/null 2>&1; then docker start hydroserver-redis; else docker run --name hydroserver-redis -p 6379:6379 -d --restart unless-stopped redis; fi", + "problemMatcher": [] + }, + { + "label": "Start Celery Worker", + "type": "shell", + "command": "${workspaceFolder}/scripts/dev-api-command -m celery -A hydroserver worker --loglevel=INFO", + "options": { + "cwd": "${workspaceFolder}/django" + }, + "presentation": { + "group": "dev-environment", + "panel": "dedicated", + "reveal": "always" + }, + "problemMatcher": [] + }, + { + "label": "Start Celery Beat", + "type": "shell", + "command": "${workspaceFolder}/scripts/dev-api-command -m celery -A hydroserver beat --loglevel=INFO", + "options": { + "cwd": "${workspaceFolder}/django" + }, + "presentation": { + "group": "dev-environment", + "panel": "dedicated", + "reveal": "always" + }, + "problemMatcher": [] + }, + { + "label": "Start Dev Environment", + "dependsOn": [ + "Start Backend", + "Start Data Management App", + "Start QC App" + ], + "dependsOrder": "parallel", + "problemMatcher": [] + }, + { + "label": "Start Frontends Against Playground", + "dependsOn": [ + "Start Data Management App (Playground)", + "Start QC App (Playground)" + ], + "dependsOrder": "parallel", + "problemMatcher": [] + }, + { + "label": "Start Frontends Against Utah Water Rights Dev", + "dependsOn": [ + "Start Data Management App (Utah Water Rights Dev)", + "Start QC App (Utah Water Rights Dev)" + ], + "dependsOrder": "parallel", + "problemMatcher": [] + }, + { + "label": "Start Frontends Against Utah Water Rights Test", + "dependsOn": [ + "Start Data Management App (Utah Water Rights Test)", + "Start QC App (Utah Water Rights Test)" + ], + "dependsOrder": "parallel", + "problemMatcher": [] + }, + { + "label": "Start Dev Environment + Celery", + "dependsOn": [ + "Start Redis", + "Start Backend", + "Start Data Management App", + "Start QC App", + "Start Celery Worker", + "Start Celery Beat" + ], + "dependsOrder": "parallel", + "problemMatcher": [] + } + ] +} diff --git a/README.md b/README.md index 04bf6f527..df204ec66 100644 --- a/README.md +++ b/README.md @@ -33,14 +33,21 @@ HydroServer is organized as follows: - `deploy/dev`: local development deployment assets. - `deploy/prod`: production deployment assets. -**System Tests** (`tests/`) +**End-to-End Tests** -- `tests/e2e`: end-to-end tests for the HydroServer product as a whole. - The Playwright suite boots the API and data-management app automatically against an isolated `hydroserver_e2e` database. The human release signoff list lives in `tests/manual/MANUAL_RELEASE_TESTING_CHECKLIST.md`. +Each frontend owns its own Playwright suite and config — run `npm run e2e` from +inside an app directory: + +- `apps/data-management/e2e`: boots the API and app automatically against an + isolated `hydroserver_e2e` database (needs local `postgres` + `redis`). +- `apps/qc-app/e2e`: runs against the QC Vite dev server with mocked responses + (no API needed). + +From the repo root, `npm run e2e` runs both. The human release signoff list lives +in `tests/manual/MANUAL_RELEASE_TESTING_CHECKLIST.md`. **Scripts** (`scripts/`) -- `scripts/e2e`: canonical local and CI entrypoint for the browser E2E suite. - `scripts/release-test`: root runner for API, Python-client, and browser release regression suites. ## History diff --git a/apps/data-management/.gitignore b/apps/data-management/.gitignore index fbb298e2a..4379921af 100644 --- a/apps/data-management/.gitignore +++ b/apps/data-management/.gitignore @@ -12,6 +12,11 @@ dist dist-ssr *.local +# Playwright e2e +playwright-report/ +test-results/ +playwright/.cache/ + # Editor directories and files .vscode/* coverage/* diff --git a/tests/manual/MANUAL_RELEASE_TESTING_CHECKLIST.md b/apps/data-management/MANUAL_RELEASE_TESTING_CHECKLIST.md similarity index 100% rename from tests/manual/MANUAL_RELEASE_TESTING_CHECKLIST.md rename to apps/data-management/MANUAL_RELEASE_TESTING_CHECKLIST.md diff --git a/apps/data-management/e2e/README.md b/apps/data-management/e2e/README.md new file mode 100644 index 000000000..aea8e036d --- /dev/null +++ b/apps/data-management/e2e/README.md @@ -0,0 +1,39 @@ +# Data Management End-to-End Tests + +Playwright suite for the data-management frontend. Run it from this app directory: + +```bash +npm run e2e +``` + +Playwright (`apps/data-management/playwright.config.ts`) starts everything the +suite needs: + +- the Django API in `django` (on `http://127.0.0.1:18000`) +- this Vue app, built and previewed (on `http://127.0.0.1:14173`) +- a deterministic seeded database created by + `e2e/scripts/ensure_e2e_database.py` + `python manage.py setup_e2e_data` + +It requires local `postgres` and `redis` to be running. The runner is isolated +from normal local development and uses a dedicated `hydroserver_e2e` database. + +## Seeded users + +All seeded users use the password `HydroServer123!`: + +- `owner@example.com`: primary workspace owner for CRUD flows +- `viewer@example.com`: collaborator target +- `unaffiliated@example.com`: workspace-transfer target +- `profile@example.com`: profile-editing user with organization data +- `delete-me@example.com`: disposable account-deletion user + +## Notes + +- Most authenticated page tests use a seeded session cookie helper because the + app login flow does not persist the session cookie reliably under browser + automation. Browser login itself is still exercised through the UI. +- The visualization tests rely on the app honoring `VITE_APP_PROXY_BASE_URL` so + the isolated Playwright API instance is used instead of the local dev port. +- The manual release checklist in + `tests/manual/MANUAL_RELEASE_TESTING_CHECKLIST.md` complements this suite for + external-system checks that are not deterministic in headless automation. diff --git a/tests/e2e/scripts/ensure_e2e_database.py b/apps/data-management/e2e/scripts/ensure_e2e_database.py similarity index 100% rename from tests/e2e/scripts/ensure_e2e_database.py rename to apps/data-management/e2e/scripts/ensure_e2e_database.py diff --git a/tests/e2e/specs/account.spec.ts b/apps/data-management/e2e/specs/account.spec.ts similarity index 100% rename from tests/e2e/specs/account.spec.ts rename to apps/data-management/e2e/specs/account.spec.ts diff --git a/tests/e2e/specs/auth.setup.ts b/apps/data-management/e2e/specs/auth.setup.ts similarity index 100% rename from tests/e2e/specs/auth.setup.ts rename to apps/data-management/e2e/specs/auth.setup.ts diff --git a/tests/e2e/specs/auth.spec.ts b/apps/data-management/e2e/specs/auth.spec.ts similarity index 100% rename from tests/e2e/specs/auth.spec.ts rename to apps/data-management/e2e/specs/auth.spec.ts diff --git a/apps/data-management/e2e/specs/general.spec.ts b/apps/data-management/e2e/specs/general.spec.ts new file mode 100644 index 000000000..cc5e250a9 --- /dev/null +++ b/apps/data-management/e2e/specs/general.spec.ts @@ -0,0 +1,44 @@ +import { expect, test } from '@playwright/test' + +import { authenticateSession } from '../support/auth' +import { fixtures, users } from '../support/fixtures' + +test.describe('general navigation', () => { + test('root redirects to browse and the logo returns to browse from about', async ({ + page, + }) => { + await page.goto('/') + await expect(page).toHaveURL(/\/browse$/) + await expect( + page.getByRole('heading', { name: 'Monitoring sites', level: 1 }) + ).toBeVisible() + + await page.goto('/about') + await expect(page.getByText(/About (HydroServer|us)/).first()).toBeVisible() + + await page.getByRole('link', { name: 'Logo', exact: true }).first().click() + await expect(page).toHaveURL(/\/browse$/) + }) + + test('browse page site type filter filters the site list and can be cleared', async ({ + page, + }) => { + await authenticateSession(page, users.owner.email, users.owner.password) + await page.goto('/browse?siteTypes=Private') + await expect( + page.getByRole('heading', { name: 'Monitoring sites', level: 1 }) + ).toBeVisible() + + await expect( + page.getByText(fixtures.things.private.name, { exact: true }) + ).toBeVisible() + await expect( + page.getByText(fixtures.things.public.name, { exact: true }) + ).toHaveCount(0) + + await page.getByRole('button', { name: 'Reset', exact: true }).click() + await expect( + page.getByText(fixtures.things.public.name, { exact: true }) + ).toBeVisible() + }) +}) diff --git a/tests/e2e/specs/metadata.spec.ts b/apps/data-management/e2e/specs/metadata.spec.ts similarity index 97% rename from tests/e2e/specs/metadata.spec.ts rename to apps/data-management/e2e/specs/metadata.spec.ts index 77160a9d8..dafd09f45 100644 --- a/tests/e2e/specs/metadata.spec.ts +++ b/apps/data-management/e2e/specs/metadata.spec.ts @@ -273,18 +273,26 @@ test.describe('metadata management', () => { test('metadata search box filters visible rows', async ({ page }) => { await authenticateSession(page, users.owner.email, users.owner.password) await page.goto('/metadata') + await selectWorkspace(page, fixtures.workspaces.public.name) + + const workspaceTable = page.getByTestId('workspace-metadata-table') const searchBox = page.getByRole('textbox', { name: /search/i }).first() await expect(searchBox).toBeVisible() await searchBox.fill('Public Assigned Sensor') await expect( - page.locator('tr').filter({ hasText: 'Public Assigned Sensor' }).first() + workspaceTable + .locator('tr') + .filter({ hasText: 'Public Assigned Sensor' }) + .first() ).toBeVisible() await searchBox.fill('zzz-no-match-e2e') await expect( - page.locator('tr').filter({ hasText: 'Public Assigned Sensor' }) + workspaceTable + .locator('tr') + .filter({ hasText: 'Public Assigned Sensor' }) ).toHaveCount(0) await searchBox.clear() diff --git a/tests/e2e/specs/orchestration.spec.ts b/apps/data-management/e2e/specs/orchestration.spec.ts similarity index 100% rename from tests/e2e/specs/orchestration.spec.ts rename to apps/data-management/e2e/specs/orchestration.spec.ts diff --git a/tests/e2e/specs/sites.spec.ts b/apps/data-management/e2e/specs/sites.spec.ts similarity index 94% rename from tests/e2e/specs/sites.spec.ts rename to apps/data-management/e2e/specs/sites.spec.ts index 8dae09662..a5f8f0840 100644 --- a/tests/e2e/specs/sites.spec.ts +++ b/apps/data-management/e2e/specs/sites.spec.ts @@ -1,4 +1,4 @@ -import { expect, test } from '@playwright/test' +import { expect, test, type Page } from '@playwright/test' import { authenticateSession } from '../support/auth' import { fixtures, users } from '../support/fixtures' @@ -8,6 +8,9 @@ import { selectWorkspace, } from '../support/ui' +const datastreamEntriesByName = (page: Page, name: string) => + page.locator('[data-datastream-id]').filter({ hasText: name }) + test.describe('sites and workspaces', () => { const apiBaseUrl = process.env.E2E_API_BASE_URL || 'http://127.0.0.1:18000' const sitePhotoPng = Buffer.from( @@ -254,10 +257,7 @@ test.describe('sites and workspaces', () => { .fill('Temporary datastream created by the Playwright CRUD suite.') await page.getByRole('button', { name: 'Create datastream' }).click() - const datastreamRow = page - .locator('tr, .datastream-card') - .filter({ hasText: datastreamName }) - .first() + const datastreamRow = datastreamEntriesByName(page, datastreamName).first() await expect(datastreamRow).toBeVisible() await datastreamRow.locator('[data-testid^="datastream-actions-"]').click() @@ -265,10 +265,10 @@ test.describe('sites and workspaces', () => { await page.getByLabel('Datastream name *').fill(renamedDatastreamName) await page.getByRole('button', { name: 'Update datastream' }).click() - const renamedDatastreamRow = page - .locator('tr, .datastream-card') - .filter({ hasText: renamedDatastreamName }) - .first() + const renamedDatastreamRow = datastreamEntriesByName( + page, + renamedDatastreamName + ).first() await expect(renamedDatastreamRow).toBeVisible() await renamedDatastreamRow @@ -278,9 +278,7 @@ test.describe('sites and workspaces', () => { await page.locator('input').last().fill('Delete') await page.getByRole('button', { name: 'Confirm' }).click() await expect( - page.locator('tr, .datastream-card').filter({ - hasText: renamedDatastreamName, - }) + datastreamEntriesByName(page, renamedDatastreamName) ).toHaveCount(0) await page.getByTestId('delete-site-button').click() @@ -363,9 +361,7 @@ test.describe('sites and workspaces', () => { .toBe(true) await anonymousPage.goto(`/sites/${fixtures.things.public.id}`) await expect( - anonymousPage.locator('tr, .datastream-card').filter({ - hasText: fixtures.datastreams.public.name, - }) + datastreamEntriesByName(anonymousPage, fixtures.datastreams.public.name) ).toHaveCount(0) await datastreamPrivacyToggle.click() await expect @@ -398,11 +394,10 @@ test.describe('sites and workspaces', () => { }) .toBe(false) await anonymousPage.goto(`/sites/${fixtures.things.public.id}`) - const anonymousPublicDatastreamRow = anonymousPage - .locator('tr, .datastream-card') - .filter({ - hasText: fixtures.datastreams.public.name, - }) + const anonymousPublicDatastreamRow = datastreamEntriesByName( + anonymousPage, + fixtures.datastreams.public.name + ) await expect(anonymousPublicDatastreamRow).toHaveCount(1) await dataVisibilityToggle.click() await expect @@ -417,9 +412,7 @@ test.describe('sites and workspaces', () => { await anonymousPage.goto(`/sites/${fixtures.things.public.id}`) await expect( - anonymousPage.locator('tr, .datastream-card').filter({ - hasText: fixtures.datastreams.public.name, - }) + datastreamEntriesByName(anonymousPage, fixtures.datastreams.public.name) ).toHaveCount(1) await anonymousContext.close() @@ -431,10 +424,10 @@ test.describe('sites and workspaces', () => { await authenticateSession(page, users.owner.email, users.owner.password) await page.goto(`/sites/${fixtures.things.public.id}`) - const datastreamRow = page - .locator('tr, .datastream-card') - .filter({ hasText: fixtures.datastreams.public.name }) - .first() + const datastreamRow = datastreamEntriesByName( + page, + fixtures.datastreams.public.name + ).first() await expect(datastreamRow).toBeVisible() await datastreamRow @@ -485,10 +478,10 @@ test.describe('sites and workspaces', () => { await expect(page.getByText(fixtures.datastreams.public.name, { exact: true })).toBeVisible() await page.goto(`/sites/${fixtures.things.public.id}`) - const datastreamRow = page - .locator('tr, .datastream-card') - .filter({ hasText: fixtures.datastreams.public.name }) - .first() + const datastreamRow = datastreamEntriesByName( + page, + fixtures.datastreams.public.name + ).first() await datastreamRow .locator('[data-testid^="datastream-actions-"]') .click() diff --git a/tests/e2e/specs/transfers.spec.ts b/apps/data-management/e2e/specs/transfers.spec.ts similarity index 100% rename from tests/e2e/specs/transfers.spec.ts rename to apps/data-management/e2e/specs/transfers.spec.ts diff --git a/tests/e2e/specs/visualization.spec.ts b/apps/data-management/e2e/specs/visualization.spec.ts similarity index 100% rename from tests/e2e/specs/visualization.spec.ts rename to apps/data-management/e2e/specs/visualization.spec.ts diff --git a/tests/e2e/specs/workspaces.spec.ts b/apps/data-management/e2e/specs/workspaces.spec.ts similarity index 91% rename from tests/e2e/specs/workspaces.spec.ts rename to apps/data-management/e2e/specs/workspaces.spec.ts index faebd2c0e..23a8ba064 100644 --- a/tests/e2e/specs/workspaces.spec.ts +++ b/apps/data-management/e2e/specs/workspaces.spec.ts @@ -21,7 +21,9 @@ test.describe('workspace management', () => { await page.getByLabel('Name *').fill(workspaceName) await page.getByRole('button', { name: 'Save' }).click() - const workspaceRow = page.getByRole('row', { name: new RegExp(workspaceName) }) + const workspaceRow = page.getByRole('row', { + name: new RegExp(workspaceName), + }) await expect(workspaceRow).toBeVisible() await workspaceRow.getByRole('button').nth(1).click() @@ -74,7 +76,9 @@ test.describe('workspace management', () => { await page.getByLabel('Name *').fill(workspaceName) await page.getByRole('button', { name: 'Save' }).click() - const workspaceRow = page.getByRole('row', { name: new RegExp(workspaceName) }) + const workspaceRow = page.getByRole('row', { + name: new RegExp(workspaceName), + }) await expect(workspaceRow).toBeVisible() await workspaceRow .locator('[data-testid^="workspace-access-control-"]') @@ -93,19 +97,13 @@ test.describe('workspace management', () => { await expect(collaboratorRow).toContainText('Viewer') await collaboratorRow.click() - await page - .getByTestId(`edit-collaborator-${users.viewer.email}`) - .click() + await page.getByTestId(`edit-collaborator-${users.viewer.email}`).click() await collaboratorRow.getByRole('combobox').first().click() await page.getByRole('option', { name: /Editor/ }).click() - await page - .getByTestId(`save-collaborator-${users.viewer.email}`) - .click() + await page.getByTestId(`save-collaborator-${users.viewer.email}`).click() await expect(collaboratorRow).toContainText('Editor') - await page - .getByTestId(`remove-collaborator-${users.viewer.email}`) - .click() + await page.getByTestId(`remove-collaborator-${users.viewer.email}`).click() await expect(collaboratorRow).toHaveCount(0) await page.getByRole('button', { name: 'Close' }).click() diff --git a/tests/e2e/support/auth.ts b/apps/data-management/e2e/support/auth.ts similarity index 100% rename from tests/e2e/support/auth.ts rename to apps/data-management/e2e/support/auth.ts diff --git a/tests/e2e/support/fixtures.ts b/apps/data-management/e2e/support/fixtures.ts similarity index 100% rename from tests/e2e/support/fixtures.ts rename to apps/data-management/e2e/support/fixtures.ts diff --git a/tests/e2e/support/globalSetup.ts b/apps/data-management/e2e/support/globalSetup.ts similarity index 79% rename from tests/e2e/support/globalSetup.ts rename to apps/data-management/e2e/support/globalSetup.ts index a6a6a9314..62fc6274a 100644 --- a/tests/e2e/support/globalSetup.ts +++ b/apps/data-management/e2e/support/globalSetup.ts @@ -1,5 +1,5 @@ import type { FullConfig } from '@playwright/test' -import { chromium, request } from '@playwright/test' +import { chromium } from '@playwright/test' const FRONTEND_READY_TIMEOUT_MS = 30_000 const APP_BOOT_ROUTE = '/browse' @@ -15,12 +15,6 @@ function getAppBaseUrl(config: FullConfig) { return `http://${appHost}:${appPort}` } -function getApiBaseUrl() { - const apiHost = process.env.E2E_API_HOST || '127.0.0.1' - const apiPort = process.env.E2E_API_PORT || '18000' - return `http://${apiHost}:${apiPort}` -} - function summarizeMessages(title: string, messages: string[]) { if (!messages.length) return `${title}: none` return `${title}:\n- ${messages.slice(0, 5).join('\n- ')}` @@ -28,18 +22,10 @@ function summarizeMessages(title: string, messages: string[]) { export default async function globalSetup(config: FullConfig) { const appBaseUrl = getAppBaseUrl(config) - const apiBaseUrl = getApiBaseUrl() - - const apiContext = await request.newContext({ baseURL: apiBaseUrl }) - const apiResponse = await apiContext.get('/api/data/workspaces') - if (!apiResponse.ok()) { - await apiContext.dispose() - throw new Error( - `E2E startup check failed before browser launch: API readiness probe returned ${apiResponse.status()} from ${apiBaseUrl}/api/data/workspaces` - ) - } - await apiContext.dispose() + // Playwright's webServer `url` checks already block until the API and app + // respond, so this hook only needs to verify the Vue app actually mounts + // (catches a "server serves HTML but the SPA white-screens" boot). const browser = await chromium.launch() const page = await browser.newPage() const consoleErrors: string[] = [] diff --git a/tests/e2e/support/ui.ts b/apps/data-management/e2e/support/ui.ts similarity index 100% rename from tests/e2e/support/ui.ts rename to apps/data-management/e2e/support/ui.ts diff --git a/apps/data-management/package-lock.json b/apps/data-management/package-lock.json index 3158e0b34..4bd28cc23 100644 --- a/apps/data-management/package-lock.json +++ b/apps/data-management/package-lock.json @@ -1,18 +1,16 @@ { "name": "frontend", - "version": "0.1.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "frontend", - "version": "0.1.0", "dependencies": { "@googlemaps/js-api-loader": "^1.16.6", - "@googlemaps/markerclusterer": "^2.5.3", - "@hydroserver/client": "0.5.0", + "@hydroserver/client": "file:../../packages/hydroserver-ts", "@mdi/js": "^7.4.47", "country-list": "^2.3.0", + "jszip": "^3.10.1", "lodash-es": "^4.17.21", "maska": "^3.2.0", "ol": "^10.5.0", @@ -24,6 +22,7 @@ "vuetify": "^3.11.8" }, "devDependencies": { + "@playwright/test": "^1.61.0", "@tailwindcss/vite": "^4.1.18", "@types/country-list": "^2.1.4", "@types/google.maps": "^3.54.10", @@ -38,7 +37,6 @@ "jsdom": "^22.1.0", "postcss": "^8.4.23", "prettier": "^2.8.8", - "resize-observer-polyfill": "^1.5.1", "rxjs": "^7.8.1", "sass": "^1.62.0", "tailwindcss": "^4.1.18", @@ -49,6 +47,25 @@ "vue-tsc": "^3.2.4" } }, + "../../packages/hydroserver-ts": { + "name": "@hydroserver/client", + "version": "1.0.0-beta.3", + "license": "BSD-3-Clause", + "devDependencies": { + "@types/node": "^24.1.0", + "@vitest/coverage-v8": "^4.1.0", + "jsdom": "^22.1.0", + "openapi-zod-client": "^1.18.3", + "p-limit": "^6.2.0", + "tsx": "^4.16.2", + "typescript": "^5.8.3", + "vite": "^7.3.1", + "vitest": "^4.0.18" + }, + "engines": { + "node": ">=20" + } + }, "node_modules/@babel/helper-string-parser": { "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", @@ -553,26 +570,9 @@ "integrity": "sha512-c2erv2k7P2ilYzMmtYcMgAR21AULosQuUHJbStnrvRk2dG93k5cqptDrh9A8p+ZNlyhiqEOgHW7N9PAizdUM7Q==", "license": "Apache-2.0" }, - "node_modules/@googlemaps/markerclusterer": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/@googlemaps/markerclusterer/-/markerclusterer-2.6.2.tgz", - "integrity": "sha512-U6uVhq8iWhiIckA89sgRu8OK35mjd6/3CuoZKWakKEf0QmRRWpatlsPb3kqXkoWSmbcZkopRiI4dnW6DQSd7bQ==", - "license": "Apache-2.0", - "dependencies": { - "@types/supercluster": "^7.1.3", - "fast-equals": "^5.2.2", - "supercluster": "^8.0.1" - } - }, "node_modules/@hydroserver/client": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/@hydroserver/client/-/client-0.5.0.tgz", - "license": "ISC", - "dependencies": { - "cross-fetch": "^4.1.0", - "jszip": "^3.10.1", - "p-limit": "^6.2.0" - } + "resolved": "../../packages/hydroserver-ts", + "link": true }, "node_modules/@jridgewell/gen-mapping": { "version": "0.3.13", @@ -945,10 +945,26 @@ "integrity": "sha512-VgffxawQde93xKxT3qap3OH+meZf7VaSB5Sqd4Rqc+FP5alWbpOyan/7tRbOAvynjpG3GpdtAuGU/NdhQpmrog==", "license": "MIT" }, + "node_modules/@playwright/test": { + "version": "1.61.0", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.61.0.tgz", + "integrity": "sha512-cKA5B6lpFEMyMGjxF54QihfYpB4FkEGH+qZhtArDEG+wezQAJY8Pq6C7T1SjWz+FFzt3TbyoXBQYk/0292TdJA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "playwright": "1.61.0" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.45.1.tgz", - "integrity": "sha512-NEySIFvMY0ZQO+utJkgoMiCAjMrGvnbDLHvcmlA33UXJpYBCvlBEbMMtV837uCkS+plG2umfhn0T5mMAxGrlRA==", + "version": "4.61.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.61.0.tgz", + "integrity": "sha512-dnxczajOqt0gesZlN5pGQ1s1imQVrsmCw5G2Ci4oM+0WvNz3pyRnlWrT7McoZIb8VlFwCawdmbWRmxRn7HI+VQ==", "cpu": [ "arm" ], @@ -960,9 +976,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.45.1.tgz", - "integrity": "sha512-ujQ+sMXJkg4LRJaYreaVx7Z/VMgBBd89wGS4qMrdtfUFZ+TSY5Rs9asgjitLwzeIbhwdEhyj29zhst3L1lKsRQ==", + "version": "4.61.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.61.0.tgz", + "integrity": "sha512-Bp3JpGP00Vu3f238ivRrjf7z3xSzVPXqCmaJYA9t2c+c8vKYvOzmXF7LkkeUalTEGd6cZcSWe+PFIP3Vy48fRg==", "cpu": [ "arm64" ], @@ -974,9 +990,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.45.1.tgz", - "integrity": "sha512-FSncqHvqTm3lC6Y13xncsdOYfxGSLnP+73k815EfNmpewPs+EyM49haPS105Rh4aF5mJKywk9X0ogzLXZzN9lA==", + "version": "4.61.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.61.0.tgz", + "integrity": "sha512-zaYIpr670mUmmZ1tVzUFplbQbG7h3Gugx3L5FoqhsC2m/YnLlR1a7zVLmXNPy+iY1tFPEbNG+HHBXZGyId0G5w==", "cpu": [ "arm64" ], @@ -988,9 +1004,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.45.1.tgz", - "integrity": "sha512-2/vVn/husP5XI7Fsf/RlhDaQJ7x9zjvC81anIVbr4b/f0xtSmXQTFcGIQ/B1cXIYM6h2nAhJkdMHTnD7OtQ9Og==", + "version": "4.61.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.61.0.tgz", + "integrity": "sha512-+P49fvkv2dSoeevUW+lgZ/I2JHSsJCK1Lyjj7Cu6E4UHG4tS9XIefzIjo5qhgELjAclnen1rLzK2PMKJdo+Dyg==", "cpu": [ "x64" ], @@ -1002,9 +1018,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.45.1.tgz", - "integrity": "sha512-4g1kaDxQItZsrkVTdYQ0bxu4ZIQ32cotoQbmsAnW1jAE4XCMbcBPDirX5fyUzdhVCKgPcrwWuucI8yrVRBw2+g==", + "version": "4.61.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.61.0.tgz", + "integrity": "sha512-l3FAAOyKJXH2ea6KNFN+MMgC/rnE94YGLXs2ehYqDcCoHt1DpvgWX75BhUJxN38XojP7Ul+4H8PRn7EdyqSDrw==", "cpu": [ "arm64" ], @@ -1016,9 +1032,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.45.1.tgz", - "integrity": "sha512-L/6JsfiL74i3uK1Ti2ZFSNsp5NMiM4/kbbGEcOCps99aZx3g8SJMO1/9Y0n/qKlWZfn6sScf98lEOUe2mBvW9A==", + "version": "4.61.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.61.0.tgz", + "integrity": "sha512-VokPN3TSctKj65cyCNPaUh4vMFA8awxOot/0sp+4J7ZlNRKQEhXhawqPwajoi8H5ZFt61i0ugZJuTKXBjGJ17Q==", "cpu": [ "x64" ], @@ -1030,9 +1046,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.45.1.tgz", - "integrity": "sha512-RkdOTu2jK7brlu+ZwjMIZfdV2sSYHK2qR08FUWcIoqJC2eywHbXr0L8T/pONFwkGukQqERDheaGTeedG+rra6Q==", + "version": "4.61.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.61.0.tgz", + "integrity": "sha512-DxH0P3wxm+Yzs/p3zrk9dw1rURu8p0Nv5+MRK/L7OtnLNg5rLZraSBFZ8iUXOd9f2BlhJyEpIZUH/emjq4UJ4g==", "cpu": [ "arm" ], @@ -1044,9 +1060,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.45.1.tgz", - "integrity": "sha512-3kJ8pgfBt6CIIr1o+HQA7OZ9mp/zDk3ctekGl9qn/pRBgrRgfwiffaUmqioUGN9hv0OHv2gxmvdKOkARCtRb8Q==", + "version": "4.61.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.61.0.tgz", + "integrity": "sha512-T6ZvMNe84kAz6TBWHC7hGAoEtzP1LWYw/AqayGWEF6uISt3Abk/st06LqRD9THd7Xz3NxzurUpzAuEAUbZf+nw==", "cpu": [ "arm" ], @@ -1058,9 +1074,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.45.1.tgz", - "integrity": "sha512-k3dOKCfIVixWjG7OXTCOmDfJj3vbdhN0QYEqB+OuGArOChek22hn7Uy5A/gTDNAcCy5v2YcXRJ/Qcnm4/ma1xw==", + "version": "4.61.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.61.0.tgz", + "integrity": "sha512-q/4hzvQkDs8b4jIBab1pnLiiM0ayTZsN2amBFPDzuyZxjEd4wDwx0UJFYM3cOZzSf5Kw8fnWSprJzIBMkcR44Q==", "cpu": [ "arm64" ], @@ -1072,9 +1088,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.45.1.tgz", - "integrity": "sha512-PmI1vxQetnM58ZmDFl9/Uk2lpBBby6B6rF4muJc65uZbxCs0EA7hhKCk2PKlmZKuyVSHAyIw3+/SiuMLxKxWog==", + "version": "4.61.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.61.0.tgz", + "integrity": "sha512-vvYWX3akdEAY6km+9wAqFDnk6pQsbJKVnj7xawcvs/+fdlYBGp+U+Qq/lLfpIxYIZvZLHMAKD9HLdacSx/r3dw==", "cpu": [ "arm64" ], @@ -1085,10 +1101,10 @@ "linux" ] }, - "node_modules/@rollup/rollup-linux-loongarch64-gnu": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.45.1.tgz", - "integrity": "sha512-9UmI0VzGmNJ28ibHW2GpE2nF0PBQqsyiS4kcJ5vK+wuwGnV5RlqdczVocDSUfGX/Na7/XINRVoUgJyFIgipoRg==", + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.61.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.61.0.tgz", + "integrity": "sha512-DePa5cqOxDP/Zp0VOXpeWaGew5iIv5DXp9NYbzkX5PFQyWVX9184WCTh3hvr/7lhXo8ZVlbFLkz8+o/q1dU6gA==", "cpu": [ "loong64" ], @@ -1099,10 +1115,38 @@ "linux" ] }, - "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.45.1.tgz", - "integrity": "sha512-7nR2KY8oEOUTD3pBAxIBBbZr0U7U+R9HDTPNy+5nVVHDXI4ikYniH1oxQz9VoB5PbBU1CZuDGHkLJkd3zLMWsg==", + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.61.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.61.0.tgz", + "integrity": "sha512-LV8aWMB8UChglMCEzs7RkN0GsH29RJaLLqwm9fCIjlqwxQTiWAqNcc7wjBkH31hV0PU/yVxGYvrYsgfea2qw6g==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.61.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.61.0.tgz", + "integrity": "sha512-QoNSnwQtaeNu5grdBbsL0tt1uyl5EnS8DA8Mr3nluMXbhdQNyhN+G4tBax7VCdxLKj8YJ0/4OO9Ho84jMnJtKA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.61.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.61.0.tgz", + "integrity": "sha512-/zZp5MKapIIApE8trN8qLGNSiRN9TUoaUZ1cmVu4XnVdd5LQLOXTtyi+vtfUbNnT3iyjzpPqYeKXmvJ+gJGYWw==", "cpu": [ "ppc64" ], @@ -1114,9 +1158,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.45.1.tgz", - "integrity": "sha512-nlcl3jgUultKROfZijKjRQLUu9Ma0PeNv/VFHkZiKbXTBQXhpytS8CIj5/NfBeECZtY2FJQubm6ltIxm/ftxpw==", + "version": "4.61.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.61.0.tgz", + "integrity": "sha512-RbrzcD3aJ1k3UbtMRRBNwojdVVyXjuVAFTfn/xPa6EEl6GE9Sm/akPgFTb9aAC9pMKGJ6CtWxaGrqWcabH+ySg==", "cpu": [ "riscv64" ], @@ -1128,9 +1172,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.45.1.tgz", - "integrity": "sha512-HJV65KLS51rW0VY6rvZkiieiBnurSzpzore1bMKAhunQiECPuxsROvyeaot/tcK3A3aGnI+qTHqisrpSgQrpgA==", + "version": "4.61.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.61.0.tgz", + "integrity": "sha512-ZF+onDsBso8PJf1XaG9lB+O9RnBpKGnY6OrzC4CSHrtC1jb6jWLTKK4bRqdoCXHd22gyr2hiYmEAm8Wns/BOCw==", "cpu": [ "riscv64" ], @@ -1142,9 +1186,9 @@ ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.45.1.tgz", - "integrity": "sha512-NITBOCv3Qqc6hhwFt7jLV78VEO/il4YcBzoMGGNxznLgRQf43VQDae0aAzKiBeEPIxnDrACiMgbqjuihx08OOw==", + "version": "4.61.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.61.0.tgz", + "integrity": "sha512-Atk0aSIk5Zx2Wuh9dgRQgLP0Koc8hOeYpbWryMXyk8G8/HmPkwPPkMqIIDhrXHHYqfUzSJA/I7IWSBv8xSmRBA==", "cpu": [ "s390x" ], @@ -1156,9 +1200,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.45.1.tgz", - "integrity": "sha512-+E/lYl6qu1zqgPEnTrs4WysQtvc/Sh4fC2nByfFExqgYrqkKWp1tWIbe+ELhixnenSpBbLXNi6vbEEJ8M7fiHw==", + "version": "4.61.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.61.0.tgz", + "integrity": "sha512-0uMOcf3eZ5K+K4cYHkdxShFMPlPXCOdfDFEFn9dNYAEEd2cVvmOfH7zFgRVoDgmtQ1m9k5q7qfrHzyMAubKYUA==", "cpu": [ "x64" ], @@ -1170,9 +1214,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.45.1.tgz", - "integrity": "sha512-a6WIAp89p3kpNoYStITT9RbTbTnqarU7D8N8F2CV+4Cl9fwCOZraLVuVFvlpsW0SbIiYtEnhCZBPLoNdRkjQFw==", + "version": "4.61.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.61.0.tgz", + "integrity": "sha512-mvFtE4A/t/7hRJ7X8Ozmu8FsIkAUat2nzl12pgU337BRmq87AQUJztwHz2Zv5/tjo9/C95E66CK03SI/ToEDJw==", "cpu": [ "x64" ], @@ -1183,10 +1227,38 @@ "linux" ] }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.61.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.61.0.tgz", + "integrity": "sha512-z9b9+aTxvt8n2rNltMPvyaUfB8NJ+CVyOrGK/MdIKHx7B+lXmZpm/XbRsU7Rpf3fRqJ2uS6mBJiJveCtq8LHDg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.61.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.61.0.tgz", + "integrity": "sha512-jXaXFqKMehsOc+g8R6oo33RRC6w07G9jDBxAE5eAKX7mOcCbZloYIPNhfG9Wl+P9O9IWHFO4OJgPi1Ml2qkt7w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.45.1.tgz", - "integrity": "sha512-T5Bi/NS3fQiJeYdGvRpTAP5P02kqSOpqiopwhj0uaXB6nzs5JVi2XMJb18JUSKhCOX8+UE1UKQufyD6Or48dJg==", + "version": "4.61.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.61.0.tgz", + "integrity": "sha512-OXNWVFocS2IA4+QplhTZZ2a+8hPZR7T8KuozsNmJKK8y7cp83StHvGksfHzPG3wczWTczyWHVQuqeiTUbjiyBg==", "cpu": [ "arm64" ], @@ -1198,9 +1270,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.45.1.tgz", - "integrity": "sha512-lxV2Pako3ujjuUe9jiU3/s7KSrDfH6IgTSQOnDWr9aJ92YsFd7EurmClK0ly/t8dzMkDtd04g60WX6yl0sGfdw==", + "version": "4.61.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.61.0.tgz", + "integrity": "sha512-AlAbNtBO637LxSldqV43z0FfXoGfl2TW1DgAg/bs7aQswFbDewz2SJm3BUhiGfbOVtW571xbc9p+REdxhyN/Eg==", "cpu": [ "ia32" ], @@ -1211,10 +1283,24 @@ "win32" ] }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.61.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.61.0.tgz", + "integrity": "sha512-QRSrQXyJ1M4tjNXdR0/G/IgV6lzfQQJYBjlWIEYkY2Xs86DRl/iEpQ4blMDjJxSl7n19eDKKXMg0AmuBVYy8pQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.45.1.tgz", - "integrity": "sha512-M/fKi4sasCdM8i0aWJjCSFm2qEnYRR8AMLG2kxp6wD13+tMGA4Z1tVAuHkNRjud5SW2EM3naLuK35w9twvf6aA==", + "version": "4.61.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.61.0.tgz", + "integrity": "sha512-tkuFxhvKO/HlGd0VsINF6vHSYH8AF8W0TcNxKDK6JZmrehngFj78pToc8iemtnvwilDjs2G/qSzYFhe9U8q+fw==", "cpu": [ "x64" ], @@ -1455,6 +1541,66 @@ "node": ">=14.0.0" } }, + "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/core": { + "version": "1.7.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.1.0", + "tslib": "^2.4.0" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/runtime": { + "version": "1.7.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/wasi-threads": { + "version": "1.1.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@napi-rs/wasm-runtime": { + "version": "1.1.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "^1.7.1", + "@emnapi/runtime": "^1.7.1", + "@tybys/wasm-util": "^0.10.1" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@tybys/wasm-util": { + "version": "0.10.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/tslib": { + "version": "2.8.1", + "dev": true, + "inBundle": true, + "license": "0BSD", + "optional": true + }, "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { "version": "4.1.18", "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.1.18.tgz", @@ -1505,9 +1651,9 @@ } }, "node_modules/@tootallnate/once": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", - "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.1.tgz", + "integrity": "sha512-HqmEUIGRJ5fSXchkVgR5F7qn48bDBzv0kWj/Kfu5e6uci4UlEeng4331LnBkWffb++Ei3FOVLxo8JJWMFBDMeQ==", "dev": true, "license": "MIT", "engines": { @@ -1540,18 +1686,12 @@ "license": "MIT" }, "node_modules/@types/estree": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", - "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", + "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", "devOptional": true, "license": "MIT" }, - "node_modules/@types/geojson": { - "version": "7946.0.16", - "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.16.tgz", - "integrity": "sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==", - "license": "MIT" - }, "node_modules/@types/google.maps": { "version": "3.58.1", "resolved": "https://registry.npmjs.org/@types/google.maps/-/google.maps-3.58.1.tgz", @@ -1592,15 +1732,6 @@ "integrity": "sha512-+N+2H39P8X+Hy1I5mC6awlTX54k3FhiUmvt7HWzGJZvF+syUAAxP/stwppS8JE84YHqFgRMv6fCy31202CMFxQ==", "license": "MIT" }, - "node_modules/@types/supercluster": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/@types/supercluster/-/supercluster-7.1.3.tgz", - "integrity": "sha512-Z0pOY34GDFl3Q6hUFYf3HkTwKEE02e7QgtJppBt+beEAxnyOpJua+voGFvxINBHa06GwLFFym7gRPY2SiKIfIA==", - "license": "MIT", - "dependencies": { - "@types/geojson": "*" - } - }, "node_modules/@vitejs/plugin-vue": { "version": "5.2.4", "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-5.2.4.tgz", @@ -1616,29 +1747,29 @@ } }, "node_modules/@vitest/coverage-v8": { - "version": "4.0.18", - "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-4.0.18.tgz", - "integrity": "sha512-7i+N2i0+ME+2JFZhfuz7Tg/FqKtilHjGyGvoHYQ6iLV0zahbsJ9sljC9OcFcPDbhYKCet+sG8SsVqlyGvPflZg==", + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-4.1.8.tgz", + "integrity": "sha512-lt3kovsyHwYe00wq4D1ti0Z974fWj4NLp6siqiyEufUpyFwK9Yhi7rBhac9JL5aA0zoMrJqc4vYPZRUnI7l7nw==", "dev": true, "license": "MIT", "dependencies": { "@bcoe/v8-coverage": "^1.0.2", - "@vitest/utils": "4.0.18", - "ast-v8-to-istanbul": "^0.3.10", + "@vitest/utils": "4.1.8", + "ast-v8-to-istanbul": "^1.0.0", "istanbul-lib-coverage": "^3.2.2", "istanbul-lib-report": "^3.0.1", "istanbul-reports": "^3.2.0", - "magicast": "^0.5.1", + "magicast": "^0.5.2", "obug": "^2.1.1", - "std-env": "^3.10.0", - "tinyrainbow": "^3.0.3" + "std-env": "^4.0.0-rc.1", + "tinyrainbow": "^3.1.0" }, "funding": { "url": "https://opencollective.com/vitest" }, "peerDependencies": { - "@vitest/browser": "4.0.18", - "vitest": "4.0.18" + "@vitest/browser": "4.1.8", + "vitest": "4.1.8" }, "peerDependenciesMeta": { "@vitest/browser": { @@ -1647,31 +1778,31 @@ } }, "node_modules/@vitest/expect": { - "version": "4.0.18", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.0.18.tgz", - "integrity": "sha512-8sCWUyckXXYvx4opfzVY03EOiYVxyNrHS5QxX3DAIi5dpJAAkyJezHCP77VMX4HKA2LDT/Jpfo8i2r5BE3GnQQ==", + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.8.tgz", + "integrity": "sha512-h3nDO677RDLEGlBxyQ5CW8RlMThSKSRLUePLOx09gNIWRL40edgA1GCZSZgf1W55MFAG6/Sw14KeaAnqv0NKdQ==", "dev": true, "license": "MIT", "dependencies": { - "@standard-schema/spec": "^1.0.0", + "@standard-schema/spec": "^1.1.0", "@types/chai": "^5.2.2", - "@vitest/spy": "4.0.18", - "@vitest/utils": "4.0.18", - "chai": "^6.2.1", - "tinyrainbow": "^3.0.3" + "@vitest/spy": "4.1.8", + "@vitest/utils": "4.1.8", + "chai": "^6.2.2", + "tinyrainbow": "^3.1.0" }, "funding": { "url": "https://opencollective.com/vitest" } }, "node_modules/@vitest/mocker": { - "version": "4.0.18", - "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.0.18.tgz", - "integrity": "sha512-HhVd0MDnzzsgevnOWCBj5Otnzobjy5wLBe4EdeeFGv8luMsGcYqDuFRMcttKWZA5vVO8RFjexVovXvAM4JoJDQ==", + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.8.tgz", + "integrity": "sha512-LEiN/xe4OSIbKe9HQIp5OC24agGD9J5CnmMgsLohVVoOPWL9a2sBoR6VBx43jQZb7Kr1l4RCuyCJzcAa0+dojw==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/spy": "4.0.18", + "@vitest/spy": "4.1.8", "estree-walker": "^3.0.3", "magic-string": "^0.30.21" }, @@ -1680,7 +1811,7 @@ }, "peerDependencies": { "msw": "^2.4.9", - "vite": "^6.0.0 || ^7.0.0-0" + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" }, "peerDependenciesMeta": { "msw": { @@ -1702,26 +1833,26 @@ } }, "node_modules/@vitest/pretty-format": { - "version": "4.0.18", - "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.0.18.tgz", - "integrity": "sha512-P24GK3GulZWC5tz87ux0m8OADrQIUVDPIjjj65vBXYG17ZeU3qD7r+MNZ1RNv4l8CGU2vtTRqixrOi9fYk/yKw==", + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.8.tgz", + "integrity": "sha512-9GasEBxpZ1VYIpqHf/0+YGg121uSNwCKOJqIrTwWP/TB7DmFCiaBpNl3aPZzoLWfWkuqhbH8vJIVobZkvdo2cA==", "dev": true, "license": "MIT", "dependencies": { - "tinyrainbow": "^3.0.3" + "tinyrainbow": "^3.1.0" }, "funding": { "url": "https://opencollective.com/vitest" } }, "node_modules/@vitest/runner": { - "version": "4.0.18", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.0.18.tgz", - "integrity": "sha512-rpk9y12PGa22Jg6g5M3UVVnTS7+zycIGk9ZNGN+m6tZHKQb7jrP7/77WfZy13Y/EUDd52NDsLRQhYKtv7XfPQw==", + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.8.tgz", + "integrity": "sha512-EmVxeBAfMJvycdjd6Hm+RbFBbA9fKvo0Kx37hNpBYoYeavH3RNsBXWDooR1mgD52dCrxIIuP7UotpfiwOikvcg==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/utils": "4.0.18", + "@vitest/utils": "4.1.8", "pathe": "^2.0.3" }, "funding": { @@ -1729,13 +1860,14 @@ } }, "node_modules/@vitest/snapshot": { - "version": "4.0.18", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.0.18.tgz", - "integrity": "sha512-PCiV0rcl7jKQjbgYqjtakly6T1uwv/5BQ9SwBLekVg/EaYeQFPiXcgrC2Y7vDMA8dM1SUEAEV82kgSQIlXNMvA==", + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.8.tgz", + "integrity": "sha512-acfZboRmAIf05DEKcBQy33VXojFJjtUdLyo7oOmV9kebb2xdU01UknNiPuPZoJZQyO7DF0gZdTGTpeAzET9QPQ==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "4.0.18", + "@vitest/pretty-format": "4.1.8", + "@vitest/utils": "4.1.8", "magic-string": "^0.30.21", "pathe": "^2.0.3" }, @@ -1744,9 +1876,9 @@ } }, "node_modules/@vitest/spy": { - "version": "4.0.18", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.0.18.tgz", - "integrity": "sha512-cbQt3PTSD7P2OARdVW3qWER5EGq7PHlvE+QfzSC0lbwO+xnt7+XH06ZzFjFRgzUX//JmpxrCu92VdwvEPlWSNw==", + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.8.tgz", + "integrity": "sha512-6EevtBp6OZOPF7bmz36HrGMeP3txgVSrgebWxHOafDXGkhIzfXK14f8KF6MuFfgXXUeHxmpD3BQxkV00/3s5mA==", "dev": true, "license": "MIT", "funding": { @@ -1754,14 +1886,15 @@ } }, "node_modules/@vitest/utils": { - "version": "4.0.18", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.0.18.tgz", - "integrity": "sha512-msMRKLMVLWygpK3u2Hybgi4MNjcYJvwTb0Ru09+fOyCXIgT5raYP041DRRdiJiI3k/2U6SEbAETB3YtBrUkCFA==", + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.8.tgz", + "integrity": "sha512-uOJamYALNhfJ6iolExyQM40yIQwDqYnkKtQ5VCiSe17E33H0aQ/u+1GlRuz4LZBk6Mm3sg90G9hEbmEt37C1Zg==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "4.0.18", - "tinyrainbow": "^3.0.3" + "@vitest/pretty-format": "4.1.8", + "convert-source-map": "^2.0.0", + "tinyrainbow": "^3.1.0" }, "funding": { "url": "https://opencollective.com/vitest" @@ -1913,9 +2046,9 @@ } }, "node_modules/@vue/language-core/node_modules/picomatch": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", - "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", "dev": true, "license": "MIT", "engines": { @@ -2038,9 +2171,9 @@ } }, "node_modules/ast-v8-to-istanbul": { - "version": "0.3.11", - "resolved": "https://registry.npmjs.org/ast-v8-to-istanbul/-/ast-v8-to-istanbul-0.3.11.tgz", - "integrity": "sha512-Qya9fkoofMjCBNVdWINMjB5KZvkYfaO9/anwkWnjxibpWUxo5iHl2sOdP7/uAqaRuUYuoo8rDwnbaaKVFxoUvw==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/ast-v8-to-istanbul/-/ast-v8-to-istanbul-1.0.3.tgz", + "integrity": "sha512-jCMQ6ZylLPudp0CDfBmQBZUsrh1/8psbmu9ibeVWKuHWD0YrH9YABwlKu5kVEFoT0GCQQW9Z/SxfuEbbkGQCRg==", "dev": true, "license": "MIT", "dependencies": { @@ -2225,6 +2358,13 @@ "node": ">= 0.8" } }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, "node_modules/core-util-is": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", @@ -2237,15 +2377,6 @@ "integrity": "sha512-qZk66RlmQm7fQjMYWku1AyjlKPogjPEorAZJG88owPExoPV8EsyCcuFLvO2afTXHEhi9liVOoyd+5A6ZS5QwaA==", "license": "MIT" }, - "node_modules/cross-fetch": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-4.1.0.tgz", - "integrity": "sha512-uKm5PU+MHTootlWEY+mZ4vvXoCn4fLQxT9dSc1sXVMSFkINTJVN8cAQROpwcKm8bJ/c7rgZVIBWzH5T78sNZZw==", - "license": "MIT", - "dependencies": { - "node-fetch": "^2.7.0" - } - }, "node_modules/cssstyle": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-3.0.0.tgz", @@ -2431,9 +2562,9 @@ } }, "node_modules/es-module-lexer": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz", - "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.1.0.tgz", + "integrity": "sha512-n27zTYMjYu1aj4MjCWzSP7G9r75utsaoc8m61weK+W8JMBGGQybd43GstCXZ3WNmSFtGT9wi59qQTW6mhTR5LQ==", "dev": true, "license": "MIT" }, @@ -2534,15 +2665,6 @@ "node": ">=12.0.0" } }, - "node_modules/fast-equals": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/fast-equals/-/fast-equals-5.2.2.tgz", - "integrity": "sha512-V7/RktU11J3I36Nwq2JnZEM7tNm17eBJz+u25qdxBZeCKiX6BkVSZQjwWIr+IobgnZy+ag73tTZgZi7tr0LrBw==", - "license": "MIT", - "engines": { - "node": ">=6.0.0" - } - }, "node_modules/fill-range": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", @@ -2818,9 +2940,9 @@ "license": "MIT" }, "node_modules/immutable": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/immutable/-/immutable-5.1.3.tgz", - "integrity": "sha512-+chQdDfvscSF1SJqv2gn4SRO2ZyS3xL3r7IW/wWEEzrzLisnOlKiQu5ytC/BVNcS15C39WT2Hg/bjKjDMcu+zg==", + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-5.1.6.tgz", + "integrity": "sha512-q1swsS8K7L8usSHuOqF2TAoCCkonYz0SG38wLAggaa4Wml70zixIvt2ql4coQ2C2B3hTjltJry4r6bULwgAXLQ==", "dev": true, "license": "MIT" }, @@ -2990,12 +3112,6 @@ "setimmediate": "^1.0.5" } }, - "node_modules/kdbush": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/kdbush/-/kdbush-4.0.2.tgz", - "integrity": "sha512-WbCVYJ27Sz8zi9Q7Q0xHC+05iwkm3Znipc2XTlrnJbsHMYktW4hPhXUE8Ys1engBrvffoSCqbil1JQAa7clRpA==", - "license": "ISC" - }, "node_modules/lerc": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/lerc/-/lerc-3.0.0.tgz", @@ -3283,9 +3399,9 @@ } }, "node_modules/lodash-es": { - "version": "4.17.23", - "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.23.tgz", - "integrity": "sha512-kVI48u3PZr38HdYz98UmfPnXl2DXrpdctLrFLCd3kOx1xUkOmpFPx7gCWWM5MPkL/fD8zb+Ph0QzjGFs4+hHWg==", + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.18.1.tgz", + "integrity": "sha512-J8xewKD/Gk22OZbhpOVSwcs60zhd95ESDwezOFuA3/099925PdHJ7OFHNTGtajL3AlZkykD32HykiMo+BIBI8A==", "license": "MIT" }, "node_modules/magic-string": { @@ -3394,9 +3510,9 @@ "license": "MIT" }, "node_modules/nanoid": { - "version": "3.3.11", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", - "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "version": "3.3.12", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.12.tgz", + "integrity": "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==", "funding": [ { "type": "github", @@ -3419,48 +3535,6 @@ "license": "MIT", "optional": true }, - "node_modules/node-fetch": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", - "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", - "license": "MIT", - "dependencies": { - "whatwg-url": "^5.0.0" - }, - "engines": { - "node": "4.x || >=6.0.0" - }, - "peerDependencies": { - "encoding": "^0.1.0" - }, - "peerDependenciesMeta": { - "encoding": { - "optional": true - } - } - }, - "node_modules/node-fetch/node_modules/tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", - "license": "MIT" - }, - "node_modules/node-fetch/node_modules/webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", - "license": "BSD-2-Clause" - }, - "node_modules/node-fetch/node_modules/whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", - "license": "MIT", - "dependencies": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" - } - }, "node_modules/node-releases": { "version": "2.0.19", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", @@ -3513,21 +3587,6 @@ "url": "https://opencollective.com/openlayers" } }, - "node_modules/p-limit": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-6.2.0.tgz", - "integrity": "sha512-kuUqqHNUqoIWp/c467RI4X6mmyuojY5jGutNU0wVTmEOOfcuwLqyMVoAi9MKi2Ak+5i9+nhmrK4ufZE8069kHA==", - "license": "MIT", - "dependencies": { - "yocto-queue": "^1.1.1" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/pako": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", @@ -3599,9 +3658,9 @@ "license": "ISC" }, "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", "dev": true, "license": "MIT", "optional": true, @@ -3634,6 +3693,53 @@ } } }, + "node_modules/playwright": { + "version": "1.61.0", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.61.0.tgz", + "integrity": "sha512-Z+7BeeqQPRRzklHsVFP4KTGIyMxKUmfeRA4WisM6G3/XW6nwGeX6fX9qYaDa+CiUqpOkb2f6X3nar05R3kSuJQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "playwright-core": "1.61.0" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "fsevents": "2.3.2" + } + }, + "node_modules/playwright-core": { + "version": "1.61.0", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.61.0.tgz", + "integrity": "sha512-caX7TrY3Ml6egyDX0WUcTHDxodl/b51y5wJOdCEA36QviK/s2g081hvmGs8eaE3DWb6NYZQ6BjO/QkNRPenoPA==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "playwright-core": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/playwright/node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, "node_modules/plotly.js-dist": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/plotly.js-dist/-/plotly.js-dist-3.3.1.tgz", @@ -3641,9 +3747,9 @@ "license": "MIT" }, "node_modules/postcss": { - "version": "8.5.6", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", - "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", + "version": "8.5.15", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.15.tgz", + "integrity": "sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==", "funding": [ { "type": "opencollective", @@ -3660,7 +3766,7 @@ ], "license": "MIT", "dependencies": { - "nanoid": "^3.3.11", + "nanoid": "^3.3.12", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" }, @@ -3698,9 +3804,9 @@ "license": "MIT" }, "node_modules/protocol-buffers-schema": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/protocol-buffers-schema/-/protocol-buffers-schema-3.6.0.tgz", - "integrity": "sha512-TdDRD+/QNdrCGCE7v8340QyuXd4kIWIgapsE2+n/SaGiSSbomYl4TjHlvIoCWRpE7wFt02EpB35VVA2ImcBVqw==", + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/protocol-buffers-schema/-/protocol-buffers-schema-3.6.1.tgz", + "integrity": "sha512-VG2K63Igkiv9p76tk1lilczEK1cT+kCjKtkdhw1dQZV3k3IXJbd3o6Ho8b9zJZaHSnT2hKe4I+ObmX9w6m5SmQ==", "license": "MIT" }, "node_modules/psl": { @@ -3796,13 +3902,6 @@ "dev": true, "license": "MIT" }, - "node_modules/resize-observer-polyfill": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz", - "integrity": "sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==", - "dev": true, - "license": "MIT" - }, "node_modules/resolve-protobuf-schema": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/resolve-protobuf-schema/-/resolve-protobuf-schema-2.1.0.tgz", @@ -3813,13 +3912,13 @@ } }, "node_modules/rollup": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.45.1.tgz", - "integrity": "sha512-4iya7Jb76fVpQyLoiVpzUrsjQ12r3dM7fIVz+4NwoYvZOShknRmiv+iu9CClZml5ZLGb0XMcYLutK6w9tgxHDw==", + "version": "4.61.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.61.0.tgz", + "integrity": "sha512-T9mWdbWfQtp0B5lv/HX+wrhYsmXRlcWnXXmJbXqKJhlRaoS6KMhq0gpyzW4UJfclcxrEdLnTgjT2NjruLONu0g==", "devOptional": true, "license": "MIT", "dependencies": { - "@types/estree": "1.0.8" + "@types/estree": "1.0.9" }, "bin": { "rollup": "dist/bin/rollup" @@ -3829,26 +3928,31 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.45.1", - "@rollup/rollup-android-arm64": "4.45.1", - "@rollup/rollup-darwin-arm64": "4.45.1", - "@rollup/rollup-darwin-x64": "4.45.1", - "@rollup/rollup-freebsd-arm64": "4.45.1", - "@rollup/rollup-freebsd-x64": "4.45.1", - "@rollup/rollup-linux-arm-gnueabihf": "4.45.1", - "@rollup/rollup-linux-arm-musleabihf": "4.45.1", - "@rollup/rollup-linux-arm64-gnu": "4.45.1", - "@rollup/rollup-linux-arm64-musl": "4.45.1", - "@rollup/rollup-linux-loongarch64-gnu": "4.45.1", - "@rollup/rollup-linux-powerpc64le-gnu": "4.45.1", - "@rollup/rollup-linux-riscv64-gnu": "4.45.1", - "@rollup/rollup-linux-riscv64-musl": "4.45.1", - "@rollup/rollup-linux-s390x-gnu": "4.45.1", - "@rollup/rollup-linux-x64-gnu": "4.45.1", - "@rollup/rollup-linux-x64-musl": "4.45.1", - "@rollup/rollup-win32-arm64-msvc": "4.45.1", - "@rollup/rollup-win32-ia32-msvc": "4.45.1", - "@rollup/rollup-win32-x64-msvc": "4.45.1", + "@rollup/rollup-android-arm-eabi": "4.61.0", + "@rollup/rollup-android-arm64": "4.61.0", + "@rollup/rollup-darwin-arm64": "4.61.0", + "@rollup/rollup-darwin-x64": "4.61.0", + "@rollup/rollup-freebsd-arm64": "4.61.0", + "@rollup/rollup-freebsd-x64": "4.61.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.61.0", + "@rollup/rollup-linux-arm-musleabihf": "4.61.0", + "@rollup/rollup-linux-arm64-gnu": "4.61.0", + "@rollup/rollup-linux-arm64-musl": "4.61.0", + "@rollup/rollup-linux-loong64-gnu": "4.61.0", + "@rollup/rollup-linux-loong64-musl": "4.61.0", + "@rollup/rollup-linux-ppc64-gnu": "4.61.0", + "@rollup/rollup-linux-ppc64-musl": "4.61.0", + "@rollup/rollup-linux-riscv64-gnu": "4.61.0", + "@rollup/rollup-linux-riscv64-musl": "4.61.0", + "@rollup/rollup-linux-s390x-gnu": "4.61.0", + "@rollup/rollup-linux-x64-gnu": "4.61.0", + "@rollup/rollup-linux-x64-musl": "4.61.0", + "@rollup/rollup-openbsd-x64": "4.61.0", + "@rollup/rollup-openharmony-arm64": "4.61.0", + "@rollup/rollup-win32-arm64-msvc": "4.61.0", + "@rollup/rollup-win32-ia32-msvc": "4.61.0", + "@rollup/rollup-win32-x64-gnu": "4.61.0", + "@rollup/rollup-win32-x64-msvc": "4.61.0", "fsevents": "~2.3.2" } }, @@ -3959,9 +4063,9 @@ "license": "MIT" }, "node_modules/std-env": { - "version": "3.10.0", - "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.10.0.tgz", - "integrity": "sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-4.1.0.tgz", + "integrity": "sha512-Rq7ybcX2RuC55r9oaPVEW7/xu3tj8u4GeBYHBWCychFtzMIr86A7e3PPEBPT37sHStKX3+TiX/Fr/ACmJLVlLQ==", "dev": true, "license": "MIT" }, @@ -3974,15 +4078,6 @@ "safe-buffer": "~5.1.0" } }, - "node_modules/supercluster": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/supercluster/-/supercluster-8.0.1.tgz", - "integrity": "sha512-IiOea5kJ9iqzD2t7QJq/cREyLHTtSmUT6gQsweojg9WH2sYJqZK9SswTu6jrscO6D1G5v5vYZ9ru/eq85lXeZQ==", - "license": "ISC", - "dependencies": { - "kdbush": "^4.0.2" - } - }, "node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -4077,9 +4172,9 @@ } }, "node_modules/tinyglobby/node_modules/picomatch": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", - "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", "devOptional": true, "license": "MIT", "engines": { @@ -4090,9 +4185,9 @@ } }, "node_modules/tinyrainbow": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-3.0.3.tgz", - "integrity": "sha512-PSkbLUoxOFRzJYjjxHJt9xro7D+iilgMX/C9lawzVuYiIdcihh9DXmVibBe8lmcFrRi/VzlPjBxbN7rH24q8/Q==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-3.1.0.tgz", + "integrity": "sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==", "dev": true, "license": "MIT", "engines": { @@ -4239,9 +4334,9 @@ "license": "MIT" }, "node_modules/vite": { - "version": "6.4.1", - "resolved": "https://registry.npmjs.org/vite/-/vite-6.4.1.tgz", - "integrity": "sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g==", + "version": "6.4.3", + "resolved": "https://registry.npmjs.org/vite/-/vite-6.4.3.tgz", + "integrity": "sha512-NTKlcQjlAK7MlQoyb6LgaqHc8sso/pVyUJYWMws3jg21uTJw/LddqIFPcPqP6PzpgbIcZyKI85sFE4HBrQDA8A==", "devOptional": true, "license": "MIT", "dependencies": { @@ -4349,9 +4444,9 @@ } }, "node_modules/vite/node_modules/picomatch": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", - "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", "devOptional": true, "license": "MIT", "engines": { @@ -4362,31 +4457,31 @@ } }, "node_modules/vitest": { - "version": "4.0.18", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.0.18.tgz", - "integrity": "sha512-hOQuK7h0FGKgBAas7v0mSAsnvrIgAvWmRFjmzpJ7SwFHH3g1k2u37JtYwOwmEKhK6ZO3v9ggDBBm0La1LCK4uQ==", + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.8.tgz", + "integrity": "sha512-flY6ScbCIt9HThs+C5HS7jvGOB560DJtk/Z15IQROTA6zEy49Nh8T/dofWTQL+n3vswqn87sbJNiuqw1SDp5Ig==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/expect": "4.0.18", - "@vitest/mocker": "4.0.18", - "@vitest/pretty-format": "4.0.18", - "@vitest/runner": "4.0.18", - "@vitest/snapshot": "4.0.18", - "@vitest/spy": "4.0.18", - "@vitest/utils": "4.0.18", - "es-module-lexer": "^1.7.0", - "expect-type": "^1.2.2", + "@vitest/expect": "4.1.8", + "@vitest/mocker": "4.1.8", + "@vitest/pretty-format": "4.1.8", + "@vitest/runner": "4.1.8", + "@vitest/snapshot": "4.1.8", + "@vitest/spy": "4.1.8", + "@vitest/utils": "4.1.8", + "es-module-lexer": "^2.0.0", + "expect-type": "^1.3.0", "magic-string": "^0.30.21", "obug": "^2.1.1", "pathe": "^2.0.3", "picomatch": "^4.0.3", - "std-env": "^3.10.0", + "std-env": "^4.0.0-rc.1", "tinybench": "^2.9.0", "tinyexec": "^1.0.2", "tinyglobby": "^0.2.15", - "tinyrainbow": "^3.0.3", - "vite": "^6.0.0 || ^7.0.0", + "tinyrainbow": "^3.1.0", + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0", "why-is-node-running": "^2.3.0" }, "bin": { @@ -4402,12 +4497,15 @@ "@edge-runtime/vm": "*", "@opentelemetry/api": "^1.9.0", "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0", - "@vitest/browser-playwright": "4.0.18", - "@vitest/browser-preview": "4.0.18", - "@vitest/browser-webdriverio": "4.0.18", - "@vitest/ui": "4.0.18", + "@vitest/browser-playwright": "4.1.8", + "@vitest/browser-preview": "4.1.8", + "@vitest/browser-webdriverio": "4.1.8", + "@vitest/coverage-istanbul": "4.1.8", + "@vitest/coverage-v8": "4.1.8", + "@vitest/ui": "4.1.8", "happy-dom": "*", - "jsdom": "*" + "jsdom": "*", + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" }, "peerDependenciesMeta": { "@edge-runtime/vm": { @@ -4428,6 +4526,12 @@ "@vitest/browser-webdriverio": { "optional": true }, + "@vitest/coverage-istanbul": { + "optional": true + }, + "@vitest/coverage-v8": { + "optional": true + }, "@vitest/ui": { "optional": true }, @@ -4436,13 +4540,16 @@ }, "jsdom": { "optional": true + }, + "vite": { + "optional": false } } }, "node_modules/vitest/node_modules/picomatch": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", - "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", "dev": true, "license": "MIT", "engines": { @@ -4666,9 +4773,9 @@ } }, "node_modules/ws": { - "version": "8.18.3", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz", - "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==", + "version": "8.21.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.21.0.tgz", + "integrity": "sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g==", "dev": true, "license": "MIT", "engines": { @@ -4710,18 +4817,6 @@ "dev": true, "license": "MIT" }, - "node_modules/yocto-queue": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.2.1.tgz", - "integrity": "sha512-AyeEbWOu/TAXdxlV9wmGcR0+yh2j3vYPGOECcIj2S7MkrLyC7ne+oye2BKTItt0ii2PHk4cDy+95+LshzbXnGg==", - "license": "MIT", - "engines": { - "node": ">=12.20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/zstddec": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/zstddec/-/zstddec-0.1.0.tgz", diff --git a/apps/data-management/package.json b/apps/data-management/package.json index 81b3f6735..6650f124a 100644 --- a/apps/data-management/package.json +++ b/apps/data-management/package.json @@ -1,7 +1,6 @@ { "name": "frontend", "private": true, - "version": "0.1.0", "type": "module", "scripts": { "dev": "vite", @@ -10,14 +9,15 @@ "django-build": "vue-tsc && vite build --mode django", "preview": "vite preview", "test": "vitest", - "coverage": "vitest run --coverage --environment jsdom" + "coverage": "vitest run --coverage --environment jsdom", + "e2e": "playwright test" }, "dependencies": { "@googlemaps/js-api-loader": "^1.16.6", - "@googlemaps/markerclusterer": "^2.5.3", - "@hydroserver/client": "0.5.0", + "@hydroserver/client": "file:../../packages/hydroserver-ts", "@mdi/js": "^7.4.47", "country-list": "^2.3.0", + "jszip": "^3.10.1", "lodash-es": "^4.17.21", "maska": "^3.2.0", "ol": "^10.5.0", @@ -29,6 +29,7 @@ "vuetify": "^3.11.8" }, "devDependencies": { + "@playwright/test": "^1.61.0", "@tailwindcss/vite": "^4.1.18", "@types/country-list": "^2.1.4", "@types/google.maps": "^3.54.10", @@ -43,7 +44,6 @@ "jsdom": "^22.1.0", "postcss": "^8.4.23", "prettier": "^2.8.8", - "resize-observer-polyfill": "^1.5.1", "rxjs": "^7.8.1", "sass": "^1.62.0", "tailwindcss": "^4.1.18", diff --git a/tests/e2e/playwright.config.ts b/apps/data-management/playwright.config.ts similarity index 59% rename from tests/e2e/playwright.config.ts rename to apps/data-management/playwright.config.ts index 0f391f917..5de9acf6e 100644 --- a/tests/e2e/playwright.config.ts +++ b/apps/data-management/playwright.config.ts @@ -1,9 +1,12 @@ -import { defineConfig, devices } from '@playwright/test' import path from 'path' +import { fileURLToPath } from 'url' +import { defineConfig, devices } from '@playwright/test' -const repoRoot = path.resolve(__dirname, '../..') +const here = path.dirname(fileURLToPath(import.meta.url)) +const repoRoot = path.resolve(here, '../..') const apiDir = path.join(repoRoot, 'django') -const appDir = path.join(repoRoot, 'apps/data-management') +const ensureDbScript = path.join(here, 'e2e/scripts/ensure_e2e_database.py') + const e2ePython = process.env.E2E_PYTHON || 'python3.11' const apiHost = process.env.E2E_API_HOST || '127.0.0.1' const apiPort = process.env.E2E_API_PORT || '18000' @@ -11,6 +14,21 @@ const apiBaseUrl = `http://${apiHost}:${apiPort}` const appHost = process.env.E2E_APP_HOST || '127.0.0.1' const appPort = process.env.E2E_APP_PORT || '14173' const appBaseUrl = `http://${appHost}:${appPort}` + +// Local runs use the fast Vite dev server (quiet, no rebuild between runs); CI +// builds and previews the production bundle so the release gate tests the real +// artifact. +const appCommand = process.env.CI + ? `npm run build && npm run preview -- --host ${appHost} --port ${appPort}` + : `npm run dev -- --host ${appHost} --port ${appPort}` + +// Suppress the managed servers' output in the local test console so the +// reporter's pass/fail list stays readable. CI keeps stderr so a failed run +// still surfaces server tracebacks in the workflow logs. +const serverOutput = { + stdout: 'ignore', + stderr: process.env.CI ? 'pipe' : 'ignore', +} as const const e2eDatabaseUrl = process.env.E2E_DATABASE_URL || 'postgresql://hsdbadmin:admin@127.0.0.1:5432/hydroserver_e2e' @@ -18,9 +36,24 @@ const e2eAdminDatabaseUrl = process.env.E2E_ADMIN_DATABASE_URL || 'postgresql://hsdbadmin:admin@127.0.0.1:5432/postgres' +process.env.E2E_API_BASE_URL = process.env.E2E_API_BASE_URL || apiBaseUrl +process.env.E2E_APP_BASE_URL = process.env.E2E_APP_BASE_URL || appBaseUrl + +const reporter = process.env.CI + ? [ + ['list'] as const, + ['github'] as const, + ['html', { open: 'never', outputFolder: 'playwright-report' }] as const, + ['junit', { outputFile: 'test-results/results.xml' }] as const, + ] + : [ + ['list'] as const, + ['html', { open: 'never', outputFolder: 'playwright-report' }] as const, + ] + export default defineConfig({ - testDir: path.join(__dirname, 'specs'), - globalSetup: path.join(__dirname, 'support', 'globalSetup.ts'), + testDir: path.join(here, 'e2e', 'specs'), + globalSetup: path.join(here, 'e2e', 'support', 'globalSetup.ts'), fullyParallel: false, forbidOnly: !!process.env.CI, retries: process.env.CI ? 1 : 0, @@ -29,14 +62,8 @@ export default defineConfig({ timeout: 10 * 1000, }, workers: process.env.CI ? 2 : 1, - reporter: process.env.CI - ? [ - ['list'], - ['github'], - ['html', { open: 'never' }], - ['junit', { outputFile: 'test-results/results.xml' }], - ] - : [['list'], ['html', { open: 'never' }]], + reporter, + outputDir: path.join(here, 'test-results'), use: { baseURL: appBaseUrl, trace: process.env.CI ? 'on-first-retry' : 'retain-on-failure', @@ -53,7 +80,7 @@ export default defineConfig({ ], webServer: [ { - command: `${e2ePython} ../tests/e2e/scripts/ensure_e2e_database.py && ${e2ePython} manage.py setup_e2e_data && ${e2ePython} manage.py runserver ${apiHost}:${apiPort}`, + command: `${e2ePython} ${ensureDbScript} && ${e2ePython} manage.py setup_e2e_data && ${e2ePython} manage.py runserver ${apiHost}:${apiPort}`, cwd: apiDir, env: { ...process.env, @@ -77,13 +104,13 @@ export default defineConfig({ url: `${apiBaseUrl}/api/data/workspaces`, reuseExistingServer: false, timeout: 180 * 1000, + ...serverOutput, }, { - command: `npm run build && npm run preview -- --host ${appHost} --port ${appPort}`, - cwd: appDir, + command: appCommand, + cwd: here, env: { ...process.env, - VITE_APP_VERSION: 'e2e', VITE_APP_GOOGLE_MAPS_API_KEY: '', VITE_APP_GOOGLE_MAPS_MAP_ID: '', VITE_APP_PROXY_BASE_URL: apiBaseUrl, @@ -91,8 +118,9 @@ export default defineConfig({ VITE_HYDROSERVER_CLIENT_PATH: '../../packages/hydroserver-ts/src', }, url: `${appBaseUrl}/browse`, - reuseExistingServer: false, + reuseExistingServer: !process.env.CI, timeout: 300 * 1000, + ...serverOutput, }, ], }) diff --git a/apps/data-management/src/components/Browse/BrowseFilterTool.vue b/apps/data-management/src/components/Browse/BrowseFilterTool.vue index 34c55c076..3cf825966 100644 --- a/apps/data-management/src/components/Browse/BrowseFilterTool.vue +++ b/apps/data-management/src/components/Browse/BrowseFilterTool.vue @@ -1,125 +1,400 @@ + + diff --git a/apps/data-management/src/components/Datastream/DatastreamInformationPanels.vue b/apps/data-management/src/components/Datastream/DatastreamInformationPanels.vue index 79db94f07..83b05c9c4 100644 --- a/apps/data-management/src/components/Datastream/DatastreamInformationPanels.vue +++ b/apps/data-management/src/components/Datastream/DatastreamInformationPanels.vue @@ -141,7 +141,10 @@ onMounted(async () => { if (!Array.isArray(d.tags)) { try { const tagsResponse = await hs.datastreams.getTags(d.id) - datastreamTags = Array.isArray(tagsResponse.data) ? tagsResponse.data : [] + datastreamTags = + tagsResponse.ok && Array.isArray(tagsResponse.data) + ? tagsResponse.data + : [] } catch (error) { console.error('Error fetching datastream tags', error) } diff --git a/apps/data-management/src/components/Datastream/DatastreamPopupPlot.vue b/apps/data-management/src/components/Datastream/DatastreamPopupPlot.vue index 7847cfa49..0bfad609e 100644 --- a/apps/data-management/src/components/Datastream/DatastreamPopupPlot.vue +++ b/apps/data-management/src/components/Datastream/DatastreamPopupPlot.vue @@ -88,8 +88,13 @@ diff --git a/apps/data-management/src/components/Orchestration/connections/extractors/ExtractorForm.vue b/apps/data-management/src/components/Orchestration/connections/extractors/ExtractorForm.vue index 7a0d7f47a..7b71d3296 100644 --- a/apps/data-management/src/components/Orchestration/connections/extractors/ExtractorForm.vue +++ b/apps/data-management/src/components/Orchestration/connections/extractors/ExtractorForm.vue @@ -132,6 +132,14 @@ const runtimeSourceOptions = [ title: 'Job execution time', value: 'run_time', }, + { + title: 'Data ingestion window start', + value: 'window_start', + }, + { + title: 'Data ingestion window end', + value: 'window_end', + }, ] as const async function validate() { diff --git a/apps/data-management/src/components/Orchestration/connections/extractors/HTTPExtractorForm.vue b/apps/data-management/src/components/Orchestration/connections/extractors/HTTPExtractorForm.vue deleted file mode 100644 index 45a928687..000000000 --- a/apps/data-management/src/components/Orchestration/connections/extractors/HTTPExtractorForm.vue +++ /dev/null @@ -1,217 +0,0 @@ - - - diff --git a/apps/data-management/src/components/Orchestration/connections/extractors/LocalFileExtractorForm.vue b/apps/data-management/src/components/Orchestration/connections/extractors/LocalFileExtractorForm.vue deleted file mode 100644 index bb583c8c2..000000000 --- a/apps/data-management/src/components/Orchestration/connections/extractors/LocalFileExtractorForm.vue +++ /dev/null @@ -1,25 +0,0 @@ - - - diff --git a/apps/data-management/src/components/Orchestration/connections/loaders/LoaderForm.vue b/apps/data-management/src/components/Orchestration/connections/loaders/LoaderForm.vue deleted file mode 100644 index 3be5a10ba..000000000 --- a/apps/data-management/src/components/Orchestration/connections/loaders/LoaderForm.vue +++ /dev/null @@ -1,31 +0,0 @@ - - - diff --git a/apps/data-management/src/components/Orchestration/connections/transformers/PayloadWindowForm.vue b/apps/data-management/src/components/Orchestration/connections/transformers/PayloadWindowForm.vue new file mode 100644 index 000000000..258f61c54 --- /dev/null +++ b/apps/data-management/src/components/Orchestration/connections/transformers/PayloadWindowForm.vue @@ -0,0 +1,216 @@ + + + \ No newline at end of file diff --git a/apps/data-management/src/components/Orchestration/connections/transformers/TransformerForm.vue b/apps/data-management/src/components/Orchestration/connections/transformers/TransformerForm.vue index afdcb9d39..e9f8b6673 100644 --- a/apps/data-management/src/components/Orchestration/connections/transformers/TransformerForm.vue +++ b/apps/data-management/src/components/Orchestration/connections/transformers/TransformerForm.vue @@ -19,6 +19,7 @@ + @@ -29,6 +30,7 @@ import { useDataConnectionStore } from '@/store/dataConnection' import { storeToRefs } from 'pinia' import JSONTransformerForm from './JSONTransformerForm.vue' import CSVTransformerForm from './CSVTransformerForm.vue' +import PayloadWindowForm from './PayloadWindowForm.vue' import { VForm } from 'vuetify/lib/components/index.mjs' const localForm = ref() @@ -46,10 +48,19 @@ const { dataConnection } = storeToRefs(useDataConnectionStore()) const payloadType = computed({ get: () => dataConnection.value.payload.type, set: (newType: 'CSV' | 'JSON') => { + const currentWindow = dataConnection.value.payload.dataIngestionWindow if (newType === 'CSV') { - dataConnection.value.payload = { type: 'CSV', timestampKey: '' } + dataConnection.value.payload = { + type: 'CSV', + timestampKey: '', + dataIngestionWindow: currentWindow, + } } else { - dataConnection.value.payload = { type: 'JSON', timestampKey: '' } + dataConnection.value.payload = { + type: 'JSON', + timestampKey: '', + dataIngestionWindow: currentWindow, + } } }, }) diff --git a/apps/data-management/src/components/Orchestration/data-products/ProductTaskSwimlanes.vue b/apps/data-management/src/components/Orchestration/data-products/ProductTaskSwimlanes.vue new file mode 100644 index 000000000..ee191fa13 --- /dev/null +++ b/apps/data-management/src/components/Orchestration/data-products/ProductTaskSwimlanes.vue @@ -0,0 +1,353 @@ + + + + + diff --git a/apps/data-management/src/components/Orchestration/data-products/RatingCurveSwimlanes.vue b/apps/data-management/src/components/Orchestration/data-products/RatingCurveSwimlanes.vue index 8cbafa212..5f561bdec 100644 --- a/apps/data-management/src/components/Orchestration/data-products/RatingCurveSwimlanes.vue +++ b/apps/data-management/src/components/Orchestration/data-products/RatingCurveSwimlanes.vue @@ -6,18 +6,23 @@ Output datastream -
+
-
- {{ t.inputDatastream?.name || '—' }} - - via {{ t.ratingCurve.name }} - - {{ t.inputDatastream?.id || '—' }} +
+
+ {{ + t.inputDatastream?.name || '—' + }} + + via {{ t.ratingCurve.name }} + + {{ inputDatastreamId(t) || '—' }} +
+
@@ -26,12 +31,21 @@
-
- {{ t.outputDatastream?.name || '—' }} - - {{ outputThingName(t) }} - - {{ t.outputDatastream?.id || '—' }} +
+
+ {{ + t.outputDatastream?.name || '—' + }} + + {{ outputThingName(t) }} + + {{ outputDatastreamId(t) || '—' }} +
+
@@ -42,26 +56,54 @@ @@ -138,6 +180,18 @@ function outputThingName(t: RatingCurveTransformation) { text-align: left; overflow: hidden; } +.datastream-display { + flex-direction: row; + align-items: center; + gap: 8px; +} +.datastream-display__content { + min-width: 0; + flex: 1 1 auto; + display: flex; + flex-direction: column; + justify-content: center; +} .target-id { color: rgba(0, 0, 0, 0.55); overflow-wrap: anywhere; diff --git a/apps/data-management/src/components/Orchestration/data-products/RatingCurveTable.vue b/apps/data-management/src/components/Orchestration/data-products/RatingCurveTable.vue index e883c94bd..3a01d022e 100644 --- a/apps/data-management/src/components/Orchestration/data-products/RatingCurveTable.vue +++ b/apps/data-management/src/components/Orchestration/data-products/RatingCurveTable.vue @@ -218,7 +218,7 @@