reworked policy add for client, site, and agent. removed vue unit tests, added alertign to auto tasks, added edit autotask capabilities for certain fields, moved policy generation logic to save method on Client, Site, Agent, Policy models

This commit is contained in:
sadnub
2021-01-29 13:25:58 -05:00
parent 76d3efe20b
commit 351fe17bb6
16 changed files with 378 additions and 1836 deletions

View File

@@ -665,12 +665,16 @@ export default {
}
},
showPolicyAdd(agent) {
this.$q.dialog({
component: PolicyAdd,
parent: this,
type: "agent",
object: agent,
});
this.$q
.dialog({
component: PolicyAdd,
parent: this,
type: "agent",
object: agent,
})
.onOk(() => {
this.$emit("refreshEdit");
});
},
toggleMaintenance(agent) {
let data = {

View File

@@ -33,6 +33,21 @@
<small>Enabled</small>
</q-th>
</template>
<template v-slot:header-cell-smsalert="props">
<q-th auto-width :props="props">
<q-icon name="phone_android" size="1.5em">
<q-tooltip>SMS Alert</q-tooltip>
</q-icon>
</q-th>
</template>
<template v-slot:header-cell-emailalert="props">
<q-th auto-width :props="props">
<q-icon name="email" size="1.5em">
<q-tooltip>Email Alert</q-tooltip>
</q-icon>
</q-th>
</template>
<template v-slot:header-cell-policystatus="props">
<q-th auto-width :props="props"></q-th>
</template>
@@ -48,13 +63,7 @@
</q-item-section>
<q-item-section>Run task now</q-item-section>
</q-item>
<q-item
clickable
v-close-popup
@click="showEditAutomatedTask = true"
v-if="!props.row.managed_by_policy"
v-show="false"
>
<q-item clickable v-close-popup @click="showEditTask(props.row)" v-if="!props.row.managed_by_policy">
<q-item-section side>
<q-icon name="edit" />
</q-item-section>
@@ -86,6 +95,24 @@
:disable="props.row.managed_by_policy"
/>
</q-td>
<!-- text alert -->
<q-td>
<q-checkbox
dense
@input="taskAlert(props.row.id, 'Text', props.row.text_alert, props.row.managed_by_policy)"
v-model="props.row.text_alert"
:disable="props.row.managed_by_policy"
/>
</q-td>
<!-- email alert -->
<q-td>
<q-checkbox
dense
@input="taskAlert(props.row.id, 'Email', props.row.email_alert, props.row.managed_by_policy)"
v-model="props.row.email_alert"
:disable="props.row.managed_by_policy"
/>
</q-td>
<!-- policy check icon -->
<q-td v-if="props.row.managed_by_policy">
<q-icon style="font-size: 1.3rem" name="policy">
@@ -139,6 +166,7 @@ import { mapState } from "vuex";
import { mapGetters } from "vuex";
import mixins from "@/mixins/mixins";
import AddAutomatedTask from "@/components/modals/tasks/AddAutomatedTask";
import EditAutomatedTask from "@/components/modals/tasks/EditAutomatedTask";
import ScriptOutput from "@/components/modals/checks/ScriptOutput";
export default {
@@ -155,6 +183,8 @@ export default {
scriptInfo: {},
columns: [
{ name: "enabled", align: "left", field: "enabled" },
{ name: "smsalert", field: "text_alert", align: "left" },
{ name: "emailalert", field: "email_alert", align: "left" },
{ name: "policystatus", align: "left" },
{ name: "name", label: "Name", field: "name", align: "left" },
{ name: "sync_status", label: "Sync Status", field: "sync_status", align: "left" },
@@ -202,6 +232,34 @@ export default {
})
.catch(e => this.notifyError("Something went wrong"));
},
taskAlert(pk, alert_type, action, managed_by_policy) {
if (managed_by_policy) {
return;
}
this.$q.loading.show();
const data = {
id: pk,
};
if (alert_type === "Email") {
data.email_alert = action;
} else {
data.text_alert = action;
}
const act = action ? "enabled" : "disabled";
axios
.put(`/tasks/${pk}/automatedtasks/`, data)
.then(r => {
this.$q.loading.hide();
this.notifySuccess(`${alert_type} alerts ${act}`);
})
.catch(e => {
this.$q.loading.hide();
this.notifyError("There was an issue editing task");
});
},
refreshTasks(id) {
this.$store.dispatch("loadAutomatedTasks", id);
},
@@ -209,6 +267,17 @@ export default {
this.scriptInfo = props;
this.showScriptOutput = true;
},
showEditTask(task) {
this.$q
.dialog({
component: EditAutomatedTask,
parent: this,
task: task,
})
.onOk(() => {
this.refreshTasks(this.automatedTasks.pk);
});
},
runTask(pk, enabled) {
if (!enabled) {
this.notifyError("Task cannot be run when it's disabled. Enable it first.");

View File

@@ -39,6 +39,22 @@
<small>Enabled</small>
</q-th>
</template>
<template v-slot:header-cell-smsalert="props">
<q-th auto-width :props="props">
<q-icon name="phone_android" size="1.5em">
<q-tooltip>SMS Alert</q-tooltip>
</q-icon>
</q-th>
</template>
<template v-slot:header-cell-emailalert="props">
<q-th auto-width :props="props">
<q-icon name="email" size="1.5em">
<q-tooltip>Email Alert</q-tooltip>
</q-icon>
</q-th>
</template>
<!-- body slots -->
<template v-slot:body="props" :props="props">
<q-tr @contextmenu="editTaskPk = props.row.id">
@@ -51,6 +67,12 @@
</q-item-section>
<q-item-section>Run task now</q-item-section>
</q-item>
<q-item clickable v-close-popup @click="showEditTask(props.row)" v-if="!props.row.managed_by_policy">
<q-item-section side>
<q-icon name="edit" />
</q-item-section>
<q-item-section>Edit</q-item-section>
</q-item>
<q-item clickable v-close-popup @click="deleteTask(props.row.name, props.row.id)">
<q-item-section side>
<q-icon name="delete" />
@@ -78,6 +100,22 @@
v-model="props.row.enabled"
/>
</q-td>
<q-td>
<q-checkbox
dense
@input="taskAlert(props.row.id, 'Text', props.row.text_alert, props.row.managed_by_policy)"
v-model="props.row.text_alert"
/>
</q-td>
<!-- email alert -->
<q-td>
<q-checkbox
dense
@input="taskAlert(props.row.id, 'Email', props.row.email_alert, props.row.managed_by_policy)"
v-model="props.row.email_alert"
/>
</q-td>
<q-td>{{ props.row.name }}</q-td>
<q-td>{{ props.row.schedule }}</q-td>
<q-td>
@@ -110,8 +148,8 @@
<script>
import mixins from "@/mixins/mixins";
import DialogWrapper from "@/components/ui/DialogWrapper";
import AddAutomatedTask from "@/components/modals/tasks/AddAutomatedTask";
import EditAutomatedTask from "@/components/modals/tasks/EditAutomatedTask";
import PolicyStatus from "@/components/automation/modals/PolicyStatus";
export default {
@@ -127,6 +165,8 @@ export default {
showAddTask: false,
columns: [
{ name: "enabled", align: "left", field: "enabled" },
{ name: "smsalert", field: "text_alert", align: "left" },
{ name: "emailalert", field: "email_alert", align: "left" },
{ name: "name", label: "Name", field: "name", align: "left" },
{
name: "schedule",
@@ -188,6 +228,42 @@ export default {
this.notifyError("There was an issue editing the task");
});
},
taskAlert(pk, alert_type, action, managed_by_policy) {
this.$q.loading.show();
const data = {
id: pk,
};
if (alert_type === "Email") {
data.email_alert = action;
} else {
data.text_alert = action;
}
const act = action ? "enabled" : "disabled";
this.$axios
.put(`/tasks/${pk}/automatedtasks/`, data)
.then(r => {
this.$q.loading.hide();
this.notifySuccess(`${alert_type} alerts ${act}`);
})
.catch(e => {
this.$q.loading.hide();
this.notifyError("There was an issue editing task");
});
},
showEditTask(task) {
this.$q
.dialog({
component: EditAutomatedTask,
parent: this,
task: task,
})
.onOk(() => {
this.getTasks();
});
},
showStatus(task) {
this.$q.dialog({
component: PolicyStatus,

View File

@@ -1,6 +1,6 @@
<template>
<q-dialog ref="dialog" @hide="onHide">
<q-card class="q-dialog-plugin" style="width: 900px; max-width: 90vw">
<q-card class="q-dialog-plugin" style="width: 90vw; max-width: 90vw">
<q-bar>
<q-btn @click="getPolicyTree" class="q-mr-sm" dense flat push icon="refresh" />Policy Overview
<q-space />

View File

@@ -109,29 +109,47 @@ export default {
}
this.$q.loading.show();
let data = {
pk: this.object.id,
type: this.type,
};
let data = {};
let url = "";
if (this.type === "client" || this.type === "site") {
data.server_policy = this.selectedServerPolicy;
data.workstation_policy = this.selectedWorkstationPolicy;
} else if (this.type === "agent") {
data.policy = this.selectedAgentPolicy;
}
data = {
pk: this.object.id,
server_policy: this.selectedServerPolicy,
workstation_policy: this.selectedWorkstationPolicy,
};
this.$axios
.post(`/automation/related/`, data)
.then(r => {
this.$q.loading.hide();
this.onOk();
this.notifySuccess("Policies Updated Successfully!");
})
.catch(e => {
this.$q.loading.hide();
this.notifyError("There was an error updating policies");
});
if (this.type === "client") url = `/clients/${this.object.id}/client/`;
else if (this.type === "site") url = `/clients/${this.object.id}/site/`;
this.$axios
.put(url, data)
.then(r => {
this.$q.loading.hide();
this.onOk();
this.notifySuccess("Policies Updated Successfully!");
})
.catch(e => {
this.$q.loading.hide();
this.notifyError("There was an error updating policies");
});
} else if (this.type === "agent") {
data = {
id: this.object.id,
policy: this.selectedAgentPolicy,
};
this.$axios
.patch("/agents/editagent/", data)
.then(r => {
this.$q.loading.hide();
this.onOk();
this.notifySuccess("Policies Updated Successfully!");
})
.catch(e => {
this.$q.loading.hide();
this.notifyError("There was an error updating policies");
});
}
},
getPolicies() {
this.$q.loading.show();

View File

@@ -56,11 +56,14 @@
</q-card-section>
<q-card-section>
<q-select
v-model="autotask.alert_severity"
:options="severityOptions"
dense
label="Alert Severity"
outlined
v-model="autotask.alert_severity"
:options="severityOptions"
map-options
emit-value
options-dense
/>
</q-card-section>
<q-card-section>
@@ -180,14 +183,13 @@ export default {
remove_if_not_scheduled: false,
task_type: "scheduled",
timeout: 120,
alert_severity: null,
severityOptions: [
{ label: "", value: null },
{ label: "Informational", value: "info" },
{ label: "Warning", value: "warning" },
{ label: "Error", value: "error" },
],
alert_severity: info,
},
severityOptions: [
{ label: "Informational", value: "info" },
{ label: "Warning", value: "warning" },
{ label: "Error", value: "error" },
],
dayOptions: [
{ label: "Monday", value: "Monday" },
{ label: "Tuesday", value: "Tuesday" },

View File

@@ -0,0 +1,164 @@
<template>
<q-dialog ref="dialog" @hide="onHide">
<q-card class="q-dialog-plugin" style="width: 60vw">
<q-bar>
Edit {{ task.name }}
<q-space />
<q-btn dense flat icon="close" v-close-popup>
<q-tooltip content-class="bg-white text-primary">Close</q-tooltip>
</q-btn>
</q-bar>
<q-form @submit="submit">
<q-card-section>
<q-select
:rules="[val => !!val || '*Required']"
dense
options-dense
outlined
v-model="localTask.script"
:options="scriptOptions"
label="Select script"
map-options
emit-value
/>
</q-card-section>
<q-card-section>
<q-select
dense
label="Script Arguments (press Enter after typing each argument)"
filled
v-model="localTask.script_args"
use-input
use-chips
multiple
hide-dropdown-icon
input-debounce="0"
new-value-mode="add"
/>
</q-card-section>
<q-card-section>
<q-input
:rules="[val => !!val || '*Required']"
outlined
dense
v-model="localTask.name"
label="Descriptive name of task"
/>
</q-card-section>
<q-card-section>
<q-select
v-model="localTask.alert_severity"
:options="severityOptions"
dense
label="Alert Severity"
outlined
map-options
emit-value
options-dense
/>
</q-card-section>
<q-card-section>
<q-input
:rules="[val => !!val || '*Required']"
outlined
dense
v-model.number="localTask.timeout"
type="number"
label="Maximum permitted execution time (seconds)"
/>
</q-card-section>
<q-card-actions align="right">
<q-btn dense flat label="Cancel" v-close-popup />
<q-btn flat label="Submit" color="primary" type="submit" />
</q-card-actions>
</q-form>
</q-card>
</q-dialog>
</template>
<script>
import mixins from "@/mixins/mixins";
export default {
name: "EditAutomatedTask",
mixins: [mixins],
props: {
task: !Object,
},
data() {
return {
localTask: {
id: null,
name: "",
script: null,
script_args: [],
alert_severity: null,
timeout: 120,
},
scriptOptions: [],
severityOptions: [
{ label: "Informational", value: "info" },
{ label: "Warning", value: "warning" },
{ label: "Error", value: "error" },
],
};
},
methods: {
submit() {
this.$q.loading.show();
this.$axios
.put(`/tasks/${this.localTask.id}/automatedtasks/`, this.localTask)
.then(r => {
this.$q.loading.hide();
this.onOk();
this.notifySuccess("Task was edited successfully");
})
.catch(e => {
this.$q.loading.hide();
this.notifyError("There was an issue editing the task");
});
},
getScripts() {
this.$q.loading.show();
this.$axios
.get("/scripts/scripts/")
.then(r => {
this.scriptOptions = r.data
.map(script => ({ label: script.name, value: script.id }))
.sort((a, b) => a.label.localeCompare(b.label));
this.$q.loading.hide();
})
.catch(e => {
this.$q.loading.hide();
this.notifyError("There was an error getting scripts");
});
},
show() {
this.$refs.dialog.show();
},
hide() {
this.$refs.dialog.hide();
},
onHide() {
this.$emit("hide");
},
onOk() {
this.$emit("ok");
this.hide();
},
},
mounted() {
this.getScripts();
// copy only certain task props locally
this.localTask.id = this.task.id;
this.localTask.name = this.task.name;
this.localTask.script = this.task.script;
this.localTask.script_args = this.task.script_args;
this.localTask.alert_severity = this.task.alert_severity;
this.localTask.timeout = this.task.timeout;
},
};
</script>

View File

@@ -1,248 +0,0 @@
import { mount, createWrapper, createLocalVue } from "@vue/test-utils";
import Vuex from "vuex";
import AutomationManager from "@/components/automation/AutomationManager";
import "../../utils/quasar.js";
const localVue = createLocalVue();
localVue.use(Vuex);
const bodyWrapper = createWrapper(document.body);
// 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());
});
describe("AutomationManager.vue", () => {
const policiesData = [
{
id: 1,
name: "Test Policy",
desc: "Description",
active: true,
clients: [],
sites: [{}, {}],
agents: [{}]
},
{
id: 2,
name: "Test Policy 2",
desc: "Description 2",
active: false,
clients: [],
sites: [{}, {}],
agents: [{}]
}
];
let wrapper;
let state, mutations, actions, store;
// Runs before every test
beforeEach(() => {
// Create the Test store
state = {
selectedPolicy: null,
checks: {},
automatedTasks: {},
policies: policiesData,
};
mutations = {
setSelectedPolicy: jest.fn((state, key) => { state.selectedPolicy = key }),
setPolicyChecks: jest.fn(),
setPolicyAutomatedTasks: jest.fn(),
};
actions = {
loadPolicies: jest.fn(),
loadPolicyChecks: jest.fn(),
loadPolicyAutomatedTasks: jest.fn(),
deletePolicy: jest.fn()
};
store = new Vuex.Store({
modules: {
automation: {
namespaced: true,
state,
mutations,
actions
}
}
});
// Mount all sub components except the ones specified
wrapper = mount(AutomationManager, {
store,
localVue,
stubs: [
"PolicySubTableTabs",,
"PolicyOverview",
"PolicyForm",
"RelationsView"
]
});
});
// The Tests
it("calls vuex loadPolicies action on mount", () => {
expect(actions.loadPolicies).toHaveBeenCalled();
expect(mutations.setSelectedPolicy).toHaveBeenCalledWith(expect.anything(), null);
expect(mutations.setPolicyChecks).toHaveBeenCalledWith(expect.anything(), []);
expect(mutations.setPolicyAutomatedTasks).toHaveBeenCalledWith(expect.anything(), {});
});
it("renders table when policies is set from store computed", () => {
const rows = wrapper.findAll("tbody > tr.q-tr").wrappers;
expect(rows).toHaveLength(2);
});
it("sends vuex mutations and actions when policy is selected", () => {
const row = wrapper.findAll("tbody > tr.q-tr").wrappers[1];
row.trigger("click");
expect(mutations.setSelectedPolicy).toHaveBeenCalledWith(expect.anything(), 2);
expect(actions.loadPolicyChecks).toHaveBeenCalledWith(expect.anything(), 2);
expect(actions.loadPolicyAutomatedTasks).toHaveBeenCalledWith(expect.anything(), 2);
});
it("shows edit policy modal on edit 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 > .q-tr").trigger("contextmenu");
await localVue.nextTick();
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);
});
it("shows add policy modal on button press", async () => {
const button = wrapper.findComponent({ ref: "new" });
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("shows edit policy modal on edit button press", async () => {
const button = wrapper.findComponent({ ref: "edit" });
expect(bodyWrapper.find(".q-dialog").exists()).toBe(false);
await button.trigger("click")
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);
});
it("deletes selected policy on delete button press", 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);
//Get OK button and click it
await bodyWrapper.findAll(".q-btn").wrappers[1].trigger("click");
expect(actions.deletePolicy).toHaveBeenCalledWith(expect.anything(), 1);
});
it("deletes selected policy", 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");
await localVue.nextTick();
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");
expect(actions.deletePolicy).toHaveBeenCalledWith(expect.anything(), 1);
});
it("shows overview modal when button clicked", async () => {
const button = wrapper.findComponent({ ref: "overview" });
expect(bodyWrapper.find(".q-dialog").exists()).toBe(false);
await button.trigger("click");
expect(bodyWrapper.find(".q-dialog").exists()).toBe(true);
expect(mutations.setSelectedPolicy).toHaveBeenCalledWith(expect.anything(), null);
expect(mutations.setPolicyChecks).toHaveBeenCalledWith(expect.anything(), []);
expect(mutations.setPolicyAutomatedTasks).toHaveBeenCalledWith(expect.anything(), {});
});
it("calls vuex loadPolicies action when refresh button clicked and clears selected", () => {
const button = wrapper.findComponent({ ref: "refresh" });
button.trigger("click");
expect(actions.loadPolicies).toHaveBeenCalled();
expect(mutations.setSelectedPolicy).toHaveBeenCalledWith(expect.anything(), null);
expect(mutations.setPolicyChecks).toHaveBeenCalledWith(expect.anything(), []);
expect(mutations.setPolicyAutomatedTasks).toHaveBeenCalledWith(expect.anything(), {});
});
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");
await localVue.nextTick();
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);
});
// TODO Test Checkboxes on table
// TODO: Test @close and @hide events
});

