MatcherAspect.java

package io.wcm.qa.glnm.aspectj;

import static org.apache.commons.lang3.StringUtils.abbreviateMiddle;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.hamcrest.Matcher;
import org.hamcrest.StringDescription;
import org.openqa.selenium.TakesScreenshot;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import io.wcm.qa.glnm.reporting.GaleniumReportUtil;

/**
 * <p>
 * Adds matches to Allure Report with correct pass/fail status.
 * </p>
 *
 * @since 5.0.0
 */
@Aspect("perthis(execution(* *..*.matches(..)))")
public class MatcherAspect {

  private static final Logger LOG = LoggerFactory.getLogger(MatcherAspect.class);
  private String startStepUuid;

  /**
   * <p>doneMatching.</p>
   *
   * @param joinPoint a {@link org.aspectj.lang.JoinPoint} object.
   * @param result a boolean.
   */
  @AfterReturning(pointcut = "execution(boolean org.hamcrest.Matcher.matches(..))", returning = "result")
  public void doneMatching(final JoinPoint joinPoint, boolean result) {
    if (LOG.isTraceEnabled()) {
      LOG.trace("done: " + joinPoint.getSignature().toLongString());
    }
    Matcher matcher = (Matcher)joinPoint.getTarget();
    if (result) {
      passStep(matcher);
    }
    else {
      failStep(joinPoint, matcher);
    }
  }

  /**
   * <p>
   * hamcrestMatcherMatch.
   * </p>
   */
  @Pointcut("execution(boolean org.hamcrest.Matcher.matches(..))")
  public void hamcrestMatcherMatch() {
    // pointcut body, should be empty
  }

  /**
   * <p>
   * startMatch.
   * </p>
   *
   * @param joinPoint a {@link org.aspectj.lang.JoinPoint} object.
   */
  @Before("execution(boolean org.hamcrest.Matcher.matches(..))")
  public void startMatching(final JoinPoint joinPoint) {
    if (LOG.isTraceEnabled()) {
      LOG.trace("before: " + joinPoint.getSignature().toLongString());
    }
    startStepUuid = GaleniumReportUtil.startStep("matching");
  }

  private StringDescription descriptionFor(Matcher matcher) {
    StringDescription description = new StringDescription();
    description
        .appendText("Expected: ")
        .appendDescriptionOf(matcher);
    return description;
  }

  private void failStep(final JoinPoint joinPoint, Matcher matcher) {
    StringDescription description = descriptionFor(matcher);
    description
        .appendText(System.lineSeparator())
        .appendText("     but: ");
    Object matchedItem = joinPoint.getArgs()[0];
    describeMismatch(matcher, description, matchedItem);
    GaleniumReportUtil.updateStepName(startStepUuid, description.toString());
    GaleniumReportUtil.failStep(startStepUuid);
    screenshot(matchedItem);
    GaleniumReportUtil.stopStep();
  }

  private void passStep(Matcher matcher) {
    GaleniumReportUtil.updateStepName(startStepUuid, descriptionFor(matcher).toString());
    GaleniumReportUtil.passStep(startStepUuid);
    GaleniumReportUtil.stopStep();
  }

  private static void describeMismatch(Matcher matcher, StringDescription description, Object matchedItem) {
    if (matchedItem != null
        && matchedItem instanceof String) {
      String shortMessage = abbreviateMiddle((String)matchedItem, "[...]", 200);
      matcher.describeMismatch(shortMessage, description);
      return;
    }

    matcher.describeMismatch(matchedItem, description);
  }

  private static void screenshot(Object matchedItem) {
    if (matchedItem == null) {
      return;
    }
    if (matchedItem instanceof TakesScreenshot) {
      if (LOG.isDebugEnabled()) {
        LOG.debug("taking screenshot of " + matchedItem);
      }
      GaleniumReportUtil.takeScreenshot("matcher-screenshot", (TakesScreenshot)matchedItem, false);
      return;
    }
  }

}