Skip to content

Commit

Permalink
chore: Add support for before and after each hooks
Browse files Browse the repository at this point in the history
  • Loading branch information
sauravdas1997 committed Feb 2, 2024
1 parent 94b3b99 commit 3befdcc
Show file tree
Hide file tree
Showing 3 changed files with 166 additions and 1 deletion.
4 changes: 3 additions & 1 deletion nightwatch/globals.js
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ module.exports = {
}
});

eventBroadcaster.on('TestStepStarted', (args) => {
eventBroadcaster.on('TestStepStarted', async (args) => {
if (!helper.isTestObservabilitySession()) {
return;
}
Expand All @@ -166,6 +166,7 @@ module.exports = {
const pickleData = reportData.pickle.find((pickle) => pickle.id === pickleId);
const testSteps = reportData.testCases.find((testCase) => testCase.id === testCaseId).testSteps;
const testStepId = reportData.testStepStarted[args.envelope.testCaseStartedId].testStepId;
await testObservability.sendHook(args, 'HookRunStarted', testSteps, testStepId, _tests[testCaseId]);
const pickleStepId = testSteps.find((testStep) => testStep.id === testStepId).pickleStepId;
if (pickleStepId && _tests[testCaseId]?.['testStepId'] !== testStepId) {
_tests[testCaseId]['testStepId'] = testStepId;
Expand Down Expand Up @@ -200,6 +201,7 @@ module.exports = {
const pickleData = reportData.pickle.find((pickle) => pickle.id === pickleId);
const testSteps = reportData.testCases.find((testCase) => testCase.id === testCaseId).testSteps;
const testStepId = reportData.testStepFinished[args.envelope.testCaseStartedId].testStepId;
await testObservability.sendHook(args, 'HookRunFinished', testSteps, testStepId, _tests[testCaseId]);
const pickleStepId = testSteps.find((testStep) => testStep.id === testStepId).pickleStepId;
let failure;
let failureType;
Expand Down
144 changes: 144 additions & 0 deletions src/testObservability.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ const {makeRequest} = require('./utils/requestHelper');
const CrashReporter = require('./utils/crashReporter');
const Logger = require('./utils/logger');
const {API_URL} = require('./utils/constants');
const hooksMap = {};

class TestObservability {
configure(settings = {}) {
Expand Down Expand Up @@ -469,6 +470,14 @@ class TestObservability {
}
}

if (eventType === 'TestRunFinished') {
const hooksList = this.getHooksListForTest(args);
if (hooksList && hooksList.length > 0) {
testData.hooks = hooksList;
this.updateTestStatus(args, testData);
}
}

const uploadData = {
event_type: eventType,
test_run: testData
Expand All @@ -477,6 +486,141 @@ class TestObservability {

}

updateTestStatus(args, testData) {
const testCaseStartedId = args.envelope.testCaseStartedId;
const hookList = hooksMap[testCaseStartedId];
if (hookList instanceof Array) {
for (const hook of hookList) {
if (hook.result === 'failed') {
testData.result = hook.result;
testData.failure = hook.failure_data;
testData.failure_reason = (hook.failure_data instanceof Array) ? hook.failure_data[0]?.backtrace.join('\n') : '';
testData.failure_type = hook.failure_type;

return testData;
}
}
};
}

getHooksListForTest(args) {
const testCaseStartedId = args.envelope.testCaseStartedId;
const hookList = [];
hooksMap[testCaseStartedId].map(hookDetail => hookList.push(hookDetail.uuid));

return hookList;
}

getHookRunEventData(args, eventType, hookData, testMetaData, hookType) {
if (eventType === 'HookRunFinished') {
const finishedAt = new Date().toISOString();
const testCaseStartedId = args.envelope.testCaseStartedId;
const hookList = hooksMap[testCaseStartedId];
if (!hookList) {
return;
}

const hookEventData = hookList.find(hook => hook.uuid === hookData.id);
if (!hookEventData) {
return;
}
const result = this.getHookResult(args);
hookEventData.result = result.status;
hookEventData.finished_at = finishedAt;
hookEventData.failure_type = result.failureType;
hookEventData.failure_data = [{backtrace: result.failureData}];

return hookEventData;
}
const hookDetails = args.report.hooks.find(hookDetail => hookDetail.id === hookData.hookId);
const relativeFilePath = hookDetails?.sourceReference?.uri;
if (!relativeFilePath) {
return;
} else if (relativeFilePath.includes('setup_cucumber_runner')) {
return;
}
const startedAt = new Date().toISOString();
const result = 'pending';
const hookTagsList = hookDetails.tagExpression ? hookDetails.tagExpression.split(' ').filter(val => val.includes('@')) : null;

const hookEventData = {
uuid: hookData.id,
type: 'hook',
hook_type: hookType,
name: hookDetails?.name || '',
body: {
lang: 'NodeJs',
code: null
},
tags: hookTagsList,
scope: testMetaData?.feature?.name,
scopes: [testMetaData?.feature?.name || ''],
file_name: relativeFilePath,
location: relativeFilePath,
vc_filepath: (this._gitMetadata && this._gitMetadata.root) ? path.relative(this._gitMetadata.root, relativeFilePath) : null,
result: result.status,
started_at: startedAt,
framework: 'nightwatch'
};

return hookEventData;
}

async sendHook(args, eventType, testSteps, testStepId, testMetaData) {
const hookData = testSteps.find((testStep) => testStep.id === testStepId);
if (!hookData.hookId) {
return;
}
const testCaseStartedId = args.envelope.testCaseStartedId;
const hookType = this.getCucumberHookType(testSteps, hookData);
const hookRunEvent = this.getHookRunEventData(args, eventType, hookData, testMetaData, hookType);
if (!hookRunEvent) {
return;
}
if (eventType === 'HookRunStarted') {
if (hooksMap[testCaseStartedId]) {
hooksMap[testCaseStartedId].push(hookRunEvent);
} else {
hooksMap[testCaseStartedId] = [hookRunEvent];
}
}
const hookEventUploadData = {
event_type: eventType,
hook_run: hookRunEvent
};
await helper.uploadEventData(hookEventUploadData);
}

getHookResult(args) {
const testCaseStartedId = args.envelope.testCaseStartedId;
const hookResult = args.report.testStepFinished[testCaseStartedId].testStepResult;
let failure;
let failureType;
if (hookResult?.status.toString().toLowerCase() === 'failed') {
failure = (hookResult?.exception === undefined) ? hookResult?.message : hookResult?.exception?.message;
failureType = (hookResult?.exception === undefined) ? 'UnhandledError' : hookResult?.message.match(/Assert/) ? 'AssertionError' : 'UnhandledError';
}

return {
status: hookResult.status.toLowerCase(),
failureType: failureType || null,
failureData: (!failure) ? null : [failure]
};
}

// BEFORE_ALL and AFTER_ALL are not implemented for TO
getCucumberHookType(testSteps, hookData) {
let isStep = false;
for (const step of testSteps) {
if (step.pickleStepId) {
isStep = true;
}
if (hookData.id === step.id) {
return (isStep) ? 'AFTER_EACH' : 'BEFORE_EACH';
}
}
}

async appendTestItemLog (log, testUuid) {
try {
if (testUuid) {
Expand Down
19 changes: 19 additions & 0 deletions src/utils/helper.js
Original file line number Diff line number Diff line change
Expand Up @@ -732,3 +732,22 @@ exports.storeSessionsData = (data) => {
});
}
};

exports.deepClone = (obj) => {
if (obj === null || typeof obj !== 'object') {
return obj;
}

if (Array.isArray(obj)) {
return obj.map(exports.deepClone);
}

const cloned = {};
for (const key in obj) {
if (Object.prototype.hasOwnProperty.call(obj, key)) {
cloned[key] = exports.deepClone(obj[key]);
}
}

return cloned;
};

0 comments on commit 3befdcc

Please sign in to comment.