fix(android): verify public WebView smoke
This commit is contained in:
parent
ef9ac148e5
commit
17478f6303
|
|
@ -131,6 +131,7 @@ android {
|
|||
"FIREBASE_GCM_SENDER_ID",
|
||||
quotedBuildConfig(firebaseSenderId.get())
|
||||
)
|
||||
buildConfigField("boolean", "SAFE_PAGE_LOAD_DIAGNOSTICS", "false")
|
||||
|
||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||
}
|
||||
|
|
@ -178,6 +179,7 @@ android {
|
|||
"BASE_URL",
|
||||
"\"${releaseBaseUrl.get().replace("\\", "\\\\").replace("\"", "\\\"")}\""
|
||||
)
|
||||
buildConfigField("boolean", "SAFE_PAGE_LOAD_DIAGNOSTICS", "true")
|
||||
manifestPlaceholders["usesCleartextTraffic"] = "false"
|
||||
manifestPlaceholders["deepLinkHost"] =
|
||||
releaseManifestOrigin?.host ?: "invalid.whoneedhelp.local"
|
||||
|
|
@ -197,6 +199,7 @@ android {
|
|||
"BASE_URL",
|
||||
"\"${releaseBaseUrl.get().replace("\\", "\\\\").replace("\"", "\\\"")}\""
|
||||
)
|
||||
buildConfigField("boolean", "SAFE_PAGE_LOAD_DIAGNOSTICS", "true")
|
||||
manifestPlaceholders["usesCleartextTraffic"] = "false"
|
||||
manifestPlaceholders["deepLinkHost"] =
|
||||
releaseManifestOrigin?.host ?: "invalid.whoneedhelp.local"
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import static androidx.test.espresso.web.assertion.WebViewAssertions.webMatches;
|
|||
import static androidx.test.espresso.web.sugar.Web.onWebView;
|
||||
import static androidx.test.espresso.web.webdriver.DriverAtoms.findElement;
|
||||
import static androidx.test.espresso.web.webdriver.DriverAtoms.getText;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
|
||||
import android.content.Context;
|
||||
|
|
@ -28,10 +29,15 @@ public final class PublicStagingInstrumentedTest {
|
|||
|
||||
@Test
|
||||
public void publicHomeAndSafetyDeepLinkRenderExpectedDom() throws Exception {
|
||||
assertTrue(
|
||||
"Public non-production smoke builds must expose redacted page-load diagnostics",
|
||||
BuildConfig.SAFE_PAGE_LOAD_DIAGNOSTICS
|
||||
);
|
||||
|
||||
try (ActivityScenario<MainActivity> scenario = launch("/")) {
|
||||
waitForElementText(
|
||||
"main-content",
|
||||
"Help can be closer than you think."
|
||||
"Need help nearby? Ask the community."
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -61,6 +61,8 @@ import org.json.JSONObject;
|
|||
|
||||
public final class MainActivity extends ComponentActivity {
|
||||
private static final String LOG_TAG = "WhoNeedHelpWebView";
|
||||
private static final boolean PAGE_LOAD_DIAGNOSTICS =
|
||||
BuildConfig.DEBUG || BuildConfig.SAFE_PAGE_LOAD_DIAGNOSTICS;
|
||||
private WebView webView;
|
||||
private TrustedOrigin trustedOrigin;
|
||||
private GeolocationPermissions.Callback pendingLocationCallback;
|
||||
|
|
@ -201,13 +203,26 @@ public final class MainActivity extends ComponentActivity {
|
|||
}
|
||||
|
||||
private static String safeLogPath(Uri uri) {
|
||||
if (uri == null) {
|
||||
return "/";
|
||||
}
|
||||
|
||||
String path = uri.getPath();
|
||||
|
||||
if (path != null && path.startsWith("/users/log-in/")) {
|
||||
return "/users/log-in/[redacted]";
|
||||
}
|
||||
|
||||
return path;
|
||||
return path == null || path.isEmpty() ? "/" : path;
|
||||
}
|
||||
|
||||
private static void logMainFrameFailure(String detail, Uri uri) {
|
||||
if (PAGE_LOAD_DIAGNOSTICS) {
|
||||
Log.e(
|
||||
LOG_TAG,
|
||||
"Main-frame load failed: " + detail + " path=" + safeLogPath(uri)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -886,6 +901,10 @@ public final class MainActivity extends ComponentActivity {
|
|||
SslError error
|
||||
) {
|
||||
mainFrameLoadFailed = true;
|
||||
logMainFrameFailure(
|
||||
"ssl_error=" + error.getPrimaryError(),
|
||||
Uri.parse(error.getUrl() == null ? "" : error.getUrl())
|
||||
);
|
||||
handler.cancel();
|
||||
showPageLoadError(R.string.secure_connection_failed);
|
||||
}
|
||||
|
|
@ -898,17 +917,7 @@ public final class MainActivity extends ComponentActivity {
|
|||
) {
|
||||
if (request.isForMainFrame()) {
|
||||
mainFrameLoadFailed = true;
|
||||
|
||||
if (BuildConfig.DEBUG) {
|
||||
Log.e(
|
||||
LOG_TAG,
|
||||
"Main-frame load failed: code="
|
||||
+ error.getErrorCode()
|
||||
+ " path="
|
||||
+ safeLogPath(request.getUrl())
|
||||
);
|
||||
}
|
||||
|
||||
logMainFrameFailure("code=" + error.getErrorCode(), request.getUrl());
|
||||
showPageLoadError(R.string.page_load_failed);
|
||||
}
|
||||
}
|
||||
|
|
@ -921,6 +930,10 @@ public final class MainActivity extends ComponentActivity {
|
|||
) {
|
||||
if (request.isForMainFrame()) {
|
||||
mainFrameLoadFailed = true;
|
||||
logMainFrameFailure(
|
||||
"http_status=" + errorResponse.getStatusCode(),
|
||||
request.getUrl()
|
||||
);
|
||||
showPageLoadError(R.string.page_load_failed);
|
||||
}
|
||||
}
|
||||
|
|
@ -930,7 +943,7 @@ public final class MainActivity extends ComponentActivity {
|
|||
mainFrameLoadFailed = false;
|
||||
dismissPageLoadError();
|
||||
|
||||
if (BuildConfig.DEBUG) {
|
||||
if (PAGE_LOAD_DIAGNOSTICS) {
|
||||
Log.d(LOG_TAG, "Main-frame load started: path=" + safeLogPath(Uri.parse(url)));
|
||||
}
|
||||
|
||||
|
|
@ -946,7 +959,7 @@ public final class MainActivity extends ComponentActivity {
|
|||
dismissPageLoadError();
|
||||
}
|
||||
|
||||
if (BuildConfig.DEBUG) {
|
||||
if (PAGE_LOAD_DIAGNOSTICS) {
|
||||
Log.d(LOG_TAG, "Main-frame load finished: path=" + safeLogPath(Uri.parse(url)));
|
||||
scheduleMapDiagnostics(view, Uri.parse(url));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,16 @@
|
|||
package org.whoneedhelp.mobile;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
public final class BuildTypeConfigurationTest {
|
||||
@Test
|
||||
public void safePageLoadDiagnosticsAreLimitedToPublicNonProductionBuilds() {
|
||||
boolean expected =
|
||||
BuildConfig.APPLICATION_ID.endsWith(".development")
|
||||
|| BuildConfig.APPLICATION_ID.endsWith(".staging");
|
||||
|
||||
assertEquals(expected, BuildConfig.SAFE_PAGE_LOAD_DIAGNOSTICS);
|
||||
}
|
||||
}
|
||||
|
|
@ -1413,6 +1413,22 @@ None of the observations below describe the current delivery path.
|
|||
production pipeline separately passed release tests, lint, R8/resource
|
||||
shrinking, APK/AAB signing checks, Bundletool validation, and production App
|
||||
Links identity validation.
|
||||
- The API 37 development emulator smoke now passes the signed
|
||||
`org.whoneedhelp.mobile.development` APK against
|
||||
`https://whoneedhelp.imalto.site`. It observed the home and `/safety`
|
||||
main-frame loads, asserted both DOMs, rejected an unrelated HTTPS origin,
|
||||
found no load/TLS error, and captured the rendered phone screenshots.
|
||||
Evidence is retained at
|
||||
`output/android-development-smoke/20260723225620-2568739`. Its run-scoped
|
||||
container, AVD volume, and image were all absent after cleanup.
|
||||
- This replay exposed and fixed two stale checks rather than treating a build as
|
||||
runtime proof. Development/staging APKs deliberately have Android debugging
|
||||
disabled, so their previous smoke harness waited for a debug-only page-load
|
||||
message even after WebView rendered successfully. Redacted path-only load
|
||||
diagnostics are now enabled only for the signed non-production public build
|
||||
types, while WebView debugging and the release build remain disabled. The
|
||||
public DOM assertion was also updated from the retired hero copy to the
|
||||
current `Need help nearby? Ask the community.` heading.
|
||||
- The complete isolated quality/security gate passed with 353 ExUnit tests,
|
||||
ShellCheck, Hadolint, actionlint, Compose/Helm validation, format/compiler,
|
||||
xref, Credo, Sobelow, Dialyzer, Hex/npm audits, source scanning, and all
|
||||
|
|
@ -1459,14 +1475,17 @@ None of the observations below describe the current delivery path.
|
|||
after that promotion; the current test origin still depends on its configured
|
||||
workstation/VPN/gateway path.
|
||||
- The final Android application ID is `org.whoneedhelp.mobile`. The application
|
||||
publishes environment-specific `/.well-known/assetlinks.json`. The previous
|
||||
staging identity was checked against its HTTPS response; repeat that online
|
||||
check for the new `org.whoneedhelp.mobile.development` identity after the
|
||||
controlled development rebuild. Before a Play release, register the
|
||||
application, add the Play App Signing certificate fingerprint alongside any
|
||||
sideload/upload fingerprint, repeat Android's domain verification on a
|
||||
device, and complete store policy/release work. A dedicated upload key and
|
||||
signed APK/AAB exist, but no Play application has been registered.
|
||||
publishes environment-specific `/.well-known/assetlinks.json`. The online
|
||||
development response now agrees with
|
||||
`org.whoneedhelp.mobile.development` and its signed certificate, and the
|
||||
explicit same-origin deep link rendered in the API 37 smoke. Android still
|
||||
reported the fresh emulator's domain-verification state as `none`, so a
|
||||
verified implicit App Link has not yet been observed on a device. Before a
|
||||
Play release, register the application, add the Play App Signing certificate
|
||||
fingerprint alongside any sideload/upload fingerprint, repeat Android's
|
||||
domain verification on a device, and complete store policy/release work. A
|
||||
dedicated upload key and signed APK/AAB exist, but no Play application has
|
||||
been registered.
|
||||
- Operate PostgreSQL/PostGIS with off-site backups, recovery testing, monitoring,
|
||||
and the availability model selected for real usage.
|
||||
- The development Brevo SMTP transport and sender have completed both an
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user