npm install -D cypress | Cypress 설치 |
npx cypress open | Cypress 앱 열기 |
npx cypress run | 헤드리스 테스트 실행 |
npx cypress run --browser chrome | Chrome에서 실행 |
npx cypress run --spec "cypress/e2e/login.cy.js" | 특정 스펙 실행 |
npx cypress run --headed | 브라우저 표시하여 실행 |
npx cypress run --record | 대시보드에 기록 |
npx cypress verify | 설치 확인 |
describe('Login Page', () => {
beforeEach(() => {
cy.visit('/login');
});
it('displays login form', () => {
cy.get('[data-cy=email]').should('be.visible');
cy.get('[data-cy=password]').should('be.visible');
});
it('logs in successfully', () => {
cy.get('[data-cy=email]').type('user@example.com');
cy.get('[data-cy=password]').type('password123');
cy.get('[data-cy=submit]').click();
cy.url().should('include', '/dashboard');
});
}); // cypress.config.js
const { defineConfig } = require('cypress');
module.exports = defineConfig({
e2e: {
baseUrl: 'http://localhost:3000',
viewportWidth: 1280,
viewportHeight: 720,
video: true,
screenshotOnRunFailure: true,
defaultCommandTimeout: 10000,
setupNodeEvents(on, config) {
// Node event listeners
},
},
}); cy.get('button'); // CSS selector
cy.get('.class-name'); // Class
cy.get('#id'); // ID
cy.get('[data-cy=submit]'); // Data attribute (recommended)
cy.get('[data-testid=button]'); // Test ID
cy.get('input[type="text"]'); // Attribute selector cy.contains('Submit'); // Text content
cy.contains('button', 'Submit'); // Element with text
// Chaining
cy.get('.form')
.find('input') // Find within
.first() // First element
.last() // Last element
.eq(2); // Element at index cy.get('li').parent(); // Parent element
cy.get('ul').children(); // Direct children
cy.get('div').siblings(); // Siblings
cy.get('span').closest('form'); // Closest ancestor
cy.get('ul').next(); // Next sibling
cy.get('ul').prev(); // Previous sibling
cy.get('div').filter('.active'); // Filter by selector cy.get('button').click();
cy.get('button').dblclick(); // Double click
cy.get('button').rightclick(); // Right click
cy.get('button').click({ force: true }); // Force click
cy.get('button').click('topRight'); // Click position
cy.get('button').click(80, 20); // Click coordinates cy.get('input').type('Hello World');
cy.get('input').type('text{enter}'); // With enter key
cy.get('input').type('{selectall}{backspace}'); // Clear
cy.get('input').type('{ctrl+a}'); // Keyboard shortcuts
cy.get('input').clear(); // Clear input
cy.get('input').type('slow', { delay: 100 }); // Slow typing {enter} // Enter key
{backspace} // Backspace
{del} // Delete
{esc} // Escape
{tab} // Tab
{uparrow} // Up arrow
{downarrow} // Down arrow
{leftarrow} // Left arrow
{rightarrow} // Right arrow
{ctrl+a} // Select all
{alt+f4} // Alt+F4
{shift+tab} // Shift+Tab cy.get('select').select('Option 1'); // Select by text
cy.get('select').select('value1'); // Select by value
cy.get('input[type="checkbox"]').check();
cy.get('input[type="checkbox"]').uncheck();
cy.get('input[type="radio"]').check('value');
cy.get('input[type="file"]').selectFile('file.txt');
cy.get('form').submit(); cy.get('element').scrollIntoView();
cy.scrollTo('bottom');
cy.scrollTo(0, 500); // x, y coordinates
cy.scrollTo('50%', '50%'); // Percentage
cy.get('input').focus();
cy.get('input').blur(); cy.get('.draggable').trigger('mousedown', { button: 0 });
cy.get('.droppable').trigger('mousemove').trigger('mouseup');
// Or with drag command (requires plugin)
cy.get('.item').drag('.target'); // Cypress doesn't have native hover
cy.get('button').trigger('mouseover');
cy.get('button').trigger('mouseenter');
cy.get('button').realHover(); // requires cypress-real-events cy.get('button').should('be.visible');
cy.get('button').should('not.be.visible');
cy.get('button').should('exist');
cy.get('button').should('not.exist');
cy.get('button').should('be.hidden'); cy.get('h1').should('have.text', 'Welcome');
cy.get('h1').should('contain', 'Welcome');
cy.get('h1').should('include.text', 'come');
cy.get('input').should('have.value', 'Hello');
cy.get('div').should('be.empty');
cy.get('div').should('not.be.empty'); cy.get('input').should('have.attr', 'placeholder');
cy.get('input').should('have.attr', 'type', 'text');
cy.get('a').should('have.attr', 'href', '/home');
cy.get('button').should('be.disabled');
cy.get('button').should('be.enabled');
cy.get('input').should('be.focused');
cy.get('checkbox').should('be.checked'); cy.get('div').should('have.class', 'active');
cy.get('div').should('not.have.class', 'hidden');
cy.get('div').should('have.css', 'display', 'flex');
cy.get('div').should('have.css', 'background-color', 'rgb(0, 0, 255)'); cy.get('li').should('have.length', 3);
cy.get('li').should('have.length.gt', 2); // Greater than
cy.get('li').should('have.length.gte', 3); // Greater or equal
cy.get('li').should('have.length.lt', 5); // Less than
cy.get('li').should('have.length.lte', 3); // Less or equal
// Multiple assertions
cy.get('button')
.should('be.visible')
.and('be.enabled')
.and('contain', 'Submit'); // Intercept and alias
cy.intercept('GET', '/api/users').as('getUsers');
cy.visit('/');
cy.wait('@getUsers');
// Intercept with response stub
cy.intercept('GET', '/api/users', {
statusCode: 200,
body: [{ id: 1, name: 'John' }],
}).as('getUsers'); cy.intercept('POST', '/api/login', (req) => {
// Modify request
req.headers['Authorization'] = 'Bearer token';
req.body.extra = 'data';
// Continue with modified request
req.continue();
});
cy.intercept('/api/*', (req) => {
req.reply((res) => {
// Modify response
res.body.modified = true;
res.send();
});
}); cy.intercept('GET', '/api/users').as('users');
cy.visit('/');
// Wait and access response
cy.wait('@users').then((interception) => {
expect(interception.response.statusCode).to.eq(200);
expect(interception.response.body).to.have.length(3);
});
// Wait multiple times
cy.wait(['@users', '@posts']); // cypress/fixtures/users.json
// [{ "id": 1, "name": "John" }]
cy.intercept('GET', '/api/users', {
fixture: 'users.json',
}).as('getUsers');
// With delay
cy.intercept('GET', '/api/users', {
fixture: 'users.json',
delay: 1000,
}); // cypress/support/commands.js
Cypress.Commands.add('login', (email, password) => {
cy.visit('/login');
cy.get('[data-cy=email]').type(email);
cy.get('[data-cy=password]').type(password);
cy.get('[data-cy=submit]').click();
cy.url().should('include', '/dashboard');
});
// Usage in test
cy.login('user@example.com', 'password123'); Cypress.Commands.add('loginByApi', (email, password) => {
cy.request({
method: 'POST',
url: '/api/login',
body: { email, password },
}).then((response) => {
window.localStorage.setItem('token', response.body.token);
});
});
// Much faster than UI login
cy.loginByApi('user@example.com', 'password'); Cypress.Commands.overwrite('visit', (originalFn, url, options) => {
// Add auth header to all visits
const token = localStorage.getItem('token');
return originalFn(url, {
...options,
onBeforeLoad(win) {
win.localStorage.setItem('token', token);
},
});
}); cy.wait(1000); // Wait 1 second
// Custom timeout
cy.get('button', { timeout: 10000 }).should('be.visible');
// Retry assertions
cy.get('button')
.should('be.visible')
.and('be.enabled'); // Local Storage
localStorage.setItem('key', 'value');
cy.window().then((win) => {
win.localStorage.setItem('token', 'abc123');
});
cy.clearLocalStorage();
// Cookies
cy.setCookie('name', 'value');
cy.getCookie('name').should('have.property', 'value', 'value');
cy.clearCookies();
// Session Storage
cy.window().then((win) => {
win.sessionStorage.setItem('key', 'value');
}); cy.viewport(1280, 720);
cy.viewport('iphone-x');
cy.viewport('macbook-15');
// Presets: iphone-6, iphone-x, samsung-s10
// ipad-2, macbook-11, macbook-13, macbook-15, macbook-16 cy.screenshot(); // Auto-named
cy.screenshot('login-page'); // Named screenshot
cy.screenshot({ capture: 'fullPage' });
// cypress.config.js
module.exports = defineConfig({
video: true,
videosFolder: 'cypress/videos',
screenshotsFolder: 'cypress/screenshots',
});