@finos/legend-application-studio 28.13.2 → 28.13.4

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 (95) hide show
  1. package/lib/__lib__/LegendStudioApplicationNavigationContext.d.ts +1 -0
  2. package/lib/__lib__/LegendStudioApplicationNavigationContext.d.ts.map +1 -1
  3. package/lib/__lib__/LegendStudioApplicationNavigationContext.js +1 -0
  4. package/lib/__lib__/LegendStudioApplicationNavigationContext.js.map +1 -1
  5. package/lib/application/LegendStudioApplicationConfig.d.ts +5 -9
  6. package/lib/application/LegendStudioApplicationConfig.d.ts.map +1 -1
  7. package/lib/application/LegendStudioApplicationConfig.js +6 -10
  8. package/lib/application/LegendStudioApplicationConfig.js.map +1 -1
  9. package/lib/components/editor/ActivityBar.d.ts.map +1 -1
  10. package/lib/components/editor/ActivityBar.js +3 -3
  11. package/lib/components/editor/ActivityBar.js.map +1 -1
  12. package/lib/components/editor/editor-group/connection-editor/RelationalDatabaseConnectionEditor.js +15 -15
  13. package/lib/components/editor/editor-group/connection-editor/RelationalDatabaseConnectionEditor.js.map +1 -1
  14. package/lib/components/editor/editor-group/function-activator/FunctionEditor.d.ts +1 -1
  15. package/lib/components/editor/editor-group/function-activator/FunctionEditor.d.ts.map +1 -1
  16. package/lib/components/editor/editor-group/function-activator/FunctionEditor.js +55 -104
  17. package/lib/components/editor/editor-group/function-activator/FunctionEditor.js.map +1 -1
  18. package/lib/components/editor/editor-group/function-activator/SnowflakeAppFunctionActivatorEditor.d.ts.map +1 -1
  19. package/lib/components/editor/editor-group/function-activator/SnowflakeAppFunctionActivatorEditor.js +3 -3
  20. package/lib/components/editor/editor-group/function-activator/SnowflakeAppFunctionActivatorEditor.js.map +1 -1
  21. package/lib/components/editor/editor-group/function-activator/testable/FunctionTestableEditor.d.ts +22 -0
  22. package/lib/components/editor/editor-group/function-activator/testable/FunctionTestableEditor.d.ts.map +1 -0
  23. package/lib/components/editor/editor-group/function-activator/testable/FunctionTestableEditor.js +267 -0
  24. package/lib/components/editor/editor-group/function-activator/testable/FunctionTestableEditor.js.map +1 -0
  25. package/lib/components/editor/side-bar/Explorer.d.ts.map +1 -1
  26. package/lib/components/editor/side-bar/Explorer.js +1 -25
  27. package/lib/components/editor/side-bar/Explorer.js.map +1 -1
  28. package/lib/index.css +1 -1
  29. package/lib/package.json +1 -1
  30. package/lib/stores/editor/editor-state/element-editor-state/{FunctionActivatorPromoteState.d.ts → FunctionActivatorState.d.ts} +9 -9
  31. package/lib/stores/editor/editor-state/element-editor-state/FunctionActivatorState.d.ts.map +1 -0
  32. package/lib/stores/editor/editor-state/element-editor-state/{FunctionActivatorPromoteState.js → FunctionActivatorState.js} +24 -24
  33. package/lib/stores/editor/editor-state/element-editor-state/FunctionActivatorState.js.map +1 -0
  34. package/lib/stores/editor/editor-state/element-editor-state/FunctionEditorState.d.ts +6 -5
  35. package/lib/stores/editor/editor-state/element-editor-state/FunctionEditorState.d.ts.map +1 -1
  36. package/lib/stores/editor/editor-state/element-editor-state/FunctionEditorState.js +6 -5
  37. package/lib/stores/editor/editor-state/element-editor-state/FunctionEditorState.js.map +1 -1
  38. package/lib/stores/editor/editor-state/element-editor-state/{FunctionActivatorBuilderState.d.ts → ToDelete_FunctionActivatorBuilderState.d.ts} +1 -1
  39. package/lib/stores/editor/editor-state/element-editor-state/ToDelete_FunctionActivatorBuilderState.d.ts.map +1 -0
  40. package/lib/stores/editor/editor-state/element-editor-state/{FunctionActivatorBuilderState.js → ToDelete_FunctionActivatorBuilderState.js} +1 -1
  41. package/lib/stores/editor/editor-state/element-editor-state/ToDelete_FunctionActivatorBuilderState.js.map +1 -0
  42. package/lib/stores/editor/editor-state/element-editor-state/function-activator/INTERNAL__UnknownFunctionActivatorEditorState.js +1 -1
  43. package/lib/stores/editor/editor-state/element-editor-state/function-activator/INTERNAL__UnknownFunctionActivatorEditorState.js.map +1 -1
  44. package/lib/stores/editor/editor-state/element-editor-state/function-activator/SnowflakeAppFunctionActivatorEditorState.d.ts +1 -1
  45. package/lib/stores/editor/editor-state/element-editor-state/function-activator/SnowflakeAppFunctionActivatorEditorState.d.ts.map +1 -1
  46. package/lib/stores/editor/editor-state/element-editor-state/function-activator/SnowflakeAppFunctionActivatorEditorState.js +2 -2
  47. package/lib/stores/editor/editor-state/element-editor-state/function-activator/SnowflakeAppFunctionActivatorEditorState.js.map +1 -1
  48. package/lib/stores/editor/editor-state/element-editor-state/function-activator/testable/FunctionTestableState.d.ts +87 -0
  49. package/lib/stores/editor/editor-state/element-editor-state/function-activator/testable/FunctionTestableState.d.ts.map +1 -0
  50. package/lib/stores/editor/editor-state/element-editor-state/function-activator/testable/FunctionTestableState.js +422 -0
  51. package/lib/stores/editor/editor-state/element-editor-state/function-activator/testable/FunctionTestableState.js.map +1 -0
  52. package/lib/stores/editor/editor-state/element-editor-state/mapping/MappingEditorState.d.ts.map +1 -1
  53. package/lib/stores/editor/editor-state/element-editor-state/mapping/MappingEditorState.js +1 -1
  54. package/lib/stores/editor/editor-state/element-editor-state/mapping/MappingEditorState.js.map +1 -1
  55. package/lib/stores/editor/editor-state/element-editor-state/mapping/testable/MappingTestableState.d.ts +3 -17
  56. package/lib/stores/editor/editor-state/element-editor-state/mapping/testable/MappingTestableState.d.ts.map +1 -1
  57. package/lib/stores/editor/editor-state/element-editor-state/mapping/testable/MappingTestableState.js +8 -69
  58. package/lib/stores/editor/editor-state/element-editor-state/mapping/testable/MappingTestableState.js.map +1 -1
  59. package/lib/stores/editor/editor-state/element-editor-state/testable/TestableEditorState.d.ts +26 -3
  60. package/lib/stores/editor/editor-state/element-editor-state/testable/TestableEditorState.d.ts.map +1 -1
  61. package/lib/stores/editor/editor-state/element-editor-state/testable/TestableEditorState.js +72 -1
  62. package/lib/stores/editor/editor-state/element-editor-state/testable/TestableEditorState.js.map +1 -1
  63. package/lib/stores/graph-modifier/DomainGraphModifierHelper.d.ts +4 -1
  64. package/lib/stores/graph-modifier/DomainGraphModifierHelper.d.ts.map +1 -1
  65. package/lib/stores/graph-modifier/DomainGraphModifierHelper.js +10 -1
  66. package/lib/stores/graph-modifier/DomainGraphModifierHelper.js.map +1 -1
  67. package/lib/stores/graph-modifier/Testable_GraphModifierHelper.d.ts +3 -2
  68. package/lib/stores/graph-modifier/Testable_GraphModifierHelper.d.ts.map +1 -1
  69. package/lib/stores/graph-modifier/Testable_GraphModifierHelper.js +3 -0
  70. package/lib/stores/graph-modifier/Testable_GraphModifierHelper.js.map +1 -1
  71. package/package.json +4 -4
  72. package/src/__lib__/LegendStudioApplicationNavigationContext.ts +1 -0
  73. package/src/application/LegendStudioApplicationConfig.ts +7 -12
  74. package/src/components/editor/ActivityBar.tsx +4 -3
  75. package/src/components/editor/editor-group/connection-editor/RelationalDatabaseConnectionEditor.tsx +36 -36
  76. package/src/components/editor/editor-group/function-activator/FunctionEditor.tsx +136 -345
  77. package/src/components/editor/editor-group/function-activator/SnowflakeAppFunctionActivatorEditor.tsx +3 -10
  78. package/src/components/editor/editor-group/function-activator/testable/FunctionTestableEditor.tsx +879 -0
  79. package/src/components/editor/side-bar/Explorer.tsx +0 -59
  80. package/src/stores/editor/editor-state/element-editor-state/{FunctionActivatorPromoteState.ts → FunctionActivatorState.ts} +23 -23
  81. package/src/stores/editor/editor-state/element-editor-state/FunctionEditorState.ts +7 -6
  82. package/src/stores/editor/editor-state/element-editor-state/function-activator/INTERNAL__UnknownFunctionActivatorEditorState.ts +1 -1
  83. package/src/stores/editor/editor-state/element-editor-state/function-activator/SnowflakeAppFunctionActivatorEditorState.ts +2 -2
  84. package/src/stores/editor/editor-state/element-editor-state/function-activator/testable/FunctionTestableState.ts +646 -0
  85. package/src/stores/editor/editor-state/element-editor-state/mapping/MappingEditorState.ts +1 -4
  86. package/src/stores/editor/editor-state/element-editor-state/mapping/testable/MappingTestableState.ts +16 -97
  87. package/src/stores/editor/editor-state/element-editor-state/testable/TestableEditorState.ts +105 -3
  88. package/src/stores/graph-modifier/DomainGraphModifierHelper.ts +34 -2
  89. package/src/stores/graph-modifier/Testable_GraphModifierHelper.ts +9 -1
  90. package/tsconfig.json +4 -2
  91. package/lib/stores/editor/editor-state/element-editor-state/FunctionActivatorBuilderState.d.ts.map +0 -1
  92. package/lib/stores/editor/editor-state/element-editor-state/FunctionActivatorBuilderState.js.map +0 -1
  93. package/lib/stores/editor/editor-state/element-editor-state/FunctionActivatorPromoteState.d.ts.map +0 -1
  94. package/lib/stores/editor/editor-state/element-editor-state/FunctionActivatorPromoteState.js.map +0 -1
  95. /package/src/stores/editor/editor-state/element-editor-state/{FunctionActivatorBuilderState.ts → ToDelete_FunctionActivatorBuilderState.ts} +0 -0
@@ -0,0 +1,879 @@
1
+ /**
2
+ * Copyright (c) 2020-present, Goldman Sachs
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+
17
+ import { observer } from 'mobx-react-lite';
18
+ import {
19
+ BlankPanelPlaceholder,
20
+ ContextMenu,
21
+ Dialog,
22
+ MenuContent,
23
+ MenuContentItem,
24
+ Modal,
25
+ ModalBody,
26
+ ModalFooter,
27
+ ModalFooterButton,
28
+ ModalHeader,
29
+ ModalTitle,
30
+ PanelContent,
31
+ PanelFormTextField,
32
+ PlayIcon,
33
+ PlusIcon,
34
+ ResizablePanel,
35
+ ResizablePanelGroup,
36
+ ResizablePanelSplitter,
37
+ ResizablePanelSplitterLine,
38
+ RunAllIcon,
39
+ RunErrorsIcon,
40
+ clsx,
41
+ } from '@finos/legend-art';
42
+ import { forwardRef, useEffect, useRef, useState } from 'react';
43
+ import {
44
+ type FunctionTestSuite,
45
+ type DataElement,
46
+ type EmbeddedData,
47
+ DataElementReference,
48
+ PackageableElementExplicitReference,
49
+ RelationalCSVData,
50
+ ModelStoreData,
51
+ ExternalFormatData,
52
+ ModelEmbeddedData,
53
+ } from '@finos/legend-graph';
54
+ import type {
55
+ FunctionStoreTestDataState,
56
+ FunctionTestState,
57
+ FunctionTestSuiteState,
58
+ FunctionTestableState,
59
+ } from '../../../../../stores/editor/editor-state/element-editor-state/function-activator/testable/FunctionTestableState.js';
60
+ import {
61
+ TESTABLE_RESULT,
62
+ getTestableResultFromTestResult,
63
+ getTestableResultFromTestResults,
64
+ } from '../../../../../stores/editor/sidebar-state/testable/GlobalTestRunnerState.js';
65
+ import { flowResult } from 'mobx';
66
+ import { getTestableResultIcon } from '../../../side-bar/testable/GlobalTestRunner.js';
67
+ import { atomicTest_setDoc } from '../../../../../stores/graph-modifier/Testable_GraphModifierHelper.js';
68
+ import {
69
+ RenameModal,
70
+ SharedDataElementModal,
71
+ TestAssertionEditor,
72
+ } from '../../testable/TestableSharedComponents.js';
73
+ import { returnUndefOnError } from '@finos/legend-shared';
74
+ import {
75
+ EmbeddedDataCreatorFromEmbeddedData,
76
+ validateTestableId,
77
+ } from '../../../../../stores/editor/utils/TestableUtils.js';
78
+ import { EmbeddedDataEditor } from '../../data-editor/EmbeddedDataEditor.js';
79
+ import { LEGEND_STUDIO_APPLICATION_NAVIGATION_CONTEXT_KEY } from '../../../../../__lib__/LegendStudioApplicationNavigationContext.js';
80
+ import { useApplicationNavigationContext } from '@finos/legend-application';
81
+
82
+ const FunctionTestableContextMenu = observer(
83
+ forwardRef<
84
+ HTMLDivElement,
85
+ {
86
+ addName?: string;
87
+ _delete?: () => void;
88
+ rename?: () => void;
89
+ add?: () => void;
90
+ }
91
+ >(function TestContainerContextMenu(props, ref) {
92
+ const { addName, add, rename, _delete } = props;
93
+ const addTest = (): void => {
94
+ add?.();
95
+ };
96
+ const remove = (): void => _delete?.();
97
+ const handleRename = (): void => rename?.();
98
+ return (
99
+ <MenuContent ref={ref}>
100
+ {rename && (
101
+ <MenuContentItem onClick={handleRename}>Rename</MenuContentItem>
102
+ )}
103
+ {_delete && <MenuContentItem onClick={remove}>Delete</MenuContentItem>}
104
+ {addName && rename && (
105
+ <MenuContentItem
106
+ onClick={addTest}
107
+ >{`Add ${addName}`}</MenuContentItem>
108
+ )}
109
+ </MenuContent>
110
+ );
111
+ }),
112
+ );
113
+
114
+ const FunctionTestSuiteItem = observer(
115
+ (props: {
116
+ suite: FunctionTestSuite;
117
+ functionTestableState: FunctionTestableState;
118
+ }) => {
119
+ const { suite, functionTestableState } = props;
120
+ const [isSelectedFromContextMenu, setIsSelectedFromContextMenu] =
121
+ useState(false);
122
+ const isReadOnly = functionTestableState.functionEditorState.isReadOnly;
123
+ const openSuite = (): void => functionTestableState.changeSuite(suite);
124
+ const results = functionTestableState.testableResults?.filter(
125
+ (t) => t.parentSuite?.id === suite.id,
126
+ );
127
+ const isRunning =
128
+ functionTestableState.isRunningTestableSuitesState.isInProgress ||
129
+ (functionTestableState.isRunningFailingSuitesState.isInProgress &&
130
+ functionTestableState.failingSuites.includes(suite)) ||
131
+ functionTestableState.runningSuite === suite;
132
+ const isActive = functionTestableState.selectedTestSuite?.suite === suite;
133
+ const _testableResult = getTestableResultFromTestResults(results);
134
+ const testableResult = isRunning
135
+ ? TESTABLE_RESULT.IN_PROGRESS
136
+ : _testableResult;
137
+ const resultIcon = getTestableResultIcon(testableResult);
138
+ const onContextMenuOpen = (): void => setIsSelectedFromContextMenu(true);
139
+ const onContextMenuClose = (): void => setIsSelectedFromContextMenu(false);
140
+ const add = (): void => {
141
+ // TODO
142
+ };
143
+ const _delete = (): void => {
144
+ functionTestableState.deleteTestSuite(suite);
145
+ };
146
+ const rename = (): void => {
147
+ functionTestableState.setRenameComponent(suite);
148
+ };
149
+ const runSuite = (): void => {
150
+ flowResult(functionTestableState.runSuite(suite)).catch(
151
+ functionTestableState.editorStore.applicationStore.alertUnhandledError,
152
+ );
153
+ };
154
+ return (
155
+ <ContextMenu
156
+ className={clsx(
157
+ 'testable-test-explorer__item',
158
+ {
159
+ 'testable-test-explorer__item--selected-from-context-menu':
160
+ !isActive && isSelectedFromContextMenu,
161
+ },
162
+ { 'testable-test-explorer__item--active': isActive },
163
+ )}
164
+ disabled={isReadOnly}
165
+ content={
166
+ <FunctionTestableContextMenu
167
+ addName="Suite"
168
+ add={add}
169
+ _delete={_delete}
170
+ rename={rename}
171
+ />
172
+ }
173
+ menuProps={{ elevation: 7 }}
174
+ onOpen={onContextMenuOpen}
175
+ onClose={onContextMenuClose}
176
+ >
177
+ <button
178
+ className={clsx('testable-test-explorer__item__label')}
179
+ onClick={openSuite}
180
+ tabIndex={-1}
181
+ >
182
+ <div className="testable-test-explorer__item__label__icon">
183
+ {resultIcon}
184
+ </div>
185
+ <div className="testable-test-explorer__item__label__text">
186
+ {suite.id}
187
+ </div>
188
+ <div className="mapping-test-explorer__item__actions">
189
+ <button
190
+ className="mapping-test-explorer__item__action mapping-test-explorer__run-test-btn"
191
+ onClick={runSuite}
192
+ disabled={isRunning}
193
+ tabIndex={-1}
194
+ title={`Run ${suite.id}`}
195
+ >
196
+ {<PlayIcon />}
197
+ </button>
198
+ </div>
199
+ </button>
200
+ </ContextMenu>
201
+ );
202
+ },
203
+ );
204
+
205
+ const FunctionTestDataStateEditor = observer(
206
+ (props: {
207
+ functionTestSuiteState: FunctionTestSuiteState;
208
+ storeTestDataState: FunctionStoreTestDataState;
209
+ }) => {
210
+ const { functionTestSuiteState, storeTestDataState } = props;
211
+ const functionTestableState = functionTestSuiteState.functionTestableState;
212
+ const isReadOnly = functionTestableState.functionEditorState.isReadOnly;
213
+ const embeddedState = storeTestDataState.embeddedEditorState;
214
+ const currentData = embeddedState.embeddedData;
215
+ const isUsingReference = currentData instanceof DataElementReference;
216
+ const open = (): void => storeTestDataState.setDataElementModal(true);
217
+ const close = (): void => storeTestDataState.setDataElementModal(false);
218
+ const changeToUseMyOwn = (): void => {
219
+ if (isUsingReference) {
220
+ const newBare = returnUndefOnError(() =>
221
+ currentData.accept_EmbeddedDataVisitor(
222
+ new EmbeddedDataCreatorFromEmbeddedData(
223
+ functionTestableState.editorStore,
224
+ ),
225
+ ),
226
+ );
227
+ if (newBare) {
228
+ storeTestDataState.changeEmbeddedData(newBare);
229
+ }
230
+ }
231
+ };
232
+
233
+ const sharedDataHandler = (val: DataElement): void => {
234
+ const dataRef = new DataElementReference();
235
+ dataRef.dataElement = PackageableElementExplicitReference.create(val);
236
+ const dataElementValue = val.data;
237
+ let embeddedData: EmbeddedData = dataRef;
238
+ if (
239
+ currentData instanceof ModelStoreData &&
240
+ dataElementValue instanceof ExternalFormatData
241
+ ) {
242
+ const modelStoreVal = currentData.modelData?.[0];
243
+ if (modelStoreVal instanceof ModelEmbeddedData) {
244
+ const newModelEmbeddedData = new ModelEmbeddedData();
245
+ newModelEmbeddedData.model =
246
+ PackageableElementExplicitReference.create(
247
+ modelStoreVal.model.value,
248
+ );
249
+
250
+ newModelEmbeddedData.data = dataRef;
251
+ const modelStoreData = new ModelStoreData();
252
+ modelStoreData.modelData = [newModelEmbeddedData];
253
+ embeddedData = modelStoreData;
254
+ }
255
+ }
256
+ storeTestDataState.changeEmbeddedData(embeddedData);
257
+ };
258
+
259
+ const dataElements =
260
+ functionTestSuiteState.editorStore.graphManagerState.graph.dataElements;
261
+
262
+ const filter = (val: DataElement): boolean => {
263
+ const dataElementData = val.data;
264
+ if (currentData instanceof RelationalCSVData) {
265
+ if (dataElementData instanceof RelationalCSVData) {
266
+ return true;
267
+ }
268
+ return false;
269
+ } else if (currentData instanceof ModelStoreData) {
270
+ if (
271
+ dataElementData instanceof ExternalFormatData ||
272
+ dataElementData instanceof ModelStoreData
273
+ ) {
274
+ return true;
275
+ }
276
+ return false;
277
+ }
278
+ return true;
279
+ };
280
+ return (
281
+ <div className="service-test-data-editor">
282
+ <div className="service-test-suite-editor__header">
283
+ <div className="service-test-suite-editor__header__title">
284
+ <div className="service-test-suite-editor__header__title__label">
285
+ input data
286
+ </div>
287
+ </div>
288
+ <div className="panel__header__actions">
289
+ {isUsingReference ? (
290
+ <button
291
+ className="panel__header__action service-execution-editor__test-data__generate-btn"
292
+ onClick={changeToUseMyOwn}
293
+ disabled={!isUsingReference}
294
+ title="Use own data"
295
+ tabIndex={-1}
296
+ >
297
+ <div className="service-execution-editor__test-data__generate-btn__label">
298
+ <div className="service-execution-editor__test-data__generate-btn__label__title">
299
+ Own Data
300
+ </div>
301
+ </div>
302
+ </button>
303
+ ) : (
304
+ <button
305
+ className="panel__header__action service-execution-editor__test-data__generate-btn"
306
+ onClick={open}
307
+ title="Use Shared Data via Defined Data Element"
308
+ disabled={!dataElements.length}
309
+ tabIndex={-1}
310
+ >
311
+ <div className="service-execution-editor__test-data__generate-btn__label">
312
+ <div className="service-execution-editor__test-data__generate-btn__label__title">
313
+ Shared Data
314
+ </div>
315
+ </div>
316
+ </button>
317
+ )}
318
+ </div>
319
+ </div>
320
+
321
+ {storeTestDataState.dataElementModal && (
322
+ <SharedDataElementModal
323
+ isReadOnly={false}
324
+ editorStore={storeTestDataState.editorStore}
325
+ close={close}
326
+ filterBy={filter}
327
+ handler={sharedDataHandler}
328
+ />
329
+ )}
330
+ <EmbeddedDataEditor
331
+ isReadOnly={isReadOnly}
332
+ embeddedDataEditorState={storeTestDataState.embeddedEditorState}
333
+ />
334
+ </div>
335
+ );
336
+ },
337
+ );
338
+
339
+ const FunctionTestEditor = observer(
340
+ (props: { functionTestState: FunctionTestState }) => {
341
+ const { functionTestState } = props;
342
+ const functionTest = functionTestState.test;
343
+ return (
344
+ <div className="service-test-editor panel">
345
+ <div className="panel mapping-testable-editor">
346
+ <div className="mapping-testable-editor__content">
347
+ <ResizablePanelGroup orientation="horizontal">
348
+ <ResizablePanel size={120}>
349
+ <div className="service-test-data-editor panel">
350
+ <div className="service-test-editor__setup__configuration">
351
+ <div className="panel__content__form__section">
352
+ <div className="panel__content__form__section__header__label">
353
+ Test Documentation
354
+ </div>
355
+ <textarea
356
+ className="panel__content__form__section__textarea mapping-testable-editor__doc__textarea"
357
+ spellCheck={false}
358
+ value={functionTest.doc ?? ''}
359
+ onChange={(event) => {
360
+ atomicTest_setDoc(
361
+ functionTest,
362
+ event.target.value ? event.target.value : undefined,
363
+ );
364
+ }}
365
+ />
366
+ </div>
367
+ </div>
368
+ </div>
369
+ </ResizablePanel>
370
+ <ResizablePanelSplitter>
371
+ <ResizablePanelSplitterLine color="var(--color-dark-grey-200)" />
372
+ </ResizablePanelSplitter>
373
+ <ResizablePanel>
374
+ {functionTestState.selectedAsertionState && (
375
+ <TestAssertionEditor
376
+ testAssertionState={functionTestState.selectedAsertionState}
377
+ />
378
+ )}
379
+ </ResizablePanel>
380
+ </ResizablePanelGroup>
381
+ </div>
382
+ </div>
383
+ </div>
384
+ );
385
+ },
386
+ );
387
+
388
+ const FunctionTestItem = observer(
389
+ (props: {
390
+ suiteState: FunctionTestSuiteState;
391
+ functionTestState: FunctionTestState;
392
+ }) => {
393
+ const { functionTestState, suiteState } = props;
394
+ const functionTest = functionTestState.test;
395
+ const isRunning = functionTestState.runningTestAction.isInProgress;
396
+ const [isSelectedFromContextMenu, setIsSelectedFromContextMenu] =
397
+ useState(false);
398
+ const isReadOnly =
399
+ suiteState.functionTestableState.functionEditorState.isReadOnly;
400
+ const openTest = (): void => suiteState.changeTest(functionTest);
401
+ const isActive = suiteState.selectTestState?.test === functionTest;
402
+ const _testableResult = getTestableResultFromTestResult(
403
+ functionTestState.testResultState.result,
404
+ );
405
+ const testableResult = isRunning
406
+ ? TESTABLE_RESULT.IN_PROGRESS
407
+ : _testableResult;
408
+ const resultIcon = getTestableResultIcon(testableResult);
409
+ const onContextMenuOpen = (): void => setIsSelectedFromContextMenu(true);
410
+ const onContextMenuClose = (): void => setIsSelectedFromContextMenu(false);
411
+ const add = (): void => {
412
+ // TODO
413
+ };
414
+ const _delete = (): void => {
415
+ suiteState.deleteTest(functionTest);
416
+ };
417
+
418
+ const rename = (): void => {
419
+ suiteState.functionTestableState.setRenameComponent(functionTest);
420
+ };
421
+ const runTest = (): void => {
422
+ flowResult(functionTestState.runTest()).catch(
423
+ functionTestState.editorStore.applicationStore.alertUnhandledError,
424
+ );
425
+ };
426
+ return (
427
+ <ContextMenu
428
+ className={clsx(
429
+ 'testable-test-explorer__item',
430
+ {
431
+ 'testable-test-explorer__item--selected-from-context-menu':
432
+ !isActive && isSelectedFromContextMenu,
433
+ },
434
+ { 'testable-test-explorer__item--active': isActive },
435
+ )}
436
+ disabled={isReadOnly}
437
+ content={
438
+ <FunctionTestableContextMenu
439
+ addName="Test"
440
+ add={add}
441
+ _delete={_delete}
442
+ rename={rename}
443
+ />
444
+ }
445
+ menuProps={{ elevation: 7 }}
446
+ onOpen={onContextMenuOpen}
447
+ onClose={onContextMenuClose}
448
+ >
449
+ <button
450
+ className={clsx('testable-test-explorer__item__label')}
451
+ onClick={openTest}
452
+ tabIndex={-1}
453
+ >
454
+ <div className="testable-test-explorer__item__label__icon">
455
+ {resultIcon}
456
+ </div>
457
+ <div className="testable-test-explorer__item__label__text">
458
+ {functionTest.id}
459
+ </div>
460
+ <div className="mapping-test-explorer__item__actions">
461
+ <button
462
+ className="mapping-test-explorer__item__action mapping-test-explorer__run-test-btn"
463
+ onClick={runTest}
464
+ disabled={functionTestState.runningTestAction.isInProgress}
465
+ tabIndex={-1}
466
+ title={`Run ${functionTestState.test.id}`}
467
+ >
468
+ {<PlayIcon />}
469
+ </button>
470
+ </div>
471
+ </button>
472
+ </ContextMenu>
473
+ );
474
+ },
475
+ );
476
+
477
+ const CreateTestModal = observer(
478
+ (props: { functionSuiteState: FunctionTestSuiteState }) => {
479
+ const { functionSuiteState } = props;
480
+ const suite = functionSuiteState.suite;
481
+ // test name
482
+ const [id, setId] = useState<string | undefined>(undefined);
483
+ const isValid = id && !id.includes(' ');
484
+ const errorMessage = validateTestableId(
485
+ id,
486
+ suite.tests.map((t) => t.id),
487
+ );
488
+ const close = (): void => functionSuiteState.setShowModal(false);
489
+ const create = (): void => {
490
+ if (id) {
491
+ functionSuiteState.addNewTest(id);
492
+ close();
493
+ }
494
+ };
495
+
496
+ return (
497
+ <Dialog
498
+ open={functionSuiteState.showCreateModal}
499
+ onClose={close}
500
+ classes={{ container: 'search-modal__container' }}
501
+ PaperProps={{ classes: { root: 'search-modal__inner-container' } }}
502
+ >
503
+ <Modal darkMode={true}>
504
+ <ModalHeader>
505
+ <ModalTitle title="Create Mapping Test" />
506
+ </ModalHeader>
507
+ <ModalBody>
508
+ <PanelFormTextField
509
+ name="Name"
510
+ prompt=""
511
+ value={id}
512
+ update={(value: string | undefined): void => setId(value ?? '')}
513
+ errorMessage={errorMessage}
514
+ />
515
+ </ModalBody>
516
+ <ModalFooter>
517
+ <ModalFooterButton
518
+ disabled={!isValid}
519
+ onClick={create}
520
+ text="Create"
521
+ />
522
+ <ModalFooterButton onClick={close} text="Close" />
523
+ </ModalFooter>
524
+ </Modal>
525
+ </Dialog>
526
+ );
527
+ },
528
+ );
529
+
530
+ const FunctionTestSuiteEditor = observer(
531
+ (props: { functionTestSuiteState: FunctionTestSuiteState }) => {
532
+ const { functionTestSuiteState } = props;
533
+ const dataState = functionTestSuiteState.dataState;
534
+ const editorStore = functionTestSuiteState.editorStore;
535
+ const selectedTestState = functionTestSuiteState.selectTestState;
536
+ const addTest = (): void => {
537
+ functionTestSuiteState.setShowModal(true);
538
+ };
539
+ const runTests = (): void => {
540
+ flowResult(functionTestSuiteState.runSuite()).catch(
541
+ editorStore.applicationStore.alertUnhandledError,
542
+ );
543
+ };
544
+ const runFailingTests = (): void => {
545
+ flowResult(functionTestSuiteState.runFailingTests()).catch(
546
+ editorStore.applicationStore.alertUnhandledError,
547
+ );
548
+ };
549
+
550
+ const addStoreTestData = (): void => {
551
+ // TODO
552
+ };
553
+
554
+ const renderMappingTestEditor = (): React.ReactNode => {
555
+ if (selectedTestState) {
556
+ return <FunctionTestEditor functionTestState={selectedTestState} />;
557
+ } else if (!functionTestSuiteState.suite.tests.length) {
558
+ return (
559
+ <BlankPanelPlaceholder
560
+ text="Add Function Test"
561
+ onClick={addTest}
562
+ clickActionType="add"
563
+ tooltipText="Click to add function test"
564
+ />
565
+ );
566
+ }
567
+ return null;
568
+ };
569
+ return (
570
+ <ResizablePanelGroup orientation="horizontal">
571
+ <ResizablePanel size={300} minSize={28}>
572
+ <div className="service-test-data-editor panel">
573
+ {functionTestSuiteState.dataState.dataHolder.testData?.length ? (
574
+ <>
575
+ {dataState.selectedDataState && (
576
+ <FunctionTestDataStateEditor
577
+ functionTestSuiteState={functionTestSuiteState}
578
+ storeTestDataState={dataState.selectedDataState}
579
+ />
580
+ )}
581
+ </>
582
+ ) : (
583
+ <BlankPanelPlaceholder
584
+ text="Add Store Test Data"
585
+ onClick={addStoreTestData}
586
+ clickActionType="add"
587
+ tooltipText="Click to add store test data"
588
+ />
589
+ )}
590
+ </div>
591
+ </ResizablePanel>
592
+ <ResizablePanelSplitter>
593
+ <ResizablePanelSplitterLine color="var(--color-dark-grey-200)" />
594
+ </ResizablePanelSplitter>
595
+ <ResizablePanel minSize={56}>
596
+ <ResizablePanelGroup orientation="vertical">
597
+ <ResizablePanel size={200} minSize={28}>
598
+ <div className="binding-editor__header">
599
+ <div className="binding-editor__header__title">
600
+ <div className="panel__header__title__content">Tests</div>
601
+ </div>
602
+ <div className="panel__header__actions">
603
+ <button
604
+ className="panel__header__action testable-test-explorer__play__all__icon"
605
+ tabIndex={-1}
606
+ onClick={runTests}
607
+ title="Run All Tests"
608
+ >
609
+ <RunAllIcon />
610
+ </button>
611
+ <button
612
+ className="panel__header__action testable-test-explorer__play__all__icon"
613
+ tabIndex={-1}
614
+ onClick={runFailingTests}
615
+ title="Run All Failing Tests"
616
+ >
617
+ <RunErrorsIcon />
618
+ </button>
619
+ <button
620
+ className="panel__header__action"
621
+ tabIndex={-1}
622
+ onClick={addTest}
623
+ title="Add Function Test"
624
+ >
625
+ <PlusIcon />
626
+ </button>
627
+ </div>
628
+ </div>
629
+ <PanelContent>
630
+ {functionTestSuiteState.testStates.map((test) => (
631
+ <FunctionTestItem
632
+ key={test.uuid}
633
+ functionTestState={test}
634
+ suiteState={functionTestSuiteState}
635
+ />
636
+ ))}
637
+ {functionTestSuiteState.showCreateModal && (
638
+ <CreateTestModal
639
+ functionSuiteState={functionTestSuiteState}
640
+ />
641
+ )}
642
+ </PanelContent>
643
+ </ResizablePanel>
644
+ <ResizablePanelSplitter>
645
+ <ResizablePanelSplitterLine color="var(--color-dark-grey-200)" />
646
+ </ResizablePanelSplitter>
647
+ <ResizablePanel minSize={28}>
648
+ {renderMappingTestEditor()}
649
+ </ResizablePanel>
650
+ </ResizablePanelGroup>
651
+ </ResizablePanel>
652
+ </ResizablePanelGroup>
653
+ );
654
+ },
655
+ );
656
+
657
+ const CreateFucntionTestSuiteModal = observer(
658
+ (props: { functiontestableEditorState: FunctionTestableState }) => {
659
+ const { functiontestableEditorState } = props;
660
+ const inputRef = useRef<HTMLInputElement>(null);
661
+ const handleEnter = (): void => inputRef.current?.focus();
662
+ const [suiteName, setSuiteName] = useState<string | undefined>(undefined);
663
+ const [testName, setTestName] = useState<string | undefined>(undefined);
664
+ const isValid = suiteName && testName;
665
+
666
+ // model
667
+ const close = (): void => functiontestableEditorState.setCreateSuite(false);
668
+ const create = (): void => {
669
+ if (suiteName && testName) {
670
+ functiontestableEditorState.createSuite(suiteName, testName);
671
+ }
672
+ };
673
+ return (
674
+ <Dialog
675
+ open={true}
676
+ onClose={close}
677
+ TransitionProps={{
678
+ onEnter: handleEnter,
679
+ }}
680
+ classes={{ container: 'search-modal__container' }}
681
+ PaperProps={{ classes: { root: 'search-modal__inner-container' } }}
682
+ >
683
+ <Modal darkMode={true}>
684
+ <ModalHeader>
685
+ <ModalTitle title="Create Mapping Test Suite" />
686
+ </ModalHeader>
687
+ <ModalBody>
688
+ <PanelFormTextField
689
+ ref={inputRef}
690
+ name="Test Suite Name"
691
+ prompt="Unique Identifier for Test suite i.e Person_suite"
692
+ value={suiteName}
693
+ placeholder="Suite Name"
694
+ update={(value: string | undefined): void =>
695
+ setSuiteName(value ?? '')
696
+ }
697
+ errorMessage={validateTestableId(suiteName, undefined)}
698
+ />
699
+ <PanelFormTextField
700
+ name="Test Name"
701
+ prompt="Unique Identifier for first test in suite"
702
+ placeholder="Test Name"
703
+ value={testName}
704
+ update={(value: string | undefined): void =>
705
+ setTestName(value ?? '')
706
+ }
707
+ errorMessage={validateTestableId(testName, undefined)}
708
+ />
709
+ </ModalBody>
710
+ <ModalFooter>
711
+ <ModalFooterButton
712
+ disabled={!isValid}
713
+ title={
714
+ !isValid
715
+ ? 'Suite Name and Test Name Required'
716
+ : 'Create Test Suite'
717
+ }
718
+ onClick={create}
719
+ text="Create"
720
+ />
721
+ <ModalFooterButton onClick={close} text="Close" />
722
+ </ModalFooter>
723
+ </Modal>
724
+ </Dialog>
725
+ );
726
+ },
727
+ );
728
+
729
+ export const FunctionTestableEditor = observer(
730
+ (props: { functionTestableState: FunctionTestableState }) => {
731
+ const { functionTestableState } = props;
732
+ const suites = functionTestableState.function.tests;
733
+ const functionEditorState = functionTestableState.functionEditorState;
734
+ const isReadOnly = functionEditorState.isReadOnly;
735
+ const selectedSuiteState = functionTestableState.selectedTestSuite;
736
+ // use effect
737
+ useEffect(() => {
738
+ functionTestableState.init();
739
+ }, [functionTestableState]);
740
+
741
+ const runSuites = (): void => {
742
+ functionTestableState.runTestable();
743
+ };
744
+
745
+ const runFailingTests = (): void => {
746
+ functionTestableState.runAllFailingSuites();
747
+ };
748
+ const addSuite = (): void => {
749
+ functionTestableState.setCreateSuite(true);
750
+ };
751
+
752
+ const renderSuiteState = (): React.ReactNode => {
753
+ if (selectedSuiteState) {
754
+ return (
755
+ <FunctionTestSuiteEditor
756
+ functionTestSuiteState={selectedSuiteState}
757
+ />
758
+ );
759
+ } else if (!suites.length) {
760
+ return (
761
+ <BlankPanelPlaceholder
762
+ text="Add Test Suite"
763
+ onClick={addSuite}
764
+ clickActionType="add"
765
+ tooltipText="Click to add test suite"
766
+ />
767
+ );
768
+ }
769
+ return null;
770
+ };
771
+
772
+ const renameTestingComponent = (val: string): void => {
773
+ functionTestableState.renameTestableComponent(val);
774
+ };
775
+ useApplicationNavigationContext(
776
+ LEGEND_STUDIO_APPLICATION_NAVIGATION_CONTEXT_KEY.MAPPING_EDITOR_TEST,
777
+ );
778
+
779
+ return (
780
+ <div className="service-test-suite-editor panel">
781
+ <div className="service-test-suite-editor">
782
+ <ResizablePanelGroup orientation="vertical">
783
+ <ResizablePanel size={200} minSize={28}>
784
+ <div className="binding-editor__header">
785
+ <div className="binding-editor__header__title">
786
+ <div className="panel__header__title__content">
787
+ Test Suites
788
+ </div>
789
+ </div>
790
+
791
+ <div className="panel__header__actions">
792
+ <button
793
+ className="panel__header__action testable-test-explorer__play__all__icon"
794
+ tabIndex={-1}
795
+ onClick={runSuites}
796
+ title="Run All Suites"
797
+ >
798
+ <RunAllIcon />
799
+ </button>
800
+ <button
801
+ className="panel__header__action testable-test-explorer__play__all__icon"
802
+ tabIndex={-1}
803
+ onClick={runFailingTests}
804
+ title="Run All Failing Tests"
805
+ >
806
+ <RunErrorsIcon />
807
+ </button>
808
+ <button
809
+ className="panel__header__action"
810
+ tabIndex={-1}
811
+ onClick={addSuite}
812
+ title="Add Function Suite"
813
+ >
814
+ <PlusIcon />
815
+ </button>
816
+ </div>
817
+ </div>
818
+ <PanelContent>
819
+ {suites.map((suite) => (
820
+ <FunctionTestSuiteItem
821
+ key={suite.id}
822
+ functionTestableState={functionTestableState}
823
+ suite={suite}
824
+ />
825
+ ))}
826
+ {!suites.length && (
827
+ <BlankPanelPlaceholder
828
+ text="Add Test Suite"
829
+ onClick={addSuite}
830
+ clickActionType="add"
831
+ tooltipText="Click to add test suite"
832
+ />
833
+ )}
834
+ </PanelContent>
835
+ {!suites.length && (
836
+ <BlankPanelPlaceholder
837
+ disabled={functionEditorState.isReadOnly}
838
+ onClick={addSuite}
839
+ text="Add a Test Suite"
840
+ clickActionType="add"
841
+ tooltipText="Click to add a new function test suite"
842
+ />
843
+ )}
844
+ </ResizablePanel>
845
+ <ResizablePanelSplitter>
846
+ <ResizablePanelSplitterLine color="var(--color-dark-grey-200)" />
847
+ </ResizablePanelSplitter>
848
+ <ResizablePanel minSize={56}>
849
+ <div className="panel mapping-testable-editorr">
850
+ <div className="mapping-testable-editor__content">
851
+ {renderSuiteState()}
852
+ </div>
853
+ </div>
854
+ </ResizablePanel>
855
+ </ResizablePanelGroup>
856
+ {functionTestableState.createSuiteModal && (
857
+ <CreateFucntionTestSuiteModal
858
+ functiontestableEditorState={functionTestableState}
859
+ />
860
+ )}
861
+ {functionTestableState.testableComponentToRename && (
862
+ <RenameModal
863
+ val={functionTestableState.testableComponentToRename.id}
864
+ isReadOnly={isReadOnly}
865
+ showModal={true}
866
+ closeModal={(): void =>
867
+ functionTestableState.setRenameComponent(undefined)
868
+ }
869
+ setValue={(val: string): void => renameTestingComponent(val)}
870
+ errorMessageFunc={(_val: string | undefined) =>
871
+ validateTestableId(_val, undefined)
872
+ }
873
+ />
874
+ )}
875
+ </div>
876
+ </div>
877
+ );
878
+ },
879
+ );