Skip to content

Improve Not Found error handling#3036

Merged
samuelwei merged 25 commits into
developfrom
86-improve-notfoundexceptions
May 6, 2026
Merged

Improve Not Found error handling#3036
samuelwei merged 25 commits into
developfrom
86-improve-notfoundexceptions

Conversation

@Sabr1n4W

@Sabr1n4W Sabr1n4W commented Apr 9, 2026

Copy link
Copy Markdown
Contributor

Fixes #86
Fixes #1676

Type

  • Bugfix
  • Feature
  • Documentation
  • Refactoring (e.g. Style updates, Test implementation, etc.)
  • Other (please describe):

Checklist

  • Code updated to current develop branch head
  • Passes CI checks
  • Is a part of an issue
  • Tests added for the bugfix or newly implemented feature, describe below why if not
  • Changelog is updated
  • Documentation of code and features exists

Changes

  • Improve 404 error messages
  • Standardize 404 error handling on admin pages
    • On Index pages -> reload list
    • On View pages -> redirect to index page
  • Standardize room not found error handling inside room view
    • Authenticated user -> redirect to room index page
    • Guest -> redirect to 404 error page

Other information

Summary by CodeRabbit

  • New Features

    • Standardized "model not found" error payloads surfaced as localized toast title/details.
  • Bug Fixes

    • Missing resources now consistently redirect (rooms index or 404) and show clear model-not-found feedback instead of ambiguous errors.
  • Improvements

    • UI components emit explicit notFound events to refresh lists and navigation; favorites behavior made configurable to avoid unwanted redirects on missing rooms.

@Sabr1n4W Sabr1n4W self-assigned this Apr 9, 2026
@Sabr1n4W Sabr1n4W linked an issue Apr 9, 2026 that may be closed by this pull request
@coderabbitai

coderabbitai Bot commented Apr 9, 2026

Copy link
Copy Markdown
Contributor

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Backend now returns standardized JSON 404s { message: "model_not_found", model, ids } for model-not-found cases; frontend API, components, routes, and tests updated to interpret that payload, emit notFound events or toasts, and conditionally redirect or reload (room-specific redirect behavior added).

Changes

Cohort / File(s) Summary
Backend exception & translations
app/Exceptions/Handler.php, lang/en/app.php
Handler: return { message: "model_not_found", model, ids } for JSON NotFoundHttpExceptions with prior ModelNotFoundException; added translation keys and singular model labels.
API routes & controllers
routes/api.php, app/Http/Controllers/api/v1/RoomMemberController.php, app/Http/Controllers/api/v1/RoomPersonalizedLinkController.php
Route groups reorganized; added scopeBindings() on room group; route param names changed ({user}{member}, {link}{personalizedLink}); controllers updated to remove pre-check aborts and use renamed params.
Frontend API & constants
resources/js/constants/modelNames.js, resources/js/services/Api.js
New model name constants; Api.error() adds handleModelNotFound(error, options) and routes model_not_found payloads (special-case room redirect, toast), honoring redirectOnRoomModelNotFound option.
Room UI components
resources/js/components/RoomCard.vue, resources/js/components/RoomFavoriteButton.vue
Room favorite button gains redirectOnRoomModelNotFound prop (default true); RoomCard passes false in places; Api error handling option forwarded from component.
Per-resource UI error handling
resources/js/components/RoomTabFiles*.vue, RoomTabMembers*.vue, RoomTabPersonalizedLinks*.vue, RoomTabRecordings*.vue
Refined 404 handling: components now check error.response.data?.model and only run resource-specific “gone/not-member” flows when the response model matches the component’s resource constant.
Settings / admin delete/reset components
resources/js/components/Settings*DeleteButton.vue, SettingsUsersResetPasswordButton.vue
Delete/reset components now detect HTTP 404 and emit a notFound event (closing modals) instead of delegating to generic error handling; defineEmits updated where necessary.
Views / page wiring
resources/js/views/*.vue, resources/js/views/RoomsView.vue
Admin views listen for not-found to refresh or navigate; RoomsView updated to pass new Api option and to redirect authenticated users to rooms index vs guests to /404 for room-model-missing cases.
Frontend tests (Cypress)
tests/Frontend/e2e/**/*.cy.js
Many specs updated to mock 404 responses using { message: "model_not_found", model, ids } and to assert model-specific toasts, redirects, reloads, and notFound-driven UI flows.
Backend feature tests (PHPUnit)
tests/Backend/Feature/api/v1/**/*.php
Feature tests extended to assert model_not_found JSON payloads on 404 when models are missing/deleted across recordings, roles, rooms, files, members, room types, servers, users, etc.
Changelog
CHANGELOG.md
Documented standardized 404 handling entries and added referenced issue links.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested labels

