@shipstatic/drop 0.2.0 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +64 -0
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +5 -167
- package/dist/index.d.ts +5 -167
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/testing.cjs +149 -0
- package/dist/testing.cjs.map +1 -0
- package/dist/testing.d.cts +100 -0
- package/dist/testing.d.ts +100 -0
- package/dist/testing.js +140 -0
- package/dist/testing.js.map +1 -0
- package/dist/useDrop-CRUVkVzW.d.cts +168 -0
- package/dist/useDrop-CRUVkVzW.d.ts +168 -0
- package/package.json +14 -4
package/dist/testing.cjs
ADDED
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// src/testing.ts
|
|
4
|
+
function createMockDrop(options = {}) {
|
|
5
|
+
const {
|
|
6
|
+
phase = "idle",
|
|
7
|
+
files = [],
|
|
8
|
+
sourceName = "",
|
|
9
|
+
status = null
|
|
10
|
+
} = options;
|
|
11
|
+
const validFiles = files.filter((f) => f.status === "ready");
|
|
12
|
+
return {
|
|
13
|
+
// State
|
|
14
|
+
phase,
|
|
15
|
+
isProcessing: phase === "processing",
|
|
16
|
+
isDragging: phase === "dragging",
|
|
17
|
+
isInteractive: phase === "idle" || phase === "dragging" || phase === "ready",
|
|
18
|
+
hasError: phase === "error",
|
|
19
|
+
files,
|
|
20
|
+
validFiles,
|
|
21
|
+
sourceName,
|
|
22
|
+
status,
|
|
23
|
+
// Prop getters - return minimal objects for spreading
|
|
24
|
+
getDropzoneProps: (opts) => ({
|
|
25
|
+
onDragOver: () => {
|
|
26
|
+
},
|
|
27
|
+
onDragLeave: () => {
|
|
28
|
+
},
|
|
29
|
+
onDrop: () => {
|
|
30
|
+
},
|
|
31
|
+
...opts?.clickable !== false && { onClick: () => {
|
|
32
|
+
} }
|
|
33
|
+
}),
|
|
34
|
+
getInputProps: () => ({
|
|
35
|
+
ref: { current: null },
|
|
36
|
+
type: "file",
|
|
37
|
+
style: { display: "none" },
|
|
38
|
+
multiple: true,
|
|
39
|
+
webkitdirectory: "",
|
|
40
|
+
onChange: () => {
|
|
41
|
+
}
|
|
42
|
+
}),
|
|
43
|
+
// Actions - no-op by default, can be spied on
|
|
44
|
+
open: () => {
|
|
45
|
+
},
|
|
46
|
+
processFiles: async () => {
|
|
47
|
+
},
|
|
48
|
+
reset: () => {
|
|
49
|
+
},
|
|
50
|
+
// Helpers
|
|
51
|
+
getFilesForUpload: () => validFiles.map((f) => f.file)
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
function createMockDropWithSpies(options = {}) {
|
|
55
|
+
const baseDrop = createMockDrop(options);
|
|
56
|
+
let openCalls = 0;
|
|
57
|
+
let processFilesCalls = [];
|
|
58
|
+
let resetCalls = 0;
|
|
59
|
+
const trackedSpies = {
|
|
60
|
+
open: Object.assign(() => {
|
|
61
|
+
openCalls++;
|
|
62
|
+
}, {
|
|
63
|
+
calls: () => openCalls,
|
|
64
|
+
toHaveBeenCalled: () => openCalls > 0
|
|
65
|
+
}),
|
|
66
|
+
processFiles: Object.assign(async (files) => {
|
|
67
|
+
processFilesCalls.push(files);
|
|
68
|
+
}, {
|
|
69
|
+
calls: () => processFilesCalls,
|
|
70
|
+
toHaveBeenCalled: () => processFilesCalls.length > 0,
|
|
71
|
+
toHaveBeenCalledWith: (files) => processFilesCalls.some((c) => c === files)
|
|
72
|
+
}),
|
|
73
|
+
reset: Object.assign(() => {
|
|
74
|
+
resetCalls++;
|
|
75
|
+
}, {
|
|
76
|
+
calls: () => resetCalls,
|
|
77
|
+
toHaveBeenCalled: () => resetCalls > 0
|
|
78
|
+
}),
|
|
79
|
+
getFilesForUpload: baseDrop.getFilesForUpload
|
|
80
|
+
};
|
|
81
|
+
return {
|
|
82
|
+
drop: {
|
|
83
|
+
...baseDrop,
|
|
84
|
+
open: trackedSpies.open,
|
|
85
|
+
processFiles: trackedSpies.processFiles,
|
|
86
|
+
reset: trackedSpies.reset,
|
|
87
|
+
getFilesForUpload: trackedSpies.getFilesForUpload
|
|
88
|
+
},
|
|
89
|
+
spies: trackedSpies
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
var mockFileIdCounter = 0;
|
|
93
|
+
function createMockProcessedFile(name, options = {}) {
|
|
94
|
+
const {
|
|
95
|
+
path = name,
|
|
96
|
+
content = "test content",
|
|
97
|
+
type = "text/plain",
|
|
98
|
+
status = "ready",
|
|
99
|
+
statusMessage
|
|
100
|
+
} = options;
|
|
101
|
+
const file = new File([content], name, { type });
|
|
102
|
+
return {
|
|
103
|
+
id: `mock-file-${++mockFileIdCounter}`,
|
|
104
|
+
file,
|
|
105
|
+
path,
|
|
106
|
+
name,
|
|
107
|
+
size: file.size,
|
|
108
|
+
type,
|
|
109
|
+
lastModified: Date.now(),
|
|
110
|
+
status,
|
|
111
|
+
statusMessage
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
function createMockFile(name, content = "test content", type = "text/plain") {
|
|
115
|
+
return new File([content], name, { type, lastModified: Date.now() });
|
|
116
|
+
}
|
|
117
|
+
function createMockFileWithPath(name, webkitRelativePath, content = "test content", type = "text/plain") {
|
|
118
|
+
const file = createMockFile(name, content, type);
|
|
119
|
+
Object.defineProperty(file, "webkitRelativePath", {
|
|
120
|
+
value: webkitRelativePath,
|
|
121
|
+
writable: false,
|
|
122
|
+
enumerable: true,
|
|
123
|
+
configurable: true
|
|
124
|
+
});
|
|
125
|
+
return file;
|
|
126
|
+
}
|
|
127
|
+
function createMockErrorStatus(title = "Validation Failed", details = "One or more files failed validation", errors = []) {
|
|
128
|
+
return { title, details, errors };
|
|
129
|
+
}
|
|
130
|
+
function createMockProcessingStatus(title = "Processing...", details = "Validating and preparing files.") {
|
|
131
|
+
return { title, details };
|
|
132
|
+
}
|
|
133
|
+
function createMockReadyStatus(fileCount) {
|
|
134
|
+
return {
|
|
135
|
+
title: "Ready",
|
|
136
|
+
details: `${fileCount} file(s) are ready.`
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
exports.createMockDrop = createMockDrop;
|
|
141
|
+
exports.createMockDropWithSpies = createMockDropWithSpies;
|
|
142
|
+
exports.createMockErrorStatus = createMockErrorStatus;
|
|
143
|
+
exports.createMockFile = createMockFile;
|
|
144
|
+
exports.createMockFileWithPath = createMockFileWithPath;
|
|
145
|
+
exports.createMockProcessedFile = createMockProcessedFile;
|
|
146
|
+
exports.createMockProcessingStatus = createMockProcessingStatus;
|
|
147
|
+
exports.createMockReadyStatus = createMockReadyStatus;
|
|
148
|
+
//# sourceMappingURL=testing.cjs.map
|
|
149
|
+
//# sourceMappingURL=testing.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/testing.ts"],"names":[],"mappings":"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","file":"testing.cjs","sourcesContent":["/**\n * Test utilities for @shipstatic/drop\n *\n * Import from '@shipstatic/drop/testing' in your test files:\n *\n * ```typescript\n * import { createMockDrop, createMockFile } from '@shipstatic/drop/testing';\n * ```\n */\n\nimport type { ProcessedFile, DropStatus, DropStateValue } from './types';\nimport type { DropReturn, DropzonePropsOptions } from './hooks/useDrop';\n\n// ============================================================================\n// Mock Drop Hook Return\n// ============================================================================\n\n/**\n * Options for creating a mock drop return value\n */\nexport interface MockDropOptions {\n phase?: DropStateValue;\n files?: ProcessedFile[];\n sourceName?: string;\n status?: DropStatus | null;\n}\n\n/**\n * Creates a mock DropReturn for testing components that receive drop as a prop\n *\n * @example\n * ```tsx\n * import { createMockDrop, createMockProcessedFile } from '@shipstatic/drop/testing';\n *\n * it('renders file count when files are ready', () => {\n * const drop = createMockDrop({\n * phase: 'ready',\n * files: [createMockProcessedFile('index.html')],\n * });\n *\n * render(<DeployDropArea drop={drop} />);\n * expect(screen.getByText('1 files ready')).toBeInTheDocument();\n * });\n * ```\n */\nexport function createMockDrop(options: MockDropOptions = {}): DropReturn {\n const {\n phase = 'idle',\n files = [],\n sourceName = '',\n status = null,\n } = options;\n\n const validFiles = files.filter(f => f.status === 'ready');\n\n return {\n // State\n phase,\n isProcessing: phase === 'processing',\n isDragging: phase === 'dragging',\n isInteractive: phase === 'idle' || phase === 'dragging' || phase === 'ready',\n hasError: phase === 'error',\n files,\n validFiles,\n sourceName,\n status,\n\n // Prop getters - return minimal objects for spreading\n getDropzoneProps: (opts?: DropzonePropsOptions) => ({\n onDragOver: () => {},\n onDragLeave: () => {},\n onDrop: () => {},\n ...(opts?.clickable !== false && { onClick: () => {} }),\n }),\n getInputProps: () => ({\n ref: { current: null },\n type: 'file' as const,\n style: { display: 'none' },\n multiple: true,\n webkitdirectory: '',\n onChange: () => {},\n }),\n\n // Actions - no-op by default, can be spied on\n open: () => {},\n processFiles: async () => {},\n reset: () => {},\n\n // Helpers\n getFilesForUpload: () => validFiles.map(f => f.file),\n };\n}\n\n/**\n * Creates a mock drop with spy functions for testing interactions\n *\n * @example\n * ```tsx\n * import { createMockDropWithSpies } from '@shipstatic/drop/testing';\n *\n * it('calls reset when Clear button is clicked', async () => {\n * const { drop, spies } = createMockDropWithSpies({ phase: 'ready', files: [...] });\n *\n * render(<DeployDropArea drop={drop} />);\n * await userEvent.click(screen.getByText('Clear'));\n *\n * expect(spies.reset).toHaveBeenCalled();\n * });\n * ```\n */\nexport function createMockDropWithSpies(options: MockDropOptions = {}): {\n drop: DropReturn;\n spies: {\n open: () => void;\n processFiles: (files: File[]) => Promise<void>;\n reset: () => void;\n getFilesForUpload: () => File[];\n };\n} {\n const baseDrop = createMockDrop(options);\n\n const spies = {\n open: createNoopSpy(),\n processFiles: createAsyncNoopSpy(),\n reset: createNoopSpy(),\n getFilesForUpload: (() => baseDrop.getFilesForUpload()) as () => File[],\n };\n\n // Track calls manually (works without vitest in runtime)\n let openCalls = 0;\n let processFilesCalls: File[][] = [];\n let resetCalls = 0;\n\n const trackedSpies = {\n open: Object.assign(() => { openCalls++; }, {\n calls: () => openCalls,\n toHaveBeenCalled: () => openCalls > 0,\n }),\n processFiles: Object.assign(async (files: File[]) => { processFilesCalls.push(files); }, {\n calls: () => processFilesCalls,\n toHaveBeenCalled: () => processFilesCalls.length > 0,\n toHaveBeenCalledWith: (files: File[]) => processFilesCalls.some(c => c === files),\n }),\n reset: Object.assign(() => { resetCalls++; }, {\n calls: () => resetCalls,\n toHaveBeenCalled: () => resetCalls > 0,\n }),\n getFilesForUpload: baseDrop.getFilesForUpload,\n };\n\n return {\n drop: {\n ...baseDrop,\n open: trackedSpies.open,\n processFiles: trackedSpies.processFiles,\n reset: trackedSpies.reset,\n getFilesForUpload: trackedSpies.getFilesForUpload,\n },\n spies: trackedSpies,\n };\n}\n\n// ============================================================================\n// Mock File Utilities\n// ============================================================================\n\nlet mockFileIdCounter = 0;\n\n/**\n * Creates a mock ProcessedFile for testing\n */\nexport function createMockProcessedFile(\n name: string,\n options: {\n path?: string;\n content?: string;\n type?: string;\n status?: 'ready' | 'validation_failed' | 'processing_error' | 'empty_file';\n statusMessage?: string;\n } = {}\n): ProcessedFile {\n const {\n path = name,\n content = 'test content',\n type = 'text/plain',\n status = 'ready',\n statusMessage,\n } = options;\n\n const file = new File([content], name, { type });\n\n return {\n id: `mock-file-${++mockFileIdCounter}`,\n file,\n path,\n name,\n size: file.size,\n type,\n lastModified: Date.now(),\n status,\n statusMessage,\n };\n}\n\n/**\n * Creates a mock File object\n */\nexport function createMockFile(\n name: string,\n content: string = 'test content',\n type: string = 'text/plain'\n): File {\n return new File([content], name, { type, lastModified: Date.now() });\n}\n\n/**\n * Creates a mock File object with webkitRelativePath set\n */\nexport function createMockFileWithPath(\n name: string,\n webkitRelativePath: string,\n content: string = 'test content',\n type: string = 'text/plain'\n): File {\n const file = createMockFile(name, content, type);\n Object.defineProperty(file, 'webkitRelativePath', {\n value: webkitRelativePath,\n writable: false,\n enumerable: true,\n configurable: true,\n });\n return file;\n}\n\n// ============================================================================\n// Mock Status Utilities\n// ============================================================================\n\n/**\n * Creates a mock error status\n */\nexport function createMockErrorStatus(\n title: string = 'Validation Failed',\n details: string = 'One or more files failed validation',\n errors: string[] = []\n): DropStatus {\n return { title, details, errors };\n}\n\n/**\n * Creates a mock processing status\n */\nexport function createMockProcessingStatus(\n title: string = 'Processing...',\n details: string = 'Validating and preparing files.'\n): DropStatus {\n return { title, details };\n}\n\n/**\n * Creates a mock ready status\n */\nexport function createMockReadyStatus(fileCount: number): DropStatus {\n return {\n title: 'Ready',\n details: `${fileCount} file(s) are ready.`,\n };\n}\n\n// ============================================================================\n// Internal Helpers\n// ============================================================================\n\nfunction createNoopSpy(): () => void {\n return () => {};\n}\n\nfunction createAsyncNoopSpy(): () => Promise<void> {\n return async () => {};\n}\n"]}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { c as DropStateValue, P as ProcessedFile, d as DropStatus, a as DropReturn } from './useDrop-CRUVkVzW.cjs';
|
|
2
|
+
import '@shipstatic/types';
|
|
3
|
+
import '@shipstatic/ship';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Test utilities for @shipstatic/drop
|
|
7
|
+
*
|
|
8
|
+
* Import from '@shipstatic/drop/testing' in your test files:
|
|
9
|
+
*
|
|
10
|
+
* ```typescript
|
|
11
|
+
* import { createMockDrop, createMockFile } from '@shipstatic/drop/testing';
|
|
12
|
+
* ```
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Options for creating a mock drop return value
|
|
17
|
+
*/
|
|
18
|
+
interface MockDropOptions {
|
|
19
|
+
phase?: DropStateValue;
|
|
20
|
+
files?: ProcessedFile[];
|
|
21
|
+
sourceName?: string;
|
|
22
|
+
status?: DropStatus | null;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Creates a mock DropReturn for testing components that receive drop as a prop
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* ```tsx
|
|
29
|
+
* import { createMockDrop, createMockProcessedFile } from '@shipstatic/drop/testing';
|
|
30
|
+
*
|
|
31
|
+
* it('renders file count when files are ready', () => {
|
|
32
|
+
* const drop = createMockDrop({
|
|
33
|
+
* phase: 'ready',
|
|
34
|
+
* files: [createMockProcessedFile('index.html')],
|
|
35
|
+
* });
|
|
36
|
+
*
|
|
37
|
+
* render(<DeployDropArea drop={drop} />);
|
|
38
|
+
* expect(screen.getByText('1 files ready')).toBeInTheDocument();
|
|
39
|
+
* });
|
|
40
|
+
* ```
|
|
41
|
+
*/
|
|
42
|
+
declare function createMockDrop(options?: MockDropOptions): DropReturn;
|
|
43
|
+
/**
|
|
44
|
+
* Creates a mock drop with spy functions for testing interactions
|
|
45
|
+
*
|
|
46
|
+
* @example
|
|
47
|
+
* ```tsx
|
|
48
|
+
* import { createMockDropWithSpies } from '@shipstatic/drop/testing';
|
|
49
|
+
*
|
|
50
|
+
* it('calls reset when Clear button is clicked', async () => {
|
|
51
|
+
* const { drop, spies } = createMockDropWithSpies({ phase: 'ready', files: [...] });
|
|
52
|
+
*
|
|
53
|
+
* render(<DeployDropArea drop={drop} />);
|
|
54
|
+
* await userEvent.click(screen.getByText('Clear'));
|
|
55
|
+
*
|
|
56
|
+
* expect(spies.reset).toHaveBeenCalled();
|
|
57
|
+
* });
|
|
58
|
+
* ```
|
|
59
|
+
*/
|
|
60
|
+
declare function createMockDropWithSpies(options?: MockDropOptions): {
|
|
61
|
+
drop: DropReturn;
|
|
62
|
+
spies: {
|
|
63
|
+
open: () => void;
|
|
64
|
+
processFiles: (files: File[]) => Promise<void>;
|
|
65
|
+
reset: () => void;
|
|
66
|
+
getFilesForUpload: () => File[];
|
|
67
|
+
};
|
|
68
|
+
};
|
|
69
|
+
/**
|
|
70
|
+
* Creates a mock ProcessedFile for testing
|
|
71
|
+
*/
|
|
72
|
+
declare function createMockProcessedFile(name: string, options?: {
|
|
73
|
+
path?: string;
|
|
74
|
+
content?: string;
|
|
75
|
+
type?: string;
|
|
76
|
+
status?: 'ready' | 'validation_failed' | 'processing_error' | 'empty_file';
|
|
77
|
+
statusMessage?: string;
|
|
78
|
+
}): ProcessedFile;
|
|
79
|
+
/**
|
|
80
|
+
* Creates a mock File object
|
|
81
|
+
*/
|
|
82
|
+
declare function createMockFile(name: string, content?: string, type?: string): File;
|
|
83
|
+
/**
|
|
84
|
+
* Creates a mock File object with webkitRelativePath set
|
|
85
|
+
*/
|
|
86
|
+
declare function createMockFileWithPath(name: string, webkitRelativePath: string, content?: string, type?: string): File;
|
|
87
|
+
/**
|
|
88
|
+
* Creates a mock error status
|
|
89
|
+
*/
|
|
90
|
+
declare function createMockErrorStatus(title?: string, details?: string, errors?: string[]): DropStatus;
|
|
91
|
+
/**
|
|
92
|
+
* Creates a mock processing status
|
|
93
|
+
*/
|
|
94
|
+
declare function createMockProcessingStatus(title?: string, details?: string): DropStatus;
|
|
95
|
+
/**
|
|
96
|
+
* Creates a mock ready status
|
|
97
|
+
*/
|
|
98
|
+
declare function createMockReadyStatus(fileCount: number): DropStatus;
|
|
99
|
+
|
|
100
|
+
export { type MockDropOptions, createMockDrop, createMockDropWithSpies, createMockErrorStatus, createMockFile, createMockFileWithPath, createMockProcessedFile, createMockProcessingStatus, createMockReadyStatus };
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { c as DropStateValue, P as ProcessedFile, d as DropStatus, a as DropReturn } from './useDrop-CRUVkVzW.js';
|
|
2
|
+
import '@shipstatic/types';
|
|
3
|
+
import '@shipstatic/ship';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Test utilities for @shipstatic/drop
|
|
7
|
+
*
|
|
8
|
+
* Import from '@shipstatic/drop/testing' in your test files:
|
|
9
|
+
*
|
|
10
|
+
* ```typescript
|
|
11
|
+
* import { createMockDrop, createMockFile } from '@shipstatic/drop/testing';
|
|
12
|
+
* ```
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Options for creating a mock drop return value
|
|
17
|
+
*/
|
|
18
|
+
interface MockDropOptions {
|
|
19
|
+
phase?: DropStateValue;
|
|
20
|
+
files?: ProcessedFile[];
|
|
21
|
+
sourceName?: string;
|
|
22
|
+
status?: DropStatus | null;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Creates a mock DropReturn for testing components that receive drop as a prop
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* ```tsx
|
|
29
|
+
* import { createMockDrop, createMockProcessedFile } from '@shipstatic/drop/testing';
|
|
30
|
+
*
|
|
31
|
+
* it('renders file count when files are ready', () => {
|
|
32
|
+
* const drop = createMockDrop({
|
|
33
|
+
* phase: 'ready',
|
|
34
|
+
* files: [createMockProcessedFile('index.html')],
|
|
35
|
+
* });
|
|
36
|
+
*
|
|
37
|
+
* render(<DeployDropArea drop={drop} />);
|
|
38
|
+
* expect(screen.getByText('1 files ready')).toBeInTheDocument();
|
|
39
|
+
* });
|
|
40
|
+
* ```
|
|
41
|
+
*/
|
|
42
|
+
declare function createMockDrop(options?: MockDropOptions): DropReturn;
|
|
43
|
+
/**
|
|
44
|
+
* Creates a mock drop with spy functions for testing interactions
|
|
45
|
+
*
|
|
46
|
+
* @example
|
|
47
|
+
* ```tsx
|
|
48
|
+
* import { createMockDropWithSpies } from '@shipstatic/drop/testing';
|
|
49
|
+
*
|
|
50
|
+
* it('calls reset when Clear button is clicked', async () => {
|
|
51
|
+
* const { drop, spies } = createMockDropWithSpies({ phase: 'ready', files: [...] });
|
|
52
|
+
*
|
|
53
|
+
* render(<DeployDropArea drop={drop} />);
|
|
54
|
+
* await userEvent.click(screen.getByText('Clear'));
|
|
55
|
+
*
|
|
56
|
+
* expect(spies.reset).toHaveBeenCalled();
|
|
57
|
+
* });
|
|
58
|
+
* ```
|
|
59
|
+
*/
|
|
60
|
+
declare function createMockDropWithSpies(options?: MockDropOptions): {
|
|
61
|
+
drop: DropReturn;
|
|
62
|
+
spies: {
|
|
63
|
+
open: () => void;
|
|
64
|
+
processFiles: (files: File[]) => Promise<void>;
|
|
65
|
+
reset: () => void;
|
|
66
|
+
getFilesForUpload: () => File[];
|
|
67
|
+
};
|
|
68
|
+
};
|
|
69
|
+
/**
|
|
70
|
+
* Creates a mock ProcessedFile for testing
|
|
71
|
+
*/
|
|
72
|
+
declare function createMockProcessedFile(name: string, options?: {
|
|
73
|
+
path?: string;
|
|
74
|
+
content?: string;
|
|
75
|
+
type?: string;
|
|
76
|
+
status?: 'ready' | 'validation_failed' | 'processing_error' | 'empty_file';
|
|
77
|
+
statusMessage?: string;
|
|
78
|
+
}): ProcessedFile;
|
|
79
|
+
/**
|
|
80
|
+
* Creates a mock File object
|
|
81
|
+
*/
|
|
82
|
+
declare function createMockFile(name: string, content?: string, type?: string): File;
|
|
83
|
+
/**
|
|
84
|
+
* Creates a mock File object with webkitRelativePath set
|
|
85
|
+
*/
|
|
86
|
+
declare function createMockFileWithPath(name: string, webkitRelativePath: string, content?: string, type?: string): File;
|
|
87
|
+
/**
|
|
88
|
+
* Creates a mock error status
|
|
89
|
+
*/
|
|
90
|
+
declare function createMockErrorStatus(title?: string, details?: string, errors?: string[]): DropStatus;
|
|
91
|
+
/**
|
|
92
|
+
* Creates a mock processing status
|
|
93
|
+
*/
|
|
94
|
+
declare function createMockProcessingStatus(title?: string, details?: string): DropStatus;
|
|
95
|
+
/**
|
|
96
|
+
* Creates a mock ready status
|
|
97
|
+
*/
|
|
98
|
+
declare function createMockReadyStatus(fileCount: number): DropStatus;
|
|
99
|
+
|
|
100
|
+
export { type MockDropOptions, createMockDrop, createMockDropWithSpies, createMockErrorStatus, createMockFile, createMockFileWithPath, createMockProcessedFile, createMockProcessingStatus, createMockReadyStatus };
|
package/dist/testing.js
ADDED
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
// src/testing.ts
|
|
2
|
+
function createMockDrop(options = {}) {
|
|
3
|
+
const {
|
|
4
|
+
phase = "idle",
|
|
5
|
+
files = [],
|
|
6
|
+
sourceName = "",
|
|
7
|
+
status = null
|
|
8
|
+
} = options;
|
|
9
|
+
const validFiles = files.filter((f) => f.status === "ready");
|
|
10
|
+
return {
|
|
11
|
+
// State
|
|
12
|
+
phase,
|
|
13
|
+
isProcessing: phase === "processing",
|
|
14
|
+
isDragging: phase === "dragging",
|
|
15
|
+
isInteractive: phase === "idle" || phase === "dragging" || phase === "ready",
|
|
16
|
+
hasError: phase === "error",
|
|
17
|
+
files,
|
|
18
|
+
validFiles,
|
|
19
|
+
sourceName,
|
|
20
|
+
status,
|
|
21
|
+
// Prop getters - return minimal objects for spreading
|
|
22
|
+
getDropzoneProps: (opts) => ({
|
|
23
|
+
onDragOver: () => {
|
|
24
|
+
},
|
|
25
|
+
onDragLeave: () => {
|
|
26
|
+
},
|
|
27
|
+
onDrop: () => {
|
|
28
|
+
},
|
|
29
|
+
...opts?.clickable !== false && { onClick: () => {
|
|
30
|
+
} }
|
|
31
|
+
}),
|
|
32
|
+
getInputProps: () => ({
|
|
33
|
+
ref: { current: null },
|
|
34
|
+
type: "file",
|
|
35
|
+
style: { display: "none" },
|
|
36
|
+
multiple: true,
|
|
37
|
+
webkitdirectory: "",
|
|
38
|
+
onChange: () => {
|
|
39
|
+
}
|
|
40
|
+
}),
|
|
41
|
+
// Actions - no-op by default, can be spied on
|
|
42
|
+
open: () => {
|
|
43
|
+
},
|
|
44
|
+
processFiles: async () => {
|
|
45
|
+
},
|
|
46
|
+
reset: () => {
|
|
47
|
+
},
|
|
48
|
+
// Helpers
|
|
49
|
+
getFilesForUpload: () => validFiles.map((f) => f.file)
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
function createMockDropWithSpies(options = {}) {
|
|
53
|
+
const baseDrop = createMockDrop(options);
|
|
54
|
+
let openCalls = 0;
|
|
55
|
+
let processFilesCalls = [];
|
|
56
|
+
let resetCalls = 0;
|
|
57
|
+
const trackedSpies = {
|
|
58
|
+
open: Object.assign(() => {
|
|
59
|
+
openCalls++;
|
|
60
|
+
}, {
|
|
61
|
+
calls: () => openCalls,
|
|
62
|
+
toHaveBeenCalled: () => openCalls > 0
|
|
63
|
+
}),
|
|
64
|
+
processFiles: Object.assign(async (files) => {
|
|
65
|
+
processFilesCalls.push(files);
|
|
66
|
+
}, {
|
|
67
|
+
calls: () => processFilesCalls,
|
|
68
|
+
toHaveBeenCalled: () => processFilesCalls.length > 0,
|
|
69
|
+
toHaveBeenCalledWith: (files) => processFilesCalls.some((c) => c === files)
|
|
70
|
+
}),
|
|
71
|
+
reset: Object.assign(() => {
|
|
72
|
+
resetCalls++;
|
|
73
|
+
}, {
|
|
74
|
+
calls: () => resetCalls,
|
|
75
|
+
toHaveBeenCalled: () => resetCalls > 0
|
|
76
|
+
}),
|
|
77
|
+
getFilesForUpload: baseDrop.getFilesForUpload
|
|
78
|
+
};
|
|
79
|
+
return {
|
|
80
|
+
drop: {
|
|
81
|
+
...baseDrop,
|
|
82
|
+
open: trackedSpies.open,
|
|
83
|
+
processFiles: trackedSpies.processFiles,
|
|
84
|
+
reset: trackedSpies.reset,
|
|
85
|
+
getFilesForUpload: trackedSpies.getFilesForUpload
|
|
86
|
+
},
|
|
87
|
+
spies: trackedSpies
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
var mockFileIdCounter = 0;
|
|
91
|
+
function createMockProcessedFile(name, options = {}) {
|
|
92
|
+
const {
|
|
93
|
+
path = name,
|
|
94
|
+
content = "test content",
|
|
95
|
+
type = "text/plain",
|
|
96
|
+
status = "ready",
|
|
97
|
+
statusMessage
|
|
98
|
+
} = options;
|
|
99
|
+
const file = new File([content], name, { type });
|
|
100
|
+
return {
|
|
101
|
+
id: `mock-file-${++mockFileIdCounter}`,
|
|
102
|
+
file,
|
|
103
|
+
path,
|
|
104
|
+
name,
|
|
105
|
+
size: file.size,
|
|
106
|
+
type,
|
|
107
|
+
lastModified: Date.now(),
|
|
108
|
+
status,
|
|
109
|
+
statusMessage
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
function createMockFile(name, content = "test content", type = "text/plain") {
|
|
113
|
+
return new File([content], name, { type, lastModified: Date.now() });
|
|
114
|
+
}
|
|
115
|
+
function createMockFileWithPath(name, webkitRelativePath, content = "test content", type = "text/plain") {
|
|
116
|
+
const file = createMockFile(name, content, type);
|
|
117
|
+
Object.defineProperty(file, "webkitRelativePath", {
|
|
118
|
+
value: webkitRelativePath,
|
|
119
|
+
writable: false,
|
|
120
|
+
enumerable: true,
|
|
121
|
+
configurable: true
|
|
122
|
+
});
|
|
123
|
+
return file;
|
|
124
|
+
}
|
|
125
|
+
function createMockErrorStatus(title = "Validation Failed", details = "One or more files failed validation", errors = []) {
|
|
126
|
+
return { title, details, errors };
|
|
127
|
+
}
|
|
128
|
+
function createMockProcessingStatus(title = "Processing...", details = "Validating and preparing files.") {
|
|
129
|
+
return { title, details };
|
|
130
|
+
}
|
|
131
|
+
function createMockReadyStatus(fileCount) {
|
|
132
|
+
return {
|
|
133
|
+
title: "Ready",
|
|
134
|
+
details: `${fileCount} file(s) are ready.`
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
export { createMockDrop, createMockDropWithSpies, createMockErrorStatus, createMockFile, createMockFileWithPath, createMockProcessedFile, createMockProcessingStatus, createMockReadyStatus };
|
|
139
|
+
//# sourceMappingURL=testing.js.map
|
|
140
|
+
//# sourceMappingURL=testing.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/testing.ts"],"names":[],"mappings":"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","file":"testing.js","sourcesContent":["/**\n * Test utilities for @shipstatic/drop\n *\n * Import from '@shipstatic/drop/testing' in your test files:\n *\n * ```typescript\n * import { createMockDrop, createMockFile } from '@shipstatic/drop/testing';\n * ```\n */\n\nimport type { ProcessedFile, DropStatus, DropStateValue } from './types';\nimport type { DropReturn, DropzonePropsOptions } from './hooks/useDrop';\n\n// ============================================================================\n// Mock Drop Hook Return\n// ============================================================================\n\n/**\n * Options for creating a mock drop return value\n */\nexport interface MockDropOptions {\n phase?: DropStateValue;\n files?: ProcessedFile[];\n sourceName?: string;\n status?: DropStatus | null;\n}\n\n/**\n * Creates a mock DropReturn for testing components that receive drop as a prop\n *\n * @example\n * ```tsx\n * import { createMockDrop, createMockProcessedFile } from '@shipstatic/drop/testing';\n *\n * it('renders file count when files are ready', () => {\n * const drop = createMockDrop({\n * phase: 'ready',\n * files: [createMockProcessedFile('index.html')],\n * });\n *\n * render(<DeployDropArea drop={drop} />);\n * expect(screen.getByText('1 files ready')).toBeInTheDocument();\n * });\n * ```\n */\nexport function createMockDrop(options: MockDropOptions = {}): DropReturn {\n const {\n phase = 'idle',\n files = [],\n sourceName = '',\n status = null,\n } = options;\n\n const validFiles = files.filter(f => f.status === 'ready');\n\n return {\n // State\n phase,\n isProcessing: phase === 'processing',\n isDragging: phase === 'dragging',\n isInteractive: phase === 'idle' || phase === 'dragging' || phase === 'ready',\n hasError: phase === 'error',\n files,\n validFiles,\n sourceName,\n status,\n\n // Prop getters - return minimal objects for spreading\n getDropzoneProps: (opts?: DropzonePropsOptions) => ({\n onDragOver: () => {},\n onDragLeave: () => {},\n onDrop: () => {},\n ...(opts?.clickable !== false && { onClick: () => {} }),\n }),\n getInputProps: () => ({\n ref: { current: null },\n type: 'file' as const,\n style: { display: 'none' },\n multiple: true,\n webkitdirectory: '',\n onChange: () => {},\n }),\n\n // Actions - no-op by default, can be spied on\n open: () => {},\n processFiles: async () => {},\n reset: () => {},\n\n // Helpers\n getFilesForUpload: () => validFiles.map(f => f.file),\n };\n}\n\n/**\n * Creates a mock drop with spy functions for testing interactions\n *\n * @example\n * ```tsx\n * import { createMockDropWithSpies } from '@shipstatic/drop/testing';\n *\n * it('calls reset when Clear button is clicked', async () => {\n * const { drop, spies } = createMockDropWithSpies({ phase: 'ready', files: [...] });\n *\n * render(<DeployDropArea drop={drop} />);\n * await userEvent.click(screen.getByText('Clear'));\n *\n * expect(spies.reset).toHaveBeenCalled();\n * });\n * ```\n */\nexport function createMockDropWithSpies(options: MockDropOptions = {}): {\n drop: DropReturn;\n spies: {\n open: () => void;\n processFiles: (files: File[]) => Promise<void>;\n reset: () => void;\n getFilesForUpload: () => File[];\n };\n} {\n const baseDrop = createMockDrop(options);\n\n const spies = {\n open: createNoopSpy(),\n processFiles: createAsyncNoopSpy(),\n reset: createNoopSpy(),\n getFilesForUpload: (() => baseDrop.getFilesForUpload()) as () => File[],\n };\n\n // Track calls manually (works without vitest in runtime)\n let openCalls = 0;\n let processFilesCalls: File[][] = [];\n let resetCalls = 0;\n\n const trackedSpies = {\n open: Object.assign(() => { openCalls++; }, {\n calls: () => openCalls,\n toHaveBeenCalled: () => openCalls > 0,\n }),\n processFiles: Object.assign(async (files: File[]) => { processFilesCalls.push(files); }, {\n calls: () => processFilesCalls,\n toHaveBeenCalled: () => processFilesCalls.length > 0,\n toHaveBeenCalledWith: (files: File[]) => processFilesCalls.some(c => c === files),\n }),\n reset: Object.assign(() => { resetCalls++; }, {\n calls: () => resetCalls,\n toHaveBeenCalled: () => resetCalls > 0,\n }),\n getFilesForUpload: baseDrop.getFilesForUpload,\n };\n\n return {\n drop: {\n ...baseDrop,\n open: trackedSpies.open,\n processFiles: trackedSpies.processFiles,\n reset: trackedSpies.reset,\n getFilesForUpload: trackedSpies.getFilesForUpload,\n },\n spies: trackedSpies,\n };\n}\n\n// ============================================================================\n// Mock File Utilities\n// ============================================================================\n\nlet mockFileIdCounter = 0;\n\n/**\n * Creates a mock ProcessedFile for testing\n */\nexport function createMockProcessedFile(\n name: string,\n options: {\n path?: string;\n content?: string;\n type?: string;\n status?: 'ready' | 'validation_failed' | 'processing_error' | 'empty_file';\n statusMessage?: string;\n } = {}\n): ProcessedFile {\n const {\n path = name,\n content = 'test content',\n type = 'text/plain',\n status = 'ready',\n statusMessage,\n } = options;\n\n const file = new File([content], name, { type });\n\n return {\n id: `mock-file-${++mockFileIdCounter}`,\n file,\n path,\n name,\n size: file.size,\n type,\n lastModified: Date.now(),\n status,\n statusMessage,\n };\n}\n\n/**\n * Creates a mock File object\n */\nexport function createMockFile(\n name: string,\n content: string = 'test content',\n type: string = 'text/plain'\n): File {\n return new File([content], name, { type, lastModified: Date.now() });\n}\n\n/**\n * Creates a mock File object with webkitRelativePath set\n */\nexport function createMockFileWithPath(\n name: string,\n webkitRelativePath: string,\n content: string = 'test content',\n type: string = 'text/plain'\n): File {\n const file = createMockFile(name, content, type);\n Object.defineProperty(file, 'webkitRelativePath', {\n value: webkitRelativePath,\n writable: false,\n enumerable: true,\n configurable: true,\n });\n return file;\n}\n\n// ============================================================================\n// Mock Status Utilities\n// ============================================================================\n\n/**\n * Creates a mock error status\n */\nexport function createMockErrorStatus(\n title: string = 'Validation Failed',\n details: string = 'One or more files failed validation',\n errors: string[] = []\n): DropStatus {\n return { title, details, errors };\n}\n\n/**\n * Creates a mock processing status\n */\nexport function createMockProcessingStatus(\n title: string = 'Processing...',\n details: string = 'Validating and preparing files.'\n): DropStatus {\n return { title, details };\n}\n\n/**\n * Creates a mock ready status\n */\nexport function createMockReadyStatus(fileCount: number): DropStatus {\n return {\n title: 'Ready',\n details: `${fileCount} file(s) are ready.`,\n };\n}\n\n// ============================================================================\n// Internal Helpers\n// ============================================================================\n\nfunction createNoopSpy(): () => void {\n return () => {};\n}\n\nfunction createAsyncNoopSpy(): () => Promise<void> {\n return async () => {};\n}\n"]}
|