Implemented the "tagging()" method for the the AnalyticsEvent+Tagging extension in the Infrastructure package.

This commit is contained in:
2026-08-30 09:15:59 +02:00
parent cf58b97de1
commit 8bf6e86ffb
4 changed files with 76 additions and 3 deletions
+2 -2
View File
@@ -9,7 +9,7 @@ The shared [Hummingbird](https://github.com/hummingbird-project/hummingbird) too
| Pages and assets | `Page`, `Asset`, `AssetExtension`, `FingerprintAssets` |
| Link previews | `SocialCard`, its `Image` and `Style`, and the `Tag` meta tags it derives |
| Structured data | `StructuredData`, the `Node`, `Property`, and `Value` types of its schema.org graph, the open `Name` and `Kind` vocabularies, and the site-wide initializer building the `Organization`/`WebSite` pair |
| Analytics | `Analytics`, the `Event`s a page reports, the tracker script `Attribute`s it derives, and the `origin` its preconnect hint targets |
| Analytics | `Analytics`, the `Event`s a page reports (with `tagging()` to apply one to a tag), the tracker script `Attribute`s it derives, and the `origin` its preconnect hint targets |
| Responses | `CachedHTMLResponse`, `LocalizedHTMLCollectionResponse` |
| Contexts | `LocalizedRequestContext` |
| Constants | The `HTTPField.Name` header names, `Int.RateLimit` limits, and `String.Security` header values the middlewares default to |
@@ -29,7 +29,7 @@ Sources/
├── Public/ public API
│ ├── Builders/ RouteCollectionBuilder
│ ├── Enumerations/ AssetExtension
│ ├── Extensions/ addController, plus the default header names, rate limits, and header values
│ ├── Extensions/ addController and Analytics.Event tagging, plus the default header names, rate limits, and header values
│ ├── Methods/ FingerprintAssets
│ ├── Middlewares/ the seven HTTP middlewares
│ ├── Protocols/ Asset, LocalizedRequestContext, Page, RouterController
@@ -0,0 +1,24 @@
import Elementary
public extension Analytics.Event {
// MARK: Methods
/// The event's ``attributes`` as attributes of the tag reporting it.
///
/// ```swift
/// a(.href(url)) { "Listen" }
/// .attributes(contentsOf: Analytics.Event(name: "playlist").tagging())
/// ```
///
/// - Returns: one HTML attribute per event attribute, applied verbatim.
func tagging<Tag: HTMLTrait.Attributes.Global>() -> [HTMLAttribute<Tag>] {
attributes.map {
.custom(
name: $0.name,
value: $0.value
)
}
}
}
@@ -3,7 +3,7 @@ import Hummingbird
/// Answers a request whose path carries a trailing slash with `301 Moved Permanently` to the same path without one.
///
/// The router matches `/about` and `/about/` alike, and `FileMiddleware` serves `/robots.txt/` as readily as `/robots.txt`, so without this
/// The router matches `/page` and `/page/` alike, and `FileMiddleware` serves `/robots.txt/` as readily as `/robots.txt`, so without this
/// every address on the site answers under at least two URLs. A search engine treats those as separate pages competing with each other, and any link
/// earned by one is not credited to the other. Redirecting collapses them onto the form the pages already name as canonical.
///
@@ -0,0 +1,49 @@
import Elementary
import Testing
@testable import Infrastructure
@Suite(
"Analytics.Event+Tagging extension",
.tags(.extension)
)
struct AnalyticsEventTaggingTests {
// MARK: Methods tests
@Test
func `applies the event name to the tag`() {
let markup = a(.href("/")) { "Listen" }
.attributes(contentsOf: Analytics.Event(name: "instagram").tagging())
#expect(markup.render().contains(#"data-umami-event="instagram""#))
}
@Test
func `applies every event attribute to the tag`() {
let event = Analytics.Event(
name: "playlist",
properties: ["set": "avc-xi"]
)
let markup = button {}
.attributes(contentsOf: event.tagging())
let rendered = markup.render()
#expect(rendered.contains(#"data-umami-event="playlist""#))
#expect(rendered.contains(#"data-umami-event-set="avc-xi""#))
}
@Test
func `returns one attribute per event attribute`() {
let event = Analytics.Event(
name: "playlist",
properties: ["set": "avc-xi"]
)
let attributes: [HTMLAttribute<HTMLTag.a>] = event.tagging()
#expect(attributes.count == event.attributes.count)
}
}