Giter Club home page Giter Club logo

kakao's People

Contributors

aafanasev avatar ahampe avatar anton46 avatar artbrnv avatar ashdavies avatar btwarog avatar cdsap avatar disssection avatar dsvoronin avatar even-even avatar ghostbuster91 avatar hongwei-bai avatar jkaan avatar judrummer avatar k-kagurazaka avatar keineantwort avatar malinskiy avatar michaelbukachi avatar ming13 avatar nikitae57 avatar peerapon01 avatar psh avatar sashkir7 avatar sebastienrouif avatar undroid-dew avatar unlimity avatar v-rodionov avatar vacxe avatar verachadw avatar yapkolotilov avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

kakao's Issues

Add ability to get Matcher of KBaseView

Relevant Code:

  private val header = KTextView { withText(R.string.text) }
  private val recyclerView = KRecyclerView({ withId(R.id.recyclerView) }, itemTypeBuilder = {})


  fun scrollToTextView() {
     // I want to do something like this
     recyclerView.scrollTo(header.getViewMatchers()) 
     // Potentially to have an extension func to do this 
     recyclerView.scrollTo(header)
     // As it currently stands, it seems like I have to redefine my matchers.
     recyclerView.scrollTo { withText(R.string.text) } 
  }

In short I would like access to the matchers all KBaseView was initialized with.

hasDrawable not working with imageview as excpected

Hi Team,

First thanks for this great UI Testing framework.
Currently i am trying to check drawable with imageview but i am getting below error, as follows,

androidx.test.espresso.base.DefaultFailureHandler$AssertionFailedWithCauseError: 'with drawable id -1 or provided instance' doesn't match the selected view.
Expected: with drawable id -1 or provided instance
Got: "AppCompatImageView{id=2131230883, res-name=factImage, visibility=VISIBLE, width=180, height=220, has-focus=false, has-focusable=false, has-window-focus=true, is-clickable=false, is-enabled=true, is-focused=false, is-focusable=false, is-layout-requested=false, is-selected=false, layout-params=androidx.constraintlayout.widget.ConstraintLayout$LayoutParams@df6814a, tag=null, root-is-layout-requested=false, has-input-connection=false, x=508.0, y=65.0}"

Repository: https://github.com/TheReprator/Wipro/blob/image_error/appModules/factList/src/androidTest/kotlin/reprator/wipro/factlist/test/FactListKaspressoTest.kt

Test Name: imageViewDrawableComparison

Looking forward for a solution.

Regards,
Vikram Singh

WebView `withElement(Locator.LINK_TEXT, <>)` incorrect behavior

Steps to reproduce:

  1. Unmute
 withElement(Locator.LINK_TEXT, "My Home") {
                    click()
                }

in WebTest

Same as:

onWebView().withElement(findElement(Locator.LINK_TEXT, "My Home")).perform(webClick())

WebView content:

val webView = findViewById<WebView>(R.id.webView).apply {
            settings.javaScriptEnabled = true
        }
        webView.loadData(
            """
            <html>
            <body>

            <p id="text">Hello</p>
            <a href="#">My Home</a>

            </body>
            </html>
        """, "text/html", "utf-8"
        )

Observed Results:


java.lang.RuntimeException: java.lang.RuntimeException: Atom evaluation returned null!
	at androidx.test.espresso.web.sugar.Web$WebInteraction$ExceptionPropagator.<init>(Web.java:4)
	at androidx.test.espresso.web.sugar.Web$WebInteraction.doEval(Web.java:31)
	at androidx.test.espresso.web.sugar.Web$WebInteraction.withElement(Web.java:46)
	at io.github.kakaocup.kakao.delegate.WebInteractionDelegate.withElement(WebInteractionDelegate.kt:58)
	at io.github.kakaocup.kakao.web.WebActions$DefaultImpls.perform(WebActions.kt:55)
	at io.github.kakaocup.kakao.web.WebActions$DefaultImpls.click(WebActions.kt:23)
	at io.github.kakaocup.kakao.web.WebElementBuilder$KWebInteraction.click(WebElementBuilder.kt:34)
	at io.github.kakaocup.sample.WebTest$testWebViewHasTextHelloAndClickLink$1$1$2.invoke(WebTest.kt:27)

Expected Results:

  • Test should pass

Information

  • Only on API 29 +

Prepare 3.0.0

TODO:

  • Update package
  • Remove Code of conduct
  • Update version to 3.0.0
  • Remove Jcenter related code
  • Request maven group ID
  • Review Readme file
  • Add migration script or guide
  • Configure CI checks
  • Update libs
  • ...

Crash when app is using Firebase Performance library.

Steps to reproduce:

  1. Use firebase performance library
  2. Use Kakao 3.2.3

Observed Results:

  • App crashes when running test with a Firebase perf error, there seems to be some dependency clash maybe?
java.lang.NoSuchMethodError: No static method registerDefaultInstance(Ljava/lang/Class;Lcom/google/protobuf/GeneratedMessageLite;)V in class Lcom/google/protobuf/GeneratedMessageLite; or its super classes (declaration of 'com.google.protobuf.GeneratedMessageLite' appears in /data/app/~~4sKIYTcsfYQtc9JM7hBNvA==/me.mydomain.myapp.test-Im1U7Kqsm8hvB13qvO5VGA==/base.apk)
at com.google.firebase.perf.v1.ApplicationInfo.<clinit>(ApplicationInfo.java:1085)
at com.google.firebase.perf.v1.ApplicationInfo.newBuilder(ApplicationInfo.java:533)
at com.google.firebase.perf.transport.TransportManager.finishInitialization(TransportManager.java:227)
at com.google.firebase.perf.transport.TransportManager.syncInit(TransportManager.java:221)
at com.google.firebase.perf.transport.TransportManager.$r8$lambda$LuAwHBxy50Yf-ziHqcD54KjEPtk(Unknown Source:0)
at com.google.firebase.perf.transport.TransportManager$$ExternalSyntheticLambda1.run(Unknown Source:2)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
at java.lang.Thread.run(Thread.java:920)

