Skip to content
Draft
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
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@

import static androidx.test.espresso.Espresso.onView;
import static androidx.test.espresso.Espresso.openActionBarOverflowOrOptionsMenu;
import static androidx.test.espresso.Espresso.pressBack;
import static androidx.test.espresso.action.ViewActions.click;
import static androidx.test.espresso.assertion.ViewAssertions.doesNotExist;
import static androidx.test.espresso.assertion.ViewAssertions.matches;
Expand All @@ -64,10 +65,9 @@
public class DeleteProjectTest {

@Rule
public BaseActivityTestRule<ProjectListActivity> baseActivityTestRule = new
BaseActivityTestRule<>(ProjectListActivity.class, true, false);
public BaseActivityTestRule<ProjectListActivity> baseActivityTestRule = new BaseActivityTestRule<>(ProjectListActivity.class, true, false);

private String projectToDelete = "firstProject";
private final String projectToDelete = "firstProject";

@Before
public void setUp() throws Exception {
Expand All @@ -91,17 +91,11 @@ public void deleteProjectMultipleElementsListTest() {
.inRoot(isDialog())
.check(matches(isDisplayed()));

onView(withText(R.string.dialog_confirm_delete)).inRoot(isDialog())
.check(matches(isDisplayed()));

onView(allOf(withId(android.R.id.button2), withText(R.string.cancel)))
.check(matches(isDisplayed()));
onView(allOf(withId(android.R.id.button2), withText(R.string.cancel))).check(matches(isDisplayed()));

onView(allOf(withId(android.R.id.button1), withText(R.string.delete)))
.perform(click());
onView(allOf(withId(android.R.id.button1), withText(R.string.delete))).perform(click());

onView(withText(projectToDelete))
.check(doesNotExist());
onView(withText(projectToDelete)).check(doesNotExist());
}

@Category({Cat.AppUi.class, Level.Smoke.class})
Expand All @@ -112,9 +106,7 @@ public void deleteProjectSingleElementListTest() {
openActionBarOverflowOrOptionsMenu(InstrumentationRegistry.getInstrumentation().getTargetContext());
onView(withText(R.string.delete)).perform(click());

onView(withText(projectToDelete))
.check(doesNotExist());

onView(withText(projectToDelete)).check(doesNotExist());
}

@Category({Cat.AppUi.class, Level.Smoke.class})
Expand All @@ -139,26 +131,72 @@ public void cancelDeleteProjectTest() {
openActionBarOverflowOrOptionsMenu(InstrumentationRegistry.getInstrumentation().getTargetContext());
onView(withText(R.string.delete)).perform(click());

onRecyclerView().atPosition(1)
.performCheckItemClick();
onRecyclerView().atPosition(1).performCheckItemClick();

onView(withId(R.id.confirm)).perform(click());

onView(withText(UiTestUtils.getResources().getQuantityString(R.plurals.delete_projects, 1)))
.inRoot(isDialog())
.check(matches(isDisplayed()));

onView(withText(R.string.dialog_confirm_delete)).inRoot(isDialog())
.check(matches(isDisplayed()));
onView(allOf(withId(android.R.id.button1), withText(R.string.delete))).check(matches(isDisplayed()));

onView(allOf(withId(android.R.id.button1), withText(R.string.delete)))
.check(matches(isDisplayed()));
onView(allOf(withId(android.R.id.button2), withText(R.string.cancel))).perform(click());

onView(allOf(withId(android.R.id.button2), withText(R.string.cancel)))
.perform(click());
onView(withText(projectToDelete)).check(matches(isDisplayed()));
}

onView(withText(projectToDelete))
.check(matches(isDisplayed()));
@Category({Cat.AppUi.class, Level.Smoke.class})
@Test
public void deleteProjectShowUndoButtonTest() {
baseActivityTestRule.launchActivity(null);

openActionBarOverflowOrOptionsMenu(InstrumentationRegistry.getInstrumentation().getTargetContext());
onView(withText(R.string.delete)).perform(click());

onView(withText(projectToDelete)).check(doesNotExist());
onView(withId(R.id.menu_undo)).check(matches(isDisplayed()));
}

@Category({Cat.AppUi.class, Level.Smoke.class})
@Test
public void undoDeletedProjectTest() throws InterruptedException {
baseActivityTestRule.launchActivity(null);

openActionBarOverflowOrOptionsMenu(InstrumentationRegistry.getInstrumentation().getTargetContext());
onView(withText(R.string.delete)).perform(click());

onView(withText(projectToDelete)).check(doesNotExist());
onView(withId(R.id.menu_undo)).check(matches(isDisplayed()));

onView(withId(R.id.menu_undo)).perform(click());
Thread.sleep(1000);

onView(withText(projectToDelete)).check(matches(isDisplayed()));
onView(withId(R.id.menu_undo)).check(doesNotExist());
}

@Category({Cat.AppUi.class, Level.Smoke.class})
@Test
public void undoOptionNotVisibleAfterActivityChangeTest() throws InterruptedException {
String secondProject = "secondProject";
createProject(secondProject);
baseActivityTestRule.launchActivity(null);

openActionBarOverflowOrOptionsMenu(InstrumentationRegistry.getInstrumentation().getTargetContext());
onView(withText(R.string.delete)).perform(click());

onRecyclerView().atPosition(1).performCheckItemClick();

onView(withId(R.id.confirm)).perform(click());
onView(allOf(withId(android.R.id.button1), withText(R.string.delete))).perform(click());

onView(withText(secondProject)).perform(click());
Thread.sleep(1000);
pressBack();

onView(withId(R.id.menu_undo)).check(doesNotExist());
onView(withText(projectToDelete)).check(doesNotExist());
}

private void createProject(String projectName) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*
* Catroid: An on-device visual programming system for Android devices
* Copyright (C) 2010-2025 The Catrobat Team
* Copyright (C) 2010-2026 The Catrobat Team
* (<http://developer.catrobat.org/credits>)
*
* This program is free software: you can redistribute it and/or modify
Expand Down Expand Up @@ -34,9 +34,14 @@ import org.catrobat.catroid.ui.recyclerview.fragment.ProjectListFragment

class ProjectListActivity : BaseCastActivity() {
private lateinit var binding: ActivityRecyclerBinding
private var isUndoMenuItemVisible = false

public override fun onCreate(savedInstanceState: Bundle?) {
if (savedInstanceState != null) {
savedInstanceStateExpected = true
}
super.onCreate(savedInstanceState)
isUndoMenuItemVisible = savedInstanceState?.getBoolean(BUNDLE_IS_UNDO_MENU_ITEM_VISIBLE, false) ?: false
binding = ActivityRecyclerBinding.inflate(layoutInflater)
setContentView(binding.root)
setSupportActionBar(binding.toolbar.toolbar)
Expand Down Expand Up @@ -73,17 +78,34 @@ class ProjectListActivity : BaseCastActivity() {
override fun onCreateOptionsMenu(menu: Menu): Boolean {
menuInflater.inflate(R.menu.menu_projects_activity, menu)
menu.findItem(R.id.merge).isVisible = BuildConfig.FEATURE_MERGE_ENABLED
menu.findItem(R.id.menu_undo).isVisible = isUndoMenuItemVisible
return super.onCreateOptionsMenu(menu)
}

fun showUndo(visible: Boolean) {
isUndoMenuItemVisible = visible
try {
optionsMenu.findItem(R.id.menu_undo).isVisible = visible
} catch (_: UninitializedPropertyAccessException) {
invalidateOptionsMenu()
}
}

override fun onBackPressed() {
if (supportFragmentManager.backStackEntryCount > 0) {
supportFragmentManager.popBackStack()
} else {
(supportFragmentManager.findFragmentByTag(ProjectListFragment.TAG) as? ProjectListFragment)
?.clearDeletedProjectUndo()
super.onBackPressed()
}
}

override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
outState.putBoolean(BUNDLE_IS_UNDO_MENU_ITEM_VISIBLE, isUndoMenuItemVisible)
}

@Suppress("UNUSED_PARAMETER")
fun handleAddButton(view: View?) {
val dialog = NewProjectDialogFragment()
Expand All @@ -93,5 +115,6 @@ class ProjectListActivity : BaseCastActivity() {
companion object {
const val IMPORT_LOCAL_INTENT: String = "merge"
val TAG: String = ProjectListActivity::class.java.simpleName
private const val BUNDLE_IS_UNDO_MENU_ITEM_VISIBLE = "isUndoMenuItemVisible"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,6 @@ import org.catrobat.catroid.databinding.FragmentProjectOptionsBinding
import org.catrobat.catroid.io.StorageOperations
import org.catrobat.catroid.io.XstreamSerializer
import org.catrobat.catroid.io.asynctask.ProjectExporter
import org.catrobat.catroid.io.asynctask.ProjectSaver
import org.catrobat.catroid.io.asynctask.loadProject
import org.catrobat.catroid.io.asynctask.renameProject
import org.catrobat.catroid.io.asynctask.saveProjectSerial
Expand Down Expand Up @@ -109,7 +108,7 @@ class ProjectOptionsFragment : Fragment() {
addTextChangedListener(object : NewProjectNameTextWatcher<Nameable>() {
override fun afterTextChanged(s: Editable?) {
val error = if (s.toString() != project!!.name) {
validateInput(s.toString(), getContext())
validateInput(s.toString(), context)
} else {
null
}
Expand Down Expand Up @@ -190,7 +189,6 @@ class ProjectOptionsFragment : Fragment() {
)
AlertDialog.Builder(requireContext())
.setTitle(resources.getQuantityString(R.plurals.delete_projects, 1))
.setMessage(R.string.dialog_confirm_delete)
.setPositiveButton(R.string.yes) { _: DialogInterface?, _: Int ->
deleteProject(
projectData
Expand Down
Loading
Loading