rn-remove-image-bg 0.0.11 → 0.0.13

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.
@@ -1,16 +1,18 @@
1
- import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'
1
+ import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
2
2
 
3
3
  // Mock all dependencies before importing
4
4
  vi.mock('react-native', () => ({
5
5
  Image: {
6
6
  getSize: vi.fn((uri, success) => success(1024, 768)),
7
7
  },
8
- }))
8
+ }));
9
9
 
10
10
  vi.mock('expo-image-manipulator', () => ({
11
- manipulateAsync: vi.fn().mockResolvedValue({ uri: 'file:///mock/result.png' }),
11
+ manipulateAsync: vi
12
+ .fn()
13
+ .mockResolvedValue({ uri: 'file:///mock/result.png' }),
12
14
  SaveFormat: { WEBP: 'webp', PNG: 'png', JPEG: 'jpeg' },
13
- }))
15
+ }));
14
16
 
15
17
  vi.mock('expo-file-system/legacy', () => ({
16
18
  cacheDirectory: '/mock/cache/',
@@ -19,26 +21,28 @@ vi.mock('expo-file-system/legacy', () => ({
19
21
  readAsStringAsync: vi.fn().mockResolvedValue('{}'),
20
22
  writeAsStringAsync: vi.fn().mockResolvedValue(undefined),
21
23
  deleteAsync: vi.fn().mockResolvedValue(undefined),
22
- }))
24
+ }));
23
25
 
24
26
  vi.mock('thumbhash', () => ({
25
27
  rgbaToThumbHash: vi.fn().mockReturnValue(new Uint8Array([1, 2, 3, 4])),
26
- }))
28
+ }));
27
29
 
28
30
  vi.mock('upng-js', () => ({
29
31
  decode: vi.fn().mockReturnValue({ width: 32, height: 32 }),
30
32
  toRGBA8: vi.fn().mockReturnValue([new Uint8Array(32 * 32 * 4)]),
31
- }))
33
+ }));
32
34
 
33
35
  // Mock Nitro modules
34
- const mockRemoveBackground = vi.fn().mockResolvedValue('file:///mock/bg_removed.png')
36
+ const mockRemoveBackground = vi
37
+ .fn()
38
+ .mockResolvedValue('file:///mock/bg_removed.png');
35
39
  vi.mock('react-native-nitro-modules', () => ({
36
40
  NitroModules: {
37
41
  createHybridObject: vi.fn(() => ({
38
42
  removeBackground: mockRemoveBackground,
39
43
  })),
40
44
  },
41
- }))
45
+ }));
42
46
 
43
47
  // Import after mocking
44
48
  import {
@@ -48,53 +52,61 @@ import {
48
52
  onLowMemory,
49
53
  configureCache,
50
54
  getCacheDirectory,
51
- } from '../ImageProcessing'
52
- import { BackgroundRemovalError } from '../errors'
55
+ } from '../ImageProcessing';
56
+ import { BackgroundRemovalError } from '../errors';
53
57
 
54
58
  describe('ImageProcessing', () => {
55
59
  beforeEach(() => {
56
- vi.clearAllMocks()
57
- })
60
+ vi.clearAllMocks();
61
+ });
58
62
 
59
63
  afterEach(async () => {
60
- await clearCache()
61
- })
64
+ await clearCache();
65
+ });
62
66
 
63
67
  describe('removeBgImage', () => {
64
68
  describe('input validation', () => {
65
69
  it('should throw INVALID_PATH for empty string', async () => {
66
- await expect(removeBgImage('')).rejects.toThrow(BackgroundRemovalError)
70
+ await expect(removeBgImage('')).rejects.toThrow(BackgroundRemovalError);
67
71
  await expect(removeBgImage('')).rejects.toMatchObject({
68
72
  code: 'INVALID_PATH',
69
- })
70
- })
73
+ });
74
+ });
71
75
 
