All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.seleniumtests.util.helper.AppTestDocumentation Maven / Gradle / Ivy

The newest version!
/**
 * Orignal work: Copyright 2015 www.seleniumtests.com
 * Modified work: Copyright 2016 www.infotel.com
 * 				Copyright 2017-2019 B.Hecquet
 *
 * 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.seleniumtests.util.helper;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.NoSuchElementException;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import org.apache.commons.io.FileUtils;
import org.testng.annotations.Test;

import com.github.javaparser.JavaParser;
import com.github.javaparser.ParseResult;
import com.github.javaparser.ast.CompilationUnit;
import com.github.javaparser.ast.Modifier;
import com.github.javaparser.ast.Node;
import com.github.javaparser.ast.Node.TreeTraversal;
import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration;
import com.github.javaparser.ast.body.MethodDeclaration;
import com.github.javaparser.ast.comments.Comment;
import com.github.javaparser.ast.expr.AnnotationExpr;
import com.github.javaparser.ast.expr.MemberValuePair;
import com.github.javaparser.ast.expr.MethodCallExpr;
import com.github.javaparser.ast.expr.NormalAnnotationExpr;
import com.github.javaparser.ast.expr.ObjectCreationExpr;
import com.github.javaparser.ast.stmt.BlockStmt;
import com.github.javaparser.ast.visitor.VoidVisitorAdapter;
import com.seleniumtests.customexception.ConfigurationException;
import com.seleniumtests.util.StringUtility;

/**
 * Class for creating test documentation, which can be imported into confluence through API
 * It generates a template.confluence file which contains the formatted javadoc for each Test method and step
 * https://support.atlassian.com/confluence-cloud/docs/insert-confluence-wiki-markup/
 *
 */
public class AppTestDocumentation {
	
	private static StringBuilder javadoc;
	private static Map> stepsUsedInTests;
	private static List steps;
	private static List tests;
	private static Integer searchedElements;
	
	public static void main(String[] args) throws IOException {
		System.setProperty("mavenExecution", "true");
		stepsUsedInTests = new HashMap<>();
		steps = new ArrayList<>();
		tests = new ArrayList<>();
		searchedElements = 0;
		
		File srcDir = Paths.get(args[0].replace(File.separator,  "/"), "src", "test", "java").toFile();
		
		// find the root source path (folder where "tests" and "webpage" can be found
		List rootFolders;
		try (Stream files = Files.walk(Paths.get(srcDir.getAbsolutePath()))) {
			rootFolders = files
			        .filter(Files::isDirectory)
			        .filter(p -> p.toAbsolutePath().resolve("tests").toFile().exists() && p.toAbsolutePath().resolve("webpage").toFile().exists())
			        .collect(Collectors.toList());
		}

		javadoc = new StringBuilder("Cette page référence l'ensemble des tests et des opération disponible pour l'application\n");
		
		Path rootFolder = null;
		if (rootFolders.isEmpty()) {
			System.out.println("Cannot find a folder which contains 'tests' and 'webpage' subfolder. The project does not follow conventions");
			javadoc.append("Cannot find a folder which contains 'tests' and 'webpage' subfolder. The project does not follow conventions");
			System.exit(0);
		} else {
			rootFolder = rootFolders.get(0);
		}
		
		javadoc.append("

\n" + "" + "

"); StringBuilder pagesDoc = new StringBuilder(); pagesDoc.append("

Pages

\n"); try (Stream files = Files.walk(rootFolder)) { List pagesFolders = files .filter(Files::isDirectory) .filter(p -> p.getFileName().toString().equals("webpage")) .collect(Collectors.toList()); for (Path pagesFolder: pagesFolders) { explorePages(pagesFolder.toFile(), pagesDoc); } } catch (IndexOutOfBoundsException e) { throw new ConfigurationException("no 'webpage' sub-package found"); } StringBuilder testDoc = new StringBuilder(); testDoc.append("

Scénarios de test

\n"); testDoc.append("\n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n"); try (Stream files = Files.walk(rootFolder)) { List testsFolders = files .filter(Files::isDirectory) .filter(p -> p.getFileName().toString().equals("tests")) .collect(Collectors.toList()); for (Path testsFolder: testsFolders) { exploreTests(testsFolder.toFile(), testDoc); } } catch (IndexOutOfBoundsException e) { throw new ConfigurationException("no 'tests' sub-package found"); } testDoc.append("
ClasseTestDescriptionDetailsAttributes
\n"); javadoc.append(testDoc); javadoc.append("
"); // store usage data System.out.println(String.format("Number of tests: %d", tests.size())); System.out.println(String.format("Searched elements: %d", searchedElements)); System.out.println(String.format("Test steps: %d", steps.size())); System.out.println(String.format("Mean elements/steps: %.1f\n", searchedElements * 1.0 / steps.size())); int usedSteps = 0; Map stepReuse = new HashMap<>(); for (List stepsFromTest: stepsUsedInTests.values()) { for (String step: stepsFromTest) { if (steps.contains(step)) { stepReuse.put(step, stepReuse.getOrDefault(step, 0) + 1); usedSteps++; } } } System.out.println(String.format("Steps reuse percentage: %.2f", usedSteps * 1.0 / stepReuse.size())); javadoc.append("

Statistics

\n"); javadoc.append("\n" + " \n" + " \n" + String.format(" \n", tests.size()) + " \n" + " \n" + " \n" + String.format(" \n", searchedElements) + " \n" + " \n" + " \n" + String.format(" \n", steps.size()) + " \n" + " \n" + " \n" + String.format(" \n", searchedElements * 1.0 / steps.size()) + " \n" + " \n" + " \n" + String.format(" \n", usedSteps * 1.0 / stepReuse.size()) + " \n" + "
Nombre de tests%d
Elements recherchés%d
Nombre de steps%d
Moyenne elements/steps%.1f
Taux de réutilisation des steps%.1f
" ); /*for (String step :steps) { if (!stepReuse.containsKey(step)) { System.out.println(step); } } System.out.println(new JSONObject(stepsUsedInTests).toString(2));*/ FileUtils.write(Paths.get(args[0], "src/site/confluence/template.confluence").toFile(), javadoc, StandardCharsets.UTF_8); FileUtils.write(Paths.get(args[0], "src/site/confluence/template.xhtml").toFile(), javadoc, StandardCharsets.UTF_8); } private static StringBuilder exploreTests(File srcDir, StringBuilder testDoc) throws IOException { try (Stream files = Files.walk(Paths.get(srcDir.getAbsolutePath()))){ files.filter(Files::isRegularFile) .filter(p -> p.getFileName().toString().endsWith(".java")) .forEach(t -> { try { parseTest(t, testDoc); } catch (FileNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } }); } return testDoc; } private static void parseTest(Path path, StringBuilder testDoc) throws FileNotFoundException { FileInputStream in = new FileInputStream(path.toAbsolutePath().toString()); // parse the file ParseResult cu = new JavaParser().parse(in); // prints the resulting compilation unit to default system output ClassVisitor classVisitor = new ClassVisitor(); cu.getResult().get().accept(classVisitor, "Tests"); TestMethodVisitor methodVisitor = new TestMethodVisitor(); cu.getResult().get().accept(methodVisitor, null); int i = 0; for (Entry testEntry: methodVisitor.getMethodInfos().entrySet()) { testDoc.append("\n"); if (i == 0) { testDoc.append(String.format(" %s\n", methodVisitor.methodInfos.size(), classVisitor.getClassName())); } testDoc.append(String.format(" %s\n", testEntry.getKey().toString().split("\\.")[1]) + String.format(" %s\n", testEntry.getValue().trim()) ); testDoc.append(String.format(" %s\n", String.join(", ", methodVisitor.getStepsInScenario().get(testEntry.getKey())))); Map methodAttributes = methodVisitor.getTestAttributes().get(testEntry.getKey()); testDoc.append(String.format(" %s\n", methodAttributes.toString())); testDoc.append("\n"); i += 1; } stepsUsedInTests.putAll(methodVisitor.getStepsInScenario()); } private static void explorePages(File srcDir, StringBuilder pagesDoc) throws IOException { try (Stream files = Files.walk(Paths.get(srcDir.getAbsolutePath()))) { files.filter(Files::isRegularFile) .filter(p -> p.getFileName().toString().endsWith(".java")) .forEach(t -> { try { parseWebPage(t, pagesDoc); } catch (FileNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } }); } } private static void parseWebPage(Path path, StringBuilder pagesDoc) throws FileNotFoundException { pagesDoc.append(String.format("\n

Page: %s

\n", path.getFileName().toString())); FileInputStream in = new FileInputStream(path.toAbsolutePath().toString()); // parse the file ParseResult cu = new JavaParser().parse(in); // prints the resulting compilation unit to default system output cu.getResult().get().accept(new ClassVisitor(), "Pages"); WebPageMethodVisitor methodVisitor = new WebPageMethodVisitor(); cu.getResult().get().accept(methodVisitor, null); for (Entry pageEntry: methodVisitor.getMethodInfo().entrySet()) { pagesDoc.append(String.format("\n

Operation: %s

\n", pageEntry.getKey())); pagesDoc.append(pageEntry.getValue()); } } private static class TestAttributeVisitor extends VoidVisitorAdapter { Map attributes = new HashMap<>(); @Override public void visit(NormalAnnotationExpr n, Void arg) { for (MemberValuePair pair: n.getPairs()) { if ("attributes".equals(pair.getNameAsString())) { TestCustomAttributeVisitor taVisitor = new TestCustomAttributeVisitor(); pair.accept(taVisitor, null); attributes = taVisitor.getAttribute(); } } } public Map getAttributes() { return attributes; } } /** * Class for reading CustomAttributes * Returning all the attributes as a map * */ private static class TestCustomAttributeVisitor extends VoidVisitorAdapter { Map attribute = new HashMap<>(); @Override public void visit(NormalAnnotationExpr n, Void arg) { String name = null; String value = null; for (MemberValuePair pair: n.getPairs()) { if ("name".equals(pair.getNameAsString())) { name = pair.getValue().toString(); } else if ("values".equals(pair.getNameAsString())) { value = pair.getValue().toString(); } } if (name != null && value != null) { attribute.put(name, value); } } public Map getAttribute() { return attribute; } } private static class TestMethodVisitor extends VoidVisitorAdapter { private Map methodInfos = new HashMap<>(); private Map> testAttributes = new HashMap<>(); private Map> stepsInScenario = new HashMap<>(); private List findAllMethodCalls(Node instruction) { final List found = new ArrayList<>(); instruction.walk(TreeTraversal.BREADTHFIRST, node -> { if (MethodCallExpr.class.isAssignableFrom(node.getClass())) { found.add(MethodCallExpr.class.cast(node)); } }); Collections.reverse(found); return found; } @Override public void visit(MethodDeclaration n, Void arg) { // read all method calls so that we can correlate with webpages String methodId; try { Optional optBody = n.getBody(); Optional optParentNode = n.getParentNode(); if (optBody.isPresent() && optParentNode.isPresent()) { BlockStmt body = optBody.get(); methodId = ((ClassOrInterfaceDeclaration)(optParentNode.get())).getNameAsString() + "." + n.getNameAsString(); stepsInScenario.put(methodId, new ArrayList<>()); for (Node instruction: body.getChildNodes()) { for (MethodCallExpr methodCall: findAllMethodCalls(instruction)) { String methodName = methodCall.getNameAsString(); if (steps.contains(methodName)) { stepsInScenario.get(methodId).add(methodName); } } } } else { return; } } catch (NoSuchElementException | ClassCastException e) { // we expect that 'n' is a method, and its parent is the class itself. We do not support, for example enumeration return; } // ignore non test methods Optional optAnnotation = n.getAnnotationByClass(Test.class); if (!optAnnotation.isPresent()) { return; } // search for attributes TestAttributeVisitor taVisitor = new TestAttributeVisitor(); n.accept(taVisitor, null); testAttributes.put(methodId, taVisitor.getAttributes()); tests.add(methodId); String methodDoc = ""; Optional optComment = n.getComment(); if (optComment.isPresent()) { Comment comment = optComment.get(); methodDoc = formatJavadoc(comment.getContent()); } methodInfos.put(methodId, methodDoc); } public Map getMethodInfos() { return methodInfos; } public Map> getStepsInScenario() { return stepsInScenario; } public Map> getTestAttributes() { return testAttributes; } } private static class WebPageMethodVisitor extends VoidVisitorAdapter { private Map methodInfo = new HashMap<>(); @Override public void visit(MethodDeclaration n, Void arg) { // only display public methods if (!n.getModifiers().contains(Modifier.publicModifier())) { return; } steps.add(n.getNameAsString()); String methodJavaDoc = ""; Optional optComment = n.getComment(); if (optComment.isPresent()) { Comment comment = optComment.get(); methodJavaDoc = formatJavadoc(comment.getContent()); } methodInfo.put(n.getNameAsString(), methodJavaDoc); } public Map getMethodInfo() { return methodInfo; } } private static class ClassVisitor extends VoidVisitorAdapter { private String classDoc; private String className; @Override public void visit(ClassOrInterfaceDeclaration n, String objectType) { Optional optComment = n.getComment(); className = n.getNameAsString(); if (optComment.isPresent()) { Comment comment = optComment.get(); classDoc = String.format("%s", formatJavadoc(comment.getContent())); } else { classDoc = String.format("%s de la classe %s", objectType, n.getNameAsString()); } if ("Pages".equals(objectType)) { for (ObjectCreationExpr field: n.findAll(ObjectCreationExpr.class)) { if (field.getType().getNameAsString().contains("Element")) { searchedElements++; } } searchedElements += n.findAll(MethodCallExpr.class).stream().filter(m -> m.getNameAsString().startsWith("findElement")).collect(Collectors.toList()).size(); } } public String getClassDoc() { return classDoc; } public String getClassName() { return className; } } /** * Remove the star in front of each line * @return */ private static String formatJavadoc(String javadoc) { StringBuilder out = new StringBuilder(); for (String line: javadoc.split("\n")) { line = line.trim(); if (line.startsWith("*")) { line = line.substring(1).trim(); } if (line.startsWith("@")) { line = String.format("
%s
", StringUtility.encodeString(line, "html")); } if (line.contains("@throws")) { continue; } out.append(line + "\n"); } return out.toString(); } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy