-
Notifications
You must be signed in to change notification settings - Fork 172
/
utils.spec.js
366 lines (317 loc) · 13.2 KB
/
utils.spec.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
const mockArgv = require('mock-argv');
const mock = require('mock-fs');
const utils = require('../src/utils');
describe('Utils', () => {
describe('.lang()', () => {
it('returns correct info for scss', () => {
const lang = utils.lang('file.scss');
expect(lang.name).toEqual('SCSS');
expect(lang.mode).toEqual('scss');
expect(lang.scope).toEqual('source.scss');
});
it('returns correct info for nunjucks variants', () => {
const expected = {
name: 'HTML+Django',
mode: 'django',
scope: 'text.html.django',
color: null,
};
expect(utils.lang('file.nunjucks')).toEqual(expected);
expect(utils.lang('file.nunjs')).toEqual(expected);
expect(utils.lang('file.nunj')).toEqual(expected);
expect(utils.lang('file.nj')).toEqual(expected);
expect(utils.lang('file.jinja2')).toEqual(expected);
expect(utils.lang('file.j2')).toEqual(expected);
});
it('returns default value for unknown file type', () => {
const expected = {
name: '.THIS_IS_NOT_A_FILE',
mode: 'plaintext',
scope: null,
color: null,
};
expect(utils.lang('file.this_is_not_a_file')).toEqual(expected);
});
});
describe('.titlize()', () => {
it('returns titlized string', () => {
expect(utils.titlize('not-a-title')).toEqual('Not A Title');
});
});
describe('.slugify()', () => {
it('returns slugified string', () => {
expect(utils.slugify('Not A Slug')).toEqual('not-a-slug');
});
it('replaces special characters', () => {
expect(utils.slugify('slug-šäöüõ-slug')).toEqual('slug-saouo-slug');
});
});
describe('.escapeForRegexp()', () => {
it('returns escaped string', () => {
expect(utils.escapeForRegexp('$handle')).toEqual('\\$handle');
});
});
describe('.parseArgv()', () => {
it('returns correct data for command with arguments and options', async () => {
const expected = {
args: ['bar'],
command: 'foo',
opts: {
option: 'value',
},
};
await mockArgv(['foo', 'bar', '--option', 'value'], () => {
expect(utils.parseArgv()).toEqual(expected);
});
});
it('returns correct data without command', async () => {
const expected = {
args: [],
command: null,
opts: {},
};
await mockArgv([], () => {
expect(utils.parseArgv()).toEqual(expected);
});
});
});
describe('.stringify()', () => {
it('returns stringified object', () => {
const object = {
buffer: Buffer.from('buffer'),
func: function () {},
plainObject: {},
};
const expected = `{
"buffer": "<Buffer>",
"func": "<Function>",
"plainObject": "{}"
}`;
expect(utils.stringify(object)).toEqual(expected);
});
it('allows custom indent', () => {
const object = {
buffer: Buffer.from('buffer'),
func: () => {},
plainObject: {},
};
const expected = `{
"buffer": "<Buffer>",
"func": "<Function>",
"plainObject": "{}"
}`;
expect(utils.stringify(object, 2)).toEqual(expected);
});
});
describe('.fileExistsSync()', () => {
it('returns true if file is accessible', () => {
mock({
'path/to/accessible/file': 'can access',
});
expect(utils.fileExistsSync('path/to/accessible/file')).toBe(true);
mock.restore();
});
});
describe('.md5()', () => {
it('returns same hash for same string', () => {
const first = utils.md5('string');
const second = utils.md5('string');
expect(first).toEqual('b45cffe084dd3d20d928bee85e7b0f21');
expect(first).toEqual(second);
});
});
describe('.mergeProp()', () => {
it('Returns the upstream value if the property is undefined', () => {
expect(utils.mergeProp(undefined, 'foo')).toEqual('foo');
});
it('Returns the property value if the upstream is undefined', () => {
expect(utils.mergeProp('foo', undefined)).toEqual('foo');
});
it('Merges the contents of arrays', () => {
expect(utils.mergeProp(['one', 'two'], ['one', 'three', 'four'])).toEqual(
expect.arrayContaining(['one', 'two', 'three', 'four'])
);
});
it('Applies default values from upstream objects', () => {
expect(utils.mergeProp({ one: 'one', two: 'two' }, { one: 'eins', three: 'drei' })).toEqual({
one: 'one',
two: 'two',
three: 'drei',
});
expect(
utils.mergeProp(
{ one: 'one', nested: { three: 'three' } },
{ two: 'zwei', nested: { three: 'drei', four: 'vier' } }
)
).toEqual({ one: 'one', two: 'zwei', nested: { three: 'three', four: 'vier' } });
});
it('Does not attempt to merge objects that are instances of classes', () => {
let prop = new MyClass();
let upstream = new MyOtherClass();
let plain = { foo: 'bar' };
expect(utils.mergeProp(prop, upstream)).toEqual(prop);
expect(utils.mergeProp(plain, upstream)).toEqual(plain);
expect(utils.mergeProp(prop, plain)).toEqual(prop);
});
});
describe('.defaultsDeep()', () => {
it('Does not modify source objects', () => {
let target = {};
let defaults = { foo: 'bar' };
let result = utils.defaultsDeep(target, defaults);
expect(result).not.toBe(defaults);
expect(result).not.toEqual(target);
expect(target).toEqual({});
expect(defaults).toEqual({ foo: 'bar' });
expect(target).toEqual(target);
expect(defaults).toEqual(defaults);
});
it('Recursively merges plain objects', () => {
let target = {
top: 'from target',
item: {
nested: {
one: 'from target',
two: ['from', 'target'],
three: undefined,
four: {
five: 5,
},
},
},
};
let defaults = {
item: {
def: 'from default',
nested: {
one: 'from default',
two: ['from', 'default'],
three: ['set', 'from', 'default'],
four: {
five: 9,
six: 6,
},
},
},
};
let expected = {
top: 'from target',
item: {
def: 'from default',
nested: {
one: 'from target',
two: ['from', 'target'],
three: ['set', 'from', 'default'],
four: {
five: 5,
six: 6,
},
},
},
};
expect(utils.defaultsDeep(target, defaults)).toEqual(expected);
});
it('Does not merge non-plain-object values', () => {
let target = { item: new MyClass() };
let defaults = { item: new MyOtherClass() };
let plain = { item: { foo: 'plain' } };
expect(utils.defaultsDeep(target, defaults)).toEqual(target);
expect(utils.defaultsDeep(plain, defaults)).toEqual(plain);
expect(utils.defaultsDeep(target, plain)).toEqual(target);
});
it('Does not merge array values', () => {
let target = { items: ['one', 'two'] };
let defaults = { items: ['one', 'three', 'four'] };
expect(utils.defaultsDeep(target, defaults).items).toEqual(target.items);
});
it('Returns the default value if the target property is undefined', () => {
let target = { anotherItem: 'foo', nullItem: null, undefinedItem: undefined };
let defaults = { item: ['one', 'three', 'four'], nullItem: 'not null', undefinedItem: 'not undefined' };
let result = utils.mergeProp(target, defaults);
expect(result).toHaveProperty('anotherItem');
expect(result).toHaveProperty('item');
expect(result.nullItem).toBeNull();
expect(result.undefinedItem).toEqual('not undefined');
});
});
describe('.relUrlPath()', () => {
const opts = {
ext: '.html',
};
it('returns correct relative path for same directory', () => {
expect(utils.relUrlPath('/path/to/a', '/path/to/b', opts)).toEqual('a.html');
});
it('returns correct relative path for parent directory', () => {
expect(utils.relUrlPath('/path/to/a', '/path/b', opts)).toEqual('to/a.html');
});
it('returns toPath if it is full url', () => {
expect(utils.relUrlPath('https://fractal.build', '/path/b', opts)).toEqual('https://fractal.build');
});
it('returns toPath with extension if it is already a relative path', () => {
expect(utils.relUrlPath('./path/to/a', '/path/b', opts)).toEqual('./path/to/a.html');
});
it('returns toPath without extension if it is already a relative path but no ext in opts', () => {
expect(utils.relUrlPath('./path/to/a', '/path/b', {})).toEqual('./path/to/a');
});
it('returns correct path to root', () => {
expect(utils.relUrlPath('/', '/path/b', {})).toEqual('../..');
});
it('returns correct path to root with extension', () => {
expect(utils.relUrlPath('/', '/path/b', opts)).toEqual('../index.html');
});
it('returns correct path to file with extension', () => {
expect(utils.relUrlPath('/path/to/image.png', '/path/b', opts)).toEqual('to/image.png');
});
it('returns correct relative path to file with extension', () => {
expect(utils.relUrlPath('../to/image.png', '/path/b', opts)).toEqual('../to/image.png');
});
// for static builds
const opts2 = {
ext: '.html',
relativeToCurrentFolder: true,
};
const opts3 = {
relativeToCurrentFolder: true,
};
it('returns correct relative path from current directory for same directory', () => {
expect(utils.relUrlPath('/path/to/a', '/path/to/b', opts2)).toEqual('./a.html');
});
it('returns correct relative path from the current directory for parent directory', () => {
expect(utils.relUrlPath('/path/to/a', '/path/b', opts2)).toEqual('./to/a.html');
});
it('returns toPath if it is full url even if `relativeToCurrentFolder` is true', () => {
expect(utils.relUrlPath('https://fractal.build', '/path/b', opts2)).toEqual('https://fractal.build');
});
it('returns toPath with extension if it is already a path from current directory', () => {
expect(utils.relUrlPath('./path/to/a', '/path/b', opts2)).toEqual('./path/to/a.html');
});
it('returns toPath without extension if it is already a relative path from current directory', () => {
expect(utils.relUrlPath('./path/to/a', '/path/b', opts3)).toEqual('./path/to/a');
});
it('returns correct path to root from current directory', () => {
expect(utils.relUrlPath('/', '/path/b', opts3)).toEqual('../..');
});
it('returns correct path to root with extension from current directory', () => {
expect(utils.relUrlPath('/', '/path/b', opts2)).toEqual('../index.html');
});
it('returns self with extension if it is already a path from self', () => {
expect(utils.relUrlPath('/path/to/a', '/path/to/a', opts2)).toEqual('./a.html');
});
it('returns correct path to file with extension from current directory', () => {
expect(utils.relUrlPath('/path/to/image.png', '/path/b', opts2)).toEqual('./to/image.png');
});
it('returns correct relative path to file with extension from current directory', () => {
expect(utils.relUrlPath('../to/image.png', '/path/b', opts2)).toEqual('../to/image.png');
});
});
});
class MyClass {
constructor() {
this.foo = 'MyClass';
}
}
class MyOtherClass {
constructor() {
this.foo = 'MyOtherClass';
}
}