View File

@@ -1,106 +0,0 @@
const common = {
email_alert: false,
failure_count: 0,
failures: 5,
history: [],
last_run: null,
more_info: null,
status: "pending",
task_on_failure: null,
text_alert: false,
agent: null,
policy: 1,
assignedtask: []
};
const diskcheck = {
id: 1,
check_type: "diskspace",
disk: "C:",
threshold: 25,
readable_desc: "Disk space check: Drive C",
...common
};
const cpuloadcheck = {
id: 2,
check_type: "cpuload",
cpuload: 85,
readable_desc: "CPU Load check: > 85%",
...common
};
const memcheck = {
id: 3,
check_type: "memory",
threshold: 75,
readable_desc: "Memory checks: > 85%",
...common
};
const scriptcheck = {
id: 4,
check_type: "script",
execution_time: "0.0000",
retcode: 0,
script: {
description: "Test",
filename: "local_admin_group.bat",
filepath: "salt://scripts//userdefined//local_admin_group.bat",
id: 1,
name: "Test Script",
shell: "cmd"
},
stderr: null,
stdout: null,
timeout: 120,
readable_desc: "Script check: Test Script",
...common
};
const winservicecheck = {
id: 5,
check_type: "winsvc",
pass_if_start_pending: false,
restart_if_stopped: false,
svc_display_name: "Agent Activation Runtime_1232as",
svc_name: "AarSvc_1232as",
readable_desc: "Service check: Agent Activation Runtime_1232as",
...common
};
const pingcheck = {
id: 6,
name: "fghfgh",
check_type: "ping",
ip: "10.10.10.10",
readable_desc: "Ping Check: Test Ping Check",
...common
};
const eventlogcheck = {
id: 7,
desc: "asasasa",
check_type: "eventlog",
log_name: "Application",
event_id: 1456,
event_type: "ERROR",
fail_when: "contains",
search_last_days: 1,
readable_desc: "Event log check: asdsasa",
...common,
};
const tasks = [{ "id": 1, "assigned_check": null, "schedule": "Manual", "name": "fghf", "run_time_days": [], "run_time_minute": null, "task_type": "manual", "win_task_name": "TacticalRMM_UiOgoHSkrxtqWbSZVCGhvjjGdQflZlpnjkd", "timeout": 120, "retcode": null, "stdout": null, "stderr": null, "execution_time": "0.0000", "last_run": null, "enabled": true, "agent": null, "policy": 1, "script": 1 }, { "id": 2, "assigned_check": null, "schedule": "Mon,Tue at 12:03 AM", "name": "vjhkgh", "run_time_days": [0, 1], "run_time_minute": "00:03", "task_type": "scheduled", "win_task_name": "TacticalRMM_KlgPQGGgoVcGEnDXXxJSDjymyTGArTzsVSw", "timeout": 120, "retcode": null, "stdout": null, "stderr": null, "execution_time": "0.0000", "last_run": null, "enabled": true, "agent": null, "policy": 1, "script": 1 }, { "id": 3, "assigned_check": { "id": 2, "readable_desc": "Ping Check: 10.10.10.10", "script": null, "assigned_task": { "id": 1, "name": "test", "run_time_days": [], "run_time_minute": null, "task_type": "checkfailure", "win_task_name": "TacticalRMM_XROsZidGyNuUEwzvvdlgveDwxsrIKRAlHpr", "timeout": 120, "retcode": null, "stdout": null, "stderr": null, "execution_time": "0.0000", "last_run": null, "enabled": true, "agent": null, "policy": 1, "script": 1, "assigned_check": 2 }, "history_info": null, "name": "asdfasd", "check_type": "ping", "status": "pending", "more_info": null, "last_run": null, "email_alert": false, "text_alert": false, "fails_b4_alert": 1, "fail_count": 0, "email_sent": null, "text_sent": null, "outage_history": null, "extra_details": null, "threshold": null, "disk": null, "ip": "10.10.10.10", "timeout": null, "stdout": null, "stderr": null, "retcode": null, "execution_time": null, "history": [], "svc_name": null, "svc_display_name": null, "pass_if_start_pending": null, "restart_if_stopped": null, "svc_policy_mode": null, "log_name": null, "event_id": null, "event_type": null, "fail_when": null, "search_last_days": null, "agent": null, "policy": 1 }, "schedule": "Every time check fails", "name": "test", "run_time_days": [], "run_time_minute": null, "task_type": "checkfailure", "win_task_name": "TacticalRMM_XROsZidGyNuUEwzvvdlgveDwxsrIKRAlHpr", "timeout": 120, "retcode": null, "stdout": null, "stderr": null, "execution_time": "0.0000", "last_run": null, "enabled": true, "agent": null, "policy": 1, "script": 1 }]
export {
diskcheck,
cpuloadcheck,
memcheck,
scriptcheck,
winservicecheck,
pingcheck,
eventlogcheck,
tasks
}

View File

@@ -1,128 +0,0 @@
import { mount, createLocalVue } from "@vue/test-utils";
import flushPromises from "flush-promises";
import Vuex from "vuex";
import PolicyForm from "@/components/automation/modals/PolicyForm";
import "../../utils/quasar.js";
const localVue = createLocalVue();
localVue.use(Vuex);
/*** TEST DATA ***/
const policy = {
id: 1,
name: "Test Policy",
desc: "Test Desc",
enforced: false,
active: true
};
let actions, rootActions, store;
beforeEach(() => {
actions = {
loadPolicy: jest.fn(() => new Promise(res => res({ data: policy }))),
addPolicy: jest.fn(() => new Promise(res => res())),
editPolicy: jest.fn(() => new Promise(res => res())),
};
store = new Vuex.Store({
actions: rootActions,
modules: {
automation: {
namespaced: true,
actions,
}
}
});
})
/*** TEST SUITES ***/
describe("PolicyForm.vue when editting", () => {
let wrapper;
beforeEach(() => {
wrapper = mount(PolicyForm, {
localVue,
store,
propsData: {
pk: 1
}
});
});
/*** TESTS ***/
it("calls vuex actions on mount with pk prop set", () => {
expect(actions.loadPolicy).toHaveBeenCalledWith(expect.anything(), 1);
});
it("sends the correct edit action on submit", async () => {
await flushPromises();
const form = wrapper.findComponent({ ref: "form" });
form.vm.$emit("submit");
await wrapper.vm.$nextTick();
expect(actions.addPolicy).not.toHaveBeenCalled();
expect(actions.editPolicy).toHaveBeenCalledWith(expect.anything(), policy);
});
it("Renders correct title on edit", () => {
expect(wrapper.vm.title).toBe("Edit Policy");
});
});
describe("PolicyForm.vue when adding", () => {
let wrapper;
beforeEach(() => {
wrapper = mount(PolicyForm, {
localVue,
store
});
});
/*** TESTS ***/
it("calls vuex actions on mount", () => {
// Not called unless pk prop is set
expect(actions.loadPolicy).not.toHaveBeenCalled();
});
it("sends the correct add action on submit", async () => {
wrapper.setData({name: "Test Policy"});
const form = wrapper.findComponent({ ref: "form" });
form.vm.$emit("submit");
await wrapper.vm.$nextTick();
expect(actions.addPolicy).toHaveBeenCalled();
expect(actions.editPolicy).not.toHaveBeenCalled();
});
it("sends error when name isn't set on submit", async () => {
const form = wrapper.findComponent({ ref: "form" });
form.vm.$emit("submit");
await wrapper.vm.$nextTick();
expect(actions.addPolicy).not.toHaveBeenCalled();
expect(actions.editPolicy).not.toHaveBeenCalled();
});
it("Renders correct title on add", () => {
expect(wrapper.vm.title).toBe("Add Policy");
});
});

View File

@@ -1,139 +0,0 @@
import { mount, createLocalVue } from "test-utils";
import flushpromises from "flush-promises";
import Vuex from "vuex";
import PolicyAdd from "@/components/automation/modals/PolicyAdd";
import "../../utils/quasar.js";
const localVue = createLocalVue();
localVue.use(Vuex);
const related = {
server_policy: {
id: 1,
name: "Test Policy"
},
workstation_policy: {
id: 1,
name: "Test Policy"
}
};
const agentRelated = {
policy: {
id: 1,
name: "Test Policy"
}
};
let state, actions, getters, store;
beforeEach(() => {
state = {
policies: [
{
id: 1,
name: "Test Policy"
},
{
id: 2,
name: "Test Policy 2"
},
{
id: 3,
name: "TestPolicy 3"
}
]
};
actions = {
updateRelatedPolicies: jest.fn(),
loadPolicies: jest.fn(),
getRelatedPolicies: jest.fn(() => Promise.resolve({ data: related })),
};
getters = {
policies: (state) => {
return state.policies
}
};
store = new Vuex.Store({
modules: {
automation: {
namespaced: true,
state,
getters,
actions
}
}
});
})
describe.each([
[1, "client"],
[2, "site"],
[3, "agent"]
])("PolicyAdd.vue with pk:%i and %s type", (pk, type) => {
let wrapper;
beforeEach(() => {
wrapper = mount(PolicyAdd, {
localVue,
store,
propsData: {
pk: pk,
type: type
}
});
});
it("calls vuex actions on mount", async () => {
await flushpromises();
expect(actions.loadPolicies).toHaveBeenCalled();
expect(actions.getRelatedPolicies).toHaveBeenCalledWith(expect.anything(),
{ pk: pk, type: type }
);
});
it("renders title correctly", () => {
expect(wrapper.find(".text-h6").text()).toBe(`Edit policies assigned to ${type}`);
});
it("renders correct amount of policies in dropdown", async () => {
await flushpromises();
expect(wrapper.vm.options).toHaveLength(3);
});
it("renders correct policy in selected", async () => {
await flushpromises();
if (type === "client" || type === "site") {
expect(wrapper.vm.selectedServerPolicy).toStrictEqual({ label: related.server_policy.name, value: related.server_policy.id });
expect(wrapper.vm.selectedWorkstationPolicy).toStrictEqual({ label: related.workstation_policy.name, value: related.workstation_policy.id });
}
// not testing agent
});
it("sends correct data on form submit", async () => {
await flushpromises();
const form = wrapper.findComponent({ ref: "form" });
await form.vm.$emit("submit");
if (type === "client" || type === "site") {
expect(actions.updateRelatedPolicies).toHaveBeenCalledWith(expect.anything(),
{ pk: pk, type: type, server_policy: 1, workstation_policy: 1 }
);
}
// not testing agent actions
});
});

