Test Task

main
Yevhen Unico 1 year ago
commit 2315dd4c49

@ -0,0 +1 @@
cPanelLicensesPageUrl=https://store.cpanel.net/store/cpanel-licenses

4
.gitignore vendored

@ -0,0 +1,4 @@
node_modules/
test-results/
playwright-report/
logs/

@ -0,0 +1,11 @@
export class cPanelLicensesPage {
static orderNowButton = (index: number) => `(//*[@class='btn btn-success btn-sm btn-order-now'])[${index}]`;
static indexMap: { [key: string]: number } = {
'cPanel Solo® Cloud (1 Account)': 1,
'cPanel Admin Cloud (5 Accounts)': 2,
'cPanel Pro Cloud (30 Accounts)': 3,
'cPanel Premier (100 Accounts)': 4,
'WP Squared': 5,
};
}

@ -0,0 +1,8 @@
export class CheckoutPage {
static personalInformationBlock = "(//div[@id='containerNewUserSignup']//div[@class='row'])[1]"
static billingAddressBlock = "(//div[@id='containerNewUserSignup']//div[@class='row'])[2]"
static accountSecurity = "//div[@id='containerNewUserSecurity']"
static termsAndConditionsBlock = "//div[contains(@class, 'sub-heading') and .//span[text()='Terms & Conditions']]/following-sibling::div"
static paymentDetailsBlock = "//*[@id='paymentGatewaysContainer'] | //*[@class='cc-input-container']"
static completeOrderButton = "//*[@id='btnCompleteOrder']"
}

@ -0,0 +1,34 @@
export class ProductConfigurePage {
static ipAddressField = "//*[@class='field-container']//input";
static ipAddressLoader = "//*[@class='float-right font-size-sm']";
static addToCartButton = (index: number) => `(//*[@class='panel-add'])[${index}]`;
static productNameInOrderSummaryBlock = `//*[@class='product-name']`;
static addonNameInOrderSummaryBlock = `(//div[@id='producttotal']/div[@class='clearfix']//span[@class='pull-left float-left'])[2]`;
static productPriceInOrderSummaryBlock = `(//div[@id='producttotal']/div[@class='clearfix']//span[@class='pull-right float-right'])[1]`;
static addonPriceInOrderSummaryBlock= `(//div[@id='producttotal']/div[@class='clearfix']//span[@class='pull-right float-right'])[2]`
static setupPriceInOrderSummaryBlock = `(//div[@class='summary-totals']//div[@class='clearfix']//span[@class='pull-right float-right'])[1]`;
static monthlyPriceInOrderSummaryBlock = `(//div[@class='summary-totals']//div[@class='clearfix']//span[@class='pull-right float-right'])[2]`;
static totalDueToday= `//*[@class='amt']`
static continueButton = `//*[@type='submit']`
static indexMap: { [key: string]: number } = {
'Monthly CloudLinux': 1,
'Monthly CloudLinux for cPanel License': 2,
'Monthly KernelCare License': 3,
'LiteSpeed 8GB': 4,
'LiteSpeed UNLIMITED': 5,
'JetBackup': 6,
'Monthly Imunify360 (Unlimited)': 7,
'Monthly ImunifyAV+': 8,
'WHMCS Plus': 9,
'WHMCS Professional': 10,
'WHMCS Business 1000': 11,
'WHMCS Business 2500': 12,
'WHMCS Business 5000': 13,
'WHMCS Business 10000': 14,
'WHMCS Business 50000': 15,
'WHMCS Business 100000': 16,
'WHMCS Business Unlimited': 17,
};
}

@ -0,0 +1,8 @@
export class ReviewAndCheckoutPage {
static productName = `(//*[@class='item-group'])[1]`
static productMonthlyPrice = `(//*[@class='col-sm-4 item-price']/span[@class='cycle'])[1]`
static addonMonthlyPrice = `(//*[@class='col-sm-4 item-price']/span[@class='cycle'])[2]`
static ipAddress = `//*[@class='col-sm-7']/small`
static totalDueToday = `//*[@class='total-due-today total-due-today-padded']`
static checkoutButton = `//*[@id='checkout']`
}

