├── .gitignore ├── README.md ├── build.gradle └── src └── test ├── java └── cloud │ └── autotests │ ├── config │ ├── Project.java │ ├── ProjectConfig.java │ └── demowebshop │ │ ├── App.java │ │ └── AppConfig.java │ ├── docs │ ├── CssXpathExamples.java │ ├── JUnit5Annotations.java │ └── SelenideSnippets.java │ ├── helpers │ ├── AllureAttachments.java │ ├── AllureRestAssuredFilter.java │ ├── DriverSettings.java │ └── DriverUtils.java │ └── tests │ ├── TestBase.java │ └── demowebshop │ └── LoginTests.java └── resources ├── config ├── demowebshop │ └── app.properties └── local.properties └── tpl ├── request.ftl └── response.ftl /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled class file 2 | *.class 3 | 4 | # Log file 5 | *.log 6 | 7 | # BlueJ files 8 | *.ctxt 9 | 10 | # Mobile Tools for Java (J2ME) 11 | .mtj.tmp/ 12 | 13 | # Package Files # 14 | *.jar 15 | *.war 16 | *.nar 17 | *.ear 18 | *.zip 19 | *.tar.gz 20 | *.rar 21 | *.iml 22 | 23 | # Project gen files 24 | .idea 25 | .allure/ 26 | .gradle/ 27 | allure-results/ 28 | gradle/ 29 | build/ 30 | bin/ 31 | out/ 32 | logs/ 33 | target/ 34 | .classpath 35 | .project 36 | .settings/org.eclipse.buildship.core.prefs 37 | .vscode 38 | /gradlew 39 | /gradlew.bat 40 | .todo 41 | *.properties 42 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Project in Allure TestOps with manual & automated tests 2 | allure.autotests.cloud/project/%s (ask admin@qa.guru for access) 3 | 4 | # Jenkins job 5 | jenkins.autotests.cloud/job/%s 6 | 7 | 8 | # USAGE examples 9 | 10 | ### For run remote tests need fill remote.properties or to pass value: 11 | 12 | * browser (default chrome) 13 | * browserVersion (default 89.0) 14 | * browserSize (default 1920x1080) 15 | * browserMobileView (mobile device name, for example iPhone X) 16 | * remoteDriverUrl (url address from selenoid or grid) 17 | * videoStorage (url address where you should get video) 18 | * threads (number of threads) 19 | 20 | 21 | Run tests with filled remote.properties: 22 | ```bash 23 | gradle clean test 24 | ``` 25 | 26 | Run tests with not filled remote.properties: 27 | ```bash 28 | gradle clean -DremoteDriverUrl=https://%s:%s@selenoid.autotests.cloud/wd/hub/ -DvideoStorage=https://selenoid.autotests.cloud/video/ -Dthreads=1 test 29 | ``` 30 | 31 | Serve report: 32 | ```bash 33 | allure serve build/allure-results 34 | ``` 35 | 36 | 37 | ###### For further development there are some example tests in src/test/java/cloud.autotests/tests/demowebshop 38 | * remove @Disabled("...") annotation to run tests 39 | ```bash 40 | gradle clean demowebshop 41 | ``` 42 | 43 | :heart: qa.guru
44 | :blue_heart: t.me/qa_automation 45 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'java-library' 3 | id 'io.qameta.allure' version '2.9.6' 4 | } 5 | 6 | repositories { 7 | mavenCentral() 8 | } 9 | 10 | def allureVersion = "2.17.3", 11 | selenideVersion = "6.5.0", 12 | junitVersion = "5.8.2" 13 | 14 | allure { 15 | report { 16 | version.set(allureVersion) 17 | } 18 | adapter { 19 | aspectjWeaver.set(true) 20 | frameworks { 21 | junit5 { 22 | adapterVersion.set(allureVersion) 23 | } 24 | } 25 | } 26 | } 27 | 28 | dependencies { 29 | testImplementation( 30 | "org.aspectj:aspectjweaver:1.9.6", 31 | "com.codeborne:selenide:$selenideVersion", 32 | "io.qameta.allure:allure-selenide:$allureVersion", 33 | "io.rest-assured:rest-assured:4.3.1", 34 | "io.qameta.allure:allure-rest-assured:$allureVersion", 35 | "org.aeonbits.owner:owner:1.0.12", 36 | "org.assertj:assertj-core:3.19.0", 37 | "org.slf4j:slf4j-simple:1.7.29", 38 | "org.junit.jupiter:junit-jupiter:$junitVersion") 39 | } 40 | 41 | tasks.withType(JavaCompile) { 42 | options.encoding = 'UTF-8' 43 | sourceCompatibility = JavaVersion.VERSION_1_8 44 | targetCompatibility = JavaVersion.VERSION_1_8 45 | } 46 | 47 | tasks.withType(Test) { 48 | systemProperties(System.getProperties()) 49 | useJUnitPlatform() 50 | 51 | if (System.getProperty("threads") != null) { 52 | systemProperties += [ 53 | 'junit.jupiter.execution.parallel.enabled' : true, 54 | 'junit.jupiter.execution.parallel.mode.default' : 'concurrent', 55 | 'junit.jupiter.execution.parallel.mode.classes.default' : 'concurrent', 56 | 'junit.jupiter.execution.parallel.config.strategy' : 'fixed', 57 | 'junit.jupiter.execution.parallel.config.fixed.parallelism': System.getProperty("threads").toInteger() 58 | ] 59 | } 60 | 61 | testLogging { 62 | lifecycle { 63 | // events "started", "failed" 64 | events "started", "skipped", "failed", "standard_error", "standard_out" 65 | exceptionFormat "short" 66 | } 67 | } 68 | } 69 | 70 | task demowebshop(type: Test) { 71 | useJUnitPlatform { 72 | includeTags 'demowebshop' 73 | } 74 | } -------------------------------------------------------------------------------- /src/test/java/cloud/autotests/config/Project.java: -------------------------------------------------------------------------------- 1 | package cloud.autotests.config; 2 | 3 | import org.aeonbits.owner.ConfigFactory; 4 | 5 | public class Project { 6 | public static ProjectConfig config = ConfigFactory.create(ProjectConfig.class, System.getProperties()); 7 | 8 | public static boolean isWebMobile() { 9 | return !config.browserMobileView().equals(""); 10 | } 11 | 12 | public static boolean isRemoteWebDriver() { 13 | return !config.remoteDriverUrl().equals(""); 14 | } 15 | 16 | public static boolean isVideoOn() { 17 | return !config.videoStorage().equals(""); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/test/java/cloud/autotests/config/ProjectConfig.java: -------------------------------------------------------------------------------- 1 | package cloud.autotests.config; 2 | 3 | import org.aeonbits.owner.Config; 4 | 5 | @Config.LoadPolicy(Config.LoadType.MERGE) 6 | @Config.Sources({ 7 | "system:properties", 8 | "classpath:config/local.properties", 9 | "classpath:config/remote.properties" 10 | }) 11 | public interface ProjectConfig extends Config { 12 | 13 | @DefaultValue("chrome") 14 | String browser(); 15 | @DefaultValue("91.0") 16 | String browserVersion(); 17 | @DefaultValue("1920x1080") 18 | String browserSize(); 19 | String browserMobileView(); 20 | String remoteDriverUrl(); 21 | String videoStorage(); 22 | } 23 | -------------------------------------------------------------------------------- /src/test/java/cloud/autotests/config/demowebshop/App.java: -------------------------------------------------------------------------------- 1 | package cloud.autotests.config.demowebshop; 2 | 3 | import org.aeonbits.owner.ConfigFactory; 4 | 5 | public class App { 6 | public static AppConfig config = ConfigFactory.create(AppConfig.class, System.getProperties()); 7 | } 8 | -------------------------------------------------------------------------------- /src/test/java/cloud/autotests/config/demowebshop/AppConfig.java: -------------------------------------------------------------------------------- 1 | package cloud.autotests.config.demowebshop; 2 | 3 | import org.aeonbits.owner.Config; 4 | 5 | @Config.LoadPolicy(Config.LoadType.MERGE) 6 | @Config.Sources({ 7 | "system:properties", 8 | "classpath:config/demowebshop/app.properties" 9 | }) 10 | public interface AppConfig extends Config { 11 | 12 | String webUrl(); 13 | String apiUrl(); 14 | String userLogin(); 15 | String userPassword(); 16 | 17 | } 18 | -------------------------------------------------------------------------------- /src/test/java/cloud/autotests/docs/CssXpathExamples.java: -------------------------------------------------------------------------------- 1 | package cloud.autotests.docs; 2 | 3 | import org.openqa.selenium.By; 4 | 5 | import static com.codeborne.selenide.Selectors.*; 6 | import static com.codeborne.selenide.Selenide.$; 7 | import static com.codeborne.selenide.Selenide.$x; 8 | 9 | /* 10 | Locators examples 11 | Author: https://github.com/svasenkov 12 | */ 13 | 14 | public class CssXpathExamples { 15 | 16 | void cssXpathExamples() { 17 | 18 | // 19 | 20 | $("[data-testid=royal_email]"); 21 | $(by("data-testid", "royal_email")); 22 | 23 | 24 | // 25 | 26 | $("#email"); 27 | $(byId("email")); 28 | $(By.id("email")); 29 | $("[id=email]"); 30 | $("[id='email']"); 31 | $("[id=\"email\"]"); 32 | $("input[id=email]"); 33 | $("input#email"); 34 | $(by("id", "email")); 35 | $x("//*[@id='email']"); 36 | $x("//input[@id='email']"); 37 | $(byXpath("//input[@id='email']")); 38 | 39 | 40 | // 41 | 42 | $("[name='email']"); 43 | $("input[name='email']"); 44 | $(by("name", "email")); 45 | $(byName("email")); 46 | 47 | 48 | // 49 | 50 | $(byClassName("login_form_input_box")); 51 | $(".login_form_input_box"); 52 | $(".inputtext.login_form_input_box"); 53 | $("input.inputtext.login_form_input_box"); 54 | $x("//*[@class='login_form_input_box']"); 55 | 56 | //
57 | // 58 | //
59 | $("input.inputtext .login_form_input_box"); 60 | 61 | //
Hello qa.guru
62 | $(byText("Hello qa.guru")); 63 | $(withText("lo qa.guru")); 64 | } 65 | } -------------------------------------------------------------------------------- /src/test/java/cloud/autotests/docs/JUnit5Annotations.java: -------------------------------------------------------------------------------- 1 | package cloud.autotests.docs; 2 | 3 | import org.junit.jupiter.api.*; 4 | 5 | /* 6 | JUnit5 annotations examples 7 | Author: https://github.com/dtuchs 8 | */ 9 | 10 | @Tag("junit5") 11 | public class JUnit5Annotations { 12 | 13 | @BeforeAll 14 | static void beforeAll() { 15 | System.out.println("this is before all methods!"); 16 | } 17 | 18 | @BeforeEach 19 | void setUp() { 20 | System.out.println("this is before each method!"); 21 | } 22 | 23 | @AfterEach 24 | void tearDown() { 25 | System.out.println("this is after each method!"); 26 | } 27 | 28 | @AfterAll 29 | static void tearDownDown() { 30 | System.out.println("this is after all methods!"); 31 | } 32 | 33 | // @Test 34 | void firstTest() { 35 | System.out.println("this is the first @test!"); 36 | Assertions.assertTrue(true); 37 | } 38 | 39 | // @Test 40 | void secondTest() { 41 | System.out.println("this is the second @test!"); 42 | Assertions.assertTrue(true); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/test/java/cloud/autotests/docs/SelenideSnippets.java: -------------------------------------------------------------------------------- 1 | package cloud.autotests.docs; 2 | 3 | import com.codeborne.selenide.*; 4 | import org.openqa.selenium.Keys; 5 | 6 | import java.io.File; 7 | import java.io.FileNotFoundException; 8 | import java.time.Duration; 9 | 10 | import static com.codeborne.selenide.CollectionCondition.*; 11 | import static com.codeborne.selenide.Condition.empty; 12 | import static com.codeborne.selenide.Condition.*; 13 | import static com.codeborne.selenide.Selectors.*; 14 | import static com.codeborne.selenide.Selenide.*; 15 | 16 | /* 17 | Selenide commands examples 18 | Author: https://github.com/vinogradoff 19 | this is not a full list, just the most common 20 | */ 21 | 22 | public class SelenideSnippets { 23 | 24 | void browser_command_examples() { 25 | 26 | open("https://google.com"); 27 | open("/customer/orders"); // -Dselenide.baseUrl=http://google.com 28 | open("/", AuthenticationType.BASIC, 29 | new BasicAuthCredentials("user", "password")); 30 | 31 | Selenide.back(); 32 | Selenide.refresh(); 33 | 34 | Selenide.clearBrowserCookies(); 35 | Selenide.clearBrowserLocalStorage(); 36 | executeJavaScript("sessionStorage.clear();"); // no Selenide command for this yet 37 | 38 | Selenide.confirm(); // OK in alert dialogs 39 | Selenide.dismiss(); // Cancel in alert dialogs 40 | 41 | Selenide.closeWindow(); // close active tab 42 | Selenide.closeWebDriver(); // close browser completely 43 | 44 | Selenide.switchTo().frame("new"); 45 | Selenide.switchTo().defaultContent(); 46 | 47 | Selenide.switchTo().window("The Internet"); 48 | } 49 | 50 | void selectors_examples() { 51 | $("div").click(); 52 | element("div").click(); 53 | 54 | $("div", 2).click(); // the third div 55 | 56 | $x("//h1/div").click(); 57 | $(byXpath("//h1/div")).click(); 58 | 59 | $(byText("full text")).click(); 60 | $(withText("ull tex")).click(); 61 | 62 | $(byTagAndText("div", "full text")); 63 | $(withTagAndText("div", "ull text")); 64 | 65 | $("").parent(); // find parent element 66 | $("").sibling(2); // find down third sibling element 67 | $("").preceding(0); // find up first sibling element 68 | $("").closest("div"); // find up the tree the next element with tag 69 | $("").ancestor("div"); // the same as closest 70 | $("div:last-child"); 71 | 72 | $("div").$("h1").find(byText("abc")).click(); 73 | 74 | // very optional 75 | $(byAttribute("abc", "x")).click(); 76 | $("[abc=x]").click(); 77 | 78 | $(byId("mytext")).click(); 79 | $("#mytext").click(); 80 | 81 | $(byClassName("red")).click(); 82 | $(".red").click(); 83 | } 84 | 85 | void actions_examples() { 86 | $("").click(); 87 | $("").doubleClick(); 88 | $("").contextClick(); 89 | 90 | $("").hover(); 91 | 92 | $("").setValue("text"); 93 | $("").append("text"); 94 | $("").clear(); 95 | $("").setValue(""); // clear 96 | 97 | $("div").sendKeys("c"); // hotkey c on element 98 | actions().sendKeys("c").perform(); //hotkey c on whole application 99 | actions().sendKeys(Keys.chord(Keys.CONTROL, "f")).perform(); // Ctrl + F 100 | $("html").sendKeys(Keys.chord(Keys.CONTROL, "f")); 101 | 102 | $("").pressEnter(); 103 | $("").pressEscape(); 104 | $("").pressTab(); 105 | 106 | // complex actions with keybord and mouse, example 107 | actions().moveToElement($("div")).clickAndHold().moveByOffset(300, 200).release().perform(); 108 | 109 | // old html actions don't work with many modern frameworks 110 | $("").selectOption("dropdown_option"); 111 | $("").selectRadio("radio_options"); 112 | 113 | } 114 | 115 | void assertions_examples() { 116 | $("").shouldBe(visible); 117 | $("").shouldNotBe(visible); 118 | $("").shouldHave(text("abc")); 119 | $("").shouldNotHave(text("abc")); 120 | $("").should(appear); 121 | $("").shouldNot(appear); 122 | 123 | 124 | //longer timeouts 125 | $("").shouldBe(visible, Duration.ofSeconds(30)); 126 | // $("").waitUntil(visible, 30000); //is deprecated 127 | 128 | } 129 | 130 | void conditions_examples() { 131 | $("").shouldBe(visible); 132 | $("").shouldBe(hidden); 133 | 134 | $("").shouldHave(text("abc")); 135 | $("").shouldHave(exactText("abc")); 136 | $("").shouldHave(textCaseSensitive("abc")); 137 | $("").shouldHave(exactTextCaseSensitive("abc")); 138 | $("").should(matchText("[0-9]abc$")); 139 | 140 | $("").shouldHave(cssClass("red")); 141 | $("").shouldHave(cssValue("font-size", "12")); 142 | 143 | $("").shouldHave(value("25")); 144 | $("").shouldHave(exactValue("25")); 145 | $("").shouldBe(empty); 146 | 147 | $("").shouldHave(attribute("disabled")); 148 | $("").shouldHave(attribute("name", "example")); 149 | $("").shouldHave(attributeMatching("name", "[0-9]abc$")); 150 | 151 | $("").shouldBe(checked); // for checkboxes 152 | 153 | // Warning! Only checks if it is in DOM, not if it is visible! You don't need it in most tests! 154 | $("").should(exist); 155 | 156 | // Warning! Checks only the "disabled" attribute! Will not work with many modern frameworks 157 | $("").shouldBe(disabled); 158 | $("").shouldBe(enabled); 159 | } 160 | 161 | void collections_examples() { 162 | 163 | $$("div"); // does nothing! 164 | 165 | // selections 166 | $$("div").filterBy(text("123")).shouldHave(size(1)); 167 | $$("div").excludeWith(text("123")).shouldHave(size(1)); 168 | 169 | $$("div").first().click(); 170 | elements("div").first().click(); 171 | // $("div").click(); 172 | $$("div").last().click(); 173 | $$("div").get(1).click(); // the second! (start with 0) 174 | $("div", 1).click(); // same as previous 175 | $$("div").findBy(text("123")).click(); // finds first 176 | 177 | // assertions 178 | $$("").shouldHave(size(0)); 179 | $$("").shouldBe(CollectionCondition.empty); // the same 180 | 181 | $$("").shouldHave(texts("Alfa", "Beta", "Gamma")); 182 | $$("").shouldHave(exactTexts("Alfa", "Beta", "Gamma")); 183 | 184 | $$("").shouldHave(textsInAnyOrder("Beta", "Gamma", "Alfa")); 185 | $$("").shouldHave(exactTextsCaseSensitiveInAnyOrder("Beta", "Gamma", "Alfa")); 186 | 187 | $$("").shouldHave(itemWithText("Gamma")); // only one text 188 | 189 | $$("").shouldHave(sizeGreaterThan(0)); 190 | $$("").shouldHave(sizeGreaterThanOrEqual(1)); 191 | $$("").shouldHave(sizeLessThan(3)); 192 | $$("").shouldHave(sizeLessThanOrEqual(2)); 193 | 194 | } 195 | 196 | void file_operation_examples() throws FileNotFoundException { 197 | 198 | File file1 = $("a.fileLink").download(); // only for links 199 | File file2 = $("div").download(DownloadOptions.using(FileDownloadMode.FOLDER)); // more common options, but may have problems with Grid/Selenoid 200 | 201 | File file = new File("src/test/resources/readme.txt"); 202 | $("#file-upload").uploadFile(file); 203 | $("#file-upload").uploadFromClasspath("readme.txt"); 204 | // don't forget to submit! 205 | $("uploadButton").click(); 206 | } 207 | 208 | void javascript_examples() { 209 | executeJavaScript("alert('selenide')"); 210 | executeJavaScript("alert(arguments[0]+arguments[1])", "abc", 12); 211 | long fortyTwo = executeJavaScript("return arguments[0]*arguments[1];", 6, 7); 212 | } 213 | } -------------------------------------------------------------------------------- /src/test/java/cloud/autotests/helpers/AllureAttachments.java: -------------------------------------------------------------------------------- 1 | package cloud.autotests.helpers; 2 | 3 | import io.qameta.allure.Allure; 4 | import io.qameta.allure.Attachment; 5 | import org.slf4j.Logger; 6 | import org.slf4j.LoggerFactory; 7 | 8 | import java.io.FileNotFoundException; 9 | import java.io.IOException; 10 | import java.io.InputStream; 11 | import java.net.URL; 12 | 13 | import static com.codeborne.selenide.Selenide.sleep; 14 | 15 | public class AllureAttachments { 16 | public static final Logger LOGGER = LoggerFactory.getLogger(AllureAttachments.class); 17 | 18 | @Attachment(value = "{attachName}", type = "text/plain") 19 | private static String addMessage(String attachName, String text) { 20 | return text; 21 | } 22 | 23 | public static void addBrowserConsoleLogs() { 24 | addMessage("Browser console logs", DriverUtils.getConsoleLogs()); 25 | } 26 | 27 | @Attachment(value = "{attachName}", type = "image/png") 28 | public static byte[] addScreenshotAs(String attachName) { 29 | return DriverUtils.getScreenshotAsBytes(); 30 | } 31 | 32 | @Attachment(value = "Page source", type = "text/html") 33 | public static byte[] addPageSource() { 34 | return DriverUtils.getPageSourceAsBytes(); 35 | } 36 | 37 | public static void addVideo(String sessionId) { 38 | URL videoUrl = DriverUtils.getVideoUrl(sessionId); 39 | if (videoUrl != null) { 40 | InputStream videoInputStream = null; 41 | sleep(1000); 42 | 43 | for (int i = 0; i < 20; i++) { 44 | try { 45 | videoInputStream = videoUrl.openStream(); 46 | break; 47 | } catch (FileNotFoundException e) { 48 | sleep(1000); 49 | } catch (IOException e) { 50 | LOGGER.warn("[ALLURE VIDEO ATTACHMENT ERROR] Cant attach allure video, {}", videoUrl); 51 | e.printStackTrace(); 52 | } 53 | } 54 | if (videoInputStream != null) { 55 | Allure.addAttachment("Video", "video/mp4", videoInputStream, "mp4"); 56 | } 57 | } 58 | } 59 | 60 | 61 | } 62 | -------------------------------------------------------------------------------- /src/test/java/cloud/autotests/helpers/AllureRestAssuredFilter.java: -------------------------------------------------------------------------------- 1 | package cloud.autotests.helpers; 2 | 3 | import io.qameta.allure.restassured.AllureRestAssured; 4 | 5 | public class AllureRestAssuredFilter { 6 | private static final AllureRestAssured FILTER = new AllureRestAssured(); 7 | 8 | public static AllureRestAssured withCustomTemplates() { 9 | FILTER.setRequestTemplate("request.ftl"); 10 | FILTER.setResponseTemplate("response.ftl"); 11 | return FILTER; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/test/java/cloud/autotests/helpers/DriverSettings.java: -------------------------------------------------------------------------------- 1 | package cloud.autotests.helpers; 2 | 3 | import cloud.autotests.config.Project; 4 | import com.codeborne.selenide.Configuration; 5 | import org.openqa.selenium.chrome.ChromeOptions; 6 | import org.openqa.selenium.remote.DesiredCapabilities; 7 | 8 | import java.util.HashMap; 9 | import java.util.Map; 10 | 11 | public class DriverSettings { 12 | 13 | public static void configure() { 14 | Configuration.browser = Project.config.browser(); 15 | Configuration.browserVersion = Project.config.browserVersion(); 16 | Configuration.browserSize = Project.config.browserSize(); 17 | // Configuration.baseUrl = App.config.webUrl(); 18 | 19 | DesiredCapabilities capabilities = new DesiredCapabilities(); 20 | ChromeOptions chromeOptions = new ChromeOptions(); 21 | 22 | chromeOptions.addArguments("--no-sandbox"); 23 | chromeOptions.addArguments("--disable-infobars"); 24 | chromeOptions.addArguments("--disable-popup-blocking"); 25 | chromeOptions.addArguments("--disable-notifications"); 26 | chromeOptions.addArguments("--lang=en-en"); 27 | 28 | if (Project.isWebMobile()) { // for chrome only 29 | Map mobileDevice = new HashMap<>(); 30 | mobileDevice.put("deviceName", Project.config.browserMobileView()); 31 | chromeOptions.setExperimentalOption("mobileEmulation", mobileDevice); 32 | } 33 | 34 | if (Project.isRemoteWebDriver()) { 35 | capabilities.setCapability("enableVNC", true); 36 | capabilities.setCapability("enableVideo", true); 37 | Configuration.remote = Project.config.remoteDriverUrl(); 38 | } 39 | 40 | capabilities.setCapability(ChromeOptions.CAPABILITY, chromeOptions); 41 | Configuration.browserCapabilities = capabilities; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/test/java/cloud/autotests/helpers/DriverUtils.java: -------------------------------------------------------------------------------- 1 | package cloud.autotests.helpers; 2 | 3 | import cloud.autotests.config.Project; 4 | import com.codeborne.selenide.Selenide; 5 | import org.openqa.selenium.OutputType; 6 | import org.openqa.selenium.TakesScreenshot; 7 | import org.openqa.selenium.remote.RemoteWebDriver; 8 | import org.slf4j.Logger; 9 | import org.slf4j.LoggerFactory; 10 | 11 | import java.net.MalformedURLException; 12 | import java.net.URL; 13 | import java.nio.charset.StandardCharsets; 14 | 15 | import static com.codeborne.selenide.WebDriverRunner.getWebDriver; 16 | import static org.openqa.selenium.logging.LogType.BROWSER; 17 | 18 | public class DriverUtils { 19 | public static final Logger LOGGER = LoggerFactory.getLogger(DriverUtils.class); 20 | 21 | 22 | public static String getSessionId() { 23 | return ((RemoteWebDriver) getWebDriver()).getSessionId().toString(); 24 | } 25 | 26 | public static byte[] getScreenshotAsBytes() { 27 | return ((TakesScreenshot) getWebDriver()).getScreenshotAs(OutputType.BYTES); 28 | } 29 | 30 | public static byte[] getPageSourceAsBytes() { 31 | return getWebDriver().getPageSource().getBytes(StandardCharsets.UTF_8); 32 | } 33 | 34 | public static URL getVideoUrl(String sessionId) { 35 | String videoUrl = Project.config.videoStorage() + sessionId + ".mp4"; 36 | 37 | try { 38 | return new URL(videoUrl); 39 | } catch (MalformedURLException e) { 40 | LOGGER.warn("[ALLURE VIDEO ATTACHMENT ERROR] Wrong test video url, {}", videoUrl); 41 | e.printStackTrace(); 42 | } 43 | return null; 44 | } 45 | 46 | public static String getConsoleLogs() { // todo refactor 47 | return String.join("\n", Selenide.getWebDriverLogs(BROWSER)); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/test/java/cloud/autotests/tests/TestBase.java: -------------------------------------------------------------------------------- 1 | package cloud.autotests.tests; 2 | 3 | import cloud.autotests.config.Project; 4 | import cloud.autotests.helpers.AllureAttachments; 5 | import cloud.autotests.helpers.DriverSettings; 6 | import cloud.autotests.helpers.DriverUtils; 7 | import com.codeborne.selenide.Selenide; 8 | import com.codeborne.selenide.logevents.SelenideLogger; 9 | import io.qameta.allure.junit5.AllureJunit5; 10 | import io.qameta.allure.selenide.AllureSelenide; 11 | import org.junit.jupiter.api.AfterEach; 12 | import org.junit.jupiter.api.BeforeAll; 13 | import org.junit.jupiter.api.BeforeEach; 14 | import org.junit.jupiter.api.extension.ExtendWith; 15 | 16 | 17 | @ExtendWith({AllureJunit5.class}) 18 | public class TestBase { 19 | @BeforeAll 20 | static void beforeAll() { 21 | DriverSettings.configure(); 22 | } 23 | 24 | @BeforeEach 25 | public void beforeEach() { 26 | SelenideLogger.addListener("AllureSelenide", new AllureSelenide()); 27 | } 28 | 29 | @AfterEach 30 | public void afterEach() { 31 | String sessionId = DriverUtils.getSessionId(); 32 | 33 | AllureAttachments.addScreenshotAs("Last screenshot"); 34 | AllureAttachments.addPageSource(); 35 | // AllureAttachments.attachNetwork(); // todo 36 | AllureAttachments.addBrowserConsoleLogs(); 37 | 38 | Selenide.closeWebDriver(); 39 | 40 | if (Project.isVideoOn()) { 41 | AllureAttachments.addVideo(sessionId); 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/test/java/cloud/autotests/tests/demowebshop/LoginTests.java: -------------------------------------------------------------------------------- 1 | package cloud.autotests.tests.demowebshop; 2 | 3 | import cloud.autotests.config.demowebshop.App; 4 | import cloud.autotests.helpers.AllureRestAssuredFilter; 5 | import cloud.autotests.tests.TestBase; 6 | import com.codeborne.selenide.Configuration; 7 | import io.qameta.allure.Story; 8 | import io.restassured.RestAssured; 9 | import org.junit.jupiter.api.*; 10 | import org.openqa.selenium.Cookie; 11 | 12 | import static com.codeborne.selenide.Condition.text; 13 | import static com.codeborne.selenide.Selenide.$; 14 | import static com.codeborne.selenide.Selenide.open; 15 | import static com.codeborne.selenide.WebDriverRunner.getWebDriver; 16 | import static io.qameta.allure.Allure.step; 17 | import static io.restassured.RestAssured.given; 18 | 19 | @Story("Login tests") 20 | public class LoginTests extends TestBase { 21 | 22 | static String login, 23 | password; 24 | 25 | @BeforeAll 26 | static void configureBaseUrl() { 27 | RestAssured.baseURI = App.config.apiUrl(); 28 | Configuration.baseUrl = App.config.webUrl(); 29 | 30 | login = App.config.userLogin(); 31 | password = App.config.userPassword(); 32 | } 33 | 34 | @Test 35 | @Tag("demowebshop") 36 | @Disabled("Example test code for further test development") 37 | @DisplayName("Successful authorization to some demowebshop (UI)") 38 | void loginTest() { 39 | step("Open login page", () -> 40 | open("/login")); 41 | 42 | step("Fill login form", () -> { 43 | $("#Email").setValue(login); 44 | $("#Password").setValue(password) 45 | .pressEnter(); 46 | }); 47 | 48 | step("Verify successful authorization", () -> 49 | $(".account").shouldHave(text(login))); 50 | } 51 | 52 | @Test 53 | @Tag("demowebshop") 54 | @Disabled("Example test code for further test development") 55 | @DisplayName("Successful authorization to some demowebshop (API + UI)") 56 | void loginWithCookieTest() { 57 | step("Get cookie by api and set it to browser", () -> { 58 | String authorizationCookie = 59 | given() 60 | .filter(AllureRestAssuredFilter.withCustomTemplates()) 61 | .contentType("application/x-www-form-urlencoded; charset=UTF-8") 62 | .formParam("Email", login) 63 | .formParam("Password", password) 64 | .when() 65 | .post("/login") 66 | .then() 67 | .statusCode(302) 68 | .extract() 69 | .cookie("NOPCOMMERCE.AUTH"); 70 | 71 | step("Open minimal content, because cookie can be set when site is opened", () -> 72 | open("/Themes/DefaultClean/Content/images/logo.png")); 73 | 74 | step("Set cookie to to browser", () -> 75 | getWebDriver().manage().addCookie( 76 | new Cookie("NOPCOMMERCE.AUTH", authorizationCookie))); 77 | }); 78 | 79 | step("Open main page", () -> 80 | open("")); 81 | 82 | step("Verify successful authorization", () -> 83 | $(".account").shouldHave(text(login))); 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/test/resources/config/demowebshop/app.properties: -------------------------------------------------------------------------------- 1 | # usually, we do not store user credentials or other sensitive data in git, 2 | # that's only for an example project 3 | webUrl=http://demowebshop.tricentis.com 4 | apiUrl=http://demowebshop.tricentis.com 5 | userLogin=qaguru@qa.guru 6 | userPassword=qaguru@qa.guru1 -------------------------------------------------------------------------------- /src/test/resources/config/local.properties: -------------------------------------------------------------------------------- 1 | browser=chrome 2 | browserVersion=91.0 3 | browserSize=1920x1080 4 | browserMobileView= 5 | #browserMobileView=iPhone X 6 | remoteDriverUrl= 7 | videoStorage= -------------------------------------------------------------------------------- /src/test/resources/tpl/request.ftl: -------------------------------------------------------------------------------- 1 | 2 | <#-- @ftlvariable name="data" type="io.qameta.allure.attachment.http.HttpRequestAttachment" --> 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 22 | 23 | 24 |
25 |
<#if data.method??>${data.method}<#else>GET: <#if data.url??>${data.url}<#else>Unknown
26 |
27 | 28 | <#if data.body??> 29 |

Body

30 |
31 |
${data.body}
32 |
33 | 34 | 35 | <#if (data.headers)?has_content> 36 |

Headers

37 |
38 | <#list data.headers as name, value> 39 |
40 |
${name}: ${value}
41 |
42 | 43 |
44 | 45 | 46 | 47 | <#if (data.cookies)?has_content> 48 |

Cookies

49 |
50 | <#list data.cookies as name, value> 51 |
52 |
${name}: ${value}
53 |
54 | 55 |
56 | 57 | 58 | <#if data.curl??> 59 |

Curl

60 |
61 |
${data.curl}
62 |
63 | 64 | 65 | -------------------------------------------------------------------------------- /src/test/resources/tpl/response.ftl: -------------------------------------------------------------------------------- 1 | 2 | <#-- @ftlvariable name="data" type="io.qameta.allure.attachment.http.HttpResponseAttachment" --> 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 22 | 23 | 24 |

Status code

<#if data.responseCode??> 25 |
${data.responseCode}
26 | <#else>Unknown
27 | <#if data.url??> 28 | 29 |
30 |
${data.url}
31 |
32 | 33 | <#if (data.headers)?has_content> 34 |

Headers

35 |
36 | <#list data.headers as name, value> 37 |
38 |
${name}: ${value}
39 |
40 | 41 |
42 | 43 | 44 | <#if data.body??> 45 |

Body

46 |
47 |
${data.body}
48 |
49 | 50 | 51 | <#if (data.cookies)?has_content> 52 |

Cookies

53 |
54 | <#list data.cookies as name, value> 55 |
56 |
${name}: ${value}
57 |
58 | 59 |
60 | 61 | 62 | --------------------------------------------------------------------------------