openvibers/openvibe.coupons Commits
Code pages describe themselves (code, what for, shop, status, expiry, minimum spend, recent reports) instead of the same 'Validity unknown. Expiry unknown.' on every page (#76)
Changes
@@@ -34,6 +34,22 @@ const { SCOPES } = require('../auth/capabilities');const COUPON_ID_RE = /^cpn_[0-9A-HJKMNP-TV-Z]{26}$/;++/**++ * A code page's meta description, from the code's own facts and never its submitted description: the code, what it++ * is for and where, then its status, expiry, minimum spend, new-customer rule and recent reports, kept under 160++ * characters. It was "Validity unknown. Expiry unknown." on nearly every code page, the same line 156 times.++ */++function couponDescription(v, m) {++ const facts = [pages.STATUS_LABEL[v.status], v.expiry.known ? `expires ${v.expiry.expires_at.slice(0, 10)}` : 'no expiry stated'];++ const min = (v.restrictions || []).find((r) => r.kind === 'min_spend');++ if (min) facts.push(`minimum spend ${min.amount}`);++ if ((v.restrictions || []).some((r) => r.kind === 'new_customers_only')) facts.push('new customers only');++ const { worked = 0, failed = 0, window_days: days } = v.reports || {};++ if (worked || failed) facts.push(`${worked} worked and ${failed} failed in ${days} days`);++ const text = `Coupon code ${v.code} for ${v.title} at ${m.name}: ${facts.join(', ')}.`;++ return text.length <= 160 ? text : `${text.slice(0, 159).replace(/\s+\S*$/, '')}…`;++}++function createPublicRoutes(ctx) {const { config, store, merchants, coupons, reports, watches, installs, publication, viewers, moderation, actorLimits } = ctx;const router = express.Router();@@@ -267,7 +283,7 @@ function createPublicRoutes(ctx) {const flashMsg = FLASH[req.query.done] || null;send(req, res, 200, {title: `${v.code} — ${v.title} at ${m.name}`,-- description: `${pages.STATUS_LABEL[v.status]}. ${v.expiry.known ? `Expires ${v.expiry.expires_at.slice(0, 10)}.` : 'Expiry unknown.'}`,++ description: couponDescription(v, m),decision: publication.couponDecision(c, m), canonical: v.url, jsonLd: jsonLd(m, v),body: pages.couponPage({ v, m, history: await coupons.history(c.id), viewer: req.viewer, csrf: csrf(req), back: `/c/${c.id}`, flashMsg }),}, { cacheable: !flashMsg });@@@ -80,6 +80,10 @@ const { boot, check, done } = require('./helpers/boot');const page = (await t.get(`/c/${codeId}`)).text;assert.strictEqual((page.match(/<h1\b/g) || []).length, 1, 'the code page has one h1: its title');assert.match(page, /<h1 class="coupon-title">/);++ // Its description is its own facts, not the same "Validity unknown. Expiry unknown." as every other code.++ const desc = (page.match(/<meta name="description" content="([^"]*)">/) || [])[1] || '';++ assert.match(desc, /^Coupon code VEG5 for .+ at .+: (Validity unknown|Reported working|Reported not working), (expires \d{4}-\d{2}-\d{2}|no expiry stated)[^"]*\.$/, desc);++ assert.ok(desc.length <= 160, desc);const blocks = [...page.matchAll(/<script type="application\/ld\+json">([\s\S]*?)<\/script>/g)].map((m) => JSON.parse(m[1]));assert.deepStrictEqual(blocks.map((b) => b['@type']).sort(), ['BreadcrumbList', 'WebPage']);const json = (await t.get(`/c/${codeId}.json`)).json();
@@ -34,6 +34,22 @@ const { SCOPES } = require('../auth/capabilities');
@@ -34,6 +34,22 @@ const { SCOPES } = require('../auth/capabilities');
const COUPON_ID_RE = /^cpn_[0-9A-HJKMNP-TV-Z]{26}$/;
const COUPON_ID_RE = /^cpn_[0-9A-HJKMNP-TV-Z]{26}$/;
+/**
+ * A code page's meta description, from the code's own facts and never its submitted description: the code, what it
+ * is for and where, then its status, expiry, minimum spend, new-customer rule and recent reports, kept under 160
+ * characters. It was "Validity unknown. Expiry unknown." on nearly every code page, the same line 156 times.
+ */
+function couponDescription(v, m) {
+ const facts = [pages.STATUS_LABEL[v.status], v.expiry.known ? `expires ${v.expiry.expires_at.slice(0, 10)}` : 'no expiry stated'];
+ const min = (v.restrictions || []).find((r) => r.kind === 'min_spend');
+ if (min) facts.push(`minimum spend ${min.amount}`);
+ if ((v.restrictions || []).some((r) => r.kind === 'new_customers_only')) facts.push('new customers only');
+ const { worked = 0, failed = 0, window_days: days } = v.reports || {};
+ if (worked || failed) facts.push(`${worked} worked and ${failed} failed in ${days} days`);
+ const text = `Coupon code ${v.code} for ${v.title} at ${m.name}: ${facts.join(', ')}.`;
+ return text.length <= 160 ? text : `${text.slice(0, 159).replace(/\s+\S*$/, '')}…`;
+}
+
function createPublicRoutes(ctx) {
function createPublicRoutes(ctx) {
const { config, store, merchants, coupons, reports, watches, installs, publication, viewers, moderation, actorLimits } = ctx;
const { config, store, merchants, coupons, reports, watches, installs, publication, viewers, moderation, actorLimits } = ctx;
const router = express.Router();
const router = express.Router();
@@ -267,7 +283,7 @@ function createPublicRoutes(ctx) {
@@ -267,7 +283,7 @@ function createPublicRoutes(ctx) {
const flashMsg = FLASH[req.query.done] || null;
const flashMsg = FLASH[req.query.done] || null;
send(req, res, 200, {
send(req, res, 200, {
title: `${v.code} — ${v.title} at ${m.name}`,
title: `${v.code} — ${v.title} at ${m.name}`,
- description: `${pages.STATUS_LABEL[v.status]}. ${v.expiry.known ? `Expires ${v.expiry.expires_at.slice(0, 10)}.` : 'Expiry unknown.'}`,
+ description: couponDescription(v, m),
decision: publication.couponDecision(c, m), canonical: v.url, jsonLd: jsonLd(m, v),
decision: publication.couponDecision(c, m), canonical: v.url, jsonLd: jsonLd(m, v),
body: pages.couponPage({ v, m, history: await coupons.history(c.id), viewer: req.viewer, csrf: csrf(req), back: `/c/${c.id}`, flashMsg }),
body: pages.couponPage({ v, m, history: await coupons.history(c.id), viewer: req.viewer, csrf: csrf(req), back: `/c/${c.id}`, flashMsg }),
}, { cacheable: !flashMsg });
}, { cacheable: !flashMsg });
@@ -80,6 +80,10 @@ const { boot, check, done } = require('./helpers/boot');
@@ -80,6 +80,10 @@ const { boot, check, done } = require('./helpers/boot');
const page = (await t.get(`/c/${codeId}`)).text;
const page = (await t.get(`/c/${codeId}`)).text;
assert.strictEqual((page.match(/<h1\b/g) || []).length, 1, 'the code page has one h1: its title');
assert.strictEqual((page.match(/<h1\b/g) || []).length, 1, 'the code page has one h1: its title');
assert.match(page, /<h1 class="coupon-title">/);
assert.match(page, /<h1 class="coupon-title">/);
+ // Its description is its own facts, not the same "Validity unknown. Expiry unknown." as every other code.
+ const desc = (page.match(/<meta name="description" content="([^"]*)">/) || [])[1] || '';
+ assert.match(desc, /^Coupon code VEG5 for .+ at .+: (Validity unknown|Reported working|Reported not working), (expires \d{4}-\d{2}-\d{2}|no expiry stated)[^"]*\.$/, desc);
+ assert.ok(desc.length <= 160, desc);
const blocks = [...page.matchAll(/<script type="application\/ld\+json">([\s\S]*?)<\/script>/g)].map((m) => JSON.parse(m[1]));
const blocks = [...page.matchAll(/<script type="application\/ld\+json">([\s\S]*?)<\/script>/g)].map((m) => JSON.parse(m[1]));
assert.deepStrictEqual(blocks.map((b) => b['@type']).sort(), ['BreadcrumbList', 'WebPage']);
assert.deepStrictEqual(blocks.map((b) => b['@type']).sort(), ['BreadcrumbList', 'WebPage']);
const json = (await t.get(`/c/${codeId}.json`)).json();
const json = (await t.get(`/c/${codeId}.json`)).json();