diff --git a/src/tests/__mocks__/JobErrorDisplay.svelte b/src/tests/__mocks__/JobErrorDisplay.svelte new file mode 100644 index 0000000..5b27ca9 --- /dev/null +++ b/src/tests/__mocks__/JobErrorDisplay.svelte @@ -0,0 +1,13 @@ + + +{#if job.status === 'failed' && job.error} +
{job.error}
+{/if} diff --git a/src/tests/__mocks__/app-environment.ts b/src/tests/__mocks__/app-environment.ts new file mode 100644 index 0000000..e77f65c --- /dev/null +++ b/src/tests/__mocks__/app-environment.ts @@ -0,0 +1,4 @@ +export const browser = true; +export const building = false; +export const dev = false; +export const version = 'test'; diff --git a/src/tests/job-list-error-display.test.ts b/src/tests/job-list-error-display.test.ts new file mode 100644 index 0000000..8fd12f5 --- /dev/null +++ b/src/tests/job-list-error-display.test.ts @@ -0,0 +1,86 @@ +import { describe, it, expect } from 'vitest'; +import { render } from 'svelte/server'; +import type { Job, AudioMode, JobStatus } from '$lib/types.js'; +import JobErrorDisplay from './__mocks__/JobErrorDisplay.svelte'; + +// Factory: minimal Job shape needed by the error display logic +function makeJob(overrides: Partial = {}): Job { + return { + id: 'test-job', + status: 'pending', + title: 'Test', + source: 'test.webm', + audioMode: 'auto', + error: null, + progress: 0, + segmentsJson: null, + createdAt: '2026-01-01T00:00:00.000Z', + updatedAt: '2026-01-01T00:00:00.000Z', + whisperJobId: null, + outputDir: null, + meanVolume: null, + retryCount: null, + nextRetryAt: null, + ...overrides, + }; +} + +describe('Job list error display', () => { + it('renders error text for a failed job with error message', () => { + const result = render(JobErrorDisplay, { + props: { + job: makeJob({ status: 'failed', error: 'simulated transcription failure' }), + }, + }); + + expect(result.body).toContain('simulated transcription failure'); + expect(result.body).toContain('recent-err-line'); + }); + + it('renders nothing when job status is done with null error', () => { + const result = render(JobErrorDisplay, { + props: { + job: makeJob({ status: 'done', error: null }), + }, + }); + + // Svelte SSR leaves comment markers for false conditionals. + // Verify no error-related classes or text are present. + expect(result.body).not.toContain('recent-err-line'); + expect(result.body).not.toContain('error'); + }); + + it('renders nothing when job status is failed but error is null', () => { + const result = render(JobErrorDisplay, { + props: { + job: makeJob({ status: 'failed', error: null }), + }, + }); + + expect(result.body).not.toContain('recent-err-line'); + expect(result.body).not.toContain('error'); + }); + + it('renders nothing when job has error but status is not failed', () => { + const result = render(JobErrorDisplay, { + props: { + job: makeJob({ status: 'pending', error: 'some error' }), + }, + }); + + expect(result.body).not.toContain('recent-err-line'); + expect(result.body).not.toContain('error'); + }); + + it('sets title attribute on error element to full error text', () => { + const errMsg = 'detailed error description for tooltip'; + const result = render(JobErrorDisplay, { + props: { + job: makeJob({ status: 'failed', error: errMsg }), + }, + }); + + // The title attribute should contain the full error text + expect(result.body).toContain(`title="${errMsg}"`); + }); +}); diff --git a/vitest.config.ts b/vitest.config.ts index bc3bd48..f5198f1 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -1,7 +1,9 @@ import { defineConfig } from 'vitest/config'; +import { svelte } from '@sveltejs/vite-plugin-svelte'; import path from 'path'; export default defineConfig({ + plugins: [svelte()], test: { environment: 'node', globals: true, @@ -15,7 +17,8 @@ export default defineConfig({ }, resolve: { alias: { - '$lib': path.resolve('./src/lib') + '$lib': path.resolve('./src/lib'), + '$app/environment': path.resolve('./src/tests/__mocks__/app-environment.ts'), } } });