PrinterAccessSettings.test.tsx 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212
  1. /**
  2. * Printer access page (#1727): groups reach picked printers plus whole
  3. * locations, edited by group or by printer and saved together.
  4. */
  5. import { describe, it, expect, beforeEach } from 'vitest';
  6. import { screen, waitFor } from '@testing-library/react';
  7. import userEvent from '@testing-library/user-event';
  8. import { http, HttpResponse } from 'msw';
  9. import { render } from '../utils';
  10. import { server } from '../mocks/server';
  11. import { PrinterAccessSettings } from '../../components/PrinterAccessSettings';
  12. const printer = (id: number, name: string, location: string | null, model = 'X1C') => ({
  13. id,
  14. name,
  15. location,
  16. model,
  17. serial_number: `SN${id}`,
  18. ip_address: `10.0.0.${id}`,
  19. is_active: true,
  20. });
  21. const printers = [
  22. printer(1, 'Alpha', 'Lab A'),
  23. printer(2, 'Bravo', 'Lab A', 'P1S'),
  24. printer(3, 'Charlie', 'Lab B'),
  25. printer(4, 'Loose', null),
  26. ];
  27. const group = (id: number, name: string, extra: Record<string, unknown> = {}) => ({
  28. id,
  29. name,
  30. description: null,
  31. permissions: [],
  32. is_system: false,
  33. restrict_printers: false,
  34. printer_ids: [],
  35. locations: [],
  36. user_count: 1,
  37. created_at: '2026-01-01T00:00:00Z',
  38. updated_at: '2026-01-01T00:00:00Z',
  39. ...extra,
  40. });
  41. const groups = [
  42. group(1, 'Administrators', { is_system: true }),
  43. group(3, 'Viewers', { is_system: true }),
  44. group(5, 'Team A', { restrict_printers: true, printer_ids: [3], locations: ['Lab A'] }),
  45. group(6, 'Team Old', { restrict_printers: true, locations: ['Gone'] }),
  46. ];
  47. const users = [
  48. { id: 1, username: 'boss', is_active: true, is_admin: true, groups: [{ id: 1, name: 'Administrators' }] },
  49. { id: 2, username: 'alice', is_active: true, is_admin: false, groups: [{ id: 3, name: 'Viewers' }, { id: 5, name: 'Team A' }] },
  50. { id: 3, username: 'bob', is_active: true, is_admin: false, groups: [{ id: 3, name: 'Viewers' }] },
  51. ];
  52. let patches: Record<number, Record<string, unknown>>;
  53. beforeEach(() => {
  54. patches = {};
  55. server.use(
  56. http.get('/api/v1/groups/', () => HttpResponse.json(groups)),
  57. http.get('/api/v1/printers/', () => HttpResponse.json(printers)),
  58. http.get('/api/v1/users/', () => HttpResponse.json(users)),
  59. http.patch('/api/v1/groups/:id', async ({ request, params }) => {
  60. const body = (await request.json()) as Record<string, unknown>;
  61. patches[Number(params.id)] = body;
  62. return HttpResponse.json({ ...groups.find((g) => g.id === Number(params.id)), ...body });
  63. })
  64. );
  65. });
  66. const open = (query: string) => {
  67. window.history.pushState({}, '', `/settings?tab=users&sub=printer-access${query}`);
  68. return render(<PrinterAccessSettings />);
  69. };
  70. describe('PrinterAccessSettings by group', () => {
  71. it('shows what a group reaches through its locations and its picks', async () => {
  72. open('&group=5');
  73. expect(await screen.findByText('Members can use 3 of 4 printers.')).toBeInTheDocument();
  74. // Through Lab A: ticked and fixed
  75. expect(screen.getByLabelText('Alpha')).toBeChecked();
  76. expect(screen.getByLabelText('Alpha')).toBeDisabled();
  77. // Picked on its own
  78. expect(screen.getByLabelText('Charlie')).toBeChecked();
  79. expect(screen.getByLabelText('Charlie')).toBeEnabled();
  80. expect(screen.getByLabelText('Loose')).not.toBeChecked();
  81. });
  82. it('gives a whole location and saves it', async () => {
  83. const user = userEvent.setup();
  84. open('&group=5');
  85. await screen.findByText('Members can use 3 of 4 printers.');
  86. await user.click(screen.getByLabelText('Every printer in Lab B, including printers added there later'));
  87. await user.click(screen.getByRole('button', { name: /Save/ }));
  88. await waitFor(() => expect(patches[5]).toBeDefined());
  89. expect(patches[5]).toEqual({ restrict_printers: true, printer_ids: [3], locations: ['Lab A', 'Lab B'] });
  90. expect(patches[6]).toBeUndefined();
  91. });
  92. it('ticks only the printers the filters leave', async () => {
  93. const user = userEvent.setup();
  94. open('&group=5');
  95. await screen.findByText('Members can use 3 of 4 printers.');
  96. await user.type(screen.getByLabelText('Search name, model, serial or location'), 'loose');
  97. await waitFor(() => expect(screen.queryByLabelText('Charlie')).not.toBeInTheDocument());
  98. await user.click(screen.getByRole('button', { name: 'Tick all shown' }));
  99. await user.click(screen.getByRole('button', { name: /Save/ }));
  100. await waitFor(() => expect(patches[5]).toBeDefined());
  101. expect(patches[5].printer_ids).toEqual([3, 4]);
  102. expect(patches[5].locations).toEqual(['Lab A']);
  103. });
  104. it('lists a given location no printer has any more, so it can be removed', async () => {
  105. const user = userEvent.setup();
  106. open('&group=6');
  107. expect(await screen.findByText('Gone')).toBeInTheDocument();
  108. expect(screen.getByText(/won't see any printer/)).toBeInTheDocument();
  109. await user.click(screen.getByLabelText('Every printer in Gone, including printers added there later'));
  110. await user.click(screen.getByRole('button', { name: /Save/ }));
  111. await waitFor(() => expect(patches[6]).toBeDefined());
  112. expect(patches[6].locations).toEqual([]);
  113. });
  114. it('filters the group list', async () => {
  115. const user = userEvent.setup();
  116. open('');
  117. await screen.findByText('Team A');
  118. await user.click(screen.getByRole('button', { name: 'Not limited' }));
  119. expect(screen.queryByText('Team A')).not.toBeInTheDocument();
  120. expect(screen.getByText('Viewers')).toBeInTheDocument();
  121. await user.click(screen.getByRole('button', { name: 'All' }));
  122. await user.type(screen.getByLabelText('Search groups'), 'old');
  123. expect(screen.getByText('Team Old')).toBeInTheDocument();
  124. expect(screen.queryByText('Viewers')).not.toBeInTheDocument();
  125. });
  126. it('cannot limit Administrators', async () => {
  127. open('&group=1');
  128. expect(await screen.findByText('Administrators always see every printer.')).toBeInTheDocument();
  129. expect(screen.queryByRole('switch')).not.toBeInTheDocument();
  130. });
  131. it('discards drafts', async () => {
  132. const user = userEvent.setup();
  133. open('&group=5');
  134. await screen.findByText('Members can use 3 of 4 printers.');
  135. await user.click(screen.getByLabelText('Charlie'));
  136. expect(screen.getByText('Unsaved changes: Team A')).toBeInTheDocument();
  137. await user.click(screen.getByRole('button', { name: 'Discard' }));
  138. expect(screen.queryByText(/Unsaved changes:/)).not.toBeInTheDocument();
  139. expect(screen.getByLabelText('Charlie')).toBeChecked();
  140. });
  141. });
  142. describe('PrinterAccessSettings by printer', () => {
  143. it('shows one printer from the card menu, with who reaches it and who sees everything', async () => {
  144. open('&view=printers&printer=3');
  145. expect(await screen.findByText('Charlie')).toBeInTheDocument();
  146. expect(screen.queryByText('Alpha')).not.toBeInTheDocument();
  147. expect(screen.getByText('Team A')).toBeInTheDocument();
  148. // bob is in no limited group, so he sees every printer
  149. expect(await screen.findByText('Users in no limited group: bob')).toBeInTheDocument();
  150. });
  151. it('removes a picked printer from a group', async () => {
  152. const user = userEvent.setup();
  153. open('&view=printers&printer=3');
  154. await screen.findByText('Charlie');
  155. await user.click(screen.getByRole('button', { name: 'Remove access for Team A' }));
  156. await user.click(screen.getByRole('button', { name: /Save/ }));
  157. await waitFor(() => expect(patches[5]).toBeDefined());
  158. expect(patches[5].printer_ids).toEqual([]);
  159. expect(patches[5].locations).toEqual(['Lab A']);
  160. });
  161. it('gives a group access to a printer', async () => {
  162. const user = userEvent.setup();
  163. open('&view=printers&printer=4');
  164. await screen.findByText('Loose');
  165. await user.selectOptions(screen.getByLabelText('Give access to…'), 'Team Old');
  166. await user.click(screen.getByRole('button', { name: /Save/ }));
  167. await waitFor(() => expect(patches[6]).toBeDefined());
  168. expect(patches[6].printer_ids).toEqual([4]);
  169. });
  170. it('marks access through a location, which is changed per group', async () => {
  171. open('&view=printers&printer=1');
  172. await screen.findByText('Alpha');
  173. expect(screen.getByTitle('Through location Lab A. Change it under By group.')).toHaveTextContent('Team A');
  174. expect(screen.queryByRole('button', { name: 'Remove access for Team A' })).not.toBeInTheDocument();
  175. });
  176. });