Blob Blame History Raw
import test from 'ava';
import proxyquire from 'proxyquire';
import mockfs from 'mock-fs';
import md5Hex from 'md5-hex';
import path from 'path';
import sinon from 'sinon';

function withMockedFs(fsConfig) {
	const fs = mockfs.fs(fsConfig || {});
	fs['@global'] = true;
	const mkdirp = sinon.spy(proxyquire('mkdirp', {fs}));
	mkdirp.sync = sinon.spy(mkdirp.sync);
	var cachingTransform = proxyquire('./', {fs, mkdirp});
	cachingTransform.fs = fs;
	cachingTransform.mkdirp = mkdirp;

	return cachingTransform;
}

function wrap(opts, fsConfig) {
	if (typeof opts === 'function') {
		opts = {
			transform: opts,
			cacheDir: '/cacheDir'
		};
	}
	var cachingTransform = withMockedFs(fsConfig);
	var wrapped = cachingTransform(opts);
	wrapped.fs = cachingTransform.fs;
	wrapped.mkdirp = cachingTransform.mkdirp;
	return wrapped;
}

function append(val) {
	return input => input + ' ' + val;
}

test('saves transform result to cache directory', t => {
	const transform = wrap(append('bar'));

	t.is(transform('foo'), 'foo bar');
	t.is(transform('FOO'), 'FOO bar');

	const filename1 = path.join('/cacheDir', 'acbd18db4cc2f85cedef654fccc4a4d8');
	const filename2 = path.join('/cacheDir', '901890a8e9c8cf6d5a1a542b229febff');

	t.is(transform.fs.readFileSync(filename1, 'utf8'), 'foo bar');
	t.is(transform.fs.readFileSync(filename2, 'utf8'), 'FOO bar');
});

test('skips transform if cache file exists', t => {
	const transform = wrap(
		() => t.fail(),
		{
			'/cacheDir/acbd18db4cc2f85cedef654fccc4a4d8': 'foo bar'
		}
	);

	t.is(transform('foo'), 'foo bar');
});

test('able to specify alternate cacheDir', t => {
	const transform = wrap({
		transform: append('bar'),
		cacheDir: '/alternateDir'
	});

	t.is(transform('foo'), 'foo bar');

	const filename = path.join('/alternateDir', 'acbd18db4cc2f85cedef654fccc4a4d8');

	t.is(transform.fs.readFileSync(filename, 'utf8'), 'foo bar');
});

test('able to specify alternate extension', t => {
	const transform = wrap({
		transform: append('bar'),
		ext: '.js',
		cacheDir: '/cacheDir'
	});

	t.is(transform('foo'), 'foo bar');

	const filename = path.join('/cacheDir', 'acbd18db4cc2f85cedef654fccc4a4d8.js');

	t.is(transform.fs.readFileSync(filename, 'utf8'), 'foo bar');
});

test('mkdirp is only called once', t => {
	const transform = wrap(
		{
			transform: append('bar'),
			cacheDir: '/someDir'
		}
	);

	t.is(transform.mkdirp.sync.callCount, 0);
	t.is(transform('foo'), 'foo bar');
	t.is(transform.mkdirp.sync.callCount, 1);
	t.is(transform('bar'), 'bar bar');
	t.is(transform.mkdirp.sync.callCount, 1);
});

test('mkdirp is only called once, with factory', t => {
	const transform = wrap(
		{
			factory: () => append('bar'),
			cacheDir: '/someDir'
		}
	);

	t.is(transform.mkdirp.sync.callCount, 0);
	t.is(transform('foo'), 'foo bar');
	t.is(transform.mkdirp.sync.callCount, 1);
	t.is(transform('bar'), 'bar bar');
	t.is(transform.mkdirp.sync.callCount, 1);
});

test('mkdirp is never called if `createCacheDir === false`', t => {
	const transform = wrap(
		{
			transform: append('bar'),
			createCacheDir: false,
			cacheDir: '/someDir'
		},
		{
			'/someDir': {}
		}
	);

	t.is(transform.mkdirp.sync.callCount, 0);
	t.is(transform('foo'), 'foo bar');
	t.is(transform.mkdirp.sync.callCount, 0);
});

test('mkdirp is never called if `createCacheDir === false`, with factory', t => {
	const transform = wrap(
		{
			factory: () => append('bar'),
			createCacheDir: false,
			cacheDir: '/someDir'
		},
		{
			'/someDir': {}
		}
	);

	t.is(transform.mkdirp.sync.callCount, 0);
	t.is(transform('foo'), 'foo bar');
	t.is(transform.mkdirp.sync.callCount, 0);
});

test('additional opts are passed to transform', t => {
	const transform = wrap((input, additionalOpts) => {
		t.is(input, 'foo');
		t.same(additionalOpts, {bar: 'baz'});
		return 'FOO!'
	});

	t.is(transform('foo', {bar: 'baz'}), 'FOO!');
});

test('filename is generated from the md5 hash of the input content and the salt', t => {
	const transform = wrap (
		{
			transform: append('bar'),
			salt: 'baz',
			cacheDir: '/someDir'
		}
	);

	transform('FOO');

	const filename = path.join('/someDir', md5Hex(['FOO', 'baz']));

	t.is(transform.fs.readFileSync(filename, 'utf8'), 'FOO bar');
});

