All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.github.mvysny.kaributesting.v8.BasicUtils.kt Maven / Gradle / Ivy

The newest version!
@file:Suppress("ObjectPropertyName", "FunctionName")

package com.github.mvysny.kaributesting.v8

import com.vaadin.annotations.Theme
import com.vaadin.data.HasValue
import com.vaadin.event.FieldEvents
import com.vaadin.server.AbstractClientConnector
import com.vaadin.server.VaadinRequest
import com.vaadin.shared.communication.ClientRpc
import com.vaadin.ui.*
import java.util.*
import kotlin.test.expect
import kotlin.test.fail

/**
 * Allows us to fire any Vaadin event on any Vaadin component.
 * @receiver the component, not null.
 * @param event the event, not null.
 */
public fun AbstractClientConnector._fireEvent(event: EventObject) {
    // fireEvent() is protected, gotta make it public
    val fireEvent = AbstractClientConnector::class.java.getDeclaredMethod("fireEvent", EventObject::class.java)
    fireEvent.isAccessible = true
    fireEvent.invoke(this, event)
}

public val IntRange.size: Int get() = (endInclusive + 1 - start).coerceAtLeast(0)

/**
 * Checks that a component is actually editable by the user:
 * * The component must be effectively visible: it itself must be visible, its parent must be visible and all of its ascendants must be visible.
 *   For the purpose of testing individual components not attached to the [UI], a component may be considered visible even though it's not
 *   currently nested in a [UI].
 * * The component must be effectively enabled: it itself must be enabled, its parent must be enabled and all of its ascendants must be enabled.
 * * If the component is [HasValue], it must not be [HasValue.isReadOnly].
 * @throws IllegalStateException if any of the above doesn't hold.
 */
public fun Component.checkEditableByUser() {
    check(isEffectivelyVisible()) { "The ${toPrettyString()} is not effectively visible - either it is hidden, or its ascendant is hidden" }
    check(isEnabled) { "The ${toPrettyString()} is not enabled" }
    check(isEffectivelyEnabled()) { "The ${toPrettyString()} is nested in a disabled component" }
    if (this is HasValue<*>) {
        check(!this.isReadOnly) { "The ${toPrettyString()} is read-only" }
    }
}

/**
 * Fails if the component is editable. See [checkEditableByUser] for more details.
 * @throws AssertionError if the component is editable.
 */
public fun Component.expectNotEditableByUser() {
    try {
        checkEditableByUser()
        fail("The ${toPrettyString()} is editable")
    } catch (ex: IllegalStateException) {
        // okay
    }
}

private fun Component.isEffectivelyEnabled(): Boolean = when {
    !isEnabled -> false
    // this is the difference to Component.isConnectorEnabled which in this case returns false.
    // however, we should be perfectly able to test components not connected to a UI.
    parent == null -> true
    else -> parent.isEffectivelyEnabled()
}

/**
 * Expects that [actual] list of objects matches [expected] list of objects. Fails otherwise.
 */
public fun  expectList(vararg expected: T, actual: ()->List) {
    expect(expected.toList(), actual)
}

/**
 * Returns [Label.value] or [HasValue.getValue]; returns `null` if the receiver is neither of those two things.
 */
public val Component.value: Any? get()= when(this) {
    is Label -> this.value
    is HasValue<*> -> this.value
    else -> null
}

/**
 * Sets the value of given component, but only if it is actually possible to do so by the user, i.e. the component
 * is enabled and is not read-only. If the component is read-only or disabled, an exception is thrown.
 * @throws IllegalStateException if the field was not visible, not enabled or was read-only.
 */
public var  HasValue._value: V?
    get() = value
    set(v) {
        (this as Component).checkEditableByUser()
        value = v
    }

public fun  AbstractClientConnector.overrideRpcProxy(rpcInterface: Class, instance: T) {
    val rpcProxyMap: MutableMap, ClientRpc> = AbstractClientConnector::class.java.getDeclaredField("rpcProxyMap").run {
        isAccessible = true
        @Suppress("UNCHECKED_CAST")
        get(this@overrideRpcProxy) as MutableMap, ClientRpc>
    }
    rpcProxyMap[rpcInterface] = instance
}

/**
 * Returns [AbstractTextField.getPlaceholder]/[ComboBox.getPlaceholder]/[DateField.getPlaceholder]/[DateTimeField.getPlaceholder] or null
 * for other components.
 */
public val Component.placeholder: String?
    get() = when (this) {
        is AbstractTextField -> placeholder
        is ComboBox<*> -> this.placeholder  // https://youtrack.jetbrains.com/issue/KT-24275
        is DateField -> placeholder
        is DateTimeField -> placeholder
        else -> null
    }

/**
 * Checks whether this component matches given spec. All rules are matched except the [count] rule. The
 * rules are matched against given component only (not against its children).
 */
public fun Component.matches(spec: SearchSpec.()->Unit): Boolean =
        SearchSpec(Component::class.java).apply { spec() }.toPredicate().invoke(this)

/**
 * Returns the current Vaadin theme (the theme that the current UI uses).
 */
public val currentTheme: String get() {
    val ui = UI.getCurrent() ?: throw AssertionError("No current UI")
    return ui.theme
            ?: ui.javaClass.getAnnotation(Theme::class.java)?.value
            ?: ui.session.service.getConfiguredTheme(VaadinRequest.getCurrent())
}

/**
 * Fires [FieldEvents.FocusEvent] on the component, but only if it's editable.
 */
public fun  T._focus() where T: Component.Focusable, T: FieldEvents.FocusNotifier {
    checkEditableByUser()
    focus()
    (this as AbstractClientConnector)._fireEvent(FieldEvents.FocusEvent(this))
}

/**
 * Fires [FieldEvents.BlurEvent] on the component, but only if it's editable.
 */
public fun  T._blur() where T: Component, T: FieldEvents.BlurNotifier {
    checkEditableByUser()
    (this as AbstractClientConnector)._fireEvent(FieldEvents.BlurEvent(this))
}

public val UI._pendingFocus: Component? get() {
    val pendingFocusField = UI::class.java.getDeclaredField("pendingFocus")
    pendingFocusField.isAccessible = true
    return pendingFocusField.get(this) as Component?
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy