Migrate Cypress E2E from cy.intercept to MSW service worker
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -12,7 +12,6 @@ describe("Account Management", () => {
|
||||
|
||||
it("displays user list on account admin page", () => {
|
||||
cy.visit("/account-admin");
|
||||
cy.wait("@getUsers");
|
||||
// Should display users from fixture
|
||||
cy.contains("Test Admin").should("exist");
|
||||
cy.contains("Alice Wang").should("exist");
|
||||
@@ -21,14 +20,13 @@ describe("Account Management", () => {
|
||||
|
||||
it("shows active/inactive status badges", () => {
|
||||
cy.visit("/account-admin");
|
||||
cy.wait("@getUsers");
|
||||
cy.contains("Test Admin").should("exist");
|
||||
// The user list should show status indicators
|
||||
cy.contains("testadmin").should("exist");
|
||||
});
|
||||
|
||||
it("navigates to my-account page", () => {
|
||||
cy.visit("/my-account");
|
||||
cy.wait("@getMyAccount");
|
||||
cy.url().should("include", "/my-account");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -12,17 +12,23 @@ describe("Account duplication check.", () => {
|
||||
beforeEach(() => {
|
||||
loginWithFixtures();
|
||||
cy.visit("/account-admin");
|
||||
cy.wait("@getUsers");
|
||||
cy.contains("Test Admin").should("exist");
|
||||
});
|
||||
|
||||
it("When an account already exists, show error message on confirm.", () => {
|
||||
const testAccountName = "000000";
|
||||
|
||||
// First creation: account doesn't exist yet
|
||||
cy.intercept("GET", "/api/users/000000", {
|
||||
statusCode: 404,
|
||||
body: { detail: "Not found" },
|
||||
}).as("checkNewUser");
|
||||
// First creation: account doesn't exist yet — override via MSW
|
||||
cy.window().then((win) => {
|
||||
const { http, HttpResponse } = win.__msw__;
|
||||
win.__mswWorker__.use(
|
||||
http.get("/api/users/000000", () =>
|
||||
HttpResponse.json(
|
||||
{ detail: "Not found" },
|
||||
{ status: 404 },
|
||||
)),
|
||||
);
|
||||
});
|
||||
|
||||
cy.contains("button", "Create New").should("be.visible").click();
|
||||
cy.get("#input_account_field").type(testAccountName);
|
||||
@@ -34,20 +40,22 @@ describe("Account duplication check.", () => {
|
||||
.should("be.visible")
|
||||
.and("be.enabled")
|
||||
.click();
|
||||
cy.wait("@postUser");
|
||||
cy.contains("Account added").should("be.visible");
|
||||
|
||||
// Second creation: now account exists — override to return 200
|
||||
cy.intercept("GET", "/api/users/000000", {
|
||||
statusCode: 200,
|
||||
body: {
|
||||
// Second creation: now account exists — override to return 200 via MSW
|
||||
cy.window().then((win) => {
|
||||
const { http, HttpResponse } = win.__msw__;
|
||||
win.__mswWorker__.use(
|
||||
http.get("/api/users/000000", () =>
|
||||
HttpResponse.json({
|
||||
username: "000000",
|
||||
name: "000000",
|
||||
is_admin: false,
|
||||
is_active: true,
|
||||
roles: [],
|
||||
},
|
||||
}).as("checkExistingUser");
|
||||
})),
|
||||
);
|
||||
});
|
||||
|
||||
cy.contains("button", "Create New").should("be.visible").click();
|
||||
cy.get("#input_account_field").type(testAccountName);
|
||||
|
||||
@@ -10,7 +10,7 @@ describe("Password validation on create account.", () => {
|
||||
beforeEach(() => {
|
||||
loginWithFixtures();
|
||||
cy.visit("/account-admin");
|
||||
cy.wait("@getUsers");
|
||||
cy.contains("Test Admin").should("exist");
|
||||
});
|
||||
|
||||
it("When password is too short, confirm button stays disabled.", () => {
|
||||
|
||||
@@ -9,13 +9,22 @@ import { loginWithFixtures } from "../../support/intercept";
|
||||
describe("Create an Account", () => {
|
||||
beforeEach(() => {
|
||||
loginWithFixtures();
|
||||
// Override: new usernames should return 404 (account doesn't exist yet)
|
||||
cy.intercept("GET", "/api/users/unit-test-*", {
|
||||
statusCode: 404,
|
||||
body: { detail: "Not found" },
|
||||
}).as("checkNewUser");
|
||||
cy.visit("/account-admin");
|
||||
cy.wait("@getUsers");
|
||||
cy.contains("Test Admin").should("exist");
|
||||
// Override: new usernames should return 404 (account doesn't exist yet)
|
||||
cy.window().then((win) => {
|
||||
const { http, HttpResponse } = win.__msw__;
|
||||
win.__mswWorker__.use(
|
||||
http.get("/api/users/:username", ({ params }) => {
|
||||
if (params.username.startsWith("unit-test-")) {
|
||||
return HttpResponse.json(
|
||||
{ detail: "Not found" },
|
||||
{ status: 404 },
|
||||
);
|
||||
}
|
||||
}),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it("Create a new account with admin role; should show saved message.", () => {
|
||||
@@ -30,7 +39,6 @@ describe("Create an Account", () => {
|
||||
.should("be.visible")
|
||||
.and("be.enabled")
|
||||
.click();
|
||||
cy.wait("@postUser");
|
||||
cy.contains("Account added").should("be.visible");
|
||||
});
|
||||
|
||||
|
||||
@@ -10,14 +10,13 @@ describe("Delete an Account", () => {
|
||||
beforeEach(() => {
|
||||
loginWithFixtures();
|
||||
cy.visit("/account-admin");
|
||||
cy.wait("@getUsers");
|
||||
cy.contains("Test Admin").should("exist");
|
||||
});
|
||||
|
||||
it("Delete button opens confirmation modal and deletes on confirm.", () => {
|
||||
cy.get("img.delete-account").first().click();
|
||||
cy.contains("ARE YOU SURE TO DELETE").should("be.visible");
|
||||
cy.get("#sure_to_delete_acct_btn").click();
|
||||
cy.wait("@deleteUser");
|
||||
cy.contains("Account deleted").should("be.visible");
|
||||
});
|
||||
|
||||
|
||||
@@ -13,20 +13,17 @@ describe("Edit an account", () => {
|
||||
beforeEach(() => {
|
||||
loginWithFixtures();
|
||||
cy.visit("/account-admin");
|
||||
cy.wait("@getUsers");
|
||||
cy.contains("Test Admin").should("exist");
|
||||
});
|
||||
|
||||
it("Edit an account; modify name and see saved message.", () => {
|
||||
cy.get(".btn-edit").first().click();
|
||||
cy.wait("@getUserDetail");
|
||||
|
||||
cy.contains("h1", MODAL_TITLE_ACCOUNT_EDIT).should("exist");
|
||||
cy.get("#input_name_field").clear();
|
||||
cy.get("#input_name_field").type("Updated Name");
|
||||
|
||||
cy.contains("button", "Confirm").should("be.visible").and("be.enabled");
|
||||
cy.contains("button", "Confirm").click();
|
||||
cy.wait("@putUser");
|
||||
cy.contains(MSG_ACCOUNT_EDITED).should("be.visible");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -9,7 +9,7 @@ describe("Account Management CRUD", () => {
|
||||
beforeEach(() => {
|
||||
loginWithFixtures();
|
||||
cy.visit("/account-admin");
|
||||
cy.wait("@getUsers");
|
||||
cy.contains("Test Admin").should("exist");
|
||||
});
|
||||
|
||||
it("shows Create New button", () => {
|
||||
|
||||
@@ -9,7 +9,7 @@ describe("Account Info Modal", () => {
|
||||
beforeEach(() => {
|
||||
loginWithFixtures();
|
||||
cy.visit("/account-admin");
|
||||
cy.wait("@getUsers");
|
||||
cy.contains("Test Admin").should("exist");
|
||||
});
|
||||
|
||||
it("double-click username opens info modal with user data", () => {
|
||||
|
||||
@@ -11,7 +11,7 @@ describe("Compare", () => {
|
||||
beforeEach(() => {
|
||||
loginWithFixtures();
|
||||
cy.visit("/files");
|
||||
cy.wait("@getFiles");
|
||||
cy.contains("sample-process.xes").should("exist");
|
||||
cy.contains("li", "COMPARE").click();
|
||||
});
|
||||
|
||||
@@ -55,7 +55,6 @@ describe("Compare", () => {
|
||||
cy.get("#compareFile0").drag("#primaryDragCard");
|
||||
cy.get("#compareFile1").drag("#secondaryDragCard");
|
||||
cy.contains("button", "Compare").click();
|
||||
cy.wait("@getCompare");
|
||||
cy.url().should("include", "compare");
|
||||
|
||||
// Assert chart title spans are visible
|
||||
@@ -73,7 +72,6 @@ describe("Compare", () => {
|
||||
cy.get("#compareFile0").drag("#primaryDragCard");
|
||||
cy.get("#compareFile1").drag("#secondaryDragCard");
|
||||
cy.contains("button", "Compare").click();
|
||||
cy.wait("@getCompare");
|
||||
|
||||
cy.get("#compareState").should("exist").and("be.visible");
|
||||
});
|
||||
@@ -82,7 +80,6 @@ describe("Compare", () => {
|
||||
cy.get("#compareFile0").drag("#primaryDragCard");
|
||||
cy.get("#compareFile1").drag("#secondaryDragCard");
|
||||
cy.contains("button", "Compare").click();
|
||||
cy.wait("@getCompare");
|
||||
|
||||
cy.get("aside").should("exist");
|
||||
cy.get("aside li").should("have.length.greaterThan", 0);
|
||||
|
||||
@@ -9,7 +9,7 @@ describe("Discover Conformance Page", () => {
|
||||
beforeEach(() => {
|
||||
loginWithFixtures();
|
||||
cy.visit("/discover/log/297310264/conformance");
|
||||
cy.wait("@getLogCheckParams");
|
||||
cy.get(".p-radiobutton, [class*=conformance]").first().should("exist");
|
||||
});
|
||||
|
||||
it("page loads and loading overlay disappears", () => {
|
||||
|
||||
@@ -9,7 +9,7 @@ describe("Discover Map Page", () => {
|
||||
beforeEach(() => {
|
||||
loginWithFixtures();
|
||||
cy.visit("/discover/log/297310264/map");
|
||||
cy.wait("@getDiscover");
|
||||
cy.get("#cy").should("exist");
|
||||
});
|
||||
|
||||
it("page loads and cytoscape container exists", () => {
|
||||
|
||||
@@ -9,7 +9,7 @@ describe("Discover Performance Page", () => {
|
||||
beforeEach(() => {
|
||||
loginWithFixtures();
|
||||
cy.visit("/discover/log/297310264/performance");
|
||||
cy.wait("@getPerformance");
|
||||
cy.get(".chart-container, canvas").should("exist");
|
||||
});
|
||||
|
||||
it("page loads and loading overlay disappears", () => {
|
||||
|
||||
@@ -13,7 +13,7 @@ describe("Discover Tab Navigation", () => {
|
||||
describe("navigating from Map page", () => {
|
||||
beforeEach(() => {
|
||||
cy.visit("/discover/log/297310264/map");
|
||||
cy.wait("@getDiscover");
|
||||
cy.get("#cy").should("exist");
|
||||
});
|
||||
|
||||
it("shows DISCOVER heading and MAP/CONFORMANCE/PERFORMANCE tabs", () => {
|
||||
@@ -27,7 +27,6 @@ describe("Discover Tab Navigation", () => {
|
||||
it("clicking PERFORMANCE tab navigates to performance page", () => {
|
||||
cy.get(".nav-item").contains("PERFORMANCE").click();
|
||||
cy.url().should("include", "/performance");
|
||||
cy.wait("@getPerformance");
|
||||
cy.get(String.raw`.z-\[9999\]`, { timeout: 10000 }).should("not.exist");
|
||||
cy.contains("Time Usage").should("be.visible");
|
||||
});
|
||||
@@ -35,7 +34,6 @@ describe("Discover Tab Navigation", () => {
|
||||
it("clicking CONFORMANCE tab navigates to conformance page", () => {
|
||||
cy.get(".nav-item").contains("CONFORMANCE").click();
|
||||
cy.url().should("include", "/conformance");
|
||||
cy.wait("@getLogCheckParams");
|
||||
cy.get(String.raw`.z-\[9999\]`, { timeout: 10000 }).should("not.exist");
|
||||
cy.contains("Rule Settings").should("be.visible");
|
||||
});
|
||||
@@ -49,21 +47,19 @@ describe("Discover Tab Navigation", () => {
|
||||
describe("navigating from Performance page", () => {
|
||||
beforeEach(() => {
|
||||
cy.visit("/discover/log/297310264/performance");
|
||||
cy.wait("@getPerformance");
|
||||
cy.get(".chart-container, canvas").should("exist");
|
||||
cy.get(String.raw`.z-\[9999\]`, { timeout: 10000 }).should("not.exist");
|
||||
});
|
||||
|
||||
it("clicking MAP tab navigates to map page", () => {
|
||||
cy.get(".nav-item").contains("MAP").click();
|
||||
cy.url().should("include", "/map");
|
||||
cy.wait("@getDiscover");
|
||||
cy.get("#cy").should("exist");
|
||||
});
|
||||
|
||||
it("clicking CONFORMANCE tab navigates to conformance page", () => {
|
||||
cy.get(".nav-item").contains("CONFORMANCE").click();
|
||||
cy.url().should("include", "/conformance");
|
||||
cy.wait("@getLogCheckParams");
|
||||
cy.get(String.raw`.z-\[9999\]`, { timeout: 10000 }).should("not.exist");
|
||||
cy.contains("Rule Settings").should("be.visible");
|
||||
});
|
||||
@@ -72,21 +68,19 @@ describe("Discover Tab Navigation", () => {
|
||||
describe("navigating from Conformance page", () => {
|
||||
beforeEach(() => {
|
||||
cy.visit("/discover/log/297310264/conformance");
|
||||
cy.wait("@getLogCheckParams");
|
||||
cy.get(".p-radiobutton, [class*=conformance]").first().should("exist");
|
||||
cy.get(String.raw`.z-\[9999\]`, { timeout: 10000 }).should("not.exist");
|
||||
});
|
||||
|
||||
it("clicking MAP tab navigates to map page", () => {
|
||||
cy.get(".nav-item").contains("MAP").click();
|
||||
cy.url().should("include", "/map");
|
||||
cy.wait("@getDiscover");
|
||||
cy.get("#cy").should("exist");
|
||||
});
|
||||
|
||||
it("clicking PERFORMANCE tab navigates to performance page", () => {
|
||||
cy.get(".nav-item").contains("PERFORMANCE").click();
|
||||
cy.url().should("include", "/performance");
|
||||
cy.wait("@getPerformance");
|
||||
cy.get(String.raw`.z-\[9999\]`, { timeout: 10000 }).should("not.exist");
|
||||
cy.contains("Time Usage").should("be.visible");
|
||||
});
|
||||
|
||||
@@ -9,13 +9,18 @@ describe("Edge Cases", () => {
|
||||
describe("Empty states", () => {
|
||||
it("files page handles empty file list", () => {
|
||||
loginWithFixtures();
|
||||
// Override files intercept with empty array
|
||||
cy.intercept("GET", "/api/files", {
|
||||
statusCode: 200,
|
||||
body: [],
|
||||
}).as("getEmptyFiles");
|
||||
// Visit any page first to load the app and MSW
|
||||
cy.visit("/files");
|
||||
cy.contains("sample-process.xes").should("exist");
|
||||
// Override files endpoint with empty array via MSW
|
||||
cy.window().then((win) => {
|
||||
const { http, HttpResponse } = win.__msw__;
|
||||
win.__mswWorker__.use(
|
||||
http.get("/api/files", () => HttpResponse.json([])),
|
||||
);
|
||||
});
|
||||
// Revisit to trigger the new empty response
|
||||
cy.visit("/files");
|
||||
cy.wait("@getEmptyFiles");
|
||||
// Table should exist but have no file data
|
||||
cy.get("table").should("exist");
|
||||
cy.contains("sample-process.xes").should("not.exist");
|
||||
@@ -23,20 +28,36 @@ describe("Edge Cases", () => {
|
||||
|
||||
it("account admin handles empty user list", () => {
|
||||
loginWithFixtures();
|
||||
cy.intercept("GET", "/api/users", {
|
||||
statusCode: 200,
|
||||
body: [],
|
||||
}).as("getEmptyUsers");
|
||||
cy.visit("/account-admin");
|
||||
cy.wait("@getEmptyUsers");
|
||||
// Create New button should still work
|
||||
cy.get("#create_new_acct_btn").should("exist");
|
||||
cy.visit("/files");
|
||||
cy.contains("sample-process.xes").should("exist");
|
||||
// Override users endpoint with empty array via MSW
|
||||
cy.window().then((win) => {
|
||||
const { http, HttpResponse } = win.__msw__;
|
||||
win.__mswWorker__.use(
|
||||
http.get("/api/users", () => HttpResponse.json([])),
|
||||
);
|
||||
});
|
||||
cy.visit("/account-admin");
|
||||
// Create New button should exist even with no users
|
||||
cy.get("#create_new_acct_btn").should("exist");
|
||||
cy.contains("Test Admin").should("not.exist");
|
||||
});
|
||||
|
||||
describe("Authentication guard", () => {
|
||||
it("unauthenticated user is redirected to login", () => {
|
||||
// No loginWithFixtures - not logged in
|
||||
loginWithFixtures();
|
||||
cy.visit("/files");
|
||||
cy.contains("sample-process.xes").should("exist");
|
||||
// Override my-account to return 401
|
||||
cy.window().then((win) => {
|
||||
const { http, HttpResponse } = win.__msw__;
|
||||
win.__mswWorker__.use(
|
||||
http.get("/api/my-account", () =>
|
||||
new HttpResponse(null, { status: 401 }),
|
||||
),
|
||||
);
|
||||
});
|
||||
// Clear cookies to simulate logged out
|
||||
cy.clearCookies();
|
||||
cy.visit("/files");
|
||||
cy.url().should("include", "/login");
|
||||
});
|
||||
@@ -54,41 +75,52 @@ describe("Edge Cases", () => {
|
||||
|
||||
describe("Login validation", () => {
|
||||
it("shows error on failed login", () => {
|
||||
cy.intercept("POST", "/api/oauth/token", {
|
||||
statusCode: 401,
|
||||
body: { detail: "Invalid credentials" },
|
||||
}).as("failedLogin");
|
||||
// Visit login page first to load the app
|
||||
cy.visit("/login");
|
||||
cy.get("#account").type("wrong");
|
||||
cy.get("#password").type("wrong");
|
||||
cy.get("form").submit();
|
||||
cy.wait("@failedLogin");
|
||||
// Should stay on login page
|
||||
cy.get("#login_btn_main_btn").should("exist");
|
||||
// Override token endpoint to return 401
|
||||
cy.window().then((win) => {
|
||||
const { http, HttpResponse } = win.__msw__;
|
||||
win.__mswWorker__.use(
|
||||
http.post("/api/oauth/token", () =>
|
||||
HttpResponse.json(
|
||||
{ detail: "Invalid credentials" },
|
||||
{ status: 401 },
|
||||
),
|
||||
),
|
||||
);
|
||||
});
|
||||
cy.get("#account").type("wronguser");
|
||||
cy.get("#password").type("wrongpass");
|
||||
cy.get("#login_btn_main_btn").click();
|
||||
cy.url().should("include", "/login");
|
||||
});
|
||||
});
|
||||
|
||||
describe("Account creation validation", () => {
|
||||
beforeEach(() => {
|
||||
loginWithFixtures();
|
||||
cy.visit("/account-admin");
|
||||
cy.wait("@getUsers");
|
||||
cy.get("#create_new_acct_btn").click();
|
||||
});
|
||||
|
||||
it("confirm stays disabled with only account field filled", () => {
|
||||
cy.get("#input_account_field").type("newuser");
|
||||
cy.get(".confirm-btn").should("be.disabled");
|
||||
loginWithFixtures();
|
||||
cy.visit("/account-admin");
|
||||
cy.contains("Test Admin").should("exist");
|
||||
cy.contains("button", "Create New").click();
|
||||
cy.get("#input_account_field").type("onlyaccount");
|
||||
cy.contains("button", "Confirm").should("be.disabled");
|
||||
});
|
||||
|
||||
it("confirm stays disabled with only name field filled", () => {
|
||||
cy.get("#input_name_field").type("New User");
|
||||
cy.get(".confirm-btn").should("be.disabled");
|
||||
loginWithFixtures();
|
||||
cy.visit("/account-admin");
|
||||
cy.contains("Test Admin").should("exist");
|
||||
cy.contains("button", "Create New").click();
|
||||
cy.get("#input_name_field").type("onlyname");
|
||||
cy.contains("button", "Confirm").should("be.disabled");
|
||||
});
|
||||
|
||||
it("confirm stays disabled with only password field filled", () => {
|
||||
cy.get("#input_first_pwd").type("password1234");
|
||||
cy.get(".confirm-btn").should("be.disabled");
|
||||
loginWithFixtures();
|
||||
cy.visit("/account-admin");
|
||||
cy.contains("Test Admin").should("exist");
|
||||
cy.contains("button", "Create New").click();
|
||||
cy.get("#input_first_pwd").type("onlypassword");
|
||||
cy.contains("button", "Confirm").should("be.disabled");
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -9,7 +9,7 @@ describe("File Operations", () => {
|
||||
beforeEach(() => {
|
||||
loginWithFixtures();
|
||||
cy.visit("/files");
|
||||
cy.wait("@getFiles");
|
||||
cy.contains("sample-process.xes").should("exist");
|
||||
});
|
||||
|
||||
it("file list table has sortable columns", () => {
|
||||
|
||||
@@ -12,7 +12,7 @@ describe("Files Page", () => {
|
||||
});
|
||||
|
||||
it("displays the file list after login", () => {
|
||||
cy.wait("@getFiles");
|
||||
cy.contains("sample-process.xes").should("exist");
|
||||
cy.contains("h2", "All Files").should("exist");
|
||||
// Should display file names from fixture
|
||||
cy.contains("sample-process.xes").should("exist");
|
||||
@@ -21,37 +21,37 @@ describe("Files Page", () => {
|
||||
});
|
||||
|
||||
it("shows Recently Used section", () => {
|
||||
cy.wait("@getFiles");
|
||||
cy.contains("sample-process.xes").should("exist");
|
||||
cy.contains("h2", "Recently Used").should("exist");
|
||||
});
|
||||
|
||||
it("switches to DISCOVER tab", () => {
|
||||
cy.wait("@getFiles");
|
||||
cy.contains("sample-process.xes").should("exist");
|
||||
cy.contains(".nav-item", "DISCOVER").click();
|
||||
// DISCOVER tab shows filtered file types
|
||||
cy.contains("h2", "All Files").should("exist");
|
||||
});
|
||||
|
||||
it("switches to COMPARE tab and shows drag zones", () => {
|
||||
cy.wait("@getFiles");
|
||||
cy.contains("sample-process.xes").should("exist");
|
||||
cy.contains(".nav-item", "COMPARE").click();
|
||||
cy.contains("Performance Comparison").should("exist");
|
||||
cy.contains("Drag and drop a file here").should("exist");
|
||||
});
|
||||
|
||||
it("shows Import button on FILES tab", () => {
|
||||
cy.wait("@getFiles");
|
||||
cy.contains("sample-process.xes").should("exist");
|
||||
cy.get("#import_btn").should("contain", "Import");
|
||||
});
|
||||
|
||||
it("can switch between list and grid view", () => {
|
||||
cy.wait("@getFiles");
|
||||
cy.contains("sample-process.xes").should("exist");
|
||||
// DataTable (list view) should be visible by default
|
||||
cy.get("table").should("exist");
|
||||
});
|
||||
|
||||
it("double-click file navigates to discover page", () => {
|
||||
cy.wait("@getFiles");
|
||||
cy.contains("sample-process.xes").should("exist");
|
||||
// Double-click the first file row in the table
|
||||
// The actual route depends on file type (log→map, log-check→conformance, etc.)
|
||||
cy.get("table tbody tr").first().dblclick();
|
||||
|
||||
@@ -9,7 +9,7 @@ describe("Files Page - COMPARE Tab", () => {
|
||||
beforeEach(() => {
|
||||
loginWithFixtures();
|
||||
cy.visit("/files");
|
||||
cy.wait("@getFiles");
|
||||
cy.contains("sample-process.xes").should("exist");
|
||||
// Switch to COMPARE tab
|
||||
cy.contains("li", "COMPARE").click();
|
||||
});
|
||||
|
||||
@@ -9,7 +9,7 @@ describe("Files to Discover Entry Flow", () => {
|
||||
beforeEach(() => {
|
||||
loginWithFixtures();
|
||||
cy.visit("/files");
|
||||
cy.wait("@getFiles");
|
||||
cy.contains("sample-process.xes").should("exist");
|
||||
});
|
||||
|
||||
describe("double-click table row to enter Discover", () => {
|
||||
@@ -17,14 +17,12 @@ describe("Files to Discover Entry Flow", () => {
|
||||
// Target the Name column (has class .fileName) to avoid matching Dependency column
|
||||
cy.contains("td.fileName", "sample-process.xes").parent("tr").dblclick();
|
||||
cy.url().should("include", "/discover/log/1/map");
|
||||
cy.wait("@getDiscover");
|
||||
cy.get("#cy").should("exist");
|
||||
});
|
||||
|
||||
it("double-click filter file navigates to Map page", () => {
|
||||
cy.contains("td.fileName", "filtered-sample").parent("tr").dblclick();
|
||||
cy.url().should("include", "/discover/filter/10/map");
|
||||
cy.wait("@getFilterDiscover");
|
||||
cy.get("#cy").should("exist");
|
||||
});
|
||||
});
|
||||
@@ -39,7 +37,6 @@ describe("Files to Discover Entry Flow", () => {
|
||||
// Use last() to target the All Files grid section (not Recently Used)
|
||||
cy.get('li[title="sample-process.xes"]').last().dblclick();
|
||||
cy.url().should("include", "/discover/log/1/map");
|
||||
cy.wait("@getDiscover");
|
||||
cy.get("#cy").should("exist");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -40,23 +40,29 @@ describe("Login Flow", () => {
|
||||
cy.get("#password").type("password123");
|
||||
cy.get("#login_btn_main_btn").click();
|
||||
|
||||
cy.wait("@postToken");
|
||||
cy.url().should("include", "/files");
|
||||
});
|
||||
|
||||
it("failed login shows error message", () => {
|
||||
// Override the token intercept to return 401
|
||||
cy.intercept("POST", "/api/oauth/token", {
|
||||
statusCode: 401,
|
||||
body: { detail: "Incorrect username or password" },
|
||||
}).as("postTokenFail");
|
||||
|
||||
// Visit login first to load app + MSW
|
||||
cy.visit("/login");
|
||||
cy.get("#login_btn_main_btn").should("exist");
|
||||
// Override the token endpoint to return 401 via MSW
|
||||
cy.window().then((win) => {
|
||||
const { http, HttpResponse } = win.__msw__;
|
||||
win.__mswWorker__.use(
|
||||
http.post("/api/oauth/token", () =>
|
||||
HttpResponse.json(
|
||||
{ detail: "Incorrect username or password" },
|
||||
{ status: 401 },
|
||||
)),
|
||||
);
|
||||
});
|
||||
|
||||
cy.get("#account").type("wronguser");
|
||||
cy.get("#password").type("wrongpass");
|
||||
cy.get("#login_btn_main_btn").click();
|
||||
|
||||
cy.wait("@postTokenFail");
|
||||
cy.contains("Incorrect account or password").should("be.visible");
|
||||
});
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ describe("Logout Flow", () => {
|
||||
|
||||
it("shows account menu when head icon is clicked", () => {
|
||||
cy.visit("/files");
|
||||
cy.wait("@getFiles");
|
||||
cy.contains("sample-process.xes").should("exist");
|
||||
|
||||
// Click the head icon to open account menu
|
||||
cy.get("#acct_mgmt_button").click();
|
||||
@@ -22,7 +22,7 @@ describe("Logout Flow", () => {
|
||||
|
||||
it("account menu shows admin management link for admin user", () => {
|
||||
cy.visit("/files");
|
||||
cy.wait("@getFiles");
|
||||
cy.contains("sample-process.xes").should("exist");
|
||||
|
||||
cy.get("#acct_mgmt_button").click();
|
||||
cy.get("#account_menu").should("be.visible");
|
||||
@@ -32,7 +32,7 @@ describe("Logout Flow", () => {
|
||||
|
||||
it("account menu has logout button", () => {
|
||||
cy.visit("/files");
|
||||
cy.wait("@getFiles");
|
||||
cy.contains("sample-process.xes").should("exist");
|
||||
|
||||
cy.get("#acct_mgmt_button").click();
|
||||
cy.get("#btn_logout_in_menu").should("exist");
|
||||
@@ -40,7 +40,7 @@ describe("Logout Flow", () => {
|
||||
|
||||
it("clicking My Account navigates to /my-account", () => {
|
||||
cy.visit("/files");
|
||||
cy.wait("@getFiles");
|
||||
cy.contains("sample-process.xes").should("exist");
|
||||
|
||||
cy.get("#acct_mgmt_button").click();
|
||||
cy.get("#btn_mang_ur_acct").click();
|
||||
@@ -49,7 +49,7 @@ describe("Logout Flow", () => {
|
||||
|
||||
it("clicking Account Management navigates to /account-admin", () => {
|
||||
cy.visit("/files");
|
||||
cy.wait("@getFiles");
|
||||
cy.contains("sample-process.xes").should("exist");
|
||||
|
||||
cy.get("#acct_mgmt_button").click();
|
||||
cy.get("#btn_acct_mgmt").click();
|
||||
@@ -58,7 +58,7 @@ describe("Logout Flow", () => {
|
||||
|
||||
it("logout redirects to login page", () => {
|
||||
cy.visit("/files");
|
||||
cy.wait("@getFiles");
|
||||
cy.contains("sample-process.xes").should("exist");
|
||||
|
||||
cy.get("#acct_mgmt_button").click();
|
||||
cy.get("#btn_logout_in_menu").click();
|
||||
|
||||
@@ -9,7 +9,7 @@ describe("My Account Page", () => {
|
||||
beforeEach(() => {
|
||||
loginWithFixtures();
|
||||
cy.visit("/my-account");
|
||||
cy.wait("@getUserDetail");
|
||||
cy.contains("Test Admin").should("exist");
|
||||
});
|
||||
|
||||
it("displays user name heading", () => {
|
||||
|
||||
@@ -21,7 +21,7 @@ describe("Navigation and Routing", () => {
|
||||
it("navbar shows correct view name", () => {
|
||||
loginWithFixtures();
|
||||
cy.visit("/files");
|
||||
cy.wait("@getFiles");
|
||||
cy.contains("sample-process.xes").should("exist");
|
||||
cy.get("#nav_bar").should("exist");
|
||||
cy.get("#nav_bar h2").should("contain", "FILES");
|
||||
});
|
||||
|
||||
@@ -10,7 +10,7 @@ describe("Discover page navigation tabs", () => {
|
||||
beforeEach(() => {
|
||||
loginWithFixtures();
|
||||
cy.visit("/files");
|
||||
cy.wait("@getFiles");
|
||||
cy.contains("sample-process.xes").should("exist");
|
||||
});
|
||||
|
||||
it("Double-clicking a log file enters the MAP page.", () => {
|
||||
|
||||
@@ -20,7 +20,6 @@ describe("Paste URL login redirect", () => {
|
||||
cy.get("#account").type("testadmin");
|
||||
cy.get("#password").type("password123");
|
||||
cy.get("form").submit();
|
||||
cy.wait("@postToken");
|
||||
|
||||
// After login, the app should attempt to redirect to the return-to URL.
|
||||
// Since window.location.href is used (not router.push), we verify the
|
||||
@@ -35,18 +34,25 @@ describe("Paste URL login redirect", () => {
|
||||
cy.get("#account").type("testadmin");
|
||||
cy.get("#password").type("password123");
|
||||
cy.get("form").submit();
|
||||
cy.wait("@postToken");
|
||||
|
||||
cy.url().should("include", "/files");
|
||||
});
|
||||
|
||||
it("Unauthenticated user cannot access inner pages", () => {
|
||||
setupApiIntercepts();
|
||||
// Override my-account to return 401 (simulate logged-out state)
|
||||
cy.intercept("GET", "/api/my-account", {
|
||||
statusCode: 401,
|
||||
body: { detail: "Not authenticated" },
|
||||
}).as("getMyAccountUnauth");
|
||||
// Visit login first to load the app + MSW
|
||||
cy.visit("/login");
|
||||
cy.get("#login_btn_main_btn").should("exist");
|
||||
// Override my-account to return 401 (simulate logged-out state) via MSW
|
||||
cy.window().then((win) => {
|
||||
const { http, HttpResponse } = win.__msw__;
|
||||
win.__mswWorker__.use(
|
||||
http.get("/api/my-account", () =>
|
||||
HttpResponse.json(
|
||||
{ detail: "Not authenticated" },
|
||||
{ status: 401 },
|
||||
)),
|
||||
);
|
||||
});
|
||||
|
||||
cy.visit("/files");
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ describe("SweetAlert2 Modals", () => {
|
||||
beforeEach(() => {
|
||||
loginWithFixtures();
|
||||
cy.visit("/files");
|
||||
cy.wait("@getFiles");
|
||||
cy.contains("sample-process.xes").should("exist");
|
||||
});
|
||||
|
||||
it("right-click on table row shows context menu with Rename", () => {
|
||||
@@ -79,7 +79,7 @@ describe("SweetAlert2 Modals", () => {
|
||||
beforeEach(() => {
|
||||
loginWithFixtures();
|
||||
cy.visit("/files");
|
||||
cy.wait("@getFiles");
|
||||
cy.contains("sample-process.xes").should("exist");
|
||||
// Switch to grid view
|
||||
cy.get("svg").parent("li.cursor-pointer").last().click();
|
||||
});
|
||||
@@ -102,15 +102,15 @@ describe("SweetAlert2 Modals", () => {
|
||||
beforeEach(() => {
|
||||
loginWithFixtures();
|
||||
cy.visit("/account-admin");
|
||||
cy.wait("@getUsers");
|
||||
cy.contains("Test Admin").should("exist");
|
||||
});
|
||||
|
||||
it("delete confirmation Yes button triggers delete API", () => {
|
||||
cy.get(".delete-account").first().click();
|
||||
cy.get("#modal_container").should("be.visible");
|
||||
cy.get("#sure_to_delete_acct_btn").click();
|
||||
cy.wait("@deleteUser");
|
||||
// Modal should close after deletion
|
||||
cy.get("#modal_container").should("not.exist");
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -4,187 +4,18 @@
|
||||
// imacat.yang@dsp.im (imacat), 2026/03/05
|
||||
|
||||
/**
|
||||
* Sets up cy.intercept for all API endpoints using fixture files.
|
||||
* Call setupApiIntercepts() in beforeEach to mock the entire backend.
|
||||
* API mocking is now handled by MSW (Mock Service Worker).
|
||||
* This function is kept for backward compatibility but does nothing.
|
||||
*/
|
||||
export function setupApiIntercepts() {
|
||||
// Auth
|
||||
cy.intercept("POST", "/api/oauth/token", {
|
||||
fixture: "api/token.json",
|
||||
}).as("postToken");
|
||||
|
||||
// User account
|
||||
cy.intercept("GET", "/api/my-account", {
|
||||
fixture: "api/my-account.json",
|
||||
}).as("getMyAccount");
|
||||
|
||||
cy.intercept("PUT", "/api/my-account", {
|
||||
statusCode: 200,
|
||||
body: { success: true },
|
||||
}).as("putMyAccount");
|
||||
|
||||
// Files
|
||||
cy.intercept("GET", "/api/files", {
|
||||
fixture: "api/files.json",
|
||||
}).as("getFiles");
|
||||
|
||||
// Users (account management)
|
||||
cy.intercept("GET", "/api/users", {
|
||||
fixture: "api/users.json",
|
||||
}).as("getUsers");
|
||||
|
||||
cy.intercept("POST", "/api/users", {
|
||||
statusCode: 200,
|
||||
body: { success: true },
|
||||
}).as("postUser");
|
||||
|
||||
cy.intercept("DELETE", "/api/users/*", {
|
||||
statusCode: 200,
|
||||
body: { success: true },
|
||||
}).as("deleteUser");
|
||||
|
||||
cy.intercept("PUT", "/api/users/*", {
|
||||
statusCode: 200,
|
||||
body: { success: true },
|
||||
}).as("putUser");
|
||||
|
||||
// User detail (GET /api/users/:username)
|
||||
cy.intercept("GET", "/api/users/*", {
|
||||
fixture: "api/user-detail.json",
|
||||
}).as("getUserDetail");
|
||||
|
||||
// User roles
|
||||
cy.intercept("PUT", "/api/users/*/roles/*", {
|
||||
statusCode: 200,
|
||||
body: { success: true },
|
||||
}).as("putUserRole");
|
||||
|
||||
cy.intercept("DELETE", "/api/users/*/roles/*", {
|
||||
statusCode: 200,
|
||||
body: { success: true },
|
||||
}).as("deleteUserRole");
|
||||
|
||||
// Filter detail (for fetchFunnel when entering filter from Files)
|
||||
cy.intercept("GET", /\/api\/filters\/\d+$/, {
|
||||
statusCode: 200,
|
||||
body: { rules: [], log: { id: 1 }, name: "filtered-sample" },
|
||||
}).as("getFilterDetail");
|
||||
|
||||
// Discover (map data)
|
||||
cy.intercept("GET", "/api/logs/*/discover", {
|
||||
fixture: "api/discover.json",
|
||||
}).as("getDiscover");
|
||||
|
||||
cy.intercept("GET", "/api/filters/*/discover", {
|
||||
fixture: "api/discover.json",
|
||||
}).as("getFilterDiscover");
|
||||
|
||||
// Performance
|
||||
cy.intercept("GET", "/api/logs/*/performance", {
|
||||
fixture: "api/performance.json",
|
||||
}).as("getPerformance");
|
||||
|
||||
cy.intercept("GET", "/api/filters/*/performance", {
|
||||
fixture: "api/performance.json",
|
||||
}).as("getFilterPerformance");
|
||||
|
||||
// Traces
|
||||
cy.intercept("GET", "/api/logs/*/traces", {
|
||||
fixture: "api/traces.json",
|
||||
}).as("getTraces");
|
||||
|
||||
cy.intercept("GET", "/api/filters/*/traces", {
|
||||
fixture: "api/traces.json",
|
||||
}).as("getFilterTraces");
|
||||
|
||||
// Trace detail (must be after traces list intercepts)
|
||||
cy.intercept("GET", /\/api\/logs\/.*\/traces\/\d+/, {
|
||||
fixture: "api/trace-detail.json",
|
||||
}).as("getTraceDetail");
|
||||
|
||||
cy.intercept("GET", /\/api\/filters\/.*\/traces\/\d+/, {
|
||||
fixture: "api/trace-detail.json",
|
||||
}).as("getFilterTraceDetail");
|
||||
|
||||
// Temp filters
|
||||
cy.intercept("GET", "/api/temp-filters/*/discover", {
|
||||
fixture: "api/discover.json",
|
||||
}).as("getTempFilterDiscover");
|
||||
|
||||
cy.intercept("GET", "/api/temp-filters/*/traces", {
|
||||
fixture: "api/traces.json",
|
||||
}).as("getTempFilterTraces");
|
||||
|
||||
// Filter params
|
||||
cy.intercept("GET", "/api/filters/params*", {
|
||||
statusCode: 200,
|
||||
body: {},
|
||||
}).as("getFilterParams");
|
||||
|
||||
cy.intercept("GET", "/api/filters/has-result*", {
|
||||
statusCode: 200,
|
||||
body: false,
|
||||
}).as("getFilterHasResult");
|
||||
|
||||
// Conformance check params
|
||||
cy.intercept("GET", "/api/log-checks/params*", {
|
||||
fixture: "api/filter-params.json",
|
||||
}).as("getLogCheckParams");
|
||||
|
||||
cy.intercept("GET", "/api/filter-checks/params*", {
|
||||
fixture: "api/filter-params.json",
|
||||
}).as("getFilterCheckParams");
|
||||
|
||||
// Compare dashboard
|
||||
cy.intercept("GET", /\/api\/compare\?datasets=/, {
|
||||
fixture: "api/compare.json",
|
||||
}).as("getCompare");
|
||||
|
||||
// Dependents (for delete confirmation)
|
||||
cy.intercept("GET", "/api/logs/*/dependents", {
|
||||
statusCode: 200,
|
||||
body: [],
|
||||
}).as("getLogDependents");
|
||||
|
||||
cy.intercept("GET", "/api/filters/*/dependents", {
|
||||
statusCode: 200,
|
||||
body: [],
|
||||
}).as("getFilterDependents");
|
||||
|
||||
cy.intercept("GET", "/api/log-checks/*/dependents", {
|
||||
statusCode: 200,
|
||||
body: [],
|
||||
}).as("getLogCheckDependents");
|
||||
|
||||
cy.intercept("GET", "/api/filter-checks/*/dependents", {
|
||||
statusCode: 200,
|
||||
body: [],
|
||||
}).as("getFilterCheckDependents");
|
||||
|
||||
// Rename
|
||||
cy.intercept("PUT", "/api/logs/*/rename", {
|
||||
statusCode: 200,
|
||||
body: { success: true },
|
||||
}).as("renameLog");
|
||||
|
||||
cy.intercept("PUT", "/api/filters/*/rename", {
|
||||
statusCode: 200,
|
||||
body: { success: true },
|
||||
}).as("renameFilter");
|
||||
|
||||
// Deletion
|
||||
cy.intercept("DELETE", "/api/deletion/*", {
|
||||
statusCode: 200,
|
||||
body: { success: true },
|
||||
}).as("deleteDeletion");
|
||||
// MSW handles all API interception via service worker.
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the luciaToken cookie and isLuciaLoggedIn cookie to simulate
|
||||
* a logged-in state, then sets up all API intercepts.
|
||||
* a logged-in state. API interception is handled by MSW.
|
||||
*/
|
||||
export function loginWithFixtures() {
|
||||
setupApiIntercepts();
|
||||
cy.setCookie("luciaToken", "fake-access-token-for-testing");
|
||||
cy.setCookie("isLuciaLoggedIn", "true");
|
||||
}
|
||||
|
||||
@@ -104,8 +104,12 @@ app.directive("tooltip", Tooltip);
|
||||
*/
|
||||
async function enableMocking() {
|
||||
if (import.meta.env.VITE_MSW !== "true") return;
|
||||
const { http, HttpResponse } = await import("msw");
|
||||
const { worker } = await import("./mocks/browser.js");
|
||||
await worker.start({ onUnhandledRequest: "bypass" });
|
||||
// Expose on window so Cypress can add per-test overrides
|
||||
window.__mswWorker__ = worker;
|
||||
window.__msw__ = { http, HttpResponse };
|
||||
}
|
||||
|
||||
enableMocking().then(() => app.mount("#app"));
|
||||
|
||||
Reference in New Issue
Block a user