Files
Termind/TFITests/MobileSingleLineFieldEditingTests.swift

79 lines
3.5 KiB
Swift

// SPDX-FileCopyrightText: 2026 Kaede Akino
// SPDX-License-Identifier: GPL-3.0-only
import SwiftUI
import TermindDomain
import TermindPresentation
import Testing
import UIKit
@testable import TermindIOS
@Suite("Mobile single-line field editing", .requiresKeyWindow, .serialized)
@MainActor
struct MobileSingleLineFieldEditingTests {
@Test(arguments: ["en", "zh-Hans", "zh-Hant"])
func hostFormNormalizesPasteAndPreservesFocus(language: String) async throws {
let draft = MobileSingleLineHostTestDraft()
let controller = UIHostingController(
rootView: MobileSingleLineHostTestForm(draft: draft)
.environment(\.locale, Locale(identifier: language))
.environment(\.singleLineInputAdapter, { AnyView(MobileSingleLineFieldInput()) }))
let window = UIWindow(frame: CGRect(x: 0, y: 0, width: 520, height: 600))
window.rootViewController = controller
window.makeKeyAndVisible()
defer { window.isHidden = true }
controller.view.layoutIfNeeded()
try await Task.sleep(for: .milliseconds(100))
let field = try #require(findField(in: controller.view, value: "fixture.invalid"))
let inputDelegate = field.delegate
let pasteDelegate = try #require(field.pasteDelegate as? MobileSingleLineFieldInput.DelegateProxy)
#expect(field.becomeFirstResponder())
let selection = try #require(field.textRange(from: field.beginningOfDocument, to: field.endOfDocument))
field.selectedTextRange = selection
// Invoke the native paste transformation synchronously: other UI suites can
// change the key window while UIKit asynchronously loads item providers.
let pasted = pasteDelegate.textPasteConfigurationSupporting(
field, combineItemAttributedStrings: [NSAttributedString(string: "first.example\r\n\nsecond.example")],
for: selection)
field.insertText(pasted.string)
#expect(draft.host.host == "first.example second.example")
#expect(field.text == "first.example second.example")
#expect(field.isFirstResponder)
#expect(field.delegate === inputDelegate)
field.selectedTextRange = field.textRange(from: field.beginningOfDocument, to: field.endOfDocument)
let chineseSelection = try #require(field.selectedTextRange)
let chinesePaste = pasteDelegate.textPasteConfigurationSupporting(
field, combineItemAttributedStrings: [NSAttributedString(string: "中文\u{2028}名称")], for: chineseSelection)
field.insertText(chinesePaste.string)
#expect(draft.host.host == "中文 名称")
#expect(field.isFirstResponder)
}
private func findField(in view: UIView, value: String) -> UITextField? {
if let field = view as? UITextField, field.text == value { return field }
return view.subviews.lazy.compactMap { findField(in: $0, value: value) }.first
}
}
@Observable
@MainActor
private final class MobileSingleLineHostTestDraft {
var host = TermindDomain.Host(name: "Fixture", host: "fixture.invalid")
}
private struct MobileSingleLineHostTestForm: View {
@Bindable var draft: MobileSingleLineHostTestDraft
var body: some View {
Form {
HostOrganizationSection(host: $draft.host, detectedDistribution: draft.host.detectedDistribution) {
EmptyView()
} tagsField: {
EmptyView()
}
HostConnectionFields(host: $draft.host)
}
}
}