Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
116 changes: 33 additions & 83 deletions README.md
Original file line number Diff line number Diff line change
@@ -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 <your-forked-repo-url>
```
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
33 changes: 32 additions & 1 deletion cypress/e2e/login.cy.js
Original file line number Diff line number Diff line change
@@ -1,2 +1,33 @@
describe('Login Component', () => {
})
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', '');
});
});
47 changes: 33 additions & 14 deletions cypress/support/commands.js
Original file line number Diff line number Diff line change
Expand Up @@ -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) => { ... })
// ✅ 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);
});
53 changes: 32 additions & 21 deletions src/components/LoginForm.css
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
box-shadow: 0 0 0 2px rgba(37, 99, 235, 0.3);
}
12 changes: 9 additions & 3 deletions src/components/LoginForm.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,18 @@ function LoginForm({ onLogin }) {
}));
};

const handleSubmit = (e) => {
e.preventDefault();
onLogin(formData);
}


return (
<div className="login-form-container">
<form className="login-form">
<form className="login-form" onSubmit={handleSubmit}>
<h2>Login</h2>
<div className="form-group">
<label htmlFor="name">Name:</label>
<label htmlFor="name" className="form-label">Name</label>
<input
type="text"
id="name"
Expand All @@ -31,7 +37,7 @@ function LoginForm({ onLogin }) {
/>
</div>
<div className="form-group">
<label htmlFor="password">Password:</label>
<label htmlFor="password">Password</label>
<input
type="password"
id="password"
Expand Down
Loading