72
76
  it('should throw INVALID_PATH for whitespace-only string', async () => {
73
77
  await expect(removeBgImage(' ')).rejects.toMatchObject({
74
78
  code: 'INVALID_PATH',
75
- })
76
- })
79
+ });
80
+ });
77
81
 
78
82
  it('should throw INVALID_PATH for http URLs', async () => {
79
- await expect(removeBgImage('http://example.com/image.jpg')).rejects.toMatchObject({
83
+ await expect(
84
+ removeBgImage('http://example.com/image.jpg')
85
+ ).rejects.toMatchObject({
80
86
  code: 'INVALID_PATH',
81
- })
82
- })
87
+ });
88
+ });
83
89
 
84
90
  it('should throw INVALID_PATH for https URLs', async () => {
85
- await expect(removeBgImage('https://example.com/image.jpg')).rejects.toMatchObject({
91
+ await expect(
92
+ removeBgImage('https://example.com/image.jpg')
93
+ ).rejects.toMatchObject({
86
94
  code: 'INVALID_PATH',
87
- })
88
- })
95
+ });
96
+ });
89
97
 
90
98
  it('should accept file:// URIs', async () => {
91
- await expect(removeBgImage('file:///path/to/image.jpg')).resolves.toBeDefined()
92
- })
99
+ await expect(
100
+ removeBgImage('file:///path/to/image.jpg')
101
+ ).resolves.toBeDefined();
102
+ });
93
103
 
94
104
  it('should accept absolute paths starting with /', async () => {
95
- await expect(removeBgImage('/path/to/image.jpg')).resolves.toBeDefined()
96
- })
97
- })
105
+ await expect(
106
+ removeBgImage('/path/to/image.jpg')
107
+ ).resolves.toBeDefined();
108
+ });
109
+ });
98
110
 
99
111
  describe('options validation', () => {
100
112
  it('should throw INVALID_OPTIONS for maxDimension < 100', async () => {
@@ -102,40 +114,40 @@ describe('ImageProcessing', () => {
102
114
  removeBgImage('file:///test.jpg', { maxDimension: 50 })
103
115
  ).rejects.toMatchObject({
104
116
  code: 'INVALID_OPTIONS',
105
- })
106
- })
117
+ });
118
+ });
107
119
 
108
120
  it('should throw INVALID_OPTIONS for maxDimension > 8192', async () => {
109
121
  await expect(
110
122
  removeBgImage('file:///test.jpg', { maxDimension: 10000 })
111
123
  ).rejects.toMatchObject({
112
124
  code: 'INVALID_OPTIONS',
113
- })
114
- })
125
+ });
126
+ });
115
127
 
116
128
  it('should throw INVALID_OPTIONS for quality < 0', async () => {
117
129
  await expect(
118
130
  removeBgImage('file:///test.jpg', { quality: -10 })
119
131
  ).rejects.toMatchObject({
120
132
  code: 'INVALID_OPTIONS',
121
- })
122
- })
133
+ });
134
+ });
123
135
 
124
136
  it('should throw INVALID_OPTIONS for quality > 100', async () => {
125
137
  await expect(
126
138
  removeBgImage('file:///test.jpg', { quality: 150 })
127
139
  ).rejects.toMatchObject({
128
140
  code: 'INVALID_OPTIONS',
129
- })
130
- })
141
+ });
142
+ });
131
143
 
132
144
  it('should throw INVALID_OPTIONS for invalid format', async () => {
133
145
  await expect(
134
146
  removeBgImage('file:///test.jpg', { format: 'JPEG' as any })
135
147
  ).rejects.toMatchObject({
136
148
  code: 'INVALID_OPTIONS',
137
- })
138
- })
149
+ });
150
+ });
139
151
 