View File

@@ -1,405 +0,0 @@
import { mount, createWrapper, createLocalVue } from "@vue/test-utils";
import PolicyChecksTab from "@/components/automation/PolicyChecksTab";
import Vuex from "vuex";
import "../../utils/quasar.js"
// Import Test Data
import {
diskcheck,
cpuloadcheck,
memcheck,
scriptcheck,
winservicecheck,
pingcheck,
eventlogcheck
} from "./data.js";
const localVue = createLocalVue()
localVue.use(Vuex);
const bodyWrapper = createWrapper(document.body);
// 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());
});
/*** TEST SUITES ***/
describe("PolicyChecksTab.vue with no policy selected", () => {
let wrapper, state, getters, store;
// Runs before every test
beforeEach(() => {
// Create the Test store
state = {
checks: [],
selectedPolicy: null
};
getters = {
checks(state) {
return state.checks
},
selectedPolicyPk(state) {
return state.selectedPolicy
}
};
store = new Vuex.Store({
modules: {
automation: {
namespaced: true,
state,
getters
}
}
});
wrapper = mount(PolicyChecksTab, {
store,
localVue
});
});
/*** TESTS ***/
it("renders text when policy is selected with no checks", () => {
expect(wrapper.html()).toContain("Click on a policy to see the checks");
});
it("doesn't show refresh or add button when no policy selected", () => {
expect(wrapper.findComponent({ ref: "refresh" }).exists()).toBe(false)
expect(wrapper.findComponent({ ref: "add" }).exists()).toBe(false)
});
});
describe("PolicyChecksTab.vue with policy selected and no checks", () => {
// Used for the add check test loop
const addChecksMenu = [
{ name: "DiskSpaceCheck", index: 0 },
{ name: "PingCheck", index: 1},
{ name: "CpuLoadCheck", index: 2},
{ name: "MemCheck", index: 3},
{ name: "WinSvcCheck", index: 4},
{ name: "ScriptCheck", index: 5},
{ name: "EventLogCheck", index: 6}
];
let wrapper, store, state, actions, getters;
// Runs before every test
beforeEach(() => {
// Create the Test store
state = {
checks: [],
selectedPolicy: 1
};
getters = {
checks(state) {
return state.checks
},
selectedPolicyPk(state) {
return state.selectedPolicy
}
};
actions = {
loadPolicyChecks: jest.fn()
};
store = new Vuex.Store({
modules: {
automation: {
namespaced: true,
state,
getters,
actions
}
}
});
// Mount all sub components except the ones specified
wrapper = mount(PolicyChecksTab, {
store,
localVue,
stubs: [
"DiskSpaceCheck",
"PingCheck",
"CpuLoadCheck",
"MemCheck",
"WinSvcCheck",
"ScriptCheck",
"EventLogCheck"
]
});
});
it("renders text when policy is selected with no checks", () => {
expect(wrapper.html()).toContain("There are no checks added to this policy");
});
it("sends vuex actions on refresh button click", () => {
wrapper.findComponent({ ref: "refresh" }).trigger("click");
expect(actions.loadPolicyChecks).toHaveBeenCalledWith(expect.anything(), 1);
});
// Create a test for each Add modal
addChecksMenu.forEach(item => {
it(`opens ${item.name} Dialog`, async () => {
const addButton = wrapper.findComponent({ ref: "add" });
expect(bodyWrapper.find(".q-dialog").exists()).toBe(false);
expect(bodyWrapper.find(".q-menu").exists()).toBe(false);
await addButton.trigger("click");
await localVue.nextTick();
expect(bodyWrapper.find(".q-menu").exists()).toBe(true);
// Selects correct menu item
await bodyWrapper.findAll(".q-item").wrappers[item.index].trigger("click");
expect(bodyWrapper.find(".q-dialog").exists()).toBe(true);
expect(wrapper.vm.showDialog).toBe(true);
expect(wrapper.vm.dialogComponent).toBe(item.name);
});
});
});
describe("PolicyChecksTab.vue with policy selected and checks", () => {
// Used for the edit check test loop
const editChecksModals = [
{name: "DiskSpaceCheck", index: 0, id: 1},
{name: "CpuLoadCheck", index: 1, id: 2},
{name: "MemCheck", index: 2, id: 3},
{name: "ScriptCheck", index: 3, id: 4},
{name: "WinSvcCheck", index: 4, id: 5},
{name: "PingCheck", index: 5, id: 6},
{name: "EventLogCheck", index: 6, id: 7}
];
let state, rootActions, actions, getters, store, wrapper;
// Runs before every test
beforeEach(() => {
// Create the Test store
state = {
checks: [
diskcheck,
cpuloadcheck,
memcheck,
scriptcheck,
winservicecheck,
pingcheck,
eventlogcheck
],
selectedPolicy: 1
};
getters = {
checks(state) {
return state.checks
},
selectedPolicyPk(state) {
return state.selectedPolicy
}
};
actions = {
loadPolicyChecks: jest.fn()
};
rootActions = {
editCheckAlert: jest.fn(),
deleteCheck: jest.fn()
};
store = new Vuex.Store({
actions: rootActions,
modules: {
automation: {
namespaced: true,
state,
getters,
actions
}
}
});
// Mount all sub components except the ones specified
wrapper = mount(PolicyChecksTab, {
store,
localVue,
stubs: [
"DiskSpaceCheck",
"PingCheck",
"CpuLoadCheck",
"MemCheck",
"WinSvcCheck",
"ScriptCheck",
"EventLogCheck",
"PolicyStatus"
]
});
});
/*** TESTS ***/
it("renders the correct number of rows based on checks", () => {
const rows = wrapper.findAll(".q-table > tbody > .q-tr").wrappers;
expect(rows).toHaveLength(7);
});
// Create a test for each Edit modal
editChecksModals.forEach(item => {
it(`show ${item.name} Dialog`, async () => {
expect(bodyWrapper.find(".q-dialog").exists()).toBe(false);
expect(bodyWrapper.find(".q-menu").exists()).toBe(false);
const row = wrapper.findAll(".q-table > tbody > .q-tr").wrappers[item.index];
await row.trigger("contextmenu");
await localVue.nextTick();
expect(bodyWrapper.find(".q-menu").exists()).toBe(true);
await bodyWrapper.find("#context-edit").trigger("click");
expect(bodyWrapper.find(".q-dialog").exists()).toBe(true);
expect(wrapper.vm.showDialog).toBe(true);
expect(wrapper.vm.dialogComponent).toBe(item.name);
expect(wrapper.vm.editCheckPK).toBe(item.id);
});
});
it("shows policy status modal on cell click", async () => {
expect(bodyWrapper.find(".q-dialog").exists()).toBe(false);
const row = wrapper.findAll(".status-cell").wrappers[0];
await row.trigger("click");
expect(bodyWrapper.find(".q-dialog").exists()).toBe(true);
expect(wrapper.vm.statusCheck).toEqual(diskcheck);
});
it("shows policy status modal on context menu item click", async () => {
expect(bodyWrapper.find(".q-dialog").exists()).toBe(false);
expect(bodyWrapper.find(".q-menu").exists()).toBe(false);
const row = wrapper.findAll(".q-table > tbody > .q-tr").wrappers[0];
await row.trigger("contextmenu");
await localVue.nextTick();
expect(bodyWrapper.find(".q-menu").exists()).toBe(true);
await bodyWrapper.find("#context-status").trigger("click");
expect(bodyWrapper.find(".q-dialog").exists()).toBe(true);
expect(wrapper.vm.statusCheck).toEqual(diskcheck);
});
it("deletes check", async () => {
expect(bodyWrapper.find(".q-dialog").exists()).toBe(false);
expect(bodyWrapper.find(".q-menu").exists()).toBe(false);
const row = wrapper.findAll(".q-table > tbody > .q-tr").wrappers[0];
await row.trigger("contextmenu");
await localVue.nextTick();
expect(bodyWrapper.find(".q-menu").exists()).toBe(true);
await bodyWrapper.find("#context-delete").trigger("click");
expect(bodyWrapper.find(".q-dialog").exists()).toBe(true);
//Get OK button on confirmation dialog and click it
await bodyWrapper.findAll(".q-btn").wrappers[1].trigger("click");
expect(rootActions.deleteCheck).toHaveBeenCalledWith(expect.anything(), 1);
expect(actions.loadPolicyChecks).toHaveBeenCalled();
});
it("enables and disables text alerts for check", async () => {
//Get first checkbox in first row
const row = wrapper.findAll(".q-checkbox").wrappers[0];
//Enable Text Alert
await row.trigger("click");
expect(rootActions.editCheckAlert).toHaveBeenCalledWith(
expect.anything(),
{
pk: 1,
data: {
text_alert: true,
check_alert: true
}
}
);
//Disable Text Alert
await row.trigger("click");
expect(rootActions.editCheckAlert).toHaveBeenCalledWith(
expect.anything(),
{
pk: 1,
data: {
text_alert: false,
check_alert: true
}
}
);
});
it("enables and disables email alerts for check", async () => {
//Get second checkbox in first row
const row = wrapper.findAll(".q-checkbox").wrappers[1];
//Enable Text Alert
await row.trigger("click");
expect(rootActions.editCheckAlert).toHaveBeenCalledWith(
expect.anything(),
{
pk: 1,
data: {
email_alert: true,
check_alert: true
}
}
);
//Disable Text Alert
await row.trigger("click");
expect(rootActions.editCheckAlert).toHaveBeenCalledWith(
expect.anything(),
{
pk: 1,
data: {
email_alert: false,
check_alert: true
}
}
);
});
/* TODO: test @close and @hide events */
});

