TRAVIS_OS_NAME=osx
<<<<<< ENV
./codecov.yml
.github/CODEOWNERS
LICENSE
Makefile
Package.swift
Sources/MarkdownGenerator/Extensions.swift
Sources/MarkdownGenerator/MarkdownBlockquotes.swift
Sources/MarkdownGenerator/MarkdownCodeBlock.swift
Sources/MarkdownGenerator/MarkdownCollapsibleSection.swift
Sources/MarkdownGenerator/MarkdownConvertible.swift
Sources/MarkdownGenerator/MarkdownFile.swift
Sources/MarkdownGenerator/MarkdownHeader.swift
Sources/MarkdownGenerator/MarkdownImage.swift
Sources/MarkdownGenerator/MarkdownLink.swift
Sources/MarkdownGenerator/MarkdownList.swift
Sources/MarkdownGenerator/MarkdownTable.swift
Tests/LinuxMain.swift
Tests/MarkdownGeneratorTests/Internal/MarkdownTableInternalTests.swift
Tests/MarkdownGeneratorTests/PublicInterface/ConsecutiveBlankLinesTests.swift
Tests/MarkdownGeneratorTests/PublicInterface/ExtensionsTests.swift
Tests/MarkdownGeneratorTests/PublicInterface/MarkdownBlockquotesTests.swift
Tests/MarkdownGeneratorTests/PublicInterface/MarkdownCodeBlockTests.swift
Tests/MarkdownGeneratorTests/PublicInterface/MarkdownCollapsibleSectionTests.swift
Tests/MarkdownGeneratorTests/PublicInterface/MarkdownConvertibleTests.swift
Tests/MarkdownGeneratorTests/PublicInterface/MarkdownFileTests.swift
Tests/MarkdownGeneratorTests/PublicInterface/MarkdownHeaderTests.swift
Tests/MarkdownGeneratorTests/PublicInterface/MarkdownImageTests.swift
Tests/MarkdownGeneratorTests/PublicInterface/MarkdownLinkTests.swift
Tests/MarkdownGeneratorTests/PublicInterface/MarkdownListTests.swift
Tests/MarkdownGeneratorTests/PublicInterface/MarkdownTableTests.swift
docs/PackageModules.dot
<<<<<< network
# path=./MarkdownGenerator.framework.coverage.txt
/Users/travis/build/eneko/MarkdownGenerator/Sources/MarkdownGenerator/Extensions.swift:
1| |//
2| |// Extensions.swift
3| |// MarkdownGenerator
4| |//
5| |// Created by Eneko Alonso on 10/8/17.
6| |//
7| |
8| |import Foundation
9| |
10| |// MARK: - String: MarkdownConvertible
11| |
12| |extension String: MarkdownConvertible {
13| |
14| | /// Render a string of text as Markdown. No transformations applied.
15| 84| public var markdown: String {
16| 84| return self
17| 84| }
18| |
19| | /// Remove consecutive blank lines from a string output
20| 12| public var removingConsecutiveBlankLines: String {
21| 12| let lines = components(separatedBy: .newlines)
22| 44| let filtered: [(offset: Int, element: String)] = lines.enumerated().filter { line in
23| 44| return line.offset == 0 ||
24| 44| lines[line.offset].trimmingCharacters(in: .whitespaces).isEmpty == false ||
25| 44| lines[line.offset - 1].trimmingCharacters(in: .whitespaces).isEmpty == false
26| 44| }
27| 35| return filtered.map { $0.element }.joined(separator: String.newLine)
28| 12| }
29| |
30| | static let newLine = "\n"
31| |}
32| |
33| |extension Array: MarkdownConvertible {
34| | /// Render a collection of Markdown convertible elements.
35| | ///
36| | /// Elements are rendered separated by one blank line, to prevent formatting interference.
37| 4| public var markdown: String {
38| 12| return compactMap { ($0 as? MarkdownConvertible)?.markdown }.joined(separator: "\n\n")
39| 4| }
40| |}
/Users/travis/build/eneko/MarkdownGenerator/Sources/MarkdownGenerator/MarkdownBlockquotes.swift:
1| |//
2| |// MarkdownBlockquotes.swift
3| |// MarkdownGenerator
4| |//
5| |// Created by Eneko Alonso on 10/8/17.
6| |//
7| |
8| |import Foundation
9| |
10| |/// Render Markdown Blockquotes
11| |///
12| |/// Markdown uses email-style > characters for blockquoting.
13| |///
14| |/// > This is a blockquote with two paragraphs. Lorem ipsum dolor sit amet,
15| |/// > consectetuer adipiscing elit. Aliquam hendrerit mi posuere lectus.
16| |/// > Vestibulum enim wisi, viverra nec, fringilla in, laoreet vitae, risus.
17| |/// >
18| |/// > Donec sit amet nisl. Aliquam semper ipsum sit amet velit. Suspendisse
19| |/// > id sem consectetuer libero luctus adipiscing.
20| |///
21| |public struct MarkdownBlockquotes: MarkdownConvertible {
22| |
23| | let content: MarkdownConvertible
24| |
25| | /// MarkdownBlockquotes initializer
26| | ///
27| | /// - Parameter content: Content to be block-quoted with '> '
28| 1| public init(content: MarkdownConvertible) {
29| 1| self.content = content
30| 1| }
31| |
32| | /// Generated Markdown output
33| 1| public var markdown: String {
34| 1| return content.blockquoted.markdown
35| 1| }
36| |}
37| |
38| |// MARK: - MarkdownBlockquote
39| |extension MarkdownConvertible {
40| | /// Quoted version of the generated Markdown output of the current entity.
41| | ///
42| | /// "## H2 Header".blockquoted // > ## H2 Header
43| | ///
44| 10| public var blockquoted: MarkdownConvertible {
45| 10| let input = markdown
46| 10| if input.isEmpty {
47| 1| return ""
48| 9| }
49| 9| let lines = markdown.components(separatedBy: String.newLine)
50| 75| let quoted: [String] = lines.map { "> \($0)".trimmingCharacters(in: .whitespaces) }
51| 9| return quoted.joined(separator: String.newLine)
52| 10| }
53| |}
/Users/travis/build/eneko/MarkdownGenerator/Sources/MarkdownGenerator/MarkdownCodeBlock.swift:
1| |//
2| |// MarkdownCodeBlock.swift
3| |// MarkdownGenerator
4| |//
5| |// Created by Eneko Alonso on 10/18/17.
6| |//
7| |
8| |import Foundation
9| |
10| |public struct MarkdownCodeBlock: MarkdownConvertible {
11| | let code: String
12| | let style: CodeBlockStyle
13| |
14| 5| public init(code: String, style: CodeBlockStyle = .indented) {
15| 5| self.code = code
16| 5| self.style = style
17| 5| }
18| |
19| | /// Generated Markdown output
20| 5| public var markdown: String {
21| 5| switch style {
22| 5| case .indented:
23| 2| let lines = code.components(separatedBy: .newlines)
24| 7| let indented: [String] = lines.map { $0.isEmpty ? "" : " \($0)" }
25| 2| return indented.joined(separator: String.newLine)
26| 5| case .backticks(let language):
27| 3| return """
28| 3| ```\(language)
29| 3| \(code)
30| 3| ```
31| 5| """
32| 5| }
33| 5| }
34| |
35| |}
36| |
37| |/// Markdown format for the generated code block
38| |///
39| |/// - indented: Code block is indented by 4-spaces
40| |/// - backticks: Code block is wrapped with ```
41| |public enum CodeBlockStyle {
42| | case indented
43| | case backticks(language: String)
44| |}
/Users/travis/build/eneko/MarkdownGenerator/Sources/MarkdownGenerator/MarkdownCollapsibleSection.swift:
1| |//
2| |// MarkdownCollapsibleSection.swift
3| |// MarkdownGenerator
4| |//
5| |// Created by Eneko Alonso on 10/18/17.
6| |//
7| |
8| |import Foundation
9| |
10| |/// Collapsible blocks are a great way to hide large portions of content
11| |/// that, while valuable to the reader, would result on a lot of noise.
12| |///
13| |/// Collapsible blocks work well in most Markdown readers, including
14| |/// GitHub.com and rendered GitHub Pages. However, because they rely on
15| |/// HTML tags, they will make the _raw_ markdown output harder to read.
16| |///
17| |/// ```html
18| |/// Block Title
19| |///
20| |/// Block details.
21| |///
22| |///
23| |/// ```
24| |public struct MarkdownCollapsibleSection: MarkdownConvertible {
25| | let summary: String
26| | let details: MarkdownConvertible
27| |
28| | /// MarkdownCollapsibleSection initializer
29| | ///
30| | /// - Parameters:
31| | /// - summary: Plain text or HTML string containing the block title.
32| | /// - details: Markdown convertible elements to include in the collapsible block.
33| 2| public init(summary: String, details: MarkdownConvertible) {
34| 2| self.summary = summary
35| 2| self.details = details
36| 2| }
37| |
38| | /// Generated Markdown output
39| 2| public var markdown: String {
40| 2| return """
41| 2| \(summary)
42| 2|
43| 2| \(details.markdown)
44| 2|
45| 2|
46| 2| """
47| 2| }
48| |
49| |}
/Users/travis/build/eneko/MarkdownGenerator/Sources/MarkdownGenerator/MarkdownFile.swift:
1| |//
2| |// MarkdownFile.swift
3| |// MarkdownGenerator
4| |//
5| |// Created by Eneko Alonso on 10/8/17.
6| |//
7| |
8| |import Foundation
9| |
10| |/// Helper structure to write Markdown files to disk.
11| |public struct MarkdownFile {
12| |
13| | /// Name of the Markdown file, without extension.
14| | public let filename: String
15| |
16| | /// Path where the Markdown file will be written to.
17| | ///
18| | /// Path can be absolute or relative to the working directory. It should
19| | /// not contain a trailing slash, nor the name of the file to write.
20| | ///
21| | /// Path will be created if it doesn't already exist in the system.
22| | public let basePath: String
23| |
24| | /// MarkdownConvertible entity that will be rendered
25| | /// as the Markdown content of the file. Can be an `Array`.
26| | public var content: MarkdownConvertible
27| |
28| | /// MarkdownFile initializer
29| | ///
30| | /// - Parameters:
31| | /// - filename: Name of the Markdown file, without extension.
32| | /// - basePath: Path where the Markdown file will be written to.
33| | ///
34| | /// Path can be absolute or relative to the working directory. It should
35| | /// not contain a trailing slash, nor the name of the file to write.
36| | ///
37| | /// Path will be created if it doesn't already exist in the system.
38| | ///
39| | /// - content: MarkdownConvertible entity that will be rendered
40| | /// as the Markdown content of the file. Can be an `Array`.
41| 3| public init(filename: String, basePath: String = "", content: MarkdownConvertible) {
42| 3| self.filename = filename.trimmingCharacters(in: .whitespacesAndNewlines)
43| 3| self.basePath = basePath.trimmingCharacters(in: .whitespacesAndNewlines)
44| 3| self.content = content
45| 3| }
46| |
47| | /// Computed property containing the file path (`/.md`)
48| 3| public var filePath: String {
49| 3| return basePath.isEmpty ? "\(filename).md" : "\(basePath)/\(filename).md"
50| 3| }
51| |
52| | /// Generate and write the Markdown file to disk.
53| | ///
54| | /// - Will override the file if already existing, or create a new one.
55| | /// - Will create the path directory structure if it does not exists.
56| | ///
57| | /// - Throws: Throws an exception if the file could not be written to disk, or
58| | /// if the path could not be created.
59| 2| public func write() throws {
60| 2| try createDirectory(path: basePath)
61| 2| let output = content.markdown.removingConsecutiveBlankLines
62| 2| try output.write(toFile: filePath, atomically: true, encoding: .utf8)
63| 2| }
64| |
65| 2| private func createDirectory(path: String) throws {
66| 2| guard path.isEmpty == false else {
67| 1| return
68| 1| }
69| 1| var isDir: ObjCBool = false
70| 1| if FileManager.default.fileExists(atPath: path, isDirectory: &isDir) == false {
71| 1| try FileManager.default.createDirectory(atPath: path, withIntermediateDirectories: true, attributes: nil)
72| 1| }
73| 1| }
74| |}
/Users/travis/build/eneko/MarkdownGenerator/Sources/MarkdownGenerator/MarkdownHeader.swift:
1| |//
2| |// MarkdownHeader.swift
3| |// MarkdownGenerator
4| |//
5| |// Created by Eneko Alonso on 10/8/17.
6| |//
7| |
8| |import Foundation
9| |
10| |/// Renders a Markdown Header
11| |///
12| |/// Markdown supports two styles of headers, Setext and atx.
13| |///
14| |/// Setext-style headers are βunderlinedβ using equal signs (for first-level headers)
15| |/// and dashes (for second-level headers). For example:
16| |///
17| |/// This is an H1
18| |/// =============
19| |///
20| |/// This is an H2
21| |/// -------------
22| |///
23| |/// Atx-style headers use 1-6 hash characters at the start of the line, corresponding
24| |/// to HTML header levels 1-6. For example:
25| |///
26| |/// # This is an H1
27| |///
28| |/// ## This is an H2
29| |///
30| |/// ###### This is an H6
31| |///
32| |/// Atx-style headers can be closed (this is purely cosmetic):
33| |///
34| |/// # This is an H1 #
35| |///
36| |/// ## This is an H2 ##
37| |///
38| |/// ### This is an H3 ###
39| |///
40| |public struct MarkdownHeader: MarkdownConvertible {
41| | let title: String
42| | let level: MarkdownHeaderLevel
43| | let style: MarkdownHeaderStyle
44| | let close: Bool
45| |
46| | /// MarkdownHeader initializer.
47| | ///
48| | /// - Parameters:
49| | /// - title: Title of the header element
50| | /// - level: Header level (`h1`, `h2`... `h6`)
51| | /// - style: Header style: `setex` (underlined) or `atx` ('#') (defaults to `atx`).
52| | /// Setex format is only available for first-level (using equal signs) and
53| | /// second-level headers (using dashes).
54| | /// - close: Close `atx` style headers (defaults to `false`). When false, headers
55| | /// only include the '#' prefix. When `true`, headers also include the
56| | /// trailing '#' suffix:
57| | ///
58| | /// ### Third-level Header ###
59| | ///
60| | /// - SeeAlso: MarkdownHeaderLevel, MarkdownHeaderStyle
61| | public init(title: String, level: MarkdownHeaderLevel = .h1, style: MarkdownHeaderStyle = .atx,
62| 29| close: Bool = false) {
63| 29| self.title = title
64| 29| self.level = level
65| 29| self.style = level.rawValue < 3 ? style : .atx
66| 29| self.close = close
67| 29| }
68| |
69| | /// Generated Markdown output
70| 31| public var markdown: String {
71| 31| switch style {
72| 31| case .atx:
73| 27| let prefix = Array(repeating: "#", count: level.rawValue).joined()
74| 27| let suffix = close ? prefix : ""
75| 27| return "\(prefix) \(title) \(suffix)".trimmingCharacters(in: .whitespacesAndNewlines)
76| 31| case .setex:
77| 4| let symbol = level == .h1 ? "=" : "-"
78| 4| let underline = Array(repeating: symbol, count: title.count).joined()
79| 4| return """
80| 4| \(title)
81| 4| \(underline)
82| 4| """
83| 31| }
84| 31| }
85| 4|}
86| 4|
87| 4|/// Markdown Header level
88| 4|///
89| 4|/// - h1: first-level headers
90| 4|///
91| 4|/// # H1 Header
92| 4|///
93| 4|/// - h2: second-level headers
94| 4|///
95| 4|/// ## H2 Header
96| 4|///
97| 4|/// - h3: third-level headers
98| 4|///
99| 4|/// ### H3 Header
100| 4|///
101| 4|/// - h4: fourth-level headers
102| 4|///
103| 4|/// #### H4 Header
104| 4|///
105| 4|/// - h5: fifth-level headers
106| 4|///
107| 4|/// ##### H5 Header
108| 4|///
109| 4|/// - h6: sixth-level headers
110| 4|///
111| 4|/// ###### H6 Header
112| 4|///
113| 4|public enum MarkdownHeaderLevel: Int {
114| 4| case h1 = 1
115| 4| case h2
116| 4| case h3
117| 4| case h4
118| 4| case h5
119| 4| case h6
120| 4|}
121| 4|
122| 4|/// Markdown Header render style
123| 4|///
124| 4|/// - `setex`: Setext-style headers are βunderlinedβ using equal signs (for first-level headers)
125| 4|/// and dashes (for second-level headers). For example:
126| 4|///
127| 4|/// This is a Setex H1 Header
128| 4|/// =========================
129| 4|///
130| 4|/// This is a Setex H2 Header
131| 4|/// -------------------------
132| 4|///
133| 4|/// - `atx`: Atx-style headers use 1-6 hash characters at the start of the line, corresponding
134| 4|/// to header levels 1-6. For example:
135| 4|///
136| 4|/// # This is an Atx H1 Header
137| 4|///
138| 4|/// ## This is an Atx H2 Header
139| 4|///
140| 4|/// ###### This is a closed Atx H6 Header ######
141| 4|///
142| 4|public enum MarkdownHeaderStyle {
143| 4| case setex
144| 4| case atx
145| |}
/Users/travis/build/eneko/MarkdownGenerator/Sources/MarkdownGenerator/MarkdownImage.swift:
1| |//
2| |// MarkdownImage.swift
3| |// MarkdownGenerator
4| |//
5| |// Created by Eneko Alonso on 10/9/17.
6| |//
7| |
8| |import Foundation
9| |
10| |/// Render an HTML image in Markdown format
11| |///
12| |/// MarkdownImage(url: "http://example.com/image.jpg", altText: "SourceDocs Header").markdown
13| |///
14| |/// Would render as:
15| |///
16| |/// ![SourceDocs Header](http://example.com/image.jpg)
17| |///
18| |public struct MarkdownImage: MarkdownConvertible {
19| |
20| | /// URL where the image is located. Can be absolute or relative.
21| | public let url: String
22| |
23| | /// Alternate text to display on non-graphic browsers or to be used by screen readers.
24| | public let altText: String
25| |
26| | /// MarkdownImage initializer
27| | ///
28| | /// - Parameters:
29| | /// - url: URL where the image is located. Can be absolute or relative.
30| | /// - altText: Alternate text to display on non-graphic browsers or to be used by screen readers.
31| 2| public init(url: String, altText: String = "") {
32| 2| self.url = url
33| 2| self.altText = altText
34| 2| }
35| |
36| | /// Generated Markdown output
37| 2| public var markdown: String {
38| 2| return "![\(altText)](\(url))"
39| 2| }
40| |}
/Users/travis/build/eneko/MarkdownGenerator/Sources/MarkdownGenerator/MarkdownLink.swift:
1| |//
2| |// MarkdownLink.swift
3| |// MarkdownGenerator
4| |//
5| |// Created by Eneko Alonso on 10/9/17.
6| |//
7| |
8| |import Foundation
9| |
10| |/// Render an HTML link in Markdown format
11| |///
12| |/// MarkdownLink(text: "Google", url: "https://example.com").markdown
13| |///
14| |/// Would render as:
15| |///
16| |/// [Google](https://example.com)
17| |///
18| |public struct MarkdownLink: MarkdownConvertible {
19| |
20| | /// Text to display as hyper-linked.
21| | public let text: String
22| |
23| | /// Link URL, can be absolute, relative, or #local.
24| | public let url: String
25| |
26| | /// MarkdownLink initializer
27| | ///
28| | /// - Parameters:
29| | /// - text: Text to display as hyper-linked.
30| | /// - url: Link URL, can be absolute, relative, or #local.
31| 1| public init(text: String, url: String) {
32| 1| self.text = text
33| 1| self.url = url
34| 1| }
35| |
36| | /// Generated Markdown output
37| 1| public var markdown: String {
38| 1| return "[\(text)](\(url))"
39| 1| }
40| |}
/Users/travis/build/eneko/MarkdownGenerator/Sources/MarkdownGenerator/MarkdownList.swift:
1| |//
2| |// MarkdownList.swift
3| |// MarkdownGenerator
4| |//
5| |// Created by Eneko Alonso on 10/9/17.
6| |//
7| |
8| |import Foundation
9| |
10| |/// Render a list of elements in Markdown format
11| |///
12| |/// Unordered lists:
13| |///
14| |/// - One item
15| |/// - Another item that expands to multiple
16| |/// lines.
17| |/// - A nested item.
18| |/// - Third-level.
19| |/// - Back to first-level.
20| |///
21| |/// Ordered lists:
22| |///
23| |/// 1. First item
24| |/// 1. Second item
25| |/// 1. Nested item (second-level)
26| |/// 1. Back to first-level
27| |///
28| |public struct MarkdownList: MarkdownConvertible {
29| | let style: MarkdownListStyle
30| |
31| | /// List of items to be converted to a list.
32| | public var items: [MarkdownConvertible]
33| |
34| | /// MarkdownList initializer
35| | ///
36| | /// - Parameter items: List of items to be converted to a list.
37| 16| public init(items: [MarkdownConvertible], style: MarkdownListStyle = .unordered) {
38| 16| self.items = items
39| 16| self.style = style
40| 16| }
41| |
42| | /// Generated Markdown output
43| 16| public var markdown: String {
44| 55| return items.map { formatted(item: $0) }.joined(separator: String.newLine)
45| 16| }
46| |
47| | // Per Markdown documentation, indent all lines using 4 spaces.
48| 55| private func formatted(item: MarkdownConvertible) -> String {
49| 55| let symbol = style == .ordered ? "1. " : "- "
50| 55| var lines = item.markdown.components(separatedBy: String.newLine)
51| 55| let first = lines.removeFirst()
52| 55| let firstLine = item is MarkdownList ? " \(first)" : "\(symbol)\(first)"
53| 55|
54| 55| if lines.isEmpty {
55| 45| return firstLine
56| 45| }
57| 10|
58| 10| let blankLine = item is MarkdownList ? "" : String.newLine
59| 39| let indentedLines = lines.map { $0.isEmpty ? "" : " \($0)" }.joined(separator: String.newLine) + blankLine
60| 10| return """
61| 10| \(firstLine)
62| 10| \(indentedLines)
63| 10| """
64| 55| }
65| |
66| |}
67| |
68| |/// Specifies the type of list to be generated
69| |///
70| |/// - ordered: Ordered lists use numbers followed by periods.
71| |/// - unordered: Unordered lists use hyphens as list markers.
72| |public enum MarkdownListStyle {
73| | case ordered
74| | case unordered
75| |}
/Users/travis/build/eneko/MarkdownGenerator/Sources/MarkdownGenerator/MarkdownTable.swift:
1| |//
2| |// MarkdownTable.swift
3| |// MarkdownGenerator
4| |//
5| |// Created by Eneko Alonso on 10/9/17.
6| |//
7| |
8| |import Foundation
9| |
10| |/// Render a two dimensional Markdown table.
11| |///
12| |/// | | Name | Department |
13| |/// | -- | ------ | ---------- |
14| |/// | π | Apple | Fruits |
15| |/// | π | Orange | Fruits |
16| |/// | π₯ | Bread | Bakery |
17| |///
18| |/// *Notes*:
19| |/// - Markdown tables are not supported by all Markdown readers.
20| |/// - Table headers are required.
21| |/// - Table cells cannot contain multiple lines. New line characters are replaced by a space.
22| |public struct MarkdownTable: MarkdownConvertible {
23| |
24| | let headers: [String]
25| | let data: [[String]]
26| |
27| | /// MarkdownTable initializer
28| | ///
29| | /// - Parameters:
30| | /// - headers: List of table header titles.
31| | /// - data: Two-dimensional `String` array with the table content. Rows are defined by
32| | /// the outer array, columns are defined by the inner arrays.
33| | ///
34| | /// An array of rows, each row containing an array of columns. All rows should contain the same
35| | /// number of columns as the headers array, to avoid formatting issues.
36| 8| public init(headers: [String], data: [[String]]) {
37| 9| self.headers = headers.map { $0.isEmpty ? " " : $0 }
38| 8| self.data = data
39| 8| }
40| |
41| | /// Generated Markdown output
42| 6| public var markdown: String {
43| 6| let columnWidths = computeColumnWidths()
44| 6| if columnWidths.isEmpty {
45| 1| return .newLine
46| 5| }
47| 5| let headerRow = makeRow(values: pad(values: headers, lengths: columnWidths))
48| 10| let separatorRow = makeRow(values: columnWidths.map { String(repeating: "-", count: $0) })
49| 12| let dataRows = data.map { columns in
50| 12| return makeRow(values: pad(values: columns, lengths: columnWidths))
51| 12| }
52| 5| return """
53| 5| \(headerRow)
54| 5| \(separatorRow)
55| 5| \(dataRows.joined(separator: String.newLine))
56| 5| """
57| 6| }
58| |
59| | /// Return max length for each column, counting individual UTF16 characters for better emoji support.
60| | ///
61| | /// - Returns: Array of column widths
62| 7| func computeColumnWidths() -> [Int] {
63| 7| let rows = [headers] + data
64| 19| guard let maxColumns = rows.map({ $0.count }).max(), maxColumns > 0 else {
65| 2| return []
66| 5| }
67| 10| let columnWidths = (0.. Int in
68| 36| return columnLength(values: rows.compactMap({ $0.get(at: columnIndex) }))
69| 10| }
70| 5| return columnWidths
71| 7| }
72| |
73| 11| func columnLength(values: [String]) -> Int {
74| 31| return values.map({ $0.utf16.count }).max() ?? 0
75| 11| }
76| |
77| | /// Pad array of strings to a given length, counting individual UTF16 characters
78| | ///
79| | /// - Parameters:
80| | /// - values: array of strings to pad
81| | /// - lengths: desired lengths
82| | /// - Returns: array of right-padded strings
83| 17| func pad(values: [String], lengths: [Int]) -> [String] {
84| 17| var values = values
85| 22| while values.count < lengths.count {
86| 5| values.append("")
87| 17| }
88| 36| return zip(values, lengths).map { value, length in
89| 36| value + String(repeating: " ", count: max(0, length - value.utf16.count))
90| 36| }
91| 17| }
92| |
93| | /// Convert a String array into a markdown formatter table row.
94| | /// Table cells cannot contain multiple lines. New line characters are replaced by a space.
95| | ///
96| | /// - Parameter values: array of values
97| | /// - Returns: Markdown formatted row
98| 22| func makeRow(values: [String]) -> String {
99| 46| let values = values.map { $0.replacingOccurrences(of: String.newLine, with: " ") }
100| 22| return "| " + values.joined(separator: " | ") + " |"
101| 22| }
102| |}
103| |
104| |extension Array {
105| 36| func get(at index: Int) -> Element? {
106| 36| guard (0.. This is a quote.
24| | """
25| |
26| | let output = """
27| | > ## This is a header.
28| | >
29| | > 1. This is the first list item.
30| | > 2. This is the second list item.
31| | >
32| | > Here's some example code:
33| | >
34| | > return shell_exec("echo $input | $markdown_script");
35| | >
36| | > > This is a quote.
37| | """
38| |
39| 1| func testBlockquotes() {
40| 1| XCTAssertEqual(MarkdownBlockquotes(content: input).markdown, output)
41| 1| }
42| |
43| 1| func testBlockquoted() {
44| 1| XCTAssertEqual(input.blockquoted.markdown, output)
45| 1| }
46| |
47| 1| func testNested() {
48| 1| XCTAssertEqual(input.blockquoted.blockquoted.markdown, output.blockquoted.markdown)
49| 1| }
50| |
51| 1| func testMultipleInputs() {
52| 1| XCTAssertEqual([input, input].blockquoted.markdown, [output, output].joined(separator: "\n>\n"))
53| 1| }
54| |
55| 1| func testSingleLine() {
56| 1| let input = """
57| 1| Single line of text.
58| 1| """
59| 1| XCTAssertEqual(input.blockquoted.markdown, "> Single line of text.")
60| 1| }
61| |
62| 1| func testWhitespaces() {
63| 1| XCTAssertEqual("".blockquoted.markdown, "")
64| 1| XCTAssertEqual("\n".blockquoted.markdown, ">\n>")
65| 1| XCTAssertEqual("\t".blockquoted.markdown, ">")
66| 1| }
67| |
68| | static var allTests = [
69| | ("testBlockquotes", testBlockquotes),
70| | ("testBlockquoted", testBlockquoted),
71| | ("testNested", testNested),
72| | ("testMultipleInputs", testMultipleInputs),
73| | ("testSingleLine", testSingleLine),
74| | ("testWhitespaces", testWhitespaces)
75| | ]
76| |
77| |}
/Users/travis/build/eneko/MarkdownGenerator/Tests/MarkdownGeneratorTests/PublicInterface/MarkdownCodeBlockTests.swift:
1| |//
2| |// MarkdownCodeBlockTests.swift
3| |// MarkdownGeneratorTests
4| |//
5| |// Created by Eneko Alonso on 10/18/17.
6| |//
7| |
8| |import XCTest
9| |import MarkdownGenerator
10| |
11| |class MarkdownCodeBlockTests: XCTestCase {
12| |
13| 1| func testEmpty() {
14| 1| let input = """
15| 1| """
16| 1| let output = """
17| 1| """
18| 1| XCTAssertEqual(MarkdownCodeBlock(code: input).markdown, output)
19| 1| }
20| |
21| 1| func testIndented() {
22| 1| let input = """
23| 1| /// Defines an entity that can be represented as Markdown.
24| 1| public protocol MarkdownConvertible {
25| 1|
26| 1| /// Generated Markdown output representing the current entity.
27| 1| var markdown: String { get }
28| 1| }
29| 1| """
30| 1| let output = """
31| 1| /// Defines an entity that can be represented as Markdown.
32| 1| public protocol MarkdownConvertible {
33| 1|
34| 1| /// Generated Markdown output representing the current entity.
35| 1| var markdown: String { get }
36| 1| }
37| 1| """
38| 1| XCTAssertEqual(MarkdownCodeBlock(code: input).markdown, output)
39| 1| }
40| |
41| 1| func testBackticks() {
42| 1| let input = """
43| 1| /// Defines an entity that can be represented as Markdown.
44| 1| public protocol MarkdownConvertible {
45| 1|
46| 1| /// Generated Markdown output representing the current entity.
47| 1| var markdown: String { get }
48| 1| }
49| 1| """
50| 1| let output = """
51| 1| ```
52| 1| /// Defines an entity that can be represented as Markdown.
53| 1| public protocol MarkdownConvertible {
54| 1|
55| 1| /// Generated Markdown output representing the current entity.
56| 1| var markdown: String { get }
57| 1| }
58| 1| ```
59| 1| """
60| 1| XCTAssertEqual(MarkdownCodeBlock(code: input, style: .backticks(language: "")).markdown, output)
61| 1| }
62| |
63| 1| func testBackticksSwift() {
64| 1| let input = """
65| 1| /// Defines an entity that can be represented as Markdown.
66| 1| public protocol MarkdownConvertible {
67| 1|
68| 1| /// Generated Markdown output representing the current entity.
69| 1| var markdown: String { get }
70| 1| }
71| 1| """
72| 1| let output = """
73| 1| ```swift
74| 1| /// Defines an entity that can be represented as Markdown.
75| 1| public protocol MarkdownConvertible {
76| 1|
77| 1| /// Generated Markdown output representing the current entity.
78| 1| var markdown: String { get }
79| 1| }
80| 1| ```
81| 1| """
82| 1| XCTAssertEqual(MarkdownCodeBlock(code: input, style: .backticks(language: "swift")).markdown, output)
83| 1| }
84| |
85| |}
/Users/travis/build/eneko/MarkdownGenerator/Tests/MarkdownGeneratorTests/PublicInterface/MarkdownCollapsibleSectionTests.swift:
1| |//
2| |// MarkdownCollapsibleSectionTests.swift
3| |// MarkdownGeneratorTests
4| |//
5| |// Created by Eneko Alonso on 10/18/17.
6| |//
7| |
8| |import XCTest
9| |import MarkdownGenerator
10| |
11| |class MarkdownCollapsibleSectionTests: XCTestCase {
12| |
13| 1| func testSimple() {
14| 1| let output = """
15| 1| Hello
16| 1|
17| 1| World
18| 1|
19| 1|
20| 1| """
21| 1| XCTAssertEqual(MarkdownCollapsibleSection(summary: "Hello", details: "World").markdown, output)
22| 1| }
23| |
24| 1| func testComplex() {
25| 1| let output = """
26| 1| This is cool stuff
27| 1|
28| 1| # Title
29| 1|
30| 1| - πΆ
31| 1| - π±
32| 1| - π¦
33| 1|
34| 1| | Name | Count |
35| 1| | ---- | ----- |
36| 1| | Dog | 1 |
37| 1| | Cat | 2 |
38| 1|
39| 1| ```swift
40| 1| let foo = Bar()
41| 1| ```
42| 1|
43| 1|
44| 1| """
45| 1|
46| 1| let details: [MarkdownConvertible] = [
47| 1| MarkdownHeader(title: "Title"),
48| 1| MarkdownList(items: ["πΆ", "π±", "π¦"]),
49| 1| MarkdownTable(headers: ["Name", "Count"], data: [["Dog", "1"], ["Cat", "2"]]),
50| 1| MarkdownCodeBlock(code: "let foo = Bar()", style: .backticks(language: "swift"))
51| 1| ]
52| 1| XCTAssertEqual(MarkdownCollapsibleSection(summary: "This is cool stuff", details: details).markdown, output)
53| 1| }
54| |
55| |}
/Users/travis/build/eneko/MarkdownGenerator/Tests/MarkdownGeneratorTests/PublicInterface/MarkdownConvertibleTests.swift:
1| |//
2| |// MarkdownConvertibleTests.swift
3| |// MarkdownGeneratorTests
4| |//
5| |// Created by Eneko Alonso on 10/9/17.
6| |//
7| |
8| |import XCTest
9| |import MarkdownGenerator
10| |
11| |class MarkdownConvertibleTests: XCTestCase {
12| |
13| 1| func testCustomStructureAdoptsProtocol() {
14| 1| XCTAssertEqual(Contact(firstName: "Bob", lastName: "Jones").markdown, "**Jones**, Bob")
15| 1| }
16| |
17| | static var allTests = [
18| | ("testCustomStructureAdoptsProtocol", testCustomStructureAdoptsProtocol)
19| | ]
20| |
21| |}
22| |
23| |// Custom structure that can be rendered as Markdown
24| |struct Contact: MarkdownConvertible {
25| | let firstName: String
26| | let lastName: String
27| |
28| 1| var markdown: String {
29| 1| return "**\(lastName)**, \(firstName)"
30| 1| }
31| |}
/Users/travis/build/eneko/MarkdownGenerator/Tests/MarkdownGeneratorTests/PublicInterface/MarkdownFileTests.swift:
1| |//
2| |// MarkdownFileTests.swift
3| |// MarkdownGeneratorTests
4| |//
5| |// Created by Eneko Alonso on 10/9/17.
6| |//
7| |
8| |import XCTest
9| |import MarkdownGenerator
10| |
11| |class MarkdownFileTests: XCTestCase {
12| |
13| 1| func testFilePath() {
14| 1| let file = MarkdownFile(filename: "Test", basePath: "Path/To/File", content: "")
15| 1| XCTAssertEqual(file.filePath, "Path/To/File/Test.md")
16| 1| }
17| |
18| 1| func testFileWrite() throws {
19| 1| let content = MarkdownHeader(title: "Hello World!")
20| 1| let file = MarkdownFile(filename: "WriteTest", content: content)
21| 1| try file.write()
22| 1|
23| 1| let fileContent = try String(contentsOfFile: "WriteTest.md")
24| 1| XCTAssertEqual(fileContent, content.markdown)
25| 1| }
26| |
27| 1| func testFileWriteWithFolder() throws {
28| 1| let content = MarkdownHeader(title: "Hello World!")
29| 1| let file = MarkdownFile(filename: "WriteTest", basePath: "AFolder", content: content)
30| 1| try file.write()
31| 1|
32| 1| let fileContent = try String(contentsOfFile: "AFolder/WriteTest.md")
33| 1| XCTAssertEqual(fileContent, content.markdown)
34| 1| }
35| |
36| | static var allTests = [
37| | ("testFilePath", testFilePath)
38| | ]
39| |
40| |}
/Users/travis/build/eneko/MarkdownGenerator/Tests/MarkdownGeneratorTests/PublicInterface/MarkdownHeaderTests.swift:
1| |//
2| |// BockElementsTests.swift
3| |// MarkdownGeneratorTests
4| |//
5| |// Created by Eneko Alonso on 10/8/17.
6| |//
7| |
8| |import XCTest
9| |import MarkdownGenerator
10| |
11| |class MarkdownHeaderTests: XCTestCase {
12| |
13| 1| func testDefaultHeader() {
14| 1| XCTAssertEqual(MarkdownHeader(title: "Header Title").markdown, "# Header Title")
15| 1| }
16| |
17| 1| func testHeaderLevelsAtx() {
18| 1| XCTAssertEqual(MarkdownHeader(title: "Header Title", level: .h1).markdown, "# Header Title")
19| 1| XCTAssertEqual(MarkdownHeader(title: "Header Title", level: .h2).markdown, "## Header Title")
20| 1| XCTAssertEqual(MarkdownHeader(title: "Header Title", level: .h3).markdown, "### Header Title")
21| 1| XCTAssertEqual(MarkdownHeader(title: "Header Title", level: .h4).markdown, "#### Header Title")
22| 1| XCTAssertEqual(MarkdownHeader(title: "Header Title", level: .h5).markdown, "##### Header Title")
23| 1| XCTAssertEqual(MarkdownHeader(title: "Header Title", level: .h6).markdown, "###### Header Title")
24| 1| }
25| |
26| 1| func testHeaderLevelsSetex() {
27| 1| let h1 = """
28| 1| Header Title
29| 1| ============
30| 1| """
31| 1|
32| 1| let h2 = """
33| 1| Header Title
34| 1| ------------
35| 1| """
36| 1|
37| 1| let h3 = "### Header Title"
38| 1| let h4 = "#### Header Title"
39| 1| let h5 = "##### Header Title"
40| 1| let h6 = "###### Header Title"
41| 1|
42| 1| XCTAssertEqual(MarkdownHeader(title: "Header Title", level: .h1, style: .setex).markdown, h1)
43| 1| XCTAssertEqual(MarkdownHeader(title: "Header Title", level: .h2, style: .setex).markdown, h2)
44| 1| XCTAssertEqual(MarkdownHeader(title: "Header Title", level: .h3, style: .setex).markdown, h3)
45| 1| XCTAssertEqual(MarkdownHeader(title: "Header Title", level: .h4, style: .setex).markdown, h4)
46| 1| XCTAssertEqual(MarkdownHeader(title: "Header Title", level: .h5, style: .setex).markdown, h5)
47| 1| XCTAssertEqual(MarkdownHeader(title: "Header Title", level: .h6, style: .setex).markdown, h6)
48| 1| }
49| |
50| 1| func testHeaderLevelsAtxCosing() {
51| 1| let h1 = "# Header Title #"
52| 1| let h2 = "## Header Title ##"
53| 1| let h3 = "### Header Title ###"
54| 1| let h4 = "#### Header Title ####"
55| 1| let h5 = "##### Header Title #####"
56| 1| let h6 = "###### Header Title ######"
57| 1| XCTAssertEqual(MarkdownHeader(title: "Header Title", level: .h1, close: true).markdown, h1)
58| 1| XCTAssertEqual(MarkdownHeader(title: "Header Title", level: .h2, close: true).markdown, h2)
59| 1| XCTAssertEqual(MarkdownHeader(title: "Header Title", level: .h3, close: true).markdown, h3)
60| 1| XCTAssertEqual(MarkdownHeader(title: "Header Title", level: .h4, close: true).markdown, h4)
61| 1| XCTAssertEqual(MarkdownHeader(title: "Header Title", level: .h5, close: true).markdown, h5)
62| 1| XCTAssertEqual(MarkdownHeader(title: "Header Title", level: .h6, close: true).markdown, h6)
63| 1| }
64| |
65| 1| func testHeaderLevelsSetexClosing() {
66| 1| let h1 = """
67| 1| Header Title
68| 1| ============
69| 1| """
70| 1|
71| 1| let h2 = """
72| 1| Header Title
73| 1| ------------
74| 1| """
75| 1|
76| 1| let h3 = "### Header Title ###"
77| 1| let h4 = "#### Header Title ####"
78| 1| let h5 = "##### Header Title #####"
79| 1| let h6 = "###### Header Title ######"
80| 1|
81| 1| XCTAssertEqual(MarkdownHeader(title: "Header Title", level: .h1, style: .setex, close: true).markdown, h1)
82| 1| XCTAssertEqual(MarkdownHeader(title: "Header Title", level: .h2, style: .setex, close: true).markdown, h2)
83| 1| XCTAssertEqual(MarkdownHeader(title: "Header Title", level: .h3, style: .setex, close: true).markdown, h3)
84| 1| XCTAssertEqual(MarkdownHeader(title: "Header Title", level: .h4, style: .setex, close: true).markdown, h4)
85| 1| XCTAssertEqual(MarkdownHeader(title: "Header Title", level: .h5, style: .setex, close: true).markdown, h5)
86| 1| XCTAssertEqual(MarkdownHeader(title: "Header Title", level: .h6, style: .setex, close: true).markdown, h6)
87| 1| }
88| |
89| 1| func testCodeHeader() {
90| 1| XCTAssertEqual(MarkdownHeader(title: "`Header Title`").markdown, "# `Header Title`")
91| 1| }
92| |
93| | static var allTests = [
94| | ("testDefaultHeader", testDefaultHeader),
95| | ("testHeaderLevelsAtx", testHeaderLevelsAtx),
96| | ("testHeaderLevelsSetex", testHeaderLevelsSetex),
97| | ("testHeaderLevelsAtxCosing", testHeaderLevelsAtxCosing),
98| | ("testHeaderLevelsSetexClosing", testHeaderLevelsSetexClosing),
99| | ("testCodeHeader", testCodeHeader)
100| | ]
101| |
102| |}
/Users/travis/build/eneko/MarkdownGenerator/Tests/MarkdownGeneratorTests/PublicInterface/MarkdownImageTests.swift:
1| |//
2| |// MarkdownImageTests.swift
3| |// MarkdownGeneratorTests
4| |//
5| |// Created by Eneko Alonso on 10/9/17.
6| |//
7| |
8| |import XCTest
9| |import MarkdownGenerator
10| |
11| |class MarkdownImageTests: XCTestCase {
12| |
13| 1| func testImage() {
14| 1| XCTAssertEqual(MarkdownImage(url: "http://example.com/image.png").markdown, "![](http://example.com/image.png)")
15| 1| }
16| |
17| 1| func testImageAltText() {
18| 1| let image = MarkdownImage(url: "http://example.com/image.png", altText: "Alternate Text")
19| 1| XCTAssertEqual(image.markdown, "![Alternate Text](http://example.com/image.png)")
20| 1| }
21| |
22| | static var allTests = [
23| | ("testImage", testImage),
24| | ("testImageAltText", testImageAltText)
25| | ]
26| |
27| |}
/Users/travis/build/eneko/MarkdownGenerator/Tests/MarkdownGeneratorTests/PublicInterface/MarkdownLinkTests.swift:
1| |//
2| |// MarkdownLinkTests.swift
3| |// MarkdownGeneratorTests
4| |//
5| |// Created by Eneko Alonso on 10/9/17.
6| |//
7| |
8| |import XCTest
9| |import MarkdownGenerator
10| |
11| |class MarkdownLinkTests: XCTestCase {
12| |
13| 1| func testLink() {
14| 1| let link = MarkdownLink(text: "example.com", url: "http://example.com")
15| 1| XCTAssertEqual(link.markdown, "[example.com](http://example.com)")
16| 1| }
17| |
18| | static var allTests = [
19| | ("testLink", testLink)
20| | ]
21| |
22| |}
/Users/travis/build/eneko/MarkdownGenerator/Tests/MarkdownGeneratorTests/PublicInterface/MarkdownListTests.swift:
1| |//
2| |// MarkdownListTests.swift
3| |// MarkdownGeneratorTests
4| |//
5| |// Created by Eneko Alonso on 10/9/17.
6| |//
7| |
8| |import XCTest
9| |import MarkdownGenerator
10| |
11| |class MarkdownListTests: XCTestCase {
12| |
13| 1| func testUnorderedSimpleList() {
14| 1| let input = ["π", "π", "π", "π"]
15| 1|
16| 1| let output = """
17| 1| - π
18| 1| - π
19| 1| - π
20| 1| - π
21| 1| """
22| 1|
23| 1| XCTAssertEqual(MarkdownList(items: input).markdown, output)
24| 1| }
25| |
26| 1| func testOrderedSimpleList() {
27| 1| let input = ["π", "π", "π", "π"]
28| 1|
29| 1| let output = """
30| 1| 1. π
31| 1| 1. π
32| 1| 1. π
33| 1| 1. π
34| 1| """
35| 1|
36| 1| XCTAssertEqual(MarkdownList(items: input, style: .ordered).markdown, output)
37| 1| }
38| |
39| 1| func testUnorderedNestedList() {
40| 1| let input: [MarkdownConvertible] = [
41| 1| "Fruits",
42| 1| MarkdownList(items: ["π", "π", "π", "π"]),
43| 1| "Bakery",
44| 1| MarkdownList(items: ["π₯", "π", "π°", "π"])
45| 1| ]
46| 1|
47| 1| let output = """
48| 1| - Fruits
49| 1| - π
50| 1| - π
51| 1| - π
52| 1| - π
53| 1| - Bakery
54| 1| - π₯
55| 1| - π
56| 1| - π°
57| 1| - π
58| 1| """
59| 1|
60| 1| XCTAssertEqual(MarkdownList(items: input).markdown, output)
61| 1| }
62| |
63| 1| func testOrderedNestedList() {
64| 1| let input: [MarkdownConvertible] = [
65| 1| "Fruits",
66| 1| MarkdownList(items: ["π", "π", "π", "π"], style: .ordered),
67| 1| "Bakery",
68| 1| MarkdownList(items: ["π₯", "π", "π°", "π"], style: .ordered)
69| 1| ]
70| 1|
71| 1| let output = """
72| 1| 1. Fruits
73| 1| 1. π
74| 1| 1. π
75| 1| 1. π
76| 1| 1. π
77| 1| 1. Bakery
78| 1| 1. π₯
79| 1| 1. π
80| 1| 1. π°
81| 1| 1. π
82| 1| """
83| 1|
84| 1| XCTAssertEqual(MarkdownList(items: input, style: .ordered).markdown, output)
85| 1| }
86| |
87| 1| func testMixedNestedList() {
88| 1| let input: [MarkdownConvertible] = [
89| 1| "Fruits",
90| 1| MarkdownList(items: ["π", "π", "π", "π"]),
91| 1| "Bakery",
92| 1| MarkdownList(items: ["π₯", "π", "π°", "π"])
93| 1| ]
94| 1|
95| 1| let output = """
96| 1| 1. Fruits
97| 1| - π
98| 1| - π
99| 1| - π
100| 1| - π
101| 1| 1. Bakery
102| 1| - π₯
103| 1| - π
104| 1| - π°
105| 1| - π
106| 1| """
107| 1|
108| 1| XCTAssertEqual(MarkdownList(items: input, style: .ordered).markdown, output)
109| 1| }
110| |
111| 1| func testUnorderedThreeLevelList() {
112| 1| let citrics = MarkdownList(items: ["π", "π"])
113| 1| let list = MarkdownList(items: ["Fruits", MarkdownList(items: ["Citrics", citrics])])
114| 1|
115| 1| let output = """
116| 1| - Fruits
117| 1| - Citrics
118| 1| - π
119| 1| - π
120| 1| """
121| 1|
122| 1| XCTAssertEqual(list.markdown, output)
123| 1| }
124| |
125| 1| func testMultipleParagraphLists() {
126| 1| XCTAssertEqual(MarkdownList(items: multilineInput).markdown, multilineOutput)
127| 1| }
128| |
129| | static var allTests = [
130| | ("testUnorderedSimpleList", testUnorderedSimpleList),
131| | ("testOrderedSimpleList", testOrderedSimpleList),
132| | ("testUnorderedNestedList", testUnorderedNestedList),
133| | ("testOrderedNestedList", testOrderedNestedList),
134| | ("testMixedNestedList", testMixedNestedList),
135| | ("testUnorderedThreeLevelList", testUnorderedThreeLevelList),
136| | ("testMultipleParagraphLists", testMultipleParagraphLists)
137| | ]
138| |
139| |}
140| |
141| |let multilineInput = [
142| | """
143| | Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Donec odio.
144| | Quisque volutpat mattis eros. Nullam malesuada erat ut turpis.
145| | Suspendisse urna nibh, viverra non, semper suscipit, posuere a, pede.
146| |
147| | Indented code block
148| |
149| | Donec nec justo eget felis facilisis fermentum. Aliquam porttitor mauris
150| | sit amet orci. Aenean dignissim pellentesque felis.
151| |
152| | Morbi in sem quis dui placerat ornare. Pellentesque odio nisi, euismod in,
153| | pharetra a, ultricies in, diam. Sed arcu. Cras consequat.
154| | """,
155| |
156| | """
157| | Pellentesque fermentum dolor. Aliquam quam lectus, facilisis auctor, ultrices
158| | ut, elementum vulputate, nunc.
159| |
160| | > Blockquote paragraph that expands to
161| | > multiple lines
162| |
163| | Sed adipiscing ornare risus. Morbi est est, blandit sit amet, sagittis vel,
164| | euismod vel, velit. Pellentesque egestas sem. Suspendisse commodo
165| | ullamcorper magna.
166| | """
167| |]
168| |
169| |let multilineOutput = """
170| |- Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Donec odio.
171| | Quisque volutpat mattis eros. Nullam malesuada erat ut turpis.
172| | Suspendisse urna nibh, viverra non, semper suscipit, posuere a, pede.
173| |
174| | Indented code block
175| |
176| | Donec nec justo eget felis facilisis fermentum. Aliquam porttitor mauris
177| | sit amet orci. Aenean dignissim pellentesque felis.
178| |
179| | Morbi in sem quis dui placerat ornare. Pellentesque odio nisi, euismod in,
180| | pharetra a, ultricies in, diam. Sed arcu. Cras consequat.
181| |
182| |- Pellentesque fermentum dolor. Aliquam quam lectus, facilisis auctor, ultrices
183| | ut, elementum vulputate, nunc.
184| |
185| | > Blockquote paragraph that expands to
186| | > multiple lines
187| |
188| | Sed adipiscing ornare risus. Morbi est est, blandit sit amet, sagittis vel,
189| | euismod vel, velit. Pellentesque egestas sem. Suspendisse commodo
190| | ullamcorper magna.
191| |
192| |"""
/Users/travis/build/eneko/MarkdownGenerator/Tests/MarkdownGeneratorTests/PublicInterface/MarkdownTableTests.swift:
1| |//
2| |// MarkdownTableTests.swift
3| |// MarkdownGeneratorTests
4| |//
5| |// Created by Eneko Alonso on 10/9/17.
6| |//
7| |
8| |import XCTest
9| |import MarkdownGenerator
10| |
11| |class MarkdownTableTests: XCTestCase {
12| |
13| 1| func testEmptyTable() {
14| 1| let table = MarkdownTable(headers: [], data: [])
15| 1| XCTAssertEqual(table.markdown, "\n")
16| 1| }
17| |
18| 1| func test1x1Table() {
19| 1| let data: [[String]] = [[]]
20| 1| let table = MarkdownTable(headers: ["Header"], data: data)
21| 1|
22| 1| let output = """
23| 1| | Header |
24| 1| | ------ |
25| 1| | |
26| 1| """
27| 1|
28| 1| XCTAssertEqual(table.markdown, output)
29| 1| }
30| |
31| 1| func test3x3Table() {
32| 1| let data: [[String]] = [
33| 1| ["π", "Apple", "Fruits"],
34| 1| ["π", "Orange", "Fruits"],
35| 1| ["π₯", "Bread", "Bakery"]
36| 1| ]
37| 1| let table = MarkdownTable(headers: ["", "Name", "Department"], data: data)
38| 1|
39| 1| let output = """
40| 1| | | Name | Department |
41| 1| | -- | ------ | ---------- |
42| 1| | π | Apple | Fruits |
43| 1| | π | Orange | Fruits |
44| 1| | π₯ | Bread | Bakery |
45| 1| """
46| 1|
47| 1| XCTAssertEqual(table.markdown, output)
48| 1| }
49| |
50| 1| func testMultilineValues() {
51| 1| let data: [[String]] = [
52| 1| ["Single-line value", "Multi-line\n\nvalue"],
53| 1| ["Single-line value", "Multi-line\n\nvalue"],
54| 1| ["Single-line value", "Multi-line\n\nvalue"]
55| 1| ]
56| 1| let table = MarkdownTable(headers: ["Single-line", "Multi-line"], data: data)
57| 1|
58| 1| let output = """
59| 1| | Single-line | Multi-line |
60| 1| | ----------------- | ----------------- |
61| 1| | Single-line value | Multi-line value |
62| 1| | Single-line value | Multi-line value |
63| 1| | Single-line value | Multi-line value |
64| 1| """
65| 1|
66| 1| XCTAssertEqual(table.markdown, output)
67| 1| }
68| |
69| 1| func testMixedTable() {
70| 1| let table = MarkdownTable(headers: ["Foo"], data: [["Bar"], [], ["Baz", "Bax"]])
71| 1|
72| 1| let output = """
73| 1| | Foo | |
74| 1| | --- | --- |
75| 1| | Bar | |
76| 1| | | |
77| 1| | Baz | Bax |
78| 1| """
79| 1|
80| 1| XCTAssertEqual(table.markdown, output)
81| 1| }
82| |
83| | static var allTests = [
84| | ("test1x1Table", test1x1Table),
85| | ("test3x3Table", test3x3Table),
86| | ("testMultilineValues", testMultilineValues)
87| | ]
88| |
89| |}
<<<<<< EOF
# path=fixes
./Package.swift:3
./Sources/MarkdownGenerator/Extensions.swift:7,9,11,13,17,18,26,28,29,31,32,39,40
./Sources/MarkdownGenerator/MarkdownBlockquotes.swift:7,9,22,24,30,31,35,36,37,48,52,53
./Sources/MarkdownGenerator/MarkdownCodeBlock.swift:7,9,13,17,18,32,33,34,35,36,44
./Sources/MarkdownGenerator/MarkdownCollapsibleSection.swift:7,9,27,36,37,42,44,47,48,49
./Sources/MarkdownGenerator/MarkdownConvertible.swift:7,9,12,15
./Sources/MarkdownGenerator/MarkdownFile.swift:7,9,12,15,23,27,45,46,50,51,63,64,68,72,73,74
./Sources/MarkdownGenerator/MarkdownHeader.swift:7,9,45,67,68,83,84,85,86,120,121,145
./Sources/MarkdownGenerator/MarkdownImage.swift:7,9,19,22,25,34,35,39,40
./Sources/MarkdownGenerator/MarkdownLink.swift:7,9,19,22,25,34,35,39,40
./Sources/MarkdownGenerator/MarkdownList.swift:7,9,30,33,40,41,45,46,53,56,57,64,65,66,67,75
./Sources/MarkdownGenerator/MarkdownTable.swift:7,9,23,26,39,40,46,51,57,58,66,69,71,72,75,76,87,90,91,92,101,102,103,108,110,111
./Tests/LinuxMain.swift:3
./Tests/MarkdownGeneratorTests/Internal/MarkdownTableInternalTests.swift:7,10,12,16,17,21,22
./Tests/MarkdownGeneratorTests/PublicInterface/ConsecutiveBlankLinesTests.swift:7,10,12,16,17,21,22,26,27,33,34,38,41,42,45,47,50,51,54,55,57,58,61,63,66,67,71,72,74,75,80,82,86,87,91,92,93,95,96,97,102,104,108,109,113,115,118,120,121,126,128,131,133,137,138,151,152
./Tests/MarkdownGeneratorTests/PublicInterface/ExtensionsTests.swift:7,10,12,16,17,27,29,31,35,36,41,42
./Tests/MarkdownGeneratorTests/PublicInterface/MarkdownBlockquotesTests.swift:7,10,12,15,18,20,22,25,38,41,42,45,46,49,50,53,54,60,61,66,67,76,77
./Tests/MarkdownGeneratorTests/PublicInterface/MarkdownCodeBlockTests.swift:7,10,12,19,20,25,28,33,36,39,40,45,48,54,57,61,62,67,70,76,79,83,84,85
./Tests/MarkdownGeneratorTests/PublicInterface/MarkdownCollapsibleSectionTests.swift:7,10,12,16,18,22,23,27,29,33,38,42,45,53,54,55
./Tests/MarkdownGeneratorTests/PublicInterface/MarkdownConvertibleTests.swift:7,10,12,15,16,20,21,22,27,30,31
./Tests/MarkdownGeneratorTests/PublicInterface/MarkdownFileTests.swift:7,10,12,16,17,22,25,26,31,34,35,39,40
./Tests/MarkdownGeneratorTests/PublicInterface/MarkdownHeaderTests.swift:7,10,12,15,16,24,25,31,36,41,48,49,63,64,70,75,80,87,88,91,92,101,102
./Tests/MarkdownGeneratorTests/PublicInterface/MarkdownImageTests.swift:7,10,12,15,16,20,21,26,27
./Tests/MarkdownGeneratorTests/PublicInterface/MarkdownLinkTests.swift:7,10,12,16,17,21,22
./Tests/MarkdownGeneratorTests/PublicInterface/MarkdownListTests.swift:7,10,12,15,22,24,25,28,35,37,38,46,59,61,62,70,83,85,86,94,107,109,110,114,121,123,124,127,128,138,139,140,146,148,151,155,159,162,168,173,175,178,181,184,187,191
./Tests/MarkdownGeneratorTests/PublicInterface/MarkdownTableTests.swift:7,10,12,16,17,21,27,29,30,38,46,48,49,57,65,67,68,71,79,81,82,88,89
<<<<<< EOF