mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-09 04:50:59 +00:00
feat: let the record's module map filter its own navigation
The other half of the switched-payload change, found by the integration test that asserted the door was gone and got the door. `isNavDestinationVisible` short-circuited on `sharedRecord`, so a switched session skipped the module filter entirely and every destination the grant's scope allowed was offered. That was right while the map was the actor's: a delegate's own preferences must not hide a domain the record granted. The map is the record's now, so the objection is gone and the line is what makes a guardian's toggle visible inside the profile it was set for. Scope decides which doors the grant opens; the map decides which of them the record uses at all. The read-only profile summary needs its marker checked against `!sharedRecord` rather than reached through the removed short-circuit, and the case that pinned the old reading is turned round with the reason written out. Refs #939
This commit is contained in:
1 parent
bed2ab568e
commit
ccca056f64
3 files changed
+75
-16
No files matched your search
@@ -88,19 +88,43 @@ describe("shared-record navigation", () => {
|
||||
).toEqual(["/checkups"]);
|
||||
});
|
||||
|
||||
it("uses the active record scope instead of the actor's module flags", () => {
|
||||
const actorModules = {
|
||||
medications: false,
|
||||
labs: false,
|
||||
mood: false,
|
||||
mentalHealth: false,
|
||||
cycle: false,
|
||||
illness: false,
|
||||
inboundDocuments: false,
|
||||
};
|
||||
/**
|
||||
* Scope and modules are two filters and they answer different questions.
|
||||
*
|
||||
* This case used to assert that the module map was IGNORED inside a shared
|
||||
* record, and the reason was sound at the time: the map `GET /api/auth/me`
|
||||
* published was the ACTOR's, which describes their own dashboard, so letting
|
||||
* it hide a domain the record had granted would have been the actor's
|
||||
* preference deciding somebody else's navigation.
|
||||
*
|
||||
* v1.38.14 (#939) removed the premise rather than the filter. The payload
|
||||
* now resolves the map for the RECORD the session is inside, so the map is
|
||||
* the record's own answer to "what does this record track" — and honouring
|
||||
* it is what makes a guardian's Modules toggle visible inside the profile it
|
||||
* was set for. The scope still decides which doors the grant opens; the map
|
||||
* decides which of them the record uses at all.
|
||||
*/
|
||||
it("applies the record's own module map on top of its scope", () => {
|
||||
const scope = ["medications", "labs"] as const;
|
||||
|
||||
expect(
|
||||
visibleNavDestinations(actorModules, true, true, ["medications"]).map(
|
||||
visibleNavDestinations({}, true, true, [...scope]).map(
|
||||
(destination) => destination.href,
|
||||
),
|
||||
).toEqual(["/medications", "/labs"]);
|
||||
|
||||
// The record tracks medications and not labs. Both are in scope; one door
|
||||
// is offered.
|
||||
expect(
|
||||
visibleNavDestinations({ labs: false }, true, true, [...scope]).map(
|
||||
(destination) => destination.href,
|
||||
),
|
||||
).toEqual(["/medications"]);
|
||||
|
||||
// And the scope still binds: a module the record tracks is not a door the
|
||||
// grant opens.
|
||||
expect(
|
||||
visibleNavDestinations({}, true, true, ["medications"]).map(
|
||||
(destination) => destination.href,
|
||||
),
|
||||
).toEqual(["/medications"]);
|
||||
|
||||
@@ -89,6 +89,32 @@ describe("the destination list under a switch", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("a module the RECORD does not track", () => {
|
||||
/**
|
||||
* #939 — the map `GET /api/auth/me` publishes is the RECORD's while a
|
||||
* session is switched into one, so this filter is what makes a guardian's
|
||||
* toggle visible. Before it, turning Cycle off for a profile left the Cycle
|
||||
* door standing in that profile's own navigation, and the toggle wrote a
|
||||
* column nothing on screen was reading.
|
||||
*/
|
||||
it("drops its destination inside the record", () => {
|
||||
const withCycle = visibleNavDestinations(ALL_MODULES, true, true, null).map(
|
||||
(d) => d.href,
|
||||
);
|
||||
expect(withCycle).toContain("/cycle");
|
||||
|
||||
const withoutCycle = visibleNavDestinations(
|
||||
{ ...ALL_MODULES, cycle: false },
|
||||
true,
|
||||
true,
|
||||
null,
|
||||
).map((d) => d.href);
|
||||
expect(withoutCycle).not.toContain("/cycle");
|
||||
// Only that one door: a module toggle is not a scope change.
|
||||
expect(withoutCycle).toContain("/measurements");
|
||||
});
|
||||
});
|
||||
|
||||
describe("the utility tail under a switch", () => {
|
||||
it("disappears entirely", () => {
|
||||
// Settings holds credentials, integrations, notification channels and
|
||||
|
||||
@@ -342,13 +342,22 @@ function isNavDestinationVisible(
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
// A shared record's server-resolved scope decides which health-domain doors
|
||||
// exist. The actor's module preferences describe their own dashboard and
|
||||
// must not hide a domain the target record explicitly granted.
|
||||
if (sharedRecord) return true;
|
||||
if (d.sharedRecordOnly) return false;
|
||||
// The inverse marker: a destination that exists ONLY inside somebody else's
|
||||
// record (the read-only profile summary). Guarded on `!sharedRecord` rather
|
||||
// than reached only through the shared arm, which is what it relied on
|
||||
// before the module gate below started applying to a shared record too.
|
||||
if (!sharedRecord && d.sharedRecordOnly) return false;
|
||||
if (!d.requiresModule) return true;
|
||||
if (!mounted) return false;
|
||||
// The module map, and whose it is, is the whole point. A shared record used
|
||||
// to skip this line: the grant's scope decided which doors existed, and the
|
||||
// module map was the ACTOR's, which describes their own dashboard and must
|
||||
// not hide a domain the record granted. `GET /api/auth/me` now resolves the
|
||||
// map for the RECORD the session is inside (#939), so the objection is gone
|
||||
// and the line is the one that makes a guardian's toggle visible — without
|
||||
// it, turning Cycle off for a profile left the Cycle door standing in that
|
||||
// profile's own navigation. Scope decides which doors the grant opens;
|
||||
// this decides which of them the record tracks at all.
|
||||
return modules?.[d.requiresModule] !== false;
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user