Skip to content
This repository was archived by the owner on Apr 15, 2025. It is now read-only.

Commit 5018223

Browse files
committed
test: improve test coverage
1 parent e3e8ede commit 5018223

File tree

5 files changed

+220
-5
lines changed

5 files changed

+220
-5
lines changed

tests/CacheableImage.test.tsx

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,22 @@ describe('CacheableImage', function () {
106106
expect(MockedRNFS.copyFile).toHaveBeenCalled()
107107
})
108108

109+
it('When local file exists but it fails to be copied to the cache.', async () => {
110+
MockedRNFS.copyFile.mockRejectedValueOnce(new Error('Copy failed'))
111+
112+
const CacheableImage = imageCacheHoc(Image)
113+
114+
const local = '/exists.png'
115+
const url = 'https://example.com/exists.png'
116+
117+
const result = await CacheableImage.cacheLocalFile(local, url)
118+
119+
expect(result).toStrictEqual({
120+
url: 'https://example.com/exists.png',
121+
path: null,
122+
})
123+
})
124+
109125
it('When local file exists, it should be moved to the cache', async () => {
110126
const CacheableImage = imageCacheHoc(Image)
111127

@@ -166,6 +182,33 @@ describe('CacheableImage', function () {
166182
},
167183
)
168184
})
185+
186+
it('When local file and observable exist, it should be notified of changes even if hash fails.', async (done) => {
187+
MockedRNFS.hash.mockRejectedValueOnce(new Error('File not found.'))
188+
189+
FileSystem.cacheObservables[
190+
'90c1be491d18ff2a7280039e9b65749461a65403.png'
191+
] = new ReplaySubject<CacheFileInfo>(1)
192+
193+
const CacheableImage = imageCacheHoc(Image)
194+
195+
const local = '/exists.png'
196+
const url = 'https://example.com/exists.png'
197+
198+
await CacheableImage.cacheLocalFile(local, url)
199+
200+
FileSystem.cacheObservables['90c1be491d18ff2a7280039e9b65749461a65403.png'].subscribe(
201+
(value) => {
202+
expect(value).toStrictEqual({
203+
path:
204+
'file:///base/file/path/react-native-image-cache-hoc/90c1be491d18ff2a7280039e9b65749461a65403.png',
205+
fileName: '90c1be491d18ff2a7280039e9b65749461a65403.png',
206+
md5: undefined, // Hash failed
207+
})
208+
done()
209+
},
210+
)
211+
})
169212
})
170213

171214
it('#flush static method should work as expected.', () => {
@@ -318,6 +361,58 @@ describe('CacheableImage', function () {
318361
})
319362
})
320363

364+
it('componentDidUpdate should validate the source prop correctly.', (done) => {
365+
console.warn = jest.fn()
366+
367+
const CacheableImage = imageCacheHoc(Image)
368+
369+
const wrapper = shallow(<CacheableImage {...mockData.mockCacheableImageProps} />)
370+
371+
setImmediate(() => {
372+
expect(wrapper.prop('source')).toBeDefined()
373+
374+
// Update to an invalid uri
375+
wrapper.setProps({ source: { uri: './local-file.jpg' } })
376+
377+
setImmediate(() => {
378+
expect(console.warn).toHaveBeenNthCalledWith(
379+
1,
380+
'Invalid source prop. <CacheableImage> props.source.uri should be a web accessible url with a valid protocol and host. NOTE: Default valid protocol is https, default valid hosts are *.',
381+
)
382+
383+
expect(wrapper.prop('source')).toBeUndefined()
384+
385+
done()
386+
})
387+
})
388+
})
389+
390+
it('componentDidUpdate should allow file protocol.', (done) => {
391+
console.warn = jest.fn()
392+
393+
const CacheableImage = imageCacheHoc(Image)
394+
395+
const wrapper = shallow(<CacheableImage {...mockData.mockCacheableImageProps} />)
396+
397+
setImmediate(() => {
398+
expect(wrapper.prop('source')).toBeDefined()
399+
400+
// Update to a local file uri
401+
wrapper.setProps({ source: { uri: 'file:///local-file.jpg' } })
402+
403+
setImmediate(() => {
404+
expect(console.warn).toHaveBeenNthCalledWith(
405+
1,
406+
'Invalid source prop. <CacheableImage> props.source.uri should be a web accessible url with a valid protocol and host. NOTE: Default valid protocol is https, default valid hosts are *.',
407+
)
408+
409+
expect(wrapper.prop('source')).toHaveProperty('uri', 'file:///local-file.jpg')
410+
411+
done()
412+
})
413+
})
414+
})
415+
321416
it('#render with valid props does not throw an error.', (done) => {
322417
const CacheableImage = imageCacheHoc(Image)
323418

tests/FileSystem.test.tsx

Lines changed: 77 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,10 @@ import pathLib from 'path'
33
import { mockData } from './mockData'
44
import RNFS from 'react-native-fs'
55
import uuid from 'react-native-uuid'
6-
import FileSystemFactory, { FileSystem } from '../src/FileSystem'
6+
import FileSystemFactory, { CacheFileInfo, FileSystem } from '../src/FileSystem'
77
import { mocked } from 'ts-jest/utils'
8+
import { ReplaySubject } from 'rxjs'
9+
import composeDownloadFile from './utilities/composeDownloadFile'
810

911
describe('FileSystem', function () {
1012
const MockedRNFS = mocked(RNFS, true)
@@ -438,6 +440,59 @@ describe('FileSystem', function () {
438440
},
439441
})
440442
})
443+
444+
it('#observable should handle rejection.', (done) => {
445+
const fileSystem = FileSystemFactory()
446+
447+
MockedRNFS.hash
448+
// existing
449+
.mockRejectedValueOnce(new Error('File not found.'))
450+
451+
MockedRNFS.downloadFile.mockImplementationOnce(() => ({
452+
jobId: -1,
453+
promise: Promise.reject(new Error('Internal')),
454+
}))
455+
456+
const url =
457+
'https://img.wennermedia.com/5333a62d-07db-432a-92e2-198cafa38a14-326adb1a-d8ed-4a5d-b37e-5c88883e1989.png'
458+
459+
fileSystem.fetchFile(url).subscribe({
460+
next: (element) => {
461+
expect(element).toStrictEqual({
462+
path: null,
463+
fileName: 'cd7d2199cd8e088cdfd9c99fc6359666adc36289.png',
464+
})
465+
466+
done()
467+
},
468+
})
469+
})
470+
471+
it('#observable should use ETag header on download.', (done) => {
472+
const fileSystem = FileSystemFactory()
473+
474+
MockedRNFS.hash.mockRejectedValueOnce(new Error('File not found.'))
475+
476+
MockedRNFS.downloadFile.mockImplementationOnce(
477+
composeDownloadFile({
478+
headers: {
479+
ETag: 'foobar',
480+
},
481+
}),
482+
)
483+
484+
fileSystem
485+
.fetchFile(
486+
'https://img.wennermedia.com/5333a62d-07db-432a-92e2-198cafa38a14-326adb1a-d8ed-4a5d-b37e-5c88883e1989.png',
487+
)
488+
.subscribe({
489+
next: (element) => {
490+
expect(element).toHaveProperty('md5', 'foobar')
491+
492+
done()
493+
},
494+
})
495+
})
441496
})
442497

