diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 27ec440..514e520 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -30,7 +30,6 @@ jobs: runs-on: ubuntu-latest if: > (github.event_name == 'push' && github.ref != 'refs/heads/master') || - (github.event_name == 'pull_request' && github.base_ref != 'master') || (github.event_name == 'workflow_dispatch' && inputs.run_full != true) steps: @@ -56,8 +55,8 @@ jobs: name: Full lane (coverage + full e2e) runs-on: ubuntu-latest if: > + github.event_name == 'pull_request' || (github.event_name == 'push' && github.ref == 'refs/heads/master') || - (github.event_name == 'pull_request' && github.base_ref == 'master') || github.event_name == 'schedule' || (github.event_name == 'workflow_dispatch' && inputs.run_full == true) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9fa1246..c5ccef9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,24 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added +- CI full lane (`test:ci:full`) runs on every pull request; fast lane + remains on feature-branch pushes only; Vitest `pool: forks` and shared + Matrix client spec mocks stabilize coverage runs + ([#122](https://github.com/mjkatgithub/Decentra/issues/122)) + +### Fixed + +- Synapse E2E on Linux CI: restore data-dir ownership after `synapse generate` + so `homeserver.yaml` overrides are writable (GitHub Actions EACCES) + ([#122](https://github.com/mjkatgithub/Decentra/issues/122)) +- E2E runner: `start-server-and-test` third argument must be an npm script + name (fixes local + CI `test:e2e:run` / smoke) + ([#122](https://github.com/mjkatgithub/Decentra/issues/122)) +- Synapse E2E data dir: restore UID 991 after patching config so the + container can read `localhost.signing.key`; merge all E2E env files; + run Cucumber via Node (fixes Windows `@tag` ENOENT) + ([#122](https://github.com/mjkatgithub/Decentra/issues/122)) + - Leave Matrix rooms from the chat sidebar: confirmation dialog, `leaveRoom` via matrix-js-sdk, `m.direct` cleanup for DMs, neutral chat view after leaving the active room; Cucumber E2E for ephemeral diff --git a/README.md b/README.md index e4f1588..a970645 100644 --- a/README.md +++ b/README.md @@ -89,6 +89,25 @@ npm run test:ci:full npm run prepare:e2e ``` +### Test layers (current state) + +| Layer | Location | Role today | +|-------|----------|------------| +| Unit | `tests/unit/` | Main safety net (~438 tests); composables, utils, components | +| Integration | `tests/integration/` | Small module-level checks (3 files, 9 tests); not full Nuxt/Matrix wiring | +| E2E | `tests/e2e/` | Cucumber + Playwright against Docker Synapse | + +**Coverage:** `npm run test:coverage` reports **~50% lines** overall +(`vitest.config.ts` thresholds are intentionally low at 10–20% so CI +passes while pages/plugins stay mostly untested). A **~90%** target is +reasonable for critical modules (Matrix client, timeline, auth) but is **not** +the current baseline — raise thresholds incrementally per epic/issue. + +**Integration gap:** Issue templates mention integration tests; most features +today rely on unit + E2E. Expanding `tests/integration/` (e.g. composable +chains, i18n + component mount, timeline with mocked Matrix room) is backlog +work, not part of the CI lane fix. + ### E2E Credentials For credential-based E2E scenarios, create a local env file: @@ -104,18 +123,29 @@ The local file stays untracked. This repo uses `.github/workflows/ci.yml` with two lanes: -- **Fast lane:** runs on push + pull request (`test:ci:fast`). -- **Full lane:** runs nightly and optionally manual - (`test:ci:full`). +- **Fast lane:** runs on push to non-`master` branches (feature branches, + `develop`); runs `test:ci:fast` (unit + integration + E2E smoke). +- **Full lane:** runs on every pull request (targets `develop` or + `master`), push to `master`, nightly schedule, and manual dispatch with + `run_full: true`; runs `test:ci:full` (integration + coverage + full + Synapse E2E via Docker). + +### E2E credentials (full lane) -### Required repository secrets (for full lane) +The full lane starts a local Synapse stack in Docker and writes +`tests/e2e/.env.e2e.generated` during seeding — **no GitHub secrets are +required** for the default CI full-lane path. -Set these in GitHub under **Settings > Secrets and variables > Actions**: +For optional scenarios against an external homeserver (e.g. matrix.org), +set repository secrets under **Settings > Secrets and variables > Actions**: - `E2E_MATRIX_HOMESERVER` (optional, defaults to `https://matrix.org`) - `E2E_MATRIX_USERNAME` - `E2E_MATRIX_PASSWORD` +For local credential-based runs, copy `tests/e2e/.env.e2e.example` to +`tests/e2e/.env.e2e.local` and fill in username/password (see above). + ### How to run manually 1. Open your repository on GitHub. diff --git a/package.json b/package.json index 42bba19..7261e38 100644 --- a/package.json +++ b/package.json @@ -13,15 +13,19 @@ "test:unit": "vitest run tests/unit", "test:integration": "vitest run tests/integration --config vitest.integration.config.ts", "test:e2e": "node tests/e2e/scripts/run-synapse-e2e.mjs", - "test:e2e:run": "nuxt build && start-server-and-test preview 3000 node tests/e2e/scripts/run-cucumber-exclude-email.mjs", - "test:e2e:run:email": "nuxt build && start-server-and-test preview 3000 node tests/e2e/scripts/run-cucumber-email-only.mjs", - "test:e2e:run:recaptcha": "nuxt build && start-server-and-test preview 3000 node tests/e2e/scripts/run-cucumber-recaptcha-only.mjs", - "test:e2e:smoke": "nuxt build && start-server-and-test preview 3000 \"cucumber-js --tags @smoke\"", + "test:e2e:cucumber": "node tests/e2e/scripts/run-cucumber-exclude-email.mjs", + "test:e2e:cucumber:email": "node tests/e2e/scripts/run-cucumber-email-only.mjs", + "test:e2e:cucumber:recaptcha": "node tests/e2e/scripts/run-cucumber-recaptcha-only.mjs", + "test:e2e:smoke:cucumber": "node tests/e2e/scripts/run-cucumber-smoke.mjs", + "test:e2e:run": "nuxt build && start-server-and-test preview 3000 test:e2e:cucumber", + "test:e2e:run:email": "nuxt build && start-server-and-test preview 3000 test:e2e:cucumber:email", + "test:e2e:run:recaptcha": "nuxt build && start-server-and-test preview 3000 test:e2e:cucumber:recaptcha", + "test:e2e:smoke": "nuxt build && start-server-and-test preview 3000 test:e2e:smoke:cucumber", "test:e2e:signup-email": "cross-env DECENTRA_E2E_SIGNUP_EMAIL=1 node tests/e2e/scripts/run-synapse-e2e-signup-email.mjs", "test:e2e:signup-recaptcha": "cross-env DECENTRA_E2E_SIGNUP_RECAPTCHA=1 node tests/e2e/scripts/run-synapse-e2e-signup-recaptcha.mjs", "test:coverage": "vitest run --coverage", "test:ci:fast": "npm run test:unit && npm run test:integration && npm run test:e2e:smoke", - "test:ci:full": "npm run test:unit && npm run test:integration && npm run test:coverage && npm run test:e2e", + "test:ci:full": "npm run test:integration && npm run test:coverage && npm run test:e2e", "test:e2e:headed": "cross-env HEADLESS=false npm run test:e2e", "prepare:e2e": "playwright install chromium", "check:line-limit": "node scripts/check-file-line-limit.mjs" diff --git a/tests/e2e/scripts/run-cucumber-cli.mjs b/tests/e2e/scripts/run-cucumber-cli.mjs new file mode 100644 index 0000000..4472440 --- /dev/null +++ b/tests/e2e/scripts/run-cucumber-cli.mjs @@ -0,0 +1,54 @@ +import { spawn } from 'node:child_process' +import { existsSync } from 'node:fs' +import { dirname, resolve } from 'node:path' +import { fileURLToPath } from 'node:url' +import { loadE2EEnv } from './runtime-e2e-env.mjs' + +const currentFilePath = fileURLToPath(import.meta.url) +export const workspaceRoot = resolve( + dirname(currentFilePath), + '..', + '..', + '..' +) + +function resolveCucumberEntry() { + const entry = resolve( + workspaceRoot, + 'node_modules/@cucumber/cucumber/bin/cucumber.js' + ) + if (!existsSync(entry)) { + throw new Error(`Missing @cucumber/cucumber at ${entry}`) + } + return entry +} + +/** + * Run Cucumber via Node (no shell) so Windows does not treat `@tag` as paths. + * + * @param {string | undefined} tagExpression + */ +export function runCucumber(tagExpression) { + loadE2EEnv(workspaceRoot) + + const args = [resolveCucumberEntry()] + if (tagExpression) { + args.push('--tags', tagExpression) + } + + return new Promise((resolvePromise, rejectPromise) => { + const child = spawn(process.execPath, args, { + cwd: workspaceRoot, + stdio: 'inherit', + env: process.env, + }) + child.on('error', (error) => rejectPromise(error)) + child.on('close', (exitCode) => { + if (exitCode === 0) { + resolvePromise() + return + } + rejectPromise(new Error(`cucumber-js exited with code ${exitCode ?? 1}`)) + }) + }) +} diff --git a/tests/e2e/scripts/run-cucumber-email-only.mjs b/tests/e2e/scripts/run-cucumber-email-only.mjs index 4bc4dd8..daeadcb 100644 --- a/tests/e2e/scripts/run-cucumber-email-only.mjs +++ b/tests/e2e/scripts/run-cucumber-email-only.mjs @@ -1,37 +1,6 @@ -import { spawn } from 'node:child_process' -import { fileURLToPath } from 'node:url' -import { dirname, resolve } from 'node:path' +import { runCucumber } from './run-cucumber-cli.mjs' -const workspaceRoot = resolve( - dirname(fileURLToPath(import.meta.url)), - '..', - '..', - '..' -) - -function run() { - return new Promise((resolvePromise, rejectPromise) => { - const child = spawn( - 'npx', - ['cucumber-js', '--tags', '@email_signup'], - { - cwd: workspaceRoot, - stdio: 'inherit', - shell: process.platform === 'win32' - } - ) - child.on('error', (error) => rejectPromise(error)) - child.on('close', (code) => { - if (code === 0) { - resolvePromise() - return - } - rejectPromise(new Error(`cucumber-js exited with code ${code ?? 1}`)) - }) - }) -} - -void run().catch((error) => { +void runCucumber('@email_signup').catch((error) => { console.error(error instanceof Error ? error.message : String(error)) process.exit(1) }) diff --git a/tests/e2e/scripts/run-cucumber-exclude-email.mjs b/tests/e2e/scripts/run-cucumber-exclude-email.mjs index d6c591f..e1edef0 100644 --- a/tests/e2e/scripts/run-cucumber-exclude-email.mjs +++ b/tests/e2e/scripts/run-cucumber-exclude-email.mjs @@ -1,37 +1,6 @@ -import { spawn } from 'node:child_process' -import { fileURLToPath } from 'node:url' -import { dirname, resolve } from 'node:path' +import { runCucumber } from './run-cucumber-cli.mjs' -const workspaceRoot = resolve( - dirname(fileURLToPath(import.meta.url)), - '..', - '..', - '..' -) - -function run() { - return new Promise((resolvePromise, rejectPromise) => { - const child = spawn( - 'npx', - ['cucumber-js', '--tags', 'not @email_signup and not @recaptcha_signup'], - { - cwd: workspaceRoot, - stdio: 'inherit', - shell: process.platform === 'win32' - } - ) - child.on('error', (error) => rejectPromise(error)) - child.on('close', (code) => { - if (code === 0) { - resolvePromise() - return - } - rejectPromise(new Error(`cucumber-js exited with code ${code ?? 1}`)) - }) - }) -} - -void run().catch((error) => { +void runCucumber('not @email_signup and not @recaptcha_signup').catch((error) => { console.error(error instanceof Error ? error.message : String(error)) process.exit(1) }) diff --git a/tests/e2e/scripts/run-cucumber-recaptcha-only.mjs b/tests/e2e/scripts/run-cucumber-recaptcha-only.mjs index 5576e84..efafa18 100644 --- a/tests/e2e/scripts/run-cucumber-recaptcha-only.mjs +++ b/tests/e2e/scripts/run-cucumber-recaptcha-only.mjs @@ -1,37 +1,6 @@ -import { spawn } from 'node:child_process' -import { fileURLToPath } from 'node:url' -import { dirname, resolve } from 'node:path' +import { runCucumber } from './run-cucumber-cli.mjs' -const workspaceRoot = resolve( - dirname(fileURLToPath(import.meta.url)), - '..', - '..', - '..' -) - -function run() { - return new Promise((resolvePromise, rejectPromise) => { - const child = spawn( - 'npx', - ['cucumber-js', '--tags', '@recaptcha_signup'], - { - cwd: workspaceRoot, - stdio: 'inherit', - shell: process.platform === 'win32' - } - ) - child.on('error', (error) => rejectPromise(error)) - child.on('close', (code) => { - if (code === 0) { - resolvePromise() - return - } - rejectPromise(new Error(`cucumber-js exited with code ${code ?? 1}`)) - }) - }) -} - -void run().catch((error) => { +void runCucumber('@recaptcha_signup').catch((error) => { console.error(error instanceof Error ? error.message : String(error)) process.exit(1) }) diff --git a/tests/e2e/scripts/run-cucumber-smoke.mjs b/tests/e2e/scripts/run-cucumber-smoke.mjs new file mode 100644 index 0000000..78fe247 --- /dev/null +++ b/tests/e2e/scripts/run-cucumber-smoke.mjs @@ -0,0 +1,6 @@ +import { runCucumber } from './run-cucumber-cli.mjs' + +void runCucumber('@smoke').catch((error) => { + console.error(error instanceof Error ? error.message : String(error)) + process.exit(1) +}) diff --git a/tests/e2e/scripts/run-synapse-e2e.mjs b/tests/e2e/scripts/run-synapse-e2e.mjs index 75671b8..c37ce98 100644 --- a/tests/e2e/scripts/run-synapse-e2e.mjs +++ b/tests/e2e/scripts/run-synapse-e2e.mjs @@ -1,9 +1,12 @@ import { spawn } from 'node:child_process' +import { existsSync } from 'node:fs' import { dirname, resolve } from 'node:path' import { fileURLToPath } from 'node:url' +import { loadE2EEnv } from './runtime-e2e-env.mjs' const currentFilePath = fileURLToPath(import.meta.url) const workspaceRoot = resolve(dirname(currentFilePath), '..', '..', '..') +const generatedEnvPath = resolve(workspaceRoot, 'tests/e2e/.env.e2e.generated') function runCommand(binary, args) { return new Promise((resolvePromise, rejectPromise) => { @@ -27,6 +30,13 @@ async function main() { let testFailed = false await runCommand('node', ['tests/e2e/scripts/runtime-manage-synapse.mjs', 'up']) await runCommand('node', ['tests/e2e/scripts/runtime-seed-synapse.mjs']) + loadE2EEnv(workspaceRoot) + if (!existsSync(generatedEnvPath) || !process.env.E2E_MATRIX_USERNAME) { + throw new Error( + 'Synapse seed did not write E2E credentials to ' + + 'tests/e2e/.env.e2e.generated' + ) + } try { await runCommand('npm', ['run', 'test:e2e:run']) diff --git a/tests/e2e/scripts/runtime-e2e-env.mjs b/tests/e2e/scripts/runtime-e2e-env.mjs index ffacb08..110660f 100644 --- a/tests/e2e/scripts/runtime-e2e-env.mjs +++ b/tests/e2e/scripts/runtime-e2e-env.mjs @@ -42,7 +42,6 @@ export function loadE2EEnv(workspaceRoot) { const variableValue = trimmedLine.slice(separatorIndex + 1).trim() process.env[variableName] = variableValue } - break } if (parseBoolean(process.env.E2E_USE_LOCAL_SYNAPSE)) { diff --git a/tests/e2e/scripts/runtime-manage-synapse.mjs b/tests/e2e/scripts/runtime-manage-synapse.mjs index 15d92eb..fc416e9 100644 --- a/tests/e2e/scripts/runtime-manage-synapse.mjs +++ b/tests/e2e/scripts/runtime-manage-synapse.mjs @@ -160,10 +160,57 @@ async function waitForSynapse() { throw new Error('Synapse startup timed out after 120 seconds') } +/** matrixdotorg/synapse image runs as UID/GID 991. */ +const SYNAPSE_CONTAINER_UID = 991 +const SYNAPSE_CONTAINER_GID = 991 + +/** + * Synapse `generate` writes /data as UID 991. On Linux CI the checkout user + * must own files briefly to patch homeserver.yaml, then ownership returns to + * 991 so the Synapse container can read signing keys. + */ +async function chownDataDir(ownerUid, ownerGid) { + if (process.platform === 'win32') { + return + } + await runCommand('docker', [ + 'run', + '--rm', + '-v', + `${dataDir}:/data`, + '--user', + 'root', + 'alpine:3', + 'chown', + '-R', + `${ownerUid}:${ownerGid}`, + '/data', + ]) +} + +async function chownDataDirToHostUser() { + const uid = process.getuid?.() + const gid = process.getgid?.() + if (uid === undefined || gid === undefined) { + return + } + await chownDataDir(uid, gid) +} + +async function chownDataDirToSynapseUser() { + await chownDataDir(SYNAPSE_CONTAINER_UID, SYNAPSE_CONTAINER_GID) +} + +async function patchSynapseConfigOverrides() { + await chownDataDirToHostUser() + ensureSynapseConfigOverrides() + await chownDataDirToSynapseUser() +} + async function ensureConfigGenerated() { ensureDirectory(dataDir) if (existsSync(homeserverConfigPath)) { - ensureSynapseConfigOverrides() + await patchSynapseConfigOverrides() return } @@ -177,9 +224,9 @@ async function ensureConfigGenerated() { '-v', `${dataDir}:/data`, 'matrixdotorg/synapse:latest', - 'generate' + 'generate', ]) - ensureSynapseConfigOverrides() + await patchSynapseConfigOverrides() } async function main() { diff --git a/tests/e2e/step-definitions/login.steps.mjs b/tests/e2e/step-definitions/login.steps.mjs index fe43041..6ac2a57 100644 --- a/tests/e2e/step-definitions/login.steps.mjs +++ b/tests/e2e/step-definitions/login.steps.mjs @@ -55,9 +55,16 @@ Given('valid e2e credentials are configured', async function () { const missingKeys = requiredKeys.filter((key) => !process.env[key]) if (missingKeys.length > 0) { + const dockerHint = + 'Run `npm run test:e2e` to seed Synapse and write ' + + 'tests/e2e/.env.e2e.generated.' + const localHint = + 'Create tests/e2e/.env.e2e.local from tests/e2e/.env.e2e.example.' + const hint = process.env.E2E_USE_LOCAL_SYNAPSE === 'true' + ? dockerHint + : localHint throw new Error( - `Missing E2E credentials: ${missingKeys.join(', ')}. ` - + 'Create tests/e2e/.env.e2e.local from tests/e2e/.env.e2e.example.' + `Missing E2E credentials: ${missingKeys.join(', ')}. ${hint}` ) } }) diff --git a/tests/unit/composables/matrixClientSpecMocks.ts b/tests/unit/composables/matrixClientSpecMocks.ts new file mode 100644 index 0000000..63e1282 --- /dev/null +++ b/tests/unit/composables/matrixClientSpecMocks.ts @@ -0,0 +1,22 @@ +import { vi } from 'vitest' +import { buildMatrixSdkMock } from './matrixClientSdkMock' + +const matrixMocks = vi.hoisted(() => ({ + createClient: vi.fn(), + initCryptoWasm: vi.fn(async () => undefined), +})) + +vi.mock('matrix-js-sdk', () => buildMatrixSdkMock(matrixMocks.createClient)) +vi.mock('@matrix-org/matrix-sdk-crypto-wasm', () => ({ + initAsync: matrixMocks.initCryptoWasm, +})) +vi.mock('~/utils/videoMetadata', () => ({ + readVideoMetadata: vi.fn(async () => ({ + durationMs: 5000, + w: 640, + h: 360, + })), + captureVideoThumbnail: vi.fn(async () => ( + new Blob(['thumb'], { type: 'image/jpeg' }) + )), +})) diff --git a/tests/unit/composables/matrixClientTestSetup.ts b/tests/unit/composables/matrixClientTestSetup.ts index 283c748..1496292 100644 --- a/tests/unit/composables/matrixClientTestSetup.ts +++ b/tests/unit/composables/matrixClientTestSetup.ts @@ -1,4 +1,12 @@ import { computed, ref } from 'vue' +import { vi } from 'vitest' + +/** Reset module mocks before each useMatrixClient spec (avoids page-spec leakage). */ +export function prepareMatrixClientSpecFile(): void { + vi.unmock('~/composables/useMatrixClient') + vi.resetModules() + vi.clearAllMocks() +} export function setupMatrixClientTestGlobals(options?: { initialRestoreStatus?: 'idle' | 'loading' | 'success' | 'failure' diff --git a/tests/unit/composables/useMatrixClient.helpers.spec.ts b/tests/unit/composables/useMatrixClient.helpers.spec.ts index 92bd008..43d32c1 100644 --- a/tests/unit/composables/useMatrixClient.helpers.spec.ts +++ b/tests/unit/composables/useMatrixClient.helpers.spec.ts @@ -1,32 +1,10 @@ -import { beforeEach, describe, expect, it, vi } from 'vitest' -import { buildMatrixSdkMock } from './matrixClientSdkMock' -import { - setupFreshMatrixClientGlobals, - setupMatrixClientTestGlobals, -} from './matrixClientTestSetup' +import { beforeEach, describe, expect, it } from 'vitest' +import './matrixClientSpecMocks' +import { prepareMatrixClientSpecFile } from './matrixClientTestSetup' -const matrixMocks = vi.hoisted(() => ({ - createClient: vi.fn(), - initCryptoWasm: vi.fn(async () => undefined), -})) - -vi.mock('matrix-js-sdk', () => buildMatrixSdkMock(matrixMocks.createClient)) -vi.mock('@matrix-org/matrix-sdk-crypto-wasm', () => ({ - initAsync: matrixMocks.initCryptoWasm, -})) -vi.mock('~/utils/videoMetadata', () => ({ - readVideoMetadata: vi.fn(async () => ({ - durationMs: 5000, - w: 640, - h: 360, - })), - captureVideoThumbnail: vi.fn(async () => ( - new Blob(['thumb'], { type: 'image/jpeg' }) - )), -})) - -const createClient = matrixMocks.createClient -const initCryptoWasm = matrixMocks.initCryptoWasm +beforeEach(() => { + prepareMatrixClientSpecFile() +}) describe('resolveHomeserverBaseUrlForClient', () => { it('upgrades http to https for public hostnames', async () => { diff --git a/tests/unit/composables/useMatrixClient.messages.spec.ts b/tests/unit/composables/useMatrixClient.messages.spec.ts index 1f2517d..8d3e84c 100644 --- a/tests/unit/composables/useMatrixClient.messages.spec.ts +++ b/tests/unit/composables/useMatrixClient.messages.spec.ts @@ -1,37 +1,16 @@ import { beforeEach, describe, expect, it, vi } from 'vitest' -import { buildMatrixSdkMock } from './matrixClientSdkMock' +import './matrixClientSpecMocks' +import { createClient } from 'matrix-js-sdk' +import { initAsync as initCryptoWasm } from '@matrix-org/matrix-sdk-crypto-wasm' import { + prepareMatrixClientSpecFile, setupFreshMatrixClientGlobals, setupMatrixClientTestGlobals, } from './matrixClientTestSetup' -const matrixMocks = vi.hoisted(() => ({ - createClient: vi.fn(), - initCryptoWasm: vi.fn(async () => undefined), -})) - -vi.mock('matrix-js-sdk', () => buildMatrixSdkMock(matrixMocks.createClient)) -vi.mock('@matrix-org/matrix-sdk-crypto-wasm', () => ({ - initAsync: matrixMocks.initCryptoWasm, -})) -vi.mock('~/utils/videoMetadata', () => ({ - readVideoMetadata: vi.fn(async () => ({ - durationMs: 5000, - w: 640, - h: 360, - })), - captureVideoThumbnail: vi.fn(async () => ( - new Blob(['thumb'], { type: 'image/jpeg' }) - )), -})) - -const createClient = matrixMocks.createClient -const initCryptoWasm = matrixMocks.initCryptoWasm - describe('useMatrixClient messages', () => { beforeEach(() => { - vi.resetModules() - vi.clearAllMocks() + prepareMatrixClientSpecFile() setupMatrixClientTestGlobals() }) diff --git a/tests/unit/composables/useMatrixClient.rooms.spec.ts b/tests/unit/composables/useMatrixClient.rooms.spec.ts index 6a0f4ac..6ae681a 100644 --- a/tests/unit/composables/useMatrixClient.rooms.spec.ts +++ b/tests/unit/composables/useMatrixClient.rooms.spec.ts @@ -1,37 +1,16 @@ import { beforeEach, describe, expect, it, vi } from 'vitest' -import { buildMatrixSdkMock } from './matrixClientSdkMock' +import './matrixClientSpecMocks' +import { createClient } from 'matrix-js-sdk' +import { initAsync as initCryptoWasm } from '@matrix-org/matrix-sdk-crypto-wasm' import { + prepareMatrixClientSpecFile, setupFreshMatrixClientGlobals, setupMatrixClientTestGlobals, } from './matrixClientTestSetup' -const matrixMocks = vi.hoisted(() => ({ - createClient: vi.fn(), - initCryptoWasm: vi.fn(async () => undefined), -})) - -vi.mock('matrix-js-sdk', () => buildMatrixSdkMock(matrixMocks.createClient)) -vi.mock('@matrix-org/matrix-sdk-crypto-wasm', () => ({ - initAsync: matrixMocks.initCryptoWasm, -})) -vi.mock('~/utils/videoMetadata', () => ({ - readVideoMetadata: vi.fn(async () => ({ - durationMs: 5000, - w: 640, - h: 360, - })), - captureVideoThumbnail: vi.fn(async () => ( - new Blob(['thumb'], { type: 'image/jpeg' }) - )), -})) - -const createClient = matrixMocks.createClient -const initCryptoWasm = matrixMocks.initCryptoWasm - describe('useMatrixClient rooms', () => { beforeEach(() => { - vi.resetModules() - vi.clearAllMocks() + prepareMatrixClientSpecFile() setupMatrixClientTestGlobals() localStorage.clear() if (typeof sessionStorage !== 'undefined') { diff --git a/tests/unit/composables/useMatrixClient.session.spec.ts b/tests/unit/composables/useMatrixClient.session.spec.ts index 21eddc2..9a9b2aa 100644 --- a/tests/unit/composables/useMatrixClient.session.spec.ts +++ b/tests/unit/composables/useMatrixClient.session.spec.ts @@ -1,37 +1,16 @@ import { beforeEach, describe, expect, it, vi } from 'vitest' -import { buildMatrixSdkMock } from './matrixClientSdkMock' +import './matrixClientSpecMocks' +import { createClient } from 'matrix-js-sdk' +import { initAsync as initCryptoWasm } from '@matrix-org/matrix-sdk-crypto-wasm' import { + prepareMatrixClientSpecFile, setupFreshMatrixClientGlobals, setupMatrixClientTestGlobals, } from './matrixClientTestSetup' -const matrixMocks = vi.hoisted(() => ({ - createClient: vi.fn(), - initCryptoWasm: vi.fn(async () => undefined), -})) - -vi.mock('matrix-js-sdk', () => buildMatrixSdkMock(matrixMocks.createClient)) -vi.mock('@matrix-org/matrix-sdk-crypto-wasm', () => ({ - initAsync: matrixMocks.initCryptoWasm, -})) -vi.mock('~/utils/videoMetadata', () => ({ - readVideoMetadata: vi.fn(async () => ({ - durationMs: 5000, - w: 640, - h: 360, - })), - captureVideoThumbnail: vi.fn(async () => ( - new Blob(['thumb'], { type: 'image/jpeg' }) - )), -})) - -const createClient = matrixMocks.createClient -const initCryptoWasm = matrixMocks.initCryptoWasm - describe('useMatrixClient session', () => { beforeEach(() => { - vi.resetModules() - vi.clearAllMocks() + prepareMatrixClientSpecFile() setupMatrixClientTestGlobals() }) diff --git a/tests/unit/composables/useMatrixClient.signup.spec.ts b/tests/unit/composables/useMatrixClient.signup.spec.ts index 0cca8ea..721ffbb 100644 --- a/tests/unit/composables/useMatrixClient.signup.spec.ts +++ b/tests/unit/composables/useMatrixClient.signup.spec.ts @@ -1,37 +1,16 @@ import { beforeEach, describe, expect, it, vi } from 'vitest' -import { buildMatrixSdkMock } from './matrixClientSdkMock' +import './matrixClientSpecMocks' +import { createClient } from 'matrix-js-sdk' +import { initAsync as initCryptoWasm } from '@matrix-org/matrix-sdk-crypto-wasm' import { + prepareMatrixClientSpecFile, setupFreshMatrixClientGlobals, setupMatrixClientTestGlobals, } from './matrixClientTestSetup' -const matrixMocks = vi.hoisted(() => ({ - createClient: vi.fn(), - initCryptoWasm: vi.fn(async () => undefined), -})) - -vi.mock('matrix-js-sdk', () => buildMatrixSdkMock(matrixMocks.createClient)) -vi.mock('@matrix-org/matrix-sdk-crypto-wasm', () => ({ - initAsync: matrixMocks.initCryptoWasm, -})) -vi.mock('~/utils/videoMetadata', () => ({ - readVideoMetadata: vi.fn(async () => ({ - durationMs: 5000, - w: 640, - h: 360, - })), - captureVideoThumbnail: vi.fn(async () => ( - new Blob(['thumb'], { type: 'image/jpeg' }) - )), -})) - -const createClient = matrixMocks.createClient -const initCryptoWasm = matrixMocks.initCryptoWasm - describe('useMatrixClient signup', () => { beforeEach(() => { - vi.resetModules() - vi.clearAllMocks() + prepareMatrixClientSpecFile() setupMatrixClientTestGlobals() }) diff --git a/vitest.config.ts b/vitest.config.ts index 4de2c03..cea342c 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -7,6 +7,7 @@ export default defineConfig({ test: { setupFiles: ['./tests/setup.ts'], environment: 'happy-dom', + pool: 'forks', include: ['tests/unit/**/*.spec.ts', 'tests/unit/**/*.test.ts'], coverage: { provider: 'v8',