mirror of
https://github.com/Ombi-app/Ombi.git
synced 2025-08-24 15:05:24 -07:00
tests
This commit is contained in:
parent
e8c64b777b
commit
4114c2356a
5 changed files with 467 additions and 161 deletions
|
@ -4,24 +4,39 @@ import { addCucumberPreprocessorPlugin } from "@badeball/cypress-cucumber-prepro
|
|||
import createEsbuildPlugin from "@badeball/cypress-cucumber-preprocessor/esbuild";
|
||||
|
||||
export default defineConfig({
|
||||
watchForFileChanges: true,
|
||||
video: true,
|
||||
// Performance optimizations
|
||||
watchForFileChanges: false, // Disable in CI
|
||||
video: false, // Disable video recording for faster runs
|
||||
screenshotOnRunFailure: true,
|
||||
trashAssetsBeforeRuns: true,
|
||||
|
||||
// Security and viewport
|
||||
chromeWebSecurity: false,
|
||||
viewportWidth: 2560,
|
||||
viewportHeight: 1440,
|
||||
viewportWidth: 1920,
|
||||
viewportHeight: 1080,
|
||||
|
||||
// Retry configuration
|
||||
retries: {
|
||||
runMode: 2,
|
||||
openMode: 0,
|
||||
},
|
||||
|
||||
// Environment variables
|
||||
env: {
|
||||
username: 'a',
|
||||
password: 'a',
|
||||
dockerhost: 'http://172.17.0.1'
|
||||
dockerhost: 'http://172.17.0.1',
|
||||
// Add test environment flags
|
||||
isCI: process.env.CI === 'true',
|
||||
// Add API base URL
|
||||
apiBaseUrl: 'http://localhost:5000/api/v1'
|
||||
},
|
||||
|
||||
// Project configuration
|
||||
projectId: 'o5451s',
|
||||
|
||||
e2e: {
|
||||
// We've imported your old cypress plugins here.
|
||||
// You may want to clean this up later by importing these.
|
||||
// Setup node events
|
||||
async setupNodeEvents(
|
||||
on: Cypress.PluginEvents,
|
||||
config: Cypress.PluginConfigOptions
|
||||
|
@ -35,12 +50,53 @@ export default defineConfig({
|
|||
})
|
||||
);
|
||||
|
||||
// Make sure to return the config object as it might have been modified by the plugin.
|
||||
// Add performance monitoring
|
||||
on('task', {
|
||||
log(message) {
|
||||
console.log(message);
|
||||
return null;
|
||||
},
|
||||
table(message) {
|
||||
console.table(message);
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
return config;
|
||||
// return require('./cypress/plugins/index.js')(on, config)
|
||||
},
|
||||
|
||||
// Base configuration
|
||||
baseUrl: 'http://localhost:5000',
|
||||
specPattern: ['cypress/tests/**/*.spec.ts*', '**/*.feature'],
|
||||
excludeSpecPattern: ['**/snapshots/*'],
|
||||
specPattern: [
|
||||
'cypress/tests/**/*.spec.ts*',
|
||||
'cypress/features/**/*.feature'
|
||||
],
|
||||
excludeSpecPattern: [
|
||||
'**/snapshots/*',
|
||||
'**/examples/*',
|
||||
'**/*.skip.ts'
|
||||
],
|
||||
|
||||
// Test isolation and performance
|
||||
experimentalRunAllSpecs: true,
|
||||
experimentalModifyObstructiveThirdPartyCode: true,
|
||||
|
||||
// Better error handling
|
||||
defaultCommandTimeout: 10000,
|
||||
requestTimeout: 10000,
|
||||
responseTimeout: 10000,
|
||||
|
||||
// Screenshot and video settings
|
||||
screenshotsFolder: 'cypress/screenshots',
|
||||
videosFolder: 'cypress/videos',
|
||||
},
|
||||
})
|
||||
|
||||
// Component testing (if needed later)
|
||||
component: {
|
||||
devServer: {
|
||||
framework: 'angular',
|
||||
bundler: 'webpack',
|
||||
},
|
||||
specPattern: 'cypress/component/**/*.cy.ts',
|
||||
},
|
||||
});
|
||||
|
|
|
@ -1,10 +1,79 @@
|
|||
|
||||
import { navBar as NavBar } from './shared/NavBar';
|
||||
export abstract class BasePage {
|
||||
abstract visit(options: Cypress.VisitOptions): Cypress.Chainable<Cypress.AUTWindow>;
|
||||
abstract visit(): Cypress.Chainable<Cypress.AUTWindow>;
|
||||
abstract visit(id: string): Cypress.Chainable<Cypress.AUTWindow>;
|
||||
abstract visit(id: string, options: Cypress.VisitOptions): Cypress.Chainable<Cypress.AUTWindow>;
|
||||
|
||||
navbar = NavBar;
|
||||
export abstract class BasePage {
|
||||
// Abstract visit methods
|
||||
abstract visit(options?: any): Cypress.Chainable<any>;
|
||||
abstract visit(id?: string, options?: any): Cypress.Chainable<any>;
|
||||
|
||||
// Common page properties
|
||||
navbar = NavBar;
|
||||
|
||||
// Common page methods
|
||||
/**
|
||||
* Wait for page to be fully loaded
|
||||
*/
|
||||
waitForPageLoad(): Cypress.Chainable<any> {
|
||||
return cy.get('body').should('be.visible');
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if page is loaded by verifying a unique element
|
||||
*/
|
||||
isPageLoaded(uniqueSelector: string): Cypress.Chainable<any> {
|
||||
return cy.get(uniqueSelector).should('exist').then(() => true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Scroll to element with smooth behavior
|
||||
*/
|
||||
scrollToElement(selector: string): Cypress.Chainable<any> {
|
||||
return cy.get(selector).scrollIntoView({ behavior: 'smooth' });
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait for loading spinner to disappear
|
||||
*/
|
||||
waitForLoadingToComplete(): Cypress.Chainable<any> {
|
||||
return cy.get('[data-test="loading-spinner"]', { timeout: 10000 }).should('not.exist');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get element by data-test attribute
|
||||
*/
|
||||
getByData(selector: string): Cypress.Chainable<any> {
|
||||
return cy.get(`[data-test="${selector}"]`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get element by data-test attribute with partial match
|
||||
*/
|
||||
getByDataLike(selector: string): Cypress.Chainable<any> {
|
||||
return cy.get(`[data-test*="${selector}"]`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if element is visible and enabled
|
||||
*/
|
||||
isElementInteractive(selector: string): Cypress.Chainable<any> {
|
||||
return cy.get(selector)
|
||||
.should('be.visible')
|
||||
.should('not.be.disabled')
|
||||
.then(() => true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Take screenshot of current page
|
||||
*/
|
||||
takeScreenshot(name?: string): Cypress.Chainable<any> {
|
||||
const screenshotName = name || `${this.constructor.name}_${Date.now()}`;
|
||||
return cy.screenshot(screenshotName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Log page action for debugging
|
||||
*/
|
||||
logAction(action: string, details?: any): void {
|
||||
cy.log(`[${this.constructor.name}] ${action}`, details);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,36 +1,147 @@
|
|||
import { BasePage } from "../base.page";
|
||||
|
||||
class LoginPage extends BasePage {
|
||||
export class LoginPage extends BasePage {
|
||||
// Page selectors
|
||||
private readonly selectors = {
|
||||
username: '#username-field',
|
||||
password: '#password-field',
|
||||
ombiSignInButton: '[data-cy=OmbiButton]',
|
||||
plexSignInButton: '[data-cy=oAuthPlexButton]',
|
||||
loginForm: '[data-test="login-form"]',
|
||||
errorMessage: '[data-test="error-message"]',
|
||||
loadingSpinner: '[data-test="loading-spinner"]'
|
||||
} as const;
|
||||
|
||||
// Getters for page elements
|
||||
get username() {
|
||||
return cy.get(this.selectors.username);
|
||||
}
|
||||
|
||||
get username(): Cypress.Chainable<any> {
|
||||
return cy.get('#username-field');
|
||||
}
|
||||
get password() {
|
||||
return cy.get(this.selectors.password);
|
||||
}
|
||||
|
||||
get password(): Cypress.Chainable<any> {
|
||||
return cy.get('#password-field');
|
||||
}
|
||||
get ombiSignInButton() {
|
||||
return cy.get(this.selectors.ombiSignInButton);
|
||||
}
|
||||
|
||||
get ombiSignInButton(): Cypress.Chainable<any> {
|
||||
return cy.get('[data-cy=OmbiButton]');
|
||||
}
|
||||
get plexSignInButton() {
|
||||
return cy.get(this.selectors.plexSignInButton);
|
||||
}
|
||||
|
||||
get plexSignInButton(): Cypress.Chainable<any> {
|
||||
return cy.get('[data-cy=oAuthPlexButton]');
|
||||
}
|
||||
get loginForm() {
|
||||
return cy.get(this.selectors.loginForm);
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
get errorMessage() {
|
||||
return cy.get(this.selectors.errorMessage);
|
||||
}
|
||||
|
||||
visit(options: Cypress.VisitOptions): Cypress.Chainable<Cypress.AUTWindow>;
|
||||
visit(): Cypress.Chainable<Cypress.AUTWindow>;
|
||||
visit(id: string): Cypress.Chainable<Cypress.AUTWindow>;
|
||||
visit(id: string, options: Cypress.VisitOptions): Cypress.Chainable<Cypress.AUTWindow>;
|
||||
visit(id?: any, options?: any) {
|
||||
return cy.visit(`/login`, options);
|
||||
}
|
||||
get loadingSpinner() {
|
||||
return cy.get(this.selectors.loadingSpinner);
|
||||
}
|
||||
|
||||
// Page visit method
|
||||
visit(options?: any): Cypress.Chainable<any> {
|
||||
this.logAction('Visiting login page');
|
||||
return cy.visit('/login', options);
|
||||
}
|
||||
|
||||
// Page-specific methods
|
||||
/**
|
||||
* Fill login form with credentials
|
||||
*/
|
||||
fillLoginForm(username: string, password: string): Cypress.Chainable<any> {
|
||||
this.logAction('Filling login form', { username });
|
||||
|
||||
return cy.wrap(null).then(() => {
|
||||
this.username.clear().type(username);
|
||||
this.password.clear().type(password);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Submit login form
|
||||
*/
|
||||
submitLoginForm(): Cypress.Chainable<any> {
|
||||
this.logAction('Submitting login form');
|
||||
return this.ombiSignInButton.click();
|
||||
}
|
||||
|
||||
/**
|
||||
* Complete login flow
|
||||
*/
|
||||
login(username: string, password: string): Cypress.Chainable<any> {
|
||||
this.logAction('Performing login', { username });
|
||||
|
||||
return this.fillLoginForm(username, password)
|
||||
.then(() => this.submitLoginForm());
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait for login to complete
|
||||
*/
|
||||
waitForLoginComplete(): Cypress.Chainable<any> {
|
||||
this.logAction('Waiting for login to complete');
|
||||
|
||||
return cy.url().should('not.include', '/login')
|
||||
.then(() => {
|
||||
this.logAction('Login completed successfully');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if login form is visible
|
||||
*/
|
||||
isLoginFormVisible(): Cypress.Chainable<any> {
|
||||
return this.loginForm.should('be.visible').then(() => true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if Plex OAuth button is visible
|
||||
*/
|
||||
isPlexOAuthVisible(): Cypress.Chainable<any> {
|
||||
return this.plexSignInButton.should('be.visible').then(() => true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if error message is displayed
|
||||
*/
|
||||
isErrorMessageVisible(): Cypress.Chainable<any> {
|
||||
return this.errorMessage.should('be.visible').then(() => true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get error message text
|
||||
*/
|
||||
getErrorMessageText(): Cypress.Chainable<any> {
|
||||
return this.errorMessage.invoke('text');
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear login form
|
||||
*/
|
||||
clearLoginForm(): Cypress.Chainable<any> {
|
||||
this.logAction('Clearing login form');
|
||||
|
||||
return cy.wrap(null).then(() => {
|
||||
this.username.clear();
|
||||
this.password.clear();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify page is loaded
|
||||
*/
|
||||
verifyPageLoaded(): Cypress.Chainable<any> {
|
||||
this.logAction('Verifying login page is loaded');
|
||||
|
||||
return this.waitForPageLoad()
|
||||
.then(() => this.isLoginFormVisible())
|
||||
.then(() => {
|
||||
this.logAction('Login page loaded successfully');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export const loginPage = new LoginPage();
|
||||
|
|
|
@ -1,126 +1,174 @@
|
|||
// ***********************************************
|
||||
// This example commands.js shows you how to
|
||||
// create various custom commands and overwrite
|
||||
// existing commands.
|
||||
//
|
||||
// For more comprehensive examples of custom
|
||||
// commands please read more here:
|
||||
// https://on.cypress.io/custom-commands
|
||||
// Enhanced custom commands with better TypeScript support
|
||||
// ***********************************************
|
||||
//
|
||||
//
|
||||
// -- This is a parent command --
|
||||
// Cypress.Commands.add("login", (email, password) => { ... })
|
||||
//
|
||||
//
|
||||
// -- This is a child command --
|
||||
// Cypress.Commands.add("drag", { prevSubject: 'element'}, (subject, options) => { ... })
|
||||
//
|
||||
//
|
||||
// -- This is a dual command --
|
||||
// Cypress.Commands.add("dismiss", { prevSubject: 'optional'}, (subject, options) => { ... })
|
||||
//
|
||||
//
|
||||
// -- This will overwrite an existing command --
|
||||
// Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... })
|
||||
|
||||
import 'cypress-wait-until';
|
||||
|
||||
Cypress.Commands.add("landingSettings", (enabled) => {
|
||||
cy.fixture('login/landingPageSettings').then((settings) => {
|
||||
settings.enabled = enabled;
|
||||
cy.intercept("GET", "**/Settings/LandingPage", settings).as("landingPageSettingsDisabled");
|
||||
})
|
||||
})
|
||||
|
||||
Cypress.Commands.add('loginWithCreds', (username, password) => {
|
||||
cy.request({
|
||||
method: 'POST',
|
||||
url: '/api/v1/token',
|
||||
body: {
|
||||
username: username,
|
||||
password: password,
|
||||
}
|
||||
})
|
||||
.then((resp) => {
|
||||
window.localStorage.setItem('id_token', resp.body.access_token);
|
||||
});
|
||||
});
|
||||
|
||||
Cypress.Commands.add('login', () => {
|
||||
cy.clearLocalStorage();
|
||||
cy.request({
|
||||
method: 'POST',
|
||||
url: '/api/v1/token',
|
||||
body: {
|
||||
username: Cypress.env('username'),
|
||||
password: Cypress.env('password'),
|
||||
}
|
||||
})
|
||||
.then((resp) => {
|
||||
window.localStorage.setItem('id_token', resp.body.access_token);
|
||||
});
|
||||
});
|
||||
|
||||
Cypress.Commands.add('removeLogin', () => {
|
||||
window.localStorage.removeItem('id_token');
|
||||
});
|
||||
|
||||
Cypress.Commands.add('verifyNotification', (text) => {
|
||||
cy.contains(text, {timeout: 10000});
|
||||
});
|
||||
|
||||
Cypress.Commands.add('createUser', (username, password, claims) => {
|
||||
cy.request({
|
||||
method: 'POST',
|
||||
url: '/api/v1/identity',
|
||||
body: {
|
||||
UserName: username,
|
||||
Password: password,
|
||||
Claims: claims,
|
||||
},
|
||||
headers: {
|
||||
'Authorization': 'Bearer ' + window.localStorage.getItem('id_token'),
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
Cypress.Commands.add('generateUniqueId', () => {
|
||||
const uniqueSeed = Date.now().toString();
|
||||
const id = Cypress._.uniqueId(uniqueSeed);
|
||||
cy.wrap(id);
|
||||
});
|
||||
|
||||
Cypress.Commands.add("getByData", (selector, ...args) => {
|
||||
return cy.get(`[data-test=${selector}]`, ...args);
|
||||
});
|
||||
|
||||
|
||||
Cypress.Commands.add("getByData", (selector) => {
|
||||
return cy.get(`[data-test=${selector}]`);
|
||||
});
|
||||
|
||||
Cypress.Commands.add("getByDataLike", (selector) => {
|
||||
return cy.get(`[data-test*=${selector}]`);
|
||||
});
|
||||
|
||||
Cypress.Commands.add('triggerHover', function(elements) {
|
||||
|
||||
fireEvent(elements, 'mouseover');
|
||||
|
||||
|
||||
function fireEvent(element, event) {
|
||||
if (element.fireEvent) {
|
||||
element.fireEvent('on' + event);
|
||||
} else {
|
||||
const evObj = document.createEvent('Events');
|
||||
|
||||
evObj.initEvent(event, true, false);
|
||||
|
||||
element.dispatchEvent(evObj);
|
||||
}
|
||||
// Type definitions for custom commands
|
||||
declare global {
|
||||
namespace Cypress {
|
||||
interface Chainable {
|
||||
landingSettings(enabled: boolean): Chainable<void>;
|
||||
loginWithCreds(username: string, password: string): Chainable<void>;
|
||||
login(): Chainable<void>;
|
||||
removeLogin(): Chainable<void>;
|
||||
verifyNotification(text: string): Chainable<void>;
|
||||
createUser(username: string, password: string, claims: string[]): Chainable<void>;
|
||||
generateUniqueId(): Chainable<string>;
|
||||
getByData(selector: string): Chainable<JQuery<HTMLElement>>;
|
||||
getByDataLike(selector: string): Chainable<JQuery<HTMLElement>>;
|
||||
triggerHover(elements: JQuery<HTMLElement>): Chainable<void>;
|
||||
waitForApiResponse(alias: string, timeout?: number): Chainable<void>;
|
||||
clearTestData(): Chainable<void>;
|
||||
seedTestData(fixture: string): Chainable<void>;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Enhanced landing page settings command
|
||||
Cypress.Commands.add("landingSettings", (enabled: boolean) => {
|
||||
cy.fixture('login/landingPageSettings').then((settings) => {
|
||||
settings.enabled = enabled;
|
||||
cy.intercept("GET", "**/Settings/LandingPage", settings).as("landingPageSettings");
|
||||
});
|
||||
});
|
||||
|
||||
// Enhanced login with credentials
|
||||
Cypress.Commands.add('loginWithCreds', (username: string, password: string) => {
|
||||
cy.request({
|
||||
method: 'POST',
|
||||
url: '/api/v1/token',
|
||||
body: { username, password },
|
||||
failOnStatusCode: false
|
||||
}).then((resp) => {
|
||||
if (resp.status === 200) {
|
||||
window.localStorage.setItem('id_token', resp.body.access_token);
|
||||
cy.log(`Successfully logged in as ${username}`);
|
||||
} else {
|
||||
cy.log(`Login failed for ${username}: ${resp.status}`);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Enhanced default login
|
||||
Cypress.Commands.add('login', () => {
|
||||
cy.clearLocalStorage();
|
||||
cy.clearCookies();
|
||||
|
||||
const username = Cypress.env('username');
|
||||
const password = Cypress.env('password');
|
||||
|
||||
if (!username || !password) {
|
||||
throw new Error('Username and password must be set in environment variables');
|
||||
}
|
||||
|
||||
cy.loginWithCreds(username, password);
|
||||
});
|
||||
|
||||
// Enhanced login removal
|
||||
Cypress.Commands.add('removeLogin', () => {
|
||||
cy.clearLocalStorage();
|
||||
cy.clearCookies();
|
||||
cy.log('Cleared authentication data');
|
||||
});
|
||||
|
||||
// Enhanced notification verification with better error handling
|
||||
Cypress.Commands.add('verifyNotification', (text: string) => {
|
||||
cy.contains(text, { timeout: 10000 })
|
||||
.should('be.visible')
|
||||
.then(() => {
|
||||
cy.log(`Notification "${text}" verified successfully`);
|
||||
});
|
||||
});
|
||||
|
||||
// Enhanced user creation with better error handling
|
||||
Cypress.Commands.add('createUser', (username: string, password: string, claims: string[]) => {
|
||||
const token = window.localStorage.getItem('id_token');
|
||||
if (!token) {
|
||||
throw new Error('No authentication token found. Please login first.');
|
||||
}
|
||||
|
||||
cy.request({
|
||||
method: 'POST',
|
||||
url: '/api/v1/identity',
|
||||
body: {
|
||||
UserName: username,
|
||||
Password: password,
|
||||
Claims: claims,
|
||||
},
|
||||
headers: {
|
||||
'Authorization': `Bearer ${token}`,
|
||||
},
|
||||
failOnStatusCode: false
|
||||
}).then((resp) => {
|
||||
if (resp.status === 200) {
|
||||
cy.log(`User ${username} created successfully`);
|
||||
} else {
|
||||
cy.log(`Failed to create user ${username}: ${resp.status}`);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Enhanced unique ID generation
|
||||
Cypress.Commands.add('generateUniqueId', () => {
|
||||
const uniqueSeed = Date.now().toString();
|
||||
const id = Cypress._.uniqueId(uniqueSeed);
|
||||
cy.wrap(id);
|
||||
});
|
||||
|
||||
// Enhanced data attribute selectors with better typing
|
||||
Cypress.Commands.add("getByData", (selector: string) => {
|
||||
return cy.get(`[data-test="${selector}"]`);
|
||||
});
|
||||
|
||||
Cypress.Commands.add("getByDataLike", (selector: string) => {
|
||||
return cy.get(`[data-test*="${selector}"]`);
|
||||
});
|
||||
|
||||
// Enhanced hover trigger with better event handling
|
||||
Cypress.Commands.add('triggerHover', function(elements: JQuery<HTMLElement>) {
|
||||
elements.each((index, element) => {
|
||||
const mouseoverEvent = new MouseEvent('mouseover', {
|
||||
bubbles: true,
|
||||
cancelable: true,
|
||||
view: window
|
||||
});
|
||||
element.dispatchEvent(mouseoverEvent);
|
||||
});
|
||||
});
|
||||
|
||||
// New command: Wait for API response with timeout
|
||||
Cypress.Commands.add('waitForApiResponse', (alias: string, timeout: number = 10000) => {
|
||||
cy.wait(`@${alias}`, { timeout });
|
||||
});
|
||||
|
||||
// New command: Clear test data
|
||||
Cypress.Commands.add('clearTestData', () => {
|
||||
cy.clearLocalStorage();
|
||||
cy.clearCookies();
|
||||
cy.clearSessionStorage();
|
||||
cy.log('All test data cleared');
|
||||
});
|
||||
|
||||
// New command: Seed test data from fixture
|
||||
Cypress.Commands.add('seedTestData', (fixture: string) => {
|
||||
cy.fixture(fixture).then((data) => {
|
||||
// Implementation depends on your seeding strategy
|
||||
cy.log(`Seeding test data from ${fixture}`);
|
||||
// Example: cy.request('POST', '/api/v1/test/seed', data);
|
||||
});
|
||||
});
|
||||
|
||||
// Override visit command to add better logging
|
||||
Cypress.Commands.overwrite('visit', (originalFn, url, options) => {
|
||||
cy.log(`Visiting: ${url}`);
|
||||
return originalFn(url, options);
|
||||
});
|
||||
|
||||
// Override click command to add better logging
|
||||
Cypress.Commands.overwrite('click', (originalFn, subject, options) => {
|
||||
cy.log(`Clicking element: ${subject.selector || 'unknown'}`);
|
||||
return originalFn(subject, options);
|
||||
});
|
||||
|
||||
|
|
@ -1,16 +1,38 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"target": "es6",
|
||||
"target": "ES2022",
|
||||
"lib": ["ES2022", "DOM"],
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "node",
|
||||
"strict": true,
|
||||
"esModuleInterop": true,
|
||||
"lib": ["es2018", "dom"],
|
||||
"types": ["cypress", "cypress-wait-until", "cypress-image-snapshot", "cypress-real-events", "@bahmutov/cy-api", "node"],
|
||||
"skipLibCheck": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"noEmit": true,
|
||||
"types": [
|
||||
"cypress",
|
||||
"cypress-wait-until",
|
||||
"cypress-image-snapshot",
|
||||
"cypress-real-events",
|
||||
"@bahmutov/cy-api",
|
||||
"node"
|
||||
],
|
||||
"baseUrl": "./cypress",
|
||||
"paths": {
|
||||
"@/*": ["./*"]
|
||||
"@/*": ["./*"],
|
||||
"@fixtures/*": ["./fixtures/*"],
|
||||
"@page-objects/*": ["./integration/page-objects/*"],
|
||||
"@support/*": ["./support/*"]
|
||||
}
|
||||
},
|
||||
"include": [
|
||||
"**/*.ts",
|
||||
"support/*.ts"
|
||||
],
|
||||
"exclude": [
|
||||
"node_modules"
|
||||
]
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue