trip-planner/tests/specs/integration/plannable-items.test.js

296 lines
12 KiB
JavaScript
Raw Normal View History

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('Plannable Items Feature 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: `Plannable Test User ${timestamp}`,
email: `plannable.test.${timestamp}@example.com`,
password: 'PlanTest123!'
};
testTrip = {
name: `Test Trip with Plannables ${timestamp}`,
description: 'A trip to test plannable items feature',
startDate: '02/01/2025',
endDate: '02/05/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(process.env.APP_URL || 'http://localhost:5173');
await driver.wait(until.urlContains('/'), 5000);
});
describe('Plannable Items Management', () => {
it('should create a trip with auto-generated calendar slots', async () => {
// Register new user
await driver.wait(until.elementLocated(By.css('[data-testid="register-link"]')), 10000);
await driver.findElement(By.css('[data-testid="register-link"]')).click();
await registrationPage.register(testUser);
await driver.wait(until.urlContains('/'), 10000);
// Create a new trip
await tripPage.openCreateModal();
await tripPage.fillTripForm(testTrip);
await tripPage.submitForm();
// Wait for trip to be created
await driver.wait(until.elementLocated(By.xpath(`//h3[contains(text(), "${testTrip.name}")]`)), 10000);
// Click on the trip card to navigate to detail page
const tripCard = await driver.findElement(By.xpath(`//h3[contains(text(), "${testTrip.name}")]/ancestor::div[contains(@class, 'trip-card')]`));
await tripCard.click();
// Wait for trip detail page to load
await driver.wait(until.urlContains('/trip/'), 10000);
// Verify trip detail page elements
await driver.wait(until.elementLocated(By.xpath(`//h1[contains(text(), "${testTrip.name}")]`)), 10000);
// Verify calendar slots were created (5 days: Feb 1-5)
const daySlots = await driver.findElements(By.xpath('//h3[contains(@class, "section-title") and contains(text(), "Day")]'));
expect(daySlots.length).toBeGreaterThanOrEqual(5); // Should have at least 5 day slots
});
it('should add a plannable item', async () => {
// Login with existing user
await loginPage.login(testUser.email, testUser.password);
await driver.wait(until.urlContains('/'), 10000);
// Navigate to the trip detail page
const tripCard = await driver.findElement(By.xpath(`//h3[contains(text(), "${testTrip.name}")]/ancestor::div[contains(@class, 'trip-card')]`));
await tripCard.click();
await driver.wait(until.urlContains('/trip/'), 10000);
// Click Add Item button
await driver.wait(until.elementLocated(By.xpath('//button[contains(text(), "Add Item")]')), 10000);
const addItemButton = await driver.findElement(By.xpath('//button[contains(text(), "Add Item")]'));
await addItemButton.click();
// Wait for form modal to appear
await driver.wait(until.elementLocated(By.className('plannable-form-modal')), 5000);
// Fill in the plannable item form
const itemData = {
name: 'Eiffel Tower Visit',
type: 'attraction',
address: 'Champ de Mars, 5 Avenue Anatole France, 75007 Paris',
notes: 'Book tickets in advance for sunset visit'
};
await driver.findElement(By.name('name')).sendKeys(itemData.name);
// Select type
const typeSelect = await driver.findElement(By.name('type'));
await typeSelect.findElement(By.xpath(`//option[@value="${itemData.type}"]`)).click();
await driver.findElement(By.name('address')).sendKeys(itemData.address);
await driver.findElement(By.name('notes')).sendKeys(itemData.notes);
// Assign to Day 2
const slotSelect = await driver.findElement(By.name('calendar_slot_id'));
const day2Option = await slotSelect.findElement(By.xpath('//option[contains(text(), "Day 2")]'));
await day2Option.click();
// Submit the form
const submitButton = await driver.findElement(By.xpath('//button[contains(text(), "Add Item")]'));
await submitButton.click();
// Wait for modal to close and item to appear
await driver.wait(until.stalenessOf(driver.findElement(By.className('plannable-form-overlay'))), 5000);
// Verify the item appears in Day 2 section
await driver.wait(until.elementLocated(By.xpath(`//h4[contains(text(), "${itemData.name}")]`)), 10000);
const itemElement = await driver.findElement(By.xpath(`//h4[contains(text(), "${itemData.name}")]`));
expect(await itemElement.isDisplayed()).toBe(true);
});
it('should edit a plannable item', async () => {
// Login with existing user
await loginPage.login(testUser.email, testUser.password);
await driver.wait(until.urlContains('/'), 10000);
// Navigate to the trip detail page
const tripCard = await driver.findElement(By.xpath(`//h3[contains(text(), "${testTrip.name}")]/ancestor::div[contains(@class, 'trip-card')]`));
await tripCard.click();
await driver.wait(until.urlContains('/trip/'), 10000);
// Find the item and hover to show actions
const itemElement = await driver.findElement(By.xpath('//h4[contains(text(), "Eiffel Tower Visit")]/ancestor::div[contains(@class, "plannable-item")]'));
await driver.actions().move({ origin: itemElement }).perform();
// Click edit button
await driver.wait(until.elementLocated(By.className('btn-edit')), 5000);
const editButton = await driver.findElement(By.className('btn-edit'));
await editButton.click();
// Wait for form modal to appear
await driver.wait(until.elementLocated(By.className('plannable-form-modal')), 5000);
// Update the item name
const nameInput = await driver.findElement(By.name('name'));
await nameInput.clear();
await nameInput.sendKeys('Eiffel Tower Evening Visit');
// Update notes
const notesInput = await driver.findElement(By.name('notes'));
await notesInput.clear();
await notesInput.sendKeys('Sunset visit confirmed for 7 PM');
// Submit the form
const updateButton = await driver.findElement(By.xpath('//button[contains(text(), "Update Item")]'));
await updateButton.click();
// Wait for modal to close
await driver.wait(until.stalenessOf(driver.findElement(By.className('plannable-form-overlay'))), 5000);
// Verify the item was updated
await driver.wait(until.elementLocated(By.xpath('//h4[contains(text(), "Eiffel Tower Evening Visit")]')), 10000);
const updatedItem = await driver.findElement(By.xpath('//h4[contains(text(), "Eiffel Tower Evening Visit")]'));
expect(await updatedItem.isDisplayed()).toBe(true);
});
it('should delete a plannable item', async () => {
// Login with existing user
await loginPage.login(testUser.email, testUser.password);
await driver.wait(until.urlContains('/'), 10000);
// Navigate to the trip detail page
const tripCard = await driver.findElement(By.xpath(`//h3[contains(text(), "${testTrip.name}")]/ancestor::div[contains(@class, 'trip-card')]`));
await tripCard.click();
await driver.wait(until.urlContains('/trip/'), 10000);
// Find the item and hover to show actions
const itemElement = await driver.findElement(By.xpath('//h4[contains(text(), "Eiffel Tower Evening Visit")]/ancestor::div[contains(@class, "plannable-item")]'));
await driver.actions().move({ origin: itemElement }).perform();
// Click delete button
await driver.wait(until.elementLocated(By.className('btn-delete')), 5000);
const deleteButton = await driver.findElement(By.className('btn-delete'));
await deleteButton.click();
// Accept confirmation dialog
await driver.wait(until.alertIsPresent(), 5000);
const alert = await driver.switchTo().alert();
await alert.accept();
// Verify the item is removed
await driver.sleep(1000); // Give time for the item to be removed
const items = await driver.findElements(By.xpath('//h4[contains(text(), "Eiffel Tower Evening Visit")]'));
expect(items.length).toBe(0);
});
it('should handle multiple plannable items of different types', async () => {
// Login with existing user
await loginPage.login(testUser.email, testUser.password);
await driver.wait(until.urlContains('/'), 10000);
// Navigate to the trip detail page
const tripCard = await driver.findElement(By.xpath(`//h3[contains(text(), "${testTrip.name}")]/ancestor::div[contains(@class, 'trip-card')]`));
await tripCard.click();
await driver.wait(until.urlContains('/trip/'), 10000);
// Test data for different item types
const itemsToAdd = [
{
name: 'Hotel Le Meurice',
type: 'hotel',
address: '228 Rue de Rivoli, 75001 Paris',
notes: 'Check-in at 3 PM'
},
{
name: 'Le Jules Verne Restaurant',
type: 'restaurant',
address: 'Eiffel Tower, Avenue Gustave Eiffel, 75007 Paris',
notes: 'Reservation at 8 PM'
},
{
name: 'Louvre Museum',
type: 'attraction',
address: 'Rue de Rivoli, 75001 Paris',
notes: 'Morning visit'
}
];
// Add each item
for (const item of itemsToAdd) {
// Click Add Item button
const addItemButton = await driver.findElement(By.xpath('//button[contains(text(), "Add Item")]'));
await addItemButton.click();
// Wait for form modal to appear
await driver.wait(until.elementLocated(By.className('plannable-form-modal')), 5000);
// Fill in the form
await driver.findElement(By.name('name')).sendKeys(item.name);
const typeSelect = await driver.findElement(By.name('type'));
await typeSelect.findElement(By.xpath(`//option[@value="${item.type}"]`)).click();
await driver.findElement(By.name('address')).sendKeys(item.address);
await driver.findElement(By.name('notes')).sendKeys(item.notes);
// Submit the form
const submitButton = await driver.findElement(By.xpath('//button[contains(text(), "Add Item")]'));
await submitButton.click();
// Wait for modal to close
await driver.wait(until.stalenessOf(driver.findElement(By.className('plannable-form-overlay'))), 5000);
// Verify the item appears
await driver.wait(until.elementLocated(By.xpath(`//h4[contains(text(), "${item.name}")]`)), 10000);
}
// Verify all items are displayed
for (const item of itemsToAdd) {
const itemElement = await driver.findElement(By.xpath(`//h4[contains(text(), "${item.name}")]`));
expect(await itemElement.isDisplayed()).toBe(true);
}
// Verify items show correct type icons
const hotelItem = await driver.findElement(By.xpath('//h4[contains(text(), "Hotel Le Meurice")]/preceding-sibling::div[contains(@class, "item-icon")]'));
const hotelIcon = await hotelItem.getText();
expect(hotelIcon).toContain('🏨');
const restaurantItem = await driver.findElement(By.xpath('//h4[contains(text(), "Le Jules Verne")]/preceding-sibling::div[contains(@class, "item-icon")]'));
const restaurantIcon = await restaurantItem.getText();
expect(restaurantIcon).toContain('🍽️');
const attractionItem = await driver.findElement(By.xpath('//h4[contains(text(), "Louvre Museum")]/preceding-sibling::div[contains(@class, "item-icon")]'));
const attractionIcon = await attractionItem.getText();
expect(attractionIcon).toContain('🎯');
});
});
});