Skip to content

Commit 1c197f2

Browse files
authored
Merge pull request #74 from myoshi2891/dev
IntersectionObserver モックをリファクタリングしテスト安定性を改善
2 parents 80890ed + d620f59 commit 1c197f2

45 files changed

Lines changed: 7280 additions & 413 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.claude/skills/fix-mermaid/SKILL.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,28 @@ vi.mock("@/components/docs/MermaidDiagram", () => ({
245245
}));
246246
```
247247

248+
### 外側 DOMPurify による過剰サニタイズで図が壊れる(2026年6月追記・QA_Studies 実地)
249+
250+
**症状**: ダークモードでノード内テキストが消える(背景と同化)/矢印・枠線のスタイルが当たらない。構文は正しくブラウザ Console にも構文エラーは出ない。
251+
252+
**根本原因**: `mermaid.render()` の出力 SVG に対し、コンポーネント側で**外側から** `DOMPurify.sanitize(svg, { USE_PROFILES: { svg: true } })` をかけていた。SVG プロファイルは HTML 要素を許可しないため、以下が同時に除去される。
253+
254+
- `htmlLabels: true` のノードラベルは `<foreignObject>` 内の `<div class="nodeLabel">`(=HTML)として描画される → `div`/`span` が除去され **テキスト消失**
255+
- ノード/エッジ/テキストの色を与える `<style>` ブロックが **`<style>` 要素ごと除去** → 色・矢印・枠線が既定(黒)に戻る。
256+
- `ADD_TAGS: ['style']` を足しても `USE_PROFILES` 制約下では除去が続く。
257+
258+
**恒久対策**:
259+
260+
1. **図表が開発者直書きの静的定数(外部入力なし)なら、外側 DOMPurify は不要・有害**。撤去して `mermaid.render()` の出力をそのまま描画する。XSS 攻撃面が無く、Mermaid v11 は内部に DOMPurify を同梱しているため自前で無害化される。
261+
2. どうしても外側サニタイズを残すなら `USE_PROFILES: { svg: true }`(HTML を排除するプロファイル)は使わず、`style` タグ+CSS 内容・`foreignObject``div`/`span``style`/`class` 属性を明示許可する。ただし CSS 無害化が脆く mermaid 出力変化に弱いため非推奨。
262+
263+
**テストの落とし穴(再発防止)**:
264+
265+
- モックが `<svg><g>ok</g></svg>` のような自明 SVG だと、`<style>``<foreignObject>` を含まないため壊れるサニタイズ経路を一度も通らず、**テストが緑のまま本番が壊れる**。モックには必ず `<style>` ブロックと `<foreignObject>` 内 HTML ラベルを含めること。
266+
- ただし **happy-dom / jsdom は SVG 内 `<style>` 内容や `foreignObject` の HTML 子要素の `DOMParser`/`XMLSerializer` ラウンドトリップを完全再現できない**。ユニットテストでは「`<style>` 要素が除去されないこと」など機械的に確実な差分のみを検証し、色・ラベル文字列の可視性は実ブラウザ(Playwright 等)の目視で担保する。
267+
268+
詳細な解決記録は `docs/archive/MERMAID_TROUBLESHOOTING.md` を参照。
269+
248270
---
249271

250272
### Mermaid を諦めて HTML/CSS に置き換えるべきケース

CLAUDE.md

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# CLAUDE.md
22

3-
Updated 2026-06-08
3+
Updated 2026-06-10
44

55
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
66

@@ -18,14 +18,14 @@ bun run dev # 開発サーバー起動(HMR あり)
1818
bun run build # 本番ビルド(.next/ へ出力)
1919
bun start # ビルド成果物をプロダクションモードで起動
2020
bun run lint # ESLint 実行
21-
bun test # ユニットテスト (bun test, 149 specs)
21+
bun test # ユニットテスト (bun test, 151 specs)
2222
```
2323

2424
### E2E テスト (Playwright)
2525

