1 |
|
2 | 'use strict';
|
3 |
|
4 | const os = require('os');
|
5 | const path = require('path');
|
6 | const fs = require('mz/fs');
|
7 | const madge = require('../lib/api');
|
8 |
|
9 | require('should');
|
10 |
|
11 | describe('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();
|
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 | describe('dependencyFilter', () => {
|
104 | it('will stop traversing when returning false', (done) => {
|
105 | madge(__dirname + '/cjs/a.js', {
|
106 | dependencyFilter: () => {
|
107 | return false;
|
108 | }
|
109 | }).then((res) => {
|
110 | res.obj().should.eql({
|
111 | 'a.js': []
|
112 | });
|
113 | done();
|
114 | }).catch(done);
|
115 | });
|
116 |
|
117 | it('will not stop traversing when not returning anything', (done) => {
|
118 | madge(__dirname + '/cjs/a.js', {
|
119 | dependencyFilter: () => {}
|
120 | }).then((res) => {
|
121 | res.obj().should.eql({
|
122 | 'a.js': ['b.js', 'c.js'],
|
123 | 'b.js': ['c.js'],
|
124 | 'c.js': []
|
125 | });
|
126 | done();
|
127 | }).catch(done);
|
128 | });
|
129 |
|
130 | it('will pass arguments to the function', (done) => {
|
131 | let counter = 0;
|
132 |
|
133 | madge(__dirname + '/cjs/a.js', {
|
134 | dependencyFilter: (dependencyFilePath, traversedFilePath, baseDir) => {
|
135 | if (counter === 0) {
|
136 | dependencyFilePath.should.match(/test\/cjs\/b\.js$/);
|
137 | traversedFilePath.should.match(/test\/cjs\/a\.js$/);
|
138 | baseDir.should.match(/test\/cjs$/);
|
139 | }
|
140 |
|
141 | if (counter === 1) {
|
142 | dependencyFilePath.should.match(/test\/cjs\/c\.js$/);
|
143 | traversedFilePath.should.match(/test\/cjs\/a\.js$/);
|
144 | baseDir.should.match(/test\/cjs$/);
|
145 | }
|
146 |
|
147 | if (counter === 2) {
|
148 | dependencyFilePath.should.match(/test\/cjs\/c\.js$/);
|
149 | traversedFilePath.should.match(/test\/cjs\/b\.js$/);
|
150 | baseDir.should.match(/test\/cjs$/);
|
151 | }
|
152 |
|
153 | counter++;
|
154 | }
|
155 | }).then(() => {
|
156 | done();
|
157 | }).catch(done);
|
158 | });
|
159 | });
|
160 |
|
161 | describe('obj()', () => {
|
162 | it('returns dependency object', (done) => {
|
163 | madge(__dirname + '/cjs/a.js').then((res) => {
|
164 | res.obj().should.eql({
|
165 | 'a.js': ['b.js', 'c.js'],
|
166 | 'b.js': ['c.js'],
|
167 | 'c.js': []
|
168 | });
|
169 | done();
|
170 | }).catch(done);
|
171 | });
|
172 | });
|
173 |
|
174 | describe('warnings()', () => {
|
175 | it('returns an array of skipped files', (done) => {
|
176 | madge(__dirname + '/cjs/missing.js').then((res) => {
|
177 | res.obj().should.eql({
|
178 | 'missing.js': ['c.js'],
|
179 | 'c.js': []
|
180 | });
|
181 | res.warnings().should.eql({
|
182 | skipped: ['./path/non/existing/file']
|
183 | });
|
184 | done();
|
185 | }).catch(done);
|
186 | });
|
187 | });
|
188 |
|
189 | describe('dot()', () => {
|
190 | it('returns a promise resolved with graphviz DOT output', (done) => {
|
191 | madge(__dirname + '/cjs/b.js')
|
192 | .then((res) => res.dot())
|
193 | .then((output) => {
|
194 | output.should.eql('digraph G {\n "b.js";\n "c.js";\n "b.js" -> "c.js";\n}\n');
|
195 | done();
|
196 | })
|
197 | .catch(done);
|
198 | });
|
199 | });
|
200 |
|
201 | describe('depends()', () => {
|
202 | it('returns modules that depends on another', (done) => {
|
203 | madge(__dirname + '/cjs/a.js').then((res) => {
|
204 | res.depends('c.js').should.eql(['a.js', 'b.js']);
|
205 | done();
|
206 | }).catch(done);
|
207 | });
|
208 | });
|
209 |
|
210 | describe('orphans()', () => {
|
211 | it('returns modules that no one is depending on', (done) => {
|
212 | madge(__dirname + '/cjs/normal').then((res) => {
|
213 | res.orphans().should.eql(['a.js']);
|
214 | done();
|
215 | }).catch(done);
|
216 | });
|
217 | });
|
218 |
|
219 | describe('image()', () => {
|
220 | let imagePath;
|
221 |
|
222 | beforeEach(() => {
|
223 | imagePath = path.join(os.tmpdir(), 'madge_' + Date.now() + '_image.png');
|
224 | });
|
225 |
|
226 | afterEach(() => {
|
227 | return fs.unlink(imagePath).catch(() => {});
|
228 | });
|
229 |
|
230 | it('rejects if a filename is not supplied', (done) => {
|
231 | madge(__dirname + '/cjs/a.js')
|
232 | .then((res) => res.image())
|
233 | .catch((err) => {
|
234 | err.message.should.eql('imagePath not provided');
|
235 | done();
|
236 | });
|
237 | });
|
238 |
|
239 | it('rejects on unsupported image format', (done) => {
|
240 | madge(__dirname + '/cjs/a.js')
|
241 | .then((res) => res.image('image.zyx'))
|
242 | .catch((err) => {
|
243 | err.message.should.match(/Format: "zyx" not recognized/);
|
244 | done();
|
245 | });
|
246 | });
|
247 |
|
248 | it('rejects if graphviz is not installed', (done) => {
|
249 | madge(__dirname + '/cjs/a.js', {graphVizPath: '/invalid/path'})
|
250 | .then((res) => res.image('image.png'))
|
251 | .catch((err) => {
|
252 | err.message.should.match(/Could not execute .*gvpr \-V/);
|
253 | done();
|
254 | });
|
255 | });
|
256 |
|
257 | it('writes image to file', (done) => {
|
258 | madge(__dirname + '/cjs/a.js')
|
259 | .then((res) => res.image(imagePath))
|
260 | .then((writtenImagePath) => {
|
261 | writtenImagePath.should.eql(imagePath);
|
262 |
|
263 | return fs
|
264 | .exists(imagePath)
|
265 | .then((exists) => {
|
266 | if (!exists) {
|
267 | throw new Error(imagePath + ' not created');
|
268 | }
|
269 | done();
|
270 | });
|
271 | })
|
272 | .catch(done);
|
273 | });
|
274 | });
|
275 | });
|