@finos/legend-studio
Version:
320 lines (290 loc) • 9.57 kB
text/typescript
/**
* Copyright (c) 2020-present, Goldman Sachs
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import {
type TestAssertion,
type AssertionStatus,
AssertFail,
type TestResult,
TestPassed,
TestError,
TestFailed,
EqualToJson,
ExternalFormatData,
EqualToJsonAssertFail,
MultiExecutionServiceTestResult,
AssertPass,
} from '@finos/legend-graph';
import {
type GeneratorFn,
ActionState,
assertErrorThrown,
ContentType,
} from '@finos/legend-shared';
import { action, flow, makeObservable, observable } from 'mobx';
import type { EditorStore } from '../../../EditorStore.js';
import { externalFormatData_setData } from '../../../graphModifier/DSLData_GraphModifierHelper.js';
import {
getTestableResultFromAssertionStatus,
TESTABLE_RESULT,
} from '../../../sidebar-state/testable/GlobalTestRunnerState.js';
import type { TestableTestEditorState } from './TestableEditorState.js';
export enum TEST_ASSERTION_TAB {
ASSERTION_SETUP = 'ASSERTION_SETUP',
ASSERTION_RESULT = 'ASSERTION_RESULT',
}
export abstract class TestAssertionStatusState {
resultState: TestAssertionResultState;
status: AssertionStatus;
constructor(resultState: TestAssertionResultState, status: AssertionStatus) {
this.resultState = resultState;
this.status = status;
}
}
export class AssertFailState extends TestAssertionStatusState {
declare status: AssertFail;
constructor(resultState: TestAssertionResultState, status: AssertFail) {
super(resultState, status);
this.status = status;
makeObservable(this, {
status: observable,
});
}
}
export class EqualToJsonAssertFailState extends AssertFailState {
declare status: EqualToJsonAssertFail;
diffModal = false;
constructor(
resultState: TestAssertionResultState,
status: EqualToJsonAssertFail,
) {
super(resultState, status);
this.status = status;
makeObservable(this, {
diffModal: observable,
setDiffModal: action,
});
}
setDiffModal(val: boolean): void {
this.diffModal = val;
}
}
export class UnsupportedAssertionStatusState extends TestAssertionStatusState {}
export class TestAssertionResultState {
testResult: TestResult | undefined;
statusState:
| TestAssertionStatusState
| Map<string, TestAssertionResultState>
| undefined;
readonly editorStore: EditorStore;
readonly assertionState: TestAssertionEditorState;
constructor(
editorStore: EditorStore,
assertionState: TestAssertionEditorState,
) {
makeObservable(this, {
testResult: observable,
setTestResult: action,
statusState: observable,
});
this.editorStore = editorStore;
this.assertionState = assertionState;
}
setTestResult(val: TestResult | undefined): void {
this.testResult = val;
this.statusState = undefined;
if (val instanceof TestFailed) {
const status = val.assertStatuses.find(
(_status) => _status.assertion === this.assertionState.assertion,
);
this.statusState = this.buildStatus(status);
} else if (val instanceof MultiExecutionServiceTestResult) {
const statusMap = new Map<string, TestAssertionResultState>();
Array.from(val.keyIndexedTestResults.entries()).forEach((keyedResult) => {
const resultState = new TestAssertionResultState(
this.editorStore,
this.assertionState,
);
resultState.setTestResult(keyedResult[1]);
statusMap.set(keyedResult[0], resultState);
});
this.statusState = statusMap;
}
}
buildStatus(
val: AssertionStatus | undefined,
): TestAssertionStatusState | undefined {
if (val) {
if (val instanceof EqualToJsonAssertFail) {
return new EqualToJsonAssertFailState(this, val);
}
if (val instanceof AssertFail) {
return new AssertFailState(this, val);
}
return new UnsupportedAssertionStatusState(this, val);
}
return undefined;
}
get result(): TESTABLE_RESULT {
if (this.assertionState.testState.runningTestAction.isInProgress) {
return TESTABLE_RESULT.IN_PROGRESS;
}
if (this.testResult instanceof TestError) {
return TESTABLE_RESULT.ERROR;
} else if (this.testResult instanceof TestPassed) {
return TESTABLE_RESULT.PASSED;
} else if (
this.testResult instanceof TestFailed &&
this.statusState instanceof TestAssertionStatusState
) {
return getTestableResultFromAssertionStatus(this.statusState.status);
} else if (this.testResult instanceof MultiExecutionServiceTestResult) {
const passed = Array.from(
this.testResult.keyIndexedTestResults.entries(),
).every((keyResult) => {
const result = keyResult[1];
if (result instanceof TestPassed) {
return true;
}
if (result instanceof TestFailed) {
const status = result.assertStatuses.find(
(_status) => _status.assertion === this.assertionState.assertion,
);
if (status instanceof AssertPass) {
return true;
}
}
return false;
});
if (passed) {
return TESTABLE_RESULT.PASSED;
}
const assertionErrors = Array.from(
this.testResult.keyIndexedTestResults.values(),
).find((t) => t instanceof TestError);
if (assertionErrors) {
return TESTABLE_RESULT.ERROR;
}
return TESTABLE_RESULT.FAILED;
}
return TESTABLE_RESULT.DID_NOT_RUN;
}
}
export abstract class TestAssertionState {
readonly editorStore: EditorStore;
assertion: TestAssertion;
result: TestAssertionResultState;
constructor(
editorStore: EditorStore,
assertionState: TestAssertionEditorState,
) {
this.editorStore = editorStore;
this.assertion = assertionState.assertion;
this.result = new TestAssertionResultState(editorStore, assertionState);
}
abstract generateExpected(status: AssertFail): void;
abstract generateBare(): TestAssertion;
abstract label(): string;
}
export class EqualToJsonAssertionState extends TestAssertionState {
declare assertion: EqualToJson;
setExpectedValue(val: string): void {
externalFormatData_setData(this.assertion.expected, val);
}
generateExpected(status: AssertFail): void {
if (status instanceof EqualToJsonAssertFail) {
const expected = status.actual;
this.setExpectedValue(expected);
}
}
generateBare(): TestAssertion {
const bareAssertion = new EqualToJson();
bareAssertion.expected = new ExternalFormatData();
bareAssertion.expected.contentType = ContentType.APPLICATION_JSON;
bareAssertion.expected.data = '';
return bareAssertion;
}
label(): string {
return 'EqualToJSON';
}
}
export class UnsupportedAssertionState extends TestAssertionState {
generateBare(): TestAssertion {
throw new Error('Method not implemented.');
}
generateExpected(status: AssertFail): void {
return;
}
label(): string {
return 'Unsupported';
}
}
export class TestAssertionEditorState {
readonly editorStore: EditorStore;
readonly testState: TestableTestEditorState;
assertionState: TestAssertionState;
assertionResultState: TestAssertionResultState;
assertion: TestAssertion;
selectedTab = TEST_ASSERTION_TAB.ASSERTION_SETUP;
generatingExpectedAction = ActionState.create();
constructor(
editorStore: EditorStore,
assertion: TestAssertion,
testState: TestableTestEditorState,
) {
makeObservable(this, {
selectedTab: observable,
assertionResultState: observable,
setSelectedTab: action,
generateExpected: flow,
});
this.editorStore = editorStore;
this.assertion = assertion;
this.testState = testState;
this.assertionState = this.buildAssertionState(assertion);
this.assertionResultState = new TestAssertionResultState(editorStore, this);
}
setSelectedTab(val: TEST_ASSERTION_TAB): void {
this.selectedTab = val;
}
*generateExpected(): GeneratorFn<void> {
try {
this.generatingExpectedAction.inProgress();
const bare = this.assertionState.generateBare();
bare.parentTest = this.assertion.parentTest;
const status =
(yield this.editorStore.graphManagerState.graphManager.generateExpectedResult(
this.testState.testable,
this.testState.test,
bare,
this.editorStore.graphManagerState.graph,
)) as AssertFail;
this.assertionState.generateExpected(status);
this.generatingExpectedAction.complete();
} catch (error) {
assertErrorThrown(error);
this.editorStore.applicationStore.notifyError(
`Error generating expected result: ${error.message}`,
);
this.generatingExpectedAction.fail();
}
}
buildAssertionState(assertion: TestAssertion): TestAssertionState {
if (assertion instanceof EqualToJson) {
return new EqualToJsonAssertionState(this.editorStore, this);
}
return new UnsupportedAssertionState(this.editorStore, this);
}
}