mirror of
https://github.com/wu736139669/hapi.git
synced 2026-11-07 01:21:37 +00:00
Typing / in the iOS composer offered nothing; web and Android show the session's commands. While the input is a lone /token, a list above the card shows matching commands. A tap inserts "/name " so the user can add arguments. As on Android, the sources are metadata.slashCommands plus the /slash-commands RPC. The RPC is fetched once, when the input first enters slash mode, and its entries override bare names. A failed fetch retries on the next /. There is no copy of the shared built-in table, so the CLI stays the source of truth. Ranking follows web: exact, prefix, contains, then near-miss typos. The list is hidden in Scratchlist mode. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
40 lines
1.9 KiB
Swift
40 lines
1.9 KiB
Swift
import Foundation
|
|
@testable import HapiClient
|
|
import HapiProtocol
|
|
import Testing
|
|
|
|
/// Composer `/` autocomplete: the Android `SlashCommandsTest` cases plus the
|
|
/// web Levenshtein tail (`useSlashCommands`).
|
|
@Suite("Slash command suggestions")
|
|
struct SlashCommandSuggestionsTests {
|
|
@Test func onlyALoneLeadingTokenOpensTheMenu() {
|
|
#expect(SlashCommandSuggestions.query(of: "/") == "")
|
|
#expect(SlashCommandSuggestions.query(of: "/co") == "co")
|
|
#expect(SlashCommandSuggestions.query(of: "/foo:bar") == "foo:bar")
|
|
#expect(SlashCommandSuggestions.query(of: "/compact now") == nil)
|
|
#expect(SlashCommandSuggestions.query(of: "hi /co") == nil)
|
|
#expect(SlashCommandSuggestions.query(of: "") == nil)
|
|
}
|
|
|
|
@Test func rpcEntriesOverrideMetadataNamesAndTakeTheLaterPosition() {
|
|
let merged = SlashCommandSuggestions.merge(
|
|
metadataNames: ["/deploy", "Compact", " "],
|
|
fetched: [SlashCommand(name: "compact", description: "Summarize", source: "builtin")]
|
|
)
|
|
#expect(merged.map(\.name) == ["deploy", "compact"])
|
|
#expect(merged.last?.description == "Summarize")
|
|
#expect(merged.first?.source == "session")
|
|
}
|
|
|
|
@Test func rankingIsExactPrefixContainsThenCloseTypos() {
|
|
let commands = ["review", "compact", "context", "co", "status", "plan"].map {
|
|
SlashCommand(name: $0, source: "builtin")
|
|
}
|
|
#expect(SlashCommandSuggestions.filter(commands, query: "co").map(\.name) == ["co", "compact", "context"])
|
|
#expect(SlashCommandSuggestions.filter(commands, query: "CONT").map(\.name) == ["context", "co"])
|
|
#expect(SlashCommandSuggestions.filter(commands, query: "stauts").map(\.name) == ["status"])
|
|
#expect(SlashCommandSuggestions.filter(commands, query: "").count == 6)
|
|
#expect(SlashCommandSuggestions.levenshtein("kitten", "sitting") == 3)
|
|
}
|
|
}
|