diff options
24 files changed, 964 insertions, 679 deletions
diff --git a/packages/DocumentsUI/res/layout/drawer_layout.xml b/packages/DocumentsUI/res/layout/drawer_layout.xml index 4898f13d893a..32ba6d03c249 100644 --- a/packages/DocumentsUI/res/layout/drawer_layout.xml +++ b/packages/DocumentsUI/res/layout/drawer_layout.xml @@ -42,7 +42,7 @@ android:popupTheme="?actionBarPopupTheme"> <com.android.documentsui.DropdownBreadcrumb - android:id="@+id/breadcrumb" + android:id="@+id/dropdown_breadcrumb" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="4dp" diff --git a/packages/DocumentsUI/res/layout/fixed_layout.xml b/packages/DocumentsUI/res/layout/fixed_layout.xml index 6c6543780972..9882e94878f7 100644 --- a/packages/DocumentsUI/res/layout/fixed_layout.xml +++ b/packages/DocumentsUI/res/layout/fixed_layout.xml @@ -40,7 +40,7 @@ android:popupTheme="?actionBarPopupTheme"> <com.android.documentsui.HorizontalBreadcrumb - android:id="@+id/breadcrumb" + android:id="@+id/horizontal_breadcrumb" android:layout_marginRight="20dp" android:layout_width="match_parent" android:layout_height="wrap_content" /> diff --git a/packages/DocumentsUI/res/values-sw720dp-land/config.xml b/packages/DocumentsUI/res/values-sw720dp-land/config.xml deleted file mode 100644 index 6893d7a6735b..000000000000 --- a/packages/DocumentsUI/res/values-sw720dp-land/config.xml +++ /dev/null @@ -1,18 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2016 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> - -<resources> -</resources> diff --git a/packages/DocumentsUI/res/values/layouts.xml b/packages/DocumentsUI/res/values/layouts.xml index c9308a19ebd8..a60ce87ba92c 100644 --- a/packages/DocumentsUI/res/values/layouts.xml +++ b/packages/DocumentsUI/res/values/layouts.xml @@ -17,5 +17,4 @@ <resources> <item name="documents_activity" type="layout">@layout/drawer_layout</item> <item name="files_activity" type="layout">@layout/drawer_layout</item> - <item name="downloads_activity" type="layout">@layout/single_pane_layout</item> </resources> diff --git a/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java b/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java index a2f588b80065..3d1a176a8e5f 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java +++ b/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java @@ -141,8 +141,12 @@ public abstract class BaseActivity extends Activity mSearchManager = new SearchViewManager(this, icicle); DocumentsToolbar toolbar = (DocumentsToolbar) findViewById(R.id.toolbar); - Breadcrumb breadcrumb = (Breadcrumb) findViewById(R.id.breadcrumb); setActionBar(toolbar); + + Breadcrumb breadcrumb = + Shared.findView(this, R.id.dropdown_breadcrumb, R.id.horizontal_breadcrumb); + assert(breadcrumb != null); + mNavigator = new NavigationViewManager(mDrawer, toolbar, mState, this, breadcrumb); // Base classes must update result in their onCreate. diff --git a/packages/DocumentsUI/src/com/android/documentsui/Shared.java b/packages/DocumentsUI/src/com/android/documentsui/Shared.java index 2a81c4830dc4..c1db87d507e0 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/Shared.java +++ b/packages/DocumentsUI/src/com/android/documentsui/Shared.java @@ -33,6 +33,8 @@ import java.text.Collator; import java.util.ArrayList; import java.util.List; +import javax.annotation.Nullable; + /** @hide */ public final class Shared { @@ -221,4 +223,15 @@ public final class Shared { Log.e(TAG, "Calling from non-UI thread!"); } } + + public static @Nullable <T> T findView(Activity activity, int... resources) { + for (int id : resources) { + @SuppressWarnings("unchecked") + T r = (T) activity.findViewById(id); + if (r != null) { + return r; + } + } + return null; + } } diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/ActivityTest.java b/packages/DocumentsUI/tests/src/com/android/documentsui/ActivityTest.java index 683fd6c92caf..8ad30d71a811 100644 --- a/packages/DocumentsUI/tests/src/com/android/documentsui/ActivityTest.java +++ b/packages/DocumentsUI/tests/src/com/android/documentsui/ActivityTest.java @@ -20,7 +20,6 @@ import static com.android.documentsui.StubProvider.DEFAULT_AUTHORITY; import static com.android.documentsui.StubProvider.ROOT_0_ID; import static com.android.documentsui.StubProvider.ROOT_1_ID; -import android.annotation.Nullable; import android.app.Activity; import android.content.ContentProviderClient; import android.content.ContentResolver; @@ -32,17 +31,14 @@ import android.support.test.uiautomator.Configurator; import android.support.test.uiautomator.UiDevice; import android.support.test.uiautomator.UiObjectNotFoundException; import android.test.ActivityInstrumentationTestCase2; -import android.util.Log; import android.view.MotionEvent; -import com.android.documentsui.BaseActivity; -import com.android.documentsui.EventListener; -import com.android.documentsui.bots.DirectoryListBot; -import com.android.documentsui.bots.KeyboardBot; -import com.android.documentsui.bots.RootsListBot; +import com.android.documentsui.bots.Bots; import com.android.documentsui.bots.UiBot; import com.android.documentsui.model.RootInfo; +import javax.annotation.Nullable; + /** * Provides basic test environment for UI tests: * - Launches activity @@ -55,6 +51,7 @@ public abstract class ActivityTest<T extends Activity> extends ActivityInstrumen // Testing files. For custom ones, override initTestFiles(). public static final String dirName1 = "Dir1"; + public static final String childDir1 = "ChildDir1"; public static final String fileName1 = "file1.log"; public static final String fileName2 = "file12.png"; public static final String fileName3 = "anotherFile0.log"; @@ -81,8 +78,7 @@ public abstract class ActivityTest<T extends Activity> extends ActivityInstrumen * Override the method if you want to open different root on start. * @return Root that will be opened. Return null if you want to open activity's default root. */ - @Nullable - protected RootInfo getInitialRoot() { + protected @Nullable RootInfo getInitialRoot() { return rootDir0; } @@ -157,29 +153,16 @@ public abstract class ActivityTest<T extends Activity> extends ActivityInstrumen } void assertDefaultContentOfTestDir0() throws UiObjectNotFoundException { + bots.directory.waitForDocument(fileName1); + bots.directory.waitForDocument(fileName2); + bots.directory.waitForDocument(dirName1); + bots.directory.waitForDocument(fileNameNoRename); bots.directory.assertDocumentsCount(4); - bots.directory.assertDocumentsPresent(fileName1, fileName2, dirName1, fileNameNoRename); } void assertDefaultContentOfTestDir1() throws UiObjectNotFoundException { + bots.directory.waitForDocument(fileName3); + bots.directory.waitForDocument(fileName4); bots.directory.assertDocumentsCount(2); - bots.directory.assertDocumentsPresent(fileName3, fileName4); - } - - /** - * Handy collection of bots for working with Files app. - */ - public static final class Bots { - public final UiBot main; - public final RootsListBot roots; - public final DirectoryListBot directory; - public final KeyboardBot keyboard; - - private Bots(UiDevice device, Context context, int timeout) { - this.main = new UiBot(device, context, TIMEOUT); - this.roots = new RootsListBot(device, context, TIMEOUT); - this.directory = new DirectoryListBot(device, context, TIMEOUT); - this.keyboard = new KeyboardBot(device, context, TIMEOUT); - } } } diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/FileManagementUiTest.java b/packages/DocumentsUI/tests/src/com/android/documentsui/FileManagementUiTest.java new file mode 100644 index 000000000000..dd9709d1ced1 --- /dev/null +++ b/packages/DocumentsUI/tests/src/com/android/documentsui/FileManagementUiTest.java @@ -0,0 +1,121 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.documentsui; + +import static com.android.documentsui.StubProvider.ROOT_0_ID; +import static com.android.documentsui.StubProvider.ROOT_1_ID; + +import android.net.Uri; +import android.os.RemoteException; +import android.test.suitebuilder.annotation.LargeTest; +import android.view.KeyEvent; + +@LargeTest +public class FileManagementUiTest extends ActivityTest<FilesActivity> { + + public FileManagementUiTest() { + super(FilesActivity.class); + } + + @Override + public void setUp() throws Exception { + super.setUp(); + initTestFiles(); + } + + @Override + public void initTestFiles() throws RemoteException { + Uri uri = mDocsHelper.createFolder(rootDir0, dirName1); + mDocsHelper.createFolder(uri, childDir1); + + mDocsHelper.createDocument(rootDir0, "text/plain", "file0.log"); + mDocsHelper.createDocument(rootDir0, "image/png", "file1.png"); + mDocsHelper.createDocument(rootDir0, "text/csv", "file2.csv"); + + mDocsHelper.createDocument(rootDir1, "text/plain", "anotherFile0.log"); + mDocsHelper.createDocument(rootDir1, "text/plain", "poodles.text"); + } + + public void testCreateDirectory() throws Exception { + bots.main.openOverflowMenu(); + device.waitForIdle(); + + bots.main.clickToolbarOverflowItem("New folder"); + device.waitForIdle(); + + bots.main.setDialogText("Kung Fu Panda"); + device.waitForIdle(); + + bots.keyboard.pressEnter(); + + bots.directory.waitForDocument("Kung Fu Panda"); + } + + public void testDeleteDocument() throws Exception { + bots.directory.clickDocument("file1.png"); + device.waitForIdle(); + bots.main.clickToolbarItem(R.id.menu_delete); + + bots.main.clickDialogOkButton(); + device.waitForIdle(); + + bots.directory.assertDocumentsAbsent("file1.png"); + } + + public void testKeyboard_CutDocument() throws Exception { + bots.directory.clickDocument("file1.png"); + device.waitForIdle(); + bots.keyboard.pressKey(KeyEvent.KEYCODE_X, KeyEvent.META_CTRL_ON); + + device.waitForIdle(); + + bots.roots.openRoot(ROOT_1_ID); + bots.keyboard.pressKey(KeyEvent.KEYCODE_V, KeyEvent.META_CTRL_ON); + + bots.directory.waitForDocument("file1.png"); + bots.directory.assertDocumentsPresent("file1.png"); + + bots.roots.openRoot(ROOT_0_ID); + bots.directory.assertDocumentsAbsent("file1.png"); + } + + public void testKeyboard_CopyDocument() throws Exception { + bots.directory.clickDocument("file1.png"); + device.waitForIdle(); + bots.keyboard.pressKey(KeyEvent.KEYCODE_C, KeyEvent.META_CTRL_ON); + + device.waitForIdle(); + + bots.roots.openRoot(ROOT_1_ID); + bots.keyboard.pressKey(KeyEvent.KEYCODE_V, KeyEvent.META_CTRL_ON); + + bots.directory.waitForDocument("file1.png"); + + bots.roots.openRoot(ROOT_0_ID); + bots.directory.waitForDocument("file1.png"); + } + + public void testDeleteDocument_Cancel() throws Exception { + bots.directory.clickDocument("file1.png"); + device.waitForIdle(); + bots.main.clickToolbarItem(R.id.menu_delete); + + bots.main.clickDialogCancelButton(); + + bots.directory.waitForDocument("file1.png"); + } +} diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/FilesActivityDefaultsUiTest.java b/packages/DocumentsUI/tests/src/com/android/documentsui/FilesActivityDefaultsUiTest.java new file mode 100644 index 000000000000..d0ec9d7164dc --- /dev/null +++ b/packages/DocumentsUI/tests/src/com/android/documentsui/FilesActivityDefaultsUiTest.java @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.documentsui; + +import static com.android.documentsui.StubProvider.ROOT_0_ID; +import static com.android.documentsui.StubProvider.ROOT_1_ID; + +import android.content.Intent; +import android.provider.DocumentsContract; +import android.test.suitebuilder.annotation.LargeTest; + +import com.android.documentsui.model.RootInfo; + +@LargeTest +public class FilesActivityDefaultsUiTest extends ActivityTest<FilesActivity> { + + public FilesActivityDefaultsUiTest() { + super(FilesActivity.class); + } + + @Override + protected RootInfo getInitialRoot() { + return null; // test the default, unaffected state of the app. + } + + public void testDefaultDirectory() throws Exception { + device.waitForIdle(); + + // Separate logic for "Documents" root, which presence depends on the config setting + if (docsRootEnabled()) { + bots.main.assertWindowTitle("Documents"); + } else { + bots.main.assertWindowTitle("Downloads"); + } + } + + public void testDefaultRoots() throws Exception { + device.waitForIdle(); + + // Should also have Drive, but that requires pre-configuration of devices + // We omit for now. + bots.roots.assertRootsPresent( + "Images", + "Videos", + "Audio", + "Downloads", + ROOT_0_ID, + ROOT_1_ID); + + // Separate logic for "Documents" root, which presence depends on the config setting + if (docsRootEnabled()) { + bots.roots.assertRootsPresent("Documents"); + } else { + bots.roots.assertRootsAbsent("Documents"); + } + } + + private boolean docsRootEnabled() { + return Shared.shouldShowDocumentsRoot(context, new Intent(DocumentsContract.ACTION_BROWSE)); + } +} diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/FilesActivityUiTest.java b/packages/DocumentsUI/tests/src/com/android/documentsui/FilesActivityUiTest.java index 75843b0fd143..1b47705f48dc 100644 --- a/packages/DocumentsUI/tests/src/com/android/documentsui/FilesActivityUiTest.java +++ b/packages/DocumentsUI/tests/src/com/android/documentsui/FilesActivityUiTest.java @@ -16,24 +16,9 @@ package com.android.documentsui; -import static com.android.documentsui.StubProvider.ROOT_0_ID; -import static com.android.documentsui.StubProvider.ROOT_1_ID; - -import android.app.DownloadManager; -import android.app.DownloadManager.Request; -import android.content.Context; -import android.content.Intent; import android.net.Uri; import android.os.RemoteException; -import android.provider.DocumentsContract; -import android.support.test.uiautomator.Configurator; -import android.support.test.uiautomator.UiObject; import android.test.suitebuilder.annotation.LargeTest; -import android.test.suitebuilder.annotation.Suppress; -import android.view.KeyEvent; -import android.view.MotionEvent; - -import com.android.documentsui.model.RootInfo; @LargeTest public class FilesActivityUiTest extends ActivityTest<FilesActivity> { @@ -43,13 +28,15 @@ public class FilesActivityUiTest extends ActivityTest<FilesActivity> { } @Override - protected RootInfo getInitialRoot() { - return null; + public void setUp() throws Exception { + super.setUp(); + initTestFiles(); } @Override public void initTestFiles() throws RemoteException { - mDocsHelper.createFolder(rootDir0, dirName1); + Uri uri = mDocsHelper.createFolder(rootDir0, dirName1); + mDocsHelper.createFolder(uri, childDir1); mDocsHelper.createDocument(rootDir0, "text/plain", "file0.log"); mDocsHelper.createDocument(rootDir0, "image/png", "file1.png"); @@ -59,58 +46,16 @@ public class FilesActivityUiTest extends ActivityTest<FilesActivity> { mDocsHelper.createDocument(rootDir1, "text/plain", "poodles.text"); } - public void testRootsListed() throws Exception { - initTestFiles(); - - // Should also have Drive, but that requires pre-configuration of devices - // We omit for now. - bots.roots.assertRootsPresent( - "Images", - "Videos", - "Audio", - "Downloads", - ROOT_0_ID, - ROOT_1_ID); - - // Separate logic for "Documents" root, which presence depends on the config setting - if (docsRootEnabled()) { - bots.roots.assertRootsPresent("Documents"); - } else { - bots.roots.assertRootsAbsent("Documents"); - } - } - public void testFilesListed() throws Exception { - initTestFiles(); - - bots.roots.openRoot(ROOT_0_ID); bots.directory.assertDocumentsPresent("file0.log", "file1.png", "file2.csv"); } - public void testLoadsDefaultDirectory() throws Exception { - initTestFiles(); - - device.waitForIdle(); - - // Separate logic for "Documents" root, which presence depends on the config setting - if (docsRootEnabled()) { - bots.main.assertWindowTitle("Documents"); - } else { - bots.main.assertWindowTitle("Downloads"); - } - } - - public void testRootClickSetsWindowTitle() throws Exception { - initTestFiles(); - + public void testRootClick_SetsWindowTitle() throws Exception { bots.roots.openRoot("Images"); bots.main.assertWindowTitle("Images"); } public void testFilesList_LiveUpdate() throws Exception { - initTestFiles(); - - bots.roots.openRoot(ROOT_0_ID); mDocsHelper.createDocument(rootDir0, "yummers/sandwich", "Ham & Cheese.sandwich"); bots.directory.waitForDocument("Ham & Cheese.sandwich"); @@ -118,185 +63,16 @@ public class FilesActivityUiTest extends ActivityTest<FilesActivity> { "file0.log", "file1.png", "file2.csv", "Ham & Cheese.sandwich"); } - public void testCreateDirectory() throws Exception { - initTestFiles(); - - bots.roots.openRoot(ROOT_0_ID); - - bots.main.openOverflowMenu(); - bots.main.menuNewFolder().click(); - bots.main.setDialogText("Kung Fu Panda"); - - bots.keyboard.pressEnter(); - - bots.directory.assertDocumentsPresent("Kung Fu Panda"); - } - - public void testOpenBreadcrumb() throws Exception { - initTestFiles(); - - bots.roots.openRoot(ROOT_0_ID); - + public void testNavigateByBreadcrumb() throws Exception { bots.directory.openDocument(dirName1); - if (bots.main.isTablet()) { - openBreadcrumbTabletHelper(); - } else { - openBreadcrumbPhoneHelper(); - } - bots.main.assertBreadcrumbItemsPresent(dirName1, "TEST_ROOT_0"); - bots.main.clickBreadcrumbItem("TEST_ROOT_0"); - - bots.directory.assertDocumentsPresent(dirName1); - } - - private void openBreadcrumbTabletHelper() throws Exception { - } - - private void openBreadcrumbPhoneHelper() throws Exception { - bots.main.clickDropdownBreadcrumb(); - } - - public void testDeleteDocument() throws Exception { - initTestFiles(); - - bots.roots.openRoot(ROOT_0_ID); + bots.directory.waitForDocument(childDir1); // wait for known content + bots.directory.assertDocumentsPresent(childDir1); - bots.directory.clickDocument("file1.png"); + bots.breadcrumb.revealAsNeeded(); device.waitForIdle(); - bots.main.menuDelete().click(); - - bots.main.clickDialogOkButton(); - - bots.directory.assertDocumentsAbsent("file1.png"); - } - - public void testKeyboard_CutDocument() throws Exception { - initTestFiles(); - - bots.roots.openRoot(ROOT_0_ID); - - bots.directory.clickDocument("file1.png"); - device.waitForIdle(); - bots.main.pressKey(KeyEvent.KEYCODE_X, KeyEvent.META_CTRL_ON); - - device.waitForIdle(); - - bots.roots.openRoot(ROOT_1_ID); - bots.main.pressKey(KeyEvent.KEYCODE_V, KeyEvent.META_CTRL_ON); - - bots.directory.waitForDocument("file1.png"); - bots.directory.assertDocumentsPresent("file1.png"); - - bots.roots.openRoot(ROOT_0_ID); - bots.directory.assertDocumentsAbsent("file1.png"); - } - - public void testKeyboard_CopyDocument() throws Exception { - initTestFiles(); - - bots.roots.openRoot(ROOT_0_ID); - - bots.directory.clickDocument("file1.png"); - device.waitForIdle(); - bots.main.pressKey(KeyEvent.KEYCODE_C, KeyEvent.META_CTRL_ON); - - device.waitForIdle(); - - bots.roots.openRoot(ROOT_1_ID); - bots.main.pressKey(KeyEvent.KEYCODE_V, KeyEvent.META_CTRL_ON); - - bots.directory.waitForDocument("file1.png"); - bots.directory.assertDocumentsPresent("file1.png"); - - bots.roots.openRoot(ROOT_0_ID); - bots.directory.assertDocumentsPresent("file1.png"); - } - - public void testDeleteDocument_Cancel() throws Exception { - initTestFiles(); - - bots.roots.openRoot(ROOT_0_ID); - - bots.directory.clickDocument("file1.png"); - device.waitForIdle(); - bots.main.menuDelete().click(); - - bots.main.clickDialogCancelButton(); - - bots.directory.assertDocumentsPresent("file1.png"); - } - - // Tests that pressing tab switches focus between the roots and directory listings. - @Suppress - public void testKeyboard_tab() throws Exception { - bots.main.pressKey(KeyEvent.KEYCODE_TAB); - bots.roots.assertHasFocus(); - bots.main.pressKey(KeyEvent.KEYCODE_TAB); - bots.directory.assertHasFocus(); - } - - // Tests that arrow keys do not switch focus away from the dir list. - @Suppress - public void testKeyboard_arrowsDirList() throws Exception { - for (int i = 0; i < 10; i++) { - bots.main.pressKey(KeyEvent.KEYCODE_DPAD_LEFT); - bots.directory.assertHasFocus(); - } - for (int i = 0; i < 10; i++) { - bots.main.pressKey(KeyEvent.KEYCODE_DPAD_RIGHT); - bots.directory.assertHasFocus(); - } - } - - // Tests that arrow keys do not switch focus away from the roots list. - public void testKeyboard_arrowsRootsList() throws Exception { - bots.main.pressKey(KeyEvent.KEYCODE_TAB); - for (int i = 0; i < 10; i++) { - bots.main.pressKey(KeyEvent.KEYCODE_DPAD_RIGHT); - bots.roots.assertHasFocus(); - } - for (int i = 0; i < 10; i++) { - bots.main.pressKey(KeyEvent.KEYCODE_DPAD_LEFT); - bots.roots.assertHasFocus(); - } - } - - // We don't really need to test the entirety of download support - // since downloads is (almost) just another provider. - @Suppress - public void testDownload_Queued() throws Exception { - DownloadManager dm = (DownloadManager) context.getSystemService( - Context.DOWNLOAD_SERVICE); - // This downloads ends up being queued (because DNS can't be resolved). - // We'll still see an entry in the downloads UI with a "Queued" label. - dm.enqueue(new Request(Uri.parse("http://hammychamp.toodles"))); - - bots.roots.openRoot("Downloads"); - bots.directory.assertDocumentsPresent("Queued"); - } - - @Suppress - public void testDownload_RetryUnsuccessful() throws Exception { - DownloadManager dm = (DownloadManager) context.getSystemService( - Context.DOWNLOAD_SERVICE); - // This downloads fails! But it'll still show up. - dm.enqueue(new Request(Uri.parse("http://www.google.com/hamfancy"))); - - bots.roots.openRoot("Downloads"); - UiObject doc = bots.directory.findDocument("Unsuccessful"); - doc.waitForExists(TIMEOUT); - - int toolType = Configurator.getInstance().getToolType(); - Configurator.getInstance().setToolType(MotionEvent.TOOL_TYPE_FINGER); - doc.click(); - Configurator.getInstance().setToolType(toolType); - - assertTrue(bots.main.findDownloadRetryDialog().exists()); - - device.pressBack(); // to clear the dialog. - } + bots.breadcrumb.assertItemsPresent(dirName1, "TEST_ROOT_0"); - private boolean docsRootEnabled() { - return Shared.shouldShowDocumentsRoot(context, new Intent(DocumentsContract.ACTION_BROWSE)); + bots.breadcrumb.clickItem("TEST_ROOT_0"); + bots.directory.waitForDocument(dirName1); } } diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/IntegratedDownloadsUiTest.java b/packages/DocumentsUI/tests/src/com/android/documentsui/IntegratedDownloadsUiTest.java new file mode 100644 index 000000000000..ef4a68ddb6a6 --- /dev/null +++ b/packages/DocumentsUI/tests/src/com/android/documentsui/IntegratedDownloadsUiTest.java @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.documentsui; + +import android.app.DownloadManager; +import android.app.DownloadManager.Request; +import android.content.Context; +import android.net.Uri; +import android.support.test.uiautomator.Configurator; +import android.support.test.uiautomator.UiObject; +import android.test.suitebuilder.annotation.LargeTest; +import android.test.suitebuilder.annotation.Suppress; +import android.view.MotionEvent; + +// TODO: As of this writing all tests in this class are disabled. Please fix. +@LargeTest +public class IntegratedDownloadsUiTest extends ActivityTest<FilesActivity> { + + public IntegratedDownloadsUiTest() { + super(FilesActivity.class); + } + + // We don't really need to test the entirety of download support + // since downloads is (almost) just another provider. + @Suppress + public void testDownload_Queued() throws Exception { + DownloadManager dm = (DownloadManager) context.getSystemService( + Context.DOWNLOAD_SERVICE); + // This downloads ends up being queued (because DNS can't be resolved). + // We'll still see an entry in the downloads UI with a "Queued" label. + dm.enqueue(new Request(Uri.parse("http://hammychamp.toodles"))); + + bots.roots.openRoot("Downloads"); + bots.directory.assertDocumentsPresent("Queued"); + } + + @Suppress + public void testDownload_RetryUnsuccessful() throws Exception { + DownloadManager dm = (DownloadManager) context.getSystemService( + Context.DOWNLOAD_SERVICE); + // This downloads fails! But it'll still show up. + dm.enqueue(new Request(Uri.parse("http://www.google.com/hamfancy"))); + + bots.roots.openRoot("Downloads"); + UiObject doc = bots.directory.findDocument("Unsuccessful"); + doc.waitForExists(TIMEOUT); + + int toolType = Configurator.getInstance().getToolType(); + Configurator.getInstance().setToolType(MotionEvent.TOOL_TYPE_FINGER); + doc.click(); + Configurator.getInstance().setToolType(toolType); + + assertTrue(bots.main.findDownloadRetryDialog().exists()); + + device.pressBack(); // to clear the dialog. + } +} diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/KeyboardNavigationUiTest.java b/packages/DocumentsUI/tests/src/com/android/documentsui/KeyboardNavigationUiTest.java new file mode 100644 index 000000000000..1657a87a74d8 --- /dev/null +++ b/packages/DocumentsUI/tests/src/com/android/documentsui/KeyboardNavigationUiTest.java @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.documentsui; + +import android.test.suitebuilder.annotation.LargeTest; +import android.test.suitebuilder.annotation.Suppress; +import android.view.KeyEvent; + +@LargeTest +public class KeyboardNavigationUiTest extends ActivityTest<FilesActivity> { + + public KeyboardNavigationUiTest() { + super(FilesActivity.class); + } + + // Tests that pressing tab switches focus between the roots and directory listings. + @Suppress + public void testKeyboard_tab() throws Exception { + bots.keyboard.pressKey(KeyEvent.KEYCODE_TAB); + bots.roots.assertHasFocus(); + bots.keyboard.pressKey(KeyEvent.KEYCODE_TAB); + bots.directory.assertHasFocus(); + } + + // Tests that arrow keys do not switch focus away from the dir list. + @Suppress + public void testKeyboard_arrowsDirList() throws Exception { + for (int i = 0; i < 10; i++) { + bots.keyboard.pressKey(KeyEvent.KEYCODE_DPAD_LEFT); + bots.directory.assertHasFocus(); + } + for (int i = 0; i < 10; i++) { + bots.keyboard.pressKey(KeyEvent.KEYCODE_DPAD_RIGHT); + bots.directory.assertHasFocus(); + } + } + + // Tests that arrow keys do not switch focus away from the roots list. + public void testKeyboard_arrowsRootsList() throws Exception { + bots.keyboard.pressKey(KeyEvent.KEYCODE_TAB); + for (int i = 0; i < 10; i++) { + bots.keyboard.pressKey(KeyEvent.KEYCODE_DPAD_RIGHT); + bots.roots.assertHasFocus(); + } + for (int i = 0; i < 10; i++) { + bots.keyboard.pressKey(KeyEvent.KEYCODE_DPAD_LEFT); + bots.roots.assertHasFocus(); + } + } +} diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/RenameDocumentUiTest.java b/packages/DocumentsUI/tests/src/com/android/documentsui/RenameDocumentUiTest.java index 1f831ee48a29..e7ac2934ced4 100644 --- a/packages/DocumentsUI/tests/src/com/android/documentsui/RenameDocumentUiTest.java +++ b/packages/DocumentsUI/tests/src/com/android/documentsui/RenameDocumentUiTest.java @@ -16,15 +16,8 @@ package com.android.documentsui; -import static android.support.test.espresso.Espresso.onView; -import static android.support.test.espresso.action.ViewActions.closeSoftKeyboard; -import static android.support.test.espresso.action.ViewActions.swipeLeft; -import static android.support.test.espresso.matcher.ViewMatchers.withId; -import static com.android.documentsui.StubProvider.ROOT_0_ID; -import static com.android.documentsui.StubProvider.ROOT_1_ID; - +import android.support.test.uiautomator.UiObjectNotFoundException; import android.test.suitebuilder.annotation.LargeTest; -import android.test.suitebuilder.annotation.Suppress; @LargeTest public class RenameDocumentUiTest extends ActivityTest<FilesActivity> { @@ -42,6 +35,7 @@ public class RenameDocumentUiTest extends ActivityTest<FilesActivity> { bots.roots.closeDrawer(); } + // TODO: Move this over to the FilesMenuManagerTest. public void testRenameEnabled_SingleSelection() throws Exception { bots.directory.selectDocument(fileName1); bots.main.openOverflowMenu(); @@ -51,6 +45,7 @@ public class RenameDocumentUiTest extends ActivityTest<FilesActivity> { device.pressBack(); } + // TODO: Move this over to the FilesMenuManagerTest. public void testNoRenameSupport_SingleSelection() throws Exception { bots.directory.selectDocument(fileNameNoRename); bots.main.openOverflowMenu(); @@ -60,6 +55,7 @@ public class RenameDocumentUiTest extends ActivityTest<FilesActivity> { device.pressBack(); } + // TODO: Move this over to the FilesMenuManagerTest. public void testOneHasRenameSupport_MultipleSelection() throws Exception { bots.directory.selectDocument(fileName1); bots.directory.selectDocument(fileNameNoRename); @@ -70,6 +66,7 @@ public class RenameDocumentUiTest extends ActivityTest<FilesActivity> { device.pressBack(); } + // TODO: Move this over to the FilesMenuManagerTest. public void testRenameDisabled_MultipleSelection() throws Exception { bots.directory.selectDocument(fileName1); bots.directory.selectDocument(fileName2); @@ -82,34 +79,41 @@ public class RenameDocumentUiTest extends ActivityTest<FilesActivity> { public void testRenameFile_OkButton() throws Exception { bots.directory.selectDocument(fileName1); - bots.main.openOverflowMenu(); - bots.main.menuRename().click(); + + clickRename(); + + device.waitForIdle(); bots.main.setDialogText(newName); + device.waitForIdle(); bots.main.clickDialogOkButton(); + bots.directory.waitForDocument(newName); bots.directory.assertDocumentsAbsent(fileName1); - bots.directory.assertDocumentsPresent(newName); bots.directory.assertDocumentsCount(4); } public void testRenameFile_Enter() throws Exception { bots.directory.selectDocument(fileName1); - bots.main.openOverflowMenu(); - bots.main.menuRename().click(); + + clickRename(); + + device.waitForIdle(); bots.main.setDialogText(newName); + device.waitForIdle(); bots.keyboard.pressEnter(); + bots.directory.waitForDocument(newName); bots.directory.assertDocumentsAbsent(fileName1); - bots.directory.assertDocumentsPresent(newName); bots.directory.assertDocumentsCount(4); } public void testRenameFile_Cancel() throws Exception { bots.directory.selectDocument(fileName1); - bots.main.openOverflowMenu(); - bots.main.menuRename().click(); + + clickRename(); + bots.main.setDialogText(newName); bots.main.clickDialogCancelButton(); @@ -122,10 +126,10 @@ public class RenameDocumentUiTest extends ActivityTest<FilesActivity> { public void testRenameDir() throws Exception { String oldName = "Dir1"; String newName = "Dir123"; - bots.directory.selectDocument(oldName); - bots.main.openOverflowMenu(); - bots.main.menuRename().click(); + + clickRename(); + bots.main.setDialogText(newName); bots.keyboard.pressEnter(); @@ -139,8 +143,9 @@ public class RenameDocumentUiTest extends ActivityTest<FilesActivity> { // Check that document with the new name exists bots.directory.assertDocumentsPresent(fileName2); bots.directory.selectDocument(fileName1); - bots.main.openOverflowMenu(); - bots.main.menuRename().click(); + + clickRename(); + bots.main.setDialogText(fileName2); bots.keyboard.pressEnter(); @@ -150,4 +155,9 @@ public class RenameDocumentUiTest extends ActivityTest<FilesActivity> { bots.directory.assertDocumentsPresent(fileName2); bots.directory.assertDocumentsCount(4); } + + private void clickRename() throws UiObjectNotFoundException { + bots.main.clickActionbarOverflowItem("Rename"); + device.waitForIdle(); + } } diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/RootsUiTest.java b/packages/DocumentsUI/tests/src/com/android/documentsui/RootsUiTest.java index 038a20882607..4edfd8a5fe22 100644 --- a/packages/DocumentsUI/tests/src/com/android/documentsui/RootsUiTest.java +++ b/packages/DocumentsUI/tests/src/com/android/documentsui/RootsUiTest.java @@ -39,7 +39,7 @@ public class RootsUiTest extends ActivityTest<FilesActivity> { public void testRootTapped_GoToRootFromChildDir() throws Exception { bots.directory.openDocument(dirName1); - bots.main.assertBreadcrumbTitle(dirName1); + bots.breadcrumb.assertTitle(dirName1); bots.roots.openRoot(ROOT_0_ID); bots.main.assertWindowTitle(ROOT_0_ID); assertDefaultContentOfTestDir0(); diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/SearchViewUiTest.java b/packages/DocumentsUI/tests/src/com/android/documentsui/SearchViewUiTest.java index 1c99db15f7e8..cba51a7ed8f8 100644 --- a/packages/DocumentsUI/tests/src/com/android/documentsui/SearchViewUiTest.java +++ b/packages/DocumentsUI/tests/src/com/android/documentsui/SearchViewUiTest.java @@ -16,16 +16,13 @@ package com.android.documentsui; -import static android.support.test.espresso.Espresso.onView; -import static android.support.test.espresso.action.ViewActions.swipeLeft; -import static android.support.test.espresso.matcher.ViewMatchers.withId; import static com.android.documentsui.StubProvider.ROOT_0_ID; import static com.android.documentsui.StubProvider.ROOT_1_ID; -import android.content.res.Configuration; +import android.support.test.filters.Suppress; +import android.support.test.uiautomator.UiObjectNotFoundException; import android.support.v7.recyclerview.R; import android.test.suitebuilder.annotation.LargeTest; -import android.test.suitebuilder.annotation.Suppress; @LargeTest public class SearchViewUiTest extends ActivityTest<FilesActivity> { @@ -37,168 +34,150 @@ public class SearchViewUiTest extends ActivityTest<FilesActivity> { @Override public void setUp() throws Exception { super.setUp(); + initTestFiles(); // Drawer interferes with a lot of search action; going to try to close any opened ones bots.roots.closeDrawer(); + + openRoot(ROOT_0_ID); // Even if this is the default root...it `wait`s for more better tests + } + + public void testSearchIconVisible_RootWithSearchSupport() throws Exception { + bots.search.assertInputExists(false); + bots.search.assertIconVisible(true); + } + + public void testSearchIconHidden_RootNoSearchSupport() throws Exception { + openRoot(ROOT_1_ID); + + bots.search.assertIconVisible(false); + bots.search.assertInputExists(false); } public void testSearchView_ExpandsOnClick() throws Exception { - bots.main.openSearchView(); - bots.main.assertSearchTextFiledAndIcon(true, false); + bots.search.clickIcon(); + device.waitForIdle(); + + bots.search.assertInputExists(true); + bots.search.assertInputFocused(true); + + // FIXME: Matchers fail the not-present check if we've ever clicked this. + // bots.search.assertIconVisible(false); } public void testSearchView_CollapsesOnBack() throws Exception { - bots.main.openSearchView(); - + bots.search.clickIcon(); device.pressBack(); - bots.main.assertSearchTextFiledAndIcon(false, true); + bots.search.assertIconVisible(true); + bots.search.assertInputExists(false); } public void testSearchView_ClearsTextOnBack() throws Exception { - String query = "file2"; - bots.main.openSearchView(); - bots.main.setSearchQuery(query); + bots.search.clickIcon(); + bots.search.setInputText("file2"); device.pressBack(); - bots.main.assertSearchTextFiledAndIcon(false, true); + // Wait for a file in the default directory to be listed. + bots.directory.waitForDocument(dirName1); + + bots.search.assertIconVisible(true); + bots.search.assertInputExists(false); } - public void testSearch_ResultsFound() throws Exception { - initTestFiles(); - assertDefaultContentOfTestDir0(); + public void testSearchView_StateAfterSearch() throws Exception { + bots.search.clickIcon(); + bots.search.setInputText("file1"); + bots.keyboard.pressEnter(); + device.waitForIdle(); - String query = "file1"; - bots.main.openSearchView(); - bots.main.setSearchQuery(query); - bots.main.assertSearchTextField(true, query); + bots.search.assertInputEquals("file1"); + bots.search.assertInputFocused(false); + } + public void testSearch_ResultsFound() throws Exception { + bots.search.clickIcon(); + bots.search.setInputText("file1"); bots.keyboard.pressEnter(); bots.directory.assertDocumentsCountOnList(true, 2); bots.directory.assertDocumentsPresent(fileName1, fileName2); - - bots.main.assertSearchTextField(false, query); } - public void testSearchDownloads() throws Exception { - initTestFiles(); - bots.roots.openRoot(ROOT_0_ID); - - bots.directory.copyFilesToClipboard(fileName1, fileName2); - device.waitForIdle(); - - bots.roots.openRoot("Downloads"); - bots.directory.pasteFilesFromClipboard(); - - //TODO: Why do we need to click on Downloads again so this will work? - bots.roots.openRoot(ROOT_0_ID); - bots.roots.openRoot("Downloads"); - device.waitForIdle(); - - String query = "file12"; - bots.main.openSearchView(); - bots.main.setSearchQuery(query); + public void testSearch_NoResults() throws Exception { + bots.search.clickIcon(); + bots.search.setInputText("chocolate"); bots.keyboard.pressEnter(); - bots.directory.assertDocumentsCountOnList(true, 1); - bots.directory.assertDocumentsPresent(fileName2); - - device.pressBack(); + String msg = String.valueOf(context.getString(R.string.no_results)); + bots.directory.assertMessageTextView(String.format(msg, "TEST_ROOT_0")); } + @Suppress public void testSearchResultsFound_ClearsOnBack() throws Exception { - initTestFiles(); - assertDefaultContentOfTestDir0(); - - String query = fileName1; - bots.main.openSearchView(); - bots.main.setSearchQuery(query); + bots.search.clickIcon(); + bots.search.setInputText(fileName1); bots.keyboard.pressEnter(); device.pressBack(); + device.waitForIdle(); assertDefaultContentOfTestDir0(); } - public void testSearch_NoResults() throws Exception { - initTestFiles(); - assertDefaultContentOfTestDir0(); - - String query = "chocolate"; - bots.main.openSearchView(); - bots.main.setSearchQuery(query); - - bots.keyboard.pressEnter(); - - String msg = String.valueOf(context.getString(R.string.no_results)); - bots.directory.assertMessageTextView(String.format(msg, "TEST_ROOT_0")); - - bots.main.assertSearchTextField(false, query); - } - + @Suppress public void testSearchNoResults_ClearsOnBack() throws Exception { - initTestFiles(); - assertDefaultContentOfTestDir0(); - - String query = "chocolate"; - bots.main.openSearchView(); - bots.main.setSearchQuery(query); + bots.search.clickIcon(); + bots.search.setInputText("chocolate bunny"); bots.keyboard.pressEnter(); device.pressBack(); - device.waitForIdle(); + assertDefaultContentOfTestDir0(); } - + @Suppress public void testSearchResultsFound_ClearsOnDirectoryChange() throws Exception { - // Skipping this test for phones since currently there's no way to open the drawer on - // phones after doing a search (it's a back button instead of a hamburger button) - if (!bots.main.isTablet()) { - return; - } + // Skipping this test for phones since currently there's no way to open the drawer on + // phones after doing a search (it's a back button instead of a hamburger button) + if (!bots.main.inFixedLayout()) { + return; + } - initTestFiles(); - assertDefaultContentOfTestDir0(); + bots.search.clickIcon(); - String query = fileName1; - bots.main.openSearchView(); - bots.main.setSearchQuery(query); + bots.search.setInputText(fileName1); bots.keyboard.pressEnter(); - bots.roots.openRoot(ROOT_1_ID); + openRoot(ROOT_1_ID); + device.waitForIdle(); assertDefaultContentOfTestDir1(); - bots.roots.openRoot(ROOT_0_ID); - assertDefaultContentOfTestDir0(); - } - - public void testSearchIconVisible_RootWithSearchSupport() throws Exception { - bots.roots.openRoot(ROOT_0_ID); - bots.main.assertSearchTextFiledAndIcon(false, true); - } + openRoot(ROOT_0_ID); + device.waitForIdle(); - public void testSearchIconHidden_RootNoSearchSupport() throws Exception { - bots.roots.openRoot(ROOT_1_ID); - bots.main.assertSearchTextFiledAndIcon(false, false); + assertDefaultContentOfTestDir0(); } - @Override - public void tearDown() throws Exception { - try { - // Proper clean up of #testSearchDownloads - bots.directory.clickDocument(fileName1 + ".txt"); - bots.directory.clickDocument(fileName2); - device.waitForIdle(); - bots.main.menuDelete().click(); - bots.main.clickDialogOkButton(); - } catch (Exception e) { - } finally { - super.tearDown(); + void openRoot(String rootId) throws UiObjectNotFoundException { + bots.roots.openRoot(rootId); + // Open the named root and wait for a known file in it. + // You can find known files by looking in super.initTestFiles(); + switch(rootId) { + case ROOT_0_ID: + bots.directory.waitForDocument(fileName1); + break; + case ROOT_1_ID: + bots.directory.waitForDocument(fileName3); + break; + case "Downloads": + device.waitForIdle(); + break; + default: + throw new IllegalArgumentException("Unsupported root: " + rootId); } } } diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/bots/BaseBot.java b/packages/DocumentsUI/tests/src/com/android/documentsui/bots/BaseBot.java deleted file mode 100644 index 1d2b47f2f286..000000000000 --- a/packages/DocumentsUI/tests/src/com/android/documentsui/bots/BaseBot.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.documentsui.bots; - -import static junit.framework.Assert.assertNotNull; - -import android.content.Context; -import android.support.test.uiautomator.By; -import android.support.test.uiautomator.BySelector; -import android.support.test.uiautomator.UiDevice; -import android.support.test.uiautomator.UiObject; -import android.support.test.uiautomator.UiObject2; -import android.support.test.uiautomator.UiSelector; -import android.support.test.uiautomator.Until; - -/** - * A test helper class that provides support for controlling directory list - * and making assertions against the state of it. - */ -abstract class BaseBot { - final UiDevice mDevice; - final Context mContext; - final int mTimeout; - - BaseBot(UiDevice device, Context context, int timeout) { - mDevice = device; - mContext = context; - mTimeout = timeout; - } - - /** - * Asserts that the specified view or one of its descendents has focus. - */ - protected void assertHasFocus(String resourceName) { - UiObject2 candidate = mDevice.findObject(By.res(resourceName)); - assertNotNull("Expected " + resourceName + " to have focus, but it didn't.", - candidate.findObject(By.focused(true))); - } - - protected UiObject2 find(BySelector selector) { - mDevice.wait(Until.findObject(selector), mTimeout); - return mDevice.findObject(selector); - } - - protected UiObject findObject(String resourceId) { - final UiSelector object = new UiSelector().resourceId(resourceId); - return mDevice.findObject(object); - } - - protected UiObject findObject(String parentResourceId, String childResourceId) { - final UiSelector selector = new UiSelector() - .resourceId(parentResourceId) - .childSelector(new UiSelector().resourceId(childResourceId)); - return mDevice.findObject(selector); - } - - protected void waitForIdle() { - mDevice.waitForIdle(mTimeout); - } -} diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/bots/Bots.java b/packages/DocumentsUI/tests/src/com/android/documentsui/bots/Bots.java new file mode 100644 index 000000000000..4bda5319f82e --- /dev/null +++ b/packages/DocumentsUI/tests/src/com/android/documentsui/bots/Bots.java @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.documentsui.bots; + +import static junit.framework.Assert.assertNotNull; + +import android.content.Context; +import android.support.test.uiautomator.By; +import android.support.test.uiautomator.BySelector; +import android.support.test.uiautomator.UiDevice; +import android.support.test.uiautomator.UiObject; +import android.support.test.uiautomator.UiObject2; +import android.support.test.uiautomator.UiSelector; +import android.support.test.uiautomator.Until; + +/** + * Handy collection of bots for working with Files app. + */ +public final class Bots { + + private static final int TIMEOUT = 5000; + + public final BreadBot breadcrumb; + public final DirectoryListBot directory; + public final KeyboardBot keyboard; + public final RootsListBot roots; + public final SearchBot search; + public final UiBot main; + + public Bots(UiDevice device, Context context, int timeout) { + main = new UiBot(device, context, TIMEOUT); + breadcrumb = new BreadBot(device, context, TIMEOUT, main); + roots = new RootsListBot(device, context, TIMEOUT); + directory = new DirectoryListBot(device, context, TIMEOUT); + keyboard = new KeyboardBot(device, context, TIMEOUT); + search = new SearchBot(device, context, TIMEOUT); + } + + /** + * A test helper class that provides support for controlling directory list + * and making assertions against the state of it. + */ + static abstract class BaseBot { + final UiDevice mDevice; + final Context mContext; + final int mTimeout; + + BaseBot(UiDevice device, Context context, int timeout) { + mDevice = device; + mContext = context; + mTimeout = timeout; + } + + /** + * Asserts that the specified view or one of its descendents has focus. + */ + protected void assertHasFocus(String resourceName) { + UiObject2 candidate = mDevice.findObject(By.res(resourceName)); + assertNotNull("Expected " + resourceName + " to have focus, but it didn't.", + candidate.findObject(By.focused(true))); + } + + protected UiObject2 find(BySelector selector) { + mDevice.wait(Until.findObject(selector), mTimeout); + return mDevice.findObject(selector); + } + + protected UiObject findObject(String resourceId) { + final UiSelector object = new UiSelector().resourceId(resourceId); + return mDevice.findObject(object); + } + + protected UiObject findObject(String parentResourceId, String childResourceId) { + final UiSelector selector = new UiSelector() + .resourceId(parentResourceId) + .childSelector(new UiSelector().resourceId(childResourceId)); + return mDevice.findObject(selector); + } + + protected void waitForIdle() { + mDevice.waitForIdle(mTimeout); + } + } + +} diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/bots/BreadBot.java b/packages/DocumentsUI/tests/src/com/android/documentsui/bots/BreadBot.java new file mode 100644 index 000000000000..8024ff457c91 --- /dev/null +++ b/packages/DocumentsUI/tests/src/com/android/documentsui/bots/BreadBot.java @@ -0,0 +1,138 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.documentsui.bots; + +import static android.support.test.espresso.Espresso.onView; +import static android.support.test.espresso.action.ViewActions.click; +import static android.support.test.espresso.assertion.ViewAssertions.matches; +import static android.support.test.espresso.matcher.ViewMatchers.isAssignableFrom; +import static android.support.test.espresso.matcher.ViewMatchers.withText; +import static org.hamcrest.CoreMatchers.allOf; +import static org.hamcrest.CoreMatchers.is; + +import android.content.Context; +import android.support.test.espresso.ViewInteraction; +import android.support.test.espresso.matcher.BoundedMatcher; +import android.support.test.uiautomator.UiDevice; +import android.support.test.uiautomator.UiObjectNotFoundException; + +import com.android.documentsui.DragOverTextView; +import com.android.documentsui.DropdownBreadcrumb; +import com.android.documentsui.model.DocumentInfo; + +import org.hamcrest.Description; +import org.hamcrest.Matcher; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.function.Predicate; + +import junit.framework.Assert; + +/** + * A test helper class that provides support for controlling the UI Breadcrumb + * programmatically, and making assertions against the state of the UI. + * <p> + * Support for working directly with Roots and Directory view can be found in the respective bots. + */ +public class BreadBot extends Bots.BaseBot { + + public static final String TARGET_PKG = "com.android.documentsui"; + private UiBot mMain; + + public BreadBot(UiDevice device, Context context, int timeout, UiBot main) { + super(device, context, timeout); + mMain = main; + } + + public void assertTitle(String expected) { + // There is no discrete title part on the horizontal breadcrumb... + // so we only test on dropdown. + if (mMain.inDrawerLayout()) { + Matcher<Object> titleMatcher = dropdownTitleMatcher(expected); + onView(Matchers.BREADCRUMB) + .check(matches(titleMatcher)); + } + } + + /** + * Reveals the bread crumb if it was hidden. This will likely be the case + * when the app is in drawer mode. + */ + public void revealAsNeeded() throws Exception { + if (mMain.inDrawerLayout()) { + onView(Matchers.DROPDOWN_BREADCRUMB).perform(click()); + } + } + + public void clickItem(String label) throws UiObjectNotFoundException { + if (mMain.inFixedLayout()) { + findHorizontalEntry(label).perform(click()); + } else { + mMain.findMenuWithName(label).click(); + } + } + + public void assertItemsPresent(String... items) { + Predicate<String> checker = mMain.inFixedLayout() + ? this::hasHorizontalEntry + : mMain::hasMenuWithName; + + assertItemsPresent(items, checker); + } + + public void assertItemsPresent(String[] items, Predicate<String> predicate) { + List<String> absent = new ArrayList<>(); + for (String item : items) { + if (!predicate.test(item)) { + absent.add(item); + } + } + if (!absent.isEmpty()) { + Assert.fail("Expected iteams " + Arrays.asList(items) + + ", but missing " + absent); + } + } + + public boolean hasHorizontalEntry(String label) { + return Matchers.present(findHorizontalEntry(label), withText(label)); + } + + @SuppressWarnings("unchecked") + public ViewInteraction findHorizontalEntry(String label) { + // Matchers.HORIZONTAL_BREADCRUMB + return onView(allOf(isAssignableFrom(DragOverTextView.class), withText(label))); + } + + private static Matcher<Object> dropdownTitleMatcher(String expected) { + final Matcher<String> textMatcher = is(expected); + return new BoundedMatcher<Object, DropdownBreadcrumb>(DropdownBreadcrumb.class) { + @Override + public boolean matchesSafely(DropdownBreadcrumb breadcrumb) { + DocumentInfo selectedDoc = (DocumentInfo) breadcrumb.getSelectedItem(); + return textMatcher.matches(selectedDoc.displayName); + } + + @Override + public void describeTo(Description description) { + description.appendText("with breadcrumb title: "); + textMatcher.describeTo(description); + } + }; + } +} diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/bots/DirectoryListBot.java b/packages/DocumentsUI/tests/src/com/android/documentsui/bots/DirectoryListBot.java index e2aabc7c2baa..fa1e09c36f17 100644 --- a/packages/DocumentsUI/tests/src/com/android/documentsui/bots/DirectoryListBot.java +++ b/packages/DocumentsUI/tests/src/com/android/documentsui/bots/DirectoryListBot.java @@ -21,6 +21,8 @@ import static junit.framework.Assert.assertNotNull; import static junit.framework.Assert.assertTrue; import android.content.Context; +import android.support.test.espresso.Espresso; +import android.support.test.espresso.matcher.ViewMatchers; import android.support.test.uiautomator.By; import android.support.test.uiautomator.BySelector; import android.support.test.uiautomator.Configurator; @@ -30,9 +32,12 @@ import android.support.test.uiautomator.UiObject2; import android.support.test.uiautomator.UiObjectNotFoundException; import android.support.test.uiautomator.UiSelector; import android.support.test.uiautomator.Until; +import android.support.v7.widget.RecyclerView; import android.view.KeyEvent; import android.view.MotionEvent; +import com.android.documentsui.R; + import junit.framework.Assert; import java.util.ArrayList; @@ -44,7 +49,7 @@ import java.util.regex.Pattern; * A test helper class that provides support for controlling directory list * and making assertions against the state of it. */ -public class DirectoryListBot extends BaseBot { +public class DirectoryListBot extends Bots.BaseBot { private static final String DIR_LIST_ID = "com.android.documentsui:id/dir_list"; private static final BySelector SNACK_DELETE = @@ -125,6 +130,7 @@ public class DirectoryListBot extends BaseBot { } public UiObject selectDocument(String label) throws UiObjectNotFoundException { + waitForDocument(label); UiObject doc = findDocument(label); doc.longClick(); return doc; diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/bots/KeyboardBot.java b/packages/DocumentsUI/tests/src/com/android/documentsui/bots/KeyboardBot.java index b0a4d76f4368..2e715777c19f 100644 --- a/packages/DocumentsUI/tests/src/com/android/documentsui/bots/KeyboardBot.java +++ b/packages/DocumentsUI/tests/src/com/android/documentsui/bots/KeyboardBot.java @@ -28,7 +28,7 @@ import android.widget.EditText; /** * A test helper class that provides support for keyboard manipulation. */ -public class KeyboardBot extends BaseBot { +public class KeyboardBot extends Bots.BaseBot { public KeyboardBot(UiDevice device, Context context, int timeout) { super(device, context, timeout); @@ -53,4 +53,12 @@ public class KeyboardBot extends BaseBot { // See b/28399576 onView(isAssignableFrom(EditText.class)).perform(pressImeActionButton()); } + + public void pressKey(int keyCode) { + mDevice.pressKeyCode(keyCode); + } + + public void pressKey(int keyCode, int metaState) { + mDevice.pressKeyCode(keyCode, metaState); + } } diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/bots/Matchers.java b/packages/DocumentsUI/tests/src/com/android/documentsui/bots/Matchers.java index 2343a49e53d7..8962ccb12ccc 100644 --- a/packages/DocumentsUI/tests/src/com/android/documentsui/bots/Matchers.java +++ b/packages/DocumentsUI/tests/src/com/android/documentsui/bots/Matchers.java @@ -16,15 +16,22 @@ package com.android.documentsui.bots; +import static android.support.test.espresso.Espresso.onView; +import static android.support.test.espresso.assertion.ViewAssertions.matches; import static android.support.test.espresso.matcher.ViewMatchers.isAssignableFrom; import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed; +import static android.support.test.espresso.matcher.ViewMatchers.withClassName; import static android.support.test.espresso.matcher.ViewMatchers.withId; import static android.support.test.espresso.matcher.ViewMatchers.withResourceName; import static org.hamcrest.CoreMatchers.allOf; +import static org.hamcrest.CoreMatchers.anyOf; +import static org.hamcrest.Matchers.endsWith; +import android.support.test.espresso.ViewInteraction; +import android.support.test.espresso.matcher.ViewMatchers; +import android.support.v7.widget.RecyclerView; import android.view.View; import android.widget.ImageView; -import android.widget.Spinner; import android.widget.Toolbar; import com.android.documentsui.R; @@ -35,14 +42,66 @@ import org.hamcrest.Matcher; /** * Handy matchers useful for finding stuff in the UI. Use with Espresso testing. */ -final class Matchers { - static final Matcher<View> TOOLBAR = allOf(isAssignableFrom(Toolbar.class), +@SuppressWarnings("unchecked") +public final class Matchers { + + private Matchers() {} + + public static final Matcher<View> TOOLBAR = allOf( + isAssignableFrom(Toolbar.class), withId(R.id.toolbar)); - static final Matcher<View> SEARCH_MENU = allOf(withId(R.id.menu_search), isDisplayed()); - static final Matcher<View> SEARCH_BUTTON = allOf(isAssignableFrom(ImageView.class), + + public static final Matcher<View> ACTIONBAR = allOf( + withClassName(endsWith("ActionBarContextView"))); + + public static final Matcher<View> SEARCH_MENU = allOf( + withId(R.id.menu_search), + isDisplayed()); + + public static final Matcher<View> SEARCH_BUTTON = allOf( + isAssignableFrom(ImageView.class), withResourceName("search_button")); - static final Matcher<View> BREADCRUMB = allOf(isAssignableFrom(Spinner.class), - withId(R.id.breadcrumb)); - static final Matcher<View> MENU_SEARCH = allOf(isAssignableFrom(ActionMenuItemView.class), + + public static final Matcher<View> MENU_SEARCH = allOf( + isAssignableFrom(ActionMenuItemView.class), withResourceName("menu_search")); + + public static final Matcher<View> DROPDOWN_BREADCRUMB = withId( + R.id.dropdown_breadcrumb); + + public static final Matcher<View> HORIZONTAL_BREADCRUMB = withId( + R.id.horizontal_breadcrumb); + + // When any 'ol breadcrumb will do. Could be dropdown or horizontal. + public static final Matcher<View> BREADCRUMB = anyOf( + DROPDOWN_BREADCRUMB, HORIZONTAL_BREADCRUMB); + + public static final Matcher<View> TEXT_ENTRY = allOf( + withClassName(endsWith("EditText"))); + + public static final Matcher<View> TOOLBAR_OVERFLOW = allOf( + withClassName(endsWith("OverflowMenuButton")), + ViewMatchers.isDescendantOfA(TOOLBAR)); + + public static final Matcher<View> ACTIONBAR_OVERFLOW = allOf( + withClassName(endsWith("OverflowMenuButton")), + ViewMatchers.isDescendantOfA(ACTIONBAR)); + + public static final Matcher<View> DIRECTORY_LIST = allOf( + isAssignableFrom(RecyclerView.class), + withId(R.id.dir_list)); + + public static boolean present(Matcher<View> matcher) { + return present(onView(matcher), isDisplayed()); + } + + public static boolean present(ViewInteraction vi, Matcher<View> matcher) { + try { + vi.check(matches(matcher)); + vi.check(matches(isDisplayed())); + return true; + } catch (Exception e) { + return false; + } + } } diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/bots/RootsListBot.java b/packages/DocumentsUI/tests/src/com/android/documentsui/bots/RootsListBot.java index ef22bb521eda..c73cec2a0433 100644 --- a/packages/DocumentsUI/tests/src/com/android/documentsui/bots/RootsListBot.java +++ b/packages/DocumentsUI/tests/src/com/android/documentsui/bots/RootsListBot.java @@ -17,38 +17,32 @@ package com.android.documentsui.bots; import static android.support.test.espresso.Espresso.onView; -import static android.support.test.espresso.matcher.ViewMatchers.isAssignableFrom; -import static android.support.test.espresso.matcher.ViewMatchers.withId; import static android.support.test.espresso.action.ViewActions.swipeLeft; import static android.support.test.espresso.action.ViewActions.swipeRight; +import static android.support.test.espresso.matcher.ViewMatchers.withId; import android.content.Context; -import android.support.test.espresso.UiController; -import android.support.test.espresso.ViewAction; import android.support.test.uiautomator.UiDevice; import android.support.test.uiautomator.UiObject; import android.support.test.uiautomator.UiObjectNotFoundException; import android.support.test.uiautomator.UiScrollable; import android.support.test.uiautomator.UiSelector; -import android.support.v4.view.GravityCompat; import android.util.Log; - import android.view.View; -import junit.framework.Assert; + +import com.android.documentsui.R; import java.util.ArrayList; import java.util.Arrays; import java.util.List; -import org.hamcrest.Matcher; - -import com.android.documentsui.R; +import junit.framework.Assert; /** * A test helper class that provides support for controlling and asserting against * the roots list drawer. */ -public class RootsListBot extends BaseBot { +public class RootsListBot extends Bots.BaseBot { private static final String ROOTS_LIST_ID = "com.android.documentsui:id/roots_list"; private static final String TAG = "RootsListBot"; diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/bots/SearchBot.java b/packages/DocumentsUI/tests/src/com/android/documentsui/bots/SearchBot.java new file mode 100644 index 000000000000..1c707320e712 --- /dev/null +++ b/packages/DocumentsUI/tests/src/com/android/documentsui/bots/SearchBot.java @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.documentsui.bots; + +import static android.support.test.espresso.Espresso.onView; +import static android.support.test.espresso.action.ViewActions.typeText; +import static junit.framework.Assert.assertEquals; +import static junit.framework.Assert.assertFalse; +import static junit.framework.Assert.assertTrue; + +import android.content.Context; +import android.support.test.uiautomator.UiDevice; +import android.support.test.uiautomator.UiObject; +import android.support.test.uiautomator.UiObjectNotFoundException; +import android.support.v7.recyclerview.R; + +/** + * A test helper class that provides support for controlling the search UI + * programmatically, and making assertions against the state of the UI. + * <p> + * Support for working directly with Roots and Directory view can be found in the respective bots. + */ +public class SearchBot extends Bots.BaseBot { + + public static final String TARGET_PKG = "com.android.documentsui"; + + public SearchBot(UiDevice device, Context context, int timeout) { + super(device, context, timeout); + } + + public void clickIcon() throws UiObjectNotFoundException { + UiObject searchView = findSearchView(); + searchView.click(); + assertTrue(searchView.exists()); + } + + public void setInputText(String query) throws UiObjectNotFoundException { + onView(Matchers.SEARCH_MENU).perform(typeText(query)); + } + + public void assertIconVisible(boolean visible) { + if (visible) { + assertTrue( + "Search icon should be visible.", + Matchers.present(Matchers.SEARCH_BUTTON)); + } else { + assertFalse( + "Search icon should not be visible.", + Matchers.present(Matchers.SEARCH_BUTTON)); + } + } + + public void assertInputEquals(String query) + throws UiObjectNotFoundException { + UiObject textField = findSearchViewTextField(); + + assertTrue(textField.exists()); + assertEquals(query, textField.getText()); + } + + public void assertInputFocused(boolean focused) + throws UiObjectNotFoundException { + UiObject textField = findSearchViewTextField(); + + assertTrue(textField.exists()); + assertEquals(focused, textField.isFocused()); + } + + public void assertInputExists(boolean exists) + throws UiObjectNotFoundException { + assertEquals(exists, findSearchViewTextField().exists()); + } + + private UiObject findSearchView() { + return findObject("com.android.documentsui:id/menu_search"); + } + + private UiObject findSearchViewTextField() { + return findObject("com.android.documentsui:id/menu_search", "android:id/search_src_text"); + } + + private UiObject findSearchViewIcon() { + return mContext.getResources().getBoolean(R.bool.full_bar_search_view) + ? findObject("com.android.documentsui:id/menu_search") + : findObject("com.android.documentsui:id/menu_search", "android:id/search_button"); + } +} diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/bots/UiBot.java b/packages/DocumentsUI/tests/src/com/android/documentsui/bots/UiBot.java index f33d90bb522a..fa818a0efdbf 100644 --- a/packages/DocumentsUI/tests/src/com/android/documentsui/bots/UiBot.java +++ b/packages/DocumentsUI/tests/src/com/android/documentsui/bots/UiBot.java @@ -18,20 +18,16 @@ package com.android.documentsui.bots; import static android.support.test.espresso.Espresso.onView; import static android.support.test.espresso.action.ViewActions.click; -import static android.support.test.espresso.action.ViewActions.closeSoftKeyboard; -import static android.support.test.espresso.action.ViewActions.typeText; import static android.support.test.espresso.assertion.ViewAssertions.matches; -import static android.support.test.espresso.matcher.ViewMatchers.isAssignableFrom; -import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed; import static android.support.test.espresso.matcher.ViewMatchers.withId; +import static android.support.test.espresso.matcher.ViewMatchers.withText; import static junit.framework.Assert.assertEquals; -import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertNotNull; -import static junit.framework.Assert.assertTrue; import static org.hamcrest.CoreMatchers.is; import android.content.Context; -import android.content.res.Configuration; +import android.support.test.espresso.Espresso; +import android.support.test.espresso.action.ViewActions; import android.support.test.espresso.matcher.BoundedMatcher; import android.support.test.uiautomator.By; import android.support.test.uiautomator.UiDevice; @@ -39,30 +35,24 @@ import android.support.test.uiautomator.UiObject; import android.support.test.uiautomator.UiObject2; import android.support.test.uiautomator.UiObjectNotFoundException; import android.support.test.uiautomator.UiSelector; -import android.widget.Spinner; +import android.util.TypedValue; import android.widget.Toolbar; import com.android.documentsui.R; -import com.android.documentsui.model.DocumentInfo; import org.hamcrest.Description; import org.hamcrest.Matcher; -import java.util.ArrayList; -import java.util.Arrays; import java.util.Iterator; import java.util.List; -import junit.framework.Assert; -import junit.framework.AssertionFailedError; - /** * A test helper class that provides support for controlling DocumentsUI activities * programmatically, and making assertions against the state of the UI. * <p> * Support for working directly with Roots and Directory view can be found in the respective bots. */ -public class UiBot extends BaseBot { +public class UiBot extends Bots.BaseBot { public static final String TARGET_PKG = "com.android.documentsui"; @@ -75,54 +65,17 @@ public class UiBot extends BaseBot { .check(matches(withToolbarTitle(is(expected)))); } - public void assertBreadcrumbTitle(String expected) { - if (!isTablet()) { - onView(Matchers.BREADCRUMB) - .check(matches(withBreadcrumbTitle(is(expected)))); - } - } - public void assertMenuEnabled(int id, boolean enabled) { UiObject2 menu = findMenuWithName(mContext.getString(id)); assertNotNull(menu); assertEquals(enabled, menu.isEnabled()); } - public void assertSearchTextField(boolean isFocused, String query) - throws UiObjectNotFoundException { - UiObject textField = findSearchViewTextField(); - boolean searchIconVisible = isSearchIconVisible(); - - assertFalse(searchIconVisible); - assertTrue(textField.exists()); - assertEquals(isFocused, textField.isFocused()); - if (query != null) { - assertEquals(query, textField.getText()); - } - } - - public void assertSearchTextFiledAndIcon( - boolean searchTextFieldExists, boolean searchIconExists) { - assertEquals(searchTextFieldExists, findSearchViewTextField().exists()); - boolean searchIconVisible = isSearchIconVisible(); - assertEquals(searchIconExists, searchIconVisible); - } - public void assertInActionMode(boolean inActionMode) { UiObject actionModeBar = findActionModeBar(); assertEquals(inActionMode, actionModeBar.exists()); } - public void openSearchView() throws UiObjectNotFoundException { - UiObject searchView = findSearchView(); - searchView.click(); - assertTrue(searchView.exists()); - } - - public void setSearchQuery(String query) throws UiObjectNotFoundException { - onView(Matchers.SEARCH_MENU).perform(typeText(query)); - } - public UiObject openOverflowMenu() throws UiObjectNotFoundException { UiObject obj = findMenuMoreOptions(); obj.click(); @@ -131,12 +84,21 @@ public class UiBot extends BaseBot { } public void setDialogText(String text) throws UiObjectNotFoundException { - findDialogEditText().setText(text); + onView(Matchers.TEXT_ENTRY) + .perform(ViewActions.replaceText(text)); + } + + public boolean inFixedLayout() { + TypedValue val = new TypedValue(); + // We alias files_activity to either fixed or drawer layouts based + // on screen dimensions. In order to determine which layout + // has been selected, we check the resolved value. + mContext.getResources().getValue(R.layout.files_activity, val, true); + return val.resourceId == R.layout.fixed_layout; } - public boolean isTablet() { - return (mContext.getResources().getConfiguration().screenLayout & - Configuration.SCREENLAYOUT_SIZE_MASK) >= Configuration.SCREENLAYOUT_SIZE_LARGE; + public boolean inDrawerLayout() { + return !inFixedLayout(); } void switchViewMode() { @@ -148,26 +110,6 @@ public class UiBot extends BaseBot { } } - boolean isSearchIconVisible() { - boolean searchIconVisible = true; - boolean isTablet = isTablet(); - try { - if (isTablet) { - // Tablets use ImageView for its search icon, and has search_button as its res name - onView(Matchers.SEARCH_BUTTON) - .check(matches(isDisplayed())); - } else { - // Phones use ActionMenuItemView for its search icon, and has menu_search as its res - // name - onView(Matchers.MENU_SEARCH) - .check(matches(isDisplayed())); - } - } catch (Exception | AssertionFailedError e) { - searchIconVisible = false; - } - return searchIconVisible; - } - UiObject2 menuGridMode() { // Note that we're using By.desc rather than By.res, because of b/25285770 return find(By.desc("Grid view")); @@ -178,79 +120,33 @@ public class UiBot extends BaseBot { return find(By.desc("List view")); } - public UiObject2 menuDelete() { - return find(By.res("com.android.documentsui:id/menu_delete")); - } - - public UiObject2 menuShare() { - return find(By.res("com.android.documentsui:id/menu_share")); + public void clickToolbarItem(int id) { + onView(withId(id)).perform(click()); } - public UiObject2 menuRename() { - return findMenuWithName(mContext.getString(R.string.menu_rename)); - } + public void clickNewFolder() { + onView(Matchers.ACTIONBAR_OVERFLOW).perform(click()); - public UiObject2 menuNewFolder() { - return findMenuWithName(mContext.getString(R.string.menu_create_dir)); + // Click the item by label, since Espresso doesn't support lookup by id on overflow. + onView(withText("New folder")).perform(click()); } - UiObject findSearchView() { - return findObject("com.android.documentsui:id/menu_search"); + public void clickActionbarOverflowItem(String label) { + onView(Matchers.ACTIONBAR_OVERFLOW).perform(click()); + // Click the item by label, since Espresso doesn't support lookup by id on overflow. + onView(withText(label)).perform(click()); } - UiObject findSearchViewTextField() { - return findObject("com.android.documentsui:id/menu_search", "android:id/search_src_text"); - } - - UiObject findSearchViewIcon() { - return mContext.getResources().getBoolean(R.bool.full_bar_search_view) - ? findObject("com.android.documentsui:id/menu_search") - : findObject("com.android.documentsui:id/menu_search", "android:id/search_button"); - } - - public void clickBreadcrumbItem(String label) throws UiObjectNotFoundException { - if (isTablet()) { - findBreadcrumb(label).click(); - } else { - findMenuWithName(label).click(); - } - } - - public void clickDropdownBreadcrumb() throws UiObjectNotFoundException { - assertFalse(isTablet()); - onView(isAssignableFrom(Spinner.class)).perform(click()); - } - - public UiObject findBreadcrumb(String label) throws UiObjectNotFoundException { - final UiSelector breadcrumbList = new UiSelector().resourceId( - "com.android.documentsui:id/breadcrumb"); - - // Wait for the first list item to appear - new UiObject(breadcrumbList.childSelector(new UiSelector())).waitForExists(mTimeout); - - return mDevice.findObject(breadcrumbList.childSelector(new UiSelector().text(label))); - } - - public void assertBreadcrumbItemsPresent(String... labels) throws UiObjectNotFoundException { - List<String> absent = new ArrayList<>(); - for (String label : labels) { - // For non-Tablet devices, a dropdown List menu is shown instead - if (isTablet() ? !findBreadcrumb(label).exists() : findMenuWithName(label) == null) { - absent.add(label); - } - } - if (!absent.isEmpty()) { - Assert.fail("Expected documents " + Arrays.asList(labels) - + ", but missing " + absent); - } + public void clickToolbarOverflowItem(String label) { + onView(Matchers.TOOLBAR_OVERFLOW).perform(click()); + // Click the item by label, since Espresso doesn't support lookup by id on overflow. + onView(withText(label)).perform(click()); } UiObject findActionModeBar() { - return findObject("android:id/action_mode_bar"); - } - - public UiObject findDialogEditText() { - return findObject("android:id/content", "android:id/text1"); + UiObject bar = findObject("android:id/action_mode_bar"); + bar.waitForExists(mTimeout); + return bar; } public UiObject findDownloadRetryDialog() { @@ -263,13 +159,15 @@ public class UiBot extends BaseBot { public void clickDialogOkButton() { // Espresso has flaky results when keyboard shows up, so hiding it for now // before trying to click on any dialog button - onView(withId(android.R.id.button1)).perform(closeSoftKeyboard(), click()); + Espresso.closeSoftKeyboard(); + onView(withId(android.R.id.button1)).perform(click()); } public void clickDialogCancelButton() throws UiObjectNotFoundException { // Espresso has flaky results when keyboard shows up, so hiding it for now // before trying to click on any dialog button - onView(withId(android.R.id.button2)).perform(closeSoftKeyboard(), click()); + Espresso.closeSoftKeyboard(); + onView(withId(android.R.id.button2)).perform(click()); } UiObject findMenuLabelWithName(String label) { @@ -292,6 +190,10 @@ public class UiBot extends BaseBot { return menuItem; } + boolean hasMenuWithName(String label) { + return findMenuWithName(label) != null; + } + UiObject findMenuMoreOptions() { UiSelector selector = new UiSelector().className("android.widget.ImageButton") .descriptionContains("More options"); @@ -299,14 +201,6 @@ public class UiBot extends BaseBot { return mDevice.findObject(selector); } - public void pressKey(int keyCode) { - mDevice.pressKeyCode(keyCode); - } - - public void pressKey(int keyCode, int metaState) { - mDevice.pressKeyCode(keyCode, metaState); - } - private static Matcher<Object> withToolbarTitle( final Matcher<CharSequence> textMatcher) { return new BoundedMatcher<Object, Toolbar>(Toolbar.class) { @@ -322,21 +216,4 @@ public class UiBot extends BaseBot { } }; } - - private static Matcher<Object> withBreadcrumbTitle( - final Matcher<CharSequence> textMatcher) { - return new BoundedMatcher<Object, Spinner>(Spinner.class) { - @Override - public boolean matchesSafely(Spinner breadcrumb) { - DocumentInfo selectedDoc = (DocumentInfo) breadcrumb.getSelectedItem(); - return textMatcher.matches(selectedDoc.displayName); - } - - @Override - public void describeTo(Description description) { - description.appendText("with breadcrumb title: "); - textMatcher.describeTo(description); - } - }; - } } |