Added AttributedStringBuilder tests.

This commit is contained in:
Stefan Ceriu
2022-03-30 14:32:41 +03:00
parent 9169a9def9
commit 9db1dda718
6 changed files with 387 additions and 36 deletions
+8 -6
View File
@@ -27,6 +27,9 @@
18920C1627F2E3F400A717B5 /* NoticeRoomTimelineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18920C1427F2E3F400A717B5 /* NoticeRoomTimelineView.swift */; }; 18920C1627F2E3F400A717B5 /* NoticeRoomTimelineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18920C1427F2E3F400A717B5 /* NoticeRoomTimelineView.swift */; };
18920C1727F2E3F400A717B5 /* EmoteRoomTimelineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18920C1527F2E3F400A717B5 /* EmoteRoomTimelineView.swift */; }; 18920C1727F2E3F400A717B5 /* EmoteRoomTimelineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18920C1527F2E3F400A717B5 /* EmoteRoomTimelineView.swift */; };
18920C1927F3222E00A717B5 /* Colors.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18920C1827F3222E00A717B5 /* Colors.swift */; }; 18920C1927F3222E00A717B5 /* Colors.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18920C1827F3222E00A717B5 /* Colors.swift */; };
189E495227F43BCD00D86BA3 /* ElementXAttributeScope.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18DDB72027EB9D57000F1ABF /* ElementXAttributeScope.swift */; };
189E495327F474A200D86BA3 /* RoomScreenUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18F2BAC127D25B4000DD1988 /* RoomScreenUITests.swift */; };
189E495427F474A500D86BA3 /* RoomScreenViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18F2BAC327D25B4000DD1988 /* RoomScreenViewModelTests.swift */; };
18ADC7D527E4B20300A8C953 /* PlaceholderAvatarImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18ADC7D427E4B20300A8C953 /* PlaceholderAvatarImage.swift */; }; 18ADC7D527E4B20300A8C953 /* PlaceholderAvatarImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18ADC7D427E4B20300A8C953 /* PlaceholderAvatarImage.swift */; };
18ADC7D827E4B63C00A8C953 /* MatrixRustSDK in Frameworks */ = {isa = PBXBuildFile; productRef = 18ADC7D727E4B63C00A8C953 /* MatrixRustSDK */; }; 18ADC7D827E4B63C00A8C953 /* MatrixRustSDK in Frameworks */ = {isa = PBXBuildFile; productRef = 18ADC7D727E4B63C00A8C953 /* MatrixRustSDK */; };
18ADC7FA27EB02D900A8C953 /* AttributedStringBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18ADC7F927EB02D900A8C953 /* AttributedStringBuilder.swift */; }; 18ADC7FA27EB02D900A8C953 /* AttributedStringBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18ADC7F927EB02D900A8C953 /* AttributedStringBuilder.swift */; };
@@ -113,8 +116,6 @@
18F2BB1A27D25BE800DD1988 /* RoomTimelineController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18F2BB1927D25BE800DD1988 /* RoomTimelineController.swift */; }; 18F2BB1A27D25BE800DD1988 /* RoomTimelineController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18F2BB1927D25BE800DD1988 /* RoomTimelineController.swift */; };
18F2BB1D27D25D1C00DD1988 /* LoginScreenViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18F2BAD127D25B4000DD1988 /* LoginScreenViewModelTests.swift */; }; 18F2BB1D27D25D1C00DD1988 /* LoginScreenViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18F2BAD127D25B4000DD1988 /* LoginScreenViewModelTests.swift */; };
18F2BB1E27D25D2200DD1988 /* HomeScreenViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18F2BAA927D25B4000DD1988 /* HomeScreenViewModelTests.swift */; }; 18F2BB1E27D25D2200DD1988 /* HomeScreenViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18F2BAA927D25B4000DD1988 /* HomeScreenViewModelTests.swift */; };
18F2BB1F27D25D3100DD1988 /* RoomScreenViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18F2BAC327D25B4000DD1988 /* RoomScreenViewModelTests.swift */; };
18F2BB2027D25D3400DD1988 /* RoomScreenUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18F2BAC127D25B4000DD1988 /* RoomScreenUITests.swift */; };
18F2BB2127D25D3C00DD1988 /* HomeScreenUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18F2BAA727D25B4000DD1988 /* HomeScreenUITests.swift */; }; 18F2BB2127D25D3C00DD1988 /* HomeScreenUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18F2BAA727D25B4000DD1988 /* HomeScreenUITests.swift */; };
18F2BB2227D25D4600DD1988 /* LoginScreenUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18F2BACF27D25B4000DD1988 /* LoginScreenUITests.swift */; }; 18F2BB2227D25D4600DD1988 /* LoginScreenUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18F2BACF27D25B4000DD1988 /* LoginScreenUITests.swift */; };
18F2BB2827D2647A00DD1988 /* MockRoomTimelineController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18F2BB2727D2647A00DD1988 /* MockRoomTimelineController.swift */; }; 18F2BB2827D2647A00DD1988 /* MockRoomTimelineController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18F2BB2727D2647A00DD1988 /* MockRoomTimelineController.swift */; };
@@ -501,15 +502,15 @@
18F2BA7D27D25B4000DD1988 /* Other */ = { 18F2BA7D27D25B4000DD1988 /* Other */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
18920C1827F3222E00A717B5 /* Colors.swift */,
18F2BA9A27D25B4000DD1988 /* Coordinator.swift */, 18F2BA9A27D25B4000DD1988 /* Coordinator.swift */,
18F2BA9427D25B4000DD1988 /* MXLog.swift */,
18DDB72D27EF936D000F1ABF /* MatrixEntitityRegex.swift */, 18DDB72D27EF936D000F1ABF /* MatrixEntitityRegex.swift */,
18F2BA9427D25B4000DD1988 /* MXLog.swift */,
18F2BA8727D25B4000DD1988 /* Activity */, 18F2BA8727D25B4000DD1988 /* Activity */,
18ADC7F827EB02D900A8C953 /* HTMLParsing */, 18ADC7F827EB02D900A8C953 /* HTMLParsing */,
18F2BA7E27D25B4000DD1988 /* Routers */, 18F2BA7E27D25B4000DD1988 /* Routers */,
18F2BA9B27D25B4000DD1988 /* SwiftUI */, 18F2BA9B27D25B4000DD1988 /* SwiftUI */,
18F2BA9527D25B4000DD1988 /* WeakDictionary */, 18F2BA9527D25B4000DD1988 /* WeakDictionary */,
18920C1827F3222E00A717B5 /* Colors.swift */,
); );
path = Other; path = Other;
sourceTree = "<group>"; sourceTree = "<group>";
@@ -1018,11 +1019,12 @@
isa = PBXSourcesBuildPhase; isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
18F2BB2027D25D3400DD1988 /* RoomScreenUITests.swift in Sources */, 189E495227F43BCD00D86BA3 /* ElementXAttributeScope.swift in Sources */,
18DDB73227F19F82000F1ABF /* AttributedStringBuilderTests.swift in Sources */, 18DDB73227F19F82000F1ABF /* AttributedStringBuilderTests.swift in Sources */,
18F2BB1E27D25D2200DD1988 /* HomeScreenViewModelTests.swift in Sources */, 18F2BB1E27D25D2200DD1988 /* HomeScreenViewModelTests.swift in Sources */,
18F2BB1D27D25D1C00DD1988 /* LoginScreenViewModelTests.swift in Sources */, 18F2BB1D27D25D1C00DD1988 /* LoginScreenViewModelTests.swift in Sources */,
1850253F27B6918D002E6B18 /* ElementXTests.swift in Sources */, 1850253F27B6918D002E6B18 /* ElementXTests.swift in Sources */,
189E495427F474A500D86BA3 /* RoomScreenViewModelTests.swift in Sources */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
@@ -1032,9 +1034,9 @@
files = ( files = (
18F2BB2127D25D3C00DD1988 /* HomeScreenUITests.swift in Sources */, 18F2BB2127D25D3C00DD1988 /* HomeScreenUITests.swift in Sources */,
1850254B27B6918D002E6B18 /* ElementXUITestsLaunchTests.swift in Sources */, 1850254B27B6918D002E6B18 /* ElementXUITestsLaunchTests.swift in Sources */,
189E495327F474A200D86BA3 /* RoomScreenUITests.swift in Sources */,
1850254927B6918D002E6B18 /* ElementXUITests.swift in Sources */, 1850254927B6918D002E6B18 /* ElementXUITests.swift in Sources */,
18F2BB2227D25D4600DD1988 /* LoginScreenUITests.swift in Sources */, 18F2BB2227D25D4600DD1988 /* LoginScreenUITests.swift in Sources */,
18F2BB1F27D25D3100DD1988 /* RoomScreenViewModelTests.swift in Sources */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
@@ -0,0 +1,99 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1320"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "1850252327B6918C002E6B18"
BuildableName = "ElementX.app"
BlueprintName = "ElementX"
ReferencedContainer = "container:ElementX.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES"
codeCoverageEnabled = "YES">
<Testables>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "1850253927B6918D002E6B18"
BuildableName = "ElementXTests.xctest"
BlueprintName = "ElementXTests"
ReferencedContainer = "container:ElementX.xcodeproj">
</BuildableReference>
</TestableReference>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "1850254327B6918D002E6B18"
BuildableName = "ElementXUITests.xctest"
BlueprintName = "ElementXUITests"
ReferencedContainer = "container:ElementX.xcodeproj">
</BuildableReference>
</TestableReference>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "1850252327B6918C002E6B18"
BuildableName = "ElementX.app"
BlueprintName = "ElementX"
ReferencedContainer = "container:ElementX.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "1850252327B6918C002E6B18"
BuildableName = "ElementX.app"
BlueprintName = "ElementX"
ReferencedContainer = "container:ElementX.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>
@@ -18,7 +18,6 @@ struct AttributedStringBuilder: AttributedStringBuilderProtocol {
private let userIdDetector: NSRegularExpression private let userIdDetector: NSRegularExpression
private let roomIdDetector: NSRegularExpression private let roomIdDetector: NSRegularExpression
private let eventIdDetector: NSRegularExpression private let eventIdDetector: NSRegularExpression
private let groupIdDetector: NSRegularExpression
private let roomAliasDetector: NSRegularExpression private let roomAliasDetector: NSRegularExpression
private let linkDetector: NSDataDetector private let linkDetector: NSDataDetector
@@ -27,7 +26,6 @@ struct AttributedStringBuilder: AttributedStringBuilderProtocol {
userIdDetector = try NSRegularExpression(pattern: MatrixEntityRegex.userId.rawValue, options: .caseInsensitive) userIdDetector = try NSRegularExpression(pattern: MatrixEntityRegex.userId.rawValue, options: .caseInsensitive)
roomIdDetector = try NSRegularExpression(pattern: MatrixEntityRegex.roomId.rawValue, options: .caseInsensitive) roomIdDetector = try NSRegularExpression(pattern: MatrixEntityRegex.roomId.rawValue, options: .caseInsensitive)
eventIdDetector = try NSRegularExpression(pattern: MatrixEntityRegex.eventId.rawValue, options: .caseInsensitive) eventIdDetector = try NSRegularExpression(pattern: MatrixEntityRegex.eventId.rawValue, options: .caseInsensitive)
groupIdDetector = try NSRegularExpression(pattern: MatrixEntityRegex.groupId.rawValue, options: .caseInsensitive)
roomAliasDetector = try NSRegularExpression(pattern: MatrixEntityRegex.roomAlias.rawValue, options: .caseInsensitive) roomAliasDetector = try NSRegularExpression(pattern: MatrixEntityRegex.roomAlias.rawValue, options: .caseInsensitive)
linkDetector = try NSDataDetector(types: NSTextCheckingResult.CheckingType.link.rawValue) linkDetector = try NSDataDetector(types: NSTextCheckingResult.CheckingType.link.rawValue)
} catch { } catch {
@@ -67,7 +65,6 @@ struct AttributedStringBuilder: AttributedStringBuilderProtocol {
DTDefaultFontFamily: defaultFont.familyName, DTDefaultFontFamily: defaultFont.familyName,
DTDefaultFontName: defaultFont.fontName, DTDefaultFontName: defaultFont.fontName,
DTDefaultFontSize: defaultFont.pointSize, DTDefaultFontSize: defaultFont.pointSize,
DTDefaultLinkDecoration: false,
DTDefaultStyleSheet: DTCSSStylesheet(styleBlock: self.defaultCSS) as Any DTDefaultStyleSheet: DTCSSStylesheet(styleBlock: self.defaultCSS) as Any
] ]
@@ -84,6 +81,7 @@ struct AttributedStringBuilder: AttributedStringBuilderProtocol {
} }
let mutableAttributedString = NSMutableAttributedString(attributedString: attributedString) let mutableAttributedString = NSMutableAttributedString(attributedString: attributedString)
removeDefaultForegroundColor(mutableAttributedString)
addLinks(mutableAttributedString) addLinks(mutableAttributedString)
removeLinkColors(mutableAttributedString) removeLinkColors(mutableAttributedString)
removeDTCoreTextArtifacts(mutableAttributedString) removeDTCoreTextArtifacts(mutableAttributedString)
@@ -161,7 +159,6 @@ struct AttributedStringBuilder: AttributedStringBuilderProtocol {
var matches = userIdDetector.matches(in: string, options: [], range: range) var matches = userIdDetector.matches(in: string, options: [], range: range)
matches.append(contentsOf: roomIdDetector.matches(in: string, options: [], range: range)) matches.append(contentsOf: roomIdDetector.matches(in: string, options: [], range: range))
matches.append(contentsOf: eventIdDetector.matches(in: string, options: [], range: range)) matches.append(contentsOf: eventIdDetector.matches(in: string, options: [], range: range))
matches.append(contentsOf: groupIdDetector.matches(in: string, options: [], range: range))
matches.append(contentsOf: roomAliasDetector.matches(in: string, options: [], range: range)) matches.append(contentsOf: roomAliasDetector.matches(in: string, options: [], range: range))
matches.append(contentsOf: linkDetector.matches(in: string, options: [], range: range)) matches.append(contentsOf: linkDetector.matches(in: string, options: [], range: range))
@@ -174,14 +171,36 @@ struct AttributedStringBuilder: AttributedStringBuilderProtocol {
return return
} }
var hasLink = false
attributedString.enumerateAttribute(.link, in: match.range, options: []) { value, _, stop in
if value != nil {
hasLink = true
stop.pointee = true
}
}
if hasLink {
return
}
let link = string[matchRange].addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) let link = string[matchRange].addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)
attributedString.addAttribute(.link, value: link as Any, range: match.range) attributedString.addAttribute(.link, value: link as Any, range: match.range)
} }
} }
private func removeDefaultForegroundColor(_ attributedString: NSMutableAttributedString) {
attributedString.enumerateAttribute(.foregroundColor, in: .init(location: 0, length: attributedString.length), options: []) { value, range, _ in
if value as? UIColor == UIColor.black {
attributedString.removeAttribute(.foregroundColor, range: range)
}
}
}
private func removeLinkColors(_ attributedString: NSMutableAttributedString) { private func removeLinkColors(_ attributedString: NSMutableAttributedString) {
attributedString.enumerateAttribute(.link, in: .init(location: 0, length: attributedString.length), options: []) { _, range, _ in attributedString.enumerateAttribute(.link, in: .init(location: 0, length: attributedString.length), options: []) { value, range, _ in
attributedString.removeAttribute(.foregroundColor, range: range) if value != nil {
attributedString.removeAttribute(.foregroundColor, range: range)
}
} }
} }
@@ -14,7 +14,6 @@ enum MatrixEntityRegex: String {
case roomAlias case roomAlias
case roomId case roomId
case eventId case eventId
case groupId
var rawValue: String { var rawValue: String {
switch self { switch self {
@@ -28,8 +27,6 @@ enum MatrixEntityRegex: String {
return "![A-Z0-9]+:" + MatrixEntityRegex.homeserver.rawValue return "![A-Z0-9]+:" + MatrixEntityRegex.homeserver.rawValue
case .eventId: case .eventId:
return "\\$[A-Z0-9]+:" + MatrixEntityRegex.homeserver.rawValue return "\\$[A-Z0-9]+:" + MatrixEntityRegex.homeserver.rawValue
case .groupId:
return "\\+[A-Z0-9=_\\-./]+:" + MatrixEntityRegex.homeserver.rawValue
} }
} }
} }
@@ -11,11 +11,261 @@ import XCTest
class AttributedStringBuilderTests: XCTestCase { class AttributedStringBuilderTests: XCTestCase {
override func setUpWithError() throws { let attributedStringBuilder = AttributedStringBuilder()
// Put setup code here. This method is called before the invocation of each test method in the class. let maxHeaderPointSize = ceil(UIFont.preferredFont(forTextStyle: .body).pointSize * 1.2)
func testRenderHTMLStringWithHeaders() {
let h1HTMLString = "<h1>Large Heading</h1>";
let h2HTMLString = "<h2>Smaller Heading</h2>";
let h3HTMLString = "<h3>Acceptable Heading</h3>";
guard let h1AttributedString = attributedStringBuilder.fromHTML(h1HTMLString),
let h2AttributedString = attributedStringBuilder.fromHTML(h2HTMLString),
let h3AttributedString = attributedStringBuilder.fromHTML(h3HTMLString) else {
XCTFail("Could not build the attributed string")
return
}
XCTAssertEqual(String(h1AttributedString.characters), "Large Heading")
XCTAssertEqual(String(h2AttributedString.characters), "Smaller Heading")
XCTAssertEqual(String(h3AttributedString.characters), "Acceptable Heading")
XCTAssert(h1AttributedString.runs.count == 1)
XCTAssert(h2AttributedString.runs.count == 1)
XCTAssert(h3AttributedString.runs.count == 1)
let h1Font = h1AttributedString.runs.first?.uiKit.font
let h2Font = h2AttributedString.runs.first?.uiKit.font
let h3Font = h3AttributedString.runs.first?.uiKit.font
XCTAssertEqual(h1Font, h2Font)
XCTAssertEqual(h2Font, h3Font)
XCTAssert(h1Font!.pointSize > UIFont.preferredFont(forTextStyle: .body).pointSize)
XCTAssert(h1Font!.pointSize <= maxHeaderPointSize)
} }
override func tearDownWithError() throws { func testRenderHTMLStringWithPreCode() {
// Put teardown code here. This method is called after the invocation of each test method in the class. let htmlString = "<pre><code>1\n2\n3\n4\n</code></pre>"
guard let attributedString = attributedStringBuilder.fromHTML(htmlString) else {
XCTFail("Could not build the attributed string")
return
}
XCTAssertEqual(attributedString.runs.first?.uiKit.font?.fontName, "Menlo-Regular")
let string = String(attributedString.characters)
let regex = try! NSRegularExpression(pattern: "\\R", options: [])
XCTAssertEqual(regex.numberOfMatches(in: string, options: [], range: .init(location: 0, length: string.count)), 3)
}
func testRenderHTMLStringWithLink() {
let htmlString = "This text contains a <a href=\"https://www.matrix.org/\">link</a>.";
guard let attributedString = attributedStringBuilder.fromHTML(htmlString) else {
XCTFail("Could not build the attributed string")
return
}
XCTAssertEqual(String(attributedString.characters), "This text contains a link.");
XCTAssertEqual(attributedString.runs.count, 3)
let link = attributedString.runs.first(where: { $0.link != nil })?.link
XCTAssertEqual(link?.host, "www.matrix.org")
}
func testRenderPlainStringWithLink() {
let plainString = "This text contains a https://www.matrix.org link.";
guard let attributedString = attributedStringBuilder.fromPlain(plainString) else {
XCTFail("Could not build the attributed string")
return
}
XCTAssertEqual(String(attributedString.characters), plainString);
XCTAssertEqual(attributedString.runs.count, 3)
let link = attributedString.runs.first(where: { $0.link != nil })?.link
XCTAssertEqual(link?.host, "www.matrix.org")
}
func testRenderHTMLStringWithLinkInHeader() {
let h1HTMLString = "<h1><a href=\"https://www.matrix.org/\">Matrix.org</a></h1>";
let h2HTMLString = "<h2><a href=\"https://www.matrix.org/\">Matrix.org</a></h2>";
let h3HTMLString = "<h3><a href=\"https://www.matrix.org/\">Matrix.org</a></h3>";
guard let h1AttributedString = attributedStringBuilder.fromHTML(h1HTMLString),
let h2AttributedString = attributedStringBuilder.fromHTML(h2HTMLString),
let h3AttributedString = attributedStringBuilder.fromHTML(h3HTMLString) else {
XCTFail("Could not build the attributed string")
return
}
XCTAssertEqual(String(h1AttributedString.characters), "Matrix.org")
XCTAssertEqual(String(h2AttributedString.characters), "Matrix.org")
XCTAssertEqual(String(h3AttributedString.characters), "Matrix.org")
XCTAssertEqual(h1AttributedString.runs.count, 1)
XCTAssertEqual(h2AttributedString.runs.count, 1)
XCTAssertEqual(h3AttributedString.runs.count, 1)
let h1Font = h1AttributedString.runs.first?.uiKit.font
let h2Font = h2AttributedString.runs.first?.uiKit.font
let h3Font = h3AttributedString.runs.first?.uiKit.font
XCTAssertEqual(h1Font, h2Font)
XCTAssertEqual(h2Font, h3Font)
XCTAssert(h1Font!.pointSize > UIFont.preferredFont(forTextStyle: .body).pointSize)
XCTAssert(h1Font!.pointSize <= maxHeaderPointSize)
XCTAssertEqual(h1AttributedString.runs.first?.link?.host, "www.matrix.org")
XCTAssertEqual(h2AttributedString.runs.first?.link?.host, "www.matrix.org")
XCTAssertEqual(h3AttributedString.runs.first?.link?.host, "www.matrix.org")
}
func testRenderHTMLStringWithIFrame() {
let htmlString = "<iframe src=\"https://www.matrix.org/\"></iframe>";
guard let attributedString = attributedStringBuilder.fromHTML(htmlString) else {
XCTFail("Could not build the attributed string")
return
}
XCTAssertNil(attributedString.uiKit.attachment, "iFrame attachments should be removed as they're not included in the allowedHTMLTags array.")
}
func testUserIdLink() {
let userId = "@user:matrix.org"
let string = "The user is \(userId).";
checkMatrixEntityLinkIn(attributedString: attributedStringBuilder.fromHTML(string), expected: userId)
checkMatrixEntityLinkIn(attributedString: attributedStringBuilder.fromPlain(string), expected: userId)
}
func testRoomAliasLink() {
let roomAlias = "#matrix:matrix.org"
let string = "The room alias is \(roomAlias).";
checkMatrixEntityLinkIn(attributedString: attributedStringBuilder.fromHTML(string), expected: roomAlias)
checkMatrixEntityLinkIn(attributedString: attributedStringBuilder.fromPlain(string), expected: roomAlias)
}
func testRoomIdLink() {
let roomId = "!roomidentifier:matrix.org"
let string = "The room is \(roomId).";
checkMatrixEntityLinkIn(attributedString: attributedStringBuilder.fromHTML(string), expected: roomId)
checkMatrixEntityLinkIn(attributedString: attributedStringBuilder.fromPlain(string), expected: roomId)
}
func testEventIdLink() {
let eventId = "$eventidentifier:matrix.org"
let string = "The event is \(eventId).";
checkMatrixEntityLinkIn(attributedString: attributedStringBuilder.fromHTML(string), expected: eventId)
checkMatrixEntityLinkIn(attributedString: attributedStringBuilder.fromPlain(string), expected: eventId)
}
func testDefaultFont() {
let htmlString = "<b>Test</b> <i>string</i>."
guard let attributedString = attributedStringBuilder.fromHTML(htmlString) else {
XCTFail("Could not build the attributed string")
return
}
XCTAssertEqual(attributedString.runs.count, 4)
for run in attributedString.runs {
XCTAssertEqual(run.uiKit.font?.familyName, UIFont.preferredFont(forTextStyle: .body).familyName)
}
}
func testDefaultForegroundColor() {
let htmlString = "<b>Test</b> <i>string</i>."
guard let attributedString = attributedStringBuilder.fromHTML(htmlString) else {
XCTFail("Could not build the attributed string")
return
}
XCTAssertEqual(attributedString.runs.count, 4)
for run in attributedString.runs {
XCTAssertNil(run.uiKit.foregroundColor)
}
}
func testCustomForegroundColor() {
let htmlString = "<font color=\"#ff00be\">R</font><font color=\"#ff0082\">a</font><font color=\"#ff0047\">i</font><font color=\"#ff5800\">n </font><font color=\"#ffa300\">w</font><font color=\"#d2ba00\">w</font><font color=\"#97ca00\">w</font><font color=\"#3ed500\">.</font><font color=\"#00dd00\">m</font><font color=\"#00e251\">a</font><font color=\"#00e595\">t</font><font color=\"#00e7d6\">r</font><font color=\"#00e7ff\">i</font><font color=\"#00e6ff\">x</font><font color=\"#00e3ff\">.</font><font color=\"#00dbff\">o</font><font color=\"#00ceff\">r</font><font color=\"#00baff\">g</font><font color=\"#f477ff\"> b</font><font color=\"#ff3aff\">o</font><font color=\"#ff00fb\">w</font>"
guard let attributedString = attributedStringBuilder.fromHTML(htmlString) else {
XCTFail("Could not build the attributed string")
return
}
XCTAssertEqual(attributedString.runs.count, 8)
var foundLink = false
for run in attributedString.runs {
if run.link != nil {
XCTAssertEqual(run.link?.path, "www.matrix.org")
XCTAssertNil(run.uiKit.foregroundColor)
foundLink = true
} else {
XCTAssertNotNil(run.uiKit.foregroundColor)
}
}
XCTAssertTrue(foundLink)
}
func testBlockquotes() {
let htmlString = """
The text before the blockquote
<blockquote cite="http://www.worldwildlife.org/who/index.html"> For 50 years, WWF has been protecting the future of nature. The world's leading conservation organization, WWF works in 100 countries and is supported by 1.2 million members in the United States and close to 5 million globally.</blockquote>
The text after the blockquote
"""
guard let attributedString = attributedStringBuilder.fromHTML(htmlString) else {
XCTFail("Could not build the attributed string")
return
}
XCTAssertEqual(attributedString.runs.count, 3)
for run in attributedString.runs {
if run.elementX.blockquote != nil {
return
}
}
XCTFail("Couldn't find blockquote")
XCTAssertEqual(attributedStringBuilder.blockquoteCoalescedComponentsFrom(attributedString)?.count, 3)
}
// MARK: - Private
private func checkMatrixEntityLinkIn(attributedString: AttributedString?, expected: String) {
guard let attributedString = attributedString else {
XCTFail("Could not build the attributed string")
return
}
XCTAssertEqual(attributedString.runs.count, 3)
for run in attributedString.runs {
if run.link != nil {
XCTAssertEqual(run.link?.path, expected)
return
}
}
XCTFail("Couldn't find expected value.")
} }
} }
-16
View File
@@ -17,20 +17,4 @@ class ElementXTests: XCTestCase {
override func tearDownWithError() throws { override func tearDownWithError() throws {
// Put teardown code here. This method is called after the invocation of each test method in the class. // Put teardown code here. This method is called after the invocation of each test method in the class.
} }
func testExample() throws {
// This is an example of a functional test case.
// Use XCTAssert and related functions to verify your tests produce the correct results.
// Any test you write for XCTest can be annotated as throws and async.
// Mark your test throws to produce an unexpected failure when your test encounters an uncaught error.
// Mark your test async to allow awaiting for asynchronous code to complete. Check the results with assertions afterwards.
}
func testPerformanceExample() throws {
// This is an example of a performance test case.
self.measure {
// Put the code you want to measure the time of here.
}
}
} }