443498
describe('fetchFile', () => {
@@ -529,5 +584,26 @@ describe('FileSystem', function () {
529584

530585
return expect(fileSystem.unlink(invalidPath)).resolves.toEqual(true)
531586
})
587+
588+
it('#unlink should be notify subscribers of changes', async (done) => {
589+
const fileSystem = FileSystemFactory()
590+
591+
const url = 'https://example.com/exists.png'
592+
const fileName = fileSystem.getFileNameFromUrl(url)
593+
594+
FileSystem.cacheObservables[fileName] = new ReplaySubject<CacheFileInfo>(1)
595+
596+
expect(fileSystem.unlink(fileName)).resolves.toEqual(true)
597+
598+
FileSystem.cacheObservables['90c1be491d18ff2a7280039e9b65749461a65403.png'].subscribe(
599+
(value) => {
600+
expect(value).toStrictEqual({
601+
path: null,
602+
fileName,
603+
})
604+
done()
605+
},
606+
)
607+
})
532608
})
533609
})

tests/__mocks__/react-native-fs.tsx

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
import { ReadDirItem } from 'react-native-fs'
1+
import { ReadDirItem, DownloadFileOptions, DownloadResult } from 'react-native-fs'
22
import { mockData } from '../mockData'
3+
import composeDownloadFile from '../utilities/composeDownloadFile'
34

45
const readDir = jest.fn<Promise<ReadDirItem[]>, any[]>().mockResolvedValue([
56
{
@@ -86,9 +87,13 @@ module.exports = {
8687
writeFile: jest.fn(),
8788
appendFile: jest.fn(),
8889
write: jest.fn(),
89-
downloadFile: jest.fn(() => ({
90-
promise: Promise.resolve({ statusCode: 200 }),
91-
})),
90+
downloadFile: jest
91+
.fn<{ jobId: number; promise: Promise<DownloadResult> }, DownloadFileOptions[]>()
92+
.mockImplementation(
93+
composeDownloadFile({
94+
statusCode: 200,
95+
}),
96+
),
9297
uploadFiles: jest.fn(),
9398
touch: jest.fn(),
9499
MainBundlePath: jest.fn(),

tests/imageCacheHoc.test.tsx

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,4 +89,20 @@ describe('CacheableImage', function () {
8989

9090
expect(wrapper).toMatchSnapshot()
9191
})
92+
93+
it('renders file protocol correctly.', (done) => {
94+
console.warn = jest.fn()
95+
96+
const CacheableImage = imageCacheHoc(Image)
97+
98+
const wrapper = shallow(<CacheableImage source={{ uri: 'file:///exists.jpg' }} />)
99+
100+
setImmediate(() => {
101+
expect(wrapper.prop('source')).toStrictEqual({
102+
uri: 'file:///exists.jpg',
103+
})
104+
105+
done()
106+
})
107+
})
92108
})
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import RNFS from 'react-native-fs'
2+
3+
export default ({
4+
statusCode = 200,
5+
headers = {},
6+
}: {
7+
statusCode?: number
8+
headers?: RNFS.Headers
9+
}) => (options: RNFS.DownloadFileOptions) => {
10+
if (options.begin !== undefined && statusCode >= 200 && statusCode < 300) {
11+
options.begin({
12+
jobId: -1,
13+
statusCode,
14+
contentLength: 1,
15+
headers,
16+
})
17+
}
18+
19+
return {
20+
jobId: -1,
21+
promise: Promise.resolve({ jobId: -1, statusCode, bytesWritten: 1 }),
22+
}
23+
}

0 commit comments

Comments
 (0)