Skip to content

Commit d605b49

Browse files
try and catch on mergeAllOfs for unsatisfied schemas (#860)
* try and catch on mergeAllOfs for unsatisfied schemas * refactor error handling to not require main, to decouple tests * apply formatting changes * return false for unsatisfiable schema * apply formatting changes * add readme file on testing --------- Co-authored-by: M-Jafarkhani <[email protected]> Co-authored-by: Logende <[email protected]>
1 parent 14f9df3 commit d605b49

20 files changed

Lines changed: 124 additions & 78 deletions

documentation_developer/TESTING.md

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# Testing
2+
3+
## Best Practices
4+
5+
Keep code decoupled and functions small and atomic.
6+
Always keep testability in mind.
7+
8+
## Unit Tests
9+
10+
If new functionality is developed, unit tests should be written for it.
11+
We use the testing framework `vitest`.
12+
Run all tests using `npm run test`.
13+
14+
Unit tests should test small blocks of the code, mainly individual functions.
15+
A few parts of the code (e.g., the worker for validation) can not be executed by the unit tests.
16+
Hence, every code that has a dependency to these untestable parts needs to specify mocks for these parts.
17+
To avoid the need for writing many mocks, keep the coupling as small as possible and avoid importing more than needed.
18+
If, for example, a file imports from the `main.ts` script, it indirectly imports everything which `main.ts` refers to, which is almost all of the project.
19+
Therefore, when `main.ts` is imported, to write tests for your components you need to mock many components.
20+
This should be avoided and instead the dependencies which you need should be extracted from the `main.ts` into a smaller, independent, file.
21+
22+
## E2E Tests
23+
24+
This project also has end-to-end tests which open MetaConfigurator in a browser and then simulate different clicks and keyboard inputs to test the full behavior of the app.
25+
More details can be found in the [e2e test directory](../meta_configurator/e2e).

meta_configurator/src/components/toolbar/SearchBar.vue

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import type {MenuItem} from 'primevue/menuitem';
44
import Menu from 'primevue/menu';
55
import {useSessionStore} from '@/store/sessionStore';
66
import Button from 'primevue/button';
7-
import {errorService} from '@/main';
87
98
import InputText from 'primevue/inputtext';
109
import {useMagicKeys, watchDebounced} from '@vueuse/core';
@@ -14,6 +13,7 @@ import {focus} from '@/utility/focusUtils';
1413
import {GuiConstants} from '@/constants';
1514
1615
import {getSessionForMode} from '@/data/useDataLink';
16+
import {useErrorService} from '@/utility/errorServiceInstance';
1717
1818
const searchTerm: Ref<string> = ref('');
1919
@@ -51,7 +51,7 @@ watchDebounced(
5151
.slice(0, GuiConstants.MAX_SEARCH_RESULTS);
5252
})
5353
.catch(error => {
54-
errorService.onError(error);
54+
useErrorService().onError(error);
5555
});
5656
},
5757
{debounce: 500}

