Browse Source

Add location filter tests and changelog entry for printer search (#920)

  PR #920 was merged without a CHANGELOG entry and without test coverage
  for the location filter dropdown (every other new control — name
  search, model search, serial search, whitespace trim, clear button,
  status filter, empty states, combined filters — already had tests).

  Adds:
  - `filters by location via dropdown` — overrides the printers mock so
    printer 1 has location 'Workshop' and printer 2 has location
    'Office', then verifies that selecting each location shows only the
    matching printer and that switching between the two works.
  - `hides location filter when no printers have a location` — both
    printers get a null location, and the test asserts the status filter
    dropdown is still rendered but the location filter dropdown is not.
  - CHANGELOG entry under [0.2.3b3] > New Features crediting the
    contributor and documenting the search/filter feature, including the
    WebSocket-reactive status filter behavior.
maziggy 4 months ago
parent
commit
b453385d22

+ 1 - 0
CHANGELOG.md

@@ -8,6 +8,7 @@ All notable changes to Bambuddy will be documented in this file.
 - **AMS Drying Support for P2S** — Remote AMS drying and queue auto-drying now work on P2S printers with firmware 01.02.00.00 or later. Previously P2S was hard-blocked from the drying feature.
 - **AMS Drying Support for P2S** — Remote AMS drying and queue auto-drying now work on P2S printers with firmware 01.02.00.00 or later. Previously P2S was hard-blocked from the drying feature.
 
 
 ### New Features
 ### New Features
+- **Printers Page Search and Filters** ([#852](https://github.com/maziggy/bambuddy/issues/852)) — The Printers page now has a live search bar and two filter dropdowns (status and location) to make finding specific printers in large setups easier, especially on mobile where Ctrl+F is impractical. Search matches printer name, model, location, and serial number (case-insensitive, whitespace-trimmed) and has a clear button. The status filter covers All / Printing / Paused / Idle / Finished / Error / Offline and is reactive to WebSocket status updates via a React Query cache subscription — so a print finishing while "Printing" is selected immediately removes the printer from the filtered list. The location filter is only shown when at least one printer has a location configured. All three filters are combinable; the controls are hidden when no printers are configured yet; and an empty-state message appears when no printer matches the current search/filters. Fully localized across all 7 UI languages. Thanks to @legend813 for the contribution.
 - **LDAP Default Fallback Group** — Settings → Authentication → LDAP → Advanced now has a "Default group" selector. When an LDAP user authenticates but is not listed in any mapped LDAP group, they are automatically assigned to this fallback group instead of being left without permissions. Previously such users could log in successfully but landed on empty pages because every permission check failed. Leave the setting empty to preserve the old behavior. A warning is logged each time the fallback is applied so administrators can spot missing group assignments.
 - **LDAP Default Fallback Group** — Settings → Authentication → LDAP → Advanced now has a "Default group" selector. When an LDAP user authenticates but is not listed in any mapped LDAP group, they are automatically assigned to this fallback group instead of being left without permissions. Previously such users could log in successfully but landed on empty pages because every permission check failed. Leave the setting empty to preserve the old behavior. A warning is logged each time the fallback is applied so administrators can spot missing group assignments.
 
 
 ### Fixed
 ### Fixed

+ 54 - 0
frontend/src/__tests__/pages/PrintersPage.test.tsx

@@ -625,5 +625,59 @@ describe('PrintersPage', () => {
         expect(screen.queryByText('P1S Backup')).not.toBeInTheDocument();
         expect(screen.queryByText('P1S Backup')).not.toBeInTheDocument();
       });
       });
     });
     });
+
+    it('filters by location via dropdown', async () => {
+      // Override: give printer 2 its own location so the dropdown has two options
+      // and we can verify the filter picks the right one. Printer 1 stays at 'Workshop'.
+      server.use(
+        http.get('/api/v1/printers/', () =>
+          HttpResponse.json([
+            mockPrinters[0],
+            { ...mockPrinters[1], location: 'Office' },
+          ])
+        )
+      );
+
+      render(<PrintersPage />);
+      await waitFor(() => {
+        expect(screen.getByText('X1 Carbon')).toBeInTheDocument();
+        expect(screen.getByText('P1S Backup')).toBeInTheDocument();
+      });
+
+      // Select "Workshop" from the location filter dropdown
+      fireEvent.change(screen.getByDisplayValue('All locations'), { target: { value: 'Workshop' } });
+
+      await waitFor(() => {
+        expect(screen.getByText('X1 Carbon')).toBeInTheDocument();
+        expect(screen.queryByText('P1S Backup')).not.toBeInTheDocument();
+      });
+
+      // Switch to "Office" — the other printer should now be the only one visible
+      fireEvent.change(screen.getByDisplayValue('Workshop'), { target: { value: 'Office' } });
+
+      await waitFor(() => {
+        expect(screen.queryByText('X1 Carbon')).not.toBeInTheDocument();
+        expect(screen.getByText('P1S Backup')).toBeInTheDocument();
+      });
+    });
+
+    it('hides location filter when no printers have a location', async () => {
+      // Both printers have null location — dropdown should not render at all
+      server.use(
+        http.get('/api/v1/printers/', () =>
+          HttpResponse.json([
+            { ...mockPrinters[0], location: null },
+            { ...mockPrinters[1], location: null },
+          ])
+        )
+      );
+
+      render(<PrintersPage />);
+      await waitFor(() => expect(screen.getByText('X1 Carbon')).toBeInTheDocument());
+
+      // Status filter is still there, but the location filter should be absent.
+      expect(screen.getByDisplayValue('All statuses')).toBeInTheDocument();
+      expect(screen.queryByDisplayValue('All locations')).not.toBeInTheDocument();
+    });
   });
   });
 });
 });

File diff suppressed because it is too large
+ 0 - 0
static/assets/index-CihbWKPG.css


File diff suppressed because it is too large
+ 0 - 0
static/assets/index-DlWXszIh.js


+ 2 - 2
static/index.html

@@ -23,8 +23,8 @@
 
 
     <!-- Splash screens for iOS -->
     <!-- Splash screens for iOS -->
     <link rel="apple-touch-startup-image" href="/img/android-chrome-512x512.png" />
     <link rel="apple-touch-startup-image" href="/img/android-chrome-512x512.png" />
-    <script type="module" crossorigin src="/assets/index-BLKXbeO0.js"></script>
-    <link rel="stylesheet" crossorigin href="/assets/index-Caj-77TJ.css">
+    <script type="module" crossorigin src="/assets/index-DlWXszIh.js"></script>
+    <link rel="stylesheet" crossorigin href="/assets/index-CihbWKPG.css">
   </head>
   </head>
   <body>
   <body>
     <div id="root"></div>
     <div id="root"></div>

+ 11 - 0
test_backend.sh

@@ -0,0 +1,11 @@
+#!/bin/sh
+
+cd backend
+ruff check && ruff format --check
+
+#if [ "$1" = "--full" ]; then
+../venv/bin/python3 -m pytest tests/ -v -n 30
+#else
+#../venv/bin/python3 -m pytest tests/ -v -n 30 --ignore=tests/unit/services/test_bambu_ftp.py
+#fi
+#cd ..

Some files were not shown because too many files changed in this diff