diff --git a/index.test.js b/index.test.js new file mode 100644 index 000000000..49b44f8d9 --- /dev/null +++ b/index.test.js @@ -0,0 +1,376 @@ +'use strict'; +/** + * Unit tests for index. + */ + +const _ = require('lodash'); +const BbPromise = require('bluebird'); +const chai = require('chai'); +const sinon = require('sinon'); +const mockery = require('mockery'); +const Serverless = require('serverless'); +const Module = require('module'); + +chai.use(require('chai-as-promised')); +chai.use(require('sinon-chai')); + +const expect = chai.expect; + +describe('ServerlessWebpack', () => { + let sandbox; + let serverless; + let ServerlessWebpack; + + before(function() { + // Mockery might take some time to clear the cache. So add 3 seconds to the default timeout. + this.timeout(5000); + + sandbox = sinon.createSandbox(); + + mockery.enable({ useCleanCache: true, warnOnUnregistered: false }); + mockery.registerMock('ts-node/register', {}); + mockery.registerMock('webpack', {}); + + ServerlessWebpack = require('./index'); + sandbox.spy(Module, '_load'); + }); + + beforeEach(() => { + serverless = new Serverless(); + serverless.cli = { + log: sandbox.stub(), + consoleLog: sandbox.stub() + }; + + sandbox.stub(serverless.pluginManager, 'spawn').returns(BbPromise.resolve()); + }); + + afterEach(() => { + sandbox.resetHistory(); + }); + + after(() => { + sandbox.restore(); + mockery.disable(); + mockery.deregisterAll(); + }); + + it('should expose a lib object', () => { + const lib = ServerlessWebpack.lib; + expect(lib).to.be.an('object'); + expect(lib).to.have.a.property('entries').that.is.an('object').that.is.empty; + expect(lib).to.have.a.property('webpack').that.is.an('object').that.deep.equals({ + isLocal: false + }); + }); + + describe('with a TS webpack configuration', () => { + it('should support old config and register ts-node', () => { + _.set(serverless, 'service.custom.webpack', 'webpack.config.ts'); + new ServerlessWebpack(serverless, {}); + expect(Module._load).to.have.been.calledOnce; + expect(Module._load).to.have.been.calledWith('ts-node/register'); + }); + + it('should support new config and register ts-node', () => { + _.set(serverless, 'service.custom.webpack.webpackConfig', 'webpack.config.ts'); + new ServerlessWebpack(serverless, {}); + expect(Module._load).to.have.been.calledOnce; + expect(Module._load).to.have.been.calledWith('ts-node/register'); + }); + }); + + describe('with a JS webpack configuration', () => { + it('should not load ts-node', () => { + _.set(serverless, 'service.custom.webpack', 'webpack.config.js'); + new ServerlessWebpack(serverless, {}); + expect(Module._load).to.not.have.been.called; + }); + }); + + _.forEach([ + 'commands.webpack', + 'commands.webpack.commands.validate', + 'commands.webpack.commands.compile', + 'commands.webpack.commands.compile.commands.watch', + 'commands.webpack.commands.package', + ], command => { + it(`should expose command/entrypoint ${_.last(_.split(command, '.'))}`, () => { + const slsw = new ServerlessWebpack(serverless, {}); + expect(slsw).to.have.a.nested.property(command); + }); + }); + + describe('hooks', () => { + let slsw; + + before(() => { + slsw = new ServerlessWebpack(serverless, {}); + sandbox.stub(slsw, 'cleanup').returns(BbPromise.resolve()); + sandbox.stub(slsw, 'watch').returns(BbPromise.resolve()); + sandbox.stub(slsw, 'wpwatch').returns(BbPromise.resolve()); + sandbox.stub(slsw, 'packExternalModules').returns(BbPromise.resolve()); + sandbox.stub(slsw, 'prepareRun').returns(BbPromise.resolve()); + sandbox.stub(slsw, 'watchRun').returns(BbPromise.resolve()); + sandbox.stub(slsw, 'validate').returns(BbPromise.resolve()); + sandbox.stub(slsw, 'compile').returns(BbPromise.resolve()); + sandbox.stub(slsw, 'packageModules').returns(BbPromise.resolve()); + sandbox.stub(slsw, 'prepareLocalInvoke').returns(BbPromise.resolve()); + sandbox.stub(slsw, 'prepareOfflineInvoke').returns(BbPromise.resolve()); + sandbox.stub(slsw, 'prepareStepOfflineInvoke').returns(BbPromise.resolve()); + }); + + beforeEach(() => { + ServerlessWebpack.lib.webpack.isLocal = false; + }); + + after(() => { + slsw.cleanup.restore(); + }); + + _.forEach([ + { + name: 'before:package:createDeploymentArtifacts', + test: () => { + it('should spawn validate, compile and package', () => { + return expect(slsw.hooks['before:package:createDeploymentArtifacts']()).to.be.fulfilled + .then(() => { + expect(slsw.serverless.pluginManager.spawn).to.have.been.calledThrice; + expect(slsw.serverless.pluginManager.spawn.firstCall).to.have.been.calledWithExactly('webpack:validate'); + expect(slsw.serverless.pluginManager.spawn.secondCall).to.have.been.calledWithExactly('webpack:compile'); + expect(slsw.serverless.pluginManager.spawn.thirdCall).to.have.been.calledWithExactly('webpack:package'); + return null; + }); + }); + } + }, + { + name: 'after:package:createDeploymentArtifacts', + test: () => { + it('should call cleanup', () => { + return expect(slsw.hooks['after:package:createDeploymentArtifacts']()).to.be.fulfilled + .then(() => { + expect(slsw.cleanup).to.have.been.calledOnce; + return null; + }); + }); + } + }, + { + name: 'before:deploy:function:packageFunction', + test: () => { + it('should spawn validate, compile and package', () => { + return expect(slsw.hooks['before:deploy:function:packageFunction']()).to.be.fulfilled + .then(() => { + expect(slsw.serverless.pluginManager.spawn).to.have.been.calledThrice; + expect(slsw.serverless.pluginManager.spawn.firstCall).to.have.been.calledWithExactly('webpack:validate'); + expect(slsw.serverless.pluginManager.spawn.secondCall).to.have.been.calledWithExactly('webpack:compile'); + expect(slsw.serverless.pluginManager.spawn.thirdCall).to.have.been.calledWithExactly('webpack:package'); + return null; + }); + }); + } + }, + { + name: 'webpack:webpack', + test: () => { + it('should spawn validate, compile and package', () => { + return expect(slsw.hooks['webpack:webpack']()).to.be.fulfilled + .then(() => { + expect(slsw.serverless.pluginManager.spawn).to.have.been.calledThrice; + expect(slsw.serverless.pluginManager.spawn.firstCall).to.have.been.calledWithExactly('webpack:validate'); + expect(slsw.serverless.pluginManager.spawn.secondCall).to.have.been.calledWithExactly('webpack:compile'); + expect(slsw.serverless.pluginManager.spawn.thirdCall).to.have.been.calledWithExactly('webpack:package'); + return null; + }); + }); + } + }, + { + name: 'before:invoke:local:invoke', + test: () => { + it('should prepare for local invoke', () => { + return expect(slsw.hooks['before:invoke:local:invoke']()).to.be.fulfilled + .then(() => { + expect(ServerlessWebpack.lib.webpack.isLocal).to.be.true; + expect(slsw.serverless.pluginManager.spawn).to.have.been.calledTwice; + expect(slsw.serverless.pluginManager.spawn.firstCall).to.have.been.calledWithExactly('webpack:validate'); + expect(slsw.serverless.pluginManager.spawn.secondCall).to.have.been.calledWithExactly('webpack:compile'); + expect(slsw.prepareLocalInvoke).to.have.been.calledOnce; + return null; + }); + }); + } + }, + { + name: 'after:invoke:local:invoke', + test: () => { + it('should return if watch is disabled', () => { + slsw.options.watch = false; + return expect(slsw.hooks['after:invoke:local:invoke']()).to.be.fulfilled + .then(() => { + expect(slsw.watch).to.not.have.been.called; + return null; + }); + }); + + it('should watch if enabled', () => { + slsw.options.watch = true; + return expect(slsw.hooks['after:invoke:local:invoke']()).to.be.fulfilled + .then(() => { + expect(slsw.watch).to.have.been.calledOnce; + expect(slsw.watch).to.have.been.calledWithExactly('invoke:local'); + return null; + }); + }); + } + }, + { + name: 'before:run:run', + test: () => { + it('should prepare for run', () => { + return expect(slsw.hooks['before:run:run']()).to.be.fulfilled + .then(() => { + expect(slsw.serverless.service.package.individually).to.be.false; + expect(slsw.serverless.pluginManager.spawn).to.have.been.calledTwice; + expect(slsw.serverless.pluginManager.spawn.firstCall).to.have.been.calledWithExactly('webpack:validate'); + expect(slsw.serverless.pluginManager.spawn.secondCall).to.have.been.calledWithExactly('webpack:compile'); + expect(slsw.packExternalModules).to.have.been.calledOnce; + expect(slsw.prepareRun).to.have.been.calledOnce; + return null; + }); + }); + } + }, + { + name: 'after:run:run', + test: () => { + it('should return if watch is disabled', () => { + slsw.options.watch = false; + return expect(slsw.hooks['after:run:run']()).to.be.fulfilled + .then(() => { + expect(slsw.watch).to.not.have.been.called; + return null; + }); + }); + + it('should watch if enabled', () => { + slsw.options.watch = true; + return expect(slsw.hooks['after:run:run']()).to.be.fulfilled + .then(() => { + expect(slsw.watch).to.have.been.calledOnce; + expect(slsw.watch).to.have.been.calledOnce; + return null; + }); + }); + } + }, + { + name: 'webpack:validate:validate', + test: () => { + it('should call validate', () => { + return expect(slsw.hooks['webpack:validate:validate']()).to.be.fulfilled + .then(() => { + expect(slsw.validate).to.have.been.calledOnce; + return null; + }); + }); + } + }, + { + name: 'webpack:compile:compile', + test: () => { + it('should call compile', () => { + return expect(slsw.hooks['webpack:compile:compile']()).to.be.fulfilled + .then(() => { + expect(slsw.compile).to.have.been.calledOnce; + return null; + }); + }); + } + }, + { + name: 'webpack:compile:watch:compile', + test: () => { + it('should resolve', () => { + return expect(slsw.hooks['webpack:compile:watch:compile']()).to.be.fulfilled; + }); + } + }, + { + name: 'webpack:package:packExternalModules', + test: () => { + it('should call packExternalModules', () => { + return expect(slsw.hooks['webpack:package:packExternalModules']()).to.be.fulfilled + .then(() => { + expect(slsw.packExternalModules).to.have.been.calledOnce; + return null; + }); + }); + } + }, + { + name: 'webpack:package:packageModules', + test: () => { + it('should call packageModules', () => { + return expect(slsw.hooks['webpack:package:packageModules']()).to.be.fulfilled + .then(() => { + expect(slsw.packageModules).to.have.been.calledOnce; + return null; + }); + }); + } + }, + { + name: 'before:offline:start', + test: () => { + it('should prepare offline', () => { + return expect(slsw.hooks['before:offline:start']()).to.be.fulfilled + .then(() => { + expect(ServerlessWebpack.lib.webpack.isLocal).to.be.true; + expect(slsw.prepareOfflineInvoke).to.have.been.calledOnce; + expect(slsw.wpwatch).to.have.been.calledOnce; + return null; + }); + }); + } + }, + { + name: 'before:offline:start:init', + test: () => { + it('should prepare offline', () => { + return expect(slsw.hooks['before:offline:start:init']()).to.be.fulfilled + .then(() => { + expect(ServerlessWebpack.lib.webpack.isLocal).to.be.true; + expect(slsw.prepareOfflineInvoke).to.have.been.calledOnce; + expect(slsw.wpwatch).to.have.been.calledOnce; + return null; + }); + }); + } + }, + { + name: 'before:step-functions-offline:start', + test: () => { + it('should prepare offline', () => { + return expect(slsw.hooks['before:step-functions-offline:start']()).to.be.fulfilled + .then(() => { + expect(ServerlessWebpack.lib.webpack.isLocal).to.be.true; + expect(slsw.prepareStepOfflineInvoke).to.have.been.calledOnce; + expect(slsw.compile).to.have.been.calledOnce; + return null; + }); + }); + } + }, + ], hook => { + it(`should expose hook ${hook.name}`, () => { + expect(slsw).to.have.a.nested.property(`hooks.${hook.name}`); + }); + + describe(hook.name, () => { + hook.test(); + }); + }); + }); +}); diff --git a/package.json b/package.json index a59127c03..356218169 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,7 @@ }, "homepage": "https://github.com/serverless-heaven/serverless-webpack#readme", "scripts": { - "test": "nyc ./node_modules/mocha/bin/_mocha tests/all \"lib/**/*.test.js\" -R spec --recursive", + "test": "nyc ./node_modules/mocha/bin/_mocha tests/all index.test.js \"lib/**/*.test.js\" -R spec --recursive", "eslint": "node node_modules/eslint/bin/eslint.js --ext .js lib" }, "nyc": {