This Fix works for me:
Exclude protobuf-lite dependency from Kakao

    androidTestImplementation ("io.github.kakaocup:kakao:3.2.3")
    {
        exclude module: "protobuf-lite"
    }

Tests with KViewPager/KViewPager2 SwipeableActions methods are flaky

Steps to reproduce:

(It's hard to reproduce. We are having like 1 flaky test in 10k runs)

  1. Use any of SwipeableActions interface methods on ViewPager/ViewPager2
  2. Assert that child of ViewPager is completely displayed.

Observed Results:

Current page of ViewPager is not fully settled after end of SwipeableActions

Expected Results:

Assertions can be made immediately after swipe action

Relevant Code:

viewPager.swipeLeft()
someChild.isCompletelyDisplayed()

PR with reproduction on Sample App

--

KTextInputLayout's hasHint() and hasError() doesn't work with Spannable

Since TextInputLayout.hint and TextInputLayout.error are CharSequence, the KTextInputLayout.hasHint() and KTextInputLayout.hasError() checks may fail if we assign a Spannable instance to this attributes.

Relevant Code:

   fun hasHint(hint: String) {
       view.check(ViewAssertion { view, notFoundException ->
           if (view is TextInputLayout) {
              if (hint != view.hint) {

KRecyclerView: RecyclerActions::scrollToEnd() throws NullPointerException

Steps to reproduce:

  1. Use long-loading RecyclerView items in a standard RecyclerView
  2. call scrollToEnd() in a UI test

Observed Results:

The code

                    val lastView = view.findViewHolderForLayoutPosition(position)!!.itemView
                    view.scrollBy(0, lastView.height)

throws a NullPointerException because lastView is null.

Expected Results:

I expected the RecyclerView just to scroll down to have the last item fully visible.

Relevant Code:

  override fun scrollToEnd() {
      view.perform(object : ViewAction {
          override fun getDescription() = "Scroll RecyclerView to the bottom"

          override fun getConstraints() = ViewMatchers.isAssignableFrom(RecyclerView::class.java)

          override fun perform(controller: UiController, view: View) {
              if (view is RecyclerView) {
                  val position = view.adapter!!.itemCount - 1
                  view.scrollToPosition(position)
                  controller.loopMainThreadUntilIdle()
                  val lastView = view.findViewHolderForLayoutPosition(position)!!.itemView
                  view.scrollBy(0, lastView.height)
                  controller.loopMainThreadUntilIdle()
              }
          }
      })
  }

Workaround:

I've created an extension function to still be able to do what I'd like to do:

fun KRecyclerView.scrollToEndRepeatedly(repetitions: Int) {

    view.perform(
        object : ViewAction {
            override fun getDescription() =
                "Scroll RecyclerView to the bottom"

            override fun getConstraints() =
                ViewMatchers.isAssignableFrom(
                    RecyclerView::class.java
                )

            override fun perform(controller: UiController, view: View) {
                if (view is RecyclerView) {
                    var lastViewFound = false
                    var tryCount = 0
                    do {
                        tryCount++
                        val position = view.adapter!!.itemCount - 1
                        view.scrollToPosition(position)
                        controller.loopMainThreadUntilIdle()
                        val lastView =
                            view.findViewHolderForLayoutPosition(
                                position
                            )
                        lastView?.let {
                            view.scrollBy(0, lastView.itemView.height)
                            lastViewFound = false
                        }
                        controller.loopMainThreadUntilIdle()
                    } while ((!lastViewFound) && (tryCount < repetitions))
                }
            }

        }
    )
}

While this does what it's supposed to do, I think, there must be a better solution using interceptors which fit's more naturally into Kakaos concepts, i.e. making the repetions parameter superfluous.

android.support.test.espresso.PerformException: Error performing 'fast swipe'

The test is passing 90 percent of the time but failing randomly with below exception, as follows,

android.support.test.espresso.PerformException: Error performing 'fast swipe'
on view 'with id: my.app.package:id/my_refresh_layout'.
...
Caused by: java.lang.RuntimeException: Action will not be performed because the target view
does not match one or more of the following constraints:
at least 90 percent of the view's area is displayed to the user.
Target view: "SwipeRefreshLayout{id=2131689751, res-name=my_refresh_layout, visibility=VISIBLE,
width=480, height=672, has-focus=false, has-focusable=true, has-window-focus=true,
is-clickable=false, is-enabled=true, is-focused=false, is-focusable=false, is-layout-requested=false,
is-selected=false, root-is-layout-requested=false, has-input-connection=false, x=0.0, y=0.0,
child-count=2}"

Repository: https://github.com/TheReprator/Wipro/blob/image_error/appModules/factList/src/androidTest/kotlin/reprator/wipro/factlist/test/FactListFragmentTest.kt

TestName: swipeToRefresh

Regards,
Vikram Singh

KImageView -> hasDrawable doesn't work with SVG images

Steps to reproduce:

  1. Import a SVG image in Android Studio
  2. Try to use hasDrawable with the SVG set.

Observed Results:

The images are considered different although they are the same

Expected Results:

The images should be the same

Relevant Code:

withId<KImageView>(R.id.criticalErrorImage) {
    hasDrawable(R.drawable.error_graphic)
}

I've tried also to create a CustomMatcher trying to compare the constantState but it fails anyway unfortunately.

Implement more extensions to access android resources

Steps to reproduce:

  1. Open ContextUtils.kt
  2. Observe only 3 ext methods
  3. Call Vacxe

Expected Results:

  • What did you expect to happen?

I would like to have more extension to use because right now the codebase is not consistent.
We use the ext method getResourceString to access the string, but if we would like to access the string with args or a quantity string, we have to either have our own ext or invoke InstrumentationRegistry directly.

Relevant Code:

fun getQuantityString(@PluralsRes resId: Int, quantity: Int) = 
 InstrumentationRegistry.getInstrumentation().targetContext.resources.getQuantityString(
              resId,
              quantity,
          )
fun getResourceString(@StringRes resId: Int, vararg args: Any) = 
  InstrumentationRegistry.getInstrumentation().targetContext.resources.getString(
              resId,
              args,
          ) 

PR with reproduction on Sample App

  • See: #

Autocomplete Test API 29

Master:

io.github.kakaocup.sample.AutoCompleteTest > testContentItemsListView[emulator(AVD) - 10] FAILED 
	androidx.test.espresso.NoMatchingRootException: Matcher '(with decor view of type PopupWindow$PopupViewContainer)' did not match any of the following roots: [Root{application-window-token=android.view.ViewRootImpl$W@df914cb, window-token=android.view.ViewRootImpl$W@df914cb, has-window-focus=true, layout-params-type=1, layout-params-string={(0,0)(fillxfill) ty=BASE_APPLICATION wanim=0x10302fe
	fl=LAYOUT_IN_SCREEN LAYOUT_INSET_DECOR SPLIT_TOUCH HARDWARE_ACCELERATED DRAWS_SYSTEM_BAR_BACKGROUNDS

Reproduced only on 29 API

io.github.kakaocup.kakao.common.actions.BaseActions#scrollTo doesn't support recycler view

Steps to reproduce:

1.Edit io.github.kakaocup.sample.NestedRecyclerTest#testSingleItemTypeRecyclerView like that:
image

Observed Results:

java.lang.RuntimeException: Action will not be performed because the target view does not match one or more of the following constraints:
(view has effective visibility <VISIBLE> and is descendant of a view matching (is assignable from class <class android.widget.ScrollView> or is assignable from class <class android.widget.HorizontalScrollView> or is assignable from class <class androidx.core.widget.NestedScrollView> or is assignable from class <class android.widget.ListView>))

Expected Results:

Test doesn't fail

Question on the use of the Awaitility library with Kakao

Hi,

This a question, rather than a bug. Is there some way I could extend Kakao (+Kakao Compose) to implement the Awaitility library ? At the moment, I need to add code like this below which pollutes my test code somewhat. I'd like to hide the await.atMost statements from the test code.

The app I'm testing needs a hybrid of Espresso, Compose and UIAutomator and I use different Page Object implementations depending on the view I'm automating.

       await.atMost(TEN_SECONDS).untilAsserted {
            filesCheckedTitle {
                assertIsDisplayed()
                assertTextEquals(title.value)
            }
        }

Thanks,

Pentti

checking the initial state of app with progressbar

Hi Team,

Actually i am trying to test the initial state of app, where app is in loading state but whenever i run the test in throws me following error, as follows,

Exception:
junit.framework.AssertionFailedError: 'is displayed on the screen to the user' doesn't match the selected view.
Expected: is displayed on the screen to the user
Got: "ProgressBar{id=2131230934, res-name=lee_progress, visibility=GONE, width=96, height=96, has-focus=false, has-focusable=false, has-window-focus=true, is-clickable=false, is-enabled=true, is-focused=false, is-focusable=false, is-layout-requested=true, is-selected=false, layout-params=android.widget.FrameLayout$LayoutParams@f29348d, tag=null, root-is-layout-requested=false, has-input-connection=false, x=312.0, y=676.0}"

at dalvik.system.VMStack.getThreadStackTrace(Native Method)
at java.lang.Thread.getStackTrace(Thread.java:1720)
at com.kaspersky.kaspresso.failure.FailureLoggingProviderImpl.describedWith(FailureLoggingProviderImpl.kt:91)
at com.kaspersky.kaspresso.failure.FailureLoggingProviderImpl.logDescriptionAndThrow(FailureLoggingProviderImpl.kt:69)
at com.kaspersky.kaspresso.failure.LoggingFailureHandler.logDescriptionAndThrow(Unknown Source:2)
at com.kaspersky.kaspresso.failure.LoggingFailureHandler.handle(LoggingFailureHandler.kt:21)
at androidx.test.espresso.ViewInteraction.waitForAndHandleInteractionResults(ViewInteraction.java:103)
at androidx.test.espresso.ViewInteraction.check(ViewInteraction.java:31)
at com.kaspersky.kaspresso.interceptors.tolibrary.kakao.KakaoViewInterceptor$interceptCheck$1.invoke(KakaoViewInterceptor.kt:29)
at com.kaspersky.kaspresso.interceptors.tolibrary.kakao.KakaoViewInterceptor$interceptCheck$1.invoke(KakaoViewInterceptor.kt:16)
at com.kaspersky.kaspresso.autoscroll.AutoScrollProviderImpl.withAutoScroll(AutoScrollProviderImpl.kt:30)
at com.kaspersky.kaspresso.interceptors.behavior.impl.autoscroll.AutoScrollViewBehaviorInterceptor.withAutoScroll(Unknown Source:12)
at com.kaspersky.kaspresso.interceptors.behavior.impl.autoscroll.AutoScrollViewBehaviorInterceptor.intercept(AutoScrollViewBehaviorInterceptor.kt:26)
at com.kaspersky.kaspresso.interceptors.behavior.impl.autoscroll.AutoScrollViewBehaviorInterceptor.intercept(AutoScrollViewBehaviorInterceptor.kt:14)
at com.kaspersky.kaspresso.interceptors.tolibrary.kakao.KakaoViewInterceptor$interceptCheck$$inlined$fold$lambda$1.invoke(KakaoViewInterceptor.kt:34)
at com.kaspersky.kaspresso.interceptors.tolibrary.kakao.KakaoViewInterceptor$interceptCheck$$inlined$fold$lambda$1.invoke(KakaoViewInterceptor.kt:16)
at com.kaspersky.kaspresso.systemsafety.SystemDialogSafetyProviderImpl.passSystemDialogs(SystemDialogSafetyProviderImpl.kt:51)
at com.kaspersky.kaspresso.interceptors.behavior.impl.systemsafety.SystemDialogSafetyViewBehaviorInterceptor.passSystemDialogs(Unknown Source:7)
at com.kaspersky.kaspresso.interceptors.behavior.impl.systemsafety.SystemDialogSafetyViewBehaviorInterceptor.intercept(SystemDialogSafetyViewBehaviorInterceptor.kt:28)
at com.kaspersky.kaspresso.interceptors.behavior.impl.systemsafety.SystemDialogSafetyViewBehaviorInterceptor.intercept(SystemDialogSafetyViewBehaviorInterceptor.kt:15)
at com.kaspersky.kaspresso.interceptors.tolibrary.kakao.KakaoViewInterceptor$interceptCheck$$inlined$fold$lambda$1.invoke(KakaoViewInterceptor.kt:34)
at com.kaspersky.kaspresso.interceptors.tolibrary.kakao.KakaoViewInterceptor$interceptCheck$$inlined$fold$lambda$1.invoke(KakaoViewInterceptor.kt:16)
at com.kaspersky.kaspresso.flakysafety.algorithm.FlakySafetyAlgorithm.invokeFlakySafely(FlakySafetyAlgorithm.kt:32)
at com.kaspersky.kaspresso.flakysafety.algorithm.FlakySafetyAlgorithm.invokeFlakySafely$default(FlakySafetyAlgorithm.kt:24)
at com.kaspersky.kaspresso.flakysafety.FlakySafetyProviderSimpleImpl.flakySafely(FlakySafetyProviderSimpleImpl.kt:27)
at com.kaspersky.kaspresso.interceptors.behavior.impl.flakysafety.FlakySafeViewBehaviorInterceptor.flakySafely(Unknown Source:7)
at com.kaspersky.kaspresso.interceptors.behavior.impl.flakysafety.FlakySafeViewBehaviorInterceptor.intercept(FlakySafeViewBehaviorInterceptor.kt:26)
at com.kaspersky.kaspresso.interceptors.behavior.impl.flakysafety.FlakySafeViewBehaviorInterceptor.intercept(FlakySafeViewBehaviorInterceptor.kt:14)
at com.kaspersky.kaspresso.interceptors.tolibrary.kakao.KakaoViewInterceptor$interceptCheck$$inlined$fold$lambda$1.invoke(KakaoViewInterceptor.kt:34)
at com.kaspersky.kaspresso.interceptors.tolibrary.kakao.KakaoViewInterceptor$interceptCheck$$inlined$fold$lambda$1.invoke(KakaoViewInterceptor.kt:16)
at com.kaspersky.kaspresso.interceptors.tolibrary.kakao.KakaoViewInterceptor.interceptCheck(KakaoViewInterceptor.kt:36)
at com.kaspersky.kaspresso.interceptors.tolibrary.LibraryInterceptorsInjector$injectKaspressoInKakao$1$1$1.invoke(LibraryInterceptorsInjector.kt:54)
at com.kaspersky.kaspresso.interceptors.tolibrary.LibraryInterceptorsInjector$injectKaspressoInKakao$1$1$1.invoke(LibraryInterceptorsInjector.kt:22)
at io.github.kakaocup.kakao.delegate.Delegate$DefaultImpls.interceptOnCheck(Delegate.kt:61)
at io.github.kakaocup.kakao.delegate.Delegate$DefaultImpls.access$interceptOnCheck(Delegate.kt:13)
at io.github.kakaocup.kakao.delegate.Delegate$interceptCheck$1.invoke(Delegate.kt:28)
at io.github.kakaocup.kakao.delegate.Delegate$DefaultImpls.interceptCheck(Delegate.kt:33)
at io.github.kakaocup.kakao.delegate.ViewInteractionDelegate.interceptCheck(ViewInteractionDelegate.kt:21)
at io.github.kakaocup.kakao.delegate.ViewInteractionDelegate.interceptCheck(ViewInteractionDelegate.kt:21)
at io.github.kakaocup.kakao.delegate.ViewInteractionDelegate.check(ViewInteractionDelegate.kt:26)
at io.github.kakaocup.kakao.common.assertions.BaseAssertions$DefaultImpls.isDisplayed(BaseAssertions.kt:36)
at io.github.kakaocup.kakao.common.views.KBaseView.isDisplayed(KBaseView.kt:34)
at reprator.wipro.factlist.test.FactListKaspressoTest$initialScreenTest$3$1$1$1.invoke(FactListKaspressoTest.kt:62)
at reprator.wipro.factlist.test.FactListKaspressoTest$initialScreenTest$3$1$1$1.invoke(FactListKaspressoTest.kt:20)
at io.github.kakaocup.kakao.common.views.KBaseView.invoke(KBaseView.kt:83)
at reprator.wipro.factlist.test.FactListKaspressoTest$initialScreenTest$3$1$1.invoke(FactListKaspressoTest.kt:61)
at reprator.wipro.factlist.test.FactListKaspressoTest$initialScreenTest$3$1$1.invoke(FactListKaspressoTest.kt:20)
at io.github.kakaocup.kakao.screen.Screen.invoke(Screen.kt:119)
at reprator.wipro.factlist.test.FactListKaspressoTest$initialScreenTest$3$1.invoke(FactListKaspressoTest.kt:59)
at reprator.wipro.factlist.test.FactListKaspressoTest$initialScreenTest$3$1.invoke(FactListKaspressoTest.kt:20)
at com.kaspersky.kaspresso.testcases.core.testcontext.TestContext.step(TestContext.kt:39)
at reprator.wipro.factlist.test.FactListKaspressoTest$initialScreenTest$3.invoke(FactListKaspressoTest.kt:56)
at reprator.wipro.factlist.test.FactListKaspressoTest$initialScreenTest$3.invoke(FactListKaspressoTest.kt:20)
at com.kaspersky.kaspresso.testcases.core.TestRunner.runMainTestSection(TestRunner.kt:144)
at com.kaspersky.kaspresso.testcases.core.TestRunner.run(TestRunner.kt:58)
at com.kaspersky.kaspresso.testcases.core.sections.MainTestSection.run(MainTestSection.kt:29)
at reprator.wipro.factlist.test.FactListKaspressoTest.initialScreenTest(FactListKaspressoTest.kt:55)
at java.lang.reflect.Method.invoke(Native Method)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:59)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:56)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at androidx.test.internal.runner.junit4.statement.RunBefores.evaluate(RunBefores.java:80)
at androidx.test.internal.runner.junit4.statement.RunAfters.evaluate(RunAfters.java:61)
at dagger.hilt.android.internal.testing.MarkThatRulesRanRule$1.evaluate(MarkThatRulesRanRule.java:106)
at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
at org.junit.runners.BlockJUnit4ClassRunner$1.evaluate(BlockJUnit4ClassRunner.java:100)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:366)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:103)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:63)
at org.junit.runners.ParentRunner$4.run(ParentRunner.java:331)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:79)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:329)
at org.junit.runners.ParentRunner.access$100(ParentRunner.java:66)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:293)
at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
at org.junit.runners.ParentRunner.run(ParentRunner.java:413)
at org.junit.runners.Suite.runChild(Suite.java:128)
at org.junit.runners.Suite.runChild(Suite.java:27)
at org.junit.runners.ParentRunner$4.run(ParentRunner.java:331)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:79)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:329)
at org.junit.runners.ParentRunner.access$100(ParentRunner.java:66)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:293)
at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
at org.junit.runners.ParentRunner.run(ParentRunner.java:413)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at org.junit.runner.JUnitCore.run(JUnitCore.java:115)
at androidx.test.internal.runner.TestExecutor.execute(TestExecutor.java:56)
at androidx.test.runner.AndroidJUnitRunner.onStart(AndroidJUnitRunner.java:395)
at android.app.Instrumentation$InstrumentationThread.run(Instrumentation.java:2198)

