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
108 changes: 82 additions & 26 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,35 @@
# zipBoard Junior Position Test Project

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.
This is a test repository for the zipBoard junior position application process. This project demonstrates a secure React login form with comprehensive testing.

## Important Note

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.

## Challenge Description

This challenge involved fixing a login redirect bug and implementing comprehensive test coverage. The solution includes:

1. Enhanced form validation and security features
2. Comprehensive Cypress test suite
3. Accessibility improvements (WCAG 2.1)

### Implementation Details

- Added form validation with error handling
- Added ARIA attributes for accessibility
- Created comprehensive test suite covering:
- Authentication flow
- Form validation
- Accessibility compliance
- Session management
- Error handling
- Fixed responsive design issues:
- Added box-sizing: border-box for proper sizing
- Implemented responsive form container with margins
- Fixed input field overflow issues
- Ensured mobile-friendly layout

## Required Technologies

To run this project locally, you need to have the following installed:
Expand All @@ -14,6 +38,15 @@ To run this project locally, you need to have the following installed:
- npm (comes with Node.js)
- Git

## Test Credentials

For testing purposes, use these credentials:

```
Username: zipboard_test
Password: ZipBoard@2025
```

## Getting Started

1. Fork this repository to your own account
Expand All @@ -33,51 +66,61 @@ To run this project locally, you need to have the following installed:

## Testing with Cypress

This project uses Cypress for end-to-end testing. To run the tests:
The project includes a comprehensive test suite covering authentication, security, and accessibility:

### Running Tests

1. Make sure the development server is running (`npm start`)
2. In a new terminal, you can run Cypress in two ways:
1. Start the development server:

### Open Cypress Test Runner (Interactive Mode)
```bash
npm run cypress:open
npm start
```
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

### Run Tests in Headless Mode
2. Run tests in interactive mode:

```bash
npm run cypress:run
npm run cypress:open
```
This will run all tests in the terminal without opening the UI.

### Run Tests with Dev Server
3. Run tests in headless mode:
```bash
npm run test:e2e
npm run cypress:run
```
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

### Test Coverage

The test suite includes:

- Authentication flow validation
- Login form elements presence
- Input field behavior
- Successful login flow
- Invalid credentials handling
- Logout functionality
- Security measures
- Session termination
- Accessibility compliance
- ARIA attributes
- Required field indicators
- Error message announcements
- Form validation
- Required fields
- Error state handling

## Project Structure

```
├── src/
│ ├── components/
│ │ ├── LoginForm.js
│ │ ├── LoginForm.js # Enhanced login form with validation
│ │ ├── LoginForm.css
│ │ ├── Welcome.js
│ │ ├── Welcome.js # Welcome screen component
│ │ └── Welcome.css
│ ├── App.js
│ ├── App.js # Main app with auth logic
│ └── App.css
├── cypress/
│ ├── e2e/
│ │ └── login.cy.js
│ │ └── login.cy.js # Comprehensive test suite
│ └── support/
│ ├── commands.js
│ └── e2e.js
Expand All @@ -91,7 +134,20 @@ This project uses Cypress for end-to-end testing. To run the tests:
- `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
- `npm run test:e2e` - Runs Cypress tests with dev server

## Security Features

- Form validation with error handling
- Secure session management
- Protected routes after logout

## Accessibility

- WCAG 2.1 compliant
- Proper ARIA attributes
- Keyboard navigation support
- Clear error messaging

## License

Expand Down
10 changes: 5 additions & 5 deletions cypress.config.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
const { defineConfig } = require('cypress')
const { defineConfig } = require("cypress");

module.exports = defineConfig({
e2e: {
baseUrl: 'http://localhost:3000',
supportFile: 'cypress/support/e2e.js',
specPattern: 'cypress/e2e/**/*.cy.{js,jsx,ts,tsx}',
baseUrl: "http://localhost:3000",
supportFile: "cypress/support/e2e.js",
specPattern: "cypress/e2e/**/*.cy.{js,jsx,ts,tsx}",
setupNodeEvents(on, config) {
// implement node event listeners here
},
},
})
});
94 changes: 92 additions & 2 deletions cypress/e2e/login.cy.js
Original file line number Diff line number Diff line change
@@ -1,2 +1,92 @@
describe('Login Component', () => {
})
/* eslint-disable no-undef */

describe("Login Form Authentication Flow", () => {
beforeEach(() => {
// Visit the app before each test
cy.visit("http://localhost:3000");
});

const validCredentials = {
username: "zipboard_test",
password: "ZipBoard@2025",
};

it("validates presence of essential authentication interface elements", () => {
cy.get(".login-form").should("be.visible");
cy.get('input[name="name"]').should("be.visible");
cy.get('input[name="password"]').should("be.visible");
cy.get('button[type="submit"]').should("be.visible");
});

it("ensures dynamic field updates reflect user input correctly", () => {
cy.get('input[name="name"]')
.type(validCredentials.username)
.should("have.value", validCredentials.username);
cy.get('input[name="password"]')
.type(validCredentials.password)
.should("have.value", validCredentials.password);
});

it("enforces mandatory field validation protocol", () => {
cy.get('button[type="submit"]').click();
cy.get('input[name="name"]').should("have.attr", "required");
cy.get('input[name="password"]').should("have.attr", "required");
});

it("handles invalid login attempts appropriately", () => {
// Try with wrong credentials
cy.get('input[name="name"]').type("wronguser");
cy.get('input[name="password"]').type("wrongpass");
cy.get('button[type="submit"]').click();

// Should show error message
cy.get(".error-message")
.should("be.visible")
.and("contain", "Invalid username or password");

// Form should still be accessible
cy.get(".login-form").should("be.visible");
});

it("executes successful login flow correctly", () => {
cy.get('input[name="name"]').type(validCredentials.username);
cy.get('input[name="password"]').type(validCredentials.password);
cy.get('button[type="submit"]').click();

cy.get(".welcome-container").should("be.visible");
cy.get(".welcome-card h1").should(
"contain",
`Welcome, ${validCredentials.username}!`
);
});

it("implements secure session termination workflow", () => {
// Login first
cy.get('input[name="name"]').type(validCredentials.username);
cy.get('input[name="password"]').type(validCredentials.password);
cy.get('button[type="submit"]').click();

// Logout
cy.get(".logout-button").click();

// Verify back at login form
cy.get(".login-form").should("be.visible");
});

it("maintains basic accessibility requirements", () => {
cy.get('input[name="name"]').should("have.attr", "aria-required", "true");
cy.get('input[name="password"]').should(
"have.attr",
"aria-required",
"true"
);

// Trigger error message by submitting with wrong credentials
cy.get('input[name="name"]').type("wrong");
cy.get('input[name="password"]').type("wrong");
cy.get('button[type="submit"]').click();

// Now check error message accessibility
cy.get(".error-message").should("have.attr", "role", "alert");
});
});
Loading