Files
logos-web/apps/web/lib/__tests__/rfps-github-links.test.ts
T
Jinho Jang 96be50d8d0 refactor: make @repo/content the single source for home, get-started & movement page copy (#62)
* docs: add page-copy single-source spec and implementation plan

* feat(content): add typed home section schemas and migrate home copy into content

Add 7 bespoke home* section schemas (+ techStack ctas) to the page section
union and populate content/pages/en/home.json with home copy moved verbatim
from messages.

* refactor(web): source home page copy from @repo/content; drop home namespace from messages

Feed every home section from content via findSection/data props (replacing
next-intl getTranslations), remove the home namespace from messages/en.json,
and add a regression guard plus content-based tests. Render output unchanged.

* docs: add get-started/movement page-copy dedup spec and plan

* feat(content): add get-started & movement section schemas and content docs

Move get-started/movement page copy verbatim into content/pages so it is
single-sourced in @repo/content.

* refactor(web): source get-started/movement copy from content; drop those namespaces from messages

Feed both pages from content via findSection/data props (replacing next-intl),
switch them to content-based metadata, delete pages.getStarted/movement from
messages, and add content-based tests + ownership guard. Render unchanged.

* docs: add navbar-pages CMS copy spec and plan

* feat(content): add navbar page section schemas + Pages docs (book, brand-kit, research, node-programme, lambda-prize, manifesto, media, podcast, broadcast, tech-stack explorer)

* refactor(web): source navbar pages copy from content; drop those namespaces from messages

Migrate book, brand-kit, research (incl. rich-text via a tagged-text renderer),
node-programme, lambda-prize, manifesto, media, podcast, logos-broadcast-network,
and the tech-stack explorer to read copy from content via findSection/data props
and content-based metadata; remove the migrated pages.* namespaces from messages;
update guards + contracts. Render output unchanged.

* feat(web): source activist/coalition pages copy from content (CMS title/description)

Migrate activistBuilder, activistLeaderSteward, and coalitionPartner page
metadata from next-intl messages.pages.* into @repo/content PageCopy JSON,
following the established bookCopy pattern.

- Add activistBuilderCopySectionSchema, activistLeaderStewardCopySectionSchema,
  coalitionPartnerCopySectionSchema to packages/content/src/schemas/pages.ts
  and wire them into the pageSectionSchema discriminated union
- Create content/pages/en/activist-builder.json,
  activist-leader-steward.json, coalition-partner.json with verbatim
  title/description/heading values from the old messages namespace
- Switch all three route pages from createTranslatedPageMetadata to
  createPageMetadata(ROUTE), removing the NAMESPACE constant
- Remove activistBuilder, activistLeaderSteward, coalitionPartner from
  apps/web/messages/en.json pages object
- Add schema parse tests, renderToStaticMarkup page tests, messages ownership
  guards, and content-route contract entries for all three pages

* chore(web): remove dead faq (pages.faq + unused FaqSection components)

* fix: remove stale faq links

* fix: pass payload env through turbo build

* fix: preserve lambda prize support labels

* fix: preserve live copy after master rebase

* fix: address page copy review feedback

* refactor: move design guide copy to content

* feat: add schemas for home and page copy sections

- Introduced home section schemas including homeSocialProof, homeChoosePath, homeDecide, homeStartBuilding, homeAbout, homeUseCases, and homeBuilderPortal.
- Added page copy section schemas such as getStartedCopy, movementCopy, bookCopy, designGuideCopy, activistBuilderCopy, activistLeaderStewardCopy, coalitionPartnerCopy, researchCopy, nodeProgrammeCopy, lambdaPrizeCopy, mediaCopy, podcastCopy, broadcastCopy, and manifestoCopy.
- Created shared schema for section keys to maintain consistency across sections.

* chore(web): remove dead pages.faq from messages

* fix: resolve page copy migration conflicts

* fix: source connect form page copy from content

* fix: remove duplicated afform page copy exports

* fix: stop generating afform page copy exports

* fix(cms): require only server actions key

* fix(cms): load env files before build validation

* fix(cms): make env loader available during build

* fix(cms): load next env via require

* test(cms): isolate missing env validation

* feat: add homepage testnet highlight

* fix: restore lambda prize support links
2026-07-06 19:09:02 +09:00

106 lines
3.9 KiB
TypeScript

import { afterEach, describe, expect, it, vi } from 'vitest'
import {
fetchRfpMarkdownEntryForTest,
rewriteRfpMarkdownLinks,
} from '@/lib/rfps-github'
const FILE_URL =
'https://github.com/logos-co/rfp/blob/master/RFPs/RFP-016-lbp-launchpad.md'
describe('rewriteRfpMarkdownLinks', () => {
it('rewrites sibling RFP cross-references to internal detail routes', () => {
const md = 'See [LBP](./RFP-008-lending-borrowing-protocol.md) for context.'
expect(rewriteRfpMarkdownLinks(md, FILE_URL)).toBe(
'See [LBP](/builders-hub/rfps/lending-borrowing-protocol) for context.'
)
})
it('rewrites RFP references without a leading ./', () => {
const md = '[admin](RFP-001-admin-authority-lib.md)'
expect(rewriteRfpMarkdownLinks(md, FILE_URL)).toBe(
'[admin](/builders-hub/rfps/admin-authority-lib)'
)
})
it('resolves other repo-relative .md links to absolute GitHub URLs, keeping anchors', () => {
const md =
'[fees](../appendix/token-launchpad-ecosystem.md#fee-structures)'
expect(rewriteRfpMarkdownLinks(md, FILE_URL)).toBe(
'[fees](https://github.com/logos-co/rfp/blob/master/appendix/token-launchpad-ecosystem.md#fee-structures)'
)
})
it('leaves external, absolute, and anchor links unchanged', () => {
const md = [
'[ext](https://github.com/logos-co/lambda-prize/blob/master/prizes/LP-0004.md)',
'[abs](/builders-hub/rfps)',
'[frag](#overview)',
].join('\n')
expect(rewriteRfpMarkdownLinks(md, FILE_URL)).toBe(md)
})
it('leaves relative non-markdown links untouched', () => {
const md = '![diagram](./images/flow.png)'
expect(rewriteRfpMarkdownLinks(md, FILE_URL)).toBe(md)
})
})
describe('fetchRfpMarkdownEntryForTest', () => {
afterEach(() => {
vi.restoreAllMocks()
})
it('falls back from download_url to raw GitHub URL when the first fetch fails', async () => {
const fetchMock = vi
.spyOn(globalThis, 'fetch')
.mockResolvedValueOnce(new Response('not found', { status: 404 }))
.mockResolvedValueOnce(new Response('not found', { status: 404 }))
.mockResolvedValueOnce(new Response('not found', { status: 404 }))
.mockResolvedValueOnce(new Response('# RFP raw markdown', { status: 200 }))
const result = await fetchRfpMarkdownEntryForTest({
name: 'RFP-001-example.md',
download_url: 'https://objects.githubusercontent.com/RFP-001-example.md',
html_url:
'https://github.com/logos-co/rfp/blob/master/RFPs/RFP-001-example.md',
git_url: 'https://api.github.com/repos/logos-co/rfp/git/blobs/example',
})
expect(result).toBe('# RFP raw markdown')
expect(fetchMock).toHaveBeenCalledTimes(4)
expect(fetchMock.mock.calls[3]?.[0]).toBe(
'https://raw.githubusercontent.com/logos-co/rfp/master/RFPs/RFP-001-example.md'
)
})
it('decodes GitHub blob content when raw URL fetches fail', async () => {
const encoded = Buffer.from('# RFP blob markdown', 'utf8').toString('base64')
vi.spyOn(globalThis, 'fetch')
.mockResolvedValueOnce(new Response('not found', { status: 404 }))
.mockResolvedValueOnce(new Response('not found', { status: 404 }))
.mockResolvedValueOnce(new Response('not found', { status: 404 }))
.mockResolvedValueOnce(new Response('not found', { status: 404 }))
.mockResolvedValueOnce(new Response('not found', { status: 404 }))
.mockResolvedValueOnce(new Response('not found', { status: 404 }))
.mockResolvedValueOnce(
Response.json({ content: encoded, encoding: 'base64' })
)
const result = await fetchRfpMarkdownEntryForTest({
name: 'RFP-002-example.md',
download_url: 'https://objects.githubusercontent.com/RFP-002-example.md',
html_url:
'https://github.com/logos-co/rfp/blob/master/RFPs/RFP-002-example.md',
git_url: 'https://api.github.com/repos/logos-co/rfp/git/blobs/example',
})
expect(result).toBe('# RFP blob markdown')
})
})