← back to Robet Site
round2 item (2): JSON-LD structured data (Person + ScheduleAction → booking URL) + validity test (9/9); CSP-exempt data block
19f4d34aae884dcafbbbacecaabac4245408ffbd · 2026-06-29 23:30:54 -0700 · Steve
Files touched
M public/index.htmlM tests/api.test.mjs
Diff
commit 19f4d34aae884dcafbbbacecaabac4245408ffbd
Author: Steve <steve@designerwallcoverings.com>
Date: Mon Jun 29 23:30:54 2026 -0700
round2 item (2): JSON-LD structured data (Person + ScheduleAction → booking URL) + validity test (9/9); CSP-exempt data block
---
public/index.html | 25 +++++++++++++++++++++++++
tests/api.test.mjs | 10 ++++++++++
2 files changed, 35 insertions(+)
diff --git a/public/index.html b/public/index.html
index 9c47736..6e37d60 100644
--- a/public/index.html
+++ b/public/index.html
@@ -25,6 +25,31 @@
<meta name="twitter:description" content="Pick a time, tell me what you need, get a straight answer." />
<meta name="twitter:image" content="https://robert.agentabrams.com/og.png" />
+ <!-- structured data (data block; not executed → exempt from CSP script-src) -->
+ <script type="application/ld+json">
+ {
+ "@context": "https://schema.org",
+ "@type": "Person",
+ "name": "Robert",
+ "url": "https://robert.agentabrams.com/",
+ "image": "https://robert.agentabrams.com/og.png",
+ "email": "info@designerwallcoverings.com",
+ "description": "Book a meeting with Robert. Pick a time, get a straight answer.",
+ "potentialAction": {
+ "@type": "ScheduleAction",
+ "name": "Book a meeting",
+ "target": {
+ "@type": "EntryPoint",
+ "urlTemplate": "https://venturacorridor.com/book?owner=info@designerwallcoverings.com",
+ "actionPlatform": [
+ "https://schema.org/DesktopWebPlatform",
+ "https://schema.org/MobileWebPlatform"
+ ]
+ }
+ }
+ }
+ </script>
+
<link rel="stylesheet" href="/styles.css" />
</head>
<body>
diff --git a/tests/api.test.mjs b/tests/api.test.mjs
index 0064db3..3deb6aa 100644
--- a/tests/api.test.mjs
+++ b/tests/api.test.mjs
@@ -85,6 +85,16 @@ test('contact: honeypot + validation + rate limit', async () => {
assert.ok(saw429, 'rate limit should trigger 429 on burst');
});
+test('homepage embeds valid JSON-LD with a booking action', async () => {
+ const html = await (await fetch(`${BASE}/`)).text();
+ const m = html.match(/<script type="application\/ld\+json">([\s\S]*?)<\/script>/);
+ assert.ok(m, 'ld+json block present');
+ const j = JSON.parse(m[1]); // throws if invalid → test fails
+ assert.equal(j['@type'], 'Person');
+ assert.equal(j.potentialAction['@type'], 'ScheduleAction');
+ assert.match(j.potentialAction.target.urlTemplate, /venturacorridor\.com\/book/);
+});
+
test('malformed JSON body → clean JSON error, not HTML stack', async () => {
const r = await fetch(`${BASE}/api/contact`, {
method: 'POST', headers: { 'Content-Type': 'application/json' }, body: '{bad json',
← de5f5ab loop ledger: round2 item (3) done + deployed
·
back to Robet Site
·
loop ledger: round2 item (2) done + deployed 7a5ae6e →