org.openqa.selenium.server.htmlrunner.ReflectivelyDiscoveredSteps.java Source code

Java tutorial

Introduction

Here is the source code for org.openqa.selenium.server.htmlrunner.ReflectivelyDiscoveredSteps.java

Source

// Licensed to the Software Freedom Conservancy (SFC) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The SFC licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License.  You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied.  See the License for the
// specific language governing permissions and limitations
// under the License.

package org.openqa.selenium.server.htmlrunner;

import com.google.common.base.Supplier;
import com.google.common.base.Suppliers;
import com.google.common.collect.ImmutableMap;

import com.thoughtworks.selenium.SeleneseTestBase;
import com.thoughtworks.selenium.Selenium;
import com.thoughtworks.selenium.SeleniumException;
import com.thoughtworks.selenium.Wait;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

class ReflectivelyDiscoveredSteps implements Supplier<ImmutableMap<String, CoreStepFactory>> {
    private static final Logger LOG = Logger.getLogger("Selenium Core Step");

    private static Supplier<ImmutableMap<String, CoreStepFactory>> REFLECTIVE_STEPS = Suppliers
            .memoize(ReflectivelyDiscoveredSteps::discover);

    public ImmutableMap<String, CoreStepFactory> get() {
        return REFLECTIVE_STEPS.get();
    }

    private static ImmutableMap<String, CoreStepFactory> discover() {
        ImmutableMap.Builder<String, CoreStepFactory> factories = ImmutableMap.builder();

        Set<String> seenNames = new HashSet<>();
        // seed the seen names with methods we definitely don't want folks accessing
        seenNames.add("addCustomRequestHeader");
        seenNames.add("allowNativeXpath");
        seenNames.add("pause");
        seenNames.add("rollup");
        seenNames.add("setBrowserLogLevel");
        seenNames.add("setExtensionJs");
        seenNames.add("start");
        seenNames.add("stop");

        for (final Method method : Selenium.class.getMethods()) {
            if (!seenNames.add(method.getName())) {
                continue;
            }

            if (method.getParameterCount() > 2) {
                continue;
            }

            CoreStepFactory factory = ((locator, value) -> (selenium, state) -> invokeMethod(method, selenium,
                    locator, value, (result -> NextStepDecorator.IDENTITY)));

            factories.put(method.getName(), factory);

            // Methods of the form getFoo(target) result in commands:
            // getFoo, assertFoo, verifyFoo, assertNotFoo, verifyNotFoo
            // storeFoo, waitForFoo, and waitForNotFoo.
            final String shortName;
            boolean isAccessor;
            if (method.getName().startsWith("get")) {
                shortName = method.getName().substring("get".length());
                isAccessor = true;
            } else if (method.getName().startsWith("is")) {
                shortName = method.getName().substring("is".length());
                isAccessor = true;
            } else {
                shortName = null;
                isAccessor = false;
            }

            if (shortName != null) {
                String negatedName = negateName(shortName);

                factories.put("assert" + shortName, (loc, val) -> (selenium, state) -> invokeMethod(method,
                        selenium, state.expand(loc), state.expand(val), (seen -> {
                            Object expected = getExpectedValue(method, state.expand(loc), state.expand(val));
                            try {
                                SeleneseTestBase.assertEquals(expected, seen);
                            } catch (AssertionError e) {
                                return NextStepDecorator.ASSERTION_FAILED(e.getMessage());
                            }
                            return NextStepDecorator.IDENTITY;
                        })));

                factories.put("assert" + negatedName, (loc, val) -> (selenium, state) -> invokeMethod(method,
                        selenium, state.expand(loc), state.expand(val), (seen) -> {
                            Object expected = getExpectedValue(method, state.expand(loc), state.expand(val));

                            try {
                                SeleneseTestBase.assertNotEquals(expected, seen);
                                return NextStepDecorator.IDENTITY;
                            } catch (AssertionError e) {
                                return NextStepDecorator.ASSERTION_FAILED(e.getMessage());
                            }
                        }));

                factories.put("verify" + shortName, (loc, val) -> (selenium, state) -> invokeMethod(method,
                        selenium, state.expand(loc), state.expand(val), (seen) -> {
                            Object expected = getExpectedValue(method, state.expand(loc), state.expand(val));

                            try {
                                SeleneseTestBase.assertEquals(expected, seen);
                                return NextStepDecorator.IDENTITY;
                            } catch (AssertionError e) {
                                return NextStepDecorator.VERIFICATION_FAILED(e.getMessage());
                            }
                        }));

                factories.put("verify" + negatedName, (loc, val) -> (selenium, state) -> invokeMethod(method,
                        selenium, state.expand(loc), state.expand(val), (seen) -> {
                            Object expected = getExpectedValue(method, state.expand(loc), state.expand(val));

                            try {
                                SeleneseTestBase.assertNotEquals(expected, seen);
                                return NextStepDecorator.IDENTITY;
                            } catch (AssertionError e) {
                                return NextStepDecorator.VERIFICATION_FAILED(e.getMessage());
                            }
                        }));
            }

            if (isAccessor) {
                factories.put("store" + shortName, (loc, val) -> (selenium, state) -> invokeMethod(method, selenium,
                        state.expand(loc), state.expand(val), (toStore) -> {
                            state.store(state.expand(loc), toStore);
                            return NextStepDecorator.IDENTITY;
                        }));

                factories.put("waitFor" + shortName, (loc, val) -> (selenium, state) -> {
                    String[] args = buildArgs(method, state.expand(loc), state.expand(val));

                    try {
                        new Wait() {
                            @Override
                            public boolean until() {
                                try {
                                    Object result = method.invoke(selenium, args);
                                    if (Boolean.class.isAssignableFrom(result.getClass())) {
                                        return (Boolean) result;
                                    }
                                    return true;
                                } catch (IllegalAccessException e) {
                                    // It's going to be interesting to see this be thrown
                                    throw new RuntimeException(e);
                                } catch (InvocationTargetException e) {
                                    return false;
                                }
                            }
                        }.wait("Can't wait for " + shortName);
                    } catch (Wait.WaitTimedOutException e) {
                        return NextStepDecorator.ERROR(e);
                    }
                    return NextStepDecorator.IDENTITY;
                });

                factories.put("waitFor" + negateName(shortName), (loc, val) -> (selenium, state) -> {
                    String[] args = buildArgs(method, state.expand(loc), state.expand(val));

                    try {
                        new Wait() {
                            @Override
                            public boolean until() {
                                try {
                                    Object result = method.invoke(selenium, args);
                                    if (Boolean.class.isAssignableFrom(result.getClass())) {
                                        return !(Boolean) result;
                                    }
                                    return false;
                                } catch (IllegalAccessException e) {
                                    // It's going to be interesting to see this be thrown
                                    throw new RuntimeException(e);
                                } catch (InvocationTargetException e) {
                                    return true;
                                }
                            }
                        }.wait("Managed to wait for " + shortName);
                    } catch (Wait.WaitTimedOutException e) {
                        return NextStepDecorator.ERROR(e);
                    }
                    return NextStepDecorator.IDENTITY;
                });

                //        factories.put(
                //          "waitFor" + shortName,
                //          (loc, val) -> (selenium, state) -> {
                //            String locator = state.expand(loc);
                //            String value = state.expand(val);
                //
                //            String[] args = buildArgs(method, locator, value);
                //
                //            new Wait() {
                //              @Override
                //              public boolean until() {
                //                invokeMethod(method, selenium, buildArgs(method, state.expand(loc), state.expand(val)));
                //                return true;
                //              }
                //            }.wait("Can't wait for " + shortName);
                //            return NextStepDecorator.IDENTITY;
                //          });
                //
                //        factories.put(
                //          "waitFor" + negateName(shortName),
                //          (loc, val) -> (selenium, state) -> {
                //            String locator = state.expand(loc);
                //            String value = state.expand(val);
                //            try {
                //              new Wait() {
                //                @Override
                //                public boolean until() {
                //                  invokeMethod(method, selenium, buildArgs(method, locator, value));
                //                  return true;
                //                }
                //              }.wait("Can't wait for " + shortName);
                //              return NextStepDecorator.ASSERTION_FAILED("Wait succeeded but should have failed");
                //            } catch (Wait.WaitTimedOutException e) {
                //              return NextStepDecorator.IDENTITY;
                //            }
                //          });
            }

            factories.put(method.getName() + "AndWait", (loc, val) -> (selenium, state) -> invokeMethod(method,
                    selenium, state.expand(loc), state.expand(val), (seen) -> {
                        // TODO: Hard coding this is obviously bogus
                        selenium.waitForPageToLoad("30000");
                        return NextStepDecorator.IDENTITY;
                    }));
        }

        return factories.build();
    }

