@finos/legend-application-studio 28.21.4 → 28.21.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (100) hide show
  1. package/lib/__lib__/LegendStudioEvent.d.ts +4 -1
  2. package/lib/__lib__/LegendStudioEvent.d.ts.map +1 -1
  3. package/lib/__lib__/LegendStudioEvent.js +3 -0
  4. package/lib/__lib__/LegendStudioEvent.js.map +1 -1
  5. package/lib/__lib__/LegendStudioTelemetryHelper.d.ts +2 -1
  6. package/lib/__lib__/LegendStudioTelemetryHelper.d.ts.map +1 -1
  7. package/lib/__lib__/LegendStudioTelemetryHelper.js +11 -3
  8. package/lib/__lib__/LegendStudioTelemetryHelper.js.map +1 -1
  9. package/lib/__lib__/LegendStudioUserDataHelper.d.ts +41 -1
  10. package/lib/__lib__/LegendStudioUserDataHelper.d.ts.map +1 -1
  11. package/lib/__lib__/LegendStudioUserDataHelper.js +120 -1
  12. package/lib/__lib__/LegendStudioUserDataHelper.js.map +1 -1
  13. package/lib/components/editor/editor-group/data-editor/EmbeddedDataEditor.d.ts +1 -1
  14. package/lib/components/editor/editor-group/data-editor/EmbeddedDataEditor.d.ts.map +1 -1
  15. package/lib/components/editor/editor-group/data-editor/EmbeddedDataEditor.js +3 -3
  16. package/lib/components/editor/editor-group/data-editor/EmbeddedDataEditor.js.map +1 -1
  17. package/lib/components/editor/editor-group/data-editor/RelationElementsDataEditor.d.ts +3 -0
  18. package/lib/components/editor/editor-group/data-editor/RelationElementsDataEditor.d.ts.map +1 -1
  19. package/lib/components/editor/editor-group/data-editor/RelationElementsDataEditor.js +13 -35
  20. package/lib/components/editor/editor-group/data-editor/RelationElementsDataEditor.js.map +1 -1
  21. package/lib/components/editor/editor-group/dataProduct/DataProductEditor.d.ts.map +1 -1
  22. package/lib/components/editor/editor-group/dataProduct/DataProductEditor.js +20 -7
  23. package/lib/components/editor/editor-group/dataProduct/DataProductEditor.js.map +1 -1
  24. package/lib/components/editor/editor-group/dataProduct/testable/DataProductTestableEditor.d.ts.map +1 -1
  25. package/lib/components/editor/editor-group/dataProduct/testable/DataProductTestableEditor.js +59 -22
  26. package/lib/components/editor/editor-group/dataProduct/testable/DataProductTestableEditor.js.map +1 -1
  27. package/lib/components/editor/editor-group/function-activator/testable/FunctionTestableEditor.d.ts.map +1 -1
  28. package/lib/components/editor/editor-group/function-activator/testable/FunctionTestableEditor.js +113 -75
  29. package/lib/components/editor/editor-group/function-activator/testable/FunctionTestableEditor.js.map +1 -1
  30. package/lib/components/editor/editor-group/testable/TestableSharedComponents.d.ts.map +1 -1
  31. package/lib/components/editor/editor-group/testable/TestableSharedComponents.js +2 -2
  32. package/lib/components/editor/editor-group/testable/TestableSharedComponents.js.map +1 -1
  33. package/lib/components/editor/side-bar/DevMetadataPanel.d.ts.map +1 -1
  34. package/lib/components/editor/side-bar/DevMetadataPanel.js +37 -6
  35. package/lib/components/editor/side-bar/DevMetadataPanel.js.map +1 -1
  36. package/lib/components/workspace-setup/RecentWorkspacesPanel.d.ts +22 -0
  37. package/lib/components/workspace-setup/RecentWorkspacesPanel.d.ts.map +1 -0
  38. package/lib/components/workspace-setup/RecentWorkspacesPanel.js +80 -0
  39. package/lib/components/workspace-setup/RecentWorkspacesPanel.js.map +1 -0
  40. package/lib/components/workspace-setup/WorkspaceSetup.d.ts.map +1 -1
  41. package/lib/components/workspace-setup/WorkspaceSetup.js +61 -6
  42. package/lib/components/workspace-setup/WorkspaceSetup.js.map +1 -1
  43. package/lib/index.css +2 -2
  44. package/lib/index.css.map +1 -1
  45. package/lib/package.json +1 -1
  46. package/lib/stores/editor/EditorStore.d.ts.map +1 -1
  47. package/lib/stores/editor/EditorStore.js +31 -0
  48. package/lib/stores/editor/EditorStore.js.map +1 -1
  49. package/lib/stores/editor/editor-state/element-editor-state/data/EmbeddedDataState.d.ts +1 -1
  50. package/lib/stores/editor/editor-state/element-editor-state/data/EmbeddedDataState.d.ts.map +1 -1
  51. package/lib/stores/editor/editor-state/element-editor-state/data/EmbeddedDataState.js +20 -48
  52. package/lib/stores/editor/editor-state/element-editor-state/data/EmbeddedDataState.js.map +1 -1
  53. package/lib/stores/editor/editor-state/element-editor-state/dataProduct/testable/DataProductTestableState.d.ts +9 -14
  54. package/lib/stores/editor/editor-state/element-editor-state/dataProduct/testable/DataProductTestableState.d.ts.map +1 -1
  55. package/lib/stores/editor/editor-state/element-editor-state/dataProduct/testable/DataProductTestableState.js +125 -78
  56. package/lib/stores/editor/editor-state/element-editor-state/dataProduct/testable/DataProductTestableState.js.map +1 -1
  57. package/lib/stores/editor/editor-state/element-editor-state/function-activator/testable/FunctionTestableState.d.ts +18 -4
  58. package/lib/stores/editor/editor-state/element-editor-state/function-activator/testable/FunctionTestableState.d.ts.map +1 -1
  59. package/lib/stores/editor/editor-state/element-editor-state/function-activator/testable/FunctionTestableState.js +216 -53
  60. package/lib/stores/editor/editor-state/element-editor-state/function-activator/testable/FunctionTestableState.js.map +1 -1
  61. package/lib/stores/editor/sidebar-state/ProjectOverviewState.d.ts.map +1 -1
  62. package/lib/stores/editor/sidebar-state/ProjectOverviewState.js +11 -0
  63. package/lib/stores/editor/sidebar-state/ProjectOverviewState.js.map +1 -1
  64. package/lib/stores/editor/sidebar-state/WorkspaceReviewState.d.ts.map +1 -1
  65. package/lib/stores/editor/sidebar-state/WorkspaceReviewState.js +11 -0
  66. package/lib/stores/editor/sidebar-state/WorkspaceReviewState.js.map +1 -1
  67. package/lib/stores/editor/sidebar-state/dev-metadata/DevMetadataState.d.ts +9 -0
  68. package/lib/stores/editor/sidebar-state/dev-metadata/DevMetadataState.d.ts.map +1 -1
  69. package/lib/stores/editor/sidebar-state/dev-metadata/DevMetadataState.js +57 -1
  70. package/lib/stores/editor/sidebar-state/dev-metadata/DevMetadataState.js.map +1 -1
  71. package/lib/stores/project-reviewer/ProjectReviewerStore.d.ts.map +1 -1
  72. package/lib/stores/project-reviewer/ProjectReviewerStore.js +12 -0
  73. package/lib/stores/project-reviewer/ProjectReviewerStore.js.map +1 -1
  74. package/lib/stores/workspace-setup/WorkspaceSetupStore.d.ts +17 -0
  75. package/lib/stores/workspace-setup/WorkspaceSetupStore.d.ts.map +1 -1
  76. package/lib/stores/workspace-setup/WorkspaceSetupStore.js +61 -0
  77. package/lib/stores/workspace-setup/WorkspaceSetupStore.js.map +1 -1
  78. package/package.json +16 -16
  79. package/src/__lib__/LegendStudioEvent.ts +3 -0
  80. package/src/__lib__/LegendStudioTelemetryHelper.ts +35 -11
  81. package/src/__lib__/LegendStudioUserDataHelper.ts +204 -1
  82. package/src/components/editor/editor-group/data-editor/EmbeddedDataEditor.tsx +4 -0
  83. package/src/components/editor/editor-group/data-editor/RelationElementsDataEditor.tsx +209 -187
  84. package/src/components/editor/editor-group/dataProduct/DataProductEditor.tsx +26 -7
  85. package/src/components/editor/editor-group/dataProduct/testable/DataProductTestableEditor.tsx +149 -86
  86. package/src/components/editor/editor-group/function-activator/testable/FunctionTestableEditor.tsx +425 -308
  87. package/src/components/editor/editor-group/testable/TestableSharedComponents.tsx +3 -11
  88. package/src/components/editor/side-bar/DevMetadataPanel.tsx +194 -10
  89. package/src/components/workspace-setup/RecentWorkspacesPanel.tsx +161 -0
  90. package/src/components/workspace-setup/WorkspaceSetup.tsx +97 -8
  91. package/src/stores/editor/EditorStore.ts +44 -0
  92. package/src/stores/editor/editor-state/element-editor-state/data/EmbeddedDataState.ts +28 -50
  93. package/src/stores/editor/editor-state/element-editor-state/dataProduct/testable/DataProductTestableState.ts +164 -100
  94. package/src/stores/editor/editor-state/element-editor-state/function-activator/testable/FunctionTestableState.ts +307 -72
  95. package/src/stores/editor/sidebar-state/ProjectOverviewState.ts +14 -0
  96. package/src/stores/editor/sidebar-state/WorkspaceReviewState.ts +14 -0
  97. package/src/stores/editor/sidebar-state/dev-metadata/DevMetadataState.ts +84 -1
  98. package/src/stores/project-reviewer/ProjectReviewerStore.ts +15 -0
  99. package/src/stores/workspace-setup/WorkspaceSetupStore.ts +93 -0
  100. package/tsconfig.json +1 -0
