diff --git a/.gitmodules b/.gitmodules index e03f5e9..1926979 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,9 @@ [submodule "dependencies/kaluga-swiftui"] path = dependencies/kaluga-swiftui url = https://github.com/splendo/kaluga-swiftui.git +[submodule "dependencies/kaluga-uitest"] + path = dependencies/kaluga-uitest + url = git@github.com:splendo/kaluga-uitest.git +[submodule "dependencies/kaluga-cucumber"] + path = dependencies/kaluga-cucumber + url = git@github.com:splendo/kaluga-cucumber.git diff --git a/README.md b/README.md index 6ff53e4..7a915f8 100644 --- a/README.md +++ b/README.md @@ -103,7 +103,7 @@ While the Android implementation will be split into 2+ files. A test runner file @RunWith(Cucumber::class) @CucumberOptions( features = ["features"], - glue = ["com.corrado4eyes.cucumberplayground.test"] + glue = ["com.splendo.cucumberplayground.test"] ) class TestRunner : CucumberAndroidJUnitRunner() ``` diff --git a/android/build.gradle.kts b/android/build.gradle.kts index 15799f7..7e24b33 100644 --- a/android/build.gradle.kts +++ b/android/build.gradle.kts @@ -4,17 +4,17 @@ plugins { } android { - namespace = "com.corrado4eyes.cucumberplayground.android" - compileSdk = 33 + namespace = "com.splendo.cucumberplayground.android" + compileSdk = 34 defaultConfig { - applicationId = "com.corrado4eyes.cucumberplayground.android" + applicationId = "com.splendo.cucumberplayground.android" minSdk = 29 - targetSdk = 33 + targetSdk = 34 versionCode = 1 versionName = "1.0" - testApplicationId = "com.corrado4eyes.cucumberplayground.test" - testInstrumentationRunner = "com.corrado4eyes.cucumberplayground.test.CucumberTests" + testApplicationId = "com.splendo.cucumberplayground.test" + testInstrumentationRunner = "com.splendo.cucumberplayground.test.CucumberTests" } buildFeatures { @@ -52,17 +52,16 @@ dependencies { val kalugaVersion: String by project implementation(project(":shared")) - implementation(project(":cucumber")) - implementation("androidx.compose.ui:ui:1.4.3") - implementation("androidx.compose.ui:ui-tooling:1.4.3") - implementation("androidx.compose.ui:ui-tooling-preview:1.4.3") - implementation("androidx.compose.foundation:foundation:1.4.3") - implementation("androidx.compose.material:material:1.4.3") - implementation("androidx.activity:activity-compose:1.7.2") + implementation("androidx.compose.ui:ui:1.5.3") + implementation("androidx.compose.ui:ui-tooling:1.5.3") + implementation("androidx.compose.ui:ui-tooling-preview:1.5.3") + implementation("androidx.compose.foundation:foundation:1.5.3") + implementation("androidx.compose.material:material:1.5.3") + implementation("androidx.activity:activity-compose:1.8.0") androidTestImplementation(kotlin("test")) - androidTestImplementation("androidx.compose.ui:ui-test-junit4:1.4.3") - debugImplementation("androidx.compose.ui:ui-test-manifest:$1.4.3") + androidTestImplementation("androidx.compose.ui:ui-test-junit4:1.5.3") + debugImplementation("androidx.compose.ui:ui-test-manifest:1.5.3") implementation("com.splendo.kaluga:architecture-compose:$kalugaVersion") implementation("com.splendo.kaluga:resources-compose:$kalugaVersion") @@ -73,5 +72,5 @@ dependencies { // TODO figure out how it can be updated without breeaking the project androidTestImplementation("io.cucumber:cucumber-java8:4.8.1") - implementation("io.insert-koin:koin-androidx-compose:3.4.1") + implementation("io.insert-koin:koin-androidx-compose:3.4.4") } \ No newline at end of file diff --git a/android/src/androidTest/assets/features/Home.feature b/android/src/androidTest/assets/features/Home.feature index e2e6bb0..b6f0e26 100644 --- a/android/src/androidTest/assets/features/Home.feature +++ b/android/src/androidTest/assets/features/Home.feature @@ -4,5 +4,6 @@ Feature: Home And I am in the "Home" screen Then I see "test@test.com" text And I see the "Logout" button + And I see "15" in the scrollview When I press the "Logout" button - Then I see the "Login" screen + Then I see the "Login" screen \ No newline at end of file diff --git a/android/src/androidTest/assets/features/Login.feature b/android/src/androidTest/assets/features/Login.feature index 4290305..f4d1263 100644 --- a/android/src/androidTest/assets/features/Login.feature +++ b/android/src/androidTest/assets/features/Login.feature @@ -5,7 +5,7 @@ Feature: Login And I see the "Password" textfield with text "Password" And I see the "Login" button When I type "test@test.com" in the "Email" textfield - And I type "1234" in the "Password" secure textfield + And I type "1234" in the "Password" textfield And I press the "Login" button Then I see the "Home" screen And I see "test@test.com" text diff --git a/android/src/androidTest/kotlin/com/corrado4eyes/cucumberplayground/test/CucumberTests.kt b/android/src/androidTest/kotlin/com/corrado4eyes/cucumberplayground/test/CucumberTests.kt index f8f1dcd..d1c7995 100644 --- a/android/src/androidTest/kotlin/com/corrado4eyes/cucumberplayground/test/CucumberTests.kt +++ b/android/src/androidTest/kotlin/com/corrado4eyes/cucumberplayground/test/CucumberTests.kt @@ -1,4 +1,4 @@ -package com.corrado4eyes.cucumberplayground.test +package com.splendo.cucumberplayground.test import io.cucumber.android.runner.CucumberAndroidJUnitRunner import io.cucumber.junit.Cucumber @@ -8,6 +8,6 @@ import org.junit.runner.RunWith @RunWith(Cucumber::class) @CucumberOptions( features = ["features"], - glue = ["com.corrado4eyes.cucumberplayground.test"] + glue = ["com.splendo.cucumberplayground.test"] ) class CucumberTests : CucumberAndroidJUnitRunner() \ No newline at end of file diff --git a/android/src/androidTest/kotlin/com/corrado4eyes/cucumberplayground/test/StepDefinitions.kt b/android/src/androidTest/kotlin/com/corrado4eyes/cucumberplayground/test/StepDefinitions.kt index 4e4f2c1..411b928 100644 --- a/android/src/androidTest/kotlin/com/corrado4eyes/cucumberplayground/test/StepDefinitions.kt +++ b/android/src/androidTest/kotlin/com/corrado4eyes/cucumberplayground/test/StepDefinitions.kt @@ -1,114 +1,80 @@ -package com.corrado4eyes.cucumberplayground.test +package com.splendo.cucumberplayground.test -import android.app.Activity -import android.content.Intent -import androidx.compose.ui.test.assertHasClickAction import androidx.compose.ui.test.assertIsDisplayed -import androidx.compose.ui.test.assertTextContains import androidx.compose.ui.test.junit4.createComposeRule import androidx.compose.ui.test.onNodeWithTag import androidx.compose.ui.test.onNodeWithText -import androidx.compose.ui.test.performClick -import androidx.compose.ui.test.performTextInput -import androidx.test.core.app.ActivityScenario -import androidx.test.platform.app.InstrumentationRegistry -import com.corrado4eyes.cucumber.errors.UIElementException -import com.corrado4eyes.cucumberplayground.android.MainActivity -import com.corrado4eyes.cucumberplayground.models.Strings -import com.corrado4eyes.cucumbershared.tests.Definitions +import androidx.compose.ui.test.performScrollToIndex +import com.splendo.cucumberplayground.models.Strings +import com.splendo.cucumbershared.tests.AppDefinitions +import com.splendo.kaluga.uitest.DefaultApplicationAdapter import io.cucumber.java8.En import io.cucumber.junit.WithJunitRule import org.junit.Rule @WithJunitRule class StepDefinitions : En { - - private val arguments = mutableMapOf() - private var scenario: ActivityScenario<*>? = null - @get:Rule(order = 0) val testRule = createComposeRule() + private val application = DefaultApplicationAdapter(testRule) init { - Definitions.values().forEach { + AppDefinitions.allCases.forEach { val definitionString = it.definition.definitionString when (it) { - Definitions.I_AM_IN_THE_EXPECT_VALUE_STRING_SCREEN -> Given(definitionString) { screenName: String -> - val screenTitleTag = when (screenName) { - Strings.Screen.Tag.login -> { - arguments["isLoggedIn"] = "false" - arguments["testEmail"] = "" - Strings.Screen.Title.login - } - - Strings.Screen.Tag.home -> { - arguments["isLoggedIn"] = "true" - Strings.Screen.Title.home - } - - else -> throw UIElementException.Screen.NotFound(screenName) - } - setLaunchScreen() - testRule.onNodeWithTag(screenTitleTag).assertIsDisplayed() + is AppDefinitions.CrossPlatform.IAmInScreen -> Given(definitionString) { screenName: String -> + val assertions = AppDefinitions.CrossPlatform.IAmInScreen( + "MainActivity", + application, + listOf(screenName) + ).runAndGetAssertions() + application.assertAll(assertions) } - Definitions.I_SEE_EXPECT_VALUE_STRING_TEXT -> Then(definitionString) { title: String -> - testRule.onNodeWithText(title).assertIsDisplayed() + is AppDefinitions.CrossPlatform.ISeeText -> Then(definitionString) { textViewTitle: String -> + val assertions = AppDefinitions.CrossPlatform.ISeeText(application, listOf(textViewTitle)).runAndGetAssertions() + application.assertAll(assertions) } - Definitions.I_SEE_THE_EXPECT_VALUE_STRING_BUTTON -> Then(definitionString) { buttonName: String -> - when (buttonName) { - Strings.Button.Text.login -> testRule.onNodeWithText(Strings.Button.Text.login).assertIsDisplayed().assertHasClickAction() - Strings.Button.Text.logout -> testRule.onNodeWithTag(Strings.Button.Text.logout).assertIsDisplayed().assertHasClickAction() - else -> throw UIElementException.Button.NotFound(buttonName) - } + is AppDefinitions.CrossPlatform.ISeeButton -> Then(definitionString) { buttonTitle: String -> + val assertions = AppDefinitions.CrossPlatform.ISeeButton(application, listOf(buttonTitle)).runAndGetAssertions() + application.assertAll(assertions) } - Definitions.I_SEE_THE_EXPECT_VALUE_STRING_SCREEN -> Then(definitionString) { screenName: String -> - Thread.sleep(1000) - when (screenName) { - Strings.Screen.Tag.login -> testRule.onNodeWithTag(Strings.Screen.Title.login).assertIsDisplayed() - Strings.Screen.Tag.home -> testRule.onNodeWithTag(Strings.Screen.Title.home).assertIsDisplayed() - else -> throw UIElementException.Screen.NotFound(screenName) - } + is AppDefinitions.CrossPlatform.ISeeScreen -> Then(definitionString) { screenTitle: String -> + val assertions = AppDefinitions.CrossPlatform.ISeeScreen(application, listOf(screenTitle)).runAndGetAssertions() + application.assertAll(assertions) } - Definitions.I_SEE_THE_EXPECT_VALUE_STRING_TEXT_FIELD_WITH_TEXT_EXPECT_VALUE_STRING -> Then(definitionString) { tag: String, text: String -> - val elementNode = try { - testRule.onNodeWithTag(tag).assertIsDisplayed() - } catch (e: AssertionError) { - throw UIElementException.TextField.NotFound(tag) - } - elementNode.assertTextContains(text) + is AppDefinitions.CrossPlatform.ISeeTextFieldWithText -> Then(definitionString) { textFieldTag: String, textFieldText: String -> + val assertions = AppDefinitions.CrossPlatform.ISeeTextFieldWithText(application, listOf(textFieldTag, textFieldText)).runAndGetAssertions() + application.assertAll(assertions) } - Definitions.I_TYPE_EXPECT_VALUE_STRING_IN_THE_EXPECT_VALUE_STRING_TEXT_FIELD -> When(definitionString) { textInput: String, tag: String, -> - testRule.onNodeWithText(tag).performTextInput(textInput) + is AppDefinitions.CrossPlatform.ITypeTextIntoTextField -> When(definitionString) { textInput: String, tag: String, -> + val assertions = AppDefinitions.CrossPlatform.ITypeTextIntoTextField(application, listOf(textInput, tag)).runAndGetAssertions() + application.assertAll(assertions) } - Definitions.I_TYPE_EXPECT_VALUE_STRING_IN_THE_EXPECT_VALUE_STRING_SECURE_TEXT_FIELD -> When(definitionString) { textInput: String, tag: String -> - testRule.onNodeWithText(tag).performTextInput(textInput) + is AppDefinitions.CrossPlatform.IPressTheButton -> When(definitionString) { buttonTag: String -> + val assertions = AppDefinitions.CrossPlatform.IPressTheButton(application, listOf(buttonTag)).runAndGetAssertions() + application.assertAll(assertions) } - Definitions.I_PRESS_THE_EXPECT_VALUE_STRING_BUTTON -> When(definitionString) { buttonName: String -> - val elementNode = try { - testRule.onNodeWithTag(buttonName).assertIsDisplayed() - } catch (e: AssertionError) { - throw UIElementException.TextField.NotFound(buttonName) - } - elementNode.performClick() + is AppDefinitions.CrossPlatform.SetLoggedInUserEmail -> Given(definitionString) { loggedInEmail: String -> + application.assertAll(AppDefinitions.CrossPlatform.SetLoggedInUserEmail(application, listOf(loggedInEmail)).runAndGetAssertions()) } - Definitions.EMAIL_IS_EXPECT_VALUE_STRING -> Given(definitionString) { loggedInEmail: String -> - arguments["testEmail"] = loggedInEmail + is AppDefinitions.Platform.ISeeValueInScrollView -> When(definitionString) { index: String -> + + // Platform implementation + testRule + .onNodeWithTag(Strings.ScrollView.Tag.homeScrollView) + .performScrollToIndex(index.toInt()) + + testRule.onNodeWithText(index).assertIsDisplayed() + + // Cross-platform implementation +// val element = application.findView(Strings.ScrollView.Tag.homeScrollView) +// element.swipeUntilIndex(index.toInt()) +// val text = application.findView(index) +// application.assert(text.isVisible()) } + } } } - - private fun setLaunchScreen() { - val instrumentation = InstrumentationRegistry.getInstrumentation() - launch( - Intent(instrumentation.targetContext, MainActivity::class.java) - .putExtra("isLoggedIn", arguments["isLoggedIn"]) - .putExtra("testEmail", arguments["testEmail"]) - ) - } - - private fun launch(intent: Intent) { - scenario = ActivityScenario.launch(intent) - } } \ No newline at end of file diff --git a/android/src/main/java/com/corrado4eyes/cucumberplayground/android/CucumberApp.kt b/android/src/main/java/com/corrado4eyes/cucumberplayground/android/CucumberApp.kt index 28a9a3d..c1702e9 100644 --- a/android/src/main/java/com/corrado4eyes/cucumberplayground/android/CucumberApp.kt +++ b/android/src/main/java/com/corrado4eyes/cucumberplayground/android/CucumberApp.kt @@ -1,7 +1,7 @@ -package com.corrado4eyes.cucumberplayground.android +package com.splendo.cucumberplayground.android import android.app.Application -import com.corrado4eyes.cucumberplayground.di.initKoin +import com.splendo.cucumberplayground.di.initKoin import com.splendo.kaluga.base.ApplicationHolder class CucumberApp : Application() { diff --git a/android/src/main/java/com/corrado4eyes/cucumberplayground/android/MainActivity.kt b/android/src/main/java/com/corrado4eyes/cucumberplayground/android/MainActivity.kt index 310cd7d..c6a1512 100644 --- a/android/src/main/java/com/corrado4eyes/cucumberplayground/android/MainActivity.kt +++ b/android/src/main/java/com/corrado4eyes/cucumberplayground/android/MainActivity.kt @@ -1,11 +1,11 @@ -package com.corrado4eyes.cucumberplayground.android +package com.splendo.cucumberplayground.android import android.content.Intent import android.os.Bundle import androidx.activity.compose.setContent import androidx.appcompat.app.AppCompatActivity -import com.corrado4eyes.cucumberplayground.models.DefaultTestConfiguration -import com.corrado4eyes.cucumberplayground.models.TestConfiguration +import com.splendo.cucumberplayground.models.DefaultTestConfiguration +import com.splendo.cucumberplayground.models.TestConfiguration class MainActivity : AppCompatActivity() { private lateinit var testConfiguration: TestConfiguration diff --git a/android/src/main/java/com/corrado4eyes/cucumberplayground/android/MainLayout.kt b/android/src/main/java/com/corrado4eyes/cucumberplayground/android/MainLayout.kt index 5cdf74d..ccd902c 100644 --- a/android/src/main/java/com/corrado4eyes/cucumberplayground/android/MainLayout.kt +++ b/android/src/main/java/com/corrado4eyes/cucumberplayground/android/MainLayout.kt @@ -1,4 +1,4 @@ -package com.corrado4eyes.cucumberplayground.android +package com.splendo.cucumberplayground.android import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.material.MaterialTheme @@ -8,12 +8,12 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.ui.Modifier import androidx.compose.ui.tooling.preview.Preview -import com.corrado4eyes.cucumberplayground.android.home.HomeLayout -import com.corrado4eyes.cucumberplayground.android.login.LoginLayout -import com.corrado4eyes.cucumberplayground.models.DefaultTestConfiguration -import com.corrado4eyes.cucumberplayground.models.TestConfiguration -import com.corrado4eyes.cucumberplayground.viewModels.main.AppNavigator -import com.corrado4eyes.cucumberplayground.viewModels.main.MainViewModel +import com.splendo.cucumberplayground.android.home.HomeLayout +import com.splendo.cucumberplayground.android.login.LoginLayout +import com.splendo.cucumberplayground.models.DefaultTestConfiguration +import com.splendo.cucumberplayground.models.TestConfiguration +import com.splendo.cucumberplayground.viewModels.main.AppNavigator +import com.splendo.cucumberplayground.viewModels.main.MainViewModel import com.splendo.kaluga.architecture.compose.state import com.splendo.kaluga.architecture.compose.viewModel.ViewModelComposable import org.koin.androidx.compose.koinViewModel diff --git a/android/src/main/java/com/corrado4eyes/cucumberplayground/android/MyApplicationTheme.kt b/android/src/main/java/com/corrado4eyes/cucumberplayground/android/MyApplicationTheme.kt index 04cecef..3351d52 100644 --- a/android/src/main/java/com/corrado4eyes/cucumberplayground/android/MyApplicationTheme.kt +++ b/android/src/main/java/com/corrado4eyes/cucumberplayground/android/MyApplicationTheme.kt @@ -1,4 +1,4 @@ -package com.corrado4eyes.cucumberplayground.android +package com.splendo.cucumberplayground.android import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.foundation.shape.RoundedCornerShape diff --git a/android/src/main/java/com/corrado4eyes/cucumberplayground/android/home/HomeLayout.kt b/android/src/main/java/com/corrado4eyes/cucumberplayground/android/home/HomeLayout.kt index ae217f4..38aa8ab 100644 --- a/android/src/main/java/com/corrado4eyes/cucumberplayground/android/home/HomeLayout.kt +++ b/android/src/main/java/com/corrado4eyes/cucumberplayground/android/home/HomeLayout.kt @@ -1,14 +1,21 @@ -package com.corrado4eyes.cucumberplayground.android.home +package com.splendo.cucumberplayground.android.home import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.items import androidx.compose.material.Button import androidx.compose.material.Text import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.platform.testTag import androidx.compose.ui.tooling.preview.Preview -import com.corrado4eyes.cucumberplayground.viewModels.home.HomeViewModel +import androidx.compose.ui.unit.dp +import com.splendo.cucumberplayground.models.Strings +import com.splendo.cucumberplayground.viewModels.home.HomeViewModel import com.splendo.kaluga.architecture.compose.viewModel.ViewModelComposable import org.koin.androidx.compose.koinViewModel @@ -19,15 +26,33 @@ fun HomeLayout() { Column(modifier = Modifier.fillMaxSize()) { Text( this@ViewModelComposable.screenTitle, - modifier = Modifier.testTag(this@ViewModelComposable.screenTitle) + modifier = Modifier.testTag(Strings.Screen.Tag.home) + ) + Text( + this@ViewModelComposable.getCurrentUser().email, + modifier = Modifier.testTag(this@ViewModelComposable.getCurrentUser().email) ) - Text(this@ViewModelComposable.getCurrentUser().email) Button( this@ViewModelComposable::logout, - modifier = Modifier.testTag(this@ViewModelComposable.buttonTitle) + modifier = Modifier.testTag(Strings.Button.Tag.logout) ) { Text(this@ViewModelComposable.buttonTitle) } + + LazyColumn( + horizontalAlignment = Alignment.CenterHorizontally, + modifier = Modifier + .fillMaxWidth() + .height(200.dp) + .testTag(Strings.ScrollView.Tag.homeScrollView) + ) { + items( + viewModel.scrollableItems, + key = { it } + ) { + Text(it.toString()) + } + } } } } diff --git a/android/src/main/java/com/corrado4eyes/cucumberplayground/android/login/LoginLayout.kt b/android/src/main/java/com/corrado4eyes/cucumberplayground/android/login/LoginLayout.kt index db3721e..0db920a 100644 --- a/android/src/main/java/com/corrado4eyes/cucumberplayground/android/login/LoginLayout.kt +++ b/android/src/main/java/com/corrado4eyes/cucumberplayground/android/login/LoginLayout.kt @@ -1,4 +1,4 @@ -package com.corrado4eyes.cucumberplayground.android.login +package com.splendo.cucumberplayground.android.login import androidx.compose.foundation.layout.Column import androidx.compose.material.Button @@ -14,7 +14,8 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.testTag import androidx.compose.ui.text.input.TextFieldValue import androidx.compose.ui.tooling.preview.Preview -import com.corrado4eyes.cucumberplayground.viewModels.login.LoginViewModel +import com.splendo.cucumberplayground.models.Strings +import com.splendo.cucumberplayground.viewModels.login.LoginViewModel import com.splendo.kaluga.architecture.compose.state import com.splendo.kaluga.architecture.compose.viewModel.ViewModelComposable import com.splendo.kaluga.architecture.observable.StateFlowInitializedSubject @@ -26,18 +27,21 @@ fun LoginLayout() { ViewModelComposable(viewModel) { val isLoading by this.isLoading.state() Column { - Text(text = this@ViewModelComposable.screenTitle, modifier = Modifier.testTag("Login screen")) + Text( + text = this@ViewModelComposable.screenTitle, + modifier = Modifier.testTag(Strings.Screen.Tag.login) + ) CustomTextField( value = this@ViewModelComposable.emailText, label = "Email", - modifier = Modifier.testTag("Email") + modifier = Modifier.testTag(Strings.TextField.Tag.email) ) val emailErrorText by this@ViewModelComposable.emailErrorText.state() Text(text = emailErrorText, color = Color.Red) CustomTextField( value = this@ViewModelComposable.passwordText, label = "Password", - modifier = Modifier.testTag("Password") + modifier = Modifier.testTag(Strings.TextField.Tag.password) ) val passwordErrorText by this@ViewModelComposable.passwordErrorText.state() Text(text = passwordErrorText, color = Color.Red) @@ -50,9 +54,9 @@ fun LoginLayout() { Button( this@ViewModelComposable::login, - modifier = Modifier.testTag("Login") + modifier = Modifier.testTag(Strings.Button.Tag.login) ) { - Text("Login") + Text(viewModel.buttonTitle) } } } diff --git a/build.gradle.kts b/build.gradle.kts index deac9e0..1566e67 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,13 +1,14 @@ plugins { //trick: for the same plugin versions in all sub-modules - id("com.android.application").version("8.0.2").apply(false) - id("com.android.library").version("8.0.2").apply(false) - kotlin("android").version("1.8.21").apply(false) - kotlin("multiplatform").version("1.8.21").apply(false) + id("com.android.application").version("8.3.0-alpha07").apply(false) + id("com.android.library").version("8.3.0-alpha07").apply(false) + kotlin("android").version("1.9.10").apply(false) + kotlin("multiplatform").version("1.9.10").apply(false) } buildscript { repositories { + mavenLocal() mavenCentral() } diff --git a/cucumber/build.gradle.kts b/cucumber/build.gradle.kts deleted file mode 100644 index 5e95c2b..0000000 --- a/cucumber/build.gradle.kts +++ /dev/null @@ -1,122 +0,0 @@ -import org.jetbrains.kotlin.gradle.targets.native.tasks.PodGenTask - -plugins { - kotlin("multiplatform") - kotlin("native.cocoapods") - id("com.android.library") -} - -kotlin { - android { - compilations.all { - kotlinOptions { - jvmTarget = "11" - } - } - } - iosX64() - iosArm64() - iosSimulatorArm64() - - cocoapods { - summary = "Wrapper for Cucumberish" - homepage = "https://github.com/splendo/CucumberMultiplatform" - version = "1.0" - ios.deploymentTarget = "14.1" - framework { - baseName = "cucumber" - } - pod("Cucumberish") { - extraOpts = listOf("-Xforeign-exception-mode", "objc-wrap") - } - } - - sourceSets { - val commonMain by getting - val commonTest by getting { - dependencies { - val kalugaVersion: String by project - - implementation(kotlin("test")) - implementation("com.splendo.kaluga:test-utils-base:$kalugaVersion") - } - } - val androidMain by getting { - dependencies { - implementation("io.cucumber:cucumber-java8:4.8.1") - } - } - val androidUnitTest by getting - val iosX64Main by getting - val iosArm64Main by getting - val iosSimulatorArm64Main by getting - val iosMain by creating { - dependsOn(commonMain) - iosX64Main.dependsOn(this) - iosArm64Main.dependsOn(this) - iosSimulatorArm64Main.dependsOn(this) - } - val iosX64Test by getting - val iosArm64Test by getting - val iosSimulatorArm64Test by getting - val iosTest by creating { - dependsOn(commonTest) - iosX64Test.dependsOn(this) - iosArm64Test.dependsOn(this) - iosSimulatorArm64Test.dependsOn(this) - } - } -} - -android { - namespace = "com.corrado4eyes.cucumber" - compileSdk = 33 - defaultConfig { - minSdk = 29 - } - compileOptions { - sourceCompatibility = JavaVersion.VERSION_11 - targetCompatibility = JavaVersion.VERSION_11 - } -} - -/** - * Custom gradle task since Cucumberish library targets iOS 8. User running on the latest macOS version will have errors when syncing since iOS 8 won't be a valid target anymore. - * This script will override the target version to the given string. - */ -tasks.withType().configureEach { - doLast { - val xcodeprojFiles = listOf( - "Pods/Pods.xcodeproj", - "synthetic.xcodeproj", - ) - - for (xcodeprojFile in xcodeprojFiles) { - logger.lifecycle("Processing $xcodeprojFile") - val file = project.buildDir.resolve("cocoapods/synthetic/${family.name}/$xcodeprojFile/project.pbxproj") - setIosDeploymentTarget(file) - } - } -} - -fun Project.setIosDeploymentTarget( - xcodeprojFile: File, - source: String = "8.0", - target: String = "14.1", -) { - val lines = xcodeprojFile.readLines() - val out = xcodeprojFile.bufferedWriter() - out.use { - for (line in lines) { - out.write(line.replace("IPHONEOS_DEPLOYMENT_TARGET = $source;", "IPHONEOS_DEPLOYMENT_TARGET = $target;")) - out.write(("\n")) - } - } -} - -android { - compileOptions { - sourceCompatibility = JavaVersion.VERSION_11 - targetCompatibility = JavaVersion.VERSION_11 - } -} \ No newline at end of file diff --git a/cucumber/cucumber.podspec b/cucumber/cucumber.podspec deleted file mode 100644 index ad432c0..0000000 --- a/cucumber/cucumber.podspec +++ /dev/null @@ -1,39 +0,0 @@ -Pod::Spec.new do |spec| - spec.name = 'cucumber' - spec.version = '1.0' - spec.homepage = 'https://github.com/splendo/CucumberMultiplatform' - spec.source = { :http=> ''} - spec.authors = '' - spec.license = '' - spec.summary = 'Wrapper for Cucumberish' - spec.vendored_frameworks = 'build/cocoapods/framework/cucumber.framework' - spec.libraries = 'c++' - spec.ios.deployment_target = '14.1' - spec.dependency 'Cucumberish' - - spec.pod_target_xcconfig = { - 'KOTLIN_PROJECT_PATH' => ':cucumber', - 'PRODUCT_MODULE_NAME' => 'cucumber', - } - - spec.script_phases = [ - { - :name => 'Build cucumber', - :execution_position => :before_compile, - :shell_path => '/bin/sh', - :script => <<-SCRIPT - if [ "YES" = "$OVERRIDE_KOTLIN_BUILD_IDE_SUPPORTED" ]; then - echo "Skipping Gradle build task invocation due to OVERRIDE_KOTLIN_BUILD_IDE_SUPPORTED environment variable set to \"YES\"" - exit 0 - fi - set -ev - REPO_ROOT="$PODS_TARGET_SRCROOT" - "$REPO_ROOT/../gradlew" -p "$REPO_ROOT" $KOTLIN_PROJECT_PATH:syncFramework \ - -Pkotlin.native.cocoapods.platform=$PLATFORM_NAME \ - -Pkotlin.native.cocoapods.archs="$ARCHS" \ - -Pkotlin.native.cocoapods.configuration="$CONFIGURATION" - SCRIPT - } - ] - -end \ No newline at end of file diff --git a/cucumber/src/androidMain/kotlin/com/corrado4eyes/cucumber/DefaultGherkinRunner.kt b/cucumber/src/androidMain/kotlin/com/corrado4eyes/cucumber/DefaultGherkinRunner.kt deleted file mode 100644 index 61b68db..0000000 --- a/cucumber/src/androidMain/kotlin/com/corrado4eyes/cucumber/DefaultGherkinRunner.kt +++ /dev/null @@ -1,137 +0,0 @@ -package com.corrado4eyes.cucumber - -import io.cucumber.java8.En - -actual val EXPECT_VALUE_STRING = "{string}" - -actual class GherkinLambda0 (private val lambda: () -> Unit) : () -> Unit, GherkinLambda { - override fun invoke() { - lambda() - } -} - -actual class GherkinLambda1 (private val lambda: (String) -> Unit) : (String) -> Unit, GherkinLambda { - override fun invoke(p0: String) { - lambda(p0) - } -} - -actual class GherkinLambda2 (private val lambda: (String, String) -> Unit) : (String, String) -> Unit, GherkinLambda { - override fun invoke(p0: String, p1: String) { - lambda(p0, p1) - } -} - -//actual class GherkinLambda3 (private val lambda: (String, String, String) -> Unit) : (String, String, String) -> Unit, GherkinLambda { -// override fun invoke(p0: String, p1: String, p2: String) { -// lambda(p0, p1, p2) -// } -//} -// -//actual class GherkinLambda4 (private val lambda: (String, String, String, String) -> Unit) : (String, String, String, String) -> Unit, GherkinLambda { -// override fun invoke(p0: String, p1: String, p2: String, p3: String) { -// lambda(p0, p1, p2, p3) -// } -//} -// -//actual class GherkinLambda5 (private val lambda: (String, String, String, String, String) -> Unit) : (String, String, String, String, String) -> Unit, GherkinLambda { -// override fun invoke(p0: String, p1: String, p2: String, p3: String, p4: String) { -// lambda(p0, p1, p2, p3, p4) -// } -//} -// -//actual class GherkinLambda6 (private val lambda: (String, String, String, String, String, String) -> Unit) : (String, String, String, String, String, String) -> Unit, GherkinLambda { -// override fun invoke(p0: String, p1: String, p2: String, p3: String, p4: String, p5: String) { -// lambda(p0, p1, p2, p3, p4, p5) -// } -//} -// -//actual class GherkinLambda7 (private val lambda: (String, String, String, String, String, String, String) -> Unit) : (String, String, String, String, String, String, String) -> Unit, GherkinLambda { -// override fun invoke(p0: String, p1: String, p2: String, p3: String, p4: String, p5: String, p6: String) { -// lambda(p0, p1, p2, p3, p4, p5, p6) -// } -//} -// -//actual class GherkinLambda8 (private val lambda: (String, String, String, String, String, String, String, String) -> Unit) : (String, String, String, String, String, String, String, String) -> Unit, GherkinLambda { -// override fun invoke(p0: String, p1: String, p2: String, p3: String, p4: String, p5: String, p6: String, p7: String) { -// lambda(p0, p1, p2, p3, p4, p5, p6, p7) -// } -//} -// -//actual class GherkinLambda9 (private val lambda: (String, String, String, String, String, String, String, String, String) -> Unit) : (String, String, String, String, String, String, String, String, String) -> Unit, GherkinLambda { -// override fun invoke(p0: String, p1: String, p2: String, p3: String, p4: String, p5: String, p6: String, p7: String, p8: String) { -// lambda(p0, p1, p2, p3, p4, p5, p6, p7, p8) -// } -//} - -actual fun given(definitionString: String, lambda: GherkinLambda0) { - object : En { - init { - Given(definitionString, lambda) - } - } -} - -actual fun given(definitionString: String, lambda: GherkinLambda1) { - object : En { - init { - Given(definitionString, lambda) - } - } -} - -actual fun given(definitionString: String, lambda: GherkinLambda2) { - object : En { - init { - Given(definitionString, lambda) - } - } -} - -actual fun `when`(definitionString: String, lambda: GherkinLambda0) { - object : En { - init { - When(definitionString, lambda) - } - } -} - -actual fun `when`(definitionString: String, lambda: GherkinLambda1) { - object : En { - init { - When(definitionString, lambda) - } - } -} - -actual fun `when`(definitionString: String, lambda: GherkinLambda2) { - object : En { - init { - When(definitionString, lambda) - } - } -} - -actual fun then(definitionString: String, lambda: GherkinLambda0) { - object : En { - init { - Then(definitionString, lambda) - } - } -} - -actual fun then(definitionString: String, lambda: GherkinLambda1) { - object : En { - init { - Then(definitionString, lambda) - } - } -} - -actual fun then(definitionString: String, lambda: GherkinLambda2) { - object : En { - init { - Then(definitionString, lambda) - } - } -} diff --git a/cucumber/src/commonMain/kotlin/com/corrado4eyes/cucumber/CucumberDefinition.kt b/cucumber/src/commonMain/kotlin/com/corrado4eyes/cucumber/CucumberDefinition.kt deleted file mode 100644 index 276ff96..0000000 --- a/cucumber/src/commonMain/kotlin/com/corrado4eyes/cucumber/CucumberDefinition.kt +++ /dev/null @@ -1,155 +0,0 @@ -package com.corrado4eyes.cucumber - -typealias Scenario = CucumberDefinition.Descriptive.Example - -sealed interface Definition { - val definitionString: String - sealed interface Descriptive : Definition { - - /** - * Represents a whole feature to be described. It groups the other descriptive keywords. - */ - interface Feature : Descriptive { - val message: String - } - - /** - * Groups more scenerio that tests a similar flow with a certain rule - */ - interface Rule : Descriptive { - override val definitionString: String - } - - /** - * Represents a Scenario, hence a group of steps. - */ - interface Example : Descriptive { - val message: String - } - } - - sealed interface Step : Definition { - override val definitionString: String - - interface Given : Step - interface When : Step - interface Then : Step - } -} - -sealed class CucumberDefinition(val definitionString: String) { - - sealed class Descriptive(definitionString: String) : Definition.Descriptive, CucumberDefinition(definitionString) { - - /** - * Represents a whole feature to be described. It groups the other descriptive keywords. - */ - class Feature(override val message: String) : Definition.Descriptive.Feature, Descriptive(message) - - /** - * Groups more scenerio that tests a similar flow with a certain rule - */ - class Rule(definitionString: String) : Definition.Descriptive.Rule, Descriptive(definitionString) - - /** - * Represents a Scenario, hence a group of steps. - */ - class Example(override val message: String) : Definition.Descriptive.Example, Descriptive(message) - } - - sealed class Step(definitionString: String) : Definition.Step, CucumberDefinition(definitionString) { - class Given(definitionString: String) : Definition.Step.Given, Step(definitionString) - class When(definitionString: String) : Definition.Step.When, Step(definitionString) - class Then(definitionString: String) : Definition.Step.Then, Step(definitionString) - } - - sealed class SubStep(definitionString: String) : CucumberDefinition(definitionString) { - class And(definitionString: String) : SubStep(definitionString) - } -} - -interface GherkinTestCase { - val definition: D -} - -/** - * How each platform represent an expected parameter in a definitionString. - */ -expect val EXPECT_VALUE_STRING: String - -/** - * An interface that marks a class as a GherkingLambda, hence a lambda defined on each module that takes specific arguments, or a specific amount - * of arguments and does some assertion on the views. - * - * On Android a step like Given or When will take a definitionString and an amount of arguments, while on iOS each step definition will always take a definitionString and a CCIStepBody. - * Therefore there will be multiple GherkinLambda to reflect how Android works. - */ -interface GherkinLambda - -/** - * Lambda that takes 0 parameters - */ -expect class GherkinLambda0 : GherkinLambda - -/** - * Lambda that takes 1 parameters - */ -expect class GherkinLambda1 : GherkinLambda - -/** - * Lambda that takes 2 parameters - */ -expect class GherkinLambda2 : GherkinLambda -//expect class GherkinLambda3 : GherkinLambda -//expect class GherkinLambda4 : GherkinLambda -//expect class GherkinLambda5 : GherkinLambda -//expect class GherkinLambda6 : GherkinLambda -//expect class GherkinLambda7 : GherkinLambda -//expect class GherkinLambda8 : GherkinLambda -//expect class GherkinLambda9 : GherkinLambda - - -/** - * Method that wraps the given method that takes 0 parameters - */ -expect fun given(definitionString: String, lambda: GherkinLambda0) - -/** - * Method that wraps the given method that takes 1 parameter - */ -expect fun given(definitionString: String, lambda: GherkinLambda1) - -/** - * Method that wraps the Given method that takes 2 parameters - */ -expect fun given(definitionString: String, lambda: GherkinLambda2) - -/** - * Method that wraps the Then method that takes 0 parameters - */ -expect fun then(definitionString: String, lambda: GherkinLambda0) - -/** - * Method that wraps the Then method that takes 1 parameter - */ -expect fun then(definitionString: String, lambda: GherkinLambda1) - -/** - * Method that wraps the Then method that takes 2 parameters - */ -expect fun then(definitionString: String, lambda: GherkinLambda2) - -/** - * Method that wraps the When method that takes 0 parameters - */ -expect fun `when`(definitionString: String, lambda: GherkinLambda0) - -/** - * Method that wraps the Then method that takes 1 parameter - */ -expect fun `when`(definitionString: String, lambda: GherkinLambda1) - -/** - * Method that wraps the Then method that takes 2 parameters - */ -expect fun `when`(definitionString: String, lambda: GherkinLambda2) diff --git a/cucumber/src/commonMain/kotlin/com/corrado4eyes/cucumber/errors/UIElementError.kt b/cucumber/src/commonMain/kotlin/com/corrado4eyes/cucumber/errors/UIElementError.kt deleted file mode 100644 index 9405ce7..0000000 --- a/cucumber/src/commonMain/kotlin/com/corrado4eyes/cucumber/errors/UIElementError.kt +++ /dev/null @@ -1,35 +0,0 @@ -package com.corrado4eyes.cucumber.errors - -enum class UIElementType { - SCREEN, - BUTTON, - TEXT, - TEXT_FIELD -} - -class ArgumentNotFound(argumentName: String) : Throwable() { - override val message: String = "Couldn't find the argument called $argumentName" -} - -sealed class UIElementException : Throwable() { - abstract val elementTitle: String - abstract val elementType: UIElementType - override val message: String by lazy { "Couldn't find ${elementType.name} $elementTitle" } - - sealed class Screen : UIElementException() { - override val elementType: UIElementType = UIElementType.SCREEN - data class NotFound(override val elementTitle: String) : Screen() - } - sealed class Button : UIElementException() { - override val elementType: UIElementType = UIElementType.BUTTON - data class NotFound(override val elementTitle: String) : Button() - } - sealed class Text : UIElementException() { - override val elementType: UIElementType = UIElementType.TEXT - data class NotFound(override val elementTitle: String) : Text() - } - sealed class TextField : UIElementException() { - override val elementType: UIElementType = UIElementType.TEXT_FIELD - data class NotFound(override val elementTitle: String) : TextField() - } -} diff --git a/cucumber/src/commonTest/kotlin/com/corrado4eyes/cucumber/CucumberRunnerTest.kt b/cucumber/src/commonTest/kotlin/com/corrado4eyes/cucumber/CucumberRunnerTest.kt deleted file mode 100644 index d50a357..0000000 --- a/cucumber/src/commonTest/kotlin/com/corrado4eyes/cucumber/CucumberRunnerTest.kt +++ /dev/null @@ -1,38 +0,0 @@ -package com.corrado4eyes.cucumber - -import kotlin.test.Test - -sealed class CucumberDefinitionMock { - class GivenMock( - override val definitionString: String, - ) : Definition.Step.Given, CucumberDefinitionMock() - - class ThenSingleMock( - override val definitionString: String, - mockParameter: String - ) : Definition.Step.Then, CucumberDefinitionMock() -} - -sealed class StubTestCase : GherkinTestCase { - class TestGivenCase : StubTestCase() { - override val definition: CucumberDefinitionMock.GivenMock = CucumberDefinitionMock.GivenMock("Test Given with GherkinLambdaMock0") - } - class TestThenCaseWithSingleParameter : StubTestCase() { - companion object { - const val EXPECTED_VALUE = "TEST_THEN" - } - override val definition: CucumberDefinitionMock.ThenSingleMock = CucumberDefinitionMock.ThenSingleMock("Test Then with GherkinLambdaMock1", mockParameter = EXPECTED_VALUE) - } -} - -class CucumberRunnerTest { - @Test - fun test_given_case() { - TODO("Implement once there is a common implementation for Gherkin Definitions") - } - - @Test - fun test_then_case_with_parameter() { - TODO("Implement once there is a common implementation for Gherkin Definitions") - } -} diff --git a/cucumber/src/iosMain/kotlin/com/corrado4eyes/cucumber/DefaultGherkinRunner.kt b/cucumber/src/iosMain/kotlin/com/corrado4eyes/cucumber/DefaultGherkinRunner.kt deleted file mode 100644 index 3b458a4..0000000 --- a/cucumber/src/iosMain/kotlin/com/corrado4eyes/cucumber/DefaultGherkinRunner.kt +++ /dev/null @@ -1,50 +0,0 @@ -package com.corrado4eyes.cucumber - -import cocoapods.Cucumberish.CCIStepBody -import cocoapods.Cucumberish.Given -import cocoapods.Cucumberish.Then -import cocoapods.Cucumberish.When - -actual val EXPECT_VALUE_STRING = "\\\"(.*)\\\"" - -actual class GherkinLambda0(val lambda: CCIStepBody) : GherkinLambda - -actual class GherkinLambda1(val lambda: CCIStepBody) : GherkinLambda - -actual class GherkinLambda2(val lambda: CCIStepBody) : GherkinLambda - -actual fun given(definitionString: String, lambda: GherkinLambda0) { - Given(definitionString, lambda.lambda) -} - -actual fun given(definitionString: String, lambda: GherkinLambda1) { - Given(definitionString, lambda.lambda) -} - -actual fun given(definitionString: String, lambda: GherkinLambda2) { - Given(definitionString, lambda.lambda) -} - -actual fun then(definitionString: String, lambda: GherkinLambda0) { - Then(definitionString, lambda.lambda) -} - -actual fun then(definitionString: String, lambda: GherkinLambda1) { - Then(definitionString, lambda.lambda) -} - -actual fun then(definitionString: String, lambda: GherkinLambda2) { - Then(definitionString, lambda.lambda) -} - -actual fun `when`(definitionString: String, lambda: GherkinLambda0) { - When(definitionString, lambda.lambda) -} - -actual fun `when`(definitionString: String, lambda: GherkinLambda1) { - When(definitionString, lambda.lambda) -} - -actual fun `when`(definitionString: String, lambda: GherkinLambda2) { - When(definitionString, lambda.lambda) -} diff --git a/cucumberShared/build.gradle.kts b/cucumberShared/build.gradle.kts index 9f6e57c..523975d 100644 --- a/cucumberShared/build.gradle.kts +++ b/cucumberShared/build.gradle.kts @@ -1,12 +1,18 @@ +import co.touchlab.skie.configuration.DefaultArgumentInterop +import co.touchlab.skie.configuration.EnumInterop +import co.touchlab.skie.configuration.ExperimentalFeatures +import co.touchlab.skie.configuration.FlowInterop +import co.touchlab.skie.configuration.SealedInterop +import co.touchlab.skie.configuration.SuspendInterop +import java.util.* import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget -import org.jetbrains.kotlin.konan.file.File -import org.jetbrains.kotlin.konan.properties.Properties -import org.jetbrains.kotlin.konan.properties.hasProperty import org.jetbrains.kotlin.konan.properties.loadProperties plugins { kotlin("multiplatform") + kotlin("plugin.serialization") id("com.android.library") + id("co.touchlab.skie") version "0.5.0" } kotlin { @@ -24,13 +30,14 @@ kotlin { framework { transitiveExport = true export(project(":shared")) - export(project(":cucumber")) + export("com.splendo.kaluga.cucumber:kaluga-cucumber") + export("com.splendo.kaluga.uitest:kaluga-uitest") baseName = "shared" - linkFrameworkSearchPaths("$projectDir/../cucumber") + linkFrameworkSearchPaths("$rootDir/dependencies/kaluga-cucumber/kaluga-cucumber") getTest("DEBUG").apply { - linkFrameworkSearchPaths("$projectDir/../cucumber") + linkFrameworkSearchPaths("$rootDir/dependencies/kaluga-cucumber/kaluga-cucumber") } } } @@ -44,7 +51,10 @@ kotlin { val commonMain by getting { dependencies { api(project(":shared")) - api(project(":cucumber")) + api("com.splendo.kaluga.uitest:kaluga-uitest") + api("com.splendo.kaluga.cucumber:kaluga-cucumber") + + implementation("co.touchlab.skie:configuration-annotations:0.5.0") } } val commonTest by getting { @@ -76,7 +86,7 @@ kotlin { } android { - namespace = "com.corrado4eyes.cucumbershared" + namespace = "com.splendo.cucumbershared" compileSdk = 33 defaultConfig { minSdk = 29 @@ -87,6 +97,24 @@ android { } } +skie { + analytics { + disableUpload.set(true) + enabled.set(false) + } + + features { + group { + DefaultArgumentInterop.Enabled(false) + EnumInterop.Enabled(false) + ExperimentalFeatures.Enabled(false) + FlowInterop.Enabled(false) + SealedInterop.Enabled(false) + SuspendInterop.Enabled(false) + } + } +} + val KotlinNativeTarget.targetType: String get() { val isSimulatorTarget: Boolean = konanTarget is org.jetbrains.kotlin.konan.target.KonanTarget.IOS_X64 || konanTarget is org.jetbrains.kotlin.konan.target.KonanTarget.IOS_SIMULATOR_ARM64 @@ -95,8 +123,7 @@ val KotlinNativeTarget.targetType: String get() { fun Properties.getPropertyOrNull(key: String) = if (hasProperty(key)) getProperty(key) else null fun Properties.getListProperty(key: String) = getPropertyOrNull(key).orEmpty().split("(\" )?\"".toRegex()).filter { it.isNotEmpty() } - -fun Properties.getFrameworkSearchPaths() = (getListProperty("FRAMEWORK_SEARCH_PATHS") + getPropertyOrNull("CONFIGURATION_BUILD_DIR")).filterNotNull() +fun Properties.getFrameworkSearchPaths() = (listOf(getProperty("FRAMEWORK_SEARCH_PATHS")) + getProperty("CONFIGURATION_BUILD_DIR")).filterNotNull() /** * Gets the [Properties] file of a [KotlinNativeTarget] for a given dependency loaded by the `dependencies` module @@ -107,6 +134,7 @@ fun Properties.getFrameworkSearchPaths() = (getListProperty("FRAMEWORK_SEARCH_PA fun KotlinNativeTarget.getPropertiesForDependency(dependency: String, pathToIosDependencies: String): Properties { val path = "$pathToIosDependencies/build/cocoapods/buildSettings/build-settings-$targetType-$dependency.properties" val file = org.jetbrains.kotlin.konan.file.File(path) + return file.loadProperties() } @@ -120,7 +148,8 @@ fun KotlinNativeTarget.getPropertiesForDependency(dependency: String, pathToIosD fun Project.dependenciesFromDefs(pathToIosDependencies: String, includeDependency: (String) -> Boolean = { true }): List { // Load all .def files from the defs folder to determine the list of dependencies val defs = File("$pathToIosDependencies/build/cocoapods/defs") - return defs.listFilesOrEmpty.mapNotNull { dependency -> + val files = defs.listFiles()?.toList() ?: emptyList() + return files.mapNotNull { dependency -> val fileName = dependency.name.removeSuffix(".${dependency.extension}") when { dependency.extension != "def" -> null @@ -138,8 +167,14 @@ fun Project.dependenciesFromDefs(pathToIosDependencies: String, includeDependenc */ fun KotlinNativeTarget.createFrameworkSearchPath(pathToIosDependencies: String, includeDependency: (String) -> Boolean = { true }): Set { val dependencies = dependenciesFromDefs(pathToIosDependencies, includeDependency) + println("Dependencies: $dependencies") + return dependencies.map { dependency -> val properties = getPropertiesForDependency(dependency, pathToIosDependencies) + println("Found properties = ${properties.stringPropertyNames()}") +// listOfNotNull(properties.getProperty("FRAMEWORK_SEARCH_PATHS") + properties.getProperty("CONFIGURATION_BUILD_DIR")).also { +// println("FrameworkSearchPaths = ${properties.stringPropertyNames()}") +// } properties.getFrameworkSearchPaths() }.flatten().toSet() } @@ -150,7 +185,10 @@ fun KotlinNativeTarget.createFrameworkSearchPath(pathToIosDependencies: String, * @param includeDependency returns whether a dependency with a given name should be added to the `NativeBinary`. */ fun org.jetbrains.kotlin.gradle.plugin.mpp.NativeBinary.linkFrameworkSearchPaths(pathToIosDependencies: String, includeDependency: (String) -> Boolean = { true }) { + println("PathToIosDependencies: $pathToIosDependencies") val frameworkSearchPaths = target.createFrameworkSearchPath(pathToIosDependencies, includeDependency) + println("FrameworkSearchPaths: $frameworkSearchPaths") + // Add all framework search paths linkerOpts(frameworkSearchPaths.map { "-F$it" }) @@ -167,4 +205,4 @@ fun org.jetbrains.kotlin.gradle.plugin.mpp.NativeBinary.linkFrameworkSearchPaths linkerOpts("-rpath", it) } } -} \ No newline at end of file +} diff --git a/cucumberShared/src/commonMain/kotlin/com/corrado4eyes/cucumbershared/CucumberShared.kt b/cucumberShared/src/commonMain/kotlin/com/corrado4eyes/cucumbershared/CucumberShared.kt index 0a61698..9a4c084 100644 --- a/cucumberShared/src/commonMain/kotlin/com/corrado4eyes/cucumbershared/CucumberShared.kt +++ b/cucumberShared/src/commonMain/kotlin/com/corrado4eyes/cucumbershared/CucumberShared.kt @@ -1 +1 @@ -package com.corrado4eyes.cucumbershared +package com.splendo.cucumbershared diff --git a/cucumberShared/src/commonMain/kotlin/com/corrado4eyes/cucumbershared/tests/TestCase.kt b/cucumberShared/src/commonMain/kotlin/com/corrado4eyes/cucumbershared/tests/TestCase.kt index d811af6..0b9cd8b 100644 --- a/cucumberShared/src/commonMain/kotlin/com/corrado4eyes/cucumbershared/tests/TestCase.kt +++ b/cucumberShared/src/commonMain/kotlin/com/corrado4eyes/cucumbershared/tests/TestCase.kt @@ -1,40 +1,248 @@ -package com.corrado4eyes.cucumbershared.tests - -import com.corrado4eyes.cucumber.CucumberDefinition -import com.corrado4eyes.cucumber.Definition -import com.corrado4eyes.cucumber.EXPECT_VALUE_STRING -import com.corrado4eyes.cucumber.GherkinTestCase - -enum class Definitions(override val definition: Definition): GherkinTestCase { - I_AM_IN_THE_EXPECT_VALUE_STRING_SCREEN( - CucumberDefinition.Step.Given("I am in the $EXPECT_VALUE_STRING screen") - ), - I_SEE_EXPECT_VALUE_STRING_TEXT( - CucumberDefinition.Step.Then("I see $EXPECT_VALUE_STRING text") - ), - I_SEE_THE_EXPECT_VALUE_STRING_BUTTON( - CucumberDefinition.Step.Then("I see the $EXPECT_VALUE_STRING button") - ), - I_SEE_THE_EXPECT_VALUE_STRING_SCREEN( - CucumberDefinition.Step.Then("I see the $EXPECT_VALUE_STRING screen") - ), - I_SEE_THE_EXPECT_VALUE_STRING_TEXT_FIELD_WITH_TEXT_EXPECT_VALUE_STRING( - CucumberDefinition.Step.Then("I see the $EXPECT_VALUE_STRING textfield with text $EXPECT_VALUE_STRING") - ), - I_TYPE_EXPECT_VALUE_STRING_IN_THE_EXPECT_VALUE_STRING_TEXT_FIELD( - CucumberDefinition.Step.When("I type $EXPECT_VALUE_STRING in the $EXPECT_VALUE_STRING textfield") - ), - I_TYPE_EXPECT_VALUE_STRING_IN_THE_EXPECT_VALUE_STRING_SECURE_TEXT_FIELD( - CucumberDefinition.Step.When("I type $EXPECT_VALUE_STRING in the $EXPECT_VALUE_STRING secure textfield") - ), - I_PRESS_THE_EXPECT_VALUE_STRING_BUTTON( - CucumberDefinition.Step.When("I press the $EXPECT_VALUE_STRING button") - ), - EMAIL_IS_EXPECT_VALUE_STRING( - CucumberDefinition.Step.Given("Email is $EXPECT_VALUE_STRING") - ); +package com.splendo.cucumbershared.tests + +import co.touchlab.skie.configuration.annotations.SealedInterop +import com.splendo.cucumberplayground.models.Strings +import com.splendo.kaluga.cucumber.CucumberDefinition +import com.splendo.kaluga.cucumber.Definition +import com.splendo.kaluga.cucumber.EXPECT_VALUE_STRING +import com.splendo.kaluga.uitest.ApplicationAdapter +import com.splendo.kaluga.uitest.ApplicationArguments +import com.splendo.kaluga.uitest.AssertionResult +import com.splendo.kaluga.uitest.Node +import com.splendo.kaluga.uitest.TestCase +import com.splendo.kaluga.uitest.TimeoutDuration +import com.splendo.kaluga.uitest.errors.UIElementException + +@SealedInterop.Enabled +sealed class AppDefinitions : TestCase { + abstract val definition: Definition companion object { - val allCases: List = Definitions.values().toList() + private val defaultApplicationAdapter = object : ApplicationAdapter { + override val applicationArguments: ApplicationArguments + get() = TODO("Not yet implemented") + + override fun launch(identifier: String?, arguments: Map) { + TODO("Not yet implemented") + } + + override fun findViewByTag(tag: String): Node { + TODO("Not yet implemented") + } + + override fun findViewByText(text: String): Node { + TODO("Not yet implemented") + } + + override fun tearDown() { + TODO("Not yet implemented") + } + + override fun assert(assertionResult: AssertionResult) { + TODO("Not yet implemented") + } + + override fun assertUntil( + timeout: TimeoutDuration, + blockAssertionResult: () -> AssertionResult + ) { + TODO("Not yet implemented") + } + + override fun assertAll(assertions: List) { + TODO("Not yet implemented") + } + + override fun get(key: String): String? { + TODO("Not yet implemented") + } + + override fun set(key: String, value: String) { + TODO("Not yet implemented") + } + } + + val allCases = listOf( + CrossPlatform.IAmInScreen("", defaultApplicationAdapter, listOf()), + CrossPlatform.IPressTheButton(defaultApplicationAdapter, listOf()), + CrossPlatform.ISeeButton(defaultApplicationAdapter, listOf()), + CrossPlatform.ISeeScreen(defaultApplicationAdapter, listOf()), + CrossPlatform.ISeeText(defaultApplicationAdapter, listOf()), + CrossPlatform.ISeeTextFieldWithText(defaultApplicationAdapter, listOf()), + CrossPlatform.ITypeTextIntoTextField(defaultApplicationAdapter, listOf()), + CrossPlatform.SetLoggedInUserEmail(defaultApplicationAdapter, listOf()), + Platform.ISeeValueInScrollView + ) + } + + @SealedInterop.Enabled + sealed class CrossPlatform( + protected val application: ApplicationAdapter, + args: List? + ) : TestCase.CrossPlatform, AppDefinitions() { + + protected val args = args ?: emptyList() + + class IAmInScreen( + private val launchScreenName: String? = null, + application: ApplicationAdapter, + args: List? + ) : CrossPlatform(application, args) { + override val definition: Definition = CucumberDefinition.Step.Given("I am in the $EXPECT_VALUE_STRING screen") + + override fun runAndGetAssertions(): List { + + val screenTitleTag = when (val screenName = args[0]) { + Strings.Screen.Title.login -> { + application["isLoggedIn"] = "false" + application["testEmail"] = "" + Strings.Screen.Tag.login + } + + Strings.Screen.Title.home -> { + application["isLoggedIn"] = "true" + Strings.Screen.Tag.home + } + else -> throw UIElementException.Screen.NotFound(screenName) + } + application.launch(launchScreenName, application.applicationArguments) + val element = application.findViewByTag(screenTitleTag) + return listOf( + element.waitExists(TimeoutDuration.SHORT) + ) + } + } + + class ISeeText( + application: ApplicationAdapter, + args: List? + ) : CrossPlatform(application, args) { + override val definition: Definition = CucumberDefinition.Step.Then( + "I see $EXPECT_VALUE_STRING text" + ) + + override fun runAndGetAssertions(): List { + val textViewTitle = args[0] + val element = application.findViewByTag(textViewTitle) + return listOf(element.waitExists(TimeoutDuration.SHORT), element.isVisible()) + } + } + + class ISeeButton( + application: ApplicationAdapter, + args: List? + ) : CrossPlatform(application, args) { + override val definition: Definition = CucumberDefinition.Step.Then( + "I see the $EXPECT_VALUE_STRING button" + ) + + override fun runAndGetAssertions(): List { + val element = when (val buttonTitle = args[0]) { + Strings.Button.Title.login -> application.findViewByTag(Strings.Button.Tag.login) + Strings.Button.Title.logout -> application.findViewByTag(Strings.Button.Tag.logout) + else -> throw UIElementException.Button.NotFound(buttonTitle) + } + return listOf( + element.waitExists(TimeoutDuration.SHORT), + element.isVisible(), + element.isButton() + ) + } + + } + class ISeeScreen( + application: ApplicationAdapter, + args: List? + ) : CrossPlatform(application, args) { + override val definition: Definition = CucumberDefinition.Step.Then("I see the $EXPECT_VALUE_STRING screen") + + override fun runAndGetAssertions(): List { + val element = when (val screenTitle = args[0]) { + Strings.Screen.Title.login -> application.findViewByTag(Strings.Screen.Tag.login) + Strings.Screen.Title.home -> application.findViewByTag(Strings.Screen.Tag.home) + else -> throw UIElementException.Screen.NotFound(screenTitle) + } + return listOf(element.exists()) + } + } + + class ISeeTextFieldWithText( + application: ApplicationAdapter, + args: List? + ) : CrossPlatform(application, args) { + override val definition: Definition = CucumberDefinition.Step.Then("I see the $EXPECT_VALUE_STRING textfield with text $EXPECT_VALUE_STRING") + + override fun runAndGetAssertions(): List { + val textFieldTag = args[1] + val textFieldText = args[1] + val element = application.findViewByTag(textFieldTag) + return listOf( + element.waitExists(TimeoutDuration.SHORT), + element.isHintEqualTo(textFieldText, true) + ) + } + } + + class ITypeTextIntoTextField( + application: ApplicationAdapter, + args: List? + ) : CrossPlatform(application, args) { + override val definition: Definition = CucumberDefinition.Step.When( + "I type $EXPECT_VALUE_STRING in the $EXPECT_VALUE_STRING textfield" + ) + + override fun runAndGetAssertions(): List { + val textInput = args[0] + val tag = args[1] + application + .findViewByTag(tag) + .typeText(textInput) + return emptyList() + } + } + + class IPressTheButton( + application: ApplicationAdapter, + args: List? + ) : CrossPlatform(application, args) { + override val definition: Definition = CucumberDefinition.Step.When( + "I press the $EXPECT_VALUE_STRING button" + ) + + override fun runAndGetAssertions(): List { + val element = when(val buttonTag = args[0]) { + Strings.Button.Title.login -> application.findViewByTag(Strings.Button.Tag.login) + Strings.Button.Title.logout -> application.findViewByTag(Strings.Button.Tag.logout) + else -> throw UIElementException.Button.NotFound(buttonTag) + } + application.assert(element.waitExists(TimeoutDuration.SHORT)) + element.tap() + return emptyList() + } + } + + class SetLoggedInUserEmail( + application: ApplicationAdapter, + args: List? + ) : CrossPlatform(application, args) { + override val definition: Definition = CucumberDefinition.Step.Given( + "Email is $EXPECT_VALUE_STRING" + ) + + override fun runAndGetAssertions(): List { + val loggedInEmail = args[0] + application["testEmail"] = loggedInEmail + return emptyList() + } + } + } + + @SealedInterop.Enabled + sealed class Platform : TestCase.Platform, AppDefinitions() { + object ISeeValueInScrollView : Platform() { + override val definition: Definition = CucumberDefinition.Step.Then( + "I see $EXPECT_VALUE_STRING in the scrollview" + ) + } } } diff --git a/cucumberShared/src/commonTest/kotlin/SomeTest.kt b/cucumberShared/src/commonTest/kotlin/SomeTest.kt index 093ad03..ac9a6ce 100644 --- a/cucumberShared/src/commonTest/kotlin/SomeTest.kt +++ b/cucumberShared/src/commonTest/kotlin/SomeTest.kt @@ -1,10 +1,2 @@ -import kotlin.test.Test -import kotlin.test.assertEquals - class SomeTest { - - @Test - fun runSomeTest() { - assertEquals(0, 1) - } } \ No newline at end of file diff --git a/dependencies/kaluga-cucumber b/dependencies/kaluga-cucumber new file mode 160000 index 0000000..d192d64 --- /dev/null +++ b/dependencies/kaluga-cucumber @@ -0,0 +1 @@ +Subproject commit d192d643e28bd9b8525f1323249069214bd67c21 diff --git a/dependencies/kaluga-swiftui b/dependencies/kaluga-swiftui index 260db7b..b2c6a94 160000 --- a/dependencies/kaluga-swiftui +++ b/dependencies/kaluga-swiftui @@ -1 +1 @@ -Subproject commit 260db7bd1ec2552d407433d4ebd3ec1ab27cadb1 +Subproject commit b2c6a941f15fc8b183d4322def71a9816169d07d diff --git a/dependencies/kaluga-uitest b/dependencies/kaluga-uitest new file mode 160000 index 0000000..5e876c1 --- /dev/null +++ b/dependencies/kaluga-uitest @@ -0,0 +1 @@ +Subproject commit 5e876c15ccbbe701b0d95a179bb7a77ac177f4ba diff --git a/gradle.properties b/gradle.properties index b3e2ce2..87ac561 100644 --- a/gradle.properties +++ b/gradle.properties @@ -11,9 +11,10 @@ android.nonTransitiveRClass=true #MPP kotlin.mpp.enableCInteropCommonization=true kotlin.mpp.androidSourceSetLayoutVersion=2 +kotlin.mpp.import.enableKgpDependencyResolution=true #Kaluga kalugaVersion=1.2.1 #Koin -koinVersion=3.3.2 +koinVersion=3.5.0 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index cf4b049..9b3bf2e 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ #Mon Jun 26 16:11:07 CEST 2023 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.1.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-rc-2-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/ios/CucumberTests/CucumberTests.swift b/ios/CucumberTests/CucumberTests.swift index 1d0e8f8..5770edc 100644 --- a/ios/CucumberTests/CucumberTests.swift +++ b/ios/CucumberTests/CucumberTests.swift @@ -13,127 +13,118 @@ import Cucumberish @objc public class CucumberishInitializer: NSObject { static var app: XCUIApplication! + static var applicationAdapter: ApplicationAdapter! + + @objc public class func assertAll(assertions: [AssertionResult]) { + for assertion in assertions { + XCTAssert(assertion is AssertionResult.Success) + } + } @objc public class func CucumberishSwiftInit() { - - before { _ in + beforeStart { app = XCUIApplication() app.launchArguments.append("test") + applicationAdapter = DefaultApplicationAdapter(app: app) } - for test in Definitions.companion.allCases { - let definitionString = test.definition.definitionString - switch test { - case .iAmInTheExpectValueStringScreen: Given(definitionString) { args, userInfo in - guard let screenName = args?[0] as? String else { return } - - let text: XCUIElement - switch(screenName) { - case Strings.ScreenTag.shared.home: - app.launchEnvironment["isLoggedIn"] = "true" - text = app.staticTexts[Strings.ScreenTitle.shared.home] - case Strings.ScreenTag.shared.login: - app.launchEnvironment["isLoggedIn"] = "false" - text = app.staticTexts[Strings.ScreenTitle.shared.login] - - default: - text = app.staticTexts["Fail"] - XCTFail("Couldn't find \(screenName) screen") - } - - app.launch() - - XCTAssert(text.exists(timeout: .short), "Couldn't validate to be in \(screenName)") - return - } - case .iSeeExpectValueStringText: Then(definitionString){ args, userInfo in - guard let textString = args?[0] as? String else { return } - let text = app.staticTexts[textString] - XCTAssert(text.exists(timeout: .short), "Couldn't find \(text) text") - return - } - case .iSeeTheExpectValueStringButton: Then(definitionString) { args, userInfo in - guard let text = args?[0] as? String else { return } - let button = app.buttons[text] - XCTAssert(button.exists(timeout: .short), "\"\(text)\" button should be visible") - return - } - case .iSeeTheExpectValueStringScreen: Then(definitionString) { args, userInfo in - guard let screenName = args?[0] as? String else { return } - switch(screenName) { - case Strings.ScreenTag.shared.home: - let text = app.staticTexts[Strings.ScreenTitle.shared.home] - XCTAssert(text.exists(timeout: .short), "Couldn't validate to be in \(screenName)") - case Strings.ScreenTag.shared.login: - let text = app.staticTexts[Strings.ScreenTitle.shared.login] - XCTAssert(text.exists(timeout: .short), "Couldn't validate to be in \(screenName)") - default: XCTFail("Couldn't find \(screenName) screen") - } - return - } - case .iSeeTheExpectValueStringTextFieldWithTextExpectValueString: Then(definitionString) { args, userInfo in - guard let textfieldName = args?[0] as? String else { return } - guard let textfieldText = args?[1] as? String else { return } - let textfield: XCUIElement? = { - switch (textfieldName){ - case Strings.TextFieldTag.shared.email: - return app.textFields[textfieldName] - case Strings.TextFieldTag.shared.password: - return app.secureTextFields[textfieldName] - default: - return nil + afterFinish { + applicationAdapter.tearDown() + } + + + + for testCase in AppDefinitions.companion.allCases { + let definitionString = testCase.definition.definitionString + switch onEnum(of: testCase) { + + case .crossPlatform(let crossPlatformTestCase): + switch onEnum(of: crossPlatformTestCase) { + case .iAmInScreen(_): + Given(definitionString) { args, userInfo in + let assertions = AppDefinitions.CrossPlatformIAmInScreen( + launchScreenName: nil, + application: applicationAdapter, + args: args + ).runAndGetAssertions() + + assertAll(assertions: assertions) + } + case .iPressTheButton(_): + When(definitionString) { args, userInfo in + let assertions = AppDefinitions.CrossPlatformIPressTheButton(application: applicationAdapter, args: args).runAndGetAssertions() + assertAll(assertions: assertions) + } + case .iSeeButton(_): + Then(definitionString) { args, userInfo in + let assertions = AppDefinitions.CrossPlatformISeeButton( + application: applicationAdapter, + args: args + ).runAndGetAssertions() + + assertAll(assertions: assertions) + } + case .iSeeScreen(_): + Then(definitionString) { args, userInfo in + let assertions = AppDefinitions.CrossPlatformISeeScreen( + application: applicationAdapter, + args: args + ).runAndGetAssertions() + + assertAll(assertions: assertions) + } + case .iSeeText(_): + Then(definitionString) { args, userInfo in + let assertions = AppDefinitions.CrossPlatformISeeText( + application: applicationAdapter, + args: args + ).runAndGetAssertions() + + assertAll(assertions: assertions) + } + case .iSeeTextFieldWithText(_): + Then(definitionString) { args, userInfo in + let assertions = AppDefinitions.CrossPlatformISeeTextFieldWithText( + application: applicationAdapter, + args: args + ).runAndGetAssertions() + + assertAll(assertions: assertions) + } + case .iTypeTextIntoTextField(_): + When(definitionString) { args, userInfo in + let assertions = AppDefinitions.CrossPlatformITypeTextIntoTextField(application: applicationAdapter, args: args).runAndGetAssertions() + assertAll(assertions: assertions) + } + case .setLoggedInUserEmail(_): + Given(definitionString) { args, userInfo in + let assertions = AppDefinitions.CrossPlatformSetLoggedInUserEmail(application: applicationAdapter, args: args).runAndGetAssertions() + assertAll(assertions: assertions) } - }() - if textfield == nil { - XCTFail("Couldn't find \"\(textfieldName)\" field") - return } - - XCTAssert(textfield?.value as? String == textfieldText, "\"\(textfieldName)\" field text should be \"\(textfield?.value)\"") - return - } - case .iTypeExpectValueStringInTheExpectValueStringTextField: When(definitionString) { args, userInfo in - guard let textfieldName = args?[1] as? String else { return } - guard let textfieldText = args?[0] as? String else { return } - let textfield = app.textFields[textfieldName] - textfield.tap() - textfield.typeText(textfieldText) - return - } - case .iTypeExpectValueStringInTheExpectValueStringSecureTextField: When(definitionString) { args, userInfo in - guard let textfieldName = args?[1] as? String else { return } - guard let textfieldText = args?[0] as? String else { return } - let textfield = app.secureTextFields[textfieldName] - textfield.tap() - textfield.typeText(textfieldText) - return - } - - case .iPressTheExpectValueStringButton: When(definitionString) { args, userInfo in - guard let buttonName = args?[0] as? String else { return } - let button = app.buttons[buttonName] - let link = app.links[buttonName] - - if button.exists(timeout: .medium) && button.isEnabled(timeout: .short) { - button.tap() - } else if link.exists(timeout: .medium) && link.isEnabled(timeout: .short) { - link.tap() - } else { - XCTFail("I press Login button failed") + case .platform(let platformTestCase): + switch onEnum(of: platformTestCase) { + case .iSeeValueInScrollView(_): + Then(definitionString) { args, userInfo in + guard let scrollViewItemIndex = args?[0] as? String else { return } + // Platform implementation + app.descendants(matching: .any) + .matching(identifier: Strings.ScrollViewTag.shared.homeScrollView) + .element.swipeUp() + let scrollItem = app.staticTexts[scrollViewItemIndex] + XCTAssert(scrollItem.waitForExistence(timeout: 0.1)) + + // Cross-platform implementation +// let element = applicationAdapter.findView(tag: Strings.ScrollViewTag.shared.homeScrollView) +// element.swipeUntilIndex(index: Int32(scrollViewItemIndex)!, velocity: 200.0) +// let text = applicationAdapter.findView(tag: scrollViewItemIndex) +// assertAll(assertions: [text.exists()]) + } } - return - } - case .emailIsExpectValueString: Given(definitionString) { args, userInfo in - guard let email = args?[0] as? String else { return } - app.launchEnvironment["testEmail"] = email - return } - default: - XCTFail("unrecognised test case.") - } - - let bundle = Bundle(for: CucumberishInitializer.self) - Cucumberish.executeFeatures(inDirectory: "Features", from: bundle, includeTags: nil, excludeTags: ["ignore"]) } + + let bundle = Bundle(for: CucumberishInitializer.self) + Cucumberish.executeFeatures(inDirectory: "Features", from: bundle, includeTags: nil, excludeTags: ["ignore"]) } } diff --git a/ios/CucumberTests/Features/Home.feature b/ios/CucumberTests/Features/Home.feature index e2e6bb0..f551517 100644 --- a/ios/CucumberTests/Features/Home.feature +++ b/ios/CucumberTests/Features/Home.feature @@ -4,5 +4,6 @@ Feature: Home And I am in the "Home" screen Then I see "test@test.com" text And I see the "Logout" button + And I see "15" in the scrollview When I press the "Logout" button Then I see the "Login" screen diff --git a/ios/CucumberTests/Features/Login.feature b/ios/CucumberTests/Features/Login.feature index 4290305..f4d1263 100644 --- a/ios/CucumberTests/Features/Login.feature +++ b/ios/CucumberTests/Features/Login.feature @@ -5,7 +5,7 @@ Feature: Login And I see the "Password" textfield with text "Password" And I see the "Login" button When I type "test@test.com" in the "Email" textfield - And I type "1234" in the "Password" secure textfield + And I type "1234" in the "Password" textfield And I press the "Login" button Then I see the "Home" screen And I see "test@test.com" text diff --git a/ios/CucumberTests/Util/Node+Extensions.swift b/ios/CucumberTests/Util/Node+Extensions.swift new file mode 100644 index 0000000..37fd3b6 --- /dev/null +++ b/ios/CucumberTests/Util/Node+Extensions.swift @@ -0,0 +1,16 @@ +// +// Node+Extensions.swift +// CucumberTests +// +// Created by Corrado Quattrocchi on 15/09/2023. +// Copyright © 2023 orgName. All rights reserved. +// + +import shared +import XCTest + +extension Node { + func assert(assertionResult: AssertionResult, message: String) { + XCTAssert(assertionResult is AssertionResult.Success, message) + } +} diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 3aa2a56..74e28cc 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -11,6 +11,6 @@ SPEC REPOS: SPEC CHECKSUMS: Cucumberish: 6cbd0c1f50306b369acebfe7d9f514c9c287d26c -PODFILE CHECKSUM: 3eb9ba1ad789ee4ee8d7d6ed53f6cce6610a45a3 +PODFILE CHECKSUM: 628c667357c176acb30372b1f5ac2574ef0cf182 COCOAPODS: 1.12.1 diff --git a/ios/ios.xcodeproj/project.pbxproj b/ios/ios.xcodeproj/project.pbxproj index 0323218..9178f81 100644 --- a/ios/ios.xcodeproj/project.pbxproj +++ b/ios/ios.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 52; + objectVersion = 54; objects = { /* Begin PBXBuildFile section */ @@ -20,11 +20,12 @@ 2152FB042600AC8F00CF470E /* iOSApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2152FB032600AC8F00CF470E /* iOSApp.swift */; }; 277600BC3DC673E70E5A18C5 /* Color+Color.generated.swift in Sources */ = {isa = PBXBuildFile; fileRef = 76088B1B2130282DA6BB6E45 /* Color+Color.generated.swift */; }; 29395FA8970D38A0CBBB8913 /* KalugaBackgroundStyle+SwiftUI.generated.swift in Sources */ = {isa = PBXBuildFile; fileRef = F91AE96E17B56B56CB7AF766 /* KalugaBackgroundStyle+SwiftUI.generated.swift */; }; - 4140822635AF9CFFF681C02E /* Pods_CucumberTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7ECF88FBB45FBC97FE0BEDC8 /* Pods_CucumberTests.framework */; }; + 2F840DCC27B0A6704BC055E6 /* Pods_CucumberTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 46F9ECEF183B5649D7FE3536 /* Pods_CucumberTests.framework */; }; 436308B705EE84E8FEE8CD21 /* DefaultValues.generated.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC158DB5DDE249A03B2F51AC /* DefaultValues.generated.swift */; }; 48C5442ECE76BED5A8FFFAE9 /* ListObservable.generated.swift in Sources */ = {isa = PBXBuildFile; fileRef = 140577234C8D9C9BD309475E /* ListObservable.generated.swift */; }; 4D7D41A3A4FA6F91C431D068 /* KalugaDate+Extensions.generated.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1D787A9CEB2779127CD15B6F /* KalugaDate+Extensions.generated.swift */; }; 5A0347FCDC0723695A7D6A7C /* PlatformValueMapper.generated.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E798EA8B136B833082411B4 /* PlatformValueMapper.generated.swift */; }; + 640CC3C82AB49ECA003E3D8C /* Node+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 640CC3C72AB49ECA003E3D8C /* Node+Extensions.swift */; }; 640DFEEC2A370DA300ABF2DD /* CucumberTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 640DFEEB2A370DA300ABF2DD /* CucumberTests.swift */; }; 640DFEF52A370DD200ABF2DD /* Features in Resources */ = {isa = PBXBuildFile; fileRef = 640DFEF42A370DD200ABF2DD /* Features */; }; 640DFEF82A370F5C00ABF2DD /* CucumberTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 640DFEF72A370F5C00ABF2DD /* CucumberTests.m */; }; @@ -74,6 +75,16 @@ FAF26C36EFC614CD413B9488 /* UninitializedSubject.generated.swift in Sources */ = {isa = PBXBuildFile; fileRef = 191BB22D1226AD539DDC6631 /* UninitializedSubject.generated.swift */; }; /* End PBXBuildFile section */ +/* Begin PBXContainerItemProxy section */ + 64C235582AE942A600AA3E3E /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 7555FF73242A565900829871 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 7555FF7A242A565900829871; + remoteInfo = ios; + }; +/* End PBXContainerItemProxy section */ + /* Begin PBXFileReference section */ 058557BA273AAA24004C7B11 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 058557D8273AAEEB004C7B11 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; @@ -86,21 +97,25 @@ 1A7A62257F00AFBB12B8616B /* PlatformMappers.generated.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; path = PlatformMappers.generated.swift; sourceTree = ""; }; 1D787A9CEB2779127CD15B6F /* KalugaDate+Extensions.generated.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; path = "KalugaDate+Extensions.generated.swift"; sourceTree = ""; }; 2152FB032600AC8F00CF470E /* iOSApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = iOSApp.swift; sourceTree = ""; }; + 46F9ECEF183B5649D7FE3536 /* Pods_CucumberTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_CucumberTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 48B350D3EB640C8B7B253753 /* UninitializedObservable.generated.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; path = UninitializedObservable.generated.swift; sourceTree = ""; }; - 4DEA2654EFE69B013946D9B0 /* Pods-CucumberTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-CucumberTests.release.xcconfig"; path = "Target Support Files/Pods-CucumberTests/Pods-CucumberTests.release.xcconfig"; sourceTree = ""; }; 5416DF2E8BB6BE190C0431AF /* NavigationBarColor.generated.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; path = NavigationBarColor.generated.swift; sourceTree = ""; }; + 640CC3C72AB49ECA003E3D8C /* Node+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Node+Extensions.swift"; sourceTree = ""; }; 640DFEE92A370DA300ABF2DD /* CucumberTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = CucumberTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 640DFEEB2A370DA300ABF2DD /* CucumberTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CucumberTests.swift; sourceTree = ""; }; 640DFEF42A370DD200ABF2DD /* Features */ = {isa = PBXFileReference; lastKnownFileType = folder; path = Features; sourceTree = ""; }; 640DFEF62A370F5C00ABF2DD /* CucumberTests-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "CucumberTests-Bridging-Header.h"; sourceTree = ""; }; 640DFEF72A370F5C00ABF2DD /* CucumberTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CucumberTests.m; sourceTree = ""; }; - 640DFEFC2A371F6300ABF2DD /* Cucumberish.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Cucumberish.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 6483290C2A4AE1C000E5A4A8 /* LoginView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginView.swift; sourceTree = ""; }; 648329292A4B0C4C00E5A4A8 /* HomeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeView.swift; sourceTree = ""; }; 6483292C2A4B2A5600E5A4A8 /* TestPlan.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; path = TestPlan.xctestplan; sourceTree = ""; }; + 649AA4382AE94CAA0070EDEF /* Cucumberish.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Cucumberish.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 649AA43A2AE94CB90070EDEF /* Cucumberish.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Cucumberish.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 649B6F932A388AC600C40DCC /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Platforms/iPhoneOS.platform/Developer/Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; }; 649B6F992A389F5F00C40DCC /* XCUIElement.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XCUIElement.swift; sourceTree = ""; }; 649B6F9B2A389F9300C40DCC /* Timeout.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Timeout.swift; sourceTree = ""; }; + 64DDA56B2AE94C7600AF0D5A /* Cucumberish.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Cucumberish.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 67E93FB7E23655257ECC8D9B /* Pods-CucumberTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-CucumberTests.release.xcconfig"; path = "Target Support Files/Pods-CucumberTests/Pods-CucumberTests.release.xcconfig"; sourceTree = ""; }; 69246E55BCB962279D2220E0 /* Observable.generated.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; path = Observable.generated.swift; sourceTree = ""; }; 6DA39B482E4B46EDBEF4E573 /* RoutingState.generated.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; path = RoutingState.generated.swift; sourceTree = ""; }; 71FBD06B4210EBAC91D0F6B4 /* HasDefaultValue.generated.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; path = HasDefaultValue.generated.swift; sourceTree = ""; }; @@ -108,12 +123,11 @@ 7555FF82242A565900829871 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; 7555FF8C242A565B00829871 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 76088B1B2130282DA6BB6E45 /* Color+Color.generated.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; path = "Color+Color.generated.swift"; sourceTree = ""; }; - 7ECF88FBB45FBC97FE0BEDC8 /* Pods_CucumberTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_CucumberTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 9BE43767F0B59D440634298D /* ContainerView.generated.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; path = ContainerView.generated.swift; sourceTree = ""; }; 9E798EA8B136B833082411B4 /* PlatformValueMapper.generated.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; path = PlatformValueMapper.generated.swift; sourceTree = ""; }; A492DFDE55EF1239A015ECBA /* Navigation.generated.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; path = Navigation.generated.swift; sourceTree = ""; }; + C740D7E2E8EF042A9EF20A6B /* Pods-CucumberTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-CucumberTests.debug.xcconfig"; path = "Target Support Files/Pods-CucumberTests/Pods-CucumberTests.debug.xcconfig"; sourceTree = ""; }; CC158DB5DDE249A03B2F51AC /* DefaultValues.generated.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; path = DefaultValues.generated.swift; sourceTree = ""; }; - D8AF9DD8B6C07A08BD9ED21E /* Pods-CucumberTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-CucumberTests.debug.xcconfig"; path = "Target Support Files/Pods-CucumberTests/Pods-CucumberTests.debug.xcconfig"; sourceTree = ""; }; D963EF3C4F0C141A4A80C891 /* KalugaStyledString+SwiftUI.generated.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; path = "KalugaStyledString+SwiftUI.generated.swift"; sourceTree = ""; }; E52FF51F397E317F81326758 /* TintedImage+Image.generated.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; path = "TintedImage+Image.generated.swift"; sourceTree = ""; }; EA6CBAFDA191747DE6BFB4B0 /* LazyView.generated.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; path = LazyView.generated.swift; sourceTree = ""; }; @@ -123,7 +137,7 @@ /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ - 46300BA9CC087F9271C3AAF0 /* Frameworks */ = { + 07039B23A7E4365375829194 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( @@ -135,7 +149,7 @@ buildActionMask = 2147483647; files = ( 649B6F942A388AC600C40DCC /* XCTest.framework in Frameworks */, - 4140822635AF9CFFF681C02E /* Pods_CucumberTests.framework in Frameworks */, + 2F840DCC27B0A6704BC055E6 /* Pods_CucumberTests.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -150,15 +164,6 @@ path = "Preview Content"; sourceTree = ""; }; - 0A0D2C84F18C844F76244379 /* Pods */ = { - isa = PBXGroup; - children = ( - D8AF9DD8B6C07A08BD9ED21E /* Pods-CucumberTests.debug.xcconfig */, - 4DEA2654EFE69B013946D9B0 /* Pods-CucumberTests.release.xcconfig */, - ); - path = Pods; - sourceTree = ""; - }; 640DFEEA2A370DA300ABF2DD /* CucumberTests */ = { isa = PBXGroup; children = ( @@ -202,6 +207,7 @@ children = ( 649B6F992A389F5F00C40DCC /* XCUIElement.swift */, 649B6F9B2A389F9300C40DCC /* Timeout.swift */, + 640CC3C72AB49ECA003E3D8C /* Node+Extensions.swift */, ); path = Util; sourceTree = ""; @@ -212,9 +218,9 @@ 7555FF7D242A565900829871 /* ios */, 640DFEEA2A370DA300ABF2DD /* CucumberTests */, 7555FF7C242A565900829871 /* Products */, - 0A0D2C84F18C844F76244379 /* Pods */, 8A0E1A9AAD55C42A9D6F2E2E /* Frameworks */, 7959A7E223B309B74DABEA7A /* Generated */, + CC211C4B63C4CCEDBB37D71C /* Pods */, ); sourceTree = ""; }; @@ -251,9 +257,11 @@ 8A0E1A9AAD55C42A9D6F2E2E /* Frameworks */ = { isa = PBXGroup; children = ( + 649AA43A2AE94CB90070EDEF /* Cucumberish.framework */, + 649AA4382AE94CAA0070EDEF /* Cucumberish.framework */, + 64DDA56B2AE94C7600AF0D5A /* Cucumberish.framework */, 649B6F932A388AC600C40DCC /* XCTest.framework */, - 640DFEFC2A371F6300ABF2DD /* Cucumberish.framework */, - 7ECF88FBB45FBC97FE0BEDC8 /* Pods_CucumberTests.framework */, + 46F9ECEF183B5649D7FE3536 /* Pods_CucumberTests.framework */, ); name = Frameworks; sourceTree = ""; @@ -289,6 +297,15 @@ path = KalugaSwiftUI; sourceTree = ""; }; + CC211C4B63C4CCEDBB37D71C /* Pods */ = { + isa = PBXGroup; + children = ( + C740D7E2E8EF042A9EF20A6B /* Pods-CucumberTests.debug.xcconfig */, + 67E93FB7E23655257ECC8D9B /* Pods-CucumberTests.release.xcconfig */, + ); + path = Pods; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -296,16 +313,17 @@ isa = PBXNativeTarget; buildConfigurationList = 640DFEF32A370DA300ABF2DD /* Build configuration list for PBXNativeTarget "CucumberTests" */; buildPhases = ( - 693FEEC8CBD656800AEB356B /* [CP] Check Pods Manifest.lock */, + 87EB082BB1DBD788E080346B /* [CP] Check Pods Manifest.lock */, 648329012A43377A00E5A4A8 /* Embed framework */, 640DFEE52A370DA300ABF2DD /* Sources */, 640DFEE62A370DA300ABF2DD /* Frameworks */, 640DFEE72A370DA300ABF2DD /* Resources */, - 558CBE5BFDE9242446515341 /* [CP] Embed Pods Frameworks */, + 1C5C2AF5FCE568B7621337F7 /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); dependencies = ( + 64C235592AE942A600AA3E3E /* PBXTargetDependency */, ); name = CucumberTests; productName = CucumberTests; @@ -319,7 +337,7 @@ 648329002A43373100E5A4A8 /* Embed Framework */, 7555FF77242A565900829871 /* Sources */, 7555FF79242A565900829871 /* Resources */, - 46300BA9CC087F9271C3AAF0 /* Frameworks */, + 07039B23A7E4365375829194 /* Frameworks */, ); buildRules = ( ); @@ -390,7 +408,7 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ - 558CBE5BFDE9242446515341 /* [CP] Embed Pods Frameworks */ = { + 1C5C2AF5FCE568B7621337F7 /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -443,7 +461,7 @@ shellPath = /bin/sh; shellScript = "cd \"$SRCROOT/../\"\n\n./gradlew :cucumberShared:embedAndSignAppleFrameworkForXcode\n"; }; - 693FEEC8CBD656800AEB356B /* [CP] Check Pods Manifest.lock */ = { + 87EB082BB1DBD788E080346B /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -489,6 +507,7 @@ 648329182A4AE6BB00E5A4A8 /* RoutingState.generated.swift in Sources */, 648329262A4AE6BB00E5A4A8 /* DefaultValues.generated.swift in Sources */, 6483291D2A4AE6BB00E5A4A8 /* Shape+Helpers.generated.swift in Sources */, + 640CC3C82AB49ECA003E3D8C /* Node+Extensions.swift in Sources */, 648329032A43391500E5A4A8 /* iOSApp.swift in Sources */, 640DFEF82A370F5C00ABF2DD /* CucumberTests.m in Sources */, 648329152A4AE6BB00E5A4A8 /* Subject.generated.swift in Sources */, @@ -544,10 +563,18 @@ }; /* End PBXSourcesBuildPhase section */ +/* Begin PBXTargetDependency section */ + 64C235592AE942A600AA3E3E /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 7555FF7A242A565900829871 /* ios */; + targetProxy = 64C235582AE942A600AA3E3E /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + /* Begin XCBuildConfiguration section */ 640DFEF12A370DA300ABF2DD /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = D8AF9DD8B6C07A08BD9ED21E /* Pods-CucumberTests.debug.xcconfig */; + baseConfigurationReference = C740D7E2E8EF042A9EF20A6B /* Pods-CucumberTests.debug.xcconfig */; buildSettings = { CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; CLANG_ENABLE_MODULES = YES; @@ -557,11 +584,9 @@ DEFINES_MODULE = YES; DEVELOPMENT_TEAM = NHQ559J67Q; FRAMEWORK_SEARCH_PATHS = ( - "\"$(PLATFORM_DIR)/Developer/Library/Frameworks\"", "$(inherited)", "$(SRCROOT)/../cucumberShared/build/xcode-frameworks/$(CONFIGURATION)/$(SDK_NAME)", - "\"${PODS_CONFIGURATION_BUILD_DIR}/Cucumberish\"", - "$(SRCROOT)/../shared/build/xcode-frameworks/$(CONFIGURATION)/$(SDK_NAME)", + "$(PLATFORM_DIR)/Developer/Library/Frameworks", ); GENERATE_INFOPLIST_FILE = YES; IPHONEOS_DEPLOYMENT_TARGET = 16.0; @@ -576,7 +601,7 @@ "-framework", shared, ); - PRODUCT_BUNDLE_IDENTIFIER = com.corrado4eyes.CucumberTests; + PRODUCT_BUNDLE_IDENTIFIER = com.splendo.CucumberTests; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_EMIT_LOC_STRINGS = NO; SWIFT_OBJC_BRIDGING_HEADER = "CucumberTests/CucumberTests-Bridging-Header.h"; @@ -589,7 +614,7 @@ }; 640DFEF22A370DA300ABF2DD /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 4DEA2654EFE69B013946D9B0 /* Pods-CucumberTests.release.xcconfig */; + baseConfigurationReference = 67E93FB7E23655257ECC8D9B /* Pods-CucumberTests.release.xcconfig */; buildSettings = { CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; CLANG_ENABLE_MODULES = YES; @@ -599,9 +624,7 @@ DEVELOPMENT_TEAM = NHQ559J67Q; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", - "$(FRAMEWORK_SEARCH_PATHS)", "$(SRCROOT)/../cucumberShared/build/xcode-frameworks/$(CONFIGURATION)/$(SDK_NAME)", - "$(SRCROOT)/../shared/build/xcode-frameworks/$(CONFIGURATION)/$(SDK_NAME)", ); GENERATE_INFOPLIST_FILE = YES; IPHONEOS_DEPLOYMENT_TARGET = 16.0; @@ -616,7 +639,7 @@ "-framework", shared, ); - PRODUCT_BUNDLE_IDENTIFIER = com.corrado4eyes.CucumberTests; + PRODUCT_BUNDLE_IDENTIFIER = com.splendo.CucumberTests; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_EMIT_LOC_STRINGS = NO; SWIFT_OBJC_BRIDGING_HEADER = "CucumberTests/CucumberTests-Bridging-Header.h"; @@ -751,7 +774,6 @@ ENABLE_PREVIEWS = YES; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", - "\"$(PLATFORM_DIR)/Developer/Library/Frameworks\"", "$(SRCROOT)/../shared/build/xcode-frameworks/$(CONFIGURATION)/$(SDK_NAME)", ); INFOPLIST_FILE = ios/Info.plist; @@ -778,7 +800,10 @@ CODE_SIGN_STYLE = Automatic; DEVELOPMENT_ASSET_PATHS = "\"ios/Preview Content\""; ENABLE_PREVIEWS = YES; - FRAMEWORK_SEARCH_PATHS = "$(SRCROOT)/../shared/build/xcode-frameworks/$(CONFIGURATION)/$(SDK_NAME)"; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(SRCROOT)/../shared/build/xcode-frameworks/$(CONFIGURATION)/$(SDK_NAME)", + ); INFOPLIST_FILE = ios/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", diff --git a/ios/ios.xcodeproj/xcshareddata/xcschemes/ios.xcscheme b/ios/ios.xcodeproj/xcshareddata/xcschemes/ios.xcscheme index c3cdfd9..6bce15f 100644 --- a/ios/ios.xcodeproj/xcshareddata/xcschemes/ios.xcscheme +++ b/ios/ios.xcodeproj/xcshareddata/xcschemes/ios.xcscheme @@ -52,6 +52,56 @@ BlueprintName = "CucumberTests" ReferencedContainer = "container:ios.xcodeproj"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ios/ios/ContentView.swift b/ios/ios/ContentView.swift index 4a5d865..9507fda 100644 --- a/ios/ios/ContentView.swift +++ b/ios/ios/ContentView.swift @@ -16,11 +16,7 @@ struct ContentView: View { init() { let testConfiguration: TestConfiguration? = { let arguments = CommandLine.arguments - guard arguments.contains("test") else { - return nil - } let tc = DefaultTestConfiguration(configuration: ProcessInfo.processInfo.environment) - print(tc) return tc }() let mainViewModel = MainViewModel(testConfiguration: testConfiguration) diff --git a/ios/ios/Screens/Home/HomeView.swift b/ios/ios/Screens/Home/HomeView.swift index fc557f0..56ea156 100644 --- a/ios/ios/Screens/Home/HomeView.swift +++ b/ios/ios/Screens/Home/HomeView.swift @@ -21,14 +21,21 @@ struct HomeView: SwiftUI.View { NavigationView { VStack { Text(viewModel.user.email) - + .accessibilityLabel(viewModel.user.email) Button(action: viewModel.logout) { Text(viewModel.buttonTitle) } - .accessibilityLabel(viewModel.buttonTitle) + .accessibilityLabel(Strings.ButtonTag.shared.logout) + ScrollView(.vertical) { + ForEach(viewModel.scrollableItems, id: \.intValue) { index in + Text("\(index)") + } + }.frame(height: 200) + .accessibilityLabel(Strings.ScrollViewTag.shared.homeScrollView) }.toolbar { ToolbarItem(placement: .principal) { Text(viewModel.screenTitle) + .accessibilityLabel(Strings.ScreenTag.shared.home) } }.navigationBarTitleDisplayMode(.inline) } diff --git a/ios/ios/Screens/Login/LoginView.swift b/ios/ios/Screens/Login/LoginView.swift index 69ab862..b46ce58 100644 --- a/ios/ios/Screens/Login/LoginView.swift +++ b/ios/ios/Screens/Login/LoginView.swift @@ -41,15 +41,14 @@ struct LoginView: SwiftUI.View { VStack { TextField(viewModel.emailPlaceholder, text: $emailText.value) .autocapitalization(.none) - .accessibilityLabel(viewModel.emailPlaceholder) + .accessibilityLabel(Strings.TextFieldTag.shared.email) Text(emailErrorText.value) .foregroundColor(Color.red) SecureField(viewModel.passwordPlaceholder, text: $passwordText.value) .autocapitalization(.none) - .accessibilityLabel(viewModel.passwordPlaceholder) + .accessibilityLabel(Strings.TextFieldTag.shared.password) Text(passwordErrorText.value) .foregroundColor(Color.red) - Text(formFooterErrorText.value) .foregroundColor(Color.red) @@ -61,12 +60,13 @@ struct LoginView: SwiftUI.View { Text(viewModel.buttonTitle) } .disabled(!isButtonEnabled.value) - .accessibilityLabel(viewModel.buttonTitle) + .accessibilityLabel(Strings.ButtonTag.shared.login) Spacer() } .toolbar { ToolbarItem(placement: .principal) { Text(viewModel.screenTitle) + .accessibilityLabel(Strings.ScreenTag.shared.login) } }.navigationBarTitleDisplayMode(.inline) } diff --git a/settings.gradle.kts b/settings.gradle.kts index 0e3e57c..f84b979 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -2,6 +2,7 @@ pluginManagement { repositories { google() gradlePluginPortal() + mavenLocal() mavenCentral() } } @@ -9,6 +10,7 @@ pluginManagement { dependencyResolutionManagement { repositories { google() + mavenLocal() mavenCentral() } } @@ -16,5 +18,7 @@ dependencyResolutionManagement { rootProject.name = "CucumberPlayground" include(":android") include(":shared") -include(":cucumber") include(":cucumberShared") + +includeBuild("dependencies/kaluga-uitest") +includeBuild("dependencies/kaluga-cucumber") diff --git a/shared/build.gradle.kts b/shared/build.gradle.kts index 461ee2d..4276a54 100644 --- a/shared/build.gradle.kts +++ b/shared/build.gradle.kts @@ -96,8 +96,8 @@ kotlin { } android { - namespace = "com.corrado4eyes.cucumberplayground" - compileSdk = 33 + namespace = "com.splendo.cucumberplayground" + compileSdk = 34 defaultConfig { minSdk = 29 } diff --git a/shared/src/androidMain/kotlin/com/corrado4eyes/cucumberplayground/di/CucumberDependencyInjection.kt b/shared/src/androidMain/kotlin/com/corrado4eyes/cucumberplayground/di/CucumberDependencyInjection.kt index 28bc1b4..bdb389a 100644 --- a/shared/src/androidMain/kotlin/com/corrado4eyes/cucumberplayground/di/CucumberDependencyInjection.kt +++ b/shared/src/androidMain/kotlin/com/corrado4eyes/cucumberplayground/di/CucumberDependencyInjection.kt @@ -1,11 +1,11 @@ @file:JvmName("AndroidDependencyInjection") -package com.corrado4eyes.cucumberplayground.di +package com.splendo.cucumberplayground.di -import com.corrado4eyes.cucumberplayground.models.TestConfiguration -import com.corrado4eyes.cucumberplayground.viewModels.home.HomeViewModel -import com.corrado4eyes.cucumberplayground.viewModels.login.LoginViewModel -import com.corrado4eyes.cucumberplayground.viewModels.main.MainViewModel +import com.splendo.cucumberplayground.models.TestConfiguration +import com.splendo.cucumberplayground.viewModels.home.HomeViewModel +import com.splendo.cucumberplayground.viewModels.login.LoginViewModel +import com.splendo.cucumberplayground.viewModels.main.MainViewModel import com.splendo.kaluga.base.ApplicationHolder import kotlinx.coroutines.Dispatchers import org.koin.android.ext.koin.androidContext diff --git a/shared/src/commonMain/kotlin/com/corrado4eyes/cucumberplayground/di/CucumberDependencyInjection.kt b/shared/src/commonMain/kotlin/com/corrado4eyes/cucumberplayground/di/CucumberDependencyInjection.kt index b24de4f..11f1fa8 100644 --- a/shared/src/commonMain/kotlin/com/corrado4eyes/cucumberplayground/di/CucumberDependencyInjection.kt +++ b/shared/src/commonMain/kotlin/com/corrado4eyes/cucumberplayground/di/CucumberDependencyInjection.kt @@ -1,8 +1,8 @@ -package com.corrado4eyes.cucumberplayground.di +package com.splendo.cucumberplayground.di -import com.corrado4eyes.cucumberplayground.services.AuthService -import com.corrado4eyes.cucumberplayground.services.AuthServiceImpl -import com.corrado4eyes.cucumberplayground.services.previewMocks.AuthServicePreviewMock +import com.splendo.cucumberplayground.services.AuthService +import com.splendo.cucumberplayground.services.AuthServiceImpl +import com.splendo.cucumberplayground.services.previewMocks.AuthServicePreviewMock import org.koin.core.context.loadKoinModules import org.koin.core.context.startKoin import org.koin.core.module.Module diff --git a/shared/src/commonMain/kotlin/com/corrado4eyes/cucumberplayground/login/model/AuthResponse.kt b/shared/src/commonMain/kotlin/com/corrado4eyes/cucumberplayground/login/model/AuthResponse.kt index 3a82fa4..088da61 100644 --- a/shared/src/commonMain/kotlin/com/corrado4eyes/cucumberplayground/login/model/AuthResponse.kt +++ b/shared/src/commonMain/kotlin/com/corrado4eyes/cucumberplayground/login/model/AuthResponse.kt @@ -1,4 +1,4 @@ -package com.corrado4eyes.cucumberplayground.login.model +package com.splendo.cucumberplayground.login.model sealed class AuthResponse { object Success : AuthResponse() diff --git a/shared/src/commonMain/kotlin/com/corrado4eyes/cucumberplayground/models/Strings.kt b/shared/src/commonMain/kotlin/com/corrado4eyes/cucumberplayground/models/Strings.kt index 2493ef9..eaf0850 100644 --- a/shared/src/commonMain/kotlin/com/corrado4eyes/cucumberplayground/models/Strings.kt +++ b/shared/src/commonMain/kotlin/com/corrado4eyes/cucumberplayground/models/Strings.kt @@ -1,15 +1,21 @@ -package com.corrado4eyes.cucumberplayground.models +package com.splendo.cucumberplayground.models object Strings { object Screen { object Title { - val login = "Login screen" - val home = "Home screen" - } - object Tag { val login = "Login" val home = "Home" } + object Tag { + val login = "Login Screen" + val home = "Home Screen" + } + } + + object ScrollView { + object Tag { + val homeScrollView = "Home ScrollView" + } } object TextField { @@ -25,9 +31,13 @@ object Strings { } object Button { - object Text { + object Title { val login = "Login" val logout = "Logout" } + object Tag { + val login = "Login Button" + val logout = "Logout Button" + } } } \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/com/corrado4eyes/cucumberplayground/models/TestConfiguration.kt b/shared/src/commonMain/kotlin/com/corrado4eyes/cucumberplayground/models/TestConfiguration.kt index 2cc1e91..cef901c 100644 --- a/shared/src/commonMain/kotlin/com/corrado4eyes/cucumberplayground/models/TestConfiguration.kt +++ b/shared/src/commonMain/kotlin/com/corrado4eyes/cucumberplayground/models/TestConfiguration.kt @@ -1,4 +1,4 @@ -package com.corrado4eyes.cucumberplayground.models +package com.splendo.cucumberplayground.models typealias TestConfigurationMap = Map diff --git a/shared/src/commonMain/kotlin/com/corrado4eyes/cucumberplayground/models/User.kt b/shared/src/commonMain/kotlin/com/corrado4eyes/cucumberplayground/models/User.kt index d364dd9..65dd6d9 100644 --- a/shared/src/commonMain/kotlin/com/corrado4eyes/cucumberplayground/models/User.kt +++ b/shared/src/commonMain/kotlin/com/corrado4eyes/cucumberplayground/models/User.kt @@ -1,3 +1,3 @@ -package com.corrado4eyes.cucumberplayground.models +package com.splendo.cucumberplayground.models data class User(val email: String, val pass: String) \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/com/corrado4eyes/cucumberplayground/services/AuthService.kt b/shared/src/commonMain/kotlin/com/corrado4eyes/cucumberplayground/services/AuthService.kt index e4727f2..d4f41ae 100644 --- a/shared/src/commonMain/kotlin/com/corrado4eyes/cucumberplayground/services/AuthService.kt +++ b/shared/src/commonMain/kotlin/com/corrado4eyes/cucumberplayground/services/AuthService.kt @@ -1,7 +1,7 @@ -package com.corrado4eyes.cucumberplayground.services +package com.splendo.cucumberplayground.services -import com.corrado4eyes.cucumberplayground.models.User -import com.corrado4eyes.cucumberplayground.login.model.AuthResponse +import com.splendo.cucumberplayground.models.User +import com.splendo.cucumberplayground.login.model.AuthResponse import kotlinx.coroutines.delay import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow diff --git a/shared/src/commonMain/kotlin/com/corrado4eyes/cucumberplayground/services/previewMocks/AuthServicePreviewMock.kt b/shared/src/commonMain/kotlin/com/corrado4eyes/cucumberplayground/services/previewMocks/AuthServicePreviewMock.kt index 617d169..a9420ce 100644 --- a/shared/src/commonMain/kotlin/com/corrado4eyes/cucumberplayground/services/previewMocks/AuthServicePreviewMock.kt +++ b/shared/src/commonMain/kotlin/com/corrado4eyes/cucumberplayground/services/previewMocks/AuthServicePreviewMock.kt @@ -1,8 +1,8 @@ -package com.corrado4eyes.cucumberplayground.services.previewMocks +package com.splendo.cucumberplayground.services.previewMocks -import com.corrado4eyes.cucumberplayground.models.User -import com.corrado4eyes.cucumberplayground.login.model.AuthResponse -import com.corrado4eyes.cucumberplayground.services.AuthService +import com.splendo.cucumberplayground.models.User +import com.splendo.cucumberplayground.login.model.AuthResponse +import com.splendo.cucumberplayground.services.AuthService import kotlinx.coroutines.delay import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow diff --git a/shared/src/commonMain/kotlin/com/corrado4eyes/cucumberplayground/viewModels/home/HomeViewModel.kt b/shared/src/commonMain/kotlin/com/corrado4eyes/cucumberplayground/viewModels/home/HomeViewModel.kt index e859478..b955178 100644 --- a/shared/src/commonMain/kotlin/com/corrado4eyes/cucumberplayground/viewModels/home/HomeViewModel.kt +++ b/shared/src/commonMain/kotlin/com/corrado4eyes/cucumberplayground/viewModels/home/HomeViewModel.kt @@ -1,7 +1,7 @@ -package com.corrado4eyes.cucumberplayground.viewModels.home +package com.splendo.cucumberplayground.viewModels.home -import com.corrado4eyes.cucumberplayground.models.Strings -import com.corrado4eyes.cucumberplayground.services.AuthService +import com.splendo.cucumberplayground.models.Strings +import com.splendo.cucumberplayground.services.AuthService import com.splendo.kaluga.architecture.viewmodel.BaseLifecycleViewModel import kotlinx.coroutines.launch import org.koin.core.component.KoinComponent @@ -10,12 +10,13 @@ import org.koin.core.component.inject class HomeViewModel : BaseLifecycleViewModel(), KoinComponent { private val authService: AuthService by inject() - val screenTitle = Strings.Screen.Title.home - val buttonTitle = Strings.Button.Text.logout + val buttonTitle = Strings.Button.Title.logout fun getCurrentUser() = authService.getCurrentUserIfAny()!! + val user = authService.getCurrentUserIfAny()!! + val scrollableItems: List = (1..20).toList() fun logout() { coroutineScope.launch { diff --git a/shared/src/commonMain/kotlin/com/corrado4eyes/cucumberplayground/viewModels/login/LoginViewModel.kt b/shared/src/commonMain/kotlin/com/corrado4eyes/cucumberplayground/viewModels/login/LoginViewModel.kt index 7afa255..f2575fe 100644 --- a/shared/src/commonMain/kotlin/com/corrado4eyes/cucumberplayground/viewModels/login/LoginViewModel.kt +++ b/shared/src/commonMain/kotlin/com/corrado4eyes/cucumberplayground/viewModels/login/LoginViewModel.kt @@ -1,8 +1,8 @@ -package com.corrado4eyes.cucumberplayground.viewModels.login +package com.splendo.cucumberplayground.viewModels.login -import com.corrado4eyes.cucumberplayground.login.model.AuthResponse -import com.corrado4eyes.cucumberplayground.models.Strings -import com.corrado4eyes.cucumberplayground.services.AuthService +import com.splendo.cucumberplayground.login.model.AuthResponse +import com.splendo.cucumberplayground.models.Strings +import com.splendo.cucumberplayground.services.AuthService import com.splendo.kaluga.architecture.observable.toInitializedObservable import com.splendo.kaluga.architecture.observable.toInitializedSubject import com.splendo.kaluga.architecture.viewmodel.BaseLifecycleViewModel @@ -75,7 +75,7 @@ class LoginViewModel : BaseLifecycleViewModel(), KoinComponent { } else "" }.toInitializedObservable("", coroutineScope) - val buttonTitle = Strings.Button.Text.login + val buttonTitle = Strings.Button.Title.login val isButtonEnabled = viewState.map { it !is LoginViewState.Loading } .toInitializedObservable(false, coroutineScope) val isLoading = viewState.map { it is LoginViewState.Loading } diff --git a/shared/src/commonMain/kotlin/com/corrado4eyes/cucumberplayground/viewModels/main/MainViewModel.kt b/shared/src/commonMain/kotlin/com/corrado4eyes/cucumberplayground/viewModels/main/MainViewModel.kt index c206a92..4580362 100644 --- a/shared/src/commonMain/kotlin/com/corrado4eyes/cucumberplayground/viewModels/main/MainViewModel.kt +++ b/shared/src/commonMain/kotlin/com/corrado4eyes/cucumberplayground/viewModels/main/MainViewModel.kt @@ -1,8 +1,8 @@ -package com.corrado4eyes.cucumberplayground.viewModels.main +package com.splendo.cucumberplayground.viewModels.main -import com.corrado4eyes.cucumberplayground.services.AuthService -import com.corrado4eyes.cucumberplayground.models.TestConfiguration -import com.corrado4eyes.cucumberplayground.models.User +import com.splendo.cucumberplayground.models.TestConfiguration +import com.splendo.cucumberplayground.models.User +import com.splendo.cucumberplayground.services.AuthService import com.splendo.kaluga.architecture.observable.toInitializedObservable import com.splendo.kaluga.architecture.viewmodel.BaseLifecycleViewModel import kotlinx.coroutines.flow.MutableStateFlow @@ -33,6 +33,8 @@ class MainViewModel( if (testConfiguration != null) { if (testConfiguration.isLoggedIn) { authService.login(testConfiguration.testEmail, "1234") + } else { + authService.logout() } } authService.observeUser.collect { user -> diff --git a/shared/src/commonTest/kotlin/com/corrado4eyes/cucumberplayground/cucumber/login/AuthServiceMock.kt b/shared/src/commonTest/kotlin/com/corrado4eyes/cucumberplayground/cucumber/login/AuthServiceMock.kt index 24f9086..fea7677 100644 --- a/shared/src/commonTest/kotlin/com/corrado4eyes/cucumberplayground/cucumber/login/AuthServiceMock.kt +++ b/shared/src/commonTest/kotlin/com/corrado4eyes/cucumberplayground/cucumber/login/AuthServiceMock.kt @@ -1,8 +1,9 @@ -package com.corrado4eyes.cucumberplayground.cucumber.login +package com.splendo.cucumberplayground.cucumber.login -import com.corrado4eyes.cucumberplayground.models.User -import com.corrado4eyes.cucumberplayground.services.AuthService -import com.corrado4eyes.cucumberplayground.login.model.AuthResponse +import com.splendo.cucumberplayground.cucumber.login.DummyUsers.defaultTestUser +import com.splendo.cucumberplayground.login.model.AuthResponse +import com.splendo.cucumberplayground.models.User +import com.splendo.cucumberplayground.services.AuthService import kotlinx.coroutines.delay import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow @@ -11,13 +12,14 @@ import kotlinx.coroutines.flow.asStateFlow /** * Mock is the same as impl but here for the sake of proper interfacing */ -class AuthServiceMock : AuthService { +class AuthServiceMock(loggedInUser: User?) : AuthService { - private var currentUser: MutableStateFlow = MutableStateFlow(null) + private var currentUser: MutableStateFlow = MutableStateFlow(loggedInUser) private val users = mutableListOf( User("alex@alex.com", "1234"), - User("corrado@corrado.com", "1234") + User("corrado@corrado.com", "1234"), + defaultTestUser ) override suspend fun login(email: String, pass: String): AuthResponse { @@ -31,8 +33,9 @@ class AuthServiceMock : AuthService { } ?: AuthResponse.Error("Invalid credentials") } + var logoutCalledTimes = 0 override suspend fun logout() { - delay(1000) + logoutCalledTimes++ currentUser.value = null } @@ -50,7 +53,13 @@ class AuthServiceMock : AuthService { } } - override fun observeUser(): Flow { - return currentUser.asStateFlow() + override val observeUser: Flow = currentUser.asStateFlow() + + override fun getCurrentUserIfAny(): User? { + return currentUser.asStateFlow().value } } + +object DummyUsers { + val defaultTestUser = User("test@test.com", "1234") +} diff --git a/shared/src/commonTest/kotlin/com/corrado4eyes/cucumberplayground/cucumber/viewModels/HomeViewModelTest.kt b/shared/src/commonTest/kotlin/com/corrado4eyes/cucumberplayground/cucumber/viewModels/HomeViewModelTest.kt new file mode 100644 index 0000000..f99f199 --- /dev/null +++ b/shared/src/commonTest/kotlin/com/corrado4eyes/cucumberplayground/cucumber/viewModels/HomeViewModelTest.kt @@ -0,0 +1,57 @@ +package com.splendo.cucumberplayground.cucumber.viewModels + +import com.splendo.cucumberplayground.cucumber.login.AuthServiceMock +import com.splendo.cucumberplayground.cucumber.login.DummyUsers.defaultTestUser +import com.splendo.cucumberplayground.models.Strings +import com.splendo.cucumberplayground.services.AuthService +import com.splendo.cucumberplayground.viewModels.home.HomeViewModel +import com.splendo.kaluga.test.base.yieldMultiple +import com.splendo.kaluga.test.koin.KoinUIThreadViewModelTest +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertNull +import kotlinx.coroutines.CoroutineScope +import org.koin.core.component.get +import org.koin.dsl.module + +class HomeViewModelTest : KoinUIThreadViewModelTest() { + class KoinContext : KoinViewModelTestContext( + module { + single { AuthServiceMock(getOrNull()) } + single { defaultTestUser } + } + ) { + val authService = get() as AuthServiceMock + override val viewModel: HomeViewModel = HomeViewModel() + } + + @Test + fun test_title_matches_value() = testOnUIThread { + assertEquals(Strings.Screen.Title.home, viewModel.screenTitle) + } + + @Test + fun test_button_title_matches_value() = testOnUIThread { + assertEquals(Strings.Button.Title.logout, viewModel.buttonTitle) + } + + @Test + fun test_button_action_triggers_logout() = testOnUIThread { + assertEquals(0, authService.logoutCalledTimes) + assertEquals(defaultTestUser, authService.getCurrentUserIfAny()) + viewModel.logout() + yieldMultiple(3) + assertEquals(1, authService.logoutCalledTimes) + assertNull(authService.getCurrentUserIfAny()) + + } + + @Test + fun test_list_contains_20_items() = testOnUIThread { + assertEquals(20, viewModel.scrollableItems.size) + } + + override val createTestContext: suspend (scope: CoroutineScope) -> KoinContext + get() = { KoinContext() } + +} \ No newline at end of file diff --git a/shared/src/commonTest/kotlin/com/corrado4eyes/cucumberplayground/cucumber/viewModels/LoginViewModelTest.kt b/shared/src/commonTest/kotlin/com/corrado4eyes/cucumberplayground/cucumber/viewModels/LoginViewModelTest.kt index a311605..4092254 100644 --- a/shared/src/commonTest/kotlin/com/corrado4eyes/cucumberplayground/cucumber/viewModels/LoginViewModelTest.kt +++ b/shared/src/commonTest/kotlin/com/corrado4eyes/cucumberplayground/cucumber/viewModels/LoginViewModelTest.kt @@ -1,8 +1,8 @@ -package com.corrado4eyes.cucumberplayground.cucumber.viewModels +package com.splendo.cucumberplayground.cucumber.viewModels -import com.corrado4eyes.cucumberplayground.cucumber.login.AuthServiceMock -import com.corrado4eyes.cucumberplayground.services.AuthService -import com.corrado4eyes.cucumberplayground.viewModels.login.LoginViewModel +import com.splendo.cucumberplayground.cucumber.login.AuthServiceMock +import com.splendo.cucumberplayground.services.AuthService +import com.splendo.cucumberplayground.viewModels.login.LoginViewModel import com.splendo.kaluga.test.koin.KoinUIThreadViewModelTest import kotlin.test.Test import kotlin.test.assertEquals @@ -15,11 +15,11 @@ import org.koin.dsl.module class LoginViewModelTest : KoinUIThreadViewModelTest() { class KoinContext : KoinViewModelTestContext( module { - single { AuthServiceMock() } + single { AuthServiceMock(getOrNull()) } } ) { val authService = get() as AuthServiceMock - override val viewModel: LoginViewModel = LoginViewModel(authService) + override val viewModel: LoginViewModel = LoginViewModel() } @Test diff --git a/shared/src/iosMain/kotlin/com/corrado4eyes/cucumberplayground/di/CucumberDependencyInjection.kt b/shared/src/iosMain/kotlin/com/corrado4eyes/cucumberplayground/di/CucumberDependencyInjection.kt index 21d5914..d9627e3 100644 --- a/shared/src/iosMain/kotlin/com/corrado4eyes/cucumberplayground/di/CucumberDependencyInjection.kt +++ b/shared/src/iosMain/kotlin/com/corrado4eyes/cucumberplayground/di/CucumberDependencyInjection.kt @@ -1,4 +1,4 @@ -package com.corrado4eyes.cucumberplayground.di +package com.splendo.cucumberplayground.di import kotlinx.coroutines.newSingleThreadContext import org.koin.core.scope.Scope