102 lines
5.1 KiB
TypeScript
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()); }
|
|
});
|
|
});
|