2626
```sh
2727
bun run e2e:install # 初回のみ: chromium バイナリ取得 (~150 MB)
28-
bun run e2e #31 ルートのスモーク E2E (webServer 自動起動)
28+
bun run e2e #33 ルートのスモーク E2E (webServer 自動起動)
2929
bun run e2e:ui # Playwright UI モードで対話実行
3030
bun run lhci:autorun # Lighthouse CI 自動実行(本番ビルドの品質予算検証)
3131
bun run e2e:report # 直近の HTML レポートを表示
@@ -137,6 +137,9 @@ Next.js App Router 構成:
137137
- `app/istqb-ct-ste-complete-guide.css` — セキュリティテストエンジニア(CT-STE)ガイド固有スタイル
138138
- `app/istqb-ct-ste-complete-guide/page.tsx` — セキュリティテストエンジニア(CT-STE)ガイドページ
139139
- `app/istqb-ct-ste-complete-guide/NavBar.tsx` — CT-STE ページ固有スティッキーナビ(`'use client'`
140+
- `app/istqb-ct-game-complete-guide/istqb-ct-game-complete-guide.css` — ゲームテスト(CT-GaMe)ガイド固有スタイル
141+
- `app/istqb-ct-game-complete-guide/page.tsx` — ゲームテスト(CT-GaMe)ガイドページ
142+
- `app/istqb-ct-game-complete-guide/NavBar.tsx` — CT-GaMe ページ固有スティッキーナビ(`'use client'`
140143
- `app/istqb-ct-pt-complete-guide.css` — パフォーマンステスト(CT-PT)ガイド固有スタイル
141144
- `app/istqb-ct-pt-complete-guide/page.tsx` — パフォーマンステスト(CT-PT)ガイドページ
142145
- `app/istqb-ct-pt-complete-guide/NavBar.tsx` — CT-PT ページ固有スティッキーナビ(`'use client'``IntersectionObserver` でアクティブリンク制御)
@@ -331,7 +334,7 @@ bun run lint # ESLint エラーなし
331334

332335
```text
333336
コンテキスト:
334-
- 最新 HEAD: `abf37c8`
337+
- 最新 HEAD: `fdc0ea1`
335338
- **新規ガイド追加中**: CT-AuTの移行が完了し、次はCT-GaMeの移行に入ります。
336339
- 合計 32 ルート(ホーム + 31 ガイド)が Next.js で管理されています。
337340
- 各種テスト(ユニット、型チェック、ESLint)はすべて最新の構成に同期され、通過しています。

GEMINI.md

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Project Overview
22

3-
Updated 2026-06-08
3+
Updated 2026-06-10
44

55
This project is a Next.js (App Router) web application designed as a comprehensive learning resource and guide for Quality Assurance (QA) and Software Testing. It provides extensive documentation on modern software testing methodologies (Unit, Functional, Integration, E2E, BDD, Security, Accessibility) as well as AI system testing based on ISTQB CT-AI and CT-GenAI standards.
66

