diff --git a/local-cli/__mocks__/fs.js b/local-cli/__mocks__/fs.js index d4cdab42b..db689247e 100644 --- a/local-cli/__mocks__/fs.js +++ b/local-cli/__mocks__/fs.js @@ -118,6 +118,33 @@ fs.writeFileSync.mockImplementation((filePath, content, options) => { node[path.basename(filePath)] = content; }); +const openFds = new Map(); +let nextFd = 3; + +fs.openSync.mockImplementation((filePath, flags) => { + const dirPath = path.dirname(filePath); + const node = getToNode(dirPath); + if (!isDirNode(node)) { + throw fsError('ENOTDIR', 'not a directory: ' + dirPath); + } + node[path.basename(filePath)] = ''; + openFds.set(nextFd, {filePath, flags, node}); + return nextFd++; +}); + +fs.writeSync.mockImplementation((fd, str) => { + invariant(typeof str === 'string', 'only strings supported'); + const data = openFds.get(fd); + if (data == null || data.flags !== 'w') { + throw fsError('EBADF', 'bad file descriptor, write'); + } + data.node[path.basename(data.filePath)] += str; +}); + +fs.closeSync.mockImplementation(fd => { + openFds.delete(fd); +}); + fs.mkdir.mockImplementation(asyncify(fs.mkdirSync)); fs.mkdirSync.mockImplementation((dirPath, mode) => { @@ -332,26 +359,34 @@ fs.createReadStream.mockImplementation(filepath => { }); }); -fs.createWriteStream.mockImplementation(file => { +fs.createWriteStream.mockImplementation(filePath => { let node; + const writeStream = new stream.Writable({ + write(chunk, encoding, callback) { + this.__chunks.push(chunk); + callback(); + }, + final(callback) { + node[path.basename(filePath)] = this.__chunks.join(''); + callback(); + }, + }); + writeStream.__file = filePath; + writeStream.__chunks = []; + writeStream.end = jest.fn(writeStream.end); + fs.createWriteStream.mock.returned.push(writeStream); try { - node = getToNode(dirname(file)); - } finally { - if (typeof node === 'object') { - const writeStream = new stream.Writable({ - write(chunk) { - this.__chunks.push(chunk); - }, - }); - writeStream.__file = file; - writeStream.__chunks = []; - writeStream.end = jest.fn(writeStream.end); - fs.createWriteStream.mock.returned.push(writeStream); - return writeStream; - } else { - throw new Error('Cannot open file ' + file); + const dirPath = dirname(filePath); + node = getToNode(dirPath); + if (!isDirNode(node)) { + throw fsError('ENOTDIR', 'not a directory: ' + dirPath); } + // Truncate the file on opening. + node[path.basename(filePath)] = ''; + } catch (error) { + process.nextTick(() => writeStream.emit('error', error)); } + return writeStream; }); fs.createWriteStream.mock.returned = []; diff --git a/local-cli/__tests__/fs-mock-test.js b/local-cli/__tests__/fs-mock-test.js index c390e3cf2..43efc8bce 100644 --- a/local-cli/__tests__/fs-mock-test.js +++ b/local-cli/__tests__/fs-mock-test.js @@ -24,6 +24,10 @@ jest.mock('fs'); const fs = require('fs'); describe('fs mock', () => { + beforeEach(() => { + (fs: $FlowFixMe).mock.clear(); + }); + describe('writeFileSync()', () => { it('stores content correctly', () => { fs.writeFileSync('/test', 'foobar', 'utf8'); @@ -55,4 +59,28 @@ describe('fs mock', () => { expect(content).toEqual('foobar'); }); }); + + describe('createWriteStream()', () => { + it('writes content', done => { + const stream = fs.createWriteStream('/test'); + stream.write('hello, '); + stream.write('world'); + stream.end('!'); + process.nextTick(() => { + const content = fs.readFileSync('/test', 'utf8'); + expect(content).toEqual('hello, world!'); + done(); + }); + }); + }); + + describe('writeSync()', () => { + it('writes content', () => { + const fd = fs.openSync('/test', 'w'); + fs.writeSync(fd, 'hello, world!'); + fs.closeSync(fd); + const content = fs.readFileSync('/test', 'utf8'); + expect(content).toEqual('hello, world!'); + }); + }); });