diff --git a/package.json b/package.json index 7174bbf6..6d95e958 100644 --- a/package.json +++ b/package.json @@ -125,7 +125,7 @@ "test:nuts:retrieve": "nyc mocha \"test/nuts/retrieve/*.nut.ts\" --slow 4500 --timeout 1200000 --parallel --retries 0 --jobs 20", "test:nuts:specialTypes": "nyc mocha \"test/nuts/specialTypes/*.nut.ts\" --slow 4500 --timeout 1200000 --parallel --retries 0 --jobs 20", "test:nuts:specialTypes:translations": "mocha \"test/nuts/specialTypes/translation.nut.ts\" --slow 4500 --timeout 1200000 --retries 0 --jobs 20", - "test:nuts:static": "nyc mocha \"test/commands/**/*.nut.ts\" \"test/nuts/*.nut.ts\" --slow 4500 --timeout 1200000 --parallel --retries 0 --jobs 20", + "test:nuts:static": "nyc mocha \"test/nuts/metadata/**/*.nut.ts\" \"test/nuts/*.nut.ts\" --slow 4500 --timeout 1200000 --parallel --retries 0 --jobs 20", "test:nuts:tracking": "nyc mocha \"test/nuts/tracking/*.nut.ts\" --slow 4500 --timeout 1200000 --parallel --retries 0 --jobs 20", "test:only": "wireit", "version": "oclif readme" diff --git a/src/formatters/deployResultFormatter.ts b/src/formatters/deployResultFormatter.ts index 8533cb2f..4690c720 100644 --- a/src/formatters/deployResultFormatter.ts +++ b/src/formatters/deployResultFormatter.ts @@ -25,6 +25,7 @@ import { JUnitReporter, TestResult, } from '@salesforce/apex-node'; +import { getState } from '@salesforce/source-deploy-retrieve/lib/src/client/deployMessages.js'; import { DeployResultJson, isSdrFailure, @@ -51,10 +52,10 @@ import { import { TestResultsFormatter } from '../formatters/testResultsFormatter.js'; export class DeployResultFormatter extends TestResultsFormatter implements Formatter { - private relativeFiles: FileResponse[]; - private absoluteFiles: FileResponse[]; - private coverageOptions: CoverageReporterOptions; - private resultsDir: string; + private readonly relativeFiles: FileResponse[]; + private readonly absoluteFiles: FileResponse[]; + private readonly coverageOptions: CoverageReporterOptions; + private readonly resultsDir: string; private readonly junit: boolean | undefined; public constructor( @@ -285,6 +286,26 @@ export class DeployResultFormatter extends TestResultsFormatter implements Forma private displaySuccesses(): void { const successes = this.relativeFiles.filter(isSdrSuccess); + ensureArray(this.result.response.details.componentSuccesses) + .filter( + (fromServer) => + // removes package.xml, other manifests + fromServer.componentType !== '' && + // if we don't find the file in the response, it's because it doesn't exist locally, yet + !successes.find( + (fromLocal) => fromServer.fullName === fromLocal.fullName && fromServer.componentType === fromLocal.type + ) + ) + .map((s) => + successes.push({ + fullName: s.fullName, + // @ts-expect-error getState can return 'failed' which isn't applicable to FileSuccess + state: getState(s), + type: s.componentType ?? '', + filePath: 'Not found in project', + }) + ); + if (!successes.length || this.result.response.status === RequestStatus.Failed) return; const columns = { diff --git a/test/commands/deploy/metadata/cancel.nut.ts b/test/nuts/metadata/cancel.nut.ts similarity index 97% rename from test/commands/deploy/metadata/cancel.nut.ts rename to test/nuts/metadata/cancel.nut.ts index 3f7b497f..4eef0f40 100644 --- a/test/commands/deploy/metadata/cancel.nut.ts +++ b/test/nuts/metadata/cancel.nut.ts @@ -11,8 +11,8 @@ import { strict as assert } from 'node:assert'; import { TestSession, execCmd } from '@salesforce/cli-plugins-testkit'; import { expect } from 'chai'; import { RequestStatus } from '@salesforce/source-deploy-retrieve'; -import { DeployResultJson } from '../../../../src/utils/types.js'; -import { CachedOptions } from '../../../../src/utils/deploy.js'; +import { DeployResultJson } from '../../../src/utils/types.js'; +import { CachedOptions } from '../../../src/utils/deploy.js'; function readDeployCache(sessionDir: string): Record { const contents = fs.readFileSync(path.join(sessionDir, '.sf', 'deploy-cache.json'), 'utf-8'); diff --git a/test/commands/deploy/metadata.nut.ts b/test/nuts/metadata/metadata.nut.ts similarity index 100% rename from test/commands/deploy/metadata.nut.ts rename to test/nuts/metadata/metadata.nut.ts diff --git a/test/commands/deploy/metadata/quick.nut.ts b/test/nuts/metadata/quick.nut.ts similarity index 99% rename from test/commands/deploy/metadata/quick.nut.ts rename to test/nuts/metadata/quick.nut.ts index aa748f04..caed2bf7 100644 --- a/test/commands/deploy/metadata/quick.nut.ts +++ b/test/nuts/metadata/quick.nut.ts @@ -11,7 +11,7 @@ import path from 'node:path'; import { SourceTestkit } from '@salesforce/source-testkit'; import { assert, config } from 'chai'; import { execCmd } from '@salesforce/cli-plugins-testkit'; -import { DeployResultJson } from '../../../../src/utils/types.js'; +import { DeployResultJson } from '../../../src/utils/types.js'; config.truncateThreshold = 0; diff --git a/test/commands/deploy/metadata/report-mdapi.nut.ts b/test/nuts/metadata/report-mdapi.nut.ts similarity index 98% rename from test/commands/deploy/metadata/report-mdapi.nut.ts rename to test/nuts/metadata/report-mdapi.nut.ts index d838828b..5c9bef5b 100644 --- a/test/commands/deploy/metadata/report-mdapi.nut.ts +++ b/test/nuts/metadata/report-mdapi.nut.ts @@ -11,7 +11,7 @@ import { fileURLToPath } from 'node:url'; import { SourceTestkit } from '@salesforce/source-testkit'; import { assert, expect } from 'chai'; import { RequestStatus } from '@salesforce/source-deploy-retrieve'; -import { DeployResultJson } from '../../../../src/utils/types.js'; +import { DeployResultJson } from '../../../src/utils/types.js'; describe('[project deploy report] NUTs with metadata-dir', () => { let testkit: SourceTestkit; diff --git a/test/commands/deploy/metadata/report.nut.ts b/test/nuts/metadata/report.nut.ts similarity index 98% rename from test/commands/deploy/metadata/report.nut.ts rename to test/nuts/metadata/report.nut.ts index a4ce3620..26314e43 100644 --- a/test/commands/deploy/metadata/report.nut.ts +++ b/test/nuts/metadata/report.nut.ts @@ -11,7 +11,7 @@ import { fileURLToPath } from 'node:url'; import { SourceTestkit } from '@salesforce/source-testkit'; import { assert, isObject } from '@salesforce/ts-types'; import { expect } from 'chai'; -import { DeployResultJson } from '../../../../src/utils/types.js'; +import { DeployResultJson } from '../../../src/utils/types.js'; describe('[project deploy report] NUTs with source-dir', () => { let testkit: SourceTestkit; diff --git a/test/commands/deploy/metadata/resume.nut.ts b/test/nuts/metadata/resume.nut.ts similarity index 97% rename from test/commands/deploy/metadata/resume.nut.ts rename to test/nuts/metadata/resume.nut.ts index 8afa3c6f..f4673dc4 100644 --- a/test/commands/deploy/metadata/resume.nut.ts +++ b/test/nuts/metadata/resume.nut.ts @@ -12,8 +12,8 @@ import { strict as assert } from 'node:assert'; import { SourceTestkit } from '@salesforce/source-testkit'; import { expect } from 'chai'; import { RequestStatus } from '@salesforce/source-deploy-retrieve'; -import { DeployResultJson } from '../../../../src/utils/types.js'; -import { CachedOptions } from '../../../../src/utils/deploy.js'; +import { DeployResultJson } from '../../../src/utils/types.js'; +import { CachedOptions } from '../../../src/utils/deploy.js'; function readDeployCache(projectDir: string): Record { // source-testkit doesn't expose the session, so we'll go up 1 level from the project to get to it diff --git a/test/commands/deploy/metadata/validate.nut.ts b/test/nuts/metadata/validate.nut.ts similarity index 98% rename from test/commands/deploy/metadata/validate.nut.ts rename to test/nuts/metadata/validate.nut.ts index 5fa12870..ea805524 100644 --- a/test/commands/deploy/metadata/validate.nut.ts +++ b/test/nuts/metadata/validate.nut.ts @@ -11,7 +11,7 @@ import { SourceTestkit } from '@salesforce/source-testkit'; import { isObject } from '@salesforce/ts-types'; import { assert, expect } from 'chai'; import { execCmd } from '@salesforce/cli-plugins-testkit'; -import { DeployResultJson } from '../../../../src/utils/types.js'; +import { DeployResultJson } from '../../../src/utils/types.js'; describe('deploy metadata validate NUTs', () => { let testkit: SourceTestkit; diff --git a/test/utils/deployResponses.ts b/test/utils/deployResponses.ts index ca96c705..c2c95360 100644 --- a/test/utils/deployResponses.ts +++ b/test/utils/deployResponses.ts @@ -389,7 +389,7 @@ export const getDeployResult = ( const successes = response.details.componentSuccesses; fileProps = ensureArray(successes); return fileProps - .filter((p) => p.fileName !== 'package.xml') + .filter((p) => p.fileName !== 'package.xml' && p.fileName !== '') .map((comp) => ({ fullName: comp.fullName, filePath: comp.fileName, diff --git a/test/utils/output.test.ts b/test/utils/deployResultFormatter.test.ts similarity index 86% rename from test/utils/output.test.ts rename to test/utils/deployResultFormatter.test.ts index 05796638..16f0dc28 100644 --- a/test/utils/output.test.ts +++ b/test/utils/deployResultFormatter.test.ts @@ -5,10 +5,12 @@ * For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause */ import * as path from 'node:path'; +import { join } from 'node:path'; import { assert, expect, config } from 'chai'; import sinon from 'sinon'; import { DeployMessage, DeployResult, FileResponse } from '@salesforce/source-deploy-retrieve'; import { ux } from '@oclif/core'; +import { ensureArray } from '@salesforce/kit'; import { getCoverageFormattersOptions } from '../../src/utils/coverage.js'; import { DeployResultFormatter } from '../../src/formatters/deployResultFormatter.js'; import { getDeployResult } from './deployResponses.js'; @@ -22,6 +24,45 @@ describe('deployResultFormatter', () => { sandbox.restore(); }); + describe('displaySuccesses', () => { + const deployResultSuccess = getDeployResult('successSync'); + let tableStub: sinon.SinonStub; + + beforeEach(() => { + tableStub = sandbox.stub(ux, 'table'); + }); + + it('finds components in server response, and adds them if not in fileResponses', () => { + ensureArray(deployResultSuccess.response.details.componentSuccesses).push({ + changed: 'false', + created: 'true', + createdDate: '123', + fullName: 'remoteClass', + componentType: 'ApexClass', + success: 'true', + deleted: 'false', + fileName: '', + }); + const formatter = new DeployResultFormatter(deployResultSuccess, { verbose: true }); + formatter.display(); + expect(tableStub.callCount).to.equal(1); + expect(tableStub.firstCall.args[0]).to.deep.equal([ + { + filePath: join('classes', 'ProductController.cls'), + fullName: 'ProductController', + state: 'Changed', + type: 'ApexClass', + }, + { + filePath: 'Not found in project', + fullName: 'remoteClass', + state: 'Created', + type: 'ApexClass', + }, + ]); + }); + }); + describe('displayFailures', () => { const deployResultFailure = getDeployResult('failed'); let tableStub: sinon.SinonStub;