From 6641bc1f8fbfffd487a64745f7da4b81d8d14e08 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BE=9D=E7=91=AA=E8=B2=93?= Date: Thu, 5 Mar 2026 20:52:38 +0800 Subject: [PATCH] Add E2E tests for my-account, account info modal, and compare tab Co-Authored-By: Claude Opus 4.6 --- cypress/e2e/accountInfo.cy.js | 35 ++++++++++++++ cypress/e2e/filesCompare.cy.js | 44 +++++++++++++++++ cypress/e2e/myAccount.cy.js | 68 +++++++++++++++++++++++++++ cypress/fixtures/api/discover.json | 55 ++++++++++++++++------ cypress/fixtures/api/traces.json | 30 ++++++------ cypress/fixtures/api/user-detail.json | 5 +- cypress/support/intercept.js | 11 +++++ 7 files changed, 217 insertions(+), 31 deletions(-) create mode 100644 cypress/e2e/accountInfo.cy.js create mode 100644 cypress/e2e/filesCompare.cy.js create mode 100644 cypress/e2e/myAccount.cy.js diff --git a/cypress/e2e/accountInfo.cy.js b/cypress/e2e/accountInfo.cy.js new file mode 100644 index 0000000..df8b1e4 --- /dev/null +++ b/cypress/e2e/accountInfo.cy.js @@ -0,0 +1,35 @@ +import { loginWithFixtures } from '../support/intercept'; + +describe('Account Info Modal', () => { + beforeEach(() => { + loginWithFixtures(); + cy.visit('/account-admin'); + cy.wait('@getUsers'); + }); + + it('double-click username opens info modal with user data', () => { + cy.get('.account-cell').first().dblclick(); + cy.get('#modal_container').should('be.visible'); + cy.get('#acct_info_user_name').should('exist'); + }); + + it('info modal shows Account Information header', () => { + cy.get('.account-cell').first().dblclick(); + cy.get('#modal_container').should('be.visible'); + cy.contains('Account Information').should('exist'); + }); + + it('info modal shows account visit info', () => { + cy.get('.account-cell').first().dblclick(); + cy.get('#modal_container').should('be.visible'); + cy.get('#account_visit_info').should('exist'); + cy.get('#account_visit_info').should('contain', 'Account:'); + }); + + it('info modal can be closed via X button', () => { + cy.get('.account-cell').first().dblclick(); + cy.get('#modal_container').should('be.visible'); + cy.get('img[alt="X"]').click(); + cy.get('#modal_container').should('not.exist'); + }); +}); diff --git a/cypress/e2e/filesCompare.cy.js b/cypress/e2e/filesCompare.cy.js new file mode 100644 index 0000000..fe19da4 --- /dev/null +++ b/cypress/e2e/filesCompare.cy.js @@ -0,0 +1,44 @@ +import { loginWithFixtures } from '../support/intercept'; + +describe('Files Page - COMPARE Tab', () => { + beforeEach(() => { + loginWithFixtures(); + cy.visit('/files'); + cy.wait('@getFiles'); + // Switch to COMPARE tab + cy.contains('li', 'COMPARE').click(); + }); + + it('shows Performance Comparison heading', () => { + cy.contains('h2', 'Performance Comparison').should('be.visible'); + }); + + it('shows two drag-and-drop slots', () => { + cy.get('#primaryDragCard').should('exist'); + cy.get('#secondaryDragCard').should('exist'); + }); + + it('drag slots show placeholder text', () => { + cy.get('#primaryDragCard').should('contain', 'Drag and drop a file here'); + cy.get('#secondaryDragCard').should('contain', 'Drag and drop a file here'); + }); + + it('Compare button is disabled when no files are dragged', () => { + cy.contains('button', 'Compare').should('be.disabled'); + }); + + it('shows sorting dropdown', () => { + cy.get('.p-dropdown').should('exist'); + }); + + it('grid cards display file names', () => { + cy.get('#compareGridCards').should('exist'); + cy.get('#compareGridCards li').should('have.length.greaterThan', 0); + }); + + it('clicking sorting dropdown shows sort options', () => { + cy.get('.p-dropdown').click(); + cy.get('.p-dropdown-items').should('be.visible'); + cy.contains('.p-dropdown-item', 'By File Name').should('exist'); + }); +}); diff --git a/cypress/e2e/myAccount.cy.js b/cypress/e2e/myAccount.cy.js new file mode 100644 index 0000000..5e022bf --- /dev/null +++ b/cypress/e2e/myAccount.cy.js @@ -0,0 +1,68 @@ +import { loginWithFixtures } from '../support/intercept'; + +describe('My Account Page', () => { + beforeEach(() => { + loginWithFixtures(); + cy.visit('/my-account'); + cy.wait('@getUserDetail'); + }); + + it('displays user name heading', () => { + cy.get('#general_acct_info_user_name').should('exist'); + cy.get('#general_acct_info_user_name').should('contain', 'Test Admin'); + }); + + it('shows Admin badge for admin user', () => { + cy.contains('Admin').should('exist'); + }); + + it('shows visit count info', () => { + cy.get('#general_account_visit_info').should('exist'); + cy.get('#general_account_visit_info').should('contain', 'Total visits'); + }); + + it('displays account username (read-only)', () => { + cy.contains('Test Admin').should('exist'); + }); + + it('shows Edit button for name field', () => { + cy.contains('button', 'Edit').should('exist'); + }); + + it('clicking Edit shows input field and Save/Cancel buttons', () => { + cy.contains('button', 'Edit').first().click(); + cy.get('#input_name_field').should('exist'); + cy.contains('button', 'Save').should('exist'); + cy.contains('button', 'Cancel').should('exist'); + }); + + it('clicking Cancel reverts name field to read-only', () => { + cy.contains('button', 'Edit').first().click(); + cy.get('#input_name_field').should('exist'); + cy.contains('button', 'Cancel').click(); + cy.get('#input_name_field').should('not.exist'); + }); + + it('shows Reset button for password field', () => { + cy.contains('button', 'Reset').should('exist'); + }); + + it('clicking Reset shows password input and Save/Cancel', () => { + cy.contains('button', 'Reset').click(); + cy.get('input[type="password"]').should('exist'); + cy.contains('button', 'Save').should('exist'); + cy.contains('button', 'Cancel').should('exist'); + }); + + it('clicking Cancel on password field hides the input', () => { + cy.contains('button', 'Reset').click(); + cy.get('input[type="password"]').should('exist'); + // The Cancel button for password is the second one + cy.get('.cancel-btn').click(); + cy.get('input[type="password"]').should('not.exist'); + }); + + it('shows Session section', () => { + cy.contains('Session').should('exist'); + }); +}); diff --git a/cypress/fixtures/api/discover.json b/cypress/fixtures/api/discover.json index d592293..d19c703 100644 --- a/cypress/fixtures/api/discover.json +++ b/cypress/fixtures/api/discover.json @@ -1,4 +1,44 @@ { + "process_map": { + "vertices": [ + { + "id": 0, + "type": "event", + "event_type": "start", + "label": "start" + }, + { + "id": 1, + "type": "activity", + "label": "Task A", + "freq": { "total": 150, "rel_freq": 1.0, "average": 150, "median": 150, "max": 150, "min": 150, "cases": 150 }, + "duration": { "total": 5400, "rel_duration": 0.6, "average": 36, "median": 30, "max": 60, "min": 10 } + }, + { + "id": 2, + "type": "activity", + "label": "Task B", + "freq": { "total": 120, "rel_freq": 0.8, "average": 120, "median": 120, "max": 120, "min": 120, "cases": 120 }, + "duration": { "total": 2700, "rel_duration": 0.3, "average": 22.5, "median": 20, "max": 45, "min": 5 } + }, + { + "id": 3, + "type": "event", + "event_type": "end", + "label": "end" + } + ], + "edges": [ + { "tail": 0, "head": 1, "freq": { "total": 150, "rel_freq": 1.0, "average": 150, "median": 150, "max": 150, "min": 150 }, "duration": null }, + { "tail": 1, "head": 2, "freq": { "total": 120, "rel_freq": 0.8, "average": 120, "median": 120, "max": 120, "min": 120 }, "duration": { "total": 1800, "rel_duration": 0.5, "average": 15, "median": 10, "max": 30, "min": 5 } }, + { "tail": 2, "head": 3, "freq": { "total": 120, "rel_freq": 0.8, "average": 120, "median": 120, "max": 120, "min": 120 }, "duration": null }, + { "tail": 1, "head": 3, "freq": { "total": 30, "rel_freq": 0.2, "average": 30, "median": 30, "max": 30, "min": 30 }, "duration": null } + ] + }, + "bpmn": { + "vertices": [], + "edges": [] + }, "stats": { "cases": { "count": 150, "total": 200, "ratio": 0.75 }, "traces": { "count": 45, "total": 60, "ratio": 0.75 }, @@ -13,18 +53,5 @@ "median": 86400 } }, - "graph": { - "nodes": [ - { "id": "start", "label": "Start", "type": "start" }, - { "id": "task_a", "label": "Task A", "type": "task" }, - { "id": "task_b", "label": "Task B", "type": "task" }, - { "id": "end", "label": "End", "type": "end" } - ], - "edges": [ - { "source": "start", "target": "task_a", "count": 150 }, - { "source": "task_a", "target": "task_b", "count": 120 }, - { "source": "task_b", "target": "end", "count": 120 }, - { "source": "task_a", "target": "end", "count": 30 } - ] - } + "insights": [] } diff --git a/cypress/fixtures/api/traces.json b/cypress/fixtures/api/traces.json index 0c6ae51..a2c5f79 100644 --- a/cypress/fixtures/api/traces.json +++ b/cypress/fixtures/api/traces.json @@ -1,16 +1,14 @@ -{ - "traces": [ - { - "id": "trace-001", - "case_id": "CASE-001", - "count": 50, - "activities": ["Task A", "Task B", "End"] - }, - { - "id": "trace-002", - "case_id": "CASE-002", - "count": 30, - "activities": ["Task A", "End"] - } - ] -} +[ + { + "id": 1, + "case_id": "CASE-001", + "count": 50, + "activities": ["Task A", "Task B", "End"] + }, + { + "id": 2, + "case_id": "CASE-002", + "count": 30, + "activities": ["Task A", "End"] + } +] diff --git a/cypress/fixtures/api/user-detail.json b/cypress/fixtures/api/user-detail.json index ec14689..296f3fb 100644 --- a/cypress/fixtures/api/user-detail.json +++ b/cypress/fixtures/api/user-detail.json @@ -7,5 +7,8 @@ "has_data": true, "roles": [ { "code": "admin", "name": "Administrator" } - ] + ], + "detail": { + "visits": 42 + } } diff --git a/cypress/support/intercept.js b/cypress/support/intercept.js index 4d2a92c..b6f4bad 100644 --- a/cypress/support/intercept.js +++ b/cypress/support/intercept.js @@ -86,6 +86,17 @@ export function setupApiIntercepts() { fixture: 'api/traces.json', }).as('getFilterTraces'); + // Trace detail (must be after traces list intercepts) + cy.intercept('GET', /\/api\/logs\/.*\/traces\/\d+/, { + statusCode: 200, + body: { task_seq: [], events: [] }, + }).as('getTraceDetail'); + + cy.intercept('GET', /\/api\/filters\/.*\/traces\/\d+/, { + statusCode: 200, + body: { task_seq: [], events: [] }, + }).as('getFilterTraceDetail'); + // Temp filters cy.intercept('GET', '/api/temp-filters/*/discover', { fixture: 'api/discover.json',