Get found in search and see where sign-ups come from
@slashclone/website-traffic
Get your web app found in Google and AI search, and see which channels bring sign-ups. Sets up sitemap, robots, canonical URLs, share images, and structured data with a page checker, then consent-aware first-touch tracking from UTM tags and referral links, saved at sign-up. Includes a launch channel checklist. Engineering guidance, not legal advice.
- Activity
- New: be the first to try it
- Installs
- 0 installs
- Likes
- 0 likes
- Clones
- 0 clones
- 1// Example tests (Vitest style). Adapt imports and the render helper to the project.
- 2// The point: render the real help page, then prove every visible question is marked up,
- 3// the JSON-LD parses, and no answer carries markup.
- 5import { describe, expect, it } from "vitest";
- 6import { faqPage, itemList, plainText, safeJson, softwareApplication } from "./structured-data";
- 7// import { renderHelpPage } from "../app/help/render"; // returns the page HTML as a string
- 9function jsonLdBlocks(html: string): unknown[] {
- 10 const re = /<script[^>]*type="application\/ld\+json"[^>]*>([\s\S]*?)<\/script>/g;
- 11 return [...html.matchAll(re)].map((m) => JSON.parse(m[1]));
- 12}
- 14describe("structured data", () => {
- 15 it("marks up every FAQ on the help page with plain-text answers", async () => {
- 16 const html =
- 17 "<h3>Is there a free plan?</h3>" +
- 18 `<script type="application/ld+json">${safeJson(
- 19 faqPage([
- 20 { question: "Is there a free plan?", answer: 'Yes. See <a href="/pricing">pricing</a>.' },
- 21 ]),
- 22 )}</script>`; // replace with: await renderHelpPage()
- 23 const visible = [...html.matchAll(/<h3>([^<]+)<\/h3>/g)].map((m) => m[1]);
- 24 const faq = jsonLdBlocks(html).find(
- 25 (b) => (b as { "@type"?: string })["@type"] === "FAQPage",
- 26 ) as {
- 27 mainEntity: { name: string; acceptedAnswer: { text: string } }[];
- 28 };
- 29 expect(faq.mainEntity.map((q) => q.name)).toEqual(visible);
- 30 for (const q of faq.mainEntity) expect(q.acceptedAnswer.text).not.toMatch(/<[a-z/]/i);
- 31 });
- 33 it("states the real price", () => {
- 34 const app = softwareApplication({
- 35 name: "Example",
- 36 description: "Plans your week.",
- 37 url: "https://app.example.com/",
- 38 price: 9,
- 39 currency: "USD",
- 40 });
- 41 expect(app.offers).toMatchObject({ price: "9.00", priceCurrency: "USD" });
- 42 });
- 44 it("numbers list items from 1 in display order", () => {
- 45 const list = itemList([
- 46 { name: "A", url: "https://app.example.com/a" },
- 47 { name: "B", url: "https://app.example.com/b" },
- 48 ]);
- 49 expect((list.itemListElement as { position: number }[]).map((i) => i.position)).toEqual([1, 2]);
- 50 });
- 52 it("cannot close the script tag", () => {
- 53 expect(safeJson({ name: "</script><script>x()</script>" })).not.toContain("</script>");
- 54 });
- 56 it("strips links to their words", () => {
- 57 expect(plainText("See [pricing](/pricing) or <b>email</b> us.")).toBe(
- 58 "See pricing or email us.",
- 59 );
- 60 });
- 61});
