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'),
}
}
});