package detective.core; import geb.Browser; import groovy.json.JsonBuilder; import groovy.json.JsonSlurper; import groovy.lang.Closure; import groovy.util.XmlSlurper; import groovy.xml.MarkupBuilder; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import org.hamcrest.Matcher; import org.hamcrest.core.AllOf; import org.hamcrest.core.AnyOf; import org.hamcrest.core.IsNot; import org.junit.Assert; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.typesafe.config.Config; import detective.common.trace.TraceRecord; import detective.common.trace.TraceRecordBuilder; import detective.common.trace.TraceRecorder; import detective.common.trace.impl.TraceRecorderElasticSearchImpl; import detective.core.dsl.WrappedObject; import detective.core.dsl.builder.DslBuilder; import detective.core.geb.GebSession; import detective.core.matcher.IsEqual; import detective.core.matcher.Subset; import detective.core.runner.DslBuilderAndRun; import detective.core.services.DetectiveFactory; import detective.task.EchoTask; import detective.task.HttpClientTask; import detective.utils.StringUtils; import detective.utils.TablePrinter; /** * The Factory / Entry Point class for Detective Framework * * @author James Luo * */ public class Detective { private static final Logger logger = LoggerFactory.getLogger(Detective.class); public static final String PARAMETER_NAME_USER_MESSAGES = "_userMessages"; private enum Recorder { INSTANCE; private final transient TraceRecorderElasticSearchImpl recorder = new TraceRecorderElasticSearchImpl(); public TraceRecorder getRecorder() { return recorder; } } public enum LogLevel{ FATAL, ERROR, WARN, INFO, DEBUG, TRACE; } public static DslBuilder story(){ return new DslBuilderAndRun(); } public static DslBuilder feature(){ return new DslBuilderAndRun(); } public static JsonBuilder jsonBuilder(){ return new JsonBuilder(); } public static JsonBuilder jsonBuilder(Closure c){ JsonBuilder builder = new JsonBuilder(); builder.call(c); return builder; } public static Object jsonParser(String json){ return (new JsonSlurper()).parseText(json); } public static MarkupBuilder xmlBuilder(Closure c){ MarkupBuilder builder = new MarkupBuilder(); return builder; } public static Object xmlParser(String xml){ try { return (new XmlSlurper()).parseText(xml); } catch (Throwable e) { throw new RuntimeException(e); } } public static TraceRecord record(TraceRecord record){ Recorder.INSTANCE.getRecorder().record(record); return record; } public static TraceRecord recordLog(LogLevel level, String message){ TraceRecord record = TraceRecordBuilder.newRecord().withSimpleDateAsHashKey().getRecord(); record.setType("log"); record.setCaption(message); return record(record); } public static TraceRecord info(String message){ logger.info(message); return recordLog(LogLevel.INFO, message); } public static TraceRecord error(String message){ return recordLog(LogLevel.ERROR, message); } public static TraceRecord error(String message, Throwable e){ TraceRecord record = TraceRecordBuilder.newRecord() .withSimpleDateAsHashKey() .withException(e) .getRecord(); record.setType("log"); record.setCaption(message); return record(record); } public static TraceRecord debug(String message){ return recordLog(LogLevel.DEBUG, message); } /** * The message is not only print into console, but aslo added into the output result section */ public static void logUserMessage(Parameters params, String msg){ List<String> userMsgs = (List<String>)params.get(PARAMETER_NAME_USER_MESSAGES); if (userMsgs == null){ synchronized (params){ userMsgs = new ArrayList<String>(); params.put(PARAMETER_NAME_USER_MESSAGES, userMsgs); } } userMsgs.add(msg); info(msg); } public static void logUserMessageAsTable(Parameters params, String title, Object table){ logUserMessage(params, TablePrinter.printObjectAsTable(table, title)); } public static List<String> getUserMessage(Parameters params){ return (List<String>)params.get(PARAMETER_NAME_USER_MESSAGES); } public static EchoTask echoTask(){ return new EchoTask(); } public static HttpClientTask httpclientTask(){ return new HttpClientTask(); } //Matches ============== public static <T> Matcher<T> equalTo(T operand) { return IsEqual.equalTo(operand); } public static <T> Matcher<T> subsetOf(T operand) { if (operand != null && operand instanceof WrappedObject){ operand = (T)((WrappedObject)operand).getValue(); } return Subset.subsetOf(operand); } public static <T> Matcher<T> not(T value) { return IsNot.not(equalTo(value)); } /** * Creates a matcher that matches if the examined object matches <b>ALL</b> of the specified matchers. * <p></p> * For example: * <pre>assertThat("myValue", allOf(startsWith("my"), containsString("Val")))</pre> */ public static <T> Matcher<T> allOf(Matcher<? super T>... matchers) { return AllOf.allOf(Arrays.asList(matchers)); } /** * Creates a matcher that matches if the examined object matches <b>ALL</b> of the specified matchers. * <p></p> * For example: * <pre>assertThat("myValue", allOf(startsWith("my"), containsString("Val")))</pre> */ public static <T> Matcher<T> allOf(Matcher<? super T> first, Matcher<? super T> second) { return AllOf.allOf(first, second); } /** * Creates a matcher that matches if the examined object matches <b>ALL</b> of the specified matchers. * <p></p> * For example: * <pre>assertThat("myValue", allOf(startsWith("my"), containsString("Val")))</pre> */ public static <T> Matcher<T> allOf(Matcher<? super T> first, Matcher<? super T> second, Matcher<? super T> third) { return AllOf.allOf(first, second, third); } /** * Creates a matcher that matches if the examined object matches <b>ALL</b> of the specified matchers. * <p></p> * For example: * <pre>assertThat("myValue", allOf(startsWith("my"), containsString("Val")))</pre> */ public static <T> Matcher<T> allOf(Matcher<? super T> first, Matcher<? super T> second, Matcher<? super T> third, Matcher<? super T> fourth) { return AllOf.allOf(first, second, third, fourth); } /** * Creates a matcher that matches if the examined object matches <b>ALL</b> of the specified matchers. * <p></p> * For example: * <pre>assertThat("myValue", allOf(startsWith("my"), containsString("Val")))</pre> */ public static <T> Matcher<T> allOf(Matcher<? super T> first, Matcher<? super T> second, Matcher<? super T> third, Matcher<? super T> fourth, Matcher<? super T> fifth) { return AllOf.allOf(first, second, third, fourth, fifth); } /** * Creates a matcher that matches if the examined object matches <b>ALL</b> of the specified matchers. * <p></p> * For example: * <pre>assertThat("myValue", allOf(startsWith("my"), containsString("Val")))</pre> */ public static <T> Matcher<T> allOf(Matcher<? super T> first, Matcher<? super T> second, Matcher<? super T> third, Matcher<? super T> fourth, Matcher<? super T> fifth, Matcher<? super T> sixth) { return AllOf.allOf(first, second, third, fourth, fifth, sixth); } /** * Creates a matcher that matches if the examined object matches <b>Any</b> of the specified matchers. * <p></p> * For example: * <pre>assertThat("myValue", anyOf(startsWith("my"), containsString("Val")))</pre> */ public static <T> Matcher<T> anyOf(Matcher<? super T>... matchers) { return AnyOf.anyOf(Arrays.asList(matchers)); } /** * Asserts that <code>actual</code> satisfies the condition specified by * <code>matcher</code>. If not, an {@link AssertionError} is thrown with * information about the matcher and failing value. Example: * * <pre> * assertThat(0, is(1)); // fails: * // failure message: * // expected: is <1> * // got value: <0> * assertThat(0, is(not(1))) // passes * </pre> * * @param <T> * the static type accepted by the matcher (this can flag obvious * compile-time problems such as {@code assertThat(1, is("a"))} * @param actual * the computed value being compared * @param matcher * an expression, built of {@link Matcher}s, specifying allowed * values * * @see org.hamcrest.CoreMatchers * @see org.junit.matchers.JUnitMatchers */ public static <T> void assertThat(T actual, Matcher<T> matcher) { assertThat("", actual, matcher); } /** * Asserts that <code>actual</code> satisfies the condition specified by * <code>matcher</code>. If not, an {@link AssertionError} is thrown with * the reason and information about the matcher and failing value. Example: * * <pre> * : * assertThat("Help! Integers don't work", 0, is(1)); // fails: * // failure message: * // Help! Integers don't work * // expected: is <1> * // got value: <0> * assertThat("Zero is one", 0, is(not(1))) // passes * </pre> * * @param reason * additional information about the error * @param <T> * the static type accepted by the matcher (this can flag obvious * compile-time problems such as {@code assertThat(1, is("a"))} * @param actual * the computed value being compared * @param matcher * an expression, built of {@link Matcher}s, specifying allowed * values * * @see org.hamcrest.CoreMatchers * @see org.junit.matchers.JUnitMatchers */ public static <T> void assertThat(String reason, T actual, Matcher<T> matcher) { Assert.assertThat(reason, actual, matcher); } //Utilities ================ public static String randomId() { return StringUtils.randomBase64UUID(); } public static Config getConfig(){ return DetectiveFactory.INSTANCE.getConfig(); } //GEB==================== /** * The browser is the centre of Geb. It encapsulates a {@link org.openqa.selenium.WebDriver} implementation and references * a {@link geb.Page} object that provides access to the content. * <p> * Browser objects dynamically delegate all method calls and property read/writes that it doesn't implement to the current * page instance via {@code propertyMissing()} and {@code methodMissing()}. * * If a call come from This class, it resolved first * Then get.Browser * Then currentPage * * @see geb.Browser * @return */ public static Browser newBrowser(){ return GebSession.getBrowser(); } /** * Creates a new browser object via the default constructor and executes the closure * with the browser instance as the closure's delegate. * * @return the created browser */ public static Browser _browser(Closure script) { Browser browser = newBrowser(); try { _browser(browser, script); } finally{ browser.close(); } return browser; } public static Browser _browser(Browser browser, Closure script) { script.setResolveStrategy(Closure.DELEGATE_ONLY); Browser.drive(browser, script); return browser; } }