aa8b5563e6
PostRowView adds three alerts for the fire-and-forget interaction view models (boost / reaction / poll-vote) the same way the actions view model already had one. Their errorMessage was previously written into a property that no UI ever read — so a failed upload left the button in the wrong state with no signal to the user. ComposeViewModel gains draftKey + saveDraftIfTopLevel + clearDraft, and ComposeView wires onChange(of:text) / onDisappear to persist, plus clearDraft on successful publish. Top-level posts only; reply and group contexts are intentionally not drafted because their context URLs might be stale by the time the user reopens the sheet. ProfileView gets .refreshable that bypasses the CDN cache and also forces the follow coordinator to refresh — matches the existing Timeline pull-to-refresh so both tabs feel the same. Two new integration tests on PostWriter.editPost cover the full-fidelity edit path end-to-end: setting LANG/TAGS/MOOD/VISIBILITY + retargeting the timestamp, and the inverse — clearing every property. Three mention-renderer tests pin the public contract around inline `@nick` output and its `.link` attribute so refactors can't quietly break autocomplete round-tripping.
51 lines
2.0 KiB
Swift
51 lines
2.0 KiB
Swift
import Foundation
|
||
import Testing
|
||
@testable import OrgSocialKit
|
||
|
||
/// The App layer keeps mention rewrite logic (encode/decode) close to the
|
||
/// Compose / Edit views so the lib stays unaware of UI concerns. That said,
|
||
/// the renderer's inline `@nick` output is part of the public contract —
|
||
/// it's what callers rely on to display the body. These tests pin the
|
||
/// renderer behaviour used by both directions.
|
||
@Suite("OrgBodyRenderer – mention round-trip")
|
||
struct OrgBodyMentionRoundtripTests {
|
||
|
||
@Test("inline text shows @nick in place of the Org link")
|
||
func inlineShortForm() {
|
||
let body = "Hey [[org-social:https://alice.test/social.org][alice]], how are you?"
|
||
let rendered = OrgBodyRenderer.render(body)
|
||
let inline = String(rendered.inline.characters)
|
||
#expect(inline.contains("Hey @alice, how are you?"))
|
||
#expect(!inline.contains("[[org-social"))
|
||
}
|
||
|
||
@Test("multiple mentions are each rewritten inline and side-carred")
|
||
func multipleMentions() {
|
||
let body = """
|
||
[[org-social:https://a.test/social.org][alice]] and \
|
||
[[org-social:https://b.test/social.org][bob]] chat.
|
||
"""
|
||
let rendered = OrgBodyRenderer.render(body)
|
||
let inline = String(rendered.inline.characters)
|
||
#expect(inline.contains("@alice"))
|
||
#expect(inline.contains("@bob"))
|
||
#expect(rendered.mentions.count == 2)
|
||
}
|
||
|
||
@Test("inline mentions carry a link attribute to the feed URL")
|
||
func inlineLinkAttribute() {
|
||
let body = "Hi [[org-social:https://alice.test/social.org][alice]]"
|
||
let rendered = OrgBodyRenderer.render(body)
|
||
// Walk the runs; find the one over "@alice" and check its link.
|
||
var foundLink: URL?
|
||
for run in rendered.inline.runs {
|
||
let slice = String(rendered.inline[run.range].characters)
|
||
if slice == "@alice" {
|
||
foundLink = run.link
|
||
break
|
||
}
|
||
}
|
||
#expect(foundLink?.absoluteString == "https://alice.test/social.org")
|
||
}
|
||
}
|