@ -0,0 +1,21 @@
import {Page} from '@playwright/test';
import {cPanelLicensesPage} from '../pages/cPanelLicensesPage';
import {config} from 'dotenv';
import {PageBase} from "../../utils/page-base";
config();
export class cPanelLicensesPageStepDefs extends PageBase {
constructor(page: Page) {
super(page);
}
async open() {
await this.navigateTo(process.env.cPanelLicensesPageUrl || '');
}
async clickOnOrderNowLicense(product: string): Promise<void> {
await this.page.click(cPanelLicensesPage.orderNowButton(cPanelLicensesPage.indexMap[product]));
await this.page.waitForLoadState()
}
}

@ -0,0 +1,36 @@
import { Page, expect } from "@playwright/test";
import { CheckoutPage } from "../pages/checkoutPage";
import { PageBase } from "../../utils/page-base";
export class CheckoutPageStepDefs extends PageBase {
constructor(page: Page) {
super(page);
}
async assertPersonalInformationBlockIsVisible(): Promise<void> {
await this.waitForElement(CheckoutPage.personalInformationBlock)
await expect(this.page.locator(CheckoutPage.personalInformationBlock)).toBeVisible();
}
async assertBillingAddressBlockIsVisible(): Promise<void> {
await expect(this.page.locator(CheckoutPage.billingAddressBlock)).toBeVisible();
}
async assertAccountSecurityBlockIsVisible(): Promise<void> {
await expect(this.page.locator(CheckoutPage.accountSecurity)).toBeVisible();
}
async assertTermsAndConditionsBlockIsVisible(): Promise<void> {
await expect(this.page.locator(CheckoutPage.termsAndConditionsBlock)).toBeVisible();
}
async assertPaymentDetailsBlockIsVisible(): Promise<void> {
await expect(this.page.locator(CheckoutPage.paymentDetailsBlock).nth(0)).toBeVisible();
await expect(this.page.locator(CheckoutPage.paymentDetailsBlock).nth(1)).toBeVisible();
}
async assertCompleteOrderButtonIsVisibleAndDisabled(): Promise<void> {
await expect(this.page.locator(CheckoutPage.completeOrderButton)).toBeVisible();
await expect(this.page.locator(CheckoutPage.completeOrderButton)).toBeDisabled();
}
}

@ -0,0 +1,67 @@
import {expect, Page} from '@playwright/test';
import {ProductConfigurePage} from "../pages/productConfigurePage";
import {PageBase} from "../../utils/page-base";
import {DateOperations} from "../../utils/dateOperations";
export class ProductConfigurePageStepDefs extends PageBase {
constructor(page: Page) {
super(page);
}
async fillIpAddressWithData(ipAddress: string): Promise<void> {
await this.waitForElement(ProductConfigurePage.ipAddressField)
await this.page.fill(ProductConfigurePage.ipAddressField, ipAddress);
await this.page.keyboard.press('Enter');
await this.waitForElement(ProductConfigurePage.ipAddressLoader, "hidden");
}
async clickOnAddToCartProduct(product: string): Promise<void> {
await this.page.click(ProductConfigurePage.addToCartButton(ProductConfigurePage.indexMap[product]));
await this.waitForElement(ProductConfigurePage.addonNameInOrderSummaryBlock);
}
async assertProductName(productName: string): Promise<void> {
await expect(this.page.locator(ProductConfigurePage.productNameInOrderSummaryBlock)).toHaveText(productName);
}
async assertAddonName(addonName: string): Promise<void> {
await expect(this.page.locator(ProductConfigurePage.addonNameInOrderSummaryBlock)).toHaveText("+ " + addonName);
}
async assertProductPrice(productPrice: string): Promise<void> {
await expect(this.page.locator(ProductConfigurePage.productPriceInOrderSummaryBlock)).toHaveText(productPrice);
}
async assertAddonPrice(addonPrice: string): Promise<void> {
await expect(this.page.locator(ProductConfigurePage.addonPriceInOrderSummaryBlock)).toHaveText(addonPrice);
}
async assertSetupFeePrice(feePrice: string): Promise<void> {
await expect(this.page.locator(ProductConfigurePage.setupPriceInOrderSummaryBlock)).toHaveText(feePrice);
}
async assertMonthlyPrice(): Promise<void> {
const productPrice = await DateOperations.extractPrice(this.page, ProductConfigurePage.productPriceInOrderSummaryBlock);
const addonPrice = await DateOperations.extractPrice(this.page, ProductConfigurePage.addonPriceInOrderSummaryBlock);
const setupPrice = await DateOperations.extractPrice(this.page, ProductConfigurePage.setupPriceInOrderSummaryBlock);
const monthlyPrice = await DateOperations.extractPrice(this.page, ProductConfigurePage.monthlyPriceInOrderSummaryBlock);
const totalCalculated = productPrice + addonPrice + setupPrice;
expect(totalCalculated).toBeCloseTo(monthlyPrice, 2);
}
async assertTotalDue(): Promise<void> {
const monthlyPrice = await DateOperations.extractPrice(this.page, ProductConfigurePage.monthlyPriceInOrderSummaryBlock);
const totalDueToday = await DateOperations.extractPrice(this.page, ProductConfigurePage.totalDueToday);
const expectedTotalDueToday = DateOperations.calculateTotalDue(monthlyPrice);
expect(totalDueToday).toBeCloseTo(expectedTotalDueToday, 2);
}
async clickOnContinue(): Promise<void> {
await this.page.click(ProductConfigurePage.continueButton);
}
}

