From 989244ff1ec66c5e0cdde1361cbfd9258db414d3 Mon Sep 17 00:00:00 2001 From: Arata1202 Date: Sun, 2 Aug 2026 10:34:35 +0900 Subject: [PATCH] feat: anonymize sponsored article disclosure --- README.md | 5 ++--- e2e/fixtures/content.mjs | 1 - e2e/sponsored.spec.ts | 4 +--- .../Common/SponsoredDisclosure/index.tsx | 5 ++--- .../Article/__tests__/Article.test.tsx | 3 +-- src/components/Features/Article/index.tsx | 2 +- src/libs/__tests__/microcms.test.ts | 7 +++---- src/types/microcms.ts | 1 - src/utils/__tests__/sponsored.test.ts | 19 +++++-------------- src/utils/sponsored.ts | 6 +----- 10 files changed, 16 insertions(+), 37 deletions(-) diff --git a/README.md b/README.md index 16f061d5..e7e0323b 100644 --- a/README.md +++ b/README.md @@ -140,12 +140,11 @@ microCMSの`blog` APIに以下のフィールドを追加する。 | フィールドID | 種類 | 設定 | | ------------- | -------- | --------------------- | | `isSponsored` | 真偽値 | 初期値を`false`にする | -| `sponsorName` | テキスト | 広告主の正式名称 | | `sponsorUrl` | テキスト | 広告主の公式URL | -`isSponsored`を有効にした記事では、一覧と記事上部にPR表示が追加される。本文中のリンクは、`sponsorUrl`と同じドメインまたはそのサブドメインに限り`rel="sponsored"`が付与される。 +`isSponsored`を有効にした記事では、一覧と記事上部にPR表示が追加され、記事上部に「本記事は、広告主から依頼を受けて制作した広告です。」と表示される。本文中のリンクは、`sponsorUrl`と同じドメインまたはそのサブドメインに限り`rel="sponsored"`が付与される。 -スポンサー記事では`sponsorName`と有効な`sponsorUrl`が必須となり、不足している場合はビルドが失敗する。 +スポンサー記事では有効な`sponsorUrl`が必須となり、不足している場合はビルドが失敗する。 スポンサー記事は、初回公開時のOneSignalプッシュ通知から自動的に除外される。 diff --git a/e2e/fixtures/content.mjs b/e2e/fixtures/content.mjs index 0cd401bb..f585f78a 100644 --- a/e2e/fixtures/content.mjs +++ b/e2e/fixtures/content.mjs @@ -112,7 +112,6 @@ const createArticle = (index, baseUrl) => { }, ], isSponsored: index === 1, - sponsorName: index === 1 ? 'Example Sponsor' : undefined, sponsorUrl: index === 1 ? 'https://sponsor.example' : undefined, createdAt: publishedAt, publishedAt, diff --git a/e2e/sponsored.spec.ts b/e2e/sponsored.spec.ts index c2ec797a..1b61c593 100644 --- a/e2e/sponsored.spec.ts +++ b/e2e/sponsored.spec.ts @@ -9,9 +9,7 @@ test('discloses sponsored articles and marks only sponsor links', async ({ page await sponsoredCard.click(); await expect(page.getByText('PR', { exact: true })).toBeVisible(); - await expect( - page.getByText('本記事は、Example Sponsorから依頼を受けて制作した広告です。'), - ).toBeVisible(); + await expect(page.getByText('本記事は、広告主から依頼を受けて制作した広告です。')).toBeVisible(); await expect(page.getByRole('link', { name: 'スポンサーリンク' })).toHaveAttribute( 'rel', 'sponsored', diff --git a/src/components/Common/SponsoredDisclosure/index.tsx b/src/components/Common/SponsoredDisclosure/index.tsx index 6c236964..f148288a 100644 --- a/src/components/Common/SponsoredDisclosure/index.tsx +++ b/src/components/Common/SponsoredDisclosure/index.tsx @@ -11,11 +11,10 @@ import { } from '@/styles/designTokens'; type Props = { - sponsorName?: string; compact?: boolean; }; -export default function SponsoredDisclosure({ sponsorName, compact = false }: Props) { +export default function SponsoredDisclosure({ compact = false }: Props) { const { theme } = useTheme(); const badgeClassName = `${styles.badge} ${radiusClassNames.control} ${colorClassNames.accentBadge}`; @@ -34,7 +33,7 @@ export default function SponsoredDisclosure({ sponsorName, compact = false }: Pr return ( ); } diff --git a/src/components/Features/Article/__tests__/Article.test.tsx b/src/components/Features/Article/__tests__/Article.test.tsx index 5428f688..c264ea5b 100644 --- a/src/components/Features/Article/__tests__/Article.test.tsx +++ b/src/components/Features/Article/__tests__/Article.test.tsx @@ -25,7 +25,6 @@ describe('ArticleFeature', () => { it('shows a clear disclosure for sponsored articles', () => { const article = createArticle({ isSponsored: true, - sponsorName: 'Example Sponsor', sponsorUrl: 'https://sponsor.example', }); @@ -33,7 +32,7 @@ describe('ArticleFeature', () => { expect(screen.getByText('PR')).toBeInTheDocument(); expect( - screen.getByText('本記事は、Example Sponsorから依頼を受けて制作した広告です。'), + screen.getByText('本記事は、広告主から依頼を受けて制作した広告です。'), ).toBeInTheDocument(); }); diff --git a/src/components/Features/Article/index.tsx b/src/components/Features/Article/index.tsx index 7d60d98f..2c6b0199 100644 --- a/src/components/Features/Article/index.tsx +++ b/src/components/Features/Article/index.tsx @@ -85,7 +85,7 @@ export default function ArticleFeature({ data, relatedArticles }: Props) {

{data.title}

- {data.isSponsored && } + {data.isSponsored && } diff --git a/src/libs/__tests__/microcms.test.ts b/src/libs/__tests__/microcms.test.ts index aa2bf84b..784ba24a 100644 --- a/src/libs/__tests__/microcms.test.ts +++ b/src/libs/__tests__/microcms.test.ts @@ -47,7 +47,7 @@ describe('microcms client helpers', () => { await expect(getDetail('article-a', queries)).resolves.toEqual({ id: 'article-a' }); const blogQueries = { - fields: 'id,title,isSponsored,sponsorName,sponsorUrl', + fields: 'id,title,isSponsored,sponsorUrl', }; expect(microcmsSdkMock.client.getAllContents).toHaveBeenCalledWith({ @@ -71,11 +71,10 @@ describe('microcms client helpers', () => { microcmsSdkMock.client.getListDetail.mockResolvedValue({ title: 'Sponsored article', isSponsored: true, - sponsorName: '', - sponsorUrl: 'https://sponsor.example', + sponsorUrl: '', }); - await expect(getDetail('article-a')).rejects.toThrow(/sponsorName/); + await expect(getDetail('article-a')).rejects.toThrow(/sponsorUrl/); }); it('delegates category helpers to the categories endpoint', async () => { diff --git a/src/types/microcms.ts b/src/types/microcms.ts index faf2ab9d..b0ca6620 100644 --- a/src/types/microcms.ts +++ b/src/types/microcms.ts @@ -9,7 +9,6 @@ export type Blog = { introduction_blocks: IntroductionBlock[]; content_blocks: ContentBlock[]; isSponsored?: boolean; - sponsorName?: string; sponsorUrl?: string; }; diff --git a/src/utils/__tests__/sponsored.test.ts b/src/utils/__tests__/sponsored.test.ts index ac2fb92c..0fade44d 100644 --- a/src/utils/__tests__/sponsored.test.ts +++ b/src/utils/__tests__/sponsored.test.ts @@ -22,23 +22,15 @@ describe('sponsored article utilities', () => { expect(mergeSponsoredRel('sponsored')).toBe('sponsored'); }); - it('requires a sponsor name and valid URL only for sponsored articles', () => { + it('requires a valid sponsor URL only for sponsored articles', () => { expect(() => assertValidSponsoredArticle(createArticle())).not.toThrow(); - expect(() => - assertValidSponsoredArticle( - createArticle({ isSponsored: true, sponsorUrl: 'https://sponsor.example' }), - ), - ).toThrow(/sponsorName/); - expect(() => - assertValidSponsoredArticle( - createArticle({ isSponsored: true, sponsorName: 'Example Sponsor' }), - ), - ).toThrow(/sponsorUrl/); + expect(() => assertValidSponsoredArticle(createArticle({ isSponsored: true }))).toThrow( + /sponsorUrl/, + ); expect(() => assertValidSponsoredArticle( createArticle({ isSponsored: true, - sponsorName: 'Example Sponsor', sponsorUrl: 'https://sponsor.example', }), ), @@ -46,12 +38,11 @@ describe('sponsored article utilities', () => { }); it('includes sponsored fields in partial microCMS queries', () => { - expect(includeSponsoredFields('id,title')).toBe('id,title,isSponsored,sponsorName,sponsorUrl'); + expect(includeSponsoredFields('id,title')).toBe('id,title,isSponsored,sponsorUrl'); expect(includeSponsoredFields(['id', 'title'])).toEqual([ 'id', 'title', 'isSponsored', - 'sponsorName', 'sponsorUrl', ]); expect(includeSponsoredFields('')).toBe(''); diff --git a/src/utils/sponsored.ts b/src/utils/sponsored.ts index 9eda8c28..e7eb4064 100644 --- a/src/utils/sponsored.ts +++ b/src/utils/sponsored.ts @@ -2,7 +2,7 @@ import type { CheerioAPI } from 'cheerio'; import type { Blog } from '@/types/microcms'; const SPONSORED_REL_VALUE = 'sponsored'; -const SPONSORED_FIELDS = ['isSponsored', 'sponsorName', 'sponsorUrl'] as const; +const SPONSORED_FIELDS = ['isSponsored', 'sponsorUrl'] as const; const normalizeHostname = (hostname: string) => hostname.toLowerCase().replace(/^www\./, ''); @@ -57,10 +57,6 @@ export const assertValidSponsoredArticle = (article: Blog) => { return; } - if (!article.sponsorName?.trim()) { - throw new Error(`Sponsored article "${article.title}" requires sponsorName`); - } - if (!article.sponsorUrl?.trim() || !parseHttpUrl(article.sponsorUrl)) { throw new Error(`Sponsored article "${article.title}" requires a valid sponsorUrl`); }