Skip to content

chore: use shoehorn#152

Open
mcansh wants to merge 11 commits into
mainfrom
copilot/use-shoehorn-integration
Open

chore: use shoehorn#152
mcansh wants to merge 11 commits into
mainfrom
copilot/use-shoehorn-integration

Conversation

@mcansh
Copy link
Copy Markdown
Owner

@mcansh mcansh commented Apr 23, 2026

Summary by CodeRabbit

  • New Features

    • Added a hidden cheat activation feature triggered by entering a specific keyboard sequence within a 2-second window. Once enabled, cheat functionality persists across your session.
    • Enhanced keyboard component with improved accessibility using interactive button elements and proper labels.
  • Tests

    • Added test coverage for cheat feature activation logic and keyboard component rendering.

Copilot AI and others added 9 commits April 23, 2026 00:19
Copilot AI review requested due to automatic review settings April 23, 2026 01:53
@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented Apr 23, 2026

⚠️ No Changeset found

Latest commit: 5b2239e

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 23, 2026

Warning

Rate limit exceeded

@Copilot has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 54 minutes and 46 seconds before requesting another review.

Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 54 minutes and 46 seconds.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: c29e40e5-8202-4433-821c-600240aec469

📥 Commits

Reviewing files that changed from the base of the PR and between 779b040 and 5b2239e.

📒 Files selected for processing (1)
  • app/components/form.test.tsx

Walkthrough

The PR implements a keyboard-triggered cheat feature for a Remix Wordle game, replacing query-string-based cheating with a hidden keystroke sequence detector. Users typing "c-h-e-a-t" within 2 seconds enables a cheat input and persists the state in sessionStorage. The keyboard component is simultaneously refactored from divs to buttons for improved accessibility and semantic HTML.

Changes

Cohort / File(s) Summary
Cheat Feature Implementation
app/components/form.tsx, app/components/form.test.tsx
Added keyboard listener to detect "cheat" keystroke sequence within 2-second window. On successful detection, enables hidden cheat input, persists state to sessionStorage, and triggers Remix handle.update(). Updated component signature to accept Handle parameter. Test suite includes fake timer tests for both successful sequence (within 2s) and timeout scenarios.
Session & Controller Logic
app/controllers/home/controller.tsx, app/constants.ts
Replaced query-string REVEAL_WORD flag with session-based CHEAT_SESSION_KEY. Controller now reads/writes cheat state from/to session instead of URL parameters. Added new exported constant for the session key name.
Keyboard Accessibility
app/components/keyboard.tsx, app/components/keyboard.test.tsx
Refactored keyboard letter elements from non-interactive <div> to <button> elements with proper aria-label attributes. Added unit test verifying button rendering and text content for all expected letters.
Dependencies
package.json, pnpm-workspace.yaml
Added @total-typescript/shoehorn to project dependencies and pnpm catalog at version 0.1.2.

Sequence Diagram

sequenceDiagram
    participant User
    participant GuessForm as GuessForm<br/>Component
    participant SessionStorage as sessionStorage
    participant RemixHandle as Remix<br/>handle.update()

    User->>GuessForm: Type 'c'
    GuessForm->>GuessForm: Accumulate 'c'<br/>(start 2s timer)
    User->>GuessForm: Type 'h'
    GuessForm->>GuessForm: Accumulate 'h'
    User->>GuessForm: Type 'e'
    GuessForm->>GuessForm: Accumulate 'e'
    User->>GuessForm: Type 'a'
    GuessForm->>GuessForm: Accumulate 'a'
    User->>GuessForm: Type 't'<br/>(within 2s)
    GuessForm->>GuessForm: Detect 'cheat' sequence
    GuessForm->>SessionStorage: Write "wordle-cheat-enabled"<br/>= "true"
    GuessForm->>RemixHandle: Call handle.update()
    GuessForm->>GuessForm: Set cheatEnabled = true
    GuessForm->>GuessForm: Render hidden<br/>cheat input
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Title check ⚠️ Warning The PR title 'chore: use shoehorn' is too vague and does not accurately reflect the main substantive changes in the PR. Update the title to describe the actual changes, such as 'refactor: implement cheat code sequence detection with keyboard input handling' or similar to reflect the substantive feature work.
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch copilot/use-shoehorn-integration

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@railway-app
Copy link
Copy Markdown

