diff --git a/README.md b/README.md index f2d257cb..7ec3f6fc 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ # Caramel - Trusted Coupon Finder **Caramel is the open‑source, privacy‑first alternative to Honey.** -It automatically tests codes at checkout, never sells your data, and never overwrites creators’ affiliate links. Today Caramel already supports more than **5 000 stores**. You can find even more information at https://grabcaramel.com. +It automatically tests codes at checkout, never sells your data, and never overwrites creators’ affiliate links. Today Caramel already supports more than **4,000 stores**. You can find even more information at https://grabcaramel.com. caramel-banner @@ -14,8 +14,8 @@ It automatically tests codes at checkout, never sells your data, and never overw ## Why choose Caramel? - **100% open source** – every release is on GitHub for public audit -- **Privacy first** – the extension only asks for tab access while you shop, nothing more -- **Real‑time savings meter** – see exactly how much a coupon saves you before you commit +- **Privacy first** – the extension asks only for the permissions it needs to find the store you're on, read the checkout page and apply codes (tabs, storage, sign-in) – no ads, no third‑party trackers +- **Real‑time savings meter** – see exactly how much each code saved as it is tested - **Creator‑friendly** – Caramel keeps existing affiliate links intact so influencers get the credit they deserve - **Community‑powered** – join our Discord, file issues, or open PRs to make Caramel even sweeter diff --git a/apps/caramel-app/e2e/home.spec.ts b/apps/caramel-app/e2e/home.spec.ts index 15115d7e..8e16c9bf 100644 --- a/apps/caramel-app/e2e/home.spec.ts +++ b/apps/caramel-app/e2e/home.spec.ts @@ -55,7 +55,7 @@ test.describe('Home Page - Critical Sections', () => { page.getByRole('heading', { name: 'Creator-Friendly' }), ).toBeVisible() await expect( - page.getByRole('heading', { name: 'Lightning Fast' }), + page.getByRole('heading', { name: 'Fast & Lightweight' }), ).toBeVisible() await expect( page.getByRole('heading', { name: 'Cross-Browser Support' }), diff --git a/apps/caramel-app/e2e/pages.spec.ts b/apps/caramel-app/e2e/pages.spec.ts index 6daa747c..dd8f1fab 100644 --- a/apps/caramel-app/e2e/pages.spec.ts +++ b/apps/caramel-app/e2e/pages.spec.ts @@ -7,7 +7,7 @@ test.describe('Coupons Page', () => { await expect( page.getByRole('heading', { level: 1, - name: /verified coupon codes/i, + name: /today's coupon codes/i, }), ).toBeVisible() await expect(page.getByText(/browse.*coupon/i).first()).toBeVisible() diff --git a/apps/caramel-app/e2e/seo-regression.spec.ts b/apps/caramel-app/e2e/seo-regression.spec.ts index ac55dffa..a686f408 100644 --- a/apps/caramel-app/e2e/seo-regression.spec.ts +++ b/apps/caramel-app/e2e/seo-regression.spec.ts @@ -223,6 +223,33 @@ test.describe('SEO regression gate (raw server HTML)', () => { expect(types).toContain('"@type":"SoftwareApplication"') }) + // Claim-integrity pin (2026-09-11): the two catalog figures the site is + // allowed to quote are "4,000+ stores" and "100,000+ coupon codes", + // rounded DOWN from prod on that date (4,314 store pages in sitemap.xml, + // 107,827 from GET /api/coupons?limit=1 → total). Both are static copy + // (heroStats.ts, FaqSection.tsx, PricingSection.tsx, SupportedSection.tsx) + // so this holds in both e2e contexts. The negative pin is the retired + // "139,000" FAQ number that outlived the stats-census change for six + // weeks — a figure must never drift back silently. Re-verify the live + // counts before changing either string; never round up. + test('home raw HTML quotes only the reconciled catalog figures', async ({ + page, + }) => { + const html = await (await page.request.get('/')).text() + expect(html, 'store count must read 4,000+').toContain('4,000+') + expect(html, 'coupon count must read 100,000').toContain( + 'over 100,000 coupon codes across more than 4,000 online stores', + ) + expect( + html, + 'retired FAQ figure 139,000 must not reappear', + ).not.toContain('139,000') + expect( + html, + 'superseded store count 3,000+ must not reappear', + ).not.toContain('3,000+') + }) + test('robots.txt honours the env-aware indexing contract', async ({ baseURL, page, diff --git a/apps/caramel-app/src/app/(marketing)/coupons/[store]/page.tsx b/apps/caramel-app/src/app/(marketing)/coupons/[store]/page.tsx index c4927fe9..df150e50 100644 --- a/apps/caramel-app/src/app/(marketing)/coupons/[store]/page.tsx +++ b/apps/caramel-app/src/app/(marketing)/coupons/[store]/page.tsx @@ -92,7 +92,7 @@ export async function generateMetadata({ // here or these pages share links with no preview image at all. const banner = `${baseUrl}/caramel_banner.png` const title = `${base} Coupons & Promo Codes | Caramel` - const description = `Find verified ${base} coupon codes, promo codes, and discounts. Updated daily.` + const description = `Find ${base} coupon codes, promo codes, and discounts — refreshed as new codes are found.` // Canonical always points at the NORMALIZED base-domain URL: this route // serves the same content for /coupons/www.nike.com, /coupons/shop.nike.com // and /coupons/nike.com, so every variant must canonicalize to ONE URL or @@ -238,9 +238,9 @@ export default async function StoreCouponsPage({ codes for that store from its own catalog, tries them in the promo-code field, and keeps the one with the biggest discount. It never replaces affiliate links, and it reports - back only whether a code worked — with no account - information attached — so code rankings stay accurate for - every shopper. + back whether a code worked (linked to your account only when + you're signed in) so code rankings stay accurate for every + shopper.

diff --git a/apps/caramel-app/src/app/(marketing)/coupons/page.tsx b/apps/caramel-app/src/app/(marketing)/coupons/page.tsx index 53f1557d..0cdde37a 100644 --- a/apps/caramel-app/src/app/(marketing)/coupons/page.tsx +++ b/apps/caramel-app/src/app/(marketing)/coupons/page.tsx @@ -17,7 +17,7 @@ const PAGE_SIZE = 5 const title = 'Coupon Codes & Promo Codes for Top Stores | Caramel' const description = - 'Browse verified coupon codes and promo codes for your favorite stores. Caramel finds and applies the best deals automatically at checkout.' + 'Browse coupon codes and promo codes for your favorite stores. Caramel finds and applies the best deals automatically at checkout.' const canonicalUrl = 'https://grabcaramel.com/coupons' const banner = `${BASE_URL}/caramel_banner.png` @@ -64,7 +64,7 @@ export default async function CouponsPage() { const structuredData = { '@context': 'https://schema.org', '@type': 'ItemList', - name: 'Verified coupon codes and promo codes', + name: 'Coupon codes and promo codes', url: canonicalUrl, numberOfItems: total, itemListElement: couponsWithSignals.map((coupon, idx) => ({ @@ -82,8 +82,8 @@ export default async function CouponsPage() { initialCoupons={couponsWithSignals} initialTotal={total} disableInitialFetch - heroTitle="Today's Verified Coupon Codes" - heroSubtitle="Browse verified coupon codes, promo codes, and offers for your favorite stores." + heroTitle="Today's Coupon Codes" + heroSubtitle="Browse coupon codes, promo codes, and offers for your favorite stores." />