ci: check the new online search in the iOS simulator tests

The smoke test, the device test plan and the Claude prompt check the enable alert (Cancel sends nothing, Enable searches, later searches don't ask), one list with Open and no headers, the Search key with "Connect to …" kept, Open reaching the prepared channel without an alert and with its subscriber count, the load row's count, loading and retry, New chat's card and the long-press menu. The load row's disabled state is read before the screenshot, which takes long enough for the stopped directory's request to fail.

Run: 011-search-redesign, slice 3 (feature CI)
This commit is contained in:
spacedandy-agents-simplex committed 2026-10-20 23:47:19 +00:00
1 parent 4e29980068
commit 45187c7c30
6 files changed
+281 -156

No files matched your search

+1 -1
View File
@@ -458,7 +458,7 @@ write_seed() {
echo "| \`$avatar_search_text\` | ci_avatar_g1…3 (groups), ci_avatar_c1…3 (channels), each with an avatar the size the app makes | avatars and the response size limit: about one entry per page |"
echo "| \`ci long\` | $long_name (group): a long name and a two-line description | D3 |"
echo "| \`ci markdown\` | ci_markdown (group): markdown in the description | D5 |"
echo "| \`cinews\` | cinews (channel, with the name cinews.testing), cinews_digest and cinews_weekly (groups) | a search that shows local chats, Names and Directory |"
echo "| \`cinews\` | cinews (channel, with the name cinews.testing), cinews_digest and cinews_weekly (groups) | the Search key with a name typed: directory results, with Connect to cinews.testing still shown |"
echo
sqlite3 -markdown "$work/directory_chat.db" "$listings_query"
echo
+19 -19
View File
@@ -13,47 +13,47 @@ You are testing the SimpleX Chat iOS app in an iPhone 17 simulator (iOS 26.5, sc
- **Positions.** "Above" means a smaller y. Judge order and position from the tree's coordinates, and look at a screenshot when you judge layout (truncation, overlap, images).
- **The search field** has no label; tap it with `ui tap-value "Search or paste SimpleX link"` when empty, or by its text. To clear it, tap the field so it is focused, then "Cancel" next to it ("Cancel" exists only while the field is focused; it also hides the keyboard); tapping the "Close" image inside the field did not clear it in an earlier run. Type text as one quoted argument (`ui type "ci seed"`). The keyboard capitalizes the first letter and autocorrects words (an earlier run saw "ci" become "Ci" and a typed name become another word when a button was tapped), so check the field's value in the tree after typing, and before typing check that the field shows its placeholder. The keyboard's Search key is `ui key return`; the keyboard's keys are below the screen and not listed in the tree, and the screenshots do not show the keyboard.
- **Pasting** (C4, W3): `ui clipboard "<link>"` with the link given under "This session", tap the empty field once to focus it and once more to open its menu, then tap its "Paste" item (two elements carry that label: use `ui tap-xy` on the one `ui find Paste` shows in the menu).
- **Directory search.** "Search in Directory" (a row) searches the directory; the first time after the hints are reset the alert "Search in Directory?" asks first, with "Cancel", "Search" and "Search and don't show again" (`ui tap Search` taps the alert's button). Results come as a heading `Heading "Directory"`, then per entry an avatar image, `"#name"`, the description, "N members" or "N subscriber(s)", and "Join group" or "Join channel" as separate texts. An entry you already have that the screen doesn't already list is shown instead as that chat's row, with no `#` and no "Join …": on the chat list the same row as your other chats (name, last message, time), in New chat a row like a contact's. When every result of the last page loaded is a chat already listed, the part shows "All results on this page are already in your chats". A failed search shows "Search failed, tap to retry"; more results show "Show more". A search takes 1–2 s here (it sets up an encrypted connection) and times out after 10 s. A channel's row shows the subscriber count the directory publishes for it (`publicMemberCount`), not the listings' members column, which counts every member record (owner, relay, directory), so the two differ by design.
- **Order with the one-hand UI.** Results read from the bottom like the chats: the first result is nearest the search field, each "Show more" adds its page above the earlier ones, and "Show more" sits right under the "Directory" heading. This order is settled (the user's decision: the list is mirrored; in New chat, Names stays above Directory); what must hold is that the heading is above all of the part's rows.
- **Directory search.** "Search in Directory" (a row) and the keyboard's Search key search the directory, and only the directory: no SimpleX name is resolved by a search, and "Connect to …" stays shown next to the results. While the switch Settings → Privacy → "Directory search" is off (the default), every search first asks "Enable Directory search?" with "Cancel" and "Enable"; Enable turns the switch on and searches, and later searches don't ask. "Search in Directory" is hidden while results for the current text load or show. Results come as one list without headers: your chats first, then per result an avatar image, `"#name"`, the description, "N members" or "N subscriber(s)", and "Open", as separate texts. An entry you already have that the screen doesn't already list is shown instead as that chat's row, with no `#` and no "Open": on the chat list the same row as your other chats (name, last message, time), in New chat a row like a contact's. When every result of the last page loaded is a chat already listed, the list shows "All results on this page are already in your chats". The last row is the load row, "Load more · N left" (or "Load more" when the directory sends no count); a failed search or page shows "Search failed, tap to retry". These two are buttons with an icon, so their label in the tree may start with the icon's name: find them with `ui find` and tap with `ui tap-xy` when `ui tap` with the text alone finds nothing. A search takes 1–2 s here (it sets up an encrypted connection) and times out after 10 s. A channel's row shows the subscriber count the directory publishes for it (`publicMemberCount`), not the listings' members column, which counts every member record (owner, relay, directory), so the two differ by design.
- **Order with the one-hand UI.** Results read from the bottom like the chats: your chats nearest the search field, then the results, each "Load more" adding its page above the earlier ones, and the load row at the top of the results, farthest from the field. With the top bar the order runs downwards from the field, the load row last.
- **Long press** (`ui long-press <x> <y>` at a result's centre): on a result row it opens a menu with one item, "Open", over a lifted copy of the row without its "Open". Close the menu by tapping outside it (for example `ui tap-xy 200 100`), not by choosing Open, unless the case says so.
- **Disabled elements** end with "disabled" in the tree.
- **Holding a search open.** A search answers in 1–2 s here, too fast to see its loading state. For the cases about a search in progress (D1, D12, D13, W9, G3, and "Show more" while loading in D7), stop the directory first (`ui directory stop`): the search then waits 10 s before it fails. Start it again afterwards. The 10 s count from the search's start, and each of your turns takes several seconds, so do the case's action (for G3, `ui tap Compose`) as the very next command after starting the search, without a shot in between; an action after the search has failed tests nothing.
- **Holding a search open.** A search answers in 1–2 s here, too fast to see its loading state. For the cases about a search in progress (D1, D11, D12, W9, G3, and "Load more" while loading in D6), stop the directory first (`ui directory stop`): the search then waits 10 s before it fails. Start it again afterwards. The 10 s count from the search's start, and each of your turns takes several seconds, so do the case's action (for G3, `ui tap Compose`) as the very next command after starting the search, without a shot in between; an action after the search has failed tests nothing.
- **Ambiguous labels.** When `ui tap` says several elements match a label (seen for "Paste", "Channels", "Account"), tap the one you mean with `ui tap-xy` at its centre, taken from `ui find`.
- **Hidden elements.** The tree always contains the closed user picker: the profile name, "Create SimpleX address", "Chat preferences", "Your chat profiles", "Use from desktop", "Settings", at coordinates on the screen even while it is hidden. Do not tap them unless you opened the picker by tapping your profile image in the toolbar (`Image "Account"`, 32 x 32; tap its centre with `ui tap-xy`).
- **Settings → Reset all hints:** open the picker, "Settings", scroll to the bottom, the app version row ("v…"), "Developer", "Reset all hints". Go back with the navigation back buttons and close Settings by swiping it down.
- **Settings → Privacy → "Directory search"** (C5–C7): open the picker, "Settings", "Privacy", the "Directory search" switch in the Chats section. Go back with the navigation back buttons and close Settings by swiping it down.
- **New chat** is the "Compose" button (the pencil) in the toolbar. With the top bar the toolbar is hidden while the search field is focused; tapping the "Search in Directory" row hides the keyboard, and the pencil shows again while that search runs.
- **Counting requests.** `ui requests` tells how many directory searches the app sent; compare before and after an action to prove that something was or was not sent (C6, C9, D12, D13).
- **Joining** (the app's normal flow, from `NewChatView.swift`; an earlier session took its first step for a failure). Tapping a group or channel the profile does not know yet, from a link or a list, shows an alert with its name (and subscriber count for a channel) and "Cancel" / "Open channel" or "Open group": that is the start of joining, not a sign that the entry is known. "Open …" opens the chat, not yet joined, with "Tap Join channel" in it and a "Join channel" / "Join group" bar at the bottom; tapping that bar joins (a channel then shows "you are subscriber"; a group may stay "connecting"). A contact works the same way: "Open new chat", then the chat's connect button. An entry already opened, joined or connecting shows the same kind of alert, plus your role ("You are a subscriber", "You are a member") once joined. So each case that joins uses the entry named for it under "This session", which no earlier case has touched. Opened and joined chats appear in the chat list, and the profile is no longer "without conversations".
- **A second profile** (D14, and D15 once the first profile has chats): open the picker, "Your chat profiles", add a profile; a new profile has no conversations. Switch back to the first profile when done.
- **Counting requests.** `ui requests` tells how many directory searches the app sent; compare before and after an action to prove that something was or was not sent (C6, C9, D11, D12).
- **Joining** (the app's normal flow, from `NewChatView.swift`; an earlier session took its first step for a failure). Tapping a result or its "Open" shows "Loading profile…" in the field, then opens the group or channel, not yet joined, with no alert: "Tap Join channel" in it and a "Join channel" / "Join group" bar at the bottom; tapping that bar joins (a channel then shows "you are subscriber"; a group may stay "connecting"). Opening a group or channel from a link instead shows an alert first, with its name (and subscriber count for a channel) and "Cancel" / "Open channel" or "Open group": that is the start of joining, not a sign that the entry is known. An entry already opened, joined or connecting shows the same kind of alert, plus your role ("You are a subscriber", "You are a member") once joined. So each case that opens or joins uses the entry named for it under "This session", which no earlier case has touched. Opened and joined chats appear in the chat list, and the profile is no longer "without conversations".
- **A second profile** (D13, and D14 once the first profile has chats): open the picker, "Your chat profiles", add a profile; a new profile has no conversations. Switch back to the first profile when done.
- **Text size** (D3): `ui text-size accessibility-large`, and back to `ui text-size medium` afterwards. Only one phone size is available.
- **If the app gets stuck**, `ui relaunch` restarts it (search text and results are cleared, everything else stays).
- Waiting: prefer `ui wait <text> <secs>` over repeated trees; `ui wait` returns at once when the text is already shown.
## The test backend
When "This session" gives the test directory's address, the runner also runs a test backend: a chat relay, a SimpleX Directory with the groups and channels listed under "Test directory" below, a server that resolves SimpleX names, and contacts that accept connections. `ui directory stop|start` stops and starts the directory (after `ui directory start`, wait about 15 s before using it), and `ui listings` prints what it lists. Stop the directory instead of turning networking off (D8, D9; the simulator shares the runner's network): while it is stopped, searches fail after 10 s.
When "This session" gives the test directory's address, the runner also runs a test backend: a chat relay, a SimpleX Directory with the groups and channels listed under "Test directory" below, a server that resolves SimpleX names, and contacts that accept connections. `ui directory stop|start` stops and starts the directory (after `ui directory start`, wait about 15 s before using it), and `ui listings` prints what it lists. Stop the directory instead of turning networking off (D7, D8; the simulator shares the runner's network): while it is stopped, searches fail after 10 s.
**SimpleX names**: the names server's address is under "This session"; the names it knows are under "Test directory" → "SimpleX names" (all end in `.testing`; a bare word of 5 or more letters is completed with `.testing`, so `cinews` is `cinews.testing`). Set the app up for it once, before the first case that needs names (if an earlier session in this run did it, check it instead: the server is listed under "Your servers" and "To resolve names" is "yes" on it; finish step 4 on that server if not, rather than adding it again, as a second copy of an address is an error):
**SimpleX names**: a search never resolves names; only "Connect to …" does (C3, C11, W2, G5). The names server's address is under "This session"; the names it knows are under "Test directory" → "SimpleX names" (all end in `.testing`; a bare word of 5 or more letters is completed with `.testing`, so `cinews` is `cinews.testing`). Set the app up for it once, before the first case that taps "Connect to …" (if an earlier session in this run did it, check it instead: the server is listed under "Your servers" and "To resolve names" is "yes" on it; finish step 4 on that server if not, rather than adding it again, as a second copy of an address is an error):
1. Open the picker (your profile image), "Settings", "Network & servers".
2. Under "Preset servers", open each operator ("SimpleX Chat", "Flux"), turn "To resolve names" off, and go back. Otherwise the app may ask the real servers, which do not know the test names.
3. "Your servers" → "Add server" → "Enter server manually". Put the names server's address in the address field by pasting it (`ui clipboard "<address>"`, tap the field twice, tap the menu's "Paste"; typing it would let the keyboard capitalize it), check it in the tree, and go back.
4. In "Your servers", open the new server and turn "Use for new connections" on: only then does the server show its pickers "To receive", "For private routing" and "To resolve names". Set them to "no", "no" and "yes", then go back.
5. On "Network & servers", tap "Save servers" (and "Save" if asked). Close Settings.
6. Check: type `cinews`, press the Search key: a "Names" part with a row for the channel cinews must appear. If it does not, record what you see and look at the steps again before running the N cases.
6. Check: type `cinews` and tap "Connect to cinews.testing": the alert for the channel cinews must appear (Cancel it). If it does not, record what you see and look at the steps again before running the cases that connect by name.
**What the names are for:** `cinews` is a listed channel (N1, N6, the full search); `cialice` and `cibobby` are contacts that accept a connection at once (N2, N5, N10, W7, W8, G1; `cibobby`'s profile is called "Robert", so its name is not in its display name); `ciboth` has both a contact address and a channel (N3); `cinone` is not registered (N4). N7 needs the second profile with "To resolve names" off for both operators (step 2 only, no server added); a new profile copies the current profile's servers, the names server included, so on the second profile also open "Your servers" and set that server's "To resolve names" to "no". Which case uses which contact in this layout is under "This session".
**The full search** (after N1): with a local chat whose name contains "cinews" (join the group cinews_digest from its Directory row first, if it is not in your chats yet), type `cinews` and press the Search key. The plan's parts are the chats already loaded, a "Names" part and a "Directory" part (N1: the "Names" header above the Directory part; W4 for New chat: "Names" and "Directory" after "Your Contacts"). Record for this layout, from the tree, every header and row with its y, top to bottom: the local chats, "Names" with its rows, "Directory" with its rows (cinews_weekly; cinews should not repeat there, as it is the Names row), and say whether the part names and their order match the plan. Do the same in New chat (W4) when the session's cases include New chat.
**What the names are for:** `cinews` is a listed channel (C11, G5); `cialice` and `cibobby` are contacts that accept a connection at once (G1, G5; `cibobby`'s profile is called "Robert", so its name is not in its display name); `ciboth` has both a contact address and a channel; `cinone` is not registered. Which case uses which contact in this layout is under "This session".
## How to run the cases
- Run each case of the session's list, in the plan's order unless a case needs another first (for example D15 needs a profile without conversations; U1, U2, U4 and W12 need a joined channel: use the one named under "This session", or one joined in D6).
- Run each case of the session's list, in the plan's order unless a case needs another first (for example D14 needs a profile without conversations; U1, U2, U4 and W12 need a joined channel: use the one named under "This session", or one joined in D5).
- The plan's tables (ID, Do, Expect) are at the end of `prompt.md` in the current directory: read the row before running a case and do exactly what it says. An earlier session did not find them and filed other actions under some IDs.
- For each case, do what the plan says, decide pass or fail against the plan's "Expect" column, and take at least one `ui shot` at the moment that shows the result, named after the case and layout without a number, for example `ui shot D2-reachable` (the helper numbers the files).
- Pass only what you saw: quote the evidence (element labels, values and coordinates from the tree, or what a screenshot shows). If an expectation cannot be checked here, say which part and why, and mark the case passed for the parts you checked only if those parts are the substance of the case; otherwise skipped.
- **Skip** any case that needs something this runner lacks, saying exactly what is missing. Known: U3 (needs a listed group joined through another link), W10 (needs a notification from another profile), W11 unless "Archived contacts" is shown. Do not skip a case only because it takes several steps.
- **The Directory header** must be above its results in this layout (D2, D4, D8 and every screen with results): record the heading's y and the first and last result's y each time, and note whether `Heading "Directory"` comes before or after the result texts in the tree's order.
- **Avatars and the response size limit** (part of D2 and D7): search `ci avatar`. Six entries match; each has an avatar about the size the app makes, and a directory response must fit 10737 bytes, so expect about one entry per page. Tap "Show more" until it disappears and record which entries each page added. Compare with the page-by-page table under "Test directory" below: every entry reachable, none twice, none silently missing. Look at the screenshots: does every row show its picture (not the placeholder icon), is any row drawn wrong?
- **Skip** any case that needs something this runner lacks, saying exactly what is missing. Known: U3 (needs a listed group joined through another link), D16 (needs a pending join through a full link), W10 (needs a notification from another profile), W11 unless "Archived contacts" is shown. Do not skip a case only because it takes several steps.
- **The order of the results** in this layout (D2, D6, U2 and every screen with results): record the y of your chats' rows, the first and last result and the load row each time, and check the order described under "Order with the one-hand UI"; there must be no "Directory" or "Names" header.
- **The long-press menu** (D15, W8): look at the screenshot taken while the menu shows. Record whether the lifted copy of the row is upright and readable, has no "Open", and whether the menu shows "Open"; then close it as described above.
- **Avatars and the response size limit** (part of D2 and D6): search `ci avatar`. Six entries match; each has an avatar about the size the app makes, and a directory response must fit 10737 bytes, so expect about one entry per page. Tap the load row until it disappears and record which entries each page added and the "N left" each time. Compare with the page-by-page table under "Test directory" below: every entry reachable, none twice, none silently missing. Look at the screenshots: does every row show its picture (not the placeholder icon), is any row drawn wrong?
- Other app bugs you notice are findings: write what you did, what you saw, what you expected, and the screenshot.
- Mind the session's turn limit under "This session": every `ui` command and every file edit is a turn.
@@ -63,8 +63,8 @@ Write `report.md` in the current directory as you go, so a session that is cut s
1. `# Device test report: <layout>` and one line on the app state at the start.
2. `## Results`: a table with one row per case of the session's list: `| ID | Result | Evidence | Screenshots |`, Result being pass, fail or skipped (with the reason).
3. `## Directory header`: per screen with results, the heading's y and the first and last result's y, and the heading's place in the tree order.
4. `## Avatars and the size limit` (when the session's cases include D2 or D7): what each "Show more" added, compared with the table below, and how the avatars looked.
3. `## Order of the results`: per screen with results, the y of your chats' rows, the first and last result and the load row, and whether the order matches.
4. `## Avatars and the size limit` (when the session's cases include D2 or D6): what each "Load more" added and its "N left", compared with the table below, and how the avatars looked.
5. `## Findings`: app bugs, each with steps, what you saw, what you expected, screenshots.
6. `## Harness notes`: anything about `ui` or the runner that got in the way.
+4 -4
View File
@@ -5,9 +5,9 @@
backend: yes
model: claude-haiku-5-5
layouts: reachable top
cases-reachable: U1, U2, U4 and W12
cases-top: U2 and U4 (the list's structure differs in this layout)
entries-reachable: the smoke test joined the channel ci_cards; U1 and U4 search `ci_cards`, U2 and W12 search `ci cards` (the space keeps the local list from matching it); join nothing else
entries-top: the same entries: U4 searches `ci_cards`, U2 searches `ci cards`; nothing else is joined
cases-reachable: U1, U2, U4, D15 and W12
cases-top: U2, U4 and D15 (the list's order and the row's flip differ in this layout)
entries-reachable: the smoke test joined the channel ci_cards; U1 and U4 search `ci_cards`, U2 and W12 search `ci cards` (the space keeps the local list from matching it); D15 searches `ci seed` and long-presses #ci_group, closing the menu without choosing Open; open and join nothing else
entries-top: the same entries: U4 searches `ci_cards`, U2 searches `ci cards`, D15 long-presses #ci_group from `ci seed`; nothing else is opened or joined
max-turns: 300
max-budget-usd: 3
+1 -1
View File
@@ -77,7 +77,7 @@ write_prompt() {
echo "- No test backend runs in this session."
fi
if [ -z "$previous" ] && [ -n "$BACKEND_DIR" ]; then
echo "- At the start: the smoke test onboarded a profile (typed as \"citester\"; the keyboard may have autocorrected the name), searched the directory in both layouts, choosing \"Search\" in the alert each time, and joined the channel ci_cards; it is the profile's only conversation."
echo "- At the start: the smoke test onboarded a profile (typed as \"citester\"; the keyboard may have autocorrected the name), enabled Directory search in the first search's alert, searched the directory in both layouts, and opened and joined the channel ci_cards; it is the profile's only conversation."
elif [ -z "$previous" ]; then
echo "- At the start: the smoke test onboarded a profile (typed as \"citester\"; the keyboard may have autocorrected the name) and checked the chat list in both layouts; the profile has no conversations."
else
+43 -55
View File
@@ -1,11 +1,11 @@
# Device test plan: iOS online search (f/directory-search)
Run these on an iOS simulator or device on the Mac. Each case says what to do and what you should see. The case IDs map to the run's acceptance criteria (AC1–AC8) and review fixes (R1-x).
Run these on an iOS simulator or device on the Mac. Each case says what to do and what you should see. The case IDs map to the run's acceptance criteria (AC3–AC9).
## Setup
1. **Build** `f/directory-search` (HEAD `e7678b634` or later) in Xcode on a simulator. Use a small one (iPhone SE) and a large one, or switch Dynamic Type to a large size for the layout cases.
2. **A directory the app can reach with service requests.** The app sends every search to `DIRECTORY_SERVICE_LINK` (`apps/ios/Shared/Model/DirectorySearch.swift:14`), which is the live SimpleX Directory. Searches against the live directory fail with `ASENotDRAddress` until it runs `/_rotate_address_keys <bot user id>`. Pick one:
1. **Build** `f/directory-search` in Xcode on a simulator. Use a small one (iPhone SE) and a large one, or switch Dynamic Type to a large size for the layout cases. The long-press menu (D15, W8) needs iOS 16 or later; on iOS 15 there is no menu.
2. **A directory the app can reach with service requests.** The app sends every search to `DIRECTORY_SERVICE_LINK` (`apps/ios/Shared/Model/DirectorySearch.swift`), which is the live SimpleX Directory. Searches against the live directory fail with `ASENotDRAddress` until it runs `/_rotate_address_keys <bot user id>`. Pick one:
- **Live directory:** rotate its address keys first. The published link does not change.
- **Test directory:** run `simplex-directory-service` against your own servers (it turns service requests on by itself). Create its address with `/ad pq_ratchet=on`, take the short link, and point `DIRECTORY_SERVICE_LINK` at it in your local build only. Do not commit that change.
3. **Directory content** (most useful with a test directory):
@@ -14,8 +14,8 @@ Run these on an iOS simulator or device on the Mac. Each case says what to do an
- one entry with a long name and a two-line description;
- one entry whose name contains markdown, e.g. `*bold* [x](https://example.com)`;
- one channel the test profile has already joined.
4. **SimpleX names** (for the Names cases): the test profile needs a server that resolves names. You also need a channel with a verified `#name` and a contact address with an `@name`, each at least 5 characters long (a bare word is completed with `.testing`). Without these, only cases N4 and N7 can run.
5. **Consent alert:** Settings → "Reset all hints" brings it back between runs.
4. **SimpleX names** (for "Connect to …", C3, C11 and G5): the test profile needs a server that resolves names, and a channel with a verified `#name` and a contact address with an `@name`, each at least 5 characters long (a bare word is completed with `.testing`). The search itself never resolves names.
5. **Directory search switch:** Settings → Privacy → "Directory search" (in the Chats section), off by default. While it is off, every search asks first; turn it off again to repeat the consent cases.
6. Run every chat-list case in **both toolbar modes** (one-hand UI on and off).
## Chat list: search rows and consent
@@ -25,74 +25,61 @@ Run these on an iOS simulator or device on the Mac. Each case says what to do an
| C1 | Empty search field | Tags in the slot above the field (one-hand) or above as usual (top bar); no "Search in Directory" row |
| C2 | Type `ne` | One-hand: "Search in Directory" replaces the tags above the field. Top bar: tags stay, "Search in Directory" appears below the field |
| C3 | Type a 5+ letter word, e.g. `alice` | "Connect to alice.testing" and "Search in Directory" (same slots as C2) |
| C4 (AC4) | Paste a SimpleX link | One-hand: the **tags stay** in the slot (before this change the slot was empty). Neither mode offers "Search in Directory"; the link connects as before |
| C5 (AC8) | After resetting hints, tap "Search in Directory" | Alert "Search in Directory?": the text goes to SimpleX Directory, and if it is a SimpleX name, also to your server that resolves names. Actions: Cancel / Search / Search and don't show again |
| C6 | In C5, choose Cancel | Nothing is sent: no spinner, no results |
| C7 | Choose "Search and don't show again", then search again | No alert the second time; "Reset all hints" brings it back |
| C8 | Type text, press the keyboard **Search** key | The key is labelled Search; it runs the same directory search (alert first time) |
| C4 | Paste a SimpleX link | One-hand: the tags stay in the slot. Neither mode offers "Search in Directory"; the link connects as before |
| C5 (AC4) | With "Directory search" off, tap "Search in Directory" | Alert "Enable Directory search?", message "Search text is sent to SimpleX Directory, each search separately and without your profile, so it can't build your search history.", actions Cancel and Enable |
| C6 (AC4) | In C5, choose Cancel | Nothing is sent: no spinner, no results, the request count does not change; "Search in Directory" is still shown |
| C7 (AC4) | Choose Enable | The search runs; Settings → Privacy → "Directory search" is now on; later searches, by the row or the key, do not ask; turning the switch off brings the alert back |
| C8 (AC3) | Type text, press the keyboard **Search** key | The key is labelled Search; it runs the same directory search as the row (the alert first while the switch is off) |
| C9 | Whitespace only, then press Search or tap the row | Nothing happens |
| C10 (AC3) | Search, then look at the rows by the field while the results load and show; then change the text | "Search in Directory" is hidden while the results for the current text load or show; it comes back when the text changes |
| C11 (AC3) | Type a 5+ letter name of a listed channel, e.g. `cinews`, press **Search** | Directory results only: no "Names" part, no name resolution. "Connect to cinews.testing" stays shown next to the results (one-hand: above the field; top bar: below it) |
## Chat list: directory results (AC1–AC3)
## Chat list: results (AC5–AC8)
| ID | Do | Expect |
|----|----|--------|
| D1 | Search for a term that matches listed groups | Spinner in the search field after about 1 s. While loading: **no "Directory" header**, no "No chats found" overlay |
| D2 | Results arrive | A "Directory" header row, then rows: avatar, `#` name, the directory's SimpleX name (if any), description up to 2 lines, member or subscriber count, and **"Join channel"** for channels or **"Join group"** for groups at the trailing edge. "Join …" rows are for entries you don't have; an entry you already have is a chat row (U2) |
| D3 (R1-4) | Long-named entry, small phone, large text | The verb stays on one line; the name and description truncate instead |
| D4 | Scroll the list, especially in one-hand mode | The "Directory" header scrolls with the rows and is not pinned to the screen edge |
| D5 | Entry with markdown in its name or description | Shown literally: no bold, no tappable link |
| D6 | Tap a directory row | The usual join flow (channel or group alert); the keyboard hides |
| D7 | More entries than one page | A "Show more" row; tapping it appends the next page, is disabled while loading, and disappears at the end. No entry appears twice |
| D8 | Turn networking off, search | After about 10 s: "Search failed, tap to retry" under the "Directory" header. Turn networking on, tap it: the page loads |
| D9 | Fail a "Show more" (network off), then retry | The retry continues from the next page; it does not start over |
| D10 (AC3) | Search for nonsense, e.g. `zqxvjk`, with no local chat matching | No Directory part, no "No results" row; the centred "No chats found" appears once the search finishes |
| D11 | Search for nonsense that matches a local chat name | Only the local chats; no Directory part, no overlay |
| D12 | Start a search, change the text before it returns | Results and spinner clear at once; the old reply never appears |
| D13 | Start a search, clear the field | The spinner stops; nothing appears later |
| D14 | With results shown, switch profile | Results disappear |
| D15 | Profile with no conversations (onboarding pages shown) | The search bar is shown with the onboarding pages (below them with the one-hand UI, above them with the top bar). A search replaces them with the results while it runs or shows rows; a search that finds nothing shows a "No chats found" row; clearing the text brings the onboarding pages back |
| D1 | Search for a term that matches listed groups | Spinner in the search field after about 1 s. While loading: no header, no "No chats found" overlay |
| D2 (AC5) | Results arrive | One list without headers (no "Directory", no "Names"): your chats first, then the results. A result row: avatar the size of a chat row's, `#` name, the directory's SimpleX name (if any), description up to 2 lines, member or subscriber count, and **"Open"** at the trailing edge. No "Join channel" or "Join group" |
| D3 | Long-named entry, small phone, large text | "Open" stays on one line; the name and description truncate instead |
| D4 | Entry with markdown in its name or description | Shown literally: no bold, no tappable link |
| D5 (AC6) | Tap a channel's result (or its "Open") | The keyboard hides, the spinner shows in the field ("Loading profile…"), then the channel opens, not yet joined, with "Tap Join channel" and the "Join channel" bar, and with **no "Open channel?" alert**. Its subscriber count shows in the toolbar and in the banner under the name. A group's result opens the same way, with "Tap Join group" |
| D6 (AC7) | More entries than one page | The last row of the results is "Load more · N left" (N from the directory) or "Load more" (when the directory sends no count), with a plus-magnifier icon; one-hand: it is the top row of the results, farthest from the field. Tapping it appends the next page; while it loads the row is grey and disabled; it disappears at the end. No entry appears twice |
| D7 (AC7) | Stop the directory, search | After about 10 s: "Search failed, tap to retry" with a circular arrow, where the results go. Start the directory, tap it: the page loads |
| D8 (AC7) | Fail a "Load more" (directory stopped), then retry | The row turns into "Search failed, tap to retry"; the retry continues from the next page; it does not start over |
| D9 | Search for nonsense, e.g. `zqxvjk`, with no local chat matching | No result rows; the centred "No chats found" appears once the search finishes |
| D10 | Search for nonsense that matches a local chat name | Only the local chats; no overlay |
| D11 | Start a search, change the text before it returns | Results and spinner clear at once; the old reply never appears |
| D12 | Start a search, clear the field | The spinner stops; nothing appears later |
| D13 | With results shown, switch profile | Results disappear |
| D14 | Profile with no conversations (onboarding pages shown) | The search bar is shown with the onboarding pages (below them with the one-hand UI, above them with the top bar). A search replaces them with the results while it runs or shows rows; a search that finds nothing shows a "No chats found" row; clearing the text brings the onboarding pages back |
| D15 (AC8) | Long press a result | A menu with one item, "Open", over a lifted preview of the row (the row without its "Open"), drawn upright in both layouts. Choosing Open does what a tap does; dismissing the menu opens nothing |
| D16 (AC5) | Manual only: a full-link entry you are already joining (joined, not yet connected) | Its caption says "Already joining" in place of the count, with nothing at the trailing edge; tapping it shows the usual "already joining" sheet or alert |
## Chat list: no duplicates (AC6)
## Chat list: your chats (AC5)
| ID | Do | Expect |
|----|----|--------|
| U1 | Search for the name of a channel you joined (it is also listed) | It appears once, as your local chat; no Directory row for it |
| U2 | Search for a word only in that channel's description, so the local list does not show it | It appears in the Directory part as your chat's row (as in the chat list: name without `#`, last message, time), with no "Join channel"; tapping it opens the channel, with no alert |
| U3 | A listed regular group you joined through a different link (or by invitation) | It shows twice, local and Directory. This is the documented limit, not a regression |
| U4 | Search for the name of a joined channel that is the page's only result, so the list shows it as your chat | Under the "Directory" header: "All results on this page are already in your chats" (grey), and "Show more" when there are more pages (with the one-hand UI it sits right under the header, as in D7); nothing loads more pages by itself |
| U1 | Search for the name of a channel you joined (it is also listed) | It appears once, as your local chat; no result row for it |
| U2 | Search for a word only in that channel's description, so the local list does not show it | It appears as your chat's row (as in the chat list: name without `#`, last message, time), before the result rows, with no "Open"; tapping it opens the channel, with no alert |
| U3 | A listed regular group you joined with `/c` or through another link, or by invitation | It shows as a result with "Open" beside its own chat row. This is the documented limit, not a regression |
| U4 | Search for the name of a joined channel that is the page's only result, so the list shows it as your chat | "All results on this page are already in your chats" (grey), and the load row when there are more pages; nothing loads more pages by itself |
## Chat list: names (AC5)
| ID | Do | Expect |
|----|----|--------|
| N1 | Type a bare name registered only as a channel (e.g. `news1`), press **Search** | Directory results as above, plus a "Names" header above the Directory part with one row: avatar, profile name in bold, `(#news1…)` in grey, description. The "Connect to …" row disappears |
| N2 | Type `@alice.testing` (registered contact address), press Search | One Names row for the contact |
| N3 | Bare name registered as both a contact and a channel | Up to two Names rows |
| N4 (R1-6 note) | Name that is not registered, press Search | **No alert**, no Names row; "Connect to …" stays; directory results still come |
| N5 | Name of a contact you already have, shown in the local list | No Names row for it |
| N6 | A channel found both by name and in the directory | Shown once, as the Names row. If it also shows in Directory, note it (link encoding differs; nothing should ever be hidden wrongly) |
| N7 | Profile without a name-resolving server, press Search on a name | No "None of your servers…" alert; just no Names row |
| N8 | After names arrive, tap "Search in Directory", "Show more" or retry | The Names rows stay |
| N9 | Tap "Search in Directory" (not the key) with a name candidate | Directory only; no Names part |
| N10 | Tap a Names row | The usual connect flow (contact or channel alert). If the directory page is still loading, it stops, and the Names rows stay (also on the onboarding screen) |
| N11 | Start a connect (tap a result, "Loading profile…"), and while it runs press Search | The spinner stays the connection's; when both finish, no spinner is left behind |
## New chat (AC7)
## New chat (AC9)
| ID | Do | Expect |
|----|----|--------|
| W1 | Open New chat (pencil), type text | "Search in Directory" below the field; the action rows (1-time link, scan, create group/channel) hide as before |
| W2 | Type a 5+ letter word | "Connect to … " and "Search in Directory" below the field |
| W3 | Paste a link | No "Search in Directory"; the link connects as before |
| W4 | Press the keyboard **Search** key | "Names" and "Directory" sections after "Your Contacts" (which shows only while a contact matches), with uppercase grey headers like it; first time, the same consent alert |
| W5 | Search with no contact matching | "No filtered contacts" is hidden while searching and when there are results; shown when nothing is found anywhere |
| W6 | Tap a Directory or Names result | The sheet closes and the join or connect flow runs |
| W7 | Name of a contact listed in "Your Contacts" | No Names row for it |
| W8 | Name of a known contact whose display name does not contain the typed text | It shows as a Names row (documented gap); tapping opens the existing chat |
| W4 | Press the keyboard **Search** key | The directory results, after the alert while the switch is off; "Connect to …" stays; "Search in Directory" hides while the results show |
| W5 | Results arrive | One card without a header after "Your Contacts" (which shows only while a contact matches): your chats, then the results with 30-pt avatars and "Open", the load row last |
| W6 | Search with no contact matching | "No filtered contacts" is hidden while searching and when there are results; shown when nothing is found anywhere |
| W7 | Tap a result or its "Open" | The sheet closes and the chat opens prepared, with no alert |
| W8 | Long press a result | A menu with "Open", as in D15 |
| W9 | Close the sheet while a search runs | The spinner stops; the chat list is unaffected |
| W10 (R1-1) | Sheet open with results, tap a notification from another profile | The results disappear |
| W10 | Sheet open with results, tap a notification from another profile | The results disappear |
| W11 | New chat → Archived contacts, type and press return | The key is "return", not Search; no online rows; empty states as before |
| W12 | In New chat, search the directory for a word in the description of a channel or group you joined | In the Directory section it is a chat row like a contact's (avatar, name without `#`, a star if favourite, a mask if incognito), with no "Join …"; tapping it closes the sheet and opens the chat |
| W12 | In New chat, search the directory for a word in the description of a channel or group you joined | In the results card it is a chat row like a contact's (avatar, name without `#`, a star if favourite, a mask if incognito), with no "Open"; tapping it closes the sheet and opens the chat |
## Regression checks
@@ -102,3 +89,4 @@ Run these on an iOS simulator or device on the Mac. Each case says what to do an
| G2 | Tag filters, unread filter, clearing the search | Unchanged |
| G3 | Tap the pencil while a chat-list search runs | The chat-list search is cancelled (spinner stops) |
| G4 | Pull to refresh (reconnect servers) | Unchanged |
| G5 | Tap "Connect to …" for a registered name | The name resolves and the usual contact or channel alert shows, as before |
+213 -76
View File
@@ -2,11 +2,14 @@
# Installs the app in a new iPhone simulator and runs the scripted UI cases with the ui helper: onboarding to the
# chat list with the one-hand UI (the default), then the chat list again with the top bar. It leaves the app in the one-hand UI.
# With BACKEND_DIR, the work dir of the running test backend, it then searches the test directory, whose results
# must show their avatars and have the Directory header above them in both layouts; then, with a joined channel,
# the chat list's cards must show while typing and hide under the results; a page whose only result is that channel
# must say so; the channel found by its description must be its chat row, opening it, on the chat list in both layouts
# and in New chat; and "Show more" must be disabled while its page loads (a screenshot shows its colour).
# With BACKEND_DIR, the work dir of the running test backend, it then searches the test directory: the first search
# asks to enable Directory search (Cancel sends nothing, Enable searches), and no later search, the Search key's included,
# asks again; the results show Open and their avatars, with no header, in both layouts; Open prepares a channel and
# opens it without an alert, with its subscriber count; once joined, the channel is its chat row among the results, on
# the chat list in both layouts and in New chat; the chat list's cards show while typing and hide under the results;
# a page whose only result is that channel says so; the load row says how many results are left, is disabled while
# its page loads (a screenshot shows its colour) and offers a retry when that fails; and a long press on a result
# shows a menu with Open (not fatal: the gesture is new to this harness; a screenshot shows it).
# Usage: scripts/ci/ios-sim/smoke-test.sh <path to SimpleX.app> <output dir>
# Each step saves a screenshot and an accessibility tree to the output dir and prints the tree to the log.
# Later steps of the job get SIM_UDID and UI_OUT, with the simulator still running.
@@ -91,23 +94,53 @@ expect_chat_list() {
echo "in the $1 layout the chat list shows the onboarding pages and the toolbar"
}
# the y of the first element with exactly this label
element_y() {
# the first element whose label contains the text
labelled_containing() {
local tree
tree=$("$ui" tree)
grep -F "\"$1\" value=" <<< "$tree" | head -n 1 | sed -E 's/.* at [0-9-]+,([0-9-]+) .*/\1/'
grep -F -- "$1" <<< "$tree" | head -n 1 || true
}
expect_header_above_results() {
local header group channel
header=$(element_y Directory)
group=$(element_y "#ci_group")
channel=$(element_y "#ci_channel")
if [ -z "$header" ] || [ -z "$group" ] || [ -z "$channel" ] || [ "$header" -ge "$group" ] || [ "$header" -ge "$channel" ]; then
echo "error: in the $1 layout the Directory header (y=$header) is not above its results (#ci_group y=$group, #ci_channel y=$channel)"
# The "Open" on the row of the result labelled exactly $1, or nothing: the row's texts are stacked beside its avatar
# and Open is centred on the row, so Open's top is within a row's height of the name's top.
open_in_row() {
local tree name top line y
tree=$("$ui" tree)
name=$(grep -F "\"$1\" value=" <<< "$tree" | head -n 1 || true)
if [ -z "$name" ]; then
return 0
fi
top=$(sed -E 's/.* at [0-9-]+,([0-9-]+) .*/\1/' <<< "$name")
while IFS= read -r line; do
y=$(sed -E 's/.* at [0-9-]+,([0-9-]+) .*/\1/' <<< "$line")
if [ "$y" -ge $((top - 10)) ] && [ "$y" -le $((top + 60)) ]; then
echo "$line"
return 0
fi
done < <(grep -F '"Open" value=' <<< "$tree" || true)
}
# #ci_group and #ci_channel each with Open on its row and its avatar; no header, and no "Search in Directory" while results show
expect_seed_results() {
local tree name
for name in "#ci_group" "#ci_channel"; do
if [ -z "$(open_in_row "$name")" ]; then
echo "error: in the $1 layout $name is not a result with Open on its row"
exit 1
fi
done
tree=$("$ui" tree)
# matched exactly: "Search in Directory" also contains the word
if grep -qF '"Directory" value=' <<< "$tree"; then
echo "error: in the $1 layout the results have a Directory header"
exit 1
fi
echo "in the $1 layout the Directory header (y=$header) is above its results (#ci_group y=$group, #ci_channel y=$channel)"
if grep -qF '"Search in Directory"' <<< "$tree"; then
echo "error: in the $1 layout \"Search in Directory\" is shown while the results are"
exit 1
fi
expect_avatars "$1"
echo "in the $1 layout #ci_group and #ci_channel are results with Open, with no header and no \"Search in Directory\""
}
# a result without an avatar shows a placeholder icon labelled with its symbol name; an avatar image has no label
@@ -127,12 +160,24 @@ type_search() {
sleep 2
}
# the alert asks before every search, as the first search chose "Search", not "Search and don't show again"
search_directory() {
"$ui" tap "Search in Directory"
if "$ui" wait "Search in Directory?" 10; then
"$ui" tap "Search"
# the first search turned Directory search on, so no later one may ask to enable it
expect_no_consent() {
local tree
tree=$("$ui" tree)
if grep -qF "Enable Directory search?" <<< "$tree"; then
"$ui" shot consent-asked-again
echo "error: $1 asked to enable Directory search again"
exit 1
fi
}
tap_search_in_directory() {
"$ui" tap "Search in Directory"
expect_no_consent "\"Search in Directory\""
}
search_directory() {
tap_search_in_directory
"$ui" wait "$1" 60
}
@@ -183,8 +228,7 @@ expect_cards_hidden_by_results() {
echo "error: in the $1 layout the directory results are shown with cards: $(paste -sd ',' - <<< "$results")"
exit 1
fi
expect_header_above_results "$1 (chat list)"
expect_avatars "$1 (chat list)"
expect_seed_results "$1 (chat list)"
clear_search
sleep 2
"$ui" shot "cards-cleared-$1"
@@ -196,12 +240,12 @@ expect_cards_hidden_by_results() {
echo "in the $1 layout the cards ($(paste -sd ',' - <<< "$typing")) show while typing, are hidden under the results and come back when the search is cleared"
}
# the joined channel must not be offered to join again
# ci_cards is the only result for "ci cards": once joined, it must be its chat row, not a result to open
expect_no_join_row() {
local tree
tree=$("$ui" tree)
if grep -qF '"#ci_cards"' <<< "$tree" || grep -qF 'Join channel' <<< "$tree"; then
echo "error: in $1 the joined ci_cards is a Directory row (#ci_cards, Join channel), not its chat"
if grep -qF '"#ci_cards"' <<< "$tree" || grep -qF '"Open" value=' <<< "$tree"; then
echo "error: in $1 the joined ci_cards is a result row (#ci_cards, Open), not its chat"
exit 1
fi
}
@@ -242,6 +286,7 @@ topmost() {
sed -E 's/.* at [0-9-]+,([0-9-]+) .*/\1 &/' <<< "$1" | sort -n | head -n 1 | cut -d ' ' -f 2-
}
# taps the centre of the element on this tree line, or long-presses it with long-press
tap_element() {
local x y w h
read -r x y w h <<< "$(sed -nE 's/.* at ([0-9]+),([0-9]+) size ([0-9]+)x([0-9]+).*/\1 \2 \3 \4/p' <<< "$1")"
@@ -249,24 +294,32 @@ tap_element() {
echo "error: no on-screen element to tap in: $1"
exit 1
fi
"$ui" tap-xy $((x + w / 2)) $((y + h / 2))
"$ui" "${2:-tap-xy}" $((x + w / 2)) $((y + h / 2))
}
# New chat lists only contacts, so there the joined channel is a contact-style chat row. The chat list behind the sheet
# may be in the tree too, with its own search field (at the bottom with the one-hand UI) and ci_cards row, so the sheet's
# field is the topmost one and its row is the ci_cards element the search adds.
open_known_chat_from_new_chat() {
# New chat lists only contacts, so there the seeded results are rows with Open and the joined channel is a contact-style
# chat row. The chat list behind the sheet may be in the tree too, with its own search field (at the bottom with the
# one-hand UI) and ci_cards row, so the sheet's field is the topmost one and its row is the ci_cards element the search adds.
search_in_new_chat() {
local before row deadline
before=$(labelled ci_cards)
"$ui" tap Compose
"$ui" wait "Create 1-time link" 10
tap_element "$(topmost "$("$ui" tree | grep -F 'TextField null value="Search or paste SimpleX link"')")"
"$ui" type "$search_text"
sleep 2
search_directory '"#ci_group"'
"$ui" shot new-chat-results
if [ -z "$(open_in_row "#ci_group")" ]; then
echo "error: in New chat #ci_group is not a result with Open on its row"
exit 1
fi
tap_element "$(topmost "$("$ui" tree | grep -F 'TextField null value=')")"
"$ui" tap Cancel
tap_element "$(topmost "$("$ui" tree | grep -F 'TextField null value="Search or paste SimpleX link"')")"
"$ui" type "ci cards"
sleep 2
"$ui" tap "Search in Directory"
if "$ui" wait "Search in Directory?" 10; then
"$ui" tap "Search"
fi
tap_search_in_directory
deadline=$((SECONDS + 60))
until row=$(added_labelled ci_cards "$before") && [ -n "$row" ]; do
if [ "$SECONDS" -ge "$deadline" ]; then
@@ -282,7 +335,7 @@ open_known_chat_from_new_chat() {
"$ui" wait "you are subscriber" 30
"$ui" shot known-chat-opened-new-chat
"$ui" tap Back
echo "in New chat the directory result for the joined ci_cards is its chat row ($row), which closes the sheet and opens the channel"
echo "in New chat #ci_group is a result with Open, and the directory result for the joined ci_cards is its chat row ($row), which closes the sheet and opens the channel"
}
xcrun simctl install "$SIM_UDID" "$app"
@@ -335,56 +388,104 @@ sleep 2
expect_in_tree -iF "\"$search_text\""
expect_in_tree -F '"Search in Directory"'
# the directory finds the seeded group and channel, after the first-search consent
# Directory search is off until enabled: the first search asks, and Cancel sends nothing
"$ui" tap "Search in Directory"
"$ui" wait "Search in Directory?" 10
"$ui" wait "Enable Directory search?" 10
"$ui" shot search-consent
"$ui" tap "Search"
if ! "$ui" wait "Join channel" 60; then
"$ui" tap Cancel
sleep 2
"$ui" shot search-cancelled
tree=$("$ui" tree)
if grep -qF '"Open" value=' <<< "$tree" || ! grep -qF '"Search in Directory"' <<< "$tree"; then
echo "error: Cancel in \"Enable Directory search?\" searched, or \"Search in Directory\" is gone"
exit 1
fi
echo "Cancel in \"Enable Directory search?\" searched nothing, and \"Search in Directory\" is still shown"
# Enable turns Directory search on and searches: the directory finds the seeded group and channel
"$ui" tap "Search in Directory"
"$ui" wait "Enable Directory search?" 10
"$ui" tap Enable
if ! "$ui" wait '"Open"' 60; then
"$ui" shot search-failed
"$ui" tap "Search failed, tap to retry"
"$ui" wait "Join channel" 60
tap_element "$(labelled_containing "Search failed, tap to retry")"
"$ui" wait '"Open"' 60
fi
"$ui" shot directory-results
# the header alone is matched exactly: "Search in Directory" stays shown above the field
expect_in_tree -F '"Directory"'
expect_in_tree -F '#ci_group'
expect_in_tree -F 'Join group'
expect_in_tree -F '#ci_channel'
expect_in_tree -F 'Join channel'
expect_header_above_results reachable
expect_avatars reachable
expect_seed_results reachable
# the Search key searches the directory too, and never resolves names: "Connect to …" stays for the name typed
clear_search
type_search cinews
"$ui" key return
expect_no_consent "The Search key"
"$ui" wait '"#cinews"' 60
"$ui" shot search-key-results
# the keyboard capitalizes the first letter
expect_in_tree -iF '"Connect to cinews.testing"'
tree=$("$ui" tree)
if grep -qF '"Names"' <<< "$tree"; then
echo "error: the Search key shows a Names part"
exit 1
fi
echo "the Search key shows the directory's results for cinews, with \"Connect to cinews.testing\" and no Names part"
clear_search
"$ui" layout top
type_search "$search_text"
search_directory "Join channel"
search_directory '"Open"'
"$ui" shot directory-results-top
expect_header_above_results top
expect_avatars top
expect_seed_results top
"$ui" layout reachable
# with a conversation the chat list replaces the onboarding pages, and has cards around the chats
type_search "ci_cards"
search_directory "#ci_cards"
"$ui" tap "#ci_cards"
"$ui" tap "Open channel" 30
"$ui" wait "Tap Join channel" 30
# Open prepares the channel and opens it without asking, so no tree read until it opens may show the "Open channel" alert;
# with a conversation the chat list then replaces the onboarding pages, and has cards around the chats
type_search "ci cards"
search_directory '"#ci_cards"'
"$ui" shot ci-cards-result
open=$(open_in_row "#ci_cards")
if [ -z "$open" ]; then
echo "error: #ci_cards is not a result with Open on its row"
exit 1
fi
tap_element "$open"
deadline=$((SECONDS + 60))
until tree=$("$ui" tree) && grep -qF "Tap Join channel" <<< "$tree"; do
if grep -qF 'Button "Open channel"' <<< "$tree"; then
"$ui" shot open-channel-alert
echo "error: Open on #ci_cards showed the \"Open channel\" alert"
exit 1
fi
if [ "$SECONDS" -ge "$deadline" ]; then
"$ui" shot prepared-channel-missing
echo "error: Open on #ci_cards did not open the channel with \"Tap Join channel\" after 60 s"
exit 1
fi
sleep 1
done
"$ui" shot prepared-channel
# the toolbar and the banner
subscribers=$(grep -cE '[0-9]+ subscribers?' <<< "$tree" || true)
if [ "$subscribers" -lt 2 ]; then
echo "error: the prepared channel shows its subscriber count $subscribers times, not in both the toolbar and the banner"
exit 1
fi
echo "Open on #ci_cards opened the prepared channel without an alert, with its subscriber count in the toolbar and the banner"
"$ui" tap "Join channel"
"$ui" wait "you are subscriber" 60
"$ui" shot joined-channel
"$ui" tap Back
# the search text matches the joined channel, so the list shows it as a chat, and its Directory row must be gone
# "ci cards" doesn't match the chat's name, so the list doesn't show it: the results show it as its chat row
"$ui" shot joined-channel-search
expect_in_tree -F 'StaticText "ci_cards"'
tree=$("$ui" tree)
if grep -qF '"#ci_cards"' <<< "$tree"; then
echo "error: the Directory row #ci_cards is still shown next to the chat opened from it"
exit 1
fi
echo "after joining from its Directory row, ci_cards is a chat in the search results and its Directory row is gone"
# the page's only result is that channel, which the list shows, so the Directory part says the page added none
expect_in_tree -F '"All results on this page are already in your chats"'
echo "the Directory part says all results on the page are already in your chats"
expect_no_join_row "the chat list after joining"
echo "after joining through Open, ci_cards is its chat row among the results and no longer a result to open"
clear_search
# the page's only result is that channel, which the list shows, so the results say the page added none
type_search "ci_cards"
search_directory '"All results on this page are already in your chats"'
"$ui" shot page-in-chats
echo "the results say all results on the page are already in your chats"
clear_search
expect_cards_hidden_by_results reachable
open_known_chat_from_results reachable
@@ -393,15 +494,51 @@ dismiss_lock_notice
expect_cards_hidden_by_results top
open_known_chat_from_results top
"$ui" layout reachable
open_known_chat_from_new_chat
search_in_new_chat
# with the directory stopped, the next page waits 10 s before it fails: long enough to see "Show more" while it loads
# with the directory stopped, the next page waits 10 s before it fails: long enough to see the load row while it loads
type_search "ci avatar"
search_directory "Show more"
search_directory "Load more"
"$ui" shot load-more
expect_in_tree -E 'Load more · [0-9]+ left'
loaded=$("$ui" tree | grep -cF '"#ci_avatar_' || true)
"$ui" directory stop
"$ui" tap "Show more"
"$ui" shot show-more-loading
expect_in_tree -E '^Button "Show more" .* disabled$'
tap_element "$(labelled_containing "Load more")"
# read before the screenshot, which takes long enough for the 10 s request to fail
expect_in_tree -E '^Button ".*Load more.*" .* disabled$'
"$ui" shot load-more-loading
"$ui" wait "Search failed, tap to retry" 30
"$ui" shot load-more-failed
"$ui" directory start
# the directory takes a while to accept requests again, so the retry is repeated until the next page comes
deadline=$((SECONDS + 120))
until [ "$("$ui" tree | grep -cF '"#ci_avatar_' || true)" -gt "$loaded" ]; do
if [ "$SECONDS" -ge "$deadline" ]; then
"$ui" shot load-more-retry-failed
echo "error: retrying the failed page did not load more results after 120 s"
exit 1
fi
retry=$(labelled_containing "Search failed, tap to retry")
if [ -n "$retry" ]; then
tap_element "$retry"
fi
sleep 5
done
"$ui" shot load-more-retried
echo "the load row says how many results are left, is disabled while its page loads, offers a retry when it fails, and the retry loads the page"
clear_search
echo "smoke test passed: onboarding reaches the chat list in both layouts; the directory search for \"$search_text\" shows #ci_group (Join group) and #ci_channel (Join channel) with their avatars and the Directory header above them; with a chat, the list's cards show while typing and hide under the results; a page of chats already listed says so; the joined channel found by its description is its chat row on the chat list in both layouts and in New chat, and opens it; \"Show more\" is disabled while its page loads"
# not fatal: the long press is new to this harness, and the screenshot shows the menu's preview over the flipped list
type_search "$search_text"
search_directory '"#ci_group"'
opens=$(labelled Open)
tap_element "$(labelled_containing '"#ci_group" value=')" long-press
"$ui" shot long-press-menu
if [ -n "$(added_labelled Open "$opens")" ]; then
echo "a long press on #ci_group shows a menu with Open"
"$ui" tap-xy 200 100
else
echo "WARN: a long press on #ci_group shows no menu with Open; see the long-press-menu screenshot"
fi
clear_search || true
echo "smoke test passed: onboarding reaches the chat list in both layouts; the first directory search asks to enable Directory search, Cancel sends nothing and Enable searches, and no later search asks; the search for \"$search_text\" shows #ci_group and #ci_channel with Open and their avatars, with no header; the Search key searches the directory and keeps \"Connect to …\"; Open opens a prepared channel without an alert, with its subscriber count; with a chat, the list's cards show while typing and hide under the results; a page of chats already listed says so; the joined channel found by its description is its chat row on the chat list in both layouts and in New chat, and opens it; the load row says how many results are left, is disabled while its page loads and retries a failed page"