Files
andros aa8b5563e6 Surface interaction errors, persist compose drafts, pull-to-refresh profile
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.
2026-04-24 11:48:48 +02:00

51 lines
2.0 KiB
Swift
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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")
}
}