Source code

Revision control

Copy as Markdown

Other Tools

/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*-
* Any copyright is dedicated to the Public Domain.
package org.mozilla.geckoview.test
import android.graphics.* // ktlint-disable no-wildcard-imports
import android.graphics.Bitmap
import android.os.SystemClock
import android.util.Base64
import android.view.MotionEvent
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.MediumTest
import org.hamcrest.Matchers.* // ktlint-disable no-wildcard-imports
import org.hamcrest.Matchers.closeTo
import org.hamcrest.Matchers.equalTo
import org.junit.Assume.assumeThat
import org.junit.Test
import org.junit.runner.RunWith
import org.mozilla.geckoview.GeckoResult
import org.mozilla.geckoview.GeckoSession
import org.mozilla.geckoview.GeckoSession.ContentDelegate
import org.mozilla.geckoview.GeckoSession.ScrollDelegate
import org.mozilla.geckoview.test.rule.GeckoSessionTestRule.AssertCalled
import org.mozilla.geckoview.test.rule.GeckoSessionTestRule.WithDisplay
import java.io.ByteArrayOutputStream
private const val SCREEN_WIDTH = 100
private const val SCREEN_HEIGHT = 200
@RunWith(AndroidJUnit4::class)
@MediumTest
class DynamicToolbarTest : BaseSessionTest() {
// Makes sure we can load a page when the dynamic toolbar is bigger than the whole content
@WithDisplay(height = SCREEN_HEIGHT, width = SCREEN_WIDTH)
@Test
fun outOfRangeValue() {
val dynamicToolbarMaxHeight = SCREEN_HEIGHT + 1
sessionRule.display?.run { setDynamicToolbarMaxHeight(dynamicToolbarMaxHeight) }
// Set active since setVerticalClipping call affects only for forground tab.
mainSession.setActive(true)
mainSession.loadTestPath(HELLO_HTML_PATH)
mainSession.waitForPageStop()
}
private fun assertScreenshotResult(result: GeckoResult<Bitmap>, comparisonImage: Bitmap) {
sessionRule.waitForResult(result).let {
assertThat(
"Screenshot is not null",
it,
notNullValue(),
)
assertThat("Widths are the same", comparisonImage.width, equalTo(it.width))
assertThat("Heights are the same", comparisonImage.height, equalTo(it.height))
assertThat("Byte counts are the same", comparisonImage.byteCount, equalTo(it.byteCount))
assertThat("Configs are the same", comparisonImage.config, equalTo(it.config))
if (!comparisonImage.sameAs(it)) {
val outputForComparison = ByteArrayOutputStream()
comparisonImage.compress(Bitmap.CompressFormat.PNG, 100, outputForComparison)
val outputForActual = ByteArrayOutputStream()
it.compress(Bitmap.CompressFormat.PNG, 100, outputForActual)
val actualString: String = Base64.encodeToString(outputForActual.toByteArray(), Base64.DEFAULT)
val comparisonString: String = Base64.encodeToString(outputForComparison.toByteArray(), Base64.DEFAULT)
assertThat("Encoded strings are the same", comparisonString, equalTo(actualString))
}
assertThat("Bytes are the same", comparisonImage.sameAs(it), equalTo(true))
}
}
/**
* Returns a whole green Bitmap.
* This Bitmap would be a reference image of tests in this file.
*/
private fun getComparisonScreenshot(width: Int, height: Int): Bitmap {
val screenshotFile = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
val canvas = Canvas(screenshotFile)
val paint = Paint()
paint.color = Color.rgb(0, 128, 0)
canvas.drawRect(0f, 0f, width.toFloat(), height.toFloat(), paint)
return screenshotFile
}
// With the dynamic toolbar max height vh units values exceed
// the top most window height. This is a test case that exceeded area
// is rendered properly (on the compositor).
@WithDisplay(height = SCREEN_HEIGHT, width = SCREEN_WIDTH)
@Test
fun positionFixedElementClipping() {
sessionRule.display?.run { setDynamicToolbarMaxHeight(SCREEN_HEIGHT / 2) }
val reference = getComparisonScreenshot(SCREEN_WIDTH, SCREEN_HEIGHT)
// FIXED_VH is an HTML file which has a position:fixed element whose
// style is "width: 100%; height: 200vh" and the document is scaled by
// minimum-scale 0.5, so that the height of the element exceeds the
// window height.
mainSession.loadTestPath(BaseSessionTest.FIXED_VH)
mainSession.waitForPageStop()
// Scroll down bit, if we correctly render the document, the position
// fixed element still covers whole the document area.
mainSession.evaluateJS("window.scrollTo({ top: 100, behavior: 'instant' })")
// Wait a while to make sure the scrolling result is composited on the compositor
// since capturePixels() takes a snapshot directly from the compositor without
// waiting for a corresponding MozAfterPaint on the main-thread so it's possible
// to take a stale snapshot even if it's a result of syncronous scrolling.
mainSession.evaluateJS("new Promise(resolve => window.setTimeout(resolve, 1000))")
sessionRule.display?.let {
assertScreenshotResult(it.capturePixels(), reference)
}
}
// Asynchronous scrolling with the dynamic toolbar max height causes
// situations where the visual viewport size gets bigger than the layout
// viewport on the compositor thread because of 200vh position:fixed
// elements. This is a test case that a 200vh position element is
// properly rendered its positions.
@WithDisplay(height = SCREEN_HEIGHT, width = SCREEN_WIDTH)
@Test
fun layoutViewportExpansion() {
sessionRule.display?.run { setDynamicToolbarMaxHeight(SCREEN_HEIGHT / 2) }
val reference = getComparisonScreenshot(SCREEN_WIDTH, SCREEN_HEIGHT)
mainSession.loadTestPath(BaseSessionTest.FIXED_VH)
mainSession.waitForPageStop()
mainSession.evaluateJS("window.scrollTo(0, 100)")
// Scroll back to the original position by asynchronous scrolling.
mainSession.evaluateJS("window.scrollTo({ top: 0, behavior: 'smooth' })")
mainSession.evaluateJS("new Promise(resolve => window.setTimeout(resolve, 1000))")
sessionRule.display?.let {
assertScreenshotResult(it.capturePixels(), reference)
}
}
@WithDisplay(height = SCREEN_HEIGHT, width = SCREEN_WIDTH)
@Test
fun visualViewportEvents() {
val dynamicToolbarMaxHeight = SCREEN_HEIGHT / 2
sessionRule.display?.run { setDynamicToolbarMaxHeight(dynamicToolbarMaxHeight) }
// Set active since setVerticalClipping call affects only for forground tab.
mainSession.setActive(true)
mainSession.loadTestPath(BaseSessionTest.FIXED_VH)
mainSession.waitForPageStop()
val pixelRatio = mainSession.evaluateJS("window.devicePixelRatio") as Double
val scale = mainSession.evaluateJS("window.visualViewport.scale") as Double
for (i in 1..dynamicToolbarMaxHeight) {
// Simulate the dynamic toolbar is going to be hidden.
sessionRule.display?.run { setVerticalClipping(-i) }
val expectedViewportHeight = (SCREEN_HEIGHT - dynamicToolbarMaxHeight + i) / scale / pixelRatio
val promise = mainSession.evaluatePromiseJS(
"""
new Promise(resolve => {
window.visualViewport.addEventListener('resize', resolve(window.visualViewport.height));
});
""".trimIndent(),
)
assertThat(
"The visual viewport height should be changed in response to the dynamc toolbar transition",
promise.value as Double,
closeTo(expectedViewportHeight, .01),
)
}
}
@WithDisplay(height = SCREEN_HEIGHT, width = SCREEN_WIDTH)
@Test
fun percentBaseValueOnPositionFixedElement() {
val dynamicToolbarMaxHeight = SCREEN_HEIGHT / 2
sessionRule.display?.run { setDynamicToolbarMaxHeight(dynamicToolbarMaxHeight) }
// Set active since setVerticalClipping call affects only for forground tab.
mainSession.setActive(true)
mainSession.loadTestPath(BaseSessionTest.FIXED_PERCENT)
mainSession.waitForPageStop()
val originalHeight = mainSession.evaluateJS(
"""
getComputedStyle(document.querySelector('#fixed-element')).height
""".trimIndent(),
) as String
// Set the vertical clipping value to the middle of toolbar transition.
sessionRule.display?.run { setVerticalClipping(-dynamicToolbarMaxHeight / 2) }
var height = mainSession.evaluateJS(
"""
getComputedStyle(document.querySelector('#fixed-element')).height
""".trimIndent(),
) as String
assertThat(
"The %-based height should be the static in the middle of toolbar tansition",
height,
equalTo(originalHeight),
)
// Set the vertical clipping value to hide the toolbar completely.
sessionRule.display?.run { setVerticalClipping(-dynamicToolbarMaxHeight) }
height = mainSession.evaluateJS(
"""
getComputedStyle(document.querySelector('#fixed-element')).height
""".trimIndent(),
) as String
val scale = mainSession.evaluateJS("window.visualViewport.scale") as Double
val expectedHeight = (SCREEN_HEIGHT / scale).toInt()
assertThat(
"The %-based height should be now recomputed based on the screen height",
height,
equalTo(expectedHeight.toString() + "px"),
)
}
@WithDisplay(height = SCREEN_HEIGHT, width = SCREEN_WIDTH)
@Test
fun resizeEvents() {
val dynamicToolbarMaxHeight = SCREEN_HEIGHT / 2
sessionRule.display?.run { setDynamicToolbarMaxHeight(dynamicToolbarMaxHeight) }
// Set active since setVerticalClipping call affects only for forground tab.
mainSession.setActive(true)
mainSession.loadTestPath(BaseSessionTest.FIXED_VH)
mainSession.waitForPageStop()
for (i in 1..dynamicToolbarMaxHeight - 1) {
val promise = mainSession.evaluatePromiseJS(
"""
new Promise(resolve => {
let fired = false;
window.addEventListener('resize', () => { fired = true; }, { once: true });
// Note that `resize` event is fired just before rAF callbacks, so under ideal
// circumstances waiting for a rAF should be sufficient, even if it's not sufficient
// unexpected resize event(s) will be caught in the next loop.
requestAnimationFrame(() => { resolve(fired); });
});
""".trimIndent(),
)
// Simulate the dynamic toolbar is going to be hidden.
sessionRule.display?.run { setVerticalClipping(-i) }
assertThat(
"'resize' event on window should not be fired in response to the dynamc toolbar transition",
promise.value as Boolean,
equalTo(false),
)
}
val promise = mainSession.evaluatePromiseJS(
"""
new Promise(resolve => {
window.addEventListener('resize', () => { resolve(true); }, { once: true });
});
""".trimIndent(),
)
sessionRule.display?.run { setVerticalClipping(-dynamicToolbarMaxHeight) }
assertThat(
"'resize' event on window should be fired when the dynamc toolbar is completely hidden",
promise.value as Boolean,
equalTo(true),
)
}
@WithDisplay(height = SCREEN_HEIGHT, width = SCREEN_WIDTH)
@Test
fun windowInnerHeight() {
val dynamicToolbarMaxHeight = SCREEN_HEIGHT / 2
sessionRule.display?.run { setDynamicToolbarMaxHeight(dynamicToolbarMaxHeight) }
// Set active since setVerticalClipping call affects only for forground tab.
mainSession.setActive(true)
// We intentionally use FIXED_BOTTOM instead of FIXED_VH in this test since
// FIXED_VH has `minimum-scale=0.5` thus we can't properly test window.innerHeight
// with FXIED_VH for now due to bug 1598487.
mainSession.loadTestPath(BaseSessionTest.FIXED_BOTTOM)
mainSession.waitForPageStop()
val pixelRatio = mainSession.evaluateJS("window.devicePixelRatio") as Double
for (i in 1..dynamicToolbarMaxHeight - 1) {
val promise = mainSession.evaluatePromiseJS(
"""
new Promise(resolve => {
window.visualViewport.addEventListener('resize', resolve(window.innerHeight));
});
""".trimIndent(),
)
// Simulate the dynamic toolbar is going to be hidden.
sessionRule.display?.run { setVerticalClipping(-i) }
assertThat(
"window.innerHeight should not be changed in response to the dynamc toolbar transition",
promise.value as Double,
closeTo(SCREEN_HEIGHT / 2 / pixelRatio, .01),
)
}
val promise = mainSession.evaluatePromiseJS(
"""
new Promise(resolve => {
window.addEventListener('resize', () => { resolve(window.innerHeight); }, { once: true });
});
""".trimIndent(),
)
sessionRule.display?.run { setVerticalClipping(-dynamicToolbarMaxHeight) }
assertThat(
"window.innerHeight should be changed when the dynamc toolbar is completely hidden",
promise.value as Double,
closeTo(SCREEN_HEIGHT / pixelRatio, .01),
)
}
@WithDisplay(height = SCREEN_HEIGHT, width = SCREEN_WIDTH)
@Test
fun notCrashOnResizeEvent() {
val dynamicToolbarMaxHeight = SCREEN_HEIGHT / 2
sessionRule.display?.run { setDynamicToolbarMaxHeight(dynamicToolbarMaxHeight) }
// Set active since setVerticalClipping call affects only for forground tab.
mainSession.setActive(true)
mainSession.loadTestPath(BaseSessionTest.FIXED_VH)
mainSession.waitForPageStop()
val promise = mainSession.evaluatePromiseJS(
"""
new Promise(resolve => window.addEventListener('resize', () => resolve(true)));
""".trimIndent(),
)
// Do some setVerticalClipping calls that we might try to queue two window resize events.
sessionRule.display?.run { setVerticalClipping(-dynamicToolbarMaxHeight) }
sessionRule.display?.run { setVerticalClipping(-dynamicToolbarMaxHeight + 1) }
sessionRule.display?.run { setVerticalClipping(-dynamicToolbarMaxHeight) }
assertThat("Got a rezie event", promise.value as Boolean, equalTo(true))
}
@WithDisplay(height = SCREEN_HEIGHT, width = SCREEN_WIDTH)
@Test
fun showDynamicToolbar() {
val dynamicToolbarMaxHeight = SCREEN_HEIGHT / 2
sessionRule.display?.run { setDynamicToolbarMaxHeight(dynamicToolbarMaxHeight) }
// Set active since setVerticalClipping call affects only for forground tab.
mainSession.setActive(true)
mainSession.loadTestPath(SHOW_DYNAMIC_TOOLBAR_HTML_PATH)
mainSession.waitForPageStop()
mainSession.evaluateJS("window.scrollTo(0, " + dynamicToolbarMaxHeight + ")")
mainSession.waitUntilCalled(object : ScrollDelegate {
@AssertCalled(count = 1)
override fun onScrollChanged(session: GeckoSession, scrollX: Int, scrollY: Int) {
}
})
// Simulate the dynamic toolbar being hidden by the scroll
sessionRule.display?.run { setVerticalClipping(-dynamicToolbarMaxHeight) }
mainSession.synthesizeTap(5, 25)
mainSession.waitUntilCalled(object : ContentDelegate {
@AssertCalled(count = 1)
override fun onShowDynamicToolbar(session: GeckoSession) {
}
})
}
@WithDisplay(height = SCREEN_HEIGHT, width = SCREEN_WIDTH)
@Test
fun showDynamicToolbarOnOverflowHidden() {
val dynamicToolbarMaxHeight = SCREEN_HEIGHT / 2
sessionRule.display?.run { setDynamicToolbarMaxHeight(dynamicToolbarMaxHeight) }
// Set active since setVerticalClipping call affects only for forground tab.
mainSession.setActive(true)
mainSession.loadTestPath(SHOW_DYNAMIC_TOOLBAR_HTML_PATH)
mainSession.waitForPageStop()
mainSession.evaluateJS("window.scrollTo(0, " + dynamicToolbarMaxHeight + ")")
mainSession.waitUntilCalled(object : ScrollDelegate {
@AssertCalled(count = 1)
override fun onScrollChanged(session: GeckoSession, scrollX: Int, scrollY: Int) {
}
})
// Simulate the dynamic toolbar being hidden by the scroll
sessionRule.display?.run { setVerticalClipping(-dynamicToolbarMaxHeight) }
mainSession.evaluateJS("document.documentElement.style.overflow = 'hidden'")
mainSession.waitUntilCalled(object : ContentDelegate {
@AssertCalled(count = 1)
override fun onShowDynamicToolbar(session: GeckoSession) {
}
})
}
private fun getComputedViewportHeight(style: String): Double {
val viewportHeight = mainSession.evaluateJS(
"""
const target = document.createElement('div');
target.style.height = '$style';
document.body.appendChild(target);
parseFloat(getComputedStyle(target).height);
""".trimIndent(),
) as Double
return viewportHeight
}
@WithDisplay(height = SCREEN_HEIGHT, width = SCREEN_WIDTH)
@Test
fun viewportVariants() {
val dynamicToolbarMaxHeight = SCREEN_HEIGHT / 2
sessionRule.display?.run { setDynamicToolbarMaxHeight(dynamicToolbarMaxHeight) }
// Set active since setVerticalClipping call affects only for forground tab.
mainSession.setActive(true)
mainSession.loadTestPath(BaseSessionTest.VIEWPORT_PATH)
mainSession.waitForPageStop()
val pixelRatio = mainSession.evaluateJS("window.devicePixelRatio") as Double
val scale = mainSession.evaluateJS("window.visualViewport.scale") as Double
var smallViewportHeight = getComputedViewportHeight("100svh")
assertThat(
"svh value at the initial state",
smallViewportHeight,
closeTo((SCREEN_HEIGHT - dynamicToolbarMaxHeight) / scale / pixelRatio, 0.1),
)
var largeViewportHeight = getComputedViewportHeight("100lvh")
assertThat(
"lvh value at the initial state",
largeViewportHeight,
closeTo(SCREEN_HEIGHT / scale / pixelRatio, 0.1),
)
var dynamicViewportHeight = getComputedViewportHeight("100dvh")
assertThat(
"dvh value at the initial state",
dynamicViewportHeight,
closeTo((SCREEN_HEIGHT - dynamicToolbarMaxHeight) / scale / pixelRatio, 0.1),
)
// Move down the toolbar at a fourth of its position.
sessionRule.display?.run { setVerticalClipping(-dynamicToolbarMaxHeight / 4) }
smallViewportHeight = getComputedViewportHeight("100svh")
assertThat(
"svh value during toolbar transition",
smallViewportHeight,
closeTo((SCREEN_HEIGHT - dynamicToolbarMaxHeight) / scale / pixelRatio, 0.1),
)
largeViewportHeight = getComputedViewportHeight("100lvh")
assertThat(
"lvh value during toolbar transition",
largeViewportHeight,
closeTo(SCREEN_HEIGHT / scale / pixelRatio, 0.1),
)
dynamicViewportHeight = getComputedViewportHeight("100dvh")
assertThat(
"dvh value during toolbar transition",
dynamicViewportHeight,
closeTo((SCREEN_HEIGHT - dynamicToolbarMaxHeight + dynamicToolbarMaxHeight / 4) / scale / pixelRatio, 0.1),
)
}
// With dynamic toolbar, there was a floating point rounding error in Gecko layout side.
// The error was appeared by user interactive async scrolling, not by programatic async
// scrolling, e.g. scrollTo() method. If the error happens there will appear 1px gap
// between <body> and an element which covers up the <body> element.
// This test simulates the situation.
@WithDisplay(height = SCREEN_HEIGHT, width = SCREEN_WIDTH)
@Test
fun noGapAppearsBetweenBodyAndElementFullyCoveringBody() {
// Bug 1764219 - disable the test to reduce intermittent failure rate
assumeThat(sessionRule.env.isDebugBuild, equalTo(false))
val dynamicToolbarMaxHeight = SCREEN_HEIGHT / 2
sessionRule.display?.run { setDynamicToolbarMaxHeight(dynamicToolbarMaxHeight) }
// Set active since setVerticalClipping call affects only for forground tab.
mainSession.setActive(true)
val reference = getComparisonScreenshot(SCREEN_WIDTH, SCREEN_HEIGHT)
mainSession.loadTestPath(BaseSessionTest.BODY_FULLY_COVERED_BY_GREEN_ELEMENT)
mainSession.waitForPageStop()
mainSession.flushApzRepaints()
// Scrolling down by touch events.
var downTime = SystemClock.uptimeMillis()
var down = MotionEvent.obtain(
downTime,
SystemClock.uptimeMillis(),
MotionEvent.ACTION_DOWN,
50f,
70f,
0,
)
mainSession.panZoomController.onTouchEvent(down)
var move = MotionEvent.obtain(
downTime,
SystemClock.uptimeMillis(),
MotionEvent.ACTION_MOVE,
50f,
30f,
0,
)
mainSession.panZoomController.onTouchEvent(move)
var up = MotionEvent.obtain(
downTime,
SystemClock.uptimeMillis(),
MotionEvent.ACTION_UP,
50f,
10f,
0,
)
mainSession.panZoomController.onTouchEvent(up)
mainSession.flushApzRepaints()
// Scrolling up by touch events to restore the original position.
downTime = SystemClock.uptimeMillis()
down = MotionEvent.obtain(
downTime,
SystemClock.uptimeMillis(),
MotionEvent.ACTION_DOWN,
50f,
10f,
0,
)
mainSession.panZoomController.onTouchEvent(down)
move = MotionEvent.obtain(
downTime,
SystemClock.uptimeMillis(),
MotionEvent.ACTION_MOVE,
50f,
30f,
0,
)
mainSession.panZoomController.onTouchEvent(move)
up = MotionEvent.obtain(
downTime,
SystemClock.uptimeMillis(),
MotionEvent.ACTION_UP,
50f,
70f,
0,
)
mainSession.panZoomController.onTouchEvent(up)
mainSession.flushApzRepaints()
sessionRule.display?.let {
assertScreenshotResult(it.capturePixels(), reference)
}
}
@WithDisplay(height = SCREEN_HEIGHT, width = SCREEN_WIDTH)
@Test
fun zoomedOverflowHidden() {
val reference = getComparisonScreenshot(SCREEN_WIDTH, SCREEN_HEIGHT)
val dynamicToolbarMaxHeight = SCREEN_HEIGHT / 2
sessionRule.display?.run { setDynamicToolbarMaxHeight(dynamicToolbarMaxHeight) }
// Set active since setVerticalClipping call affects only for foreground tab.
mainSession.setActive(true)
mainSession.loadTestPath(BaseSessionTest.FIXED_BOTTOM)
mainSession.waitForPageStop()
// Change the body background color to match the reference image's background color.
mainSession.evaluateJS("document.body.style.background = 'rgb(0, 128, 0)'")
// Hide the vertical scrollbar.
mainSession.evaluateJS("document.documentElement.style.scrollbarWidth = 'none'")
// Zoom in the content so that the content's visual viewport can be scrollable.
mainSession.setResolutionAndScaleTo(10.0f)
// Simulate the dynamic toolbar being hidden by the scroll
sessionRule.display?.run { setVerticalClipping(-dynamicToolbarMaxHeight) }
mainSession.flushApzRepaints()
sessionRule.display?.let {
assertScreenshotResult(it.capturePixels(), reference)
}
}
@WithDisplay(height = SCREEN_HEIGHT, width = SCREEN_WIDTH)
@Test
fun zoomedPositionFixedRoot() {
val reference = getComparisonScreenshot(SCREEN_WIDTH, SCREEN_HEIGHT)
val dynamicToolbarMaxHeight = SCREEN_HEIGHT / 2
sessionRule.display?.run { setDynamicToolbarMaxHeight(dynamicToolbarMaxHeight) }
// Set active since setVerticalClipping call affects only for foreground tab.
mainSession.setActive(true)
mainSession.loadTestPath(BaseSessionTest.FIXED_BOTTOM)
mainSession.waitForPageStop()
// Change the body background color to match the reference image's background color.
mainSession.evaluateJS("document.body.style.background = 'rgb(0, 128, 0)'")
// Change the root `overlow` style to make it scrollable and change the position style
// to `fixed` so that the root container is not scrollable.
mainSession.evaluateJS("document.body.style.overflow = 'scroll'")
mainSession.evaluateJS("document.documentElement.style.position = 'fixed'")
// Hide the vertical scrollbar.
mainSession.evaluateJS("document.documentElement.style.scrollbarWidth = 'none'")
// Zoom in the content so that the content's visual viewport can be scrollable.
mainSession.setResolutionAndScaleTo(10.0f)
// Simulate the dynamic toolbar being hidden by the scroll
sessionRule.display?.run { setVerticalClipping(-dynamicToolbarMaxHeight) }
mainSession.flushApzRepaints()
sessionRule.display?.let {
assertScreenshotResult(it.capturePixels(), reference)
}
}
@WithDisplay(height = SCREEN_HEIGHT, width = SCREEN_WIDTH)
@Test
fun backgroundImageFixed() {
val reference = getComparisonScreenshot(SCREEN_WIDTH, SCREEN_HEIGHT)
val dynamicToolbarMaxHeight = SCREEN_HEIGHT / 2
sessionRule.display?.run { setDynamicToolbarMaxHeight(dynamicToolbarMaxHeight) }
// Set active since setVerticalClipping call affects only for forground tab.
mainSession.setActive(true)
mainSession.loadTestPath(BaseSessionTest.TOUCH_ACTION_HTML_PATH)
mainSession.waitForPageStop()
// Specify the root background-color to match the reference image color and specify
// `background-attachment: fixed`.
mainSession.evaluateJS("document.documentElement.style.background = 'linear-gradient(green, green) fixed'")
// Make the root element scrollable.
mainSession.evaluateJS("document.documentElement.style.height = '100vh'")
// Hide the vertical scrollbar.
mainSession.evaluateJS("document.documentElement.style.scrollbarWidth = 'none'")
mainSession.flushApzRepaints()
// Simulate the dynamic toolbar being hidden by the scroll
sessionRule.display?.run { setVerticalClipping(-dynamicToolbarMaxHeight) }
mainSession.flushApzRepaints()
sessionRule.display?.let {
assertScreenshotResult(it.capturePixels(), reference)
}
}
@WithDisplay(height = SCREEN_HEIGHT, width = SCREEN_WIDTH)
@Test
fun backgroundAttachmentFixed() {
val reference = getComparisonScreenshot(SCREEN_WIDTH, SCREEN_HEIGHT)
val dynamicToolbarMaxHeight = SCREEN_HEIGHT / 2
sessionRule.display?.run { setDynamicToolbarMaxHeight(dynamicToolbarMaxHeight) }
// Set active since setVerticalClipping call affects only for forground tab.
mainSession.setActive(true)
mainSession.loadTestPath(BaseSessionTest.TOUCH_ACTION_HTML_PATH)
mainSession.waitForPageStop()
// Specify the root background-color to match the reference image color and specify
// `background-attachment: fixed`.
mainSession.evaluateJS("document.documentElement.style.background = 'rgb(0, 128, 0) fixed'")
// Make the root element scrollable.
mainSession.evaluateJS("document.documentElement.style.height = '100vh'")
// Hide the vertical scrollbar.
mainSession.evaluateJS("document.documentElement.style.scrollbarWidth = 'none'")
mainSession.flushApzRepaints()
// Simulate the dynamic toolbar being hidden by the scroll
sessionRule.display?.run { setVerticalClipping(-dynamicToolbarMaxHeight) }
mainSession.flushApzRepaints()
sessionRule.display?.let {
assertScreenshotResult(it.capturePixels(), reference)
}
}
}