First off, thank you for considering contributing to this project! It's people like you that make this framework better for everyone.
- Code of Conduct
- Getting Started
- How Can I Contribute?
- Coding Standards
- Naming Conventions
- Project Structure Guidelines
- Commit Message Guidelines
- Pull Request Process
- Style Guide
- Testing Guidelines
We are committed to providing a welcoming and inspiring community for all. Please be respectful and constructive in your interactions.
Positive behavior includes:
- Using welcoming and inclusive language
- Being respectful of differing viewpoints
- Gracefully accepting constructive criticism
- Focusing on what is best for the community
- Showing empathy towards other community members
Unacceptable behavior includes:
- Harassment, trolling, or derogatory comments
- Publishing others' private information
- Any conduct which could reasonably be considered inappropriate in a professional setting
-
Fork the repository on GitHub
-
Clone your fork locally:
git clone https://github.com/YOUR-USERNAME/ProtractorPageObjectModel.git cd ProtractorPageObjectModel -
Add upstream remote:
git remote add upstream https://github.com/lkumarra/ProtractorPageObjectModel.git
-
Install dependencies:
npm install
-
Create a branch for your changes:
git checkout -b feature/your-feature-name # or git checkout -b fix/your-bug-fix -
Keep your fork updated:
git fetch upstream git merge upstream/master
Before creating bug reports, please check the issue list to avoid duplicates. When creating a bug report, include:
- Clear title and description
- Steps to reproduce the problem
- Expected behavior vs actual behavior
- Screenshots if applicable
- Environment details: OS, Node version, Chrome version
- Error messages and stack traces
Bug Report Template:
## Bug Description
A clear and concise description of the bug.
## Steps to Reproduce
1. Go to '...'
2. Click on '...'
3. Scroll down to '...'
4. See error
## Expected Behavior
What you expected to happen.
## Actual Behavior
What actually happened.
## Environment
- OS: [e.g., macOS 12.0, Windows 11]
- Node Version: [e.g., 16.14.0]
- Chrome Version: [e.g., 108.0.5359.124]
- Framework Version: [e.g., 1.0.0]
## Additional Context
Add any other context about the problem here.Enhancement suggestions are tracked as GitHub issues. When creating an enhancement suggestion:
- Use a clear and descriptive title
- Provide a detailed description of the suggested enhancement
- Explain why this enhancement would be useful
- List alternatives you've considered
Pull requests are the best way to propose changes to the codebase:
- Follow all instructions in the PR template
- Follow the coding standards
- Include appropriate test cases
- Update documentation as needed
- Ensure all tests pass
Always use explicit types. Avoid any unless absolutely necessary.
// ✅ Good
function calculateTotal(price: number, quantity: number): number {
return price * quantity;
}
// ❌ Bad
function calculateTotal(price: any, quantity: any): any {
return price * quantity;
}Use interfaces for defining object structures:
// ✅ Good
export interface ICustomerData {
customerId: string;
customerName: string;
customerEmail: string;
dateOfBirth: Date;
}
// ❌ Bad - using 'any' or no type
let customer: any = { ... };Prefer async/await over promises:
// ✅ Good
async function loginUser(username: string, password: string): Promise<void> {
await usernameField.sendKeys(username);
await passwordField.sendKeys(password);
await loginButton.click();
}
// ❌ Bad
function loginUser(username: string, password: string) {
return usernameField.sendKeys(username).then(() => {
return passwordField.sendKeys(password);
}).then(() => {
return loginButton.click();
});
}Always handle errors appropriately:
// ✅ Good
async function performAction(): Promise<void> {
try {
await someAsyncOperation();
logger.info('Operation completed successfully');
} catch (error) {
logger.error(`Operation failed: ${error.message}`);
throw new CustomException('Failed to perform action', error);
}
}Use UPPER_SNAKE_CASE for constants:
// ✅ Good
const MAX_RETRY_ATTEMPTS = 3;
const DEFAULT_TIMEOUT = 5000;
const BASE_URL = 'https://demo.guru99.com';
// ❌ Bad
const maxRetryAttempts = 3;
const defaulttimeout = 5000;- Indentation: Use 4 spaces (no tabs)
- Line Length: Maximum 120 characters
- Semicolons: Always use semicolons
- Quotes: Use single quotes for strings
- Braces: Opening brace on same line
// ✅ Good
export class LoginPage extends Page {
constructor() {
super();
this.logger = LogUtils.getLogger();
}
public async login(username: string, password: string): Promise<void> {
await this.enterUsername(username);
await this.enterPassword(password);
await this.clickLoginButton();
}
}| Type | Convention | Example |
|---|---|---|
| Page Objects | PascalCase + Page suffix | LoginPage.ts |
| Locators | PascalCase + Locators suffix | LoginPageLocators.ts |
| Interfaces | PascalCase with 'I' prefix | ILoginPage.ts |
| Test Cases | PascalCase + Test suffix | LoginPageTest.ts |
| Utilities | PascalCase + Util suffix | TestUtil.ts |
| Configuration | PascalCase | Config.ts |
| Data Files | PascalCase | Data.ts |
Use kebab-case (lowercase with hyphens) for folder names following TypeScript/JavaScript industry standards:
✅ Good:
- pages/
- test-cases/
- test-data/
- log-manager/
❌ Bad (old PascalCase convention):
- Pages/
- TestCases/
- TestData/
- LogManager/
Note: This project has been refactored to follow modern kebab-case convention for all folders.
| Type | Convention | Example |
|---|---|---|
| Variables | camelCase | userName, customerId |
| Constants | UPPER_SNAKE_CASE | MAX_TIMEOUT, BASE_URL |
| Private Properties | camelCase with underscore | _locator, _logger |
| Interfaces | PascalCase with 'I' prefix | ILoginPage, ICustomer |
| Classes | PascalCase | LoginPage, TestUtil |
| Methods | camelCase | clickLoginButton(), enterUsername() |
| Boolean Variables | is/has/should prefix | isVisible, hasError, shouldRetry |
Use descriptive, action-oriented names:
// ✅ Good
async clickLoginButton(): Promise<void>
async enterUsername(username: string): Promise<void>
async verifyHomePageDisplayed(): Promise<boolean>
async waitForElementVisible(element: ElementFinder): Promise<void>
// ❌ Bad
async click(): Promise<void>
async enter(text: string): Promise<void>
async verify(): Promise<boolean>
async wait(el: any): Promise<void>Projects/Guru99BankTestAutomation/
├── config/ # Configuration files
├── exceptions/ # Custom exception classes
├── exports/ # Barrel exports for modules
├── interfaces/ # TypeScript interfaces
├── log-manager/ # Logging configuration and utilities
├── pages/
│ ├── actions/ # Page action implementations
│ ├── base/ # Base page class
│ └── locators/ # Element locators (separate from actions)
├── suites/ # Test suite definitions
├── test-cases/ # Test specifications
├── test-data/ # Test data (JSON, Excel, etc.)
├── test-reports/ # Generated test reports
└── utils/ # Utility functions and helpers
- One class per file: Each file should contain only one class or interface
- Logical grouping: Related files should be in the same directory
- Separation of concerns:
- Locators in
pages/locators/folder - Actions in
pages/actions/folder - Test cases in
test-cases/folder
- Locators in
- Barrel exports: Use
exports/for centralized exports
Each page should have three components:
-
Interface (in
interfaces/):// ILoginPage.ts export interface ILoginPage { enterUsername(username: string): Promise<void>; enterPassword(password: string): Promise<void>; clickLoginButton(): Promise<void>; login(username: string, password: string): Promise<void>; }
-
Locators (in
pages/locators/):// LoginPageLocators.ts export class LoginPageLocators { public static readonly USERNAME_FIELD = by.name('uid'); public static readonly PASSWORD_FIELD = by.name('password'); public static readonly LOGIN_BUTTON = by.name('btnLogin'); }
-
Actions (in
pages/actions/):// LoginPage.ts export class LoginPage extends Page implements ILoginPage { // Implementation }
Use the following format for commit messages:
<type>(<scope>): <subject>
<body>
<footer>
- feat: A new feature
- fix: A bug fix
- docs: Documentation only changes
- style: Changes that don't affect code meaning (formatting, etc.)
- refactor: Code change that neither fixes a bug nor adds a feature
- perf: Performance improvement
- test: Adding or updating tests
- chore: Changes to build process or auxiliary tools
feat(login): add remember me functionality
Implemented remember me checkbox on login page with
local storage support for saving user preferences.
Closes #123
fix(customer): correct typo in customer file names
Renamed files from "Costumer" to "Customer" to fix
spelling error throughout the project.
- Renamed IEditCostumerPage.ts to IEditCustomerPage.ts
- Renamed EditCostumerPage.ts to EditCustomerPage.ts
- Updated all imports and references
Fixes #456
docs(readme): update installation instructions
Added troubleshooting section and clarified prerequisites
for better onboarding experience.
- Use imperative mood: "add" not "added", "fix" not "fixed"
- Capitalize first letter: "Add feature" not "add feature"
- No period at the end: "Add feature" not "Add feature."
- Limit subject line to 50 characters
- Wrap body at 72 characters
- Reference issues: Use "Fixes #123" or "Closes #123"
-
Update your branch with latest changes from master:
git fetch upstream git rebase upstream/master
-
Run tests locally:
npm test -
Compile TypeScript without errors:
npm run tsc
-
Check code quality (if linter is configured):
npm run lint
When creating a PR, include:
## Description
Brief description of changes
## Type of Change
- [ ] Bug fix (non-breaking change which fixes an issue)
- [ ] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
- [ ] Documentation update
## Testing
- [ ] I have tested this code locally
- [ ] I have added tests that prove my fix is effective or that my feature works
- [ ] All new and existing tests pass
## Checklist
- [ ] My code follows the style guidelines of this project
- [ ] I have performed a self-review of my own code
- [ ] I have commented my code, particularly in hard-to-understand areas
- [ ] I have made corresponding changes to the documentation
- [ ] My changes generate no new warnings
- [ ] I have checked my code and corrected any misspellings
## Related Issues
Fixes #(issue number)
## Screenshots (if applicable)- At least one maintainer must review and approve
- All CI checks must pass
- No merge conflicts with master branch
- All conversations must be resolved
Use JSDoc style comments for classes and methods:
/**
* Represents the login page of the application.
* Provides methods to interact with login functionality.
*
* @extends Page
* @implements ILoginPage
*/
export class LoginPage extends Page implements ILoginPage {
/**
* Enters username in the username field
* @param {string} username - The username to enter
* @returns {Promise<void>}
*/
public async enterUsername(username: string): Promise<void> {
await this.sendKeys(LoginPageLocators.USERNAME_FIELD, username);
}
}Use appropriate log levels:
// Information messages
logger.info('Starting login process');
// Debug information (detailed)
logger.debug(`Username entered: ${username}`);
// Warning messages
logger.warn('Login attempt failed, retrying...');
// Error messages
logger.error(`Login failed: ${error.message}`);
// Fatal errors
logger.fatal('Critical error: Application crashed');Organize imports in the following order:
- Node.js built-in modules
- External dependencies
- Internal modules (grouped by type)
// Node.js built-ins
import * as path from 'path';
import * as fs from 'fs';
// External dependencies
import { browser, element, by, ElementFinder } from 'protractor';
// Internal - Interfaces
import { ILoginPage } from '../interfaces/ILoginPage';
// Internal - Pages
import { Page } from '../pages/base/Page';
// Internal - Locators
import { LoginPageLocators } from '../pages/locators/LoginPageLocators';
// Internal - Utils
import { LogUtils } from '../log-manager/LogUtils';Follow the AAA (Arrange-Act-Assert) pattern:
it('should login with valid credentials', async () => {
// Arrange
const username = testData.validUsername;
const password = testData.validPassword;
// Act
await loginPage.login(username, password);
// Assert
expect(await homePage.isDisplayed()).toBe(true);
expect(await homePage.getWelcomeMessage()).toContain(username);
});Use descriptive test names that explain what is being tested:
// ✅ Good
it('should display error message when username is empty', async () => { ... });
it('should redirect to home page after successful login', async () => { ... });
it('should disable submit button when form is invalid', async () => { ... });
// ❌ Bad
it('test login', async () => { ... });
it('should work', async () => { ... });
it('test 1', async () => { ... });- Group related tests using
describeblocks - Use
beforeEachandafterEachfor setup and cleanup - Keep tests independent and isolated
- One assertion per test when possible
describe('Login Page Tests', () => {
let loginPage: LoginPage;
let homePage: HomePage;
beforeEach(async () => {
loginPage = new LoginPage();
homePage = new HomePage();
await loginPage.navigateTo();
});
describe('Valid Login Scenarios', () => {
it('should login with valid manager credentials', async () => { ... });
it('should login with valid customer credentials', async () => { ... });
});
describe('Invalid Login Scenarios', () => {
it('should show error with invalid username', async () => { ... });
it('should show error with invalid password', async () => { ... });
it('should show error with empty credentials', async () => { ... });
});
afterEach(async () => {
await browser.restart();
});
});If you need help with your contribution:
- Check the README.md documentation
- Look through existing issues
- Ask questions in your pull request or issue
- Reach out to maintainers
Your contributions help make this project better for everyone. We appreciate your time and effort!
Happy Contributing! 🚀