diff --git a/.github/workflows/automation-tests.yml b/.github/workflows/automation-tests.yml index 434d0c1..7a02472 100644 --- a/.github/workflows/automation-tests.yml +++ b/.github/workflows/automation-tests.yml @@ -1,4 +1,4 @@ -name: CI with 🐋 & Selenide +name: automation tests on: push: @@ -24,5 +24,5 @@ jobs: - name: Build test docker container run: docker-compose build - - name: Run Selenide tests - run: mvn -B -ntp test -P automation-tests -D headless=true + - name: Run automation tests + run: mvn -B -ntp test -P automation-tests -D selenide.headless=true diff --git a/.github/workflows/release-github-tag.yml b/.github/workflows/release-github-tag.yml index 38d6089..0aede50 100644 --- a/.github/workflows/release-github-tag.yml +++ b/.github/workflows/release-github-tag.yml @@ -38,7 +38,7 @@ jobs: mvn \ --no-transfer-progress \ --batch-mode \ - -Dgpg.passphrase=${{ secrets.OSSRH_GPG_SECRET_KEY_PASSWORD }} \ + -D gpg.passphrase=${{ secrets.OSSRH_GPG_SECRET_KEY_PASSWORD }} \ -P ossrh \ clean deploy env: @@ -56,7 +56,6 @@ jobs: - name: Build docker init container run: | - echo "Building docker image = kilmajster/keycloak-username-password-attribute-authenticator:$TAG" && \ docker build \ --build-arg VERSION="$TAG" \ -f src/main/docker/initContainer.Dockerfile \ diff --git a/pom.xml b/pom.xml index fd08d53..4e2dc21 100644 --- a/pom.xml +++ b/pom.xml @@ -62,15 +62,16 @@ 11 11 - 13.0.0 + 13.0.1 3.19.0 + 6.10.4 4.13.2 + 3.141.59 + 5.21.0 1.15.3 1.7.0 - 5.21.0 - 3.141.59 3.8.1 @@ -96,23 +97,24 @@ org.keycloak - keycloak-server-spi + keycloak-services ${keycloak.version} provided org.keycloak - keycloak-server-spi-private + keycloak-server-spi ${keycloak.version} provided org.keycloak - keycloak-services + keycloak-server-spi-private ${keycloak.version} provided + junit @@ -154,6 +156,19 @@ org.seleniumhq.selenium selenium-chrome-driver ${selenium-chrome-driver.version} + test + + + io.cucumber + cucumber-java + ${cucumber.version} + test + + + io.cucumber + cucumber-junit + ${cucumber.version} + test @@ -184,7 +199,7 @@ maven-surefire-plugin ${maven-surefire-plugin.version} - **/*AT + **/CucumberConfig.java @@ -237,7 +252,6 @@ maven-gpg-plugin ${maven-gpg-plugin.version} - --pinentry-mode loopback diff --git a/src/test/java/io.github.kilmajster.keycloak/CucumberConfig.java b/src/test/java/io.github.kilmajster.keycloak/CucumberConfig.java new file mode 100644 index 0000000..4b2cc87 --- /dev/null +++ b/src/test/java/io.github.kilmajster.keycloak/CucumberConfig.java @@ -0,0 +1,10 @@ +package io.github.kilmajster.keycloak; + +import io.cucumber.junit.Cucumber; +import io.cucumber.junit.CucumberOptions; +import org.junit.runner.RunWith; + +@RunWith(Cucumber.class) +@CucumberOptions(features = "src/test/resources/cucumber") +public class CucumberConfig { +} \ No newline at end of file diff --git a/src/test/java/io.github.kilmajster.keycloak/KeycloakSteps.java b/src/test/java/io.github.kilmajster.keycloak/KeycloakSteps.java new file mode 100644 index 0000000..4b479ea --- /dev/null +++ b/src/test/java/io.github.kilmajster.keycloak/KeycloakSteps.java @@ -0,0 +1,93 @@ +package io.github.kilmajster.keycloak; + +import com.codeborne.selenide.SelenideElement; +import dasniko.testcontainers.keycloak.KeycloakContainer; +import io.cucumber.java.en.And; +import io.cucumber.java.en.Given; +import io.cucumber.java.en.Then; +import io.cucumber.java.en.When; +import org.junit.Before; +import org.openqa.selenium.By; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.testcontainers.containers.output.Slf4jLogConsumer; + +import static com.codeborne.selenide.Condition.text; +import static com.codeborne.selenide.Selenide.$; +import static com.codeborne.selenide.Selenide.open; +import static io.github.kilmajster.keycloak.TestConstants.*; +import static org.assertj.core.api.Assertions.assertThat; + +public final class KeycloakSteps { + + private static final Logger log = LoggerFactory.getLogger(KeycloakSteps.class); + + private final KeycloakContainer keycloak = new KeycloakContainer(KEYCLOAK_DEV_DOCKER_IMAGE) + .withRealmImportFile("dev-realm.json") + .withLogConsumer(new Slf4jLogConsumer(log)); + + @Given("keycloak is running with default setup") + public void keycloak_is_running_with_default_setup() { + if (!keycloak.isRunning()) { + log.info("Starting keycloak container..."); + keycloak.start(); + } + } + + @Given("keycloak is running with LOGIN_FORM_ATTRIBUTE_LABEL = {string}") + public void keycloak_is_running_with_login_form_attribute_label_env(final String envLoginFormAttributeLabel) { + keycloak.addEnv("LOGIN_FORM_ATTRIBUTE_LABEL", envLoginFormAttributeLabel); + if (!keycloak.isRunning()) { + log.info("Starting keycloak container with LOGIN_FORM_ATTRIBUTE_LABEL = " + envLoginFormAttributeLabel); + keycloak.start(); + } + } + + @When("user goes to the account console page") + public void go_to_keycloak_account_page() { + final String keycloakUrl = TestConstants.KEYCLOAK_LOCAL_URL_PREFIX + keycloak.getFirstMappedPort(); + open(keycloakUrl + "/auth/realms/dev-realm/account"); + } + + @Then("user should be not logged in") + public void user_should_be_not_logged_in() { + final String loggedInUser = $(By.id("landingLoggedInUser")).val(); + assertThat(loggedInUser).isNullOrEmpty(); + } + + @When("user clicks a sign in button") + public static void click_sign_in_button() { + log.info("click_sign_in_button()"); + + $(By.id("landingSignInButton")).click(); + } + + @Then("login form with attribute input labeled as {string} should be shown") + public static void verify_login_form_is_displayed_with_user_attribute_label(final String label) { + log.info("verify_login_form_is_displayed_with_user_attribute_label( label = " + label + " )"); + + assertThat($(By.id("kc-form-login")).isDisplayed()).isTrue(); + userAttributeFormLabel().shouldHave(text(label)); + } + + @When("user log into account console with a valid credentials and user attribute equal {string}") + public static void log_into_account_console(final String attribute) { + log.info("log_into_account_console()"); + + $(By.id("username")).val(TEST_USERNAME); + $(By.id("password")).val(TEST_PASSWORD); + $(By.id("login_form_user_attribute")).val(attribute); + $(By.id("kc-login")).click(); + } + + @Then("user should be logged into account console") + public static void verify_that_user_is_logged_in() { + log.info("verify_that_user_is_logged_in()"); + + $(By.id("landingLoggedInUser")).shouldHave(text("test")); + } + + private static SelenideElement userAttributeFormLabel() { + return $(By.xpath("//input[@id='login_form_user_attribute']/preceding-sibling::label")); + } +} diff --git a/src/test/java/io.github.kilmajster.keycloak/TestConstants.java b/src/test/java/io.github.kilmajster.keycloak/TestConstants.java new file mode 100644 index 0000000..6d0c7ba --- /dev/null +++ b/src/test/java/io.github.kilmajster.keycloak/TestConstants.java @@ -0,0 +1,9 @@ +package io.github.kilmajster.keycloak; + +public interface TestConstants { + String KEYCLOAK_DEV_DOCKER_IMAGE = "kilmajster/keycloak-with-authenticator:test"; + String KEYCLOAK_LOCAL_URL_PREFIX = "http://localhost:"; + + String TEST_USERNAME = "test"; + String TEST_PASSWORD = "test"; +} \ No newline at end of file diff --git a/src/test/java/io.github.kilmajster.keycloak/UsernamePasswordAttributeFormAT.java b/src/test/java/io.github.kilmajster.keycloak/UsernamePasswordAttributeFormAT.java deleted file mode 100644 index cfc6808..0000000 --- a/src/test/java/io.github.kilmajster.keycloak/UsernamePasswordAttributeFormAT.java +++ /dev/null @@ -1,32 +0,0 @@ -package io.github.kilmajster.keycloak; - - -import dasniko.testcontainers.keycloak.KeycloakContainer; -import io.github.kilmajster.keycloak.base.BaseKeycloakInDockerAT; -import org.junit.Rule; -import org.junit.Test; -import org.testcontainers.containers.output.Slf4jLogConsumer; - -import static io.github.kilmajster.keycloak.base.Steps.*; - - -public class UsernamePasswordAttributeFormAT extends BaseKeycloakInDockerAT { - - @Rule - public KeycloakContainer keycloak = new KeycloakContainer(KEYCLOAK_DEV_DOCKER_IMAGE) - .withRealmImportFile("dev-realm.json") - .withNetwork(testNetwork) - .withNetworkAliases(KEYCLOAK_NETWORK_ALIAS) - .withLogConsumer(new Slf4jLogConsumer(log)); - - @Test - public void shouldLogIntoAccountConsole() { - final String keycloakBaseUrl = getKeycloakBaseUrl(keycloak); - - go_to_keycloak_account_page(keycloakBaseUrl); - click_sign_in_button(); - verify_login_form_is_displayed_with_user_attribute_label("Test attr"); - log_into_account_console(); - verify_that_user_is_logged_in(); - } -} \ No newline at end of file diff --git a/src/test/java/io.github.kilmajster.keycloak/UsernamePasswordAttributeFormEnvVarConfigAT.java b/src/test/java/io.github.kilmajster.keycloak/UsernamePasswordAttributeFormEnvVarConfigAT.java deleted file mode 100644 index 05ffc96..0000000 --- a/src/test/java/io.github.kilmajster.keycloak/UsernamePasswordAttributeFormEnvVarConfigAT.java +++ /dev/null @@ -1,32 +0,0 @@ -package io.github.kilmajster.keycloak; - -import dasniko.testcontainers.keycloak.KeycloakContainer; -import io.github.kilmajster.keycloak.base.BaseKeycloakInDockerAT; -import org.junit.Rule; -import org.junit.Test; -import org.testcontainers.containers.output.Slf4jLogConsumer; - -import static io.github.kilmajster.keycloak.base.Steps.*; - - -public class UsernamePasswordAttributeFormEnvVarConfigAT extends BaseKeycloakInDockerAT { - - @Rule - public KeycloakContainer keycloak = new KeycloakContainer(KEYCLOAK_DEV_DOCKER_IMAGE) - .withRealmImportFile("dev-realm.json") - .withEnv("LOGIN_FORM_ATTRIBUTE_LABEL", "Custom label") - .withNetwork(testNetwork) - .withNetworkAliases(KEYCLOAK_NETWORK_ALIAS) - .withLogConsumer(new Slf4jLogConsumer(log)); - - @Test - public void shouldTakeAttributeLabelFromEnvVariable() { - final String keycloakBaseUrl = getKeycloakBaseUrl(keycloak); - - go_to_keycloak_account_page(keycloakBaseUrl); - click_sign_in_button(); - verify_login_form_is_displayed_with_user_attribute_label("Custom label"); - log_into_account_console(); - verify_that_user_is_logged_in(); - } -} \ No newline at end of file diff --git a/src/test/java/io.github.kilmajster.keycloak/base/BaseKeycloakInDockerAT.java b/src/test/java/io.github.kilmajster.keycloak/base/BaseKeycloakInDockerAT.java deleted file mode 100644 index 53947f9..0000000 --- a/src/test/java/io.github.kilmajster.keycloak/base/BaseKeycloakInDockerAT.java +++ /dev/null @@ -1,59 +0,0 @@ -package io.github.kilmajster.keycloak.base; - -import com.codeborne.selenide.WebDriverRunner; -import dasniko.testcontainers.keycloak.KeycloakContainer; -import org.junit.After; -import org.junit.Before; -import org.junit.Rule; -import org.openqa.selenium.chrome.ChromeOptions; -import org.openqa.selenium.remote.RemoteWebDriver; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.testcontainers.containers.BrowserWebDriverContainer; -import org.testcontainers.containers.Container; -import org.testcontainers.containers.Network; -import org.testcontainers.containers.output.Slf4jLogConsumer; - -public abstract class BaseKeycloakInDockerAT { - - protected static final Logger log = LoggerFactory.getLogger(BaseKeycloakInDockerAT.class); - - protected static final String KEYCLOAK_DEV_DOCKER_IMAGE = "kilmajster/keycloak-with-authenticator:test"; - protected static final String KEYCLOAK_NETWORK_ALIAS = "keycloak-host"; - protected static final int KEYCLOAK_DEFAULT_PORT = 8080; - protected static final String KEYCLOAK_DOCKER_URL = "http://" + KEYCLOAK_NETWORK_ALIAS + ":" + KEYCLOAK_DEFAULT_PORT; - protected static final String KEYCLOAK_LOCAL_URL_PREFIX = "http://localhost:"; - - @Rule - public Network testNetwork = isHeadless() ? Network.newNetwork() : null; - - @Rule - public BrowserWebDriverContainer chrome = isHeadless() ? (BrowserWebDriverContainer) new BrowserWebDriverContainer() - .withCapabilities(new ChromeOptions()) - .withNetwork(testNetwork) - .withLogConsumer(new Slf4jLogConsumer(log)) : null; - - @Before - public void setUp() { - if (isHeadless()) { - log.info("Headless mode is enabled!"); - RemoteWebDriver driver = chrome.getWebDriver(); - WebDriverRunner.setWebDriver(driver); - } - } - - @After - public void tearDown() { - if (isHeadless()) { - WebDriverRunner.closeWebDriver(); - } - } - - protected static boolean isHeadless() { - return Boolean.parseBoolean(System.getProperty("headless")); - } - - protected String getKeycloakBaseUrl(final KeycloakContainer keycloakContainer) { - return isHeadless() ? KEYCLOAK_DOCKER_URL : KEYCLOAK_LOCAL_URL_PREFIX + keycloakContainer.getFirstMappedPort(); - } -} \ No newline at end of file diff --git a/src/test/java/io.github.kilmajster.keycloak/base/Steps.java b/src/test/java/io.github.kilmajster.keycloak/base/Steps.java deleted file mode 100644 index 707723f..0000000 --- a/src/test/java/io.github.kilmajster.keycloak/base/Steps.java +++ /dev/null @@ -1,55 +0,0 @@ -package io.github.kilmajster.keycloak.base; - -import com.codeborne.selenide.SelenideElement; -import org.openqa.selenium.By; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import static com.codeborne.selenide.Condition.text; -import static com.codeborne.selenide.Selenide.$; -import static com.codeborne.selenide.Selenide.open; -import static org.assertj.core.api.Assertions.assertThat; - -public final class Steps { - - protected static final Logger log = LoggerFactory.getLogger(Steps.class); - - - public static void go_to_keycloak_account_page(final String keycloakUrl) { - log.info("go_to_keycloak_account_page( keycloakUrl = " + keycloakUrl + " )"); - - open(keycloakUrl + "/auth/realms/dev-realm/account"); - } - - public static void click_sign_in_button() { - log.info("click_sign_in_button()"); - - $(By.id("landingSignInButton")).click(); - } - - public static void verify_login_form_is_displayed_with_user_attribute_label(final String label) { - log.info("verify_login_form_is_displayed_with_user_attribute_label( label = " + label + " )"); - - assertThat($(By.id("kc-form-login")).isDisplayed()).isTrue(); - userAttributeFormLabel().shouldHave(text(label)); - } - - public static void log_into_account_console() { - log.info("log_into_account_console()"); - - $(By.id("username")).val("test"); - $(By.id("password")).val("test"); - $(By.id("login_form_user_attribute")).val("test"); - $(By.id("kc-login")).click(); - } - - public static void verify_that_user_is_logged_in() { - log.info("verify_that_user_is_logged_in()"); - - $(By.id("landingLoggedInUser")).shouldHave(text("test")); - } - - private static SelenideElement userAttributeFormLabel() { - return $(By.xpath("//input[@id='login_form_user_attribute']/preceding-sibling::label")); - } -} diff --git a/src/test/resources/cucumber.properties b/src/test/resources/cucumber.properties new file mode 100644 index 0000000..aa51b35 --- /dev/null +++ b/src/test/resources/cucumber.properties @@ -0,0 +1 @@ +cucumber.publish.quiet=true \ No newline at end of file diff --git a/src/test/resources/cucumber/login-form-env-var-config.feature b/src/test/resources/cucumber/login-form-env-var-config.feature new file mode 100644 index 0000000..e100c1d --- /dev/null +++ b/src/test/resources/cucumber/login-form-env-var-config.feature @@ -0,0 +1,8 @@ +Feature: Login form with user attribute with environment variable based configuration + + Scenario: label is taken from environment variable + Given keycloak is running with LOGIN_FORM_ATTRIBUTE_LABEL = "Custom label" + When user goes to the account console page + Then user should be not logged in + When user clicks a sign in button + Then login form with attribute input labeled as "Custom label" should be shown \ No newline at end of file diff --git a/src/test/resources/cucumber/login-form.feature b/src/test/resources/cucumber/login-form.feature new file mode 100644 index 0000000..5a294d8 --- /dev/null +++ b/src/test/resources/cucumber/login-form.feature @@ -0,0 +1,10 @@ +Feature: Login form with user attribute + + Scenario: user can log into account console + Given keycloak is running with default setup + When user goes to the account console page + Then user should be not logged in + When user clicks a sign in button + Then login form with attribute input labeled as "Test attr" should be shown + When user log into account console with a valid credentials and user attribute equal "test" + Then user should be logged into account console \ No newline at end of file