5180 fichiers 38.5 Mo 0 téléchargements 352 vues mis à jour il y a 7 heures ui
nitro-v36-with-renderer-v36 / src / components / catalog / admin / studio / CatalogStudioProvider.test.tsx
CatalogStudioProvider.test.tsx 8.3 Ko Télécharger le fichier
/* @vitest-environment jsdom */

import { act, cleanup, render, screen } from '@testing-library/react';
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
import { SendMessageComposer } from '../../../../api';
import { useConnectionState, useMessageEvent } from '../../../../hooks';
import { CatalogStudioProvider } from './CatalogStudioProvider';
import { useCatalogStudio } from './useCatalogStudio';

vi.mock('../../../../api', () => ({ SendMessageComposer: vi.fn() }));
vi.mock('../../../../hooks', () => ({ useConnectionState: vi.fn(), useMessageEvent: vi.fn() }));

const handlers = new Map<string, (event: any) => void>();
const pageSnapshot = (caption: string) => ({
    catalogType: 'NORMAL' as const, pageId: 42, parentId: -1, captionSave: 'page_42', caption,
    pageLayout: 'default_3x3', iconColor: 1, iconImage: 1, minRank: 1, orderNum: 1,
    visible: true, enabled: true, clubOnly: false, catalogMode: 'NORMAL', vipOnly: false,
    pageHeadline: '', pageTeaser: '', pageSpecial: '', pageText1: '', pageText2: '',
    pageTextDetails: '', pageTextTeaser: '', roomId: 0, includes: ''
});

const Probe = () => {
    const studio = useCatalogStudio();

    return (
        <div>
            <span data-testid="draft">{studio.session?.draftVersionId ?? 0}</span>
            <span data-testid="revision">{studio.revision}</span>
            <span data-testid="pending">{studio.pendingCount}</span>
            <span data-testid="error">{studio.lastError ?? ''}</span>
            <span data-testid="page-caption">{studio.session?.pages.find(page => page.pageId === 42)?.caption ?? ''}</span>
            <span data-testid="history-count">{studio.historyTotalCount}</span>
            <button onClick={() => studio.applyDocument('SQL', 'UPDATE catalog_pages SET caption = \'Shop\' WHERE id = 1;', 'fingerprint', 'Import catalog SQL file')}>apply</button>
            <button onClick={() => studio.applyMutation({
                operationId: 'save-page-1', action: 'savePage', revision: 8, entityType: 'PAGE', catalogType: 'NORMAL',
                entity: pageSnapshot('Renamed'),
                historyGroup: {
                    id: 91, revision: 8, actorId: 9, actorName: 'Alice', summary: 'Edit page', source: 'UI', createdAt: '',
                    entries: [ { entityType: 'PAGE', catalogType: 'NORMAL', entityId: 42, operation: 'UPDATE' } ]
                }
            })}>delta</button>
        </div>
    );
};

const emit = (eventName: string, parser: Record<string, unknown>) =>
    act(() => handlers.get(eventName)?.({ getParser: () => parser }));

describe('CatalogStudioProvider', () => {
    beforeEach(() => {
        handlers.clear();
        vi.mocked(SendMessageComposer).mockClear();
        vi.mocked(useConnectionState).mockReturnValue({
            phase: 'connected',
            reconnectAttempt: 0,
            maxReconnectAttempts: 7,
            authenticated: true,
            closeCode: null,
            closeReason: ''
        });
        vi.mocked(useMessageEvent).mockImplementation((eventType: any, handler: any) => {
            handlers.set(eventType.name, handler);
        });
    });

    afterEach(() => {
        cleanup();
        vi.useRealTimers();
    });

    it('opens and hydrates the shared server session', () => {
        render(<CatalogStudioProvider active><Probe /></CatalogStudioProvider>);

        expect(vi.mocked(SendMessageComposer).mock.calls[0][0].constructor.name).toBe('CatalogStudioOpenSessionComposer');

        emit('CatalogStudioSessionEvent', {
            activeVersionId: 11,
            draftVersionId: 12,
            revision: 7,
            activeUpdatedAt: '2026-08-02T10:00:00Z',
            draftCreatedAt: '2026-08-02T10:05:00Z',
            pendingCount: 3,
            actors: [ { id: 9, username: 'Alice' } ],
            validationCurrent: false,
            validationIssueCount: 0,
            publishedVersions: []
        });

        expect(screen.getByTestId('draft')).toHaveTextContent('12');
        expect(screen.getByTestId('revision')).toHaveTextContent('7');
        expect(screen.getByTestId('pending')).toHaveTextContent('3');
    });

    it('applies a mutation delta without requesting a broad session or history refresh', () => {
        render(<CatalogStudioProvider active><Probe /></CatalogStudioProvider>);
        emit('CatalogStudioSessionEvent', {
            activeVersionId: 11, draftVersionId: 12, revision: 7,
            activeUpdatedAt: '', draftCreatedAt: '', pendingCount: 3,
            actors: [], validationCurrent: true, validationIssueCount: 0, publishedVersions: [],
            pages: [ pageSnapshot('Original') ], offers: []
        });
        const callsBefore = vi.mocked(SendMessageComposer).mock.calls.length;

        act(() => screen.getByText('delta').click());

        expect(screen.getByTestId('revision')).toHaveTextContent('8');
        expect(screen.getByTestId('pending')).toHaveTextContent('4');
        expect(screen.getByTestId('page-caption')).toHaveTextContent('Renamed');
        expect(screen.getByTestId('history-count')).toHaveTextContent('1');
        expect(vi.mocked(SendMessageComposer).mock.calls).toHaveLength(callsBefore);
    });

    it('waits for authentication and opens a fresh session after reconnecting', () => {
        vi.mocked(useConnectionState).mockReturnValue({
            phase: 'connecting',
            reconnectAttempt: 0,
            maxReconnectAttempts: 7,
            authenticated: false,
            closeCode: null,
            closeReason: ''
        });
        const view = render(<CatalogStudioProvider active><Probe /></CatalogStudioProvider>);

        expect(vi.mocked(SendMessageComposer)).not.toHaveBeenCalled();

        vi.mocked(useConnectionState).mockReturnValue({
            phase: 'connected',
            reconnectAttempt: 0,
            maxReconnectAttempts: 7,
            authenticated: true,
            closeCode: null,
            closeReason: ''
        });
        view.rerender(<CatalogStudioProvider active><Probe /></CatalogStudioProvider>);

        expect(vi.mocked(SendMessageComposer).mock.calls
            .filter(([ composer ]) => composer.constructor.name === 'CatalogStudioOpenSessionComposer')).toHaveLength(1);

        emit('CatalogStudioSessionEvent', {
            activeVersionId: 11, draftVersionId: 12, revision: 7,
            activeUpdatedAt: '', draftCreatedAt: '', pendingCount: 0,
            actors: [], validationCurrent: false, validationIssueCount: 0, publishedVersions: []
        });
        expect(screen.getByTestId('draft')).toHaveTextContent('12');

        vi.mocked(useConnectionState).mockReturnValue({
            phase: 'reconnecting',
            reconnectAttempt: 1,
            maxReconnectAttempts: 7,
            authenticated: false,
            closeCode: null,
            closeReason: ''
        });
        view.rerender(<CatalogStudioProvider active><Probe /></CatalogStudioProvider>);
        expect(screen.getByTestId('draft')).toHaveTextContent('0');

        vi.mocked(useConnectionState).mockReturnValue({
            phase: 'connected',
            reconnectAttempt: 0,
            maxReconnectAttempts: 7,
            authenticated: true,
            closeCode: null,
            closeReason: ''
        });
        view.rerender(<CatalogStudioProvider active><Probe /></CatalogStudioProvider>);

        expect(vi.mocked(SendMessageComposer).mock.calls
            .filter(([ composer ]) => composer.constructor.name === 'CatalogStudioOpenSessionComposer')).toHaveLength(2);
    });

    it('applies a confirmed SQL dry-run directly without a separate root lock', () => {
        render(<CatalogStudioProvider active><Probe /></CatalogStudioProvider>);
        emit('CatalogStudioSessionEvent', {
            activeVersionId: 11, draftVersionId: 12, revision: 7,
            activeUpdatedAt: '', draftCreatedAt: '', pendingCount: 0,
            actors: [], validationCurrent: false, validationIssueCount: 0, publishedVersions: []
        });

        act(() => screen.getByText('apply').click());
        const apply = vi.mocked(SendMessageComposer).mock.calls.at(-1)[0] as any;
        expect(apply.constructor.name).toBe('CatalogStudioDocumentApplyComposer');
        expect(apply.getMessageArray().slice(1)).toEqual([
            12, 7, '', 'SQL', "UPDATE catalog_pages SET caption = 'Shop' WHERE id = 1;", 'fingerprint', 'Import catalog SQL file'
        ]);
    });

});
15Sujets
1Dépôts
20Membres
3En ligne
Profile
Inscrit
Dernière visite
Messages
0
Votes positifs
0
Votes négatifs
0

Nous utilisons des cookies pour assurer le bon fonctionnement du site et améliorer votre expérience. en savoir plus

Autoriser