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
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 All @@ -23,11 +23,19 @@

package org.catrobat.catroid.uiespresso.ui.dialog

import android.view.View
import androidx.test.core.app.ApplicationProvider
import androidx.test.espresso.Espresso
import androidx.test.espresso.action.ViewActions
import androidx.test.espresso.assertion.ViewAssertions
import androidx.test.espresso.matcher.ViewMatchers
import androidx.test.espresso.Espresso.onView
import androidx.test.espresso.IdlingRegistry
import androidx.test.espresso.action.ViewActions.click
import androidx.test.espresso.action.ViewActions.pressBack
import androidx.test.espresso.action.ViewActions.swipeLeft
import androidx.test.espresso.assertion.ViewAssertions.matches
import androidx.test.espresso.assertion.ViewAssertions.doesNotExist
import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
import androidx.test.espresso.matcher.ViewMatchers.isRoot
import androidx.test.espresso.matcher.ViewMatchers.withText
import androidx.test.espresso.matcher.ViewMatchers.withId
import androidx.test.platform.app.InstrumentationRegistry
import org.catrobat.catroid.ProjectManager
import org.catrobat.catroid.R
Expand All @@ -43,6 +51,7 @@ import org.catrobat.catroid.io.asynctask.saveProjectSerial
import org.catrobat.catroid.test.utils.TestUtils
import org.catrobat.catroid.ui.ProjectListActivity
import org.catrobat.catroid.uiespresso.util.rules.BaseActivityTestRule
import org.catrobat.catroid.uiespresso.util.idlingresources.ViewVisibilityIdlingResource
import org.junit.After
import org.junit.Assert
import org.junit.Before
Expand Down Expand Up @@ -73,16 +82,21 @@ class ImportLocalSpriteTest {
ProjectListActivity::class.java, true, false
)

private val progressIdlingResource = ViewVisibilityIdlingResource(R.id.progress_bar, View.GONE)

@Before
@Throws(Exception::class)
fun setUp() {
IdlingRegistry.getInstance().register(progressIdlingResource)
createTestProjects()
activityTestRule.launchActivity(null)
InstrumentationRegistry.getInstrumentation().waitForIdleSync()
}

