From 4fe607a5ab864dad30b65771a1890cbb74b7da45 Mon Sep 17 00:00:00 2001 From: Graham Campbell Date: Mon, 8 Jun 2026 23:53:49 +0100 Subject: [PATCH] Clean up framework component tests --- test/unit/components/framework/index.test.js | 548 ++++++------------- 1 file changed, 170 insertions(+), 378 deletions(-) diff --git a/test/unit/components/framework/index.test.js b/test/unit/components/framework/index.test.js index 5bf051d..f905004 100644 --- a/test/unit/components/framework/index.test.js +++ b/test/unit/components/framework/index.test.js @@ -14,23 +14,50 @@ const { outputFile, remove } = require('../../../lib/fs'); const expect = chai.expect; -const createSpawnExecution = ({ code = 0, stdout = '', stderr = '' } = {}) => { +const INFO_OUTPUT = 'region: us-east-1\n\nStack Outputs:\n Key: Output'; + +const loadFrameworkComponent = (spawnStub) => + proxyquire('../../../../components/framework/index.js', { + '../../src/utils/spawn': spawnStub, + }); + +const createOutputStream = (output) => ({ + on: (event, callback) => { + if (event === 'data' && output) callback(Buffer.from(output)); + }, +}); + +const createClassicSpawnResult = ({ code = 0, stdout, stderr, closeOnNextTick = false } = {}) => { const child = { - stdout: { - on: (event, callback) => { - if (event === 'data' && stdout) callback(Buffer.from(stdout)); - }, - }, - stderr: { - on: (event, callback) => { - if (event === 'data' && stderr) callback(Buffer.from(stderr)); - }, - }, on: (event, callback) => { - if (event === 'close') process.nextTick(() => callback(code)); + if (event !== 'close') return; + if (closeOnNextTick) process.nextTick(() => callback(code)); + else callback(code); }, kill: sinon.stub(), }; + + if (stdout !== undefined) child.stdout = createOutputStream(stdout); + if (stderr !== undefined) child.stderr = createOutputStream(stderr); + + return child; +}; + +const createSpawnStub = (...spawnResults) => { + const spawnStub = sinon.stub(); + + if (spawnResults.length === 0) return spawnStub.returns(createClassicSpawnResult()); + if (spawnResults.length === 1) return spawnStub.returns(spawnResults[0]); + + for (const [index, spawnResult] of spawnResults.entries()) { + spawnStub.onCall(index).returns(spawnResult); + } + + return spawnStub; +}; + +const createSpawnExecution = ({ code = 0, stdout = '', stderr = '' } = {}) => { + const child = createClassicSpawnResult({ code, stdout, stderr, closeOnNextTick: true }); const execution = Promise.resolve({ child, stdoutBuffer: Buffer.from(stdout), @@ -48,6 +75,16 @@ const createSpawnExecution = ({ code = 0, stdout = '', stderr = '' } = {}) => { return execution; }; +const expectSpawnCall = (spawnStub, index, expectedArgs, expectedOptions = {}) => { + const [command, args, options] = spawnStub.getCall(index).args; + + expect(command).to.equal('serverless'); + expect(args).to.deep.equal(expectedArgs); + expect(options).to.include(expectedOptions); + + return options; +}; + /** * @returns {Promise} */ @@ -67,21 +104,8 @@ const getContext = async () => { describe('test/unit/components/framework/index.test.js', () => { it('correctly handles deploy', async () => { - const spawnStub = sinon.stub().returns({ - on: (arg, cb) => { - if (arg === 'close') cb(0); - }, - stdout: { - on: (arg, cb) => { - const data = 'region: us-east-1\n\nStack Outputs:\n Key: Output'; - if (arg === 'data') cb(data); - }, - }, - kill: () => {}, - }); - const FrameworkComponent = proxyquire('../../../../components/framework/index.js', { - '../../src/utils/spawn': spawnStub, - }); + const spawnStub = createSpawnStub(createClassicSpawnResult({ stdout: INFO_OUTPUT })); + const FrameworkComponent = loadFrameworkComponent(spawnStub); const context = await getContext(); const component = new FrameworkComponent('some-id', context, { path: 'path' }); @@ -89,31 +113,22 @@ describe('test/unit/components/framework/index.test.js', () => { await component.deploy(); expect(spawnStub).to.be.calledTwice; - expect(spawnStub.getCall(0).args[0]).to.equal('serverless'); - expect(spawnStub.getCall(0).args[1]).to.deep.equal(['deploy', '--stage', 'dev']); - expect(spawnStub.getCall(0).args[2].cwd).to.equal('path'); - expect(spawnStub.getCall(1).args[0]).to.equal('serverless'); - expect(spawnStub.getCall(1).args[1]).to.deep.equal(['info', '--verbose', '--stage', 'dev']); - expect(spawnStub.getCall(1).args[2].cwd).to.equal('path'); + expectSpawnCall(spawnStub, 0, ['deploy', '--stage', 'dev'], { cwd: 'path' }); + expectSpawnCall(spawnStub, 1, ['info', '--verbose', '--stage', 'dev'], { cwd: 'path' }); expect(context.state).to.deep.equal({ detectedFrameworkVersion: '9.9.9' }); expect(context.outputs).to.deep.equal({ Key: 'Output' }); }); it('supports the shared spawn helper promise shape when executing osls commands', async () => { - const spawnStub = sinon.stub(); - spawnStub.onFirstCall().returns( + const spawnStub = createSpawnStub( createSpawnExecution({ - stdout: 'region: us-east-1\n\nStack Outputs:\n Key: Output', - }) - ); - spawnStub.onSecondCall().returns( + stdout: INFO_OUTPUT, + }), createSpawnExecution({ - stdout: 'region: us-east-1\n\nStack Outputs:\n Key: Output', + stdout: INFO_OUTPUT, }) ); - const FrameworkComponent = proxyquire('../../../../components/framework/index.js', { - '../../src/utils/spawn': spawnStub, - }); + const FrameworkComponent = loadFrameworkComponent(spawnStub); const context = await getContext(); const component = new FrameworkComponent('some-id', context, { path: 'path' }); @@ -125,21 +140,8 @@ describe('test/unit/components/framework/index.test.js', () => { }); it('correctly handles package', async () => { - const spawnStub = sinon.stub().returns({ - on: (arg, cb) => { - if (arg === 'close') cb(0); - }, - stdout: { - on: (arg, cb) => { - const data = 'region: us-east-1\n\nStack Outputs:\n Key: Output'; - if (arg === 'data') cb(data); - }, - }, - kill: () => {}, - }); - const FrameworkComponent = proxyquire('../../../../components/framework/index.js', { - '../../src/utils/spawn': spawnStub, - }); + const spawnStub = createSpawnStub(createClassicSpawnResult({ stdout: INFO_OUTPUT })); + const FrameworkComponent = loadFrameworkComponent(spawnStub); const context = await getContext(); const component = new FrameworkComponent('some-id', context, { path: 'path' }); @@ -147,29 +149,14 @@ describe('test/unit/components/framework/index.test.js', () => { await component.package(); expect(spawnStub).to.be.calledOnce; - expect(spawnStub.getCall(0).args[0]).to.equal('serverless'); - expect(spawnStub.getCall(0).args[1]).to.deep.equal(['package', '--stage', 'dev']); - expect(spawnStub.getCall(0).args[2].cwd).to.equal('path'); + expectSpawnCall(spawnStub, 0, ['package', '--stage', 'dev'], { cwd: 'path' }); expect(context.state).to.deep.equal({ detectedFrameworkVersion: '9.9.9' }); expect(context.outputs).to.deep.equal({ Key: 'Output' }); }); it('correctly handles refresh-outputs', async () => { - const spawnStub = sinon.stub().returns({ - on: (arg, cb) => { - if (arg === 'close') cb(0); - }, - stdout: { - on: (arg, cb) => { - const data = 'region: us-east-1\n\nStack Outputs:\n Key: Output'; - if (arg === 'data') cb(data); - }, - }, - kill: () => {}, - }); - const FrameworkComponent = proxyquire('../../../../components/framework/index.js', { - '../../src/utils/spawn': spawnStub, - }); + const spawnStub = createSpawnStub(createClassicSpawnResult({ stdout: INFO_OUTPUT })); + const FrameworkComponent = loadFrameworkComponent(spawnStub); const context = await getContext(); const component = new FrameworkComponent('some-id', context, { path: 'path' }); @@ -177,29 +164,14 @@ describe('test/unit/components/framework/index.test.js', () => { await component.refreshOutputs(); expect(spawnStub).to.be.calledOnce; - expect(spawnStub.getCall(0).args[0]).to.equal('serverless'); - expect(spawnStub.getCall(0).args[1]).to.deep.equal(['info', '--verbose', '--stage', 'dev']); - expect(spawnStub.getCall(0).args[2].cwd).to.equal('path'); + expectSpawnCall(spawnStub, 0, ['info', '--verbose', '--stage', 'dev'], { cwd: 'path' }); expect(context.state).to.deep.equal({ detectedFrameworkVersion: '9.9.9' }); expect(context.outputs).to.deep.equal({ Key: 'Output' }); }); it('correctly recognizes region in inputs', async () => { - const spawnStub = sinon.stub().returns({ - on: (arg, cb) => { - if (arg === 'close') cb(0); - }, - stdout: { - on: (arg, cb) => { - const data = 'region: us-east-1\n\nStack Outputs:\n Key: Output'; - if (arg === 'data') cb(data); - }, - }, - kill: () => {}, - }); - const FrameworkComponent = proxyquire('../../../../components/framework/index.js', { - '../../src/utils/spawn': spawnStub, - }); + const spawnStub = createSpawnStub(createClassicSpawnResult({ stdout: INFO_OUTPUT })); + const FrameworkComponent = loadFrameworkComponent(spawnStub); const context = await getContext(); const component = new FrameworkComponent('some-id', context, { @@ -210,36 +182,19 @@ describe('test/unit/components/framework/index.test.js', () => { await component.refreshOutputs(); expect(spawnStub).to.be.calledOnce; - expect(spawnStub.getCall(0).args[0]).to.equal('serverless'); - expect(spawnStub.getCall(0).args[1]).to.deep.equal([ - 'info', - '--verbose', - '--stage', - 'dev', - '--region', - 'eu-central-1', - ]); - expect(spawnStub.getCall(0).args[2].cwd).to.equal('path'); + expectSpawnCall( + spawnStub, + 0, + ['info', '--verbose', '--stage', 'dev', '--region', 'eu-central-1'], + { cwd: 'path' } + ); expect(context.state).to.deep.equal({ detectedFrameworkVersion: '9.9.9' }); expect(context.outputs).to.deep.equal({ Key: 'Output' }); }); it('correctly recognizes config in inputs', async () => { - const spawnStub = sinon.stub().returns({ - on: (arg, cb) => { - if (arg === 'close') cb(0); - }, - stdout: { - on: (arg, cb) => { - const data = 'region: us-east-1\n\nStack Outputs:\n Key: Output'; - if (arg === 'data') cb(data); - }, - }, - kill: () => {}, - }); - const FrameworkComponent = proxyquire('../../../../components/framework/index.js', { - '../../src/utils/spawn': spawnStub, - }); + const spawnStub = createSpawnStub(createClassicSpawnResult({ stdout: INFO_OUTPUT })); + const FrameworkComponent = loadFrameworkComponent(spawnStub); const context = await getContext(); const component = new FrameworkComponent('some-id', context, { @@ -250,36 +205,19 @@ describe('test/unit/components/framework/index.test.js', () => { await component.refreshOutputs(); expect(spawnStub).to.be.calledOnce; - expect(spawnStub.getCall(0).args[0]).to.equal('serverless'); - expect(spawnStub.getCall(0).args[1]).to.deep.equal([ - 'info', - '--verbose', - '--stage', - 'dev', - '--config', - 'different.yml', - ]); - expect(spawnStub.getCall(0).args[2].cwd).to.equal('path'); + expectSpawnCall( + spawnStub, + 0, + ['info', '--verbose', '--stage', 'dev', '--config', 'different.yml'], + { cwd: 'path' } + ); expect(context.state).to.deep.equal({ detectedFrameworkVersion: '9.9.9' }); expect(context.outputs).to.deep.equal({ Key: 'Output' }); }); it('correctly set compose-specific specific env vars', async () => { - const spawnStub = sinon.stub().returns({ - on: (arg, cb) => { - if (arg === 'close') cb(0); - }, - stdout: { - on: (arg, cb) => { - const data = 'region: us-east-1\n\nStack Outputs:\n Key: Output'; - if (arg === 'data') cb(data); - }, - }, - kill: () => {}, - }); - const FrameworkComponent = proxyquire('../../../../components/framework/index.js', { - '../../src/utils/spawn': spawnStub, - }); + const spawnStub = createSpawnStub(createClassicSpawnResult({ stdout: INFO_OUTPUT })); + const FrameworkComponent = loadFrameworkComponent(spawnStub); const context = await getContext(); const component = new FrameworkComponent('some-id', context, { path: 'path' }); @@ -299,21 +237,8 @@ describe('test/unit/components/framework/index.test.js', () => { process.env.SLS_LOG_LEVEL = 'info'; process.env.SLS_LOG_DEBUG = 'aws'; - const spawnStub = sinon.stub().returns({ - on: (arg, cb) => { - if (arg === 'close') cb(0); - }, - stdout: { - on: (arg, cb) => { - const data = 'region: us-east-1\n\nStack Outputs:\n Key: Output'; - if (arg === 'data') cb(data); - }, - }, - kill: () => {}, - }); - const FrameworkComponent = proxyquire('../../../../components/framework/index.js', { - '../../src/utils/spawn': spawnStub, - }); + const spawnStub = createSpawnStub(createClassicSpawnResult({ stdout: INFO_OUTPUT })); + const FrameworkComponent = loadFrameworkComponent(spawnStub); const context = await getContext(); const component = new FrameworkComponent('some-id', context, { path: 'path' }); @@ -332,29 +257,17 @@ describe('test/unit/components/framework/index.test.js', () => { }); it('correctly handles refresh-outputs with malformed info outputs', async () => { - const spawnStub = sinon.stub().returns({ - on: (arg, cb) => { - if (arg === 'close') cb(0); - }, - stdout: { - on: (arg, cb) => { - // Simulate the output we get with Serverless Domain Manager - // https://github.com/serverless/compose/issues/105 - const data = - 'region: us-east-1\n\n' + - 'Stack Outputs:\n' + - ' Key: Output\n' + - 'Serverless Domain Manager:\n' + - ' Domain Name: example.com\n' + - ' ------------------------'; - if (arg === 'data') cb(data); - }, - }, - kill: () => {}, - }); - const FrameworkComponent = proxyquire('../../../../components/framework/index.js', { - '../../src/utils/spawn': spawnStub, - }); + // Simulate the output we get with Serverless Domain Manager + // https://github.com/serverless/compose/issues/105 + const infoOutput = + 'region: us-east-1\n\n' + + 'Stack Outputs:\n' + + ' Key: Output\n' + + 'Serverless Domain Manager:\n' + + ' Domain Name: example.com\n' + + ' ------------------------'; + const spawnStub = createSpawnStub(createClassicSpawnResult({ stdout: infoOutput })); + const FrameworkComponent = loadFrameworkComponent(spawnStub); const context = await getContext(); const component = new FrameworkComponent('some-id', context, { path: 'path' }); @@ -362,24 +275,15 @@ describe('test/unit/components/framework/index.test.js', () => { await component.refreshOutputs(); expect(spawnStub).to.be.calledOnce; - expect(spawnStub.getCall(0).args[0]).to.equal('serverless'); - expect(spawnStub.getCall(0).args[1]).to.deep.equal(['info', '--verbose', '--stage', 'dev']); - expect(spawnStub.getCall(0).args[2].cwd).to.equal('path'); + expectSpawnCall(spawnStub, 0, ['info', '--verbose', '--stage', 'dev'], { cwd: 'path' }); expect(context.state).to.deep.equal({ detectedFrameworkVersion: '9.9.9' }); expect(context.outputs).to.deep.equal({ Key: 'Output' }); }); it('correctly handles remove', async () => { - const spawnStub = sinon.stub().returns({ - on: (arg, cb) => { - if (arg === 'close') cb(0); - }, - kill: () => {}, - }); + const spawnStub = createSpawnStub(); - const FrameworkComponent = proxyquire('../../../../components/framework/index.js', { - '../../src/utils/spawn': spawnStub, - }); + const FrameworkComponent = loadFrameworkComponent(spawnStub); const context = await getContext(); const component = new FrameworkComponent('some-id', context, { path: 'path' }); @@ -394,24 +298,15 @@ describe('test/unit/components/framework/index.test.js', () => { await component.remove(); expect(spawnStub).to.be.calledOnce; - expect(spawnStub.getCall(0).args[0]).to.equal('serverless'); - expect(spawnStub.getCall(0).args[1]).to.deep.equal(['remove', '--stage', 'dev']); - expect(spawnStub.getCall(0).args[2].cwd).to.equal('path'); + expectSpawnCall(spawnStub, 0, ['remove', '--stage', 'dev'], { cwd: 'path' }); expect(context.state).to.deep.equal({}); expect(context.outputs).to.deep.equal({}); }); it('correctly handles command', async () => { - const spawnStub = sinon.stub().returns({ - on: (arg, cb) => { - if (arg === 'close') cb(0); - }, - kill: () => {}, - }); + const spawnStub = createSpawnStub(); - const FrameworkComponent = proxyquire('../../../../components/framework/index.js', { - '../../src/utils/spawn': spawnStub, - }); + const FrameworkComponent = loadFrameworkComponent(spawnStub); const context = await getContext(); const component = new FrameworkComponent('some-id', context, { path: 'custom-path' }); @@ -420,17 +315,12 @@ describe('test/unit/components/framework/index.test.js', () => { await component.command('print', { key: 'val', flag: true, o: 'shortoption' }); expect(spawnStub).to.be.calledOnce; - expect(spawnStub.getCall(0).args[0]).to.equal('serverless'); - expect(spawnStub.getCall(0).args[1]).to.deep.equal([ - 'print', - '--key=val', - '--flag', - '-o', - 'shortoption', - '--stage', - 'dev', - ]); - expect(spawnStub.getCall(0).args[2].cwd).to.equal('custom-path'); + expectSpawnCall( + spawnStub, + 0, + ['print', '--key=val', '--flag', '-o', 'shortoption', '--stage', 'dev'], + { cwd: 'custom-path' } + ); }); it('passes documented nested osls commands through to the osls CLI', async () => { @@ -496,15 +386,8 @@ describe('test/unit/components/framework/index.test.js', () => { ]; for (const testCase of cases) { - const spawnStub = sinon.stub().returns({ - on: (arg, cb) => { - if (arg === 'close') cb(0); - }, - kill: () => {}, - }); - const FrameworkComponent = proxyquire('../../../../components/framework/index.js', { - '../../src/utils/spawn': spawnStub, - }); + const spawnStub = createSpawnStub(); + const FrameworkComponent = loadFrameworkComponent(spawnStub); const context = await getContext(); const component = new FrameworkComponent('some-id', context, { path: 'path' }); @@ -513,24 +396,14 @@ describe('test/unit/components/framework/index.test.js', () => { await component.command(testCase.command, testCase.options); expect(spawnStub).to.be.calledOnce; - expect(spawnStub.getCall(0).args[0]).to.equal('serverless'); - expect(spawnStub.getCall(0).args[1]).to.deep.equal(testCase.expectedArgs); - expect(spawnStub.getCall(0).args[2].cwd).to.equal('path'); - expect(spawnStub.getCall(0).args[2].stdio).to.equal('inherit'); + expectSpawnCall(spawnStub, 0, testCase.expectedArgs, { cwd: 'path', stdio: 'inherit' }); } }); it('preserves repeated passthrough options', async () => { - const spawnStub = sinon.stub().returns({ - on: (arg, cb) => { - if (arg === 'close') cb(0); - }, - kill: () => {}, - }); + const spawnStub = createSpawnStub(); - const FrameworkComponent = proxyquire('../../../../components/framework/index.js', { - '../../src/utils/spawn': spawnStub, - }); + const FrameworkComponent = loadFrameworkComponent(spawnStub); const context = await getContext(); const component = new FrameworkComponent('some-id', context, { path: 'path' }); @@ -543,19 +416,24 @@ describe('test/unit/components/framework/index.test.js', () => { }); expect(spawnStub).to.be.calledOnce; - expect(spawnStub.getCall(0).args[1]).to.deep.equal([ - 'invoke', - 'local', - '--function=handler', - '--env=VAR1=value1', - '--env=VAR2=value2', - '-e', - 'SHORT1=value1', - '-e', - 'SHORT2=value2', - '--stage', - 'dev', - ]); + expectSpawnCall( + spawnStub, + 0, + [ + 'invoke', + 'local', + '--function=handler', + '--env=VAR1=value1', + '--env=VAR2=value2', + '-e', + 'SHORT1=value1', + '-e', + 'SHORT2=value2', + '--stage', + 'dev', + ], + { cwd: 'path', stdio: 'inherit' } + ); }); it('shows command-specific progress for selected passthrough commands', async () => { @@ -605,15 +483,8 @@ describe('test/unit/components/framework/index.test.js', () => { ]; for (const testCase of cases) { - const spawnStub = sinon.stub().returns({ - on: (arg, cb) => { - if (arg === 'close') cb(0); - }, - kill: () => {}, - }); - const FrameworkComponent = proxyquire('../../../../components/framework/index.js', { - '../../src/utils/spawn': spawnStub, - }); + const spawnStub = createSpawnStub(); + const FrameworkComponent = loadFrameworkComponent(spawnStub); const context = await getContext(); sinon.spy(context, 'startProgress'); @@ -631,16 +502,9 @@ describe('test/unit/components/framework/index.test.js', () => { }); it('does not show special progress for unknown passthrough commands', async () => { - const spawnStub = sinon.stub().returns({ - on: (arg, cb) => { - if (arg === 'close') cb(0); - }, - kill: () => {}, - }); + const spawnStub = createSpawnStub(); - const FrameworkComponent = proxyquire('../../../../components/framework/index.js', { - '../../src/utils/spawn': spawnStub, - }); + const FrameworkComponent = loadFrameworkComponent(spawnStub); const context = await getContext(); sinon.spy(context, 'startProgress'); @@ -653,21 +517,13 @@ describe('test/unit/components/framework/index.test.js', () => { expect(context.startProgress.called).to.equal(false); expect(context.successProgress.called).to.equal(false); - expect(spawnStub.getCall(0).args[1]).to.deep.equal(['print', '--stage', 'dev']); - expect(spawnStub.getCall(0).args[2].stdio).to.equal('inherit'); + expectSpawnCall(spawnStub, 0, ['print', '--stage', 'dev'], { stdio: 'inherit' }); }); it('correctly ignores `stage` from options to not duplicate it when executing command', async () => { - const spawnStub = sinon.stub().returns({ - on: (arg, cb) => { - if (arg === 'close') cb(0); - }, - kill: () => {}, - }); + const spawnStub = createSpawnStub(); - const FrameworkComponent = proxyquire('../../../../components/framework/index.js', { - '../../src/utils/spawn': spawnStub, - }); + const FrameworkComponent = loadFrameworkComponent(spawnStub); const context = await getContext(); const component = new FrameworkComponent('some-id', context, { path: 'custom-path' }); @@ -676,13 +532,10 @@ describe('test/unit/components/framework/index.test.js', () => { await component.command('print', { key: 'val', flag: true, stage: 'dev' }); expect(spawnStub).to.be.calledOnce; - expect(spawnStub.getCall(0).args[1]).to.deep.equal([ - 'print', - '--key=val', - '--flag', - '--stage', - 'dev', - ]); + expectSpawnCall(spawnStub, 0, ['print', '--key=val', '--flag', '--stage', 'dev'], { + cwd: 'custom-path', + stdio: 'inherit', + }); }); it('reports detected unsupported framework version', async () => { @@ -690,9 +543,7 @@ describe('test/unit/components/framework/index.test.js', () => { stdoutBuffer: Buffer.from('Framework Core: 2.1.0'), }); - const FrameworkComponent = proxyquire('../../../../components/framework/index.js', { - '../../src/utils/spawn': spawnExtStub, - }); + const FrameworkComponent = loadFrameworkComponent(spawnExtStub); const context = await getContext(); const component = new FrameworkComponent('some-id', context, { path: 'foo' }); @@ -702,22 +553,10 @@ describe('test/unit/components/framework/index.test.js', () => { }); it('correctly handles logs for component with functions', async () => { - const spawnStub = sinon.stub().returns({ - on: (arg, cb) => { - if (arg === 'close') cb(0); - }, - stdout: { - on: (arg, cb) => { - const data = - 'functions:\n hello:\n handler: handler.hello\n other:\n handler: handler.other'; - if (arg === 'data') cb(data); - }, - }, - kill: () => {}, - }); - const FrameworkComponent = proxyquire('../../../../components/framework/index.js', { - '../../src/utils/spawn': spawnStub, - }); + const functionsOutput = + 'functions:\n hello:\n handler: handler.hello\n other:\n handler: handler.other'; + const spawnStub = createSpawnStub(createClassicSpawnResult({ stdout: functionsOutput })); + const FrameworkComponent = loadFrameworkComponent(spawnStub); const context = await getContext(); const component = new FrameworkComponent('some-id', context, { path: 'path' }); @@ -725,42 +564,20 @@ describe('test/unit/components/framework/index.test.js', () => { await component.logs({}); expect(spawnStub).to.be.calledThrice; - expect(spawnStub.getCall(0).args[0]).to.equal('serverless'); - expect(spawnStub.getCall(0).args[1]).to.deep.equal(['print', '--stage', 'dev']); - expect(spawnStub.getCall(1).args[0]).to.equal('serverless'); - expect(spawnStub.getCall(1).args[1]).to.deep.equal([ - 'logs', - '--function', - 'hello', - '--stage', - 'dev', - ]); - expect(spawnStub.getCall(2).args[0]).to.equal('serverless'); - expect(spawnStub.getCall(2).args[1]).to.deep.equal([ - 'logs', - '--function', - 'other', - '--stage', - 'dev', - ]); + expectSpawnCall(spawnStub, 0, ['print', '--stage', 'dev'], { cwd: 'path' }); + expectSpawnCall(spawnStub, 1, ['logs', '--function', 'hello', '--stage', 'dev'], { + cwd: 'path', + }); + expectSpawnCall(spawnStub, 2, ['logs', '--function', 'other', '--stage', 'dev'], { + cwd: 'path', + }); }); it('correctly handles logs for component without functions', async () => { - const spawnStub = sinon.stub().returns({ - on: (arg, cb) => { - if (arg === 'close') cb(0); - }, - stdout: { - on: (arg, cb) => { - const data = 'provider:\n name: aws'; - if (arg === 'data') cb(data); - }, - }, - kill: () => {}, - }); - const FrameworkComponent = proxyquire('../../../../components/framework/index.js', { - '../../src/utils/spawn': spawnStub, - }); + const spawnStub = createSpawnStub( + createClassicSpawnResult({ stdout: 'provider:\n name: aws' }) + ); + const FrameworkComponent = loadFrameworkComponent(spawnStub); const context = await getContext(); const component = new FrameworkComponent('some-id', context, { path: 'path' }); @@ -768,26 +585,13 @@ describe('test/unit/components/framework/index.test.js', () => { await component.logs({}); expect(spawnStub).to.be.calledOnce; - expect(spawnStub.getCall(0).args[0]).to.equal('serverless'); - expect(spawnStub.getCall(0).args[1]).to.deep.equal(['print', '--stage', 'dev']); + expectSpawnCall(spawnStub, 0, ['print', '--stage', 'dev'], { cwd: 'path' }); }); it('correctly handles tail option for logs', async () => { - const spawnStub = sinon.stub().returns({ - on: (arg, cb) => { - if (arg === 'close') cb(0); - }, - stdout: { - on: (arg, cb) => { - const data = 'functions:\n hello:\n handler: handler.hello'; - if (arg === 'data') cb(data); - }, - }, - kill: () => {}, - }); - const FrameworkComponent = proxyquire('../../../../components/framework/index.js', { - '../../src/utils/spawn': spawnStub, - }); + const functionsOutput = 'functions:\n hello:\n handler: handler.hello'; + const spawnStub = createSpawnStub(createClassicSpawnResult({ stdout: functionsOutput })); + const FrameworkComponent = loadFrameworkComponent(spawnStub); const context = await getContext(); const component = new FrameworkComponent('some-id', context, { path: 'path' }); @@ -795,17 +599,10 @@ describe('test/unit/components/framework/index.test.js', () => { await component.logs({ tail: true }); expect(spawnStub).to.be.calledTwice; - expect(spawnStub.getCall(0).args[0]).to.equal('serverless'); - expect(spawnStub.getCall(0).args[1]).to.deep.equal(['print', '--stage', 'dev']); - expect(spawnStub.getCall(1).args[0]).to.equal('serverless'); - expect(spawnStub.getCall(1).args[1]).to.deep.equal([ - 'logs', - '--function', - 'hello', - '--tail', - '--stage', - 'dev', - ]); + expectSpawnCall(spawnStub, 0, ['print', '--stage', 'dev'], { cwd: 'path' }); + expectSpawnCall(spawnStub, 1, ['logs', '--function', 'hello', '--tail', '--stage', 'dev'], { + cwd: 'path', + }); }); it('rejects invalid inputs', () => { @@ -839,9 +636,7 @@ describe('test/unit/components/framework/index.test.js', () => { await outputFile(path.join(serviceDir, 'handler.js'), 'module.exports = 1;\n'); const spawnStub = sinon.stub(); - const FrameworkComponent = proxyquire('../../../../components/framework/index.js', { - '../../src/utils/spawn': spawnStub, - }); + const FrameworkComponent = loadFrameworkComponent(spawnStub); const context = await getContext(); const inputs = { @@ -868,17 +663,14 @@ describe('test/unit/components/framework/index.test.js', () => { const filePath = path.join(serviceDir, 'handler.js'); await outputFile(filePath, 'module.exports = 1;\n'); - const spawnStub = sinon.stub(); - spawnStub.onFirstCall().returns(createSpawnExecution({ stderr: 'deployed' })); - spawnStub.onSecondCall().returns( + const spawnStub = createSpawnStub( + createSpawnExecution({ stderr: 'deployed' }), createSpawnExecution({ - stdout: 'region: us-east-1\n\nStack Outputs:\n Key: Output', + stdout: INFO_OUTPUT, }) ); - const FrameworkComponent = proxyquire('../../../../components/framework/index.js', { - '../../src/utils/spawn': spawnStub, - }); + const FrameworkComponent = loadFrameworkComponent(spawnStub); const context = await getContext(); const inputs = {