diff --git a/Jenkinsfile b/Jenkinsfile index 26ef98a4bc..911121da6a 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -135,7 +135,7 @@ pipeline { always { sh './gradlew stopEmulator' junitAndCoverage "$reports/coverage/debug/report.xml", 'device', javaSrc - archiveArtifacts 'app/build/outputs/logs/logcat.text' + archiveArtifacts 'app/build/outputs/logcat.text' } } } diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/BitmapIntegrationTest.java b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/BitmapIntegrationTest.java new file mode 100644 index 0000000000..9b96e6bae2 --- /dev/null +++ b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/BitmapIntegrationTest.java @@ -0,0 +1,64 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2015 The Catrobat Team + * () + * + * 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. + * + * 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 . + */ + +package org.catrobat.paintroid.test.espresso; + +import android.util.DisplayMetrics; + +import org.catrobat.paintroid.MainActivity; +import org.catrobat.paintroid.test.utils.ScreenshotOnFailRule; +import org.catrobat.paintroid.tools.ToolType; +import org.catrobat.paintroid.tools.Workspace; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import androidx.test.ext.junit.runners.AndroidJUnit4; +import androidx.test.rule.ActivityTestRule; + +import static org.catrobat.paintroid.test.espresso.util.wrappers.ToolBarViewInteraction.onToolBarView; +import static org.junit.Assert.assertEquals; + +@RunWith(AndroidJUnit4.class) +public class BitmapIntegrationTest { + @Rule + public ActivityTestRule launchActivityRule = new ActivityTestRule<>(MainActivity.class); + + @Rule + public ScreenshotOnFailRule screenshotOnFailRule = new ScreenshotOnFailRule(); + + @Before + public void setUp() { + onToolBarView() + .performSelectTool(ToolType.BRUSH); + } + + @Test + public void drawingSurfaceBitmapIsDisplaySize() { + MainActivity activity = launchActivityRule.getActivity(); + Workspace workspace = activity.workspace; + final int bitmapWidth = workspace.getWidth(); + final int bitmapHeight = workspace.getHeight(); + + DisplayMetrics metrics = activity.getResources().getDisplayMetrics(); + assertEquals(metrics.widthPixels, bitmapWidth); + assertEquals(metrics.heightPixels, bitmapHeight); + } +} diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/CatrobatImageIOIntegrationTest.kt b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/CatrobatImageIOIntegrationTest.kt new file mode 100644 index 0000000000..126e5fb51d --- /dev/null +++ b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/CatrobatImageIOIntegrationTest.kt @@ -0,0 +1,106 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2021 The Catrobat Team + * () + * + * 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. + * + * 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 . + */ +package org.catrobat.paintroid.test.espresso + +import android.net.Uri +import androidx.test.espresso.Espresso +import androidx.test.espresso.Espresso.onView +import androidx.test.espresso.action.ViewActions +import androidx.test.espresso.action.ViewActions.replaceText +import androidx.test.espresso.matcher.RootMatchers +import androidx.test.espresso.matcher.ViewMatchers.withText +import androidx.test.espresso.matcher.ViewMatchers.withId +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.rule.ActivityTestRule +import androidx.test.rule.GrantPermissionRule +import org.catrobat.paintroid.FileIO +import org.catrobat.paintroid.MainActivity +import org.catrobat.paintroid.R +import org.catrobat.paintroid.test.espresso.util.DrawingSurfaceLocationProvider +import org.catrobat.paintroid.test.espresso.util.EspressoUtils +import org.catrobat.paintroid.test.espresso.util.UiInteractions +import org.catrobat.paintroid.test.espresso.util.wrappers.DrawingSurfaceInteraction.onDrawingSurfaceView +import org.catrobat.paintroid.test.espresso.util.wrappers.TopBarViewInteraction +import org.catrobat.paintroid.test.utils.ScreenshotOnFailRule +import org.hamcrest.Matchers +import org.hamcrest.core.AllOf +import org.junit.After +import org.junit.Assert +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import java.io.File + +@RunWith(AndroidJUnit4::class) +class CatrobatImageIOIntegrationTest { + + @get:Rule + val launchActivityRule = ActivityTestRule(MainActivity::class.java) + + @get:Rule + val screenshotOnFailRule = ScreenshotOnFailRule() + + @get:Rule + val grantPermissionRule: GrantPermissionRule = EspressoUtils.grantPermissionRulesVersionCheck() + + private lateinit var uriFile: Uri + private lateinit var activity: MainActivity + + companion object { + private const val IMAGE_NAME = "fileName" + } + @Before + fun setUp() { + activity = launchActivityRule.activity + } + + @After + fun tearDown() { + with(File(uriFile.path!!)) { + if (exists()) { + delete() + } + } + } + + @Test + fun testWriteAndReadCatrobatImage() { + onDrawingSurfaceView() + .perform(UiInteractions.touchAt(DrawingSurfaceLocationProvider.MIDDLE)) + TopBarViewInteraction.onTopBarView() + .performOpenMoreOptions() + onView(withText(R.string.menu_save_image)) + .perform(ViewActions.click()) + onView(withId(R.id.pocketpaint_save_dialog_spinner)) + .perform(ViewActions.click()) + Espresso.onData( + AllOf.allOf( + Matchers.`is`(Matchers.instanceOf(String::class.java)), + Matchers.`is`(FileIO.FileType.CATROBAT.value) + ) + ).inRoot(RootMatchers.isPlatformPopup()).perform(ViewActions.click()) + onView(withId(R.id.pocketpaint_image_name_save_text)) + .perform(replaceText(IMAGE_NAME)) + onView(withText(R.string.save_button_text)).perform(ViewActions.click()) + uriFile = activity.model.savedPictureUri!! + Assert.assertNotNull(uriFile) + Assert.assertNotNull(activity.workspace.getCommandSerializationHelper().readFromFile(uriFile)) + } +} diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/FileFromOtherSourceIntegrationTest.java b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/FileFromOtherSourceIntegrationTest.java new file mode 100644 index 0000000000..04682c0545 --- /dev/null +++ b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/FileFromOtherSourceIntegrationTest.java @@ -0,0 +1,147 @@ +package org.catrobat.paintroid.test.espresso; + +import android.content.ContentResolver; +import android.content.ContentValues; +import android.content.Intent; +import android.graphics.Bitmap; +import android.net.Uri; +import android.os.Build; +import android.os.Bundle; +import android.os.Environment; +import android.provider.MediaStore; +import android.util.Log; + +import org.catrobat.paintroid.FileIO; +import org.catrobat.paintroid.MainActivity; +import org.catrobat.paintroid.test.espresso.util.EspressoUtils; +import org.catrobat.paintroid.test.utils.ScreenshotOnFailRule; +import org.catrobat.paintroid.tools.ToolType; +import org.junit.After; +import org.junit.Before; +import org.junit.ClassRule; +import org.junit.Rule; +import org.junit.Test; + +import java.io.File; +import java.io.IOException; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.Objects; + +import androidx.test.rule.ActivityTestRule; +import androidx.test.rule.GrantPermissionRule; + +import static org.catrobat.paintroid.test.espresso.util.wrappers.ToolBarViewInteraction.onToolBarView; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +public class FileFromOtherSourceIntegrationTest { + + private static ArrayList deletionFileList = null; + + @Rule + public ActivityTestRule launchActivityRule = new ActivityTestRule<>(MainActivity.class); + + @Rule + public ScreenshotOnFailRule screenshotOnFailRule = new ScreenshotOnFailRule(); + + @ClassRule + public static GrantPermissionRule grantPermissionRule = EspressoUtils.grantPermissionRulesVersionCheck(); + + private ContentResolver resolver; + private MainActivity activity; + + @Before + public void setUp() { + onToolBarView().performSelectTool(ToolType.BRUSH); + deletionFileList = new ArrayList<>(); + activity = launchActivityRule.getActivity(); + resolver = launchActivityRule.getActivity().getContentResolver(); + } + + @Test + public void testGetSharedPictureFromOtherApp() { + Intent intent = new Intent(); + Uri receivedUri = createTestImageFile(); + Bitmap receivedBitmap = null; + + try { + receivedBitmap = FileIO.getBitmapFromUri(resolver, receivedUri, activity); + } catch (Exception e) { + Log.e("Can't read", "Can't get Bitmap from File"); + } + + Objects.requireNonNull(receivedBitmap); + intent.setData(receivedUri); + intent.setType("image/png"); + intent.setAction(Intent.ACTION_SEND); + intent.putExtra(Intent.EXTRA_STREAM, receivedUri); + + launchActivityRule.launchActivity(intent); + Intent mainActivityIntent = launchActivityRule.getActivity().getIntent(); + + String intentAction = intent.getAction(); + String intentType = intent.getType(); + Bundle intentBundle = intent.getExtras(); + Objects.requireNonNull(intentBundle); + Uri intentUri = (Uri) intentBundle.get(Intent.EXTRA_STREAM); + + String mainActivityIntentAction = mainActivityIntent.getAction(); + String mainActivityIntentType = mainActivityIntent.getType(); + Bundle mainActivityIntentBundle = mainActivityIntent.getExtras(); + Objects.requireNonNull(mainActivityIntentBundle); + Uri mainActivityIntentUri = (Uri) mainActivityIntentBundle.get(Intent.EXTRA_STREAM); + Bitmap mainActivityIntentBitmap = null; + Objects.requireNonNull(mainActivityIntentUri); + + try { + mainActivityIntentBitmap = FileIO.getBitmapFromUri(resolver, mainActivityIntentUri, activity); + } catch (Exception e) { + Log.e("Can't read", "Can't get Bitmap from File"); + } + + Objects.requireNonNull(mainActivityIntentBitmap); + + assertEquals(intentAction, mainActivityIntentAction); + assertEquals(intentType, mainActivityIntentType); + assertEquals(intentUri, mainActivityIntentUri); + assertEquals(receivedBitmap.getWidth(), mainActivityIntentBitmap.getWidth()); + assertEquals(receivedBitmap.getHeight(), mainActivityIntentBitmap.getHeight()); + } + + @After + public void tearDown() { + for (File file : deletionFileList) { + if (file != null && file.exists()) { + assertTrue(file.delete()); + } + } + } + + private Uri createTestImageFile() { + Bitmap bitmap = Bitmap.createBitmap(400, 400, Bitmap.Config.ARGB_8888); + + ContentValues contentValues = new ContentValues(); + contentValues.put(MediaStore.Images.Media.DISPLAY_NAME, "testfile.jpg"); + contentValues.put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg"); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + contentValues.put(MediaStore.Images.Media.RELATIVE_PATH, Environment.DIRECTORY_PICTURES); + } + + Uri imageUri = resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues); + + try { + OutputStream fos = resolver.openOutputStream(Objects.requireNonNull(imageUri)); + assertTrue(bitmap.compress(Bitmap.CompressFormat.JPEG, 100, fos)); + assert fos != null; + fos.close(); + } catch (IOException e) { + throw new AssertionError("Picture file could not be created.", e); + } + + File imageFile = new File(imageUri.getPath(), "testfile.jpg"); + deletionFileList.add(imageFile); + + return imageUri; + } +} diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/MainActivityIntegrationTest.java b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/MainActivityIntegrationTest.java new file mode 100644 index 0000000000..ac5de2ceb6 --- /dev/null +++ b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/MainActivityIntegrationTest.java @@ -0,0 +1,144 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2022 The Catrobat Team + * () + * + * 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. + * + * 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 . + */ + +package org.catrobat.paintroid.test.espresso; + +import android.app.Activity; +import android.content.Context; + +import org.catrobat.paintroid.MainActivity; +import org.catrobat.paintroid.R; +import org.catrobat.paintroid.UserPreferences; +import org.catrobat.paintroid.command.CommandFactory; +import org.catrobat.paintroid.command.CommandManager; +import org.catrobat.paintroid.contract.MainActivityContracts; +import org.catrobat.paintroid.controller.ToolController; +import org.catrobat.paintroid.presenter.MainActivityPresenter; +import org.catrobat.paintroid.test.utils.ScreenshotOnFailRule; +import org.catrobat.paintroid.tools.Workspace; +import org.catrobat.paintroid.ui.Perspective; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.io.File; + +import androidx.test.ext.junit.runners.AndroidJUnit4; +import androidx.test.platform.app.InstrumentationRegistry; +import androidx.test.rule.ActivityTestRule; + +import static org.catrobat.paintroid.test.espresso.util.wrappers.TopBarViewInteraction.onTopBarView; +import static org.mockito.Mockito.verify; + +import static androidx.test.espresso.Espresso.onView; +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; +import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed; +import static androidx.test.espresso.matcher.ViewMatchers.withText; + +@RunWith(AndroidJUnit4.class) +@SuppressWarnings("PMD.UnusedPrivateField") +public class MainActivityIntegrationTest { + + @Mock + private MainActivityContracts.MainView view; + @Mock + private MainActivityContracts.Model model; + @Mock + private MainActivityContracts.Navigator navigator; + @Mock + private MainActivityContracts.Interactor interactor; + @Mock + private MainActivityContracts.TopBarViewHolder topBarViewHolder; + @Mock + private MainActivityContracts.DrawerLayoutViewHolder drawerLayoutViewHolder; + @Mock + private Workspace workspace; + @Mock + private Perspective perspective; + @Mock + private ToolController toolController; + @Mock + private CommandFactory commandFactory; + @Mock + private CommandManager commandManager; + @Mock + private MainActivityContracts.BottomBarViewHolder bottomBarViewHolder; + @Mock + private MainActivityContracts.BottomNavigationViewHolder bottomNavigationViewHolder; + @Mock + private UserPreferences sharedPreferences; + @Mock + private Context context; + @Mock + private File internalMemoryPath; + + @InjectMocks + private MainActivityPresenter presenter; + + @Rule + public ActivityTestRule launchActivityRule = new ActivityTestRule<>(MainActivity.class); + + @Rule + public ScreenshotOnFailRule screenshotOnFailRule = new ScreenshotOnFailRule(); + + @Test + public void testMoreOptionsMenuAboutTextIsCorrect() { + + onTopBarView() + .performOpenMoreOptions(); + onView(withText(R.string.pocketpaint_menu_about)) + .perform(click()); + + Context context = InstrumentationRegistry.getInstrumentation().getTargetContext(); + String aboutTextExpected = context.getString(R.string.pocketpaint_about_content, + context.getString(R.string.pocketpaint_about_license)); + + onView(withText(aboutTextExpected)) + .check(matches(isDisplayed())); + } + + @Test + public void testMoreOptionsMenuAboutClosesMoreOptions() { + + onTopBarView() + .performOpenMoreOptions(); + + onView(withText(R.string.pocketpaint_menu_about)) + .perform(click()); + + pressBack(); + + onView(withText(R.string.pocketpaint_menu_about)) + .check(doesNotExist()); + } + + @Test + public void testHandleActivityResultWhenIntentIsNull() { + launchActivityRule.getActivity().onActivityResult(0, Activity.RESULT_OK, null); + MockitoAnnotations.initMocks(this); + presenter.handleActivityResult(0, Activity.RESULT_OK, null); + verify(view).superHandleActivityResult(0, Activity.RESULT_OK, null); + } +} diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/MenuFileActivityIntegrationTest.kt b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/MenuFileActivityIntegrationTest.kt new file mode 100644 index 0000000000..488bfea654 --- /dev/null +++ b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/MenuFileActivityIntegrationTest.kt @@ -0,0 +1,524 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2021 The Catrobat Team + * () + * + * 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. + * + * 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 . + */ +package org.catrobat.paintroid.test.espresso + +import android.app.Activity +import android.app.Instrumentation.ActivityResult +import android.content.ContentValues +import android.content.Context +import android.content.Intent +import android.graphics.Bitmap +import android.graphics.Color +import android.net.Uri +import android.os.Build +import android.os.Environment +import android.provider.MediaStore +import androidx.test.espresso.Espresso.onData +import androidx.test.espresso.Espresso.onView +import androidx.test.espresso.Espresso.pressBack +import androidx.test.espresso.action.ViewActions.click +import androidx.test.espresso.action.ViewActions.pressMenuKey +import androidx.test.espresso.action.ViewActions.replaceText +import androidx.test.espresso.assertion.ViewAssertions.doesNotExist +import androidx.test.espresso.assertion.ViewAssertions.matches +import androidx.test.espresso.intent.Intents +import androidx.test.espresso.intent.matcher.IntentMatchers.hasAction +import androidx.test.espresso.intent.rule.IntentsTestRule +import androidx.test.espresso.matcher.RootMatchers +import androidx.test.espresso.matcher.ViewMatchers.withText +import androidx.test.espresso.matcher.ViewMatchers.isRoot +import androidx.test.espresso.matcher.ViewMatchers.withId +import androidx.test.espresso.matcher.ViewMatchers.isDisplayed +import androidx.test.espresso.matcher.ViewMatchers.withSpinnerText +import androidx.test.espresso.matcher.ViewMatchers.isClickable +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.rule.GrantPermissionRule +import org.catrobat.paintroid.FileIO +import org.catrobat.paintroid.MainActivity +import org.catrobat.paintroid.R +import org.catrobat.paintroid.presenter.MainActivityPresenter +import org.catrobat.paintroid.test.espresso.util.BitmapLocationProvider +import org.catrobat.paintroid.test.espresso.util.DrawingSurfaceLocationProvider.HALFWAY_BOTTOM_MIDDLE +import org.catrobat.paintroid.test.espresso.util.DrawingSurfaceLocationProvider.HALFWAY_RIGHT_MIDDLE +import org.catrobat.paintroid.test.espresso.util.DrawingSurfaceLocationProvider.MIDDLE +import org.catrobat.paintroid.test.espresso.util.EspressoUtils.grantPermissionRulesVersionCheck +import org.catrobat.paintroid.test.espresso.util.UiInteractions.touchAt +import org.catrobat.paintroid.test.espresso.util.UiInteractions.waitFor +import org.catrobat.paintroid.test.espresso.util.wrappers.DrawingSurfaceInteraction.onDrawingSurfaceView +import org.catrobat.paintroid.test.espresso.util.wrappers.ToolBarViewInteraction +import org.catrobat.paintroid.test.espresso.util.wrappers.TopBarViewInteraction.onTopBarView +import org.catrobat.paintroid.test.utils.ScreenshotOnFailRule +import org.catrobat.paintroid.tools.ToolType +import org.hamcrest.Matchers.containsString +import org.hamcrest.Matchers.instanceOf +import org.hamcrest.Matchers.`is` +import org.hamcrest.core.AllOf.allOf +import org.hamcrest.core.IsNot +import org.junit.After +import org.junit.Assert.assertEquals +import org.junit.Assert.assertFalse +import org.junit.Assert.assertNotNull +import org.junit.Assert.assertNotSame +import org.junit.Assert.assertTrue +import org.junit.Before +import org.junit.ClassRule +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import java.io.File +import java.io.IOException +import java.lang.AssertionError + +@RunWith(AndroidJUnit4::class) +class MenuFileActivityIntegrationTest { + @get:Rule + var launchActivityRule = IntentsTestRule(MainActivity::class.java) + + @get:Rule + var screenshotOnFailRule = ScreenshotOnFailRule() + + private lateinit var activity: MainActivity + private var defaultFileName = "menuTestDefaultFile" + + companion object { + private lateinit var deletionFileList: ArrayList + + @get:ClassRule + var grantPermissionRule: GrantPermissionRule = grantPermissionRulesVersionCheck() + } + + @Before + fun setUp() { + ToolBarViewInteraction.onToolBarView().performSelectTool(ToolType.BRUSH) + deletionFileList = ArrayList() + activity = launchActivityRule.activity + } + + @After + fun tearDown() { + for (file in deletionFileList) { + if (file != null && file.exists()) { + assertTrue(file.delete()) + } + } + } + + @Test + fun testNewEmptyDrawingWithSave() { + onDrawingSurfaceView().perform(touchAt(MIDDLE)) + onDrawingSurfaceView().checkPixelColor(Color.BLACK, BitmapLocationProvider.MIDDLE) + onTopBarView().performOpenMoreOptions() + onView(withText(R.string.menu_new_image)).perform(click()) + onView(withText(R.string.save_button_text)).perform(click()) + onView(isRoot()).perform(waitFor(100)) + onView(withId(R.id.pocketpaint_image_name_save_text)) + .perform(replaceText("test987654")) + onView(withText(R.string.save_button_text)).perform(click()) + onView(isRoot()).perform(waitFor(100)) + onDrawingSurfaceView().checkPixelColor(Color.TRANSPARENT, BitmapLocationProvider.MIDDLE) + } + + @Test + fun testLoadImageDialog() { + onDrawingSurfaceView().perform(touchAt(MIDDLE)) + onTopBarView().performOpenMoreOptions() + onView(withText(R.string.menu_load_image)).perform(click()) + onView(withText(R.string.menu_load_image)).check(matches(isDisplayed())) + onView(withText(R.string.dialog_warning_new_image)).check(matches(isDisplayed())) + onView(withText(R.string.save_button_text)).check(matches(isDisplayed())) + onView(withText(R.string.discard_button_text)).check(matches(isDisplayed())) + } + + @Test + fun testLoadImageDialogIntentCancel() { + onDrawingSurfaceView().perform(touchAt(MIDDLE)) + val resultCancel = ActivityResult(Activity.RESULT_CANCELED, Intent()) + Intents.intending(hasAction(Intent.ACTION_GET_CONTENT)).respondWith(resultCancel) + onTopBarView().performOpenMoreOptions() + onView(withText(R.string.menu_load_image)).perform(click()) + onView(withText(R.string.discard_button_text)).perform(click()) + onView(withText(R.string.dialog_warning_new_image)).check(doesNotExist()) + onDrawingSurfaceView().checkPixelColor(Color.BLACK, BitmapLocationProvider.MIDDLE) + } + + @Test + fun testLoadImageDialogIntentOK() { + onDrawingSurfaceView().perform(touchAt(HALFWAY_RIGHT_MIDDLE)) + onDrawingSurfaceView().checkPixelColor(Color.TRANSPARENT, BitmapLocationProvider.MIDDLE) + val intent = Intent() + intent.data = createTestImageFile() + val resultOK = ActivityResult(Activity.RESULT_OK, intent) + Intents.intending(hasAction(Intent.ACTION_GET_CONTENT)).respondWith(resultOK) + onTopBarView().performOpenMoreOptions() + onView(withText(R.string.menu_load_image)).perform(click()) + onView(withText(R.string.discard_button_text)).perform(click()) + onView(withText(R.string.dialog_warning_new_image)).check(doesNotExist()) + onDrawingSurfaceView().checkPixelColor(Color.BLACK, BitmapLocationProvider.MIDDLE) + } + + @Test + fun testLoadImageDialogOnBackPressed() { + onDrawingSurfaceView().perform(touchAt(MIDDLE)) + onTopBarView().performOpenMoreOptions() + onView(withText(R.string.menu_load_image)).perform(click()) + pressBack() + onDrawingSurfaceView().check(matches(isDisplayed())) + } + + @Test + fun testOnHelpDisabled() { + onTopBarView().performOpenMoreOptions() + onView(withText(R.string.help_title)).check(matches(IsNot.not(isClickable()))) + } + + @Test + fun testWarningDialogOnNewImage() { + onDrawingSurfaceView().perform(touchAt(MIDDLE)) + onTopBarView().performOpenMoreOptions() + onView(withText(R.string.menu_new_image)).perform(click()) + onView(withText(R.string.dialog_warning_new_image)).check(matches(isDisplayed())) + onView(withText(R.string.save_button_text)).check(matches(isDisplayed())) + onView(withText(R.string.discard_button_text)).check(matches(isDisplayed())) + pressBack() + onView(withText(R.string.dialog_warning_new_image)).check(doesNotExist()) + } + + @Test + fun testNewEmptyDrawingWithDiscard() { + onDrawingSurfaceView().perform(touchAt(MIDDLE)) + onTopBarView().performOpenMoreOptions() + onView(withText(R.string.menu_new_image)).perform(click()) + onView(withText(R.string.discard_button_text)).perform(click()) + onView(withText(R.string.dialog_warning_new_image)).check(doesNotExist()) + onDrawingSurfaceView().checkPixelColor(Color.TRANSPARENT, BitmapLocationProvider.MIDDLE) + } + + @Test + fun testNewEmptyDrawingDialogOnBackPressed() { + onDrawingSurfaceView().perform(touchAt(MIDDLE)) + onTopBarView().performOpenMoreOptions() + onView(withText(R.string.menu_new_image)).perform(click()) + onView(withText(R.string.dialog_warning_new_image)).check(matches(isDisplayed())) + onView(withText(R.string.save_button_text)).check(matches(isDisplayed())) + onView(withText(R.string.discard_button_text)).check(matches(isDisplayed())) + pressBack() + onView(withText(R.string.dialog_warning_new_image)).check(doesNotExist()) + onDrawingSurfaceView().checkPixelColor(Color.BLACK, BitmapLocationProvider.MIDDLE) + } + + @Test + fun testSavedStateChangeAfterSave() { + onDrawingSurfaceView().perform(touchAt(MIDDLE)) + assertFalse(activity.model.isSaved) + pressMenuKey() + onTopBarView().performOpenMoreOptions() + onView(withText(R.string.menu_save_image)).perform(click()) + onView(withId(R.id.pocketpaint_image_name_save_text)) + .perform(replaceText("test98765")) + onView(withText(R.string.save_button_text)).perform(click()) + onView(isRoot()).perform(waitFor(100)) + assertNotNull(activity.model.savedPictureUri) + addUriToDeletionFileList(activity.model.savedPictureUri) + assertTrue(activity.model.isSaved) + } + + @Test + fun testSaveImage() { + onDrawingSurfaceView().perform(touchAt(MIDDLE)) + onTopBarView().performOpenMoreOptions() + onView(withText(R.string.menu_save_image)).perform(click()) + onView(withText(R.string.save_button_text)).perform(click()) + onView(isRoot()).perform(waitFor(100)) + assertNotNull(activity.model.savedPictureUri) + if (!activity.model.isOpenedFromCatroid) { + assertNotSame( + "null", + MainActivityPresenter.getPathFromUri(activity, activity.model.savedPictureUri!!) + ) + } + addUriToDeletionFileList(activity.model.savedPictureUri) + } + + @Test + fun testSaveCopy() { + launchActivityRule.activity.getPreferences(Context.MODE_PRIVATE) + .edit() + .clear() + .commit() + onDrawingSurfaceView().perform(touchAt(MIDDLE)) + onTopBarView().performOpenMoreOptions() + onView(withText(R.string.menu_save_image)).perform(click()) + onView(withId(R.id.pocketpaint_image_name_save_text)) + .perform(replaceText("save1")) + onView(withText(R.string.save_button_text)).perform(click()) + onView(isRoot()).perform(waitFor(100)) + assertNotNull(activity.model.savedPictureUri) + if (!activity.model.isOpenedFromCatroid) { + assertNotSame( + "null", + MainActivityPresenter.getPathFromUri(activity, activity.model.savedPictureUri!!) + ) + } + addUriToDeletionFileList(activity.model.savedPictureUri) + val oldFile = File(activity.model.savedPictureUri.toString()) + onView(withText(R.string.pocketpaint_no)).perform(click()) + onView(withText(R.string.pocketpaint_ok)).perform(click()) + onDrawingSurfaceView().perform(touchAt(HALFWAY_BOTTOM_MIDDLE)) + onTopBarView().performOpenMoreOptions() + onView(withText(R.string.menu_save_copy)).perform(click()) + onView(withId(R.id.pocketpaint_image_name_save_text)) + .perform(replaceText("copy1")) + onView(withText(R.string.save_button_text)).perform(click()) + onView(isRoot()).perform(waitFor(100)) + val newFile = File(activity.model.savedPictureUri.toString()) + assertNotSame("Changes to saved", oldFile, newFile) + assertNotNull(activity.model.savedPictureUri) + if (!activity.model.isOpenedFromCatroid) { + assertNotSame( + "null", + MainActivityPresenter.getPathFromUri(activity, activity.model.savedPictureUri!!) + ) + } + addUriToDeletionFileList(activity.model.savedPictureUri) + } + + @Test + fun testAskForSaveAfterSavedOnce() { + onDrawingSurfaceView().perform(touchAt(MIDDLE)) + onTopBarView().performOpenMoreOptions() + onView(withText(R.string.menu_save_image)).perform(click()) + onView(withId(R.id.pocketpaint_image_name_save_text)) + .perform(replaceText("AskForSaveAfterSavedOnce")) + onView(withText(R.string.save_button_text)).perform(click()) + onView(isRoot()).perform(waitFor(100)) + assertNotNull(activity.model.savedPictureUri) + addUriToDeletionFileList(activity.model.savedPictureUri) + onDrawingSurfaceView().perform(touchAt(MIDDLE)) + pressBack() + onView(withText(R.string.menu_quit)).check(matches(isDisplayed())) + } + + @Test + fun testShowOverwriteDialogAfterSavingAgain() { + onDrawingSurfaceView().perform(touchAt(MIDDLE)) + onTopBarView().performOpenMoreOptions() + onView(withText(R.string.menu_save_image)).perform(click()) + onView(withId(R.id.pocketpaint_image_name_save_text)) + .perform(replaceText("12345test12345")) + onView(withText(R.string.save_button_text)).perform(click()) + onView(isRoot()).perform(waitFor(100)) + assertNotNull(activity.model.savedPictureUri) + addUriToDeletionFileList(activity.model.savedPictureUri) + onTopBarView().performOpenMoreOptions() + onView(withText(R.string.menu_save_image)).perform(click()) + onView(withText(R.string.save_button_text)).perform(click()) + onView(isRoot()).perform(waitFor(100)) + onView(withText(R.string.overwrite_button_text)).check(matches(isDisplayed())) + } + + @Test + fun testCheckImageNumberIncrementAfterSaveWithStandardName() { + FileIO.filename = "image" + val imageNumber = launchActivityRule.activity.presenter.imageNumber + onTopBarView().performOpenMoreOptions() + onView(withText(R.string.menu_save_image)).perform(click()) + onView(withText(R.string.save_button_text)).perform(click()) + onView(isRoot()).perform(waitFor(200)) + assertNotNull(activity.model.savedPictureUri) + addUriToDeletionFileList(activity.model.savedPictureUri) + onDrawingSurfaceView().perform(touchAt(MIDDLE)) + onTopBarView().performOpenMoreOptions() + onView(withText(R.string.menu_save_image)).perform(click()) + onView(isRoot()).perform(waitFor(200)) + val newImageNumber = launchActivityRule.activity.presenter.imageNumber + assertEquals((imageNumber + 1).toLong(), newImageNumber.toLong()) + } + + @Test + fun testCheckImageNumberSameAfterSaveWithNonStandardName() { + onDrawingSurfaceView().perform(touchAt(MIDDLE)) + onTopBarView().performOpenMoreOptions() + onView(withText(R.string.menu_save_image)).perform(click()) + val imageNumber = launchActivityRule.activity.presenter.imageNumber + onView(withId(R.id.pocketpaint_image_name_save_text)) + .perform(replaceText("test9876")) + onView(withText(R.string.save_button_text)).perform(click()) + onView(isRoot()).perform(waitFor(100)) + assertNotNull(activity.model.savedPictureUri) + addUriToDeletionFileList(activity.model.savedPictureUri) + val newImageNumber = launchActivityRule.activity.presenter.imageNumber + assertEquals(imageNumber.toLong(), newImageNumber.toLong()) + } + + @Test + fun testCheckSaveFileWithDifferentFormats() { + onDrawingSurfaceView().perform(touchAt(MIDDLE)) + onTopBarView().performOpenMoreOptions() + onView(withText(R.string.menu_save_image)).perform(click()) + onView(withId(R.id.pocketpaint_save_info_title)).check(matches(isDisplayed())) + onView(withId(R.id.pocketpaint_image_name_save_text)).check(matches(isDisplayed())) + onView(withId(R.id.pocketpaint_save_dialog_spinner)).check(matches(isDisplayed())) + onView(withId(R.id.pocketpaint_save_dialog_spinner)).perform(click()) + onData(allOf(`is`(instanceOf(String::class.java)), `is`("png"))) + .inRoot(RootMatchers.isPlatformPopup()).perform(click()) + onView(withId(R.id.pocketpaint_image_name_save_text)) + .perform(replaceText(defaultFileName)) + onView(withText(R.string.save_button_text)).perform(click()) + onView(isRoot()).perform(waitFor(100)) + assertNotNull(activity.model.savedPictureUri) + addUriToDeletionFileList(activity.model.savedPictureUri) + val oldFile = File(activity.model.savedPictureUri.toString()) + onTopBarView().performOpenMoreOptions() + onView(withText(R.string.menu_save_image)).perform(click()) + onView(withId(R.id.pocketpaint_save_dialog_spinner)).perform(click()) + onData(allOf(`is`(instanceOf(String::class.java)), `is`("jpg"))) + .inRoot(RootMatchers.isPlatformPopup()).perform(click()) + onView(withId(R.id.pocketpaint_image_name_save_text)) + .perform(replaceText(defaultFileName)) + onView(withText(R.string.save_button_text)).perform(click()) + onView(isRoot()).perform(waitFor(100)) + assertNotNull(activity.model.savedPictureUri) + addUriToDeletionFileList(activity.model.savedPictureUri) + val newFile = File(activity.model.savedPictureUri.toString()) + assertNotSame(oldFile, newFile) + } + + @Test + fun testCheckSaveImageDialogShowJPGSpinnerText() { + createImageIntent() + onTopBarView().performOpenMoreOptions() + onView(withText(R.string.menu_load_image)).perform(click()) + onView(withText(R.string.dialog_warning_new_image)).check(doesNotExist()) + onDrawingSurfaceView().perform(touchAt(MIDDLE)) + onTopBarView().performOpenMoreOptions() + onView(withText(R.string.menu_save_image)).perform(click()) + onView(withId(R.id.pocketpaint_save_dialog_spinner)) + .check(matches(withSpinnerText(containsString("jpg")))) + } + + @Test + fun testCheckSaveImageDialogShowPNGSpinnerText() { + FileIO.compressFormat = Bitmap.CompressFormat.PNG + onDrawingSurfaceView().perform(touchAt(MIDDLE)) + onTopBarView().performOpenMoreOptions() + onView(withText(R.string.menu_save_image)).perform(click()) + onView(withId(R.id.pocketpaint_save_dialog_spinner)) + .check(matches(withSpinnerText(containsString("png")))) + } + + @Test + fun testCheckSaveImageDialogShowORASpinnerText() { + FileIO.isCatrobatImage = true + onDrawingSurfaceView().perform(touchAt(MIDDLE)) + onTopBarView().performOpenMoreOptions() + onView(withText(R.string.menu_save_image)).perform(click()) + onView(withId(R.id.pocketpaint_save_dialog_spinner)) + .check(matches(withSpinnerText(containsString("ora")))) + } + + @Test + fun testCheckSaveImageDialogShowsSavedImageOptions() { + onDrawingSurfaceView().perform(touchAt(MIDDLE)) + onTopBarView().performOpenMoreOptions() + onView(withText(R.string.menu_save_image)).perform(click()) + val imageName = "test12345" + onView(withId(R.id.pocketpaint_image_name_save_text)).perform(replaceText(imageName)) + onView(withId(R.id.pocketpaint_save_dialog_spinner)).perform(click()) + onData(allOf(`is`(instanceOf(String::class.java)), `is`("png"))) + .inRoot(RootMatchers.isPlatformPopup()).perform(click()) + onView(withText(R.string.save_button_text)).perform(click()) + onView(isRoot()).perform(waitFor(100)) + assertNotNull(activity.model.savedPictureUri) + addUriToDeletionFileList(activity.model.savedPictureUri) + onTopBarView().performOpenMoreOptions() + onView(withText(R.string.menu_save_image)).perform(click()) + onView(withText(imageName)).check(matches(isDisplayed())) + onView(withText("png")).check(matches(isDisplayed())) + } + + @Test + fun testCheckCopyIsAlwaysDefaultOptions() { + onDrawingSurfaceView().perform(touchAt(MIDDLE)) + onTopBarView().performOpenMoreOptions() + onView(withText(R.string.menu_save_copy)).perform(click()) + var imageNumber = launchActivityRule.activity.presenter.imageNumber + onView(withText("png")).check(matches(isDisplayed())) + onView(withText("image$imageNumber")).check(matches(isDisplayed())) + onView(withId(R.id.pocketpaint_save_dialog_spinner)).perform(click()) + onData(allOf(`is`(instanceOf(String::class.java)), `is`("png"))) + .inRoot(RootMatchers.isPlatformPopup()).perform(click()) + onView(withText(R.string.save_button_text)).perform(click()) + onView(isRoot()).perform(waitFor(100)) + onTopBarView().performOpenMoreOptions() + onView(withText(R.string.menu_save_copy)).perform(click()) + imageNumber = launchActivityRule.activity.presenter.imageNumber + onView(withText("png")).check(matches(isDisplayed())) + onView(withText("image$imageNumber")).check(matches(isDisplayed())) + } + + private fun createImageIntent() { + val intent = Intent() + intent.data = createTestImageFile() + val resultOK = ActivityResult(Activity.RESULT_OK, intent) + Intents.intending(hasAction(Intent.ACTION_GET_CONTENT)).respondWith(resultOK) + } + + private fun createTestImageFile(): Uri? { + val bitmap = Bitmap.createBitmap(400, 400, Bitmap.Config.ARGB_8888) + val contentValues = ContentValues() + contentValues.put(MediaStore.Images.Media.DISPLAY_NAME, "testfile.jpg") + contentValues.put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg") + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + contentValues.put(MediaStore.Images.Media.RELATIVE_PATH, Environment.DIRECTORY_PICTURES) + } + val resolver = activity.contentResolver + val imageUri = resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues) + try { + val fos = imageUri?.let { resolver.openOutputStream(it) } + assertTrue(bitmap.compress(Bitmap.CompressFormat.JPEG, 100, fos)) + assert(fos != null) + fos?.close() + } catch (e: IOException) { + throw AssertionError("Picture file could not be created.", e) + } + val imageFile = File(imageUri?.path, "testfile.jpg") + deletionFileList.add(imageFile) + return imageUri + } + + @Test + fun testLoadImageTransparency() { + val intent = Intent() + intent.data = createTestImageFile() + val result = ActivityResult(Activity.RESULT_OK, intent) + Intents.intending(hasAction(Intent.ACTION_GET_CONTENT)).respondWith(result) + onTopBarView().performOpenMoreOptions() + onView(withText(R.string.menu_load_image)).perform(click()) + ToolBarViewInteraction.onToolBarView().performSelectTool(ToolType.ERASER) + onDrawingSurfaceView().checkPixelColor(Color.BLACK, BitmapLocationProvider.MIDDLE) + onDrawingSurfaceView().perform(touchAt(MIDDLE)) + onDrawingSurfaceView().checkPixelColor(Color.TRANSPARENT, BitmapLocationProvider.MIDDLE) + } + + private fun addUriToDeletionFileList(uri: Uri?) { + uri?.path?.let { + deletionFileList.add(File(it)) + } + } +} diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/MoreOptionsIntegrationTest.java b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/MoreOptionsIntegrationTest.java new file mode 100644 index 0000000000..cdc9110605 --- /dev/null +++ b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/MoreOptionsIntegrationTest.java @@ -0,0 +1,298 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2015 The Catrobat Team + * () + * + * 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. + * + * 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 . + */ + +package org.catrobat.paintroid.test.espresso; + +import android.app.Activity; +import android.app.Instrumentation; +import android.content.Context; +import android.content.Intent; + +import org.catrobat.paintroid.MainActivity; +import org.catrobat.paintroid.R; +import org.catrobat.paintroid.test.espresso.util.EspressoUtils; +import org.catrobat.paintroid.test.utils.ScreenshotOnFailRule; +import org.catrobat.paintroid.tools.helper.AdvancedSettingsAlgorithms; +import org.junit.After; +import org.junit.Before; +import org.junit.ClassRule; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import static org.catrobat.paintroid.test.espresso.util.UiInteractions.waitFor; +import static org.hamcrest.Matchers.instanceOf; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.core.AllOf.allOf; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import androidx.test.espresso.action.ViewActions; +import androidx.test.espresso.intent.Intents; +import androidx.test.espresso.intent.matcher.IntentMatchers; +import androidx.test.ext.junit.runners.AndroidJUnit4; +import androidx.test.platform.app.InstrumentationRegistry; +import androidx.test.rule.ActivityTestRule; +import androidx.test.rule.GrantPermissionRule; +import androidx.test.uiautomator.UiDevice; +import androidx.test.uiautomator.UiObject; +import androidx.test.uiautomator.UiSelector; + +import static org.catrobat.paintroid.test.espresso.util.wrappers.OptionsMenuViewInteraction.onOptionsMenu; +import static org.catrobat.paintroid.test.espresso.util.wrappers.TopBarViewInteraction.onTopBarView; + +import static androidx.test.espresso.Espresso.onData; +import static androidx.test.espresso.Espresso.onView; +import static androidx.test.espresso.Espresso.pressBack; +import static androidx.test.espresso.action.ViewActions.click; +import static androidx.test.espresso.action.ViewActions.replaceText; +import static androidx.test.espresso.assertion.ViewAssertions.doesNotExist; +import static androidx.test.espresso.assertion.ViewAssertions.matches; +import static androidx.test.espresso.matcher.RootMatchers.isPlatformPopup; +import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed; +import static androidx.test.espresso.matcher.ViewMatchers.isRoot; +import static androidx.test.espresso.matcher.ViewMatchers.withId; +import static androidx.test.espresso.matcher.ViewMatchers.withText; + +@RunWith(AndroidJUnit4.class) +public class MoreOptionsIntegrationTest { + + @Rule + public ActivityTestRule activityTestRule = new ActivityTestRule<>(MainActivity.class); + + @Rule + public ScreenshotOnFailRule screenshotOnFailRule = new ScreenshotOnFailRule(); + + @ClassRule + public static GrantPermissionRule grantPermissionRule = EspressoUtils.grantPermissionRulesVersionCheck(); + + public String defaultPictureName = "moreOptionsImageTest"; + + @Before + public void setUp() { + onTopBarView() + .performOpenMoreOptions(); + + activityTestRule.getActivity().getPreferences(Context.MODE_PRIVATE) + .edit() + .clear() + .commit(); + } + + @After + public void tearDown() { + activityTestRule.getActivity().getPreferences(Context.MODE_PRIVATE) + .edit() + .clear() + .commit(); + } + + @Test + public void testMoreOptionsCloseOnBack() { + onView(withText(R.string.menu_load_image)) + .check(matches(isDisplayed())); + + pressBack(); + onView(withText(R.string.menu_load_image)) + .check(doesNotExist()); + } + + @Test + public void testMoreOptionsAllItemsExist() { + onOptionsMenu() + .checkItemExists(R.string.menu_load_image) + .checkItemExists(R.string.menu_hide_menu) + .checkItemExists(R.string.help_title) + .checkItemExists(R.string.pocketpaint_menu_about) + .checkItemExists(R.string.menu_rate_us) + .checkItemExists(R.string.menu_save_image) + .checkItemExists(R.string.menu_save_copy) + .checkItemExists(R.string.menu_new_image) + .checkItemExists(R.string.menu_feedback) + .checkItemExists(R.string.share_image_menu) + .checkItemExists(R.string.menu_advanced) + + .checkItemDoesNotExist(R.string.menu_discard_image) + .checkItemDoesNotExist(R.string.menu_export); + } + + @Test + public void testMoreOptionsItemHelpClick() { + onView(withText(R.string.help_title)).perform(click()); + } + + @Test + public void testMoreOptionsItemAboutClick() { + onView(withText(R.string.pocketpaint_about_title)).perform(click()); + } + + @Test + public void testMoreOptionsShareImageClicked() { + onView(withText(R.string.share_image_menu)).perform(click()); + UiDevice mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()); + UiObject uiObject = mDevice.findObject(new UiSelector()); + assertTrue(uiObject.exists()); + mDevice.pressBack(); + } + + @Test + public void testMoreOptionsItemNewImageClick() { + onView(withText(R.string.menu_new_image)).perform(click()); + } + + @Test + public void testMoreOptionsItemMenuSaveClick() { + onView(withText(R.string.menu_save_image)).perform(click()); + } + + @Test + public void testMoreOptionsItemMenuCopyClick() { + onView(withText(R.string.menu_save_copy)).perform(click()); + } + @Test + public void testMoreOptionsFeedbackClick() { + Intent intent = new Intent(); + Intents.init(); + Instrumentation.ActivityResult intentResult = new Instrumentation.ActivityResult(Activity.RESULT_OK, intent); + + Intents.intending(IntentMatchers.anyIntent()).respondWith(intentResult); + + onView(withText(R.string.menu_feedback)).perform(click()); + + Intents.intended(IntentMatchers.hasAction(Intent.ACTION_SENDTO)); + Intents.release(); + } + + @Test + public void testShowLikeUsDialogOnFirstSave() { + onView(withText(R.string.menu_save_image)).perform(click()); + onView(withId(R.id.pocketpaint_image_name_save_text)) + .perform(ViewActions.replaceText("likeus")); + onView(withText(R.string.save_button_text)).perform(click()); + onView(isRoot()).perform(waitFor(100)); + onView(withText(R.string.pocketpaint_like_us)).check(matches(isDisplayed())); + } + + @Test + public void testSaveDialogAppearsOnSaveImageClick() { + onView(withText(R.string.menu_save_image)).perform(click()); + onView(withText(R.string.dialog_save_image_name)).check(matches(isDisplayed())); + } + + @Test + public void testSaveDialogAppearsOnSaveCopyClick() { + onView(withText(R.string.menu_save_copy)).perform(click()); + onView(withText(R.string.dialog_save_image_name)).check(matches(isDisplayed())); + } + + @Test + public void testSaveDialogSavesChanges() { + onView(withText(R.string.menu_save_image)).perform(click()); + + onView(withId(R.id.pocketpaint_save_info_title)).check(matches(isDisplayed())); + onView(withId(R.id.pocketpaint_image_name_save_text)).check(matches(isDisplayed())); + onView(withId(R.id.pocketpaint_save_dialog_spinner)).check(matches(isDisplayed())); + + onView(withId(R.id.pocketpaint_save_dialog_spinner)) + .perform(click()); + onData(allOf(is(instanceOf(String.class)), + is("png"))).inRoot(isPlatformPopup()).perform(click()); + onView(withId(R.id.pocketpaint_image_name_save_text)) + .perform(replaceText(defaultPictureName)); + + onView(withText(R.string.save_button_text)) + .perform(click()); + pressBack(); + + onTopBarView() + .performOpenMoreOptions(); + + onView(withText(R.string.menu_save_image)).perform(click()); + + onView(withText("png")).check(matches(isDisplayed())); + onView(withText(defaultPictureName)).check(matches(isDisplayed())); + } + + @Test + public void testShowRateUsDialogOnLikeUsDialogPositiveButtonPressed() { + onView(withText(R.string.menu_save_image)).perform(click()); + onView(withId(R.id.pocketpaint_image_name_save_text)) + .perform(replaceText("1")); + onView(withText(R.string.save_button_text)).perform(click()); + onView(withText(R.string.pocketpaint_yes)).perform(click()); + onView(withText(R.string.pocketpaint_rate_us)).check(matches(isDisplayed())); + } + + @Test + public void testShowFeedbackDialogOnLikeUsDialogNegativeButtonPressed() { + onView(withText(R.string.menu_save_image)).perform(click()); + onView(withId(R.id.pocketpaint_image_name_save_text)) + .perform(replaceText("12")); + onView(withText(R.string.save_button_text)).perform(click()); + onView(withText(R.string.pocketpaint_no)).perform(click()); + onView(withText(R.string.pocketpaint_feedback)).check(matches(isDisplayed())); + } + + @Test + public void testLikeUsDialogNotShownOnSecondSave() { + onView(withText(R.string.menu_save_image)).perform(click()); + onView(withId(R.id.pocketpaint_image_name_save_text)) + .perform(replaceText("123")); + onView(withText(R.string.save_button_text)).perform(click()); + + onView(withText(R.string.pocketpaint_like_us)).check(matches(isDisplayed())); + pressBack(); + + onTopBarView() + .performOpenMoreOptions(); + + onView(withText(R.string.menu_save_image)).perform(click()); + onView(withText(R.string.pocketpaint_like_us)).check(doesNotExist()); + onView(withText(R.string.save_button_text)).perform(click()); + } + + @Test + public void testOnOffSmoothOptions() { + onView(withText(R.string.menu_advanced)).perform(click()); + onView(withId(R.id.pocketpaint_smoothing)).perform(click()); + + onView(withText(R.string.pocketpaint_ok)).perform(click()); + + assertFalse("Smoothing is still on!", AdvancedSettingsAlgorithms.smoothing); + + onTopBarView() + .performOpenMoreOptions(); + + onView(withText(R.string.menu_advanced)).perform(click()); + onView(withId(R.id.pocketpaint_smoothing)).perform(click()); + + onView(withText(R.string.pocketpaint_ok)).perform(click()); + + assertTrue("Smoothing is still off!", AdvancedSettingsAlgorithms.smoothing); + } + + @Test + public void testNoChangeOnSmoothingWhenCancel() { + onView(withText(R.string.menu_advanced)).perform(click()); + onView(withId(R.id.pocketpaint_smoothing)).perform(click()); + + onView(withText(R.string.cancel_button_text)).perform(click()); + assertTrue("Smoothing is off after cancel!", AdvancedSettingsAlgorithms.smoothing); + } +} diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/OraFileFormatIntegrationTest.kt b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/OraFileFormatIntegrationTest.kt new file mode 100644 index 0000000000..d6bf48c3e0 --- /dev/null +++ b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/OraFileFormatIntegrationTest.kt @@ -0,0 +1,190 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2021 The Catrobat Team + * () + * + * 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. + * + * 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 . + */ +package org.catrobat.paintroid.test.espresso + +import android.app.Activity +import android.app.Instrumentation.ActivityResult +import android.content.Intent +import android.net.Uri +import androidx.test.espresso.Espresso.onData +import androidx.test.espresso.Espresso.onView +import androidx.test.espresso.action.ViewActions +import androidx.test.espresso.assertion.ViewAssertions +import androidx.test.espresso.intent.Intents +import androidx.test.espresso.intent.matcher.IntentMatchers +import androidx.test.espresso.intent.rule.IntentsTestRule +import androidx.test.espresso.matcher.RootMatchers +import androidx.test.espresso.matcher.ViewMatchers.isDisplayed +import androidx.test.espresso.matcher.ViewMatchers.withId +import androidx.test.espresso.matcher.ViewMatchers.withText +import androidx.test.espresso.matcher.ViewMatchers.isRoot +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.rule.GrantPermissionRule +import kotlinx.coroutines.delay +import kotlinx.coroutines.runBlocking +import org.catrobat.paintroid.MainActivity +import org.catrobat.paintroid.R +import org.catrobat.paintroid.test.espresso.util.DrawingSurfaceLocationProvider +import org.catrobat.paintroid.test.espresso.util.EspressoUtils.grantPermissionRulesVersionCheck +import org.catrobat.paintroid.test.espresso.util.UiInteractions.touchAt +import org.catrobat.paintroid.test.espresso.util.UiInteractions.waitFor +import org.catrobat.paintroid.test.espresso.util.wrappers.DrawingSurfaceInteraction.onDrawingSurfaceView +import org.catrobat.paintroid.test.espresso.util.wrappers.LayerMenuViewInteraction +import org.catrobat.paintroid.test.espresso.util.wrappers.TopBarViewInteraction.onTopBarView +import org.catrobat.paintroid.test.utils.ScreenshotOnFailRule +import org.hamcrest.Matchers.instanceOf +import org.hamcrest.Matchers.`is` +import org.hamcrest.core.AllOf +import org.junit.After +import org.junit.Assert.assertNotNull +import org.junit.Assert.assertTrue +import org.junit.Before +import org.junit.ClassRule +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import java.io.File + +@RunWith(AndroidJUnit4::class) +class OraFileFormatIntegrationTest { + @get:Rule + var launchActivityRule = IntentsTestRule(MainActivity::class.java) + + @get:Rule + var screenshotOnFailRule = ScreenshotOnFailRule() + private lateinit var activity: MainActivity + + companion object { + private lateinit var deletionFileList: ArrayList + + @get:ClassRule + var grantPermissionRule: GrantPermissionRule = grantPermissionRulesVersionCheck() + } + + @Before + fun setUp() { + deletionFileList = ArrayList() + activity = launchActivityRule.activity + } + + @After + fun tearDown() { + for (file in deletionFileList) { + if (file != null && file.exists()) { + assertTrue(file.delete()) + } + } + } + + @Test + fun testSaveAsOraFile() { + onDrawingSurfaceView().perform(touchAt(DrawingSurfaceLocationProvider.MIDDLE)) + onTopBarView().performOpenMoreOptions() + onView(withText(R.string.menu_save_image)).perform(ViewActions.click()) + onView(withId(R.id.pocketpaint_save_dialog_spinner)).perform(ViewActions.click()) + onData(AllOf.allOf(`is`(instanceOf(String::class.java)), `is`("ora"))) + .inRoot(RootMatchers.isPlatformPopup()).perform(ViewActions.click()) + onView(withId(R.id.pocketpaint_image_name_save_text)) + .perform(ViewActions.replaceText("test1337")) + runBlocking { + onView(withText(R.string.save_button_text)).perform(ViewActions.click()) + delay(500) + } + assertNotNull(activity.model.savedPictureUri) + addUriToDeletionFileList(activity.model.savedPictureUri) + } + + @Test + fun testSaveAndOverrideOraFile() { + onDrawingSurfaceView().perform(touchAt(DrawingSurfaceLocationProvider.MIDDLE)) + onTopBarView().performOpenMoreOptions() + onView(withText(R.string.menu_save_image)).perform(ViewActions.click()) + onView(withId(R.id.pocketpaint_save_dialog_spinner)).perform(ViewActions.click()) + onData(AllOf.allOf(`is`(instanceOf(String::class.java)), `is`("ora"))) + .inRoot(RootMatchers.isPlatformPopup()).perform(ViewActions.click()) + onView(withId(R.id.pocketpaint_image_name_save_text)) + .perform(ViewActions.replaceText("OraOverride")) + onView(withText(R.string.save_button_text)).perform(ViewActions.click()) + onView(isRoot()).perform(waitFor(100)) + onView(withText(R.string.pocketpaint_no)).perform(ViewActions.click()) + onView(withText(R.string.pocketpaint_ok)).perform(ViewActions.click()) + onDrawingSurfaceView().perform(touchAt(DrawingSurfaceLocationProvider.BOTTOM_MIDDLE)) + onTopBarView().performOpenMoreOptions() + onView(withText(R.string.menu_save_image)).perform(ViewActions.click()) + onView(withText(R.string.save_button_text)).perform(ViewActions.click()) + onView(withText(R.string.pocketpaint_overwrite_title)).check( + ViewAssertions.matches( + isDisplayed() + ) + ) + + onView(withText(R.string.overwrite_button_text)).perform(ViewActions.click()) + onView(isRoot()).perform(waitFor(500)) + + val imageUri = activity.model.savedPictureUri + assertNotNull(imageUri) + addUriToDeletionFileList(imageUri) + } + + @Test + fun testOraFileWithMultipleLayersSaveAndLoad() { + onDrawingSurfaceView().perform(touchAt(DrawingSurfaceLocationProvider.MIDDLE)) + LayerMenuViewInteraction.onLayerMenuView() + .performOpen() + .performAddLayer() + .checkLayerCount(2) + .performClose() + onDrawingSurfaceView().perform(touchAt(DrawingSurfaceLocationProvider.TOP_MIDDLE)) + LayerMenuViewInteraction.onLayerMenuView() + .performOpen() + .performAddLayer() + .checkLayerCount(3) + .performClose() + onDrawingSurfaceView().perform(touchAt(DrawingSurfaceLocationProvider.BOTTOM_MIDDLE)) + onTopBarView().performOpenMoreOptions() + onView(withText(R.string.menu_save_image)).perform(ViewActions.click()) + onView(withId(R.id.pocketpaint_save_dialog_spinner)).perform(ViewActions.click()) + onData(AllOf.allOf(`is`(instanceOf(String::class.java)), `is`("ora"))) + .inRoot(RootMatchers.isPlatformPopup()).perform(ViewActions.click()) + onView(withId(R.id.pocketpaint_image_name_save_text)) + .perform(ViewActions.replaceText("MoreLayersOraTest")) + + onView(withText(R.string.save_button_text)).perform(ViewActions.click()) + onView(isRoot()).perform(waitFor(1000)) + + val fileUri = activity.model.savedPictureUri + assertNotNull(fileUri) + addUriToDeletionFileList(fileUri) + val intent = Intent() + intent.data = fileUri + val resultOK = ActivityResult(Activity.RESULT_OK, intent) + Intents.intending(IntentMatchers.hasAction(Intent.ACTION_GET_CONTENT)).respondWith(resultOK) + onTopBarView().performOpenMoreOptions() + onView(withText(R.string.menu_load_image)).perform(ViewActions.click()) + LayerMenuViewInteraction.onLayerMenuView() + .performOpen() + .checkLayerCount(3) + } + + private fun addUriToDeletionFileList(uri: Uri?) { + uri?.path?.let { + deletionFileList.add(File(it)) + } + } +} diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/OraFileIntentTest.java b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/OraFileIntentTest.java new file mode 100644 index 0000000000..eb1cb39365 --- /dev/null +++ b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/OraFileIntentTest.java @@ -0,0 +1,152 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2021 The Catrobat Team + * () + * + * 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. + * + * 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 . + */ + +package org.catrobat.paintroid.test.espresso; + +import android.content.ContentResolver; +import android.content.ContentValues; +import android.content.Intent; +import android.graphics.Bitmap; +import android.net.Uri; +import android.os.Build; +import android.os.Bundle; +import android.os.Environment; +import android.provider.MediaStore; +import android.util.Log; + +import org.catrobat.paintroid.FileIO; +import org.catrobat.paintroid.MainActivity; +import org.catrobat.paintroid.tools.ToolType; +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.io.File; +import java.io.IOException; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.Objects; + +import androidx.test.ext.junit.runners.AndroidJUnit4; +import androidx.test.rule.ActivityTestRule; + +import static org.catrobat.paintroid.test.espresso.util.wrappers.ToolBarViewInteraction.onToolBarView; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +@RunWith(AndroidJUnit4.class) +public class OraFileIntentTest { + private static ArrayList deletionFileList = null; + @Rule + public ActivityTestRule launchActivityRule = new ActivityTestRule<>(MainActivity.class); + private ContentResolver resolver; + + @Before + public void setUp() { + onToolBarView().performSelectTool(ToolType.BRUSH); + deletionFileList = new ArrayList<>(); + resolver = launchActivityRule.getActivity().getContentResolver(); + } + + @After + public void tearDown() { + for (File file : deletionFileList) { + if (file != null && file.exists()) { + assertTrue(file.delete()); + } + } + } + + @Test + public void testCheckIntentForOraFile() { + Intent intent = new Intent(); + Uri receivedUri = createTestImageFile(); + Bitmap receivedBitmap = null; + + try { + receivedBitmap = FileIO.getBitmapFromUri(resolver, receivedUri, launchActivityRule.getActivity().getBaseContext()); + } catch (Exception e) { + Log.e("Can't Read", "Can't get Bitmap from File"); + } + + Objects.requireNonNull(receivedBitmap); + intent.setAction(Intent.ACTION_EDIT); + intent.setData(receivedUri); + intent.setType("image/*"); + intent.putExtra(Intent.EXTRA_STREAM, receivedUri); + + launchActivityRule.launchActivity(intent); + Intent mainActivityIntent = launchActivityRule.getActivity().getIntent(); + + String intentAction = intent.getAction(); + String intentType = intent.getType(); + Bundle intentBundle = intent.getExtras(); + Objects.requireNonNull(intentBundle); + Uri intentUri = (Uri) intentBundle.get(Intent.EXTRA_STREAM); + + String mainActivityIntentAction = mainActivityIntent.getAction(); + String mainActivityIntentType = mainActivityIntent.getType(); + Bundle mainActivityIntentBundle = mainActivityIntent.getExtras(); + Objects.requireNonNull(mainActivityIntentBundle); + Uri mainActivityIntentUri = (Uri) mainActivityIntentBundle.get(Intent.EXTRA_STREAM); + Bitmap mainActivityIntentBitmap = null; + Objects.requireNonNull(mainActivityIntentUri); + + try { + mainActivityIntentBitmap = FileIO.getBitmapFromUri(resolver, mainActivityIntentUri, launchActivityRule.getActivity().getBaseContext()); + } catch (Exception e) { + Log.e("Can't read", "Can't get Bitmap From File"); + } + + Objects.requireNonNull(mainActivityIntentBitmap); + + assertEquals(intentAction, mainActivityIntentAction); + assertEquals(intentType, mainActivityIntentType); + assertEquals(intentUri, mainActivityIntentUri); + assertEquals(receivedBitmap.getWidth(), mainActivityIntentBitmap.getWidth()); + assertEquals(receivedBitmap.getHeight(), mainActivityIntentBitmap.getHeight()); + } + + private Uri createTestImageFile() { + Bitmap bitmap = Bitmap.createBitmap(400, 400, Bitmap.Config.ARGB_8888); + + ContentValues contentValues = new ContentValues(); + contentValues.put(MediaStore.Images.Media.DISPLAY_NAME, "testfile.ora"); + contentValues.put(MediaStore.Images.Media.MIME_TYPE, "image/*"); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + contentValues.put(MediaStore.Images.Media.RELATIVE_PATH, Environment.DIRECTORY_PICTURES); + } + + Uri imageUri = resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues); + try { + OutputStream fos = resolver.openOutputStream(Objects.requireNonNull(imageUri)); + assertTrue(bitmap.compress(Bitmap.CompressFormat.PNG, 100, fos)); + assert fos != null; + fos.close(); + } catch (IOException e) { + throw new AssertionError("Picture file could not be created.", e); + } + + File imageFile = new File(imageUri.getPath(), "testfile.ora"); + deletionFileList.add(imageFile); + return imageUri; + } +} diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/SaveCompressImageIntegrationTest.java b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/SaveCompressImageIntegrationTest.java new file mode 100644 index 0000000000..246c3b6d74 --- /dev/null +++ b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/SaveCompressImageIntegrationTest.java @@ -0,0 +1,162 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2022 The Catrobat Team + * () + * + * 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. + * + * 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 . + */ + +package org.catrobat.paintroid.test.espresso; + +import android.app.Activity; +import android.app.Instrumentation; +import android.content.Intent; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.net.Uri; + +import org.catrobat.paintroid.FileIO; +import org.catrobat.paintroid.MainActivity; +import org.catrobat.paintroid.R; +import org.catrobat.paintroid.test.utils.ScreenshotOnFailRule; +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.Objects; +import java.util.Random; +import java.util.UUID; + +import androidx.test.espresso.intent.rule.IntentsTestRule; +import androidx.test.rule.ActivityTestRule; + +import static org.catrobat.paintroid.test.espresso.util.UiInteractions.waitFor; +import static org.catrobat.paintroid.test.espresso.util.wrappers.TopBarViewInteraction.onTopBarView; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.instanceOf; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.core.AllOf.allOf; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; + +import static androidx.test.espresso.Espresso.onData; +import static androidx.test.espresso.Espresso.onView; +import static androidx.test.espresso.action.ViewActions.click; +import static androidx.test.espresso.action.ViewActions.replaceText; +import static androidx.test.espresso.intent.Intents.intending; +import static androidx.test.espresso.intent.matcher.IntentMatchers.hasAction; +import static androidx.test.espresso.matcher.RootMatchers.isPlatformPopup; +import static androidx.test.espresso.matcher.ViewMatchers.isRoot; +import static androidx.test.espresso.matcher.ViewMatchers.withId; +import static androidx.test.espresso.matcher.ViewMatchers.withText; + +public class SaveCompressImageIntegrationTest { + + @Rule + public ActivityTestRule activityTestRule = new IntentsTestRule<>(MainActivity.class); + + @Rule + public ScreenshotOnFailRule screenshotOnFailRule = new ScreenshotOnFailRule(); + + private File testImageFile; + private static ArrayList deletionFileList = null; + private MainActivity activity; + + @Before + public void setUp() { + try { + activity = activityTestRule.getActivity(); + testImageFile = File.createTempFile("PocketPaintTest", ".jpg"); + deletionFileList = new ArrayList<>(); + deletionFileList.add(testImageFile); + Bitmap bitmap = createTestBitmap(); + OutputStream outputStream = new FileOutputStream(testImageFile); + assertTrue(bitmap.compress(Bitmap.CompressFormat.PNG, 100, outputStream)); + outputStream.close(); + } catch (IOException e) { + throw new AssertionError("Could not create temp file", e); + } + + Intent intent = new Intent(); + intent.setData(Uri.fromFile(testImageFile)); + Instrumentation.ActivityResult resultOK = new Instrumentation.ActivityResult(Activity.RESULT_OK, intent); + intending(hasAction(Intent.ACTION_GET_CONTENT)).respondWith(resultOK); + } + + @After + public void tearDown() { + for (File file : deletionFileList) { + if (file != null && file.exists()) { + assertTrue(file.delete()); + } + } + } + + @Test + public void testSaveImage() throws IOException { + String testName = UUID.randomUUID().toString(); + onTopBarView() + .performOpenMoreOptions(); + onView(withText(R.string.menu_load_image)).perform(click()); + + onTopBarView() + .performOpenMoreOptions(); + onView(withText(R.string.menu_save_image)).perform(click()); + onView(withId(R.id.pocketpaint_image_name_save_text)).perform(replaceText(testName)); + onView(withId(R.id.pocketpaint_save_dialog_spinner)).perform(click()); + onData(allOf(is(instanceOf(String.class)), is("jpg"))).inRoot(isPlatformPopup()).perform(click()); + onView(withText(R.string.save_button_text)).perform(click()); + onView(isRoot()).perform(waitFor(100)); + BitmapFactory.Options options = new BitmapFactory.Options(); + options.inMutable = true; + Bitmap compressedBitmap = FileIO.INSTANCE.decodeBitmapFromUri(this.activity.getContentResolver(), Objects.requireNonNull(activity.model.getSavedPictureUri()), options, this.activity.getApplicationContext()); + Bitmap testBitmap = FileIO.INSTANCE.getBitmapFromFile(testImageFile); + assertThat(compressedBitmap.getWidth(), is(equalTo(testBitmap.getWidth()))); + assertThat(compressedBitmap.getHeight(), is(equalTo(testBitmap.getHeight()))); + } + + private Bitmap createTestBitmap() { + Bitmap bitmap; + int width = 720; + int height = 1280; + Bitmap.Config bitmapConfig = Bitmap.Config.ARGB_8888; + int bytesPerPixel = 4; + + byte[] b = new byte[width * height * bytesPerPixel]; + Random r = new Random(); + r.setSeed(0); + r.nextBytes(b); + bitmap = Bitmap.createBitmap(width, height, bitmapConfig); + Canvas canvas = new Canvas(bitmap); + int byteIndex = 0; + Paint paint = new Paint(); + for (int i = 0; i < height; i++) { + for (int j = 0; j < width; j++) { + int color = Color.argb(b[byteIndex++], b[byteIndex++], b[byteIndex++], b[byteIndex++]); + paint.setColor(color); + canvas.drawPoint(j, i, paint); + } + } + return bitmap; + } +} diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/SaveImageProgressBarTest.kt b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/SaveImageProgressBarTest.kt new file mode 100644 index 0000000000..19f4dac720 --- /dev/null +++ b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/SaveImageProgressBarTest.kt @@ -0,0 +1,89 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2021 The Catrobat Team + * () + * + * 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. + * + * 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 . + */ +package org.catrobat.paintroid.test.espresso + +import android.os.SystemClock +import androidx.core.widget.ContentLoadingProgressBar +import androidx.test.espresso.Espresso.onView +import androidx.test.espresso.action.ViewActions +import androidx.test.espresso.action.ViewActions.replaceText +import androidx.test.espresso.assertion.ViewAssertions +import androidx.test.espresso.matcher.ViewMatchers +import androidx.test.espresso.matcher.ViewMatchers.withId +import androidx.test.espresso.matcher.ViewMatchers.withText +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.rule.ActivityTestRule +import org.catrobat.paintroid.MainActivity +import org.catrobat.paintroid.R +import org.catrobat.paintroid.test.espresso.util.wrappers.TopBarViewInteraction +import org.hamcrest.Matchers +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith + +@RunWith(AndroidJUnit4::class) +class SaveImageProgressBarTest { + + @get:Rule + val launchActivityRule = ActivityTestRule(MainActivity::class.java) + + private lateinit var activity: MainActivity + + companion object { + private const val IMAGE_NAME = "fileName" + } + @Before + fun setUp() { + activity = launchActivityRule.activity + } + + @Test + fun testProgressBarShown() { + val progressBar = activity.findViewById(R.id.pocketpaint_content_loading_progress_bar) + progressBar.show() + SystemClock.sleep(501) + onView(withId(R.id.pocketpaint_content_loading_progress_bar)) + .check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + progressBar.hide() + } + + @Test + fun testProgressBarNotShown() { + val progressBar = activity.findViewById(R.id.pocketpaint_content_loading_progress_bar) + progressBar.show() + onView(withId(R.id.pocketpaint_content_loading_progress_bar)) + .check(ViewAssertions.matches(Matchers.not(ViewMatchers.isDisplayed()))) + progressBar.hide() + } + + @Test + fun testProgressBarNotShownWhenSaving() { + TopBarViewInteraction.onTopBarView() + .performOpenMoreOptions() + onView(withText(R.string.menu_save_image)) + .perform(ViewActions.click()) + onView(withId(R.id.pocketpaint_image_name_save_text)) + .perform(replaceText(IMAGE_NAME)) + onView(withText(R.string.save_button_text)) + .perform(ViewActions.click()) + onView(withId(R.id.pocketpaint_content_loading_progress_bar)) + .check(ViewAssertions.matches(Matchers.not(ViewMatchers.isDisplayed()))) + } +} diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/ScrollingViewIntegrationTest.java b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/ScrollingViewIntegrationTest.java new file mode 100644 index 0000000000..886bdd5c8c --- /dev/null +++ b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/ScrollingViewIntegrationTest.java @@ -0,0 +1,334 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2015 The Catrobat Team + * () + * + * 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. + * + * 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 . + */ + +package org.catrobat.paintroid.test.espresso; + +import android.content.res.TypedArray; +import android.graphics.PointF; + +import org.catrobat.paintroid.MainActivity; +import org.catrobat.paintroid.test.espresso.util.UiInteractions; +import org.catrobat.paintroid.test.utils.ScreenshotOnFailRule; +import org.catrobat.paintroid.tools.ToolType; +import org.catrobat.paintroid.ui.Perspective; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import androidx.test.ext.junit.runners.AndroidJUnit4; +import androidx.test.rule.ActivityTestRule; + +import static org.catrobat.paintroid.test.espresso.util.UiInteractions.touchCenterMiddle; +import static org.catrobat.paintroid.test.espresso.util.UiInteractions.touchLongAt; +import static org.catrobat.paintroid.test.espresso.util.wrappers.ToolBarViewInteraction.onToolBarView; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertTrue; + +import static androidx.test.espresso.Espresso.onView; +import static androidx.test.espresso.matcher.ViewMatchers.isRoot; + +@RunWith(AndroidJUnit4.class) +public class ScrollingViewIntegrationTest { + @Rule + public ActivityTestRule launchActivityRule = new ActivityTestRule<>(MainActivity.class); + + @Rule + public ScreenshotOnFailRule screenshotOnFailRule = new ScreenshotOnFailRule(); + + private int drawerEdgeSize; + private Perspective perspective; + private MainActivity mainActivity; + + @Before + public void setUp() { + MainActivity activity = launchActivityRule.getActivity(); + float displayDensity = activity.getResources().getDisplayMetrics().density; + perspective = activity.perspective; + drawerEdgeSize = (int) (20 * displayDensity + 0.5f); + mainActivity = launchActivityRule.getActivity(); + } + + @Test + public void testScrollingViewDrawTool() { + onToolBarView().performCloseToolOptionsView(); + final int perspectiveScale = 5; + perspective.setScale(perspectiveScale); + + float surfaceWidth = perspective.surfaceWidth; + float surfaceHeight = perspective.surfaceHeight; + + float xRight = surfaceWidth - 1 - drawerEdgeSize; + float xLeft = 1 + drawerEdgeSize; + float xMiddle = surfaceWidth / 2; + + int statusBarHeight = 0; + int resourceId = mainActivity.getResources().getIdentifier("status_bar_height", "dimen", "android"); + if (resourceId > 0) { + statusBarHeight = mainActivity.getResources().getDimensionPixelSize(resourceId); + } + + int actionBarHeight; + final TypedArray styledAttributes = mainActivity.getTheme().obtainStyledAttributes( + new int[] {android.R.attr.actionBarSize} + ); + actionBarHeight = (int) styledAttributes.getDimension(0, 0); + + float yMiddle = surfaceHeight / 2 + actionBarHeight + statusBarHeight; + float yTop = actionBarHeight + statusBarHeight; + float yBottom = surfaceHeight + yTop - 1; + + PointF middle = new PointF(xMiddle, yMiddle); + PointF rightMiddle = new PointF(xRight, yMiddle); + PointF leftMiddle = new PointF(xLeft, yMiddle); + PointF topMiddle = new PointF(xMiddle, yTop); + PointF bottomMiddle = new PointF(xMiddle, yBottom); + PointF topLeft = new PointF(xLeft, yTop); + PointF bottomRight = new PointF(xRight, yBottom); + PointF bottomLeft = new PointF(xLeft, yBottom); + PointF topRight = new PointF(xRight, yTop); + + onToolBarView() + .performSelectTool(ToolType.BRUSH); + + longpressOnPointAndCheckIfCanvasPointHasNotChanged(middle); + + longpressOnPointAndCheckIfCanvasPointHasChangedInXOrY(rightMiddle); + longpressOnPointAndCheckIfCanvasPointHasChangedInXOrY(leftMiddle); + longpressOnPointAndCheckIfCanvasPointHasChangedInXOrY(topMiddle); + longpressOnPointAndCheckIfCanvasPointHasChangedInXOrY(bottomMiddle); + + longpressOnPointAndCheckIfCanvasPointHasChangedInXAndY(bottomRight); + longpressOnPointAndCheckIfCanvasPointHasChangedInXAndY(topLeft); + longpressOnPointAndCheckIfCanvasPointHasChangedInXAndY(bottomLeft); + longpressOnPointAndCheckIfCanvasPointHasChangedInXAndY(topRight); + } + + @Test + public void testScrollingViewCursorTool() { + final int perspectiveScale = 5; + perspective.setScale(perspectiveScale); + onToolBarView().performCloseToolOptionsView(); + float surfaceWidth = perspective.surfaceWidth; + float surfaceHeight = perspective.surfaceHeight; + + int statusBarHeight = 0; + int resourceId = mainActivity.getResources().getIdentifier("status_bar_height", "dimen", "android"); + if (resourceId > 0) { + statusBarHeight = mainActivity.getResources().getDimensionPixelSize(resourceId); + } + + int actionBarHeight; + final TypedArray styledAttributes = mainActivity.getTheme().obtainStyledAttributes( + new int[] {android.R.attr.actionBarSize} + ); + actionBarHeight = (int) styledAttributes.getDimension(0, 0); + + float xRight = surfaceWidth - 100; + float xLeft = 1; + float xMiddle = surfaceWidth / 2; + + float yMiddle = surfaceHeight / 2 + actionBarHeight + statusBarHeight; + float yTop = actionBarHeight + statusBarHeight; + float yBottom = surfaceHeight + yTop - 1; + + PointF middle = new PointF(xMiddle, yMiddle); + PointF rightMiddle = new PointF(xRight, yMiddle); + PointF leftMiddle = new PointF(xLeft, yMiddle); + PointF topMiddle = new PointF(xMiddle, yTop); + PointF bottomMiddle = new PointF(xMiddle, yBottom); + PointF topLeft = new PointF(xLeft, yTop); + PointF bottomRight = new PointF(xRight, yBottom); + PointF bottomLeft = new PointF(xLeft, yBottom); + PointF topRight = new PointF(xRight, yTop); + + onToolBarView() + .performSelectTool(ToolType.CURSOR); + onToolBarView().performCloseToolOptionsView(); + longpressOnPointAndCheckIfCanvasPointHasNotChanged(rightMiddle); + longpressOnPointAndCheckIfCanvasPointHasNotChanged(leftMiddle); + longpressOnPointAndCheckIfCanvasPointHasNotChanged(topMiddle); + longpressOnPointAndCheckIfCanvasPointHasNotChanged(bottomMiddle); + longpressOnPointAndCheckIfCanvasPointHasNotChanged(bottomRight); + longpressOnPointAndCheckIfCanvasPointHasNotChanged(topLeft); + longpressOnPointAndCheckIfCanvasPointHasNotChanged(bottomLeft); + longpressOnPointAndCheckIfCanvasPointHasNotChanged(topRight); + + dragAndCheckIfCanvasHasMovedInXOrY(bottomMiddle, topMiddle); + dragAndCheckIfCanvasHasMovedInXOrY(topMiddle, middle); + dragAndCheckIfCanvasHasMovedInXOrY(topMiddle, bottomMiddle); + dragAndCheckIfCanvasHasMovedInXOrY(bottomMiddle, middle); + + onView(isRoot()).perform(touchCenterMiddle()); + } + + public void longpressOnPointAndCheckIfCanvasPointHasChangedInXAndY(PointF clickPoint) { + int statusBarHeight = 0; + int resourceId = mainActivity.getResources().getIdentifier("status_bar_height", "dimen", "android"); + if (resourceId > 0) { + statusBarHeight = mainActivity.getResources().getDimensionPixelSize(resourceId); + } + + int actionBarHeight; + final TypedArray styledAttributes = mainActivity.getTheme().obtainStyledAttributes( + new int[] {android.R.attr.actionBarSize} + ); + actionBarHeight = (int) styledAttributes.getDimension(0, 0); + + PointF startPointSurface = new PointF(perspective.surfaceCenterX, perspective.surfaceCenterY + actionBarHeight + statusBarHeight); + + PointF startPointCanvas = perspective.getCanvasPointFromSurfacePoint(startPointSurface); + + onView(isRoot()).perform(touchLongAt(clickPoint.x, clickPoint.y)); + + PointF endPointCanvas = perspective.getCanvasPointFromSurfacePoint(startPointSurface); + + float delta = 0.5f; + assertNotEquals("view should scroll in x", startPointCanvas.x, endPointCanvas.x, delta); + assertNotEquals("view should scroll in y", startPointCanvas.y, endPointCanvas.y, delta); + } + + public void longpressOnPointAndCheckIfCanvasPointHasChangedInXOrY(PointF clickPoint) { + int statusBarHeight = 0; + int resourceId = mainActivity.getResources().getIdentifier("status_bar_height", "dimen", "android"); + if (resourceId > 0) { + statusBarHeight = mainActivity.getResources().getDimensionPixelSize(resourceId); + } + + int actionBarHeight; + final TypedArray styledAttributes = mainActivity.getTheme().obtainStyledAttributes( + new int[] {android.R.attr.actionBarSize} + ); + actionBarHeight = (int) styledAttributes.getDimension(0, 0); + + PointF startPointSurface = new PointF(perspective.surfaceCenterX, perspective.surfaceCenterY + actionBarHeight + statusBarHeight); + + PointF startPointCanvas = perspective.getCanvasPointFromSurfacePoint(startPointSurface); + + onView(isRoot()).perform(touchLongAt(clickPoint.x, clickPoint.y)); + + PointF endPointCanvas = perspective.getCanvasPointFromSurfacePoint(startPointSurface); + + assertTrue("scrolling did not work", startPointCanvas.x != endPointCanvas.x || startPointCanvas.y != endPointCanvas.y); + } + + public void longpressOnPointAndCheckIfCanvasPointHasNotChanged(PointF clickPoint) { + int statusBarHeight = 0; + int resourceId = mainActivity.getResources().getIdentifier("status_bar_height", "dimen", "android"); + if (resourceId > 0) { + statusBarHeight = mainActivity.getResources().getDimensionPixelSize(resourceId); + } + + int actionBarHeight; + final TypedArray styledAttributes = mainActivity.getTheme().obtainStyledAttributes( + new int[] {android.R.attr.actionBarSize} + ); + actionBarHeight = (int) styledAttributes.getDimension(0, 0); + + PointF startPointSurface = new PointF(perspective.surfaceCenterX, perspective.surfaceCenterY + actionBarHeight + statusBarHeight); + + PointF startPointCanvas = perspective.getCanvasPointFromSurfacePoint(startPointSurface); + + onView(isRoot()).perform(touchLongAt(clickPoint.x, clickPoint.y)); + + PointF endPointCanvas = perspective.getCanvasPointFromSurfacePoint(startPointSurface); + + float delta = 0.5f; + assertEquals("view should not scroll in x", startPointCanvas.x, endPointCanvas.x, delta); + assertEquals("view should not scroll in y", startPointCanvas.y, endPointCanvas.y, delta); + } + + public void dragAndCheckIfCanvasHasMovedInXAndY(PointF fromPoint, PointF toPoint) { + int statusBarHeight = 0; + int resourceId = mainActivity.getResources().getIdentifier("status_bar_height", "dimen", "android"); + if (resourceId > 0) { + statusBarHeight = mainActivity.getResources().getDimensionPixelSize(resourceId); + } + + int actionBarHeight; + final TypedArray styledAttributes = mainActivity.getTheme().obtainStyledAttributes( + new int[] {android.R.attr.actionBarSize} + ); + actionBarHeight = (int) styledAttributes.getDimension(0, 0); + + PointF startPointSurface = new PointF(fromPoint.x, fromPoint.y + actionBarHeight + statusBarHeight); + PointF startPointCanvas = perspective.getCanvasPointFromSurfacePoint(startPointSurface); + + onView(isRoot()).perform(UiInteractions.swipe(fromPoint, toPoint)); + + PointF endPointSurface = new PointF(fromPoint.x, fromPoint.y + actionBarHeight + statusBarHeight); + PointF endPointCanvas = perspective.getCanvasPointFromSurfacePoint(endPointSurface); + + assertNotEquals("scrolling did not work in x", startPointCanvas.x, endPointCanvas.x); + assertNotEquals("scrolling did not work in y", startPointCanvas.y, endPointCanvas.y); + } + + public void dragAndCheckIfCanvasHasMovedInXOrY(PointF fromPoint, PointF toPoint) { + int statusBarHeight = 0; + int resourceId = mainActivity.getResources().getIdentifier("status_bar_height", "dimen", "android"); + if (resourceId > 0) { + statusBarHeight = mainActivity.getResources().getDimensionPixelSize(resourceId); + } + + int actionBarHeight; + final TypedArray styledAttributes = mainActivity.getTheme().obtainStyledAttributes( + new int[] {android.R.attr.actionBarSize} + ); + actionBarHeight = (int) styledAttributes.getDimension(0, 0); + + PointF startPointSurface = new PointF(fromPoint.x, fromPoint.y + actionBarHeight + statusBarHeight); + PointF startPointCanvas = perspective.getCanvasPointFromSurfacePoint(startPointSurface); + + onView(isRoot()).perform(UiInteractions.swipe(fromPoint, toPoint)); + + PointF endPointSurface = new PointF(fromPoint.x, fromPoint.y + actionBarHeight + statusBarHeight); + PointF endPointCanvas = perspective.getCanvasPointFromSurfacePoint(endPointSurface); + + String message = "startX(" + startPointCanvas.x + ") != endX(" + endPointCanvas.x + + ") || startY(" + startPointCanvas.y + ") != endY(" + endPointCanvas.y + ")"; + assertTrue(message, startPointCanvas.x != endPointCanvas.x || startPointCanvas.y != endPointCanvas.y); + } + + public void dragAndCheckIfCanvasHasNotMoved(PointF fromPoint, PointF toPoint) { + int statusBarHeight = 0; + int resourceId = mainActivity.getResources().getIdentifier("status_bar_height", "dimen", "android"); + if (resourceId > 0) { + statusBarHeight = mainActivity.getResources().getDimensionPixelSize(resourceId); + } + + int actionBarHeight; + final TypedArray styledAttributes = mainActivity.getTheme().obtainStyledAttributes( + new int[] {android.R.attr.actionBarSize} + ); + actionBarHeight = (int) styledAttributes.getDimension(0, 0); + + PointF startPointSurface = new PointF(fromPoint.x, fromPoint.y + actionBarHeight + statusBarHeight); + PointF startPointCanvas = perspective.getCanvasPointFromSurfacePoint(startPointSurface); + + onView(isRoot()).perform(UiInteractions.swipe(fromPoint, toPoint)); + + PointF endPointSurface = new PointF(fromPoint.x, fromPoint.y + actionBarHeight + statusBarHeight); + PointF endPointCanvas = perspective.getCanvasPointFromSurfacePoint(endPointSurface); + + float delta = 0.5f; + assertEquals("view should not scroll but did it in x direction", startPointCanvas.x, endPointCanvas.x, delta); + assertEquals("view should not scroll but did it in y direction", startPointCanvas.y, endPointCanvas.y, delta); + } +} diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/StatusbarIntegrationTest.java b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/StatusbarIntegrationTest.java new file mode 100644 index 0000000000..cae04493f7 --- /dev/null +++ b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/StatusbarIntegrationTest.java @@ -0,0 +1,61 @@ +/** + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2015 The Catrobat Team + * () + * + * 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. + * + * 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 . + */ + +package org.catrobat.paintroid.test.espresso; + +import org.catrobat.paintroid.MainActivity; +import org.catrobat.paintroid.R; +import org.catrobat.paintroid.test.utils.ScreenshotOnFailRule; +import org.catrobat.paintroid.tools.ToolType; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import androidx.test.ext.junit.runners.AndroidJUnit4; +import androidx.test.rule.ActivityTestRule; + +import static org.catrobat.paintroid.test.espresso.util.wrappers.ToolBarViewInteraction.onToolBarView; + +import static androidx.test.espresso.Espresso.onView; +import static androidx.test.espresso.assertion.ViewAssertions.matches; +import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed; +import static androidx.test.espresso.matcher.ViewMatchers.withId; + +@RunWith(AndroidJUnit4.class) +public class StatusbarIntegrationTest { + + @Rule + public ActivityTestRule launchActivityRule = new ActivityTestRule<>(MainActivity.class); + + @Rule + public ScreenshotOnFailRule screenshotOnFailRule = new ScreenshotOnFailRule(); + + @Before + public void setUp() { + onToolBarView() + .performSelectTool(ToolType.BRUSH); + } + + @Test + public void statusBarButtonsShouldAllBeVisible() { + onView(withId(R.id.pocketpaint_btn_top_undo)).check(matches(isDisplayed())); + onView(withId(R.id.pocketpaint_btn_top_redo)).check(matches(isDisplayed())); + } +} diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/TemporaryFileSavingTest.kt b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/TemporaryFileSavingTest.kt new file mode 100644 index 0000000000..64fa7e2ffc --- /dev/null +++ b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/TemporaryFileSavingTest.kt @@ -0,0 +1,139 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2022 The Catrobat Team + * () + * + * 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. + * + * 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 . + */ +package org.catrobat.paintroid.test.espresso + +import android.content.Intent +import android.graphics.Color +import androidx.test.espresso.Espresso +import androidx.test.espresso.action.ViewActions +import androidx.test.espresso.matcher.ViewMatchers +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.rule.ActivityTestRule +import org.catrobat.paintroid.MainActivity +import org.catrobat.paintroid.R +import org.catrobat.paintroid.common.TEMP_IMAGE_PATH +import org.catrobat.paintroid.test.espresso.util.BitmapLocationProvider +import org.catrobat.paintroid.test.espresso.util.DrawingSurfaceLocationProvider +import org.catrobat.paintroid.test.espresso.util.UiInteractions +import org.catrobat.paintroid.test.espresso.util.wrappers.DrawingSurfaceInteraction.onDrawingSurfaceView +import org.catrobat.paintroid.test.espresso.util.wrappers.ToolBarViewInteraction +import org.catrobat.paintroid.test.espresso.util.wrappers.TopBarViewInteraction +import org.catrobat.paintroid.tools.ToolReference +import org.catrobat.paintroid.tools.ToolType +import org.catrobat.paintroid.tools.Workspace +import org.catrobat.paintroid.ui.Perspective +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import java.io.File + +private const val THREAD_WAITING_TIME: Long = 2500 +private const val THREAD_SHORT_WAITING_TIME: Long = 1000 + +@RunWith(AndroidJUnit4::class) +class TemporaryFileSavingTest { + + @get:Rule + val launchActivityRule = ActivityTestRule(MainActivity::class.java) + + private lateinit var workspace: Workspace + private lateinit var perspective: Perspective + private lateinit var toolReference: ToolReference + private lateinit var mainActivity: MainActivity + private lateinit var intent: Intent + + @Before + fun setUp() { + ToolBarViewInteraction.onToolBarView() + .performSelectTool(ToolType.BRUSH) + intent = Intent().putExtra("isTemporaryFileSavingTest", true) + mainActivity = launchActivityRule.launchActivity(intent) + workspace = mainActivity.workspace + perspective = mainActivity.perspective + toolReference = mainActivity.toolReference + val file = File(mainActivity.filesDir, TEMP_IMAGE_PATH) + if (file.exists()) { + file.delete() + } + } + + @Test + fun testOneUserInteraction() { + onDrawingSurfaceView() + .perform(UiInteractions.touchAt(DrawingSurfaceLocationProvider.MIDDLE)) + Thread.sleep(THREAD_WAITING_TIME) + launchActivityRule.finishActivity() + launchActivityRule.launchActivity(intent) + onDrawingSurfaceView() + .checkPixelColor(Color.BLACK, BitmapLocationProvider.MIDDLE) + } + + @Test + fun testTooShortWaitingTime() { + onDrawingSurfaceView() + .perform(UiInteractions.touchAt(DrawingSurfaceLocationProvider.MIDDLE)) + Thread.sleep(THREAD_SHORT_WAITING_TIME) + launchActivityRule.finishActivity() + launchActivityRule.launchActivity(intent) + onDrawingSurfaceView() + .checkPixelColor(Color.TRANSPARENT, BitmapLocationProvider.MIDDLE) + } + + @Test + fun testMultipleUserInteractions() { + onDrawingSurfaceView() + .perform(UiInteractions.touchAt(DrawingSurfaceLocationProvider.MIDDLE)) + onDrawingSurfaceView() + .perform(UiInteractions.touchAt(DrawingSurfaceLocationProvider.HALFWAY_TOP_LEFT)) + Thread.sleep(THREAD_WAITING_TIME) + onDrawingSurfaceView() + .perform(UiInteractions.touchAt(DrawingSurfaceLocationProvider.HALFWAY_BOTTOM_RIGHT)) + Thread.sleep(THREAD_WAITING_TIME) + onDrawingSurfaceView() + .perform(UiInteractions.touchAt(DrawingSurfaceLocationProvider.HALFWAY_BOTTOM_LEFT)) + launchActivityRule.finishActivity() + launchActivityRule.launchActivity(intent) + onDrawingSurfaceView() + .checkPixelColor(Color.BLACK, BitmapLocationProvider.MIDDLE) + onDrawingSurfaceView() + .checkPixelColor(Color.BLACK, BitmapLocationProvider.HALFWAY_TOP_LEFT) + onDrawingSurfaceView() + .checkPixelColor(Color.BLACK, BitmapLocationProvider.HALFWAY_BOTTOM_RIGHT) + onDrawingSurfaceView() + .checkPixelColor(Color.TRANSPARENT, BitmapLocationProvider.HALFWAY_BOTTOM_LEFT) + } + + @Test + fun testTempFilesDeletedAfterCreatingNewImage() { + onDrawingSurfaceView() + .perform(UiInteractions.touchAt(DrawingSurfaceLocationProvider.MIDDLE)) + Thread.sleep(THREAD_WAITING_TIME) + TopBarViewInteraction.onTopBarView() + .performOpenMoreOptions() + Espresso.onView(ViewMatchers.withText(R.string.menu_new_image)) + .perform(ViewActions.click()) + Espresso.onView(ViewMatchers.withText(R.string.discard_button_text)) + .perform(ViewActions.click()) + launchActivityRule.finishActivity() + launchActivityRule.launchActivity(intent) + onDrawingSurfaceView() + .checkPixelColor(Color.TRANSPARENT, BitmapLocationProvider.MIDDLE) + } +} diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/ToolOptionsIntegrationTest.java b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/ToolOptionsIntegrationTest.java new file mode 100644 index 0000000000..52b1f29f7b --- /dev/null +++ b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/ToolOptionsIntegrationTest.java @@ -0,0 +1,137 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2015 The Catrobat Team + * () + * + * 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. + * + * 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 . + */ + +package org.catrobat.paintroid.test.espresso; + +import android.app.Activity; +import android.app.Instrumentation; +import android.content.Intent; +import android.graphics.Bitmap; +import android.net.Uri; + +import org.catrobat.paintroid.MainActivity; +import org.catrobat.paintroid.test.utils.ScreenshotOnFailRule; +import org.catrobat.paintroid.tools.ToolType; +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.util.Arrays; + +import androidx.test.espresso.intent.rule.IntentsTestRule; +import androidx.test.rule.ActivityTestRule; + +import static org.catrobat.paintroid.test.espresso.util.wrappers.ToolBarViewInteraction.onToolBarView; +import static org.hamcrest.Matchers.not; +import static org.junit.Assert.assertTrue; +import static org.junit.runners.Parameterized.Parameter; +import static org.junit.runners.Parameterized.Parameters; + +import static androidx.test.espresso.assertion.ViewAssertions.matches; +import static androidx.test.espresso.intent.Intents.intending; +import static androidx.test.espresso.intent.matcher.IntentMatchers.hasAction; +import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed; + +@RunWith(Parameterized.class) +public class ToolOptionsIntegrationTest { + + @Rule + public ActivityTestRule activityTestRule = new IntentsTestRule<>(MainActivity.class); + + @Rule + public ScreenshotOnFailRule screenshotOnFailRule = new ScreenshotOnFailRule(); + + @Parameter + public ToolType toolType; + + @Parameter(1) + public boolean toolOptionsShownInitially; + + @Parameter(2) + public boolean hasToolOptionsView; + + private File testImageFile; + + @Parameters(name = "{0}") + public static Iterable data() { + return Arrays.asList(new Object[][]{ + {ToolType.BRUSH, true, true}, + {ToolType.SHAPE, true, true}, + {ToolType.TRANSFORM, true, true}, + {ToolType.LINE, true, true}, + {ToolType.CURSOR, true, true}, + {ToolType.FILL, true, true}, + {ToolType.PIPETTE, false, false}, + {ToolType.STAMP, true, true}, + {ToolType.ERASER, true, true}, + {ToolType.TEXT, true, true}, + {ToolType.HAND, false, false}, + {ToolType.WATERCOLOR, true, true} + }); + } + + @Before + public void setUp() { + try { + testImageFile = File.createTempFile("PocketPaintTest", ".png"); + Bitmap bitmap = Bitmap.createBitmap(25, 25, Bitmap.Config.ARGB_8888); + OutputStream outputStream = new FileOutputStream(testImageFile); + assertTrue(bitmap.compress(Bitmap.CompressFormat.JPEG, 100, outputStream)); + outputStream.close(); + } catch (IOException e) { + throw new AssertionError("Could not create temp file", e); + } + + Intent intent = new Intent(); + intent.setData(Uri.fromFile(testImageFile)); + Instrumentation.ActivityResult resultOK = new Instrumentation.ActivityResult(Activity.RESULT_OK, intent); + intending(hasAction(Intent.ACTION_GET_CONTENT)).respondWith(resultOK); + } + + @After + public void tearDown() { + assertTrue(testImageFile.delete()); + } + + @Test + public void testToolOptions() { + onToolBarView() + .performSelectTool(toolType); + + if (!toolOptionsShownInitially) { + onToolBarView() + .performOpenToolOptionsView(); + } + + if (hasToolOptionsView) { + onToolBarView().onToolOptionsView() + .check(matches(isDisplayed())); + } else { + onToolBarView().onToolOptionsView() + .check(matches(not(isDisplayed()))); + } + } +} diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/UndoRedoIntegrationTest.java b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/UndoRedoIntegrationTest.java new file mode 100644 index 0000000000..48938fb7b7 --- /dev/null +++ b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/UndoRedoIntegrationTest.java @@ -0,0 +1,369 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2022 The Catrobat Team + * () + * + * 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. + * + * 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 . + */ + +package org.catrobat.paintroid.test.espresso; + +import android.content.pm.ActivityInfo; +import android.graphics.Color; + +import org.catrobat.paintroid.MainActivity; +import org.catrobat.paintroid.R; +import org.catrobat.paintroid.test.espresso.util.BitmapLocationProvider; +import org.catrobat.paintroid.test.espresso.util.DrawingSurfaceLocationProvider; +import org.catrobat.paintroid.test.espresso.util.MainActivityHelper; +import org.catrobat.paintroid.test.espresso.util.wrappers.DrawingSurfaceInteraction; +import org.catrobat.paintroid.test.espresso.util.wrappers.LayerMenuViewInteraction; +import org.catrobat.paintroid.test.utils.ScreenshotOnFailRule; +import org.catrobat.paintroid.tools.ToolType; +import org.catrobat.paintroid.ui.Perspective; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import androidx.test.ext.junit.runners.AndroidJUnit4; +import androidx.test.rule.ActivityTestRule; + +import static org.catrobat.paintroid.test.espresso.util.UiInteractions.swipeAccurate; +import static org.catrobat.paintroid.test.espresso.util.UiInteractions.touchAt; +import static org.catrobat.paintroid.test.espresso.util.UiMatcher.withDrawable; +import static org.catrobat.paintroid.test.espresso.util.wrappers.ColorPickerViewInteraction.onColorPickerView; +import static org.catrobat.paintroid.test.espresso.util.wrappers.DrawingSurfaceInteraction.onDrawingSurfaceView; +import static org.catrobat.paintroid.test.espresso.util.wrappers.ToolBarViewInteraction.onToolBarView; +import static org.catrobat.paintroid.test.espresso.util.wrappers.TopBarViewInteraction.onTopBarView; +import static org.catrobat.paintroid.test.utils.TestUtils.selectColorInDialog; +import static org.hamcrest.Matchers.allOf; +import static org.hamcrest.Matchers.not; +import static org.junit.Assert.assertEquals; + +import static androidx.test.espresso.Espresso.onView; +import static androidx.test.espresso.action.ViewActions.click; +import static androidx.test.espresso.assertion.ViewAssertions.matches; +import static androidx.test.espresso.matcher.ViewMatchers.isEnabled; +import static androidx.test.espresso.matcher.ViewMatchers.withId; + +@RunWith(AndroidJUnit4.class) +public class UndoRedoIntegrationTest { + @Rule + public ActivityTestRule launchActivityRule = new ActivityTestRule<>(MainActivity.class); + + @Rule + public ScreenshotOnFailRule screenshotOnFailRule = new ScreenshotOnFailRule(); + + private MainActivityHelper activityHelper; + + private Perspective perspective; + + @Before + public void setUp() { + activityHelper = new MainActivityHelper(launchActivityRule.getActivity()); + + activityHelper.setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); + + perspective = launchActivityRule.getActivity().perspective; + + onToolBarView() + .performSelectTool(ToolType.BRUSH); + } + + @Test + public void testUndoRedoIconsWhenSwitchToLandscapeMode() { + assertEquals(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT, activityHelper.getScreenOrientation()); + + onTopBarView().onUndoButton() + .check(matches(allOf(withDrawable(R.drawable.ic_pocketpaint_undo_disabled), not(isEnabled())))); + onTopBarView().onRedoButton() + .check(matches(allOf(withDrawable(R.drawable.ic_pocketpaint_redo_disabled), not(isEnabled())))); + + onDrawingSurfaceView() + .perform(touchAt(DrawingSurfaceLocationProvider.HALFWAY_TOP_LEFT)); + onDrawingSurfaceView() + .checkPixelColor(Color.BLACK, BitmapLocationProvider.HALFWAY_TOP_LEFT); + + onTopBarView().onUndoButton() + .check(matches(allOf(withDrawable(R.drawable.ic_pocketpaint_undo), isEnabled()))); + + onDrawingSurfaceView() + .perform(touchAt(DrawingSurfaceLocationProvider.MIDDLE)) + .perform(touchAt(DrawingSurfaceLocationProvider.HALFWAY_BOTTOM_RIGHT)); + onDrawingSurfaceView() + .checkPixelColor(Color.BLACK, BitmapLocationProvider.MIDDLE) + .checkPixelColor(Color.BLACK, BitmapLocationProvider.HALFWAY_BOTTOM_RIGHT); + + onTopBarView().onRedoButton() + .check(matches(allOf(withDrawable(R.drawable.ic_pocketpaint_redo_disabled), not(isEnabled())))); + + onTopBarView() + .performUndo(); + + onTopBarView().onRedoButton() + .check(matches(allOf(withDrawable(R.drawable.ic_pocketpaint_redo), isEnabled()))); + + activityHelper.setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); + assertEquals(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE, activityHelper.getScreenOrientation()); + + onTopBarView().onUndoButton() + .check(matches(allOf(withDrawable(R.drawable.ic_pocketpaint_undo), isEnabled()))); + onTopBarView().onRedoButton() + .check(matches(allOf(withDrawable(R.drawable.ic_pocketpaint_redo), isEnabled()))) + .perform(click()) + .check(matches(allOf(withDrawable(R.drawable.ic_pocketpaint_redo_disabled), not(isEnabled())))); + + onDrawingSurfaceView() + .checkPixelColor(Color.BLACK, BitmapLocationProvider.HALFWAY_BOTTOM_RIGHT) + .checkPixelColor(Color.BLACK, BitmapLocationProvider.MIDDLE) + .checkPixelColor(Color.BLACK, BitmapLocationProvider.HALFWAY_TOP_LEFT); + + onTopBarView().onUndoButton() + .check(matches(allOf(withDrawable(R.drawable.ic_pocketpaint_undo), isEnabled()))) + .perform(click()); + + onDrawingSurfaceView() + .checkPixelColor(Color.TRANSPARENT, BitmapLocationProvider.HALFWAY_BOTTOM_RIGHT); + onTopBarView() + .performUndo(); + onDrawingSurfaceView() + .checkPixelColor(Color.TRANSPARENT, BitmapLocationProvider.MIDDLE); + onTopBarView() + .performUndo(); + onDrawingSurfaceView() + .checkPixelColor(Color.TRANSPARENT, BitmapLocationProvider.HALFWAY_TOP_LEFT); + + onView(withId(R.id.pocketpaint_btn_top_undo)) + .check(matches(allOf(withDrawable(R.drawable.ic_pocketpaint_undo_disabled), not(isEnabled())))); + } + + @Test + public void testDisableEnableUndo() { + onTopBarView().onUndoButton() + .check(matches(allOf(withDrawable(R.drawable.ic_pocketpaint_undo_disabled), not(isEnabled())))); + + onDrawingSurfaceView() + .perform(touchAt(DrawingSurfaceLocationProvider.MIDDLE)); + + onTopBarView().onUndoButton() + .check(matches(allOf(withDrawable(R.drawable.ic_pocketpaint_undo), isEnabled()))); + + onTopBarView() + .performUndo(); + + onTopBarView().onUndoButton() + .check(matches(allOf(withDrawable(R.drawable.ic_pocketpaint_undo_disabled), not(isEnabled())))); + } + + @Test + public void testDisableEnableRedo() { + onTopBarView().onRedoButton() + .check(matches(allOf(withDrawable(R.drawable.ic_pocketpaint_redo_disabled), not(isEnabled())))); + + onDrawingSurfaceView() + .perform(touchAt(DrawingSurfaceLocationProvider.MIDDLE)); + + onTopBarView().onRedoButton() + .check(matches(allOf(withDrawable(R.drawable.ic_pocketpaint_redo_disabled), not(isEnabled())))); + + onTopBarView() + .performUndo(); + + onTopBarView().onRedoButton() + .check(matches(allOf(withDrawable(R.drawable.ic_pocketpaint_redo), isEnabled()))); + } + + @Test + public void testPreserveZoomAndMoveAfterUndo() { + + onDrawingSurfaceView() + .perform(touchAt(DrawingSurfaceLocationProvider.MIDDLE)); + + float scale = .5f; + int translationX = 10; + int translationY = 15; + + perspective.setScale(scale); + perspective.surfaceTranslationX = translationX; + perspective.surfaceTranslationY = translationY; + + onTopBarView() + .performUndo(); + + assertEquals(scale, perspective.getScale(), Float.MIN_VALUE); + assertEquals(translationX, perspective.surfaceTranslationX, Float.MIN_VALUE); + assertEquals(translationY, perspective.surfaceTranslationY, Float.MIN_VALUE); + } + + @Test + public void testPreserveZoomAndMoveAfterRedo() { + + onDrawingSurfaceView() + .perform(touchAt(DrawingSurfaceLocationProvider.MIDDLE)); + + onTopBarView() + .performUndo(); + + float scale = .5f; + int translationX = 10; + int translationY = 15; + + perspective.setScale(scale); + perspective.surfaceTranslationX = translationX; + perspective.surfaceTranslationY = translationY; + + onTopBarView() + .performRedo(); + + assertEquals(scale, perspective.getScale(), Float.MIN_VALUE); + assertEquals(translationX, perspective.surfaceTranslationX, Float.MIN_VALUE); + assertEquals(translationY, perspective.surfaceTranslationY, Float.MIN_VALUE); + } + + @Test + public void testUndoDoesNotResetLayerVisibility() { + onDrawingSurfaceView() + .perform(swipeAccurate(DrawingSurfaceLocationProvider.HALFWAY_TOP_MIDDLE, DrawingSurfaceLocationProvider.HALFWAY_RIGHT_MIDDLE)) + .perform(swipeAccurate(DrawingSurfaceLocationProvider.HALFWAY_RIGHT_MIDDLE, DrawingSurfaceLocationProvider.HALFWAY_BOTTOM_MIDDLE)) + .perform(swipeAccurate(DrawingSurfaceLocationProvider.HALFWAY_BOTTOM_MIDDLE, DrawingSurfaceLocationProvider.HALFWAY_LEFT_MIDDLE)) + .perform(swipeAccurate(DrawingSurfaceLocationProvider.HALFWAY_LEFT_MIDDLE, DrawingSurfaceLocationProvider.HALFWAY_TOP_MIDDLE)); + + LayerMenuViewInteraction.onLayerMenuView() + .performOpen() + .performToggleLayerVisibility(0) + .performClose(); + + onTopBarView() + .performUndo(); + + DrawingSurfaceInteraction.onDrawingSurfaceView().checkPixelColor(Color.TRANSPARENT, BitmapLocationProvider.HALFWAY_TOP_MIDDLE); + + onTopBarView() + .performUndo(); + + DrawingSurfaceInteraction.onDrawingSurfaceView().checkPixelColor(Color.TRANSPARENT, BitmapLocationProvider.HALFWAY_RIGHT_MIDDLE); + + onTopBarView() + .performUndo(); + + DrawingSurfaceInteraction.onDrawingSurfaceView().checkPixelColor(Color.TRANSPARENT, BitmapLocationProvider.HALFWAY_BOTTOM_MIDDLE); + + onTopBarView() + .performUndo(); + + DrawingSurfaceInteraction.onDrawingSurfaceView().checkPixelColor(Color.TRANSPARENT, BitmapLocationProvider.HALFWAY_LEFT_MIDDLE); + } + + @Test + public void testUndoRedoDoesNotResetLayerVisibility() { + onDrawingSurfaceView() + .perform(swipeAccurate(DrawingSurfaceLocationProvider.HALFWAY_TOP_MIDDLE, DrawingSurfaceLocationProvider.HALFWAY_RIGHT_MIDDLE)) + .perform(swipeAccurate(DrawingSurfaceLocationProvider.HALFWAY_RIGHT_MIDDLE, DrawingSurfaceLocationProvider.HALFWAY_BOTTOM_MIDDLE)) + .perform(swipeAccurate(DrawingSurfaceLocationProvider.HALFWAY_BOTTOM_MIDDLE, DrawingSurfaceLocationProvider.HALFWAY_LEFT_MIDDLE)) + .perform(swipeAccurate(DrawingSurfaceLocationProvider.HALFWAY_LEFT_MIDDLE, DrawingSurfaceLocationProvider.HALFWAY_TOP_MIDDLE)); + + LayerMenuViewInteraction.onLayerMenuView() + .performOpen() + .performToggleLayerVisibility(0) + .performClose(); + + onTopBarView() + .performUndo(); + + DrawingSurfaceInteraction.onDrawingSurfaceView().checkPixelColor(Color.TRANSPARENT, BitmapLocationProvider.HALFWAY_TOP_MIDDLE); + + onTopBarView() + .performUndo(); + + DrawingSurfaceInteraction.onDrawingSurfaceView().checkPixelColor(Color.TRANSPARENT, BitmapLocationProvider.HALFWAY_RIGHT_MIDDLE); + + onTopBarView() + .performUndo(); + + DrawingSurfaceInteraction.onDrawingSurfaceView().checkPixelColor(Color.TRANSPARENT, BitmapLocationProvider.HALFWAY_BOTTOM_MIDDLE); + + onTopBarView() + .performUndo(); + + DrawingSurfaceInteraction.onDrawingSurfaceView().checkPixelColor(Color.TRANSPARENT, BitmapLocationProvider.HALFWAY_LEFT_MIDDLE); + + onTopBarView() + .performRedo(); + + DrawingSurfaceInteraction.onDrawingSurfaceView().checkPixelColor(Color.TRANSPARENT, BitmapLocationProvider.HALFWAY_TOP_MIDDLE); + + onTopBarView() + .performRedo(); + + DrawingSurfaceInteraction.onDrawingSurfaceView().checkPixelColor(Color.TRANSPARENT, BitmapLocationProvider.HALFWAY_RIGHT_MIDDLE); + + onTopBarView() + .performRedo(); + + DrawingSurfaceInteraction.onDrawingSurfaceView().checkPixelColor(Color.TRANSPARENT, BitmapLocationProvider.HALFWAY_BOTTOM_MIDDLE); + + onTopBarView() + .performRedo(); + + DrawingSurfaceInteraction.onDrawingSurfaceView().checkPixelColor(Color.TRANSPARENT, BitmapLocationProvider.HALFWAY_LEFT_MIDDLE); + + LayerMenuViewInteraction.onLayerMenuView() + .performOpen() + .performToggleLayerVisibility(0) + .performClose(); + + DrawingSurfaceInteraction.onDrawingSurfaceView().checkPixelColor(Color.BLACK, BitmapLocationProvider.HALFWAY_LEFT_MIDDLE); + } + + @Test + public void testChangeColorUndoRedo() { + selectColorInDialog(0); + selectColorInDialog(1); + selectColorInDialog(2); + + onTopBarView().performUndo(); + onTopBarView().performUndo(); + onTopBarView().performUndo(); + onColorPickerView() + .performOpenColorPicker(); + onColorPickerView().checkCurrentViewColor(Color.BLACK); + onColorPickerView() + .onNegativeButton() + .perform(click()); + onTopBarView().performRedo(); + onTopBarView().performRedo(); + onTopBarView().performRedo(); + + onDrawingSurfaceView().perform(swipeAccurate(DrawingSurfaceLocationProvider.HALFWAY_TOP_MIDDLE, DrawingSurfaceLocationProvider.HALFWAY_RIGHT_MIDDLE)); + + DrawingSurfaceInteraction.onDrawingSurfaceView().checkPixelColor(Color.parseColor("#FF078707"), BitmapLocationProvider.HALFWAY_TOP_MIDDLE); + + onTopBarView().performUndo(); + onTopBarView().performUndo(); + onTopBarView().performUndo(); + + onDrawingSurfaceView().perform(swipeAccurate(DrawingSurfaceLocationProvider.HALFWAY_BOTTOM_MIDDLE, DrawingSurfaceLocationProvider.HALFWAY_LEFT_MIDDLE)); + + DrawingSurfaceInteraction.onDrawingSurfaceView().checkPixelColor(Color.parseColor("#FF0074CD"), BitmapLocationProvider.HALFWAY_BOTTOM_MIDDLE); + + onTopBarView().performUndo(); + onTopBarView().performUndo(); + onTopBarView().performRedo(); + + onDrawingSurfaceView().perform(swipeAccurate(DrawingSurfaceLocationProvider.HALFWAY_TOP_MIDDLE, DrawingSurfaceLocationProvider.HALFWAY_RIGHT_MIDDLE)); + + DrawingSurfaceInteraction.onDrawingSurfaceView().checkPixelColor(Color.parseColor("#FF0074CD"), BitmapLocationProvider.HALFWAY_TOP_MIDDLE); + } +} diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/catroid/MoreOptionsIntegrationTest.java b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/catroid/MoreOptionsIntegrationTest.java new file mode 100644 index 0000000000..e9982ff553 --- /dev/null +++ b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/catroid/MoreOptionsIntegrationTest.java @@ -0,0 +1,128 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2015 The Catrobat Team + * () + * + * 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. + * + * 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 . + */ + +package org.catrobat.paintroid.test.espresso.catroid; + +import android.content.Intent; +import android.graphics.Color; + +import org.catrobat.paintroid.MainActivity; +import org.catrobat.paintroid.R; +import org.catrobat.paintroid.test.espresso.util.BitmapLocationProvider; +import org.catrobat.paintroid.test.espresso.util.DrawingSurfaceLocationProvider; +import org.catrobat.paintroid.test.espresso.util.EspressoUtils; +import org.catrobat.paintroid.test.utils.ScreenshotOnFailRule; +import org.junit.Before; +import org.junit.ClassRule; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import static org.catrobat.paintroid.common.ConstantsKt.PAINTROID_PICTURE_NAME; +import static org.catrobat.paintroid.common.ConstantsKt.PAINTROID_PICTURE_PATH; +import static org.junit.Assert.assertTrue; + +import androidx.test.espresso.intent.rule.IntentsTestRule; +import androidx.test.ext.junit.runners.AndroidJUnit4; +import androidx.test.platform.app.InstrumentationRegistry; +import androidx.test.rule.GrantPermissionRule; +import androidx.test.uiautomator.UiDevice; +import androidx.test.uiautomator.UiObject; +import androidx.test.uiautomator.UiSelector; + +import static org.catrobat.paintroid.test.espresso.util.UiInteractions.touchAt; +import static org.catrobat.paintroid.test.espresso.util.wrappers.DrawingSurfaceInteraction.onDrawingSurfaceView; +import static org.catrobat.paintroid.test.espresso.util.wrappers.OptionsMenuViewInteraction.onOptionsMenu; +import static org.catrobat.paintroid.test.espresso.util.wrappers.TopBarViewInteraction.onTopBarView; + +import static androidx.test.espresso.Espresso.onView; +import static androidx.test.espresso.action.ViewActions.click; +import static androidx.test.espresso.matcher.ViewMatchers.withText; + +@RunWith(AndroidJUnit4.class) +public class MoreOptionsIntegrationTest { + + @Rule + public IntentsTestRule launchActivityRule = new IntentsTestRule<>(MainActivity.class, false, false); + + @Rule + public ScreenshotOnFailRule screenshotOnFailRule = new ScreenshotOnFailRule(); + + @ClassRule + public static GrantPermissionRule grantPermissionRule = EspressoUtils.grantPermissionRulesVersionCheck(); + + @Before + public void setUp() { + Intent intent = new Intent(); + intent.putExtra(PAINTROID_PICTURE_PATH, ""); + intent.putExtra(PAINTROID_PICTURE_NAME, "testFile"); + launchActivityRule.launchActivity(intent); + } + + @Test + public void testMoreOptionsAllItemsExist() { + onTopBarView() + .performOpenMoreOptions(); + + onOptionsMenu() + .checkItemExists(R.string.menu_load_image) + .checkItemExists(R.string.menu_hide_menu) + .checkItemExists(R.string.help_title) + .checkItemExists(R.string.pocketpaint_menu_about) + .checkItemExists(R.string.share_image_menu) + + .checkItemDoesNotExist(R.string.menu_save_image) + .checkItemDoesNotExist(R.string.menu_save_copy) + .checkItemDoesNotExist(R.string.menu_new_image) + .checkItemDoesNotExist(R.string.menu_rate_us) + + .checkItemExists(R.string.menu_discard_image) + .checkItemExists(R.string.menu_export); + } + + @Test + public void testMoreOptionsDiscardImage() { + onDrawingSurfaceView() + .perform(touchAt(DrawingSurfaceLocationProvider.MIDDLE)); + onDrawingSurfaceView() + .checkPixelColor(Color.BLACK, BitmapLocationProvider.MIDDLE); + onTopBarView() + .performOpenMoreOptions(); + onView(withText(R.string.menu_discard_image)) + .perform(click()); + onDrawingSurfaceView() + .checkPixelColor(Color.TRANSPARENT, BitmapLocationProvider.MIDDLE); + } + + @Test + public void testMoreOptionsShareImageClick() { + onDrawingSurfaceView() + .perform(touchAt(DrawingSurfaceLocationProvider.MIDDLE)); + onDrawingSurfaceView() + .checkPixelColor(Color.BLACK, BitmapLocationProvider.MIDDLE); + onTopBarView() + .performOpenMoreOptions(); + onView(withText(R.string.share_image_menu)) + .perform(click()); + UiDevice mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()); + UiObject uiObject = mDevice.findObject(new UiSelector()); + assertTrue(uiObject.exists()); + mDevice.pressBack(); + } +} diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/catroid/OpenedFromPocketCodeNewImageTest.java b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/catroid/OpenedFromPocketCodeNewImageTest.java new file mode 100644 index 0000000000..ddf590f1ea --- /dev/null +++ b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/catroid/OpenedFromPocketCodeNewImageTest.java @@ -0,0 +1,208 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2021 The Catrobat Team + * () + * + * 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. + * + * 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 . + */ + +package org.catrobat.paintroid.test.espresso.catroid; + +import android.app.Activity; +import android.app.Instrumentation; +import android.content.Intent; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Color; +import android.net.Uri; + +import org.catrobat.paintroid.FileIO; +import org.catrobat.paintroid.MainActivity; +import org.catrobat.paintroid.R; +import org.catrobat.paintroid.test.espresso.util.BitmapLocationProvider; +import org.catrobat.paintroid.test.espresso.util.DrawingSurfaceLocationProvider; +import org.catrobat.paintroid.test.espresso.util.EspressoUtils; +import org.catrobat.paintroid.test.utils.ScreenshotOnFailRule; +import org.catrobat.paintroid.tools.ToolType; +import org.junit.After; +import org.junit.Before; +import org.junit.ClassRule; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.io.File; +import java.io.IOException; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.Objects; + +import androidx.test.espresso.Espresso; +import androidx.test.espresso.intent.rule.IntentsTestRule; +import androidx.test.ext.junit.runners.AndroidJUnit4; +import androidx.test.rule.GrantPermissionRule; + +import static org.catrobat.paintroid.common.ConstantsKt.PAINTROID_PICTURE_NAME; +import static org.catrobat.paintroid.common.ConstantsKt.PAINTROID_PICTURE_PATH; +import static org.catrobat.paintroid.test.espresso.util.UiInteractions.touchAt; +import static org.catrobat.paintroid.test.espresso.util.wrappers.DrawingSurfaceInteraction.onDrawingSurfaceView; +import static org.catrobat.paintroid.test.espresso.util.wrappers.ToolBarViewInteraction.onToolBarView; +import static org.catrobat.paintroid.test.espresso.util.wrappers.TopBarViewInteraction.onTopBarView; +import static org.hamcrest.Matchers.greaterThan; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; + +import static androidx.test.espresso.Espresso.onView; +import static androidx.test.espresso.action.ViewActions.click; +import static androidx.test.espresso.assertion.ViewAssertions.doesNotExist; +import static androidx.test.espresso.intent.Intents.intending; +import static androidx.test.espresso.intent.matcher.IntentMatchers.hasAction; +import static androidx.test.espresso.matcher.ViewMatchers.withText; + +@RunWith(AndroidJUnit4.class) +public class OpenedFromPocketCodeNewImageTest { + + private static final String IMAGE_NAME = "testFile"; + private static final String IMAGE_TO_LOAD_NAME = "loadFile"; + + @Rule + public IntentsTestRule launchActivityRule = new IntentsTestRule<>(MainActivity.class, false, false); + + @Rule + public ScreenshotOnFailRule screenshotOnFailRule = new ScreenshotOnFailRule(); + + @ClassRule + public static GrantPermissionRule grantPermissionRule = EspressoUtils.grantPermissionRulesVersionCheck(); + + private File imageFile = null; + private MainActivity activity; + private ArrayList deletionFileList = null; + + @Before + public void setUp() { + Intent intent = new Intent(); + intent.putExtra(PAINTROID_PICTURE_PATH, ""); + intent.putExtra(PAINTROID_PICTURE_NAME, IMAGE_NAME); + + launchActivityRule.launchActivity(intent); + deletionFileList = new ArrayList<>(); + activity = launchActivityRule.getActivity(); + imageFile = getNewImageFile(IMAGE_NAME); + deletionFileList.add(imageFile); + + onToolBarView() + .performSelectTool(ToolType.BRUSH); + } + + @After + public void tearDown() { + for (File file : deletionFileList) { + if (file != null && file.exists()) { + assertTrue(file.delete()); + } + } + } + + @Test + public void testSave() { + onDrawingSurfaceView() + .perform(touchAt(DrawingSurfaceLocationProvider.MIDDLE)); + + Espresso.pressBackUnconditionally(); + verifyImageFile(); + } + + @Test + public void testLoadWithoutChange() { + createImageIntent(); + + onTopBarView() + .performOpenMoreOptions(); + + onView(withText(R.string.menu_load_image)).perform(click()); + onView(withText(R.string.dialog_warning_new_image)).check(doesNotExist()); + + onDrawingSurfaceView() + .checkPixelColor(Color.WHITE, BitmapLocationProvider.MIDDLE); + + Espresso.pressBackUnconditionally(); + verifyImageFile(); + } + + @Test + public void testLoadWithChange() { + createImageIntent(); + + onTopBarView() + .performOpenMoreOptions(); + + onView(withText(R.string.menu_load_image)).perform(click()); + onView(withText(R.string.dialog_warning_new_image)).check(doesNotExist()); + + onDrawingSurfaceView() + .checkPixelColor(Color.WHITE, BitmapLocationProvider.MIDDLE); + onDrawingSurfaceView() + .perform(touchAt(DrawingSurfaceLocationProvider.MIDDLE)); + onDrawingSurfaceView() + .checkPixelColor(Color.BLACK, BitmapLocationProvider.MIDDLE); + + Espresso.pressBackUnconditionally(); + verifyImageFile(); + } + + private File getNewImageFile(String filename) { + try { + return FileIO.createNewEmptyPictureFile(filename, launchActivityRule.getActivity()); + } catch (NullPointerException e) { + throw new AssertionError("Could not create temp file", e); + } + } + + private void createImageIntent() { + Intent intent = new Intent(); + intent.setData(createTestImageFile()); + Instrumentation.ActivityResult resultOK = new Instrumentation.ActivityResult(Activity.RESULT_OK, intent); + intending(hasAction(Intent.ACTION_GET_CONTENT)).respondWith(resultOK); + } + + private Uri createTestImageFile() { + Bitmap bitmap = Bitmap.createBitmap(400, 400, Bitmap.Config.ARGB_8888); + Canvas canvas = new Canvas(bitmap); + canvas.drawColor(Color.WHITE); + canvas.drawBitmap(bitmap, 0F, 0F, null); + + File imageFile = new File(activity.getExternalFilesDir(null).getAbsolutePath(), IMAGE_TO_LOAD_NAME + ".jpg"); + Uri imageUri = Uri.fromFile(imageFile); + try { + OutputStream fos = activity.getContentResolver().openOutputStream(Objects.requireNonNull(imageUri)); + assertTrue(bitmap.compress(Bitmap.CompressFormat.JPEG, 100, fos)); + assert fos != null; + fos.close(); + } catch (IOException e) { + throw new AssertionError("Picture file could not be created.", e); + } + + deletionFileList.add(imageFile); + return imageUri; + } + + private void verifyImageFile() { + String path = launchActivityRule.getActivityResult().getResultData().getStringExtra(PAINTROID_PICTURE_PATH); + assertEquals(imageFile.getAbsolutePath(), path); + + assertTrue(imageFile.exists()); + assertThat(imageFile.length(), greaterThan(0L)); + } +} diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/catroid/OpenedFromPocketCodeWithImageTest.kt b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/catroid/OpenedFromPocketCodeWithImageTest.kt new file mode 100644 index 0000000000..4c0740c404 --- /dev/null +++ b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/catroid/OpenedFromPocketCodeWithImageTest.kt @@ -0,0 +1,246 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2022 The Catrobat Team + * () + * + * 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. + * + * 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 . + */ +package org.catrobat.paintroid.test.espresso.catroid + +import android.app.Activity +import android.app.Instrumentation.ActivityResult +import android.content.Intent +import android.graphics.Bitmap +import android.graphics.Canvas +import android.graphics.Color +import android.net.Uri +import androidx.test.espresso.Espresso.onView +import androidx.test.espresso.Espresso.pressBackUnconditionally +import androidx.test.espresso.action.ViewActions.click +import androidx.test.espresso.assertion.ViewAssertions +import androidx.test.espresso.intent.Intents +import androidx.test.espresso.intent.matcher.IntentMatchers +import androidx.test.espresso.intent.rule.IntentsTestRule +import androidx.test.espresso.matcher.ViewMatchers +import androidx.test.ext.junit.runners.AndroidJUnit4 +import id.zelory.compressor.Compressor +import kotlinx.coroutines.delay +import kotlinx.coroutines.runBlocking +import org.catrobat.paintroid.FileIO.createNewEmptyPictureFile +import org.catrobat.paintroid.MainActivity +import org.catrobat.paintroid.R +import org.catrobat.paintroid.common.PAINTROID_PICTURE_PATH +import org.catrobat.paintroid.test.espresso.util.BitmapLocationProvider +import org.catrobat.paintroid.test.espresso.util.DrawingSurfaceLocationProvider +import org.catrobat.paintroid.test.espresso.util.EspressoUtils +import org.catrobat.paintroid.test.espresso.util.UiInteractions +import org.catrobat.paintroid.test.espresso.util.wrappers.DrawingSurfaceInteraction +import org.catrobat.paintroid.test.espresso.util.wrappers.ToolBarViewInteraction +import org.catrobat.paintroid.test.espresso.util.wrappers.TopBarViewInteraction +import org.catrobat.paintroid.test.utils.ScreenshotOnFailRule +import org.catrobat.paintroid.tools.ToolType +import org.hamcrest.Matchers +import org.junit.After +import org.junit.Assert.assertEquals +import org.junit.Assert.assertThat +import org.junit.Assert.assertTrue +import org.junit.Before +import org.junit.ClassRule +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import java.io.File +import java.io.IOException +import java.lang.AssertionError +import java.lang.NullPointerException +import java.util.Objects +import kotlin.collections.ArrayList + +private const val IMAGE_NAME = "testFile" + +@RunWith(AndroidJUnit4::class) +class OpenedFromPocketCodeWithImageTest { + @get:Rule + var launchActivityRule = IntentsTestRule( + MainActivity::class.java, false, true + ) + + @get:Rule + var screenshotOnFailRule = ScreenshotOnFailRule() + + private var imageFile: File? = null + private var activity: MainActivity? = null + private var deletionFileList: ArrayList? = null + + @Before + fun setUp() { + deletionFileList = ArrayList() + activity = launchActivityRule.activity + imageFile = getNewImageFile(IMAGE_NAME) + deletionFileList!!.add(imageFile) + launchActivityRule.activity.model.savedPictureUri = Uri.fromFile(imageFile) + launchActivityRule.activity.model.isOpenedFromCatroid = true + ToolBarViewInteraction.onToolBarView() + .performSelectTool(ToolType.BRUSH) + } + + @After + fun tearDown() { + for (file in deletionFileList!!) { + if (file != null && file.exists()) { + assertTrue(file.delete()) + } + } + } + + @Test + fun testSave() { + DrawingSurfaceInteraction.onDrawingSurfaceView() + .perform(UiInteractions.touchAt(DrawingSurfaceLocationProvider.MIDDLE)) + val lastModifiedBefore = imageFile!!.lastModified() + val fileSizeBefore = imageFile!!.length() + pressBackUnconditionally() + runBlocking { + delay(500) + } + verifyImageFile(lastModifiedBefore, fileSizeBefore) + } + + @Test + fun testLoadWithoutChange() { + val lastModifiedBefore = imageFile!!.lastModified() + val fileSizeBefore = imageFile!!.length() + createImageIntent() + TopBarViewInteraction.onTopBarView() + .performOpenMoreOptions() + onView(ViewMatchers.withText(R.string.menu_load_image)) + .perform(click()) + onView(ViewMatchers.withText(R.string.dialog_warning_new_image)) + .check(ViewAssertions.doesNotExist()) + DrawingSurfaceInteraction.onDrawingSurfaceView() + .checkPixelColor(Color.WHITE, BitmapLocationProvider.MIDDLE) + pressBackUnconditionally() + verifyImageFile(lastModifiedBefore, fileSizeBefore) + } + + @Test + fun testLoadWithChange() { + val lastModifiedBefore = imageFile!!.lastModified() + val fileSizeBefore = imageFile!!.length() + createImageIntent() + TopBarViewInteraction.onTopBarView() + .performOpenMoreOptions() + onView(ViewMatchers.withText(R.string.menu_load_image)) + .perform(click()) + onView(ViewMatchers.withText(R.string.dialog_warning_new_image)) + .check(ViewAssertions.doesNotExist()) + DrawingSurfaceInteraction.onDrawingSurfaceView() + .checkPixelColor(Color.WHITE, BitmapLocationProvider.MIDDLE) + DrawingSurfaceInteraction.onDrawingSurfaceView() + .perform(UiInteractions.touchAt(DrawingSurfaceLocationProvider.MIDDLE)) + DrawingSurfaceInteraction.onDrawingSurfaceView() + .checkPixelColor(Color.BLACK, BitmapLocationProvider.MIDDLE) + pressBackUnconditionally() + verifyImageFile(lastModifiedBefore, fileSizeBefore) + } + + @Test + fun testBackToPocketCode() { + DrawingSurfaceInteraction.onDrawingSurfaceView() + .perform(UiInteractions.touchAt(DrawingSurfaceLocationProvider.MIDDLE)) + pressBackUnconditionally() + val lastModifiedBefore = imageFile!!.lastModified() + val fileSizeBefore = imageFile!!.length() + assertThat( + "Image modified", + imageFile!!.lastModified(), + Matchers.equalTo(lastModifiedBefore) + ) + assertThat( + "Saved image length changed", + imageFile!!.length(), + Matchers.equalTo(fileSizeBefore) + ) + } + + private fun getNewImageFile(filename: String): File { + return try { + createNewEmptyPictureFile(filename, launchActivityRule.activity) + } catch (e: NullPointerException) { + throw AssertionError("Could not create temp file", e) + } + } + + private fun createImageIntent() { + val intent = Intent() + intent.data = createTestImageFile() + val resultOK = ActivityResult(Activity.RESULT_OK, intent) + Intents.intending(IntentMatchers.hasAction(Intent.ACTION_GET_CONTENT)).respondWith(resultOK) + } + + private fun createTestImageFile(): Uri { + val bitmap = Bitmap.createBitmap(400, 400, Bitmap.Config.ARGB_8888) + val canvas = Canvas(bitmap) + canvas.drawColor(Color.WHITE) + canvas.drawBitmap(bitmap, 0f, 0f, null) + val uncompressedImageFile = File( + activity!!.getExternalFilesDir(null)!!.absolutePath, + "uncompressed_$IMAGE_NAME.jpg" + ) + try { + val uncompressedImageUri = Uri.fromFile(uncompressedImageFile) + val fos = activity!!.contentResolver.openOutputStream( + Objects.requireNonNull(uncompressedImageUri) + ) + assertTrue(bitmap.compress(Bitmap.CompressFormat.PNG, 100, fos)) + assert(fos != null) + fos!!.close() + } catch (e: IOException) { + throw AssertionError("Picture file could not be created.", e) + } + val compressor = Compressor(launchActivityRule.activity) + compressor.setCompressFormat(Bitmap.CompressFormat.JPEG) + compressor.setQuality(100) + compressor.setDestinationDirectoryPath(activity!!.getExternalFilesDir(null)!!.absolutePath + "/Pictures") + deletionFileList!!.add(uncompressedImageFile) + imageFile = try { + compressor.compressToFile(uncompressedImageFile, "$IMAGE_NAME.png") + } catch (e: IOException) { + throw AssertionError("Test Picture file could not be created.", e) + } + deletionFileList!!.add(imageFile) + return Uri.fromFile(imageFile) + } + + private fun verifyImageFile(lastModifiedBefore: Long, fileSizeBefore: Long) { + val path = + launchActivityRule.activityResult.resultData.getStringExtra(PAINTROID_PICTURE_PATH) + assertEquals(imageFile!!.absolutePath, path) + assertThat( + "Image modification not saved", + imageFile!!.lastModified(), + Matchers.greaterThan(lastModifiedBefore) + ) + assertThat( + "Saved image length not changed", + imageFile!!.length(), + Matchers.greaterThan(fileSizeBefore) + ) + } + + companion object { + @get:ClassRule + var grantPermissionRule = EspressoUtils.grantPermissionRulesVersionCheck() + } +} diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/dialog/BrushPickerIntegrationTest.java b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/dialog/BrushPickerIntegrationTest.java new file mode 100644 index 0000000000..60075323b2 --- /dev/null +++ b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/dialog/BrushPickerIntegrationTest.java @@ -0,0 +1,298 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2021 The Catrobat Team + * () + * + * 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. + * + * 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 . + */ + +package org.catrobat.paintroid.test.espresso.dialog; + +import android.graphics.Paint; +import android.graphics.Paint.Cap; + +import org.catrobat.paintroid.MainActivity; +import org.catrobat.paintroid.R; +import org.catrobat.paintroid.test.utils.ScreenshotOnFailRule; +import org.catrobat.paintroid.tools.ToolType; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import androidx.test.espresso.Espresso; +import androidx.test.ext.junit.runners.AndroidJUnit4; +import androidx.test.rule.ActivityTestRule; + +import static org.catrobat.paintroid.test.espresso.util.EspressoUtils.DEFAULT_STROKE_WIDTH; +import static org.catrobat.paintroid.test.espresso.util.UiInteractions.setProgress; +import static org.catrobat.paintroid.test.espresso.util.UiInteractions.touchCenterLeft; +import static org.catrobat.paintroid.test.espresso.util.UiMatcher.withProgress; +import static org.catrobat.paintroid.test.espresso.util.wrappers.ToolBarViewInteraction.onToolBarView; +import static org.catrobat.paintroid.test.espresso.util.wrappers.TopBarViewInteraction.onTopBarView; +import static org.hamcrest.core.IsNot.not; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import static androidx.test.espresso.Espresso.onView; +import static androidx.test.espresso.action.ViewActions.click; +import static androidx.test.espresso.action.ViewActions.replaceText; +import static androidx.test.espresso.assertion.ViewAssertions.matches; +import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed; +import static androidx.test.espresso.matcher.ViewMatchers.isSelected; +import static androidx.test.espresso.matcher.ViewMatchers.withId; +import static androidx.test.espresso.matcher.ViewMatchers.withText; + +@RunWith(AndroidJUnit4.class) +public class BrushPickerIntegrationTest { + private static final int MIN_STROKE_WIDTH = 1; + private static final int MIDDLE_STROKE_WIDTH = 50; + private static final int MAX_STROKE_WIDTH = 100; + + @Rule + public ActivityTestRule launchActivityRule = new ActivityTestRule<>(MainActivity.class); + + @Rule + public ScreenshotOnFailRule screenshotOnFailRule = new ScreenshotOnFailRule(); + + @Before + public void setUp() { + onToolBarView() + .performSelectTool(ToolType.BRUSH); + } + + private Paint getCurrentToolBitmapPaint() { + return launchActivityRule.getActivity().toolPaint.getPaint(); + } + + private Paint getCurrentToolCanvasPaint() { + return launchActivityRule.getActivity().toolPaint.getPreviewPaint(); + } + + private void assertStrokePaint(Paint strokePaint, int expectedStrokeWidth, Cap expectedCap) { + int paintStrokeWidth = (int) strokePaint.getStrokeWidth(); + Cap paintCap = strokePaint.getStrokeCap(); + + assertEquals("Stroke did not change", expectedStrokeWidth, paintStrokeWidth); + assertEquals("Stroke cap not " + expectedCap.toString(), expectedCap, paintCap); + } + + private void setStrokeWidth(int strokeWidth, int expectedStrokeWidth) { + onView(withId(R.id.pocketpaint_stroke_width_seek_bar)) + .perform(setProgress(strokeWidth)) + .check(matches(withProgress(expectedStrokeWidth))); + } + + private void setStrokeWidth(int strokeWidth) { + setStrokeWidth(strokeWidth, strokeWidth); + } + + @Test + public void brushPickerDialogDefaultLayoutAndToolChanges() { + onView(withId(R.id.pocketpaint_brush_tool_preview)) + .check(matches(isDisplayed())); + onView(withId(R.id.pocketpaint_stroke_width_seek_bar)) + .check(matches(isDisplayed())) + .check(matches(withProgress(DEFAULT_STROKE_WIDTH))); + onView(withId(R.id.pocketpaint_stroke_width_width_text)) + .check(matches(isDisplayed())) + .check(matches(withText(Integer.toString(DEFAULT_STROKE_WIDTH)))); + onView(withId(R.id.pocketpaint_stroke_ibtn_rect)) + .check(matches(isDisplayed())) + .check(matches(not(isSelected()))); + onView(withId(R.id.pocketpaint_stroke_ibtn_circle)) + .check(matches(isDisplayed())) + .check(matches(isSelected())); + + setStrokeWidth(MIN_STROKE_WIDTH); + setStrokeWidth(MIDDLE_STROKE_WIDTH); + setStrokeWidth(MAX_STROKE_WIDTH); + + assertStrokePaint(getCurrentToolCanvasPaint(), MAX_STROKE_WIDTH, Cap.ROUND); + + onView(withId(R.id.pocketpaint_stroke_ibtn_rect)) + .perform(click()) + .check(matches(isSelected())); + onView(withId(R.id.pocketpaint_stroke_ibtn_circle)) + .check(matches(not(isSelected()))); + + assertStrokePaint(getCurrentToolCanvasPaint(), MAX_STROKE_WIDTH, Cap.SQUARE); + + onToolBarView() + .performCloseToolOptionsView(); + + assertStrokePaint(getCurrentToolCanvasPaint(), MAX_STROKE_WIDTH, Cap.SQUARE); + } + + @Test + public void brushPickerDialogKeepStrokeOnToolChange() { + final int newStrokeWidth = 80; + + setStrokeWidth(newStrokeWidth); + onView(withId(R.id.pocketpaint_stroke_ibtn_rect)) + .perform(click()); + + assertStrokePaint(getCurrentToolCanvasPaint(), newStrokeWidth, Cap.SQUARE); + + onToolBarView() + .performCloseToolOptionsView() + .performSelectTool(ToolType.CURSOR); + + onView(withId(R.id.pocketpaint_stroke_width_seek_bar)) + .check(matches(withProgress(newStrokeWidth))); + assertStrokePaint(getCurrentToolCanvasPaint(), newStrokeWidth, Cap.SQUARE); + + onToolBarView() + .performCloseToolOptionsView(); + } + + @Test + public void brushPickerDialogMinimumBrushWidth() { + setStrokeWidth(0, MIN_STROKE_WIDTH); + setStrokeWidth(MIN_STROKE_WIDTH); + + onToolBarView() + .performCloseToolOptionsView(); + } + + @Test + public void brushPickerAntiAliasingOffAtMinimumBrushSize() { + onView(withId(R.id.pocketpaint_stroke_width_seek_bar)) + .perform(touchCenterLeft()); + + onToolBarView() + .performCloseToolOptionsView(); + + Paint bitmapPaint = getCurrentToolBitmapPaint(); + Paint canvasPaint = getCurrentToolCanvasPaint(); + + assertFalse("BITMAP_PAINT antialiasing should be off", bitmapPaint.isAntiAlias()); + assertFalse("CANVAS_PAINT antialiasing should be off", canvasPaint.isAntiAlias()); + } + + @Test + public void setAntiAliasingNotOnWhenCancelPressed() { + onTopBarView() + .performOpenMoreOptions(); + + onView(withText(R.string.menu_advanced)) + .perform(click()); + + onView(withId(R.id.pocketpaint_antialiasing)) + .perform(click()); + + onView(withText(R.string.cancel_button_text)) + .perform(click()); + + Paint bitmapPaint = getCurrentToolBitmapPaint(); + Paint canvasPaint = getCurrentToolCanvasPaint(); + + assertTrue("BITMAP_PAINT antialiasing should be on", bitmapPaint.isAntiAlias()); + assertTrue("CANVAS_PAINT antialiasing should be on", canvasPaint.isAntiAlias()); + } + + @Test + public void setAntiAliasingOffWhenAdvancedSettingsTurnOffAndOn() { + onTopBarView() + .performOpenMoreOptions(); + + onView(withText(R.string.menu_advanced)) + .check(matches(isDisplayed())); + + onView(withText(R.string.menu_advanced)) + .perform(click()); + + onView(withId(R.id.pocketpaint_antialiasing)) + .perform(click()); + + onView(withText(R.string.pocketpaint_ok)) + .perform(click()); + + Paint bitmapPaint = getCurrentToolBitmapPaint(); + Paint canvasPaint = getCurrentToolCanvasPaint(); + + assertFalse("BITMAP_PAINT antialiasing should be off", bitmapPaint.isAntiAlias()); + assertFalse("CANVAS_PAINT antialiasing should be off", canvasPaint.isAntiAlias()); + + onTopBarView() + .performOpenMoreOptions(); + + onView(withText(R.string.menu_advanced)) + .perform(click()); + + onView(withId(R.id.pocketpaint_antialiasing)) + .perform(click()); + + onView(withText(R.string.pocketpaint_ok)) + .perform(click()); + + bitmapPaint = getCurrentToolBitmapPaint(); + canvasPaint = getCurrentToolCanvasPaint(); + + assertTrue("BITMAP_PAINT antialiasing should be on", bitmapPaint.isAntiAlias()); + assertTrue("CANVAS_PAINT antialiasing should be on", canvasPaint.isAntiAlias()); + } + + @Test + public void brushPickerDialogRadioButtonsBehaviour() { + onView(withId(R.id.pocketpaint_stroke_ibtn_rect)) + .check(matches(not(isSelected()))); + onView(withId(R.id.pocketpaint_stroke_ibtn_circle)) + .check(matches(isSelected())); + + onView(withId(R.id.pocketpaint_stroke_ibtn_rect)) + .perform(click()) + .check(matches(isSelected())); + + onView(withId(R.id.pocketpaint_stroke_ibtn_circle)) + .check(matches(not(isSelected()))); + + onToolBarView() + .performCloseToolOptionsView(); + + assertStrokePaint(getCurrentToolCanvasPaint(), DEFAULT_STROKE_WIDTH, Cap.SQUARE); + + onToolBarView() + .performOpenToolOptionsView(); + + onView(withId(R.id.pocketpaint_stroke_ibtn_circle)) + .perform(click()) + .check(matches(isSelected())); + + onView(withId(R.id.pocketpaint_stroke_ibtn_rect)) + .check(matches(not(isSelected()))); + + assertStrokePaint(getCurrentToolCanvasPaint(), DEFAULT_STROKE_WIDTH, Cap.ROUND); + + onToolBarView() + .performCloseToolOptionsView(); + + assertStrokePaint(getCurrentToolCanvasPaint(), DEFAULT_STROKE_WIDTH, Cap.ROUND); + } + + @Test + public void brushPickerDialogEditTextBehaviour() { + onView(withId(R.id.pocketpaint_stroke_width_width_text)) + .perform(replaceText(String.valueOf(MIDDLE_STROKE_WIDTH))); + + Espresso.closeSoftKeyboard(); + + onView(withId(R.id.pocketpaint_stroke_width_width_text)) + .check(matches(withText(String.valueOf(MIDDLE_STROKE_WIDTH)))); + + onView(withId(R.id.pocketpaint_stroke_width_seek_bar)) + .check(matches(withProgress(MIDDLE_STROKE_WIDTH))); + } +} diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/dialog/ColorDialogIntegrationTest.java b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/dialog/ColorDialogIntegrationTest.java new file mode 100644 index 0000000000..a91b14e16a --- /dev/null +++ b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/dialog/ColorDialogIntegrationTest.java @@ -0,0 +1,776 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2022 The Catrobat Team + * () + * + * 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. + * + * 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 . + */ + +package org.catrobat.paintroid.test.espresso.dialog; + +import android.content.pm.ActivityInfo; +import android.content.res.Resources; +import android.content.res.TypedArray; +import android.graphics.Color; +import android.graphics.Paint; +import android.view.View; + +import org.catrobat.paintroid.MainActivity; +import org.catrobat.paintroid.R; +import org.catrobat.paintroid.colorpicker.HSVColorPickerView; +import org.catrobat.paintroid.colorpicker.PresetSelectorView; +import org.catrobat.paintroid.colorpicker.RgbSelectorView; +import org.catrobat.paintroid.test.espresso.util.DrawingSurfaceLocationProvider; +import org.catrobat.paintroid.test.utils.ScreenshotOnFailRule; +import org.catrobat.paintroid.tools.ToolReference; +import org.catrobat.paintroid.ui.Perspective; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import androidx.test.espresso.action.ViewActions; +import androidx.test.ext.junit.runners.AndroidJUnit4; +import androidx.test.rule.ActivityTestRule; + +import static org.catrobat.paintroid.test.espresso.util.UiInteractions.touchAt; +import static org.catrobat.paintroid.test.espresso.util.UiInteractions.touchCenterLeft; +import static org.catrobat.paintroid.test.espresso.util.UiInteractions.touchCenterMiddle; +import static org.catrobat.paintroid.test.espresso.util.UiInteractions.touchCenterRight; +import static org.catrobat.paintroid.test.espresso.util.UiMatcher.withBackground; +import static org.catrobat.paintroid.test.espresso.util.UiMatcher.withBackgroundColor; +import static org.catrobat.paintroid.test.espresso.util.UiMatcher.withTextColor; +import static org.catrobat.paintroid.test.espresso.util.wrappers.ColorPickerViewInteraction.onColorPickerView; +import static org.catrobat.paintroid.test.espresso.util.wrappers.DrawingSurfaceInteraction.onDrawingSurfaceView; +import static org.catrobat.paintroid.test.espresso.util.wrappers.ToolPropertiesInteraction.onToolProperties; +import static org.hamcrest.Matchers.allOf; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.instanceOf; +import static org.hamcrest.Matchers.not; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; + +import static androidx.test.espresso.Espresso.onView; +import static androidx.test.espresso.action.ViewActions.click; +import static androidx.test.espresso.action.ViewActions.pressBack; +import static androidx.test.espresso.action.ViewActions.replaceText; +import static androidx.test.espresso.action.ViewActions.scrollTo; +import static androidx.test.espresso.action.ViewActions.swipeUp; +import static androidx.test.espresso.assertion.ViewAssertions.matches; +import static androidx.test.espresso.matcher.ViewMatchers.hasSibling; +import static androidx.test.espresso.matcher.ViewMatchers.hasTextColor; +import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed; +import static androidx.test.espresso.matcher.ViewMatchers.isRoot; +import static androidx.test.espresso.matcher.ViewMatchers.withClassName; +import static androidx.test.espresso.matcher.ViewMatchers.withId; +import static androidx.test.espresso.matcher.ViewMatchers.withText; + +@RunWith(AndroidJUnit4.class) +public class ColorDialogIntegrationTest { + + private static final String TAB_VIEW_PRESET_SELECTOR_CLASS = PresetSelectorView.class.getSimpleName(); + private static final String TAB_VIEW_HSV_SELECTOR_CLASS = HSVColorPickerView.class.getSimpleName(); + private static final String TAB_VIEW_RGBA_SELECTOR_CLASS = RgbSelectorView.class.getSimpleName(); + + private static final String TEXT_RGB_MIN = "0"; + private static final String TEXT_RGB_MAX = "255"; + + private static final String TEXT_ALPHA_MIN = "0"; + private static final String TEXT_ALPHA_MAX = "100"; + + private static final String TEXT_PERCENT_SIGN = "%"; + + @Rule + public ActivityTestRule launchActivityRule = new ActivityTestRule<>(MainActivity.class); + + @Rule + public ScreenshotOnFailRule screenshotOnFailRule = new ScreenshotOnFailRule(); + + private ToolReference toolReference; + + @Before + public void setUp() { + toolReference = launchActivityRule.getActivity().toolReference; + } + + private int getColorById(int colorId) { + return launchActivityRule.getActivity().getResources().getColor(colorId); + } + + @Test + public void testStandardTabSelected() { + onColorPickerView() + .performOpenColorPicker(); + + onView(withClassName(containsString(TAB_VIEW_PRESET_SELECTOR_CLASS))).check(matches(isDisplayed())); + } + + @Test + public void testCorrectColorAfterApplyWithoutNewColorSelected() { + Paint initialPaint = toolReference.getTool().getDrawPaint(); + + onColorPickerView() + .performOpenColorPicker() + .onPositiveButton() + .perform(click()); + + assertEquals(initialPaint.getColor(), toolReference.getTool().getDrawPaint().getColor()); + } + + @Test + public void testTabsAreSelectable() { + onColorPickerView() + .performOpenColorPicker(); + + onView(withClassName(containsString(TAB_VIEW_PRESET_SELECTOR_CLASS))).check(matches(isDisplayed())); + + onView(allOf(withId(R.id.color_picker_tab_icon), withBackground(R.drawable.ic_color_picker_tab_hsv))).perform(click()); + onView(withClassName(containsString(TAB_VIEW_HSV_SELECTOR_CLASS))).check(matches(isDisplayed())); + + onView(allOf(withId(R.id.color_picker_tab_icon), withBackground(R.drawable.ic_color_picker_tab_rgba))).perform(click()); + onView(withClassName(containsString(TAB_VIEW_RGBA_SELECTOR_CLASS))).check(matches(isDisplayed())); + + onView(allOf(withId(R.id.color_picker_tab_icon), withBackground(R.drawable.ic_color_picker_tab_preset))).perform(click()); + onView(withClassName(containsString(TAB_VIEW_PRESET_SELECTOR_CLASS))).check(matches(isDisplayed())); + } + + @Test + public void dontShowAlphaRelatedStuffFromCatroidFormulaEditor() { + launchActivityRule.getActivity().model.setOpenedFromCatroid(true); + launchActivityRule.getActivity().model.setOpenedFromFormulaEditorInCatroid(true); + + onColorPickerView() + .performOpenColorPicker(); + + onView(withId(R.id.color_picker_base_layout)) + .perform(swipeUp()); + + onView(withId(R.id.color_alpha_slider)) + .check(matches(not(isDisplayed()))); + + onView(allOf(withId(R.id.color_picker_tab_icon), withBackground(R.drawable.ic_color_picker_tab_hsv))).perform(click()); + + onView(withId(R.id.color_picker_base_layout)) + .perform(swipeUp()); + + onView(withId(R.id.color_alpha_slider)) + .check(matches(not(isDisplayed()))); + + onColorPickerView() + .onPositiveButton() + .perform(click()); + + int currentSelectColor = toolReference.getTool().getDrawPaint().getColor(); + + onColorPickerView() + .performOpenColorPicker(); + + onView(allOf(withId(R.id.color_picker_tab_icon), withBackground(R.drawable.ic_color_picker_tab_rgba))).perform(click()); + + onView(withId(R.id.color_picker_base_layout)) + .perform(swipeUp()); + + onView(withId(R.id.color_picker_alpha_row)) + .check(matches(not(isDisplayed()))); + + onView(withId(R.id.color_picker_color_rgb_hex)) + .check(matches(withText(String.format("#%02X%02X%02X", Color.red(currentSelectColor), Color.green(currentSelectColor), Color.blue(currentSelectColor))))); + } + + @Test + public void showAlphaSliderFromCatroid() { + launchActivityRule.getActivity().model.setOpenedFromCatroid(true); + + onColorPickerView() + .performOpenColorPicker(); + + onView(withId(R.id.color_picker_base_layout)) + .perform(swipeUp()); + + onView(withId(R.id.color_alpha_slider)) + .check(matches(isDisplayed())); + + onView(allOf(withId(R.id.color_picker_tab_icon), withBackground(R.drawable.ic_color_picker_tab_hsv))).perform(click()); + + onView(withId(R.id.color_picker_base_layout)) + .perform(swipeUp()); + + onView(withId(R.id.color_alpha_slider)) + .check(matches(isDisplayed())); + } + + @Test + public void showAlphaSliderIfNotCatroidFlagSet() { + onColorPickerView() + .performOpenColorPicker(); + + onView(withId(R.id.color_picker_base_layout)) + .perform(swipeUp()); + + onView(withId(R.id.color_alpha_slider)) + .check(matches(isDisplayed())); + + onView(allOf(withId(R.id.color_picker_tab_icon), withBackground(R.drawable.ic_color_picker_tab_hsv))).perform(click()); + + onView(withId(R.id.color_picker_base_layout)) + .perform(swipeUp()); + + onView(withId(R.id.color_alpha_slider)) + .check(matches(isDisplayed())); + } + + @Test + public void dontShowAlphaSliderInRgb() { + onColorPickerView() + .performOpenColorPicker(); + + onView(allOf(withId(R.id.color_picker_tab_icon), withBackground(R.drawable.ic_color_picker_tab_rgba))).perform(click()); + + onView(withId(R.id.color_picker_base_layout)) + .perform(swipeUp()); + + onView(withId(R.id.color_alpha_slider)) + .check(matches(not(isDisplayed()))); + } + + @Test + public void testColorSelectionChangesNewColorViewColor() { + + onColorPickerView() + .performOpenColorPicker(); + + final Resources resources = launchActivityRule.getActivity().getResources(); + final TypedArray presetColors = resources.obtainTypedArray(R.array.pocketpaint_color_picker_preset_colors); + for (int counterColors = 0; counterColors < presetColors.length(); counterColors++) { + onColorPickerView() + .performClickColorPickerPresetSelectorButton(counterColors); + + int arrayColor = presetColors.getColor(counterColors, Color.BLACK); + + onView(allOf(withId(R.id.color_picker_new_color_view), instanceOf(View.class))) + .check(matches(withBackgroundColor(arrayColor))); + } + presetColors.recycle(); + } + + @Test + public void testColorNewColorViewChangesStandard() { + final Resources resources = launchActivityRule.getActivity().getResources(); + final TypedArray presetColors = resources.obtainTypedArray(R.array.pocketpaint_color_picker_preset_colors); + for (int counterColors = 0; counterColors < presetColors.length(); counterColors++) { + onColorPickerView() + .performOpenColorPicker(); + + onColorPickerView() + .performClickColorPickerPresetSelectorButton(counterColors); + + onColorPickerView() + .onPositiveButton() + .perform(click()); + + int arrayColor = presetColors.getColor(counterColors, Color.BLACK); + int selectedColor = toolReference.getTool().getDrawPaint().getColor(); + + assertEquals("Color in array and selected color not the same", arrayColor, selectedColor); + } + presetColors.recycle(); + } + + @Test + public void testCurrentColorViewHasInitialColor() { + int selectedColor = toolReference.getTool().getDrawPaint().getColor(); + onColorPickerView() + .performOpenColorPicker(); + + onColorPickerView() + .checkCurrentViewColor(selectedColor); + } + + @Test + public void testCurrentColorViewDoesNotChangeColor() { + onColorPickerView() + .performOpenColorPicker(); + + int initialColor = toolReference.getTool().getDrawPaint().getColor(); + final Resources resources = launchActivityRule.getActivity().getResources(); + final TypedArray presetColors = resources.obtainTypedArray(R.array.pocketpaint_color_picker_preset_colors); + for (int counterColors = 0; counterColors < presetColors.length(); counterColors++) { + + onColorPickerView() + .performClickColorPickerPresetSelectorButton(counterColors); + + onView(allOf(withId(R.id.color_picker_current_color_view), instanceOf(View.class))) + .check(matches(withBackgroundColor(initialColor))); + } + presetColors.recycle(); + } + + @Test + public void testColorPickerDialogOnBackPressedSelectedColorShouldChange() { + int initialColor = toolReference.getTool().getDrawPaint().getColor(); + + onColorPickerView() + .performOpenColorPicker(); + + onView(allOf(withId(R.id.color_picker_tab_icon), withBackground(R.drawable.ic_color_picker_tab_preset))).perform(click()); + onView(withClassName(containsString(TAB_VIEW_PRESET_SELECTOR_CLASS))).check(matches(isDisplayed())); + + TypedArray presetColors = launchActivityRule.getActivity().getResources().obtainTypedArray(R.array.pocketpaint_color_picker_preset_colors); + int colorToSelectIndex = presetColors.length() / 2; + int colorToSelect = presetColors.getColor(colorToSelectIndex, Color.WHITE); + presetColors.recycle(); + + assertNotEquals("Selected color should not be the same as the initial color", colorToSelect, initialColor); + + onColorPickerView() + .performClickColorPickerPresetSelectorButton(colorToSelectIndex); + + // Close color picker dialog + onView(isRoot()).perform(pressBack()); + + int currentSelectedColor = toolReference.getTool().getDrawPaint().getColor(); + assertEquals("Selected color has not changed", colorToSelect, currentSelectedColor); + } + + @Test + public void testIfRGBSeekBarsDoChangeColor() { + final Resources resources = launchActivityRule.getActivity().getResources(); + final TypedArray presetColors = resources.obtainTypedArray(R.array.pocketpaint_color_picker_preset_colors); + + onColorPickerView() + .performOpenColorPicker(); + + onView(allOf(withId(R.id.color_picker_tab_icon), withBackground(R.drawable.ic_color_picker_tab_preset))).perform(click()); + onView(withClassName(containsString(TAB_VIEW_PRESET_SELECTOR_CLASS))).check(matches(isDisplayed())); + + onColorPickerView() + .performClickColorPickerPresetSelectorButton(0); + + onView(allOf(withId(R.id.color_picker_tab_icon), withBackground(R.drawable.ic_color_picker_tab_rgba))).perform(click()); + onView(withClassName(containsString(TAB_VIEW_RGBA_SELECTOR_CLASS))).check(matches(isDisplayed())); + + onView(withId(R.id.color_picker_color_rgb_textview_red)).check(matches(allOf(isDisplayed(), withText(R.string.color_red), withTextColor(getColorById(R.color.pocketpaint_color_picker_rgb_red))))); + onView(withId(R.id.color_picker_color_rgb_textview_green)).check(matches(allOf(isDisplayed(), withText(R.string.color_green), withTextColor(getColorById(R.color.pocketpaint_color_picker_rgb_green))))); + onView(withId(R.id.color_picker_color_rgb_textview_blue)).check(matches(allOf(isDisplayed(), withText(R.string.color_blue), withTextColor(getColorById(R.color.pocketpaint_color_picker_rgb_blue))))); + onView(withId(R.id.color_picker_color_rgb_textview_alpha)).check(matches(allOf(isDisplayed(), withText(R.string.color_alpha), withTextColor(getColorById(R.color.pocketpaint_color_picker_rgb_alpha))))); + + onView(withId(R.id.color_picker_color_rgb_seekbar_red)).check(matches(isDisplayed())); + onView(withId(R.id.color_picker_color_rgb_seekbar_green)).check(matches(isDisplayed())); + onView(withId(R.id.color_picker_color_rgb_seekbar_blue)).check(matches(isDisplayed())); + onView(withId(R.id.color_picker_color_rgb_seekbar_alpha)).check(matches(isDisplayed())); + onView(withId(R.id.color_picker_color_rgb_hex)).check(matches(isDisplayed())); + + onView(withId(R.id.color_picker_rgb_red_value)).check(matches(isDisplayed())); + onView(withId(R.id.color_picker_rgb_green_value)).check(matches(isDisplayed())); + onView(withId(R.id.color_picker_rgb_blue_value)).check(matches(isDisplayed())); + onView(withId(R.id.color_picker_rgb_alpha_value)).check(matches(isDisplayed())); + onView(allOf(withText(TEXT_PERCENT_SIGN), hasSibling(withId(R.id.color_picker_rgb_alpha_value)))).check(matches(isDisplayed())); + + int currentSelectedColor = presetColors.getColor(0, Color.BLACK); + + onView(withId(R.id.color_picker_rgb_red_value)).check(matches(withText(Integer.toString(Color.red(currentSelectedColor))))); + onView(withId(R.id.color_picker_rgb_green_value)).check(matches(withText(Integer.toString(Color.green(currentSelectedColor))))); + onView(withId(R.id.color_picker_rgb_blue_value)).check(matches(withText(Integer.toString(Color.blue(currentSelectedColor))))); + onView(withId(R.id.color_picker_rgb_alpha_value)).check(matches( + withText( + Integer.toString( + (int) (Color.alpha(currentSelectedColor) / 2.55f) + ) + ) + )); + + onView(withId(R.id.color_picker_color_rgb_seekbar_red)).perform(touchCenterLeft()); + onView(withId(R.id.color_picker_rgb_red_value)).check(matches(withText(TEXT_RGB_MIN))); + onView(withId(R.id.color_picker_color_rgb_seekbar_red)).perform(touchCenterRight()); + onView(withId(R.id.color_picker_rgb_red_value)).check(matches(withText(TEXT_RGB_MAX))); + + onView(withId(R.id.color_picker_color_rgb_seekbar_green)).perform(touchCenterLeft()); + onView(withId(R.id.color_picker_rgb_green_value)).check(matches(withText(TEXT_RGB_MIN))); + onView(withId(R.id.color_picker_color_rgb_seekbar_green)).perform(touchCenterRight()); + onView(withId(R.id.color_picker_rgb_green_value)).check(matches(withText(TEXT_RGB_MAX))); + + onView(withId(R.id.color_picker_color_rgb_seekbar_blue)).perform(touchCenterLeft()); + onView(withId(R.id.color_picker_rgb_blue_value)).check(matches(withText(TEXT_RGB_MIN))); + onView(withId(R.id.color_picker_color_rgb_seekbar_blue)).perform(touchCenterRight()); + onView(withId(R.id.color_picker_rgb_blue_value)).check(matches(withText(TEXT_RGB_MAX))); + + onView(withId(R.id.color_picker_color_rgb_seekbar_alpha)).perform(touchCenterLeft()); + onView(withId(R.id.color_picker_rgb_alpha_value)).check(matches(withText(TEXT_ALPHA_MIN))); + onView(withId(R.id.color_picker_color_rgb_seekbar_alpha)).perform(touchCenterRight()); + onView(withId(R.id.color_picker_rgb_alpha_value)).check(matches(withText(TEXT_ALPHA_MAX))); + + // Select color red #FFFF0000 by using hex input + onView(withId(R.id.color_picker_color_rgb_hex)).perform(replaceText("#FFFF0000")); + + onColorPickerView() + .checkNewColorViewColor(Color.RED); + + onView(allOf(withId(R.id.color_picker_tab_icon), withBackground(R.drawable.ic_color_picker_tab_rgba))).perform(click()); + + // Select color blue #FF0000FF by using seekbars + onView(withId(R.id.color_picker_color_rgb_seekbar_red)).perform(touchCenterLeft()); + onView(withId(R.id.color_picker_color_rgb_seekbar_green)).perform(touchCenterLeft()); + onView(withId(R.id.color_picker_color_rgb_seekbar_blue)).perform(touchCenterRight()); + onView(withId(R.id.color_picker_color_rgb_seekbar_alpha)).perform(touchCenterRight()); + + onColorPickerView() + .onPositiveButton() + .perform(click()); + + assertNotEquals("Selected color changed to blue from black", toolReference.getTool().getDrawPaint().getColor(), Color.BLACK); + assertEquals("Selected color is not blue", toolReference.getTool().getDrawPaint().getColor(), Color.BLUE); + } + + @Test + public void testHEXEditTextMaxInputLength() { + onColorPickerView() + .performOpenColorPicker(); + + onView(allOf(withId(R.id.color_picker_tab_icon), withBackground(R.drawable.ic_color_picker_tab_rgba))).perform(click()); + onView(withClassName(containsString(TAB_VIEW_RGBA_SELECTOR_CLASS))).check(matches(isDisplayed())); + + onView(withId(R.id.color_picker_color_rgb_hex)).perform(replaceText("#0123456789ABCDEF01234")); + + onView(withId(R.id.color_picker_color_rgb_hex)).check(matches( + withText( + String.format("#0123456789ABCDEF0")))); + } + + @Test + public void testHEXUpdatingOnColorChange() { + onColorPickerView() + .performOpenColorPicker(); + + onView(allOf(withId(R.id.color_picker_tab_icon), withBackground(R.drawable.ic_color_picker_tab_preset))).perform(click()); + onView(withClassName(containsString(TAB_VIEW_PRESET_SELECTOR_CLASS))).check(matches(isDisplayed())); + + onColorPickerView() + .performClickColorPickerPresetSelectorButton(10); + + onColorPickerView() + .onPositiveButton() + .perform(click()); + + int currentSelectColor = toolReference.getTool().getDrawPaint().getColor(); + + onColorPickerView() + .performOpenColorPicker(); + + onView(allOf(withId(R.id.color_picker_tab_icon), withBackground(R.drawable.ic_color_picker_tab_rgba))).perform(click()); + onView(withClassName(containsString(TAB_VIEW_RGBA_SELECTOR_CLASS))).check(matches(isDisplayed())); + + onView(withId(R.id.color_picker_color_rgb_hex)).check(matches( + withText( + String.format("#FF%06X", 0xFFFFFF & currentSelectColor)))); + + onView(allOf(withId(R.id.color_picker_tab_icon), withBackground(R.drawable.ic_color_picker_tab_hsv))).perform(click()); + onView(withClassName(containsString(TAB_VIEW_HSV_SELECTOR_CLASS))).check(matches(isDisplayed())); + + onColorPickerView().perform(touchCenterMiddle()); + + onColorPickerView() + .onPositiveButton() + .perform(click()); + + currentSelectColor = toolReference.getTool().getDrawPaint().getColor(); + + onColorPickerView() + .performOpenColorPicker(); + + onView(allOf(withId(R.id.color_picker_tab_icon), withBackground(R.drawable.ic_color_picker_tab_rgba))).perform(click()); + onView(withClassName(containsString(TAB_VIEW_RGBA_SELECTOR_CLASS))).check(matches(isDisplayed())); + + onView(withId(R.id.color_picker_color_rgb_hex)).check(matches( + withText( + String.format("#FF%06X", 0xFFFFFF & currentSelectColor)))); + + onView(withId(R.id.color_picker_color_rgb_seekbar_red)).perform(touchCenterLeft()); + onView(withId(R.id.color_picker_color_rgb_seekbar_blue)).perform(touchCenterLeft()); + onView(withId(R.id.color_picker_color_rgb_seekbar_green)).perform(touchCenterLeft()); + onView(withId(R.id.color_picker_color_rgb_seekbar_alpha)).perform(touchCenterRight()); + + onColorPickerView() + .onPositiveButton() + .perform(click()); + + currentSelectColor = toolReference.getTool().getDrawPaint().getColor(); + + onColorPickerView() + .performOpenColorPicker(); + + onView(allOf(withId(R.id.color_picker_tab_icon), withBackground(R.drawable.ic_color_picker_tab_rgba))).perform(click()); + onView(withClassName(containsString(TAB_VIEW_RGBA_SELECTOR_CLASS))).check(matches(isDisplayed())); + + onView(withId(R.id.color_picker_color_rgb_hex)).check(matches( + withText( + String.format("#FF%06X", 0xFFFFFF & currentSelectColor)))); + } + + @Test + public void testHEXEditTextMarkingWrongInput() { + onColorPickerView() + .performOpenColorPicker(); + + onView(allOf(withId(R.id.color_picker_tab_icon), withBackground(R.drawable.ic_color_picker_tab_rgba))).perform(click()); + onView(withClassName(containsString(TAB_VIEW_RGBA_SELECTOR_CLASS))).check(matches(isDisplayed())); + + //set to invalid length of 6 (alpha missing) + onView(withId(R.id.color_picker_color_rgb_hex)).perform(replaceText("#FF0000")); + onView(withId(R.id.color_picker_color_rgb_hex)).check(matches(hasTextColor(R.color.pocketpaint_color_picker_hex_wrong_value_red))); + + //set to invalid value + onView(withId(R.id.color_picker_color_rgb_hex)).perform(replaceText("#FFXXYYZZ")); + onView(withId(R.id.color_picker_color_rgb_hex)).check(matches(hasTextColor(R.color.pocketpaint_color_picker_hex_wrong_value_red))); + + //set to invalid value (# missing) + onView(withId(R.id.color_picker_color_rgb_hex)).perform(replaceText("FF000000")); + onView(withId(R.id.color_picker_color_rgb_hex)).check(matches(hasTextColor(R.color.pocketpaint_color_picker_hex_wrong_value_red))); + } + + @Test + public void testHEXEditTextMarkingCorrectInputAfterWrongInput() { + onColorPickerView() + .performOpenColorPicker(); + + onView(allOf(withId(R.id.color_picker_tab_icon), withBackground(R.drawable.ic_color_picker_tab_rgba))).perform(click()); + onView(withClassName(containsString(TAB_VIEW_RGBA_SELECTOR_CLASS))).check(matches(isDisplayed())); + + //set to invalid length of 6 (alpha missing) + onView(withId(R.id.color_picker_color_rgb_hex)).perform(replaceText("#FF0000")); + + //set correct HEX value + onView(withId(R.id.color_picker_color_rgb_hex)).perform(replaceText("#FF000000")); + onView(withId(R.id.color_picker_color_rgb_hex)).check(matches(hasTextColor(R.color.pocketpaint_color_picker_hex_correct_black))); + } + + @Test + public void testHEXEditTextInitialColorIsSetCorrectly() { + onColorPickerView() + .performOpenColorPicker(); + + onView(allOf(withId(R.id.color_picker_tab_icon), withBackground(R.drawable.ic_color_picker_tab_rgba))).perform(click()); + onView(withClassName(containsString(TAB_VIEW_RGBA_SELECTOR_CLASS))).check(matches(isDisplayed())); + + //Inital text color should be black + onView(withId(R.id.color_picker_color_rgb_hex)).check(matches(hasTextColor(R.color.pocketpaint_color_picker_hex_correct_black))); + } + + @Test + public void testOpenColorPickerOnClickOnColorButton() { + onColorPickerView() + .performOpenColorPicker(); + + onView(withId(R.id.color_picker_base_layout)) + .check(matches(isDisplayed())); + onColorPickerView() + .check(matches(isDisplayed())); + } + + @Test + public void testStandardColorDoesNotChangeOnCancelButtonPress() { + int initialColor = toolReference.getTool().getDrawPaint().getColor(); + + onColorPickerView() + .performOpenColorPicker(); + + onColorPickerView() + .performClickColorPickerPresetSelectorButton(0); + + onColorPickerView() + .onNegativeButton() + .perform(click()); + + onToolProperties() + .checkMatchesColor(initialColor); + } + + @Test + public void testStandardColorDoesChangeOnCancel() { + int initialColor = toolReference.getTool().getDrawPaint().getColor(); + + onColorPickerView() + .performOpenColorPicker(); + onColorPickerView() + .performClickColorPickerPresetSelectorButton(0); + onColorPickerView() + .perform(ViewActions.pressBack()); + onToolProperties() + .checkDoesNotMatchColor(initialColor); + } + + @Test + public void testColorOnlyUpdatesOncePerColorPickerIntent() { + int initialColor = toolReference.getTool().getDrawPaint().getColor(); + + onColorPickerView() + .performOpenColorPicker(); + onColorPickerView() + .performClickColorPickerPresetSelectorButton(0); + onToolProperties() + .checkMatchesColor(initialColor); + onColorPickerView() + .performClickColorPickerPresetSelectorButton(1); + onToolProperties() + .checkMatchesColor(initialColor); + onColorPickerView() + .performClickColorPickerPresetSelectorButton(2); + onToolProperties() + .checkMatchesColor(initialColor); + + onColorPickerView() + .perform(ViewActions.pressBack()); + onToolProperties() + .checkDoesNotMatchColor(initialColor); + } + + @Test + public void testColorPickerRemainsOpenOnOrientationChange() { + onColorPickerView() + .performOpenColorPicker(); + + launchActivityRule.getActivity().setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); + + onColorPickerView().check(matches(isDisplayed())); + } + + @Test + public void testColorPickerTabRestoredOnOrientationChange() { + onColorPickerView() + .performOpenColorPicker(); + onView(allOf(withId(R.id.color_picker_tab_icon), withBackground(R.drawable.ic_color_picker_tab_rgba))) + .perform(click()); + + launchActivityRule.getActivity().setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); + + onView(withClassName(containsString(TAB_VIEW_RGBA_SELECTOR_CLASS))) + .check(matches(isDisplayed())); + } + + @Test + public void testColorPickerInitializesRgbTabTransparentColor() { + TypedArray presetColors = launchActivityRule.getActivity().getResources().obtainTypedArray(R.array.pocketpaint_color_picker_preset_colors); + onColorPickerView() + .performOpenColorPicker(); + onColorPickerView() + .performClickColorPickerPresetSelectorButton(presetColors.length() - 1); + onColorPickerView() + .onPositiveButton() + .perform(click()); + onColorPickerView() + .performOpenColorPicker(); + onView(allOf(withId(R.id.color_picker_tab_icon), withBackground(R.drawable.ic_color_picker_tab_rgba))).perform(click()); + onView(withId(R.id.color_picker_rgb_red_value)).check(matches(withText(Integer.toString(Color.red(Color.TRANSPARENT))))); + onView(withId(R.id.color_picker_rgb_green_value)).check(matches(withText(Integer.toString(Color.green(Color.TRANSPARENT))))); + onView(withId(R.id.color_picker_rgb_blue_value)).check(matches(withText(Integer.toString(Color.blue(Color.TRANSPARENT))))); + onView(withId(R.id.color_picker_rgb_alpha_value)).check(matches( + withText( + Integer.toString( + (int) (Color.alpha(Color.TRANSPARENT) / 2.55f) + ) + ) + )); + presetColors.recycle(); + } + + @Test + public void testInsertInvalidHexInputAndSlideSeekbar() { + onColorPickerView() + .performOpenColorPicker(); + onView(allOf(withId(R.id.color_picker_tab_icon), withBackground(R.drawable.ic_color_picker_tab_rgba))).perform(click()); + onView(withId(R.id.color_picker_color_rgb_hex)).perform(replaceText("#FFFF0000xxxx")); + + onView(withId(R.id.color_picker_color_rgb_seekbar_blue)).perform(touchCenterRight()); + onView(withId(R.id.color_picker_color_rgb_hex)).check(matches( + withText( + String.format("#FF%06X", 0xFFFFFF & 0xFF0000FF)))); + } + + @Test + public void testPipetteButtonIsDisplayed() { + onColorPickerView() + .performOpenColorPicker(); + + onView(withId(R.id.color_picker_pipette_btn)) + .check(matches(isDisplayed())) + .check(matches(withText(R.string.color_picker_pipette))); + } + + @Test + public void testColorViewsAreDisplayed() { + onColorPickerView() + .performOpenColorPicker(); + + onView(withId(R.id.color_picker_new_color_view)) + .check(matches(isDisplayed())) + .check(matches(withBackgroundColor(Color.BLACK))); + + onView(withId(R.id.color_picker_current_color_view)) + .check(matches(isDisplayed())) + .check(matches(withBackgroundColor(Color.BLACK))); + } + + @Test + public void alphaValueIsSetInSliderWhenChangedInSeekBar() { + onColorPickerView() + .performOpenColorPicker(); + onView(allOf(withId(R.id.color_picker_tab_icon), withBackground(R.drawable.ic_color_picker_tab_rgba))).perform(click()); + + // set color to value #7F000000, alpha seekbar 49% + onView(withId(R.id.color_picker_color_rgb_seekbar_alpha)).perform(touchCenterMiddle()); + onView(allOf(withId(R.id.color_picker_tab_icon), withBackground(R.drawable.ic_color_picker_tab_preset))).perform(scrollTo(), click()); + onColorPickerView() + .onPositiveButton() + .perform(click()); + onToolProperties() + .checkMatchesColor(Color.parseColor("#7F000000")); + } + + @Test + public void alphaValueIsSetInSeekBarWhenChangedInSlider() { + onColorPickerView() + .performOpenColorPicker(); + onView(allOf(withId(R.id.color_picker_tab_icon), withBackground(R.drawable.ic_color_picker_tab_preset))).perform(click()); + + // set color to value #80000000, alpha seekbar 50% + onView(withId(R.id.color_alpha_slider)).perform(scrollTo(), touchCenterMiddle()); + + onView(allOf(withId(R.id.color_picker_tab_icon), withBackground(R.drawable.ic_color_picker_tab_rgba))).perform(click()); + onView(withId(R.id.color_picker_rgb_alpha_value)).check(matches( + withText("50") + )); + } + + @Test + public void testPreserveZoomAfterPipetteUsage() { + Perspective perspective = launchActivityRule.getActivity().getPerspective(); + + onDrawingSurfaceView() + .perform(touchAt(DrawingSurfaceLocationProvider.MIDDLE)); + + float scale = 4f; + + perspective.setScale(scale); + + onColorPickerView() + .performOpenColorPicker(); + + onView(withId(R.id.color_picker_pipette_btn)).perform(click()); + onView(withId(R.id.doneAction)).perform(click()); + + onColorPickerView() + .performCloseColorPickerWithDialogButton(); + + assertEquals(scale, perspective.getScale(), Float.MIN_VALUE); + } +} diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/dialog/IndeterminateProgressDialogIntegrationTest.kt b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/dialog/IndeterminateProgressDialogIntegrationTest.kt new file mode 100644 index 0000000000..87a761e234 --- /dev/null +++ b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/dialog/IndeterminateProgressDialogIntegrationTest.kt @@ -0,0 +1,103 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2015 The Catrobat Team + * () + * + * 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. + * + * 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 . + */ +package org.catrobat.paintroid.test.espresso.dialog + +import android.content.pm.ActivityInfo +import android.content.res.Resources +import android.graphics.PointF +import android.os.Build +import androidx.annotation.RequiresApi +import androidx.fragment.app.DialogFragment +import androidx.test.espresso.Espresso +import androidx.test.espresso.assertion.ViewAssertions +import androidx.test.espresso.matcher.ViewMatchers +import androidx.test.espresso.matcher.ViewMatchers.withId +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.rule.ActivityTestRule +import org.catrobat.paintroid.MainActivity +import org.catrobat.paintroid.R +import org.catrobat.paintroid.dialog.IndeterminateProgressDialog +import org.catrobat.paintroid.test.espresso.util.UiInteractions +import org.catrobat.paintroid.test.utils.ScreenshotOnFailRule +import org.junit.After +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith + +@RunWith(AndroidJUnit4::class) +class IndeterminateProgressDialogIntegrationTest { + @get:Rule + var activityTestRule = ActivityTestRule(MainActivity::class.java) + + @get:Rule + var screenshotOnFailRule = ScreenshotOnFailRule() + private lateinit var dialog: DialogFragment + + @Before + fun setUp() { + dialog = IndeterminateProgressDialog() + dialog.show(activityTestRule.activity.supportFragmentManager, "PROGRESS_TAG_TEST") + } + + @After + fun tearDown() { + dialog.dismiss() + } + + @RequiresApi(Build.VERSION_CODES.N) + @Test + fun testDialogIsShown() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + Espresso.onView(withId(R.id.pocketpaint_progress_bar)) + .check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + } + } + + @RequiresApi(Build.VERSION_CODES.N) + @Test + fun testDialogIsNotCancelableOnBack() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + Espresso.pressBack() + Espresso.onView(withId(R.id.pocketpaint_progress_bar)) + .check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + } + } + + @RequiresApi(Build.VERSION_CODES.N) + @Test + fun testDialogIsNotCancelable() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + val metrics = Resources.getSystem().displayMetrics + val point = PointF(-metrics.widthPixels / 4f, -metrics.heightPixels / 4f) + Espresso.onView(withId(R.id.pocketpaint_progress_bar)) + .perform(UiInteractions.touchAt(point)) + .check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + } + } + + @RequiresApi(Build.VERSION_CODES.N) + @Test + fun testDialogIsRotateAble() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + activityTestRule.activity.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE + activityTestRule.activity.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT + } + } +} diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/intro/IntroIntegrationTest.java b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/intro/IntroIntegrationTest.java new file mode 100644 index 0000000000..560f0884ad --- /dev/null +++ b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/intro/IntroIntegrationTest.java @@ -0,0 +1,136 @@ +package org.catrobat.paintroid.test.espresso.intro; + +import org.catrobat.paintroid.R; +import org.catrobat.paintroid.WelcomeActivity; +import org.catrobat.paintroid.intro.IntroPageViewAdapter; +import org.catrobat.paintroid.test.utils.ScreenshotOnFailRule; +import org.catrobat.paintroid.tools.ToolType; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import androidx.test.ext.junit.runners.AndroidJUnit4; +import androidx.test.rule.ActivityTestRule; +import androidx.viewpager.widget.ViewPager; + +import static org.junit.Assert.assertTrue; + +import static androidx.test.espresso.Espresso.onView; +import static androidx.test.espresso.action.ViewActions.click; +import static androidx.test.espresso.action.ViewActions.swipeLeft; +import static androidx.test.espresso.assertion.ViewAssertions.matches; +import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed; +import static androidx.test.espresso.matcher.ViewMatchers.withId; +import static androidx.test.espresso.matcher.ViewMatchers.withText; + +@RunWith(AndroidJUnit4.class) +public class IntroIntegrationTest { + + @Rule + public ActivityTestRule activityTestRule = new ActivityTestRule<>(WelcomeActivity.class); + + @Rule + public ScreenshotOnFailRule screenshotOnFailRule = new ScreenshotOnFailRule(); + + @Test + public void testIntroWelcomePage() { + onView(withText(R.string.welcome_to_pocket_paint)).check(matches(isDisplayed())); + onView(withText(R.string.intro_welcome_text)).check(matches(isDisplayed())); + onView(withText(R.string.next)).check(matches(isDisplayed())); + onView(withText(R.string.skip)).check(matches(isDisplayed())); + } + + @Test + public void testOnSkipPressedActivityFinished() { + onView(withId(R.id.pocketpaint_btn_skip)).perform(click()); + assertTrue(activityTestRule.getActivity().isFinishing()); + } + + @Test + public void testOnLetsGoPressedActivityFinished() { + ViewPager viewPager = activityTestRule.getActivity().viewPager; + IntroPageViewAdapter adapter = (IntroPageViewAdapter) viewPager.getAdapter(); + + for (int i = 0; i < adapter.layouts.length - 1; i++) { + onView(withId(R.id.pocketpaint_btn_next)).perform(click()); + } + + onView(withId(R.id.pocketpaint_btn_next)).perform(click()); + assertTrue(activityTestRule.getActivity().isFinishing()); + } + + @Test + public void testIntroToolsPageShowDescriptionOnPress() { + ViewPager viewPager = activityTestRule.getActivity().viewPager; + IntroPageViewAdapter adapter = (IntroPageViewAdapter) viewPager.getAdapter(); + + for (int layout: adapter.layouts) { + if (layout == R.layout.pocketpaint_slide_intro_tools_selection) { + break; + } + onView(withId(R.id.pocketpaint_btn_next)).perform(click()); + } + + onView(withId(R.id.pocketpaint_textview_intro_tools_header)) + .check(matches(isDisplayed())); + + for (ToolType toolType: ToolType.values()) { + if (!toolType.equals(ToolType.UNDO) && !toolType.equals(ToolType.REDO) && !toolType.equals(ToolType.LAYER) && !toolType.equals(ToolType.COLORCHOOSER)) { + onView(withId(toolType.getToolButtonID())).perform(click()); + onView(withText(toolType.getHelpTextResource())).check(matches(isDisplayed())); + } + } + } + + @Test + public void testIntroViewPagerSwipeChangePage() { + onView(withId(R.id.pocketpaint_intro_welcome_head)) + .check(matches(isDisplayed())); + + onView(withId(R.id.pocketpaint_view_pager)).perform(swipeLeft()); + + onView(withId(R.id.pocketpaint_intro_possibilities_head)) + .check(matches(isDisplayed())); + + onView(withId(R.id.pocketpaint_view_pager)).perform(swipeLeft()); + + onView(withId(R.id.pocketpaint_textview_intro_tools_header)) + .check(matches(isDisplayed())); + + onView(withId(R.id.pocketpaint_view_pager)).perform(swipeLeft()); + + onView(withId(R.id.pocketpaint_intro_landscape_head)) + .check(matches(isDisplayed())); + + onView(withId(R.id.pocketpaint_view_pager)).perform(swipeLeft()); + + onView(withId(R.id.pocketpaint_intro_started_head)) + .check(matches(isDisplayed())); + } + + @Test + public void testIntroViewPagerNextButtonChangePage() { + onView(withId(R.id.pocketpaint_intro_welcome_head)) + .check(matches(isDisplayed())); + + onView(withId(R.id.pocketpaint_btn_next)).perform(click()); + + onView(withId(R.id.pocketpaint_intro_possibilities_head)) + .check(matches(isDisplayed())); + + onView(withId(R.id.pocketpaint_btn_next)).perform(click()); + + onView(withId(R.id.pocketpaint_textview_intro_tools_header)) + .check(matches(isDisplayed())); + + onView(withId(R.id.pocketpaint_btn_next)).perform(click()); + + onView(withId(R.id.pocketpaint_intro_landscape_head)) + .check(matches(isDisplayed())); + + onView(withId(R.id.pocketpaint_btn_next)).perform(click()); + + onView(withId(R.id.pocketpaint_intro_started_head)) + .check(matches(isDisplayed())); + } +} diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/rtl/ButtonLayersRtlLayoutTest.java b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/rtl/ButtonLayersRtlLayoutTest.java new file mode 100644 index 0000000000..2636bd161b --- /dev/null +++ b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/rtl/ButtonLayersRtlLayoutTest.java @@ -0,0 +1,72 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2015 The Catrobat Team + * () + *

+ * 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. + *

+ * 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 . + */ + +package org.catrobat.paintroid.test.espresso.rtl; + +import org.catrobat.paintroid.MainActivity; +import org.catrobat.paintroid.R; +import org.catrobat.paintroid.test.espresso.rtl.util.RtlActivityTestRule; +import org.catrobat.paintroid.test.utils.ScreenshotOnFailRule; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import androidx.test.ext.junit.runners.AndroidJUnit4; +import androidx.test.rule.ActivityTestRule; + +import static org.catrobat.paintroid.test.espresso.util.wrappers.BottomNavigationViewInteraction.onBottomNavigationView; + +import static androidx.test.espresso.Espresso.onView; +import static androidx.test.espresso.action.ViewActions.click; +import static androidx.test.espresso.assertion.ViewAssertions.matches; +import static androidx.test.espresso.matcher.ViewMatchers.isClickable; +import static androidx.test.espresso.matcher.ViewMatchers.isCompletelyDisplayed; +import static androidx.test.espresso.matcher.ViewMatchers.withId; + +@RunWith(AndroidJUnit4.class) +public class ButtonLayersRtlLayoutTest { + @Rule + public ActivityTestRule mainActivityActivityTestRule = + new RtlActivityTestRule<>(MainActivity.class, "ar"); + + @Rule + public ScreenshotOnFailRule screenshotOnFailRule = new ScreenshotOnFailRule(); + + @Test + public void testButtonLayers() { + onBottomNavigationView() + .onLayersClicked(); + + onView(withId(R.id.pocketpaint_nav_view_layer)) + .check(matches(isCompletelyDisplayed())); + + onView(withId(R.id.pocketpaint_layer_side_nav_button_add)) + .check(matches(isClickable())) + .perform(click()); + onView(withId(R.id.pocketpaint_layer_side_nav_button_delete)) + .check(matches(isClickable())) + .perform(click()); + onView(withId(R.id.pocketpaint_stroke_ibtn_circle)) + .check(matches(isClickable())) + .perform(click()); + onView(withId(R.id.pocketpaint_stroke_ibtn_rect)) + .check(matches(isClickable())) + .perform(click()); + } +} diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/rtl/HindiNumberFormatTest.java b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/rtl/HindiNumberFormatTest.java new file mode 100644 index 0000000000..58cc109171 --- /dev/null +++ b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/rtl/HindiNumberFormatTest.java @@ -0,0 +1,104 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2015 The Catrobat Team + * () + * + * 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. + * + * 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 . + */ + +package org.catrobat.paintroid.test.espresso.rtl; + +import org.catrobat.paintroid.MainActivity; +import org.catrobat.paintroid.R; +import org.catrobat.paintroid.test.espresso.rtl.util.RtlActivityTestRule; +import org.catrobat.paintroid.test.utils.ScreenshotOnFailRule; +import org.catrobat.paintroid.tools.ToolType; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import androidx.test.ext.junit.runners.AndroidJUnit4; +import androidx.test.rule.ActivityTestRule; + +import static org.catrobat.paintroid.test.espresso.util.UiMatcher.withBackground; +import static org.catrobat.paintroid.test.espresso.util.wrappers.ColorPickerViewInteraction.onColorPickerView; +import static org.catrobat.paintroid.test.espresso.util.wrappers.ToolBarViewInteraction.onToolBarView; +import static org.hamcrest.Matchers.allOf; +import static org.hamcrest.Matchers.containsString; + +import static androidx.test.espresso.Espresso.onView; +import static androidx.test.espresso.action.ViewActions.click; +import static androidx.test.espresso.assertion.ViewAssertions.matches; +import static androidx.test.espresso.matcher.ViewMatchers.withId; +import static androidx.test.espresso.matcher.ViewMatchers.withText; + +@RunWith(AndroidJUnit4.class) +public class HindiNumberFormatTest { + private static final String EXPECTED_RED_VALUE = "٢٤٠"; + private static final String EXPECTED_GREEN_VALUE = "٢٢٨"; + private static final String EXPECTED_BLAU_VALUE = "١٦٨"; + private static final String EXPECTED_ALFA_VALUE = "١٠٠"; + private static final String EXPECTED_STROKE_WIDTH_VALUE = "٢٥"; + private static final String EXPECTED_COLOR_TOLERANCE_VALUE = "١٢"; + @Rule + public ActivityTestRule launchActivityRule = new RtlActivityTestRule<>(MainActivity.class, "ar"); + + @Rule + public ScreenshotOnFailRule screenshotOnFailRule = new ScreenshotOnFailRule(); + + @Test + public void testHindiNumberAtTool() { + onToolBarView() + .performSelectTool(ToolType.BRUSH); + onView(withId(R.id.pocketpaint_stroke_width_width_text)) + .check(matches(withText(containsString(EXPECTED_STROKE_WIDTH_VALUE)))); + + onToolBarView() + .performSelectTool(ToolType.LINE); + onView(withId(R.id.pocketpaint_stroke_width_width_text)) + .check(matches(withText(containsString(EXPECTED_STROKE_WIDTH_VALUE)))); + + onToolBarView() + .performSelectTool(ToolType.CURSOR); + onView(withId(R.id.pocketpaint_stroke_width_width_text)) + .check(matches(withText(containsString(EXPECTED_STROKE_WIDTH_VALUE)))); + + onToolBarView() + .performSelectTool(ToolType.FILL); + onView(withId(R.id.pocketpaint_fill_tool_dialog_color_tolerance_input)) + .check(matches(withText(containsString(EXPECTED_COLOR_TOLERANCE_VALUE)))); + + onToolBarView() + .performSelectTool(ToolType.ERASER); + onView(withId(R.id.pocketpaint_stroke_width_width_text)) + .check(matches(withText(containsString(EXPECTED_STROKE_WIDTH_VALUE)))); + } + + @Test + public void testHindiNumberAtColorDialog() { + onColorPickerView() + .performOpenColorPicker() + .performClickColorPickerPresetSelectorButton(7); + onView(allOf(withId(R.id.color_picker_tab_icon), withBackground(R.drawable.ic_color_picker_tab_rgba))) + .perform(click()); + onView(withId(R.id.color_picker_rgb_red_value)) + .check(matches(withText(containsString(EXPECTED_RED_VALUE)))); + onView(withId(R.id.color_picker_rgb_green_value)) + .check(matches(withText(containsString(EXPECTED_GREEN_VALUE)))); + onView(withId(R.id.color_picker_rgb_blue_value)) + .check(matches(withText(containsString(EXPECTED_BLAU_VALUE)))); + onView(withId(R.id.color_picker_rgb_alpha_value)) + .check(matches(withText(containsString(EXPECTED_ALFA_VALUE)))); + } +} diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/rtl/util/RtlActivityTestRule.java b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/rtl/util/RtlActivityTestRule.java new file mode 100644 index 0000000000..f158afb772 --- /dev/null +++ b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/rtl/util/RtlActivityTestRule.java @@ -0,0 +1,56 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2015 The Catrobat Team + * () + * + * 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. + * + * 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 . + */ + +package org.catrobat.paintroid.test.espresso.rtl.util; + +import android.app.Activity; +import android.content.Context; + +import org.catrobat.paintroid.test.espresso.util.LanguageSupport; + +import java.util.Locale; + +import androidx.test.platform.app.InstrumentationRegistry; +import androidx.test.rule.ActivityTestRule; + +public class RtlActivityTestRule extends ActivityTestRule { + private final String language; + + public RtlActivityTestRule(Class activityClass, String language) { + super(activityClass); + this.language = language; + } + + @Override + protected void beforeActivityLaunched() { + super.beforeActivityLaunched(); + + Locale locale = new Locale(language); + Context targetContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); + LanguageSupport.setLocale(targetContext, locale); + } + + @Override + protected void afterActivityFinished() { + super.afterActivityFinished(); + + Context targetContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); + LanguageSupport.setLocale(targetContext, new Locale("en")); + } +} diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/rtl/util/RtlUiTestUtils.java b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/rtl/util/RtlUiTestUtils.java new file mode 100644 index 0000000000..ff3573bdcf --- /dev/null +++ b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/rtl/util/RtlUiTestUtils.java @@ -0,0 +1,31 @@ +/** + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2015 The Catrobat Team + * () + * + * 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. + * + * 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 . + */ + +package org.catrobat.paintroid.test.espresso.rtl.util; + +public final class RtlUiTestUtils { + private RtlUiTestUtils() { + throw new AssertionError(); + } + + public static boolean checkTextDirection(String string) { + return Character.getDirectionality(string.charAt(0)) == Character.DIRECTIONALITY_RIGHT_TO_LEFT + || Character.getDirectionality(string.charAt(0)) == Character.DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC; + } +} diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/tools/EraserToolIntegrationTest.java b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/tools/EraserToolIntegrationTest.java new file mode 100644 index 0000000000..8c77c5204c --- /dev/null +++ b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/tools/EraserToolIntegrationTest.java @@ -0,0 +1,266 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2015 The Catrobat Team + * () + * + * 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. + * + * 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 . + */ + +package org.catrobat.paintroid.test.espresso.tools; + +import android.graphics.Color; +import android.graphics.Paint.Cap; + +import org.catrobat.paintroid.MainActivity; +import org.catrobat.paintroid.test.espresso.util.BitmapLocationProvider; +import org.catrobat.paintroid.test.espresso.util.DrawingSurfaceLocationProvider; +import org.catrobat.paintroid.test.utils.ScreenshotOnFailRule; +import org.catrobat.paintroid.tools.ToolType; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import androidx.test.ext.junit.runners.AndroidJUnit4; +import androidx.test.rule.ActivityTestRule; + +import static org.catrobat.paintroid.test.espresso.util.EspressoUtils.DEFAULT_STROKE_WIDTH; +import static org.catrobat.paintroid.test.espresso.util.UiInteractions.setProgress; +import static org.catrobat.paintroid.test.espresso.util.UiInteractions.touchAt; +import static org.catrobat.paintroid.test.espresso.util.UiMatcher.withProgress; +import static org.catrobat.paintroid.test.espresso.util.wrappers.BrushPickerViewInteraction.onBrushPickerView; +import static org.catrobat.paintroid.test.espresso.util.wrappers.DrawingSurfaceInteraction.onDrawingSurfaceView; +import static org.catrobat.paintroid.test.espresso.util.wrappers.ToolBarViewInteraction.onToolBarView; +import static org.catrobat.paintroid.test.espresso.util.wrappers.ToolPropertiesInteraction.onToolProperties; +import static org.hamcrest.Matchers.allOf; + +import static androidx.test.espresso.action.ViewActions.click; +import static androidx.test.espresso.assertion.ViewAssertions.matches; +import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed; +import static androidx.test.espresso.matcher.ViewMatchers.isSelected; +import static androidx.test.espresso.matcher.ViewMatchers.withText; + +@RunWith(AndroidJUnit4.class) +public class EraserToolIntegrationTest { + + private static final String TEXT_DEFAULT_STROKE_WIDTH = Integer.toString(DEFAULT_STROKE_WIDTH); + + @Rule + public ActivityTestRule launchActivityRule = new ActivityTestRule<>(MainActivity.class); + + @Rule + public ScreenshotOnFailRule screenshotOnFailRule = new ScreenshotOnFailRule(); + + @Test + public void testEraseOnEmptyBitmap() { + onDrawingSurfaceView() + .checkPixelColor(Color.TRANSPARENT, BitmapLocationProvider.MIDDLE); + + onToolBarView() + .performSelectTool(ToolType.ERASER); + + onDrawingSurfaceView() + .perform(touchAt(DrawingSurfaceLocationProvider.MIDDLE)); + + onDrawingSurfaceView() + .checkPixelColor(Color.TRANSPARENT, BitmapLocationProvider.MIDDLE); + } + + @Test + public void testEraseSinglePixel() { + onDrawingSurfaceView() + .perform(touchAt(DrawingSurfaceLocationProvider.MIDDLE)); + + onDrawingSurfaceView() + .checkPixelColor(Color.BLACK, BitmapLocationProvider.MIDDLE); + + onToolBarView() + .performSelectTool(ToolType.ERASER); + + onDrawingSurfaceView() + .perform(touchAt(DrawingSurfaceLocationProvider.MIDDLE)); + + onDrawingSurfaceView() + .checkPixelColor(Color.TRANSPARENT, BitmapLocationProvider.MIDDLE); + } + + @Test + public void testSwitchingBetweenBrushAndEraser() { + onDrawingSurfaceView() + .perform(touchAt(DrawingSurfaceLocationProvider.MIDDLE)); + + onDrawingSurfaceView() + .checkPixelColor(Color.BLACK, BitmapLocationProvider.MIDDLE); + + onToolBarView() + .performSelectTool(ToolType.ERASER); + + onDrawingSurfaceView() + .perform(touchAt(DrawingSurfaceLocationProvider.MIDDLE)); + + onDrawingSurfaceView() + .checkPixelColor(Color.TRANSPARENT, BitmapLocationProvider.MIDDLE); + + onToolBarView() + .performSelectTool(ToolType.BRUSH); + + onDrawingSurfaceView() + .perform(touchAt(DrawingSurfaceLocationProvider.MIDDLE)); + + onDrawingSurfaceView() + .checkPixelColor(Color.BLACK, BitmapLocationProvider.MIDDLE); + + onToolBarView() + .performSelectTool(ToolType.ERASER); + + onDrawingSurfaceView() + .perform(touchAt(DrawingSurfaceLocationProvider.MIDDLE)); + + onDrawingSurfaceView() + .checkPixelColor(Color.TRANSPARENT, BitmapLocationProvider.MIDDLE); + } + + @Test + public void testChangeEraserBrushSize() { + int newStrokeWidth = 90; + onBrushPickerView().onStrokeWidthSeekBar() + .perform(setProgress(newStrokeWidth)) + .check(matches(withProgress(newStrokeWidth))); + + onDrawingSurfaceView() + .perform(touchAt(DrawingSurfaceLocationProvider.MIDDLE)); + + onDrawingSurfaceView() + .checkPixelColor(Color.BLACK, BitmapLocationProvider.MIDDLE); + + onToolBarView() + .performSelectTool(ToolType.ERASER); + + onBrushPickerView().onStrokeWidthSeekBar() + .check(matches(allOf(isDisplayed(), withProgress(newStrokeWidth)))); + onBrushPickerView().onStrokeWidthTextView() + .check(matches(allOf(isDisplayed(), withText(Integer.toString(newStrokeWidth))))); + + newStrokeWidth = 80; + onBrushPickerView().onStrokeWidthSeekBar() + .perform(setProgress(newStrokeWidth)) + .check(matches(withProgress(newStrokeWidth))); + + onToolBarView() + .performCloseToolOptionsView(); + + onToolProperties() + .checkStrokeWidth(80); + + onDrawingSurfaceView() + .perform(touchAt(DrawingSurfaceLocationProvider.MIDDLE)); + + onDrawingSurfaceView() + .checkPixelColor(Color.TRANSPARENT, BitmapLocationProvider.MIDDLE); + } + + @Test + public void testChangeEraserBrushForm() { + onBrushPickerView() + .onStrokeWidthSeekBar() + .perform(setProgress(70)); + + onDrawingSurfaceView() + .perform(touchAt(DrawingSurfaceLocationProvider.MIDDLE)); + + onDrawingSurfaceView() + .checkPixelColor(Color.BLACK, BitmapLocationProvider.MIDDLE); + + onBrushPickerView() + .onStrokeWidthSeekBar() + .perform(setProgress(50)); + + onToolBarView() + .performSelectTool(ToolType.ERASER); + + onBrushPickerView().onStrokeCapSquareView() + .perform(click()); + + onToolBarView() + .performCloseToolOptionsView(); + + onToolProperties() + .checkCap(Cap.SQUARE); + + onDrawingSurfaceView() + .perform(touchAt(DrawingSurfaceLocationProvider.MIDDLE)); + + onDrawingSurfaceView() + .checkPixelColor(Color.TRANSPARENT, BitmapLocationProvider.MIDDLE); + } + + @Test + public void testRestorePreviousToolSettings() { + onDrawingSurfaceView() + .perform(touchAt(DrawingSurfaceLocationProvider.MIDDLE)); + + onDrawingSurfaceView() + .checkPixelColor(Color.BLACK, BitmapLocationProvider.MIDDLE); + + onToolBarView() + .performSelectTool(ToolType.ERASER); + + onBrushPickerView().onStrokeWidthTextView() + .check(matches(allOf(isDisplayed(), withText(TEXT_DEFAULT_STROKE_WIDTH)))); + onBrushPickerView().onStrokeWidthSeekBar() + .check(matches(allOf(isDisplayed(), withProgress(DEFAULT_STROKE_WIDTH)))); + + int newStrokeWidth = 80; + onBrushPickerView().onStrokeWidthSeekBar() + .perform(setProgress(newStrokeWidth)) + .check(matches(withProgress(newStrokeWidth))); + + onBrushPickerView().onStrokeCapSquareView() + .perform(click()); + + onToolProperties() + .checkStrokeWidth(newStrokeWidth) + .checkCap(Cap.SQUARE); + + onToolBarView() + .performCloseToolOptionsView() + .performOpenToolOptionsView(); + + onBrushPickerView().onStrokeWidthSeekBar() + .check(matches(withProgress(newStrokeWidth))); + + int eraserStrokeWidth = 60; + onBrushPickerView().onStrokeWidthSeekBar() + .perform(setProgress(eraserStrokeWidth)) + .check(matches(withProgress(eraserStrokeWidth))); + + onBrushPickerView().onStrokeCapRoundView() + .perform(click()); + + onToolProperties() + .checkStrokeWidth(eraserStrokeWidth) + .checkCap(Cap.ROUND); + + onToolBarView() + .performSelectTool(ToolType.BRUSH); + + onBrushPickerView().onStrokeWidthSeekBar() + .check(matches(withProgress(eraserStrokeWidth))); + onBrushPickerView().onStrokeCapRoundView() + .check(matches(isSelected())); + + onToolProperties() + .checkCap(Cap.ROUND) + .checkStrokeWidth(eraserStrokeWidth); + } +} diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/tools/FillToolIntegrationTest.java b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/tools/FillToolIntegrationTest.java new file mode 100644 index 0000000000..bedb83ab37 --- /dev/null +++ b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/tools/FillToolIntegrationTest.java @@ -0,0 +1,268 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2015 The Catrobat Team + * () + * + * 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. + * + * 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 . + */ + +package org.catrobat.paintroid.test.espresso.tools; + +import android.graphics.Color; +import android.net.Uri; + +import org.catrobat.paintroid.MainActivity; +import org.catrobat.paintroid.R; +import org.catrobat.paintroid.test.espresso.util.BitmapLocationProvider; +import org.catrobat.paintroid.test.espresso.util.DrawingSurfaceLocationProvider; +import org.catrobat.paintroid.test.utils.ScreenshotOnFailRule; +import org.catrobat.paintroid.tools.ToolReference; +import org.catrobat.paintroid.tools.ToolType; +import org.catrobat.paintroid.tools.implementation.FillTool; +import org.catrobat.paintroid.ui.Perspective; +import org.junit.Before; +import org.junit.Ignore; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.io.File; + +import androidx.test.espresso.ViewInteraction; +import androidx.test.ext.junit.runners.AndroidJUnit4; +import androidx.test.rule.ActivityTestRule; + +import static org.catrobat.paintroid.test.espresso.util.UiInteractions.swipeAccurate; +import static org.catrobat.paintroid.test.espresso.util.UiInteractions.touchAt; +import static org.catrobat.paintroid.test.espresso.util.UiMatcher.withProgress; +import static org.catrobat.paintroid.test.espresso.util.wrappers.DrawingSurfaceInteraction.onDrawingSurfaceView; +import static org.catrobat.paintroid.test.espresso.util.wrappers.ToolBarViewInteraction.onToolBarView; +import static org.catrobat.paintroid.test.espresso.util.wrappers.ToolPropertiesInteraction.onToolProperties; +import static org.catrobat.paintroid.test.espresso.util.wrappers.TopBarViewInteraction.onTopBarView; +import static org.catrobat.paintroid.tools.implementation.FillToolKt.DEFAULT_TOLERANCE_IN_PERCENT; +import static org.junit.Assert.assertEquals; + +import static androidx.test.espresso.Espresso.onView; +import static androidx.test.espresso.action.ViewActions.closeSoftKeyboard; +import static androidx.test.espresso.action.ViewActions.replaceText; +import static androidx.test.espresso.assertion.ViewAssertions.matches; +import static androidx.test.espresso.matcher.ViewMatchers.withId; +import static androidx.test.espresso.matcher.ViewMatchers.withText; + +@RunWith(AndroidJUnit4.class) +public class FillToolIntegrationTest { + + private static final double TOLERANCE_DELTA = 0.05d; + + @Rule + public ActivityTestRule launchActivityRule = new ActivityTestRule<>(MainActivity.class); + + @Rule + public ScreenshotOnFailRule screenshotOnFailRule = new ScreenshotOnFailRule(); + + private Perspective perspective; + private ToolReference toolReference; + private MainActivity mainActivity; + + @Before + public void setUp() { + mainActivity = launchActivityRule.getActivity(); + perspective = mainActivity.perspective; + toolReference = mainActivity.toolReference; + + onToolBarView() + .performSelectTool(ToolType.FILL); + } + + @Test + public void testFloodFillIfImageLoaded() { + mainActivity.model.setSavedPictureUri(Uri.fromFile(new File("dummy"))); + + onToolProperties() + .checkMatchesColor(Color.BLACK); + + onDrawingSurfaceView() + .perform(touchAt(DrawingSurfaceLocationProvider.MIDDLE)); + + onDrawingSurfaceView() + .checkPixelColor(Color.BLACK, BitmapLocationProvider.MIDDLE); + + mainActivity.model.setSavedPictureUri(null); + } + + @Test + public void testBitmapIsFilled() { + onToolProperties() + .checkMatchesColor(Color.BLACK); + onDrawingSurfaceView() + .perform(touchAt(DrawingSurfaceLocationProvider.MIDDLE)); + onDrawingSurfaceView() + .checkPixelColor(Color.BLACK, BitmapLocationProvider.MIDDLE); + } + + @Test + public void testNothingHappensWhenClickedOutsideDrawingArea() { + perspective.multiplyScale(.5f); + onToolProperties() + .checkMatchesColor(Color.BLACK); + onDrawingSurfaceView() + .perform(touchAt(DrawingSurfaceLocationProvider.OUTSIDE_MIDDLE_RIGHT)); + onDrawingSurfaceView() + .checkPixelColor(Color.TRANSPARENT, BitmapLocationProvider.MIDDLE); + } + + @Test + public void testOnlyFillInnerArea() { + onToolBarView() + .performSelectTool(ToolType.BRUSH); + + onToolProperties() + .checkMatchesColor(Color.BLACK); + + onDrawingSurfaceView() + .perform(swipeAccurate(DrawingSurfaceLocationProvider.HALFWAY_TOP_MIDDLE, DrawingSurfaceLocationProvider.HALFWAY_RIGHT_MIDDLE)) + .perform(swipeAccurate(DrawingSurfaceLocationProvider.HALFWAY_RIGHT_MIDDLE, DrawingSurfaceLocationProvider.HALFWAY_BOTTOM_MIDDLE)) + .perform(swipeAccurate(DrawingSurfaceLocationProvider.HALFWAY_BOTTOM_MIDDLE, DrawingSurfaceLocationProvider.HALFWAY_LEFT_MIDDLE)) + .perform(swipeAccurate(DrawingSurfaceLocationProvider.HALFWAY_LEFT_MIDDLE, DrawingSurfaceLocationProvider.HALFWAY_TOP_MIDDLE)); + + onToolBarView() + .performSelectTool(ToolType.FILL); + + onToolProperties() + .setColorResource(R.color.pocketpaint_color_picker_green1); + + onDrawingSurfaceView() + .perform(touchAt(DrawingSurfaceLocationProvider.MIDDLE)); + + onDrawingSurfaceView() + .checkPixelColorResource(R.color.pocketpaint_color_picker_green1, BitmapLocationProvider.MIDDLE) + .checkPixelColor(Color.TRANSPARENT, BitmapLocationProvider.MIDDLE_RIGHT) + .checkPixelColor(Color.BLACK, BitmapLocationProvider.HALFWAY_RIGHT_MIDDLE); + } + + @Test + public void testFillToolOptionsDialog() { + FillTool fillTool = (FillTool) toolReference.getTool(); + assertEquals( + "Wrong fill tool member value for color tolerance", + fillTool.getToleranceAbsoluteValue(DEFAULT_TOLERANCE_IN_PERCENT), + fillTool.colorTolerance, + TOLERANCE_DELTA + ); + + onToolBarView() + .performClickSelectedToolButton(); + + final ViewInteraction colorToleranceInput = onView(withId(R.id.pocketpaint_fill_tool_dialog_color_tolerance_input)); + final ViewInteraction colorToleranceSeekBar = onView(withId(R.id.pocketpaint_color_tolerance_seek_bar)); + + String testToleranceText = "100"; + + colorToleranceInput.check(matches(withText(Integer.toString(DEFAULT_TOLERANCE_IN_PERCENT)))); + + colorToleranceInput.perform(replaceText(testToleranceText), closeSoftKeyboard()); + + colorToleranceInput.check(matches(withText(testToleranceText))); + colorToleranceSeekBar.check(matches(withProgress(Integer.parseInt(testToleranceText)))); + + float expectedAbsoluteTolerance = fillTool.getToleranceAbsoluteValue(100); + assertEquals("Wrong fill tool member value for color tolerance", expectedAbsoluteTolerance, fillTool.colorTolerance, TOLERANCE_DELTA); + + // Close tool options + onToolBarView() + .performClickSelectedToolButton(); + } + + @Test + public void testFillToolDialogAfterToolSwitch() { + FillTool fillTool = (FillTool) toolReference.getTool(); + + onToolBarView() + .performClickSelectedToolButton(); + + final ViewInteraction colorToleranceInput = onView(withId(R.id.pocketpaint_fill_tool_dialog_color_tolerance_input)); + final ViewInteraction colorToleranceSeekBar = onView(withId(R.id.pocketpaint_color_tolerance_seek_bar)); + + int toleranceInPercent = 50; + + colorToleranceInput.perform(replaceText(String.valueOf(toleranceInPercent))); + + float expectedAbsoluteTolerance = fillTool.getToleranceAbsoluteValue(toleranceInPercent); + + assertEquals("Wrong fill tool member value for color tolerance", expectedAbsoluteTolerance, fillTool.colorTolerance, TOLERANCE_DELTA); + + // Close tool options + onToolBarView() + .performClickSelectedToolButton(); + + onToolBarView() + .performSelectTool(ToolType.BRUSH); + + onToolBarView() + .performSelectTool(ToolType.FILL); + onToolBarView() + .performClickSelectedToolButton(); + + colorToleranceInput.check(matches(withText(Integer.toString(DEFAULT_TOLERANCE_IN_PERCENT)))); + colorToleranceSeekBar.check(matches(withProgress(DEFAULT_TOLERANCE_IN_PERCENT))); + } + + @Ignore("Fails on Jenkins, trying out if everything works without this test or if error is due to a bug on Jenkins") + @Test + public void testFillToolUndoRedoWithTolerance() { + onToolBarView() + .performSelectTool(ToolType.BRUSH); + + onDrawingSurfaceView() + .checkPixelColor(Color.TRANSPARENT, BitmapLocationProvider.MIDDLE) + .perform(touchAt(DrawingSurfaceLocationProvider.MIDDLE)); + + onDrawingSurfaceView() + .checkPixelColor(Color.BLACK, BitmapLocationProvider.MIDDLE); + + onToolProperties() + .setColorResource(R.color.pocketpaint_color_picker_brown2) + .checkMatchesColorResource(R.color.pocketpaint_color_picker_brown2); + + onToolBarView() + .performSelectTool(ToolType.FILL) + .performOpenToolOptionsView(); + + onView(withId(R.id.pocketpaint_fill_tool_dialog_color_tolerance_input)) + .perform(replaceText(String.valueOf(100))); + + onToolBarView() + .performCloseToolOptionsView(); + + onDrawingSurfaceView() + .perform(touchAt(DrawingSurfaceLocationProvider.MIDDLE)); + onDrawingSurfaceView() + .checkPixelColorResource(R.color.pocketpaint_color_picker_brown2, BitmapLocationProvider.MIDDLE) + .checkPixelColorResource(R.color.pocketpaint_color_picker_brown2, BitmapLocationProvider.HALFWAY_RIGHT_MIDDLE); + + onTopBarView() + .performUndo(); + + onDrawingSurfaceView() + .checkPixelColor(Color.BLACK, BitmapLocationProvider.MIDDLE) + .checkPixelColor(Color.TRANSPARENT, BitmapLocationProvider.HALFWAY_RIGHT_MIDDLE); + + onTopBarView() + .performRedo(); + + onDrawingSurfaceView() + .checkPixelColorResource(R.color.pocketpaint_color_picker_brown2, BitmapLocationProvider.MIDDLE) + .checkPixelColorResource(R.color.pocketpaint_color_picker_brown2, BitmapLocationProvider.HALFWAY_RIGHT_MIDDLE); + } +} diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/tools/FlipToolIntegrationTest.java b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/tools/FlipToolIntegrationTest.java new file mode 100644 index 0000000000..4c42807ca6 --- /dev/null +++ b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/tools/FlipToolIntegrationTest.java @@ -0,0 +1,96 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2015 The Catrobat Team + * () + * + * 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. + * + * 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 . + */ + +package org.catrobat.paintroid.test.espresso.tools; + +import android.graphics.Color; + +import org.catrobat.paintroid.MainActivity; +import org.catrobat.paintroid.test.espresso.util.BitmapLocationProvider; +import org.catrobat.paintroid.test.espresso.util.DrawingSurfaceLocationProvider; +import org.catrobat.paintroid.test.utils.ScreenshotOnFailRule; +import org.catrobat.paintroid.tools.ToolType; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import androidx.test.ext.junit.runners.AndroidJUnit4; +import androidx.test.rule.ActivityTestRule; + +import static org.catrobat.paintroid.test.espresso.util.UiInteractions.touchAt; +import static org.catrobat.paintroid.test.espresso.util.wrappers.DrawingSurfaceInteraction.onDrawingSurfaceView; +import static org.catrobat.paintroid.test.espresso.util.wrappers.ToolBarViewInteraction.onToolBarView; +import static org.catrobat.paintroid.test.espresso.util.wrappers.TransformToolOptionsViewInteraction.onTransformToolOptionsView; + +@RunWith(AndroidJUnit4.class) +public class FlipToolIntegrationTest { + + @Rule + public ActivityTestRule launchActivityRule = new ActivityTestRule<>(MainActivity.class); + + @Rule + public ScreenshotOnFailRule screenshotOnFailRule = new ScreenshotOnFailRule(); + + @Before + public void setUp() { + onToolBarView() + .performSelectTool(ToolType.BRUSH); + } + + @Test + public void testHorizontalFlip() { + onDrawingSurfaceView() + .perform(touchAt(DrawingSurfaceLocationProvider.HALFWAY_TOP_MIDDLE)); + + onDrawingSurfaceView() + .checkPixelColor(Color.BLACK, BitmapLocationProvider.HALFWAY_TOP_MIDDLE) + .checkPixelColor(Color.TRANSPARENT, BitmapLocationProvider.HALFWAY_BOTTOM_MIDDLE); + + onToolBarView() + .performSelectTool(ToolType.TRANSFORM); + + onTransformToolOptionsView() + .performFlipHorizontal(); + + onDrawingSurfaceView() + .checkPixelColor(Color.TRANSPARENT, BitmapLocationProvider.HALFWAY_TOP_MIDDLE) + .checkPixelColor(Color.BLACK, BitmapLocationProvider.HALFWAY_BOTTOM_MIDDLE); + } + + @Test + public void testVerticalFlip() { + onDrawingSurfaceView() + .perform(touchAt(DrawingSurfaceLocationProvider.HALFWAY_LEFT_MIDDLE)); + + onDrawingSurfaceView() + .checkPixelColor(Color.BLACK, BitmapLocationProvider.HALFWAY_LEFT_MIDDLE) + .checkPixelColor(Color.TRANSPARENT, BitmapLocationProvider.HALFWAY_RIGHT_MIDDLE); + + onToolBarView() + .performSelectTool(ToolType.TRANSFORM); + + onTransformToolOptionsView() + .performFlipVertical(); + + onDrawingSurfaceView() + .checkPixelColor(Color.TRANSPARENT, BitmapLocationProvider.HALFWAY_LEFT_MIDDLE) + .checkPixelColor(Color.BLACK, BitmapLocationProvider.HALFWAY_RIGHT_MIDDLE); + } +} diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/tools/ImportToolIntegrationTest.java b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/tools/ImportToolIntegrationTest.java new file mode 100644 index 0000000000..009cbf8e29 --- /dev/null +++ b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/tools/ImportToolIntegrationTest.java @@ -0,0 +1,96 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2015 The Catrobat Team + * () + * + * 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. + * + * 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 . + */ + +package org.catrobat.paintroid.test.espresso.tools; + +import org.catrobat.paintroid.MainActivity; +import org.catrobat.paintroid.R; +import org.catrobat.paintroid.test.espresso.rtl.util.RtlActivityTestRule; +import org.catrobat.paintroid.test.utils.ScreenshotOnFailRule; +import org.catrobat.paintroid.tools.ToolType; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import androidx.test.ext.junit.runners.AndroidJUnit4; +import androidx.test.rule.ActivityTestRule; + +import static org.catrobat.paintroid.test.espresso.util.wrappers.ToolBarViewInteraction.onToolBarView; +import static org.junit.Assert.assertEquals; + +import static androidx.test.espresso.Espresso.onView; +import static androidx.test.espresso.action.ViewActions.click; +import static androidx.test.espresso.assertion.ViewAssertions.doesNotExist; +import static androidx.test.espresso.assertion.ViewAssertions.matches; +import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed; +import static androidx.test.espresso.matcher.ViewMatchers.withId; +import static androidx.test.espresso.matcher.ViewMatchers.withText; + +@RunWith(AndroidJUnit4.class) +public class ImportToolIntegrationTest { + + @Rule + public ActivityTestRule launchActivityRule = new RtlActivityTestRule<>(MainActivity.class, "ar"); + + @Rule + public ScreenshotOnFailRule screenshotOnFailRule = new ScreenshotOnFailRule(); + + private MainActivity mainActivity; + + @Before + public void setUp() { + mainActivity = launchActivityRule.getActivity(); + onToolBarView() + .performSelectTool(ToolType.IMPORTPNG); + } + + @Test + public void testImportDialogShownOnImportToolSelected() { + onView(withId(R.id.pocketpaint_dialog_import_stickers)).check(matches(isDisplayed())); + onView(withId(R.id.pocketpaint_dialog_import_gallery)).check(matches(isDisplayed())); + } + + @Test + public void testImportDialogDismissedOnCancelClicked() { + onView(withText(R.string.pocketpaint_cancel)).perform(click()); + + onView(withId(R.id.pocketpaint_dialog_import_stickers)).check(doesNotExist()); + onView(withId(R.id.pocketpaint_dialog_import_gallery)).check(doesNotExist()); + } + + @Test + public void testImportDoesNotResetPerspectiveScale() { + onView(withText(R.string.pocketpaint_cancel)).perform(click()); + + onToolBarView() + .performSelectTool(ToolType.BRUSH); + + float scale = 2.0f; + mainActivity.perspective.setScale(scale); + mainActivity.refreshDrawingSurface(); + + onToolBarView() + .performSelectTool(ToolType.IMPORTPNG); + + onView(withText(R.string.pocketpaint_cancel)).perform(click()); + + assertEquals(scale, mainActivity.perspective.getScale(), Float.MIN_VALUE); + } +} diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/tools/PipetteToolIntegrationTest.java b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/tools/PipetteToolIntegrationTest.java new file mode 100644 index 0000000000..860a0b76c1 --- /dev/null +++ b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/tools/PipetteToolIntegrationTest.java @@ -0,0 +1,182 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2015 The Catrobat Team + * () + * + * 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. + * + * 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 . + */ + +package org.catrobat.paintroid.test.espresso.tools; + +import android.graphics.Color; + +import org.catrobat.paintroid.MainActivity; +import org.catrobat.paintroid.R; +import org.catrobat.paintroid.test.espresso.util.BitmapLocationProvider; +import org.catrobat.paintroid.test.espresso.util.DrawingSurfaceLocationProvider; +import org.catrobat.paintroid.test.utils.ScreenshotOnFailRule; +import org.catrobat.paintroid.tools.ToolType; +import org.junit.Before; +import org.junit.Ignore; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import androidx.test.ext.junit.runners.AndroidJUnit4; +import androidx.test.rule.ActivityTestRule; + +import static org.catrobat.paintroid.test.espresso.util.UiInteractions.touchAt; +import static org.catrobat.paintroid.test.espresso.util.wrappers.DrawingSurfaceInteraction.onDrawingSurfaceView; +import static org.catrobat.paintroid.test.espresso.util.wrappers.LayerMenuViewInteraction.onLayerMenuView; +import static org.catrobat.paintroid.test.espresso.util.wrappers.ToolBarViewInteraction.onToolBarView; +import static org.catrobat.paintroid.test.espresso.util.wrappers.ToolPropertiesInteraction.onToolProperties; +import static org.catrobat.paintroid.test.espresso.util.wrappers.TopBarViewInteraction.onTopBarView; + +@RunWith(AndroidJUnit4.class) +public class PipetteToolIntegrationTest { + + @Rule + public ActivityTestRule launchActivityRule = new ActivityTestRule<>(MainActivity.class); + + @Rule + public ScreenshotOnFailRule screenshotOnFailRule = new ScreenshotOnFailRule(); + + @Before + public void setUp() { + onToolBarView() + .performSelectTool(ToolType.BRUSH); + } + + @Test + public void testOnEmptyBitmap() { + onToolProperties() + .checkMatchesColor(Color.BLACK); + + onDrawingSurfaceView() + .checkPixelColor(Color.TRANSPARENT, BitmapLocationProvider.MIDDLE); + + onToolBarView() + .performSelectTool(ToolType.PIPETTE); + + onDrawingSurfaceView() + .perform(touchAt(DrawingSurfaceLocationProvider.MIDDLE)); + + onToolProperties() + .checkMatchesColor(Color.TRANSPARENT); + } + + @Test + public void testPipetteAfterBrushOnSingleLayer() { + onToolProperties() + .setColor(Color.RED); + onDrawingSurfaceView() + .perform(touchAt(DrawingSurfaceLocationProvider.MIDDLE)); + + onDrawingSurfaceView() + .checkPixelColor(Color.RED, BitmapLocationProvider.MIDDLE); + + onToolProperties() + .setColorResource(R.color.pocketpaint_color_picker_transparent) + .checkMatchesColor(Color.TRANSPARENT); + onToolBarView() + .performSelectTool(ToolType.PIPETTE); + + onDrawingSurfaceView() + .perform(touchAt(DrawingSurfaceLocationProvider.MIDDLE)); + + onToolProperties() + .checkMatchesColor(Color.RED); + } + + @Test + public void testPipetteAfterBrushOnMultiLayer() { + onDrawingSurfaceView() + .perform(touchAt(DrawingSurfaceLocationProvider.MIDDLE)); + + onDrawingSurfaceView() + .checkPixelColor(Color.BLACK, BitmapLocationProvider.MIDDLE); + + onLayerMenuView() + .performOpen() + .performAddLayer() + .performClose(); + + onToolProperties() + .setColor(Color.TRANSPARENT); + onToolBarView() + .performSelectTool(ToolType.PIPETTE); + + onToolProperties() + .checkMatchesColor(Color.TRANSPARENT); + + onDrawingSurfaceView() + .checkPixelColor(Color.TRANSPARENT, BitmapLocationProvider.MIDDLE) + .perform(touchAt(DrawingSurfaceLocationProvider.MIDDLE)); + + onToolProperties() + .checkMatchesColor(Color.BLACK); + } + + @Ignore("Flaky on Jenkins, for further information https://github.com/Catrobat/Paintroid/pull/794") + @Test + public void testPipetteAfterUndo() { + + onDrawingSurfaceView() + .perform(touchAt(DrawingSurfaceLocationProvider.MIDDLE)); + + onToolBarView() + .performSelectTool(ToolType.PIPETTE); + + onDrawingSurfaceView() + .perform(touchAt(DrawingSurfaceLocationProvider.MIDDLE)); + + onToolProperties() + .checkMatchesColor(Color.BLACK); + + onTopBarView() + .performUndo(); + + onDrawingSurfaceView() + .perform(touchAt(DrawingSurfaceLocationProvider.MIDDLE)); + onToolProperties() + .checkMatchesColor(Color.TRANSPARENT); + } + + @Ignore("Flaky on Jenkins, for further information https://github.com/Catrobat/Paintroid/pull/794") + @Test + public void testPipetteAfterRedo() { + + onDrawingSurfaceView() + .perform(touchAt(DrawingSurfaceLocationProvider.MIDDLE)); + + onToolBarView() + .performSelectTool(ToolType.PIPETTE); + + onDrawingSurfaceView() + .perform(touchAt(DrawingSurfaceLocationProvider.MIDDLE)); + onToolProperties() + .checkMatchesColor(Color.BLACK); + + onTopBarView() + .performUndo(); + + onTopBarView() + .performRedo(); + + onDrawingSurfaceView() + .perform(touchAt(DrawingSurfaceLocationProvider.MIDDLE)); + onToolProperties() + .checkMatchesColor(Color.BLACK); + } +} diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/tools/ShapeToolEraseIntegrationTest.java b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/tools/ShapeToolEraseIntegrationTest.java new file mode 100644 index 0000000000..b9e76e5e9e --- /dev/null +++ b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/tools/ShapeToolEraseIntegrationTest.java @@ -0,0 +1,99 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2015 The Catrobat Team + * () + * + * 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. + * + * 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 . + */ + +package org.catrobat.paintroid.test.espresso.tools; + +import android.graphics.Color; + +import org.catrobat.paintroid.MainActivity; +import org.catrobat.paintroid.test.espresso.util.BitmapLocationProvider; +import org.catrobat.paintroid.test.utils.ScreenshotOnFailRule; +import org.catrobat.paintroid.tools.ToolType; +import org.catrobat.paintroid.tools.drawable.DrawableShape; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameter; +import org.junit.runners.Parameterized.Parameters; + +import java.util.Arrays; + +import androidx.test.rule.ActivityTestRule; + +import static org.catrobat.paintroid.test.espresso.util.wrappers.DrawingSurfaceInteraction.onDrawingSurfaceView; +import static org.catrobat.paintroid.test.espresso.util.wrappers.ShapeToolOptionsViewInteraction.onShapeToolOptionsView; +import static org.catrobat.paintroid.test.espresso.util.wrappers.ToolBarViewInteraction.onToolBarView; +import static org.catrobat.paintroid.test.espresso.util.wrappers.ToolPropertiesInteraction.onToolProperties; +import static org.catrobat.paintroid.test.espresso.util.wrappers.TopBarViewInteraction.onTopBarView; + +@RunWith(Parameterized.class) +public class ShapeToolEraseIntegrationTest { + + @Parameter + public DrawableShape shape; + + @Parameters(name = "{0}") + public static Iterable data() { + return Arrays.asList(new Object[][]{ + {DrawableShape.RECTANGLE}, + {DrawableShape.OVAL}, + {DrawableShape.HEART}, + {DrawableShape.STAR} + }); + } + + @Rule + public ActivityTestRule launchActivityRule = new ActivityTestRule<>(MainActivity.class); + + @Rule + public ScreenshotOnFailRule screenshotOnFailRule = new ScreenshotOnFailRule(); + + @Test + public void testEraseWithFilledShape() { + + onToolBarView() + .performSelectTool(ToolType.SHAPE) + .performCloseToolOptionsView(); + + onTopBarView() + .performClickCheckmark(); + + onDrawingSurfaceView() + .checkPixelColor(Color.BLACK, BitmapLocationProvider.MIDDLE); + + onToolProperties() + .setColor(Color.TRANSPARENT); + + onToolBarView() + .performOpenToolOptionsView(); + + onShapeToolOptionsView() + .performSelectShape(shape); + + onToolBarView() + .performCloseToolOptionsView(); + + onTopBarView() + .performClickCheckmark(); + + onDrawingSurfaceView() + .checkPixelColor(Color.TRANSPARENT, BitmapLocationProvider.MIDDLE); + } +} diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/tools/ShapeToolIntegrationTest.java b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/tools/ShapeToolIntegrationTest.java new file mode 100644 index 0000000000..2596727bce --- /dev/null +++ b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/tools/ShapeToolIntegrationTest.java @@ -0,0 +1,248 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2015 The Catrobat Team + * () + * + * 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. + * + * 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 . + */ + +package org.catrobat.paintroid.test.espresso.tools; + +import android.graphics.Color; +import android.graphics.Paint; + +import org.catrobat.paintroid.MainActivity; +import org.catrobat.paintroid.R; +import org.catrobat.paintroid.test.espresso.util.BitmapLocationProvider; +import org.catrobat.paintroid.test.espresso.util.DrawingSurfaceLocationProvider; +import org.catrobat.paintroid.test.utils.ScreenshotOnFailRule; +import org.catrobat.paintroid.tools.ToolReference; +import org.catrobat.paintroid.tools.ToolType; +import org.catrobat.paintroid.tools.drawable.DrawableShape; +import org.catrobat.paintroid.tools.drawable.DrawableStyle; +import org.catrobat.paintroid.tools.implementation.BaseToolWithRectangleShape; +import org.catrobat.paintroid.tools.implementation.ShapeTool; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import androidx.test.ext.junit.runners.AndroidJUnit4; +import androidx.test.rule.ActivityTestRule; + +import static org.catrobat.paintroid.test.espresso.util.OffsetLocationProvider.withOffset; +import static org.catrobat.paintroid.test.espresso.util.UiInteractions.touchCenterLeft; +import static org.catrobat.paintroid.test.espresso.util.wrappers.DrawingSurfaceInteraction.onDrawingSurfaceView; +import static org.catrobat.paintroid.test.espresso.util.wrappers.ShapeToolOptionsViewInteraction.onShapeToolOptionsView; +import static org.catrobat.paintroid.test.espresso.util.wrappers.ToolBarViewInteraction.onToolBarView; +import static org.catrobat.paintroid.test.espresso.util.wrappers.ToolPropertiesInteraction.onToolProperties; +import static org.catrobat.paintroid.test.espresso.util.wrappers.TopBarViewInteraction.onTopBarView; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertTrue; + +@RunWith(AndroidJUnit4.class) +public class ShapeToolIntegrationTest { + + @Rule + public ActivityTestRule launchActivityRule = new ActivityTestRule<>(MainActivity.class); + + @Rule + public ScreenshotOnFailRule screenshotOnFailRule = new ScreenshotOnFailRule(); + + private ToolReference toolReference; + private MainActivity mainActivity; + + @Before + public void setUp() { + mainActivity = launchActivityRule.getActivity(); + toolReference = mainActivity.toolReference; + + onToolBarView() + .performSelectTool(ToolType.SHAPE); + } + + private Paint getCurrentToolBitmapPaint() { + return ((ShapeTool) toolReference.getTool()).getShapeBitmapPaint(); + } + + private Paint getToolPaint() { + return mainActivity.toolPaint.getPaint(); + } + + @Test + public void testEllipseIsDrawnOnBitmap() { + onShapeToolOptionsView() + .performSelectShape(DrawableShape.OVAL); + + BaseToolWithRectangleShape ellipseTool = (BaseToolWithRectangleShape) toolReference.getTool(); + float rectHeight = ellipseTool.boxHeight; + + onToolBarView() + .performCloseToolOptionsView(); + + onTopBarView() + .performClickCheckmark(); + + onDrawingSurfaceView() + .checkPixelColor(Color.BLACK, BitmapLocationProvider.MIDDLE) + .checkPixelColor(Color.BLACK, withOffset(BitmapLocationProvider.MIDDLE, (int) (rectHeight / 2.5f), 0)) + .checkPixelColor(Color.TRANSPARENT, withOffset(BitmapLocationProvider.MIDDLE, (int) (rectHeight / 2.5f), (int) (rectHeight / 2.5f))); + } + + @Test + public void testUndoRedo() { + onToolBarView() + .performCloseToolOptionsView(); + + onTopBarView() + .performClickCheckmark(); + + onDrawingSurfaceView() + .checkPixelColor(Color.BLACK, BitmapLocationProvider.MIDDLE); + + onTopBarView() + .performUndo(); + + onDrawingSurfaceView() + .checkPixelColor(Color.TRANSPARENT, BitmapLocationProvider.MIDDLE); + + onTopBarView() + .performRedo(); + + onDrawingSurfaceView() + .checkPixelColor(Color.BLACK, BitmapLocationProvider.MIDDLE); + } + + @Test + public void testFilledRectChangesColor() { + onToolBarView() + .performCloseToolOptionsView(); + + onToolProperties() + .setColorResource(R.color.pocketpaint_color_picker_brown1); + + onTopBarView() + .performClickCheckmark(); + + onDrawingSurfaceView() + .checkPixelColorResource(R.color.pocketpaint_color_picker_brown1, BitmapLocationProvider.MIDDLE); + } + + @Test + public void testDrawWithHeartShape() { + onShapeToolOptionsView() + .performSelectShape(DrawableShape.HEART); + + onToolBarView() + .performCloseToolOptionsView(); + + onTopBarView() + .performClickCheckmark(); + + onDrawingSurfaceView() + .checkPixelColor(Color.BLACK, BitmapLocationProvider.MIDDLE); + } + + @Test + public void testAntiAliasingIsOffIfShapeOutlineWidthIsOne() { + onToolBarView() + .performSelectTool(ToolType.SHAPE); + onShapeToolOptionsView() + .performSelectShapeDrawType(DrawableStyle.STROKE); + onShapeToolOptionsView() + .performSetOutlineWidth(touchCenterLeft()); + + drawShape(); + + Paint bitmapPaint = getCurrentToolBitmapPaint(); + Paint toolPaint = getToolPaint(); + + assertFalse("BITMAP_PAINT antialiasing should be off", bitmapPaint.isAntiAlias()); + assertTrue("TOOL_PAINT antialiasing should be on", toolPaint.isAntiAlias()); + } + + @Test + public void testDoNotUseRegularToolPaintInShapeTool() { + onToolBarView() + .performSelectTool(ToolType.SHAPE); + onShapeToolOptionsView() + .performSelectShapeDrawType(DrawableStyle.FILL); + + drawShape(); + + Paint bitmapPaint = getCurrentToolBitmapPaint(); + Paint toolPaint = getToolPaint(); + + assertNotEquals("bitmapPaint and toolPaint should differ", bitmapPaint, toolPaint); + } + + @Test + public void testShapeWithOutlineAlsoWorksWithTransparentColor() { + onToolBarView() + .performSelectTool(ToolType.SHAPE); + onShapeToolOptionsView() + .performSelectShape(DrawableShape.RECTANGLE); + onShapeToolOptionsView() + .performSelectShapeDrawType(DrawableStyle.FILL); + onToolProperties() + .setColor(Color.BLACK); + drawShape(); + onToolBarView() + .performClickSelectedToolButton(); + + onShapeToolOptionsView() + .performSelectShape(DrawableShape.OVAL); + onShapeToolOptionsView() + .performSelectShapeDrawType(DrawableStyle.STROKE); + onToolProperties() + .setColor(Color.TRANSPARENT); + drawShape(); + onDrawingSurfaceView() + .checkPixelColor(Color.BLACK, DrawingSurfaceLocationProvider.TOOL_POSITION); + onDrawingSurfaceView() + .checkPixelColor(Color.TRANSPARENT, DrawingSurfaceLocationProvider.TOP_MIDDLE); + } + + @Test + public void testShapeToolBoxGetsPlacedCorrectWhenZoomedIn() { + onToolBarView() + .performSelectTool(ToolType.BRUSH); + + mainActivity.perspective.surfaceTranslationY = 200; + mainActivity.perspective.surfaceTranslationX = 50; + mainActivity.perspective.setScale(2.0f); + mainActivity.refreshDrawingSurface(); + + onToolBarView() + .performSelectTool(ToolType.SHAPE); + onShapeToolOptionsView() + .performSelectShape(DrawableShape.RECTANGLE); + onShapeToolOptionsView() + .performSelectShapeDrawType(DrawableStyle.FILL); + onTopBarView() + .performClickCheckmark(); + + onDrawingSurfaceView() + .checkPixelColor(Color.BLACK, mainActivity.perspective.surfaceCenterX - mainActivity.perspective.surfaceTranslationX, + mainActivity.perspective.surfaceCenterY - mainActivity.perspective.surfaceTranslationY); + } + + public void drawShape() { + onToolBarView() + .performCloseToolOptionsView(); + onTopBarView() + .performClickCheckmark(); + } +} diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/tools/ShapeToolOrientationIntegrationTest.java b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/tools/ShapeToolOrientationIntegrationTest.java new file mode 100644 index 0000000000..d515b87fa3 --- /dev/null +++ b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/tools/ShapeToolOrientationIntegrationTest.java @@ -0,0 +1,154 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2015 The Catrobat Team + * () + * + * 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. + * + * 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 . + */ + +package org.catrobat.paintroid.test.espresso.tools; + +import android.content.pm.ActivityInfo; +import android.graphics.Bitmap; + +import org.catrobat.paintroid.MainActivity; +import org.catrobat.paintroid.R; +import org.catrobat.paintroid.test.espresso.util.DrawingSurfaceLocationProvider; +import org.catrobat.paintroid.test.utils.ScreenshotOnFailRule; +import org.catrobat.paintroid.tools.ToolType; +import org.catrobat.paintroid.tools.Workspace; +import org.catrobat.paintroid.tools.drawable.DrawableShape; +import org.catrobat.paintroid.tools.drawable.DrawableStyle; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +import java.util.Arrays; + +import androidx.test.rule.ActivityTestRule; + +import static org.catrobat.paintroid.test.espresso.util.UiInteractions.touchAt; +import static org.catrobat.paintroid.test.espresso.util.wrappers.DrawingSurfaceInteraction.onDrawingSurfaceView; +import static org.catrobat.paintroid.test.espresso.util.wrappers.ShapeToolOptionsViewInteraction.onShapeToolOptionsView; +import static org.catrobat.paintroid.test.espresso.util.wrappers.ToolBarViewInteraction.onToolBarView; +import static org.catrobat.paintroid.test.espresso.util.wrappers.TopBarViewInteraction.onTopBarView; +import static org.junit.Assert.assertTrue; +import static org.junit.runners.Parameterized.Parameter; +import static org.junit.runners.Parameterized.Parameters; + +import static androidx.test.espresso.Espresso.onView; +import static androidx.test.espresso.assertion.ViewAssertions.matches; +import static androidx.test.espresso.matcher.ViewMatchers.isSelected; +import static androidx.test.espresso.matcher.ViewMatchers.withId; + +@RunWith(Parameterized.class) +public class ShapeToolOrientationIntegrationTest { + + @Rule + public ActivityTestRule activityTestRule = new ActivityTestRule<>(MainActivity.class); + + @Rule + public ScreenshotOnFailRule screenshotOnFailRule = new ScreenshotOnFailRule(); + + @Parameter + public DrawableShape shape; + @Parameter(1) + public int shapeId; + + private Workspace workspace; + + @Parameters(name = "{0}") + public static Iterable data() { + return Arrays.asList(new Object[][]{ + {DrawableShape.RECTANGLE, R.id.pocketpaint_shapes_square_btn}, + {DrawableShape.OVAL, R.id.pocketpaint_shapes_circle_btn}, + {DrawableShape.HEART, R.id.pocketpaint_shapes_heart_btn}, + {DrawableShape.STAR, R.id.pocketpaint_shapes_star_btn} + }); + } + + @Before + public void setUp() { + workspace = activityTestRule.getActivity().workspace; + onToolBarView() + .performSelectTool(ToolType.SHAPE); + } + + @Test + public void testRememberShapeAfterOrientationChange() { + onShapeToolOptionsView() + .performSelectShape(shape); + onView(withId(shapeId)) + .check(matches(isSelected())); + onToolBarView() + .performCloseToolOptionsView(); + + onDrawingSurfaceView() + .perform(touchAt(DrawingSurfaceLocationProvider.TOOL_POSITION)); + + Bitmap expectedBitmap = workspace.getBitmapOfCurrentLayer(); + + onTopBarView() + .performUndo(); + + activityTestRule.getActivity().setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); + + onView(withId(shapeId)) + .check(matches(isSelected())); + + onToolBarView() + .performCloseToolOptionsView(); + + onDrawingSurfaceView() + .perform(touchAt(DrawingSurfaceLocationProvider.TOOL_POSITION)); + + assertTrue(expectedBitmap.sameAs(workspace.getBitmapOfCurrentLayer())); + } + + @Test + public void testRememberOutlineShapeAfterOrientationChange() { + onShapeToolOptionsView() + .performSelectShape(shape) + .performSelectShapeDrawType(DrawableStyle.STROKE); + onView(withId(shapeId)) + .check(matches(isSelected())); + onView(withId(R.id.pocketpaint_shape_ibtn_outline)) + .check(matches(isSelected())); + onToolBarView() + .performCloseToolOptionsView(); + + onDrawingSurfaceView() + .perform(touchAt(DrawingSurfaceLocationProvider.TOOL_POSITION)); + + Bitmap expectedBitmap = workspace.getBitmapOfCurrentLayer(); + onTopBarView() + .performUndo(); + + activityTestRule.getActivity().setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); + onView(withId(shapeId)) + .check(matches(isSelected())); + onView(withId(R.id.pocketpaint_shape_ibtn_outline)) + .check(matches(isSelected())); + + onToolBarView() + .performCloseToolOptionsView(); + + onDrawingSurfaceView() + .perform(touchAt(DrawingSurfaceLocationProvider.TOOL_POSITION)); + + assertTrue(expectedBitmap.sameAs(workspace.getBitmapOfCurrentLayer())); + } +} diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/tools/SmudgeToolIntegrationTest.kt b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/tools/SmudgeToolIntegrationTest.kt new file mode 100644 index 0000000000..bb16c56033 --- /dev/null +++ b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/tools/SmudgeToolIntegrationTest.kt @@ -0,0 +1,94 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2021 The Catrobat Team + * () + * + * 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. + * + * 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 . + */ +package org.catrobat.paintroid.test.espresso.tools + +import androidx.test.espresso.Espresso.onView +import androidx.test.espresso.action.ViewActions +import androidx.test.espresso.action.ViewActions.replaceText +import androidx.test.espresso.assertion.ViewAssertions.matches +import androidx.test.espresso.matcher.ViewMatchers.withId +import androidx.test.espresso.matcher.ViewMatchers.withText +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.rule.ActivityTestRule +import org.catrobat.paintroid.MainActivity +import org.catrobat.paintroid.R +import org.catrobat.paintroid.test.espresso.util.UiMatcher.withProgress +import org.catrobat.paintroid.test.espresso.util.wrappers.ToolBarViewInteraction +import org.catrobat.paintroid.test.utils.ScreenshotOnFailRule +import org.catrobat.paintroid.tools.ToolReference +import org.catrobat.paintroid.tools.ToolType +import org.catrobat.paintroid.tools.implementation.DEFAULT_DRAG_IN_PERCENT +import org.catrobat.paintroid.tools.implementation.DEFAULT_PRESSURE_IN_PERCENT +import org.catrobat.paintroid.tools.implementation.SmudgeTool +import org.junit.Assert +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith + +@RunWith(AndroidJUnit4::class) +class SmudgeToolIntegrationTest { + + @get:Rule + var launchActivityRule = ActivityTestRule(MainActivity::class.java) + + @get:Rule + var screenshotOnFailRule = ScreenshotOnFailRule() + + private lateinit var toolReference: ToolReference + + @Before + fun setUp() { + toolReference = launchActivityRule.activity.toolReference + ToolBarViewInteraction.onToolBarView().performSelectTool(ToolType.SMUDGE) + } + + @Test + fun testSmudgeToolOptionsDialog() { + val smudgeTool = toolReference.tool as SmudgeTool + ToolBarViewInteraction.onToolBarView() + .performClickSelectedToolButton() + + val pressureInput = onView(withId(R.id.pocketpaint_smudge_tool_dialog_pressure_input)) + val pressureSeekBar = onView(withId(R.id.pocketpaint_pressure_seek_bar)) + val testPressureText = "100" + pressureInput.check(matches(withText(Integer.toString(DEFAULT_PRESSURE_IN_PERCENT)))) + pressureInput.perform(replaceText(testPressureText), ViewActions.closeSoftKeyboard()) + pressureInput.check(matches(withText(testPressureText))) + pressureSeekBar.check(matches(withProgress(testPressureText.toInt()))) + val expectedPressure = 1f + Assert.assertEquals(expectedPressure, smudgeTool.maxPressure) + + val dragInput = onView(withId(R.id.pocketpaint_smudge_tool_dialog_drag_input)) + val dragSeekBar = onView(withId(R.id.pocketpaint_drag_seek_bar)) + val testDragText = "100" + dragInput.check(matches(withText(Integer.toString(DEFAULT_DRAG_IN_PERCENT)))) + dragInput.perform(replaceText(testDragText), ViewActions.closeSoftKeyboard()) + dragInput.check(matches(withText(testDragText))) + dragSeekBar.check(matches(withProgress(testDragText.toInt()))) + val expectedMaxSize = 25f + val expectedMinSize = 25f + Assert.assertEquals(expectedMaxSize, smudgeTool.maxSmudgeSize) + Assert.assertEquals(expectedMinSize, smudgeTool.minSmudgeSize) + + // Close tool options + ToolBarViewInteraction.onToolBarView() + .performClickSelectedToolButton() + } +} diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/tools/SprayToolIntegrationTest.kt b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/tools/SprayToolIntegrationTest.kt new file mode 100644 index 0000000000..dbc637bf63 --- /dev/null +++ b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/tools/SprayToolIntegrationTest.kt @@ -0,0 +1,64 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2021 The Catrobat Team + * () + * + * 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. + * + * 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 . + */ +package org.catrobat.paintroid.test.espresso.tools + +import androidx.test.espresso.Espresso.onView +import androidx.test.espresso.action.ViewActions.replaceText +import androidx.test.espresso.assertion.ViewAssertions.matches +import androidx.test.espresso.matcher.ViewMatchers.withId +import androidx.test.espresso.matcher.ViewMatchers.withText +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.rule.ActivityTestRule +import org.catrobat.paintroid.MainActivity +import org.catrobat.paintroid.R +import org.catrobat.paintroid.test.espresso.util.UiMatcher.withProgress +import org.catrobat.paintroid.test.espresso.util.wrappers.ToolBarViewInteraction +import org.catrobat.paintroid.test.utils.ScreenshotOnFailRule +import org.catrobat.paintroid.tools.ToolType +import org.catrobat.paintroid.ui.tools.MIN_RADIUS +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith + +@RunWith(AndroidJUnit4::class) +class SprayToolIntegrationTest { + + @get:Rule + var launchActivityRule = ActivityTestRule(MainActivity::class.java) + + @get:Rule + var screenshotOnFailRule = ScreenshotOnFailRule() + + @Before + fun setUp() { + ToolBarViewInteraction.onToolBarView().performSelectTool(ToolType.SPRAY) + } + + @Test + fun testEmptyRadius() { + val emptyString = "" + onView(withId(R.id.pocketpaint_radius_text)) + .perform(replaceText(emptyString)) + .check(matches(withText(emptyString))) + + onView(withId(R.id.pocketpaint_spray_radius_seek_bar)) + .check(matches(withProgress(MIN_RADIUS))) + } +} diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/tools/StampToolIntegrationTest.java b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/tools/StampToolIntegrationTest.java new file mode 100644 index 0000000000..8e6f2827a3 --- /dev/null +++ b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/tools/StampToolIntegrationTest.java @@ -0,0 +1,256 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2015 The Catrobat Team + * () + * + * 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. + * + * 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 . + */ + +package org.catrobat.paintroid.test.espresso.tools; + +import android.content.pm.ActivityInfo; +import android.graphics.Bitmap; +import android.graphics.Color; +import android.graphics.PointF; + +import org.catrobat.paintroid.MainActivity; +import org.catrobat.paintroid.test.espresso.util.DrawingSurfaceLocationProvider; +import org.catrobat.paintroid.test.espresso.util.wrappers.StampToolViewInteraction; +import org.catrobat.paintroid.test.utils.ScreenshotOnFailRule; +import org.catrobat.paintroid.tools.ToolReference; +import org.catrobat.paintroid.tools.ToolType; +import org.catrobat.paintroid.tools.Workspace; +import org.catrobat.paintroid.tools.drawable.DrawableShape; +import org.catrobat.paintroid.tools.drawable.DrawableStyle; +import org.catrobat.paintroid.tools.implementation.BaseToolWithRectangleShape; +import org.catrobat.paintroid.tools.implementation.StampTool; +import org.catrobat.paintroid.ui.Perspective; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import androidx.test.ext.junit.runners.AndroidJUnit4; +import androidx.test.rule.ActivityTestRule; + +import static org.catrobat.paintroid.test.espresso.util.UiInteractions.touchAt; +import static org.catrobat.paintroid.test.espresso.util.wrappers.DrawingSurfaceInteraction.onDrawingSurfaceView; +import static org.catrobat.paintroid.test.espresso.util.wrappers.LayerMenuViewInteraction.onLayerMenuView; +import static org.catrobat.paintroid.test.espresso.util.wrappers.ShapeToolOptionsViewInteraction.onShapeToolOptionsView; +import static org.catrobat.paintroid.test.espresso.util.wrappers.ToolBarViewInteraction.onToolBarView; +import static org.catrobat.paintroid.test.espresso.util.wrappers.TopBarViewInteraction.onTopBarView; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +@RunWith(AndroidJUnit4.class) +public class StampToolIntegrationTest { + + private static final float SCALE_25 = 0.25f; + private static final float STAMP_RESIZE_FACTOR = 1.5f; + @Rule + public ActivityTestRule launchActivityRule = new ActivityTestRule<>(MainActivity.class); + + @Rule + public ScreenshotOnFailRule screenshotOnFailRule = new ScreenshotOnFailRule(); + + private Workspace workspace; + private Perspective perspective; + private ToolReference toolReference; + private MainActivity mainActivity; + + @Before + public void setUp() { + onToolBarView() + .performSelectTool(ToolType.BRUSH); + mainActivity = launchActivityRule.getActivity(); + workspace = mainActivity.workspace; + perspective = mainActivity.perspective; + toolReference = mainActivity.toolReference; + } + + @Test + public void testBorders() { + onToolBarView() + .performSelectTool(ToolType.SHAPE); + + onShapeToolOptionsView() + .performSelectShape(DrawableShape.RECTANGLE) + .performSelectShapeDrawType(DrawableStyle.STROKE); + + onTopBarView() + .performClickCheckmark(); + + onToolBarView() + .performSelectTool(ToolType.STAMP); + + StampTool stampTool = (StampTool) toolReference.getTool(); + stampTool.boxHeight -= 25; + stampTool.boxWidth -= 25; + + StampToolViewInteraction.Companion.onStampToolViewInteraction() + .performCopy(); + + int topLeft = stampTool.drawingBitmap.getPixel(0, 0); + int topRight = stampTool.drawingBitmap.getPixel(stampTool.drawingBitmap.getWidth() - 1, 0); + int bottomLeft = stampTool.drawingBitmap.getPixel(0, stampTool.drawingBitmap.getHeight() - 1); + int bottomRight = stampTool.drawingBitmap.getPixel(stampTool.drawingBitmap.getWidth() - 1, stampTool.drawingBitmap.getHeight() - 1); + + assertEquals(topLeft, Color.BLACK); + assertEquals(topRight, Color.BLACK); + assertEquals(bottomLeft, Color.BLACK); + assertEquals(bottomRight, Color.BLACK); + } + + @Test + public void testCopyPixel() { + + onDrawingSurfaceView() + .perform(touchAt(DrawingSurfaceLocationProvider.MIDDLE)); + + onToolBarView() + .performSelectTool(ToolType.STAMP); + + StampToolViewInteraction.Companion.onStampToolViewInteraction() + .performCopy(); + + StampTool stampTool = (StampTool) toolReference.getTool(); + stampTool.toolPosition.set(stampTool.toolPosition.x, stampTool.toolPosition.y * .5f); + + StampToolViewInteraction.Companion.onStampToolViewInteraction() + .performPaste(); + + onDrawingSurfaceView() + .checkPixelColor(Color.BLACK, stampTool.toolPosition.x, stampTool.toolPosition.y); + } + + @Test + public void testCutAndPastePixel() { + + onDrawingSurfaceView() + .perform(touchAt(DrawingSurfaceLocationProvider.MIDDLE)); + + onToolBarView() + .performSelectTool(ToolType.STAMP); + + StampToolViewInteraction.Companion.onStampToolViewInteraction() + .performCut(); + + StampTool stampTool = (StampTool) toolReference.getTool(); + + onDrawingSurfaceView() + .checkPixelColor(Color.TRANSPARENT, stampTool.toolPosition.x, stampTool.toolPosition.y); + + StampToolViewInteraction.Companion.onStampToolViewInteraction() + .performPaste(); + + onDrawingSurfaceView() + .checkPixelColor(Color.BLACK, stampTool.toolPosition.x, stampTool.toolPosition.y); + } + + @Test + public void testStampToolNotCapturingOtherLayers() { + onDrawingSurfaceView() + .perform(touchAt(DrawingSurfaceLocationProvider.MIDDLE)); + + onToolBarView() + .performSelectTool(ToolType.STAMP); + + onLayerMenuView() + .performOpen() + .performAddLayer(); + + onLayerMenuView() + .performClose(); + + StampToolViewInteraction.Companion.onStampToolViewInteraction() + .performCopy(); + + StampTool stampTool = (StampTool) toolReference.getTool(); + stampTool.toolPosition.set(stampTool.toolPosition.x, stampTool.toolPosition.y * .5f); + + StampToolViewInteraction.Companion.onStampToolViewInteraction() + .performPaste(); + + onDrawingSurfaceView() + .checkPixelColor(Color.TRANSPARENT, stampTool.toolPosition.x, stampTool.toolPosition.y * .5f); + } + + @Test + public void testStampOutsideDrawingSurface() { + onDrawingSurfaceView() + .perform(touchAt(DrawingSurfaceLocationProvider.MIDDLE)); + + int bitmapWidth = workspace.getWidth(); + int bitmapHeight = workspace.getHeight(); + perspective.setScale(SCALE_25); + + onToolBarView() + .performSelectTool(ToolType.STAMP); + + StampTool stampTool = (StampTool) toolReference.getTool(); + PointF toolPosition = new PointF(perspective.surfaceCenterX, perspective.surfaceCenterY); + stampTool.toolPosition.set(toolPosition); + stampTool.boxWidth = (int) (bitmapWidth * STAMP_RESIZE_FACTOR); + stampTool.boxHeight = (int) (bitmapHeight * STAMP_RESIZE_FACTOR); + + StampToolViewInteraction.Companion.onStampToolViewInteraction() + .performPaste(); + + assertNotNull(stampTool.drawingBitmap); + } + + @Test + public void testBitmapSavedOnOrientationChange() { + onDrawingSurfaceView() + .perform(touchAt(DrawingSurfaceLocationProvider.MIDDLE)); + + onToolBarView() + .performSelectTool(ToolType.STAMP); + + Bitmap emptyBitmap = Bitmap.createBitmap(((BaseToolWithRectangleShape) + toolReference.getTool()).drawingBitmap); + + StampToolViewInteraction.Companion.onStampToolViewInteraction() + .performCopy(); + + Bitmap expectedBitmap = Bitmap.createBitmap(((BaseToolWithRectangleShape) + toolReference.getTool()).drawingBitmap); + + assertFalse(expectedBitmap.sameAs(emptyBitmap)); + + mainActivity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); + + Bitmap actualBitmap = Bitmap.createBitmap(((BaseToolWithRectangleShape) + toolReference.getTool()).drawingBitmap); + + assertTrue(expectedBitmap.sameAs(actualBitmap)); + } + + @Test + public void testStampToolDoesNotResetPerspectiveScale() { + float scale = 2.0f; + + perspective.setScale(scale); + perspective.surfaceTranslationX = 50; + perspective.surfaceTranslationY = 200; + mainActivity.refreshDrawingSurface(); + + onToolBarView() + .performSelectTool(ToolType.STAMP); + + assertEquals(scale, perspective.getScale(), 0.0001f); + } +} diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/tools/TextToolFontListTest.java b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/tools/TextToolFontListTest.java new file mode 100644 index 0000000000..3df0586f63 --- /dev/null +++ b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/tools/TextToolFontListTest.java @@ -0,0 +1,91 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2015 The Catrobat Team + * () + * + * 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. + * + * 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 . + */ + +package org.catrobat.paintroid.test.espresso.tools; + +import android.content.Context; +import android.graphics.Typeface; + +import org.catrobat.paintroid.MainActivity; +import org.catrobat.paintroid.R; +import org.catrobat.paintroid.test.utils.ScreenshotOnFailRule; +import org.catrobat.paintroid.tools.ToolType; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import androidx.core.content.res.ResourcesCompat; +import androidx.test.espresso.contrib.RecyclerViewActions; +import androidx.test.ext.junit.runners.AndroidJUnit4; +import androidx.test.platform.app.InstrumentationRegistry; +import androidx.test.rule.ActivityTestRule; + +import static org.catrobat.paintroid.test.espresso.util.UiMatcher.atPosition; +import static org.catrobat.paintroid.test.espresso.util.UiMatcher.hasTypeFace; +import static org.catrobat.paintroid.test.espresso.util.wrappers.ToolBarViewInteraction.onToolBarView; + +import static androidx.test.espresso.Espresso.onView; +import static androidx.test.espresso.assertion.ViewAssertions.matches; +import static androidx.test.espresso.matcher.ViewMatchers.hasDescendant; +import static androidx.test.espresso.matcher.ViewMatchers.isChecked; +import static androidx.test.espresso.matcher.ViewMatchers.withId; +import static androidx.test.espresso.matcher.ViewMatchers.withText; + +@RunWith(AndroidJUnit4.class) +public class TextToolFontListTest { + private final int normalStyle = Typeface.NORMAL; + private final Context context = InstrumentationRegistry.getInstrumentation().getTargetContext(); + private final Typeface sansSerifFontFace = Typeface.create(Typeface.SANS_SERIF, normalStyle); + private final Typeface serifFontFace = Typeface.create(Typeface.SERIF, normalStyle); + private final Typeface monospaceFontFace = Typeface.create(Typeface.MONOSPACE, normalStyle); + private final Typeface stcFontFace = ResourcesCompat.getFont(context, R.font.stc_regular); + private final Typeface dubaiFontFace = ResourcesCompat.getFont(context, R.font.dubai); + + @Rule + public ActivityTestRule launchActivityRule = new ActivityTestRule<>(MainActivity.class); + + @Rule + public ScreenshotOnFailRule screenshotOnFailRule = new ScreenshotOnFailRule(); + + @Test + public void testTextFontFaceOfFontSpinnerEnglish() { + onToolBarView() + .performSelectTool(ToolType.TEXT); + + onView(withId(R.id.pocketpaint_text_tool_dialog_list_font)) + .check(matches(atPosition(0, hasDescendant(hasTypeFace(sansSerifFontFace))))); + onView(withId(R.id.pocketpaint_text_tool_dialog_list_font)) + .check(matches(atPosition(1, hasDescendant(hasTypeFace(monospaceFontFace))))); + onView(withId(R.id.pocketpaint_text_tool_dialog_list_font)).perform(RecyclerViewActions.scrollToPosition(2)) + .check(matches(atPosition(2, hasDescendant(hasTypeFace(serifFontFace))))); + onView(withId(R.id.pocketpaint_text_tool_dialog_list_font)).perform(RecyclerViewActions.scrollToPosition(3)) + .check(matches(atPosition(3, hasDescendant(hasTypeFace(dubaiFontFace))))); + onView(withId(R.id.pocketpaint_text_tool_dialog_list_font)).perform(RecyclerViewActions.scrollToPosition(4)) + .check(matches(atPosition(4, hasDescendant(hasTypeFace(stcFontFace))))); + } + + @Test + public void checkIfSansSerifIsDefaultSpinnerFont() { + onToolBarView() + .performSelectTool(ToolType.TEXT); + + onView(withText(R.string.text_tool_dialog_font_sans_serif)) + .check(matches(isChecked())); + } +} diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/tools/TextToolFontListTestArabic.java b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/tools/TextToolFontListTestArabic.java new file mode 100644 index 0000000000..23ab68d413 --- /dev/null +++ b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/tools/TextToolFontListTestArabic.java @@ -0,0 +1,90 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2015 The Catrobat Team + * () + * + * 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. + * + * 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 . + */ + +package org.catrobat.paintroid.test.espresso.tools; + +import android.content.Context; +import android.graphics.Typeface; +import android.view.View; + +import org.catrobat.paintroid.MainActivity; +import org.catrobat.paintroid.R; +import org.catrobat.paintroid.test.espresso.rtl.util.RtlActivityTestRule; +import org.catrobat.paintroid.test.utils.ScreenshotOnFailRule; +import org.catrobat.paintroid.tools.ToolType; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.Locale; + +import androidx.core.content.res.ResourcesCompat; +import androidx.test.espresso.contrib.RecyclerViewActions; +import androidx.test.ext.junit.runners.AndroidJUnit4; +import androidx.test.platform.app.InstrumentationRegistry; +import androidx.test.rule.ActivityTestRule; + +import static org.catrobat.paintroid.test.espresso.rtl.util.RtlUiTestUtils.checkTextDirection; +import static org.catrobat.paintroid.test.espresso.util.EspressoUtils.getConfiguration; +import static org.catrobat.paintroid.test.espresso.util.UiMatcher.atPosition; +import static org.catrobat.paintroid.test.espresso.util.UiMatcher.hasTypeFace; +import static org.catrobat.paintroid.test.espresso.util.wrappers.ToolBarViewInteraction.onToolBarView; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import static androidx.test.espresso.Espresso.onView; +import static androidx.test.espresso.assertion.ViewAssertions.matches; +import static androidx.test.espresso.matcher.ViewMatchers.hasDescendant; +import static androidx.test.espresso.matcher.ViewMatchers.withId; + +@RunWith(AndroidJUnit4.class) +public class TextToolFontListTestArabic { + private final int normalStyle = Typeface.NORMAL; + private final Context context = InstrumentationRegistry.getInstrumentation().getTargetContext(); + private final Typeface sansSerifFontFace = Typeface.create(Typeface.SANS_SERIF, normalStyle); + private final Typeface serifFontFace = Typeface.create(Typeface.SERIF, normalStyle); + private final Typeface monospaceFontFace = Typeface.create(Typeface.MONOSPACE, normalStyle); + private final Typeface stcFontFace = ResourcesCompat.getFont(context, R.font.stc_regular); + private final Typeface dubaiFontFace = ResourcesCompat.getFont(context, R.font.dubai); + + @Rule + public ActivityTestRule launchActivityRule = new RtlActivityTestRule<>(MainActivity.class, "ar"); + + @Rule + public ScreenshotOnFailRule screenshotOnFailRule = new ScreenshotOnFailRule(); + + @Test + public void testTextFontFaceOfFontSpinnerArabic() { + assertEquals(View.LAYOUT_DIRECTION_RTL, getConfiguration().getLayoutDirection()); + assertTrue(checkTextDirection(Locale.getDefault().getDisplayName())); + + onToolBarView() + .performSelectTool(ToolType.TEXT); + onView(withId(R.id.pocketpaint_text_tool_dialog_list_font)) + .check(matches(atPosition(0, hasDescendant(hasTypeFace(sansSerifFontFace))))); + onView(withId(R.id.pocketpaint_text_tool_dialog_list_font)) + .check(matches(atPosition(1, hasDescendant(hasTypeFace(monospaceFontFace))))); + onView(withId(R.id.pocketpaint_text_tool_dialog_list_font)).perform(RecyclerViewActions.scrollToPosition(2)) + .check(matches(atPosition(2, hasDescendant(hasTypeFace(serifFontFace))))); + onView(withId(R.id.pocketpaint_text_tool_dialog_list_font)).perform(RecyclerViewActions.scrollToPosition(3)) + .check(matches(atPosition(3, hasDescendant(hasTypeFace(dubaiFontFace))))); + onView(withId(R.id.pocketpaint_text_tool_dialog_list_font)).perform(RecyclerViewActions.scrollToPosition(4)) + .check(matches(atPosition(4, hasDescendant(hasTypeFace(stcFontFace))))); + } +} diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/tools/TextToolIntegrationTest.java b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/tools/TextToolIntegrationTest.java new file mode 100644 index 0000000000..e5f4bf6aaf --- /dev/null +++ b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/tools/TextToolIntegrationTest.java @@ -0,0 +1,772 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2015 The Catrobat Team + * () + * + * 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. + * + * 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 . + */ + +package org.catrobat.paintroid.test.espresso.tools; + +import android.content.pm.ActivityInfo; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.PointF; +import android.graphics.Typeface; +import android.widget.EditText; + +import com.google.android.material.button.MaterialButton; + +import org.catrobat.paintroid.MainActivity; +import org.catrobat.paintroid.R; +import org.catrobat.paintroid.contract.LayerContracts; +import org.catrobat.paintroid.test.espresso.util.DrawingSurfaceLocationProvider; +import org.catrobat.paintroid.test.espresso.util.MainActivityHelper; +import org.catrobat.paintroid.test.utils.ScreenshotOnFailRule; +import org.catrobat.paintroid.tools.FontType; +import org.catrobat.paintroid.tools.ToolReference; +import org.catrobat.paintroid.tools.ToolType; +import org.catrobat.paintroid.tools.implementation.TextTool; +import org.catrobat.paintroid.ui.tools.FontListAdapter; +import org.junit.Before; +import org.junit.Ignore; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.ArrayList; + +import androidx.core.content.res.ResourcesCompat; +import androidx.recyclerview.widget.RecyclerView; +import androidx.test.espresso.Espresso; +import androidx.test.espresso.contrib.RecyclerViewActions; +import androidx.test.ext.junit.runners.AndroidJUnit4; +import androidx.test.rule.ActivityTestRule; + +import static org.catrobat.paintroid.test.espresso.util.UiInteractions.touchAt; +import static org.catrobat.paintroid.test.espresso.util.UiMatcher.atPosition; +import static org.catrobat.paintroid.test.espresso.util.wrappers.DrawingSurfaceInteraction.onDrawingSurfaceView; +import static org.catrobat.paintroid.test.espresso.util.wrappers.ToolBarViewInteraction.onToolBarView; +import static org.catrobat.paintroid.test.espresso.util.wrappers.ToolPropertiesInteraction.onToolProperties; +import static org.catrobat.paintroid.test.espresso.util.wrappers.TopBarViewInteraction.onTopBarView; +import static org.catrobat.paintroid.tools.implementation.TextToolKt.BOX_OFFSET; +import static org.catrobat.paintroid.tools.implementation.TextToolKt.MARGIN_TOP; +import static org.catrobat.paintroid.tools.implementation.TextToolKt.TEXT_SIZE_MAGNIFICATION_FACTOR; +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertTrue; + +import static androidx.test.espresso.Espresso.onView; +import static androidx.test.espresso.Espresso.pressBack; +import static androidx.test.espresso.action.ViewActions.click; +import static androidx.test.espresso.action.ViewActions.replaceText; +import static androidx.test.espresso.assertion.ViewAssertions.matches; +import static androidx.test.espresso.matcher.ViewMatchers.hasDescendant; +import static androidx.test.espresso.matcher.ViewMatchers.hasFocus; +import static androidx.test.espresso.matcher.ViewMatchers.isChecked; +import static androidx.test.espresso.matcher.ViewMatchers.isNotChecked; +import static androidx.test.espresso.matcher.ViewMatchers.withHint; +import static androidx.test.espresso.matcher.ViewMatchers.withId; +import static androidx.test.espresso.matcher.ViewMatchers.withText; + +@RunWith(AndroidJUnit4.class) +public class TextToolIntegrationTest { + private static final String TEST_TEXT = "123 www 123"; + private static final String TEST_TEXT_ADVANCED = "testing 123 new"; + private static final String TEST_TEXT_MULTILINE = "testing\nmultiline\ntext\n\n123"; + + private static final double EQUALS_DELTA = 0.25d; + @Rule + public ActivityTestRule launchActivityRule = new ActivityTestRule<>(MainActivity.class); + + @Rule + public ScreenshotOnFailRule screenshotOnFailRule = new ScreenshotOnFailRule(); + + private MainActivityHelper activityHelper; + private TextTool textTool; + private TextTool textToolAfterZoom; + private EditText textEditText; + private RecyclerView fontList; + private MaterialButton underlinedToggleButton; + private MaterialButton italicToggleButton; + private MaterialButton boldToggleButton; + private EditText textSize; + private LayerContracts.Model layerModel; + private MainActivity activity; + private ToolReference toolReference; + + @Before + public void setUp() { + activity = launchActivityRule.getActivity(); + activityHelper = new MainActivityHelper(activity); + layerModel = activity.layerModel; + toolReference = activity.toolReference; + + onToolBarView() + .performSelectTool(ToolType.TEXT); + + textTool = (TextTool) toolReference.getTool(); + + textEditText = activity.findViewById(R.id.pocketpaint_text_tool_dialog_input_text); + fontList = activity.findViewById(R.id.pocketpaint_text_tool_dialog_list_font); + underlinedToggleButton = activity.findViewById(R.id.pocketpaint_text_tool_dialog_toggle_underlined); + italicToggleButton = activity.findViewById(R.id.pocketpaint_text_tool_dialog_toggle_italic); + boldToggleButton = activity.findViewById(R.id.pocketpaint_text_tool_dialog_toggle_bold); + textSize = activity.findViewById(R.id.pocketpaint_font_size_text); + textTool.resetBoxPosition(); + } + + @Test + public void testTextToolStillEditableAfterClosingTextTool() { + selectFormatting(FormattingOptions.ITALIC); + selectFormatting(FormattingOptions.BOLD); + selectFormatting(FormattingOptions.UNDERLINE); + enterTestText(); + + onDrawingSurfaceView() + .perform(touchAt(DrawingSurfaceLocationProvider.TOP_MIDDLE)); + + onView(withId(R.id.pocketpaint_text_tool_dialog_input_text)).perform(replaceText(TEST_TEXT_ADVANCED)); + + assertTrue(italicToggleButton.isChecked()); + assertTrue(boldToggleButton.isChecked()); + assertTrue(underlinedToggleButton.isChecked()); + assertEquals(TEST_TEXT_ADVANCED, textEditText.getText().toString()); + } + + @Ignore("Fix bug in own ticket , focus is not correctly implemented in google play either") + @Test + public void testDialogKeyboardTextBoxAppearanceOnStartup() { + onView(withId(R.id.pocketpaint_text_tool_dialog_input_text)).check(matches(hasFocus())); + checkTextBoxDimensionsAndDefaultPosition(); + } + + @Test + public void testDialogDefaultValues() { + onView(withId(R.id.pocketpaint_text_tool_dialog_input_text)) + .check(matches(withHint(R.string.text_tool_dialog_input_hint))) + .check(matches(withText(textTool.text))); + + onToolBarView() + .performSelectTool(ToolType.TEXT); + onView(withId(R.id.pocketpaint_text_tool_dialog_list_font)) + .check(matches(atPosition(0, hasDescendant(isChecked())))); + onView(withId(R.id.pocketpaint_text_tool_dialog_list_font)) + .check(matches(atPosition(1, hasDescendant(isNotChecked())))); + onView(withId(R.id.pocketpaint_text_tool_dialog_list_font)).perform(RecyclerViewActions.scrollToPosition(2)) + .check(matches(atPosition(2, hasDescendant(isNotChecked())))); + onView(withId(R.id.pocketpaint_text_tool_dialog_list_font)).perform(RecyclerViewActions.scrollToPosition(3)) + .check(matches(atPosition(3, hasDescendant(isNotChecked())))); + onView(withId(R.id.pocketpaint_text_tool_dialog_list_font)).perform(RecyclerViewActions.scrollToPosition(4)) + .check(matches(atPosition(4, hasDescendant(isNotChecked())))); + + assertFalse(textTool.underlined); + assertFalse(textTool.italic); + assertFalse(textTool.bold); + } + + @Test + public void testDialogToolInteraction() { + enterTestText(); + assertEquals(TEST_TEXT, textTool.text); + + selectFontType(FontType.SERIF); + assertEquals(FontType.SERIF, textTool.font); + assertEquals(FontType.SERIF, ((FontListAdapter) fontList.getAdapter()).getSelectedItem()); + + selectFormatting(FormattingOptions.UNDERLINE); + assertTrue(textTool.underlined); + assertTrue(underlinedToggleButton.isChecked()); + assertEquals(getFormattingOptionAsString(FormattingOptions.UNDERLINE), underlinedToggleButton.getText().toString()); + + selectFormatting(FormattingOptions.UNDERLINE); + assertFalse(textTool.underlined); + assertFalse(underlinedToggleButton.isChecked()); + assertEquals(getFormattingOptionAsString(FormattingOptions.UNDERLINE), underlinedToggleButton.getText().toString()); + + selectFormatting(FormattingOptions.ITALIC); + assertTrue(getToolMemberItalic()); + assertTrue(italicToggleButton.isChecked()); + assertEquals(getFormattingOptionAsString(FormattingOptions.ITALIC), italicToggleButton.getText().toString()); + + selectFormatting(FormattingOptions.ITALIC); + assertFalse(getToolMemberItalic()); + assertFalse(italicToggleButton.isChecked()); + assertEquals(getFormattingOptionAsString(FormattingOptions.ITALIC), italicToggleButton.getText().toString()); + + selectFormatting(FormattingOptions.BOLD); + assertTrue(getToolMemberBold()); + assertTrue(boldToggleButton.isChecked()); + assertEquals(getFormattingOptionAsString(FormattingOptions.BOLD), boldToggleButton.getText().toString()); + + selectFormatting(FormattingOptions.BOLD); + assertFalse(getToolMemberBold()); + assertFalse(boldToggleButton.isChecked()); + assertEquals(getFormattingOptionAsString(FormattingOptions.BOLD), boldToggleButton.getText().toString()); + } + + @Test + public void testDialogAndTextBoxAfterReopenDialog() { + enterTestText(); + selectFontType(FontType.SANS_SERIF); + + selectFormatting(FormattingOptions.UNDERLINE); + selectFormatting(FormattingOptions.ITALIC); + selectFormatting(FormattingOptions.BOLD); + + onToolBarView() + .performCloseToolOptionsView(); + + PointF boxPosition = getToolMemberBoxPosition(); + PointF newBoxPosition = new PointF(boxPosition.x + 100, boxPosition.y + 200); + setToolMemberBoxPosition(newBoxPosition); + + onToolBarView() + .performOpenToolOptionsView(); + + assertEquals(TEST_TEXT, textEditText.getText().toString()); + assertEquals(FontType.SANS_SERIF, ((FontListAdapter) fontList.getAdapter()).getSelectedItem()); + assertTrue(underlinedToggleButton.isChecked()); + assertTrue(italicToggleButton.isChecked()); + assertTrue(boldToggleButton.isChecked()); + checkTextBoxDimensions(); + } + + @Test + public void testStateRestoredAfterOrientationChange() { + enterTestText(); + selectFontType(FontType.SANS_SERIF); + selectFormatting(FormattingOptions.UNDERLINE); + selectFormatting(FormattingOptions.ITALIC); + selectFormatting(FormattingOptions.BOLD); + + final PointF toolMemberBoxPosition = getToolMemberBoxPosition(); + PointF expectedPosition = new PointF(toolMemberBoxPosition.x, toolMemberBoxPosition.y); + + activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); + + textTool = (TextTool) toolReference.getTool(); + + assertEquals(TEST_TEXT, textEditText.getText().toString()); + assertEquals(FontType.SANS_SERIF, ((FontListAdapter) fontList.getAdapter()).getSelectedItem()); + assertTrue(underlinedToggleButton.isChecked()); + assertTrue(italicToggleButton.isChecked()); + assertTrue(boldToggleButton.isChecked()); + + assertEquals(expectedPosition, getToolMemberBoxPosition()); + checkTextBoxDimensions(); + } + + @Test + public void testCheckBoxSizeAndContentAfterFormatting() { + enterTestText(); + + assertFalse(underlinedToggleButton.isChecked()); + assertFalse(boldToggleButton.isChecked()); + assertFalse(italicToggleButton.isChecked()); + + ArrayList fonts = new ArrayList<>(); + fonts.add(FontType.SERIF); + fonts.add(FontType.SANS_SERIF); + fonts.add(FontType.MONOSPACE); + fonts.add(FontType.DUBAI); + fonts.add(FontType.STC); + + checkTextBoxDimensionsAndDefaultPosition(); + + for (FontType font : fonts) { + layerModel.getCurrentLayer().getBitmap().eraseColor(Color.TRANSPARENT); + + float boxWidth = getToolMemberBoxWidth(); + float boxHeight = getToolMemberBoxHeight(); + + selectFontType(font); + assertFalse(boxWidth == getToolMemberBoxWidth() && boxHeight == getToolMemberBoxHeight()); + + PointF canvasPoint = centerBox(); + + layerModel.getCurrentLayer().getBitmap().eraseColor(Color.TRANSPARENT); + onTopBarView() + .performClickCheckmark(); + + int surfaceBitmapHeight = layerModel.getHeight(); + int[] pixelsDrawingSurface = new int[surfaceBitmapHeight]; + layerModel.getCurrentLayer().getBitmap().getPixels(pixelsDrawingSurface, 0, 1, (int) canvasPoint.x, 0, 1, surfaceBitmapHeight); + int pixelAmountBefore = countPixelsWithColor(pixelsDrawingSurface, Color.BLACK); + assert pixelAmountBefore > 0; + + selectFormatting(FormattingOptions.UNDERLINE); + assertTrue(underlinedToggleButton.isChecked()); + + layerModel.getCurrentLayer().getBitmap().eraseColor(Color.TRANSPARENT); + onTopBarView() + .performClickCheckmark(); + + layerModel.getCurrentLayer().getBitmap().getPixels(pixelsDrawingSurface, 0, 1, (int) canvasPoint.x, 0, 1, surfaceBitmapHeight); + int pixelAmountAfter = countPixelsWithColor(pixelsDrawingSurface, Color.BLACK); + assert pixelAmountAfter > 0; + + assertTrue(pixelAmountBefore < pixelAmountAfter); + + selectFormatting(FormattingOptions.ITALIC); + assertTrue(italicToggleButton.isChecked()); + assertTrue(getToolMemberItalic()); + + layerModel.getCurrentLayer().getBitmap().eraseColor(Color.TRANSPARENT); + onTopBarView() + .performClickCheckmark(); + + layerModel.getCurrentLayer().getBitmap().getPixels(pixelsDrawingSurface, 0, 1, (int) canvasPoint.x, 0, 1, surfaceBitmapHeight); + pixelAmountBefore = countPixelsWithColor(pixelsDrawingSurface, Color.BLACK); + assert pixelAmountBefore > 0; + + selectFormatting(FormattingOptions.BOLD); + assertTrue(boldToggleButton.isChecked()); + + layerModel.getCurrentLayer().getBitmap().eraseColor(Color.TRANSPARENT); + onTopBarView() + .performClickCheckmark(); + + layerModel.getCurrentLayer().getBitmap().getPixels(pixelsDrawingSurface, 0, 1, (int) canvasPoint.x, 0, 1, surfaceBitmapHeight); + pixelAmountAfter = countPixelsWithColor(pixelsDrawingSurface, Color.BLACK); + assert pixelAmountAfter > 0; + + assertTrue(pixelAmountAfter > pixelAmountBefore); + + selectFormatting(FormattingOptions.UNDERLINE); + assertFalse(underlinedToggleButton.isChecked()); + selectFormatting(FormattingOptions.ITALIC); + assertFalse(italicToggleButton.isChecked()); + selectFormatting(FormattingOptions.BOLD); + assertFalse(boldToggleButton.isChecked()); + } + } + + @Test + public void testInputTextAndFormatForTextSize50() { + enterTestText(); + + float boxWidth = getToolMemberBoxWidth(); + float boxHeight = getToolMemberBoxHeight(); + onView(withId(R.id.pocketpaint_font_size_text)).perform(replaceText("50")); + checkTextBoxDimensions(); + assertTrue("Text box width should be larger with bigger text size", getToolMemberBoxWidth() > boxWidth); + assertTrue("Text box height should be larger with bigger text size", getToolMemberBoxHeight() > boxHeight); + } + + @Test + public void testInputTextAndFormatForTextSize100() { + enterTestText(); + + float boxWidth = getToolMemberBoxWidth(); + float boxHeight = getToolMemberBoxHeight(); + onView(withId(R.id.pocketpaint_font_size_text)).perform(replaceText("100")); + checkTextBoxDimensions(); + assertTrue("Text box width should be larger with bigger text size", getToolMemberBoxWidth() > boxWidth); + assertTrue("Text box height should be larger with bigger text size", getToolMemberBoxHeight() > boxHeight); + } + + @Test + public void testInputTextAndFormatForTextSize300() { + enterTestText(); + + float boxWidth = getToolMemberBoxWidth(); + float boxHeight = getToolMemberBoxHeight(); + onView(withId(R.id.pocketpaint_font_size_text)).perform(replaceText("300")); + checkTextBoxDimensions(); + assertTrue("Text box width should be larger with bigger text size", getToolMemberBoxWidth() > boxWidth); + assertTrue("Text box height should be larger with bigger text size", getToolMemberBoxHeight() > boxHeight); + } + + @Test + public void testCommandUndoAndRedo() { + enterMultilineTestText(); + + onToolBarView() + .performCloseToolOptionsView(); + + PointF canvasPoint = centerBox(); + + onTopBarView() + .performClickCheckmark(); + + int surfaceBitmapWidth = layerModel.getWidth(); + int[] pixelsDrawingSurface = new int[surfaceBitmapWidth]; + layerModel.getCurrentLayer().getBitmap().getPixels(pixelsDrawingSurface, 0, surfaceBitmapWidth, 0, (int) canvasPoint.y, surfaceBitmapWidth, 1); + int pixelAmount = countPixelsWithColor(pixelsDrawingSurface, Color.BLACK); + assert pixelAmount > 0; + + onTopBarView() + .performUndo(); + + layerModel.getCurrentLayer().getBitmap().getPixels(pixelsDrawingSurface, 0, surfaceBitmapWidth, 0, (int) canvasPoint.y, surfaceBitmapWidth, 1); + assertEquals(0, countPixelsWithColor(pixelsDrawingSurface, Color.BLACK)); + + onTopBarView() + .performRedo(); + + layerModel.getCurrentLayer().getBitmap().getPixels(pixelsDrawingSurface, 0, surfaceBitmapWidth, 0, (int) canvasPoint.y, surfaceBitmapWidth, 1); + pixelAmount = countPixelsWithColor(pixelsDrawingSurface, Color.BLACK); + assert pixelAmount > 0; + } + + @Test + public void testChangeTextColor() { + enterTestText(); + + onToolBarView() + .performCloseToolOptionsView(); + + PointF canvasPoint = centerBox(); + + onToolProperties() + .setColor(Color.WHITE); + + Paint paint = textTool.textPaint; + int selectedColor = paint.getColor(); + assertEquals(Color.WHITE, selectedColor); + + onTopBarView() + .performClickCheckmark(); + + int surfaceBitmapWidth = layerModel.getWidth(); + int[] pixelsDrawingSurface = new int[surfaceBitmapWidth]; + layerModel.getCurrentLayer().getBitmap().getPixels(pixelsDrawingSurface, 0, surfaceBitmapWidth, 0, (int) canvasPoint.y, surfaceBitmapWidth, 1); + int pixelAmount = countPixelsWithColor(pixelsDrawingSurface, Color.WHITE); + assert pixelAmount > 0; + + onToolProperties() + .setColor(Color.BLACK); + + selectedColor = paint.getColor(); + assertEquals(Color.BLACK, selectedColor); + + onTopBarView() + .performClickCheckmark(); + + layerModel.getCurrentLayer().getBitmap().getPixels(pixelsDrawingSurface, 0, surfaceBitmapWidth, 0, (int) canvasPoint.y, surfaceBitmapWidth, 1); + pixelAmount = countPixelsWithColor(pixelsDrawingSurface, Color.BLACK); + assert pixelAmount > 0; + } + + @Test + public void testChangeToolFromEraser() { + + int color = textTool.textPaint.getColor(); + + onToolBarView() + .performSelectTool(ToolType.ERASER) + .performSelectTool(ToolType.TEXT); + + int newColor = textTool.textPaint.getColor(); + + assertEquals(color, Color.BLACK); + assertEquals(color, newColor); + } + + @Test + public void testMultiLineText() { + checkTextBoxDimensionsAndDefaultPosition(); + + enterMultilineTestText(); + + onToolBarView() + .performCloseToolOptionsView(); + + String[] expectedTextSplitUp = {"testing", "multiline", "text", "", "123"}; + String[] actualTextSplitUp = getToolMemberMultilineText(); + + assertArrayEquals(expectedTextSplitUp, actualTextSplitUp); + } + + @Test + public void testTextToolAppliedWhenSelectingOtherTool() { + enterTestText(); + + onToolBarView() + .performSelectTool(ToolType.BRUSH); + + int surfaceBitmapWidth = layerModel.getWidth(); + int[] pixelsDrawingSurface = new int[surfaceBitmapWidth]; + layerModel.getCurrentLayer().getBitmap().getPixels(pixelsDrawingSurface, 0, surfaceBitmapWidth, 0, (int) textTool.toolPosition.y, surfaceBitmapWidth, 1); + int numberOfBlackPixels = countPixelsWithColor(pixelsDrawingSurface, Color.BLACK); + assertTrue(numberOfBlackPixels > 0); + } + + @Test + public void testTextToolNotAppliedWhenPressingBack() { + enterTestText(); + + onToolBarView() + .performCloseToolOptionsView(); + + pressBack(); + + int surfaceBitmapWidth = layerModel.getWidth(); + int[] pixelsDrawingSurface = new int[surfaceBitmapWidth]; + layerModel.getCurrentLayer().getBitmap().getPixels(pixelsDrawingSurface, 0, surfaceBitmapWidth, 0, (int) textTool.toolPosition.y, surfaceBitmapWidth, 1); + int numberOfBlackPixels = countPixelsWithColor(pixelsDrawingSurface, Color.BLACK); + assertEquals(0, numberOfBlackPixels); + } + + @Test + public void testTextToolDoesNotResetPerspectiveScale() { + onToolBarView() + .performSelectTool(ToolType.BRUSH); + + float scale = 2.0f; + + activity.perspective.setScale(scale); + activity.perspective.surfaceTranslationY = 200; + activity.perspective.surfaceTranslationX = 50; + activity.refreshDrawingSurface(); + + onToolBarView() + .performSelectTool(ToolType.TEXT); + + enterTestText(); + + assertEquals(scale, activity.perspective.getScale(), 0.0001f); + } + + @Test + public void testTextToolBoxIsPlacedCorrectlyWhenZoomedIn() { + onToolBarView() + .performSelectTool(ToolType.TEXT); + + enterTestText(); + + PointF initialPosition = getToolMemberBoxPosition(); + + onToolBarView() + .performSelectTool(ToolType.BRUSH); + + float scale = 2.0f; + + activity.perspective.setScale(scale); + activity.perspective.surfaceTranslationY = 200; + activity.perspective.surfaceTranslationX = 50; + activity.refreshDrawingSurface(); + + onToolBarView() + .performSelectTool(ToolType.TEXT); + + enterTestText(); + + textToolAfterZoom = (TextTool) activity.toolReference.getTool(); + + PointF positionAfterZoom = getToolMemberBoxPosition(); + + assertEquals(scale, activity.perspective.getScale(), 0.0001f); + + onTopBarView() + .performClickCheckmark(); + + assertNotEquals(initialPosition, positionAfterZoom); + } + + private PointF centerBox() { + PointF screenPoint = new PointF(activityHelper.getDisplayWidth() / 2.0f, activityHelper.getDisplayHeight() / 2.0f); + PointF canvasPoint = new PointF(screenPoint.x, screenPoint.y); + canvasPoint.x = (float) Math.round(canvasPoint.x); + canvasPoint.y = (float) Math.round(canvasPoint.y); + setToolMemberBoxPosition(canvasPoint); + return canvasPoint; + } + + private void checkTextBoxDimensions() { + float actualBoxWidth = getToolMemberBoxWidth(); + float actualBoxHeight = getToolMemberBoxHeight(); + + boolean italic = italicToggleButton.isChecked(); + + FontType font = ((FontListAdapter) fontList.getAdapter()).getSelectedItem(); + + String stringTextSize = textSize.getText().toString(); + float textSize = Float.parseFloat(stringTextSize) * TEXT_SIZE_MAGNIFICATION_FACTOR; + + Paint textPaint = new Paint(); + textPaint.setAntiAlias(true); + textPaint.setTextSize(textSize); + + int style = italic ? Typeface.ITALIC : Typeface.NORMAL; + + switch (font) { + case SANS_SERIF: + textPaint.setTypeface(Typeface.create(Typeface.SANS_SERIF, style)); + break; + case SERIF: + textPaint.setTypeface(Typeface.create(Typeface.SERIF, style)); + break; + case STC: + textPaint.setTypeface(ResourcesCompat.getFont(launchActivityRule.getActivity(), R.font.stc_regular)); + break; + case DUBAI: + textPaint.setTypeface(ResourcesCompat.getFont(launchActivityRule.getActivity(), R.font.dubai)); + break; + default: + textPaint.setTypeface(Typeface.create(Typeface.MONOSPACE, style)); + break; + } + + float textDescent = textPaint.descent(); + float textAscent = textPaint.ascent(); + + String[] multilineText = getToolMemberMultilineText(); + + float maxTextWidth = 0; + for (String str : multilineText) { + float textWidth = textPaint.measureText(str); + if (textWidth > maxTextWidth) { + maxTextWidth = textWidth; + } + } + float expectedBoxWidth = maxTextWidth + 2 * BOX_OFFSET; + + float textHeight = textDescent - textAscent; + float expectedBoxHeight = textHeight * multilineText.length + 2 * BOX_OFFSET; + + assertEquals(expectedBoxWidth, actualBoxWidth, EQUALS_DELTA); + assertEquals(expectedBoxHeight, actualBoxHeight, EQUALS_DELTA); + } + + private void checkTextBoxDefaultPosition() { + float marginTop = MARGIN_TOP; + PointF actualBoxPosition = getToolMemberBoxPosition(); + float boxHeight = getToolMemberBoxHeight(); + + float expectedBoxPositionX = layerModel.getWidth() / 2.0f; + float expectedBoxPositionY = boxHeight / 2.0f + marginTop; + + assertEquals(expectedBoxPositionX, actualBoxPosition.x, EQUALS_DELTA); + assertEquals(expectedBoxPositionY, actualBoxPosition.y, EQUALS_DELTA); + } + + private void checkTextBoxDimensionsAndDefaultPosition() { + checkTextBoxDimensions(); + checkTextBoxDefaultPosition(); + } + + private void enterTextInput(final String textToEnter) { + /* + * Use replaceText instead of typeText to support the arabic input. + * + * See: + * java.ic_pocketpaint_menu_language.RuntimeException: Failed to get key events for string السلام عليكم 123 (i.e. + * current IME does not understand how to translatePerspective the string into key events). As a + * workaround, you can use replaceText action to set the text directly in the EditText field. + */ + + onView(withId(R.id.pocketpaint_text_tool_dialog_input_text)).perform(replaceText(textToEnter)); + Espresso.closeSoftKeyboard(); + onView(withId(R.id.pocketpaint_text_tool_dialog_input_text)).check(matches(withText(textToEnter))); + } + + private void enterTestText() { + enterTextInput(TEST_TEXT); + } + + private void enterMultilineTestText() { + enterTextInput(TEST_TEXT_MULTILINE); + } + + private void selectFormatting(FormattingOptions format) { + onView(withText(getFormattingOptionAsString(format))).perform(click()); + } + + private void selectFontType(FontType fontType) { + onView(withId(R.id.pocketpaint_text_tool_dialog_list_font)) + .perform(RecyclerViewActions.scrollTo(hasDescendant(withText(getFontTypeAsString(fontType))))); + onView(withText(getFontTypeAsString(fontType))) + .perform(click()); + } + + private String getFontTypeAsString(FontType fontType) { + switch (fontType) { + case SANS_SERIF: + return activity.getString(R.string.text_tool_dialog_font_sans_serif); + case SERIF: + return activity.getString(R.string.text_tool_dialog_font_serif); + case MONOSPACE: + return activity.getString(R.string.text_tool_dialog_font_monospace); + case STC: + return activity.getString(R.string.text_tool_dialog_font_arabic_stc); + case DUBAI: + return activity.getString(R.string.text_tool_dialog_font_dubai); + default: + return null; + } + } + + private String getFormattingOptionAsString(FormattingOptions format) { + switch (format) { + case UNDERLINE: + return activity.getString(R.string.text_tool_dialog_underline_shortcut); + case ITALIC: + return activity.getString(R.string.text_tool_dialog_italic_shortcut); + case BOLD: + return activity.getString(R.string.text_tool_dialog_bold_shortcut); + default: + return null; + } + } + + private int countPixelsWithColor(int[] pixels, int color) { + int count = 0; + for (int pixel : pixels) { + if (pixel == color) { + count++; + } + } + return count; + } + + private float getToolMemberBoxWidth() { + return textTool.boxWidth; + } + + private float getToolMemberBoxHeight() { + return textTool.boxHeight; + } + + private PointF getToolMemberBoxPosition() { + if (textToolAfterZoom != null) { + return textToolAfterZoom.toolPosition; + } else { + return textTool.toolPosition; + } + } + + private void setToolMemberBoxPosition(PointF position) { + textTool.toolPosition.set(position); + } + + private boolean getToolMemberItalic() { + return textTool.italic; + } + + private boolean getToolMemberBold() { + return textTool.bold; + } + + private String[] getToolMemberMultilineText() { + return textTool.getMultilineText(); + } + + private enum FormattingOptions { + UNDERLINE, ITALIC, BOLD + } +} diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/tools/WatercolorToolIntegrationTest.kt b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/tools/WatercolorToolIntegrationTest.kt new file mode 100644 index 0000000000..feb8caf5ee --- /dev/null +++ b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/tools/WatercolorToolIntegrationTest.kt @@ -0,0 +1,151 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2022 The Catrobat Team + * () + * + * 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. + * + * 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 . + */ +package org.catrobat.paintroid.test.espresso.tools + +import android.graphics.Color +import androidx.test.espresso.Espresso.onView +import androidx.test.espresso.Espresso.pressBack +import androidx.test.espresso.action.ViewActions.click +import androidx.test.espresso.action.ViewActions.scrollTo +import androidx.test.espresso.assertion.ViewAssertions.matches +import androidx.test.espresso.matcher.ViewMatchers.isDisplayed +import androidx.test.espresso.matcher.ViewMatchers.withId +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.rule.ActivityTestRule +import org.catrobat.paintroid.MainActivity +import org.catrobat.paintroid.R +import org.catrobat.paintroid.test.espresso.util.BitmapLocationProvider +import org.catrobat.paintroid.test.espresso.util.DrawingSurfaceLocationProvider +import org.catrobat.paintroid.test.espresso.util.UiInteractions +import org.catrobat.paintroid.test.espresso.util.wrappers.BottomNavigationViewInteraction +import org.catrobat.paintroid.test.espresso.util.wrappers.ColorPickerViewInteraction.onColorPickerView +import org.catrobat.paintroid.test.espresso.util.wrappers.DrawingSurfaceInteraction +import org.catrobat.paintroid.test.espresso.util.wrappers.ToolBarViewInteraction +import org.catrobat.paintroid.test.utils.ScreenshotOnFailRule +import org.catrobat.paintroid.tools.ToolType +import org.junit.Assert.assertTrue +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith + +@RunWith(AndroidJUnit4::class) +class WatercolorToolIntegrationTest { + + @get:Rule + var launchActivityRule = ActivityTestRule(MainActivity::class.java) + + @get:Rule + var screenshotOnFailRule = ScreenshotOnFailRule() + + @Before + fun setUp() { + ToolBarViewInteraction.onToolBarView().performSelectTool(ToolType.WATERCOLOR) + } + + @Test + fun testSwitchingBackToBrushOnBackPressed() { + DrawingSurfaceInteraction.onDrawingSurfaceView() + .perform(UiInteractions.touchAt(DrawingSurfaceLocationProvider.MIDDLE)) + + assertTrue( + "Paint has maskFilter", + launchActivityRule.activity.toolPaint.paint.maskFilter != null + ) + + pressBack() + + BottomNavigationViewInteraction.onBottomNavigationView() + .checkShowsCurrentTool(ToolType.BRUSH) + + DrawingSurfaceInteraction.onDrawingSurfaceView() + .perform(UiInteractions.touchAt(DrawingSurfaceLocationProvider.MIDDLE)) + + assertTrue( + "Paint maskFilter has been reset", + launchActivityRule.activity.toolPaint.paint.maskFilter == null + ) + } + + @Test + fun testSwitchingBetweenBrushAndWatercolorAndEraserAndLookForMaskFilter() { + DrawingSurfaceInteraction.onDrawingSurfaceView() + .perform(UiInteractions.touchAt(DrawingSurfaceLocationProvider.MIDDLE)) + + assertTrue( + "Paint has no maskFilter", + launchActivityRule.activity.toolPaint.paint.maskFilter != null + ) + + ToolBarViewInteraction.onToolBarView() + .performSelectTool(ToolType.ERASER) + + DrawingSurfaceInteraction.onDrawingSurfaceView() + .perform(UiInteractions.touchAt(DrawingSurfaceLocationProvider.MIDDLE)) + + DrawingSurfaceInteraction.onDrawingSurfaceView() + .checkPixelColor(Color.TRANSPARENT, BitmapLocationProvider.MIDDLE) + + ToolBarViewInteraction.onToolBarView() + .performSelectTool(ToolType.BRUSH) + + BottomNavigationViewInteraction.onBottomNavigationView() + .checkShowsCurrentTool(ToolType.BRUSH) + + DrawingSurfaceInteraction.onDrawingSurfaceView() + .perform(UiInteractions.touchAt(DrawingSurfaceLocationProvider.MIDDLE)) + + ToolBarViewInteraction.onToolBarView() + .performSelectTool(ToolType.PIPETTE) + + DrawingSurfaceInteraction.onDrawingSurfaceView() + .perform(UiInteractions.touchAt(DrawingSurfaceLocationProvider.MIDDLE)) + + assertTrue( + "Paint still has maskFilter", + launchActivityRule.activity.toolPaint.paint.maskFilter == null + ) + } + + @Test + fun drawOnBitmapThenChangeMaskFilter() { + DrawingSurfaceInteraction.onDrawingSurfaceView() + .perform(UiInteractions.touchAt(DrawingSurfaceLocationProvider.MIDDLE)) + + val oldFilter = launchActivityRule.activity.toolPaint.paint.maskFilter + + onColorPickerView() + .performOpenColorPicker() + + onView(withId(R.id.color_alpha_slider)) + .perform(scrollTo()) + .check(matches(isDisplayed())) + .perform(click()) + + onColorPickerView() + .onPositiveButton() + .perform(click()) + + DrawingSurfaceInteraction.onDrawingSurfaceView() + .perform(UiInteractions.touchAt(DrawingSurfaceLocationProvider.TOP_MIDDLE)) + + val newFilter = launchActivityRule.activity.toolPaint.paint.maskFilter + assertTrue("oldFilter and newFilter is the same", oldFilter != newFilter) + } +} diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/BitmapLocationProvider.java b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/BitmapLocationProvider.java new file mode 100644 index 0000000000..1f25b64e59 --- /dev/null +++ b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/BitmapLocationProvider.java @@ -0,0 +1,100 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2015 The Catrobat Team + * () + * + * 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. + * + * 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 . + */ + +package org.catrobat.paintroid.test.espresso.util; + +import android.view.View; + +import org.catrobat.paintroid.MainActivity; +import org.catrobat.paintroid.tools.Workspace; + +import androidx.test.espresso.action.CoordinatesProvider; + +import static org.catrobat.paintroid.test.espresso.util.MainActivityHelper.getMainActivityFromView; + +public enum BitmapLocationProvider implements CoordinatesProvider{ + MIDDLE { + @Override + public float[] calculateCoordinates(View view) { + return calculatePercentageOffset(view, .5f, .5f); + } + }, + MIDDLE_RIGHT { + @Override + public float[] calculateCoordinates(View view) { + return calculatePercentageOffset(view, 1f, .5f); + } + }, + OUTSIDE_MIDDLE_RIGHT { + @Override + public float[] calculateCoordinates(View view) { + return calculatePercentageOffset(view, 1.5f, .5f); + } + }, + HALFWAY_RIGHT_MIDDLE { + @Override + public float[] calculateCoordinates(View view) { + return calculatePercentageOffset(view, .75f, .5f); + } + }, + HALFWAY_LEFT_MIDDLE { + @Override + public float[] calculateCoordinates(View view) { + return calculatePercentageOffset(view, .25f, .5f); + } + }, + HALFWAY_BOTTOM_MIDDLE { + @Override + public float[] calculateCoordinates(View view) { + return calculatePercentageOffset(view, .5f, .75f); + } + }, + HALFWAY_TOP_MIDDLE { + @Override + public float[] calculateCoordinates(View view) { + return calculatePercentageOffset(view, .5f, .25f); + } + }, + HALFWAY_TOP_LEFT { + @Override + public float[] calculateCoordinates(View view) { + return calculatePercentageOffset(view, .25f, .25f); + } + }, + HALFWAY_BOTTOM_RIGHT { + @Override + public float[] calculateCoordinates(View view) { + return calculatePercentageOffset(view, .75f, .75f); + } + }, + HALFWAY_BOTTOM_LEFT { + @Override + public float[] calculateCoordinates(View view) { + return calculatePercentageOffset(view, .25f, .75f); + } + }; + + private static float[] calculatePercentageOffset(View view, float percentageX, float percentageY) { + MainActivity mainActivity = getMainActivityFromView(view); + Workspace workspace = mainActivity.workspace; + float pointX = (workspace.getWidth() - 1) * percentageX; + float pointY = workspace.getHeight() * percentageY; + return new float[] {pointX, pointY}; + } +} diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/CustomSwiper.java b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/CustomSwiper.java new file mode 100644 index 0000000000..ed47dc3029 --- /dev/null +++ b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/CustomSwiper.java @@ -0,0 +1,73 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2015 The Catrobat Team + * () + * + * 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. + * + * 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 . + */ + +package org.catrobat.paintroid.test.espresso.util; + +import android.os.SystemClock; +import android.view.MotionEvent; + +import androidx.test.espresso.UiController; +import androidx.test.espresso.action.MotionEvents; +import androidx.test.espresso.action.Swiper; + +public enum CustomSwiper implements Swiper { + ACCURATE { + @Override + public Status sendSwipe(UiController uiController, float[] startCoordinates, float[] endCoordinates, float[] precision) { + final float[][] steps = interpolate(startCoordinates, endCoordinates, STEPS); + final int delayBetweenMovements = DURATION / steps.length; + + MotionEvent downEvent = MotionEvents.sendDown(uiController, startCoordinates, precision).down; + try { + for (int i = 0; i < steps.length; i++) { + MotionEvents.sendMovement(uiController, downEvent, steps[i]); + long desiredTime = downEvent.getDownTime() + delayBetweenMovements * i; + long timeUntilDesired = desiredTime - SystemClock.uptimeMillis(); + if (timeUntilDesired > 10) { + uiController.loopMainThreadForAtLeast(timeUntilDesired); + } + } + MotionEvents.sendUp(uiController, downEvent, endCoordinates); + } finally { + downEvent.recycle(); + } + return Status.SUCCESS; + } + }; + + private static final int STEPS = 10; + private static final int DURATION = 500; + + private static float[][] interpolate(float[] start, float[] end, int steps) { + float[][] res = new float[steps][2]; + + res[0][0] = start[0]; + res[0][1] = start[1]; + + for (int i = 2; i < steps; i++) { + res[i - 1][0] = start[0] + (end[0] - start[0]) * i / (steps + 2f); + res[i - 1][1] = start[1] + (end[1] - start[1]) * i / (steps + 2f); + } + + res[steps - 1][0] = end[0]; + res[steps - 1][1] = end[1]; + + return res; + } +} diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/DrawingSurfaceLocationProvider.java b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/DrawingSurfaceLocationProvider.java new file mode 100644 index 0000000000..39af0c3434 --- /dev/null +++ b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/DrawingSurfaceLocationProvider.java @@ -0,0 +1,168 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2015 The Catrobat Team + * () + * + * 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. + * + * 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 . + */ + +package org.catrobat.paintroid.test.espresso.util; + +import android.graphics.PointF; +import android.view.View; + +import org.catrobat.paintroid.MainActivity; +import org.catrobat.paintroid.tools.Workspace; +import org.catrobat.paintroid.tools.implementation.BaseToolWithShape; + +import androidx.test.espresso.action.CoordinatesProvider; + +import static org.catrobat.paintroid.test.espresso.util.MainActivityHelper.getMainActivityFromView; +import static org.catrobat.paintroid.test.espresso.util.PositionCoordinatesProvider.calculateViewOffset; + +public enum DrawingSurfaceLocationProvider implements CoordinatesProvider { + MIDDLE { + @Override + public float[] calculateCoordinates(View view) { + return calculatePercentageOffset(view, .5f, .5f); + } + }, + LEFT_MIDDLE { + @Override + public float[] calculateCoordinates(View view) { + return calculatePercentageOffset(view, .0f, .5f); + } + }, + HALFWAY_LEFT_MIDDLE { + @Override + public float[] calculateCoordinates(View view) { + return calculatePercentageOffset(view, .25f, .5f); + } + }, + RIGHT_MIDDLE { + @Override + public float[] calculateCoordinates(View view) { + return calculatePercentageOffset(view, 1f, .5f); + } + }, + HALFWAY_RIGHT_MIDDLE { + @Override + public float[] calculateCoordinates(View view) { + return calculatePercentageOffset(view, .75f, .5f); + } + }, + TOP_MIDDLE { + @Override + public float[] calculateCoordinates(View view) { + return calculatePercentageOffset(view, .5f, 0f); + } + }, + HALFWAY_TOP_MIDDLE { + @Override + public float[] calculateCoordinates(View view) { + return calculatePercentageOffset(view, .5f, .25f); + } + }, + HALFWAY_BOTTOM_MIDDLE { + @Override + public float[] calculateCoordinates(View view) { + return calculatePercentageOffset(view, .5f, .75f); + } + }, + HALFWAY_TOP_LEFT { + @Override + public float[] calculateCoordinates(View view) { + return calculatePercentageOffset(view, .25f, .25f); + } + }, + HALFWAY_TOP_RIGHT { + @Override + public float[] calculateCoordinates(View view) { + return calculatePercentageOffset(view, .75f, .25f); + } + }, + HALFWAY_BOTTOM_LEFT { + @Override + public float[] calculateCoordinates(View view) { + return calculatePercentageOffset(view, .25f, .75f); + } + }, + HALFWAY_BOTTOM_RIGHT { + @Override + public float[] calculateCoordinates(View view) { + return calculatePercentageOffset(view, .75f, .75f); + } + }, + BOTTOM_RIGHT_CLOSE_CENTER { + @Override + public float[] calculateCoordinates(View view) { + return calculatePercentageOffset(view, .55f, .55f); + } + }, + BOTTOM_RIGHT_CORNER { + @Override + public float[] calculateCoordinates(View view) { + return calculatePercentageOffset(view, 1f, 1f); + } + }, + BOTTOM_MIDDLE { + @Override + public float[] calculateCoordinates(View view) { + return calculatePercentageOffset(view, .5f, 1f); + } + }, + OUTSIDE_MIDDLE_RIGHT { + @Override + public float[] calculateCoordinates(View view) { + return calculatePercentageOffset(view, 1.5f, .5f); + } + }, + OUTSIDE_MIDDLE_LEFT { + @Override + public float[] calculateCoordinates(View view) { + return calculatePercentageOffset(view, -.3f, .5f); + } + }, + OUTSIDE_MIDDLE_BOTTOM { + @Override + public float[] calculateCoordinates(View view) { + return calculatePercentageOffset(view, 1.3f, .5f); + } + }, + OUTSIDE_MIDDLE_TOP { + @Override + public float[] calculateCoordinates(View view) { + return calculatePercentageOffset(view, .5f, -.3f); + } + }, + TOOL_POSITION { + @Override + public float[] calculateCoordinates(View view) { + MainActivity mainActivity = getMainActivityFromView(view); + Workspace workspace = mainActivity.workspace; + PointF toolPosition = ((BaseToolWithShape) mainActivity.toolReference.getTool()).toolPosition; + PointF point = workspace.getSurfacePointFromCanvasPoint(toolPosition); + return calculateViewOffset(view, point.x, point.y); + } + }; + + private static float[] calculatePercentageOffset(View view, float percentageX, float percentageY) { + MainActivity mainActivity = getMainActivityFromView(view); + Workspace workspace = mainActivity.workspace; + float pointX = workspace.getWidth() * percentageX; + float pointY = workspace.getHeight() * percentageY; + PointF point = workspace.getSurfacePointFromCanvasPoint(new PointF(pointX, pointY)); + return calculateViewOffset(view, point.x, point.y); + } +} diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/EspressoUtils.java b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/EspressoUtils.java new file mode 100644 index 0000000000..0f619b3e6e --- /dev/null +++ b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/EspressoUtils.java @@ -0,0 +1,146 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2015 The Catrobat Team + * () + * + * 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. + * + * 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 . + */ + +package org.catrobat.paintroid.test.espresso.util; + +import android.Manifest; +import android.app.Activity; +import android.content.Context; +import android.content.res.Configuration; +import android.content.res.Resources; +import android.graphics.PointF; +import android.os.Build; +import android.util.DisplayMetrics; +import android.util.TypedValue; +import android.view.View; + +import org.catrobat.paintroid.MainActivity; +import org.hamcrest.Matcher; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import androidx.test.espresso.NoMatchingViewException; +import androidx.test.espresso.ViewInteraction; +import androidx.test.platform.app.InstrumentationRegistry; +import androidx.test.rule.GrantPermissionRule; +import androidx.test.runner.lifecycle.ActivityLifecycleMonitorRegistry; +import androidx.test.runner.lifecycle.Stage; + +import static org.catrobat.paintroid.test.espresso.util.UiMatcher.isToast; +import static org.junit.Assert.assertNotEquals; + +import static androidx.test.espresso.Espresso.onView; +import static androidx.test.espresso.assertion.ViewAssertions.matches; +import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed; +import static androidx.test.espresso.matcher.ViewMatchers.isRoot; + +public final class EspressoUtils { + + public static final int DEFAULT_STROKE_WIDTH = 25; + + public static MainActivity getMainActivity() { + final List resumedActivities = new ArrayList<>(); + InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() { + @Override + public void run() { + Collection activitiesInStage = ActivityLifecycleMonitorRegistry.getInstance().getActivitiesInStage(Stage.RESUMED); + resumedActivities.addAll(activitiesInStage); + } + }); + return (MainActivity) resumedActivities.get(0); + } + + private EspressoUtils() { + } + + /** + * @deprecated Do not use this in new tests + */ + @Deprecated + public static float getActionbarHeight() { + Context context = InstrumentationRegistry.getInstrumentation().getTargetContext(); + TypedValue tv = new TypedValue(); + context.getTheme().resolveAttribute(android.R.attr.actionBarSize, tv, true); + Resources resources = context.getResources(); + DisplayMetrics metrics = resources.getDisplayMetrics(); + return TypedValue.complexToDimensionPixelSize(tv.data, metrics); + } + + /** + * @deprecated Do not use this in new tests + */ + @Deprecated + public static float getStatusbarHeight() { + Resources resources = InstrumentationRegistry.getInstrumentation().getTargetContext().getResources(); + int resourceId = resources.getIdentifier("status_bar_height", "dimen", "android"); + assertNotEquals(0, resourceId); + return resources.getDimensionPixelSize(resourceId); + } + + /** + * @deprecated Do not use this in new tests + */ + @Deprecated + public static PointF getSurfacePointFromScreenPoint(PointF screenPoint) { + return new PointF(screenPoint.x, screenPoint.y - getActionbarHeight() - getStatusbarHeight()); + } + + /** + * @deprecated Do not use this in new tests + */ + @Deprecated + public static PointF getScreenPointFromSurfaceCoordinates(float pointX, float pointY) { + return new PointF(pointX, pointY + getStatusbarHeight() + getActionbarHeight()); + } + + public static void waitForToast(Matcher viewMatcher, int duration) { + final long waitTime = System.currentTimeMillis() + duration; + final ViewInteraction viewInteraction = onView(viewMatcher).inRoot(isToast()); + + while (System.currentTimeMillis() < waitTime) { + try { + viewInteraction.check(matches(isDisplayed())); + return; + } catch (NoMatchingViewException e) { + waitMillis(250); + } + } + + viewInteraction.check(matches(isDisplayed())); + } + + public static void waitMillis(final long millis) { + onView(isRoot()).perform(UiInteractions.waitFor(millis)); + } + + public static Configuration getConfiguration() { + return InstrumentationRegistry.getInstrumentation().getTargetContext().getResources().getConfiguration(); + } + + public static GrantPermissionRule grantPermissionRulesVersionCheck() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + return GrantPermissionRule.grant(Manifest.permission.READ_EXTERNAL_STORAGE); + } else { + return GrantPermissionRule.grant(Manifest.permission.WRITE_EXTERNAL_STORAGE, + Manifest.permission.READ_EXTERNAL_STORAGE); + } + } +} diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/LanguageSupport.java b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/LanguageSupport.java new file mode 100644 index 0000000000..9177c524f1 --- /dev/null +++ b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/LanguageSupport.java @@ -0,0 +1,46 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2021 The Catrobat Team + * () + * + * 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. + * + * 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 . + */ + +package org.catrobat.paintroid.test.espresso.util; + +import android.content.Context; +import android.content.res.Configuration; +import android.content.res.Resources; +import android.os.Build; + +import java.util.Locale; + +public final class LanguageSupport { + private LanguageSupport() { + throw new IllegalArgumentException(); + } + + public static void setLocale(Context context, Locale locale) { + if (Build.VERSION.SDK_INT >= 24) { + Locale.setDefault(Locale.Category.DISPLAY, locale); + } else { + Locale.setDefault(locale); + } + + Resources resources = context.getResources(); + Configuration conf = resources.getConfiguration(); + conf.setLocale(locale); + resources.updateConfiguration(conf, resources.getDisplayMetrics()); + } +} diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/MainActivityHelper.java b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/MainActivityHelper.java new file mode 100644 index 0000000000..654eb47010 --- /dev/null +++ b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/MainActivityHelper.java @@ -0,0 +1,68 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2015 The Catrobat Team + * () + * + * 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. + * + * 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 . + */ + +package org.catrobat.paintroid.test.espresso.util; + +import android.content.Context; +import android.content.ContextWrapper; +import android.graphics.Point; +import android.view.View; + +import org.catrobat.paintroid.MainActivity; + +public class MainActivityHelper { + private final MainActivity activity; + + public MainActivityHelper(MainActivity activity) { + this.activity = activity; + } + + public Point getDisplaySize() { + Point displaySize = new Point(); + activity.getWindowManager().getDefaultDisplay().getSize(displaySize); + return displaySize; + } + + public int getDisplayWidth() { + return getDisplaySize().x; + } + + public int getDisplayHeight() { + return getDisplaySize().y; + } + + public int getScreenOrientation() { + return activity.getRequestedOrientation(); + } + + public void setScreenOrientation(int orientation) { + activity.setRequestedOrientation(orientation); + } + + public static MainActivity getMainActivityFromView(View view) { + Context context = view.getContext(); + while (context instanceof ContextWrapper) { + if (context instanceof MainActivity) { + return (MainActivity) context; + } + context = ((ContextWrapper) context).getBaseContext(); + } + throw new RuntimeException("View context does not implement MainActivity"); + } +} diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/OffsetLocationProvider.java b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/OffsetLocationProvider.java new file mode 100644 index 0000000000..8ef3730679 --- /dev/null +++ b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/OffsetLocationProvider.java @@ -0,0 +1,49 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2015 The Catrobat Team + * () + * + * 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. + * + * 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 . + */ + +package org.catrobat.paintroid.test.espresso.util; + +import android.view.View; + +import androidx.test.espresso.action.CoordinatesProvider; + +public class OffsetLocationProvider implements CoordinatesProvider { + private final CoordinatesProvider locationProvider; + private final int xOffset; + private final int yOffset; + + public OffsetLocationProvider(CoordinatesProvider locationProvider, int xOffset, int yOffset) { + + this.locationProvider = locationProvider; + this.xOffset = xOffset; + this.yOffset = yOffset; + } + + public static CoordinatesProvider withOffset(CoordinatesProvider locationProvider, int xOffset, int yOffset) { + return new OffsetLocationProvider(locationProvider, xOffset, yOffset); + } + + @Override + public float[] calculateCoordinates(View view) { + float[] coordinates = locationProvider.calculateCoordinates(view); + coordinates[0] += xOffset; + coordinates[1] += yOffset; + return coordinates; + } +} diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/PositionCoordinatesProvider.java b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/PositionCoordinatesProvider.java new file mode 100644 index 0000000000..bbc957eb85 --- /dev/null +++ b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/PositionCoordinatesProvider.java @@ -0,0 +1,52 @@ +/** + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2015 The Catrobat Team + * () + *

+ * 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. + *

+ * 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 . + */ + +package org.catrobat.paintroid.test.espresso.util; + +import android.view.View; + +import androidx.test.espresso.action.CoordinatesProvider; + +public class PositionCoordinatesProvider implements CoordinatesProvider { + private final float xCoordinate; + private final float yCoordinate; + + public PositionCoordinatesProvider(float x, float y) { + this.xCoordinate = x; + this.yCoordinate = y; + } + + public static CoordinatesProvider at(float x, float y) { + return new PositionCoordinatesProvider(x, y); + } + + @Override + public float[] calculateCoordinates(View view) { + return calculateViewOffset(view, xCoordinate, yCoordinate); + } + + public static float[] calculateViewOffset(View view, float x, float y) { + final int[] screenLocation = new int[2]; + view.getLocationOnScreen(screenLocation); + + final float touchX = screenLocation[0] + x; + final float touchY = screenLocation[1] + y; + return new float[] {touchX, touchY}; + } +} diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/UiInteractions.java b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/UiInteractions.java new file mode 100644 index 0000000000..c1d16eea9b --- /dev/null +++ b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/UiInteractions.java @@ -0,0 +1,309 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2015 The Catrobat Team + * () + * + * 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. + * + * 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 . + */ + +package org.catrobat.paintroid.test.espresso.util; + +import android.graphics.PointF; +import android.graphics.Rect; +import android.view.InputDevice; +import android.view.MotionEvent; +import android.view.View; +import android.widget.ListAdapter; +import android.widget.ListView; +import android.widget.SeekBar; + +import org.hamcrest.Matcher; + +import androidx.test.espresso.NoMatchingViewException; +import androidx.test.espresso.UiController; +import androidx.test.espresso.ViewAction; +import androidx.test.espresso.ViewAssertion; +import androidx.test.espresso.action.CoordinatesProvider; +import androidx.test.espresso.action.GeneralClickAction; +import androidx.test.espresso.action.GeneralLocation; +import androidx.test.espresso.action.GeneralSwipeAction; +import androidx.test.espresso.action.MotionEvents; +import androidx.test.espresso.action.Press; +import androidx.test.espresso.action.ScrollToAction; +import androidx.test.espresso.action.Swipe; +import androidx.test.espresso.action.Tap; +import androidx.test.espresso.action.Tapper; +import androidx.test.espresso.matcher.ViewMatchers; +import androidx.viewpager.widget.ViewPager; + +import static org.catrobat.paintroid.test.espresso.util.CustomSwiper.ACCURATE; +import static org.hamcrest.Matchers.is; + +import static androidx.test.espresso.action.ViewActions.actionWithAssertions; +import static androidx.test.espresso.matcher.ViewMatchers.assertThat; +import static androidx.test.espresso.matcher.ViewMatchers.isAssignableFrom; +import static androidx.test.espresso.matcher.ViewMatchers.isRoot; +import static androidx.test.espresso.matcher.ViewMatchers.withEffectiveVisibility; + +public final class UiInteractions { + + private UiInteractions() { + } + + public static ViewAction unconstrainedScrollTo() { + return actionWithAssertions(new UnconstrainedScrollToAction()); + } + + public static ViewAction waitFor(final long millis) { + return new ViewAction() { + @Override + public Matcher getConstraints() { + return isRoot(); + } + + @Override + public String getDescription() { + return "Wait for " + millis + " milliseconds."; + } + + @Override + public void perform(UiController uiController, final View view) { + uiController.loopMainThreadForAtLeast(millis); + } + }; + } + + public static ViewAssertion assertListViewCount(final int expectedCount) { + return new ViewAssertion() { + @Override + public void check(View view, NoMatchingViewException noViewFoundException) { + if (noViewFoundException != null) { + throw noViewFoundException; + } + + ListAdapter adapter = ((ListView) view).getAdapter(); + assertThat(adapter.getCount(), is(expectedCount)); + } + }; + } + + public static ViewAction setProgress(final int progress) { + return new ViewAction() { + + @Override + public Matcher getConstraints() { + return isAssignableFrom(SeekBar.class); + } + + @Override + public String getDescription() { + return "Set a progress"; + } + + @Override + public void perform(UiController uiController, View view) { + ((SeekBar) view).setProgress(progress); + } + }; + } + + public static ViewAction clickOutside(final Direction direction) { + return actionWithAssertions( + new GeneralClickAction(Tap.SINGLE, new CoordinatesProvider() { + @Override + public float[] calculateCoordinates(View view) { + Rect r = new Rect(); + view.getGlobalVisibleRect(r); + switch (direction) { + case ABOVE: + return new float[]{r.centerX(), r.top - 50}; + case BELOW: + return new float[]{r.centerX(), r.bottom + 50}; + case LEFT: + return new float[]{r.left - 50, r.centerY()}; + case RIGHT: + return new float[]{r.right + 50, r.centerY()}; + } + return null; + } + }, Press.FINGER, 0, 1) + ); + } + + public static ViewAction touchAt(final CoordinatesProvider provider) { + return touchAt(provider, Tap.SINGLE); + } + + public static ViewAction touchAt(final PointF coordinates) { + return touchAt(coordinates, Tap.SINGLE); + } + + public static ViewAction touchAt(final int x, final int y) { + return touchAt((float) x, (float) y); + } + + public static ViewAction touchAt(final float x, final float y) { + return touchAt(x, y, Tap.SINGLE); + } + + public static ViewAction touchLongAt(final CoordinatesProvider provider) { + return touchAt(provider, Tap.LONG); + } + + public static ViewAction touchLongAt(final PointF coordinates) { + return touchAt(coordinates, Tap.LONG); + } + + public static ViewAction touchLongAt(final float x, final float y) { + return touchAt(x, y, Tap.LONG); + } + + public static ViewAction touchAt(final CoordinatesProvider provider, final Tapper tapStyle) { + return actionWithAssertions( + new GeneralClickAction(tapStyle, provider, Press.FINGER, 0, 0)); + } + + public static ViewAction touchAt(final PointF coordinates, final Tapper tapStyle) { + return touchAt(coordinates.x, coordinates.y, tapStyle); + } + + public static ViewAction touchAt(final float x, final float y, final Tapper tapStyle) { + return actionWithAssertions( + new GeneralClickAction(tapStyle, PositionCoordinatesProvider.at(x, y), Press.FINGER, 0, 0) + ); + } + + public static ViewAction touchCenterLeft() { + return new GeneralClickAction(Tap.SINGLE, GeneralLocation.CENTER_LEFT, Press.FINGER, 0, 0); + } + + public static ViewAction touchCenterMiddle() { + return new GeneralClickAction(Tap.SINGLE, GeneralLocation.CENTER, Press.FINGER, 0, 0); + } + + public static ViewAction touchCenterRight() { + return new GeneralClickAction(Tap.SINGLE, GeneralLocation.CENTER_RIGHT, Press.FINGER, 0, 0); + } + + public static ViewAction swipe(PointF start, PointF end) { + return swipe((int) start.x, (int) start.y, (int) end.x, (int) end.y); + } + + public static ViewAction swipe(float startX, float startY, float endX, float endY) { + return swipe((int) startX, (int) startY, (int) endX, (int) endY); + } + + public static ViewAction swipe(int startX, int startY, int endX, int endY) { + return swipe(PositionCoordinatesProvider.at(startX, startY), PositionCoordinatesProvider.at(endX, endY)); + } + + public static ViewAction swipe(CoordinatesProvider startCoordinatesProvider, CoordinatesProvider endCoordinatesProvider) { + return new GeneralSwipeAction(Swipe.FAST, startCoordinatesProvider, endCoordinatesProvider, Press.FINGER); + } + + public static ViewAction swipeAccurate(CoordinatesProvider startCoordinatesProvider, CoordinatesProvider endCoordinatesProvider) { + return new GeneralSwipeAction(ACCURATE, startCoordinatesProvider, endCoordinatesProvider, Press.FINGER); + } + + public static ViewAction selectViewPagerPage(final int pos) { + return new ViewAction() { + @Override + public Matcher getConstraints() { + return isAssignableFrom(ViewPager.class); + } + + @Override + public String getDescription() { + return "select page in ViewPager"; + } + + @Override + public void perform(UiController uiController, View view) { + ((ViewPager) view).setCurrentItem(pos); + } + }; + } + + public enum Direction { + ABOVE, + BELOW, + LEFT, + RIGHT + } + + private static class UnconstrainedScrollToAction implements ViewAction { + private ViewAction action = new ScrollToAction(); + + @Override + public Matcher getConstraints() { + return withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE); + } + + @Override + public String getDescription() { + return action.getDescription(); + } + + @Override + public void perform(UiController uiController, View view) { + action.perform(uiController, view); + } + } + + public static class DefinedLongTap implements Tapper { + + private int longPressTimeout; + + DefinedLongTap(int longPressTimeout) { + this.longPressTimeout = longPressTimeout; + } + + public static Tapper withPressTimeout(final int longPressTimeout) { + return new DefinedLongTap(longPressTimeout); + } + + @Override + public Status sendTap( + UiController uiController, float[] coordinates, float[] precision, int inputDevice, + int buttonState) { + MotionEvent downEvent = MotionEvents.sendDown(uiController, coordinates, precision, + inputDevice, buttonState).down; + try { + // Duration before a press turns into a long press. + // Factor 1.5 is needed, otherwise a long press is not safely detected. + // See android.test.TouchUtils longClickView + long longPressTimeout = (long) (this.longPressTimeout * 1.5f); + uiController.loopMainThreadForAtLeast(longPressTimeout); + + if (!MotionEvents.sendUp(uiController, downEvent)) { + MotionEvents.sendCancel(uiController, downEvent); + return Status.FAILURE; + } + } finally { + downEvent.recycle(); + } + return Status.SUCCESS; + } + + /** + * @deprecated use other sendTap instead + */ + @Deprecated + @Override + public Status sendTap(UiController uiController, float[] coordinates, float[] precision) { + return sendTap(uiController, coordinates, precision, InputDevice.SOURCE_UNKNOWN, + MotionEvent.BUTTON_PRIMARY); + } + } +} diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/UiMatcher.java b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/UiMatcher.java new file mode 100644 index 0000000000..3fc0475ff4 --- /dev/null +++ b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/UiMatcher.java @@ -0,0 +1,600 @@ +/** + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2015 The Catrobat Team + * () + *

+ * 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. + *

+ * 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 . + */ + +package org.catrobat.paintroid.test.espresso.util; + +import android.content.res.Resources; +import android.graphics.Bitmap; +import android.graphics.Rect; +import android.graphics.Typeface; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.ColorDrawable; +import android.graphics.drawable.Drawable; +import android.graphics.drawable.LayerDrawable; +import android.graphics.drawable.StateListDrawable; +import android.graphics.drawable.VectorDrawable; +import android.view.MenuItem; +import android.view.View; +import android.view.ViewGroup; +import android.view.ViewParent; +import android.view.WindowManager; +import android.widget.Adapter; +import android.widget.AdapterView; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.SeekBar; +import android.widget.TableRow; +import android.widget.TextView; + +import org.hamcrest.Description; +import org.hamcrest.Matcher; +import org.hamcrest.TypeSafeMatcher; + +import androidx.annotation.NonNull; +import androidx.recyclerview.widget.RecyclerView; +import androidx.test.espresso.NoMatchingViewException; +import androidx.test.espresso.Root; +import androidx.test.espresso.ViewAssertion; +import androidx.test.espresso.matcher.BoundedMatcher; +import androidx.test.espresso.matcher.ViewMatchers; +import androidx.test.espresso.util.HumanReadables; +import androidx.vectordrawable.graphics.drawable.VectorDrawableCompat; + +import static org.hamcrest.Matchers.is; +import static org.junit.Assert.assertThat; + +import static androidx.core.util.Preconditions.checkNotNull; +import static androidx.test.espresso.matcher.ViewMatchers.withEffectiveVisibility; + +public final class UiMatcher { + + private UiMatcher() { + } + + public static Matcher atPosition(final int position, @NonNull final Matcher itemMatcher) { + checkNotNull(itemMatcher); + return new BoundedMatcher(RecyclerView.class) { + @Override + public void describeTo(Description description) { + description.appendText("has item at position " + position + ": "); + itemMatcher.describeTo(description); + } + + @Override + protected boolean matchesSafely(final RecyclerView view) { + RecyclerView.ViewHolder viewHolder = view.findViewHolderForAdapterPosition(position); + if (viewHolder == null) { + // has no item on such position + return false; + } + return itemMatcher.matches(viewHolder.itemView); + } + }; + } + + public static Matcher withIndex(final Matcher matcher, final int index) { + return new TypeSafeMatcher() { + int currentIndex = 0; + + @Override + public void describeTo(Description description) { + description.appendText("with index: "); + description.appendValue(index); + matcher.describeTo(description); + } + + @Override + public boolean matchesSafely(View view) { + return matcher.matches(view) && currentIndex++ == index; + } + }; + } + + public static Matcher hasTypeFace(final Typeface typeface) { + return new TypeSafeMatcher() { + + @Override + protected boolean matchesSafely(final View view) { + return view instanceof TextView && ((TextView) view).getTypeface() == typeface; + } + + @Override + public void describeTo(Description description) { + description.appendText("the selected TextView doesn't have the TypeFace:" + typeface); + } + }; + } + + public static Matcher hasChildPosition(final int position) { + return new TypeSafeMatcher() { + @Override + public void describeTo(Description description) { + description.appendText("is child #" + position); + } + + @Override + public boolean matchesSafely(View view) { + ViewParent viewParent = view.getParent(); + + if (!(viewParent instanceof ViewGroup)) { + return false; + } + + ViewGroup viewGroup = (ViewGroup) viewParent; + return viewGroup.indexOfChild(view) == position; + } + }; + } + + public static Matcher hasTablePosition(final int rowIndex, final int columnIndex) { + return new TypeSafeMatcher() { + @Override + public void describeTo(Description description) { + description.appendText("is child in cell @(" + rowIndex + "|" + columnIndex + ")"); + } + + @Override + public boolean matchesSafely(View view) { + ViewParent tableRow = view.getParent(); + if (!(tableRow instanceof ViewGroup)) { + return false; + } + if (((ViewGroup) tableRow).indexOfChild(view) != columnIndex) { + return false; + } + + ViewParent tableLayout = tableRow.getParent(); + if (!(tableLayout instanceof ViewGroup)) { + return false; + } + + return ((ViewGroup) tableLayout).indexOfChild((TableRow) tableRow) == rowIndex; + } + }; + } + + public static Matcher withBackgroundColor(final Matcher colorMatcher) { + + return new TypeSafeMatcher() { + @Override + protected boolean matchesSafely(View view) { + ColorDrawable colorDrawable = (ColorDrawable) view.getBackground(); + + if (colorDrawable == null) { + return false; + } + + int bgColor = colorDrawable.getColor(); + + return colorMatcher.matches(bgColor); + } + + @Override + public void describeTo(Description description) { + description.appendText("with background color: "); + colorMatcher.describeTo(description); + } + }; + } + + public static Matcher withBackgroundColor(final int color) { + + return new TypeSafeMatcher() { + @Override + protected boolean matchesSafely(View view) { + Drawable background = view.getBackground(); + + if (background == null) { + return false; + } + + if (background instanceof ColorDrawable) { + return color == ((ColorDrawable) background).getColor(); + } else if (background instanceof LayerDrawable) { + LayerDrawable layerDrawable = (LayerDrawable) background; + Drawable drawable = layerDrawable.getDrawable(0); + return drawable instanceof ColorDrawable + && color == ((ColorDrawable) drawable).getColor(); + } + return false; + } + + @Override + public void describeTo(Description description) { + description.appendText("with background color: " + color); + } + }; + } + + public static Matcher withTextColor(final Matcher colorMatcher) { + + return new TypeSafeMatcher() { + @Override + protected boolean matchesSafely(View view) { + if (!(view instanceof TextView)) { + return false; + } + + TextView textView = (TextView) view; + + int textColor = textView.getCurrentTextColor(); + + return colorMatcher.matches(textColor); + } + + @Override + public void describeTo(Description description) { + description.appendText("with text color: "); + colorMatcher.describeTo(description); + } + }; + } + + public static Matcher withTextColor(final int color) { + + return new TypeSafeMatcher() { + @Override + protected boolean matchesSafely(View view) { + if (!(view instanceof TextView)) { + return false; + } + + TextView textView = (TextView) view; + + int textColor = textView.getCurrentTextColor(); + + return textColor == color; + } + + @Override + public void describeTo(Description description) { + description.appendText("with text color: " + color); + } + }; + } + + public static Matcher withProgress(final int progress) { + + return new TypeSafeMatcher() { + @Override + protected boolean matchesSafely(View view) { + if (!(view instanceof SeekBar)) { + return false; + } + + SeekBar seekbarView = (SeekBar) view; + + int seekbarProgress = seekbarView.getProgress(); + + return seekbarProgress == progress; + } + + @Override + public void describeTo(Description description) { + description.appendText("with progress: " + progress); + } + }; + } + + public static Matcher withBackground(final int resourceId) { + + return new TypeSafeMatcher() { + String resourceName; + + @Override + protected boolean matchesSafely(View target) { + Resources resources = target.getContext().getResources(); + resourceName = resources.getResourceEntryName(resourceId); + + if (!(target instanceof ImageView)) { + return false; + } + + Drawable expectedDrawable = resources.getDrawable(resourceId); + Drawable targetDrawable = target.getBackground(); + + if (expectedDrawable == null || targetDrawable == null) { + return false; + } + + Bitmap expectedBitmap = ((BitmapDrawable) expectedDrawable).getBitmap(); + + if (targetDrawable instanceof BitmapDrawable) { + Bitmap bitmap = ((BitmapDrawable) targetDrawable).getBitmap(); + return bitmap.sameAs(expectedBitmap); + } else if (targetDrawable instanceof StateListDrawable) { + Bitmap bitmap = ((BitmapDrawable) targetDrawable.getCurrent()).getBitmap(); + return bitmap.sameAs(expectedBitmap); + } + return false; + } + + @Override + public void describeTo(Description description) { + description.appendText("with drawable from resource id: "); + description.appendValue(resourceId); + if (resourceName != null) { + description.appendText("["); + description.appendText(resourceName); + description.appendText("]"); + } + } + }; + } + + public static Matcher withChildren(final Matcher numberOfChildrenMatcher) { + + return new TypeSafeMatcher() { + + @Override + protected boolean matchesSafely(View target) { + if (!(target instanceof ViewGroup)) { + return false; + } + + return numberOfChildrenMatcher.matches(((ViewGroup) target).getChildCount()); + } + + @Override + public void describeTo(Description description) { + description.appendText("with children # is "); + numberOfChildrenMatcher.describeTo(description); + } + }; + } + + public static Matcher equalsNumberDots(final int value) { + return new BoundedMatcher(LinearLayout.class) { + private String layoutCount = null; + + @Override + public void describeTo(Description description) { + description.appendText("Number of dots does not match.\n"); + + description.appendText("Expected: " + value); + + if (layoutCount != null) { + description.appendText("\nIs: " + layoutCount); + } + } + + @Override + public boolean matchesSafely(LinearLayout layout) { + layoutCount = String.valueOf(layout.getChildCount()); + return layout.getChildCount() == value; + } + }; + } + + public static Matcher checkDotsColors(final int activeIndex, final int colorActive, + final int colorInactive) { + + return new BoundedMatcher(LinearLayout.class) { + private String errorTextView = null; + private int currentIndex = -1; + private int currentColor; + private int expectedColor; + + @Override + public boolean matchesSafely(LinearLayout layout) { + for (currentIndex = 0; currentIndex < layout.getChildCount(); currentIndex++) { + TextView textView = (TextView) layout.getChildAt(currentIndex); + + if (textView == null) { + errorTextView = "DotView is not TextView"; + return false; + } + + currentColor = textView.getCurrentTextColor(); + + if (currentIndex == activeIndex) { + if (currentColor != colorActive) { + expectedColor = colorActive; + return false; + } + } else { + if (currentColor != colorInactive) { + expectedColor = colorInactive; + return false; + } + } + } + + return true; + } + + @Override + public void describeTo(Description description) { + description.appendText("\nAt Index: " + currentIndex); + if (errorTextView != null) { + description.appendText("\nIs not a textview"); + return; + } + + description.appendText("Dot Color does not match "); + description.appendText("\nExcepted: " + expectedColor); + description.appendText("\nIs: " + currentColor); + } + }; + } + + public static Matcher withDrawable(final int resourceId) { + + return new TypeSafeMatcher() { + String resourceName; + + @Override + protected boolean matchesSafely(View target) { + Resources resources = target.getContext().getResources(); + resourceName = resources.getResourceEntryName(resourceId); + + if (!(target instanceof ImageView)) { + return false; + } + + Drawable expectedDrawable = resources.getDrawable(resourceId); + ImageView targetImageView = (ImageView) target; + Drawable targetDrawable = targetImageView.getDrawable(); + + if (expectedDrawable == null || targetDrawable == null) { + return false; + } + + Bitmap expectedBitmap; + + if (targetDrawable instanceof BitmapDrawable) { + Bitmap targetBitmap = ((BitmapDrawable) targetDrawable).getBitmap(); + expectedBitmap = ((BitmapDrawable) expectedDrawable).getBitmap(); + return targetBitmap.sameAs(expectedBitmap); + } else if (targetDrawable instanceof VectorDrawable || targetDrawable instanceof VectorDrawableCompat) { + Bitmap targetBitmap = vectorToBitmap((VectorDrawable) expectedDrawable); + expectedBitmap = vectorToBitmap((VectorDrawable) expectedDrawable); + return targetBitmap.sameAs(expectedBitmap); + } else if (targetDrawable instanceof StateListDrawable) { + Bitmap targetBitmap = vectorToBitmap((VectorDrawable) expectedDrawable); + expectedBitmap = vectorToBitmap((VectorDrawable) expectedDrawable); + return targetBitmap.sameAs(expectedBitmap); + } + return false; + } + + @Override + public void describeTo(Description description) { + description.appendText("with drawable from resource id: "); + description.appendValue(resourceId); + if (resourceName != null) { + description.appendText("["); + description.appendText(resourceName); + description.appendText("]"); + } + } + + private Bitmap vectorToBitmap(VectorDrawable vectorDrawable) { + return Bitmap.createBitmap(vectorDrawable.getIntrinsicWidth(), + vectorDrawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888); + } + }; + } + + /** + * Matches {@link Root}s that are toasts (i.e. is not a window of the currently resumed activity). + * + * @see androidx.test.espresso.matcher.RootMatchers#isDialog() + */ + public static Matcher isToast() { + return new TypeSafeMatcher() { + @Override + public void describeTo(Description description) { + description.appendText("is toast"); + } + + @Override + public boolean matchesSafely(Root root) { + int type = root.getWindowLayoutParams().get().type; + return type == WindowManager.LayoutParams.TYPE_TOAST; + } + }; + } + + public static ViewAssertion isNotVisible() { + return new ViewAssertion() { + @Override + public void check(View view, NoMatchingViewException noView) { + if (view != null) { + boolean isRect = view.getGlobalVisibleRect(new Rect()); + boolean isVisible = withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE).matches(view); + boolean retVal = !(isRect && isVisible); + + assertThat("View is present in the hierarchy: " + HumanReadables.describe(view), + retVal, is(true)); + } + } + }; + } + + public static Matcher isOnLeftSide() { + return new TypeSafeMatcher() { + + @Override + public void describeTo(Description description) { + description.appendText("View is not on the Left Side"); + } + + @Override + public boolean matchesSafely(View view) { + int displayMiddle = Resources.getSystem().getDisplayMetrics().widthPixels / 2; + int viewStartX = (int) view.getX(); + int viewEndX = viewStartX + view.getWidth(); + + return viewStartX < displayMiddle && viewEndX < displayMiddle; + } + }; + } + + public static Matcher isOnRightSide() { + return new TypeSafeMatcher() { + + @Override + public void describeTo(Description description) { + description.appendText("View is not on the Right Side"); + } + + @Override + public boolean matchesSafely(View view) { + int displayMiddle = Resources.getSystem().getDisplayMetrics().widthPixels / 2; + int viewStartX = (int) view.getX(); + int viewEndX = viewStartX + view.getWidth(); + + return viewStartX > displayMiddle && viewEndX > displayMiddle; + } + }; + } + + public static Matcher withAdaptedData(final int resourceId) { + return new TypeSafeMatcher() { + + @Override + public void describeTo(Description description) { + description.appendText("with class name: "); + } + + @Override + public boolean matchesSafely(View view) { + String resourceName; + + if (!(view instanceof AdapterView)) { + return false; + } + + Resources resources = view.getContext().getResources(); + resourceName = resources.getString(resourceId); + + @SuppressWarnings("rawtypes") + Adapter adapter = ((AdapterView) view).getAdapter(); + for (int i = 0; i < adapter.getCount(); i++) { + if (resourceName.equals(((MenuItem) adapter.getItem(i)).getTitle().toString())) { + return true; + } + } + + return false; + } + }; + } +} diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/wrappers/BottomNavigationViewInteraction.java b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/wrappers/BottomNavigationViewInteraction.java new file mode 100644 index 0000000000..6ea15ecb20 --- /dev/null +++ b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/wrappers/BottomNavigationViewInteraction.java @@ -0,0 +1,74 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2015 The Catrobat Team + * () + * + * 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. + * + * 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 . + */ + +package org.catrobat.paintroid.test.espresso.util.wrappers; + +import org.catrobat.paintroid.R; +import org.catrobat.paintroid.tools.ToolType; + +import androidx.test.espresso.ViewInteraction; + +import static org.catrobat.paintroid.test.espresso.util.UiMatcher.withDrawable; +import static org.hamcrest.Matchers.allOf; + +import static androidx.test.espresso.Espresso.onView; +import static androidx.test.espresso.action.ViewActions.click; +import static androidx.test.espresso.assertion.ViewAssertions.matches; +import static androidx.test.espresso.matcher.ViewMatchers.hasDescendant; +import static androidx.test.espresso.matcher.ViewMatchers.isDescendantOfA; +import static androidx.test.espresso.matcher.ViewMatchers.withId; +import static androidx.test.espresso.matcher.ViewMatchers.withText; + +public final class BottomNavigationViewInteraction extends CustomViewInteraction { + private BottomNavigationViewInteraction() { + super(onView(withId(R.id.pocketpaint_bottom_navigation))); + } + + public static BottomNavigationViewInteraction onBottomNavigationView() { + return new BottomNavigationViewInteraction(); + } + + public ViewInteraction onToolsClicked() { + return onView(allOf(withId(R.id.icon), isDescendantOfA(withId(R.id.action_tools)))) + .perform(click()); + } + + public ViewInteraction onCurrentClicked() { + return onView(allOf(withId(R.id.icon), isDescendantOfA(withId(R.id.action_current_tool)))) + .perform(click()); + } + + public ViewInteraction checkShowsCurrentTool(ToolType toolType) { + onView(allOf(withId(R.id.icon), isDescendantOfA(withId(R.id.action_current_tool)))) + .check(matches(withDrawable(toolType.getDrawableResource()))); + + return onView(withId(R.id.action_current_tool)) + .check(matches(hasDescendant(withText(toolType.getNameResource())))); + } + + public ViewInteraction onColorClicked() { + return onView(allOf(withId(R.id.icon), isDescendantOfA(withId(R.id.action_color_picker)))) + .perform(click()); + } + + public ViewInteraction onLayersClicked() { + return onView(allOf(withId(R.id.icon), isDescendantOfA(withId(R.id.action_layers)))) + .perform(click()); + } +} diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/wrappers/BrushPickerViewInteraction.java b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/wrappers/BrushPickerViewInteraction.java new file mode 100644 index 0000000000..00f1b6c78f --- /dev/null +++ b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/wrappers/BrushPickerViewInteraction.java @@ -0,0 +1,53 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2015 The Catrobat Team + * () + * + * 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. + * + * 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 . + */ + +package org.catrobat.paintroid.test.espresso.util.wrappers; + +import org.catrobat.paintroid.R; + +import androidx.test.espresso.ViewInteraction; + +import static androidx.test.espresso.Espresso.onView; +import static androidx.test.espresso.matcher.ViewMatchers.withId; + +public final class BrushPickerViewInteraction extends CustomViewInteraction { + private BrushPickerViewInteraction() { + super(onView(withId(R.id.pocketpaint_layout_tool_options))); + } + + public static BrushPickerViewInteraction onBrushPickerView() { + return new BrushPickerViewInteraction(); + } + + public ViewInteraction onStrokeWidthSeekBar() { + return onView(withId(R.id.pocketpaint_stroke_width_seek_bar)); + } + + public ViewInteraction onStrokeWidthTextView() { + return onView(withId(R.id.pocketpaint_stroke_width_width_text)); + } + + public ViewInteraction onStrokeCapSquareView() { + return onView(withId(R.id.pocketpaint_stroke_ibtn_rect)); + } + + public ViewInteraction onStrokeCapRoundView() { + return onView(withId(R.id.pocketpaint_stroke_ibtn_circle)); + } +} diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/wrappers/ColorPickerViewInteraction.java b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/wrappers/ColorPickerViewInteraction.java new file mode 100644 index 0000000000..72fb08d2a4 --- /dev/null +++ b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/wrappers/ColorPickerViewInteraction.java @@ -0,0 +1,110 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2015 The Catrobat Team + * () + * + * 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. + * + * 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 . + */ + +package org.catrobat.paintroid.test.espresso.util.wrappers; + +import android.widget.TableLayout; +import android.widget.TableRow; + +import org.catrobat.paintroid.R; +import org.catrobat.paintroid.colorpicker.PresetSelectorView; + +import static org.catrobat.paintroid.test.espresso.util.UiMatcher.hasTablePosition; +import static org.catrobat.paintroid.test.espresso.util.UiMatcher.withBackgroundColor; +import static org.catrobat.paintroid.test.espresso.util.wrappers.BottomNavigationViewInteraction.onBottomNavigationView; +import static org.hamcrest.Matchers.allOf; +import static org.hamcrest.Matchers.containsString; + +import androidx.test.espresso.ViewInteraction; + +import static androidx.test.espresso.Espresso.onView; +import static androidx.test.espresso.action.ViewActions.click; +import static androidx.test.espresso.action.ViewActions.closeSoftKeyboard; +import static androidx.test.espresso.action.ViewActions.scrollTo; +import static androidx.test.espresso.assertion.ViewAssertions.matches; +import static androidx.test.espresso.matcher.ViewMatchers.isAssignableFrom; +import static androidx.test.espresso.matcher.ViewMatchers.isDescendantOfA; +import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed; +import static androidx.test.espresso.matcher.ViewMatchers.withClassName; +import static androidx.test.espresso.matcher.ViewMatchers.withId; + +public final class ColorPickerViewInteraction extends CustomViewInteraction { + private static final int COLOR_PICKER_BUTTONS_PER_ROW = 4; + + protected ColorPickerViewInteraction() { + super(onView(withId(R.id.color_picker_view))); + } + + public static ColorPickerViewInteraction onColorPickerView() { + return new ColorPickerViewInteraction(); + } + + public ViewInteraction onPositiveButton() { + return onView(withId(android.R.id.button1)) + // to avoid following exception when running on emulator: + // Caused by: java.lang.SecurityException: + // Injecting to another application requires INJECT_EVENTS permission + .perform(closeSoftKeyboard()); + } + + public ColorPickerViewInteraction performOpenColorPicker() { + onBottomNavigationView() + .onColorClicked(); + return this; + } + + public ViewInteraction onNegativeButton() { + return onView(withId(android.R.id.button2)) + // to avoid following exception when running on emulator: + // Caused by: java.lang.SecurityException: + // Injecting to another application requires INJECT_EVENTS permission + .perform(closeSoftKeyboard()); + } + + public void checkCurrentViewColor(int color) { + onView(withId(R.id.color_picker_current_color_view)) + .check(matches(withBackgroundColor(color))); + } + + public void checkNewColorViewColor(int color) { + onView(withId(R.id.color_picker_new_color_view)) + .check(matches(withBackgroundColor(color))); + } + + public ColorPickerViewInteraction performCloseColorPickerWithDialogButton() { + check(matches(isDisplayed())); + onPositiveButton() + .perform(click()); + return this; + } + + public ColorPickerViewInteraction performClickColorPickerPresetSelectorButton(int buttonPosition) { + final int colorButtonRowPosition = buttonPosition / COLOR_PICKER_BUTTONS_PER_ROW; + final int colorButtonColPosition = buttonPosition % COLOR_PICKER_BUTTONS_PER_ROW; + + onView(allOf(isDescendantOfA(withClassName(containsString(PresetSelectorView.class.getSimpleName()))), + isDescendantOfA(isAssignableFrom(TableLayout.class)), + isDescendantOfA(isAssignableFrom(TableRow.class)), + hasTablePosition(colorButtonRowPosition, colorButtonColPosition))) + .perform(closeSoftKeyboard()) + .perform(scrollTo()) + .perform(click()); + return this; + } +} diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/wrappers/ConfirmQuitDialogInteraction.java b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/wrappers/ConfirmQuitDialogInteraction.java new file mode 100644 index 0000000000..68f6d90c8c --- /dev/null +++ b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/wrappers/ConfirmQuitDialogInteraction.java @@ -0,0 +1,83 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2015 The Catrobat Team + * () + * + * 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. + * + * 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 . + */ + +package org.catrobat.paintroid.test.espresso.util.wrappers; + +import android.widget.Button; + +import org.catrobat.paintroid.R; + +import androidx.test.espresso.ViewAssertion; +import androidx.test.espresso.ViewInteraction; + +import static org.hamcrest.Matchers.allOf; + +import static androidx.test.espresso.Espresso.onView; +import static androidx.test.espresso.matcher.RootMatchers.isDialog; +import static androidx.test.espresso.matcher.ViewMatchers.isAssignableFrom; +import static androidx.test.espresso.matcher.ViewMatchers.withId; +import static androidx.test.espresso.matcher.ViewMatchers.withText; + +public final class ConfirmQuitDialogInteraction extends CustomViewInteraction { + private ConfirmQuitDialogInteraction() { + super(onView(withText(R.string.closing_security_question)).inRoot(isDialog())); + } + + public static ConfirmQuitDialogInteraction onConfirmQuitDialog() { + return new ConfirmQuitDialogInteraction(); + } + + public ViewInteraction onPositiveButton() { + return onView(allOf(withId(android.R.id.button1), withText(R.string.save_button_text), isAssignableFrom(Button.class))); + } + + public ConfirmQuitDialogInteraction checkPositiveButton(ViewAssertion matcher) { + onPositiveButton() + .check(matcher); + return this; + } + + public ViewInteraction onNegativeButton() { + return onView(allOf(withId(android.R.id.button2), withText(R.string.discard_button_text), isAssignableFrom(Button.class))); + } + + public ConfirmQuitDialogInteraction checkNegativeButton(ViewAssertion matcher) { + onNegativeButton() + .check(matcher); + return this; + } + + public ConfirmQuitDialogInteraction checkNeutralButton(ViewAssertion matcher) { + onView(withId(android.R.id.button3)) + .check(matcher); + return this; + } + + public ConfirmQuitDialogInteraction checkMessage(ViewAssertion matcher) { + onView(withText(R.string.closing_security_question)) + .check(matcher); + return this; + } + + public ConfirmQuitDialogInteraction checkTitle(ViewAssertion matcher) { + onView(withText(R.string.closing_security_question_title)) + .check(matcher); + return this; + } +} diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/wrappers/CustomViewInteraction.java b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/wrappers/CustomViewInteraction.java new file mode 100644 index 0000000000..044b713959 --- /dev/null +++ b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/wrappers/CustomViewInteraction.java @@ -0,0 +1,56 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2015 The Catrobat Team + * () + * + * 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. + * + * 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 . + */ + +package org.catrobat.paintroid.test.espresso.util.wrappers; + +import org.hamcrest.Matcher; + +import androidx.test.espresso.FailureHandler; +import androidx.test.espresso.Root; +import androidx.test.espresso.ViewAction; +import androidx.test.espresso.ViewAssertion; +import androidx.test.espresso.ViewInteraction; + +public abstract class CustomViewInteraction { + protected ViewInteraction viewInteraction; + + protected CustomViewInteraction(ViewInteraction viewInteraction) { + this.viewInteraction = viewInteraction; + } + + public final ViewInteraction perform(final ViewAction... viewActions) { + return viewInteraction.perform(viewActions); + } + + public final ViewInteraction withFailureHandler(FailureHandler var1) { + return viewInteraction.withFailureHandler(var1); + } + + public final ViewInteraction inRoot(Matcher var1) { + return viewInteraction.inRoot(var1); + } + + public final ViewInteraction noActivity() { + return viewInteraction.noActivity(); + } + + public final ViewInteraction check(final ViewAssertion viewAssert) { + return viewInteraction.check(viewAssert); + } +} diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/wrappers/DrawingSurfaceInteraction.java b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/wrappers/DrawingSurfaceInteraction.java new file mode 100644 index 0000000000..4f12342243 --- /dev/null +++ b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/wrappers/DrawingSurfaceInteraction.java @@ -0,0 +1,145 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2015 The Catrobat Team + * () + * + * 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. + * + * 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 . + */ + +package org.catrobat.paintroid.test.espresso.util.wrappers; + +import android.graphics.Bitmap; +import android.view.View; + +import org.catrobat.paintroid.MainActivity; +import org.catrobat.paintroid.R; +import org.catrobat.paintroid.contract.LayerContracts; +import org.hamcrest.Description; +import org.hamcrest.Matcher; +import org.hamcrest.TypeSafeMatcher; + +import androidx.annotation.ColorInt; +import androidx.annotation.ColorRes; +import androidx.core.content.ContextCompat; +import androidx.test.espresso.action.CoordinatesProvider; +import androidx.test.platform.app.InstrumentationRegistry; + +import static org.catrobat.paintroid.test.espresso.util.MainActivityHelper.getMainActivityFromView; +import static org.hamcrest.Matchers.is; + +import static androidx.test.espresso.Espresso.onView; +import static androidx.test.espresso.assertion.ViewAssertions.matches; +import static androidx.test.espresso.matcher.ViewMatchers.withId; + +public final class DrawingSurfaceInteraction extends CustomViewInteraction { + + private DrawingSurfaceInteraction() { + super(onView(withId(R.id.pocketpaint_drawing_surface_view))); + } + + public static DrawingSurfaceInteraction onDrawingSurfaceView() { + return new DrawingSurfaceInteraction(); + } + + public DrawingSurfaceInteraction checkPixelColor(@ColorInt final int expectedColor, final CoordinatesProvider coordinateProvider) { + check(matches(new TypeSafeMatcher() { + @Override + public void describeTo(Description description) { + description.appendText("Color at coordinates is " + Integer.toHexString(expectedColor)); + } + + @Override + protected boolean matchesSafely(View view) { + MainActivity activity = getMainActivityFromView(view); + LayerContracts.Layer currentLayer = activity.layerModel.getCurrentLayer(); + float[] coordinates = coordinateProvider.calculateCoordinates(view); + int actualColor = currentLayer.getBitmap().getPixel((int) coordinates[0], (int) coordinates[1]); + return expectedColor == actualColor; + } + })); + return this; + } + + public DrawingSurfaceInteraction checkPixelColor(@ColorInt final int expectedColor, final float x, final float y) { + check(matches(new TypeSafeMatcher() { + @Override + public void describeTo(Description description) { + description.appendText("Color at coordinates is " + Integer.toHexString(expectedColor)); + } + + @Override + protected boolean matchesSafely(View view) { + MainActivity activity = getMainActivityFromView(view); + LayerContracts.Layer currentLayer = activity.layerModel.getCurrentLayer(); + int actualColor = currentLayer.getBitmap().getPixel((int) x, (int) y); + return expectedColor == actualColor; + } + })); + return this; + } + + public DrawingSurfaceInteraction checkPixelColorResource(@ColorRes int expectedColorRes, CoordinatesProvider coordinateProvider) { + int expectedColor = ContextCompat.getColor(InstrumentationRegistry.getInstrumentation().getTargetContext(), expectedColorRes); + return checkPixelColor(expectedColor, coordinateProvider); + } + + public DrawingSurfaceInteraction checkBitmapDimension(final int expectedWidth, final int expectedHeight) { + check(matches(new TypeSafeMatcher() { + @Override + public void describeTo(Description description) { + description.appendText("Bitmap has is size " + + expectedWidth + "x and " + + expectedHeight + "y"); + } + + @Override + protected boolean matchesSafely(View view) { + MainActivity activity = getMainActivityFromView(view); + LayerContracts.Model layerModel = activity.layerModel; + Bitmap bitmap = layerModel.getCurrentLayer().getBitmap(); + return expectedWidth == bitmap.getWidth() && expectedHeight == bitmap.getHeight(); + } + })); + return this; + } + + public DrawingSurfaceInteraction checkLayerDimensions(int expectedWidth, int expectedHeight) { + checkThatLayerDimensions(is(expectedWidth), is(expectedHeight)); + return this; + } + + public DrawingSurfaceInteraction checkThatLayerDimensions(final Matcher matchesWidth, final Matcher matchesHeight) { + check(matches(new TypeSafeMatcher() { + @Override + public void describeTo(Description description) { + description.appendText("All layers have expected size"); + } + + @Override + protected boolean matchesSafely(View view) { + MainActivity activity = getMainActivityFromView(view); + LayerContracts.Model layerModel = activity.layerModel; + for (LayerContracts.Layer layer : layerModel.getLayers()) { + Bitmap bitmap = layer.getBitmap(); + if (!matchesWidth.matches(bitmap.getWidth()) || !matchesHeight.matches(bitmap.getHeight())) { + return false; + } + } + return true; + } + })); + + return this; + } +} diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/wrappers/LayerMenuViewInteraction.java b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/wrappers/LayerMenuViewInteraction.java new file mode 100644 index 0000000000..8799cc94b8 --- /dev/null +++ b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/wrappers/LayerMenuViewInteraction.java @@ -0,0 +1,180 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2015 The Catrobat Team + * () + * + * 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. + * + * 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 . + */ + +package org.catrobat.paintroid.test.espresso.util.wrappers; + +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.drawable.Drawable; +import android.view.Gravity; +import android.view.View; +import android.widget.ImageView; + +import org.catrobat.paintroid.R; +import org.catrobat.paintroid.model.Layer; +import org.hamcrest.Description; +import org.hamcrest.Matcher; +import org.hamcrest.TypeSafeMatcher; + +import androidx.annotation.ColorInt; +import androidx.test.espresso.DataInteraction; +import androidx.test.espresso.ViewInteraction; +import androidx.test.espresso.contrib.DrawerActions; + +import static org.catrobat.paintroid.test.espresso.util.UiInteractions.assertListViewCount; +import static org.catrobat.paintroid.test.espresso.util.wrappers.BottomNavigationViewInteraction.onBottomNavigationView; +import static org.hamcrest.Matchers.instanceOf; + +import static androidx.test.espresso.Espresso.onData; +import static androidx.test.espresso.Espresso.onView; +import static androidx.test.espresso.action.ViewActions.click; +import static androidx.test.espresso.assertion.ViewAssertions.matches; +import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed; +import static androidx.test.espresso.matcher.ViewMatchers.withId; + +public final class LayerMenuViewInteraction extends CustomViewInteraction { + private LayerMenuViewInteraction() { + super(onView(withId(R.id.pocketpaint_nav_view_layer))); + } + + public static LayerMenuViewInteraction onLayerMenuView() { + return new LayerMenuViewInteraction(); + } + + public ViewInteraction onButtonAdd() { + return onView(withId(R.id.pocketpaint_layer_side_nav_button_add)); + } + + public ViewInteraction onButtonDelete() { + return onView(withId(R.id.pocketpaint_layer_side_nav_button_delete)); + } + + public ViewInteraction onLayerList() { + return onView(withId(R.id.pocketpaint_layer_side_nav_list)); + } + + public LayerMenuViewInteraction checkLayerCount(int count) { + onLayerList() + .check(assertListViewCount(count)); + return this; + } + + public DataInteraction onLayerAt(int listPosition) { + return onData(instanceOf(Layer.class)) + .inAdapterView(withId(R.id.pocketpaint_layer_side_nav_list)) + .atPosition(listPosition); + } + + public LayerMenuViewInteraction performOpen() { + onBottomNavigationView() + .onLayersClicked(); + check(matches(isDisplayed())); + return this; + } + + public LayerMenuViewInteraction performClose() { + check(matches(isDisplayed())); + onView(withId(R.id.pocketpaint_drawer_layout)) + .perform(DrawerActions.close(Gravity.END)); + return this; + } + + public LayerMenuViewInteraction performSelectLayer(int listPosition) { + check(matches(isDisplayed())); + onLayerAt(listPosition) + .perform(click()); + return this; + } + + public LayerMenuViewInteraction performStartDragging(int listPosition) { + check(matches(isDisplayed())); + onLayerAt(listPosition) + .onChildView(withId(R.id.pocketpaint_layer_drag_handle)) + .perform(click()); + return this; + } + + public LayerMenuViewInteraction performAddLayer() { + check(matches(isDisplayed())); + onButtonAdd() + .perform(click()); + return this; + } + + public LayerMenuViewInteraction performDeleteLayer() { + check(matches(isDisplayed())); + onButtonDelete() + .perform(click()); + return this; + } + + public LayerMenuViewInteraction performToggleLayerVisibility(int position) { + check(matches(isDisplayed())); + onView(withIndex(withId(R.id.pocketpaint_checkbox_layer), position)).perform(click()); + return this; + } + + public static Matcher withIndex(final Matcher matcher, final int index) { + return new TypeSafeMatcher() { + int currentIndex = 0; + + @Override + public void describeTo(Description description) { + description.appendText("with index: "); + description.appendValue(index); + matcher.describeTo(description); + } + + @Override + public boolean matchesSafely(View view) { + return matcher.matches(view) && currentIndex++ == index; + } + }; + } + + public LayerMenuViewInteraction checkLayerAtPositionHasTopLeftPixelWithColor(int listPosition, @ColorInt final int expectedColor) { + onData(instanceOf(Layer.class)) + .inAdapterView(withId(R.id.pocketpaint_layer_side_nav_list)) + .atPosition(listPosition) + .onChildView(withId(R.id.pocketpaint_item_layer_image)) + .check(matches(new TypeSafeMatcher() { + @Override + public void describeTo(Description description) { + description.appendText("Color at coordinates is " + Integer.toHexString(expectedColor)); + } + + @Override + protected boolean matchesSafely(View view) { + Bitmap bitmap = getBitmap(((ImageView) view).getDrawable()); + int actualColor = bitmap.getPixel(0, 0); + return actualColor == expectedColor; + } + })); + + return this; + } + + private Bitmap getBitmap(Drawable drawable) { + Bitmap bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888); + Canvas canvas = new Canvas(bitmap); + drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight()); + drawable.draw(canvas); + return bitmap; + } +} diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/wrappers/OptionsMenuViewInteraction.java b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/wrappers/OptionsMenuViewInteraction.java new file mode 100644 index 0000000000..21ebb418da --- /dev/null +++ b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/wrappers/OptionsMenuViewInteraction.java @@ -0,0 +1,38 @@ +package org.catrobat.paintroid.test.espresso.util.wrappers; + +import android.view.View; + +import org.hamcrest.CoreMatchers; + +import androidx.annotation.StringRes; +import androidx.appcompat.widget.MenuPopupWindow; +import androidx.test.espresso.ViewInteraction; + +import static org.catrobat.paintroid.test.espresso.util.UiMatcher.withAdaptedData; +import static org.hamcrest.Matchers.not; + +import static androidx.test.espresso.Espresso.onView; +import static androidx.test.espresso.assertion.ViewAssertions.matches; + +public final class OptionsMenuViewInteraction { + static ViewInteraction optionsMenu; + + private OptionsMenuViewInteraction() { + optionsMenu = onView(CoreMatchers.instanceOf(MenuPopupWindow.MenuDropDownListView.class)); + } + + public static OptionsMenuViewInteraction onOptionsMenu() { + return new OptionsMenuViewInteraction(); + } + + public OptionsMenuViewInteraction checkItemExists(@StringRes int item) { + optionsMenu.check(matches(withAdaptedData(item))); + + return this; + } + + public OptionsMenuViewInteraction checkItemDoesNotExist(@StringRes int item) { + optionsMenu.check(matches(not(withAdaptedData(item)))); + return this; + } +} diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/wrappers/ShapeToolOptionsViewInteraction.java b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/wrappers/ShapeToolOptionsViewInteraction.java new file mode 100644 index 0000000000..d4e13a4f2a --- /dev/null +++ b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/wrappers/ShapeToolOptionsViewInteraction.java @@ -0,0 +1,81 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2015 The Catrobat Team + * () + * + * 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. + * + * 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 . + */ + +package org.catrobat.paintroid.test.espresso.util.wrappers; + +import org.catrobat.paintroid.R; +import org.catrobat.paintroid.tools.drawable.DrawableShape; +import org.catrobat.paintroid.tools.drawable.DrawableStyle; + +import androidx.test.espresso.ViewAction; + +import static androidx.test.espresso.Espresso.onView; +import static androidx.test.espresso.action.ViewActions.click; +import static androidx.test.espresso.matcher.ViewMatchers.withId; + +public final class ShapeToolOptionsViewInteraction extends CustomViewInteraction { + private ShapeToolOptionsViewInteraction() { + super(onView(withId(R.id.pocketpaint_layout_tool_options))); + } + + public static ShapeToolOptionsViewInteraction onShapeToolOptionsView() { + return new ShapeToolOptionsViewInteraction(); + } + + private int getButtonIdFromBaseShape(DrawableShape baseShape) { + switch (baseShape) { + case RECTANGLE: + return R.id.pocketpaint_shapes_square_btn; + case OVAL: + return R.id.pocketpaint_shapes_circle_btn; + case HEART: + return R.id.pocketpaint_shapes_heart_btn; + case STAR: + return R.id.pocketpaint_shapes_star_btn; + } + throw new IllegalArgumentException(); + } + + private int getButtonIdFromShapeDrawType(DrawableStyle shapeDrawType) { + switch (shapeDrawType) { + case STROKE: + return R.id.pocketpaint_shape_ibtn_outline; + case FILL: + return R.id.pocketpaint_shape_ibtn_fill; + } + throw new IllegalArgumentException(); + } + + public ShapeToolOptionsViewInteraction performSelectShape(DrawableShape shape) { + onView(withId(getButtonIdFromBaseShape(shape))) + .perform(click()); + return this; + } + + public ShapeToolOptionsViewInteraction performSelectShapeDrawType(DrawableStyle shapeDrawType) { + onView(withId(getButtonIdFromShapeDrawType(shapeDrawType))) + .perform(click()); + return this; + } + + public void performSetOutlineWidth(ViewAction setWidth) { + onView(withId(R.id.pocketpaint_shape_stroke_width_seek_bar)) + .perform(setWidth); + } +} diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/wrappers/StampToolViewInteraction.kt b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/wrappers/StampToolViewInteraction.kt new file mode 100644 index 0000000000..53ff101f1e --- /dev/null +++ b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/wrappers/StampToolViewInteraction.kt @@ -0,0 +1,52 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2021 The Catrobat Team + * () + * + * 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. + * + * 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 . + */ +package org.catrobat.paintroid.test.espresso.util.wrappers + +import androidx.test.espresso.Espresso.onView +import androidx.test.espresso.ViewInteraction +import androidx.test.espresso.action.ViewActions.click +import androidx.test.espresso.matcher.ViewMatchers.withId +import org.catrobat.paintroid.R + +class StampToolViewInteraction(viewInteraction: ViewInteraction) : + CustomViewInteraction(viewInteraction) { + + companion object { + fun onStampToolViewInteraction(): StampToolViewInteraction = + StampToolViewInteraction(onView(withId(R.id.pocketpaint_layout_tool_specific_options))) + } + + fun performCopy(): StampToolViewInteraction { + onView(withId(R.id.action_copy)) + .perform(click()) + return this + } + + fun performCut(): StampToolViewInteraction { + onView(withId(R.id.action_cut)) + .perform(click()) + return this + } + + fun performPaste(): StampToolViewInteraction { + onView(withId(R.id.action_paste)) + .perform(click()) + return this + } +} diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/wrappers/ToolBarViewInteraction.java b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/wrappers/ToolBarViewInteraction.java new file mode 100644 index 0000000000..f0f0d67f44 --- /dev/null +++ b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/wrappers/ToolBarViewInteraction.java @@ -0,0 +1,98 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2015 The Catrobat Team + * () + * + * 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. + * + * 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 . + */ + +package org.catrobat.paintroid.test.espresso.util.wrappers; + +import org.catrobat.paintroid.R; +import org.catrobat.paintroid.tools.ToolType; + +import androidx.test.espresso.ViewInteraction; + +import static org.catrobat.paintroid.test.espresso.util.EspressoUtils.getMainActivity; +import static org.catrobat.paintroid.test.espresso.util.wrappers.BottomNavigationViewInteraction.onBottomNavigationView; +import static org.hamcrest.Matchers.not; + +import static androidx.test.espresso.Espresso.onView; +import static androidx.test.espresso.action.ViewActions.click; +import static androidx.test.espresso.assertion.ViewAssertions.matches; +import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed; +import static androidx.test.espresso.matcher.ViewMatchers.withId; + +public final class ToolBarViewInteraction extends CustomViewInteraction { + + private ToolBarViewInteraction() { + super(onView(withId(R.id.pocketpaint_toolbar))); + } + + public static ToolBarViewInteraction onToolBarView() { + return new ToolBarViewInteraction(); + } + + public ViewInteraction onSelectedToolButton() { + return onView(withId(getCurrentToolType().getToolButtonID())); + } + + public ViewInteraction onToolOptionsView() { + return onView(withId(R.id.pocketpaint_layout_tool_specific_options)); + } + + public ToolBarViewInteraction performClickSelectedToolButton() { + onBottomNavigationView() + .onToolsClicked(); + onSelectedToolButton() + .perform(click()); + return this; + } + + public ToolBarViewInteraction onToolsClicked() { + onBottomNavigationView() + .onToolsClicked(); + return this; + } + + public ToolBarViewInteraction performSelectTool(ToolType toolType) { + if (getCurrentToolType() != toolType) { + onBottomNavigationView() + .onToolsClicked(); + onView(withId(toolType.getToolButtonID())) + .perform(click()); + } + return this; + } + + private ToolType getCurrentToolType() { + return getMainActivity().toolReference.getTool().getToolType(); + } + + public ToolBarViewInteraction performOpenToolOptionsView() { + onToolOptionsView() + .check(matches(not(isDisplayed()))); + onBottomNavigationView() + .onCurrentClicked(); + return this; + } + + public ToolBarViewInteraction performCloseToolOptionsView() { + onToolOptionsView() + .check(matches(isDisplayed())); + onBottomNavigationView() + .onCurrentClicked(); + return this; + } +} diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/wrappers/ToolPropertiesInteraction.java b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/wrappers/ToolPropertiesInteraction.java new file mode 100644 index 0000000000..ed9accb005 --- /dev/null +++ b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/wrappers/ToolPropertiesInteraction.java @@ -0,0 +1,102 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2021 The Catrobat Team + * () + * + * 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. + * + * 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 . + */ + +package org.catrobat.paintroid.test.espresso.util.wrappers; + +import android.content.Context; +import android.graphics.Paint; +import android.graphics.Paint.Cap; + +import org.catrobat.paintroid.R; +import org.catrobat.paintroid.tools.Tool; + +import androidx.annotation.ColorInt; +import androidx.annotation.ColorRes; +import androidx.core.content.ContextCompat; +import androidx.test.platform.app.InstrumentationRegistry; + +import static org.catrobat.paintroid.test.espresso.util.EspressoUtils.getMainActivity; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; + +public final class ToolPropertiesInteraction extends CustomViewInteraction { + private ToolPropertiesInteraction() { + super(null); + } + public static ToolPropertiesInteraction onToolProperties() { + return new ToolPropertiesInteraction(); + } + + public ToolPropertiesInteraction checkMatchesColor(@ColorInt int expectedColor) { + assertEquals(expectedColor, getCurrentTool().getDrawPaint().getColor()); + return this; + } + + public ToolPropertiesInteraction checkDoesNotMatchColor(@ColorInt int color) { + assertNotEquals(color, getCurrentTool().getDrawPaint().getColor()); + return this; + } + + public ToolPropertiesInteraction checkMatchesColorResource(@ColorRes int expectedColorRes) { + int expectedColor = ContextCompat.getColor(InstrumentationRegistry.getInstrumentation().getTargetContext(), expectedColorRes); + return checkMatchesColor(expectedColor); + } + + public ToolPropertiesInteraction checkCap(Cap expectedCap) { + Paint strokePaint = getCurrentTool().getDrawPaint(); + assertEquals(expectedCap, strokePaint.getStrokeCap()); + return this; + } + + public ToolPropertiesInteraction setCap(Cap expectedCap) { + getCurrentTool().changePaintStrokeCap(expectedCap); + return this; + } + + public ToolPropertiesInteraction checkStrokeWidth(float expectedStrokeWidth) { + Paint strokePaint = getCurrentTool().getDrawPaint(); + assertEquals(expectedStrokeWidth, strokePaint.getStrokeWidth(), Float.MIN_VALUE); + return this; + } + + public ToolPropertiesInteraction setStrokeWidth(float expectedStrokeWidth) { + getCurrentTool().changePaintStrokeWidth((int) expectedStrokeWidth); + return this; + } + + public ToolPropertiesInteraction setColor(int color) { + getCurrentTool().changePaintColor(color); + return this; + } + + public Tool getCurrentTool() { + return getMainActivity().toolReference.getTool(); + } + + public ToolPropertiesInteraction setColorResource(@ColorRes int colorResource) { + int color = ContextCompat.getColor(InstrumentationRegistry.getInstrumentation().getTargetContext(), colorResource); + return setColor(color); + } + + public ToolPropertiesInteraction setColorPreset(int colorPresetPosition) { + Context targetContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); + int[] presetColors = targetContext.getResources().getIntArray(R.array.pocketpaint_color_picker_preset_colors); + return setColor(presetColors[colorPresetPosition]); + } +} diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/wrappers/TopBarViewInteraction.java b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/wrappers/TopBarViewInteraction.java new file mode 100644 index 0000000000..d48d78ca6f --- /dev/null +++ b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/wrappers/TopBarViewInteraction.java @@ -0,0 +1,91 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2015 The Catrobat Team + * () + * + * 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. + * + * 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 . + */ + +package org.catrobat.paintroid.test.espresso.util.wrappers; + +import org.catrobat.paintroid.R; + +import androidx.test.espresso.Espresso; +import androidx.test.espresso.ViewInteraction; +import androidx.test.platform.app.InstrumentationRegistry; + +import static androidx.test.espresso.Espresso.onView; +import static androidx.test.espresso.Espresso.openActionBarOverflowOrOptionsMenu; +import static androidx.test.espresso.action.ViewActions.click; +import static androidx.test.espresso.matcher.ViewMatchers.withId; + +public final class TopBarViewInteraction extends CustomViewInteraction { + private TopBarViewInteraction() { + super(onView(withId(R.id.pocketpaint_layout_top_bar))); + } + + public static TopBarViewInteraction onTopBarView() { + return new TopBarViewInteraction(); + } + + public ViewInteraction onUndoButton() { + return onView(withId(R.id.pocketpaint_btn_top_undo)); + } + + public ViewInteraction onRedoButton() { + return onView(withId(R.id.pocketpaint_btn_top_redo)); + } + + public ViewInteraction onCheckmarkButton() { + return onView(withId(R.id.pocketpaint_btn_top_checkmark)); + } + + public ViewInteraction onPlusButton() { + return onView(withId(R.id.pocketpaint_btn_top_plus)); + } + + public TopBarViewInteraction performUndo() { + onUndoButton() + .perform(click()); + return this; + } + + public TopBarViewInteraction performRedo() { + onRedoButton() + .perform(click()); + return this; + } + + public TopBarViewInteraction performClickCheckmark() { + onCheckmarkButton() + .perform(click()); + return this; + } + + public TopBarViewInteraction performClickPlus() { + onPlusButton() + .perform(click()); + return this; + } + + public TopBarViewInteraction performOpenMoreOptions() { + openActionBarOverflowOrOptionsMenu(InstrumentationRegistry.getInstrumentation().getTargetContext()); + return this; + } + + public TopBarViewInteraction onHomeClicked() { + Espresso.pressBack(); + return this; + } +} diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/wrappers/TransformToolOptionsViewInteraction.java b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/wrappers/TransformToolOptionsViewInteraction.java new file mode 100644 index 0000000000..3705ce9b09 --- /dev/null +++ b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/wrappers/TransformToolOptionsViewInteraction.java @@ -0,0 +1,163 @@ +/* + * Paintroid: An image manipulation application for Android. + * Copyright (C) 2010-2015 The Catrobat Team + * () + * + * 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. + * + * 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 . + */ + +package org.catrobat.paintroid.test.espresso.util.wrappers; + +import android.view.View; +import android.widget.EditText; +import android.widget.TextView; + +import org.catrobat.paintroid.R; +import org.hamcrest.Description; +import org.hamcrest.Matcher; +import org.hamcrest.TypeSafeMatcher; + +import static org.catrobat.paintroid.test.espresso.util.UiInteractions.setProgress; +import static org.hamcrest.Matchers.not; + +import static androidx.test.espresso.Espresso.onView; +import static androidx.test.espresso.action.ViewActions.click; +import static androidx.test.espresso.assertion.ViewAssertions.matches; +import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed; +import static androidx.test.espresso.matcher.ViewMatchers.withId; +import static androidx.test.espresso.matcher.ViewMatchers.withText; + +public final class TransformToolOptionsViewInteraction extends CustomViewInteraction { + private TransformToolOptionsViewInteraction() { + super(onView(withId(R.id.pocketpaint_layout_tool_options))); + } + + public static TransformToolOptionsViewInteraction onTransformToolOptionsView() { + return new TransformToolOptionsViewInteraction(); + } + + public TransformToolOptionsViewInteraction performSetCenterClick() { + onView(withId(R.id.pocketpaint_transform_set_center_btn)) + .perform(click()); + return this; + } + + public TransformToolOptionsViewInteraction performAutoCrop() { + onView(withId(R.id.pocketpaint_transform_auto_crop_btn)) + .perform(click()); + return this; + } + + public TransformToolOptionsViewInteraction checkAutoDisplayed() { + onView(withText(R.string.transform_auto_crop_text)) + .check(matches(isDisplayed())); + return this; + } + + public TransformToolOptionsViewInteraction performRotateClockwise() { + onView(withId(R.id.pocketpaint_transform_rotate_right_btn)) + .perform(click()); + return this; + } + + public TransformToolOptionsViewInteraction performRotateCounterClockwise() { + onView(withId(R.id.pocketpaint_transform_rotate_left_btn)) + .perform(click()); + return this; + } + + public TransformToolOptionsViewInteraction performFlipVertical() { + onView(withId(R.id.pocketpaint_transform_flip_vertical_btn)) + .perform(click()); + return this; + } + + public TransformToolOptionsViewInteraction performFlipHorizontal() { + onView(withId(R.id.pocketpaint_transform_flip_horizontal_btn)) + .perform(click()); + return this; + } + + public TransformToolOptionsViewInteraction moveSliderTo(int moveTo) { + onView(withId(R.id.pocketpaint_transform_resize_seekbar)) + .perform(setProgress(moveTo)); + return this; + } + + public TransformToolOptionsViewInteraction performApplyResize() { + onView(withId(R.id.pocketpaint_transform_apply_resize_btn)) + .perform(click()); + return this; + } + + public TransformToolOptionsViewInteraction checkPercentageTextMatches(int expected) { + onView(withId(R.id.pocketpaint_transform_resize_percentage_text)) + .check(matches(withText(Integer.toString(expected)))); + return this; + } + + public TransformToolOptionsViewInteraction checkLayerWidthMatches(int expected) { + Integer expectedValue = expected; + onView(withId(R.id.pocketpaint_transform_width_value)) + .check(matches(hasValueEqualTo(expectedValue.toString()))); + return this; + } + + Matcher hasValueEqualTo(final String content) { + + return new TypeSafeMatcher() { + + @Override + public void describeTo(Description description) { + description.appendText("Has EditText/TextView the value: " + content); + } + + @Override + public boolean matchesSafely(View view) { + if (!(view instanceof TextView) && !(view instanceof EditText)) { + return false; + } + if (view != null) { + String text; + if (view instanceof TextView) { + text = ((TextView) view).getText().toString(); + } else { + text = ((EditText) view).getText().toString(); + } + return text.matches(content); + } + return false; + } + }; + } + + public TransformToolOptionsViewInteraction checkLayerHeightMatches(int expected) { + Integer expectedValue = expected; + onView(withId(R.id.pocketpaint_transform_height_value)) + .check(matches(hasValueEqualTo(expectedValue.toString()))); + return this; + } + + public TransformToolOptionsViewInteraction checkIsDisplayed() { + onView(withId(R.id.pocketpaint_layout_tool_options)) + .check(matches(isDisplayed())); + return this; + } + + public TransformToolOptionsViewInteraction checkIsNotDisplayed() { + onView(withId(R.id.pocketpaint_layout_tool_options)) + .check(matches(not(isDisplayed()))); + return this; + } +}