mirror of
https://github.com/wu736139669/hapi.git
synced 2026-10-09 19:29:41 +00:00
192 lines
7.1 KiB
Swift
192 lines
7.1 KiB
Swift
import Foundation
|
|
import HapiClient
|
|
import HapiProtocol
|
|
import Testing
|
|
|
|
/// Transcription of the Android `FileViewerViewModelTest` suite against the
|
|
/// real `FileViewerModel` (only the gateway is faked). One adaptation: on
|
|
/// iOS the unified-diff *parser* lives in HapiUI, so `DiffState.ready`
|
|
/// carries the raw stdout judged through the injected `hasRenderableDiff`
|
|
/// probe (parse-level assertions are covered by `HapiUITests/DiffModelTests`);
|
|
/// these tests use the default non-empty probe.
|
|
@Suite("FileViewerModel")
|
|
@MainActor
|
|
struct FileViewerModelTests {
|
|
|
|
private func makeModel(
|
|
_ gateway: FakeFilesGateway,
|
|
path: String = "src/app.ts",
|
|
staged: Bool? = nil,
|
|
mode: FileViewerModel.Mode? = nil,
|
|
line: Int? = nil
|
|
) -> FileViewerModel {
|
|
FileViewerModel(
|
|
sessionId: "s1",
|
|
path: path,
|
|
initialStaged: staged,
|
|
initialMode: mode,
|
|
focusLine: line,
|
|
requester: gateway
|
|
)
|
|
}
|
|
|
|
private func b64(_ text: String) -> String {
|
|
Data(text.utf8).base64EncodedString()
|
|
}
|
|
|
|
private let sampleDiff = """
|
|
diff --git a/src/app.ts b/src/app.ts
|
|
--- a/src/app.ts
|
|
+++ b/src/app.ts
|
|
@@ -1,2 +1,2 @@
|
|
keep
|
|
-old
|
|
+new
|
|
"""
|
|
|
|
private func settled(_ model: FileViewerModel) async -> Bool {
|
|
await filesEventually { model.diff != .loading && model.content != .loading }
|
|
}
|
|
|
|
@Test func parsesTheDiffAndStaysInDiffMode() async {
|
|
let gateway = FakeFilesGateway()
|
|
await gateway.setDiffFile(GitCommandResponse(success: true, stdout: sampleDiff))
|
|
await gateway.setReadFile(FileReadResponse(success: true, content: b64("keep\nnew\n")))
|
|
let model = makeModel(gateway)
|
|
model.start()
|
|
|
|
#expect(await settled(model))
|
|
#expect(model.mode == .diff)
|
|
#expect(model.diff == .ready(unifiedDiff: sampleDiff))
|
|
#expect(await gateway.diffFileCalls == [DiffFileCall(path: "src/app.ts", staged: false)])
|
|
}
|
|
|
|
@Test func emptyDiffAutoFallsBackToFileMode() async {
|
|
let gateway = FakeFilesGateway()
|
|
await gateway.setDiffFile(GitCommandResponse(success: true, stdout: ""))
|
|
await gateway.setReadFile(FileReadResponse(success: true, content: b64("hello")))
|
|
let model = makeModel(gateway)
|
|
model.start()
|
|
|
|
#expect(await settled(model))
|
|
#expect(model.mode == .file)
|
|
#expect(model.diff == .empty)
|
|
#expect(model.content == .text("hello", language: "ts", isMarkdown: false))
|
|
}
|
|
|
|
@Test func failedDiffAutoFallsBackToFileModeWithMessageKept() async {
|
|
let gateway = FakeFilesGateway()
|
|
await gateway.setDiffFile(GitCommandResponse(success: false, error: "not a repo"))
|
|
await gateway.setReadFile(FileReadResponse(success: true, content: b64("x")))
|
|
let model = makeModel(gateway)
|
|
model.start()
|
|
|
|
#expect(await settled(model))
|
|
#expect(model.mode == .file)
|
|
#expect(model.diff == .failed("not a repo"))
|
|
}
|
|
|
|
@Test func explicitFileModeFromTheRouteDisablesAutoBehavior() async {
|
|
let gateway = FakeFilesGateway()
|
|
await gateway.setDiffFile(GitCommandResponse(success: true, stdout: sampleDiff))
|
|
await gateway.setReadFile(FileReadResponse(success: true, content: b64("body")))
|
|
let model = makeModel(gateway, mode: .file, line: 12)
|
|
model.start()
|
|
|
|
#expect(await settled(model))
|
|
#expect(model.mode == .file)
|
|
#expect(model.focusLine == 12)
|
|
// The diff still loaded, so the user can flip to it.
|
|
#expect(model.diff == .ready(unifiedDiff: sampleDiff))
|
|
}
|
|
|
|
@Test func stagedToggleReloadsTheDiffForTheOtherSide() async {
|
|
let gateway = FakeFilesGateway()
|
|
await gateway.setDiffFile(GitCommandResponse(success: true, stdout: sampleDiff))
|
|
await gateway.setReadFile(FileReadResponse(success: true, content: b64("x")))
|
|
let model = makeModel(gateway, staged: false)
|
|
model.start()
|
|
#expect(await settled(model))
|
|
|
|
model.setStaged(true)
|
|
#expect(await filesEventually { await gateway.diffFileCalls.count == 2 })
|
|
|
|
#expect(await gateway.diffFileCalls == [
|
|
DiffFileCall(path: "src/app.ts", staged: false),
|
|
DiffFileCall(path: "src/app.ts", staged: true),
|
|
])
|
|
#expect(model.staged)
|
|
|
|
// Same side again is a no-op.
|
|
model.setStaged(true)
|
|
try? await Task.sleep(for: .milliseconds(50))
|
|
#expect(await gateway.diffFileCalls.count == 2)
|
|
}
|
|
|
|
@Test func markdownFilesFlagMarkdownAndDefaultToPreview() async {
|
|
let gateway = FakeFilesGateway()
|
|
await gateway.setDiffFile(GitCommandResponse(success: true, stdout: ""))
|
|
await gateway.setReadFile(FileReadResponse(success: true, content: b64("# Title"), size: 7, modified: 123))
|
|
let model = makeModel(gateway, path: "README.md")
|
|
model.start()
|
|
|
|
#expect(await settled(model))
|
|
#expect(model.content == .text("# Title", language: "md", isMarkdown: true))
|
|
#expect(model.markdownPreview)
|
|
#expect(model.sizeBytes == 7)
|
|
#expect(model.modifiedAt == 123)
|
|
|
|
model.setMarkdownPreview(false)
|
|
#expect(model.markdownPreview == false)
|
|
}
|
|
|
|
@Test func imageExtensionsDecodeToImageContent() async {
|
|
let bytes = Data([1, 2, 3, 4])
|
|
let gateway = FakeFilesGateway()
|
|
await gateway.setDiffFile(GitCommandResponse(success: true, stdout: ""))
|
|
await gateway.setReadFile(FileReadResponse(success: true, content: bytes.base64EncodedString()))
|
|
let model = makeModel(gateway, path: "assets/logo.PNG")
|
|
model.start()
|
|
|
|
#expect(await settled(model))
|
|
#expect(model.content == .image(data: bytes, mimeType: "image/png"))
|
|
#expect(model.mode == .file)
|
|
}
|
|
|
|
@Test func nulBytesClassifyAsBinary() async {
|
|
let gateway = FakeFilesGateway()
|
|
await gateway.setDiffFile(GitCommandResponse(success: true, stdout: ""))
|
|
await gateway.setReadFile(FileReadResponse(
|
|
success: true,
|
|
content: Data([104, 105, 0, 106]).base64EncodedString()
|
|
))
|
|
let model = makeModel(gateway, path: "blob.bin")
|
|
model.start()
|
|
|
|
#expect(await settled(model))
|
|
#expect(model.content == .binary)
|
|
}
|
|
|
|
@Test func readFailureSurfacesItsMessage() async {
|
|
let gateway = FakeFilesGateway()
|
|
await gateway.setDiffFile(GitCommandResponse(success: true, stdout: ""))
|
|
await gateway.setReadFile(FileReadResponse(success: false, error: "File not found"))
|
|
let model = makeModel(gateway)
|
|
model.start()
|
|
|
|
#expect(await settled(model))
|
|
#expect(model.content == .failed("File not found"))
|
|
}
|
|
|
|
@Test func invalidBase64ClassifiesAsBinary() async {
|
|
let gateway = FakeFilesGateway()
|
|
await gateway.setDiffFile(GitCommandResponse(success: true, stdout: ""))
|
|
await gateway.setReadFile(FileReadResponse(success: true, content: "%%%not-base64%%%"))
|
|
let model = makeModel(gateway)
|
|
model.start()
|
|
|
|
#expect(await settled(model))
|
|
#expect(model.content == .binary)
|
|
}
|
|
}
|