keycloak-aplcache

Merge pull request #3963 from Pepo48/KEYCLOAK-4488 KEYCLOAK-4488

3/24/2017 4:45:48 AM

Details

diff --git a/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/authentication/flows/CreateExecutionForm.java b/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/authentication/flows/CreateExecutionForm.java
index eef0389..5362250 100644
--- a/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/authentication/flows/CreateExecutionForm.java
+++ b/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/authentication/flows/CreateExecutionForm.java
@@ -28,20 +28,30 @@ import org.openqa.selenium.support.ui.Select;
 /**
  *
  * @author <a href="mailto:vramik@redhat.com">Vlastislav Ramik</a>
+ * @author <a href="mailto:pzaoral@redhat.com">Peter Zaoral</a>
  */
 public class CreateExecutionForm extends Form {
     public enum ProviderOption {
-        DIRECT_GRANT_VALIDATE_USERNAME("direct-grant-validate-username"),
-        RESET_OTP("reset-otp"),
-        AUTH_COOKIE("auth-cookie"),
-        RESET_CREDENTIALS_CHOOSE_USER("reset-credentials-choose-user"),
-        DIRECT_GRANT_VALIDATE_PASSWORD("direct-grant-validate-password"),
-        AUTH_USERNAME_PASSWORD_FORM("auth-username-password-form"),
-        AUTH_OTP_FORM("auth-otp-form"),
-        AUTH_SPNEGO("auth-spnego"),
-        DIRECT_GRANT_VALIDATE_OPT("direct-grant-validate-otp"),
-        RESET_CREDENTIALS_EMAIL("reset-credential-email"),
-        RESET_PASSWORD("reset-password");
+        IDENTITY_PROVIDER_REDIRECTOR("Identity Provider Redirector"),
+        USERNAME_VALIDATION("Username Validation"),
+        RESET_OTP("Reset OTP"),
+        COOKIE("Cookie"),
+        CHOOSE_USER("Choose User"),
+        PASSWORD("Password"),
+        REVIEW_PROFILE("Review Profile"),
+        CONFIRM_LINK_EXISTING_ACCOUNT("Confirm Link Existing Account"),
+        CONDITIONAL_OTP("Conditional OTP"),
+        USERNAME_PASSWORD("Username Password"),
+        KERBEROS("Kerberos"),
+        SEND_RESET_EMAIL("Send Reset Email"),
+        RESET_PASSWORD("Reset Password"),
+        HTTP_BASIC_AUTHETICATION("HTTP Basic Authentication"),
+        OTP_FORM("OTP Form"),
+        USERNAME_PASSWORD_FORM_FOR_IDENTITY_PROVIDER_REAUTH("Username Password For Identity Provider Reauthentication"),
+        VERIFY_EXISTING_ACCOUNT_BY_EMAIL("Verify Existing Account By Email"),
+        SCRIPT("Script"),
+        OTP("OTP"),
+        CREATE_USER_IF_UNIQUE("Create User If Unique");
         
         private final String name;
 
diff --git a/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/authentication/flows/Flows.java b/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/authentication/flows/Flows.java
index 63694cc..262a834 100644
--- a/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/authentication/flows/Flows.java
+++ b/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/authentication/flows/Flows.java
@@ -5,9 +5,13 @@ import org.openqa.selenium.WebElement;
 import org.openqa.selenium.support.FindBy;
 import org.openqa.selenium.support.ui.Select;
 
+import java.util.List;
+import java.util.stream.Collectors;
+
 /**
  * @author tkyjovsk
  * @author mhajas
+ * @author pzaoral
  */
 public class Flows extends Authentication {
 
@@ -19,19 +23,19 @@ public class Flows extends Authentication {
     @FindBy(tagName = "select")
     private Select flowSelect;
 
-    @FindBy(xpath = "//button[text() = 'New']")
+    @FindBy(xpath = ".//button[@data-ng-click='createFlow()']")
     private WebElement newButton;
 
-    @FindBy(xpath = "//button[text() = 'Copy']")
+    @FindBy(xpath = ".//button[@data-ng-click='copyFlow()']")
     private WebElement copyButton;
 
-    @FindBy(xpath = "//button[text() = 'Delete']")
+    @FindBy(xpath = ".//button[@data-ng-click='deleteFlow()']")
     private WebElement deleteButton;
 
-    @FindBy(xpath = "//button[text() = 'Add Execution']")
+    @FindBy(xpath = ".//button[@data-ng-click='addExecution()']")
     private WebElement addExecutionButton;
 
-    @FindBy(xpath = "//button[text() = 'Add Flow']")
+    @FindBy(xpath = ".//button[@data-ng-click='addFlow()']")
     private WebElement addFlowButton;
 
     @FindBy(tagName = "table")
@@ -39,10 +43,10 @@ public class Flows extends Authentication {
 
     public enum FlowOption {
 
-        DIRECT_GRANT("Direct grant"), 
+        DIRECT_GRANT("Direct Grant"),
         REGISTRATION("Registration"), 
         BROWSER("Browser"),
-        RESET_CREDENTIALS("Reset credentials"), 
+        RESET_CREDENTIALS("Reset Credentials"),
         CLIENTS("Clients");
 
         private final String name;
@@ -64,6 +68,10 @@ public class Flows extends Authentication {
         return flowSelect.getFirstSelectedOption().getText();
     }
 
+    public List<String> getFlowAllValues() {
+        return flowSelect.getOptions().stream().map(WebElement::getText).collect(Collectors.toList());
+    }
+
     public FlowsTable table() {
         return flowsTable;
     }
diff --git a/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/authentication/flows/FlowsTable.java b/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/authentication/flows/FlowsTable.java
index 1e3401b..3757996 100644
--- a/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/authentication/flows/FlowsTable.java
+++ b/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/authentication/flows/FlowsTable.java
@@ -25,11 +25,16 @@ import org.openqa.selenium.By;
 import org.openqa.selenium.WebElement;
 import org.openqa.selenium.support.FindBy;
 
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
 import static org.keycloak.testsuite.util.WaitUtils.waitUntilElement;
 
 /**
  *
  * @author <a href="mailto:vramik@redhat.com">Vlastislav Ramik</a>
+ * @author <a href="mailto:pzaoral@redhat.com">Peter Zaoral</a>
  */
 public class FlowsTable {
     public enum RequirementOption {
@@ -53,8 +58,8 @@ public class FlowsTable {
     public enum Action {
         
         DELETE("Delete"),
-        ADD_EXECUTION("Add Execution"),
-        ADD_FLOW("Add Flow");
+        ADD_EXECUTION("Add execution"),
+        ADD_FLOW("Add flow");
         
         private final String name;
         
@@ -69,26 +74,51 @@ public class FlowsTable {
     
     @FindBy(tagName = "tbody")
     private WebElement tbody;
-    
+
     private WebElement getRowByLabelText(String text) {
         WebElement row = tbody.findElement(By.xpath("//span[text() = '" + text + "']/../.."));
+        //tbody.findElement(By.xpath("//span[contains(text(),\"" + text + "\")]/../.."));
         waitUntilElement(row).is().present();
         return row;
     }
-    
+
     public void clickLevelUpButton(String rowLabel) {
-        getRowByLabelText(rowLabel).findElement(By.xpath("//i[contains(@class, 'up')]/..")).click();
+        getRowByLabelText(rowLabel).findElement(By.xpath(".//button[@data-ng-click='raisePriority(execution)']")).click();
     }
-    
+
     public void clickLevelDownButton(String rowLabel) {
-        getRowByLabelText(rowLabel).findElement(By.xpath("//i[contains(@class, 'down')]/..")).click();
+        getRowByLabelText(rowLabel).findElement(By.xpath(".//button[@data-ng-click='lowerPriority(execution)']")).click();
     }
-    
+
     public void changeRequirement(String rowLabel, RequirementOption option) {
-        getRowByLabelText(rowLabel).findElement(By.xpath("//input[@value = '" + option + "']")).click();
+        getRowByLabelText(rowLabel).findElement(By.xpath(".//input[@value = '" + option + "']")).click();
     }
-    
+
     public void performAction(String rowLabel, Action action) {
-        
+
+        getRowByLabelText(rowLabel).findElement(
+                By.xpath(".//div[@class = 'dropdown']/a[@class='dropdown-toggle ng-binding']")).click();
+        WebElement currentAction = getRowByLabelText(rowLabel).findElement(
+                    By.xpath("//div[@class = 'dropdown open']/ul[@class = 'dropdown-menu']/li/" +
+                            "a[@class='ng-binding' and text()='" + action.getName() + "']"));
+        currentAction.click();
+    }
+
+    // Returns all aliases of flows (first "Auth Type" column in table) including the names of execution flows
+    // Each returned alias (key) has also the Requirement option (value) assigned in the Map
+    public Map<String, String> getFlowsAliasesWithRequirements(){
+        Map<String, String> flows = new LinkedHashMap<>();
+        List<WebElement> aliases = tbody.findElements(By.xpath("//span[@class='ng-binding']"));
+
+        for(WebElement alias : aliases)
+        {
+            List<WebElement> requirementsOptions = alias.findElements(By.xpath(".//../parent::*//input[@type='radio']"));
+            for (WebElement requirement : requirementsOptions) {
+                if (requirement.isSelected()) {
+                    flows.put(alias.getText(), requirement.getAttribute("value"));
+                }
+            }
+        }
+        return flows;
     }
 }
diff --git a/testsuite/integration-arquillian/tests/other/console/src/test/java/org/keycloak/testsuite/console/authentication/FlowsTest.java b/testsuite/integration-arquillian/tests/other/console/src/test/java/org/keycloak/testsuite/console/authentication/FlowsTest.java
index d3dfb22..35b8421 100644
--- a/testsuite/integration-arquillian/tests/other/console/src/test/java/org/keycloak/testsuite/console/authentication/FlowsTest.java
+++ b/testsuite/integration-arquillian/tests/other/console/src/test/java/org/keycloak/testsuite/console/authentication/FlowsTest.java
@@ -21,10 +21,12 @@
  */
 package org.keycloak.testsuite.console.authentication;
 
+import org.apache.commons.collections.CollectionUtils;
 import org.jboss.arquillian.graphene.page.Page;
 import org.junit.Before;
-import org.junit.Ignore;
 import org.junit.Test;
+import org.keycloak.representations.idm.AuthenticationExecutionExportRepresentation;
+import org.keycloak.representations.idm.AuthenticationFlowRepresentation;
 import org.keycloak.testsuite.console.AbstractConsoleTest;
 import org.keycloak.testsuite.console.page.authentication.flows.CreateExecution;
 import org.keycloak.testsuite.console.page.authentication.flows.CreateExecutionForm;
@@ -33,18 +35,26 @@ import org.keycloak.testsuite.console.page.authentication.flows.CreateFlowForm;
 import org.keycloak.testsuite.console.page.authentication.flows.Flows;
 import org.keycloak.testsuite.console.page.authentication.flows.FlowsTable;
 
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import static org.hamcrest.CoreMatchers.hasItem;
+import static org.hamcrest.core.IsNot.not;
 import static org.junit.Assert.*;
 
 /**
  *
  * @author <a href="mailto:vramik@redhat.com">Vlastislav Ramik</a>
+ * @author <a href="mailto:pzaoral@redhat.com">Peter Zaoral</a>
  */
-@Ignore //waiting for KEYCLOAK-1967(KEYCLOAK-1966)
+
 public class FlowsTest extends AbstractConsoleTest {
     
     @Page
     private Flows flowsPage;
-    
+
     @Page
     private CreateFlow createFlowPage;
     
@@ -58,53 +68,72 @@ public class FlowsTest extends AbstractConsoleTest {
     
     @Test
     public void createDeleteFlowTest() {
-        log.info("add new flow");
+        // Adding new flow
         flowsPage.clickNew();
         createFlowPage.form().setValues("testFlow", "testDesc", CreateFlowForm.FlowType.GENERIC);
-        assertEquals("Success! Flow Created.", createFlowPage.getSuccessMessage());
-        log.debug("new flow created via UI");
-        
-        log.info("check if test flow is created via rest");
-        //rest: flow is present
-        log.debug("checked");
+        assertAlertSuccess();
+
+        // Checking if test flow is created via rest
+        AuthenticationFlowRepresentation testFlow = getLastFlowFromREST();
+        assertEquals("testFlow", testFlow.getAlias());
         
-        log.debug("check if testFlow is selected in UI");
+        // Checking if testFlow is selected in UI
         assertEquals("TestFlow", flowsPage.getFlowSelectValue());
         
-        log.info("add new execution flow within testFlow");
+        // Adding new execution flow within testFlow
         flowsPage.clickAddFlow();
-        createFlowPage.form().setValues("testExecutionFlow", "executionDesc", CreateFlowForm.FlowType.GENERIC);
-        assertEquals("Success! Flow Created.", createFlowPage.getSuccessMessage());
-        log.debug("new execution flow created via UI");
+        createFlowPage.form().setValues("testExecution", "executionDesc", CreateFlowForm.FlowType.GENERIC);
+        assertAlertSuccess();
         
-        log.info("check if execution flow is created via rest");
-        //rest: flow within nested flow is present
-        log.debug("checked");
+        // Checking if execution flow is created via rest
+        testFlow = getLastFlowFromREST();
+        assertEquals("testExecution", testFlow.getAuthenticationExecutions().get(0).getFlowAlias());
         
-        log.debug("check if testFlow is selected in UI");
+        // Checking if testFlow is selected in UI
         assertEquals("TestFlow", flowsPage.getFlowSelectValue());
         
-        log.info("delete test flow");
+        // Deleting test flow
         flowsPage.clickDelete();
-        assertEquals("Success! Flow removed", createFlowPage.getSuccessMessage());
-        log.debug("test flow removed via UI");
-        
-        log.info("check if both test flow and execution flow is removed via rest");
-        //rest
-        log.debug("checked");
+        modalDialog.confirmDeletion();
+        assertAlertSuccess();
+
+        // Checking if both test flow and execution flow is removed via UI
+        assertEquals("Browser", flowsPage.getFlowSelectValue());
+        assertThat(flowsPage.getFlowAllValues(), not(hasItem("TestFlow")));
+
+        // Checking if both test flow and execution flow is removed via rest
+        assertThat(testRealmResource().flows().getFlows(), not(hasItem(testFlow)));
     }
-    
+
+    @Test
+    public void selectFlowOptionTest() {
+        flowsPage.selectFlowOption(Flows.FlowOption.DIRECT_GRANT);
+        assertEquals("Direct Grant", flowsPage.getFlowSelectValue());
+        flowsPage.selectFlowOption(Flows.FlowOption.BROWSER);
+        assertEquals("Browser", flowsPage.getFlowSelectValue());
+        flowsPage.selectFlowOption(Flows.FlowOption.CLIENTS);
+        assertEquals("Clients", flowsPage.getFlowSelectValue());
+    }
+
     @Test
     public void createFlowWithEmptyAliasTest() {
         flowsPage.clickNew();
         createFlowPage.form().setValues("", "testDesc", CreateFlowForm.FlowType.GENERIC);
-        assertEquals("Error! Missing or invalid field(s). Please verify the fields in red.", createFlowPage.getErrorMessage());
+        assertAlertDanger();
         
         //rest:flow isn't present
-        
-        //best-efford: check empty alias in nested flow
     }
-    
+
+    @Test
+    public void createNestedFlowWithEmptyAliasTest() {
+        //best-effort: check empty alias in nested flow
+        flowsPage.clickNew();
+        createFlowPage.form().setValues("testFlow", "testDesc", CreateFlowForm.FlowType.GENERIC);
+        flowsPage.clickAddFlow();
+        createFlowPage.form().setValues("", "executionDesc", CreateFlowForm.FlowType.GENERIC);
+        assertAlertDanger();
+    }
+
     @Test
     public void copyFlowTest() {
         flowsPage.selectFlowOption(Flows.FlowOption.BROWSER);
@@ -112,86 +141,164 @@ public class FlowsTest extends AbstractConsoleTest {
         
         modalDialog.setName("test copy of browser");
         modalDialog.ok();
-        assertEquals("Success! Flow copied.", createFlowPage.getSuccessMessage());
+        assertAlertSuccess();
+
+        //UI
+        assertEquals("Test Copy Of Browser", flowsPage.getFlowSelectValue());
+        assertTrue(flowsPage.table().getFlowsAliasesWithRequirements().containsKey("Test Copy Of Browser Forms"));
+        assertEquals(6,flowsPage.table().getFlowsAliasesWithRequirements().size());
+
         
         //rest: copied flow present
+        assertThat(testRealmResource().flows().getFlows().stream()
+                .map(AuthenticationFlowRepresentation::getAlias).
+                        collect(Collectors.toList()), hasItem(getLastFlowFromREST().getAlias()));
     }
     
     @Test
     public void createDeleteExecutionTest() {
-        //rest: add new flow
-        
-        log.info("add new execution within testFlow");
+        // Adding new execution within testFlow
+
+        flowsPage.clickNew();
+        createFlowPage.form().setValues("testFlow", "testDesc", CreateFlowForm.FlowType.GENERIC);
+
         flowsPage.clickAddExecution();
         createExecutionPage.form().selectProviderOption(CreateExecutionForm.ProviderOption.RESET_PASSWORD);
         createExecutionPage.form().save();
+        assertAlertSuccess();
         
-        assertEquals("Success! Execution Created.", createExecutionPage.getSuccessMessage());
-        log.debug("new execution flow created via UI");
-        
-        //rest:check new execution
-        
-        log.debug("check if testFlow is selected in UI");
+        // REST
+        assertEquals(1, getLastFlowFromREST().getAuthenticationExecutions().size());
+        assertEquals("reset-password", getLastFlowFromREST().getAuthenticationExecutions().get(0).getAuthenticator());
+
+        // UI
         assertEquals("TestFlow", flowsPage.getFlowSelectValue());
-        
-        log.info("delete test flow");
+        assertEquals(1,flowsPage.table().getFlowsAliasesWithRequirements().size());
+        assertTrue(flowsPage.table().getFlowsAliasesWithRequirements().keySet().contains("Reset Password"));
+
+        // Deletion
         flowsPage.clickDelete();
-        assertEquals("Success! Flow removed", createFlowPage.getSuccessMessage());
-        log.debug("test flow removed via UI");
-        
-        log.info("check if both test flow and execution flow is removed via rest");
-        //rest
-        log.debug("checked");
+        modalDialog.confirmDeletion();
+        assertAlertSuccess();
+        assertThat(flowsPage.getFlowAllValues(), not(hasItem("TestFlow")));
     }
     
     @Test
     public void navigationTest() {
-        //rest: add or copy flow to test navigation (browser)
+        flowsPage.selectFlowOption(Flows.FlowOption.BROWSER);
+        flowsPage.clickCopy();
+        modalDialog.ok();
         
-        //rest:
-        log.debug("check if there is expected structure of the flow");
+        //init order
         //first should be Cookie
         //second Kerberos
-        //third Test Copy Of Browser Forms
+        //third Identity provider redirector
+        //fourth Test Copy Of Browser Forms
             //a) Username Password Form
             //b) OTP Form
         
         
         flowsPage.table().clickLevelDownButton("Cookie");
-        assertEquals("Success! Priority lowered", flowsPage.getSuccessMessage());
+        assertAlertSuccess();
         
-        flowsPage.table().clickLevelUpButton("Test Copy Of Browser Forms");
-        assertEquals("Success! Priority raised", flowsPage.getSuccessMessage());
+        flowsPage.table().clickLevelUpButton("Cookie");
+        assertAlertSuccess();
 
-        flowsPage.table().clickLevelUpButton("OTP Forms");
-        assertEquals("Success! Priority raised", flowsPage.getSuccessMessage());
-        
-        //rest:check if navigation was changed properly
+        flowsPage.table().clickLevelUpButton("Kerberos");
+        assertAlertSuccess();
+
+        flowsPage.table().clickLevelDownButton("Identity Provider Redirector");
+        assertAlertSuccess();
+
+        flowsPage.table().clickLevelUpButton("OTP Form");
+        assertAlertSuccess();
+
+        List<String> expectedOrder = new ArrayList<>();
+        Collections.addAll(expectedOrder, "Kerberos", "Cookie", "Copy Of Browser Forms", "OTP Form",
+                                          "Username Password Form", "Identity Provider Redirector");
+
+        //UI
+        assertEquals(6,flowsPage.table().getFlowsAliasesWithRequirements().size());
+        assertTrue(expectedOrder.containsAll(flowsPage.table().getFlowsAliasesWithRequirements().keySet()));
+
+        //REST
+        assertEquals("auth-spnego", getLastFlowFromREST().getAuthenticationExecutions().get(0).getAuthenticator());
+        assertEquals("auth-cookie", getLastFlowFromREST().getAuthenticationExecutions().get(1).getAuthenticator());
+        assertEquals("Copy of browser forms", getLastFlowFromREST().getAuthenticationExecutions().get(2).getFlowAlias());
+        assertEquals("identity-provider-redirector", getLastFlowFromREST().getAuthenticationExecutions().get(3).getAuthenticator());
+        flowsPage.clickDelete();
+        modalDialog.confirmDeletion();
     }
     
     @Test
     public void requirementTest() {
         //rest: add or copy flow to test navigation (browser), add reset, password
-        
+        flowsPage.selectFlowOption(Flows.FlowOption.BROWSER);
         flowsPage.table().changeRequirement("Cookie", FlowsTable.RequirementOption.DISABLED);
+        assertAlertSuccess();
+        flowsPage.table().changeRequirement("Kerberos", FlowsTable.RequirementOption.REQUIRED);
+        assertAlertSuccess();
         flowsPage.table().changeRequirement("Kerberos", FlowsTable.RequirementOption.ALTERNATIVE);
-        flowsPage.table().changeRequirement("Copy Of Browser Forms", FlowsTable.RequirementOption.REQUIRED);
-        flowsPage.table().changeRequirement("Reset Password", FlowsTable.RequirementOption.REQUIRED);
+        assertAlertSuccess();
+        flowsPage.table().changeRequirement("OTP Form", FlowsTable.RequirementOption.DISABLED);
+        assertAlertSuccess();
+        flowsPage.table().changeRequirement("OTP Form", FlowsTable.RequirementOption.OPTIONAL);
+        assertAlertSuccess();
+
+        //UI
+        List<String> expectedOrder = new ArrayList<>();
+        Collections.addAll(expectedOrder,"DISABLED", "ALTERNATIVE", "ALTERNATIVE",
+                                         "ALTERNATIVE", "REQUIRED", "OPTIONAL");
+        assertTrue(expectedOrder.containsAll(flowsPage.table().getFlowsAliasesWithRequirements().values()));
         
-        //rest:check
+        //REST:
+        List<AuthenticationExecutionExportRepresentation> browserFlow = testRealmResource().flows()
+                                                                    .getFlows().get(0).getAuthenticationExecutions();
+        assertEquals("DISABLED", browserFlow.get(0).getRequirement());
+        assertEquals("ALTERNATIVE", browserFlow.get(1).getRequirement());
+        assertEquals("ALTERNATIVE", browserFlow.get(2).getRequirement());
     }
     
     @Test
     public void actionsTest() {
         //rest: add or copy flow to test navigation (browser)
-        
+        flowsPage.selectFlowOption(Flows.FlowOption.BROWSER);
+        flowsPage.clickCopy();
+        modalDialog.ok();
+
+        flowsPage.table().performAction("Cookie", FlowsTable.Action.DELETE);
+        modalDialog.confirmDeletion();
+        assertAlertSuccess();
         flowsPage.table().performAction("Kerberos", FlowsTable.Action.DELETE);
+        modalDialog.confirmDeletion();
+        assertAlertSuccess();
         flowsPage.table().performAction("Copy Of Browser Forms", FlowsTable.Action.ADD_FLOW);
-        
-        createFlowPage.form().setValues("nestedFlow", "", CreateFlowForm.FlowType.CLIENT);
-        
-        //todo: perform all remaining actions
-        
-        //rest: check
+        createFlowPage.form().setValues("nestedFlow", "testDesc", CreateFlowForm.FlowType.FORM);
+        assertAlertSuccess();
+        flowsPage.table().performAction("Copy Of Browser Forms",FlowsTable.Action.ADD_EXECUTION);
+        createExecutionPage.form().selectProviderOption(CreateExecutionForm.ProviderOption.RESET_PASSWORD);
+        createExecutionPage.form().save();
+        assertAlertSuccess();
+
+        //UI
+        List<String> expectedOrder = new ArrayList<>();
+        Collections.addAll(expectedOrder, "Identity Provider Redirector", "Copy Of Browser Forms",
+                                          "Username Password Form", "OTP Form", "NestedFlow", "Reset Password");
+
+        assertEquals(6,flowsPage.table().getFlowsAliasesWithRequirements().size());
+        assertTrue(expectedOrder.containsAll(flowsPage.table().getFlowsAliasesWithRequirements().keySet()));
+
+        //REST
+        assertEquals("identity-provider-redirector", getLastFlowFromREST().getAuthenticationExecutions().get(0).getAuthenticator());
+        String tmpFlowAlias = getLastFlowFromREST().getAuthenticationExecutions().get(1).getFlowAlias();
+        assertEquals("Copy of browser forms", tmpFlowAlias);
+        assertEquals("Username Password Form", testRealmResource().flows().getExecutions(tmpFlowAlias).get(0).getDisplayName());
+        assertEquals("nestedFlow", testRealmResource().flows().getExecutions(tmpFlowAlias).get(2).getDisplayName());
+    }
+
+    private AuthenticationFlowRepresentation getLastFlowFromREST() {
+        List<AuthenticationFlowRepresentation> allFlows = testRealmResource().flows().getFlows();
+        return (AuthenticationFlowRepresentation) CollectionUtils.
+                get(allFlows, (allFlows.size() - 1));
     }
 }