diff --git a/README.md b/README.md index 02c49ae82..9d338e6ad 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Neodymium 2.0.0 +# Neodymium 2.1.0 Neodymium tries to solve your typical and most pressing UI test automation problems by combining JUnit, WebDriver, BDD/Cucumber, and proper reporting. It gives you ready to use templates, assembles well-known open source projects, and enhances this all with additional functionality that is often missing. Neodymium is basically the combination of state of the art open source test libraries with additionally glue to make it stick reliably together. diff --git a/config/browser.properties b/config/browser.properties index de4040662..2c3658132 100644 --- a/config/browser.properties +++ b/config/browser.properties @@ -154,6 +154,7 @@ browserprofile.Galaxy_Note3_Emulation.browser = chrome browserprofile.Galaxy_Note3_Emulation.chromeEmulationProfile = Samsung Galaxy Note 3 browserprofile.iphone5.name = iphone 5 on saucelabs +browserprofile.iphone5.browser = iphone browserprofile.iphone5.platform = OS X 10.10 browserprofile.iphone5.version = 8.4 browserprofile.iphone5.deviceName = iPhone 5 diff --git a/pom.xml b/pom.xml index ad3f196e1..87647172e 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 com.xceptance neodymium-library - 2.0.0 + 2.1.0 neodymium-library https://github.com/Xceptance/neodymium-library diff --git a/src/main/java/com/xceptance/neodymium/NeodymiumCucumberRunListener.java b/src/main/java/com/xceptance/neodymium/NeodymiumCucumberRunListener.java index 05c6e918a..af30ea9dc 100644 --- a/src/main/java/com/xceptance/neodymium/NeodymiumCucumberRunListener.java +++ b/src/main/java/com/xceptance/neodymium/NeodymiumCucumberRunListener.java @@ -17,13 +17,15 @@ public class NeodymiumCucumberRunListener extends RunListener { + public static final String LISTENER_NAME = "allure-selenide-cucumber"; + private static final Logger LOGGER = LoggerFactory.getLogger(NeodymiumCucumberRunListener.class); private List failures = new LinkedList<>(); public NeodymiumCucumberRunListener() { - SelenideLogger.addListener("allure-selenide", new AllureSelenide()); + SelenideLogger.addListener(LISTENER_NAME, new AllureSelenide()); } @Override diff --git a/src/main/java/com/xceptance/neodymium/NeodymiumRunner.java b/src/main/java/com/xceptance/neodymium/NeodymiumRunner.java index 2afeae2fb..4bb74f4f6 100644 --- a/src/main/java/com/xceptance/neodymium/NeodymiumRunner.java +++ b/src/main/java/com/xceptance/neodymium/NeodymiumRunner.java @@ -17,12 +17,15 @@ import org.junit.runners.model.InitializationError; import org.junit.runners.model.Statement; +import com.codeborne.selenide.logevents.SelenideLogger; import com.xceptance.neodymium.module.EnhancedMethod; import com.xceptance.neodymium.module.StatementBuilder; import com.xceptance.neodymium.module.order.DefaultStatementRunOrder; import com.xceptance.neodymium.module.statement.browser.multibrowser.Browser; import com.xceptance.neodymium.util.Neodymium; +import io.qameta.allure.selenide.AllureSelenide; + /** * This class executes {@link JUnit4} test classes (aka JUnit Runner) and adds several features to test execution e.g. * multi {@link Browser browser} and @@ -66,9 +69,12 @@ */ public class NeodymiumRunner extends BlockJUnit4ClassRunner { + public static final String LISTENER_NAME = "allure-selenide-java"; + public NeodymiumRunner(Class klass) throws InitializationError { super(klass); + SelenideLogger.addListener(LISTENER_NAME, new AllureSelenide()); } public enum DescriptionMode diff --git a/src/main/java/com/xceptance/neodymium/module/statement/browser/multibrowser/configuration/BrowserConfigurationMapper.java b/src/main/java/com/xceptance/neodymium/module/statement/browser/multibrowser/configuration/BrowserConfigurationMapper.java index 4e296d2a9..c669543d5 100644 --- a/src/main/java/com/xceptance/neodymium/module/statement/browser/multibrowser/configuration/BrowserConfigurationMapper.java +++ b/src/main/java/com/xceptance/neodymium/module/statement/browser/multibrowser/configuration/BrowserConfigurationMapper.java @@ -102,7 +102,7 @@ else if ("opera".equals(emulatedBrowser)) } else { - capabilities = DesiredCapabilities.firefox(); + capabilities = new DesiredCapabilities(); } /* diff --git a/src/main/java/com/xceptance/neodymium/util/SelenideAddons.java b/src/main/java/com/xceptance/neodymium/util/SelenideAddons.java index 3b2753ea0..f20bc0abd 100644 --- a/src/main/java/com/xceptance/neodymium/util/SelenideAddons.java +++ b/src/main/java/com/xceptance/neodymium/util/SelenideAddons.java @@ -8,9 +8,13 @@ import org.openqa.selenium.By; import org.openqa.selenium.StaleElementReferenceException; +import org.openqa.selenium.WebElement; +import com.codeborne.selenide.Condition; import com.codeborne.selenide.ElementsCollection; import com.codeborne.selenide.SelenideElement; +import com.codeborne.selenide.ex.UIAssertionError; +import com.codeborne.selenide.impl.Html; import com.codeborne.selenide.impl.WebElementsCollectionWrapper; /** @@ -143,4 +147,75 @@ public ElementsCollection get() // never get here return null; } + + /** + * The missing regular expression condition for value attributes.
+ *
+ *

