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

[CM-2285] Added Github Actions for Automation Testing. #473

Merged
merged 4 commits into from
Jan 29, 2025
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
6 changes: 4 additions & 2 deletions .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,7 @@
## Motivation
<!-- Why are you making this change? -->

## Testing
<!-- How was the code tested? Be as specific as possible. -->
## Testing Branches
<!-- How was the code tested? Be as specific as possible. -->
KM_ChatUI_Branch : `dev`
KM_Core_Branch: `dev`
29 changes: 0 additions & 29 deletions .github/workflows/automerge.yml

This file was deleted.

179 changes: 179 additions & 0 deletions .github/workflows/ios-automation-test.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
name: iOS Automation Test Workflow

on:
pull_request: # Trigger this workflow on pull request events
types:
- opened

jobs:
build-and-test:
name: Build and Test
runs-on: macos-latest
timeout-minutes: 45 # Prevents the workflow from hanging indefinitely

steps:
# Step 1: Checkout the code
- name: Checkout Repository
uses: actions/checkout@v3

# Step 2: Fetch PR Branch Information
- name: Fetch PR Comments
id: fetch-branches
uses: actions/github-script@v6
with:
script: |
const prBody = context.payload.pull_request?.body;
if (!prBody) {
core.setFailed("No pull request body found.");
return;
}

console.log("Pull request body:", prBody); // Debugging: log the PR body

