/// <summary>
        /// Gets the test classes associated with this page object.
        /// </summary>
        /// <param name="source">The source page object.</param>
        /// <returns>The test classes associated with this page object.</returns>
        internal static IEnumerable <Type> TestClasses(this IPageObject source)
        {
            var interfaces           = source.GetType().GetInterfaces();
            var pageObjectInterfaces = interfaces.Where(e => e.GetTypeInfo().ImplementedInterfaces.Contains(typeof(IPageObject)));
            var matchingTypes        = pageObjectInterfaces.Union(new[] { source.GetType() });
            var result = TestClassTypes.Where(type => type.GetInterfaces().Any(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IPageObjectTestClass <>) && matchingTypes.Contains(i.GenericTypeArguments.First()))).ToArray();

            return(result);
        }
        /// <summary>
        /// Add page object statistics.
        /// </summary>
        /// <param name="source">The page object</param>
        /// <param name="testClassStatistic">The statistics to add.</param>
        internal void Add(IPageObject source, TestClassStatistic testClassStatistic)
        {
            var t = source.GetType();

            if (!pageObjectStatistic.ContainsKey(t))
            {
                pageObjectStatistic[t] = new PageObjectStatistic();
            }

            pageObjectStatistic[t] += testClassStatistic;
        }
        /// <summary>
        /// Gets the page object statistic for a certain page object type.
        /// </summary>
        /// <param name="pageObject">The page object.</param>
        /// <returns>The page object statistic.</returns>
        internal static PageObjectStatistic PageObjectStatistic(IPageObject pageObject)
        {
            var t = pageObject.GetType();

            if (!pageObjectStatistic.ContainsKey(t))
            {
                pageObjectStatistic[t] = new PageObjectStatistic();
            }

            return(pageObjectStatistic[t]);
        }
        /// <summary>
        /// Find a page object in the page object tree.
        /// </summary>
        /// <param name="condition">The condition that must evaluate true for the resulting page object.</param>
        /// <param name="result">The result page object.</param>
        /// <typeparam name="TPageObject">The page object type to search for.</typeparam>
        /// <returns>The page object.</returns>
        private bool TryFind <TPageObject>(Predicate <TPageObject> condition, out TPageObject result) where TPageObject : IPageObject
        {
            Queue <IPageObject> q = new Queue <IPageObject>();

            if (TryGet(out IEnumerable <TPageObject> registeredObjects))
            {
                foreach (var a in registeredObjects)
                {
                    q.Enqueue(a);
                }
            }

            q.Enqueue(rootObject);

            while (q.Count > 0)
            {
                IPageObject current = q.Dequeue();

                // interface types match any implementation, whereas classes must match exactly
                var match = typeof(TPageObject).IsInterface ? current is TPageObject : current.GetType().Equals(typeof(TPageObject));
                if (match)
                {
                    TPageObject page = (TPageObject)current;
                    Register <TPageObject>(page);

                    if ((page as ITreeObject).OnCondition && condition(page))
                    {
                        var parent = (IPageObject)page.Parent;
                        result = parent == null ? page : (TPageObject)(Activator.CreateInstance(page.GetType()) as IUIObjectInternal).Init(parent, true);
                        return(true);
                    }
                }

                // traverse children
                var c = ((ITreeObject)current).Children <TPageObject>();

                foreach (IPageObject childPageObject in c)
                {
                    // generic page object must be child-less
                    if (current.GetType().IsGenericType)
                    {
                        Trace.TraceWarning($"Ignoring children of generic page object type <{current.GetType().Name}>");
                        continue;
                    }

                    q.Enqueue(childPageObject);
                }
            }

            result = default(TPageObject);
            return(false);
        }
        /// <summary>
        /// Run all tests with the <c>PageTestAttribute</c>.
        /// </summary>
        /// <param name="source">The source page object.</param>
        /// <param name="pageObjectStatistics">Whether to write the result tree.</param>
        /// <param name="filter">The test method filter predicate.</param>
        /// <param name="writeTree">Whether to write the result tree.</param>
        /// <returns>This page object.</returns>
        private static Tree TestBottomUp(this IPageObject source, Func <MethodInfo, Type, bool> filter, bool writeTree, PageObjectStatistics pageObjectStatistics)
        {
            Trace.WriteLine($"Run tests for {source.GetType().FullName}");

            // run tests for every child
            foreach (var child in source.Children())
            {
                TestBottomUp(child, filter, false, pageObjectStatistics);
            }

            // run tests for this page object
            return(Test(source, filter, writeTree, pageObjectStatistics));
        }
        /// <summary>
        /// Run all tests with the <c>PageTestAttribute</c>.
        /// </summary>
        /// <param name="source">The source page object.</param>
        /// <param name="methodFilter">The test method filter predicate.</param>
        /// <param name="pageTestClassFilter">The page test class filter predicate.</param>
        /// <returns>This page object.</returns>
        public static IPageObject TestBottomUp(this IPageObject source, Predicate <MethodInfo> methodFilter = null, Predicate <IPageObjectTests> pageTestClassFilter = null)
        {
            Trace.WriteLine("Run tests for " + source.GetType().FullName);

            // run tests for every child
            foreach (var child in source.Children())
            {
                child.TestBottomUp(methodFilter, pageTestClassFilter);
            }

            // run tests for this page object
            source.Test(methodFilter, pageTestClassFilter);

            return(source);
        }
        /// <summary>
        /// Gets the page object types.
        /// </summary>
        /// <param name="parentToFind">The parent page object.</param>
        /// <returns>The child types.</returns>
        internal static IEnumerable <Type> ChildTypes(IPageObject parentToFind)
        {
            var notMatchingTypeYetMatchingQualifiedName = ChildToParent.Where(childAndParents => !childAndParents.Value.Contains(parentToFind.GetType()) && childAndParents.Value.Select(parent => parent.AssemblyQualifiedName).Contains(parentToFind.GetType().AssemblyQualifiedName));

            if (notMatchingTypeYetMatchingQualifiedName.Any())
            {
                var similarParents       = notMatchingTypeYetMatchingQualifiedName.First().Value;
                var similarParent        = similarParents.First(parent => parent.AssemblyQualifiedName == parentToFind.GetType().AssemblyQualifiedName);
                var childOfSimilarParent = notMatchingTypeYetMatchingQualifiedName.First().Key;
                throw new AmbiguousParentObjectFoundException(parentToFind, similarParent, childOfSimilarParent);
            }

            var resultByEquality = ChildToParent.Where(e => e.Value.Contains(parentToFind.GetType())).Select(e => e.Key);

            return(resultByEquality);
        }
 /// <summary>
 /// Gets the page object types.
 /// </summary>
 /// <param name="pageObject">The parent page object.</param>
 /// <returns>The child types.</returns>
 internal static IEnumerable <Type> ChildTypes(IPageObject pageObject) => ParentToChildMap.Where(e => e.Value.Contains(pageObject.GetType())).Select(e => e.Key);