tests, refactor

Suggested reviewers

  • samuelwei
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 53.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main objective of the pull request: improving the handling of HTTP 404 (Not Found) errors throughout the codebase.
Description check ✅ Passed The description is comprehensive and follows the template structure with linked issues, type selection, checklist completion, and detailed changes listed.
Linked Issues check ✅ Passed The pull request successfully implements all requirements from issues #86 and #1676: custom 404 handler returning JSON with model/ids [#86], standardized admin-page 404 handling with index-reload and view-redirect patterns [#1676], and room-view 404 handling with auth-state-based redirects.
Out of Scope Changes check ✅ Passed All changes are directly scoped to improving 404 error handling as specified in linked issues. Backend handler, API responses, frontend error handling, routing, and comprehensive test coverage are all aligned with stated objectives.

✏️ 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 86-improve-notfoundexceptions

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.

@cypress

cypress Bot commented Apr 9, 2026

Copy link
Copy Markdown

PILOS    Run #3005

Run Properties:  status check failed Failed #3005  •  git commit 76ecb4f5f4: Improve Not Found error handling
Project PILOS
Branch Review 86-improve-notfoundexceptions
Run status status check failed Failed #3005
Run duration 07m 38s
Commit git commit 76ecb4f5f4: Improve Not Found error handling
Committer Sabrina Wüst
View all properties for this run ↗︎

Test results
Tests that failed  Failures 1
Tests that were flaky  Flaky 0
Tests that did not run due to a developer annotating a test with .skip  Pending 0
Tests that did not run due to a failure in a mocha hook  Skipped 0
Tests that passed  Passing 631
View all changes introduced in this branch ↗︎

Tests for review

Failed  e2e/RoomsJoinWithLobby.cy.js • 1 failed test • System tests

View Output

Test Artifacts
Room Join with lobby settings > Lobby enabled for guests only Test Replay Screenshots

@codecov

codecov Bot commented Apr 9, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 99.18033% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 96.81%. Comparing base (e59b182) to head (76ecb4f).

Files with missing lines Patch % Lines
resources/js/services/Api.js 94.44% 1 Missing ⚠️
Additional details and impacted files
@@              Coverage Diff              @@
##             develop    #3036      +/-   ##
=============================================
+ Coverage      96.78%   96.81%   +0.02%     
+ Complexity      1940     1939       -1     
=============================================
  Files            465      466       +1     
  Lines          13164    13239      +75     
  Branches        2145     2186      +41     
=============================================
+ Hits           12741    12817      +76     
+ Misses           423      422       -1     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@Sabr1n4W Sabr1n4W marked this pull request as ready for review April 10, 2026 08:01

@coderabbitai coderabbitai Bot left a comment

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.

Actionable comments posted: 8

🧹 Nitpick comments (4)
resources/js/views/RoomsView.vue (1)

442-448: Use router.replace() for terminal not-found redirects.

Keeping push() here leaves the broken room URL in browser history, so Back can reopen the same missing-room redirect loop. replace() avoids that while preserving the new auth-vs-guest behavior.

♻️ Proposed change
-            router.push({ name: "rooms.index" });
+            router.replace({ name: "rooms.index" });
...
-            router.push({ name: "404" });
+            router.replace({ name: "404" });

Also applies to: 524-530

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

In `@resources/js/views/RoomsView.vue` around lines 442 - 448, The 404 handling in
RoomsView.vue currently uses router.push which leaves the broken room URL in
history; replace router.push calls with router.replace in the
error.response.status === env.HTTP_NOT_FOUND block (the branch that routes to
"rooms.index" or "404") so the missing-room redirect is terminal, and make the
same change for the second identical block around the other not-found handler
(the other router.push at lines ~524-530) to avoid back-button loops.
tests/Backend/Feature/api/v1/Room/MembershipTest.php (1)

785-791: Consider a shared assertion helper for model_not_found payloads.

These repeated JSON checks are consistent, but a tiny helper (e.g., assertModelNotFound($response, $model, $ids)) would reduce duplication and keep future contract changes centralized.

Also applies to: 801-807, 848-854, 888-894

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

