Skip to content

Commit e54922b

Browse files
committed
Element tests to ensure Ignite behavior is fixed.
1 parent 306e2a1 commit e54922b

File tree

5 files changed

+344
-1
lines changed

5 files changed

+344
-1
lines changed

Tests/IgniteTesting/Elements/Body.swift

Lines changed: 92 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,32 @@ import Testing
1010

1111
@testable import Ignite
1212

13-
/// Tests for the `title` element.
13+
/// A site configured to use remote Bootstrap CDN.
14+
private struct RemoteBootstrapSite: Site {
15+
var name = "Test"
16+
var url = URL(static: "https://www.example.com")
17+
var homePage = TestPage()
18+
var layout = EmptyLayout()
19+
var useDefaultBootstrapURLs: BootstrapOptions = .remoteBootstrap
20+
}
21+
22+
/// A site configured with visible line numbers.
23+
private struct LineNumbersSite: Site {
24+
var name = "Test"
25+
var url = URL(static: "https://www.example.com")
26+
var homePage = TestPage()
27+
var layout = EmptyLayout()
28+
var syntaxHighlighterConfiguration: SyntaxHighlighterConfiguration
29+
30+
init(lineNumberVisibility: SyntaxHighlighterConfiguration.LineNumberVisibility = .visible) {
31+
self.syntaxHighlighterConfiguration = SyntaxHighlighterConfiguration(
32+
languages: [],
33+
lineNumberVisibility: lineNumberVisibility
34+
)
35+
}
36+
}
37+
38+
/// Tests for the `Body` element.
1439
@Suite("Body Tests")
1540
@MainActor class BodyTests: IgniteTestSuite {
1641
@Test("Simple Body Test")
@@ -49,4 +74,70 @@ import Testing
4974
let output = element.markupString()
5075
#expect(output.contains("data-theme=\"dark\""))
5176
}
77+
78+
// MARK: - Bootstrap branches
79+
80+
@Test("Remote bootstrap includes CDN script with integrity and crossorigin")
81+
func remoteBootstrap() async throws {
82+
try PublishingContext.initialize(for: RemoteBootstrapSite(), from: #filePath)
83+
let element = Body()
84+
let output = element.markupString()
85+
#expect(output.contains("cdn.jsdelivr.net"))
86+
#expect(output.contains("integrity="))
87+
#expect(output.contains("crossorigin=\"anonymous\""))
88+
}
89+
90+
// MARK: - Syntax highlighting
91+
92+
@Test("Body includes syntax highlighting script when highlighters are present")
93+
func syntaxHighlightingScript() async throws {
94+
publishingContext.syntaxHighlighters.append(.swift)
95+
let element = Body()
96+
let output = element.markupString()
97+
#expect(output.contains("/js/syntax-highlighting.js"))
98+
}
99+
100+
// MARK: - Tooltip initialization
101+
102+
@Test("Body includes tooltip initialization when content has tooltip triggers")
103+
func tooltipInitScript() async throws {
104+
let element = Body {
105+
Text("Hover me")
106+
.customAttribute(name: "data-bs-toggle", value: "tooltip")
107+
}
108+
let output = element.markupString()
109+
#expect(output.contains("bootstrap.Tooltip"))
110+
}
111+
112+
// MARK: - Line number visibility
113+
114+
@Test("Body with visible line numbers adds line-numbers class")
115+
func visibleLineNumbers() async throws {
116+
try PublishingContext.initialize(for: LineNumbersSite(), from: #filePath)
117+
let element = Body()
118+
let output = element.markupString()
119+
#expect(output.contains("line-numbers"))
120+
}
121+
122+
@Test("Body with visible line numbers and custom start adds data-start attribute")
123+
func lineNumbersCustomStart() async throws {
124+
try PublishingContext.initialize(
125+
for: LineNumbersSite(lineNumberVisibility: .visible(firstLine: 5, linesWrap: false)),
126+
from: #filePath
127+
)
128+
let element = Body()
129+
let output = element.markupString()
130+
#expect(output.contains("data-start=\"5\""))
131+
}
132+
133+
@Test("Body with visible line numbers and wrapping adds pre-wrap style")
134+
func lineNumbersWrapping() async throws {
135+
try PublishingContext.initialize(
136+
for: LineNumbersSite(lineNumberVisibility: .visible(firstLine: 1, linesWrap: true)),
137+
from: #filePath
138+
)
139+
let element = Body()
140+
let output = element.markupString()
141+
#expect(output.contains("white-space: pre-wrap"))
142+
}
52143
}

Tests/IgniteTesting/Elements/CodeBlock.swift

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,22 @@ import Testing
1010

1111
@testable import Ignite
1212

13+
/// A site configured with a specific line number visibility for CodeBlock tests.
14+
private struct LineNumbersSite: Site {
15+
var name = "Test"
16+
var url = URL(static: "https://www.example.com")
17+
var homePage = TestPage()
18+
var layout = EmptyLayout()
19+
var syntaxHighlighterConfiguration: SyntaxHighlighterConfiguration
20+
21+
init(lineNumberVisibility: SyntaxHighlighterConfiguration.LineNumberVisibility = .visible) {
22+
self.syntaxHighlighterConfiguration = SyntaxHighlighterConfiguration(
23+
languages: [],
24+
lineNumberVisibility: lineNumberVisibility
25+
)
26+
}
27+
}
28+
1329
/// Tests for the `CodeBlock` element.
1430
@Suite("CodeBlock Tests")
1531
@MainActor
@@ -65,4 +81,69 @@ class CodeBlockTests: IgniteTestSuite {
6581
let output = element.markupString()
6682
#expect(output.contains("data-line=\"1-3\""))
6783
}
84+
85+
@Test("Code block with mixed highlighted lines and ranges combines data-line values")
86+
func mixedHighlightedLinesAndRanges() {
87+
let element = CodeBlock(.swift) { "a\nb\nc\nd\ne" }
88+
.highlightedLines(1, 5, ranges: 2...4)
89+
let output = element.markupString()
90+
#expect(output.contains("data-line=\"1,5,2-4\""))
91+
}
92+
93+
// MARK: - Line number visibility matrix
94+
95+
@Test("Site visible + element hidden adds no-line-numbers class")
96+
func siteVisibleElementHidden() throws {
97+
try PublishingContext.initialize(for: LineNumbersSite(), from: #filePath)
98+
let element = CodeBlock(.swift) { "let x = 1" }
99+
.lineNumberVisibility(.hidden)
100+
let output = element.markupString()
101+
#expect(output.contains("no-line-numbers"))
102+
}
103+
104+
@Test("Site hidden + element visible adds line-numbers class")
105+
func siteHiddenElementVisible() throws {
106+
try PublishingContext.initialize(for: LineNumbersSite(lineNumberVisibility: .hidden), from: #filePath)
107+
let element = CodeBlock(.swift) { "let x = 1" }
108+
.lineNumberVisibility(.visible)
109+
let output = element.markupString()
110+
#expect(output.contains("line-numbers"))
111+
#expect(!output.contains("data-start"))
112+
}
113+
114+
@Test("Site hidden + element visible with custom start adds data-start")
115+
func siteHiddenElementVisibleCustomStart() throws {
116+
try PublishingContext.initialize(for: LineNumbersSite(lineNumberVisibility: .hidden), from: #filePath)
117+
let element = CodeBlock(.swift) { "let x = 1" }
118+
.lineNumberVisibility(.visible(firstLine: 10, linesWrap: false))
119+
let output = element.markupString()
120+
#expect(output.contains("data-start=\"10\""))
121+
}
122+
123+
@Test("Site hidden + element visible with wrapping adds pre-wrap style")
124+
func siteHiddenElementVisibleWrapping() throws {
125+
try PublishingContext.initialize(for: LineNumbersSite(lineNumberVisibility: .hidden), from: #filePath)
126+
let element = CodeBlock(.swift) { "let x = 1" }
127+
.lineNumberVisibility(.visible(firstLine: 1, linesWrap: true))
128+
let output = element.markupString()
129+
#expect(output.contains("white-space: pre-wrap"))
130+
}
131+
132+
@Test("Both visible with different start lines adds data-start for element value")
133+
func bothVisibleDifferentStart() throws {
134+
try PublishingContext.initialize(for: LineNumbersSite(), from: #filePath)
135+
let element = CodeBlock(.swift) { "let x = 1" }
136+
.lineNumberVisibility(.visible(firstLine: 5, linesWrap: false))
137+
let output = element.markupString()
138+
#expect(output.contains("data-start=\"5\""))
139+
}
140+
141+
@Test("Both visible with different wrapping adds white-space style")
142+
func bothVisibleDifferentWrapping() throws {
143+
try PublishingContext.initialize(for: LineNumbersSite(), from: #filePath)
144+
let element = CodeBlock(.swift) { "let x = 1" }
145+
.lineNumberVisibility(.visible(firstLine: 1, linesWrap: true))
146+
let output = element.markupString()
147+
#expect(output.contains("white-space: pre-wrap"))
148+
}
68149
}

Tests/IgniteTesting/Elements/SubscribeForm.swift

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,4 +76,64 @@ class SubscribeFormTests: IgniteTestSuite {
7676
let output = element.markupString()
7777
#expect(output.contains(">Join Now</button>"))
7878
}
79+
80+
// MARK: - Form style
81+
82+
@Test("Stacked form style changes layout to vertical")
83+
func stackedFormStyle() async throws {
84+
let element = SubscribeForm(.sendFox(listID: "x", formID: "y"))
85+
.formStyle(.stacked)
86+
let output = element.markupString()
87+
#expect(output.contains("col-md-12"))
88+
#expect(output.contains("w-100"))
89+
}
90+
91+
// MARK: - Control size
92+
93+
@Test("Small control size adds small classes")
94+
func smallControlSize() async throws {
95+
let element = SubscribeForm(.sendFox(listID: "x", formID: "y"))
96+
.controlSize(.small)
97+
let output = element.markupString()
98+
#expect(output.contains("form-control-sm"))
99+
#expect(output.contains("btn-sm"))
100+
}
101+
102+
@Test("Large control size adds large classes")
103+
func largeControlSize() async throws {
104+
let element = SubscribeForm(.sendFox(listID: "x", formID: "y"))
105+
.controlSize(.large)
106+
let output = element.markupString()
107+
#expect(output.contains("form-control-lg"))
108+
#expect(output.contains("btn-lg"))
109+
}
110+
111+
// MARK: - Button customization
112+
113+
@Test("Custom button role changes button class")
114+
func customButtonRole() async throws {
115+
let element = SubscribeForm(.sendFox(listID: "x", formID: "y"))
116+
.subscribeButtonRole(.danger)
117+
let output = element.markupString()
118+
#expect(output.contains("btn-danger"))
119+
#expect(!output.contains("btn-primary"))
120+
}
121+
122+
// MARK: - Provider-specific behavior
123+
124+
@Test("Mailchimp form includes honeypot field with correct name")
125+
func mailchimpHoneypot() async throws {
126+
let element = SubscribeForm(.mailchimp(username: "user", uValue: "abc", listID: "123"))
127+
let output = element.markupString()
128+
#expect(output.contains("name=\"b_abc_123\""))
129+
#expect(output.contains("aria-hidden=\"true\""))
130+
}
131+
132+
@Test("Kit form has no honeypot field and no external script")
133+
func kitNoHoneypotNoScript() async throws {
134+
let element = SubscribeForm(.kit("myToken"))
135+
let output = element.markupString()
136+
#expect(!output.contains("aria-hidden"))
137+
#expect(!output.contains("<script"))
138+
}
79139
}

Tests/IgniteTesting/Elements/Table.swift

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,4 +81,58 @@ class TableTests: IgniteTestSuite {
8181
let output = element.markupString()
8282
#expect(output.contains("<caption>Test caption</caption>"))
8383
}
84+
85+
// MARK: - Filter title
86+
87+
@Test("Table with filter title renders input with igniteFilterTable")
88+
func filterTitle() async throws {
89+
let element = Table(filterTitle: "Search...") {
90+
Row { Column { "Cell" } }
91+
}
92+
let output = element.markupString()
93+
#expect(output.contains("placeholder=\"Search...\""))
94+
#expect(output.contains("igniteFilterTable"))
95+
#expect(output.contains("form-control mb-2"))
96+
}
97+
98+
// MARK: - Sequence initializers
99+
100+
@Test("Table from sequence renders rows")
101+
func sequenceInitializer() async throws {
102+
let items = ["Alice", "Bob"]
103+
let element = Table(items) { name in
104+
Row { Column { name } }
105+
}
106+
let output = element.markupString()
107+
#expect(output.contains("Alice"))
108+
#expect(output.contains("Bob"))
109+
#expect(output.contains("<tbody>"))
110+
}
111+
112+
@Test("Table from sequence with header renders thead and rows")
113+
func sequenceInitializerWithHeader() async throws {
114+
let items = ["Alice", "Bob"]
115+
let element = Table(items) { name in
116+
Row { Column { name } }
117+
} header: {
118+
"Name"
119+
}
120+
let output = element.markupString()
121+
#expect(output.contains("<thead><tr><th>Name</th></tr></thead>"))
122+
#expect(output.contains("Alice"))
123+
}
124+
125+
// MARK: - Combined options
126+
127+
@Test("Table with border, striped rows, and caption combines all attributes")
128+
func combinedOptions() async throws {
129+
let element = Table { }
130+
.tableBorder(true)
131+
.tableStyle(.stripedRows)
132+
.accessibilityLabel("My Table")
133+
let output = element.markupString()
134+
#expect(output.contains("table-bordered"))
135+
#expect(output.contains("table-striped"))
136+
#expect(output.contains("<caption>My Table</caption>"))
137+
}
84138
}

Tests/IgniteTesting/Modifiers/MediaQuery.swift

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,4 +144,61 @@ class MediaQueryTests: IgniteTestSuite {
144144
#expect(query != nil)
145145
#expect(query?.condition == "min-width: 768px")
146146
}
147+
148+
// MARK: - CSS MediaQuery render output
149+
150+
@Test("CSS MediaQuery single feature renders with @media wrapper")
151+
func cssMediaQuerySingleFeature() async throws {
152+
let query = MediaQuery(ColorSchemeQuery.dark) {
153+
Ruleset(.class("test"), styles: [InlineStyle(.opacity, value: "0.5")])
154+
}
155+
156+
let output = query.render()
157+
#expect(output.contains("@media (prefers-color-scheme: dark)"))
158+
#expect(output.contains("opacity: 0.5"))
159+
}
160+
161+
@Test("CSS MediaQuery and combinator joins features")
162+
func cssMediaQueryAndCombinator() async throws {
163+
let query = MediaQuery(ColorSchemeQuery.dark, BreakpointQuery.medium, combinator: .and) {
164+
Ruleset(.class("test"), styles: [InlineStyle(.opacity, value: "1")])
165+
}
166+
167+
let output = query.render()
168+
#expect(output.contains("@media (prefers-color-scheme: dark) and (min-width: 768px)"))
169+
}
170+
171+
@Test("CSS MediaQuery or combinator joins features")
172+
func cssMediaQueryOrCombinator() async throws {
173+
let query = MediaQuery(ColorSchemeQuery.dark, BreakpointQuery.medium, combinator: .or) {
174+
Ruleset(.class("test"), styles: [InlineStyle(.opacity, value: "1")])
175+
}
176+
177+
let output = query.render()
178+
#expect(output.contains("@media (prefers-color-scheme: dark) or (min-width: 768px)"))
179+
}
180+
181+
@Test("CSS MediaQuery array initializer matches variadic initializer")
182+
func cssMediaQueryArrayInitializerParity() async throws {
183+
let features: [MediaFeature] = [ColorSchemeQuery.dark, BreakpointQuery.medium]
184+
185+
let variadicQuery = MediaQuery(ColorSchemeQuery.dark, BreakpointQuery.medium) {
186+
Ruleset(.class("a"), styles: [])
187+
}
188+
189+
let arrayQuery = MediaQuery(features) {
190+
Ruleset(.class("a"), styles: [])
191+
}
192+
193+
#expect(variadicQuery.render() == arrayQuery.render())
194+
}
195+
196+
@Test("CSS MediaQuery description matches render output")
197+
func cssMediaQueryDescriptionMatchesRender() async throws {
198+
let query = MediaQuery(ColorSchemeQuery.dark) {
199+
Ruleset(.class("x"), styles: [InlineStyle(.color, value: "red")])
200+
}
201+
202+
#expect(query.description == query.render())
203+
}
147204
}

0 commit comments

Comments
 (0)