feat(ui): refine live CV preview presentation
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import React from 'react';
|
||||
import '@testing-library/jest-dom';
|
||||
import { fireEvent, render, screen, waitFor, within } from '@testing-library/react';
|
||||
import { act, fireEvent, render, screen, waitFor, within } from '@testing-library/react';
|
||||
import { createMemoryRouter, RouterProvider } from 'react-router-dom';
|
||||
|
||||
import CvBuilderEditor from './views/CvBuilderEditor';
|
||||
@@ -655,12 +655,99 @@ test('preview failure is visible and retryable without leaving the editor', asyn
|
||||
renderAt(3);
|
||||
|
||||
expect(await screen.findByText('Preview unavailable')).toBeInTheDocument();
|
||||
expect(screen.getByRole('region', { name: 'Live preview' })).toHaveAttribute('aria-busy', 'false');
|
||||
expect(screen.queryByText('updating…')).not.toBeInTheDocument();
|
||||
fireEvent.click(screen.getByRole('button', { name: 'Retry preview' }));
|
||||
await waitFor(() => expect(screen.getByTitle('CV preview').getAttribute('srcdoc')).toContain('<p>retry</p>'));
|
||||
expect(screen.getByTitle('CV preview')).toHaveAttribute('sandbox', 'allow-same-origin allow-scripts');
|
||||
await waitFor(() => expect(screen.queryByText('Preview unavailable')).not.toBeInTheDocument());
|
||||
});
|
||||
|
||||
function settlePreview(count: number) {
|
||||
const frame = screen.getByTitle('CV preview') as HTMLIFrameElement;
|
||||
const doc = frame.contentDocument!;
|
||||
doc.body.innerHTML = '<div class="page">Measured document</div>';
|
||||
delete doc.documentElement.dataset.cvPreviewReady;
|
||||
fireEvent.load(frame);
|
||||
act(() => {
|
||||
doc.documentElement.dataset.cvPreviewPages = String(count);
|
||||
doc.documentElement.dataset.cvPreviewReady = 'true';
|
||||
frame.contentWindow!.dispatchEvent(new Event('cv-preview-ready'));
|
||||
});
|
||||
}
|
||||
|
||||
test('[P5-11 PRES-C06 E08] automatic fit attaches when the asynchronously loaded preview pane appears', async () => {
|
||||
const width = jest.spyOn(HTMLElement.prototype, 'clientWidth', 'get').mockImplementation(function (this: HTMLElement) {
|
||||
return this.getAttribute('aria-label') === 'Live preview' ? 400 : 0;
|
||||
});
|
||||
try {
|
||||
routeGet(() => Promise.resolve({ data: variant }));
|
||||
renderAt(3);
|
||||
await screen.findByDisplayValue('Backend CV');
|
||||
expect(await screen.findByText('Fit width · 48%')).toBeInTheDocument();
|
||||
} finally { width.mockRestore(); }
|
||||
});
|
||||
|
||||
test('[P5-11 PRES-C06 C12] page count is announced only after the renderer has settled', async () => {
|
||||
routeGet(() => Promise.resolve({ data: variant }));
|
||||
renderAt(3);
|
||||
const region = await screen.findByRole('region', { name: 'Live preview' });
|
||||
await waitFor(() => expect(screen.getByTitle('CV preview').getAttribute('srcdoc')).toContain('<p>cv</p>'));
|
||||
expect(region).toHaveAttribute('aria-busy', 'true');
|
||||
expect(screen.queryByText('1-page CV')).not.toBeInTheDocument();
|
||||
settlePreview(3);
|
||||
expect(region).toHaveAttribute('aria-busy', 'false');
|
||||
expect(screen.getByText('3-page CV')).toBeInTheDocument();
|
||||
expect(region.querySelectorAll('[data-cv-page]')).toHaveLength(3);
|
||||
});
|
||||
|
||||
test('[P5-11 PRES-C06 E09] overlapping preview successes and failures cannot replace the newest document', async () => {
|
||||
const pending: { resolve: (value: any) => void; reject: (reason: Error) => void }[] = [];
|
||||
mockedApi.post.mockImplementation(() => new Promise((resolve, reject) => { pending.push({ resolve, reject }); }));
|
||||
try {
|
||||
routeGet(() => Promise.resolve({ data: variant }));
|
||||
renderAt(3);
|
||||
await openContentTab();
|
||||
const headline = screen.getByLabelText('Headline override');
|
||||
const region = screen.getByRole('region', { name: 'Live preview' });
|
||||
await waitFor(() => expect(pending).toHaveLength(1));
|
||||
fireEvent.change(headline, { target: { value: 'Newer draft' } });
|
||||
await waitFor(() => expect(pending).toHaveLength(2));
|
||||
await act(async () => { pending[1].resolve({ data: { html: '<p>Newer document</p>' } }); });
|
||||
settlePreview(2);
|
||||
await act(async () => { pending[0].resolve({ data: { html: '<p>Stale document</p>' } }); });
|
||||
expect(screen.getByTitle('CV preview').getAttribute('srcdoc')).toContain('Newer document');
|
||||
expect(region).toHaveAttribute('aria-busy', 'false');
|
||||
fireEvent.change(headline, { target: { value: 'Intermediate draft' } });
|
||||
await waitFor(() => expect(pending).toHaveLength(3));
|
||||
fireEvent.change(headline, { target: { value: 'Newest draft' } });
|
||||
await waitFor(() => expect(pending).toHaveLength(4));
|
||||
await act(async () => { pending[3].resolve({ data: { html: '<p>Newest document</p>' } }); });
|
||||
settlePreview(4);
|
||||
await act(async () => { pending[2].reject(new Error('stale failure')); });
|
||||
expect(screen.getByTitle('CV preview').getAttribute('srcdoc')).toContain('Newest document');
|
||||
expect(screen.queryByText('Preview unavailable')).not.toBeInTheDocument();
|
||||
expect(region).toHaveAttribute('aria-busy', 'false');
|
||||
expect(screen.getByText('4-page CV')).toBeInTheDocument();
|
||||
expect(headline).toHaveValue('Newest draft');
|
||||
} finally { mockedApi.post.mockResolvedValue({ data: { html: '<p>cv</p>' } } as any); }
|
||||
});
|
||||
|
||||
test('[P5-11 PRES-C06 C10 E08] returning from preview focuses the existing editor without changing the pending draft', async () => {
|
||||
routeGet(() => Promise.resolve({ data: variant }));
|
||||
renderAt(3);
|
||||
await openContentTab();
|
||||
const headline = screen.getByLabelText('Headline override');
|
||||
fireEvent.change(headline, { target: { value: 'Retained preview draft' } });
|
||||
const editor = screen.getByRole('region', { name: 'CV editing controls' });
|
||||
editor.scrollIntoView = jest.fn();
|
||||
fireEvent.click(screen.getByRole('button', { name: 'Go to editor' }));
|
||||
await waitFor(() => expect(editor).toHaveFocus());
|
||||
expect(headline).toHaveValue('Retained preview draft');
|
||||
expect(screen.getByText('Unsaved')).toBeInTheDocument();
|
||||
expect(mockedApi.put).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
async function reviewDocumentSuggestion(result: string) {
|
||||
routeGet(() => Promise.resolve({ data: variant }), { headline: 'Canonical headline', sections: [] });
|
||||
mockedApi.post.mockImplementation((url: string) => Promise.resolve({ data: url === '/cv/ai/assist'
|
||||
|
||||
Reference in New Issue
Block a user