damus

nostr ios client
git clone git://jb55.com/damus
Log | Files | Refs | README | LICENSE

PostViewTests.swift (14246B)


      1 //
      2 //  PostViewTests.swift
      3 //  damusTests
      4 //
      5 //  Created by Daniel D’Aquino on 2023-08-19.
      6 //
      7 import Foundation
      8 import XCTest
      9 import SnapshotTesting
     10 import SwiftUI
     11 @testable import damus
     12 import SwiftUI
     13 
     14 final class PostViewTests: XCTestCase {
     15     
     16     override func setUpWithError() throws {
     17         // Put setup code here. This method is called before the invocation of each test method in the class.
     18     }
     19     
     20     override func tearDownWithError() throws {
     21         // Put teardown code here. This method is called after the invocation of each test method in the class.
     22     }
     23     
     24     /*
     25     func testTextWrapperViewWillWrapText() {
     26         // Setup test variables to be passed into the TextViewWrapper
     27         let tagModel: TagModel = TagModel()
     28         var textHeight: CGFloat? = nil
     29         let textHeightBinding: Binding<CGFloat?> = Binding(get: {
     30             return textHeight
     31         }, set: { newValue in
     32             textHeight = newValue
     33         })
     34         
     35         // Setup the test view
     36         let textEditorView = TextViewWrapper(
     37             attributedText: .constant(NSMutableAttributedString(string: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.")),
     38             textHeight: textHeightBinding,
     39             initialTextSuffix: nil,
     40             cursorIndex: 9,
     41             updateCursorPosition: { _ in return }
     42         ).environmentObject(tagModel)
     43         let hostView = UIHostingController(rootView: textEditorView)
     44         
     45         // Run snapshot check
     46         assertSnapshot(matching: hostView, as: .image(on: .iPhoneSe(.portrait)))
     47     }
     48      */
     49 
     50     /// Based on https://github.com/damus-io/damus/issues/1375
     51     /// Tests whether the editor properly handles mention links after they have been added, to avoid manual editing of attributed links
     52     func testMentionLinkEditorHandling() throws {
     53         var content: NSMutableAttributedString
     54 
     55         // Test normal insertion
     56         checkMentionLinkEditorHandling(content: NSMutableAttributedString(string: "Hello"), replacementText: "@", replacementRange: NSRange(location: 0, length: 0), shouldBeAbleToChangeAutomatically: true)
     57         checkMentionLinkEditorHandling(content: NSMutableAttributedString(string: "Hello "), replacementText: "@", replacementRange: NSRange(location: 6, length: 0), shouldBeAbleToChangeAutomatically: true)
     58         checkMentionLinkEditorHandling(content: NSMutableAttributedString(string: "Helo "), replacementText: "l", replacementRange: NSRange(location: 3, length: 0), shouldBeAbleToChangeAutomatically: true)
     59         
     60         // Test normal backspacing
     61         checkMentionLinkEditorHandling(content: NSMutableAttributedString(string: "Hello"), replacementText: "", replacementRange: NSRange(location: 5, length: 1), shouldBeAbleToChangeAutomatically: true)
     62         checkMentionLinkEditorHandling(content: NSMutableAttributedString(string: "Hello "), replacementText: "", replacementRange: NSRange(location: 6, length: 1), shouldBeAbleToChangeAutomatically: true)
     63         checkMentionLinkEditorHandling(content: NSMutableAttributedString(string: "Helo "), replacementText: "", replacementRange: NSRange(location: 3, length: 1), shouldBeAbleToChangeAutomatically: true)
     64 
     65         // Test normal insertion after mention link
     66         content = NSMutableAttributedString(string: "Hello @user ")
     67         content.addAttribute(.link, value: "damus:1234", range: NSRange(location: 6, length: 5))
     68         checkMentionLinkEditorHandling(content: content, replacementText: "a", replacementRange: NSRange(location: 12, length: 0), shouldBeAbleToChangeAutomatically: true)
     69 
     70         // Test insertion right at the end of a mention link, at the end of the text
     71         content = NSMutableAttributedString(string: "Hello @user")
     72         content.addAttribute(.link, value: "damus:1234", range: NSRange(location: 6, length: 5))
     73         checkMentionLinkEditorHandling(content: content, replacementText: ",", replacementRange: NSRange(location: 11, length: 0), shouldBeAbleToChangeAutomatically: false, expectedNewCursorIndex: 12, handleNewContent: { newManuallyEditedContent in
     74             XCTAssertEqual(newManuallyEditedContent.string, "Hello @user,")
     75             XCTAssertNil(newManuallyEditedContent.attribute(.link, at: 11, effectiveRange: nil))
     76         })
     77 
     78         // Test insertion right at the end of a mention link, in the middle of the text
     79         content = NSMutableAttributedString(string: "Hello @user how are you?")
     80         content.addAttribute(.link, value: "damus:1234", range: NSRange(location: 6, length: 5))
     81         checkMentionLinkEditorHandling(content: content, replacementText: ",", replacementRange: NSRange(location: 11, length: 0), shouldBeAbleToChangeAutomatically: false, expectedNewCursorIndex: 12, handleNewContent: { newManuallyEditedContent in
     82             XCTAssertEqual(newManuallyEditedContent.string, "Hello @user, how are you?")
     83             XCTAssertNil(newManuallyEditedContent.attribute(.link, at: 11, effectiveRange: nil))
     84         })
     85 
     86         // Test insertion in the middle of a mention link to check if the link is removed
     87         content = NSMutableAttributedString(string: "Hello @user how are you?")
     88         content.addAttribute(.link, value: "damus:1234", range: NSRange(location: 6, length: 5))
     89         checkMentionLinkEditorHandling(content: content, replacementText: "a", replacementRange: NSRange(location: 8, length: 0), shouldBeAbleToChangeAutomatically: false, expectedNewCursorIndex: 9, handleNewContent: { newManuallyEditedContent in
     90             XCTAssertEqual(newManuallyEditedContent.string, "Hello @uaser how are you?")
     91             XCTAssertNil(newManuallyEditedContent.attribute(.link, at: 8, effectiveRange: nil))
     92         })
     93 
     94         // Test insertion in the middle of a mention link to check if the link is removed, at the end of the text
     95         content = NSMutableAttributedString(string: "Hello @user")
     96         content.addAttribute(.link, value: "damus:1234", range: NSRange(location: 6, length: 5))
     97         checkMentionLinkEditorHandling(content: content, replacementText: "a", replacementRange: NSRange(location: 8, length: 0), shouldBeAbleToChangeAutomatically: false, expectedNewCursorIndex: 9, handleNewContent: { newManuallyEditedContent in
     98             XCTAssertEqual(newManuallyEditedContent.string, "Hello @uaser")
     99             XCTAssertNil(newManuallyEditedContent.attribute(.link, at: 8, effectiveRange: nil))
    100         })
    101 
    102         // Test backspacing right at the end of a mention link, at the end of the text
    103         content = NSMutableAttributedString(string: "Hello @user")
    104         content.addAttribute(.link, value: "damus:1234", range: NSRange(location: 6, length: 5))
    105         checkMentionLinkEditorHandling(content: content, replacementText: "", replacementRange: NSRange(location: 10, length: 1), shouldBeAbleToChangeAutomatically: false, expectedNewCursorIndex: 10, handleNewContent: { newManuallyEditedContent in
    106             XCTAssertEqual(newManuallyEditedContent.string, "Hello @use")
    107             XCTAssertNil(newManuallyEditedContent.attribute(.link, at: 6, effectiveRange: nil))
    108         })
    109 
    110         // Test adding text right at the start of a mention link, to check that the link is removed
    111         content = NSMutableAttributedString(string: "Hello @user")
    112         content.addAttribute(.link, value: "damus:1234", range: NSRange(location: 6, length: 5))
    113         checkMentionLinkEditorHandling(content: content, replacementText: "a", replacementRange: NSRange(location: 6, length: 0), shouldBeAbleToChangeAutomatically: false, expectedNewCursorIndex: 7, handleNewContent: { newManuallyEditedContent in
    114             XCTAssertEqual(newManuallyEditedContent.string, "Hello a@user")
    115             XCTAssertNil(newManuallyEditedContent.attribute(.link, at: 7, effectiveRange: nil))
    116         })
    117 
    118         // Test that removing one link does not affect the other
    119         content = NSMutableAttributedString(string: "Hello @user1 @user2")
    120         content.addAttribute(.link, value: "damus:1234", range: NSRange(location: 6, length: 6))
    121         content.addAttribute(.link, value: "damus:5678", range: NSRange(location: 13, length: 6))
    122         checkMentionLinkEditorHandling(content: content, replacementText: "", replacementRange: NSRange(location: 18, length: 1), shouldBeAbleToChangeAutomatically: false, expectedNewCursorIndex: 18, handleNewContent: { newManuallyEditedContent in
    123             XCTAssertEqual(newManuallyEditedContent.string, "Hello @user1 @user")
    124             XCTAssertNil(newManuallyEditedContent.attribute(.link, at: 13, effectiveRange: nil))
    125             XCTAssertNotNil(newManuallyEditedContent.attribute(.link, at: 6, effectiveRange: nil))
    126         })
    127 
    128         // Test that replacing a whole range intersecting with two links removes both links
    129         content = NSMutableAttributedString(string: "Hello @user1 @user2")
    130         content.addAttribute(.link, value: "damus:1234", range: NSRange(location: 6, length: 6))
    131         content.addAttribute(.link, value: "damus:5678", range: NSRange(location: 13, length: 6))
    132         checkMentionLinkEditorHandling(content: content, replacementText: "a", replacementRange: NSRange(location: 10, length: 4), shouldBeAbleToChangeAutomatically: false, expectedNewCursorIndex: 11, handleNewContent: { newManuallyEditedContent in
    133             XCTAssertEqual(newManuallyEditedContent.string, "Hello @useauser2")
    134             XCTAssertNil(newManuallyEditedContent.attribute(.link, at: 6, effectiveRange: nil))
    135             XCTAssertNil(newManuallyEditedContent.attribute(.link, at: 11, effectiveRange: nil))
    136         })
    137 
    138         // Test that replacing a whole range including two links removes both links naturally
    139         content = NSMutableAttributedString(string: "Hello @user1 @user2, how are you?")
    140         content.addAttribute(.link, value: "damus:1234", range: NSRange(location: 6, length: 6))
    141         content.addAttribute(.link, value: "damus:5678", range: NSRange(location: 13, length: 6))
    142         checkMentionLinkEditorHandling(content: content, replacementText: "", replacementRange: NSRange(location: 5, length: 28), shouldBeAbleToChangeAutomatically: true)
    143         
    144     }
    145     
    146     func testMentionLinkEditorHandling_noWhitespaceAfterLink1_addsWhitespace() {
    147         var content: NSMutableAttributedString
    148 
    149         content = NSMutableAttributedString(string: "Hello @user ")
    150         content.addAttribute(.link, value: "damus:1234", range: NSRange(location: 6, length: 5))
    151         checkMentionLinkEditorHandling(content: content, replacementText: "up", replacementRange: NSRange(location: 11, length: 1), shouldBeAbleToChangeAutomatically: true, expectedNewCursorIndex: 13, handleNewContent: { newManuallyEditedContent in
    152             XCTAssertEqual(newManuallyEditedContent.string, "Hello @user up")
    153             XCTAssertNil(newManuallyEditedContent.attribute(.link, at: 11, effectiveRange: nil))
    154         })
    155     }
    156     
    157     func testMentionLinkEditorHandling_noWhitespaceAfterLink2_addsWhitespace() {
    158         var content: NSMutableAttributedString
    159 
    160         content = NSMutableAttributedString(string: "Hello @user test")
    161         content.addAttribute(.link, value: "damus:1234", range: NSRange(location: 6, length: 5))
    162         checkMentionLinkEditorHandling(content: content, replacementText: "up", replacementRange: NSRange(location: 11, length: 1), shouldBeAbleToChangeAutomatically: true, expectedNewCursorIndex: 13, handleNewContent: { newManuallyEditedContent in
    163             XCTAssertEqual(newManuallyEditedContent.string, "Hello @user uptest")
    164             XCTAssertNil(newManuallyEditedContent.attribute(.link, at: 11, effectiveRange: nil))
    165         })
    166     }
    167     
    168     func testMentionLinkEditorHandling_nonAlphanumericAfterLink_noWhitespaceAdded() {
    169         let nonAlphaNumerics = [" ", "!", "\"", "#", "$", "%", "&", "'", "(", ")", "*", "+", ",", "-", ".", "/", ":", ";", "<", "=", ">", "?", "@", "[", "\\", "]", "^", "_", "`", "{", "|", "}", "~"]
    170         
    171         nonAlphaNumerics.forEach { testAddingStringAfterLink(str: $0)}
    172     }
    173 }
    174 
    175 func checkMentionLinkEditorHandling(
    176     content: NSMutableAttributedString,
    177     replacementText: String,
    178     replacementRange: NSRange,
    179     shouldBeAbleToChangeAutomatically: Bool,
    180     expectedNewCursorIndex: Int? = nil,
    181     handleNewContent: ((NSMutableAttributedString) -> Void)? = nil) {
    182         let bindingContent: Binding<NSMutableAttributedString> = Binding(get: {
    183             return content
    184         }, set: { newValue in
    185             handleNewContent?(newValue)
    186         })
    187         let coordinator: TextViewWrapper.Coordinator = TextViewWrapper.Coordinator(attributedText: bindingContent, getFocusWordForMention: nil, updateCursorPosition: { newCursorIndex in
    188             if let expectedNewCursorIndex {
    189                 XCTAssertEqual(newCursorIndex, expectedNewCursorIndex)
    190             }
    191         }, initialTextSuffix: nil)
    192         let textView = UITextView()
    193         textView.attributedText = content
    194 
    195         XCTAssertEqual(coordinator.textView(textView, shouldChangeTextIn: replacementRange, replacementText: replacementText), shouldBeAbleToChangeAutomatically, "Expected shouldChangeTextIn to return \(shouldBeAbleToChangeAutomatically), but was \(!shouldBeAbleToChangeAutomatically)")
    196 }
    197 
    198 func testAddingStringAfterLink(str: String) {
    199     var content: NSMutableAttributedString
    200 
    201     content = NSMutableAttributedString(string: "Hello @user test")
    202     content.addAttribute(.link, value: "damus:1234", range: NSRange(location: 6, length: 5))
    203     checkMentionLinkEditorHandling(content: content, replacementText: str, replacementRange: NSRange(location: 11, length: 0), shouldBeAbleToChangeAutomatically: false, expectedNewCursorIndex: 12, handleNewContent: { newManuallyEditedContent in
    204         XCTAssertEqual(newManuallyEditedContent.string, "Hello @user" + str + " test")
    205         XCTAssertNil(newManuallyEditedContent.attribute(.link, at: 11, effectiveRange: nil))
    206     })
    207 }
    208 
    209