Merge pull request #4439 from vector-im/feature/adm/developer-mode-sanity-check

Developer mode sanity check & failure screenshots
This commit is contained in:
Benoit Marty 2021-11-10 18:53:50 +01:00 committed by GitHub
commit 8b655edd34
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 362 additions and 61 deletions

View File

@ -18,7 +18,7 @@ jobs:
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
api-level: [28] api-level: [ 29 ]
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
with: with:
@ -56,7 +56,24 @@ jobs:
java-version: '11' java-version: '11'
- name: Run sanity tests on API ${{ matrix.api-level }} - name: Run sanity tests on API ${{ matrix.api-level }}
uses: reactivecircus/android-emulator-runner@v2 uses: reactivecircus/android-emulator-runner@v2
continue-on-error: true # allow pipeline to upload failure results
with: with:
emulator-options: -no-snapshot-save -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none
api-level: ${{ matrix.api-level }} api-level: ${{ matrix.api-level }}
script: ./gradlew $CI_GRADLE_ARG_PROPERTIES -PallWarningsAsErrors=false connectedGplayDebugAndroidTest -Pandroid.testInstrumentationRunnerArguments.class=im.vector.app.ui.UiAllScreensSanityTest emulator-build: 7425822 # workaround to emulator bug: https://github.com/ReactiveCircus/android-emulator-runner/issues/160
script: |
adb root
adb logcat -c
touch emulator.log
chmod 777 emulator.log
adb logcat >> emulator.log &
./gradlew $CI_GRADLE_ARG_PROPERTIES -PallWarningsAsErrors=false connectedGplayDebugAndroidTest -Pandroid.testInstrumentationRunnerArguments.class=im.vector.app.ui.UiAllScreensSanityTest || adb pull storage/emulated/0/Pictures/failure_screenshots
- name: Upload Failing Test Report Log
if: failure()
uses: actions/upload-artifact@v2
with:
name: sanity-error-results
path: |
emulator.log
failure_screenshots/

View File

