From c7af1165fb7223af3104e225e9c10d8f74ce6bf8 Mon Sep 17 00:00:00 2001 From: ecksteij Date: Mon, 15 Nov 2021 15:16:11 -0500 Subject: [PATCH] All unit tests refactored and pass --- lib/index.test.js | 569 ++++++++++++++++++++++++++++++++-------------- 1 file changed, 392 insertions(+), 177 deletions(-) diff --git a/lib/index.test.js b/lib/index.test.js index 4010439..8514124 100644 --- a/lib/index.test.js +++ b/lib/index.test.js @@ -1,13 +1,9 @@ 'use strict'; -const Chance = require('chance'); const { promisify: getMockExecAsync } = require('util'); -const path = require('path'); const verifyDeps = require('.'); -const { join: mockJoin } = path; - jest.mock('child_process', () => ({})); jest.mock('util', () => { const mockExecAsync = jest.fn(); @@ -20,7 +16,6 @@ jest.mock('chalk', () => ({ red: (str) => str })); -const chance = new Chance(); const mockExecAsync = getMockExecAsync(); describe('lib/index', () => { @@ -34,14 +29,14 @@ describe('lib/index', () => { }); test('should upgrade upto patch versions when versions prefixed with ~', async () => { - const dir = 'foo'; + const dir = 'dir1'; jest.mock( - `foo/package.json`, + `dir1/package.json`, () => ({ dependencies: { foo1: '~46.45.44' }, devDependencies: {} }), { virtual: true } ); - jest.mock('foo/node_modules/foo1/package.json', () => ({ version: '46.45.44' }), { + jest.mock('dir1/node_modules/foo1/package.json', () => ({ version: '46.45.44' }), { virtual: true }); @@ -64,13 +59,14 @@ describe('lib/index', () => { expect(logger.info).toHaveBeenNthCalledWith(4, 'npm i foo1@46.45.45 '); }); - test.only('should upgrade upto minor versions when versions prefixed with ^', async () => { + test('should upgrade upto minor versions when versions prefixed with ^', async () => { + const dir = 'dir2'; jest.mock( - `bar/package.json`, + `dir2/package.json`, () => ({ dependencies: { foo1: '^46.45.44' }, devDependencies: {} }), { virtual: true } ); - jest.mock('bar/node_modules/foo1/package.json', () => ({ version: '46.45.44' }), { + jest.mock('dir2/node_modules/foo1/package.json', () => ({ version: '46.45.44' }), { virtual: true }); @@ -82,7 +78,7 @@ describe('lib/index', () => { Promise.resolve({ stdout: JSON.stringify({ latest: '46.46.0' }) }) ); - await expect(verifyDeps({ dir: 'bar', logger })).rejects.toThrow( + await expect(verifyDeps({ dir, logger })).rejects.toThrow( 'Please update your installed modules' ); @@ -94,9 +90,15 @@ describe('lib/index', () => { }); test('should not upgrade locked versions', async () => { - mockExports.version = '46.45.44'; - mockExports.dependencies = { foo1: '46.45.44' }; - mockExports.devDependencies = {}; + const dir = 'dir3'; + jest.mock( + `dir3/package.json`, + () => ({ dependencies: { foo1: '46.45.44' }, devDependencies: {} }), + { virtual: true } + ); + jest.mock('dir3/node_modules/foo1/package.json', () => ({ version: '46.45.44' }), { + virtual: true + }); await expect(verifyDeps({ dir, logger })).resolves.toBeUndefined(); @@ -106,15 +108,18 @@ describe('lib/index', () => { }); test('should compare installed dependencies to latest NPM versions', async () => { - mockExports.version = '1.0.0'; - mockExports.dependencies = { jugageni: '^1.0.0', soawitut: '^1.0.0' }; - mockExports.devDependencies = {}; + const dir = 'dir4'; + jest.mock( + `dir4/package.json`, + () => ({ dependencies: { jugageni: '^1.0.0' }, devDependencies: {} }), + { virtual: true } + ); + jest.mock('dir4/node_modules/jugageni/package.json', () => ({ version: '1.0.0' }), { + virtual: true + }); + mockExecAsync - .mockImplementationOnce(() => Promise.resolve({ stdout: JSON.stringify(['1.0.0', '1.0.1']) })) .mockImplementationOnce(() => Promise.resolve({ stdout: JSON.stringify(['1.0.0', '1.0.1']) })) - .mockImplementationOnce(() => - Promise.resolve({ stdout: JSON.stringify({ latest: '1.0.1' }) }) - ) .mockImplementationOnce(() => Promise.resolve({ stdout: JSON.stringify({ latest: '1.0.1' }) }) ); @@ -122,53 +127,62 @@ describe('lib/index', () => { 'Please update your installed modules.' ); expect(mockExecAsync).toHaveBeenCalledWith(`npm view jugageni versions --json`); - expect(mockExecAsync).toHaveBeenCalledWith(`npm view soawitut versions --json`); - expect(mockJoin).toHaveBeenCalledWith(dir, 'package.json'); - expect(mockJoin).toHaveBeenCalledWith(dir, 'node_modules', 'jugageni', 'package.json'); - expect(mockJoin).toHaveBeenCalledWith(dir, 'node_modules', 'soawitut', 'package.json'); + expect(mockExecAsync).toHaveBeenCalledWith(`npm view jugageni dist-tags --json`); }); test('should compare installed devDpendencies to latest NPM versions', async () => { - mockExports.version = '1.0.0'; - mockExports.dependencies = {}; - mockExports.devDependencies = { jugageni: '^1.0.0', soawitut: '^1.0.0' }; + const dir = 'dir5'; + jest.mock( + `dir5/package.json`, + () => ({ dependencies: {}, devDependencies: { jugageni: '^1.0.0' } }), + { virtual: true } + ); + jest.mock('dir5/node_modules/jugageni/package.json', () => ({ version: '1.0.0' }), { + virtual: true + }); mockExecAsync .mockImplementationOnce(() => Promise.resolve({ stdout: JSON.stringify(['1.0.0', '1.0.1']) })) - .mockImplementationOnce(() => Promise.resolve({ stdout: JSON.stringify(['1.0.0']) })) - .mockImplementationOnce(() => Promise.resolve({ stdout: JSON.stringify(['1.0.0', '1.0.1']) })) - .mockImplementationOnce(() => Promise.resolve({ stdout: JSON.stringify(['1.0.0']) })); + .mockImplementationOnce(() => + Promise.resolve({ stdout: JSON.stringify({ latest: '1.0.1' }) }) + ); await expect(verifyDeps({ dir, logger })).rejects.toThrow( 'Please update your installed modules.' ); expect(mockExecAsync).toHaveBeenCalledWith(`npm view jugageni versions --json`); - expect(mockExecAsync).toHaveBeenCalledWith(`npm view soawitut versions --json`); expect(mockExecAsync).toHaveBeenCalledWith(`npm view jugageni dist-tags --json`); - expect(mockExecAsync).toHaveBeenCalledWith(`npm view soawitut dist-tags --json`); - expect(mockJoin).toHaveBeenCalledWith(dir, 'package.json'); - expect(mockJoin).toHaveBeenCalledWith(dir, 'node_modules', 'jugageni', 'package.json'); - expect(mockJoin).toHaveBeenCalledWith(dir, 'node_modules', 'soawitut', 'package.json'); }); test('should show dependency update required when using semver and later version in range is available', async () => { - mockExports.version = '1.0.0'; - mockExports.dependencies = { suji: '^1.0.0', tohepu: '^1.0.0' }; - mockExports.devDependencies = { ju: '^1.0.0', waz: '^1.0.0' }; + const dir = 'dir6'; + jest.mock( + `dir6/package.json`, + () => ({ dependencies: { suji: '^1.0.0' }, devDependencies: { ju: '^1.0.0' } }), + { virtual: true } + ); + jest.mock('dir6/node_modules/suji/package.json', () => ({ version: '1.0.0' }), { + virtual: true + }); + + jest.mock('dir6/node_modules/ju/package.json', () => ({ version: '1.0.0' }), { + virtual: true + }); mockExecAsync .mockImplementationOnce(() => Promise.resolve({ stdout: JSON.stringify(['1.0.0', '1.0.1']) })) - .mockImplementationOnce(() => Promise.resolve({ stdout: JSON.stringify(['1.0.0']) })) .mockImplementationOnce(() => Promise.resolve({ stdout: JSON.stringify(['1.0.0', '1.0.1']) })) - .mockImplementationOnce(() => Promise.resolve({ stdout: JSON.stringify(['1.0.0']) })) - .mockImplementationOnce(() => Promise.resolve({ stdout: JSON.stringify(['1.0.0', '1.0.1']) })) - .mockImplementationOnce(() => Promise.resolve({ stdout: JSON.stringify(['1.0.0']) })) - .mockImplementationOnce(() => Promise.resolve({ stdout: JSON.stringify(['1.0.0', '1.0.1']) })) - .mockImplementationOnce(() => Promise.resolve({ stdout: JSON.stringify(['1.0.0']) })); + .mockImplementationOnce(() => + Promise.resolve({ stdout: JSON.stringify({ latest: '1.0.1' }) }) + ) + .mockImplementationOnce(() => + Promise.resolve({ stdout: JSON.stringify({ latest: '1.0.1' }) }) + ); await expect(verifyDeps({ dir, logger })).rejects.toThrow( 'Please update your installed modules.' ); + console.warn(logger.info.mock.calls); expect(logger.info).toHaveBeenCalledTimes(5); expect(logger.info).toHaveBeenNthCalledWith(1, 'Verifying dependencies…\n'); expect(logger.info).toHaveBeenNthCalledWith(2, `suji is outdated: 1.0.0 → 1.0.1`); @@ -178,19 +192,21 @@ describe('lib/index', () => { }); test('should not show dependency update required when using semver and later version is out of range', async () => { - mockExports.version = '1.0.0'; - mockExports.dependencies = { mi: '^1.0.0', nawpo: '^1.0.0' }; - mockExports.devDependencies = { di: '^1.0.0', koptigu: '^1.0.0' }; + const dir = 'dir7'; + jest.mock( + `dir7/package.json`, + () => ({ dependencies: {}, devDependencies: { mi: '^1.0.0' } }), + { virtual: true } + ); + jest.mock('dir7/node_modules/mi/package.json', () => ({ version: '1.0.0' }), { + virtual: true + }); mockExecAsync .mockImplementationOnce(() => Promise.resolve({ stdout: JSON.stringify(['1.0.0', '2.0.0']) })) - .mockImplementationOnce(() => Promise.resolve({ stdout: JSON.stringify(['1.0.0']) })) - .mockImplementationOnce(() => Promise.resolve({ stdout: JSON.stringify(['1.0.0', '2.0.0']) })) - .mockImplementationOnce(() => Promise.resolve({ stdout: JSON.stringify(['1.0.0']) })) - .mockImplementationOnce(() => Promise.resolve({ stdout: JSON.stringify(['1.0.0', '2.0.0']) })) - .mockImplementationOnce(() => Promise.resolve({ stdout: JSON.stringify(['1.0.0']) })) - .mockImplementationOnce(() => Promise.resolve({ stdout: JSON.stringify(['1.0.0', '2.0.0']) })) - .mockImplementationOnce(() => Promise.resolve({ stdout: JSON.stringify(['1.0.0']) })); + .mockImplementationOnce(() => + Promise.resolve({ stdout: JSON.stringify({ latest: '1.0.0' }) }) + ); await verifyDeps({ dir, logger }); expect(logger.info).toHaveBeenCalledTimes(2); @@ -199,20 +215,44 @@ describe('lib/index', () => { }); test('should show dependency update required when version is locked if non-major-version update available', async () => { - mockExports.version = '1.0.0'; - mockExports.dependencies = { fu: '^1.0.0', lezejujuk: '^1.0.0' }; - mockExports.devDependencies = { kuvrib: '^1.0.0', voliki: '^1.0.0' }; + const dir = 'dir8'; + jest.mock( + `dir8/package.json`, + () => ({ + dependencies: { fu: '^1.0.0', lezejujuk: '^1.0.0' }, + devDependencies: { kuvrib: '^1.0.0', voliki: '^1.0.0' } + }), + { virtual: true } + ); + jest.mock('dir8/node_modules/fu/package.json', () => ({ version: '1.0.0' }), { + virtual: true + }); + jest.mock('dir8/node_modules/lezejujuk/package.json', () => ({ version: '1.0.0' }), { + virtual: true + }); + jest.mock('dir8/node_modules/kuvrib/package.json', () => ({ version: '1.0.0' }), { + virtual: true + }); + jest.mock('dir8/node_modules/voliki/package.json', () => ({ version: '1.0.0' }), { + virtual: true + }); mockExecAsync .mockImplementationOnce(() => Promise.resolve({ stdout: JSON.stringify(['1.0.0']) })) .mockImplementationOnce(() => Promise.resolve({ stdout: JSON.stringify(['1.0.0', '1.1.0']) })) .mockImplementationOnce(() => Promise.resolve({ stdout: JSON.stringify(['1.0.0']) })) .mockImplementationOnce(() => Promise.resolve({ stdout: JSON.stringify(['1.0.0', '1.1.0']) })) - .mockImplementationOnce(() => Promise.resolve({ stdout: JSON.stringify(['1.0.0']) })) - .mockImplementationOnce(() => Promise.resolve({ stdout: JSON.stringify(['1.0.0', '1.1.0']) })) - .mockImplementationOnce(() => Promise.resolve({ stdout: JSON.stringify(['1.0.0']) })) .mockImplementationOnce(() => - Promise.resolve({ stdout: JSON.stringify(['1.0.0', '1.1.0']) }) + Promise.resolve({ stdout: JSON.stringify({ latest: '1.0.0' }) }) + ) + .mockImplementationOnce(() => + Promise.resolve({ stdout: JSON.stringify({ latest: '1.1.0' }) }) + ) + .mockImplementationOnce(() => + Promise.resolve({ stdout: JSON.stringify({ latest: '1.0.0' }) }) + ) + .mockImplementationOnce(() => + Promise.resolve({ stdout: JSON.stringify({ latest: '1.1.0' }) }) ); await expect(verifyDeps({ dir, logger })).rejects.toThrow( @@ -229,16 +269,32 @@ describe('lib/index', () => { ); }); - test('should not show dependency update required when version is locked if only major-version update available', async () => { - mockExports.version = '1.0.0'; - mockExports.dependencies = { an: '^1.0.0', nagte: '1.0.0' }; - mockExports.devDependencies = { pu: '^1.0.0', tedo: '1.0.0' }; + test('should not show dependency update required when version is locked if major-version update available', async () => { + const dir = 'dir9'; + jest.mock( + `dir9/package.json`, + () => ({ + dependencies: { an: '^1.0.0' }, + devDependencies: { pu: '^1.0.0' } + }), + { virtual: true } + ); + jest.mock('dir9/node_modules/an/package.json', () => ({ version: '1.0.0' }), { + virtual: true + }); + jest.mock('dir9/node_modules/pu/package.json', () => ({ version: '1.0.0' }), { + virtual: true + }); mockExecAsync - .mockImplementationOnce(() => Promise.resolve({ stdout: JSON.stringify(['1.0.0']) })) - .mockImplementationOnce(() => Promise.resolve({ stdout: JSON.stringify(['1.0.0']) })) - .mockImplementationOnce(() => Promise.resolve({ stdout: JSON.stringify(['1.0.0']) })) - .mockImplementationOnce(() => Promise.resolve({ stdout: JSON.stringify(['1.0.0']) })); + .mockImplementationOnce(() => Promise.resolve({ stdout: JSON.stringify(['1.0.0', '2.0.0']) })) + .mockImplementationOnce(() => Promise.resolve({ stdout: JSON.stringify(['1.0.0', '2.0.0']) })) + .mockImplementationOnce(() => + Promise.resolve({ stdout: JSON.stringify({ latest: '2.0.0' }) }) + ) + .mockImplementationOnce(() => + Promise.resolve({ stdout: JSON.stringify({ latest: '2.0.0' }) }) + ); await verifyDeps({ dir, logger }); expect(logger.info).toHaveBeenCalledTimes(2); expect(logger.info).toHaveBeenNthCalledWith(1, 'Verifying dependencies…\n'); @@ -246,24 +302,34 @@ describe('lib/index', () => { }); test('should show dependency install required if module cannot be found', async () => { - mockExports.version = '1.0.0'; - mockExports.dependencies = { eri: '^1.0.0', unapup: '^1.0.0' }; - mockExports.devDependencies = { ok: '^1.0.0', zousga: '^1.0.0' }; - - mockJoin.mockImplementation((...args) => { - if (args[2] === 'eri') throw new Error('module not found'); - return pathString; + const dir = 'dir10'; + jest.mock( + `dir10/package.json`, + () => ({ + dependencies: { eri: '^1.0.0' }, + devDependencies: { ok: '^1.0.0' } + }), + { virtual: true } + ); + jest.mock('dir10/node_modules/unapup/package.json', () => ({ version: '1.0.0' }), { + virtual: true + }); + jest.mock('dir10/node_modules/ok/package.json', () => ({ version: '1.0.0' }), { + virtual: true + }); + jest.mock('dir10/node_modules/zousga/package.json', () => ({ version: '1.0.0' }), { + virtual: true }); mockExecAsync .mockImplementationOnce(() => Promise.resolve({ stdout: JSON.stringify(['1.0.0', '1.0.1']) })) - .mockImplementationOnce(() => Promise.resolve({ stdout: JSON.stringify(['1.0.0']) })) - .mockImplementationOnce(() => Promise.resolve({ stdout: JSON.stringify(['1.0.0', '1.0.1']) })) - .mockImplementationOnce(() => Promise.resolve({ stdout: JSON.stringify(['1.0.0']) })) - .mockImplementationOnce(() => Promise.resolve({ stdout: JSON.stringify(['1.0.0', '1.0.1']) })) - .mockImplementationOnce(() => Promise.resolve({ stdout: JSON.stringify(['1.0.0']) })) .mockImplementationOnce(() => Promise.resolve({ stdout: JSON.stringify(['1.0.0', '1.0.1']) })) - .mockImplementationOnce(() => Promise.resolve({ stdout: JSON.stringify(['1.0.0']) })); + .mockImplementationOnce(() => + Promise.resolve({ stdout: JSON.stringify({ latest: '1.0.1' }) }) + ) + .mockImplementationOnce(() => + Promise.resolve({ stdout: JSON.stringify({ latest: '1.0.1' }) }) + ); await expect(verifyDeps({ dir, logger })).rejects.toThrow( 'Please update your installed modules.' @@ -273,7 +339,7 @@ describe('lib/index', () => { expect(logger.info).toHaveBeenNthCalledWith(1, 'Verifying dependencies…\n'); expect(logger.info).toHaveBeenNthCalledWith( 2, - 'Error getting a list of installed modules from package.json - Error: module not found' + "Error getting a list of installed modules from package.json - Error: Cannot find module 'dir10/node_modules/eri/package.json' from 'index.js'" ); expect(logger.info).toHaveBeenNthCalledWith(3, `eri is not installed`); expect(logger.info).toHaveBeenNthCalledWith(4, `ok is outdated: 1.0.0 → 1.0.1`); @@ -282,8 +348,17 @@ describe('lib/index', () => { }); test('should show dependency install required if fetching versions does not return valid JSON output', async () => { - mockExports.version = '1.0.0'; - const invalidOutput = chance.word(); + const dir = 'dir11'; + + jest.mock(`dir11/package.json`, () => ({ dependencies: { kita: '^1.0.0' } }), { + virtual: true + }); + + jest.mock('dir11/node_modules/kita/package.json', () => ({ version: '1.0.0' }), { + virtual: true + }); + + const invalidOutput = 'boo'; mockExecAsync.mockImplementationOnce(() => ({ stdout: invalidOutput })); let syntaxError; try { @@ -297,20 +372,18 @@ describe('lib/index', () => { }); test('should show dependency install required if fetching tags does not return valid JSON output', async () => { - mockExports.version = '1.0.0'; - const invalidOutput = chance.word(); + const dir = 'dir12'; + jest.mock(`dir12/package.json`, () => ({ dependencies: { mks: '^1.0.0' } }), { + virtual: true + }); + + jest.mock('dir12/node_modules/mks/package.json', () => ({ version: '1.0.0' }), { + virtual: true + }); + const invalidOutput = 'boo'; mockExecAsync .mockImplementationOnce(() => - Promise.resolve({ stdout: JSON.stringify({ latest: '1.0.1' }) }) - ) - .mockImplementationOnce(() => - Promise.resolve({ stdout: JSON.stringify({ latest: '1.0.1' }) }) - ) - .mockImplementationOnce(() => - Promise.resolve({ stdout: JSON.stringify({ latest: '1.0.1' }) }) - ) - .mockImplementationOnce(() => - Promise.resolve({ stdout: JSON.stringify({ latest: '1.0.1' }) }) + Promise.resolve({ stdout: JSON.stringify({ stdout: JSON.stringify(['1.0.0', '1.0.1']) }) }) ) .mockImplementationOnce(() => ({ stdout: invalidOutput })); let syntaxError; @@ -325,7 +398,15 @@ describe('lib/index', () => { }); test('throw error when getting latest versions fails', async () => { - mockExports.version = '1.0.0'; + const dir = 'dir13'; + jest.mock(`dir13/package.json`, () => ({ dependencies: { tans: '^1.0.0' } }), { + virtual: true + }); + + jest.mock('dir12/node_modules/tans/package.json', () => ({ version: '1.0.0' }), { + virtual: true + }); + mockExecAsync.mockImplementation(() => { throw new Error('foo'); }); @@ -335,50 +416,83 @@ describe('lib/index', () => { }); test('throw error when getting latest tag fails', async () => { - mockExports.version = '1.0.0'; - mockExecAsync.mockImplementation((command) => { - if (command.match('dist-tags --json')) { - throw new Error('foo1'); - } - return Promise.resolve({ stdout: '{}' }); + const dir = 'dir14'; + jest.mock(`dir14/package.json`, () => ({ dependencies: { utex: '^1.0.0' } }), { + virtual: true }); + + jest.mock('dir14/node_modules/utex/package.json', () => ({ version: '1.0.0' }), { + virtual: true + }); + + mockExecAsync + .mockImplementationOnce(() => + Promise.resolve({ stdout: JSON.stringify({ stdout: JSON.stringify(['1.0.0', '1.0.1']) }) }) + ) + .mockImplementationOnce(() => { + throw new Error('boo'); + }); + await expect(verifyDeps({ dir, logger })).rejects.toThrow( - `Error getting latest tag - Error: foo1` + `Error getting latest tag - Error: boo` ); }); test('should show dependency install required if latest module is installed but not reflected in package.json', async () => { - mockExports.version = '1.0.0'; - mockExports.dependencies = { lule: '^1.0.1', zew: '^1.0.0' }; - mockExports.devDependencies = { neh: '^1.0.1', pe: '^1.0.0' }; + const dir = 'dir15'; + jest.mock( + `dir15/package.json`, + () => ({ + dependencies: { lule: '^1.0.0' }, + devDependencies: { neh: '^1.0.0' } + }), + { + virtual: true + } + ); + + jest.mock('dir15/node_modules/lule/package.json', () => ({ version: '1.0.1' }), { + virtual: true + }); + jest.mock('dir15/node_modules/neh/package.json', () => ({ version: '1.0.1' }), { + virtual: true + }); mockExecAsync .mockImplementationOnce(() => Promise.resolve({ stdout: JSON.stringify(['1.0.0', '1.0.1']) })) - .mockImplementationOnce(() => Promise.resolve({ stdout: JSON.stringify(['1.0.0']) })) .mockImplementationOnce(() => Promise.resolve({ stdout: JSON.stringify(['1.0.0', '1.0.1']) })) - .mockImplementationOnce(() => Promise.resolve({ stdout: JSON.stringify(['1.0.0']) })) - .mockImplementationOnce(() => Promise.resolve({ stdout: JSON.stringify(['1.0.0', '1.0.1']) })) - .mockImplementationOnce(() => Promise.resolve({ stdout: JSON.stringify(['1.0.0']) })) - .mockImplementationOnce(() => Promise.resolve({ stdout: JSON.stringify(['1.0.0', '1.0.1']) })) - .mockImplementationOnce(() => Promise.resolve({ stdout: JSON.stringify(['1.0.0']) })); + .mockImplementationOnce(() => + Promise.resolve({ stdout: JSON.stringify({ latest: '1.0.1' }) }) + ) + .mockImplementationOnce(() => + Promise.resolve({ stdout: JSON.stringify({ latest: '1.0.1' }) }) + ); await expect(verifyDeps({ dir, logger })).rejects.toThrow( 'Please update your installed modules.' ); - // TODO: Handle this case, to avoid misleading messages expect(logger.info).toHaveBeenCalledTimes(5); expect(logger.info).toHaveBeenNthCalledWith(1, 'Verifying dependencies…\n'); - expect(logger.info).toHaveBeenNthCalledWith(2, `lule is outdated: 1.0.1 → 1.0.1`); - expect(logger.info).toHaveBeenNthCalledWith(3, `neh is outdated: 1.0.1 → 1.0.1`); + expect(logger.info).toHaveBeenNthCalledWith(2, `lule is outdated: 1.0.0 → 1.0.1`); + expect(logger.info).toHaveBeenNthCalledWith(3, `neh is outdated: 1.0.0 → 1.0.1`); expect(logger.info).toHaveBeenNthCalledWith(4, `\n${'To resolve this, run:'}`); expect(logger.info).toHaveBeenNthCalledWith(5, `npm i lule@1.0.1 \nnpm i -D neh@1.0.1 `); }); test('should not throw an error if no dependencies are in package.json', async () => { - mockExports.version = '1.0.0'; - mockExports.dependencies = undefined; - mockExports.devDependencies = undefined; + const dir = 'dir16'; + jest.mock( + `dir16/package.json`, + () => ({ + dependencies: undefined, + devDependencies: undefined + }), + { + virtual: true + } + ); + await verifyDeps({ dir, logger }); expect(logger.info).toHaveBeenCalledTimes(2); expect(logger.info).toHaveBeenNthCalledWith(1, 'Verifying dependencies…\n'); @@ -386,21 +500,39 @@ describe('lib/index', () => { }); test('should default to native console when no logger is passed', async () => { - mockExports.version = '1.0.0'; const consoleInfo = console.info; console.info = jest.fn(); - mockExports.dependencies = { eri: '^1.0.0', unapup: '^1.0.0' }; - mockExports.devDependencies = { ok: '^1.0.0', zousga: '^1.0.0' }; + + const dir = 'dir17'; + + jest.mock( + `dir17/package.json`, + () => ({ + dependencies: { eri: '^1.0.0' }, + devDependencies: { ok: '^1.0.0' } + }), + { + virtual: true + } + ); + + jest.mock('dir17/node_modules/eri/package.json', () => ({ version: '1.0.0' }), { + virtual: true + }); + + jest.mock('dir17/node_modules/ok/package.json', () => ({ version: '1.0.0' }), { + virtual: true + }); mockExecAsync .mockImplementationOnce(() => Promise.resolve({ stdout: JSON.stringify(['1.0.0', '1.0.1']) })) - .mockImplementationOnce(() => Promise.resolve({ stdout: JSON.stringify(['1.0.0']) })) - .mockImplementationOnce(() => Promise.resolve({ stdout: JSON.stringify(['1.0.0', '1.0.1']) })) - .mockImplementationOnce(() => Promise.resolve({ stdout: JSON.stringify(['1.0.0']) })) - .mockImplementationOnce(() => Promise.resolve({ stdout: JSON.stringify(['1.0.0', '1.0.1']) })) - .mockImplementationOnce(() => Promise.resolve({ stdout: JSON.stringify(['1.0.0']) })) .mockImplementationOnce(() => Promise.resolve({ stdout: JSON.stringify(['1.0.0', '1.0.1']) })) - .mockImplementationOnce(() => Promise.resolve({ stdout: JSON.stringify(['1.0.0']) })); + .mockImplementationOnce(() => + Promise.resolve({ stdout: JSON.stringify({ latest: '1.0.1' }) }) + ) + .mockImplementationOnce(() => + Promise.resolve({ stdout: JSON.stringify({ latest: '1.0.1' }) }) + ); await expect(verifyDeps({ dir })).rejects.toThrow('Please update your installed modules.'); expect(console.info).toHaveBeenCalledTimes(5); @@ -413,18 +545,19 @@ describe('lib/index', () => { }); test('should not throw type error if options are not passed', async () => { - mockExports.version = '1.0.0'; - mockExports.dependencies = { eri: '^1.0.0', unapup: '^1.0.0' }; - mockExports.devDependencies = { ok: '^1.0.0', zousga: '^1.0.0' }; + jest.mock(`package.json`, () => ({ dependencies: { eri: '^1.0.0' } }), { + virtual: true + }); + + jest.mock('node_modules/eri/package.json', () => ({ version: '1.0.0' }), { + virtual: true + }); + mockExecAsync .mockImplementationOnce(() => Promise.resolve({ stdout: JSON.stringify(['1.0.0', '1.0.1']) })) - .mockImplementationOnce(() => Promise.resolve({ stdout: JSON.stringify(['1.0.0']) })) - .mockImplementationOnce(() => Promise.resolve({ stdout: JSON.stringify(['1.0.0', '1.0.1']) })) - .mockImplementationOnce(() => Promise.resolve({ stdout: JSON.stringify(['1.0.0']) })) - .mockImplementationOnce(() => Promise.resolve({ stdout: JSON.stringify(['1.0.0', '1.0.1']) })) - .mockImplementationOnce(() => Promise.resolve({ stdout: JSON.stringify(['1.0.0']) })) - .mockImplementationOnce(() => Promise.resolve({ stdout: JSON.stringify(['1.0.0', '1.0.1']) })) - .mockImplementationOnce(() => Promise.resolve({ stdout: JSON.stringify(['1.0.0']) })); + .mockImplementationOnce(() => + Promise.resolve({ stdout: JSON.stringify({ latest: '1.0.1' }) }) + ); const consoleInfo = console.info; console.info = jest.fn(); await expect(verifyDeps()).rejects.toThrow('Please update your installed modules.'); @@ -432,9 +565,23 @@ describe('lib/index', () => { }); test('should update to version aliased as latest when aliased latest is less that most recent published version', async () => { - mockExports.version = '1.2.3'; - mockExports.dependencies = { foo1: '^1.2.3' }; - mockExports.devDependencies = { fooDev1: '^1.2.3' }; + const dir = 'dir19'; + + jest.mock( + `dir19/package.json`, + () => ({ dependencies: { foo1: '^1.2.3' }, devDependencies: { fooDev1: '^1.2.3' } }), + { + virtual: true + } + ); + + jest.mock('dir19/node_modules/foo1/package.json', () => ({ version: '1.2.3' }), { + virtual: true + }); + + jest.mock('dir19/node_modules/fooDev1/package.json', () => ({ version: '1.2.3' }), { + virtual: true + }); mockExecAsync .mockImplementationOnce(() => Promise.resolve({ stdout: JSON.stringify(['1.2.4', '1.2.5']) })) @@ -464,9 +611,20 @@ describe('lib/index', () => { }); test('should upgrade pre-release versions to latest pre-release version available', async () => { - mockExports.version = '1.0.0-alpha.0'; - mockExports.dependencies = { foo1: '^1.0.0-alpha.0' }; - mockExports.devDependencies = {}; + const dir = 'dir20'; + + jest.mock( + `dir20/package.json`, + () => ({ dependencies: { foo1: '^1.0.0-alpha.0' }, devDependencies: {} }), + { + virtual: true + } + ); + + jest.mock('dir20/node_modules/foo1/package.json', () => ({ version: '1.0.0-alpha.0' }), { + virtual: true + }); + mockExecAsync .mockImplementationOnce(() => Promise.resolve({ stdout: JSON.stringify(['1.0.0-alpha.1', '1.0.0-alpha.2', '1.2.4']) }) @@ -489,13 +647,18 @@ describe('lib/index', () => { }); test('should not upgrade when no version available for major version of pre-release', async () => { - mockExports.version = '1.0.0-alpha.0'; - mockExports.dependencies = { foo1: '^1.0.0-alpha.0' }; - mockExports.devDependencies = {}; + const dir = 'dir21'; + + jest.mock( + `dir21/package.json`, + () => ({ dependencies: { foo1: '^1.0.0-alpha.0' }, devDependencies: {} }), + { + virtual: true + } + ); - mockJoin.mockImplementation((...args) => { - if (args[2] === 'eri') throw new Error('module not found'); - return pathString; + jest.mock('dir21/node_modules/foo1/package.json', () => ({ version: '1.0.0-alpha.0' }), { + virtual: true }); mockExecAsync @@ -522,12 +685,24 @@ describe('lib/index', () => { }); test('should throw an error when version in package.json is invalid (likely unpublished)', async () => { - mockExports.version = '1.2.4'; - mockExports.dependencies = { foo1: '^1.2.4' }; - mockExports.devDependencies = {}; + const dir = 'dir22'; + + jest.mock( + `dir22/package.json`, + () => ({ dependencies: { foo1: '^1.2.4' }, devDependencies: {} }), + { + virtual: true + } + ); + + jest.mock('dir22/node_modules/foo1/package.json', () => ({ version: '1.2.4' }), { + virtual: true + }); mockExecAsync .mockImplementationOnce(() => Promise.resolve({ stdout: JSON.stringify(['1.0.0', '1.2.1']) })) - .mockImplementationOnce(() => Promise.resolve({ stdout: JSON.stringify(['1.2.1']) })); + .mockImplementationOnce(() => + Promise.resolve({ stdout: JSON.stringify({ latest: '1.2.1' }) }) + ); const error = new Error( `Current version of foo1:^1.2.4 seems to be invalid. The version was likely unpublished. Please manually upgrade to a valid version and re-run this application.` ); @@ -535,19 +710,36 @@ describe('lib/index', () => { }); test('autoUpgrade modules', async () => { - mockExports.version = '1.0.0'; - mockExports.dependencies = { oli: '^1.0.0', upe: '^1.0.0' }; - mockExports.devDependencies = { barda: '^1.0.0', gugkih: '^1.0.0' }; + const dir = 'dir23'; + + jest.mock( + `dir23/package.json`, + () => ({ + dependencies: { oli: '^1.0.0' }, + devDependencies: { barda: '^1.0.0' } + }), + { + virtual: true + } + ); + + jest.mock('dir23/node_modules/oli/package.json', () => ({ version: '1.0.0' }), { + virtual: true + }); + + jest.mock('dir23/node_modules/barda/package.json', () => ({ version: '1.0.0' }), { + virtual: true + }); mockExecAsync .mockImplementationOnce(() => Promise.resolve({ stdout: JSON.stringify(['1.0.0', '1.0.1']) })) - .mockImplementationOnce(() => Promise.resolve({ stdout: JSON.stringify(['1.0.0']) })) - .mockImplementationOnce(() => Promise.resolve({ stdout: JSON.stringify(['1.0.0', '1.0.1']) })) - .mockImplementationOnce(() => Promise.resolve({ stdout: JSON.stringify(['1.0.0']) })) - .mockImplementationOnce(() => Promise.resolve({ stdout: JSON.stringify(['1.0.0', '1.0.1']) })) - .mockImplementationOnce(() => Promise.resolve({ stdout: JSON.stringify(['1.0.0']) })) .mockImplementationOnce(() => Promise.resolve({ stdout: JSON.stringify(['1.0.0', '1.0.1']) })) - .mockImplementationOnce(() => Promise.resolve({ stdout: JSON.stringify(['1.0.0']) })) + .mockImplementationOnce(() => + Promise.resolve({ stdout: JSON.stringify({ latest: '1.0.1' }) }) + ) + .mockImplementationOnce(() => + Promise.resolve({ stdout: JSON.stringify({ latest: '1.0.1' }) }) + ) .mockImplementationOnce(() => Promise.resolve({ stdout: 'oli@1.0.1' })) .mockImplementationOnce(() => Promise.resolve({ stdout: 'barda@1.0.1' })); @@ -567,25 +759,48 @@ describe('lib/index', () => { }); test('throw error when npm module name is invalid', async () => { - mockExports.version = '1.2.3'; - mockExports.dependencies = { 'bad name Dependency': '^1.2.3' }; - mockExports.devDependencies = {}; - mockExecAsync.mockImplementationOnce(() => Promise.resolve({ stdout: ['1.2.3'] })); + const dir = 'dir24'; + + jest.mock( + `dir24/package.json`, + () => ({ dependencies: { 'bad name Dependency': '^1.2.3' }, devDependencies: {} }), + { + virtual: true + } + ); + + jest.mock('dir24/node_modules/bad name Dependency/package.json', () => ({ version: '1.2.3' }), { + virtual: true + }); + await expect(verifyDeps({ autoUpgrade: true, dir, logger })).rejects.toThrow( 'NPM package name: "bad name Dependency" is invalid. name can only contain URL-friendly characters' ); }); - test('should verify dependencies when npm module has only one version available, npm view returns string instead of array', async () => { - mockExports.version = '1.1.1'; - mockExports.dependencies = { foo1: '1.1.1' }; - mockExports.devDependencies = {}; + test('should verify dependencies when npm module has one version available, npm view returns string instead of array', async () => { + const dir = 'dir25'; + + jest.mock( + `dir25/package.json`, + () => ({ + dependencies: { foo1: '^1.1.1' }, + devDependencies: {} + }), + { + virtual: true + } + ); + + jest.mock('dir25/node_modules/foo1/package.json', () => ({ version: '1.1.1' }), { + virtual: true + }); mockExecAsync .mockImplementationOnce(() => Promise.resolve({ stdout: '"1.1.1"' })) - .mockImplementationOnce(() => Promise.resolve({ stdout: '"1.1.1"' })) - .mockImplementationOnce(() => Promise.resolve({ stdout: '"1.1.1"' })) - .mockImplementationOnce(() => Promise.resolve({ stdout: '"1.1.1"' })); + .mockImplementationOnce(() => + Promise.resolve({ stdout: JSON.stringify({ latest: '1.1.1' }) }) + ); await verifyDeps({ dir, logger }); expect(logger.info).toHaveBeenCalledTimes(2);