Skip to content

Commit

Permalink
issue #467: first commit for error detection
Browse files Browse the repository at this point in the history
  • Loading branch information
bhecquet committed Nov 19, 2021
1 parent a492e22 commit 5d5aff7
Show file tree
Hide file tree
Showing 11 changed files with 446 additions and 15 deletions.
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
package com.seleniumtests.connectors.selenium.fielddetector;

import java.io.File;
import java.util.ArrayList;
import java.util.List;

import com.seleniumtests.customexception.ConfigurationException;
import com.seleniumtests.customexception.ScenarioException;
Expand All @@ -24,13 +22,14 @@ public class FieldDetectorConnector {
private String url;
private static final String STATUS_URL = "/status";
private static final String DETECT_URL = "/detect";
private static final String DETECT_ERROR_URL = "/detectError";

/**
*
* @param url URL of the service
*/
public FieldDetectorConnector(String url) {
this.url = url + DETECT_URL;
this.url = url;

try {
HttpResponse<String> response = Unirest.get(url + STATUS_URL).asString();
Expand All @@ -42,18 +41,58 @@ public FieldDetectorConnector(String url) {
}
}

/**
* Detect fields and labels
* @param imageFile
* @return
*/
public JSONObject detect(File imageFile) {
return detect(imageFile, 1);
}

/**
* Detect fields and labels
* @param imageFile
* @return
*/
public JSONObject detect(File imageFile, double resizeFactor) {
return detect(imageFile, resizeFactor, DETECT_URL);
}

/**
* Detect error message and fieids in error
* @param imageFile
* @return
*/
public JSONObject detectError(File imageFile) {
return detectError(imageFile, 1);
}

/**
* Detect error message and fieids in error
* @param imageFile
* @return
*/
public JSONObject detectError(File imageFile, double resizeFactor) {
return detect(imageFile, resizeFactor, DETECT_ERROR_URL);
}

/**
* Send image to image field detector and retrieve the box and text
* @param imageFile
* @param resizeFactor
* @param urlPath
* @return
*/
private JSONObject detect(File imageFile, double resizeFactor, String urlPath) {
if (imageFile == null) {
throw new ScenarioException("Image file is null");
}
if (!imageFile.exists()) {
throw new ScenarioException(String.format("Image file %s not found", imageFile.getAbsolutePath()));
}

HttpResponse<JsonNode> fieldDefinition = Unirest.post(url)
HttpResponse<JsonNode> fieldDefinition = Unirest.post(url + urlPath)
.field("resize", resizeFactor)
.field("image", imageFile)
.asJson();
Expand All @@ -69,3 +108,6 @@ public JSONObject detect(File imageFile, double resizeFactor) {

}
}



Original file line number Diff line number Diff line change
Expand Up @@ -25,34 +25,60 @@ public class ImageFieldDetector {
private double resizeFactor;
private FieldDetectorConnector fieldDetectorInstance;
private JSONObject detectionJsonData;
private FieldType fieldTypeToDetect;

public enum FieldType {
ALL_FORM_FIELDS, // all fields: radio, text fields, buttons, ...
ERROR_MESSAGES_AND_FIELDS; // error messages and fields in error
}

public ImageFieldDetector(File image) {
this(image, 1);
}
public ImageFieldDetector(File image, double resizeFactor) {
this(image, resizeFactor, FieldType.ALL_FORM_FIELDS);
}

/**
* Initialize the image field detector
* Depending on fieldTypeToDetect, it will use either a yolo model or an other
* @param image the image to detect fields in
* @param resizeFactor resizing of input image. With a factor > 1, detection will be better but longer
* @param fieldTypeToDetect ALL_FORM_FIELDS: all fields: radio, text fields, buttons, ...
* ERROR_MESSAGE_AND_FIELDS: error messages and fields in error
*/
public ImageFieldDetector(File image, double resizeFactor, FieldType fieldTypeToDetect) {
this.image = image;
this.resizeFactor = resizeFactor;
this.fieldTypeToDetect = fieldTypeToDetect;
fieldDetectorInstance = SeleniumTestsContextManager.getThreadContext().getFieldDetectorInstance();
if (fieldDetectorInstance == null) {
throw new ConfigurationException("Image Field detector has not been properly configured");
}
}

private void callDetector() {
if (detectionJsonData == null) {
if (fieldTypeToDetect == FieldType.ALL_FORM_FIELDS) {
detectionJsonData = fieldDetectorInstance.detect(image, resizeFactor);
} else if (fieldTypeToDetect == FieldType.ERROR_MESSAGES_AND_FIELDS) {
detectionJsonData = fieldDetectorInstance.detectError(image, resizeFactor);
}
}
}

/**
* get list of fields for the image
* if detection has not already been done, do it
* @return
*/
public List<Field> detectFields() {
if (detectionJsonData == null) {
detectionJsonData = fieldDetectorInstance.detect(image, resizeFactor);
}
callDetector();

List<Field> fields = new ArrayList<>();
try {
List<JSONObject> jsonFields = detectionJsonData.getJSONArray("fields").toList();


for (JSONObject jsonNode: jsonFields) {
fields.add(Field.fromJson(jsonNode));
}
Expand All @@ -69,9 +95,7 @@ public List<Field> detectFields() {
* @return
*/
public List<Label> detectLabels() {
if (detectionJsonData == null) {
detectionJsonData = fieldDetectorInstance.detect(image, resizeFactor);
}
callDetector();

List<Label> labels = new ArrayList<>();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@
import com.seleniumtests.core.SeleniumTestsContextManager;
import com.seleniumtests.core.TestStepManager;
import com.seleniumtests.core.TestTasks;
import com.seleniumtests.core.testanalysis.ErrorCauseFinder;
import com.seleniumtests.core.testretry.TestRetryAnalyzer;
import com.seleniumtests.core.utils.TestNGResultUtils;
import com.seleniumtests.customexception.ConfigurationException;
Expand Down Expand Up @@ -523,6 +524,7 @@ private void logLastStep(ITestResult testResult) {
TestStepManager.logTestStep(tearDownStep);

// at this stage we have the pictures of the last state. Try to find error cause if test is failed
new ErrorCauseFinder().findErrorCause();

}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
package com.seleniumtests.core.testanalysis;

import java.io.File;
import java.util.ArrayList;
import java.util.List;

import org.apache.log4j.Logger;

import com.seleniumtests.connectors.selenium.fielddetector.Field;
import com.seleniumtests.connectors.selenium.fielddetector.ImageFieldDetector;
import com.seleniumtests.connectors.selenium.fielddetector.Label;
import com.seleniumtests.connectors.selenium.fielddetector.ImageFieldDetector.FieldType;
import com.seleniumtests.core.TestStepManager;
import com.seleniumtests.reporter.logger.Snapshot;
import com.seleniumtests.reporter.logger.TestStep;
import com.seleniumtests.util.logging.SeleniumRobotLogger;

/**
* Class that may help the tester to know what if the cause of test failure
* - we were not on the right page when performing the last step
* - we were on the right page, but an error occured
* - error message is displayed
* - a field is in error
* - we were on the right page, but this page has slightly changed and a new field may have appeared
*
* Some other errors may be found using network data (probably using selenium 4 features)
* - some resources took too long to display
* - error loading resources (HTTP code different from 2xx or 3xx)
*
* @author S047432
*
*/
public class ErrorCauseFinder {

// TODO: link to RootCause and RootCause details from the Step annotation which can indicate what is the root cause of this error (declarative for a step)

/*
* TESTS:
* - si le ImageFieldDetector ne s'initialise pas, il ne faut pas planter
*/

private static final String[] ERROR_WORDS = new String[] {"error", "erreur", "problem", "problème"};

private static final Logger logger = SeleniumRobotLogger.getLogger(ErrorCauseFinder.class);

public enum ErrorType {
ERROR_MESSAGE,
ERROR_IN_FIELD
}

public class ErrorCause {

private ErrorType type;
private String description;

public ErrorCause(ErrorType type, String description) {
this.type = type;
this.description = description;
}

public ErrorType getType() {
return type;
}

public String getDescription() {
return description;
}

@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}

if (obj instanceof ErrorCause && this == obj) {
return true;
}

ErrorCause cause = (ErrorCause)obj;
if (cause.description != null && cause.description.equals(description) && cause.type == type) {
return true;
} else {
return false;
}
}

@Override
public int hashCode() {
if (description != null) {
return description.hashCode();
} else {
return 0;
}

}


}

public void findErrorCause() {
findErrorInLastStepSnapshots();
}

private List<ErrorCause> findErrorInLastStepSnapshots() {

List<ErrorCause> causes = new ArrayList<>();

TestStep lastTestStep = TestStepManager.getInstance().getLastTestStep();
if (lastTestStep != null) {
for (Snapshot snapshot: lastTestStep.getSnapshots()) {
try {
ImageFieldDetector imageFieldDetector = new ImageFieldDetector(new File(snapshot.getScreenshot().getFullImagePath()), 1, FieldType.ERROR_MESSAGES_AND_FIELDS);
List<Field> fields = imageFieldDetector.detectFields();
List<Label> labels = imageFieldDetector.detectLabels();


// are some text considered as error messages (mainly in red on page)
for (Field field: fields) {
if ("error_message".equals(field.getClassName())) {
// find the related label
for (Label label: labels) {
if (label.isInside(field)) {
causes.add(new ErrorCause(ErrorType.ERROR_MESSAGE, label.getText()));
}
}
} else if ("error_field".equals(field.getClassName())) {
causes.add(new ErrorCause(ErrorType.ERROR_IN_FIELD, "At least one field in error"));
}
}

// do some label contain "error" or "problem"
for (Label label: labels) {
for (String errorWord: ERROR_WORDS) {
ErrorCause possibleErrorCause = new ErrorCause(ErrorType.ERROR_MESSAGE, label.getText());
if (label.getText().contains(errorWord) && !causes.contains(possibleErrorCause)) {
causes.add(possibleErrorCause);
}
}
}

} catch (Exception e) {
logger.error("Error searching for errors in last snapshots: " + e.getMessage());
break;
}

/*
* les "error_field" vont nous dire qu'il y a X champs en erreur, on ne pourra pas nécessairement faire plus
* Cela dit juste que dans le formulaire, il y a des erreurs de saisie (champs manquants ou valeurs incorrectes)
* les "error_message" doivent être mis en relation avec les labels qui sont trouvés pour disposer du texte
*/
}
}

return causes;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ public void reinit() {
System.clearProperty(SeleniumTestsContext.IMAGE_FIELD_DETECTOR_SERVER_URL);
}

@Test(groups="it", enabled = true)
@Test(groups="it", enabled = false)
public void testFieldDetection() throws IOException {
ImageFieldDetector imageFieldDetector;
try {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package com.seleniumtests.it.core.testanalysis;

import org.testng.ITestContext;
import org.testng.annotations.Test;
import org.testng.xml.XmlSuite.ParallelMode;

import com.seleniumtests.GenericTest;
import com.seleniumtests.core.SeleniumTestsContext;
import com.seleniumtests.it.reporter.ReporterTest;

public class TestErrorCauseFInder extends GenericTest {

@Test(groups={"it"})
public void testMultiThreadTests(ITestContext testContext) throws Exception {

try {
//System.setProperty(SeleniumTestsContext.IMAGE_FIELD_DETECTOR_SERVER_URL, "http://localhost:5000");

ReporterTest.executeSubTest(1, new String[] {"com.seleniumtests.it.stubclasses.StubTestClassForDriverTest"}, ParallelMode.NONE, new String[] {"testImageDetection"});


} finally {
System.clearProperty(SeleniumTestsContext.IMAGE_FIELD_DETECTOR_SERVER_URL);
}

}

}
Loading

0 comments on commit 5d5aff7

Please sign in to comment.