diff --git a/src/__tests__/io.test.ts b/src/__tests__/io.test.ts new file mode 100644 index 0000000..a6434dc --- /dev/null +++ b/src/__tests__/io.test.ts @@ -0,0 +1,45 @@ +import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest' +import { pickJsonFile } from '../io' + +describe('pickJsonFile', () => { + const originalCreateElement = document.createElement.bind(document) + let createdInput: HTMLInputElement + + beforeEach(() => { + vi.spyOn(document, 'createElement').mockImplementation((tag: string) => { + const el = originalCreateElement(tag) + if (tag === 'input') { + createdInput = el as HTMLInputElement + vi.spyOn(createdInput, 'click').mockImplementation(() => {}) + } + return el + }) + }) + + afterEach(() => { + vi.restoreAllMocks() + }) + + it('resolves with file contents when a file is selected', async () => { + const promise = pickJsonFile() + + const file = new File(['{"hello":"world"}'], 'test.json', { + type: 'application/json', + }) + Object.defineProperty(createdInput, 'files', { + value: [file], + configurable: true, + }) + createdInput.dispatchEvent(new Event('change')) + + await expect(promise).resolves.toBe('{"hello":"world"}') + }) + + it('resolves with null when the user cancels the file picker', async () => { + const promise = pickJsonFile() + + createdInput.dispatchEvent(new Event('cancel')) + + await expect(promise).resolves.toBeNull() + }) +}) diff --git a/src/hooks/useShareImport.ts b/src/hooks/useShareImport.ts index 1dee8a5..0f6b5b6 100644 --- a/src/hooks/useShareImport.ts +++ b/src/hooks/useShareImport.ts @@ -133,6 +133,7 @@ export function useShareImport({ getFramework, navigate, addRaw, replace, addImp (onImported: (fw: Framework) => void) => { pickJsonFile() .then((text) => { + if (text === null) return const fw = JSON.parse(text) if (fw.name && fw.quadrants && fw.quadrants.length === 4) { const imported: Framework = { diff --git a/src/io.ts b/src/io.ts index 18e1b80..761e95a 100644 --- a/src/io.ts +++ b/src/io.ts @@ -8,7 +8,7 @@ export function downloadJson(filename: string, data: string): void { URL.revokeObjectURL(url) } -export function pickJsonFile(): Promise { +export function pickJsonFile(): Promise { return new Promise((resolve, reject) => { const input = document.createElement('input') input.type = 'file' @@ -24,6 +24,7 @@ export function pickJsonFile(): Promise { reader.onerror = () => reject(new Error('Failed to read file')) reader.readAsText(file) } + input.oncancel = () => resolve(null) input.click() }) }