214 lines
8.1 KiB
Swift
214 lines
8.1 KiB
Swift
import Elementary
|
|
import Foundation
|
|
import Testing
|
|
|
|
@testable import Infrastructure
|
|
|
|
@Suite(
|
|
"Page protocol",
|
|
.tags(.`protocol`)
|
|
)
|
|
struct PageTests {
|
|
|
|
// MARK: Functional tests
|
|
|
|
@Test
|
|
func `assembles the document around the page's parts`() {
|
|
let html = StubPage().render()
|
|
|
|
#expect(html.contains("<title>Stub Page</title>"))
|
|
#expect(html.contains(#"lang="en""#))
|
|
#expect(html.contains(#"name="viewport""#))
|
|
#expect(html.contains(#"<meta name="stub" content="marker">"#))
|
|
#expect(html.contains(#"<link rel="stylesheet" href="/css/stub.css">"#))
|
|
#expect(html.contains(#"<script defer src="/js/stub.js"></script>"#))
|
|
#expect(html.contains("Stub content"))
|
|
}
|
|
|
|
@Test
|
|
func `places the metadata between the viewport and the stylesheets`() throws {
|
|
let html = StubPage().render()
|
|
|
|
let viewport = try #require(html.range(of: #"name="viewport""#))
|
|
let metadata = try #require(html.range(of: #"name="stub""#))
|
|
let stylesheet = try #require(html.range(of: "/css/stub.css"))
|
|
|
|
#expect(viewport.lowerBound < metadata.lowerBound)
|
|
#expect(metadata.lowerBound < stylesheet.lowerBound)
|
|
}
|
|
|
|
@Test
|
|
func `renders the scripts deferred in the head, after the stylesheets`() throws {
|
|
let html = StubPage().render()
|
|
|
|
let stylesheet = try #require(html.range(of: "/css/stub.css"))
|
|
let script = try #require(html.range(of: "/js/stub.js"))
|
|
let content = try #require(html.range(of: "Stub content"))
|
|
|
|
// Deferred head scripts start downloading during head parsing but still execute, in order, only after the
|
|
// document is parsed — the semantics end-of-body tags gave, minus the late download start.
|
|
#expect(stylesheet.lowerBound < script.lowerBound)
|
|
#expect(script.lowerBound < content.lowerBound)
|
|
}
|
|
|
|
@Test
|
|
func `omits the summary and canonical tags by default`() {
|
|
let html = StubPage().render()
|
|
|
|
#expect(!html.contains(#"name="description""#))
|
|
#expect(!html.contains(#"rel="canonical""#))
|
|
}
|
|
|
|
@Test
|
|
func `renders the summary and canonical tags when provided`() {
|
|
let html = StubPage(
|
|
canonicalURL: "https://stub.example/",
|
|
summary: "A stub page."
|
|
).render()
|
|
|
|
#expect(html.contains(#"<meta name="description" content="A stub page.">"#))
|
|
#expect(html.contains(#"<link rel="canonical" href="https://stub.example/">"#))
|
|
}
|
|
|
|
@Test
|
|
func `omits the social card tags by default`() {
|
|
let html = StubPage().render()
|
|
|
|
#expect(!html.contains(#"property="og:"#))
|
|
#expect(!html.contains(#"name="twitter:card""#))
|
|
}
|
|
|
|
@Test
|
|
func `renders the social card tags when provided`() {
|
|
let html = StubPage(socialCard: .init(
|
|
title: "Stub Page",
|
|
summary: "A stub page.",
|
|
url: "https://stub.example/",
|
|
image: .init(
|
|
url: "https://stub.example/img/card.png",
|
|
width: 2400,
|
|
height: 1260
|
|
)
|
|
)).render()
|
|
|
|
#expect(html.contains(#"<meta property="og:type" content="website">"#))
|
|
#expect(html.contains(#"<meta property="og:title" content="Stub Page">"#))
|
|
#expect(html.contains(#"<meta property="og:description" content="A stub page.">"#))
|
|
#expect(html.contains(#"<meta property="og:url" content="https://stub.example/">"#))
|
|
#expect(html.contains(#"<meta property="og:image" content="https://stub.example/img/card.png">"#))
|
|
#expect(html.contains(#"<meta property="og:image:width" content="2400">"#))
|
|
#expect(html.contains(#"<meta property="og:image:height" content="1260">"#))
|
|
#expect(html.contains(#"<meta name="twitter:card" content="summary_large_image">"#))
|
|
}
|
|
|
|
@Test
|
|
func `omits the structured data script by default`() {
|
|
let html = StubPage().render()
|
|
|
|
#expect(!html.contains("application/ld+json"))
|
|
}
|
|
|
|
@Test
|
|
func `renders the structured data script when provided`() {
|
|
let html = StubPage(structuredData: .init(
|
|
name: "Stub Site",
|
|
url: "https://stub.example/",
|
|
logo: "https://stub.example/logo.png",
|
|
profiles: ["https://social.example/stub"]
|
|
)).render()
|
|
|
|
#expect(html.contains(
|
|
#"<script type="application/ld+json">"# +
|
|
#"{"@context":"https://schema.org","@graph":["# +
|
|
#"{"@type":"Organization","@id":"https://stub.example/#organization","name":"Stub Site","url":"https://stub.example/","logo":"https://stub.example/logo.png","sameAs":["https://social.example/stub"]},"# +
|
|
#"{"@type":"WebSite","name":"Stub Site","url":"https://stub.example/","publisher":{"@id":"https://stub.example/#organization"}}]}"# +
|
|
#"</script>"#
|
|
))
|
|
}
|
|
|
|
@Test
|
|
func `omits the analytics tracker by default`() {
|
|
let html = StubPage().render()
|
|
|
|
#expect(!html.contains("data-website-id"))
|
|
#expect(!html.contains(#"rel="preconnect""#))
|
|
}
|
|
|
|
@Test
|
|
func `renders the analytics tracker when provided`() throws {
|
|
let html = StubPage(analytics: .init(
|
|
scriptURL: "https://analytics.example.com/script",
|
|
websiteID: "0000-website-id",
|
|
domains: "example.com"
|
|
)).render()
|
|
|
|
#expect(html.contains(#"<script defer src="https://analytics.example.com/script" data-website-id="0000-website-id" data-domains="example.com" data-exclude-hash="true" data-do-not-track="true" data-performance="true"></script>"#))
|
|
|
|
// The preconnect hint warms the tracker origin's connection before the parser reaches the script tag.
|
|
let preconnect = try #require(html.range(of: #"<link rel="preconnect" href="https://analytics.example.com">"#))
|
|
let script = try #require(html.range(of: #"<script defer src="https://analytics.example.com/script""#))
|
|
|
|
#expect(preconnect.lowerBound < script.lowerBound)
|
|
}
|
|
|
|
@Test
|
|
func `omits the session recorder script by default`() {
|
|
let html = StubPage(analytics: .init(
|
|
scriptURL: "https://analytics.example.com/script",
|
|
websiteID: "0000-website-id",
|
|
domains: "example.com"
|
|
)).render()
|
|
|
|
#expect(!html.contains("recorder.js"))
|
|
}
|
|
|
|
@Test
|
|
func `renders the session recorder script when recorder mode is on`() throws {
|
|
let html = StubPage(analytics: .init(
|
|
scriptURL: "https://analytics.example.com/script",
|
|
websiteID: "0000-website-id",
|
|
domains: "example.com",
|
|
recorder: true
|
|
)).render()
|
|
|
|
#expect(html.contains(#"<script defer src="https://analytics.example.com/recorder.js" data-website-id="0000-website-id"></script>"#))
|
|
|
|
let tracker = try #require(html.range(of: #"<script defer src="https://analytics.example.com/script""#))
|
|
let recorder = try #require(html.range(of: #"<script defer src="https://analytics.example.com/recorder.js""#))
|
|
|
|
#expect(tracker.lowerBound < recorder.lowerBound)
|
|
}
|
|
|
|
@Test
|
|
func `omits the analytics behavior flags that are disabled`() {
|
|
let html = StubPage(analytics: .init(
|
|
scriptURL: "https://analytics.example.com/script",
|
|
websiteID: "0000-website-id",
|
|
domains: "example.com",
|
|
excludeHash: true,
|
|
doNotTrack: false,
|
|
performance: false
|
|
)).render()
|
|
|
|
#expect(html.contains(#"<script defer src="https://analytics.example.com/script" data-website-id="0000-website-id" data-domains="example.com" data-exclude-hash="true"></script>"#))
|
|
#expect(!html.contains("data-do-not-track"))
|
|
#expect(!html.contains("data-performance"))
|
|
}
|
|
|
|
@Test
|
|
func `appends the version token to the asset URLs`() {
|
|
let html = StubPage(assetVersion: "0123456789abcdef").render()
|
|
|
|
#expect(html.contains("/css/stub.css?v=0123456789abcdef"))
|
|
#expect(html.contains("/js/stub.js?v=0123456789abcdef"))
|
|
}
|
|
|
|
@Test
|
|
func `derives the document language from the locale`() {
|
|
let html = StubPage(locale: .init(identifier: "de-DE")).render()
|
|
|
|
#expect(html.contains(#"lang="de""#))
|
|
}
|
|
|
|
}
|