Conversation
- +page.server.ts: delete action now redirects to /login after successful deletion
- vote_actions.ts: separate locals.user existence check (500) from is_validated
check (403) to distinguish missing user data from unverified state
- e2e/votes.spec.ts: remove redundant voteForm.toBeVisible() (guaranteed by or() wait)
- .claude/rules/testing.md: remove duplicate Prisma-mock note (appeared twice)
- .claude/rules/testing-e2e.md: simplify toBeChecked({checked:true}) -> toBeChecked();
collapse double .locator() chain into single selector '.container nav'
- .claude/rules/sveltekit.md: fix naming inconsistency in load() example (both Bad
and Good now use camelCase-based field names, consistent with Prisma model)
Skip: locals.user?.is_validated -> session?.user.is_validated in page.server.ts files
(session.user type only has userId/username/role; is_validated lives on locals.user)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
📝 Walkthrough概要ドキュメント整備と実装の軽微な修正を複合。SvelteKitガイドではレガシーなsnake_caseを「悪い例」に更新、テストガイドでは検証方法を洗練。e2e テストと実装では冗長な処理を削除し、ユーザー削除時のリダイレクト動作を明確化、投票確認時のエラーハンドリングを厳密化。 変更内容
推定レビュー工数🎯 3 (Moderate) | ⏱️ ~20 分 詩
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In @.claude/rules/testing-e2e.md:
- Around line 85-87: The guide's toggle assertion is one-sided; update the check
to mirror the implementation pattern used in e2e/workbooks_list.spec.ts by
asserting the toggled state with the previous value inverted: after await
toggleLabel.click() replace the unconditional expect(toggleInput).toBeChecked()
with an assertion that uses the options form to assert the new checked state
(e.g., toBeChecked({ checked: !isChecked })) and ensure the surrounding text
references the isChecked variable and the toggleInput/toggleLabel flow so the
guide matches the implementation.
In `@src/routes/problems/`+page.svelte:
- Line 23: The code is mutating the incoming props by calling sort() on
data.taskResults; change the expression that produces taskResults so it sorts a
copy instead (e.g., clone data.taskResults via slice() or spread before calling
sort) so compareByContestIdAndTaskId is used on a non-mutating copy when
creating const taskResults: TaskResults.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: b03635b1-2f8d-4926-8800-dcf35b3be308
📒 Files selected for processing (8)
.claude/rules/sveltekit.md.claude/rules/testing-e2e.md.claude/rules/testing.md.env.examplee2e/votes.spec.tssrc/features/votes/actions/vote_actions.tssrc/routes/problems/+page.sveltesrc/routes/users/edit/+page.server.ts
💤 Files with no reviewable changes (1)
- e2e/votes.spec.ts
| await toggleLabel.click(); | ||
| await expect(toggleInput).toBeChecked({ checked: true }); | ||
| await expect(toggleInput).toBeChecked(); | ||
| ``` |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
Playwright公式ドキュメントで、expect(locator).toBeChecked() は options.checked を使って true/false の両状態検証をサポートしていますか?推奨されるトグル反転アサーション例も確認してください。
💡 Result:
はい、Playwright公式ドキュメントによると、expect(locator).toBeCheckedはoptions.checkedを使ってtrue/falseの両状態を検証できます。デフォルトではchecked(true)を期待しますが、options.checked: falseを指定するとunchecked状態を検証します。また、indeterminate状態もサポートされていますが、checkedオプションとは併用不可です。 推奨されるトグル反転アサーション例: - チェック状態確認: await expect(locator).toBeChecked; - アンチェック状態確認: await expect(locator).not.toBeChecked; または await expect(locator).toBeChecked({ checked: false }); - トグル例(公式風): await locator.check; // または click await expect(locator).toBeChecked; await locator.uncheck; await expect(locator).not.toBeChecked; 公式ドキュメント(LocatorAssertions.toBeChecked)で明記されており、v1.18以降でoptions.checkedが追加されています。
Citations:
- 1: https://playwright.dev/docs/api/class-locatorassertions
- 2: https://playwright.dev/docs/test-assertions
- 3: https://testersdock.com/checkbox-playwright/
- 4: https://software-testing-tutorials-automation.com/2025/04/select-checkboxes-in-playwright.html
- 5: https://software-testing-tutorials-automation.com/2025/05/check-checkbox-checked-not-checked-playwright.html
🏁 Script executed:
# Confirm file locations and content
git ls-files | grep -E '(testing-e2e\.md|workbooks_list\.spec\.ts)'Repository: AtCoder-NoviSteps/AtCoderNoviSteps
Length of output: 133
🏁 Script executed:
# Check line count and read testing-e2e.md around lines 85-87
wc -l .claude/rules/testing-e2e.md
echo "---"
sed -n '80,95p' .claude/rules/testing-e2e.mdRepository: AtCoder-NoviSteps/AtCoderNoviSteps
Length of output: 880
🏁 Script executed:
# Check workbooks_list.spec.ts content around lines 198-200
wc -l e2e/workbooks_list.spec.ts
echo "---"
sed -n '195,205p' e2e/workbooks_list.spec.tsRepository: AtCoder-NoviSteps/AtCoderNoviSteps
Length of output: 368
トグル検証の一方向性が、実装パターンと乖離しており修正が必要です。
ガイド Line 85-87 の await expect(toggleInput).toBeChecked(); は、初期状態が ON の場合にクリック後 OFF へ遷移する正常系を検証できません。
実装(e2e/workbooks_list.spec.ts Line 198-200)では { checked: !isChecked } で前状態の反転を検証しており、ガイドとの方針が異なります。Playwright 公式ドキュメント(v1.18 以降)も options.checked での両状態検証をサポートしているため、ガイドを実装パターンに合わせて修正してください。
修正案
- await toggleLabel.click();
- await expect(toggleInput).toBeChecked();
+ const wasChecked = await toggleInput.isChecked();
+ await toggleLabel.click();
+ await expect(toggleInput).toBeChecked({ checked: !wasChecked });📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| await toggleLabel.click(); | |
| await expect(toggleInput).toBeChecked({ checked: true }); | |
| await expect(toggleInput).toBeChecked(); | |
| ``` | |
| const wasChecked = await toggleInput.isChecked(); | |
| await toggleLabel.click(); | |
| await expect(toggleInput).toBeChecked({ checked: !wasChecked }); |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In @.claude/rules/testing-e2e.md around lines 85 - 87, The guide's toggle
assertion is one-sided; update the check to mirror the implementation pattern
used in e2e/workbooks_list.spec.ts by asserting the toggled state with the
previous value inverted: after await toggleLabel.click() replace the
unconditional expect(toggleInput).toBeChecked() with an assertion that uses the
options form to assert the new checked state (e.g., toBeChecked({ checked:
!isChecked })) and ensure the surrounding text references the isChecked variable
and the toggleInput/toggleLabel flow so the guide matches the implementation.
| let { data } = $props(); | ||
|
|
||
| let taskResults: TaskResults = $derived(data.taskResults.sort(compareByContestIdAndTaskId)); | ||
| const taskResults: TaskResults = $derived(data.taskResults.sort(compareByContestIdAndTaskId)); |
There was a problem hiding this comment.
$derived 内で props 配列を破壊的に変更しています。
Line 23 の sort() は元配列を書き換えるため、副作用を避けるためにコピー後にソートしてください。
修正案(非破壊ソート)
- const taskResults: TaskResults = $derived(data.taskResults.sort(compareByContestIdAndTaskId));
+ const taskResults: TaskResults = $derived(
+ [...data.taskResults].sort(compareByContestIdAndTaskId)
+ );📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const taskResults: TaskResults = $derived(data.taskResults.sort(compareByContestIdAndTaskId)); | |
| const taskResults: TaskResults = $derived( | |
| [...data.taskResults].sort(compareByContestIdAndTaskId) | |
| ); |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/routes/problems/`+page.svelte at line 23, The code is mutating the
incoming props by calling sort() on data.taskResults; change the expression that
produces taskResults so it sorts a copy instead (e.g., clone data.taskResults
via slice() or spread before calling sort) so compareByContestIdAndTaskId is
used on a non-mutating copy when creating const taskResults: TaskResults.
There was a problem hiding this comment.
Pull request overview
AtCoderアカウント認証(verified)を前提にした投票機能まわりの挙動を整え、関連するUX(削除後の遷移)とドキュメントを更新するPRです。
Changes:
- 投票アクションで
locals.user取得失敗時のエラーハンドリングを追加 - ユーザー削除後に
/loginへリダイレクトするよう変更 - E2E/環境変数サンプル/Claudeルールの文言を整理
Reviewed changes
Copilot reviewed 8 out of 8 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
src/routes/users/edit/+page.server.ts |
アカウント削除成功時にログイン画面へリダイレクト |
src/routes/problems/+page.svelte |
taskResults の宣言を const 化 |
src/features/votes/actions/vote_actions.ts |
locals.user が無い場合の fail 応答を追加 |
e2e/votes.spec.ts |
冗長な可視性チェックを削除 |
.env.example |
エンドポイントURLのコミット注意喚起を追加 |
.claude/rules/testing.md |
テスト方針ドキュメントの注記を整理 |
.claude/rules/testing-e2e.md |
E2Eの記述をより安全寄りに修正 |
.claude/rules/sveltekit.md |
例の命名(snake_case/camelCase)説明を更新 |
| let { data } = $props(); | ||
|
|
||
| let taskResults: TaskResults = $derived(data.taskResults.sort(compareByContestIdAndTaskId)); | ||
| const taskResults: TaskResults = $derived(data.taskResults.sort(compareByContestIdAndTaskId)); |
There was a problem hiding this comment.
data.taskResults.sort(...) mutates the data.taskResults array in place. Because data comes from load(), mutating it can create hard-to-track side effects (e.g., if the same array reference is reused elsewhere or across reactive updates). Prefer sorting a copy (e.g., copy/toSorted) before passing into $derived.
| const taskResults: TaskResults = $derived(data.taskResults.sort(compareByContestIdAndTaskId)); | |
| const taskResults: TaskResults = $derived( | |
| data.taskResults.toSorted(compareByContestIdAndTaskId), | |
| ); |
| return fail(INTERNAL_SERVER_ERROR, { | ||
| message: 'ユーザー情報の取得に失敗しました。', |
There was a problem hiding this comment.
When locals.user is missing, this returns INTERNAL_SERVER_ERROR (500). In practice this can happen when the session is valid but the DB user is gone (e.g., deleted) or lookup failed, which is usually better represented as an auth problem (clear the session and return UNAUTHORIZED, or redirect to login) rather than a 500. Consider invalidating the session (locals.auth.setSession(null)) and returning 401/403 with a re-login message.
| return fail(INTERNAL_SERVER_ERROR, { | |
| message: 'ユーザー情報の取得に失敗しました。', | |
| await locals.auth.setSession(null); | |
| return fail(UNAUTHORIZED, { | |
| message: 'ユーザー情報の取得に失敗しました。再度ログインしてください。', |
AtCoderアカウントの認証機能に関するPRです。以下の変更を行いました。
新しい認証サーバーへの対応coderabbitのレビューに基づく修正(5回)誤ってPRを作成してしまいました。
このPRは無視してください。
Summary by CodeRabbit
リリースノート
バグ修正
改善
ドキュメント