End-to-end testing skill using Cypress for web applications, covering custom commands, network intercepts, fixtures, cy.session, and component testing patterns.
npx skills add https://github.com/PramodDutta/qaskills --skill Cypress E2E Testing
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.
async/await with Cypress commands.cy.intercept() to control and assert on network requests.cy.session() for auth.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
// 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}',
},
});
// 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}"]`);
});
cy.session() for AuthCypress.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);
},
}
);
});
// 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();
import { loginPage } from '../pages/login.page';
describe('Login', () => {
beforeEach(() => {
loginPage.visit();
});
it('should login successfully with valid credentials', () => {
loginPage.login('[email protected]', 'SecurePass123!');
cy.url().should('include', '/dashboard');
cy.contains('Welcome back').should('be.visible');
});
it('should show error for invalid credentials', () => {
loginPage.login('[email protected]', 'wrongpassword');
loginPage.assertError('Invalid email or password');
});
it('should disable submit button when form is empty', () => {
loginPage.submitButton.should('be.disabled');
});
});
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');
});
});
});
// cypress/fixtures/users.json
{
"validUser": {
"email": "[email protected]",
"password": "SecurePass123!",
"name": "Test User"
},
"adminUser": {
"email": "[email protected]",
"password": "AdminPass123!",
"name": "Admin User"
}
}
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');
});
});
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: '[email protected]' },
}).as('register');
cy.get('#name').type('New User');
cy.get('#email').type('[email protected]');
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');
});
});
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',
});
});
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');
});
// 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');
});
});
cy.intercept() over cy.server()/cy.route() -- The newer API is more powerful.cy.session() for authentication -- It caches session state across tests.data-testid attributes -- They survive refactoring better than class selectors.cy.wait(ms) -- Use cy.wait('@alias') for network requests or assertions for DOM.beforeEach not before -- Each test should set up its own state.cy.request() to set up data instead of UI clicks.10. Limit use of .then() -- Most operations should be chainable assertions.
async/await -- Cypress commands are not Promises. They queue commands.const el = cy.get('.foo') does not work as expected.cy.wait(5000) is a guaranteed source of flakiness..then() for simple assertions -- Use .should() instead, which retries.cy.wrap() -- Use it only when you genuinely need to wrap non-Cypress values.10. Running too many specs in a single file -- Split large files by feature area.
cy.log() to print messages to the Cypress command log.cy.debug() to pause and inspect in DevTools.cy.pause() to step through commands one at a time..then(console.log) to inspect values during test execution.npx cypress open.cy.screenshot() to capture the current state for debugging.# .github/workflows/cypress.yml
name: Cypress Tests
on: [push, pull_request]
jobs:
cypress:
runs-on: ubuntu-latest
strategy:
matrix:
browser: [chrome, firefox, edge]
steps:
- uses: actions/checkout@v4
- uses: cypress-io/github-action@v6
with:
browser: ${{ matrix.browser }}
start: npm run dev
wait-on: 'http://localhost:3000'
record: true
env:
CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }}
Toolkit for interacting with and testing local web applications using Playwright. Supports verifying frontend functionality, debugging UI behavior, capturing browser screenshots, and viewing browser logs.
Use when implementation is complete, all tests pass, and you need to decide how to integrate the work - guides completion of development work by presenting structured options for merge, PR, or cleanup
Use when implementing any feature or bugfix, before writing implementation code
Use when encountering any bug, test failure, or unexpected behavior, before proposing fixes
Use when about to claim work is complete, fixed, or passing, before committing or creating PRs - requires running verification commands and confirming output before making any success claims; evidence before assertions always
Expert guidance for systematic backtesting of trading strategies. Use when developing, testing, stress-testing, or validating quantitative trading strategies. Covers "beating ideas to death" methodology, parameter robustness testing, slippage modeling, bias prevention, and interpreting backtest results. Applicable when user asks about backtesting, strategy validation, robustness testing, avoiding overfitting, or systematic trading development.
Cloud laboratory platform for automated protein testing and validation. Use when designing proteins and needing experimental validation including binding assays, expression testing, thermostability measurements, enzyme activity assays, or protein sequence optimization. Also use for submitting experiments via API, tracking experiment status, downloading results, optimizing protein sequences for better expression using computational tools (NetSolP, SoluProt, SolubleMPNN, ESM), or managing protein design workflows with wet-lab validation.
This skill should be used for time series machine learning tasks including classification, regression, clustering, forecasting, anomaly detection, segmentation, and similarity search. Use when working with temporal data, sequential patterns, or time-indexed observations requiring specialized algorithms beyond standard ML approaches. Particularly suited for univariate and multivariate time series analysis with scikit-learn compatible APIs.
Take pramoddutta/cypress e2e testing from the repository into ~/.claude/skills for personal
use, or into .claude/skills inside a project.
The agent identifies a skill by the name field in its header. Two skills with the
same name cannot sit side by side — one of them will be ignored.
The instructions reference npx.
Without those the skill loads but fails at the first command.