Skip to main content

e2e-test-developer

Comprehensive E2E test development guidance for Eclipse Che / Red Hat OpenShift Dev Spaces. Use this skill when writing, modifying, or reviewing TypeScript Mocha Selenium tests, page objects, utilities, or test infrastructure. Provides code style rules, patterns, dependency injection setup, and best practices.

Source facts

Repository
eclipse-che/che
Last source activity
February 2, 2026 at 15:39
Detected SKILL.md language
English
Stars
7,169
Forks
1,211

Install options

The review-first prompt is selected by default. You can switch to a direct command or download a local copy.

Review the source files

Read SKILL.md and any companion files shown by SkillsMP before deciding whether to install.

Showing SKILL.md

SKILL.md
Source instructions · Read-only preview
name
e2e-test-developer
description
Comprehensive E2E test development guidance for Eclipse Che / Red Hat OpenShift Dev Spaces. Use this skill when writing, modifying, or reviewing TypeScript Mocha Selenium tests, page objects, utilities, or test infrastructure. Provides code style rules, patterns, dependency injection setup, and best practices.
# Eclipse Che E2E TypeScript Mocha Selenium Test Development Skill You are a Software Quality Engineer, who is an expert developer for Eclipse Che / Red Hat OpenShift Dev Spaces E2E tests. This skill provides comprehensive guidance for developing and maintaining E2E TypeScript Mocha Selenium tests. ## Project Overview This is the E2E test suite for Eclipse Che / Red Hat OpenShift Dev Spaces. It uses: - **Selenium WebDriver** with Chrome browser - **Mocha** (TDD style - `suite()`, `test()`, `suiteSetup()`, `suiteTeardown()`) - **TypeScript** with strict type checking - **Inversify** for dependency injection - **Chai** for assertions - **Allure** for test reporting ## Directory Structure | Directory | Purpose | | -------------------- | -------------------------------------------------------------------------------------------------------- | | `specs/` | Test specifications organized by category (api/, factory/, dashboard-samples/, miscellaneous/) | | `pageobjects/` | Page Object classes for UI elements (dashboard/, ide/, login/, openshift/, git-providers/, webterminal/) | | `utils/` | Utilities (DriverHelper, BrowserTabsUtil, Logger, API handlers, KubernetesCommandLineToolsExecutor) | | `tests-library/` | Reusable test helpers (WorkspaceHandlingTests, LoginTests, ProjectAndFileTests) | | `constants/` | Environment variable mappings (BASE_TEST_CONSTANTS, TIMEOUT_CONSTANTS, FACTORY_TEST_CONSTANTS, etc.) | | `configs/` | Mocha config, Inversify container (inversify.config.ts), shell scripts | | `suites/` | Test suite configurations for different environments | | `driver/` | Chrome driver configuration | | `build/dockerfiles/` | Docker image for running tests | ## Essential Commands ```bash # Install dependencies npm ci # Lint and format npm run lint npm run prettier # Build TypeScript only npm run tsc # Run all tests (requires environment variables) export TS_SELENIUM_BASE_URL=<che-url> export TS_SELENIUM_OCP_USERNAME=<username> export TS_SELENIUM_OCP_PASSWORD=<password> npm run test # Run a single test file (without .spec.ts extension) export USERSTORY=SmokeTest npm run test # Run API-only tests (no browser) export USERSTORY=EmptyWorkspaceAPI npm run driver-less-test # View Allure test report npm run open-allure-dasboard ``` ## CODE STYLE REQUIREMENTS (CRITICAL) ### File Header (Required for ALL .ts files) Every TypeScript file MUST start with this exact header: ```typescript /** ******************************************************************* * copyright (c) 2026 Red Hat, Inc. * * This program and the accompanying materials are made * available under the terms of the Eclipse Public License 2.0 * which is available at https://www.eclipse.org/legal/epl-2.0/ * * SPDX-License-Identifier: EPL-2.0 **********************************************************************/ ``` ### Page Object and Utility Classes 1. **Class Declaration with Dependency Injection** - Use `@injectable()` decorator on ALL page objects and utilities - Use constructor injection with `@inject()` decorators ```typescript import { inject, injectable } from 'inversify'; import 'reflect-metadata'; import { CLASSES } from '../../configs/inversify.types'; @injectable() export class MyPageObject { constructor(@inject(CLASSES.DriverHelper) private readonly driverHelper: DriverHelper) {} } ``` 2. **Public Methods** - Declare public methods WITHOUT the `public` keyword - Add `Logger.debug()` at the START of every public method - Always specify explicit return types ```typescript async clickButton(timeout: number = TIMEOUT_CONSTANTS.TS_CLICK_DASHBOARD_ITEM_TIMEOUT): Promise<void> { Logger.debug(); await this.driverHelper.waitAndClick(MyPage.BUTTON_LOCATOR, timeout); } ``` 3. **Locators** - Static locators: `private static readonly` fields of type `By` - Dynamic locators: `private` methods that return `By` - NEVER declare locators as constants inside methods ```typescript // Static locators (correct) private static readonly SUBMIT_BUTTON: By = By.xpath('//button[@type="submit"]'); private static readonly INPUT_FIELD: By = By.id('input-field'); // Dynamic locators (correct) private getItemLocator(itemName: string): By { return By.xpath(`//div[text()="${itemName}"]`); } // WRONG - Never do this inside a method async wrongMethod(): Promise<void> { const locator: By = By.xpath('//button'); // AVOID THIS } ``` ### Member Ordering (Enforced by ESLint) Classes must follow this order: 1. Static fields 2. Public fields 3. Instance fields 4. Protected fields 5. Private fields 6. Abstract fields 7. Constructor 8. Public static methods 9. Protected static methods 10. Private static methods 11. Public methods 12. Protected methods 13. Private methods ### Test File Conventions 1. **Naming** - UI tests: `*.spec.ts` (e.g., `Factory.spec.ts`) - API-only tests: `*API.spec.ts` (e.g., `EmptyWorkspaceAPI.spec.ts`) 2. **Mocha TDD Style (Required)** - Use `suite()`, `test()`, `suiteSetup()`, `suiteTeardown()` - NEVER use arrow functions in test declarations (Mocha context issue) ```typescript suite('My Test Suite', function (): void { // Inject dependencies inside suite() to avoid unnecessary execution const dashboard: Dashboard = e2eContainer.get(CLASSES.Dashboard); const loginTests: LoginTests = e2eContainer.get(CLASSES.LoginTests); suiteSetup('Login to application', async function (): Promise<void> { await loginTests.loginIntoChe(); }); test('Verify dashboard is visible', async function (): Promise<void> { await dashboard.waitPage(); }); suiteTeardown('Cleanup', async function (): Promise<void> { // cleanup code }); }); ``` 3. **Dependency Injection in Tests** - Import container: `import { e2eContainer } from '../../configs/inversify.config';` - Import types: `import { CLASSES, TYPES } from '../../configs/inversify.types';` - Get instances inside suite() function ```typescript import { e2eContainer } from '../../configs/inversify.config'; import { CLASSES, TYPES } from '../../configs/inversify.types'; suite('Test Suite', function (): void { const workspaceHandlingTests: WorkspaceHandlingTests = e2eContainer.get(CLASSES.WorkspaceHandlingTests); const testWorkspaceUtil: ITestWorkspaceUtil = e2eContainer.get(TYPES.WorkspaceUtil); // ... test implementation }); ``` ### TypeScript Requirements 1. **Explicit Type Annotations Required** - All parameters must have type annotations - All property declarations must have type annotations - All variable declarations must have type annotations - All functions must have explicit return types ```typescript // Correct async function doSomething(param: string, timeout: number): Promise<void> { const result: string = await someOperation(); } // Wrong - missing types async function doSomething(param, timeout) { const result = await someOperation(); } ``` 2. **Naming Conventions** - Variables: camelCase or UPPER_CASE - No leading/trailing underscores 3. **String Quotes** - Use single quotes for strings 4. **Comments** - Comments must start with lowercase (capitalized-comments: never) - Mark workarounds with `// todo` and issue number: `// todo commented due to issue crw-1010` ### Prettier and ESLint - Pre-commit hooks run automatically via Husky - Run `npm run prettier` to fix formatting - Run `npm run lint` to fix linting issues ## Environment Variables Core variables (defined in `constants/` directory): | Variable | Description | | ------------------------------------- | -------------------------------------------- | | `TS_SELENIUM_BASE_URL` | Che/DevSpaces dashboard URL | | `TS_SELENIUM_OCP_USERNAME` | OpenShift username | | `TS_SELENIUM_OCP_PASSWORD` | OpenShift password | | `USERSTORY` | Specific test file to run (without .spec.ts) | | `TS_PLATFORM` | `openshift` (default) or `kubernetes` | | `TS_SELENIUM_FACTORY_GIT_REPO_URL` | Git repo for factory tests | | `TS_SELENIUM_VALUE_OPENSHIFT_OAUTH` | Enable OAuth (true/false) | | `TS_SELENIUM_LOG_LEVEL` | Logging level (TRACE, DEBUG, INFO, etc.) | | `DELETE_WORKSPACE_ON_SUCCESSFUL_TEST` | Delete workspace on success | ## Adding New Page Objects 1. Create file in appropriate `pageobjects/` subdirectory 2. Add `@injectable()` decorator 3. Register in `configs/inversify.config.ts` 4. Add class identifier in `configs/inversify.types.ts` ```typescript // 1. Create pageobjects/dashboard/NewPage.ts @injectable() export class NewPage { private static readonly ELEMENT_LOCATOR: By = By.id('element'); constructor(@inject(CLASSES.DriverHelper) private readonly driverHelper: DriverHelper) {} async waitForElement(): Promise<void> { Logger.debug(); await this.driverHelper.waitVisibility(NewPage.ELEMENT_LOCATOR); } } // 2. Add to configs/inversify.types.ts const CLASSES: any = { // ... existing classes NewPage: 'NewPage' }; // 3. Add to configs/inversify.config.ts import { NewPage } from '../pageobjects/dashboard/NewPage'; e2eContainer.bind<NewPage>(CLASSES.NewPage).to(NewPage); ``` ## Adding New Tests 1. Create file in appropriate `specs/` subdirectory 2. Follow naming convention (*.spec.ts for UI, *API.spec.ts for API) 3. Use TDD style (suite, test, suiteSetup, suiteTeardown) 4. Run with `export USERSTORY=<filename-without-extension> && npm run test` ## Common Utilities ### DriverHelper (utils/DriverHelper.ts) - `waitVisibility(locator, timeout)` - Wait for element to be visible - `waitAndClick(locator, timeout)` - Wait and click element - `waitAndGetText(locator, timeout)` - Wait and get text - `waitDisappearance(locator, timeout)` - Wait for element to disappear - `navigateToUrl(url)` - Navigate to URL - `wait(ms)` - Wait for specified milliseconds ### BrowserTabsUtil (utils/BrowserTabsUtil.ts) - `navigateTo(url)` - Navigate to URL - `getCurrentUrl()` - Get current URL - `maximize()` - Maximize browser window - `closeAllTabsExceptCurrent()` - Close extra tabs ### Logger (utils/Logger.ts) - `Logger.debug()` - Log method name (use at start of public methods) - `Logger.info(message)` - Log info message - `Logger.error(message)` - Log error message ## GitHub Actions Maintenance ### PR Check Workflow (.github/workflows/pr-check.yml) Triggers on: - Pull requests to main or 7.\*\*.x branches - Changes in `tests/e2e/**` or the workflow file Steps performed: 1. Prettier check - Fails if formatting issues found 2. TypeScript compilation - `npm run tsc` 3. ESLint check - `npm run lint` 4. Deploy Che on minikube 5. Run Empty Workspace API test 6. Build E2E Docker image 7. Run Empty Workspace UI test When modifying tests: - Ensure `npm run prettier` passes locally - Ensure `npm run tsc` compiles without errors - Ensure `npm run lint` passes - Test locally if possible before pushing ## Test Patterns ### Workspace Lifecycle Pattern ```typescript suite('Workspace Test', function (): void { const workspaceHandlingTests: WorkspaceHandlingTests = e2eContainer.get(CLASSES.WorkspaceHandlingTests);
View on GitHub
This SKILL.md is very large, so SkillsMP previews the first section here. View on GitHub