Skip to main content ホーム クリエイター pramoddutta qaskills cypress-e2e-testing
cypress-e2e-testing End-to-end testing skill using Cypress for web applications, covering custom commands, network intercepts, fixtures, cy.session, and component testing patterns.
インストールへ移動 Skills Marketplace コミュニティが作成したAIスキルを発見・探索
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
直接コマンドでは確認用 Prompt が省略されます。実行前にソースを確認してください。
npx skills add https://github.com/PramodDutta/qaskills --skill cypress-e2e-testingコマンドは1行のまま表示されます。コピー前に横へスクロールして全体を確認してください。
ローカルで確認しますか?SkillsMP が現在取得できるファイルをダウンロードできます。
Zipをダウンロード ダウンロード中... このリポジトリの他の Skills Use when publishing SEO blog articles to qaskills.sh, e.g. "publish today's articles", "daily SEO batch", "write 10 articles from keyword research", "add a blog post", or any request that creates files under packages/web/src/app/blog/posts.
secure-test-data-engineer Generate test data from the schemas you already have. Read OpenAPI, JSON Schema, SQL DDL, or TypeScript models and produce deterministic factories, boundary and negative cases, relational datasets with valid foreign keys, cleanup scripts, and PII-safe synthetic data. Production records never leave the machine.
Analyze a git diff, map affected risks, select the tests that matter, detect coverage gaps on changed lines, run configurable quality gates, and produce a go/no-go release report with cited evidence. Recommends only; never merges or deploys.
name Cypress E2E Testing description End-to-end testing skill using Cypress for web applications, covering custom commands, network intercepts, fixtures, cy.session, and component testing patterns. version 1.0.0 author thetestingacademy license MIT tags ["cypress","e2e","web-testing","automation","component-testing"] testingTypes ["e2e"] frameworks ["cypress"] languages ["javascript","typescript"] domains ["web"] agents ["claude-code","cursor","github-copilot","windsurf","codex","aider","continue","cline","zed","bolt"]
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
Cypress is not Selenium -- Cypress runs in the browser alongside the app. Embrace its architecture.
Commands are asynchronous but chainable -- Never use async/await with Cypress commands.
Retry-ability -- Cypress automatically retries assertions. Lean on this feature.
Network control -- Use cy.intercept() to control and assert on network requests.
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
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 ,
: ,
: ,
( ) {
config;
},
},
: {
: {
: ,
: ,
},
: ,
},
});
screenshotOnRunFailure
true
experimentalRunAllSpecs
true
setupNodeEvents
on, config
return
component
devServer
framework
'react'
bundler
'vite'
specPattern
'src/**/*.cy.{ts,tsx}'
Custom Commands
Defining Custom Commands
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 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
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 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 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
{
"validUser" : {
"email" : "user@example.com" ,
"password" : "SecurePass123!" ,
"name" : "Test User"
} ,
"adminUser" : {
"email" : "admin@example.com" ,
"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' );
});
});
Form Testing 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 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 it ('should handle links opening in new 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
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
Use cy.intercept() over cy.server()/cy.route() -- The newer API is more powerful.
Prefer cy.session() for authentication -- It caches session state across tests.
Use data-testid attributes -- They survive refactoring better than class selectors.
Never use cy.wait(ms) -- Use cy.wait('@alias') for network requests or assertions for DOM.
Keep tests independent -- Do not rely on test execution order.
Use beforeEach not before -- Each test should set up its own state.
Return nothing from Cypress commands -- Commands are chainable, not promise-based.
Avoid conditional testing -- Cypress tests should be deterministic.
Use API shortcuts for state setup -- Use cy.request() to set up data instead of UI clicks.
Limit use of .then() -- Most operations should be chainable assertions.
Anti-Patterns to Avoid
Using async/await -- Cypress commands are not Promises. They queue commands.
Assigning Cypress commands to variables -- const el = cy.get('.foo') does not work as expected.
Using arbitrary waits -- cy.wait(5000) is a guaranteed source of flakiness.
Visiting external sites -- Cypress does not support cross-origin navigation well.
Testing third-party widgets directly -- Stub them or use their test hooks.
Using .then() for simple assertions -- Use .should() instead, which retries.
Deeply nested callbacks -- Flatten your test logic; avoid callback hell.
Overusing cy.wrap() -- Use it only when you genuinely need to wrap non-Cypress values.
Testing implementation details -- Focus on what the user sees and does.
Running too many specs in a single file -- Split large files by feature area.
Debugging Tips
Use cy.log() to print messages to the Cypress command log.
Use cy.debug() to pause and inspect in DevTools.
Use cy.pause() to step through commands one at a time.
Use .then(console.log) to inspect values during test execution.
Open Cypress in interactive mode: npx cypress open.
Check the Cypress command log sidebar for time-travel debugging.
Use cy.screenshot() to capture the current state for debugging.
CI Integration
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 }}