railway-app Bot commented Apr 23, 2026

🚅 Deployed to the remix-wordle-pr-152 environment in remix-wordle

Service Status Web Updated (UTC)
remix-wordle 😴 Sleeping (View Logs) Web Apr 23, 2026 at 3:47 pm

@railway-app railway-app Bot temporarily deployed to remix-wordle / remix-wordle-pr-152 April 23, 2026 01:53 Destroyed
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces @total-typescript/shoehorn for safer type narrowing/casting in the local form schema utilities, and updates the app’s “cheat” behavior to persist via session/sessionStorage instead of a query param. It also improves keyboard semantics by rendering keys as real <button> elements and adds/updates tests around these behaviors.

Changes:

  • Add @total-typescript/shoehorn to the workspace catalog and dependencies, and use it in local-form-schema for typed output.
  • Move cheat enablement from a ?cheat query param to a persisted session/sessionStorage flag (and update redirect flow accordingly).
  • Render keyboard letters as buttons with accessible labels and add new component tests.

Reviewed changes

Copilot reviewed 9 out of 10 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
pnpm-workspace.yaml Adds @total-typescript/shoehorn to the workspace catalog.
pnpm-lock.yaml Updates lockfile for shoehorn plus additional Remix preview package revisions.
package.json Adds @total-typescript/shoehorn dependency via the workspace catalog.
app/utils/local-form-schema.ts Uses shoehorn’s fromAny for typed return value and adjusts the output typing.
app/controllers/home/controller.tsx Persists cheat enablement in session and removes query-param-based reveal.
app/constants.ts Adds CHEAT_SESSION_KEY constant (and leaves REVEAL_WORD present).
app/components/keyboard.tsx Switches keyboard keys from <div> to <button> with aria-label.
app/components/keyboard.test.tsx Adds coverage for keyboard button rendering.
app/components/form.tsx Adds client-side “cheat code” detection, sessionStorage persistence, and hidden input gating.
app/components/form.test.tsx Adds tests verifying cheat code timing behavior and sessionStorage persistence.
Files not reviewed (1)
  • pnpm-lock.yaml: Language not supported

Comment thread pnpm-workspace.yaml
"@tailwindcss/vite": ^4.2.2
"@testing-library/jest-dom": ^6.9.1
"@testing-library/user-event": ^14.6.1
'@total-typescript/shoehorn': 0.1.2
Copy link

Copilot AI Apr 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In catalog, the new entry uses single quotes while the rest of this file consistently uses double quotes for package names. For consistency (and to reduce unnecessary diffs in future), switch this key to double quotes.

Suggested change
'@total-typescript/shoehorn': 0.1.2
"@total-typescript/shoehorn": 0.1.2

Copilot uses AI. Check for mistakes.
Comment thread app/constants.ts
@@ -2,3 +2,4 @@ export const WORD_LENGTH = 5
export const LETTER_INPUTS = [...Array(WORD_LENGTH).keys()]
export const TOTAL_GUESSES = 6
export const REVEAL_WORD = "cheat"
Copy link

Copilot AI Apr 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

REVEAL_WORD appears to be unused now (search shows no references outside this constants file). Since the cheat flow has moved to a session key, consider removing REVEAL_WORD (or reintroducing its usage) to avoid leaving a misleading/obsolete constant behind.

Suggested change
export const REVEAL_WORD = "cheat"

Copilot uses AI. Check for mistakes.
Comment on lines +68 to +72
if (data.value.cheat) {
session.set(CHEAT_SESSION_KEY, true)
}

return redirect(routes.home.index.href())
Copy link

Copilot AI Apr 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The PR title suggests this is just adopting @total-typescript/shoehorn, but this change also alters the cheat mechanism (query-param based -> session based) and changes redirect behavior. Please update the PR title/description to reflect the functional behavior change, or split the dependency/tooling change from the feature change to keep history/review focused.