test('factory is only called once', t => {
	const factory = sinon.spy(() => append('foo'));

	const transform = wrap(
		{
			factory,
			cacheDir: '/cacheDir'
		}
	);

	t.is(factory.callCount, 0);
	t.is(transform('bar'), 'bar foo');
	t.is(factory.callCount, 1);
	t.same(factory.firstCall.args, ['/cacheDir']);
	t.is(transform('baz'), 'baz foo');
	t.is(factory.callCount, 1);
});

test('checks for sensible options', t => {
	const transform = append('bar');
	const factory = () => transform;
	const cacheDir = '/someDir';
  t.throws(() => wrap({factory, transform, cacheDir}));
	t.throws(() => wrap({cacheDir}));
	t.throws(() => wrap({factory}));
	t.throws(() => wrap({transform}));

	t.doesNotThrow(() => {
		wrap({factory, cacheDir});
		wrap({transform, cacheDir});
	});
});

test('cacheDir is only required if caching is enabled', t => {
	t.doesNotThrow(() => {
		wrap({transform: append('bar'), disableCache: true});
	});
	t.throws(() => {
		wrap({transform: append('bar')});
	});
});

test('shouldTransform can bypass transform', t => {
	const transform = wrap({
		shouldTransform: (code, file) => {
			t.is(code, 'baz');
			t.is(file, '/baz.js');
			return false;
		},
		transform: () => t.fail(),
		cacheDir: '/someDir'
	});

	t.is(transform('baz', '/baz.js'), 'baz');
});

test('shouldTransform can enable transform', t => {
	const transform = wrap({
		shouldTransform: (code, file) => {
			t.is(code, 'foo');
			t.is(file, '/foo.js');
			return true;
		},
		transform: append('bar'),
		cacheDir: '/someDir'
	});

	t.is(transform('foo', '/foo.js'), 'foo bar');
});

test('disableCache:true, disables cache - transform is called multiple times', t => {
	const transformSpy = sinon.spy(append('bar'));
	const transform = wrap({
		disableCache: true,
		transform: transformSpy,
		cacheDir: '/someDir'
	});

	t.is(transformSpy.callCount, 0);
	t.is(transform('foo'), 'foo bar');
	t.is(transformSpy.callCount, 1);
	t.is(transform('foo'), 'foo bar');
	t.is(transformSpy.callCount, 2);
});

test('disableCache:default, enables cache - transform is called once per hashed input', t => {
	const transformSpy = sinon.spy(append('bar'));
	const transform = wrap({
		transform: transformSpy,
		cacheDir: '/someDir'
	});

	t.is(transformSpy.callCount, 0);
	t.is(transform('foo'), 'foo bar');
	t.is(transformSpy.callCount, 1);
	t.is(transform('foo'), 'foo bar');
	t.is(transformSpy.callCount, 1);
});

test('can provide custom hash function', t => {
	t.plan(5);

	const hash = sinon.spy(function (code, filename, salt) {
		t.is(code, 'foo');
		t.is(filename, '/foo.js');
		t.is(salt, 'this is salt');
		return 'foo-hash';
	});

	const transform = wrap({
		salt: 'this is salt',
		cacheDir: '/cacheDir',
		transform: append('bar'),
		hash
	});

	t.is(transform('foo', '/foo.js'), 'foo bar');
	t.is(transform.fs.readFileSync('/cacheDir/foo-hash', 'utf8'), 'foo bar');
});

test('custom encoding changes value loaded from disk', t => {
	const transform = wrap({
		transform: () => t.fail(),
		encoding: 'hex',
		cacheDir: '/cacheDir'
	}, {
		['/cacheDir/' + md5Hex('foo')]: 'foo bar'
	});

	t.is(transform('foo'), new Buffer('foo bar').toString('hex'));
});

test('custom encoding changes the value stored to disk', t => {
	const transform = wrap({
		transform: code => new Buffer(code + ' bar').toString('hex'),
		encoding: 'hex',
		cacheDir: '/cacheDir'
	});

	t.is(transform('foo'), new Buffer('foo bar').toString('hex'));
	t.is(transform.fs.readFileSync('/cacheDir/' + md5Hex('foo'), 'utf8'), 'foo bar');
});

test('buffer encoding returns a buffer', t => {
	const transform = wrap({
		transform: () => t.fail(),
		encoding: 'buffer',
		cacheDir: '/cacheDir'
	}, {
		['/cacheDir/' + md5Hex('foo')]: 'foo bar'
	});

	var result = transform('foo');
	t.true(Buffer.isBuffer(result));
	t.is(result.toString(), 'foo bar');
});

test('salt can be a buffer', t => {
	const transform = wrap({
		transform: () => t.fail(),
		salt: new Buffer('some-salt'),
		cacheDir: '/cacheDir'
	}, {
		['/cacheDir/' + md5Hex(['foo', new Buffer('some-salt', 'utf8')])]: 'foo bar'
	});

	t.is(transform('foo'), 'foo bar');
});