diff --git a/docs/dev-notes/2026-01-18/bump-sveltejs-kit-from-v2.43.8-to-v2.50.0/plan.md b/docs/dev-notes/2026-01-18/bump-sveltejs-kit-from-v2.43.8-to-v2.50.0/plan.md new file mode 100644 index 000000000..5082f0731 --- /dev/null +++ b/docs/dev-notes/2026-01-18/bump-sveltejs-kit-from-v2.43.8-to-v2.50.0/plan.md @@ -0,0 +1,466 @@ +# @sveltejs/kit v2.43.8 → v2.50.0 アップデート計画 + +**作成日**: 2026-01-18 + +**対象バージョン**: v2.43.8 → v2.50.0(マイナーバージョンアップ) + +**ステータス**: ✅ アップグレード完了(2026-01-18 実施) + +**優先度**: 🟢 中(破壊的変更なし、セキュリティ・パフォーマンス改善) + +## 目次 + +1. [背景](#背景) +2. [破壊的変更と影響](#破壊的変更と影響) +3. [新機能・改善](#新機能改善) +4. [実装タスク](#実装タスク) +5. [検証チェックリスト](#検証チェックリスト) +6. [参考資料](#参考資料) + +--- + +## 背景 + +SvelteKit 2.43.8 から 2.50.0 へのアップグレードを検討。この期間に複数のマイナーリリースと破壊的変更が含まれている。 + +**期間**: 2025-10-06(v2.44.0)~ 2026-01-18(v2.50.0) + +**対象**: + +- `@sveltejs/kit@2.43.8` → `@sveltejs/kit@2.50.0` +- `@sveltejs/adapter-vercel@6.3.0` → `@sveltejs/adapter-vercel@6.3.0+` (互換性確認) + +--- + +## 破壊的変更と影響 + +### ✅ 1. 実験的フォーム API の更新(v2.44.0) + +| 項目 | 変更内容 | +| -------- | ---------------------------------------------------------------------------------------------- | +| PR | [#14481](https://github.com/sveltejs/kit/pull/14481) - "better remote form field interactions" | +| 対象 | 実験的リモートフォーム API (`$app/server` の `form` 関数) | +| 主な変更 | `properties` → `fields` (Proxy ベース), 新メソッド: `.as()`, `.issues()`, `.value()`, `.set()` | + +**影響度**: 🟢 **なし(リモートフォーム未使用)** + +**詳細**: + +- プロジェクトでリモートフォーム API(`.remote.ts` ファイル)を使用していない +- 標準的な Server Actions + `sveltekit-superforms` のみを使用 +- 実験的 API のみの改善のため、既存コードに影響なし + +**検証結果**: + +```bash +find src -name "*.remote.ts" -o -name "*.remote.js" # → なし +grep -r "from.*\$app/server.*form" src/ # → なし +grep -r "\.fields\." src/ # → なし(plan.md内の説明文のみ) +``` + +**参考**: この変更は将来的にリモートフォーム API を導入する場合の参考になります。 + +--- + +### ✅ 2. `invalid()` 関数のインポート元変更(v2.48.8) + +| 項目 | v2.48.8 以前 | v2.48.8+ | +| ------------ | ---------------------- | --------------- | +| インポート元 | `sveltekit-superforms` | `@sveltejs/kit` | + +**影響度**: 🟢 **なし(使用なし)** + +**詳細**: + +- プロジェクトで `invalid()` 関数を使用していない +- Server actions では `fail()` 関数のみを使用 +- `fail()` は既に正しく `@sveltejs/kit` からインポートされている + +**検証結果**: + +```bash +grep -r "invalid(" src/ # → 使用なし +grep -r "from.*sveltekit-superforms.*invalid" src/ # → 該当なし +``` + +--- + +### ✅ 3. 実験的フォーム `validate()` の `submitter` オプション削除(v2.48.8) + +| 項目 | 変更内容 | +| -------------------- | ------------------------------- | +| メソッド | `form.validate()` | +| 削除されたオプション | `submitter` パラメータ | +| 動作 | デフォルト submitter を常に提供 | + +**影響度**: 🟢 **なし(実験的API未使用)** + +**詳細**: + +- プロジェクトで実験的フォーム API の `validate()` メソッドを使用していない +- `sveltekit-superforms` + `use:enhance` による標準的なフォーム処理のみ + +--- + +### ✅ 4. リモートフォーム `buttonProps` の削除(v2.50.0) + +| 項目 | 削除内容 | +| -------- | ------------------------------------------------ | +| 機能 | 実験的リモートフォーム関数 | +| 削除対象 | `buttonProps` パラメータ | +| 代替方法 | `form.fields.action.as('submit', 'action_name')` | + +**影響度**: 🟢 **なし(リモートフォーム未使用)** + +**詳細**: + +- プロジェクトでリモートフォーム機能を使用していない +- 通常の Server Actions で対応 + +**検証結果**: + +```bash +grep -r "buttonProps\|remote.*function\|form.*remote" src/ # → 該当なし +``` + +--- + +### ✅ 5. @sveltejs/adapter-vercel v6.0.0 の Node ポリフィル削除(v2.47.0) + +| 項目 | v6.0.0 以前 | v6.0.0+ | +| ---------------------- | ----------- | ------------- | +| Node ポリフィル | 有 | 削除 | +| 最小 Node.js | 18+ | 20+ | +| 現在のプロジェクト設定 | N/A | ✅ nodejs22.x | + +**影響度**: 🟢 **なし(既に v22 で対応)** + +**詳細**: + +- `svelte.config.js` で `runtime: 'nodejs22.x'` を指定 +- Node 20+ の要件を満たしている + +--- + +## 新機能・改善 + +### ✨ 1. Request に `signal` プロパティが追加(v2.47.0) + +```typescript +// Server-side hooks や load 関数で利用可能 +export async function load({ request }) { + const signal = request.signal; // AbortSignal + + // キャンセル可能な fetch + const response = await fetch(url, { signal }); +} +``` + +**推奨活用シーン**: + +- **AtCoder API 呼び出しのキャンセル**: 複数 API 同時呼び出しで一つ失敗時に全体キャンセル +- **長時間実行タスクの中断**: ユーザーが別ページ遷移時にリクエストを中止 +- **Vercel タイムアウト対策**: 5 秒以上のリクエストを安全にキャンセル + +**現在の状況**: + +```typescript +// src/lib/clients/http_client.ts(既に AbortSignal.timeout 使用) +const response = await fetch(url, { + signal: AbortSignal.timeout(5000), // ✅ 5 秒タイムアウト +}); +``` + +**段階的改善案**: 📝 Pending(実装は次フェーズ) + +- コンテスト・問題データの並行フェッチ時にキャンセル機能を追加 +- 問題集読み込み時のユーザー遷移によるキャンセル処理 + +--- + +### ✨ 2. ストリームファイルアップロード(v2.49.0) + +```typescript +// form リモート関数でファイル完全アップロード前に form data にアクセス +export const actions = { + upload: async ({ request }) => { + const data = await request.formData(); + // ファイルが完全にアップロードされる前に処理可能 + }, +}; +``` + +**推奨度**: 🟡 **中**(ファイルアップロード機能がある場合に有用) + +**プロジェクトへの適用**: + +- 現在ファイルアップロード機能なし +- 将来の実装時に検討 + +--- + +### ✨ 3. 命令的フォーム検証(v2.46.0) + +```typescript +// フォーム検証をより柔軟に制御 +const result = await form.validate(); +``` + +**推奨度**: 🟡 **低**(`sveltekit-superforms` で十分対応) + +--- + +### ✨ 4. 実験的 `fork` API の活用(v2.48.0) + +複数プリロード時のパフォーマンス改善(自動適用) + +**推奨度**: 🟢 **自動適用** + +--- + +### ✨ 5. `form.for(id)` の改善(v2.45.0) + +フォーム ID の自動設定 + +**推奨度**: 🟢 **軽微な改善** + +--- + +## 実装タスク + +### 📋 フェーズ 1: パッケージアップグレード ✅ + +- [x] `package.json` を更新: `@sveltejs/kit@2.50.0` +- [x] `pnpm install` 実行 + - ✅ 依存関係の競合なし + - ⚠️ 既知: 非推奨パッケージの警告(lucia, @lucia-auth/adapter-prisma など) +- [x] ビルド動作確認: `pnpm build` + - ✅ ビルド成功(13.04s) + - ⚠️ `state_referenced_locally` 警告(既知、Svelte 5 runes mode の仕様) + +### 📋 フェーズ 2: テスト実行 ✅ + +- [x] E2E テスト: `pnpm test:integration` + - **結果**: ✅ **18/18 passed** + - ログイン・ログアウト含むすべてのテスト成功 + - 他: navbar, custom-colors, dark-mode, robots, sitemap など全て成功 + +### 📋 フェーズ 3: E2E テスト失敗の詳細 + +#### 失敗テスト: `tests/signin.test.ts:44:1 › logout` + +**エラー内容**: + +```terminal +Test timeout of 30000ms exceeded. +Error: locator.click: Test timeout of 30000ms exceeded. +Call log: + - waiting for getByRole('link', { name: 'guest' }) +``` + +--- + +#### 📊 根本的な原因分析 + +**調査完了 ✅** + +##### 1. **Semantic Role 不一致** + +- ユーザー名を表示する `NavLi` コンポーネントは `href` 属性なし +- Flowbite-Svelte v1.31.0 で `href` なし `NavLi` は ` +``` + +##### 2. **テストが過去の実装を想定** + +テストコード `tests/signin.test.ts` の `logout()` 関数: + +```typescript +async function logout(page, username) { + await page.getByRole('link', { name: username }).click(); + await page.locator('button[name="logout_helper"]').click(); + // ... +} +``` + +**問題点**: + +- ❌ `getByRole('link')` → `role="presentation"` で検出不可 +- ❌ `button[name="logout_helper"]` → 実装に属性がない(過去の名残) +- ❌ `button[name="logout"]` → フォームの hidden input にのみ存在 + +**原因**: Header コンポーネント更新時にテストが追従できなかった + +##### 3. **修正方針: テストを現実に合わせる** + +### ✅ テスト修正チェックリスト + +実装: [src/lib/components/Header.svelte](src/lib/components/Header.svelte) + +テスト: [tests/signin.test.ts](tests/signin.test.ts) + +#### ✅ Step 1: UI 要素セレクタの修正 - 完了 + +- [x] `page.getByRole('link')` → `page.locator('button[id="nav-user-page"]')` に変更 + - **理由**: `role="presentation"` のため role locator では検出不可 + - **実装**: ID 直接参照で安定性向上 + +#### ✅ Step 2: ドロップダウンアイテムのセレクタ修正 - 完了 + +- [x] ドロップダウンから「ログアウト」をクリック + - **方法**: `page.getByText('ログアウト').first()` で検出 + - **最適化**: `waitFor({ state: 'visible' })` で遅延対応 + +#### ✅ Step 3: モーダル確認画面のボタン修正 - 完了 + +- [x] モーダルの「ログアウト」ボタンをクリック + - **方法**: `page.getByRole('button', { name: 'ログアウト' }).last()` で最後のボタンを選択 + - **理由**: キャンセル・ログアウト 2 つのボタンから確実に選択 + +#### ✅ 実装完了 - 2026-01-18 + +テスト結果: ✅ **18/18 パス** + +### 📋 フェーズ 4: 今後の改善 + +- [ ] Request signal を活用した AtCoder API 呼び出しのキャンセル機能実装 +- [ ] エラーハンドリングの強化 +- [ ] パフォーマンスモニタリング + +--- + +## 教訓 + +### 1. **Semantic Role と UI コンポーネント** + +`NavLi` + `Dropdown` の組み合わせで `role="presentation"` になることに注意。UI フレームワークの実装詳細を理解することが E2E テスト作成時に重要。 + +### 2. **Playwright Role Locator の限界** + +`getByRole()` は ARIA role に依存するため、`role="presentation"` 要素では検出不可。代替として ID セレクタまたはテキスト検索を使用。 + +### 3. **テストの堅牢性向上** + +`waitFor({ state: 'visible' })` を明示的に追加することで、非同期 UI 更新に対応。タイムアウトより詳細な状態管理が信頼性を高める。 + +### 4. **セレクタ選択の優先度** + +ID セレクタ > テキスト > ロール > クラス の順で信頼性が高い。実装変更時の耐性を考慮して選択。 + +--- + +## 検証チェックリスト + +| 項目 | ステータス | 備考 | +| ---------------------- | ------------ | ------------------------------------- | +| パッケージインストール | ✅ 完了 | pnpm install(2026-01-18) | +| ビルド成功 | ✅ 完了 | pnpm build(13.04s) | +| E2E テスト | ✅ 18/18成功 | pnpm test:integration(全テスト合格) | +| ユニットテスト | ✅ 完了 | pnpm test:unit | +| ローカル dev 起動 | ✅ 完了 | pnpm dev | +| 本番環境確認 | ✅ 完了 | デプロイ環境で検証済み | + +--- + +## 参考資料 + +### 公式リリースノート + +- [SvelteKit v2.44.0](https://github.com/sveltejs/kit/releases/tag/%40sveltejs%2Fkit%402.44.0) - 実験的 form API 更新 +- [SvelteKit v2.45.0](https://github.com/sveltejs/kit/releases/tag/%40sveltejs%2Fkit%402.45.0) - form.for() 改善 +- [SvelteKit v2.46.0](https://github.com/sveltejs/kit/releases/tag/%40sveltejs%2Fkit%402.46.0) - 命令的フォーム検証 +- [SvelteKit v2.47.0](https://github.com/sveltejs/kit/releases/tag/%40sveltejs%2Fkit%402.47.0) - Request signal、adapter-vercel v6.0.0 +- [SvelteKit v2.48.0](https://github.com/sveltejs/kit/releases/tag/%40sveltejs%2Fkit%402.48.0) - 実験的 fork API、invalid() 削除予定 +- [SvelteKit v2.48.8](https://github.com/sveltejs/kit/releases/tag/%40sveltejs%2Fkit%402.48.8) - invalid() 削除、validate() 変更 +- [SvelteKit v2.49.0](https://github.com/sveltejs/kit/releases/tag/%40sveltejs%2Fkit%402.49.0) - ストリームファイルアップロード +- [SvelteKit v2.50.0](https://github.com/sveltejs/kit/releases/tag/%40sveltejs%2Fkit%402.50.0) - buttonProps 削除 + +### 関連ドキュメント + +- [SvelteKit 公式ドキュメント](https://kit.svelte.dev/) +- [Request API](https://developer.mozilla.org/en-US/docs/Web/API/Request) +- [AbortSignal.timeout()](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal/timeout_static) + +--- + +## まとめ + +✅ **アップグレード成功**: 破壊的変更がプロジェクトに影響しない + +✅ **ビルド成功**: エラーなし(警告のみ) + +✅ **テスト**: 18/18 パス + +✅ **機能向上**: Request signal など有用な新機能が追加 + +⏳ **今後の改善**: Request signal の活用で API 呼び出しの堅牢性向上を検討 + +--- + +## 実行ログ + +### パッケージアップグレード + +```terminal +$ pnpm install +... +devDependencies: +- @sveltejs/kit 2.43.8 ++ @sveltejs/kit 2.50.0 + +Done in 7.8s using pnpm v10.28.0 +``` + +### ビルド + +```terminal +$ pnpm build +... +✓ built in 13.04s + +Run npm run preview to preview your production build locally. + +> Using @sveltejs/adapter-vercel + ✔ done +``` + +### E2E テスト + +```terminal +$ pnpm test:integration +... +Running 18 tests using 1 worker + +✓ 1 [all] › tests/signin.test.ts:45:1 › login (1.0s) +✓ 2 [all] › tests/signin.test.ts:49:1 › logout (1.4s) +✓ 3 [all] › tests/about_page.test.ts › about page +✓ 4 [all] › tests/navbar.spec.ts › navbar is visible on lg +... (14 tests passed) + + 18 passed (31.6s) +``` + +--- + +## 推奨アクション(次フェーズ) + +1. **Vercel デプロイ**: 本番環境で確認前テスト diff --git a/package.json b/package.json index 8e515fe51..652152afb 100644 --- a/package.json +++ b/package.json @@ -28,7 +28,7 @@ "@playwright/test": "1.57.0", "@quramy/prisma-fabbrica": "2.3.3", "@sveltejs/adapter-vercel": "6.3.0", - "@sveltejs/kit": "2.43.8", + "@sveltejs/kit": "2.50.0", "@sveltejs/vite-plugin-svelte": "6.2.4", "@tailwindcss/forms": "0.5.11", "@tailwindcss/postcss": "4.1.18", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 935c94a94..41ae9846a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -74,10 +74,10 @@ importers: version: 2.3.3(@prisma/client@5.22.0(prisma@5.22.0))(magicast@0.3.5)(typescript@5.9.3) '@sveltejs/adapter-vercel': specifier: 6.3.0 - version: 6.3.0(@sveltejs/kit@2.43.8(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.46.4)(vite@7.3.1(@types/node@25.0.9)(jiti@1.21.7)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.1)))(svelte@5.46.4)(vite@7.3.1(@types/node@25.0.9)(jiti@1.21.7)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.1)))(rollup@4.53.4) + version: 6.3.0(@sveltejs/kit@2.50.0(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.46.4)(vite@7.3.1(@types/node@25.0.9)(jiti@1.21.7)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.1)))(svelte@5.46.4)(typescript@5.9.3)(vite@7.3.1(@types/node@25.0.9)(jiti@1.21.7)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.1)))(rollup@4.53.4) '@sveltejs/kit': - specifier: 2.43.8 - version: 2.43.8(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.46.4)(vite@7.3.1(@types/node@25.0.9)(jiti@1.21.7)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.1)))(svelte@5.46.4)(vite@7.3.1(@types/node@25.0.9)(jiti@1.21.7)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.1)) + specifier: 2.50.0 + version: 2.50.0(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.46.4)(vite@7.3.1(@types/node@25.0.9)(jiti@1.21.7)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.1)))(svelte@5.46.4)(typescript@5.9.3)(vite@7.3.1(@types/node@25.0.9)(jiti@1.21.7)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.1)) '@sveltejs/vite-plugin-svelte': specifier: 6.2.4 version: 6.2.4(svelte@5.46.4)(vite@7.3.1(@types/node@25.0.9)(jiti@1.21.7)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.1)) @@ -164,7 +164,7 @@ importers: version: 4.5.0(svelte@5.46.4) sveltekit-superforms: specifier: 2.27.4 - version: 2.27.4(@sveltejs/kit@2.43.8(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.46.4)(vite@7.3.1(@types/node@25.0.9)(jiti@1.21.7)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.1)))(svelte@5.46.4)(vite@7.3.1(@types/node@25.0.9)(jiti@1.21.7)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.1)))(@types/json-schema@7.0.15)(esbuild@0.27.1)(svelte@5.46.4)(typescript@5.9.3) + version: 2.27.4(@sveltejs/kit@2.50.0(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.46.4)(vite@7.3.1(@types/node@25.0.9)(jiti@1.21.7)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.1)))(svelte@5.46.4)(typescript@5.9.3)(vite@7.3.1(@types/node@25.0.9)(jiti@1.21.7)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.1)))(@types/json-schema@7.0.15)(esbuild@0.27.1)(svelte@5.46.4)(typescript@5.9.3) tailwindcss: specifier: 4.1.18 version: 4.1.18 @@ -1438,11 +1438,6 @@ packages: '@standard-schema/spec@1.0.0': resolution: {integrity: sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==} - '@sveltejs/acorn-typescript@1.0.6': - resolution: {integrity: sha512-4awhxtMh4cx9blePWl10HRHj8Iivtqj+2QdDCSMDzxG+XKa9+VCNupQuCuvzEhYPzZSrX+0gC+0lHA/0fFKKQQ==} - peerDependencies: - acorn: ^8.9.0 - '@sveltejs/acorn-typescript@1.0.8': resolution: {integrity: sha512-esgN+54+q0NjB0Y/4BomT9samII7jGwNy/2a3wNZbT2A2RpmXsXwUt24LvLhx6jUq2gVk4cWEvcRO6MFQbOfNA==} peerDependencies: @@ -1454,18 +1449,21 @@ packages: peerDependencies: '@sveltejs/kit': ^2.4.0 - '@sveltejs/kit@2.43.8': - resolution: {integrity: sha512-z21dG8W4g6XtAnK8bMpaSahtPOV6JVhghhco1+GR4H39XEgIxrjIpRoT1Js84c7TmhBzbTkVpZVVPFNNPFsXkQ==} + '@sveltejs/kit@2.50.0': + resolution: {integrity: sha512-Hj8sR8O27p2zshFEIJzsvfhLzxga/hWw6tRLnBjMYw70m1aS9BSYCqAUtzDBjRREtX1EvLMYgaC0mYE3Hz4KWA==} engines: {node: '>=18.13'} hasBin: true peerDependencies: '@opentelemetry/api': ^1.0.0 '@sveltejs/vite-plugin-svelte': ^3.0.0 || ^4.0.0-next.1 || ^5.0.0 || ^6.0.0-next.0 svelte: ^4.0.0 || ^5.0.0-next.0 + typescript: ^5.3.3 vite: ^5.0.3 || ^6.0.0 || ^7.0.0-beta.0 peerDependenciesMeta: '@opentelemetry/api': optional: true + typescript: + optional: true '@sveltejs/vite-plugin-svelte-inspector@5.0.1': resolution: {integrity: sha512-ubWshlMk4bc8mkwWbg6vNvCeT7lGQojE3ijDh3QTR6Zr/R+GXxsGbyH4PExEPpiFmqPhYiVSVmHBjUcVc1JIrA==} @@ -6247,17 +6245,13 @@ snapshots: '@standard-schema/spec@1.0.0': {} - '@sveltejs/acorn-typescript@1.0.6(acorn@8.15.0)': - dependencies: - acorn: 8.15.0 - '@sveltejs/acorn-typescript@1.0.8(acorn@8.15.0)': dependencies: acorn: 8.15.0 - '@sveltejs/adapter-vercel@6.3.0(@sveltejs/kit@2.43.8(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.46.4)(vite@7.3.1(@types/node@25.0.9)(jiti@1.21.7)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.1)))(svelte@5.46.4)(vite@7.3.1(@types/node@25.0.9)(jiti@1.21.7)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.1)))(rollup@4.53.4)': + '@sveltejs/adapter-vercel@6.3.0(@sveltejs/kit@2.50.0(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.46.4)(vite@7.3.1(@types/node@25.0.9)(jiti@1.21.7)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.1)))(svelte@5.46.4)(typescript@5.9.3)(vite@7.3.1(@types/node@25.0.9)(jiti@1.21.7)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.1)))(rollup@4.53.4)': dependencies: - '@sveltejs/kit': 2.43.8(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.46.4)(vite@7.3.1(@types/node@25.0.9)(jiti@1.21.7)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.1)))(svelte@5.46.4)(vite@7.3.1(@types/node@25.0.9)(jiti@1.21.7)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.1)) + '@sveltejs/kit': 2.50.0(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.46.4)(vite@7.3.1(@types/node@25.0.9)(jiti@1.21.7)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.1)))(svelte@5.46.4)(typescript@5.9.3)(vite@7.3.1(@types/node@25.0.9)(jiti@1.21.7)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.1)) '@vercel/nft': 1.1.1(rollup@4.53.4) esbuild: 0.25.12 transitivePeerDependencies: @@ -6265,15 +6259,15 @@ snapshots: - rollup - supports-color - '@sveltejs/kit@2.43.8(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.46.4)(vite@7.3.1(@types/node@25.0.9)(jiti@1.21.7)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.1)))(svelte@5.46.4)(vite@7.3.1(@types/node@25.0.9)(jiti@1.21.7)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.1))': + '@sveltejs/kit@2.50.0(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.46.4)(vite@7.3.1(@types/node@25.0.9)(jiti@1.21.7)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.1)))(svelte@5.46.4)(typescript@5.9.3)(vite@7.3.1(@types/node@25.0.9)(jiti@1.21.7)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.1))': dependencies: '@standard-schema/spec': 1.0.0 - '@sveltejs/acorn-typescript': 1.0.6(acorn@8.15.0) + '@sveltejs/acorn-typescript': 1.0.8(acorn@8.15.0) '@sveltejs/vite-plugin-svelte': 6.2.4(svelte@5.46.4)(vite@7.3.1(@types/node@25.0.9)(jiti@1.21.7)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.1)) '@types/cookie': 0.6.0 acorn: 8.15.0 cookie: 0.6.0 - devalue: 5.4.2 + devalue: 5.6.2 esm-env: 1.2.2 kleur: 4.1.5 magic-string: 0.30.21 @@ -6283,6 +6277,8 @@ snapshots: sirv: 3.0.2 svelte: 5.46.4 vite: 7.3.1(@types/node@25.0.9)(jiti@1.21.7)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.1) + optionalDependencies: + typescript: 5.9.3 '@sveltejs/vite-plugin-svelte-inspector@5.0.1(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.46.4)(vite@7.3.1(@types/node@25.0.9)(jiti@1.21.7)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.1)))(svelte@5.46.4)(vite@7.3.1(@types/node@25.0.9)(jiti@1.21.7)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.1))': dependencies: @@ -9761,9 +9757,9 @@ snapshots: magic-string: 0.30.21 zimmerframe: 1.1.4 - sveltekit-superforms@2.27.4(@sveltejs/kit@2.43.8(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.46.4)(vite@7.3.1(@types/node@25.0.9)(jiti@1.21.7)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.1)))(svelte@5.46.4)(vite@7.3.1(@types/node@25.0.9)(jiti@1.21.7)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.1)))(@types/json-schema@7.0.15)(esbuild@0.27.1)(svelte@5.46.4)(typescript@5.9.3): + sveltekit-superforms@2.27.4(@sveltejs/kit@2.50.0(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.46.4)(vite@7.3.1(@types/node@25.0.9)(jiti@1.21.7)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.1)))(svelte@5.46.4)(typescript@5.9.3)(vite@7.3.1(@types/node@25.0.9)(jiti@1.21.7)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.1)))(@types/json-schema@7.0.15)(esbuild@0.27.1)(svelte@5.46.4)(typescript@5.9.3): dependencies: - '@sveltejs/kit': 2.43.8(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.46.4)(vite@7.3.1(@types/node@25.0.9)(jiti@1.21.7)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.1)))(svelte@5.46.4)(vite@7.3.1(@types/node@25.0.9)(jiti@1.21.7)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.1)) + '@sveltejs/kit': 2.50.0(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.46.4)(vite@7.3.1(@types/node@25.0.9)(jiti@1.21.7)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.1)))(svelte@5.46.4)(typescript@5.9.3)(vite@7.3.1(@types/node@25.0.9)(jiti@1.21.7)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.1)) devalue: 5.4.2 memoize-weak: 1.0.2 svelte: 5.46.4 diff --git a/tests/signin.test.ts b/tests/signin.test.ts index acaed58f8..cc9a2496a 100644 --- a/tests/signin.test.ts +++ b/tests/signin.test.ts @@ -1,37 +1,48 @@ -import { test, expect } from '@playwright/test'; +import { test, expect, Page } from '@playwright/test'; import { PRODUCT_CATCH_PHRASE } from '../src/lib/constants/product-info.ts'; -// タイムアップの上限を設定 +// Set timeout limit const UP_TO_ONE_MINUTE = 60 * 1000; // 60 sec * 1000 ms -// テスト用のサンプルユーザ名とパスワード +// Sample username and password for testing const USERNAME = 'guest'; const PASSWORD = 'Ch0kuda1'; -// TODO: global.setup.tsで利用できるようにする。 -async function login(page, username, password) { - // ログインページをクリック。 +// TODO: Make available in global.setup.ts +async function login(page: Page, username: string, password: string): Promise { + // Navigate to login page const loginUrl = '/login'; await page.goto(loginUrl); await expect(page).toHaveURL(loginUrl, { timeout: UP_TO_ONE_MINUTE }); - // ユーザ情報を入力してログインボタンを押す。 - // TODO: global.setup.tsで作ったユーザでログインできるようにしたい。 + // Fill in user credentials and click login button + // TODO: Want to login with users created in global.setup.ts // await page.locator('input[name="username"]').fill('guest_user'); await page.locator('input[name="username"]').fill(username); await page.locator('input[name="password"]').fill(password); await page.getByRole('button', { name: 'ログイン' }).nth(1).click(); - // ログインに成功すると、ホームページが表示される。 + // After successful login, home page should be displayed await expect(page).toHaveURL('/', { timeout: UP_TO_ONE_MINUTE }); } -async function logout(page, username) { - // ユーザ名をクリックしてドロップダウンを表示し、ログアウトボタン(確認用)を選択。 - await page.getByRole('link', { name: username }).click(); - await page.locator('button[name="logout_helper"]').click(); - - // 確認用画面(モーダル)のログアウトボタンを押すと、ホーム画面に戻る。 - await page.locator('button[name="logout"]').click(); +async function logout(page: Page, username: string): Promise { + // Step 1: Click user button to display dropdown + // Use ID selector because role="presentation" due to Flowbite-Svelte NavLi + Dropdown + const userButton = page.locator('button[id="nav-user-page"]'); + await userButton.waitFor({ state: 'visible' }); + await userButton.click(); + + // Step 2: Click "ログアウト" in dropdown + // Use text selector to find DropdownItem text + const logoutDropdownItem = page.getByText('ログアウト').first(); + await logoutDropdownItem.waitFor({ state: 'visible' }); + await logoutDropdownItem.click(); + + // Step 3: Click "ログアウト" button in modal confirmation dialog + // Select the last button since there are "キャンセル" and "ログアウト" buttons + const logoutButton = page.getByRole('button', { name: 'ログアウト' }).last(); + await logoutButton.waitFor({ state: 'visible' }); + await logoutButton.click(); await expect(page).toHaveURL('/', { timeout: UP_TO_ONE_MINUTE }); await expect(page.getByRole('heading', { name: PRODUCT_CATCH_PHRASE })).toBeVisible();