Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
106 changes: 106 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
# Android Studio
*.iml
.gradle
/local.properties
<<<<<<< HEAD
/.idea/
=======
/.idea/caches
/.idea/libraries
/.idea/modules.xml
/.idea/workspace.xml
/.idea/navEditor.xml
/.idea/assetWizardSettings.xml
>>>>>>> pr-7-Santix1234-kotlinTodoApp
.DS_Store
/build
/captures
.externalNativeBuild
.cxx
<<<<<<< HEAD

# Gradle files
*.gradle
/gradle/
gradlew
gradlew.bat

# Compiled class files
*.class

# Log files
*.log

# Package Files
*.jar
*.war
*.nar
*.ear
*.zip
*.tar.gz
*.rar

# Virtual Machine crash logs
hs_err_pid*

# Android specific
bin/
gen/
out/
release/

# Dependencies
/node_modules
/app/build/

# Secrets and local configs
*.env
local.properties
=======
local.properties

# Kotlin/Android
bin/
gen/
out/
*.class
*.log
*.dex
*.apk

# Dependency directories
node_modules/
jspm_packages/
app/libs/

# Gradle
.gradle/
build/

# Testing
app/build/
app/test/
app/androidTest/

# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*

# Virtual environment
venv/
.env

# IDE specific files
.vscode/
.idea/
*.ipr
*.iws

# Misc
*.swp
*.swo
.DS_Store
>>>>>>> pr-7-Santix1234-kotlinTodoApp
45 changes: 44 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,44 @@
Hello
# Kotlin Todo App

## Project Overview
A simple Todo application built with Kotlin, demonstrating Android development best practices.

## Testing Strategy
The project includes multiple layers of testing:

### Unit Tests
- Located in `app/src/test`
- Test ViewModel logic and data transformations
- Use Mockito for mocking dependencies
- Utilize Kotlin Coroutines test utilities

### Instrumentation Tests
- Located in `app/src/androidTest`
- Test UI components and interactions
- Use Espresso for UI testing
- Verify activity behaviors and user flows

### Test Coverage
- ViewModel unit tests
- Activity UI tests
- Input validation tests
- Basic functionality verification

## Running Tests
### Unit Tests
```bash
./gradlew test
```

### Instrumentation Tests
```bash
./gradlew connectedAndroidTest
```

## Dependencies
- Android Jetpack
- Kotlin Coroutines
- Room Database
- JUnit
- Mockito
- Espresso
62 changes: 62 additions & 0 deletions app/src/androidTest/java/com/todoapp/ui/EditTodoActivityTest.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package com.todoapp.ui

import androidx.test.espresso.Espresso.onView
import androidx.test.espresso.action.ViewActions.*
import androidx.test.espresso.assertion.ViewAssertions.matches
import androidx.test.espresso.matcher.ViewMatchers.*
import androidx.test.ext.junit.rules.ActivityScenarioRule
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.todoapp.R
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith

@RunWith(AndroidJUnit4::class)
class EditTodoActivityTest {

@get:Rule
val activityRule = ActivityScenarioRule(EditTodoActivity::class.java)

@Test
fun testTodoCreation() {
// Enter todo title
onView(withId(R.id.edit_todo_title))
.perform(typeText("Test Todo"), closeSoftKeyboard())

// Enter todo description
onView(withId(R.id.edit_todo_description))
.perform(typeText("This is a test description"), closeSoftKeyboard())

// Click save button
onView(withId(R.id.save_todo_button))
.perform(click())

// Verify behavior (could be checking for toast or navigation)
}

@Test
fun testEmptyTitleValidation() {
// Leave title empty
onView(withId(R.id.edit_todo_title))
.perform(clearText(), closeSoftKeyboard())

// Click save button
onView(withId(R.id.save_todo_button))
.perform(click())

// Verify error state (depends on how you implement validation)
onView(withId(R.id.edit_todo_title))
.check(matches(hasErrorText("Title cannot be empty")))
}

@Test
fun testCompletedSwitch() {
// Toggle completed switch
onView(withId(R.id.edit_todo_completed_switch))
.perform(click())

// Verify switch state changed
onView(withId(R.id.edit_todo_completed_switch))
.check(matches(isChecked()))
}
}
111 changes: 111 additions & 0 deletions app/src/main/java/com/todoapp/ui/EditTodoActivity.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
package com.todoapp.ui

import android.os.Bundle
import android.widget.Button
import android.widget.EditText
import android.widget.Switch
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope
import com.todoapp.R
import com.todoapp.data.Todo
import com.todoapp.viewmodel.TodoViewModel
import kotlinx.coroutines.launch

class EditTodoActivity : AppCompatActivity() {

private lateinit var todoViewModel: TodoViewModel
private lateinit var titleEditText: EditText
private lateinit var descriptionEditText: EditText
private lateinit var completedSwitch: Switch
private lateinit var saveButton: Button
private lateinit var cancelButton: Button

private var todoId: Long = -1

companion object {
const val EXTRA_TODO_ID = "extra_todo_id"
}

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_edit_todo)

// Initialize ViewModel
todoViewModel = ViewModelProvider(this)[TodoViewModel::class.java]

// Initialize views
titleEditText = findViewById(R.id.edit_todo_title)
descriptionEditText = findViewById(R.id.edit_todo_description)
completedSwitch = findViewById(R.id.edit_todo_completed_switch)
saveButton = findViewById(R.id.save_todo_button)
cancelButton = findViewById(R.id.cancel_todo_button)

// Get todo item ID from intent
todoId = intent.getLongExtra(EXTRA_TODO_ID, -1)

// Load existing todo item if editing
if (todoId != -1L) {
loadTodoItem()
}

// Save button click listener
saveButton.setOnClickListener {
saveTodoItem()
}

// Cancel button click listener
cancelButton.setOnClickListener {
finish()
}
}

private fun loadTodoItem() {
lifecycleScope.launch {
val todo = todoViewModel.getTodoById(todoId)
todo?.let {
titleEditText.setText(it.title)
descriptionEditText.setText(it.description)
completedSwitch.isChecked = it.isCompleted
}
}
}

private fun saveTodoItem() {
val title = titleEditText.text.toString().trim()
val description = descriptionEditText.text.toString().trim()
val isCompleted = completedSwitch.isChecked

// Validate input
if (title.isEmpty()) {
titleEditText.error = "Title cannot be empty"
return
}

lifecycleScope.launch {
try {
// Create or update todo item
val todo = Todo(
id = if (todoId == -1L) 0 else todoId,
title = title,
description = description,
isCompleted = isCompleted
)

if (todoId == -1L) {
todoViewModel.insertTodo(todo)
Toast.makeText(this@EditTodoActivity, "Todo created", Toast.LENGTH_SHORT).show()
} else {
todoViewModel.updateTodo(todo)
Toast.makeText(this@EditTodoActivity, "Todo updated", Toast.LENGTH_SHORT).show()
}

// Close the activity
finish()
} catch (e: Exception) {
Toast.makeText(this@EditTodoActivity, "Error saving todo", Toast.LENGTH_SHORT).show()
}
}
}
}
Loading