Exemple #9
0
 /// <summary>
 /// Initializes a new instance of the <see cref="AmbiguousParentObjectFoundException"/> class.
 /// </summary>
 public AmbiguousParentObjectFoundException(IPageObject parentToFind, Type similarParent, Type childOfSimilarParent)
     : base($"Error when searching for page object '{parentToFind.GetType().Name}' in location '{parentToFind.GetType().Assembly.Location}' in the page object tree: Found the same class in location {similarParent.Assembly.Location}' with child '{childOfSimilarParent}'. Ensure that page objects are only loaded once. Check https://stackoverflow.com/a/3624044 for ideas on how to solve this issue.")
 {
 }
        /// <summary>
        /// Run tests for this page object.
        /// </summary>
        /// <param name="source">The source page object.</param>
        /// <param name="methodFilter">The test method filter predicate.</param>
        /// <param name="pageTestClassFilter">The page test class filter predicate.</param>
        /// <returns>This page object.</returns>
        public static IPageObject Test(this IPageObject source, Predicate <MethodInfo> methodFilter = null, Predicate <IPageObjectTests> pageTestClassFilter = null)
        {
            methodFilter        = methodFilter ?? (_ => true);
            pageTestClassFilter = pageTestClassFilter ?? (_ => true);

            foreach (Type classWithTests in source.TestClasses())
            {
                Trace.WriteLine("Found page test class for current class " + source.GetType().ToString() + ": " + classWithTests.ToString());

                // create and initialize page test class
                IPageObjectTestsInternal instance;
                var root = ((IUIObjectInternal)source).Root;
                try
                {
                    var configuration = root.Configuration;
                    configuration
                    .DependencyRegistrator
                    .RegisterType(classWithTests);

                    instance = (IPageObjectTestsInternal)configuration.resolver.Resolve(classWithTests);
                }
                catch (Stashbox.Exceptions.ResolutionFailedException exception)
                {
                    throw new TypeResolutionFailedException(exception, $"Configure the resolver via '{nameof(Configuration.DependencyRegistrator)}' in class '{root.GetType().FullName}'.");
                }

                // init page test class
                instance.Init(source);

                // check if tests should be executed according to the page test filter
                if (pageTestClassFilter(instance))
                {
                    Trace.WriteLine("Page test class filter returned true; running tests...");
                }
                else
                {
                    Trace.WriteLine("Page test class filter returned true; skipping tests...");
                    continue;
                }

                // check if tests should be executed according to the runnable predicate
                if (instance.Runnable)
                {
                    Trace.WriteLine("Runnable returned true; running tests...");
                }
                else
                {
                    Trace.WriteLine("Runnable returned false; skipping tests");
                    continue;
                }

                // get page test methods, and order them from top to bottom
                // ordering does not respect inheritence
                var methods               = classWithTests.GetMethods().Where(method => method.GetCustomAttributes(typeof(PageTestAttribute), false).Length > 0);
                var methodsByLine         = methods.OrderBy(method => method.GetCustomAttribute <PageTestAttribute>(true).Line);
                var matchingMethodsByLine = methodsByLine.Where(method => methodFilter(method));

                int testMethodsCount = matchingMethodsByLine.Count();
                if (testMethodsCount > 0)
                {
                    Trace.WriteLine("Found " + testMethodsCount + " test methods");
                }
                else
                {
                    Trace.WriteLine("No test method found; skipping test class");
                    continue;
                }

                Trace.WriteLine($"Execute function {nameof(IPageObjectTests.BeforeFirstTest)}...");
                instance.BeforeFirstTest();

                // check if tests should be executed
                if (!instance.ReadyToRun)
                {
                    throw new TestNotReadyToRunException(classWithTests.FullName);
                }
                else
                {
                    Trace.WriteLine("Tests are ready to run...");
                }

                // create class statistics object
                TestClassStatistic testClassStatistic = new TestClassStatistic(classWithTests);

                // execute test methods
                try
                {
                    foreach (var testMethod in matchingMethodsByLine)
                    {
                        if (instance.IsTestRunnable(testMethod))
                        {
                            Test(testClassStatistic, instance, testMethod);
                        }
                        else
                        {
                            Trace.WriteLine($"Skipping test: '{nameof(instance.IsTestRunnable)}' returned false for test method '{testMethod.Name}'.");
                        }
                    }

                    Trace.WriteLine($"Execute function {nameof(IPageObjectTests.AfterLastTest)}...");
                    instance.AfterLastTest();
                }
                finally
                {
                    var pageObjectStatistic = PageObjectStatistic(source);
                    pageObjectStatistic += testClassStatistic;
                }
            }

            return(source);
        }
 /// <summary>
 /// Clear the page object statistic.
 /// </summary>
 /// <param name="pageObject">The page object.</param>
 internal static void ClearPageObjectStatistic(IPageObject pageObject)
 {
     pageObjectStatistic[pageObject.GetType()] = new PageObjectStatistic();
 }
        /// <summary>
        /// Run tests for this page object.
        /// </summary>
        /// <param name="source">The source page object.</param>
        /// <param name="filter">The test method filter predicate.</param>
        /// <param name="writeTree">Whether to write the result tree.</param>
        /// <param name="pageObjectStatistics">The page object statistics.</param>
        /// <returns>This page object.</returns>
        private static Tree Test(this IPageObject source, Func <MethodInfo, Type, bool> filter, bool writeTree, PageObjectStatistics pageObjectStatistics)
        {
            filter = filter ?? ((e, f) => true);
            Tree result  = null;
            bool success = true;

            try
            {
                foreach (Type classWithTests in source.TestClasses())
                {
                    Trace.WriteLine($"Found page test class for current class {source.GetType().ToString()}: {classWithTests.ToString()}");

                    // create and initialize page test class
                    var instance = (IPageObjectTestsInternal)Activator.CreateInstance(classWithTests);
                    instance.Init(source);

                    // check if tests should be executed
                    if (instance.Runnable)
                    {
                        Trace.WriteLine("Runnable returned true; running tests...");
                    }
                    else
                    {
                        Trace.WriteLine("Runnable returned false; skipping tests");
                        continue;
                    }

                    // get page test methods, and order them from top to bottom
                    // ordering does not respect inheritance
                    var methods               = classWithTests.GetMethods().Where(m => m.GetCustomAttributes(typeof(PageTestAttribute), false).Length > 0);
                    var methodsByLine         = methods.OrderBy(asd => asd.GetCustomAttribute <PageTestAttribute>(true).Line);
                    var matchingMethodsByLine = methodsByLine.Where(f => filter(f, classWithTests));

                    int testMethodsCount = matchingMethodsByLine.Count();
                    if (testMethodsCount > 0)
                    {
                        Trace.WriteLine($"Found {testMethodsCount} test methods");
                    }
                    else
                    {
                        Trace.WriteLine("No test method found; skipping test class");
                        continue;
                    }

                    Trace.WriteLine($"Execute function {nameof(IPageObjectTests.BeforeFirstTest)}...");
                    instance.BeforeFirstTest();

                    // check if tests should be executed
                    if (!instance.ReadyToRun)
                    {
                        throw new TestNotReadyToRunException(classWithTests.FullName);
                    }
                    else
                    {
                        Trace.WriteLine("Tests are ready to run...");
                    }

                    // create class statistics object
                    TestClassStatistic testClassStatistic = new TestClassStatistic(classWithTests);

                    // execute test methods
                    try
                    {
                        foreach (var testMethod in matchingMethodsByLine)
                        {
                            Test(testClassStatistic, instance, testMethod);
                        }

                        Trace.WriteLine($"Execute function {nameof(IPageObjectTests.AfterLastTest)}...");
                        instance.AfterLastTest();
                    }
                    finally
                    {
                        success = false;
                        pageObjectStatistics.Add(source, testClassStatistic);
                    }
                }
            }
            finally
            {
                result = Root(source).Tree;
                foreach (var pair in pageObjectStatistics.Stats)
                {
                    // merge trees
                    result += pair.Value;
                    result += new Edge {
                        To = pair.Value.Root.Id, From = new Node()
                        {
                            Id = pair.Key.FullName
                        }.Id
                    };
                }

                if (!success || writeTree)
                {
                    result.WriteGraph();
                }
            }

            return(result);
        }