/// <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);
/// <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); }