pramoddutta/qaskills/pramoddutta-cypress-e2e

Trust tier 3
  • community
Source check pending

Checking that the SKILL.md file is still in the repository.

End-to-end testing skill using Cypress for web applications, covering custom commands, network intercepts, fixtures, cy.session, and component testing patterns.

Install

npx vskill@latest install pramoddutta/qaskills/pramoddutta-cypress-e2e

SKILL.md

Cypress E2E Testing Skill

You are an expert QA automation engineer specializing in Cypress end-to-end testing. When the user asks you to write, review, or debug Cypress E2E tests, follow these detailed instructions.

Core Principles

  1. Cypress is not Selenium -- Cypress runs in the browser alongside the app. Embrace its architecture.
  2. Commands are asynchronous but chainable -- Never use async/await with Cypress commands.
  3. Retry-ability -- Cypress automatically retries assertions. Lean on this feature.
  4. Network control -- Use cy.intercept() to control and assert on network requests.
  5. Test isolation -- Each test should start from a clean state. Use cy.session() for auth.

Project Structure

cypress/
e2e/
auth/
login.cy.ts
signup.cy.ts
dashboard/
dashboard.cy.ts
checkout/
cart.cy.ts
fixtures/
users.json
products.json
support/
commands.ts
e2e.ts
component.ts
pages/
login.page.ts
dashboard.page.ts
plugins/
index.ts
cypress.config.ts

Configuration

typescript
// cypress.config.ts
import { defineConfig } from 'cypress';
export default defineConfig({
e2e: {
baseUrl: 'http://localhost:3000',
viewportWidth: 1280,
viewportHeight: 720,
defaultCommandTimeout: 10000,
requestTimeout: 15000,
responseTimeout: 30000,
retries: {
runMode: 2,
openMode: 0,
},
video: false,
screenshotOnRunFailure: true,
experimentalRunAllSpecs: true,
setupNodeEvents(on, config) {
// Register plugins here
return config;
},
},
component: {
devServer: {
framework: 'react',
bundler: 'vite',
},
specPattern: 'src/**/*.cy.{ts,tsx}',
},
});

Custom Commands

Defining Custom Commands
typescript
// cypress/support/commands.ts
declare global {
namespace Cypress {
interface Chainable {
login(email: string, password: string): Chainable<void>;
loginByApi(email: string, password: string): Chainable<void>;
getByTestId(testId: string): Chainable<JQuery<HTMLElement>>;
shouldBeVisible(text: string): Chainable<void>;
}
}
}
Cypress.Commands.add('login', (email: string, password: string) => {
cy.visit('/login');
cy.get('[data-testid="email-input"]').type(email);
cy.get('[data-testid="password-input"]').type(password);
cy.get('[data-testid="login-button"]').click();
cy.url().should('include', '/dashboard');
});
Cypress.Commands.add('loginByApi', (email: string, password: string) => {
cy.request({
method: 'POST',
url: '/api/auth/login',
body: { email, password },
}).then((response) => {
window.localStorage.setItem('authToken', response.body.token);
});
});
Cypress.Commands.add('getByTestId', (testId: string) => {
return cy.get(`[data-testid="${testId}"]`);
});
Using cy.session() for Auth
typescript
Cypress.Commands.add('login', (email: string, password: string) => {
cy.session(
[email, password],
() => {
cy.visit('/login');
cy.get('#email').type(email);
cy.get('#password').type(password);
cy.get('button[type="submit"]').click();
cy.url().should('include', '/dashboard');
},
{
validate() {
cy.request('/api/auth/me').its('status').should('eq', 200);
},
}
);
});

Page Object Pattern

typescript
// cypress/pages/login.page.ts
export class LoginPage {
get emailInput() {
return cy.get('[data-testid="email-input"]');
}
get passwordInput() {
return cy.get('[data-testid="password-input"]');
}
get submitButton() {
return cy.get('[data-testid="login-button"]');
}
get errorMessage() {
return cy.get('[data-testid="error-message"]');
}
visit() {
cy.visit('/login');
return this;
}
fillEmail(email: string) {
this.emailInput.clear().type(email);
return this;
}
fillPassword(password: string) {
this.passwordInput.clear().type(password);
return this;
}
submit() {
this.submitButton.click();
return this;
}
login(email: string, password: string) {
this.fillEmail(email);
this.fillPassword(password);
this.submit();
return this;
}
assertError(message: string) {
this.errorMessage.should('be.visible').and('contain.text', message);
return this;
}
}
export const loginPage = new LoginPage();

