Files
pleno-vue/tests/unit/subuser-management-labels.spec.js
Jeppe BandJeppe Bundgaard 7a84cd9162 Align subuser self-service controls (#234)
## Summary
- Centralize subuser permission capability labels so the grant editor,
table header, and action settings wheel use the same self-service text.
- Add the self-service access control to each grant section in the
subuser action wheel.
- Add focused unit coverage for the shared label helper and subuser
action wheel payload.

## Verification
- `npm run test:unit:single --
tests/unit/subuser-management-labels.spec.js
tests/unit/subuser-grant-permission-nodes.spec.js`
- `npx eslint
src/components/displays/selectors/SubuserGrantPermissionNodes.vue
src/components/displays/superuser/tables/SubusersTable.vue
src/components/session/subuser/subuserPermissionLabels.js
tests/unit/subuser-management-labels.spec.js --quiet`
- `npm run build`

## Visual change previews
### View: Customer subuser management
**Description:** Shows the customer-facing chauffeur table and settings
wheel self-service label aligned to the same wording across table
header, row control, and wheel item.

#### Mobile (390x844)
**Before:** ![Before
mobile](https://raw.githubusercontent.com/copenhagentruckwash/pleno-vue/wb-550fbd4a-subuser-module/visual-previews/subuser-management/mobile-before.png)
**After:** ![After
mobile](https://raw.githubusercontent.com/copenhagentruckwash/pleno-vue/wb-550fbd4a-subuser-module/visual-previews/subuser-management/mobile-after.png)

#### Tablet (768x1024)
**Before:** ![Before
tablet](https://raw.githubusercontent.com/copenhagentruckwash/pleno-vue/wb-550fbd4a-subuser-module/visual-previews/subuser-management/tablet-before.png)
**After:** ![After
tablet](https://raw.githubusercontent.com/copenhagentruckwash/pleno-vue/wb-550fbd4a-subuser-module/visual-previews/subuser-management/tablet-after.png)

#### Desktop (1440x900)
**Before:** ![Before
desktop](https://raw.githubusercontent.com/copenhagentruckwash/pleno-vue/wb-550fbd4a-subuser-module/visual-previews/subuser-management/desktop-before.png)
**After:** ![After
desktop](https://raw.githubusercontent.com/copenhagentruckwash/pleno-vue/wb-550fbd4a-subuser-module/visual-previews/subuser-management/desktop-after.png)

---------

Co-authored-by: Jeppe Bundgaard <jb@truckwash.dk>
2026-07-28 17:06:36 +02:00

168 lines
5.2 KiB
JavaScript

// @vitest-environment jsdom
import { mount } from "@vue/test-utils";
import { describe, expect, it, vi } from "vitest";
import { createI18n } from "vue-i18n";
const translateMock = vi.hoisted(() => {
const translations = {
"common.active": "Active",
"common.inactive": "Inactive",
"common.disabled": "Disabled",
"templates.generated.compat.common.none": "None",
"templates.generated.compat.superuser.driver_contact.missing": "Missing",
"templates.generated.compat.superuser.driver_contact.verified": "Verified",
"templates.generated.compat.superuser.driver_contact.unverified": "Unverified",
"templates.generated.compat.superuser.driver_wheel.account_section": "Account",
"templates.generated.compat.superuser.driver_wheel.contact_section": "Contact",
"templates.generated.compat.superuser.driver_wheel.access_section": "Access",
"templates.generated.compat.superuser.driver_wheel.details_section": "Details",
"templates.generated.compat.superuser.driver_wheel.identity_subsection": "Identity",
"templates.generated.compat.superuser.driver_wheel.system_subsection": "System",
"superuser.driver_access.capabilities.start_selfserve": "Start self-service",
"superuser.driver_access.templates.driver.label": "Driver",
"superuser.driver_access.summary.no_permissions": "No permissions",
"admin.pos.settings_wheel.shortcuts_section": "Shortcuts",
};
return (key, values = undefined) => {
const value = translations[key] || key;
if (!values) {
return value;
}
return Object.entries(values).reduce(
(text, [name, replacement]) => text.replace(`{${name}}`, String(replacement)),
value
);
};
});
vi.mock("@/i18n", () => ({
default: {
global: {
t: translateMock,
},
},
}));
vi.mock("@/components/session/token/SessionUser.vue", () => ({
SessionUser: {
canAccessSuperUser: vi.fn(() => false),
canManageSubusers: vi.fn(() => true),
objects: {
subusers: {
functions: {
permissionSummary: vi.fn(() => "Driver"),
},
},
},
},
}));
const wheelSections = [];
vi.mock("@/components/displays/buttons/ActionSettingsWheelButton.vue", () => ({
default: {
props: ["menuSections"],
setup(props) {
wheelSections.push(props.menuSections);
return () => null;
},
},
}));
import SubusersTable from "@/components/displays/superuser/tables/SubusersTable.vue";
import {
permissionCapabilityLabel,
selfServeAccessLabel,
} from "@/components/session/subuser/subuserPermissionLabels.js";
import { readFileSync } from "node:fs";
import { join } from "node:path";
const mountTable = () =>
mount(SubusersTable, {
props: {
objects: [
{
id: 7,
username: "driver",
name: "Demo Driver",
phone_country_code: 45,
phone: 12345678,
created_at: "2026-07-27 12:00:00",
updated_at: "2026-07-27 12:00:00",
grant_id: 77,
grant_enabled: true,
grant_permissions: ["SELFSERVE_LIST", "SELFSERVE_ADD"],
permission_template_key: "driver",
access_state: "active",
},
],
},
global: {
plugins: [
createI18n({
legacy: false,
locale: "en",
missingWarn: false,
fallbackWarn: false,
messages: { en: {} },
}),
],
stubs: {
"b-switch": {
props: ["modelValue"],
template: '<input type="checkbox" :checked="modelValue" />',
},
"b-tooltip": {
template: "<span><slot /></span>",
},
},
},
});
const flattenItems = (sections) =>
sections.flatMap((section) =>
(section.items || []).flatMap((item) => (item.items ? [item, ...flattenItems([item])] : [item]))
);
describe("subuser management labels", () => {
it("uses one self-service capability label for helpers", () => {
expect(permissionCapabilityLabel("selfserve_add")).toBe("Start self-service");
expect(selfServeAccessLabel()).toBe("Start self-service");
});
it("uses the same self-service label in the table header and settings wheel item", () => {
wheelSections.length = 0;
const wrapper = mountTable();
expect(wrapper.find("thead").text()).toContain("Start self-service");
const wheelItems = flattenItems(wheelSections.at(-1) || []);
expect(wheelItems).toEqual(
expect.arrayContaining([
expect.objectContaining({
key: "subuser-self-serve",
label: "Start self-service",
type: "select",
}),
])
);
});
it("restores the settings-wheel selection when the permission update fails", () => {
const tableSource = readFileSync(
join(process.cwd(), "src/components/displays/superuser/tables/SubusersTable.vue"),
"utf8"
);
const selectSource = readFileSync(
join(process.cwd(), "src/components/displays/buttons/ActionSettingsWheelSelectItem.vue"),
"utf8"
);
expect(tableSource).toContain("if (rejectOnFailure)");
expect(tableSource).toContain("onToggleDognvaskFromSettingsWheel");
expect(selectSource).toContain("localValue.value = normalizeSelectValue(props.value)");
expect(selectSource).toContain("} catch {");
});
});