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
1 change: 1 addition & 0 deletions .github/workflows/discord-ci-notify.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ on:
- CI
- Expo versions
- Gryt Git notification
- Phone flows
- Release Android
- Release iOS
- Release Mobile
Expand Down
117 changes: 117 additions & 0 deletions .github/workflows/phone-flows.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
name: Phone flows

on:
schedule:
- cron: "0 3 * * *"
workflow_dispatch:
pull_request:
paths:
- ".maestro/**"
- ".github/workflows/phone-flows.yml"

concurrency:
group: phone-flows
cancel-in-progress: true

jobs:
flows:
name: Tap through the app
runs-on: xcode-27
timeout-minutes: 60
env:
LANG: en_US.UTF-8
LC_ALL: en_US.UTF-8
MAESTRO_CLI_NO_ANALYTICS: "1"
MAESTRO_DRIVER_STARTUP_TIMEOUT: "300000"

steps:
- uses: actions/checkout@v4

- uses: actions/setup-node@v4
with:
node-version: 24
cache: yarn

- uses: actions/setup-java@v4
with:
distribution: temurin
java-version: 17

- name: Select Xcode 27.1
run: sudo xcode-select -s /Applications/Xcode_27.1.app

- run: yarn install --frozen-lockfile

- name: Install Maestro
run: |
curl -fsSL --retry 5 --retry-all-errors -o maestro.zip https://github.com/mobile-dev-inc/maestro/releases/download/cli-2.12.0/maestro.zip
echo "a6044a84542f522f434c5dc3d609e60c7446d423f3ed1b2ae513f5c48663ec5e maestro.zip" | shasum -a 256 -c -
unzip -q maestro.zip -d "$HOME/maestro-cli"
echo "$HOME/maestro-cli/maestro/bin" >> "$GITHUB_PATH"

- uses: actions/checkout@v4
with:
repository: Gryt-chat/server
path: test-server

# There is no Docker on macOS runners, so the server runs from source.
- name: Start a throwaway Gryt server
working-directory: test-server
env:
PORT: "5077"
HOST: 127.0.0.1
DATA_DIR: ${{ runner.temp }}/gryt-data
DISABLE_S3: "true"
GRYT_IDENTITY_TIERS: local,account
run: |
yarn install --frozen-lockfile
export JWT_SECRET=$(openssl rand -hex 32)
start() { nohup node -r ts-node/register src/index.ts > "$RUNNER_TEMP/server.log" 2>&1 & echo $! > "$RUNNER_TEMP/server.pid"; }
ready() { for _ in $(seq 1 60); do curl -sf http://127.0.0.1:5077/info > /dev/null && return 0; sleep 2; done; return 1; }
start
ready
# A new server only takes invites, and no env var changes that. /info reads it at boot.
sqlite3 "$DATA_DIR/gryt.db" "UPDATE server_config SET join_policy='open', display_name='Maestro test', is_configured=1"
kill "$(cat "$RUNNER_TEMP/server.pid")"
sleep 2
start
ready
curl -s http://127.0.0.1:5077/info | grep -q '"joinPolicy":"open"'

# Named devices differ between runner images, so take an iPhone on the newest runtime it has.
- name: Pick a simulator
run: |
DEVICE=$(xcrun simctl list devices available -j | node -e '
const { devices } = JSON.parse(require("fs").readFileSync(0, "utf8"));
const version = (k) => k.split("iOS-")[1].split("-").map(Number);
const newest = Object.keys(devices).filter((k) => k.includes("iOS-") && devices[k].some((d) => d.name.startsWith("iPhone")))
.sort((a, b) => version(b)[0] - version(a)[0] || version(b)[1] - version(a)[1])[0];
console.log(devices[newest].find((d) => d.name.startsWith("iPhone")).udid);
')
test -n "$DEVICE"
echo "DEVICE=$DEVICE" >> "$GITHUB_ENV"

- name: Build for the simulator
run: |
set -o pipefail
yarn expo prebuild --platform ios --no-install
(cd ios && pod install)
xcodebuild -workspace ios/Gryt.xcworkspace -scheme Gryt -configuration Release \
-destination "platform=iOS Simulator,id=$DEVICE" -derivedDataPath build/sim \
CODE_SIGN_IDENTITY=- CODE_SIGN_STYLE=Manual DEVELOPMENT_TEAM=8883W2XTQ8 PROVISIONING_PROFILE_SPECIFIER= \
build | tail -20

- name: Run the flows
run: |
xcrun simctl boot "$DEVICE"
xcrun simctl bootstatus "$DEVICE"
xcrun simctl install "$DEVICE" build/sim/Build/Products/Release-iphonesimulator/Gryt.app
maestro test -e GRYT_TEST_SERVER=127.0.0.1:5077 .maestro

- uses: actions/upload-artifact@v4
if: failure()
with:
name: maestro-debug
path: |
~/.maestro/tests
${{ runner.temp }}/server.log
20 changes: 20 additions & 0 deletions .maestro/add-server-from-drawer.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
appId: chat.gryt.app
name: Add a server from the server drawer
---
- runFlow: subflows/join.yaml
- tapOn: "Maestro test.*"
- assertVisible: "Your servers"
# Outside the drawer, which has to close it.
- tapOn:
point: "90%,50%"
- assertNotVisible: "Your servers"
- tapOn: "Maestro test.*"
- tapOn: "Add a server"
# It froze here once: the sheet never showed and no touch got through.
- extendedWaitUntil:
visible: "Invite link or server address"
timeout: 5000
- inputText: "127.0.0.1:5077"
- extendedWaitUntil:
visible: "Maestro test"
timeout: 10000
4 changes: 4 additions & 0 deletions .maestro/config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
flows:
- "*.yaml"
excludeTags:
- subflow
17 changes: 17 additions & 0 deletions .maestro/message-long-press.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
appId: chat.gryt.app
name: Send a message and long-press it
---
- runFlow: subflows/join.yaml
- tapOn: "General"
- tapOn: "Message #General.*"
- inputText: "**bold** hello"
- tapOn: "Send"
- tapOn: "Agree"
- extendedWaitUntil:
visible: ".*bold hello. Hold for actions"
timeout: 5000
- longPressOn: ".*bold hello. Hold for actions"
- assertVisible: "Reply"
- assertVisible: "Copy text"
- assertVisible: ".*bold hello.*"
- assertNotVisible: ".*\\*\\*bold.*"
28 changes: 28 additions & 0 deletions .maestro/subflows/join.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
appId: chat.gryt.app
tags:
- subflow
---
- launchApp:
clearState: true
clearKeychain: true
- extendedWaitUntil:
visible: "Get started"
timeout: 15000
- tapOn: "Get started"
- tapOn: "Add a server"
- inputText: ${GRYT_TEST_SERVER}
# iOS's first-run keyboard tip, which covers the bottom half of a fresh simulator.
- runFlow:
when:
visible: "Continue"
commands:
- tapOn: "Continue"
- extendedWaitUntil:
visible: "Add Maestro test"
timeout: 10000
- tapOn: "Add Maestro test"
- tapOn: "Not now"
- tapOn: "Skip"
- extendedWaitUntil:
visible: "General"
timeout: 20000
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,16 @@ CocoaPods reading its own path, not anything about this project:
LANG=en_US.UTF-8 LC_ALL=en_US.UTF-8 npx expo prebuild --platform ios
```

## Tapping through it: Maestro

The flows in `.maestro/` go through the app on a simulator the way a person would. CI runs them every night and on any PR that changes them. To run them yourself you need Java 17, the [Maestro CLI](https://github.com/mobile-dev-inc/maestro), a Release simulator build and a Gryt server called "Maestro test" with open joining. The flows are in `.github/workflows/phone-flows.yml` step by step if you need the server setup.

```sh
maestro test -e GRYT_TEST_SERVER=127.0.0.1:5077 .maestro
```

A failed run leaves its screenshots in `~/.maestro/tests`. Every flow starts from a wiped app and joins the server again.

## Giving it to somebody else: TestFlight

`expo run:ios --device` above installs onto a phone you're holding. This is the
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
"@gryt/owl": "^0.7.0",
"@gryt/theme": "^0.13.0",
"@gryt/ui": "^0.46.0",
"@gryt/ui-native": "^0.18.1",
"@gryt/ui-native": "^0.18.2",
"@gryt/voice": "0.9.1",
"@noble/ciphers": "^2.4.0",
"@noble/curves": "^2.3.0",
Expand Down
8 changes: 4 additions & 4 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1357,10 +1357,10 @@
resolved "https://registry.yarnpkg.com/@gryt/theme/-/theme-0.13.0.tgz#640f3049b53cfe427f0067cfc5faeffbd04d36aa"
integrity sha512-7CW/wBKZFL1bfk5xQcJtvdoD+rbCvmHeA0U2uTQMQ9gfXAvIbPx7GL+TjMYYouTtLdJ/OKFARK+NXuS0RaBveg==

"@gryt/ui-native@^0.18.1":
version "0.18.1"
resolved "https://registry.yarnpkg.com/@gryt/ui-native/-/ui-native-0.18.1.tgz#a2c7eb1097951330fc719c41b179ac3dd11513d5"
integrity sha512-5KLrUEY2gMIaapTy7LRyX2V7cLbU5Uf03dyoz0KVmW5kIfqEUu8AXinEliczUUREEL6Isbh7GIM/hrMSDtDCxQ==
"@gryt/ui-native@^0.18.2":
version "0.18.2"
resolved "https://registry.yarnpkg.com/@gryt/ui-native/-/ui-native-0.18.2.tgz#ee468e0af96e93b90c06ddb44b0617dd8c5c6fb7"
integrity sha512-6QN/ZesYfsvbTPq2sibguPIoCavY9M3eJiCAqXdoNy8G2BLsJGLJi7Bcaf/gmnwsy3/0NcYnlhxgfGDkQZn47w==
dependencies:
"@gryt/owl" "^0.7.0"
"@gryt/theme" "^0.13.0"
Expand Down
Loading