diff --git a/eslint.config.mjs b/eslint.config.mjs index 9bad6e2d..c7c8ac1e 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -144,11 +144,32 @@ export default [ 'no-console': 'off', 'n/no-process-exit': 'off', 'n/hashbang': 'off', - // `_` / `__` as a deliberate throwaway binding — `catch (_)`, a - // discarded destructuring slot. Narrow on purpose: the pattern matches - // UNDERSCORES ONLY, so a real name that happens to start with `_` is - // still reported. v9 drives plain `.js` through the CORE rule (the - // `@typescript-eslint` swap is per-file-type), so it is set here. + // Tests import devDependencies by definition; this rule is about what + // ships in the published package, which tests/ never does. + 'n/no-unpublished-import': 'off', + }, + }, + + { + // `_` / `__` as a deliberate throwaway binding — `catch (_)`, a discarded + // destructuring slot. Narrow on purpose: the pattern matches UNDERSCORES + // ONLY, so a real name that happens to start with `_` is still reported. + // + // 🔴 `.js` / `.mjs` ONLY, NOT `.ts`. The CORE rule is not TypeScript-aware: + // applied to a `.ts` file it reads the parameter NAMES inside a function + // TYPE as bindings and reports them unused. Measured on humaniq — + // + // t?: (app: string, key: string) => string + // + // produced four `no-unused-vars` errors for `app` and `key`, which are + // documentation, not variables. The same mis-scoping made every unused + // `catch (e)` in a `.ts` spec report TWICE, once per rule. + // + // v9 already turns the core rule off for `.ts` and drives + // `@typescript-eslint/no-unused-vars` instead; naming `.ts` here switched + // it back on. TypeScript files are handled by the block below. + files: ['tests/**/*.js', 'tests/**/*.mjs'], + rules: { 'no-unused-vars': [ 'error', { @@ -165,12 +186,117 @@ export default [ ignoreRestSiblings: true, }, ], - // Tests import devDependencies by definition; this rule is about what - // ships in the published package, which tests/ never does. - 'n/no-unpublished-import': 'off', }, }, + { + // The TypeScript half of the block above. Same intent, same patterns, on + // the rule that actually understands the language: it knows a name inside + // a function type is not a binding, so type annotations stay quiet while a + // genuinely dead local is still reported. + files: ['tests/**/*.ts', 'tests/**/*.tsx'], + rules: { + '@typescript-eslint/no-unused-vars': [ + 'error', + { + varsIgnorePattern: '^_+$', + caughtErrors: 'all', + caughtErrorsIgnorePattern: '^_+$', + argsIgnorePattern: '^_', + ignoreRestSiblings: true, + }, + ], + }, + }, + + { + // 🔴 Node-side CLI tooling under `scripts/`, which is COMMONJS. Flat + // config defaults every `.js` to ESM with browser-ish globals, so without + // this block eslint reports the CommonJS wrapper itself as undefined + // identifiers. Measured on this app: 52 of the 233 errors under + // `tests/` + `scripts/` were `no-undef`, ALL of them in `scripts/`, and + // all five names were the environment rather than a typo — `process` 23, + // `require` 20, `__dirname` 6, `__filename` 2, `module` 1. + // + // This is describing the environment, not relaxing a rule, and it is the + // same argument the test-globals block below makes: declaring them keeps + // `no-undef` able to do its real job, which is catching a genuinely + // misspelled identifier. Suppressing the rule instead would bury that. + // + // `no-console` is off because printing its report is what a CLI checker + // is FOR. + // + // 🔴 NO `n/*` ENTRIES HERE, DELIBERATELY. `eslint-plugin-n` is NOT + // registered for these files under eslint 10 + @nextcloud/eslint-config + // 9, so `'n/no-process-exit': 'off'` would be dead config that reads as + // if it were doing something. Measured both ways on this app: 0 `n/` + // findings with the entries and 0 without. + // + // What DID report was the opposite — four `scripts/*.js` carried + // `/* eslint-disable n/no-process-exit */` and `/* eslint-disable + // n/shebang */` left over from the eslintrc era, and an inline disable + // naming an unregistered plugin is itself an error ("Definition for rule + // 'n/shebang' was not found"). Those 8 comments are removed; do not add + // `n/*` rules back to replace them. + // + // ⚠️ `.js` and `.cjs` ONLY. A `scripts/*.mjs` is genuinely ESM and must + // keep the default `sourceType`, or `import` stops parsing there. + files: ['scripts/**/*.js', 'scripts/**/*.cjs'], + languageOptions: { + sourceType: 'commonjs', + globals: { + require: 'readonly', + module: 'writable', + exports: 'writable', + process: 'readonly', + __dirname: 'readonly', + __filename: 'readonly', + console: 'readonly', + Buffer: 'readonly', + global: 'readonly', + URL: 'readonly', + TextEncoder: 'readonly', + TextDecoder: 'readonly', + }, + }, + rules: { + 'no-console': 'off', + }, + }, + + { + // The ESM half of the block above. A `scripts/*.mjs` is genuinely a module + // and must keep the default `sourceType`, so it gets Node's globals but + // none of the CommonJS wrapper. Measured: `process` reported undefined 2x + // in hermiq's generate-opengemeenten-icons.mjs and 4x in openregister's + // l10n/runtime-check.mjs, which the `.js`/`.cjs` block deliberately does + // not match. + files: ['scripts/**/*.mjs', 'tests/**/*.mjs'], + languageOptions: { + globals: { + process: 'readonly', + console: 'readonly', + Buffer: 'readonly', + global: 'readonly', + URL: 'readonly', + TextEncoder: 'readonly', + TextDecoder: 'readonly', + }, + }, + rules: { + 'no-console': 'off', + }, + }, + + { + // eslint must not try to PARSE a shell script. `tests/e2e/seed.test.sh` + // matches the `**/*.test.*` glob some presets use, and eslint then reads + // it as JavaScript and reports "Parsing error: Unexpected character" — + // a finding about a file it should never have opened. + ignores: ['**/*.sh', '**/*.bash'], + }, + + { // Test globals. Several apps keep their spec files INSIDE `src/`, which the // lint script scans, and neither `@nextcloud/eslint-config` nor the runner diff --git a/package.json b/package.json index 8d700702..dc0a8fcb 100644 --- a/package.json +++ b/package.json @@ -19,7 +19,7 @@ "build": "NODE_ENV=production webpack --config webpack.config.js --progress", "dev": "NODE_ENV=development webpack --config webpack.config.js --progress", "watch": "NODE_ENV=development webpack --config webpack.config.js --progress --watch", - "lint": "eslint src", + "lint": "eslint src tests scripts", "lint-fix": "npm run lint -- --fix && npm run format:fix", "test": "jest --silent", "test-coverage": "jest --silent --coverage", diff --git a/scripts/build-l10n-js.js b/scripts/build-l10n-js.js index 176ef519..335b08d2 100644 --- a/scripts/build-l10n-js.js +++ b/scripts/build-l10n-js.js @@ -101,6 +101,9 @@ function renderJs(id, translations, pluralForm) { ].join('\n') } +/** + * + */ function main() { const check = process.argv.includes('--check') const id = appId() diff --git a/scripts/check-schema-l10n.js b/scripts/check-schema-l10n.js index 8a860b33..3c4b2626 100644 --- a/scripts/check-schema-l10n.js +++ b/scripts/check-schema-l10n.js @@ -113,6 +113,9 @@ function collect(node, where, sink) { for (const value of Object.values(node)) collect(value, where, sink) } +/** + * + */ function main() { const update = process.argv.includes('--update') const list = process.argv.includes('--list') diff --git a/scripts/check-vue-demi.js b/scripts/check-vue-demi.js index 34da9a90..7b5f1842 100644 --- a/scripts/check-vue-demi.js +++ b/scripts/check-vue-demi.js @@ -24,9 +24,9 @@ * then hard-fail. Never silently continue. */ +const { spawnSync } = require('child_process') const fs = require('fs') const path = require('path') -const { spawnSync } = require('child_process') const SHIM = path.resolve( __dirname, diff --git a/src/utils/moderationItem.js b/src/utils/moderationItem.js index f54a260d..07fb0cb8 100644 --- a/src/utils/moderationItem.js +++ b/src/utils/moderationItem.js @@ -13,14 +13,7 @@ * SPDX-License-Identifier: EUPL-1.2 */ -const TITLE_FIELDS = [ - 'name', - 'name', - 'titel', - 'title', - 'organization', - 'organisation', -] +const TITLE_FIELDS = ['name', 'titel', 'title', 'organization', 'organisation'] const SUBTITLE_FIELDS = [ 'email', 'contactEmail', diff --git a/tests/e2e/global-setup.ts b/tests/e2e/global-setup.ts index b6d5f3b2..2a8dc4b7 100644 --- a/tests/e2e/global-setup.ts +++ b/tests/e2e/global-setup.ts @@ -8,10 +8,12 @@ * in the static HTML. We must wait for the input fields to hydrate before filling. */ -import { chromium, type FullConfig } from '@playwright/test' -import * as path from 'path' +import type { FullConfig } from '@playwright/test' + +import { chromium } from '@playwright/test' import * as fs from 'fs' -import { resolveBaseUrl } from './base-url' +import * as path from 'path' +import { resolveBaseUrl } from './base-url.ts' const AUTH_DIR = path.resolve(__dirname, '.auth') const STORAGE_STATE = path.join(AUTH_DIR, 'admin.json') @@ -91,7 +93,7 @@ export default async function globalSetup(config: FullConfig): Promise { await page.evaluate(() => { try { window.localStorage.setItem('cn-walkthrough-seen:stackiq', '999.0.0') - } catch (e) { + } catch { // localStorage unavailable — specs fall back to dismissing by hand. } }) diff --git a/tests/e2e/manifest-pages.spec.ts b/tests/e2e/manifest-pages.spec.ts index 6ea7a166..7526cc33 100644 --- a/tests/e2e/manifest-pages.spec.ts +++ b/tests/e2e/manifest-pages.spec.ts @@ -33,8 +33,10 @@ * and carry standalone `@e2e exclude` directives in their spec blocks. */ -import { test, expect, type Page } from '@playwright/test' -import { APP_PATH } from './base-url' +import type { Page } from '@playwright/test' + +import { expect, test } from '@playwright/test' +import { APP_PATH } from './base-url.ts' // Was the hardcoded pretty path `/apps/stackiq`. See the APP_PATH // docblock in tests/e2e/base-url.ts: without a rewrite rule that path is not a diff --git a/tests/e2e/org-archimate-export.spec.ts b/tests/e2e/org-archimate-export.spec.ts index 13a66029..2b92dede 100644 --- a/tests/e2e/org-archimate-export.spec.ts +++ b/tests/e2e/org-archimate-export.spec.ts @@ -75,13 +75,10 @@ * @e2e org-archimate-export::boolean-parameters-accept-various-truthy-values */ -import { - test, - expect, - request as playwrightRequest, - type Page, -} from '@playwright/test' -import { APP_PATH, resolveBaseUrl } from './base-url' +import type { Page } from '@playwright/test' + +import { expect, request as playwrightRequest, test } from '@playwright/test' +import { APP_PATH, resolveBaseUrl } from './base-url.ts' // --------------------------------------------------------------------------- // Fixture setup diff --git a/tests/e2e/playwright.config.ts b/tests/e2e/playwright.config.ts index cc05c8cb..7d3958a8 100644 --- a/tests/e2e/playwright.config.ts +++ b/tests/e2e/playwright.config.ts @@ -57,8 +57,7 @@ import { defineConfig, devices } from '@playwright/test' import * as path from 'path' - -import { BASE_URL } from './base-url' +import { BASE_URL } from './base-url.ts' const APP_ROOT = path.resolve(__dirname, '..', '..') diff --git a/tests/e2e/sbom-import.spec.ts b/tests/e2e/sbom-import.spec.ts index ebf912d8..66544a80 100644 --- a/tests/e2e/sbom-import.spec.ts +++ b/tests/e2e/sbom-import.spec.ts @@ -42,17 +42,19 @@ * `cyclonedx-1.5-valid.json`) is uploaded through the real file input. */ -import { test, expect, type Page } from '@playwright/test' +import type { Page } from '@playwright/test' + +import { expect, test } from '@playwright/test' import * as path from 'path' +import { APP_PATH } from './base-url.ts' +import { dismissWalkthrough } from './spec-coverage/_helpers.ts' import { + cleanupByToken, + createObject, newApiContext, resolveConfig, - createObject, - cleanupByToken, RUN_ID, -} from './workflows/_fixtures' -import { dismissWalkthrough } from './spec-coverage/_helpers' -import { APP_PATH } from './base-url' +} from './workflows/_fixtures.ts' const FIXTURES_DIR = path.resolve(__dirname, '../fixtures/sbom') const CYCLONEDX_16 = path.join(FIXTURES_DIR, 'cyclonedx-1.6-valid.json') // 3 components @@ -109,7 +111,7 @@ async function openComponentsTab(page: Page): Promise { // tests/e2e/base-url.ts. `domcontentloaded`, not `networkidle`: the SPA keeps // a background poll alive so the network never goes idle; the app-root wait // below is the real readiness signal. - await page.goto(`${APP_PATH}/#/moduleversies/${moduleVersieId}`, { + await page.goto(`${APP_PATH}/moduleversies/${moduleVersieId}`, { waitUntil: 'domcontentloaded', }) await page diff --git a/tests/e2e/smoke/app-mounts.spec.ts b/tests/e2e/smoke/app-mounts.spec.ts index 687e11b0..673bb688 100644 --- a/tests/e2e/smoke/app-mounts.spec.ts +++ b/tests/e2e/smoke/app-mounts.spec.ts @@ -31,7 +31,7 @@ * Run: npx playwright test --project smoke */ -import { test, expect } from '@playwright/test' +import { expect, test } from '@playwright/test' const ROUTES = [ { name: 'app root', path: '/index.php/apps/stackiq/' }, diff --git a/tests/e2e/spec-coverage/_helpers.ts b/tests/e2e/spec-coverage/_helpers.ts index 6487256c..97210bfe 100644 --- a/tests/e2e/spec-coverage/_helpers.ts +++ b/tests/e2e/spec-coverage/_helpers.ts @@ -1,3 +1,5 @@ +import type { Page } from '@playwright/test' + // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2026 Conduction B.V. /** @@ -10,8 +12,8 @@ * buttons, view toggles, empty-state, dashboard widgets, settings sections) — * no Vue `$data`/`__vue__` patching. */ -import { expect, type Page } from '@playwright/test' -import { APP_PATH } from '../base-url' +import { expect } from '@playwright/test' +import { APP_PATH } from '../base-url.ts' // Was the hardcoded pretty path `/apps/stackiq`, which only resolves // behind a rewrite rule. See the APP_PATH docblock in tests/e2e/base-url.ts — diff --git a/tests/e2e/spec-coverage/catalog-ratings.spec.ts b/tests/e2e/spec-coverage/catalog-ratings.spec.ts index 4aca5eb9..13634eac 100644 --- a/tests/e2e/spec-coverage/catalog-ratings.spec.ts +++ b/tests/e2e/spec-coverage/catalog-ratings.spec.ts @@ -1,3 +1,7 @@ +import type { APIRequestContext } from '@playwright/test' +import type { Page } from '@playwright/test' +import type { VoorzieningenConfig } from '../workflows/_fixtures.ts' + // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2026 Conduction B.V. /** @@ -28,13 +32,16 @@ * * @spec openspec/specs/catalog-ratings/spec.md */ +import { expect, request as playwrightRequest, test } from '@playwright/test' import { - test, - expect, - request as playwrightRequest, - type Page, -} from '@playwright/test' -import type { APIRequestContext } from '@playwright/test' + BASE_URL, + createObject, + deleteObject, + findAll, + newApiContext, + resolveConfig, + RUN_ID, +} from '../workflows/_fixtures.ts' import { APP_BASE, APP_MAIN, @@ -43,19 +50,8 @@ import { dismissSupportDialog, dismissWalkthrough, expectNoAppErrors, - gotoAppRoute, navClickTo, -} from './_helpers' -import { - BASE_URL, - RUN_ID, - createObject, - findAll, - newApiContext, - resolveConfig, - deleteObject, - type VoorzieningenConfig, -} from '../workflows/_fixtures' +} from './_helpers.ts' const MODULE_NAME = `Review subject ${RUN_ID}` /** Unique per test so the moderation queue row this test acts on is its own. */ diff --git a/tests/e2e/spec-coverage/compliance-matrix.spec.ts b/tests/e2e/spec-coverage/compliance-matrix.spec.ts index aef90820..720d3450 100644 --- a/tests/e2e/spec-coverage/compliance-matrix.spec.ts +++ b/tests/e2e/spec-coverage/compliance-matrix.spec.ts @@ -14,14 +14,14 @@ * @spec openspec/specs/module-compliance-assessment/spec.md * @spec openspec/specs/bio-compliance-assessment/spec.md */ -import { test, expect } from '@playwright/test' +import { expect, test } from '@playwright/test' import { APP_MAIN, collectAppErrors, expectNoAppErrors, navClickTo, -} from './_helpers' -import { ComplianceMatrixView } from './page-components' +} from './_helpers.ts' +import { ComplianceMatrixView } from './page-components.ts' // @e2e module-compliance-assessment::matrix-renders-the-three-cell-states // @e2e module-compliance-assessment::matrix-selection-is-shareable diff --git a/tests/e2e/spec-coverage/contract-administration.spec.ts b/tests/e2e/spec-coverage/contract-administration.spec.ts index 7ce3afac..63f14359 100644 --- a/tests/e2e/spec-coverage/contract-administration.spec.ts +++ b/tests/e2e/spec-coverage/contract-administration.spec.ts @@ -13,13 +13,13 @@ * * @spec openspec/specs/contract-administration/spec.md */ -import { test, expect } from '@playwright/test' +import { expect, test } from '@playwright/test' import { APP_MAIN, collectAppErrors, expectNoAppErrors, navClickTo, -} from './_helpers' +} from './_helpers.ts' // @e2e contract-administration::index-columns-render-real-data // @e2e contract-administration::expiring-soon-filter-shows-only-contracts-in-the-window diff --git a/tests/e2e/spec-coverage/contract-approval-panel.spec.ts b/tests/e2e/spec-coverage/contract-approval-panel.spec.ts index 92ceedd0..12d54473 100644 --- a/tests/e2e/spec-coverage/contract-approval-panel.spec.ts +++ b/tests/e2e/spec-coverage/contract-approval-panel.spec.ts @@ -14,14 +14,14 @@ * * @spec openspec/changes/stackiq-contracts-to-decidesk/specs/contract-decision-delegation/spec.md */ -import { test, expect } from '@playwright/test' +import { expect, test } from '@playwright/test' import { APP_MAIN, collectAppErrors, expectNoAppErrors, gotoAppRoute, navClickTo, -} from './_helpers' +} from './_helpers.ts' // @e2e contract-decision-delegation::approval-panel-shows-projected-state-and-submit-action // @e2e contract-decision-delegation::approval-action-hidden-when-delegation-is-not-configured diff --git a/tests/e2e/spec-coverage/dashboard.spec.ts b/tests/e2e/spec-coverage/dashboard.spec.ts index a765c0b2..d6418c6d 100644 --- a/tests/e2e/spec-coverage/dashboard.spec.ts +++ b/tests/e2e/spec-coverage/dashboard.spec.ts @@ -10,14 +10,14 @@ * "Ga naar Organisaties" navigation button which routes to the organisaties * index (navigationStore.setSelected('organisaties')). */ -import { test, expect } from '@playwright/test' +import { expect, test } from '@playwright/test' import { - gotoAppRoute, - navClickTo, + APP_MAIN, collectAppErrors, expectNoAppErrors, - APP_MAIN, -} from './_helpers' + gotoAppRoute, + navClickTo, +} from './_helpers.ts' test('dashboard: renders the overview surface (info box, refresh, statistics tables)', async ({ page, diff --git a/tests/e2e/spec-coverage/demo-data-setup-step.spec.ts b/tests/e2e/spec-coverage/demo-data-setup-step.spec.ts index 2459c27b..037c59a6 100644 --- a/tests/e2e/spec-coverage/demo-data-setup-step.spec.ts +++ b/tests/e2e/spec-coverage/demo-data-setup-step.spec.ts @@ -1,3 +1,5 @@ +import type { Page } from '@playwright/test' + /* * SPDX-FileCopyrightText: 2026 Conduction B.V. * SPDX-License-Identifier: EUPL-1.2 @@ -34,7 +36,7 @@ * * @spec exclude ADR-042/ADR-111 setup contract; no per-app behavioural spec. */ -import { test, expect, type Page } from '@playwright/test' +import { expect, test } from '@playwright/test' import * as path from 'path' const STORAGE_STATE = path.resolve(__dirname, '../.auth/admin.json') @@ -53,12 +55,12 @@ async function api( method, headers: { 'Content-Type': 'application/json', - // eslint-disable-next-line no-undef + requesttoken: (window as any).OC?.requestToken || '', 'OCS-APIREQUEST': 'true', }, }) - let json: any = null + let json: any try { json = await res.json() } catch { diff --git a/tests/e2e/spec-coverage/features-roadmap.spec.ts b/tests/e2e/spec-coverage/features-roadmap.spec.ts index a46ddd43..69dc47a2 100644 --- a/tests/e2e/spec-coverage/features-roadmap.spec.ts +++ b/tests/e2e/spec-coverage/features-roadmap.spec.ts @@ -12,14 +12,14 @@ * (src/main.js), the resolved manifest is what the router serves, so the * roadmap page renders its real content. */ -import { test, expect } from '@playwright/test' +import { expect, test } from '@playwright/test' import { - gotoAppRoute, + APP_MAIN, + appNav, collectAppErrors, expectNoAppErrors, - appNav, - APP_MAIN, -} from './_helpers' + gotoAppRoute, +} from './_helpers.ts' test('features-roadmap: deep-link route mounts the roadmap surface', async ({ page, diff --git a/tests/e2e/spec-coverage/gemma-faceted-search.spec.ts b/tests/e2e/spec-coverage/gemma-faceted-search.spec.ts index a5fd1c7f..5991b553 100644 --- a/tests/e2e/spec-coverage/gemma-faceted-search.spec.ts +++ b/tests/e2e/spec-coverage/gemma-faceted-search.spec.ts @@ -1,3 +1,6 @@ +import type { APIRequestContext } from '@playwright/test' +import type { VoorzieningenConfig } from '../workflows/_fixtures.ts' + // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2026 Conduction B.V. /** @@ -30,22 +33,20 @@ * * @spec openspec/specs/gemma-faceted-search/spec.md */ -import { test, expect } from '@playwright/test' -import type { APIRequestContext } from '@playwright/test' -import { - APP_MAIN, - collectAppErrors, - expectNoAppErrors, - navClickTo, -} from './_helpers' +import { expect, test } from '@playwright/test' import { - RUN_ID, createObject, deleteObject, newApiContext, resolveConfig, - type VoorzieningenConfig, -} from '../workflows/_fixtures' + RUN_ID, +} from '../workflows/_fixtures.ts' +import { + APP_MAIN, + collectAppErrors, + expectNoAppErrors, + navClickTo, +} from './_helpers.ts' const FACETS = '/index.php/apps/stackiq/api/facets' /** The four GEMMA dimensions the endpoint must always describe. */ @@ -120,7 +121,7 @@ test('facets: the response carries all four GEMMA dimensions, empty ones as [] n // rather than a weaker version of it. for (const dim of DIMENSIONS) { expect( - Object.prototype.hasOwnProperty.call(body, dim), + Object.hasOwn(body, dim), `dimension "${dim}" is missing from the response`, ).toBe(true) expect( diff --git a/tests/e2e/spec-coverage/index-pages.spec.ts b/tests/e2e/spec-coverage/index-pages.spec.ts index 039f56b2..5a863bde 100644 --- a/tests/e2e/spec-coverage/index-pages.spec.ts +++ b/tests/e2e/spec-coverage/index-pages.spec.ts @@ -27,15 +27,15 @@ * blob. See the block above the standards test for why repointing the page was * the right fix and attaching the schema to the catalog register was not. */ -import { test, expect } from '@playwright/test' +import { expect, test } from '@playwright/test' import { - navClickTo, - gotoAppRoute, + APP_MAIN, collectAppErrors, - expectNoAppErrors, expectIndexSurface, - APP_MAIN, -} from './_helpers' + expectNoAppErrors, + gotoAppRoute, + navClickTo, +} from './_helpers.ts' interface IndexPage { /** Exact app-navigation link label. */ diff --git a/tests/e2e/spec-coverage/license-posture.spec.ts b/tests/e2e/spec-coverage/license-posture.spec.ts index 06398742..4e3d423d 100644 --- a/tests/e2e/spec-coverage/license-posture.spec.ts +++ b/tests/e2e/spec-coverage/license-posture.spec.ts @@ -22,14 +22,14 @@ * * @spec openspec/specs/software-license-posture/spec.md */ -import { test, expect } from '@playwright/test' +import { expect, test } from '@playwright/test' import { APP_MAIN, collectAppErrors, expectNoAppErrors, navClickTo, -} from './_helpers' -import { LicensePostureView } from './page-components' +} from './_helpers.ts' +import { LicensePostureView } from './page-components.ts' // @e2e software-license-posture::open-source-vs-closed-source-share-reflects-deployments-not-catalogue-rows test('license posture: nav reaches the dashboard; portfolio share renders', async ({ diff --git a/tests/e2e/spec-coverage/lifecycle-roadmap.spec.ts b/tests/e2e/spec-coverage/lifecycle-roadmap.spec.ts index 1ae85adb..60585bc7 100644 --- a/tests/e2e/spec-coverage/lifecycle-roadmap.spec.ts +++ b/tests/e2e/spec-coverage/lifecycle-roadmap.spec.ts @@ -18,14 +18,14 @@ * * @spec openspec/specs/application-lifecycle-tracking/spec.md */ -import { test, expect } from '@playwright/test' +import { expect, test } from '@playwright/test' import { APP_MAIN, collectAppErrors, expectNoAppErrors, navClickTo, -} from './_helpers' -import { LifecycleRoadmapView } from './page-components' +} from './_helpers.ts' +import { LifecycleRoadmapView } from './page-components.ts' // @e2e application-lifecycle-tracking::roadmap-groups-and-orders-the-portfolio test('roadmap: nav entry reaches the organisation-first roadmap surface', async ({ diff --git a/tests/e2e/spec-coverage/page-surfaces.spec.ts b/tests/e2e/spec-coverage/page-surfaces.spec.ts index 02a7de91..c1cebaa1 100644 --- a/tests/e2e/spec-coverage/page-surfaces.spec.ts +++ b/tests/e2e/spec-coverage/page-surfaces.spec.ts @@ -1,3 +1,5 @@ +import type { Page } from '@playwright/test' + // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2026 Conduction B.V. /** @@ -34,13 +36,13 @@ * @spec openspec/specs/portfolio-rationalization-time/spec.md * @spec openspec/specs/eol-feed-integration/spec.md */ -import { test, expect, type Page } from '@playwright/test' +import { expect, test } from '@playwright/test' import { APP_MAIN, collectAppErrors, expectNoAppErrors, navClickTo, -} from './_helpers' +} from './_helpers.ts' /** * The four GEMMA dimensions `FacetedCatalogIndexView` declares in diff --git a/tests/e2e/spec-coverage/settings.spec.ts b/tests/e2e/spec-coverage/settings.spec.ts index 8814b99a..fadbf25c 100644 --- a/tests/e2e/spec-coverage/settings.spec.ts +++ b/tests/e2e/spec-coverage/settings.spec.ts @@ -25,9 +25,9 @@ * so opening Settings no longer logs "Failed to load users"; collectAppErrors no * longer filters that message, so this suite asserts it is genuinely absent. */ -import { test, expect } from '@playwright/test' -import { collectAppErrors, expectNoAppErrors } from './_helpers' -import { VersionInformation } from './page-components' +import { expect, test } from '@playwright/test' +import { collectAppErrors, expectNoAppErrors } from './_helpers.ts' +import { VersionInformation } from './page-components.ts' /** * Open the app's Nextcloud admin settings section and return its host element. diff --git a/tests/e2e/spec-coverage/suite-wizard.spec.ts b/tests/e2e/spec-coverage/suite-wizard.spec.ts index 797513cc..56f3c99f 100644 --- a/tests/e2e/spec-coverage/suite-wizard.spec.ts +++ b/tests/e2e/spec-coverage/suite-wizard.spec.ts @@ -1,3 +1,7 @@ +import type { APIRequestContext } from '@playwright/test' +import type { Page } from '@playwright/test' +import type { VoorzieningenConfig } from '../workflows/_fixtures.ts' + // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2026 Conduction B.V. /** @@ -26,22 +30,20 @@ * * @spec openspec/specs/suite-wizard/spec.md */ -import { test, expect, type Page } from '@playwright/test' -import { - APP_MAIN, - collectAppErrors, - expectNoAppErrors, - navClickTo, -} from './_helpers' +import { expect, test } from '@playwright/test' import { - RUN_ID, cleanupByToken, createObject, newApiContext, resolveConfig, - type VoorzieningenConfig, -} from '../workflows/_fixtures' -import type { APIRequestContext } from '@playwright/test' + RUN_ID, +} from '../workflows/_fixtures.ts' +import { + APP_MAIN, + collectAppErrors, + expectNoAppErrors, + navClickTo, +} from './_helpers.ts' const APP_A = `Suite member A ${RUN_ID}` const APP_B = `Suite member B ${RUN_ID}` diff --git a/tests/e2e/spec-coverage/vulnerability-tracking.spec.ts b/tests/e2e/spec-coverage/vulnerability-tracking.spec.ts index c128b764..02c0c09c 100644 --- a/tests/e2e/spec-coverage/vulnerability-tracking.spec.ts +++ b/tests/e2e/spec-coverage/vulnerability-tracking.spec.ts @@ -20,14 +20,14 @@ * * @spec openspec/specs/module-vulnerability-tracking/spec.md */ -import { test, expect } from '@playwright/test' +import { expect, test } from '@playwright/test' import { APP_MAIN, collectAppErrors, expectNoAppErrors, navClickTo, -} from './_helpers' -import { KwetsbaarhedenView } from './page-components' +} from './_helpers.ts' +import { KwetsbaarhedenView } from './page-components.ts' // @e2e module-vulnerability-tracking::report-a-vulnerability-affecting-an-application // @e2e module-vulnerability-tracking::the-capability-makes-the-shipped-notification-reachable diff --git a/tests/e2e/visual/_visual-helpers.ts b/tests/e2e/visual/_visual-helpers.ts index a96bb7b5..0d80da57 100644 --- a/tests/e2e/visual/_visual-helpers.ts +++ b/tests/e2e/visual/_visual-helpers.ts @@ -1,3 +1,5 @@ +import type { Locator, Page } from '@playwright/test' + /* * SPDX-License-Identifier: EUPL-1.2 * @@ -24,7 +26,7 @@ * own baselines on first run, or (b) stay non-gating until baselined in the CI * environment. See tests/e2e/visual/README in-repo wiring notes. */ -import { expect, type Page, type Locator } from '@playwright/test' +import { expect } from '@playwright/test' /** Common screenshot options applied to every visual assertion. */ export const SHOT_OPTIONS = { diff --git a/tests/e2e/visual/stackiq.visual.spec.ts b/tests/e2e/visual/stackiq.visual.spec.ts index 28fdbf56..1b833cba 100644 --- a/tests/e2e/visual/stackiq.visual.spec.ts +++ b/tests/e2e/visual/stackiq.visual.spec.ts @@ -13,16 +13,16 @@ // (the bare /apps/stackiq/ route 404s), so navigation targets the // /index entrypoint. import { test } from '@playwright/test' -import { shootSurface, shootByNav } from './_visual-helpers' +import { shootByNav, shootSurface } from './_visual-helpers.ts' const APP = '/index.php/apps/stackiq/index' test.describe('Stackiq — visual baselines', () => { test('dashboard', async ({ page }) => { - await shootSurface(page, `${APP}#/`, 'dashboard.png') + await shootSurface(page, `${APP}/`, 'dashboard.png') }) test('organisations list', async ({ page }) => { - await shootByNav(page, `${APP}#/`, 'Organisations', 'organisations.png') + await shootByNav(page, `${APP}/`, 'Organisations', 'organisations.png') }) }) diff --git a/tests/e2e/workflows/_fixtures.ts b/tests/e2e/workflows/_fixtures.ts index 8df01dc0..ed688de9 100644 --- a/tests/e2e/workflows/_fixtures.ts +++ b/tests/e2e/workflows/_fixtures.ts @@ -23,11 +23,10 @@ * = POST, deleteObject = DELETE on `/api/objects/{register}/{schema}[/{id}]`). */ -import { - request as playwrightRequest, - type APIRequestContext, -} from '@playwright/test' -import { resolveBaseUrl } from '../base-url' +import type { APIRequestContext } from '@playwright/test' + +import { request as playwrightRequest } from '@playwright/test' +import { resolveBaseUrl } from '../base-url.ts' // Re-exported from the single central resolver (tests/e2e/base-url.ts). These // fixtures CREATE organisations and contracts, so a `localhost:8080` fallback @@ -127,7 +126,7 @@ export async function deleteObject( ) if (!res.ok() && res.status() !== 404) { // Non-fatal during cleanup; log only. - // eslint-disable-next-line no-console + console.warn( `deleteObject(${register}/${schema}/${id}) returned ${res.status()}`, ) diff --git a/tests/e2e/workflows/_ui.ts b/tests/e2e/workflows/_ui.ts index 1d65a1d9..231c857a 100644 --- a/tests/e2e/workflows/_ui.ts +++ b/tests/e2e/workflows/_ui.ts @@ -1,3 +1,5 @@ +import type { Locator, Page } from '@playwright/test' + // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2026 Conduction B.V. /** @@ -10,20 +12,20 @@ * NcActions menu with View/Edit/Copy/Delete, the create/edit form fields and * the delete-confirm dialog). No Vue `$data` / `__vue__` patching. */ -import { expect, type Page, type Locator } from '@playwright/test' +import { expect } from '@playwright/test' // `gotoAppRoute` is re-exported alongside `navClickTo` because not every // manifest page has a navigation entry: `/contactpersonen` is routable but was // deliberately dropped from the menu when contact identity moved to the // Nextcloud addressbook, so for that page the route IS the user's real path. export { - navClickTo, - gotoAppRoute, - dismissSupportDialog, + APP_MAIN, collectAppErrors, + dismissSupportDialog, expectNoAppErrors, - APP_MAIN, -} from '../spec-coverage/_helpers' + gotoAppRoute, + navClickTo, +} from '../spec-coverage/_helpers.ts' /** The CnIndexPage main content region. */ export function indexMain(page: Page): Locator { diff --git a/tests/e2e/workflows/crud-persistence.spec.ts b/tests/e2e/workflows/crud-persistence.spec.ts index 41b745ac..b3cf7aa6 100644 --- a/tests/e2e/workflows/crud-persistence.spec.ts +++ b/tests/e2e/workflows/crud-persistence.spec.ts @@ -1,3 +1,7 @@ +import type { Page } from '@playwright/test' +import type { APIRequestContext } from '@playwright/test' +import type { VoorzieningenConfig } from './_fixtures.ts' + // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2026 Conduction B.V. /** @@ -41,27 +45,21 @@ * exactly this run's rows through the OR deleteObject verb and never touches the * pre-existing demo data. */ -import { test, expect, type APIRequestContext } from '@playwright/test' +import { expect, test } from '@playwright/test' +import { cleanupByToken, newApiContext, resolveConfig, RUN_ID } from './_fixtures.ts' import { - navClickTo, - gotoAppRoute, - dismissSupportDialog, + clickAction, collectAppErrors, + dismissSupportDialog, expectNoAppErrors, + gotoAppRoute, indexMain, - showTable, listTotal, + navClickTo, openCreateDialog, openRowActions, - clickAction, -} from './_ui' -import { - newApiContext, - resolveConfig, - cleanupByToken, - RUN_ID, - type VoorzieningenConfig, -} from './_fixtures' + showTable, +} from './_ui.ts' /** * Wait for the edit dialog after an `Edit` row action, wherever it opens. @@ -79,7 +77,7 @@ import { * @param page The Playwright page. * @return The visible edit dialog locator. */ -async function editDialogAfterEdit(page: import('@playwright/test').Page) { +async function editDialogAfterEdit(page: Page) { const dialog = page.locator('[role="dialog"], .modal-container').first() const direct = await dialog .waitFor({ state: 'visible', timeout: 5000 }) @@ -108,7 +106,7 @@ test.beforeAll(async () => { test.afterAll(async () => { if (apiCtx && cfg) { const removed = await cleanupByToken(apiCtx, cfg, RUN_ID) - // eslint-disable-next-line no-console + console.log( `[crud-persistence] cleaned up ${removed} seeded row(s) for ${RUN_ID}`, ) diff --git a/tests/e2e/workflows/org-export-workflow.spec.ts b/tests/e2e/workflows/org-export-workflow.spec.ts index f8e4d01f..08e793b1 100644 --- a/tests/e2e/workflows/org-export-workflow.spec.ts +++ b/tests/e2e/workflows/org-export-workflow.spec.ts @@ -1,3 +1,6 @@ +import type { APIRequestContext, Page } from '@playwright/test' +import type { VoorzieningenConfig } from './_fixtures.ts' + // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2026 Conduction B.V. /** @@ -32,17 +35,16 @@ * AMEF XML body — it activates automatically once the AMEF register is * configured. */ -import { test, expect, type APIRequestContext, type Page } from '@playwright/test' +import { expect, test } from '@playwright/test' import { - newApiContext, - resolveConfig, createObject, deleteObject, findAll, nameOf, + newApiContext, + resolveConfig, RUN_ID, - type VoorzieningenConfig, -} from './_fixtures' +} from './_fixtures.ts' let apiCtx: APIRequestContext let cfg: VoorzieningenConfig diff --git a/tests/e2e/workflows/organisatie-crud.spec.ts b/tests/e2e/workflows/organisatie-crud.spec.ts index b5c14a02..e3fcbf71 100644 --- a/tests/e2e/workflows/organisatie-crud.spec.ts +++ b/tests/e2e/workflows/organisatie-crud.spec.ts @@ -1,3 +1,6 @@ +import type { APIRequestContext } from '@playwright/test' +import type { VoorzieningenConfig } from './_fixtures.ts' + // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2026 Conduction B.V. /** @@ -25,23 +28,22 @@ * Cleanup: the seeded org carries the RUN_ID token; afterAll deletes it via the * OR deleteObject verb. */ -import { test, expect, type APIRequestContext } from '@playwright/test' +import { expect, test } from '@playwright/test' +import { + cleanupByToken, + createObject, + newApiContext, + resolveConfig, + RUN_ID, +} from './_fixtures.ts' import { - navClickTo, - dismissSupportDialog, collectAppErrors, + dismissSupportDialog, expectNoAppErrors, indexMain, + navClickTo, openCreateDialog, -} from './_ui' -import { - newApiContext, - resolveConfig, - createObject, - cleanupByToken, - RUN_ID, - type VoorzieningenConfig, -} from './_fixtures' +} from './_ui.ts' let apiCtx: APIRequestContext let cfg: VoorzieningenConfig @@ -73,7 +75,7 @@ test.beforeAll(async () => { test.afterAll(async () => { if (apiCtx && cfg) { const removed = await cleanupByToken(apiCtx, cfg, RUN_ID) - // eslint-disable-next-line no-console + console.log( `[organisatie-crud] cleaned up ${removed} seeded row(s) for ${RUN_ID}`, ) diff --git a/tests/l10n/check-l10n-parity.js b/tests/l10n/check-l10n-parity.js index 70e99a9a..460ccf46 100644 --- a/tests/l10n/check-l10n-parity.js +++ b/tests/l10n/check-l10n-parity.js @@ -89,7 +89,7 @@ function loadJsonSet (file) { /** True when a translation value is empty (string) or has an empty plural. */ function isEmpty (v) { - if (v == null) { + if ((v === null || v === undefined)) { return true } if (Array.isArray(v)) { @@ -135,8 +135,8 @@ for (const set of sets) { failures.push({ set: set.kind, loc, kind: 'UNPARSEABLE', detail: e.message }) continue } - const missing = enKeys.filter((k) => !Object.prototype.hasOwnProperty.call(locObj, k)) - const empty = enKeys.filter((k) => Object.prototype.hasOwnProperty.call(locObj, k) && isEmpty(locObj[k])) + const missing = enKeys.filter((k) => !Object.hasOwn(locObj, k)) + const empty = enKeys.filter((k) => Object.hasOwn(locObj, k) && isEmpty(locObj[k])) if (missing.length || empty.length) { failures.push({ set: set.kind, loc, kind: 'INCOMPLETE', missing, empty, total: enKeys.length }) } diff --git a/tests/l10n/check-l10n.js b/tests/l10n/check-l10n.js index ad441dad..4f06ede0 100644 --- a/tests/l10n/check-l10n.js +++ b/tests/l10n/check-l10n.js @@ -128,7 +128,7 @@ function unescape (s) { const used = new Map() function record (key, file, idx, content) { - if (key == null) { + if ((key === null || key === undefined)) { return } const k = unescape(key) @@ -154,7 +154,7 @@ for (const file of files) { const missing = [] for (const [key, locations] of used) { - if (!Object.prototype.hasOwnProperty.call(translations, key)) { + if (!Object.hasOwn(translations, key)) { missing.push({ key, locations: [...locations] }) } } diff --git a/tests/validate-manifest.js b/tests/validate-manifest.js index 5540a6ca..113a6057 100644 --- a/tests/validate-manifest.js +++ b/tests/validate-manifest.js @@ -83,8 +83,8 @@ function loadJson(file) { } function loadAjv() { - let Ajv2020 = null - let addFormats = null + let Ajv2020 + let addFormats try { Ajv2020 = require('ajv/dist/2020').default || require('ajv/dist/2020') } catch (_) { diff --git a/tests/vitest/adminApi.spec.js b/tests/vitest/adminApi.spec.js index 0f86ee69..12378bb0 100644 --- a/tests/vitest/adminApi.spec.js +++ b/tests/vitest/adminApi.spec.js @@ -7,10 +7,10 @@ * @spec openspec/changes/open-data-publishing/specs/open-data-publishing/spec.md */ -import { describe, it, expect, vi } from 'vitest' +import { describe, expect, it, vi } from 'vitest' import { - apiUrl, apiRequest, + apiUrl, normaliseFederationStatus, } from '../../src/utils/adminApi.js' diff --git a/tests/vitest/complianceMatrix.spec.js b/tests/vitest/complianceMatrix.spec.js index a708da30..d41ef626 100644 --- a/tests/vitest/complianceMatrix.spec.js +++ b/tests/vitest/complianceMatrix.spec.js @@ -11,17 +11,17 @@ * @spec openspec/specs/bio-compliance-assessment/spec.md */ -import { describe, it, expect } from 'vitest' +import { describe, expect, it } from 'vitest' import { + buildComplianceMatrix, + buildOrganisationCoverage, CELL, COLUMN_SOURCE, - resolveUuid, + columnLabel, hasEvidence, partitionCompliancy, - buildComplianceMatrix, - buildOrganisationCoverage, + resolveUuid, standardLabel, - columnLabel, } from '../../src/utils/complianceMatrix.js' describe('complianceMatrix.resolveUuid', () => { diff --git a/tests/vitest/contractCost.spec.js b/tests/vitest/contractCost.spec.js index b9f47674..94a0bab2 100644 --- a/tests/vitest/contractCost.spec.js +++ b/tests/vitest/contractCost.spec.js @@ -4,13 +4,13 @@ * @spec openspec/changes/contract-administration/specs/contract-administration/spec.md */ -import { describe, it, expect } from 'vitest' +import { describe, expect, it } from 'vitest' import { - PERIOD, - parseAmount, annualisedCost, - totalAnnualisedCost, isOneOff, + parseAmount, + PERIOD, + totalAnnualisedCost, } from '../../src/utils/contractCost.js' describe('contractCost.parseAmount', () => { diff --git a/tests/vitest/facetSchema.spec.js b/tests/vitest/facetSchema.spec.js index 7c62927c..eab5ee14 100644 --- a/tests/vitest/facetSchema.spec.js +++ b/tests/vitest/facetSchema.spec.js @@ -17,10 +17,10 @@ * reads the package's own registry. */ -import { describe, it, expect } from 'vitest' -import { buildFacetDimensionSchema } from '../../src/utils/facetSchema.js' +import { describe, expect, it } from 'vitest' // The REAL implementation from the installed package — not a local restatement. import { filtersFromSchema } from '../../node_modules/@conduction/nextcloud-vue/src/utils/schema.js' +import { buildFacetDimensionSchema } from '../../src/utils/facetSchema.js' /** The four GEMMA dimensions FacetedCatalogIndexView declares. */ const DIMENSION_LABELS = { diff --git a/tests/vitest/heartbeat.spec.js b/tests/vitest/heartbeat.spec.js index b3d5979c..e7805f0f 100644 --- a/tests/vitest/heartbeat.spec.js +++ b/tests/vitest/heartbeat.spec.js @@ -9,7 +9,7 @@ * and the withHeartbeat convenience wrapper. */ -import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest' +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' // The module instantiates a singleton + reads global OC.requestToken inside // sendHeartbeat(). We stub the globals and the fetch implementation before diff --git a/tests/vitest/licensePosture.spec.js b/tests/vitest/licensePosture.spec.js index 19bdf031..9b453542 100644 --- a/tests/vitest/licensePosture.spec.js +++ b/tests/vitest/licensePosture.spec.js @@ -9,14 +9,14 @@ * @spec openspec/specs/software-license-posture/spec.md */ -import { describe, it, expect } from 'vitest' +import { describe, expect, it } from 'vitest' import { + deploymentCount, LICENSE_TYPE, normaliseLicenseType, - deploymentCount, - portfolioPosture, - perVendorRollup, perOrganisationPosture, + perVendorRollup, + portfolioPosture, } from '../../src/utils/licensePosture.js' // Modules: M1 open (vendor VA), M2 closed (vendor VA), M3 unknown-type (vendor VB), @@ -40,11 +40,13 @@ const modules = [ { id: 'M4', name: 'Shelfware', licentietype: 'Closed source', provider: 'VB' }, ] -const inProd = (extra) => ({ - startDateInProduction: '2025-01-01', - startDateOutPhased: '', - ...extra, -}) +function inProd(extra) { + return { + startDateInProduction: '2025-01-01', + startDateOutPhased: '', + ...extra, + } +} // Usages: M1 deployed twice (O1, O2); M2 once (O1); M3 once (O1); M4 phased out (O1). const usages = [ diff --git a/tests/vitest/lifecyclePhase.spec.js b/tests/vitest/lifecyclePhase.spec.js index a67a9b6b..98726249 100644 --- a/tests/vitest/lifecyclePhase.spec.js +++ b/tests/vitest/lifecyclePhase.spec.js @@ -7,13 +7,13 @@ * @spec openspec/changes/application-lifecycle-tracking/specs/application-lifecycle-tracking/spec.md */ -import { describe, it, expect } from 'vitest' +import { describe, expect, it } from 'vitest' import { - PHASE, - parseDate, derivePhase, endOfSupportState, isEolApproaching, + parseDate, + PHASE, phaseOrder, resolveUuid, } from '../../src/utils/lifecyclePhase.js' diff --git a/tests/vitest/manifestWidgetIcons.spec.js b/tests/vitest/manifestWidgetIcons.spec.js index 3f614dc0..2d274415 100644 --- a/tests/vitest/manifestWidgetIcons.spec.js +++ b/tests/vitest/manifestWidgetIcons.spec.js @@ -21,10 +21,10 @@ * SPDX-License-Identifier: EUPL-1.2 */ -import { describe, it, expect } from 'vitest' import fs from 'fs' import path from 'path' import { fileURLToPath } from 'url' +import { describe, expect, it } from 'vitest' const here = path.dirname(fileURLToPath(import.meta.url)) const repoRoot = path.resolve(here, '../..') diff --git a/tests/vitest/moderationItem.spec.js b/tests/vitest/moderationItem.spec.js index 4cd54c13..fcd60223 100644 --- a/tests/vitest/moderationItem.spec.js +++ b/tests/vitest/moderationItem.spec.js @@ -4,10 +4,10 @@ * @spec openspec/changes/open-data-publishing/specs/open-data-publishing/spec.md */ -import { describe, it, expect } from 'vitest' +import { describe, expect, it } from 'vitest' import { - moderationItemTitle, moderationItemSubtitle, + moderationItemTitle, } from '../../src/utils/moderationItem.js' describe('moderationItemTitle', () => { @@ -25,7 +25,11 @@ describe('moderationItemTitle', () => { }) it('ignores blank/whitespace title fields', () => { - expect(moderationItemTitle({ name: ' ', name: 'Real' })).toBe('Real') + // A blank `name` must fall THROUGH to the next title field. The old + // assertion passed `{ name: ' ', name: 'Real' }`, where the second + // key silently overwrites the first, so the object it actually built + // was `{ name: 'Real' }` and the blank branch was never taken. + expect(moderationItemTitle({ name: ' ', title: 'Real' })).toBe('Real') }) }) diff --git a/tests/vitest/navigationStore.spec.js b/tests/vitest/navigationStore.spec.js index a6e6f6b3..2b6201d1 100644 --- a/tests/vitest/navigationStore.spec.js +++ b/tests/vitest/navigationStore.spec.js @@ -9,8 +9,8 @@ * through a real Pinia instance; console noise is silenced. */ -import { describe, it, expect, beforeEach, vi } from 'vitest' import { createPinia, setActivePinia } from 'pinia' +import { beforeEach, describe, expect, it, vi } from 'vitest' import { useNavigationStore } from '../../src/store/modules/navigation.js' describe('stackiq navigation store', () => { diff --git a/tests/vitest/openDataProjection.spec.js b/tests/vitest/openDataProjection.spec.js index 1ddb211b..ed89f682 100644 --- a/tests/vitest/openDataProjection.spec.js +++ b/tests/vitest/openDataProjection.spec.js @@ -4,11 +4,11 @@ * @spec openspec/changes/open-data-publishing/specs/open-data-publishing/spec.md */ -import { describe, it, expect } from 'vitest' +import { describe, expect, it } from 'vitest' import { - projectOpenData, - isClean, DEFAULT_LICENSE, + isClean, + projectOpenData, } from '../../src/utils/openDataProjection.js' describe('openDataProjection.projectOpenData', () => { diff --git a/tests/vitest/orClient.spec.js b/tests/vitest/orClient.spec.js index d3875c05..189a2898 100644 --- a/tests/vitest/orClient.spec.js +++ b/tests/vitest/orClient.spec.js @@ -7,17 +7,15 @@ * Exact-output assertions on every branch (ADR-025 i18n + multi-tenancy). */ -import { describe, it, expect, beforeEach } from 'vitest' - +import { beforeEach, describe, expect, it } from 'vitest' import { + buildObjectUrl, + buildWriteHeaders, OR_API_BASE, resolveLanguage, withLanguageParam, - buildWriteHeaders, - buildObjectUrl, } from '../../src/composables/orClient.js' - -import { __setLanguage, __resetLanguage } from './stubs/nextcloud-l10n.js' +import { __resetLanguage, __setLanguage } from './stubs/nextcloud-l10n.js' describe('OR_API_BASE', () => { it('points at the OpenRegister object API root', () => { diff --git a/tests/vitest/portfolioReport.spec.js b/tests/vitest/portfolioReport.spec.js index 4451fd1e..da3b7b36 100644 --- a/tests/vitest/portfolioReport.spec.js +++ b/tests/vitest/portfolioReport.spec.js @@ -11,14 +11,14 @@ * @spec openspec/changes/portfolio-rationalization-time/specs/portfolio-rationalization-time/spec.md */ -import { describe, it, expect } from 'vitest' +import { describe, expect, it } from 'vitest' import { - QUADRANT_ORDER, - quadrantColor, + buildCsvExportUrl, cloudTransitionLabel, formatCurrency, groupRowsByQuadrant, - buildCsvExportUrl, + QUADRANT_ORDER, + quadrantColor, } from '../../src/utils/portfolioReport.js' describe('QUADRANT_ORDER', () => { diff --git a/tests/vitest/reviewAggregate.spec.js b/tests/vitest/reviewAggregate.spec.js index 572e53eb..fdbddd1f 100644 --- a/tests/vitest/reviewAggregate.spec.js +++ b/tests/vitest/reviewAggregate.spec.js @@ -5,7 +5,7 @@ * @spec openspec/specs/catalog-ratings/spec.md#requirement-module-and-dienst-detail-pages-must-display-an-aggregate-rating-computed-only-from-approved-reviews */ -import { describe, it, expect } from 'vitest' +import { describe, expect, it } from 'vitest' import { aggregatePath, normaliseAggregate, diff --git a/tests/vitest/reviewForm.spec.js b/tests/vitest/reviewForm.spec.js index 84124354..e5d34473 100644 --- a/tests/vitest/reviewForm.spec.js +++ b/tests/vitest/reviewForm.spec.js @@ -9,12 +9,12 @@ * @spec openspec/specs/catalog-ratings/spec.md#requirement-the-submitting-users-identity-must-be-bound-server-side-and-must-not-be-accepted-from-client-input */ -import { describe, it, expect } from 'vitest' +import { describe, expect, it } from 'vitest' import { - ratingOptions, - isReviewFormValid, buildReviewPayload, buildReviewSubmission, + isReviewFormValid, + ratingOptions, } from '../../src/utils/reviewForm.js' describe('reviewForm.ratingOptions', () => { diff --git a/tests/vitest/sbomVulnerabilityMatch.spec.js b/tests/vitest/sbomVulnerabilityMatch.spec.js index 105c0ff9..89316a18 100644 --- a/tests/vitest/sbomVulnerabilityMatch.spec.js +++ b/tests/vitest/sbomVulnerabilityMatch.spec.js @@ -9,13 +9,13 @@ * @spec openspec/specs/sbom-import/spec.md#requirement-components-are-matched-against-existing-kwetsbaarheden-without-external-calls */ -import { describe, it, expect } from 'vitest' +import { describe, expect, it } from 'vitest' import { - purlPackageName, confirmedMatches, - possibleMatches, matchComponent, matchComponents, + possibleMatches, + purlPackageName, } from '../../src/utils/sbomVulnerabilityMatch.js' describe('sbomVulnerabilityMatch.purlPackageName', () => { diff --git a/tests/vitest/sectionInfoSlot.spec.js b/tests/vitest/sectionInfoSlot.spec.js index a2b7a4a0..34d048b6 100644 --- a/tests/vitest/sectionInfoSlot.spec.js +++ b/tests/vitest/sectionInfoSlot.spec.js @@ -35,9 +35,8 @@ * @spec openspec/specs/fe-shell-navigation/spec.md */ -import { describe, it, expect } from 'vitest' import { mount } from '@vue/test-utils' - +import { describe, expect, it } from 'vitest' import AlwaysVisibleSection from '../../src/components/AlwaysVisibleSection.vue' import CollapsibleSection from '../../src/components/CollapsibleSection.vue' diff --git a/tests/vitest/settingsInfoPanels.spec.js b/tests/vitest/settingsInfoPanels.spec.js index 7d4c1abb..ef90e86c 100644 --- a/tests/vitest/settingsInfoPanels.spec.js +++ b/tests/vitest/settingsInfoPanels.spec.js @@ -41,11 +41,11 @@ * @spec openspec/specs/fe-shell-navigation/spec.md */ -import { describe, it, expect } from 'vitest' -import { readFileSync, readdirSync } from 'fs' -import path from 'path' -import { parse } from '@vue/compiler-sfc' import { compile } from '@vue/compiler-dom' +import { parse } from '@vue/compiler-sfc' +import { readdirSync, readFileSync } from 'fs' +import path from 'path' +import { describe, expect, it } from 'vitest' import * as VueRuntime from 'vue' import { createApp } from 'vue' @@ -106,7 +106,7 @@ function renderFragment(markup) { hoistStatic: false, prefixIdentifiers: true, }) - // eslint-disable-next-line no-new-func + const render = new Function('Vue', code)(VueRuntime) const host = document.createElement('div') diff --git a/tests/vitest/suiteWizard.spec.js b/tests/vitest/suiteWizard.spec.js index e3418534..daaa0190 100644 --- a/tests/vitest/suiteWizard.spec.js +++ b/tests/vitest/suiteWizard.spec.js @@ -7,11 +7,11 @@ * @spec openspec/specs/suite-wizard/spec.md#requirement-the-wizard-must-require-at-least-one-attached-application-before-advancing-past-the-applications-step */ -import { describe, it, expect } from 'vitest' +import { describe, expect, it } from 'vitest' import { - isDetailsStepValid, - isApplicationsStepValid, buildSuitePayload, + isApplicationsStepValid, + isDetailsStepValid, summarizeApplications, } from '../../src/utils/suiteWizard.js' diff --git a/tests/vitest/translationBadge.spec.js b/tests/vitest/translationBadge.spec.js index f8e53d9d..fff2db6f 100644 --- a/tests/vitest/translationBadge.spec.js +++ b/tests/vitest/translationBadge.spec.js @@ -8,10 +8,10 @@ * language. Exact-output assertions on every branch. */ -import { describe, it, expect } from 'vitest' +import { describe, expect, it } from 'vitest' import { - languageName, getSourceLanguage, + languageName, shouldShowTranslationBadge, translationBadge, } from '../../src/utils/translationBadge.js' diff --git a/tests/vitest/vulnerabilityExposure.spec.js b/tests/vitest/vulnerabilityExposure.spec.js index 80c1f8d0..fc05f497 100644 --- a/tests/vitest/vulnerabilityExposure.spec.js +++ b/tests/vitest/vulnerabilityExposure.spec.js @@ -9,13 +9,13 @@ * @spec openspec/specs/module-vulnerability-tracking/spec.md */ -import { describe, it, expect } from 'vitest' +import { describe, expect, it } from 'vitest' import { - isInProduction, affectedModuleIds, computeExposure, - exposureCount, exposedOrganisations, + exposureCount, + isInProduction, } from '../../src/utils/vulnerabilityExposure.js' // A vulnerability affecting module M1. diff --git a/tests/vitest/vulnerabilitySeverity.spec.js b/tests/vitest/vulnerabilitySeverity.spec.js index 852b11f9..03f0a727 100644 --- a/tests/vitest/vulnerabilitySeverity.spec.js +++ b/tests/vitest/vulnerabilitySeverity.spec.js @@ -8,14 +8,14 @@ * @spec openspec/specs/module-vulnerability-tracking/spec.md */ -import { describe, it, expect } from 'vitest' +import { describe, expect, it } from 'vitest' import { - SEVERITY, + deriveSeverity, + matchesSeverity, parseCvss, + SEVERITY, severityFromScore, - deriveSeverity, severityOrder, - matchesSeverity, } from '../../src/utils/vulnerabilitySeverity.js' describe('vulnerabilitySeverity.parseCvss', () => {