Skip to content

Commit

Permalink
[MBL-943] Add Additional Header Support to HTML Parser (#1844)
Browse files Browse the repository at this point in the history
* updated headers from h1 to include h1 through h6.

* updated tests, made a correct to include h6 tag in view element type.
  • Loading branch information
msadoon committed Aug 21, 2023
1 parent baffd7c commit 15d3e2b
Show file tree
Hide file tree
Showing 6 changed files with 160 additions and 48 deletions.
52 changes: 32 additions & 20 deletions KsApi/lib/HTML Parser/HTMLParserTests.swift
Expand Up @@ -218,27 +218,39 @@ final class HTMLParserTests: XCTestCase {
XCTAssertEqual(viewElement.embeddedURLContentHeight, 400)
}

func testHTMLParser_WithTextHeadline_Success() {
let viewElements = self.htmlParser.parse(bodyHtml: HTMLParserTemplates.validHeaderText.data)

guard let viewElement = viewElements.first as? TextViewElement else {
XCTFail("text view element should be created.")

return
}

guard viewElement.components.count == 1 else {
XCTFail()

return
func testHTMLParser_WithTextHeadlines_Success() {
let viewElementsToTextStyleHeaders =
[
HTMLParserTemplates.validHeader1Text.data: TextComponent.TextStyleType.header1,
HTMLParserTemplates.validHeader2Text.data: TextComponent.TextStyleType.header2,
HTMLParserTemplates.validHeader3Text.data: TextComponent.TextStyleType.header3,
HTMLParserTemplates.validHeader4Text.data: TextComponent.TextStyleType.header4,
HTMLParserTemplates.validHeader5Text.data: TextComponent.TextStyleType.header5,
HTMLParserTemplates.validHeader6Text.data: TextComponent.TextStyleType.header6
]

_ = viewElementsToTextStyleHeaders.map { textData, headerStyle in
let viewElements = self.htmlParser.parse(bodyHtml: textData)

guard let viewElement = viewElements.first as? TextViewElement else {
XCTFail("text view element should be created.")

return
}

guard viewElement.components.count == 1 else {
XCTFail()

return
}

XCTAssertEqual(
viewElement.components[0].text,
"Please participate in helping me finish my film! Just pick a level in the right hand column and click to donate — it only takes a minute."
)
XCTAssertNil(viewElement.components[0].link)
XCTAssertEqual(viewElement.components[0].styles, [headerStyle])
}

XCTAssertEqual(
viewElement.components[0].text,
"Please participate in helping me finish my film! Just pick a level in the right hand column and click to donate — it only takes a minute."
)
XCTAssertNil(viewElement.components[0].link)
XCTAssertEqual(viewElement.components[0].styles, [TextComponent.TextStyleType.header])
}

func testHTMLParser_WithMultipleParagraphsLinksAndStyles_Success() {
Expand Down
58 changes: 54 additions & 4 deletions KsApi/lib/HTML Parser/Templates/HTMLParserTemplates.swift
Expand Up @@ -11,7 +11,12 @@ public enum HTMLParserTemplates {
case validHiddenVideo
case validIFrame
case validIFrameWithEmbeddedSource
case validHeaderText
case validHeader1Text
case validHeader2Text
case validHeader3Text
case validHeader4Text
case validHeader5Text
case validHeader6Text
case validParagraphTextWithStyles
case validParagraphTextWithLinksAndStyles
case validListWithNestedLinks
Expand Down Expand Up @@ -39,8 +44,18 @@ public enum HTMLParserTemplates {
return self.validExternalSource
case .validIFrameWithEmbeddedSource:
return self.validEmbeddedExternalSource
case .validHeaderText:
return self.validHeaderText
case .validHeader1Text:
return self.validHeader1Text
case .validHeader2Text:
return self.validHeader2Text
case .validHeader3Text:
return self.validHeader3Text
case .validHeader4Text:
return self.validHeader4Text
case .validHeader5Text:
return self.validHeader5Text
case .validHeader6Text:
return self.validHeader6Text
case .validParagraphTextWithLinksAndStyles:
return self.validParagraphTextWithLinksAndStyles
case .validParagraphTextWithStyles:
Expand Down Expand Up @@ -74,13 +89,48 @@ public enum HTMLParserTemplates {
"""
}

private var validHeaderText: String {
private var validHeader1Text: String {
"""
<h1 id=\"h:please-participate-i\" class=\"page-anchor\">Please participate in helping me finish my film! Just pick a level in the right hand column and click to donate — it only takes a minute.</h1>
\n<br>\n
"""
}

private var validHeader2Text: String {
"""
<h2 id=\"h:please-participate-i\" class=\"page-anchor\">Please participate in helping me finish my film! Just pick a level in the right hand column and click to donate — it only takes a minute.</h2>
\n<br>\n
"""
}

private var validHeader3Text: String {
"""
<h3 id=\"h:please-participate-i\" class=\"page-anchor\">Please participate in helping me finish my film! Just pick a level in the right hand column and click to donate — it only takes a minute.</h3>
\n<br>\n
"""
}

private var validHeader4Text: String {
"""
<h4 id=\"h:please-participate-i\" class=\"page-anchor\">Please participate in helping me finish my film! Just pick a level in the right hand column and click to donate — it only takes a minute.</h4>
\n<br>\n
"""
}

private var validHeader5Text: String {
"""
<h5 id=\"h:please-participate-i\" class=\"page-anchor\">Please participate in helping me finish my film! Just pick a level in the right hand column and click to donate — it only takes a minute.</h5>
\n<br>\n
"""
}

private var validHeader6Text: String {
"""
<h6 id=\"h:please-participate-i\" class=\"page-anchor\">Please participate in helping me finish my film! Just pick a level in the right hand column and click to donate — it only takes a minute.</h6>
\n<br>\n
"""
}

private var validAudio: String {
"""
\n
Expand Down
12 changes: 11 additions & 1 deletion KsApi/lib/HTML Parser/View Elements/TextComponent.swift
Expand Up @@ -9,6 +9,11 @@ public struct TextComponent {
enum TextBlockType: String, CaseIterable {
case paragraph = "p"
case header1 = "h1"
case header2 = "h2"
case header3 = "h3"
case header4 = "h4"
case header5 = "h5"
case header6 = "h6"
case unorderedList = "ul"
case orderedList = "ol"
}
Expand All @@ -20,6 +25,11 @@ public struct TextComponent {
case bulletStart = "li"
case bulletEnd = "</li>"
case link = "a"
case header = "h1"
case header1 = "h1"
case header2 = "h2"
case header3 = "h3"
case header4 = "h4"
case header5 = "h5"
case header6 = "h6"
}
}
5 changes: 5 additions & 0 deletions KsApi/lib/HTML Parser/View Elements/ViewElementType.swift
Expand Up @@ -18,6 +18,11 @@ enum ViewElementType: String {

self = childElementType
case TextComponent.TextBlockType.header1.rawValue,
TextComponent.TextBlockType.header2.rawValue,
TextComponent.TextBlockType.header3.rawValue,
TextComponent.TextBlockType.header4.rawValue,
TextComponent.TextBlockType.header5.rawValue,
TextComponent.TextBlockType.header6.rawValue,
TextComponent.TextBlockType.unorderedList.rawValue,
TextComponent.TextBlockType.orderedList.rawValue,
TextComponent.TextBlockType.paragraph.rawValue:
Expand Down
28 changes: 24 additions & 4 deletions Library/ViewModels/HTML Parser/TextViewElementCellViewModel.swift
Expand Up @@ -52,8 +52,28 @@ private func attributedText(textElement: TextViewElement) -> SignalProducer<NSAt
let fullRange = (componentText as NSString).localizedStandardRange(of: textItem.text)
let baseFontSize: CGFloat = 16.0
let baseFont = UIFont.ksr_body(size: baseFontSize)
let headerFontSize: CGFloat = 20.0
let headerFont = UIFont.ksr_body(size: headerFontSize).bolded
let header1FontSize: CGFloat = 28.0
let header1Font = UIFont.ksr_body(size: header1FontSize).bolded
let header2FontSize: CGFloat = 26.0
let header2Font = UIFont.ksr_body(size: header2FontSize).bolded
let header3FontSize: CGFloat = 24.0
let header3Font = UIFont.ksr_body(size: header3FontSize).bolded
let header4FontSize: CGFloat = 22.0
let header4Font = UIFont.ksr_body(size: header4FontSize).bolded
let header5FontSize: CGFloat = 20.0
let header5Font = UIFont.ksr_body(size: header5FontSize).bolded
let header6FontSize: CGFloat = 18.0
let header6Font = UIFont.ksr_body(size: header6FontSize).bolded

let textHeaderFonts = [
TextComponent.TextStyleType.header1: header1Font,
TextComponent.TextStyleType.header2: header2Font,
TextComponent.TextStyleType.header3: header3Font,
TextComponent.TextStyleType.header4: header4Font,
TextComponent.TextStyleType.header5: header5Font,
TextComponent.TextStyleType.header6: header6Font
]

paragraphStyle.minimumLineHeight = 22
let baseFontAttributes = [
NSAttributedString.Key.font: baseFont,
Expand Down Expand Up @@ -111,8 +131,8 @@ private func attributedText(textElement: TextViewElement) -> SignalProducer<NSAt
if moreBulletPointsExist {
completedAttributedText.append(NSAttributedString(string: "\n"))
}
case .header:
combinedAttributes[NSAttributedString.Key.font] = headerFont
case .header1, .header2, .header3, .header4, .header5, .header6:
combinedAttributes[NSAttributedString.Key.font] = textHeaderFonts[textStyleType]
combinedAttributes[NSAttributedString.Key.foregroundColor] = UIColor.ksr_support_700
paragraphStyle.minimumLineHeight = 25
combinedAttributes[NSAttributedString.Key.paragraphStyle] = paragraphStyle
Expand Down
Expand Up @@ -11,7 +11,12 @@ internal final class TextElementCellViewModelTests: TestCase {

private let expectedSampleString = "sample attributed string"
private let expectedBaseFontSize: CGFloat = 16.0
private let expectedHeaderFontSize: CGFloat = 20.0
private let expectedHeader1FontSize: CGFloat = 28.0
private let expectedHeader2FontSize: CGFloat = 26.0
private let expectedHeader3FontSize: CGFloat = 24.0
private let expectedHeader4FontSize: CGFloat = 22.0
private let expectedHeader5FontSize: CGFloat = 20.0
private let expectedHeader6FontSize: CGFloat = 18.0
private var expectedBaseFont = UIFont.ksr_body()
private var expectedParagraphStyle = NSMutableParagraphStyle()
private var expectedHeaderFont = UIFont.ksr_body()
Expand All @@ -21,7 +26,6 @@ internal final class TextElementCellViewModelTests: TestCase {
super.setUp()

self.expectedBaseFont = UIFont.ksr_body(size: self.expectedBaseFontSize)
self.expectedHeaderFont = UIFont.ksr_body(size: self.expectedHeaderFontSize).bolded
self.expectedParagraphStyle.minimumLineHeight = 22
self.expectedFontAttributes = [
NSAttributedString.Key.font: self.expectedBaseFont,
Expand Down Expand Up @@ -148,26 +152,37 @@ internal final class TextElementCellViewModelTests: TestCase {
self.bodyText.assertValue(expectedLinkWithNoStylesAttributedText)
}

func testHeaderElement() {
let headerTextComponent = TextComponent(
text: expectedSampleString,
link: nil,
styles: [.header]
)
let headerTextElement = TextViewElement(components: [headerTextComponent])
func testHeaderElements() {
let headerTypesToFontSizes = [
TextComponent.TextStyleType.header1: self.expectedHeader1FontSize,
TextComponent.TextStyleType.header2: self.expectedHeader2FontSize,
TextComponent.TextStyleType.header3: self.expectedHeader3FontSize,
TextComponent.TextStyleType.header4: self.expectedHeader4FontSize,
TextComponent.TextStyleType.header5: self.expectedHeader5FontSize,
TextComponent.TextStyleType.header6: self.expectedHeader6FontSize
]
_ = headerTypesToFontSizes.map { headerType, fontSize in
let headerTextComponent = TextComponent(
text: expectedSampleString,
link: nil,
styles: [headerType]
)
let headerTextElement = TextViewElement(components: [headerTextComponent])

expectedFontAttributes[NSAttributedString.Key.font] = self.expectedHeaderFont
self.expectedFontAttributes[NSAttributedString.Key.foregroundColor] = UIColor.ksr_support_700
self.expectedParagraphStyle.minimumLineHeight = 25
self.expectedFontAttributes[NSAttributedString.Key.paragraphStyle] = self.expectedParagraphStyle
self.expectedHeaderFont = UIFont.ksr_body(size: fontSize).bolded
expectedFontAttributes[NSAttributedString.Key.font] = self.expectedHeaderFont
self.expectedFontAttributes[NSAttributedString.Key.foregroundColor] = UIColor.ksr_support_700
self.expectedParagraphStyle.minimumLineHeight = 25
self.expectedFontAttributes[NSAttributedString.Key.paragraphStyle] = self.expectedParagraphStyle

let expectedHeaderAttributedText = NSAttributedString(
string: expectedSampleString,
attributes: expectedFontAttributes
)
let expectedHeaderAttributedText = NSAttributedString(
string: expectedSampleString,
attributes: expectedFontAttributes
)

self.vm.inputs.configureWith(textElement: headerTextElement)
self.bodyText.assertValue(expectedHeaderAttributedText)
self.vm.inputs.configureWith(textElement: headerTextElement)
self.bodyText.assertLastValue(expectedHeaderAttributedText)
}
}

func testListElement() {
Expand Down

0 comments on commit 15d3e2b

Please sign in to comment.