@ -19,6 +19,7 @@ package im.vector.app
import android.app.Activity import android.app.Activity
import android.view.View import android.view.View
import androidx.annotation.StringRes import androidx.annotation.StringRes
import androidx.fragment.app.FragmentActivity
import androidx.lifecycle.Observer import androidx.lifecycle.Observer
import androidx.test.espresso.Espresso import androidx.test.espresso.Espresso
import androidx.test.espresso.IdlingRegistry import androidx.test.espresso.IdlingRegistry
@ -35,6 +36,11 @@ import androidx.test.runner.lifecycle.ActivityLifecycleCallback
import androidx.test.runner.lifecycle.ActivityLifecycleMonitorRegistry import androidx.test.runner.lifecycle.ActivityLifecycleMonitorRegistry
import androidx.test.runner.lifecycle.Stage import androidx.test.runner.lifecycle.Stage
import com.adevinta.android.barista.interaction.BaristaClickInteractions import com.adevinta.android.barista.interaction.BaristaClickInteractions
import com.google.android.material.bottomsheet.BottomSheetBehavior
import com.google.android.material.bottomsheet.BottomSheetDialog
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
import im.vector.app.core.platform.VectorBaseBottomSheetDialogFragment
import im.vector.app.espresso.tools.waitUntilViewVisible
import org.hamcrest.Matcher import org.hamcrest.Matcher
import org.hamcrest.Matchers import org.hamcrest.Matchers
import org.hamcrest.StringDescription import org.hamcrest.StringDescription
@ -52,6 +58,18 @@ object EspressoHelper {
} }
return currentActivity return currentActivity
} }
inline fun <reified T : VectorBaseBottomSheetDialogFragment<*>> getBottomSheetDialog(): BottomSheetDialogFragment? {
return (getCurrentActivity() as? FragmentActivity)
?.supportFragmentManager
?.fragments
?.filterIsInstance<T>()
?.firstOrNull()
}
}
fun getString(@StringRes id: Int): String {
return EspressoHelper.getCurrentActivity()!!.resources.getString(id)
} }
fun waitForView(viewMatcher: Matcher<View>, timeout: Long = 10_000, waitForDisplayed: Boolean = true): ViewAction { fun waitForView(viewMatcher: Matcher<View>, timeout: Long = 10_000, waitForDisplayed: Boolean = true): ViewAction {
@ -216,3 +234,46 @@ fun clickOnAndGoBack(@StringRes name: Int, block: () -> Unit) {
block() block()
Espresso.pressBack() Espresso.pressBack()
} }
inline fun <reified T : VectorBaseBottomSheetDialogFragment<*>> interactWithSheet(contentMatcher: Matcher<View>, noinline block: () -> Unit = {}) {
waitUntilViewVisible(contentMatcher)
val behaviour = (EspressoHelper.getBottomSheetDialog<T>()!!.dialog as BottomSheetDialog).behavior
withIdlingResource(BottomSheetResource(behaviour, BottomSheetBehavior.STATE_EXPANDED), block)
withIdlingResource(BottomSheetResource(behaviour, BottomSheetBehavior.STATE_HIDDEN)) {}
}
class BottomSheetResource(
private val bottomSheetBehavior: BottomSheetBehavior<*>,
@BottomSheetBehavior.State private val wantedState: Int
) : IdlingResource, BottomSheetBehavior.BottomSheetCallback() {
private var isIdle: Boolean = false
private var resourceCallback: IdlingResource.ResourceCallback? = null
override fun onSlide(bottomSheet: View, slideOffset: Float) {}
override fun onStateChanged(bottomSheet: View, newState: Int) {
val wasIdle = isIdle
isIdle = newState == BottomSheetBehavior.STATE_EXPANDED
if (!wasIdle && isIdle) {
bottomSheetBehavior.removeBottomSheetCallback(this)
resourceCallback?.onTransitionToIdle()
}
}
override fun getName() = "BottomSheet awaiting state: $wantedState"
override fun isIdleNow() = isIdle
override fun registerIdleTransitionCallback(callback: IdlingResource.ResourceCallback) {
resourceCallback = callback
val state = bottomSheetBehavior.state
isIdle = state == wantedState
if (isIdle) {
resourceCallback!!.onTransitionToIdle()
} else {
bottomSheetBehavior.addBottomSheetCallback(this)
}
}
}

View File

@ -0,0 +1,117 @@
/*
* Copyright (c) 2021 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package im.vector.app.espresso.tools
import android.content.ContentResolver
import android.content.ContentValues
import android.graphics.Bitmap
import android.net.Uri
import android.os.Environment
import android.provider.MediaStore
import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation
import org.junit.rules.TestWatcher
import org.junit.runner.Description
import timber.log.Timber
import java.io.File
import java.io.FileOutputStream
import java.io.IOException
import java.io.OutputStream
import java.text.SimpleDateFormat
import java.util.Date
import java.util.Locale
private val SCREENSHOT_FOLDER_LOCATION = "${Environment.DIRECTORY_PICTURES}/failure_screenshots"
private val deviceLanguage = Locale.getDefault().language
class ScreenshotFailureRule : TestWatcher() {
override fun failed(e: Throwable?, description: Description) {
val screenShotName = "$deviceLanguage-${description.methodName}-${SimpleDateFormat("EEE-MMMM-dd-HH:mm:ss").format(Date())}"
val bitmap = getInstrumentation().uiAutomation.takeScreenshot()
storeFailureScreenshot(bitmap, screenShotName)
}
}
/**
* Stores screenshots in sdcard/Pictures/failure_screenshots
*/
private fun storeFailureScreenshot(bitmap: Bitmap, screenshotName: String) {
val contentResolver = getInstrumentation().targetContext.applicationContext.contentResolver
val contentValues = ContentValues().apply {
put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg")
put(MediaStore.Images.Media.DATE_TAKEN, System.currentTimeMillis())
}
if (android.os.Build.VERSION.SDK_INT >= 29) {
useMediaStoreScreenshotStorage(
contentValues,
contentResolver,
screenshotName,
SCREENSHOT_FOLDER_LOCATION,
bitmap
)
} else {
usePublicExternalScreenshotStorage(
contentValues,
contentResolver,
screenshotName,
SCREENSHOT_FOLDER_LOCATION,
bitmap
)
}
}
private fun useMediaStoreScreenshotStorage(
contentValues: ContentValues,
contentResolver: ContentResolver,
screenshotName: String,
screenshotLocation: String,
bitmap: Bitmap
) {
contentValues.put(MediaStore.MediaColumns.DISPLAY_NAME, "$screenshotName.jpeg")
contentValues.put(MediaStore.Images.Media.RELATIVE_PATH, screenshotLocation)
val uri: Uri? = contentResolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues)
if (uri != null) {
contentResolver.openOutputStream(uri)?.let { saveScreenshotToStream(bitmap, it) }
contentResolver.update(uri, contentValues, null, null)
}
}
private fun usePublicExternalScreenshotStorage(
contentValues: ContentValues,
contentResolver: ContentResolver,
screenshotName: String,
screenshotLocation: String,
bitmap: Bitmap
) {
val directory = File(Environment.getExternalStoragePublicDirectory(screenshotLocation).toString())
if (!directory.exists()) {
directory.mkdirs()
}
val file = File(directory, "$screenshotName.jpeg")
saveScreenshotToStream(bitmap, FileOutputStream(file))
contentResolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues)
}
private fun saveScreenshotToStream(bitmap: Bitmap, outputStream: OutputStream) {
outputStream.use {
try {
bitmap.compress(Bitmap.CompressFormat.JPEG, 50, it)
} catch (e: IOException) {
Timber.e("Screenshot was not stored at this time")
}
}
}