Copilot uses AI. Check for mistakes.
Comment thread pnpm-lock.yaml
Comment on lines +1074 to +1078
'@remix-run/assert@https://codeload.github.com/remix-run/remix/tar.gz/f6eccf67550df17c9ce67dccd7defe120444d2b4#path:packages/assert':
resolution: {path: packages/assert, tarball: https://codeload.github.com/remix-run/remix/tar.gz/f6eccf67550df17c9ce67dccd7defe120444d2b4}
version: 0.1.0

'@remix-run/async-context-middleware@https://codeload.github.com/remix-run/remix/tar.gz/ccafabbb1c6a20bf0b013c63cd3af76a3194abdc#path:packages/async-context-middleware':
resolution: {path: packages/async-context-middleware, tarball: https://codeload.github.com/remix-run/remix/tar.gz/ccafabbb1c6a20bf0b013c63cd3af76a3194abdc}
version: 0.2.0
'@remix-run/async-context-middleware@https://codeload.github.com/remix-run/remix/tar.gz/f6eccf67550df17c9ce67dccd7defe120444d2b4#path:packages/async-context-middleware':
Copy link

Copilot AI Apr 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pnpm-lock.yaml includes a large set of Remix preview tarball/hash/version changes in addition to adding @total-typescript/shoehorn. If these bumps are unintentional lockfile churn, consider regenerating the lockfile with only the intended change (or call out the Remix upgrade explicitly in the PR description).

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@app/components/form.test.tsx`:
- Line 3: The test file imports test helpers directly from "vitest"; update the
import statement used in this file (the named imports afterEach, beforeEach,
describe, it, expect, vi) to come from "vite-plus/test" instead of "vitest" so
the project uses the vite-plus wrapper for test APIs; locate the import line
that currently reads import { afterEach, beforeEach, describe, it, expect, vi }
from "vitest" and change the module specifier to "vite-plus/test".

In `@app/components/form.tsx`:
- Around line 101-111: The cheat-detection branch (checking CHEAT_CODE,
CHEAT_WINDOW_MS, cheatBuffer, cheatStartedAt) currently flips client state and
writes window.sessionStorage but does not inform the server before calling
handle.update(); change it to perform a POST/fetch to the server action/endpoint
that sets session.set(CHEAT_SESSION_KEY, true) (send e.g. { cheat: true } or
call the existing server action) and await that response, then set cheatEnabled,
write sessionStorage, and finally await handle.update(); update code around the
block handling cheatEnabled and handle.update() so the server session flag is
set before revalidation.

In `@app/components/keyboard.test.tsx`:
- Line 3: The test currently imports test helpers directly from "vitest"
(describe, expect, it) in app/components/keyboard.test.tsx; update the import to
use the project's wrapper by importing those symbols from "vite-plus/test"
instead (replace the import line that references "vitest" so describe, expect,
it are imported from "vite-plus/test").

In `@app/components/keyboard.tsx`:
- Around line 18-26: The current JSX uses an interactive <button> to render
keyboard letter states (the element with data-state={letter.state} and
key={`keyboard-letter-${letter.letter}`}) but it is non-functional; replace the
<button> with a non-interactive element (e.g., <div> or <span>) while preserving
data-state, class, key and aria-label, and ensure it is not focusable (set
tabIndex={-1}) or use aria-disabled="true" if you must keep a button; this
removes misleading interactivity while keeping styling and accessibility labels
consistent.

In `@app/utils/local-form-schema.ts`:
- Line 2: The import and use of fromAny from `@total-typescript/shoehorn` must be
removed because shoehorn is a test-only helper and its fromAny call uses the
wrong generic arity; revert the narrowing back to a plain compile-time assertion
(or a satisfies narrowing) instead of runtime passthrough. Specifically, remove
the fromAny import and replace the fromAny<ParsedFormData<schema>, typeof
output>(output) usage in the function that produces output with an assertion
like `as ParsedFormData<schema>` (or use TypeScript's `satisfies` pattern) so
the code remains production-safe and types match (do not attempt to pass two
generics to fromAny).
🪄 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: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 6a7541cb-d3e0-4da5-845a-a9949c6815b6

📥 Commits

Reviewing files that changed from the base of the PR and between 71f2eb4 and f9c4032.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (9)
  • app/components/form.test.tsx
  • app/components/form.tsx
  • app/components/keyboard.test.tsx
  • app/components/keyboard.tsx
  • app/constants.ts
  • app/controllers/home/controller.tsx
  • app/utils/local-form-schema.ts
  • package.json
  • pnpm-workspace.yaml

import { render, screen } from "@mcansh/remix-testing-library"
import { describe, it, expect, vi } from "vitest"
import { fireEvent, render, screen } from "@mcansh/remix-testing-library"
import { afterEach, beforeEach, describe, it, expect, vi } from "vitest"
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: Find direct imports from vite/vitest in TS/TSX files.
# Expectation: no direct imports from "vite" or "vitest"; test APIs should come from "vite-plus/test".
rg -nP 'from\s+["'\''](?:vite|vitest)(?:/[^"'\'']*)?["'\'']' --type ts --type tsx -C2

Repository: mcansh/remix-wordle

Length of output: 90


🏁 Script executed:

#!/bin/bash
# Read the form.test.tsx file to check the actual imports
head -20 app/components/form.test.tsx

Repository: mcansh/remix-wordle

Length of output: 479


🏁 Script executed:

#!/bin/bash
# Check if vite-plus is in package.json and what test dependencies are listed
cat package.json | head -50

Repository: mcansh/remix-wordle

Length of output: 1466


🏁 Script executed:

#!/bin/bash
# Check more of devDependencies and look for vite-plus reference
cat package.json | tail -30

Repository: mcansh/remix-wordle

Length of output: 1205


🏁 Script executed:

#!/bin/bash
# Find other test files and check their imports
find . -name "*.test.ts" -o -name "*.test.tsx" | head -10

Repository: mcansh/remix-wordle

Length of output: 408


🏁 Script executed:

#!/bin/bash
# Check imports in several test files to see the pattern
for file in ./app/components/keyboard.test.tsx ./app/utils/auth-session.test.ts ./app/middleware/auth.test.ts; do
  echo "=== $file ==="
  head -5 "$file"
  echo ""
done

Repository: mcansh/remix-wordle

Length of output: 707


🏁 Script executed:

#!/bin/bash
# Check all test files to see which import pattern each uses
echo "Files importing from 'vitest':"
grep -l "from [\"']vitest[\"']" ./app/**/*.test.* 2>/dev/null | sort

echo ""
echo "Files importing from 'vite-plus/test':"
grep -l "from [\"']vite-plus/test[\"']" ./app/**/*.test.* 2>/dev/null | sort

Repository: mcansh/remix-wordle

Length of output: 463


🏁 Script executed:

#!/bin/bash
# Check letter-input.test.tsx to see which import it uses (appeared in both lists)
head -10 ./app/components/letter-input.test.tsx

Repository: mcansh/remix-wordle

Length of output: 392


Import test APIs from vite-plus/test instead of vitest.

Line 3 imports directly from vitest, but per project guidelines, test APIs should be imported from the vite-plus wrapper.

Proposed fix
-import { afterEach, beforeEach, describe, it, expect, vi } from "vitest"
+import { afterEach, beforeEach, describe, expect, it, vi } from "vite-plus/test"
📝 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.

Suggested change
import { afterEach, beforeEach, describe, it, expect, vi } from "vitest"
import { afterEach, beforeEach, describe, expect, it, vi } from "vite-plus/test"
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/components/form.test.tsx` at line 3, The test file imports test helpers
directly from "vitest"; update the import statement used in this file (the named
imports afterEach, beforeEach, describe, it, expect, vi) to come from
"vite-plus/test" instead of "vitest" so the project uses the vite-plus wrapper
for test APIs; locate the import line that currently reads import { afterEach,
beforeEach, describe, it, expect, vi } from "vitest" and change the module
specifier to "vite-plus/test".

Comment thread app/components/form.tsx Outdated
Comment on lines +101 to +111
if (cheatBuffer === CHEAT_CODE && now - cheatStartedAt <= CHEAT_WINDOW_MS) {
cheatBuffer = ""
cheatStartedAt = 0
if (!cheatEnabled) {
cheatEnabled = true
if (typeof window !== "undefined") {
window.sessionStorage.setItem(CHEAT_SESSION_KEY, "true")
}
if (handle) {
await handle.update()
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: Trace all cheat-state read/write paths.
# Expectation: the cheat-code trigger should send a request that sets server Session before page revalidation.
rg -n -C4 'CHEAT_SESSION_KEY|sessionStorage|handle\\.update\\(|name="cheat"|session\\.set\\(CHEAT_SESSION_KEY|session\\.get\\(CHEAT_SESSION_KEY' --type ts --type tsx

Repository: mcansh/remix-wordle

Length of output: 360


🏁 Script executed:

#!/bin/bash
# Search for CHEAT_SESSION_KEY and related cheat code handling
rg -n "CHEAT_SESSION_KEY" --type ts --type tsx

Repository: mcansh/remix-wordle

Length of output: 90


🏁 Script executed:

#!/bin/bash
# Search for handle.update calls
rg -n "handle\.update" --type ts --type tsx -A 2

Repository: mcansh/remix-wordle

Length of output: 90


🏁 Script executed:

#!/bin/bash
# Search for sessionStorage references
rg -n "sessionStorage" --type ts --type tsx -B 2 -A 2

Repository: mcansh/remix-wordle

Length of output: 90


🏁 Script executed:

#!/bin/bash
# Search for cheat input field rendering
rg -n 'name="cheat"' --type ts --type tsx -B 2 -A 2

Repository: mcansh/remix-wordle

Length of output: 90


🏁 Script executed:

#!/bin/bash
# Search for server session.set and session.get with cheat
rg -n "session\.(set|get)" --type ts --type tsx | grep -i cheat

Repository: mcansh/remix-wordle

Length of output: 90


🏁 Script executed:

#!/bin/bash
# Search for CHEAT_SESSION_KEY in JS/TS files
rg -n "CHEAT_SESSION_KEY" -g "*.ts" -g "*.tsx" -g "*.js" -g "*.jsx"

Repository: mcansh/remix-wordle

Length of output: 754


🏁 Script executed:

#!/bin/bash
# Search for handle.update calls
rg -n "handle\.update" -g "*.ts" -g "*.tsx" -g "*.js" -g "*.jsx" -A 3

Repository: mcansh/remix-wordle

Length of output: 784


🏁 Script executed:

#!/bin/bash
# Search for sessionStorage references
rg -n "sessionStorage" -g "*.ts" -g "*.tsx" -g "*.js" -g "*.jsx" -B 2 -A 2

Repository: mcansh/remix-wordle

Length of output: 2297


🏁 Script executed:

#!/bin/bash
# Search for cheat input field
rg -n 'name="cheat"' -g "*.ts" -g "*.tsx" -g "*.js" -g "*.jsx"

Repository: mcansh/remix-wordle

Length of output: 545


🏁 Script executed:

#!/bin/bash
# Look at the form.tsx file to understand the structure
head -150 app/components/form.tsx | tail -100

Repository: mcansh/remix-wordle

Length of output: 2767


Send a cheat-enable request to the server before revalidating.

The cheat code detection sets sessionStorage and calls handle.update(), but sessionStorage is browser-only and never sent to the server. The server's session.set(CHEAT_SESSION_KEY, true) is only called when the form is submitted with cheat=true in the POST data—which happens on the user's next guess. This means handle.update() revalidates the loader before the server session knows the cheat is enabled, so the word won't be revealed until the user enters another guess. Add an action/fetch to set the server session flag before calling handle.update().

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/components/form.tsx` around lines 101 - 111, The cheat-detection branch
(checking CHEAT_CODE, CHEAT_WINDOW_MS, cheatBuffer, cheatStartedAt) currently
flips client state and writes window.sessionStorage but does not inform the
server before calling handle.update(); change it to perform a POST/fetch to the
server action/endpoint that sets session.set(CHEAT_SESSION_KEY, true) (send e.g.
{ cheat: true } or call the existing server action) and await that response,
then set cheatEnabled, write sessionStorage, and finally await handle.update();
update code around the block handling cheatEnabled and handle.update() so the
server session flag is set before revalidation.

@@ -0,0 +1,31 @@
import "@testing-library/jest-dom/vitest"
import { render, screen } from "@mcansh/remix-testing-library"
import { describe, expect, it } from "vitest"
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: Find direct imports from vite/vitest in TS/TSX files.
# Expectation: no direct imports from "vite" or "vitest"; test APIs should come from "vite-plus/test".
rg -nP 'from\s+["'\''](?:vite|vitest)(?:/[^"'\'']*)?["'\'']' --type ts --type tsx -C2

Repository: mcansh/remix-wordle

Length of output: 90


🏁 Script executed:

# Search for direct vitest/vite imports in test files
rg 'from\s+["\'](?:vite|vitest)' app/components/keyboard.test.tsx

# Also check the specific line in context
head -20 app/components/keyboard.test.tsx | cat -n

Repository: mcansh/remix-wordle

Length of output: 215


🏁 Script executed:

cat -n app/components/keyboard.test.tsx | head -10

Repository: mcansh/remix-wordle

Length of output: 457


🏁 Script executed:

# Check full file for vitest imports
grep -n "vitest" app/components/keyboard.test.tsx

# Check if vite-plus is configured
cat package.json | grep -A5 -B5 "vite-plus"

Repository: mcansh/remix-wordle

Length of output: 636


Import test APIs from vite-plus/test.

Line 3 imports directly from vitest; use the vite-plus/test wrapper instead.

-import { describe, expect, it } from "vitest"
+import { describe, expect, it } from "vite-plus/test"
📝 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.

Suggested change
import { describe, expect, it } from "vitest"
import { describe, expect, it } from "vite-plus/test"
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/components/keyboard.test.tsx` at line 3, The test currently imports test
helpers directly from "vitest" (describe, expect, it) in
app/components/keyboard.test.tsx; update the import to use the project's wrapper
by importing those symbols from "vite-plus/test" instead (replace the import
line that references "vitest" so describe, expect, it are imported from
"vite-plus/test").

Comment on lines +18 to +26
<button
type="button"
data-state={letter.state}
class={`flex size-10 items-center justify-center rounded text-center text-white uppercase data-[state=Blank]:bg-gray-400 data-[state=Match]:bg-green-500 data-[state=Miss]:bg-red-500 data-[state=Present]:bg-yellow-500`}
key={`keyboard-letter-${letter.letter}`}
aria-label={`keyboard letter ${letter.letter}`}
>
{letter.letter}
</div>
</button>
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: Inspect keyboard usage and possible delegated click handling.
# Expectation: if keyboard keys remain buttons, there should be a click/activation path that inserts the letter.
rg -n -C4 '<Keyboard|Keyboard\\(|keyboard-letter|keyboard letter|onClick|on\\("click"' --type ts --type tsx

Repository: mcansh/remix-wordle

Length of output: 254


🏁 Script executed:

#!/bin/bash
# Fixed search for Keyboard component usage and click handlers
echo "=== Files containing Keyboard component usage ==="
rg -l "Keyboard" --type ts --type tsx

echo -e "\n=== Looking for click handlers in keyboard.tsx ==="
cat app/components/keyboard.tsx

echo -e "\n=== Searching for Keyboard component instantiation and usage ==="
rg "Keyboard\(" --type ts --type tsx -A3 -B3

echo -e "\n=== Looking for button click handlers in the file ==="
rg "onClick|on:click" app/components/keyboard.tsx

Repository: mcansh/remix-wordle

Length of output: 1430


🏁 Script executed:

#!/bin/bash
# Find where Keyboard component is imported and used
echo "=== Searching for imports of keyboard component ==="
rg "from.*keyboard|import.*Keyboard" --type=ts --type=js

echo -e "\n=== Searching for Keyboard usage in game files ==="
rg -l "board" app/components/ | head -5 | while read f; do echo "File: $f"; head -20 "$f"; done

echo -e "\n=== Check all component files for Keyboard or keyboard usage ==="
find app/components -type f -name "*.tsx" -o -name "*.ts" | head -20

echo -e "\n=== Looking for event delegation patterns ==="
rg "delegate|addEventListener|onClickCapture" --type=ts --type=js -B2 -A2

Repository: mcansh/remix-wordle

Length of output: 3736


🏁 Script executed:

#!/bin/bash
# Check parent component implementations
echo "=== home/page.tsx content (where Keyboard is used) ==="
cat app/controllers/home/page.tsx

echo -e "\n=== history/game.tsx content (where Keyboard is used) ==="
cat app/controllers/history/game.tsx

Repository: mcansh/remix-wordle

Length of output: 5584


Use non-interactive elements for keyboard status display.

These buttons are focusable and announced as interactive, but clicking them does nothing. Since the keyboard serves only to display letter states (matched, missed, present), not for user input, use <div> elements styled as buttons or explicitly disable them instead.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/components/keyboard.tsx` around lines 18 - 26, The current JSX uses an
interactive <button> to render keyboard letter states (the element with
data-state={letter.state} and key={`keyboard-letter-${letter.letter}`}) but it
is non-functional; replace the <button> with a non-interactive element (e.g.,
<div> or <span>) while preserving data-state, class, key and aria-label, and
ensure it is not focusable (set tabIndex={-1}) or use aria-disabled="true" if
you must keep a button; this removes misleading interactivity while keeping
styling and accessibility labels consistent.

Comment thread app/utils/local-form-schema.ts Outdated
Copy link
Copy Markdown
Owner Author

mcansh commented Apr 23, 2026

@copilot no dont use shoehorn in the schema, remove the pick from GuessForm and let it take the full Handle, and use shoehorn in the test instead

@railway-app railway-app Bot temporarily deployed to remix-wordle / remix-wordle-pr-152 April 23, 2026 13:43 Destroyed
Copy link
Copy Markdown
Contributor

Copilot AI commented Apr 23, 2026

@copilot no dont use shoehorn in the schema, remove the pick from GuessForm and let it take the full Handle, and use shoehorn in the test instead

Done in 779b040 (with cleanup in 5b2239e). I removed shoehorn from app/utils/local-form-schema.ts, changed GuessForm to take full Handle (no Pick), and updated app/components/form.test.tsx to use @total-typescript/shoehorn for the test handle mock.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (3)
app/components/form.test.tsx (2)

5-5: ⚠️ Potential issue | 🟡 Minor

Import test APIs from vite-plus/test instead of vitest.

Per project guidelines, test helpers must come from the vite-plus wrapper.

Proposed fix
-import { afterEach, beforeEach, describe, it, expect, vi } from "vitest"
+import { afterEach, beforeEach, describe, expect, it, vi } from "vite-plus/test"

As per coding guidelines: "Import modules from vite-plus instead of vite or vitest (e.g., import { defineConfig } from 'vite-plus'; or import { expect, test, vi } from 'vite-plus/test';)".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/components/form.test.tsx` at line 5, The test file imports testing APIs
from "vitest" but per project guidelines those helpers must be imported from
"vite-plus/test"; update the import statement that currently lists afterEach,
beforeEach, describe, it, expect, vi (in app/components/form.test.tsx) to import
the same named symbols from "vite-plus/test" instead of "vitest" so the test
uses the project's vite-plus wrapper.

128-145: ⚠️ Potential issue | 🟡 Minor

Update imports to use vite-plus/test per coding guidelines.

Line 5 imports from vitest, but per project guidelines should import from vite-plus/test:

import { afterEach, beforeEach, describe, it, expect, vi } from "vite-plus/test"

Additionally, the concern about test reliability remains valid: the mocked handle.update() only returns a resolved promise without triggering framework re-render, and cheatEnabled is a closure variable. Confirm that the component function is re-invoked after handle.update() completes, and verify the test fails when the cheat logic is intentionally broken.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/components/form.test.tsx` around lines 128 - 145, Replace the vitest
import with the project-approved import from "vite-plus/test" (e.g., import {
afterEach, beforeEach, describe, it, expect, vi } from "vite-plus/test"); then
make the test ensure the component actually re-renders after the mocked
handle.update resolves by awaiting the update and any pending effects (use
act/await waitFor or await a resolved promise) so the GuessForm function is
re-invoked and the closure variable cheatEnabled reflects the change; confirm
the mock handle.update returns a promise that resolves and that you wait for the
component to settle before asserting on cheat-enabled behavior (referencing
GuessForm, handle.update, and cheatEnabled).
app/components/form.tsx (1)

101-111: ⚠️ Potential issue | 🟠 Major

Server session is still not set before handle.update().

Same concern as previously raised: sessionStorage.setItem(CHEAT_SESSION_KEY, "true") is browser-only, and session.set(CHEAT_SESSION_KEY, true) in app/controllers/home/controller.tsx is only run on POST with cheat=true. Calling handle.update() here revalidates the loader against a server session that still does not have the cheat flag, so the cheat won't take effect until the user submits their next guess. Consider firing a POST/fetch to an action that sets the server-side flag and awaiting it before handle.update().

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/components/form.tsx` around lines 101 - 111, The client-side cheat flow
currently sets only sessionStorage (CHEAT_SESSION_KEY) and then calls
handle.update(), but the server session (set via session.set in
app/controllers/home/controller.tsx) isn't updated so revalidation won't mark
the cheat active; modify the cheat handler around CHEAT_CODE/CHEAT_WINDOW_MS
(where cheatBuffer, cheatStartedAt, cheatEnabled are used) to send a POST/fetch
to the server action that sets the cheat flag (the same action that checks for
cheat=true on POST in app/controllers/home/controller.tsx), await that request's
successful response, then call handle.update() so the loader sees the updated
server session before revalidation. Ensure errors are handled and only set
cheatEnabled after the server confirms the session flag.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@app/components/form.test.tsx`:
- Around line 20-32: The top-level initialization of handle via
fromPartial<Handle> is redundant because the first beforeEach reassigns it;
remove the initial let-assignment block and consolidate the two beforeEach
blocks into a single beforeEach that (1) reassigns handle using
fromPartial<Handle> with update: vi.fn(async () => {}), and (2) calls
window.sessionStorage.clear(); keep the same handle variable declaration (let
handle) and the same mock signature so tests behavior is unchanged.

---

Duplicate comments:
In `@app/components/form.test.tsx`:
- Line 5: The test file imports testing APIs from "vitest" but per project
guidelines those helpers must be imported from "vite-plus/test"; update the
import statement that currently lists afterEach, beforeEach, describe, it,
expect, vi (in app/components/form.test.tsx) to import the same named symbols
from "vite-plus/test" instead of "vitest" so the test uses the project's
vite-plus wrapper.
- Around line 128-145: Replace the vitest import with the project-approved
import from "vite-plus/test" (e.g., import { afterEach, beforeEach, describe,
it, expect, vi } from "vite-plus/test"); then make the test ensure the component
actually re-renders after the mocked handle.update resolves by awaiting the
update and any pending effects (use act/await waitFor or await a resolved
promise) so the GuessForm function is re-invoked and the closure variable
cheatEnabled reflects the change; confirm the mock handle.update returns a
promise that resolves and that you wait for the component to settle before
asserting on cheat-enabled behavior (referencing GuessForm, handle.update, and
cheatEnabled).

In `@app/components/form.tsx`:
- Around line 101-111: The client-side cheat flow currently sets only
sessionStorage (CHEAT_SESSION_KEY) and then calls handle.update(), but the
server session (set via session.set in app/controllers/home/controller.tsx)
isn't updated so revalidation won't mark the cheat active; modify the cheat
handler around CHEAT_CODE/CHEAT_WINDOW_MS (where cheatBuffer, cheatStartedAt,
cheatEnabled are used) to send a POST/fetch to the server action that sets the
cheat flag (the same action that checks for cheat=true on POST in
app/controllers/home/controller.tsx), await that request's successful response,
then call handle.update() so the loader sees the updated server session before
revalidation. Ensure errors are handled and only set cheatEnabled after the
server confirms the session flag.
🪄 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: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: ecf26f91-7456-48d2-80fe-c2b646f6d092

📥 Commits

Reviewing files that changed from the base of the PR and between f9c4032 and 779b040.

📒 Files selected for processing (2)
  • app/components/form.test.tsx
  • app/components/form.tsx

Comment thread app/components/form.test.tsx Outdated
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants