UNPKG

@flxbl-io/sfp

Version:

sfp is a CLI tool to help you manage your Salesforce projects in an artifact centric model

236 lines 20.3 kB
"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 }); const sfp_logger_1 = __importStar(require("@flxbl-io/sfp-logger")); const IndividualClassCoverage_1 = __importDefault(require("../../apex/coverage/IndividualClassCoverage")); const SfpPackage_1 = require("../SfpPackage"); const ApexClassFetcher_1 = __importDefault(require("../../apex/ApexClassFetcher")); const ApexCodeCoverageAggregateFetcher_1 = __importDefault(require("../../apex/coverage/ApexCodeCoverageAggregateFetcher")); const ApexTriggerFetcher_1 = __importDefault(require("../../apex/ApexTriggerFetcher")); class PackageTestCoverage { constructor(pkg, codeCoverage, logger, conn) { this.pkg = pkg; this.codeCoverage = codeCoverage; this.logger = logger; this.conn = conn; this.packageTestCoverage = -1; // Set inital value this.individualClassCoverage = new IndividualClassCoverage_1.default(this.codeCoverage, this.logger); } async getCurrentPackageTestCoverage() { let packageClasses = this.pkg.apexClassWithOutTestClasses; let triggers = this.pkg.triggers; let filteredCodeCoverage = this.filterCodeCoverageToPackageClassesAndTriggers(this.codeCoverage, packageClasses, triggers); let totalLines = 0; let totalCovered = 0; for (let classCoverage of filteredCodeCoverage) { if (classCoverage.coveredPercent !== null) { totalLines += classCoverage.totalLines; totalCovered += classCoverage.totalCovered; } } let listOfApexClassOrTriggerId = []; let classesNotTouchedByTestClass = this.getClassesNotTouchedByTestClass(packageClasses, this.codeCoverage); if (classesNotTouchedByTestClass.length > 0) { let apexClassIds = (await new ApexClassFetcher_1.default(this.conn).fetchApexClassByName(classesNotTouchedByTestClass)).map((apexClass) => apexClass.Id); listOfApexClassOrTriggerId = listOfApexClassOrTriggerId.concat(apexClassIds); } let triggersNotTouchedByTestClass = this.getTriggersNotTouchedByTestClass(triggers, this.codeCoverage); if (triggersNotTouchedByTestClass.length > 0) { let triggerIds = (await new ApexTriggerFetcher_1.default(this.conn).fetchApexTriggerByName(triggersNotTouchedByTestClass)).map((trigger) => trigger.Id); listOfApexClassOrTriggerId = listOfApexClassOrTriggerId.concat(triggerIds); } if (listOfApexClassOrTriggerId.length > 0) { let recordsOfApexCodeCoverageAggregate = await new ApexCodeCoverageAggregateFetcher_1.default(this.conn).fetchACCAById(listOfApexClassOrTriggerId); if (recordsOfApexCodeCoverageAggregate.length > 0) { let numLinesUncovered = 0; // aggregate number of unconvered lines for classes & triggers that are not touched by any test classes recordsOfApexCodeCoverageAggregate.forEach((record) => { numLinesUncovered += record.NumLinesUncovered; }); totalLines += numLinesUncovered; } } let testCoverage = Math.floor((totalCovered / totalLines) * 100); this.packageTestCoverage = testCoverage; return testCoverage; } async validateTestCoverage(coverageThreshold) { if (this.packageTestCoverage == -1) //No Value available await this.getCurrentPackageTestCoverage(); let classesCovered = this.getIndividualClassCoverageByPackage(this.codeCoverage); if (coverageThreshold == undefined || coverageThreshold < 75) { sfp_logger_1.default.log('Setting minimum coverage percentage to 75%.'); coverageThreshold = 75; } if (this.pkg.packageType === SfpPackage_1.PackageType.Unlocked) { if (this.packageTestCoverage < coverageThreshold) { // Coverage inadequate, set result to false return { result: false, // Had earlier Changed to warning in Apr-22, due to unstable coverage, now reverting packageTestCoverage: this.packageTestCoverage, classesCovered: classesCovered, message: `${(0, sfp_logger_1.COLOR_WARNING)(`The package has an overall coverage of ${this.packageTestCoverage}%, which does not meet the required overall coverage of ${coverageThreshold}%`)}`, }; } else { return { result: true, packageTestCoverage: this.packageTestCoverage, classesCovered: classesCovered, message: `Package overall coverage is greater than ${coverageThreshold}%`, }; } } else if (this.pkg.packageType === SfpPackage_1.PackageType.Source || this.pkg.packageType === SfpPackage_1.PackageType.Diff) { sfp_logger_1.default.log("Package type is Source. Validating individual class coverage"); let individualClassValidationResults = this.individualClassCoverage.validateIndividualClassCoverage(this.getIndividualClassCoverageByPackage(this.codeCoverage), coverageThreshold); if (individualClassValidationResults.result) { return { result: true, packageTestCoverage: this.packageTestCoverage, classesCovered: classesCovered, classesWithInvalidCoverage: individualClassValidationResults.classesWithInvalidCoverage, message: `Individidual coverage of classes is greater than ${coverageThreshold}%`, }; } else { return { result: false, packageTestCoverage: this.packageTestCoverage, classesCovered: classesCovered, classesWithInvalidCoverage: individualClassValidationResults.classesWithInvalidCoverage, message: `There are classes that do not satisfy the minimum code coverage of ${coverageThreshold}%`, }; } } else { throw new Error('Unhandled package type'); } } getIndividualClassCoverageByPackage(codeCoverageReport) { let individualClassCoverage = []; let packageClasses = this.pkg.apexClassWithOutTestClasses; let triggers = this.pkg.triggers; codeCoverageReport = this.filterCodeCoverageToPackageClassesAndTriggers(codeCoverageReport, packageClasses, triggers); for (let classCoverage of codeCoverageReport) { if (classCoverage['coveredPercent'] !== null) { individualClassCoverage.push({ name: classCoverage['name'], coveredPercent: classCoverage['coveredPercent'], }); } } let namesOfClassesWithoutTest = this.getClassesNotTouchedByTestClass(packageClasses, codeCoverageReport); if (namesOfClassesWithoutTest.length > 0) { let classesWithoutTest = namesOfClassesWithoutTest.map((className) => { return { name: className, coveredPercent: 0 }; }); individualClassCoverage = individualClassCoverage.concat(classesWithoutTest); } // Check for triggers with no test class let namesOfTriggersWithoutTest = this.getTriggersNotTouchedByTestClass(triggers, codeCoverageReport); if (namesOfTriggersWithoutTest.length > 0) { let triggersWithoutTest = namesOfTriggersWithoutTest.map((triggerName) => { return { name: triggerName, coveredPercent: 0 }; }); individualClassCoverage = individualClassCoverage.concat(triggersWithoutTest); } return individualClassCoverage; } /** * Returns names of triggers in the package that are not triggered by the execution of any test classes * Returns empty array if triggers is null or undefined * @param triggers * @param codeCoverageReport * @returns */ getTriggersNotTouchedByTestClass(triggers, codeCoverageReport) { if (triggers != null) { return triggers.filter((trigger) => { for (let classCoverage of codeCoverageReport) { if (classCoverage['name'] === trigger) { // Filter out triggers if accounted for in coverage json return false; } } return true; }); } else return []; } /** * Returns name of classes in the package that are not touched by the execution of any test classes * Returns empty array if packageClasses is null or undefined * @param packageClasses * @param codeCoverageReport * @returns */ getClassesNotTouchedByTestClass(packageClasses, codeCoverageReport) { if (packageClasses != null) { return packageClasses.filter((packageClass) => { for (let classCoverage of codeCoverageReport) { if (classCoverage['name'] === packageClass) { // Filter out package class if accounted for in coverage json return false; } } return true; }); } else return []; } /** * Filter code coverage to classes and triggers in the package * @param codeCoverage * @param packageClasses * @param triggers */ filterCodeCoverageToPackageClassesAndTriggers(codeCoverage, packageClasses, triggers) { let filteredCodeCoverage = codeCoverage.filter((classCoverage) => { if (packageClasses != null) { for (let packageClass of packageClasses) { if (packageClass === classCoverage['name']) return true; } } if (triggers != null) { for (let trigger of triggers) { if (trigger === classCoverage['name']) { return true; } } } return false; }); return filteredCodeCoverage; } } exports.default = PackageTestCoverage; //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiUGFja2FnZVRlc3RDb3ZlcmFnZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL3NyYy9jb3JlL3BhY2thZ2UvY292ZXJhZ2UvUGFja2FnZVRlc3RDb3ZlcmFnZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0FBQUEsbUVBQXdFO0FBQ3hFLDBHQUFrRjtBQUNsRiw4Q0FBd0Q7QUFFeEQsbUZBQTJEO0FBQzNELDRIQUFvRztBQUNwRyx1RkFBK0Q7QUFFL0QsTUFBcUIsbUJBQW1CO0lBSXBDLFlBQ1ksR0FBZSxFQUNmLFlBQWlCLEVBQ2pCLE1BQWMsRUFDTCxJQUFnQjtRQUh6QixRQUFHLEdBQUgsR0FBRyxDQUFZO1FBQ2YsaUJBQVksR0FBWixZQUFZLENBQUs7UUFDakIsV0FBTSxHQUFOLE1BQU0sQ0FBUTtRQUNMLFNBQUksR0FBSixJQUFJLENBQVk7UUFON0Isd0JBQW1CLEdBQVcsQ0FBQyxDQUFDLENBQUMsQ0FBQyxtQkFBbUI7UUFRekQsSUFBSSxDQUFDLHVCQUF1QixHQUFHLElBQUksaUNBQXVCLENBQUMsSUFBSSxDQUFDLFlBQVksRUFBRSxJQUFJLENBQUMsTUFBTSxDQUFDLENBQUM7SUFDL0YsQ0FBQztJQUVNLEtBQUssQ0FBQyw2QkFBNkI7UUFDdEMsSUFBSSxjQUFjLEdBQWEsSUFBSSxDQUFDLEdBQUcsQ0FBQywyQkFBMkIsQ0FBQztRQUNwRSxJQUFJLFFBQVEsR0FBYSxJQUFJLENBQUMsR0FBRyxDQUFDLFFBQVEsQ0FBQztRQUUzQyxJQUFJLG9CQUFvQixHQUFHLElBQUksQ0FBQyw2Q0FBNkMsQ0FDekUsSUFBSSxDQUFDLFlBQVksRUFDakIsY0FBYyxFQUNkLFFBQVEsQ0FDWCxDQUFDO1FBRUYsSUFBSSxVQUFVLEdBQVcsQ0FBQyxDQUFDO1FBQzNCLElBQUksWUFBWSxHQUFXLENBQUMsQ0FBQztRQUM3QixLQUFLLElBQUksYUFBYSxJQUFJLG9CQUFvQixFQUFFLENB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