diff options
Diffstat (limited to 'libs/editor/WordPressEditor/src/androidTest')
7 files changed, 481 insertions, 0 deletions
diff --git a/libs/editor/WordPressEditor/src/androidTest/AndroidManifest.xml b/libs/editor/WordPressEditor/src/androidTest/AndroidManifest.xml new file mode 100644 index 000000000..eeef377fb --- /dev/null +++ b/libs/editor/WordPressEditor/src/androidTest/AndroidManifest.xml @@ -0,0 +1,12 @@ +<?xml version="1.0" encoding="utf-8"?> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="org.wordpress.android.editor" > + <application> + <activity android:name=".MockActivity" + android:theme="@style/Theme.AppCompat.Light.DarkActionBar" + android:exported="false" /> + <activity android:name=".MockEditorActivity" + android:theme="@style/Theme.AppCompat.Light.DarkActionBar" + android:exported="false"/> + </application>> +</manifest> diff --git a/libs/editor/WordPressEditor/src/androidTest/java/org.wordpress.android.editor/EditorFragmentForTests.java b/libs/editor/WordPressEditor/src/androidTest/java/org.wordpress.android.editor/EditorFragmentForTests.java new file mode 100644 index 000000000..1babaa3db --- /dev/null +++ b/libs/editor/WordPressEditor/src/androidTest/java/org.wordpress.android.editor/EditorFragmentForTests.java @@ -0,0 +1,41 @@ +package org.wordpress.android.editor; + +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import java.util.Map; + +public class EditorFragmentForTests extends EditorFragment { + protected EditorWebViewAbstract mWebView; + + protected boolean mInitCalled = false; + protected boolean mDomLoaded = false; + protected boolean mOnSelectionStyleChangedCalled = false; + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + View view = super.onCreateView(inflater, container, savedInstanceState); + mWebView = (EditorWebViewAbstract) view.findViewById(R.id.webview); + return view; + } + + @Override + protected void initJsEditor() { + super.initJsEditor(); + mInitCalled = true; + } + + @Override + public void onDomLoaded() { + super.onDomLoaded(); + mDomLoaded = true; + } + + @Override + public void onSelectionStyleChanged(final Map<String, Boolean> changeMap) { + super.onSelectionStyleChanged(changeMap); + mOnSelectionStyleChangedCalled = true; + } +} diff --git a/libs/editor/WordPressEditor/src/androidTest/java/org.wordpress.android.editor/EditorFragmentTest.java b/libs/editor/WordPressEditor/src/androidTest/java/org.wordpress.android.editor/EditorFragmentTest.java new file mode 100644 index 000000000..a955f0a98 --- /dev/null +++ b/libs/editor/WordPressEditor/src/androidTest/java/org.wordpress.android.editor/EditorFragmentTest.java @@ -0,0 +1,177 @@ +package org.wordpress.android.editor; + +import android.app.Activity; +import android.test.ActivityInstrumentationTestCase2; +import android.view.View; +import android.widget.ToggleButton; + +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.CountDownLatch; + +import static org.wordpress.android.editor.TestingUtils.waitFor; + +public class EditorFragmentTest extends ActivityInstrumentationTestCase2<MockEditorActivity> { + private Activity mActivity; + private EditorFragmentForTests mFragment; + + public EditorFragmentTest() { + super(MockEditorActivity.class); + } + + @Override + protected void setUp() throws Exception { + super.setUp(); + mActivity = getActivity(); + mFragment = (EditorFragmentForTests) mActivity.getFragmentManager().findFragmentByTag("editorFragment"); + } + + public void testDomLoadedCallbackReceived() { + // initJsEditor() should have been called on setup + assertTrue(mFragment.mInitCalled); + + waitForOnDomLoaded(); + + // The JS editor should have sent out a callback when the DOM loaded, triggering onDomLoaded() + assertTrue(mFragment.mDomLoaded); + } + + public void testFormatBarToggledOnSelectedFieldChanged() { + Map<String, String> selectionArgs = new HashMap<>(); + + selectionArgs.put("id", "zss_field_title"); + mFragment.onSelectionChanged(selectionArgs); + + waitFor(100); + + View view = mFragment.getView(); + + if (view == null) { + throw(new IllegalStateException("Fragment view is empty")); + } + + // The formatting buttons should be disabled while the title field is selected + ToggleButton mediaButton = (ToggleButton) view.findViewById(R.id.format_bar_button_media); + ToggleButton boldButton = (ToggleButton) view.findViewById(R.id.format_bar_button_bold); + ToggleButton italicButton = (ToggleButton) view.findViewById(R.id.format_bar_button_italic); + ToggleButton quoteButton = (ToggleButton) view.findViewById(R.id.format_bar_button_quote); + ToggleButton ulButton = (ToggleButton) view.findViewById(R.id.format_bar_button_ul); + ToggleButton olButton = (ToggleButton) view.findViewById(R.id.format_bar_button_ol); + ToggleButton linkButton = (ToggleButton) view.findViewById(R.id.format_bar_button_link); + ToggleButton strikethroughButton = (ToggleButton) view.findViewById(R.id.format_bar_button_strikethrough); + + assertFalse(mediaButton.isEnabled()); + assertFalse(boldButton.isEnabled()); + assertFalse(italicButton.isEnabled()); + assertFalse(quoteButton.isEnabled()); + assertFalse(ulButton.isEnabled()); + assertFalse(olButton.isEnabled()); + assertFalse(linkButton.isEnabled()); + + if (strikethroughButton != null) { + assertFalse(strikethroughButton.isEnabled()); + } + + // The HTML button should always be enabled + ToggleButton htmlButton = (ToggleButton) view.findViewById(R.id.format_bar_button_html); + assertTrue(htmlButton.isEnabled()); + + selectionArgs.clear(); + selectionArgs.put("id", "zss_field_content"); + mFragment.onSelectionChanged(selectionArgs); + + waitFor(500); + + // The formatting buttons should be enabled while the content field is selected + assertTrue(mediaButton.isEnabled()); + assertTrue(boldButton.isEnabled()); + assertTrue(italicButton.isEnabled()); + assertTrue(quoteButton.isEnabled()); + assertTrue(ulButton.isEnabled()); + assertTrue(olButton.isEnabled()); + assertTrue(linkButton.isEnabled()); + + if (strikethroughButton != null) { + assertTrue(strikethroughButton.isEnabled()); + } + + // The HTML button should always be enabled + assertTrue(htmlButton.isEnabled()); + } + + public void testHtmlModeToggleTextTransfer() throws InterruptedException { + waitForOnDomLoaded(); + + final View view = mFragment.getView(); + + if (view == null) { + throw (new IllegalStateException("Fragment view is empty")); + } + + final ToggleButton htmlButton = (ToggleButton) view.findViewById(R.id.format_bar_button_html); + + String content = mFragment.getContent().toString(); + + final SourceViewEditText titleText = (SourceViewEditText) view.findViewById(R.id.sourceview_title); + final SourceViewEditText contentText = (SourceViewEditText) view.findViewById(R.id.sourceview_content); + + // -- Check that title and content text is properly loaded into the EditTexts when switching to HTML mode + + final CountDownLatch uiThreadLatch1 = new CountDownLatch(1); + + mActivity.runOnUiThread(new Runnable() { + @Override + public void run() { + htmlButton.performClick(); // Turn on HTML mode + + uiThreadLatch1.countDown(); + } + }); + + uiThreadLatch1.await(); + + waitFor(500); + + // The HTML mode fields should be populated with the raw HTML loaded into the WebView on load + // (see MockEditorActivity) + assertEquals("A title", titleText.getText().toString()); + assertEquals(content, contentText.getText().toString()); + + // -- Check that the title and content text is updated in the WebView when switching back from HTML mode + + final CountDownLatch uiThreadLatch2 = new CountDownLatch(1); + + mActivity.runOnUiThread(new Runnable() { + @Override + public void run() { + titleText.setText("new title"); + contentText.setText("new <b>content</b>"); + + // Check that getTitle() and getContent() return latest version even in HTML mode + assertEquals("new title", mFragment.getTitle()); + assertEquals("new <b>content</b>", mFragment.getContent()); + + htmlButton.performClick(); // Turn off HTML mode + + uiThreadLatch2.countDown(); + } + }); + + uiThreadLatch2.await(); + + waitFor(300); // Wait for JS to update the title/content + + assertEquals("new title", mFragment.getTitle()); + assertEquals("new <b>content</b>", mFragment.getContent()); + } + + private void waitForOnDomLoaded() { + long start = System.currentTimeMillis(); + while(!mFragment.mDomLoaded) { + waitFor(10); + if (System.currentTimeMillis() - start > 5000) { + throw(new RuntimeException("Callback wait timed out")); + } + } + } +}
\ No newline at end of file diff --git a/libs/editor/WordPressEditor/src/androidTest/java/org.wordpress.android.editor/MockActivity.java b/libs/editor/WordPressEditor/src/androidTest/java/org.wordpress.android.editor/MockActivity.java new file mode 100644 index 000000000..580ea2de4 --- /dev/null +++ b/libs/editor/WordPressEditor/src/androidTest/java/org.wordpress.android.editor/MockActivity.java @@ -0,0 +1,7 @@ +package org.wordpress.android.editor; + +import android.support.v7.app.AppCompatActivity; + +public class MockActivity extends AppCompatActivity { + +} diff --git a/libs/editor/WordPressEditor/src/androidTest/java/org.wordpress.android.editor/MockEditorActivity.java b/libs/editor/WordPressEditor/src/androidTest/java/org.wordpress.android.editor/MockEditorActivity.java new file mode 100644 index 000000000..ecd515154 --- /dev/null +++ b/libs/editor/WordPressEditor/src/androidTest/java/org.wordpress.android.editor/MockEditorActivity.java @@ -0,0 +1,102 @@ +package org.wordpress.android.editor; + +import android.app.FragmentManager; +import android.app.FragmentTransaction; +import android.net.Uri; +import android.os.Bundle; +import android.support.v7.app.AppCompatActivity; +import android.view.DragEvent; +import android.widget.LinearLayout; + +import org.wordpress.android.editor.EditorFragmentAbstract.EditorDragAndDropListener; +import org.wordpress.android.editor.EditorFragmentAbstract.EditorFragmentListener; +import org.wordpress.android.editor.EditorFragmentAbstract.TrackableEvent; +import org.wordpress.android.util.helpers.MediaFile; + +import java.util.ArrayList; + +public class MockEditorActivity extends AppCompatActivity implements EditorFragmentListener, + EditorDragAndDropListener { + public static final int LAYOUT_ID = 999; + + EditorFragment mEditorFragment; + + @SuppressWarnings("ResourceType") + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + LinearLayout linearLayout = new LinearLayout(this); + linearLayout.setId(LAYOUT_ID); + setContentView(linearLayout); + + FragmentManager fragmentManager = getFragmentManager(); + FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction(); + + mEditorFragment = new EditorFragmentForTests(); + fragmentTransaction.add(linearLayout.getId(), mEditorFragment, "editorFragment"); + fragmentTransaction.commit(); + } + + @Override + public void onEditorFragmentInitialized() { + mEditorFragment.setTitle("A title"); + mEditorFragment.setContent("<p>Example <strong>content</strong></p>"); + } + + @Override + public void onSettingsClicked() { + + } + + @Override + public void onAddMediaClicked() { + + } + + @Override + public void onMediaRetryClicked(String mediaId) { + + } + + @Override + public void onMediaUploadCancelClicked(String mediaId, boolean delete) { + + } + + @Override + public void onFeaturedImageChanged(long mediaId) { + + } + + @Override + public void onVideoPressInfoRequested(String videoId) { + + } + + @Override + public String onAuthHeaderRequested(String url) { + return ""; + } + + @Override + public void saveMediaFile(MediaFile mediaFile) { + + } + + @Override + public void onTrackableEvent(TrackableEvent event) { + + } + + @Override + public void onMediaDropped(ArrayList<Uri> mediaUri) { + + } + + @Override + public void onRequestDragAndDropPermissions(DragEvent dragEvent) { + + } +} + diff --git a/libs/editor/WordPressEditor/src/androidTest/java/org.wordpress.android.editor/TestingUtils.java b/libs/editor/WordPressEditor/src/androidTest/java/org.wordpress.android.editor/TestingUtils.java new file mode 100644 index 000000000..e51cba6ba --- /dev/null +++ b/libs/editor/WordPressEditor/src/androidTest/java/org.wordpress.android.editor/TestingUtils.java @@ -0,0 +1,14 @@ +package org.wordpress.android.editor; + +import org.wordpress.android.util.AppLog; + +public class TestingUtils { + + static public void waitFor(long milliseconds) { + try { + Thread.sleep(milliseconds); + } catch(InterruptedException e) { + AppLog.e(AppLog.T.EDITOR, "Thread interrupted"); + } + } +} diff --git a/libs/editor/WordPressEditor/src/androidTest/java/org.wordpress.android.editor/ZssEditorTest.java b/libs/editor/WordPressEditor/src/androidTest/java/org.wordpress.android.editor/ZssEditorTest.java new file mode 100644 index 000000000..9989cbd65 --- /dev/null +++ b/libs/editor/WordPressEditor/src/androidTest/java/org.wordpress.android.editor/ZssEditorTest.java @@ -0,0 +1,128 @@ +package org.wordpress.android.editor; + +import android.annotation.SuppressLint; +import android.app.Activity; +import android.app.Instrumentation; +import android.os.Build; +import android.test.ActivityInstrumentationTestCase2; +import android.webkit.JavascriptInterface; + +import java.util.HashSet; +import java.util.Set; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +/** + * Tests for the <code>ZSSEditor</code> inside an <code>EditorWebViewAbstract</code>, with no UI. + */ +public class ZssEditorTest extends ActivityInstrumentationTestCase2<MockActivity> { + private static final String JS_CALLBACK_HANDLER = "nativeCallbackHandler"; + + private Instrumentation mInstrumentation; + private EditorWebViewAbstract mWebView; + + private CountDownLatch mSetUpLatch; + + private TestMethod mTestMethod; + private CountDownLatch mCallbackLatch; + private CountDownLatch mDomLoadedCallbackLatch; + private Set<String> mCallbackSet; + + private enum TestMethod { + INIT + } + + public ZssEditorTest() { + super(MockActivity.class); + } + + @SuppressLint("AddJavascriptInterface") + @Override + protected void setUp() throws Exception { + super.setUp(); + mInstrumentation = getInstrumentation(); + Activity activity = getActivity(); + mSetUpLatch = new CountDownLatch(1); + mDomLoadedCallbackLatch = new CountDownLatch(1); + + mSetUpLatch.countDown(); + + String htmlEditor = Utils.getHtmlFromFile(activity, "android-editor.html"); + + if (htmlEditor != null) { + htmlEditor = htmlEditor.replace("%%TITLE%%", getActivity().getString(R.string.visual_editor)); + htmlEditor = htmlEditor.replace("%%ANDROID_API_LEVEL%%", String.valueOf(Build.VERSION.SDK_INT)); + htmlEditor = htmlEditor.replace("%%LOCALIZED_STRING_INIT%%", + "nativeState.localizedStringEdit = '" + getActivity().getString(R.string.edit) + "';\n" + + "nativeState.localizedStringUploading = '" + getActivity().getString(R.string.uploading) + "';\n" + + "nativeState.localizedStringUploadingGallery = '" + + getActivity().getString(R.string.uploading_gallery_placeholder) + "';\n"); + } + + final String finalHtmlEditor = htmlEditor; + + activity.runOnUiThread(new Runnable() { + @Override + public void run() { + mWebView = new EditorWebView(mInstrumentation.getContext(), null); + if (Build.VERSION.SDK_INT < 17) { + mWebView.setJsCallbackReceiver(new MockJsCallbackReceiver(new EditorFragmentForTests())); + } else { + mWebView.addJavascriptInterface(new MockJsCallbackReceiver(new EditorFragmentForTests()), + JS_CALLBACK_HANDLER); + } + mWebView.loadDataWithBaseURL("file:///android_asset/", finalHtmlEditor, "text/html", "utf-8", ""); + mSetUpLatch.countDown(); + } + }); + } + + public void testInitialization() throws InterruptedException { + // Wait for setUp() to finish initializing the WebView + mSetUpLatch.await(); + + // Identify this method to the MockJsCallbackReceiver + mTestMethod = TestMethod.INIT; + + // Expecting three startup callbacks from the ZSS editor + mCallbackLatch = new CountDownLatch(3); + mCallbackSet = new HashSet<>(); + boolean callbacksReceived = mCallbackLatch.await(5, TimeUnit.SECONDS); + assertTrue(callbacksReceived); + + Set<String> expectedSet = new HashSet<>(); + expectedSet.add("callback-new-field:id=zss_field_title"); + expectedSet.add("callback-new-field:id=zss_field_content"); + expectedSet.add("callback-dom-loaded:"); + + assertEquals(expectedSet, mCallbackSet); + } + + private class MockJsCallbackReceiver extends JsCallbackReceiver { + public MockJsCallbackReceiver(EditorFragmentAbstract editorFragmentAbstract) { + super(editorFragmentAbstract); + } + + @JavascriptInterface + public void executeCallback(String callbackId, String params) { + if (callbackId.equals("callback-dom-loaded")) { + // Notify test methods that the dom has loaded + mDomLoadedCallbackLatch.countDown(); + } + + // Handle callbacks and count down latches according to the currently running test + switch(mTestMethod) { + case INIT: + if (callbackId.equals("callback-dom-loaded")) { + mCallbackSet.add(callbackId + ":"); + } else if (callbackId.equals("callback-new-field")) { + mCallbackSet.add(callbackId + ":" + params); + } + mCallbackLatch.countDown(); + break; + default: + throw(new RuntimeException("Unknown calling method")); + } + } + } +} |