diff --git a/jest.config.js b/jest.config.js index d472a26..638e343 100644 --- a/jest.config.js +++ b/jest.config.js @@ -3,4 +3,6 @@ module.exports = { moduleNameMapper: { quasar: "quasar/dist/quasar.umd.min.js" }, + //"collectCoverage": true, + //"collectCoverageFrom": ["**/*.{js,vue}", "!**/node_modules/**"] } diff --git a/package-lock.json b/package-lock.json index 7f7bb4c..487c5e2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2100,9 +2100,9 @@ "dev": true }, "@vue/test-utils": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@vue/test-utils/-/test-utils-1.0.2.tgz", - "integrity": "sha512-pnRWJbb0cLqjSJIKRpqoSISeYtufEn8D16VmhlCrDWIVt4iAY4Og4JpOPmFytvtQVz96p6n7T6ERI55ue6n0Ew==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@vue/test-utils/-/test-utils-1.0.3.tgz", + "integrity": "sha512-mmsKXZSGfvd0bH05l4SNuczZ2MqlJH2DWhiul5wJXFxbf/gRRd2UL4QZgozEMQ30mRi9i4/+p4JJat8S4Js64Q==", "dev": true, "requires": { "dom-event-types": "^1.0.0", @@ -5941,6 +5941,12 @@ "locate-path": "^2.0.0" } }, + "flush-promises": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/flush-promises/-/flush-promises-1.0.2.tgz", + "integrity": "sha512-G0sYfLQERwKz4+4iOZYQEZVpOt9zQrlItIxQAAYAWpfby3gbHrx0osCHz5RLl/XoXevXk0xoN4hDFky/VV9TrA==", + "dev": true + }, "flush-write-stream": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-1.1.1.tgz", @@ -13178,9 +13184,9 @@ } }, "vue-router": { - "version": "3.1.6", - "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-3.1.6.tgz", - "integrity": "sha512-GYhn2ynaZlysZMkFE5oCHRUTqE8BWs/a9YbKpNLi0i7xD6KG1EzDqpHQmv1F5gXjr8kL5iIVS8EOtRaVUEXTqA==" + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-3.2.0.tgz", + "integrity": "sha512-khkrcUIzMcI1rDcNtqkvLwfRFzB97GmJEsPAQdj7t/VvpGhmXLOkUfhc+Ah8CvpSXGXwuWuQO+x8Sy/xDhXZIA==" }, "vue-style-loader": { "version": "4.1.2", @@ -13217,9 +13223,9 @@ "dev": true }, "vuex": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/vuex/-/vuex-3.3.0.tgz", - "integrity": "sha512-1MfcBt+YFd20DPwKe0ThhYm1UEXZya4gVKUvCy7AtS11YAOUR+9a6u4fsv1Rr6ePZCDNxW/M1zuIaswp6nNv8Q==" + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/vuex/-/vuex-3.4.0.tgz", + "integrity": "sha512-ajtqwEW/QhnrBZQsZxCLHThZZaa+Db45c92Asf46ZDXu6uHXgbfVuBaJ4gzD2r4UX0oMJHstFwd2r2HM4l8umg==" }, "w3c-hr-time": { "version": "1.0.2", diff --git a/package.json b/package.json index d3d903b..6be732a 100644 --- a/package.json +++ b/package.json @@ -13,8 +13,8 @@ "core-js": "^3.6.5", "quasar": "^1.11.3", "vue": "^2.6.11", - "vue-router": "^3.1.6", - "vuex": "^3.3.0" + "vue-router": "^3.2.0", + "vuex": "^3.4.0" }, "devDependencies": { "@vue/cli-plugin-babel": "~4.3.1", @@ -22,8 +22,9 @@ "@vue/cli-plugin-unit-jest": "^4.3.1", "@vue/cli-plugin-vuex": "~4.3.1", "@vue/cli-service": "~4.3.1", - "@vue/test-utils": "~1.0.2", + "@vue/test-utils": "^1.0.3", "babel-plugin-transform-imports": "1.5.0", + "flush-promises": "^1.0.2", "stylus": "^0.54.7", "stylus-loader": "^3.0.2", "vue-cli-plugin-quasar": "^2.0.2", diff --git a/src/components/automation/AutomationManager.vue b/src/components/automation/AutomationManager.vue index e2c5027..0760e76 100644 --- a/src/components/automation/AutomationManager.vue +++ b/src/components/automation/AutomationManager.vue @@ -21,30 +21,6 @@ icon="add" @click="showAddPolicyModal" /> - - @@ -133,6 +162,7 @@ export default { showPolicyOverviewModal: false, showRelationsViewModal: false, policy: null, + editPolicyId: null, selected: [], pagination: { rowsPerPage: 0, @@ -189,7 +219,7 @@ export default { this.$store.commit("automation/setPolicyChecks", {}); this.$store.commit("automation/setPolicyAutomatedTasks", {}); }, - deletePolicy() { + deletePolicy(id) { this.$q .dialog({ title: "Delete policy?", @@ -198,7 +228,7 @@ export default { }) .onOk(() => { this.$store - .dispatch("automation/deletePolicy", this.selectedRow) + .dispatch("automation/deletePolicy", id) .then(response => { this.notifySuccess(`Policy was deleted!`); }) @@ -215,8 +245,15 @@ export default { this.policy = null; this.showRelationsViewModal = false; }, + showEditPolicyModal(id) { + this.editPolicyId = id; + this.showPolicyFormModal = true; + }, + closeEditPolicyModal() { + this.showPolicyFormModal = false; + this.editPolicyId = null; + }, showAddPolicyModal() { - this.clearRow(); this.showPolicyFormModal = true; } }, @@ -227,7 +264,7 @@ export default { }) }, mounted() { - this.getPolicies(); + this.clearRow(); } }; \ No newline at end of file diff --git a/src/components/automation/PolicyChecksTab.vue b/src/components/automation/PolicyChecksTab.vue index 0d0223c..03e627d 100644 --- a/src/components/automation/PolicyChecksTab.vue +++ b/src/components/automation/PolicyChecksTab.vue @@ -99,7 +99,22 @@ Delete + + + + + + + Status + + + + Close @@ -120,13 +135,6 @@ v-model="props.row.email_alert" /> - - - - - - - Disk Space Drive {{ props.row.disk }} > {{props.row.threshold }}% @@ -145,27 +153,18 @@ Service Check - {{ props.row.svc_display_name }} - Awaiting First Synchronization - - Passing + + - - Failing - - - output - - - output - - {{ props.row.more_info }} - {{ props.row.last_run }} @@ -202,7 +201,6 @@ :policypk="checks.id" /> - @@ -213,7 +211,6 @@ :policypk="checks.id" /> - @@ -235,6 +232,12 @@ :policypk="checks.id" /> + + + @@ -254,6 +257,7 @@ import AddWinSvcCheck from "@/components/modals/checks/AddWinSvcCheck"; import EditWinSvcCheck from "@/components/modals/checks/EditWinSvcCheck"; import AddScriptCheck from "@/components/modals/checks/AddScriptCheck"; import EditScriptCheck from "@/components/modals/checks/EditScriptCheck"; +import PolicyCheckStatus from "@/components/automation/modals/PolicyCheckStatus"; export default { name: "PolicyChecksTab", @@ -270,7 +274,8 @@ export default { AddWinSvcCheck, EditWinSvcCheck, AddScriptCheck, - EditScriptCheck + EditScriptCheck, + PolicyCheckStatus }, mixins: [mixins], data() { @@ -287,26 +292,14 @@ export default { showEditWinSvcCheck: false, showAddScriptCheck: false, showEditScriptCheck: false, + showPolicyCheckStatus: false, editCheckPK: null, - scriptInfo: {}, + statusCheck: {}, columns: [ { name: "smsalert", field: "text_alert", align: "left" }, { name: "emailalert", field: "email_alert", align: "left" }, - { name: "statusicon", align: "left" }, { name: "desc", label: "Description", align: "left" }, - { name: "status", label: "Status", field: "status", align: "left" }, - { - name: "moreinfo", - label: "More Info", - field: "more_info", - align: "left" - }, - { - name: "datetime", - label: "Date / Time", - field: "last_run", - align: "left" - } + { name: "status", label: "Status", field: "status", align: "left" } ], pagination: { rowsPerPage: 9999 @@ -334,19 +327,6 @@ export default { onRefresh(id) { this.$store.dispatch("automation/loadPolicyChecks", id); }, - moreInfo(name, output) { - this.$q.dialog({ - title: `${name} output`, - style: "width: 35vw; max-width: 50vw", - message: `
${output}
`, - html: true, - dark: true - }); - }, - scriptMoreInfo(props) { - this.scriptInfo = props; - this.showScriptOutput = true; - }, editCheck(category) { switch (category) { case "diskspace": @@ -389,6 +369,14 @@ export default { }) .catch(e => this.notifyError(e.response.data.error)); }); + }, + showPolicyCheckStatusModal(check) { + this.statusCheck = check; + this.showPolicyCheckStatus = true; + }, + closePolicyCheckStatusModal() { + this.showPolicyCheckStatus = false; + this.statusCheck = {}; } }, computed: { diff --git a/src/components/automation/modals/PolicyCheckStatus.vue b/src/components/automation/modals/PolicyCheckStatus.vue new file mode 100644 index 0000000..406f026 --- /dev/null +++ b/src/components/automation/modals/PolicyCheckStatus.vue @@ -0,0 +1,33 @@ + + + \ No newline at end of file diff --git a/src/components/automation/modals/PolicyForm.vue b/src/components/automation/modals/PolicyForm.vue index 79fa9c9..7578185 100644 --- a/src/components/automation/modals/PolicyForm.vue +++ b/src/components/automation/modals/PolicyForm.vue @@ -205,7 +205,7 @@ export default { } }, mounted() { - //If pk prop is set that means we are editting + // If pk prop is set that means we are editting if (this.pk) { this.getPolicy(); } diff --git a/src/components/automation/modals/RelationsView.vue b/src/components/automation/modals/RelationsView.vue index d3f069a..7e91c39 100644 --- a/src/components/automation/modals/RelationsView.vue +++ b/src/components/automation/modals/RelationsView.vue @@ -17,9 +17,9 @@ narrow-indicator no-caps > - - - + + + @@ -27,7 +27,7 @@
Clients
- + {{ item.client }} @@ -38,7 +38,7 @@
Sites
- + {{ item.site }} @@ -54,7 +54,7 @@ {{ item.hostname }} - {{ item.client }} {{ item.site }} + {{ item.client }} {{ item.site }} @@ -80,7 +80,6 @@ export default { } }, mounted() { - this.$q.loading.show(); this.$store diff --git a/tests/unit/automation/automationmanager.spec.js b/tests/unit/automation/automationmanager.spec.js index 0d72ff2..8628954 100644 --- a/tests/unit/automation/automationmanager.spec.js +++ b/tests/unit/automation/automationmanager.spec.js @@ -75,7 +75,8 @@ describe("AutomationManager.vue", () => { localVue, stubs: [ "PolicySubTableTabs", - "PolicyForm" + "PolicyForm", + "RelationsView" ], }); @@ -86,7 +87,9 @@ describe("AutomationManager.vue", () => { // This is needed to remove q-dialogs since body doesn't rerender afterEach(() => { const dialogs = document.querySelectorAll(".q-dialog"); + const menus = document.querySelectorAll(".q-menu"); dialogs.forEach(x => x.remove()); + menus.forEach(x => x.remove()); }); @@ -116,17 +119,17 @@ describe("AutomationManager.vue", () => { }); - it("shows edit policy modal on edit button press", async () => { - - const button = wrapper.findComponent({ ref: "edit" }); + it("shows edit policy modal on edit context menu button press", async () => { expect(bodyWrapper.find(".q-dialog").exists()).toBe(false); - await button.trigger("click") - expect(bodyWrapper.find(".q-dialog").exists()).toBe(false); + expect(bodyWrapper.find(".q-menu").exists()).toBe(false); - //Select Row - await wrapper.find("tbody > tr.q-tr").trigger("click"); - await button.trigger("click"); + // Right Click on Row + await wrapper.find("tbody > tr.q-tr").trigger("contextmenu"); + expect(bodyWrapper.find(".q-menu").exists()).toBe(true); + await bodyWrapper.find("#context-edit").trigger("click"); + + expect(wrapper.vm.editPolicyId).toBe(1); expect(bodyWrapper.find(".q-dialog").exists()).toBe(true); }); @@ -137,19 +140,21 @@ describe("AutomationManager.vue", () => { expect(bodyWrapper.find(".q-dialog").exists()).toBe(false); await button.trigger("click"); + + expect(wrapper.vm.editPolicyId).toBe(null); expect(bodyWrapper.find(".q-dialog").exists()).toBe(true); }); it("deletes selected policy", async () => { - const button = wrapper.findComponent({ ref: "delete" }); - expect(bodyWrapper.find(".q-dialog").exists()).toBe(false); - // Select Row - await wrapper.find("tbody > tr.q-tr").trigger("click"); - await button.trigger("click"); - expect(bodyWrapper.find(".q-dialog").exists()).toBe(true); + expect(bodyWrapper.find(".q-menu").exists()).toBe(false); + + // Right Click on Row + await wrapper.find("tbody > tr.q-tr").trigger("contextmenu"); + expect(bodyWrapper.find(".q-menu").exists()).toBe(true); + await bodyWrapper.find("#context-delete").trigger("click"); //Get OK button and click it bodyWrapper.findAll(".q-btn").wrappers[1].trigger("click"); @@ -179,4 +184,19 @@ describe("AutomationManager.vue", () => { }); + it("shows relation modal on context menu button press", async () => { + + expect(bodyWrapper.find(".q-dialog").exists()).toBe(false); + expect(bodyWrapper.find(".q-menu").exists()).toBe(false); + + // Right Click on Row + await wrapper.find("tbody > tr.q-tr").trigger("contextmenu"); + expect(bodyWrapper.find(".q-menu").exists()).toBe(true); + await bodyWrapper.find("#context-relation").trigger("click"); + + expect(wrapper.vm.policy).toBe(policiesData[0]); + expect(bodyWrapper.find(".q-dialog").exists()).toBe(true); + + }); + }); diff --git a/tests/unit/automation/automationmodals.spec.js b/tests/unit/automation/formmodal.spec.js similarity index 80% rename from tests/unit/automation/automationmodals.spec.js rename to tests/unit/automation/formmodal.spec.js index b8cfa90..7c79025 100644 --- a/tests/unit/automation/automationmodals.spec.js +++ b/tests/unit/automation/formmodal.spec.js @@ -1,4 +1,5 @@ -import { mount, createLocalVue } from "@vue/test-utils"; +import { mount, createLocalVue, createWrapper } from "@vue/test-utils"; +import flushPromises from "flush-promises"; import Vuex from "vuex"; import PolicyForm from "@/components/automation/modals/PolicyForm"; import "@/quasar.js" @@ -8,17 +9,40 @@ localVue.use(Vuex); describe("PolicyForm.vue", () => { - const clients = []; - const sites = []; - const agents = []; + const clients = [ + { + id: 1, + client: "Test Client" + }, + { + id: 2, + client: "Test Client2" + }, + { + id: 3, + client: "Test Client3" + } + ]; + const sites = [ + { + id: 1, + site: "Site Name", + client_name: "Test Client" + }, + { + id: 2, + site: "Site Name2", + client_name: "Test Client2" + } + ]; const policy = { id: 1, name: "Test Policy", active: true, - clients: [{id: 1, client: "Test Name"}], - sites: [{id: 1, site: "Test Name"}], - agents: [{pk: 1, hostname: "Test Name"}] + clients: [], + sites: [], + agents: [] }; let methods; @@ -34,7 +58,6 @@ describe("PolicyForm.vue", () => { rootActions = { loadClients: jest.fn(() => new Promise(res => res({ data: clients }))), loadSites: jest.fn(() => new Promise(res => res({ data: sites }))), - loadAgents: jest.fn(() => new Promise(res => res({ data: agents }))), }; actions = { @@ -65,7 +88,6 @@ describe("PolicyForm.vue", () => { expect(rootActions.loadClients).toHaveBeenCalled(); expect(rootActions.loadSites).toHaveBeenCalled(); - expect(rootActions.loadAgents).toHaveBeenCalled(); // Not called unless pk prop is set expect(actions.loadPolicy).not.toHaveBeenCalled(); @@ -74,7 +96,7 @@ describe("PolicyForm.vue", () => { it("calls vuex actions on mount with pk prop set", () => { - const wrapper = mount(PolicyForm, { + mount(PolicyForm, { localVue, store, propsData: { @@ -84,14 +106,24 @@ describe("PolicyForm.vue", () => { expect(rootActions.loadClients).toHaveBeenCalled(); expect(rootActions.loadSites).toHaveBeenCalled(); - expect(rootActions.loadAgents).toHaveBeenCalled(); expect(actions.loadPolicy).toHaveBeenCalled(); }); - /*it("renders the client, site, and agent dropdowns correctly", async () => { + it("Sets client and site options correctly", async () => { - })*/ + const wrapper = mount(PolicyForm, { + localVue, + store + }); + + // Make sure the promises are resolved + await flushPromises(); + + expect(wrapper.vm.clientOptions).toHaveLength(3); + expect(wrapper.vm.siteOptions).toHaveLength(2); + + }); it("sends the correct add action on submit", async () => { diff --git a/tests/unit/automation/relationview.spec.js b/tests/unit/automation/relationview.spec.js new file mode 100644 index 0000000..d36b219 --- /dev/null +++ b/tests/unit/automation/relationview.spec.js @@ -0,0 +1,121 @@ +import { mount, createLocalVue } from "@vue/test-utils"; +import Vuex from "vuex"; +import RelationsView from "@/components/automation/modals/RelationsView"; +import "@/quasar.js" + +const localVue = createLocalVue(); +localVue.use(Vuex); + +describe("Relations.vue", () => { + + const policy = { + id: 1, + name: "Test Policy", + active: true, + clients: [{id: 1, client: "Test Name"}], + sites: [{id: 1, site: "Test Name"}], + agents: [] + }; + + const related = { + agents: [ + { + pk: 1, + hostname: "Test Name", + site: "Test Site", + client: "Test Client"}, + { + pk: 2, + site: "Test Site", + hostname: "Test Name2", + site: "Test Site", + client: "Test Client" + } + ], + sites: [ + { + id: 1, + client_name: "Test Name", + site: "Test Name" + } + ], + clients: [ + { + id: 1, + client: "Test Name" + }, + { + id: 2, + client: "Test Name2" + }, + { + id: 3, + client: "Test Name3" + } + ] + }; + + let wrapper, actions, store; + + // Runs before every test + beforeEach(() => { + + actions = { + getRelated: jest.fn(() => new Promise(res => res({ data: related }))), + }; + + store = new Vuex.Store({ + modules: { + automation: { + namespaced: true, + actions, + } + } + }); + + wrapper = mount(RelationsView, { + localVue, + store, + propsData: { + policy: policy + } + }); + + }); + + // The Tests + it("calls vuex actions on mount", () => { + + expect(actions.getRelated).toHaveBeenCalledWith(expect.anything(), policy.id); + + }); + + it("Checks the correct number of list items are rendered in clients tab", async () => { + + await wrapper.findComponent({ref: "clients"}).trigger("click"); + + const list = wrapper.findAll(".q-item"); + expect(list.length).toBeGreaterThanOrEqual(related.clients.length); + + }); + + it("Checks the correct number of list items are rendered in sites tab", async () => { + + await wrapper.findComponent({ref: "sites"}).trigger("click"); + + const list = wrapper.findAll(".q-item"); + expect(list.length).toBeGreaterThanOrEqual(related.sites.length); + + }); + + it("Checks the correct number of list items are rendered in agents tab", async () => { + + await wrapper.findComponent({ref: "agents"}).trigger("click"); + + const list = wrapper.findAll(".q-item"); + expect(list.length).toBeGreaterThanOrEqual(related.agents.length); + + }); + +}); +