meta_configurator/src/components/toolbar/Toolbar.vue

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
import {ref} from 'vue';
33
import TopToolbar from '@/components/toolbar/TopToolbar.vue';
44
import {SessionMode} from '@/store/sessionMode';
5-
import {errorService} from '@/main';
65
import {schemaCollection} from '@/packaged-schemas/schemaCollection';
76
import {fetchSchemasFromJSONSchemaStore} from '@/components/toolbar/fetchSchemasFromJsonSchemaStore';
87
import {openUploadSchemaDialog} from '@/components/toolbar/uploadFile';
@@ -18,6 +17,7 @@ import NewsDialog from '@/components/toolbar/dialogs/NewsDialog.vue';
1817
import {useSettings} from '@/settings/useSettings';
1918
import {hasCurrentNewsChanged, setCurrentNewsHash} from '@/components/toolbar/currentNews';
2019
import DataExportDialog from '@/components/toolbar/dialogs/data-export/DataExportDialog.vue';
20+
import {useErrorService} from '@/utility/errorServiceInstance';
2121
2222
const props = defineProps<{
2323
currentMode: SessionMode;
@@ -83,7 +83,7 @@ async function showSchemaStoreDialog(): Promise<void> {
8383
fetchedSchemasSelectionDialog.value.setSchemas(await fetchSchemasFromJSONSchemaStore());
8484
fetchedSchemasSelectionDialog.value.show();
8585
} catch (error) {
86-
errorService.onError(error);
86+
useErrorService().onError(error);
8787
}
8888
}
8989
function showExampleSchemasDialog() {

meta_configurator/src/components/toolbar/createSampleData.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import {confirmationService} from '@/utility/confirmationService';
22
import {toastService} from '@/utility/toastService';
3-
import {errorService} from '@/main';
43
import _ from 'lodash';
54
import {useCurrentData} from '@/data/useDataLink';
65
import {useDataSource} from '@/data/dataSource';
6+
import {useErrorService} from '@/utility/errorServiceInstance';
77

88
/**
99
* Generates sample data for the given schema.
@@ -28,7 +28,7 @@ function generateSampleDataAndUseAsFileData() {
2828
generateSampleData(useDataSource().userSchemaData.value)
2929
.then(data => (useDataSource().userData.value = data))
3030
.catch((error: Error) =>
31-
errorService.onError({
31+
useErrorService().onError({
3232
message: 'Error generating sample data',
3333
details: error.message,
3434
stack: error.stack,

meta_configurator/src/components/toolbar/dialogs/FetchedSchemasSelectionDialog.vue

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@ import Listbox from 'primevue/listbox';
66
import type {SchemaOption} from '@/packaged-schemas/schemaOption';
77
import {fetchSchemaFromUrl} from '@/components/toolbar/fetchSchemaFromUrl';
88
import {openClearDataEditorDialog} from '@/components/toolbar/clearFile';
9-
import {errorService} from '@/main';
109
import {loadExampleSchema} from '@/components/toolbar/fetchExampleSchemas';
10+
import {useErrorService} from '@/utility/errorServiceInstance';
1111
1212
const showDialog = ref(false);
1313
@@ -24,15 +24,15 @@ watch(selectedSchema, async newSelectedSchema => {
2424
hideDialog();
2525
openClearDataEditorDialog();
2626
} catch (error) {
27-
errorService.onError(error);
27+
useErrorService().onError(error);
2828
}
2929
} else if (newSelectedSchema.key) {
3030
try {
3131
loadExampleSchema(newSelectedSchema.key);
3232
hideDialog();
3333
openClearDataEditorDialog();
3434
} catch (error) {
35-
errorService.onError(error);
35+
useErrorService().onError(error);
3636
}
3737
}
3838
});

meta_configurator/src/components/toolbar/dialogs/csvimport/importCsvUtils.ts

Lines changed: 15 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import {type CsvError, parse} from 'csv-parse/browser/esm';
1717
import type {JsonSchemaType} from '@/schema/jsonSchemaType';
1818
import {identifyArraysInJson} from '@/utility/arrayPathUtils';
1919
import {stringToIdentifier} from '@/utility/stringToIdentifier';
20+
import {useErrorService} from '@/utility/errorServiceInstance';
2021

2122
export function requestUploadFileToRef(resultString: Ref<string>, resultTableName: Ref<string>) {
2223
const {open, onChange, reset} = useFileDialog({
@@ -51,28 +52,19 @@ export function inferSchemaForNewDataAndMergeIntoCurrentSchema(
5152
if (!inferredSchema) {
5253
throw Error('Unable to infer schema for the given data.');
5354
}
54-
//for (const column of currentColumnMapping) {
55-
// addCustomTitleToSchemaProperty(inferredSchema, column);
56-
//}
5755

5856
const schema = getSchemaForMode(SessionMode.DataEditor);
5957
const currentSchema = schema.schemaRaw.value;
6058
// then we merge the new schema into the current one
61-
getSchemaForMode(SessionMode.DataEditor).schemaRaw.value = mergeAllOfs({
62-
allOf: [currentSchema, inferredSchema],
63-
});
59+
try {
60+
getSchemaForMode(SessionMode.DataEditor).schemaRaw.value = mergeAllOfs({
61+
allOf: [currentSchema, inferredSchema],
62+
});
63+
} catch (error) {
64+
useErrorService().onError(error);
65+
}
6466
}
6567

66-
// temporarily removed to reduce complexity for the user
67-
/*function addCustomTitleToSchemaProperty(inferredSchema: any, column: CsvImportColumnMappingData) {
68-
const propertySchemaTitlePath = [
69-
...dataPathToSchemaPath(column.getPathForJsonDocument(0)),
70-
'title',
71-
];
72-
const titlePathString = pathToString(propertySchemaTitlePath);
73-
_.set(inferredSchema, titlePathString, column.titleInSchema);
74-
}*/
75-
7668
export function loadCsvFromUserString(
7769
currentUserDataString: Ref<string>,
7870
currentUserCsv: Ref<any[]>,
@@ -230,11 +222,6 @@ export function inferExpansionSchema(
230222
throw Error('Unable to access expansion schema of the inferred table schema.');
231223
}
232224

233-
// Does not yet work because the addCustomTitle function is not yet adjusted to deal with expansion properties
234-
//for (const column of currentColumnMapping) {
235-
// addCustomTitleToSchemaProperty(expansionPropSchema, column);
236-
//}
237-
238225
// after having inferred expansion schema by using whole table as basis, we remove the other table props from the schema
239226
// this way, we overwrite only the expansion property and not also the other table properties
240227
const objectWithOnlyExpandedProp: any = {};
@@ -256,8 +243,11 @@ export function inferExpansionSchema(
256243
undefined
257244
);
258245

259-
// then we merge the new schema into the current one
260-
getSchemaForMode(SessionMode.DataEditor).schemaRaw.value = mergeAllOfs({
261-
allOf: [currentSchema, tableSchema],
262-
});
246+
try {
247+
getSchemaForMode(SessionMode.DataEditor).schemaRaw.value = mergeAllOfs({
248+
allOf: [currentSchema, tableSchema],
249+
});
250+
} catch (error) {
251+
useErrorService().onError(error);
252+
}
263253
}

meta_configurator/src/components/toolbar/fetchExampleSchemas.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
import {openClearDataEditorDialog} from '@/components/toolbar/clearFile';
2-
import {errorService} from '@/main';
32
import {toastService} from '@/utility/toastService';
43
import {useDataSource} from '@/data/dataSource';
54
import {schemaCollection} from '@/packaged-schemas/schemaCollection';
65
import {adaptComplexitySettingsToLoadedSchema} from '@/settings/settingsUpdater';
76
import {getDataForMode} from '@/data/useDataLink';
87
import {SessionMode} from '@/store/sessionMode';
8+
import {useErrorService} from '@/utility/errorServiceInstance';
99

1010
/**
1111
* Loads the example schema with the given key.
@@ -34,6 +34,6 @@ export function loadExampleSchema(schemaKey: string): void {
3434
});
3535
}
3636
} catch (error) {
37-
errorService.onError(error);
37+
useErrorService().onError(error);
3838
}
3939
}

meta_configurator/src/dataformats/__tests__/formatRegistry.test.ts

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import {beforeEach, describe, expect, it, vi} from 'vitest';
1+
import {beforeEach, describe, expect, it} from 'vitest';
22
import {FormatRegistry, useDataConverter, usePathIndexLink} from '../formatRegistry';
33
import {DataConverterJson, DataConverterYaml} from '../dataConverter';
44
import {useDataSource} from '@/data/dataSource';
@@ -12,10 +12,6 @@ function setDataFormat(format: string | undefined) {
1212
triggerRef(useDataSource().settingsData);
1313
}
1414

15-
vi.mock('@/main', () => ({
16-
errorService: {},
17-
}));
18-
1915
describe('formatRegistry', () => {
2016
const formatRegistry = new FormatRegistry();
2117

meta_configurator/src/dataformats/__tests__/pathIndexLinkJson.test.ts

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,7 @@
1-
import {describe, expect, it, vi} from 'vitest';
1+
import {describe, expect, it} from 'vitest';
22
import {PathIndexLinkJson} from '../pathIndexLinkJson';
33
import type {Path} from '../../utility/path';
44

5-
vi.mock('@/main', () => ({
6-
errorService: {},
7-
}));
8-
95
describe('pathIndexLinkJson', () => {
106
const pathIndexLinkJson = new PathIndexLinkJson();
117

meta_configurator/src/dataformats/pathIndexLinkJson.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ import type {
99
CstNodeObjectProperty,
1010
} from 'json-cst';
1111
import {parse} from 'json-cst';
12-
import {errorService} from '@/main';
13-
import {pathToJsonPointer, pathToString} from '@/utility/pathUtils';
12+
import {pathToJsonPointer} from '@/utility/pathUtils';
13+
import {useErrorService} from '@/utility/errorServiceInstance';
1414

1515
/**
1616
* Implementation of PathIndexLink for JSON data.
@@ -28,7 +28,7 @@ export class PathIndexLinkJson implements PathIndexLink {
2828
const cst = this.getCst(editorContent);
2929
return this.determineIndexStep(cst.root, currentPath);
3030
} catch (e) {
31-
errorService.onError(e);
31+
useErrorService().onError(e);
3232
return 0;
3333
}
3434
}
@@ -174,7 +174,7 @@ export class PathIndexLinkJson implements PathIndexLink {
174174
this.traverseCstForIndexesForPaths(cst.root, pathSet, [], result);
175175
return result;
176176
} catch (e) {
177-
errorService.onError(e);
177+
useErrorService().onError(e);
178178
return {};
179179
}
180180
}

0 commit comments

Comments
 (0)