View File

@ -19,10 +19,15 @@ package im.vector.app.ui
import androidx.test.ext.junit.rules.ActivityScenarioRule import androidx.test.ext.junit.rules.ActivityScenarioRule
import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.LargeTest import androidx.test.filters.LargeTest
import im.vector.app.R
import im.vector.app.espresso.tools.ScreenshotFailureRule
import im.vector.app.features.MainActivity import im.vector.app.features.MainActivity
import im.vector.app.getString
import im.vector.app.ui.robot.ElementRobot import im.vector.app.ui.robot.ElementRobot
import im.vector.app.ui.robot.withDeveloperMode
import org.junit.Rule import org.junit.Rule
import org.junit.Test import org.junit.Test
import org.junit.rules.RuleChain
import org.junit.runner.RunWith import org.junit.runner.RunWith
import java.util.UUID import java.util.UUID
@ -34,7 +39,9 @@ import java.util.UUID
class UiAllScreensSanityTest { class UiAllScreensSanityTest {
@get:Rule @get:Rule
val activityRule = ActivityScenarioRule(MainActivity::class.java) val testRule = RuleChain
.outerRule(ActivityScenarioRule(MainActivity::class.java))
.around(ScreenshotFailureRule())
private val elementRobot = ElementRobot() private val elementRobot = ElementRobot()
@ -69,13 +76,30 @@ class UiAllScreensSanityTest {
createNewRoom { createNewRoom {
crawl() crawl()
createRoom { createRoom {
postMessage("Hello world!") val message = "Hello world!"
postMessage(message)
crawl() crawl()
crawlMessage(message)
openSettings { crawl() } openSettings { crawl() }
} }
} }
} }
elementRobot.withDeveloperMode {
settings {
advancedSettings { crawlDeveloperOptions() }
}
roomList {
openRoom(getString(R.string.room_displayname_empty_room)) {
val message = "Test view source"
postMessage(message)
openMessageMenu(message) {
viewSource()
}
}
}
}
elementRobot.roomList { elementRobot.roomList {
verifyCreatedRoom() verifyCreatedRoom()
} }

View File

@ -141,3 +141,9 @@ class ElementRobot {
} }
private fun Boolean.toWarningType() = if (this) "shown" else "skipped" private fun Boolean.toWarningType() = if (this) "shown" else "skipped"
fun ElementRobot.withDeveloperMode(block: ElementRobot.() -> Unit) {
settings { toggleDeveloperMode() }
block()
settings { toggleDeveloperMode() }
}

View File

