From ecf14e763333c5cd14cb12f03f10abc2e01afac7 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C5=81ukasz=20Mierzwa?=
Date: Tue, 15 Aug 2017 19:43:49 -0700
Subject: [PATCH 1/7] Add a test for unsilence button
---
assets/static/__mocks__/ajaxSuccessMock.js | 12 +++++++++
assets/static/unsilence.test.js | 30 ++++++++++++++++++++--
2 files changed, 40 insertions(+), 2 deletions(-)
create mode 100644 assets/static/__mocks__/ajaxSuccessMock.js
diff --git a/assets/static/__mocks__/ajaxSuccessMock.js b/assets/static/__mocks__/ajaxSuccessMock.js
new file mode 100644
index 000000000..0155f863d
--- /dev/null
+++ b/assets/static/__mocks__/ajaxSuccessMock.js
@@ -0,0 +1,12 @@
+const mockXHR = require("mock-xhr");
+
+function ajaxSuccessMockServer() {
+ var server = new mockXHR.server();
+ server.handle = function (request) {
+ request.setResponseHeader("Content-Type", "application/json");
+ request.receive(200, JSON.stringify({"status":"success"}));
+ };
+ return server;
+}
+
+exports.ajaxSuccessMockServer = ajaxSuccessMockServer;
diff --git a/assets/static/unsilence.test.js b/assets/static/unsilence.test.js
index 1e94b8d4d..5e511d2b5 100644
--- a/assets/static/unsilence.test.js
+++ b/assets/static/unsilence.test.js
@@ -1,5 +1,31 @@
-test("silence setupSilenceForm()", () => {
- window.jQuery = require("jquery");
+const $ = window.jQuery = require("jquery");
+const templatesMock = require("./__mocks__/templatesMock");
+
+test("unsilence init()", () => {
+ var body = templatesMock.loadTemplates();
+ body.push(
+ ""
+ );
+ document.body.innerHTML = body;
+
+ require("bootstrap/js/tooltip.js");
const unsilence = require("./unsilence");
+
+ const ajaxMock = require("./__mocks__/ajaxSuccessMock").ajaxSuccessMockServer();
+ ajaxMock.start();
+
unsilence.init();
+ // icon should be trash-o before clicking
+ expect($("button > span.fa").hasClass("fa-trash-o")).toBe(true);
+ $("button.silence-delete").click();
+ // and switch to green check mark in circle after
+ expect($("button > span.fa").hasClass("fa-trash-o")).toBe(false);
+ expect($("button > span.fa").hasClass("fa-check-circle")).toBe(true);
+ expect($("button > span.fa").hasClass("text-success")).toBe(true);
+
+ ajaxMock.stop();
});
From 77ec8d9a8b2db9f30231ca9010ef592de12d98d5 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C5=81ukasz=20Mierzwa?=
Date: Tue, 15 Aug 2017 19:45:00 -0700
Subject: [PATCH 2/7] Use correct icon
Tests are failing because we're not cleaning icons properly
---
assets/static/unsilence.js | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/assets/static/unsilence.js b/assets/static/unsilence.js
index f11d6d671..6eb7a9212 100644
--- a/assets/static/unsilence.js
+++ b/assets/static/unsilence.js
@@ -18,7 +18,7 @@ function markInProgress(alertmanagerURI, silenceID) {
var elem = unsilenceButtonByID(alertmanagerURI, silenceID);
elem.attr("title", "Silence is being deleted from Alertmanager");
elem.tooltip("fixTitle");
- elem.find(".fa").removeClass("fa-times").addClass("fa-refresh fa-spin");
+ elem.find(".fa").removeClass("fa-trash-o").addClass("fa-refresh fa-spin");
}
function markFailed(alertmanagerURI, silenceID, xhr) {
@@ -26,12 +26,12 @@ function markFailed(alertmanagerURI, silenceID, xhr) {
var elem = unsilenceButtonByID(alertmanagerURI, silenceID);
elem.attr("title", err);
elem.tooltip("fixTitle");
- elem.find(".fa").removeClass("fa-times fa-refresh fa-spin").addClass("fa-exclamation-circle text-danger");
+ elem.find(".fa").removeClass("fa-trash-o fa-refresh fa-spin").addClass("fa-exclamation-circle text-danger");
// Disable button, wait 5s and reset button to the original state
elem.data("disabled", "true");
setTimeout(function() {
- elem.find(".fa").removeClass("fa-exclamation-circle text-danger").addClass("fa-times");
+ elem.find(".fa").removeClass("fa-exclamation-circle text-danger").addClass("fa-trash-o");
elem.removeData("disabled");
elem.attr("title", "Delete this silence");
elem.tooltip("fixTitle");
@@ -42,7 +42,7 @@ function markSuccess(alertmanagerURI, silenceID) {
var elem = unsilenceButtonByID(alertmanagerURI, silenceID);
elem.attr("title", "Silence deleted from Alertmanager");
elem.tooltip("fixTitle");
- elem.find(".fa").removeClass("fa-times fa-refresh fa-spin").addClass("fa-check-circle text-success");
+ elem.find(".fa").removeClass("fa-trash-o fa-refresh fa-spin").addClass("fa-check-circle text-success");
// disable button so it's no longer clickable
elem.data("disabled", "true");
}
From 431540c05ee782fd5790ffea91c5874584256ae8 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C5=81ukasz=20Mierzwa?=
Date: Tue, 15 Aug 2017 19:50:58 -0700
Subject: [PATCH 3/7] Add a test case for error handling when unsilence request
fails
---
assets/static/__mocks__/ajaxErrorMock.js | 16 +++++++++
assets/static/unsilence.test.js | 46 +++++++++++++++++++-----
2 files changed, 54 insertions(+), 8 deletions(-)
create mode 100644 assets/static/__mocks__/ajaxErrorMock.js
diff --git a/assets/static/__mocks__/ajaxErrorMock.js b/assets/static/__mocks__/ajaxErrorMock.js
new file mode 100644
index 000000000..5632939f2
--- /dev/null
+++ b/assets/static/__mocks__/ajaxErrorMock.js
@@ -0,0 +1,16 @@
+const mockXHR = require("mock-xhr");
+
+function ajaxErrorMockServer() {
+ var server = new mockXHR.server();
+ server.handle = function (request) {
+ request.setResponseHeader("Content-Type", "application/json");
+ request.receive(500, JSON.stringify({
+ "status": "error",
+ "errorType": "server_error",
+ "error": "end time must not be modified for elapsed silence"
+ }));
+ };
+ return server;
+}
+
+exports.ajaxErrorMockServer = ajaxErrorMockServer;
diff --git a/assets/static/unsilence.test.js b/assets/static/unsilence.test.js
index 5e511d2b5..792d27978 100644
--- a/assets/static/unsilence.test.js
+++ b/assets/static/unsilence.test.js
@@ -1,15 +1,18 @@
const $ = window.jQuery = require("jquery");
const templatesMock = require("./__mocks__/templatesMock");
-test("unsilence init()", () => {
+const unsilenceButtonHTML =
+ "";
+
+jest.useFakeTimers();
+
+test("unsilence button icons after success", () => {
var body = templatesMock.loadTemplates();
- body.push(
- ""
- );
+ body.push(unsilenceButtonHTML);
document.body.innerHTML = body;
require("bootstrap/js/tooltip.js");
@@ -29,3 +32,30 @@ test("unsilence init()", () => {
ajaxMock.stop();
});
+
+test("unsilence button icons after failed delete", () => {
+ var body = templatesMock.loadTemplates();
+ body.push(unsilenceButtonHTML);
+ document.body.innerHTML = body;
+
+ require("bootstrap/js/tooltip.js");
+ const unsilence = require("./unsilence");
+
+ const ajaxMock = require("./__mocks__/ajaxErrorMock").ajaxErrorMockServer();
+ ajaxMock.start();
+
+ unsilence.init();
+ // icon should be trash-o before clicking
+ expect($("button > span.fa").hasClass("fa-trash-o")).toBe(true);
+ $("button.silence-delete").click();
+ // and switch to green check mark in circle after
+ expect($("button > span.fa").hasClass("fa-trash-o")).toBe(false);
+ expect($("button > span.fa").hasClass("fa-exclamation-circle")).toBe(true);
+ expect($("button > span.fa").hasClass("text-danger")).toBe(true);
+
+ // run timers, it should reset the icon back to trash-o
+ jest.runOnlyPendingTimers();
+ expect($("button > span.fa").hasClass("fa-trash-o")).toBe(true);
+
+ ajaxMock.stop();
+});
From 866797cc4209ec3b5143316d37d5bc53bf0c21e8 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C5=81ukasz=20Mierzwa?=
Date: Tue, 15 Aug 2017 20:20:35 -0700
Subject: [PATCH 4/7] Add more silence.js tests
---
assets/static/__mocks__/ajaxMock.js | 12 ++++
.../static/__snapshots__/silence.test.js.snap | 15 +++++
assets/static/silence.js | 1 +
assets/static/silence.test.js | 63 ++++++++++++++++++-
4 files changed, 90 insertions(+), 1 deletion(-)
create mode 100644 assets/static/__mocks__/ajaxMock.js
create mode 100644 assets/static/__snapshots__/silence.test.js.snap
diff --git a/assets/static/__mocks__/ajaxMock.js b/assets/static/__mocks__/ajaxMock.js
new file mode 100644
index 000000000..c561cbae6
--- /dev/null
+++ b/assets/static/__mocks__/ajaxMock.js
@@ -0,0 +1,12 @@
+const mockXHR = require("mock-xhr");
+
+function createServer(status, payload) {
+ var server = new mockXHR.server();
+ server.handle = function (request) {
+ request.setResponseHeader("Content-Type", "application/json");
+ request.receive(status, JSON.stringify(payload));
+ };
+ return server;
+}
+
+exports.createServer = createServer;
diff --git a/assets/static/__snapshots__/silence.test.js.snap b/assets/static/__snapshots__/silence.test.js.snap
new file mode 100644
index 000000000..5a486013a
--- /dev/null
+++ b/assets/static/__snapshots__/silence.test.js.snap
@@ -0,0 +1,15 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`failed sendSilencePOST() 1`] = `
+"
+
+ request failed
+
"
+`;
+
+exports[`successful sendSilencePOST() 1`] = `
+"
+
+ abcdef
+
"
+`;
diff --git a/assets/static/silence.js b/assets/static/silence.js
index 73ccb8366..d4216758e 100644
--- a/assets/static/silence.js
+++ b/assets/static/silence.js
@@ -328,3 +328,4 @@ function setupSilenceForm() {
}
exports.setupSilenceForm = setupSilenceForm;
+exports.sendSilencePOST = sendSilencePOST;
diff --git a/assets/static/silence.test.js b/assets/static/silence.test.js
index 2ce6d91dd..52ca7a0aa 100644
--- a/assets/static/silence.test.js
+++ b/assets/static/silence.test.js
@@ -1,5 +1,66 @@
+const $ = window.jQuery = require("jquery");
+const templatesMock = require("./__mocks__/templatesMock");
+const ajaxMock = require("./__mocks__/ajaxMock");
+
test("silence setupSilenceForm()", () => {
- window.jQuery = require("jquery");
+ var body = templatesMock.loadTemplates();
+ document.body.innerHTML = body;
+
const silence = require("./silence");
silence.setupSilenceForm();
});
+
+test("successful sendSilencePOST()", () => {
+ var body = templatesMock.loadTemplates();
+ body.push(
+ "" +
+ ""
+ );
+ document.body.innerHTML = body;
+
+ const templates = require("./templates");
+ templates.init();
+
+ const ajaxServer = ajaxMock.createServer(200, {
+ "status": "success",
+ "data": {"silenceId": "abcdef"}
+ });
+ ajaxServer.start();
+
+ const silence = require("./silence");
+ silence.sendSilencePOST("http://localhost", {});
+
+ let resultElem = $(".silence-post-result").html().trim();
+ expect(resultElem).toMatchSnapshot();
+
+ ajaxServer.stop();
+});
+
+test("failed sendSilencePOST()", () => {
+ var body = templatesMock.loadTemplates();
+ body.push(
+ "" +
+ ""
+ );
+ document.body.innerHTML = body;
+
+ const templates = require("./templates");
+ templates.init();
+
+ const ajaxServer = ajaxMock.createServer(500, {
+ "status": "success",
+ "errorType": "server_error",
+ "error": "request failed"
+ });
+ ajaxServer.start();
+
+ const silence = require("./silence");
+ silence.sendSilencePOST("http://localhost", {});
+
+ let resultElem = $(".silence-post-result").html().trim();
+ expect(resultElem).toMatchSnapshot();
+
+ ajaxServer.stop();
+});
From ac8b3391d77ae7bab106156575027e16449cc0d8 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C5=81ukasz=20Mierzwa?=
Date: Tue, 15 Aug 2017 20:22:57 -0700
Subject: [PATCH 5/7] Re-use ajax mocks instead of creating one per response
---
assets/static/__mocks__/ajaxErrorMock.js | 16 ----------------
assets/static/__mocks__/ajaxSuccessMock.js | 12 ------------
assets/static/unsilence.test.js | 17 +++++++++++------
3 files changed, 11 insertions(+), 34 deletions(-)
delete mode 100644 assets/static/__mocks__/ajaxErrorMock.js
delete mode 100644 assets/static/__mocks__/ajaxSuccessMock.js
diff --git a/assets/static/__mocks__/ajaxErrorMock.js b/assets/static/__mocks__/ajaxErrorMock.js
deleted file mode 100644
index 5632939f2..000000000
--- a/assets/static/__mocks__/ajaxErrorMock.js
+++ /dev/null
@@ -1,16 +0,0 @@
-const mockXHR = require("mock-xhr");
-
-function ajaxErrorMockServer() {
- var server = new mockXHR.server();
- server.handle = function (request) {
- request.setResponseHeader("Content-Type", "application/json");
- request.receive(500, JSON.stringify({
- "status": "error",
- "errorType": "server_error",
- "error": "end time must not be modified for elapsed silence"
- }));
- };
- return server;
-}
-
-exports.ajaxErrorMockServer = ajaxErrorMockServer;
diff --git a/assets/static/__mocks__/ajaxSuccessMock.js b/assets/static/__mocks__/ajaxSuccessMock.js
deleted file mode 100644
index 0155f863d..000000000
--- a/assets/static/__mocks__/ajaxSuccessMock.js
+++ /dev/null
@@ -1,12 +0,0 @@
-const mockXHR = require("mock-xhr");
-
-function ajaxSuccessMockServer() {
- var server = new mockXHR.server();
- server.handle = function (request) {
- request.setResponseHeader("Content-Type", "application/json");
- request.receive(200, JSON.stringify({"status":"success"}));
- };
- return server;
-}
-
-exports.ajaxSuccessMockServer = ajaxSuccessMockServer;
diff --git a/assets/static/unsilence.test.js b/assets/static/unsilence.test.js
index 792d27978..7d2bf3027 100644
--- a/assets/static/unsilence.test.js
+++ b/assets/static/unsilence.test.js
@@ -1,5 +1,6 @@
const $ = window.jQuery = require("jquery");
const templatesMock = require("./__mocks__/templatesMock");
+const ajaxMock = require("./__mocks__/ajaxMock");
const unsilenceButtonHTML =
"
"
`;
+exports[`silence form 1`] = `
+"
+
+
+
+
+ New silence form rendering failed.
+
+
+ request failed
+
+
"
+`;
+
+exports[`silence form 2`] = `
+""
+`;
+
+exports[`silence form 3`] = `
+"
+
+
+
+ |
+
+ mock
+
+ |
+
+
+
+ abcdef
+
+ |
+
+
+
+
"
+`;
+
exports[`successful sendSilencePOST() 1`] = `
"
diff --git a/assets/static/silence.test.js b/assets/static/silence.test.js
index 52ca7a0aa..e653f6473 100644
--- a/assets/static/silence.test.js
+++ b/assets/static/silence.test.js
@@ -1,13 +1,120 @@
const $ = window.jQuery = require("jquery");
+const moment = require("moment");
const templatesMock = require("./__mocks__/templatesMock");
const ajaxMock = require("./__mocks__/ajaxMock");
-test("silence setupSilenceForm()", () => {
- var body = templatesMock.loadTemplates();
+jest.useFakeTimers();
+
+test("silence form", () => {
+ let body = templatesMock.loadTemplates();
+ body.push(
+ "
" +
+ "" +
+ ""
+ );
document.body.innerHTML = body;
+ const templates = require("./templates");
+ templates.init();
+
+ const config = require("./config");
+ config.init({
+ CopySelector: "#copy-settings-with-filter",
+ SaveSelector: "#save-default-filter",
+ ResetSelector: "#reset-settings"
+ });
+
const silence = require("./silence");
silence.setupSilenceForm();
+
+ require("bootstrap/js/tooltip.js");
+ require("bootstrap/js/modal.js");
+
+ // rendering silence form requires AJAX call to pull data
+ // first check failed request
+ let ajaxServer = ajaxMock.createServer(500, {
+ "status": "error",
+ "errorType": "server_error",
+ "error": "request failed"
+ });
+ ajaxServer.start();
+ // click on the button, modal should show and render via ajax call
+ $("#silenceButton").click();
+ jest.runOnlyPendingTimers();
+ let silenceModal = $("#silenceModal").html().trim();
+ expect(silenceModal).toMatchSnapshot();
+ ajaxServer.stop();
+
+ // hide the form
+ $("#silenceModal").modal("hide");
+
+ // next try successful request
+ ajaxServer = ajaxMock.createServer(200, {
+ "groups": [ {
+ "receiver": "default",
+ "labels": {"alertname": "fakeAlert"},
+ "alerts": [ {
+ "annotations": {},
+ "labels": {
+ "alertname": "fakeAlert",
+ "cluster": "prod",
+ "foo": "bar"
+ },
+ "startsAt": "2017-07-22T01:07:54.32189391Z",
+ "endsAt": "0001-01-01T00:00:00Z",
+ "state": "active",
+ "alertmanager": [ {
+ "name": "mock",
+ "uri": "http://localhost",
+ "state": "active",
+ "startsAt": "2017-07-22T01:07:54.32189391Z",
+ "endsAt": "0001-01-01T00:00:00Z",
+ "source": "localhost/prometheus",
+ "silences": {}
+ } ],
+ "receiver": "default",
+ "links": {}
+ } ],
+ "id": "12345",
+ "hash": "abcdef",
+ "stateCount": {"active": 1, "suppressed": 0, "unprocessed": 0}
+ } ]
+ });
+ ajaxServer.start();
+ // click on the button, modal should show and render via ajax call
+ $("#silenceButton").click();
+ jest.runOnlyPendingTimers();
+ // default times are relative to current time, use fixed values
+ let startsAt = moment("2050-01-01T01:00:00.000Z").utc();
+ let endsAt = moment("2050-01-01T02:00:00.000Z").utc();
+ $("#endsAt").data("DateTimePicker").date(endsAt);
+ $("#startsAt").data("DateTimePicker").date(startsAt);
+ // compare html to a snapshot
+ silenceModal = $("#silenceModal").html().trim();
+ expect(silenceModal).toMatchSnapshot();
+ ajaxServer.stop();
+
+ // submit silence
+ ajaxServer = ajaxMock.createServer(200, {
+ "status": "success",
+ "data": {"silenceId": "abcdef"}
+ });
+ ajaxServer.start();
+ $("#newSilenceForm").submit();
+ ajaxServer.stop();
+ silenceModal = $("#silenceModal").html().trim();
+ expect(silenceModal).toMatchSnapshot();
});
test("successful sendSilencePOST()", () => {
From 3124049c5a04daf08c9943f969212cdb59a2854d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C5=81ukasz=20Mierzwa?=
Date: Thu, 17 Aug 2017 19:33:16 -0700
Subject: [PATCH 7/7] Fix wrong status in error
Doesn't matter for test result, but should be error since we return 500
---
assets/static/silence.test.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/assets/static/silence.test.js b/assets/static/silence.test.js
index e653f6473..0150f02f8 100644
--- a/assets/static/silence.test.js
+++ b/assets/static/silence.test.js
@@ -157,7 +157,7 @@ test("failed sendSilencePOST()", () => {
templates.init();
const ajaxServer = ajaxMock.createServer(500, {
- "status": "success",
+ "status": "error",
"errorType": "server_error",
"error": "request failed"
});