/******************************************************************************* * Copyright 2017 Ivan Shubin http://galenframework.com * * Licensed 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 com.galenframework.junit; import com.galenframework.browser.Browser; import com.galenframework.browser.SeleniumBrowserFactory; import com.galenframework.speclang2.pagespec.SectionFilter; import com.galenframework.specs.page.PageSection; import com.galenframework.suite.GalenPageAction; import com.galenframework.validation.PageValidation; import com.galenframework.validation.ValidationListener; import com.galenframework.validation.ValidationResult; import org.junit.runner.Description; import org.junit.runner.Runner; import org.junit.runner.notification.Failure; import org.junit.runner.notification.RunNotifier; import org.junit.runners.model.InitializationError; import java.awt.*; import java.io.File; import java.io.IOException; import java.lang.annotation.Annotation; import java.util.*; import java.util.List; import static com.galenframework.api.Galen.checkLayout; import static java.util.Arrays.asList; import static java.util.Collections.emptyList; import static java.util.Collections.emptyMap; import static org.junit.runner.Description.createSuiteDescription; import static org.junit.runner.Description.createTestDescription; /** * Performs a single page test using JUnit. Have a look at the following * example * <pre> * @RunWith(GalenSpecRunner.class) * @Exclude("firstExclude", "secondExclude") * @Include("firstInclude", "secondInclude") * @Size(width=640, height=480) * @Spec("/my/package/homepage.gspec") * @Url("http://localhost:13728/") * public void SinglePageTest { * * } * </pre> * <p>This test performs a single page test for the URL * {@code http://localhost:13728/}. It starts the browser, sets its size to * {@code 640x480} and verifies that the page fulfills the specification * {@code homepage.spec}. It excludes the sections {@code firstExclude} and * {@code secondExclude} of the specification and includes the sections * {@code firstInclude} and {@code secondInclude}. * <p>The annotations {@code @Size}, {@code @Spec} and * {@code @Url} are mandatory. */ public class GalenSpecRunner extends Runner { private static final Map<String, Object> NO_JS_VARIABLES = emptyMap(); private static final Properties NO_PROPERTIES = new Properties(); private static final File NO_SCREENSHOT = null; private static final List<String> NO_TAGS = emptyList(); private Class<?> testClass; /** * Constructs a new {@code GalenSpecRunner} that will run {@code testClass}. * * @param testClass the class with the test specification. */ public GalenSpecRunner(Class<?> testClass) throws InitializationError { this.testClass = testClass; } @Override public Description getDescription() { return createSuiteDescription(testClass); } @Override public void run(RunNotifier notifier) { try { Dimension windowsSize = getWindowSize(); String specPath = getSpecPath(); String pageUrl = getUrl(); SectionFilter sectionFilter = getSectionFilter(); run(notifier, windowsSize, sectionFilter, specPath, pageUrl); } catch (Throwable e) { Failure failure = new Failure(getDescription(), e); notifier.fireTestFailure(failure); } } private void run(RunNotifier notifier, Dimension windowsSize, SectionFilter sectionFilter, String specPath, String pageUrl) throws IOException { JUnitListener listener = new JUnitListener(notifier); run(listener, windowsSize, sectionFilter, specPath, pageUrl); } private void run(JUnitListener listener, Dimension windowsSize, SectionFilter sectionFilter, String specPath, String url) throws IOException { Browser browser = createBrowser(); try { browser.load(url); browser.changeWindowSize(windowsSize); checkLayout(browser, specPath, sectionFilter, NO_PROPERTIES, NO_JS_VARIABLES, NO_SCREENSHOT, listener); } finally { browser.quit(); } } private Browser createBrowser() { return new SeleniumBrowserFactory().openBrowser(); } private SectionFilter getSectionFilter() { return new SectionFilter(getIncludedTags(), getExcludedTags()); } private List<String> getExcludedTags() { Exclude annotation = testClass.getAnnotation(Exclude.class); return annotation == null ? NO_TAGS : asList(annotation.value()); } private List<String> getIncludedTags() { Include annotation = testClass.getAnnotation(Include.class); return annotation == null ? NO_TAGS : asList(annotation.value()); } private String getSpecPath() { return getMandatoryAnnotation(Spec.class).value(); } private String getUrl() { return getMandatoryAnnotation(Url.class).value(); } private Dimension getWindowSize() { Size size = getMandatoryAnnotation(Size.class); return new Dimension(size.width(), size.height()); } private <A extends Annotation> A getMandatoryAnnotation(Class<A> annotationType) { A annotation = testClass.getAnnotation(annotationType); if (annotation == null) { throw new IllegalStateException("The annotation @" + annotationType.getSimpleName() + " is missing."); } else { return annotation; } } private static class JUnitListener implements ValidationListener { private final RunNotifier runNotifier; public JUnitListener(RunNotifier runNotifier) { this.runNotifier = runNotifier; } @Override public void onObject(PageValidation pageValidation, String objectName) { } @Override public void onAfterObject(PageValidation pageValidation, String objectName) { } @Override public void onBeforeSpec(PageValidation pageValidation, String objectName, com.galenframework.specs.Spec spec) { Description description = createDescriptionForSpec(objectName, spec); runNotifier.fireTestStarted(description); } @Override public void onSpecError(PageValidation pageValidation, String objectName, com.galenframework.specs.Spec spec, ValidationResult validationResult) { Description description = createDescriptionForSpec(objectName, spec); Failure failure = new Failure(description, new AssertionError(validationResult.getError().getMessages())); runNotifier.fireTestFailure(failure); runNotifier.fireTestFinished(description); } @Override public void onSpecSuccess(PageValidation pageValidation, String objectName, com.galenframework.specs.Spec spec, ValidationResult validationResult) { Description description = createDescriptionForSpec(objectName, spec); runNotifier.fireTestFinished(description); } private Description createDescriptionForSpec(String objectName, com.galenframework.specs.Spec spec) { return createTestDescription(objectName, spec.getOriginalText()); } @Override public void onGlobalError(Exception e) { } @Override public void onBeforePageAction(GalenPageAction action) { } @Override public void onAfterPageAction(GalenPageAction action) { } @Override public void onBeforeSection(PageValidation pageValidation, PageSection pageSection) { } @Override public void onAfterSection(PageValidation pageValidation, PageSection pageSection) { } @Override public void onSubLayout(PageValidation pageValidation, String objectName) { } @Override public void onAfterSubLayout(PageValidation pageValidation, String objectName) { } @Override public void onSpecGroup(PageValidation pageValidation, String specGroupName) { } @Override public void onAfterSpecGroup(PageValidation pageValidation, String specGroupName) { } } }