Skip to content
This repository was archived by the owner on Feb 5, 2021. It is now read-only.

Make ViewFactory.showRendering function responsible for applying the ComposeViewFactoryRoot. #24

Merged
merged 1 commit into from
May 18, 2020
Merged
Show file tree
Hide file tree
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
2 changes: 1 addition & 1 deletion .github/workflows/kotlin.yml
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ jobs:
name: Instrumentation tests
needs: assemble
runs-on: macos-latest
timeout-minutes: 20
timeout-minutes: 30
Copy link
Collaborator Author

@zach-klippenstein zach-klippenstein May 18, 2020

Choose a reason for hiding this comment

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

This PR adds a bunch of UI tests, so we need to increase the CI timeout a bit.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Better fix coming in #30.

strategy:
# Allow tests to continue on other devices if they fail on one device.
fail-fast: false
Expand Down
1 change: 1 addition & 0 deletions core-compose/api/core-compose.api
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ public final class com/squareup/workflow/ui/compose/ComposeViewFactoryRoot$Compa
}

public final class com/squareup/workflow/ui/compose/ComposeViewFactoryRootKt {
public static final fun <clinit> ()V
public static final fun ComposeViewFactoryRoot (Lkotlin/jvm/functions/Function2;)Lcom/squareup/workflow/ui/compose/ComposeViewFactoryRoot;
public static final fun withComposeViewFactoryRoot (Lcom/squareup/workflow/ui/ViewEnvironment;Lkotlin/jvm/functions/Function2;)Lcom/squareup/workflow/ui/ViewEnvironment;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
/*
* Copyright 2020 Square Inc.
*
* 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.squareup.workflow.ui.compose

import android.content.Context
import android.widget.FrameLayout
import androidx.compose.FrameManager
import androidx.compose.mutableStateOf
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.ui.foundation.Text
import androidx.ui.layout.Column
import androidx.ui.test.assertIsDisplayed
import androidx.ui.test.createComposeRule
import androidx.ui.test.findByText
import com.squareup.workflow.ui.ViewEnvironment
import com.squareup.workflow.ui.ViewRegistry
import com.squareup.workflow.ui.WorkflowViewStub
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith

@RunWith(AndroidJUnit4::class)
class ComposeViewFactoryTest {

@Rule @JvmField val composeRule = createComposeRule()

@Test fun wrapsFactoryWithRoot() {
val wrapperText = mutableStateOf("one")
val viewEnvironment = ViewEnvironment(ViewRegistry(TestFactory))
.withComposeViewFactoryRoot { content ->
Column {
Text(wrapperText.value)
content()
}
}

composeRule.setContent {
// This is valid Compose code, but the IDE doesn't know that yet so it will show an
// unsuppressable error.
RootView(viewEnvironment = viewEnvironment)
}

findByText("one\ntwo").assertIsDisplayed()
FrameManager.framed {
wrapperText.value = "ENO"
}
findByText("ENO\ntwo").assertIsDisplayed()
}

private class RootView(context: Context) : FrameLayout(context) {
private val stub = WorkflowViewStub(context).also(::addView)

fun setViewEnvironment(viewEnvironment: ViewEnvironment) {
stub.update(TestRendering("two"), viewEnvironment)
}
}

private data class TestRendering(val text: String)

private companion object {
val TestFactory = bindCompose<TestRendering> { rendering, _ ->
Text(rendering.text)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
/*
* Copyright 2020 Square Inc.
*
* 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.
*/
@file:Suppress("RemoveEmptyParenthesesFromAnnotationEntry")

package com.squareup.workflow.ui.compose.internal

import android.content.Context
import android.widget.FrameLayout
import androidx.compose.Composable
import androidx.compose.CompositionReference
import androidx.compose.FrameManager
import androidx.compose.Providers
import androidx.compose.Recomposer
import androidx.compose.ambientOf
import androidx.compose.compositionReference
import androidx.compose.currentComposer
import androidx.compose.mutableStateOf
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.ui.foundation.Text
import androidx.ui.test.assertIsDisplayed
import androidx.ui.test.createComposeRule
import androidx.ui.test.findByText
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith

@RunWith(AndroidJUnit4::class)
class ComposeSupportTest {

@Rule @JvmField val composeRule = createComposeRule()

@Test fun ambientsPassThroughSubcomposition() {
composeRule.setContent {
TestComposable("foo")
}

findByText("foo").assertIsDisplayed()
}

@Test fun ambientChangesPassThroughSubcomposition() {
val ambientValue = mutableStateOf("foo")
composeRule.setContent {
TestComposable(ambientValue.value)
}

findByText("foo").assertIsDisplayed()
FrameManager.framed {
ambientValue.value = "bar"
}
findByText("bar").assertIsDisplayed()
}

@Composable private fun TestComposable(ambientValue: String) {
Providers(TestAmbient provides ambientValue) {
LegacyHostComposable {
Text(TestAmbient.current)
}
}
}

@Composable private fun LegacyHostComposable(leafContent: @Composable() () -> Unit) {
val wormhole = Wormhole(currentComposer.recomposer, compositionReference(), leafContent)
// This is valid Compose code, but the IDE doesn't know that yet so it will show an
// unsuppressable error.
WormholeView(wormhole = wormhole)
}

private class Wormhole(
val recomposer: Recomposer,
val parentReference: CompositionReference,
val childContent: @Composable() () -> Unit
)

private class WormholeView(context: Context) : FrameLayout(context) {
fun setWormhole(wormhole: Wormhole) {
setContent(wormhole.recomposer, wormhole.parentReference, wormhole.childContent)
}
}

private companion object {
val TestAmbient = ambientOf<String> { error("Ambient not provided") }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
/*
* Copyright 2020 Square Inc.
*
* 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.squareup.workflow.ui.compose.internal

import androidx.compose.FrameManager
import androidx.compose.mutableStateOf
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.ui.foundation.Text
import androidx.ui.layout.Column
import androidx.ui.test.assertIsDisplayed
import androidx.ui.test.createComposeRule
import androidx.ui.test.findByText
import com.squareup.workflow.ui.ViewEnvironment
import com.squareup.workflow.ui.ViewRegistry
import com.squareup.workflow.ui.compose.withComposeViewFactoryRoot
import com.squareup.workflow.ui.compose.wrapWithRootIfNecessary
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith

@RunWith(AndroidJUnit4::class)
class ComposeViewFactoryRootTest {

@Rule @JvmField val composeRule = createComposeRule()

@Test fun wrapWithRootIfNecessary_handlesNoRoot() {
val viewEnvironment = ViewEnvironment(ViewRegistry())

composeRule.setContent {
wrapWithRootIfNecessary(viewEnvironment) {
Text("foo")
}
}

findByText("foo").assertIsDisplayed()
}

@Test fun wrapWithRootIfNecessary_wrapsWhenNecessary() {
val viewEnvironment = ViewEnvironment(ViewRegistry())
.withComposeViewFactoryRoot { content ->
Column {
Text("one")
content()
}
}

composeRule.setContent {
wrapWithRootIfNecessary(viewEnvironment) {
Text("two")
}
}

findByText("one\ntwo").assertIsDisplayed()
}

@Test fun wrapWithRootIfNecessary_onlyWrapsOnce() {
val viewEnvironment = ViewEnvironment(ViewRegistry())
.withComposeViewFactoryRoot { content ->
Column {
Text("one")
content()
}
}

composeRule.setContent {
wrapWithRootIfNecessary(viewEnvironment) {
Text("two")
wrapWithRootIfNecessary(viewEnvironment) {
Text("three")
}
}
}

findByText("one\ntwo\nthree").assertIsDisplayed()
}

@Test fun wrapWithRootIfNecessary_seesUpdatesFromRootWrapper() {
val wrapperText = mutableStateOf("one")
val viewEnvironment = ViewEnvironment(ViewRegistry())
.withComposeViewFactoryRoot { content ->
Column {
Text(wrapperText.value)
content()
}
}

composeRule.setContent {
wrapWithRootIfNecessary(viewEnvironment) {
Text("two")
}
}

findByText("one\ntwo").assertIsDisplayed()
FrameManager.framed {
wrapperText.value = "ENO"
}
findByText("ENO\ntwo").assertIsDisplayed()
}

@Test fun wrapWithRootIfNecessary_rewrapsWhenDifferentRoot() {
val viewEnvironment1 = ViewEnvironment(ViewRegistry())
.withComposeViewFactoryRoot { content ->
Column {
Text("one")
content()
}
}
val viewEnvironment2 = ViewEnvironment(ViewRegistry())
.withComposeViewFactoryRoot { content ->
Column {
Text("ENO")
content()
}
}
val viewEnvironment = mutableStateOf(viewEnvironment1)

composeRule.setContent {
wrapWithRootIfNecessary(viewEnvironment.value) {
Text("two")
}
}

findByText("one\ntwo").assertIsDisplayed()
FrameManager.framed {
viewEnvironment.value = viewEnvironment2
}
findByText("ENO\ntwo").assertIsDisplayed()
}
}
Loading