Move backoffice department selector into title
This commit is contained in:
@@ -29,8 +29,8 @@ export const sourceMappings = [
|
||||
projects: chromiumProjects,
|
||||
},
|
||||
{
|
||||
name: "backoffice",
|
||||
patterns: [/^src\/views\/backoffice\//u],
|
||||
name: "limited-backoffice",
|
||||
patterns: [/^src\/views\/backoffice\//u, /^src\/services\/limitedBackoffice\.js$/u],
|
||||
specs: ["tests/e2e/limited-backoffice.spec.ts"],
|
||||
projects: chromiumProjects,
|
||||
},
|
||||
|
||||
@@ -442,6 +442,12 @@ export const router = createRouter({
|
||||
},
|
||||
{
|
||||
name: 'limitedBackofficeEmployees',
|
||||
path: '/backoffice/departments/:departmentId/employees',
|
||||
component: LimitedBackofficeEmployees,
|
||||
meta: { middleware: authMiddleware, titleKey: 'templates.limited_backoffice.employees.title' }
|
||||
},
|
||||
{
|
||||
name: 'limitedBackofficeEmployeesLegacy',
|
||||
path: '/backoffice/employees',
|
||||
component: LimitedBackofficeEmployees,
|
||||
meta: { middleware: authMiddleware, titleKey: 'templates.limited_backoffice.employees.title' }
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<script setup>
|
||||
import { computed, onMounted, ref } from "vue";
|
||||
import { computed, onMounted, ref, watch } from "vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import { useRoute, useRouter } from "vue-router";
|
||||
import RestrictedPageWrapper from "@/components/page/wrappers/RestrictedPageWrapper.vue";
|
||||
import { SessionUser } from "@/components/session/token/SessionUser.vue";
|
||||
import LimitedBackofficeLayout from "@/views/backoffice/components/LimitedBackofficeLayout.vue";
|
||||
@@ -33,6 +34,8 @@ const loadQRCodeModule = () => {
|
||||
};
|
||||
|
||||
const { t } = useI18n();
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
const departments = ref([]);
|
||||
const roles = ref([]);
|
||||
const employees = ref([]);
|
||||
@@ -60,6 +63,19 @@ const form = ref({
|
||||
department_ids: [],
|
||||
});
|
||||
|
||||
const selectedDepartmentId = computed(() => {
|
||||
const departmentId = Number.parseInt(String(route.params.departmentId ?? ""), 10);
|
||||
return Number.isInteger(departmentId) && departmentId > 0 ? departmentId : null;
|
||||
});
|
||||
|
||||
const selectedDepartment = computed(() =>
|
||||
departments.value.find((department) => Number(department.id) === selectedDepartmentId.value) || null
|
||||
);
|
||||
|
||||
const hasForbiddenDepartment = computed(
|
||||
() => selectedDepartmentId.value !== null && departments.value.length > 0 && !selectedDepartment.value
|
||||
);
|
||||
|
||||
const roleMessages = computed(() => ({
|
||||
viewer: {
|
||||
label: t("templates.limited_backoffice.roles.viewer.label"),
|
||||
@@ -272,11 +288,23 @@ const sanitizePhone = (event) => {
|
||||
|
||||
const absoluteLoginLink = (loginPath) => new URL(String(loginPath || ""), window.location.origin).toString();
|
||||
|
||||
const visibleEmployees = computed(() => {
|
||||
if (!selectedDepartmentId.value) {
|
||||
return employees.value;
|
||||
}
|
||||
|
||||
return employees.value.filter((employee) =>
|
||||
(employee.departments || []).some((department) => Number(department.id) === selectedDepartmentId.value)
|
||||
);
|
||||
});
|
||||
|
||||
const loadEmployees = async () => {
|
||||
const response = await getLimitedBackofficeEmployees({ includeInactive: includeInactive.value });
|
||||
employees.value = unwrapLimitedBackofficeResponse(response) || [];
|
||||
};
|
||||
|
||||
const defaultDepartmentIds = () => (selectedDepartment.value?.id ? [Number(selectedDepartment.value.id)] : []);
|
||||
|
||||
const loadData = async () => {
|
||||
loading.value = true;
|
||||
errorMessage.value = "";
|
||||
@@ -291,6 +319,18 @@ const loadData = async () => {
|
||||
if (!ROLE_KEYS.includes(form.value.role_key) || !roles.value.some((role) => role.key === form.value.role_key)) {
|
||||
form.value.role_key = roles.value[0]?.key || "viewer";
|
||||
}
|
||||
|
||||
const firstDepartment = departments.value[0] || null;
|
||||
if (!selectedDepartmentId.value && firstDepartment?.id) {
|
||||
await router.replace(`/backoffice/departments/${firstDepartment.id}/employees`);
|
||||
}
|
||||
|
||||
resetForm();
|
||||
if (hasForbiddenDepartment.value) {
|
||||
employees.value = [];
|
||||
return;
|
||||
}
|
||||
|
||||
await loadEmployees();
|
||||
} catch (error) {
|
||||
errorMessage.value = limitedBackofficeErrorMessage(
|
||||
@@ -312,7 +352,7 @@ const resetForm = () => {
|
||||
phone: "",
|
||||
password: "",
|
||||
role_key: roles.value[0]?.key || "viewer",
|
||||
department_ids: [],
|
||||
department_ids: defaultDepartmentIds(),
|
||||
};
|
||||
};
|
||||
|
||||
@@ -479,7 +519,16 @@ const closeEmployeeLoginLink = () => {
|
||||
loginLinkCopyMessage.value = "";
|
||||
};
|
||||
|
||||
const changeDepartment = async (departmentId) => {
|
||||
await router.push(`/backoffice/departments/${departmentId}/employees`);
|
||||
};
|
||||
|
||||
const reloadEmployees = async () => {
|
||||
if (hasForbiddenDepartment.value) {
|
||||
employees.value = [];
|
||||
return;
|
||||
}
|
||||
|
||||
loading.value = true;
|
||||
errorMessage.value = "";
|
||||
try {
|
||||
@@ -497,12 +546,36 @@ const reloadEmployees = async () => {
|
||||
onMounted(() => {
|
||||
void loadData();
|
||||
});
|
||||
|
||||
watch(
|
||||
() => selectedDepartmentId.value,
|
||||
async (nextDepartmentId, previousDepartmentId) => {
|
||||
if (nextDepartmentId === previousDepartmentId || loading.value || departments.value.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
resetForm();
|
||||
await reloadEmployees();
|
||||
}
|
||||
);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<RestrictedPageWrapper :hasPermission="SessionUser.hasPermission('limited_backoffice_access')">
|
||||
<LimitedBackofficeLayout active-tab="employees" :departments="departments">
|
||||
<div class="columns is-variable is-5">
|
||||
<LimitedBackofficeLayout
|
||||
active-tab="employees"
|
||||
:departments="departments"
|
||||
:selected-department-id="selectedDepartmentId"
|
||||
:loading-departments="loading"
|
||||
show-department-switcher
|
||||
@change-department="changeDepartment"
|
||||
>
|
||||
<div v-if="hasForbiddenDepartment" class="notification is-danger is-light" data-testid="limited-employees-forbidden">
|
||||
<strong>{{ t("templates.limited_backoffice.forbidden.title") }}</strong>
|
||||
<p>{{ t("templates.limited_backoffice.forbidden.department") }}</p>
|
||||
</div>
|
||||
|
||||
<div v-else class="columns is-variable is-5">
|
||||
<div class="column is-5">
|
||||
<form class="box limited-employee-form" data-testid="limited-employee-form" @submit.prevent="saveEmployee">
|
||||
<h2 class="title is-5">
|
||||
@@ -726,11 +799,15 @@ onMounted(() => {
|
||||
{{ successMessage }}
|
||||
</div>
|
||||
|
||||
<div v-if="!loading && employees.length === 0" class="notification is-light" data-testid="limited-employees-empty">
|
||||
<div
|
||||
v-if="!loading && visibleEmployees.length === 0"
|
||||
class="notification is-light"
|
||||
data-testid="limited-employees-empty"
|
||||
>
|
||||
{{ t("templates.limited_backoffice.employees.empty") }}
|
||||
</div>
|
||||
|
||||
<div v-if="!loading && employees.length > 0" class="table-container">
|
||||
<div v-if="!loading && visibleEmployees.length > 0" class="table-container">
|
||||
<table class="table is-fullwidth is-hoverable limited-employees-table" data-testid="limited-employees-table">
|
||||
<thead>
|
||||
<tr>
|
||||
@@ -743,7 +820,7 @@ onMounted(() => {
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="employee in employees" :key="employee.id" :data-testid="`limited-employee-row-${employee.id}`">
|
||||
<tr v-for="employee in visibleEmployees" :key="employee.id" :data-testid="`limited-employee-row-${employee.id}`">
|
||||
<td>
|
||||
<span class="tag is-light" :data-testid="`limited-employee-user-id-${employee.id}`">
|
||||
{{ employee.user_id || employee.id }}
|
||||
|
||||
@@ -17,6 +17,10 @@ const departments = ref([]);
|
||||
const loading = ref(false);
|
||||
const errorMessage = ref("");
|
||||
|
||||
const changeDepartment = async (departmentId) => {
|
||||
await router.push(`/backoffice/departments/${departmentId}/prices`);
|
||||
};
|
||||
|
||||
const loadDepartments = async () => {
|
||||
loading.value = true;
|
||||
errorMessage.value = "";
|
||||
|
||||
@@ -35,6 +35,12 @@ const pricesRoute = computed(() =>
|
||||
: "/backoffice"
|
||||
);
|
||||
|
||||
const employeesRoute = computed(() =>
|
||||
props.selectedDepartmentId
|
||||
? `/backoffice/departments/${encodeURIComponent(String(props.selectedDepartmentId))}/employees`
|
||||
: "/backoffice/employees"
|
||||
);
|
||||
|
||||
const tabs = computed(() => [
|
||||
{
|
||||
key: "prices",
|
||||
@@ -44,7 +50,7 @@ const tabs = computed(() => [
|
||||
{
|
||||
key: "employees",
|
||||
label: t("templates.limited_backoffice.nav.employees"),
|
||||
to: "/backoffice/employees",
|
||||
to: employeesRoute.value,
|
||||
},
|
||||
]);
|
||||
|
||||
@@ -63,7 +69,33 @@ const selectDepartment = (event) => {
|
||||
<PageTitle
|
||||
:title="t('templates.limited_backoffice.title')"
|
||||
:subtitle="t('templates.limited_backoffice.subtitle')"
|
||||
/>
|
||||
>
|
||||
<template #buttons>
|
||||
<div v-if="props.showDepartmentSwitcher" class="field limited-backoffice__department-field">
|
||||
<label class="label is-sr-only" for="limited-backoffice-department">
|
||||
{{ t("templates.limited_backoffice.department") }}
|
||||
</label>
|
||||
<div class="control">
|
||||
<div class="select is-small is-fullwidth">
|
||||
<select
|
||||
id="limited-backoffice-department"
|
||||
:value="props.selectedDepartmentId || ''"
|
||||
:disabled="props.loadingDepartments || props.departments.length === 0"
|
||||
data-testid="limited-backoffice-department-select"
|
||||
@change="selectDepartment"
|
||||
>
|
||||
<option value="" disabled>
|
||||
{{ t("templates.limited_backoffice.select_department") }}
|
||||
</option>
|
||||
<option v-for="department in props.departments" :key="department.id" :value="department.id">
|
||||
{{ department.name }}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</PageTitle>
|
||||
|
||||
<div class="limited-backoffice__toolbar">
|
||||
<div class="tabs is-toggle is-small limited-backoffice__tabs" data-testid="limited-backoffice-tabs">
|
||||
@@ -75,30 +107,6 @@ const selectDepartment = (event) => {
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div v-if="props.showDepartmentSwitcher" class="field limited-backoffice__department-field">
|
||||
<label class="label" for="limited-backoffice-department">
|
||||
{{ t("templates.limited_backoffice.department") }}
|
||||
</label>
|
||||
<div class="control">
|
||||
<div class="select is-fullwidth">
|
||||
<select
|
||||
id="limited-backoffice-department"
|
||||
:value="props.selectedDepartmentId || ''"
|
||||
:disabled="props.loadingDepartments || props.departments.length === 0"
|
||||
data-testid="limited-backoffice-department-select"
|
||||
@change="selectDepartment"
|
||||
>
|
||||
<option value="" disabled>
|
||||
{{ t("templates.limited_backoffice.select_department") }}
|
||||
</option>
|
||||
<option v-for="department in props.departments" :key="department.id" :value="department.id">
|
||||
{{ department.name }}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<slot />
|
||||
@@ -112,10 +120,8 @@ const selectDepartment = (event) => {
|
||||
|
||||
.limited-backoffice__toolbar {
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
justify-content: space-between;
|
||||
gap: 1rem;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
margin-bottom: 1.25rem;
|
||||
}
|
||||
|
||||
@@ -124,7 +130,7 @@ const selectDepartment = (event) => {
|
||||
}
|
||||
|
||||
.limited-backoffice__department-field {
|
||||
min-width: min(100%, 260px);
|
||||
min-width: min(42vw, 280px);
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
@@ -138,11 +144,14 @@ const selectDepartment = (event) => {
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
.limited-backoffice__tabs,
|
||||
.limited-backoffice__department-field {
|
||||
.limited-backoffice__tabs {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.limited-backoffice__department-field {
|
||||
min-width: 9rem;
|
||||
}
|
||||
|
||||
.limited-backoffice__tabs :deep(ul) {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { expect, test } from "@playwright/test";
|
||||
import { expect, test, type Page } from "@playwright/test";
|
||||
import { API_HOST, seedAuthenticatedState } from "./support/network.js";
|
||||
import { isDesktopProject } from "./support/projects";
|
||||
|
||||
@@ -14,6 +14,7 @@ const limitedManagerPermissions = [
|
||||
"limited_backoffice_prices_manage",
|
||||
"limited_backoffice_employees_manage",
|
||||
"department_access_1",
|
||||
"department_access_2",
|
||||
];
|
||||
|
||||
const sessionData = {
|
||||
@@ -62,7 +63,10 @@ const adminLimitedSessionData = {
|
||||
permissions: ["user", "admin", "limited_backoffice_access", "department_access_1"],
|
||||
};
|
||||
|
||||
const assignedDepartments = [{ id: 1, name: "Assigned Depot", description: "", visible: true, archived: false }];
|
||||
const assignedDepartments = [
|
||||
{ id: 1, name: "Assigned Depot", description: "", visible: true, archived: false },
|
||||
{ id: 2, name: "Remote Depot", description: "", visible: true, archived: false },
|
||||
];
|
||||
|
||||
const pricePayload = {
|
||||
department: { id: 1, name: "Assigned Depot", description: "" },
|
||||
@@ -235,6 +239,11 @@ const rolePermissionGroups = {
|
||||
],
|
||||
};
|
||||
|
||||
const remotePricePayload = {
|
||||
...pricePayload,
|
||||
department: { id: 2, name: "Remote Depot", description: "" },
|
||||
};
|
||||
|
||||
const rolesPayload = [
|
||||
{ key: "viewer", label: "Viewer", description: "Can view.", permission_groups: rolePermissionGroups.viewer },
|
||||
{ key: "cashier", label: "Cashier", description: "Can sell.", permission_groups: rolePermissionGroups.cashier },
|
||||
@@ -274,6 +283,17 @@ const employeesPayload = [
|
||||
created_at: "2026-01-01 00:00:00",
|
||||
updated_at: "2026-01-01 00:00:00",
|
||||
},
|
||||
{
|
||||
id: 502,
|
||||
customer_number: 900000502,
|
||||
display_name: "Riley Remote",
|
||||
email: "riley@example.com",
|
||||
active: true,
|
||||
role: { key: "viewer", label: "Viewer", description: "Can view." },
|
||||
departments: [{ id: 2, name: "Remote Depot" }],
|
||||
created_at: "2026-01-01 00:00:00",
|
||||
updated_at: "2026-01-01 00:00:00",
|
||||
},
|
||||
];
|
||||
|
||||
async function seedLimitedBackofficeSession(page, token = "limited-backoffice-token") {
|
||||
@@ -350,6 +370,11 @@ async function mockLimitedBackofficeApi(page, authSessionData = sessionData, opt
|
||||
return;
|
||||
}
|
||||
|
||||
if (pathname.endsWith("/limited-backoffice/departments/2/prices") && method === "GET") {
|
||||
await route.fulfill(json({ data: remotePricePayload }));
|
||||
return;
|
||||
}
|
||||
|
||||
if (pathname.endsWith("/limited-backoffice/departments/1/prices") && method === "PUT") {
|
||||
const body = request.postDataJSON?.() || null;
|
||||
priceUpdateCalls.push(body);
|
||||
@@ -462,6 +487,20 @@ async function mockLimitedBackofficeApi(page, authSessionData = sessionData, opt
|
||||
};
|
||||
}
|
||||
|
||||
async function expectDepartmentSelectorInTitleRow(page: Page) {
|
||||
const headingBox = await page.getByRole("heading", { name: "Backoffice" }).boundingBox();
|
||||
const selectBox = await page.getByTestId("limited-backoffice-department-select").boundingBox();
|
||||
|
||||
expect(headingBox).not.toBeNull();
|
||||
expect(selectBox).not.toBeNull();
|
||||
if (!headingBox || !selectBox) {
|
||||
return;
|
||||
}
|
||||
|
||||
expect(selectBox.x).toBeGreaterThan(headingBox.x);
|
||||
expect(Math.abs(selectBox.y + selectBox.height / 2 - (headingBox.y + headingBox.height / 2))).toBeLessThan(48);
|
||||
}
|
||||
|
||||
test.describe("Limited backoffice", () => {
|
||||
test("shows the limited backoffice header shortcut only on department-scoped admin pages", async ({
|
||||
page,
|
||||
@@ -539,7 +578,9 @@ test.describe("Limited backoffice", () => {
|
||||
await page.goto("/backoffice/departments/1/prices");
|
||||
|
||||
await expect(page.getByTestId("limited-prices-title")).toBeVisible();
|
||||
await expectDepartmentSelectorInTitleRow(page);
|
||||
await expect(page.getByTestId("limited-backoffice-department-select")).toContainText("Assigned Depot");
|
||||
await expect(page.getByTestId("limited-backoffice-department-select")).toContainText("Remote Depot");
|
||||
await expect(page.getByTestId("limited-backoffice-department-select")).not.toContainText("Other Depot");
|
||||
await expect(page.getByTestId("limited-price-row-101")).toContainText("Truck wash");
|
||||
await expect(page.getByTestId("limited-price-input-101")).toHaveValue("125");
|
||||
@@ -718,12 +759,16 @@ test.describe("Limited backoffice", () => {
|
||||
await seedLimitedBackofficeSession(page);
|
||||
const api = await mockLimitedBackofficeApi(page);
|
||||
|
||||
await page.goto("/backoffice/employees");
|
||||
await page.goto("/backoffice/departments/1/employees");
|
||||
|
||||
await expect(page.getByTestId("limited-employees-title")).toBeVisible();
|
||||
await expectDepartmentSelectorInTitleRow(page);
|
||||
await expect(page.getByTestId("limited-employee-row-501")).toContainText("Casey Clerk");
|
||||
await expect(page.getByTestId("limited-employee-user-id-501")).toHaveText("501");
|
||||
await expect(page.getByTestId("limited-employee-phone-501")).toHaveText("+45 12345678");
|
||||
await expect(page.getByTestId("limited-employee-row-502")).toHaveCount(0);
|
||||
await expect(page.getByTestId("limited-employee-department-1")).toBeChecked();
|
||||
await expect(page.getByTestId("limited-employee-department-2")).not.toBeChecked();
|
||||
await expect(page.locator("#limited-employee-role option")).toHaveCount(5);
|
||||
await expect(page.getByTestId("limited-employee-role")).not.toContainText("Superuser");
|
||||
await expect(page.locator("body")).not.toContainText("department_access_1");
|
||||
@@ -834,15 +879,14 @@ test.describe("Limited backoffice", () => {
|
||||
await expect(page.getByTestId("limited-employees-title")).toBeVisible();
|
||||
await expect(page.getByTestId("limited-employee-save")).toBeDisabled();
|
||||
await expect(page.getByTestId("limited-employee-save")).toHaveClass(/is-fullwidth/);
|
||||
await expect(page.getByTestId("limited-employee-departments").locator(".switch")).toHaveCount(1);
|
||||
await expect(page.getByTestId("limited-employee-departments").locator(".switch")).toHaveCount(2);
|
||||
await expect(page.getByTestId("limited-employee-department-1")).toBeChecked();
|
||||
await expect(page.getByTestId("limited-employee-department-2")).not.toBeChecked();
|
||||
await expect(page.getByTestId("limited-employee-phone-country-code")).toContainText("+45");
|
||||
|
||||
await page.getByTestId("limited-employee-name").fill("No Phone Worker");
|
||||
await page.getByTestId("limited-employee-email").fill("no-phone@example.com");
|
||||
await page.getByTestId("limited-employee-password").fill("Secret123!");
|
||||
await expect(page.getByTestId("limited-employee-save")).toBeDisabled();
|
||||
|
||||
await page.getByTestId("limited-employee-department-1").click();
|
||||
await expect(page.getByTestId("limited-employee-save")).toBeEnabled();
|
||||
await page.getByTestId("limited-employee-save").click();
|
||||
|
||||
@@ -856,14 +900,13 @@ test.describe("Limited backoffice", () => {
|
||||
role_key: "viewer",
|
||||
department_ids: [1],
|
||||
});
|
||||
await expect(page.getByTestId("limited-employee-row-902")).toContainText("No Phone Worker");
|
||||
await expect(page.getByTestId("limited-employee-row-903")).toContainText("No Phone Worker");
|
||||
|
||||
await page.getByTestId("limited-employee-name").fill("Phone Worker");
|
||||
await page.getByTestId("limited-employee-email").fill("phone@example.com");
|
||||
await page.getByTestId("limited-employee-phone-country-code").selectOption("358");
|
||||
await page.getByTestId("limited-employee-phone").fill("87654321");
|
||||
await page.getByTestId("limited-employee-password").fill("Secret123!");
|
||||
await page.getByTestId("limited-employee-department-1").click();
|
||||
await expect(page.getByTestId("limited-employee-save")).toBeEnabled();
|
||||
await page.getByTestId("limited-employee-save").click();
|
||||
|
||||
@@ -877,8 +920,8 @@ test.describe("Limited backoffice", () => {
|
||||
role_key: "viewer",
|
||||
department_ids: [1],
|
||||
});
|
||||
await expect(page.getByTestId("limited-employee-row-903")).toContainText("Phone Worker");
|
||||
await expect(page.getByTestId("limited-employee-phone-903")).toHaveText("+358 87654321");
|
||||
await expect(page.getByTestId("limited-employee-row-904")).toContainText("Phone Worker");
|
||||
await expect(page.getByTestId("limited-employee-phone-904")).toHaveText("+358 87654321");
|
||||
});
|
||||
|
||||
test("edits employee contact details without requiring a new password", async ({ page }) => {
|
||||
@@ -912,19 +955,75 @@ test.describe("Limited backoffice", () => {
|
||||
await expect(page.getByTestId("limited-employee-phone-501")).toHaveText("+358 87654321");
|
||||
});
|
||||
|
||||
test("keeps department context across employee access navigation", async ({ page }, testInfo) => {
|
||||
test.skip(!isDesktopProject(testInfo), "Desktop only");
|
||||
|
||||
await seedLimitedBackofficeSession(page);
|
||||
const api = await mockLimitedBackofficeApi(page);
|
||||
|
||||
await page.goto("/backoffice/employees");
|
||||
|
||||
await expect(page).toHaveURL(/\/backoffice\/departments\/1\/employees$/);
|
||||
await expect(page.getByTestId("limited-backoffice-tab-prices")).toHaveAttribute(
|
||||
"href",
|
||||
"/backoffice/departments/1/prices"
|
||||
);
|
||||
await expect(page.getByTestId("limited-backoffice-tab-employees")).toHaveAttribute(
|
||||
"href",
|
||||
"/backoffice/departments/1/employees"
|
||||
);
|
||||
|
||||
await page.getByTestId("limited-backoffice-department-select").selectOption("2");
|
||||
|
||||
await expect(page).toHaveURL(/\/backoffice\/departments\/2\/employees$/);
|
||||
await expect(page.getByTestId("limited-employee-row-501")).toHaveCount(0);
|
||||
await expect(page.getByTestId("limited-employee-row-502")).toContainText("Riley Remote");
|
||||
await expect(page.getByTestId("limited-employee-department-1")).not.toBeChecked();
|
||||
await expect(page.getByTestId("limited-employee-department-2")).toBeChecked();
|
||||
await expect(page.getByTestId("limited-backoffice-tab-prices")).toHaveAttribute(
|
||||
"href",
|
||||
"/backoffice/departments/2/prices"
|
||||
);
|
||||
|
||||
await page.getByTestId("limited-backoffice-tab-prices").click();
|
||||
await expect(page).toHaveURL(/\/backoffice\/departments\/2\/prices$/);
|
||||
await expect(page.getByTestId("limited-backoffice-tab-employees")).toHaveAttribute(
|
||||
"href",
|
||||
"/backoffice/departments/2/employees"
|
||||
);
|
||||
|
||||
expect(api.forbiddenCalls).toEqual([]);
|
||||
});
|
||||
|
||||
test("does not render data for a department outside the manager scope", async ({ page }, testInfo) => {
|
||||
test.skip(!isDesktopProject(testInfo), "Desktop only");
|
||||
|
||||
await seedLimitedBackofficeSession(page);
|
||||
const api = await mockLimitedBackofficeApi(page);
|
||||
|
||||
await page.goto("/backoffice/departments/2/prices");
|
||||
await page.goto("/backoffice/departments/3/prices");
|
||||
|
||||
await expect(page.getByTestId("limited-prices-forbidden")).toBeVisible();
|
||||
await expect(page.locator("body")).not.toContainText("Secret Depot");
|
||||
await expect(page.getByTestId("limited-prices-table")).toHaveCount(0);
|
||||
|
||||
expect(api.calls.some((call) => call.includes("/limited-backoffice/departments/2/prices"))).toBe(false);
|
||||
expect(api.calls.some((call) => call.includes("/limited-backoffice/departments/3/prices"))).toBe(false);
|
||||
expect(api.forbiddenCalls).toEqual([]);
|
||||
});
|
||||
|
||||
test("does not render employee data for a department outside the manager scope", async ({ page }, testInfo) => {
|
||||
test.skip(!isDesktopProject(testInfo), "Desktop only");
|
||||
|
||||
await seedLimitedBackofficeSession(page);
|
||||
const api = await mockLimitedBackofficeApi(page);
|
||||
|
||||
await page.goto("/backoffice/departments/3/employees");
|
||||
|
||||
await expect(page.getByTestId("limited-employees-forbidden")).toBeVisible();
|
||||
await expect(page.locator("body")).not.toContainText("Secret Depot");
|
||||
await expect(page.getByTestId("limited-employees-table")).toHaveCount(0);
|
||||
|
||||
expect(api.calls.some((call) => call.includes("/limited-backoffice/employees"))).toBe(false);
|
||||
expect(api.forbiddenCalls).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -39,13 +39,14 @@ describe("Playwright PR mapping", () => {
|
||||
).toContain("tests/e2e/admin-department-notifications.spec.ts");
|
||||
});
|
||||
|
||||
it("maps limited backoffice view changes to limited backoffice E2E coverage", () => {
|
||||
it("maps limited backoffice view and service changes to limited backoffice E2E coverage", () => {
|
||||
expect(specsFor("src/views/backoffice/LimitedBackofficePrices.vue")).toContain(
|
||||
"tests/e2e/limited-backoffice.spec.ts"
|
||||
);
|
||||
expect(specsFor("src/views/backoffice/components/LimitedBackofficeLayout.vue")).toContain(
|
||||
"tests/e2e/limited-backoffice.spec.ts"
|
||||
);
|
||||
expect(specsFor("src/services/limitedBackoffice.js")).toContain("tests/e2e/limited-backoffice.spec.ts");
|
||||
});
|
||||
|
||||
it("maps superuser role permission page changes to role permissions E2E coverage", () => {
|
||||
|
||||
Reference in New Issue
Block a user