diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 5d33560..cdb8c03 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -17,7 +17,16 @@ android { versionCode = 1 versionName = "1.0" - testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" + testInstrumentationRunner = "com.bober.notesapp.HiltTestRunner" + } + + packaging { + resources { + excludes += "META-INF/LICENSE.md" + excludes += "META-INF/LICENSE-notice.md" + excludes += "META-INF/LICENSE*" + excludes += "META-INF/NOTICE*" + } } buildTypes { diff --git a/app/src/androidTest/java/com/bober/notesapp/ExampleInstrumentedTest.kt b/app/src/androidTest/java/com/bober/notesapp/ExampleInstrumentedTest.kt deleted file mode 100644 index acb0d34..0000000 --- a/app/src/androidTest/java/com/bober/notesapp/ExampleInstrumentedTest.kt +++ /dev/null @@ -1,24 +0,0 @@ -package com.bober.notesapp - -import androidx.test.platform.app.InstrumentationRegistry -import androidx.test.ext.junit.runners.AndroidJUnit4 - -import org.junit.Test -import org.junit.runner.RunWith - -import org.junit.Assert.* - -/** - * Instrumented test, which will execute on an Android device. - * - * See [testing documentation](http://d.android.com/tools/testing). - */ -@RunWith(AndroidJUnit4::class) -class ExampleInstrumentedTest { - @Test - fun useAppContext() { - // Context of the app under test. - val appContext = InstrumentationRegistry.getInstrumentation().targetContext - assertEquals("com.bober.notesapp", appContext.packageName) - } -} \ No newline at end of file diff --git a/app/src/androidTest/java/com/bober/notesapp/HiltTestRunner.kt b/app/src/androidTest/java/com/bober/notesapp/HiltTestRunner.kt new file mode 100644 index 0000000..dfb25ef --- /dev/null +++ b/app/src/androidTest/java/com/bober/notesapp/HiltTestRunner.kt @@ -0,0 +1,18 @@ +package com.bober.notesapp + +import android.app.Application +import android.content.Context +import androidx.test.runner.AndroidJUnitRunner +import dagger.hilt.android.testing.HiltTestApplication + + +class HiltTestRunner: AndroidJUnitRunner() { + + override fun newApplication( + cl: ClassLoader?, + className: String?, + context: Context? + ): Application? { + return super.newApplication(cl, HiltTestApplication::class.java.name, context) + } +} \ No newline at end of file diff --git a/app/src/androidTest/java/com/bober/notesapp/di/AppModuleTest.kt b/app/src/androidTest/java/com/bober/notesapp/di/AppModuleTest.kt new file mode 100644 index 0000000..ba21349 --- /dev/null +++ b/app/src/androidTest/java/com/bober/notesapp/di/AppModuleTest.kt @@ -0,0 +1,34 @@ +package com.bober.notesapp.di + +import android.app.Application +import androidx.room.Room +import com.bober.notesapp.data.local.NoteDatabase +import com.bober.notesapp.data.repository.FakeNoteRepository +import com.bober.notesapp.data.repository.NoteRepositoryImpl +import com.bober.notesapp.domain.repository.NoteRepository +import dagger.Module +import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent +import javax.inject.Singleton + +@Module +@InstallIn(SingletonComponent::class) +object AppModuleTest { + + @Provides + @Singleton + fun provideNoteDatabase(app: Application): NoteDatabase { + return Room.inMemoryDatabaseBuilder( + app, + NoteDatabase::class.java + ).build() + } + + @Provides + @Singleton + fun provideNoteRepository(db : NoteDatabase): NoteRepository{ + return NoteRepositoryImpl(db.noteDao()) + // return FakeNoteRepository() + } +} \ No newline at end of file diff --git a/app/src/androidTest/java/com/bober/notesapp/presentation/notes/NoteScreenTest.kt b/app/src/androidTest/java/com/bober/notesapp/presentation/notes/NoteScreenTest.kt new file mode 100644 index 0000000..8e99f7b --- /dev/null +++ b/app/src/androidTest/java/com/bober/notesapp/presentation/notes/NoteScreenTest.kt @@ -0,0 +1,69 @@ +package com.bober.notesapp.presentation.notes + +import androidx.compose.ui.test.assertIsDisplayed +import androidx.compose.ui.test.assertIsNotSelected +import androidx.compose.ui.test.assertIsSelected +import androidx.compose.ui.test.filterToOne +import androidx.compose.ui.test.hasClickAction +import androidx.compose.ui.test.junit4.createAndroidComposeRule +import androidx.compose.ui.test.onChildren +import androidx.compose.ui.test.onNodeWithContentDescription +import androidx.compose.ui.test.onNodeWithTag +import androidx.compose.ui.test.onNodeWithText +import androidx.compose.ui.test.performClick +import com.bober.notesapp.core.util.TestTags +import com.bober.notesapp.di.AppModule +import com.bober.notesapp.presentation.MainActivity +import dagger.hilt.android.testing.HiltAndroidRule +import dagger.hilt.android.testing.HiltAndroidTest +import dagger.hilt.android.testing.UninstallModules +import org.junit.Before +import org.junit.Rule +import org.junit.Test + +@HiltAndroidTest +@UninstallModules(AppModule::class) +class NoteScreenTest { + + @get:Rule(order = 0) + val hiltRule = HiltAndroidRule(this) + + @get:Rule(order = 1) + val composeRule = createAndroidComposeRule() + + @Before + fun setUp(){ + + hiltRule.inject() + } + + @Test + fun clickToggleOrderSection_isVisible() { + composeRule.onNodeWithTag(TestTags.ORDER_SECTION).assertDoesNotExist() + composeRule.onNodeWithContentDescription("Sort").performClick() + composeRule.onNodeWithTag(TestTags.ORDER_SECTION).assertIsDisplayed() + } + + @Test + fun clickToggleOrderSectionAndChangeToTitle_titleIsClicked() { + composeRule.onNodeWithTag(TestTags.ORDER_SECTION).assertDoesNotExist() + composeRule.onNodeWithContentDescription("Sort").performClick() + + val titleRadioButton = composeRule + .onNodeWithTag(TestTags.TITLE_RADIO_BUTTON, useUnmergedTree = true) + .onChildren() + .filterToOne(hasClickAction()) + + titleRadioButton.assertIsNotSelected() + composeRule.onNodeWithText("Title").assertIsDisplayed() + titleRadioButton.performClick() + titleRadioButton.assertIsSelected() + } + + @Test + fun clickAddNote_navigatesToAddEditNoteScreen() { + composeRule.onNodeWithText("Your note").assertIsDisplayed() + composeRule.onNodeWithContentDescription("Add Note").performClick() + composeRule.onNodeWithText("Enter title...").assertIsDisplayed() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/bober/notesapp/core/util/TestTags.kt b/app/src/main/java/com/bober/notesapp/core/util/TestTags.kt new file mode 100644 index 0000000..ddadd61 --- /dev/null +++ b/app/src/main/java/com/bober/notesapp/core/util/TestTags.kt @@ -0,0 +1,7 @@ +package com.bober.notesapp.core.util + +object TestTags { + + const val ORDER_SECTION = "ORDER_SECTION" + const val TITLE_RADIO_BUTTON = "TITLE_RADIO_BUTTON" +} \ No newline at end of file diff --git a/app/src/main/java/com/bober/notesapp/presentation/notes/NoteScreen.kt b/app/src/main/java/com/bober/notesapp/presentation/notes/NoteScreen.kt index 58cd452..f99d779 100644 --- a/app/src/main/java/com/bober/notesapp/presentation/notes/NoteScreen.kt +++ b/app/src/main/java/com/bober/notesapp/presentation/notes/NoteScreen.kt @@ -35,10 +35,12 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.testTag import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel import androidx.navigation.NavController +import com.bober.notesapp.core.util.TestTags import com.bober.notesapp.domain.model.Note import com.bober.notesapp.presentation.notes.components.NoteItem import com.bober.notesapp.presentation.notes.components.OrderSection @@ -115,7 +117,8 @@ fun NoteScreenContent( OrderSection( modifier = Modifier .fillMaxWidth() - .padding(vertical = 16.dp), + .padding(vertical = 16.dp) + .testTag(TestTags.ORDER_SECTION), noteOrder = state.noteOrder, onOrderChange = { onEvent(NotesEvent.Order(it)) diff --git a/app/src/main/java/com/bober/notesapp/presentation/notes/components/NoteItem.kt b/app/src/main/java/com/bober/notesapp/presentation/notes/components/NoteItem.kt index 6c0ccd8..75ab6d3 100644 --- a/app/src/main/java/com/bober/notesapp/presentation/notes/components/NoteItem.kt +++ b/app/src/main/java/com/bober/notesapp/presentation/notes/components/NoteItem.kt @@ -99,7 +99,7 @@ fun NoteItem( ) { Icon( imageVector = Icons.Default.Delete, - contentDescription = "Delete note", + contentDescription = "Delete note ${note.title}", tint = Color.Black ) } diff --git a/app/src/main/java/com/bober/notesapp/presentation/notes/components/OrderSection.kt b/app/src/main/java/com/bober/notesapp/presentation/notes/components/OrderSection.kt index c9e1edf..51ff264 100644 --- a/app/src/main/java/com/bober/notesapp/presentation/notes/components/OrderSection.kt +++ b/app/src/main/java/com/bober/notesapp/presentation/notes/components/OrderSection.kt @@ -8,7 +8,9 @@ import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.width import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.testTag import androidx.compose.ui.unit.dp +import com.bober.notesapp.core.util.TestTags import com.bober.notesapp.domain.util.NoteOrder import com.bober.notesapp.domain.util.OrderType @@ -29,7 +31,8 @@ fun OrderSection( selected = noteOrder is NoteOrder.Title, onSelect = { onOrderChange(NoteOrder.Title(noteOrder.orderType)) - } + }, + Modifier.testTag(TestTags.TITLE_RADIO_BUTTON) ) Spacer(modifier = Modifier.width(8.dp)) DefaultRadioButton(