Skip to content

Commit b11661a

Browse files
committed
chore(dotnet-maui): add integration tests
1 parent 578403c commit b11661a

File tree

11 files changed

+1295
-0
lines changed

11 files changed

+1295
-0
lines changed
Lines changed: 367 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,367 @@
1+
#
2+
# .github/workflows/dotnet-maui-browserstack.yml
3+
# Workflow for building and testing dotnet-maui on BrowserStack physical devices
4+
#
5+
---
6+
name: dotnet-maui-browserstack
7+
8+
on:
9+
pull_request:
10+
branches:
11+
- main
12+
- 'sdk-*'
13+
paths:
14+
- 'dotnet-maui/**'
15+
- '.github/workflows/dotnet-maui-browserstack.yml'
16+
push:
17+
branches:
18+
- main
19+
- 'sdk-*'
20+
paths:
21+
- 'dotnet-maui/**'
22+
- '.github/workflows/dotnet-maui-browserstack.yml'
23+
workflow_dispatch: # Allow manual trigger
24+
25+
concurrency:
26+
group: ${{ github.workflow }}-${{ github.ref }}
27+
cancel-in-progress: true
28+
29+
jobs:
30+
build-and-test:
31+
name: Build and Test on BrowserStack
32+
runs-on: macos-latest # Required for iOS builds
33+
34+
steps:
35+
- name: Checkout code
36+
uses: actions/checkout@v4
37+
38+
- name: Setup .NET
39+
uses: actions/setup-dotnet@v4
40+
with:
41+
dotnet-version: '9.0.x'
42+
43+
- name: Setup .NET MAUI workload
44+
run: dotnet workload install maui
45+
46+
- name: Create .env file
47+
run: |
48+
echo "DITTO_APP_ID=${{ secrets.DITTO_APP_ID }}" > .env
49+
echo "DITTO_PLAYGROUND_TOKEN=${{ secrets.DITTO_PLAYGROUND_TOKEN }}" >> .env
50+
echo "DITTO_AUTH_URL=${{ secrets.DITTO_AUTH_URL }}" >> .env
51+
echo "DITTO_WEBSOCKET_URL=${{ secrets.DITTO_WEBSOCKET_URL }}" >> .env
52+
53+
- name: Seed GitHub test document to Ditto Cloud
54+
id: seed-document
55+
run: |
56+
# Create unique document ID for this test run
57+
TIMESTAMP=$(date +%s)
58+
INVERTED_TIMESTAMP=$((9999999999 - TIMESTAMP))
59+
DOC_ID="github_test_${GITHUB_RUN_ID}_${GITHUB_RUN_NUMBER}"
60+
TITLE="GitHub Test Task ${{ github.run_id }}"
61+
62+
echo "doc_id=$DOC_ID" >> $GITHUB_OUTPUT
63+
echo "title=$TITLE" >> $GITHUB_OUTPUT
64+
65+
# Insert test document via Ditto Cloud HTTP API
66+
curl -X POST \
67+
-H 'Content-type: application/json' \
68+
-H "Authorization: Bearer ${{ secrets.DITTO_API_KEY }}" \
69+
-d "{
70+
\"statement\": \"INSERT INTO tasks DOCUMENTS (:newTask) ON ID CONFLICT DO UPDATE\",
71+
\"args\": {
72+
\"newTask\": {
73+
\"_id\": \"$DOC_ID\",
74+
\"title\": \"$TITLE\",
75+
\"done\": false,
76+
\"deleted\": false
77+
}
78+
}
79+
}" \
80+
"https://${{ secrets.DITTO_API_URL }}/api/v4/store/execute"
81+
82+
echo "Seeded test document: $DOC_ID with title: $TITLE"
83+
84+
- name: Build iOS app
85+
working-directory: dotnet-maui/DittoMauiTasksApp
86+
run: |
87+
# Build only the main app project for iOS (exclude UITests)
88+
dotnet build DittoMauiTasksApp.csproj -f net9.0-ios -c Release \
89+
-p:MtouchLink=None \
90+
-p:EnableAssemblyILStripping=false \
91+
-p:MtouchInterpreter=all \
92+
-p:MtouchDebug=true \
93+
-p:MtouchFastDev=true \
94+
-p:EnableLLVMOptimizations=false \
95+
-p:MtouchUseLlvm=false \
96+
-p:OptimizePNGs=false
97+
98+
99+
# Create IPA for BrowserStack upload
100+
dotnet publish DittoMauiTasksApp.csproj -f net9.0-ios -c Release \
101+
-p:RuntimeIdentifier=ios-arm64 \
102+
-p:ArchiveOnBuild=true \
103+
-p:EnableAssemblyILStripping=false \
104+
-p:MtouchLink=None \
105+
-p:MtouchInterpreter=all \
106+
-p:MtouchDebug=true \
107+
-p:MtouchFastDev=true \
108+
-p:EnableLLVMOptimizations=false \
109+
-p:MtouchUseLlvm=false \
110+
-p:OptimizePNGs=false \
111+
-p:TrimMode=partial \
112+
-p:PublishReadyToRun=false \
113+
-p:MtouchNoSymbolStrip=true
114+
115+
- name: Build Android app
116+
working-directory: dotnet-maui/DittoMauiTasksApp
117+
run: |
118+
# Build only the main app project for Android (exclude UITests)
119+
dotnet build DittoMauiTasksApp.csproj -f net9.0-android -c Release \
120+
-p:AndroidLinkMode=None \
121+
-p:RunAOTCompilation=false \
122+
-p:EnableProguard=false \
123+
-p:AndroidUseSharedRuntime=false
124+
125+
# Create APK for BrowserStack upload
126+
dotnet publish DittoMauiTasksApp.csproj -f net9.0-android -c Release \
127+
-p:RuntimeIdentifier=android-arm64 \
128+
-p:AndroidPackageFormat=apk \
129+
-p:AndroidLinkMode=None \
130+
-p:RunAOTCompilation=false \
131+
-p:EnableProguard=false
132+
133+
- name: Install Appium and drivers
134+
run: |
135+
npm install -g appium
136+
appium driver install uiautomator2
137+
appium driver install xcuitest
138+
139+
- name: Install BrowserStack SDK (Apple Silicon support)
140+
if: runner.arch == 'ARM64'
141+
run: |
142+
dotnet tool install browserstack-sdk --version 1.16.3 --create-manifest-if-needed
143+
dotnet browserstack-sdk setup-dotnet --dotnet-path "." --dotnet-version "9.0.x" --yes
144+
145+
- name: Build UI Test project
146+
working-directory: dotnet-maui/DittoMauiTasksApp.UITests
147+
run: |
148+
# Build UITests for .NET only (not for mobile platforms)
149+
dotnet restore --ignore-failed-sources
150+
dotnet build DittoMauiTasksApp.UITests.csproj -c Release -f net9.0
151+
152+
- name: Upload iOS app to BrowserStack
153+
id: upload-ios
154+
run: |
155+
# Find the IPA file
156+
IPA_PATH=$(find dotnet-maui/DittoMauiTasksApp/bin/Release/net9.0-ios/ios-arm64 -name "*.ipa" | head -n 1)
157+
158+
if [ -z "$IPA_PATH" ]; then
159+
echo "Error: No IPA file found"
160+
exit 1
161+
fi
162+
163+
echo "Uploading iOS app: $IPA_PATH"
164+
165+
IOS_UPLOAD_RESPONSE=$(curl -u "${{ secrets.BROWSERSTACK_USERNAME }}:${{ secrets.BROWSERSTACK_ACCESS_KEY }}" \
166+
-X POST "https://api-cloud.browserstack.com/app-automate/upload" \
167+
-F "file=@$IPA_PATH" \
168+
-F "custom_id=ditto-maui-ios-app")
169+
170+
echo "iOS upload response: $IOS_UPLOAD_RESPONSE"
171+
IOS_APP_URL=$(echo $IOS_UPLOAD_RESPONSE | jq -r .app_url)
172+
173+
if [ "$IOS_APP_URL" = "null" ] || [ -z "$IOS_APP_URL" ]; then
174+
echo "Error: Failed to upload iOS app"
175+
echo "Response: $IOS_UPLOAD_RESPONSE"
176+
exit 1
177+
fi
178+
179+
echo "ios_app_url=$IOS_APP_URL" >> $GITHUB_OUTPUT
180+
echo "iOS app uploaded successfully: $IOS_APP_URL"
181+
182+
- name: Upload Android app to BrowserStack
183+
id: upload-android
184+
run: |
185+
# Find the APK file
186+
APK_PATH=$(find dotnet-maui/DittoMauiTasksApp/bin/Release/net9.0-android/android-arm64 -name "*.apk" | head -n 1)
187+
188+
if [ -z "$APK_PATH" ]; then
189+
echo "Error: No APK file found"
190+
exit 1
191+
fi
192+
193+
echo "Uploading Android app: $APK_PATH"
194+
195+
ANDROID_UPLOAD_RESPONSE=$(curl -u "${{ secrets.BROWSERSTACK_USERNAME }}:${{ secrets.BROWSERSTACK_ACCESS_KEY }}" \
196+
-X POST "https://api-cloud.browserstack.com/app-automate/upload" \
197+
-F "file=@$APK_PATH" \
198+
-F "custom_id=ditto-maui-android-app")
199+
200+
echo "Android upload response: $ANDROID_UPLOAD_RESPONSE"
201+
ANDROID_APP_URL=$(echo $ANDROID_UPLOAD_RESPONSE | jq -r .app_url)
202+
203+
if [ "$ANDROID_APP_URL" = "null" ] || [ -z "$ANDROID_APP_URL" ]; then
204+
echo "Error: Failed to upload Android app"
205+
echo "Response: $ANDROID_UPLOAD_RESPONSE"
206+
exit 1
207+
fi
208+
209+
echo "android_app_url=$ANDROID_APP_URL" >> $GITHUB_OUTPUT
210+
echo "Android app uploaded successfully: $ANDROID_APP_URL"
211+
212+
- name: Run BrowserStack tests
213+
id: test-browserstack
214+
env:
215+
BROWSERSTACK_USERNAME: ${{ secrets.BROWSERSTACK_USERNAME }}
216+
BROWSERSTACK_ACCESS_KEY: ${{ secrets.BROWSERSTACK_ACCESS_KEY }}
217+
BROWSERSTACK_IOS_APP_URL: ${{ steps.upload-ios.outputs.ios_app_url }}
218+
BROWSERSTACK_ANDROID_APP_URL: ${{ steps.upload-android.outputs.android_app_url }}
219+
GITHUB_TEST_DOC_ID: ${{ steps.seed-document.outputs.doc_id }}
220+
GITHUB_TEST_TITLE: ${{ steps.seed-document.outputs.title }}
221+
GITHUB_RUN_ID: ${{ github.run_id }}
222+
GITHUB_RUN_NUMBER: ${{ github.run_number }}
223+
GITHUB_REF_NAME: ${{ github.ref_name }}
224+
working-directory: dotnet-maui/DittoMauiTasksApp.UITests
225+
run: |
226+
echo "Starting BrowserStack tests via SDK..."
227+
228+
# The BrowserStack SDK will handle platform-specific testing based on browserstack.yml
229+
if [ "${{ runner.arch }}" = "ARM64" ]; then
230+
# For Apple Silicon Macs
231+
./dotnet test DittoMauiTasksApp.UITests.csproj \
232+
--configuration Release \
233+
--logger "trx;LogFileName=browserstack-results.trx" \
234+
--logger "console;verbosity=detailed"
235+
else
236+
# For Intel Macs
237+
dotnet test DittoMauiTasksApp.UITests.csproj \
238+
--configuration Release \
239+
--logger "trx;LogFileName=browserstack-results.trx" \
240+
--logger "console;verbosity=detailed"
241+
fi
242+
243+
TEST_EXIT_CODE=$?
244+
echo "Tests completed with exit code: $TEST_EXIT_CODE"
245+
246+
# Generate a build ID for consistency
247+
BUILD_ID="browserstack_$(date +%s)"
248+
echo "build_id=$BUILD_ID" >> $GITHUB_OUTPUT
249+
250+
if [ $TEST_EXIT_CODE -eq 0 ]; then
251+
echo "✅ All BrowserStack tests passed"
252+
else
253+
echo "❌ Some BrowserStack tests failed"
254+
exit $TEST_EXIT_CODE
255+
fi
256+
257+
258+
- name: Collect test results
259+
if: always()
260+
run: |
261+
BUILD_ID="${{ steps.test-browserstack.outputs.build_id }}"
262+
263+
echo "Collecting test results..."
264+
echo "Build ID: $BUILD_ID"
265+
266+
# Copy test result files
267+
mkdir -p test-results
268+
269+
if [ -f "dotnet-maui/DittoMauiTasksApp.UITests/TestResults/browserstack-results.trx" ]; then
270+
cp "dotnet-maui/DittoMauiTasksApp.UITests/TestResults/browserstack-results.trx" test-results/
271+
echo "✅ BrowserStack test results collected"
272+
else
273+
echo "⚠️ BrowserStack test results not found"
274+
fi
275+
276+
# Check for BrowserStack logs and artifacts
277+
if [ -d "dotnet-maui/DittoMauiTasksApp.UITests/browserstack-artifacts" ]; then
278+
cp -r "dotnet-maui/DittoMauiTasksApp.UITests/browserstack-artifacts" test-results/
279+
echo "✅ BrowserStack artifacts collected"
280+
fi
281+
282+
# Check for any screenshots or other test artifacts
283+
find dotnet-maui/DittoMauiTasksApp.UITests -name "*.png" -o -name "*.jpg" -o -name "*.xml" | head -10
284+
285+
- name: Generate test report
286+
if: always()
287+
run: |
288+
BUILD_ID="${{ steps.test-browserstack.outputs.build_id }}"
289+
290+
# Create test report
291+
echo "# BrowserStack MAUI Test Report" > test-report.md
292+
echo "" >> test-report.md
293+
echo "**Test Document ID**: ${{ steps.seed-document.outputs.doc_id }}" >> test-report.md
294+
echo "**Test Document Title**: ${{ steps.seed-document.outputs.title }}" >> test-report.md
295+
echo "**Build Number**: ${{ github.run_number }}" >> test-report.md
296+
echo "**Build ID**: $BUILD_ID" >> test-report.md
297+
echo "" >> test-report.md
298+
299+
# Test Results Summary
300+
echo "## Test Results" >> test-report.md
301+
echo "" >> test-report.md
302+
echo "The BrowserStack SDK automatically ran tests on the following devices:" >> test-report.md
303+
echo "" >> test-report.md
304+
echo "### iOS Devices" >> test-report.md
305+
echo "- iPhone 15 (iOS 17)" >> test-report.md
306+
echo "- iPhone 14 (iOS 16)" >> test-report.md
307+
echo "- iPad Pro 12.9 2022 (iOS 16)" >> test-report.md
308+
echo "" >> test-report.md
309+
echo "### Android Devices" >> test-report.md
310+
echo "- Google Pixel 8 (Android 14)" >> test-report.md
311+
echo "- Samsung Galaxy S23 (Android 13)" >> test-report.md
312+
echo "- Google Pixel 6 (Android 12)" >> test-report.md
313+
echo "" >> test-report.md
314+
echo "View full results in the BrowserStack Dashboard: https://app-automate.browserstack.com/dashboard" >> test-report.md
315+
316+
- name: Upload test artifacts
317+
if: always()
318+
uses: actions/upload-artifact@v4
319+
with:
320+
name: test-results
321+
path: |
322+
dotnet-maui/DittoMauiTasksApp/bin/
323+
test-report.md
324+
325+
- name: Comment PR with results
326+
if: github.event_name == 'pull_request' && always()
327+
uses: actions/github-script@v7
328+
with:
329+
script: |
330+
const buildId = '${{ steps.test-browserstack.outputs.build_id }}';
331+
const status = '${{ job.status }}';
332+
const runUrl = '${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}';
333+
const docId = '${{ steps.seed-document.outputs.doc_id }}';
334+
const docTitle = '${{ steps.seed-document.outputs.title }}';
335+
336+
const body = `## 📱 BrowserStack MAUI Test Results
337+
338+
**Status:** ${status === 'success' ? '✅ Passed' : '❌ Failed'}
339+
**Build:** [#${{ github.run_number }}](${runUrl})
340+
**Test Document:** \`${docId}\` - "${docTitle}"
341+
342+
### Test Execution Summary:
343+
Tests were executed via BrowserStack SDK on the following real devices:
344+
345+
**iOS Devices:**
346+
- iPhone 15 (iOS 17)
347+
- iPhone 14 (iOS 16)
348+
- iPad Pro 12.9 2022 (iOS 16)
349+
350+
**Android Devices:**
351+
- Google Pixel 8 (Android 14)
352+
- Samsung Galaxy S23 (Android 13)
353+
- Google Pixel 6 (Android 12)
354+
355+
### View Results:
356+
[BrowserStack Dashboard](https://app-automate.browserstack.com/dashboard)
357+
358+
### Test Validation:
359+
This test verifies that documents inserted via the Ditto Cloud HTTP API successfully sync to the MAUI application running on real devices via BrowserStack's device cloud.
360+
`;
361+
362+
github.rest.issues.createComment({
363+
issue_number: context.issue.number,
364+
owner: context.repo.owner,
365+
repo: context.repo.repo,
366+
body: body
367+
});

0 commit comments

Comments
 (0)