make the test case results errors work with WorkflowTaskException and use in ErrorForDisplay
This commit is contained in:
parent
4e359e6574
commit
ed42c6c399
|
@ -2,12 +2,14 @@ import glob
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
|
import traceback
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from typing import Any
|
from typing import Any
|
||||||
from typing import Callable
|
from typing import Callable
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
from lxml import etree # type: ignore
|
from lxml import etree # type: ignore
|
||||||
|
from SpiffWorkflow.bpmn.exceptions import WorkflowTaskException # type: ignore
|
||||||
from SpiffWorkflow.bpmn.workflow import BpmnWorkflow # type: ignore
|
from SpiffWorkflow.bpmn.workflow import BpmnWorkflow # type: ignore
|
||||||
from SpiffWorkflow.task import Task as SpiffTask # type: ignore
|
from SpiffWorkflow.task import Task as SpiffTask # type: ignore
|
||||||
from SpiffWorkflow.task import TaskState
|
from SpiffWorkflow.task import TaskState
|
||||||
|
@ -43,12 +45,23 @@ class MissingInputTaskData(Exception):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class TestCaseErrorDetails:
|
||||||
|
error_messages: list[str]
|
||||||
|
task_error_line: Optional[str] = None
|
||||||
|
task_trace: Optional[list[str]] = None
|
||||||
|
task_bpmn_identifier: Optional[str] = None
|
||||||
|
task_bpmn_name: Optional[str] = None
|
||||||
|
task_line_number: Optional[int] = None
|
||||||
|
stacktrace: Optional[list[str]] = None
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class TestCaseResult:
|
class TestCaseResult:
|
||||||
passed: bool
|
passed: bool
|
||||||
bpmn_file: str
|
bpmn_file: str
|
||||||
test_case_identifier: str
|
test_case_identifier: str
|
||||||
error_messages: Optional[list[str]] = None
|
test_case_error_details: Optional[TestCaseErrorDetails] = None
|
||||||
|
|
||||||
|
|
||||||
DEFAULT_NSMAP = {
|
DEFAULT_NSMAP = {
|
||||||
|
@ -127,8 +140,8 @@ class ProcessModelTestRunner:
|
||||||
formatted_tests = ["FAILING TESTS:"]
|
formatted_tests = ["FAILING TESTS:"]
|
||||||
for failing_test in self.failing_tests():
|
for failing_test in self.failing_tests():
|
||||||
msg = ""
|
msg = ""
|
||||||
if failing_test.error_messages:
|
if failing_test.test_case_error_details is not None:
|
||||||
msg = "\n\t\t".join(failing_test.error_messages)
|
msg = "\n\t\t".join(failing_test.test_case_error_details.error_messages)
|
||||||
formatted_tests.append(f"\t{failing_test.bpmn_file}: {failing_test.test_case_identifier}: {msg}")
|
formatted_tests.append(f"\t{failing_test.bpmn_file}: {failing_test.test_case_identifier}: {msg}")
|
||||||
return "\n".join(formatted_tests)
|
return "\n".join(formatted_tests)
|
||||||
|
|
||||||
|
@ -147,8 +160,7 @@ class ProcessModelTestRunner:
|
||||||
try:
|
try:
|
||||||
self.run_test_case(bpmn_file, test_case_identifier, test_case_contents)
|
self.run_test_case(bpmn_file, test_case_identifier, test_case_contents)
|
||||||
except Exception as ex:
|
except Exception as ex:
|
||||||
ex_as_array = str(ex).split("\n")
|
self._add_test_result(False, bpmn_file, test_case_identifier, exception=ex)
|
||||||
self._add_test_result(False, bpmn_file, test_case_identifier, ex_as_array)
|
|
||||||
|
|
||||||
def run_test_case(self, bpmn_file: str, test_case_identifier: str, test_case_contents: dict) -> None:
|
def run_test_case(self, bpmn_file: str, test_case_identifier: str, test_case_contents: dict) -> None:
|
||||||
bpmn_process_instance = self._instantiate_executer(bpmn_file)
|
bpmn_process_instance = self._instantiate_executer(bpmn_file)
|
||||||
|
@ -329,15 +341,40 @@ class ProcessModelTestRunner:
|
||||||
def _get_relative_path_of_bpmn_file(self, bpmn_file: str) -> str:
|
def _get_relative_path_of_bpmn_file(self, bpmn_file: str) -> str:
|
||||||
return os.path.relpath(bpmn_file, start=self.process_model_directory_path)
|
return os.path.relpath(bpmn_file, start=self.process_model_directory_path)
|
||||||
|
|
||||||
|
def _exception_to_test_case_error_details(self, exception: Exception) -> TestCaseErrorDetails:
|
||||||
|
error_messages = str(exception).split("\n")
|
||||||
|
test_case_error_details = TestCaseErrorDetails(error_messages=error_messages)
|
||||||
|
if isinstance(exception, WorkflowTaskException):
|
||||||
|
test_case_error_details.task_error_line = exception.error_line
|
||||||
|
test_case_error_details.task_trace = exception.task_trace
|
||||||
|
test_case_error_details.task_line_number = exception.line_number
|
||||||
|
test_case_error_details.task_bpmn_identifier = exception.task_spec.bpmn_id
|
||||||
|
test_case_error_details.task_bpmn_name = exception.task_spec.bpmn_name
|
||||||
|
else:
|
||||||
|
test_case_error_details.stacktrace = traceback.format_exc().split("\n")
|
||||||
|
|
||||||
|
return test_case_error_details
|
||||||
|
|
||||||
def _add_test_result(
|
def _add_test_result(
|
||||||
self, passed: bool, bpmn_file: str, test_case_identifier: str, error_messages: Optional[list[str]] = None
|
self,
|
||||||
|
passed: bool,
|
||||||
|
bpmn_file: str,
|
||||||
|
test_case_identifier: str,
|
||||||
|
error_messages: Optional[list[str]] = None,
|
||||||
|
exception: Optional[Exception] = None,
|
||||||
) -> None:
|
) -> None:
|
||||||
|
test_case_error_details = None
|
||||||
|
if exception is not None:
|
||||||
|
test_case_error_details = self._exception_to_test_case_error_details(exception)
|
||||||
|
elif error_messages:
|
||||||
|
test_case_error_details = TestCaseErrorDetails(error_messages=error_messages)
|
||||||
|
|
||||||
bpmn_file_relative = self._get_relative_path_of_bpmn_file(bpmn_file)
|
bpmn_file_relative = self._get_relative_path_of_bpmn_file(bpmn_file)
|
||||||
test_result = TestCaseResult(
|
test_result = TestCaseResult(
|
||||||
passed=passed,
|
passed=passed,
|
||||||
bpmn_file=bpmn_file_relative,
|
bpmn_file=bpmn_file_relative,
|
||||||
test_case_identifier=test_case_identifier,
|
test_case_identifier=test_case_identifier,
|
||||||
error_messages=error_messages,
|
test_case_error_details=test_case_error_details,
|
||||||
)
|
)
|
||||||
self.test_case_results.append(test_result)
|
self.test_case_results.append(test_result)
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,7 @@ import {
|
||||||
ErrorForDisplay,
|
ErrorForDisplay,
|
||||||
ProcessInstanceEventErrorDetail,
|
ProcessInstanceEventErrorDetail,
|
||||||
ProcessInstanceLogEntry,
|
ProcessInstanceLogEntry,
|
||||||
|
TestCaseErrorDetails,
|
||||||
} from '../interfaces';
|
} from '../interfaces';
|
||||||
|
|
||||||
function errorDetailDisplay(
|
function errorDetailDisplay(
|
||||||
|
@ -40,6 +41,22 @@ export const errorForDisplayFromProcessInstanceErrorDetail = (
|
||||||
return errorForDisplay;
|
return errorForDisplay;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const errorForDisplayFromTestCaseErrorDetails = (
|
||||||
|
testCaseErrorDetails: TestCaseErrorDetails
|
||||||
|
) => {
|
||||||
|
const errorForDisplay: ErrorForDisplay = {
|
||||||
|
message: testCaseErrorDetails.error_messages.join('\n'),
|
||||||
|
messageClassName: 'failure-string',
|
||||||
|
task_name: testCaseErrorDetails.task_bpmn_name,
|
||||||
|
task_id: testCaseErrorDetails.task_bpmn_identifier,
|
||||||
|
line_number: testCaseErrorDetails.task_line_number,
|
||||||
|
error_line: testCaseErrorDetails.task_line_contents,
|
||||||
|
task_trace: testCaseErrorDetails.task_trace,
|
||||||
|
stacktrace: testCaseErrorDetails.stacktrace,
|
||||||
|
};
|
||||||
|
return errorForDisplay;
|
||||||
|
};
|
||||||
|
|
||||||
export const childrenForErrorObject = (errorObject: ErrorForDisplay) => {
|
export const childrenForErrorObject = (errorObject: ErrorForDisplay) => {
|
||||||
let sentryLinkTag = null;
|
let sentryLinkTag = null;
|
||||||
if (errorObject.sentry_link) {
|
if (errorObject.sentry_link) {
|
||||||
|
|
|
@ -3,19 +3,24 @@ import { Button, Modal } from '@carbon/react';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { useUriListForPermissions } from '../hooks/UriListForPermissions';
|
import { useUriListForPermissions } from '../hooks/UriListForPermissions';
|
||||||
import HttpService from '../services/HttpService';
|
import HttpService from '../services/HttpService';
|
||||||
import { ProcessFile } from '../interfaces';
|
import { ProcessFile, TestCaseResult, TestCaseResults } from '../interfaces';
|
||||||
|
import {
|
||||||
|
childrenForErrorObject,
|
||||||
|
errorForDisplayFromTestCaseErrorDetails,
|
||||||
|
} from './ErrorDisplay';
|
||||||
|
|
||||||
type OwnProps = {
|
type OwnProps = {
|
||||||
processModelFile: ProcessFile;
|
processModelFile: ProcessFile;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function ProcessModelTestRun({ processModelFile }: OwnProps) {
|
export default function ProcessModelTestRun({ processModelFile }: OwnProps) {
|
||||||
const [testCaseResults, setTestCaseResults] = useState<any>(null);
|
const [testCaseResults, setTestCaseResults] =
|
||||||
|
useState<TestCaseResults | null>(null);
|
||||||
const [showTestCaseResultsModal, setShowTestCaseResultsModal] =
|
const [showTestCaseResultsModal, setShowTestCaseResultsModal] =
|
||||||
useState<boolean>(false);
|
useState<boolean>(false);
|
||||||
const { targetUris } = useUriListForPermissions();
|
const { targetUris } = useUriListForPermissions();
|
||||||
|
|
||||||
const onProcessModelTestRunSuccess = (result: any) => {
|
const onProcessModelTestRunSuccess = (result: TestCaseResults) => {
|
||||||
setTestCaseResults(result);
|
setTestCaseResults(result);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -60,18 +65,80 @@ export default function ProcessModelTestRun({ processModelFile }: OwnProps) {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const testCaseFormattedResultTag = () => {
|
||||||
|
if (!testCaseResults) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const passingRows: any[] = [];
|
||||||
|
const failingRows: any[] = [];
|
||||||
|
|
||||||
|
testCaseResults.passing.forEach((testCaseResult: TestCaseResult) => {
|
||||||
|
passingRows.push(<p>{testCaseResult.test_case_identifier}</p>);
|
||||||
|
});
|
||||||
|
|
||||||
|
testCaseResults.failing
|
||||||
|
.slice(0, 2)
|
||||||
|
.forEach((testCaseResult: TestCaseResult) => {
|
||||||
|
if (testCaseResult.test_case_error_details) {
|
||||||
|
const errorForDisplay = errorForDisplayFromTestCaseErrorDetails(
|
||||||
|
testCaseResult.test_case_error_details
|
||||||
|
);
|
||||||
|
const errorChildren = childrenForErrorObject(errorForDisplay);
|
||||||
|
failingRows.push(
|
||||||
|
<>
|
||||||
|
<br />
|
||||||
|
<p>
|
||||||
|
Test Case:{' '}
|
||||||
|
<strong>{testCaseResult.test_case_identifier}</strong>
|
||||||
|
</p>
|
||||||
|
{errorChildren}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<p>Passing: {testCaseResults.passing.length}</p>
|
||||||
|
<p>Failing: {testCaseResults.failing.length}</p>
|
||||||
|
<br />
|
||||||
|
{failingRows.length > 0 ? (
|
||||||
|
<>
|
||||||
|
<p>Failure Details:</p>
|
||||||
|
{failingRows}
|
||||||
|
</>
|
||||||
|
) : null}
|
||||||
|
{passingRows.length > 0 ? (
|
||||||
|
<>
|
||||||
|
<p>Successful Test Cases:</p>
|
||||||
|
{passingRows}
|
||||||
|
</>
|
||||||
|
) : null}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
const testCaseResultsModal = () => {
|
const testCaseResultsModal = () => {
|
||||||
|
if (!testCaseResults) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
let modalHeading = 'All Tests PASSED';
|
||||||
|
if (!testCaseResults.all_passed) {
|
||||||
|
modalHeading = 'Some Tests FAILED';
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
open={showTestCaseResultsModal}
|
open={showTestCaseResultsModal}
|
||||||
data-qa="test-case-results-modal"
|
data-qa="test-case-results-modal"
|
||||||
modalHeading="RESULT FOR"
|
modalHeading={modalHeading}
|
||||||
modalLabel="LABLE"
|
modalLabel="Test Case Rsults"
|
||||||
primaryButtonText="OK"
|
primaryButtonText="OK"
|
||||||
onRequestSubmit={() => setShowTestCaseResultsModal(false)}
|
onRequestSubmit={() => setShowTestCaseResultsModal(false)}
|
||||||
onRequestClose={() => setShowTestCaseResultsModal(false)}
|
onRequestClose={() => setShowTestCaseResultsModal(false)}
|
||||||
>
|
>
|
||||||
{JSON.stringify(testCaseResults)}
|
{testCaseFormattedResultTag()}
|
||||||
</Modal>
|
</Modal>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -365,3 +365,26 @@ export interface InterstitialPageResponse {
|
||||||
task?: ProcessInstanceTask;
|
task?: ProcessInstanceTask;
|
||||||
process_instance?: ProcessInstance;
|
process_instance?: ProcessInstance;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface TestCaseErrorDetails {
|
||||||
|
error_messages: string[];
|
||||||
|
stacktrace?: string[];
|
||||||
|
task_bpmn_identifier?: string;
|
||||||
|
task_bpmn_name?: string;
|
||||||
|
task_line_contents?: string;
|
||||||
|
task_line_number?: number;
|
||||||
|
task_trace?: string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TestCaseResult {
|
||||||
|
bpmn_file: string;
|
||||||
|
passed: boolean;
|
||||||
|
test_case_identifier: string;
|
||||||
|
test_case_error_details?: TestCaseErrorDetails;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TestCaseResults {
|
||||||
|
all_passed: boolean;
|
||||||
|
failing: TestCaseResult[];
|
||||||
|
passing: TestCaseResult[];
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue