Skip to content

ADFA-2738 connectedV8DebugAndroidTest Part 3 of 3: iterate over 1st three templates#1187

Merged
hal-eisen-adfa merged 4 commits into
stagefrom
ADFA-2738-connectedV8DebugAndroidTest-part3-projects-actually
Apr 16, 2026
Merged

ADFA-2738 connectedV8DebugAndroidTest Part 3 of 3: iterate over 1st three templates#1187
hal-eisen-adfa merged 4 commits into
stagefrom
ADFA-2738-connectedV8DebugAndroidTest-part3-projects-actually

Conversation

@hal-eisen-adfa
Copy link
Copy Markdown
Collaborator

@hal-eisen-adfa hal-eisen-adfa commented Apr 16, 2026

Extend the end2end test to iterate over the 1st three project templates

It's tricky getting the tests to not get blocked by the universal tooltips

Added a template loop to EndToEndTest that iterates over three templates (No Activity, Empty Activity, Basic Activity), and for each one: clicks "Create project" on the home screen, selects the template from the list, sets a meaningful project name, clicks "Create project" on the settings page, then runs the InitializationProjectAndCancelingBuildScenario which dismisses the first-build notice, clicks "Sync project" to trigger Gradle initialization, waits for "Project initialized", clicks "Quick run" to build the APK, waits for the system package installer to appear (confirming the APK was built successfully), dismisses it, and closes the project back to the home screen before the next iteration. The key technical challenge was that the IDETooltipWebviewFragment creates a transparent WebView overlay that intercepts all coordinate-based clicks (both Espresso and UiAutomator), so had to use accessibility ACTION_CLICK via clickFirstAccessibilityNodeByText and related helpers throughout. Extracted three new shared helpers (clickFirstAccessibilityNodeByDescription, clickFirstAccessibilityNodeParentByText, and setAccessibilityEditText) into DevicePermissionGrantUiHelper to eliminate duplicated accessibility tree traversal code across the screen objects and scenario.

@hal-eisen-adfa hal-eisen-adfa changed the title ADFA-2738 Extend the end2end test to iterate over the 1st three project templates ADFA-2738 connectedV8DebugAndroidTest Part 3 of 3: iterate over 1st three templates Apr 16, 2026
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 16, 2026

Warning

Rate limit exceeded

@hal-eisen-adfa has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 44 minutes and 6 seconds before requesting another review.

Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 44 minutes and 6 seconds.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: b2ed8f8a-7c5e-4a31-af4a-dad5e25c1912

📥 Commits

Reviewing files that changed from the base of the PR and between 71f7307 and 30dc545.

📒 Files selected for processing (1)
  • app/src/androidTest/kotlin/com/itsaky/androidide/helper/DevicePermissionGrantUiHelper.kt
📝 Walkthrough

Walkthrough

Added accessibility-driven UIAutomator helpers and migrated several instrumentation test screens and scenarios from Espresso/Kakao to accessibility-based interactions; extended the end-to-end test to create and initialize projects for three templates, invoking project settings, naming, and cancelling builds.

Changes

Cohort / File(s) Summary
Accessibility Helper Functions
app/src/androidTest/.../DevicePermissionGrantUiHelper.kt
Added clickFirstAccessibilityNodeByDescription, clickFirstAccessibilityNodeParentByText, setAccessibilityEditText, and internalized findAndActOnAccessibilityNode; refactored existing text-click helper to return success and use centralized lookup/action logic.
End-to-End Test Extension
app/src/androidTest/.../EndToEndTest.kt
Appended "Phase 2" to the e2e flow: iterates three templates (No Activity, Empty Activity, Basic Activity), performs create-project flow, sets template-specific names, initializes project, cancels build, and ensures home screen between iterations.
Initialization & Build Scenario
app/src/androidTest/.../InitializationProjectAndCancelingBuildScenario.kt
Rewrote build/init scenario to use UiAutomator/accessibility helpers: dismiss initial dialogs conditionally, wait for initialization text, trigger quick-run via toolbar accessibility, detect package installer, and close project via accessibility interactions. Added clickToolbarButton.
Home screen UI interactions
app/src/androidTest/.../HomeScreen.kt
Removed RecyclerView/Kakao scrolling and replaced "Create project" action with clickFirstAccessibilityNodeByText("Create project") plus ui idle wait.
Project settings interactions
app/src/androidTest/.../ProjectSettingsScreen.kt
Replaced Kakao button click with accessibility-based clickFirstAccessibilityNodeByText and added setProjectName(name: String) using setAccessibilityEditText.
Template selection
app/src/androidTest/.../TemplateScreen.kt
Removed Kaspresso/KScreen template discovery; added UiAutomator resource/text lookup and clickFirstAccessibilityNodeParentByText to select templates with explicit existence checks.

Sequence Diagram(s)

sequenceDiagram
    participant Test as Test Runner
    participant Home as IDE Home UI
    participant Template as Template List UI
    participant Settings as Project Settings UI
    participant Build as Build Process / Package Installer
    participant Device as Android Device

    Test->>Home: ensure on home screen
    Home->>Test: home visible
    Test->>Home: click "Create project" (accessibility)
    Home->>Template: open template list
    Test->>Template: select template (accessibility parent click)
    Template->>Settings: open project settings
    Test->>Settings: set project name (ACTION_SET_TEXT)
    Test->>Settings: click "Create" / initialize
    Settings->>Build: start project init & quick-run
    Build->>Device: show package installer / permission UI
    Test->>Device: pressBack to dismiss installer
    Test->>Home: return to home (close project)
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested reviewers

  • jatezzz
  • Daniel-ADFA
  • jomen-adfa

Poem

🐰 I hopped through views with gentle taps and cheer,
I clicked, I named, three templates appeared,
Accessibility guides my nimble paw,
Projects bloom, then vanish—no build to gnaw,
A rabbit's test-run dance, robust and cleared.

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 45.45% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main change: iterating over the first three project templates in the end-to-end test as part 3 of the implementation.
Description check ✅ Passed The description is directly related to the changeset, explaining the template iteration loop, the technical challenge with WebView overlays, and the accessibility-based solution with extracted helpers.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch ADFA-2738-connectedV8DebugAndroidTest-part3-projects-actually

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (3)
app/src/androidTest/kotlin/com/itsaky/androidide/screens/ProjectSettingsScreen.kt (1)

116-124: Hardcoded "My Application" may break if default project name changes.

The helper searches for text starting with "My Application" to locate the project name field. If the default project name changes or differs by locale, this will fail.

Consider using a resource ID-based lookup as a fallback, or documenting this assumption.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@app/src/androidTest/kotlin/com/itsaky/androidide/screens/ProjectSettingsScreen.kt`
around lines 116 - 124, The locator in TestContext<Unit>.setProjectName uses a
hardcoded UiSelector().textStartsWith("My Application") which is brittle; update
setProjectName to first try a stable resource-id or content-description lookup
(e.g., the view's resource id / accessibility label used by
setAccessibilityEditText) and only fall back to textStartsWith when the
id/description isn't found, or document the assumption if id isn't available;
ensure you reference the existing helper setAccessibilityEditText and the
UiSelector().textStartsWith("My Application") usage when making the change so
the fallback strategy is implemented around those symbols.
app/src/androidTest/kotlin/com/itsaky/androidide/helper/DevicePermissionGrantUiHelper.kt (1)

94-98: Consider adding isEnabled and isVisibleToUser checks for consistency.

clickFirstAccessibilityNodeByText checks isEnabled and isVisibleToUser before clicking, but this function only checks isClickable. This could lead to clicking disabled or invisible toolbar buttons.

♻️ Suggested fix
             val desc = node.contentDescription?.toString() ?: ""
-            if (!clicked && desc.contains(searchText, ignoreCase = true) && node.isClickable) {
+            if (!clicked && desc.contains(searchText, ignoreCase = true) 
+                && node.isClickable && node.isEnabled && node.isVisibleToUser) {
                 clicked = node.performAction(AccessibilityNodeInfo.ACTION_CLICK)
             }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@app/src/androidTest/kotlin/com/itsaky/androidide/helper/DevicePermissionGrantUiHelper.kt`
around lines 94 - 98, The loop that finds and clicks toolbar nodes only checks
node.isClickable and may click disabled or invisible elements; update the logic
in the same method (the loop iterating over nodes, using
node.contentDescription, searchText, clicked, and
node.performAction(AccessibilityNodeInfo.ACTION_CLICK)) to also verify
node.isEnabled and node.isVisibleToUser before attempting performAction,
mirroring the safeguards used in clickFirstAccessibilityNodeByText so you only
click nodes that are clickable, enabled, and visible to the user.
app/src/androidTest/kotlin/com/itsaky/androidide/EndToEndTest.kt (1)

220-232: Consider wrapping template operations in steps for better failure diagnostics.

Currently only clickCreateProjectHomeScreen() is inside a step(). If selectProjectTemplate, setProjectName, clickCreateProjectProjectSettings, or initializeProjectAndCancelBuild fail, the failure won't indicate which operation failed for each template.

♻️ Proposed structure for better traceability
         for ((index, config) in templates.withIndex()) {
-            step("Create+build template ${index + 1}/${templates.size}: ${config.label}") {
-                clickCreateProjectHomeScreen()
-            }
-            selectProjectTemplate("Select ${config.label} template", config.templateResId)
-            setProjectName(config.projectName)
-            clickCreateProjectProjectSettings()
-            initializeProjectAndCancelBuild()
+            step("Click create project for ${config.label}") {
+                clickCreateProjectHomeScreen()
+            }
+            step("Select ${config.label} template") {
+                selectProjectTemplate("Select ${config.label} template", config.templateResId)
+            }
+            step("Set project name for ${config.label}") {
+                setProjectName(config.projectName)
+            }
+            step("Click create project settings for ${config.label}") {
+                clickCreateProjectProjectSettings()
+            }
+            step("Initialize and cancel build for ${config.label}") {
+                initializeProjectAndCancelBuild()
+            }

             if (index < templates.lastIndex) {
-                ensureOnHomeScreenBeforeCreateProject()
+                step("Return to home screen after ${config.label}") {
+                    ensureOnHomeScreenBeforeCreateProject()
+                }
             }
         }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/src/androidTest/kotlin/com/itsaky/androidide/EndToEndTest.kt` around
lines 220 - 232, The current step("Create+build template ...") only wraps
clickCreateProjectHomeScreen(), which loses per-operation context on failure;
change the step around the whole template sequence so that
selectProjectTemplate, setProjectName, clickCreateProjectProjectSettings, and
initializeProjectAndCancelBuild are executed inside the same step block (keeping
the step label using config.label and index), e.g. open the step before
clickCreateProjectHomeScreen() and close it after
initializeProjectAndCancelBuild so failures report the template and specific
step, and keep ensureOnHomeScreenBeforeCreateProject() outside the step for the
index < templates.lastIndex case.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@app/src/androidTest/kotlin/com/itsaky/androidide/helper/DevicePermissionGrantUiHelper.kt`:
- Around line 125-135: The parent chain traversal in
DevicePermissionGrantUiHelper iterates using current: AccessibilityNodeInfo? =
node and sets current = current.parent but never recycles those parent
instances, leaking AccessibilityNodeInfo objects; fix by capturing
current.parent into a temporary variable (e.g., parent = current.parent), and
after using current (checking isClickable and performAction) call
current.recycle() before assigning current = parent so every node retrieved via
.parent is recycled, taking care not to recycle the original `node` twice (only
recycle it once at the end if not already recycled).

