1010 */
1111Cypress . Commands . add ( 'login' , ( email , password ) => {
1212 cy . visit ( '/login' ) ;
13-
13+
1414 // Fill in login form using data-cy attributes
1515 cy . get ( '[data-cy="input-email"]' ) . clear ( ) . type ( email ) ;
1616 cy . get ( '[data-cy="input-password"]' ) . clear ( ) . type ( password ) ;
17-
17+
1818 // Submit the form
1919 cy . get ( '[data-cy="btn-submit"]' ) . click ( ) ;
20-
20+
2121 // Wait for redirect or success indication
2222 cy . url ( ) . should ( 'not.include' , '/login' , { timeout : 10000 } ) ;
23-
23+
2424 // Verify token is stored
2525 cy . window ( ) . its ( 'localStorage.token' ) . should ( 'exist' ) ;
2626} ) ;
@@ -33,7 +33,7 @@ Cypress.Commands.add('login', (email, password) => {
3333 */
3434Cypress . Commands . add ( 'loginViaAPI' , ( email , password ) => {
3535 const apiUrl = Cypress . env ( 'apiUrl' ) || 'http://localhost:3001' ;
36-
36+
3737 cy . request ( {
3838 method : 'POST' ,
3939 url : `${ apiUrl } /auth/login` ,
@@ -45,19 +45,19 @@ Cypress.Commands.add('loginViaAPI', (email, password) => {
4545 expect ( response . status ) . to . eq ( 200 ) ;
4646 expect ( response . body ) . to . have . property ( 'success' ) ;
4747 expect ( response . body . success ) . to . be . true ;
48-
48+
4949 // Handle response structure: { success, data: { token, user } }
5050 const token = response . body . data ?. token || response . body . token ;
5151 const user = response . body . data ?. user || response . body . user ;
52-
52+
5353 // Visit the home page first to set up the window context
5454 cy . visit ( '/' ) ;
55-
55+
5656 // Store token and user info in localStorage
5757 cy . window ( ) . then ( ( win ) => {
5858 win . localStorage . setItem ( 'token' , token ) ;
5959 win . localStorage . setItem ( 'user' , JSON . stringify ( user ) ) ;
60-
60+
6161 // Dispatch login event
6262 win . dispatchEvent ( new CustomEvent ( 'user:login' , { detail : user } ) ) ;
6363 } ) ;
@@ -72,7 +72,7 @@ Cypress.Commands.add('logout', () => {
7272 // Clear authentication data
7373 cy . clearLocalStorage ( ) ;
7474 cy . clearCookies ( ) ;
75-
75+
7676 // Visit home or login page
7777 cy . visit ( '/' ) ;
7878} ) ;
@@ -88,19 +88,19 @@ Cypress.Commands.add('logout', () => {
8888 */
8989Cypress . Commands . add ( 'register' , ( userData ) => {
9090 cy . visit ( '/signup' ) ;
91-
91+
9292 // Fill in registration form using data-cy attributes
9393 cy . get ( '[data-cy="input-name"]' ) . clear ( ) . type ( userData . name ) ;
9494 cy . get ( '[data-cy="input-email"]' ) . clear ( ) . type ( userData . email ) ;
9595 cy . get ( '[data-cy="input-password"]' ) . clear ( ) . type ( userData . password ) ;
9696 cy . get ( '[data-cy="input-confirmPassword"]' ) . clear ( ) . type ( userData . confirmPassword || userData . password ) ;
97-
97+
9898 // Submit the form
9999 cy . get ( '[data-cy="btn-submit"]' ) . click ( ) ;
100-
100+
101101 // Wait for redirect or success indication
102102 cy . url ( ) . should ( 'not.include' , '/signup' , { timeout : 10000 } ) ;
103-
103+
104104 // Verify token is stored
105105 cy . window ( ) . its ( 'localStorage.token' ) . should ( 'exist' ) ;
106106} ) ;
@@ -145,11 +145,11 @@ Cypress.Commands.add('clearAuthToken', () => {
145145Cypress . Commands . add ( 'loginAsTestUser' , ( role = 'validUser' ) => {
146146 cy . fixture ( 'test-users' ) . then ( ( users ) => {
147147 const user = users [ role ] ;
148-
148+
149149 if ( ! user ) {
150150 throw new Error ( `User role "${ role } " not found in test-users fixture` ) ;
151151 }
152-
152+
153153 // Use API login for speed
154154 cy . loginViaAPI ( user . email , user . password ) ;
155155 } ) ;
@@ -213,12 +213,12 @@ Cypress.Commands.add('interceptAPI', (endpoint, method = 'GET') => {
213213 navigation : { pattern : '**/navigation*' , alias : 'getNavigation' } ,
214214 calculateRoute : { pattern : '**/routes*' , alias : 'calculateRoute' }
215215 } ;
216-
216+
217217 const config = aliasMap [ endpoint ] ;
218218 if ( ! config ) {
219219 throw new Error ( `Unknown API endpoint: ${ endpoint } . Available: ${ Object . keys ( aliasMap ) . join ( ', ' ) } ` ) ;
220220 }
221-
221+
222222 cy . intercept ( method , config . pattern ) . as ( config . alias ) ;
223223} ) ;
224224
@@ -234,22 +234,34 @@ Cypress.Commands.add('interceptAPI', (endpoint, method = 'GET') => {
234234Cypress . Commands . add ( 'waitForAPIAndLoading' , ( alias , options = { } ) => {
235235 const { waitForLoading = true , timeout = 10000 } = options ;
236236 const cleanAlias = alias . startsWith ( '@' ) ? alias : `@${ alias } ` ;
237-
237+
238238 cy . wait ( cleanAlias , { timeout } ) ;
239-
239+
240240 if ( waitForLoading ) {
241241 cy . get ( '[data-cy="loading-spinner"], .loading, .spinner' , { timeout } ) . should ( 'not.exist' ) ;
242242 }
243243} ) ;
244244
245245/**
246246 * Navigate to a page using navigation link and verify URL
247+ * Handles items inside user dropdown by opening it first
247248 * @param {string } dataCy - data-cy attribute value for the navigation link
248249 * @param {string } expectedPath - Expected path in URL after navigation
249250 * @example cy.navigateViaNav('nav-recommendations', '/recommendations')
250251 */
251252Cypress . Commands . add ( 'navigateViaNav' , ( dataCy , expectedPath ) => {
252- cy . get ( `[data-cy="${ dataCy } "]` ) . click ( ) ;
253+ // Items that are inside the user dropdown
254+ const dropdownItems = [ 'nav-preferences' , 'nav-profile' ] ;
255+
256+ if ( dropdownItems . includes ( dataCy ) ) {
257+ // First open the user dropdown
258+ cy . get ( '[data-cy="user-dropdown-trigger"]' ) . should ( 'be.visible' ) . click ( ) ;
259+ // Wait for dropdown menu to appear, then click the item
260+ cy . get ( `[data-cy="${ dataCy } "]` ) . should ( 'be.visible' ) . click ( ) ;
261+ } else {
262+ // Regular nav item - just click it
263+ cy . get ( `[data-cy="${ dataCy } "]` ) . click ( ) ;
264+ }
253265 cy . url ( ) . should ( 'include' , expectedPath ) ;
254266} ) ;
255267
0 commit comments