keycloak-memoizeit

Details

diff --git a/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/clientscopes/ClientScopesEvaluate.java b/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/clientscopes/ClientScopesEvaluate.java
new file mode 100644
index 0000000..a72e410
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/clientscopes/ClientScopesEvaluate.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2017 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * 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 org.keycloak.testsuite.console.page.clients.clientscopes;
+
+import org.jboss.arquillian.graphene.page.Page;
+import org.keycloak.testsuite.console.page.clients.Client;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class ClientScopesEvaluate extends Client {
+
+    @Page
+    private ClientScopesEvaluateForm form;
+
+    @Override
+    public String getUriFragment() {
+        return super.getUriFragment() + "/client-scopes/evaluate-scopes";
+    }
+
+    public ClientScopesEvaluateForm form() {
+        return form;
+    }
+}
diff --git a/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/clientscopes/ClientScopesEvaluateForm.java b/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/clientscopes/ClientScopesEvaluateForm.java
new file mode 100644
index 0000000..2a42652
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/clientscopes/ClientScopesEvaluateForm.java
@@ -0,0 +1,157 @@
+/*
+ * Copyright 2017 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * 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 org.keycloak.testsuite.console.page.clients.clientscopes;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import org.keycloak.testsuite.console.page.fragment.DataTable;
+import org.keycloak.testsuite.page.Form;
+import org.keycloak.testsuite.util.WaitUtils;
+import org.openqa.selenium.By;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+import org.openqa.selenium.support.ui.Select;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class ClientScopesEvaluateForm extends Form {
+
+    @FindBy(id = "scopeParam")
+    private WebElement scopeParamInput;
+
+    @FindBy(id = "available")
+    protected Select availableClientScopesSelect;
+
+    @FindBy(id = "assigned")
+    protected Select assignedClientScopesSelect;
+
+    @FindBy(id = "effective")
+    protected Select effectiveClientScopesSelect;
+
+    @FindBy(css = "button[ng-click*='addAppliedClientScope']")
+    protected WebElement addAppliedClientScopesButton;
+
+    @FindBy(css = "button[ng-click*='deleteAppliedClientScope']")
+    protected WebElement deleteAppliedClientScopesButton;
+
+    @FindBy(css = "button[data-ng-click*='sendEvaluationRequest']")
+    protected WebElement evaluateButton;
+
+    // Bottom part of the page (stuff shown after "Evaluate" button clicked)
+    @FindBy(css = "li[data-ng-click*='showTab(1)']")
+    protected WebElement showProtocolMappersLink;
+
+    @FindBy(css = "li[data-ng-click*='showTab(2)']")
+    protected WebElement showRolesLink;
+
+    @FindBy(css = "li[data-ng-click*='showTab(3)']")
+    protected WebElement showTokenLink;
+
+    @FindBy(css = "table[data-ng-show*='protocolMappersShown']")
+    protected DataTable protocolMappersTable;
+
+    @FindBy(id = "available-realm-roles")
+    protected Select notGrantedRealmRolesSelect;
+
+    @FindBy(id = "realm-composite")
+    protected Select grantedRealmRolesSelect;
+
+    @FindBy(tagName = "textarea")
+    private WebElement accessTokenTextArea;
+
+
+    public Set<String> getAvailableClientScopes() {
+        return ClientScopesSetupForm.getSelectValues(availableClientScopesSelect);
+    }
+
+    public Set<String> getAssignedClientScopes() {
+        return ClientScopesSetupForm.getSelectValues(assignedClientScopesSelect);
+    }
+
+    public Set<String> getEffectiveClientScopes() {
+        return ClientScopesSetupForm.getSelectValues(effectiveClientScopesSelect);
+    }
+
+    public void setAssignedClientScopes(Collection<String> scopes) {
+        ClientScopesSetupForm.removeRedundantScopes(assignedClientScopesSelect, deleteAppliedClientScopesButton, scopes);
+        ClientScopesSetupForm.addMissingScopes(availableClientScopesSelect, addAppliedClientScopesButton, scopes);
+    }
+
+
+    public void selectUser(String username) {
+        // TODO: Should be probably better way how to work with the "ui-select2" component
+        driver.findElement(By.id("select2-chosen-1")).click();
+        driver.findElement(By.className("select2-input")).sendKeys(username);
+        driver.findElement(By.className("select2-result-label")).click();
+    }
+
+
+    public void evaluate() {
+        evaluateButton.click();
+        WaitUtils.waitForPageToLoad();
+    }
+
+
+    public void showProtocolMappers() {
+        showProtocolMappersLink.click();
+        WaitUtils.waitForPageToLoad();
+    }
+
+    public void showRoles() {
+        showRolesLink.click();
+        WaitUtils.waitForPageToLoad();
+    }
+
+    public void showToken() {
+        showTokenLink.click();
+        WaitUtils.waitForPageToLoad();
+    }
+
+
+    // Bottom part of the page (stuff shown after "Evaluate" button clicked)
+    public Set<String> getEffectiveProtocolMapperNames() {
+        List<WebElement> rows = protocolMappersTable.rows();
+
+        Set<String> names = rows.stream().map((WebElement row) -> {
+
+            return row.findElement(By.xpath("td[1]")).getText();
+
+        }).collect(Collectors.toSet());
+
+        return names;
+    }
+
+
+    public Set<String> getGrantedRealmRoles() {
+        return ClientScopesSetupForm.getSelectValues(grantedRealmRolesSelect);
+    }
+
+    public Set<String> getNotGrantedRealmRoles() {
+        return ClientScopesSetupForm.getSelectValues(notGrantedRealmRolesSelect);
+    }
+
+    public String getAccessToken() {
+        return accessTokenTextArea.getText();
+    }
+
+
+}
diff --git a/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/clientscopes/ClientScopesSetup.java b/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/clientscopes/ClientScopesSetup.java
new file mode 100644
index 0000000..6f4ecfe
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/clientscopes/ClientScopesSetup.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2017 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * 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 org.keycloak.testsuite.console.page.clients.clientscopes;
+
+import org.jboss.arquillian.graphene.page.Page;
+import org.keycloak.testsuite.console.page.clients.Client;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class ClientScopesSetup extends Client {
+
+    @Page
+    private ClientScopesSetupForm form;
+
+    @Override
+    public String getUriFragment() {
+        return super.getUriFragment() + "/client-scopes/setup-scopes";
+    }
+
+    public ClientScopesSetupForm form() {
+        return form;
+    }
+}
diff --git a/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/clientscopes/ClientScopesSetupForm.java b/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/clientscopes/ClientScopesSetupForm.java
new file mode 100644
index 0000000..6ce0a46
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/clientscopes/ClientScopesSetupForm.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright 2017 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * 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 org.keycloak.testsuite.console.page.clients.clientscopes;
+
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.keycloak.testsuite.page.Form;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+import org.openqa.selenium.support.ui.Select;
+
+import static org.keycloak.testsuite.util.WaitUtils.waitUntilElement;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class ClientScopesSetupForm extends Form {
+
+    @FindBy(id = "available")
+    protected Select availableDefaultClientScopesSelect;
+
+    @FindBy(id = "assigned")
+    protected Select defaultClientScopesSelect;
+
+    @FindBy(id = "available-opt")
+    protected Select availableOptionalClientScopesSelect;
+
+    @FindBy(id = "assigned-opt")
+    protected Select optionalClientScopesSelect;
+
+
+    @FindBy(css = "button[ng-click*='addDefaultClientScope']")
+    protected WebElement addSelectedDefaultClientScopesButton;
+
+    @FindBy(css = "button[ng-click*='addOptionalClientScope']")
+    protected WebElement addSelectedOptionalClientScopesButton;
+
+    @FindBy(css = "button[ng-click*='deleteDefaultClientScope']")
+    protected WebElement removeSelectedDefaultClientScopesButton;
+
+    @FindBy(css = "button[ng-click*='deleteOptionalClientScope']")
+    protected WebElement removeSelectedOptionalClientScopesButton;
+
+
+    public Set<String> getAvailableDefaultClientScopes() {
+        return getSelectValues(availableDefaultClientScopesSelect);
+    }
+
+    public Set<String> getDefaultClientScopes() {
+        return getSelectValues(defaultClientScopesSelect);
+    }
+
+    public Set<String> getAvailableOptionalClientScopes() {
+        return getSelectValues(availableOptionalClientScopesSelect);
+    }
+
+    public Set<String> getOptionalClientScopes() {
+        return getSelectValues(optionalClientScopesSelect);
+    }
+
+
+    public void setDefaultClientScopes(Collection<String> scopes) {
+        removeRedundantScopes(defaultClientScopesSelect, removeSelectedDefaultClientScopesButton, scopes);
+        addMissingScopes(availableDefaultClientScopesSelect, addSelectedDefaultClientScopesButton, scopes);
+    }
+
+    public void setOptionalClientScopes(Collection<String> scopes) {
+        removeRedundantScopes(optionalClientScopesSelect, removeSelectedOptionalClientScopesButton, scopes);
+        addMissingScopes(availableOptionalClientScopesSelect, addSelectedOptionalClientScopesButton, scopes);
+    }
+
+
+    // Static helper methods
+
+    static Set<String> getSelectValues(Select select) {
+        Set<String> roles = new HashSet<>();
+        for (WebElement option : select.getOptions()) {
+            roles.add(option.getText());
+        }
+        return roles;
+    }
+
+
+    static void removeRedundantScopes(Select select, WebElement button, Collection<String> scopes) {
+        boolean someRemoved = false;
+
+        select.deselectAll();
+        for (String scope : getSelectValues(select)) {
+            if (scopes == null // if scopes not provided, remove all
+                    || !scopes.contains(scope)) { // if scopes provided, remove only the redundant
+                select.selectByVisibleText(scope);
+                someRemoved = true;
+            }
+        }
+
+        if (someRemoved) {
+            waitUntilElement(button).is().enabled();
+            button.click();
+        }
+    }
+
+
+    static void addMissingScopes(Select select, WebElement button, Collection<String> scopes) {
+        select.deselectAll();
+        if (scopes != null) { // if scopes not provided, don't add any
+            boolean someAdded = false;
+
+            for (String scope : getSelectValues(select)) {
+                if (scopes.contains(scope)) { // if scopes provided, add only the missing
+                    select.selectByVisibleText(scope);
+                    someAdded = true;
+                }
+            }
+
+            if (someAdded) {
+                waitUntilElement(button).is().enabled();
+                button.click();
+            }
+        }
+    }
+
+}
diff --git a/testsuite/integration-arquillian/tests/other/console/src/test/java/org/keycloak/testsuite/console/clients/ClientClientScopesTest.java b/testsuite/integration-arquillian/tests/other/console/src/test/java/org/keycloak/testsuite/console/clients/ClientClientScopesTest.java
new file mode 100644
index 0000000..e7cb74f
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/other/console/src/test/java/org/keycloak/testsuite/console/clients/ClientClientScopesTest.java
@@ -0,0 +1,172 @@
+/*
+ * Copyright 2017 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * 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 org.keycloak.testsuite.console.clients;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+
+import org.jboss.arquillian.graphene.page.Page;
+import org.junit.Before;
+import org.junit.Test;
+import org.keycloak.representations.AccessToken;
+import org.keycloak.representations.idm.ClientRepresentation;
+import org.keycloak.testsuite.Assert;
+import org.keycloak.testsuite.console.page.clients.clientscopes.ClientScopesEvaluate;
+import org.keycloak.testsuite.console.page.clients.clientscopes.ClientScopesEvaluateForm;
+import org.keycloak.testsuite.console.page.clients.clientscopes.ClientScopesSetup;
+import org.keycloak.testsuite.console.page.clients.clientscopes.ClientScopesSetupForm;
+import org.keycloak.util.JsonSerialization;
+import org.keycloak.util.TokenUtil;
+
+import static org.junit.Assert.assertNotNull;
+import static org.keycloak.testsuite.auth.page.login.Login.OIDC;
+
+/**
+ * Test for the "Client Scopes" tab of client (Binding between "Client" and "Client Scopes")
+ *
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class ClientClientScopesTest extends AbstractClientTest {
+
+    private ClientRepresentation newClient;
+    private ClientRepresentation found;
+
+
+    @Page
+    private ClientScopesSetup clientScopesSetupPage;
+
+    @Page
+    private ClientScopesEvaluate clientScopesEvaluatePage;
+
+
+    @Before
+    public void before() {
+        newClient = createClientRep(TEST_CLIENT_ID, OIDC);
+        newClient.setFullScopeAllowed(false);
+
+        testRealmResource().clients().create(newClient).close();
+
+        found = findClientByClientId(TEST_CLIENT_ID);
+        assertNotNull("Client " + TEST_CLIENT_ID + " was not found.", found);
+        clientScopesSetupPage.setId(found.getId());
+        clientScopesSetupPage.navigateTo();
+    }
+
+
+    @Test
+    public void testSetupClientScopes() {
+        ClientScopesSetupForm setupForm = clientScopesSetupPage.form();
+
+        // Test the initial state
+        Assert.assertNames(setupForm.getAvailableDefaultClientScopes());
+        Assert.assertNames(setupForm.getDefaultClientScopes(), "email", "profile");
+        Assert.assertNames(setupForm.getAvailableOptionalClientScopes());
+        Assert.assertNames(setupForm.getOptionalClientScopes(), "address", "phone", "offline_access");
+
+        // Remove 'profile' as default client scope and assert
+        setupForm.setDefaultClientScopes(Collections.singletonList("email"));
+        Assert.assertNames(setupForm.getAvailableDefaultClientScopes(), "profile");
+        Assert.assertNames(setupForm.getDefaultClientScopes(), "email");
+        Assert.assertNames(setupForm.getAvailableOptionalClientScopes(), "profile");
+        Assert.assertNames(setupForm.getOptionalClientScopes(), "address", "phone", "offline_access");
+
+        // Add 'profile' as optional client scope and assert
+        setupForm.setOptionalClientScopes(Arrays.asList("profile", "address", "phone", "offline_access"));
+        Assert.assertNames(setupForm.getAvailableDefaultClientScopes());
+        Assert.assertNames(setupForm.getDefaultClientScopes(), "email");
+        Assert.assertNames(setupForm.getAvailableOptionalClientScopes());
+        Assert.assertNames(setupForm.getOptionalClientScopes(), "profile", "address", "phone", "offline_access");
+
+        // Retrieve client through adminClient
+        found = findClientByClientId(TEST_CLIENT_ID);
+        Assert.assertNames(found.getDefaultClientScopes(), "email", "role_list"); // SAML client scope 'role_list' is included too in the rep
+        Assert.assertNames(found.getOptionalClientScopes(), "profile", "address", "phone", "offline_access");
+
+
+        // Revert and check things successfully reverted
+        setupForm.setOptionalClientScopes(Arrays.asList("address", "phone", "offline_access"));
+        Assert.assertNames(setupForm.getAvailableDefaultClientScopes(), "profile");
+        setupForm.setDefaultClientScopes(Arrays.asList("profile", "email"));
+
+        Assert.assertNames(setupForm.getAvailableDefaultClientScopes());
+        Assert.assertNames(setupForm.getDefaultClientScopes(), "email", "profile");
+        Assert.assertNames(setupForm.getAvailableOptionalClientScopes());
+        Assert.assertNames(setupForm.getOptionalClientScopes(), "address", "phone", "offline_access");
+    }
+
+
+    @Test
+    public void testEvaluateClientScopes() throws IOException {
+        clientScopesEvaluatePage.setId(found.getId());
+        clientScopesEvaluatePage.navigateTo();
+
+        ClientScopesEvaluateForm evaluateForm = clientScopesEvaluatePage.form();
+
+        // Check the defaults
+        Assert.assertNames(evaluateForm.getAvailableClientScopes(), "address", "phone", "offline_access");
+        Assert.assertNames(evaluateForm.getAssignedClientScopes());
+        Assert.assertNames(evaluateForm.getEffectiveClientScopes(), "profile", "email");
+
+        // Add some optional scopes to the evaluation
+        evaluateForm.setAssignedClientScopes(Arrays.asList("address", "phone"));
+        Assert.assertNames(evaluateForm.getAvailableClientScopes(), "offline_access");
+        Assert.assertNames(evaluateForm.getAssignedClientScopes(), "address", "phone");
+        Assert.assertNames(evaluateForm.getEffectiveClientScopes(), "address", "phone", "profile", "email");
+
+        // Remove optional 'phone' scope from the evaluation
+        evaluateForm.setAssignedClientScopes(Arrays.asList("address", "offline_access"));
+        Assert.assertNames(evaluateForm.getAvailableClientScopes(), "phone");
+        Assert.assertNames(evaluateForm.getAssignedClientScopes(), "address", "offline_access");
+        Assert.assertNames(evaluateForm.getEffectiveClientScopes(), "address", "offline_access", "profile", "email");
+
+        // Select some user
+        evaluateForm.selectUser("test");
+
+        // Submit
+        evaluateForm.evaluate();
+
+        // Test protocolMappers of 'address' , 'profile' and 'email' scopes are included
+        Set<String> protocolMappers = evaluateForm.getEffectiveProtocolMapperNames();
+        Assert.assertTrue(protocolMappers.contains("address"));
+        Assert.assertTrue(protocolMappers.contains("email"));
+        Assert.assertTrue(protocolMappers.contains("email verified"));
+        Assert.assertTrue(protocolMappers.contains("username"));
+        Assert.assertTrue(protocolMappers.contains("full name"));
+        Assert.assertFalse(protocolMappers.contains("phone"));
+
+        // Test roles
+        evaluateForm.showRoles();
+        Assert.assertNames(evaluateForm.getGrantedRealmRoles(), "offline_access");
+        Assert.assertNames(evaluateForm.getNotGrantedRealmRoles(), "uma_authorization");
+
+        // Test access token
+        evaluateForm.showToken();
+        String accessTokenStr = evaluateForm.getAccessToken();
+
+        AccessToken token = JsonSerialization.readValue(accessTokenStr, AccessToken.class);
+        String scopeParam = token.getScope();
+        Assert.assertTrue(TokenUtil.isOIDCRequest(scopeParam));
+        Assert.assertTrue(TokenUtil.hasScope(scopeParam, "address"));
+        Assert.assertTrue(TokenUtil.hasScope(scopeParam, "profile"));
+        Assert.assertTrue(TokenUtil.hasScope(scopeParam, "email"));
+        Assert.assertFalse(TokenUtil.hasScope(scopeParam, "phone"));
+    }
+}
diff --git a/testsuite/integration-arquillian/tests/other/console/src/test/java/org/keycloak/testsuite/console/clients/ClientScopeTest.java b/testsuite/integration-arquillian/tests/other/console/src/test/java/org/keycloak/testsuite/console/clients/ClientScopeTest.java
index 1c9d03e..51e0ccf 100644
--- a/testsuite/integration-arquillian/tests/other/console/src/test/java/org/keycloak/testsuite/console/clients/ClientScopeTest.java
+++ b/testsuite/integration-arquillian/tests/other/console/src/test/java/org/keycloak/testsuite/console/clients/ClientScopeTest.java
@@ -38,6 +38,7 @@ import static org.junit.Assert.*;
 import static org.keycloak.testsuite.auth.page.login.Login.OIDC;
 
 /**
+ * Test for the "Scope" tab of client (Client role mappings)
  *
  * @author <a href="mailto:vramik@redhat.com">Vlastislav Ramik</a>
  */