Skip to content

Commit 16328f2

Browse files
authored
ci: support using shopping app for integration testing in device farm (#48)
1 parent 2bd2962 commit 16328f2

File tree

8 files changed

+727
-14
lines changed

8 files changed

+727
-14
lines changed
Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
name: Integration Test
2+
3+
on:
4+
pull_request:
5+
branches: [ "main" ]
6+
7+
jobs:
8+
e2e-test:
9+
runs-on: macos-13-xl
10+
permissions:
11+
contents: write
12+
checks: write
13+
pull-requests: write
14+
id-token: write
15+
env:
16+
iam_role_to_assume: ${{ secrets.ROLE_ARN }}
17+
device_farm_project_arn: ${{ secrets.DEVICE_FARM_PROJECT_ARN }}
18+
device_farm_pool_arn: ${{ secrets.DEVICE_FARM_POOL_ARN }}
19+
device_farm_test_spec_arn: ${{ secrets.DEVICE_FARM_TEST_SPEC_ARN }}
20+
clickstream_app_id: ${{ secrets.CLICKSTREAM_APP_ID }}
21+
clickstream_endpoint: ${{ secrets.CLICKSTREAM_ENDPOINT }}
22+
steps:
23+
- uses: actions/checkout@v4
24+
with:
25+
token: ${{ secrets.PROJECT_TOKEN }}
26+
- name: Modify SDK for integration test
27+
run: |
28+
sed -i '' -e "s#private(set) var bundleSequenceId: Int#private(set) var bundleSequenceId: Int\n var allEventJson: String = \"\"#g" Sources/Clickstream/Dependency/Clickstream/Analytics/EventRecorder.swift
29+
sed -i '' -e "s#toPrettierJsonString())\")#toPrettierJsonString())\")\n allEventJson.append(\"Saved event \\\(event.eventType):\\\(eventObject.toJsonString())\\\n\")\n UIPasteboard.general.string = allEventJson#g" Sources/Clickstream/Dependency/Clickstream/Analytics/EventRecorder.swift
30+
sed -i '' -e "s#batchEvent.eventCount) events\")#batchEvent.eventCount) events\")\n allEventJson.append(\"Send \\\(batchEvent.eventCount) events\\\n\")\n UIPasteboard.general.string = allEventJson#g" Sources/Clickstream/Dependency/Clickstream/Analytics/EventRecorder.swift
31+
git diff
32+
- name: Prepare sample iOS app
33+
run: |
34+
cd ..
35+
git clone https://github.com/aws-samples/clickstream-sdk-samples
36+
cd clickstream-sdk-samples/ios
37+
sed -i '' -e "s#\"appId\": \"your appId\"#\"appId\": \"${{ env.clickstream_app_id }}\"#g" ModerneShopping/amplifyconfiguration.json
38+
sed -i '' -e "s#\"endpoint\": \"your endpoint\"#\"endpoint\": \"${{ env.clickstream_endpoint }}\"#g" ModerneShopping/amplifyconfiguration.json
39+
sed -i '' -e "s#if index==0 || index==1 {}#if index==0 || index==1 {\n cart.addToCart(addedProduct: product, quantity: 1)\n }#g" ModerneShopping/Views/ProductViews/ProductList.swift
40+
sed -i '' -e "s#3A1001882A1DDF4300DF72CB /\* XCRemoteSwiftPackageReference \"clickstream-swift\" \*\/,#3A67FCB62B6F26C60098082A /\* XCLocalSwiftPackageReference \"../../clickstream-swift\" \*\/,#g" ModerneShopping.xcodeproj/project.pbxproj
41+
perl -0777 -pi -e 's#/\* Begin XCRemoteSwiftPackageReference section \*/\n(\t+)3A1001882A1DDF4300DF72CB /\* XCRemoteSwiftPackageReference \"clickstream-swift\" \*/ = {\n(\t+)isa = XCRemoteSwiftPackageReference;\n(\t+)repositoryURL = \"https://github.com/awslabs/clickstream-swift.git\";\n(\t+)requirement = {\n(\t+)branch = main;\n(\t+)kind = branch;\n(\t+)};\n(\t+)};#/\* Begin XCLocalSwiftPackageReference section \*/\n\t\t3A67FCB62B6F26C60098082A /\* XCLocalSwiftPackageReference \"../../clickstream-swift\" \*/ = {\n$2isa = XCLocalSwiftPackageReference;\n$3relativePath = \"../../clickstream-swift\";\n$1};\n/\* End XCLocalSwiftPackageReference section \*/\n\n/* Begin XCRemoteSwiftPackageReference section \*/#' ModerneShopping.xcodeproj/project.pbxproj
42+
perl -0777 -pi -e 's#isa = XCSwiftPackageProductDependency;\n(\t+)package = 3A1001882A1DDF4300DF72CB /\* XCRemoteSwiftPackageReference \"clickstream-swift\" \*/;#isa = XCSwiftPackageProductDependency;#' ModerneShopping.xcodeproj/project.pbxproj
43+
perl -0777 -pi -e 's#\n {\n "identity" : "clickstream-swift",\n "kind" : "remoteSourceControl",\n "location" : "https://github.com/awslabs/clickstream-swift.git",\n "state" : {\n "branch" : "main",\n "revision" : "2bd29626068a6dff158f41f9d81295a6eaa59be5"\n }\n },##' ModerneShopping.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved
44+
perl -0777 -pi -e 's#objectVersion = 54;#objectVersion = 60;#' ModerneShopping.xcodeproj/project.pbxproj
45+
git diff
46+
- name: Generate export options
47+
run: |
48+
echo '${{ vars.EXPORT_OPTIONS }}' >> ExportOptions.plist
49+
cat ExportOptions.plist
50+
ls
51+
- name: Install the Apple certificate and provisioning profile
52+
env:
53+
BUILD_CERTIFICATE_BASE64: ${{ secrets.P12_BASE64 }}
54+
P12_PASSWORD: ${{ secrets.CERTIFICATE_PASSWORD }}
55+
BUILD_PROVISION_PROFILE_BASE64: ${{ secrets.MOBILEPROVISION_BASE64 }}
56+
KEYCHAIN_PASSWORD: ${{ secrets.KEYCHAIN_PASSWORD }}
57+
run: |
58+
CERTIFICATE_PATH=$RUNNER_TEMP/build_certificate.p12
59+
PP_PATH=$RUNNER_TEMP/shoppingmacdev.mobileprovision
60+
KEYCHAIN_PATH=$RUNNER_TEMP/app-signing.keychain-db
61+
# import certificate and provisioning profile from secrets
62+
echo -n "$BUILD_CERTIFICATE_BASE64" | base64 --decode -o $CERTIFICATE_PATH
63+
echo -n "$BUILD_PROVISION_PROFILE_BASE64" | base64 --decode -o $PP_PATH
64+
# create temporary keychain
65+
security create-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH
66+
security set-keychain-settings -lut 21600 $KEYCHAIN_PATH
67+
security unlock-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH
68+
# import certificate to keychain
69+
security import $CERTIFICATE_PATH -P "$P12_PASSWORD" -A -t cert -f pkcs12 -k $KEYCHAIN_PATH
70+
security list-keychain -d user -s $KEYCHAIN_PATH
71+
# apply provisioning profile
72+
mkdir -p ~/Library/MobileDevice/Provisioning\ Profiles
73+
cp $PP_PATH ~/Library/MobileDevice/Provisioning\ Profiles
74+
- name: Build archive
75+
run: |
76+
sudo xcode-select -s '/Applications/Xcode_15.2.app/Contents/Developer'
77+
xcode-select --print-path
78+
cd ../clickstream-sdk-samples/ios/
79+
xcodebuild -resolvePackageDependencies
80+
echo "start build app"
81+
xcodebuild -scheme "ModerneShopping" \
82+
-archivePath $RUNNER_TEMP/ModerneShopping.xcarchive \
83+
-sdk iphoneos \
84+
-configuration Release \
85+
-destination generic/platform=iOS \
86+
clean archive | xcpretty
87+
- name: Export ipa
88+
run: |
89+
EXPORT_OPTS_PATH=ExportOptions.plist
90+
xcodebuild -exportArchive -archivePath $RUNNER_TEMP/ModerneShopping.xcarchive -exportOptionsPlist $EXPORT_OPTS_PATH -exportPath output
91+
cd output
92+
ls
93+
- name: Build Device Farm test file
94+
run: |
95+
cd IntegrationTest
96+
pip install virtualenv
97+
virtualenv --help
98+
virtualenv workspace
99+
cd workspace
100+
source bin/activate
101+
pip install -r ../requirements.txt
102+
mkdir tests
103+
cp ../appium/shopping_test.py tests/
104+
find tests/
105+
py.test --collect-only tests/
106+
cd tests/
107+
find . -name '__pycache__' -type d -exec rm -r {} +
108+
find . -name '*.pyc' -exec rm -f {} +
109+
find . -name '*.pyo' -exec rm -f {} +
110+
find . -name '*~' -exec rm -f {} +
111+
cd ..
112+
pip freeze > requirements.txt
113+
zip -r test_bundle.zip tests/ requirements.txt
114+
ls
115+
cd ..
116+
- name: Configure AWS Credentials
117+
if: ${{ env.iam_role_to_assume != '' }}
118+
uses: aws-actions/configure-aws-credentials@v4
119+
with:
120+
role-to-assume: ${{ env.iam_role_to_assume }}
121+
aws-region: us-west-2
122+
- name: Execute device farm test
123+
run: |
124+
cd IntegrationTest
125+
pip install -r requirements.txt
126+
cd devicefarm
127+
cp ../../output/ModerneShopping.ipa ./
128+
cp ../workspace/test_bundle.zip ./
129+
ls
130+
python -u -c "from automate_device_farm import upload_and_test_ios; upload_and_test_ios('ModerneShopping.ipa', 'test_bundle.zip', '${{ env.device_farm_project_arn }}', '${{ env.device_farm_test_spec_arn }}', '${{ env.device_farm_pool_arn }}')"
131+
- name: Execute logcat test
132+
run: |
133+
cd IntegrationTest/devicefarm
134+
pytest logcat_test.py -s --junitxml=report/logcat_test_report.xml --html=report/logcat_test_report.html
135+
- name: Publish Test Report
136+
uses: mikepenz/action-junit-report@v4
137+
if: success() || failure()
138+
with:
139+
report_paths: 'IntegrationTest/devicefarm/report/*.xml'
140+
require_tests: true
141+
detailed_summary: true
142+
include_passed: true
143+
fail_on_failure: true
144+
job_name: integration test
145+
- name: Upload test result
146+
uses: actions/upload-artifact@v4
147+
if: success() || failure()
148+
with:
149+
name: test-result
150+
path: |
151+
IntegrationTest/devicefarm/report/
152+
IntegrationTest/devicefarm/MyAndroidAppTest-*/**

.github/workflows/release.yml

Lines changed: 30 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ jobs:
1212
release:
1313
runs-on: ubuntu-latest
1414
steps:
15-
- uses: actions/checkout@v3
15+
- uses: actions/checkout@v4
1616
with:
1717
ref: main
1818
fetch-depth: 0
@@ -22,17 +22,33 @@ jobs:
2222
chmod +x release.sh
2323
./release.sh ${{ env.NEW_VERSION }}
2424
git diff
25-
git config user.name '${{ vars.USER_NAME }}'
26-
git config user.email '${{ vars.USER_EMAIL }}'
27-
git add .
28-
git commit -m 'release: clickstream Swift ${{ env.NEW_VERSION }}'
29-
git push
30-
git tag ${{ env.NEW_VERSION }}
31-
git push origin ${{ env.NEW_VERSION }}
32-
- name: Create GitHub release
33-
uses: softprops/action-gh-release@v1
25+
git config user.name "github-actions"
26+
git config user.email "github-actions@github.com"
27+
- name: Create Pull Request
28+
id: create-pr
29+
uses: peter-evans/create-pull-request@v5
3430
with:
35-
name: "Clickstream Swift ${{ env.NEW_VERSION }}"
36-
tag_name: "${{ env.NEW_VERSION }}"
37-
prerelease: true
38-
generate_release_notes: true
31+
token: ${{ secrets.PROJECT_TOKEN }}
32+
commit-message: 'release: clickstream Swift ${{ env.NEW_VERSION }}'
33+
title: 'release: clickstream Swift ${{ env.NEW_VERSION }}'
34+
author: github-actions <github-actions@github.com>
35+
committer: github-actions <github-actions@github.com>
36+
signoff: true
37+
body: |
38+
## Description
39+
1. release: clickstream Swift ${{ env.NEW_VERSION }}
40+
41+
## General Checklist
42+
<!-- Check or cross out if not relevant -->
43+
44+
- [x] Added new tests to cover change, if needed
45+
- [x] Build succeeds using Swift Package Manager
46+
- [x] All unit tests pass
47+
- [x] Documentation update for the change if required
48+
- [x] PR title conforms to conventional commit style
49+
- [x] If breaking change, documentation/changelog update with migration instructions
50+
51+
By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.
52+
53+
labels: release
54+
branch: release_${{ env.NEW_VERSION }}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
name: Create Tag And Release
2+
on:
3+
push:
4+
branches: [ "main" ]
5+
6+
jobs:
7+
release:
8+
if: ${{ startsWith(github.event.head_commit.message, 'release:') }}
9+
runs-on: ubuntu-latest
10+
env:
11+
COMMIT_MESSAGE: ${{ github.event.head_commit.message }}
12+
permissions:
13+
contents: write
14+
steps:
15+
- uses: actions/checkout@v4
16+
with:
17+
ref: main
18+
fetch-depth: 0
19+
token: ${{ secrets.PROJECT_TOKEN }}
20+
- name: Create new tag
21+
run: |
22+
echo "${{ env.COMMIT_MESSAGE }}"
23+
version=$(echo "${{ env.COMMIT_MESSAGE }}" | grep -oP 'Swift \K\d+\.\d+\.\d+')
24+
echo "release_version=$version" >> "$GITHUB_ENV"
25+
echo $version
26+
git config user.name '${{ vars.USER_NAME }}'
27+
git config user.email '${{ vars.USER_EMAIL }}'
28+
git tag v$version
29+
git push origin v$version
30+
- name: Create GitHub release
31+
uses: softprops/action-gh-release@v1
32+
with:
33+
name: "Clickstream Swift ${{ env.release_version }}"
34+
tag_name: "${{ env.release_version }}"
35+
prerelease: true
36+
generate_release_notes: true

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,4 @@ DerivedData/
77
.swiftpm/config/registries.json
88
.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
99
.netrc
10+
.idea
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
"""
2+
Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance
5+
with the License. A copy of the License is located at
6+
7+
http://www.apache.org/licenses/LICENSE-2.0
8+
9+
or in the 'license' file accompanying this file. This file is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES
10+
OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions
11+
and limitations under the License.
12+
"""
13+
import pytest
14+
from time import sleep
15+
16+
from appium import webdriver
17+
from appium.options.ios import XCUITestOptions
18+
from appium.webdriver.common.appiumby import AppiumBy
19+
from selenium.common.exceptions import NoSuchElementException
20+
21+
capabilities = dict(
22+
platformName='ios',
23+
automationName='xcuitest',
24+
deviceName='iPhone',
25+
bundleId='software.aws.solution.ModerneShopping',
26+
language='en',
27+
locale='US',
28+
)
29+
30+
appium_server_url = 'http://0.0.0.0:4723/wd/hub'
31+
32+
33+
class TestShopping:
34+
def setup_method(self):
35+
self.driver = webdriver.Remote(appium_server_url, options=XCUITestOptions().load_capabilities(capabilities))
36+
self.driver.implicitly_wait(10)
37+
38+
def teardown_method(self):
39+
if self.driver:
40+
self.driver.quit()
41+
42+
@pytest.mark.parametrize("test_suite", [
43+
"test suite 1",
44+
"test suite 2"
45+
])
46+
def test_shopping(self, test_suite):
47+
sleep(3)
48+
self.perform_click_element('Profile')
49+
self.perform_click_element('sign_in')
50+
sleep(3)
51+
self.perform_click_element('Cart')
52+
self.perform_click_element('check_out')
53+
self.perform_click_element('purchase')
54+
self.perform_click_element('Profile')
55+
self.perform_click_element("sign_out")
56+
self.driver.execute_script('mobile: backgroundApp', {"seconds": 5})
57+
sleep(1)
58+
self.perform_click_element("show_log_text")
59+
event_log = self.driver.find_element(by=AppiumBy.ID, value="event_log")
60+
self.driver.log_event("app_event_log", event_log.text)
61+
print(event_log.text)
62+
sleep(1)
63+
64+
def perform_click_element(self, element_id):
65+
try:
66+
element = self.driver.find_element(by=AppiumBy.ID, value=element_id)
67+
element.click()
68+
sleep(2)
69+
except NoSuchElementException:
70+
pytest.skip(f"Element with ID '{element_id}' not found. Skipped the test")
71+
72+
73+
if __name__ == '__main__':
74+
TestShopping.test_shopping()

0 commit comments

Comments
 (0)