@ -0,0 +1,60 @@
/*
* Copyright (c) 2021 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package im.vector.app.ui.robot
import androidx.test.espresso.Espresso.pressBack
import com.adevinta.android.barista.interaction.BaristaClickInteractions.clickOn
import com.adevinta.android.barista.interaction.BaristaListInteractions.clickListItem
import im.vector.app.R
import java.lang.Thread.sleep
class MessageMenuRobot(
var autoClosed: Boolean = false
) {
fun viewSource() {
clickOn(R.string.view_source)
// wait for library
sleep(1000)
pressBack()
autoClosed = true
}
fun editHistory() {
clickOn(R.string.message_view_edit_history)
pressBack()
autoClosed = true
}
fun addQuickReaction(quickReaction: String) {
clickOn(quickReaction)
autoClosed = true
}
fun addReactionFromEmojiPicker() {
clickOn(R.string.message_add_reaction)
// Wait for emoji to load, it's async now
sleep(2000)
clickListItem(R.id.emojiRecyclerView, 4)
autoClosed = true
}
fun edit() {
clickOn(R.string.edit)
autoClosed = true
}
}

View File

@ -17,21 +17,24 @@
package im.vector.app.ui.robot package im.vector.app.ui.robot
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import androidx.test.espresso.Espresso import androidx.test.espresso.Espresso.onView
import androidx.test.espresso.Espresso.pressBack import androidx.test.espresso.Espresso.pressBack
import androidx.test.espresso.action.ViewActions import androidx.test.espresso.action.ViewActions
import androidx.test.espresso.contrib.RecyclerViewActions import androidx.test.espresso.contrib.RecyclerViewActions
import androidx.test.espresso.matcher.ViewMatchers import androidx.test.espresso.matcher.ViewMatchers
import androidx.test.espresso.matcher.ViewMatchers.withId import androidx.test.espresso.matcher.ViewMatchers.withId
import androidx.test.espresso.matcher.ViewMatchers.withText
import com.adevinta.android.barista.interaction.BaristaClickInteractions import com.adevinta.android.barista.interaction.BaristaClickInteractions
import com.adevinta.android.barista.interaction.BaristaClickInteractions.clickOn import com.adevinta.android.barista.interaction.BaristaClickInteractions.clickOn
import com.adevinta.android.barista.interaction.BaristaClickInteractions.longClickOn import com.adevinta.android.barista.interaction.BaristaClickInteractions.longClickOn
import com.adevinta.android.barista.interaction.BaristaEditTextInteractions.writeTo import com.adevinta.android.barista.interaction.BaristaEditTextInteractions.writeTo
import com.adevinta.android.barista.interaction.BaristaListInteractions.clickListItem
import com.adevinta.android.barista.interaction.BaristaMenuClickInteractions.clickMenu import com.adevinta.android.barista.interaction.BaristaMenuClickInteractions.clickMenu
import com.adevinta.android.barista.interaction.BaristaMenuClickInteractions.openMenu import com.adevinta.android.barista.interaction.BaristaMenuClickInteractions.openMenu
import im.vector.app.R import im.vector.app.R
import im.vector.app.espresso.tools.waitUntilViewVisible import im.vector.app.espresso.tools.waitUntilViewVisible
import im.vector.app.features.home.room.detail.timeline.action.MessageActionsBottomSheet
import im.vector.app.features.reactions.data.EmojiDataSource
import im.vector.app.interactWithSheet
import im.vector.app.waitForView import im.vector.app.waitForView
import java.lang.Thread.sleep import java.lang.Thread.sleep
@ -39,7 +42,9 @@ class RoomDetailRobot {
fun postMessage(content: String) { fun postMessage(content: String) {
writeTo(R.id.composerEditText, content) writeTo(R.id.composerEditText, content)
waitUntilViewVisible(withId(R.id.sendButton))
clickOn(R.id.sendButton) clickOn(R.id.sendButton)
waitUntilViewVisible(withText(content))
} }
fun crawl() { fun crawl() {
@ -55,61 +60,54 @@ class RoomDetailRobot {
pressBack() pressBack()
clickMenu(R.id.search) clickMenu(R.id.search)
pressBack() pressBack()
// Long click on the message
longClickOnMessageTest()
} }
private fun longClickOnMessageTest() { fun crawlMessage(message: String) {
// Test quick reaction // Test quick reaction
longClickOnMessage() val quickReaction = EmojiDataSource.quickEmojis[0] // 👍
waitUntilViewVisible(withId(R.id.bottomSheetRecyclerView)) openMessageMenu(message) {
// Add quick reaction addQuickReaction(quickReaction)
clickOn("\uD83D\uDC4D") // 👍 }
waitUntilViewVisible(withId(R.id.composerEditText))
// Open reactions // Open reactions
longClickOn("\uD83D\uDC4D") // 👍 longClickOn(quickReaction)
// wait for bottom sheet // wait for bottom sheet
pressBack() pressBack()
// Test add reaction // Test add reaction
longClickOnMessage() openMessageMenu(message) {
waitUntilViewVisible(withId(R.id.bottomSheetRecyclerView)) addReactionFromEmojiPicker()
clickOn(R.string.message_add_reaction) }
// Filter
// TODO clickMenu(R.id.search)
// Wait for emoji to load, it's async now
sleep(2000)
clickListItem(R.id.emojiRecyclerView, 4)
waitUntilViewVisible(withId(R.id.composerEditText))
// Test Edit mode // Test Edit mode
longClickOnMessage() openMessageMenu(message) {
waitUntilViewVisible(withId(R.id.bottomSheetRecyclerView)) edit()
clickOn(R.string.edit) }
waitUntilViewVisible(withId(R.id.composerEditText))
// TODO Cancel action // TODO Cancel action
writeTo(R.id.composerEditText, "Hello universe!") writeTo(R.id.composerEditText, "Hello universe!")
// Wait a bit for the keyboard layout to update // Wait a bit for the keyboard layout to update
sleep(30) waitUntilViewVisible(withId(R.id.sendButton))
clickOn(R.id.sendButton) clickOn(R.id.sendButton)
// Wait for the UI to update // Wait for the UI to update
sleep(1000) waitUntilViewVisible(withText("Hello universe! (edited)"))
// Open edit history // Open edit history
longClickOnMessage("Hello universe! (edited)") openMessageMenu("Hello universe! (edited)") {
waitUntilViewVisible(withId(R.id.bottomSheetRecyclerView)) editHistory()
clickOn(R.string.message_view_edit_history) }
pressBack()
} }
private fun longClickOnMessage(text: String = "Hello world!") { fun openMessageMenu(message: String, block: MessageMenuRobot.() -> Unit) {
Espresso.onView(withId(R.id.timelineRecyclerView)) onView(withId(R.id.timelineRecyclerView))
.perform( .perform(
RecyclerViewActions.actionOnItem<RecyclerView.ViewHolder>( RecyclerViewActions.actionOnItem<RecyclerView.ViewHolder>(
ViewMatchers.hasDescendant(ViewMatchers.withText(text)), ViewMatchers.hasDescendant(ViewMatchers.withText(message)),
ViewActions.longClick() ViewActions.longClick()
) )
) )
interactWithSheet<MessageActionsBottomSheet>(contentMatcher = withId(R.id.bottomSheetRecyclerView)) {
val messageMenuRobot = MessageMenuRobot()
block(messageMenuRobot)
if (!messageMenuRobot.autoClosed) {
pressBack()
}
}
} }
fun openSettings(block: RoomSettingsRobot.() -> Unit) { fun openSettings(block: RoomSettingsRobot.() -> Unit) {

View File

@ -17,38 +17,46 @@
package im.vector.app.ui.robot package im.vector.app.ui.robot
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import androidx.test.espresso.Espresso import androidx.test.espresso.Espresso.onView
import androidx.test.espresso.Espresso.pressBack
import androidx.test.espresso.action.ViewActions import androidx.test.espresso.action.ViewActions
import androidx.test.espresso.contrib.RecyclerViewActions import androidx.test.espresso.contrib.RecyclerViewActions
import androidx.test.espresso.matcher.ViewMatchers import androidx.test.espresso.matcher.ViewMatchers
import androidx.test.espresso.matcher.ViewMatchers.withText
import com.adevinta.android.barista.assertion.BaristaVisibilityAssertions import com.adevinta.android.barista.assertion.BaristaVisibilityAssertions
import com.adevinta.android.barista.interaction.BaristaClickInteractions import com.adevinta.android.barista.interaction.BaristaClickInteractions.clickOn
import im.vector.app.R import im.vector.app.R
import im.vector.app.espresso.tools.waitUntilActivityVisible import im.vector.app.espresso.tools.waitUntilActivityVisible
import im.vector.app.features.roomdirectory.RoomDirectoryActivity import im.vector.app.features.roomdirectory.RoomDirectoryActivity
class RoomListRobot { class RoomListRobot {
fun openRoom(roomName: String, block: RoomDetailRobot.() -> Unit) {
clickOn(roomName)
block(RoomDetailRobot())
pressBack()
}
fun verifyCreatedRoom() { fun verifyCreatedRoom() {
Espresso.onView(ViewMatchers.withId(R.id.roomListView)) onView(ViewMatchers.withId(R.id.roomListView))
.perform( .perform(
RecyclerViewActions.actionOnItem<RecyclerView.ViewHolder>( RecyclerViewActions.actionOnItem<RecyclerView.ViewHolder>(
ViewMatchers.hasDescendant(ViewMatchers.withText(R.string.room_displayname_empty_room)), ViewMatchers.hasDescendant(withText(R.string.room_displayname_empty_room)),
ViewActions.longClick() ViewActions.longClick()
) )
) )
Espresso.pressBack() pressBack()
} }
fun newRoom(block: NewRoomRobot.() -> Unit) { fun newRoom(block: NewRoomRobot.() -> Unit) {
BaristaClickInteractions.clickOn(R.id.createGroupRoomButton) clickOn(R.id.createGroupRoomButton)
waitUntilActivityVisible<RoomDirectoryActivity> { waitUntilActivityVisible<RoomDirectoryActivity> {
BaristaVisibilityAssertions.assertDisplayed(R.id.publicRoomsList) BaristaVisibilityAssertions.assertDisplayed(R.id.publicRoomsList)
} }
val newRoomRobot = NewRoomRobot() val newRoomRobot = NewRoomRobot()
block(newRoomRobot) block(newRoomRobot)
if (!newRoomRobot.createdRoom) { if (!newRoomRobot.createdRoom) {
Espresso.pressBack() pressBack()
} }
} }
} }

View File

@ -17,8 +17,11 @@
package im.vector.app.ui.robot.settings package im.vector.app.ui.robot.settings
import androidx.test.espresso.Espresso.pressBack import androidx.test.espresso.Espresso.pressBack
import androidx.test.espresso.matcher.ViewMatchers.withText
import com.adevinta.android.barista.interaction.BaristaClickInteractions.clickOn
import im.vector.app.R import im.vector.app.R
import im.vector.app.espresso.tools.clickOnPreference import im.vector.app.espresso.tools.clickOnPreference
import im.vector.app.espresso.tools.waitUntilViewVisible
class SettingsAdvancedRobot { class SettingsAdvancedRobot {
@ -28,20 +31,19 @@ class SettingsAdvancedRobot {
clickOnPreference(R.string.settings_push_rules) clickOnPreference(R.string.settings_push_rules)
pressBack() pressBack()
}
/* TODO P2 test developer screens fun toggleDeveloperMode() {
// Enable developer mode clickOn(R.string.settings_developer_mode_summary)
clickOnSwitchPreference("SETTINGS_DEVELOPER_MODE_PREFERENCE_KEY") }
fun crawlDeveloperOptions() {
clickOnPreference(R.string.settings_account_data) clickOnPreference(R.string.settings_account_data)
waitUntilViewVisible(withText("m.push_rules"))
clickOn("m.push_rules") clickOn("m.push_rules")
pressBack() pressBack()
pressBack() pressBack()
clickOnPreference(R.string.settings_key_requests) clickOnPreference(R.string.settings_key_requests)
pressBack() pressBack()
// Disable developer mode
clickOnSwitchPreference("SETTINGS_DEVELOPER_MODE_PREFERENCE_KEY")
*/
} }
} }

View File

@ -21,6 +21,12 @@ import im.vector.app.clickOnAndGoBack
class SettingsRobot { class SettingsRobot {
fun toggleDeveloperMode() {
advancedSettings {
toggleDeveloperMode()
}
}
fun general(block: SettingsGeneralRobot.() -> Unit) { fun general(block: SettingsGeneralRobot.() -> Unit) {
clickOnAndGoBack(R.string.settings_general_title) { block(SettingsGeneralRobot()) } clickOnAndGoBack(R.string.settings_general_title) { block(SettingsGeneralRobot()) }
} }
@ -50,7 +56,9 @@ class SettingsRobot {
} }
fun advancedSettings(block: SettingsAdvancedRobot.() -> Unit) { fun advancedSettings(block: SettingsAdvancedRobot.() -> Unit) {
clickOnAndGoBack(R.string.settings_advanced_settings) { block(SettingsAdvancedRobot()) } clickOnAndGoBack(R.string.settings_advanced_settings) {
block(SettingsAdvancedRobot())
}
} }
fun helpAndAbout(block: SettingsHelpRobot.() -> Unit) { fun helpAndAbout(block: SettingsHelpRobot.() -> Unit) {