({
+ "@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({ preview = false }) {
+ return {
+ props: {
+ preview,
+ },
+ 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..d1bb38b1
--- /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({ preview = false }) {
+ return {
+ props: {
+ preview,
+ },
+ 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..4e490ab5
--- /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({ preview = false }) {
+ return {
+ props: {
+ preview,
+ },
+ revalidate: 600,
+ };
+}
\ No newline at end of file
diff --git a/pages/sitemap.xml.tsx b/pages/sitemap.xml.tsx
index 613920dc..c6f44d15 100644
--- a/pages/sitemap.xml.tsx
+++ b/pages/sitemap.xml.tsx
@@ -73,15 +73,60 @@ 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 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));
+
+ 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: 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));
@@ -91,7 +136,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..e23eb3da
--- /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({ preview = false }) {
+ return {
+ props: {
+ preview,
+ },
+ revalidate: 600,
+ };
+}
\ No newline at end of file
diff --git a/tests/e2e/SeoMeta.spec.ts b/tests/e2e/SeoMeta.spec.ts
index 1ff25cbd..7da6fe0a 100644
--- a/tests/e2e/SeoMeta.spec.ts
+++ b/tests/e2e/SeoMeta.spec.ts
@@ -124,6 +124,33 @@ 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('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 }) => {