Skip to content
This repository was archived by the owner on May 1, 2020. It is now read-only.

Commit 8924704

Browse files
committed
test(cleancss): added tests for cleancss
1 parent d57e9c3 commit 8924704

File tree

2 files changed

+221
-54
lines changed

2 files changed

+221
-54
lines changed

src/cleancss.spec.ts

+182
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,182 @@
1+
import { join } from 'path';
2+
import * as rewire from 'rewire';
3+
4+
const cleanCss = rewire('./cleancss');
5+
6+
import * as cleanCssFactory from './util/clean-css-factory';
7+
import * as config from './util/config';
8+
import * as helpers from './util/helpers';
9+
import * as workerClient from './worker-client';
10+
11+
12+
describe('clean css task', () => {
13+
14+
describe('cleancss', () => {
15+
it('should return when the worker returns', async () => {
16+
// arrange
17+
const context = { };
18+
const configFile: any = null;
19+
const spy = spyOn(workerClient, workerClient.runWorker.name).and.returnValue(Promise.resolve());
20+
// act
21+
await (cleanCss as any).cleancss(context, null);
22+
// assert
23+
expect(spy).toHaveBeenCalledWith('cleancss', 'cleancssWorker', context, configFile);
24+
});
25+
26+
it('should throw when the worker throws', async () => {
27+
// arrange
28+
const context = { };
29+
const errorMessage = 'Simulating an error';
30+
spyOn(workerClient, workerClient.runWorker.name).and.throwError(errorMessage);
31+
try {
32+
// act
33+
await (cleanCss as any).cleancss(context, null);
34+
throw new Error('Should never get here');
35+
} catch (ex) {
36+
// assert
37+
expect(ex.message).toEqual(errorMessage, `Expected ex.message ${ex.message} to equal ${errorMessage}`);
38+
}
39+
});
40+
});
41+
42+
describe('cleancssworker', () => {
43+
it('should throw when reading the file throws', async (done) => {
44+
const errorMessage = 'simulating an error';
45+
try {
46+
// arrange
47+
const context = { buildDir: 'www'};
48+
const cleanCssConfig = { sourceFileName: 'sourceFileName', destFileName: 'destFileName'};
49+
spyOn(config, config.generateContext.name).and.returnValue(context);
50+
spyOn(config, config.fillConfigDefaults.name).and.returnValue(cleanCssConfig);
51+
spyOn(helpers, helpers.readFileAsync.name).and.throwError(errorMessage);
52+
53+
// act
54+
await (cleanCss as any).cleancssWorker(context, null);
55+
56+
throw new Error('Should never get here');
57+
} catch (ex) {
58+
expect(ex.message).toEqual(errorMessage);
59+
done();
60+
}
61+
});
62+
63+
it('should return what writeFileAsync returns', async (done) => {
64+
// arrange
65+
const context = { buildDir: 'www'};
66+
const cleanCssConfig = { sourceFileName: 'sourceFileName', destFileName: 'destFileName'};
67+
const fileContent = 'content';
68+
const minifiedContent = 'someContent';
69+
spyOn(config, config.generateContext.name).and.returnValue(context);
70+
spyOn(config, config.fillConfigDefaults.name).and.returnValue(cleanCssConfig);
71+
spyOn(helpers, helpers.readFileAsync.name).and.returnValue(Promise.resolve(fileContent));
72+
spyOn(helpers, helpers.writeFileAsync.name).and.returnValue(Promise.resolve());
73+
74+
// use rewire to stub this since jasmine is insufficient
75+
const spy = jasmine.createSpy('mySpy').and.returnValue(Promise.resolve(minifiedContent));
76+
cleanCss.__set__('runCleanCss', spy);
77+
78+
// act
79+
await (cleanCss as any).cleancssWorker(context, null);
80+
81+
// assert
82+
expect(config.generateContext).toHaveBeenCalledWith(context);
83+
expect(config.fillConfigDefaults).toHaveBeenCalledWith(null, (cleanCss as any).taskInfo.defaultConfigFile);
84+
expect(helpers.readFileAsync).toHaveBeenCalledWith(join(context.buildDir, cleanCssConfig.sourceFileName));
85+
expect(helpers.writeFileAsync).toHaveBeenCalledWith(join(context.buildDir, cleanCssConfig.destFileName), minifiedContent);
86+
expect(spy).toHaveBeenCalledWith(cleanCssConfig, fileContent);
87+
done();
88+
});
89+
});
90+
91+
describe('runCleanCss', () => {
92+
it('should reject when minification errors out', async (done) => {
93+
// arrange
94+
const errorMessage = 'simulating an error';
95+
const configFile = { options: {} };
96+
const fileContent = 'fileContent';
97+
let minifySpy: jasmine.Spy = null;
98+
try {
99+
const destinationFilePath = 'filePath';
100+
const mockMinifier = {
101+
minify: () => {}
102+
};
103+
minifySpy = spyOn(mockMinifier, mockMinifier.minify.name);
104+
spyOn(cleanCssFactory, cleanCssFactory.getCleanCssInstance.name).and.returnValue(mockMinifier);
105+
106+
// act
107+
const promise = (cleanCss as any).runCleanCss(configFile, fileContent, destinationFilePath);
108+
// call the callback from the spy's args
109+
const callback = minifySpy.calls.mostRecent().args[1];
110+
callback(new Error(errorMessage), null);
111+
112+
await promise;
113+
114+
throw new Error('Should never get here');
115+
} catch (ex) {
116+
// assert
117+
expect(ex.message).toEqual(errorMessage);
118+
done();
119+
}
120+
});
121+
122+
it('should reject when minification has one or more errors', async (done) => {
123+
// arrange
124+
const configFile = { options: {} };
125+
const fileContent = 'fileContent';
126+
let minifySpy: jasmine.Spy = null;
127+
const minificationResponse = {
128+
errors: ['some error']
129+
};
130+
try {
131+
const destinationFilePath = 'filePath';
132+
const mockMinifier = {
133+
minify: () => {}
134+
};
135+
minifySpy = spyOn(mockMinifier, mockMinifier.minify.name);
136+
spyOn(cleanCssFactory, cleanCssFactory.getCleanCssInstance.name).and.returnValue(mockMinifier);
137+
138+
// act
139+
const promise = (cleanCss as any).runCleanCss(configFile, fileContent, destinationFilePath);
140+
// call the callback from the spy's args
141+
const callback = minifySpy.calls.mostRecent().args[1];
142+
callback(null, minificationResponse);
143+
144+
await promise;
145+
146+
throw new Error('Should never get here');
147+
} catch (ex) {
148+
// assert
149+
expect(ex.message).toEqual(minificationResponse.errors[0]);
150+
done();
151+
}
152+
});
153+
154+
it('should return minified content', async (done) => {
155+
const configFile = { options: {} };
156+
const fileContent = 'fileContent';
157+
let minifySpy: jasmine.Spy = null;
158+
const minificationResponse = {
159+
styles: 'minifiedContent'
160+
};
161+
const destinationFilePath = 'filePath';
162+
const mockMinifier = {
163+
minify: () => {}
164+
};
165+
minifySpy = spyOn(mockMinifier, mockMinifier.minify.name);
166+
spyOn(cleanCssFactory, cleanCssFactory.getCleanCssInstance.name).and.returnValue(mockMinifier);
167+
168+
// act
169+
const promise = (cleanCss as any).runCleanCss(configFile, fileContent, destinationFilePath);
170+
// call the callback from the spy's args
171+
const callback = minifySpy.calls.mostRecent().args[1];
172+
callback(null, minificationResponse);
173+
174+
const result = await promise;
175+
expect(result).toEqual(minificationResponse.styles);
176+
expect(cleanCssFactory.getCleanCssInstance).toHaveBeenCalledWith(configFile.options);
177+
expect(minifySpy.calls.mostRecent().args[0]).toEqual(fileContent);
178+
done();
179+
});
180+
});
181+
});
182+

