UNPKG

7.72 kBJavaScriptView Raw
1/* eslint-env mocha */
2'use strict';
3
4const os = require('os');
5const path = require('path');
6const fs = require('mz/fs');
7const madge = require('../lib/api');
8
9require('should');
10
11describe('API', () => {
12 it('throws error on missing path argument', () => {
13 (() => {
14 madge();
15 }).should.throw('path argument not provided');
16 });
17
18 it('returns a Promise', () => {
19 madge(__dirname + '/cjs/a.js').should.be.Promise(); // eslint-disable-line new-cap
20 });
21
22 it('throws error if file or directory does not exists', (done) => {
23 madge(__dirname + '/missing.js').catch((err) => {
24 err.message.should.match(/no such file or directory/);
25 done();
26 }).catch(done);
27 });
28
29 it('takes single file as path', (done) => {
30 madge(__dirname + '/cjs/a.js').then((res) => {
31 res.obj().should.eql({
32 'a.js': ['b.js', 'c.js'],
33 'b.js': ['c.js'],
34 'c.js': []
35 });
36 done();
37 }).catch(done);
38 });
39
40 it('takes an array of files as path and combines the result', (done) => {
41 madge([__dirname + '/cjs/a.js', __dirname + '/cjs/normal/d.js']).then((res) => {
42 res.obj().should.eql({
43 'a.js': ['b.js', 'c.js'],
44 'b.js': ['c.js'],
45 'c.js': [],
46 'normal/d.js': []
47 });
48 done();
49 }).catch(done);
50 });
51
52 it('take a single directory as path and find files in it', (done) => {
53 madge(__dirname + '/cjs/normal').then((res) => {
54 res.obj().should.eql({
55 'a.js': ['sub/b.js'],
56 'd.js': [],
57 'sub/b.js': ['sub/c.js'],
58 'sub/c.js': ['d.js']
59 });
60 done();
61 }).catch(done);
62 });
63
64 it('takes an array of directories as path and compute the basedir correctly', (done) => {
65 madge([__dirname + '/cjs/multibase/1', __dirname + '/cjs/multibase/2']).then((res) => {
66 res.obj().should.eql({
67 '1/a.js': [],
68 '2/b.js': []
69 });
70 done();
71 }).catch(done);
72 });
73
74 it('takes a predefined tree', (done) => {
75 madge({
76 a: ['b', 'c', 'd'],
77 b: ['c'],
78 c: [],
79 d: ['a']
80 }).then((res) => {
81 res.obj().should.eql({
82 a: ['b', 'c', 'd'],
83 b: ['c'],
84 c: [],
85 d: ['a']
86 });
87 done();
88 }).catch(done);
89 });
90
91 it('can exclude modules using RegExp', (done) => {
92 madge(__dirname + '/cjs/a.js', {
93 excludeRegExp: ['^b.js$']
94 }).then((res) => {
95 res.obj().should.eql({
96 'a.js': ['c.js'],
97 'c.js': []
98 });
99 done();
100 }).catch(done);
101 });
102
103 it('extracts dependencies but excludes .git', (done) => {
104 // eslint-disable-next-line no-sync
105 fs.renameSync(`${__dirname}/git/.git_tmp`, `${__dirname}/git/.git`);
106
107 madge(__dirname + '/git/a.js', {}).then((res) => {
108 res.obj().should.eql({
109 'a.js': ['b.js', 'c.js'],
110 'b.js': ['c.js'],
111 'c.js': []
112 });
113 done();
114 }).catch(() => {
115 done();
116 }).finally(() => {
117 // eslint-disable-next-line no-sync
118 fs.renameSync(`${__dirname}/git/.git`, `${__dirname}/git/.git_tmp`);
119 });
120 });
121
122 describe('dependencyFilter', () => {
123 it('will stop traversing when returning false', (done) => {
124 madge(__dirname + '/cjs/a.js', {
125 dependencyFilter: () => {
126 return false;
127 }
128 }).then((res) => {
129 res.obj().should.eql({
130 'a.js': []
131 });
132 done();
133 }).catch(done);
134 });
135
136 it('will not stop traversing when not returning anything', (done) => {
137 madge(__dirname + '/cjs/a.js', {
138 dependencyFilter: () => {}
139 }).then((res) => {
140 res.obj().should.eql({
141 'a.js': ['b.js', 'c.js'],
142 'b.js': ['c.js'],
143 'c.js': []
144 });
145 done();
146 }).catch(done);
147 });
148
149 it('will pass arguments to the function', (done) => {
150 let counter = 0;
151
152 madge(__dirname + '/cjs/a.js', {
153 dependencyFilter: (dependencyFilePath, traversedFilePath, baseDir) => {
154 if (counter === 0) {
155 dependencyFilePath.should.match(/test\/cjs\/b\.js$/);
156 traversedFilePath.should.match(/test\/cjs\/a\.js$/);
157 baseDir.should.match(/test\/cjs$/);
158 }
159
160 if (counter === 1) {
161 dependencyFilePath.should.match(/test\/cjs\/c\.js$/);
162 traversedFilePath.should.match(/test\/cjs\/a\.js$/);
163 baseDir.should.match(/test\/cjs$/);
164 }
165
166 if (counter === 2) {
167 dependencyFilePath.should.match(/test\/cjs\/c\.js$/);
168 traversedFilePath.should.match(/test\/cjs\/b\.js$/);
169 baseDir.should.match(/test\/cjs$/);
170 }
171
172 counter++;
173 }
174 }).then(() => {
175 done();
176 }).catch(done);
177 });
178 });
179
180 describe('obj()', () => {
181 it('returns dependency object', (done) => {
182 madge(__dirname + '/cjs/a.js').then((res) => {
183 res.obj().should.eql({
184 'a.js': ['b.js', 'c.js'],
185 'b.js': ['c.js'],
186 'c.js': []
187 });
188 done();
189 }).catch(done);
190 });
191 });
192
193 describe('warnings()', () => {
194 it('returns an array of skipped files', (done) => {
195 madge(__dirname + '/cjs/missing.js').then((res) => {
196 res.obj().should.eql({
197 'missing.js': ['c.js'],
198 'c.js': []
199 });
200 res.warnings().should.eql({
201 skipped: ['./path/non/existing/file']
202 });
203 done();
204 }).catch(done);
205 });
206 });
207
208 describe('dot()', () => {
209 it('returns a promise resolved with graphviz DOT output', async () => {
210 const res = await madge(__dirname + '/cjs/b.js');
211 const output = await res.dot();
212 output.should.match(/digraph G/);
213 output.should.match(/bgcolor="#111111"/);
214 output.should.match(/fontcolor="#c6c5fe"/);
215 output.should.match(/color="#757575"/);
216 output.should.match(/fontcolor="#cfffac"/);
217 });
218 });
219
220 describe('depends()', () => {
221 it('returns modules that depends on another', (done) => {
222 madge(__dirname + '/cjs/a.js').then((res) => {
223 res.depends('c.js').should.eql(['a.js', 'b.js']);
224 done();
225 }).catch(done);
226 });
227 });
228
229 describe('orphans()', () => {
230 it('returns modules that no one is depending on', (done) => {
231 madge(__dirname + '/cjs/normal').then((res) => {
232 res.orphans().should.eql(['a.js']);
233 done();
234 }).catch(done);
235 });
236 });
237
238 describe('leaves()', () => {
239 it('returns modules that have no dependencies', (done) => {
240 madge(__dirname + '/cjs/normal').then((res) => {
241 res.leaves().should.eql(['d.js']);
242 done();
243 }).catch(done);
244 });
245 });
246
247 describe('svg()', () => {
248 it('returns a promise resolved with XML SVG output in a Buffer', (done) => {
249 madge(__dirname + '/cjs/b.js')
250 .then((res) => res.svg())
251 .then((output) => {
252 output.should.instanceof(Buffer);
253 output.toString().should.match(/<svg.*/);
254 done();
255 })
256 .catch(done);
257 });
258 });
259
260 describe('image()', () => {
261 let imagePath;
262
263 beforeEach(() => {
264 imagePath = path.join(os.tmpdir(), 'madge_' + Date.now() + '_image.png');
265 });
266
267 afterEach(() => {
268 return fs.unlink(imagePath).catch(() => {});
269 });
270
271 it('rejects if a filename is not supplied', (done) => {
272 madge(__dirname + '/cjs/a.js')
273 .then((res) => res.image())
274 .catch((err) => {
275 err.message.should.eql('imagePath not provided');
276 done();
277 });
278 });
279
280 it('rejects on unsupported image format', (done) => {
281 madge(__dirname + '/cjs/a.js')
282 .then((res) => res.image('image.zyx'))
283 .catch((err) => {
284 err.message.should.match(/Format: "zyx" not recognized/);
285 done();
286 });
287 });
288
289 it('rejects if graphviz is not installed', (done) => {
290 madge(__dirname + '/cjs/a.js', {graphVizPath: '/invalid/path'})
291 .then((res) => res.image('image.png'))
292 .catch((err) => {
293 err.message.should.match(/Could not execute .*gvpr \-V/);
294 done();
295 });
296 });
297
298 it('writes image to file', (done) => {
299 madge(__dirname + '/cjs/a.js')
300 .then((res) => res.image(imagePath))
301 .then((writtenImagePath) => {
302 writtenImagePath.should.eql(imagePath);
303
304 return fs
305 .exists(imagePath)
306 .then((exists) => {
307 if (!exists) {
308 throw new Error(imagePath + ' not created');
309 }
310 done();
311 });
312 })
313 .catch(done);
314 });
315 });
316});