-
Notifications
You must be signed in to change notification settings - Fork 159
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
ISSUE-405: init commit with simple code gen (#584)
* ISSUE-405: init commit with simple code gen * ISSUE-405: some refactoring * ISSUE-405: some refactoring and adding test prototype * ISSUE-405: some refactoring * ISSUE-405: some refactoring * ISSUE-405: some refactoring and create unit test * ISSUE-405: some refactoring * ISSUE-405: preparing to add recyclerview support * ISSUE-405: add recyclerview support * ISSUE-405: add recyclerview support * ISSUE-405: fix static analysis * ISSUE-405: fix static analysis * ISSUE-405: fix static analysis * ISSUE-405: add 1 unit test and create SupportedViews.kt to easily adding new supported views * ISSUE-405: add ViewType.kt Enum class for storage supported views * ISSUE-405: fix typo and remove some useless dependencies in build.gradle.kts * ISSUE-405: fix typo and upgrade build.gradle.kts to create .jar and copy it to artifacts * ISSUE-405: remove test prints * ISSUE-405: fix conflict * ISSUE-405: fix conflict #2 * ISSUE-405: improve ViewType.kt * ISSUE-405: create one more test * ISSUE-405: crate shell script to launch jar file * ISSUE-405: crate bat script to launch jar file --------- Co-authored-by: Ovsyannikov_M <[email protected]> Co-authored-by: Maksim Ovsyannikov <[email protected]>
- Loading branch information
1 parent
a55eaa2
commit 03a2d0f
Showing
24 changed files
with
742 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 |
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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<Jar>() { | ||
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 | ||
} | ||
} | ||
} |
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
<manifest package="com.kaspersky.components.pageobjectcodegen" /> |
134 changes: 134 additions & 0 deletions
134
...en/src/main/java/com/kaspresso/components/pageobjectcodegen/CreatePageObjectFromUiDump.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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<BaseView> = findAllViewInDump(doc.firstChild.firstChild) | ||
|
||
PageObjectGenerator(screenElements, filePackage, className).writeToFile(outputFilePath) | ||
} | ||
|
||
fun findAllViewInDump(root: Node, goToSiblings: Boolean = true): MutableList<BaseView> { | ||
val result = mutableListOf<BaseView>() | ||
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<List<BaseView>>() | ||
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<List<BaseView>>): 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() | ||
} | ||
} |
5 changes: 5 additions & 0 deletions
5
page-object-code-gen/src/main/java/com/kaspresso/components/pageobjectcodegen/Generator.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
package com.kaspresso.components.pageobjectcodegen | ||
|
||
interface Generator { | ||
fun generate(writer: TextWriter) | ||
} |
27 changes: 27 additions & 0 deletions
27
...-code-gen/src/main/java/com/kaspresso/components/pageobjectcodegen/KotlinCodeGenerator.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
package com.kaspresso.components.pageobjectcodegen | ||
|
||
abstract class KotlinCodeGenerator(val elements: List<BaseView>, 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<BaseView>): List<String> { | ||
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() | ||
} | ||
} |
46 changes: 46 additions & 0 deletions
46
...-code-gen/src/main/java/com/kaspresso/components/pageobjectcodegen/PageObjectGenerator.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
package com.kaspresso.components.pageobjectcodegen | ||
|
||
class PageObjectGenerator(elements: List<BaseView>, 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<View>) : 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<BaseView>): List<String> { | ||
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\")" | ||
} | ||
} |
62 changes: 62 additions & 0 deletions
62
page-object-code-gen/src/main/java/com/kaspresso/components/pageobjectcodegen/TextWriter.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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<Any>() | ||
|
||
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 | ||
} | ||
} |
Oops, something went wrong.