Skip to content

The three non-blocking loaders at the end of onMounted never run (pins, advisories, policies) #160

Description

@rubenvdlinde

Measured, not inferred

Captured every app_versions URL the browser requested while loading the admin settings page (tests/e2e/pinning.spec.ts, PR #159):

200 /index.php/settings/admin/app_versions
200 /apps/app_versions/css/app_versions-main.css
200 /apps/app_versions/css/main-*.chunk.css
200 /apps/app_versions/js/app_versions-main.mjs
200 /ocs/v2.php/apps/app_versions/api/pats?format=json
200 /ocs/v2.php/apps/app_versions/api/sources?format=json
200 /ocs/v2.php/apps/app_versions/api/cache?format=json
200 /ocs/v2.php/apps/app_versions/api/update-channel?format=json
200 /apps/app_versions/img/app.svg
200 /ocs/v2.php/apps/app_versions/api/apps?format=json

Absent: /api/pins, /api/advisories, /api/policies — which are exactly the last three statements of onMounted:

try {
    await checkUpdateChannel()
    await loadApps()
} finally {
    isLoading.value = false
}
// Kick off advisory correlation, pin state, and auto-update policies after
// the list renders (non-blocking).
void loadAdvisories()
void loadPins()
void loadPolicies()

Everything before that point fires (update-channel, then apps). Everything after it does not.

Visible consequence

pinning.spec.ts:70 has failed on every run: pins stays {}, so the app-card badge's v-if="pinFor(app.id)" never matches and no pin badge renders — even though the pin exists and GET /api/pins returns it when called directly. Advisory badges and auto-update policy state are presumably affected the same way.

What this is NOT

Each ruled out by reading the source, so nobody re-checks:

  • not the API — a direct GET /api/pins returns the pin (asserted in the test before the UI is touched)
  • not Pin::toArray() — it carries no appId, so the + ['appId' => …] union is safe (PHP's + keeps the LEFT operand)
  • not an id mismatch — /api/apps returns 'id' => $appId and /api/pins returns 'appId' => $appId, both the same app id
  • not core-app filtering — coreAppsVisibility defaults to 'show'
  • not a throwing loader — checkUpdateChannel, loadApps and loadAdvisories each wrap their whole body in try/catch
  • not the test harness — the capture is proven by a positive control asserting /api/apps was seen, and the wait uses expect.poll, not a bare assertion

Why static reading did not finish the job

The try has a finally but no catch, so anything thrown by the awaited calls would run finally and then propagate, skipping the three void statements. That is the obvious mechanism — but all three loaders catch internally, so what throws is not visible from the source. It needs a browser console/stack trace from a running instance.

Filed rather than guessed at: changing onMounted blind risks breaking the 92 specs that pass.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

triageAwaiting triage

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions