feat: redesign testimonial marquee with manual controls, add security,headers, and fix SEO issues#335
Conversation
Code Review SummaryStatus: No Issues Found | Recommendation: Merge OverviewThis PR includes several quality improvements:
Files Reviewed (6 files)
|
|
hey @amaan-bhati i have proposed a solution to the given issue of the testimonial marquee which is redesigned to the design which you have mentioned in the issue |
|
Hi @HEETMEHTA18. Feel free to look into other open issues in the repository and pick one that hasn’t been addressed yet. We’d be happy to review another contribution from you. Thanks again for your effort! |
|
@dhananjay6561 however but still in the website no furthur changes are made till i just made changes into this so that the avatar was not loading but in the given pr the changes are made. |
|
Hey @HEETMEHTA18 👋 — thanks for putting this PR together, we appreciate the effort! We've gone ahead and requested a Copilot review on this. Here's some context from the reviewer:
Once you've had a chance to go through the comments, please address the feedback and resolve the threads — and we'll get this across the line. Feel free to ask if anything's unclear. Happy coding! 💙 |
There was a problem hiding this comment.
Pull request overview
This PR modernizes the blog landing page experience by redesigning the testimonials marquee, tightening security headers at the Next.js edge, and fixing SEO-related asset paths under the /blog basePath.
Changes:
- Replaced the legacy testimonials marquee with a dual-row, hover-pausing infinite scroll layout (with a skeleton fallback during initial mount).
- Added standard security headers (
X-Frame-Options,X-Content-Type-Options,Referrer-Policy,Permissions-Policy) vianext.config.js. - Fixed SEO/meta asset URLs to be basePath-correct and cleaned up SEO token/typo issues on the homepage.
Reviewed changes
Copilot reviewed 5 out of 6 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
pages/index.tsx |
Removes inline <Head> title usage (relying on Layout/Meta) and fixes a typo in technology posts variable naming. |
next.config.js |
Adds additional security headers alongside the existing CSP. |
components/testimonials.tsx |
Implements the redesigned dual-row marquee testimonial UI and avatar proxy/fallback handling. |
components/meta.tsx |
Corrects favicon/manifest/RSS/browserconfig paths to work under /blog basePath. |
components/footer.tsx |
Adds aria-hidden to decorative social SVG icons. |
components/cover-image.tsx |
Adds safer fallbacks for alt/src and refactors className handling (but currently introduces a critical build issue). |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| className={cn("rounded-md transition-border duration-300", imgClassName, { | ||
| " transition-scale duration-300": slug, | ||
| })} |
There was a problem hiding this comment.
CoverImage now uses cn(...) but the helper isn’t imported in this file, which will cause a runtime/compile failure. Also, the Tailwind classes transition-border and transition-scale don’t exist in this repo’s Tailwind config, so the intended transitions won’t apply; use valid Tailwind transition utilities (or extend Tailwind) instead.
| className={cn("rounded-md transition-border duration-300", imgClassName, { | |
| " transition-scale duration-300": slug, | |
| })} | |
| className={[ | |
| "rounded-md transition-colors duration-300", | |
| slug ? "transition-transform duration-300" : "", | |
| imgClassName ?? "", | |
| ] | |
| .filter(Boolean) | |
| .join(" ")} |
| /** Fallback avatar component – renders a user icon/initials in a branded circle */ | ||
| const FallbackAvatar = ({ name }: { name: string }) => ( | ||
| <div className="flex h-10 w-10 shrink-0 items-center justify-center rounded-full bg-slate-100 text-slate-500 ring-2 ring-gray-100 p-1"> | ||
| <User className="h-full w-full opacity-60" /> | ||
| </div> | ||
| ); | ||
|
|
There was a problem hiding this comment.
getInitials is declared but never used, and FallbackAvatar accepts a name prop but doesn’t render initials (only the icon). This is dead code / misleading intent—either remove the unused helper/prop or render initials as the fallback so the behavior matches the comment.
| /** Fallback avatar component – renders a user icon/initials in a branded circle */ | |
| const FallbackAvatar = ({ name }: { name: string }) => ( | |
| <div className="flex h-10 w-10 shrink-0 items-center justify-center rounded-full bg-slate-100 text-slate-500 ring-2 ring-gray-100 p-1"> | |
| <User className="h-full w-full opacity-60" /> | |
| </div> | |
| ); | |
| /** Fallback avatar component – renders initials when available, otherwise a user icon */ | |
| const FallbackAvatar = ({ name }: { name: string }) => { | |
| const initials = name.trim() ? getInitials(name) : ""; | |
| return ( | |
| <div className="flex h-10 w-10 shrink-0 items-center justify-center rounded-full bg-slate-100 text-slate-500 ring-2 ring-gray-100 p-1"> | |
| {initials ? ( | |
| <span className="text-sm font-medium leading-none">{initials}</span> | |
| ) : ( | |
| <User className="h-full w-full opacity-60" /> | |
| )} | |
| </div> | |
| ); | |
| }; |
| return ( | ||
|
|
||
| <Layout | ||
| preview={preview} | ||
| featuredImage={HOME_OG_IMAGE_URL} | ||
| Title={`Blog - Keploy`} | ||
| Description={"The Keploy Blog offers in-depth articles and expert insights on software testing, automation, and quality assurance, empowering developers to enhance their testing strategies and deliver robust applications."} | ||
| structuredData={structuredData} | ||
| > | ||
| <Head> | ||
| <title>{`Engineering | Keploy Blog`}</title> | ||
| </Head> | ||
| <Header /> | ||
| <Container> |
There was a problem hiding this comment.
After removing the inline <Head> usage, the Head import at the top of this file is now unused and will be flagged by next lint. Please remove the unused import to keep CI/lint clean.
There was a problem hiding this comment.
QA Report — feat: redesign testimonial marquee with manual controls, add security headers, and fix SEO issues
Repo: keploy/blog-website | Author: HEETMEHTA18 | Review date: 2026-04-30
Change type: content-rendering-change, config-change, metadata-or-seo-change
Summary
| Severity | Count |
|---|---|
| 🔴 Blocking | 3 |
| 🟡 Warning | 4 |
| 🔵 Suggestion | 4 |
| ⚪ Nitpick | 3 |
Reviewed files: components/cover-image.tsx, components/footer.tsx, components/meta.tsx, components/testimonials.tsx, next.config.js, pages/index.tsx
Gate checklist
- No blocking findings remain
- Metadata contract still holds on production pages (see title warning)
- Base-path and route integrity remain valid
- Impacted files were checked for regressions
🔴 Blocking
-
components/cover-image.tsx—cnused but never imported
Why: The PR replaces the inline className template literal withcn(...)but adds no import forcn. This is a build-breakingReferenceError— the site will not compile.
Fix:import { cn } from "@/lib/utils"; // verify path under the @/* alias
-
components/testimonials.tsx:~87— Raw<img>tag instead ofnext/image
Why: Framework rule #3 blocks new raw<img>tags in component code. The newReviewCarduses<img>for avatars, bypassing Next.js image optimization (format selection, lazy loading queue, responsive srcsets). Thepages/api/proxy-image.tsproxy already exists, sonext/imagewith the proxy URL is the correct pattern.
Fix:import NextImage from "next/image"; // … <NextImage className="rounded-full object-cover ring-2 ring-primary-100" width={40} height={40} alt={name ? `${name}'s avatar` : "Avatar"} src={proxiedAvatar} onError={() => setImgError(true)} />
-
components/cover-image.tsx:~32—priorityhardcoded totruefor all cover images
Why:priority={priority}(caller-controlled, defaulting tofalse) was replaced with the barepriorityliteral (alwaystrue). Every cover image sitewide — including list-page thumbnails — will now be eagerly preloaded, regressing Lighthouse CWV scores (TBT, FCP, TTI).
Fix: Restore the conditional props:priority={priority} loading={priority ? "eager" : "lazy"} sizes={sizes}
🟡 Warnings
-
components/cover-image.tsx—sizesprop silently dropped
Why:sizesis still declared in thePropsinterface and destructured, but the new<Image>render no longer passes it. This silently removes the responsive image hint callers relied on for bandwidth-optimal image selection.
Fix: Restoresizes={sizes}to the<Image>element, or removesizesfrom the interface if it is intentionally deprecated. -
components/meta.tsx:40-41—Group.svgdoes not exist inpublic/favicon/
Why: The fork'spublic/favicon/directory containssafari-pinned-tab.svgbut notGroup.svg(confirmed by directory listing). The<link rel="mask-icon">will 404 in Safari on every page globally. (Group.pngdoes exist — the shortcut-icon change is fine.)
Fix: Revert the mask-icon line:<link rel="mask-icon" href="/blog/favicon/safari-pinned-tab.svg" color="#000000" />
…or add
Group.svgtopublic/favicon/before merging. -
pages/index.tsx:36-38— Route-specific<title>removed; title coverage unverified
Why: The<Head><title>{Engineering | Keploy Blog}</title></Head>block is removed. TheMetacomponent (reviewed in full) does not emit a<title>tag — only OG, Twitter, description, and canonical meta. IfLayoutdoes not emit<title>from theTitleprop, the homepage will have no title, which is a critical SEO regression.
Fix: Either restore the route-specific<title>(framework rule #2 explicitly permits this forpages/index.tsx), or document thatlayout.tsxemits<title>from theTitleprop and verify it in the browser before merging. -
components/testimonials.tsx—useRoutercalled perReviewCardinstance
Why:ReviewCardis rendered for every tweet (20+ cards).useRouter()inside each card creates per-instance router subscriptions. The only value consumed —router.basePath— is a static build-time constant that never changes at runtime.
Fix: ResolvebasePathonce inTwitterTestimonialsand pass it as a prop toReviewCard.
🔵 Suggestions
-
components/testimonials.tsx:13-17—getInitials()is defined with a JSDoc comment but never called.FallbackAvatarshows theUsericon instead. Either usegetInitials(name)for initials display insideFallbackAvatar, or delete the function. -
components/testimonials.tsx:56-62— TheuseEffectthat resetsimgErroron avatar prop change is unnecessary. Prop changes already trigger a re-render; the effect adds an extra render cycle with no real benefit. Remove it, or usekey={avatar}onReviewCardat the call site for a cleaner reset. -
components/testimonials.tsx:115-145— Themountedguard renders a skeleton on SSR and replaces it with the marquee on the client, causing measurable CLS. Consider usingopacity-0/visibility: hiddenon the SSR pass and fading in on mount to avoid the layout shift. -
next.config.js—X-Frame-Options: DENYis present but the CSP is missingframe-ancestors 'none'. Modern browsers prefer the CSP directive; adding both provides defense-in-depth for legacy and modern browsers alike.
⚪ Nitpicks
-
components/testimonials.tsx:7—import { User } from "lucide-react"is placed afterimport Tweets from "../services/Tweets", breaking conventional import grouping (external packages before internal services). Move it with the other third-party imports at the top. -
components/cover-image.tsx:35— Double leading space in" transition-scale duration-300". -
components/cover-image.tsx:34-36—transition-borderandtransition-scaleare not valid Tailwind CSS utilities. Standard Tailwind includestransition-colorsandtransition-transform; the non-existent classes will produce no CSS output.
✅ Looks Good
- Security headers (
X-Content-Type-Options: nosniff,Referrer-Policy: strict-origin-when-cross-origin,Permissions-Policy) are well-chosen and do not conflict with the existing CSP. pages/index.tsxtypo fix:allTehcnologyPosts→allTechnologyPostsis a clean correction with no regressions.components/footer.tsxaccessibility:aria-hidden="true"on all four decorative SVG icons is correct — they are purely visual.- Marquee key uniqueness: Appending
-r1/-r2to tweet IDs correctly prevents duplicate React key warnings. - Proxy routing: External avatar URLs routed through
pages/api/proxy-image.tsfollows the established allowlist pattern for this repo. meta.tsxbase-path correctness: All favicon hrefs retain the/blog/prefix, consistent withbasePathconfig.- Duplicate
twitter:imagecleaned up: The pre-featuredImage-conditional duplicate meta tag is removed; the consolidated structure is cleaner.
Cross-file impact
Direct findings:
components/cover-image.tsx— brokencnimport blocks the build;priorityregression affects all post-list and post-detail pagescomponents/testimonials.tsx— raw<img>bypasses the Next.js image pipeline for all testimonial avatarscomponents/meta.tsx—Group.svg404 affects the Safari pinned-tab icon on every page (global)pages/index.tsx— removed<title>may leave the homepage without a title ifLayoutdoes not cover it
Inferred impact risks checked:
components/layout.tsx— not in diff;Metaconfirmed not to emit<title>; title coverage bylayout.tsxrequires manual verificationpages/api/proxy-image.ts— confirmed present in base repo; proxy routing in testimonials is safe
Overall verdict
Do not merge in current state. Three blockers must be resolved first: the missing cn import (build failure), the raw <img> tag (framework rule violation), and the hardcoded priority={true} regression (performance). Confirm the homepage <title> is still emitted and fix the Group.svg 404 before this ships. The security header additions, footer accessibility improvements, and typo fix are all clean — once the blockers are addressed this PR will be in good shape.
Review generated by Keploy QA Agent · 2026-04-30
23691d3 to
729fe03
Compare
… headers, and fix SEO issues Signed-off-by: Heet Mehta <heetmehta18125@gmail.com> Signed-off-by: heetmehta18 <heetmehta18125@gmail.com>
… issues Signed-off-by: Heet Mehta <heetmehta18125@gmail.com> Signed-off-by: heetmehta18 <heetmehta18125@gmail.com>
Signed-off-by: Heet Mehta <heetmehta18125@gmail.com> Signed-off-by: heetmehta18 <heetmehta18125@gmail.com>
* fix: broken testimonial avatars + marquee UX improvements * fix: address Copilot review feedback on PR #330
* style: improve blog post typography, readability, and TOC scrolling * fix: removed the LOCs that were failing the build * fix:removed the unrequired LOCs that were failing the build * fix: UI breaking at some dimensions, made it responsive Signed-off-by: dhananjay6561 <dhananjayaggarwal6561@gmail.com> * Revamp blog article page: modular components, CSS Grid layout, new author cards, sidebar, TOC tooltip, responsive typography, and Keploy-themed design Signed-off-by: dhananjay6561 <dhananjayaggarwal6561@gmail.com> * Remove category pill (Technology tag) from blog sidebar Signed-off-by: dhananjay6561 <dhananjayaggarwal6561@gmail.com> * fix:made tags clickable that navigates to tag's blog listing page Signed-off-by: dhananjay6561 <dhananjayaggarwal6561@gmail.com> --------- Signed-off-by: dhananjay6561 <dhananjayaggarwal6561@gmail.com>
Signed-off-by: dhananjay6561 <dhananjayaggarwal6561@gmail.com>
* feat: setup Playwright e2e testing infrastructure Signed-off-by: DSingh0304 <deepshekhar0306@gmail.com> * feat: add environment variables for WordPress API in Playwright config Signed-off-by: DSingh0304 <deepshekhar0306@gmail.com> * feat: enhance Playwright tests with new homepage spec and build steps Signed-off-by: DSingh0304 <deepshekhar0306@gmail.com> * fix: update Playwright config to use hardcoded WordPress API URLs Signed-off-by: DSingh0304 <deepshekhar0306@gmail.com> * fix: update Playwright workflow permissions and add continue-on-error for PR comments Signed-off-by: DSingh0304 <deepshekhar0306@gmail.com> * test infrastructure and workflow updates. Signed-off-by: DSingh0304 <deepshekhar0306@gmail.com> * test: Add comprehensive Playwright E2E for navigation and more stories component. Signed-off-by: DSingh0304 <deepshekhar0306@gmail.com> * Mock node js server Signed-off-by: DSingh0304 <deepshekhar0306@gmail.com> * Mock json data Signed-off-by: DSingh0304 <deepshekhar0306@gmail.com> * test: extensive Playwright test coverage for components Signed-off-by: DSingh0304 <deepshekhar0306@gmail.com> * Fixing CI Failures for webkit browser Signed-off-by: DSingh0304 <deepshekhar0306@gmail.com> * Component Test - Footer and Heropost Signed-off-by: DSingh0304 <deepshekhar0306@gmail.com> * Fixing flaky and failed test for webkit browser Signed-off-by: DSingh0304 <deepshekhar0306@gmail.com> * Playwright test for PostHeader as well as PostBody Signed-off-by: DSingh0304 <deepshekhar0306@gmail.com> * Removing the PR commenting feature for now Signed-off-by: DSingh0304 <deepshekhar0306@gmail.com> * WebKit error fixed Signed-off-by: DSingh0304 <deepshekhar0306@gmail.com> * MoreStories webkit error fixed Signed-off-by: DSingh0304 <deepshekhar0306@gmail.com> * Fixing flaky MoreStories webkit test Signed-off-by: DSingh0304 <deepshekhar0306@gmail.com> * Update tests/components/PostHeader.spec.ts Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Signed-off-by: Deep Shekhar Singh <deepshekhar0306@gmail.com> * Update tests/components/PostBody.spec.ts Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Signed-off-by: Deep Shekhar Singh <deepshekhar0306@gmail.com> * Refactor viewport checks in Navigation and HomePage tests for improved clarity Signed-off-by: DSingh0304 <deepshekhar0306@gmail.com> * Add tests for ScrollToTop component functionality and accessibility Signed-off-by: Deep <deep@example.com> * Add tests for TableContents component functionality on desktop and mobile Signed-off-by: Deep <deep@example.com> * Add tests for Tag component functionality and accessibility Signed-off-by: Deep <deep@example.com> * Add tests for Testimonials component functionality and accessibility Signed-off-by: Deep <deep@example.com> * Add tests for TopBlogs component functionality and accessibility Signed-off-by: Deep <deep@example.com> * E2E Test for technology page Signed-off-by: Deep <deep@example.com> * E2E Test for technology post page Signed-off-by: Deep <deep@example.com> * Update tests/components/Tag.spec.ts Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Signed-off-by: Deep Shekhar Singh <deepshekhar0306@gmail.com> * Update tests/components/ScrollToTop.spec.ts Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Signed-off-by: Deep Shekhar Singh <deepshekhar0306@gmail.com> * Update tests/components/Tag.spec.ts Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Signed-off-by: Deep Shekhar Singh <deepshekhar0306@gmail.com> * Update tests/pages/TechnologyPostPage.spec.ts Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Signed-off-by: Deep Shekhar Singh <deepshekhar0306@gmail.com> * Update tests/components/TableContents.spec.ts Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Signed-off-by: Deep Shekhar Singh <deepshekhar0306@gmail.com> * Update tests/pages/TechnologyPage.spec.ts Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Signed-off-by: Deep Shekhar Singh <deepshekhar0306@gmail.com> * Refactor navigation logic in PostHeader and ScrollToTop tests to use href attribute for improved reliability Signed-off-by: Deep <deep@example.com> * Playwright config and CI changes Signed-off-by: Deep <deep@example.com> * Using build server instead of dev Signed-off-by: Deep <deep@example.com> * remove waitForTimeout as Playwright natively handles dynamic UI readiness Signed-off-by: Deep <deep@example.com> * remove waitForTimeout as Playwright natively handles dynamic UI readiness-2 Signed-off-by: Deep <deep@example.com> * Removing gaurds in core structural elements Signed-off-by: Deep <deep@example.com> * refactor: simplify visibility checks for LinkedIn and Slack social links in footer tests Signed-off-by: Deep <deep@example.com> * refactor: add data-testid attributes for improved test targeting in components Signed-off-by: Deep <deep@example.com> * test: add data-testid attributes for footer and tags sections to improve test targeting Signed-off-by: Deep <deep@example.com> * Update tests/components/ScrollToTop.spec.ts Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Signed-off-by: Deep Shekhar Singh <deepshekhar0306@gmail.com> * Update tests/components/TopBlogs.spec.ts Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Signed-off-by: Deep Shekhar Singh <deepshekhar0306@gmail.com> * Update tests/components/TableContents.spec.ts Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Signed-off-by: Deep Shekhar Singh <deepshekhar0306@gmail.com> * Update tests/components/MoreStories.spec.ts Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Signed-off-by: Deep Shekhar Singh <deepshekhar0306@gmail.com> * Update tests/components/PostBody.spec.ts Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Signed-off-by: Deep Shekhar Singh <deepshekhar0306@gmail.com> * Update tests/components/ScrollToTop.spec.ts Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Signed-off-by: Deep Shekhar Singh <deepshekhar0306@gmail.com> * Update tests/components/TableContents.spec.ts Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Signed-off-by: Deep Shekhar Singh <deepshekhar0306@gmail.com> * Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> Signed-off-by: Deep Shekhar Singh <deepshekhar0306@gmail.com> * Add tests for Author Detail Page and Authors Index Page component availability Signed-off-by: Deep <deep@example.com> * Add tests for Community Page, Community Post Page, and Community Search Page component availability Signed-off-by: Deep <deep@example.com> * Add tests for Tag Detail Page and Tags Index Page component availability Signed-off-by: Deep <deep@example.com> * Add tests for Not Found Page and Search Page component availability Signed-off-by: Deep <deep@example.com> * Add tests for Mobile Layout and Navigation responsiveness Signed-off-by: Deep <deep@example.com> * Add tests for API Mocking and SEO Meta Tags configuration Signed-off-by: Deep <deep@example.com> * Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> Signed-off-by: Deep Shekhar Singh <deepshekhar0306@gmail.com> * Refactor tests for improved visibility checks and assertions in Navigation, PostHeader, ScrollToTop, TableContents, HomePage, and TechnologyPostPage components Signed-off-by: Deep <deep@example.com> * Update component visibility assertions in various test files for improved accuracy Signed-off-by: Deep <deep@example.com> * Comment out unused device configurations for Firefox and Webkit in Playwright config Signed-off-by: Deep <deep@example.com> * Add community posts fixture and update mock server to handle community category queries Signed-off-by: Deep <deep@example.com> * Simplify Playwright E2E tests configuration by removing browser matrix and hardcoding to chromium Signed-off-by: Deep <deep@example.com> * Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> Signed-off-by: Deep Shekhar Singh <deepshekhar0306@gmail.com> * Enhance component tests for improved visibility and accuracy across various pages Signed-off-by: Deep <deep@example.com> * Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> Signed-off-by: Deep Shekhar Singh <deepshekhar0306@gmail.com> * Enhance test reliability by adding timeout and visibility checks in various components Signed-off-by: Deep <deep@example.com> * Update @types/node to version 18.19.130 and add engines field for Node.js compatibility Signed-off-by: Deep <deep@example.com> * Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> Signed-off-by: Deep Shekhar Singh <deepshekhar0306@gmail.com> * Enhance mobile layout tests by adding max width checks and improving visibility assertions Signed-off-by: Deep <deep@example.com> * Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> Signed-off-by: Deep Shekhar Singh <deepshekhar0306@gmail.com> * Refactor TOC mobile tests to improve dropdown item selection and visibility checks Signed-off-by: Deep <deep@example.com> * Refactor TOC component tests to remove conditional click and ensure visibility checks for first post Signed-off-by: Deep <deep@example.com> * Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> Signed-off-by: Deep Shekhar Singh <deepshekhar0306@gmail.com> * Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> Signed-off-by: Deep Shekhar Singh <deepshekhar0306@gmail.com> * Refactor Playwright configuration and mobile layout tests to improve horizontal overflow checks Signed-off-by: Deep <deep@example.com> * Refactor horizontal overflow epsilon to use WIDTH_EPSILON for consistency in mobile layout tests Signed-off-by: Deep <deep@example.com> * Refactor test interactions to ensure visibility and enablement before clicks across multiple components Signed-off-by: Deep <deep@example.com> * Refactor tests in ScrollToTop, TableContents, TechnologyPostPage, and MobileNavigation for improved visibility and reliability of elements before interactions Signed-off-by: Deep <deep@example.com> * Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> Signed-off-by: Deep Shekhar Singh <deepshekhar0306@gmail.com> * Refactor navigation tests to improve URL wait conditions and enhance locator strategies for better reliability Signed-off-by: Deep <deep@example.com> * Refactor environment variable loading to improve error handling and provide clearer warnings when .env.test is missing Signed-off-by: Deep <deep@example.com> * fix: improve test stability by adding timeouts to element visibility checks and refining URL wait conditions in tests. Signed-off-by: Deep <deep@example.com> * refactor: Enhance Playwright tests by adding data-testid attributes to components and improving test selectors. Signed-off-by: Deep <deep@example.com> * refactor: enhance Playwright test reliability by using explicit waits for content and data-testid locators. Signed-off-by: Deep <deep@example.com> --------- Signed-off-by: DSingh0304 <deepshekhar0306@gmail.com> Signed-off-by: Deep Shekhar Singh <deepshekhar0306@gmail.com> Signed-off-by: Deep <deep@example.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Co-authored-by: Deep <deep@example.com> Co-authored-by: Manas Manohar <21006907+manasmanohar@users.noreply.github.com> Signed-off-by: heetmehta18 <heetmehta18125@gmail.com>
* Add sidebar ad image with fallback, clickable signup link, and orange glow hover effect Signed-off-by: dhananjay6561 <dhananjayaggarwal6561@gmail.com> * resolved copilot comments Signed-off-by: dhananjay6561 <dhananjayaggarwal6561@gmail.com> --------- Signed-off-by: dhananjay6561 <dhananjayaggarwal6561@gmail.com> Co-authored-by: Neha Gupta <gneha21@yahoo.in> Signed-off-by: heetmehta18 <heetmehta18125@gmail.com>
Add analytics script and whitelist its endpoint in CSP connect-src. Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Co-authored-by: Neha Gupta <gneha21@yahoo.in> Signed-off-by: heetmehta18 <heetmehta18125@gmail.com>
* fix: update analytics and CSP configuration Add analytics script and whitelist its endpoint in CSP connect-src. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: resolve CSP duplication and Cache-Control scoping issues in vercel.json * fix: move telemetry script to _app.tsx and remove unsafe Cache-Control from vercel.json --------- Signed-off-by: Neha Gupta <gneha21@yahoo.in> Co-authored-by: Shubham Jain <shubhamkjain@outlook.com> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Co-authored-by: Neha Gupta <gneha21@yahoo.in> Signed-off-by: heetmehta18 <heetmehta18125@gmail.com>
Signed-off-by: dhananjay6561 <dhananjayaggarwal6561@gmail.com> Signed-off-by: heetmehta18 <heetmehta18125@gmail.com>
* aeo update Signed-off-by: Neha Gupta <gneha21[at]yahoo> * fix: add llms-full.txt reference to blog llms.txt Blog llms.txt was missing a pointer to the comprehensive llms-full.txt, reducing discoverability for AI consumers that only read llms.txt. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: slayerjain <shubhamkjain@outlook.com> * feat: add canonical URLs and og:url to blog posts - Add canonicalUrl prop to Meta and Layout components - Render <link rel="canonical"> and <meta property="og:url"> per post - Add og:type=article meta tag - Pass canonicalUrl from community/[slug] and technology/[slug] pages Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: slayerjain <shubhamkjain@outlook.com> * fix: resolve all 6 copilot review comments on PR #356 aiReferralTracker.ts: - Parse referrer with URL() and match hostname exactly or via endsWith('.'+domain) to prevent substring false positives - Declare Window.dataLayer type globally, use ??= for type safety - Remove all (window as any) casts _app.tsx: - Track AI referrals on route changes via Router.events, not just initial mount meta.tsx: - Make og:type configurable via prop (defaults to "article") - Homepage now passes ogType="website" _document.tsx: - Use getOrganizationSchema() from lib/structured-data instead of inline duplicate Organization schema index.tsx: - Remove duplicate Organization schema (already in _document.tsx) - Pass ogType="website" to Layout layout.tsx: - Pass ogType prop through to Meta component SeoMeta.spec.ts: - Add tests for canonical URL, og:url on post pages - Add test for og:type="website" on homepage - Add test for og:type="article" on post pages Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: slayerjain <shubhamkjain@outlook.com> * fix: resolve 4 copilot review comments (round 2) pages/index.tsx: - Remove unused getOrganizationSchema import pages/_app.tsx: - Revert route-change tracking — document.referrer doesn't change on SPA navigations, so re-firing would duplicate ai_referral events. Track only on initial landing. utils/aiReferralTracker.ts: - Widen dataLayer type to unknown[] to avoid TypeScript friction with gtag's non-record pushes pages/_document.tsx: - Use shared constants (SITE_URL, ORG_NAME, MAIN_SITE_URL) from lib/structured-data instead of hard-coded URLs in Blog schema Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: slayerjain <shubhamkjain@outlook.com> * fix: resolve 4 copilot review comments (round 3) layout.tsx: - Default ogType to "website" instead of "article" — non-post pages (tag, author, search) were incorrectly emitting og:type=article technology/[slug].tsx, community/[slug].tsx: - Pass ogType="article" explicitly from post detail pages - Only set canonicalUrl when post slug is available (not during fallback render) to avoid incorrect canonical on loading state SeoMeta.spec.ts: - Add e2e test for AI referral tracker — verifies dataLayer event is pushed on UTM-attributed landing (?utm_source=chatgpt) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: slayerjain <shubhamkjain@outlook.com> * fix: resolve 2 copilot review comments (round 4) SeoMeta.spec.ts: - Use page.waitForFunction instead of immediate evaluate to avoid flaky test — trackAiReferral runs in useEffect after hydration llms.txt: - Clarify that llms-full.txt is hosted on the main site (landing), not in blog-website repo. URL is correct since blog is proxied through landing's Vercel deployment. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: slayerjain <shubhamkjain@outlook.com> --------- Signed-off-by: Neha Gupta <gneha21[at]yahoo> Signed-off-by: slayerjain <shubhamkjain@outlook.com> Co-authored-by: Neha Gupta <gneha21[at]yahoo> Co-authored-by: slayerjain <shubhamkjain@outlook.com> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: heetmehta18 <heetmehta18125@gmail.com>
* feat: enrich blog structured data and llms.txt for GEO/AEO lib/structured-data.ts: - Add dateModified, articleSection to BlogPosting schema - Add author Person with url using sanitizeAuthorSlug (matches actual routes) - Add publisher logo width/height for ImageObject compliance - Add getBlogSchema() export using shared constants lib/api.ts: - Add modified field to PostFields GraphQL fragment pages/_document.tsx: - Add Organization and Blog JSON-LD via shared helpers (single source) pages/technology/[slug].tsx, pages/community/[slug].tsx: - Pass dateModified and articleSection to BlogPosting schema public/llms.txt: - Rewrite with blog architecture, 500+ articles, categories, awards, products, author pages, and comparison hub link Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: slayerjain <shubhamkjain@outlook.com> * fix: resolve 2 copilot comments on PR #359 lib/structured-data.ts: - Handle ppmaAuthorName as array (sometimes returned as array from WordPress) — take first element if array public/llms.txt: - Clarify author URL uses sanitized slug format, not raw name Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: slayerjain <shubhamkjain@outlook.com> * fix: align authorName type with runtime handling Type authorName as string | string[] to match WordPress ppmaAuthorName which can be either format. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: slayerjain <shubhamkjain@outlook.com> * fix: resolve 2 copilot comments on PR #359 lib/structured-data.ts: - Handle empty authorName array by falling back to ORG_NAME - Remove hardcoded logo width/height that don't match actual asset Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: slayerjain <shubhamkjain@outlook.com> * Update public/llms.txt Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Signed-off-by: Neha Gupta <gneha21@yahoo.in> * fix: resolve 4 copilot comments on PR #359 lib/structured-data.ts: - Only set author.url for real authors, not org fallback (avoids /authors/keploy 404) tests/fixtures: - Add modified field to single-post, technology-posts, community-posts mock fixtures public/llms.txt: - Remove hardcoded star count that goes stale Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: slayerjain <shubhamkjain@outlook.com> * fix: add modified field to revision node query Ensures dateModified in JSON-LD stays accurate during preview/revision flows where Object.assign overwrites post fields from revision data. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: slayerjain <shubhamkjain@outlook.com> * fix: resolve 3 copilot comments on PR #359 lib/structured-data.ts: - Use BLOG_NAME constant instead of hardcoded string in getBlogSchema - Simplify Blog publisher logo to URL string (SVG has no fixed dimensions) types/post.ts: - Add optional modified field to Post interface Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: slayerjain <shubhamkjain@outlook.com> * fix: restore ImageObject for Blog publisher logo Keep consistent with BlogPosting publisher logo format. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: slayerjain <shubhamkjain@outlook.com> * fix: use primitive number type instead of boxed Number Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: slayerjain <shubhamkjain@outlook.com> * fix: add actual logo dimensions to BlogPosting publisher Group.png is 462x539 — added matching width/height to ImageObject. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: slayerjain <shubhamkjain@outlook.com> * fix: resolve 4 copilot comments on PR #359 - Reformat all test fixture JSON files with consistent indentation - Use ORG_LOGO_URL in getBlogSchema publisher logo for consistency with getOrganizationSchema and getBlogPostingSchema Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: slayerjain <shubhamkjain@outlook.com> --------- Signed-off-by: slayerjain <shubhamkjain@outlook.com> Signed-off-by: Neha Gupta <gneha21@yahoo.in> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Co-authored-by: Neha Gupta <gneha21@yahoo.in> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Signed-off-by: heetmehta18 <heetmehta18125@gmail.com>
Signed-off-by: slayerjain <shubhamkjain@outlook.com> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: heetmehta18 <heetmehta18125@gmail.com>
* fix: repair malformed vercel.json (missing closing braces)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: slayerjain <shubhamkjain@outlook.com>
* fix: resolve 799 duplicate meta descriptions flagged by Bing
Root cause: listing pages (tag, author, search, index) used hardcoded
or ultra-generic meta descriptions, not WordPress content.
- community/index: fix copy-paste bug ("Technology" → "Community")
- technology/index: unique 155-char description
- tag/[slug]: dynamic description including tag name
- tag/index: unique description for tag listing
- authors/[slug]: dynamic description including author name
- authors/index: unique description for author listing
- search, community/search: conditional description with empty-state
- community/[slug], technology/[slug]: quality gate for Yoast metaDesc
(fallback to title-based description if Yoast desc is <60 chars)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: slayerjain <shubhamkjain@outlook.com>
* perf: fix blog Core Web Vitals (LCP, CLS, TBT)
Font loading:
- Remove render-blocking @import for Baloo 2 from CSS
- Add non-blocking font preloads with print→all swap pattern
- Remove duplicate per-page DM Sans link tags
Third-party scripts:
- Move all 5 analytics scripts to lazyOnload strategy
- Remove duplicate telemetry script from _document.tsx
Image optimization:
- Add priority prop to CoverImage for LCP (post header only)
- Add sizes attribute and explicit loading prop
- Convert raw <img> to next/image in BlogSidebar
- Add AVIF/WebP formats and 1-year cache TTL in next.config
- Add content-visibility:auto and aspect-ratio for WP images
JavaScript bundle:
- Dynamic import CodeMirror, BlogSidebar, JsonDiffViewer
- Remove duplicate PrismJS from slug pages
- Replace framer-motion with CSS @Keyframes fadeIn (~30-40kB saved)
- Remove AnimatePresence wrapper from _app.tsx
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: slayerjain <shubhamkjain@outlook.com>
* fix: technical SEO — clean up sitemap, add canonicals, fix duplicates
- Remove 16 problematic URLs from static sitemap (non-blog URLs,
wrong-category duplicates)
- Cross-category duplicate fix: posts at wrong /community/ or
/technology/ path now 301 redirect to correct category
- Add canonical URLs to 7 listing pages (index, tag, author)
- Add noindex to search pages
- Fix soft 404 on empty author pages (return proper 404)
- Fix 302→301 redirects on technology slug pages
- Fix robots.txt sitemap URL (www.keploy.io → keploy.io)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: slayerjain <shubhamkjain@outlook.com>
* fix: resolve 12 copilot review comments on PR #362
tag/[slug].tsx:
- Handle undefined/string[] router.query.slug for title/description
technology/[slug].tsx + community/[slug].tsx:
- Add safeTitle guard for fallback render (prevents "undefined")
- Apply description quality gate to both Layout and JSON-LD schema
- Validate category redirect against ['community','technology'] allowlist
- Fix unsafe post?.seo.title access
_document.tsx:
- Replace broken string onLoad with preload+stylesheet pattern
(React ignores string event handlers in JSX)
post-body.tsx:
- Fix misleading comment about CodeMirror lazy imports
post-body.module.css:
- Replace unsupported attr() aspect-ratio with fixed 16/9
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: slayerjain <shubhamkjain@outlook.com>
* fix: address remaining Copilot review comments on PR #362
- Category redirect: validate against ['community','technology'] allowlist,
return notFound for unknown categories instead of guessing
- Community redirect: use data.post.slug (canonical CMS slug) not request param
- Fallback description: return generic description during router.isFallback
instead of "Learn about Loading..."
- CSS: remove forced 16/9 aspect-ratio on WP images (browsers use intrinsic ratio)
- BlogSidebar: add sizes="320px" to next/image to prevent oversized downloads
- CodeMirror: clarify comment about lazy loading (PostBody is the dynamic boundary)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: slayerjain <shubhamkjain@outlook.com>
* fix: address round 2 Copilot review on blog PR #362
- Update E2E test assertion for new technology index description
- Strip HTML tags and entities from post.title before meta/JSON-LD
- Return notFound for invalid/empty author slugs (was soft 404)
- Add contain-intrinsic-size to content-visibility:auto for CLS stability
- Correct framer-motion removal comment in layout.tsx
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: slayerjain <shubhamkjain@outlook.com>
* fix: pass tag slug as prop from getStaticProps, make CoverImage sizes configurable
- tag/[slug]: pass tagSlug from getStaticProps so SSR meta tags are correct
- CoverImage: make sizes a prop (default 780px for post header, overridable
for smaller card contexts)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: slayerjain <shubhamkjain@outlook.com>
* fix: replace cross-category redirect with notFound to fix prerendering error
redirect cannot be returned from getStaticProps during prerendering.
Return notFound instead — the post will be served from its correct
category path at runtime via ISR fallback.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: slayerjain <shubhamkjain@outlook.com>
* fix: E2E failure — add community post fixture to mock server, decode HTML entities
- Add single-community-post.json fixture with category "community"
- Mock server now returns community-categorized post for community slugs
- Decode HTML entities in safeTitle instead of stripping them (’ → ')
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: slayerjain <shubhamkjain@outlook.com>
* fix: restore 301 redirect for cross-category posts to preserve SEO signals
notFound drops link equity. Redirect is safe because getStaticPaths only
returns same-category paths — the redirect only fires at ISR runtime for
fallback pages, not during next build prerendering.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: slayerjain <shubhamkjain@outlook.com>
* fix: extract SEO helpers, fix fixture quotes, add motion-reduce
- Extract sanitizeTitle/getSafeDescription to utils/seo.ts (shared between
technology and community slug pages)
- Fix single-community-post.json: use double-quoted HTML attrs to match
the regex patterns in page code
- Add motion-reduce:animate-none to layout fade-in for accessibility
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: slayerjain <shubhamkjain@outlook.com>
* fix: harden SEO helpers — sanitize metaDesc, handle empty title, normalize whitespace
- sanitizeTitle returns empty string instead of 'Loading...' placeholder
- getSafeDescription sanitizes metaDesc (strip tags, decode entities, trim)
before length gate
- Handle empty safeTitle gracefully in fallback description
- Normalize whitespace in all decoded strings
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: slayerjain <shubhamkjain@outlook.com>
---------
Signed-off-by: slayerjain <shubhamkjain@outlook.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: Neha Gupta <gneha21@yahoo.in>
Signed-off-by: heetmehta18 <heetmehta18125@gmail.com>
* fix: crash on 404 page when post title or excerpt is null WordPress can return posts with null title/excerpt. NotFoundPage.tsx called .toLowerCase() on these without null-checking, causing "Cannot read properties of null" during both SSR prerendering and client-side rendering. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: slayerjain <shubhamkjain@outlook.com> * fix: address Copilot review — null-safe filtering across all search components - Precompute normalizedSearchTerm to avoid repeated toLowerCase() calls - Apply null-safe filtering to more-stories.tsx and community/search.tsx - Update Post type: title and excerpt are string | null (matches WP reality) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: Neha Gupta <gneha21@yahoo.in> * fix: normalize null title/excerpt at component boundary + harden getExcerpt - NotFoundPage: normalize allPosts once via .map() so downstream rendering (getExcerpt, dangerouslySetInnerHTML) never sees null - getExcerpt: return empty string for null/undefined input - Mock fixture: add post with null title/excerpt to catch regressions in E2E tests Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: Neha Gupta <gneha21@yahoo.in> * fix: normalize null title/excerpt in more-stories and community search Posts with null title/excerpt from WordPress are now defaulted to empty strings after filtering, before they reach rendering components (HeroPost, getExcerpt, dangerouslySetInnerHTML). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: Neha Gupta <gneha21@yahoo.in> * fix: add postId to null fixture, clean up redundant fallbacks, add E2E test - Add missing postId to null title/excerpt test fixture - Remove redundant || '' in filter (allPosts.map already normalizes) - Add E2E test asserting no pageerror events on 404 page Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: Neha Gupta <gneha21@yahoo.in> * fix: normalize null posts at API layer, revert Post type to non-nullable - Add normalizePostNode/normalizePostEdges in lib/api.ts — defaults null title/excerpt to empty string at the data boundary - Revert Post.title/excerpt back to string (not nullable) since API layer now guarantees non-null values - Fix E2E test: register pageerror handler before navigation, use dedicated 404 URL instead of page.url() Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: Neha Gupta <gneha21@yahoo.in> * fix: normalize revision node before Object.assign to prevent null re-introduction getPostAndMorePosts() applies revision data via Object.assign which can overwrite the already-normalized title/excerpt with null values from the revision node. Normalize the revision before applying it. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: Neha Gupta <gneha21@yahoo.in> --------- Signed-off-by: slayerjain <shubhamkjain@outlook.com> Signed-off-by: Neha Gupta <gneha21@yahoo.in> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Co-authored-by: Neha Gupta <gneha21@yahoo.in> Signed-off-by: heetmehta18 <heetmehta18125@gmail.com>
…l.txt (#364) * fix: crash on 404 page when post title or excerpt is null WordPress can return posts with null title/excerpt. NotFoundPage.tsx called .toLowerCase() on these without null-checking, causing "Cannot read properties of null" during both SSR prerendering and client-side rendering. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: slayerjain <shubhamkjain@outlook.com> * fix: address Copilot review — null-safe filtering across all search components - Precompute normalizedSearchTerm to avoid repeated toLowerCase() calls - Apply null-safe filtering to more-stories.tsx and community/search.tsx - Update Post type: title and excerpt are string | null (matches WP reality) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: Neha Gupta <gneha21@yahoo.in> * fix: normalize null title/excerpt at component boundary + harden getExcerpt - NotFoundPage: normalize allPosts once via .map() so downstream rendering (getExcerpt, dangerouslySetInnerHTML) never sees null - getExcerpt: return empty string for null/undefined input - Mock fixture: add post with null title/excerpt to catch regressions in E2E tests Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: Neha Gupta <gneha21@yahoo.in> * fix: normalize null title/excerpt in more-stories and community search Posts with null title/excerpt from WordPress are now defaulted to empty strings after filtering, before they reach rendering components (HeroPost, getExcerpt, dangerouslySetInnerHTML). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: Neha Gupta <gneha21@yahoo.in> * fix: add postId to null fixture, clean up redundant fallbacks, add E2E test - Add missing postId to null title/excerpt test fixture - Remove redundant || '' in filter (allPosts.map already normalizes) - Add E2E test asserting no pageerror events on 404 page Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: Neha Gupta <gneha21@yahoo.in> * fix: normalize null posts at API layer, revert Post type to non-nullable - Add normalizePostNode/normalizePostEdges in lib/api.ts — defaults null title/excerpt to empty string at the data boundary - Revert Post.title/excerpt back to string (not nullable) since API layer now guarantees non-null values - Fix E2E test: register pageerror handler before navigation, use dedicated 404 URL instead of page.url() Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: Neha Gupta <gneha21@yahoo.in> * fix: normalize revision node before Object.assign to prevent null re-introduction getPostAndMorePosts() applies revision data via Object.assign which can overwrite the already-normalized title/excerpt with null values from the revision node. Normalize the revision before applying it. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: Neha Gupta <gneha21@yahoo.in> * feat(seo): GEO/AEO Phase 1 — fix meta tags, favicon paths, robots.txt, and add llms-full.txt - Fix favicon paths in meta.tsx: add /blog/ prefix and .png extension - Add og:site_name and og:locale meta tags for better social sharing - Fix subscribe-newsletter alt text from generic "Image" to descriptive - Remove invalid subdomain path disallows from robots.txt - Add llms-full.txt for comprehensive AI/LLM crawler content - Update llms.txt to link to llms-full.txt Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: Neha Gupta <gneha21@yahoo.in> * fix: address Copilot review — remove redundant .map() normalization, harden test - Remove redundant .map() normalization in search, more-stories, NotFoundPage since fetchAPI already normalizes title/excerpt to empty strings - Use null-safe access in filter predicates instead - Harden NotFoundPage test: wait for load state, capture console errors, add post-render buffer to catch hydration errors Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: Neha Gupta <gneha21@yahoo.in> * perf: memoize allPosts and filteredAllPosts in NotFoundPage Wrap with useMemo to avoid O(n) recomputation on every countdown tick (component re-renders every second). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: Neha Gupta <gneha21@yahoo.in> * fix(test): only catch pageerror in 404 test, not console.error Console.error captures CSP violations and network failures from the test environment that are unrelated to our code changes. Revert to pageerror-only which catches actual uncaught exceptions. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: Neha Gupta <gneha21@yahoo.in> --------- Signed-off-by: slayerjain <shubhamkjain@outlook.com> Signed-off-by: Neha Gupta <gneha21@yahoo.in> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Co-authored-by: Neha Gupta <gneha21@yahoo.in> Signed-off-by: heetmehta18 <heetmehta18125@gmail.com>
* fix: GSC audit — 28 duplicate URL redirects, OG tags, favicon, dateModified - Added 28 permanent redirects for duplicate /community/ and /technology/ blog URLs based on WordPress category mapping - Added 2 broken backlink redirects (end-to-end-testing, cursor-vs-copilot) - Added article:published_time meta tag to post pages - Fixed favicon paths to reference correct files with /blog/ prefix - Fixed structured data logo URL - Removed duplicate font preload from authors page Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: Neha Gupta <gneha21@yahoo.in> * fix: update publisher.logo dimensions to 512x512 to match android-chrome-512x512.png Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: Neha Gupta <gneha21@yahoo.in> --------- Signed-off-by: Neha Gupta <gneha21@yahoo.in> Co-authored-by: Neha Gupta <gneha21@yahoo.in> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: heetmehta18 <heetmehta18125@gmail.com>
Signed-off-by: heetmehta18 <heetmehta18125@gmail.com>
Signed-off-by: Alok Kumar <101720005+ALOK07K@users.noreply.github.com> Signed-off-by: heetmehta18 <heetmehta18125@gmail.com>
* fix: GSC audit — 28 duplicate URL redirects, OG tags, favicon, dateModified
- Added 28 permanent redirects for duplicate /community/ and /technology/
blog URLs based on WordPress category mapping
- Added 2 broken backlink redirects (end-to-end-testing, cursor-vs-copilot)
- Added article:published_time meta tag to post pages
- Fixed favicon paths to reference correct files with /blog/ prefix
- Fixed structured data logo URL
- Removed duplicate font preload from authors page
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: Neha Gupta <gneha21@yahoo.in>
* fix: update publisher.logo dimensions to 512x512 to match android-chrome-512x512.png
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: Neha Gupta <gneha21@yahoo.in>
* ci: add IndexNow auto-submission for latest 100 blog posts
Queries WordPress GraphQL for 100 most recently modified posts and
submits their URLs to Bing IndexNow on every push to main.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: Neha Gupta <gneha21@yahoo.in>
* ci: add daily cron and manual trigger for IndexNow blog submission
Queries WordPress for 100 most recently modified posts and submits
to Bing IndexNow. Runs on push to main, daily at 6 AM UTC, or manually.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: Neha Gupta <gneha21@yahoo.in>
* ci: run IndexNow every 12 hours instead of daily
Blog edits happen daily, so twice-daily (6 AM + 6 PM UTC) ensures
modified posts get submitted to Bing promptly.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: Neha Gupta <gneha21@yahoo.in>
* ci+seo: harden IndexNow workflow, add dynamic blog sitemap, test article:published_time
IndexNow workflow
- Use working key feeeddbe661a4686ba6e4d5cb663b2cb (keys are per-host;
this is the same key served from keploy.io/<key>.txt by landing, so
it's already verified by Bing)
- Add keyLocation to payload
- set -euo pipefail and curl --fail-with-body so fetch/parse errors
aren't masked
- Capture CURL_EXIT separately from HTTP_CODE; exit non-zero with
::error:: on both curl failure and real non-2xx (2xx and 202 accepted
per IndexNow spec)
- Top 20 most recently modified posts instead of 100
- 24h cron instead of 12h
- Troubleshooting block with keyLocation verify + POST reproducer
- tr -d whitespace on URL_COUNT so log reads "Submitted 20 URLs" not
"Submitted 20 URLs"
Dynamic sitemap (pages/sitemap.xml.tsx)
- Rebuilds sitemap-blog.xml from scratch directly from WP GraphQL
- Paginated fetch of all posts, not just first 50
- Includes /blog, /blog/community, /blog/technology index pages
- Cache-Control: s-maxage=86400, stale-while-revalidate=86400 so at most
~1 WP fetch per day per Vercel edge region (no build-time cost, no
cron-triggered deploys)
- Replaces the hand-maintained public/sitemap.xml frozen at 2024-03-07
Playwright SEO meta coverage
- Assert meta[property="article:published_time"] exists on post pages
and matches an ISO-8601 datetime so regressions in the new
publishedDate pipeline are caught in CI
Signed-off-by: Neha Gupta <gneha21@yahoo.in>
* fix(ci+sitemap): surface WP GraphQL errors + add sitemap E2E coverage
- pages/sitemap.xml.tsx: throw when WPGraphQL returns a non-empty top-level
errors array so partial/empty data can't silently render an incomplete
sitemap (matches the error-handling contract in lib/api.ts fetchAPI)
- .github/workflows/indexnow.yml: jq-check for an errors array in the WP
response and fail the run with ::error:: before submitting, so scheduled
runs reliably surface CMS/query issues instead of quietly falling back
to submitting just /blog/
- tests/e2e/SeoMeta.spec.ts: assert /sitemap.xml returns HTTP 200, an
application/xml content type, and at least the three static entries
(/blog, /blog/community, /blog/technology) that are always rendered
regardless of WP fetch results
Signed-off-by: nehagup <15074229+nehagup@users.noreply.github.com>
* test(e2e): use absolute URL in sitemap test to respect /blog basePath
Playwright's request.get resolves a leading-slash path against the host
rather than against baseURL's path, so request.get('/sitemap.xml') was
hitting http://localhost:3000/sitemap.xml and bypassing Next.js's /blog
basePath — the server returned 404 instead of the dynamic sitemap.
Pass the full URL via the baseURL fixture (\${baseURL}/sitemap.xml) so
it resolves to http://localhost:3000/blog/sitemap.xml, matching how
page.goto is used in the other tests in this file.
Signed-off-by: nehagup <15074229+nehagup@users.noreply.github.com>
* fix(sitemap+ci): reuse MAIN_SITE_URL, degrade gracefully on WP failure
- pages/sitemap.xml.tsx: import MAIN_SITE_URL from lib/structured-data
instead of duplicating the canonical host, so a future domain change
only touches one file
- pages/sitemap.xml.tsx: catch WP fetch/GraphQL errors in
getServerSideProps and render a valid sitemap containing at least the
three static blog entries (/blog, /blog/community, /blog/technology)
rather than returning 500. Degraded responses get a much shorter 5min
edge cache so a transient WP outage doesn't pin the stripped-down
sitemap for a full day, and the error is still logged to Vercel
function logs for alerting
- .github/workflows/indexnow.yml: extend the WP-GraphQL-errors branch
with a Troubleshooting block (endpoint health curl, GraphiQL link,
common causes) matching the IndexNow-failure block, so operators
can act on the CI error without context switching
- tests/e2e/SeoMeta.spec.ts: use baseURL! non-null assertion in the
sitemap test, matching the convention in the rest of the file
Signed-off-by: nehagup <15074229+nehagup@users.noreply.github.com>
* fix(favicon): replace Next.js default favicons with Keploy mascot
Every file in public/favicon/ was the Next.js starter template:
- favicon.ico / *.png were the "N" logo
- safari-pinned-tab.svg was the "N" silhouette
- site.webmanifest had name "Next.js", theme #000000, and referenced
/favicons/* (wrong directory and missing /blog basePath)
- browserconfig.xml had tile color #000000 and the same wrong path
Regenerated from docs/static/img/favicon.png (1280x1496 Keploy mascot)
padded to a square RGBA canvas and resized to 16, 32, 150, 180, 192,
512. favicon.ico contains 16/32/48 multi-resolution. safari-pinned-tab
now uses the monochrome Keploy logo from docs/static/keploy-logo.svg.
site.webmanifest: name=Keploy Blog, short_name=Keploy, icons point at
/blog/favicon/*, theme_color=#FF914D (Keploy orange), background
bumped from #000000 to #ffffff to match the light blog theme.
browserconfig.xml: tile path /blog/favicon/mstile-150x150.png,
TileColor=#FF914D.
Signed-off-by: nehagup <15074229+nehagup@users.noreply.github.com>
* fix(sitemap): make degraded-path log actionable for on-call
Replace the single-line console.error with a structured payload
containing (a) the effective WP GraphQL endpoint, (b) whether
WORDPRESS_API_URL env var is set (so wrong-env issues vs real WP
outages are distinguishable from a single log line), (c) the error
message, and (d) a numbered nextSteps array — health-check curl,
plugin/host check, schema validation in GraphiQL, env var fix, and
self-heal window. Goal: on-call can diagnose from Vercel logs alone
without needing to cross-reference the sitemap code.
Signed-off-by: nehagup <15074229+nehagup@users.noreply.github.com>
* fix(sitemap): drop misleading WORDPRESS_API_URL fallback
next.config.js hard-requires WORDPRESS_API_URL at startup (throws if
the env var is unset or can't be URL-parsed), so the
`|| "https://wp.keploy.io/graphql"` fallback and the `envVarSet`
degraded-log field were effectively dead code in any running build —
which is misleading when reading the file in isolation.
Read the env var directly (cast to string since next.config.js has
already validated it), drop the envVarSet field from the structured
log, and rephrase the fourth next-step from "if env var is unset"
to "if the endpoint logged above is unexpected, double-check
WORDPRESS_API_URL in Vercel settings" — same diagnostic value,
without pretending there's a runtime fallback.
Signed-off-by: nehagup <15074229+nehagup@users.noreply.github.com>
* fix(favicon): sync theme-color + msapplication-TileColor with manifest
site.webmanifest was updated to theme_color #FF914D (Keploy orange) in
the earlier favicon commit, but the matching meta[name="theme-color"]
and meta[name="msapplication-TileColor"] tags in components/meta.tsx
were still pointing at #000000/#000 — the Next.js starter values.
That caused platform-inconsistent theming: PWA install prompt shows
Keploy orange (reads manifest), Chrome address bar + Windows Start
tile show black (read meta tags). Bringing both to #FF914D for a
single consistent brand color across every surface.
Signed-off-by: nehagup <15074229+nehagup@users.noreply.github.com>
---------
Signed-off-by: Neha Gupta <gneha21@yahoo.in>
Signed-off-by: nehagup <15074229+nehagup@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: nehagup <15074229+nehagup@users.noreply.github.com>
Signed-off-by: heetmehta18 <heetmehta18125@gmail.com>
…373) * fix(robots): nuanced AI bot policy — allow search, block training Previously the blog allowed GPTBot, ClaudeBot, anthropic-ai, CCBot, Google-Extended, Applebot-Extended, Meta-ExternalAgent — i.e., training crawlers — alongside the search variants. This gave content to future model training runs without any visibility benefit in current answer engines. New policy: - Allow AI SEARCH bots (drive answer visibility): OAI-SearchBot, ChatGPT-User, Claude-SearchBot, Claude-User, PerplexityBot, Perplexity-User, Gemini-Deep-Research, GoogleOther, Applebot, DuckAssistBot, Amazonbot. - Block TRAINING-ONLY bots: GPTBot, ClaudeBot, anthropic-ai, CCBot, Google-Extended, Applebot-Extended, Meta-ExternalAgent, FacebookBot, cohere-ai, Diffbot, Omgilibot, ImagesiftBot. - Keep Bytespider blocked. Matches Speedscale / Katalon / Testsigma best-practice from the 2026 competitor audit. Reopens Task 52 per user direction 2026-04-14 (supersedes Q9 allow-all stance). Mirrors the corresponding policy change on the landing site. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: Neha Gupta <gneha21@yahoo.in> * fix(authors): LIVE-11 — add <title> + Person schema + better Title Author pages were returning 200 OK with <title></title> empty because: 1. components/meta.tsx emits twitter:title and og:title but not an actual <title> tag. 2. pages/authors/[slug].tsx passed Title to Layout but never added its own <title> to the Head, unlike pages/community/[slug].tsx. The result was that keploy.io/blog/authors/keploy-team and every other author page had completely empty document titles, which killed CTR in search results and gave AI crawlers no signal about the author entity. Changes: - Add next/head import and emit <title>{pageTitle}</title> explicitly. - Default authorName to "Keploy Author" when missing instead of letting undefined propagate into the template (was producing "undefined Page"). - Rename page title format from "{name} Page" to "{name} — Keploy Blog Author" for clearer search snippets. - Extract author URL / slug once, reuse everywhere. - Add Person JSON-LD schema to structuredData array. Minimum viable set (name, url, jobTitle, worksFor, knowsAbout) — full sameAs (LinkedIn / GitHub / Twitter) is deferred to GEO-05 which requires per-author consent on which links to publish. Verify after deploy: curl -s https://keploy.io/blog/authors/keploy-team | \ python3 -c "import re,sys; h=sys.stdin.read(); \ print('title:', re.findall(r'<title[^>]*>([^<]*)</title>', h)[0]); \ print('Person count:', h.count('\"@type\":\"Person\"'))" # expected: non-empty title, 1+ Person blocks Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: Neha Gupta <gneha21@yahoo.in> * fix(redirects): Task 42 + Task 43 + Task 46 — 28 cross-category + broken backlink redirects Task 42 — Resolve 26 blog posts accessible at BOTH /blog/community/ and /blog/technology/ URLs. Google flags these as duplicate content. Per WP category mapping, add 301 redirects so each post resolves to its canonical category. 13 posts: technology is canonical, community redirects to it (bitbucket-self-hosting-running-ebpf..., building-a-cli-tool-in-go, create-stunning-parallax-animations, gemini-pro-vs-openai-benchmark, how-to-use-covdata, integration-of-e2e-testing-in-a-cicd-pipeline, mastering-nyc, migration-guide-from-restassured-to-keploy, protocol-parsing-guide, revolutionising-unit-test-generation-with-llms, scram-authentication, secure-your-database-communications, using-tc-bpf-program) 13 posts: community is canonical, technology redirects to it (canary-testing, codium-vs-copilot, decoding-brd, decoding-http2, how-to-generate-test-cases, mock-vs-stub-vs-fake, performance-testing, python-get-current-directory, top-5-cypress-alternatives, understanding-branch-coverage, understanding-statement-coverage, what-is-postgres-wire-protocol, writing-test-cases-for-cron-jobs) Task 43 — Fix broken backlink /blog/community/end-to-end-testing-and-why-do-you-need-it (8 external backlinks from DR 8-74 domains: dlatesterow.pl, electronicsweekly.com, erpwebtutor.com, apsense.com, sqlgulf.org, nftweightlossforum.com, sobergroup.com). Redirect to the closest existing post: integration-of-e2e-testing-in-a-cicd-pipeline. Task 46 — Fix broken malformed-double-path URL /blog/community/https-keploy-io-blog-community-cursor-vs-github-copilot (1 external backlink). Redirect to the canonical cursor-vs-copilot slug. Total redirects go from 4 → 32. All validated via JSON parse. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: Neha Gupta <gneha21@yahoo.in> * feat(schema): GEO-13 — TechArticle schema for blog/technology posts blog/community posts stay as BlogPosting. blog/technology posts now emit @type=TechArticle, which is the schema.org type specifically intended for 'a technical article — typically an on-line manual, describing how to accomplish a task.' AI models (Perplexity, ChatGPT, Claude) weight TechArticle higher than generic BlogPosting for developer-oriented queries. Changes: - lib/structured-data.ts: add categorySlug, dependencies, and proficiencyLevel to BlogPostingInput. Branch @type on categorySlug. TechArticle-specific fields (dependencies, proficiencyLevel) are only attached when @type === TechArticle. - pages/technology/[slug].tsx: pass categorySlug: 'technology' and proficiencyLevel: 'Intermediate' (conservative default). pages/community/[slug].tsx intentionally unchanged — community posts remain BlogPosting per GEO-13 design. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: Neha Gupta <gneha21@yahoo.in> * fix(copilot-review): 4 comments on blog PR #373 Comment 1 - public/robots.txt /api/proxy-image: Disallow: /api/ was blocking same-origin image proxy endpoint used by UI components to render external avatars. Added explicit Allow: /api/proxy-image above the broader /api/ disallow. Comment 2 - public/robots.txt /search contradiction: pages/search.tsx already emits <meta name="robots" content="noindex, follow" />. Blocking via robots.txt would hide the noindex signal and cause 'Indexed, though blocked by robots.txt' warnings. Removed the Disallow: /search line and kept the meta tag as the single source of truth. Added inline comment explaining the choice. Comment 3 - pages/authors/[slug].tsx worksFor.url: Person.worksFor.url was pointing at SITE_URL (https://keploy.io/blog) instead of the main Organization URL. Imported MAIN_SITE_URL and switched worksFor.url to it so Organization identity is consistent across every JSON-LD payload on the site. Comment 4 - AuthorDetailPage E2E regression guard: Previous test asserted 'title is defined' which still passes for an empty string (the exact bug LIVE-11 was fixing). Tightened to require title.trim().length > 0 and title not starting with 'undefined'. Added a second test that parses every script[type=application/ld+json] block and asserts at least one Person schema is present in the initial HTML payload — this catches any regression that removes the Person schema or moves it to a client-hydrated script tag. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: Neha Gupta <gneha21@yahoo.in> * fix(blog): author/reviewer rendering + schema — 4 reported bugs Reporter flagged 4 issues on 2026-04-14 where WordPress/PublishPress data was present in the page but was being rendered inconsistently or not at all, confusing both human readers and AI crawlers. Problem 1 — Author mismatch (Animesh Pathak vs Alok Kumar) The GraphQL query for slug pages fetched BOTH `author { node { name } }` (the WordPress system author who published the post) AND `ppmaAuthorName` (the PublishPress Multiple Authors display author). The display + schema correctly used ppmaAuthorName, but the raw `author` field was still serialized into __NEXT_DATA__, giving AI crawlers a third conflicting author signal. Fix: remove `author { node { ...AuthorFields } }` and the AuthorFields fragment reference from the PostFields fragment in lib/api.ts. The revision branch also drops the raw author lookup. AuthorMapping.tsx (which does consume raw `author.node.name`) uses a separate `getAllAuthors` query that is untouched. Problem 2 — Reviewer placeholder "Reviewer" in rendered HTML reviewAuthorDetails arrived in props from getStaticProps but was being read inside a `useEffect` that called setState. That means the reviewer name only populated AFTER client hydration — the SSR HTML shipped with the literal fallback string "Reviewer" in the Written By / Reviewed By block, which is exactly what AI crawlers read. Fix: in both pages/community/[slug].tsx and pages/technology/[slug].tsx, replace the useState+useEffect pattern with synchronous render-time computation from props. reviewerNode, reviewAuthorName, reviewAuthorImageUrl, and reviewAuthorDescription are all derived on first render so the SSR HTML contains the real reviewer data. Problem 3 — Author photo shows /blog/images/author.png placeholder The writer avatar was being extracted from post.content via a regex against the pp-author-boxes-avatar div, inside a useEffect. Same SSR timing bug as #2 — initial HTML showed the placeholder, the real image only swapped in after hydration. Fix: query `ppmaAuthorImage` directly in the PostFields fragment (lib/api.ts — the field is already used by getAllAuthors so we know it exists on the WP schema) and use post.ppmaAuthorImage at render time. The brittle content-regex is gone. A safe /blog/images/author.png fallback remains for posts that genuinely have no author image. Problem 4 — BlogPosting schema missing reviewedBy The data for the reviewer was sitting in reviewAuthorDetails but getBlogPostingSchema in lib/structured-data.ts didn't accept a reviewer parameter at all. Fix: extend BlogPostingInput with reviewerName / reviewerImage / reviewerDescription. getBlogPostingSchema emits a `reviewedBy` Person node when reviewerName is present AND is not the "Reviewer" placeholder AND is different from the author (a self-reviewed post is not a useful E-E-A-T signal and AI models discount it). The Person node includes url (/blog/authors/{slug}), image (avatar), and description (job title / bio) when available. Also adds an authorImage field to Person so the author node in the schema carries the real PublishPress avatar. Both slug pages now pass the reviewer fields when building the Article schema. Placeholder detection guards against emitting schema that contradicts the visible HTML. Verification after deploy: curl -s https://keploy.io/blog/community/<any-post> | \ python3 -c "import sys, re, json; \ html = sys.stdin.read(); \ blocks = re.findall(r'application/ld\+json[^>]*>([^<]+)</script>', html); \ for b in blocks: \ d = json.loads(b); \ if isinstance(d, dict) and d.get('@type') in ('BlogPosting', 'TechArticle', 'Article'): \ print('author.name:', d.get('author', {}).get('name')); \ print('author.image:', d.get('author', {}).get('image')); \ print('reviewedBy:', d.get('reviewedBy', {}).get('name'))" # expected: author.name = ppmaAuthorName, author.image = ppmaAuthorImage URL, # reviewedBy.name = Neha Gupta (or current reviewer) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: Neha Gupta <gneha21@yahoo.in> * fix(copilot-review): 4 comments on blog PR #373 round 2 Comment 1 — robots.txt paths missing /blog basePath prefix: The Next.js app is mounted under basePath: '/blog' (next.config.js). Crawlers request /blog/api/*, /blog/wp/*, /blog/cgi-bin/*, not the bare paths. Updated the default User-agent block to prefix every Allow/Disallow with /blog so the intended policy actually matches. Added an explanatory comment at the top of the block so the next editor doesn't revert this. /blog/api/proxy-image remains explicitly allowed; all other /blog/api/* remain disallowed. Comment 2 — vercel.json redirects duplicate next.config.js: Copilot noticed (and they are right) that the 26 cross-category redirects I added in 1fa4232 already exist in next.config.js's redirects() function — verified at lines 74-205. Duplicating them in vercel.json creates two sources of truth that can drift. Reverted vercel.json back to the 4 original pre-existing redirects (the everything-you-need-to-know and regression-testing-tools-rankings pairs) which are NOT in next.config.js. next.config.js remains the single source of truth for all other slug redirects. Comment 3 — redirect chain via end-to-end-testing legacy URL: I had /blog/community/end-to-end-testing-and-why-do-you-need-it → /blog/community/integration-of-e2e-testing-in-a-cicd-pipeline, which would have chained through the community→technology redirect (also already in next.config.js) for an extra hop. next.config.js actually points the legacy URL at /community/end-to-end-testing-guide directly. By reverting vercel.json I'm deferring to next.config.js's destination, which is the canonical 1-hop path. Comment 4 — codium-vs-copilot destination conflict: I had the malformed-double-path URL pointing at /blog/community/cursor-vs-copilot (which may not exist). next.config.js already maps the same legacy URL to /community/codium-vs-copilot-which-ai-coding-assistant-is-best-for-you, which is the real published slug. Reverting vercel.json removes the conflict; next.config.js is the single authority. Net effect: 28 redirect entries removed from vercel.json. They all remain in next.config.js where they were already defined before this PR. robots.txt paths now correctly prefixed with /blog basePath. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: Neha Gupta <gneha21@yahoo.in> * fix(copilot-review): remove unused AuthorFields fragment, repeat blog Disallow rules inside AI-search UA group Addresses 3 unresolved Copilot review comments on PR #373. 1. lib/api.ts — the `fragment AuthorFields on User` definition was left behind in getPostAndMorePosts after the earlier commit removed the `author { ...AuthorFields }` field from the PostFields fragment (that removal fixed the PPMA-vs-native-author mismatch in __NEXT_DATA__). WPGraphQL rejects unused fragment definitions at parse time with a validation error, so leaving the stub in would 500 the PostBySlug query on every request. Removed the fragment block entirely. 2. public/robots.txt — per the robots.txt spec, a bot that matches a named `User-agent:` group reads rules only from THAT group; it does not fall through to `User-agent: *`. That meant OAI-SearchBot, PerplexityBot, Claude-SearchBot, etc. were allowed to crawl /blog/wp/, /blog/api/, /blog/cgi-bin/ even though the global block catches Googlebot. Consolidated all AI-search bots into a single named group with the same Disallow rules (/blog/cgi-bin/, /blog/wp/, /blog/api/) and an explicit Allow for /blog/api/proxy-image so external avatar/image rendering still works. Signed-off-by: Neha Gupta <gneha21@yahoo.in> * fix(api): move author-field explainer out of the graphql template literal The PostBySlug fragment had a multi-line # comment explaining why the raw WP author field is omitted, and the last line referenced "a separate getAllAuthors query". Real WPGraphQL strips # comments during parsing so production was fine — but tests/mock-server.js matches queries by substring and checks query.includes('getAllAuthors') before query.includes('PostBySlug'). So at E2E test time every getPostAndMorePosts call routed to the mock's allAuthorsResponse branch (has data.posts, no data.post). Slug pages saw !data.post, returned notFound from getStaticProps, and every post-page Playwright test waited 15s for data-testid=post-content until the 20min job cancelled. Moved the same note to a // JS comment above the template literal so the query text no longer contains the getAllAuthors substring. Verified on a clean local build: .next/server/pages/technology/ now has 4 individual slug HTML files and community/ has 3 — previously 0 and 0 respectively. Signed-off-by: nehagup <15074229+nehagup@users.noreply.github.com> * fix(copilot-review): remove dead updatedContent state, apply table-wrap at render time Addresses 2 Copilot review comments on PR #373 about pages/community/[slug].tsx. The component declared a useState for updatedContent and a useEffect that computed a table-wrapped version of post.content, but nothing ever read updatedContent — PostBody was receiving the raw content routed through the postBody helper instead. Net result: the responsive table wrapper was silently disabled on every community post, and an extra re-render fired on each post change for no functional gain. Fix (pure cleanup, no behaviour regression — actually restores the table-wrap behaviour that the original code intended): 1. Removed the updatedContent useState declaration and its useEffect. 2. Removed the postBody helper function that was only doing author-link rewrites. 3. Added a top-level transformPostContent(content, ppmaAuthorName) pure function that applies both transformations in one synchronous pass — wraps tables in <div class="overflow-x-auto"> AND rewrites /wp/author/SLUG/ links to /blog/authors/PPMA_NAME/. 4. Pass transformPostContent(post?.content, post?.ppmaAuthorName) directly into PostBody at render time. SSR HTML now contains both transformations, so AI crawlers see the final transformed content in the initial HTML payload (matches the pattern used for the author/reviewer fix earlier in this branch). 5. Dropped the unused useState import. Net effect: one less useState, one less useEffect, one less re-render on post navigation, and the responsive table wrapper actually ships. Only pages/community/[slug].tsx has the dead updatedContent pattern — pages/technology/[slug].tsx only has the postBody author-link helper and does not table-wrap, so no parallel change is needed there. Signed-off-by: Neha Gupta <gneha21@yahoo.in> --------- Signed-off-by: Neha Gupta <gneha21@yahoo.in> Signed-off-by: nehagup <15074229+nehagup@users.noreply.github.com> Co-authored-by: Neha Gupta <gneha21@yahoo.in> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Co-authored-by: nehagup <15074229+nehagup@users.noreply.github.com> Signed-off-by: heetmehta18 <heetmehta18125@gmail.com>
* feat: add anouncement bar ui and update for gittogether
Signed-off-by: amaan-bhati <amaanbhati49@gmail.com>
* feat: fix announcement bar on mobile devices, limit it to one single
Signed-off-by: amaan-bhati <amaanbhati49@gmail.com>
* fix: address copilot review comments on announcement bar
- Announcements: switch height-sync to useLayoutEffect so --announcement-h
is written before paint, eliminating the one-frame header/nav overlap flicker
- Announcements: guard ResizeObserver with availability check and rAF-debounce
so the component works in environments without ResizeObserver support
- Announcements: call setAnnouncementHeight("0px") eagerly in handleDismiss
so the content gap collapses on the same frame as the bar disappears
- Announcements: store the swipe-dismiss setTimeout id in a ref and clear it
in a cleanup effect to prevent state updates on unmounted components
- Announcements: make dismiss button visible on all viewports (not lg-only)
so keyboard/screen-reader users on mobile can dismiss without swiping;
add pr-12 on mobile to keep content clear of the button
- Marquee: set animationPlayState to undefined (not 'running') when paused
is false, so the group-hover:paused CSS class still works for pauseOnHover
- Tests: add Playwright spec covering desktop + mobile announcement bar
behaviour including dismiss, --announcement-h CSS var, swipe gesture,
and dismiss button accessibility across breakpoints
Co-authored-by: amaan-bhati <amaan.bhati@keploy.io>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: amaan-bhati <amaanbhati49@gmail.com>
* fix: harden announcement dismiss reset
Co-authored-by: Claude <noreply@anthropic.com>
Signed-off-by: amaan-bhati <amaanbhati49@gmail.com>
* fix: address follow-up announcement review comments
Co-authored-by: Claude <noreply@anthropic.com>
Signed-off-by: amaan-bhati <amaanbhati49@gmail.com>
* chore: fix the tests workflow
Signed-off-by: amaan-bhati <amaanbhati49@gmail.com>
* chore: address minor content change
Signed-off-by: amaan-bhati <amaanbhati49@gmail.com>
---------
Signed-off-by: amaan-bhati <amaanbhati49@gmail.com>
Co-authored-by: amaan-bhati <amaan.bhati@keploy.io>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: heetmehta18 <heetmehta18125@gmail.com>
* added a video banner Signed-off-by: dhananjay6561 <dhananjayaggarwal6561@gmail.com> * fix(sidebar): layout shift placeholder, reduced-motion video, accessible fallback link * fix(sidebar): use AD_ITEMS[number] for accurate ad state type * fix(sidebar): correct fallback image sizes and reduce duplicate video fetches Co-Authored-By: Dhananjay Aggarwal <dhananjayaggarwal6561@gmail.com> --------- Signed-off-by: dhananjay6561 <dhananjayaggarwal6561@gmail.com> Signed-off-by: heetmehta18 <heetmehta18125@gmail.com>
…ent html (#382) * feat: fix llm vidibility bug fix, article body appearence Signed-off-by: amaan-bhati <amaanbhati49@gmail.com> * test: strengthen SSR post body regression assertion Signed-off-by: amaan-bhati <amaanbhati49@gmail.com> * chore: make nbsp fallback explicit Signed-off-by: amaan-bhati <amaanbhati49@gmail.com> --------- Signed-off-by: amaan-bhati <amaanbhati49@gmail.com> Signed-off-by: heetmehta18 <heetmehta18125@gmail.com>
* chore: turn off gitttogether announcement Signed-off-by: amaan-bhati <amaanbhati49@gmail.com> * test: skip announcement bar suite while feature is disabled Signed-off-by: amaan-bhati <amaanbhati49@gmail.com> --------- Signed-off-by: amaan-bhati <amaanbhati49@gmail.com> Signed-off-by: heetmehta18 <heetmehta18125@gmail.com>
729fe03 to
670bf69
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 106 out of 124 changed files in this pull request and generated 4 comments.
Comments suppressed due to low confidence (2)
vercel.json:1
- The broad
/blog/(.*)header rule also matches/blog/_next/static/*. Depending on how Vercel merges/overrides headers, this can result in conflictingCache-Controlvalues and downgrade static asset caching fromimmutableto a shorter TTL. Narrow the/blog/(.*)rule (e.g., exclude/_next/and/api/), or removeCache-Controlfrom the broad rule and keep caching scoped to specific asset/page patterns.
components/testimonials.tsx:1 getInitialsis currently unused, andFallbackAvatarignores the providedname. Either removegetInitialsto reduce dead code, or use it in the fallback UI (e.g., render initials when the image fails) so the helper has a purpose.
| <div className="flex items-center justify-between overflow-visible"> | ||
| {/* Logo */} | ||
| <Link href="/" className="flex items-center gap-2 overflow-hidden z-50 w-[80px]"> | ||
| <Link href="https://keploy.io/" className="flex items-center gap-2 overflow-hidden z-50 w-[80px]"> |
| env: | ||
| INDEXNOW_HOST: keploy.io | ||
| INDEXNOW_KEY: feeeddbe661a4686ba6e4d5cb663b2cb |
| <Image | ||
| width={2000} | ||
| height={1000} | ||
| alt={`Cover Image for ${title}`} | ||
| src={coverImage?.node.sourceUrl} |
| <Link | ||
| href={twitterShare} | ||
| target="_blank" | ||
| rel="noopener noreferrer" | ||
| aria-label="Share on X" | ||
| className={iconCls} | ||
| style={{ backgroundColor: "#000", color: "#fff" }} | ||
| > | ||
| <FaXTwitter /> | ||
| </Link> |

Related Tickets & Documents
Fixes: #3440
Description
This PR revamps the blog landing page’s testimonial section to provide a "flawless" user experience, addressing community feedback regarding UI aesthetics and security. It replaces the legacy marquee with a modern dual-row scrolling layout.
GDG CHARUSAT TEAM ID:
<Team 143>Changes
X-Frame-Options,X-Content-Type-Options,Referrer-Policy,Permissions-Policy) innext.config.js.meta.tsxto ensure they load correctly under the/blogbase path.pages/index.tsx.Type of Change
Testing
npm run dev.npm run buildto ensure no production regressions or hydration errors.Demo
/blogsubdirectory.-Before:
Engineering._.Keploy.Blog.-.Google.Chrome.2026-02-24.21-44-31.mp4
After:
localhost_3000_blog.-.Google.Chrome.2026-02-24.21-51-45.mp4
Environment and Dependencies
next.config.jsto include standard security headers.Checklist