const kmChatUIBranchMatch = prBody.match(/KM_ChatUI_Branch\s*:\s*`([^`]+)`/);
const kmCoreBranchMatch = prBody.match(/KM_Core_Branch\s*:\s*`([^`]+)`/);

if (!kmChatUIBranchMatch || !kmCoreBranchMatch) {
core.setFailed("No branch information found in the pull request body.");
return;
}

core.setOutput("kmChatUIBranch", kmChatUIBranchMatch[1].trim());
core.setOutput("kmCoreBranch", kmCoreBranchMatch[1].trim());

# Step 3: Set up Xcode
- name: Set up Xcode
uses: maxim-lobanov/setup-xcode@v1
with:
xcode-version: '16.1.0'

# Step 4: Set Environment Variables
- name: Set Environment Variables
run: |
echo "KM_CHATUI_BRANCH=${{ steps.fetch-branches.outputs.kmChatUIBranch }}" >> $GITHUB_ENV
echo "KM_CORE_BRANCH=${{ steps.fetch-branches.outputs.kmCoreBranch }}" >> $GITHUB_ENV

# Step 5: Install dependencies
- name: Install Dependencies
run: |
pod install --project-directory=Example

# Step 4: Install Certificates, Provisioning Profiles, and Set Up Keychain
- name: Install Apple Certificate, Provisioning Profile, and Set Up Keychain
env:
BUILD_CERTIFICATE_BASE64: ${{ secrets.BUILD_CERTIFICATE_BASE64 }}
P12_PASSWORD: ${{ secrets.P12_PASSWORD }}
BUILD_PROVISION_PROFILE_BASE64: ${{ secrets.BUILD_PROVISION_PROFILE_BASE64 }}
KEYCHAIN_PASSWORD: ${{ secrets.KEYCHAIN_PASSWORD }}
run: |
# Define file paths
CERTIFICATE_PATH=$RUNNER_TEMP/build_certificate.p12
PP_PATH=$RUNNER_TEMP/build_pp.mobileprovision
KEYCHAIN_PATH=$RUNNER_TEMP/app-signing.keychain-db

# Decode the certificate and provisioning profile from secrets
echo -n "$BUILD_CERTIFICATE_BASE64" | base64 --decode -o $CERTIFICATE_PATH
echo -n "$BUILD_PROVISION_PROFILE_BASE64" | base64 --decode -o $PP_PATH

# Create and configure a temporary keychain
security create-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH
security set-keychain-settings -lut 3600 $KEYCHAIN_PATH
security unlock-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH
security list-keychain -d user -s $KEYCHAIN_PATH

# Import the certificate into the keychain
security import $CERTIFICATE_PATH -P "$P12_PASSWORD" -A -t cert -f pkcs12 -k $KEYCHAIN_PATH

# Copy the provisioning profile to the expected location
mkdir -p ~/Library/MobileDevice/Provisioning\ Profiles
cp $PP_PATH ~/Library/MobileDevice/Provisioning\ Profiles

# Step 5: Update Info.plist Files with Secrets
- name: Append new key in Info.plist for Example
run: |
PLIST_PATH="Example/Kommunicate/Info.plist"
KEY="KOMMUNICATE_APP_ID"
VALUE="${{ secrets.KOMMUNICATE_APP_ID }}"
/usr/libexec/PlistBuddy -c "Set :$KEY $VALUE" "$PLIST_PATH" || \
/usr/libexec/PlistBuddy -c "Add :$KEY string $VALUE" "$PLIST_PATH"
cat "$PLIST_PATH"

- name: Append new key in Info.plist for Tests
run: |
PLIST_PATH="Example/Tests/Info.plist"
KEY="KOMMUNICATE_APP_ID"
VALUE="${{ secrets.KOMMUNICATE_APP_ID }}"
/usr/libexec/PlistBuddy -c "Set :$KEY $VALUE" "$PLIST_PATH" || \
/usr/libexec/PlistBuddy -c "Add :$KEY string $VALUE" "$PLIST_PATH"
cat "$PLIST_PATH"

- name: Append new key in Info.plist for UI Tests
run: |
PLIST_PATH="Example/Kommunicate_ExampleUITests/Info.plist"
KEY="KOMMUNICATE_APP_ID"
VALUE="${{ secrets.KOMMUNICATE_APP_ID }}"
/usr/libexec/PlistBuddy -c "Set :$KEY $VALUE" "$PLIST_PATH" || \
/usr/libexec/PlistBuddy -c "Add :$KEY string $VALUE" "$PLIST_PATH"
cat "$PLIST_PATH"

# Step 6: Run Tests with Keychain Management
- name: Run XCTest with Keychain
env:
KOMMUNICATE_APP_ID: ${{ secrets.KOMMUNICATE_APP_ID }}
KEYCHAIN_PASSWORD: ${{ secrets.KEYCHAIN_PASSWORD }} # Keychain password
TEAM_ID: ${{ secrets.TEAM_ID }} # Apple Developer Team ID
run: |
# Unlock the keychain for signing during tests
KEYCHAIN_PATH=$RUNNER_TEMP/app-signing.keychain-db
security unlock-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH"

# Run the tests, ensuring code signing is handled
xcodebuild test \
-workspace Example/Kommunicate.xcworkspace \
-scheme Kommunicate_Example \
-destination 'platform=iOS Simulator,name=iPhone 16 Pro Max,OS=18.1' \
-enableCodeCoverage YES \
-derivedDataPath ./DerivedData \
CODE_SIGN_IDENTITY="iPhone Developer" \
CODE_SIGNING_ALLOWED=YES \
CODE_SIGNING_REQUIRED=YES \
DEVELOPMENT_TEAM="$TEAM_ID" \
KEYCHAIN="$KEYCHAIN_PATH"

# Step 7: Print Test Results
- name: Print Test Results
run: |
cat ./DerivedData/Logs/Test/*.xcresult/TestSummaries.plist || echo "No test results found."

# Step 8: Debug Environment Variables (Optional)
- name: Debug Environment Variables
run: |
echo "Environment variables set successfully."

# Step 9: Post Test Results as a PR Comment
- name: Post Test Results to PR
if: github.event_name == 'pull_request'
uses: actions/github-script@v6
with:
script: |
const fs = require('fs');
const path = './DerivedData/Logs/Test/*.xcresult/TestSummaries.plist';
let content = 'Test results not found.';

try {
content = fs.readFileSync(path, 'utf8');
} catch (err) {
console.log('Error reading test results:', err);
}

const comment = `
## iOS Automation Test Results
Congratulations All Tests Passed! 🎉
`;

await github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: comment,
});
5 changes: 5 additions & 0 deletions Example/Kommunicate/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,11 @@
if let regexPattern = ProcessInfo.processInfo.environment["restrictedMessageRegexPattern"] {
Kommunicate.defaultConfiguration.restrictedMessageRegexPattern = regexPattern
}

if let KMAppID = Bundle.main.object(forInfoDictionaryKey: "KOMMUNICATE_APP_ID") as? String {
appId = KMAppID
}

setUpNavigationBarAppearance()

UNUserNotificationCenter.current().delegate = self
Expand Down
4 changes: 4 additions & 0 deletions Example/Kommunicate_Example.entitlements
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,9 @@
<dict>
<key>aps-environment</key>
<string>development</string>
<key>keychain-access-groups</key>
<array>
<string>$(AppIdentifierPrefix)com.Kommunicate.demo</string>
</array>
</dict>
</plist>
Original file line number Diff line number Diff line change
Expand Up @@ -125,13 +125,19 @@ class KommunicateCreateConversationAndSendMessagesTests: XCTestCase {

private func beforeTest_Launch_NewConversation() -> (XCUIApplication) {
let app = XCUIApplication()
if app.buttons[InAppButton.LaunchScreen.logoutButton].exists {
app.buttons[InAppButton.LaunchScreen.logoutButton].tap()
let loginAsVisitorButton = app.buttons[InAppButton.LaunchScreen.loginAsVisitor]
waitFor(object: loginAsVisitorButton) { $0.exists }
loginAsVisitorButton.tap()
}
let launchConversationButton = app.buttons[InAppButton.EditGroup.launch]
waitFor(object: launchConversationButton) { $0.exists }
launchConversationButton.tap()
sleep(3)
// Check if the specific screen is on top
let isScreenOnTop = app.navigationBars[AppScreen.myChatScreen].exists

if isScreenOnTop {
// Perform actions only if the screen is not on top
let createConversationButton = app.navigationBars[AppScreen.myChatScreen]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,10 @@ class KommunicateFormRichMessageUITests: XCTestCase {
waitFor(object: nameField) { $0.exists }
nameField.tap()
nameField.typeText(FormData.name)
app.swipeUp()
passwordField.tap()
passwordField.typeText(FormData.password)
app.swipeUp()
let innerchatscreentableviewTable = app.tables[AppScreen.innerChatScreenTableView]
innerchatscreentableviewTable.staticTexts[RichMessageButtons.male].tap()
innerchatscreentableviewTable.staticTexts[RichMessageButtons.metal].tap()
Expand All @@ -63,6 +65,7 @@ class KommunicateFormRichMessageUITests: XCTestCase {
let submitResponse = innerchatscreentableviewTable.textViews[RichMessageResponseText.formTemplateResponse1]
waitFor(object: submitResponse) { $0.exists }
}


func testFormTemplate2() {
let app = beforeTest_Launch_NewConversation()
Expand All @@ -81,25 +84,31 @@ class KommunicateFormRichMessageUITests: XCTestCase {
waitFor(object: nameField) { $0.exists }
nameField.tap()
nameField.typeText(FormData.name)
app.swipeUp()
passwordField.tap()
passwordField.typeText(FormData.password)
app.swipeUp()
emailField.tap()
emailField.typeText(FormData.email)
app.swipeUp()
phoneField.tap()
phoneField.typeText(FormData.phoneNumber)
app.swipeUp()
addressField.tap()
addressField.typeText(FormData.address)
app.swipeUp()
let innerchatscreentableviewTable = app.tables[AppScreen.innerChatScreenTableView]
app.tables[AppScreen.innerChatScreenTableView].staticTexts[RichMessageButtons.submit].tap()
let submitResponse = innerchatscreentableviewTable.textViews[RichMessageResponseText.formTemplateResponse2]
waitFor(object: submitResponse) { $0.exists }
}

func testFormTemplate3() {
let app = beforeTest_Launch_NewConversation()
waitFor(object: app) { $0.exists }
sleep(4)
app.typeText(GroupData.typeText3) // typing message
sleep(2)
app.buttons[InAppButton.ConversationScreen.send].tap() // sending message in group
let formFirstResponse = app.tables[AppScreen.innerChatScreenTableView]
.textViews[RichMessageResponseText.formFirstResponse]
Expand All @@ -109,20 +118,23 @@ class KommunicateFormRichMessageUITests: XCTestCase {
waitFor(object: nameField) { $0.exists }
nameField.tap()
nameField.typeText(FormData.name)
app.swipeUp()
passwordField.tap()
passwordField.typeText(FormData.password)

app.swipeUp()
let dateField = app.tables.textFields[FormIdentifier.dateTime]
dateField.tap()
sleep(1)
app.buttons[InAppButton.ConversationScreen.doneButton].tap()
sleep(1)
app.swipeUp()
let innerchatscreentableviewTable = app.tables[AppScreen.innerChatScreenTableView]
innerchatscreentableviewTable.staticTexts[RichMessageButtons.male].tap()
app.tables[AppScreen.innerChatScreenTableView].staticTexts[RichMessageButtons.submit].tap()
let submitResponse = innerchatscreentableviewTable.textViews[RichMessageResponseText.formTemplateResponse1]
waitFor(object: submitResponse) { $0.exists }
}


private func login() {
let path = Bundle(for: KommunicateRichMessageUITests.self).url(forResource: "Info", withExtension: "plist")
Expand All @@ -144,9 +156,9 @@ class KommunicateFormRichMessageUITests: XCTestCase {
let app = XCUIApplication()
if app.buttons[InAppButton.LaunchScreen.logoutButton].exists {
app.buttons[InAppButton.LaunchScreen.logoutButton].tap()
sleep(5)
let loginAsVisitorButton = app.scrollViews.otherElements
loginAsVisitorButton.buttons[InAppButton.LaunchScreen.loginAsVisitor].tap()
let loginAsVisitorButton = app.buttons[InAppButton.LaunchScreen.loginAsVisitor]
waitFor(object: loginAsVisitorButton) { $0.exists }
loginAsVisitorButton.tap()
}

let launchConversationButton = app.buttons[InAppButton.EditGroup.launch]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,12 @@ import XCTest
class KommunicateLoginAndWelcomeMessage: XCTestCase {
enum GroupData {
static let AppId = loginCreadentials.testAppID
static let fillUserId = loginCreadentials.userID
static let fillPassword = loginCreadentials.password
static let fillUserId = loginWelcomeMessageCredentialsTest.userID
static let fillPassword = loginWelcomeMessageCredentialsTest.password
}

var isfreshLogin: Bool = false

override func setUp() {
super.setUp()
continueAfterFailure = false
Expand Down Expand Up @@ -42,13 +44,19 @@ class KommunicateLoginAndWelcomeMessage: XCTestCase {
func testLoginAndCustomWelcomeMessage() {
let app = beforeTest_Launch_NewConversation()
waitFor(object: app) { $0.exists }
sleep(5)
let innerchatscreentableviewTable = app.tables[AppScreen.innerChatScreenTableView]
let welcomMessageResponse = innerchatscreentableviewTable.textViews[RichMessageResponseText.customWelcomeMessage]
waitFor(object: welcomMessageResponse) { $0.exists }
}

private func beforeTest_Launch_NewConversation() -> (XCUIApplication) {
let app = XCUIApplication()
if !isfreshLogin && app.buttons[InAppButton.LaunchScreen.logoutButton].exists {
app.buttons[InAppButton.LaunchScreen.logoutButton].tap()
sleep(5)
login()
}
let launchConversationButton = app.buttons[InAppButton.EditGroup.launch]
waitFor(object: launchConversationButton) { $0.exists }
launchConversationButton.tap()
Expand Down Expand Up @@ -94,6 +102,7 @@ class KommunicateLoginAndWelcomeMessage: XCTestCase {
passwordSecureTextField.tap()
passwordSecureTextField.typeText(password)
elementsQuery.buttons[InAppButton.LaunchScreen.getStarted].tap()
isfreshLogin = true
}

private func appIdFromEnvVars() -> String? {
Expand Down
Loading