From 9dae0a0e6a50578c68f3c868973595983f4975e5 Mon Sep 17 00:00:00 2001 From: valerpan Date: Mon, 10 Aug 2026 14:34:42 +0300 Subject: [PATCH] Initial commit ui-develop --- .gitignore | 4 + pom.xml | 42 +++++ src/main/java/elements/MainMenu.java | 66 ++++++++ src/main/java/models/Dictionaries.java | 47 ++++++ src/main/java/models/Ticket.java | 179 ++++++++++++++++++++++ src/main/java/pages/AbstractPage.java | 16 ++ src/main/java/pages/CreateTicketPage.java | 91 +++++++++++ src/main/java/pages/LoginPage.java | 55 +++++++ src/main/java/pages/MainPage.java | 32 ++++ src/main/java/pages/TicketPage.java | 69 +++++++++ src/main/java/pages/TicketsPage.java | 49 ++++++ src/main/java/pages/ViewPage.java | 77 ++++++++++ src/main/resources/config.properties | 2 + src/main/resources/user.properties | 2 + src/test/java/web/HelpdeskUITest.java | 130 ++++++++++++++++ 15 files changed, 861 insertions(+) create mode 100644 .gitignore create mode 100644 pom.xml create mode 100644 src/main/java/elements/MainMenu.java create mode 100644 src/main/java/models/Dictionaries.java create mode 100644 src/main/java/models/Ticket.java create mode 100644 src/main/java/pages/AbstractPage.java create mode 100644 src/main/java/pages/CreateTicketPage.java create mode 100644 src/main/java/pages/LoginPage.java create mode 100644 src/main/java/pages/MainPage.java create mode 100644 src/main/java/pages/TicketPage.java create mode 100644 src/main/java/pages/TicketsPage.java create mode 100644 src/main/java/pages/ViewPage.java create mode 100644 src/main/resources/config.properties create mode 100644 src/main/resources/user.properties create mode 100644 src/test/java/web/HelpdeskUITest.java diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b939794 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +/.idea/ +/out/ +/target/ +*.iml \ No newline at end of file diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..6f184c5 --- /dev/null +++ b/pom.xml @@ -0,0 +1,42 @@ + + + 4.0.0 + + org.example + HelpdeskUITest + 1.0-SNAPSHOT + + + + org.apache.maven.plugins + maven-compiler-plugin + + 8 + 8 + + + + + + + + org.seleniumhq.selenium + selenium-java + 3.141.59 + + + org.testng + testng + 6.9.6 + + + io.qameta.allure + allure-testng + 2.13.8 + + + + + \ No newline at end of file diff --git a/src/main/java/elements/MainMenu.java b/src/main/java/elements/MainMenu.java new file mode 100644 index 0000000..ebbd1ee --- /dev/null +++ b/src/main/java/elements/MainMenu.java @@ -0,0 +1,66 @@ +package elements; + +import io.qameta.allure.Step; +import models.Ticket; +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.WebElement; +import org.openqa.selenium.support.FindBy; +import org.openqa.selenium.support.PageFactory; + +/** Главное меню (блок элементов) */ +public class MainMenu { + + // Способ объявления элементов страницы, через аннотацию @FindBy (с последующей инициализацией) + + @FindBy(xpath = "//span[contains(text(),'New Ticket')]") + private WebElement newTicketButton; + + @FindBy(id = "userDropdown") + private WebElement logInButton; + + @FindBy(xpath = "//input[@id='search_query']") + private WebElement inputSearch; + + @FindBy(xpath = "//nav//button[@type='submit']") + private WebElement goButton; + + public MainMenu(WebDriver driver) { + /* Необходимо инициализировать элементы класса, аннотированные @FindBy. + Лучше всего это делать в конструкторе. */ + PageFactory.initElements(driver, this); + } + + @Step("Нажать кнопку создания новго тикета") + public void clickOnNewTicketButton() { + newTicketButton.click(); + } + + @Step("Нажать кнопку логина") + public void clickOnLogInButton() { + logInButton.click(); + } + + @Step("Найти тикет с помощью поиска") + public void searchTicket(Ticket ticket) { + setInputSearch(ticket.getTitle()) + .clickOnGoButton(); + } + + /* Если после вызова void метода, может потребоваться вызов другого метода этого же класса, + то можно вернуть сам класс и вызвать следующий метод через точку. */ + @Step("Ввести в поле поиска значение {text}") + public MainMenu setInputSearch(String text) { + inputSearch.sendKeys(text); + return this; + } + + @Step("Нажать кнопку поиска") + public void clickOnGoButton() { + goButton.click(); + } + + @Step("Получить логин пользователя") + public String loginedUser() { + return logInButton.getText(); + } +} diff --git a/src/main/java/models/Dictionaries.java b/src/main/java/models/Dictionaries.java new file mode 100644 index 0000000..1858a3e --- /dev/null +++ b/src/main/java/models/Dictionaries.java @@ -0,0 +1,47 @@ +package models; + +import java.util.HashMap; +import java.util.Map; + +/** Словари */ +public class Dictionaries { + + private static final HashMap priorities = new HashMap<>(); + private static final HashMap queues = new HashMap<>(); + + static { + priorities.put(1, "1. Critical"); + priorities.put(2, "2. High"); + priorities.put(3, "3. Normal"); + priorities.put(4, "4. Low"); + priorities.put(5, "5. Very Low"); + + queues.put(1, "Django Helpdesk"); + queues.put(2, "Some Product"); + } + + public static String getPriority(int priority) { + return priorities.get(priority); + } + + public static String getQueue(int queue) { + return queues.get(queue); + } + + public static K getKeyByValue(Map map, V value) { + for (Map.Entry entry : map.entrySet()) { + if (entry.getValue().equals(value)) { + return entry.getKey(); + } + } + return null; + } + + public static Integer getPriorityKey(String name) { + return getKeyByValue(priorities, name); + } + + public static Integer getQueueKey(String name) { + return getKeyByValue(queues, name); + } +} diff --git a/src/main/java/models/Ticket.java b/src/main/java/models/Ticket.java new file mode 100644 index 0000000..65dbde2 --- /dev/null +++ b/src/main/java/models/Ticket.java @@ -0,0 +1,179 @@ +package models; + +import java.io.File; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; + +public class Ticket { + + /* Класс Ticket пакета models реализуем по аналогии c домашним заданием по тестированию API. + Класс должен содержать набор полей, необходимый для заполнения формы создания тикета. + Тип данных для каждого поля должен соответствовать документации swagger (см. раздел Models в документации). */ + + private Integer id; + private String title; + private String due_date; + private File file; + private String assigned_to; + private String created; + private String modified; + private String submitter_email; + private Integer status; + private Boolean on_hold; + private String description; + private String resolution; + private Integer priority; + private String last_escalation; + private String secret_key; + private Integer queue; + private Integer kbitem; + private Integer merged_to; + + public Integer getId(){return id;} + + public void setId(Integer id) { + this.id = id; + } + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public String getDue_date() { + return due_date; + } + + // обычный сеттер + public void setDue_date(String due_date) { + this.due_date = due_date; + } + + // перегруженный сеттер, который принимает дату и форматирует её в строку по шаблону + public void setDue_date(LocalDateTime due_date) { + this.due_date = due_date.format(DateTimeFormatter.ofPattern("yyyy-MM-dd hh:mm:ss")); + } + + public File getFile() { + return file; + } + + public void setFile(File file) { + this.file = file; + } + + public String getAssigned_to() { + return assigned_to; + } + + public void setAssigned_to(String assigned_to) { + this.assigned_to = assigned_to; + } + + public String getCreated() { + return created; + } + + public void setCreated(String created) { + this.created = created; + } + + public String getModified() { + return modified; + } + + public void setModified(String modified) { + this.modified = modified; + } + + public String getSubmitter_email() { + return submitter_email; + } + + public void setSubmitter_email(String submitter_email) { + this.submitter_email = submitter_email; + } + + public Integer getStatus() { + return status; + } + + public void setStatus(Integer status) { + this.status = status; + } + + public Boolean getOn_hold() { + return on_hold; + } + + public void setOn_hold(Boolean on_hold) { + this.on_hold = on_hold; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public String getResolution() { + return resolution; + } + + public void setResolution(String resolution) { + this.resolution = resolution; + } + + public Integer getPriority() { + return priority; + } + + public void setPriority(Integer priority) { + this.priority = priority; + } + + public String getLast_escalation() { + return last_escalation; + } + + public void setLast_escalation(String last_escalation) { + this.last_escalation = last_escalation; + } + + public String getSecret_key() { + return secret_key; + } + + public void setSecret_key(String secret_key) { + this.secret_key = secret_key; + } + + public Integer getQueue() { + return queue; + } + + public void setQueue(Integer queue) { + this.queue = queue; + } + + public Integer getKbitem() { + return kbitem; + } + + public void setKbitem(Integer kbitem) { + this.kbitem = kbitem; + } + + public Integer getMerged_to() { + return merged_to; + } + + public void setMerged_to(Integer merged_to) { + this.merged_to = merged_to; + } +} diff --git a/src/main/java/pages/AbstractPage.java b/src/main/java/pages/AbstractPage.java new file mode 100644 index 0000000..7779cc6 --- /dev/null +++ b/src/main/java/pages/AbstractPage.java @@ -0,0 +1,16 @@ +package pages; + +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.WebElement; +import org.openqa.selenium.support.FindBy; + +/** Элементы общие для всех страниц */ +public abstract class AbstractPage { + + protected static WebDriver driver; + + public static void setDriver(WebDriver webDriver) { + driver = webDriver; + } + +} diff --git a/src/main/java/pages/CreateTicketPage.java b/src/main/java/pages/CreateTicketPage.java new file mode 100644 index 0000000..f81cca3 --- /dev/null +++ b/src/main/java/pages/CreateTicketPage.java @@ -0,0 +1,91 @@ +package pages; + +import io.qameta.allure.Step; +import models.Dictionaries; +import models.Ticket; +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.WebElement; +import org.openqa.selenium.support.FindBy; +import org.openqa.selenium.support.PageFactory; +import org.openqa.selenium.support.ui.Select; + +/** Страница создания тикета */ +public class CreateTicketPage extends HelpdeskBasePage { + + @FindBy(id = "id_queue") + private WebElement selectQueue; + + @FindBy(id = "id_title") + private WebElement inputProblem; + + @FindBy(id = "id_body") + private WebElement inputDescription; + + @FindBy(id = "id_priority") + private WebElement inputPriority; + + @FindBy(id = "id_due_date") + private WebElement inputDueOn; + + @FindBy(id = "id_submitter_email") + private WebElement inputEmailAddress; + + @FindBy(xpath = "//button[@type='submit']") + private WebElement submitTicketButton; + + public CreateTicketPage(WebDriver driver) { + AbstractPage.driver = driver; + PageFactory.initElements(driver, this); + } + + @Step("Создать тикет") + public CreateTicketPage createTicket(Ticket ticket) { + selectQueue(ticket.getQueue()); + setInputProblem(ticket.getTitle()); + setInputDescription(ticket.getDescription()); + selectPriority(ticket.getPriority()); + setDueDate(ticket.getDue_date()); + setEmailAddress(ticket.getSubmitter_email()); + + clickOnSubmitButton(); + return this; + } + + @Step("Ввести имя проблемы: {text}") + public void setInputProblem(String text) { + inputProblem.sendKeys(text); + } + + @Step("Нажать на кнопку создания тикета") + public void clickOnSubmitButton() { + submitTicketButton.click(); + } + + @Step("Выбрать очередь: {queue}") + public void selectQueue(int queue) { + Select select = new Select(selectQueue); + select.selectByVisibleText(Dictionaries.getQueue(queue)); + } + + @Step("Ввести описание проблемы: {text}") + public void setInputDescription(String text) { + inputDescription.sendKeys(text); + } + + @Step("Выбрать приоритет: {priority}") + public void selectPriority(int priority) { + Select select = new Select(inputPriority); + select.selectByVisibleText(Dictionaries.getPriority(priority)); + } + + @Step("Ввести срок выполнения: {date}") + public void setDueDate(String date) { + inputDueOn.sendKeys(date); + } + + @Step("Ввести email: {email}") + public void setEmailAddress(String email) { + inputEmailAddress.sendKeys(email); + } + +} diff --git a/src/main/java/pages/LoginPage.java b/src/main/java/pages/LoginPage.java new file mode 100644 index 0000000..2d28186 --- /dev/null +++ b/src/main/java/pages/LoginPage.java @@ -0,0 +1,55 @@ +package pages; + +import io.qameta.allure.Step; +import org.openqa.selenium.WebElement; +import org.openqa.selenium.support.FindBy; +import org.openqa.selenium.support.PageFactory; + +/** Страница логина */ +public class LoginPage extends HelpdeskBasePage { + + // поиск элемента через xpath + @FindBy(id = "username") + private WebElement user; + + // поиск элемента по id + @FindBy(id = "password") + private WebElement password; + + // поиск элемента через css + @FindBy(css = "[type='submit']") + private WebElement loginButton; + + public LoginPage() { + PageFactory.initElements(driver, this); + } + + /** + * Авторизация пользователя + * + * @param user логин пользователя + * @param password пароль пользователя + */ + @Step("Авторизация пользователя") + public LoginPage login(String user, String password) { + setUser(user); + setPassword(password); + clickOnLoginButton(); + return this; + } + + @Step("Ввести логин {user}") + private void setUser(String user) { + this.user.sendKeys(user); + } + + @Step("Ввести пароль") + private void setPassword(String password) { + this.password.sendKeys(password); + } + + @Step("Нажать кнопку авторизации") + private void clickOnLoginButton() { + this.loginButton.click(); + } +} diff --git a/src/main/java/pages/MainPage.java b/src/main/java/pages/MainPage.java new file mode 100644 index 0000000..24bb9ec --- /dev/null +++ b/src/main/java/pages/MainPage.java @@ -0,0 +1,32 @@ +package pages; + +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.WebElement; +import org.openqa.selenium.support.FindBy; +import org.openqa.selenium.support.PageFactory; + +/** Главная страница */ +public class MainPage extends HelpdeskBasePage { + + public MainPage() { + PageFactory.initElements(driver, this); + } + + @FindBy(xpath = "//span[text()='New Ticket']") + private WebElement newTicketButton; + + public void clickOnNewTicket() { + if (newTicketButton != null) { + newTicketButton.click(); + } + else {throw new RuntimeException();} + } + + @FindBy(xpath = "//a[contains(text(),'Log In')]") + private WebElement loginButton; + + public void clickOnLogin() { + if (loginButton != null) {loginButton.click();} + else {throw new RuntimeException();} + } +} diff --git a/src/main/java/pages/TicketPage.java b/src/main/java/pages/TicketPage.java new file mode 100644 index 0000000..705b668 --- /dev/null +++ b/src/main/java/pages/TicketPage.java @@ -0,0 +1,69 @@ +package pages; + +import io.qameta.allure.Step; +import models.Dictionaries; +import models.Ticket; +import org.openqa.selenium.By; +import org.openqa.selenium.WebElement; +import org.testng.Assert; + +import java.util.Optional; + +/** Страница отдельного тикета (авторизированный пользователь) */ +public class TicketPage extends HelpdeskBasePage { + + /* Верстка страницы может измениться, поэтому для таблиц вместо индексов строк и столбцов лучше использовать + более универсальные локаторы, например поиск по тексту + parent, following-sibling и другие. + + Текст тоже может измениться, но в этом случае элемент не будет найден и тест упадет, + а ошибку можно будет легко локализовать и исправить. + В случае изменений ячеек таблицы, локатор будет продолжать работать, но будет указывать на другой элемент, + поведение теста при этом изменится непредсказуемым образом и ошибку будет сложно найти. */ + private final WebElement title = driver.findElement( + By.xpath("//th[@colspan='4']/h3")); + + private final WebElement queue = driver.findElement( + By.xpath("//th[contains(text(), 'Queue:')]")); + + private final WebElement dueDate = driver.findElement( + By.xpath("//th[text()='Due Date']/following-sibling::td[1]")); + + private final WebElement email = driver.findElement( + By.xpath("//th[text()='Submitter E-Mail']/following-sibling::td[1]")); + + private final WebElement priority = driver.findElement( + By.xpath("//th[text()='Priority']/following-sibling::td[1]")); + + private final WebElement description = driver.findElement( + By.xpath("//td[@id='ticket-description']/p")); + + @Step("Проверить значение полей на странице тикета") + public void checkTicket(Ticket ticket) { + Assert.assertEquals(extractTicketName(title.getText()), ticket.getTitle(), "Название тикета не соответствует"); + Assert.assertEquals((extractQueue(queue.getText())), ticket.getQueue(), "Очередь тикета не соответствует"); + Assert.assertEquals((email.getText()), ticket.getSubmitter_email(), "Почта не соответствует"); + Assert.assertEquals(Dictionaries.getPriorityKey(priority.getText()), ticket.getPriority(), "Приоритетность тикета не соответствует"); + Assert.assertEquals((description.getText()), ticket.getDescription(), "Описание тикета не соответствует"); + } + + public static Integer extractQueue(String queue) { + if (queue != null && queue.contains(":")) { + queue = queue.split(":")[1].trim(); + + int toolbarIndex = queue.indexOf("Edit"); + if (toolbarIndex >= 0) { + queue = queue.substring(0, toolbarIndex).trim(); + } + } + + return Dictionaries.getQueueKey(queue); + } + + public static String extractTicketName(String title){ + if (title == null || !title.contains(".") || !title.contains("[")) { + return title; + } + return title.split("\\.")[1].split("\\[")[0].trim(); + } + +} diff --git a/src/main/java/pages/TicketsPage.java b/src/main/java/pages/TicketsPage.java new file mode 100644 index 0000000..e87d64b --- /dev/null +++ b/src/main/java/pages/TicketsPage.java @@ -0,0 +1,49 @@ +package pages; + +import io.qameta.allure.Step; +import models.Ticket; +import org.openqa.selenium.WebElement; +import org.openqa.selenium.support.FindBy; +import org.openqa.selenium.support.PageFactory; + +import java.util.List; + +/** Страница с таблицей тикетов и фильтрами */ +public class TicketsPage extends HelpdeskBasePage { + public TicketsPage() { PageFactory.initElements(driver, this);} + + @FindBy(xpath = "//div[@class='tickettitle']/a") + private List ticketsHref; + + @FindBy(id = "search_query") + private WebElement search; + + @FindBy(xpath = "//i[@class='fas fa-search']/parent::button") + private WebElement searchButton; + + public TicketsPage clickOnSearch(String title) { + if (searchButton != null && search != null) { + search.clear(); + search.sendKeys(title); + searchButton.click(); + } + else {throw new RuntimeException();} + return this; + } + + /** + * Ищем строку с и тикетом и нажимаем на нее + * + * @param ticket + */ + @Step("Открыть тикет с id {ticket.id}") + public void openTicket(Ticket ticket) { + String id = String.valueOf(ticket.getId()); + ticketsHref.stream() + .filter(WebElement::isDisplayed) + .filter(ticketHref -> ticketHref.getText().startsWith(id)) + .findFirst() + .orElseThrow(() -> new AssertionError("Не найден тикет с id " + id)) + .click(); + } +} diff --git a/src/main/java/pages/ViewPage.java b/src/main/java/pages/ViewPage.java new file mode 100644 index 0000000..e35eecf --- /dev/null +++ b/src/main/java/pages/ViewPage.java @@ -0,0 +1,77 @@ +package pages; + +import io.qameta.allure.Step; +import models.Dictionaries; +import models.Ticket; +import org.openqa.selenium.By; +import org.openqa.selenium.WebElement; +import org.openqa.selenium.support.ui.ExpectedCondition; +import org.openqa.selenium.support.ui.ExpectedConditions; +import org.openqa.selenium.support.ui.WebDriverWait; +import org.testng.Assert; + +import static pages.TicketPage.extractQueue; +import static pages.TicketPage.extractTicketName; + +/** Страница просмотра карточки тикета (неавторизированный пользователь) */ +public class ViewPage extends HelpdeskBasePage { + + // Способ объявления и инициализации элементов страницы через driver.findElement(locator) + + /* Инициализация сразу при объявлении элемента. + Элемент должен присутствовать на странице браузера в момент создания объекта страницы new ViewPage() */ + private final WebElement queue = driver.findElement(By.xpath("//th[contains(text(), 'Queue:')]")); + private final WebElement email = driver.findElement(By.xpath("//th[text()='Submitter E-Mail']/following-sibling::td[1]")); + private final WebElement priority = driver.findElement(By.xpath("//th[text()='Priority']/following-sibling::td[1]")); + + // Пример поиска description, используя промежуточный элемент descriptionLabel + private final WebElement descriptionLabel = driver.findElement(By.xpath("//th[text()='Description']")); + // Поиск элемента можно выполнять не только относительно driver, но и относительно другого элемента + private final WebElement description = descriptionLabel.findElement(By.xpath("./parent::*/following-sibling::tr[1]")); + + /* Поиск элемента по локатору. + Используется для элементов, которых нет на странице браузера в момент создания объекта страницы, + так как локатор может быть объявлен и проинициализирован до появления элемнета на странице. */ + private final By captionLocator = By.xpath("//table/caption"); + private final WebElement caption; + + + public ViewPage() { + // В данном случае инициализация через PageFactory не нужна, + // но можем проинициализировать элементы по локаторам (если элементы отображаются) + caption = driver.findElement(captionLocator); + } + + @Step("Проверить значение полей на карточке тикета") + public ViewPage checkTicket(Ticket ticket) { + Assert.assertTrue(getTicketTitle().contains(ticket.getTitle()), "Имя тикета не соответствует"); + //Assert.assertEquals(extractTicketName(caption.getText()), ticket.getTitle(), "Название тикета не соответствует"); + Assert.assertEquals((extractQueue(queue.getText())), ticket.getQueue(), "Очередь тикета не соответствует"); + Assert.assertEquals((email.getText()), ticket.getSubmitter_email(), "Почта не соответствует"); + Assert.assertEquals(Dictionaries.getPriorityKey(priority.getText()), ticket.getPriority(), "Приоритетность тикета не соответствует"); + Assert.assertEquals((description.getText()), ticket.getDescription(), "Описание тикета не соответствует"); + return this; + } + + @Step("Получить заголовок тикета") + public String getTicketTitle() { + // Если элемент появляется не сразу, можно выполнить ожидание по условию + + // условие видимости при поиске по локатору + ExpectedCondition condition = ExpectedConditions.visibilityOfElementLocated(captionLocator); + + // поиск с ожиданием по условию + WebElement ticketTitle = new WebDriverWait(driver, 5).until(condition); + + return ticketTitle.getText(); + } + + @Step("Сохранить id тикета в объект") + public void saveId(Ticket ticket) { + String captionText = caption.getText(); + String id = captionText.substring(captionText.indexOf("-") + 1, captionText.indexOf("]")); + ticket.setId(Integer.parseInt(id)); + } + + +} diff --git a/src/main/resources/config.properties b/src/main/resources/config.properties new file mode 100644 index 0000000..e3985ac --- /dev/null +++ b/src/main/resources/config.properties @@ -0,0 +1,2 @@ +site.url=https://at-sandbox.workbench.lanit.ru/ +webdriver.chrome.driver=F:\\chromedriver\\chromedriver.exe \ No newline at end of file diff --git a/src/main/resources/user.properties b/src/main/resources/user.properties new file mode 100644 index 0000000..0af68c6 --- /dev/null +++ b/src/main/resources/user.properties @@ -0,0 +1,2 @@ +user=admin +password=adminat \ No newline at end of file diff --git a/src/test/java/web/HelpdeskUITest.java b/src/test/java/web/HelpdeskUITest.java new file mode 100644 index 0000000..4653983 --- /dev/null +++ b/src/test/java/web/HelpdeskUITest.java @@ -0,0 +1,130 @@ +package web; + +import io.qameta.allure.Step; +import models.Dictionaries; +import models.Ticket; +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.chrome.ChromeDriver; +import org.testng.annotations.AfterTest; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.Test; +import pages.*; + +import java.io.IOException; +import java.util.concurrent.TimeUnit; + +public class HelpdeskUITest { + + private WebDriver driver; + private MainPage mainPage; + private CreateTicketPage createTicketPage; + private TicketsPage ticketsPage; + private LoginPage loginPage; + private ViewPage viewPage; + private TicketPage ticketPage; + + private String username; + private String password; + private String webdriver; + + @BeforeClass + public void setup() throws IOException { + loadProperties(); + setupDriver(); + openMainPage(); + + mainPage = new MainPage(); + createTicketPage = new CreateTicketPage(driver); + loginPage = new LoginPage(); + ticketsPage = new TicketsPage(); + } + + @Step("Загрузить конфигурационные файлы") + private void loadProperties() throws IOException { + // Читаем конфигурационные файлы в System.properties + System.getProperties().load(ClassLoader.getSystemResourceAsStream("config.properties")); + System.getProperties().load(ClassLoader.getSystemResourceAsStream("user.properties")); + + username = System.getProperty("user"); + password = System.getProperty("password"); + + setWebdriver(System.getProperty("webdriver.chrome.driver")); + System.setProperty("webdriver.chrome.driver", webdriver); + } + + @Step("Создать экземпляр драйвера") + private void setupDriver() { + // Создание экземпляра драйвера + driver = new ChromeDriver(); + // Устанавливаем размер окна браузера, как максимально возможный + driver.manage().window().maximize(); + // Установим время ожидания для поиска элементов + driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS); + // Установить созданный драйвер для поиска в веб-страницах + AbstractPage.setDriver(driver); + } + + @Step("Открыть главную страницу") + private void openMainPage() { + String siteUrl = System.getProperty("site.url"); + driver.get(siteUrl); + } + + @Test + public void createTicketTest() { + mainPage.clickOnNewTicket(); + + Ticket ticket1 = buildNewTicket(); + createTicketPage.createTicket(ticket1); + + viewPage = new ViewPage(); + viewPage.checkTicket(ticket1); + viewPage.saveId(ticket1); + + //int id = ticket1.getId(); + + mainPage.clickOnLogin(); + loginPage.login(username, password); + + ticketsPage.clickOnSearch(ticket1.getTitle()); + ticketsPage.openTicket(ticket1); + + ticketPage = new TicketPage(); + ticketPage.checkTicket(ticket1); + } + + private Ticket buildNewTicket() { + Ticket ticket = new Ticket(); + + String title = "Проверка проблемы"; + String description = "Необходимо проверить пользовательский интерфейс"; + String due_date = "2026-08-17"; + String email = "helpdesk@example.com"; + + ticket.setTitle(title); + ticket.setDescription(description); + ticket.setQueue(Dictionaries.getQueueKey("Django Helpdesk")); + ticket.setPriority(Dictionaries.getPriorityKey("1. Critical")); + ticket.setDue_date(due_date); + ticket.setSubmitter_email(email); + + return ticket; + } + + @AfterTest + public void close() { + if (driver != null) { + // Закрываем одно текущее окно браузера + driver.close(); + // Закрываем все открытые окна браузера, завершаем работу браузера, освобождаем ресурсы + driver.quit(); + } + } + + public void setWebdriver(String webdriver) { + if (webdriver == null || webdriver .isEmpty()) { + throw new RuntimeException("В файле \"config.properties\" отсутствует значение \"webdriver.chrome.driver\""); + } + this.webdriver = webdriver; + } +}