1. Разработаны объекты для описания модели страниц приложения (MainPage.kt, NewHabitPage.kt)

2. Созданы тесты для базовых сценариев работы приложения (HabitsSmokeTest.kt)
3. Добавлены зависимости для сборки Allure-отчета
pull/2225/head
Anastasiia Skvortsova 1 week ago
parent 2b24759d6f
commit 7bd9d094b4

@ -4,6 +4,7 @@ plugins {
alias(libs.plugins.ksp) apply false alias(libs.plugins.ksp) apply false
alias(libs.plugins.ktlint.plugin) apply false alias(libs.plugins.ktlint.plugin) apply false
alias(libs.plugins.shadow) apply false alias(libs.plugins.shadow) apply false
id("io.qameta.allure") version "2.11.2"
} }
apply { apply {

@ -33,6 +33,7 @@ shadow = "8.1.1"
sqliteJdbc = "3.45.1.0" sqliteJdbc = "3.45.1.0"
uiautomator = "2.3.0" uiautomator = "2.3.0"
documentfile = "1.0.1" documentfile = "1.0.1"
espressoCore = "3.7.0"
[libraries] [libraries]
annotation = { group = "androidx.annotation", name = "annotation", version.ref = "annotation" } annotation = { group = "androidx.annotation", name = "annotation", version.ref = "annotation" }
@ -75,6 +76,7 @@ rules = { group = "androidx.test", name = "rules", version.ref = "rules" }
sqlite-jdbc = { module = "org.xerial:sqlite-jdbc", version.ref = "sqliteJdbc" } sqlite-jdbc = { module = "org.xerial:sqlite-jdbc", version.ref = "sqliteJdbc" }
uiautomator = { group = "androidx.test.uiautomator", name = "uiautomator", version.ref = "uiautomator" } uiautomator = { group = "androidx.test.uiautomator", name = "uiautomator", version.ref = "uiautomator" }
documentfile = { group = "androidx.documentfile", name = "documentfile", version.ref = "documentfile" } documentfile = { group = "androidx.documentfile", name = "documentfile", version.ref = "documentfile" }
androidx-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" }
[bundles] [bundles]
androidTest = [ androidTest = [

@ -22,12 +22,22 @@ plugins {
alias(libs.plugins.kotlin.android) alias(libs.plugins.kotlin.android)
alias(libs.plugins.ksp) alias(libs.plugins.ksp)
alias(libs.plugins.ktlint.plugin) alias(libs.plugins.ktlint.plugin)
id("io.qameta.allure") version "2.11.2"
} }
tasks.compileLint { tasks.compileLint {
dependsOn("updateTranslators") dependsOn("updateTranslators")
} }
allure {
adapter {
frameworks {
junit4 { }
}
autoconfigureListeners.set(true)
}
}
/* /*
Added on top of kotlinOptions to work around this issue: Added on top of kotlinOptions to work around this issue:
https://youtrack.jetbrains.com/issue/KTIJ-24311/task-current-target-is-17-and-kaptGenerateStubsProductionDebugKotlin-task-current-target-is-1.8-jvm-target-compatibility-should#focus=Comments-27-6798448.0-0 https://youtrack.jetbrains.com/issue/KTIJ-24311/task-current-target-is-17-and-kaptGenerateStubsProductionDebugKotlin-task-current-target-is-1.8-jvm-target-compatibility-should#focus=Comments-27-6798448.0-0
@ -49,7 +59,7 @@ android {
minSdk = 28 minSdk = 28
targetSdk = 36 targetSdk = 36
applicationId = "org.isoron.uhabits" applicationId = "org.isoron.uhabits"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner = "io.qameta.allure.android.runners.AllureAndroidJUnitRunner"
} }
signingConfigs { signingConfigs {
@ -89,6 +99,7 @@ android {
} }
dependencies { dependencies {
implementation(libs.androidx.espresso.core)
compileOnly(libs.jsr250.api) compileOnly(libs.jsr250.api)
coreLibraryDesugaring(libs.desugar.jdk.libs) coreLibraryDesugaring(libs.desugar.jdk.libs)
implementation(libs.appIntro) implementation(libs.appIntro)
@ -114,4 +125,6 @@ dependencies {
androidTestImplementation(libs.bundles.androidTest) androidTestImplementation(libs.bundles.androidTest)
testImplementation(libs.bundles.test) testImplementation(libs.bundles.test)
androidTestImplementation("io.qameta.allure:allure-kotlin-junit4:2.4.0")
androidTestImplementation("io.qameta.allure:allure-kotlin-android:2.4.0")
} }

@ -0,0 +1,54 @@
package org.isoron.uhabits
import io.qameta.allure.Feature
import io.qameta.allure.junit4.DisplayName
import org.isoron.uhabits.acceptance.steps.CommonSteps.launchApp
import org.isoron.uhabits.pages.MainPage
import org.isoron.uhabits.pages.NewHabitPage
import org.junit.Test
@Feature("Habits smoke test")
//@RunWith(AllureAndroidJUnit4::class)
//@RunWith(AndroidJUnit4::class)
class HabitsSmokeTest : BaseUserInterfaceTest() {
@Test
@DisplayName("Create new habit")
fun createNewHabit() {
launchApp()
MainPage.openNewHabitPage()
NewHabitPage.fillDefaultHabit("Test Name", "Test Question")
MainPage.checkHabitPresentOnScroller("Test Name")
}
@Test
@DisplayName("Delete habit")
fun deleteHabit() {
launchApp()
with(MainPage) {
checkHabitPresentOnScroller("Wake up early")
deleteHabit("Wake up early")
checkHabitIsNotPresentOnScroller("Wake up early")
}
}
@Test
@DisplayName("Hide completed habits")
fun hideCompleted() {
launchApp()
with(MainPage) {
hideCompleted()
checkHabitIsNotPresentOnScroller("Track time")
}
}
@Test
@DisplayName("Hide archived habits")
fun hideArchived() {
launchApp()
with(MainPage) {
makeArchived("Track time")
checkHabitIsNotPresentOnScroller("Track time")
}
}
}

@ -0,0 +1,122 @@
package org.isoron.uhabits.pages
import androidx.test.espresso.Espresso.onView
import androidx.test.espresso.ViewInteraction
import androidx.test.espresso.action.ViewActions
import androidx.test.espresso.assertion.ViewAssertions.doesNotExist
import androidx.test.espresso.assertion.ViewAssertions.matches
import androidx.test.espresso.matcher.ViewMatchers
import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
import androidx.test.espresso.matcher.ViewMatchers.withId
import androidx.test.espresso.matcher.ViewMatchers.withText
import io.qameta.allure.Step
import org.hamcrest.CoreMatchers
import org.hamcrest.Matchers.allOf
import org.hamcrest.Matchers.endsWith
import org.isoron.uhabits.BaseUserInterfaceTest.Companion.device
import org.isoron.uhabits.R
import org.isoron.uhabits.acceptance.steps.CommonSteps.scrollToText
object MainPage {
private val addHabitButton by lazy { onView(withId(R.id.actionCreateHabit)) }
private val sortMenuButton by lazy { onView(withId(R.id.action_filter)) }
private val moreMenuButton by lazy {
onView(
CoreMatchers.allOf(
ViewMatchers.withContentDescription("More options"),
ViewMatchers.withParent(
ViewMatchers.withParent(
ViewMatchers.withClassName(
endsWith("Toolbar")
)
)
)
)
)
}
// More menu window
private val deleteMenuButton by lazy { onView(withText(R.string.delete)) }
private val yesDeleteMenuButton by lazy { onView(withText("Yes")) }
// Sort window
private val hideCompleted by lazy { onView(withText(R.string.hide_completed)) }
private val hideArchived by lazy { onView(withText(R.string.hide_archived)) }
private val archive by lazy { onView(withText(R.string.archive)) }
private val sort by lazy { onView(withText("Sort")) }
private val habitsScrollerRow =
Getter<String, ViewInteraction> { habitName ->
onView(
allOf(
ViewMatchers.hasDescendant(withText(habitName)),
ViewMatchers.withClassName(endsWith("HabitCardView"))
)
)
}
fun interface Getter<V, R> {
operator fun get(value: V): R
}
private val habitTypeWindow by lazy { onView(withText(R.string.yes_or_no_example)) }
private val habitTypeYesOnNo by lazy { onView(withText("Yes or No")) }
@Step("Open New Habit Page")
fun openNewHabitPage() {
addHabitButton.perform(ViewActions.click())
habitTypeWindow.check(matches(isDisplayed()))
habitTypeYesOnNo.perform(ViewActions.click())
}
@Step("Hide completed")
fun hideCompleted() {
sortMenuButton.perform(ViewActions.click())
hideCompleted.perform(ViewActions.click())
}
@Step("Hide archived")
fun hideArchived() {
sortMenuButton.perform(ViewActions.click())
hideArchived.perform(ViewActions.click())
}
@Step("Change sort on {sortText}")
fun changeSort(sortText: String) {
sortMenuButton.perform(ViewActions.click())
sort.perform(ViewActions.click())
onView(withText(sortText)).perform(ViewActions.click())
}
@Step("Delete habit with name {habitName}")
fun deleteHabit(habitName: String) {
scrollToText(habitName)
onView(withText(habitName)).perform(ViewActions.longClick())
device.waitForIdle()
moreMenuButton.perform(ViewActions.click())
deleteMenuButton.perform(ViewActions.click())
yesDeleteMenuButton.perform(ViewActions.click())
}
@Step("Check habit with name {habitName} present on scroller")
fun checkHabitPresentOnScroller(habitName: String) {
scrollToText(habitName)
habitsScrollerRow[habitName].check(matches(isDisplayed()))
}
@Step("Check habit with name {habitName} is not present on scroller")
fun checkHabitIsNotPresentOnScroller(habitName: String) {
onView(withText(habitName)).check(doesNotExist())
}
@Step("Check habit with name {habitName} is not present on scroller")
fun makeArchived(habitName: String) {
scrollToText(habitName)
onView(withText(habitName)).perform(ViewActions.longClick())
device.waitForIdle()
moreMenuButton.perform(ViewActions.click())
archive.perform(ViewActions.click())
}
}

@ -0,0 +1,41 @@
package org.isoron.uhabits.pages
import androidx.test.espresso.Espresso.onView
import androidx.test.espresso.action.ViewActions
import androidx.test.espresso.matcher.ViewMatchers.withId
import org.isoron.uhabits.R
object NewHabitPage {
private val nameInput by lazy { onView(withId(R.id.nameInput)) }
private val questionInput by lazy { onView(withId(R.id.questionInput)) }
private val frequencyPicker by lazy { onView(withId(R.id.boolean_frequency_picker)) }
private val notesInput by lazy { onView(withId(R.id.notesInput)) }
private val colorButton by lazy { onView(withId(R.id.colorButton)) }
private val save by lazy { onView(withId(R.id.buttonSave)) }
fun fillDefaultHabit(name: String, question: String, note: String? = null) {
nameInput.perform(
ViewActions.clearText(),
ViewActions.typeText(name),
ViewActions.closeSoftKeyboard()
)
questionInput.perform(
ViewActions.clearText(),
ViewActions.typeText(question),
ViewActions.closeSoftKeyboard()
)
note?.let {
notesInput.perform(
ViewActions.clearText(),
ViewActions.typeText(it),
ViewActions.closeSoftKeyboard()
)
}
save.perform(ViewActions.click())
}
}
Loading…
Cancel
Save