src/cleancss.ts

+39-54
Original file line numberDiff line numberDiff line change
@@ -4,74 +4,59 @@ import { BuildError } from './util/errors';
44
import { fillConfigDefaults, generateContext, getUserConfigFile } from './util/config';
55
import { Logger } from './logger/logger';
66
import { readFileAsync, writeFileAsync } from './util/helpers';
7-
import { runWorker } from './worker-client';
8-
import * as cleanCss from 'clean-css';
7+
import * as workerClient from './worker-client';
8+
import { CleanCssConfig, getCleanCssInstance } from './util/clean-css-factory';
99

1010

11-
export function cleancss(context: BuildContext, configFile?: string) {
12-
configFile = getUserConfigFile(context, taskInfo, configFile);
13-
11+
export async function cleancss(context: BuildContext, configFile?: string) {
1412
const logger = new Logger('cleancss');
15-
16-
return runWorker('cleancss', 'cleancssWorker', context, configFile)
17-
.then(() => {
18-
logger.finish();
19-
})
20-
.catch(err => {
21-
throw logger.fail(err);
22-
});
13+
try {
14+
configFile = getUserConfigFile(context, taskInfo, configFile);
15+
await workerClient.runWorker('cleancss', 'cleancssWorker', context, configFile);
16+
logger.finish();
17+
} catch (ex) {
18+
throw logger.fail(ex);
19+
}
2320
}
2421

2522

26-
export function cleancssWorker(context: BuildContext, configFile: string): Promise<any> {
27-
return new Promise((resolve, reject) => {
28-
context = generateContext(context);
29-
const cleanCssConfig: CleanCssConfig = fillConfigDefaults(configFile, taskInfo.defaultConfigFile);
30-
const srcFile = join(context.buildDir, cleanCssConfig.sourceFileName);
31-
const destFile = join(context.buildDir, cleanCssConfig.destFileName);
32-
33-
Logger.debug(`cleancss read: ${srcFile}`);
34-
35-
readFileAsync(srcFile).then(fileContent => {
36-
const minifier = new cleanCss(cleanCssConfig.options);
37-
minifier.minify(fileContent, (err, minified) => {
38-
if (err) {
39-
reject(new BuildError(err));
40-
41-
} else if (minified.errors && minified.errors.length > 0) {
42-
// just return the first error for now I guess
43-
minified.errors.forEach(e => {
44-
Logger.error(e);
45-
});
46-
reject(new BuildError());
23+
export async function cleancssWorker(context: BuildContext, configFile: string): Promise<any> {
24+
context = generateContext(context);
25+
const config: CleanCssConfig = fillConfigDefaults(configFile, taskInfo.defaultConfigFile);
26+
const srcFile = join(context.buildDir, config.sourceFileName);
27+
const destFilePath = join(context.buildDir, config.destFileName);
28+
Logger.debug(`[Clean CSS] cleancssWorker: reading source file ${srcFile}`);
29+
const fileContent = await readFileAsync(srcFile);
30+
const minifiedContent = await runCleanCss(config, fileContent);
31+
Logger.debug(`[Clean CSS] runCleanCss: writing file to disk ${destFilePath}`);
32+
await writeFileAsync(destFilePath, minifiedContent);
33+
}
4734

48-
} else {
49-
Logger.debug(`cleancss write: ${destFile}`);
50-
writeFileAsync(destFile, minified.styles).then(() => {
51-
resolve();
52-
});
53-
}
54-
});
35+
// exporting for easier unit testing
36+
export function runCleanCss(cleanCssConfig: CleanCssConfig, fileContent: string): Promise<string> {
37+
return new Promise((resolve, reject) => {
38+
const minifier = getCleanCssInstance(cleanCssConfig.options);
39+
minifier.minify(fileContent, (err, minified) => {
40+
if (err) {
41+
reject(new BuildError(err));
42+
} else if (minified.errors && minified.errors.length > 0) {
43+
// just return the first error for now I guess
44+
minified.errors.forEach(e => {
45+
Logger.error(e);
46+
});
47+
reject(new BuildError(minified.errors[0]));
48+
} else {
49+
resolve(minified.styles);
50+
}
5551
});
56-
5752
});
5853
}
5954

60-
61-
const taskInfo: TaskInfo = {
55+
// export for testing only
56+
export const taskInfo: TaskInfo = {
6257
fullArg: '--cleancss',
6358
shortArg: '-e',
6459
envVar: 'IONIC_CLEANCSS',
6560
packageConfig: 'ionic_cleancss',
6661
defaultConfigFile: 'cleancss.config'
6762
};
68-
69-
70-
export interface CleanCssConfig {
71-
// https://www.npmjs.com/package/clean-css
72-
sourceFileName: string;
73-
// sourceSourceMapName: string;
74-
destFileName: string;
75-
// options: cleanCss Options;
76-
options?: cleanCss.Options;
77-
}

0 commit comments

Comments
 (0)