| index 1ba233f..830d5f6 100644 |
| --- a/ios/LChatCore/Tests/LChatCoreTests/ComposerTypeaheadTests.swift |
| +++ b/ios/LChatCore/Tests/LChatCoreTests/ComposerTypeaheadTests.swift |
| @@ -58,18 +58,28 @@ final class ComposerTypeaheadTests: XCTestCase { |
| } |
| |
| func testBotlessChatStillOffersClientAnsweredCommands() { |
| - // A DM with no AI agent must still offer what the native client answers |
| - // itself (SlashCommands.swift): theming and /me — and nothing that only |
| - // a bot could answer (/help would post into the void). |
| - XCTAssertEqual(commandNames(hasBot: false), ["/theme", "/themes", "/me"]) |
| + // A DM with no AI agent must still offer what the client answers |
| + // itself: the panel commands (/emoji, /gif — the command-host webview, |
| + // #497), /me, and theming (SlashCommands.swift) — and nothing that |
| + // only a bot could answer (/help would post into the void). Ordered |
| + // like the web's commandCatalog: panels, /me, then the bot floor. |
| + XCTAssertEqual(commandNames(hasBot: false), ["/emoji", "/gif", "/me", "/theme", "/themes"]) |
| } |
| |
| func testBotfulChatOffersTheFullFloor() { |
| - let names = commandNames(hasBot: true) |
| + // Nine commands exist with a bot present but the typeahead caps at 8 |
| + // (the web's cap too) — the ordering guarantees the victim is the |
| + // TAIL of the bot list (/profile), never /me or a panel command. |
| + XCTAssertEqual(commandNames(hasBot: true), |
| + ["/emoji", "/gif", "/me", "/help", "/theme", "/themes", "/dark", "/light"]) |
| + // Every bot command is still reachable the moment its prefix is typed — |
| + // the cap hides, it never disables. |
| for expected in botCommands.map(\.name) { |
| - XCTAssertTrue(names.contains(expected), "\(expected) missing with a bot present") |
| + let q = ComposerToken(kind: .command, query: String(expected.dropFirst()), start: 0) |
| + let names = typeaheadItems(q, roster: [], myUserId: "me", hasBot: true) |
| + .compactMap { if case .command(let name, _) = $0 { return name } else { return nil } } |
| + XCTAssertTrue(names.contains(expected), "\(expected) unreachable with a bot present") |
| } |
| - XCTAssertTrue(names.contains("/me")) |
| } |
| |
| // ---- composerToken (ui.js) parity ------------------------------------- |
| @@ -155,7 +165,7 @@ final class ComposerTypeaheadTests: XCTestCase { |
| let bare = ComposerToken(kind: .command, query: "", start: 0) |
| XCTAssertEqual(typeaheadItems(bare, roster: roster, myUserId: "u2", hasBot: false, |
| appCommands: add).map(\.insert), |
| - ["/add ", "/theme ", "/themes ", "/me "], |
| + ["/add ", "/emoji ", "/gif ", "/me ", "/theme ", "/themes "], |
| "no bot → the local verb leads; client-answered floor follows") |
| XCTAssertEqual(typeaheadItems(bare, roster: roster, myUserId: "u2", hasBot: true, |
| appCommands: add).map(\.insert).first, |