Skip to content

Commit

Permalink
Match CLI result file/output behavior for synchronous test runs (#164)
Browse files Browse the repository at this point in the history
@W-9015909@
  • Loading branch information
AnanyaJha authored and rcoringrato-sfdc committed Mar 18, 2021
1 parent 530b66d commit 135b0ab
Show file tree
Hide file tree
Showing 9 changed files with 344 additions and 22 deletions.
10 changes: 7 additions & 3 deletions packages/apex-node/src/tests/testService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,7 @@ export class TestService {
apiTestResult.numTestsRun
),
skipRate: this.calculatePercentage(0, apiTestResult.numTestsRun),
testStartTime: formatStartTime(String(startTime)),
testStartTime: formatStartTime(startTime),
testExecutionTimeInMs: apiTestResult.totalTime ?? 0,
testTotalTimeInMs: apiTestResult.totalTime ?? 0,
commandTimeInMs: getCurrentTime() - startTime,
Expand Down Expand Up @@ -833,7 +833,9 @@ export class TestService {
fileMap.push({
path: join(
dirPath,
`test-result-${result.summary.testRunId}.json`
result.summary.testRunId
? `test-result-${result.summary.testRunId}.json`
: `test-result.json`
),
content: this.stringify(result)
});
Expand All @@ -853,7 +855,9 @@ export class TestService {
fileMap.push({
path: join(
dirPath,
`test-result-${result.summary.testRunId}-junit.xml`
result.summary.testRunId
? `test-result-${result.summary.testRunId}-junit.xml`
: `test-result-junit.xml`
),
content: junitResult
});
Expand Down
7 changes: 6 additions & 1 deletion packages/apex-node/src/utils/dateUtil.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,12 @@ export function getCurrentTime(): number {
return new Date().getTime();
}

export function formatStartTime(startTime: string): string {
/**
* Returns the formatted date and time given the milliseconds in numbers or UTC formatted string
* @param startTime start time in millisecond numbers or UTC format string
* @returns date and time formatted for locale
*/
export function formatStartTime(startTime: string | number): string {
const date = new Date(startTime);
return `${date.toDateString()} ${date.toLocaleTimeString()}`;
}
Expand Down
1 change: 0 additions & 1 deletion packages/apex-node/test/tests/asyncTests.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1019,7 +1019,6 @@ describe('Run Apex tests asynchronously', () => {
});

afterEach(() => {
timeStub.restore();
sandboxStub1.restore();
});

Expand Down
57 changes: 55 additions & 2 deletions packages/apex-node/test/tests/syncTests.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,21 +8,34 @@
import { AuthInfo, Connection } from '@salesforce/core';
import { MockTestOrgData, testSetup } from '@salesforce/core/lib/testSetup';
import { expect } from 'chai';
import { assert, createSandbox, SinonSandbox, SinonStub } from 'sinon';
import {
assert,
createSandbox,
SinonSandbox,
SinonSpy,
SinonStub
} from 'sinon';
import * as fs from 'fs';
import * as stream from 'stream';
import { join } from 'path';
import { SyncTestConfiguration, TestService } from '../../src/tests';
import {
TestLevel,
ApexOrgWideCoverage,
ApexCodeCoverageAggregate,
ApexCodeCoverage
ApexCodeCoverage,
ResultFormat,
OutputDirConfig
} from '../../src/tests/types';
import { nls } from '../../src/i18n';
import {
codeCoverageQueryResult,
perClassCodeCoverage,
syncResult,
syncTestResultSimple,
syncTestResultWithFailures
} from './testData';
import { JUnitReporter } from '../../src';

const $$ = testSetup();
let mockConnection: Connection;
Expand All @@ -39,6 +52,8 @@ describe('Run Apex tests synchronously', () => {
testLevel: 'RunSpecifiedTests'
};

let createStreamStub: SinonStub;
let junitSpy: SinonSpy;
beforeEach(async () => {
sandboxStub = createSandbox();
$$.setConfigStubContents('AuthInfoConfig', {
Expand All @@ -61,6 +76,11 @@ describe('Run Apex tests synchronously', () => {
body: JSON.stringify(requestOptions),
headers: { 'content-type': 'application/json' }
};

createStreamStub = sandboxStub.stub(fs, 'createWriteStream');
// eslint-disable-next-line @typescript-eslint/no-explicit-any
createStreamStub.returns(new stream.PassThrough() as any);
junitSpy = sandboxStub.spy(JUnitReporter.prototype, 'format');
});

afterEach(() => {
Expand Down Expand Up @@ -280,4 +300,37 @@ describe('Run Apex tests synchronously', () => {
}
});
});

describe('Create Synchronous Result Files', async () => {
it('should create json result file without testRunId for sync runs', async () => {
const config = {
dirPath: 'path/to/directory',
resultFormats: [ResultFormat.json]
} as OutputDirConfig;
const testSrv = new TestService(mockConnection);
await testSrv.writeResultFiles(syncResult, config);

expect(
createStreamStub.calledWith(join(config.dirPath, `test-result.json`))
).to.be.true;
expect(createStreamStub.callCount).to.eql(2);
});

it('should create junit result file without testRunId for sync runs', async () => {
const config = {
dirPath: 'path/to/directory',
resultFormats: [ResultFormat.junit]
} as OutputDirConfig;
const testSrv = new TestService(mockConnection);
await testSrv.writeResultFiles(syncResult, config);

expect(
createStreamStub.calledWith(
join(config.dirPath, `test-result-junit.xml`)
)
).to.be.true;
expect(junitSpy.calledOnce).to.be.true;
expect(createStreamStub.callCount).to.eql(2);
});
});
});
43 changes: 43 additions & 0 deletions packages/apex-node/test/tests/testData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
ApexCodeCoverageRecord,
ApexTestResultOutcome,
ApexTestResultRecord,
ApexTestRunResultStatus,
SyncTestResult,
TestResult
} from '../../src/tests/types';
Expand Down Expand Up @@ -92,6 +93,48 @@ export const testStartTime = '2020-11-09T18:02:50.000+0000';
const date = new Date(testStartTime);
const localStartTime = `${date.toDateString()} ${date.toLocaleTimeString()}`;
export const testRunId = '707xx0000AGQ3jbQQD';

export const syncResult: TestResult = {
// @ts-ignore
summary: {
outcome: ApexTestRunResultStatus.Passed,
testsRan: 1,
passing: 1,
failing: 0,
skipped: 0,
passRate: '100%',
failRate: '0%',
skipRate: '0%',
testStartTime: localStartTime,
testExecutionTimeInMs: 1765,
testTotalTimeInMs: 1765,
commandTimeInMs: 2000,
testRunId: '',
userId: '005xx000000abcDAAU'
},
tests: [
{
id: '',
queueItemId: '',
stackTrace: '',
message: '',
asyncApexJobId: '',
methodName: 'testMethod',
outcome: ApexTestResultOutcome.Pass,
apexLogId: null,
apexClass: {
id: '01pxx00000O6tXZQAZ',
name: 'TestApexClass',
namespacePrefix: 't3st',
fullName: 't3st__TestApexClass'
},
runTime: 8,
testTimestamp: '',
fullName: `t3st__TestApexClass.testMethod`
}
]
};

export const testResultData: TestResult = {
// @ts-ignore
summary: {
Expand Down
21 changes: 15 additions & 6 deletions packages/plugin-apex/src/commands/force/apex/test/run.ts
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,8 @@ export default class Run extends SfdxCommand {
jsonOutput,
this.flags.outputdir,
this.flags.resultformat,
this.flags.detailedcoverage
this.flags.detailedcoverage,
this.flags.synchronous
);

await testService.writeResultFiles(
Expand Down Expand Up @@ -175,11 +176,19 @@ export default class Run extends SfdxCommand {
this.ux.logJson(this.logJson(result));
break;
default:
const id = result.summary.testRunId;
const username = result.summary.username;
this.ux.log(
messages.getMessage('runTestReportCommand', [id, username])
);
if (this.flags.synchronous) {
this.logHuman(
result,
this.flags.detailedcoverage,
this.flags.outputdir
);
} else {
const id = result.summary.testRunId;
const username = result.summary.username;
this.ux.log(
messages.getMessage('runTestReportCommand', [id, username])
);
}
}
} catch (e) {
this.ux.logJson(result);
Expand Down
27 changes: 19 additions & 8 deletions packages/plugin-apex/src/reporters/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,20 +21,27 @@ import { CliJsonFormat } from './jsonReporter';
* @param outputDir Output directory for result files
* @param resultFormat Result format for output files
* @param detailedCoverage Boolean to control detailed coverage reporting
* @param synchronous Whether the test run was synchronous
* @returns Output directory configuration
*/
export function buildOutputDirConfig(
result: TestResult,
jsonOutput: CliJsonFormat,
outputDir: string,
resultFormat: ResultFormat,
detailedCoverage: boolean
resultFormat: ResultFormat | undefined,
detailedCoverage: boolean,
synchronous = false
): OutputDirConfig {
const outputDirConfig: OutputDirConfig = {
dirPath: outputDir,
fileInfos: [
dirPath: outputDir
};

if (typeof resultFormat !== 'undefined' || synchronous) {
outputDirConfig.fileInfos = [
{
filename: `test-result-${result.summary.testRunId}.json`,
filename: result.summary.testRunId
? `test-result-${result.summary.testRunId}.json`
: `test-result.json`,
content: jsonOutput
},
...(jsonOutput.coverage
Expand All @@ -45,9 +52,13 @@ export function buildOutputDirConfig(
}
]
: [])
],
resultFormats: [ResultFormat.junit]
};
];
outputDirConfig.resultFormats = [ResultFormat.junit];
}

if (typeof resultFormat === 'undefined' && synchronous) {
resultFormat = ResultFormat.human;
}

switch (resultFormat) {
case 'tap':
Expand Down
Loading

0 comments on commit 135b0ab

Please sign in to comment.