// SPDX-FileCopyrightText: 2026 Kaede Akino // SPDX-License-Identifier: GPL-3.0-only import Foundation import SwiftUI import TermindPresentation import Testing @Suite("Single-line text editing") struct SingleLineTextInputTests { @Test func attributedPasteKeepsSurroundingAttributesAndUnicodeOffsets() { let key = NSAttributedString.Key("testStyle") let original = NSMutableAttributedString(string: "πŸ‘©πŸ½β€πŸ’»δΈ­ζ–‡\r\n\nName", attributes: [key: "first"]) original.addAttribute(key, value: "second", range: NSRange(location: original.length - 4, length: 4)) let normalized = SingleLineTextInput.normalized(original) #expect(normalized.string == "πŸ‘©πŸ½β€πŸ’»δΈ­ζ–‡ Name") #expect(normalized.attribute(key, at: 0, effectiveRange: nil) as? String == "first") #expect(normalized.attribute(key, at: normalized.length - 1, effectiveRange: nil) as? String == "second") #expect(original.string == "πŸ‘©πŸ½β€πŸ’»δΈ­ζ–‡\r\n\nName") } @Test(arguments: ["\n", "\r", "\r\n", "\n\r\n", "\u{000B}", "\u{000C}", "\u{0085}", "\u{2028}", "\u{2029}"]) func consecutiveLineBreaksBecomeOneSpace(_ lineBreak: String) { #expect(SingleLineTextInput.normalized("first\(lineBreak)second") == "first second") #expect(SingleLineTextInput.normalized("\(lineBreak)first\(lineBreak)") == " first ") } @Test(arguments: ["", "example.invalid", "δΈ­ζ–‡δΈ»ζœΊ", " two spaces \t remain ", "πŸ‘©πŸ½β€πŸ’»ζœεŠ‘ε™¨"]) func singleLineContentIsUnchanged(_ text: String) { #expect(SingleLineTextInput.normalized(text) == text) } @Test func separatedLineBreakRunsKeepExistingSpacesAndTabs() { #expect(SingleLineTextInput.normalized("a\r\n\n b\t\nc") == "a b\t c") } @MainActor @Test func bindingReadsLegacyValuesUnchangedAndNormalizesOnlyWrites() { var stored = "legacy\nvalue" var receivedTransaction: Transaction? let source = Binding( get: { stored }, set: { value, transaction in stored = value receivedTransaction = transaction } ) let field = source.singleLineText #expect(field.wrappedValue == "legacy\nvalue") #expect(stored == "legacy\nvalue") var transaction = Transaction() transaction.disablesAnimations = true field.transaction(transaction).wrappedValue = "ζ–°ηš„\r\n\n名称" #expect(stored == "ζ–°ηš„ 名称") #expect(receivedTransaction?.disablesAnimations == true) source.wrappedValue = "script\nbody" #expect(stored == "script\nbody") } }