@ -0,0 +1,50 @@
import { expect, Page } from '@playwright/test';
import { ReviewAndCheckoutPage } from "../pages/reviewAndCheckoutPage";
import {DateOperations} from "../../utils/dateOperations";
import {PageBase} from "../../utils/page-base";
export class ReviewAndCheckoutPageStepDefs extends PageBase {
constructor(page: Page) {
super(page);
}
async assertLicenseName(productLicenseName: string): Promise<void> {
await this.page.waitForLoadState()
await this.waitForElement(ReviewAndCheckoutPage.productName)
await expect(this.page.locator(ReviewAndCheckoutPage.productName)).toHaveText(productLicenseName);
}
async assertIpAddress(ipAddress: string): Promise<void> {
await this.waitForElement(ReviewAndCheckoutPage.productName)
await expect(this.page.locator(ReviewAndCheckoutPage.ipAddress)).toHaveText("» IP Address: " + ipAddress);
}
async assertProductMonthlyPrice(productMonthlyPrice: string): Promise<void> {
await expect(this.page.locator(ReviewAndCheckoutPage.productMonthlyPrice)).toHaveText(productMonthlyPrice+ " Monthly");
}
async assertAddonMonthlyPrice(addonMonthlyPrice: string): Promise<void> {
await expect(this.page.locator(ReviewAndCheckoutPage.addonMonthlyPrice)).toHaveText(addonMonthlyPrice + " Monthly");
}
async assertTotalDue(): Promise<void> {
const productTotalDue = await DateOperations.extractPrice(this.page, ReviewAndCheckoutPage.productMonthlyPrice).then(DateOperations.calculateTotalDue);
const addonTotalDue = await DateOperations.extractPrice(this.page, ReviewAndCheckoutPage.addonMonthlyPrice).then(DateOperations.calculateTotalDue);
console.log(`Product Monthly Price: ${productTotalDue}`);
console.log(`Addon Monthly Price: ${addonTotalDue}`);
const expectedTotalDueToday = productTotalDue + addonTotalDue;
const actualTotalDue = await DateOperations.extractPrice(this.page, ReviewAndCheckoutPage.totalDueToday)
expect(expectedTotalDueToday).toBeCloseTo(actualTotalDue, 2);
}
async clickOnCheckout(): Promise<void> {
await this.page.click(ReviewAndCheckoutPage.checkoutButton)
}
}

@ -0,0 +1,22 @@
import { Page } from '@playwright/test';
export class DateOperations {
static async extractPrice(page: Page, locator: string): Promise<number> {
const text = await page.locator(locator).textContent();
if (text === null) {
throw new Error(`No text found in this locator: ${locator}`);
}
return parseFloat(text.replace(/[^0-9.]/g, ''));
}
static calculateTotalDue(monthlyPrice: number): number {
const currentDate = new Date();
const daysInMonth = new Date(currentDate.getFullYear(), currentDate.getMonth() + 1, 0).getDate();
const daysLeftInMonth = daysInMonth - currentDate.getDate() + 1;
const dailyRate = monthlyPrice / daysInMonth;
const totalDue = dailyRate * daysLeftInMonth;
return parseFloat(totalDue.toFixed(2));
}
}

@ -0,0 +1,16 @@
import { Page } from '@playwright/test';
import {ProductConfigurePage} from "../cPanel/pages/productConfigurePage";
export class PageBase {
constructor(protected readonly page: Page) {}
async navigateTo(url: string): Promise<void> {
await this.page.goto(url);
}
async waitForElement(locator: string, state: 'attached' | 'detached' | 'visible' | 'hidden' = 'visible'): Promise<void> {
await this.page.waitForSelector(locator, { state });
}
}

@ -0,0 +1,41 @@
import { test } from '@playwright/test';
import {cPanelLicensesPageStepDefs} from "../src/main/cPanel/step-definitions/cPanelLicensesPageStepDefs";
import {ProductConfigurePageStepDefs} from "../src/main/cPanel/step-definitions/productConfigurePageStepDefs";
import {ReviewAndCheckoutPageStepDefs} from "../src/main/cPanel/step-definitions/reviewAndCheckoutPageStepDefs";
import {CheckoutPageStepDefs} from "../src/main/cPanel/step-definitions/checkoutPageStepDefs";
test.describe('Order License from cPanel Licenses Page with one Addon', () => {
test('cPanel Licenses Page', async ({ page }) => {
const cPanelLicensesPage = new cPanelLicensesPageStepDefs(page);
await cPanelLicensesPage.open();
await cPanelLicensesPage.clickOnOrderNowLicense("cPanel Pro Cloud (30 Accounts)")
const productConfigurePage = new ProductConfigurePageStepDefs(page)
await productConfigurePage.fillIpAddressWithData("2.2.2.2")
await productConfigurePage.clickOnAddToCartProduct("Monthly CloudLinux")
await productConfigurePage.assertProductName("cPanel Pro Cloud (30 Accounts)")
await productConfigurePage.assertAddonName("Monthly CloudLinux")
await productConfigurePage.assertProductPrice("$42.99 USD")
await productConfigurePage.assertAddonPrice("$26.00 USD")
await productConfigurePage.assertSetupFeePrice("$0.00 USD")
await productConfigurePage.assertMonthlyPrice()
await productConfigurePage.assertTotalDue()
await productConfigurePage.clickOnContinue()
const reviewAndCheckoutPage = new ReviewAndCheckoutPageStepDefs(page)
await reviewAndCheckoutPage.assertLicenseName("cPanel Licenses")
await reviewAndCheckoutPage.assertIpAddress("2.2.2.2")
// await reviewAndCheckoutPage.assertProductMonthlyPrice("$42.99 USD")
await reviewAndCheckoutPage.assertAddonMonthlyPrice("$26.00 USD")
// await reviewAndCheckoutPage.assertTotalDue()
await reviewAndCheckoutPage.clickOnCheckout()
const checkoutPage = new CheckoutPageStepDefs(page)
await checkoutPage.assertPersonalInformationBlockIsVisible()
await checkoutPage.assertBillingAddressBlockIsVisible()
await checkoutPage.assertAccountSecurityBlockIsVisible()
await checkoutPage.assertTermsAndConditionsBlockIsVisible()
await checkoutPage.assertPaymentDetailsBlockIsVisible()
await checkoutPage.assertCompleteOrderButtonIsVisibleAndDisabled()
});
})

@ -0,0 +1,17 @@
{
"compilerOptions": {
"target": "ES2020",
"module": "commonjs",
"types": ["node"],
"strict": true,
"baseUrl": "./",
"typeRoots": [
"./node_modules/@types",
"./src/types" // Add the path to your declaration files here
]
},
"include": [
"src/**/*",
"src/types/**/*" // Ensure this is included if declaration files are in a different directory
]
}
Loading…
Cancel
Save