Writing Tests

Basic Test Structure
typescript
import { loginPage } from '../pages/login.page';
describe('Login', () => {
beforeEach(() => {
loginPage.visit();
});
it('should login successfully with valid credentials', () => {
loginPage.login('user@example.com', 'SecurePass123!');
cy.url().should('include', '/dashboard');
cy.contains('Welcome back').should('be.visible');
});
it('should show error for invalid credentials', () => {
loginPage.login('user@example.com', 'wrongpassword');
loginPage.assertError('Invalid email or password');
});
it('should disable submit button when form is empty', () => {
loginPage.submitButton.should('be.disabled');
});
});
Network Intercept Patterns
typescript
describe('Product listing', () => {
it('should display products from API', () => {
cy.intercept('GET', '/api/products', {
fixture: 'products.json',
}).as('getProducts');
cy.visit('/products');
cy.wait('@getProducts');
cy.get('[data-testid="product-card"]').should('have.length', 3);
});
it('should show error state on API failure', () => {
cy.intercept('GET', '/api/products', {
statusCode: 500,
body: { error: 'Internal Server Error' },
}).as('getProductsFail');
cy.visit('/products');
cy.wait('@getProductsFail');
cy.contains('Something went wrong').should('be.visible');
cy.get('[data-testid="retry-button"]').should('be.visible');
});
it('should show loading state', () => {
cy.intercept('GET', '/api/products', (req) => {
req.on('response', (res) => {
res.setDelay(2000);
});
}).as('getProductsSlow');
cy.visit('/products');
cy.get('[data-testid="loading-spinner"]').should('be.visible');
cy.wait('@getProductsSlow');
cy.get('[data-testid="loading-spinner"]').should('not.exist');
});
it('should send correct query parameters', () => {
cy.intercept('GET', '/api/products*').as('getProducts');
cy.visit('/products');
cy.get('[data-testid="search-input"]').type('laptop');
cy.get('[data-testid="search-button"]').click();
cy.wait('@getProducts').then((interception) => {
expect(interception.request.url).to.include('q=laptop');
});
});
});
Working with Fixtures
json
// cypress/fixtures/users.json
{
"validUser": {
"email": "user@example.com",
"password": "SecurePass123!",
"name": "Test User"
},
"adminUser": {
"email": "admin@example.com",
"password": "AdminPass123!",
"name": "Admin User"
}
}
typescript
describe('User management', () => {
beforeEach(() => {
cy.fixture('users.json').as('users');
});
it('should login with fixture data', function () {
const { email, password } = this.users.validUser;
cy.login(email, password);
cy.url().should('include', '/dashboard');
});
});
Form Testing
typescript
describe('Registration form', () => {
beforeEach(() => {
cy.visit('/register');
});
it('should validate required fields', () => {
cy.get('button[type="submit"]').click();
cy.contains('Name is required').should('be.visible');
cy.contains('Email is required').should('be.visible');
cy.contains('Password is required').should('be.visible');
});
it('should validate email format', () => {
cy.get('#email').type('not-an-email');
cy.get('#email').blur();
cy.contains('Please enter a valid email').should('be.visible');
});
it('should validate password strength', () => {
cy.get('#password').type('123');
cy.get('#password').blur();
cy.contains('Password must be at least 8 characters').should('be.visible');
});
it('should complete registration successfully', () => {
cy.intercept('POST', '/api/auth/register', {
statusCode: 201,
body: { id: '123', email: 'new@example.com' },
}).as('register');
cy.get('#name').type('New User');
cy.get('#email').type('new@example.com');
cy.get('#password').type('SecurePass123!');
cy.get('#confirmPassword').type('SecurePass123!');
cy.get('button[type="submit"]').click();
cy.wait('@register');
cy.url().should('include', '/login');
cy.contains('Registration successful').should('be.visible');
});
});
File Upload
typescript
it('should upload a file', () => {
cy.get('[data-testid="file-input"]').selectFile('cypress/fixtures/sample.pdf');
cy.contains('sample.pdf').should('be.visible');
cy.get('[data-testid="upload-button"]').click();
cy.contains('Upload successful').should('be.visible');
});
it('should drag and drop a file', () => {
cy.get('[data-testid="file-input"]').selectFile('cypress/fixtures/image.png', {
action: 'drag-drop',
});
});
Multi-Tab and Window Handling
typescript
it('should handle links opening in new tab', () => {
// Remove target="_blank" to keep navigation in same tab
cy.get('a[data-testid="external-link"]')
.invoke('removeAttr', 'target')
.click();
cy.url().should('include', '/external-page');
});
it('should verify external link href', () => {
cy.get('a[data-testid="external-link"]')
.should('have.attr', 'href')
.and('include', 'https://external-site.com');
});

Component Testing

typescript
// src/components/Button.cy.tsx
import { Button } from './Button';
describe('Button component', () => {
it('should render with correct text', () => {
cy.mount(<Button>Click me</Button>);
cy.contains('Click me').should('be.visible');
});
it('should handle click events', () => {
const onClick = cy.stub().as('onClick');
cy.mount(<Button onClick={onClick}>Click me</Button>);
cy.contains('Click me').click();
cy.get('@onClick').should('have.been.calledOnce');
});
it('should be disabled when disabled prop is true', () => {
cy.mount(<Button disabled>Click me</Button>);
cy.get('button').should('be.disabled');
});
it('should apply variant styles', () => {
cy.mount(<Button variant="primary">Primary</Button>);
cy.get('button').should('have.class', 'btn-primary');
});
});

Best Practices

  1. Use `cy.intercept()` over `cy.server()`/`cy.route()` -- The newer API is more powerful.
  2. Prefer `cy.session()` for authentication -- It caches session state across tests.
  3. Use `data-testid` attributes -- They survive refactoring better than class selectors.
  4. Never use `cy.wait(ms)` -- Use cy.wait('@alias') for network requests or assertions for DOM.
  5. Keep tests independent -- Do not rely on test execution order.
  6. Use `beforeEach` not `before` -- Each test should set up its own state.
  7. Return nothing from Cypress commands -- Commands are chainable, not promise-based.
  8. Avoid conditional testing -- Cypress tests should be deterministic.
  9. Use API shortcuts for state setup -- Use cy.request() to set up data instead of UI clicks.
  10. Limit use of `.then()` -- Most operations should be chainable assertions.

Anti-Patterns to Avoid

This is the start of the file. Read the full SKILL.md on GitHub.

Security

  • Security scanPassed, 100 of 100Pattern checks on the skill's files.
  • Model reviewPassed

Quality evaluation

  • Task evaluationNot runNo evaluation yet. Run one in Skill Studio.

Source

Popularity

Repository stars
117
Repository forks
5
7-day trend score
24

Stars and forks are for the whole GitHub repository, not this skill alone. Refreshed 10 May 2026, 5 months ago.

Works with

Any agent vskill installs to: 56 targets in all. These 39 have a page on this site.

  • Claude Code
  • Cursor
  • GitHub Copilot
  • Windsurf
  • Codex
  • Gemini CLI
  • Cline
  • Amp
  • Roo Code
  • Goose
  • Continue
  • Kilo Code
  • Junie
  • Trae
  • OpenHands
  • Qwen Code
  • AI Agent
  • Tabnine Chat
  • Sourcegraph Cody
  • Amazon Q
  • Firebase Genkit
  • Sweep
  • Devin
  • Phind
  • Mentat
  • Claude Desktop
  • Aider
  • GPT Pilot
  • Plandex
  • bolt.new
  • v0
  • Replit Agent
  • Anse
  • TypingMind
  • BetterChatGPT
  • LibreChat
  • Warp AI
  • Pieces
  • Fig AI
Embed badge
vskill VERIFIED badge
[![vskill VERIFIED](https://verified-skill.com/api/v1/skills/pramoddutta/qaskills/pramoddutta-cypress-e2e/badge)](https://verified-skill.com/skills/pramoddutta/qaskills/pramoddutta-cypress-e2e)

Something wrong with this skill or its page? Tell us.