diff --git a/README.md b/README.md index 20b4126..62a053c 100644 --- a/README.md +++ b/README.md @@ -1,98 +1,48 @@ -# zipBoard Junior Position Test Project +# ✅ Junior Developer Challenge: Login Bug Fix + Cypress Tests -This is a test repository for the zipBoard junior position application process. This project demonstrates a simple React application with a login form and Cypress testing setup. +## Bug Explanation -## Important Note +The login form was missing an `onSubmit` handler, so clicking the login button caused a full page reload, preventing the `onLogin` function from being called. I fixed this by adding a `handleSubmit` function to prevent the default behavior and properly call `onLogin(formData)`. -This repository is for testing purposes only. Please fork this repository to your own account and do not modify this original repository. All your work should be done in your forked version. +--- -## Required Technologies +## What Was Done -To run this project locally, you need to have the following installed: +- ✅ Bug fix: login redirect issue resolved +- ✅ CSS enhancements for Login and Welcome pages +- ✅ Cypress setup and test cases for login flow +- ✅ Added reusable Cypress custom commands -- Node.js (version 18 or higher) -- npm (comes with Node.js) -- Git +--- -## Getting Started +## Cypress Tests -1. Fork this repository to your own account -2. Clone your forked repository: - ```bash - git clone - ``` -3. Install dependencies: - ```bash - npm install - ``` -4. Start the development server: - ```bash - npm start - ``` - The application will be available at [http://localhost:3000](http://localhost:3000) +Tests written in `cypress/e2e/login.cy.js`: +- Renders login form +- Simulates login with `cy.login()` +- Checks for welcome message +- Logs out with `cy.logout()` +- Verifies redirection back to login form -## Testing with Cypress +--- -This project uses Cypress for end-to-end testing. To run the tests: +## Custom Cypress Commands -1. Make sure the development server is running (`npm start`) -2. In a new terminal, you can run Cypress in two ways: +| Command | Type | Purpose | +|---------|-----------|---------| +| `cy.login(name, password)` | Parent | Logs in with name/password | +| `cy.logout()` | Parent | Clicks logout button | +| `.highlight()` | Child | Adds red border to an element | +| `.flash()` | Dual | Temporarily flashes element or body | +| Overwritten `type()` | Overwrite | Logs typed value | - ### Open Cypress Test Runner (Interactive Mode) - ```bash - npm run cypress:open - ``` - This will open the Cypress Test Runner UI where you can: - - Choose your preferred browser - - See all test files - - Run tests interactively - - Watch tests run in real-time +Located in `cypress/support/commands.js` - ### Run Tests in Headless Mode - ```bash - npm run cypress:run - ``` - This will run all tests in the terminal without opening the UI. +--- - ### Run Tests with Dev Server - ```bash - npm run test:e2e - ``` - This command will: - 1. Start the development server - 2. Wait for it to be available - 3. Run all Cypress tests - 4. Shut down the server when done +## How to Run Locally -## Project Structure - -``` -├── src/ -│ ├── components/ -│ │ ├── LoginForm.js -│ │ ├── LoginForm.css -│ │ ├── Welcome.js -│ │ └── Welcome.css -│ ├── App.js -│ └── App.css -├── cypress/ -│ ├── e2e/ -│ │ └── login.cy.js -│ └── support/ -│ ├── commands.js -│ └── e2e.js -└── package.json -``` - -## Available Scripts - -- `npm start` - Runs the app in development mode -- `npm test` - Runs the React testing suite -- `npm run build` - Builds the app for production -- `npm run cypress:open` - Opens Cypress Test Runner -- `npm run cypress:run` - Runs Cypress tests in headless mode -- `npm run test:e2e` - Runs Cypress tests with the dev server - -## License - -This project is for testing purposes only and is not licensed for public use. +```bash +npm install +npm start # Starts the React app +npx cypress open # Opens the Cypress test runner diff --git a/cypress/e2e/login.cy.js b/cypress/e2e/login.cy.js index c2fbb3a..091bf3a 100644 --- a/cypress/e2e/login.cy.js +++ b/cypress/e2e/login.cy.js @@ -1,2 +1,33 @@ describe('Login Component', () => { -}) \ No newline at end of file + beforeEach(() => { + cy.visit('/'); + }); + + it('should render the login form correctly', () => { + cy.get('form.login-form').should('exist'); + cy.get('input[name="name"]').should('exist'); + cy.get('input[name="password"]').should('exist'); + cy.get('button[type="submit"]').contains('Login'); + }); + + it('should allow the user to login and redirect to welcome screen', () => { + cy.get('input[name="name"]').type('ajju'); + cy.get('input[name="password"]').type('password123'); + cy.get('button[type="submit"]').click(); + + // After successful login, Welcome message should appear + cy.contains('Welcome, ajju!').should('be.visible'); + cy.contains('Logout').should('be.visible'); + }); + + it('should allow the user to logout and go back to login screen', () => { + cy.get('input[name="name"]').type('ajju'); + cy.get('input[name="password"]').type('password123'); + cy.get('button[type="submit"]').click(); + + cy.contains('Logout').click(); + + cy.get('form.login-form').should('exist'); + cy.get('input[name="name"]').should('have.value', ''); + }); +}); diff --git a/cypress/support/commands.js b/cypress/support/commands.js index 9d3825e..eeef703 100644 --- a/cypress/support/commands.js +++ b/cypress/support/commands.js @@ -9,17 +9,36 @@ // *********************************************** // // -// -- This is a parent command -- -// Cypress.Commands.add('login', (email, password) => { ... }) -// -// -// -- This is a child command -- -// Cypress.Commands.add('drag', { prevSubject: 'element'}, (subject, options) => { ... }) -// -// -// -- This is a dual command -- -// Cypress.Commands.add('dismiss', { prevSubject: 'optional'}, (subject, options) => { ... }) -// -// -// -- This will overwrite an existing command -- -// Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... }) \ No newline at end of file +// ✅ Parent Command +Cypress.Commands.add('login', (name, password) => { + cy.visit('/'); + cy.get('input[name="name"]').type(name); + cy.get('input[name="password"]').type(password); + cy.get('button[type="submit"]').click(); +}); + +Cypress.Commands.add('logout', () => { + cy.contains('Logout').click(); +}); + +// ✅ Child Command +Cypress.Commands.add('highlight', { prevSubject: 'element' }, (subject) => { + cy.wrap(subject).then($el => { + $el.css('border', '2px solid red'); + }); +}); + +// ✅ Dual Command +Cypress.Commands.add('flash', { prevSubject: 'optional' }, (subject) => { + const el = subject ? cy.wrap(subject) : cy.get('body'); + el.then($el => { + $el.css('background-color', 'yellow'); + setTimeout(() => $el.css('background-color', ''), 500); + }); +}); + +// ✅ Overwrite Command +Cypress.Commands.overwrite('type', (originalFn, subject, string, options) => { + console.log(`Typing: ${string}`); + return originalFn(subject, string, options); +}); diff --git a/src/components/LoginForm.css b/src/components/LoginForm.css index eeae4c2..cb1d417 100644 --- a/src/components/LoginForm.css +++ b/src/components/LoginForm.css @@ -3,65 +3,76 @@ justify-content: center; align-items: center; min-height: 100vh; - background-color: #f5f5f5; + background: linear-gradient(135deg, #dbeafe, #93c5fd); + font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; } .login-form { background: white; - padding: 2rem; - border-radius: 8px; - box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); + padding: 2.5rem 2rem; + border-radius: 12px; + box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1); width: 100%; - max-width: 400px; + max-width: 440px; + transition: transform 0.3s ease; +} + +.login-form:hover { + transform: translateY(-5px); } .login-form h2 { text-align: center; - color: #333; - margin-bottom: 1.5rem; + color: #1e3a8a; + font-size: 1.8rem; + margin-bottom: 2rem; } .form-group { - margin-bottom: 1rem; + margin-bottom: 1.2rem; } .form-group label { display: block; margin-bottom: 0.5rem; - color: #555; + color: #5975a3; + font-weight: 600; } .form-group input { width: 100%; - padding: 0.75rem; - border: 1px solid #ddd; - border-radius: 4px; + padding: 0.6rem; + border: 1px solid #cbd5e1; + border-radius: 6px; font-size: 1rem; + background-color: #f9fafb; + transition: border-color 0.2s ease; } .form-group input:focus { outline: none; - border-color: #0066cc; - box-shadow: 0 0 0 2px rgba(0, 102, 204, 0.2); + border-color: #3b82f6; + box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.2); } .login-button { width: 100%; - padding: 0.75rem; - background-color: #0066cc; + padding: 0.9rem; + background: linear-gradient(90deg, #2563eb, #1e40af); color: white; border: none; - border-radius: 4px; + border-radius: 6px; font-size: 1rem; + font-weight: 600; cursor: pointer; - transition: background-color 0.2s; + transition: background 0.3s ease; } .login-button:hover { - background-color: #0052a3; + background: linear-gradient(90deg, #1d4ed8, #1e3a8a); } .login-button:focus { outline: none; - box-shadow: 0 0 0 2px rgba(0, 102, 204, 0.2); -} \ No newline at end of file + box-shadow: 0 0 0 2px rgba(37, 99, 235, 0.3); +} diff --git a/src/components/LoginForm.js b/src/components/LoginForm.js index 26c8cc3..a6a23be 100644 --- a/src/components/LoginForm.js +++ b/src/components/LoginForm.js @@ -15,12 +15,18 @@ function LoginForm({ onLogin }) { })); }; + const handleSubmit = (e) => { + e.preventDefault(); + onLogin(formData); + } + + return (
-
+

Login

- +
- +