@@ -17,9 +17,11 @@
17
17
  import { observer } from 'mobx-react-lite';
18
18
  import {
19
19
  BlankPanelPlaceholder,
20
+ BlankPanelContent,
20
21
  ContextMenu,
21
22
  CustomSelectorInput,
22
23
  Dialog,
24
+ ErrorWarnIcon,
23
25
  FilledWindowMaximizeIcon,
24
26
  MenuContent,
25
27
  MenuContentItem,
@@ -29,8 +31,11 @@ import {
29
31
  ModalFooterButton,
30
32
  ModalHeader,
31
33
  ModalTitle,
32
- PanelContent,
34
+ Panel,
33
35
  PanelFormTextField,
36
+ PanelHeader,
37
+ PanelHeaderActionItem,
38
+ PanelHeaderActions,
34
39
  PlayIcon,
35
40
  PlusIcon,
36
41
  RefreshIcon,
@@ -68,7 +73,6 @@ import {
68
73
  import {
69
74
  TESTABLE_RESULT,
70
75
  getTestableResultFromTestResult,
71
- getTestableResultFromTestResults,
72
76
  } from '../../../../../stores/editor/sidebar-state/testable/GlobalTestRunnerState.js';
73
77
  import { flowResult } from 'mobx';
74
78
  import { getTestableResultIcon } from '../../../side-bar/testable/GlobalTestRunner.js';
@@ -131,97 +135,6 @@ const FunctionTestableContextMenu = observer(
131
135
  }),
132
136
  );
133
137
 
134
- const FunctionTestSuiteItem = observer(
135
- (props: {
136
- suite: FunctionTestSuite;
137
- functionTestableState: FunctionTestableState;
138
- }) => {
139
- const { suite, functionTestableState } = props;
140
- const [isSelectedFromContextMenu, setIsSelectedFromContextMenu] =
141
- useState(false);
142
- const isReadOnly = functionTestableState.functionEditorState.isReadOnly;
143
- const openSuite = (): void => functionTestableState.changeSuite(suite);
144
- const results = functionTestableState.testableResults?.filter(
145
- (t) => t.parentSuite?.id === suite.id,
146
- );
147
- const isRunning =
148
- functionTestableState.isRunningTestableSuitesState.isInProgress ||
149
- (functionTestableState.isRunningFailingSuitesState.isInProgress &&
150
- functionTestableState.failingSuites.includes(suite)) ||
151
- functionTestableState.runningSuite === suite;
152
- const isActive = functionTestableState.selectedTestSuite?.suite === suite;
153
- const _testableResult = getTestableResultFromTestResults(results);
154
- const testableResult = isRunning
155
- ? TESTABLE_RESULT.IN_PROGRESS
156
- : _testableResult;
157
- const resultIcon = getTestableResultIcon(testableResult);
158
- const onContextMenuOpen = (): void => setIsSelectedFromContextMenu(true);
159
- const onContextMenuClose = (): void => setIsSelectedFromContextMenu(false);
160
- const add = (): void => {
161
- // TODO
162
- };
163
- const _delete = (): void => {
164
- functionTestableState.deleteTestSuite(suite);
165
- };
166
- const rename = (): void => {
167
- functionTestableState.setRenameComponent(suite);
168
- };
169
- const runSuite = (): void => {
170
- flowResult(functionTestableState.runSuite(suite)).catch(
171
- functionTestableState.editorStore.applicationStore.alertUnhandledError,
172
- );
173
- };
174
- return (
175
- <ContextMenu
176
- className={clsx(
177
- 'testable-test-explorer__item',
178
- {
179
- 'testable-test-explorer__item--selected-from-context-menu':
180
- !isActive && isSelectedFromContextMenu,
181
- },
182
- { 'testable-test-explorer__item--active': isActive },
183
- )}
184
- disabled={isReadOnly}
185
- content={
186
- <FunctionTestableContextMenu
187
- addName="Suite"
188
- add={add}
189
- _delete={_delete}
190
- rename={rename}
191
- />
192
- }
193
- menuProps={{ elevation: 7 }}
194
- onOpen={onContextMenuOpen}
195
- onClose={onContextMenuClose}
196
- >
197
- <div
198
- className={clsx('testable-test-explorer__item__label')}
199
- onClick={openSuite}
200
- tabIndex={-1}
201
- >
202
- <div className="testable-test-explorer__item__label__icon">
203
- {resultIcon}
204
- </div>
205
- <div className="testable-test-explorer__item__label__text">
206
- {suite.id}
207
- </div>
208
- <div className="mapping-test-explorer__item__actions">
209
- <button
210
- className="mapping-test-explorer__item__action mapping-test-explorer__run-test-btn"
211
- onClick={runSuite}
212
- disabled={isRunning}
213
- tabIndex={-1}
214
- title={`Run ${suite.id}`}
215
- >
216
- {<PlayIcon />}
217
- </button>
218
- </div>
219
- </div>
220
- </ContextMenu>
221
- );
222
- },
223
- );
224
-
225
138
  const FunctionTestDataStateEditor = observer(
226
139
  (props: {
227
140
  functionTestSuiteState: FunctionTestSuiteState;
@@ -813,8 +726,11 @@ const CreateTestModal = observer(
813
726
  const close = (): void => functionSuiteState.setShowModal(false);
814
727
  const create = (): void => {
815
728
  if (id) {
816
- functionSuiteState.addNewTest(id);
817
- close();
729
+ flowResult(functionSuiteState.addNewTest(id))
730
+ .then(() => close())
731
+ .catch(
732
+ functionSuiteState.editorStore.applicationStore.alertUnhandledError,
733
+ );
818
734
  }
819
735
  };
820
736
 
@@ -859,11 +775,215 @@ const CreateTestModal = observer(
859
775
  );
860
776
  },
861
777
  );
862
- const FunctionTestSuiteEditorInner = observer(
778
+ // ──────────────────────────────────────────────────────────────────────────────
779
+ // Add Element Modal
780
+ // ──────────────────────────────────────────────────────────────────────────────
781
+
782
+ const AddDataElementModal = observer(
783
+ (props: { functionTestSuiteState: FunctionTestSuiteState }) => {
784
+ const { functionTestSuiteState } = props;
785
+ const dataState = functionTestSuiteState.dataState;
786
+ const applicationStore =
787
+ functionTestSuiteState.editorStore.applicationStore;
788
+ const options = dataState.availableElementsToAdd.map((e) => ({
789
+ value: e.path,
790
+ label: e.path,
791
+ }));
792
+ const [selectedPath, setSelectedPath] = useState<string | undefined>(
793
+ options[0]?.value,
794
+ );
795
+ const close = (): void => dataState.setShowAddElementModal(false);
796
+ const add = (): void => {
797
+ if (selectedPath) {
798
+ dataState.addDataElement(selectedPath);
799
+ close();
800
+ }
801
+ };
802
+ const onChange = (val: { label: string; value: string } | null): void => {
803
+ setSelectedPath(val?.value);
804
+ };
805
+
806
+ return (
807
+ <Dialog
808
+ open={dataState.showAddElementModal}
809
+ onClose={close}
810
+ classes={{ container: 'search-modal__container' }}
811
+ slotProps={{
812
+ paper: {
813
+ classes: { root: 'search-modal__inner-container' },
814
+ },
815
+ }}
816
+ >
817
+ <Modal
818
+ darkMode={
819
+ !applicationStore.layoutService.TEMPORARY__isLightColorThemeEnabled
820
+ }
821
+ >
822
+ <ModalHeader>
823
+ <ModalTitle title="Add Element" />
824
+ </ModalHeader>
825
+ <ModalBody>
826
+ <CustomSelectorInput
827
+ className="panel__content__form__section__dropdown"
828
+ options={options}
829
+ onChange={onChange}
830
+ value={
831
+ selectedPath
832
+ ? { value: selectedPath, label: selectedPath }
833
+ : null
834
+ }
835
+ placeholder="Select element..."
836
+ darkMode={
837
+ !applicationStore.layoutService
838
+ .TEMPORARY__isLightColorThemeEnabled
839
+ }
840
+ />
841
+ </ModalBody>
842
+ <ModalFooter>
843
+ <ModalFooterButton
844
+ disabled={!selectedPath}
845
+ onClick={add}
846
+ text="Add"
847
+ />
848
+ <ModalFooterButton onClick={close} text="Close" type="secondary" />
849
+ </ModalFooter>
850
+ </Modal>
851
+ </Dialog>
852
+ );
853
+ },
854
+ );
855
+
856
+ // ──────────────────────────────────────────────────────────────────────────────
857
+ // Test Data Editor (left panel) — elements list + per-element data editor
858
+ // ──────────────────────────────────────────────────────────────────────────────
859
+
860
+ const FunctionTestDataPanel = observer(
861
+ (props: { functionTestSuiteState: FunctionTestSuiteState }) => {
862
+ const { functionTestSuiteState } = props;
863
+ const dataState = functionTestSuiteState.dataState;
864
+ const hasIngestOrDataProductAccessors =
865
+ functionTestSuiteState.functionTestableState
866
+ .resolvedIngestOrDataProductAccessors.length > 0;
867
+ const hasTestData = Boolean(dataState.dataHolder.testData?.length);
868
+
869
+ const addStoreTestData = (): void => {
870
+ if (!dataState.availableElementsToAdd.length) {
871
+ functionTestSuiteState.editorStore.applicationStore.notificationService.notifyWarning(
872
+ hasIngestOrDataProductAccessors
873
+ ? `All referenced elements' data is already present`
874
+ : `No elements available to add`,
875
+ );
876
+ return;
877
+ }
878
+ dataState.setShowAddElementModal(true);
879
+ };
880
+
881
+ return (
882
+ <div
883
+ className={clsx('service-test-data-editor panel', {
884
+ 'service-test-data-editor--no-data': !hasTestData,
885
+ })}
886
+ >
887
+ <div className="service-test-data-editor__data">
888
+ <ResizablePanelGroup orientation="vertical">
889
+ <ResizablePanel minSize={100} size={180}>
890
+ <div className="binding-editor__header">
891
+ <div className="binding-editor__header__title">
892
+ <div className="panel__header__title__content">Test Data</div>
893
+ </div>
894
+ <div className="panel__header__actions">
895
+ <button
896
+ className="panel__header__action"
897
+ tabIndex={-1}
898
+ onClick={addStoreTestData}
899
+ title="Add Element"
900
+ >
901
+ <PlusIcon />
902
+ </button>
903
+ </div>
904
+ </div>
905
+ {!hasTestData ? (
906
+ <div className="service-test-data-editor__warning">
907
+ <ErrorWarnIcon />
908
+ <span>Add an element to configure test data</span>
909
+ </div>
910
+ ) : (
911
+ <div>
912
+ {(dataState.dataHolder.testData ?? []).map((td) => (
913
+ <div
914
+ key={td.element.value.path}
915
+ className={clsx('testable-test-explorer__item', {
916
+ 'testable-test-explorer__item--active':
917
+ dataState.selectedDataState?.storeTestData === td,
918
+ })}
919
+ >
920
+ <div
921
+ className="testable-test-explorer__item__label"
922
+ onClick={(): void => dataState.openStoreTestData(td)}
923
+ tabIndex={-1}
924
+ >
925
+ <div className="testable-test-explorer__item__label__text">
926
+ {td.element.value.path}
927
+ </div>
928
+ <div className="mapping-test-explorer__item__actions">
929
+ <button
930
+ className="mapping-test-explorer__item__action"
931
+ onClick={(e): void => {
932
+ e.stopPropagation();
933
+ dataState.deleteStoreTestData(td);
934
+ }}
935
+ tabIndex={-1}
936
+ title="Delete"
937
+ >
938
+ <TimesIcon />
939
+ </button>
940
+ </div>
941
+ </div>
942
+ </div>
943
+ ))}
944
+ </div>
945
+ )}
946
+ </ResizablePanel>
947
+ <ResizablePanelSplitter>
948
+ <ResizablePanelSplitterLine color="var(--color-dark-grey-200)" />
949
+ </ResizablePanelSplitter>
950
+ <ResizablePanel minSize={200}>
951
+ {dataState.selectedDataState ? (
952
+ <FunctionTestDataStateEditor
953
+ functionTestSuiteState={functionTestSuiteState}
954
+ storeTestDataState={dataState.selectedDataState}
955
+ />
956
+ ) : (
957
+ <BlankPanelContent>
958
+ Select an element to configure its test data
959
+ </BlankPanelContent>
960
+ )}
961
+ </ResizablePanel>
962
+ </ResizablePanelGroup>
963
+ </div>
964
+ {dataState.showAddElementModal && (
965
+ <AddDataElementModal
966
+ functionTestSuiteState={functionTestSuiteState}
967
+ />
968
+ )}
969
+ </div>
970
+ );
971
+ },
972
+ );
973
+
974
+ // ──────────────────────────────────────────────────────────────────────────────
975
+ // Tests Editor (right panel) — tests list + test detail (assertion/setup)
976
+ // ──────────────────────────────────────────────────────────────────────────────
977
+
978
+ const FunctionTestsPanel = observer(
863
979
  (props: { functionTestSuiteState: FunctionTestSuiteState }) => {
864
980
  const { functionTestSuiteState } = props;
865
981
  const editorStore = functionTestSuiteState.editorStore;
982
+ const isReadOnly =
983
+ functionTestSuiteState.functionTestableState.functionEditorState
984
+ .isReadOnly;
866
985
  const selectedTestState = functionTestSuiteState.selectTestState;
986
+
867
987
  const addTest = (): void => {
868
988
  functionTestSuiteState.setShowModal(true);
869
989
  };
@@ -877,134 +997,160 @@ const FunctionTestSuiteEditorInner = observer(
877
997
  editorStore.applicationStore.alertUnhandledError,
878
998
  );
879
999
  };
880
- const renderFunctionTestEditor = (): React.ReactNode => {
881
- if (selectedTestState) {
882
- return <FunctionTestEditor functionTestState={selectedTestState} />;
883
- } else if (!functionTestSuiteState.suite.tests.length) {
884
- return (
885
- <BlankPanelPlaceholder
886
- text="Add Function Test"
887
- onClick={addTest}
888
- clickActionType="add"
889
- tooltipText="Click to add function test"
890
- />
891
- );
892
- }
893
- return null;
894
- };
895
1000
 
896
1001
  return (
897
- <ResizablePanelGroup orientation="vertical">
898
- <ResizablePanel size={200} minSize={28}>
899
- <div className="binding-editor__header">
900
- <div className="binding-editor__header__title">
901
- <div className="panel__header__title__content">Tests</div>
902
- </div>
903
- <div className="panel__header__actions">
904
- <button
905
- className="panel__header__action testable-test-explorer__play__all__icon"
906
- tabIndex={-1}
907
- onClick={runTests}
908
- title="Run All Tests"
909
- >
910
- <RunAllIcon />
911
- </button>
912
- <button
913
- className="panel__header__action testable-test-explorer__play__all__icon"
914
- tabIndex={-1}
915
- onClick={runFailingTests}
916
- title="Run All Failing Tests"
917
- >
918
- <RunErrorsIcon />
919
- </button>
920
- <button
921
- className="panel__header__action"
922
- tabIndex={-1}
923
- onClick={addTest}
924
- title="Add Function Test"
925
- >
926
- <PlusIcon />
927
- </button>
928
- </div>
929
- </div>
930
- <PanelContent>
931
- {functionTestSuiteState.testStates.map((test) => (
932
- <FunctionTestItem
933
- key={test.uuid}
934
- functionTestState={test}
935
- suiteState={functionTestSuiteState}
936
- />
937
- ))}
938
- {functionTestSuiteState.showCreateModal && (
939
- <CreateTestModal functionSuiteState={functionTestSuiteState} />
940
- )}
941
- </PanelContent>
942
- </ResizablePanel>
943
- <ResizablePanelSplitter>
944
- <ResizablePanelSplitterLine color="var(--color-dark-grey-200)" />
945
- </ResizablePanelSplitter>
946
- <ResizablePanel minSize={28}>
947
- {renderFunctionTestEditor()}
948
- </ResizablePanel>
949
- </ResizablePanelGroup>
1002
+ <div className="panel service-test-editor">
1003
+ <div className="service-test-editor__content">
1004
+ <ResizablePanelGroup orientation="vertical">
1005
+ <ResizablePanel minSize={100} size={200}>
1006
+ <div className="binding-editor__header">
1007
+ <div className="binding-editor__header__title">
1008
+ <div className="panel__header__title__content">Tests</div>
1009
+ </div>
1010
+ <div className="panel__header__actions">
1011
+ <button
1012
+ className="panel__header__action testable-test-explorer__play__all__icon"
1013
+ tabIndex={-1}
1014
+ onClick={runTests}
1015
+ title="Run All Tests"
1016
+ >
1017
+ <RunAllIcon />
1018
+ </button>
1019
+ <button
1020
+ className="panel__header__action testable-test-explorer__play__all__icon"
1021
+ tabIndex={-1}
1022
+ onClick={runFailingTests}
1023
+ title="Run All Failing Tests"
1024
+ >
1025
+ <RunErrorsIcon />
1026
+ </button>
1027
+ {!isReadOnly && (
1028
+ <button
1029
+ className="panel__header__action"
1030
+ tabIndex={-1}
1031
+ onClick={addTest}
1032
+ title="Add Function Test"
1033
+ >
1034
+ <PlusIcon />
1035
+ </button>
1036
+ )}
1037
+ </div>
1038
+ </div>
1039
+ <div>
1040
+ {functionTestSuiteState.testStates.map((test) => (
1041
+ <FunctionTestItem
1042
+ key={test.uuid}
1043
+ functionTestState={test}
1044
+ suiteState={functionTestSuiteState}
1045
+ />
1046
+ ))}
1047
+ </div>
1048
+ </ResizablePanel>
1049
+ <ResizablePanelSplitter>
1050
+ <ResizablePanelSplitterLine color="var(--color-dark-grey-200)" />
1051
+ </ResizablePanelSplitter>
1052
+ <ResizablePanel minSize={56}>
1053
+ {selectedTestState ? (
1054
+ <FunctionTestEditor functionTestState={selectedTestState} />
1055
+ ) : (
1056
+ <BlankPanelPlaceholder
1057
+ text="Select a test"
1058
+ tooltipText="Select a test from the list above"
1059
+ />
1060
+ )}
1061
+ </ResizablePanel>
1062
+ </ResizablePanelGroup>
1063
+ </div>
1064
+ {functionTestSuiteState.showCreateModal && (
1065
+ <CreateTestModal functionSuiteState={functionTestSuiteState} />
1066
+ )}
1067
+ </div>
950
1068
  );
951
1069
  },
952
1070
  );
953
1071
 
1072
+ // ──────────────────────────────────────────────────────────────────────────────
1073
+ // Suite Editor — horizontal split: test data (left) + tests (right)
1074
+ // ──────────────────────────────────────────────────────────────────────────────
1075
+
954
1076
  const FunctionTestSuiteEditor = observer(
955
1077
  (props: { functionTestSuiteState: FunctionTestSuiteState }) => {
956
1078
  const { functionTestSuiteState } = props;
957
- const dataState = functionTestSuiteState.dataState;
958
- const addStoreTestData = (): void => {
959
- // TODO
960
- };
1079
+ const hasTestData = Boolean(
1080
+ functionTestSuiteState.dataState.dataHolder.testData?.length,
1081
+ );
1082
+ const hasIngestOrDataProductAccessors =
1083
+ functionTestSuiteState.functionTestableState
1084
+ .resolvedIngestOrDataProductAccessors.length > 0;
1085
+ const showTestDataPanel =
1086
+ functionTestSuiteState.functionTestableState.containsRuntime ||
1087
+ hasIngestOrDataProductAccessors ||
1088
+ hasTestData;
961
1089
 
962
- if (!functionTestSuiteState.functionTestableState.containsRuntime) {
1090
+ if (!showTestDataPanel) {
1091
+ // No test data — just show the tests panel full width
963
1092
  return (
964
- <FunctionTestSuiteEditorInner
965
- functionTestSuiteState={functionTestSuiteState}
966
- />
1093
+ <div className="service-test-suite-editor">
1094
+ <FunctionTestsPanel functionTestSuiteState={functionTestSuiteState} />
1095
+ </div>
967
1096
  );
968
1097
  }
1098
+
969
1099
  return (
970
- <ResizablePanelGroup orientation="horizontal">
971
- <ResizablePanel size={300} minSize={28}>
972
- <div className="service-test-data-editor panel">
973
- {functionTestSuiteState.dataState.dataHolder.testData?.length ? (
974
- <>
975
- {dataState.selectedDataState && (
976
- <FunctionTestDataStateEditor
977
- functionTestSuiteState={functionTestSuiteState}
978
- storeTestDataState={dataState.selectedDataState}
979
- />
980
- )}
981
- </>
982
- ) : (
983
- <BlankPanelPlaceholder
984
- text="Add Store Test Data"
985
- onClick={addStoreTestData}
986
- clickActionType="add"
987
- tooltipText="Click to add store test data"
988
- />
989
- )}
990
- </div>
991
- </ResizablePanel>
992
- <ResizablePanelSplitter>
993
- <ResizablePanelSplitterLine color="var(--color-dark-grey-200)" />
994
- </ResizablePanelSplitter>
995
- <ResizablePanel minSize={56}>
996
- {
997
- <FunctionTestSuiteEditorInner
1100
+ <div className="service-test-suite-editor">
1101
+ <ResizablePanelGroup orientation="horizontal">
1102
+ <ResizablePanel size={300} minSize={28}>
1103
+ <FunctionTestDataPanel
998
1104
  functionTestSuiteState={functionTestSuiteState}
999
1105
  />
1000
- }
1001
- </ResizablePanel>
1002
- </ResizablePanelGroup>
1106
+ </ResizablePanel>
1107
+ <ResizablePanelSplitter>
1108
+ <ResizablePanelSplitterLine color="var(--color-dark-grey-200)" />
1109
+ </ResizablePanelSplitter>
1110
+ <ResizablePanel minSize={56}>
1111
+ <FunctionTestsPanel
1112
+ functionTestSuiteState={functionTestSuiteState}
1113
+ />
1114
+ </ResizablePanel>
1115
+ </ResizablePanelGroup>
1116
+ </div>
1003
1117
  );
1004
1118
  },
1005
1119
  );
1006
1120
 
1007
- const CreateFucntionTestSuiteModal = observer(
1121
+ // ──────────────────────────────────────────────────────────────────────────────
1122
+ // Suite Tab Context Menu (rename/delete)
1123
+ // ──────────────────────────────────────────────────────────────────────────────
1124
+
1125
+ const SuiteHeaderTabContextMenu = observer(
1126
+ forwardRef<
1127
+ HTMLDivElement,
1128
+ {
1129
+ suite: FunctionTestSuite;
1130
+ functionTestableState: FunctionTestableState;
1131
+ }
1132
+ >(function SuiteHeaderTabContextMenu(props, ref) {
1133
+ const { suite, functionTestableState } = props;
1134
+ const deleteSuite = (): void => {
1135
+ functionTestableState.deleteTestSuite(suite);
1136
+ };
1137
+ const rename = (): void => {
1138
+ functionTestableState.setRenameComponent(suite);
1139
+ };
1140
+ return (
1141
+ <MenuContent ref={ref}>
1142
+ <MenuContentItem onClick={rename}>Rename</MenuContentItem>
1143
+ <MenuContentItem onClick={deleteSuite}>Delete</MenuContentItem>
1144
+ </MenuContent>
1145
+ );
1146
+ }),
1147
+ );
1148
+
1149
+ // ──────────────────────────────────────────────────────────────────────────────
1150
+ // Create Suite Modal
1151
+ // ──────────────────────────────────────────────────────────────────────────────
1152
+
1153
+ const CreateFunctionTestSuiteModal = observer(
1008
1154
  (props: { functionTestableEditorState: FunctionTestableState }) => {
1009
1155
  const { functionTestableEditorState } = props;
1010
1156
  const applicationStore =
@@ -1015,11 +1161,15 @@ const CreateFucntionTestSuiteModal = observer(
1015
1161
  const [testName, setTestName] = useState<string | undefined>(undefined);
1016
1162
  const isValid = suiteName && testName;
1017
1163
 
1018
- // model
1019
1164
  const close = (): void => functionTestableEditorState.setCreateSuite(false);
1020
1165
  const create = (): void => {
1021
1166
  if (suiteName && testName) {
1022
- functionTestableEditorState.createSuite(suiteName, testName);
1167
+ flowResult(
1168
+ functionTestableEditorState.createSuite(suiteName, testName),
1169
+ ).catch(
1170
+ functionTestableEditorState.editorStore.applicationStore
1171
+ .alertUnhandledError,
1172
+ );
1023
1173
  }
1024
1174
  };
1025
1175
  return (
@@ -1086,6 +1236,10 @@ const CreateFucntionTestSuiteModal = observer(
1086
1236
  },
1087
1237
  );
1088
1238
 
1239
+ // ──────────────────────────────────────────────────────────────────────────────
1240
+ // Main Testing Tab — suite tabs at top, suite editor below
1241
+ // ──────────────────────────────────────────────────────────────────────────────
1242
+
1089
1243
  export const FunctionTestableEditor = observer(
1090
1244
  (props: { functionTestableState: FunctionTestableState }) => {
1091
1245
  const { functionTestableState } = props;
@@ -1093,128 +1247,91 @@ export const FunctionTestableEditor = observer(
1093
1247
  const functionEditorState = functionTestableState.functionEditorState;
1094
1248
  const isReadOnly = functionEditorState.isReadOnly;
1095
1249
  const selectedSuiteState = functionTestableState.selectedTestSuite;
1096
- // use effect
1250
+
1097
1251
  useEffect(() => {
1098
1252
  functionTestableState.init();
1099
1253
  }, [functionTestableState]);
1100
1254
 
1101
- const runSuites = (): void => {
1102
- functionTestableState.runTestable();
1103
- };
1104
-
1105
- const runFailingTests = (): void => {
1106
- functionTestableState.runAllFailingSuites();
1107
- };
1108
1255
  const addSuite = (): void => {
1109
1256
  functionTestableState.setCreateSuite(true);
1110
1257
  };
1111
1258
 
1112
- const renderSuiteState = (): React.ReactNode => {
1113
- if (selectedSuiteState) {
1114
- return (
1115
- <FunctionTestSuiteEditor
1116
- functionTestSuiteState={selectedSuiteState}
1117
- />
1118
- );
1119
- } else if (!suites.length) {
1120
- return (
1121
- <BlankPanelPlaceholder
1122
- text="Add Test Suite"
1123
- onClick={addSuite}
1124
- clickActionType="add"
1125
- tooltipText="Click to add test suite"
1126
- />
1127
- );
1128
- }
1129
- return null;
1259
+ const changeSuite = (suite: FunctionTestSuite): void => {
1260
+ functionTestableState.changeSuite(suite);
1261
+ };
1262
+
1263
+ const runSuites = (): void => {
1264
+ functionTestableState.runTestable();
1130
1265
  };
1131
1266
 
1132
1267
  const renameTestingComponent = (val: string): void => {
1133
1268
  functionTestableState.renameTestableComponent(val);
1134
1269
  };
1270
+
1135
1271
  useApplicationNavigationContext(
1136
1272
  LEGEND_STUDIO_APPLICATION_NAVIGATION_CONTEXT_KEY.FUNCTION_EDITOR_TEST,
1137
1273
  );
1138
1274
 
1139
1275
  return (
1140
- <div className="function-testable-editor panel">
1141
- <div className="function-testable-editor">
1142
- <ResizablePanelGroup orientation="vertical">
1143
- <ResizablePanel size={200} minSize={28}>
1144
- <div className="binding-editor__header">
1145
- <div className="binding-editor__header__title">
1146
- <div className="panel__header__title__content">
1147
- Test Suites
1148
- </div>
1149
- </div>
1150
- <div className="panel__header__actions">
1151
- <button
1152
- className="panel__header__action testable-test-explorer__play__all__icon"
1153
- tabIndex={-1}
1154
- onClick={runSuites}
1155
- title="Run All Suites"
1156
- >
1157
- <RunAllIcon />
1158
- </button>
1159
- <button
1160
- className="panel__header__action testable-test-explorer__play__all__icon"
1161
- tabIndex={-1}
1162
- onClick={runFailingTests}
1163
- title="Run All Failing Tests"
1164
- >
1165
- <RunErrorsIcon />
1166
- </button>
1167
- <button
1168
- className="panel__header__action"
1169
- tabIndex={-1}
1170
- onClick={addSuite}
1171
- title="Add Function Suite"
1172
- >
1173
- <PlusIcon />
1174
- </button>
1175
- </div>
1176
- </div>
1177
- <PanelContent>
1276
+ <Panel className="service-test-suite-editor">
1277
+ {functionTestableState.createSuiteModal && (
1278
+ <CreateFunctionTestSuiteModal
1279
+ functionTestableEditorState={functionTestableState}
1280
+ />
1281
+ )}
1282
+
1283
+ <PanelHeader>
1284
+ {suites.length ? (
1285
+ <PanelHeader className="service-test-suite-editor__header service-test-suite-editor__header--with-tabs">
1286
+ <div className="uml-element-editor__tabs">
1178
1287
  {suites.map((suite) => (
1179
- <FunctionTestSuiteItem
1288
+ <div
1180
1289
  key={suite.id}
1181
- functionTestableState={functionTestableState}
1182
- suite={suite}
1183
- />
1290
+ onClick={(): void => changeSuite(suite)}
1291
+ className={clsx('service-test-suite-editor__tab', {
1292
+ 'service-test-suite-editor__tab--active':
1293
+ selectedSuiteState?.suite === suite,
1294
+ })}
1295
+ >
1296
+ <ContextMenu
1297
+ className="mapping-editor__header__tab__content"
1298
+ content={
1299
+ <SuiteHeaderTabContextMenu
1300
+ functionTestableState={functionTestableState}
1301
+ suite={suite}
1302
+ />
1303
+ }
1304
+ >
1305
+ {suite.id}
1306
+ </ContextMenu>
1307
+ </div>
1184
1308
  ))}
1185
- {!suites.length && (
1186
- <BlankPanelPlaceholder
1187
- text="Add Test Suite"
1188
- onClick={addSuite}
1189
- clickActionType="add"
1190
- tooltipText="Click to add test suite"
1191
- />
1192
- )}
1193
- {!suites.length && (
1194
- <BlankPanelPlaceholder
1195
- disabled={functionEditorState.isReadOnly}
1196
- onClick={addSuite}
1197
- text="Add a Test Suite"
1198
- clickActionType="add"
1199
- tooltipText="Click to add a new function test suite"
1200
- />
1201
- )}
1202
- </PanelContent>
1203
- </ResizablePanel>
1204
- <ResizablePanelSplitter>
1205
- <ResizablePanelSplitterLine color="var(--color-dark-grey-200)" />
1206
- </ResizablePanelSplitter>
1207
- <ResizablePanel minSize={56}>
1208
- <div className="function-test-suite-editor">
1209
- <div className="function-test-suite-editor__content">
1210
- {renderSuiteState()}
1211
- </div>
1212
1309
  </div>
1213
- </ResizablePanel>
1214
- </ResizablePanelGroup>
1215
- {functionTestableState.createSuiteModal && (
1216
- <CreateFucntionTestSuiteModal
1217
- functionTestableEditorState={functionTestableState}
1310
+ </PanelHeader>
1311
+ ) : (
1312
+ <div></div>
1313
+ )}
1314
+ <PanelHeaderActions>
1315
+ <PanelHeaderActionItem onClick={runSuites} title="Run All Suites">
1316
+ <RunAllIcon />
1317
+ </PanelHeaderActionItem>
1318
+ <PanelHeaderActionItem onClick={addSuite} title="Add Test Suite">
1319
+ <PlusIcon />
1320
+ </PanelHeaderActionItem>
1321
+ </PanelHeaderActions>
1322
+ </PanelHeader>
1323
+ <Panel className="service-test-suite-editor">
1324
+ {selectedSuiteState && (
1325
+ <FunctionTestSuiteEditor
1326
+ functionTestSuiteState={selectedSuiteState}
1327
+ />
1328
+ )}
1329
+ {!suites.length && (
1330
+ <BlankPanelPlaceholder
1331
+ text="Add Test Suite"
1332
+ onClick={addSuite}
1333
+ clickActionType="add"
1334
+ tooltipText="Click to add test suite"
1218
1335
  />
1219
1336
  )}
1220
1337
  {functionTestableState.testableComponentToRename && (
@@ -1231,8 +1348,8 @@ export const FunctionTestableEditor = observer(
1231
1348
  }
1232
1349
  />
1233
1350
  )}
1234
- </div>
1235
- </div>
1351
+ </Panel>
1352
+ </Panel>
1236
1353
  );
1237
1354
  },
1238
1355
  );