UNPKG

html-reporter

Version:

Html-reporter and GUI for viewing and managing results of a tests run. Currently supports Testplane and Hermione.

287 lines 12.1 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.PlaywrightTestResultAdapter = exports.getStatus = exports.DEFAULT_DIFF_OPTIONS = exports.ImageTitleEnding = exports.PwtTestStatus = void 0; const path_1 = __importDefault(require("path")); const image_size_1 = __importDefault(require("image-size")); const lodash_1 = __importDefault(require("lodash")); const strip_ansi_1 = __importDefault(require("strip-ansi")); const common_utils_1 = require("../../common-utils"); const constants_1 = require("../../constants"); const errors_1 = require("../../errors"); const types_1 = require("../../types"); var PwtTestStatus; (function (PwtTestStatus) { PwtTestStatus["PASSED"] = "passed"; PwtTestStatus["FAILED"] = "failed"; PwtTestStatus["TIMED_OUT"] = "timedOut"; PwtTestStatus["INTERRUPTED"] = "interrupted"; PwtTestStatus["SKIPPED"] = "skipped"; })(PwtTestStatus || (exports.PwtTestStatus = PwtTestStatus = {})); var ImageTitleEnding; (function (ImageTitleEnding) { ImageTitleEnding["Expected"] = "-expected.png"; ImageTitleEnding["Actual"] = "-actual.png"; ImageTitleEnding["Diff"] = "-diff.png"; ImageTitleEnding["Previous"] = "-previous.png"; })(ImageTitleEnding || (exports.ImageTitleEnding = ImageTitleEnding = {})); const ANY_IMAGE_ENDING_REGEXP = new RegExp(Object.values(ImageTitleEnding).map(ending => `${ending}$`).join('|')); exports.DEFAULT_DIFF_OPTIONS = { diffColor: '#ff00ff' }; const getStatus = (result) => { if (result.status === constants_1.TestStatus.RUNNING) { return constants_1.TestStatus.RUNNING; } if (result.status === constants_1.TestStatus.UPDATED) { return constants_1.TestStatus.UPDATED; } if (result.status === PwtTestStatus.PASSED) { return constants_1.TestStatus.SUCCESS; } if ([PwtTestStatus.FAILED, PwtTestStatus.TIMED_OUT, PwtTestStatus.INTERRUPTED].includes(result.status)) { return constants_1.TestStatus.FAIL; } if (result.status === PwtTestStatus.SKIPPED) { return constants_1.TestStatus.SKIPPED; } return constants_1.TestStatus.IDLE; }; exports.getStatus = getStatus; const extractErrorMessage = (result) => { if (lodash_1.default.isEmpty(result.errors)) { return ''; } if (result.errors.length === 1) { return (0, strip_ansi_1.default)(result.errors[0].message || '').split('\n')[0]; } return JSON.stringify(result.errors.map(err => (0, strip_ansi_1.default)(err.message || '').split('\n')[0])); }; const extractErrorStack = (result) => { if (lodash_1.default.isEmpty(result.errors)) { return ''; } if (result.errors.length === 1) { return (0, strip_ansi_1.default)(result.errors[0].stack || ''); } return JSON.stringify(result.errors.map(err => (0, strip_ansi_1.default)(err.stack || ''))); }; const extractToMatchScreenshotError = (result, { state, expectedAttachment, diffAttachment, actualAttachment }) => { const snapshotName = state + '.png'; if (expectedAttachment && diffAttachment && actualAttachment) { const errors = (result.errors || []); const imageDiffError = errors.find(err => { return err.meta?.type === errors_1.ErrorName.IMAGE_DIFF && err.meta.snapshotName === snapshotName; }); return { name: errors_1.ErrorName.IMAGE_DIFF, message: '', diffClusters: imageDiffError?.meta?.diffClusters }; } const errors = (result.errors || []); const noRefImageError = errors.find(err => { return err.meta?.type === errors_1.ErrorName.NO_REF_IMAGE && err.meta.snapshotName === snapshotName; }); return noRefImageError ? { name: errors_1.ErrorName.NO_REF_IMAGE, message: (0, strip_ansi_1.default)(noRefImageError?.message) } : null; }; const getImageData = (attachment) => { if (!attachment) { return null; } return { path: attachment.path, size: !attachment.size ? lodash_1.default.pick((0, image_size_1.default)(attachment.path), ['height', 'width']) : attachment.size, relativePath: attachment.relativePath || path_1.default.relative(process.cwd(), attachment.path) }; }; const getHistory = (steps) => { return steps?.map(step => ({ [types_1.TestStepKey.Name]: step.title, [types_1.TestStepKey.Args]: [], [types_1.TestStepKey.IsFailed]: Boolean(step.error), [types_1.TestStepKey.TimeStart]: step.startTime instanceof Date ? step.startTime.getTime() : new Date(step.startTime).getTime(), [types_1.TestStepKey.Duration]: step.duration, [types_1.TestStepKey.Children]: getHistory(step.steps), [types_1.TestStepKey.IsGroup]: step.steps?.length > 0 })) ?? []; }; class PlaywrightTestResultAdapter { static create(testCase, testResult, attempt) { return new this(testCase, testResult, attempt); } constructor(testCase, testResult, attempt) { this._testCase = testCase; this._testResult = testResult; this._attempt = attempt; } get attempt() { return this._attempt; } get browserId() { return this._testCase.parent.project()?.name; } get description() { return undefined; } get error() { const message = extractErrorMessage(this._testResult); if (message) { const result = { name: errors_1.ErrorName.GENERAL_ERROR, message }; const stack = extractErrorStack(this._testResult); if (!lodash_1.default.isNil(stack)) { result.stack = stack; } if (/snapshot .*doesn't exist/.test(message) && message.includes('.png')) { result.name = errors_1.ErrorName.NO_REF_IMAGE; } else if (message.includes('Screenshot comparison failed')) { result.name = errors_1.ErrorName.IMAGE_DIFF; } return result; } return undefined; } get errorDetails() { return null; } get file() { return path_1.default.relative(process.cwd(), this._testCase.location.file); } get fullName() { return this.testPath.join(constants_1.DEFAULT_TITLE_DELIMITER); } get history() { return getHistory(this._testResult.steps); } get id() { return this.testPath.concat(this.browserId, this.attempt.toString()).join(' '); } get imageDir() { return (0, common_utils_1.getShortMD5)(this.fullName); } get imagesInfo() { const imagesInfo = Object.entries(this._attachmentsByState).map(([state, attachments]) => { const expectedAttachment = attachments.find(a => a.name?.endsWith(ImageTitleEnding.Expected)); const diffAttachment = attachments.find(a => a.name?.endsWith(ImageTitleEnding.Diff)); const actualAttachment = attachments.find(a => a.name?.endsWith(ImageTitleEnding.Actual)); const [expectedImg, diffImg, actualImg] = [expectedAttachment, diffAttachment, actualAttachment].map(getImageData); const error = extractToMatchScreenshotError(this._testResult, { state, expectedAttachment, diffAttachment, actualAttachment }) || this.error; // We now provide refImg here, though on some pwt versions it's impossible to provide correct path: // older pwt versions had test-results directory in expected path instead of project directory. if (error?.name === errors_1.ErrorName.IMAGE_DIFF && expectedImg && diffImg && actualImg) { return { status: constants_1.FAIL, stateName: state, diffImg, actualImg, expectedImg, refImg: lodash_1.default.clone(expectedImg), diffClusters: lodash_1.default.get(error, 'diffClusters', []), // TODO: extract diffOptions from config diffOptions: { current: actualImg.path, reference: expectedImg.path, ...exports.DEFAULT_DIFF_OPTIONS } }; } else if (error?.name === errors_1.ErrorName.NO_REF_IMAGE && actualImg) { return { status: constants_1.ERROR, stateName: state, error: lodash_1.default.pick(error, ['message', 'name', 'stack']), actualImg, ...(expectedImg ? { refImg: lodash_1.default.clone(expectedImg) } : {}) }; } else if (expectedAttachment?.isUpdated && expectedImg && actualImg) { return { status: constants_1.UPDATED, stateName: state, refImg: lodash_1.default.clone(expectedImg), expectedImg, actualImg }; } else if (!error && expectedImg) { return { status: constants_1.SUCCESS, stateName: state, expectedImg, ...(actualImg ? { actualImg } : {}) }; } return null; }).filter((value) => value !== null); if (this.screenshot) { imagesInfo.push({ status: lodash_1.default.isEmpty((0, common_utils_1.getError)(this.error)) ? constants_1.SUCCESS : constants_1.ERROR, actualImg: this.screenshot }); } return imagesInfo; } get meta() { return Object.fromEntries(this._testCase.annotations.map(a => [a.type, a.description ?? ''])); } get multipleTabs() { return true; } get screenshot() { const pageScreenshot = this._testResult.attachments.find(a => a.contentType === 'image/png' && a.name === 'screenshot'); return getImageData(pageScreenshot); } get sessionId() { // TODO: implement getting sessionId return ''; } get skipReason() { return this._testCase.annotations.find(a => a.type === 'skip')?.description || ''; } get state() { return { name: this._testCase.title }; } get status() { const status = (0, exports.getStatus)(this._testResult); if (status === constants_1.TestStatus.FAIL) { if ((0, common_utils_1.isNoRefImageError)(this.error) || (0, common_utils_1.isImageDiffError)(this.error)) { return constants_1.FAIL; } return constants_1.TestStatus.ERROR; } return status; } get testPath() { // slicing because first entries are not actually test-name, but a file, etc. return this._testCase.titlePath().slice(3); } get timestamp() { return this._testResult.startTime.getTime(); } get url() { // TODO: HERMIONE-1191 return ''; } get _attachmentsByState() { // Filtering out only images. Page screenshots on reject are named "screenshot", we don't want them in state either. const imageAttachments = this._testResult.attachments.filter(a => a.contentType === 'image/png' && ANY_IMAGE_ENDING_REGEXP.test(a.name)); return lodash_1.default.groupBy(imageAttachments, a => a.name.replace(ANY_IMAGE_ENDING_REGEXP, '')); } get duration() { return this._testResult.duration; } get attachments() { if (this._testCase.tags && this._testCase.tags.length > 0) { return [ { type: types_1.AttachmentType.Tags, list: this._testCase.tags.map((tag) => ({ title: tag.slice(1), dynamic: false })) } ]; } return []; } } exports.PlaywrightTestResultAdapter = PlaywrightTestResultAdapter; //# sourceMappingURL=playwright.js.map