address review comments
This commit is contained in:
parent
4bdea1ed61
commit
f9061ebb93
@ -16,7 +16,6 @@ class Incident extends BeanModel {
|
||||
|
||||
/**
|
||||
* Return an object that ready to parse to JSON for public
|
||||
* Only show necessary data to public
|
||||
* @returns {object} Object ready to parse
|
||||
*/
|
||||
toPublicJSON() {
|
||||
@ -29,16 +28,6 @@ class Incident extends BeanModel {
|
||||
active: !!this.active,
|
||||
createdDate: this.createdDate,
|
||||
lastUpdatedDate: this.lastUpdatedDate,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Return full object for admin use
|
||||
* @returns {object} Object ready to parse
|
||||
*/
|
||||
toJSON() {
|
||||
return {
|
||||
...this.toPublicJSON(),
|
||||
status_page_id: this.status_page_id,
|
||||
};
|
||||
}
|
||||
|
||||
@ -502,28 +502,50 @@ class StatusPage extends BeanModel {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get paginated incident history for a status page
|
||||
* Get paginated incident history for a status page using cursor-based pagination
|
||||
* @param {number} statusPageId ID of the status page
|
||||
* @param {number} page Page number (1-based)
|
||||
* @param {string|null} cursor ISO date string cursor (created_date of last item from previous page)
|
||||
* @param {boolean} isPublic Whether to return public or admin data
|
||||
* @returns {Promise<object>} Paginated incident data
|
||||
* @returns {Promise<object>} Paginated incident data with cursor
|
||||
*/
|
||||
static async getIncidentHistory(statusPageId, page, isPublic = true) {
|
||||
const offset = (page - 1) * INCIDENT_PAGE_SIZE;
|
||||
static async getIncidentHistory(statusPageId, cursor = null, isPublic = true) {
|
||||
let incidents;
|
||||
|
||||
const incidents = await R.find("incident", " status_page_id = ? ORDER BY created_date DESC LIMIT ? OFFSET ? ", [
|
||||
statusPageId,
|
||||
INCIDENT_PAGE_SIZE,
|
||||
offset,
|
||||
]);
|
||||
if (cursor) {
|
||||
incidents = await R.find(
|
||||
"incident",
|
||||
" status_page_id = ? AND created_date < ? ORDER BY created_date DESC LIMIT ? ",
|
||||
[statusPageId, cursor, INCIDENT_PAGE_SIZE]
|
||||
);
|
||||
} else {
|
||||
incidents = await R.find("incident", " status_page_id = ? ORDER BY created_date DESC LIMIT ? ", [
|
||||
statusPageId,
|
||||
INCIDENT_PAGE_SIZE,
|
||||
]);
|
||||
}
|
||||
|
||||
const total = await R.count("incident", " status_page_id = ? ", [statusPageId]);
|
||||
|
||||
const lastIncident = incidents[incidents.length - 1];
|
||||
let nextCursor = null;
|
||||
let hasMore = false;
|
||||
|
||||
if (lastIncident) {
|
||||
const moreCount = await R.count("incident", " status_page_id = ? AND created_date < ? ", [
|
||||
statusPageId,
|
||||
lastIncident.createdDate,
|
||||
]);
|
||||
hasMore = moreCount > 0;
|
||||
if (hasMore) {
|
||||
nextCursor = lastIncident.createdDate;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
incidents: incidents.map((i) => (isPublic ? i.toPublicJSON() : i.toJSON())),
|
||||
incidents: incidents.map((i) => i.toPublicJSON()),
|
||||
total,
|
||||
page,
|
||||
totalPages: Math.ceil(total / INCIDENT_PAGE_SIZE),
|
||||
nextCursor,
|
||||
hasMore,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -155,8 +155,8 @@ router.get("/api/status-page/:slug/incident-history", cache("5 minutes"), async
|
||||
return;
|
||||
}
|
||||
|
||||
const page = parseInt(request.query.page) || 1;
|
||||
const result = await StatusPage.getIncidentHistory(statusPageID, page, true);
|
||||
const cursor = request.query.cursor || null;
|
||||
const result = await StatusPage.getIncidentHistory(statusPageID, cursor, true);
|
||||
response.json({
|
||||
ok: true,
|
||||
...result,
|
||||
|
||||
@ -99,36 +99,15 @@ module.exports.statusPageSocketHandler = (socket) => {
|
||||
}
|
||||
});
|
||||
|
||||
socket.on("getIncidentHistory", async (slug, page, callback) => {
|
||||
try {
|
||||
checkLogin(socket);
|
||||
|
||||
let statusPageID = await StatusPage.slugToID(slug);
|
||||
if (!statusPageID) {
|
||||
throw new Error("slug is not found");
|
||||
}
|
||||
|
||||
const result = await StatusPage.getIncidentHistory(statusPageID, page, false);
|
||||
callback({
|
||||
ok: true,
|
||||
...result,
|
||||
});
|
||||
} catch (error) {
|
||||
callback({
|
||||
ok: false,
|
||||
msg: error.message,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
socket.on("getPublicIncidentHistory", async (slug, page, callback) => {
|
||||
socket.on("getIncidentHistory", async (slug, cursor, callback) => {
|
||||
try {
|
||||
let statusPageID = await StatusPage.slugToID(slug);
|
||||
if (!statusPageID) {
|
||||
throw new Error("slug is not found");
|
||||
}
|
||||
|
||||
const result = await StatusPage.getIncidentHistory(statusPageID, page, true);
|
||||
const isPublic = !socket.userID;
|
||||
const result = await StatusPage.getIncidentHistory(statusPageID, cursor, isPublic);
|
||||
callback({
|
||||
ok: true,
|
||||
...result,
|
||||
@ -193,7 +172,7 @@ module.exports.statusPageSocketHandler = (socket) => {
|
||||
ok: true,
|
||||
msg: "Saved.",
|
||||
msgi18n: true,
|
||||
incident: bean.toJSON(),
|
||||
incident: bean.toPublicJSON(),
|
||||
});
|
||||
} catch (error) {
|
||||
callback({
|
||||
@ -274,7 +253,7 @@ module.exports.statusPageSocketHandler = (socket) => {
|
||||
ok: true,
|
||||
msg: "Resolved",
|
||||
msgi18n: true,
|
||||
incident: bean.toJSON(),
|
||||
incident: bean.toPublicJSON(),
|
||||
});
|
||||
} catch (error) {
|
||||
callback({
|
||||
|
||||
@ -496,20 +496,12 @@
|
||||
</div>
|
||||
|
||||
<!-- Past Incidents -->
|
||||
<div class="past-incidents-section mb-4">
|
||||
<h2 class="past-incidents-title mb-3">{{ $t("Past Incidents") }}</h2>
|
||||
<div v-if="pastIncidentCount > 0" class="past-incidents-section mb-4">
|
||||
<h2 class="past-incidents-title mb-3">
|
||||
{{ $t("Past Incidents") }}
|
||||
</h2>
|
||||
|
||||
<div v-if="incidentHistoryLoading && incidentHistory.length === 0" class="text-center py-4">
|
||||
<div class="spinner-border text-primary" role="status">
|
||||
<span class="visually-hidden">{{ $t("Loading...") }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-else-if="incidentHistory.length === 0" class="text-center py-4 text-muted">
|
||||
{{ $t("No incidents recorded") }}
|
||||
</div>
|
||||
|
||||
<template v-else>
|
||||
<div class="past-incidents-content">
|
||||
<div
|
||||
v-for="(dateGroup, dateKey) in groupedIncidentHistory"
|
||||
:key="dateKey"
|
||||
@ -528,10 +520,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-if="incidentHistoryPage < incidentHistoryTotalPages"
|
||||
class="load-more-controls d-flex justify-content-center mt-3"
|
||||
>
|
||||
<div v-if="incidentHistoryHasMore" class="load-more-controls d-flex justify-content-center mt-3">
|
||||
<button
|
||||
class="btn btn-outline-secondary btn-sm"
|
||||
:disabled="incidentHistoryLoading"
|
||||
@ -545,7 +534,7 @@
|
||||
{{ $t("Load More") }}
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Incident Manage Modal -->
|
||||
@ -715,8 +704,8 @@ export default {
|
||||
loading: true,
|
||||
incidentHistory: [],
|
||||
incidentHistoryLoading: false,
|
||||
incidentHistoryPage: 1,
|
||||
incidentHistoryTotalPages: 1,
|
||||
incidentHistoryNextCursor: null,
|
||||
incidentHistoryHasMore: false,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
@ -878,12 +867,22 @@ export default {
|
||||
},
|
||||
|
||||
/**
|
||||
* Group incidents by date for display
|
||||
* Count of past incidents (non-active or unpinned)
|
||||
* @returns {number} Number of past incidents
|
||||
*/
|
||||
pastIncidentCount() {
|
||||
return this.incidentHistory.filter((i) => !(i.active && i.pin)).length;
|
||||
},
|
||||
|
||||
/**
|
||||
* Group past incidents (non-active or unpinned) by date for display
|
||||
* Active+pinned incidents are shown separately at the top, not in this section
|
||||
* @returns {object} Incidents grouped by date string
|
||||
*/
|
||||
groupedIncidentHistory() {
|
||||
const groups = {};
|
||||
for (const incident of this.incidentHistory) {
|
||||
const pastIncidents = this.incidentHistory.filter((i) => !(i.active && i.pin));
|
||||
for (const incident of pastIncidents) {
|
||||
const dateKey = this.formatDateKey(incident.createdDate);
|
||||
if (!groups[dateKey]) {
|
||||
groups[dateKey] = [];
|
||||
@ -998,7 +997,6 @@ export default {
|
||||
this.$root.publicGroupList = res.data.publicGroupList;
|
||||
|
||||
this.loading = false;
|
||||
this.loadIncidentHistory();
|
||||
|
||||
feedInterval = setInterval(
|
||||
() => {
|
||||
@ -1031,6 +1029,7 @@ export default {
|
||||
});
|
||||
|
||||
this.updateHeartbeatList();
|
||||
this.loadIncidentHistory();
|
||||
|
||||
// Go to edit page if ?edit present
|
||||
// null means ?edit present, but no value
|
||||
@ -1393,20 +1392,20 @@ export default {
|
||||
* @returns {void}
|
||||
*/
|
||||
loadIncidentHistory() {
|
||||
this.loadIncidentHistoryPage(1);
|
||||
this.loadIncidentHistoryWithCursor(null);
|
||||
},
|
||||
|
||||
/**
|
||||
* Load a specific page of incident history
|
||||
* @param {number} page - Page number to load
|
||||
* Load incident history using cursor-based pagination
|
||||
* @param {string|null} cursor - Cursor for pagination (created_date of last item)
|
||||
* @param {boolean} append - Whether to append to existing list
|
||||
* @returns {void}
|
||||
*/
|
||||
loadIncidentHistoryPage(page, append = false) {
|
||||
loadIncidentHistoryWithCursor(cursor, append = false) {
|
||||
this.incidentHistoryLoading = true;
|
||||
|
||||
if (this.enableEditMode) {
|
||||
this.$root.getSocket().emit("getIncidentHistory", this.slug, page, (res) => {
|
||||
this.$root.getSocket().emit("getIncidentHistory", this.slug, cursor, (res) => {
|
||||
this.incidentHistoryLoading = false;
|
||||
if (res.ok) {
|
||||
if (append) {
|
||||
@ -1414,16 +1413,19 @@ export default {
|
||||
} else {
|
||||
this.incidentHistory = res.incidents;
|
||||
}
|
||||
this.incidentHistoryPage = res.page;
|
||||
this.incidentHistoryTotalPages = res.totalPages;
|
||||
this.incidentHistoryNextCursor = res.nextCursor;
|
||||
this.incidentHistoryHasMore = res.hasMore;
|
||||
} else {
|
||||
console.error("Failed to load incident history:", res.msg);
|
||||
this.$root.toastError(res.msg);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
const url = cursor
|
||||
? `/api/status-page/${this.slug}/incident-history?cursor=${encodeURIComponent(cursor)}`
|
||||
: `/api/status-page/${this.slug}/incident-history`;
|
||||
axios
|
||||
.get(`/api/status-page/${this.slug}/incident-history?page=${page}`)
|
||||
.get(url)
|
||||
.then((res) => {
|
||||
this.incidentHistoryLoading = false;
|
||||
if (res.data.ok) {
|
||||
@ -1432,8 +1434,8 @@ export default {
|
||||
} else {
|
||||
this.incidentHistory = res.data.incidents;
|
||||
}
|
||||
this.incidentHistoryPage = res.data.page;
|
||||
this.incidentHistoryTotalPages = res.data.totalPages;
|
||||
this.incidentHistoryNextCursor = res.data.nextCursor;
|
||||
this.incidentHistoryHasMore = res.data.hasMore;
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
@ -1444,12 +1446,12 @@ export default {
|
||||
},
|
||||
|
||||
/**
|
||||
* Load more incident history (next page, appended)
|
||||
* Load more incident history using cursor-based pagination
|
||||
* @returns {void}
|
||||
*/
|
||||
loadMoreIncidentHistory() {
|
||||
if (this.incidentHistoryPage < this.incidentHistoryTotalPages) {
|
||||
this.loadIncidentHistoryPage(this.incidentHistoryPage + 1, true);
|
||||
if (this.incidentHistoryHasMore && this.incidentHistoryNextCursor) {
|
||||
this.loadIncidentHistoryWithCursor(this.incidentHistoryNextCursor, true);
|
||||
}
|
||||
},
|
||||
|
||||
@ -1601,12 +1603,14 @@ footer {
|
||||
|
||||
/* Reset button placed at top-left of the logo */
|
||||
.reset-top-left {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: -15px;
|
||||
z-index: 2;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
transition:
|
||||
transform $easing-in 0.18s,
|
||||
box-shadow $easing-in 0.18s,
|
||||
background-color $easing-in 0.18s;
|
||||
font-size: 18px;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
padding: 0;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
@ -1615,11 +1619,6 @@ footer {
|
||||
border: none;
|
||||
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.2);
|
||||
cursor: pointer;
|
||||
padding: 0;
|
||||
transition:
|
||||
transform $easing-in 0.18s,
|
||||
box-shadow $easing-in 0.18s,
|
||||
background-color $easing-in 0.18s;
|
||||
transform-origin: center;
|
||||
|
||||
&:hover {
|
||||
@ -1762,6 +1761,12 @@ footer {
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
.past-incidents-section {
|
||||
.past-incidents-content {
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.incident-date-group {
|
||||
.incident-date-header {
|
||||
font-size: 1rem;
|
||||
|
||||
@ -10,8 +10,8 @@
|
||||
*/
|
||||
var _a;
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.CONSOLE_STYLE_FgViolet = exports.CONSOLE_STYLE_FgLightBlue = exports.CONSOLE_STYLE_FgLightGreen = exports.CONSOLE_STYLE_FgOrange = exports.CONSOLE_STYLE_FgGray = exports.CONSOLE_STYLE_FgWhite = exports.CONSOLE_STYLE_FgCyan = exports.CONSOLE_STYLE_FgMagenta = exports.CONSOLE_STYLE_FgBlue = exports.CONSOLE_STYLE_FgYellow = exports.CONSOLE_STYLE_FgGreen = exports.CONSOLE_STYLE_FgRed = exports.CONSOLE_STYLE_FgBlack = exports.CONSOLE_STYLE_Hidden = exports.CONSOLE_STYLE_Reverse = exports.CONSOLE_STYLE_Blink = exports.CONSOLE_STYLE_Underscore = exports.CONSOLE_STYLE_Dim = exports.CONSOLE_STYLE_Bright = exports.CONSOLE_STYLE_Reset = exports.RESPONSE_BODY_LENGTH_MAX = exports.RESPONSE_BODY_LENGTH_DEFAULT = exports.PING_PER_REQUEST_TIMEOUT_DEFAULT = exports.PING_PER_REQUEST_TIMEOUT_MAX = exports.PING_PER_REQUEST_TIMEOUT_MIN = exports.PING_COUNT_DEFAULT = exports.PING_COUNT_MAX = exports.PING_COUNT_MIN = exports.PING_GLOBAL_TIMEOUT_DEFAULT = exports.PING_GLOBAL_TIMEOUT_MAX = exports.PING_GLOBAL_TIMEOUT_MIN = exports.PING_PACKET_SIZE_DEFAULT = exports.PING_PACKET_SIZE_MAX = exports.PING_PACKET_SIZE_MIN = exports.MIN_INTERVAL_SECOND = exports.MAX_INTERVAL_SECOND = exports.SQL_DATETIME_FORMAT_WITHOUT_SECOND = exports.SQL_DATETIME_FORMAT = exports.SQL_DATE_FORMAT = exports.STATUS_PAGE_MAINTENANCE = exports.STATUS_PAGE_PARTIAL_DOWN = exports.STATUS_PAGE_ALL_UP = exports.STATUS_PAGE_ALL_DOWN = exports.MAINTENANCE = exports.PENDING = exports.UP = exports.DOWN = exports.appName = exports.isNode = exports.isDev = void 0;
|
||||
exports.TYPES_WITH_DOMAIN_EXPIRY_SUPPORT_VIA_FIELD = exports.evaluateJsonQuery = exports.intHash = exports.localToUTC = exports.utcToLocal = exports.utcToISODateTime = exports.isoToUTCDateTime = exports.parseTimeFromTimeObject = exports.parseTimeObject = exports.getMonitorRelativeURL = exports.genSecret = exports.getCryptoRandomInt = exports.getRandomInt = exports.getRandomArbitrary = exports.TimeLogger = exports.polyfill = exports.log = exports.debug = exports.ucfirst = exports.sleep = exports.flipStatus = exports.badgeConstants = exports.CONSOLE_STYLE_BgGray = exports.CONSOLE_STYLE_BgWhite = exports.CONSOLE_STYLE_BgCyan = exports.CONSOLE_STYLE_BgMagenta = exports.CONSOLE_STYLE_BgBlue = exports.CONSOLE_STYLE_BgYellow = exports.CONSOLE_STYLE_BgGreen = exports.CONSOLE_STYLE_BgRed = exports.CONSOLE_STYLE_BgBlack = exports.CONSOLE_STYLE_FgPink = exports.CONSOLE_STYLE_FgBrown = void 0;
|
||||
exports.CONSOLE_STYLE_FgLightBlue = exports.CONSOLE_STYLE_FgLightGreen = exports.CONSOLE_STYLE_FgOrange = exports.CONSOLE_STYLE_FgGray = exports.CONSOLE_STYLE_FgWhite = exports.CONSOLE_STYLE_FgCyan = exports.CONSOLE_STYLE_FgMagenta = exports.CONSOLE_STYLE_FgBlue = exports.CONSOLE_STYLE_FgYellow = exports.CONSOLE_STYLE_FgGreen = exports.CONSOLE_STYLE_FgRed = exports.CONSOLE_STYLE_FgBlack = exports.CONSOLE_STYLE_Hidden = exports.CONSOLE_STYLE_Reverse = exports.CONSOLE_STYLE_Blink = exports.CONSOLE_STYLE_Underscore = exports.CONSOLE_STYLE_Dim = exports.CONSOLE_STYLE_Bright = exports.CONSOLE_STYLE_Reset = exports.RESPONSE_BODY_LENGTH_MAX = exports.RESPONSE_BODY_LENGTH_DEFAULT = exports.PING_PER_REQUEST_TIMEOUT_DEFAULT = exports.PING_PER_REQUEST_TIMEOUT_MAX = exports.PING_PER_REQUEST_TIMEOUT_MIN = exports.PING_COUNT_DEFAULT = exports.PING_COUNT_MAX = exports.PING_COUNT_MIN = exports.PING_GLOBAL_TIMEOUT_DEFAULT = exports.PING_GLOBAL_TIMEOUT_MAX = exports.PING_GLOBAL_TIMEOUT_MIN = exports.PING_PACKET_SIZE_DEFAULT = exports.PING_PACKET_SIZE_MAX = exports.PING_PACKET_SIZE_MIN = exports.INCIDENT_PAGE_SIZE = exports.MIN_INTERVAL_SECOND = exports.MAX_INTERVAL_SECOND = exports.SQL_DATETIME_FORMAT_WITHOUT_SECOND = exports.SQL_DATETIME_FORMAT = exports.SQL_DATE_FORMAT = exports.STATUS_PAGE_MAINTENANCE = exports.STATUS_PAGE_PARTIAL_DOWN = exports.STATUS_PAGE_ALL_UP = exports.STATUS_PAGE_ALL_DOWN = exports.MAINTENANCE = exports.PENDING = exports.UP = exports.DOWN = exports.appName = exports.isNode = exports.isDev = void 0;
|
||||
exports.TYPES_WITH_DOMAIN_EXPIRY_SUPPORT_VIA_FIELD = exports.evaluateJsonQuery = exports.intHash = exports.localToUTC = exports.utcToLocal = exports.utcToISODateTime = exports.isoToUTCDateTime = exports.parseTimeFromTimeObject = exports.parseTimeObject = exports.getMonitorRelativeURL = exports.genSecret = exports.getCryptoRandomInt = exports.getRandomInt = exports.getRandomArbitrary = exports.TimeLogger = exports.polyfill = exports.log = exports.debug = exports.ucfirst = exports.sleep = exports.flipStatus = exports.badgeConstants = exports.CONSOLE_STYLE_BgGray = exports.CONSOLE_STYLE_BgWhite = exports.CONSOLE_STYLE_BgCyan = exports.CONSOLE_STYLE_BgMagenta = exports.CONSOLE_STYLE_BgBlue = exports.CONSOLE_STYLE_BgYellow = exports.CONSOLE_STYLE_BgGreen = exports.CONSOLE_STYLE_BgRed = exports.CONSOLE_STYLE_BgBlack = exports.CONSOLE_STYLE_FgPink = exports.CONSOLE_STYLE_FgBrown = exports.CONSOLE_STYLE_FgViolet = void 0;
|
||||
const dayjs_1 = require("dayjs");
|
||||
const jsonata = require("jsonata");
|
||||
exports.isDev = process.env.NODE_ENV === "development";
|
||||
|
||||
138
test/e2e/specs/incident-history.spec.js
Normal file
138
test/e2e/specs/incident-history.spec.js
Normal file
@ -0,0 +1,138 @@
|
||||
import { expect, test } from "@playwright/test";
|
||||
import { login, restoreSqliteSnapshot, screenshot } from "../util-test";
|
||||
|
||||
test.describe("Incident History", () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await restoreSqliteSnapshot(page);
|
||||
});
|
||||
|
||||
test("past incidents section is hidden when no incidents exist", async ({ page }, testInfo) => {
|
||||
test.setTimeout(60000);
|
||||
|
||||
await page.goto("./add");
|
||||
await login(page);
|
||||
|
||||
await page.goto("./add-status-page");
|
||||
await page.getByTestId("name-input").fill("Empty Test");
|
||||
await page.getByTestId("slug-input").fill("empty-test");
|
||||
await page.getByTestId("submit-button").click();
|
||||
await page.waitForURL("/status/empty-test?edit");
|
||||
|
||||
await page.getByTestId("save-button").click();
|
||||
await expect(page.getByTestId("edit-sidebar")).toHaveCount(0);
|
||||
|
||||
const pastIncidentsSection = page.locator(".past-incidents-section");
|
||||
await expect(pastIncidentsSection).toHaveCount(0);
|
||||
|
||||
await screenshot(testInfo, page);
|
||||
});
|
||||
|
||||
test("active pinned incidents are shown at top and not in past incidents", async ({ page }, testInfo) => {
|
||||
test.setTimeout(60000);
|
||||
|
||||
await page.goto("./add");
|
||||
await login(page);
|
||||
|
||||
await page.goto("./add-status-page");
|
||||
await page.getByTestId("name-input").fill("Dedup Test");
|
||||
await page.getByTestId("slug-input").fill("dedup-test");
|
||||
await page.getByTestId("submit-button").click();
|
||||
await page.waitForURL("/status/dedup-test?edit");
|
||||
|
||||
await page.getByTestId("create-incident-button").click();
|
||||
await page.getByTestId("incident-title").fill("Active Incident");
|
||||
await page.getByTestId("incident-content-editable").fill("This is an active incident");
|
||||
await page.getByTestId("post-incident-button").click();
|
||||
|
||||
await page.getByTestId("save-button").click();
|
||||
await expect(page.getByTestId("edit-sidebar")).toHaveCount(0);
|
||||
|
||||
const activeIncident = page.getByTestId("incident").filter({ hasText: "Active Incident" });
|
||||
await expect(activeIncident).toBeVisible();
|
||||
|
||||
const pastIncidentsSection = page.locator(".past-incidents-section");
|
||||
await expect(pastIncidentsSection).toHaveCount(0);
|
||||
|
||||
await screenshot(testInfo, page);
|
||||
});
|
||||
|
||||
test("resolved incidents appear in past incidents section", async ({ page }, testInfo) => {
|
||||
test.setTimeout(90000);
|
||||
|
||||
await page.goto("./add");
|
||||
await login(page);
|
||||
|
||||
await page.goto("./add-status-page");
|
||||
await page.getByTestId("name-input").fill("Resolve Test");
|
||||
await page.getByTestId("slug-input").fill("resolve-test");
|
||||
await page.getByTestId("submit-button").click();
|
||||
await page.waitForURL("/status/resolve-test?edit");
|
||||
|
||||
await page.getByTestId("create-incident-button").click();
|
||||
await page.getByTestId("incident-title").fill("Resolved Incident");
|
||||
await page.getByTestId("incident-content-editable").fill("This incident will be resolved");
|
||||
await page.getByTestId("post-incident-button").click();
|
||||
|
||||
await page.waitForTimeout(500);
|
||||
|
||||
const resolveButton = page.locator("button", { hasText: "Resolve" }).first();
|
||||
await expect(resolveButton).toBeVisible();
|
||||
await resolveButton.click();
|
||||
|
||||
await page.waitForTimeout(1000);
|
||||
|
||||
const activeIncident = page.getByTestId("incident").filter({ hasText: "Resolved Incident" });
|
||||
await expect(activeIncident).toHaveCount(0);
|
||||
|
||||
const pastIncidentsSection = page.locator(".past-incidents-section");
|
||||
await expect(pastIncidentsSection).toBeVisible();
|
||||
|
||||
const resolvedIncidentInHistory = pastIncidentsSection.locator("text=Resolved Incident");
|
||||
await expect(resolvedIncidentInHistory).toBeVisible();
|
||||
|
||||
await screenshot(testInfo, page);
|
||||
});
|
||||
|
||||
test("incident history pagination loads more incidents", async ({ page }, testInfo) => {
|
||||
test.setTimeout(120000);
|
||||
|
||||
await page.goto("./add");
|
||||
await login(page);
|
||||
|
||||
await page.goto("./add-status-page");
|
||||
await page.getByTestId("name-input").fill("Pagination Test");
|
||||
await page.getByTestId("slug-input").fill("pagination-test");
|
||||
await page.getByTestId("submit-button").click();
|
||||
await page.waitForURL("/status/pagination-test?edit");
|
||||
|
||||
for (let i = 1; i <= 12; i++) {
|
||||
await page.getByTestId("create-incident-button").click();
|
||||
await page.getByTestId("incident-title").fill("Incident " + i);
|
||||
await page.getByTestId("incident-content-editable").fill("Content for incident " + i);
|
||||
await page.getByTestId("post-incident-button").click();
|
||||
await page.waitForTimeout(300);
|
||||
|
||||
const resolveButton = page.locator("button", { hasText: "Resolve" }).first();
|
||||
if (await resolveButton.isVisible()) {
|
||||
await resolveButton.click();
|
||||
await page.waitForTimeout(300);
|
||||
}
|
||||
}
|
||||
|
||||
await page.getByTestId("save-button").click();
|
||||
await expect(page.getByTestId("edit-sidebar")).toHaveCount(0);
|
||||
|
||||
await page.waitForTimeout(1000);
|
||||
|
||||
const pastIncidentsSection = page.locator(".past-incidents-section");
|
||||
await expect(pastIncidentsSection).toBeVisible();
|
||||
|
||||
const loadMoreButton = page.locator("button", { hasText: "Load More" });
|
||||
|
||||
if (await loadMoreButton.isVisible()) {
|
||||
await loadMoreButton.click();
|
||||
await page.waitForTimeout(1000);
|
||||
await screenshot(testInfo, page);
|
||||
}
|
||||
});
|
||||
});
|
||||
Loading…
Reference in New Issue
Block a user