@flxbl-io/sfp
Version:
sfp is a CLI tool to help you manage your Salesforce projects in an artifact centric model
542 lines • 57.3 kB
JavaScript
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.ProgressReporter = void 0;
const fs = require('fs-extra');
const path = require("path");
const TestOptions_1 = require("./TestOptions");
const IndividualClassCoverage_1 = __importDefault(require("../apex/coverage/IndividualClassCoverage"));
const TestReportDisplayer_1 = require("./TestReportDisplayer");
const PackageTestCoverage_1 = __importDefault(require("../package/coverage/PackageTestCoverage"));
const sfp_logger_1 = __importStar(require("@flxbl-io/sfp-logger"));
const SFPStatsSender_1 = __importDefault(require("../stats/SFPStatsSender"));
const core_1 = require("@salesforce/core");
const apex_node_1 = require("@salesforce/apex-node");
const JSONReporter_1 = require("./JSONReporter");
const kit_1 = require("@salesforce/kit");
const ClearCodeCoverage_1 = __importDefault(require("./ClearCodeCoverage"));
const lodash_1 = __importDefault(require("lodash"));
const retry = require('async-retry');
const dedent_1 = __importDefault(require("dedent"));
class TriggerApexTests {
constructor(target_org, testOptions, coverageOptions, project_directory, fileLogger) {
this.target_org = target_org;
this.testOptions = testOptions;
this.coverageOptions = coverageOptions;
this.project_directory = project_directory;
this.fileLogger = fileLogger;
this.cancellationTokenSource = new apex_node_1.CancellationTokenSource();
}
async exec() {
let org = await core_1.Org.create({ aliasOrUsername: this.target_org });
this.conn = org.getConnection();
// graceful shutdown
const exitHandler = async () => {
await this.cancellationTokenSource.asyncCancel();
process.exit();
};
process.on('SIGINT', exitHandler);
process.on('SIGTERM', exitHandler);
let startTime = Date.now();
let testExecutionResult = false;
let testsRan;
let commandTime;
try {
const testService = new apex_node_1.TestService(this.conn);
//Clear Code Coverage before triggering tests
try {
let clearCodeCoverage = new ClearCodeCoverage_1.default(org, this.fileLogger);
await clearCodeCoverage.clear();
}
catch (error) {
sfp_logger_1.default.log(`Ignoring error in clearing code coverage attributed to ${error}.`, sfp_logger_1.LoggerLevel.DEBUG, this.fileLogger);
}
//Translate Tests to test levels used by apex-node
let translatedTestLevel;
//Fetch tests passed in the testOptions
let tests = null;
let suites = null;
let isCoverageToBeFetched = this.coverageOptions.isIndividualClassCoverageToBeValidated ||
this.coverageOptions.isPackageCoverageToBeValidated;
//Translate Test Option
({ translatedTestLevel, tests, suites } = await this.translateTestOptionToAPIVars(this.testOptions));
//Trigger tests asynchronously
let testRunResult;
try {
testRunResult = (await this.triggerTestAsynchronously(testService, translatedTestLevel, isCoverageToBeFetched, tests, suites));
}
catch (error) {
if (error.message.includes(`Cannot read properties of undefined (reading 'Status')`)) {
return {
result: false,
id: null,
message: (0, dedent_1.default) `Unable to obtain status of test run, Did you cancel the test run?
This error happens when the test run is cancelled and the status is not available.
Please retry`,
};
}
return {
result: false,
id: null,
message: error.message,
};
}
//Fetch Test Results
let testResult = await retry(async (bail) => {
return await testService.reportAsyncResults(testRunResult.summary.testRunId, isCoverageToBeFetched, this.cancellationTokenSource.token);
}, { retries: 2, minTimeout: 3000 });
testResult = this.fixBadNamespaceClassFullNames(testResult);
//Collect Failed Tests only if Parallel
testResult = await this.triggerSecondRunInSerialForParallelFailedTests(testResult, testService, translatedTestLevel, isCoverageToBeFetched);
//Filter testResult for duplicate test listing
testResult = this.removeDuplicateTestListing(testResult);
//Write Test Results to file
let jsonOutput = undefined;
try {
jsonOutput = this.writeTestOutput(testResult);
}
catch (error) {
sfp_logger_1.default.log(`Unable to write test results to file due to ${error}`, sfp_logger_1.LoggerLevel.DEBUG, this.fileLogger);
return {
result: false,
id: testResult.summary.testRunId,
message: (0, dedent_1.default) `Unable to fetch test execution results,
Please check the results in the org by using the URL below
${this.conn.instanceUrl}/lightning/setup/ApexTestHistory/home
Please try the test execution again`,
};
}
//Print tests result to screen
let testReportDisplayer = new TestReportDisplayer_1.TestReportDisplayer(jsonOutput, this.testOptions, this.fileLogger);
testReportDisplayer.printTestResults();
commandTime = testResult.summary.commandTimeInMs;
if (testResult.summary.outcome == 'Failed') {
testExecutionResult = false;
return {
result: false,
id: testResult.summary.testRunId,
message: 'Test Execution failed',
};
}
else {
if (isCoverageToBeFetched) {
let coverageResults = await this.validateForApexCoverage(jsonOutput.coverage.coverage);
testReportDisplayer.printCoverageReport(this.coverageOptions.coverageThreshold, coverageResults.classesCovered, coverageResults.classesWithInvalidCoverage);
testsRan = testResult.summary.testsRan;
testReportDisplayer.printTestSummary(coverageResults.packageTestCoverage);
testExecutionResult = coverageResults.result;
SFPStatsSender_1.default.logGauge('apextest.testcoverage', coverageResults.packageTestCoverage, {
package: this.testOptions instanceof TestOptions_1.RunAllTestsInPackageOptions
? this.testOptions.sfppackage.packageName
: null,
});
return {
result: coverageResults.result,
id: testResult.summary.testRunId,
message: coverageResults.message,
};
}
else {
testExecutionResult = true;
SFPStatsSender_1.default.logGauge('apextest.testcoverage', Number.parseInt(testResult.summary.testRunCoverage), {
package: this.testOptions instanceof TestOptions_1.RunAllTestsInPackageOptions
? this.testOptions.sfppackage.packageName
: null,
});
return {
result: true,
id: testResult.summary.testRunId,
message: `Test execution succesfully completed`,
};
}
}
}
finally {
this.reportMetrics(this.testOptions, {
targetOrg: this.target_org,
startTime,
testExecutionResult,
testsRan,
commandTime,
});
}
}
async translateTestOptionToAPIVars(testOptions) {
let translatedTestLevel;
let tests;
let suites;
if (testOptions instanceof TestOptions_1.RunAllTestsInPackageOptions) {
({ translatedTestLevel, tests } = await this.getTranslatedOptionsForAllTestInPackageOptions(testOptions));
}
else if (testOptions instanceof TestOptions_1.RunSpecifiedTestsOption) {
({ translatedTestLevel, tests } = await this.getTranslatedOptionsForSpecifiedTests(testOptions));
}
else if (testOptions instanceof TestOptions_1.RunApexTestSuitesOption) {
translatedTestLevel = "RunSpecifiedTests" /* TestLevel.RunSpecifiedTests */;
suites = testOptions.suiteNames;
sfp_logger_1.default.log(`Test Suites to be executed: ${(0, sfp_logger_1.COLOR_KEY_MESSAGE)(suites)}`, sfp_logger_1.LoggerLevel.INFO, this.fileLogger);
}
else if (testOptions instanceof TestOptions_1.RunLocalTests) {
translatedTestLevel = "RunLocalTests" /* TestLevel.RunLocalTests */;
sfp_logger_1.default.log(`Triggering all ${(0, sfp_logger_1.COLOR_KEY_MESSAGE)(`local tests`)}in the org`, sfp_logger_1.LoggerLevel.INFO, this.fileLogger);
}
else if (testOptions instanceof TestOptions_1.RunAllTestsInOrg) {
sfp_logger_1.default.log(`Triggering all ${(0, sfp_logger_1.COLOR_KEY_MESSAGE)(`all tests`)}in the org`, sfp_logger_1.LoggerLevel.INFO, this.fileLogger);
translatedTestLevel = "RunAllTestsInOrg" /* TestLevel.RunAllTestsInOrg */;
}
return { translatedTestLevel, tests, suites };
}
removeDuplicateTestListing(testResult) {
let modifiedTestResult = lodash_1.default.cloneDeep(testResult);
let toEliminateIndices = [];
for (let index = 0; index < modifiedTestResult.tests.length; index++) {
let idx = index;
let duplicateIndices = [index];
while (idx != -1) {
idx = lodash_1.default.findIndex(modifiedTestResult.tests, (elem) => {
return elem.methodName == modifiedTestResult.tests[index].methodName
&& elem.apexClass.name == modifiedTestResult.tests[index].apexClass.name;
}, idx + 1);
if (idx != -1)
duplicateIndices.push(idx);
}
if (duplicateIndices.length > 1) {
for (const idx of duplicateIndices) {
if (modifiedTestResult.tests[idx].outcome != 'Pass')
toEliminateIndices.push(idx);
}
}
}
modifiedTestResult.tests = modifiedTestResult.tests.filter(function (value, index, arr) {
return !toEliminateIndices.includes(index);
});
if (toEliminateIndices.length > 0)
modifiedTestResult = this.combineTestResult(modifiedTestResult);
return modifiedTestResult;
}
async getTranslatedOptionsForSpecifiedTests(testOptions) {
let translatedTestLevel = "RunSpecifiedTests" /* TestLevel.RunSpecifiedTests */;
let tests = testOptions.specifiedTests;
sfp_logger_1.default.log(`Tests to be executed: ${(0, sfp_logger_1.COLOR_KEY_MESSAGE)(tests)}`, sfp_logger_1.LoggerLevel.INFO, this.fileLogger);
sfp_logger_1.default.log(`Test Mode: ${(0, sfp_logger_1.COLOR_KEY_MESSAGE)(this.testOptions.synchronous == true ? 'serial' : 'parallel')}`, sfp_logger_1.LoggerLevel.INFO, this.fileLogger);
//Toggle to serial
await this.toggleParallelApexTesting(this.conn, this.fileLogger, this.testOptions.synchronous == true ? true : false);
return { translatedTestLevel, tests };
}
async getTranslatedOptionsForAllTestInPackageOptions(testOptions) {
sfp_logger_1.default.log(`Test Mode Descriptor in Package 'testSynchronous': ${testOptions.sfppackage.packageDescriptor.testSynchronous
? testOptions.sfppackage.packageDescriptor.testSynchronous
: false}`, sfp_logger_1.LoggerLevel.TRACE, this.fileLogger);
sfp_logger_1.default.log(`Test Mode: ${(0, sfp_logger_1.COLOR_KEY_MESSAGE)(testOptions.synchronous == true ? 'serial' : 'parallel')}`, sfp_logger_1.LoggerLevel.INFO, this.fileLogger);
await this.toggleParallelApexTesting(this.conn, this.fileLogger, testOptions.synchronous == true ? true : false);
let translatedTestLevel = "RunSpecifiedTests" /* TestLevel.RunSpecifiedTests */;
let tests = testOptions.specifiedTests;
sfp_logger_1.default.log(`Tests to be executed: ${(0, sfp_logger_1.COLOR_KEY_MESSAGE)(tests)}`, sfp_logger_1.LoggerLevel.INFO, this.fileLogger);
return { translatedTestLevel, tests };
}
async triggerSecondRunInSerialForParallelFailedTests(testResult, testService, translatedTestLevel, isCoverageToBeFetched) {
let modifiedTestResult = lodash_1.default.cloneDeep(testResult);
if (!this.testOptions.synchronous) {
let parallelFailedTestClasses = [];
let testClassesThatDonotContributedCoverage = [];
let testToBeTriggered = [];
for (const test of modifiedTestResult.tests) {
if (test.outcome == "Fail" /* ApexTestResultOutcome.Fail */) {
//Check for messages
if (test.message.includes(`Your request exceeded the time limit for processing`) ||
test.message.includes(`UNABLE_TO_LOCK_ROW`) ||
test.message.includes(`Internal Salesforce Error`) ||
test.message.includes(`LIMIT_EXCEEDED`) ||
test.message.includes(`Too many concurrent Apex compilations during resource mitigation`)) {
if (!testToBeTriggered.includes(test.apexClass.fullName)) {
parallelFailedTestClasses.push(test.apexClass.fullName);
testToBeTriggered.push(test.apexClass.fullName);
}
}
}
if (test.outcome == "Pass" /* ApexTestResultOutcome.Pass */) {
if (!test.perClassCoverage &&
(this.coverageOptions.isPackageCoverageToBeValidated ||
this.coverageOptions.isIndividualClassCoverageToBeValidated)) {
if (!testToBeTriggered.includes(test.apexClass.fullName)) {
testClassesThatDonotContributedCoverage.push(test.apexClass.fullName);
if (!testToBeTriggered.includes(test.apexClass.fullName))
testToBeTriggered.push(test.apexClass.fullName);
}
}
}
}
if (parallelFailedTestClasses.length > 0) {
sfp_logger_1.default.log(`Failed Tests while triggered in parallel: ${(0, sfp_logger_1.COLOR_KEY_MESSAGE)(parallelFailedTestClasses.toString())}`, sfp_logger_1.LoggerLevel.INFO, this.fileLogger);
}
if (testClassesThatDonotContributedCoverage.length > 0) {
sfp_logger_1.default.log(`Test Classes that were not able to contribute coverage: ${(0, sfp_logger_1.COLOR_KEY_MESSAGE)(testClassesThatDonotContributedCoverage.toString())}`, sfp_logger_1.LoggerLevel.INFO, this.fileLogger);
}
if (testToBeTriggered.length > 0) {
sfp_logger_1.default.log(`Triggering tests synchronously: ${(0, sfp_logger_1.COLOR_KEY_MESSAGE)(testToBeTriggered.toString())}`, sfp_logger_1.LoggerLevel.INFO, this.fileLogger);
//Trigger Second Test Run
//Convert to sequential
await this.toggleParallelApexTesting(this.conn, this.fileLogger, true);
//Trigger tests asynchronously
let secondRuntestRunResult;
secondRuntestRunResult = await retry(async (bail) => {
return (await this.triggerTestAsynchronously(testService, translatedTestLevel, isCoverageToBeFetched, testToBeTriggered.toString(), null));
}, { retries: 2, minTimeout: 3000 });
secondRuntestRunResult = this.fixBadNamespaceClassFullNames(secondRuntestRunResult);
//Fetch Test Results
const secondTestResult = this.fixBadNamespaceClassFullNames(await testService.reportAsyncResults(secondRuntestRunResult.summary.testRunId, true, this.cancellationTokenSource.token));
this.writeTestOutput(secondTestResult);
//Merge original test results with second run
const mergedTestResults = modifiedTestResult.tests;
for (const testObject of secondTestResult.tests) {
const index = mergedTestResults.findIndex((test) => test.fullName === testObject.fullName);
if (index !== -1) {
mergedTestResults[index] = testObject;
}
else {
mergedTestResults.push(testObject);
}
}
modifiedTestResult.tests = mergedTestResults;
//Merge original code coverage with second run
if (isCoverageToBeFetched) {
const mergedCodecoverage = modifiedTestResult.codecoverage;
for (const codeCoverageObject of secondTestResult.codecoverage) {
const index = mergedCodecoverage.findIndex((codeCoverage) => codeCoverage.name === codeCoverageObject.name);
if (index !== -1) {
mergedCodecoverage[index] = codeCoverageObject;
}
else {
mergedCodecoverage.push(codeCoverageObject);
}
}
modifiedTestResult.codecoverage = mergedCodecoverage;
}
//Now redo the math
modifiedTestResult = this.combineTestResult(modifiedTestResult, secondRuntestRunResult);
}
}
return modifiedTestResult;
}
fixBadNamespaceClassFullNames(testResult) {
let modifiedTestResult = lodash_1.default.cloneDeep(testResult);
try {
modifiedTestResult.tests = modifiedTestResult.tests.map((test) => {
return {
...test,
...{
fullName: test.fullName?.replace('__', '.'),
apexClass: {
...test.apexClass,
...{
fullName: test.apexClass?.fullName?.replace('__', '.'),
},
},
},
};
});
}
catch (error) {
sfp_logger_1.default.log(`Unable to fix bad namespace class full names due to ${error}`, sfp_logger_1.LoggerLevel.DEBUG, this.fileLogger);
modifiedTestResult = lodash_1.default.cloneDeep(testResult);
}
return modifiedTestResult;
}
combineTestResult(testResult, testResultSecondRun) {
testResult.summary.failing = 0;
testResult.summary.passing = 0;
testResult.summary.skipped = 0;
for (const test of testResult.tests) {
if (test.outcome === "Pass" /* ApexTestResultOutcome.Pass */)
testResult.summary.passing++;
else if (test.outcome === "Fail" /* ApexTestResultOutcome.Fail */)
testResult.summary.failing++;
else if (test.outcome === "Skip" /* ApexTestResultOutcome.Skip */)
testResult.summary.skipped++;
}
if (testResult.summary.failing > 0)
testResult.summary.outcome = 'Failed';
else
testResult.summary.outcome = 'Passed';
testResult.summary.passRate = (testResult.summary.passing / testResult.summary.testsRan) * 100 + '%';
testResult.summary.failRate = (testResult.summary.failing / testResult.summary.testsRan) * 100 + '%';
testResult.summary.commandTimeInMs =
testResult.summary.commandTimeInMs + testResultSecondRun?.summary.commandTimeInMs;
testResult.summary.testExecutionTimeInMs =
testResult.summary.testExecutionTimeInMs + testResultSecondRun?.summary.testExecutionTimeInMs;
testResult.summary.testTotalTimeInMs =
testResult.summary.testTotalTimeInMs + testResultSecondRun?.summary.testTotalTimeInMs;
delete testResult.summary.testRunCoverage;
delete testResult.summary.orgWideCoverage;
delete testResult.summary.totalLines;
delete testResult.summary.coveredLines;
if (testResultSecondRun)
testResult.summary.testRunId = testResult.summary.testRunId.concat('_', testResultSecondRun.summary.testRunId);
return testResult;
}
/**
* Trigger tests asynchronously
* @param {TestService} testService
* @param {TestLevel} testLevel
* @param {string} tests?
* @param {string} suites?
*/
async triggerTestAsynchronously(testService, testLevel, isCoverageToBeFetched, tests, suites) {
const payload = await testService.buildAsyncPayload(testLevel, null, tests, suites);
let result = await testService.runTestAsynchronous(payload, isCoverageToBeFetched, false, new ProgressReporter(this.fileLogger), this.cancellationTokenSource.token);
if (this.cancellationTokenSource.token.isCancellationRequested) {
throw new Error(`A previous run is being cancelled.. Please try after some time`);
}
return result;
}
async validateForApexCoverage(coverageReport) {
if (this.testOptions instanceof TestOptions_1.RunAllTestsInPackageOptions) {
let packageTestCoverage = new PackageTestCoverage_1.default(this.testOptions.sfppackage, coverageReport, this.fileLogger, this.conn);
return packageTestCoverage.validateTestCoverage(this.coverageOptions.coverageThreshold);
}
else {
if (this.coverageOptions.isIndividualClassCoverageToBeValidated) {
let coverageValidator = new IndividualClassCoverage_1.default(coverageReport, this.fileLogger);
return coverageValidator.validateIndividualClassCoverage(coverageValidator.getIndividualClassCoverage(this.coverageOptions.classesToBeValidated), this.coverageOptions.coverageThreshold);
}
else {
let coverageValidator = new IndividualClassCoverage_1.default(coverageReport, this.fileLogger);
return coverageValidator.validateIndividualClassCoverage(coverageValidator.getIndividualClassCoverage());
}
}
}
writeJUnit(testResult) {
sfp_logger_1.default.log(`Junit Report file available at ${path.join(this.testOptions.outputdir, `test-result-${testResult.summary.testRunId}-junit.xml`)}`);
let reportAsJUnitReport = new apex_node_1.JUnitReporter().format(testResult);
fs.writeFileSync(path.join(this.testOptions.outputdir, `test-result-${testResult.summary.testRunId}-junit.xml`), reportAsJUnitReport);
}
writeTestOutput(testResult) {
const jsonOutput = this.formatResultInJson(testResult);
//write output files
fs.ensureDirSync(this.testOptions.outputdir);
//Write files
fs.writeJSONSync(path.join(this.testOptions.outputdir, `test-result-${testResult.summary.testRunId}.json`), testResult, { spaces: 4 });
if (jsonOutput.coverage)
fs.writeJSONSync(path.join(this.testOptions.outputdir, `test-result-${testResult.summary.testRunId}-coverage.json`), jsonOutput.coverage?.coverage, { spaces: 4 });
//Write Junit Result no matter what
this.writeJUnit(testResult);
return jsonOutput;
}
formatResultInJson(result) {
try {
const reporter = new JSONReporter_1.JsonReporter();
return reporter.format(result);
}
catch (error) {
return null;
}
}
//Enable Synchronus Compile on Deploy
async toggleParallelApexTesting(conn, logger, toEnable) {
try {
sfp_logger_1.default.log(`Set enableDisableParallelApexTesting:${toEnable}`, sfp_logger_1.LoggerLevel.TRACE, logger);
let apexSettingMetadata = { fullName: 'ApexSettings', enableDisableParallelApexTesting: toEnable };
let result = await conn.metadata.upsert('ApexSettings', apexSettingMetadata);
if (result.success) {
sfp_logger_1.default.log(`Successfully updated apex testing setting`, sfp_logger_1.LoggerLevel.INFO, logger);
}
}
catch (error) {
sfp_logger_1.default.log(`Skipping toggling of enableDisableParallelApexTesting due to ${error}..`, sfp_logger_1.LoggerLevel.INFO, logger);
}
}
reportMetrics(testOptions, testMetrics) {
let elapsedTime = Date.now() - testMetrics.startTime;
if (testMetrics.testExecutionResult)
SFPStatsSender_1.default.logGauge('apextest.tests.ran', testMetrics.testsRan, {
test_result: String(testMetrics.testExecutionResult),
package: testOptions instanceof TestOptions_1.RunAllTestsInPackageOptions ? testOptions.sfppackage.packageName : null,
type: testOptions.testLevel,
target_org: testMetrics.targetOrg,
});
SFPStatsSender_1.default.logGauge('apextest.testtotal.time', elapsedTime, {
test_result: String(testMetrics.testExecutionResult),
package: testOptions instanceof TestOptions_1.RunAllTestsInPackageOptions ? testOptions.sfppackage.packageName : null,
type: testOptions['testlevel'],
target_org: testMetrics.targetOrg,
});
if (testMetrics.commandTime)
SFPStatsSender_1.default.logGauge('apextest.command.time', testMetrics.commandTime, {
test_result: String(testMetrics.testExecutionResult),
package: testOptions instanceof TestOptions_1.RunAllTestsInPackageOptions ? testOptions.sfppackage.packageName : null,
type: testOptions.testLevel,
target_org: testMetrics.targetOrg,
});
SFPStatsSender_1.default.logCount('apextests.triggered', {
test_result: String(testMetrics.testExecutionResult),
package: testOptions instanceof TestOptions_1.RunAllTestsInPackageOptions ? testOptions.sfppackage.packageName : null,
type: testOptions.testLevel,
target_org: testMetrics.targetOrg,
});
}
}
exports.default = TriggerApexTests;
class ProgressReporter {
constructor(logger) {
this.logger = logger;
this.lastExecutedTime = Date.now();
}
report(value) {
try {
let count = {};
//Limit printing an update to 30 seconds
if (Date.now() - this.lastExecutedTime > kit_1.Duration.seconds(30).milliseconds) {
if (value.type == 'TestQueueProgress') {
for (const elem of value.value.records) {
if (elem?.Status) {
if (!count[elem.Status]) {
count[elem.Status] = 1;
}
else
count[elem.Status]++;
}
}
let statusString = '';
//Compute total
let total = 0;
for (const [key, value] of Object.entries(count)) {
total += value;
}
statusString = `Completed:${count['Completed'] ? count['Completed'] : 0}/${total} Queued(${count['Queued'] ? count['Queued'] : 0}) Failed(${(0, sfp_logger_1.COLOR_ERROR)(count['Failed'] ? count['Failed'] : 0)}) `;
sfp_logger_1.default.log(`Test Status: ` + (0, sfp_logger_1.COLOR_KEY_MESSAGE)(statusString), sfp_logger_1.LoggerLevel.INFO, this.logger);
this.lastExecutedTime = Date.now();
}
}
}
catch (error) {
console.log(`Bummer`);
}
}
}
exports.ProgressReporter = ProgressReporter;
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiVHJpZ2dlckFwZXhUZXN0cy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy9jb3JlL2FwZXh0ZXN0L1RyaWdnZXJBcGV4VGVzdHMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7QUFBQSxNQUFNLEVBQUUsR0FBRyxPQUFPLENBQUMsVUFBVSxDQUFDLENBQUM7QUFDL0IsNkJBQThCO0FBQzlCLCtDQU91QjtBQUN2Qix1R0FBb0c7QUFDcEcsK0RBQTREO0FBQzVELGtHQUEwRTtBQUMxRSxtRUFBc0c7QUFDdEcsNkVBQXFEO0FBQ3JELDJDQUFtRDtBQUNuRCxxREFXK0I7QUFDL0IsaURBQTZEO0FBQzdELHlDQUEyQztBQUMzQyw0RUFBb0Q7QUFDcEQsb0RBQXVCO0FBQ3ZCLE1BQU0sS0FBSyxHQUFHLE9BQU8sQ0FBQyxhQUFhLENBQUMsQ0FBQztBQUNyQyxvREFBNEI7QUFFNUIsTUFBcUIsZ0JBQWdCO0lBSWpDLFlBQ1ksVUFBa0IsRUFDbEIsV0FBd0IsRUFDeEIsZUFBZ0MsRUFDaEMsaUJBQXlCLEVBQ3pCLFVBQWdCO1FBSmhCLGVBQVUsR0FBVixVQUFVLENBQVE7UUFDbEIsZ0JBQVcsR0FBWCxXQUFXLENBQWE7UUFDeEIsb0JBQWUsR0FBZixlQUFlLENBQWlCO1FBQ2hDLHNCQUFpQixHQUFqQixpQkFBaUIsQ0FBUTtRQUN6QixlQUFVLEdBQVYsVUFBVSxDQUFNO1FBUGxCLDRCQUF1QixHQUFHLElBQUksbUNBQXVCLEVBQUUsQ0FBQztJQVEvRCxDQUFDO0lBRUcsS0FBSyxDQUFDLElBQUk7UUFLYixJQUFJLEdBQUcsR0FBRyxNQUFNLFVBQUcsQ0FBQyxNQUFNLENBQUMsRUFBRSxlQUFlLEVBQUUsSUFBSSxDQUFDLFVBQVUsRUFBRSxDQUFDLENBQUM7UUFDakUsSUFBSSxDQUFDLElBQUksR0FBRyxHQUFHLENBQUMsYUFBYSxFQUFFLENBQUM7UUFFaEMsb0JBQW9CO1FBQ3BCLE1B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