diff --git a/CHANGELOG.md b/CHANGELOG.md index 343f870..0805cdc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,12 @@ All notable public product changes are recorded here. Release notes contain only product scope and reproducible validation; platform identifiers and private operational details are excluded. +## v1.48.0 — 2019 春季《爆丸 決戰星球》 + +- 收錄日本播出版、1 首 OP 與 2 首 ED,保留共同作曲、共同編曲及前後期片尾;後期 ED 註明 2020 年更新。 +- 以台灣播出方與繁中季表核對作品名稱,中文主題影片只作名稱來源;未確認的歌曲發行日期、影片、外部 ID 及 Romaji 保留空缺。 +- 春季共 32 套作品、94 筆 OP/ED;本季與夏季仍在補充,2025 秋季尚未收錄。 + ## v1.47.0 — 作品頁歌曲導覽 - 超過 4 筆歌曲的作品頁新增可收合的頁內導覽,按 OP/ED 分組,完整顯示曲名與演唱者並直接跳到對應版本。 diff --git a/README.md b/README.md index ee0b6f4..b4f6a8b 100644 --- a/README.md +++ b/README.md @@ -2,9 +2,9 @@ Anisonary 是以 Astro + strict TypeScript 建立的動畫歌曲目錄,按季度與日本編輯播出日瀏覽作品、OP 與 ED。網站輸出為純靜態資產,正式交付使用 **Cloudflare Workers Static Assets**;它不是 Cloudflare Pages,也沒有 application Worker backend。下方同時保留 English technical notes。 -This repository contains the completed static product: season directory, anime detail pages, traceable OP/ED credits and links, per-song source ledgers, source-attributed media, local-only cross-season search, privacy-bounded offline reading, a GitHub correction flow, and deployment through Cloudflare Workers Static Assets. The default catalogue covers twenty-nine reviewed snapshots across 2019–2026, with 1,948 unique titles and 4,320 known OP/ED records. The 2019 spring snapshot contains thirty-one TV titles, including television shorts and a TV recut; summer contains 38 TV titles and four web series. Both quarters are still being expanded, and 2025 fall is not yet included. Fictional Mock Data remains test-only. +This repository contains the completed static product: season directory, anime detail pages, traceable OP/ED credits and links, per-song source ledgers, source-attributed media, local-only cross-season search, privacy-bounded offline reading, a GitHub correction flow, and deployment through Cloudflare Workers Static Assets. The default catalogue covers twenty-nine reviewed snapshots across 2019–2026, with 1,949 unique titles and 4,323 known OP/ED records. The 2019 spring snapshot contains thirty-two TV titles, including television shorts and a TV recut; summer contains 38 TV titles and four web series. Both quarters are still being expanded, and 2025 fall is not yet included. Fictional Mock Data remains test-only. -目前 source 版本:**v1.47.0**。歌曲較多的作品頁提供可收合的 OP/ED 導覽,按曲名與演唱者直接選擇版本;鍵盤、無 JavaScript 與離線閱讀均可使用。目錄共有 29 個季度、1,948 套作品與 4,320 筆 OP/ED;春季為 31 套作品、91 筆歌曲,春、夏季仍在補充,2025 秋季尚未收錄。 +目前 source 版本:**v1.48.0**。新增《爆丸 決戰星球》的日本播出版與 3 筆 OP/ED,保留共同詞曲編曲署名,並區分前後期片尾;未確認的歌曲發行日期及影片保留空缺。目錄共有 29 個季度、1,949 套作品與 4,323 筆 OP/ED;春季為 32 套作品、94 筆歌曲,春、夏季仍在補充,2025 秋季尚未收錄。 導覽延續固定五個入口與年代、年份、季度目錄;搜尋可按動畫、歌曲、創作者、年份、季度及 OP/ED 篩選,每頁顯示 12 套作品。搜尋完全在瀏覽器內進行,YouTube 只於明確操作後連線。網站與同源靜態 JSON API 共用已審閱資料,無 application backend 或 database binding。依賴安全 gate 繼續鎖定 `fast-uri` 3.1.6 並稽核開發依賴。 @@ -12,7 +12,7 @@ Production build 會從最終 HTML 自動產生 hash-based Content Security Poli | 可用性 / Availability | 成熟度 / Maturity | 證據 / Evidence | | -------------------------------------- | --------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| 公開靜態目錄 / Public static catalogue | Source 版本為 `v1.47.0`;正式發布狀態以對應 Release 驗收摘要為準 | [入口網站 / Live](https://anisonary.k-y.cc) · [版本與驗收 / Releases](https://github.com/kyeunga25/anisonary/releases) · [資料來源 / Sources](docs/DATA_SOURCES.md) · [安全政策 / Security](SECURITY.md) · [授權 / Licence](LICENSING.md) · [版權 / Copyright](COPYRIGHT.md) | +| 公開靜態目錄 / Public static catalogue | Source 版本為 `v1.48.0`;正式發布狀態以對應 Release 驗收摘要為準 | [入口網站 / Live](https://anisonary.k-y.cc) · [版本與驗收 / Releases](https://github.com/kyeunga25/anisonary/releases) · [資料來源 / Sources](docs/DATA_SOURCES.md) · [安全政策 / Security](SECURITY.md) · [授權 / Licence](LICENSING.md) · [版權 / Copyright](COPYRIGHT.md) | ## 技術棧|Technology stack diff --git a/docs/DATA_PROVENANCE.md b/docs/DATA_PROVENANCE.md index ec0654b..98d83a3 100644 --- a/docs/DATA_PROVENANCE.md +++ b/docs/DATA_PROVENANCE.md @@ -31,9 +31,9 @@ API、資料庫、商標或媒體重用權。完整授權邊界見 `first_party` 表示動畫製作/發行方、唱片公司、藝人官方渠道、官方影片或正式播出機構的公開頁面。`cross_check` 表示用於發現缺口及交叉比對的公開索引。兩種角色都是來源分類,不代表數值信心或內容評分。 -2019 春季目前整理 31 套 TV 作品(含電視短篇及重編版),夏季整理 38 套 TV 作品及 4 套網絡連載;兩季的其他作品、跨季延續與特殊歌曲仍待核對;`coverageNote` 會在季度頁及 API 公開說明本季範圍。 +2019 春季目前整理 32 套 TV 作品(含電視短篇及重編版),夏季整理 38 套 TV 作品及 4 套網絡連載;兩季的其他作品、跨季延續與特殊歌曲仍待核對;`coverageNote` 會在季度頁及 API 公開說明本季範圍。 -目前二十九季快照的 season references、1,948 個作品及 4,320 筆 OP/ED 都有結構化 provenance;4,320 筆歌曲全部至少保留一個 `first_party` 與一個 `cross_check` 來源。`tests/unit/curated-catalog.test.ts` 鎖定來源數量、角色、語言、HTTPS、核對日期、Mock URL 禁止條件,以及 legacy labels 與結構化 ledger 的一致性。 +目前二十九季快照的 season references、1,949 個作品及 4,323 筆 OP/ED 都有結構化 provenance;4,323 筆歌曲全部至少保留一個 `first_party` 與一個 `cross_check` 來源。`tests/unit/curated-catalog.test.ts` 鎖定來源數量、角色、語言、HTTPS、核對日期、Mock URL 禁止條件,以及 legacy labels 與結構化 ledger 的一致性。 ## 欄位決策 diff --git a/docs/DATA_SOURCES.md b/docs/DATA_SOURCES.md index efe9072..8701ff0 100644 --- a/docs/DATA_SOURCES.md +++ b/docs/DATA_SOURCES.md @@ -11,13 +11,13 @@ repository 的 Apache-2.0 軟件授權範圍內,詳見 季度完整性定義為:在日本於該季度日曆月份首播的非成人 `TV`/`WEB` 連載動畫,包含短篇、新季度與分割 cour;排除劇場版、OVA、宣傳短片及 18+ 作品。正式播出/配信的獨立特別篇只有在至少兩個季度來源一致歸入該季、並有可追溯第一方發布時才收錄。若作品在季度開始前數日先行發布,亦須由至少兩個季度目錄或主題曲索引一致歸入該季。同一作品跨季時共用一個詳情頁。 -目前 repository 中已審閱的快照為:2026 夏季 70 套、2026 春季 70 套、2026 冬季 66 套、2025 夏季 75 套、2025 春季 82 套、2025 冬季 59 套、2024 秋季 88 套、2024 夏季 68 套、2024 春季 76 套、2024 冬季 75 套、2023 秋季 100 套、2023 夏季 75 套、2023 春季 79 套、2023 冬季 72 套、2022 秋季 80 套、2022 夏季 69 套、2022 春季 79 套、2022 冬季 58 套、2021 秋季 65 套、2021 夏季 46 套、2021 春季 73 套、2021 冬季 67 套、2020 秋季 67 套、2020 夏季 31 套、2020 春季 61 套、2020 冬季 58 套、2019 秋季 67 套、2019 夏季 42 套(38 套 TV、4 套網絡連載)及 2019 春季 31 套 TV 作品(含電視短篇及重編版),共 1,948 個作品詳情、4,320 筆 OP/ED 與 567 個歌曲層 YouTube 直連;其中 1,805 筆已保留官方或正式授權影片 metadata。 +目前 repository 中已審閱的快照為:2026 夏季 70 套、2026 春季 70 套、2026 冬季 66 套、2025 夏季 75 套、2025 春季 82 套、2025 冬季 59 套、2024 秋季 88 套、2024 夏季 68 套、2024 春季 76 套、2024 冬季 75 套、2023 秋季 100 套、2023 夏季 75 套、2023 春季 79 套、2023 冬季 72 套、2022 秋季 80 套、2022 夏季 69 套、2022 春季 79 套、2022 冬季 58 套、2021 秋季 65 套、2021 夏季 46 套、2021 春季 73 套、2021 冬季 67 套、2020 秋季 67 套、2020 夏季 31 套、2020 春季 61 套、2020 冬季 58 套、2019 秋季 67 套、2019 夏季 42 套(38 套 TV、4 套網絡連載)及 2019 春季 32 套 TV 作品(含電視短篇及重編版),共 1,949 個作品詳情、4,323 筆 OP/ED 與 567 個歌曲層 YouTube 直連;其中 1,805 筆已保留官方或正式授權影片 metadata。 每季先以繁中年度動畫表的對應月份建立日曆清單,再與 Annict、Bangumi 及 AniList identifier 取聯集核對。OP/ED 以作品官網和官方 YouTube 為主,並用 AnimeThemes、UZUREA 與 UtaTen 等季度主題曲表交叉確認;季度歌曲索引未收錄的作品,須返回 Netflix、唱片公司、IP 官網、正式播出機構或製作公司等第一方頁面補證。台港正式代理、串流或出版社名稱優先;未有可確認的繁中名稱時保留原文,不宣稱為正式授權譯名。 2019 春、夏季仍在補充,2025 秋季尚未收錄;季度快照代表已核對範圍,不代表歷史作品或歌曲已全部補齊。 -每首已發布歌曲在 `PublicTheme.sources[]` 留下 URL、語言、`first_party`/`cross_check` 角色及核對日期。二十九季快照的 4,320 筆歌曲都必須同時具備兩種角色;這是公開 provenance gate,不是 confidence score。第三方索引將歌曲標為 OP/ED、但第一方頁面只證實插曲、劇中歌、Victory Dance、其他節目用途、舊季度歌曲、其他改編版本或一般「主題歌」時,不把它升格為官方 OP/ED;在既有導航契約中保留的一般主題曲會明示「主題歌(官方分類)」。完整欄位與媒體邊界見 [`DATA_PROVENANCE.md`](./DATA_PROVENANCE.md)。 +每首已發布歌曲在 `PublicTheme.sources[]` 留下 URL、語言、`first_party`/`cross_check` 角色及核對日期。二十九季快照的 4,323 筆歌曲都必須同時具備兩種角色;這是公開 provenance gate,不是 confidence score。第三方索引將歌曲標為 OP/ED、但第一方頁面只證實插曲、劇中歌、Victory Dance、其他節目用途、舊季度歌曲、其他改編版本或一般「主題歌」時,不把它升格為官方 OP/ED;在既有導航契約中保留的一般主題曲會明示「主題歌(官方分類)」。完整欄位與媒體邊界見 [`DATA_PROVENANCE.md`](./DATA_PROVENANCE.md)。 ## 固定來源 diff --git a/docs/PROJECT_PLAN.md b/docs/PROJECT_PLAN.md index 461d83d..ec2f8aa 100644 --- a/docs/PROJECT_PLAN.md +++ b/docs/PROJECT_PLAN.md @@ -80,6 +80,7 @@ - **v1.45.0 source**:歌曲署名直接開啟本機創作者文字搜尋,保留完整名稱及既有字數上限;入口條件使用不會送出的網址片段,手動編輯時清除,資料、API 與全域導覽數量不變;發布證據見對應 GitHub Release。 - **v1.46.0 source**:2019 春季增加《強襲魔女 501部隊出動!》TV 短篇、13 筆 OP/ED 與 2 筆官方影片 metadata;12 個片尾演唱版本各自保留,作曲署名衝突欄位暫缺,春季共 31 套作品與 91 筆歌曲;發布證據見對應 GitHub Release。 - **v1.47.0 source**:歌曲超過 4 筆的作品新增可收合 OP/ED 導覽,完整列出曲名與演唱者,保留原有深連結、鍵盤焦點及無 JavaScript/離線閱讀;長清單高度受限,資料、API 與全域導覽數量不變;發布證據見對應 GitHub Release。 +- **v1.48.0 source**:2019 春季增加《爆丸 決戰星球》日本播出版與 3 筆 OP/ED;區分前後期片尾、共同製作署名與台灣名稱來源,未確認發行日期及影片留空,春季共 32 套作品與 94 筆歌曲;發布證據見對應 GitHub Release。 - 正式網站使用 custom domain;非正式 Cloudflare hostname 不在公開文檔記錄; - 私有 backend、資料庫、crawler、帳戶設定、憑證與內部規則不屬於本公開 repository。 diff --git a/docs/QA_V1_48_2019_SPRING_BAKUGAN.md b/docs/QA_V1_48_2019_SPRING_BAKUGAN.md new file mode 100644 index 0000000..d2898ec --- /dev/null +++ b/docs/QA_V1_48_2019_SPRING_BAKUGAN.md @@ -0,0 +1,27 @@ +# v1.48.0|2019 春季《爆丸 決戰星球》QA + +核對日期:2026-09-07。本文件記錄 source 範圍與本機驗證;正式部署須於固定版本合併後獨立驗收。 + +## 資料與來源 + +- 新增《爆丸バトルプラネット/爆丸 決戰星球》日本播出版、1 首 OP 與 2 首 ED;春季共 32 套 TV 作品、94 筆歌曲,仍保留季度未完成說明。 +- [東京電視台播出表](https://www.tv-tokyo.co.jp/anime/bakugan-bp/onair/)與 [SEGA 2019 年公告](https://www.sega.jp/topics/detail/190306_goods_1/)核對日本首播為 2019-04-01、星期一 17:55。使用具來源的本站識別碼,不生成外部索引 ID、Romaji 或圖片。 +- [YOYOTV 台灣播出方](https://www.youtube.com/watch?v=9yiBbt820uo)及 [YourAnimes 繁中季表](https://youranimes.tw/bangumi/201904)核對繁中名稱。YOYOTV 的中文主題影片只作作品名稱來源,未關聯為日語 OP/ED 影片,也不自動載入媒體。 +- OP〈情熱ジャンボリー〉由 HiHi Jets 演唱;SEGA 與[東京電視台製作頁](https://www.tv-tokyo.co.jp/anime/bakugan-bp/staff/)核對作詞 MiNE、共同作曲川口進/MiNE/Atsushi Shimada,以及共同編曲 Atsushi Shimada/Peach。 +- 前期 ED〈Be my story〉由 HiHi Jets 演唱;SEGA 及 [onetrap 創作者官方](https://onetrap.ageha.net/archives/onetrap_news/0067)核對中村崇人的詞曲署名,SEGA 另列編曲 Dr.Dalmatian。 +- 後期 ED〈サヨナラの方程式〉由 HiHi Jets 演唱;東京電視台製作頁核對作詞 miyakei、共同作曲大智/児山啓介。[作品首頁](https://www.tv-tokyo.co.jp/anime/bakugan-bp/)保留 2020-01-20 主題曲更新公告,因此明示後期歌曲於 2020 年更新,作品仍按日本首次播出的 2019 春季分類。 +- [アニソンライブラリー](https://japan-anime-song.com/bakumarubatorupuranetto-anison/)交叉核對 OP/ED 次序與署名。其影片公開日期不作歌曲發行日期;三筆歌曲的發行日期、日語官方影片與後期 ED 編曲均保留空缺,不額外推定單集使用範圍。 + +## 本機驗收 + +- `npm run catalog:check`:232 項通過。 +- `npm run check` 中公開邊界、Lint、335 項單元測試、建置及 Cloudflare dry-run 通過;瀏覽器啟動受到本機沙箱限制,另在允許環境執行 `npm run test:e2e`,43 項全部通過且沒有重試。 +- 完整依賴與正式依賴稽核均為 0 項漏洞;`fast-uri` 保持 3.1.6,既有安全回歸通過。 +- 與 v1.47.0 source 比較:1,977 份 API 逐位元相同,只更新春季清單並新增作品詳情,沒有移除。建置產生 1,993 份 HTML、3,985 份公開產物,dry-run 沒有 runtime bindings。 +- 四項新增資料測試先確認缺少記錄時失敗,再驗證日本版身份、台灣名稱來源、共同製作署名、未知欄位、API 讀回與繁中/創作者搜尋。首輪瀏覽器回歸發現既有 2020 秋季的影片篩選預期數誤改,恢復原值後通過;該季資料沒有變動。 +- 新增 E2E 涵蓋 1280、960、390、320px 的繁中搜尋、創作者連結、年份/季度/ED 篩選及後期片尾深連結,以及 390px 無 JavaScript 年度、季度、作品與來源導覽。原有離線、鍵盤、影片同意、404 與錯誤狀態回歸通過。 +- Chromium 檢查作品、OP、前期 ED、後期 ED 與春季頁共 25 個畫面,包含 390px 無 JavaScript 閱讀;沒有水平溢出、頁面錯誤或使用者操作前的 iframe/YouTube 請求。人工檢視桌面及手機作品與後期片尾截圖。 + +## 發布界線 + +本機與分支建置結果不代表正式網站已更新。發布時仍須核對合併 SHA、CI、Cloudflare 實際版本與流量,以及 live routes、headers、API 與產物內容。本切片未驗證實體裝置、Safari、Firefox、Lighthouse/Core Web Vitals 或實際 YouTube 播放。 diff --git a/package-lock.json b/package-lock.json index 7970680..dbcd016 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "anisonary", - "version": "1.47.0", + "version": "1.48.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "anisonary", - "version": "1.47.0", + "version": "1.48.0", "license": "SEE LICENSE IN LICENSING.md", "dependencies": { "@astrojs/sitemap": "^3.7.3", diff --git a/package.json b/package.json index aecb53f..fdb4666 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "anisonary", - "version": "1.47.0", + "version": "1.48.0", "private": true, "license": "SEE LICENSE IN LICENSING.md", "type": "module", diff --git a/src/data/curated-data.ts b/src/data/curated-data.ts index 18dce20..1ea800d 100644 --- a/src/data/curated-data.ts +++ b/src/data/curated-data.ts @@ -4606,6 +4606,12 @@ const animeOfficialSourceOverrides: Record = { + "catalog-bakugan-battle-planet-2019": [{ + label: "YOYOTV 台灣播出方:繁中作品名稱;中文主題影片不作日語 OP/ED 版本", + url: "https://www.youtube.com/watch?v=9yiBbt820uo", + language: "zh-Hant", + role: "first_party" + }], 105749: [{ label: "東京電視台:2019 年首集、首播日期與播出時段", url: "https://www.tv-tokyo.co.jp/broad_tvtokyo/program/detail/201904/22856_201904021755.html", diff --git a/src/data/curated-season-registry.ts b/src/data/curated-season-registry.ts index 706f8d0..5a8b635 100644 --- a/src/data/curated-season-registry.ts +++ b/src/data/curated-season-registry.ts @@ -452,7 +452,7 @@ export const curatedSeasonRegistry = validateCuratedSeasonRegistry([ quarter: "spring", titleZhHant: "春季動畫", titleJa: "2019年春アニメ", - coverageNote: "本季正在補充,目前收錄 31 套 TV 作品(含電視短篇及重編版)。其餘作品、跨季延續及特殊歌曲版本仍待核對。", + coverageNote: "本季正在補充,目前收錄 32 套 TV 作品(含電視短篇及重編版)。其餘作品、跨季延續及特殊歌曲版本仍待核對。", seeds: curated2019SpringSeeds, animeIds: curated2019SpringAnimeIds } diff --git a/src/data/curated-seeds/2019/spring.ts b/src/data/curated-seeds/2019/spring.ts index b43423c..ab3d8a8 100644 --- a/src/data/curated-seeds/2019/spring.ts +++ b/src/data/curated-seeds/2019/spring.ts @@ -1698,6 +1698,66 @@ const strikeWitches501EndingRows = [ ] as const; const independentSpringSeeds: NativeCuratedAnimeSeed[] = [{ + id: "catalog-bakugan-battle-planet-2019", + slug: "bakugan-battle-planet-2019", + titleJa: "爆丸バトルプラネット", + titleZhHant: "爆丸 決戰星球", + seasonIds: ["2019-spring"], + startDate: "2019-04-01", + editorialWeekday: 1, + broadcastTimeJst: "17:55", + broadcastLabel: "東京電視台每週一 17:55(日本時間;日本播出版)", + status: "finished", + verifiedAt: "2026-09-07", + officialSiteUrl: "https://www.tv-tokyo.co.jp/anime/bakugan-bp/", + identifierSource: { + label: "東京電視台:日本播出版作品識別、首播日期與時段", + url: "https://www.tv-tokyo.co.jp/anime/bakugan-bp/onair/", + language: "ja" + }, + wikipediaUrl: "https://youranimes.tw/bangumi/201904", + sourceReferenceUrls: [ + "https://youranimes.tw/animes/721", + "https://www.sega.jp/topics/detail/190306_goods_1/" + ], + themes: [{ + type: "OP", sequence: 1, + titleJa: "情熱ジャンボリー", + artistDisplayName: "HiHi Jets", + versionLabel: "日本播出版", + credits: [ + { name: "HiHi Jets", role: "vocals" }, + { name: "MiNE", role: "lyrics" }, + { name: "川口進", role: "composition" }, + { name: "MiNE", role: "composition" }, + { name: "Atsushi Shimada", role: "composition" }, + { name: "Atsushi Shimada", role: "arrangement" }, + { name: "Peach", role: "arrangement" } + ] + }, { + type: "ED", sequence: 1, + titleJa: "Be my story", + artistDisplayName: "HiHi Jets", + versionLabel: "日本播出版/前期 ED", + credits: [ + { name: "HiHi Jets", role: "vocals" }, + { name: "中村崇人", role: "lyrics" }, + { name: "中村崇人", role: "composition" }, + { name: "Dr.Dalmatian", role: "arrangement" } + ] + }, { + type: "ED", sequence: 2, + titleJa: "サヨナラの方程式", + artistDisplayName: "HiHi Jets", + versionLabel: "日本播出版/後期 ED(2020 年更新)", + credits: [ + { name: "HiHi Jets", role: "vocals" }, + { name: "miyakei", role: "lyrics" }, + { name: "大智", role: "composition" }, + { name: "児山啓介", role: "composition" } + ] + }] +}, { id: "catalog-strike-witches-501-2019", slug: "strike-witches-501-takeoff-2019", titleJa: "ストライクウィッチーズ 501部隊発進しますっ!", diff --git a/src/data/curated-theme-sources/2019/spring.ts b/src/data/curated-theme-sources/2019/spring.ts index b08745d..a5e9bc6 100644 --- a/src/data/curated-theme-sources/2019/spring.ts +++ b/src/data/curated-theme-sources/2019/spring.ts @@ -3,6 +3,18 @@ import { getCuratedAnimeKey, getCuratedThemeKey } from "@/data/curated-seeds/ide import type { CuratedThemeSourceOverrideMap, CuratedThemeSourceSeed } from "@/data/curated-theme-sources/types"; const firstPartyUrlsByTheme: Readonly> = { + "catalog-bakugan-battle-planet-2019:OP:1": [ + "https://www.sega.jp/topics/detail/190306_goods_1/", + "https://www.tv-tokyo.co.jp/anime/bakugan-bp/staff/" + ], + "catalog-bakugan-battle-planet-2019:ED:1": [ + "https://www.sega.jp/topics/detail/190306_goods_1/", + "https://onetrap.ageha.net/archives/onetrap_news/0067" + ], + "catalog-bakugan-battle-planet-2019:ED:2": [ + "https://www.tv-tokyo.co.jp/anime/bakugan-bp/staff/", + "https://www.tv-tokyo.co.jp/anime/bakugan-bp/" + ], "catalog-strike-witches-501-2019:OP:1": [ "https://columbia.jp/artist-info/ishidayoko/info/64416.html", "https://w-witch.jp/news/20190226_2" @@ -239,6 +251,10 @@ const firstPartyUrlsByTheme: Readonly> = { }; const sourceLabelsByUrl: Readonly> = { + "https://www.sega.jp/topics/detail/190306_goods_1/": "SEGA 官方公告:日本版 OP 與前期 ED、演唱者及詞曲編曲署名", + "https://www.tv-tokyo.co.jp/anime/bakugan-bp/staff/": "東京電視台:日本版 OP 與後期 ED、演唱者及製作資料", + "https://onetrap.ageha.net/archives/onetrap_news/0067": "onetrap 創作者官方:前期 ED 用途與中村崇人的詞曲署名", + "https://www.tv-tokyo.co.jp/anime/bakugan-bp/": "東京電視台:日本版作品與 2020 年主題曲更新公告", "https://columbia.jp/prod-info/COCX-40890/": "日本 Columbia:TV 片尾 12 個演唱版本、CD 完整版日期與 #12 試聽", "https://columbia.jp/artist-info/ishidayoko/info/64416.html": "日本 Columbia:OP/ED 用途、OP 製作資料、ED 作詞與編曲;ED 作曲署名待核對", "https://w-witch.jp/news/20190226_2": "動畫官方:OP 歌曲、製作資料與短版 MV", @@ -356,6 +372,12 @@ const sourceLabelsByUrl: Readonly> = { }; const crossCheckSourcesByAnime: Readonly> = { + "catalog-bakugan-battle-planet-2019": { + label: "アニソンライブラリー:日本版 OP/ED 次序及署名交叉核對;影片公開日不作歌曲發行日期", + url: "https://japan-anime-song.com/bakumarubatorupuranetto-anison/", + language: "ja", + role: "cross_check" + }, "catalog-strike-witches-501-2019": { label: "Animate Times:2019 年 TV 版 OP/ED 用途與演唱名義交叉核對;輪替版本以唱片公司為準", url: "https://www.animatetimes.com/tag/details.php?id=15506", diff --git a/tests/e2e/phase1.spec.ts b/tests/e2e/phase1.spec.ts index 4ef3831..78e4b0a 100644 --- a/tests/e2e/phase1.spec.ts +++ b/tests/e2e/phase1.spec.ts @@ -460,7 +460,7 @@ test("cross-season search stays local and matches anime, songs, and artists", as await page.goto("/search/"); await expect(page.getByRole("heading", { name: "跨季度搜尋" })).toBeVisible(); - await expect(page.locator("[data-catalog-anime-count]")).toHaveText("1948"); + await expect(page.locator("[data-catalog-anime-count]")).toHaveText("1949"); const search = page.getByRole("searchbox", { name: "搜尋動畫、歌曲或創作者" }); await search.fill("MYTH & ROID"); @@ -500,7 +500,7 @@ test("cross-season search stays local and matches anime, songs, and artists", as await expect(page.locator("[data-catalog-anime-count]")).toHaveText("0"); await search.press("Escape"); - await expect(page.locator("[data-catalog-anime-count]")).toHaveText("1948"); + await expect(page.locator("[data-catalog-anime-count]")).toHaveText("1949"); expect(externalRequests).toEqual([]); }); @@ -908,7 +908,7 @@ test("catalogue navigation stays compact and drills down through published years await expect(navigation.getByRole("link", { name: /動畫目錄/ })).toHaveAttribute("aria-current", "true"); await expect(page.getByRole("navigation", { name: "2025 年季度導覽" }).getByRole("link", { name: "春季" })).toHaveAttribute("aria-current", "page"); await expect(page.getByRole("navigation", { name: "所在位置" }).getByRole("link", { name: "2025", exact: true })).toHaveAttribute("href", "/catalog/2025/"); - await expect(page.getByLabel("季度資料狀態")).toContainText("已發布 29 個季度、1,948 個作品頁與 4,320 首 OP/ED"); + await expect(page.getByLabel("季度資料狀態")).toContainText("已發布 29 個季度、1,949 個作品頁與 4,323 首 OP/ED"); await page.setViewportSize({ width: 390, height: 844 }); await page.reload(); @@ -1245,10 +1245,10 @@ test("2019 spring browsing and creator search reach the correct special ending o for (const width of [1280, 390]) { await page.setViewportSize({ width, height: 844 }); await page.goto("/catalog/2019/"); - await page.getByRole("link", { name: /春季動畫.*31 套動畫/ }).click(); + await page.getByRole("link", { name: /春季動畫.*32 套動畫/ }).click(); await expect(page.getByRole("heading", { name: "2019 春季動畫" })).toBeVisible(); - await expect(page.getByText("本季正在補充,目前收錄 31 套 TV 作品(含電視短篇及重編版)。其餘作品、跨季延續及特殊歌曲版本仍待核對。", { exact: true })).toBeVisible(); - await expect(page.locator("[data-anime-card]")).toHaveCount(31); + await expect(page.getByText("本季正在補充,目前收錄 32 套 TV 作品(含電視短篇及重編版)。其餘作品、跨季延續及特殊歌曲版本仍待核對。", { exact: true })).toBeVisible(); + await expect(page.locator("[data-anime-card]")).toHaveCount(32); await expect(page.locator("[data-anime-card] img")).toHaveCount(0); expect(await page.evaluate(() => document.documentElement.scrollWidth <= innerWidth)).toBe(true); @@ -1681,6 +1681,62 @@ test("anime details keep full-width poster-free headings and readable metadata a } }); +test("Bakugan Japanese theme search preserves shared credits and separates the later ending from localized media", async ({ page, browser, request }) => { + const slug = "bakugan-battle-planet-2019"; + const mediaRequests: string[] = []; + page.on("request", (item) => { + if (/youtube|ytimg|googlevideo/.test(new URL(item.url()).hostname)) mediaRequests.push(item.url()); + }); + const response = await request.get(`/api/v1/anime/${slug}.json`); + expect(response.status()).toBe(200); + const detail = await response.json(); + expect(detail.id).toBe("catalog-bakugan-battle-planet-2019"); + expect(detail.themes).toHaveLength(3); + expect(detail.themes.every((theme: { releaseDate?: string }) => !theme.releaseDate)).toBe(true); + for (const width of [1280, 960, 390, 320]) { + await page.setViewportSize({ width, height: 844 }); + await page.goto("/search/"); + await page.getByRole("searchbox", { name: "搜尋動畫、歌曲或創作者" }).fill("爆丸 決戰星球"); + await page.getByRole("link", { name: /爆丸バトルプラネット/ }).first().click(); + await expect(page).toHaveURL(new RegExp(`/anime/${slug}/$`)); + await expect(page.locator(".anime-hero__zh")).toHaveText("爆丸 決戰星球"); + await expect(page.locator(".anime-hero__romaji")).toHaveCount(0); + await expect(page.locator(".theme-card")).toHaveCount(3); + await expect(page.locator(".theme-navigation")).toHaveCount(0); + await expect(page.locator(".theme-card time")).toHaveCount(0); + await expect(page.locator(`#theme-${slug}-op-1 .theme-card__credits dd`)).toHaveText(["MiNE", "川口進、MiNE、Atsushi Shimada", "Atsushi Shimada、Peach"]); + await expect(page.locator(`#theme-${slug}-ed-1 .theme-card__credits dd`)).toHaveText(["中村崇人", "中村崇人", "Dr.Dalmatian"]); + const later = page.locator(`#theme-${slug}-ed-2`); + await expect(later.locator(".theme-card__credits dt")).toHaveText(["作詞", "作曲"]); + await expect(later).toContainText("後期 ED(2020 年更新)"); + await later.getByRole("link", { name: "搜尋「児山啓介」的歌曲", exact: true }).focus(); + await page.keyboard.press("Enter"); + await expect(page.getByRole("searchbox", { name: "搜尋動畫、歌曲或創作者" })).toHaveValue("児山啓介"); + for (const [label, value] of [["年份", "2019"], ["季度", "spring"], ["歌曲用途", "ED"]]) await page.getByLabel(label!, { exact: true }).selectOption(value!); + await expect(page.locator("[data-catalog-result]")).toHaveCount(1); + await page.getByRole("link", { name: "サヨナラの方程式", exact: true }).click(); + await expect(later).toBeFocused(); + await expect(page.locator("iframe")).toHaveCount(0); + await expect(page.getByRole("button", { name: /載入 YouTube 影片/ })).toHaveCount(0); + expect(await page.evaluate(() => document.documentElement.scrollWidth <= innerWidth)).toBe(true); + } + expect(mediaRequests).toEqual([]); + const context = await browser.newContext({ javaScriptEnabled: false, viewport: { width: 390, height: 844 } }); + try { + const nativePage = await context.newPage(); + await nativePage.goto(`${e2eOrigin}/catalog/2019/`); + await nativePage.getByRole("link", { name: /春季動畫.*32 套動畫/ }).click(); + await nativePage.getByRole("link", { name: /爆丸バトルプラネット/ }).first().click(); + await expect(nativePage).toHaveURL(new RegExp(`/anime/${slug}/$`)); + await expect(nativePage.locator(".theme-card")).toHaveCount(3); + await expect(nativePage.getByRole("link", { name: /YOYOTV 台灣播出方/ })).toHaveAttribute("href", "https://www.youtube.com/watch?v=9yiBbt820uo"); + await expect(nativePage.locator("iframe")).toHaveCount(0); + expect(await nativePage.evaluate(() => document.documentElement.scrollWidth <= innerWidth)).toBe(true); + } finally { + await context.close(); + } +}); + test("long song lists provide compact local navigation with keyboard, history and correct ending targets", async ({ page }) => { const slug = "strike-witches-501-takeoff-2019"; const mediaRequests: string[] = []; @@ -1810,7 +1866,7 @@ test("501 Takeoff creator search reaches the correct solo ending and keeps the g try { const nativePage = await context.newPage(); await nativePage.goto(`${e2eOrigin}/catalog/2019/`); - await nativePage.getByRole("link", { name: /春季動畫.*31 套動畫/ }).click(); + await nativePage.getByRole("link", { name: /春季動畫.*32 套動畫/ }).click(); await nativePage.getByRole("link", { name: /ストライクウィッチーズ 501部隊発進しますっ/ }).first().click(); await expect(nativePage).toHaveURL(new RegExp(`/anime/${slug}/$`)); await expect(nativePage.locator(".theme-card")).toHaveCount(13); @@ -1872,7 +1928,7 @@ test("Gonjiro search preserves independent identity, co-composers and the licens const context = await browser.newContext({ javaScriptEnabled: false, viewport: { width: 390, height: 844 } }); const nativePage = await context.newPage(); await nativePage.goto(`${e2eOrigin}/catalog/2019/`); - await nativePage.getByRole("link", { name: /春季動畫.*31 套動畫/ }).click(); + await nativePage.getByRole("link", { name: /春季動畫.*32 套動畫/ }).click(); await nativePage.getByRole("link", { name: /けだまのゴンじろー/ }).first().click(); await expect(nativePage).toHaveURL(/\/anime\/gonjiro-2019\/$/); await expect(nativePage.locator(".theme-card")).toHaveCount(2); @@ -2182,7 +2238,7 @@ test("weekday navigation follows populated groups and remains useful after filte await expect(navigation.getByRole("link", { name: "週六" })).toHaveAttribute("aria-current", "location"); await page.getByRole("button", { name: "清除篩選" }).click(); await expect(navigation.getByRole("link")).toHaveText(["週一", "週二", "週三", "週四", "週五", "週六", "週日"]); - await expect(page.locator("[data-result-count]")).toHaveText("31"); + await expect(page.locator("[data-result-count]")).toHaveText("32"); await expect(page.getByRole("checkbox", { name: "有 OP" })).toBeFocused(); expect(await page.evaluate(() => document.documentElement.scrollWidth <= innerWidth)).toBe(true); } @@ -2205,7 +2261,7 @@ test("weekday navigation follows populated groups and remains useful after filte await expect(nativePage.getByRole("navigation", { name: "跳到播出星期" }).getByRole("link")) .toHaveText(["週一", "週二", "週三", "週四", "週五", "週六", "週日"]); await expect(nativePage.locator("[data-weekday-section]")).toHaveCount(7); - await expect(nativePage.locator("[data-anime-card]")).toHaveCount(31); + await expect(nativePage.locator("[data-anime-card]")).toHaveCount(32); await nativePage.getByRole("link", { name: "週五", exact: true }).click(); await expect(nativePage).toHaveURL(/#weekday-5$/); expect(await nativePage.evaluate(() => document.documentElement.scrollWidth <= innerWidth)).toBe(true); diff --git a/tests/unit/api-smoke.test.ts b/tests/unit/api-smoke.test.ts index 1d0e2e8..d082d12 100644 --- a/tests/unit/api-smoke.test.ts +++ b/tests/unit/api-smoke.test.ts @@ -16,7 +16,7 @@ describe("production API smoke gate", () => { it("accepts the complete reviewed catalogue and its not-found behavior", async () => { await expect(smokePublicApi(new CuratedProvider())).resolves.toEqual({ seasonCount: 29, - animeCount: 1948 + animeCount: 1949 }); }); diff --git a/tests/unit/catalog-navigation.test.ts b/tests/unit/catalog-navigation.test.ts index 19f6c4d..0a46844 100644 --- a/tests/unit/catalog-navigation.test.ts +++ b/tests/unit/catalog-navigation.test.ts @@ -27,7 +27,7 @@ describe("catalogue navigation and search", () => { expect(results.some(({ anime }) => anime.slug === "re-zero-season-4")).toBe(true); } expect(searchCatalog(index, { ...defaults, year: "2025", quarter: "fall" })).toEqual([]); - expect(searchCatalog(index, defaults)).toHaveLength(1948); + expect(searchCatalog(index, defaults)).toHaveLength(1949); }); it("combines multilingual queries with exact song, creator, and OP/ED filters", () => { diff --git a/tests/unit/curated-2019-spring.test.ts b/tests/unit/curated-2019-spring.test.ts index 09bd27c..34fbc61 100644 --- a/tests/unit/curated-2019-spring.test.ts +++ b/tests/unit/curated-2019-spring.test.ts @@ -10,6 +10,79 @@ const spring = curatedSeasonDetails.find(({ id }) => id === "2019-spring")!; const anime = (id: number) => curatedAnimeDetails.find((item) => item.id === `curated-${id}`)!; describe("2019 spring reviewed TV catalogue", () => { + it("identifies Bakugan Battle Planet by its 2019 Japanese TV premiere and the Taiwanese broadcaster's title", async () => { + const seed = curated2019SpringSeeds.find(({ id }) => id === "catalog-bakugan-battle-planet-2019")!; + expect(seed).toMatchObject({ startDate: "2019-04-01", editorialWeekday: 1, broadcastTimeJst: "17:55", seasonIds: ["2019-spring"] }); + const detail = curatedAnimeDetails.find(({ id }) => id === seed.id)!; + expect(detail).toMatchObject({ slug: "bakugan-battle-planet-2019", titleJa: "爆丸バトルプラネット", titleZhHant: "爆丸 決戰星球", status: "finished" }); + for (const omitted of ["anilistId", "anilistUrl", "titleRomaji", "posterUrl", "bannerUrl"]) { + expect(seed).not.toHaveProperty(omitted); + expect(detail).not.toHaveProperty(omitted); + } + expect(detail.sources).toContainEqual(expect.objectContaining({ url: "https://www.tv-tokyo.co.jp/anime/bakugan-bp/onair/", role: "identifier", language: "ja" })); + expect(detail.sources).toContainEqual(expect.objectContaining({ url: "https://www.youtube.com/watch?v=9yiBbt820uo", role: "first_party", language: "zh-Hant" })); + const provider = new ApiProvider("https://anisonary.k-y.cc/api/v1", { + fetch: vi.fn().mockResolvedValue(new Response(JSON.stringify(detail), { headers: { "Content-Type": "application/json" } })) + }); + expect(await provider.getAnime(detail.slug)).toEqual(detail); + }); + + it("keeps Bakugan's Japanese opening and two endings distinct from localized songs and unconfirmed release dates", () => { + const themes = curatedAnimeDetails.find(({ id }) => id === "catalog-bakugan-battle-planet-2019")!.themes; + expect(themes.map(({ type, sequence, titleJa, artistDisplayName }) => [type, sequence, titleJa, artistDisplayName])).toEqual([ + ["OP", 1, "情熱ジャンボリー", "HiHi Jets"], + ["ED", 1, "Be my story", "HiHi Jets"], + ["ED", 2, "サヨナラの方程式", "HiHi Jets"] + ]); + for (const theme of themes) { + expect(theme.versionLabel).toContain("日本播出版"); + expect(theme.releaseDate).toBeUndefined(); + expect(theme.videos).toEqual([]); + expect(theme.links.some(({ platform }) => platform === "YouTube")).toBe(false); + } + expect(themes[2]?.versionLabel).toContain("後期 ED"); + expect(themes[0]?.credits.filter(({ role }) => role === "composition")).toEqual([ + { name: "川口進", role: "composition" }, { name: "MiNE", role: "composition" }, { name: "Atsushi Shimada", role: "composition" } + ]); + expect(themes[0]?.credits.filter(({ role }) => role === "arrangement")).toEqual([ + { name: "Atsushi Shimada", role: "arrangement" }, { name: "Peach", role: "arrangement" } + ]); + expect(themes[1]?.credits).toEqual([ + { name: "HiHi Jets", role: "vocals" }, { name: "中村崇人", role: "lyrics" }, + { name: "中村崇人", role: "composition" }, { name: "Dr.Dalmatian", role: "arrangement" } + ]); + expect(themes[2]?.credits).toEqual([ + { name: "HiHi Jets", role: "vocals" }, { name: "miyakei", role: "lyrics" }, + { name: "大智", role: "composition" }, { name: "児山啓介", role: "composition" } + ]); + }); + + it("preserves independent first-party song evidence and actual verification dates for Bakugan", () => { + const themes = curatedAnimeDetails.find(({ id }) => id === "catalog-bakugan-battle-planet-2019")!.themes; + for (const theme of themes) { + expect(theme.lastVerifiedAt).toBe("2026-09-07"); + expect(theme.sources.every(({ verifiedAt }) => verifiedAt === "2026-09-07")).toBe(true); + expect(theme.sources).toContainEqual(expect.objectContaining({ url: "https://japan-anime-song.com/bakumarubatorupuranetto-anison/", role: "cross_check", language: "ja" })); + expect(theme.sources.some(({ url }) => url.includes("anisil.com") || url.includes("tms-e.co.jp"))).toBe(false); + } + expect(themes[0]?.sources).toContainEqual(expect.objectContaining({ url: "https://www.sega.jp/topics/detail/190306_goods_1/", role: "first_party" })); + expect(themes[1]?.sources).toContainEqual(expect.objectContaining({ url: "https://onetrap.ageha.net/archives/onetrap_news/0067", role: "first_party" })); + expect(themes[2]?.sources).toContainEqual(expect.objectContaining({ url: "https://www.tv-tokyo.co.jp/anime/bakugan-bp/staff/", role: "first_party" })); + }); + + it("finds Bakugan by its Chinese title, opening co-arranger and second-ending co-composer without leaking into another quarter", async () => { + const index = buildCatalogSearchIndex((await loadCatalogSearchData(new CuratedProvider(), true)).entries); + const options = { year: "2019", quarter: "spring" } as const; + const titleResults = searchCatalog(index, { ...options, query: "爆丸 決戰星球", scope: "anime", type: "all" }); + expect(titleResults.map(({ anime: item }) => item.slug)).toEqual(["bakugan-battle-planet-2019"]); + for (const [query, type, title] of [["Peach", "OP", "情熱ジャンボリー"], ["児山啓介", "ED", "サヨナラの方程式"]] as const) { + const results = searchCatalog(index, { ...options, query, scope: "creators", type }); + expect(results.map(({ anime: item }) => item.slug)).toEqual(["bakugan-battle-planet-2019"]); + expect(results[0]?.themes.map(({ titleJa }) => titleJa)).toEqual([title]); + } + expect(searchCatalog(index, { ...options, query: "HiHi Jets", scope: "creators", type: "all", quarter: "summer" })).toEqual([]); + }); + it("identifies 501 Takeoff as the 2019 TV short on its Tuesday editorial premiere without borrowing a sequel identity", async () => { const seed = curated2019SpringSeeds.find(({ id }) => id === "catalog-strike-witches-501-2019")!; expect(seed).toMatchObject({ startDate: "2019-04-09", seasonIds: ["2019-spring"], verifiedAt: "2026-09-07" }); @@ -595,9 +668,9 @@ describe("2019 spring reviewed TV catalogue", () => { }); it("publishes a partial quarter with reviewed identity and song evidence, without unverified artwork", () => { - expect(spring.anime).toHaveLength(31); + expect(spring.anime).toHaveLength(32); expect(spring.coverageNote).toContain("跨季延續及特殊歌曲版本仍待核對"); - expect(curated2019SpringSeeds.flatMap(({ themes }) => themes)).toHaveLength(91); + expect(curated2019SpringSeeds.flatMap(({ themes }) => themes)).toHaveLength(94); expect(curatedSeasonDetails.some(({ id }) => id === "2025-fall")).toBe(false); for (const seed of curated2019SpringSeeds) { expect(seed.seasonIds).toEqual(["2019-spring"]); diff --git a/tests/unit/curated-catalog.test.ts b/tests/unit/curated-catalog.test.ts index 6e72a5f..f0b9b31 100644 --- a/tests/unit/curated-catalog.test.ts +++ b/tests/unit/curated-catalog.test.ts @@ -38,7 +38,7 @@ describe("curated public catalogue", () => { "2019-spring" ]); expect(curatedSeasonDetails).toHaveLength(29); - expect(curatedSeasonDetails.map((season) => season.anime.length)).toEqual([70, 70, 66, 75, 82, 59, 88, 68, 76, 75, 100, 75, 79, 72, 80, 69, 79, 58, 65, 46, 73, 67, 67, 31, 61, 58, 67, 42, 31]); + expect(curatedSeasonDetails.map((season) => season.anime.length)).toEqual([70, 70, 66, 75, 82, 59, 88, 68, 76, 75, 100, 75, 79, 72, 80, 69, 79, 58, 65, 46, 73, 67, 67, 31, 61, 58, 67, 42, 32]); expect(curatedSeasonDetails.map((season) => [season.id, season.reviewState, season.verifiedAt])).toEqual([ ["2026-summer", "reviewed", "2026-08-02"], ["2026-spring", "reviewed", "2026-08-02"], @@ -70,10 +70,10 @@ describe("curated public catalogue", () => { ["2019-summer", "reviewed", "2026-09-07"], ["2019-spring", "reviewed", "2026-09-07"] ]); - expect(curatedAnimeDetails).toHaveLength(1948); - expect(curatedAnimeDetails.filter((anime) => anime.themes.length > 0)).toHaveLength(1584); - expect(curatedAnimeDetails.flatMap((anime) => anime.themes)).toHaveLength(4320); - expect(curatedAnimeDetails.filter((anime) => anime.themeAvailability === "documented")).toHaveLength(1584); + expect(curatedAnimeDetails).toHaveLength(1949); + expect(curatedAnimeDetails.filter((anime) => anime.themes.length > 0)).toHaveLength(1585); + expect(curatedAnimeDetails.flatMap((anime) => anime.themes)).toHaveLength(4323); + expect(curatedAnimeDetails.filter((anime) => anime.themeAvailability === "documented")).toHaveLength(1585); expect(curatedAnimeDetails.filter((anime) => anime.themeAvailability === "not_used")).toHaveLength(2); expect(curatedAnimeDetails.filter((anime) => anime.themeAvailability === "not_announced")).toHaveLength(362); const youtubeLinks = curatedAnimeDetails diff --git a/tests/unit/curated-season-registry.test.ts b/tests/unit/curated-season-registry.test.ts index 1b65c0f..420c531 100644 --- a/tests/unit/curated-season-registry.test.ts +++ b/tests/unit/curated-season-registry.test.ts @@ -40,7 +40,7 @@ describe("curated season registry", () => { ]); const ownedSeeds = curatedSeasonRegistry.flatMap(({ seeds }) => seeds); - expect(ownedSeeds).toHaveLength(1948); + expect(ownedSeeds).toHaveLength(1949); expect(new Set(ownedSeeds.map(getCuratedAnimeKey)).size).toBe(ownedSeeds.length); for (const entry of curatedSeasonRegistry) { diff --git a/tests/unit/curated-theme-source-registry.test.ts b/tests/unit/curated-theme-source-registry.test.ts index 77648cc..0b034d4 100644 --- a/tests/unit/curated-theme-source-registry.test.ts +++ b/tests/unit/curated-theme-source-registry.test.ts @@ -70,11 +70,11 @@ describe("curated theme source registry", () => { 180, 161, 103, - 91 + 94 ]); const keys = curatedThemeSourceRegistry.flatMap(({ overrides }) => Object.keys(overrides)); - expect(keys).toHaveLength(2058); + expect(keys).toHaveLength(2061); expect(new Set(keys).size).toBe(keys.length); expect(Object.keys(curatedThemeSourceOverrides)).toHaveLength(keys.length); }); diff --git a/tests/unit/page-data.test.ts b/tests/unit/page-data.test.ts index bcfc738..6f229ef 100644 --- a/tests/unit/page-data.test.ts +++ b/tests/unit/page-data.test.ts @@ -65,9 +65,9 @@ describe("catalogue search data states", () => { expect(result.status).toBe("ready"); if (result.status !== "ready") throw new Error("Expected ready catalogue search data"); - expect(result.entries).toHaveLength(1948); - expect(new Set(result.entries.map(({ anime }) => anime.slug)).size).toBe(1948); - expect(result.entries.filter(({ anime }) => anime.themes.length > 0)).toHaveLength(1584); + expect(result.entries).toHaveLength(1949); + expect(new Set(result.entries.map(({ anime }) => anime.slug)).size).toBe(1949); + expect(result.entries.filter(({ anime }) => anime.themes.length > 0)).toHaveLength(1585); }); it("returns a safe public error state without exposing provider details", async () => {