/** * @ai-summary End-to-end tests for Gas Stations feature * * Prerequisites: * - Backend API running * - Test user authenticated * - Google Maps API key configured * * Run with: npm run e2e */ describe('Gas Stations Feature', () => { beforeEach(() => { // Login as test user cy.login(); cy.visit('/stations'); }); const enterSampleAddress = () => { cy.get('input[name="street"]').clear().type('123 Main St'); cy.get('input[name="city"]').clear().type('San Francisco'); cy.get('select[name="state"]').select('CA'); cy.get('input[name="zip"]').clear().type('94105'); }; describe('Search for Nearby Stations', () => { it('should allow searching with current location', () => { // Mock geolocation cy.window().then((win) => { cy.stub(win.navigator.geolocation, 'getCurrentPosition').callsFake((successCallback) => { successCallback({ coords: { latitude: 37.7749, longitude: -122.4194, accuracy: 10 } } as GeolocationPosition); }); }); // Click current location button cy.contains('button', 'Current Location').click(); // Click search cy.contains('button', 'Search').click(); // Verify results displayed cy.get('[data-testid="station-card"]').should('have.length.greaterThan', 0); cy.contains('Shell').or('Chevron').or('76').or('Exxon').should('be.visible'); }); it('should allow searching with a manual address', () => { // Enter manual address fields enterSampleAddress(); // Adjust radius cy.get('[data-testid="radius-slider"]').click(); // Search cy.contains('button', 'Search').click(); // Verify results cy.get('[data-testid="station-card"]').should('exist'); }); it('should require address details when location is unavailable', () => { // Attempt to search without address or geolocation cy.contains('button', 'Search').click(); // Verify error message cy.contains('Enter Street, City, State, and ZIP', { matchCase: false }).should('be.visible'); }); it('should display loading state during search', () => { cy.intercept('POST', '/api/stations/search', { delay: 1000, body: { stations: [] } }); cy.contains('button', 'Search').click(); // Verify loading indicator cy.get('[data-testid="loading-skeleton"]').should('be.visible'); }); }); describe('View Stations on Map', () => { beforeEach(() => { // Perform a search first enterSampleAddress(); cy.contains('button', 'Search').click(); cy.wait(2000); }); it('should display map with station markers', () => { // Verify map is loaded cy.get('[data-testid="station-map"]').should('be.visible'); // Verify markers present (if Google Maps loaded) cy.get('.gm-style').should('exist'); }); it('should show info window when marker clicked', () => { // This test assumes Google Maps is loaded // Click first marker (may need custom data-testid on markers) cy.get('[data-testid="map-marker"]').first().click(); // Verify info window content cy.contains('Get Directions').should('be.visible'); }); it('should zoom to fit all markers', () => { // Verify map auto-fits to show all markers cy.get('[data-testid="station-map"]').should('be.visible'); // Check that multiple markers are in view cy.get('[data-testid="station-card"]').then(($cards) => { expect($cards.length).to.be.greaterThan(1); }); }); }); describe('Save Station to Favorites', () => { beforeEach(() => { // Search first enterSampleAddress(); cy.contains('button', 'Search').click(); cy.wait(1000); }); it('should save a station to favorites', () => { // Find first station card cy.get('[data-testid="station-card"]').first().within(() => { // Click bookmark button cy.get('button[title*="favorites"]').click(); }); // Verify optimistic UI update (bookmark filled) cy.get('[data-testid="station-card"]').first().within(() => { cy.get('button[title*="Remove"]').should('exist'); }); // Navigate to Saved tab cy.contains('Saved Stations').click(); // Verify station appears in saved list cy.get('[data-testid="saved-station-item"]').should('have.length.greaterThan', 0); }); it('should allow adding nickname and notes', () => { // Open station details/save modal (if exists) cy.get('[data-testid="station-card"]').first().click(); // Enter nickname cy.get('input[name="nickname"]').type('Work Gas Station'); cy.get('textarea[name="notes"]').type('Best prices in area'); // Save cy.contains('button', 'Save').click(); // Verify saved cy.contains('Work Gas Station').should('be.visible'); }); it('should prevent duplicate saves', () => { // Save station cy.get('[data-testid="station-card"]').first().within(() => { cy.get('button[title*="favorites"]').click(); }); // Try to save again (should toggle off) cy.get('[data-testid="station-card"]').first().within(() => { cy.get('button[title*="Remove"]').click(); }); // Verify removed cy.get('[data-testid="station-card"]').first().within(() => { cy.get('button[title*="Add"]').should('exist'); }); }); }); describe('View Saved Stations List', () => { beforeEach(() => { // Navigate to saved tab cy.contains('Saved Stations').click(); }); it('should display all saved stations', () => { cy.get('[data-testid="saved-station-item"]').should('exist'); }); it('should show empty state when no saved stations', () => { // If no stations saved cy.get('body').then(($body) => { if ($body.find('[data-testid="saved-station-item"]').length === 0) { cy.contains('No saved stations').should('be.visible'); } }); }); it('should display custom nicknames', () => { // Verify stations show nicknames if set cy.get('[data-testid="saved-station-item"]').first().should('contain.text', ''); }); }); describe('Delete Saved Station', () => { beforeEach(() => { // Ensure at least one station is saved cy.visit('/stations'); cy.get('input[name="latitude"]').clear().type('37.7749'); cy.get('input[name="longitude"]').clear().type('-122.4194'); cy.contains('button', 'Search').click(); cy.wait(1000); cy.get('[data-testid="station-card"]').first().within(() => { cy.get('button[title*="favorites"]').click(); }); cy.wait(500); cy.contains('Saved Stations').click(); }); it('should delete a saved station', () => { // Count initial stations cy.get('[data-testid="saved-station-item"]').its('length').then((initialCount) => { // Delete first station cy.get('[data-testid="saved-station-item"]').first().within(() => { cy.get('button[title*="delete"]').or('button[title*="remove"]').click(); }); // Verify count decreased cy.get('[data-testid="saved-station-item"]').should('have.length', initialCount - 1); }); }); it('should show optimistic removal', () => { // Get station name cy.get('[data-testid="saved-station-item"]').first().invoke('text').then((stationName) => { // Delete cy.get('[data-testid="saved-station-item"]').first().within(() => { cy.get('button[title*="delete"]').click(); }); // Verify immediately removed from UI cy.contains(stationName).should('not.exist'); }); }); it('should handle delete errors', () => { // Mock API error cy.intercept('DELETE', '/api/stations/saved/*', { statusCode: 500, body: { error: 'Server error' } }); // Try to delete cy.get('[data-testid="saved-station-item"]').first().within(() => { cy.get('button[title*="delete"]').click(); }); // Verify error message or rollback cy.contains('error', { matchCase: false }).should('be.visible'); }); }); describe('Mobile Navigation Flow', () => { beforeEach(() => { cy.viewport('iphone-x'); cy.visit('/m/stations'); }); it('should navigate between tabs', () => { // Verify Search tab active cy.contains('Search').should('have.class', 'Mui-selected').or('have.attr', 'aria-selected', 'true'); // Click Saved tab cy.contains('Saved').click(); cy.contains('Saved').should('have.class', 'Mui-selected'); // Click Map tab cy.contains('Map').click(); cy.contains('Map').should('have.class', 'Mui-selected'); }); it('should display mobile-optimized layout', () => { // Verify bottom navigation present cy.get('[role="tablist"]').should('be.visible'); // Verify touch targets are 44px minimum cy.get('button').first().should('have.css', 'min-height').and('match', /44/); }); }); describe('Error Recovery', () => { it('should recover from network errors', () => { // Mock network failure cy.intercept('POST', '/api/stations/search', { forceNetworkError: true }); // Try to search cy.contains('button', 'Search').click(); // Verify error displayed cy.contains('error', { matchCase: false }).or('network').should('be.visible'); // Retry button should be present cy.contains('button', 'Retry').click(); }); it('should handle authentication errors', () => { // Mock 401 cy.intercept('GET', '/api/stations/saved', { statusCode: 401, body: { error: 'Unauthorized' } }); cy.visit('/stations'); // Should redirect to login or show auth error cy.url().should('include', '/login').or('contain', '/auth'); }); }); describe('Integration with Fuel Logs', () => { it('should allow selecting station when creating fuel log', () => { // Navigate to fuel logs cy.visit('/fuel-logs/new'); // Open station picker cy.get('input[name="station"]').or('[data-testid="station-picker"]').click(); // Select a saved station cy.contains('Work Station').or('Shell').click(); // Verify selection cy.get('input[name="station"]').should('have.value', ''); }); }); }); /** * Custom Cypress commands for stations feature */ declare global { namespace Cypress { interface Chainable { login(): Chainable; } } }