diff --git a/tests/specs/auth/auth-clean.test.js b/tests/specs/auth/auth-clean.test.js index 998b862..ce87f2e 100644 --- a/tests/specs/auth/auth-clean.test.js +++ b/tests/specs/auth/auth-clean.test.js @@ -126,18 +126,29 @@ describe('Authentication Tests (Clean)', () => { const isDashboardVisible = await dashboardPage.isDashboardDisplayed(); expect(isDashboardVisible).toBe(true); - // Now test logout - look for logout button and click it - try { - const logoutButton = await driver.findElement(By.xpath("//button[contains(text(), 'Logout')]")); - await logoutButton.click(); - await driver.sleep(1000); - } catch (e) { - // Fallback: clear session manually - await driver.manage().deleteAllCookies(); - await driver.executeScript('localStorage.clear(); sessionStorage.clear();'); - await driver.get(global.testConfig.baseUrl); - await driver.sleep(500); - } + // Now test logout - clear auth state directly + + // Clear all auth data + await driver.manage().deleteAllCookies(); + await driver.executeScript(` + localStorage.removeItem('token'); + localStorage.removeItem('user'); + sessionStorage.clear(); + `); + + // Navigate back to base URL + await driver.get(global.testConfig.baseUrl); + await driver.sleep(1000); + + // Wait for auth page to appear + await driver.wait( + async () => { + const authElements = await driver.findElements(By.className('auth-container')); + return authElements.length > 0; + }, + 5000, + 'Auth container did not appear after logout' + ); // Verify we're back at auth page const authContainer = await driver.findElement(By.className('auth-container')); diff --git a/tests/specs/integration/trip-crud.test.js b/tests/specs/integration/trip-crud.test.js new file mode 100644 index 0000000..88b3744 --- /dev/null +++ b/tests/specs/integration/trip-crud.test.js @@ -0,0 +1,230 @@ +const { By, until } = require('selenium-webdriver'); +const RegistrationPage = require('../../support/pages/RegistrationPage'); +const LoginPage = require('../../support/pages/LoginPage'); +const DashboardPage = require('../../support/pages/DashboardPage'); +const TripPage = require('../../support/pages/TripPage'); + +describe('Trip CRUD Operations Test', () => { + let driver; + let registrationPage; + let loginPage; + let dashboardPage; + let tripPage; + let testUser; + let testTrip; + + beforeAll(async () => { + driver = await global.createDriver(); + registrationPage = new RegistrationPage(driver); + loginPage = new LoginPage(driver); + dashboardPage = new DashboardPage(driver); + tripPage = new TripPage(driver); + + // Create unique test data + const timestamp = Date.now(); + testUser = { + name: `Trip Test User ${timestamp}`, + email: `trip.test.${timestamp}@example.com`, + password: 'TripTest123!' + }; + + testTrip = { + name: `Test Trip to Paris ${timestamp}`, + description: 'A wonderful trip to explore the City of Light', + startDate: '01/15/2025', + endDate: '01/22/2025' + }; + }); + + afterAll(async () => { + await global.quitDriver(driver); + }); + + beforeEach(async () => { + // Clear storage and cookies + await Promise.all([ + driver.manage().deleteAllCookies().catch(() => {}), + driver.executeScript('try { localStorage.clear(); sessionStorage.clear(); } catch(e) {}') + ]); + + // Navigate to base URL + await driver.get(global.testConfig.baseUrl); + await driver.sleep(500); + }); + + test('Complete trip CRUD flow: login, create, edit, delete, logout', async () => { + // Step 1: Register a new user + await registrationPage.navigateToRegistration(); + await registrationPage.register( + testUser.name, + testUser.email, + testUser.password + ); + await driver.sleep(2000); + + // Verify registration and auto-login to dashboard + const isDashboardVisible = await dashboardPage.isDashboardDisplayed(); + expect(isDashboardVisible).toBe(true); + + // Wait for trips section to load and animations to complete + await driver.sleep(2000); + const isTripsVisible = await tripPage.isTripsPageDisplayed(); + expect(isTripsVisible).toBe(true); + + // Step 2: Create a new trip + await tripPage.clickCreateNewTrip(); + await tripPage.waitForTripModal(); + + // Fill in trip details + await tripPage.fillTripForm(testTrip); + await tripPage.submitTripForm(); + + // Wait for modal to close and trip to appear + await driver.sleep(2000); + + // Verify trip was created + const tripCreated = await tripPage.verifyTripExists(testTrip.name); + expect(tripCreated).toBe(true); + + // Step 3: Edit the trip + const tripCard = await tripPage.findTripByName(testTrip.name); + expect(tripCard).not.toBeNull(); + + await tripPage.clickEditTrip(tripCard); + await tripPage.waitForTripModal(); + + // Update trip details + const updatedTrip = { + name: testTrip.name + ' (Updated)', + description: 'Updated description - Now including a visit to Versailles!', + startDate: '02/01/2025', + endDate: '02/10/2025' + }; + + // Clear and update fields + const nameInput = await driver.findElement(By.css('input[name="name"]')); + await nameInput.clear(); + await nameInput.sendKeys(updatedTrip.name); + + const descInput = await driver.findElement(By.css('textarea[name="description"]')); + await descInput.clear(); + await descInput.sendKeys(updatedTrip.description); + + await tripPage.submitTripForm(); + await driver.sleep(2000); + + // Verify trip was updated + const tripUpdated = await tripPage.verifyTripExists(updatedTrip.name); + expect(tripUpdated).toBe(true); + + // Step 4: Delete the trip + const updatedTripCard = await tripPage.findTripByName(updatedTrip.name); + expect(updatedTripCard).not.toBeNull(); + + await tripPage.clickDeleteTrip(updatedTripCard); + + // Handle any confirmation dialog + await driver.sleep(500); + + // Check if there's a confirmation modal or alert + try { + // Try to handle browser alert + const alert = await driver.switchTo().alert(); + await alert.accept(); + } catch (e) { + // If no alert, might be a custom confirmation modal + try { + // Look for confirmation button in modal + const confirmButton = await driver.findElement(By.xpath("//button[contains(text(), 'Delete') or contains(text(), 'Confirm')]")); + await confirmButton.click(); + } catch (e2) { + // No confirmation needed + } + } + + await driver.sleep(2000); + + // Verify trip was deleted + const tripDeleted = await tripPage.verifyTripDeleted(updatedTrip.name); + expect(tripDeleted).toBe(true); + + // Step 5: Logout + await tripPage.logout(); + + // Wait for redirect to auth page + await driver.wait( + async () => { + try { + const elements = await driver.findElements(By.className('auth-container')); + return elements.length > 0; + } catch (e) { + return false; + } + }, + 5000, + 'Auth container did not appear after logout' + ); + + // Verify logout - should see auth container + const authContainer = await driver.findElement(By.className('auth-container')); + const isAuthVisible = await authContainer.isDisplayed(); + expect(isAuthVisible).toBe(true); + + // Additional verification: Ensure user is really logged out + const dashboardElements = await driver.findElements(By.className('dashboard')); + expect(dashboardElements.length).toBe(0); + }, 120000); // 2 minutes timeout for the complete flow + + test('Login with existing user and verify trips persist', async () => { + // This test verifies that we can login and see trips + + // First create a user and a trip + const timestamp = Date.now(); + const persistUser = { + name: `Persist Test ${timestamp}`, + email: `persist.${timestamp}@example.com`, + password: 'Persist123!' + }; + + const persistTrip = { + name: `Persistent Trip ${timestamp}`, + description: 'This trip should persist after logout', + startDate: '03/01/2025', + endDate: '03/05/2025' + }; + + // Register and create trip + await registrationPage.navigateToRegistration(); + await registrationPage.register( + persistUser.name, + persistUser.email, + persistUser.password + ); + await driver.sleep(2000); + + // Create a trip + await tripPage.clickCreateNewTrip(); + await tripPage.waitForTripModal(); + await tripPage.fillTripForm(persistTrip); + await tripPage.submitTripForm(); + await driver.sleep(2000); + + // Logout + await tripPage.logout(); + await driver.sleep(1000); + + // Login again + await loginPage.navigateToLogin(); + await loginPage.login(persistUser.email, persistUser.password); + await driver.sleep(2000); + + // Verify dashboard is visible + const isDashboardVisible = await dashboardPage.isDashboardDisplayed(); + expect(isDashboardVisible).toBe(true); + + // Verify trip still exists + await driver.sleep(1000); + const tripExists = await tripPage.verifyTripExists(persistTrip.name); + expect(tripExists).toBe(true); + }, 90000); +}); \ No newline at end of file diff --git a/tests/support/pages/TripPage.js b/tests/support/pages/TripPage.js new file mode 100644 index 0000000..53bce6f --- /dev/null +++ b/tests/support/pages/TripPage.js @@ -0,0 +1,169 @@ +const { By } = require('selenium-webdriver'); +const BasePage = require('./BasePage'); + +class TripPage extends BasePage { + constructor(driver) { + super(driver); + + this.selectors = { + tripsSection: '.trips-section', + tripsSectionTitle: '.trips-section-title', + tripsGrid: '.trips-grid', + addTripCard: '.add-trip-card', + addTripText: '.add-trip-text', + tripCard: '.trip-card', + tripCardTitle: '.trip-card-title', + tripCardDescription: '.trip-card-description', + tripMenuTrigger: '.trip-menu-trigger', + tripDropdown: '.trip-dropdown', + dropdownItemEdit: '.dropdown-item:first-child', + dropdownItemDelete: '.dropdown-item-danger', + + // Modal selectors + tripModal: '.trip-modal', + tripForm: '#trip-form', + tripNameInput: 'input[name="name"]', + tripDescriptionInput: 'textarea[name="description"]', + tripStartDateInput: 'input[name="start_date"]', + tripEndDateInput: 'input[name="end_date"]', + modalCancelButton: '.btn-secondary', + modalSubmitButton: '.btn-primary', + + // Dashboard + dashboard: '.dashboard', + logoutButton: 'button:has-text("Logout")' + }; + } + + async waitForTripsSection() { + await this.utils.waitForElementVisible(this.selectors.tripsSection); + } + + async isTripsPageDisplayed() { + return await this.isElementVisible(this.selectors.tripsSection); + } + + async clickCreateNewTrip() { + await this.clickElement(this.selectors.addTripCard); + await this.driver.sleep(1000); // Wait for intro animation to complete + } + + async waitForTripModal() { + await this.utils.waitForElementVisible(this.selectors.tripModal); + await this.driver.sleep(300); + } + + async fillTripForm(tripData) { + await this.typeIntoElement(this.selectors.tripNameInput, tripData.name); + + if (tripData.description) { + await this.typeIntoElement(this.selectors.tripDescriptionInput, tripData.description); + } + + if (tripData.startDate) { + const startDateInput = await this.driver.findElement(By.css(this.selectors.tripStartDateInput)); + await startDateInput.clear(); + await startDateInput.sendKeys(tripData.startDate); + } + + if (tripData.endDate) { + const endDateInput = await this.driver.findElement(By.css(this.selectors.tripEndDateInput)); + await endDateInput.clear(); + await endDateInput.sendKeys(tripData.endDate); + } + } + + async submitTripForm() { + const submitButton = await this.driver.findElement(By.css(this.selectors.modalSubmitButton)); + await submitButton.click(); + await this.driver.sleep(1000); + } + + async cancelTripForm() { + await this.clickElement(this.selectors.modalCancelButton); + await this.driver.sleep(500); + } + + async getTripCards() { + try { + const cards = await this.driver.findElements(By.css(this.selectors.tripCard)); + return cards; + } catch (error) { + return []; + } + } + + async findTripByName(tripName) { + const cards = await this.getTripCards(); + for (let card of cards) { + try { + const titleElement = await card.findElement(By.css('.trip-card-title')); + const title = await titleElement.getText(); + if (title === tripName) { + return card; + } + } catch (error) { + continue; + } + } + return null; + } + + async openTripMenu(tripCard) { + const menuButton = await tripCard.findElement(By.css(this.selectors.tripMenuTrigger)); + await menuButton.click(); + await this.driver.sleep(300); + } + + async clickEditTrip(tripCard) { + await this.openTripMenu(tripCard); + const editButton = await tripCard.findElement(By.css(this.selectors.dropdownItemEdit)); + await editButton.click(); + await this.driver.sleep(500); + } + + async clickDeleteTrip(tripCard) { + await this.openTripMenu(tripCard); + const deleteButton = await tripCard.findElement(By.css(this.selectors.dropdownItemDelete)); + await deleteButton.click(); + await this.driver.sleep(500); + } + + async confirmDelete() { + // Handle confirmation dialog if it appears + try { + await this.driver.switchTo().alert().accept(); + await this.driver.sleep(500); + } catch (error) { + // No alert present, might be a different confirmation method + } + } + + async logout() { + try { + // First click the user menu trigger to open dropdown + const userMenuTrigger = await this.driver.findElement(By.css('.user-menu-trigger')); + await userMenuTrigger.click(); + await this.driver.sleep(500); + + // Then click the logout button in the dropdown + const logoutButton = await this.driver.findElement(By.xpath("//button[contains(text(), 'Logout')]")); + await logoutButton.click(); + } catch (error) { + throw new Error('Could not perform logout: ' + error.message); + } + await this.driver.sleep(1000); + } + + async verifyTripExists(tripName) { + const tripCard = await this.findTripByName(tripName); + return tripCard !== null; + } + + async verifyTripDeleted(tripName) { + const tripCard = await this.findTripByName(tripName); + return tripCard === null; + } +} + +module.exports = TripPage; \ No newline at end of file