Assessor_2.0/tests/features/gpo-folder-import-diagnosti...

102 lines
5.1 KiB
TypeScript

// @vitest-environment jsdom
import { act } from 'react';
import { createRoot } from 'react-dom/client';
import { afterEach, describe, expect, it, vi } from 'vitest';
import { AssessorApiClient } from '../../src/api/assessor-api-client';
import { GpoFolderImport } from '../../src/features/project-import/GpoFolderImport';
afterEach(() => { vi.restoreAllMocks(); vi.unstubAllGlobals(); });
const privateName = 'Synthetic confidential customer';
const error = (name: string) => new DOMException(`C:/Customers/${privateName}/backup.xml`, name);
describe.each(['dark', 'light'])('GPO folder click diagnostics (%s)', theme => {
it.each([
['enumeration', 'Folder enumeration: NotFoundError (1)'],
['access', 'File access: NotAllowedError (1)'],
['reading', 'File reading: NotReadableError (1)'],
['size', 'File reading: SizeMismatch (1)'],
['unknown', 'File reading: UnknownError (1)'],
['empty', 'contains no files'],
['selection', 'Folder selection: SecurityError (1)'],
])('reports %s without exposing customer data or uploading', async (scenario, expected) => {
vi.stubGlobal('IS_REACT_ACT_ENVIRONMENT', true);
const submit = vi.spyOn(AssessorApiClient.prototype, 'submitGpoBackup');
const picker = vi.fn(async () => {
if (scenario === 'selection') throw error('SecurityError');
return {
kind: 'directory', name: privateName,
async *values() {
if (scenario === 'enumeration') throw error('NotFoundError');
if (scenario === 'empty') return;
yield { kind: 'file', name: 'confidential.xml', getFile: async () => {
if (scenario === 'access') throw error('NotAllowedError');
return { size: 1, arrayBuffer: async () => {
if (scenario === 'size') return new ArrayBuffer(0);
throw error(scenario === 'unknown' ? privateName : 'NotReadableError');
} };
} };
}
};
});
vi.stubGlobal('showDirectoryPicker', picker);
const container = document.createElement('div');
container.setAttribute('data-theme', theme);
const root = createRoot(container);
try {
await act(async () => root.render(<GpoFolderImport projectId="synthetic-project" assessmentId="synthetic-slot" showStatus={false}/>));
const button = Array.from(container.querySelectorAll('button')).find(item => item.textContent === 'Import GPO Folder')!;
await act(async () => button.click());
expect(picker).toHaveBeenCalledWith({ mode: 'read' });
const message = container.querySelector('[role="alert"]')?.textContent;
expect(message).toContain(expected);
expect(message).toContain('Import GPO ZIP');
expect(message).not.toContain(privateName);
expect(message).not.toContain('confidential.xml');
expect(message).not.toContain('C:/Customers');
expect(submit).not.toHaveBeenCalled();
expect(button.disabled).toBe(false);
} finally { await act(async () => root.unmount()); }
});
it('cancels folder selection quietly and permits another attempt', async () => {
vi.stubGlobal('IS_REACT_ACT_ENVIRONMENT', true);
const submit = vi.spyOn(AssessorApiClient.prototype, 'submitGpoBackup');
vi.stubGlobal('showDirectoryPicker', vi.fn().mockRejectedValue(error('AbortError')));
const container = document.createElement('div');
const root = createRoot(container);
try {
await act(async () => root.render(<GpoFolderImport projectId="synthetic-project" showStatus={false}/>));
const button = container.querySelector('button')!;
await act(async () => button.click());
expect(container.querySelector('[role="alert"]')).toBeNull();
expect(button.disabled).toBe(false);
expect(submit).not.toHaveBeenCalled();
} finally { await act(async () => root.unmount()); }
});
it('reports unreadable files through the directory-input fallback', async () => {
vi.stubGlobal('IS_REACT_ACT_ENVIRONMENT', true);
vi.stubGlobal('showDirectoryPicker', undefined);
const submit = vi.spyOn(AssessorApiClient.prototype, 'submitGpoBackup');
const container = document.createElement('div');
const root = createRoot(container);
try {
await act(async () => root.render(<GpoFolderImport projectId="synthetic-project" showStatus={false}/>));
const input = container.querySelector('input[type="file"]')!;
const open = vi.spyOn(input as HTMLInputElement, 'click');
await act(async () => container.querySelector('button')!.click());
expect(open).toHaveBeenCalledOnce();
Object.defineProperty(input, 'files', { value: [{
webkitRelativePath: 'Synthetic/backup.xml', size: 1,
arrayBuffer: async () => { throw error('NotFoundError'); }
}] });
await act(async () => input.dispatchEvent(new Event('change', { bubbles: true })));
const message = container.querySelector('[role="alert"]')?.textContent;
expect(message).toContain('File reading: NotFoundError (1)');
expect(message).not.toContain(privateName);
expect(submit).not.toHaveBeenCalled();
} finally { await act(async () => root.unmount()); }
});
});