Skip to content
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
11 changes: 10 additions & 1 deletion sdk/src/main/java/com/hcaptcha/sdk/HCaptchaDialogFragment.java
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,10 @@ public final class HCaptchaDialogFragment extends DialogFragment implements IHCa

private boolean readyForInteraction = false;

private boolean webViewLoaded = false;

private boolean replayLoadedOnStart = false;

@Nullable
private static HCaptchaWebView sPreloadWebView;

Expand Down Expand Up @@ -197,6 +201,7 @@ public void onDestroy() {
HCaptchaLog.d("DialogFragment.onDestroy");
super.onDestroy();
readyForInteraction = false;
replayLoadedOnStart = webViewLoaded;
if (webViewHelper != null) {
webViewHelper.reset();
}
Expand All @@ -216,7 +221,8 @@ public void onStart() {
window.setDimAmount(0);
}
}
if (!readyForInteraction && webViewHelper != null) {
if (!readyForInteraction && webViewHelper != null && replayLoadedOnStart) {
replayLoadedOnStart = false;
HCaptchaLog.d("DialogFragment.onStart: re-triggering onLoaded after reset");
onLoaded();
}
Expand Down Expand Up @@ -258,6 +264,7 @@ public void onLoaded() {
HCaptchaLog.w("DialogFragment.onLoaded webViewHelper == null, likely about to destroy");
return;
}
webViewLoaded = true;
if (listener != null) {
listener.onLoaded();
}
Expand Down Expand Up @@ -371,6 +378,8 @@ public void reset() {
if (webViewHelper != null) {
webViewHelper.reset();
}
webViewLoaded = false;
replayLoadedOnStart = false;
if (isAdded()) {
dismissAllowingStateLoss();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import static androidx.test.espresso.Espresso.onView;
import static androidx.test.espresso.action.ViewActions.click;
import static androidx.test.espresso.assertion.ViewAssertions.matches;
import static androidx.test.espresso.intent.Intents.intended;
import static androidx.test.espresso.intent.Intents.intending;
import static androidx.test.espresso.intent.matcher.IntentMatchers.hasAction;
import static androidx.test.espresso.intent.matcher.IntentMatchers.hasData;
Expand All @@ -14,8 +15,8 @@
import static androidx.test.espresso.web.webdriver.DriverAtoms.clearElement;
import static androidx.test.espresso.web.webdriver.DriverAtoms.findElement;
import static androidx.test.espresso.web.webdriver.DriverAtoms.webClick;
import static androidx.test.espresso.intent.Intents.intended;
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
import static com.hcaptcha.sdk.AssertUtil.evaluateJavascript;
import static com.hcaptcha.sdk.AssertUtil.waitHCaptchaWebViewErrorByInput;
import static com.hcaptcha.sdk.AssertUtil.waitHCaptchaWebViewToken;
import static com.hcaptcha.sdk.AssertUtil.waitToBeDisplayed;
Expand Down Expand Up @@ -157,11 +158,17 @@ private void waitForWebViewToEmitToken(final CountDownLatch latch)

@Test
public void loaderVisible() {
launchInContainer();
launchInContainer(
config,
internalConfig.toBuilder()
.htmlProvider(new HCaptchaTestHtml(false))
.build(),
new HCaptchaStateTestAdapter());
onView(withId(R.id.loadingContainer)).perform(waitToBeDisplayed());
onView(withId(R.id.loadingContainer)).check(matches(isDisplayed()));
onView(withId(R.id.loadingContainer)).check(matches(withBackgroundColor(android.graphics.Color.WHITE)));
onView(withId(R.id.webView)).perform(waitToBeDisplayed());
onView(withId(R.id.webView)).perform(evaluateJavascript("onHcaptchaLoaded()"));
final long waitToDisappearMs = 10000;
onView(withId(R.id.loadingContainer)).perform(waitToDisappear(waitToDisappearMs));
}
Expand Down Expand Up @@ -473,6 +480,39 @@ public void testTouchShouldNotCloseCaptchaWithoutDefaultLoadingIndicator() {
}
}

@Test
public void testInvisibleDialogDoesNotExecuteBeforeBridgeLoaded() throws InterruptedException {
final CountDownLatch loadedLatch = new CountDownLatch(1);
final CountDownLatch failureLatch = new CountDownLatch(1);
final HCaptchaStateListener listener = new HCaptchaStateTestAdapter() {
@Override
void onLoaded() {
loadedLatch.countDown();
}

@Override
void onFailure(HCaptchaException exception) {
failureLatch.countDown();
}
};

try (FragmentScenario<HCaptchaDialogFragment> scenario = launch(
config.toBuilder()
.size(HCaptchaSize.INVISIBLE)
.hideDialog(false)
.build(),
internalConfig.toBuilder()
.htmlProvider(new HCaptchaTestHtml(false, true))
.build(),
listener)) {
assertFalse(loadedLatch.await(AWAIT_CALLBACK_MS, TimeUnit.MILLISECONDS));
assertFalse(failureLatch.await(AWAIT_CALLBACK_MS, TimeUnit.MILLISECONDS));
onView(withId(R.id.webView)).perform(evaluateJavascript("onHcaptchaLoaded()"));
assertTrue(loadedLatch.await(AWAIT_CALLBACK_MS, TimeUnit.MILLISECONDS));
assertTrue(failureLatch.await(AWAIT_CALLBACK_MS, TimeUnit.MILLISECONDS));
}
}

@Test
public void testBackShouldNotCloseCaptchaWithCustomRetry() {
try (FragmentScenario<HCaptchaDialogFragment> scenario = launch(
Expand Down
37 changes: 37 additions & 0 deletions test/src/androidTest/java/com/hcaptcha/sdk/HCaptchaTest.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.hcaptcha.sdk;

import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
import static com.hcaptcha.sdk.AssertUtil.waitHCaptchaWebViewToken;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
Expand All @@ -20,6 +21,7 @@

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;

public class HCaptchaTest {
private static final long AWAIT_CALLBACK_MS = 5000;
Expand Down Expand Up @@ -126,6 +128,41 @@ public void e2eWithDebugTokenHeadlessWebView() throws Exception {
assertTrue(latch.await(E2E_AWAIT_CALLBACK_MS, TimeUnit.MILLISECONDS));
}

@Test
public void dialogVerifierReplaysLoadedAfterReset() throws Exception {
final CountDownLatch firstSuccessLatch = new CountDownLatch(1);
final CountDownLatch secondFailureLatch = new CountDownLatch(1);
final AtomicReference<HCaptcha> hCaptchaRef = new AtomicReference<>();

final HCaptchaInternalConfig resetAwareInternalConfig = internalConfig.toBuilder()
.htmlProvider(new HCaptchaTestHtml(true, false, true))
.build();
final HCaptchaConfig dialogConfig = config.toBuilder().hideDialog(false).build();

final ActivityScenario<TestActivity> scenario = rule.getScenario();
scenario.onActivity(activity -> {
final HCaptcha hCaptcha = HCaptcha.getClient(activity, resetAwareInternalConfig);
hCaptchaRef.set(hCaptcha);
hCaptcha.verifyWithHCaptcha(dialogConfig)
.addOnSuccessListener(response -> firstSuccessLatch.countDown())
.addOnFailureListener(exception -> {
if (firstSuccessLatch.getCount() == 0
&& exception.getHCaptchaError() == HCaptchaError.ERROR) {
secondFailureLatch.countDown();
} else {
fail("Unexpected failure: " + exception.getHCaptchaError());
}
});
});

waitHCaptchaWebViewToken(firstSuccessLatch, AWAIT_CALLBACK_MS);
getInstrumentation().waitForIdleSync();

scenario.onActivity(activity -> hCaptchaRef.get().verifyWithHCaptcha(dialogConfig));

assertTrue(secondFailureLatch.await(AWAIT_CALLBACK_MS, TimeUnit.MILLISECONDS));
}

@Test(expected = IllegalStateException.class)
public void badActivity() {
Looper.prepare();
Expand Down
17 changes: 17 additions & 0 deletions test/src/androidTest/java/com/hcaptcha/sdk/HCaptchaTestHtml.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,25 @@
class HCaptchaTestHtml implements IHCaptchaHtmlProvider {

private final boolean callBridgeOnLoaded;
private final boolean failOnExecute;
private final boolean failOnExecuteAfterReset;

HCaptchaTestHtml() {
this(true);
}

HCaptchaTestHtml(boolean callBridgeOnLoaded) {
this(callBridgeOnLoaded, false);
}

HCaptchaTestHtml(boolean callBridgeOnLoaded, boolean failOnExecute) {
this(callBridgeOnLoaded, failOnExecute, false);
}

HCaptchaTestHtml(boolean callBridgeOnLoaded, boolean failOnExecute, boolean failOnExecuteAfterReset) {
this.callBridgeOnLoaded = callBridgeOnLoaded;
this.failOnExecute = failOnExecute;
this.failOnExecuteAfterReset = failOnExecuteAfterReset;
}

@Override
Expand Down Expand Up @@ -40,6 +52,7 @@ public String getHtml() {
+ " console.assert(typeof window.JSDI.getSysDebug() === 'object');\n"
+ " var BridgeObject = window.JSInterface;\n"
+ " var bridgeConfig = JSON.parse(BridgeObject.getConfig());\n"
+ " var resetCount = 0;\n"
+ " function onHcaptchaLoaded() {\n"
+ " try {\n"
+ " BridgeObject.onLoaded();\n"
Expand All @@ -64,9 +77,13 @@ public String getHtml() {
+ " TestObject.setData(JSON.stringify(arg));\n"
+ " }\n"
+ " function reset() {\n"
+ " resetCount += 1;\n"
+ " document.getElementById(\"input-text\").value = \"reset\";\n"
+ " }\n"
+ " function execute() {\n"
+ (failOnExecute ? " BridgeObject.onError(29);\n" : "")
+ (failOnExecuteAfterReset
? " if (resetCount > 1) { BridgeObject.onError(29); }\n" : "")
+ " }\n"
+ (callBridgeOnLoaded ? "onHcaptchaLoaded();\n" : "")
+ " </script>\n"
Expand Down
Loading