UNPKG

file-js

Version:

Abstract representation of a pathname

768 lines (652 loc) 22.8 kB
import { assert } from 'chai'; import * as fs from 'fs'; import * as moment from 'moment'; import * as path from 'path'; import * as sinon from 'sinon'; import { File } from '../src/file'; import * as fsp from '../src/fsp'; import { TestStats } from './testStats'; const sandbox = sinon.sandbox.create(); interface DateOpts { duration?: any; modifier?: string; } function getFixturePath(file: string): string { return path.join(`${__dirname}/fixtures/`, file); } function getAbsolutePath(dir: string): string { return `${process.cwd()}/${dir}`; } function getCanonicalPath(relativePath: string): string { return path.normalize(getAbsolutePath(relativePath)); } function qualifyNames(names: string[]): string[] { return names.map(getFixturePath); } function formatDate(date: moment.Moment): string { return date.format('DD/MM/YYYY'); } function createFile(fname: string, opts: DateOpts): void { const time = new Date(moment().subtract(opts.duration, opts.modifier).toString()); const fd = fs.openSync(fname, 'w+'); fs.futimesSync(fd, time, time); fs.closeSync(fd); } function deleteFile(fname: string): void { fs.unlinkSync(fname); } function createFileStructure(fixturePath: string, depth: number = 5): void { let filePath = getFixturePath(fixturePath); for (let i = 0; i < depth; i++) { filePath = `${filePath}/subDir_${i}`; const subDirPath = filePath.split('fixtures', filePath.length)[1]; const subFile = getFixturePath(`${subDirPath}/subFile_${i}.txt`); const anotherSubFile = getFixturePath(`${subDirPath}/anotherSubFile_${i}.txt`); fs.mkdirSync(filePath); createFile(subFile, { duration: 1, modifier: 'hours' }); createFile(anotherSubFile, { duration: 1, modifier: 'hours' }); } } describe('File', () => { afterEach(() => { sandbox.restore(); }); describe('.isDirectorySync', () => { it('returns true when a pathname is a directory', () => { const file = new File(getFixturePath('/justFiles')); assert.isTrue(file.isDirectorySync()); }); it('returns false when a pathname is not a directory', () => { const file = new File(getFixturePath('/justFiles/a.json')); assert(!file.isDirectorySync()); }); }); describe('.isDirectory', () => { it('returns true when a pathname is a directory', async () => { const file = new File(getFixturePath('/justFiles')); const isDirectory = await file.isDirectory(); assert.isTrue(isDirectory); }); it('returns false when a pathname is not a directory', async () => { const file = new File(getFixturePath('/justFiles/a.json')); const isDirectory = await file.isDirectory(); assert.isFalse(isDirectory); }); }); describe('.isSocketSync', () => { it('returns true when a pathname is a socket', () => { sandbox.stub(fs, 'statSync').returns(new TestStats()); const file = new File(getFixturePath('/types/mySocketfile')); assert(file.isSocketSync()); }); it('returns false when a pathname is not a socket', () => { const file = new File(getFixturePath('/justFiles/a.json')); assert(!file.isSocketSync()); }); }); describe('.isSocket', () => { it('returns true when a pathname is a Socket', async () => { sandbox.stub(fsp, 'stat').resolves(new TestStats()); const file = new File(getFixturePath('/types/mySocketfile')); const isSocket = await file.isSocket(); assert.isTrue(isSocket); }); it('returns false when a pathname is not a Socket', async () => { const file = new File(getFixturePath('/justFiles/a.json')); const isSocket = await file.isSocket(); assert.isFalse(isSocket); }); }); describe('.isFileSync', () => { it('returns true when a pathname is a file', () => { const file = new File(getFixturePath('/justFiles/a.json')); assert(file.isFileSync()); }); it('returns false when a pathname is not a file', () => { const file = new File(getFixturePath('/justFiles')); assert(!file.isFileSync()); }); }); describe('.isFile', () => { it('returns true when a pathname is a file', async () => { const file = new File(getFixturePath('/justFiles/a.json')); const isFile = await file.isFile(); assert.isTrue(isFile); }); it('returns false when a pathname is not a file', async () => { const file = new File(getFixturePath('/justFiles')); const isFile = await file.isFile(); assert.isFalse(isFile); }); }); describe('.getListSync', () => { it('returns a list of files for a given directory', () => { const file = new File(getFixturePath('/justFiles')); const files = file.getListSync(); const expected = qualifyNames([ 'justFiles/a.json', 'justFiles/b.json', 'justFiles/dummy.txt' ]); assert.deepEqual(files, expected); }); it('returns null when pathname is not a directory', () => { const file = new File(getFixturePath('/justFiles/a.json')); const files = file.getListSync(); assert.strictEqual(files, null); }); }); describe('.getFiles', () => { it('returns a list of files objects for a given directory', async () => { const file = new File(getFixturePath('/justFiles')); const files = await file.getFiles(); const expected = qualifyNames([ 'justFiles/a.json', 'justFiles/b.json', 'justFiles/dummy.txt' ]).map(pathname => new File(pathname)); assert.deepEqual(files, expected); }); it('returns a list of files using a file glob', async () => { const file = new File(getFixturePath('/justFiles')); const files = await file.getFiles('*.json'); const expected = qualifyNames([ 'justFiles/a.json', 'justFiles/b.json' ]).map(pathname => new File(pathname)); assert.deepEqual(files, expected); }); it('returns an empty array when pathname is not a directory', async () => { const file = new File(getFixturePath('/justFiles/a.json')); const files = await file.getFiles(); assert.deepEqual(files, []); }); }); describe('.getFilesSync', () => { it('returns a list of files objects for a given directory', () => { const file = new File(getFixturePath('/justFiles')); const files = file.getFilesSync(); const expected = qualifyNames([ 'justFiles/a.json', 'justFiles/b.json', 'justFiles/dummy.txt' ]).map(pathname => new File(pathname)); assert.deepEqual(files, expected); }); it('returns a list of files using a file glob', () => { const file = new File(getFixturePath('/justFiles')); const files = file.getFilesSync('*.json'); const expected = qualifyNames([ 'justFiles/a.json', 'justFiles/b.json' ]).map(pathname => new File(pathname)); assert.deepEqual(files, expected); }); it('returns null when pathname is not a directory', () => { const file = new File(getFixturePath('/justFiles/a.json')); const files = file.getFilesSync(); assert.strictEqual(files, null); }); }); describe('.getList', () => { it('returns a list of files for a given directory', async () => { const file = new File(getFixturePath('/justFiles')); const files = await file.getList(); const expected = qualifyNames([ 'justFiles/a.json', 'justFiles/b.json', 'justFiles/dummy.txt' ]); assert.deepEqual(files, expected); }); it('returns an empty array when pathname is not a directory', async () => { const file = new File(getFixturePath('/justFiles/a.json')); const files = await file.getList(); assert.deepEqual(files, []); }); }); describe('.isHiddenSync', () => { it('returns true when the file is hidden', () => { const hiddenPaths = [ './test/fixtures/visibility/.hidden.json', './test/fixtures/visibility/.hidden/.hidden.json' ]; hiddenPaths.forEach((hiddenPath) => { const file = new File(hiddenPath); assert.strictEqual(file.isHiddenSync(), true); }); }); it('returns false when the file is visible', () => { const visiblePaths = [ './test/fixtures/visibility/visible.json', './test/fixtures/visibility/.hidden/visible.json', './test/fixtures/visibility/visible' ]; visiblePaths.forEach((visiblePath) => { const file = new File(visiblePath); assert.strictEqual(file.isHiddenSync(), false); }); }); }); describe('.isHidden', () => { it('returns true when the file is hidden', async () => { const file = new File('./test/fixtures/visibility/.hidden.json'); const isHidden = await file.isHidden(); assert.isTrue(isHidden); }); it('returns false when the file is visible', async () => { const file = new File('./test/fixtures/visibility/'); const isHidden = await file.isHidden(); assert.isFalse(isHidden); }); }); describe('.getDepthSync', () => { it('returns the depth of a directory', () => { const file = new File('./test/fixtures/justFiles'); assert.equal(file.getDepthSync(), 3); }); it('returns the depth of a file', () => { const file = new File('./test/fixtures/justFiles/a.json'); assert.equal(file.getDepthSync(), 3); }); }); describe('.getPathExtension', () => { it('returns the extension for a file', () => { const file = new File(getFixturePath('/justFiles/a.json')); assert.equal(file.getPathExtension(), 'json'); }); it('returns the extension for a directory', () => { const file = new File(getFixturePath('/test.d')); assert.equal(file.getPathExtension(), 'd'); }); }); describe('.isMatch', () => { it('returns true if the pathname is a match for a given glob', async () => { const paths = [ ['./test/fixtures/justFiles/a.json', '*.json'], ['./test/fixtures/justFiles', '*justFiles*'] ]; paths.forEach(async (testCase) => { const [pathname, glob] = testCase; const file = new File(pathname); assert.isTrue(await file.isMatch(glob)); }); }); it('returns false if the pathname is not a match for a given glob', async () => { const paths = [ ['./test/fixtures/justFiles/a.txt', '*.json'], ['./test/fixtures', '*justFiles*'] ]; paths.forEach(async (testCase) => { const [pathname, glob] = testCase; const file = new File(pathname); assert.isFalse(await file.isMatch(glob)); }); }); }); describe('.lastModifiedSync', () => { before(() => { fs.mkdirSync(getFixturePath('dates')); }); after(() => { fs.rmdirSync(getFixturePath('dates')); }); const files = [ { name: getFixturePath('dates/a.txt'), modified: 10 }, { name: getFixturePath('dates/w.txt'), modified: 9 }, { name: getFixturePath('dates/x.txt'), modified: 2 }, { name: getFixturePath('dates/y.txt'), modified: 1 }, { name: getFixturePath('dates/z.txt'), modified: 0 } ]; beforeEach(() => { files.forEach((file) => { createFile(file.name, { duration: file.modified, modifier: 'days' }); }); }); afterEach(() => { files.forEach((file) => { deleteFile(file.name); }); }); it('returns the modified time of a given file', () => { files.forEach((file) => { const pathname = new File(file.name); const actual = formatDate(moment(pathname.lastModifiedSync())); assert.equal(actual, formatDate(moment().subtract(file.modified, 'days'))); }); }); }); describe('.lastAccessedSync', () => { before(() => { fs.mkdirSync(getFixturePath('dates')); }); after(() => { fs.rmdirSync(getFixturePath('dates')); }); const files = [ { name: getFixturePath('dates/a.txt'), accessed: 10 }, { name: getFixturePath('dates/w.txt'), accessed: 9 }, { name: getFixturePath('dates/x.txt'), accessed: 2 }, { name: getFixturePath('dates/y.txt'), accessed: 1 }, { name: getFixturePath('dates/z.txt'), accessed: 0 } ]; beforeEach(() => { files.forEach((file) => { createFile(file.name, { duration: file.accessed, modifier: 'hours' }); }); }); afterEach(() => { files.forEach((file) => { deleteFile(file.name); }); }); it('returns the accessed time of a given file', () => { files.forEach((file) => { const pathname = new File(file.name); const actual = formatDate(moment(pathname.lastAccessedSync())); const expectedDate = formatDate(moment().subtract(file.accessed, 'hours')); assert.equal(actual, expectedDate); }); }); }); describe('.lastChangedSync', () => { let statSync; before(() => { fs.mkdirSync(getFixturePath('dates')); statSync = sandbox.stub(fs, 'statSync'); statSync.returns({ isDirectory(): boolean { return true; } }); }); after(() => { fs.rmdirSync(getFixturePath('dates')); sandbox.restore(); }); const files = [ { name: getFixturePath('dates/a.txt'), changed: 10 }, { name: getFixturePath('dates/w.txt'), changed: 9 }, { name: getFixturePath('dates/x.txt'), changed: 2 }, { name: getFixturePath('dates/y.txt'), changed: 1 }, { name: getFixturePath('dates/z.txt'), changed: 0 } ]; beforeEach(() => { files.forEach((file) => { createFile(file.name, { duration: file.changed, modifier: 'hours' }); statSync.withArgs(file.name).returns({ ctime: moment().subtract(file.changed, 'hours'), isDirectory(): boolean { return false; } }); }); }); afterEach(() => { files.forEach((file) => { deleteFile(file.name); }); }); it('returns the last changed time of a given file', () => { files.forEach((file) => { const pathname = new File(file.name); const actual = formatDate(moment(pathname.lastChangedSync())); const expectedDate = formatDate(moment().subtract(file.changed, 'hours')); assert.equal(actual, expectedDate); }); }); }); describe('.sizeSync', () => { it('returns the size of a pathname in bytes', () => { const pathname = new File(getFixturePath('sizes/10b.txt')); assert.equal(pathname.sizeSync(), 10); }); }); describe('.getName', () => { it('returns the pathname representation by the object', () => { const file = new File(getFixturePath('dates/a.txt')); assert.equal(file.getName(), getFixturePath('dates/a.txt')); }); }); describe('.isWritable', () => { it('returns true when the file has write permission', async () => { const file = new File(getFixturePath('justFiles/a.json')); const isWritable = await file.isWritable(); assert.isTrue(isWritable); }); it('returns false when the file does not have write permission', async () => { fsp.chmodSync(getFixturePath('permissions/notWritable.json'), '444'); const file = new File(getFixturePath('permissions/notWritable.json')); const isWritable = await file.isWritable(); assert.isFalse(isWritable); }); }); describe('.exists', () => { it('returns true when the file exists', async () => { const file = new File(getFixturePath('permissions/readWrite.json')); const exists = await file.exists(); assert.isTrue(exists); }); it('returns false when the file does not exists', async () => { const file = new File(getFixturePath('permissions/bad.json')); const exists = await file.exists(); assert.isFalse(exists); }); }); describe('.isReadable', () => { it('returns true when the file has read permission', async () => { const file = new File(getFixturePath('permissions/readWrite.json')); const isReadable = await file.isReadable(); assert.isTrue(isReadable); }); }); describe('.isExecutable', () => { it('returns true when the file has read permission', async () => { const file = new File(getFixturePath('permissions/executable.sh')); const isExecutable = await file.isExecutable(); assert.isTrue(isExecutable); }); it('returns false when the file does not have read permission', async () => { const file = new File(getFixturePath('permissions/notExecutable.sh')); const isExecutable = await file.isExecutable(); assert.isFalse(isExecutable); }); }); describe('.delete', () => { before(() => { fs.mkdirSync(getFixturePath('delete')); }); after(() => { fs.rmdirSync(getFixturePath('delete')); }); const fileToDelete = getFixturePath('delete/a.txt'); beforeEach(() => { createFile(fileToDelete, { duration: 1, modifier: 'hours' }); }); it('deletes a file that exists', async () => { const file = new File(getFixturePath('delete/a.txt')); await file.delete(); assert.throws(() => { fs.statSync(getFixturePath('delete/a.txt')); }, /ENOENT/); }); }); describe('.deleteRecursively', () => { before(() => { fs.mkdirSync(getFixturePath('delete')); }); after(() => { if (fs.existsSync('delete')) { fs.rmdirSync(getFixturePath('delete')); } }); const fileToDelete = getFixturePath('delete/a.txt'); beforeEach(() => { createFile(fileToDelete, { duration: 1, modifier: 'hours' }); createFileStructure('delete'); }); it('recursively deletes a folder and its contents', async () => { const file = new File(getFixturePath('delete/')); await file.deleteRecursively(); assert.throws(() => { fs.statSync(getFixturePath('delete/')); }, /ENOENT/); }); }); describe('.copyRecursively', () => { let shallowCopySource; let deepCopySource; let destinationPath; const file = getFixturePath('shallowCopySource/a.txt'); beforeEach(() => { fs.mkdirSync(getFixturePath('shallowCopySource')); createFile(file, { duration: 1, modifier: 'hours' }); shallowCopySource = new File(getFixturePath('shallowCopySource/')); destinationPath = getFixturePath('copyDest/'); }); afterEach(async () => { await shallowCopySource.deleteRecursively(); const destination = new File(destinationPath); await destination.deleteRecursively(); }); it('copies a directory and its contents to destination', async () => { await shallowCopySource.copyRecursively(destinationPath); assert.isTrue(fs.existsSync(destinationPath)); assert.exists(fs.statSync(`${destinationPath}a.txt`)); }); it('deep copies a directory and its sub-contents to destination', async () => { fs.mkdirSync(getFixturePath('deepCopySource')); createFileStructure('deepCopySource', 5); deepCopySource = new File(getFixturePath('deepCopySource/')); await deepCopySource.copyRecursively(destinationPath); assert.isTrue(fs.existsSync(destinationPath)); assert.isTrue(fs.existsSync(`${destinationPath}subDir_0`)); await deepCopySource.deleteRecursively(); }); describe('overwrite', () => { beforeEach(() => { fs.mkdirSync(destinationPath); const existingFile = getFixturePath('copyDest/originalFile.txt'); createFile(existingFile, { duration: 1, modifier: 'hours' }); }); context('true', () => { it('overwrites existing destination when copying', async () => { await shallowCopySource.copyRecursively(destinationPath, { overwrite: true }); assert.isTrue(fs.existsSync(destinationPath)); assert.exists(fs.statSync(`${destinationPath}a.txt`)); assert.isFalse(fs.existsSync(`${destinationPath}originalFile.txt`)); }); }); context('false', () => { it('does not overwrite existing destination when copying', async () => { try { await shallowCopySource.copyRecursively(destinationPath); } catch (error) { assert.strictEqual(error.message, `Directory: "${destinationPath}" already exists.`); return; } assert.fail(); assert.isTrue(fs.existsSync(destinationPath)); assert.isTrue(fs.existsSync(`${destinationPath}originalFile.txt`)); assert.isFalse(fs.existsSync(`${destinationPath}a.txt`)); }); }); }); }); describe('.getFixturePath', () => { it('returns the absolute path for a relative pathname', () => { const relativePath = './test/fixtures/justFiles/a.json'; const file = new File(relativePath); assert.equal(file.getAbsolutePath(), getAbsolutePath(relativePath)); }); it('returns the absolute path for an absolute pathname', () => { const relativePath = './test/fixtures/justFiles/a.json'; const absolutePath = getAbsolutePath(relativePath); const file = new File(absolutePath); assert.equal(file.getAbsolutePath(), absolutePath); }); }); describe('.getCanonicalPath', () => { it('returns the canonical path for a relative pathname', () => { const relativePath = './test/fixtures/justFiles/a.json'; const file = new File(relativePath); assert.equal(file.getCanonicalPath(), getCanonicalPath('./test/fixtures/justFiles/a.json')); }); it('returns the canonical path for an absolute pathname', () => { const relativePath = './test/fixtures/justFiles/a.json'; const absolutePath = getAbsolutePath(relativePath); const canonicalPath = getCanonicalPath('./test/fixtures/justFiles/a.json'); const file = new File(absolutePath); assert.equal(file.getCanonicalPath(), canonicalPath); }); }); describe('.getName', () => { it('returns the pathname representation by the object', () => { const file = new File(getFixturePath('dates/a.txt')); assert.equal(file.getName(), getFixturePath('dates/a.txt')); }); }); });