Caused by: junit.framework.AssertionFailedError: 'is displayed on the screen to the user' doesn't match the selected view.
Expected: is displayed on the screen to the user
Got: "ProgressBar{id=2131230934, res-name=lee_progress, visibility=GONE, width=96, height=96, has-focus=false, has-focusable=false, has-window-focus=true, is-clickable=false, is-enabled=true, is-focused=false, is-focusable=false, is-layout-requested=true, is-selected=false, layout-params=android.widget.FrameLayout$LayoutParams@f29348d, tag=null, root-is-layout-requested=false, has-input-connection=false, x=312.0, y=676.0}"

at androidx.test.espresso.matcher.ViewMatchers.assertThat(ViewMatchers.java:17)
at androidx.test.espresso.assertion.ViewAssertions$MatchesViewAssertion.check(ViewAssertions.java:15)
at com.kaspersky.kaspresso.proxy.ViewAssertionProxy.check(ViewAssertionProxy.kt:26)
at androidx.test.espresso.ViewInteraction$SingleExecutionViewAssertion.check(ViewInteraction.java:10)
at androidx.test.espresso.ViewInteraction$2.call(ViewInteraction.java:11)
at androidx.test.espresso.ViewInteraction$2.call(ViewInteraction.java:2)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at android.os.Handler.handleCallback(Handler.java:914)
at android.os.Handler.dispatchMessage(Handler.java:100)
at android.os.Looper.loop(Looper.java:225)
at android.app.ActivityThread.main(ActivityThread.java:7563)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:539)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:994)

