Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
d2722d7
feat: groups banners now check non existence
mikimandoki Jan 4, 2026
a42590a
feat: add kick and remove messages test
mikimandoki Jan 5, 2026
6f86bb6
fix: new android workflow for add contact
mikimandoki Jan 6, 2026
b0f3949
fix: add uiscrollable for delete group
mikimandoki Jan 6, 2026
4abcf7f
fix: use proper locator for invite contact
mikimandoki Jan 7, 2026
6465648
Merge remote-tracking branch 'origin/dev' into feat/manage-members
mikimandoki Jan 7, 2026
26538f6
feat: add message history and invite account id tests
mikimandoki Jan 8, 2026
063ee59
Merge origin/dev
mikimandoki Jan 8, 2026
74508f1
feat: add promote test
mikimandoki Jan 8, 2026
859e5a0
Merge remote-tracking branch 'origin/main' into feat/manage-members
mikimandoki Jan 8, 2026
deaf2e7
Merge remote-tracking branch 'origin/dev' into feat/manage-members
mikimandoki Jan 9, 2026
fd982b0
chore: use correct qa-tags
mikimandoki Jan 9, 2026
6ff9141
chore: begin implementing new groups locators
mikimandoki Jan 12, 2026
4adc830
chore: weed out message request item locators
mikimandoki Jan 12, 2026
0aedace
feat: add accept message request method
mikimandoki Jan 12, 2026
1f42fe4
chore: continue adding locator classes and test steps
mikimandoki Jan 20, 2026
8c7c114
wip: continue adding test.steps
mikimandoki Jan 20, 2026
01ae8a2
Merge remote-tracking branch 'origin/dev' into feat/manage-members
mikimandoki Jan 20, 2026
b8b86d8
Merge remote-tracking branch 'origin/dev' into feat/manage-members
mikimandoki Jan 26, 2026
d105075
chore: add test steps to manage member tests
mikimandoki Jan 27, 2026
d97b812
feat: add multi admin leave test
mikimandoki Jan 28, 2026
01435e1
fix: tidy up multi admin leave test
mikimandoki Jan 29, 2026
d381058
feat: add network target input to workflow
mikimandoki Feb 1, 2026
82eee52
feat: write network target to allure report
mikimandoki Feb 1, 2026
3d6da8a
Merge remote-tracking branch 'origin/main' into feat/manage-members
mikimandoki Feb 2, 2026
d172d9e
Merge remote-tracking branch 'origin/dev' into feat/manage-members
mikimandoki Feb 2, 2026
fe2bf00
chore: update strings
mikimandoki Feb 2, 2026
1600160
fix: address broken tests
mikimandoki Feb 2, 2026
929fdbc
Merge remote-tracking branch 'origin/main' into feat/manage-members
mikimandoki Feb 2, 2026
371df50
fix: try ipv4 first when installing deps
mikimandoki Feb 2, 2026
132aeae
chore: bump timeouts
mikimandoki Feb 2, 2026
0c5995f
chore: linting
mikimandoki Feb 2, 2026
3c40b21
feat: add multi admin promotion test
mikimandoki Feb 2, 2026
7e0bfcb
feat: add linked device promote test
mikimandoki Feb 3, 2026
f1f96be
Merge remote-tracking branch 'origin/main' into feat/manage-members
mikimandoki Feb 4, 2026
20a7baa
Merge remote-tracking branch 'origin/dev' into feat/manage-members
mikimandoki Feb 4, 2026
a1da68f
feat: add ban test
mikimandoki Feb 5, 2026
16aebb9
feat: add ban and delete test
mikimandoki Feb 5, 2026
cf47d45
Merge remote-tracking branch 'origin/dev' into feat/manage-members
mikimandoki Feb 9, 2026
8a96589
Merge remote-tracking branch 'origin/dev' into feat/manage-members
mikimandoki Feb 9, 2026
af70b93
Merge remote-tracking branch 'origin/dev' into feat/manage-members
mikimandoki Feb 9, 2026
a5da631
fix: start headless and use sw rendering
mikimandoki Feb 9, 2026
e8cb9e3
Revert "fix: start headless and use sw rendering"
mikimandoki Feb 9, 2026
4fd26a7
chore: add emulator health check
mikimandoki Feb 9, 2026
a88804c
Merge pull request #91 from session-foundation/chore/emulator-health-…
mikimandoki Feb 9, 2026
6d36e82
fix: wait longer for promotion sent not present
mikimandoki Feb 10, 2026
1c4c78a
Merge remote-tracking branch 'origin/dev' into feat/manage-members
mikimandoki Feb 10, 2026
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
103 changes: 62 additions & 41 deletions .github/workflows/android-regression.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,15 @@ run-name: '${{ inputs.RISK }} regressions on ${{ github.head_ref || github.ref }
on:
workflow_dispatch:
inputs:
NETWORK_TARGET:
description: 'network target to run the tests on'
required: true
type: choice
options:
- 'devnet'
- 'mainnet'
default: 'devnet'

APK_URL:
description: 'apk url to test (.tar.xz)'
required: true
Expand Down Expand Up @@ -98,57 +107,62 @@ jobs:
run: |
pwd

# Check if devnet is accessible before choosing APK
echo "Checking devnet accessibility for APK selection..."
DEVNET_ACCESSIBLE=false

# Retry logic
for attempt in 1 2 3; do
echo "Devnet check attempt $attempt/3..."
if curl -s --connect-timeout 5 --max-time 10 http://sesh-net.local:1280 >/dev/null 2>&1; then
echo "Devnet is accessible on attempt $attempt"
DEVNET_ACCESSIBLE=true
break
else
echo "Attempt $attempt failed"
if [ $attempt -lt 3 ]; then
echo "Waiting ${attempt}s before retry..."
sleep $attempt
fi
fi
done

if [ "$DEVNET_ACCESSIBLE" = "false" ]; then
echo "Devnet is not accessible after 3 attempts"
fi
NETWORK_TARGET="${{ github.event.inputs.NETWORK_TARGET }}"
echo "Network target: $NETWORK_TARGET"

# Download and extract APK
# Download and extract APK
wget -q -O session-android.apk.tar.xz ${{ github.event.inputs.APK_URL }}
tar xf session-android.apk.tar.xz
mv session-android-*universal extracted

