diff --git a/cypress/e2e/edgeCases.cy.js b/cypress/e2e/edgeCases.cy.js new file mode 100644 index 0000000..701f59a --- /dev/null +++ b/cypress/e2e/edgeCases.cy.js @@ -0,0 +1,89 @@ +import { loginWithFixtures } from '../support/intercept'; + +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'); + 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'); + }); + + 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'); + }); + }); + + describe('Authentication guard', () => { + it('unauthenticated user is redirected to login', () => { + // No loginWithFixtures - not logged in + cy.visit('/files'); + cy.url().should('include', '/login'); + }); + + it('unauthenticated user cannot access account-admin', () => { + cy.visit('/account-admin'); + cy.url().should('include', '/login'); + }); + + it('unauthenticated user cannot access my-account', () => { + cy.visit('/my-account'); + cy.url().should('include', '/login'); + }); + }); + + describe('Login validation', () => { + it('shows error on failed login', () => { + cy.intercept('POST', '/api/oauth/token', { + statusCode: 401, + body: { detail: 'Invalid credentials' }, + }).as('failedLogin'); + 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.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'); + }); + + it('confirm stays disabled with only name field filled', () => { + cy.get('#input_name_field').type('New User'); + cy.get('.confirm-btn').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'); + }); + }); +}); diff --git a/cypress/e2e/sweetAlertModals.cy.js b/cypress/e2e/sweetAlertModals.cy.js new file mode 100644 index 0000000..4abaacb --- /dev/null +++ b/cypress/e2e/sweetAlertModals.cy.js @@ -0,0 +1,111 @@ +import { loginWithFixtures } from '../support/intercept'; + +describe('SweetAlert2 Modals', () => { + describe('File Context Menu - Rename', () => { + beforeEach(() => { + loginWithFixtures(); + cy.visit('/files'); + cy.wait('@getFiles'); + }); + + it('right-click on table row shows context menu with Rename', () => { + cy.get('table tbody tr').first().rightclick(); + cy.contains('Rename').should('be.visible'); + }); + + it('right-click context menu shows Download option', () => { + cy.get('table tbody tr').first().rightclick(); + cy.contains('Download').should('be.visible'); + }); + + it('right-click context menu shows Delete option', () => { + cy.get('table tbody tr').first().rightclick(); + cy.contains('Delete').should('be.visible'); + }); + + it('clicking Rename opens SweetAlert rename dialog', () => { + cy.get('table tbody tr').first().rightclick(); + cy.contains('Rename').click(); + // SweetAlert popup should appear with RENAME title + cy.get('.swal2-popup').should('be.visible'); + cy.get('.swal2-title').should('contain', 'RENAME'); + cy.get('.swal2-input').should('exist'); + }); + + it('rename dialog has pre-filled file name', () => { + cy.get('table tbody tr').first().rightclick(); + cy.contains('Rename').click(); + cy.get('.swal2-input').should('not.have.value', ''); + }); + + it('rename dialog can be cancelled', () => { + cy.get('table tbody tr').first().rightclick(); + cy.contains('Rename').click(); + cy.get('.swal2-popup').should('be.visible'); + cy.get('.swal2-cancel').click(); + cy.get('.swal2-popup').should('not.exist'); + }); + + it('clicking Delete opens SweetAlert delete confirmation', () => { + cy.get('table tbody tr').first().rightclick(); + cy.contains('Delete').click(); + // SweetAlert popup should appear with CONFIRM DELETION + cy.get('.swal2-popup').should('be.visible'); + cy.get('.swal2-title').should('contain', 'CONFIRM DELETION'); + }); + + it('delete confirmation shows file name', () => { + cy.get('table tbody tr').first().rightclick(); + cy.contains('Delete').click(); + cy.get('.swal2-popup').should('be.visible'); + cy.get('.swal2-html-container').should('contain', 'delete'); + }); + + it('delete confirmation can be cancelled', () => { + cy.get('table tbody tr').first().rightclick(); + cy.contains('Delete').click(); + cy.get('.swal2-popup').should('be.visible'); + cy.get('.swal2-cancel').click(); + cy.get('.swal2-popup').should('not.exist'); + }); + }); + + describe('File Context Menu on Grid View', () => { + beforeEach(() => { + loginWithFixtures(); + cy.visit('/files'); + cy.wait('@getFiles'); + // Switch to grid view + cy.get('svg').parent('li.cursor-pointer').last().click(); + }); + + it('right-click on grid card shows context menu', () => { + cy.get('li[title]').first().rightclick(); + cy.contains('Rename').should('be.visible'); + cy.contains('Delete').should('be.visible'); + }); + + it('grid card rename opens SweetAlert dialog', () => { + cy.get('li[title]').first().rightclick(); + cy.contains('Rename').click(); + cy.get('.swal2-popup').should('be.visible'); + cy.get('.swal2-title').should('contain', 'RENAME'); + }); + }); + + describe('Account Delete Confirmation', () => { + beforeEach(() => { + loginWithFixtures(); + cy.visit('/account-admin'); + cy.wait('@getUsers'); + }); + + 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 + }); + }); +}); diff --git a/cypress/support/intercept.js b/cypress/support/intercept.js index b6f4bad..04f5d7d 100644 --- a/cypress/support/intercept.js +++ b/cypress/support/intercept.js @@ -128,6 +128,38 @@ export function setupApiIntercepts() { body: {}, }).as('getFilterCheckParams'); + // 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,