From d58cac4faa99e5d798572c4ead5082ca1587eb2b Mon Sep 17 00:00:00 2001 From: dhananjay6561 Date: Thu, 16 Apr 2026 18:10:23 +0530 Subject: [PATCH 1/4] fix(seo): add hub pages, sitemap/redirect updates, and schema/a11y fixes --- components/subscribe-newsletter.tsx | 5 +- next.config.js | 10 +++ pages/case-studies/index.tsx | 102 ++++++++++++++++++++++++++ pages/glossary/index.tsx | 104 +++++++++++++++++++++++++++ pages/integrations/index.tsx | 107 ++++++++++++++++++++++++++++ pages/sitemap.xml.tsx | 49 +++++++++++-- pages/solutions/index.tsx | 102 ++++++++++++++++++++++++++ 7 files changed, 474 insertions(+), 5 deletions(-) create mode 100644 pages/case-studies/index.tsx create mode 100644 pages/glossary/index.tsx create mode 100644 pages/integrations/index.tsx create mode 100644 pages/solutions/index.tsx diff --git a/components/subscribe-newsletter.tsx b/components/subscribe-newsletter.tsx index 55cfc7e2..f1f4a16a 100644 --- a/components/subscribe-newsletter.tsx +++ b/components/subscribe-newsletter.tsx @@ -120,7 +120,10 @@ export default function SubscribeNewsletter(props: { isSmallScreen: Boolean }) { return (
- Subscribe to Keploy newsletter + Cartoon bunny mascot holding a letter near the Keploy newsletter signup form
({ + "@type": "ListItem", + position: index + 1, + item: { + "@type": "CreativeWork", + name: `${study.company} case study`, + description: study.outcome, + }, + })), + }, + }; + + return ( + + + {pageTitle} + +
+ +
+

Case Studies

+

{pageDescription}

+
    + {caseStudies.map((study) => ( +
  • +

    {study.company}

    +

    {study.outcome}

    +
  • + ))} +
+
+
+ + ); +} + +export async function getStaticProps() { + return { + props: { + preview: false, + }, + revalidate: 600, + }; +} \ No newline at end of file diff --git a/pages/glossary/index.tsx b/pages/glossary/index.tsx new file mode 100644 index 00000000..797475ca --- /dev/null +++ b/pages/glossary/index.tsx @@ -0,0 +1,104 @@ +import Head from "next/head"; +import Layout from "../../components/layout"; +import Header from "../../components/header"; +import Container from "../../components/container"; +import { HOME_OG_IMAGE_URL } from "../../lib/constants"; +import { getBreadcrumbListSchema, SITE_URL } from "../../lib/structured-data"; + +const glossaryTerms = [ + { + name: "API Regression Testing", + description: + "A testing method that verifies API behavior remains stable after code changes, dependency updates, or infrastructure changes.", + }, + { + name: "Behavior Replay", + description: + "Reproducing recorded production API traffic in test environments to detect deviations before release.", + }, + { + name: "Test Generation", + description: + "Automatically creating test cases from observed API traffic, contracts, or source code context.", + }, + { + name: "Dependency Virtualization", + description: + "Simulating external services so APIs can be tested deterministically without brittle external dependencies.", + }, + { + name: "Flaky Test", + description: + "A non-deterministic test that passes or fails inconsistently without relevant code changes.", + }, + { + name: "Shift-Left Testing", + description: + "A practice of moving testing earlier into development and pull request workflows.", + }, +]; + +export default function GlossaryHub({ preview }) { + const pageUrl = `${SITE_URL}/glossary`; + const pageTitle = "Keploy API Testing Glossary"; + const pageDescription = + "Definitions of key API testing and quality engineering terms used across the Keploy ecosystem."; + + const definedTermSetSchema = { + "@context": "https://schema.org", + "@type": "DefinedTermSet", + name: "Keploy API Testing Glossary", + url: pageUrl, + hasDefinedTerm: glossaryTerms.map((term) => ({ + "@type": "DefinedTerm", + name: term.name, + description: term.description, + inDefinedTermSet: pageUrl, + })), + }; + + return ( + + + {pageTitle} + +
+ +
+

Glossary

+

{pageDescription}

+
+ {glossaryTerms.map((term) => ( +
+
{term.name}
+
{term.description}
+
+ ))} +
+
+
+ + ); +} + +export async function getStaticProps() { + return { + props: { + preview: false, + }, + revalidate: 600, + }; +} \ No newline at end of file diff --git a/pages/integrations/index.tsx b/pages/integrations/index.tsx new file mode 100644 index 00000000..8add1f06 --- /dev/null +++ b/pages/integrations/index.tsx @@ -0,0 +1,107 @@ +import Head from "next/head"; +import Layout from "../../components/layout"; +import Header from "../../components/header"; +import Container from "../../components/container"; +import { HOME_OG_IMAGE_URL } from "../../lib/constants"; +import { getBreadcrumbListSchema, SITE_URL } from "../../lib/structured-data"; + +const integrations = [ + { + name: "GitHub Actions", + summary: "Run Keploy API tests in CI with pull request checks and release pipelines.", + }, + { + name: "GitLab CI", + summary: "Validate API behavior as part of merge request and deployment workflows.", + }, + { + name: "Jenkins", + summary: "Integrate replay-based API regression tests into existing enterprise build jobs.", + }, + { + name: "Kubernetes", + summary: "Run Keploy in staging clusters for environment-safe traffic replay testing.", + }, + { + name: "Postman", + summary: "Bridge existing API collections with behavior-driven test generation.", + }, + { + name: "Docker", + summary: "Containerize test capture and replay workflows for consistent team usage.", + }, +]; + +export default function IntegrationsHub({ preview }) { + const pageUrl = `${SITE_URL}/integrations`; + const pageTitle = "Keploy Integrations"; + const pageDescription = + "Explore Keploy integrations for CI/CD, containers, cloud, and developer workflows to automate API testing at scale."; + + const collectionSchema = { + "@context": "https://schema.org", + "@type": "CollectionPage", + name: pageTitle, + url: pageUrl, + description: pageDescription, + mainEntity: { + "@type": "ItemList", + numberOfItems: integrations.length, + itemListElement: integrations.map((integration, index) => ({ + "@type": "ListItem", + position: index + 1, + item: { + "@type": "SoftwareApplication", + name: integration.name, + applicationCategory: "DeveloperApplication", + description: integration.summary, + }, + })), + }, + }; + + return ( + + + {pageTitle} + +
+ +
+

Integrations

+

{pageDescription}

+
    + {integrations.map((integration) => ( +
  • +

    {integration.name}

    +

    {integration.summary}

    +
  • + ))} +
+
+
+ + ); +} + +export async function getStaticProps() { + return { + props: { + preview: false, + }, + revalidate: 600, + }; +} \ No newline at end of file diff --git a/pages/sitemap.xml.tsx b/pages/sitemap.xml.tsx index 613920dc..72d787e7 100644 --- a/pages/sitemap.xml.tsx +++ b/pages/sitemap.xml.tsx @@ -73,10 +73,51 @@ function escapeXml(s: string): string { function buildSitemap(posts: PostNode[]): string { const today = new Date().toISOString().split("T")[0]; + const normalizeDate = (value?: string) => { + if (!value) return null; + const dateOnly = value.split("T")[0]; + return /^\d{4}-\d{2}-\d{2}$/.test(dateOnly) ? dateOnly : null; + }; + + const allPostDates = posts + .map((post) => normalizeDate(post.modified)) + .filter((value): value is string => Boolean(value)); + + const latestPostDate = + allPostDates.length > 0 + ? allPostDates.reduce((max, current) => (current > max ? current : max), allPostDates[0]) + : today; + + const latestByCategory = (categorySlug: string) => { + const dates = posts + .filter((post) => + post.categories.edges.some((edge) => edge.node.slug === categorySlug) + ) + .map((post) => normalizeDate(post.modified)) + .filter((value): value is string => Boolean(value)); + + if (dates.length === 0) { + return latestPostDate; + } + return dates.reduce((max, current) => (current > max ? current : max), dates[0]); + }; + const staticEntries = [ - { loc: `${MAIN_SITE_URL}/blog`, lastmod: today, priority: "1.00" }, - { loc: `${MAIN_SITE_URL}/blog/community`, lastmod: today, priority: "0.80" }, - { loc: `${MAIN_SITE_URL}/blog/technology`, lastmod: today, priority: "0.80" }, + { loc: `${MAIN_SITE_URL}/blog`, lastmod: latestPostDate, priority: "1.00" }, + { + loc: `${MAIN_SITE_URL}/blog/community`, + lastmod: latestByCategory("community"), + priority: "0.80", + }, + { + loc: `${MAIN_SITE_URL}/blog/technology`, + lastmod: latestByCategory("technology"), + priority: "0.80", + }, + { loc: `${MAIN_SITE_URL}/blog/integrations`, lastmod: today, priority: "0.62" }, + { loc: `${MAIN_SITE_URL}/blog/solutions`, lastmod: today, priority: "0.62" }, + { loc: `${MAIN_SITE_URL}/blog/case-studies`, lastmod: today, priority: "0.62" }, + { loc: `${MAIN_SITE_URL}/blog/glossary`, lastmod: today, priority: "0.62" }, ]; const postEntries: { loc: string; lastmod: string; priority: string }[] = []; @@ -91,7 +132,7 @@ function buildSitemap(posts: PostNode[]): string { seen.add(loc); postEntries.push({ loc, - lastmod: post.modified.split("T")[0], + lastmod: normalizeDate(post.modified) || today, priority: "0.64", }); } diff --git a/pages/solutions/index.tsx b/pages/solutions/index.tsx new file mode 100644 index 00000000..32284dad --- /dev/null +++ b/pages/solutions/index.tsx @@ -0,0 +1,102 @@ +import Head from "next/head"; +import Layout from "../../components/layout"; +import Header from "../../components/header"; +import Container from "../../components/container"; +import { HOME_OG_IMAGE_URL } from "../../lib/constants"; +import { getBreadcrumbListSchema, SITE_URL } from "../../lib/structured-data"; + +const solutions = [ + { + name: "Startups", + summary: "Ship faster with automated API regression tests that fit lean engineering teams.", + }, + { + name: "Scaleups", + summary: "Reduce release risk across microservices with behavior replay and test generation.", + }, + { + name: "Enterprise", + summary: "Add governance-friendly API testing with auditability, dedicated support, and controls.", + }, + { + name: "Developer Platforms", + summary: "Embed API quality gates into internal developer platforms and golden pipelines.", + }, + { + name: "Fintech and SaaS", + summary: "Validate critical transaction workflows with deterministic, production-like API tests.", + }, +]; + +export default function SolutionsHub({ preview }) { + const pageUrl = `${SITE_URL}/solutions`; + const pageTitle = "Keploy Solutions"; + const pageDescription = + "Discover Keploy solutions for startups, scaleups, and enterprise teams building reliable APIs and faster release pipelines."; + + const collectionSchema = { + "@context": "https://schema.org", + "@type": "CollectionPage", + name: pageTitle, + url: pageUrl, + description: pageDescription, + mainEntity: { + "@type": "ItemList", + numberOfItems: solutions.length, + itemListElement: solutions.map((solution, index) => ({ + "@type": "ListItem", + position: index + 1, + item: { + "@type": "Thing", + name: solution.name, + description: solution.summary, + }, + })), + }, + }; + + return ( + + + {pageTitle} + +
+ +
+

Solutions

+

{pageDescription}

+
    + {solutions.map((solution) => ( +
  • +

    {solution.name}

    +

    {solution.summary}

    +
  • + ))} +
+
+
+ + ); +} + +export async function getStaticProps() { + return { + props: { + preview: false, + }, + revalidate: 600, + }; +} \ No newline at end of file From a32cfd7855a65eb78e41109a8b5d63723704c84d Mon Sep 17 00:00:00 2001 From: dhananjay6561 Date: Mon, 20 Apr 2026 10:09:24 +0530 Subject: [PATCH 2/4] fix(seo): address sitemap freshness and preview review comments --- pages/sitemap.xml.tsx | 16 ++++++++++------ pages/solutions/index.tsx | 4 ++-- tests/e2e/SeoMeta.spec.ts | 4 ++++ 3 files changed, 16 insertions(+), 8 deletions(-) diff --git a/pages/sitemap.xml.tsx b/pages/sitemap.xml.tsx index 72d787e7..c6f44d15 100644 --- a/pages/sitemap.xml.tsx +++ b/pages/sitemap.xml.tsx @@ -79,7 +79,11 @@ function buildSitemap(posts: PostNode[]): string { return /^\d{4}-\d{2}-\d{2}$/.test(dateOnly) ? dateOnly : null; }; - const allPostDates = posts + const includedPosts = posts.filter((post) => + post.categories.edges.some((edge) => VALID_CATEGORIES.has(edge.node.slug)) + ); + + const allPostDates = includedPosts .map((post) => normalizeDate(post.modified)) .filter((value): value is string => Boolean(value)); @@ -114,15 +118,15 @@ function buildSitemap(posts: PostNode[]): string { lastmod: latestByCategory("technology"), priority: "0.80", }, - { loc: `${MAIN_SITE_URL}/blog/integrations`, lastmod: today, priority: "0.62" }, - { loc: `${MAIN_SITE_URL}/blog/solutions`, lastmod: today, priority: "0.62" }, - { loc: `${MAIN_SITE_URL}/blog/case-studies`, lastmod: today, priority: "0.62" }, - { loc: `${MAIN_SITE_URL}/blog/glossary`, lastmod: today, priority: "0.62" }, + { loc: `${MAIN_SITE_URL}/blog/integrations`, lastmod: latestPostDate, priority: "0.62" }, + { loc: `${MAIN_SITE_URL}/blog/solutions`, lastmod: latestPostDate, priority: "0.62" }, + { loc: `${MAIN_SITE_URL}/blog/case-studies`, lastmod: latestPostDate, priority: "0.62" }, + { loc: `${MAIN_SITE_URL}/blog/glossary`, lastmod: latestPostDate, priority: "0.62" }, ]; const postEntries: { loc: string; lastmod: string; priority: string }[] = []; const seen = new Set(); - for (const post of posts) { + for (const post of includedPosts) { const category = post.categories.edges .map((e) => e.node.slug) .find((slug) => VALID_CATEGORIES.has(slug)); diff --git a/pages/solutions/index.tsx b/pages/solutions/index.tsx index 32284dad..e23eb3da 100644 --- a/pages/solutions/index.tsx +++ b/pages/solutions/index.tsx @@ -92,10 +92,10 @@ export default function SolutionsHub({ preview }) { ); } -export async function getStaticProps() { +export async function getStaticProps({ preview = false }) { return { props: { - preview: false, + preview, }, revalidate: 600, }; diff --git a/tests/e2e/SeoMeta.spec.ts b/tests/e2e/SeoMeta.spec.ts index 1ff25cbd..ee8329b3 100644 --- a/tests/e2e/SeoMeta.spec.ts +++ b/tests/e2e/SeoMeta.spec.ts @@ -124,6 +124,10 @@ test.describe('SEO and Meta Tags Configuration', () => { expect(body).toContain('https://keploy.io/blog'); expect(body).toContain('https://keploy.io/blog/community'); expect(body).toContain('https://keploy.io/blog/technology'); + expect(body).toContain('https://keploy.io/blog/integrations'); + expect(body).toContain('https://keploy.io/blog/solutions'); + expect(body).toContain('https://keploy.io/blog/case-studies'); + expect(body).toContain('https://keploy.io/blog/glossary'); }); test('AI referral tracker should push event to dataLayer on UTM-attributed landing', async ({ page, baseURL }) => { From e2f428244a973c7d3c9d36170971a8631420b169 Mon Sep 17 00:00:00 2001 From: dhananjay6561 Date: Mon, 20 Apr 2026 10:38:29 +0530 Subject: [PATCH 3/4] fix(blog): propagate preview mode on hubs and add hub seo e2e coverage --- PR_DESCRIPTION.md | 125 +++++++++++++++++++++++++++++++++++ pages/case-studies/index.tsx | 4 +- pages/glossary/index.tsx | 4 +- pages/integrations/index.tsx | 4 +- tests/e2e/SeoMeta.spec.ts | 23 +++++++ 5 files changed, 154 insertions(+), 6 deletions(-) create mode 100644 PR_DESCRIPTION.md diff --git a/PR_DESCRIPTION.md b/PR_DESCRIPTION.md new file mode 100644 index 00000000..c7375526 --- /dev/null +++ b/PR_DESCRIPTION.md @@ -0,0 +1,125 @@ +# PR: Detailed Issue-Wise Fix Description + +## Overview +This PR documents all reported items that are fixed in the current blog codebase, in issue-wise format. + +## Fixed Issues from Status Report + +### 1) TASK-5 +What was the issue: +- Sitemap lastmod freshness was not fully aligned with emitted post URLs, and hub freshness behavior was unstable. + +How it was fixed: +- Sitemap now computes post freshness from category-filtered included posts. +- Hub routes use stable latestPostDate instead of daily-changing today. + +Evidence: +- pages/sitemap.xml.tsx:86 +- pages/sitemap.xml.tsx:121 + +### 2) GEO-12 +What was the issue: +- Glossary needed DefinedTerm and DefinedTermSet structured data. + +How it was fixed: +- Glossary page emits DefinedTermSet with hasDefinedTerm entries. + +Evidence: +- pages/glossary/index.tsx:49 +- pages/glossary/index.tsx:52 + +### 3) GEO-13 +What was the issue: +- Technology blog posts were not mapped to TechArticle schema. + +How it was fixed: +- Structured data now maps categorySlug technology posts to TechArticle. + +Evidence: +- lib/structured-data.ts:137 + +### 4) TASK-17 +What was the issue: +- BlogPosting dateModified handling was not safely guarded for missing values. + +How it was fixed: +- dateModified now falls back to datePublished when missing. + +Evidence: +- lib/structured-data.ts:159 + +### 5) GEO-15 (hub-level) +What was the issue: +- Case studies hub requirement was not satisfied at the hub level. + +How it was fixed: +- Case studies hub exists with 5 entries and ItemList count wired to the dataset. + +Evidence: +- pages/case-studies/index.tsx:8 +- pages/case-studies/index.tsx:45 + +### 6) TASK-31 +What was the issue: +- Newsletter visual image alt text was generic. + +How it was fixed: +- Updated to descriptive alt text for better accessibility and SEO context. + +Evidence: +- components/subscribe-newsletter.tsx:125 + +### 7) TASK-45 +What was the issue: +- Legacy truncated backlink path /unit-test-generat was unresolved. + +How it was fixed: +- Added permanent redirect for /unit-test-generat to the canonical destination. + +Evidence: +- next.config.js:229 + +### 8) TASK-47 +What was the issue: +- Broken backlink /test-case-generation was unresolved. + +How it was fixed: +- Added permanent redirect for /test-case-generation to the canonical destination. + +Evidence: +- next.config.js:234 + +## Additional Review-Driven Changes in This PR + +### A) Sitemap regression coverage for hub URLs +What was the issue: +- E2E sitemap test did not assert newly added hub loc entries. + +How it was fixed: +- Added assertions for integrations, solutions, case-studies, and glossary loc values. + +File: +- tests/e2e/SeoMeta.spec.ts + +### B) Preview mode propagation for solutions page +What was the issue: +- getStaticProps in solutions page was hard-coded to preview false. + +How it was fixed: +- Updated getStaticProps to receive preview context and pass it through. + +File: +- pages/solutions/index.tsx + +## Validation +Executed: +- npx playwright test tests/e2e/SeoMeta.spec.ts --grep "sitemap.xml endpoint should return XML with the static blog entries" + +Result: +- 1 passed + +## Impact +1. Reported SEO/schema/redirect gaps listed above are now addressed in code. +2. Sitemap freshness signals are more accurate and stable. +3. Regression protection improved for sitemap hub URLs. +4. Preview behavior is corrected for solutions route. diff --git a/pages/case-studies/index.tsx b/pages/case-studies/index.tsx index 8ef191ab..90308ed2 100644 --- a/pages/case-studies/index.tsx +++ b/pages/case-studies/index.tsx @@ -92,10 +92,10 @@ export default function CaseStudiesHub({ preview }) { ); } -export async function getStaticProps() { +export async function getStaticProps({ preview = false }) { return { props: { - preview: false, + preview, }, revalidate: 600, }; diff --git a/pages/glossary/index.tsx b/pages/glossary/index.tsx index 797475ca..d1bb38b1 100644 --- a/pages/glossary/index.tsx +++ b/pages/glossary/index.tsx @@ -94,10 +94,10 @@ export default function GlossaryHub({ preview }) { ); } -export async function getStaticProps() { +export async function getStaticProps({ preview = false }) { return { props: { - preview: false, + preview, }, revalidate: 600, }; diff --git a/pages/integrations/index.tsx b/pages/integrations/index.tsx index 8add1f06..4e490ab5 100644 --- a/pages/integrations/index.tsx +++ b/pages/integrations/index.tsx @@ -97,10 +97,10 @@ export default function IntegrationsHub({ preview }) { ); } -export async function getStaticProps() { +export async function getStaticProps({ preview = false }) { return { props: { - preview: false, + preview, }, revalidate: 600, }; diff --git a/tests/e2e/SeoMeta.spec.ts b/tests/e2e/SeoMeta.spec.ts index ee8329b3..7da6fe0a 100644 --- a/tests/e2e/SeoMeta.spec.ts +++ b/tests/e2e/SeoMeta.spec.ts @@ -130,6 +130,29 @@ test.describe('SEO and Meta Tags Configuration', () => { expect(body).toContain('https://keploy.io/blog/glossary'); }); + test('Glossary hub route should render with canonical metadata and DefinedTermSet schema', async ({ page, baseURL }) => { + const response = await page.goto(`${baseURL!}/glossary`); + expect(response?.status()).toBe(200); + await page.waitForLoadState('domcontentloaded'); + + const canonical = page.locator('link[rel="canonical"]'); + await expect(canonical).toBeAttached(); + await expect(canonical).toHaveAttribute('href', 'https://keploy.io/blog/glossary'); + + const schemas = page.locator('script[type="application/ld+json"]'); + const contents = await schemas.allTextContents(); + const hasDefinedTermSet = contents.some((content) => { + try { + const parsed = JSON.parse(content); + return parsed?.['@type'] === 'DefinedTermSet'; + } catch { + return false; + } + }); + + expect(hasDefinedTermSet).toBe(true); + }); + test('AI referral tracker should push event to dataLayer on UTM-attributed landing', async ({ page, baseURL }) => { await page.goto(`${baseURL!}/?utm_source=chatgpt`); From f395432aafe9807d4ea441c58de52f104b88bb20 Mon Sep 17 00:00:00 2001 From: Dhananjay Aggarwal <133662894+dhananjay6561@users.noreply.github.com> Date: Fri, 24 Apr 2026 07:47:09 +0530 Subject: [PATCH 4/4] Removed one unnecessary file Signed-off-by: Dhananjay Aggarwal <133662894+dhananjay6561@users.noreply.github.com> --- PR_DESCRIPTION.md | 125 ---------------------------------------------- 1 file changed, 125 deletions(-) delete mode 100644 PR_DESCRIPTION.md diff --git a/PR_DESCRIPTION.md b/PR_DESCRIPTION.md deleted file mode 100644 index c7375526..00000000 --- a/PR_DESCRIPTION.md +++ /dev/null @@ -1,125 +0,0 @@ -# PR: Detailed Issue-Wise Fix Description - -## Overview -This PR documents all reported items that are fixed in the current blog codebase, in issue-wise format. - -## Fixed Issues from Status Report - -### 1) TASK-5 -What was the issue: -- Sitemap lastmod freshness was not fully aligned with emitted post URLs, and hub freshness behavior was unstable. - -How it was fixed: -- Sitemap now computes post freshness from category-filtered included posts. -- Hub routes use stable latestPostDate instead of daily-changing today. - -Evidence: -- pages/sitemap.xml.tsx:86 -- pages/sitemap.xml.tsx:121 - -### 2) GEO-12 -What was the issue: -- Glossary needed DefinedTerm and DefinedTermSet structured data. - -How it was fixed: -- Glossary page emits DefinedTermSet with hasDefinedTerm entries. - -Evidence: -- pages/glossary/index.tsx:49 -- pages/glossary/index.tsx:52 - -### 3) GEO-13 -What was the issue: -- Technology blog posts were not mapped to TechArticle schema. - -How it was fixed: -- Structured data now maps categorySlug technology posts to TechArticle. - -Evidence: -- lib/structured-data.ts:137 - -### 4) TASK-17 -What was the issue: -- BlogPosting dateModified handling was not safely guarded for missing values. - -How it was fixed: -- dateModified now falls back to datePublished when missing. - -Evidence: -- lib/structured-data.ts:159 - -### 5) GEO-15 (hub-level) -What was the issue: -- Case studies hub requirement was not satisfied at the hub level. - -How it was fixed: -- Case studies hub exists with 5 entries and ItemList count wired to the dataset. - -Evidence: -- pages/case-studies/index.tsx:8 -- pages/case-studies/index.tsx:45 - -### 6) TASK-31 -What was the issue: -- Newsletter visual image alt text was generic. - -How it was fixed: -- Updated to descriptive alt text for better accessibility and SEO context. - -Evidence: -- components/subscribe-newsletter.tsx:125 - -### 7) TASK-45 -What was the issue: -- Legacy truncated backlink path /unit-test-generat was unresolved. - -How it was fixed: -- Added permanent redirect for /unit-test-generat to the canonical destination. - -Evidence: -- next.config.js:229 - -### 8) TASK-47 -What was the issue: -- Broken backlink /test-case-generation was unresolved. - -How it was fixed: -- Added permanent redirect for /test-case-generation to the canonical destination. - -Evidence: -- next.config.js:234 - -## Additional Review-Driven Changes in This PR - -### A) Sitemap regression coverage for hub URLs -What was the issue: -- E2E sitemap test did not assert newly added hub loc entries. - -How it was fixed: -- Added assertions for integrations, solutions, case-studies, and glossary loc values. - -File: -- tests/e2e/SeoMeta.spec.ts - -### B) Preview mode propagation for solutions page -What was the issue: -- getStaticProps in solutions page was hard-coded to preview false. - -How it was fixed: -- Updated getStaticProps to receive preview context and pass it through. - -File: -- pages/solutions/index.tsx - -## Validation -Executed: -- npx playwright test tests/e2e/SeoMeta.spec.ts --grep "sitemap.xml endpoint should return XML with the static blog entries" - -Result: -- 1 passed - -## Impact -1. Reported SEO/schema/redirect gaps listed above are now addressed in code. -2. Sitemap freshness signals are more accurate and stable. -3. Regression protection improved for sitemap hub URLs. -4. Preview behavior is corrected for solutions route.