Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add run-maestro-ios-runner.sh script #2042

Merged
merged 6 commits into from
Sep 11, 2024
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
2 changes: 1 addition & 1 deletion .github/workflows/test-e2e.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ jobs:
cache: gradle

- name: Build xctest-runner
run: ./maestro-ios-xctest-runner/build-maestro-ios-runner.sh
run: ./maestro-ios-xctest-runner/build-maestro-ios-runner.sh | xcbeautify

- name: Build Maestro CLI
run: ./gradlew :maestro-cli:distZip
Expand Down
98 changes: 75 additions & 23 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,24 +24,27 @@ public Maestro Slack channel.

Once your PR is merged, it usually takes about a week until it becomes publicly available and included into the next release.

## Testing
## Developing

There are 3 ways to test your changes:
### Requirements

- Integration tests
- Run them via `./gradlew :maestro-test:test` (or from IDE)
- Tests are using real implementation of most components except for `Driver`. We use `FakeDriver` which pretends to be a real device.
- Manual testing
- Run `./maestro` instead of `maestro` to use your local code.
- Unit tests
- All the other tests in the projects. Run them via `./gradlew test` (or from IDE)
Maestro's minimal deployment target is Java 8, and we strive to keep it this way
for as long possible, because our analytics indicate that (as of September 2024) many
users still use Java 8.

For development, you need to use Java 11 or newer.

If you made changes to the CLI, rebuilt it with `./gradlew :maestro-cli:installDist`. This will generate a startup shell
script in `./maestro-cli/build/install/maestro/bin/maestro`. Use it instead of globally installed `maestro`.

If you made changes to the iOS XCUITest driver, rebuild it by running `./maestro-ios-xctest-runner/build-maestro-ios-runner.sh`.
### Debugging

Maestro stores logs for every test run in the following locations:

- CLI Logs: `~/.maestro/tests/*/maestro.log`
- iOS test runner logs: `~/Library/Logs/maestro/xctest_runner_logs`

### Running on Android
### Android artifacts

Maestro requires 2 artifacts to run on Android:

Expand All @@ -51,7 +54,7 @@ Maestro requires 2 artifacts to run on Android:
These artifacts are built by `./gradlew :maestro-android:assemble` and `./gradlew :maestro-android:assembleAndroidTest`, respectively.
They are placed in `maestro-android/build/outputs/apk`, and are copied over to `maestro-client/src/main/resources`.

### Running on iOS
### iOS artifacts

Maestro requires 3 artifacts to run on iOS:

Expand All @@ -61,24 +64,73 @@ Maestro requires 3 artifacts to run on iOS:

These artifacts are built by the `build-maestro-ios-runner.sh` script. It places them in `maestro-ios-driver/src/main/resources`.

### Artifacts and the CLI
### Running standalone iOS XCTest runner app

`maestro-cli` depends on both `maestro-ios-driver` and `maestro-client`. This is how the CLI gets these artifacts.
The iOS XCTest runner can be run without Maestro CLI. To do so, make sure you built the artifacts, and then run:

## Developing
```console
./maestro-ios-xctest-runner/run-maestro-ios-runner.sh
```

Maestro's minimal deployment target is Java 8, and we strive to keep it this way
for as long possible, because our analytics indicate many users still use that
version.
This will use `xcodebuild test-without-building` to run the test runner on the connected iOS device. Now, you can reach
the HTTP server that runs inside the XCTest runner app (by default on port 22087):

For development, you need to use Java 11 or newer.
```console
curl -fsSL -X GET localhost:22087/deviceInfo | jq
```

## Debugging
<details>
<summary>See example output</summary>

Maestro stores logs for every test run in the following locations:
```json
{
"heightPoints": 852,
"heightPixels": 2556,
"widthPixels": 1179,
"widthPoints": 393
}
```

- CLI Logs: `~/.maestro/tests/*/maestro.log`
- iOS test runner logs: `~/Library/Logs/maestro/xctest_runner_logs`
</details>

```console
curl -fsSL -X POST localhost:22087/touch -d '
{
"x": 150,
"y": 150,
"duration": 0.2
}'
```

```console
curl -sSL -X GET localhost:22087/swipe -d '
{
"startX": 150,
"startY": 426,
"endX": 426,
"endY": 350,
"duration": 1
}'
```


### Artifacts and the CLI

`maestro-cli` depends on both `maestro-ios-driver` and `maestro-client`. This is how the CLI gets these artifacts.

## Testing

There are 3 ways to test your changes:

- Integration tests
- Run them via `./gradlew :maestro-test:test` (or from IDE)
- Tests are using real implementation of most components except for `Driver`. We use `FakeDriver` which pretends to be a real device.
- Manual testing
- Run `./maestro` instead of `maestro` to use your local code.
- Unit tests
- All the other tests in the projects. Run them via `./gradlew test` (or from IDE)

If you made changes to the iOS XCUITest driver, rebuild it by running `./maestro-ios-xctest-runner/build-maestro-ios-runner.sh`.

## Architectural considerations

Expand Down
51 changes: 28 additions & 23 deletions maestro-ios-xctest-runner/build-maestro-ios-runner.sh
Original file line number Diff line number Diff line change
@@ -1,33 +1,38 @@
#!/usr/bin/env sh