In
`@app/src/androidTest/kotlin/com/itsaky/androidide/scenarios/InitializationProjectAndCancelingBuildScenario.kt`:
- Around line 71-77: The code re-uses the existing UiObject saveBtn after
waitForExists returns false, but UiObject doesn't re-query the UI - recreate the
object after the back press and before checking existence again; specifically,
after d.pressBack() and d.waitForIdle(), reassign saveBtn =
d.findObject(UiSelector().text("Save files and close project")) (or create a new
local variable) and then call waitForExists(3_000) on that new UiObject to
ensure the UI tree is re-searched.

---

Nitpick comments:
In `@app/src/androidTest/kotlin/com/itsaky/androidide/EndToEndTest.kt`:
- Around line 220-232: The current step("Create+build template ...") only wraps
clickCreateProjectHomeScreen(), which loses per-operation context on failure;
change the step around the whole template sequence so that
selectProjectTemplate, setProjectName, clickCreateProjectProjectSettings, and
initializeProjectAndCancelBuild are executed inside the same step block (keeping
the step label using config.label and index), e.g. open the step before
clickCreateProjectHomeScreen() and close it after
initializeProjectAndCancelBuild so failures report the template and specific
step, and keep ensureOnHomeScreenBeforeCreateProject() outside the step for the
index < templates.lastIndex case.

In
`@app/src/androidTest/kotlin/com/itsaky/androidide/helper/DevicePermissionGrantUiHelper.kt`:
- Around line 94-98: The loop that finds and clicks toolbar nodes only checks
node.isClickable and may click disabled or invisible elements; update the logic
in the same method (the loop iterating over nodes, using
node.contentDescription, searchText, clicked, and
node.performAction(AccessibilityNodeInfo.ACTION_CLICK)) to also verify
node.isEnabled and node.isVisibleToUser before attempting performAction,
mirroring the safeguards used in clickFirstAccessibilityNodeByText so you only
click nodes that are clickable, enabled, and visible to the user.

In
`@app/src/androidTest/kotlin/com/itsaky/androidide/screens/ProjectSettingsScreen.kt`:
- Around line 116-124: The locator in TestContext<Unit>.setProjectName uses a
hardcoded UiSelector().textStartsWith("My Application") which is brittle; update
setProjectName to first try a stable resource-id or content-description lookup
(e.g., the view's resource id / accessibility label used by
setAccessibilityEditText) and only fall back to textStartsWith when the
id/description isn't found, or document the assumption if id isn't available;
ensure you reference the existing helper setAccessibilityEditText and the
UiSelector().textStartsWith("My Application") usage when making the change so
the fallback strategy is implemented around those symbols.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: f1016b59-e58a-41d7-b8ef-b41a7dc3e7f0

📥 Commits

Reviewing files that changed from the base of the PR and between 7549d8f and 8fc7e66.

📒 Files selected for processing (6)
  • app/src/androidTest/kotlin/com/itsaky/androidide/EndToEndTest.kt
  • app/src/androidTest/kotlin/com/itsaky/androidide/helper/DevicePermissionGrantUiHelper.kt
  • app/src/androidTest/kotlin/com/itsaky/androidide/scenarios/InitializationProjectAndCancelingBuildScenario.kt
  • app/src/androidTest/kotlin/com/itsaky/androidide/screens/HomeScreen.kt
  • app/src/androidTest/kotlin/com/itsaky/androidide/screens/ProjectSettingsScreen.kt
  • app/src/androidTest/kotlin/com/itsaky/androidide/screens/TemplateScreen.kt

@hal-eisen-adfa hal-eisen-adfa requested a review from jatezzz April 16, 2026 06:38
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
app/src/androidTest/kotlin/com/itsaky/androidide/helper/DevicePermissionGrantUiHelper.kt (1)

136-147: Ensure root/node recycling is exception-safe.

root is recycled only after findAccessibilityNodeInfosByText succeeds, and node.recycle() is skipped if action(node) throws. Wrapping both operations in try/finally prevents leaks in failure paths.

♻️ Proposed fix
-    val nodes = root.findAccessibilityNodeInfosByText(searchText)
     var success = false
     try {
+        val nodes = root.findAccessibilityNodeInfosByText(searchText)
         for (node in nodes) {
-            if (!success) {
-                success = action(node)
+            try {
+                if (!success) {
+                    success = action(node)
+                }
+            } finally {
+                node.recycle()
             }
-            node.recycle()
         }
     } finally {
         root.recycle()
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@app/src/androidTest/kotlin/com/itsaky/androidide/helper/DevicePermissionGrantUiHelper.kt`
around lines 136 - 147, The code currently calls
root.findAccessibilityNodeInfosByText(...) and recycles root only in the outer
finally, but if findAccessibilityNodeInfosByText or action(node) throws some
nodes may never be recycled; change the structure so root and every node are
always recycled: after obtaining nodes from
root.findAccessibilityNodeInfosByText(searchText) wrap the node iteration in a
try/finally where each node is recycled in an inner finally (e.g., for each node
do try { success = success || action(node) } finally { node.recycle() }), and
then in the outer finally call root.recycle(); ensure you reference the existing
symbols root, nodes, action(node), node.recycle(), root.recycle(), and
findAccessibilityNodeInfosByText(searchText) when applying the change.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@app/src/androidTest/kotlin/com/itsaky/androidide/helper/DevicePermissionGrantUiHelper.kt`:
- Line 60: The KDoc for the function in DevicePermissionGrantUiHelper that uses
check(success) is incorrect (it states AssertionError) — update the
documentation to state that the call will throw IllegalStateException when no
button is found (include context mentioning the check(success) call and the
errorLabel used in the message), or alternatively change the runtime check to
throw AssertionError if you prefer that contract; modify the KDoc and/or the
check usage so the documented exception type matches the actual thrown type.

---

Nitpick comments:
In
`@app/src/androidTest/kotlin/com/itsaky/androidide/helper/DevicePermissionGrantUiHelper.kt`:
- Around line 136-147: The code currently calls
root.findAccessibilityNodeInfosByText(...) and recycles root only in the outer
finally, but if findAccessibilityNodeInfosByText or action(node) throws some
nodes may never be recycled; change the structure so root and every node are
always recycled: after obtaining nodes from
root.findAccessibilityNodeInfosByText(searchText) wrap the node iteration in a
try/finally where each node is recycled in an inner finally (e.g., for each node
do try { success = success || action(node) } finally { node.recycle() }), and
then in the outer finally call root.recycle(); ensure you reference the existing
symbols root, nodes, action(node), node.recycle(), root.recycle(), and
findAccessibilityNodeInfosByText(searchText) when applying the change.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: a6c69f5e-4ac6-4274-ae71-f193ee5f9cb4

📥 Commits

Reviewing files that changed from the base of the PR and between 8fc7e66 and 71f7307.

📒 Files selected for processing (2)
  • app/src/androidTest/kotlin/com/itsaky/androidide/helper/DevicePermissionGrantUiHelper.kt
  • app/src/androidTest/kotlin/com/itsaky/androidide/scenarios/InitializationProjectAndCancelingBuildScenario.kt
🚧 Files skipped from review as they are similar to previous changes (1)
  • app/src/androidTest/kotlin/com/itsaky/androidide/scenarios/InitializationProjectAndCancelingBuildScenario.kt

@hal-eisen-adfa hal-eisen-adfa merged commit 8c1620d into stage Apr 16, 2026
2 checks passed
@hal-eisen-adfa hal-eisen-adfa deleted the ADFA-2738-connectedV8DebugAndroidTest-part3-projects-actually branch April 16, 2026 18:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants