diff --git a/messages/report.json b/messages/report.json index e16f9c2dc..6c776f440 100644 --- a/messages/report.json +++ b/messages/report.json @@ -15,5 +15,6 @@ "jobid": "job ID of the deployment you want to check; defaults to your most recent CLI deployment if not specified", "wait": "wait time for command to finish in minutes", "verbose": "verbose output of deploy result" - } + }, + "mdapiDeployFailed": "The metadata deploy operation failed." } diff --git a/src/commands/force/source/deploy/report.ts b/src/commands/force/source/deploy/report.ts index 12c2aae58..b2f7aa8c7 100644 --- a/src/commands/force/source/deploy/report.ts +++ b/src/commands/force/source/deploy/report.ts @@ -8,13 +8,17 @@ import * as os from 'os'; import { Messages, SfdxProject } from '@salesforce/core'; import { flags, FlagsConfig } from '@salesforce/command'; -import { Duration } from '@salesforce/kit'; +import { Duration, env } from '@salesforce/kit'; +import { MetadataApiDeploy } from '@salesforce/source-deploy-retrieve'; import { DeployCommand } from '../../../../deployCommand'; import { DeployReportCommandResult, DeployReportResultFormatter, } from '../../../../formatters/deployReportResultFormatter'; import { ComponentSetBuilder } from '../../../../componentSetBuilder'; +import { ProgressFormatter } from '../../../../formatters/progressFormatter'; +import { DeployProgressBarFormatter } from '../../../../formatters/deployProgressBarFormatter'; +import { DeployProgressStatusFormatter } from '../../../../formatters/deployProgressStatusFormatter'; Messages.importMessagesDirectory(__dirname); const messages = Messages.loadMessages('@salesforce/plugin-source', 'report'); @@ -38,13 +42,21 @@ export class Report extends DeployCommand { description: messages.getMessage('flags.verbose'), }), }; - public async run(): Promise { await this.doReport(); this.resolveSuccess(); return this.formatResult(); } + /** + * This method is here to provide a workaround to stubbing a constructor in the tests. + * + * @param id + */ + public createDeploy(id?: string): MetadataApiDeploy { + return new MetadataApiDeploy({ usernameOrConnection: this.org.getUsername(), id }); + } + protected async doReport(): Promise { const deployId = this.resolveDeployId(this.getFlag('jobid')); @@ -61,6 +73,16 @@ export class Report extends DeployCommand { } this.componentSet = await ComponentSetBuilder.build({ sourcepath }); } + + const waitDuration = this.getFlag('wait'); + const deploy = this.createDeploy(deployId); + if (!this.isJsonOutput()) { + const progressFormatter: ProgressFormatter = env.getBoolean('SFDX_USE_PROGRESS_BAR', true) + ? new DeployProgressBarFormatter(this.logger, this.ux) + : new DeployProgressStatusFormatter(this.logger, this.ux); + progressFormatter.progress(deploy); + } + await deploy.pollStatus(500, waitDuration.seconds); this.deployResult = await this.report(deployId); } diff --git a/src/formatters/deployReportResultFormatter.ts b/src/formatters/deployReportResultFormatter.ts index 9efe5d95c..f5f082620 100644 --- a/src/formatters/deployReportResultFormatter.ts +++ b/src/formatters/deployReportResultFormatter.ts @@ -5,8 +5,9 @@ * For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause */ -import { MetadataApiDeployStatus } from '@salesforce/source-deploy-retrieve/lib/src/client/types'; +import { MetadataApiDeployStatus, RequestStatus } from '@salesforce/source-deploy-retrieve/lib/src/client/types'; import { getString } from '@salesforce/ts-types'; +import { SfdxError } from '@salesforce/core'; import { DeployResultFormatter } from './deployResultFormatter'; export type DeployReportCommandResult = MetadataApiDeployStatus; @@ -35,10 +36,16 @@ export class DeployReportResultFormatter extends DeployResultFormatter { } else { this.ux.log('No components deployed'); } - return; + } else { + this.displaySuccesses(); + this.displayFailures(); + this.displayTestResults(); } - this.displaySuccesses(); - this.displayFailures(); - this.displayTestResults(); + + if (status === RequestStatus.Failed) { + throw SfdxError.create('@salesforce/plugin-source', 'report', 'mdapiDeployFailed'); + } + + return; } } diff --git a/test/commands/source/report.test.ts b/test/commands/source/report.test.ts index a2ef51a17..37bdd3a67 100644 --- a/test/commands/source/report.test.ts +++ b/test/commands/source/report.test.ts @@ -12,9 +12,12 @@ import { fromStub, spyMethod, stubInterface, stubMethod } from '@salesforce/ts-s import { ConfigFile, Org, SfdxProject } from '@salesforce/core'; import { IConfig } from '@oclif/config'; import { UX } from '@salesforce/command'; +import { MetadataApiDeploy } from '@salesforce/source-deploy-retrieve'; import { Report } from '../../../src/commands/force/source/deploy/report'; import { DeployReportResultFormatter } from '../../../src/formatters/deployReportResultFormatter'; import { DeployCommandResult } from '../../../src/formatters/deployResultFormatter'; +import { DeployProgressBarFormatter } from '../../../src/formatters/deployProgressBarFormatter'; +import { DeployProgressStatusFormatter } from '../../../src/formatters/deployProgressStatusFormatter'; import { getDeployResult } from './deployResponses'; describe('force:source:report', () => { @@ -33,6 +36,7 @@ describe('force:source:report', () => { const oclifConfigStub = fromStub(stubInterface(sandbox)); let checkDeployStatusStub: sinon.SinonStub; let uxLogStub: sinon.SinonStub; + let pollStatusStub: sinon.SinonStub; class TestReport extends Report { public async runIt() { @@ -45,6 +49,11 @@ describe('force:source:report', () => { public setProject(project: SfdxProject) { this.project = project; } + + public createDeploy(): MetadataApiDeploy { + pollStatusStub = sandbox.stub(MetadataApiDeploy.prototype, 'pollStatus'); + return MetadataApiDeploy.prototype; + } } const runReportCmd = async (params: string[]) => { @@ -91,9 +100,11 @@ describe('force:source:report', () => { }); it('should display stashed deploy ID', async () => { + const progressBarStub = sandbox.stub(DeployProgressBarFormatter.prototype, 'progress').returns(); const result = await runReportCmd([]); expect(result).to.deep.equal(expectedResults); expect(uxLogStub.firstCall.args[0]).to.contain(stashedDeployId); + expect(progressBarStub.calledOnce).to.equal(true); }); it('should use the jobid flag', async () => { @@ -105,18 +116,22 @@ describe('force:source:report', () => { }); it('should display the jobid flag', async () => { + const progressBarStub = sandbox.stub(DeployProgressBarFormatter.prototype, 'progress').returns(); const result = await runReportCmd(['--jobid', expectedResults.id]); expect(result).to.deep.equal(expectedResults); expect(uxLogStub.firstCall.args[0]).to.contain(expectedResults.id); + expect(progressBarStub.calledOnce).to.equal(true); }); it('should display output with no --json', async () => { const displayStub = sandbox.stub(DeployReportResultFormatter.prototype, 'display'); + const progressBarStub = sandbox.stub(DeployProgressBarFormatter.prototype, 'progress').returns(); const getJsonStub = sandbox.stub(DeployReportResultFormatter.prototype, 'getJson'); await runReportCmd([]); expect(displayStub.calledOnce).to.equal(true); expect(getJsonStub.calledOnce).to.equal(true); expect(uxLogStub.called).to.equal(true); + expect(progressBarStub.calledOnce).to.equal(true); }); it('should NOT display output with --json', async () => { @@ -127,4 +142,13 @@ describe('force:source:report', () => { expect(getJsonStub.calledOnce).to.equal(true); expect(uxLogStub.called).to.equal(false); }); + + it('should call the correct progress method', async () => { + const progressBarStub = sandbox.stub(DeployProgressBarFormatter.prototype, 'progress').returns(); + const progressStatusStub = sandbox.stub(DeployProgressStatusFormatter.prototype, 'progress').returns(); + await runReportCmd([]); + expect(progressStatusStub.calledOnce).to.equal(false); + expect(progressBarStub.calledOnce).to.equal(true); + expect(pollStatusStub.calledOnce).to.equal(true); + }); });