Repository:
https://github.com/TheReprator/Wipro/blob/initial_state_test/appModules/factList/src/androidTest/kotlin/reprator/wipro/factlist/test/FactListKaspressoTest.kt

Test Name: initialScreenTest

Kindly assist.

Regards,
Vikram Singh

hasText not checking escaped "%" character

Steps to reproduce:

  1. Set the text on a TextView to have an escaped %. Ex. <string name="recording_scaling">Scaling (%%)</string>
  2. call hasText on the KTextView with the resource id

Observed Results:

Expected: with string from resource id: <2131821564>[recording_scaling] value: Scaling (%%)
     Got: "AppCompatTextView{id=2131362820, res-name=titleTextView, visibility=VISIBLE, width=220, height=48, has-focus=false, has-focusable=false, has-window-focus=true, is-clickable=false, is-enabled=true, is-focused=false, is-focusable=false, is-layout-requested=false, is-selected=false, layout-params=androidx.constraintlayout.widget.ConstraintLayout$LayoutParams@e829a5e, tag=null, root-is-layout-requested=false, has-input-connection=false, x=16.0, y=0.0, text=Scaling (%), input-type=0, ime-target=false, has-links=false}"

Expected Results:

  • The checked should have succeeded

Relevant Code:

<string name="recording_scaling">Scaling (%%)</string>
title.hasText(R.string.recording_scaling) // Fails

