story-ui-job-list-error: test error display on job list page

Add SSR-based component test verifying error display logic from
+page.svelte. Uses svelte/server render() to avoid mount/browser
requirements.

- JobErrorDisplay.svelte: test component replicating error display template
- app-environment.ts: mock for /environment (needed by accent.ts)
- job-list-error-display.test.ts: 5 tests covering:
  * failed job with error -> error text rendered
  * done job with null error -> no error text
  * failed job with null error -> no error text
  * non-failed status with error -> no error text
  * title attribute contains full error text
- vitest.config.ts: add svelte() plugin for .svelte file transforms,
  add /environment alias for test mocking

Tests: 237 passed (19 files), 0 type errors, build OK.
This commit is contained in:
Giancarmine Salucci
2026-07-15 23:57:11 +02:00
parent 117882ec17
commit c20f0d5f22
4 changed files with 107 additions and 1 deletions
@@ -0,0 +1,13 @@
<script lang="ts">
import type { Job } from '$lib/types.js';
interface Props {
job: Job;
}
let { job }: Props = $props();
</script>
{#if job.status === 'failed' && job.error}
<div class="recent-err-line" title={job.error}>{job.error}</div>
{/if}
+4
View File
@@ -0,0 +1,4 @@
export const browser = true;
export const building = false;
export const dev = false;
export const version = 'test';
+86
View File
@@ -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> = {}): 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}"`);
});
});
+4 -1
View File
@@ -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'),
}
}
});