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

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); } }