-
-
Notifications
You must be signed in to change notification settings - Fork 10
Feature/atcoder verified voting #3319
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
27 commits
Select commit
Hold shift + click to select a range
0080b3b
feat: add EDIT_PROFILE_PAGE constant
river0525 e9ea7a0
feat: re-enable AtCoder verification tab on /users/edit
river0525 833d928
feat: require AtCoder verification to cast a grade vote
river0525 0ad162f
feat: show AtCoder verification prompt on vote detail page for unveri…
river0525 1c5681e
feat: propagate isAtCoderVerified to VotableGrade dropdown on problem…
river0525 a8ca83c
feat: open AtCoder tab when navigating from unverified-user prompt vi…
river0525 e5db921
docs: add conflict resolution plan for PR #3319 × staging
KATO-Hiro 3302158
fix: resolve staging merge conflicts for PR #3319 atcoder-verified-vo…
KATO-Hiro a7b16c2
fix: remove username from server-side error log in users/edit load
KATO-Hiro 3c67710
docs(rules): add load() model grouping and component prop patterns
KATO-Hiro 3bdbfed
chore: Fix format
KATO-Hiro 4d391c1
chore: Fix format
KATO-Hiro 2b1d983
chore: Fix format
KATO-Hiro 9645d5b
chore: Fix https status
KATO-Hiro d730ffb
docs: update conflict resolution plan checklist and add review findings
KATO-Hiro af142c0
docs: add CodeRabbit findings and DRY refactor task to plan
KATO-Hiro 27566f6
refactor: extract parseUsernameAndAuthorize helper in users/edit actions
KATO-Hiro 16d05e5
docs: mark DRY refactor complete and triage CodeRabbit findings in plan
KATO-Hiro e4365ac
docs: trim plan to decision-relevant content only
KATO-Hiro eef8613
docs: Remove old plan
KATO-Hiro 78599b5
tests: Fix broken e2e tests
KATO-Hiro 25f0614
docs: Add and update rules
KATO-Hiro 791969a
chore: Remove old plan
KATO-Hiro b8fd4ff
docs: update .env.example comment for AtCoder verification API URL
river0525 31c501a
fix: address CodeRabbit round 1 findings
river0525 83d58c3
fix: address CodeRabbit round 2 findings
river0525 caedfa9
fix: address CodeRabbit round 3 findings
river0525 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,122 @@ | ||
| --- | ||
| description: E2E testing rules and patterns (Playwright) | ||
| paths: | ||
| - '**/*.spec.ts' | ||
| - 'e2e/**' | ||
| --- | ||
|
|
||
| # E2E Tests (Playwright) | ||
|
|
||
| ## No Path Aliases | ||
|
|
||
| The `e2e/` directory is outside SvelteKit's build pipeline — `$lib`, `$features`, and other path aliases are not resolved. Define string constant values as local constants with a reference comment: | ||
|
|
||
| ```typescript | ||
| // Mirrors WorkBookTab.SOLUTION from $features/workbooks/types/workbook | ||
| const TAB_SOLUTION = 'solution'; | ||
| ``` | ||
|
|
||
| Avoid importing values from `src/` in E2E test files. Type-only imports (`import type`) are acceptable since they are erased at compile time: | ||
|
|
||
| ```typescript | ||
| // Bad: runtime import — path alias not resolved in e2e/ | ||
| import { TAB_SOLUTION } from '$features/workbooks/types/workbook'; | ||
|
|
||
| // Good: type-only import — compile-time only | ||
| import type { WorkBookTab } from '$features/workbooks/types/workbook'; | ||
| const TAB_SOLUTION: WorkBookTab = 'solution'; | ||
| ``` | ||
|
|
||
| ## Describe Hierarchy | ||
|
|
||
| When a `describe` block for a user role grows large, split it by behavioral dimension rather than adding more flat `test()` calls: | ||
|
|
||
| ```typescript | ||
| test.describe('logged-in user', () => { | ||
| test.describe('tab visibility', () => { ... }); | ||
| test.describe('URL parameter handling', () => { ... }); | ||
| test.describe('navigation interactions', () => { ... }); | ||
| test.describe('session state', () => { ... }); | ||
| }); | ||
| ``` | ||
|
|
||
| ## Parameterized Tests | ||
|
|
||
| Playwright has no native `test.each`. Use `for...of` loops — the official recommended pattern. | ||
|
|
||
| **Single test with a loop** — use when testing a sequence or workflow within one test: | ||
|
|
||
| ```typescript | ||
| // Mirrors TaskGrade from $lib/types/task — do not import from src/ in E2E files | ||
| const GRADES = ['Q10', 'Q9', 'Q8'] as const; | ||
|
|
||
| for (const grade of GRADES) { | ||
| await gradeButton(page, grade).click(); | ||
| await expect(page).toHaveURL(`?grades=${grade}`); | ||
| } | ||
| ``` | ||
|
|
||
| **Multiple tests from parameters** — use when each parameter represents an independent case: | ||
|
|
||
| ```typescript | ||
| const GRADES = ['Q10', 'Q9', 'Q8'] as const; | ||
|
|
||
| for (const grade of GRADES) { | ||
| test(`filters by grade ${grade}`, async ({ page }) => { | ||
| await page.goto('/tasks'); | ||
| await gradeButton(page, grade).click(); | ||
| await expect(page).toHaveURL(`?grades=${grade}`); | ||
| }); | ||
| } | ||
| ``` | ||
|
|
||
| ## Assertions | ||
|
|
||
| After an interaction that changes element state (active tab, toggle, selection), assert the _new_ state — not just that the element is visible, which may have been true before the interaction. Assert an active CSS class, `aria-selected`, or similar attribute instead of `toBeVisible()`. | ||
|
|
||
| ## Flowbite Toggle | ||
|
|
||
| Flowbite's `Toggle` renders an `sr-only` `<input type="checkbox">` inside a `<label>`. Clicking the input directly fails because the visual `<span>` sibling intercepts pointer events. Click the label wrapper instead: | ||
|
|
||
| ```typescript | ||
| const toggleInput = page.locator('input[aria-label="<aria-label value>"]'); | ||
| const toggleLabel = page.locator('label:has(input[aria-label="<aria-label value>"])'); | ||
|
|
||
| await toggleLabel.click(); | ||
| await expect(toggleInput).toBeChecked({ checked: true }); | ||
| ``` | ||
|
|
||
| The same pattern applies to any Flowbite component that visually overlays its native input (e.g. `Checkbox`, `Radio`). | ||
|
|
||
| ## Strict Mode: Scope Locators to the Content Area | ||
|
|
||
| When the navbar and page body both contain a link or button with the same text (e.g., a breadcrumb and a nav link share the same label), `getByRole` in strict mode will find multiple matches and throw. Scope the locator to the page's content container: | ||
|
|
||
| ```typescript | ||
| // Bad: matches navbar link AND breadcrumb link | ||
| await page.getByRole('link', { name: 'グレード投票' }).click(); | ||
|
|
||
| // Good: scoped to page content only | ||
| await page.locator('.container').locator('nav').getByRole('link', { name: 'グレード投票' }).click(); | ||
| await page.locator('.container').getByRole('link', { name: 'ログイン' }).click(); | ||
| ``` | ||
|
|
||
| Use `.container` (page content wrapper) to exclude the global navbar. Prefer the narrowest scope that remains stable — breadcrumb `nav` inside `.container` is more precise than `.container` alone when the link only appears there. | ||
|
|
||
| ## Conditional Skip Based on Runtime State | ||
|
|
||
| When a test depends on DB or session state that may vary across environments (e.g., a user's AtCoder verification status), use `test.skip(condition, reason)` inside the test body instead of a static `test.skip`. This way the test runs automatically when the precondition is met: | ||
|
|
||
| ```typescript | ||
| test('sees vote grade buttons', async ({ page }) => { | ||
| await page.goto(url); | ||
|
|
||
| const isUnverified = await page.getByText('AtCoderアカウントの認証が必要です').isVisible(); | ||
| test.skip(isUnverified, 'test user is not AtCoder-verified'); | ||
|
|
||
| // assertions below run only when precondition holds | ||
| await expect(page.locator('form[action="?/voteAbsoluteGrade"]')).toBeVisible(); | ||
| }); | ||
| ``` | ||
|
|
||
| Prefer this over a hard-coded `test.skip` whenever the condition is observable on the page. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,3 +1,3 @@ | ||
| # AtCoder affiliation confirmation API endpoint | ||
| # Set this to the actual endpoint URL in your .env file (do not commit real URLs here) | ||
| # AtCoder affiliation confirmation API endpoint (NoviSteps organization crawler) | ||
| # See team documentation for the actual URL. | ||
| CONFIRM_API_URL=https://your-confirm-api-endpoint.example.com/confirm | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.