mirror of
https://github.com/louislam/uptime-kuma.git
synced 2026-11-10 13:14:24 +08:00
fix: regression - incorrect uptime badge color in status pages (#8141)
This commit is contained in:
1 parent
c64c6d950d
commit
b376409720
4 files changed
+84
-2
No files matched your search
@@ -72,3 +72,25 @@ export function applyLatestBeats(
|
||||
}
|
||||
heartbeatList[monitorID] = beats;
|
||||
}
|
||||
|
||||
/**
|
||||
* Store the newest beat of each monitor from a REST heartbeat payload
|
||||
* @param state Mixin state
|
||||
* @param heartbeatList Beats per monitor, oldest first
|
||||
* @param translate Translation function
|
||||
* @returns void
|
||||
*/
|
||||
export function applyHeartbeatSnapshot(
|
||||
state: BeatState,
|
||||
heartbeatList: Record<number | string, unknown[]>,
|
||||
translate: Translate
|
||||
): void {
|
||||
for (const monitorID in heartbeatList) {
|
||||
const beats = heartbeatList[monitorID];
|
||||
if (!beats || beats.length === 0) {
|
||||
continue;
|
||||
}
|
||||
const beat = beats[beats.length - 1];
|
||||
applyBeat(state, Number(monitorID), beat, beatStatusEntry(translate, beat));
|
||||
}
|
||||
}
|
||||
@@ -635,6 +635,7 @@ import {
|
||||
} from "../util.ts";
|
||||
import Tag from "../components/Tag.vue";
|
||||
import VueMultiselect from "vue-multiselect";
|
||||
import { applyHeartbeatSnapshot } from "../beat-state";
|
||||
|
||||
const toast = useToast();
|
||||
dayjs.extend(duration);
|
||||
@@ -1075,6 +1076,7 @@ export default {
|
||||
|
||||
this.$root.heartbeatList = heartbeatList;
|
||||
this.$root.uptimeList = uptimeList;
|
||||
applyHeartbeatSnapshot(this.$root, heartbeatList, this.$t.bind(this));
|
||||
|
||||
const heartbeatIds = Object.keys(heartbeatList);
|
||||
const downMonitors = heartbeatIds.reduce((downMonitorsAmount, currentId) => {
|
||||
|
||||
@@ -84,6 +84,22 @@ test.describe("Public status page", () => {
|
||||
expect(await badge.text()).toContain("<svg");
|
||||
});
|
||||
|
||||
test("shows the uptime badge in green for an up monitor", async ({ page }) => {
|
||||
const pushUrl = await addPushMonitor(page, "Uptime Badge Monitor");
|
||||
|
||||
// One up beat gives a 100% uptime badge; push it before the status page is fetched
|
||||
await page.request.get(pushUrl);
|
||||
|
||||
await addStatusPageWithMonitor(page, "Uptime Badge Demo", "uptime-badge-demo", "Uptime Badge Monitor");
|
||||
|
||||
await page.context().clearCookies();
|
||||
await page.goto("./status/uptime-badge-demo");
|
||||
|
||||
const badge = page.getByTestId("monitor").locator(".badge");
|
||||
await expect(badge).toHaveText(/%$/);
|
||||
await expect(badge).toHaveClass(/bg-primary/);
|
||||
});
|
||||
|
||||
test("shows an active maintenance window to logged-out visitors", async ({ page }) => {
|
||||
const pushUrl = await addPushMonitor(page, "Maintenance Monitor");
|
||||
await addStatusPageWithMonitor(page, "Maintenance Demo", "maintenance-demo", "Maintenance Monitor");
|
||||
@@ -107,7 +123,7 @@ test.describe("Public status page", () => {
|
||||
await page.context().clearCookies();
|
||||
await page.goto("./status/maintenance-demo");
|
||||
|
||||
await expect(page.locator(".bg-maintenance")).toContainText("Planned Window");
|
||||
await expect(page.locator(".alert.bg-maintenance")).toContainText("Planned Window");
|
||||
await expect(page.getByTestId("monitor-name")).toContainText("Maintenance Monitor");
|
||||
});
|
||||
});
|
||||
@@ -1,6 +1,6 @@
|
||||
import { describe, test } from "node:test";
|
||||
import assert from "node:assert";
|
||||
import { applyBeat, applyLatestBeats, beatStatusEntry } from "../../src/beat-state";
|
||||
import { applyBeat, applyHeartbeatSnapshot, applyLatestBeats, beatStatusEntry } from "../../src/beat-state";
|
||||
import { DOWN, MAINTENANCE, PENDING, UP } from "../../src/util";
|
||||
|
||||
/**
|
||||
@@ -78,6 +78,48 @@ describe("applyLatestBeats()", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("applyHeartbeatSnapshot()", () => {
|
||||
/**
|
||||
* Build empty mixin state
|
||||
* @returns {object} State
|
||||
*/
|
||||
function emptyState() {
|
||||
return { lastHeartbeatList: {}, statusList: {}, lastBeatRaw: {} };
|
||||
}
|
||||
|
||||
test("fills the badge state from a REST payload with string monitor ids", () => {
|
||||
const state = emptyState();
|
||||
const beats = [{ status: UP }];
|
||||
|
||||
applyHeartbeatSnapshot(state, { 7: beats }, translate);
|
||||
|
||||
assert.strictEqual(state.lastHeartbeatList[7], beats[0]);
|
||||
assert.deepStrictEqual(state.statusList[7], { text: "Up", color: "primary" });
|
||||
});
|
||||
|
||||
test("uses the newest beat of every monitor", () => {
|
||||
const state = emptyState();
|
||||
const heartbeatList = {
|
||||
1: [{ status: DOWN }, { status: UP }],
|
||||
2: [{ status: DOWN }],
|
||||
};
|
||||
|
||||
applyHeartbeatSnapshot(state, heartbeatList, translate);
|
||||
|
||||
assert.deepStrictEqual(state.statusList[1], { text: "Up", color: "primary" });
|
||||
assert.deepStrictEqual(state.statusList[2], { text: "Down", color: "danger" });
|
||||
});
|
||||
|
||||
test("leaves monitors without beats untouched", () => {
|
||||
const state = { lastHeartbeatList: { 1: { status: UP } }, statusList: {}, lastBeatRaw: {} };
|
||||
|
||||
applyHeartbeatSnapshot(state, { 2: [] }, translate);
|
||||
|
||||
assert.strictEqual(Object.keys(state.lastHeartbeatList).length, 1);
|
||||
assert.deepStrictEqual(state.statusList, {});
|
||||
});
|
||||
});
|
||||
|
||||
describe("applyBeat()", () => {
|
||||
test("updates the three maps in place", () => {
|
||||
const state = { lastHeartbeatList: {}, statusList: {}, lastBeatRaw: {} };
|
||||
|
||||
Reference in new issue
Block a user