feat: integrate nudge API for new application #1144
feat: integrate nudge API for new application #1144MayankBansal12 wants to merge 2 commits intodevelopfrom
Conversation
Deploying www-rds with
|
| Latest commit: |
3ef71a6
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://3e54fc05.www-rds.pages.dev |
| Branch Preview URL: | https://feat-nudge-api.www-rds.pages.dev |
WalkthroughThis PR implements nudge functionality for applications by extending the detail header component with async API calls, loading states, and toast notifications. It also refactors the application detail route to fetch applications by user ID instead of application ID, with supporting utilities and tests. Changes
Sequence DiagramsequenceDiagram
participant User
participant DetailHeader as DetailHeader Component
participant Toast as Toast Service
participant NudgeAPI as nudge-api Utility
participant RemoteAPI as Remote API
participant Controller as Detail Controller
User->>DetailHeader: Clicks nudge button
activate DetailHeader
DetailHeader->>DetailHeader: Set isLoading = true
DetailHeader->>DetailHeader: Disable nudge button
DetailHeader->>NudgeAPI: Call nudgeApplication(applicationId)
activate NudgeAPI
NudgeAPI->>RemoteAPI: POST /nudge/{applicationId}
activate RemoteAPI
RemoteAPI-->>NudgeAPI: Response with updated nudge data
deactivate RemoteAPI
deactivate NudgeAPI
DetailHeader->>DetailHeader: Update local nudge data
DetailHeader->>Toast: Show success toast
DetailHeader->>Controller: Emit onNudge with updated data
activate Controller
Controller->>Controller: Update nudgeCount & lastNudgedAt
deactivate Controller
DetailHeader->>DetailHeader: Set isLoading = false
deactivate DetailHeader
User->>User: See updated nudge state
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 1 | ❌ 2❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (1 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
🧪 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: 10
🤖 Fix all issues with AI agents
In `@app/components/application/detail-header.js`:
- Around line 94-100: The client currently builds updatedNudgeData using
this.nudgeCount + 1 and a client-side lastNudgedAt after calling
nudgeApplication(this.application.id), which can diverge from server state;
update the post-nudge logic to read the server response from nudgeApplication
(use returned nudgeCount and lastNudgedAt if present) and only fall back to
computing nudgeCount = this.nudgeCount + 1 and lastNudgedAt = new
Date().toISOString() when the API response lacks those fields, then set the
local state accordingly.
- Line 6: The imported utility nudgeApplication conflicts with the action method
of the same name; rename the component's action to handleNudgeClick, update the
action method declaration (e.g., change the action previously named
nudgeApplication to handleNudgeClick and call the imported nudgeApplication
inside it), and update the template/button to use {{this.handleNudgeClick}} (and
update any other internal references to the old action name).
In `@app/controllers/applications/detail.js`:
- Around line 46-51: The handleNudge action mutates application.nudgeCount and
application.lastNudgedAt directly which won't notify Glimmer; to fix, make the
controller's application reference tracked (add a `@tracked` application property
on the controller class) or update handleNudge to replace the application object
instead of mutating it (e.g. set this.application = { ...this.application,
nudgeCount: nudgeData.nudgeCount, lastNudgedAt: nudgeData.lastNudgedAt });
update the controller class to declare the `@tracked` field if you choose that
approach and ensure handleNudge uses the tracked property or object replacement
so the UI re-renders.
In `@app/routes/applications/detail.js`:
- Line 51: Replace the explicit property assignment "application: application"
with ES6 object shorthand by using just "application" inside the object literal
(locate the object that currently contains the property 'application' in
routes/applications/detail.js and update it to use the shorthand form).
- Around line 47-48: The current code blindly picks applications[0] from
applicationData.applications; update the logic to handle multiple applications:
if applications.length > 1, log a warning including the number of applications
found (use processLogger or the existing logger), then sort the applications
array by creation timestamp (e.g., createdAt or created_at) descending and set
application = applications[0]; alternatively replace this with a selector UI if
UX requires — ensure you reference the variables applicationData, applications
and application when implementing the change.
- Line 16: Restore explicit ID handling in the route: change model() back to
accept params (async model(params)) and use params.id to fetch the requested
application (e.g., call the application lookup by id inside model using
params.id); if params.id is missing and you still need user-based fallback,
explicitly fetch all applications for the current user and either return the
matched application or return the list (do not silently pick applications[0]) so
callers can handle multiples; also ensure callers (e.g., the code that
transitions using this.applicationId) continue to pass the application id or are
updated to transition to a user-based route if you intentionally remove the
'/:id' param.
In `@tests/integration/components/application/detail-header-test.js`:
- Around line 150-162: The test replaces window.fetch with a stub but doesn't
guarantee restoration on failure; update the block that sets const originalFetch
= window.fetch and window.fetch = () => Promise.resolve({ ok: true }) to wrap
the render(...) and await click('[data-test-button="nudge-button"]') calls in a
try/finally so that window.fetch = originalFetch is executed in the finally
clause; locate the replacement around the Application::DetailHeader render in
tests/integration/components/application/detail-header-test.js (symbols:
originalFetch, window.fetch, render, click, '[data-test-button="nudge-button"]')
and move the restoration into finally to ensure cleanup regardless of test
assertions.
- Around line 113-129: The test overrides window.fetch (originalFetch) and
restores it only at the end, which leaves the mock in place if an assertion
fails; wrap the code that sets window.fetch, the
click('[data-test-button="nudge-button"]'), waitFor(...) and settled() calls in
a try block and move window.fetch = originalFetch into a finally block so
window.fetch is always restored even on test failures; reference the existing
symbols originalFetch, resolveNudge, window.fetch, click, waitFor, and settled
when editing the test.
In `@tests/unit/routes/applications/detail-test.js`:
- Around line 81-106: The assertion is referencing this.toast which isn't
defined; update the test to assert against the stubbed toast on the route
(this.route.toast.error) instead of this.toast. Locate the test "displays error
toast on 404 response" and replace the failing assertion that checks
this.toast.error.calledOnce with an assertion that this.route.toast.error was
called (e.g., this.route.toast.error.calledOnce) so it uses the actual stubbed
method used when calling this.route.model.
- Around line 134-151: The test references a missing toast stub and should also
assert the applications API isn't called when userId is missing: ensure the test
initializes a toast error stub on the test context (e.g., set this.toast = {
error: sinon.stub() } or equivalent) before calling this.route.model, keep using
this.fetchStub and this.route.model from the diff, and after invoking
this.route.model assert the toast error stub was called once and that
this.fetchStub was not invoked for the applications endpoint (e.g., assert no
calls/matches to a URL containing '/applications' or that fetchStub.callCount
equals the expected number for only the user fetch).
| import { service } from '@ember/service'; | ||
| import { tracked } from '@glimmer/tracking'; | ||
| import { TOAST_OPTIONS } from '../../constants/toast-options'; | ||
| import nudgeApplication from '../../utils/nudge-api'; |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
Naming collision between imported function and action method.
Both the imported utility and the action method are named nudgeApplication. While JavaScript resolves this correctly (the import is used on line 94), it reduces readability and could cause confusion during maintenance.
♻️ Rename the action to avoid confusion
-import nudgeApplication from '../../utils/nudge-api';
+import nudgeApi from '../../utils/nudge-api'; `@action`
- async nudgeApplication() {
+ async handleNudgeClick() {
this.isLoading = true;
try {
- await nudgeApplication(this.application.id);
+ await nudgeApi(this.application.id);Then update the template to use {{this.handleNudgeClick}} for the button action.
Also applies to: 90-94
🤖 Prompt for AI Agents
In `@app/components/application/detail-header.js` at line 6, The imported utility
nudgeApplication conflicts with the action method of the same name; rename the
component's action to handleNudgeClick, update the action method declaration
(e.g., change the action previously named nudgeApplication to handleNudgeClick
and call the imported nudgeApplication inside it), and update the
template/button to use {{this.handleNudgeClick}} (and update any other internal
references to the old action name).
| await nudgeApplication(this.application.id); | ||
|
|
||
| const currentNudgeCount = this.nudgeCount; | ||
| const updatedNudgeData = { | ||
| nudgeCount: currentNudgeCount + 1, | ||
| lastNudgedAt: new Date().toISOString(), | ||
| }; |
There was a problem hiding this comment.
Client-side nudge count calculation may diverge from server state.
The code increments nudgeCount locally (currentNudgeCount + 1) and generates lastNudgedAt on the client. If the server returns different values (e.g., due to concurrent nudges or server-side timestamp), the UI will be out of sync.
Consider using the response data from the API if available, falling back to local calculation only if the server doesn't return the updated values.
🤖 Prompt for AI Agents
In `@app/components/application/detail-header.js` around lines 94 - 100, The
client currently builds updatedNudgeData using this.nudgeCount + 1 and a
client-side lastNudgedAt after calling nudgeApplication(this.application.id),
which can diverge from server state; update the post-nudge logic to read the
server response from nudgeApplication (use returned nudgeCount and lastNudgedAt
if present) and only fall back to computing nudgeCount = this.nudgeCount + 1 and
lastNudgedAt = new Date().toISOString() when the API response lacks those
fields, then set the local state accordingly.
| @action | ||
| handleNudge(nudgeData) { | ||
| const application = this.application; | ||
| application.nudgeCount = nudgeData.nudgeCount; | ||
| application.lastNudgedAt = nudgeData.lastNudgedAt; | ||
| } |
There was a problem hiding this comment.
Potential reactivity issue with direct property mutation.
Directly mutating application.nudgeCount and application.lastNudgedAt on a plain object won't trigger Glimmer's reactivity system. The UI may not re-render to reflect the updated nudge count and timestamp.
Consider using @tracked properties or replacing the entire application object to trigger reactivity:
🐛 Proposed fix using object replacement for reactivity
`@action`
handleNudge(nudgeData) {
- const application = this.application;
- application.nudgeCount = nudgeData.nudgeCount;
- application.lastNudgedAt = nudgeData.lastNudgedAt;
+ // Replace the application object to trigger reactivity
+ this.model = {
+ ...this.model,
+ application: {
+ ...this.application,
+ nudgeCount: nudgeData.nudgeCount,
+ lastNudgedAt: nudgeData.lastNudgedAt,
+ },
+ };
}
}Note: This requires adding @tracked model; to the controller class or using a different state management approach.
🤖 Prompt for AI Agents
In `@app/controllers/applications/detail.js` around lines 46 - 51, The handleNudge
action mutates application.nudgeCount and application.lastNudgedAt directly
which won't notify Glimmer; to fix, make the controller's application reference
tracked (add a `@tracked` application property on the controller class) or update
handleNudge to replace the application object instead of mutating it (e.g. set
this.application = { ...this.application, nudgeCount: nudgeData.nudgeCount,
lastNudgedAt: nudgeData.lastNudgedAt }); update the controller class to declare
the `@tracked` field if you choose that approach and ensure handleNudge uses the
tracked property or object replacement so the UI re-renders.
| @service router; | ||
|
|
||
| async model(params) { | ||
| async model() { |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# Find the router file and check the route definition
find . -name "router.js" -o -name "router.ts" | head -5Repository: RealDevSquad/website-www
Length of output: 83
🏁 Script executed:
# Look for the applications/detail route definition
rg -n "applications.*detail|detail.*route" --type js --type ts -C 3 | head -50Repository: RealDevSquad/website-www
Length of output: 3819
🏁 Script executed:
# Check for transitions and links to applications.detail route
rg -n "transitionTo.*applications.*detail|link-to.*applications.*detail" --type js --type hbs -C 2Repository: RealDevSquad/website-www
Length of output: 533
🏁 Script executed:
# Check for any queryParams or dynamic segments in routes
rg -n "route\(.*detail|Route.*detail" --type js --type ts -C 2Repository: RealDevSquad/website-www
Length of output: 645
🏁 Script executed:
# Read the detail.js file to confirm the model() signature
cat -n app/routes/applications/detail.js | head -60Repository: RealDevSquad/website-www
Length of output: 2473
🏁 Script executed:
# Check the detail route tests to understand expected behavior
cat -n tests/unit/routes/applications/detail-test.jsRepository: RealDevSquad/website-www
Length of output: 5697
🏁 Script executed:
# Search for all transitions to applications.detail route
rg -n "applications\.detail|transitionTo.*detail" --type js --type hbsRepository: RealDevSquad/website-www
Length of output: 807
Route changed from ID-based to user-based application lookup, breaking URL parameters and ID handling.
The model() method no longer accepts params, yet the route definition still declares { path: '/:id' } and calling code (status-card.js line 108) still passes this.applicationId to the transition. The route now ignores the ID and instead fetches all applications for the current user, returning applications[0]. This breaks the ability to access a specific application by ID and silently ignores any extra parameters passed during transitions.
Additionally, when a user has multiple applications, only the first one is retrieved with no indication that others exist.
🤖 Prompt for AI Agents
In `@app/routes/applications/detail.js` at line 16, Restore explicit ID handling
in the route: change model() back to accept params (async model(params)) and use
params.id to fetch the requested application (e.g., call the application lookup
by id inside model using params.id); if params.id is missing and you still need
user-based fallback, explicitly fetch all applications for the current user and
either return the matched application or return the list (do not silently pick
applications[0]) so callers can handle multiples; also ensure callers (e.g., the
code that transitions using this.applicationId) continue to pass the application
id or are updated to transition to a user-based route if you intentionally
remove the '/:id' param.
| const applications = applicationData?.applications || []; | ||
| const application = applications[0] || null; |
There was a problem hiding this comment.
Silently takes only the first application if multiple exist.
If a user has multiple applications, this code takes applications[0] without any indication to the user. Consider either:
- Showing a list/selector if multiple applications exist
- Sorting by creation date to ensure the most relevant one is shown
- Logging when multiple applications are found
🛡️ Add logging for multiple applications
const applicationData = await applicationResponse.json();
const applications = applicationData?.applications || [];
+ if (applications.length > 1) {
+ console.warn(`User has ${applications.length} applications, showing the first one`);
+ }
const application = applications[0] || null;📝 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 applications = applicationData?.applications || []; | |
| const application = applications[0] || null; | |
| const applications = applicationData?.applications || []; | |
| if (applications.length > 1) { | |
| console.warn(`User has ${applications.length} applications, showing the first one`); | |
| } | |
| const application = applications[0] || null; |
🤖 Prompt for AI Agents
In `@app/routes/applications/detail.js` around lines 47 - 48, The current code
blindly picks applications[0] from applicationData.applications; update the
logic to handle multiple applications: if applications.length > 1, log a warning
including the number of applications found (use processLogger or the existing
logger), then sort the applications array by creation timestamp (e.g., createdAt
or created_at) descending and set application = applications[0]; alternatively
replace this with a selector UI if UX requires — ensure you reference the
variables applicationData, applications and application when implementing the
change.
|
|
||
| return { | ||
| application: applicationData?.application, | ||
| application: application, |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
Use object shorthand.
Minor style improvement: use ES6 object shorthand syntax.
♻️ Proposed fix
return {
- application: application,
+ application,
currentUser: userData,
};📝 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.
| application: application, | |
| return { | |
| application, | |
| currentUser: userData, | |
| }; |
🤖 Prompt for AI Agents
In `@app/routes/applications/detail.js` at line 51, Replace the explicit property
assignment "application: application" with ES6 object shorthand by using just
"application" inside the object literal (locate the object that currently
contains the property 'application' in routes/applications/detail.js and update
it to use the shorthand form).
| const originalFetch = window.fetch; | ||
| let resolveNudge; | ||
| window.fetch = () => | ||
| new Promise((resolve) => { | ||
| resolveNudge = () => resolve({ ok: true }); | ||
| }); | ||
|
|
||
| click('[data-test-button="nudge-button"]'); | ||
|
|
||
| await waitFor('[data-test-button="nudge-button"][disabled]'); | ||
| assert.dom('[data-test-button="nudge-button"]').hasAttribute('disabled'); | ||
|
|
||
| resolveNudge(); | ||
| await settled(); | ||
|
|
||
| window.fetch = originalFetch; | ||
| }); |
There was a problem hiding this comment.
Use try/finally to ensure fetch is restored on test failure.
If an assertion fails before window.fetch = originalFetch, the mock remains in place and can break subsequent tests. Wrap the test logic in try/finally to guarantee cleanup.
🛡️ Proposed fix with try/finally
const originalFetch = window.fetch;
let resolveNudge;
window.fetch = () =>
new Promise((resolve) => {
resolveNudge = () => resolve({ ok: true });
});
- click('[data-test-button="nudge-button"]');
-
- await waitFor('[data-test-button="nudge-button"][disabled]');
- assert.dom('[data-test-button="nudge-button"]').hasAttribute('disabled');
-
- resolveNudge();
- await settled();
-
- window.fetch = originalFetch;
+ try {
+ click('[data-test-button="nudge-button"]');
+
+ await waitFor('[data-test-button="nudge-button"][disabled]');
+ assert.dom('[data-test-button="nudge-button"]').hasAttribute('disabled');
+
+ resolveNudge();
+ await settled();
+ } finally {
+ window.fetch = originalFetch;
+ }
});📝 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 originalFetch = window.fetch; | |
| let resolveNudge; | |
| window.fetch = () => | |
| new Promise((resolve) => { | |
| resolveNudge = () => resolve({ ok: true }); | |
| }); | |
| click('[data-test-button="nudge-button"]'); | |
| await waitFor('[data-test-button="nudge-button"][disabled]'); | |
| assert.dom('[data-test-button="nudge-button"]').hasAttribute('disabled'); | |
| resolveNudge(); | |
| await settled(); | |
| window.fetch = originalFetch; | |
| }); | |
| const originalFetch = window.fetch; | |
| let resolveNudge; | |
| window.fetch = () => | |
| new Promise((resolve) => { | |
| resolveNudge = () => resolve({ ok: true }); | |
| }); | |
| try { | |
| click('[data-test-button="nudge-button"]'); | |
| await waitFor('[data-test-button="nudge-button"][disabled]'); | |
| assert.dom('[data-test-button="nudge-button"]').hasAttribute('disabled'); | |
| resolveNudge(); | |
| await settled(); | |
| } finally { | |
| window.fetch = originalFetch; | |
| } | |
| }); |
🤖 Prompt for AI Agents
In `@tests/integration/components/application/detail-header-test.js` around lines
113 - 129, The test overrides window.fetch (originalFetch) and restores it only
at the end, which leaves the mock in place if an assertion fails; wrap the code
that sets window.fetch, the click('[data-test-button="nudge-button"]'),
waitFor(...) and settled() calls in a try block and move window.fetch =
originalFetch into a finally block so window.fetch is always restored even on
test failures; reference the existing symbols originalFetch, resolveNudge,
window.fetch, click, waitFor, and settled when editing the test.
| const originalFetch = window.fetch; | ||
| window.fetch = () => Promise.resolve({ ok: true }); | ||
|
|
||
| await render(hbs` | ||
| <Application::DetailHeader | ||
| @application={{this.application}} | ||
| @onNudge={{this.onNudge}} | ||
| /> | ||
| `); | ||
|
|
||
| await click('[data-test-button="nudge-button"]'); | ||
|
|
||
| window.fetch = originalFetch; |
There was a problem hiding this comment.
Same cleanup issue: wrap in try/finally.
Apply the same try/finally pattern here to ensure window.fetch is restored even if assertions fail.
🛡️ Proposed fix
const originalFetch = window.fetch;
window.fetch = () => Promise.resolve({ ok: true });
- await render(hbs`
- <Application::DetailHeader
- `@application`={{this.application}}
- `@onNudge`={{this.onNudge}}
- />
- `);
-
- await click('[data-test-button="nudge-button"]');
-
- window.fetch = originalFetch;
+ try {
+ await render(hbs`
+ <Application::DetailHeader
+ `@application`={{this.application}}
+ `@onNudge`={{this.onNudge}}
+ />
+ `);
+
+ await click('[data-test-button="nudge-button"]');
+ } finally {
+ window.fetch = originalFetch;
+ }
});📝 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 originalFetch = window.fetch; | |
| window.fetch = () => Promise.resolve({ ok: true }); | |
| await render(hbs` | |
| <Application::DetailHeader | |
| @application={{this.application}} | |
| @onNudge={{this.onNudge}} | |
| /> | |
| `); | |
| await click('[data-test-button="nudge-button"]'); | |
| window.fetch = originalFetch; | |
| const originalFetch = window.fetch; | |
| window.fetch = () => Promise.resolve({ ok: true }); | |
| try { | |
| await render(hbs` | |
| <Application::DetailHeader | |
| `@application`={{this.application}} | |
| `@onNudge`={{this.onNudge}} | |
| /> | |
| `); | |
| await click('[data-test-button="nudge-button"]'); | |
| } finally { | |
| window.fetch = originalFetch; | |
| } |
🤖 Prompt for AI Agents
In `@tests/integration/components/application/detail-header-test.js` around lines
150 - 162, The test replaces window.fetch with a stub but doesn't guarantee
restoration on failure; update the block that sets const originalFetch =
window.fetch and window.fetch = () => Promise.resolve({ ok: true }) to wrap the
render(...) and await click('[data-test-button="nudge-button"]') calls in a
try/finally so that window.fetch = originalFetch is executed in the finally
clause; locate the replacement around the Application::DetailHeader render in
tests/integration/components/application/detail-header-test.js (symbols:
originalFetch, window.fetch, render, click, '[data-test-button="nudge-button"]')
and move the restoration into finally to ensure cleanup regardless of test
assertions.
| test('displays error toast on 404 response', async function (assert) { | ||
| this.fetchStub | ||
| .onCall(0) | ||
| .resolves(new Response(JSON.stringify({}), { status: 200 })); | ||
| .resolves(new Response(JSON.stringify({ id: 'user1' }), { status: 200 })); | ||
| this.fetchStub | ||
| .onCall(1) | ||
| .resolves(new Response(JSON.stringify({}), { status: 404 })); | ||
| .resolves( | ||
| new Response(JSON.stringify({ applications: [] }), { status: 404 }), | ||
| ); | ||
|
|
||
| const result = await this.route.model({ id: '123' }); | ||
|
|
||
| assert.deepEqual( | ||
| result, | ||
| { application: null, currentUser: null }, | ||
| 'Returns null object for 404', | ||
| { application: null, currentUser: { id: 'user1' } }, | ||
| 'Returns null application for 404 but returns user', | ||
| ); | ||
| assert.ok( | ||
| this.route.toast.error.calledOnce, | ||
| 'Error toast is displayed for 404', | ||
| this.fetchStub.secondCall.calledWith( | ||
| APPLICATIONS_BY_USER_URL('user1'), | ||
| sinon.match.object, | ||
| ), | ||
| 'API call is made to fetch applications by userId', | ||
| ); | ||
| assert.ok(this.toast.error.calledOnce, 'Error toast is displayed for 404'); | ||
| }); |
There was a problem hiding this comment.
Use the stubbed toast reference.
this.toast isn’t defined; only this.route.toast.error is stubbed, so this assertion will fail or miss the call.
🔧 Proposed fix
- assert.ok(this.toast.error.calledOnce, 'Error toast is displayed for 404');
+ assert.ok(
+ this.route.toast.error.calledOnce,
+ 'Error toast is displayed for 404'
+ );📝 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.
| test('displays error toast on 404 response', async function (assert) { | |
| this.fetchStub | |
| .onCall(0) | |
| .resolves(new Response(JSON.stringify({}), { status: 200 })); | |
| .resolves(new Response(JSON.stringify({ id: 'user1' }), { status: 200 })); | |
| this.fetchStub | |
| .onCall(1) | |
| .resolves(new Response(JSON.stringify({}), { status: 404 })); | |
| .resolves( | |
| new Response(JSON.stringify({ applications: [] }), { status: 404 }), | |
| ); | |
| const result = await this.route.model({ id: '123' }); | |
| assert.deepEqual( | |
| result, | |
| { application: null, currentUser: null }, | |
| 'Returns null object for 404', | |
| { application: null, currentUser: { id: 'user1' } }, | |
| 'Returns null application for 404 but returns user', | |
| ); | |
| assert.ok( | |
| this.route.toast.error.calledOnce, | |
| 'Error toast is displayed for 404', | |
| this.fetchStub.secondCall.calledWith( | |
| APPLICATIONS_BY_USER_URL('user1'), | |
| sinon.match.object, | |
| ), | |
| 'API call is made to fetch applications by userId', | |
| ); | |
| assert.ok(this.toast.error.calledOnce, 'Error toast is displayed for 404'); | |
| }); | |
| test('displays error toast on 404 response', async function (assert) { | |
| this.fetchStub | |
| .onCall(0) | |
| .resolves(new Response(JSON.stringify({ id: 'user1' }), { status: 200 })); | |
| this.fetchStub | |
| .onCall(1) | |
| .resolves( | |
| new Response(JSON.stringify({ applications: [] }), { status: 404 }), | |
| ); | |
| const result = await this.route.model({ id: '123' }); | |
| assert.deepEqual( | |
| result, | |
| { application: null, currentUser: { id: 'user1' } }, | |
| 'Returns null application for 404 but returns user', | |
| ); | |
| assert.ok( | |
| this.fetchStub.secondCall.calledWith( | |
| APPLICATIONS_BY_USER_URL('user1'), | |
| sinon.match.object, | |
| ), | |
| 'API call is made to fetch applications by userId', | |
| ); | |
| assert.ok( | |
| this.route.toast.error.calledOnce, | |
| 'Error toast is displayed for 404' | |
| ); | |
| }); |
🤖 Prompt for AI Agents
In `@tests/unit/routes/applications/detail-test.js` around lines 81 - 106, The
assertion is referencing this.toast which isn't defined; update the test to
assert against the stubbed toast on the route (this.route.toast.error) instead
of this.toast. Locate the test "displays error toast on 404 response" and
replace the failing assertion that checks this.toast.error.calledOnce with an
assertion that this.route.toast.error was called (e.g.,
this.route.toast.error.calledOnce) so it uses the actual stubbed method used
when calling this.route.model.
| test('handles missing userId in user data', async function (assert) { | ||
| this.fetchStub | ||
| .onCall(0) | ||
| .resolves( | ||
| new Response(JSON.stringify({ first_name: 'John' }), { status: 200 }), | ||
| ); | ||
|
|
||
| const result = await this.route.model({ id: '123' }); | ||
|
|
||
| assert.deepEqual( | ||
| result, | ||
| { application: null, currentUser: { first_name: 'John' } }, | ||
| 'Returns null application when userId is missing', | ||
| ); | ||
| assert.ok( | ||
| this.toast.error.calledOnce, | ||
| 'Error toast is displayed for missing userId', | ||
| ); |
There was a problem hiding this comment.
Fix toast reference and guard against unintended API call.
this.toast isn’t defined; also consider asserting that the applications endpoint is not called when userId is missing to prevent regressions.
🔧 Proposed fixes
- assert.ok(
- this.toast.error.calledOnce,
- 'Error toast is displayed for missing userId',
- );
+ assert.ok(
+ this.route.toast.error.calledOnce,
+ 'Error toast is displayed for missing userId',
+ );
+ assert.ok(
+ this.fetchStub.calledOnce,
+ 'Does not call applications endpoint without userId',
+ );🤖 Prompt for AI Agents
In `@tests/unit/routes/applications/detail-test.js` around lines 134 - 151, The
test references a missing toast stub and should also assert the applications API
isn't called when userId is missing: ensure the test initializes a toast error
stub on the test context (e.g., set this.toast = { error: sinon.stub() } or
equivalent) before calling this.route.model, keep using this.fetchStub and
this.route.model from the diff, and after invoking this.route.model assert the
toast error stub was called once and that this.fetchStub was not invoked for the
applications endpoint (e.g., assert no calls/matches to a URL containing
'/applications' or that fetchStub.callCount equals the expected number for only
the user fetch).
Date:
change date hereDeveloper Name:
developer name hereIssue Ticket Number:-
Description:
Add description of the PR here
Is Under Feature Flag
Database changes
Breaking changes (If your feature is breaking/missing something please mention pending tickets)
Is Development Tested?
Tested in staging?
Add relevant Screenshot below ( e.g test coverage etc. )