    private static String negateName(String shortName) {
        Pattern pattern = Pattern.compile("^(.*)Present$");
        Matcher matcher = pattern.matcher(shortName);
        if (matcher.matches()) {
            return matcher.group(1) + "NotPresent";
        }
        return "Not" + shortName;
    }

    private static String[] buildArgs(Method method, String locator, String value) {
        String[] args = new String[method.getParameterCount()];
        switch (method.getParameterCount()) {
        case 2:
            args[1] = value.trim();
            // Fall through

        case 1:
            args[0] = locator.trim();
            break;

        case 0:
            // Nothing to do. Including for completeness.
            break;
        }
        return args;
    }

    private static Object getExpectedValue(Method method, String locator, String value) {
        Class<?> returnType = method.getReturnType();
        if (returnType.equals(boolean.class) || returnType.equals(Boolean.class)) {
            return true;
        }

        switch (method.getParameterCount()) {
        case 0:
            return locator;

        case 1:
            return value;

        default:
            throw new SeleniumException("Unable to find expected result: " + method.getName());
        }
    }

    private static NextStepDecorator invokeMethod(Method method, Selenium selenium, String locator, String value,
            OnSuccess onSuccess) {
        String[] args = buildArgs(method, locator, value);
        try {
            Object result = method.invoke(selenium, args);
            return onSuccess.handle(result);
        } catch (ReflectiveOperationException e) {
            for (Throwable cause = e; cause != null; cause = cause.getCause()) {
                if (cause instanceof SeleniumException) {
                    return NextStepDecorator.ERROR(cause);
                }
                if (cause instanceof Wait.WaitTimedOutException) {
                    return NextStepDecorator.ERROR(cause);
                }
            }
            throw new CoreRunnerError(
                    String.format("Unable to emulate %s %s", method.getName(), Arrays.asList(args)), e);
        }
    }

    private interface OnSuccess {
        NextStepDecorator handle(Object result);
    }

}