Add tests for About page, header brand, and site icons (#442)
- Implement test for About page to ensure it includes the Claracle image linked to Spotify. - Create tests for the header brand to verify the use of the Claracle image asset and its existence. - Add tests for site icon metadata to confirm the correct usage of the Claracle image in favicons and web manifest, as well as the existence of required PNG files. Co-authored-by: Copilot <copilot@github.com>
Juan Manuel Servera committed
Jun 12, 2026 at 22:44 UTC
598d5d063e2d3928e6f8db8ea44eab4bab4d5870
23 files changed
+155
-39
.vscode/settings.json
new
+7
@@ -0,0 +1,7 @@
1
+{
2
+ "python.testing.pytestArgs": [
3
+ "."
4
+ ],
5
+ "python.testing.unittestEnabled": false,
6
+ "python.testing.pytestEnabled": true
7
+}
\ No newline at end of file
assets/css/common/header.css
+7
@@ -49,6 +49,13 @@
49
color: var(--color-accent);
50
}
51
52
+.site-brand__mark img {
53
+ width: 100%;
54
+ height: 100%;
55
+ border-radius: calc(var(--radius-md) - 2px);
56
+ object-fit: cover;
57
+}
58
+
59
.site-header__cluster {
60
gap: var(--space-3);
61
}
assets/images/_0e614603-592a-452d-9b27-fc552644a790.jpeg
Binary files /dev/null and b/assets/images/_0e614603-592a-452d-9b27-fc552644a790.jpeg differ
assets/images/_d2de8277-55b9-4a57-94b7-447992e757e9.jpeg
Binary files /dev/null and b/assets/images/_d2de8277-55b9-4a57-94b7-447992e757e9.jpeg differ
assets/images/_fb9af526-dbab-4b40-af04-5d8da3d2eda9.jpeg
Binary files /dev/null and b/assets/images/_fb9af526-dbab-4b40-af04-5d8da3d2eda9.jpeg differ
assets/images/claracle.jpeg
Binary files /dev/null and b/assets/images/claracle.jpeg differ
content/about/_index.md
+2
@@ -8,6 +8,8 @@ description = 'Learn what Claracle publishes and how to follow its GitHub trend
8
9
## About Claracle
10
11
+[](https://open.spotify.com/show/033xdn5nDMoCWxB3bss2dB)
12
+
13
Claracle surfaces the strongest weekly signal from GitHub activity, then organizes that analysis into monthly and yearly views for long-term pattern spotting.
14
15
Use the homepage to jump straight into the latest summary, scan the archive, or move up to broader rollups when you want a bigger-picture view.
content/methodology/_index.md
+1
-1
@@ -49,7 +49,7 @@ These caveats are part of the analysis, not a disclaimer that makes the output n
49
50
## Signal Check podcast
51
52
-Claracle publishes a companion podcast, **Signal Check**, derived from the weekly written article. Signal Check is an 8–10 minute two-host conversational episode that highlights the week's signal picks, noise calls, gaps, and predictions. It is distributed through an external podcast platform (currently Spotify); the Claracle website links to the external podcast page but does not host audio.
52
+Claracle publishes a companion podcast, **Signal Check**, derived from the weekly written article. Signal Check is an approximately 5-minute two-host conversational episode that highlights the week's signal picks, noise calls, gaps, and predictions. It is distributed through Spotify; the Claracle website links to the external podcast page but does not host audio.
53
54
### How Signal Check is produced
55
hugo.toml
+2
-2
@@ -35,8 +35,8 @@ rssLimit = 20
35
mainSections = ['weekly', 'monthly', 'yearly']
36
37
# External podcast link (leave empty to hide)
38
- # When set, a "Podcast" link appears in the site footer navigation.
39
- podcast_url = ""
38
+ # When set, a "Podcast" link appears in the site footer navigation and weekly report shortcuts.
39
+ podcast_url = "https://open.spotify.com/show/033xdn5nDMoCWxB3bss2dB"
40
41
[params.homeInfoParams]
42
Title = 'Weekly tech signal from GitHub'
layouts/partials/head.html
+8
-6
@@ -86,12 +86,14 @@
86
{{- end -}}
87
88
{{- /* Favicons */}}
89
-<link rel="icon" href="/favicon.ico" sizes="any">
90
-<link rel="icon" type="image/svg+xml" href="/images/squadscope-icon.svg">
91
-<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png">
92
-<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png">
93
-<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png">
94
-<link rel="mask-icon" href="/safari-pinned-tab.svg" color="#0066CC">
89
+{{- with resources.Get "images/claracle.jpeg" }}
90
+{{- $favicon16 := .Fill "16x16 png" }}
91
+{{- $favicon32 := .Fill "32x32 png" }}
92
+{{- $appleTouchIcon := .Fill "180x180 png" }}
93
+<link rel="icon" type="image/png" sizes="16x16" href="{{ $favicon16.RelPermalink }}">
94
+<link rel="icon" type="image/png" sizes="32x32" href="{{ $favicon32.RelPermalink }}">
95
+<link rel="apple-touch-icon" sizes="180x180" href="{{ $appleTouchIcon.RelPermalink }}">
96
+{{- end }}
97
<link rel="manifest" href="/site.webmanifest">
98
<meta name="theme-color" content="#0066CC">
99
<meta name="msapplication-TileColor" content="#0066CC">
layouts/partials/header.html
+15
-13
@@ -2,25 +2,16 @@
2
<nav class="header-nav" aria-label="Primary">
3
<a class="site-brand" href="{{ "" | absLangURL }}" accesskey="h" title="Claracle (Alt + H)" aria-label="Claracle home">
4
<span class="site-brand__mark" aria-hidden="true">
5
- <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" width="100%" height="100%" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
6
- <line x1="32" y1="4" x2="32" y2="14"/>
7
- <circle cx="32" cy="4" r="3" fill="currentColor" stroke="none"/>
8
- <rect x="10" y="14" width="44" height="36" rx="8" ry="8"/>
9
- <circle cx="22" cy="30" r="9"/>
10
- <circle cx="22" cy="30" r="4" fill="currentColor" stroke="none"/>
11
- <circle cx="42" cy="30" r="9"/>
12
- <circle cx="42" cy="30" r="4" fill="currentColor" stroke="none"/>
13
- <line x1="31" y1="30" x2="33" y2="30" stroke-width="4"/>
14
- <line x1="26" y1="42" x2="38" y2="42"/>
15
- <line x1="28" y1="45" x2="36" y2="45"/>
16
- <path d="M14 54 L14 58 Q14 62 18 62 L46 62 Q50 62 50 58 L50 54" stroke-width="2"/>
17
- </svg>
5
+ {{- with resources.Get "images/claracle.jpeg" }}
6
+ <img src="{{ .RelPermalink }}" width="32" height="32" alt="">
7
+ {{- end }}
8
</span>
9
<span class="site-brand__text">Claracle</span>
10
</a>
11
12
<div class="site-header__cluster">
13
{{- $currentPage := . }}
14
+ {{- $podcastURL := site.Params.podcast_url | default "" | strings.TrimSpace }}
15
{{- define "primary-menu-items" }}
16
{{- $currentPage := . }}
17
{{- range site.Menus.main }}
@@ -49,6 +40,17 @@
40
</details>
41
42
<div class="site-actions" aria-label="Site actions">
43
+ {{- with $podcastURL }}
44
+ <a class="icon-button spotify-button" href="{{ . | safeURL }}" target="_blank" rel="noopener noreferrer" aria-label="Spotify" title="Listen on Spotify">
45
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
46
+ <circle cx="12" cy="12" r="10"></circle>
47
+ <path d="M8 9.5c2.9-.9 5.8-.7 8.5.7"></path>
48
+ <path d="M8.7 12.4c2.3-.7 4.7-.5 6.8.5"></path>
49
+ <path d="M9.4 15.2c1.6-.4 3.2-.3 4.7.4"></path>
50
+ </svg>
51
+ </a>
52
+ {{- end }}
53
+
54
<a class="icon-button github-button" href="https://github.com/jmservera/SquadScope" target="_blank" rel="noopener" aria-label="GitHub">
55
<svg fill="currentColor" viewBox="0 0 24 24" aria-hidden="true">
56
<path fill-rule="evenodd" clip-rule="evenodd" d="M12 2C6.477 2 2 6.484 2 12.017c0 4.425 2.865 8.18 6.839 9.504.5.092.682-.217.682-.483 0-.237-.008-.868-.013-1.703-2.782.605-3.369-1.343-3.369-1.343-.454-1.158-1.11-1.466-1.11-1.466-.908-.62.069-.608.069-.608 1.003.07 1.531 1.032 1.531 1.032.892 1.53 2.341 1.088 2.91.832.092-.647.35-1.088.636-1.338-2.22-.253-4.555-1.113-4.555-4.951 0-1.093.39-1.988 1.029-2.688-.103-.253-.446-1.272.098-2.65 0 0 .84-.27 2.75 1.026A9.564 9.564 0 0112 6.844c.85.004 1.705.115 2.504.337 1.909-1.296 2.747-1.027 2.747-1.027.546 1.379.202 2.398.1 2.651.64.7 1.028 1.595 1.028 2.688 0 3.848-2.339 4.695-4.566 4.943.359.309.678.92.678 1.855 0 1.338-.012 2.419-.012 2.747 0 .268.18.58.688.482A10.019 10.019 0 0022 12.017C22 6.484 17.522 2 12 2z"></path>
layouts/partials/report-shortcuts.html
+4
@@ -7,6 +7,7 @@
7
{{- $isWeekly := eq $page.Type "weekly" -}}
8
{{- $monthlyURL := "monthly/" | absURL -}}
9
{{- $yearlyURL := "yearly/" | absURL -}}
10
+{{- $podcastURL := site.Params.podcast_url | default "" | strings.TrimSpace -}}
11
{{- if $isWeekly -}}
12
{{- with site.GetPage (printf "/monthly/%s/%s" ($page.Date.Format "2006") ($page.Date.Format "01")) -}}
13
{{- $monthlyURL = .RelPermalink -}}
@@ -25,4 +26,7 @@
26
<a href="{{ $categoriesURL }}">Categories</a>
27
<a href="{{ $searchURL }}">Search</a>
28
<a href="{{ $rssURL }}">RSS</a>
29
+ {{- if and $isWeekly $podcastURL }}
30
+ <a href="{{ $podcastURL | safeURL }}" target="_blank" rel="noopener noreferrer">Spotify</a>
31
+ {{- end }}
32
</nav>
reply_thread1.graphql
deleted
-10
@@ -1,10 +0,0 @@
1
-mutation {
2
- addPullRequestReviewThreadReply(input: {
3
- threadId: "PRRT_kwDOSgq4hM6C3UXy"
4
- body: "✅ Fixed: Removed the submodule initialization instruction from the rollout checklist. The project does not use git submodules."
5
- }) {
6
- comment {
7
- id
8
- }
9
- }
10
-}
static/android-chrome-192x192.png
Binary files a/static/android-chrome-192x192.png and b/static/android-chrome-192x192.png differ
static/android-chrome-512x512.png
Binary files a/static/android-chrome-512x512.png and b/static/android-chrome-512x512.png differ
static/apple-touch-icon.png
Binary files a/static/apple-touch-icon.png and b/static/apple-touch-icon.png differ
static/favicon-16x16.png
Binary files a/static/favicon-16x16.png and b/static/favicon-16x16.png differ
static/favicon-32x32.png
Binary files a/static/favicon-32x32.png and b/static/favicon-32x32.png differ
static/site.webmanifest
+2
-1
@@ -10,7 +10,8 @@
10
{
11
"src": "/android-chrome-512x512.png",
12
"sizes": "512x512",
13
- "type": "image/png"
13
+ "type": "image/png",
14
+ "purpose": "any maskable"
15
}
16
],
17
"theme_color": "#0066CC",
tests/test_about_page.py
new
+15
@@ -0,0 +1,15 @@
1
+"""Tests for About page content."""
2
+
3
+from __future__ import annotations
4
+
5
+from pathlib import Path
6
+
7
+REPO_ROOT = Path(__file__).resolve().parent.parent
8
+
9
+
10
+def test_about_page_includes_claracle_image() -> None:
11
+ """About page should link the full Claracle image to Spotify near the top."""
12
+ about = (REPO_ROOT / "content" / "about" / "_index.md").read_text(encoding="utf-8")
13
+ linked_image = "[](https://open.spotify.com/show/033xdn5nDMoCWxB3bss2dB)"
14
+ assert linked_image in about
15
+ assert about.index(linked_image) < about.index("Claracle surfaces")
\ No newline at end of file
tests/test_header_brand.py
new
+20
@@ -0,0 +1,20 @@
1
+"""Tests for the site header brand mark."""
2
+
3
+from __future__ import annotations
4
+
5
+from pathlib import Path
6
+
7
+REPO_ROOT = Path(__file__).resolve().parent.parent
8
+
9
+
10
+def test_header_brand_uses_claracle_image_asset() -> None:
11
+ """Header brand mark must render the Claracle image through Hugo resources."""
12
+ header = (REPO_ROOT / "layouts" / "partials" / "header.html").read_text(encoding="utf-8")
13
+ assert 'resources.Get "images/claracle.jpeg"' in header
14
+ assert '<img src="{{ .RelPermalink }}" width="32" height="32" alt="">' in header
15
+ assert "<svg" not in header.split('<span class="site-brand__text">Claracle</span>', maxsplit=1)[0]
16
+
17
+
18
+def test_claracle_brand_image_exists() -> None:
19
+ """Brand image asset must exist at the path used by the header template."""
20
+ assert (REPO_ROOT / "assets" / "images" / "claracle.jpeg").is_file()
\ No newline at end of file
tests/test_podcast_link.py
+22
-6
@@ -9,9 +9,10 @@ REPO_ROOT = Path(__file__).resolve().parent.parent
9
10
11
def test_podcast_url_param_exists_in_hugo_config() -> None:
12
- """hugo.toml must define podcast_url param (can be empty)."""
12
+ """hugo.toml must define the live Spotify podcast URL."""
13
config = (REPO_ROOT / "hugo.toml").read_text(encoding="utf-8")
14
assert "podcast_url" in config
15
+ assert 'podcast_url = "https://open.spotify.com/show/033xdn5nDMoCWxB3bss2dB"' in config
16
17
18
def test_footer_conditionally_renders_podcast_link() -> None:
@@ -29,8 +30,23 @@ def test_footer_conditionally_renders_podcast_link() -> None:
30
), "Footer podcast link must use dot context piped through safeURL with target=_blank and rel=noopener noreferrer"
31
32
32
-def test_podcast_url_defaults_to_empty() -> None:
33
- """When podcast_url is empty, no link should render (Hugo `with` handles this)."""
34
- config = (REPO_ROOT / "hugo.toml").read_text(encoding="utf-8")
35
- # The default value must be empty string
36
- assert 'podcast_url = ""' in config
33
+def test_report_shortcuts_link_weekly_reports_to_spotify() -> None:
34
+ """Weekly report shortcuts must expose the configured Spotify URL."""
35
+ shortcuts = (REPO_ROOT / "layouts" / "partials" / "report-shortcuts.html").read_text(encoding="utf-8")
36
+ assert 'site.Params.podcast_url | default "" | strings.TrimSpace' in shortcuts
37
+ assert "if and $isWeekly $podcastURL" in shortcuts
38
+ assert re.search(
39
+ r'<a\s+href="{{\s*\$podcastURL\s*\|\s*safeURL\s*}}"[^>]*target="_blank"[^>]*rel="noopener noreferrer"[^>]*>Spotify</a>',
40
+ shortcuts,
41
+ ), "Weekly shortcuts must link to Spotify with target=_blank and rel=noopener noreferrer"
42
+
43
+
44
+def test_header_exposes_spotify_button_when_podcast_url_is_configured() -> None:
45
+ """Header actions must expose the configured Spotify URL."""
46
+ header = (REPO_ROOT / "layouts" / "partials" / "header.html").read_text(encoding="utf-8")
47
+ assert 'site.Params.podcast_url | default "" | strings.TrimSpace' in header
48
+ assert 'class="icon-button spotify-button"' in header
49
+ assert re.search(
50
+ r'<a\s+class="icon-button spotify-button"\s+href="{{\s*\.\s*\|\s*safeURL\s*}}"[^>]*target="_blank"[^>]*rel="noopener noreferrer"[^>]*aria-label="Spotify"',
51
+ header,
52
+ ), "Header Spotify button must use configured URL with target=_blank and rel=noopener noreferrer"
tests/test_site_icons.py
new
+50
@@ -0,0 +1,50 @@
1
+"""Tests for site icon metadata."""
2
+
3
+from __future__ import annotations
4
+
5
+import json
6
+from pathlib import Path
7
+
8
+REPO_ROOT = Path(__file__).resolve().parent.parent
9
+
10
+
11
+def test_head_favicons_use_claracle_image_asset() -> None:
12
+ """Head template must derive browser icons from the Claracle image asset."""
13
+ head = (REPO_ROOT / "layouts" / "partials" / "head.html").read_text(encoding="utf-8")
14
+ assert 'resources.Get "images/claracle.jpeg"' in head
15
+ assert '.Fill "16x16 png"' in head
16
+ assert '.Fill "32x32 png"' in head
17
+ assert '.Fill "180x180 png"' in head
18
+ assert "/images/squadscope-icon.svg" not in head
19
+ assert "/favicon-16x16.png" not in head
20
+ assert "/favicon-32x32.png" not in head
21
+ assert "/apple-touch-icon.png" not in head
22
+
23
+
24
+def test_web_manifest_uses_claracle_image_icon() -> None:
25
+ """Web app manifest must point at compatible PNG site icons."""
26
+ manifest = json.loads((REPO_ROOT / "static" / "site.webmanifest").read_text(encoding="utf-8"))
27
+ icons = manifest["icons"]
28
+ assert icons == [
29
+ {"src": "/android-chrome-192x192.png", "sizes": "192x192", "type": "image/png"},
30
+ {
31
+ "src": "/android-chrome-512x512.png",
32
+ "sizes": "512x512",
33
+ "type": "image/png",
34
+ "purpose": "any maskable",
35
+ },
36
+ ]
37
+
38
+
39
+def test_static_icon_png_files_exist() -> None:
40
+ """All browser and app icon files must exist as PNG files."""
41
+ for icon_path in (
42
+ "favicon-16x16.png",
43
+ "favicon-32x32.png",
44
+ "apple-touch-icon.png",
45
+ "android-chrome-192x192.png",
46
+ "android-chrome-512x512.png",
47
+ ):
48
+ icon = REPO_ROOT / "static" / icon_path
49
+ assert icon.is_file()
50
+ assert icon.read_bytes().startswith(b"\x89PNG\r\n\x1a\n")
\ No newline at end of file