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
19 changes: 18 additions & 1 deletion .github/README-AI.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ The PR reviewer agent conducts thorough, constructive code reviews of .NET MAUI

### How to Use

#### Option 1: GitHub Copilot CLI (Local)

```bash
# Start GitHub Copilot CLI with agent support
copilot --allow-all-tools --allow-all-paths
Expand All @@ -19,14 +21,29 @@ copilot --allow-all-tools --allow-all-paths
please review <link to PR>
```

### Example
#### Example

```bash
copilot --allow-all-tools --allow-all-paths
/agent pr-reviewer
please review https://github.com/dotnet/maui/pull/32372
```

#### Option 2: GitHub Copilot Agents (Web)

1. **Navigate to the agents tab** at https://github.com/copilot/agents

2. **Select your repository and branch** using the dropdown menus in the text box

3. **Choose your agent** from the dropdown (pr-reviewer)

4. **Enter a task** in the text box:
- For PR reviews: `Please review this PR: https://github.com/dotnet/maui/pull/XXXXX`

5. **Click Start task** or press Return

6. **Follow the agent's progress** - The agent task will appear below the text box. Click into it to see live updates.

## What the Agent Does

Every PR review includes:
Expand Down
86 changes: 84 additions & 2 deletions .github/instructions/appium-control.instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,16 @@ description: "Quick reference for creating standalone Appium control scripts for

Create standalone C# scripts for manual Appium-based debugging and exploration of .NET MAUI apps. Use these when you need direct control outside of automated tests.

**🚨 CRITICAL: Use .NET 10 Native Scripting (NOT dotnet-script)**

This repository uses **.NET 10's built-in scripting features** with the `#:package` directive. Do NOT use `dotnet-script` or the old `#r` directive syntax.

**🚨 CRITICAL: Appium is the ONLY way to interact with device UI**

- ✅ **ALWAYS use Appium** for tapping, swiping, finding elements, rotating device, etc.
- ❌ **NEVER use** `adb shell input tap`, `adb shell input swipe`, or coordinate-based commands
- **Why**: Appium provides reliable element location, proper waits, and cross-device compatibility

**Common Command Patterns**: For UDID extraction, device boot, and build patterns, see [Common Testing Patterns](common-testing-patterns.md).

## When to Use
Expand All @@ -15,6 +25,7 @@ Create standalone C# scripts for manual Appium-based debugging and exploration o
- **Investigation** - Reproduce issues or explore edge cases
- **Learning** - Understand how Appium interacts with MAUI apps
- **Prototyping** - Test Appium interactions before creating full UI tests
- **PR validation** - Testing PRs that affect UI behavior

**Not for automated testing** - For automated UI tests, use the established test infrastructure in `src/Controls/tests/`.

Expand Down Expand Up @@ -90,6 +101,8 @@ The fastest way to experiment with Appium is using the Sandbox app (`src/Control

# Run your script (from SandboxAppium/ directory)
cd SandboxAppium

# Run with .NET 10's native scripting (NOT dotnet-script)
dotnet run yourscript.cs
```

Expand All @@ -116,7 +129,12 @@ Ensure Appium server is running on `http://localhost:4723` before running your s

## Basic Template

**IMPORTANT: Create script files inside project directory (e.g., `SandboxAppium/`), not in repository root or `/tmp`.**
**🚨 CRITICAL: .NET 10 Scripting Requirements**

1. **Use `#:package` directive** (NOT `#r` or dotnet-script syntax)
2. **Create scripts inside project directory** (e.g., `SandboxAppium/`), not in `/tmp` or repository root
3. **Run with `dotnet run`** (NOT `dotnet script` or `dotnet-script`)
4. **Requires .NET 10 SDK** - These scripts use .NET 10's native scripting features

```csharp
#:package Appium.WebDriver@8.0.1
Expand Down Expand Up @@ -230,6 +248,14 @@ catch (Exception ex)

## Running the Script

**🚨 CRITICAL: .NET 10 Native Scripting (NOT dotnet-script)**

- ✅ **DO**: Use `dotnet run yourscript.cs` (.NET 10 native scripting)
- ✅ **DO**: Use `#:package Appium.WebDriver@8.0.1` directive
- ❌ **DON'T**: Use `dotnet script` or `dotnet-script` commands
- ❌ **DON'T**: Use `#r` directive syntax (that's for dotnet-script, not .NET 10)
- ❌ **DON'T**: Create scripts in `/tmp` or repository root

**⚠️ Important: Create scripts inside project directory (e.g., `SandboxAppium/`), not in `/tmp` or repository root.**

```bash
Expand All @@ -255,6 +281,62 @@ dotnet run yourscript.cs

**Complete workflow:** See [Quick Start with Sandbox App](#quick-start-with-sandbox-app) above for full end-to-end example.

## ❌ Wrong vs ✅ Right Approach

### Opening a Flyout Menu

**❌ WRONG - Using ADB commands (brittle, unreliable)**:
```bash
# DON'T DO THIS - coordinates are device-specific and unreliable
adb shell input tap 100 100 # Guess where hamburger menu is
sleep 2 # Hope it opened
```

**✅ RIGHT - Using Appium (reliable, verifiable)**:
```csharp
// Find the hamburger menu by its accessibility properties
var flyoutButton = driver.FindElement(
MobileBy.XPath("//android.widget.ImageButton[@content-desc='Open navigation drawer']")
);
flyoutButton.Click();

// Verify it actually opened
var flyoutItems = driver.FindElements(MobileBy.XPath("//android.widget.TextView[contains(@text, 'Item')]"));
Console.WriteLine($"Flyout opened with {flyoutItems.Count} items");
```

### Rotating Device

**❌ WRONG - Using simctl/ADB**:
```bash
# DON'T DO THIS - doesn't guarantee app orientation changed
adb shell content insert --uri content://settings/system --bind name:s:user_rotation --bind value:i:1
```

**✅ RIGHT - Using Appium**:
```csharp
// Appium handles rotation properly and waits for completion
driver.Orientation = ScreenOrientation.Landscape;

// Verify rotation succeeded
if (driver.Orientation != ScreenOrientation.Landscape)
{
Console.WriteLine("❌ Rotation failed!");
}
```

### Taking Screenshots

**✅ BOTH are acceptable** (Appium preferred for consistency):
```csharp
// Appium (preferred - works cross-platform)
var screenshot = driver.GetScreenshot();
screenshot.SaveAsFile("/tmp/screenshot.png");

// ADB (acceptable for Android-only scenarios)
// adb exec-out screencap -p > screenshot.png
```

## Common Appium Operations

### Finding and Interacting with Elements
Expand Down Expand Up @@ -349,7 +431,7 @@ For Shell-specific testing patterns (e.g., opening flyouts), see [UI Tests Instr
**"Device not found" (Android)**
- Verify DEVICE_UDID is set: `echo $DEVICE_UDID`
- List devices: `adb devices`
- Start emulator via Android Studio or: `emulator -avd [avd-name]`
- Start emulator: See [Common Testing Patterns: Android Emulator Startup](common-testing-patterns.md#android-emulator-startup-with-error-checking) for the correct background daemon pattern

**"Appium server keeps stopping"**
- Check if port 4723 is already in use: `lsof -i :4723`
Expand Down
87 changes: 87 additions & 0 deletions .github/instructions/common-testing-patterns.md
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,93 @@ echo "Simulator is booted and ready"

---

### Android Emulator Startup with Error Checking

**Used in**: PR reviews, investigation work

**Pattern**:
```bash
# Clean up any existing emulator processes
pkill -9 qemu-system-x86_64 2>/dev/null || true
pkill -9 emulator 2>/dev/null || true
sleep 2

# CRITICAL: Start emulator as background daemon that survives session end
# Use subshell with & to fully detach from current session
cd $ANDROID_HOME/emulator && (./emulator -avd Pixel_9 -no-snapshot-load -no-audio -no-boot-anim > /tmp/emulator.log 2>&1 &)

# Wait a moment for process to start
sleep 3

# Verify emulator process started
EMULATOR_PID=$(ps aux | grep "qemu.*Pixel_9" | grep -v grep | awk '{print $2}')
if [ -z "$EMULATOR_PID" ]; then
echo "❌ ERROR: Emulator failed to start"
exit 1
fi
echo "✅ Emulator started as background daemon (PID: $EMULATOR_PID)"

# Wait for device to appear
echo "Waiting for device to appear..."
adb wait-for-device

# Wait for boot to complete
echo "Waiting for boot to complete..."
until [ "$(adb shell getprop sys.boot_completed 2>/dev/null)" = "1" ]; do
sleep 2
echo -n "."
done
echo ""

# Get device UDID
export DEVICE_UDID=$(adb devices | grep -v "List" | grep "device" | awk '{print $1}' | head -1)

# Verify device is ready
if [ -z "$DEVICE_UDID" ]; then
echo "❌ ERROR: Emulator started but device not found"
exit 1
fi

