diff options
Diffstat (limited to 'mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/BaseSessionTest.kt')
-rw-r--r-- | mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/BaseSessionTest.kt | 297 |
1 files changed, 297 insertions, 0 deletions
diff --git a/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/BaseSessionTest.kt b/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/BaseSessionTest.kt new file mode 100644 index 0000000000..d2964aa54b --- /dev/null +++ b/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/BaseSessionTest.kt @@ -0,0 +1,297 @@ +/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*- + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package org.mozilla.geckoview.test + +import android.os.Parcel +import android.os.SystemClock +import android.view.KeyEvent +import androidx.test.platform.app.InstrumentationRegistry +import org.hamcrest.Matcher +import org.hamcrest.Matchers +import org.json.JSONArray +import org.json.JSONObject +import org.junit.Assume.assumeThat +import org.junit.Rule +import org.junit.rules.ErrorCollector +import org.junit.rules.RuleChain +import org.mozilla.geckoview.GeckoRuntimeSettings +import org.mozilla.geckoview.GeckoSession +import org.mozilla.geckoview.test.rule.GeckoSessionTestRule +import kotlin.reflect.KClass + +/** + * Common base class for tests using GeckoSessionTestRule, + * providing the test rule and other utilities. + */ +open class BaseSessionTest(noErrorCollector: Boolean = false) { + companion object { + const val RESUBMIT_CONFIRM = "/assets/www/resubmit.html" + const val BEFORE_UNLOAD = "/assets/www/beforeunload.html" + const val CLICK_TO_RELOAD_HTML_PATH = "/assets/www/clickToReload.html" + const val CLIPBOARD_READ_HTML_PATH = "/assets/www/clipboard_read.html" + const val CONTENT_CRASH_URL = "about:crashcontent" + const val DOWNLOAD_HTML_PATH = "/assets/www/download.html" + const val FORM_BLANK_HTML_PATH = "/assets/www/form_blank.html" + const val FORMS_HTML_PATH = "/assets/www/forms.html" + const val FORMS_XORIGIN_HTML_PATH = "/assets/www/forms_xorigin.html" + const val FORMS2_HTML_PATH = "/assets/www/forms2.html" + const val FORMS3_HTML_PATH = "/assets/www/forms3.html" + const val FORMS4_HTML_PATH = "/assets/www/forms4.html" + const val FORMS5_HTML_PATH = "/assets/www/forms5.html" + const val SELECT_HTML_PATH = "/assets/www/select.html" + const val SELECT_MULTIPLE_HTML_PATH = "/assets/www/select-multiple.html" + const val SELECT_LISTBOX_HTML_PATH = "/assets/www/select-listbox.html" + const val ADDRESS_FORM_HTML_PATH = "/assets/www/address_form.html" + const val FORMS_AUTOCOMPLETE_HTML_PATH = "/assets/www/forms_autocomplete.html" + const val FORMS_ID_VALUE_HTML_PATH = "/assets/www/forms_id_value.html" + const val CC_FORM_HTML_PATH = "/assets/www/cc_form.html" + const val HELLO_HTML_PATH = "/assets/www/hello.html" + const val HELLO2_HTML_PATH = "/assets/www/hello2.html" + const val HELLO_IFRAME_HTML_PATH = "/assets/www/iframe_hello.html" + const val INPUTS_PATH = "/assets/www/inputs.html" + const val INVALID_URI = "not a valid uri" + const val LINKS_HTML_PATH = "/assets/www/links.html" + const val LOREM_IPSUM_HTML_PATH = "/assets/www/loremIpsum.html" + const val METATAGS_PATH = "/assets/www/metatags.html" + const val MOUSE_TO_RELOAD_HTML_PATH = "/assets/www/mouseToReload.html" + const val NEW_SESSION_CHILD_HTML_PATH = "/assets/www/newSession_child.html" + const val NEW_SESSION_HTML_PATH = "/assets/www/newSession.html" + const val POPUP_HTML_PATH = "/assets/www/popup.html" + const val PRINT_CONTENT_CHANGE = "/assets/www/print_content_change.html" + const val PRINT_IFRAME = "/assets/www/print_iframe.html" + const val PROMPT_HTML_PATH = "/assets/www/prompts.html" + const val SAVE_STATE_PATH = "/assets/www/saveState.html" + const val TEST_GIF_PATH = "/assets/www/images/test.gif" + const val TITLE_CHANGE_HTML_PATH = "/assets/www/titleChange.html" + const val TRACKERS_PATH = "/assets/www/trackers.html" + const val VIDEO_OGG_PATH = "/assets/www/ogg.html" + const val VIDEO_MP4_PATH = "/assets/www/mp4.html" + const val VIDEO_WEBM_PATH = "/assets/www/webm.html" + const val VIDEO_BAD_PATH = "/assets/www/badVideoPath.html" + const val UNKNOWN_HOST_URI = "https://www.test.invalid/" + const val UNKNOWN_PROTOCOL_URI = "htt://invalid" + const val FULLSCREEN_PATH = "/assets/www/fullscreen.html" + const val VIEWPORT_PATH = "/assets/www/viewport.html" + const val IFRAME_REDIRECT_LOCAL = "/assets/www/iframe_redirect_local.html" + const val IFRAME_REDIRECT_AUTOMATION = "/assets/www/iframe_redirect_automation.html" + const val AUTOPLAY_PATH = "/assets/www/autoplay.html" + const val SCROLL_TEST_PATH = "/assets/www/scroll.html" + const val COLORS_HTML_PATH = "/assets/www/colors.html" + const val FIXED_BOTTOM = "/assets/www/fixedbottom.html" + const val FIXED_VH = "/assets/www/fixedvh.html" + const val FIXED_PERCENT = "/assets/www/fixedpercent.html" + const val STORAGE_TITLE_HTML_PATH = "/assets/www/reflect_local_storage_into_title.html" + const val HUNG_SCRIPT = "/assets/www/hungScript.html" + const val PUSH_HTML_PATH = "/assets/www/push/push.html" + const val OPEN_WINDOW_PATH = "/assets/www/worker/open_window.html" + const val OPEN_WINDOW_TARGET_PATH = "/assets/www/worker/open_window_target.html" + const val DATA_URI_PATH = "/assets/www/data_uri.html" + const val IFRAME_UNKNOWN_PROTOCOL = "/assets/www/iframe_unknown_protocol.html" + const val MEDIA_SESSION_DOM1_PATH = "/assets/www/media_session_dom1.html" + const val MEDIA_SESSION_DEFAULT1_PATH = "/assets/www/media_session_default1.html" + const val TOUCH_HTML_PATH = "/assets/www/touch.html" + const val TOUCH_XORIGIN_HTML_PATH = "/assets/www/touch_xorigin.html" + const val GETUSERMEDIA_XORIGIN_CONTAINER_HTML_PATH = "/assets/www/getusermedia_xorigin_container.html" + const val ROOT_100_PERCENT_HEIGHT_HTML_PATH = "/assets/www/root_100_percent_height.html" + const val ROOT_98VH_HTML_PATH = "/assets/www/root_98vh.html" + const val ROOT_100VH_HTML_PATH = "/assets/www/root_100vh.html" + const val IFRAME_100_PERCENT_HEIGHT_NO_SCROLLABLE_HTML_PATH = "/assets/www/iframe_100_percent_height_no_scrollable.html" + const val IFRAME_100_PERCENT_HEIGHT_SCROLLABLE_HTML_PATH = "/assets/www/iframe_100_percent_height_scrollable.html" + const val IFRAME_98VH_SCROLLABLE_HTML_PATH = "/assets/www/iframe_98vh_scrollable.html" + const val IFRAME_98VH_NO_SCROLLABLE_HTML_PATH = "/assets/www/iframe_98vh_no_scrollable.html" + const val TOUCHSTART_HTML_PATH = "/assets/www/touchstart.html" + const val TOUCH_ACTION_HTML_PATH = "/assets/www/touch-action.html" + const val TOUCH_ACTION_WHEEL_LISTENER_HTML_PATH = "/assets/www/touch-action-wheel-listener.html" + const val OVERSCROLL_BEHAVIOR_AUTO_HTML_PATH = "/assets/www/overscroll-behavior-auto.html" + const val OVERSCROLL_BEHAVIOR_AUTO_NONE_HTML_PATH = "/assets/www/overscroll-behavior-auto-none.html" + const val OVERSCROLL_BEHAVIOR_NONE_AUTO_HTML_PATH = "/assets/www/overscroll-behavior-none-auto.html" + const val OVERSCROLL_BEHAVIOR_NONE_NON_ROOT_HTML_PATH = "/assets/www/overscroll-behavior-none-on-non-root.html" + const val SCROLL_HANDOFF_HTML_PATH = "/assets/www/scroll-handoff.html" + const val SHOW_DYNAMIC_TOOLBAR_HTML_PATH = "/assets/www/showDynamicToolbar.html" + const val CONTEXT_MENU_AUDIO_HTML_PATH = "/assets/www/context_menu_audio.html" + const val CONTEXT_MENU_IMAGE_NESTED_HTML_PATH = "/assets/www/context_menu_image_nested.html" + const val CONTEXT_MENU_IMAGE_HTML_PATH = "/assets/www/context_menu_image.html" + const val CONTEXT_MENU_LINK_HTML_PATH = "/assets/www/context_menu_link.html" + const val CONTEXT_MENU_VIDEO_HTML_PATH = "/assets/www/context_menu_video.html" + const val CONTEXT_MENU_BLOB_FULL_HTML_PATH = "/assets/www/context_menu_blob_full.html" + const val CONTEXT_MENU_BLOB_BUFFERED_HTML_PATH = "/assets/www/context_menu_blob_buffered.html" + const val REMOTE_IFRAME = "/assets/www/accessibility/test-remote-iframe.html" + const val LOCAL_IFRAME = "/assets/www/accessibility/test-local-iframe.html" + const val BODY_FULLY_COVERED_BY_GREEN_ELEMENT = "/assets/www/red-background-body-fully-covered-by-green-element.html" + const val COLOR_GRID_HTML_PATH = "/assets/www/color_grid.html" + const val COLOR_ORANGE_BACKGROUND_HTML_PATH = "/assets/www/color_orange_background.html" + const val TRACEMONKEY_PDF_PATH = "/assets/www/tracemonkey.pdf" + const val HELLO_PDF_WORLD_PDF_PATH = "/assets/www/helloPDFWorld.pdf" + const val NO_META_VIEWPORT_HTML_PATH = "/assets/www/no-meta-viewport.html" + + const val TEST_ENDPOINT = GeckoSessionTestRule.TEST_ENDPOINT + const val TEST_HOST = GeckoSessionTestRule.TEST_HOST + const val TEST_PORT = GeckoSessionTestRule.TEST_PORT + } + + val sessionRule = GeckoSessionTestRule() + + // Override this to include more `evaluate` rules in the chain + @get:Rule + open val rules = RuleChain.outerRule(sessionRule) + + @get:Rule var temporaryProfile = TemporaryProfileRule() + + @get:Rule val errors = ErrorCollector() + + val mainSession get() = sessionRule.session + + fun <T> assertThat(reason: String, v: T, m: Matcher<in T>) = sessionRule.checkThat(reason, v, m) + fun <T> assertInAutomationThat(reason: String, v: T, m: Matcher<in T>) = + if (sessionRule.env.isAutomation) { + assertThat(reason, v, m) + } else { + assumeThat(reason, v, m) + } + + init { + if (!noErrorCollector) { + sessionRule.errorCollector = errors + } + } + + fun <T> forEachCall(vararg values: T): T = sessionRule.forEachCall(*values) + + fun getTestBytes(path: String) = + InstrumentationRegistry.getInstrumentation().targetContext.resources.assets + .open(path.removePrefix("/assets/")).readBytes() + + fun createTestUrl(path: String) = GeckoSessionTestRule.TEST_ENDPOINT + path + + fun GeckoSession.loadTestPath(path: String) = + this.loadUri(createTestUrl(path)) + + inline fun GeckoRuntimeSettings.toParcel(lambda: (Parcel) -> Unit) { + val parcel = Parcel.obtain() + try { + this.writeToParcel(parcel, 0) + + val pos = parcel.dataPosition() + parcel.setDataPosition(0) + + lambda(parcel) + + assertThat( + "Read parcel matches written parcel", + parcel.dataPosition(), + Matchers.equalTo(pos), + ) + } finally { + parcel.recycle() + } + } + + fun GeckoSession.open() = + sessionRule.openSession(this) + + fun GeckoSession.waitForPageStop() = + sessionRule.waitForPageStop(this) + + fun GeckoSession.waitForPageStops(count: Int) = + sessionRule.waitForPageStops(this, count) + + fun GeckoSession.waitUntilCalled(ifce: KClass<*>, vararg methods: String) = + sessionRule.waitUntilCalled(this, ifce, *methods) + + fun GeckoSession.waitUntilCalled(callback: Any) = + sessionRule.waitUntilCalled(this, callback) + + fun GeckoSession.addDisplay(x: Int, y: Int) = + sessionRule.addDisplay(this, x, y) + + fun GeckoSession.releaseDisplay() = + sessionRule.releaseDisplay(this) + + fun GeckoSession.forCallbacksDuringWait(callback: Any) = + sessionRule.forCallbacksDuringWait(this, callback) + + fun GeckoSession.delegateUntilTestEnd(callback: Any) = + sessionRule.delegateUntilTestEnd(this, callback) + + fun GeckoSession.delegateDuringNextWait(callback: Any) = + sessionRule.delegateDuringNextWait(this, callback) + + fun GeckoSession.synthesizeTap(x: Int, y: Int) = + sessionRule.synthesizeTap(this, x, y) + + fun GeckoSession.synthesizeMouseMove(x: Int, y: Int) = + sessionRule.synthesizeMouseMove(this, x, y) + + fun GeckoSession.evaluateJS(js: String): Any? = + sessionRule.evaluateJS(this, js) + + fun GeckoSession.evaluatePromiseJS(js: String): GeckoSessionTestRule.ExtensionPromise = + sessionRule.evaluatePromiseJS(this, js) + + fun GeckoSession.waitForJS(js: String): Any? = + sessionRule.waitForJS(this, js) + + fun GeckoSession.waitForRoundTrip() = sessionRule.waitForRoundTrip(this) + + fun GeckoSession.pressKey(keyCode: Int) { + // Create a Promise to listen to the key event, and wait on it below. + val promise = this.evaluatePromiseJS( + """new Promise(r => window.addEventListener( + 'keyup', r, { once: true }))""", + ) + val time = SystemClock.uptimeMillis() + val keyEvent = KeyEvent(time, time, KeyEvent.ACTION_DOWN, keyCode, 0) + this.textInput.onKeyDown(keyCode, keyEvent) + this.textInput.onKeyUp( + keyCode, + KeyEvent.changeAction(keyEvent, KeyEvent.ACTION_UP), + ) + promise.value + } + + fun GeckoSession.flushApzRepaints() = sessionRule.flushApzRepaints(this) + + fun GeckoSession.promiseAllPaintsDone() = sessionRule.promiseAllPaintsDone(this) + + fun GeckoSession.getLinkColor(selector: String) = sessionRule.getLinkColor(this, selector) + + fun GeckoSession.setResolutionAndScaleTo(resolution: Float) = + sessionRule.setResolutionAndScaleTo(this, resolution) + + fun GeckoSession.triggerCookieBannerDetected() = + sessionRule.triggerCookieBannerDetected(this) + + fun GeckoSession.triggerCookieBannerHandled() = + sessionRule.triggerCookieBannerHandled(this) + + var GeckoSession.active: Boolean + get() = sessionRule.getActive(this) + set(value) = setActive(value) + + @Suppress("UNCHECKED_CAST") + fun Any?.asJsonArray(): JSONArray = this as JSONArray + + @Suppress("UNCHECKED_CAST") + fun<V> JSONObject.asMap(): Map<String?, V?> { + val result = HashMap<String?, V?>() + for (key in this.keys()) { + result[key] = this[key] as V + } + return result + } + + @Suppress("UNCHECKED_CAST") + fun<T> Any?.asJSList(): List<T> { + val array = this.asJsonArray() + val result = ArrayList<T>() + + for (i in 0 until array.length()) { + result.add(array[i] as T) + } + + return result + } +} |