diff --git a/changelog.md b/changelog.md
index 1d4a970a4..20cc6eee0 100644
--- a/changelog.md
+++ b/changelog.md
@@ -89,6 +89,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
* Display buttons for each OIDC provider and perform user authentication if the global configuration file is declared.
* Draw page:
* Add variables panel in left drawer.
+ * Add panel to display all errors.
+ * Select component in error and open its details from the error's console.
+ * Select file in error and open text editor and selected file open.
* Rename component id.
* Text editor page:
* Add panel to display all errors.
diff --git a/src/components/table/ErrorsTable.vue b/src/components/table/ErrorsTable.vue
index f02d16611..f12b71a1c 100644
--- a/src/components/table/ErrorsTable.vue
+++ b/src/components/table/ErrorsTable.vue
@@ -6,12 +6,34 @@
:columns="columns"
row-key="message"
data-cy="errors-table"
- />
+ >
+
+
+
+ {{ data.value }}
+
+
+
+
+
+
+ {{ data.value }}
+
+
+
+
+
+
diff --git a/src/i18n/en-US/index.js b/src/i18n/en-US/index.js
index 37a70a535..4cc196073 100644
--- a/src/i18n/en-US/index.js
+++ b/src/i18n/en-US/index.js
@@ -495,6 +495,7 @@ export default {
errorsTable: {
message: 'Message',
line: 'Start/End Line',
+ file: 'File',
column: 'Start/End Column',
severity: 'Severity',
component: 'Component',
diff --git a/src/layouts/ModelizerDrawLayout.vue b/src/layouts/ModelizerDrawLayout.vue
index 80869d7b7..ab93d50af 100644
--- a/src/layouts/ModelizerDrawLayout.vue
+++ b/src/layouts/ModelizerDrawLayout.vue
@@ -118,7 +118,7 @@ async function initView() {
).then((logs) => {
LogEvent.FileLogEvent.next(logs.map((log) => ({
...log,
- componentId: log.componentId ? data.plugin.data.getComponentById(log.componentId).externalId : '',
+ componentName: log.componentId ? data.plugin.data.getComponentById(log.componentId).externalId : '',
})));
data.plugin.draw();
}),
diff --git a/src/layouts/ModelizerTextLayout.vue b/src/layouts/ModelizerTextLayout.vue
index dafbe4f2e..e430d5f86 100644
--- a/src/layouts/ModelizerTextLayout.vue
+++ b/src/layouts/ModelizerTextLayout.vue
@@ -9,7 +9,6 @@
v-model="splitter"
:limits="[50, 100]"
separator-class="separator-class"
- :class="isVisible ? '' : 'splitter-invisible'"
:style="{ height: `calc(100vh - ${reservedHeight + 70}px)` }"
>
diff --git a/src/pages/ModelizerDrawPage.vue b/src/pages/ModelizerDrawPage.vue
index e8bcb3498..744ca475a 100644
--- a/src/pages/ModelizerDrawPage.vue
+++ b/src/pages/ModelizerDrawPage.vue
@@ -61,7 +61,7 @@ import {
reactive,
ref,
} from 'vue';
-import { useRoute } from 'vue-router';
+import { useRoute, useRouter } from 'vue-router';
import { Notify } from 'quasar';
import { useI18n } from 'vue-i18n';
import PluginEvent from 'src/composables/events/PluginEvent';
@@ -69,9 +69,11 @@ import { getTemplatesByType } from 'src/composables/TemplateManager';
import DialogEvent from 'src/composables/events/DialogEvent';
import { ComponentLink } from '@ditrit/leto-modelizer-plugin-core';
import DrawerEvent from 'src/composables/events/DrawerEvent';
+import FileEvent from 'src/composables/events/FileEvent';
const { t } = useI18n();
const route = useRoute();
+const router = useRouter();
const HAS_BACKEND = computed(() => process.env.HAS_BACKEND);
const projectName = computed(() => route.params.projectName);
@@ -193,6 +195,20 @@ async function onRequestEvent(event) {
key: 'ComponentDetailPanel',
id: event.id,
});
+ } else if (event.type === 'select') {
+ const parent = data.plugin.data.getComponentById(event.ids[0]);
+ data.plugin.data.scene.selection = event.ids;
+ data.plugin.data.scene.selectionRef = parent.id;
+ data.plugin.draw();
+ } else if (event.type === 'openFile') {
+ await router.push({
+ name: 'Text',
+ params: {
+ projectName: projectName.value,
+ },
+ query: query.value,
+ });
+ FileEvent.SelectFileTabEvent.next(event.path);
}
if (needRender) {
diff --git a/tests/unit/components/table/ErrorsTable.spec.js b/tests/unit/components/table/ErrorsTable.spec.js
index b090419b2..32b41ac1a 100644
--- a/tests/unit/components/table/ErrorsTable.spec.js
+++ b/tests/unit/components/table/ErrorsTable.spec.js
@@ -2,6 +2,7 @@ import { installQuasarPlugin } from '@quasar/quasar-app-extension-testing-unit-j
import { shallowMount } from '@vue/test-utils';
import i18nConfiguration from 'src/i18n';
import ErrorsTable from 'src/components/table/ErrorsTable.vue';
+import PluginEvent from 'src/composables/events/PluginEvent';
installQuasarPlugin();
@@ -11,6 +12,12 @@ jest.mock('vue-i18n', () => ({
}),
}));
+jest.mock('src/composables/events/PluginEvent', () => ({
+ RequestEvent: {
+ next: jest.fn(),
+ },
+}));
+
describe('Test component: ErrorsTable', () => {
let wrapper;
const { createI18n } = jest.requireActual('vue-i18n');
@@ -44,11 +51,14 @@ describe('Test component: ErrorsTable', () => {
editorType: 'diagram',
});
- expect(wrapper.vm.columns.length).toEqual(4);
+ expect(wrapper.vm.columns.length).toEqual(7);
expect(wrapper.vm.columns[0].name).toEqual('severity');
expect(wrapper.vm.columns[1].name).toEqual('component');
expect(wrapper.vm.columns[2].name).toEqual('attribute');
- expect(wrapper.vm.columns[3].name).toEqual('message');
+ expect(wrapper.vm.columns[3].name).toEqual('file');
+ expect(wrapper.vm.columns[4].name).toEqual('line');
+ expect(wrapper.vm.columns[5].name).toEqual('column');
+ expect(wrapper.vm.columns[6].name).toEqual('message');
});
it('should have valid columns for text page', async () => {
@@ -63,4 +73,31 @@ describe('Test component: ErrorsTable', () => {
expect(wrapper.vm.columns[3].name).toEqual('message');
});
});
+
+ describe('Test function: selectComponent', () => {
+ it('should emit events', () => {
+ PluginEvent.RequestEvent.next.mockClear();
+
+ wrapper.vm.selectComponent('id_1');
+
+ expect(PluginEvent.RequestEvent.next).toHaveBeenCalledTimes(2);
+ expect(PluginEvent.RequestEvent.next.mock.calls).toEqual([
+ [{ type: 'select', ids: ['id_1'] }],
+ [{ type: 'edit', id: 'id_1' }],
+ ]);
+ });
+ });
+
+ describe('Test function: selectFile', () => {
+ it('should emit event', () => {
+ PluginEvent.RequestEvent.next.mockClear();
+
+ wrapper.vm.selectFile('path');
+
+ expect(PluginEvent.RequestEvent.next).toHaveBeenCalledTimes(1);
+ expect(PluginEvent.RequestEvent.next.mock.calls).toEqual([
+ [{ type: 'openFile', path: 'path' }],
+ ]);
+ });
+ });
});
diff --git a/tests/unit/pages/ModelizerDrawPage.spec.js b/tests/unit/pages/ModelizerDrawPage.spec.js
index ca1783519..a44d24971 100644
--- a/tests/unit/pages/ModelizerDrawPage.spec.js
+++ b/tests/unit/pages/ModelizerDrawPage.spec.js
@@ -6,6 +6,8 @@ import PluginManager from 'src/composables/PluginManager';
import TemplateManager from 'src/composables/TemplateManager';
import PluginEvent from 'src/composables/events/PluginEvent';
import DrawerEvent from 'src/composables/events/DrawerEvent';
+import FileEvent from 'src/composables/events/FileEvent';
+import { useRouter } from 'vue-router';
installQuasarPlugin({
plugins: [Notify],
@@ -28,12 +30,16 @@ jest.mock('vue-router', () => ({
plugin: 'plugin',
},
}),
+ useRouter: jest.fn(() => ({
+ push: () => Promise.resolve(),
+ })),
}));
jest.mock('src/composables/PluginManager', () => ({
getPluginByName: jest.fn(() => ({
data: {
name: 'pluginName',
+ getComponentById: jest.fn(() => ({ id: 2 })),
addComponent: jest.fn(),
removeComponentById: jest.fn(),
definitions: {
@@ -41,6 +47,10 @@ jest.mock('src/composables/PluginManager', () => ({
{ type: 'testComponent', isTemplate: false, icon: 'icon' },
],
},
+ scene: {
+ selection: [],
+ selectionRef: null,
+ },
},
configuration: {
defaultFileName: 'defaultFileName',
@@ -81,6 +91,12 @@ jest.mock('src/composables/events/DrawerEvent', () => ({
subscribe: jest.fn(),
}));
+jest.mock('src/composables/events/FileEvent', () => ({
+ SelectFileTabEvent: {
+ next: jest.fn(),
+ },
+}));
+
describe('Test page component: ModelizerDrawPage', () => {
let wrapper;
let pluginDefaultSubscribe;
@@ -89,6 +105,7 @@ describe('Test page component: ModelizerDrawPage', () => {
let pluginRequestUnsubscribe;
let drawerEventSubscribe;
let drawerEventUnsubscribe;
+ let push;
beforeEach(() => {
pluginDefaultSubscribe = jest.fn();
@@ -97,6 +114,11 @@ describe('Test page component: ModelizerDrawPage', () => {
pluginRequestUnsubscribe = jest.fn();
drawerEventSubscribe = jest.fn();
drawerEventUnsubscribe = jest.fn();
+ push = jest.fn(() => Promise.resolve());
+
+ useRouter.mockImplementation(() => ({
+ push,
+ }));
PluginEvent.DefaultEvent.subscribe.mockImplementation(() => {
pluginDefaultSubscribe();
@@ -382,6 +404,39 @@ describe('Test page component: ModelizerDrawPage', () => {
id: 1,
});
});
+
+ it('should select component and draw', () => {
+ DrawerEvent.next.mockClear();
+
+ wrapper.vm.onRequestEvent({
+ type: 'select',
+ ids: [1],
+ });
+ expect(wrapper.vm.data.plugin.draw).toBeCalled();
+ });
+
+ it('should go to editor page and send event to set active file', async () => {
+ FileEvent.SelectFileTabEvent.next.mockClear();
+ const router = useRouter();
+ router.push.mockClear();
+
+ await wrapper.vm.onRequestEvent({
+ type: 'openFile',
+ path: 'main.tf',
+ });
+
+ expect(router.push).toBeCalledWith({
+ name: 'Text',
+ params: {
+ projectName: 'project-00000000',
+ },
+ query: {
+ path: 'path',
+ plugin: 'plugin',
+ },
+ });
+ expect(FileEvent.SelectFileTabEvent.next).toBeCalledWith('main.tf');
+ });
});
describe('Test function: askAI', () => {