diff --git a/artifacts/page-object-code-gen b/artifacts/page-object-code-gen new file mode 100755 index 000000000..4b47a1c91 --- /dev/null +++ b/artifacts/page-object-code-gen @@ -0,0 +1,7 @@ +#!/bin/bash +echo "Make ui damp and pull it" +pathToUiDump=$(adb shell uiautomator dump | grep -oE '/.*?xml') +adb pull "$pathToUiDump" +echo "Create page object" +java -jar page-object-code-gen.jar window_dump.xml "$1" "$2" +rm ./*.xml diff --git a/artifacts/page-object-code-gen.bat b/artifacts/page-object-code-gen.bat new file mode 100644 index 000000000..a80abaa14 --- /dev/null +++ b/artifacts/page-object-code-gen.bat @@ -0,0 +1,7 @@ +echo off +echo Make ui damp and pull it +adb shell uiautomator dump +adb pull /sdcard/window_dump.xml +echo Create page object +java -jar page-object-code-gen.jar window_dump.xml %~1 %~2 +del window_dump.xml diff --git a/artifacts/page-object-code-gen.jar b/artifacts/page-object-code-gen.jar new file mode 100644 index 000000000..76a67dc10 Binary files /dev/null and b/artifacts/page-object-code-gen.jar differ diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 08e00c3ab..ed883b701 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -13,6 +13,13 @@ androidXTest = "1.6.1" testOrchestrator = "1.4.2" lifecycle = "2.6.2" thirdPartyReport = "0.19.1035" +agp = "7.2.2" +org-jetbrains-kotlin-android = "1.9.0" +core-ktx = "1.9.0" +junit = "4.13.2" +androidx-test-ext-junit = "1.1.5" +appcompat = "1.6.1" +material = "1.8.0" [libraries] # plugins @@ -52,6 +59,7 @@ kakaoCompose = { module = "io.github.kakaocup:compose", version.ref = "kakaoComp kakaoExtClicks = { module = "io.github.kakaocup:kakao-ext-clicks", version.ref = "kakaoExtClicks" } junit = "junit:junit:4.13.2" junitJupiter = "org.junit.jupiter:junit-jupiter:5.9.0" +assertj = "org.assertj:assertj-core:3.11.1" truth = "com.google.truth:truth:1.3.0" mockk = "io.mockk:mockk:1.13.12" @@ -79,8 +87,15 @@ allureKotlinModel = { module = "io.qameta.allure:allure-kotlin-model", version.r allureKotlinCommons = { module = "io.qameta.allure:allure-kotlin-commons", version.ref = "allure" } allureKotlinJunit4 = { module = "io.qameta.allure:allure-kotlin-junit4", version.ref = "allure" } allureKotlinAndroid = { module = "io.qameta.allure:allure-kotlin-android", version.ref = "allure" } - +core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "core-ktx" } +junit-junit = { group = "junit", name = "junit", version.ref = "junit" } +androidx-test-ext-junit = { group = "androidx.test.ext", name = "junit", version.ref = "androidx-test-ext-junit" } +androidx-appcompat-appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" } +com-google-android-material-material = { group = "com.google.android.material", name = "material", version.ref = "material" } [bundles] espresso = ["espressoCore", "espressoWeb"] allure = ["allureKotlinModel", "allureKotlinCommons", "allureKotlinJunit4", "allureKotlinAndroid"] compose = ["composeActivity", "composeUiTooling", "composeMaterial", "composeTestManifest", "composeCompiler"] +[plugins] +com-android-library = { id = "com.android.library", version.ref = "agp" } +org-jetbrains-kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "org-jetbrains-kotlin-android" } diff --git a/page-object-code-gen/build.gradle.kts b/page-object-code-gen/build.gradle.kts new file mode 100644 index 000000000..34b391182 --- /dev/null +++ b/page-object-code-gen/build.gradle.kts @@ -0,0 +1,36 @@ +plugins { + id("convention.kotlin-app") + id("convention.third-party-report") +} + +dependencies { + implementation(libs.kotlinStdlib) + implementation(libs.kotlinCli) + implementation(files("src/libs/kakao.jar")) + testImplementation(libs.junit) + testImplementation(libs.assertj) +} + +setProperty("mainClassName", "com.kaspresso.components.pageobjectcodegen.CreatePageObjectFromUiDumpKt") + +tasks.withType() { + manifest { + attributes["Main-Class"] = "com.kaspresso.components.pageobjectcodegen.CreatePageObjectFromUiDumpKt" + } + from( + configurations.runtimeClasspath.get().map { + if (it.isDirectory) it else zipTree(it) + }, + ) { + exclude("META-INF/**/**/module-info.class") + } + exclude("NOTICE.txt") + exclude("LICENSE.txt") + doLast{ + copy{ + from("$buildDir/libs/page-object-code-gen.jar") + into("$rootDir/artifacts") + duplicatesStrategy = DuplicatesStrategy.INCLUDE + } + } +} diff --git a/page-object-code-gen/src/libs/kakao.jar b/page-object-code-gen/src/libs/kakao.jar new file mode 100644 index 000000000..5d1bcf2e7 Binary files /dev/null and b/page-object-code-gen/src/libs/kakao.jar differ diff --git a/page-object-code-gen/src/main/AndroidManifest.xml b/page-object-code-gen/src/main/AndroidManifest.xml new file mode 100644 index 000000000..a88d3d0a1 --- /dev/null +++ b/page-object-code-gen/src/main/AndroidManifest.xml @@ -0,0 +1 @@ + diff --git a/page-object-code-gen/src/main/java/com/kaspresso/components/pageobjectcodegen/CreatePageObjectFromUiDump.kt b/page-object-code-gen/src/main/java/com/kaspresso/components/pageobjectcodegen/CreatePageObjectFromUiDump.kt new file mode 100644 index 000000000..f0f8e632b --- /dev/null +++ b/page-object-code-gen/src/main/java/com/kaspresso/components/pageobjectcodegen/CreatePageObjectFromUiDump.kt @@ -0,0 +1,134 @@ +package com.kaspresso.components.pageobjectcodegen + +import com.kaspresso.components.pageobjectcodegen.ViewType.Companion.collectableElements +import com.kaspresso.components.pageobjectcodegen.ViewType.Companion.elementsWithChild +import org.w3c.dom.Node +import java.io.File +import java.nio.charset.Charset +import javax.xml.parsers.DocumentBuilderFactory + +/** + * inputs: + * 1. Path to xml file with UI Dump + * 2. Name of generated class + * 3. Path for generated file + * output: + * Kotlin file with screen code in the same directory as jar execute + */ +fun main(vararg args: String) { + lateinit var inputFilePath: String + lateinit var className: String + + try { + inputFilePath = args[0] + } catch (e: Exception) { + throw Exception("No file path") + } + if (!File(inputFilePath).isFile) { + throw Exception("File is not exist or directory") + } + + className = try { + args[1] + } catch (e: Exception) { + println("You put empty class name, we change it to \"TestClass\"") + "TestClass" + } + + if (!className.contains(Regex("^[A-Z]\\S*$"))) { + println("You put incorrect class name, we change it to \"TestClass\"") + className = "TestClass" + } + + var outputFilePath: String = try { + args[2] + } catch (e: Exception) { + println("Output file will be locate in directory where you ran this script with name $className.kt") + "" + } + + if (!File(outputFilePath).exists() && outputFilePath.isNotEmpty()) { + File(outputFilePath).mkdirs() + } + + outputFilePath = if (outputFilePath.isNotEmpty()) { + "$outputFilePath/$className.kt" + } else { + "$className.kt" + } + + val filePackage = outputFilePath.findPackage() + + val documentBuilderFactory = DocumentBuilderFactory.newInstance() + val documentBuilder = documentBuilderFactory.newDocumentBuilder() + val doc = documentBuilder.parse(inputFilePath) + + val screenElements: List = findAllViewInDump(doc.firstChild.firstChild) + + PageObjectGenerator(screenElements, filePackage, className).writeToFile(outputFilePath) +} + +fun findAllViewInDump(root: Node, goToSiblings: Boolean = true): MutableList { + val result = mutableListOf() + if (root.nodeName == "node") { + val attr = root.attributes + if (attr.getNamedItem("class").nodeValue in collectableElements && attr.getNamedItem("resource-id").nodeValue != "") { + result.add(getViewFromNode(root)) + } + if (attr.getNamedItem("class").nodeValue in elementsWithChild) { + val res = mutableSetOf>() + val children = root.childNodes + for (i in 0 until children.length) { + if (children.item(i).nodeName == "node") { + res.add(findAllViewInDump(children.item(i), false)) + } + } + result.add(getViewWithChildrenFromNode(root, res)) + } + if (root.hasChildNodes() && attr.getNamedItem("class").nodeValue !in elementsWithChild) { + result.addAll(findAllViewInDump(root.firstChild)) + } + } + if (root.nextSibling != null && goToSiblings) { + result.addAll(findAllViewInDump(root.nextSibling)) + } + return result +} + +fun getViewWithChildrenFromNode(node: Node, childViews: Set>): RecyclerView { + val attr = node.attributes + val viewType = attr.getNamedItem("class").nodeValue.substringAfterLast(".") + return RecyclerView( + attr.getNamedItem("resource-id").nodeValue.substringAfterLast("/"), + ViewType.valueOf(viewType), + attr.getNamedItem("package").nodeValue, + childViews, + ) +} + +fun getViewFromNode(node: Node): View { + val attr = node.attributes + val viewType = attr.getNamedItem("class").nodeValue.substringAfterLast(".") + return View( + attr.getNamedItem("resource-id").nodeValue.substringAfterLast("/"), + ViewType.valueOf(viewType), + attr.getNamedItem("package").nodeValue, + ) +} + +fun String.findPackage(): String { + return split("/").toMutableList() + .dropWhile { it != "com" }.dropLast(1).joinToString(separator = ".") +} + +fun Generator.writeToFile(filePath: String) { + val writer = TextWriter() + generate(writer) + val file = File(filePath) + val printWriter = file.printWriter(Charset.forName("UTF-8")) + try { + printWriter.print(writer.toString()) + } finally { + printWriter.close() + } +} diff --git a/page-object-code-gen/src/main/java/com/kaspresso/components/pageobjectcodegen/Generator.kt b/page-object-code-gen/src/main/java/com/kaspresso/components/pageobjectcodegen/Generator.kt new file mode 100644 index 000000000..5bbf03345 --- /dev/null +++ b/page-object-code-gen/src/main/java/com/kaspresso/components/pageobjectcodegen/Generator.kt @@ -0,0 +1,5 @@ +package com.kaspresso.components.pageobjectcodegen + +interface Generator { + fun generate(writer: TextWriter) +} diff --git a/page-object-code-gen/src/main/java/com/kaspresso/components/pageobjectcodegen/KotlinCodeGenerator.kt b/page-object-code-gen/src/main/java/com/kaspresso/components/pageobjectcodegen/KotlinCodeGenerator.kt new file mode 100644 index 000000000..aa3c3eff9 --- /dev/null +++ b/page-object-code-gen/src/main/java/com/kaspresso/components/pageobjectcodegen/KotlinCodeGenerator.kt @@ -0,0 +1,27 @@ +package com.kaspresso.components.pageobjectcodegen + +abstract class KotlinCodeGenerator(val elements: List, private val filePackage: String) : Generator { + override fun generate(writer: TextWriter) { + with(writer) { + if (filePackage.isNotEmpty()) { + append("package $filePackage", 2) + } + createImports(elements).forEach { + append(it) + } + nextLine() + } + } + + private fun createImports(screenElements: List): List { + val importsList = mutableSetOf("import com.screens.common.KScreen", "import ${screenElements.first().packages}.R") + + for (element in screenElements) { + importsList.addAll(element.viewType.getClass()) + if (element is RecyclerView) { + importsList.addAll(createImports(element.childView.flatten())) + } + } + return importsList.sorted() + } +} diff --git a/page-object-code-gen/src/main/java/com/kaspresso/components/pageobjectcodegen/PageObjectGenerator.kt b/page-object-code-gen/src/main/java/com/kaspresso/components/pageobjectcodegen/PageObjectGenerator.kt new file mode 100644 index 000000000..961d9f8c4 --- /dev/null +++ b/page-object-code-gen/src/main/java/com/kaspresso/components/pageobjectcodegen/PageObjectGenerator.kt @@ -0,0 +1,46 @@ +package com.kaspresso.components.pageobjectcodegen + +class PageObjectGenerator(elements: List, filePackage: String, private val className: String) : + KotlinCodeGenerator(elements, filePackage) { + override fun generate(writer: TextWriter) { + super.generate(writer) + with(writer) { + codeBlock("object $className : KScreen<$className>()") { + append(LAYOUT) + append(VIEWCLASS, 2) + createElements(elements).forEach { + append(it) + } + nextLine() + elements.forEach { view -> + if (view is RecyclerView) { + for (i in 0 until view.childView.size) { + codeBlock( + "class ${view.childClassNames[i]}(matcher: Matcher) : KRecyclerItem<${view.childClassNames[i]}>(matcher)", + countOfLinesAfterBegin = 1, + countOfLinesAfterEnd = 2, + ) { + createElements(view.childView.elementAt(i)).forEach { + append(it, countOfLinesAfterText = 0, countOfLinesBeforeText = 1) + } + } + } + } + } + codeBlock("override fun BaseTestContext.waitForScreen()", countOfLinesAfterBegin = 1) { + append(TODO, 0) + } + } + } + } + + private fun createElements(screenElements: List): List { + return screenElements.map { it.toKaspressoExpression() } + } + + companion object Constants { + private const val LAYOUT = "override val layoutId: Int? = TODO(\"Need To Implement\")" + private const val VIEWCLASS = "override val viewClass: Class<*>? = TODO(\"Need To Implement\")" + private const val TODO = "TODO(\"Need To Implement\")" + } +} diff --git a/page-object-code-gen/src/main/java/com/kaspresso/components/pageobjectcodegen/TextWriter.kt b/page-object-code-gen/src/main/java/com/kaspresso/components/pageobjectcodegen/TextWriter.kt new file mode 100644 index 000000000..e34f275dc --- /dev/null +++ b/page-object-code-gen/src/main/java/com/kaspresso/components/pageobjectcodegen/TextWriter.kt @@ -0,0 +1,62 @@ +package com.kaspresso.components.pageobjectcodegen + +import java.lang.StringBuilder + +class TextWriter(private val indentation: Int = 0) { + + private val line = StringBuilder() + private val lines = mutableListOf() + + init { + append(" ".repeat(indentation), 0) + } + + fun append(text: String, countOfLinesAfterText: Int = 1, countOfLinesBeforeText: Int = 0): TextWriter = apply { + nextLine(countOfLinesBeforeText) + line.append(text) + nextLine(countOfLinesAfterText) + } + + fun nextLine(count: Int = 1): TextWriter = apply { + for (i in 0 until count) { + commitLine() + initNewLine() + } + } + + private fun commitLine() { + if (!line.all { it == ' ' }) { + lines.add(line.toString()) + } else { + lines.add("") + } + } + + private fun initNewLine() { + line.setLength(0) + line.append(" ".repeat(indentation)) + } + + private fun withIncreasedIndentation(): TextWriter { + val writer = TextWriter(indentation + INDENTATION_STEP) + lines.add(writer) + return writer + } + + override fun toString(): String { + commitLine() + return lines.joinToString("\n") + } + + fun codeBlock(header: String, countOfLinesAfterBegin: Int = 2, countOfLinesAfterEnd: Int = 0, block: TextWriter.() -> Unit) { + append("$header {", countOfLinesAfterBegin) + with(withIncreasedIndentation()) { + block() + } + append("}", countOfLinesAfterEnd) + } + + companion object Constants { + private const val INDENTATION_STEP: Int = 4 + } +} diff --git a/page-object-code-gen/src/main/java/com/kaspresso/components/pageobjectcodegen/View.kt b/page-object-code-gen/src/main/java/com/kaspresso/components/pageobjectcodegen/View.kt new file mode 100644 index 000000000..cfc6036b1 --- /dev/null +++ b/page-object-code-gen/src/main/java/com/kaspresso/components/pageobjectcodegen/View.kt @@ -0,0 +1,43 @@ +package com.kaspresso.components.pageobjectcodegen + +data class View( + override val resourceId: String, + override val viewType: ViewType, + override val packages: String, +) : BaseView { + + override fun toKaspressoExpression(): String { + return "val ${resourceId.toCamelCase()} = K$viewType { withId(R.id.$resourceId) }" + } +} + +data class RecyclerView( + override val resourceId: String, + override val viewType: ViewType, + override val packages: String, + val childView: Set>, +) : BaseView { + + val childClassNames = List(childView.size) { if (it == 0) "RecyclerViewItem" else "RecyclerViewItem$it" } + + override fun toKaspressoExpression(): String { + return """val ${resourceId.toCamelCase()} = KRecyclerView( + builder = { withId(R.id.$resourceId) }, + itemTypeBuilder = { ${childClassNames.joinToString(separator = ",\n" + "\t".repeat(AMOUNT_OF_TABS)) { "itemType(::$it)" }} }, + )""" + } + + companion object { + private const val AMOUNT_OF_TABS = 7 + } +} + +interface BaseView { + + val resourceId: String + val viewType: ViewType + val packages: String + fun toKaspressoExpression(): String + + fun String.toCamelCase() = replace("_[a-z]".toRegex()) { it.value.last().uppercase() } +} diff --git a/page-object-code-gen/src/main/java/com/kaspresso/components/pageobjectcodegen/ViewType.kt b/page-object-code-gen/src/main/java/com/kaspresso/components/pageobjectcodegen/ViewType.kt new file mode 100644 index 000000000..c12110460 --- /dev/null +++ b/page-object-code-gen/src/main/java/com/kaspresso/components/pageobjectcodegen/ViewType.kt @@ -0,0 +1,41 @@ +package com.kaspresso.components.pageobjectcodegen + +import io.github.kakaocup.kakao.edit.KEditText +import io.github.kakaocup.kakao.image.KImageView +import io.github.kakaocup.kakao.recycler.KRecyclerItem +import io.github.kakaocup.kakao.recycler.KRecyclerView +import io.github.kakaocup.kakao.text.KButton +import io.github.kakaocup.kakao.text.KTextView + +enum class ViewType(val androidName: String) { + TextView("android.widget.TextView") { + override fun getClass(): List { + return listOf("import ${KTextView::class.qualifiedName}") + } + }, + ImageView("android.widget.ImageView") { + override fun getClass(): List { + return listOf("import ${KImageView::class.qualifiedName}") + } + }, + Button("android.widget.Button") { + override fun getClass(): List { + return listOf("import ${KButton::class.qualifiedName}") + } + }, + EditText("android.widget.EditText") { + override fun getClass(): List { + return listOf("import ${KEditText::class.qualifiedName}") + } + }, + RecyclerView("androidx.recyclerview.widget.RecyclerView") { + override fun getClass(): List { + return listOf("import ${KRecyclerItem::class.qualifiedName}", "import ${KRecyclerView::class.qualifiedName}") + } + }, ; + abstract fun getClass(): List + companion object { + val elementsWithChild = listOf(RecyclerView.androidName) + val collectableElements = ViewType.values().map { it.androidName }.filter { it !in elementsWithChild } + } +} diff --git a/page-object-code-gen/src/test/java/com/kaspresso/components/pageobjectcodegen/CodeGenTest.kt b/page-object-code-gen/src/test/java/com/kaspresso/components/pageobjectcodegen/CodeGenTest.kt new file mode 100644 index 000000000..87d7cc084 --- /dev/null +++ b/page-object-code-gen/src/test/java/com/kaspresso/components/pageobjectcodegen/CodeGenTest.kt @@ -0,0 +1,40 @@ +package com.kaspresso.components.pageobjectcodegen + +import org.assertj.core.api.Assertions.assertThat +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.Parameterized +import java.io.File +import java.lang.Runtime + +@RunWith(Parameterized::class) +class CodeGenTest( + private val inputPath: String, + private val outputDirectory: String, + private val className: String, + private val resultFile: String, +) { + + @Test + fun checkCodeGen() { + val jarFile = File("build/libs/page-object-code-gen.jar") + val inputFile = File("src/test/resources/$inputPath") + Runtime.getRuntime().exec("java -jar $jarFile $inputFile $className $outputDirectory") + Thread.sleep(15000) + val actualFile = File("$outputDirectory/$className.kt") + val expectedFile1 = File("src/test/resources/$resultFile.txt") + assertThat(actualFile).hasSameContentAs(expectedFile1) + } + companion object { + @JvmStatic + @Parameterized.Parameters + fun data(): Collection> { + return listOf( + arrayOf("source1.xml", "build/generated/res/com/kaspresso/components/pageobjectcodegen", "TestClass1", "Result1"), + arrayOf("source_recycler_view.xml", "build/generated/res/com/kaspresso/components/pageobjectcodegen", "RecyclerView", "ResultRecyclerView"), + arrayOf("source2.xml", "build/generated/res/com/kaspresso/components/pageobjectcodegen", "TestClass2", "Result2"), + arrayOf("source3.xml", "build/generated/res/com/kaspresso/components/pageobjectcodegen", "TestClass3", "Result3"), + ) + } + } +} diff --git a/page-object-code-gen/src/test/resources/Result1.txt b/page-object-code-gen/src/test/resources/Result1.txt new file mode 100644 index 000000000..8478fc4c2 --- /dev/null +++ b/page-object-code-gen/src/test/resources/Result1.txt @@ -0,0 +1,22 @@ +package com.kaspresso.components.pageobjectcodegen + +import com.kms.free.R +import com.screens.common.KScreen +import io.github.kakaocup.kakao.image.KImageView +import io.github.kakaocup.kakao.text.KButton +import io.github.kakaocup.kakao.text.KTextView + +object TestClass1 : KScreen() { + + override val layoutId: Int? = TODO("Need To Implement") + override val viewClass: Class<*>? = TODO("Need To Implement") + + val featureInfoImage = KImageView { withId(R.id.feature_info_image) } + val featureInfoTitle = KTextView { withId(R.id.feature_info_title) } + val featureInfoMessage = KTextView { withId(R.id.feature_info_message) } + val featureInfoPrimaryButton = KButton { withId(R.id.feature_info_primary_button) } + + override fun BaseTestContext.waitForScreen() { + TODO("Need To Implement") + } +} diff --git a/page-object-code-gen/src/test/resources/Result2.txt b/page-object-code-gen/src/test/resources/Result2.txt new file mode 100644 index 000000000..db13a404f --- /dev/null +++ b/page-object-code-gen/src/test/resources/Result2.txt @@ -0,0 +1,28 @@ +package com.kaspresso.components.pageobjectcodegen + +import com.kms.free.R +import com.screens.common.KScreen +import io.github.kakaocup.kakao.image.KImageView +import io.github.kakaocup.kakao.text.KButton +import io.github.kakaocup.kakao.text.KTextView + +object TestClass2 : KScreen() { + + override val layoutId: Int? = TODO("Need To Implement") + override val viewClass: Class<*>? = TODO("Need To Implement") + + val profileLabel = KTextView { withId(R.id.profile_label) } + val accountInfoHeaderText = KTextView { withId(R.id.account_info_header_text) } + val accountInfoDescription = KTextView { withId(R.id.account_info_description) } + val signInButton = KButton { withId(R.id.sign_in_button) } + val licenseIcon = KImageView { withId(R.id.license_icon) } + val licenseHeaderText = KTextView { withId(R.id.license_header_text) } + val promoFirstHeader = KTextView { withId(R.id.promo_first_header) } + val promoSecondHeader = KTextView { withId(R.id.promo_second_header) } + val promoIcon = KImageView { withId(R.id.promo_icon) } + val enterPromo = KButton { withId(R.id.enter_promo) } + + override fun BaseTestContext.waitForScreen() { + TODO("Need To Implement") + } +} diff --git a/page-object-code-gen/src/test/resources/Result3.txt b/page-object-code-gen/src/test/resources/Result3.txt new file mode 100644 index 000000000..904c6b83d --- /dev/null +++ b/page-object-code-gen/src/test/resources/Result3.txt @@ -0,0 +1,23 @@ +package com.kaspresso.components.pageobjectcodegen + +import com.kms.free.R +import com.screens.common.KScreen +import io.github.kakaocup.kakao.edit.KEditText +import io.github.kakaocup.kakao.text.KButton +import io.github.kakaocup.kakao.text.KTextView + +object TestClass3 : KScreen() { + + override val layoutId: Int? = TODO("Need To Implement") + override val viewClass: Class<*>? = TODO("Need To Implement") + + val title = KTextView { withId(R.id.title) } + val subtitle = KTextView { withId(R.id.subtitle) } + val addFromContacts = KTextView { withId(R.id.add_from_contacts) } + val phoneNumberEditText = KEditText { withId(R.id.phone_number_edit_text) } + val checkForLeaks = KButton { withId(R.id.check_for_leaks) } + + override fun BaseTestContext.waitForScreen() { + TODO("Need To Implement") + } +} diff --git a/page-object-code-gen/src/test/resources/ResultRecyclerView.txt b/page-object-code-gen/src/test/resources/ResultRecyclerView.txt new file mode 100644 index 000000000..81b1bddca --- /dev/null +++ b/page-object-code-gen/src/test/resources/ResultRecyclerView.txt @@ -0,0 +1,34 @@ +package com.kaspresso.components.pageobjectcodegen + +import com.kms.free.R +import com.screens.common.KScreen +import io.github.kakaocup.kakao.image.KImageView +import io.github.kakaocup.kakao.recycler.KRecyclerItem +import io.github.kakaocup.kakao.recycler.KRecyclerView +import io.github.kakaocup.kakao.text.KButton +import io.github.kakaocup.kakao.text.KTextView + +object RecyclerView : KScreen() { + + override val layoutId: Int? = TODO("Need To Implement") + override val viewClass: Class<*>? = TODO("Need To Implement") + + val dialogTitle = KTextView { withId(R.id.dialog_title) } + val settingsText = KTextView { withId(R.id.settings_text) } + val choicesList = KRecyclerView( + builder = { withId(R.id.choices_list) }, + itemTypeBuilder = { itemType(::RecyclerViewItem) }, + ) + val cancelButton = KButton { withId(R.id.cancel_button) } + + class RecyclerViewItem(matcher: Matcher) : KRecyclerItem(matcher) { + + val icon = KImageView { withId(R.id.icon) } + val title = KTextView { withId(R.id.title) } + val subtitle = KTextView { withId(R.id.subtitle) } + } + + override fun BaseTestContext.waitForScreen() { + TODO("Need To Implement") + } +} diff --git a/page-object-code-gen/src/test/resources/source1.xml b/page-object-code-gen/src/test/resources/source1.xml new file mode 100644 index 000000000..97b6a484b --- /dev/null +++ b/page-object-code-gen/src/test/resources/source1.xml @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/page-object-code-gen/src/test/resources/source2.xml b/page-object-code-gen/src/test/resources/source2.xml new file mode 100644 index 000000000..b154f29dc --- /dev/null +++ b/page-object-code-gen/src/test/resources/source2.xml @@ -0,0 +1,50 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/page-object-code-gen/src/test/resources/source3.xml b/page-object-code-gen/src/test/resources/source3.xml new file mode 100644 index 000000000..7e06cc4bf --- /dev/null +++ b/page-object-code-gen/src/test/resources/source3.xml @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/page-object-code-gen/src/test/resources/source_recycler_view.xml b/page-object-code-gen/src/test/resources/source_recycler_view.xml new file mode 100644 index 000000000..7498d0405 --- /dev/null +++ b/page-object-code-gen/src/test/resources/source_recycler_view.xml @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/settings.gradle.kts b/settings.gradle.kts index 5fb8856ce..eca832010 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -36,6 +36,8 @@ include( ":adb-server:adb-server-desktop-device-connection", ":adb-server:adb-server-device", + ":page-object-code-gen", + ":kaspresso", ":kautomator", ":allure-support", @@ -48,5 +50,5 @@ include( ":samples:kaspresso-allure-support-sample", ":samples:kaspresso-compose-support-sample", - ":tutorial" + ":tutorial", )