# Choose APK based on devnet accessibility
if ls extracted/*automaticQa.apk 1>/dev/null 2>&1; then
if [ "$DEVNET_ACCESSIBLE" = "true" ]; then
echo "Using AQA build (devnet accessible)"
# Choose APK based on network target
if [ "$NETWORK_TARGET" = "devnet" ]; then
echo "Devnet target - checking devnet accessibility..."
DEVNET_ACCESSIBLE=false

# Retry logic
for attempt in 1 2 3; do
echo "Devnet check attempt $attempt/3..."
if curl -s --connect-timeout 5 --max-time 10 http://sesh-net.local:1280 >/dev/null 2>&1; then
echo "Devnet is accessible on attempt $attempt"
DEVNET_ACCESSIBLE=true
break
else
echo "Attempt $attempt failed"
if [ $attempt -lt 3 ]; then
echo "Waiting ${attempt}s before retry..."
sleep $attempt
fi
fi
done

if [ "$DEVNET_ACCESSIBLE" = "false" ]; then
echo "ERROR: Devnet is not accessible after 3 attempts, but devnet target was selected"
exit 1
fi

# Use AQA build for devnet
if ls extracted/*automaticQa.apk 1>/dev/null 2>&1; then
echo "Using AQA build for devnet"
mv extracted/*automaticQa.apk extracted/session-android.apk
echo "IS_AUTOMATIC_QA=true" >> $GITHUB_ENV
else
echo "AQA build available but devnet not accessible - falling back to regular QA build"
if ls extracted/*qa.apk 1>/dev/null 2>&1; then
mv extracted/*qa.apk extracted/session-android.apk
echo "IS_AUTOMATIC_QA=false" >> $GITHUB_ENV
else
echo "No regular QA build found as fallback"
exit 1
fi
echo "ERROR: No AQA build found for devnet target"
exit 1
fi
elif ls extracted/*qa.apk 1>/dev/null 2>&1; then
echo "Using regular QA build"
mv extracted/*qa.apk extracted/session-android.apk
echo "IS_AUTOMATIC_QA=false" >> $GITHUB_ENV

elif [ "$NETWORK_TARGET" = "mainnet" ]; then
echo "Mainnet target - using regular QA build"
if ls extracted/*qa.apk 1>/dev/null 2>&1; then
mv extracted/*qa.apk extracted/session-android.apk
echo "IS_AUTOMATIC_QA=false" >> $GITHUB_ENV
else
echo "ERROR: No regular QA build found for mainnet target"
exit 1
fi

else
echo "No suitable APK found"
echo "ERROR: Unknown network target: $NETWORK_TARGET"
exit 1
fi

Expand Down Expand Up @@ -195,6 +209,9 @@ jobs:
PLATFORM: ${{ env.PLATFORM }}
UPLOAD_IDENTIFIER: 'devices-1-test-run'

- name: Recover emulators if needed
run: pnpm recover-emulators

- name: Run the 2-devices tests ​​with 2 workers
continue-on-error: true
id: devices-2-test-run
Expand All @@ -214,6 +231,9 @@ jobs:
PLATFORM: ${{ env.PLATFORM }}
UPLOAD_IDENTIFIER: 'devices-2-test-run'

- name: Recover emulators if needed
run: pnpm recover-emulators

- name: Run the other tests with 1 worker
continue-on-error: true
id: other-devices-test-run
Expand All @@ -238,6 +258,7 @@ jobs:
RISK: ${{github.event.inputs.RISK}}
GITHUB_RUN_NUMBER: ${{ github.run_number }}
GITHUB_RUN_ATTEMPT: ${{ github.run_attempt }}
NETWORK_TARGET: ${{ github.event.inputs.NETWORK_TARGET }}

- name: Upload results of this run
uses: ./github/actions/upload-test-results
Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/ios-regression.yml
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ jobs:
APPIUM_ADB_FULL_PATH: '<just_not_empty>'
ANDROID_SDK_ROOT: '<just_not_empty>'
PLAYWRIGHT_RETRIES_COUNT: ${{ github.event.inputs.PLAYWRIGHT_RETRIES_COUNT }}
NETWORK_TARGET: 'mainnet' # iOS only supports mainnet for now
_TESTING: 1 # Always hide webdriver logs (@appium/support/ flag)
PRINT_FAILED_TEST_LOGS: ${{ github.event.inputs.LOG_LEVEL != 'minimal' && '1' || '0' }} # Show stdout/stderr if test fails (@session-foundation/playwright-reporter/ flag)
PRINT_ONGOING_TEST_LOGS: ${{ github.event.inputs.LOG_LEVEL == 'verbose' && '1' || '0' }} # Show everything as it happens (@session-foundation/playwright-reporter/ flag)
Expand Down Expand Up @@ -152,6 +153,7 @@ jobs:
RISK: ${{github.event.inputs.RISK}}
GITHUB_RUN_NUMBER: ${{ github.run_number }}
GITHUB_RUN_ATTEMPT: ${{ github.run_attempt }}
NETWORK_TARGET: ${{ env.NETWORK_TARGET }}

- name: Upload results of this run
uses: ./github/actions/upload-test-results
Expand Down
10 changes: 7 additions & 3 deletions github/actions/generate-publish-test-report/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ inputs:
required: true
GITHUB_RUN_ATTEMPT:
required: true
NETWORK_TARGET:
required: true

runs:
using: 'composite'
Expand All @@ -27,10 +29,11 @@ runs:
PLATFORM: ${{ inputs.PLATFORM }}
BUILD_NUMBER: ${{ inputs.BUILD_NUMBER }}
GH_TOKEN: ${{ inputs.GH_TOKEN }}
APK_URL: ${{inputs.APK_URL}}
RISK: ${{inputs.RISK}}
APK_URL: ${{ inputs.APK_URL}}
RISK: ${{ inputs.RISK}}
GITHUB_RUN_NUMBER: ${{ inputs.GITHUB_RUN_NUMBER}}
GITHUB_RUN_ATTEMPT: ${{ inputs.GITHUB_RUN_ATTEMPT}}
NETWORK_TARGET: ${{ inputs.NETWORK_TARGET }}

- name: Publish report to GitHub Pages
if: ${{ success() }}
Expand All @@ -42,9 +45,10 @@ runs:
PLATFORM: ${{ inputs.PLATFORM }}
BUILD_NUMBER: ${{ inputs.BUILD_NUMBER }}
GH_TOKEN: ${{ inputs.GH_TOKEN }}
RISK: ${{inputs.RISK}}
RISK: ${{ inputs.RISK}}
GITHUB_RUN_NUMBER: ${{ inputs.GITHUB_RUN_NUMBER}}
GITHUB_RUN_ATTEMPT: ${{ inputs.GITHUB_RUN_ATTEMPT}}
NETWORK_TARGET: ${{ inputs.NETWORK_TARGET }}

- name: Annotate run summary with report link
if: ${{ success() }}
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
"scripts": {
"cleanup-simulators": "npx ts-node scripts/cleanup_ios_simulators.ts",
"create-simulators": "pnpm cleanup-simulators && npx ts-node scripts/create_ios_simulators.ts",
"recover-emulators": "npx ts-node scripts/emulator_health.ts",
"lint": "pnpm prettier . --write --cache && pnpm eslint . --cache ",
"lint-check": "pnpm prettier . --check && pnpm eslint .",
"tsc": "tsc",
Expand Down
4 changes: 2 additions & 2 deletions run/screenshots/android/settings.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions run/test/specs/community_requests_on.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import {
MessageRequestPendingDescription,
UPMMessageButton,
} from './locators/conversation';
import { MessageRequestsBanner } from './locators/home';
import { MessageRequestItem, MessageRequestsBanner } from './locators/home';
import { CommunityMessageRequestSwitch, PrivacyMenuItem, UserSettings } from './locators/settings';
import { sleepFor } from './utils';
import { joinCommunity } from './utils/community';
Expand Down Expand Up @@ -77,7 +77,7 @@ async function blindedMessageRequests(platform: SupportedPlatformsType, testInfo
await test.step(`${bob.userName} accepts message request from ${alice.userName}`, async () => {
await device2.clickOnElementAll(new MessageRequestsBanner(device2));
// Bob clicks on request conversation item
await device2.clickOnByAccessibilityID('Message request');
await device2.clickOnElementAll(new MessageRequestItem(device2));
await device2.waitForTextElementToBePresent(
new ConversationHeaderName(device2, alice.userName)
);
Expand Down
2 changes: 1 addition & 1 deletion run/test/specs/disappearing_community_invite.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ async function disappearingCommunityInviteMessage(
// Leave Invite Contacts, Conversation Settings, Community, and open convo with Bob
await alice1.navigateBack();
await alice1.navigateBack();
await alice1.navigateBack();
await alice1.onIOS().navigateBack(); // Android only needs to go back twice
await alice1.clickOnElementAll(new ConversationItem(alice1, bob.userName));
// At this point the invite should have disappeared already so we just check it's not there
await alice1.verifyElementNotPresent(new CommunityInvitation(alice1));
Expand Down
118 changes: 118 additions & 0 deletions run/test/specs/group_tests_add_accountid.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
import { test, type TestInfo } from '@playwright/test';

import { englishStrippedStr } from '../../localizer/englishStrippedStr';
import { TestSteps } from '../../types/allure';
import { androidIt } from '../../types/sessionIt';
import { USERNAME } from '../../types/testing';
import { InviteAccountIDOrONS } from './locators';
import {
AcceptMessageRequestButton,
ConversationSettings,
MessageBody,
} from './locators/conversation';
import {
InviteContactSendInviteButton,
ManageMembersMenuItem,
ShareNewMessagesRadial,
} from './locators/groups';
import { MessageRequestItem, MessageRequestsBanner } from './locators/home';
import { EnterAccountID, NextButton } from './locators/start_conversation';
import { open_Alice1_Bob1_Charlie1_Unknown1 } from './state_builder';
import { sleepFor } from './utils';
import { newUser } from './utils/create_account';
import { truncatePubkey } from './utils/get_account_id';
import { closeApp, SupportedPlatformsType } from './utils/open_app';

androidIt({
title: 'Invite Account ID to group',
risk: 'high',
testCb: addAccountIDToGroup,
countOfDevicesNeeded: 4,
allureSuites: {
parent: 'Groups',
suite: 'Edit Group',
},
allureDescription:
'Verifies that inviting a non-contact Account ID (without chat history) works as expected.',
});

async function addAccountIDToGroup(platform: SupportedPlatformsType, testInfo: TestInfo) {
const testGroupName = 'Group to test adding contact';
const {
devices: { alice1, bob1, charlie1, unknown1 },
prebuilt: { alice },
} = await test.step(TestSteps.SETUP.QA_SEEDER, async () => {
return open_Alice1_Bob1_Charlie1_Unknown1({
platform,
groupName: testGroupName,
focusGroupConvo: true,
testInfo: testInfo,
});
});
const userD = await test.step(TestSteps.SETUP.NEW_USER, async () => {
return newUser(unknown1, USERNAME.DRACULA);
});
const aliceTruncatedPubkey = truncatePubkey(alice.sessionId, platform);
const historicMsg = `Hello from ${alice.userName}`;
const userDTruncatedPubkey = truncatePubkey(userD.accountID, platform);
const userDMsg = `Hello from ${userD.userName}`;
await test.step(TestSteps.SEND.MESSAGE(alice.userName, 'group'), async () => {
await alice1.sendMessage(historicMsg);
await Promise.all(
[alice1, bob1, charlie1].map(device =>
device.waitForTextElementToBePresent(new MessageBody(device, historicMsg))
)
);
});
await test.step(TestSteps.USER_ACTIONS.GROUPS_ADD_CONTACT(userD.userName), async () => {
// Click more options
await alice1.clickOnElementAll(new ConversationSettings(alice1));
// Select edit group
await alice1.clickOnElementAll(new ManageMembersMenuItem(alice1));
await sleepFor(1000);
// Add contact to group
await alice1.clickOnElementAll(new InviteAccountIDOrONS(alice1));
await alice1.inputText(userD.accountID, new EnterAccountID(alice1));
await alice1.clickOnElementAll(new NextButton(alice1));
await alice1.clickOnElementAll(new ShareNewMessagesRadial(alice1));
await alice1.clickOnElementAll(new InviteContactSendInviteButton(alice1));
});
// Leave Manage Members
await alice1.navigateBack();
// Leave Conversation Settings
await alice1.navigateBack();
// Check control messages
await test.step('Verify group invite control message for all members', async () => {
await Promise.all(
[alice1, bob1, charlie1].map(device =>
device.waitForControlMessageToBePresent(
englishStrippedStr('groupMemberNew').withArgs({ name: userDTruncatedPubkey }).toString(),
20_000
)
)
);
});
await test.step(`${userD.userName} accepts group invite and sends a message`, async () => {
await unknown1.clickOnElementAll(new MessageRequestsBanner(unknown1));
await unknown1.clickOnElementAll(new MessageRequestItem(unknown1));
await unknown1.waitForControlMessageToBePresent(
englishStrippedStr('messageRequestGroupInvite')
.withArgs({ name: aliceTruncatedPubkey, group_name: testGroupName })
.toString()
);
await unknown1.clickOnElementAll(new AcceptMessageRequestButton(unknown1));
await unknown1.waitForControlMessageToBePresent(
englishStrippedStr('groupInviteYou').toString()
);
await unknown1.verifyElementNotPresent(new MessageBody(unknown1, historicMsg));
await unknown1.sendMessage(userDMsg);
await Promise.all(
[alice1, bob1, charlie1, unknown1].map(device =>
device.waitForTextElementToBePresent(new MessageBody(device, userDMsg))
)
);
});
await test.step(TestSteps.SETUP.CLOSE_APP, async () => {
await closeApp(alice1, bob1, charlie1, unknown1);
});
}
Loading
Loading