-
Notifications
You must be signed in to change notification settings - Fork 159
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Issue 699 compose doc loc metadata #700
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
package com.kaspersky.kaspresso.docloc.metadata.extractor | ||
|
||
import com.kaspersky.kaspresso.docloc.metadata.Metadata | ||
|
||
internal interface MetadataExtractor { | ||
fun getMetadata(): Metadata | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
package com.kaspersky.kaspresso.docloc.metadata.extractor | ||
|
||
import com.kaspersky.kaspresso.docloc.metadata.LocalizedString | ||
|
||
internal class MetadataExtractorHelper { | ||
fun resolveAmbiguous(localizedStrings: List<LocalizedString>): List<LocalizedString> { | ||
return localizedStrings.groupBy { it.locValueDescription } | ||
.values | ||
.flatMap { groupedById -> | ||
if (groupedById.size == 1) groupedById else addIndexes( | ||
groupedById | ||
) | ||
} | ||
} | ||
|
||
private fun addIndexes(groupedById: List<LocalizedString>): List<LocalizedString> { | ||
return groupedById.mapIndexed { index, locString -> | ||
locString.copy(locValueDescription = "${locString.locValueDescription}$INDEX_SEPARATOR${index + 1}") | ||
} | ||
} | ||
|
||
companion object { | ||
private const val INDEX_SEPARATOR = '_' | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,102 @@ | ||
package com.kaspersky.kaspresso.docloc.metadata.extractor | ||
|
||
import android.app.Activity | ||
import androidx.test.uiautomator.By | ||
import androidx.test.uiautomator.StaleObjectException | ||
import androidx.test.uiautomator.UiDevice | ||
import com.kaspersky.kaspresso.device.activities.Activities | ||
import com.kaspersky.kaspresso.docloc.metadata.LocalizedString | ||
import com.kaspersky.kaspresso.docloc.metadata.Metadata | ||
import com.kaspersky.kaspresso.docloc.metadata.Window | ||
import com.kaspersky.kaspresso.logger.UiTestLogger | ||
|
||
internal class UiMetadataExtractor( | ||
private val uiDevice: UiDevice, | ||
private val activities: Activities, | ||
private val logger: UiTestLogger, | ||
) : MetadataExtractor { | ||
|
||
private val metadataExtractorHelper = MetadataExtractorHelper() | ||
|
||
override fun getMetadata(): Metadata { | ||
val activity = activities.getResumed() ?: throw RuntimeException("Failed to get current activity") | ||
return createMetadata(activity) | ||
} | ||
|
||
private fun createMetadata(activity: Activity): Metadata { | ||
val localizedStrings = metadataExtractorHelper.resolveAmbiguous( | ||
getLocalizedStrings() | ||
) | ||
val window = activity.window.decorView.run { | ||
Window( | ||
left, | ||
top, | ||
width, | ||
height, | ||
localizedStrings | ||
) | ||
} | ||
return Metadata(window) | ||
} | ||
|
||
private fun getLocalizedStrings(): List<LocalizedString> { | ||
uiDevice.setCompressedLayoutHierarchy(false) | ||
val objectsWithText = uiDevice.findObjects(By.enabled(true)) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 100% needs better UiObjects tree parsing. Probably would be better if we used recursion |
||
.filterNotNull() | ||
.filter { | ||
try { | ||
(it.resourceName != null && it.children.any { !it.text.isNullOrBlank() }) || | ||
(!it.text.isNullOrBlank() && it.resourceName != null) | ||
} catch (ex: StaleObjectException) { | ||
logger.e("UiMetadataExtractor::getLocalizedStrings() - Error while loading object") | ||
false | ||
} | ||
} | ||
|
||
val localizedStrings = mutableListOf<LocalizedString>() | ||
/* | ||
There might be a case when the text itself won't have an ID, but rather the text container will. | ||
For example if we set an id to the button, Ui automator will see it as a container with an id and a child - text view without an id | ||
|
||
Probably buggy. Need better solution. | ||
*/ | ||
for (obj in objectsWithText) { | ||
try { | ||
val resName = obj.resourceName ?: continue | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. maybe it will be better to throw exception? Or we really need to skip item without res name? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The idea is to pick the view that might be the text container or a text itself. Either way we need an id which might be nullable, but I don't think we have to throw an exception |
||
if (obj.text.isNullOrBlank()) { // the text container | ||
obj.children | ||
.filter { !it.text.isNullOrBlank() } | ||
.forEach { | ||
val coords = it.visibleBounds | ||
localizedStrings.add( | ||
LocalizedString( | ||
text = it.text, | ||
locValueDescription = resName, | ||
left = coords.left, | ||
top = coords.top, | ||
width = coords.width(), | ||
height = coords.height(), | ||
) | ||
) | ||
} | ||
} else { // the text itself | ||
val coords = obj.visibleBounds | ||
localizedStrings.add( | ||
LocalizedString( | ||
text = obj.text, | ||
locValueDescription = resName, | ||
left = coords.left, | ||
top = coords.top, | ||
width = coords.width(), | ||
height = coords.height(), | ||
) | ||
) | ||
} | ||
} catch (ex: StaleObjectException) { | ||
logger.e("UiMetadataExtractor::getLocalizedStrings() - error while processing found objects") | ||
} | ||
} | ||
|
||
return localizedStrings | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
package com.kaspersky.kaspresso.docloc.metadata.saver | ||
|
||
import java.io.File | ||
|
||
interface MetadataSaver { | ||
fun saveScreenshotMetadata(folderPath: File, name: String) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
only import without usage?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is used in the consctructor. Before my changes MetadataSaver was in the same pachage as DocLocScreenshotCapturer. I made renamed it to the DefautMetadataSaver and extracted the interface MetadataSaver. Now it has to be imported