titlecase-1.0.2: tests/Unit.hs
module Unit where
import Data.Text.Titlecase
import qualified Data.Text.Titlecase.Internal as Titlecase
import Data.Text.Titlecase.Internal hiding (articles, conjunctions, prepositions)
import Test.Tasty
import Test.Tasty.HUnit
import Data.Char ( toUpper )
tests :: TestTree
tests = testGroup "Unit tests" [articles, conjunctions, prepositions, issue4, issue5, issue7, examples]
issue5 :: TestTree
issue5 = testGroup "#5: hyphenated titles"
[ testCase label $ titlecase input @?= expected
| (label, input, expected) <-
[ ("reported example", "John wins follow-up election", "John Wins Follow-up Election")
, ("final up", "a follow-up", "A Follow-Up")
, ("internal exceptions", "state-of-the-art design", "State-of-the-Art Design")
, ("mother-in-law", "mother-in-law", "Mother-in-Law")
, ("final component", "a walk-in", "A Walk-In")
, ("internal final component", "a walk-in closet", "A Walk-in Closet")
, ("existing case", "non-English text", "Non-English Text")
, ("conjunction", "a pick-and-mix selection", "A Pick-and-Mix Selection")
, ("first component", "the-art of writing", "The-Art of Writing")
, ("multi-word preposition", "in-front-of", "In-front-Of")
, ("four-word preposition", "at-the-behest-of", "At-the-behest-Of")
, ("mixed separators", " far-from perfect:\tthings to complain-about ", " Far-from Perfect:\tThings to Complain-About ")
, ("repeated and boundary hyphens", "--the--story-of--", "--The--Story-Of--")
, ("hyphens only", " - --\t- ", " - --\t- ")
, ("Unicode components", "vis-à-vis", "Vis-À-Vis")
, ("en dash is unchanged", "hello–world", "Hello–world")
, ("em dash is unchanged", "hello—world", "Hello—world")
]
]
issue7 :: TestTree
issue7 = testGroup "#7: preserve whitespace"
[ testCase label $ titlecase input @?= expected
| (label, input, expected) <-
[ ("leading spaces", " foo bar", " Foo Bar")
, ("trailing spaces", "foo bar ", "Foo Bar ")
, ("repeated spaces", "foo bar", "Foo Bar")
, ("tab", "foo\tbar", "Foo\tBar")
, ("nonbreaking space", "foo\160bar", "Foo\160Bar")
, ("newlines", "foo\r\nbar\n", "Foo\r\nBar\n")
, ("empty input", "", "")
, ("whitespace only", " \t\r\n\160 ", " \t\r\n\160 ")
, ("padded boundary words", "\tthe story of\n", "\tThe Story Of\n")
, ("two-word preposition", "\tfar from perfect about\n", "\tFar from Perfect About\n")
, ("three-word preposition", "in\tfront of", "In\tfront Of")
, ("four-word preposition", "at\tthe\nbehest\160of ", "At\tthe\nbehest\160Of ")
, ("internal preposition", "a tale in\tfront of us", "A Tale in\tfront of Us")
]
]
issue4 :: TestTree
issue4 = testCase "#4: capitalize final word after a multi-word preposition" $
titlecase "far from perfect: things to complain about"
@?= "Far from Perfect: Things to Complain About"
-- Each phrase is a separate test so failures identify the exact exception.
articles, conjunctions, prepositions :: TestTree
articles = exceptionTests "Articles" (map unArticle Titlecase.articles)
conjunctions = exceptionTests "Conjunctions" (map unConjunction Titlecase.conjunctions)
-- Hyphenated entries are governed by component rules, tested in issue5.
prepositions = exceptionTests "Prepositions" (filter (notElem '-') (map unPreposition Titlecase.prepositions))
exceptionTests :: String -> [String] -> TestTree
exceptionTests label phrases = testGroup label
[ testGroup phrase
[ testCase "first" $
titlecase (phrase ++ " example") @?= capitalize phrase ++ " Example"
, testCase "last" $
titlecase ("example " ++ phrase) @?= "Example " ++ capitalizeLast phrase
, testCase "internal" $
titlecase ("example " ++ phrase ++ " example") @?= "Example " ++ phrase ++ " Example"
, testCase "whole title" $
titlecase phrase @?= capitalize (capitalizeLast phrase)
]
| phrase <- phrases
]
where
capitalize "" = ""
capitalize (c:cs) = toUpper c : cs
capitalizeLast = unwords . reverse . firstUpper . reverse . words
firstUpper [] = []
firstUpper (w:ws) = capitalize w : ws
examples :: TestTree
examples = testGroup "Examples"
[ testCase label $ titlecase input @?= expected
| (label, input, expected) <-
[ ("ordinary words and exceptions", "a journey through the world", "A Journey through the World")
, ("single article", "the", "The")
, ("single ordinary word", "hello", "Hello")
, ("preserve existing case", "an iPhone and NASA", "An IPhone and NASA")
, ("Unicode initials", "éclair and über", "Éclair and Über")
, ("digits", "123 stories", "123 Stories")
, ("punctuation preserved", "hello, world!", "Hello, World!")
, ("overlapping prepositions", "in front of the house", "In front of the House")
]
]