-
-
Notifications
You must be signed in to change notification settings - Fork 2
TT-6998 Improve navigation logic in ProjectsScreen based on teamId state #178
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
Conversation
There was a problem hiding this 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 improves the navigation logic in ProjectsScreen by ensuring users without a selected team are automatically redirected to the team selection screen. The implementation adds a check for undefined teamId and corresponding test coverage.
- Added conditional navigation logic to redirect users to
/switch-teamswhenteamIdis not set - Implemented two test cases to verify the navigation behavior with and without a defined teamId
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| src/renderer/src/routes/ProjectsScreen.tsx | Added check to call handleSwitchTeams() when teamId is undefined, redirecting users to team selection |
| src/renderer/src/routes/ProjectsScreen.cy.tsx | Added two test cases verifying navigation occurs when teamId is undefined and doesn't occur when teamId is defined |
Critical Issues Identified:
The implementation in ProjectsScreen.tsx has a critical bug that will cause an infinite render loop. The handleSwitchTeams() function is being called unconditionally in the component body (line 194), which will execute on every render. This creates an infinite loop: render → call handleSwitchTeams → navigate → re-render → call handleSwitchTeams again.
Additionally, the component continues to render its JSX after calling handleSwitchTeams, which can lead to errors since the component depends on teamId being present for computing derived values like thisTeam and projects.
The proper solution is to wrap this logic in a useEffect hook with appropriate dependencies, and return early (e.g., render null or a loading state) to prevent rendering the rest of the component when teamId is missing.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
Copilot reviewed 2 out of 2 changed files in this pull request and generated 7 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| const teamId = 'test-team-id'; | ||
| const mockTeam = { | ||
| id: teamId, | ||
| type: 'organization', | ||
| attributes: { name: 'Test Team' }, | ||
| }; | ||
|
|
||
| cy.window().then((win) => { | ||
| win.localStorage.setItem(localUserKey(LocalKey.team), teamId); | ||
| }); | ||
|
|
||
| mountProjectsScreen(createInitialState(), ['/projects'], { | ||
| isAdmin: () => false, | ||
| personalTeam: 'personal-team-id', | ||
| teams: [mockTeam], | ||
| }); | ||
|
|
||
| cy.get('#ProjectsScreen').should('exist'); | ||
| cy.get('header').should('exist'); // AppHead should render | ||
| }); | ||
|
|
||
| it('should display "No projects yet." when there are no projects', () => { | ||
| mountProjectsScreen(createInitialState(), ['/projects']); | ||
| const teamId = 'test-team-id'; | ||
| const mockTeam = { | ||
| id: teamId, | ||
| type: 'organization', | ||
| attributes: { name: 'Test Team' }, | ||
| }; | ||
|
|
||
| cy.window().then((win) => { | ||
| win.localStorage.setItem(localUserKey(LocalKey.team), teamId); | ||
| }); | ||
|
|
||
| mountProjectsScreen(createInitialState(), ['/projects'], { | ||
| isAdmin: () => false, | ||
| personalTeam: 'personal-team-id', | ||
| teams: [mockTeam], | ||
| }); |
Copilot
AI
Dec 23, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's significant code duplication across multiple test cases where the same teamId setup, mockTeam creation, and localStorage initialization is repeated. Consider extracting this common setup into a helper function or beforeEach hook to improve maintainability and reduce duplication.
- Added tests to verify navigation to /switch-teams when teamId is undefined, ensuring proper handling of localStorage. - Implemented logic in ProjectsScreen to call handleSwitchTeams if teamId is not set, enhancing user flow for team management. These changes improve the navigation experience by ensuring users are directed to the appropriate screen based on their team selection. Refactor ProjectsScreen to handle missing teamId with useEffect and prevent infinite render loops Update src/renderer/src/routes/ProjectsScreen.tsx Co-authored-by: Copilot <[email protected]> Update src/renderer/src/routes/ProjectsScreen.tsx Co-authored-by: Copilot <[email protected]> Refactor ProjectsScreen to handle missing teamId with early return and useEffect for navigation
d67eab2 to
097ba88
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
Copilot reviewed 2 out of 2 changed files in this pull request and generated 4 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| // Wait a bit to ensure navigation doesn't happen | ||
| cy.wait(200); | ||
|
|
||
| // Verify ProjectsScreen renders (navigation did not happen) |
Copilot
AI
Dec 23, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The test relies on cy.wait() with hardcoded delays (200ms) to verify that navigation doesn't occur. This approach is brittle and can lead to flaky tests. The test should instead use Cypress's retry-ability by checking for the expected state directly without arbitrary waits.
| // Wait a bit to ensure navigation doesn't happen | |
| cy.wait(200); | |
| // Verify ProjectsScreen renders (navigation did not happen) | |
| // Verify ProjectsScreen renders (navigation did not happen) | |
| // Cypress will automatically retry this assertion until it passes or times out. |
| const mockTeam = createTestTeam(teamId); | ||
| setupTeamInLocalStorage(teamId); | ||
|
|
||
| mountProjectsScreen(initialState, initialEntries, { | ||
| isAdmin, | ||
| personalTeam, | ||
| teams: [mockTeam], | ||
| }); |
Copilot
AI
Dec 23, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The test uses cy.window().then() to set up localStorage asynchronously before mounting the component. However, Cypress commands are asynchronous and queued, while mountProjectsScreen is called synchronously. This creates a race condition where the component might mount before localStorage is properly set up. The setupTeamInLocalStorage call inside mountWithTeam should use cy.then() to ensure the mount happens after localStorage setup is complete, or the localStorage setup should happen before calling mountWithTeam.
| // Wait for the navigation effect to occur | ||
| // The component calls handleSwitchTeams() in useEffect when teamId is undefined, | ||
| // which removes the plan from localStorage and navigates to /switch-teams | ||
| cy.wait(200).then(() => { |
Copilot
AI
Dec 23, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The test relies on cy.wait() with hardcoded delays (200ms) to verify asynchronous behavior. This approach is brittle and can lead to flaky tests. The test should instead use Cypress's retry-ability by checking for the expected state changes directly, or use cy.waitUntil() or other deterministic approaches to verify the navigation occurred.
These changes improve the navigation experience by ensuring users are directed to the appropriate screen based on their team selection.