+ * Sample: $("input").waitWhile(matchesValue("foo"), 12000) + *

+ * + * @param text + * The text that should be contained within the value attribute + * @return a Selenide {@link Condition} + * @see #matchValue(String) + */ + public static Condition matchesValue(String text) + { + return matchValue(text); + } + + /** + * The missing regular expression condition for value attributes.
+ *
+ *

+ * Sample: Assert that given element's value attribute matches given regular expression + * $("input").should(matchValue("Hello\s*John")) + *

+ * + * @param regex + * e.g. Kicked.*Chuck Norris - in this case ".*" can contain any characters including spaces, tabs, CR + * etc. + * @return a Selenide {@link Condition} + */ + public static Condition matchValue(final String regex) + { + return new Condition("match value") + { + @Override + public boolean apply(WebElement element) + { + return Html.text.matches(element.getAttribute("value"), regex); + } + + @Override + public String toString() + { + return name + " '" + regex + '\''; + } + }; + } + + /** + * The missing wrapper to generate screenshots and save the html source code if a jUnit assertion fails.
+ *
+ *

+ * Sample: Assert that page title is correct and dump the page source and a screenshot in case of a mismatch + * wrapAssertionError(()->{Assert.assertEquals("MyPageTitle", Selenide.title());}); + *

+ * + * @param runnable + * The lambda containing an assertion + */ + public static void wrapAssertionError(final Runnable runnable) + { + try + { + runnable.run(); + } + catch (AssertionError e) + { + throw UIAssertionError.wrapThrowable(e, System.currentTimeMillis()); + } + } } diff --git a/src/test/java/com/xceptance/neodymium/testclasses/allure/AllureSelenideListenerIsActiveForJava.java b/src/test/java/com/xceptance/neodymium/testclasses/allure/AllureSelenideListenerIsActiveForJava.java new file mode 100644 index 000000000..ba68e58fd --- /dev/null +++ b/src/test/java/com/xceptance/neodymium/testclasses/allure/AllureSelenideListenerIsActiveForJava.java @@ -0,0 +1,20 @@ +package com.xceptance.neodymium.testclasses.allure; + +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; + +import com.codeborne.selenide.logevents.SelenideLogger; +import com.xceptance.neodymium.NeodymiumRunner; +import com.xceptance.neodymium.module.statement.browser.multibrowser.Browser; + +@RunWith(NeodymiumRunner.class) +@Browser("Chrome_headless") +public class AllureSelenideListenerIsActiveForJava +{ + @Test + public void testWaitingAnimationSelectorUnconfigured() + { + Assert.assertTrue(" AllureSelenide listener is not attached", SelenideLogger.hasListener(NeodymiumRunner.LISTENER_NAME)); + } +} diff --git a/src/test/java/com/xceptance/neodymium/testclasses/cucumber/CucumberSupport.java b/src/test/java/com/xceptance/neodymium/testclasses/cucumber/CucumberSupport.java index d5f7d6929..ba1b10562 100644 --- a/src/test/java/com/xceptance/neodymium/testclasses/cucumber/CucumberSupport.java +++ b/src/test/java/com/xceptance/neodymium/testclasses/cucumber/CucumberSupport.java @@ -2,6 +2,8 @@ import org.junit.Assert; +import com.codeborne.selenide.logevents.SelenideLogger; +import com.xceptance.neodymium.NeodymiumCucumberRunListener; import com.xceptance.neodymium.util.Neodymium; import com.xceptance.neodymium.util.WebDriverUtils; @@ -9,6 +11,7 @@ import cucumber.api.java.After; import cucumber.api.java.Before; import cucumber.api.java.en.Given; +import cucumber.api.java.en.Then; public class CucumberSupport { @@ -39,4 +42,10 @@ public void validateBrowser(String browserProfileName) { Assert.assertEquals(browserProfileName, Neodymium.getBrowserProfileName()); } + + @Then("^validate the AllureSelenide listener is active$") + public void validateAllureSelenideListenerIsActive() + { + Assert.assertTrue(" AllureSelenide listener is not attached", SelenideLogger.hasListener(NeodymiumCucumberRunListener.LISTENER_NAME)); + } } diff --git a/src/test/java/com/xceptance/neodymium/testclasses/cucumber/CucumberValidateAllureSelenideListenerIsActive.java b/src/test/java/com/xceptance/neodymium/testclasses/cucumber/CucumberValidateAllureSelenideListenerIsActive.java new file mode 100644 index 000000000..bf5e12559 --- /dev/null +++ b/src/test/java/com/xceptance/neodymium/testclasses/cucumber/CucumberValidateAllureSelenideListenerIsActive.java @@ -0,0 +1,13 @@ +package com.xceptance.neodymium.testclasses.cucumber; + +import org.junit.runner.RunWith; + +import com.xceptance.neodymium.NeodymiumCucumberRunner; + +import cucumber.api.CucumberOptions; + +@RunWith(NeodymiumCucumberRunner.class) +@CucumberOptions(features = "src/test/resources/com/xceptance/neodymium/testclasses/cucumber/CucumberValidateAllureSelenideListenerIsActive.feature", glue = "com/xceptance/neodymium/testclasses/cucumber", plugin = "null_summary") +public class CucumberValidateAllureSelenideListenerIsActive +{ +} diff --git a/src/test/java/com/xceptance/neodymium/tests/AllureSelenideListenerTest.java b/src/test/java/com/xceptance/neodymium/tests/AllureSelenideListenerTest.java new file mode 100644 index 000000000..2e99b5805 --- /dev/null +++ b/src/test/java/com/xceptance/neodymium/tests/AllureSelenideListenerTest.java @@ -0,0 +1,25 @@ +package com.xceptance.neodymium.tests; + +import org.junit.Test; +import org.junit.runner.JUnitCore; +import org.junit.runner.Result; + +import com.xceptance.neodymium.testclasses.allure.AllureSelenideListenerIsActiveForJava; +import com.xceptance.neodymium.testclasses.cucumber.CucumberValidateAllureSelenideListenerIsActive; + +public class AllureSelenideListenerTest extends NeodymiumTest +{ + @Test + public void testAllureSelenideListenerIsActiveForCucumber() + { + Result result = JUnitCore.runClasses(CucumberValidateAllureSelenideListenerIsActive.class); + checkPass(result, 1, 0, 0); + } + + @Test + public void testAllureSelenideListenerIsActiveForJava() + { + Result result = JUnitCore.runClasses(AllureSelenideListenerIsActiveForJava.class); + checkPass(result, 1, 0, 0); + } +} diff --git a/src/test/java/com/xceptance/neodymium/util/SelenideAddonsTest.java b/src/test/java/com/xceptance/neodymium/util/SelenideAddonsTest.java new file mode 100644 index 000000000..bce3d29d6 --- /dev/null +++ b/src/test/java/com/xceptance/neodymium/util/SelenideAddonsTest.java @@ -0,0 +1,67 @@ +package com.xceptance.neodymium.util; + +import static com.codeborne.selenide.Selenide.$; + +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; + +import com.codeborne.selenide.Selenide; +import com.codeborne.selenide.ex.ElementShould; +import com.codeborne.selenide.ex.UIAssertionError; +import com.xceptance.neodymium.NeodymiumRunner; +import com.xceptance.neodymium.module.statement.browser.multibrowser.Browser; + +@RunWith(NeodymiumRunner.class) +@Browser("Chrome_headless") +public class SelenideAddonsTest +{ + @Test + public void testMatchesValueCondition() + { + Selenide.open("https://blog.xceptance.com/"); + $("#masthead .search-toggle").click(); + $("#search-container .search-field").val("searchphrase").submit(); + + $("#content .search-field").should(SelenideAddons.matchesValue("earchphras")); + } + + @Test + public void testMatchValueCondition() + { + Selenide.open("https://blog.xceptance.com/"); + $("#masthead .search-toggle").click(); + $("#search-container .search-field").val("searchphrase").submit(); + + $("#content .search-field").should(SelenideAddons.matchValue("^s.a.c.p.r.s.$")); + $("#content .search-field").should(SelenideAddons.matchValue("\\D+")); + } + + @Test(expected = ElementShould.class) + public void testMatchValueConditionError() + { + Selenide.open("https://blog.xceptance.com/"); + $("#masthead .search-toggle").click(); + $("#search-container .search-field").val("searchphrase").submit(); + + $("#content .search-field").should(SelenideAddons.matchValue("\\d+")); + } + + @Test() + public void testWrapAssertion() + { + Selenide.open("https://blog.xceptance.com/"); + SelenideAddons.wrapAssertionError(() -> { + Assert.assertEquals("Passionate Testing | Xceptance Blog", Selenide.title()); + }); + } + + @Test(expected = UIAssertionError.class) + public void testWrapAssertionError() + { + Selenide.open("https://blog.xceptance.com/"); + SelenideAddons.wrapAssertionError(() -> { + Assert.assertEquals("MyPageTitle", Selenide.title()); + }); + } +} diff --git a/src/test/resources/com/xceptance/neodymium/testclasses/cucumber/CucumberValidateAllureSelenideListenerIsActive.feature b/src/test/resources/com/xceptance/neodymium/testclasses/cucumber/CucumberValidateAllureSelenideListenerIsActive.feature new file mode 100644 index 000000000..5c5b33c33 --- /dev/null +++ b/src/test/resources/com/xceptance/neodymium/testclasses/cucumber/CucumberValidateAllureSelenideListenerIsActive.feature @@ -0,0 +1,8 @@ +@SetUpWithBrowserTag +Feature: Set browser via tag + + @Chrome_headless + Scenario: Set browsers + Given the browser "Chrome_headless" is setup + Then validate the AllureSelenide listener is active +