← back to Stars of Design
starsofdesign: smoke test harness — test/smoke.js (zero deps, pure node) hits every public route and asserts 200/3xx + key DOM markers (canonical, WebSite/Person/Organization/BreadcrumbList JSON-LD, footer privacy/terms links). 40 checks. npm test wired. 40/40 pass on initial run. Mirrors asim smoke harness.
9d16cb331467f398573121f8e51d7dc3990655cb · 2026-05-12 19:50:16 -0700 · Steve Abrams
Files touched
M package.jsonA test/smoke.js
Diff
commit 9d16cb331467f398573121f8e51d7dc3990655cb
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Tue May 12 19:50:16 2026 -0700
starsofdesign: smoke test harness — test/smoke.js (zero deps, pure node) hits every public route and asserts 200/3xx + key DOM markers (canonical, WebSite/Person/Organization/BreadcrumbList JSON-LD, footer privacy/terms links). 40 checks. npm test wired. 40/40 pass on initial run. Mirrors asim smoke harness.
---
package.json | 4 +-
test/smoke.js | 129 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 132 insertions(+), 1 deletion(-)
diff --git a/package.json b/package.json
index afb1fff..5894655 100644
--- a/package.json
+++ b/package.json
@@ -6,7 +6,9 @@
"main": "server.js",
"scripts": {
"start": "node server.js",
- "dev": "node server.js"
+ "dev": "node server.js",
+ "test": "node test/smoke.js",
+ "smoke": "node test/smoke.js"
},
"dependencies": {
"cookie-parser": "^1.4.7",
diff --git a/test/smoke.js b/test/smoke.js
new file mode 100644
index 0000000..21fba75
--- /dev/null
+++ b/test/smoke.js
@@ -0,0 +1,129 @@
+#!/usr/bin/env node
+'use strict';
+// Smoke test for starsofdesign. Hits every public route + verifies key DOM
+// markers (canonical, JSON-LD type, BreadcrumbList, footer links). Zero deps.
+//
+// Usage:
+// node test/smoke.js
+// SOD_URL=https://… node test/smoke.js
+
+const http = require('http');
+const https = require('https');
+const { URL } = require('url');
+
+const BASE = process.env.SOD_URL || 'http://127.0.0.1:9928';
+const TIMEOUT_MS = 8_000;
+
+function fetch(path) {
+ const u = new URL(path, BASE);
+ const lib = u.protocol === 'https:' ? https : http;
+ return new Promise((resolve, reject) => {
+ const req = lib.get(u, { timeout: TIMEOUT_MS }, (res) => {
+ let body = '';
+ res.on('data', (c) => (body += c));
+ res.on('end', () => resolve({ status: res.statusCode, headers: res.headers, body }));
+ });
+ req.on('error', reject);
+ req.on('timeout', () => { req.destroy(new Error('timeout')); });
+ });
+}
+
+const checks = [];
+let pass = 0, fail = 0;
+function check(name, cond, hint = '') {
+ if (cond) { pass++; checks.push({ name, ok: true }); }
+ else { fail++; checks.push({ name, ok: false, hint }); }
+}
+
+(async () => {
+ console.log(`starsofdesign smoke — ${BASE}\n`);
+
+ // 1. home
+ let r = await fetch('/');
+ check('home: 200', r.status === 200);
+ check('home: title', /Stars of Design/.test(r.body));
+ check('home: canonical', /rel="canonical"/.test(r.body));
+ check('home: WebSite schema',/"@type":"WebSite"/.test(r.body));
+ check('home: SearchAction', /SearchAction/.test(r.body));
+ check('home: favicon', /rel="icon"/.test(r.body));
+ check('home: footer Privacy',/\/privacy/.test(r.body));
+ check('home: footer Terms', /\/terms/.test(r.body));
+
+ // 2. /designers
+ r = await fetch('/designers');
+ check('designers: 200', r.status === 200);
+ check('designers: canonical',/rel="canonical"/.test(r.body));
+
+ // 3. /firms
+ r = await fetch('/firms');
+ check('firms: 200', r.status === 200);
+ check('firms: canonical', /rel="canonical"/.test(r.body));
+
+ // 4. /clients
+ r = await fetch('/clients');
+ check('clients: 200', r.status === 200);
+
+ // 5. /feed
+ r = await fetch('/feed');
+ check('feed: 200', r.status === 200);
+
+ // 6. /videos
+ r = await fetch('/videos');
+ check('videos: 200', r.status === 200);
+
+ // 7. detail pages
+ r = await fetch('/designers/dorothy-draper');
+ check('designer/:slug: 200', r.status === 200);
+ check('designer/:slug: Person', /"@type":"Person"/.test(r.body));
+ check('designer/:slug: Breadcrumb', /"@type":"BreadcrumbList"/.test(r.body));
+
+ r = await fetch('/firms/nick-olsen-style');
+ check('firm/:slug: 200', r.status === 200);
+ check('firm/:slug: Organization', /"@type":"Organization"/.test(r.body));
+ check('firm/:slug: Breadcrumb', /"@type":"BreadcrumbList"/.test(r.body));
+
+ r = await fetch('/clients/nick-olsen-style-firm');
+ check('client/:slug: 200', r.status === 200);
+ check('client/:slug: Person', /"@type":"Person"/.test(r.body));
+ check('client/:slug: Breadcrumb', /"@type":"BreadcrumbList"/.test(r.body));
+
+ // 8. /api/stats
+ r = await fetch('/api/stats');
+ check('api/stats: 200', r.status === 200);
+ check('api/stats: designers field', /"designers":/.test(r.body));
+
+ // 9. /sitemap.xml + /robots.txt + /favicon.ico
+ r = await fetch('/sitemap.xml');
+ check('sitemap: 200', r.status === 200);
+ check('sitemap: urlset', /<urlset/.test(r.body));
+ check('sitemap: has /privacy', /<loc>[^<]*\/privacy<\/loc>/.test(r.body));
+ check('sitemap: has /terms', /<loc>[^<]*\/terms<\/loc>/.test(r.body));
+ r = await fetch('/robots.txt');
+ check('robots: 200', r.status === 200);
+ r = await fetch('/favicon.ico');
+ check('favicon: 200', r.status === 200);
+ check('favicon: image/svg', /image\/svg/.test(r.headers['content-type'] || ''));
+
+ // 10. legal pages
+ r = await fetch('/about');
+ check('about: 200', r.status === 200);
+ check('about: Stars of Design h1', /About Stars of Design/.test(r.body));
+ r = await fetch('/privacy');
+ check('privacy: 200', r.status === 200);
+ check('privacy: h1', /<h1[^>]*>Privacy<\/h1>/.test(r.body));
+ r = await fetch('/terms');
+ check('terms: 200', r.status === 200);
+ check('terms: h1', /<h1[^>]*>Terms of use<\/h1>/.test(r.body));
+
+ // 11. /submit
+ r = await fetch('/submit');
+ check('submit: 200', r.status === 200);
+
+ // Report
+ console.log(checks.map(c => (c.ok ? '✓' : '✗') + ' ' + c.name + (c.hint ? ' (' + c.hint + ')' : '')).join('\n'));
+ console.log(`\n${pass} passed, ${fail} failed`);
+ process.exit(fail === 0 ? 0 : 1);
+})().catch((e) => {
+ console.error('smoke harness error:', e.message);
+ process.exit(2);
+});
← 00a2a60 starsofdesign: GET /privacy + /terms — minimal legal pages (
·
back to Stars of Design
·
starsofdesign: GET /api — JSON discovery doc for public API 6e94cda →