UNPKG

6.53 kBJavaScriptView Raw
1/* global before, describe, it */
2
3'use strict'
4
5const fs = require('fs-extra')
6const path = require('path')
7const util = require('util')
8const url = require('url')
9
10const chai = require('chai')
11const dirtyChai = require('dirty-chai')
12
13const genThumbnail = require('../')
14const looksSame = util.promisify(require('looks-same'))
15const nock = require('nock')
16
17const { expect } = chai
18const absolutePath = relative => path.join(__dirname, relative)
19
20chai.use(dirtyChai)
21
22describe('simple-thumbnail creates thumbnails for videos', () => {
23 const tinySize = '50x?'
24
25 before(async () => {
26 await fs.remove(absolutePath('./out'))
27 await fs.mkdirp(absolutePath('./out/storage'))
28 await fs.mkdirp(absolutePath('./out/input-formats'))
29 await fs.mkdirp(absolutePath('./out/image-formats'))
30 await fs.mkdirp(absolutePath('./out/sizes'))
31 })
32
33 describe('invalid input', () => {
34 const filePath = absolutePath('./data/bunny.mp4')
35 const outPath = absolutePath('./out/invalid.png')
36
37 it('throws an error on malformed size string', async () => {
38 try {
39 await genThumbnail(filePath, outPath, 'not a real size')
40 } catch (err) {
41 expect(err.message).to.equal('Invalid size argument')
42 }
43 })
44
45 it('throws an error given a "?x?" size string', async () => {
46 try {
47 await genThumbnail(filePath, outPath, '?x?')
48 } catch (err) {
49 expect(err.message).to.equal('Invalid size argument')
50 }
51 })
52
53 it('throws an error given a percentage string with no value (%)', async () => {
54 try {
55 await genThumbnail(filePath, outPath, '%')
56 } catch (err) {
57 expect(err.message).to.equal('Invalid size argument')
58 }
59 })
60
61 it('throws a ffmpeg stderr dump on non-zero exit', async () => {
62 try {
63 await genThumbnail('not a real path', outPath, '200x200')
64 } catch (err) {
65 const stderrLines = err.message.split('\n')
66
67 expect(stderrLines[0]).to.equal('ffmpeg exited 1')
68 expect(stderrLines[1]).to.equal('ffmpeg stderr:')
69 }
70 })
71 })
72
73 describe('thumbnail creation for different storage mediums', () => {
74 const filePath = absolutePath('./data/bunny.webm')
75
76 it('creates thumbnails for files saved on disk', async () => {
77 try {
78 await genThumbnail(filePath, absolutePath('./out/storage/disk.png'), tinySize)
79 } catch (err) {
80 console.log(err)
81
82 expect.fail()
83 }
84 })
85
86 // Note: mp4 does not work with read streams, hence the usage of .webm
87 it('creates thumbnails from read streams', async () => {
88 const stream = fs.createReadStream(filePath)
89
90 try {
91 await genThumbnail(stream, absolutePath('./out/storage/stream.png'), tinySize)
92 } catch (err) {
93 console.log(err)
94
95 expect.fail()
96 }
97 })
98
99 describe('creates thumbnails for remote files', () => {
100 const protocols = ['http', 'https']
101
102 protocols.forEach((protocol) => {
103 const fileUrl = `${protocol}://www.w3schools.com/html/mov_bbb.webm`
104 const parsedUrl = url.parse(fileUrl)
105
106 nock(`${parsedUrl.protocol}//${parsedUrl.host}`)
107 .get(parsedUrl.path)
108 .replyWithFile(200, filePath, { 'Content-Type': 'video/webm' })
109
110 it(`${protocol} protocol`, () => {
111 it('successfully generates thumbnails', async () => {
112 try {
113 await genThumbnail(fileUrl, absolutePath(`./out/storage/${protocol}.png`), tinySize)
114 } catch (err) {
115 console.log(err)
116
117 expect.fail()
118 }
119 })
120 })
121 })
122 })
123 })
124
125 describe('file formats', () => {
126 const formats = ['webm', 'mp4']
127
128 formats.forEach((format) => {
129 it(`can create thumbnails for ${format}`, async () => {
130 const filePath = absolutePath(`./data/bunny.${format}`)
131
132 try {
133 await genThumbnail(filePath, absolutePath(`./out/input-formats/${format}.png`), tinySize)
134 } catch (err) {
135 console.log(err)
136
137 expect.fail()
138 }
139 })
140 })
141 })
142
143 describe('thumbnail sizes', () => {
144 const sizes = ['25%', '101%', '50x?', '?x50', '100x50']
145 const filePath = absolutePath('./data/bunny.webm')
146
147 sizes.forEach((size) => {
148 it(`handles sizes of the form ${size}`, async () => {
149 try {
150 await genThumbnail(
151 filePath,
152 absolutePath(`./out/sizes/size-${size.replace('%', '')}.png`),
153 size
154 )
155 } catch (err) {
156 console.log(err)
157
158 expect.fail()
159 }
160 })
161 })
162 })
163
164 describe('thumbnail output image formats', () => {
165 const filePath = absolutePath('./data/bunny.webm')
166 const formats = ['gif', 'jpg', 'png']
167
168 formats.forEach((format) => {
169 it(`can create ${format} images`, async () => {
170 try {
171 await genThumbnail(
172 filePath,
173 absolutePath(`./out/image-formats/${format}.${format}`),
174 tinySize
175 )
176 } catch (err) {
177 console.log(err)
178
179 expect.fail()
180 }
181 })
182 })
183 })
184
185 describe('thumbnail correctness', () => {
186 it('produces thumbnail images that are identical to expected output', async () => {
187 const config = { tolerance: 5 }
188
189 const storageFiles = await fs.readdir(absolutePath('./out/storage'))
190 const inputFormatFiles = await fs.readdir(absolutePath('./out/input-formats'))
191 const sizeFiles = await fs.readdir(absolutePath('./out/sizes'))
192
193 const storagePromises = storageFiles
194 .map(file => looksSame(
195 absolutePath('./expected/tiny.png'),
196 absolutePath(`./out/storage/${file}`),
197 config
198 ))
199
200 const inputFormatPromises = inputFormatFiles
201 .map(file => looksSame(
202 absolutePath('./expected/tiny.png'),
203 absolutePath(`./out/input-formats/${file}`),
204 config
205 ))
206
207 const sizePromises = sizeFiles
208 .map(file => looksSame(
209 absolutePath(`./expected/${file}`),
210 absolutePath(`./out/sizes/${file}`),
211 config
212 ))
213
214 try {
215 const results = await Promise.all(
216 storagePromises.concat(inputFormatPromises, sizePromises)
217 )
218
219 expect(results.every(x => x)).to.be.true()
220 } catch (err) {
221 console.log(err)
222
223 expect.fail()
224 }
225 })
226 })
227
228 describe('external error handling', () => {
229 it('throws an error if stderr fires an error event', async () => {
230
231 })
232 })
233})