From abdfc73b8d991076df7f390eaa21652f4c2bcd5d Mon Sep 17 00:00:00 2001 From: Donkie Date: Sun, 11 Oct 2026 14:40:51 +0200 Subject: [PATCH] Make lot numbers on spool rows a Filter menu toggle Always showing the lot was too bulky. It now sits behind "Show lot numbers" in the Filter menu, next to the archived and no-spools toggles, off by default. When on, every spool row in any grouping ends its details line with the lot. Like showEmpty it lives in the URL (lots=1) and is remembered per browser, since it only adds to what's shown. --- client_v2/locales/en/common.json | 1 + client_v2/src/lib/api/query.test.ts | 1 + .../components/library/FilamentList.svelte | 2 +- .../lib/components/library/GroupRow.svelte | 24 +++++++++-- .../lib/components/library/ListToolbar.svelte | 26 +++++++++++- .../lib/components/library/SpoolRow.svelte | 5 ++- .../lib/components/library/UnusedRow.svelte | 5 ++- client_v2/src/lib/library/params.test.ts | 19 +++++++++ client_v2/src/lib/library/params.ts | 23 ++++++++--- client_v2/src/lib/library/viewPrefs.test.ts | 36 ++++++++++++++--- client_v2/src/lib/library/viewPrefs.ts | 27 +++++++------ client_v2/src/lib/utils/library.test.ts | 40 ++++++++++++------- client_v2/src/lib/utils/library.ts | 22 ++++++---- tests_frontend_v2/tests/menu-search.spec.ts | 20 +++++++++- tests_frontend_v2/tests/screenshots.spec.ts | 1 + 15 files changed, 197 insertions(+), 55 deletions(-) diff --git a/client_v2/locales/en/common.json b/client_v2/locales/en/common.json index 1d2e007b..e65ecdc4 100644 --- a/client_v2/locales/en/common.json +++ b/client_v2/locales/en/common.json @@ -62,6 +62,7 @@ "buttons.filter": "Filter", "buttons.save": "Save", "buttons.showArchived": "Show Archived", + "buttons.showLotNumbers": "Show lot numbers", "buttons.showNoSpools": "Show filaments with no spools", "buttons.unArchive": "Unarchive", "changeFilament.adoptWeight": "Set this spool’s full weight to {{full}}", diff --git a/client_v2/src/lib/api/query.test.ts b/client_v2/src/lib/api/query.test.ts index fc5b0382..5b4f11ce 100644 --- a/client_v2/src/lib/api/query.test.ts +++ b/client_v2/src/lib/api/query.test.ts @@ -22,6 +22,7 @@ function state(over: Partial = {}): LibraryState { filters: [], showArchived: false, showEmpty: true, + showLotNumbers: false, page: 1, pageSize: 20, ...over diff --git a/client_v2/src/lib/components/library/FilamentList.svelte b/client_v2/src/lib/components/library/FilamentList.svelte index 9914f10f..a04e59b2 100644 --- a/client_v2/src/lib/components/library/FilamentList.svelte +++ b/client_v2/src/lib/components/library/FilamentList.svelte @@ -138,7 +138,7 @@ {/each} {:else} {#each flatVMs as vm (vm.spool.id)} - + {/each} {/if} diff --git a/client_v2/src/lib/components/library/GroupRow.svelte b/client_v2/src/lib/components/library/GroupRow.svelte index c7b0e729..89263a57 100644 --- a/client_v2/src/lib/components/library/GroupRow.svelte +++ b/client_v2/src/lib/components/library/GroupRow.svelte @@ -182,15 +182,33 @@ /> {#if !collapsed && !noSpools} {#each inUse as vm (vm.spool.id)} - + {/each} {#each unusedByFilament as unused (unused[0].spool.id)} {#if unused.length === 1} - + {:else} - + {/if} {/each} {#if moreCount > 0} diff --git a/client_v2/src/lib/components/library/ListToolbar.svelte b/client_v2/src/lib/components/library/ListToolbar.svelte index ecedf525..04c6e59e 100644 --- a/client_v2/src/lib/components/library/ListToolbar.svelte +++ b/client_v2/src/lib/components/library/ListToolbar.svelte @@ -366,6 +366,9 @@ let noSpoolsMatches = $derived( libraryState.group === 'filament' && matchesTerms(m['buttons.showNoSpools'](), searchTerms(menuQuery)) ); + let lotNumbersMatches = $derived( + libraryState.view === 'spools' && matchesTerms(m['buttons.showLotNumbers'](), searchTerms(menuQuery)) + ); // An option's secondary text is on screen, so it has to be searchable: typing // "petg" against a list that visibly says PETG must not come back empty. let visibleOptions = $derived( @@ -524,7 +527,28 @@ {m['buttons.showNoSpools']()} {/if} - {#if !archivedMatches && !noSpoolsMatches && !visibleCategories.length} + {#if lotNumbersMatches} + {#if visibleCategories.length && !archivedMatches && !noSpoolsMatches}{/if} + + {/if} + {#if !archivedMatches && !noSpoolsMatches && !lotNumbersMatches && !visibleCategories.length} {/if} {:else} diff --git a/client_v2/src/lib/components/library/SpoolRow.svelte b/client_v2/src/lib/components/library/SpoolRow.svelte index 636fe3a8..ceb6ff87 100644 --- a/client_v2/src/lib/components/library/SpoolRow.svelte +++ b/client_v2/src/lib/components/library/SpoolRow.svelte @@ -16,11 +16,12 @@ indent?: number; /** Listing context — drops whatever the enclosing group already implies. */ context?: RowContext; + showLotNumbers?: boolean; } - let { vm, showSwatch = false, indent = 14, context = 'flat' }: Props = $props(); + let { vm, showSwatch = false, indent = 14, context = 'flat', showLotNumbers = false }: Props = $props(); - let identity = $derived(rowIdentity(vm, context)); + let identity = $derived(rowIdentity(vm, context, showLotNumbers)); let selected = $derived(params.isSelected(page.url.searchParams, 'spool', String(vm.spool.id))); diff --git a/client_v2/src/lib/components/library/UnusedRow.svelte b/client_v2/src/lib/components/library/UnusedRow.svelte index 6366f617..777944bd 100644 --- a/client_v2/src/lib/components/library/UnusedRow.svelte +++ b/client_v2/src/lib/components/library/UnusedRow.svelte @@ -14,9 +14,10 @@ indent?: number; /** Listing context, forwarded to the expanded per-instance rows. */ context?: RowContext; + showLotNumbers?: boolean; } - let { unused, showSwatch = false, indent = 14, context = 'flat' }: Props = $props(); + let { unused, showSwatch = false, indent = 14, context = 'flat', showLotNumbers = false }: Props = $props(); // Unused spools (never drawn from — used_weight 0) of one filament are // collapsed into a single summary row. Clicking can't know *which* instance @@ -109,7 +110,7 @@ {#each unused as vm (vm.spool.id)} - + {/each} {/if} diff --git a/client_v2/src/lib/library/params.test.ts b/client_v2/src/lib/library/params.test.ts index eeb71c24..fbfa498a 100644 --- a/client_v2/src/lib/library/params.test.ts +++ b/client_v2/src/lib/library/params.test.ts @@ -217,3 +217,22 @@ describe('catalog views', () => { expect(selectHrefFromState(state, 'filament', '7')).toBe('?view=filaments&group=none&sel=filament%3A7'); }); }); + +describe('lot number visibility', () => { + it('parses off by default, opt-in on spools, and off in catalogs', async () => { + const { parseLibraryState, selectHrefFromState } = await import('./params'); + expect(parseLibraryState(new URLSearchParams()).showLotNumbers).toBe(false); + const state = parseLibraryState(new URLSearchParams('lots=1')); + expect(state.showLotNumbers).toBe(true); + expect(selectHrefFromState(state, 'spool', '7')).toBe('?lots=1&sel=spool%3A7'); + for (const view of ['filaments', 'manufacturers']) { + expect(parseLibraryState(new URLSearchParams(`view=${view}&lots=1`)).showLotNumbers).toBe(false); + } + }); + + it('restores the preference and lets an explicit URL win', async () => { + const stored = '{"group":"filament","sort":"last_used","asc":false,"lots":true}'; + expect(await hrefFor(stored, '')).toBe('/?lots=1'); + expect(await hrefFor(stored, '?lots=0')).toBeNull(); + }); +}); diff --git a/client_v2/src/lib/library/params.ts b/client_v2/src/lib/library/params.ts index c3e354bd..2544c673 100644 --- a/client_v2/src/lib/library/params.ts +++ b/client_v2/src/lib/library/params.ts @@ -50,6 +50,8 @@ export interface LibraryState { showArchived: boolean; /** Also list filaments you hold no spools of, when grouping by filament. */ showEmpty: boolean; + /** Append lot numbers to spool row secondary text. */ + showLotNumbers: boolean; page: number; pageSize: number; } @@ -62,7 +64,7 @@ const VIEWS: LibraryView[] = ['spools', 'filaments', 'manufacturers']; * holds. A URL naming none of them is the one that defers to the remembered * view; naming any of them describes a view of its own, which is taken whole so * a stored grouping never gets spliced onto a link's sort. */ -const VIEW_PARAMS = ['view', 'group', 'sort', 'dir', 'empty']; +const VIEW_PARAMS = ['view', 'group', 'sort', 'dir', 'empty', 'lots']; const DEFAULTS = { group: 'filament' as GroupMode, @@ -70,6 +72,7 @@ const DEFAULTS = { sortAsc: false, showArchived: false, showEmpty: false, + showLotNumbers: false, page: 1, pageSize: 20 }; @@ -152,6 +155,7 @@ export function parseLibraryState(params: URLSearchParams): LibraryState { filters: parseFilters(params.getAll('f')), showArchived: params.get('arch') === '1', showEmpty: params.get('empty') === '1', + showLotNumbers: params.get('lots') === '1', page: parsePositiveInt(params.get('page'), DEFAULTS.page), pageSize: parsePositiveInt(params.get('size'), DEFAULTS.pageSize) }; @@ -164,7 +168,8 @@ export function parseLibraryState(params: URLSearchParams): LibraryState { ...common, group: view === 'filaments' && group === 'none' ? 'none' : layout.group, sortKey: sort && catalogSortField(view, sort) ? sort : layout.sortKey, - showEmpty: false + showEmpty: false, + showLotNumbers: false }; } @@ -210,6 +215,7 @@ function serializeState(s: LibraryState): string { } if (s.showArchived !== DEFAULTS.showArchived) p.set('arch', s.showArchived ? '1' : '0'); if (s.showEmpty !== DEFAULTS.showEmpty) p.set('empty', s.showEmpty ? '1' : '0'); + if (s.showLotNumbers !== DEFAULTS.showLotNumbers) p.set('lots', s.showLotNumbers ? '1' : '0'); if (s.page !== DEFAULTS.page) p.set('page', String(s.page)); if (s.pageSize !== DEFAULTS.pageSize) p.set('size', String(s.pageSize)); if (s.selection) p.set('sel', `${s.selection.kind}:${s.selection.id}`); @@ -251,7 +257,8 @@ export function rememberedViewHref(url: URL): string | null { group === DEFAULTS.group && stored.sortKey === DEFAULTS.sortKey && stored.sortAsc === DEFAULTS.sortAsc && - stored.showEmpty === DEFAULTS.showEmpty + stored.showEmpty === DEFAULTS.showEmpty && + stored.showLotNumbers === DEFAULTS.showLotNumbers ) { return null; } @@ -264,7 +271,8 @@ export function rememberedViewHref(url: URL): string | null { group, sortKey: stored.sortKey, sortAsc: stored.sortAsc, - showEmpty: stored.showEmpty + showEmpty: stored.showEmpty, + showLotNumbers: stored.showLotNumbers }); return `${url.pathname}?${qs}`; } @@ -288,7 +296,8 @@ function navigate(next: LibraryState, replace = false): void { group: next.group, sortKey: next.sortKey, sortAsc: next.sortAsc, - showEmpty: next.showEmpty + showEmpty: next.showEmpty, + showLotNumbers: next.showLotNumbers }); } @@ -372,6 +381,10 @@ export function setShowEmpty(showEmpty: boolean): void { navigate({ ...currentState(), showEmpty, page: DEFAULTS.page }); } +export function setShowLotNumbers(showLotNumbers: boolean): void { + navigate({ ...currentState(), showLotNumbers, page: DEFAULTS.page }); +} + export function setShowArchived(showArchived: boolean): void { navigate({ ...currentState(), showArchived, page: DEFAULTS.page }); } diff --git a/client_v2/src/lib/library/viewPrefs.test.ts b/client_v2/src/lib/library/viewPrefs.test.ts index 8186ff1a..846c7f37 100644 --- a/client_v2/src/lib/library/viewPrefs.test.ts +++ b/client_v2/src/lib/library/viewPrefs.test.ts @@ -1,5 +1,5 @@ -import { describe, expect, it } from 'vitest'; -import { parseStoredView } from './viewPrefs'; +import { afterEach, describe, expect, it, vi } from 'vitest'; +import { parseStoredView, rememberView } from './viewPrefs'; // The stored view is the only piece of Library state that doesn't come from the // URL (#1036), so it's also the only piece that can arrive malformed — from an @@ -13,7 +13,8 @@ describe('parseStoredView', () => { group: 'location', sortKey: 'name', sortAsc: true, - showEmpty: false + showEmpty: false, + showLotNumbers: false }); }); @@ -22,7 +23,8 @@ describe('parseStoredView', () => { group: 'filament', sortKey: 'name', sortAsc: true, - showEmpty: true + showEmpty: true, + showLotNumbers: false }); }); @@ -54,7 +56,31 @@ describe('parseStoredView', () => { group: 'colour', sortKey: 'name', sortAsc: false, - showEmpty: false + showEmpty: false, + showLotNumbers: false }); }); }); + +afterEach(() => vi.unstubAllGlobals()); + +it('round-trips the lot number preference through storage', () => { + let stored: string | null = null; + vi.stubGlobal('localStorage', { + getItem: () => stored, + setItem: (_key: string, value: string) => { + stored = value; + } + }); + const view = { group: 'filament', sortKey: 'name', sortAsc: true, showEmpty: false, showLotNumbers: true }; + rememberView(view); + expect(parseStoredView(stored)).toEqual(view); + rememberView({ ...view, showLotNumbers: false }); + expect(parseStoredView(stored)?.showLotNumbers).toBe(false); +}); + +it('defaults lot numbers off for an old stored view with empty enabled', () => { + expect(parseStoredView('{"group":"filament","sort":"name","asc":true,"empty":true}')?.showLotNumbers).toBe( + false + ); +}); diff --git a/client_v2/src/lib/library/viewPrefs.ts b/client_v2/src/lib/library/viewPrefs.ts index 8d714faa..c885aa3b 100644 --- a/client_v2/src/lib/library/viewPrefs.ts +++ b/client_v2/src/lib/library/viewPrefs.ts @@ -16,12 +16,12 @@ // cache, one URL per distinct view — depends on the address bar being the whole // truth about what's on screen. // -// Only grouping and sort are remembered, plus the one toggle that ADDS rows -// (showEmpty). Filters, search and archived-visibility hide spools, and a hidden -// filter silently restored days later reads as missing data rather than as a -// preference. Showing filaments you own no spools of cannot produce that -// confusion -- restoring it can only put more on screen, never less -- so it is -// remembered while the hiding toggles still are not. +// Only grouping and sort are remembered, plus toggles that ADD information +// (showEmpty adds rows, showLotNumbers adds text). Filters, search and archived- +// visibility hide spools, and a hidden filter silently restored days later reads +// as missing data rather than as a preference. Showing empty filaments or lot +// numbers cannot produce that confusion -- restoring them can only put more on +// screen, never less -- so they are remembered while the hiding toggles are not. const KEY = 'spoolman-v2-library-view'; @@ -35,6 +35,7 @@ export interface StoredView { sortKey: string; sortAsc: boolean; showEmpty: boolean; + showLotNumbers: boolean; } /** Rebuild a stored view from its JSON, or null if there isn't a usable one. */ @@ -43,15 +44,15 @@ export function parseStoredView(raw: string | null): StoredView | null { try { const parsed: unknown = JSON.parse(raw); if (typeof parsed !== 'object' || parsed === null) return null; - const { group, sort, asc, empty } = parsed as Record; + const { group, sort, asc, empty, lots } = parsed as Record; if (typeof group !== 'string' || typeof sort !== 'string' || typeof asc !== 'boolean') { return null; } - // `empty` arrived after this key shipped, so an entry written by an older - // build simply has no such field. Treating that as "off" keeps those + // `empty` and `lots` arrived after this key shipped, so an entry written + // by an older build simply has no such fields. Treating that as "off" keeps those // entries valid; rejecting them would throw away a grouping the user // picked long ago the first time they loaded a new version. - return { group, sortKey: sort, sortAsc: asc, showEmpty: empty === true }; + return { group, sortKey: sort, sortAsc: asc, showEmpty: empty === true, showLotNumbers: lots === true }; } catch { // Corrupt entry: the shipped view is no worse than what a first-time // visitor gets. @@ -80,7 +81,8 @@ export function rememberView(view: StoredView): void { prev.group === view.group && prev.sortKey === view.sortKey && prev.sortAsc === view.sortAsc && - prev.showEmpty === view.showEmpty + prev.showEmpty === view.showEmpty && + prev.showLotNumbers === view.showLotNumbers ) { // Selecting a spool or turning a page navigates too; only the changes that // actually move the view are worth a synchronous localStorage write. @@ -109,7 +111,8 @@ function write(view: StoredView): void { group: view.group, sort: view.sortKey, asc: view.sortAsc, - empty: view.showEmpty + empty: view.showEmpty, + lots: view.showLotNumbers }) ); } catch { diff --git a/client_v2/src/lib/utils/library.test.ts b/client_v2/src/lib/utils/library.test.ts index 1073a974..07b46430 100644 --- a/client_v2/src/lib/utils/library.test.ts +++ b/client_v2/src/lib/utils/library.test.ts @@ -99,25 +99,35 @@ describe('rowIdentity', () => { rightLabel: unused ? 'unused' : 'used 3 days ago' }) as SpoolVM; - it('shows the trimmed lot alongside usage in filament groups', () => { - expect(rowIdentity(vm(' LOT-2026-0915-A7 '), 'filament')).toEqual({ - title: '', - sub: 'used 3 days ago · Lot Nr LOT-2026-0915-A7' + const contexts = ['flat', 'filament', 'vendor', 'material', 'location'] as const; + const subs = ['PLA · 1.75 mm', 'used 3 days ago', 'PLA · 1.75 mm', '1.75 mm', 'PLA · 1.75 mm']; + + it.each(contexts)('defaults to master text in %s rows', (context) => { + expect(rowIdentity(vm('LOT'), context)).toEqual({ + title: context === 'filament' ? '' : context === 'vendor' ? 'Galaxy Black' : 'Prusament Galaxy Black', + sub: subs[contexts.indexOf(context)] }); }); - it('shows the lot for individual unused spools too', () => { - expect(rowIdentity(vm('24A-1187', true), 'filament').sub).toBe('unused · Lot Nr 24A-1187'); + it.each(contexts)('appends the trimmed lot when enabled in %s rows', (context) => { + const identity = rowIdentity(vm(''), context); + expect(rowIdentity(vm(' LOT-2026-0915-A7 '), context, true)).toEqual({ + ...identity, + sub: `${identity.sub} · Lot Nr LOT-2026-0915-A7` + }); }); - it.each(['', ' '])('keeps usage alone for a blank lot (%j)', (lot) => { - expect(rowIdentity(vm(lot), 'filament')).toEqual({ title: '', sub: 'used 3 days ago' }); - }); - - it.each(['flat', 'vendor', 'material', 'location'] as const)( - 'keeps the identity unchanged in %s rows', - (context) => { - expect(rowIdentity(vm('LOT-2026-0915-A7'), context)).toEqual(rowIdentity(vm(''), context)); + it.each(contexts)('adds nothing for blank lots in %s rows', (context) => { + for (const lot of ['', ' ']) { + expect(rowIdentity(vm(lot), context, true)).toEqual(rowIdentity(vm(lot), context)); } - ); + }); + + it('shows the lot for individual unused spools too', () => { + expect(rowIdentity(vm('24A-1187', true), 'filament', true).sub).toBe('unused · Lot Nr 24A-1187'); + }); + + it('omits the separator when the existing secondary text is empty', () => { + expect(rowIdentity({ ...vm('LOT'), rightLabel: '' }, 'filament', true).sub).toBe('Lot Nr LOT'); + }); }); diff --git a/client_v2/src/lib/utils/library.ts b/client_v2/src/lib/utils/library.ts index d065a7c9..128ba84a 100644 --- a/client_v2/src/lib/utils/library.ts +++ b/client_v2/src/lib/utils/library.ts @@ -115,7 +115,16 @@ export interface RowIdentity { * only causes truncation — so we drop it and surface the part that actually * distinguishes this spool instead. */ -export function rowIdentity(vm: SpoolVM, ctx: RowContext): RowIdentity { +export function rowIdentity(vm: SpoolVM, ctx: RowContext, showLotNumbers = false): RowIdentity { + const identity = contextualIdentity(vm, ctx); + const lot = showLotNumbers ? vm.spool.lot.trim() : ''; + if (lot) { + identity.sub = [identity.sub, `${m['spool.fields.lotNr']()} ${lot}`].filter(Boolean).join(' · '); + } + return identity; +} + +function contextualIdentity(vm: SpoolVM, ctx: RowContext): RowIdentity { const { vendor, filament } = vm; const dims = `${filament.material} · ${filament.diameter} mm`; switch (ctx) { @@ -142,8 +151,9 @@ export function rowIdentity(vm: SpoolVM, ctx: RowContext): RowIdentity { * how they've been used and which lot they came from — #id, fill, weight and * location already sit in their own columns — so there's nothing to make a bold * title out of. The usage status leads, since every row can say it; the lot - * number follows when there is one (#1173), and a long one is what the ellipsis - * cuts, with the full text on hover. The inspector has the rest. + * number joins only when the user turns it on and there is one (#1173). A long + * lot is what the ellipsis cuts, with the full text on hover. The inspector has + * the rest. * * The comment deliberately stays out of it. It reads like a label on the handful * of spools whose owner treats it as one, but it's a free-form textarea: people @@ -152,11 +162,7 @@ export function rowIdentity(vm: SpoolVM, ctx: RowContext): RowIdentity { * truncates the usage status — the one thing every row can say — to make room. */ function spoolIdentity(vm: SpoolVM): RowIdentity { - const lot = vm.spool.lot.trim(); - return { - title: '', - sub: lot ? `${vm.rightLabel} · ${m['spool.fields.lotNr']()} ${lot}` : vm.rightLabel - }; + return { title: '', sub: vm.rightLabel }; } /** diff --git a/tests_frontend_v2/tests/menu-search.spec.ts b/tests_frontend_v2/tests/menu-search.spec.ts index 1d0f6bf0..61bb1ffd 100644 --- a/tests_frontend_v2/tests/menu-search.spec.ts +++ b/tests_frontend_v2/tests/menu-search.spec.ts @@ -71,7 +71,7 @@ test("the filter menu searches its properties, archived included", async ({ page await searchBox(menu).fill("lot"); // A substring match, not a fuzzy one: "Location" contains those letters in // order but not together, and must not be offered here. - expect(await itemLabels(menu)).toEqual(["Lot Nr"]); + expect(await itemLabels(menu)).toEqual(["Lot Nr", "Show lot numbers"]); // "Show Archived" is a filter like any other and answers to the same box. await searchBox(menu).fill("arch"); @@ -101,3 +101,21 @@ test("the group menu is short enough to need no search box", async ({ page }) => await expect(menu.getByRole("button", { name: "None (flat)" })).toBeVisible(); await expect(searchBox(menu)).toHaveCount(0); }); + +test("lot numbers toggle is searchable and updates the URL", async ({ page }) => { + await openApp(page); + let menu = await openToolbarMenu(page, "Filter"); + await searchBox(menu).fill("show lot"); + const toggle = menu.getByRole("menuitemcheckbox", { name: "Show lot numbers" }); + await expect(toggle).toHaveAttribute("aria-checked", "false"); + await toggle.click(); + await expect(menu).toBeHidden(); + await expect(page).toHaveURL(/lots=1/); + menu = await openToolbarMenu(page, "Filter"); + await expect(menu.getByRole("menuitemcheckbox", { name: "Show lot numbers" })).toHaveAttribute("aria-checked", "true"); + await searchBox(menu).fill("arch"); + await expect(menu.getByRole("menuitemcheckbox", { name: "Show lot numbers" })).toHaveCount(0); + await searchBox(menu).fill("show lot"); + await menu.getByRole("menuitemcheckbox", { name: "Show lot numbers" }).click(); + await expect(page).not.toHaveURL(/lots=1/); +}); diff --git a/tests_frontend_v2/tests/screenshots.spec.ts b/tests_frontend_v2/tests/screenshots.spec.ts index efa80252..0f5f3ebe 100644 --- a/tests_frontend_v2/tests/screenshots.spec.ts +++ b/tests_frontend_v2/tests/screenshots.spec.ts @@ -90,6 +90,7 @@ test.beforeAll(async ({ request }) => { const PAGES = [ { path: "/", name: "library" }, + { path: "/?lots=1", name: "library with lot numbers" }, { path: "/dashboard", name: "dashboard" }, { path: "/labels", name: "labels" }, { path: "/settings", name: "settings" },