140 lines
5.0 KiB
Java
140 lines
5.0 KiB
Java
package ru.kovbasa.pages;
|
||
|
||
import org.jsoup.Jsoup;
|
||
import org.jsoup.nodes.Document;
|
||
import org.jsoup.nodes.Element;
|
||
import org.openqa.selenium.By;
|
||
import org.openqa.selenium.JavascriptExecutor;
|
||
import org.openqa.selenium.NoSuchElementException;
|
||
import org.openqa.selenium.WebDriver;
|
||
import org.openqa.selenium.WebElement;
|
||
import org.openqa.selenium.interactions.Actions;
|
||
import org.openqa.selenium.support.ui.WebDriverWait;
|
||
import ru.kovbasa.elements.Button;
|
||
import ru.kovbasa.utils.PageUtils;
|
||
|
||
import java.time.LocalDate;
|
||
import java.time.format.DateTimeFormatter;
|
||
import java.util.Locale;
|
||
import java.util.regex.Matcher;
|
||
import java.util.regex.Pattern;
|
||
|
||
public class CoursePage {
|
||
private static final Pattern PRICE_PATTERN = Pattern.compile("(\\d[\\d\\s\\u00A0]*)\\s*₽");
|
||
|
||
|
||
private final WebDriver driver;
|
||
|
||
private final By enrollButton = By.cssSelector("button[data-testid='enroll-button']");
|
||
private final By fullPriceTab = By.xpath("//div[normalize-space()='Полная']/parent::div");
|
||
private final By fullDiscountLabel = By.xpath("//p[contains(normalize-space(),'Полная стоимость со скидкой')]");
|
||
private final By anyPriceText = By.xpath("//*[contains(normalize-space(),'₽')]");
|
||
|
||
public CoursePage(WebDriver driver) {
|
||
this.driver = driver;
|
||
}
|
||
|
||
public String getCourseTitle() {
|
||
return getFirstH1();
|
||
}
|
||
|
||
public String getFirstH1() {
|
||
Document doc = Jsoup.parse(driver.getPageSource());
|
||
return doc.select("h1")
|
||
.stream()
|
||
.map(e -> e.text().trim())
|
||
.filter(t -> !t.isEmpty())
|
||
.findFirst()
|
||
.orElseThrow(() -> new RuntimeException("Course title not found"));
|
||
}
|
||
|
||
public LocalDate getCourseStartDate(LocalDate catalogDate) {
|
||
Document doc = Jsoup.parse(driver.getPageSource());
|
||
|
||
Element dateElement = doc.selectFirst("p.sc-3cb1l3-0");
|
||
if (dateElement == null) {
|
||
throw new RuntimeException("Start date <p> not found on course page");
|
||
}
|
||
|
||
String text = dateElement.text().trim();
|
||
String fullDate = text + ", " + catalogDate.getYear();
|
||
|
||
DateTimeFormatter formatter =
|
||
DateTimeFormatter.ofPattern("d MMMM, yyyy", Locale.forLanguageTag("ru"));
|
||
|
||
return LocalDate.parse(fullDate, formatter);
|
||
}
|
||
|
||
public Button getEnrollButton() {
|
||
return new Button(driver.findElement(enrollButton));
|
||
}
|
||
|
||
public void clickEnroll() {
|
||
getEnrollButton().click();
|
||
}
|
||
|
||
public int getDiscountedFullPrice() {
|
||
PageUtils.removeBottomBanner(driver);
|
||
|
||
final WebDriverWait wait = new WebDriverWait(driver, java.time.Duration.ofSeconds(10));
|
||
final WebElement tab = wait.until(drv -> drv.findElements(fullPriceTab).stream()
|
||
.filter(WebElement::isDisplayed)
|
||
.findFirst()
|
||
.orElse(null));
|
||
|
||
((JavascriptExecutor) driver).executeScript("arguments[0].scrollIntoView({block:'center'});", tab);
|
||
try {
|
||
tab.click();
|
||
} catch (RuntimeException ignored) {
|
||
new Actions(driver).moveToElement(tab).click().perform();
|
||
}
|
||
|
||
final WebElement label = wait.until(drv -> drv.findElements(fullDiscountLabel).stream()
|
||
.filter(WebElement::isDisplayed)
|
||
.findFirst()
|
||
.orElse(null));
|
||
|
||
final WebElement priceElement = findPriceElementNearLabel(label);
|
||
final String rawPrice = priceElement.getText();
|
||
return parsePrice(rawPrice);
|
||
}
|
||
|
||
public int getPriceForComparison() {
|
||
if (hasDisplayed(fullPriceTab)) {
|
||
try {
|
||
return getDiscountedFullPrice();
|
||
} catch (RuntimeException ignored) { }
|
||
}
|
||
|
||
final WebDriverWait wait = new WebDriverWait(driver, java.time.Duration.ofSeconds(10));
|
||
final WebElement priceElement = wait.until(drv -> drv.findElements(anyPriceText).stream()
|
||
.filter(WebElement::isDisplayed)
|
||
.filter(el -> PRICE_PATTERN.matcher(el.getText()).find())
|
||
.findFirst()
|
||
.orElse(null));
|
||
|
||
return parsePrice(priceElement.getText());
|
||
}
|
||
|
||
private WebElement findPriceElementNearLabel(WebElement label) {
|
||
try {
|
||
return label.findElement(By.xpath("following-sibling::div[1]"));
|
||
} catch (NoSuchElementException ignored) {
|
||
return label.findElement(By.xpath("following::div[contains(normalize-space(),'₽')][1]"));
|
||
}
|
||
}
|
||
|
||
private int parsePrice(String rawPrice) {
|
||
final Matcher matcher = PRICE_PATTERN.matcher(rawPrice);
|
||
if (!matcher.find()) {
|
||
throw new IllegalArgumentException("Не удалось распарсить цену из строки: " + rawPrice);
|
||
}
|
||
final String normalized = matcher.group(1).replace('\u00A0', ' ').replace(" ", "");
|
||
return Integer.parseInt(normalized);
|
||
}
|
||
|
||
private boolean hasDisplayed(By by) {
|
||
return driver.findElements(by).stream().anyMatch(WebElement::isDisplayed);
|
||
}
|
||
}
|