In `@tests/Backend/Feature/api/v1/Room/MembershipTest.php` around lines 785 - 791,
Extract the repeated JSON assertions for the "model_not_found" payload into a
single test helper (e.g., assertModelNotFound($response, string $model, array
$ids)) on the base TestCase (or a shared trait) and replace the inline
->assertNotFound()->assertJson([...]) chains in MembershipTest with the new
helper; the helper should accept the response (or perform the status check
itself), assert the 404 via assertNotFound() and assert the JSON body contains
'message' => 'model_not_found', 'model' => $model, and 'ids' => $ids so all
occurrences (the deleteJson route('api.v1.rooms.member.destroy', ...) calls and
similar assertions) use this centralized assertion.
tests/Frontend/e2e/RoomsViewMembers.cy.js (1)

488-505: Register the 404 intercept before reload to avoid a flaky branch.

cy.reload() currently runs before the new roomMembersRequest 404 intercept is installed. If the members request is triggered during reload, this scenario may miss the intended stub and become nondeterministic.

🔧 Suggested stabilization
-    cy.interceptRoomViewRequests();
-    cy.reload();
-
-    // Test 404 error (room not found)
-    cy.interceptRoomIndexRequests();
-    cy.intercept("GET", "api/v1/rooms/abc-def-123/member*", {
+    cy.interceptRoomViewRequests();
+    cy.interceptRoomIndexRequests();
+    // Test 404 error (room not found)
+    cy.intercept("GET", "api/v1/rooms/abc-def-123/member*", {
       statusCode: 404,
       body: {
         message: "model_not_found",
         model: "room",
         ids: ["abc-def-123"],
       },
     }).as("roomMembersRequest");
+    cy.reload();
+    cy.wait("@roomRequest");

     cy.get("#tab-members").click();
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/Frontend/e2e/RoomsViewMembers.cy.js` around lines 488 - 505, The 404
stub for the members request is installed after cy.reload(), making the test
flaky if the members request fires during reload; move the cy.intercept("GET",
"api/v1/rooms/abc-def-123/member*", { ... }).as("roomMembersRequest") call to
run before cy.reload() (while keeping cy.interceptRoomViewRequests() /
cy.interceptRoomIndexRequests() as needed) so the "@roomMembersRequest" stub is
active during the page reload and the subsequent cy.get("#tab-members").click()
step.
resources/js/services/Api.js (1)

128-135: Prefer replace() for these 404 redirects.

Using push() keeps the stale room URL in history, so Back lands on the broken page and immediately redirects again. replace() avoids that loop and matches the existing unauthenticated/guests-only handlers.

Suggested change
     if (model === ROOM && options.redirectOnRoomModelNotFound !== false) {
       // Redirect to room index page if user is authenticated, otherwise show 404 page, because
       // unauthenticated user is not able to visit the room index page
       if (this.auth.isAuthenticated) {
-        this.router.push({ name: "rooms.index" });
+        this.router.replace({ name: "rooms.index" });
       } else {
-        this.router.push({ name: "404" });
+        this.router.replace({ name: "404" });
       }
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@resources/js/services/Api.js` around lines 128 - 135, The redirect logic that
handles model === ROOM when options.redirectOnRoomModelNotFound !== false should
use this.router.replace instead of this.router.push so the stale room URL is not
kept in history; update the branches that check this.auth.isAuthenticated and
call this.router.push({ name: "rooms.index" }) or this.router.push({ name: "404"
}) to call this.router.replace(...) with the same route objects to avoid
back-button redirect loops and match guest/unauthenticated handlers.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@lang/en/app.php`:
- Around line 69-72: The translation string for flash.model_not_found.details
contains a leading space; open the array entry for
'flash.model_not_found.details' (the 'details' key under 'model_not_found') and
remove the leading whitespace so the value reads 'Model id: :ids' instead of '
Model id: :ids' to ensure consistent UI text formatting.

In `@resources/js/components/SettingsUsersDeleteButton.vue`:
- Around line 109-114: The 404 case is being double-handled: after emitting
notFound in the catch block (when error.response.status === env.HTTP_NOT_FOUND)
the code still calls api.error(error). Modify the catch logic in the delete
flow/handler (the block using modalVisible.value, emit("notFound") and
api.error) so that once you emit("notFound") and set modalVisible.value = false
you skip calling api.error—either by returning early immediately after
emit("notFound") or by wrapping api.error(error) in an else branch that only
runs when the status is not env.HTTP_NOT_FOUND.

In `@tests/Frontend/e2e/RoomsViewDescription.cy.js`:
- Around line 466-483: The test clicks the edit button immediately after
cy.reload(), which can race with the room view request; update the test to wait
for the relevant intercepted room request to finish before reopening the editor
by awaiting the alias created by cy.interceptRoomViewRequests() or
cy.interceptRoomIndexRequests() (e.g., cy.wait('@roomRequest') or the actual
alias returned by those helpers) before calling
cy.get('[data-test="room-description-edit-button"]').click() so the reload
completes and the 404 save path is exercised reliably.

In `@tests/Frontend/e2e/RoomsViewGeneral.cy.js`:
- Line 727: Fix the typo in the inline comment that currently reads "// Check
wit 404 error (room not found) as guest" by changing "wit" to "with" so it reads
"// Check with 404 error (room not found) as guest"; this update should be made
in the RoomsViewGeneral.cy.js test comment near the guest 404 check.

In `@tests/Frontend/e2e/RoomsViewMembersBulkActions.cy.js`:
- Around line 412-422: The test races selecting members with the members grid
reload; after calling cy.interceptRoomViewRequests() and after clicking
'#tab-members' (or immediately after cy.reload()/reopening the tab), wait for
the intercepted members request alias '@roomMembersRequest' before interacting
with '[data-test="room-members-select-all-checkbox"]'; update the three
occurrences (around cy.interceptRoomViewRequests(), cy.reload(),
cy.get("#tab-members").click(), and the select-all click) to include a
cy.wait("@roomMembersRequest") so the members grid is fully loaded before
selection.

In `@tests/Frontend/e2e/RoomsViewRecordingsRecordingActions.cy.js`:
- Around line 970-1001: After reloading and clicking the recordings tab you must
wait for the refreshed recordings request before opening the delete dialog to
avoid the race; after invoking cy.reload() and/or after
cy.get("#tab-recordings").click(), add a cy.wait for the recordings intercept
alias (the `@roomRecordingsRequest` created by cy.interceptRoomRecordingsRequests)
and only then proceed to find and click the delete button and confirm the dialog
so the subsequent 404 assertion targets the refreshed list.

In `@tests/Frontend/e2e/RoomsViewSettings.cy.js`:
- Around line 2372-2378: The current cy.url().should("include", "/rooms") can
match both /rooms and /rooms/abc-def-123; replace it with a stricter assertion
that the pathname is exactly the rooms index (e.g. use
cy.location('pathname').should('match', /^\/rooms\/?$/) or assert equality to
'/rooms') so the test fails if the browser is still on the room detail URL;
update the assertion that currently calls cy.url() to use
cy.location('pathname') and the regexp or exact equals check, leaving the
cy.checkToastMessage call unchanged.
- Around line 1691-1702: The PUT intercept created with cy.intercept("PUT",
"api/v1/rooms/abc-def-123", ...) is missing the alias expected by the later
cy.wait("@roomSettingsSaveRequest"); update the intercept call to attach the
alias by calling .as("roomSettingsSaveRequest") so the cy.wait can find the
request; you should modify the cy.intercept invocation in
RoomsViewSettings.cy.js to include .as("roomSettingsSaveRequest") to match the
existing cy.wait.

---

Nitpick comments:
In `@resources/js/services/Api.js`:
- Around line 128-135: The redirect logic that handles model === ROOM when
options.redirectOnRoomModelNotFound !== false should use this.router.replace
instead of this.router.push so the stale room URL is not kept in history; update
the branches that check this.auth.isAuthenticated and call this.router.push({
name: "rooms.index" }) or this.router.push({ name: "404" }) to call
this.router.replace(...) with the same route objects to avoid back-button
redirect loops and match guest/unauthenticated handlers.

In `@resources/js/views/RoomsView.vue`:
- Around line 442-448: The 404 handling in RoomsView.vue currently uses
router.push which leaves the broken room URL in history; replace router.push
calls with router.replace in the error.response.status === env.HTTP_NOT_FOUND
block (the branch that routes to "rooms.index" or "404") so the missing-room
redirect is terminal, and make the same change for the second identical block
around the other not-found handler (the other router.push at lines ~524-530) to
avoid back-button loops.

In `@tests/Backend/Feature/api/v1/Room/MembershipTest.php`:
- Around line 785-791: Extract the repeated JSON assertions for the
"model_not_found" payload into a single test helper (e.g.,
assertModelNotFound($response, string $model, array $ids)) on the base TestCase
(or a shared trait) and replace the inline ->assertNotFound()->assertJson([...])
chains in MembershipTest with the new helper; the helper should accept the
response (or perform the status check itself), assert the 404 via
assertNotFound() and assert the JSON body contains 'message' =>
'model_not_found', 'model' => $model, and 'ids' => $ids so all occurrences (the
deleteJson route('api.v1.rooms.member.destroy', ...) calls and similar
assertions) use this centralized assertion.

In `@tests/Frontend/e2e/RoomsViewMembers.cy.js`:
- Around line 488-505: The 404 stub for the members request is installed after
cy.reload(), making the test flaky if the members request fires during reload;
move the cy.intercept("GET", "api/v1/rooms/abc-def-123/member*", { ...
}).as("roomMembersRequest") call to run before cy.reload() (while keeping
cy.interceptRoomViewRequests() / cy.interceptRoomIndexRequests() as needed) so
the "@roomMembersRequest" stub is active during the page reload and the
subsequent cy.get("#tab-members").click() step.
🪄 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: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 571f1968-c615-451e-b8a8-5e3dc5fa57af

📥 Commits

Reviewing files that changed from the base of the PR and between 84b05fc and fb9b10a.

📒 Files selected for processing (85)
  • app/Exceptions/Handler.php
  • app/Http/Controllers/api/v1/RoomMemberController.php
  • app/Http/Controllers/api/v1/RoomPersonalizedLinkController.php
  • lang/en/app.php
  • resources/js/components/RoomCard.vue
  • resources/js/components/RoomFavoriteButton.vue
  • resources/js/components/RoomTabFilesDeleteButton.vue
  • resources/js/components/RoomTabFilesEditButton.vue
  • resources/js/components/RoomTabMembersDeleteButton.vue
  • resources/js/components/RoomTabMembersEditButton.vue
  • resources/js/components/RoomTabPersonalizedLinksDeleteButton.vue
  • resources/js/components/RoomTabPersonalizedLinksEditButton.vue
  • resources/js/components/RoomTabRecordingsDeleteButton.vue
  • resources/js/components/RoomTabRecordingsEditButton.vue
  • resources/js/components/SettingsRolesDeleteButton.vue
  • resources/js/components/SettingsServerPoolsDeleteButton.vue
  • resources/js/components/SettingsServersDeleteButton.vue
  • resources/js/components/SettingsUsersDeleteButton.vue
  • resources/js/components/SettingsUsersResetPasswordButton.vue
  • resources/js/constants/modelNames.js
  • resources/js/services/Api.js
  • resources/js/views/AdminRolesIndex.vue
  • resources/js/views/AdminRolesView.vue
  • resources/js/views/AdminRoomTypesView.vue
  • resources/js/views/AdminServerPoolsIndex.vue
  • resources/js/views/AdminServerPoolsView.vue
  • resources/js/views/AdminServersIndex.vue
  • resources/js/views/AdminServersView.vue
  • resources/js/views/AdminUsersIndex.vue
  • resources/js/views/AdminUsersView.vue
  • resources/js/views/RoomsView.vue
  • routes/api.php
  • tests/Backend/Feature/api/v1/RecordingTest.php
  • tests/Backend/Feature/api/v1/RoleTest.php
  • tests/Backend/Feature/api/v1/Room/FileTest.php
  • tests/Backend/Feature/api/v1/Room/MembershipTest.php
  • tests/Backend/Feature/api/v1/Room/RoomDescriptionTest.php
  • tests/Backend/Feature/api/v1/Room/RoomPersonalizedLinkTest.php
  • tests/Backend/Feature/api/v1/Room/RoomTest.php
  • tests/Backend/Feature/api/v1/RoomTypeTest.php
  • tests/Backend/Feature/api/v1/ServerPoolTest.php
  • tests/Backend/Feature/api/v1/ServerTest.php
  • tests/Backend/Feature/api/v1/UserTest.php
  • tests/Frontend/e2e/AdminRolesEdit.cy.js
  • tests/Frontend/e2e/AdminRolesIndexRoleActions.cy.js
  • tests/Frontend/e2e/AdminRolesView.cy.js
  • tests/Frontend/e2e/AdminRolesViewRoleActions.cy.js
  • tests/Frontend/e2e/AdminRoomTypesEdit.cy.js
  • tests/Frontend/e2e/AdminRoomTypesIndexRoomTypeActions.cy.js
  • tests/Frontend/e2e/AdminRoomTypesView.cy.js
  • tests/Frontend/e2e/AdminRoomTypesViewRoomTypeActions.cy.js
  • tests/Frontend/e2e/AdminServerPoolsEdit.cy.js
  • tests/Frontend/e2e/AdminServerPoolsIndexServerPoolActions.cy.js
  • tests/Frontend/e2e/AdminServerPoolsView.cy.js
  • tests/Frontend/e2e/AdminServerPoolsViewServerPoolActions.cy.js
  • tests/Frontend/e2e/AdminServersEdit.cy.js
  • tests/Frontend/e2e/AdminServersIndexServerActions.cy.js
  • tests/Frontend/e2e/AdminServersView.cy.js
  • tests/Frontend/e2e/AdminServersViewServerActions.cy.js
  • tests/Frontend/e2e/AdminUsersEdit.cy.js
  • tests/Frontend/e2e/AdminUsersEditBase.cy.js
  • tests/Frontend/e2e/AdminUsersEditEmail.cy.js
  • tests/Frontend/e2e/AdminUsersEditOthers.cy.js
  • tests/Frontend/e2e/AdminUsersEditSecurity.cy.js
  • tests/Frontend/e2e/AdminUsersIndexUserActions.cy.js
  • tests/Frontend/e2e/AdminUsersView.cy.js
  • tests/Frontend/e2e/AdminUsersViewUserActions.cy.js
  • tests/Frontend/e2e/RoomsIndex.cy.js
  • tests/Frontend/e2e/RoomsViewDescription.cy.js
  • tests/Frontend/e2e/RoomsViewFiles.cy.js
  • tests/Frontend/e2e/RoomsViewFilesFileActions.cy.js
  • tests/Frontend/e2e/RoomsViewGeneral.cy.js
  • tests/Frontend/e2e/RoomsViewHistory.cy.js
  • tests/Frontend/e2e/RoomsViewHistoryMeetingActions.cy.js
  • tests/Frontend/e2e/RoomsViewMeetings.cy.js
  • tests/Frontend/e2e/RoomsViewMembers.cy.js
  • tests/Frontend/e2e/RoomsViewMembersBulkActions.cy.js
  • tests/Frontend/e2e/RoomsViewMembersMemberActions.cy.js
  • tests/Frontend/e2e/RoomsViewPersonalizedLinks.cy.js
  • tests/Frontend/e2e/RoomsViewPersonalizedLinksTokenActions.cy.js
  • tests/Frontend/e2e/RoomsViewRecordings.cy.js
  • tests/Frontend/e2e/RoomsViewRecordingsRecordingActions.cy.js
  • tests/Frontend/e2e/RoomsViewSettings.cy.js
  • tests/Frontend/e2e/RoomsViewStreaming.cy.js
  • tests/Frontend/e2e/RoomsViewStreamingConfigActions.cy.js

Comment thread lang/en/app.php
Comment thread resources/js/components/SettingsUsersDeleteButton.vue
Comment thread tests/Frontend/e2e/RoomsViewDescription.cy.js
Comment thread tests/Frontend/e2e/RoomsViewGeneral.cy.js Outdated
Comment thread tests/Frontend/e2e/RoomsViewMembersBulkActions.cy.js
Comment thread tests/Frontend/e2e/RoomsViewRecordingsRecordingActions.cy.js
Comment thread tests/Frontend/e2e/RoomsViewSettings.cy.js
Comment thread tests/Frontend/e2e/RoomsViewSettings.cy.js
@samuelwei samuelwei added the frontend Pull requests that update Javascript code label Apr 15, 2026

@coderabbitai coderabbitai Bot left a comment

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.

🧹 Nitpick comments (4)
tests/Backend/Feature/api/v1/UserTest.php (1)

1607-1608: Optional: also assert payload shape for the disabled-local-auth 404.

For consistency with the other assertNotFound() checks in this file, consider whether this 404 (triggered by disabling local auth rather than a missing model) should also be pinned to a specific payload — or explicitly documented as distinct from model_not_found. Not a blocker; just avoids future ambiguity if the handler contract evolves.

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

In `@tests/Backend/Feature/api/v1/UserTest.php` around lines 1607 - 1608, The 404
assertion for the disabled-local-auth case should also validate the JSON payload
shape (or specific error code) to match other assertNotFound() checks: after the
existing $this->actingAs($user)->postJson(route('api.v1.users.password.reset',
['user' => $resetUser]))->assertNotFound(), chain an assertJson (or
assertJsonStructure) that pins the error payload (e.g. the same keys/structure
other tests use like ['message' => ..., 'errors' => ...] or the specific error
code string used for disabled local auth); update the assertion on the
postJson(...) call so it both assertsNotFound() and then asserts the expected
JSON payload shape or error code to make the handler contract explicit.
tests/Frontend/e2e/RoomsViewGeneral.cy.js (2)

3225-3243: Consider also asserting the structured model_not_found path on reload.

This test covers the legacy/raw Laravel 404 message ("No query results for model [App\\Room]...") producing a generic app.flash.server_error toast on reload. Per issue #86, the backend now returns a structured model_not_found payload for AJAX 404s, which other tests in this file exercise for other endpoints (auth/membership/favorites). For completeness/parity, consider adding a reload case that returns the model_not_found payload and asserts the app.flash.model_not_found.* toast, so both 404 shapes are covered for the reload path as well.

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

In `@tests/Frontend/e2e/RoomsViewGeneral.cy.js` around lines 3225 - 3243, Add a
second reload scenario that mirrors the existing reload test but intercepts the
GET to "api/v1/rooms/abc-def-123" with the structured model_not_found payload
(e.g. { error: "model_not_found", model: "Room", id: "abc-def-123" } or whatever
shape other tests use), then trigger the reload via
cy.get('[data-test="reload-room-button"]').click(), wait for the intercepted
"@roomRequest" and assert the redirect to "/404" as before, and replace or add a
cy.checkToastMessage assertion that expects the localized model-not-found toast
keys (e.g. 'app.flash.model_not_found.title' /
'app.flash.model_not_found.message' or the same pattern used in
auth/membership/favorites tests) so both legacy and structured 404 payloads are
covered for the reload path.

3068-3073: Same suggestion for the initial-visit "room not found" test.

This scenario asserts only the raw-message 404 path (generic server_error toast + redirect). Consider adding a parallel case where the backend returns the structured model_not_found payload, so the specialized app.flash.model_not_found.* toast path on initial room load is exercised here too (currently only tested via auth/membership/favorites endpoints).

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

In `@tests/Frontend/e2e/RoomsViewGeneral.cy.js` around lines 3068 - 3073, Add a
parallel intercept for the initial-visit "room not found" test that returns the
structured model_not_found payload instead of the raw 404 body so the
specialized app.flash.model_not_found.* toast path is exercised; specifically,
duplicate or augment the existing cy.intercept("GET",
"api/v1/rooms/abc-def-123", ...) in RoomsViewGeneral.cy.js to return a JSON body
with a type or error code indicating "model_not_found" and include the model
("App\\Room") and id ("abc-def-123"); then update the test assertions to expect
the model_not_found flash toast and the same redirect behavior.
tests/Backend/Feature/api/v1/Room/RoomPersonalizedLinkTest.php (1)

507-560: Slight redundancy between "delete again" cases.

Lines 523–530 already assert the repeated-delete 404 contract. The new block at lines 551–560 repeats the same assertion for the superuser-permission path. If the intent is to show it works across permission contexts, consider keeping it; otherwise it can be dropped to reduce noise. Not blocking.

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

In `@tests/Backend/Feature/api/v1/Room/RoomPersonalizedLinkTest.php` around lines
507 - 560, In RoomPersonalizedLinkTest remove the redundant "Test delete again"
block that repeats the same assertNotFound/assertJson after the superuser
delete; specifically delete the final deleteJson(...) ->assertNotFound()
->assertJson(...) sequence (the repeated call to
route('api.v1.rooms.personalizedLinks.destroy', ['room' => $this->room,
'personalizedLink' => $link])) since the repeated-delete 404 is already asserted
earlier.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@tests/Backend/Feature/api/v1/Room/RoomPersonalizedLinkTest.php`:
- Around line 507-560: In RoomPersonalizedLinkTest remove the redundant "Test
delete again" block that repeats the same assertNotFound/assertJson after the
superuser delete; specifically delete the final deleteJson(...)
->assertNotFound() ->assertJson(...) sequence (the repeated call to
route('api.v1.rooms.personalizedLinks.destroy', ['room' => $this->room,
'personalizedLink' => $link])) since the repeated-delete 404 is already asserted
earlier.

In `@tests/Backend/Feature/api/v1/UserTest.php`:
- Around line 1607-1608: The 404 assertion for the disabled-local-auth case
should also validate the JSON payload shape (or specific error code) to match
other assertNotFound() checks: after the existing
$this->actingAs($user)->postJson(route('api.v1.users.password.reset', ['user' =>
$resetUser]))->assertNotFound(), chain an assertJson (or assertJsonStructure)
that pins the error payload (e.g. the same keys/structure other tests use like
['message' => ..., 'errors' => ...] or the specific error code string used for
disabled local auth); update the assertion on the postJson(...) call so it both
assertsNotFound() and then asserts the expected JSON payload shape or error code
to make the handler contract explicit.

In `@tests/Frontend/e2e/RoomsViewGeneral.cy.js`:
- Around line 3225-3243: Add a second reload scenario that mirrors the existing
reload test but intercepts the GET to "api/v1/rooms/abc-def-123" with the
structured model_not_found payload (e.g. { error: "model_not_found", model:
"Room", id: "abc-def-123" } or whatever shape other tests use), then trigger the
reload via cy.get('[data-test="reload-room-button"]').click(), wait for the
intercepted "@roomRequest" and assert the redirect to "/404" as before, and
replace or add a cy.checkToastMessage assertion that expects the localized
model-not-found toast keys (e.g. 'app.flash.model_not_found.title' /
'app.flash.model_not_found.message' or the same pattern used in
auth/membership/favorites tests) so both legacy and structured 404 payloads are
covered for the reload path.
- Around line 3068-3073: Add a parallel intercept for the initial-visit "room
not found" test that returns the structured model_not_found payload instead of
the raw 404 body so the specialized app.flash.model_not_found.* toast path is
exercised; specifically, duplicate or augment the existing cy.intercept("GET",
"api/v1/rooms/abc-def-123", ...) in RoomsViewGeneral.cy.js to return a JSON body
with a type or error code indicating "model_not_found" and include the model
("App\\Room") and id ("abc-def-123"); then update the test assertions to expect
the model_not_found flash toast and the same redirect behavior.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: b296c303-bdfe-4dd2-b104-59e6fa9a5c6f

📥 Commits

Reviewing files that changed from the base of the PR and between 8c048e4 and 3712ea3.

📒 Files selected for processing (21)
  • CHANGELOG.md
  • app/Http/Controllers/api/v1/RoomMemberController.php
  • app/Http/Controllers/api/v1/RoomPersonalizedLinkController.php
  • lang/en/app.php
  • resources/js/views/AdminServersIndex.vue
  • resources/js/views/RoomsView.vue
  • tests/Backend/Feature/api/v1/RecordingTest.php
  • tests/Backend/Feature/api/v1/RoleTest.php
  • tests/Backend/Feature/api/v1/Room/FileTest.php
  • tests/Backend/Feature/api/v1/Room/MembershipTest.php
  • tests/Backend/Feature/api/v1/Room/RoomPersonalizedLinkTest.php
  • tests/Backend/Feature/api/v1/ServerPoolTest.php
  • tests/Backend/Feature/api/v1/ServerTest.php
  • tests/Backend/Feature/api/v1/UserTest.php
  • tests/Frontend/e2e/AdminServersIndexServerActions.cy.js
  • tests/Frontend/e2e/RoomsViewFiles.cy.js
  • tests/Frontend/e2e/RoomsViewFilesFileActions.cy.js
  • tests/Frontend/e2e/RoomsViewGeneral.cy.js
  • tests/Frontend/e2e/RoomsViewMeetings.cy.js
  • tests/Frontend/e2e/RoomsViewRecordings.cy.js
  • tests/Frontend/e2e/RoomsViewRecordingsRecordingActions.cy.js
✅ Files skipped from review due to trivial changes (3)
  • CHANGELOG.md
  • tests/Frontend/e2e/RoomsViewRecordings.cy.js
  • tests/Backend/Feature/api/v1/Room/FileTest.php
🚧 Files skipped from review as they are similar to previous changes (13)
  • resources/js/views/AdminServersIndex.vue
  • tests/Backend/Feature/api/v1/RoleTest.php
  • tests/Backend/Feature/api/v1/ServerTest.php
  • lang/en/app.php
  • resources/js/views/RoomsView.vue
  • tests/Frontend/e2e/RoomsViewFiles.cy.js
  • tests/Backend/Feature/api/v1/RecordingTest.php
  • tests/Frontend/e2e/AdminServersIndexServerActions.cy.js
  • tests/Frontend/e2e/RoomsViewRecordingsRecordingActions.cy.js
  • tests/Frontend/e2e/RoomsViewFilesFileActions.cy.js
  • tests/Frontend/e2e/RoomsViewMeetings.cy.js
  • app/Http/Controllers/api/v1/RoomPersonalizedLinkController.php
  • tests/Backend/Feature/api/v1/Room/MembershipTest.php

@Sabr1n4W Sabr1n4W requested a review from samuelwei April 17, 2026 14:41
Comment thread lang/en/app.php
Comment thread lang/en/app.php Outdated
Comment thread app/Exceptions/Handler.php
Comment thread resources/js/services/Api.js Outdated
Comment thread resources/js/views/RoomsView.vue Outdated
Comment thread resources/js/views/RoomsView.vue Outdated
Sabr1n4W and others added 4 commits April 28, 2026 11:36
@Sabr1n4W Sabr1n4W requested a review from samuelwei May 6, 2026 07:14
@samuelwei samuelwei merged commit 25bd39a into develop May 6, 2026
21 of 23 checks passed
@samuelwei samuelwei deleted the 86-improve-notfoundexceptions branch May 6, 2026 12:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

frontend Pull requests that update Javascript code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

404 error handling on admin pages Improve NotFoundExceptions

2 participants