diff --git a/api-tests-module/pom.xml b/api-tests-module/pom.xml new file mode 100644 index 0000000..cbede40 --- /dev/null +++ b/api-tests-module/pom.xml @@ -0,0 +1,56 @@ + + + + school-2016 + ru.qatools.school + 1.0-SNAPSHOT + + 4.0.0 + + api-tests-module + API Tests + + + + com.jayway.restassured + rest-assured + 2.9.0 + + + com.squareup.retrofit2 + retrofit + 2.0.2 + + + com.google.code.gson + gson + 2.6.2 + + + com.squareup.retrofit2 + converter-gson + 2.0.2 + + + org.hamcrest + hamcrest-all + + + ru.yandex.qatools.allure + allure-junit-adaptor + + + ru.qatools.school + dbclient-module + 1.0-SNAPSHOT + + + ru.yandex.qatools.matchers + collection-matchers + 1.3 + + + + \ No newline at end of file diff --git a/api-tests-module/src/test/java/ru/qatools/school/Responses/CitiesResponse.java b/api-tests-module/src/test/java/ru/qatools/school/Responses/CitiesResponse.java new file mode 100644 index 0000000..f35b949 --- /dev/null +++ b/api-tests-module/src/test/java/ru/qatools/school/Responses/CitiesResponse.java @@ -0,0 +1,10 @@ +package ru.qatools.school.Responses; + +/** + * @author ava1on + */ +public class CitiesResponse { + + public CitiesResponse() { + } +} diff --git a/api-tests-module/src/test/java/ru/qatools/school/Responses/SuggestResponse.java b/api-tests-module/src/test/java/ru/qatools/school/Responses/SuggestResponse.java new file mode 100644 index 0000000..699b312 --- /dev/null +++ b/api-tests-module/src/test/java/ru/qatools/school/Responses/SuggestResponse.java @@ -0,0 +1,12 @@ +package ru.qatools.school.Responses; + +/** + * @author ava1on + */ +public class SuggestResponse { + private int id; + + public SuggestResponse(int id){ + this.id = id; + } +} diff --git a/api-tests-module/src/test/java/ru/qatools/school/Responses/TemperatureValues.java b/api-tests-module/src/test/java/ru/qatools/school/Responses/TemperatureValues.java new file mode 100644 index 0000000..965a8c4 --- /dev/null +++ b/api-tests-module/src/test/java/ru/qatools/school/Responses/TemperatureValues.java @@ -0,0 +1,16 @@ +package ru.qatools.school.Responses; + +/** + * @author ava1on + */ +public class TemperatureValues { + private double value; + + public TemperatureValues(double value) { + this.value = value; + } + + public double getValue() { + return value; + } +} diff --git a/api-tests-module/src/test/java/ru/qatools/school/Responses/WeatherResponse.java b/api-tests-module/src/test/java/ru/qatools/school/Responses/WeatherResponse.java new file mode 100644 index 0000000..219e4b1 --- /dev/null +++ b/api-tests-module/src/test/java/ru/qatools/school/Responses/WeatherResponse.java @@ -0,0 +1,20 @@ +package ru.qatools.school.Responses; + +/** + * @author ava1on + */ +public class WeatherResponse { + private TemperatureValues[] temperatures; + + public WeatherResponse(TemperatureValues[] temperatures) { + this.temperatures = temperatures; + } + + public TemperatureValues[] getTemperatures() { + return temperatures; + } + + public Double recalculateCelsiusToFahrenheit(){ + return (this.getTemperatures()[0].getValue()*9/5+32); + } +} diff --git a/api-tests-module/src/test/java/ru/qatools/school/apitests/DbAPITest.java b/api-tests-module/src/test/java/ru/qatools/school/apitests/DbAPITest.java new file mode 100644 index 0000000..1ec7df2 --- /dev/null +++ b/api-tests-module/src/test/java/ru/qatools/school/apitests/DbAPITest.java @@ -0,0 +1,48 @@ +package ru.qatools.school.apitests; + +import org.apache.http.HttpStatus; +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import ru.qatools.school.DbClient; +import ru.qatools.school.rules.DbClientRule; +import ru.yandex.qatools.allure.annotations.Title; + +import java.util.List; + +import static com.jayway.restassured.RestAssured.given; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.core.Is.is; +import static ru.qatools.school.data.Constants.*; + +/** + * @author ava1on + */ +public class DbAPITest { + private DbClient dbClient; + + @Rule + public DbClientRule dbClientRule = new DbClientRule(); + + @Before + public void setupDbConnection(){ + dbClient = dbClientRule.getDbClient(); + } + + @Test + @Title("Списки городов, полученные через API и Database, должны совпадать") + public void apiResponseShouldMatchDatabaseData(){ + List ids = + given() + .baseUri(BASE_URL) + .param(QUERY_PARAMETER, CITYNAME_PART) + .when() + .get(SUGGEST_REQUEST) + .then() + .assertThat().statusCode(HttpStatus.SC_OK) + .and().extract().body().path("id"); + assertThat("Полученные через API данные не соответствуют Database", ids, + is(dbClient.getCitiesByNamePart(CITYNAME_PART))); + } +} diff --git a/api-tests-module/src/test/java/ru/qatools/school/apitests/RestAssuredAPITests.java b/api-tests-module/src/test/java/ru/qatools/school/apitests/RestAssuredAPITests.java new file mode 100644 index 0000000..4f9bd6b --- /dev/null +++ b/api-tests-module/src/test/java/ru/qatools/school/apitests/RestAssuredAPITests.java @@ -0,0 +1,51 @@ +package ru.qatools.school.apitests; + +import org.apache.http.HttpStatus; +import org.junit.Test; +import ru.qatools.school.Responses.CitiesResponse; +import ru.qatools.school.Responses.WeatherResponse; +import ru.yandex.qatools.allure.annotations.Title; + +import static com.jayway.restassured.RestAssured.given; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.closeTo; +import static org.hamcrest.Matchers.is; +import static ru.qatools.school.data.Constants.*; + +/** + * @author ava1on + */ +public class RestAssuredAPITests { + + @Test + @Title("Должны получить заданное в параметре количество городов в ответе") + public void shouldReturnRequestedNumberOfCities(){ + CitiesResponse[] citiesArray = + given(). + baseUri(BASE_URL). + param(LIMIT_PARAMETER, LIMIT_VALUE). + when(). + get(CITIES_REQUEST). + then().statusCode(HttpStatus.SC_OK). + and().extract(). + body().as(CitiesResponse[].class); + assertThat("Неверное количество городов в ответе", citiesArray.length, is(LIMIT_VALUE)); + } + + @Test + @Title("Значение температуры по шкале Фаренгейта должно соответствовать значению по шкале Цельсия") + public void shouldReturnCompatibleTemperatureValuesForCelsiusAndFahrenheit(){ + WeatherResponse weatherResponse = + given(). + baseUri(BASE_URL). + param(CITY_PARAMETER, CITY_VALUE). + when(). + get(WEATHER_REQUEST). + then().statusCode(HttpStatus.SC_OK). + and().extract(). + body().as(WeatherResponse.class); + assertThat("Значение температуры не правильно переведено из шкалы Цельсия в шкалу Фаренгейта", + weatherResponse.getTemperatures()[2].getValue(), + is(closeTo(weatherResponse.recalculateCelsiusToFahrenheit(), TEMPERATURE_ERROR))); + } +} \ No newline at end of file diff --git a/api-tests-module/src/test/java/ru/qatools/school/apitests/RetrofitAPITests.java b/api-tests-module/src/test/java/ru/qatools/school/apitests/RetrofitAPITests.java new file mode 100644 index 0000000..3b6e40e --- /dev/null +++ b/api-tests-module/src/test/java/ru/qatools/school/apitests/RetrofitAPITests.java @@ -0,0 +1,55 @@ +package ru.qatools.school.apitests; + +import org.apache.http.HttpStatus; +import org.junit.BeforeClass; +import org.junit.Test; +import retrofit2.Response; +import retrofit2.Retrofit; +import retrofit2.converter.gson.GsonConverterFactory; +import ru.qatools.school.Responses.CitiesResponse; +import ru.qatools.school.Responses.WeatherResponse; +import ru.qatools.school.interfaces.Cities; +import ru.qatools.school.interfaces.Weather; +import ru.yandex.qatools.allure.annotations.Title; + +import java.io.IOException; +import java.util.List; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.closeTo; +import static org.hamcrest.Matchers.hasSize; +import static org.hamcrest.Matchers.is; +import static ru.qatools.school.data.Constants.*; + +/** + * @author ava1on + */ +public class RetrofitAPITests { + + private static Retrofit retrofit; + + @BeforeClass + public static void initRetrofit(){ + retrofit = new Retrofit.Builder().baseUrl(BASE_URL).addConverterFactory(GsonConverterFactory.create()).build(); + } + + @Test + @Title("Должны получить заданное в параметре количество городов в ответе") + public void shouldReturnRequestedNumberOfCities() throws IOException{ + Cities cities = retrofit.create(Cities.class); + Response> response = cities.citiesList(LIMIT_VALUE).execute(); + assertThat("Код ответа неверный", response.code(), is(HttpStatus.SC_OK)); + assertThat("Неверное количество городов в ответе", response.body(), hasSize(LIMIT_VALUE)); + } + + @Test + @Title("Значение температуры по шкале Фаренгейта должно соответствовать значению по шкале Цельсия") + public void shouldReturnCompatibleTemperatureValuesForCelsiusAndFahrenheit() throws IOException{ + Weather weather = retrofit.create(Weather.class); + Response response = weather.weather(CITY_PARAMETER, null).execute(); + assertThat("Код ответа неверный", response.code(), is(HttpStatus.SC_OK)); + assertThat("Значение температуры не правильно переведено из шкалы Цельсия в шкалу Фаренгейта", + response.body().getTemperatures()[2].getValue(), + is(closeTo(response.body().recalculateCelsiusToFahrenheit(), TEMPERATURE_ERROR))); + } +} \ No newline at end of file diff --git a/api-tests-module/src/test/java/ru/qatools/school/data/Constants.java b/api-tests-module/src/test/java/ru/qatools/school/data/Constants.java new file mode 100644 index 0000000..91be564 --- /dev/null +++ b/api-tests-module/src/test/java/ru/qatools/school/data/Constants.java @@ -0,0 +1,19 @@ +package ru.qatools.school.data; + +/** + * @author ava1on + */ +public class Constants { + public static final String BASE_URL = "http://weather.lanwen.ru/api/"; + public static final String LIMIT_PARAMETER = "limit"; + public static final String CITIES_REQUEST = "cities"; + public static final int LIMIT_VALUE = 3; + public static final String WEATHER_REQUEST = "weather"; + public static final String CITY_PARAMETER = "city"; + public static final String CITY_VALUE = "Saint Petersburg"; + public static final double TEMPERATURE_ERROR = 0.1; + public static final String REGION_PARAMETER = "region"; + public static final String SUGGEST_REQUEST = "suggest"; + public static final String QUERY_PARAMETER = "query"; + public static final String CITYNAME_PART = "Saint"; +} diff --git a/api-tests-module/src/test/java/ru/qatools/school/interfaces/Cities.java b/api-tests-module/src/test/java/ru/qatools/school/interfaces/Cities.java new file mode 100644 index 0000000..3e32fe8 --- /dev/null +++ b/api-tests-module/src/test/java/ru/qatools/school/interfaces/Cities.java @@ -0,0 +1,18 @@ +package ru.qatools.school.interfaces; + +import retrofit2.Call; +import retrofit2.http.GET; +import retrofit2.http.Query; +import ru.qatools.school.Responses.CitiesResponse; + +import java.util.List; + +import static ru.qatools.school.data.Constants.LIMIT_PARAMETER; + +/** + * @author ava1on + */ +public interface Cities { + @GET("cities") + Call> citiesList(@Query(LIMIT_PARAMETER) int limitValue); +} diff --git a/api-tests-module/src/test/java/ru/qatools/school/interfaces/Weather.java b/api-tests-module/src/test/java/ru/qatools/school/interfaces/Weather.java new file mode 100644 index 0000000..6ff0d8a --- /dev/null +++ b/api-tests-module/src/test/java/ru/qatools/school/interfaces/Weather.java @@ -0,0 +1,17 @@ +package ru.qatools.school.interfaces; + +import retrofit2.Call; +import retrofit2.http.GET; +import retrofit2.http.Query; +import ru.qatools.school.Responses.WeatherResponse; + +import static ru.qatools.school.data.Constants.CITY_PARAMETER; +import static ru.qatools.school.data.Constants.REGION_PARAMETER; + +/** + * @author ava1on + */ +public interface Weather { + @GET("weather") + Call weather(@Query(CITY_PARAMETER) String city, @Query(REGION_PARAMETER) String region); +} \ No newline at end of file diff --git a/commons-module/pom.xml b/commons-module/pom.xml index 97cb03e..13c90d9 100644 --- a/commons-module/pom.xml +++ b/commons-module/pom.xml @@ -29,8 +29,6 @@ com.tngtech.java junit-dataprovider - 1.10.4 - test diff --git a/commons-module/src/test/java/ru/qatools/school/webtests/WeatherWebTest.java b/commons-module/src/test/java/ru/qatools/school/webtests/WeatherWebTest.java index a4c5a40..a36cc5a 100644 --- a/commons-module/src/test/java/ru/qatools/school/webtests/WeatherWebTest.java +++ b/commons-module/src/test/java/ru/qatools/school/webtests/WeatherWebTest.java @@ -1,36 +1,202 @@ package ru.qatools.school.webtests; +import com.tngtech.java.junit.dataprovider.DataProvider; +import com.tngtech.java.junit.dataprovider.DataProviderRunner; +import com.tngtech.java.junit.dataprovider.UseDataProvider; import org.junit.Before; import org.junit.Rule; import org.junit.Test; +import org.junit.runner.RunWith; +import ru.qatools.school.data.DataPatterns; import ru.qatools.school.pages.MainPage; +import ru.qatools.school.pages.blocks.WeatherWidget; import ru.qatools.school.rules.WebDriverRule; import ru.qatools.school.steps.websteps.DefaultSteps; +import ru.qatools.school.tp.TPInformerRule; +import ru.yandex.qatools.allure.annotations.TestCaseId; import ru.yandex.qatools.allure.annotations.Title; +@RunWith(DataProviderRunner.class) public class WeatherWebTest { - public static final String MOSCOW = "Moscow"; + private static final String MOSCOW = "Moscow"; + private static final String SPB = "Saint Petersburg"; + private static final String PART_OF_CITYNAME = "Saint P"; + private static final String PAGE_TITLE = "Weather"; + private static final String NEW_WIDGET = "What a city?"; + + @DataProvider + public static Object[][] weatherDataFormat() { + return new Object[][]{ + {0, DataPatterns.SUNRISE}, + {1, DataPatterns.SUNSET}, + {2, DataPatterns.WIND}, + {3, DataPatterns.HUMIDITY} + }; + } + + @DataProvider + public static Object[][] temperatureFormat(){ + return new Object[][]{ + {0, DataPatterns.CELSIUS}, + {1, DataPatterns.KELVIN}, + {2, DataPatterns.FAHRENHEIT}, + {3, DataPatterns.KAIF}, + {4, DataPatterns.CELSIUS} + }; + } private DefaultSteps defaultSteps; @Rule public WebDriverRule webDriverRule = new WebDriverRule(); + @Rule + public TPInformerRule tms = new TPInformerRule("ava1on"); + @Before public void initSteps() { defaultSteps = new DefaultSteps(webDriverRule.getDriver()); } @Test + @TestCaseId("6") + @Title("Должны видеть только кнопку [+] ") + public void shouldSeeOnlyAddWidgetButton(){ + defaultSteps.openMainPageWithoutParameters(); + defaultSteps.shouldSee(onMainPage().getAddNewWidgetButton()); + defaultSteps.shouldHaveWidgetNumberOnMainPage(0); + } + + @Test + @TestCaseId("2") @Title("Должны видеть виджет на главной странице") public void shouldSeeWidgetOnMainPage() { defaultSteps.openMainPageWithCity(MOSCOW); - defaultSteps.shouldSee(onMainPage().getWeatherWidget().get(0)); + defaultSteps.shouldSee(getFirstWidget()); + } + + @Test + @TestCaseId("8") + @Title("В заголовке виджета должны видеть город, указанный в запросе") + public void shouldSeeSelectedCityOnWidgetTitle() { + defaultSteps.openMainPageWithCity(MOSCOW); + defaultSteps.shouldSeeCityInWidgetsTitle(MOSCOW); + } + + @Test + @TestCaseId("16") + @Title("Должны видеть новый виджет при открытии страницы без параметров") + public void shouldSeeNewWidgetOnMainPageWithoutParameters() { + defaultSteps.openMainPageWithCity(""); + defaultSteps.shouldSeeCityInWidgetsTitle(NEW_WIDGET); + } + + @Test + @TestCaseId("3") + @Title("Должны видеть на один виджет больше после нажатия на кнопку [+]") + public void shouldSeeOneMoreWidget() { + defaultSteps.openMainPageWithCity(MOSCOW); + int numberOfWidgets = onMainPage().getWeatherWidgets().size(); + defaultSteps.clickOn(onMainPage().getAddNewWidgetButton()); + defaultSteps.shouldHaveWidgetNumberOnMainPage(numberOfWidgets+1); + } + + @Test + @TestCaseId("5") + @Title("Должны видеть на один виджет меньше после нажатия на кнопку [-]") + public void shouldSeeLessWidgets(){ + defaultSteps.openMainPageWithCity(MOSCOW); + int numberOfWidgets = onMainPage().getWeatherWidgets().size(); + defaultSteps.clickOn(getFirstWidget().getRemoveWidgetButton()); + defaultSteps.shouldHaveWidgetNumberOnMainPage(numberOfWidgets-1); + } + + @Test + @TestCaseId("9") + @Title("Должны видеть время и дату в формате \"Ч AM/PM, дд ммм гг\"") + public void shouldSeeDateAndTime(){ + defaultSteps.openMainPageWithCity(MOSCOW); + defaultSteps.shouldMatchPattern(getFirstWidget().getWidgetTitle().getCurrentTime(), DataPatterns.TIME_DATE); + } + + @Test + @TestCaseId("11") + @Title("Должны видеть новое название города после изменения") + public void shouldSeeNewCityAfterChangeUsingEnterKey(){ + defaultSteps.openMainPageWithCity(MOSCOW); + defaultSteps.changeWidgetTitle(getFirstWidget(), SPB); + defaultSteps.shouldSeeCityInWidgetsTitle(SPB); + } + + @Test + @TestCaseId("12") + @Title("Должны видеть список автозаполнения") + public void shouldSeeSuggestCitiesList(){ + defaultSteps.openMainPageWithCity(MOSCOW); + defaultSteps.suggestList(PART_OF_CITYNAME, getFirstWidget().getWidgetTitle()); + defaultSteps.shouldSee(getFirstWidget().getWidgetTitle().getSuggestedCitiesList()); + } + + @Test + @TestCaseId("13") + @Title("В списке автозаполнения должны отображаться только города, содержащие введенную строку") + public void shouldSeeSuitableSuggestedCities(){ + defaultSteps.openMainPageWithCity(MOSCOW); + defaultSteps.suggestList(PART_OF_CITYNAME, getFirstWidget().getWidgetTitle()); + defaultSteps.shouldOnlySeeCitiesContaining(PART_OF_CITYNAME); + } + + @Test + @TestCaseId("14") + @Title("Должны изменить город используя список автозаполения") + public void shouldSeeNewCityAfterChangeUsingSuggestedList(){ + defaultSteps.openMainPageWithCity(MOSCOW); + defaultSteps.suggestList(PART_OF_CITYNAME, getFirstWidget().getWidgetTitle()); + defaultSteps.selectItemFromSuggestedList(SPB); + defaultSteps.shouldSeeCityInWidgetsTitle(SPB); + } + + @Test + @UseDataProvider("temperatureFormat") + @TestCaseId("4") + @Title("Температура должна отображатся в правильном формате") + public void shouldSeeTemperature(int numberOfClicks, DataPatterns pattern) { + defaultSteps.openMainPageWithCity(MOSCOW); + defaultSteps.clickOnSeveralTimes(getFirstWidget().getWidgetText().getTemperature(), numberOfClicks); + defaultSteps.shouldMatchPattern(getFirstWidget().getWidgetText().getTemperature(), pattern); + } + + @Test + @UseDataProvider("weatherDataFormat") + @TestCaseId("7") + @Title("Погодные данные должны отображаться в правильном формате") + public void shouldSeeWeatherData(int id, DataPatterns pattern) { + defaultSteps.openMainPageWithCity(MOSCOW); + defaultSteps.shouldMatchPattern(getFirstWidget().getWidgetText().getWeatherData().get(id), pattern); + } + + @Test + @TestCaseId("10") + @Title("Должны видеть заголовок страницы") + public void shouldSeePageTitle(){ + defaultSteps.openMainPageWithoutParameters(); + defaultSteps.shouldSeeTitle(PAGE_TITLE); + } + + @Test + @TestCaseId("23") + @Title("Должны видеть иконку погоды") + public void shouldSeeWeatherImage(){ + defaultSteps.openMainPageWithCity(MOSCOW); + defaultSteps.shouldSee(getFirstWidget().getWidgetText().getWeatherImage()); } private MainPage onMainPage() { return new MainPage(webDriverRule.getDriver()); } -} + private WeatherWidget getFirstWidget() { + return onMainPage().getWeatherWidgets().get(0); + } +} \ No newline at end of file diff --git a/dbclient-module/src/main/java/ru/qatools/school/DbClient.java b/dbclient-module/src/main/java/ru/qatools/school/DbClient.java index 12e1204..b9fb76d 100644 --- a/dbclient-module/src/main/java/ru/qatools/school/DbClient.java +++ b/dbclient-module/src/main/java/ru/qatools/school/DbClient.java @@ -1,11 +1,14 @@ package ru.qatools.school; -import org.jooq.*; +import org.jooq.DSLContext; +import org.jooq.Record1; +import org.jooq.SQLDialect; import org.jooq.impl.DSL; import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; +import java.util.List; import static org.jooq.impl.DSL.field; import static org.jooq.impl.DSL.table; @@ -17,7 +20,7 @@ public class DbClient { private static final String CONNECTION_STRING = System.getProperty("db.url", "jdbc:mysql://db.host.ru:3310/db_name"); private static final String USER = System.getProperty("db.user", "user"); - private static final String PASSWORD = System.getProperty("db.password", "password");; + private static final String PASSWORD = System.getProperty("db.password", "password"); private Connection connection; private DSLContext create; @@ -39,6 +42,12 @@ public String getCityById(Integer id) { return result.getValue(0, String.class); } + public List getCitiesByNamePart(String namePart){ + return create.selectFrom(table("City")) + .where(field("name").like("%" + namePart + "%")) + .fetch().getValues(field("id"), Integer.class); + } + public void close() { try { connection.close(); diff --git a/dbclient-module/src/main/java/ru/qatools/school/rules/dbClientRule.java b/dbclient-module/src/main/java/ru/qatools/school/rules/dbClientRule.java new file mode 100644 index 0000000..5565e17 --- /dev/null +++ b/dbclient-module/src/main/java/ru/qatools/school/rules/dbClientRule.java @@ -0,0 +1,24 @@ +package ru.qatools.school.rules; + +import org.junit.rules.ExternalResource; +import ru.qatools.school.DbClient; + +/** + * @author ava1on + */ +public class DbClientRule extends ExternalResource { + + private DbClient dbClient; + + public void before(){ + this.dbClient = new DbClient(); + } + + public void after(){ + dbClient.close(); + } + + public DbClient getDbClient(){ + return dbClient; + } +} diff --git a/pom.xml b/pom.xml index 2f0da8b..bd58953 100644 --- a/pom.xml +++ b/pom.xml @@ -15,6 +15,7 @@ steps-module commons-module + api-tests-module dbclient-module @@ -33,18 +34,22 @@ junit 4.12 + + com.tngtech.java + junit-dataprovider + 1.10.4 + test + - com.jayway.restassured rest-assured 2.9.0 - ru.yandex.qatools.allure allure-junit-adaptor diff --git a/steps-module/pom.xml b/steps-module/pom.xml index 9bcfe79..cb8a2be 100644 --- a/steps-module/pom.xml +++ b/steps-module/pom.xml @@ -40,11 +40,20 @@ ru.yandex.qatools.htmlelements htmlelements-matchers - com.jayway.restassured rest-assured + + org.cthul + cthul-matchers + 1.1.0 + + + ru.yandex.qatools.matchers + webdriver-matchers + 1.3 + diff --git a/steps-module/src/main/java/ru/qatools/school/data/DataPatterns.java b/steps-module/src/main/java/ru/qatools/school/data/DataPatterns.java new file mode 100644 index 0000000..ae2260d --- /dev/null +++ b/steps-module/src/main/java/ru/qatools/school/data/DataPatterns.java @@ -0,0 +1,32 @@ +package ru.qatools.school.data; + +/** + * @author ava1on + */ +public enum DataPatterns { + CELSIUS("^-?\\d{1,2}\\.\\d °C$"), + KELVIN("^[1-3]\\d{2}\\.\\d °K$"), + FAHRENHEIT("^-?[1]?\\d{1,2}\\.\\d °F$"), + KAIF("^-?\\d{1,2}\\.\\d °Kaif$"), + SUNRISE("^Sunrise\\s[0-2][0-9]:[0-5][0-9]$"), + SUNSET("^Sunset\\s[0-2][0-9]:[0-5][0-9]$"), + WIND("^Wind\\s\\d{1,}\\.?\\d{0,2}? m\\/s$"), + HUMIDITY("^Humidity\\s[0-9]{1,} %$"), + TIME_DATE("^1?\\d [A|P]M, [0-3]\\d \\w{3} \\d{2}"); + + private final String pattern; + + DataPatterns(String pattern) { + this.pattern = pattern; + } + + public String getPattern(){ + return pattern; + } + + @Override + public String toString(){ + return pattern; + } + +} diff --git a/steps-module/src/main/java/ru/qatools/school/pages/MainPage.java b/steps-module/src/main/java/ru/qatools/school/pages/MainPage.java index 4d75376..2cef9c8 100644 --- a/steps-module/src/main/java/ru/qatools/school/pages/MainPage.java +++ b/steps-module/src/main/java/ru/qatools/school/pages/MainPage.java @@ -5,6 +5,7 @@ import org.openqa.selenium.support.PageFactory; import ru.qatools.school.pages.blocks.WeatherWidget; import ru.yandex.qatools.htmlelements.annotations.Name; +import ru.yandex.qatools.htmlelements.element.HtmlElement; import ru.yandex.qatools.htmlelements.loader.decorator.HtmlElementDecorator; import ru.yandex.qatools.htmlelements.loader.decorator.HtmlElementLocatorFactory; @@ -21,10 +22,17 @@ public MainPage(WebDriver driver) { @Name("Список виджетов") @FindBy(css = ".card.card_md") - private List weatherWidget; + private List weatherWidgets; - public List getWeatherWidget() { - return weatherWidget; + @Name("Кнопка добавления виджетов") + @FindBy(css = ".new-card") + private HtmlElement addNewWidgetButton; + + public List getWeatherWidgets() { + return weatherWidgets; } + public HtmlElement getAddNewWidgetButton(){ + return addNewWidgetButton; + } } diff --git a/steps-module/src/main/java/ru/qatools/school/pages/blocks/WeatherWidget.java b/steps-module/src/main/java/ru/qatools/school/pages/blocks/WeatherWidget.java index 3990d85..4f54264 100644 --- a/steps-module/src/main/java/ru/qatools/school/pages/blocks/WeatherWidget.java +++ b/steps-module/src/main/java/ru/qatools/school/pages/blocks/WeatherWidget.java @@ -1,7 +1,6 @@ package ru.qatools.school.pages.blocks; import org.openqa.selenium.Rectangle; -import org.openqa.selenium.WebElement; import org.openqa.selenium.support.FindBy; import ru.qatools.school.pages.blocks.widgetblocks.WidgetText; import ru.qatools.school.pages.blocks.widgetblocks.WidgetTitle; @@ -21,9 +20,9 @@ public class WeatherWidget extends HtmlElement { @FindBy(css = ".card-text") private WidgetText widgetText; - @Name("Панель управления виджетом") - @FindBy(css = ".card-actions") - private WebElement actions; + @Name("Кнопка удаления виджета") + @FindBy(css = ".remove-card") + private HtmlElement removeWidgetButton; public WidgetText getWidgetText() { return widgetText; @@ -33,8 +32,8 @@ public WidgetTitle getWidgetTitle() { return widgetTitle; } - public WebElement getActions() { - return actions; + public HtmlElement getRemoveWidgetButton() { + return removeWidgetButton; } public Rectangle getRect() { diff --git a/steps-module/src/main/java/ru/qatools/school/pages/blocks/widgetblocks/WidgetText.java b/steps-module/src/main/java/ru/qatools/school/pages/blocks/widgetblocks/WidgetText.java index 510d070..1207171 100644 --- a/steps-module/src/main/java/ru/qatools/school/pages/blocks/widgetblocks/WidgetText.java +++ b/steps-module/src/main/java/ru/qatools/school/pages/blocks/widgetblocks/WidgetText.java @@ -1,11 +1,12 @@ package ru.qatools.school.pages.blocks.widgetblocks; import org.openqa.selenium.Rectangle; -import org.openqa.selenium.WebElement; import org.openqa.selenium.support.FindBy; import ru.yandex.qatools.htmlelements.annotations.Name; import ru.yandex.qatools.htmlelements.element.HtmlElement; +import java.util.List; + /** * Created by kurau. */ @@ -13,12 +14,28 @@ public class WidgetText extends HtmlElement { @Name("Картинка текущей погоды") @FindBy(css = ".weather-image") - private WebElement weatherImage; + private HtmlElement weatherImage; + + @Name("Температура") + @FindBy(css = ".weather-temperature.md-12") + private HtmlElement temperature; - public WebElement getWeatherImage() { + @Name("Погодные данные") + @FindBy(css = ".line.info-line") + private List weatherData; + + public HtmlElement getWeatherImage() { return weatherImage; } + public HtmlElement getTemperature() { + return temperature; + } + + public List getWeatherData() { + return weatherData; + } + public Rectangle getRect() { return null; } diff --git a/steps-module/src/main/java/ru/qatools/school/pages/blocks/widgetblocks/WidgetTitle.java b/steps-module/src/main/java/ru/qatools/school/pages/blocks/widgetblocks/WidgetTitle.java index 8a88782..71800c2 100644 --- a/steps-module/src/main/java/ru/qatools/school/pages/blocks/widgetblocks/WidgetTitle.java +++ b/steps-module/src/main/java/ru/qatools/school/pages/blocks/widgetblocks/WidgetTitle.java @@ -1,13 +1,49 @@ package ru.qatools.school.pages.blocks.widgetblocks; import org.openqa.selenium.Rectangle; +import org.openqa.selenium.support.FindBy; +import ru.yandex.qatools.htmlelements.annotations.Name; import ru.yandex.qatools.htmlelements.element.HtmlElement; +import java.util.List; + /** * Created by kurau. */ public class WidgetTitle extends HtmlElement { + @Name("Название города") + @FindBy(css = ".inplace") + private HtmlElement cityName; + + @Name("Время и дата") + @FindBy(css = ".card-title__secondary") + private HtmlElement currentTime; + + @Name("Список автозаполнения") + @FindBy(css = ".city-suggest") + private HtmlElement suggestedCitiesList; + + @Name("Элементы списка автозаполнения") + @FindBy(css = ".city__name") + private List suggestedCities; + + public HtmlElement getCityName(){ + return cityName; + } + + public HtmlElement getCurrentTime() { + return currentTime; + } + + public HtmlElement getSuggestedCitiesList() { + return suggestedCitiesList; + } + + public List getSuggestedCities() { + return suggestedCities; + } + public Rectangle getRect() { return null; } diff --git a/steps-module/src/main/java/ru/qatools/school/rules/WebDriverRule.java b/steps-module/src/main/java/ru/qatools/school/rules/WebDriverRule.java index 418c363..2f0c894 100644 --- a/steps-module/src/main/java/ru/qatools/school/rules/WebDriverRule.java +++ b/steps-module/src/main/java/ru/qatools/school/rules/WebDriverRule.java @@ -3,6 +3,7 @@ import org.junit.rules.ExternalResource; import org.openqa.selenium.WebDriver; import org.openqa.selenium.firefox.FirefoxDriver; +import org.openqa.selenium.support.ui.WebDriverWait; /** * Created by kurau. @@ -10,9 +11,11 @@ public class WebDriverRule extends ExternalResource { private WebDriver driver; + private WebDriverWait webDriverWait; protected void before() throws Throwable { this.driver = new FirefoxDriver(); + this.webDriverWait = new WebDriverWait(driver, 5); } protected void after() { diff --git a/steps-module/src/main/java/ru/qatools/school/steps/websteps/DefaultSteps.java b/steps-module/src/main/java/ru/qatools/school/steps/websteps/DefaultSteps.java index 1425a42..64f3269 100644 --- a/steps-module/src/main/java/ru/qatools/school/steps/websteps/DefaultSteps.java +++ b/steps-module/src/main/java/ru/qatools/school/steps/websteps/DefaultSteps.java @@ -1,25 +1,41 @@ package ru.qatools.school.steps.websteps; +import org.openqa.selenium.Keys; import org.openqa.selenium.WebDriver; -import org.openqa.selenium.WebElement; +import org.openqa.selenium.support.ui.ExpectedConditions; +import org.openqa.selenium.support.ui.WebDriverWait; +import ru.qatools.school.data.DataPatterns; import ru.qatools.school.pages.MainPage; +import ru.qatools.school.pages.blocks.WeatherWidget; +import ru.qatools.school.pages.blocks.widgetblocks.WidgetTitle; import ru.yandex.qatools.allure.annotations.Step; +import ru.yandex.qatools.htmlelements.element.HtmlElement; + +import java.util.ArrayList; +import java.util.List; import static java.lang.String.format; +import static org.cthul.matchers.CthulMatchers.containsPattern; +import static org.hamcrest.Matchers.*; +import static org.hamcrest.core.Is.is; import static org.junit.Assert.assertThat; -import static ru.yandex.qatools.htmlelements.matchers.WebElementMatchers.isDisplayed; +import static ru.yandex.qatools.htmlelements.matchers.WebElementMatchers.*; +import static ru.yandex.qatools.matchers.webdriver.TextMatcher.text; /** * Created by kurau. */ public class DefaultSteps { - public static final String MAIN_PAGE = "http://weather.lanwen.ru/#?cities=%s"; + private static final String MAIN_PAGE = "http://weather.lanwen.ru/#?cities=%s"; + private static final String MAIN_PAGE_WITHOUT_PARAMETERS = "http://weather.lanwen.ru"; private WebDriver driver; + private WebDriverWait webDriverWait; public DefaultSteps(WebDriver driver) { this.driver = driver; + webDriverWait = new WebDriverWait(driver, 5); } @Step("Открываем главную страницу для города «{0}»") @@ -27,12 +43,86 @@ public void openMainPageWithCity(String city) { driver.get(format(MAIN_PAGE, city)); } + @Step("Открываем главную страницу приложения") + public void openMainPageWithoutParameters(){ + driver.get(MAIN_PAGE_WITHOUT_PARAMETERS); + } + @Step("Должны видеть на странице «{0}»") - public void shouldSee(WebElement element) { - assertThat("Должны видеть элемент", element, isDisplayed()); + public void shouldSee(HtmlElement element) { + assertThat("Должны видеть элемент", element, allOf(exists(),isDisplayed())); + } + + @Step("Должны видеть в заголовке виджета текст: {0}") + public void shouldSeeCityInWidgetsTitle(String city){ + assertThat("Должны видеть текст", + onMainPage().getWeatherWidgets().get(0).getWidgetTitle().getCityName(), hasText(city)); + } + + @Step("Число виджетов на странице должно быть равно: {0}") + public void shouldHaveWidgetNumberOnMainPage(int numberOfWidgets){ + assertThat("Должны видеть виджетов", onMainPage().getWeatherWidgets(), hasSize(numberOfWidgets)); + } + + @Step("Изменяем город в заголовке") + public void changeWidgetTitle(WeatherWidget weatherWidget, String newCity){ + clickOn(weatherWidget.getWidgetTitle().getCityName()); + enterText(newCity, weatherWidget.getWidgetTitle().getCityName()); + weatherWidget.getWidgetTitle().getCityName().sendKeys(Keys.RETURN); + } + + @Step("Ввод текста в заголовок и ожидание появления списка автозаполнения") + public void suggestList(String part, WidgetTitle widgetTitle){ + clickOn(widgetTitle.getCityName()); + enterText(part, widgetTitle.getCityName()); + webDriverWait.until(ExpectedConditions.elementToBeClickable(widgetTitle.getSuggestedCitiesList())); + } + + @Step("В списке должны быть только города, содержащие {0}") + public void shouldOnlySeeCitiesContaining(String part){ + assertThat("", new ArrayList<>(onMainPage().getWeatherWidgets().get(0).getWidgetTitle().getSuggestedCities()), + everyItem(text(containsString(part)))); + } + + @Step("Данные в {0} отображаются в формате {1}") + public void shouldMatchPattern(HtmlElement element, DataPatterns pattern){ + assertThat("Данные отображаются в неверном формате", element.getText(), containsPattern(pattern.toString())); + } + + @Step("Должны видеть в заголовке страницы {0}") + public void shouldSeeTitle(String text){ + assertThat("Неверный заголовок страницы", driver.getTitle(), is(text)); + } + + @Step("Выбираем город из списка автозаполнения") + public void selectItemFromSuggestedList(String city){ + clickOn(findElementByName(city, onMainPage().getWeatherWidgets().get(0).getWidgetTitle().getSuggestedCities())); + webDriverWait.until(ExpectedConditions.not(ExpectedConditions.stalenessOf(onMainPage().getWeatherWidgets().get(0)))); } private MainPage onMainPage() { return new MainPage(driver); } + + private void enterText(String text, HtmlElement field){ + field.clear(); + field.sendKeys(text); + } + + public void clickOn(HtmlElement element){ + element.click(); + } + + public void clickOnSeveralTimes(HtmlElement element, int times){ + while(times-- > 0) + element.click(); + } + + private HtmlElement findElementByName(String item, List list){ + for(HtmlElement elem : list) + if(elem.getText().equals(item)) { + return elem; + } + return null; + } }