Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- /*
- Ты знаешь, версия выглядит очень странной..
- Странно, что у тебя она работала
- Взять хотя бы не рабочие кондишены и таймаут в 4 секунды...
- Я выкачала себе твое решение
- И тут буду комментировать - что я делала - чтоб у меня все работало
- что касается твоего тестового аккаунта
- http://joxi.ru/n2YkKaGUjE0N6r
- вот такое мне рассказывает
- скорее всего - в этом причина
- надо зайти вручную в этот аккаунт и все поправить
- или как вариант - другой заведи
- далее - проверяю на своем тестовом аккаунте
- селениум = 2.53.1
- файрфокс = 46.0.1 + запрет обновляться автоматом
- */
- ****************************************************************
- ****************часть 1 - assertThat & timeout *****************
- ****************звездочками разделяем куски слона********************
- ****************************************************************
- /*
- тест падает - при попытке отправить письмо - не хватает таймаута
- который = 4 секундам
- нам нужно средство - которое позволит из тест-классов влиять на таймаут
- */
- -----------------------------------
- src / test / java / pages / Configuration.java
- public class Configuration {
- public static long timeout = 4;
- }
- /*
- держать такой класс в src / test / java / pages
- да еще и не использовать - то уже перебор))
- перемести класс в пекедж core
- */
- -----------------------------------------------
- public void assertThat(ExpectedCondition<WebElement> expectedCondition, long timeoutInSceconds) {
- /*
- перепиши этот метод вот так
- public <V> V assertThat(ExpectedCondition<V> condition, long timeout)
- ты ниже - реализовала метод с сигнатурой
- public <V> V assertThat(Function<? super WebDriver, V> condition)
- верно реализовала - что касается работы с дженериками
- так что - возьми верные идеи в наш метод
- public <V> V assertThat(ExpectedCondition<V> condition, long timeout)
- что касается типа параметра condition
- да, можно применить и тип Function<? super WebDriver, V>
- именно к нему приводится ExpectedCondition<V>
- только вот зачем тебе себя пугать???
- использовав тип ExpectedCondition<V> - получишь той же красоты реализацию
- получишь вариант - см ниже
- */
- public <V> V assertThat(ExpectedCondition<V> condition, long timeout){
- return new WebDriverWait(getWebDriver(), timeout).until(condition);
- }
- ----------------------------------------------------------
- public <V> V assertThat(Function<? super WebDriver, V> condition) {
- /*
- раз у нас уже есть красивый public <V> V assertThat(ExpectedCondition<V> condition, long timeout)
- и есть Configuration.timeout - со значением таймаута
- в этом методе - вызови assertThat(ExpectedCondition<V> condition, long timeout)
- и передай в качестве таймаута Configuration.timeout
- я надеюсь, тип параметра condition и тут поправила )
- получишь - метод, который выполняет ждущую проверку с таймаутом = Configuration.timeout
- */
- -----------------------------------------
- /*
- в тест-классе - добавь BeforeClass-метод
- изменяющий Configuration.timeout = 20
- в тексте задания рекомендовано 15 секунд
- мне иногда не хватает
- */
- ------------------------------------------
- /*
- запускаем-проверяем
- уже не должно быть падений, вызванных маленьким таймаутом
- надо сказать - тест выполнился))
- но - пока радоваться рано)))
- */
- ************************************************************************
- *********************** ConciseAPI - упорядочиваем ********************
- ***********************************************************************
- /*
- Насколько я тебя поняла - ты решила реализовать версию, когда предок пейджа и предок тест-класса
- наследуются от абстрактного класса ConciseAPI
- ок)
- */
- ---------------------------------
- /*
- Переносим ConciseAPI в пекедж core
- т к эта штука - универсальная
- */
- ----------------------------------------
- @FindBy (css = "[role=main] .zA")
- private List<WebElement> mails;
- public void login(String email, String password) {
- public void newMail(String to, String subject) {
- public void assertMail(String text) {
- public void assertMails(String... texts) {
- public void switchToSent() {
- public void switchToInbox() {
- public void searchByText(String text) {
- /*
- Вот это все - ранее жило в пейдже/пейджах
- вот там ему и самое место
- разумно - предка делать без специфики
- чистым от подробностей и универсальным
- ну зачем нам в таком месте - что-то про gmail-овские инструменты
- соотвественно - в тест-методе - поправь вызовы методов
- таки внизу ревью напишу про ООП)
- */
- --------------------------------------------------
- assertThatUrlIs
- /*
- Оба метода - не используются в этом решении
- да и наш универсальный assertThat - позволит и урл проверить
- убиваем)
- */
- -----------------------------------------------
- public static By byText(String elementText) {
- return new ByText(elementText);
- }
- /*
- избавляемся от лишних сложностей -
- вместо использования дополнительного класса ByText
- напишем код вида By.xpath(...);
- и тут уже нам пригодится полезная строчка из класса ByText
- используй строчку и убивай класс ByText
- */
- ----------------------------------------------------
- public By byCss(String cssSelector) {
- public By by(String cssSelector) {
- /*
- прибивай метод by(String cssSelector)
- byCss(String cssSelector) - уже достаточно лаконично, но все еще понятно
- его достаточно
- в GmailPage ты разок использовала метод by - поправь это
- */
- ----------------------------------------------------
- public abstract class ConciseAPI {
- public abstract WebDriver getWebDriver();
- public void open(String url) {
- public <V> V assertThat(ExpectedCondition<V> condition, long timeout){
- public <V> V assertThat(ExpectedCondition<V> condition) {
- public WebElement $(By locator) {
- public WebElement $(String cssSelector) {
- public By byCss(String cssSelector) {
- public static By byText(String elementText) {
- public static By byAttribute(String attributeName, String attributeValue) {
- public static By byTitle(String title) {
- }
- /*
- вот собственно - и все, что тут останется
- только универсальное
- только применимое в каком угодно селениусмском проекте
- */
- ------------------------------------------
- /*
- проверяем-запускаем = должно работать
- радоваться по-прежнему рано)))
- */
- ********************************************************************
- *******************BasePage*****************************************
- ********************************************************************
- public class BasePage extends ConciseAPI{
- /*
- к реализации - вопросов нету)
- все ок реализовала
- только перенеси этот класс в core
- это тоже такой класс - который может быть переиспользован в любом селениумском проекте
- */
- ********************************************************************
- ************************GmailPage***********************************
- *******************************************************************
- /*
- вспоминай описание хорошей структуры проекта
- пекедж pages - с пейджами в ветке src\main
- перенеси
- внизу приведу кусочек по структуре еще раз (см конец ревью)
- вспомни селенидовскую версию этого задания
- у нас было 3 пейджа )
- и тут можно также реализовать, отнаследовав каждый из них от BasePage
- */
- -----------------------------------------------
- public GmailPage (WebDriver driver){
- ...
- @FindBy (css = "[role=main] .zA")
- private List<WebElement> mails;
- /*
- вот это - помести в самом начале кода пейджа
- уже говорила - проще воспринимать код, идя от общего к частному
- поравняй пропуски строк, реформатируй код
- */
- -------------------------------------
- public void login(String email, String password) {
- ...
- assertThat(
- presenceOfElementLocated(byCss("#Passwd")));
- ...
- /*
- ты такими проверками - затыкала проблему с нехваткой таймаута
- метод $ - ждет видимости элемента в рамках таймаута
- не нужно тебе еще одно ожидание
- метод $ - все нужное уже делает
- */
- -----------------------------------------------------------
- public void newMail(String to, String subject) {
- ...
- assertThat(presenceOfElementLocated(By.name("to")));
- /*
- это - тоже не надо
- соображения - те же
- */
- ...
- getWebDriver().findElement(By.tagName("body")).getText().contains("Your message has been sent");
- /*
- эта строка возвращает true или false
- и ничего не ждет))
- да и не надо тебе тут ожиданий
- убивай строку)
- */
- }
- -----------------------------------------------------------------------------------
- public void assertMail(String text) {
- listNthElementHasText(mails, 1, text);
- }
- public void assertMails(String... texts) {
- textsOf(mails, texts);
- }
- /*
- признаться, удивила ты меня такой реализацией)
- выражения - listNthElementHasText(...) и textsOf(...)
- возвращают нам значение типа ExpectedCondition
- и все)
- т е - проверок - мы не делаем
- а - надо делать проверку
- проверку мы делаем - вызывая assertThat для нужного нам кондишена
- это раз
- есть еще нюансы - я их на другом примере откомментирую
- ну и до кучи - почему listNthElementHasText(mails, 1, text)
- а не listNthElementHasText(mails, 0, text)
- в селенидовском проекте - с нуля нумеровали
- а тут почему?
- тоже на эту тему материалов тебе накидаю - см внизу
- */
- ---------------------------------------
- /*
- исправляем-запускаем = падает на проверке assertMail
- с очень станной ошибкой)
- org.openqa.selenium.UnhandledAlertException: Unexpected modal dialog (text: This page is asking you to confirm that you want to leave - data you have entered may not be saved.): This page is asking you to confirm that you want to leave - data you have entered may not be saved.
- ну, наверное, это хорошо - что я вижу проблему)))
- уже ближе к истине )
- осталось - привести в чувства CustomConditions
- */
- --------------------------------------------------------
- public static void refresh() {
- $(".asf").click();
- }
- /*
- помнишь - был такое метод в педже - в решении селенидовском?
- почему тут от него избавилась и юзаешь driver.navigate().refresh();?
- это ж разные вещи)
- реализуй в пейдже аналогичный метод и используй его в тест-методе
- ошибки останутся, но будут уже другими
- ни про какие диалоги - речи не будет идти
- кстати - если бы отлаживала код пошагово - наверняка бы увидела эту проблему
- осваивай отладку
- */
- *************************************************************************
- ********************CustomConditions ****************
- *************************************************************************
- public class CustomConditions {
- public static List actualTexts(List<WebElement> elements)
- {
- List<String> actualTexts;
- actualTexts = new ArrayList<String>();
- for (WebElement element : elements) {
- actualTexts.add(element.getText());
- }
- return actualTexts;
- }
- /*
- лучше имена методов формировать по принципу - что сделать=глагол + детали
- https://docs.google.com/document/d/13dNyFGbI7mV22UUhH8E0LJ7SzabAmX7Bw7VCHScYfiU/edit#bookmark=id.2pvr3ijzfuho
- из списка вебэлементов - мы только actual тексты и можем выбрать)
- поэтому - такие очевидные веши не указываем)
- т е getTexts(List<WebElement> elements) - будет в самый раз
- дальше момент
- этот метод - вспомогательный для наших кондишенов
- и в общем - не завязан именно на импользование только в кондишенах
- потому - его разумно вынести в класс-контейнер статических универсальных методов
- (Helpers , к примеру), размести его в пекедже core
- и тут - только используй
- даже если бы был у нас метод вспомогательный для кондишенов, который ну только кондишенам нужен
- то его стоило разместить не в начале кода класса, а в конце - двигаться от общего к частному
- так легче понимать незнакомый или забытый код)
- */
- ----------------------------------------------
- public static ExpectedCondition<List<WebElement>> textsOf(final By elementsLocator, final String... texts) {
- ....
- private List<String> actualTexts;
- /*
- вот мы объявили список
- */
- ...
- actualTexts(elements);
- /*
- вот мы вызвали метод, который список возвращает
- но - возвращенное значение не присвоили никакой переменной
- вернули и вернули
- а List<String> actualTexts - как был не инициализированным, так и остался
- если метод тебе что-то возвращает - и тебя интересует это значение - надо
- это фиксировать в некой переменной
- SomeClass someVar;
- ...
- someVar = getValueOfSomeClass(....);
- иначе - да, мы значение получили, но им не воспользовались
- тут у тебя должен быть код
- не
- actualTexts(elements);
- а
- actualTexts = actualTexts(elements);
- */
- -------------------------------------
- /*
- пока прибей лишние кондишены - которые с параметром типа By работают
- сначала эти отладь
- потом - когда будешь делать версию без файндбаев - реализуешь
- */
- -------------------------------------------
- public static ExpectedCondition listNthElementHasText(final List<WebElement> elements, final int index, final String text){
- return new ExpectedCondition<Boolean>() {
- private List<String> actualTexts;
- public Boolean apply(WebDriver driver) {
- actualTexts = actualTexts(elements);
- /*
- падает на строке actualTexts = actualTexts(elements);
- с ошибкой StaleElementReferenceException
- ну да, а try - ниже
- не меняй пока...
- */
- ---------------------------------------------
- https://docs.google.com/document/d/1BiYTLdypDfucSqiY9isv1HCKKQIxelzqYrN-3Ku1RWM/edit
- /*
- вот это - примени
- и тогда - проблема с возникноверием исключений будет решена аккуратнее
- */
- -------------------------------------------------
- В listNthElementHasText - странный toString()
- /*
- в toString() - показывай фактические тексты всех элементов и ожидаемый текст и индекс
- посмотри на формулировки в toString() - у родных селениумских кондишенов
- примени знания тут - какие-то стандартные обороты
- не надо в toString - снова получать элементы
- elements.get(index)
- и тут может возникнуть исключение
- все, что нужно вывести - ты собрала в apply
- там у нас исключения ловились, все ок было
- */
- ----------------------------------
- ExpectedCondition listNthElementHasText
- /*
- реализуй этот кондишен - как ExpectedCondition<WebElement>
- в случае удачной проверки - возвращай в apply элемент с таким текстом
- так больше пользы будет от возвращаемого результата
- */
- ********************Полезное - к сведению**********************************
- ************************************************************************
- **********************ООП***********************************************
- *************************************************************************
- /*
- вообще не стоит злоупотреблять наследованием
- советую пересмотреть видео Composition over Inheritance
- https://drive.google.com/file/d/0B8hgIBw8-V-AYkdNNDRJWVFndHc/view
- да и погугли это выражение )
- мноого интересного найдешь
- если кратко и на пальцах, по наследованию
- предок-потомок - хорошо и правильно реализованы
- если мы можем сказать
- потомок - это тоже предок
- и в этой фразе не будет ничего нелогичного
- class GmailPage extends BasePage
- GmailPage = это тоже BasePage - нормально все с логикой
- с предком тест-класса и тест-классом тоже все ОК
- а вот
- class BasePage extends ConciseAPI - уже вопросы к логике
- да, мы тут с п омощью абстрактного предка технично выкрутились
- с вебдрайвером и универсальными инструментами - чтоб и в пейджах, и в тесте они были доступны
- но - это и правда - недостаток текущей версии
- мы ее, кстати, должны были в следующем задании реализовывать)
- не страшно, сделаем оба задания сразу
- еще момент
- предок - должен содержать общее - для всех потомков
- не надо в предка впихивать подробности одного из потомков
- это делает логику странной и не понятной
- потом - если ты забыл, или если ты такое чужого авторства видишь
- очень тяжело понять - что на чем стояло
- предок - организм
- он ест, дышит, живет
- не пишем - что жаба например, ест вот так, живет там и т п
- т к тут не только жабы будут в наследниках
- тут мы схематично описываем общее в логике всех потомков
- потомок - жаба
- вот тут уже даем волю уточнениям и деталям ))
- потомок - обезьяна
- тут - тоже детали, но другие
- и детали жабы изнутри обезьяны нам не видны
- и это - хорошо и правильно
- а вот общая логика предка Организм - как раз и к обезьяне относится,
- и к жабе = доступна в любом потомке
- при этом - в
- этой логике нет лишних, чужих деталей
- */
- *******************************************************************
- *********************************************************************
- /*
- Что почитать про Generics
- про дженерики в общем(русский)
- http://www.quizful.net/post/java-generics-tutorial
- http://www.tutorialspoint.com/java/java_generics.htm
- http://developer.alexanderklimov.ru/android/java/generic.php
- конвеншенcы http://stackoverflow.com/questions/2900881/generic-type-parameter-naming-convention-for-java-with-multiple-chars
- https://docs.oracle.com/javase/tutorial/java/generics/types.html
- уроки
- http://docs.oracle.com/javase/tutorial/extra/generics/index.html
- очень приличный faq (есть pdf, и есть кое-что еще, помимо дженериков)
- http://www.angelikalanger.com/GenericsFAQ/JavaGenericsFAQ.html
- ----------------------------------------------------------
- для понимания кетчера (Catcher)
- http://grepcode.com/file/repo1.maven.org/maven2/com.google.guava/guava/r06/com/google/common/base/Function.java
- http://www.programcreek.com/java-api-examples/com.google.common.base.Function
- https://docs.oracle.com/javase/tutorial/java/generics/lowerBounded.html
- http://stackoverflow.com/questions/3847162/java-generics-super-keyword
- http://stackoverflow.com/questions/2800369/bounding-generics-with-super-keyword
- http://stackoverflow.com/questions/1910892/what-is-the-difference-between-super-and-extends-in-java-generics
- */
- *******************************************************************************
- ********************************************************************************
- /*
- http://joxi.ru/nAyqEx7HXvxQoA
- http://prnt.sc/bvuytd
- вот пример хорошей структуры проекта
- в src \ main
- core - универсальное, что можно переиспользовать в разных проектах
- pages - пейджи тоже можно переиспользовать для других тестов этого же приложения
- в src \ test
- testdata - тестовые данные (если такие есть и они вынесены в отдельный класс)
- testconfigs - предки тест-класса (так можно их изолировать от собственно тест-классов - чтоб легче было ориентироваться
- про пекеджи еще немного)
- если GroupID = com.somesite
- а проект todomvctest
- то пакет корневой должен быть com.somesite.todomvctest
- логика - чтобы "не смешивались имена сущностей"
- внутри одной компании - может быть несколько проектов)
- и у всех у них один com.somesite - базовый пекедж
- но для каждого проекта должен быть свой “базовый пекедж проекта"
- иначе все смешается)
- важно то, что когда этот проект выльется в отдельную библиотеку,
- то не будет конфликтов при его подключении
- */
- ********************************************************
- /*
- лучше - нумеруй с нуля и называй параметр - index
- лучше - быть понятнее для большинства
- большинство в большинстве случаев столкнулось с нумерацией с нуля)
- большинство увидело тот же термин - index
- а для индекса элемента в коллекции/массиве - применяется именно термин index
- мы используя такой термин - лишний раз подчеркнули - нумеруем с нуля
- надеюсь, я тебя убедила
- https://en.wikipedia.org/wiki/Zero-based_numbering
- http://c2.com/cgi/wiki?ZeroAndOneBasedIndexes
- http://stackoverflow.com/questions/24841172/why-is-array-indexing-in-java-start-with-0
- */
Advertisement
Add Comment
Please, Sign In to add comment