From 5594637b625b601c01781a51c824fc00d031dc40 Mon Sep 17 00:00:00 2001 From: Michael Mason <105235096+mmason2-godaddy@users.noreply.github.com> Date: Mon, 23 Jan 2023 06:20:24 -0800 Subject: [PATCH] [PFX-186] - jest refactor @gasket/plugin-webpack (#538) --- package-lock.json | 20 ++--- packages/gasket-plugin-webpack/package.json | 23 +++--- .../test/deprecated-merges.test.js | 69 +++++++++-------- .../test/init-webpack.test.js | 66 ++++++++--------- .../gasket-plugin-webpack/test/plugin.test.js | 18 ++--- .../test/webpack-metrics-plugin.test.js | 74 +++++++++---------- 6 files changed, 122 insertions(+), 148 deletions(-) diff --git a/package-lock.json b/package-lock.json index 9f1e1276e..bc4943cae 100644 --- a/package-lock.json +++ b/package-lock.json @@ -37388,18 +37388,14 @@ }, "devDependencies": { "@gasket/engine": "^6.36.1", - "assume": "^2.3.0", - "assume-sinon": "^1.1.0", + "cross-env": "^7.0.3", "eslint": "^8.7.0", "eslint-config-godaddy": "^6.0.0", + "eslint-plugin-jest": "^27.2.1", "eslint-plugin-json": "^3.1.0", - "eslint-plugin-mocha": "^10.0.3", "eslint-plugin-unicorn": "^44.0.0", - "mocha": "^10.0.0", + "jest": "^29.3.1", "mock-require": "^3.0.3", - "nyc": "^15.1.0", - "proxyquire": "^2.1.3", - "sinon": "^14.0.0", "webpack": "^5.21.2" } }, @@ -41888,18 +41884,14 @@ "version": "file:packages/gasket-plugin-webpack", "requires": { "@gasket/engine": "^6.36.1", - "assume": "^2.3.0", - "assume-sinon": "^1.1.0", + "cross-env": "*", "eslint": "^8.7.0", "eslint-config-godaddy": "^6.0.0", + "eslint-plugin-jest": "*", "eslint-plugin-json": "^3.1.0", - "eslint-plugin-mocha": "^10.0.3", "eslint-plugin-unicorn": "^44.0.0", - "mocha": "^10.0.0", + "jest": "*", "mock-require": "^3.0.3", - "nyc": "^15.1.0", - "proxyquire": "^2.1.3", - "sinon": "^14.0.0", "webpack": "^5.21.2", "webpack-chain": "^6.0.0", "webpack-merge": "^4.2.1" diff --git a/packages/gasket-plugin-webpack/package.json b/packages/gasket-plugin-webpack/package.json index e6d15a9a5..89239e1eb 100644 --- a/packages/gasket-plugin-webpack/package.json +++ b/packages/gasket-plugin-webpack/package.json @@ -11,12 +11,10 @@ "scripts": { "lint": "eslint .", "lint:fix": "npm run lint -- --fix", - "test": "npm run test:runner", - "test:runner": "mocha --require ./test/setup.js \"test/**/*.test.js\"", - "test:watch": "npm run test:runner -- --watch", - "test:coverage": "nyc --reporter=text --reporter=json-summary npm run test:runner", - "posttest": "npm run lint", - "report": "nyc report --reporter=lcov" + "test": "cross-env NODE_OPTIONS='--unhandled-rejections=strict' jest", + "test:watch": "jest --watch", + "test:coverage": "jest --coverage", + "posttest": "npm run lint" }, "repository": { "type": "git", @@ -45,23 +43,20 @@ }, "devDependencies": { "@gasket/engine": "^6.36.1", - "assume": "^2.3.0", - "assume-sinon": "^1.1.0", + "cross-env": "^7.0.3", "eslint": "^8.7.0", "eslint-config-godaddy": "^6.0.0", + "eslint-plugin-jest": "^27.2.1", "eslint-plugin-json": "^3.1.0", - "eslint-plugin-mocha": "^10.0.3", "eslint-plugin-unicorn": "^44.0.0", - "mocha": "^10.0.0", + "jest": "^29.3.1", "mock-require": "^3.0.3", - "nyc": "^15.1.0", - "proxyquire": "^2.1.3", - "sinon": "^14.0.0", "webpack": "^5.21.2" }, "eslintConfig": { "extends": [ - "godaddy" + "godaddy", + "plugin:jest/recommended" ], "plugins": [ "unicorn" diff --git a/packages/gasket-plugin-webpack/test/deprecated-merges.test.js b/packages/gasket-plugin-webpack/test/deprecated-merges.test.js index d2ea4174a..827067a60 100644 --- a/packages/gasket-plugin-webpack/test/deprecated-merges.test.js +++ b/packages/gasket-plugin-webpack/test/deprecated-merges.test.js @@ -1,18 +1,21 @@ -const assume = require('assume'); -const sinon = require('sinon'); -const proxyquire = require('proxyquire'); -const deprecatedSmartMerge = require('webpack-merge').smart; +const mockSmartStub = jest.fn(); + +jest.mock('webpack-merge', () => ({ + smart: mockSmartStub +})); + +const deprecatedMerges = require('../lib/deprecated-merges'); describe('deprecatedMerges', function () { - let deprecatedMerges, smartStub, mockGasket, mockContext, mockConfig; + let mockGasket, mockContext, mockConfig; let mockChain, webpackChainCb, webpackCb; beforeEach(function () { mockChain = { - toConfig: sinon.stub().returns({}) + toConfig: jest.fn().mockReturnValue({}) }; mockGasket = { - execApplySync: sinon.stub().callsFake((name, callback) => { + execApplySync: jest.fn().mockImplementation((name, callback) => { if (name === 'webpackChain') { webpackChainCb = callback; return mockChain; @@ -22,33 +25,27 @@ describe('deprecatedMerges', function () { } }), logger: { - warning: sinon.stub() + warning: jest.fn() }, config: {} }; mockContext = {}; mockConfig = {}; - - smartStub = sinon.stub().callsFake(deprecatedSmartMerge); - deprecatedMerges = proxyquire('../lib/deprecated-merges', { - 'webpack-merge': { - smart: smartStub - } - }); + mockSmartStub.mockReturnValue({}); }); afterEach(function () { - sinon.restore(); + jest.clearAllMocks(); }); it('returns webpack config object', function () { const results = deprecatedMerges(mockGasket, mockConfig, mockContext); - assume(results).is.an('object'); + expect(typeof results).toBe('object'); }); it('smart merges', function () { deprecatedMerges(mockGasket, mockConfig, mockContext); - assume(smartStub).called(); + expect(mockSmartStub).toHaveBeenCalled(); }); describe('gasket.config.webpack', function () { @@ -56,12 +53,12 @@ describe('deprecatedMerges', function () { it('logs deprecated warning if set', function () { mockGasket.config.webpack = {}; deprecatedMerges(mockGasket, mockConfig, mockContext); - assume(mockGasket.logger.warning).calledWithMatch(/DEPRECATED `webpack` in Gasket config/); + expect(mockGasket.logger.warning).toHaveBeenCalledWith(expect.stringMatching(/DEPRECATED `webpack` in Gasket config/)); }); it('does not log warning if not set', function () { deprecatedMerges(mockGasket, mockConfig, mockContext); - assume(mockGasket.logger.warning).not.called(); + expect(mockGasket.logger.warning).not.toHaveBeenCalled(); }); }); @@ -72,24 +69,24 @@ describe('deprecatedMerges', function () { }); it('logs deprecated warning', function () { - webpackChainCb({ name: 'mock-plugin' }, sinon.stub()); - assume(mockGasket.logger.warning).calledWithMatch(/DEPRECATED `webpackChain` lifecycle/); + webpackChainCb({ name: 'mock-plugin' }, jest.fn()); + expect(mockGasket.logger.warning).toHaveBeenCalledWith(expect.stringMatching(/DEPRECATED `webpackChain` lifecycle/)); }); it('logs plugin name', function () { - webpackChainCb({ name: 'mock-plugin' }, sinon.stub()); - assume(mockGasket.logger.warning).calledWithMatch(/mock-plugin/); + webpackChainCb({ name: 'mock-plugin' }, jest.fn()); + expect(mockGasket.logger.warning).toHaveBeenCalledWith(expect.stringMatching(/mock-plugin/)); }); it('logs unnamed plugin', function () { - webpackChainCb({}, sinon.stub()); - assume(mockGasket.logger.warning).calledWithMatch(/unnamed plugin/); + webpackChainCb({}, jest.fn()); + expect(mockGasket.logger.warning).toHaveBeenCalledWith(expect.stringMatching(/unnamed plugin/)); }); it('logs app lifecycle', function () { // eslint-disable-next-line no-undefined - webpackChainCb(undefined, sinon.stub()); - assume(mockGasket.logger.warning).calledWithMatch(/app lifecycle/); + webpackChainCb(undefined, jest.fn()); + expect(mockGasket.logger.warning).toHaveBeenCalledWith(expect.stringMatching(/app lifecycle/)); }); }); @@ -100,24 +97,24 @@ describe('deprecatedMerges', function () { }); it('logs deprecated warning', function () { - webpackCb({ name: 'mock-plugin' }, sinon.stub()); - assume(mockGasket.logger.warning).calledWithMatch(/DEPRECATED `webpack` lifecycle/); + webpackCb({ name: 'mock-plugin' }, jest.fn()); + expect(mockGasket.logger.warning).toHaveBeenCalledWith(expect.stringMatching(/DEPRECATED `webpack` lifecycle/)); }); it('logs plugin name', function () { - webpackCb({ name: 'mock-plugin' }, sinon.stub()); - assume(mockGasket.logger.warning).calledWithMatch(/mock-plugin/); + webpackCb({ name: 'mock-plugin' }, jest.fn()); + expect(mockGasket.logger.warning).toHaveBeenCalledWith(expect.stringMatching(/mock-plugin/)); }); it('logs unnamed plugin', function () { - webpackCb({}, sinon.stub()); - assume(mockGasket.logger.warning).calledWithMatch(/unnamed plugin/); + webpackCb({}, jest.fn()); + expect(mockGasket.logger.warning).toHaveBeenCalledWith(expect.stringMatching(/unnamed plugin/)); }); it('logs app lifecycle', function () { // eslint-disable-next-line no-undefined - webpackCb(undefined, sinon.stub()); - assume(mockGasket.logger.warning).calledWithMatch(/app lifecycle/); + webpackCb(undefined, jest.fn()); + expect(mockGasket.logger.warning).toHaveBeenCalledWith(expect.stringMatching(/app lifecycle/)); }); }); }); diff --git a/packages/gasket-plugin-webpack/test/init-webpack.test.js b/packages/gasket-plugin-webpack/test/init-webpack.test.js index 8b5dc1070..c37ac493f 100644 --- a/packages/gasket-plugin-webpack/test/init-webpack.test.js +++ b/packages/gasket-plugin-webpack/test/init-webpack.test.js @@ -1,46 +1,42 @@ /* eslint-disable no-sync */ -const assume = require('assume'); -const sinon = require('sinon'); -const proxyquire = require('proxyquire'); -const WebpackMetricsPlugin = require('../lib/webpack-metrics-plugin'); +jest.mock('../lib/deprecated-merges', () => (_, config) => config); +const initWebpack = require('../lib/init-webpack'); describe('deprecated merges', function () { - let initWebpack, mockGasket, mockContext, mockConfig; + let mockGasket, mockContext, mockConfig; beforeEach(function () { mockGasket = { - execApplySync: sinon.stub(), + execApplySync: jest.fn(), logger: { - warning: sinon.stub() - } + warning: jest.fn() + }, + config: {} }; mockContext = {}; mockConfig = {}; - - initWebpack = proxyquire('../lib/init-webpack', { - './deprecated-merges': sinon.stub().callsFake((_, config) => config) - }); }); afterEach(function () { - sinon.restore(); + jest.clearAllMocks(); + jest.resetModules(); }); it('returns webpack config object', function () { const results = initWebpack(mockGasket, mockConfig, mockContext); - assume(results).is.an('object'); + expect(typeof results).toBe('object'); }); it('configures webpack metrics plugin', function () { const results = initWebpack(mockGasket, mockConfig, mockContext); - assume(results).property('plugins'); - assume(results.plugins[0]).instanceof(WebpackMetricsPlugin); + expect(results).toHaveProperty('plugins'); + expect(results.plugins[0].constructor.name).toBe('WebpackMetricsPlugin'); }); it('executes webpackConfig lifecycle', function () { initWebpack(mockGasket, mockConfig, mockContext); - assume(mockGasket.execApplySync).calledWith('webpackConfig'); + expect(mockGasket.execApplySync).toHaveBeenCalledWith('webpackConfig', expect.any(Function)); }); describe('webpackConfig lifecycle callback', function () { @@ -48,67 +44,67 @@ describe('deprecated merges', function () { beforeEach(function () { mockPlugin = { name: 'mock-plugin' }; - handlerStub = sinon.stub(); + handlerStub = jest.fn(); baseConfig = initWebpack(mockGasket, {}, mockContext); - applyFn = mockGasket.execApplySync.getCall(0).args[1]; + applyFn = mockGasket.execApplySync.mock.calls[0][1]; }); it('called with baseConfig', function () { applyFn(mockPlugin, handlerStub); - assume(handlerStub).calledWith(baseConfig); + expect(handlerStub).toHaveBeenCalledWith(baseConfig, expect.any(Object)); }); it('called with context', function () { applyFn(mockPlugin, handlerStub); - const context = handlerStub.getCall(0).args[1]; - assume(context).is.an('object'); + const context = handlerStub.mock.calls[0][1]; + expect(typeof context).toBe('object'); }); // TODO: remove in next major version describe('context.webpackMerge', function () { it('getter logs deprecated warning', function () { applyFn(mockPlugin, handlerStub); - const context = handlerStub.getCall(0).args[1]; - assume(mockGasket.logger.warning).not.called(); + const context = handlerStub.mock.calls[0][1]; + expect(mockGasket.logger.warning).not.toHaveBeenCalled(); context.webpackMerge; - assume(mockGasket.logger.warning).calledWithMatch(/DEPRECATED/); + expect(mockGasket.logger.warning).toHaveBeenCalledWith(expect.stringMatching(/DEPRECATED/)); }); it('logs plugin name', function () { applyFn(mockPlugin, handlerStub); - const context = handlerStub.getCall(0).args[1]; + const context = handlerStub.mock.calls[0][1]; context.webpackMerge; - assume(mockGasket.logger.warning).calledWithMatch(/mock-plugin/); + expect(mockGasket.logger.warning).toHaveBeenCalledWith(expect.stringMatching(/mock-plugin/)); }); it('logs recommendation', function () { applyFn(mockPlugin, handlerStub); - const context = handlerStub.getCall(0).args[1]; + const context = handlerStub.mock.calls[0][1]; context.webpackMerge; - assume(mockGasket.logger.warning).calledWithMatch(/Use `require\('webpack-merge'\)`/); + expect(mockGasket.logger.warning).toHaveBeenCalledWith(expect.stringMatching(/Use `require\('webpack-merge'\)`/)); }); it('logs `unnamed plugin` if plugin name not set', function () { applyFn({}, handlerStub); - const context = handlerStub.getCall(0).args[1]; + const context = handlerStub.mock.calls[0][1]; context.webpackMerge; - assume(mockGasket.logger.warning).calledWithMatch(/unnamed plugin/); + expect(mockGasket.logger.warning).toHaveBeenCalledWith(expect.stringMatching(/unnamed plugin/)); }); it('logs app lifecycle', function () { // eslint-disable-next-line no-undefined applyFn(undefined, handlerStub); - const context = handlerStub.getCall(0).args[1]; + const context = handlerStub.mock.calls[0][1]; context.webpackMerge; - assume(mockGasket.logger.warning).calledWithMatch(/app lifecycle/); + expect(mockGasket.logger.warning).toHaveBeenCalledWith(expect.stringMatching(/app lifecycle/)); }); }); it('context.webpack returns webpack', function () { applyFn(mockPlugin, handlerStub); - const context = handlerStub.getCall(0).args[1]; - assume(context.webpack).equals(require('webpack')); + const context = handlerStub.mock.calls[0][1]; + expect(context.webpack).toEqual(require('webpack')); }); }); diff --git a/packages/gasket-plugin-webpack/test/plugin.test.js b/packages/gasket-plugin-webpack/test/plugin.test.js index d5276c766..6b3d1fd16 100644 --- a/packages/gasket-plugin-webpack/test/plugin.test.js +++ b/packages/gasket-plugin-webpack/test/plugin.test.js @@ -1,16 +1,14 @@ -const { stub } = require('sinon'); -const assume = require('assume'); const plugin = require('../lib/index'); const { devDependencies } = require('../package'); describe('Plugin', () => { it('is an object', () => { - assume(plugin).is.an('object'); + expect(typeof plugin).toBe('object'); }); it('has expected name', () => { - assume(plugin).to.have.property('name', require('../package').name); + expect(plugin).toHaveProperty('name', require('../package').name); }); it('has expected hooks', () => { @@ -19,11 +17,11 @@ describe('Plugin', () => { 'metadata' ]; - assume(plugin).to.have.property('hooks'); + expect(plugin).toHaveProperty('hooks'); const hooks = Object.keys(plugin.hooks); - assume(hooks).eqls(expected); - assume(hooks).is.length(expected.length); + expect(hooks).toEqual(expected); + expect(hooks).toHaveLength(expected.length); }); }); @@ -33,8 +31,8 @@ describe('create hook', () => { mockContext = { pkg: { - add: stub(), - has: stub() + add: jest.fn(), + has: jest.fn() } }; }); @@ -42,7 +40,7 @@ describe('create hook', () => { it('adds appropriate devDependencies', async function () { await plugin.hooks.create({}, mockContext); - assume(mockContext.pkg.add).calledWith('devDependencies', { + expect(mockContext.pkg.add).toHaveBeenCalledWith('devDependencies', { webpack: devDependencies.webpack }); }); diff --git a/packages/gasket-plugin-webpack/test/webpack-metrics-plugin.test.js b/packages/gasket-plugin-webpack/test/webpack-metrics-plugin.test.js index 14bb04e85..a47aa610a 100644 --- a/packages/gasket-plugin-webpack/test/webpack-metrics-plugin.test.js +++ b/packages/gasket-plugin-webpack/test/webpack-metrics-plugin.test.js @@ -1,35 +1,31 @@ -const assume = require('assume'); -const sinon = require('sinon'); -const mock = require('mock-require'); +jest.mock('/path/to/directory/package.json', () => ({ name: 'appName' }), { virtual: true }); const WebpackMetricsPlugin = require('../lib/webpack-metrics-plugin'); -describe('webpack metrics plugin', function () { - let metricsPlugin; - let gasket; +const gasket = { + exec: jest.fn(), + config: { + manifest: { name: 'foo' } + } +}; +const metricsPlugin = new WebpackMetricsPlugin({ gasket }); - beforeEach(function () { - gasket = { - exec: sinon.spy(sinon.stub()), - config: { - manifest: { name: 'foo' } - } - }; - metricsPlugin = new WebpackMetricsPlugin({ gasket }); +describe('webpack metrics plugin', function () { - mock('/path/to/directory/package.json', { name: 'appName' }); + beforeEach(() => { + jest.clearAllMocks(); }); it('initiates metric lifecycle with correct data format', function () { - const tap = sinon.stub().yields({ + const tap = jest.fn().mockImplementation((_, fn) => fn({ assets: { - 'test/thing/baz1.jpg': { size: sinon.stub() }, - 'test/thing/baz2.css': { size: sinon.stub() }, - 'test/thing2/baz1.js': { size: sinon.stub() }, - 'test/thing2/baz2.html': { size: sinon.stub() } + 'test/thing/baz1.jpg': { size: jest.fn() }, + 'test/thing/baz2.css': { size: jest.fn() }, + 'test/thing2/baz1.js': { size: jest.fn() }, + 'test/thing2/baz2.html': { size: jest.fn() } } - }); + })); metricsPlugin.apply({ options: { @@ -41,26 +37,26 @@ describe('webpack metrics plugin', function () { tap } }, - metrics: sinon.stub() + metrics: jest.fn() }); - assume(gasket.exec).is.calledWith('metrics', sinon.match({ + expect(gasket.exec).toHaveBeenCalledWith('metrics', expect.objectContaining({ name: 'appName', event: 'webpack', - data: sinon.match.object, - time: sinon.match.number + data: expect.any(Object), + time: expect.any(Number) })); }); it('plugins executes expected webpack hook', function () { - const tap = sinon.stub().yields({ + const tap = jest.fn().mockImplementation((_, fn) => fn({ assets: { - 'test/thing/baz1.jpg': { size: sinon.stub() }, - 'test/thing/baz2.css': { size: sinon.stub() }, - 'test/thing2/baz1.js': { size: sinon.stub() }, - 'test/thing2/baz2.html': { size: sinon.stub() } + 'test/thing/baz1.jpg': { size: jest.fn() }, + 'test/thing/baz2.css': { size: jest.fn() }, + 'test/thing2/baz1.js': { size: jest.fn() }, + 'test/thing2/baz2.html': { size: jest.fn() } } - }); + })); metricsPlugin.apply({ options: { @@ -74,18 +70,18 @@ describe('webpack metrics plugin', function () { } }); - assume(tap).is.calledWith('WebpackMetricsPlugin'); + expect(tap).toHaveBeenCalledWith('WebpackMetricsPlugin', expect.any(Function)); }); it('metric lifecycle only called once', function () { - const tap = sinon.stub().yields({ + const tap = jest.fn().mockImplementation((_, fn) => fn({ assets: { - 'test/thing/baz1.jpg': { size: sinon.stub() }, - 'test/thing/baz2.css': { size: sinon.stub() }, - 'test/thing2/baz1.js': { size: sinon.stub() }, - 'test/thing2/baz2.html': { size: sinon.stub() } + 'test/thing/baz1.jpg': { size: jest.fn() }, + 'test/thing/baz2.css': { size: jest.fn() }, + 'test/thing2/baz1.js': { size: jest.fn() }, + 'test/thing2/baz2.html': { size: jest.fn() } } - }); + })); metricsPlugin.apply({ options: { @@ -99,6 +95,6 @@ describe('webpack metrics plugin', function () { } }); - assume(gasket.exec.calledOnce); + expect(gasket.exec.mock.calls).toHaveLength(1); }); });