View File

@@ -1,146 +0,0 @@
import { mount, createLocalVue } from "@vue/test-utils";
import Vuex from "vuex";
import PolicyOverview from "@/components/automation/PolicyOverview";
import "../../utils/quasar.js";
const localVue = createLocalVue();
localVue.use(Vuex);
describe("PolicyOverview.vue", () => {
const policyTreeData = [
{
// node 0
client: "Client Name 1",
workstation_policy: {
id: 1,
name: "Policy Name 1",
active: true
},
server_policy: null,
// node -1
sites: [
{
site: "Site Name 1",
server_policy: null,
workstation_policy: null
}
]
},
{
// node -2
client: "Client Name 2",
server_policy: {
id: 2,
name: "Policy Name 2",
active: true
},
workstation_policy: null,
sites: [
{
// node -3
site: "Site Name 2",
workstation_policy: {
id: 3,
name: "Policy Name 3",
active: false
},
server_policy: {
id: 3,
name: "Policy Name 3",
active: false
}
}
]
}
];
let wrapper, actions, mutations, store;
// Runs before every test
beforeEach(() => {
actions = {
loadPolicyTreeData: jest.fn(() => new Promise(res => res({ data: policyTreeData }))),
loadPolicyChecks: jest.fn(),
loadPolicyAutomatedTasks: jest.fn()
};
mutations = {
setSelectedPolicy: jest.fn()
};
store = new Vuex.Store({
modules: {
automation: {
namespaced: true,
mutations,
actions
}
}
});
wrapper = mount(PolicyOverview, {
localVue,
store,
stubs: [
"PolicyChecksTab",
"PolicyAutomatedTasksTab"
]
});
});
// The Tests
it("calls vuex actions on mount", () => {
expect(actions.loadPolicyTreeData).toHaveBeenCalled();
});
it("renders tree data", async () => {
await localVue.nextTick();
const tree = wrapper.findComponent({ ref: "tree" });
const policy1 = tree.vm.getNodeByKey(1);
const policy2 = tree.vm.getNodeByKey(2);
const policy3 = tree.vm.getNodeByKey(3);
const client1 = tree.vm.getNodeByKey(0);
const client2 = tree.vm.getNodeByKey(-2);
const site1 = tree.vm.getNodeByKey(-1);
const site2 = tree.vm.getNodeByKey(-3);
expect(policy1.label).toBe("Policy Name 1 (Workstations)");
expect(policy2.label).toBe("Policy Name 2 (Servers)");
expect(policy3.label).toBe("Policy Name 3 (Workstations) (disabled)");
expect(client1.label).toBe("Client Name 1");
expect(client2.label).toBe("Client Name 2");
expect(site1.label).toBe("Site Name 1");
expect(site2.label).toBe("Site Name 2");
});
it("selects tree node and updates data property with policy", async () => {
// Expected rendered policy node data
const returnData = {
icon: "policy",
id: 1,
label: "Policy Name 1 (Workstations)"
};
await localVue.nextTick();
// Get second tree node which should be the first policy
wrapper.findAll(".q-tree__node-header").wrappers[1].trigger("click");
expect(wrapper.vm.selectedPolicy).toStrictEqual(returnData);
expect(actions.loadPolicyChecks).toHaveBeenCalledWith(expect.anything(), 1);
expect(mutations.setSelectedPolicy).toHaveBeenCalledWith(expect.anything(), 1);
expect(actions.loadPolicyAutomatedTasks).toHaveBeenCalledWith(expect.anything(), 1);
});
});