set -eu
#!/usr/bin/env bash
set -euo pipefail

if [ "$(basename "$PWD")" != "maestro" ]; then
echo "This script must be run from the maestro root directory"
exit 1
echo "This script must be run from the maestro root directory"
exit 1
fi

## Build the UI test
rm -rf ./build/Products

rm -rf ./build/Products || exit 1
xcodebuild ARCHS="x86_64 arm64" \
ONLY_ACTIVE_ARCH=NO \
-project ./maestro-ios-xctest-runner/maestro-driver-ios.xcodeproj \
-scheme maestro-driver-ios \
-sdk iphonesimulator \
-destination "generic/platform=iOS Simulator" \
-IDEBuildLocationStyle=Custom \
-IDECustomBuildLocationType=Absolute \
-IDECustomBuildProductsPath="$PWD/build/Products" \
build-for-testing || exit 1
xcodebuild \
ARCHS="x86_64 arm64" \
ONLY_ACTIVE_ARCH=NO \
-project ./maestro-ios-xctest-runner/maestro-driver-ios.xcodeproj \
-scheme maestro-driver-ios \
-sdk iphonesimulator \
-destination "generic/platform=iOS Simulator" \
-IDEBuildLocationStyle=Custom \
-IDECustomBuildLocationType=Absolute \
-IDECustomBuildProductsPath="$PWD/build/Products" \
build-for-testing

## Remove intermediates, output and copy runner in maestro-ios-driver
mv "$PWD"/build/Products/Debug-iphonesimulator/maestro-driver-iosUITests-Runner.app ./maestro-ios-driver/src/main/resources/maestro-driver-iosUITests-Runner.app || exit 1
cp -r \
./build/Products/Debug-iphonesimulator/maestro-driver-iosUITests-Runner.app \
./maestro-ios-driver/src/main/resources/maestro-driver-iosUITests-Runner.app

mv "$PWD"/build/Products/Debug-iphonesimulator/maestro-driver-ios.app ./maestro-ios-driver/src/main/resources/maestro-driver-ios.app || exit 1
cp -r \
./build/Products/Debug-iphonesimulator/maestro-driver-ios.app \
./maestro-ios-driver/src/main/resources/maestro-driver-ios.app

mv "$PWD"/build/Products/*.xctestrun ./maestro-ios-driver/src/main/resources/maestro-driver-ios-config.xctestrun || exit 1
cp \
./build/Products/*.xctestrun \
./maestro-ios-driver/src/main/resources/maestro-driver-ios-config.xctestrun

(cd ./maestro-ios-driver/src/main/resources && zip -r maestro-driver-iosUITests-Runner.zip ./maestro-driver-iosUITests-Runner.app) || exit 1
(cd ./maestro-ios-driver/src/main/resources && zip -r maestro-driver-ios.zip ./maestro-driver-ios.app) || exit 1
rm -r ./maestro-ios-driver/src/main/resources/*.app || exit 1
(cd ./maestro-ios-driver/src/main/resources && zip -r maestro-driver-iosUITests-Runner.zip ./maestro-driver-iosUITests-Runner.app)
(cd ./maestro-ios-driver/src/main/resources && zip -r maestro-driver-ios.zip ./maestro-driver-ios.app)
rm -r ./maestro-ios-driver/src/main/resources/*.app
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,16 @@ struct SwipeRouteHandler: HTTPHandler {
)

func handleRequest(_ request: FlyingFox.HTTPRequest) async throws -> FlyingFox.HTTPResponse {
guard let requestBody = try? JSONDecoder().decode(SwipeRequest.self, from: request.body) else {
return AppError(type: .precondition, message: "incorrect request body provided for swipe request").httpResponse
let requestBody: SwipeRequest
do {
requestBody = try JSONDecoder().decode(SwipeRequest.self, from: request.body)
} catch {
return AppError(
type: .precondition,
message: "incorrect request body provided for swipe request: \(error)"
).httpResponse
}


do {
try await swipePrivateAPI(
Expand Down
30 changes: 30 additions & 0 deletions maestro-ios-xctest-runner/run-maestro-ios-runner.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
#!/usr/bin/env bash
set -euo pipefail

if [ "$(basename "$PWD")" != "maestro" ]; then
echo "This script must be run from the maestro root directory"
exit 1
fi

DEVICE="${1:-}"
if [ -z "$DEVICE" ]; then
DEVICE="iPhone 15"
echo "No device passed, will default to $DEVICE"
fi

xctestrun_file="$(find ./build/Products -maxdepth 1 -name '*.xctestrun' -print)"
file_count="$(echo "$xctestrun_file" | wc -l | tr -d '[:blank:]')"
if [ "$file_count" = 1 ]; then
echo "xctestrun file found: $xctestrun_file"
elif [ "$file_count" = 0 ]; then
echo "xctestrun file not found in ./build/Products. Did you build the runner?"
exit 1
else
echo "Multiple ($file_count) xctestrun files found in ./build/Products. Only 1 can be present."
exit 1
fi

xcodebuild test-without-building \
-xctestrun "$xctestrun_file" \
-destination "platform=iOS Simulator,name=$DEVICE" \
-destination-timeout 1
Loading