generated from ita-social-projects/DevTemplate
-
Notifications
You must be signed in to change notification settings - Fork 10
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Remove unused imports and fix files naming
- Loading branch information
1 parent
84de993
commit 9e8ba95
Showing
5 changed files
with
687 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,131 @@ | ||
import { expect } from "@playwright/test"; | ||
|
||
class BasePage { | ||
constructor(page) { | ||
this.page = page; | ||
this.navBarCityDropdown = page.locator("header > div.right-side-menu div.city"); | ||
this.navBarCityDropdownList = page.locator("body > div:last-child ul"); | ||
this.paginationNextPageButton = page.locator('ul > li[title="Next Page"]'); | ||
this.paginationFirstPage = page.locator('li[title="1"]'); | ||
} | ||
|
||
async expectElementToHaveText(element, text) { | ||
await expect(element).toHaveText(text); | ||
} | ||
|
||
async expectElementToContainText(element, text) { | ||
await expect(element).toContainText(text); | ||
} | ||
|
||
async expectElementToNotContainText(element, text) { | ||
await expect(element).not.toContainText(text); | ||
} | ||
|
||
async assertTextContains(text, searchText) { | ||
const doesContain = text.includes(searchText); | ||
expect(doesContain).toBe(true); | ||
} | ||
|
||
async verifyElementVisibility(element, isVisible = true) { | ||
if (!(typeof isVisible === "boolean")) { | ||
throw new Error("Second paramenter should be boolean"); | ||
} | ||
isVisible | ||
? await expect(element, ' should be visible').toBeVisible() | ||
: await expect(element, 'should NOT be visible').not.toBeVisible(); | ||
} | ||
|
||
async isElementWithNamePresent(allElements, name){ | ||
const elementsTextContents = await allElements.allTextContents(); | ||
if (await elementsTextContents.includes(name)) { | ||
return true; | ||
} else if (await this.isNextPageAvailable()) { | ||
// If the element is not found on this page, check if there's a next page and recursively search | ||
await this.goToNextPage(); | ||
return await this.isElementWithNamePresent(allElements, name); | ||
} else { | ||
// If the element is not found and there are no more pages, return false | ||
return false; | ||
} | ||
} | ||
|
||
//use the method above to make assertion | ||
async verifyElementExistance(allElements, name, doesExist = true) { | ||
if (await this.isItFirstPage()) { | ||
await this.paginationFirstPage.click(); | ||
} | ||
const isElementPresent = await this.isElementWithNamePresent(allElements, name); | ||
doesExist ? expect(isElementPresent).toBe(true) : expect(isElementPresent).toBe(false); | ||
} | ||
|
||
async verifyUrl(expectedUrl) { | ||
const currentUrl = await this.page.url(); | ||
expect(currentUrl).toBe(expectedUrl); | ||
} | ||
|
||
async isNextPageAvailable() { | ||
return ( | ||
(await this.paginationNextPageButton.isVisible()) && | ||
(await this.paginationNextPageButton.getAttribute("aria-disabled")) !== "true" | ||
); | ||
} | ||
|
||
async isItFirstPage() { | ||
return ( | ||
(await this.paginationFirstPage.isVisible()) && | ||
!(await this.paginationFirstPage.getAttribute("ant-pagination-item-active")) | ||
); | ||
} | ||
|
||
async goToNextPage() { | ||
await this.paginationNextPageButton.click(); | ||
} | ||
|
||
async goToNextPageIfAvailabe(actionsOnThePage = async () => {}) { | ||
if (await this.isNextPageAvailable()) { | ||
await this.goToNextPage(); | ||
await actionsOnThePage(); | ||
} | ||
} | ||
|
||
/* | ||
*This method can be used to check whether the element is present and, if visible, | ||
*has the required text. | ||
*It combines both checks to reduce code duplication when | ||
*verifying error messages' presence or absence. | ||
*/ | ||
async verifyElementVisibilityAndText(element, isVisible = true, text) { | ||
await this.verifyElementVisibility(element, isVisible); | ||
if (isVisible === true) { | ||
await this.expectElementToHaveText(element, text); | ||
} | ||
} | ||
|
||
async fillInputField(element, value) { | ||
await element.waitFor({ timeout: 5000 }); | ||
await element.click(); | ||
await element.clear(); | ||
await element.fill(value); | ||
} | ||
|
||
async selectCityInNavBar(city) { | ||
await this.navBarCityDropdown.click(); | ||
await (await this.navBarCityDropdownList.getByText(city)).click(); | ||
} | ||
|
||
async sortElementsAsc([...elements]) { | ||
return elements.sort(); | ||
} | ||
|
||
async sortElementsDesc([...elements]) { | ||
return elements.sort((a, b) => b - a); | ||
} | ||
|
||
async verifyTooltipAppearsOnHover(selector, message) { | ||
await selector.hover(); | ||
const tooltip = this.page.locator("div.ant-tooltip-inner").filter({ hasText: `${message}` }); | ||
await this.verifyElementVisibility(tooltip, true); | ||
} | ||
} | ||
|
||
module.exports = BasePage; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,149 @@ | ||
import { expect} from "@playwright/test"; | ||
import {CLUBS_URL} from "../constants/api.constants"; | ||
import BasePage from "./BasePage"; | ||
|
||
class ClubsPage extends BasePage { | ||
constructor(page) { | ||
super(page); | ||
this.clubsPageTitle = page.locator(".city-name"); | ||
this.searchField = page.locator('div.search-container input[type="search"]'); | ||
this.searchButton = page.locator('span[aria-label="search"]'); | ||
this.cards = page.locator("div.ant-card"); | ||
this.clubsNames = page.locator("div.title"); | ||
this.clubsCategories = page.locator("div.club-tags-box span.name"); | ||
this.clubsDescriptions = page.locator("p.description"); | ||
this.clubsANDcategories = page.locator("span.and"); | ||
this.clubDetailsCategories = page.locator("div.tags.categories span.name"); | ||
this.clubDetailsCloseButton = page.locator('button[aria-label="Close"]'); | ||
this.paginationNextPageButton = page.locator('ul > li[title="Next Page"]'); | ||
this.paginationFirstPageButton = page.locator('ul > li[title="1"]'); | ||
this.advancedSearchButton = page.locator('span[title="Розширений пошук"]'); | ||
this.firstCardTitle = page.locator("div.content-clubs-list > div:first-child div.name"); | ||
|
||
this.noResultsMessage = page.locator('div.clubs-not-found'); | ||
} | ||
|
||
async gotoClubsPage() { | ||
await this.page.goto(CLUBS_URL); | ||
} | ||
|
||
// Click the advanced search button and wait for a short timeout for clubs to update | ||
async toggleAdvancedSearch() { | ||
await this.advancedSearchButton.click(); | ||
await this.page.waitForTimeout(500); | ||
} | ||
|
||
/* | ||
* Method to accumulate titles of all club cards | ||
* Iterates through pages of club cards, accumulating their titles | ||
* Uses the firstTitleLocator to ensure that previous clubs have disappeared | ||
* from the page before adding new titles to the array | ||
*/ | ||
async getAllClubsTitles() { | ||
let allClubsTitles = []; | ||
while (true) { | ||
try { | ||
const firstTitle = await this.firstCardTitle.textContent(); | ||
const firstTitleLocator = await this.page.getByRole("div", { name: firstTitle }); | ||
const pageTitlesText = await this.clubsNames.allTextContents(); | ||
allClubsTitles = allClubsTitles.concat(pageTitlesText); | ||
console.log(pageTitlesText); | ||
if (await this.isNextPageAvailable()) { | ||
await this.goToNextPage(); | ||
await firstTitleLocator.waitFor({ state: "hidden", timeout: 10000 }); | ||
} else { | ||
break; | ||
} | ||
} catch (e) { | ||
console.error("Error " + e); | ||
break; | ||
} | ||
} | ||
return allClubsTitles; | ||
} | ||
|
||
/* | ||
* Asserts that accumulated club titles are sorted in ascending order | ||
*/ | ||
async verifyClubsSortedByTitlesAsc() { | ||
const originalClubsTitles = await this.getAllClubsTitles(); | ||
const sortedClubsTitles = await this.sortElementsAsc(originalClubsTitles); | ||
expect(originalClubsTitles).toMatchObject(sortedClubsTitles); | ||
} | ||
|
||
// Check if the next page button is visible and enabled | ||
async isNextPageAvailable() { | ||
return ( | ||
(await this.paginationNextPageButton.isVisible()) && | ||
(await this.paginationNextPageButton.getAttribute("aria-disabled")) !== "true" | ||
); | ||
} | ||
|
||
async goToNextPage() { | ||
await this.paginationNextPageButton.click(); | ||
} | ||
|
||
async simpleSearchByQuery(query) { | ||
const firstTitle = await this.firstCardTitle.textContent(); | ||
const firstTitleLocator = await this.page.getByRole("div", { name: firstTitle }); | ||
await this.searchField.clear(); | ||
await this.searchField.fill(query); | ||
await this.searchButton.click(); | ||
await firstTitleLocator.waitFor({ state: "detached", timeout: 10000 }); | ||
} | ||
|
||
/* | ||
* Navigates to the next page if available and executes additional actions on the page. | ||
* @param {Function} actionsOnThePage - Optional function to perform additional actions on the page. | ||
* Defaults to an empty async function. | ||
*/ | ||
async goToNextPageIfAvailabe(actionsOnThePage = async () => {}) { | ||
if (await this.isNextPageAvailable()) { | ||
await this.goToNextPage(); | ||
await actionsOnThePage(); | ||
} | ||
} | ||
|
||
async openCardDetailsPopUp(card) { | ||
await card.locator(this.clubsNames).click(); | ||
} | ||
|
||
async closeCardDetailsPopUp() { | ||
await this.clubDetailsCloseButton.click(); | ||
} | ||
|
||
async goToFirstPage() { | ||
await this.paginationFirstPageButton.click(); | ||
} | ||
|
||
/* | ||
* Method to verify that club cards contain the specified text | ||
* Iterates through club cards, comparing their text with the search query | ||
* Opens card details to check for additional categories, if any | ||
* Utilizes recursion to iterate through all pages | ||
*/ | ||
async verifyClubCardsContainText(text) { | ||
text = text.toLowerCase(); | ||
const cards = await this.cards.all(); | ||
if (await cards.length === 0) { | ||
throw new Error("There are no result for this search query!"); | ||
} | ||
for (let card of cards) { | ||
const cardANDcategory = await card.locator(this.clubsANDcategories).textContent(); | ||
if (cardANDcategory.length > 0) { | ||
await this.openCardDetailsPopUp(card); | ||
const cardCategories = (await this.clubDetailsCategories.allTextContents()).join("; ").toLowerCase(); | ||
await this.closeCardDetailsPopUp(); | ||
card = ((await card.textContent()) + cardCategories).toLowerCase(); | ||
} else { | ||
card = (await card.textContent()).toLowerCase(); | ||
} | ||
await this.assertTextContains(card, text); | ||
} | ||
await this.goToNextPageIfAvailabe(async () => { | ||
await this.verifyClubCardsContainText(text); | ||
}); | ||
} | ||
} | ||
|
||
module.exports = ClubsPage; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
import { ADMIN_EMAIL, ADMIN_PASSWORD, USER_EMAIL, USER_PASSWORD } from "../constants/general.constants"; | ||
import {API_URL} from "../constants/api.constants"; | ||
import {ADD_CLUB_PAGE} from "../constants/locatorsText.constants"; | ||
import { SUCCESS_LOGIN_MESSAGE } from "../constants/messages.constants"; | ||
import BasePage from "./BasePage"; | ||
|
||
class HomePage extends BasePage{ | ||
constructor(page){ | ||
super(page); | ||
this.citiesDropdown = page.locator('.ant-dropdown-trigger.city'); | ||
this.userDropdown = page.locator('.ant-dropdown-trigger.user-profile'); | ||
this.registerButton = page.getByRole('menuitem', { name: 'Увійти' }); | ||
this.emailField = page.locator('input#basic_email'); | ||
this.passwordField = page.locator('input#basic_password'); | ||
this.loginButton = page.locator('button.login-button'); | ||
this.loginSuccessMessage = page.locator('div.ant-message-success span:nth-child(2)'); | ||
this.addClubButton = this.page.getByRole("button", { name: ADD_CLUB_PAGE.addClub }); | ||
this.harkivItem = page.getByRole('menuitem', { name: 'Харків' }) | ||
} | ||
|
||
async gotoHomepage(){ | ||
await this.page.goto(API_URL) | ||
} | ||
|
||
async openAddClubPage() { | ||
await this.page.goto(API_URL); | ||
await this.addClubButton.click(); | ||
} | ||
|
||
async uiLoginAs(userType){ | ||
const userData = { | ||
admin: { email: ADMIN_EMAIL, password: ADMIN_PASSWORD }, | ||
user: { email: USER_EMAIL, password: USER_PASSWORD }, | ||
}; | ||
await this.userDropdown.click(); | ||
await this.registerButton.click(); | ||
|
||
if (!userData[userType]) { | ||
throw new Error("Invalid user type: " + userType); | ||
} | ||
|
||
await this.emailField.fill(userData[userType].email); | ||
await this.passwordField.fill(userData[userType].password); | ||
|
||
await this.loginButton.click(); | ||
await this.expectElementToHaveText(this.loginSuccessMessage, SUCCESS_LOGIN_MESSAGE); | ||
} | ||
} | ||
|
||
module.exports = HomePage; |
Oops, something went wrong.