diff --git a/command-snapshot.json b/command-snapshot.json index ee5b02762..24d7fad8d 100644 --- a/command-snapshot.json +++ b/command-snapshot.json @@ -59,7 +59,7 @@ { "command": "force:source:beta:status", "plugin": "@salesforce/plugin-source", - "flags": ["apiversion", "json", "local", "loglevel", "remote", "targetusername"] + "flags": ["apiversion", "concise", "json", "local", "loglevel", "remote", "targetusername"] }, { "command": "force:source:beta:tracking:clear", diff --git a/messages/status.json b/messages/status.json index b82adf1d8..8f5b5dbf0 100644 --- a/messages/status.json +++ b/messages/status.json @@ -13,7 +13,8 @@ "local": "list the changes that have been made locally", "localLong": "Lists the changes that have been made locally.", "remote": "list the changes that have been made in the scratch org", - "remoteLong": "Lists the changes that have been made in the scratch org." + "remoteLong": "Lists the changes that have been made in the scratch org.", + "concise": "show only the changes that will be pushed or pulled; omits files that are forceignored" }, "humanSuccess": "Source Status", "noResults": "No local or remote changes found." diff --git a/src/commands/force/source/beta/status.ts b/src/commands/force/source/beta/status.ts index 082733de8..b1a7a170f 100644 --- a/src/commands/force/source/beta/status.ts +++ b/src/commands/force/source/beta/status.ts @@ -36,6 +36,9 @@ export default class Status extends SfdxCommand { longDescription: messages.getMessage('flags.remoteLong'), exclusive: ['local'], }), + concise: flags.builtin({ + description: messages.getMessage('flags.concise'), + }), }; protected static requiresUsername = true; protected static requiresProject = true; @@ -71,7 +74,12 @@ export default class Status extends SfdxCommand { } protected formatResult(): StatusResult[] { - const formatter = new StatusFormatter(this.logger, this.ux, {}, this.results); + const formatter = new StatusFormatter( + this.logger, + this.ux, + { concise: this.flags.concise as boolean }, + this.results + ); if (!this.flags.json) { formatter.display(); diff --git a/src/formatters/resultFormatter.ts b/src/formatters/resultFormatter.ts index 4ebc486c1..655cfaed1 100644 --- a/src/formatters/resultFormatter.ts +++ b/src/formatters/resultFormatter.ts @@ -9,12 +9,13 @@ import * as path from 'path'; import { UX } from '@salesforce/command'; import { Logger } from '@salesforce/core'; import { FileResponse, Failures, Successes } from '@salesforce/source-deploy-retrieve'; -import { getBoolean, getNumber } from '@salesforce/ts-types'; +import { getNumber } from '@salesforce/ts-types'; export interface ResultFormatterOptions { verbose?: boolean; quiet?: boolean; waitTime?: number; + concise?: boolean; } export function toArray(entryOrArray: T | T[] | undefined): T[] { @@ -42,11 +43,15 @@ export abstract class ResultFormatter { } public isVerbose(): boolean { - return getBoolean(this.options, 'verbose', false); + return this.options.verbose ?? false; } public isQuiet(): boolean { - return getBoolean(this.options, 'quiet', false); + return this.options.quiet ?? false; + } + + public isConcise(): boolean { + return this.options.concise ?? false; } // Sort by type > filePath > fullName diff --git a/src/formatters/statusFormatter.ts b/src/formatters/statusFormatter.ts index b19735176..a988ee718 100644 --- a/src/formatters/statusFormatter.ts +++ b/src/formatters/statusFormatter.ts @@ -46,6 +46,9 @@ export class StatusFormatter extends ResultFormatter { } public display(): void { + if (this.options.concise) { + this.statusRows = this.statusRows.filter((row) => !row.ignored); + } if (this.statusRows.length === 0) { this.ux.log(messages.getMessage('noResults')); return; diff --git a/test/formatters/statusResultFormatter.test.ts b/test/formatters/statusResultFormatter.test.ts new file mode 100644 index 000000000..ff7f8648e --- /dev/null +++ b/test/formatters/statusResultFormatter.test.ts @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2020, salesforce.com, inc. + * All rights reserved. + * Licensed under the BSD 3-Clause license. + * For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause + */ +import { Logger } from '@salesforce/core'; +import { UX } from '@salesforce/command'; +import { stubInterface } from '@salesforce/ts-sinon'; +import { expect } from 'chai'; +import * as sinon from 'sinon'; +import { StatusResult, StatusFormatter } from '../../src/formatters/statusFormatter'; + +const fakeResult: StatusResult[] = [ + { + fullName: 'ignoredProfile', + type: 'Profile', + origin: 'Remote', + ignored: true, + actualState: 'Add', + state: 'Remote Add', + }, + { + fullName: 'regularProfile', + type: 'Profile', + origin: 'Remote', + ignored: false, + actualState: 'Add', + state: 'Remote Add', + }, +]; + +describe('status results', () => { + const sandbox = sinon.createSandbox(); + let ux; + const logger = Logger.childFromRoot('retrieveTestLogger').useMemoryLogging(); + let logStub: sinon.SinonStub; + let tableStub: sinon.SinonStub; + + beforeEach(() => { + logStub = sandbox.stub(); + tableStub = sandbox.stub(); + + ux = stubInterface(sandbox, { + log: logStub, + table: tableStub, + }); + }); + + afterEach(() => { + sandbox.restore(); + }); + + it('returns expected json', () => { + const formatter = new StatusFormatter(logger, ux, {}, fakeResult); + expect(formatter.getJson()).deep.equal(fakeResult); + }); + + describe('human display', () => { + it('includes ignored files without the concise option', () => { + const formatter = new StatusFormatter(logger, ux, { concise: false }, fakeResult); + formatter.display(); + expect(tableStub.callCount).to.equal(1); + expect(tableStub.firstCall.args[0]).to.have.equal(fakeResult); + }); + it('omits ignored files with the concise option', () => { + const formatter = new StatusFormatter(logger, ux, { concise: true }, fakeResult); + formatter.display(); + expect(tableStub.callCount).to.equal(1); + expect(tableStub.firstCall.args[0]).to.deep.equal([fakeResult[1]]); + }); + it('shows no results when there are none', () => { + const formatter = new StatusFormatter(logger, ux, { concise: false }, []); + formatter.display(); + expect(logStub.callCount).to.equal(1); + expect(logStub.firstCall.args[0]).to.contain('No local or remote changes found.'); + }); + it('shows no results when there are none because concise omitted them', () => { + const formatter = new StatusFormatter(logger, ux, { concise: true }, [fakeResult[0]]); + formatter.display(); + expect(logStub.callCount).to.equal(1); + expect(logStub.firstCall.args[0]).to.contain('No local or remote changes found.'); + }); + }); +});