# Check API level
API_LEVEL=$(adb shell getprop ro.build.version.sdk)
echo "✅ Emulator ready: $DEVICE_UDID (API $API_LEVEL)"
```

**When to use**: Starting Android emulator for testing

**Why this pattern is critical**:

The subshell `()` with `&` pattern is essential for emulator persistence:
- **Problem**: Using `mode="async"` attaches the emulator to the bash session, causing it to be killed when the session ends
- **Root cause**: Background processes attached to sessions are terminated during cleanup, even with `nohup`
- **Solution**: Subshell `()` creates a new process group that's detached from the current session
- **Result**: Emulator persists even when AI agent finishes responding or sessions end

**Wrong approach (emulator dies)**:
```bash
# DON'T DO THIS - emulator will be killed when session ends
bash --mode=async ./emulator -avd Pixel_9 &
```

**Correct approach (emulator persists)**:
```bash
# Subshell with & fully detaches the process
cd $ANDROID_HOME/emulator && (./emulator -avd Pixel_9 ... &)
```

**Critical details**:
- The emulator command must be run from `$ANDROID_HOME/emulator` directory. Running from other directories causes "Qt library not found" and "qemu-system not found" errors
- **Use subshell `()` with `&`** to start emulator as true background daemon that persists after bash session ends
- **NEVER use `adb kill-server`** - This disconnects ALL active ADB connections and causes emulators to lose connection. Almost never necessary
- **NEVER use `mode="async"` for emulators** - Processes will be terminated when the session ends
- **Check first**: Run `adb devices` before starting - if device is already visible, no action needed

**Available emulators**: List with `emulator -list-avds`

**To verify persistence**: After starting emulator, note the PID, finish the current task, then check if the process still exists with `ps aux | grep <PID>`

---

## 3. Build Patterns

### Sandbox App Build (iOS)
Expand Down
37 changes: 37 additions & 0 deletions .github/instructions/pr-reviewer-agent/core-guidelines.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,43 @@ When multiple instruction files exist, follow this priority order:
- This clearly identifies agent-generated PRs containing review feedback and suggested improvements
- Example: `[PR-Reviewer] Fix RTL padding for CollectionView on iOS`

## 🤖 UI Automation: ALWAYS Use Appium

**CRITICAL RULE: For ANY device UI interaction, use Appium - NEVER use direct ADB/simctl commands**

**✅ CORRECT - Use Appium for**:
- Tapping buttons, controls, menu items
- Opening menus, drawers, flyouts
- Scrolling, swiping, gestures
- Entering text
- Rotating device orientation
- Taking screenshots
- Finding and verifying UI elements
- Any interaction a user would perform

**❌ WRONG - Never use these for UI interaction**:
- `adb shell input tap` - Use Appium element tap instead
- `adb shell input swipe` - Use Appium gestures instead
- `adb shell input text` - Use Appium SendKeys instead
- `xcrun simctl ui` - Use Appium iOS interactions instead

**When ADB/simctl ARE acceptable**:
- ✅ `adb devices` - Check device connection
- ✅ `adb logcat` - Monitor logs (read-only)
- ✅ `adb shell getprop` - Read device properties (read-only)
- ✅ `xcrun simctl list` - List simulators
- ✅ `xcrun simctl boot` - Boot simulator
- ✅ Device setup/configuration (not UI interaction)

**Why this matters**:
- Appium provides reliable element location (no guessing coordinates)
- Appium waits for elements to be ready
- Appium verifies actions succeeded
- Appium works across different screen sizes/orientations
- Coordinate-based taps are brittle and unreliable

**Reference**: See [Appium Control Scripts Instructions](../appium-control.instructions.md) for creating Appium test scripts

**Why this matters**: Code review alone is insufficient. Many issues only surface when running actual code on real platforms with real scenarios. Your testing often reveals edge cases and issues the PR author didn't consider.

**NEVER GIVE UP Principle**:
Expand Down
9 changes: 9 additions & 0 deletions .github/instructions/pr-reviewer-agent/testing-guidelines.md
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,9 @@ fi
```

**Android Testing**:

**CRITICAL**: If starting an emulator, use the background daemon pattern from [Common Testing Patterns: Android Emulator Startup](../../instructions/common-testing-patterns.md#android-emulator-startup-with-error-checking) to ensure it persists across sessions.

```bash
# Get connected device/emulator
export DEVICE_UDID=$(adb devices | grep -v "List" | grep "device" | awk '{print $1}' | head -1)
Expand All @@ -169,6 +172,12 @@ if [ -z "$DEVICE_UDID" ]; then
fi
```

**Important Android Rules**:
- ✅ **Start emulators with subshell + background**: `cd $ANDROID_HOME/emulator && (./emulator -avd Name ... &)`
- ❌ **NEVER use `adb kill-server`** - This disconnects active emulators and is almost never needed
- ❌ **NEVER use `mode="async"` for emulators** - They will be killed when the session ends
- ✅ **Check `adb devices` first** - If device is visible, no action needed

## Build and Deploy

**iOS**:
Expand Down
Loading