@finos/legend-application-studio 28.21.23 → 28.21.24
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.
- package/lib/__lib__/LegendStudioDocumentation.d.ts +2 -2
- package/lib/__lib__/LegendStudioDocumentation.d.ts.map +1 -1
- package/lib/__lib__/LegendStudioDocumentation.js +1 -1
- package/lib/__lib__/LegendStudioDocumentation.js.map +1 -1
- package/lib/components/editor/editor-group/data-editor/RelationElementsDataEditor.d.ts.map +1 -1
- package/lib/components/editor/editor-group/data-editor/RelationElementsDataEditor.js +2 -5
- package/lib/components/editor/editor-group/data-editor/RelationElementsDataEditor.js.map +1 -1
- package/lib/components/editor/editor-group/dataProduct/testable/DataProductTestableEditor.d.ts.map +1 -1
- package/lib/components/editor/editor-group/dataProduct/testable/DataProductTestableEditor.js +13 -239
- package/lib/components/editor/editor-group/dataProduct/testable/DataProductTestableEditor.js.map +1 -1
- package/lib/components/editor/editor-group/ingest-editor/IngestDefinitionEditor.d.ts.map +1 -1
- package/lib/components/editor/editor-group/ingest-editor/IngestDefinitionEditor.js +17 -3
- package/lib/components/editor/editor-group/ingest-editor/IngestDefinitionEditor.js.map +1 -1
- package/lib/components/editor/editor-group/ingest-editor/testable/IngestTestableEditor.d.ts +22 -0
- package/lib/components/editor/editor-group/ingest-editor/testable/IngestTestableEditor.d.ts.map +1 -0
- package/lib/components/editor/editor-group/ingest-editor/testable/IngestTestableEditor.js +134 -0
- package/lib/components/editor/editor-group/ingest-editor/testable/IngestTestableEditor.js.map +1 -0
- package/lib/components/editor/editor-group/testable/LakehouseTestableEditor.d.ts +89 -0
- package/lib/components/editor/editor-group/testable/LakehouseTestableEditor.d.ts.map +1 -0
- package/lib/components/editor/editor-group/testable/LakehouseTestableEditor.js +144 -0
- package/lib/components/editor/editor-group/testable/LakehouseTestableEditor.js.map +1 -0
- package/lib/components/editor/editor-group/testable/TestableSharedComponents.d.ts.map +1 -1
- package/lib/components/editor/editor-group/testable/TestableSharedComponents.js +14 -8
- package/lib/components/editor/editor-group/testable/TestableSharedComponents.js.map +1 -1
- package/lib/components/editor/ingest-definition-editor.css +1 -1
- package/lib/components/editor/ingest-definition-editor.css.map +1 -1
- package/lib/components/project-reviewer/ProjectReviewer.d.ts.map +1 -1
- package/lib/components/project-reviewer/ProjectReviewer.js +2 -1
- package/lib/components/project-reviewer/ProjectReviewer.js.map +1 -1
- package/lib/components/project-view/ProjectViewer.d.ts.map +1 -1
- package/lib/components/project-view/ProjectViewer.js +2 -2
- package/lib/components/project-view/ProjectViewer.js.map +1 -1
- package/lib/components/showcase/ShowcaseViewer.d.ts.map +1 -1
- package/lib/components/showcase/ShowcaseViewer.js +2 -2
- package/lib/components/showcase/ShowcaseViewer.js.map +1 -1
- package/lib/components/workspace-setup/WorkspaceSetup.d.ts +1 -1
- package/lib/components/workspace-setup/WorkspaceSetup.d.ts.map +1 -1
- package/lib/components/workspace-setup/WorkspaceSetup.js +98 -43
- package/lib/components/workspace-setup/WorkspaceSetup.js.map +1 -1
- package/lib/index.css +2 -2
- package/lib/index.css.map +1 -1
- package/lib/package.json +1 -1
- package/lib/stores/editor/editor-state/element-editor-state/data/EmbeddedDataState.d.ts.map +1 -1
- package/lib/stores/editor/editor-state/element-editor-state/data/EmbeddedDataState.js +1 -2
- package/lib/stores/editor/editor-state/element-editor-state/data/EmbeddedDataState.js.map +1 -1
- package/lib/stores/editor/editor-state/element-editor-state/dataProduct/DataProductEditorState.d.ts.map +1 -1
- package/lib/stores/editor/editor-state/element-editor-state/dataProduct/DataProductEditorState.js +1 -0
- package/lib/stores/editor/editor-state/element-editor-state/dataProduct/DataProductEditorState.js.map +1 -1
- package/lib/stores/editor/editor-state/element-editor-state/dataProduct/testable/DataProductTestableState.d.ts +8 -30
- package/lib/stores/editor/editor-state/element-editor-state/dataProduct/testable/DataProductTestableState.d.ts.map +1 -1
- package/lib/stores/editor/editor-state/element-editor-state/dataProduct/testable/DataProductTestableState.js +7 -147
- package/lib/stores/editor/editor-state/element-editor-state/dataProduct/testable/DataProductTestableState.js.map +1 -1
- package/lib/stores/editor/editor-state/element-editor-state/function-activator/testable/FunctionTestableState.d.ts.map +1 -1
- package/lib/stores/editor/editor-state/element-editor-state/function-activator/testable/FunctionTestableState.js +32 -28
- package/lib/stores/editor/editor-state/element-editor-state/function-activator/testable/FunctionTestableState.js.map +1 -1
- package/lib/stores/editor/editor-state/element-editor-state/ingest/IngestDefinitionEditorState.d.ts +8 -0
- package/lib/stores/editor/editor-state/element-editor-state/ingest/IngestDefinitionEditorState.d.ts.map +1 -1
- package/lib/stores/editor/editor-state/element-editor-state/ingest/IngestDefinitionEditorState.js +15 -0
- package/lib/stores/editor/editor-state/element-editor-state/ingest/IngestDefinitionEditorState.js.map +1 -1
- package/lib/stores/editor/editor-state/element-editor-state/ingest/IngestTestableState.d.ts +86 -0
- package/lib/stores/editor/editor-state/element-editor-state/ingest/IngestTestableState.d.ts.map +1 -0
- package/lib/stores/editor/editor-state/element-editor-state/ingest/IngestTestableState.js +420 -0
- package/lib/stores/editor/editor-state/element-editor-state/ingest/IngestTestableState.js.map +1 -0
- package/lib/stores/editor/editor-state/element-editor-state/testable/LakehouseTestableUtils.d.ts +62 -0
- package/lib/stores/editor/editor-state/element-editor-state/testable/LakehouseTestableUtils.d.ts.map +1 -0
- package/lib/stores/editor/editor-state/element-editor-state/testable/LakehouseTestableUtils.js +138 -0
- package/lib/stores/editor/editor-state/element-editor-state/testable/LakehouseTestableUtils.js.map +1 -0
- package/lib/stores/workspace-setup/WorkspaceSetupStore.d.ts +10 -1
- package/lib/stores/workspace-setup/WorkspaceSetupStore.d.ts.map +1 -1
- package/lib/stores/workspace-setup/WorkspaceSetupStore.js +105 -12
- package/lib/stores/workspace-setup/WorkspaceSetupStore.js.map +1 -1
- package/package.json +11 -11
- package/src/__lib__/LegendStudioDocumentation.ts +1 -1
- package/src/components/editor/editor-group/data-editor/RelationElementsDataEditor.tsx +2 -5
- package/src/components/editor/editor-group/dataProduct/testable/DataProductTestableEditor.tsx +19 -869
- package/src/components/editor/editor-group/ingest-editor/IngestDefinitionEditor.tsx +90 -34
- package/src/components/editor/editor-group/ingest-editor/testable/IngestTestableEditor.tsx +418 -0
- package/src/components/editor/editor-group/testable/LakehouseTestableEditor.tsx +564 -0
- package/src/components/editor/editor-group/testable/TestableSharedComponents.tsx +17 -9
- package/src/components/project-reviewer/ProjectReviewer.tsx +2 -0
- package/src/components/project-view/ProjectViewer.tsx +2 -1
- package/src/components/showcase/ShowcaseViewer.tsx +2 -1
- package/src/components/workspace-setup/WorkspaceSetup.tsx +169 -75
- package/src/stores/editor/editor-state/element-editor-state/data/EmbeddedDataState.ts +1 -2
- package/src/stores/editor/editor-state/element-editor-state/dataProduct/DataProductEditorState.ts +1 -0
- package/src/stores/editor/editor-state/element-editor-state/dataProduct/testable/DataProductTestableState.ts +18 -204
- package/src/stores/editor/editor-state/element-editor-state/function-activator/testable/FunctionTestableState.ts +41 -36
- package/src/stores/editor/editor-state/element-editor-state/ingest/IngestDefinitionEditorState.ts +16 -0
- package/src/stores/editor/editor-state/element-editor-state/ingest/IngestTestableState.ts +654 -0
- package/src/stores/editor/editor-state/element-editor-state/testable/LakehouseTestableUtils.ts +224 -0
- package/src/stores/workspace-setup/WorkspaceSetupStore.ts +125 -8
- package/tsconfig.json +4 -0
|
@@ -0,0 +1,564 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2026-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
|
+
/**
|
|
18
|
+
* Shared React components for Lakehouse test editors (DataProduct and Ingest).
|
|
19
|
+
*
|
|
20
|
+
* Exports:
|
|
21
|
+
* - LakehouseElementTestDataItem — sidebar row for a single element's test data
|
|
22
|
+
* - LakehouseElementTestDataEditor — editor for a selected element's RelationElementsData
|
|
23
|
+
* - LakehouseAddElementModal — modal to add a new element to suite test data
|
|
24
|
+
* - LakehouseTestDataEditor — full test-data panel (element list + editor)
|
|
25
|
+
* - LakehouseTestEditor — assertion viewer for a single test
|
|
26
|
+
* - LakehouseTestsEditor — tests list + test-detail panel
|
|
27
|
+
* - LakehouseTestSuiteEditor — horizontal split: test data + tests
|
|
28
|
+
* - LakehouseSuiteStateForEditor — interface for suite state passed to shared components
|
|
29
|
+
* - LakehouseTestableStateForEditor — interface for top-level testable state
|
|
30
|
+
*/
|
|
31
|
+
|
|
32
|
+
import { observer } from 'mobx-react-lite';
|
|
33
|
+
import { flowResult } from 'mobx';
|
|
34
|
+
import {
|
|
35
|
+
BlankPanelContent,
|
|
36
|
+
BlankPanelPlaceholder,
|
|
37
|
+
clsx,
|
|
38
|
+
CustomSelectorInput,
|
|
39
|
+
Dialog,
|
|
40
|
+
ErrorWarnIcon,
|
|
41
|
+
Modal,
|
|
42
|
+
ModalBody,
|
|
43
|
+
ModalFooter,
|
|
44
|
+
ModalFooterButton,
|
|
45
|
+
ModalHeader,
|
|
46
|
+
ModalTitle,
|
|
47
|
+
PlusIcon,
|
|
48
|
+
PlayIcon,
|
|
49
|
+
ResizablePanel,
|
|
50
|
+
ResizablePanelGroup,
|
|
51
|
+
ResizablePanelSplitter,
|
|
52
|
+
ResizablePanelSplitterLine,
|
|
53
|
+
RunAllIcon,
|
|
54
|
+
TimesIcon,
|
|
55
|
+
} from '@finos/legend-art';
|
|
56
|
+
import type { AtomicTest, TestSuite } from '@finos/legend-graph';
|
|
57
|
+
import type { ActionState, GeneratorFn } from '@finos/legend-shared';
|
|
58
|
+
import { useState } from 'react';
|
|
59
|
+
import type { TestableTestEditorState } from '../../../../stores/editor/editor-state/element-editor-state/testable/TestableEditorState.js';
|
|
60
|
+
import {
|
|
61
|
+
TESTABLE_RESULT,
|
|
62
|
+
getTestableResultFromTestResult,
|
|
63
|
+
} from '../../../../stores/editor/sidebar-state/testable/GlobalTestRunnerState.js';
|
|
64
|
+
import { getTestableResultIcon } from '../../side-bar/testable/GlobalTestRunner.js';
|
|
65
|
+
import type {
|
|
66
|
+
LakehouseElementTestDataState,
|
|
67
|
+
LakehouseTestDataStateBase,
|
|
68
|
+
} from '../../../../stores/editor/editor-state/element-editor-state/testable/LakehouseTestableUtils.js';
|
|
69
|
+
import { RelationElementsDataEditor } from '../data-editor/RelationElementsDataEditor.js';
|
|
70
|
+
import { TestAssertionEditor } from './TestableSharedComponents.js';
|
|
71
|
+
import type { EditorStore } from '../../../../stores/editor/EditorStore.js';
|
|
72
|
+
|
|
73
|
+
// ─── Public interfaces ────────────────────────────────────────────────────────
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Minimal interface for a suite state object accepted by LakehouseTestsEditor
|
|
77
|
+
* and LakehouseTestSuiteEditor. Satisfied by both IngestTestSuiteState and
|
|
78
|
+
* DataProductTestSuiteState.
|
|
79
|
+
*/
|
|
80
|
+
export interface LakehouseSuiteStateForEditor {
|
|
81
|
+
testDataState: LakehouseTestDataStateBase;
|
|
82
|
+
testStates: TestableTestEditorState[];
|
|
83
|
+
selectTestState: TestableTestEditorState | undefined;
|
|
84
|
+
runningSuiteState: ActionState;
|
|
85
|
+
runSuite(): GeneratorFn<void>;
|
|
86
|
+
changeTest(test: AtomicTest): void;
|
|
87
|
+
deleteTest(test: AtomicTest): void;
|
|
88
|
+
suite: TestSuite;
|
|
89
|
+
editorStore: EditorStore;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Minimal interface for the top-level testable state passed to the tests editor.
|
|
94
|
+
* Satisfied by both IngestTestableState and DataProductTestableState.
|
|
95
|
+
*/
|
|
96
|
+
export interface LakehouseTestableStateForEditor {
|
|
97
|
+
setShowCreateTestModal(val: boolean): void;
|
|
98
|
+
editorStore: EditorStore;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// ─── Element test data: list item ─────────────────────────────────────────────
|
|
102
|
+
|
|
103
|
+
export const LakehouseElementTestDataItem = observer(
|
|
104
|
+
(props: {
|
|
105
|
+
elementState: LakehouseElementTestDataState;
|
|
106
|
+
testDataState: LakehouseTestDataStateBase;
|
|
107
|
+
isReadOnly: boolean;
|
|
108
|
+
}) => {
|
|
109
|
+
const { elementState, testDataState, isReadOnly } = props;
|
|
110
|
+
const isActive =
|
|
111
|
+
testDataState.selectedElementTestDataState === elementState;
|
|
112
|
+
|
|
113
|
+
const select = (): void =>
|
|
114
|
+
testDataState.setSelectedElementTestDataState(elementState);
|
|
115
|
+
|
|
116
|
+
return (
|
|
117
|
+
<div
|
|
118
|
+
className={clsx('testable-test-explorer__item', {
|
|
119
|
+
'testable-test-explorer__item--active': isActive,
|
|
120
|
+
})}
|
|
121
|
+
>
|
|
122
|
+
<div
|
|
123
|
+
className="testable-test-explorer__item__label"
|
|
124
|
+
onClick={select}
|
|
125
|
+
tabIndex={-1}
|
|
126
|
+
>
|
|
127
|
+
<div className="testable-test-explorer__item__label__text">
|
|
128
|
+
<span title={elementState.element.path}>
|
|
129
|
+
{elementState.element.name}
|
|
130
|
+
</span>
|
|
131
|
+
</div>
|
|
132
|
+
{!isReadOnly && (
|
|
133
|
+
<div className="mapping-test-explorer__item__actions">
|
|
134
|
+
<button
|
|
135
|
+
className="mapping-test-explorer__item__action"
|
|
136
|
+
onClick={(e): void => {
|
|
137
|
+
e.stopPropagation();
|
|
138
|
+
testDataState.deleteElement(elementState);
|
|
139
|
+
}}
|
|
140
|
+
tabIndex={-1}
|
|
141
|
+
title="Delete"
|
|
142
|
+
>
|
|
143
|
+
<TimesIcon />
|
|
144
|
+
</button>
|
|
145
|
+
</div>
|
|
146
|
+
)}
|
|
147
|
+
</div>
|
|
148
|
+
</div>
|
|
149
|
+
);
|
|
150
|
+
},
|
|
151
|
+
);
|
|
152
|
+
|
|
153
|
+
// ─── Element test data: editor ────────────────────────────────────────────────
|
|
154
|
+
|
|
155
|
+
export const LakehouseElementTestDataEditor = observer(
|
|
156
|
+
(props: {
|
|
157
|
+
elementState: LakehouseElementTestDataState;
|
|
158
|
+
isReadOnly: boolean;
|
|
159
|
+
}) => {
|
|
160
|
+
const { elementState, isReadOnly } = props;
|
|
161
|
+
const dataState = elementState.relationElementsDataState;
|
|
162
|
+
|
|
163
|
+
if (!dataState) {
|
|
164
|
+
return (
|
|
165
|
+
<BlankPanelContent>No relation data for this element</BlankPanelContent>
|
|
166
|
+
);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
return (
|
|
170
|
+
<RelationElementsDataEditor
|
|
171
|
+
dataState={dataState}
|
|
172
|
+
isReadOnly={isReadOnly}
|
|
173
|
+
hideColumnDefinitions={true}
|
|
174
|
+
/>
|
|
175
|
+
);
|
|
176
|
+
},
|
|
177
|
+
);
|
|
178
|
+
|
|
179
|
+
// ─── Add element modal ────────────────────────────────────────────────────────
|
|
180
|
+
|
|
181
|
+
export const LakehouseAddElementModal = observer(
|
|
182
|
+
(props: { testDataState: LakehouseTestDataStateBase }) => {
|
|
183
|
+
const { testDataState } = props;
|
|
184
|
+
const applicationStore = testDataState.editorStore.applicationStore;
|
|
185
|
+
const options = testDataState.availableElementsToAdd.map((e) => ({
|
|
186
|
+
value: e.path,
|
|
187
|
+
label: e.path,
|
|
188
|
+
}));
|
|
189
|
+
const [selectedPath, setSelectedPath] = useState<string | undefined>(
|
|
190
|
+
options[0]?.value,
|
|
191
|
+
);
|
|
192
|
+
const close = (): void => testDataState.setShowAddElementModal(false);
|
|
193
|
+
const add = (): void => {
|
|
194
|
+
if (selectedPath) {
|
|
195
|
+
testDataState.addElement(selectedPath);
|
|
196
|
+
close();
|
|
197
|
+
}
|
|
198
|
+
};
|
|
199
|
+
const onChange = (val: { label: string; value: string } | null): void => {
|
|
200
|
+
setSelectedPath(val?.value);
|
|
201
|
+
};
|
|
202
|
+
|
|
203
|
+
return (
|
|
204
|
+
<Dialog
|
|
205
|
+
open={testDataState.showAddElementModal}
|
|
206
|
+
onClose={close}
|
|
207
|
+
classes={{ container: 'search-modal__container' }}
|
|
208
|
+
slotProps={{
|
|
209
|
+
paper: { classes: { root: 'search-modal__inner-container' } },
|
|
210
|
+
}}
|
|
211
|
+
>
|
|
212
|
+
<Modal
|
|
213
|
+
darkMode={
|
|
214
|
+
!applicationStore.layoutService.TEMPORARY__isLightColorThemeEnabled
|
|
215
|
+
}
|
|
216
|
+
>
|
|
217
|
+
<ModalHeader>
|
|
218
|
+
<ModalTitle title="Add Element" />
|
|
219
|
+
</ModalHeader>
|
|
220
|
+
<ModalBody>
|
|
221
|
+
<CustomSelectorInput
|
|
222
|
+
className="panel__content__form__section__dropdown"
|
|
223
|
+
options={options}
|
|
224
|
+
onChange={onChange}
|
|
225
|
+
value={
|
|
226
|
+
selectedPath
|
|
227
|
+
? { value: selectedPath, label: selectedPath }
|
|
228
|
+
: null
|
|
229
|
+
}
|
|
230
|
+
placeholder="Select element..."
|
|
231
|
+
darkMode={
|
|
232
|
+
!applicationStore.layoutService
|
|
233
|
+
.TEMPORARY__isLightColorThemeEnabled
|
|
234
|
+
}
|
|
235
|
+
/>
|
|
236
|
+
</ModalBody>
|
|
237
|
+
<ModalFooter>
|
|
238
|
+
<ModalFooterButton
|
|
239
|
+
disabled={!selectedPath}
|
|
240
|
+
onClick={add}
|
|
241
|
+
text="Add"
|
|
242
|
+
/>
|
|
243
|
+
<ModalFooterButton onClick={close} text="Close" type="secondary" />
|
|
244
|
+
</ModalFooter>
|
|
245
|
+
</Modal>
|
|
246
|
+
</Dialog>
|
|
247
|
+
);
|
|
248
|
+
},
|
|
249
|
+
);
|
|
250
|
+
|
|
251
|
+
// ─── Test data editor (full panel: element list + per-element editor) ─────────
|
|
252
|
+
|
|
253
|
+
export const LakehouseTestDataEditor = observer(
|
|
254
|
+
(props: {
|
|
255
|
+
testDataState: LakehouseTestDataStateBase;
|
|
256
|
+
isReadOnly: boolean;
|
|
257
|
+
}) => {
|
|
258
|
+
const { testDataState, isReadOnly } = props;
|
|
259
|
+
|
|
260
|
+
const addElement = (): void => {
|
|
261
|
+
if (testDataState.availableElementsToAdd.length === 0) {
|
|
262
|
+
testDataState.editorStore.applicationStore.notificationService.notifyWarning(
|
|
263
|
+
'No elements available to add',
|
|
264
|
+
);
|
|
265
|
+
return;
|
|
266
|
+
}
|
|
267
|
+
testDataState.setShowAddElementModal(true);
|
|
268
|
+
};
|
|
269
|
+
|
|
270
|
+
const hasTestData = testDataState.elementTestDataStates.length > 0;
|
|
271
|
+
|
|
272
|
+
return (
|
|
273
|
+
<div
|
|
274
|
+
className={clsx('service-test-data-editor panel', {
|
|
275
|
+
'service-test-data-editor--no-data': !hasTestData,
|
|
276
|
+
})}
|
|
277
|
+
>
|
|
278
|
+
<div className="service-test-data-editor__data">
|
|
279
|
+
<ResizablePanelGroup orientation="vertical">
|
|
280
|
+
<ResizablePanel minSize={100} size={180}>
|
|
281
|
+
<div className="binding-editor__header">
|
|
282
|
+
<div className="binding-editor__header__title">
|
|
283
|
+
<div className="panel__header__title__content">Test Data</div>
|
|
284
|
+
</div>
|
|
285
|
+
{!isReadOnly && (
|
|
286
|
+
<div className="panel__header__actions">
|
|
287
|
+
<button
|
|
288
|
+
className="panel__header__action"
|
|
289
|
+
tabIndex={-1}
|
|
290
|
+
onClick={addElement}
|
|
291
|
+
title="Add Element"
|
|
292
|
+
>
|
|
293
|
+
<PlusIcon />
|
|
294
|
+
</button>
|
|
295
|
+
</div>
|
|
296
|
+
)}
|
|
297
|
+
</div>
|
|
298
|
+
{!hasTestData ? (
|
|
299
|
+
<div className="service-test-data-editor__warning">
|
|
300
|
+
<ErrorWarnIcon />
|
|
301
|
+
<span>Add an element to configure test data</span>
|
|
302
|
+
</div>
|
|
303
|
+
) : (
|
|
304
|
+
<div>
|
|
305
|
+
{testDataState.elementTestDataStates.map((elementState) => (
|
|
306
|
+
<LakehouseElementTestDataItem
|
|
307
|
+
key={elementState.element.path}
|
|
308
|
+
elementState={elementState}
|
|
309
|
+
testDataState={testDataState}
|
|
310
|
+
isReadOnly={isReadOnly}
|
|
311
|
+
/>
|
|
312
|
+
))}
|
|
313
|
+
</div>
|
|
314
|
+
)}
|
|
315
|
+
</ResizablePanel>
|
|
316
|
+
<ResizablePanelSplitter>
|
|
317
|
+
<ResizablePanelSplitterLine color="var(--color-dark-grey-200)" />
|
|
318
|
+
</ResizablePanelSplitter>
|
|
319
|
+
<ResizablePanel minSize={200}>
|
|
320
|
+
{testDataState.selectedElementTestDataState ? (
|
|
321
|
+
<LakehouseElementTestDataEditor
|
|
322
|
+
elementState={testDataState.selectedElementTestDataState}
|
|
323
|
+
isReadOnly={isReadOnly}
|
|
324
|
+
/>
|
|
325
|
+
) : (
|
|
326
|
+
<BlankPanelContent>
|
|
327
|
+
Select an element to configure its test data
|
|
328
|
+
</BlankPanelContent>
|
|
329
|
+
)}
|
|
330
|
+
</ResizablePanel>
|
|
331
|
+
</ResizablePanelGroup>
|
|
332
|
+
</div>
|
|
333
|
+
{testDataState.showAddElementModal && (
|
|
334
|
+
<LakehouseAddElementModal testDataState={testDataState} />
|
|
335
|
+
)}
|
|
336
|
+
</div>
|
|
337
|
+
);
|
|
338
|
+
},
|
|
339
|
+
);
|
|
340
|
+
|
|
341
|
+
// ─── Test editor: assertion viewer ────────────────────────────────────────────
|
|
342
|
+
|
|
343
|
+
export const LakehouseTestEditor = observer(
|
|
344
|
+
(props: { testState: TestableTestEditorState; isReadOnly: boolean }) => {
|
|
345
|
+
const { testState } = props;
|
|
346
|
+
const selectedAssertion = testState.selectedAsertionState;
|
|
347
|
+
|
|
348
|
+
return (
|
|
349
|
+
<div className="function-test-editor panel">
|
|
350
|
+
<div className="panel__header">
|
|
351
|
+
<div className="panel__header service-test-editor__header--with-tabs">
|
|
352
|
+
<div className="panel__header__title__content">Assertion</div>
|
|
353
|
+
</div>
|
|
354
|
+
</div>
|
|
355
|
+
<div className="panel">
|
|
356
|
+
{selectedAssertion && (
|
|
357
|
+
<TestAssertionEditor testAssertionState={selectedAssertion} />
|
|
358
|
+
)}
|
|
359
|
+
{!selectedAssertion && (
|
|
360
|
+
<BlankPanelPlaceholder
|
|
361
|
+
text="No assertion"
|
|
362
|
+
tooltipText="No assertion configured for this test"
|
|
363
|
+
/>
|
|
364
|
+
)}
|
|
365
|
+
</div>
|
|
366
|
+
</div>
|
|
367
|
+
);
|
|
368
|
+
},
|
|
369
|
+
);
|
|
370
|
+
|
|
371
|
+
// ─── Test item: list row ──────────────────────────────────────────────────────
|
|
372
|
+
|
|
373
|
+
const LakehouseTestItem = observer(
|
|
374
|
+
(props: {
|
|
375
|
+
testState: TestableTestEditorState;
|
|
376
|
+
suiteState: LakehouseSuiteStateForEditor;
|
|
377
|
+
isReadOnly: boolean;
|
|
378
|
+
}) => {
|
|
379
|
+
const { testState, suiteState, isReadOnly } = props;
|
|
380
|
+
const isActive = suiteState.selectTestState === testState;
|
|
381
|
+
const isRunning = testState.runningTestAction.isInProgress;
|
|
382
|
+
const _testableResult = getTestableResultFromTestResult(
|
|
383
|
+
testState.testResultState.result,
|
|
384
|
+
);
|
|
385
|
+
const testResult = isRunning
|
|
386
|
+
? TESTABLE_RESULT.IN_PROGRESS
|
|
387
|
+
: _testableResult;
|
|
388
|
+
|
|
389
|
+
const select = (): void => suiteState.changeTest(testState.test);
|
|
390
|
+
const runTest = (): void => {
|
|
391
|
+
flowResult(testState.runTest()).catch(
|
|
392
|
+
testState.editorStore.applicationStore.alertUnhandledError,
|
|
393
|
+
);
|
|
394
|
+
};
|
|
395
|
+
const deleteTest = (): void => {
|
|
396
|
+
if (!isReadOnly) {
|
|
397
|
+
suiteState.deleteTest(testState.test);
|
|
398
|
+
}
|
|
399
|
+
};
|
|
400
|
+
|
|
401
|
+
return (
|
|
402
|
+
<div
|
|
403
|
+
className={clsx('testable-test-explorer__item', {
|
|
404
|
+
'testable-test-explorer__item--active': isActive,
|
|
405
|
+
})}
|
|
406
|
+
>
|
|
407
|
+
<div
|
|
408
|
+
className="testable-test-explorer__item__label"
|
|
409
|
+
onClick={select}
|
|
410
|
+
tabIndex={-1}
|
|
411
|
+
>
|
|
412
|
+
<div className="testable-test-explorer__item__label__icon">
|
|
413
|
+
{getTestableResultIcon(testResult)}
|
|
414
|
+
</div>
|
|
415
|
+
<div className="testable-test-explorer__item__label__text">
|
|
416
|
+
{testState.test.id}
|
|
417
|
+
</div>
|
|
418
|
+
</div>
|
|
419
|
+
<div className="mapping-test-explorer__item__actions">
|
|
420
|
+
<button
|
|
421
|
+
className="mapping-test-explorer__item__action mapping-test-explorer__run-test-btn"
|
|
422
|
+
onClick={runTest}
|
|
423
|
+
disabled={isRunning}
|
|
424
|
+
tabIndex={-1}
|
|
425
|
+
title={`Run test ${testState.test.id}`}
|
|
426
|
+
>
|
|
427
|
+
<PlayIcon />
|
|
428
|
+
</button>
|
|
429
|
+
{!isReadOnly && (
|
|
430
|
+
<button
|
|
431
|
+
className="mapping-test-explorer__item__action mapping-test-explorer__run-test-btn"
|
|
432
|
+
onClick={deleteTest}
|
|
433
|
+
tabIndex={-1}
|
|
434
|
+
title={`Delete test ${testState.test.id}`}
|
|
435
|
+
>
|
|
436
|
+
<TimesIcon />
|
|
437
|
+
</button>
|
|
438
|
+
)}
|
|
439
|
+
</div>
|
|
440
|
+
</div>
|
|
441
|
+
);
|
|
442
|
+
},
|
|
443
|
+
);
|
|
444
|
+
|
|
445
|
+
// ─── Tests editor: test list + test detail ────────────────────────────────────
|
|
446
|
+
|
|
447
|
+
export const LakehouseTestsEditor = observer(
|
|
448
|
+
(props: {
|
|
449
|
+
suiteState: LakehouseSuiteStateForEditor;
|
|
450
|
+
testableState: LakehouseTestableStateForEditor;
|
|
451
|
+
isReadOnly: boolean;
|
|
452
|
+
}) => {
|
|
453
|
+
const { suiteState, testableState, isReadOnly } = props;
|
|
454
|
+
const selectedTest = suiteState.selectTestState;
|
|
455
|
+
|
|
456
|
+
return (
|
|
457
|
+
<div className="panel service-test-editor">
|
|
458
|
+
<div className="service-test-editor__content">
|
|
459
|
+
<ResizablePanelGroup orientation="vertical">
|
|
460
|
+
<ResizablePanel minSize={100} size={200}>
|
|
461
|
+
<div className="binding-editor__header">
|
|
462
|
+
<div className="binding-editor__header__title">
|
|
463
|
+
<div className="panel__header__title__content">Tests</div>
|
|
464
|
+
</div>
|
|
465
|
+
<div className="panel__header__actions">
|
|
466
|
+
<button
|
|
467
|
+
className="panel__header__action testable-test-explorer__play__all__icon"
|
|
468
|
+
tabIndex={-1}
|
|
469
|
+
onClick={(): void => {
|
|
470
|
+
flowResult(suiteState.runSuite()).catch(
|
|
471
|
+
testableState.editorStore.applicationStore
|
|
472
|
+
.alertUnhandledError,
|
|
473
|
+
);
|
|
474
|
+
}}
|
|
475
|
+
disabled={
|
|
476
|
+
suiteState.runningSuiteState.isInProgress ||
|
|
477
|
+
suiteState.suite.tests.length === 0
|
|
478
|
+
}
|
|
479
|
+
title="Run all tests in this suite"
|
|
480
|
+
>
|
|
481
|
+
<RunAllIcon />
|
|
482
|
+
</button>
|
|
483
|
+
{!isReadOnly && (
|
|
484
|
+
<button
|
|
485
|
+
className="panel__header__action"
|
|
486
|
+
tabIndex={-1}
|
|
487
|
+
onClick={(): void =>
|
|
488
|
+
testableState.setShowCreateTestModal(true)
|
|
489
|
+
}
|
|
490
|
+
title="Add test to this suite"
|
|
491
|
+
>
|
|
492
|
+
<PlusIcon />
|
|
493
|
+
</button>
|
|
494
|
+
)}
|
|
495
|
+
</div>
|
|
496
|
+
</div>
|
|
497
|
+
<div>
|
|
498
|
+
{suiteState.testStates.map((testState) => (
|
|
499
|
+
<LakehouseTestItem
|
|
500
|
+
key={testState.test.id}
|
|
501
|
+
testState={testState}
|
|
502
|
+
suiteState={suiteState}
|
|
503
|
+
isReadOnly={isReadOnly}
|
|
504
|
+
/>
|
|
505
|
+
))}
|
|
506
|
+
</div>
|
|
507
|
+
</ResizablePanel>
|
|
508
|
+
<ResizablePanelSplitter>
|
|
509
|
+
<ResizablePanelSplitterLine color="var(--color-dark-grey-200)" />
|
|
510
|
+
</ResizablePanelSplitter>
|
|
511
|
+
<ResizablePanel minSize={56}>
|
|
512
|
+
{selectedTest ? (
|
|
513
|
+
<LakehouseTestEditor
|
|
514
|
+
testState={selectedTest}
|
|
515
|
+
isReadOnly={isReadOnly}
|
|
516
|
+
/>
|
|
517
|
+
) : (
|
|
518
|
+
<BlankPanelPlaceholder
|
|
519
|
+
text="Select a test"
|
|
520
|
+
tooltipText="Select a test from the list above"
|
|
521
|
+
/>
|
|
522
|
+
)}
|
|
523
|
+
</ResizablePanel>
|
|
524
|
+
</ResizablePanelGroup>
|
|
525
|
+
</div>
|
|
526
|
+
</div>
|
|
527
|
+
);
|
|
528
|
+
},
|
|
529
|
+
);
|
|
530
|
+
|
|
531
|
+
// ─── Suite editor: horizontal split (test data + tests) ───────────────────────
|
|
532
|
+
|
|
533
|
+
export const LakehouseTestSuiteEditor = observer(
|
|
534
|
+
(props: {
|
|
535
|
+
suiteState: LakehouseSuiteStateForEditor;
|
|
536
|
+
testableState: LakehouseTestableStateForEditor;
|
|
537
|
+
isReadOnly: boolean;
|
|
538
|
+
}) => {
|
|
539
|
+
const { suiteState, testableState, isReadOnly } = props;
|
|
540
|
+
|
|
541
|
+
return (
|
|
542
|
+
<div className="service-test-suite-editor">
|
|
543
|
+
<ResizablePanelGroup orientation="horizontal">
|
|
544
|
+
<ResizablePanel size={580} minSize={28}>
|
|
545
|
+
<LakehouseTestDataEditor
|
|
546
|
+
testDataState={suiteState.testDataState}
|
|
547
|
+
isReadOnly={isReadOnly}
|
|
548
|
+
/>
|
|
549
|
+
</ResizablePanel>
|
|
550
|
+
<ResizablePanelSplitter>
|
|
551
|
+
<ResizablePanelSplitterLine color="var(--color-dark-grey-200)" />
|
|
552
|
+
</ResizablePanelSplitter>
|
|
553
|
+
<ResizablePanel minSize={56}>
|
|
554
|
+
<LakehouseTestsEditor
|
|
555
|
+
suiteState={suiteState}
|
|
556
|
+
testableState={testableState}
|
|
557
|
+
isReadOnly={isReadOnly}
|
|
558
|
+
/>
|
|
559
|
+
</ResizablePanel>
|
|
560
|
+
</ResizablePanelGroup>
|
|
561
|
+
</div>
|
|
562
|
+
);
|
|
563
|
+
},
|
|
564
|
+
);
|
|
@@ -40,6 +40,7 @@ import {
|
|
|
40
40
|
import {
|
|
41
41
|
type DataElement,
|
|
42
42
|
DataProduct,
|
|
43
|
+
IngestDefinition,
|
|
43
44
|
type ValueSpecification,
|
|
44
45
|
type VariableExpression,
|
|
45
46
|
type PrimitiveInstanceValue,
|
|
@@ -87,6 +88,7 @@ import {
|
|
|
87
88
|
} from '@finos/legend-query-builder';
|
|
88
89
|
import { useApplicationStore } from '@finos/legend-application';
|
|
89
90
|
import type { DataProductTestState } from '../../../../stores/editor/editor-state/element-editor-state/dataProduct/testable/DataProductTestableState.js';
|
|
91
|
+
import type { IngestTestState } from '../../../../stores/editor/editor-state/element-editor-state/ingest/IngestTestableState.js';
|
|
90
92
|
|
|
91
93
|
export const SharedDataElementModal = observer(
|
|
92
94
|
(props: {
|
|
@@ -601,11 +603,14 @@ const EqualToRelationAsssertionEditor = observer(
|
|
|
601
603
|
},
|
|
602
604
|
);
|
|
603
605
|
|
|
604
|
-
const
|
|
606
|
+
const LakehouseEqualToRelationAssertionEditor = observer(
|
|
605
607
|
(props: { testAssertionEditorState: TestAssertionEditorState }) => {
|
|
606
608
|
const { testAssertionEditorState } = props;
|
|
607
|
-
const
|
|
608
|
-
testAssertionEditorState.testState
|
|
609
|
+
const isDataProductTest =
|
|
610
|
+
testAssertionEditorState.testState.testable instanceof DataProduct;
|
|
611
|
+
const testState = isDataProductTest
|
|
612
|
+
? (testAssertionEditorState.testState as DataProductTestState)
|
|
613
|
+
: (testAssertionEditorState.testState as IngestTestState);
|
|
609
614
|
const isReadOnly = testAssertionEditorState.testState.isReadOnly;
|
|
610
615
|
const relationElementState = testState.testDataRelationState;
|
|
611
616
|
|
|
@@ -613,7 +618,9 @@ const DataProductEqualToRelationAssertionEditor = observer(
|
|
|
613
618
|
<div className="service-test-data-editor panel">
|
|
614
619
|
<div className="panel__content__form__section">
|
|
615
620
|
<div className="panel__content__form__section__header__label">
|
|
616
|
-
|
|
621
|
+
{isDataProductTest
|
|
622
|
+
? `Access Point: ${(testState as DataProductTestState).accessPointLabel}`
|
|
623
|
+
: `Dataset: ${(testState as IngestTestState).datasetLabel}`}
|
|
617
624
|
</div>
|
|
618
625
|
</div>
|
|
619
626
|
{relationElementState ? (
|
|
@@ -977,8 +984,9 @@ export const TestAssertionEditor = observer(
|
|
|
977
984
|
const { testAssertionState } = props;
|
|
978
985
|
const selectedTab = testAssertionState.selectedTab;
|
|
979
986
|
const isReadOnly = testAssertionState.testState.isReadOnly;
|
|
980
|
-
const
|
|
981
|
-
testAssertionState.testState.testable instanceof DataProduct
|
|
987
|
+
const isLakehouseTest =
|
|
988
|
+
testAssertionState.testState.testable instanceof DataProduct ||
|
|
989
|
+
testAssertionState.testState.testable instanceof IngestDefinition;
|
|
982
990
|
const isDisabled =
|
|
983
991
|
isReadOnly ||
|
|
984
992
|
!testAssertionState.assertionState.supportsGeneratingAssertion ||
|
|
@@ -1001,9 +1009,9 @@ export const TestAssertionEditor = observer(
|
|
|
1001
1009
|
/>
|
|
1002
1010
|
);
|
|
1003
1011
|
} else if (state instanceof EqualToRelationAssertionState) {
|
|
1004
|
-
if (
|
|
1012
|
+
if (isLakehouseTest) {
|
|
1005
1013
|
return (
|
|
1006
|
-
<
|
|
1014
|
+
<LakehouseEqualToRelationAssertionEditor
|
|
1007
1015
|
testAssertionEditorState={testAssertionState}
|
|
1008
1016
|
/>
|
|
1009
1017
|
);
|
|
@@ -1045,7 +1053,7 @@ export const TestAssertionEditor = observer(
|
|
|
1045
1053
|
</div>
|
|
1046
1054
|
))}
|
|
1047
1055
|
</div>
|
|
1048
|
-
{!
|
|
1056
|
+
{!isLakehouseTest && (
|
|
1049
1057
|
<div className="testable-test-assertion-editor__header__actions">
|
|
1050
1058
|
<button
|
|
1051
1059
|
className="panel__header__action service-execution-editor__test-data__generate-btn"
|
|
@@ -44,6 +44,7 @@ import {
|
|
|
44
44
|
useEditorStore,
|
|
45
45
|
withEditorStore,
|
|
46
46
|
} from '../editor/EditorStoreProvider.js';
|
|
47
|
+
import { ColorThemeToggle } from '../editor/ActivityBar.js';
|
|
47
48
|
import { useApplicationStore } from '@finos/legend-application';
|
|
48
49
|
import { useParams } from '@finos/legend-application/browser';
|
|
49
50
|
import { guaranteeNonNullable } from '@finos/legend-shared';
|
|
@@ -220,6 +221,7 @@ export const ProjectReviewer = withEditorStore(
|
|
|
220
221
|
<CheckListIcon />
|
|
221
222
|
</button>
|
|
222
223
|
</div>
|
|
224
|
+
<ColorThemeToggle />
|
|
223
225
|
</div>
|
|
224
226
|
<div className="workspace-review__content-container">
|
|
225
227
|
<div className="workspace-review__content">
|
|
@@ -71,7 +71,7 @@ import {
|
|
|
71
71
|
} from '../LegendStudioFrameworkProvider.js';
|
|
72
72
|
import { EmbeddedQueryBuilder } from '../editor/EmbeddedQueryBuilder.js';
|
|
73
73
|
import type { ActivityBarItemConfig } from '@finos/legend-lego/application';
|
|
74
|
-
import { ActivityBarMenu } from '../editor/ActivityBar.js';
|
|
74
|
+
import { ActivityBarMenu, ColorThemeToggle } from '../editor/ActivityBar.js';
|
|
75
75
|
import { PanelGroup } from '../editor/panel-group/PanelGroup.js';
|
|
76
76
|
import { StoreProjectData } from '@finos/legend-server-depot';
|
|
77
77
|
import { generateGAVCoordinates } from '@finos/legend-storage';
|
|
@@ -425,6 +425,7 @@ const ProjectViewerActivityBar = observer(() => {
|
|
|
425
425
|
</button>
|
|
426
426
|
))}
|
|
427
427
|
</div>
|
|
428
|
+
<ColorThemeToggle />
|
|
428
429
|
</div>
|
|
429
430
|
);
|
|
430
431
|
});
|
|
@@ -55,7 +55,7 @@ import {
|
|
|
55
55
|
ACTIVITY_MODE,
|
|
56
56
|
GRAPH_EDITOR_MODE,
|
|
57
57
|
} from '../../stores/editor/EditorConfig.js';
|
|
58
|
-
import { ActivityBarMenu } from '../editor/ActivityBar.js';
|
|
58
|
+
import { ActivityBarMenu, ColorThemeToggle } from '../editor/ActivityBar.js';
|
|
59
59
|
import type { ActivityBarItemConfig } from '@finos/legend-lego/application';
|
|
60
60
|
import { useLegendStudioApplicationStore } from '../LegendStudioFrameworkProvider.js';
|
|
61
61
|
import { LEGEND_STUDIO_TEST_ID } from '../../__lib__/LegendStudioTesting.js';
|
|
@@ -253,6 +253,7 @@ const ShowcaseViewerActivityBar = observer(() => {
|
|
|
253
253
|
</button>
|
|
254
254
|
))}
|
|
255
255
|
</div>
|
|
256
|
+
<ColorThemeToggle />
|
|
256
257
|
</div>
|
|
257
258
|
);
|
|
258
259
|
});
|