View File

@@ -1,176 +0,0 @@
import { mount, createWrapper, createLocalVue } from "@vue/test-utils";
import Vuex from "vuex";
import flushpromises from "flush-promises";
import PolicyStatus from "@/components/automation/modals/PolicyStatus";
import "../../utils/quasar.js";
const localVue = createLocalVue();
localVue.use(Vuex);
const bodyWrapper = createWrapper(document.body);
// 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());
});
// test data
const item = {
id: 1,
readable_desc: "Check Description"
};
describe("PolicyStatus.vue viewing check status", () => {
const data = [
{
hostname: "Agent 1",
status: "pending",
check_type: "ping",
last_run: "Last Run"
},
{
hostname: "Agent 2",
status: "passing",
check_type: "script",
last_run: "Last Run"
},
{
hostname: "Agent 3",
status: "failing",
check_type: "eventlog",
last_run: "Last Run"
},
];
let wrapper, actions, store;
// Runs before every test
beforeEach(() => {
actions = {
loadCheckStatus: jest.fn(() => Promise.resolve({ data: data }))
};
store = new Vuex.Store({
modules: {
automation: {
namespaced: true,
actions,
}
}
});
wrapper = mount(PolicyStatus, {
localVue,
store,
propsData: {
item: item,
type: "check"
},
stubs: [
"ScriptOutput",
"EventLogCheckOutput"
]
});
});
it("calls correct check vuex action on mount", () => {
expect(actions.loadCheckStatus).toHaveBeenCalledWith(expect.anything(), { checkpk: item.id });
});
it("renders correct number of rows in table", async () => {
await flushpromises();
await localVue.nextTick();
const rows = wrapper.findAll(".q-table > tbody > .q-tr").wrappers;
expect(rows).toHaveLength(3);
});
it("shows event log status on cell click", async () => {
await flushpromises();
expect(bodyWrapper.find(".q-dialog").exists()).toBe(false);
const row = wrapper.findAll(".eventlog-cell").wrappers[0];
await row.trigger("click");
expect(bodyWrapper.find(".q-dialog").exists()).toBe(true);
// Needs to be equal to eventlog check in test data array
expect(wrapper.vm.evtLogData).toEqual(data[2]);
});
it("shows script status on cell click", async () => {
await flushpromises();
expect(bodyWrapper.find(".q-dialog").exists()).toBe(false);
const row = wrapper.findAll(".script-cell").wrappers[0];
await row.trigger("click");
expect(bodyWrapper.find(".q-dialog").exists()).toBe(true);
// Needs to be equal to script check in test data array
expect(wrapper.vm.scriptInfo).toEqual(data[1]);
});
it("shows pingstatus on cell click", async () => {
await flushpromises();
expect(bodyWrapper.find(".q-dialog").exists()).toBe(false);
const row = wrapper.findAll(".ping-cell").wrappers[0];
await row.trigger("click");
expect(bodyWrapper.find(".q-dialog").exists()).toBe(true);
});
});
describe("PolicyStatus.vue viewing task status", () => {
const data = [];
let wrapper, actions, store;
// Runs before every test
beforeEach(() => {
actions = {
loadAutomatedTaskStatus: jest.fn(() => Promise.resolve({ data: data })),
};
store = new Vuex.Store({
modules: {
automation: {
namespaced: true,
actions,
}
}
});
wrapper = mount(PolicyStatus, {
localVue,
store,
propsData: {
item: item,
type: "task"
},
stubs: [
"ScriptOutput",
"EventLogCheckOutput"
]
});
});
it("calls correct check vuex action on mount", () => {
expect(actions.loadAutomatedTaskStatus).toHaveBeenCalledWith(expect.anything(), { taskpk: item.id });
});
});

View File

