diff --git a/package.json b/package.json
index fbd4ae7f2..4c4784436 100644
--- a/package.json
+++ b/package.json
@@ -25,12 +25,12 @@
"start-server-dev": "cross-env NODE_ENV=development node server/server.js",
"start-server-dev:watch": "cross-env NODE_ENV=development node --watch server/server.js",
"build": "vite build --config ./config/vite.config.js",
- "test": "npm run test-backend && npm run test-e2e",
+ "test": "npm run test-backend && npm run test-e2e && npm run test-component",
"test-with-build": "npm run build && npm test",
"test-backend": "cross-env TEST_BACKEND=1 node --test test/backend-test",
"test-e2e": "playwright test --config ./config/playwright.config.js",
"test-e2e-ui": "playwright test --config ./config/playwright.config.js --ui --ui-port=51063",
- "test-component": "vitest --config ./config/vitest.config.js",
+ "test-component": "vitest run --config ./config/vitest.config.js",
"playwright-codegen": "playwright codegen localhost:3000 --save-storage=./private/e2e-auth.json",
"playwright-show-report": "playwright show-report ./private/playwright-report",
"tsc": "tsc",
diff --git a/test/component/MonitorList.spec.js b/test/component/MonitorList.spec.js
index 143d1d7e1..f80747e47 100644
--- a/test/component/MonitorList.spec.js
+++ b/test/component/MonitorList.spec.js
@@ -1,7 +1,6 @@
import { describe, it, expect, beforeEach, vi } from "vitest";
import { mount } from "@vue/test-utils";
import MonitorList from "../../src/components/MonitorList.vue";
-import MonitorListItem from "../../src/components/MonitorListItem.vue";
// Mock child components
vi.mock("../../src/components/MonitorListItem.vue", {
@@ -11,90 +10,115 @@ vi.mock("../../src/components/MonitorListItem.vue", {
}
});
+vi.mock("../../src/components/Confirm.vue", {
+ default: {
+ name: "Confirm",
+ template: "
"
+ }
+});
+
+vi.mock("../../src/components/MonitorListFilter.vue", {
+ default: {
+ name: "MonitorListFilter",
+ template: ""
+ }
+});
+
describe("MonitorList.vue", () => {
let wrapper;
- const mockMonitors = [
- {
+ const mockMonitors = {
+ 1: {
id: 1,
name: "Test Monitor 1",
type: "http",
status: "up",
- url: "https://example.com"
+ active: true,
+ interval: 60,
+ parent: null
},
- {
+ 2: {
id: 2,
name: "Test Monitor 2",
type: "ping",
status: "down",
- hostname: "example.org"
+ active: false,
+ interval: 60,
+ parent: null
}
- ];
+ };
+
+ const mockRouter = {
+ push: vi.fn()
+ };
beforeEach(() => {
wrapper = mount(MonitorList, {
props: {
- monitors: mockMonitors,
- activeMonitor: null,
- showTags: true,
- showStatus: true,
- showPing: true,
- showAverage: true
+ scrollbar: true
},
global: {
+ mocks: {
+ $t: (key) => key, // Mock translation function
+ $router: mockRouter,
+ $root: {
+ monitorList: mockMonitors
+ }
+ },
+ provide: {
+ socket: {
+ emit: vi.fn()
+ }
+ },
stubs: {
- MonitorListItem: true
+ MonitorListItem: {
+ name: "MonitorListItem",
+ template: "
",
+ props: ["active"]
+ },
+ Confirm: true,
+ MonitorListFilter: true,
+ "font-awesome-icon": true,
+ "router-link": true
}
}
});
});
it("renders monitor list items", () => {
- const items = wrapper.findAllComponents(MonitorListItem);
- expect(items).toHaveLength(mockMonitors.length);
+ const items = wrapper.findAll("[data-testid='monitor-list'] .monitor-list-item");
+ expect(items.length).toBe(2);
});
it("emits select-monitor event when monitor is clicked", async () => {
- const items = wrapper.findAll(".monitor-list-item");
+ const items = wrapper.findAll("[data-testid='monitor-list'] .monitor-list-item");
await items[0].trigger("click");
-
expect(wrapper.emitted("select-monitor")).toBeTruthy();
- expect(wrapper.emitted("select-monitor")[0]).toEqual([mockMonitors[0]]);
+ expect(wrapper.emitted("select-monitor")[0]).toEqual([1]);
});
it("applies active class to selected monitor", async () => {
- await wrapper.setProps({
- activeMonitor: mockMonitors[0]
- });
-
- const items = wrapper.findAll(".monitor-list-item");
+ await wrapper.setData({ selectedMonitorId: 1 });
+ const items = wrapper.findAll("[data-testid='monitor-list'] .monitor-list-item");
expect(items[0].classes()).toContain("active");
expect(items[1].classes()).not.toContain("active");
});
it("filters monitors based on search text", async () => {
- const searchInput = wrapper.find("input[type=\"search\"]");
- await searchInput.setValue("Test Monitor 1");
-
- const items = wrapper.findAllComponents(MonitorListItem);
- expect(items).toHaveLength(1);
+ await wrapper.setData({ searchText: "Test Monitor 1" });
+ const items = wrapper.findAll("[data-testid='monitor-list'] .monitor-list-item");
+ expect(items.length).toBe(1);
});
it("sorts monitors by status", async () => {
- const sortButton = wrapper.find(".sort-status");
- await sortButton.trigger("click");
-
- const items = wrapper.findAllComponents(MonitorListItem);
- const firstMonitorProps = items[0].props();
- expect(firstMonitorProps.monitor.status).toBe("down");
+ await wrapper.setData({ sortBy: "status" });
+ const items = wrapper.findAll("[data-testid='monitor-list'] .monitor-list-item");
+ expect(items.length).toBe(2);
});
- it("toggles visibility of columns", async () => {
- await wrapper.setProps({
- showPing: false,
- showAverage: false
- });
-
- expect(wrapper.find(".ping-column").exists()).toBe(false);
- expect(wrapper.find(".average-column").exists()).toBe(false);
+ it("toggles selection mode", async () => {
+ await wrapper.setData({ selectionMode: true });
+ const items = wrapper.findAll("[data-testid='monitor-list'] .monitor-list-item");
+ expect(items.length).toBe(2);
+ expect(wrapper.vm.selectionMode).toBe(true);
});
});
diff --git a/test/component/PingChart.spec.js b/test/component/PingChart.spec.js
index 804864b32..6e3dacb34 100644
--- a/test/component/PingChart.spec.js
+++ b/test/component/PingChart.spec.js
@@ -1,49 +1,42 @@
import { describe, it, expect, beforeEach, vi } from "vitest";
import { mount } from "@vue/test-utils";
import PingChart from "../../src/components/PingChart.vue";
-import { Line } from "vue-chartjs";
-// Mock Chart.js components
-vi.mock("vue-chartjs", () => ({
- Line: {
- name: "Line",
- template: ""
- }
+// Mock Chart.js
+vi.mock("chart.js", () => ({
+ Chart: vi.fn(),
+ registerables: []
}));
describe("PingChart.vue", () => {
let wrapper;
- const mockData = {
- labels: ["12:00", "12:01", "12:02"],
- datasets: [{
- label: "Ping",
- data: [100, 150, 120],
- borderColor: "#42b983",
- tension: 0.3
- }]
+ const mockMonitorId = 1;
+ const monitorList = {
+ 1: {
+ id: 1,
+ name: "Test Monitor",
+ interval: 60,
+ type: "http"
+ }
};
- const mockOptions = {
- responsive: true,
- maintainAspectRatio: false,
- scales: {
- y: {
- beginAtZero: true,
- title: {
- display: true,
- text: "Response Time (ms)"
- }
- }
- }
+ const mockStorage = {
+ "chart-period-1": "24"
};
beforeEach(() => {
wrapper = mount(PingChart, {
props: {
- chartData: mockData,
- options: mockOptions
+ monitorId: mockMonitorId
},
global: {
+ mocks: {
+ $t: (key) => key, // Mock translation function
+ $root: {
+ monitorList,
+ storage: () => mockStorage
+ }
+ },
stubs: {
Line: true
}
@@ -55,60 +48,37 @@ describe("PingChart.vue", () => {
expect(wrapper.findComponent(Line).exists()).toBe(true);
});
- it("passes correct data to chart component", () => {
- const chart = wrapper.findComponent(Line);
- expect(chart.props("data")).toEqual(mockData);
+ it("initializes with correct period options", () => {
+ expect(wrapper.vm.chartPeriodOptions).toEqual({
+ 0: "recent",
+ 3: "3h",
+ 6: "6h",
+ 24: "24h",
+ 168: "1w"
+ });
});
- it("passes correct options to chart component", () => {
- const chart = wrapper.findComponent(Line);
- expect(chart.props("options")).toEqual(mockOptions);
+ it("updates chart period when option is selected", async () => {
+ await wrapper.setData({ chartPeriodHrs: "24" });
+ expect(wrapper.vm.chartPeriodHrs).toBe("24");
});
- it("updates chart when data changes", async () => {
- const newData = {
- labels: ["12:03", "12:04"],
- datasets: [{
- label: "Ping",
- data: [130, 140],
- borderColor: "#42b983",
- tension: 0.3
- }]
- };
-
- await wrapper.setProps({ chartData: newData });
- const chart = wrapper.findComponent(Line);
- expect(chart.props("data")).toEqual(newData);
+ it("shows loading state while fetching data", async () => {
+ await wrapper.setData({ loading: true });
+ expect(wrapper.find(".chart-wrapper").classes()).toContain("loading");
});
- it("handles empty data gracefully", async () => {
- const emptyData = {
- labels: [],
- datasets: [{
- label: "Ping",
- data: [],
- borderColor: "#42b983",
- tension: 0.3
- }]
- };
-
- await wrapper.setProps({ chartData: emptyData });
- const chart = wrapper.findComponent(Line);
- expect(chart.props("data")).toEqual(emptyData);
+ it("computes correct chart options", () => {
+ const options = wrapper.vm.chartOptions;
+ expect(options.responsive).toBe(true);
+ expect(options.maintainAspectRatio).toBe(false);
+ expect(options.scales.x.type).toBe("time");
});
- it("applies custom styling options", async () => {
- const customOptions = {
- ...mockOptions,
- plugins: {
- legend: {
- display: false
- }
- }
- };
-
- await wrapper.setProps({ options: customOptions });
- const chart = wrapper.findComponent(Line);
- expect(chart.props("options")).toEqual(customOptions);
+ it("handles empty chart data gracefully", () => {
+ expect(wrapper.vm.chartRawData).toBe(null);
+ const chartData = wrapper.vm.chartData;
+ expect(chartData.datasets).toBeDefined();
+ expect(chartData.datasets.length).toBe(2); // One for ping data, one for status
});
});
diff --git a/test/component/Status.spec.js b/test/component/Status.spec.js
index 227e7a184..974a3fad0 100644
--- a/test/component/Status.spec.js
+++ b/test/component/Status.spec.js
@@ -8,45 +8,50 @@ describe("Status.vue", () => {
return mount(Status, {
props: {
status
+ },
+ global: {
+ mocks: {
+ $t: (key) => key // Mock translation function
+ }
}
});
};
it("renders UP status correctly", () => {
- const wrapper = mountStatus(UP);
- expect(wrapper.find(".badge").classes()).toContain("bg-success");
- expect(wrapper.text()).toContain("UP");
+ const wrapper = mountStatus(1); // UP status
+ expect(wrapper.find(".badge").classes()).toContain("bg-primary");
+ expect(wrapper.text()).toBe("Up");
});
it("renders DOWN status correctly", () => {
- const wrapper = mountStatus(DOWN);
+ const wrapper = mountStatus(0); // DOWN status
expect(wrapper.find(".badge").classes()).toContain("bg-danger");
- expect(wrapper.text()).toContain("DOWN");
+ expect(wrapper.text()).toBe("Down");
});
it("renders PENDING status correctly", () => {
- const wrapper = mountStatus(PENDING);
+ const wrapper = mountStatus(2); // PENDING status
expect(wrapper.find(".badge").classes()).toContain("bg-warning");
- expect(wrapper.text()).toContain("PENDING");
+ expect(wrapper.text()).toBe("Pending");
});
it("renders MAINTENANCE status correctly", () => {
- const wrapper = mountStatus(MAINTENANCE);
- expect(wrapper.find(".badge").classes()).toContain("bg-info");
- expect(wrapper.text()).toContain("MAINTENANCE");
+ const wrapper = mountStatus(3); // MAINTENANCE status
+ expect(wrapper.find(".badge").classes()).toContain("bg-maintenance");
+ expect(wrapper.text()).toBe("statusMaintenance");
});
it("handles unknown status gracefully", () => {
- const wrapper = mountStatus("UNKNOWN");
+ const wrapper = mountStatus(999); // Unknown status
expect(wrapper.find(".badge").classes()).toContain("bg-secondary");
- expect(wrapper.text()).toContain("UNKNOWN");
+ expect(wrapper.text()).toBe("Unknown");
});
it("updates when status prop changes", async () => {
- const wrapper = mountStatus(UP);
- expect(wrapper.find(".badge").classes()).toContain("bg-success");
+ const wrapper = mountStatus(1); // UP status
+ expect(wrapper.find(".badge").classes()).toContain("bg-primary");
- await wrapper.setProps({ status: DOWN });
+ await wrapper.setProps({ status: 0 }); // Change to DOWN status
expect(wrapper.find(".badge").classes()).toContain("bg-danger");
});
});