Skip to content

resoluteworks/alkemy

Repository files navigation

2023-04-24_18-26-03

Alkemy

GitHub release (latest by date) Coveralls

Alkemy is a browser automation framework written in Kotlin and based on Selenium and Kotest. The objective is to provide more fluent definitions for Selenium tests, using a functional style and Kotlin extension functions. Alkemy currently supports Chrome and Firefox browsers.

Feature highlights

Documentation

Dependency

testImplementation "io.resoluteworks:alkemy:${alkemyVersion}"

Usage

class MyTest : StringSpec({
    val context = defaultAlkemyContext()

String selectors

A set of extensions functions can be used against String to perform lookups and assertions.

class MyTest : StringSpec({
    val context = defaultAlkemyContext()

    "string selectors and assertions" {
        // To use String extensions the context.apply{} construct is required
        context.apply {
            get("/dynamic_controls")
            
            val form = "#input-example"
            "$form button".shouldHaveText("Enable")
                .shouldBeEnabled()
                .click()
                .shouldBeDisabled()

            "$form #loading".shouldBeVisible()
            "$form input[type='text']".shouldBeEnabled(maxWaitSeconds = 10)
        }
    }

WebDriver & WebElement extensions

Similar extensions are available for WebDriver and WebElement, including helper methods like fillForm, typeIn and assertions like shouldBeVisible, shoulHaveClass, etc.

    "login with fillForm" {
        context.get("/login")
            .fillForm(
                "username" to "tomsmith",
                "password" to "SuperSecretPassword!"
            )
            .submit() shouldHaveText "Welcome to the Secure Area"
    }

Custom test selector attribute

Alkemy can use a globally defined data-* HTML attribute to lookup elements. Read more about this here.

<body>
    <div data-test-selector="content-div">Secure area</div>
    <div>Footer</div>
</body>
byTestSelector("content-div").text shouldBe "Secure area"

Or

"[${testSelector}=content-div]".text shouldBe "Secure area"

Please note this is only available within a context.apply{} block.

Page Object Model

Alkemy also provides a very basic framework for Page Object Model approaches. This includes all the extensions and assertions available for WebDriver.

class LoginPage(context: AlkemyContext) : Page(context, "/login") {
    fun login(username: String, password: String): SecurePage {
        fillForm("username" to username, "password" to password)
            .submit()
        return next<SecurePage>()
    }
}

class SecurePage(context: AlkemyContext) : Page(context, "/secure")


"login with page object model" {
    val securePage = context
        .goTo<LoginPage>()
        .login("tomsmith", "SuperSecretPassword!")
    securePage shouldHaveText "Welcome to the Secure Area"
}
    

Using native Kotest assertions

Any Kotest assertions can be used natively in combination with the Alkemy or Selenium objects.

    "using native Kotest assertions" {
        val driver = context.get("/login")
        driver.findElements("input") shouldHaveSize 2
        driver.find("h2").text shouldContain "Login Page"
    }

Custom Alkemy Config

Example to customize the Alkemy config for a Spec:

class MyTest : StringSpec({
    val context = customAlkemyContext(
        AlkemyConfig(baseUrl = baseUrl),
    )
})

To customize the default Alkemy config for a Spec, use AlkemyConfig's copy constructor to overwrite any properties, e.g.

class MyTest : StringSpec({
    val context = customAlkemyContext(
        AlkemyConfig.fromSystemProperties().copy(
            browser = Browser.FIREFOX,
            headless = true,
            windowWidth = 800,
            windowHeight = 600,
        ),
    )
})

Disabling Kotest Auto Scan

If Kotest auto scan is disabled, to enable pooling, you will need to manually load the AlkemyWebDriverPoolExtension extension in your Project config, e.g.

class ProjectConfig : AbstractProjectConfig() {
    override fun extensions() = listOf(AlkemyWebDriverPoolExtension)
}

Documentation

See Documentation for further information.