Task 10: Create test helper files and base test templates

- Create helpers directory structure
- Add api-helpers.ts with authentication and API mocking functions
- Add ui-helpers.ts with common UI interaction utilities
- Add data-helpers.ts with test data generators
- Create base.spec.ts as reusable test template
- Update support/index.ts to import and expose helper modules globally
This commit is contained in:
gnezim
2026-04-05 19:19:49 +03:00
parent 8d562dd55c
commit 9356945d93
125 changed files with 3921 additions and 105 deletions
+40
View File
@@ -0,0 +1,40 @@
/**
* Base test template for all feature tests
* Copy this file and modify for each feature area
*/
import { uiHelpers } from '../support/helpers/ui-helpers'
import { apiHelpers } from '../support/helpers/api-helpers'
import { dataHelpers } from '../support/helpers/data-helpers'
describe('Feature: [Feature Name]', () => {
beforeEach(() => {
// Navigate to app
cy.visit('http://localhost:3001')
// Mock API responses
apiHelpers.mockFlightSearch()
// Get test data
const dates = dataHelpers.getTestDates()
})
afterEach(() => {
// Clear localStorage (handled by support/index.ts)
})
describe('Scenario: User interaction', () => {
it('should perform action and verify result', () => {
// Arrange
// Setup is in beforeEach
// Act
uiHelpers.fillInput('test-input', 'test-value')
uiHelpers.clickAndWait('submit-button', 'flightSearch')
// Assert
uiHelpers.isVisible('results')
uiHelpers.hasText('results', 'expected-text')
})
})
})
@@ -0,0 +1,27 @@
describe('Button Component', () => {
beforeEach(() => {
cy.visit('http://localhost:3001')
})
it('should render button with text', () => {
cy.get('[data-testid="button"]').should('exist')
})
it('should have correct styling', () => {
cy.get('[data-testid="button"]')
.should('have.css', 'padding')
.should('have.css', 'border-radius')
})
it('should support variant classes', () => {
cy.get('[data-testid="button"]').should('have.class', 'button')
})
it('should support size classes', () => {
cy.get('[data-testid="button"]').should('have.class', 'button--medium')
})
it('should handle disabled state', () => {
cy.get('[data-testid="button"]').should('not.be.disabled')
})
})
@@ -0,0 +1,54 @@
/**
* API helper functions for e2e tests
*/
export const apiHelpers = {
/**
* Get auth token (mock for now, will be real API later)
*/
getAuthToken: () => {
return cy.window().then(win => win.localStorage.getItem('auth_token'))
},
/**
* Set auth token in localStorage
*/
setAuthToken: (token: string) => {
cy.window().then(win => {
win.localStorage.setItem('auth_token', token)
})
},
/**
* Make API call via cy.request
*/
apiCall: (method: string, endpoint: string, body?: any) => {
return cy.request({
method,
url: `http://localhost:3000/api${endpoint}`,
body,
failOnStatusCode: false,
})
},
/**
* Mock flight search response
*/
mockFlightSearch: () => {
cy.intercept('GET', '**/api/flights/**', {
statusCode: 200,
body: {
flights: [
{
id: '1',
number: 'SU123',
departureTime: '10:00',
arrivalTime: '12:00',
from: 'SVO',
to: 'LED',
},
],
},
}).as('flightSearch')
},
}
@@ -0,0 +1,47 @@
/**
* Test data helpers
*/
export const dataHelpers = {
/**
* Generate random flight number
*/
generateFlightNumber: () => {
return `SU${Math.floor(Math.random() * 9000 + 1000)}`
},
/**
* Generate test dates (today, tomorrow, in 7 days)
*/
getTestDates: () => {
const today = new Date()
const tomorrow = new Date(today)
tomorrow.setDate(tomorrow.getDate() + 1)
const weekLater = new Date(today)
weekLater.setDate(weekLater.getDate() + 7)
return {
today: today.toISOString().split('T')[0],
tomorrow: tomorrow.toISOString().split('T')[0],
weekLater: weekLater.toISOString().split('T')[0],
}
},
/**
* Get major Russian cities for testing
*/
getMajorCities: () => [
{ code: 'SVO', name: 'Moscow (Sheremetyevo)' },
{ code: 'LED', name: 'St. Petersburg' },
{ code: 'AER', name: 'Sochi' },
{ code: 'VVO', name: 'Vladivostok' },
{ code: 'SVX', name: 'Ekaterinburg' },
],
/**
* Store test data in cy.wrap for later use
*/
storeData: (key: string, value: any) => {
cy.wrap({ [key]: value }).as(key)
},
}
+58
View File
@@ -0,0 +1,58 @@
/**
* UI interaction helper functions
*/
export const uiHelpers = {
/**
* Fill input field and verify value
*/
fillInput: (testId: string, value: string) => {
cy.getByTestId(testId).clear().type(value).should('have.value', value)
},
/**
* Select dropdown option
*/
selectDropdown: (testId: string, optionText: string) => {
cy.getByTestId(testId).click()
cy.contains(optionText).click()
},
/**
* Click button and wait for response
*/
clickAndWait: (testId: string, waitAlias?: string) => {
cy.getByTestId(testId).click()
if (waitAlias) {
cy.wait(`@${waitAlias}`)
}
},
/**
* Verify element is visible
*/
isVisible: (testId: string) => {
return cy.getByTestId(testId).should('be.visible')
},
/**
* Verify element has text
*/
hasText: (testId: string, text: string) => {
return cy.getByTestId(testId).should('contain', text)
},
/**
* Scroll to element
*/
scrollTo: (testId: string) => {
cy.getByTestId(testId).scrollIntoView()
},
/**
* Check element exists
*/
exists: (testId: string) => {
return cy.getByTestId(testId).should('exist')
},
}
+14
View File
@@ -1,4 +1,18 @@
import './commands'
import { apiHelpers } from './helpers/api-helpers'
import { uiHelpers } from './helpers/ui-helpers'
import { dataHelpers } from './helpers/data-helpers'
// Make helpers available globally
declare global {
namespace Cypress {
interface Chainable {
apiHelpers: typeof apiHelpers
uiHelpers: typeof uiHelpers
dataHelpers: typeof dataHelpers
}
}
}
afterEach(() => {
// Clean up after each test