Add Cypress E2E tests with fixture-based API mocking for UI regression protection

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-05 20:10:04 +08:00
parent 676b70caa0
commit 733bfd7509
13 changed files with 480 additions and 1 deletions

View File

@@ -0,0 +1,29 @@
import { loginWithFixtures } from '../support/intercept';
describe('Account Management', () => {
beforeEach(() => {
loginWithFixtures();
});
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');
cy.contains('Bob Chen').should('exist');
});
it('shows active/inactive status badges', () => {
cy.visit('/account-admin');
cy.wait('@getUsers');
// 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');
});
});

55
cypress/e2e/files.cy.js Normal file
View File

@@ -0,0 +1,55 @@
import { loginWithFixtures } from '../support/intercept';
describe('Files Page', () => {
beforeEach(() => {
loginWithFixtures();
cy.visit('/files');
});
it('displays the file list after login', () => {
cy.wait('@getFiles');
cy.contains('h2', 'All Files').should('exist');
// Should display file names from fixture
cy.contains('sample-process.xes').should('exist');
cy.contains('filtered-sample').should('exist');
cy.contains('production-log.csv').should('exist');
});
it('shows Recently Used section', () => {
cy.wait('@getFiles');
cy.contains('h2', 'Recently Used').should('exist');
});
it('switches to DISCOVER tab', () => {
cy.wait('@getFiles');
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('.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.get('#import_btn').should('contain', 'Import');
});
it('can switch between list and grid view', () => {
cy.wait('@getFiles');
// DataTable (list view) should be visible by default
cy.get('table').should('exist');
});
it('double-click file navigates to discover page', () => {
cy.wait('@getFiles');
// 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();
cy.url().should('include', '/discover');
});
});

71
cypress/e2e/login.cy.js Normal file
View File

@@ -0,0 +1,71 @@
import { setupApiIntercepts, loginWithFixtures } from '../support/intercept';
describe('Login Flow', () => {
beforeEach(() => {
setupApiIntercepts();
});
it('renders the login form', () => {
cy.visit('/login');
cy.get('h2').should('contain', 'LOGIN');
cy.get('#account').should('exist');
cy.get('#password').should('exist');
cy.get('#login_btn_main_btn').should('be.disabled');
});
it('login button is disabled when fields are empty', () => {
cy.visit('/login');
cy.get('#login_btn_main_btn').should('be.disabled');
// Only username filled — still disabled
cy.get('#account').type('testuser');
cy.get('#login_btn_main_btn').should('be.disabled');
});
it('login button enables when both fields are filled', () => {
cy.visit('/login');
cy.get('#account').type('testadmin');
cy.get('#password').type('password123');
cy.get('#login_btn_main_btn').should('not.be.disabled');
});
it('successful login redirects to /files', () => {
cy.visit('/login');
cy.get('#account').type('testadmin');
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');
cy.visit('/login');
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');
});
it('toggles password visibility', () => {
cy.visit('/login');
cy.get('#password').type('secret123');
cy.get('#password').should('have.attr', 'type', 'password');
// Click the eye icon to show password
cy.get('label[for="passwordt"] span.cursor-pointer').click();
cy.get('#password').should('have.attr', 'type', 'text');
// Click again to hide
cy.get('label[for="passwordt"] span.cursor-pointer').click();
cy.get('#password').should('have.attr', 'type', 'password');
});
});

View File

@@ -0,0 +1,57 @@
import { loginWithFixtures, setupApiIntercepts } from '../support/intercept';
describe('Navigation and Routing', () => {
it('redirects / to /files when logged in', () => {
loginWithFixtures();
cy.visit('/');
cy.url().should('include', '/files');
});
it('shows 404 page for unknown routes', () => {
loginWithFixtures();
cy.visit('/nonexistent-page');
cy.contains('404').should('exist');
});
it('navbar shows correct view name', () => {
loginWithFixtures();
cy.visit('/files');
cy.wait('@getFiles');
cy.get('#nav_bar').should('exist');
cy.get('#nav_bar h2').should('contain', 'FILES');
});
it('navbar shows back arrow on non-files pages', () => {
loginWithFixtures();
cy.visit('/discover/log/1/map');
// Back arrow should be visible on discover pages
cy.get('#backPage').should('exist');
});
it('navbar tabs are clickable on discover page', () => {
loginWithFixtures();
cy.visit('/discover/log/1/map');
// Discover navbar should show MAP, CONFORMANCE, PERFORMANCE tabs
cy.contains('.nav-item', 'MAP').should('exist');
cy.contains('.nav-item', 'CONFORMANCE').should('exist');
cy.contains('.nav-item', 'PERFORMANCE').should('exist');
// Click CONFORMANCE tab
cy.contains('.nav-item', 'CONFORMANCE').click();
cy.url().should('include', '/conformance');
// Click PERFORMANCE tab
cy.contains('.nav-item', 'PERFORMANCE').click();
cy.url().should('include', '/performance');
// Click MAP tab to go back
cy.contains('.nav-item', 'MAP').click();
cy.url().should('include', '/map');
});
it('login page is accessible at /login', () => {
setupApiIntercepts();
cy.visit('/login');
cy.get('h2').should('contain', 'LOGIN');
});
});