@After
@Throws(Exception::class)
fun tearDown() {
IdlingRegistry.getInstance().unregister(progressIdlingResource)
TestUtils.deleteProjects(projectToImportFrom.name)
TestUtils.deleteProjects(projectWithSameGlobals.name)
TestUtils.deleteProjects(projectWithConflicts.name)
Expand All @@ -91,44 +105,36 @@ class ImportLocalSpriteTest {

@Test
fun importObjectAndMergeGlobals() {
Espresso.onView(ViewMatchers.withText(projectWithSameGlobals.name))
.check(ViewAssertions.matches(ViewMatchers.isDisplayed()))
Espresso.onView(ViewMatchers.withText(projectToImportTo.name))
.check(ViewAssertions.matches(ViewMatchers.isDisplayed()))
onView(withText(projectWithSameGlobals.name)).check(matches(isDisplayed()))
onView(withText(projectToImportTo.name)).check(matches(isDisplayed()))

val originalUserListsSize = projectToImportTo.userLists.size
val originalUserVariableSize = projectToImportTo.userVariables.size
val originalBroadcastMessagesSize = projectToImportTo.broadcastMessageContainer
.broadcastMessages.size
val originalBroadcastMessagesSize =
projectToImportTo.broadcastMessageContainer.broadcastMessages.size

Assert.assertTrue(projectToImportTo.userVariables.contains(global1))
Assert.assertTrue(projectToImportTo.defaultScene.spriteList[1].userVariables.contains(local1))
Assert.assertTrue(projectToImportTo.userLists.contains(userListGlobal1))
Assert.assertEquals(projectToImportTo.defaultScene.spriteList.size, 2)
Assert.assertEquals(projectToImportTo.defaultScene.spriteList[1], dog)

Espresso.onView(ViewMatchers.withText(projectToImportTo.name))
.perform(ViewActions.click())
onView(withText(projectToImportTo.name)).perform(click())
InstrumentationRegistry.getInstrumentation().waitForIdleSync()
Espresso.onView(ViewMatchers.withId(R.id.button_add))
.perform(ViewActions.click())
Thread.sleep(1000)
onView(withId(R.id.button_add)).check(matches(isDisplayed()))
onView(withId(R.id.button_add)).perform(click())
InstrumentationRegistry.getInstrumentation().waitForIdleSync()
Espresso.onView(ViewMatchers.withId(R.id.dialog_new_look_from_local))
.check(ViewAssertions.matches(ViewMatchers.isDisplayed()))
Espresso.onView(ViewMatchers.withId(R.id.dialog_new_look_from_local))
.perform(ViewActions.click())
onView(withId(R.id.dialog_new_look_from_local)).check(matches(isDisplayed()))
onView(withId(R.id.dialog_new_look_from_local)).perform(click())
InstrumentationRegistry.getInstrumentation().waitForIdleSync()
Espresso.onView(ViewMatchers.withText(projectWithSameGlobals.name))
.perform(ViewActions.click())
onView(withText(projectWithSameGlobals.name)).perform(click())
InstrumentationRegistry.getInstrumentation().waitForIdleSync()

Espresso.onView(ViewMatchers.withText(R.string.new_sprite_dialog_place_visually))
.check(ViewAssertions.matches(ViewMatchers.isDisplayed()))
Espresso.onView(ViewMatchers.withId(R.id.place_visually_sprite_switch))
.perform(ViewActions.swipeLeft())
onView(withText(R.string.new_sprite_dialog_place_visually)).check(matches(isDisplayed()))
onView(withId(R.id.place_visually_sprite_switch)).perform(swipeLeft())
InstrumentationRegistry.getInstrumentation().waitForIdleSync()
Espresso.onView(ViewMatchers.withText(R.string.ok))
.perform(ViewActions.click())
onView(withText(R.string.ok)).perform(click())
InstrumentationRegistry.getInstrumentation().waitForIdleSync()

assertAddedSprite(doggo)
Expand All @@ -139,96 +145,77 @@ class ImportLocalSpriteTest {
Assert.assertEquals(originalUserVariableSize, projectToImportTo.userVariables.size)
Assert.assertFalse(checkForDuplicates(projectToImportTo.userVariables))

Assert.assertEquals(originalBroadcastMessagesSize, projectToImportTo
.broadcastMessageContainer.broadcastMessages.size)
Assert.assertEquals(
originalBroadcastMessagesSize,
projectToImportTo.broadcastMessageContainer.broadcastMessages.size
)
Assert.assertFalse(checkForDuplicates(projectToImportTo.broadcastMessageContainer.broadcastMessages))
}

@Test
fun abortImportWithConflicts() {
Espresso.onView(ViewMatchers.withText(projectWithConflicts.name))
.check(ViewAssertions.matches(ViewMatchers.isDisplayed()))
Espresso.onView(ViewMatchers.withText(projectToImportTo.name))
.check(ViewAssertions.matches(ViewMatchers.isDisplayed()))
onView(withText(projectWithConflicts.name)).check(matches(isDisplayed()))
onView(withText(projectToImportTo.name)).check(matches(isDisplayed()))

val originalSpriteSize = projectToImportTo.defaultScene.spriteList.size
Espresso.onView(ViewMatchers.withText(projectToImportTo.name))
.perform(ViewActions.click())
onView(withText(projectToImportTo.name)).perform(click())
InstrumentationRegistry.getInstrumentation().waitForIdleSync()
Thread.sleep(1000)

Espresso.onView(ViewMatchers.withId(R.id.button_add))
.perform(ViewActions.click())
onView(withId(R.id.button_add)).check(matches(isDisplayed()))
onView(withId(R.id.button_add)).perform(click())
InstrumentationRegistry.getInstrumentation().waitForIdleSync()
Espresso.onView(ViewMatchers.withId(R.id.dialog_new_look_from_local))
.check(ViewAssertions.matches(ViewMatchers.isDisplayed()))
Espresso.onView(ViewMatchers.withId(R.id.dialog_new_look_from_local))
.perform(ViewActions.click())
onView(withId(R.id.dialog_new_look_from_local)).check(matches(isDisplayed()))
onView(withId(R.id.dialog_new_look_from_local)).perform(click())
InstrumentationRegistry.getInstrumentation().waitForIdleSync()
Espresso.onView(ViewMatchers.withText(projectWithConflicts.name))
.perform(ViewActions.click())
onView(withText(projectWithConflicts.name)).perform(click())
InstrumentationRegistry.getInstrumentation().waitForIdleSync()

Assert.assertEquals(projectToImportTo.defaultScene.spriteList.size, originalSpriteSize)
}

@Test
fun importActorOrObjectTest() {
Espresso.onView(ViewMatchers.withText(projectToImportFrom.name))
.check(ViewAssertions.matches(ViewMatchers.isDisplayed()))
Espresso.onView(ViewMatchers.withText(projectToImportTo.name))
.check(ViewAssertions.matches(ViewMatchers.isDisplayed()))

Espresso.onView(ViewMatchers.withText(projectToImportFrom.name))
.perform(ViewActions.click())
onView(withText(projectToImportFrom.name)).check(matches(isDisplayed()))
onView(withText(projectToImportTo.name)).check(matches(isDisplayed()))
InstrumentationRegistry.getInstrumentation().waitForIdleSync()
onView(withText(projectToImportFrom.name)).perform(click())
Thread.sleep(1000)

Assert.assertEquals(projectToImportFrom.defaultScene.spriteList.size, 2)
Assert.assertEquals(projectToImportFrom.defaultScene.spriteList[1], cat)

Espresso.onView(ViewMatchers.withText("cat"))
.check(ViewAssertions.matches(ViewMatchers.isDisplayed()))
Espresso.onView(ViewMatchers.withText("dog"))
.check(ViewAssertions.doesNotExist())
onView(withText("cat")).check(matches(isDisplayed()))
onView(withText("dog")).check(doesNotExist())

Espresso.onView(ViewMatchers.isRoot()).perform(ViewActions.pressBack())
onView(isRoot()).perform(pressBack())
InstrumentationRegistry.getInstrumentation().waitForIdleSync()

Espresso.onView(ViewMatchers.withText(projectToImportTo.name))
.check(ViewAssertions.matches(ViewMatchers.isDisplayed()))
Espresso.onView(ViewMatchers.withText(projectToImportTo.name))
.perform(ViewActions.click())
onView(withText(projectToImportTo.name)).check(matches(isDisplayed()))
onView(withText(projectToImportTo.name)).perform(click())
InstrumentationRegistry.getInstrumentation().waitForIdleSync()
Espresso.onView(ViewMatchers.withText("dog"))
.check(ViewAssertions.matches(ViewMatchers.isDisplayed()))
Espresso.onView(ViewMatchers.withText("cat"))
.check(ViewAssertions.doesNotExist())
Thread.sleep(1000)

onView(withText("dog")).check(matches(isDisplayed()))
onView(withText("cat")).check(doesNotExist())

Assert.assertEquals(projectToImportTo.defaultScene.spriteList.size, 2)
Assert.assertEquals(projectToImportTo.defaultScene.spriteList[1], dog)

Espresso.onView(ViewMatchers.withId(R.id.button_add))
.perform(ViewActions.click())
onView(withId(R.id.button_add)).perform(click())
InstrumentationRegistry.getInstrumentation().waitForIdleSync()
Espresso.onView(ViewMatchers.withId(R.id.dialog_new_look_from_local))
.check(ViewAssertions.matches(ViewMatchers.isDisplayed()))
Espresso.onView(ViewMatchers.withId(R.id.dialog_new_look_from_local))
.perform(ViewActions.click())
onView(withId(R.id.dialog_new_look_from_local)).check(matches(isDisplayed()))
onView(withId(R.id.dialog_new_look_from_local)).perform(click())
InstrumentationRegistry.getInstrumentation().waitForIdleSync()
Espresso.onView(ViewMatchers.withText(projectToImportFrom.name))
.perform(ViewActions.click())
onView(withText(projectToImportFrom.name)).perform(click())
InstrumentationRegistry.getInstrumentation().waitForIdleSync()
Espresso.onView(ViewMatchers.withText(R.string.new_sprite_dialog_place_visually))
.check(ViewAssertions.matches(ViewMatchers.isDisplayed()))
Espresso.onView(ViewMatchers.withId(R.id.place_visually_sprite_switch))
.perform(ViewActions.swipeLeft())
onView(withText(R.string.new_sprite_dialog_place_visually)).check(matches(isDisplayed()))
onView(withId(R.id.place_visually_sprite_switch)).perform(swipeLeft())
InstrumentationRegistry.getInstrumentation().waitForIdleSync()
Espresso.onView(ViewMatchers.withText(R.string.ok))
.perform(ViewActions.click())
onView(withText(R.string.ok)).perform(click())
InstrumentationRegistry.getInstrumentation().waitForIdleSync()
Espresso.onView(ViewMatchers.withText("cat"))
.check(ViewAssertions.matches(ViewMatchers.isDisplayed()))
Espresso.onView(ViewMatchers.withText("dog"))
.check(ViewAssertions.matches(ViewMatchers.isDisplayed()))
onView(withText("cat")).check(matches(isDisplayed()))
onView(withText("dog")).check(matches(isDisplayed()))

assertAddedSprite(cat)
}
Expand All @@ -240,31 +227,27 @@ class ImportLocalSpriteTest {
Assert.assertEquals(projectToImportTo.defaultScene.spriteList.size, 3)
Assert.assertEquals(projectToImportTo.defaultScene.spriteList[1].name, dog.name)
Assert.assertEquals(projectToImportTo.defaultScene.spriteList[2].name, addedSprite.name)
for ((scriptCounter, script) in projectToImportTo.defaultScene.spriteList[2].scriptList
.withIndex()) {
for ((scriptCounter, script) in projectToImportTo.defaultScene.spriteList[2].scriptList.withIndex()) {
Assert.assertTrue(script.javaClass == originalScriptListOfCat[scriptCounter].javaClass)
for ((brickCounter, brick) in script.brickList
.withIndex()) {
Assert.assertTrue(brick.javaClass == originalScriptListOfCat[scriptCounter]
.brickList[brickCounter].javaClass)
for ((brickCounter, brick) in script.brickList.withIndex()) {
Assert.assertTrue(
brick.javaClass == originalScriptListOfCat[scriptCounter].brickList[brickCounter].javaClass
)
}
}
for ((lookCounter, look) in projectToImportTo.defaultScene.spriteList[2].lookList
.withIndex()) {
for ((lookCounter, look) in projectToImportTo.defaultScene.spriteList[2].lookList.withIndex()) {
Assert.assertTrue(look.name == cat.lookList[lookCounter].name)
Assert.assertTrue(look.file == cat.lookList[lookCounter].file)
}
for ((variableCounter, variable) in projectToImportTo.defaultScene.spriteList[2]
.userVariables
.withIndex()) {
for ((variableCounter, variable) in projectToImportTo.defaultScene.spriteList[2].userVariables.withIndex()) {
Assert.assertTrue(variable.name == addedSprite.userVariables[variableCounter].name)
Assert.assertTrue(variable.value == addedSprite.userVariables[variableCounter].value)
}
for ((listCounter, list) in projectToImportTo.defaultScene.spriteList[2].userLists
.withIndex()) {
for ((listCounter, list) in projectToImportTo.defaultScene.spriteList[2].userLists.withIndex()) {
for ((listElementCounter, listElement) in list.value.withIndex()) {
Assert.assertTrue(
listElement.equals(addedSprite.userLists[listCounter].value[listElementCounter]))
listElement.equals(addedSprite.userLists[listCounter].value[listElementCounter])
)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/*
* Catroid: An on-device visual programming system for Android devices
* Copyright (C) 2010-2026 The Catrobat Team
* (<http://developer.catrobat.org/credits>)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* An additional term exception under section 7 of the GNU Affero
* General Public License, version 3, is available at
* http://developer.catrobat.org/license_additional_term
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

package org.catrobat.catroid.uiespresso.util.idlingresources

import android.view.View
import androidx.test.espresso.IdlingResource
import androidx.test.runner.lifecycle.ActivityLifecycleMonitorRegistry
import androidx.test.runner.lifecycle.Stage

class ViewVisibilityIdlingResource(
private val viewId: Int,
private val expectedVisibility: Int
) : IdlingResource {

private var resourceCallback: IdlingResource.ResourceCallback? = null

override fun getName(): String = "ViewVisibilityIdlingResource($viewId)"

override fun isIdleNow(): Boolean {
// This method is always called on the main thread by Espresso's internal polling.
// No need for runOnMainSync.
val resumedActivities = ActivityLifecycleMonitorRegistry.getInstance()
.getActivitiesInStage(Stage.RESUMED)

if (!resumedActivities.iterator().hasNext()) {
return true // No activity yet — don't block
}

val currentActivity = resumedActivities.iterator().next()
val view: View? = currentActivity.findViewById(viewId)

if (view == null) {
return true // View not in this layout — don't block
}

val idle = view.visibility == expectedVisibility
if (idle) {
resourceCallback?.onTransitionToIdle()
}
return idle
}

override fun registerIdleTransitionCallback(callback: IdlingResource.ResourceCallback?) {
this.resourceCallback = callback
}
}
Loading
Loading