Files
ichmagmaus111andClaude Opus 5.5 0622057a4f Ghostgram: open-source release
Ghostgram for iOS, rebuilt from scratch on Telegram iOS 13.0.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-31 17:57:09 +01:00

89 lines
4.1 KiB
Swift

import XCTest
import Postbox
import FlatBuffers
import FlatSerialization
import TelegramCore
/// `RichText.textButton` (Postbox discriminator 29). Inline rather than block-level, so the cases
/// that matter most are the ones where a button sits inside other rich text.
final class RichTextButtonTests: XCTestCase {
private let sample = RichText.textButton(InstantPageButton(
text: .plain("Open"),
action: .url("https://t.me"),
color: .primary
))
private func postboxRoundTrip(_ text: RichText) -> RichText {
let encoder = PostboxEncoder()
encoder.encodeObject(text, forKey: "t")
let decoder = PostboxDecoder(buffer: MemoryBuffer(data: encoder.makeData()))
return decoder.decodeObjectForKey("t", decoder: { RichText(decoder: $0) }) as! RichText
}
private func flatBuffersRoundTrip(_ text: RichText) throws -> RichText {
var builder = FlatBufferBuilder(initialSize: 1024)
let offset = text.encodeToFlatBuffers(builder: &builder)
builder.finish(offset: offset)
var byteBuffer = ByteBuffer(data: builder.data)
let object: TelegramCore_RichText = FlatBuffers_getRoot(byteBuffer: &byteBuffer)
return try RichText(flatBuffersObject: object)
}
func test_postboxRoundTrip() {
XCTAssertEqual(self.postboxRoundTrip(self.sample), self.sample)
}
func test_flatBuffersRoundTrip() throws {
XCTAssertEqual(try self.flatBuffersRoundTrip(self.sample), self.sample)
}
/// plainText must surface the label so chat-list previews and accessibility are not blank.
func test_plainTextIsTheLabel() {
XCTAssertEqual(self.sample.plainText, "Open")
}
/// The defining inline case: a button mid-sentence must survive and contribute its label to the
/// surrounding text's plainText in the right position.
func test_nestedInConcatSurvivesAndContributesPlainText() throws {
let nested = RichText.concat([.plain("before "), self.sample, .plain(" after")])
XCTAssertEqual(self.postboxRoundTrip(nested), nested)
XCTAssertEqual(try self.flatBuffersRoundTrip(nested), nested)
XCTAssertEqual(nested.plainText, "before Open after")
}
/// A button label can carry formatting, and a button can sit inside formatting. Both nestings
/// must round-trip, since RichText is mutually recursive with InstantPageButton now.
func test_mutualNesting() throws {
let boldButton = RichText.bold(self.sample)
XCTAssertEqual(self.postboxRoundTrip(boldButton), boldButton)
XCTAssertEqual(try self.flatBuffersRoundTrip(boldButton), boldButton)
let formattedLabel = RichText.textButton(InstantPageButton(
text: .concat([.bold(.plain("B")), .italic(.plain("i"))]),
action: .copyText(payload: "x"),
color: nil
))
XCTAssertEqual(self.postboxRoundTrip(formattedLabel), formattedLabel)
XCTAssertEqual(try self.flatBuffersRoundTrip(formattedLabel), formattedLabel)
}
/// Discriminator 29 must not disturb the 28 that came before it.
func test_neighbouringDiscriminatorsUnaffected() {
let date = RichText.textDate(text: .plain("today"), date: 12345, format: nil)
XCTAssertEqual(self.postboxRoundTrip(date), date)
let spoiler = RichText.textSpoiler(text: .plain("shh"))
XCTAssertEqual(self.postboxRoundTrip(spoiler), spoiler)
}
/// Two buttons differing only in action or colour must not compare equal — otherwise a changed
/// button would not trigger a re-layout.
func test_equalityIsSensitiveToActionAndColor() {
let a = RichText.textButton(InstantPageButton(text: .plain("x"), action: .url("https://a"), color: nil))
let b = RichText.textButton(InstantPageButton(text: .plain("x"), action: .url("https://b"), color: nil))
let c = RichText.textButton(InstantPageButton(text: .plain("x"), action: .url("https://a"), color: .danger))
XCTAssertNotEqual(a, b)
XCTAssertNotEqual(a, c)
XCTAssertEqual(a, RichText.textButton(InstantPageButton(text: .plain("x"), action: .url("https://a"), color: nil)))
}
}