From 5f8711da396049c206c9aa6b7b227ec7972580c3 Mon Sep 17 00:00:00 2001 From: Rhys Arkins <rhys@keylocation.sg> Date: Thu, 20 Apr 2017 12:15:46 +0200 Subject: [PATCH] Refactor yarn helper and add tests (#174) Refactor yarn helper and increase coverage --- lib/helpers/yarn.js | 44 +++++++++++++++++++ lib/workers/branch.js | 48 +-------------------- test/helpers/yarn.spec.js | 85 +++++++++++++++++++++++++++++++++++-- test/workers/branch.spec.js | 55 ++++++++++++------------ 4 files changed, 155 insertions(+), 77 deletions(-) diff --git a/lib/helpers/yarn.js b/lib/helpers/yarn.js index 2163a50ee0..43d3190616 100644 --- a/lib/helpers/yarn.js +++ b/lib/helpers/yarn.js @@ -6,6 +6,8 @@ const path = require('path'); module.exports = { generateLockFile, + getLockFile, + maintainLockFile, }; async function generateLockFile(newPackageJson, npmrcContent, yarnrcContent) { @@ -26,7 +28,49 @@ async function generateLockFile(newPackageJson, npmrcContent, yarnrcContent) { logger.debug(String(result.stderr)); yarnLock = fs.readFileSync(path.join(tmpDir.name, 'yarn.lock')); } catch (error) { + /* istanbul ignore next */ throw error; } return yarnLock; } + +async function getLockFile(packageFile, packageContent, api) { + // Detect if a yarn.lock file is in use + const yarnLockFileName = path.join(path.dirname(packageFile), 'yarn.lock'); + if (!await api.getFileContent(yarnLockFileName)) { + return null; + } + // Copy over custom config commitFiles + const npmrcContent = await api.getFileContent('.npmrc'); + const yarnrcContent = await api.getFileContent('.yarnrc'); + // Generate yarn.lock using shell command + const newYarnLockContent = + await module.exports.generateLockFile(packageContent, npmrcContent, yarnrcContent); + // Return file object + return ({ + name: yarnLockFileName, + contents: newYarnLockContent, + }); +} + +async function maintainLockFile(inputConfig) { + logger.debug(`maintainYarnLock(${JSON.stringify(inputConfig)})`); + const packageContent = await inputConfig.api.getFileContent(inputConfig.packageFile); + const yarnLockFileName = path.join(path.dirname(inputConfig.packageFile), 'yarn.lock'); + logger.debug(`Checking for ${yarnLockFileName}`); + const existingYarnLock = await inputConfig.api.getFileContent(yarnLockFileName); + logger.silly(`existingYarnLock:\n${existingYarnLock}`); + if (!existingYarnLock) { + return null; + } + logger.debug('Found existing yarn.lock file'); + const newYarnLock = + await module.exports.getLockFile(inputConfig.packageFile, packageContent, inputConfig.api); + logger.silly(`newYarnLock:\n${newYarnLock.contents}`); + if (existingYarnLock.toString() === newYarnLock.contents.toString()) { + logger.debug('Yarn lock file does not need updating'); + return null; + } + logger.debug('Yarn lock needs updating'); + return newYarnLock; +} diff --git a/lib/workers/branch.js b/lib/workers/branch.js index a561fd0ff9..5838551e77 100644 --- a/lib/workers/branch.js +++ b/lib/workers/branch.js @@ -1,14 +1,11 @@ const logger = require('winston'); -const path = require('path'); const handlebars = require('handlebars'); const packageJsonHelper = require('../helpers/package-json'); const yarnHelper = require('../helpers/yarn'); module.exports = { getParentBranch, - getYarnLockFile, ensureBranch, - maintainYarnLock, }; async function getParentBranch(branchName, config) { @@ -52,47 +49,6 @@ async function getParentBranch(branchName, config) { return branchName; } -async function getYarnLockFile(packageFile, packageContent, api) { - // Detect if a yarn.lock file is in use - const yarnLockFileName = path.join(path.dirname(packageFile), 'yarn.lock'); - if (!await api.getFileContent(yarnLockFileName)) { - return null; - } - // Copy over custom config commitFiles - const npmrcContent = await api.getFileContent('.npmrc'); - const yarnrcContent = await api.getFileContent('.yarnrc'); - // Generate yarn.lock using shell command - const newYarnLockContent = - await yarnHelper.generateLockFile(packageContent, npmrcContent, yarnrcContent); - // Return file object - return ({ - name: yarnLockFileName, - contents: newYarnLockContent, - }); -} - -async function maintainYarnLock(inputConfig) { - logger.debug(`maintainYarnLock(${JSON.stringify(inputConfig)})`); - const packageContent = await inputConfig.api.getFileContent(inputConfig.packageFile); - const yarnLockFileName = path.join(path.dirname(inputConfig.packageFile), 'yarn.lock'); - logger.debug(`Checking for ${yarnLockFileName}`); - const existingYarnLock = await inputConfig.api.getFileContent(yarnLockFileName); - logger.silly(`existingYarnLock:\n${existingYarnLock}`); - if (!existingYarnLock) { - return null; - } - logger.debug('Found existing yarn.lock file'); - const newYarnLock = - await getYarnLockFile(inputConfig.packageFile, packageContent, inputConfig.api); - logger.silly(`newYarnLock:\n${newYarnLock.contents}`); - if (existingYarnLock.toString() === newYarnLock.contents.toString()) { - logger.debug('Yarn lock file does not need updating'); - return null; - } - logger.debug('Yarn lock needs updating'); - return newYarnLock; -} - // Ensure branch exists with appropriate content async function ensureBranch(upgrades) { logger.debug(`ensureBranch(${JSON.stringify(upgrades)})`); @@ -107,7 +63,7 @@ async function ensureBranch(upgrades) { const commitFiles = []; for (const upgrade of upgrades) { if (upgrade.upgradeType === 'maintainYarnLock') { - const newYarnLock = await maintainYarnLock(upgrade); + const newYarnLock = await yarnHelper.maintainLockFile(upgrade); if (newYarnLock) { commitFiles.push(newYarnLock); } @@ -141,7 +97,7 @@ async function ensureBranch(upgrades) { contents: packageFiles[packageFile], }); const yarnLockFile = - await module.exports.getYarnLockFile(packageFile, packageFiles[packageFile], api); + await yarnHelper.getLockFile(packageFile, packageFiles[packageFile], api); if (yarnLockFile) { // Add new yarn.lock file too logger.debug(`Adding ${yarnLockFile.name}`); diff --git a/test/helpers/yarn.spec.js b/test/helpers/yarn.spec.js index 6138d04d13..65152ea3eb 100644 --- a/test/helpers/yarn.spec.js +++ b/test/helpers/yarn.spec.js @@ -1,5 +1,84 @@ -require('../../lib/helpers/yarn'); +const yarnHelper = require('../../lib/helpers/yarn'); +const defaultConfig = require('../../lib/config/defaults').getConfig(); -it('placeholder', () => { - // TODO: write tests for this module - this is here so the file shows up in coverage +jest.mock('fs'); +jest.mock('child_process'); +jest.mock('tmp'); + +const fs = require('fs'); +const cp = require('child_process'); +const tmp = require('tmp'); + +describe('generateLockFile(newPackageJson, npmrcContent, yarnrcContent)', () => { + tmp.dirSync = jest.fn(() => ({ name: 'somedir' })); + fs.writeFileSync = jest.fn(); + fs.readFileSync = jest.fn(() => 'yarn-lock-contents'); + cp.spawnSync = jest.fn(() => ({ + stdout: '', + stderror: '', + })); + it('generates lock files', async () => { + const yarnLock = + await yarnHelper.generateLockFile('package-json-contents', 'npmrc-contents', 'yarnrc-contents'); + expect(tmp.dirSync.mock.calls.length).toEqual(1); + expect(fs.writeFileSync.mock.calls.length).toEqual(3); + expect(fs.readFileSync.mock.calls.length).toEqual(1); + expect(yarnLock).toEqual('yarn-lock-contents'); + }); +}); +describe('getLockFile(packageJson, config)', () => { + let api; + beforeEach(() => { + api = { + getFileContent: jest.fn(), + }; + }); + it('returns null if no existing yarn.lock', async () => { + api.getFileContent.mockReturnValueOnce(false); + expect(await yarnHelper.getLockFile('package.json', '', api)).toBe(null); + }); + it('returns yarn.lock file', async () => { + api.getFileContent.mockReturnValueOnce('Existing yarn.lock'); + api.getFileContent.mockReturnValueOnce(null); // npmrc + api.getFileContent.mockReturnValueOnce(null); // yarnrc + yarnHelper.generateLockFile = jest.fn(); + yarnHelper.generateLockFile.mockReturnValueOnce('New yarn.lock'); + const yarnLockFile = { + name: 'yarn.lock', + contents: 'New yarn.lock', + }; + expect(await yarnHelper.getLockFile('package.json', '', api)).toMatchObject(yarnLockFile); + }); +}); + +describe('maintainLockFile(inputConfig)', () => { + let config; + beforeEach(() => { + config = Object.assign({}, defaultConfig); + config.packageFile = 'package.json'; + config.api = { + getFileContent: jest.fn(), + }; + config.api.getFileContent.mockReturnValueOnce('oldPackageContent'); + yarnHelper.getLockFile = jest.fn(); + }); + it('returns null if no file to maintain', async () => { + const yarnLock = await yarnHelper.maintainLockFile(config); + expect(config.api.getFileContent.mock.calls.length).toBe(2); + expect(yarnLock).toEqual(null); + }); + it('returns null if contents match', async () => { + config.api.getFileContent.mockReturnValueOnce('oldYarnLockContent'); + yarnHelper.getLockFile.mockReturnValueOnce({ contents: 'oldYarnLockContent' }); + const yarnLock = await yarnHelper.maintainLockFile(config); + expect(config.api.getFileContent.mock.calls.length).toBe(2); + expect(yarnLock).toEqual(null); + }); + it('returns new yarn lock if contents differ', async () => { + config.api.getFileContent.mockReturnValueOnce('oldYarnLockContent'); + yarnHelper.getLockFile.mockReturnValueOnce({ contents: 'newYarnLockContent' }); + const yarnLock = await yarnHelper.maintainLockFile(config); + expect(config.api.getFileContent.mock.calls.length).toBe(2); + expect(yarnLock).toEqual({ contents: 'newYarnLockContent' }); + }); }); diff --git a/test/workers/branch.spec.js b/test/workers/branch.spec.js index bb5d769a90..45d75729d5 100644 --- a/test/workers/branch.spec.js +++ b/test/workers/branch.spec.js @@ -74,35 +74,13 @@ describe('workers/branch', () => { expect(await branchWorker.getParentBranch(branchName, config)).toBe(undefined); }); }); - describe('getYarnLockFile(packageJson, config)', () => { - let api; - beforeEach(() => { - api = { - getFileContent: jest.fn(), - }; - }); - it('returns null if no existing yarn.lock', async () => { - api.getFileContent.mockReturnValueOnce(false); - expect(await branchWorker.getYarnLockFile('package.json', '', api)).toBe(null); - }); - it('returns yarn.lock file', async () => { - api.getFileContent.mockReturnValueOnce('Existing yarn.lock'); - api.getFileContent.mockReturnValueOnce(null); // npmrc - api.getFileContent.mockReturnValueOnce(null); // yarnrc - yarnHelper.generateLockFile.mockReturnValueOnce('New yarn.lock'); - const yarnLockFile = { - name: 'yarn.lock', - contents: 'New yarn.lock', - }; - expect(await branchWorker.getYarnLockFile('package.json', '', api)).toMatchObject(yarnLockFile); - }); - }); describe('ensureBranch(config)', () => { let config; beforeEach(() => { packageJsonHelper.setNewValue = jest.fn(); branchWorker.getParentBranch = jest.fn(); - branchWorker.getYarnLockFile = jest.fn(); + yarnHelper.getLockFile = jest.fn(); + yarnHelper.maintainLockFile = jest.fn(); config = Object.assign({}, defaultConfig); config.api = {}; config.api.getFileContent = jest.fn(); @@ -120,7 +98,7 @@ describe('workers/branch', () => { await branchWorker.ensureBranch([config]); expect(branchWorker.getParentBranch.mock.calls.length).toBe(1); expect(packageJsonHelper.setNewValue.mock.calls.length).toBe(1); - expect(branchWorker.getYarnLockFile.mock.calls.length).toBe(0); + expect(yarnHelper.getLockFile.mock.calls.length).toBe(0); }); it('commits one file if no yarn lock found', async () => { branchWorker.getParentBranch.mockReturnValueOnce('dummy branch'); @@ -128,18 +106,39 @@ describe('workers/branch', () => { await branchWorker.ensureBranch([config]); expect(branchWorker.getParentBranch.mock.calls.length).toBe(1); expect(packageJsonHelper.setNewValue.mock.calls.length).toBe(1); - expect(branchWorker.getYarnLockFile.mock.calls.length).toBe(1); + expect(yarnHelper.getLockFile.mock.calls.length).toBe(1); expect(config.api.commitFilesToBranch.mock.calls[0][1].length).toBe(1); }); it('commits two files if yarn lock found', async () => { branchWorker.getParentBranch.mockReturnValueOnce('dummy branch'); - branchWorker.getYarnLockFile.mockReturnValueOnce('non null response'); + yarnHelper.getLockFile.mockReturnValueOnce('non null response'); packageJsonHelper.setNewValue.mockReturnValueOnce('new content'); await branchWorker.ensureBranch([config]); expect(branchWorker.getParentBranch.mock.calls.length).toBe(1); expect(packageJsonHelper.setNewValue.mock.calls.length).toBe(1); - expect(branchWorker.getYarnLockFile.mock.calls.length).toBe(1); + expect(yarnHelper.getLockFile.mock.calls.length).toBe(1); expect(config.api.commitFilesToBranch.mock.calls[0][1].length).toBe(2); }); + it('maintains lock files if needing updates', async () => { + branchWorker.getParentBranch.mockReturnValueOnce('dummy branch'); + yarnHelper.maintainLockFile.mockReturnValueOnce('non null response'); + config.upgradeType = 'maintainYarnLock'; + await branchWorker.ensureBranch([config]); + expect(branchWorker.getParentBranch.mock.calls.length).toBe(1); + expect(packageJsonHelper.setNewValue.mock.calls.length).toBe(0); + expect(yarnHelper.getLockFile.mock.calls.length).toBe(0); + expect(yarnHelper.maintainLockFile.mock.calls.length).toBe(1); + expect(config.api.commitFilesToBranch.mock.calls[0][1].length).toBe(1); + }); + it('skips maintaining lock files if no updates', async () => { + branchWorker.getParentBranch.mockReturnValueOnce('dummy branch'); + config.upgradeType = 'maintainYarnLock'; + await branchWorker.ensureBranch([config]); + expect(branchWorker.getParentBranch.mock.calls.length).toBe(1); + expect(packageJsonHelper.setNewValue.mock.calls.length).toBe(0); + expect(yarnHelper.getLockFile.mock.calls.length).toBe(0); + expect(yarnHelper.maintainLockFile.mock.calls.length).toBe(1); + expect(config.api.commitFilesToBranch.mock.calls.length).toBe(0); + }); }); }); -- GitLab