diff --git a/convex/lib/publicRouteReservations.test.ts b/convex/lib/publicRouteReservations.test.ts index 9240a90b..3c0fd7a9 100644 --- a/convex/lib/publicRouteReservations.test.ts +++ b/convex/lib/publicRouteReservations.test.ts @@ -5,7 +5,7 @@ import { } from "./publicRouteReservations"; describe("public route reservations", () => { - it.each(["admin", "clawhub", "creators", "docs", "plugins", "publishers", "skills"])( + it.each(["admin", "clawhub", "docs", "official", "plugins", "publishers", "skills"])( "reserves @%s as a public owner handle", (handle) => { expect(isReservedPublicOwnerHandle(handle)).toBe(true); @@ -17,6 +17,10 @@ describe("public route reservations", () => { expect(isReservedPublicOwnerHandle(handle)).toBe(false); }); + it("releases the removed creators route handle", () => { + expect(isReservedPublicOwnerHandle("creators")).toBe(false); + }); + it("does not normalize at-sign prefixes", () => { expect(isReservedPublicOwnerHandle("@clawhub")).toBe(false); }); diff --git a/convex/lib/publicRouteReservations.ts b/convex/lib/publicRouteReservations.ts index 9f28d612..586fdb83 100644 --- a/convex/lib/publicRouteReservations.ts +++ b/convex/lib/publicRouteReservations.ts @@ -3,8 +3,8 @@ import { OPENCLAW_EXTENSION_SLUG_TO_PACKAGE } from "clawhub-schema"; const RESERVED_PUBLIC_OWNER_HANDLES = new Set([ "admin", "clawhub", - "creators", "docs", + "official", "plugins", "publishers", "skills", diff --git a/convex/lib/skillSlugValidator.test.ts b/convex/lib/skillSlugValidator.test.ts index a685da65..9868eeb8 100644 --- a/convex/lib/skillSlugValidator.test.ts +++ b/convex/lib/skillSlugValidator.test.ts @@ -56,6 +56,10 @@ describe("assertValidSkillSlug", () => { expect(assertValidSkillSlug("A-B-C")).toBe("a-b-c"); }); + it("allows the removed creators route slug", () => { + expect(assertValidSkillSlug("creators")).toBe("creators"); + }); + it.each([ ["", "required"], [" ", "required"], @@ -82,7 +86,7 @@ describe("assertValidSkillSlug", () => { "clawhub", "souls", "packages", - "creators", + "official", "publishers", ])("rejects reserved slug %s", (slug) => { // Some short reserved entries (e.g. "u") are also blocked by the @@ -90,7 +94,7 @@ describe("assertValidSkillSlug", () => { expect(() => assertValidSkillSlug(slug)).toThrow(); }); - it.each(["openclaw", "creators", "publishers"])( + it.each(["openclaw", "official", "publishers"])( "emits the reserved-specific error for long reserved slug %s", (slug) => { expect(() => assertValidSkillSlug(slug)).toThrow(/reserved/i); diff --git a/convex/lib/skillSlugValidator.ts b/convex/lib/skillSlugValidator.ts index c7e2d1bb..89879aa0 100644 --- a/convex/lib/skillSlugValidator.ts +++ b/convex/lib/skillSlugValidator.ts @@ -34,7 +34,7 @@ const RESERVED_SKILL_SLUGS: ReadonlySet = new Set([ "orgs", "packages", "plugins", - "creators", + "official", "publishers", "publish", "publish-plugin", @@ -86,7 +86,6 @@ const RESERVED_SKILL_SLUGS: ReadonlySet = new Set([ "system", "root", "owner", - "official", "staff", "team", "mod", diff --git a/e2e/public-routes-smoke.pw.test.ts b/e2e/public-routes-smoke.pw.test.ts index 0df5174c..8c252aa2 100644 --- a/e2e/public-routes-smoke.pw.test.ts +++ b/e2e/public-routes-smoke.pw.test.ts @@ -126,18 +126,18 @@ function publicRouteCases(): PublicRouteCase[] { }, }, { - label: "creators browse", - path: () => "/creators", + label: "official browse", + path: () => "/official", assert: async (page) => { - await expect(page.getByRole("heading", { name: /^Creators/ })).toBeVisible(); + await expect(page.getByRole("heading", { name: /^Official/ })).toBeVisible(); }, }, { label: "publishers browse redirect", path: () => "/publishers", assert: async (page) => { - await expect(page).toHaveURL(/\/creators/); - await expect(page.getByRole("heading", { name: /^Creators/ })).toBeVisible(); + await expect(page).toHaveURL(/\/official/); + await expect(page.getByRole("heading", { name: /^Official/ })).toBeVisible(); }, }, { @@ -241,3 +241,10 @@ for (const route of publicRouteCases()) { await expectPublicRouteHealthy(page, route, fixtures); }); } + +test("removed creators route renders not found", async ({ page }) => { + await stubExternalMediaInVitePreview(page); + await page.goto("/creators", { waitUntil: "domcontentloaded" }); + await waitForHydration(page); + await expect(page.getByRole("heading", { name: "We couldn't find that page." })).toBeVisible(); +}); diff --git a/src/__tests__/creators-route.test.tsx b/src/__tests__/creators-route.test.tsx deleted file mode 100644 index 428a4830..00000000 --- a/src/__tests__/creators-route.test.tsx +++ /dev/null @@ -1,282 +0,0 @@ -/* @vitest-environment jsdom */ - -import { fireEvent, render, screen } from "@testing-library/react"; -import type { ComponentType, ReactNode } from "react"; -import { beforeEach, describe, expect, it, vi } from "vitest"; - -const { loaderDataMock, navigateMock, queryMock, searchMock } = vi.hoisted(() => ({ - loaderDataMock: vi.fn(), - navigateMock: vi.fn(), - queryMock: vi.fn(), - searchMock: vi.fn(), -})); - -vi.mock("../convex/client", () => ({ - convexHttp: { query: (...args: unknown[]) => queryMock(...args) }, -})); - -vi.mock("@tanstack/react-router", () => ({ - createFileRoute: - () => - (config: { - component?: unknown; - head?: unknown; - loader?: unknown; - loaderDeps?: unknown; - validateSearch?: unknown; - }) => ({ - __config: config, - useLoaderData: () => loaderDataMock(), - useNavigate: () => navigateMock, - useSearch: () => searchMock(), - }), - Link: ({ - children, - className, - resetScroll: _resetScroll, - to, - ...props - }: { - children: ReactNode; - className?: string; - resetScroll?: boolean; - to?: string; - "aria-label"?: string; - }) => ( - - {children} - - ), -})); - -vi.mock("../components/PublisherListItem", () => ({ - PublisherListItem: ({ publisher }: { publisher: { _id: string } }) =>
{publisher._id}
, -})); - -vi.mock("../lib/site", () => ({ - SITE_NAME: "ClawHub", - getClawHubSiteUrl: () => "https://clawhub.ai", -})); - -async function loadRoute() { - return (await import("../routes/creators/index")).Route as unknown as { - __config: { - component?: ComponentType; - head?: () => { - links?: Array<{ rel: string; href: string }>; - meta?: Array>; - }; - loader?: (args: { - deps: { kind?: "orgs" | "people"; official?: boolean; q?: string }; - }) => Promise; - validateSearch?: (search: Record) => Record; - }; - }; -} - -describe("creators route", () => { - beforeEach(() => { - vi.resetModules(); - loaderDataMock.mockReset(); - loaderDataMock.mockReturnValue({ - page: [], - counts: { all: 0, organizations: 0, individuals: 0 }, - continueCursor: "", - isDone: true, - }); - navigateMock.mockReset(); - queryMock.mockReset(); - queryMock.mockResolvedValue({ - page: [], - counts: { all: 0, organizations: 0, individuals: 0 }, - continueCursor: "", - isDone: true, - }); - searchMock.mockReset(); - searchMock.mockReturnValue({}); - }); - - it("renders the public creators listing surface", async () => { - const route = await loadRoute(); - const result = await route.__config.loader?.({ deps: {} }); - - expect(result).toEqual({ - page: [], - counts: { all: 0, organizations: 0, individuals: 0 }, - continueCursor: "", - isDone: true, - }); - expect(queryMock.mock.calls[0]?.[1]).toEqual({ - paginationOpts: { cursor: null, numItems: 25 }, - kind: undefined, - query: undefined, - }); - }); - - it("passes the organization filter to the public publishers query", async () => { - const route = await loadRoute(); - await route.__config.loader?.({ deps: { kind: "orgs" } }); - - expect(queryMock.mock.calls[0]?.[1]).toEqual({ - paginationOpts: { cursor: null, numItems: 25 }, - kind: "org", - query: undefined, - }); - }); - - it("normalizes legacy builder URLs to people", async () => { - const route = await loadRoute(); - - expect(route.__config.validateSearch?.({ kind: "builders" })).toEqual({ - kind: "people", - official: undefined, - q: undefined, - view: undefined, - }); - expect(route.__config.validateSearch?.({ kind: "individuals" })).toEqual({ - kind: "people", - official: undefined, - q: undefined, - view: undefined, - }); - }); - - it("passes the people filter and query to the public publishers query", async () => { - const route = await loadRoute(); - await route.__config.loader?.({ deps: { kind: "people", q: "openclaw" } }); - - expect(queryMock.mock.calls[0]?.[1]).toEqual({ - paginationOpts: { cursor: null, numItems: 25 }, - kind: "user", - query: "openclaw", - }); - }); - - it("passes the official filter to the public publishers query without a legacy scan", async () => { - const route = await loadRoute(); - - await route.__config.loader?.({ deps: { official: true } }); - - expect(queryMock.mock.calls[0]?.[1]).toEqual({ - paginationOpts: { cursor: null, numItems: 25 }, - kind: undefined, - query: undefined, - official: true, - }); - expect(queryMock).toHaveBeenCalledTimes(1); - }); - - it("renders the loaded publisher results", async () => { - const route = await loadRoute(); - const Component = route.__config.component as ComponentType; - - render(); - - expect(screen.getByText("Creators")).toBeTruthy(); - expect(screen.getByText("No publishers found")).toBeTruthy(); - }); - - it("labels highlighted publishers as popular creators", async () => { - loaderDataMock.mockReturnValue({ - page: [{ _id: "publishers:one" }], - counts: { all: 1, organizations: 0, individuals: 1 }, - continueCursor: "", - isDone: true, - }); - const route = await loadRoute(); - const Component = route.__config.component as ComponentType; - - render(); - - expect(screen.getByRole("heading", { name: "Popular creators" })).toBeTruthy(); - expect(screen.queryByText("Popular publishers")).toBeNull(); - }); - - it("does not present the bounded publisher result count as a global total", async () => { - loaderDataMock.mockReturnValue({ - page: [], - counts: { all: 17, organizations: 6, individuals: 11 }, - globalCounts: { all: 17, organizations: 6, individuals: 11 }, - continueCursor: "", - isDone: true, - }); - const route = await loadRoute(); - const Component = route.__config.component as ComponentType; - - render(); - - expect(screen.getByRole("heading", { name: "Creators" })).toBeTruthy(); - expect(screen.queryByText("17")).toBeNull(); - expect(screen.getByRole("radio", { name: "All" })).toBeTruthy(); - expect(screen.getByRole("radio", { name: "Official" })).toBeTruthy(); - expect(screen.getByRole("radio", { name: "Organizations" })).toBeTruthy(); - expect(screen.getByRole("radio", { name: "Users" })).toBeTruthy(); - expect(screen.queryByText("Builders")).toBeNull(); - expect(screen.queryByText(/Showing/i)).toBeNull(); - }); - - it("keeps the publisher heading unchanged when filters are active", async () => { - searchMock.mockReturnValue({ kind: "orgs" }); - loaderDataMock.mockReturnValue({ - page: [], - counts: { all: 6, organizations: 6, individuals: 0 }, - globalCounts: { all: 17, organizations: 6, individuals: 11 }, - continueCursor: "", - isDone: true, - }); - const route = await loadRoute(); - const Component = route.__config.component as ComponentType; - - render(); - - expect(screen.getByRole("heading", { name: "Creators" })).toBeTruthy(); - expect(screen.queryByText("17")).toBeNull(); - }); - - it("keeps publisher type filters and view controls in the horizontal controls", async () => { - const route = await loadRoute(); - const Component = route.__config.component as ComponentType; - - render(); - - const allTab = screen.getByRole("radio", { name: "All" }); - const listView = screen.getByRole("button", { name: "List" }); - const searchInput = screen.getByPlaceholderText("Search publishers..."); - - expect(allTab.closest(".browse-controls")).not.toBeNull(); - expect(listView.closest(".browse-controls")).not.toBeNull(); - expect( - Boolean(listView.compareDocumentPosition(searchInput) & Node.DOCUMENT_POSITION_FOLLOWING), - ).toBe(true); - }); - - it("clears publisher search from the search field", async () => { - searchMock.mockReturnValue({ q: "ope", kind: "orgs" }); - const route = await loadRoute(); - const Component = route.__config.component as ComponentType; - - render(); - - fireEvent.click(screen.getByRole("button", { name: "Close search" })); - - expect(navigateMock).toHaveBeenCalled(); - const lastCall = navigateMock.mock.calls.at(-1)?.[0] as { - search: (prev: Record) => Record; - replace?: boolean; - }; - expect(lastCall.search({ q: "ope", kind: "orgs" })).toEqual({ - q: undefined, - kind: "orgs", - }); - expect(lastCall.replace).toBe(true); - expect(screen.queryByRole("button", { name: "Clear" })).toBeNull(); - }); - - it("sets creators-specific sharing metadata", async () => { - const route = await loadRoute(); - const head = route.__config.head?.(); - - expect(head?.links).toContainEqual({ rel: "canonical", href: "https://clawhub.ai/creators" }); - expect(head?.meta).toContainEqual({ property: "og:title", content: "Creators · ClawHub" }); - }); -}); diff --git a/src/__tests__/header.test.tsx b/src/__tests__/header.test.tsx index 24920238..65e8c908 100644 --- a/src/__tests__/header.test.tsx +++ b/src/__tests__/header.test.tsx @@ -298,7 +298,7 @@ describe("Header", () => { ).toBeTruthy(); expect(screen.getAllByText("Skills")).toHaveLength(1); expect(screen.getAllByText("Plugins")).toHaveLength(1); - expect(screen.getAllByText("Creators")).toHaveLength(1); + expect(screen.getAllByText("Official")).toHaveLength(1); expect(screen.getAllByText("Docs")).toHaveLength(2); expect(screen.queryByText("About")).toBeNull(); expect(screen.queryByText("Dashboard")).toBeNull(); @@ -310,7 +310,7 @@ describe("Header", () => { expect(screen.getAllByText("Home")).toHaveLength(1); expect(screen.getAllByText("Skills")).toHaveLength(2); expect(screen.getAllByText("Plugins")).toHaveLength(2); - expect(screen.getAllByText("Creators")).toHaveLength(2); + expect(screen.getAllByText("Official")).toHaveLength(2); expect(screen.getAllByText("Docs")).toHaveLength(3); expect(screen.queryByText("About")).toBeNull(); }); @@ -657,7 +657,7 @@ describe("Header", () => { .map((element) => element.textContent?.trim()) .filter((label): label is string => Boolean(label)); - expect(labels.slice(0, 5)).toEqual(["Home", "Skills", "Plugins", "Creators", "Docs"]); + expect(labels.slice(0, 5)).toEqual(["Home", "Skills", "Plugins", "Official", "Docs"]); expect( document.querySelector(".mobile-nav-appearance-section .navbar-theme-switcher"), ).toBeTruthy(); @@ -684,7 +684,7 @@ describe("Header", () => { .map((element) => element.textContent?.trim()) .filter((label): label is string => Boolean(label)); - expect(labels).toEqual(["Home", "Skills", "Plugins", "Creators", "Docs"]); + expect(labels).toEqual(["Home", "Skills", "Plugins", "Official", "Docs"]); }); it("links profile and starred skills from the signed-in avatar menu", () => { diff --git a/src/__tests__/home-popular-publishers-section.test.tsx b/src/__tests__/home-popular-publishers-section.test.tsx index 12d253aa..80bfb2ac 100644 --- a/src/__tests__/home-popular-publishers-section.test.tsx +++ b/src/__tests__/home-popular-publishers-section.test.tsx @@ -38,7 +38,7 @@ vi.mock("@tanstack/react-router", () => ({ className={className} data-search={search ? JSON.stringify(search) : undefined} href={ - params?.slug ? `/${params.slug}` : params?.handle ? `/user/${params.handle}` : "/creators" + params?.slug ? `/${params.slug}` : params?.handle ? `/user/${params.handle}` : "/official" } > {children} @@ -105,9 +105,7 @@ describe("HomePopularPublishersSection", () => { expect(convexQueryMock).not.toHaveBeenCalled(); expect(screen.getByRole("heading", { name: "Official creators" })).toBeTruthy(); expect(screen.getByText("Explore skills and plugins from official creators.")).toBeTruthy(); - expect(screen.getByRole("link", { name: "Browse creators" }).dataset.search).toBe( - '{"official":true,"kind":"orgs"}', - ); + expect(screen.getByRole("link", { name: "Browse official" }).dataset.search).toBeUndefined(); await enterPublisherSection(); await waitFor(() => expect(convexQueryMock).toHaveBeenCalledTimes(1)); diff --git a/src/__tests__/official-route.test.tsx b/src/__tests__/official-route.test.tsx new file mode 100644 index 00000000..24edd749 --- /dev/null +++ b/src/__tests__/official-route.test.tsx @@ -0,0 +1,189 @@ +/* @vitest-environment jsdom */ + +import { fireEvent, render, screen } from "@testing-library/react"; +import type { ComponentType } from "react"; +import { beforeEach, describe, expect, it, vi } from "vitest"; + +const { loaderDataMock, navigateMock, publisherListItemMock, queryMock, searchMock } = vi.hoisted( + () => ({ + loaderDataMock: vi.fn(), + navigateMock: vi.fn(), + publisherListItemMock: vi.fn(), + queryMock: vi.fn(), + searchMock: vi.fn(), + }), +); + +vi.mock("../convex/client", () => ({ + convexHttp: { query: (...args: unknown[]) => queryMock(...args) }, +})); + +vi.mock("@tanstack/react-router", () => ({ + createFileRoute: + () => + (config: { + component?: unknown; + head?: unknown; + loader?: unknown; + loaderDeps?: unknown; + validateSearch?: unknown; + }) => ({ + __config: config, + useLoaderData: () => loaderDataMock(), + useNavigate: () => navigateMock, + useSearch: () => searchMock(), + }), +})); + +vi.mock("../components/PublisherListItem", () => ({ + PublisherListItem: (props: { + publisher: { _id: string }; + showOfficialBadge?: boolean; + variant?: string; + }) => { + publisherListItemMock(props); + return
{props.publisher._id}
; + }, +})); + +vi.mock("../lib/site", () => ({ + SITE_NAME: "ClawHub", + getClawHubSiteUrl: () => "https://clawhub.ai", +})); + +async function loadRoute() { + return (await import("../routes/official/index")).Route as unknown as { + __config: { + component?: ComponentType; + head?: () => { + links?: Array<{ rel: string; href: string }>; + meta?: Array>; + }; + loader?: (args: { deps: { q?: string } }) => Promise; + validateSearch?: (search: Record) => Record; + }; + }; +} + +describe("official route", () => { + beforeEach(() => { + vi.resetModules(); + loaderDataMock.mockReset(); + loaderDataMock.mockReturnValue({ + page: [], + counts: { all: 0, organizations: 0, individuals: 0 }, + continueCursor: "", + isDone: true, + }); + navigateMock.mockReset(); + publisherListItemMock.mockReset(); + queryMock.mockReset(); + queryMock.mockResolvedValue({ + page: [], + counts: { all: 0, organizations: 0, individuals: 0 }, + continueCursor: "", + isDone: true, + }); + searchMock.mockReset(); + searchMock.mockReturnValue({}); + }); + + it("loads only official organizations", async () => { + const route = await loadRoute(); + const result = await route.__config.loader?.({ deps: {} }); + + expect(result).toEqual({ + page: [], + counts: { all: 0, organizations: 0, individuals: 0 }, + continueCursor: "", + isDone: true, + }); + expect(queryMock.mock.calls[0]?.[1]).toEqual({ + paginationOpts: { cursor: null, numItems: 25 }, + kind: "org", + official: true, + query: undefined, + }); + }); + + it("passes search queries without exposing kind or official controls", async () => { + const route = await loadRoute(); + await route.__config.loader?.({ deps: { q: "openclaw" } }); + + expect(queryMock.mock.calls[0]?.[1]).toEqual({ + paginationOpts: { cursor: null, numItems: 25 }, + kind: "org", + official: true, + query: "openclaw", + }); + }); + + it("renders the official header and list-only empty state", async () => { + const route = await loadRoute(); + const Component = route.__config.component as ComponentType; + + render(); + + expect(screen.getByRole("heading", { name: "Official" })).toBeTruthy(); + expect( + screen.getByText("The organizations behind the top skills and plugins on ClawHub"), + ).toBeTruthy(); + expect(screen.getByText("No official organizations found")).toBeTruthy(); + expect(screen.queryByRole("radio")).toBeNull(); + expect(screen.queryByRole("button", { name: "Grid" })).toBeNull(); + expect(screen.queryByText("Popular publishers")).toBeNull(); + }); + + it("renders official organizations in the existing table without redundant badges", async () => { + loaderDataMock.mockReturnValue({ + page: [{ _id: "publishers:openclaw" }], + counts: { all: 1, organizations: 1, individuals: 0 }, + continueCursor: "", + isDone: true, + }); + const route = await loadRoute(); + const Component = route.__config.component as ComponentType; + + render(); + + expect(screen.getByText("Organization")).toBeTruthy(); + expect(screen.getByText("Activity")).toBeTruthy(); + expect(screen.getByText("publishers:openclaw")).toBeTruthy(); + expect(publisherListItemMock).toHaveBeenCalledWith( + expect.objectContaining({ + showOfficialBadge: false, + variant: "list", + }), + ); + }); + + it("clears official organization search from the search field", async () => { + searchMock.mockReturnValue({ q: "ope" }); + const route = await loadRoute(); + const Component = route.__config.component as ComponentType; + + render(); + + fireEvent.click(screen.getByRole("button", { name: "Close search" })); + + expect(navigateMock).toHaveBeenCalled(); + const lastCall = navigateMock.mock.calls.at(-1)?.[0] as { + search: (prev: Record) => Record; + replace?: boolean; + }; + expect(lastCall.search({ q: "ope" })).toEqual({ q: undefined }); + expect(lastCall.replace).toBe(true); + }); + + it("sets official-specific sharing metadata", async () => { + const route = await loadRoute(); + const head = route.__config.head?.(); + + expect(head?.links).toContainEqual({ rel: "canonical", href: "https://clawhub.ai/official" }); + expect(head?.meta).toContainEqual({ property: "og:title", content: "Official · ClawHub" }); + expect(head?.meta).toContainEqual({ + property: "og:description", + content: "The organizations behind the top skills and plugins on ClawHub.", + }); + }); +}); diff --git a/src/__tests__/search-route.test.tsx b/src/__tests__/search-route.test.tsx index 465de364..ca351ce4 100644 --- a/src/__tests__/search-route.test.tsx +++ b/src/__tests__/search-route.test.tsx @@ -421,7 +421,7 @@ describe("search route", () => { expect(screen.queryByRole("button", { name: "Search all types" })).toBeNull(); }); - it("links to creators browse when creator search has no matches", async () => { + it("links to official organizations when creator search has no matches", async () => { searchMock = { q: "zzzz", type: "creators" }; const route = await loadRoute(); const Component = route.__config.component as ComponentType; @@ -429,9 +429,9 @@ describe("search route", () => { render(); expect(screen.getByText('No matches for "zzzz"')).toBeTruthy(); - expect(screen.getByRole("link", { name: "Show all creators" }).getAttribute("href")).toBe( - "/creators", - ); + expect( + screen.getByRole("link", { name: "Browse official organizations" }).getAttribute("href"), + ).toBe("/official"); expect(screen.queryByRole("link", { name: "Show all skills" })).toBeNull(); expect(screen.queryByRole("link", { name: "Show all plugins" })).toBeNull(); }); diff --git a/src/__tests__/ui-design-contract.test.ts b/src/__tests__/ui-design-contract.test.ts index 39fc1520..b2f345ec 100644 --- a/src/__tests__/ui-design-contract.test.ts +++ b/src/__tests__/ui-design-contract.test.ts @@ -273,7 +273,7 @@ describe("restored UI design contract", () => { expect(headerSource).not.toContain('className="navbar-tabs-secondary"'); expect(navSource).toContain("export const SECONDARY_NAV_ITEMS"); - expect(navSource).toContain('label: "Creators"'); + expect(navSource).toContain('label: "Official"'); expect(navSource).toContain('label: "Docs"'); expect(navSource).toContain("href: CLAWHUB_DOCS_URL"); expect(publicRegistrySource).toContain( diff --git a/src/__tests__/users-route-redirect.test.ts b/src/__tests__/users-route-redirect.test.ts index 7bb295fa..a08e6827 100644 --- a/src/__tests__/users-route-redirect.test.ts +++ b/src/__tests__/users-route-redirect.test.ts @@ -17,7 +17,7 @@ vi.mock("@tanstack/react-router", () => ({ })); describe("users route redirect", () => { - it("redirects legacy /users traffic to /creators", async () => { + it("redirects legacy /users traffic to /official", async () => { const route = (await import("../routes/users/index")).Route as unknown as { __config: { beforeLoad: (args: { search: Record }) => unknown }; }; @@ -25,10 +25,10 @@ describe("users route redirect", () => { const search = { q: "builder" }; expect(() => route.__config.beforeLoad({ search })).toThrow(); - expect(redirectMock).toHaveBeenCalledWith({ to: "/creators", search, replace: true }); + expect(redirectMock).toHaveBeenCalledWith({ to: "/official", search, replace: true }); }); - it("redirects legacy /publishers traffic to /creators", async () => { + it("redirects legacy /publishers traffic to /official", async () => { const route = (await import("../routes/publishers/index")).Route as unknown as { __config: { beforeLoad: (args: { search: Record }) => unknown }; }; @@ -36,7 +36,7 @@ describe("users route redirect", () => { const search = { kind: "orgs", q: "acme" }; expect(() => route.__config.beforeLoad({ search })).toThrow(); - expect(redirectMock).toHaveBeenCalledWith({ to: "/creators", search, replace: true }); + expect(redirectMock).toHaveBeenCalledWith({ to: "/official", search, replace: true }); }); it("redirects legacy /p profile routes to user profiles", async () => { diff --git a/src/components/HomePopularPublishersSection.tsx b/src/components/HomePopularPublishersSection.tsx index f0f3642e..878fa49c 100644 --- a/src/components/HomePopularPublishersSection.tsx +++ b/src/components/HomePopularPublishersSection.tsx @@ -146,12 +146,8 @@ export function HomePopularPublishersSection() {

Explore skills and plugins from official creators.

- - Browse creators