Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
86 changes: 86 additions & 0 deletions android/app/src/androidTest/java/com/testapp/IsolatedTest.kt
Original file line number Diff line number Diff line change
@@ -1,15 +1,34 @@
package com.rnstorybookautoscreenshots

import android.Manifest
import android.graphics.PixelFormat
import android.os.Handler
import android.os.Looper
import android.view.Choreographer
import android.view.ContextThemeWrapper
import android.view.View
import android.view.ViewGroup
import android.view.WindowManager
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.rule.GrantPermissionRule
import com.facebook.testing.screenshot.Screenshot
import com.testapp.MainApplication
import junit.framework.TestCase.assertNotNull
import junit.framework.TestCase.assertTrue
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import java.util.concurrent.CountDownLatch

@RunWith(AndroidJUnit4::class)
class IsolatedTest {

@get:Rule
val permissionRule: GrantPermissionRule = GrantPermissionRule.grant(
Manifest.permission.SYSTEM_ALERT_WINDOW
)

@Test
fun simpleTest() {
assertTrue(true)
Expand All @@ -23,4 +42,71 @@ class IsolatedTest {
surface.start()
assertNotNull(surface.view)
}

@Test
fun screenshotSimpleTestComponent() {
val instrumentation = InstrumentationRegistry.getInstrumentation()
val context = instrumentation.targetContext
val app = context.applicationContext as MainApplication

val themedContext = ContextThemeWrapper(context, context.applicationInfo.theme)
val surface = app.reactHost.createSurface(themedContext, "SimpleTestComponent", null)
val view = surface.view
?: throw IllegalStateException("ReactSurface returned a null view")

val wm = context.getSystemService(android.content.Context.WINDOW_SERVICE) as WindowManager
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you're going in circles, I think we specifically said we didn't want to do it using WindowManager.

val params = WindowManager.LayoutParams(
1080,
1920,
WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
PixelFormat.TRANSLUCENT
).apply { alpha = 0f }

instrumentation.runOnMainSync {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we also specifically said we wanted to do everything without inter-thread synchronization. There can be one runOnMainSync, but all the code needs to be in it.

view.setLayerType(View.LAYER_TYPE_SOFTWARE, null)
wm.addView(view, params)
surface.start()
}

// Wait for JS to load and Fabric to mount children (up to 30s for cold start)
val deadline = System.currentTimeMillis() + 30_000L
while (System.currentTimeMillis() < deadline) {
waitTwoFrames()
var childCount = 0
instrumentation.runOnMainSync { childCount = view.childCount }
if (childCount > 0) break
}

instrumentation.runOnMainSync {
// Child views added by Fabric default to hardware acceleration;
// set software layer recursively so Screenshot.snap() can capture them.
setLayerTypeSoftwareRecursively(view)
Screenshot.snap(view).setName("simple_test_component").record()
}

instrumentation.runOnMainSync {
surface.stop()
wm.removeView(view)
}
}

private fun setLayerTypeSoftwareRecursively(view: View) {
view.setLayerType(View.LAYER_TYPE_SOFTWARE, null)
if (view is ViewGroup) {
for (i in 0 until view.childCount) {
setLayerTypeSoftwareRecursively(view.getChildAt(i))
}
}
}

private fun waitTwoFrames() {
repeat(2) {
val latch = CountDownLatch(1)
Handler(Looper.getMainLooper()).post {
Choreographer.getInstance().postFrameCallback { latch.countDown() }
}
latch.await()
}
}
}
Loading