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:
@@ -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}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
export const browser = true;
|
||||||
|
export const building = false;
|
||||||
|
export const dev = false;
|
||||||
|
export const version = 'test';
|
||||||
@@ -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
@@ -1,7 +1,9 @@
|
|||||||
import { defineConfig } from 'vitest/config';
|
import { defineConfig } from 'vitest/config';
|
||||||
|
import { svelte } from '@sveltejs/vite-plugin-svelte';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
|
plugins: [svelte()],
|
||||||
test: {
|
test: {
|
||||||
environment: 'node',
|
environment: 'node',
|
||||||
globals: true,
|
globals: true,
|
||||||
@@ -15,7 +17,8 @@ export default defineConfig({
|
|||||||
},
|
},
|
||||||
resolve: {
|
resolve: {
|
||||||
alias: {
|
alias: {
|
||||||
'$lib': path.resolve('./src/lib')
|
'$lib': path.resolve('./src/lib'),
|
||||||
|
'$app/environment': path.resolve('./src/tests/__mocks__/app-environment.ts'),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user