1 | import {dir} from 'tmp';
|
2 |
|
3 | import {
|
4 | mkdirSync,
|
5 | rmdirSync,
|
6 | writeFileSync,
|
7 | unlinkSync
|
8 | } from 'fs';
|
9 |
|
10 | import {join} from 'path';
|
11 | import {expect} from 'chai';
|
12 | import * as Promise from 'bluebird';
|
13 | import {EventEmitter} from 'eventemitter3';
|
14 |
|
15 | import {
|
16 | assertFileSystemContract,
|
17 | dirName,
|
18 | fileName,
|
19 | content,
|
20 | ignoredDir,
|
21 | ignoredFile
|
22 | } from './implementation-suite'
|
23 |
|
24 | import {EventsMatcher} from '../test-kit/drivers/events-matcher';
|
25 |
|
26 | import {
|
27 | FileSystem,
|
28 | pathSeparator,
|
29 | fileSystemEventNames
|
30 | } from '../src/universal';
|
31 |
|
32 | import {LocalFileSystem} from '../src/nodejs';
|
33 |
|
34 | describe(`the local filesystem implementation`, () => {
|
35 | let dirCleanup, rootPath, testPath;
|
36 | let counter = 0;
|
37 | let disposableFileSystem;
|
38 |
|
39 | before(done => {
|
40 | dir({unsafeCleanup:true}, (err, path, cleanupCallback) => {
|
41 | dirCleanup = cleanupCallback;
|
42 | rootPath = path;
|
43 | done();
|
44 | })
|
45 | });
|
46 | after(() => {
|
47 | try {
|
48 | dirCleanup();
|
49 |
|
50 | } catch(e) {
|
51 | console.log('cleanup error', e);
|
52 | }
|
53 | });
|
54 | afterEach(() =>{
|
55 | if (disposableFileSystem) {
|
56 | disposableFileSystem.dispose();
|
57 | }
|
58 | });
|
59 | function getFS() {
|
60 | testPath = join(rootPath, 'fs_'+(counter++));
|
61 | mkdirSync(testPath);
|
62 | disposableFileSystem = new LocalFileSystem(
|
63 | testPath,
|
64 | [ignoredDir, ignoredFile]
|
65 | );
|
66 | return disposableFileSystem.init();
|
67 | }
|
68 | const eventMatcherOptions: EventsMatcher.Options = {
|
69 | interval: 50,
|
70 | noExtraEventsGrace: 150,
|
71 | timeout: 1500
|
72 | };
|
73 | assertFileSystemContract(getFS, eventMatcherOptions);
|
74 | describe(`external changes`, () => {
|
75 | let fs: FileSystem;
|
76 | let matcher: EventsMatcher;
|
77 | beforeEach(() => {
|
78 | matcher = new EventsMatcher(eventMatcherOptions);
|
79 | return getFS().then(newFs => {
|
80 | fs = newFs
|
81 | matcher.track(fs.events as any as EventEmitter, ...fileSystemEventNames);
|
82 | });
|
83 | });
|
84 |
|
85 | it(`handles dir creation`, () => {
|
86 | const path = join(testPath, dirName);
|
87 | mkdirSync(path);
|
88 | return expect(fs.loadDirectoryTree())
|
89 | .to.eventually.have.property('children').eql([
|
90 | {children: [], fullPath: dirName, name: dirName, type:'dir'}
|
91 | ]);
|
92 | });
|
93 |
|
94 | it(`handles dir deletion`, () => {
|
95 | const path = join(testPath, dirName);
|
96 | mkdirSync(path);
|
97 | rmdirSync(path);
|
98 | return expect(fs.loadDirectoryTree()).to.eventually.have.property('children').eql([]);
|
99 | });
|
100 |
|
101 | it(`handles file creation`, () => {
|
102 | const path = join(testPath, fileName);
|
103 | writeFileSync(path, content);
|
104 | return expect(fs.loadTextFile(fileName)).to.eventually.equals(content);
|
105 | });
|
106 |
|
107 | it(`handles file deletion`, () => {
|
108 | const path = join(testPath, fileName);
|
109 | writeFileSync(path, content);
|
110 | unlinkSync(path);
|
111 | return expect(fs.loadTextFile(fileName)).to.eventually.be.rejected;
|
112 | });
|
113 |
|
114 | it(`handles file change`, () => {
|
115 | const path = join(testPath, fileName);
|
116 | const newContent = `_${content}`;
|
117 | writeFileSync(path, content);
|
118 | return matcher.expect([{type: 'fileCreated', fullPath: fileName, newContent: content}])
|
119 | .then(() => {
|
120 | writeFileSync(path, newContent);
|
121 | return Promise.resolve();
|
122 | })
|
123 | .then(() => expect(fs.loadTextFile(fileName)).to.eventually.equals(newContent));
|
124 | });
|
125 |
|
126 | it(`ignores events from ignored dir`, () => {
|
127 | mkdirSync(join(testPath, ignoredDir))
|
128 | return matcher.expect([])
|
129 | });
|
130 |
|
131 | it(`ignores events from ignored file`, () => {
|
132 | mkdirSync(join(testPath, dirName))
|
133 | return matcher.expect([{type: 'directoryCreated', fullPath: dirName}])
|
134 | .then(() => writeFileSync(join(testPath, ignoredFile), content))
|
135 | .then(() => matcher.expect([]))
|
136 | });
|
137 |
|
138 | it(`loadDirectoryTree() ignores ignored folder and file`, () => {
|
139 | const expectedStructure = {
|
140 | name: '',
|
141 | type: 'dir',
|
142 | fullPath: '',
|
143 | children: [{ name: dirName, type: 'dir', fullPath: dirName, children: []}]
|
144 | };
|
145 | mkdirSync(join(testPath, ignoredDir))
|
146 | mkdirSync(join(testPath, dirName))
|
147 | writeFileSync(join(testPath, ignoredFile), content)
|
148 | return expect(fs.loadDirectoryTree()).to.eventually.deep.equal(expectedStructure)
|
149 | });
|
150 |
|
151 | it(`loadDirectoryTree() ignores ignored folder with special characters`, () => {
|
152 | const expectedStructure = {
|
153 | name: '',
|
154 | type: 'dir',
|
155 | fullPath: '',
|
156 | children: [{ name: dirName, type: 'dir', fullPath: dirName, children: []}]
|
157 | };
|
158 | mkdirSync(join(testPath, ignoredDir))
|
159 | mkdirSync(join(testPath, ignoredDir, 'name-with-dashes'))
|
160 | mkdirSync(join(testPath, ignoredDir, 'name-with-dashes', '.name_starts_with_dot'))
|
161 | mkdirSync(join(testPath, ignoredDir, 'name-with-dashes', '.name_starts_with_dot', '.name_starts_with_dot'))
|
162 | mkdirSync(join(testPath, dirName))
|
163 | return expect(fs.loadDirectoryTree()).to.eventually.deep.equal(expectedStructure)
|
164 | });
|
165 |
|
166 | it(`ignores events in dot-folders and files`, () => {
|
167 | mkdirSync(join(testPath, ignoredDir));
|
168 | mkdirSync(join(testPath, ignoredDir, `.${dirName}`));
|
169 | writeFileSync(join(testPath, ignoredDir, `.${dirName}`, `.${fileName}`), content);
|
170 |
|
171 | return matcher.expect([]);
|
172 | });
|
173 |
|
174 | it(`loading existed ignored file - fails`, function() {
|
175 | mkdirSync(join(testPath, dirName))
|
176 | writeFileSync(join(testPath, ignoredFile), content)
|
177 |
|
178 | return expect(fs.loadTextFile(ignoredFile)).to.be.rejectedWith(Error)
|
179 | });
|
180 |
|
181 | it(`emits 'unexpectedError' if 'loadTextFile' rejected in watcher 'add' callback`, () => {
|
182 | fs.loadTextFile = path => Promise.reject('go away!');
|
183 | const path = join(testPath, fileName);
|
184 | writeFileSync(path, content);
|
185 | return matcher.expect([{type: 'unexpectedError'}]);
|
186 | });
|
187 |
|
188 | it(`emits 'unexpectedError' if 'loadTextFile' rejected in watcher 'change' callback`, () => {
|
189 | const path = join(testPath, fileName);
|
190 | return fs.saveFile(fileName, content)
|
191 | .then(() => fs.loadTextFile = path => Promise.reject('go away!'))
|
192 | .then(() => fs.saveFile(fileName, `_${content}`))
|
193 | .then(() => matcher.expect([{type: 'unexpectedError'}]))
|
194 | });
|
195 | });
|
196 | });
|