diff --git a/hugo/hugo.toml b/hugo/hugo.toml
index b66c9eca7..13f0e960f 100644
--- a/hugo/hugo.toml
+++ b/hugo/hugo.toml
@@ -88,6 +88,14 @@ enableRobotsTXT = false # we ship our own via the robots output format
[markup.goldmark.renderer]
unsafe = true
[markup.goldmark.parser]
+ # #directive-hotspots: a standalone image with a following block-attribute
+ # line (`\n{hotspots=…}`) only has its attributes delivered to the
+ # image render hook's .Attributes when the image is NOT wrapped in a
.
+ # With the default (true) goldmark wraps it and the attribute block never
+ # attaches, so `{hotspots=…}` leaked as literal text and no overlay rendered.
+ # The render hooks already emit /
wrappers, so dropping the
+ # redundant
is also cleaner HTML. No CSS depends on p>img (checked).
+ wrapStandAloneImageWithinParagraph = false
[markup.goldmark.parser.attribute]
block = true
title = true
diff --git a/scripts/fetch-tutorials.ts b/scripts/fetch-tutorials.ts
index 6f68b5b48..c0b8835fd 100644
--- a/scripts/fetch-tutorials.ts
+++ b/scripts/fetch-tutorials.ts
@@ -651,6 +651,8 @@ export function writeHugoPage(
variables?: import('./parsers/variables.js').TutorialVariable[],
syncKeysUsed?: Set,
syncGroups?: import('./parsers/types.js').TutorialFrontmatter['syncGroups'],
+ testedWith?: import('./parsers/types.js').TutorialFrontmatter['testedWith'],
+ requires?: import('./parsers/types.js').TutorialFrontmatter['requires'],
): void {
const content = renderHugoFrontmatter({
slug,
@@ -679,6 +681,8 @@ export function writeHugoPage(
variables,
syncKeysUsed,
syncGroups,
+ testedWith,
+ requires,
})
mkdirSync(outputDir, { recursive: true })
@@ -1721,6 +1725,8 @@ async function main() {
composed.variables,
composed.syncKeysUsed.size > 0 ? composed.syncKeysUsed : undefined,
composed.syncGroups,
+ composed.testedWith,
+ composed.requires,
)
authorRows.push({
authorProfile: frontmatter.author_profile ?? '',
diff --git a/scripts/parsers/__tests__/render-frontmatter-testedwith.test.ts b/scripts/parsers/__tests__/render-frontmatter-testedwith.test.ts
new file mode 100644
index 000000000..6bc6b9080
--- /dev/null
+++ b/scripts/parsers/__tests__/render-frontmatter-testedwith.test.ts
@@ -0,0 +1,63 @@
+import { describe, it, expect } from 'vitest'
+import { parse as parseYaml } from 'yaml'
+import { renderHugoFrontmatter } from '../render-frontmatter.js'
+import type { RenderHugoFrontmatterArgs } from '../render-frontmatter.js'
+import type { TutorialNavEntry } from '../types.js'
+
+// #directive-demos: testedWith + requires were parsed, composed, and accepted by
+// renderHugoFrontmatter — but writeHugoPage (fetch-tutorials.ts) never forwarded
+// them, so .Params.testedWith was always empty and the "Tested with" panel never
+// rendered. The only prior testedWith test covered checkTestedWithFreshness, not
+// the generated frontmatter — which is why the drop went unnoticed. These lock
+// the rendered-frontmatter contract the u1 layout + prereq card depend on.
+
+const minimalNav: TutorialNavEntry = {
+ slug: 'demo', title: 'Demo', description: '', time: 5, level: 'beginner',
+ stepCount: 1, primaryTag: '', displayTags: [], displayTagSlugs: [],
+ repo: 'sap-tutorials/demo', branch: 'main', prev: null, next: null,
+}
+
+function fmFrom(content: string): Record {
+ const m = content.match(/^---\r?\n([\s\S]*?)\r?\n---/)
+ if (!m) throw new Error('no frontmatter')
+ return parseYaml(m[1]) as Record
+}
+
+describe('renderHugoFrontmatter — testedWith + requires', () => {
+ const baseArgs: RenderHugoFrontmatterArgs = {
+ slug: 'demo', title: 'Demo', description: '', time: 5, level: 'beginner',
+ tags: [], primaryTag: '', author: 'Thomas Jung',
+ authorProfile: 'https://github.com/jung-thomas', youWillLearn: [],
+ prerequisites: '', steps: [{ number: 1, title: 'Step 1', content: '' }],
+ nav: minimalNav, lastUpdated: '', createdAt: '', contributors: [],
+ }
+
+ it('emits testedWith map into the frontmatter (→ .Params.testedWith)', () => {
+ const content = renderHugoFrontmatter({
+ ...baseArgs,
+ testedWith: { '@sap/cds': '10.x', node: '22' },
+ })
+ expect(content).toContain('testedWith:')
+ const fm = fmFrom(content)
+ expect(fm.testedWith).toEqual({ '@sap/cds': '10.x', node: '22' })
+ })
+
+ it('emits requires list into the frontmatter (→ .Params.requires)', () => {
+ const content = renderHugoFrontmatter({
+ ...baseArgs,
+ requires: ['hana-cloud-mission-trial-1'],
+ })
+ const fm = fmFrom(content)
+ expect(fm.requires).toEqual(['hana-cloud-mission-trial-1'])
+ })
+
+ it('omits testedWith when empty/unset', () => {
+ expect('testedWith' in fmFrom(renderHugoFrontmatter(baseArgs))).toBe(false)
+ expect('testedWith' in fmFrom(renderHugoFrontmatter({ ...baseArgs, testedWith: {} }))).toBe(false)
+ })
+
+ it('omits requires when empty/unset', () => {
+ expect('requires' in fmFrom(renderHugoFrontmatter(baseArgs))).toBe(false)
+ expect('requires' in fmFrom(renderHugoFrontmatter({ ...baseArgs, requires: [] }))).toBe(false)
+ })
+})