diff --git a/README.md b/README.md
index d4d2c61..c80cf91 100644
--- a/README.md
+++ b/README.md
@@ -45,6 +45,8 @@ File.isCustomProp('path'); // false -> internal getter/setter
Read more in [Extending Vinyl](#extending-vinyl).
### constructor(options)
+All internally managed paths (`cwd`, `base`, `path`, `[history]`) are normalized and remove a trailing separator.
+
#### options.cwd
Type: `String`
Default: `process.cwd()`
@@ -64,7 +66,7 @@ Path history. Has no effect if `options.path` is passed.
Type: `Array`
Default: `options.path ? [options.path] : []`
#### options.stat
-The result of an fs.stat call. See [fs.Stats](http://nodejs.org/api/fs.html#fs_class_fs_stats) for more information.
+The result of an fs.stat call. This is how you mark the file as a directory. See [isDirectory()](#isDirectory) and [fs.Stats](http://nodejs.org/api/fs.html#fs_class_fs_stats) for more information.
Type: `fs.Stats`
Default: `null`
@@ -92,6 +94,15 @@ Returns true if file.contents is a Stream.
### isNull()
Returns true if file.contents is null.
+### isDirectory()
+Returns true if file is a directory. File is considered a directory when:
+
+- `file.isNull()` is `true`
+- `file.stat` is an object
+- `file.stat.isDirectory()` returns `true`
+
+When constructing a Vinyl object, pass in a valid `fs.Stats` object via `options.stat`. Some operations in Vinyl might need to know the file is a directory from the get go. If you are mocking the `fs.Stats` object, ensure it has the `isDirectory()` method.
+
### clone([opt])
Returns a new File object with all attributes cloned.
By default custom attributes are deep-cloned.
@@ -124,8 +135,14 @@ if (file.isBuffer()) {
}
```
+### cwd
+Gets and sets current working directory. Defaults to `process.cwd`. Will always be normalized and remove a trailing separator.
+
+### base
+Gets and sets base directory. Used for relative pathing (typically where a glob starts). When `null` or `undefined`, it simply proxies the `file.cwd` property. Will always be normalized and remove a trailing separator.
+
### path
-Absolute pathname string or `undefined`. Setting to a different value pushes the old value to `history`.
+Absolute pathname string or `undefined`. Setting to a different value pushes the old value to `history`. All new values are normalized and remove a trailing separator.
### history
Array of `path` values the file object has had, from `history[0]` (original) through `history[history.length - 1]` (current). `history` and its elements should normally be treated as read-only and only altered indirectly by setting `path`.
@@ -146,7 +163,7 @@ console.log(file.relative); // file.coffee
```
### dirname
-Gets and sets path.dirname for the file path.
+Gets and sets path.dirname for the file path. Will always be normalized and remove a trailing separator.
Example:
@@ -225,6 +242,24 @@ console.log(file.extname); // .js
console.log(file.path); // /test/file.js
```
+### symlink
+Path where the file points to in case it's a symbolic link. Will always be normalized and remove a trailing separator.
+
+## Normalization and concatenation
+Since all properties are normalized in their setters, you can just concatenate with `/`, and normalization takes care of it properly on all platforms.
+
+Example:
+
+```javascript
+var file = new File();
+file.path = '/' + 'test' + '/' + 'foo.bar';
+console.log(file.path);
+// posix => /test/foo.bar
+// win32 => \\test\\foo.bar
+```
+
+But never concatenate with `\`, since that is a valid filename character on posix system.
+
## Extending Vinyl
When extending Vinyl into your own class with extra features, you need to think about a few things.
diff --git a/index.js b/index.js
index 0cdbdb3..975f36c 100644
--- a/index.js
+++ b/index.js
@@ -2,15 +2,18 @@ var path = require('path');
var clone = require('clone');
var cloneStats = require('clone-stats');
var cloneBuffer = require('./lib/cloneBuffer');
+var stripTrailingSep = require('./lib/stripTrailingSep');
var isBuffer = require('./lib/isBuffer');
var isStream = require('./lib/isStream');
var isNull = require('./lib/isNull');
var inspectStream = require('./lib/inspectStream');
+var normalize = require('./lib/normalize');
var Stream = require('stream');
var replaceExt = require('replace-ext');
var builtInFields = [
- '_contents', '_symlink', 'contents', 'stat', 'history', 'path', 'base', 'cwd',
+ '_contents', '_symlink', 'contents', 'stat', 'history', 'path',
+ '_base', 'base', '_cwd', 'cwd',
];
function File(file) {
@@ -20,19 +23,22 @@ function File(file) {
file = {};
}
- // Record path change
- var history = file.path ? [file.path] : file.history;
- this.history = history || [];
-
- this.cwd = file.cwd || process.cwd();
- this.base = file.base || this.cwd;
-
// Stat = files stats object
this.stat = file.stat || null;
// Contents = stream, buffer, or null if not read
this.contents = file.contents || null;
+ // Replay path history to ensure proper normalization and trailing sep
+ var history = file.path ? [file.path] : file.history || [];
+ this.history = [];
+ history.forEach(function(path) {
+ self.path = path;
+ });
+
+ this.cwd = file.cwd || process.cwd();
+ this.base = file.base;
+
this._isVinyl = true;
this._symlink = null;
@@ -156,7 +162,7 @@ File.prototype.inspect = function() {
var inspect = [];
// Use relative path if possible
- var filePath = (this.base && this.path) ? this.relative : this.path;
+ var filePath = this.path ? this.relative : null;
if (filePath) {
inspect.push('"' + filePath + '"');
@@ -195,12 +201,40 @@ Object.defineProperty(File.prototype, 'contents', {
},
});
+Object.defineProperty(File.prototype, 'cwd', {
+ get: function() {
+ return this._cwd;
+ },
+ set: function(cwd) {
+ if (!cwd || typeof cwd !== 'string') {
+ throw new Error('cwd must be a non-empty string.');
+ }
+ this._cwd = stripTrailingSep(normalize(cwd));
+ },
+});
+
+Object.defineProperty(File.prototype, 'base', {
+ get: function() {
+ return this._base || this._cwd;
+ },
+ set: function(base) {
+ if (base == null) {
+ delete this._base;
+ return;
+ }
+ if (typeof base !== 'string' || !base) {
+ throw new Error('base must be a non-empty string, or null/undefined.');
+ }
+ base = stripTrailingSep(normalize(base));
+ if (base !== this._cwd) {
+ this._base = base;
+ }
+ },
+});
+
// TODO: Should this be moved to vinyl-fs?
Object.defineProperty(File.prototype, 'relative', {
get: function() {
- if (!this.base) {
- throw new Error('No base specified! Can not get relative.');
- }
if (!this.path) {
throw new Error('No path specified! Can not get relative.');
}
@@ -222,7 +256,7 @@ Object.defineProperty(File.prototype, 'dirname', {
if (!this.path) {
throw new Error('No path specified! Can not set dirname.');
}
- this.path = path.join(dirname, path.basename(this.path));
+ this.path = path.join(dirname, this.basename);
},
});
@@ -237,7 +271,7 @@ Object.defineProperty(File.prototype, 'basename', {
if (!this.path) {
throw new Error('No path specified! Can not set basename.');
}
- this.path = path.join(path.dirname(this.path), basename);
+ this.path = path.join(this.dirname, basename);
},
});
@@ -253,7 +287,7 @@ Object.defineProperty(File.prototype, 'stem', {
if (!this.path) {
throw new Error('No path specified! Can not set stem.');
}
- this.path = path.join(path.dirname(this.path), stem + this.extname);
+ this.path = path.join(this.dirname, stem + this.extname);
},
});
@@ -278,8 +312,9 @@ Object.defineProperty(File.prototype, 'path', {
},
set: function(path) {
if (typeof path !== 'string') {
- throw new Error('path should be string');
+ throw new Error('path should be a string.');
}
+ path = stripTrailingSep(normalize(path));
// Record history only when path changed
if (path && path !== this.path) {
@@ -298,7 +333,7 @@ Object.defineProperty(File.prototype, 'symlink', {
throw new Error('symlink should be a string');
}
- this._symlink = symlink;
+ this._symlink = stripTrailingSep(normalize(symlink));
},
});
diff --git a/lib/normalize.js b/lib/normalize.js
new file mode 100644
index 0000000..5bb5537
--- /dev/null
+++ b/lib/normalize.js
@@ -0,0 +1,5 @@
+var normalize = require('path').normalize;
+
+module.exports = function(str) {
+ return str === '' ? str : normalize(str);
+};
diff --git a/lib/stripTrailingSep.js b/lib/stripTrailingSep.js
new file mode 100644
index 0000000..ecc3765
--- /dev/null
+++ b/lib/stripTrailingSep.js
@@ -0,0 +1,11 @@
+module.exports = function(str) {
+ while (endsInSeparator(str)) {
+ str = str.slice(0, -1);
+ }
+ return str;
+};
+
+function endsInSeparator(str) {
+ var last = str[str.length - 1];
+ return str.length > 1 && (last === '/' || last === '\\');
+}
diff --git a/test/File.js b/test/File.js
index 1aaebc7..351f147 100644
--- a/test/File.js
+++ b/test/File.js
@@ -31,7 +31,7 @@ describe('File', function() {
});
it('should default base to cwd', function(done) {
- var cwd = '/';
+ var cwd = path.normalize('/');
var file = new File({ cwd: cwd });
file.base.should.equal(cwd);
done();
@@ -68,21 +68,21 @@ describe('File', function() {
});
it('should set base to given value', function(done) {
- var val = '/';
+ var val = path.normalize('/');
var file = new File({ base: val });
file.base.should.equal(val);
done();
});
it('should set cwd to given value', function(done) {
- var val = '/';
+ var val = path.normalize('/');
var file = new File({ cwd: val });
file.cwd.should.equal(val);
done();
});
it('should set path to given value', function(done) {
- var val = '/test.coffee';
+ var val = path.normalize('/test.coffee');
var file = new File({ path: val });
file.path.should.equal(val);
file.history.should.eql([val]);
@@ -90,7 +90,7 @@ describe('File', function() {
});
it('should set history to given value', function(done) {
- var val = '/test.coffee';
+ var val = path.normalize('/test.coffee');
var file = new File({ history: [val] });
file.path.should.equal(val);
file.history.should.eql([val]);
@@ -117,6 +117,71 @@ describe('File', function() {
file.sourceMap.should.equal(sourceMap);
done();
});
+
+ it('should normalize path', function() {
+ var file = new File({ path: '/test/foo/../test.coffee' });
+
+ if (process.platform === 'win32') {
+ file.path.should.equal('\\test\\test.coffee');
+ file.history.should.eql(['\\test\\test.coffee']);
+ } else {
+ file.path.should.equal('/test/test.coffee');
+ file.history.should.eql(['/test/test.coffee']);
+ }
+ });
+
+ it('should correctly normalize and strip trailing sep from path', function() {
+ var file = new File({ path: '/test/foo/../foo/' });
+
+ if (process.platform === 'win32') {
+ file.path.should.equal('\\test\\foo');
+ } else {
+ file.path.should.equal('/test/foo');
+ }
+ });
+
+ it('should correctly normalize and strip trailing sep from history', function() {
+ var file = new File({
+ history: [
+ '/test/foo/../foo/',
+ '/test/bar/../bar/',
+ ],
+ });
+
+ if (process.platform === 'win32') {
+ file.history.should.eql([
+ '\\test\\foo',
+ '\\test\\bar',
+ ]);
+ } else {
+ file.history.should.eql([
+ '/test/foo',
+ '/test/bar',
+ ]);
+ }
+ });
+
+ it('should normalize history', function() {
+ var history = [
+ '/test/bar/../bar/test.coffee',
+ '/test/foo/../test.coffee',
+ ];
+ var file = new File({ history: history });
+
+ if (process.platform === 'win32') {
+ file.path.should.equal('\\test\\test.coffee');
+ file.history.should.eql([
+ '\\test\\bar\\test.coffee',
+ '\\test\\test.coffee',
+ ]);
+ } else {
+ file.path.should.equal('/test/test.coffee');
+ file.history.should.eql([
+ '/test/bar/test.coffee',
+ '/test/test.coffee',
+ ]);
+ }
+ });
});
describe('isBuffer()', function() {
@@ -367,9 +432,9 @@ describe('File', function() {
it('should properly clone the `history` property', function(done) {
var options = {
- cwd: '/',
- base: '/test/',
- path: '/test/test.js',
+ cwd: path.normalize('/'),
+ base: path.normalize('/test/'),
+ path: path.normalize('/test/test.js'),
contents: new Buffer('test'),
stat: fs.statSync(__filename),
};
@@ -413,23 +478,20 @@ describe('File', function() {
path: '/test/test.coffee',
contents: null,
};
+ var history = [
+ path.normalize('/test/test.coffee'),
+ path.normalize('/test/test.js'),
+ path.normalize('/test/test-938di2s.js'),
+ ];
var file = new File(options);
- file.path = '/test/test.js';
- file.path = '/test/test-938di2s.js';
+ file.path = history[1];
+ file.path = history[2];
var file2 = file.clone();
- file2.history.should.eql([
- '/test/test.coffee',
- '/test/test.js',
- '/test/test-938di2s.js',
- ]);
- file2.history.should.not.equal([
- '/test/test.coffee',
- '/test/test.js',
- '/test/test-938di2s.js',
- ]);
- file2.path.should.eql('/test/test-938di2s.js');
+ file2.history.should.eql(history);
+ file2.history.should.not.equal(history);
+ file2.path.should.eql(history[2]);
done();
});
@@ -652,18 +714,6 @@ describe('File', function() {
done();
});
- it('should return correct format when Buffer and only path and no base', function(done) {
- var val = new Buffer('test');
- var file = new File({
- cwd: '/',
- path: '/test/test.coffee',
- contents: val,
- });
- delete file.base;
- file.inspect().should.equal('>');
- done();
- });
-
it('should return correct format when Stream and relative path', function(done) {
var file = new File({
cwd: '/',
@@ -724,23 +774,136 @@ describe('File', function() {
});
});
- describe('relative get/set', function() {
- it('should error on set', function(done) {
+ describe('cwd get/set', function() {
+ it('should return _cwd', function() {
var file = new File();
- try {
- file.relative = 'test';
- } catch (err) {
- should.exist(err);
- done();
+ file.cwd = '/test';
+ file.cwd.should.equal(file._cwd);
+ });
+
+ it('should set cwd', function() {
+ var file = new File();
+ file.cwd = '/test';
+ file._cwd.should.equal(path.normalize('/test'));
+ });
+
+ it('should normalize and strip trailing sep on set', function() {
+ var file = new File();
+
+ file.cwd = '/test/foo/../foo/';
+
+ if (process.platform === 'win32') {
+ file.cwd.should.equal('\\test\\foo');
+ } else {
+ file.cwd.should.equal('/test/foo');
+ }
+
+ file.cwd = '\\test\\foo\\..\\foo\\';
+
+ if (process.platform === 'win32') {
+ file.cwd.should.equal('\\test\\foo');
+ } else {
+ file.cwd.should.equal('\\test\\foo\\..\\foo');
}
});
- it('should error on get when no base', function(done) {
- var a;
+ it('should throw on set when value is empty or not a string', function() {
+ var notAllowed = [
+ '', null, undefined, true, false, 0, Infinity, NaN, {}, [],
+ ];
+ notAllowed.forEach(function(val) {
+ (function() {
+ new File().cwd = val;
+ }).should.throw('cwd must be a non-empty string.');
+ });
+ });
+ });
+
+ describe('base get/set', function() {
+ it('should proxy to cwd when omitted', function() {
+ var file = new File({
+ cwd: '/test',
+ });
+ file.base.should.equal(path.normalize('/test'));
+ });
+
+ it('should proxy to cwd when same', function() {
+ var file = new File({
+ cwd: '/test',
+ base: '/test',
+ });
+ file.cwd = '/foo/';
+ file.base.should.equal(path.normalize('/foo'));
+
+ var file2 = new File({
+ cwd: '/test',
+ });
+ file2.base = '/test/';
+ file2.cwd = '/foo/';
+ file2.base.should.equal(path.normalize('/foo'));
+ });
+
+ it('should proxy to cwd when null or undefined', function() {
+ var file = new File({
+ cwd: '/foo',
+ base: '/bar',
+ });
+ file.base.should.equal(path.normalize('/bar'));
+ file.base = null;
+ file.base.should.equal(path.normalize('/foo'));
+ file.base = '/bar/';
+ file.base.should.equal(path.normalize('/bar'));
+ file.base = undefined;
+ file.base.should.equal(path.normalize('/foo'));
+ });
+
+ it('should return _base', function() {
+ var file = new File();
+ file._base = '/test/';
+ file.base.should.equal('/test/');
+ });
+
+ it('should set base', function() {
+ var file = new File();
+ file.base = '/test/foo';
+ file.base.should.equal(path.normalize('/test/foo'));
+ });
+
+ it('should normalize and strip trailing sep on set', function() {
+ var file = new File();
+
+ file.base = '/test/foo/../foo/';
+
+ if (process.platform === 'win32') {
+ file.base.should.equal('\\test\\foo');
+ } else {
+ file.base.should.equal('/test/foo');
+ }
+
+ file.base = '\\test\\foo\\..\\foo\\';
+
+ if (process.platform === 'win32') {
+ file.base.should.equal('\\test\\foo');
+ } else {
+ file.base.should.equal('\\test\\foo\\..\\foo');
+ }
+ });
+
+ it('should throw on set when not null/undefined or a non-empty string', function() {
+ var notStrings = [true, false, 1, 0, Infinity, NaN, '', {}, []];
+ notStrings.forEach(function(val) {
+ (function() {
+ new File().base = val;
+ }).should.throw('base must be a non-empty string, or null/undefined.');
+ });
+ });
+ });
+
+ describe('relative get/set', function() {
+ it('should error on set', function(done) {
var file = new File();
- delete file.base;
try {
- a = file.relative;
+ file.relative = 'test';
} catch (err) {
should.exist(err);
done();
@@ -776,6 +939,35 @@ describe('File', function() {
file.relative.should.equal(path.join('test','test.coffee'));
done();
});
+
+ it('should not append sep when directory', function() {
+ var file = new File({
+ base: '/test',
+ path: '/test/foo/bar',
+ stat: {
+ isDirectory: function() {
+ return true;
+ },
+ },
+ });
+ file.relative.should.equal(path.normalize('foo/bar'));
+ });
+
+ it('should not append sep when directory & simlink', function() {
+ var file = new File({
+ base: '/test',
+ path: '/test/foo/bar',
+ stat: {
+ isDirectory: function() {
+ return true;
+ },
+ isSymbolicLink: function() {
+ return true;
+ },
+ },
+ });
+ file.relative.should.equal(path.normalize('foo/bar'));
+ });
});
describe('dirname get/set', function() {
@@ -790,13 +982,13 @@ describe('File', function() {
}
});
- it('should return the dirname of the path', function(done) {
+ it('should return the path without trailing sep', function(done) {
var file = new File({
cwd: '/',
- base: '/test/',
+ base: '/test',
path: '/test/test.coffee',
});
- file.dirname.should.equal('/test');
+ file.dirname.should.equal(path.normalize('/test'));
done();
});
@@ -817,7 +1009,7 @@ describe('File', function() {
path: '/test/test.coffee',
});
file.dirname = '/test/foo';
- file.path.should.equal('/test/foo/test.coffee');
+ file.path.should.equal(path.normalize('/test/foo/test.coffee'));
done();
});
});
@@ -844,6 +1036,41 @@ describe('File', function() {
done();
});
+ it('should not append trailing sep', function() {
+ var file = new File({
+ path: '/test/foo',
+ stat: {
+ isDirectory: function() {
+ return true;
+ },
+ },
+ });
+ file.basename.should.equal('foo');
+
+ var file2 = new File({
+ path: '/test/foo',
+ stat: {
+ isSymbolicLink: function() {
+ return true;
+ },
+ },
+ });
+ file2.basename.should.equal('foo');
+
+ var file3 = new File({
+ path: '/test/foo',
+ stat: {
+ isDirectory: function() {
+ return true;
+ },
+ isSymbolicLink: function() {
+ return true;
+ },
+ },
+ });
+ file3.basename.should.equal('foo');
+ });
+
it('should error on set when no path', function(done) {
var file = new File();
try {
@@ -861,7 +1088,7 @@ describe('File', function() {
path: '/test/test.coffee',
});
file.basename = 'foo.png';
- file.path.should.equal('/test/foo.png');
+ file.path.should.equal(path.normalize('/test/foo.png'));
done();
});
});
@@ -905,7 +1132,7 @@ describe('File', function() {
path: '/test/test.coffee',
});
file.stem = 'foo';
- file.path.should.equal('/test/foo.coffee');
+ file.path.should.equal(path.normalize('/test/foo.coffee'));
done();
});
});
@@ -949,21 +1176,21 @@ describe('File', function() {
path: '/test/test.coffee',
});
file.extname = '.png';
- file.path.should.equal('/test/test.png');
+ file.path.should.equal(path.normalize('/test/test.png'));
done();
});
});
describe('path get/set', function() {
-
it('should record history when instantiation', function() {
var file = new File({
cwd: '/',
path: '/test/test.coffee',
});
+ var history = [path.normalize('/test/test.coffee')];
- file.path.should.eql('/test/test.coffee');
- file.history.should.eql(['/test/test.coffee']);
+ file.path.should.eql(history[0]);
+ file.history.should.eql(history);
});
it('should record history when path change', function() {
@@ -971,31 +1198,38 @@ describe('File', function() {
cwd: '/',
path: '/test/test.coffee',
});
+ var history = [
+ path.normalize('/test/test.coffee'),
+ path.normalize('/test/test.js'),
+ ];
+
+ file.path = history[history.length - 1];
+ file.path.should.eql(history[history.length - 1]);
+ file.history.should.eql(history);
- file.path = '/test/test.js';
- file.path.should.eql('/test/test.js');
- file.history.should.eql(['/test/test.coffee', '/test/test.js']);
+ history.push(path.normalize('/test/test.es6'));
- file.path = '/test/test.coffee';
- file.path.should.eql('/test/test.coffee');
- file.history.should.eql(['/test/test.coffee', '/test/test.js', '/test/test.coffee']);
+ file.path = history[history.length - 1];
+ file.path.should.eql(history[history.length - 1]);
+ file.history.should.eql(history);
});
it('should not record history when set the same path', function() {
+ var val = path.normalize('/test/test.coffee');
var file = new File({
cwd: '/',
- path: '/test/test.coffee',
+ path: val,
});
- file.path = '/test/test.coffee';
- file.path = '/test/test.coffee';
- file.path.should.eql('/test/test.coffee');
- file.history.should.eql(['/test/test.coffee']);
+ file.path = val;
+ file.path = val;
+ file.path.should.eql(val);
+ file.history.should.eql([val]);
// Ignore when set empty string
file.path = '';
- file.path.should.eql('/test/test.coffee');
- file.history.should.eql(['/test/test.coffee']);
+ file.path.should.eql(val);
+ file.history.should.eql([val]);
});
it('should throw when set path null', function() {
@@ -1009,7 +1243,39 @@ describe('File', function() {
(function() {
file.path = null;
- }).should.throw('path should be string');
+ }).should.throw('path should be a string.');
+ });
+
+ it('should normalize the path on set', function() {
+ var file = new File();
+
+ file.path = '/test/foo/../test.coffee';
+
+ if (process.platform === 'win32') {
+ file.path.should.equal('\\test\\test.coffee');
+ file.history.should.eql(['\\test\\test.coffee']);
+ } else {
+ file.path.should.equal('/test/test.coffee');
+ file.history.should.eql(['/test/test.coffee']);
+ }
+ });
+
+ it('should strip trailing sep', function() {
+ var file = new File();
+ file.path = '/test/';
+ file.path.should.eql(path.normalize('/test'));
+ file.history.should.eql([path.normalize('/test')]);
+
+ var file2 = new File({
+ stat: {
+ isDirectory: function() {
+ return true;
+ },
+ },
+ });
+ file2.path = '/test/';
+ file2.path.should.eql(path.normalize('/test'));
+ file2.history.should.eql([path.normalize('/test')]);
});
});
@@ -1025,7 +1291,7 @@ describe('File', function() {
var file = new File({
symlink: '/test/test.coffee',
});
- file.symlink.should.equal('/test/test.coffee');
+ file.symlink.should.equal(path.normalize('/test/test.coffee'));
done();
});
@@ -1042,15 +1308,27 @@ describe('File', function() {
it('should set the symlink', function(done) {
var file = new File();
file.symlink = '/test/test.coffee';
- file.symlink.should.equal('/test/test.coffee');
+ file.symlink.should.equal(path.normalize('/test/test.coffee'));
done();
});
it('should set the relative symlink', function(done) {
var file = new File();
- file.symlink = './test.coffee';
- file.symlink.should.equal('./test.coffee');
+ file.symlink = 'test.coffee';
+ file.symlink.should.equal('test.coffee');
done();
});
+
+ it('should be normalized and stripped off a trailing sep on set', function() {
+ var file = new File();
+
+ file.symlink = '/test/foo/../bar/';
+
+ if (process.platform === 'win32') {
+ file.symlink.should.equal('\\test\\bar');
+ } else {
+ file.symlink.should.equal('/test/bar');
+ }
+ });
});
});
diff --git a/test/normalize.js b/test/normalize.js
new file mode 100644
index 0000000..2bfa9a2
--- /dev/null
+++ b/test/normalize.js
@@ -0,0 +1,15 @@
+var normalize = require('../lib/normalize');
+var path = require('path');
+require('should');
+require('mocha');
+
+describe('normalize()', function() {
+ it('should leave empty strings unmodified', function() {
+ normalize('').should.equal('');
+ });
+
+ it('should apply path.normalize for everything else', function() {
+ var str = '/foo//../bar/baz';
+ normalize(str).should.equal(path.normalize(str));
+ });
+});
diff --git a/test/stripTrailingSep.js b/test/stripTrailingSep.js
new file mode 100644
index 0000000..2a432ce
--- /dev/null
+++ b/test/stripTrailingSep.js
@@ -0,0 +1,36 @@
+var stripTrailingSep = require('../lib/stripTrailingSep');
+require('should');
+require('mocha');
+
+describe('stripTrailingSep()', function() {
+ it('should strip trailing separator', function() {
+ stripTrailingSep('foo/').should.equal('foo');
+ stripTrailingSep('foo\\').should.equal('foo');
+ });
+
+ it('should not strip when the only char in the string', function() {
+ stripTrailingSep('/').should.equal('/');
+ stripTrailingSep('\\').should.equal('\\');
+ });
+
+ it('should strip only the trailing separator', function() {
+ stripTrailingSep('/test/foo/bar/').should.equal('/test/foo/bar');
+ stripTrailingSep('\\test\\foo\\bar\\').should.equal('\\test\\foo\\bar');
+ });
+
+ it('should strip multiple trailing separators', function() {
+ stripTrailingSep('/test//').should.equal('/test');
+ stripTrailingSep('\\test\\\\').should.equal('\\test');
+ });
+
+ it('should leave 1st separator in a string of only separators', function() {
+ stripTrailingSep('//').should.equal('/');
+ stripTrailingSep('////').should.equal('/');
+ stripTrailingSep('\\\\').should.equal('\\');
+ stripTrailingSep('\\\\\\\\').should.equal('\\');
+ });
+
+ it('should return back empty string', function() {
+ stripTrailingSep('').should.equal('');
+ });
+});