/**
* Copyright (c) 2020-present, Goldman Sachs
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { useState, useCallback, forwardRef } from 'react';
import { observer } from 'mobx-react-lite';
import { useDrop } from 'react-dnd';
import {
type DEPRECATED__MappingTestState,
MAPPING_TEST_EDITOR_TAB_TYPE,
TEST_RESULT,
} from '../../../../stores/editor/editor-state/element-editor-state/mapping/legacy/DEPRECATED__MappingTestState.js';
import {
clsx,
ContextMenu,
ProgressBar,
PlayIcon,
EmptyCircleIcon,
TimesCircleIcon,
CheckCircleIcon,
CircleNotchIcon,
PlusIcon,
EmptyStopCircleIcon,
ExclamationCircleIcon,
PauseCircleIcon,
PanelDropZone,
MenuContent,
MenuContentItem,
Panel,
WarningIcon,
} from '@finos/legend-art';
import {
type MappingElementDragSource,
CORE_DND_TYPE,
} from '../../../../stores/editor/utils/DnDUtils.js';
import { ClassMappingSelectorModal } from './MappingExecutionBuilder.js';
import { flowResult } from 'mobx';
import { useEditorStore } from '../../EditorStoreProvider.js';
import { useApplicationStore } from '@finos/legend-application';
import { SetImplementation } from '@finos/legend-graph';
import { MappingEditorState } from '../../../../stores/editor/editor-state/element-editor-state/mapping/MappingEditorState.js';
export const MappingTestExplorerContextMenu = observer(
forwardRef<
HTMLDivElement,
{
mappingTestState?: DEPRECATED__MappingTestState;
showCreateNewTestModal?: () => void;
isReadOnly: boolean;
}
>(function MappingTestExplorerContextMenu(props, ref) {
const { mappingTestState, isReadOnly, showCreateNewTestModal } = props;
const applicationStore = useApplicationStore();
const runMappingTest = (): void => {
if (mappingTestState) {
flowResult(mappingTestState.runTest()).catch(
applicationStore.alertUnhandledError,
);
}
};
const removeMappingTest = (): void => {
if (mappingTestState) {
flowResult(
mappingTestState.mappingEditorState.deleteTest(mappingTestState.test),
).catch(applicationStore.alertUnhandledError);
}
};
const toggleSkipTest = (): void => mappingTestState?.toggleSkipTest();
const viewTestResult = (): void => {
if (mappingTestState) {
mappingTestState.mappingEditorState.openTest(
mappingTestState.test,
MAPPING_TEST_EDITOR_TAB_TYPE.RESULT,
);
}
};
const editTest = (): void => {
if (mappingTestState) {
mappingTestState.mappingEditorState.openTest(
mappingTestState.test,
MAPPING_TEST_EDITOR_TAB_TYPE.SETUP,
);
}
};
return (
{mappingTestState && (
Run
)}
{mappingTestState && mappingTestState.result !== TEST_RESULT.NONE && (
View Result
)}
{mappingTestState && (
Edit
)}
{mappingTestState && (
{mappingTestState.isSkipped ? 'Unskip' : 'Skip'}
)}
{!isReadOnly && mappingTestState && (
Delete
)}
{!isReadOnly && !mappingTestState && showCreateNewTestModal && (
Create new test
)}
);
}),
);
export const MappingTestStatusIndicator: React.FC<{
testState: DEPRECATED__MappingTestState;
}> = (props) => {
const { testState } = props;
if (testState.isSkipped) {
return (
);
}
if (testState.isRunningTest) {
return (
);
}
return (
<>
{testState.result === TEST_RESULT.NONE && (
)}
{testState.result === TEST_RESULT.ERROR && (
)}
{testState.result === TEST_RESULT.FAILED && (
)}
{testState.result === TEST_RESULT.PASSED && (
)}
>
);
};
export const MappingTestExplorer = observer(
(props: { testState: DEPRECATED__MappingTestState; isReadOnly: boolean }) => {
const { isReadOnly, testState } = props;
const editorStore = useEditorStore();
const applicationStore = useApplicationStore();
const mappingEditorState =
editorStore.tabManagerState.getCurrentEditorState(MappingEditorState);
const openTest = applicationStore.guardUnhandledError(() =>
flowResult(mappingEditorState.openTest(testState.test)),
);
const runTest = applicationStore.guardUnhandledError(() =>
flowResult(testState.runTest()),
);
const cancelTest = (): void => {
testState.setIsRunningTest(false);
testState.setTestRunPromise(undefined);
};
const [isSelectedFromContextMenu, setIsSelectedFromContextMenu] =
useState(false);
const onContextMenuOpen = (): void => setIsSelectedFromContextMenu(true);
const onContextMenuClose = (): void => setIsSelectedFromContextMenu(false);
const isActive = mappingEditorState.currentTabState === testState;
return (
}
menuProps={{ elevation: 7 }}
onOpen={onContextMenuOpen}
onClose={onContextMenuClose}
>
{testState.isRunningTest ? (
) : (
)}
);
},
);
export const MappingTestsExplorer = observer(
(props: { isReadOnly: boolean }) => {
const { isReadOnly } = props;
const editorStore = useEditorStore();
const applicationStore = useApplicationStore();
const mappingEditorState =
editorStore.tabManagerState.getCurrentEditorState(MappingEditorState);
const runAllTests = applicationStore.guardUnhandledError(() =>
flowResult(mappingEditorState.runTests()),
);
// all test run report summary
const numberOfTests =
mappingEditorState.DEPRECATED_mappingTestStates.length;
const numberOfTestsPassed =
mappingEditorState.DEPRECATED_mappingTestStates.filter(
(testState) => testState.result === TEST_RESULT.PASSED,
).length;
const numberOfTestsFailed =
mappingEditorState.DEPRECATED_mappingTestStates.filter(
(testState) =>
testState.result === TEST_RESULT.FAILED ||
testState.result === TEST_RESULT.ERROR,
).length;
const numberOfTestSkipped =
mappingEditorState.DEPRECATED_mappingTestStates.filter(
(testState) => testState.isSkipped,
).length;
const percentageTestRun = Math.floor(
(mappingEditorState.DEPRECATED_mappingTestStates.filter(
(testState) => testState.result !== TEST_RESULT.NONE,
).length /
numberOfTests) *
100,
);
let testReportSummary = '';
switch (mappingEditorState.testSuiteResult) {
case TEST_RESULT.NONE:
testReportSummary = mappingEditorState.isRunningAllTests
? `Running ${numberOfTests} tests...`
: 'No tests run!';
break;
case TEST_RESULT.FAILED:
testReportSummary = `${numberOfTestsFailed} of ${numberOfTests} failed`;
break;
case TEST_RESULT.PASSED:
testReportSummary =
numberOfTestsPassed === numberOfTests
? `All ${numberOfTests} passed`
: `${numberOfTestsPassed} of ${numberOfTests} passed`;
break;
default:
break;
}
// Drag and Drop
const handleDrop = useCallback(
(item: MappingElementDragSource): void => {
if (isReadOnly) {
return;
}
if (item.data instanceof SetImplementation) {
flowResult(mappingEditorState.createNewTest(item.data)).catch(
applicationStore.alertUnhandledError,
);
}
},
[applicationStore.alertUnhandledError, isReadOnly, mappingEditorState],
);
const [{ isDragOver }, dropConnector] = useDrop<
MappingElementDragSource,
void,
{ isDragOver: boolean }
>(
() => ({
accept: CORE_DND_TYPE.MAPPING_EXPLORER_CLASS_MAPPING,
drop: (item) => handleDrop(item),
collect: (monitor) => ({
isDragOver: monitor.isOver({ shallow: true }),
}),
}),
[handleDrop],
);
// Class mapping selector
const [openClassMappingSelectorModal, setOpenClassMappingSelectorModal] =
useState(false);
const openMigrationtool = (): void =>
mappingEditorState.openMigrationTool();
const showClassMappingSelectorModal = (): void =>
setOpenClassMappingSelectorModal(true);
const hideClassMappingSelectorModal = (): void =>
setOpenClassMappingSelectorModal(false);
const changeClassMapping = useCallback(
(setImplementation: SetImplementation | undefined): void => {
if (setImplementation) {
flowResult(mappingEditorState.createNewTest(setImplementation)).catch(
applicationStore.alertUnhandledError,
);
hideClassMappingSelectorModal();
}
},
[applicationStore, mappingEditorState],
);
return (
{numberOfTests} total
{numberOfTestsPassed} passed
{numberOfTestsFailed} failed
{Boolean(numberOfTestSkipped) && (
{numberOfTestSkipped} skipped
)}
{mappingEditorState.testSuiteResult !== TEST_RESULT.NONE && (
{mappingEditorState.allTestRunTime}ms
)}
{Boolean(mappingEditorState.mapping.test.length) && (
)}
}
menuProps={{ elevation: 7 }}
>
{Boolean(
mappingEditorState.DEPRECATED_mappingTestStates.length,
) &&
mappingEditorState.DEPRECATED_mappingTestStates.slice().map(
(testState) => (
),
)}
{openClassMappingSelectorModal && (
)}
);
},
);