140
152
  it('should accept valid options', async () => {
141
153
  await expect(
@@ -144,45 +156,49 @@ describe('ImageProcessing', () => {
144
156
  quality: 90,
145
157
  format: 'WEBP',
146
158
  })
147
- ).resolves.toBeDefined()
148
- })
149
- })
159
+ ).resolves.toBeDefined();
160
+ });
161
+ });
150
162
 
151
163
  describe('progress callback', () => {
152
164
  it('should call onProgress during processing', async () => {
153
- const onProgress = vi.fn()
154
- await removeBgImage('file:///test.jpg', { onProgress })
165
+ const onProgress = vi.fn();
166
+ await removeBgImage('file:///test.jpg', { onProgress });
155
167
 
156
168
  // Should be called at least for start and end
157
- expect(onProgress).toHaveBeenCalled()
158
- expect(onProgress).toHaveBeenCalledWith(expect.any(Number))
159
- })
160
- })
169
+ expect(onProgress).toHaveBeenCalled();
170
+ expect(onProgress).toHaveBeenCalledWith(expect.any(Number));
171
+ });
172
+ });
161
173
 
162
174
  describe('caching', () => {
163
175
  it('should cache results when useCache is true', async () => {
164
- await removeBgImage('file:///test.jpg', { useCache: true })
165
- expect(getCacheSize()).toBe(1)
166
- })
176
+ await removeBgImage('file:///test.jpg', { useCache: true });
177
+ expect(getCacheSize()).toBe(1);
178
+ });
167
179
 
168
180
  it('should not cache results when useCache is false', async () => {
169
- await removeBgImage('file:///test.jpg', { useCache: false })
170
- expect(getCacheSize()).toBe(0)
171
- })
181
+ await removeBgImage('file:///test.jpg', { useCache: false });
182
+ expect(getCacheSize()).toBe(0);
183
+ });
172
184
 
173
185
  it('should return cached result on second call', async () => {
174
- const result1 = await removeBgImage('file:///test.jpg', { useCache: true })
175
-
186
+ const result1 = await removeBgImage('file:///test.jpg', {
187
+ useCache: true,
188
+ });
189
+
176
190
  // Reset mock to verify it's not called again
177
- mockRemoveBackground.mockClear()
178
-
179
- const result2 = await removeBgImage('file:///test.jpg', { useCache: true })
180
-
181
- expect(result1).toBe(result2)
191
+ mockRemoveBackground.mockClear();
192
+
193
+ const result2 = await removeBgImage('file:///test.jpg', {
194
+ useCache: true,
195
+ });
196
+
197
+ expect(result1).toBe(result2);
182
198
  // Native should not be called on second request (cache hit)
183
- expect(mockRemoveBackground).not.toHaveBeenCalled()
184
- })
185
- })
199
+ expect(mockRemoveBackground).not.toHaveBeenCalled();
200
+ });
201
+ });
186
202
 
187
203
  describe('native call', () => {
188
204
  it('should call native removeBackground with correct options', async () => {
@@ -191,7 +207,7 @@ describe('ImageProcessing', () => {
191
207
  format: 'WEBP',
192
208
  quality: 85,
193
209
  useCache: false, // Don't cache so we can verify the call
194
- })
210
+ });
195
211
 
196
212
  expect(mockRemoveBackground).toHaveBeenCalledWith(
197
213
  'file:///test.jpg',
@@ -200,61 +216,63 @@ describe('ImageProcessing', () => {
200
216
  format: 'WEBP',
201
217
  quality: 85,
202
218
  })
203
- )
204
- })
219
+ );
220
+ });
205
221
 
206
222
  it('should normalize result path to file:// URI', async () => {
207
- mockRemoveBackground.mockResolvedValueOnce('/path/without/scheme.png')
208
-
209
- const result = await removeBgImage('file:///test.jpg', { useCache: false })
210
-
211
- expect(result).toBe('file:///path/without/scheme.png')
212
- })
213
- })
214
- })
223
+ mockRemoveBackground.mockResolvedValueOnce('/path/without/scheme.png');
224
+
225
+ const result = await removeBgImage('file:///test.jpg', {
226
+ useCache: false,
227
+ });
228
+
229
+ expect(result).toBe('file:///path/without/scheme.png');
230
+ });
231
+ });
232
+ });
215
233
 
216
234
  describe('cache management functions', () => {
217
235
  describe('clearCache', () => {
218
236
  it('should clear all cache entries', async () => {
219
- await removeBgImage('file:///test1.jpg', { useCache: true })
220
- await removeBgImage('file:///test2.jpg', { useCache: true })
221
- expect(getCacheSize()).toBe(2)
237
+ await removeBgImage('file:///test1.jpg', { useCache: true });
238
+ await removeBgImage('file:///test2.jpg', { useCache: true });
239
+ expect(getCacheSize()).toBe(2);
222
240
 
223
- await clearCache()
224
- expect(getCacheSize()).toBe(0)
225
- })
226
- })
241
+ await clearCache();
242
+ expect(getCacheSize()).toBe(0);
243
+ });
244
+ });
227
245
 
228
246
  describe('getCacheSize', () => {
229
247
  it('should return 0 for empty cache', () => {
230
- expect(getCacheSize()).toBe(0)
231
- })
248
+ expect(getCacheSize()).toBe(0);
249
+ });
232
250
 
233
251
  it('should return correct count after adding entries', async () => {
234
- await removeBgImage('file:///test1.jpg', { useCache: true })
235
- expect(getCacheSize()).toBe(1)
236
-
237
- await removeBgImage('file:///test2.jpg', { useCache: true })
238
- expect(getCacheSize()).toBe(2)
239
- })
240
- })
252
+ await removeBgImage('file:///test1.jpg', { useCache: true });
253
+ expect(getCacheSize()).toBe(1);
254
+
255
+ await removeBgImage('file:///test2.jpg', { useCache: true });
256
+ expect(getCacheSize()).toBe(2);
257
+ });
258
+ });
241
259
 
242
260
  describe('onLowMemory', () => {
243
261
  it('should clear cache and return count', async () => {
244
- await removeBgImage('file:///test1.jpg', { useCache: true })
245
- await removeBgImage('file:///test2.jpg', { useCache: true })
246
-
247
- const cleared = await onLowMemory()
248
-
249
- expect(cleared).toBe(2)
250
- expect(getCacheSize()).toBe(0)
251
- })
262
+ await removeBgImage('file:///test1.jpg', { useCache: true });
263
+ await removeBgImage('file:///test2.jpg', { useCache: true });
264
+
265
+ const cleared = await onLowMemory();
266
+
267
+ expect(cleared).toBe(2);
268
+ expect(getCacheSize()).toBe(0);
269
+ });
252
270
 
253
271
  it('should return 0 when cache is empty', async () => {
254
- const cleared = await onLowMemory()
255
- expect(cleared).toBe(0)
256
- })
257
- })
272
+ const cleared = await onLowMemory();
273
+ expect(cleared).toBe(0);
274
+ });
275
+ });
258
276
 
259
277
  describe('configureCache', () => {
260
278
  it('should not throw when configuring cache', () => {
@@ -264,15 +282,15 @@ describe('ImageProcessing', () => {
264
282
  maxAgeMinutes: 60,
265
283
  persistToDisk: true,
266
284
  })
267
- ).not.toThrow()
268
- })
269
- })
285
+ ).not.toThrow();
286
+ });
287
+ });
270
288
 
271
289
  describe('getCacheDirectory', () => {
272
290
  it('should return a string containing bg-removal', () => {
273
- const dir = getCacheDirectory()
274
- expect(dir).toContain('bg-removal')
275
- })
276
- })
277
- })
278
- })
291
+ const dir = getCacheDirectory();
292
+ expect(dir).toContain('bg-removal');
293
+ });
294
+ });
295
+ });
296
+ });