Skip to content

Commit aa62b0d

Browse files
author
Ovidiu Barabula
committed
feat(util): improve error handling for file reader
1 parent 31826d8 commit aa62b0d

File tree

2 files changed

+57
-38
lines changed

2 files changed

+57
-38
lines changed

src/util/file-reader.spec.ts

Lines changed: 32 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
import * as chai from 'chai';
2+
import * as chaiAsPromised from 'chai-as-promised';
3+
chai.use(chaiAsPromised);
4+
15
import { assert, expect } from 'chai';
26
import * as fs from 'fs';
37
import 'mocha';
@@ -13,6 +17,8 @@ describe('FileReader', () => {
1317
const validJsonFile = 'valid.json';
1418

1519
beforeEach(() => {
20+
mockfs.restore && mockfs.restore();
21+
1622
mockfs({
1723
[testDir]: {
1824
[nonJsonFile]: 'not a JSON file',
@@ -40,29 +46,6 @@ describe('FileReader', () => {
4046
});
4147

4248

43-
it('returns false if it fails to write file', async () => {
44-
const fileReader = FileReader(path.join(testDir, readonlyFile));
45-
const contents = await fileReader.read();
46-
mockfs.restore();
47-
const fileWrite = await fileReader.write({ ...contents, ...{ customKey: 'customValue' }});
48-
expect(fileWrite).to.be.false;
49-
});
50-
51-
52-
it('returns false if it\'s not given a json-like file', async () => {
53-
const fileReader = FileReader(path.join(testDir, nonJsonFile));
54-
const contents = await fileReader.read();
55-
expect(contents).to.be.false;
56-
});
57-
58-
59-
it('returns false if read failed', async () => {
60-
const fileReader = FileReader(path.join(testDir, nonReadableFile));
61-
const response = await fileReader.read();
62-
expect(response).to.be.false;
63-
});
64-
65-
6649
it('reads json-like file', async () => {
6750
const fileReader = FileReader(path.join(testDir, validJsonFile));
6851
const contents = await fileReader.read();
@@ -73,18 +56,41 @@ describe('FileReader', () => {
7356
it('writes new contents to file', async () => {
7457
const fileReader = FileReader(path.join(testDir, validJsonFile));
7558
const contents = await fileReader.read();
76-
const fileWrite = await fileReader.write({ ...contents, ...{ customKey: 'customValue' }});
59+
const fileWrite = await fileReader.write({ ...contents, ...{ customKey: 'customValue' }});
7760
expect(fileWrite).to.be.true;
7861
});
7962

8063

8164
it('reads newly added content', async () => {
8265
const fileReader = FileReader(path.join(testDir, validJsonFile));
8366
const oldContent = await fileReader.read();
84-
const fileWrite = await fileReader.write({ ...oldContent, ...{ customKey: 'customValue' }});
67+
const fileWrite = await fileReader.write({ ...oldContent, ...{ customKey: 'customValue' }});
8568
const newContent = await fileReader.read();
8669
expect(newContent.customKey).to.equal('customValue');
8770
});
8871

89-
afterEach(mockfs.restore);
72+
73+
it('rejects promise if read failed', () => {
74+
const fileReader = FileReader(path.join(testDir, nonReadableFile));
75+
return expect(fileReader.read()).to.be.rejectedWith(RegExp(ERRORS.READ_ERROR));
76+
});
77+
78+
79+
it('rejects promise if write failed', async () => {
80+
const fileReader = FileReader(path.join(testDir, readonlyFile));
81+
const contents = await fileReader.read();
82+
mockfs.restore();
83+
const fileWrite = fileReader.write({ ...contents, ...{ customKey: 'customValue' }});
84+
return expect(fileWrite).to.be.rejectedWith(RegExp(ERRORS.WRITE_ERROR));
85+
});
86+
87+
88+
it('rejects promise if it\'s not given a JSON-like file', done => {
89+
const fileReader = FileReader(path.join(testDir, nonJsonFile));
90+
expect(fileReader.read()).to.be.rejectedWith(ERRORS.NOT_JSON).notify(done);
91+
});
92+
93+
94+
afterEach(() => mockfs.restore && mockfs.restore());
95+
after(() => mockfs.restore && mockfs.restore());
9096
});

src/util/file-reader.ts

Lines changed: 25 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,20 @@
66
*/
77

88
import * as fs from 'fs';
9+
import { Config } from '../config-manager/package-json-config-reader';
910

1011
interface FileReader {
1112
read(): Promise<any>;
12-
write(object: object): Promise<boolean>;
13+
write(object: object): Promise<boolean|Error>;
1314
}
1415

1516
export const ERRORS = {
16-
NOT_FOUND: `File not found`,
17+
NOT_FOUND: `FileReader> File not found`,
18+
NOT_JSON: `FileReader> File is not in a JSON-like format`,
1719
PATH_NOT_PASSED: `FileReader requires 1 parameter to be string <filepath>`,
1820
PATH_NOT_STRING: `FileReader requires parameter 1 to be string`,
19-
READ_ERROR: `Could not read file`,
20-
WRITE_ERROR: `Could not write file`,
21+
READ_ERROR: `FileReader> Could not read file`,
22+
WRITE_ERROR: `FileReader> Could not write file`,
2123
};
2224

2325
/**
@@ -39,14 +41,22 @@ function FileReader(filepath: string): FileReader {
3941
/**
4042
* Get file contents
4143
*/
42-
function read(): Promise<any> {
43-
return new Promise(resolve => {
44+
function read(): Promise<boolean|Error> {
45+
return new Promise((resolve, reject) => {
4446
fs.readFile(filepath, 'utf-8', (readError, data) => {
47+
// Throw back any read errors
48+
if (readError) {
49+
return reject(new Error(`${ERRORS.READ_ERROR}: ${readError.message}`));
50+
}
51+
52+
let parsedJson;
4553
try {
46-
resolve(JSON.parse(data));
54+
parsedJson = JSON.parse(data);
4755
} catch (parseError) {
48-
resolve(false);
56+
return reject(new Error(ERRORS.NOT_JSON));
4957
}
58+
59+
return resolve(parsedJson);
5060
});
5161
});
5262
}
@@ -56,19 +66,22 @@ function FileReader(filepath: string): FileReader {
5666
* Write JSON object to file
5767
* @param object Object literal
5868
*/
59-
function write(object: object): Promise<boolean> {
69+
function write(object: Config): Promise<boolean|Error> {
6070
return new Promise((resolve, reject) => {
71+
// TODO: replace 2 with 'detect-indent' module's output
6172
const content = JSON.stringify(object, null, 2);
62-
fs.writeFile(filepath, content, error => {
63-
if (error) {
64-
return resolve(false);
73+
// Write the file content with an empty line at the end
74+
fs.writeFile(filepath, `${content}\n`, writeError => {
75+
if (writeError) {
76+
return reject(new Error(`${ERRORS.WRITE_ERROR}: ${writeError.message}`));
6577
}
6678

6779
return resolve(true);
6880
});
6981
});
7082
}
7183

84+
7285
// Return public API
7386
return Object.freeze({
7487
read,

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy