commit 8f452e2fd4eae3b61c71a3fc198a01cf9d78fc31
Author: Pavel Fidranský <fidransky@outlook.com>
Date:   Sat Aug 18 20:41:04 2018 +0200

    added Selenium-based UI tests created as part of the ASWI project

diff --git a/sources/src/test/java/cz/zcu/kiv/imiger/tests/backend/GraphFilterTest.java b/sources/src/test/java/cz/zcu/kiv/imiger/tests/backend/GraphFilterTest.java
new file mode 100644
index 0000000..85b7a90
--- /dev/null
+++ b/sources/src/test/java/cz/zcu/kiv/imiger/tests/backend/GraphFilterTest.java
@@ -0,0 +1,235 @@
+package cz.zcu.kiv.imiger.tests.backend;
+
+import cz.zcu.kiv.offscreen.api.EdgeInterface;
+import cz.zcu.kiv.offscreen.api.GraphInterface;
+import cz.zcu.kiv.offscreen.graph.EdgeImpl;
+import cz.zcu.kiv.offscreen.graph.GraphManager;
+import cz.zcu.kiv.offscreen.graph.SubedgeInfo;
+import cz.zcu.kiv.offscreen.graph.loader.GraphJSONDataLoader;
+import cz.zcu.kiv.offscreen.graph.loader.JSONConfigLoader;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+public class GraphFilterTest {
+
+    private static String testDirectory;
+
+    private static GraphManager graphManager;
+
+    @BeforeAll
+    static void initTest() {
+        testDirectory = System.getProperty("user.dir") + "\\..\\test";
+
+        File graphFile = new File(testDirectory + "\\data\\test1.json");
+
+        try {
+            graphManager = new GraphJSONDataLoader(graphFile).LoadData();
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+    }
+
+    @Test
+    void testFilter1a() {
+        GraphInterface graph = createGraph("test1a.json");
+
+        List<String> vertexIds = new ArrayList<>();
+        vertexIds.add("32");
+        vertexIds.add("9");
+        vertexIds.add("1");
+        vertexIds.add("2");
+
+        List<EdgeInterface> edges = new ArrayList<>();
+
+        List<SubedgeInfo> subedgeInfos = new ArrayList<>();
+        subedgeInfos.add(createTestSubEdge(117));
+        edges.add(createTestEdge("1", "32", subedgeInfos));
+
+        subedgeInfos = new ArrayList<>();
+        subedgeInfos.add(createTestSubEdge(10));
+        edges.add(createTestEdge("1", "9", subedgeInfos));
+
+        subedgeInfos = new ArrayList<>();
+        subedgeInfos.add(createTestSubEdge(8));
+        edges.add(createTestEdge("2", "9", subedgeInfos));
+
+        graphContains(graph, vertexIds, edges);
+    }
+
+    @Test
+    void testFilter1b() {
+        GraphInterface graph = createGraph("test1b.json");
+
+        List<String> vertexIds = new ArrayList<>();
+        vertexIds.add("2");
+        vertexIds.add("9");
+        vertexIds.add("18");
+
+        List<EdgeInterface> edges = new ArrayList<>();
+
+        List<SubedgeInfo> subedgeInfos = new ArrayList<>();
+        subedgeInfos.add(createTestSubEdge(9));
+        edges.add(createTestEdge("9", "2", subedgeInfos));
+
+        subedgeInfos = new ArrayList<>();
+        subedgeInfos.add(createTestSubEdge(86));
+        edges.add(createTestEdge("18", "2", subedgeInfos));
+
+        graphContains(graph, vertexIds, edges);
+    }
+
+    @Test
+    void testFilter1c() {
+        GraphInterface graph = createGraph("test1c.json");
+
+        List<String> vertexIds = new ArrayList<>();
+        vertexIds.add("2");
+        vertexIds.add("18");
+        vertexIds.add("32");
+
+        List<EdgeInterface> edges = new ArrayList<>();
+
+        List<SubedgeInfo> subedgeInfos = new ArrayList<>();
+        subedgeInfos.add(createTestSubEdge(79));
+        edges.add(createTestEdge("2", "18", subedgeInfos));
+
+        subedgeInfos = new ArrayList<>();
+        subedgeInfos.add(createTestSubEdge(86));
+        edges.add(createTestEdge("18", "2", subedgeInfos));
+
+        graphContains(graph, vertexIds, edges);
+    }
+
+    @Test
+    void testFilter1d() {
+        GraphInterface graph = createGraph("test1d.json");
+
+        List<String> vertexIds = new ArrayList<>();
+        vertexIds.add("1");
+        vertexIds.add("9");
+        vertexIds.add("32");
+
+        List<EdgeInterface> edges = new ArrayList<>();
+
+        List<SubedgeInfo> subedgeInfos = new ArrayList<>();
+        subedgeInfos.add(createTestSubEdge(10));
+        edges.add(createTestEdge("1", "9", subedgeInfos));
+
+        subedgeInfos = new ArrayList<>();
+        subedgeInfos.add(createTestSubEdge(117));
+        edges.add(createTestEdge("1", "32", subedgeInfos));
+
+        graphContains(graph, vertexIds, edges);
+    }
+
+    @Test
+    void testFilter1e() {
+        GraphInterface graph = createGraph("test1e.json");
+
+        List<String> vertexIds = new ArrayList<>();
+        vertexIds.add("1");
+        vertexIds.add("32");
+        vertexIds.add("9");
+
+        List<EdgeInterface> edges = new ArrayList<>();
+
+        List<SubedgeInfo> subedgeInfos = new ArrayList<>();
+        subedgeInfos.add(createTestSubEdge(11));
+        edges.add(createTestEdge("9", "1", subedgeInfos));
+
+        subedgeInfos = new ArrayList<>();
+        subedgeInfos.add(createTestSubEdge(117));
+        edges.add(createTestEdge("1", "32", subedgeInfos));
+
+        graphContains(graph, vertexIds, edges);
+    }
+
+    @Test
+    void testFilter1f() {
+        GraphInterface graph = createGraph("test1f.json");
+
+        List<String> vertexIds = new ArrayList<>();
+        vertexIds.add("1");
+        vertexIds.add("2");
+        vertexIds.add("18");
+
+        List<EdgeInterface> edges = new ArrayList<>();
+
+        List<SubedgeInfo> subedgeInfos = new ArrayList<>();
+        subedgeInfos.add(createTestSubEdge(86));
+        edges.add(createTestEdge("18", "2", subedgeInfos));
+
+        graphContains(graph, vertexIds, edges);
+    }
+
+    private GraphInterface createGraph(String configFilename) {
+        String configLocation = testDirectory + "\\config";
+        JSONConfigLoader configLoader = new JSONConfigLoader(graphManager, configLocation);
+        JSONConfigLoader.configFilename = configFilename;
+        return graphManager.createGraph(configLoader);
+    }
+
+    private SubedgeInfo createTestSubEdge(int id) {
+        SubedgeInfo subedge = new SubedgeInfo(0, null);
+        subedge.id = id;
+
+        return subedge;
+    }
+
+    private EdgeInterface createTestEdge(String from, String to, List<SubedgeInfo> subedges) {
+        EdgeInterface edge = new EdgeImpl(0, from, to, false, "");
+        edge.setSubedgeInfo(subedges);
+
+        return edge;
+    }
+
+    /**
+     *
+     * @param graph - Graph object to test.
+     * @param vertexIds - vertices, that graph must contain in order to pass the test.
+     * @param edges - edges, that graph must contain in order to pass the test.
+     */
+    private void graphContains(GraphInterface graph, List<String> vertexIds, List<EdgeInterface> edges) {
+        Assertions.assertEquals(graph.getVertices().size(), vertexIds.size());
+        Assertions.assertEquals(graph.getEdges().size(), edges.size());
+
+        // Vertices
+        for (String vertexId : graph.getVertices().keySet()) {
+            Assertions.assertTrue(vertexIds.contains(vertexId));
+        }
+
+        // Edges
+        for (EdgeInterface edge : graph.getEdges()) {
+            Assertions.assertTrue(edges.contains(edge));
+
+            EdgeInterface edgeCmp = edges.get(edges.indexOf(edge));
+            Assertions.assertEquals(edge.getSubedgeInfo().size(), edgeCmp.getSubedgeInfo().size());
+
+            for (SubedgeInfo subedgeInfo : edge.getSubedgeInfo()) {
+                Assertions.assertTrue(containsSubedgeWithId(subedgeInfo, edgeCmp.getSubedgeInfo()));
+            }
+        }
+    }
+
+    /**
+     * Checks if list of subedges contains subedge with id.
+     * @param subedgeInfo - Subedge from graph to compare.
+     * @param subedgeInfos - List of subedges that the graph must contain.
+     * @return true if subedges that should be present contain id of subedge.
+     */
+    private boolean containsSubedgeWithId(SubedgeInfo subedgeInfo, List<SubedgeInfo> subedgeInfos) {
+        for (SubedgeInfo s : subedgeInfos) {
+            if (s.id == subedgeInfo.id) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+}
diff --git a/sources/src/test/java/cz/zcu/kiv/imiger/tests/frontend/ArchetypeIconConfigTest.java b/sources/src/test/java/cz/zcu/kiv/imiger/tests/frontend/ArchetypeIconConfigTest.java
new file mode 100644
index 0000000..27b66aa
--- /dev/null
+++ b/sources/src/test/java/cz/zcu/kiv/imiger/tests/frontend/ArchetypeIconConfigTest.java
@@ -0,0 +1,95 @@
+package cz.zcu.kiv.imiger.tests.frontend;
+
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+import org.openqa.selenium.By;
+import org.openqa.selenium.NoSuchElementException;
+import org.openqa.selenium.WebDriver;
+import org.openqa.selenium.WebElement;
+import org.opentest4j.AssertionFailedError;
+
+public class ArchetypeIconConfigTest {
+    static WebDriver browser;
+
+    @BeforeAll
+    public static void initTest() {
+        SeleniumUtil.prepareConfigFile("archetypeIconConfigTest.json");
+        browser = SeleniumUtil.init();
+        SeleniumUtil.loadGraphData("archetypeIconTest.json");
+    }
+
+    @Test
+    public void SpecifiedIcon1(){
+        locateTestingArchetypeIcon("vertex1");
+    }
+
+    @Test
+    public void SpecifiedIcon2(){
+        Assertions.assertThrows(NoSuchElementException.class,
+                () -> checkUnspecifiedArchetypeSymbol("vertex1", "S"));
+
+        System.out.println("Default value not found on the vertex with specified archetype icon.");
+    }
+
+    @Test
+    public void UnspecifiedIcon1(){
+        Assertions.assertThrows(NoSuchElementException.class,
+                () -> locateTestingArchetypeIcon("vertex2"));
+
+        System.out.println("Element not found.");
+    }
+
+    @Test
+    public void UnspecifiedIcon2(){
+        Assertions.assertThrows(NoSuchElementException.class,
+                () -> locateTestingArchetypeIcon("vertex3"));
+
+        System.out.println("Element not found.");
+    }
+
+    @Test
+    public void UnspecifiedIcon3(){
+        checkUnspecifiedArchetypeSymbol("vertex2", "U");
+    }
+
+    @Test
+    public void UnspecifiedIcon4(){
+        checkUnspecifiedArchetypeSymbol("vertex3", "☺");
+    }
+
+    /**
+     * Utility method for checking of the presence of the specified icon element
+     * @param elementId Id of the element in which the search is done
+     */
+    private void locateTestingArchetypeIcon(String elementId) {
+        WebElement icon = browser.findElement(By.id(elementId))
+                .findElement(By.className("archetype"));
+
+        System.out.println("Trying to locate an element with class 'testingArchetypeIcon'");
+        icon.findElement(By.className("testingArchetypeIcon"));
+        System.out.println("Element found.");
+    }
+
+    /**
+     * Utility method for checking whether specified element contains the default archetype icon
+     * @param elementId Id of the element in which the test is done
+     * @param expectedText Expected text present in the icon
+     */
+    private void checkUnspecifiedArchetypeSymbol(String elementId, String expectedText) {
+        WebElement icon = browser.findElement(By.id(elementId))
+                .findElement(By.className("archetype"));
+
+        System.out.println("Checking text of unspecified archetype icon");
+        Assertions.assertEquals(expectedText, icon.findElement(By.tagName("text")).getText(),
+                "Text of unspecified icon is different from the expected.");
+        System.out.println("Default text found.");
+    }
+
+
+    @AfterAll
+    public static void finishTest() {
+        SeleniumUtil.clear();
+    }
+}
diff --git a/sources/src/test/java/cz/zcu/kiv/imiger/tests/frontend/AssociatedArchetypeTest.java b/sources/src/test/java/cz/zcu/kiv/imiger/tests/frontend/AssociatedArchetypeTest.java
new file mode 100644
index 0000000..e714727
--- /dev/null
+++ b/sources/src/test/java/cz/zcu/kiv/imiger/tests/frontend/AssociatedArchetypeTest.java
@@ -0,0 +1,114 @@
+package cz.zcu.kiv.imiger.tests.frontend;
+
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+import org.openqa.selenium.By;
+import org.openqa.selenium.NoSuchElementException;
+import org.openqa.selenium.WebDriver;
+import org.openqa.selenium.WebElement;
+
+import java.util.Arrays;
+import java.util.List;
+
+public class AssociatedArchetypeTest {
+    static WebDriver browser;
+
+    @BeforeAll
+    public static void initTest() {
+        SeleniumUtil.prepareConfigFile("emptyConfig.json");
+        browser = SeleniumUtil.init();
+        SeleniumUtil.loadGraphData("associatedArchetypeTest.json");
+    }
+
+    @Test
+    public void visibilityOfAllIcons() {
+        checkVisibilityOfIcons("vertex1", Arrays.asList("2", "4"));
+
+        checkVisibilityOfIcons("vertex2",Arrays.asList("1"));
+
+        checkVisibilityOfIcons("vertex3",Arrays.asList("1"));
+
+        checkVisibilityOfIcons("vertex4",Arrays.asList("1", "3"));
+
+        checkVisibilityOfIcons("vertex5",Arrays.asList("4"));
+    }
+
+    @Test
+    public void vertexHighlighting() {
+        //associated vertices to V1
+        checkHighlightedVertices("archetype1_1", Arrays.asList("vertex2", "vertex3"));
+        checkHighlightedVertices("archetype1_3", Arrays.asList("vertex4"));
+
+        //associated vertices to V2
+        checkHighlightedVertices("archetype2_0", Arrays.asList("vertex1"));
+
+        //associated vertices to V3
+        checkHighlightedVertices("archetype3_0", Arrays.asList("vertex1"));
+
+        //associated vertices to V4
+        checkHighlightedVertices("archetype4_0", Arrays.asList("vertex1"));
+        checkHighlightedVertices("archetype4_2", Arrays.asList("vertex5"));
+
+        //associated vertices to V5
+        checkHighlightedVertices("archetype5_3", Arrays.asList("vertex4"));
+    }
+
+    /**
+     * Check if all the expected associated archetype icons are present
+     * @param elementId Id of the vertex in the DOM
+     * @param expectedArchetypeIconTexts Texts of all the associated archetype icons that are expected to occur
+     */
+    private void checkVisibilityOfIcons(String elementId, List<String> expectedArchetypeIconTexts) {
+        System.out.println("Checking element " + elementId);
+        WebElement vertex = browser.findElement(By.id(elementId));
+
+        List<WebElement> associatedArchetypeIcons = vertex.findElements(By.className("associatedArchetype"));
+
+        System.out.println("Associated archetypes: \n");
+        Assertions.assertEquals(associatedArchetypeIcons.size(), expectedArchetypeIconTexts.size());
+
+        for (int i = 0; i < expectedArchetypeIconTexts.size(); i++) {
+            WebElement icon = associatedArchetypeIcons.get(i);
+            System.out.println("Icon id: " + icon.getAttribute("id"));
+
+            String archetypeIconText = icon.findElement(By.tagName("text")).getText();
+            System.out.println("Icon text: " + archetypeIconText +"\n");
+
+            Assertions.assertEquals(expectedArchetypeIconTexts.get(i), archetypeIconText);
+        }
+    }
+
+    /**
+     * Check if all the expected vertices are highlighted when clicking on the associated archetype icon
+     * @param associatedArchetypeIconId Id of the associated archetype icon in the DOM
+     * @param expectedHighlightedVertexIds Ids of all the vertices that are expected to be highlighted
+     */
+    private void checkHighlightedVertices(String associatedArchetypeIconId, List<String> expectedHighlightedVertexIds) {
+        System.out.println("\nChecking associated vertices for archetype icon with id: " + associatedArchetypeIconId);
+        WebElement associatedArchetypeIcon = browser.findElement(By.id(associatedArchetypeIconId));
+        SeleniumUtil.svgClick(associatedArchetypeIcon);
+
+        List<WebElement> highlightedVertices = browser.findElements(By.className("colorHighlightArchetype"));
+
+        System.out.println("Number of highlighted vertices: " + highlightedVertices.size());
+        Assertions.assertEquals(expectedHighlightedVertexIds.size(), highlightedVertices.size());
+
+        for(int i = 0; i < highlightedVertices.size(); i++) {
+            WebElement vertex = highlightedVertices.get(i);
+            String id = vertex.getAttribute("id");
+
+            System.out.println(id);
+
+            Assertions.assertEquals(expectedHighlightedVertexIds.get(i), id);
+        }
+
+        SeleniumUtil.svgClick(associatedArchetypeIcon);
+    }
+
+    @AfterAll
+    public static void finishTest() {
+        SeleniumUtil.clear();
+    }
+}
diff --git a/sources/src/test/java/cz/zcu/kiv/imiger/tests/frontend/AutomaticGroupingTest.java b/sources/src/test/java/cz/zcu/kiv/imiger/tests/frontend/AutomaticGroupingTest.java
new file mode 100644
index 0000000..181e3db
--- /dev/null
+++ b/sources/src/test/java/cz/zcu/kiv/imiger/tests/frontend/AutomaticGroupingTest.java
@@ -0,0 +1,50 @@
+package cz.zcu.kiv.imiger.tests.frontend;
+
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+import org.openqa.selenium.By;
+import org.openqa.selenium.NoSuchElementException;
+import org.openqa.selenium.WebDriver;
+import org.openqa.selenium.WebElement;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class AutomaticGroupingTest {
+    static WebDriver browser;
+
+    @BeforeAll
+    public static void initTest() {
+        SeleniumUtil.prepareConfigFile("automaticGroupingConfigTest.json");
+        browser = SeleniumUtil.init();
+        SeleniumUtil.loadGraphData("automaticGroupingTest.json");
+    }
+
+    @Test
+    public void groups1(){
+        String groupName = "FirstGroupArchetype";
+        List<String> verticesIds = new ArrayList<>();
+        verticesIds.add("li1");
+        verticesIds.add("li3");
+
+        checkGroupExisting(browser, groupName, verticesIds);
+        System.out.println("Group: " + groupName + " was not found.");
+    }
+
+    private void checkGroupExisting(WebDriver browser, String groupName, List<String> verticesIds){
+        WebElement group = browser.findElement(By.className("component group_vertices"));
+        System.out.println("Trying to locate vertexes in group");
+        for(String id : verticesIds){
+            group.findElement(By.id(id));
+        }
+
+        System.out.println("Vertexes found.");
+    }
+
+    @AfterAll
+    public static void finishTest() {
+        SeleniumUtil.clear();
+    }
+}
diff --git a/sources/src/test/java/cz/zcu/kiv/imiger/tests/frontend/DetailsTest.java b/sources/src/test/java/cz/zcu/kiv/imiger/tests/frontend/DetailsTest.java
new file mode 100644
index 0000000..2e58c7e
--- /dev/null
+++ b/sources/src/test/java/cz/zcu/kiv/imiger/tests/frontend/DetailsTest.java
@@ -0,0 +1,119 @@
+package cz.zcu.kiv.imiger.tests.frontend;
+
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+import org.openqa.selenium.By;
+import org.openqa.selenium.WebDriver;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.interactions.Actions;
+
+import java.util.Arrays;
+import java.util.List;
+
+public class DetailsTest {
+
+    static WebDriver browser;
+
+    public static final String VISIBLE_Q_TIP_XPATH = "//div[contains(@class, 'qtip') and @aria-hidden='false']";
+
+    @BeforeAll
+    public static void initTest() {
+        SeleniumUtil.prepareConfigFile("emptyConfig.json");
+        browser = SeleniumUtil.init();
+        SeleniumUtil.loadGraphData("detailTest.json");
+    }
+
+    @Test
+    public void vertexDetail(){
+        checkQTip(
+                vertexQTip("vertex1"),
+                "V1 (3)",
+                Arrays.asList("D: E", "B: W", "A: Q")
+        );
+
+        checkQTip(
+                vertexQTip("vertex2"),
+                "V2 (2)",
+                Arrays.asList("D: E", "C: Q", "B: W")
+        );
+
+        checkQTip(
+                vertexQTip("vertex3"),
+                "V3 (1)",
+                Arrays.asList("D: Q", "C: W", "A: E")
+        );
+    }
+
+    @Test
+    public void edgeDetail() {
+        checkQTip(
+                edgeQTip("e1"),
+                "Edge Details",
+                Arrays.asList(
+                        "edgeArchetype1", "D : E", "C : W"
+                )
+        );
+
+        checkQTip(
+                edgeQTip("e2"),
+                "Edge Details",
+                Arrays.asList(
+                        "edgeArchetype1", "D : E", "B : W", "A : Q",
+                        "edgeArchetype2", "D : D", "B : S", "A : A"
+                )
+        );
+
+        checkQTip(
+                edgeQTip("e3"),
+                "Edge Details",
+                Arrays.asList(
+                        "edgeArchetype2", "C : T", "B : R"
+                )
+        );
+    }
+
+
+    private WebElement edgeQTip(String edgeElementId) {
+        WebElement arrow = browser.findElement(By.id(edgeElementId))
+                .findElement(By.className("arrow"));
+
+        return getQTipElement(arrow);
+    }
+
+    private WebElement vertexQTip(String vertexElementId) {
+        WebElement archetypeIcon = browser.findElement(By.id(vertexElementId))
+                .findElement(By.className("archetype"));
+
+        return getQTipElement(archetypeIcon);
+    }
+
+    private WebElement getQTipElement(WebElement toClick) {
+        SeleniumUtil.svgClick(toClick);
+
+        return browser.findElement(By.xpath(VISIBLE_Q_TIP_XPATH));
+    }
+
+    private void checkQTip(WebElement qTipElement, String expectedTitle, List<String> expectedAttributes) {
+        QTip qtip = new QTip(qTipElement);
+
+        System.out.println("Title: " + qtip.getTitle());
+        Assertions.assertEquals(expectedTitle, qtip.getTitle());
+
+        List<String> attributes = qtip.getAttributes();
+        System.out.println("Attributes:");
+        for (String attr : attributes) {
+            System.out.println(attr);
+        }
+        Assertions.assertEquals(expectedAttributes, attributes);
+
+        qtip.close();
+        Assertions.assertFalse(qtip.isVisible());
+    }
+
+    @AfterAll
+    public static void finishTest() {
+        SeleniumUtil.clear();
+    }
+}
diff --git a/sources/src/test/java/cz/zcu/kiv/imiger/tests/frontend/GroupStatsChangingTest.java b/sources/src/test/java/cz/zcu/kiv/imiger/tests/frontend/GroupStatsChangingTest.java
new file mode 100644
index 0000000..958a209
--- /dev/null
+++ b/sources/src/test/java/cz/zcu/kiv/imiger/tests/frontend/GroupStatsChangingTest.java
@@ -0,0 +1,151 @@
+package cz.zcu.kiv.imiger.tests.frontend;
+
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+import org.openqa.selenium.By;
+import org.openqa.selenium.WebDriver;
+import org.openqa.selenium.WebElement;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+public class GroupStatsChangingTest {
+    static WebDriver browser;
+
+    @BeforeAll
+    public static void initTest() {
+        SeleniumUtil.prepareConfigFile("emptyConfig.json");
+        browser = SeleniumUtil.init();
+        SeleniumUtil.loadGraphData("associatedArchetypeTest.json");
+        for(int i = 0; i<=5;i++)
+            SeleniumUtil.svgClick(browser.findElement(By.id("zoomOut")));
+    }
+
+    @Test
+    public void ChangingStats() {
+        String groupId = "";
+        switchExludeMode();
+        WebElement firstEcludedVertex = browser.findElement(By.id("vertex3"));
+
+        SeleniumUtil.svgClick(firstEcludedVertex);
+        System.out.println("Exclude vertex3 to group as first vertex of group");
+
+        excludeToExistingGroup("vertex2");
+        System.out.println("Exclude vertex2 to group");
+
+        //get data-id of fgroup
+        groupId = browser.findElement(By.className("group_vertices")).getAttribute("data-id");
+
+        compareExpectedValues(groupId, new ArrayList<String>(Arrays.asList("2","0","0","0")));
+
+        excludeToExistingGroup("vertex1");
+        compareExpectedValues(groupId, new ArrayList<String>(Arrays.asList("2","2","0","2")));
+        System.out.println("Exclude vertex1 to group");
+
+        System.out.println("Remove vertex2 to group");
+        removeVertexFromGroup("li2");
+        compareExpectedValues(groupId, new ArrayList<String>(Arrays.asList("1","2","0","2")));
+
+        System.out.println("Switch off stats for vertex1");
+        toogleVertexInGroup("li1");
+        compareExpectedValues(groupId, new ArrayList<String>(Arrays.asList("1","0","0","0")));
+
+        System.out.println("Switch off stats for vertex3");
+        toogleVertexInGroup("li3");
+        compareExpectedValues(groupId, new ArrayList<String>(Arrays.asList("0","0","0","0")));
+
+        System.out.println("Switch on stats for vertex1");
+        toogleVertexInGroup("li3");
+        excludeToExistingGroup("vertex2");
+        compareExpectedValues(groupId, new ArrayList<String>(Arrays.asList("2","0","0","0")));
+
+        //finaly test just one vertex in group
+        String archetypePrefix = "v_archetype_4_";
+        switchExludeMode();
+        WebElement singleEcludedVertex = browser.findElement(By.id("vertex4"));
+        System.out.println("Exclude vertex4 to group as first vertex of group");
+        SeleniumUtil.svgClick(singleEcludedVertex);
+
+        System.out.println("Checking group statistics");
+        String actualValue1 = browser.findElement(By.id(archetypePrefix + "0")).findElement(By.tagName("text")).getText();
+        String actualValue2 = browser.findElement(By.id(archetypePrefix + "2")).findElement(By.tagName("text")).getText();
+        Assertions.assertEquals(actualValue1, "2");
+        Assertions.assertEquals(actualValue2, "1");
+
+        System.out.println("Expected: 2 - Real: " + actualValue1);
+        System.out.println("Expected: 1 - Real: " + actualValue2);
+
+    }
+
+    /**
+     * Turn on exclude mode for vertex exclude
+     */
+    private void switchExludeMode(){
+        browser.findElement(By.id("remove")).click();
+        try {
+            Thread.sleep(1000);
+        } catch (InterruptedException e) {
+            e.printStackTrace();
+        }
+    }
+
+    /**
+     * Exclude vertex from graph to group
+     * @param vertexId id of vertex to exclude
+     */
+    private void excludeToExistingGroup(String vertexId){
+        WebElement secondExcludedVertex = browser.findElement(By.id(vertexId));
+        SeleniumUtil.svgContexClick(secondExcludedVertex);
+        WebElement selectedGroup = browser.findElement(By.className("component_color"));
+        SeleniumUtil.svgClick(selectedGroup);
+    }
+
+    /**
+     * Remove vertex from group back to graph
+     * @param vertexId id of vertex
+     */
+    private void removeVertexFromGroup(String vertexId){
+        WebElement removeVertex = browser.findElement(By.id(vertexId)).findElement(By.className("deleteItemGroup"));
+        SeleniumUtil.svgClick(removeVertex);
+    }
+
+    /**
+     * Toogle stats for vertex
+     * @param vertexId id of vertex
+     */
+    private void toogleVertexInGroup(String vertexId){
+        WebElement removeVertex = browser.findElement(By.id(vertexId)).findElement(By.className("toggleStat"));
+        SeleniumUtil.svgClick(removeVertex);
+    }
+
+    /**
+     * comparison of real values with expected values
+     * @param groupId id of group
+     * @param expectedValues list of expected values
+     */
+    private void compareExpectedValues(String groupId, ArrayList<String> expectedValues){
+        System.out.println("Checking group statistics");
+        String archetypePrefix = "g_archetype_" + groupId + "_";
+
+        List<String> actualValues = new ArrayList<String>();
+
+        for(int i = 0; i< expectedValues.size(); i++) {
+            String archetypePos = i+"";
+            String actualValue = browser.findElement(By.id(archetypePrefix + archetypePos)).findElement(By.tagName("text")).getText();
+            actualValues.add(actualValue);
+        }
+
+        for(int i = 0; i< expectedValues.size(); i++) {
+            Assertions.assertEquals(actualValues.get(i), expectedValues.get(i));
+            System.out.println("Expected: " + expectedValues.get(i) + " - Real: " + actualValues.get(i));
+        }
+    }
+
+    @AfterAll
+    public static void finishTest() {
+        SeleniumUtil.clear();
+    }
+}
diff --git a/sources/src/test/java/cz/zcu/kiv/imiger/tests/frontend/QTip.java b/sources/src/test/java/cz/zcu/kiv/imiger/tests/frontend/QTip.java
new file mode 100644
index 0000000..9f545e1
--- /dev/null
+++ b/sources/src/test/java/cz/zcu/kiv/imiger/tests/frontend/QTip.java
@@ -0,0 +1,38 @@
+package cz.zcu.kiv.imiger.tests.frontend;
+
+import org.openqa.selenium.By;
+import org.openqa.selenium.WebElement;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class QTip {
+    private WebElement qtip;
+
+    public QTip(WebElement qtip) {
+        this.qtip = qtip;
+    }
+
+    public boolean isVisible() {
+        return qtip.isDisplayed();
+    }
+
+    public String getTitle() {
+        return qtip.findElement(By.className("qtip-title")).getText();
+    }
+
+    public List<String> getAttributes() {
+        List<WebElement> attributes = qtip.findElements(By.tagName("li"));
+        List<String> result = new ArrayList<String>();
+
+        for (WebElement attribute : attributes) {
+            result.add(attribute.getText());
+        }
+
+        return result;
+    }
+
+    public void close() {
+        qtip.findElement(By.className("qtip-close")).click();
+    }
+}
diff --git a/sources/src/test/java/cz/zcu/kiv/imiger/tests/frontend/SampleTest.java b/sources/src/test/java/cz/zcu/kiv/imiger/tests/frontend/SampleTest.java
new file mode 100644
index 0000000..ba6272d
--- /dev/null
+++ b/sources/src/test/java/cz/zcu/kiv/imiger/tests/frontend/SampleTest.java
@@ -0,0 +1,40 @@
+package cz.zcu.kiv.imiger.tests.frontend;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+import org.openqa.selenium.By;
+import org.openqa.selenium.WebDriver;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.firefox.FirefoxDriver;
+import org.openqa.selenium.support.ui.ExpectedConditions;
+import org.openqa.selenium.support.ui.WebDriverWait;
+
+public class SampleTest {
+    static WebDriver browser;
+
+    @BeforeAll
+    public static void initTest() {
+        browser = SeleniumUtil.init();
+    }
+    @Test
+    public void Test(){
+        WebElement fileInput = browser.findElement(By.id("hidden_input"));
+        fileInput.sendKeys(System.getProperty("user.dir") + "\\..\\data\\aswi2017falsum.json" );
+        browser.findElement(By.className("load")).click();
+
+        WebDriverWait wait = new WebDriverWait(browser, 20);
+        wait.until(ExpectedConditions.presenceOfElementLocated(By.className("vertices")));
+
+        System.out.println(browser.findElement(By.id("vertex1")).getAttribute("data-id"));
+
+        //Assertions.assertTrue(header.getText().equals("Interactive Multimodal Graph Explorer"));
+    }
+
+    @AfterAll
+    public static void finishTest() {
+        SeleniumUtil.clear();
+    }
+
+}
diff --git a/sources/src/test/java/cz/zcu/kiv/imiger/tests/frontend/SeleniumUtil.java b/sources/src/test/java/cz/zcu/kiv/imiger/tests/frontend/SeleniumUtil.java
new file mode 100644
index 0000000..1640a76
--- /dev/null
+++ b/sources/src/test/java/cz/zcu/kiv/imiger/tests/frontend/SeleniumUtil.java
@@ -0,0 +1,90 @@
+package cz.zcu.kiv.imiger.tests.frontend;
+
+import org.openqa.selenium.By;
+import org.openqa.selenium.WebDriver;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.firefox.FirefoxDriver;
+import org.openqa.selenium.interactions.Actions;
+import org.openqa.selenium.support.ui.ExpectedConditions;
+import org.openqa.selenium.support.ui.WebDriverWait;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+public class SeleniumUtil {
+    /**
+     * Path to the gecko driver.
+     * Please change to your location
+     */
+    private static final String GECKO_PATH = "C:\\aswi-git\\geckodriver.exe";
+
+    private static final String URL = "http://localhost:8080/VisualizationTool";
+
+    private static final int DATA_LOAD_TIMEOUT = 20;
+
+    private static WebDriver browser;
+
+    public static WebDriver init() {
+        System.setProperty("webdriver.gecko.driver", GECKO_PATH);
+        browser = new FirefoxDriver();
+        browser.get(URL);
+        return browser;
+    }
+
+    public static void loadGraphData(String filename) {
+        WebElement fileInput = browser.findElement(By.id("hidden_input"));
+        fileInput.sendKeys(System.getProperty("user.dir") + "\\..\\test\\data\\" + filename );
+        browser.findElement(By.className("load")).click();
+
+        WebDriverWait wait = new WebDriverWait(browser, DATA_LOAD_TIMEOUT);
+        wait.until(ExpectedConditions.presenceOfElementLocated(By.className("vertices")));
+
+        try {
+            Thread.sleep(1000);
+        } catch (InterruptedException e) {
+            e.printStackTrace();
+        }
+    }
+
+    public static void prepareConfigFile(String configFile) {
+        try {
+            Files.deleteIfExists(Paths.get(System.getProperty("user.home") + "\\Documents\\aswi\\test\\config\\config.json"));
+            Files.createLink(Paths.get(System.getProperty("user.home") + "\\Documents\\aswi\\test\\config\\config.json"),
+                    Paths.get(System.getProperty("user.dir") + "\\..\\test\\config\\" + configFile));
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+    }
+
+    public static void svgClick(WebElement toClick) {
+        Actions builder = new Actions(browser);
+        builder.click(toClick).build().perform();
+
+        try {
+            Thread.sleep(1000);
+        } catch (InterruptedException e) {
+            e.printStackTrace();
+        }
+    }
+
+    /**
+     * Right mouse click
+     * @param toClick element for click
+     */
+    public static void svgContexClick(WebElement toClick) {
+        Actions builder = new Actions(browser);
+        builder.contextClick(toClick).build().perform();
+
+        try {
+            Thread.sleep(1000);
+        } catch (InterruptedException e) {
+            e.printStackTrace();
+        }
+    }
+
+    public static void clear() {
+        browser.close();
+    }
+}
