diff --git a/cypress/e2e/accountAdmin.cy.js b/cypress/e2e/accountAdmin.cy.js index 68b16af..39d8f8c 100644 --- a/cypress/e2e/accountAdmin.cy.js +++ b/cypress/e2e/accountAdmin.cy.js @@ -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"); }); }); diff --git a/cypress/e2e/accountAdmin/accountDuplicationCheck.cy.js b/cypress/e2e/accountAdmin/accountDuplicationCheck.cy.js index 464cd34..87f1a5a 100644 --- a/cypress/e2e/accountAdmin/accountDuplicationCheck.cy.js +++ b/cypress/e2e/accountAdmin/accountDuplicationCheck.cy.js @@ -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: { - username: "000000", - name: "000000", - is_admin: false, - is_active: true, - roles: [], - }, - }).as("checkExistingUser"); + // 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: [], + })), + ); + }); cy.contains("button", "Create New").should("be.visible").click(); cy.get("#input_account_field").type(testAccountName); diff --git a/cypress/e2e/accountAdmin/confirmPasswordMessage.cy.js b/cypress/e2e/accountAdmin/confirmPasswordMessage.cy.js index ec5bd88..5384eae 100644 --- a/cypress/e2e/accountAdmin/confirmPasswordMessage.cy.js +++ b/cypress/e2e/accountAdmin/confirmPasswordMessage.cy.js @@ -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.", () => { diff --git a/cypress/e2e/accountAdmin/createAccount.cy.js b/cypress/e2e/accountAdmin/createAccount.cy.js index b3080a6..33cbce5 100644 --- a/cypress/e2e/accountAdmin/createAccount.cy.js +++ b/cypress/e2e/accountAdmin/createAccount.cy.js @@ -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"); }); diff --git a/cypress/e2e/accountAdmin/deleteAccount.cy.js b/cypress/e2e/accountAdmin/deleteAccount.cy.js index 5494821..7b4b524 100644 --- a/cypress/e2e/accountAdmin/deleteAccount.cy.js +++ b/cypress/e2e/accountAdmin/deleteAccount.cy.js @@ -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"); }); diff --git a/cypress/e2e/accountAdmin/editAccount.cy.js b/cypress/e2e/accountAdmin/editAccount.cy.js index 52618a1..4da48b0 100644 --- a/cypress/e2e/accountAdmin/editAccount.cy.js +++ b/cypress/e2e/accountAdmin/editAccount.cy.js @@ -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"); }); }); diff --git a/cypress/e2e/accountCrud.cy.js b/cypress/e2e/accountCrud.cy.js index 88f7e89..7664c5a 100644 --- a/cypress/e2e/accountCrud.cy.js +++ b/cypress/e2e/accountCrud.cy.js @@ -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", () => { diff --git a/cypress/e2e/accountInfo.cy.js b/cypress/e2e/accountInfo.cy.js index 7820346..9837585 100644 --- a/cypress/e2e/accountInfo.cy.js +++ b/cypress/e2e/accountInfo.cy.js @@ -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", () => { diff --git a/cypress/e2e/compare.cy.js b/cypress/e2e/compare.cy.js index be5e99d..c4b9b46 100644 --- a/cypress/e2e/compare.cy.js +++ b/cypress/e2e/compare.cy.js @@ -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); diff --git a/cypress/e2e/discoverConformance.cy.js b/cypress/e2e/discoverConformance.cy.js index c61caec..bf8ab98 100644 --- a/cypress/e2e/discoverConformance.cy.js +++ b/cypress/e2e/discoverConformance.cy.js @@ -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", () => { diff --git a/cypress/e2e/discoverMap.cy.js b/cypress/e2e/discoverMap.cy.js index b2a6c7c..702e63d 100644 --- a/cypress/e2e/discoverMap.cy.js +++ b/cypress/e2e/discoverMap.cy.js @@ -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", () => { diff --git a/cypress/e2e/discoverPerformance.cy.js b/cypress/e2e/discoverPerformance.cy.js index 548940f..6dd31c6 100644 --- a/cypress/e2e/discoverPerformance.cy.js +++ b/cypress/e2e/discoverPerformance.cy.js @@ -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", () => { diff --git a/cypress/e2e/discoverTabs.cy.js b/cypress/e2e/discoverTabs.cy.js index 0876a52..7f9844c 100644 --- a/cypress/e2e/discoverTabs.cy.js +++ b/cypress/e2e/discoverTabs.cy.js @@ -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"); }); diff --git a/cypress/e2e/edgeCases.cy.js b/cypress/e2e/edgeCases.cy.js index 246e07d..9bf697a 100644 --- a/cypress/e2e/edgeCases.cy.js +++ b/cypress/e2e/edgeCases.cy.js @@ -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("/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"); - cy.wait("@getEmptyUsers"); - // Create New button should still work + // 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"); }); }); }); diff --git a/cypress/e2e/fileOperations.cy.js b/cypress/e2e/fileOperations.cy.js index 145f9a5..4bbd3ff 100644 --- a/cypress/e2e/fileOperations.cy.js +++ b/cypress/e2e/fileOperations.cy.js @@ -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", () => { diff --git a/cypress/e2e/files.cy.js b/cypress/e2e/files.cy.js index 197bdc8..f890849 100644 --- a/cypress/e2e/files.cy.js +++ b/cypress/e2e/files.cy.js @@ -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(); diff --git a/cypress/e2e/filesCompare.cy.js b/cypress/e2e/filesCompare.cy.js index 161170a..a92505f 100644 --- a/cypress/e2e/filesCompare.cy.js +++ b/cypress/e2e/filesCompare.cy.js @@ -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(); }); diff --git a/cypress/e2e/filesToDiscover.cy.js b/cypress/e2e/filesToDiscover.cy.js index b631647..eeff1c0 100644 --- a/cypress/e2e/filesToDiscover.cy.js +++ b/cypress/e2e/filesToDiscover.cy.js @@ -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"); }); }); diff --git a/cypress/e2e/login.cy.js b/cypress/e2e/login.cy.js index 6735ceb..1f4c777 100644 --- a/cypress/e2e/login.cy.js +++ b/cypress/e2e/login.cy.js @@ -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"); }); diff --git a/cypress/e2e/logout.cy.js b/cypress/e2e/logout.cy.js index 0029031..fd72e5b 100644 --- a/cypress/e2e/logout.cy.js +++ b/cypress/e2e/logout.cy.js @@ -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(); diff --git a/cypress/e2e/myAccount.cy.js b/cypress/e2e/myAccount.cy.js index 52d084c..dd9125c 100644 --- a/cypress/e2e/myAccount.cy.js +++ b/cypress/e2e/myAccount.cy.js @@ -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", () => { diff --git a/cypress/e2e/navigation.cy.js b/cypress/e2e/navigation.cy.js index 800ef4a..f50fab8 100644 --- a/cypress/e2e/navigation.cy.js +++ b/cypress/e2e/navigation.cy.js @@ -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"); }); diff --git a/cypress/e2e/pageAdmin.cy.js b/cypress/e2e/pageAdmin.cy.js index 6fd6688..08a0ce5 100644 --- a/cypress/e2e/pageAdmin.cy.js +++ b/cypress/e2e/pageAdmin.cy.js @@ -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.", () => { diff --git a/cypress/e2e/pasteUrlLoginRedirect.cy.js b/cypress/e2e/pasteUrlLoginRedirect.cy.js index db6edd9..73cacb2 100644 --- a/cypress/e2e/pasteUrlLoginRedirect.cy.js +++ b/cypress/e2e/pasteUrlLoginRedirect.cy.js @@ -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"); diff --git a/cypress/e2e/sweetAlertModals.cy.js b/cypress/e2e/sweetAlertModals.cy.js index 6b4d5a1..f64c17d 100644 --- a/cypress/e2e/sweetAlertModals.cy.js +++ b/cypress/e2e/sweetAlertModals.cy.js @@ -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"); }); }); }); diff --git a/cypress/support/intercept.js b/cypress/support/intercept.js index 92c71b6..f029438 100644 --- a/cypress/support/intercept.js +++ b/cypress/support/intercept.js @@ -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"); } diff --git a/src/main.ts b/src/main.ts index d762eb6..c4bed74 100644 --- a/src/main.ts +++ b/src/main.ts @@ -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"));