package net.pagejects.core.click

import com.codeborne.selenide.SelenideElement
import net.pagejects.core.annotation.Click
import net.pagejects.core.reflaction.findMethod
import net.pagejects.core.reflaction.userActionAnnotations
import net.pagejects.core.service.PageObjectService
import net.pagejects.core.service.SelenideElementService
import net.pagejects.core.testdata.PageObjectWithClickAction
import org.mockito.Mock
import org.mockito.Mockito
import org.mockito.MockitoAnnotations
import org.openqa.selenium.By
import org.testng.Assert.assertEquals
import org.testng.Assert.assertNotNull
import org.testng.annotations.BeforeClass
import org.testng.annotations.BeforeMethod
import org.testng.annotations.DataProvider
import org.testng.annotations.Test
import java.util.function.Consumer

class ClickUserActionTest {
    @Mock private lateinit var selenideElement: SelenideElement
    private val selenideElementService: SelenideElementService = object : SelenideElementService {
        override fun get(pageName: String, elementName: String): SelenideElement = selenideElement

        override fun getBy(by: By): SelenideElement = selenideElement
    }
    private val pageObjectService: PageObjectService = object : PageObjectService {
        override fun <T : Any?> pageObject(pageObjectClass: Class<T>): T = Mockito.mock(pageObjectClass)
    }

    @BeforeClass
    fun setUpClass() {
        MockitoAnnotations.initMocks(this)
    }

    @BeforeMethod
    fun setUp() {
        Mockito.reset(selenideElement)
    }

    @DataProvider(name = "dataForTest")
    fun dataForTest(): Array<Array<Any>> {
        return arrayOf(
                arrayOf(
                        createClickUserAction("simpleClick"),
                        {
                            Mockito.verify(selenideElement).click()
                            Mockito.verifyNoMoreInteractions(selenideElement)
                        },
                        Consumer<Any?>() { assertEquals(it, Unit)}
                ),
                arrayOf(
                        createClickUserAction("clickWithRedirectToPage"),
                        {
                            Mockito.verify(selenideElement).click()
                            Mockito.verifyNoMoreInteractions(selenideElement)
                        },
                        Consumer<Any?>(::assertNotNull)
                ),
                arrayOf(
                        createClickUserAction("contextClick"),
                        {
                            Mockito.verify(selenideElement).contextClick()
                            Mockito.verifyNoMoreInteractions(selenideElement)
                        },
                        Consumer<Any?>() { assertEquals(it, Unit)}
                ),
                arrayOf(
                        createClickUserAction("doubleClick"),
                        {
                            Mockito.verify(selenideElement).doubleClick()
                            Mockito.verifyNoMoreInteractions(selenideElement)
                        },
                        Consumer<Any?>() { assertEquals(it, Unit)}
                )
        )
    }

    private fun createClickUserAction(methodName: String): ClickUserAction {
        val pageName = "somePage"
        val method = PageObjectWithClickAction::class.findMethod(methodName)
        val clickAnnotation = method.userActionAnnotations.single() as Click
        val returnType = if (method.returnType == Void.TYPE) null else method.returnType

        val clickUserAction = ClickUserAction(pageName, clickAnnotation, returnType)
        clickUserAction.aware(pageObjectService)
        clickUserAction.aware(selenideElementService)
        return clickUserAction
    }

    @Test(dataProvider = "dataForTest")
    fun testPerform(clickUserAction: ClickUserAction, checkClick: () -> Unit, checkResult: Consumer<Any?>) {
        checkResult.accept(clickUserAction.perform(emptyArray()))
        checkClick()
    }
}