diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 1d39b86..39f9385 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -17,7 +17,7 @@ jobs: strategy: fail-fast: false matrix: - shardIndex: [1,2] + shardIndex: [1,2,3] shardTotal: [2] steps: @@ -31,6 +31,14 @@ jobs: run: | echo "USERNAME=${{ secrets.USERNAME }}" >> .env echo "PASSWORD=${{ secrets.PASSWORD }}" >> .env + echo "PASSWORD=${{ secrets.PASSWORD }}" >> .env + echo "NEW_PASSWORD=${{ secrets.NEW_PASSWORD }}" >> .env + echo "FIRST_NAME=${{ secrets.FIRST_NAME }}" >> .env + echo "STREET_NAME=${{ secrets.STREET_NAME }}" >> .env + echo "CITY=${{ secrets.CITY }}" >> .env + echo "STATE=${{ secrets.STATE }}" >> .env + echo "COUNTRY=${{ secrets.COUNTRY }}" >> .env + echo "ZIP_CODE=${{ secrets.ZIP_CODE }}" >> .env - name: Cache npm dependencies uses: actions/cache@v3 diff --git a/pages/AllPages.js b/pages/AllPages.js index ee413b6..8bee63e 100644 --- a/pages/AllPages.js +++ b/pages/AllPages.js @@ -1,13 +1,29 @@ import LoginPage from "./LoginPage"; -import UserPage from "./UserPage"; import InventoryPage from "./InventoryPage"; +import SignupPage from "./SignupPage"; +import HomePage from "./HomePage"; +import AllProductsPage from "./AllProductsPage"; +import ProductDetailsPage from "./ProductDetailsPage"; +import CartPage from "./CartPage"; +import CheckoutPage from "./CheckoutPage"; +import OrderPage from "./OrderPage"; // Import OrderPage +import UserPage from "./UserPage"; // Import UserPage +import OrderDetailsPage from "./OrderDetailsPage"; class AllPages { constructor(page) { this.page = page; this.loginPage = new LoginPage(page); this.inventoryPage = new InventoryPage(page); - this.userPage = new UserPage(page); + this.signupPage = new SignupPage(page); + this.homePage = new HomePage(page); + this.allProductsPage = new AllProductsPage(page); + this.productDetailsPage = new ProductDetailsPage(page); + this.cartPage = new CartPage(page); + this.checkoutPage = new CheckoutPage(page); + this.orderPage = new OrderPage(page); // Instantiate OrderPage + this.userPage = new UserPage(page); // Instantiate UserPage + this.orderDetailsPage = new OrderDetailsPage(page); } } diff --git a/pages/AllProductsPage.js b/pages/AllProductsPage.js new file mode 100644 index 0000000..bed01c5 --- /dev/null +++ b/pages/AllProductsPage.js @@ -0,0 +1,64 @@ +import BasePage from './BasePage.js'; +import { expect } from '@playwright/test'; + +class AllProductsPage extends BasePage{ + + /** + * @param {import('@playwright/test').Page} page + */ + constructor(page) { + super(page); + this.page = page; + } + + locators = { + allProductsTitle: `//h1[text()="All Products"]`, + nthProduct: `[href*="/product-detail/product"]`, + nthProductName: `[href*="/product-detail/product"] h2`, + nthProductPrice: `[href*="/product-detail/product"] p`, + nthProductReviewCount: `[href*="/product-detail/product"] h2 + div span.text-sm`, + nthProductWishlistIcon: '[aria-label="heart"]', + nthProductWishlistIconCount: '.bg-orange-100' + } + + async assertAllProductsTitle() { + await expect(this.page.locator(this.locators.allProductsTitle)).toBeVisible(); + } + + getNthProduct(n) { + return this.page.locator(this.locators.nthProduct).nth(n - 1) + } + + async clickNthProduct(n) { + await this.getNthProduct(n).click(); + } + + getNthProductName(n) { + return this.page.locator(this.locators.nthProductName).nth(n - 1).textContent(); + } + + getNthProductPrice(n) { + return this.page.locator(this.locators.nthProductPrice).nth(n - 1).textContent(); + } + + getNthProductReviewCount(n) { + return this.page.locator(this.locators.nthProductReviewCount).nth(n - 1).textContent(); + } + + getNthProductWishlistIcon(n) { + return this.page.locator(this.locators.nthProductWishlistIcon).nth(n - 1) + } + + async clickNthProductWishlistIcon(n) { + await this.getNthProduct(n).hover() + await this.getNthProductWishlistIcon(n).click(); + await expect(this.page.getByText('Added to the wishlist')).toBeVisible(); + } + + getNthProductWishlistIconCount(n) { + return this.page.locator(this.locators.nthProductWishlistIconCount).nth(n - 1); + } + +} + +export default AllProductsPage; \ No newline at end of file diff --git a/pages/CartPage.js b/pages/CartPage.js new file mode 100644 index 0000000..d7f853a --- /dev/null +++ b/pages/CartPage.js @@ -0,0 +1,128 @@ +import BasePage from './BasePage.js'; +import { expect } from '@playwright/test'; + +class CartPage extends BasePage{ + + /** + * @param {import('@playwright/test').Page} page + */ + constructor(page) { + super(page); + this.page = page; + } + + locators = { + yourCartTitle: 'h2:has-text("Your Cart")', + cartItemImage: '[data-testid="cart-item-image"]', + cartItemName: '[data-testid="cart-item-header"]', + cartItemQuantity: '[data-testid="item-quantity"]', + increaseQuantityButton: '[data-testid="increase-quantity"]', + decreaseQuantityButton: '[data-testid="decrease-quantity"]', + cartItemPrice: '[data-testid="item-price"]', + removeCartItem: '[data-testid="remove-item"]', + subtotalLabel: '[data-testid="subtotal-label"]', + subtotalValue: '[data-testid="subtotal-value"]', + shippingLabel: '[data-testid="shipping-label"]', + shippingValue: '[data-testid="shipping-value"]', + totalLabel: '[data-testid="total-label"]', + totalValue: '[data-testid="total-value"]', + checkoutButton: '[data-testid="checkout-button"]', + viewCartButton: '[data-testid="view-cart-button"]', + shoppingCartIcon: "//*[name()='svg'][.//*[name()='path' and contains(@d,'M0 24C0 10')]]", + } + + async assertYourCartTitle() { + await expect(this.page.locator(this.locators.yourCartTitle)).toBeVisible({ timeout: 10000 }); + } + + getCartItemImage() { + return this.page.locator(this.locators.cartItemImage) + } + + getCartItemName() { + return this.page.locator(this.locators.cartItemName) + } + + getCartItemQuantity() { + return this.page.locator(this.locators.cartItemQuantity) + } + + getIncreaseQuantityButton() { + return this.page.locator(this.locators.increaseQuantityButton).first(); + } + + async clickIncreaseQuantityButton() { + await this.getIncreaseQuantityButton().click(); + } + + getDecreaseQuantityButton() { + return this.page.locator(this.locators.decreaseQuantityButton) + } + + async clickDecreaseQuantityButton() { + await this.getDecreaseQuantityButton().click(); + } + + getCartItemPrice() { + return this.page.locator(this.locators.cartItemPrice) + } + + getRemoveCartItem() { + return this.page.locator(this.locators.removeCartItem) + } + + getSubtotalLabel() { + return this.page.locator(this.locators.subtotalLabel) + } + + getSubtotalValue() { + return this.page.locator(this.locators.subtotalValue) + } + + getShippingLabel() { + return this.page.locator(this.locators.shippingLabel) + } + + getShippingValue() { + return this.page.locator(this.locators.shippingValue) + } + + getTotalLabel() { + return this.page.locator(this.locators.totalLabel) + } + + getTotalValue() { + return this.page.locator(this.locators.totalValue); + } + + getCheckoutButton() { + return this.page.locator(this.locators.checkoutButton) + } + + async clickCheckoutButton() { + await this.getCheckoutButton().click(); + } + + getViewCartButton() { + return this.page.locator(this.locators.viewCartButton) + } + + async clickViewCartButton() { + await this.getViewCartButton().click(); + } + + async clickOnCartIcon() { + await this.page.locator(this.locators.shoppingCartIcon).click({ force: true }); + } + + async verifyCartItemVisible(productName) { + await expect(this.page.locator(this.locators.cartItemName)).toBeVisible(); + await expect(this.page.locator(this.locators.cartItemName)).toHaveText(productName); + } + + async clickOnCheckoutButton() { + await this.page.locator(this.locators.checkoutButton).click({ force: true }); + } +} + +export default CartPage; \ No newline at end of file diff --git a/pages/CheckoutPage.js b/pages/CheckoutPage.js new file mode 100644 index 0000000..d6d3930 --- /dev/null +++ b/pages/CheckoutPage.js @@ -0,0 +1,262 @@ +import BasePage from './BasePage.js'; +import { expect } from '@playwright/test'; + +class CheckoutPage extends BasePage { + + /** + * @param {import('@playwright/test').Page} page + */ + constructor(page) { + super(page); + this.page = page; + } + + locators = { + shippingAddress: { + checkoutTitle: 'checkout-title', + checkoutShippingAddressTitle: 'checkout-shipping-address-title', + checkoutShippingAddressFirstName: 'checkout-first-name-input', + checkoutShippingAddressEmail: 'checkout-email-input', + checkoutShippingAddressCity: 'checkout-city-input', + checkoutShippingAddressState: 'checkout-state-input', + checkoutShippingAddressStreetAddress: 'checkout-street-input', + checkoutShippingAddressZipCode: 'checkout-zip-code-input', + checkoutShippingAddressCountry: 'checkout-country-input', + checkoutCancelButton: 'checkout-cancel-button', + checkoutSaveAddressButton: 'checkout-save-address-button', + }, + paymentMethod: { + checkoutPaymentMethodTitle: 'checkout-payment-method-title', + checkoutCreditCardButton: 'checkout-credit-card-button', + checkoutDebitCardButton: 'checkout-debit-card-button', + checkoutNetbankingButton: 'checkout-netbanking-button', + checkoutCodButton: 'checkout-cod-button', + checkoutCardNumberInput: 'checkout-card-number-input', + checkoutCardHolderNameInput: 'checkout-cardholder-name-input', + checkoutExpirationDateMonthInput: 'checkout-expiration-date-month-input', + checkoutExpirationDateYearInput: 'checkout-expiration-date-year-input', + checkoutCvvInput: 'checkout-cvv-input', + }, + orderSummary: { + checkoutOrderSummaryTitle: 'checkout-order-summary-title', + checkoutOrderSummaryImage: '[data-testid="checkout-order-summary-title"] + div img', + checkoutProductName: 'checkout-product-header', + checkoutProductQuantity: 'checkout-product-quantity', + checkoutProductPrice: 'checkout-product-price', + checkoutSubtotalValue: 'checkout-subtotal-value', + checkoutShippingValue: 'checkout-shipping-value', + checkoutTotalValue: 'checkout-total-value', + checkoutPlaceOrderButton: 'checkout-place-order-button', + checkoutContinueShoppingButton: 'checkout-continue-shopping-button' + }, + // New locators + checkoutTitle: "h1[data-testid='checkout-title']", + productNameInCheckout: "//h3[normalize-space()='{}']", + cashOnDeliveryButton: "//button[normalize-space()='Cash on Delivery']", + cashOnDeliveryText: "//p[normalize-space()='Cash on Delivery']", + placeOrderButton: "//button[normalize-space()='Place Order']", + orderSuccessMessage: "//p[contains(text(), 'Your order was placed successf')]", + orderItemNameConfirmation: "p[data-testid='order-item-name']", + } + + // **************** Shipping Address **************** // + async assertCheckoutTitle() { + await expect(this.page.getByTestId(this.locators.shippingAddress.checkoutTitle)).toBeVisible({ timeout: 10000 }); + } + + getShippingAddressTitle() { + return this.page.getByTestId(this.locators.shippingAddress.checkoutShippingAddressTitle) + } + + getShippingAddressFirstName() { + return this.page.getByTestId(this.locators.shippingAddress.checkoutShippingAddressFirstName) + } + + getShippingAddressEmail() { + return this.page.getByTestId(this.locators.shippingAddress.checkoutShippingAddressEmail) + } + + getShippingAddressCity() { + return this.page.getByTestId(this.locators.shippingAddress.checkoutShippingAddressCity) + } + + getShippingAddressState() { + return this.page.getByTestId(this.locators.shippingAddress.checkoutShippingAddressState) + } + + getShippingAddressStreetAddress() { + return this.page.getByTestId(this.locators.shippingAddress.checkoutShippingAddressStreetAddress) + } + + getShippingAddressZipCode() { + return this.page.getByTestId(this.locators.shippingAddress.checkoutShippingAddressZipCode) + } + + getShippingAddressCountry() { + return this.page.getByTestId(this.locators.shippingAddress.checkoutShippingAddressCountry) + } + + getShippingAddressCancelButton() { + return this.page.getByTestId(this.locators.shippingAddress.checkoutCancelButton) + } + + async clickCancelButton() { + await this.getShippingAddressCancelButton().click(); + } + + getSaveAddressButton() { + return this.page.getByTestId(this.locators.shippingAddress.checkoutSaveAddressButton) + } + + async clickSaveAddressButton() { + await this.getSaveAddressButton().click(); + } + + async fillShippingAddress(firstName, email, city, state, streetAddress, zipCode, country) { + await this.getShippingAddressFirstName().fill(firstName); + await this.getShippingAddressEmail().fill(email); + await this.getShippingAddressCity().fill(city); + await this.getShippingAddressState().fill(state); + await this.getShippingAddressStreetAddress().fill(streetAddress); + await this.getShippingAddressZipCode().fill(zipCode); + await this.getShippingAddressCountry().fill(country); + } + + async assertAddressAddedToast() { + await expect(this.page.getByText('Address added successfully')).toBeVisible({ timeout: 10000 }); + } + + // **************** Payment Method **************** // + + getPaymentMethodTitle() { + return this.page.getByTestId(this.locators.paymentMethod.checkoutPaymentMethodTitle) + } + + getCreditCardButton() { + return this.page.getByTestId(this.locators.paymentMethod.checkoutCreditCardButton) + } + + getDebitCardButton() { + return this.page.getByTestId(this.locators.paymentMethod.checkoutDebitCardButton) + } + + getNetbankingButton() { + return this.page.getByTestId(this.locators.paymentMethod.checkoutNetbankingButton) + } + + getCodButton() { + return this.page.getByTestId(this.locators.paymentMethod.checkoutCodButton) + } + + async clickCodButton() { + await this.getCodButton().click(); + } + + getCardNumberInput() { + return this.page.getByTestId(this.locators.paymentMethod.checkoutCardNumberInput) + } + + getCardHolderNameInput() { + return this.page.getByTestId(this.locators.paymentMethod.checkoutCardHolderNameInput) + } + + getExpirationDateMonthInput() { + return this.page.getByTestId(this.locators.paymentMethod.checkoutExpirationDateMonthInput) + } + + getExpirationDateYearInput() { + return this.page.getByTestId(this.locators.paymentMethod.checkoutExpirationDateYearInput) + } + + getCvvInput() { + return this.page.getByTestId(this.locators.paymentMethod.checkoutCvvInput) + } + + // ************************** Order Summary ************************** // + + getOrderSummaryTitle() { + return this.page.getByTestId(this.locators.orderSummary.checkoutOrderSummaryTitle) + } + + async assertOrderSummaryTitle() { + await expect(this.page.getByTestId(this.locators.orderSummary.checkoutOrderSummaryTitle)).toBeVisible(); + } + + getOrderSummaryImage() { + return this.page.locator(this.locators.orderSummary.checkoutOrderSummaryImage) + } + + getOrderSummaryProductName() { + return this.page.getByTestId(this.locators.orderSummary.checkoutProductName) + } + + getOrderSummaryProductQuantity() { + return this.page.getByTestId(this.locators.orderSummary.checkoutProductQuantity) + } + + getOrderSummaryProductPrice() { + return this.page.getByTestId(this.locators.orderSummary.checkoutProductPrice) + } + + getOrderSummarySubtotalValue() { + return this.page.getByTestId(this.locators.orderSummary.checkoutSubtotalValue).textContent(); + } + + getOrderSummaryShippingValue() { + return this.page.getByTestId(this.locators.orderSummary.checkoutShippingValue) + } + + getOrderSummaryTotalValue() { + return this.page.getByTestId(this.locators.orderSummary.checkoutTotalValue) + } + + getPlaceOrderButton() { + return this.page.getByTestId(this.locators.orderSummary.checkoutPlaceOrderButton) + } + + async clickPlaceOrderButton() { + await this.getPlaceOrderButton().click(); + } + + getContinueShoppingButton() { + return this.page.getByTestId(this.locators.orderSummary.checkoutContinueShoppingButton) + } + + async clickContinueShoppingButton() { + await this.getContinueShoppingButton().click(); + } + + async verifyCheckoutTitle() { + await expect(this.page.locator(this.locators.checkoutTitle)).toBeVisible(); + } + + async verifyProductInCheckout(productName) { + const locator = this.page.locator(this.locators.productNameInCheckout.replace('{}', productName)).nth(1); + await expect(locator).toBeVisible(); + await expect(locator).toHaveText(productName); + } + + async selectCashOnDelivery() { + await this.page.locator(this.locators.cashOnDeliveryButton).click({ force: true }); + } + + async verifyCashOnDeliverySelected() { + await expect(this.page.locator(this.locators.cashOnDeliveryText)).toBeVisible(); + } + + async clickOnPlaceOrder() { + await this.page.locator(this.locators.placeOrderButton).click({ force: true }); + } + + async verifyOrderPlacedSuccessfully() { + await expect(this.page.locator(this.locators.orderSuccessMessage)).toBeVisible(); + } + + async verifyOrderItemName(productName) { + await expect(this.page.locator(this.locators.orderItemNameConfirmation)).toBeVisible(); + await expect(this.page.locator(this.locators.orderItemNameConfirmation)).toHaveText(productName); + } + +} + +export default CheckoutPage; \ No newline at end of file diff --git a/pages/HomePage.js b/pages/HomePage.js new file mode 100644 index 0000000..a2603df --- /dev/null +++ b/pages/HomePage.js @@ -0,0 +1,50 @@ +import BasePage from './BasePage.js'; +import { expect } from '@playwright/test'; + +class HomePage extends BasePage{ + + /** + * @param {import('@playwright/test').Page} page + */ + constructor(page) { + super(page); + this.page = page; + } + + locators = { + navbar: { + homeNav: `//li[text()="Home"]`, + aboutUsNav: `//li[text()="About Us"]`, + contactUsNav: `//li[text()="Contact Us"]`, + allProductsNav: `//li[text()="All Products"]`, + showNowButton: `//a[@href="/products"]/button[text()="Shop Now"]`, + } + } + + getHomeNav() { + return this.page.locator(this.locators.navbar.homeNav).first(); + } + + getAboutUsNav() { + return this.page.locator(this.locators.navbar.aboutUsNav).first(); + } + + getContactUsNav() { + return this.page.locator(this.locators.navbar.contactUsNav).first(); + } + + getAllProductsNav() { + return this.page.locator(this.locators.navbar.allProductsNav).first(); + } + + async clickAllProductsNav() { + await this.getAllProductsNav().click(); + } + + getShowNowButton() { + return this.page.locator(this.locators.navbar.showNowButton); + } + +} + +export default HomePage; \ No newline at end of file diff --git a/pages/InventoryPage.js b/pages/InventoryPage.js index 463f8af..321cbbb 100644 --- a/pages/InventoryPage.js +++ b/pages/InventoryPage.js @@ -15,12 +15,15 @@ class InventoryPage extends BasePage { locators = { shopNowBtn: `(//button[text()='Shop Now'])[1]`, allProductsTitle: `//h1[text()='All Products']`, - searchProductInput: `[placeholder="Search products..."]`, + searchProductInput: `input[placeholder='Search products...']`, selectProduct: `[class="relative pt-4 px-4"]`, productTitle: `(//h1[text()='JBL Charge 4 Bluetooth Speaker'])[1]`, wishlistIcon: `//button[.//span[@aria-label='heart']]`, + allProductsLink: "(//li[normalize-space()='All Products'])[1]", + addToCartIcon: "//*[name()='svg'][.//*[name()='path' and contains(@d,'M832 312H6')]]", + goProHero10BlackTitle: "//h2[normalize-space()='GoPro HERO10 Black']", + continueShoppingButton: "//button[normalize-space()='Continue Shopping']", - } async clickOnShopNowButton() { @@ -30,7 +33,6 @@ class InventoryPage extends BasePage { async searchProduct(productName) { await this.page.fill(this.locators.searchProductInput, productName); await this.page.keyboard.press('Enter'); - await expect(this.page.locator(this.locators.selectProduct)).toBeVisible(); } async selectProduct() { await this.page.click(this.locators.selectProduct); @@ -41,6 +43,22 @@ class InventoryPage extends BasePage { // Add any additional assertions or actions needed after adding to wishlist } + async clickOnAllProductsLink() { + await this.page.locator(this.locators.allProductsLink).click({ force: true }); + } + + async clickOnAddToCartIcon() { + await this.page.locator(this.locators.addToCartIcon).click({ force: true }); + } + + async verifyProductTitleVisible(productName) { + await expect(this.page.locator(`//h2[normalize-space()='${productName}']`)).toHaveText(productName); + } + + async clickOnContinueShopping() { + await this.page.locator(this.locators.continueShoppingButton).click({ force: true }); + } + } export default InventoryPage; \ No newline at end of file diff --git a/pages/LoginPage.js b/pages/LoginPage.js index 5842d63..ac55306 100644 --- a/pages/LoginPage.js +++ b/pages/LoginPage.js @@ -19,10 +19,11 @@ class LoginPage extends BasePage{ invalidLoginError: '[data-test="error"]', userIcon: `//*[name()='svg'][.//*[name()='path' and contains(@d,'M25.1578 1')]]`, logoutButton: `//p[text()='Log Out']`, - + signupLink: `Sign up`, + successSignInMessage: `Logged in successfully`, } - async navigateToLoginPage() { + async navigateToLoginPage() { await this.navigateTo('/'); } @@ -70,6 +71,14 @@ class LoginPage extends BasePage{ async validateSignInPage() { await expect(this.getLoginPageTitle()).toBeVisible(); } + + async clickOnSignupLink() { + await this.page.getByText(this.locators.signupLink).click(); + } + + async verifySuccessSignIn() { + await expect(this.page.getByText(this.locators.successSignInMessage)).toBeVisible({ timeout: 10000 }); + } } export default LoginPage; \ No newline at end of file diff --git a/pages/OrderDetailsPage.js b/pages/OrderDetailsPage.js new file mode 100644 index 0000000..bf75992 --- /dev/null +++ b/pages/OrderDetailsPage.js @@ -0,0 +1,149 @@ +import BasePage from './BasePage.js'; +import { expect } from '@playwright/test'; + +class OrderDetailsPage extends BasePage{ + + /** + * @param {import('@playwright/test').Page} page + */ + constructor(page) { + super(page); + this.page = page; + } + + locators = { + orderDetailsTitle: 'order-details-title', + orderId: 'order-id', + orderPlacedName: 'order-placed-name', + orderPlacedMessage: 'order-placed-message', + orderPlacedDate: 'order-placed-date', + backToHomeButton: 'back-to-home', + orderInformation: { + orderInformationTitle: 'order-information-title', + orderConfirmedTitle:'order-confirmed-title', + orderConfirmedMessage:'order-confirmed-message', + shippingDetailsTitle:'shipping-details-title', + shippingEmailValue:'shipping-email-value', + paymentMethodAmount:'payment-method-amount', + deliveryAddressLabel:'delivery-address-label', + deliveryAddressValue:'delivery-address-value', + continueShoppingButton:'continue-shopping-button' + }, + orderSummary: { + orderSummaryTitle:'order-summary-title', + orderSummaryProductName:'order-item-name', + orderSummaryProductQuantity:'order-item-quantity', + orderSummaryProductPrice:'order-item-price', + subtotalValue:'subtotal-value', + shippingValue:'shipping-value', + totalValue:'total-value' + } + } + + async assertOrderDetailsTitle() { + await expect(this.page.getByTestId(this.locators.orderDetailsTitle)).toBeVisible(); + } + + getOrderId() { + return this.page.getByTestId(this.locators.orderId) + } + + getBackToHomeButton() { + return this.page.getByTestId(this.locators.backToHomeButton) + } + + async clickBackToHomeButton() { + await this.getBackToHomeButton().click(); + } + + async assertOrderPlacedName() { + await expect(this.page.getByTestId(this.locators.orderPlacedName)).toBeVisible(); + } + + async assertOrderPlacedMessage() { + await expect(this.page.getByTestId(this.locators.orderPlacedMessage)).toBeVisible(); + } + + async assertOrderPlacedDate() { + await expect(this.page.getByTestId(this.locators.orderPlacedDate)).toBeVisible(); + } + + async assertOrderInformationTitle() { + await expect(this.page.getByTestId(this.locators.orderInformation.orderInformationTitle)).toBeVisible(); + } + + + // ****************************** Order Information ****************************** // + async assertOrderConfirmedTitle() { + await expect(this.page.getByTestId(this.locators.orderInformation.orderConfirmedTitle)).toBeVisible(); + } + + async assertOrderConfirmedMessage() { + await expect(this.page.getByTestId(this.locators.orderInformation.orderConfirmedMessage)).toBeVisible(); + } + + + async assertShippingDetailsTitle() { + await expect(this.page.getByTestId(this.locators.orderInformation.shippingDetailsTitle)).toBeVisible(); + } + + async assertShippingEmailValue(email) { + await expect(this.page.getByTestId(this.locators.orderInformation.shippingEmailValue)).toContainText(email); + } + + async assertPaymentMethodAmount(amount) { + await expect(this.page.getByTestId(this.locators.orderInformation.paymentMethodAmount)).toContainText(amount); + } + + async assertDeliveryAddressLabel() { + await expect(this.page.getByTestId(this.locators.orderInformation.deliveryAddressLabel)).toBeVisible(); + } + + async assertDeliveryAddressValue() { + await expect(this.page.getByTestId(this.locators.orderInformation.deliveryAddressValue)).toBeVisible(); + } + + getContinueShoppingButton() { + return this.page.getByTestId(this.locators.orderInformation.continueShoppingButton) + } + + async clickContinueShoppingButton() { + await this.getContinueShoppingButton().click(); + } + + async assertContinueShoppingButton() { + await expect(this.getContinueShoppingButton()).toBeVisible(); + } + + // ****************************** Order Summary ****************************** // + async assertOrderSummaryTitle() { + await expect(this.page.getByTestId(this.locators.orderSummary.orderSummaryTitle)).toBeVisible(); + } + + async assertOrderSummaryProductName(productName) { + await expect(this.page.getByTestId(this.locators.orderSummary.orderSummaryProductName)).toContainText(productName); + } + + async assertOrderSummaryProductQuantity(quantity) { + await expect(this.page.getByTestId(this.locators.orderSummary.orderSummaryProductQuantity)).toContainText(quantity); + } + + async assertOrderSummaryProductPrice(price) { + await expect(this.page.getByTestId(this.locators.orderSummary.orderSummaryProductPrice)).toContainText(price); + } + + async assertOrderSummarySubtotalValue(subtotal) { + await expect(this.page.getByTestId(this.locators.orderSummary.subtotalValue)).toContainText(subtotal); + } + + async assertOrderSummaryShippingValue(shipping) { + await expect(this.page.getByTestId(this.locators.orderSummary.shippingValue)).toContainText(shipping); + } + + async assertOrderSummaryTotalValue(total) { + await expect(this.page.getByTestId(this.locators.orderSummary.totalValue)).toContainText(total); + } + +} + +export default OrderDetailsPage; \ No newline at end of file diff --git a/pages/OrderPage.js b/pages/OrderPage.js new file mode 100644 index 0000000..c2525c8 --- /dev/null +++ b/pages/OrderPage.js @@ -0,0 +1,94 @@ +import BasePage from './BasePage.js'; +import { expect } from '@playwright/test'; + +class OrderPage extends BasePage { + + /** + * @param {import('@playwright/test').Page} page + */ + constructor(page) { + super(page); + this.page = page; + } + + locators = { + myOrdersTab: "//p[normalize-space()='My Orders']", + myOrdersTitle: "h2[data-testid='my-orders-title']", + viewDetailsButton: "(//button[normalize-space()='View'])[1]", + orderDetailsTitle: "//h1[normalize-space()='Order Details']", + orderItemName: "p[data-testid='order-item-name']", + orderItemQuantity: "p:has-text('Qty:')", + orderTotalValue: "p[data-testid='total-value']", + orderStatusDisplay: "div[class*='badge']", + cancelOrderButton: "button:has-text('Cancel')", + confirmCancellationButton: "//button[normalize-space()='Yes, Cancel Order']", + toasterMessage: "div[id='_rht_toaster'] > div > div", + paginationButton: "//button[normalize-space()='{}']", + productNameInOrderList: "h3[normalize-space()='{}']", + priceAndQuantityInOrderList: "div[normalize-space()='{}']", + orderStatusInList: "div[normalize-space()='{}']", + myOrdersCount: "span[data-testid='my-orders-count']", + } + + async clickOnMyOrdersTab() { + await this.page.locator(this.locators.myOrdersTab).click({ force: true }); + } + + async verifyMyOrdersTitle() { + await expect(this.page.locator(this.locators.myOrdersTitle)).toBeVisible(); + } + + async clickViewDetailsButton(orderIndex = 1) { + await this.page.locator(`(//button[normalize-space()='View'])[${orderIndex}]`).click(); + } + + async verifyOrderDetailsTitle() { + await expect(this.page.locator(this.locators.orderDetailsTitle)).toBeVisible(); + } + + async verifyOrderSummary(productName, quantity, amount, status) { + await expect(this.page.locator(this.locators.orderItemName)).toHaveText(productName); + await expect(this.page.locator(this.locators.orderItemQuantity)).toContainText(`Qty: ${quantity}`); + await expect(this.page.locator(this.locators.orderTotalValue)).toContainText(amount); + await expect(this.page.locator(this.locators.orderStatusDisplay)).toHaveText(status); + } + + async clickCancelOrderButton(buttonIndex = 1) { + await this.page.locator(`(//button[normalize-space()='Cancel'])[${buttonIndex}]`).click({ force: true }); + } + + async confirmCancellation() { + await this.page.locator(this.locators.confirmCancellationButton).click({ force: true }); + } + + async verifyCancellationConfirmationMessage() { + await expect(this.page.locator(this.locators.toasterMessage)).toBeVisible(); + await expect(this.page.locator(this.locators.toasterMessage)).toContainText('canceled successfully'); + } + + async verifyOrderStatusIsCanceled(productName) { + await expect(this.page.locator(`//h3[normalize-space()='${productName}']`).locator('xpath=./ancestor::div[contains(@class, "card")]//div[contains(@class, "badge")]')).toHaveText('Canceled'); + } + + async clickOnPaginationButton(pageNumber) { + await this.page.locator(this.locators.paginationButton.replace('{}', pageNumber)).click({ force: true }); + } + + async verifyProductInOrderList(productName) { + await expect(this.page.locator(this.locators.productNameInOrderList.replace('{}', productName))).toBeVisible(); + } + + async verifyPriceAndQuantityInOrderList(priceAndQuantity) { + await expect(this.page.locator(this.locators.priceAndQuantityInOrderList.replace('{}', priceAndQuantity))).toBeVisible(); + } + + async verifyOrderStatusInList(status, productName) { + await expect(this.page.locator(`//h3[normalize-space()='${productName}']`).locator('xpath=./ancestor::div[contains(@class, "card")]//div[contains(@class, "badge")]')).toHaveText(status); + } + + async verifyMyOrdersCount() { + await expect(this.page.locator(this.locators.myOrdersCount)).toBeVisible(); + } +} + +export default OrderPage; diff --git a/pages/ProductDetailsPage.js b/pages/ProductDetailsPage.js new file mode 100644 index 0000000..21ca8f2 --- /dev/null +++ b/pages/ProductDetailsPage.js @@ -0,0 +1,63 @@ +import BasePage from './BasePage.js'; +import { expect } from '@playwright/test'; + +class ProductDetailsPage extends BasePage{ + + /** + * @param {import('@playwright/test').Page} page + */ + constructor(page) { + super(page); + this.page = page; + } + + locators = { + plusIconToAddQuantity: '[aria-label="plus"]', + totalQuantity: '[aria-label="minus"] + div', + addToCartButton: 'ADD TO CART', + headerCartIcon: 'header-cart-icon' + } + + async assertProductNameTitle(productName) { + await expect(this.page.locator(`//h1[text()="${productName}"]`).first()).toBeVisible(); + } + + async assertProductReviewCount(productName, productReviewCount) { + await expect(this.page.locator(`//h1[text()="${productName}"]/following-sibling::div/p`).first()).toContainText(productReviewCount); + } + + async assertProductPrice(productName, productPrice) { + await expect(this.page.locator(`//h1[text()="${productName}"]/following-sibling::p[contains(@class, 'font-medium')]`).first()).toContainText(productPrice); + } + + getPlusIconToAddQuantity() { + return this.page.locator(this.locators.plusIconToAddQuantity) + } + + async clickPlusIconToAddQuantity() { + await this.getPlusIconToAddQuantity().click(); + } + + getTotalQuantity() { + return this.page.locator(this.locators.totalQuantity) + } + + getAddToCartButton() { + return this.page.getByText(this.locators.addToCartButton) + } + + async clickAddToCartButton() { + await this.getAddToCartButton().click(); + await expect(this.page.getByText('Added to the cart')).toBeVisible(); + } + + getCartIcon() { + return this.page.getByTestId(this.locators.headerCartIcon) + } + + async clickCartIcon() { + await this.getCartIcon().click(); + } +} + +export default ProductDetailsPage; \ No newline at end of file diff --git a/pages/SignupPage.js b/pages/SignupPage.js new file mode 100644 index 0000000..899b1d4 --- /dev/null +++ b/pages/SignupPage.js @@ -0,0 +1,76 @@ +import BasePage from './BasePage.js'; +import { expect } from '@playwright/test'; + +class SignupPage extends BasePage{ + + /** + * @param {import('@playwright/test').Page} page + */ + constructor(page) { + super(page); + this.page = page; + } + + locators = { + signupPageTitle: `//h2[text()=' Create Account']`, + firstName: `#firstname`, + lastName: `#lastname`, + email: `#email`, + password: `#password`, + signupButton: `//button[text()='Create Account']`, + successSignupMessage: `Account created successfully! Please login to continue.`, + } + + async navigateToSignupPage() { + await this.navigateTo('/signup'); + } + + getSignupPageTitle() { + return this.page.locator(this.locators.signupPageTitle); + } + + getFirstNameInput() { + return this.page.locator(this.locators.firstName); + } + + getLastNameInput() { + return this.page.locator(this.locators.lastName); + } + + getEmailInput() { + return this.page.locator(this.locators.email); + } + + getPasswordInput() { + return this.page.locator(this.locators.password); + } + + getSignupButton() { + return this.page.locator(this.locators.signupButton); + } + + + async assertSignupPage() { + await expect(this.getSignupPageTitle()).toBeVisible(); + await expect(this.getFirstNameInput()).toBeVisible(); + await expect(this.getLastNameInput()).toBeVisible(); + await expect(this.getEmailInput()).toBeVisible(); + await expect(this.getPasswordInput()).toBeVisible(); + await expect(this.getSignupButton()).toBeVisible(); + } + + async signup(firstName, lastName, email, password) { + await this.page.fill(this.locators.firstName, firstName); + await this.page.fill(this.locators.lastName, lastName); + await this.page.fill(this.locators.email, email); + await this.page.fill(this.locators.password, password); + await this.page.click(this.locators.signupButton); + await this.page.waitForTimeout(2000); + } + + async verifySuccessSignUp() { + await expect(this.page.getByText(this.locators.successSignupMessage)).toBeVisible({ timeout: 10000 }); + } +} + +export default SignupPage; \ No newline at end of file diff --git a/pages/UserPage.js b/pages/UserPage.js index 88f1ef5..aa78c9a 100644 --- a/pages/UserPage.js +++ b/pages/UserPage.js @@ -1,121 +1,165 @@ +// UserPage.js import BasePage from './BasePage.js'; import { expect } from '@playwright/test'; class UserPage extends BasePage { + /** + * @param {import('@playwright/test').Page} page + */ + constructor(page) { + super(page); + this.page = page; + } - /** - * @param {import('@playwright/test').Page} page - */ - constructor(page) { - super(page); - this.page = page; - } - - locators = { - loginPageTitle: `//h2[text()=' Sign In']`, - userName: `[placeholder="Your email address"]`, - password: `[placeholder="Your password"]`, - loginButton: `//button[text()='Sign in']`, - invalidLoginError: '[data-test="error"]', - userIcon: `//*[name()='svg'][.//*[name()='path' and contains(@d,'M25.1578 1')]]`, - logoutButton: `//p[text()='Log Out']`, - addressTab: `//p[text()='Addresses']`, - addAddressButton: `//button[text()='Add New Address']`, - firstName: `[name="firstname"]`, - lastName: `[name="lastName"]`, - contactNumber: `[name="contactNumber"]`, - email: `[name="email"]`, - address: `[name="street"]`, - city: `[name="city"]`, - state: `[name="state"]`, - country: `[name="country"]`, - zip: `[name="zipCode"]`, - saveAddressButton: `//button[text()='Save']`, - addressLocator: `h3.font-medium`, - emailLocator: `p.text-gray-500.text-sm`, - editAddressButton: `(//*[@data-icon='edit'])[1]`, - deleteAddressButton: `(//*[@data-icon='delete'])[1]`, - detetebutton: `//button[normalize-space(text())='Delete']`, - updateAddressButton: `//button[text()='Update']`, - savePersonalInfo:`[aria-label="save"]` - - } - - async clickOnAddressTab() { - await this.page.locator(this.locators.addressTab).click(); - } - async clickOnAddAddressButton() { - await this.page.locator(this.locators.addAddressButton).click(); - } - - async clickOnUserProfileIcon() { - await this.page.locator(this.locators.userIcon).click(); - } - - async fillAddressForm() { - await this.page.locator(this.locators.firstName).fill('ATest'); - await this.page.locator(this.locators.email).fill('john.doe@example.com'); - await this.page.locator(this.locators.address).fill('123 Main St'); - await this.page.locator(this.locators.city).fill('Anytown'); - await this.page.locator(this.locators.state).fill('CA'); - await this.page.locator(this.locators.country).fill('United States'); - await this.page.locator(this.locators.zip).fill('12345'); - await this.page.locator(this.locators.saveAddressButton).click(); - } - - async verifytheAddressIsAdded() { - const addressLocator = this.page.locator(this.locators.addressLocator); - const targetAddress = addressLocator.nth(2); - await expect(targetAddress).toBeVisible(); - await expect(targetAddress).toHaveText("ATest"); - } - - async clickOnEditAddressButton() { - await this.page.locator(this.locators.editAddressButton).click(); - } - - async updateAddressForm() { - await this.page.locator(this.locators.firstName).fill('Test1'); - await this.page.locator(this.locators.email).fill('john.doe@example.com'); - await this.page.locator(this.locators.address).fill('123 Main St'); - await this.page.locator(this.locators.city).fill('Anytown'); - await this.page.locator(this.locators.state).fill('CA'); - await this.page.locator(this.locators.country).fill('United States'); - await this.page.locator(this.locators.zip).fill('12345'); - await this.page.locator(this.locators.updateAddressButton).click(); - } - - async verifytheUpdatedAddressIsAdded() { - const addressLocator = this.page.locator(this.locators.addressLocator); - const targetAddress = addressLocator.nth(1); - await expect(targetAddress).toBeVisible(); - await expect(targetAddress).toHaveText("Test1"); - } - - async clickOnDeleteAddressButton() { - const addressLocator = this.page.locator(this.locators.addressLocator); - const targetAddress = addressLocator.nth(1); - await expect(targetAddress).toBeVisible(); - await expect(targetAddress).toHaveText("Test1"); - await this.page.locator(this.locators.deleteAddressButton).click(); - await this.page.locator(this.locators.detetebutton).click(); - await expect(targetAddress).not.toContainText("Test1"); - } - async updatePersonalInfo() { - await this.page.locator(this.locators.firstName).fill('Test1'); - await this.page.locator(this.locators.lastName).fill('Testing'); - await this.page.locator(this.locators.contactNumber).fill('9999999999'); - await this.page.locator(this.locators.savePersonalInfo).click(); - - } - async verifyPersonalInfoUpdated() { - await this.page.reload(); - // Verify the updated values are displayed in the fields - await expect(this.page.locator(this.locators.firstName)).toHaveValue('Test1'); - await expect(this.page.locator(this.locators.lastName)).toHaveValue('Testing'); - await expect(this.page.locator(this.locators.contactNumber)).toHaveValue('9999999999'); - } + // Single source of truth for selectors (prefer data-testid where available) + locators = { + // Header / user menu + userIcon: `//*[name()='svg'][.//*[name()='path' and contains(@d,'M25.1578 1')]]`, + logoutButton: `//p[text()='Log Out']`, + // Profile tabs & sections + addressTab: `(//*[@data-testid="menu-item-label"])[3]`, + addAddressButton: `[data-testid="add-new-address-button"]`, + addNewAddressMenu: `//h2[text()='Add New Address']`, + + // Address form (data-testid) + addressingFirstName: `[data-testid="first-name-input"]`, + addressingEmail: `[data-testid="email-input"]`, + streetAddress: `[data-testid="street-address-input"]`, + cityInput: `[data-testid="city-input"]`, + stateInput: `[data-testid="state-input"]`, + countryInput: `[data-testid="country-input"]`, + zipCodeInput: `[data-testid="zip-code-input"]`, + saveAddressButton: `[data-testid="save-address-button"]`, + + // Address cards list (generic selector for names) + addressCardName: `[data-testid="address-name"]`, + editAddressButton: `(//*[@data-icon='edit'])[1]`, + deleteAddressButton: `(//*[@data-icon='delete'])[1]`, + confirmDeleteButton: `//button[normalize-space(text())='Delete']`, + updateAddressButton: `//button[text()='Update']`, + + // Personal info section (profile form) + firstName: `[name="firstname"]`, + lastName: `[name="lastName"]`, + contactNumber: `[name="contactNumber"]`, + savePersonalInfo: `[aria-label="save"]`, + + // Security / password change + securityButton: `//button[text()="Security"]`, + enterNewPassword: `[placeholder="Enter new password"]`, + confirmNewPassword: `[placeholder="Confirm your password"]`, + updatePasswordButton: `[data-testid="my-profile-reset-password-button"]`, + updateNotification: `div[role="status"][aria-live="polite"]`, + }; + + /* ----------------------------- + * Generic helpers used in tests + * ----------------------------- */ + async clickOnUserProfileIcon() { + await this.page.locator(this.locators.userIcon).click(); + } + + /* ---------- Addresses ---------- */ + async clickOnAddressTab() { + await this.page.locator(this.locators.addressTab).click(); + } + + async clickOnAddAddressButton() { + await this.page.locator(this.locators.addAddressButton).click(); + } + + async checkAddNewAddressMenu() { + await expect(this.page.locator(this.locators.addNewAddressMenu)).toBeVisible(); + } + + async fillAddressForm() { + await this.page.locator(this.locators.addressingFirstName).fill('Tester'); + await this.page.locator(this.locators.addressingEmail).fill('testing123@example.com'); + await this.page.locator(this.locators.streetAddress).fill('SBP, Utran'); + await this.page.locator(this.locators.cityInput).fill('Surat'); + await this.page.locator(this.locators.stateInput).fill('Gujarat'); + await this.page.locator(this.locators.countryInput).fill('India'); + await this.page.locator(this.locators.zipCodeInput).fill('12345'); + await this.page.locator(this.locators.saveAddressButton).click(); + } + + async verifytheAddressIsAdded() { + // Assert at least one address card with the name we just saved is visible + const name = this.page.locator(this.locators.addressCardName); + await expect(name).toBeVisible(); + } + + async clickOnEditAddressButton() { + await this.page.locator(this.locators.editAddressButton).click(); + } + + async updateAddressForm() { + // Update to a new first name and keep other fields valid + await this.page.locator(this.locators.addressingFirstName).fill('Test1'); + await this.page.locator(this.locators.addressingEmail).fill('john.doe@example.com'); + await this.page.locator(this.locators.streetAddress).fill('123 Main St'); + await this.page.locator(this.locators.cityInput).fill('Anytown'); + await this.page.locator(this.locators.stateInput).fill('CA'); + await this.page.locator(this.locators.countryInput).fill('United States'); + await this.page.locator(this.locators.zipCodeInput).fill('12345'); + await this.page.locator(this.locators.updateAddressButton).click(); + } + + async verifytheUpdatedAddressIsAdded() { + await expect(this.page.locator(this.locators.addressCardName)).toContainText('Test1'); + } + + async clickOnDeleteAddressButton() { + // Ensure the address exists before deleting + await expect(this.page.locator(this.locators.addressCardName)).toContainText('Test1'); + await this.page.locator(this.locators.deleteAddressButton).click(); + await this.page.locator(this.locators.confirmDeleteButton).click(); + } + + /* ------- Personal Info -------- */ + async updatePersonalInfo() { + await this.page.locator(this.locators.firstName).fill('Test1'); + await this.page.locator(this.locators.lastName).fill('Testing'); + await this.page.locator(this.locators.contactNumber).fill('9999999999'); + await this.page.locator(this.locators.savePersonalInfo).click(); + } + + async verifyPersonalInfoUpdated() { + await this.page.reload(); + await expect(this.page.locator(this.locators.firstName)).toHaveValue('Test1'); + await expect(this.page.locator(this.locators.lastName)).toHaveValue('Testing'); + await expect(this.page.locator(this.locators.contactNumber)).toHaveValue('9999999999'); + } + + /* --------- Security (PW) ------ */ + async clickOnSecurityButton() { + await this.page.locator(this.locators.securityButton).click(); + } + + async enterNewPassword() { + await this.page.locator(this.locators.enterNewPassword).fill(process.env.NEW_PASSWORD); + } + + async enterConfirmNewPassword() { + await this.page.locator(this.locators.confirmNewPassword).fill(process.env.NEW_PASSWORD); + } + + async clickOnUpdatePasswordButton() { + await this.page.locator(this.locators.updatePasswordButton).click(); + } + + async revertPasswordBackToOriginal() { + await this.page.locator(this.locators.enterNewPassword).fill(process.env.PASSWORD); + await this.page.locator(this.locators.confirmNewPassword).fill(process.env.PASSWORD); + await this.page.locator(this.locators.updatePasswordButton).click(); + } + + async getUpdatePasswordNotification() { + const toast = this.page.locator(this.locators.updateNotification); + await expect(toast).toBeVisible(); + await expect(toast).toHaveText(/Password updated successfully/i); + } } -export default UserPage; \ No newline at end of file +export default UserPage; diff --git a/playwright.config.js b/playwright.config.js index 2bec931..0d0990a 100644 --- a/playwright.config.js +++ b/playwright.config.js @@ -35,4 +35,4 @@ export default defineConfig({ use: { ...devices['Desktop Chrome'] }, }, ], -}); +}); \ No newline at end of file diff --git a/tests/example.spec.js b/tests/example.spec.js index 7d6c2b8..6e8f6d0 100644 --- a/tests/example.spec.js +++ b/tests/example.spec.js @@ -1,86 +1,269 @@ // @ts-check -import { test } from '@playwright/test'; +import { expect, test } from '@playwright/test'; import AllPages from '../pages/AllPages.js'; import dotenv from 'dotenv'; -dotenv.config(); - -test.describe('Login to the application', () => { - /** @type {AllPages} */ - let allPages; - - test.beforeEach(async ({ page }) => { - allPages = new AllPages(page); - }) - - test('Verify that user can login and logout successfully', async ({ page }) => { - await test.step('Navigate to the login page and login with valid credentials', async () => { - await page.goto('http://demo.alphabin.co'); - await allPages.loginPage.clickOnUserProfileIcon(); - await allPages.loginPage.validateSignInPage(); - // await allPages.loginPage.navigateToLoginPage(); - await allPages.loginPage.login(process.env.USERNAME, process.env.PASSWORD); - }) - await test.step('again navigate to user profile icon and click on logout button', async () => { - await allPages.loginPage.clickOnUserProfileIcon(); - await allPages.loginPage.clickOnLogoutButton(); - }) - }) - - test('Verify that User Can Complete the Journey from Login to Order Placement', async ({ page }) => { - await test.step('Login to the application', async () => { - await page.goto('http://demo.alphabin.co'); - await allPages.loginPage.clickOnUserProfileIcon(); - await allPages.loginPage.validateSignInPage(); - await allPages.loginPage.login(process.env.USERNAME, process.env.PASSWORD); - }) - await test.step('Navigate to the product page and add a product to the cart', async () => { - await allPages.inventoryPage.clickOnShopNowButton(); - - }) - }) - test('Verify that User Can Add, Edit, and Delete Addresses after Logging In', async ({ page }) => { - await test.step('Login to the application', async () => { - await page.goto('http://demo.alphabin.co'); - await allPages.userPage.clickOnUserProfileIcon(); - await allPages.loginPage.validateSignInPage(); - await allPages.loginPage.login(process.env.USERNAME, process.env.PASSWORD); - }) - await test.step('Navigate to the user porifle page and add a new address', async () => { - await allPages.userPage.clickOnUserProfileIcon(); - await allPages.userPage.clickOnAddressTab(); - await allPages.userPage.clickOnAddAddressButton(); - await allPages.userPage.fillAddressForm(); - await allPages.userPage.verifytheAddressIsAdded(); - }) - await test.step('Navigate to the user porifle page and edit the address', async () => { - await allPages.userPage.clickOnUserProfileIcon(); - await allPages.userPage.clickOnAddressTab(); - await allPages.userPage.clickOnEditAddressButton(); - await allPages.userPage.updateAddressForm(); - await allPages.userPage.verifytheUpdatedAddressIsAdded(); - }) - await test.step('Navigate to the user porifle page and delete the address', async () => { - await allPages.userPage.clickOnUserProfileIcon(); - await allPages.userPage.clickOnAddressTab(); - await allPages.userPage.clickOnDeleteAddressButton(); - }) - }) - - test('Verify that user can update personal information', async ({ page }) => { - await test.step('Login to the application', async () => { - await page.goto('http://demo.alphabin.co'); - await allPages.userPage.clickOnUserProfileIcon(); - await allPages.loginPage.validateSignInPage(); - await allPages.loginPage.login(process.env.USERNAME, process.env.PASSWORD); - }) - await test.step('Navigate to the user porifle page', async () => { - await allPages.userPage.clickOnUserProfileIcon(); - }) - await test.step("Update First Name, Last Name, Contact Number and save", async () => { - await allPages.userPage.updatePersonalInfo(); - }) - await test.step("Verify the updated values are displayed in the fields", async () => { - await allPages.userPage.verifyPersonalInfoUpdated(); - }) - }) -}) +dotenv.config({ override: true }); + +let allPages; + +test.beforeEach(async ({ page }) => { + allPages = new AllPages(page); + await page.goto('/'); +}); + +async function login(username = process.env.USERNAME, password = process.env.PASSWORD) { + await allPages.loginPage.clickOnUserProfileIcon(); + await allPages.loginPage.validateSignInPage(); + await allPages.loginPage.login(username, password); +} + +async function login1(username = process.env.USERNAME1, password = process.env.PASSWORD) { + await allPages.loginPage.clickOnUserProfileIcon(); + await allPages.loginPage.validateSignInPage(); + await allPages.loginPage.login(username, password); +} + +async function logout() { + await allPages.loginPage.clickOnUserProfileIcon(); + await allPages.loginPage.clickOnLogoutButton(); +} + +// ---------------- LOGIN ---------------- +test('Verify that user can login and logout successfully', async () => { + await login(); + await logout(); +}); + +// ---------------- PROFILE ---------------- +test('Verify that user can update personal information', async () => { + await login(); + await allPages.userPage.clickOnUserProfileIcon(); + await allPages.userPage.updatePersonalInfo(); + await allPages.userPage.verifyPersonalInfoUpdated(); +}); + +test('Verify that User Can Add, Edit, and Delete Addresses after Logging In', async () => { + await login(); + + // Add + await allPages.userPage.clickOnUserProfileIcon(); + await allPages.userPage.clickOnAddressTab(); + await allPages.userPage.clickOnAddAddressButton(); + await allPages.userPage.fillAddressForm(); + await allPages.userPage.verifytheAddressIsAdded(); + + // Edit + await allPages.userPage.clickOnEditAddressButton(); + await allPages.userPage.updateAddressForm(); + await allPages.userPage.verifytheUpdatedAddressIsAdded(); + + // Delete + await allPages.userPage.clickOnDeleteAddressButton(); +}); + +test('Verify that user can change password successfully', async () => { + + await login1(); + // Change password + await allPages.userPage.clickOnUserProfileIcon(); + await allPages.userPage.clickOnSecurityButton(); + await allPages.userPage.enterNewPassword(); + await allPages.userPage.enterConfirmNewPassword(); + await allPages.userPage.clickOnUpdatePasswordButton(); + await allPages.userPage.getUpdatePasswordNotification(); + + // Re-login with new password + await logout(); + await allPages.loginPage.login(process.env.USERNAME1, process.env.NEW_PASSWORD); + + // Revert back + await allPages.userPage.clickOnUserProfileIcon(); + await allPages.userPage.clickOnSecurityButton(); + await allPages.userPage.revertPasswordBackToOriginal(); + await allPages.userPage.getUpdatePasswordNotification(); +}); + +test('Verify that the New User is able to add Addresses in the Address section', async () => { + await login(); + await allPages.userPage.clickOnUserProfileIcon(); + await allPages.userPage.clickOnAddressTab(); + await allPages.userPage.clickOnAddAddressButton(); + await allPages.userPage.checkAddNewAddressMenu(); + await allPages.userPage.fillAddressForm(); +}); + +// ---------------- ORDERS (LOGGED-IN) ---------------- +test('Verify that User Can Complete the Journey from Login to Order Placement', async () => { + const productName = 'GoPro HERO10 Black'; + await login(); + await allPages.inventoryPage.clickOnShopNowButton(); + await allPages.inventoryPage.clickOnAllProductsLink(); + await allPages.inventoryPage.searchProduct(productName); + await allPages.inventoryPage.verifyProductTitleVisible(productName); + await allPages.inventoryPage.clickOnAddToCartIcon(); +}); + +test('Verify user can place and cancel an order', async () => { + const productName = 'GoPro HERO10 Black'; + const productPriceAndQuantity = '₹49,999 × 1'; + const productQuantity = '1'; + const orderStatusProcessing = 'Processing'; + const orderStatusCanceled = 'Canceled'; + + // Login and add product + await login(); + await allPages.inventoryPage.clickOnAllProductsLink(); + await allPages.inventoryPage.searchProduct(productName); + await allPages.inventoryPage.verifyProductTitleVisible(productName); + await allPages.inventoryPage.clickOnAddToCartIcon(); + + // Cart and checkout + await allPages.cartPage.clickOnCartIcon(); + await allPages.cartPage.verifyCartItemVisible(productName); + await allPages.cartPage.clickOnCheckoutButton(); + + // Place order (COD) + await allPages.checkoutPage.verifyCheckoutTitle(); + await allPages.checkoutPage.verifyProductInCheckout(productName); + await allPages.checkoutPage.selectCashOnDelivery(); + await allPages.checkoutPage.verifyCashOnDeliverySelected(); + await allPages.checkoutPage.clickOnPlaceOrder(); + await allPages.checkoutPage.verifyOrderPlacedSuccessfully(); + await allPages.checkoutPage.verifyOrderItemName(productName); + await allPages.inventoryPage.clickOnContinueShopping(); + + // Go to My Orders and verify + await allPages.loginPage.clickOnUserProfileIcon(); + await allPages.orderPage.clickOnMyOrdersTab(); + await allPages.orderPage.verifyMyOrdersTitle(); + await allPages.orderPage.clickOnPaginationButton(2); + await allPages.orderPage.verifyProductInOrderList(productName); + await allPages.orderPage.verifyPriceAndQuantityInOrderList(productPriceAndQuantity); + await allPages.orderPage.verifyOrderStatusInList(orderStatusProcessing, productName); + await allPages.orderPage.clickOnPaginationButton(1); + await allPages.orderPage.clickViewDetailsButton(1); + await allPages.orderPage.verifyOrderDetailsTitle(); + await allPages.orderPage.verifyOrderSummary(productName, productQuantity, '₹49,999', orderStatusProcessing); + + // Cancel and verify cancellation + await allPages.orderPage.clickCancelOrderButton(2); + await allPages.orderPage.confirmCancellation(); + await allPages.orderPage.verifyCancellationConfirmationMessage(); + await allPages.orderPage.verifyMyOrdersCount(); + await allPages.orderPage.clickOnMyOrdersTab(); + await allPages.orderPage.verifyMyOrdersTitle(); + await allPages.orderPage.clickOnPaginationButton(2); + await allPages.orderPage.verifyOrderStatusInList(orderStatusCanceled, productName); +}); + +// ---------------- FULL JOURNEY (SIGNUP → ORDER) ---------------- +test('Verify that a New User Can Successfully Complete the Journey from Registration to a Single Order Placement', async () => { + // fresh test data + const email = `test+${Date.now()}@test.com`; + const firstName = 'Test'; + const lastName = 'User'; + + let productName; + let productPrice; + let productReviewCount; + + // Signup + await allPages.loginPage.clickOnUserProfileIcon(); + await allPages.loginPage.validateSignInPage(); + await allPages.loginPage.clickOnSignupLink(); + await allPages.signupPage.assertSignupPage(); + await allPages.signupPage.signup(firstName, lastName, email, process.env.PASSWORD); + await allPages.signupPage.verifySuccessSignUp(); + + // Login as new user + await allPages.loginPage.validateSignInPage(); + await allPages.loginPage.login(email, process.env.PASSWORD); + await allPages.loginPage.verifySuccessSignIn(); + await expect(allPages.homePage.getHomeNav()).toBeVisible({ timeout: 30000 }); + + // Navigate to All Products + await allPages.homePage.clickAllProductsNav(); + await allPages.allProductsPage.assertAllProductsTitle(); + + // Choose first product and wishlist + productName = await allPages.allProductsPage.getNthProductName(1); + productPrice = await allPages.allProductsPage.getNthProductPrice(1); + productReviewCount = await allPages.allProductsPage.getNthProductReviewCount(1); + + await allPages.allProductsPage.clickNthProductWishlistIcon(1); + await expect(allPages.allProductsPage.getNthProductWishlistIconCount(1)).toContainText('1'); + await allPages.allProductsPage.clickNthProduct(1); + + // Verify product details + await allPages.productDetailsPage.assertProductNameTitle(productName); + await allPages.productDetailsPage.assertProductPrice(productName, productPrice); + await allPages.productDetailsPage.assertProductReviewCount(productName, productReviewCount); + await expect(allPages.allProductsPage.getNthProductWishlistIconCount(1)).toContainText('1'); + + // Add to cart + await allPages.productDetailsPage.clickAddToCartButton(); + + // Cart checks and proceed to checkout + await allPages.productDetailsPage.clickCartIcon(); + await allPages.cartPage.assertYourCartTitle(); + await expect(allPages.cartPage.getCartItemName()).toContainText(productName, { timeout: 10000 }); + await expect(allPages.cartPage.getCartItemPrice()).toContainText(productPrice); + await expect(allPages.cartPage.getCartItemQuantity()).toContainText('1'); + await allPages.cartPage.clickIncreaseQuantityButton(); + await expect(allPages.cartPage.getCartItemQuantity()).toContainText('2'); + + const cleanPrice = productPrice.replace(/[₹,]/g, ''); + const priceValue = parseFloat(cleanPrice) * 2; + await expect(allPages.cartPage.getTotalValue()).toContainText( + priceValue.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',') + ); + await allPages.cartPage.clickOnCheckoutButton(); + + // Fill shipping address and save + await allPages.checkoutPage.verifyCheckoutTitle(); + await allPages.checkoutPage.fillShippingAddress( + firstName, email, 'New York', 'New York', '123 Main St', '10001', 'United States' + ); + await allPages.checkoutPage.clickSaveAddressButton(); + await allPages.checkoutPage.assertAddressAddedToast(); + + // COD, verify summary, place order + await allPages.checkoutPage.selectCashOnDelivery(); + await allPages.checkoutPage.verifyCheckoutTitle(); + await allPages.checkoutPage.assertOrderSummaryTitle(); + await expect(allPages.checkoutPage.getOrderSummaryImage()).toBeVisible(); + await expect(allPages.checkoutPage.getOrderSummaryProductName()).toContainText(productName); + await allPages.checkoutPage.verifyProductInCheckout(productName); + await expect(allPages.checkoutPage.getOrderSummaryProductQuantity()).toContainText('2'); + await expect(allPages.checkoutPage.getOrderSummaryProductPrice()).toContainText(productPrice); + + const subtotalValue = parseFloat(cleanPrice) * 2; + const formattedSubtotal = subtotalValue.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ','); + await expect(await allPages.checkoutPage.getOrderSummarySubtotalValue()).toContain(formattedSubtotal); + await expect(allPages.checkoutPage.getOrderSummaryShippingValue()).toContainText('Free'); + await allPages.checkoutPage.clickOnPlaceOrder(); + + // Order details and return to home + await allPages.orderDetailsPage.assertOrderDetailsTitle(); + await allPages.orderDetailsPage.assertOrderPlacedName(); + await allPages.orderDetailsPage.assertOrderPlacedMessage(); + await allPages.orderDetailsPage.assertOrderPlacedDate(); + await allPages.orderDetailsPage.assertOrderInformationTitle(); + await allPages.orderDetailsPage.assertOrderConfirmedTitle(); + await allPages.orderDetailsPage.assertOrderConfirmedMessage(); + await allPages.orderDetailsPage.assertShippingDetailsTitle(); + await allPages.orderDetailsPage.assertShippingEmailValue(email); + await allPages.orderDetailsPage.assertPaymentMethodAmount(formattedSubtotal); + await allPages.orderDetailsPage.assertDeliveryAddressLabel(); + await allPages.orderDetailsPage.assertDeliveryAddressValue(); + await allPages.orderDetailsPage.assertContinueShoppingButton(); + + await allPages.orderDetailsPage.assertOrderSummaryTitle(); + await allPages.orderDetailsPage.assertOrderSummaryProductName(productName); + await allPages.orderDetailsPage.assertOrderSummaryProductQuantity('2'); + await allPages.orderDetailsPage.assertOrderSummaryProductPrice(productPrice); + await allPages.orderDetailsPage.assertOrderSummarySubtotalValue(formattedSubtotal); + await allPages.orderDetailsPage.assertOrderSummaryShippingValue('Free'); + await allPages.orderDetailsPage.assertOrderSummaryTotalValue(formattedSubtotal); + await allPages.orderDetailsPage.clickBackToHomeButton(); +});