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

Commit

Permalink
test(cleancss): added tests for cleancss
Browse files Browse the repository at this point in the history
  • Loading branch information
danbucholtz committed Dec 14, 2016
1 parent d57e9c3 commit 8924704
Show file tree
Hide file tree
Showing 2 changed files with 221 additions and 54 deletions.
182 changes: 182 additions & 0 deletions src/cleancss.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
import { join } from 'path';
import * as rewire from 'rewire';

const cleanCss = rewire('./cleancss');

import * as cleanCssFactory from './util/clean-css-factory';
import * as config from './util/config';
import * as helpers from './util/helpers';
import * as workerClient from './worker-client';


describe('clean css task', () => {

describe('cleancss', () => {
it('should return when the worker returns', async () => {
// arrange
const context = { };
const configFile: any = null;
const spy = spyOn(workerClient, workerClient.runWorker.name).and.returnValue(Promise.resolve());
// act
await (cleanCss as any).cleancss(context, null);
// assert
expect(spy).toHaveBeenCalledWith('cleancss', 'cleancssWorker', context, configFile);
});

it('should throw when the worker throws', async () => {
// arrange
const context = { };
const errorMessage = 'Simulating an error';
spyOn(workerClient, workerClient.runWorker.name).and.throwError(errorMessage);
try {
// act
await (cleanCss as any).cleancss(context, null);
throw new Error('Should never get here');
} catch (ex) {
// assert
expect(ex.message).toEqual(errorMessage, `Expected ex.message ${ex.message} to equal ${errorMessage}`);
}
});
});

describe('cleancssworker', () => {
it('should throw when reading the file throws', async (done) => {
const errorMessage = 'simulating an error';
try {
// arrange
const context = { buildDir: 'www'};
const cleanCssConfig = { sourceFileName: 'sourceFileName', destFileName: 'destFileName'};
spyOn(config, config.generateContext.name).and.returnValue(context);
spyOn(config, config.fillConfigDefaults.name).and.returnValue(cleanCssConfig);
spyOn(helpers, helpers.readFileAsync.name).and.throwError(errorMessage);

// act
await (cleanCss as any).cleancssWorker(context, null);

throw new Error('Should never get here');
} catch (ex) {
expect(ex.message).toEqual(errorMessage);
done();
}
});

it('should return what writeFileAsync returns', async (done) => {
// arrange
const context = { buildDir: 'www'};
const cleanCssConfig = { sourceFileName: 'sourceFileName', destFileName: 'destFileName'};
const fileContent = 'content';
const minifiedContent = 'someContent';
spyOn(config, config.generateContext.name).and.returnValue(context);
spyOn(config, config.fillConfigDefaults.name).and.returnValue(cleanCssConfig);
spyOn(helpers, helpers.readFileAsync.name).and.returnValue(Promise.resolve(fileContent));
spyOn(helpers, helpers.writeFileAsync.name).and.returnValue(Promise.resolve());

// use rewire to stub this since jasmine is insufficient
const spy = jasmine.createSpy('mySpy').and.returnValue(Promise.resolve(minifiedContent));
cleanCss.__set__('runCleanCss', spy);

// act
await (cleanCss as any).cleancssWorker(context, null);

// assert
expect(config.generateContext).toHaveBeenCalledWith(context);
expect(config.fillConfigDefaults).toHaveBeenCalledWith(null, (cleanCss as any).taskInfo.defaultConfigFile);
expect(helpers.readFileAsync).toHaveBeenCalledWith(join(context.buildDir, cleanCssConfig.sourceFileName));
expect(helpers.writeFileAsync).toHaveBeenCalledWith(join(context.buildDir, cleanCssConfig.destFileName), minifiedContent);
expect(spy).toHaveBeenCalledWith(cleanCssConfig, fileContent);
done();
});
});

describe('runCleanCss', () => {
it('should reject when minification errors out', async (done) => {
// arrange
const errorMessage = 'simulating an error';
const configFile = { options: {} };
const fileContent = 'fileContent';
let minifySpy: jasmine.Spy = null;
try {
const destinationFilePath = 'filePath';
const mockMinifier = {
minify: () => {}
};
minifySpy = spyOn(mockMinifier, mockMinifier.minify.name);
spyOn(cleanCssFactory, cleanCssFactory.getCleanCssInstance.name).and.returnValue(mockMinifier);

// act
const promise = (cleanCss as any).runCleanCss(configFile, fileContent, destinationFilePath);
// call the callback from the spy's args
const callback = minifySpy.calls.mostRecent().args[1];
callback(new Error(errorMessage), null);

await promise;

throw new Error('Should never get here');
} catch (ex) {
// assert
expect(ex.message).toEqual(errorMessage);
done();
}
});

it('should reject when minification has one or more errors', async (done) => {
// arrange
const configFile = { options: {} };
const fileContent = 'fileContent';
let minifySpy: jasmine.Spy = null;
const minificationResponse = {
errors: ['some error']
};
try {
const destinationFilePath = 'filePath';
const mockMinifier = {
minify: () => {}
};
minifySpy = spyOn(mockMinifier, mockMinifier.minify.name);
spyOn(cleanCssFactory, cleanCssFactory.getCleanCssInstance.name).and.returnValue(mockMinifier);

// act
const promise = (cleanCss as any).runCleanCss(configFile, fileContent, destinationFilePath);
// call the callback from the spy's args
const callback = minifySpy.calls.mostRecent().args[1];
callback(null, minificationResponse);

await promise;

throw new Error('Should never get here');
} catch (ex) {
// assert
expect(ex.message).toEqual(minificationResponse.errors[0]);
done();
}
});

it('should return minified content', async (done) => {
const configFile = { options: {} };
const fileContent = 'fileContent';
let minifySpy: jasmine.Spy = null;
const minificationResponse = {
styles: 'minifiedContent'
};
const destinationFilePath = 'filePath';
const mockMinifier = {
minify: () => {}
};
minifySpy = spyOn(mockMinifier, mockMinifier.minify.name);
spyOn(cleanCssFactory, cleanCssFactory.getCleanCssInstance.name).and.returnValue(mockMinifier);

// act
const promise = (cleanCss as any).runCleanCss(configFile, fileContent, destinationFilePath);
// call the callback from the spy's args
const callback = minifySpy.calls.mostRecent().args[1];
callback(null, minificationResponse);

const result = await promise;
expect(result).toEqual(minificationResponse.styles);
expect(cleanCssFactory.getCleanCssInstance).toHaveBeenCalledWith(configFile.options);
expect(minifySpy.calls.mostRecent().args[0]).toEqual(fileContent);
done();
});
});
});

