diff --git a/ElementX/Sources/Other/HTMLParsing/AttributedStringBuilder.swift b/ElementX/Sources/Other/HTMLParsing/AttributedStringBuilder.swift index 8d4bc9bf3..e74e616f8 100644 --- a/ElementX/Sources/Other/HTMLParsing/AttributedStringBuilder.swift +++ b/ElementX/Sources/Other/HTMLParsing/AttributedStringBuilder.swift @@ -236,19 +236,25 @@ struct AttributedStringBuilder: AttributedStringBuilderProtocol { } content = attributedString(element: childElement, documentBody: documentBody, preserveFormatting: preserveFormatting, listTag: tag, listIndex: &listIndex, indentLevel: indentLevel + 1) + + if indentLevel > 0 { + content.insert(NSAttributedString("\n"), at: 0) + } case "li": - var bullet = "" + var bullet = String(repeating: " ", count: indentLevel) if listTag == "ol" { - bullet = "\(listIndex). " + bullet += "\(listIndex). " listIndex += 1 } else { - bullet = "• " + bullet += "• " } content = attributedString(element: childElement, documentBody: documentBody, preserveFormatting: preserveFormatting, listTag: listTag, listIndex: &childIndex, indentLevel: indentLevel + 1) content.insert(NSAttributedString(string: bullet), at: 0) - content.append(NSAttributedString(string: "\n")) + if !(content.string.last?.isNewline ?? false) { + content.append(NSAttributedString(string: "\n")) + } case "img": if let alt = try? childElement.attr("alt"), !alt.isEmpty { diff --git a/UnitTests/Sources/AttributedStringBuilderTests.swift b/UnitTests/Sources/AttributedStringBuilderTests.swift index 79fca3ddc..405fa057a 100644 --- a/UnitTests/Sources/AttributedStringBuilderTests.swift +++ b/UnitTests/Sources/AttributedStringBuilderTests.swift @@ -686,7 +686,51 @@ class AttributedStringBuilderTests: XCTestCase { return } - XCTAssertEqual(String(attributedString.characters), "like\n\n • this\ntest") + XCTAssertEqual(String(attributedString.characters), "like\n\n • this\ntest") + } + + func testUnorderedList() { + let htmlString = "" + + guard let attributedString = attributedStringBuilder.fromHTML(htmlString) else { + XCTFail("Could not build the attributed string") + return + } + + XCTAssertEqual(String(attributedString.characters), " • 1\n • 2\n • 3") + } + + func testNestedUnorderedList() { + let htmlString = "" + + guard let attributedString = attributedStringBuilder.fromHTML(htmlString) else { + XCTFail("Could not build the attributed string") + return + } + + XCTAssertEqual(String(attributedString.characters), " • A\n • A1\n • A2\n • A3\n • B\n • C") + } + + func testOrderedList() { + let htmlString = "
  1. 1
  2. 2
  3. 3
" + + guard let attributedString = attributedStringBuilder.fromHTML(htmlString) else { + XCTFail("Could not build the attributed string") + return + } + + XCTAssertEqual(String(attributedString.characters), " 1. 1\n 2. 2\n 3. 3") + } + + func testNestedOrderedList() { + let htmlString = "
  1. A
    1. A1
    2. A2
    3. A3
  2. B
  3. C
" + + guard let attributedString = attributedStringBuilder.fromHTML(htmlString) else { + XCTFail("Could not build the attributed string") + return + } + + XCTAssertEqual(String(attributedString.characters), " 1. A\n 1. A1\n 2. A2\n 3. A3\n 2. B\n 3. C") } func testOutOfOrderListNubmering() { @@ -697,7 +741,18 @@ class AttributedStringBuilderTests: XCTestCase { return } - XCTAssertEqual(String(attributedString.characters), " 2. this is a two") + XCTAssertEqual(String(attributedString.characters), " 2. this is a two") + } + + func testNestedHeterogeneousLists() { + let htmlString = "
  1. A
  2. B
  3. C
" + + guard let attributedString = attributedStringBuilder.fromHTML(htmlString) else { + XCTFail("Could not build the attributed string") + return + } + + XCTAssertEqual(String(attributedString.characters), " 1. A\n • A1\n • A2\n • A3\n 2. B\n 3. C") } // MARK: - Phishing prevention