Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor lib/__tests__/standalone-cache.test.js #5311

Merged
merged 2 commits into from
May 17, 2021
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
255 changes: 109 additions & 146 deletions lib/__tests__/standalone-cache.test.js
Original file line number Diff line number Diff line change
@@ -1,20 +1,23 @@
'use strict';

const hash = require('../utils/hash');
const path = require('path');
const standalone = require('../standalone');
const fixturesPath = path.join(__dirname, 'fixtures');
const fCache = require('file-entry-cache');
const path = require('path');
const { promises: fs, existsSync } = require('fs');

const hash = require('../utils/hash');
const replaceBackslashes = require('../testUtils/replaceBackslashes');
const { promises: fs, constants: fsConstants } = require('fs');
const standalone = require('../standalone');

const fileExists = (filePath) =>
fs.access(filePath, fsConstants.F_OK).then(
() => true,
() => false,
);
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[note] I think it simple and enough to use fs.existsSync() for tests.

// NOTE: `fs.rm(file, { force: true })` will be available when we drop the support of older Node versions.
// See <https://nodejs.org/api/fs.html#fs_fs_rm_path_options_callback>
const removeFile = async (filePath) => {
if (existsSync(filePath)) {
await fs.unlink(filePath);
}
};

const cwd = process.cwd();
const fixturesPath = path.join(__dirname, 'fixtures');
const invalidFile = path.join(fixturesPath, 'empty-block.css');
const syntaxErrorFile = path.join(fixturesPath, 'syntax_error.css');
const validFile = path.join(fixturesPath, 'cache', 'valid.css');
Expand All @@ -36,134 +39,108 @@ function getConfig() {
describe('standalone cache', () => {
const expectedCacheFilePath = path.join(cwd, '.stylelintcache');

beforeEach(() => {
beforeEach(async () => {
// Initial run to warm up the cache
return standalone(getConfig());
await standalone(getConfig());
});

afterEach(() => {
afterEach(async () => {
// Clean up after each test case
return Promise.all([
fs.unlink(expectedCacheFilePath).catch(() => {
// fs.unlink() throws an error if file doesn't exist and it's ok. We just
// want to make sure it's not there for next test.
}),
fs.unlink(newFileDest).catch(() => {
// fs.unlink() throws an error if file doesn't exist and it's ok. We just
// want to make sure it's not there for next test.
}),
]);
await removeFile(expectedCacheFilePath);
await removeFile(newFileDest);
});

it('cache file is created at $CWD/.stylelintcache', () => {
it('cache file is created at $CWD/.stylelintcache', async () => {
// Ensure cache file exists
return fileExists(expectedCacheFilePath).then((isFileExist) => {
expect(Boolean(isFileExist)).toBe(true);
expect(existsSync(expectedCacheFilePath)).toBe(true);

const fileCache = fCache.createFromFile(expectedCacheFilePath);
const { cache } = fileCache;
const { cache } = fCache.createFromFile(expectedCacheFilePath);

// Ensure cache file contains only linted css file
expect(typeof cache.getKey(validFile) === 'object').toBe(true);
expect(typeof cache.getKey(newFileDest) === 'undefined').toBe(true);
});
// Ensure cache file contains only linted css file
expect(typeof cache.getKey(validFile)).toBe('object');
expect(cache.getKey(validFile)).toBeTruthy();
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[note] I've added the expectation .toBeTruthy() because of typeof null === 'object'. But it may be a bit ugly and is not DRY. 🤔

expect(cache.getKey(newFileDest)).toBeUndefined();
});

it('only changed files are linted', () => {
it('only changed files are linted', async () => {
// Add "changed" file
return fs
.copyFile(validFile, newFileDest)
.then(() => {
// Next run should lint only changed files
return standalone(getConfig());
})
.then((output) => {
// Ensure only changed files are linted
const isValidFileLinted = Boolean(output.results.find((file) => file.source === validFile));
const isNewFileLinted = Boolean(output.results.find((file) => file.source === newFileDest));

expect(isValidFileLinted).toBe(false);
expect(isNewFileLinted).toBe(true);

const fileCache = fCache.createFromFile(expectedCacheFilePath);
const { cache } = fileCache;

expect(typeof cache.getKey(validFile) === 'object').toBe(true);
expect(typeof cache.getKey(newFileDest) === 'object').toBe(true);
});
await fs.copyFile(validFile, newFileDest);

// Next run should lint only changed files
const { results } = await standalone(getConfig());

// Ensure only changed files are linted
expect(results.some((file) => file.source === validFile)).toBe(false);
expect(results.some((file) => file.source === newFileDest)).toBe(true);

const { cache } = fCache.createFromFile(expectedCacheFilePath);

expect(typeof cache.getKey(validFile)).toBe('object');
expect(cache.getKey(validFile)).toBeTruthy();
expect(typeof cache.getKey(newFileDest)).toBe('object');
expect(cache.getKey(newFileDest)).toBeTruthy();
});

it('all files are linted on config change', () => {
it('all files are linted on config change', async () => {
const changedConfig = getConfig();

changedConfig.config.rules['block-no-empty'] = false;

return fs
.copyFile(validFile, newFileDest)
.then(() => {
// All file should be re-linted as config has changed
return standalone(changedConfig);
})
.then((output) => {
// Ensure all files are re-linted
const isValidFileLinted = Boolean(output.results.find((file) => file.source === validFile));
const isNewFileLinted = Boolean(output.results.find((file) => file.source === newFileDest));

expect(isValidFileLinted).toBe(true);
expect(isNewFileLinted).toBe(true);
});
await fs.copyFile(validFile, newFileDest);

// All file should be re-linted as config has changed
const { results } = await standalone(changedConfig);

// Ensure all files are re-linted
expect(results.some((file) => file.source === validFile)).toBe(true);
expect(results.some((file) => file.source === newFileDest)).toBe(true);
});

it('invalid files are not cached', () => {
return fs
.copyFile(invalidFile, newFileDest)
.then(() => {
// Should lint only changed files
return standalone(getConfig());
})
.then((output) => {
expect(output.errored).toBe(true);
// Ensure only changed files are linted
const isValidFileLinted = Boolean(output.results.find((file) => file.source === validFile));
const isInvalidFileLinted = Boolean(
output.results.find((file) => file.source === newFileDest),
);

expect(isValidFileLinted).toBe(false);
expect(isInvalidFileLinted).toBe(true);

const fileCache = fCache.createFromFile(expectedCacheFilePath);
const { cache } = fileCache;

expect(typeof cache.getKey(validFile) === 'object').toBe(true);
expect(typeof cache.getKey(newFileDest) === 'undefined').toBe(true);
});
it('invalid files are not cached', async () => {
await fs.copyFile(invalidFile, newFileDest);

// Should lint only changed files
const { errored, results } = await standalone(getConfig());

expect(errored).toBe(true);

// Ensure only changed files are linted
expect(results.some((file) => file.source === validFile)).toBe(false);
expect(results.some((file) => file.source === newFileDest)).toBe(true);

const { cache } = fCache.createFromFile(expectedCacheFilePath);

expect(typeof cache.getKey(validFile)).toBe('object');
expect(cache.getKey(validFile)).toBeTruthy();
expect(cache.getKey(newFileDest)).toBeUndefined();
});
it('files with syntax errors are not cached', () => {
return fs
.copyFile(syntaxErrorFile, newFileDest)
.then(() => {
// Should lint only changed files
return standalone(getConfig());
})
.then(() => {
const fileCache = fCache.createFromFile(expectedCacheFilePath);
const { cache } = fileCache;

expect(typeof cache.getKey(validFile) === 'object').toBe(true);
expect(typeof cache.getKey(newFileDest) === 'undefined').toBe(true);
});

it('files with syntax errors are not cached', async () => {
await fs.copyFile(syntaxErrorFile, newFileDest);

// Should lint only changed files
await standalone(getConfig());

const { cache } = fCache.createFromFile(expectedCacheFilePath);

expect(typeof cache.getKey(validFile)).toBe('object');
expect(cache.getKey(validFile)).toBeTruthy();
expect(cache.getKey(newFileDest)).toBeUndefined();
});

it('cache file is removed when cache is disabled', async () => {
const noCacheConfig = getConfig();

noCacheConfig.cache = false;

await standalone(noCacheConfig);
await expect(fileExists(expectedCacheFilePath)).resolves.toBe(false);
expect(existsSync(expectedCacheFilePath)).toBe(false);
});
// eslint-disable-next-line jest/no-disabled-tests
it.skip("cache doesn't do anything if string is passed", async () => {

it("cache doesn't do anything if string is passed", async () => {
// Ensure no cache file
await removeFile(expectedCacheFilePath);
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[note] This addition of removeFile() fixes #5309.


const config = {
code: '.foo {}',
codeFilename: 'foo.css',
Expand All @@ -178,64 +155,50 @@ describe('standalone cache', () => {
const lintResults = await standalone(config);

expect(lintResults.errored).toBe(false);

await expect(fileExists(expectedCacheFilePath)).resolves.toBe(false);
expect(existsSync(expectedCacheFilePath)).toBe(false);
});
});

describe('standalone cache uses cacheLocation', () => {
const cacheLocationFile = path.join(fixturesPath, 'cache', '.cachefile');
const cacheLocationDir = path.join(fixturesPath, 'cache');
const expectedCacheFilePath = path.join(cacheLocationDir, `.stylelintcache_${hash(cwd)}`);

afterEach(() => {
afterEach(async () => {
// clean up after each test
return Promise.all([
fs.unlink(expectedCacheFilePath).catch(() => {
// fs.unlink() throws an error if file doesn't exist and it's ok. We just
// want to make sure it's not there for next test.
}),
fs.unlink(cacheLocationFile).catch(() => {
// fs.unlink() throws an error if file doesn't exist and it's ok. We just
// want to make sure it's not there for next test.
}),
]);
await removeFile(expectedCacheFilePath);
await removeFile(cacheLocationFile);
});
it('cacheLocation is a file', () => {

it('cacheLocation is a file', async () => {
const config = getConfig();

config.cacheLocation = cacheLocationFile;

return standalone(config)
.then(() => {
// Ensure cache file is created
return fileExists(cacheLocationFile);
})
.then((fileStats) => {
expect(Boolean(fileStats)).toBe(true);
await standalone(config);

// Ensure cache file is created
expect(existsSync(cacheLocationFile)).toBe(true);

const fileCache = fCache.createFromFile(cacheLocationFile);
const { cache } = fileCache;
const { cache } = fCache.createFromFile(cacheLocationFile);

expect(typeof cache.getKey(validFile) === 'object').toBe(true);
});
expect(typeof cache.getKey(validFile)).toBe('object');
expect(cache.getKey(validFile)).toBeTruthy();
});
it('cacheLocation is a directory', () => {

it('cacheLocation is a directory', async () => {
const config = getConfig();

config.cacheLocation = cacheLocationDir;

return standalone(config)
.then(() => {
return fileExists(expectedCacheFilePath);
})
.then((cacheFileStats) => {
// Ensure cache file is created
expect(Boolean(cacheFileStats)).toBe(true);
await standalone(config);

// Ensure cache file is created
expect(existsSync(expectedCacheFilePath)).toBe(true);

const fileCache = fCache.createFromFile(expectedCacheFilePath);
const { cache } = fileCache;
const { cache } = fCache.createFromFile(expectedCacheFilePath);

expect(typeof cache.getKey(validFile) === 'object').toBe(true);
});
expect(typeof cache.getKey(validFile)).toBe('object');
expect(cache.getKey(validFile)).toBeTruthy();
});
});