@@ -1,300 +0,0 @@
import { mount, createWrapper, createLocalVue } from "@vue/test-utils";
import PolicyAutomatedTasksTab from "@/components/automation/PolicyAutomatedTasksTab";
import Vuex from "vuex";
import "../../utils/quasar.js";
// Import Test Data
import {
tasks
} from "./data.js";
const localVue = createLocalVue();
localVue.use(Vuex);
const bodyWrapper = createWrapper(document.body);
// 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());
});
/*** TEST SUITES ***/
describe("PolicyAutomatedTasksTab.vue with no policy selected", () => {
let wrapper, state, getters, store;
// Runs before every test
beforeEach(() => {
// Create the Test store
state = {
automatedTasks: {},
selectedPolicy: null
};
getters = {
tasks(state) {
return state.automatedTasks.autotasks;
},
selectedPolicyPk(state) {
return state.selectedPolicy;
}
};
store = new Vuex.Store({
modules: {
automation: {
namespaced: true,
state,
getters
}
}
});
wrapper = mount(PolicyAutomatedTasksTab, {
store,
localVue
});
});
/*** TESTS ***/
it("renders text when policy is selected with no tasks", () => {
expect(wrapper.html()).toContain("Click on a policy to see the tasks");
});
it("doesn't show refresh or add button when no policy selected", () => {
expect(wrapper.findComponent({ ref: "refresh" }).exists()).toBe(false)
expect(wrapper.findComponent({ ref: "add" }).exists()).toBe(false)
});
});
describe("PolicyAutomatedTasksTab.vue with policy selected and no tasks", () => {
let wrapper, store, state, actions, getters;
// Runs before every test
beforeEach(() => {
// Create the Test store
state = {
automatedTasks: {
autotasks: []
},
selectedPolicy: 1
};
getters = {
tasks(state) {
return state.automatedTasks.autotasks;
},
selectedPolicyPk(state) {
return state.selectedPolicy;
}
};
actions = {
loadPolicyAutomatedTasks: jest.fn()
};
store = new Vuex.Store({
modules: {
automation: {
namespaced: true,
state,
getters,
actions
}
}
});
// Mount all sub components except the ones specified
wrapper = mount(PolicyAutomatedTasksTab, {
store,
localVue,
stubs: [
"AddAutomatedTask",
"PolicyStatus"
]
});
});
it("renders text when policy is selected with no tasks", () => {
expect(wrapper.html()).toContain("There are no tasks added to this policy");
});
it("sends vuex actions on refresh button click", () => {
wrapper.findComponent({ ref: "refresh" }).trigger("click");
expect(actions.loadPolicyAutomatedTasks).toHaveBeenCalledWith(expect.anything(), 1);
});
it(`opens AddAutomatedTask Dialog`, async () => {
const addButton = wrapper.findComponent({ ref: "add" });
expect(bodyWrapper.find(".q-dialog").exists()).toBe(false);
expect(bodyWrapper.find(".q-menu").exists()).toBe(false);
await addButton.trigger("click");
expect(bodyWrapper.find(".q-dialog").exists()).toBe(true);
});
});
describe("PolicyChecksTab.vue with policy selected and checks", () => {
let state, rootActions, actions, getters, store, wrapper;
// Runs before every test
beforeEach(() => {
// Create the Test store
state = {
automatedTasks: {
autotasks: tasks
},
selectedPolicy: 1
};
getters = {
tasks(state) {
return state.automatedTasks.autotasks;
},
selectedPolicyPk(state) {
return state.selectedPolicy;
}
};
actions = {
loadPolicyAutomatedTasks: jest.fn(),
runPolicyTask: jest.fn()
};
rootActions = {
editAutoTask: jest.fn(),
deleteAutoTask: jest.fn()
};
store = new Vuex.Store({
actions: rootActions,
modules: {
automation: {
namespaced: true,
state,
getters,
actions
}
}
});
// Mount all sub components except the ones specified
wrapper = mount(PolicyAutomatedTasksTab, {
store,
localVue,
stubs: [
"AddAutomatedTask",
"PolicyStatus"
]
});
});
/*** TESTS ***/
it("renders the correct number of rows based on tasks", () => {
const rows = wrapper.findAll(".q-table > tbody > .q-tr").wrappers;
expect(rows).toHaveLength(3);
});
// Edit Modal
/*it(`show EditAutomatedTask Dialog`, async () => {
expect(bodyWrapper.find(".q-dialog").exists()).toBe(false);
expect(bodyWrapper.find(".q-menu").exists()).toBe(false);
const row = wrapper.findAll(".q-table > tbody > .q-tr").wrappers[1];
await row.trigger("contextmenu");
expect(bodyWrapper.find(".q-menu").exists()).toBe(true);
await bodyWrapper.find("#context-edit").trigger("click");
expect(bodyWrapper.find(".q-dialog").exists()).toBe(true);
});*/
it("shows policy status modal on cell click", async () => {
expect(bodyWrapper.find(".q-dialog").exists()).toBe(false);
const row = wrapper.findAll(".status-cell").wrappers[1];
await row.trigger("click");
expect(bodyWrapper.find(".q-dialog").exists()).toBe(true);
expect(wrapper.vm.statusTask).toEqual(tasks[1]);
});
it("shows policy status modal on context menu item click", async () => {
expect(bodyWrapper.find(".q-dialog").exists()).toBe(false);
expect(bodyWrapper.find(".q-menu").exists()).toBe(false);
const row = wrapper.findAll(".q-table > tbody > .q-tr").wrappers[0];
await row.trigger("contextmenu");
await localVue.nextTick();
expect(bodyWrapper.find(".q-menu").exists()).toBe(true);
await bodyWrapper.find("#context-status").trigger("click");
expect(bodyWrapper.find(".q-dialog").exists()).toBe(true);
expect(wrapper.vm.statusTask).toEqual(tasks[0]);
});
it("deletes task", async () => {
expect(bodyWrapper.find(".q-dialog").exists()).toBe(false);
expect(bodyWrapper.find(".q-menu").exists()).toBe(false);
const row = wrapper.findAll(".q-table > tbody > .q-tr").wrappers[0];
await row.trigger("contextmenu");
await localVue.nextTick();
expect(bodyWrapper.find(".q-menu").exists()).toBe(true);
await bodyWrapper.find("#context-delete").trigger("click");
expect(bodyWrapper.find(".q-dialog").exists()).toBe(true);
//Get OK button on confirmation dialog and click it
await bodyWrapper.findAll(".q-btn").wrappers[1].trigger("click");
expect(rootActions.deleteAutoTask).toHaveBeenCalledWith(expect.anything(), 1);
expect(actions.loadPolicyAutomatedTasks).toHaveBeenCalled();
});
it("enables and disables task", async () => {
//Get first checkbox in first row
const row = wrapper.findAll(".q-checkbox").wrappers[0];
//Disable Task
await row.trigger("click");
expect(rootActions.editAutoTask).toHaveBeenCalledWith(
expect.anything(),
{ id: 1, enableordisable: false }
);
//Enable Task
await row.trigger("click");
expect(rootActions.editAutoTask).toHaveBeenCalledWith(
expect.anything(),
{ id: 1, enableordisable: true }
);
});
/* TODO: test @close and @hide events */
});

View File

@@ -1,143 +0,0 @@
import { mount, createLocalVue } from "@vue/test-utils";
import Vuex from "vuex";
import RelationsView from "@/components/automation/modals/RelationsView";
import "../../utils/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"
}
],
server_sites: [
{
id: 1,
client_name: "Test Name",
site: "Test Name"
}
],
workstation_sites: [
{
id: 2,
client_name: "Test Name",
site: "Test Name"
}
],
server_clients: [
{
id: 1,
client: "Test Name"
},
{
id: 2,
client: "Test Name2"
},
{
id: 3,
client: "Test Name3"
}
],
workstation_clients: [
{
id: 4,
client: "Test Name"
},
{
id: 5,
client: "Test Name2"
},
{
id: 6,
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.server_clients.length + related.workstation_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.server_sites.length + related.workstation_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);
});
});