generated from mantinedev/vite-template
-
Notifications
You must be signed in to change notification settings - Fork 1
Add Country id route and update routes to use countryId path param #210
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
11 commits
Select commit
Hold shift + click to select a range
82d8bab
feat: Update router to use countryId path param
SakshiKekre 19cd457
Add missed file
SakshiKekre 3419b51
chore: lint/prettier
SakshiKekre b06b1ab
fix: Remove overengineered metadata hook
SakshiKekre 293200d
fix: Updates around default country; fix: Delete redundant hooks
SakshiKekre 301d132
feat: Move test fixtures and mocks for useCurrentCountry hook
SakshiKekre 81ac130
fix: validate country parameter in router to prevent invalid URLs
SakshiKekre 8da24ac
fix: resolve type error in countryLoader test mock
SakshiKekre b5d8ae7
feat: use guard component for country validation
SakshiKekre ab1eac6
test: add security tests for CountryGuard
SakshiKekre 82d6f30
tests: remove loader tests
SakshiKekre 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
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 was deleted.
Oops, something went wrong.
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,22 @@ | ||
import { useParams } from 'react-router-dom'; | ||
import { countryIds } from '@/libs/countries'; | ||
|
||
/** | ||
* Returns the current country ID from the URL path parameter. | ||
* | ||
* For routes with :countryId param: Returns the country from URL (already validated by loader) | ||
* For routes without :countryId param: Returns 'us' as fallback with a warning | ||
* | ||
* @returns The country ID from URL or default fallback | ||
*/ | ||
export function useCurrentCountry(): (typeof countryIds)[number] { | ||
const { countryId } = useParams<{ countryId: string }>(); | ||
|
||
if (!countryId) { | ||
console.warn('useCurrentCountry used in non-country route, returning default country'); | ||
// TODO: Replace with dynamic default country based on user location/preferences | ||
return 'us'; | ||
SakshiKekre marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
|
||
return countryId as (typeof countryIds)[number]; | ||
} |
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,38 @@ | ||
import { Navigate, useLocation, useParams } from 'react-router-dom'; | ||
import { countryIds } from '@/libs/countries'; | ||
|
||
interface CountryGuardProps { | ||
children: React.ReactNode; | ||
} | ||
|
||
/** | ||
* Guard component that validates country parameter in the route. | ||
* Wraps the Layout component and redirects if country is invalid. | ||
*/ | ||
export function CountryGuard({ children }: CountryGuardProps) { | ||
const { countryId } = useParams<{ countryId: string }>(); | ||
const location = useLocation(); | ||
|
||
// Validation logic | ||
const isValid = countryId && countryIds.includes(countryId as any); | ||
|
||
if (!isValid) { | ||
// Extract path after country segment to preserve user's intended destination. | ||
// We can't use useParams for this - it only gives us { countryId }, not the rest. | ||
// Route pattern /:countryId doesn't capture /policies/123 part. | ||
// Using /:countryId/* would capture it but breaks child route matching. | ||
// So we must use string manipulation on location.pathname. | ||
const currentPath = location.pathname; | ||
const pathAfterCountry = countryId | ||
? currentPath.substring(countryId.length + 1) // Skip "/{country}" | ||
: currentPath; | ||
const defaultCountry = 'us'; | ||
const redirectPath = `/${defaultCountry}${pathAfterCountry}`; | ||
|
||
return <Navigate to={redirectPath} replace />; | ||
} | ||
|
||
// Render children (Layout) when validation passes. | ||
// Using {children} instead of <Outlet /> keeps this a simple wrapper component. | ||
return <>{children}</>; | ||
} |
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,41 @@ | ||
/** | ||
* Test fixtures for useCurrentCountry hook tests | ||
* | ||
* Provides router wrapper and test constants for testing country extraction from URL | ||
*/ | ||
import React from 'react'; | ||
import { MemoryRouter, Route, Routes } from 'react-router-dom'; | ||
|
||
// Test constants | ||
export const TEST_COUNTRIES = { | ||
US: 'us', | ||
UK: 'uk', | ||
CA: 'ca', | ||
NG: 'ng', | ||
IL: 'il', | ||
} as const; | ||
|
||
export const INVALID_COUNTRY = 'invalid-country'; | ||
|
||
// Test paths | ||
export const TEST_PATHS = { | ||
UK_POLICIES: '/uk/policies', | ||
US_HOUSEHOLD: '/us/household', | ||
CA_ABOUT: '/ca/about', | ||
INVALID_COUNTRY_POLICIES: `/${INVALID_COUNTRY}/policies`, | ||
ROOT: '/', | ||
} as const; | ||
|
||
// Wrapper component that provides router context | ||
export function createRouterWrapper(initialPath: string) { | ||
return function Wrapper({ children }: { children: React.ReactNode }) { | ||
return ( | ||
<MemoryRouter initialEntries={[initialPath]}> | ||
<Routes> | ||
<Route path="/:countryId/*" element={children} /> | ||
<Route path="/*" element={children} /> | ||
</Routes> | ||
</MemoryRouter> | ||
); | ||
}; | ||
} |
108 changes: 108 additions & 0 deletions
108
app/src/tests/fixtures/routing/guards/countryGuardMocks.ts
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,108 @@ | ||
/** | ||
* Test fixtures for CountryGuard component tests | ||
* | ||
* Includes edge cases, security test patterns, and malicious input examples | ||
*/ | ||
|
||
// Valid countries | ||
export const VALID_COUNTRIES = { | ||
US: 'us', | ||
UK: 'uk', | ||
CA: 'ca', | ||
NG: 'ng', | ||
IL: 'il', | ||
} as const; | ||
|
||
// Invalid country examples | ||
export const INVALID_COUNTRIES = { | ||
SIMPLE: 'invalid', | ||
NUMERIC: '123', | ||
EMPTY: '', | ||
UNDEFINED: undefined, | ||
} as const; | ||
|
||
// Security test patterns - testing guard's resilience | ||
export const MALICIOUS_INPUTS = { | ||
// SQL injection attempts | ||
SQL_INJECTION: "'; DROP TABLE users; --", | ||
SQL_UNION: "1' UNION SELECT * FROM users--", | ||
|
||
// XSS attempts | ||
XSS_SCRIPT: '<script>alert("xss")</script>', | ||
XSS_IMG: '<img src=x onerror=alert("xss")>', | ||
XSS_ENCODED: '%3Cscript%3Ealert(%22xss%22)%3C/script%3E', | ||
|
||
// Path traversal attempts | ||
PATH_TRAVERSAL: '../../../etc/passwd', | ||
PATH_WINDOWS: '..\\..\\..\\windows\\system32', | ||
|
||
// Special characters and garbage | ||
SPECIAL_CHARS: '@#$%^&*()_+={}[]|\\:";\'<>?,.', | ||
UNICODE: '🇬🇧😀مرحبا你好', | ||
NULL_BYTE: 'us\x00.txt', | ||
|
||
// Buffer overflow attempts | ||
LONG_STRING: 'a'.repeat(10000), | ||
|
||
// Command injection attempts | ||
COMMAND_INJECTION: '; ls -la', | ||
PIPE_COMMAND: '| cat /etc/passwd', | ||
} as const; | ||
|
||
// Test paths with various patterns | ||
export const TEST_PATHS = { | ||
// Valid paths | ||
US_POLICIES: '/us/policies', | ||
UK_HOUSEHOLD: '/uk/household/123', | ||
CA_REPORTS: '/ca/reports/annual/2024', | ||
|
||
// Invalid country paths | ||
INVALID_SIMPLE: '/invalid/policies', | ||
GARBAGE_PATH: `/${MALICIOUS_INPUTS.SPECIAL_CHARS}/policies`, | ||
SQL_PATH: `/${MALICIOUS_INPUTS.SQL_INJECTION}/household`, | ||
XSS_PATH: `/${MALICIOUS_INPUTS.XSS_SCRIPT}/reports`, | ||
TRAVERSAL_PATH: `/${MALICIOUS_INPUTS.PATH_TRAVERSAL}/configurations`, | ||
UNICODE_PATH: `/${MALICIOUS_INPUTS.UNICODE}/about`, | ||
LONG_PATH: `/${MALICIOUS_INPUTS.LONG_STRING}/test`, | ||
|
||
// Edge cases | ||
ROOT: '/', | ||
DOUBLE_SLASH: '//policies', | ||
TRAILING_SLASH: '/us/policies/', | ||
QUERY_PARAMS: '/invalid/policies?filter=active&sort=date', | ||
HASH_FRAGMENT: '/invalid/policies#section', | ||
} as const; | ||
|
||
// Expected redirect paths | ||
export const EXPECTED_REDIRECTS = { | ||
DEFAULT_COUNTRY: 'us', | ||
|
||
// Preserve paths after redirect | ||
POLICIES: '/us/policies', | ||
HOUSEHOLD: '/us/household/123', | ||
REPORTS: '/us/reports/annual/2024', | ||
ROOT_REDIRECT: '/us/', | ||
|
||
// Complex path preservation | ||
NESTED_PATH: '/us/reports/123/edit', | ||
WITH_QUERY: '/us/policies?filter=active&sort=date', | ||
WITH_HASH: '/us/policies#section', | ||
} as const; | ||
|
||
// Helper function to create mock Navigate tracking | ||
export function createNavigateMock() { | ||
const navigateCalls: Array<{ to: string; replace?: boolean }> = []; | ||
|
||
const MockNavigate = ({ to, replace }: { to: string; replace?: boolean }) => { | ||
navigateCalls.push({ to, replace }); | ||
return null; | ||
}; | ||
|
||
return { | ||
MockNavigate, | ||
navigateCalls, | ||
getLastCall: () => navigateCalls[navigateCalls.length - 1], | ||
wasCalledWith: (to: string, replace = true) => | ||
navigateCalls.some((call) => call.to === to && call.replace === replace), | ||
}; | ||
} |
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.