@@ -92,9 +92,11 @@ This project is a Next.js (App Router) web application designed as a comprehensi
9292
- `app/istqb-ct-genai-complete-guide/page.tsx` (GenAIテスト CT-GenAI 完全ガイド、`NavBar.tsx` 付き)
9393
- `app/istqb-ct-mbt-complete-guide/page.tsx` (モデルベーステスト CT-MBT 完全ガイド、`NavBar.tsx` 付き)
9494
- `app/istqb-ct-act-complete-guide/page.tsx` (受入テスト CT-AcT 完全ガイド、`NavBar.tsx` 付き)
95+
- `app/istqb-ct-aut-complete-guide/page.tsx` (自動車ソフトウェアテスター CT-AuT 完全ガイド、`NavBar.tsx` 付き)
9596
- `app/istqb-ct-mat-complete-guide/page.tsx` (モバイルアプリテスト CT-MAT 完全ガイド、`NavBar.tsx` 付き)
9697
- `app/istqb-ct-sec-complete-guide/page.tsx` (セキュリティテストガイド、`NavBar.tsx` 付き)
9798
- `app/istqb-ct-ste-complete-guide/page.tsx` (セキュリティテストエンジニア(CT-STE)ガイド、`NavBar.tsx` 付き)
99+
- `app/istqb-ct-game-complete-guide/page.tsx` (ゲームテスト CT-GaMe 完全ガイド、`NavBar.tsx` 付き)
98100
- `app/istqb-ct-pt-complete-guide/page.tsx` (パフォーマンステスト CT-PT 完全ガイド、`NavBar.tsx` 付き)
99101
- `app/istqb-ct-ut-complete-guide/page.tsx` (ユーザビリティテスト CT-UT 完全ガイド、`NavBar.tsx` 付き)
100102
- `app/istqb-ct-tas-complete-guide/page.tsx` (テスト自動化戦略 CT-TAS 完全ガイド、`NavBar.tsx` 付き)
@@ -163,6 +165,7 @@ This project is a Next.js (App Router) web application designed as a comprehensi
163165
| `istqb-ct-mat-complete-guide.html` | `/istqb-ct-mat-complete-guide` | ✅ NavBar + aria-current あり |
164166
| `istqb-ct-sec-complete-guide.html` | `/istqb-ct-sec-complete-guide` | ✅ NavBar + aria-current あり |
165167
| `istqb-ct-ste-complete-guide.html` | `/istqb-ct-ste-complete-guide` | ✅ NavBar あり |
168+
| `istqb-ct-game-complete-guide.html` | `/istqb-ct-game-complete-guide` | ✅ NavBar + aria-current あり |
166169
| `istqb-ct-tas-complete-guide.html` | `/istqb-ct-tas-complete-guide` | ✅ NavBar あり |
167170
| `istqb-ct-ut-complete-guide.html` | `/istqb-ct-ut-complete-guide` | ✅ NavBar あり |
168171
| `istqb-ctal-atlas-complete-guide.html` | `/istqb-ctal-atlas-complete-guide` | ✅ NavBar あり |
@@ -184,9 +187,23 @@ This project is a Next.js (App Router) web application designed as a comprehensi
184187

185188
| ファイル | 予定ルート | 状態 | 備考 |
186189
|---|---|---|---|
187-
| `istqb-ct-game-complete-guide.md` | `/istqb-ct-game-complete-guide` | ⏳ 未着手 | HTML版追加済み |
188-
| `istqb-ct-gt-complete-guide.md` | `/istqb-ct-gt-complete-guide` | ⏳ 未着手 | HTML版未生成 |
190+
| `istqb-ct-gt-complete-guide.md` | `/istqb-ct-gt-complete-guide` | ⏳ 進行中 | HTML版生成済み、表示調整中 |
189191

190192
## 既知の留保事項
191193

192194
- `istqb-ctfl-at-complete-guide``bdd-testing-guide` / `ai-test-guide` は html-archive/ に元 HTML が存在しない(最初から Next.js で作成)
195+
- `istqb-ct-aut-complete-guide.html` はリポジトリ内に存在しません(not found)。
196+
- `istqb-ct-gt-complete-guide.html` は生成済みです。表示崩れや Mermaid の調整を実施中。
197+
198+
## 次回セッションでの再開プロンプト
199+
200+
```text
201+
コンテキスト:
202+
- 最新 HEAD: `fdc0ea1`
203+
- **新規ガイド追加中**: 既存の全 HTML 移行は完了しましたが、新たに CT-AuT, CT-GaMe, CT-GT ガイドが追加されました。
204+
- 合計 33 ルート(ホーム + 32 ガイド予定)が管理されています。
205+
- 各種テスト(ユニット、型チェック、ESLint)はすべて最新の構成に同期され、通過しています。
206+
207+
【指示】
208+
CT-GaMeガイドの移行が完了し、33ルートになりました。次のタスク(CT-GTガイドの移行、またはその他のタスク)について指示を仰ぎます。
209+
```

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# ソフトウェアテスト実施概要
22

3-
最終更新日: 2026-06-08
3+
最終更新日: 2026-06-10
44

55
本プロジェクトにおいて、システムの品質(正しく動くか・快適に使えるか)を担保するために実施するテスト項目の概要と目的を以下に定義します。
66

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
'use client';
2+
3+
import { useEffect, useState } from 'react';
4+
5+
/**
6+
* Renders a sticky in-page navigation bar that highlights the anchor corresponding to the section currently visible in the viewport.
7+
*
8+
* The component tracks all `section[id]` elements and applies the `active` CSS class to the matching link so the navigation reflects the user's scroll position.
9+
*
10+
* @returns The JSX element for the sticky navigation with links to table of contents, chapters, exam, and references; the link for the currently visible section will include the `active` class.
11+
*/
12+
export default function NavBar() {
13+
const [activeId, setActiveId] = useState('');
14+
15+
useEffect(() => {
16+
const observer = new IntersectionObserver(
17+
(entries) => {
18+
const visibleEntries = entries.filter((entry) => entry.isIntersecting);
19+
if (visibleEntries.length > 0) {
20+
const bestEntry = visibleEntries.reduce((prev, curr) =>
21+
curr.intersectionRatio > prev.intersectionRatio ? curr : prev
22+
);
23+
setActiveId(bestEntry.target.id);
24+
}
25+
},
26+
{ rootMargin: '-60px 0px -80% 0px' }
27+
);
28+
29+
const sections = document.querySelectorAll('section[id]');
30+
sections.forEach((section) => observer.observe(section));
31+
32+
return () => observer.disconnect();
33+
}, []);
34+
35+
return (
36+
<nav className="sticky-nav sticky z-40" style={{ top: 'calc(60px + var(--disclaimer-height, 0px))' }} aria-label="CT-GaMe セクションナビゲーション">
37+
<div className="nav-inner">
38+
<span className="nav-logo">&#127918; CT-GaMe</span>
39+
<a className={`nav-link ${activeId === 'overview' ? 'active' : ''}`} href="#overview" aria-current={activeId === 'overview' ? 'location' : undefined}>概要</a>
40+
<a className={`nav-link ${activeId === 'ch1' ? 'active' : ''}`} href="#ch1" aria-current={activeId === 'ch1' ? 'location' : undefined}>Ch.1</a>
41+
<a className={`nav-link ${activeId === 'ch2' ? 'active' : ''}`} href="#ch2" aria-current={activeId === 'ch2' ? 'location' : undefined}>Ch.2 メカニクス</a>
42+
<a className={`nav-link ${activeId === 'ch3' ? 'active' : ''}`} href="#ch3" aria-current={activeId === 'ch3' ? 'location' : undefined}>Ch.3 グラフィックス</a>
43+
<a className={`nav-link ${activeId === 'ch4' ? 'active' : ''}`} href="#ch4" aria-current={activeId === 'ch4' ? 'location' : undefined}>Ch.4 サウンド</a>
44+
<a className={`nav-link ${activeId === 'ch5' ? 'active' : ''}`} href="#ch5" aria-current={activeId === 'ch5' ? 'location' : undefined}>Ch.5 コントローラー</a>
45+
<a className={`nav-link ${activeId === 'ch6' ? 'active' : ''}`} href="#ch6" aria-current={activeId === 'ch6' ? 'location' : undefined}>Ch.6 レベル</a>
46+
<a className={`nav-link ${activeId === 'ch7' ? 'active' : ''}`} href="#ch7" aria-current={activeId === 'ch7' ? 'location' : undefined}>Ch.7 ローカライズ</a>
47+
<a className={`nav-link ${activeId === 'tools' ? 'active' : ''}`} href="#tools" aria-current={activeId === 'tools' ? 'location' : undefined}>ツール</a>
48+
<a className={`nav-link ${activeId === 'exam' ? 'active' : ''}`} href="#exam" aria-current={activeId === 'exam' ? 'location' : undefined}>試験対策</a>
49+
<a className={`nav-link ${activeId === 'refs' ? 'active' : ''}`} href="#refs" aria-current={activeId === 'refs' ? 'location' : undefined}>参照URL</a>
50+
</div>
51+
</nav>
52+
);
53+
}

0 commit comments

Comments
 (0)