Implement non-caching text matcher

Steps to reproduce:

  1. Define a KTextView using the withText(resId: Int) matcher and preform a check on it
  2. Change locale mid-test (using kaspresso SystemLanguage)
  3. Perform a check on the same KTextView again

Observed Results:

  • View not found

Expected Results:

  • View found

Relevant Code:

androidx.test.espresso.matcher.ViewMatchers.WithCharSequenceMatcher#matchesSafely caches text

@Override
  protected boolean matchesSafely(TextView textView, Description mismatchDescription) {
    if (null == expectedText) {
      try {
        expectedText = textView.getResources().getString(resourceId);
      } catch (Resources.NotFoundException ignored) {
        /* view could be from a context unaware of the resource id. */
      }
      resourceName = safeGetResourceEntryName(textView.getResources(), resourceId);
    }
    ...
  }

KTextInputLayout > hasError for null values

Steps to reproduce:

class UrlValidationScreen : Screen<UrlValidationScreen>() {
    val urlInputLayout = KTextInputLayout { withId(R.id.urlInputLayout) }
}

 // Assert
urlInputLayout {
    hasError(null) // Will not work
}

Observed Results:

Null cannot be passed to hasText
image

Expected Results:

The hasText(String) function allows null values.

Relevant Code:

See above

TextInputLayoutAssertions methods has false-positive behaviour