93 changes: 39 additions & 54 deletions src/cleancss.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,74 +4,59 @@ import { BuildError } from './util/errors';
import { fillConfigDefaults, generateContext, getUserConfigFile } from './util/config';
import { Logger } from './logger/logger';
import { readFileAsync, writeFileAsync } from './util/helpers';
import { runWorker } from './worker-client';
import * as cleanCss from 'clean-css';
import * as workerClient from './worker-client';
import { CleanCssConfig, getCleanCssInstance } from './util/clean-css-factory';


export function cleancss(context: BuildContext, configFile?: string) {
configFile = getUserConfigFile(context, taskInfo, configFile);

export async function cleancss(context: BuildContext, configFile?: string) {
const logger = new Logger('cleancss');

return runWorker('cleancss', 'cleancssWorker', context, configFile)
.then(() => {
logger.finish();
})
.catch(err => {
throw logger.fail(err);
});
try {
configFile = getUserConfigFile(context, taskInfo, configFile);
await workerClient.runWorker('cleancss', 'cleancssWorker', context, configFile);
logger.finish();
} catch (ex) {
throw logger.fail(ex);
}
}


export function cleancssWorker(context: BuildContext, configFile: string): Promise<any> {
return new Promise((resolve, reject) => {
context = generateContext(context);
const cleanCssConfig: CleanCssConfig = fillConfigDefaults(configFile, taskInfo.defaultConfigFile);
const srcFile = join(context.buildDir, cleanCssConfig.sourceFileName);
const destFile = join(context.buildDir, cleanCssConfig.destFileName);

Logger.debug(`cleancss read: ${srcFile}`);

readFileAsync(srcFile).then(fileContent => {
const minifier = new cleanCss(cleanCssConfig.options);
minifier.minify(fileContent, (err, minified) => {
if (err) {
reject(new BuildError(err));

} else if (minified.errors && minified.errors.length > 0) {
// just return the first error for now I guess
minified.errors.forEach(e => {
Logger.error(e);
});
reject(new BuildError());
export async function cleancssWorker(context: BuildContext, configFile: string): Promise<any> {
context = generateContext(context);
const config: CleanCssConfig = fillConfigDefaults(configFile, taskInfo.defaultConfigFile);
const srcFile = join(context.buildDir, config.sourceFileName);
const destFilePath = join(context.buildDir, config.destFileName);
Logger.debug(`[Clean CSS] cleancssWorker: reading source file ${srcFile}`);
const fileContent = await readFileAsync(srcFile);
const minifiedContent = await runCleanCss(config, fileContent);
Logger.debug(`[Clean CSS] runCleanCss: writing file to disk ${destFilePath}`);
await writeFileAsync(destFilePath, minifiedContent);
}

} else {
Logger.debug(`cleancss write: ${destFile}`);
writeFileAsync(destFile, minified.styles).then(() => {
resolve();
});
}
});
// exporting for easier unit testing
export function runCleanCss(cleanCssConfig: CleanCssConfig, fileContent: string): Promise<string> {
return new Promise((resolve, reject) => {
const minifier = getCleanCssInstance(cleanCssConfig.options);
minifier.minify(fileContent, (err, minified) => {
if (err) {
reject(new BuildError(err));
} else if (minified.errors && minified.errors.length > 0) {
// just return the first error for now I guess
minified.errors.forEach(e => {
Logger.error(e);
});
reject(new BuildError(minified.errors[0]));
} else {
resolve(minified.styles);
}
});

});
}


const taskInfo: TaskInfo = {
// export for testing only
export const taskInfo: TaskInfo = {
fullArg: '--cleancss',
shortArg: '-e',
envVar: 'IONIC_CLEANCSS',
packageConfig: 'ionic_cleancss',
defaultConfigFile: 'cleancss.config'
};


export interface CleanCssConfig {
// https://www.npmjs.com/package/clean-css
sourceFileName: string;
// sourceSourceMapName: string;
destFileName: string;
// options: cleanCss Options;
options?: cleanCss.Options;
}

0 comments on commit 8924704

Please sign in to comment.