Steps to reproduce:

  1. Create an XML file with TextInputEditText and add tags for TextInputEditText ( tagTextInputEditText) and for EditText inside the TextInputEditText (tagEditText). add to Hint any text (e.x. "hint")
  2. create a UI test running the activity with this XML.
  3. add this code KTextInputLayout { withTag(tagEditText) }.hasHint("any text _"+ UUID.randomUUID())

Observed Results:

The Test will pass

Expected Result

The Test should fail!
Expected an AssertionError that EditText is not TextInputLayout

Relevant Code:

The problem is that methods check instances and do not throw an exception if it is different than expected

fun hasHint(hint: String) {
      view.check(ViewAssertion { view, notFoundException ->
          if (view is TextInputLayout) {
              if (hint != view.hint.toString()) {
                  throw AssertionError(
                      "Expected hint is $hint," +
                              " but actual is ${view.hint}"
                  )
              }
          } else {
              notFoundException?.let { throw AssertionError(it) } // here code should throw AssertionError("expected TextInputLayout, but got $view") if notFoundException is null
          }
      })
// code here

childAt api is not working in recyclerview test

Hi Team,

whenever i am checking for particular position in a recyclerview, it's throwing me below error, as follows,

Exception:
androidx.test.espresso.NoMatchingViewException: No views in hierarchy found matching: (is descendant of a: (view holder at 7 position of recycler view: (with id is reprator.wipro.factlist.test:id/factListRecyclerView)) and with id is <2131230883>)

View Hierarchy:
+>DecorView{id=-1, visibility=VISIBLE, width=720, height=1600, has-focus=false, has-focusable=true, has-window-focus=true, is-clickable=false, is-enabled=true, is-focused=false, is-focusable=false, is-layout-requested=false, is-selected=false, layout-params={(0,0)(fillxfill) blurRatio=1.0 blurMode=0 ty=BASE_APPLICATION wanim=0x10302fe
fl=LAYOUT_IN_SCREEN LAYOUT_INSET_DECOR SPLIT_TOUCH HARDWARE_ACCELERATED DRAWS_SYSTEM_BAR_BACKGROUNDS
pfl=FORCE_DRAW_STATUS_BAR_BACKGROUND}, tag=null, root-is-layout-requested=false, has-input-connection=false, x=0.0, y=0.0, child-count=3}

Although, i am using kaspresso but internally it's using kakao for uI testing, and even for simple kakao testing, it will fail. So need your assistance.

Repository: https://github.com/TheReprator/Wipro/blob/recycler_scroll_error/appModules/factList/src/androidTest/kotlin/reprator/wipro/factlist/test/FactListKaspressoTest.kt

Test Name: childAtPosition7

Looking forward for a solution.

Regards,
Vikram Singh

KRecyclerView built using IndexMatcher could not match children.

Steps to reproduce:

  1. I have a TabLayout with 2 tabs. Both contain RecyclerView with the same id.
  2. I match the RecyclerView in the second tab using withIndex(1) { withId(R.id.recyclerView) }
  3. Now if I try to do any child matching with the RecyclerView inside second tab, it fails. (childAt, childWith, etc).

Observed Results:

androidx.test.espresso.NoMatchingViewException: No views in hierarchy found matching: view holder at 0 position of recycler view: (1th view with: (view.getId() is <2131363464/com.package.name.debug:id/recyclerView>))

Expected Results:

I am expecting it to match the children of the RecyclerView inside the second tab.

Note that matching child items of the RecyclerView inside the first tab (withIndex(0)) works properly.

Relevant Code:

This is how I match my RecyclerViews.

// First tab RecyclerView
KRecyclerView(
  builder = {
      withIndex(0) {
          withId(R.id.recyclerView)
      }
  },
  itemTypeBuilder = {
      itemType(::Item)
  }
)

// Second tab RecyclerView
KRecyclerView(
  builder = {
      withIndex(1) {
          withId(R.id.recyclerView)
      }
  },
  itemTypeBuilder = {
      itemType(::Item)
  }
)

// Item
private class Item(parent: Matcher<View>) : KRecyclerItem<Item>(parent) {
      val moreOptionsIcon = KImageView(parent) { withId(R.id.moreIcon) }
}

Is there something wrong with how it is set up? Please help.

Runtime JAR files in the classpath should have the same version. These files were found in the classpath

The `kotlin-dsl` plugin applied to project ':buildSrc' enables experimental Kotlin compiler features. For more information see https://docs.gradle.org/6.7.1/userguide/kotlin_dsl.html#sec:kotlin-dsl_plugin
w: Runtime JAR files in the classpath should have the same version. These files were found in the classpath:
    /Users/vacxe/.gradle/wrapper/dists/gradle-6.7.1-all/2moa8rlfac5eqlcfgk98k0deb/gradle-6.7.1/lib/kotlin-stdlib-1.3.72.jar (version 1.3)
    /Users/vacxe/.gradle/wrapper/dists/gradle-6.7.1-all/2moa8rlfac5eqlcfgk98k0deb/gradle-6.7.1/lib/kotlin-stdlib-common-1.3.72.jar (version 1.3)
    /Users/vacxe/.gradle/wrapper/dists/gradle-6.7.1-all/2moa8rlfac5eqlcfgk98k0deb/gradle-6.7.1/lib/kotlin-stdlib-jdk7-1.3.72.jar (version 1.3)
    /Users/vacxe/.gradle/wrapper/dists/gradle-6.7.1-all/2moa8rlfac5eqlcfgk98k0deb/gradle-6.7.1/lib/kotlin-stdlib-jdk8-1.3.72.jar (version 1.3)
    /Users/vacxe/.gradle/wrapper/dists/gradle-6.7.1-all/2moa8rlfac5eqlcfgk98k0deb/gradle-6.7.1/lib/kotlin-reflect-1.3.72.jar (version 1.3)
    /Users/vacxe/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk8/1.4.32/3302f9ec8a5c1ed220781dbd37770072549bd333/kotlin-stdlib-jdk8-1.4.32.jar (version 1.4)
    /Users/vacxe/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-reflect/1.4.31/63db9d66c3d20f7b8f66196e7ba86969daae8b8a/kotlin-reflect-1.4.31.jar (version 1.4)
    /Users/vacxe/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk7/1.4.32/3546900a3ebff0c43f31190baf87a9220e37b7ea/kotlin-stdlib-jdk7-1.4.32.jar (version 1.4)
    /Users/vacxe/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib/1.4.32/461367948840adbb0839c51d91ed74ef4a9ccb52/kotlin-stdlib-1.4.32.jar (version 1.4)
    /Users/vacxe/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-common/1.4.32/ef50bfa2c0491a11dcc35d9822edbfd6170e1ea2/kotlin-stdlib-common-1.4.32.jar (version 1.4)

Version 3.0.0

Compilation error with Kakao new version 3.5.0

There is a difference between the JDK being used to build the library vs the "target" it is set to. The best practice is to set the JDK to the most up to date to get compiler benefits. But the target is better to be set at a minimum level to increase compatibility. This is similar to libraries setting Android minSdk to 21 or 23. Most Kotlin libraries are set to 11.

I see in this PR from before that 2 things are updated together:

2nd one is not needed and it creates the below incompatibility.

Steps to reproduce:

  1. Just update the version
  2. Compile/assemble test build

Observed Results:

The following error is observed

e: ScreenExtensions.kt:33:12 Cannot inline bytecode built with JVM target 17 into bytecode that is being built with JVM target 11. Please specify proper '-jvm-target' option

Expected Results:

Compile just fine when the host app targets JVM 11

Memory leak at IndexMatcher class instance

Hi. We use Ui tests for memory leaks detektion. But we've found leak at IndexMatcher that was added in https://github.com/agoda-com/Kakao/pull/200/files
The IndexMatcher contains link on a View and create memory leak when we leave Activity/Fragment in test step.

To reproduce:

  1. Create any screen with using of ViewBuilder.withIndex
class TestScreen : Screen<TestScreen>() {
    val testView = KView {
        withIndex(0) {
            ...
        }
    }
}
  1. Open some TestActivity/TestFragmen.
  2. Use testView in test step.
  3. Close that TestActivity/TestFragmen.

Result:

E/TestRunner: ----- begin exception -----
E/TestRunner: leakcanary.NoLeakAssertionFailedError: Application memory leaks were detected:
E/TestRunner: ====================================
E/TestRunner: HEAP ANALYSIS RESULT
E/TestRunner: ====================================
E/TestRunner: 1 APPLICATION LEAKS
E/TestRunner: References underlined with "~~~" are likely causes.
E/TestRunner: Learn more at https://squ.re/leaks.
E/TestRunner: 12136 bytes retained by leaking objects
E/TestRunner: Displaying only 1 leak trace out of 3 with the same signature
E/TestRunner: Signature: c7099d19e12c64f391b6157a00d3e14c2eed18a7
E/TestRunner: ┬──�
E/TestRunner: ��
E/TestRunner: │ GC Root: Input or output parameters in native code
E/TestRunner: │
E/TestRunner: ├─ com.project.android.test.mock.valuation.FlatValuationUiTest instance
E/TestRunner: │    Leaking: UNKNOWN
E/TestRunner: │    Retaining 1,5 MB in 24968 objects
E/TestRunner: │    ↓ FlatValuationUiTest.valuationScreen
E/TestRunner: │                          ~~~~~~~~~~~~~~~
E/TestRunner: ├─ com.project.android.screens.FlatValuationScreen instance
E/TestRunner: │    Leaking: UNKNOWN
E/TestRunner: │    Retaining 1,5 MB in 24965 objects
E/TestRunner: │    ↓ FlatValuationScreen.salePriceView
E/TestRunner: │                          ~~~~~~~~~~~~~
E/TestRunner: ├─ io.github.kakaocup.kakao.text.KTextView instance
E/TestRunner: │    Leaking: UNKNOWN
E/TestRunner: │    Retaining 1,4 MB in 24432 objects
E/TestRunner: │    ↓ KBaseView.view
E/TestRunner: │                ~~~~
E/TestRunner: ├─ io.github.kakaocup.kakao.delegate.ViewInteractionDelegate instance
E/TestRunner: │    Leaking: UNKNOWN
E/TestRunner: │    Retaining 1,4 MB in 24431 objects
E/TestRunner: │    ↓ ViewInteractionDelegate.interaction
E/TestRunner: │                              ~~~~~~~~~~~
E/TestRunner: ├─ androidx.test.espresso.ViewInteraction instance
E/TestRunner: │    Leaking: UNKNOWN
E/TestRunner: │    Retaining 1,4 MB in 24430 objects
E/TestRunner: │    ↓ ViewInteraction.viewMatcher
E/TestRunner: │                      ~~~~~~~~~~~
E/TestRunner: ├─ org.hamcrest.core.AllOf instance
E/TestRunner: │    Leaking: UNKNOWN
E/TestRunner: │    Retaining 1,4 MB in 24418 objects
E/TestRunner: │    ↓ AllOf.matchers
E/TestRunner: │            ~~~~~~~~
E/TestRunner: ├─ java.util.ArrayList instance
E/TestRunner: │    Leaking: UNKNOWN
E/TestRunner: │    Retaining 1,4 MB in 24417 objects
E/TestRunner: │    ↓ ArrayList[0]
E/TestRunner: │               ~~~
E/TestRunner: ├─ io.github.kakaocup.kakao.common.matchers.IndexMatcher instance
E/TestRunner: │    Leaking: UNKNOWN
E/TestRunner: │    Retaining 1,4 MB in 24415 objects
E/TestRunner: │    ↓ IndexMatcher.seen
E/TestRunner: │                   ~~~~
E/TestRunner: ├─ java.util.LinkedHashSet instance
E/TestRunner: │    Leaking: UNKNOWN
E/TestRunner: │    Retaining 1,4 MB in 24407 objects
E/TestRunner: │    ↓ LinkedHashSet[element()]
E/TestRunner: │                   ~~~~~~~~~~~
E/TestRunner: ╰→ android.widget.LinearLayout instance
E/TestRunner: ​     Leaking: YES (ObjectWatcher was watching this because com.project.valuation.ui.screen.flat.FlatValuationFragment received Fragment#onDestroyView() callback (references to its views should be cleared to prevent leaks) and View.mContext references a destroyed activity)
E/TestRunner: ​     Retaining 2,1 kB in 52 objects
E/TestRunner: ​     key = ea5d88bb-8313-47b4-b590-24555ab2b30a
E/TestRunner: ​     watchDurationMillis = 6078
E/TestRunner: ​     retainedDurationMillis = 1078
E/TestRunner: ​     View not part of a window view hierarchy
E/TestRunner: ​     View.mAttachInfo is null (view detached)
E/TestRunner: ​     View.mWindowAttachCount = 1
E/TestRunner: ​     mContext instance of com.project.valuation.ui.screen.flat.FlatValuationActivity with mDestroyed = true

TabLayoutActions is using incorrect constraints.

Steps to reproduce:

  1. Invoke KTabLayout.getSelectedItem during test.

Observed Results:

  • Test fails with the following stack trace:
Caused by: java.lang.RuntimeException: Action will not be performed because the target view does not match one or more of the following constraints:
is assignable from class: class com.google.android.material.bottomnavigation.BottomNavigationView
Target view: "TabLayout{id=2131362763, res-name=tab_layout, visibility=VISIBLE, width=1080, height=154, has-focus=false, has-focusable=true, has-window-focus=true, is-clickable=false, is-enabled=true, is-focused=false, is-focusable=true, is-layout-requested=false, is-selected=false, layout-params=com.google.android.material.appbar.CollapsingToolbarLayout$LayoutParams@79c30d1, tag=null, root-is-layout-requested=false, has-input-connection=false, x=0.0, y=776.0, child-count=1}"

Expected Results:

  • Test should return the selected tab index.

Relevant Code:

class MyScreen : Screen<MyScreen>() {
    val tabs = KTabLayout { withId(R.id.tab_layout) }
}

// During test.

onScreen<MyScreen> {
    val selected = tabs.getSelectedItem()
}

/**
* Returns the currently selected item id
*
* @return selected menu item id
*/
fun getSelectedItem(): Int {
var id = 0
view.perform(object : ViewAction {
override fun getDescription() = "Gets selected item id"
override fun getConstraints() = ViewMatchers.isAssignableFrom(BottomNavigationView::class.java)
override fun perform(uiController: UiController, view: View) {
if (view is BottomNavigationView) {
id = view.selectedItemId
}
}
})

  • It should've used TabLayout as the constraint, and cast the view to TabLayout during iside perform.

Potential performance optimisation around hierarchy-traversing matchers

Tl;dr: It's possible that Kakao could be more performant by reordering matchers to apply faster matchers first.


Since Kakao wraps Espresso, Espresso's view finding mechanism is used for actions and assertions. This mechanism will always iterate through entire view hierarchy looking for all views matching the requested predicate (source).

Most matchers should be pretty fast, however some of them will also iterate some part of view hierarchy as part of their check. For example HasDescendantMatcher or IsDescendantOfAMatcher will both go up or down the view hierarchy looking for views that match the requested descendant/ancestor. This means complexity of those matchers is not constant, contrary to simple matchers like WithIdMatcher. Moreover, when these matchers are nested, the overall performance decreases dramatically — for nested isDescendantOfA matchers, Espresso will go through every view in hierarchy, first matcher will go up in hierarchy and execute the nested matcher, which will also go up the hierarchy — I believe the complexity is non-linear in that case.

This is all unavoidable to an extent, but if those slow matchers are used within composite matchers like allOf() or anyOf(), the developer can easily improve Espresso performance by simply putting the expensive, view-traversing matchers later on the list. That way Espresso will short-circuit as soon as it can, potentially not invoking the expensive matcher at all.

If I read the code correctly, Kakao will sometimes put the slow matcher first on the list that is effectively transformed into an allOf() with same ordering, for example

val vb = ViewBuilder().apply {
isDescendantOfA { withMatcher(parentMatcher as Matcher<View>) }
builder(this)
}
or
edit = KEditText {
isDescendantOfA(function)
isAssignableFrom(EditText::class.java)
}
. Simply switching those lines would improve the performance essentially for free, with no obvious downsides. The faster matchers will run first and short-circuit the check if they fail, and the slower matcher will run a couple of times instead of for every single view.

This might look like a small change, but especially with nested matchers the performance impact can be significant —  in one project with ~270 UI tests, simply putting isDescendantOfA and hasDescendant matchers last in allOf calls cut total test run time in half (the project doesn't use Kakao).

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.