mirror of
				https://github.com/inventree/InvenTree.git
				synced 2025-10-31 13:15:43 +00:00 
			
		
		
		
	API date filter updates (#8544)
* Add 'stocktake_before' and 'stocktake_after' filters for StockItem API * Enable new filters for StockItemTable * Update CUI table filters * Add more date filter options for orders * Add date filters to BuildList * Update BuildOrderTable filters * Add more order date filters * Cleanup PurchaseOrderFilter code * Implement more PUI table filters * Add "Completion Date" column to PurchaseOrderTable * Update ReturnOrderTable * Add 'text' option for TableFilter * filter state management * Bump API version * Sorting for table filters * Add playwright tests for stock table filtering * Playwright updates - Add some helper functions for common operations * Refactoring for Playwright tests
This commit is contained in:
		
							
								
								
									
										46
									
								
								src/frontend/tests/helpers.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								src/frontend/tests/helpers.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,46 @@ | ||||
| /** | ||||
|  * Open the filter drawer for the currently visible table | ||||
|  * @param page - The page object | ||||
|  */ | ||||
| export const openFilterDrawer = async (page) => { | ||||
|   await page.getByLabel('table-select-filters').click(); | ||||
| }; | ||||
|  | ||||
| /** | ||||
|  * Close the filter drawer for the currently visible table | ||||
|  * @param page - The page object | ||||
|  */ | ||||
| export const closeFilterDrawer = async (page) => { | ||||
|   await page.getByLabel('filter-drawer-close').click(); | ||||
| }; | ||||
|  | ||||
| /** | ||||
|  * Click the specified button (if it is visible) | ||||
|  * @param page - The page object | ||||
|  * @param name - The name of the button to click | ||||
|  */ | ||||
| export const clickButtonIfVisible = async (page, name, timeout = 500) => { | ||||
|   await page.waitForTimeout(timeout); | ||||
|  | ||||
|   if (await page.getByRole('button', { name }).isVisible()) { | ||||
|     await page.getByRole('button', { name }).click(); | ||||
|   } | ||||
| }; | ||||
|  | ||||
| /** | ||||
|  * Clear all filters from the currently visible table | ||||
|  * @param page - The page object | ||||
|  */ | ||||
| export const clearTableFilters = async (page) => { | ||||
|   await openFilterDrawer(page); | ||||
|   await clickButtonIfVisible(page, 'Clear Filters'); | ||||
|   await page.getByLabel('filter-drawer-close').click(); | ||||
| }; | ||||
|  | ||||
| /** | ||||
|  * Return the parent 'row' element for a given 'cell' element | ||||
|  * @param cell - The cell element | ||||
|  */ | ||||
| export const getRowFromCell = async (cell) => { | ||||
|   return cell.locator('xpath=ancestor::tr').first(); | ||||
| }; | ||||
| @@ -1,8 +1,13 @@ | ||||
| import { test } from '../baseFixtures.ts'; | ||||
| import { baseUrl } from '../defaults.ts'; | ||||
| import { | ||||
|   clickButtonIfVisible, | ||||
|   getRowFromCell, | ||||
|   openFilterDrawer | ||||
| } from '../helpers.ts'; | ||||
| import { doQuickLogin } from '../login.ts'; | ||||
|  | ||||
| test('Pages - Build Order', async ({ page }) => { | ||||
| test('Build Order - Basic Tests', async ({ page }) => { | ||||
|   await doQuickLogin(page); | ||||
|  | ||||
|   await page.goto(`${baseUrl}/part/`); | ||||
| @@ -82,7 +87,7 @@ test('Pages - Build Order', async ({ page }) => { | ||||
|     .waitFor(); | ||||
| }); | ||||
|  | ||||
| test('Pages - Build Order - Build Outputs', async ({ page }) => { | ||||
| test('Build Order - Build Outputs', async ({ page }) => { | ||||
|   await doQuickLogin(page); | ||||
|  | ||||
|   await page.goto(`${baseUrl}/part/`); | ||||
| @@ -140,7 +145,7 @@ test('Pages - Build Order - Build Outputs', async ({ page }) => { | ||||
|  | ||||
|   // Cancel one of the newly created outputs | ||||
|   const cell = await page.getByRole('cell', { name: `# ${sn}` }); | ||||
|   const row = await cell.locator('xpath=ancestor::tr').first(); | ||||
|   const row = await getRowFromCell(cell); | ||||
|   await row.getByLabel(/row-action-menu-/i).click(); | ||||
|   await page.getByRole('menuitem', { name: 'Cancel' }).click(); | ||||
|   await page.getByRole('button', { name: 'Submit' }).click(); | ||||
| @@ -148,7 +153,7 @@ test('Pages - Build Order - Build Outputs', async ({ page }) => { | ||||
|  | ||||
|   // Complete the other output | ||||
|   const cell2 = await page.getByRole('cell', { name: `# ${sn + 1}` }); | ||||
|   const row2 = await cell2.locator('xpath=ancestor::tr').first(); | ||||
|   const row2 = await getRowFromCell(cell2); | ||||
|   await row2.getByLabel(/row-action-menu-/i).click(); | ||||
|   await page.getByRole('menuitem', { name: 'Complete' }).click(); | ||||
|   await page.getByLabel('related-field-location').click(); | ||||
| @@ -158,7 +163,7 @@ test('Pages - Build Order - Build Outputs', async ({ page }) => { | ||||
|   await page.getByText('Build outputs have been completed').waitFor(); | ||||
| }); | ||||
|  | ||||
| test('Pages - Build Order - Allocation', async ({ page }) => { | ||||
| test('Build Order - Allocation', async ({ page }) => { | ||||
|   await doQuickLogin(page); | ||||
|  | ||||
|   await page.goto(`${baseUrl}/manufacturing/build-order/1/line-items`); | ||||
| @@ -170,7 +175,7 @@ test('Pages - Build Order - Allocation', async ({ page }) => { | ||||
|  | ||||
|   // The capacitor stock should be fully allocated | ||||
|   const cell = await page.getByRole('cell', { name: /C_1uF_0805/ }); | ||||
|   const row = await cell.locator('xpath=ancestor::tr').first(); | ||||
|   const row = await getRowFromCell(cell); | ||||
|  | ||||
|   await row.getByText(/150 \/ 150/).waitFor(); | ||||
|  | ||||
| @@ -237,7 +242,7 @@ test('Pages - Build Order - Allocation', async ({ page }) => { | ||||
|     const item = data[idx]; | ||||
|  | ||||
|     const cell = await page.getByRole('cell', { name: item.name }); | ||||
|     const row = await cell.locator('xpath=ancestor::tr').first(); | ||||
|     const row = await getRowFromCell(cell); | ||||
|     const progress = `${item.allocated} / ${item.required}`; | ||||
|  | ||||
|     await row.getByRole('cell', { name: item.ipn }).first().waitFor(); | ||||
| @@ -257,3 +262,14 @@ test('Pages - Build Order - Allocation', async ({ page }) => { | ||||
|     .getByRole('menuitem', { name: 'Deallocate Stock', exact: true }) | ||||
|     .waitFor(); | ||||
| }); | ||||
|  | ||||
| test('Build Order - Filters', async ({ page }) => { | ||||
|   await doQuickLogin(page); | ||||
|  | ||||
|   await page.goto(`${baseUrl}/manufacturing/index/buildorders`); | ||||
|  | ||||
|   await openFilterDrawer(page); | ||||
|   await clickButtonIfVisible(page, 'Clear Filters'); | ||||
|  | ||||
|   await page.waitForTimeout(2500); | ||||
| }); | ||||
|   | ||||
| @@ -2,7 +2,7 @@ import { test } from '../baseFixtures.js'; | ||||
| import { doQuickLogin } from '../login.js'; | ||||
| import { setPluginState } from '../settings.js'; | ||||
|  | ||||
| test('Pages - Dashboard - Basic', async ({ page }) => { | ||||
| test('Dashboard - Basic', async ({ page }) => { | ||||
|   await doQuickLogin(page); | ||||
|  | ||||
|   await page.getByText('Use the menu to add widgets').waitFor(); | ||||
| @@ -35,7 +35,7 @@ test('Pages - Dashboard - Basic', async ({ page }) => { | ||||
|   await page.getByLabel('dashboard-accept-layout').click(); | ||||
| }); | ||||
|  | ||||
| test('Pages - Dashboard - Plugins', async ({ page, request }) => { | ||||
| test('Dashboard - Plugins', async ({ page, request }) => { | ||||
|   // Ensure that the "SampleUI" plugin is enabled | ||||
|   await setPluginState({ | ||||
|     request, | ||||
|   | ||||
| @@ -1,5 +1,6 @@ | ||||
| import { test } from '../baseFixtures'; | ||||
| import { baseUrl } from '../defaults'; | ||||
| import { getRowFromCell } from '../helpers'; | ||||
| import { doQuickLogin } from '../login'; | ||||
|  | ||||
| /** | ||||
| @@ -129,9 +130,7 @@ test('Parts - Allocations', async ({ page }) => { | ||||
|  | ||||
|   // Check "progress" bar of BO0001 | ||||
|   const build_order_cell = await page.getByRole('cell', { name: 'BO0001' }); | ||||
|   const build_order_row = await build_order_cell | ||||
|     .locator('xpath=ancestor::tr') | ||||
|     .first(); | ||||
|   const build_order_row = await getRowFromCell(build_order_cell); | ||||
|   await build_order_row.getByText('11 / 75').waitFor(); | ||||
|  | ||||
|   // Expand allocations against BO0001 | ||||
| @@ -147,9 +146,7 @@ test('Parts - Allocations', async ({ page }) => { | ||||
|  | ||||
|   // Check "progress" bar of SO0025 | ||||
|   const sales_order_cell = await page.getByRole('cell', { name: 'SO0025' }); | ||||
|   const sales_order_row = await sales_order_cell | ||||
|     .locator('xpath=ancestor::tr') | ||||
|     .first(); | ||||
|   const sales_order_row = await getRowFromCell(sales_order_cell); | ||||
|   await sales_order_row.getByText('3 / 10').waitFor(); | ||||
|  | ||||
|   // Expand allocations against SO0025 | ||||
|   | ||||
| @@ -1,4 +1,5 @@ | ||||
| import { test } from '../baseFixtures.ts'; | ||||
| import { clickButtonIfVisible, openFilterDrawer } from '../helpers.ts'; | ||||
| import { doQuickLogin } from '../login.ts'; | ||||
|  | ||||
| test('Purchase Orders - General', async ({ page }) => { | ||||
| @@ -51,6 +52,30 @@ test('Purchase Orders - General', async ({ page }) => { | ||||
|   await page.getByRole('tab', { name: 'Details' }).waitFor(); | ||||
| }); | ||||
|  | ||||
| test('Purchase Orders - Filters', async ({ page }) => { | ||||
|   await doQuickLogin(page, 'reader', 'readonly'); | ||||
|  | ||||
|   await page.getByRole('tab', { name: 'Purchasing' }).click(); | ||||
|   await page.getByRole('tab', { name: 'Purchase Orders' }).click(); | ||||
|  | ||||
|   // Open filters drawer | ||||
|   await openFilterDrawer(page); | ||||
|   await clickButtonIfVisible(page, 'Clear Filters'); | ||||
|  | ||||
|   await page.getByRole('button', { name: 'Add Filter' }).click(); | ||||
|  | ||||
|   // Check for expected filter options | ||||
|   await page.getByPlaceholder('Select filter').fill('before'); | ||||
|   await page.getByRole('option', { name: 'Created Before' }).waitFor(); | ||||
|   await page.getByRole('option', { name: 'Completed Before' }).waitFor(); | ||||
|   await page.getByRole('option', { name: 'Target Date Before' }).waitFor(); | ||||
|  | ||||
|   await page.getByPlaceholder('Select filter').fill('after'); | ||||
|   await page.getByRole('option', { name: 'Created After' }).waitFor(); | ||||
|   await page.getByRole('option', { name: 'Completed After' }).waitFor(); | ||||
|   await page.getByRole('option', { name: 'Target Date After' }).waitFor(); | ||||
| }); | ||||
|  | ||||
| /** | ||||
|  * Tests for receiving items against a purchase order | ||||
|  */ | ||||
|   | ||||
| @@ -1,8 +1,9 @@ | ||||
| import { test } from '../baseFixtures.js'; | ||||
| import { baseUrl } from '../defaults.js'; | ||||
| import { clickButtonIfVisible, openFilterDrawer } from '../helpers.js'; | ||||
| import { doQuickLogin } from '../login.js'; | ||||
|  | ||||
| test('Stock', async ({ page }) => { | ||||
| test('Stock - Basic Tests', async ({ page }) => { | ||||
|   await doQuickLogin(page); | ||||
|  | ||||
|   await page.goto(`${baseUrl}/stock/location/index/`); | ||||
| @@ -49,6 +50,45 @@ test('Stock - Location Tree', async ({ page }) => { | ||||
|   await page.getByRole('cell', { name: 'Factory' }).first().waitFor(); | ||||
| }); | ||||
|  | ||||
| test('Stock - Filters', async ({ page }) => { | ||||
|   await doQuickLogin(page, 'steven', 'wizardstaff'); | ||||
|  | ||||
|   await page.goto(`${baseUrl}/stock/location/index/`); | ||||
|   await page.getByRole('tab', { name: 'Stock Items' }).click(); | ||||
|  | ||||
|   await openFilterDrawer(page); | ||||
|   await clickButtonIfVisible(page, 'Clear Filters'); | ||||
|  | ||||
|   // Filter by updated date | ||||
|   await page.getByRole('button', { name: 'Add Filter' }).click(); | ||||
|   await page.getByPlaceholder('Select filter').fill('updated'); | ||||
|   await page.getByText('Updated After').click(); | ||||
|   await page.getByPlaceholder('Select date value').fill('2010-01-01'); | ||||
|   await page.getByText('Show items updated after this date').waitFor(); | ||||
|  | ||||
|   // Filter by batch code | ||||
|   await page.getByRole('button', { name: 'Add Filter' }).click(); | ||||
|   await page.getByPlaceholder('Select filter').fill('batch'); | ||||
|   await page | ||||
|     .getByRole('option', { name: 'Batch Code', exact: true }) | ||||
|     .locator('span') | ||||
|     .click(); | ||||
|   await page.getByPlaceholder('Enter filter value').fill('TABLE-B02'); | ||||
|   await page.getByLabel('apply-text-filter').click(); | ||||
|  | ||||
|   // Close dialog | ||||
|   await page.keyboard.press('Escape'); | ||||
|  | ||||
|   // Ensure correct result is displayed | ||||
|   await page | ||||
|     .getByRole('cell', { name: 'A round table - with blue paint' }) | ||||
|     .waitFor(); | ||||
|  | ||||
|   // Clear filters (ready for next set of tests) | ||||
|   await openFilterDrawer(page); | ||||
|   await clickButtonIfVisible(page, 'Clear Filters'); | ||||
| }); | ||||
|  | ||||
| test('Stock - Serial Numbers', async ({ page }) => { | ||||
|   await doQuickLogin(page); | ||||
|  | ||||
|   | ||||
| @@ -1,23 +1,23 @@ | ||||
| import { test } from './baseFixtures.js'; | ||||
| import { baseUrl } from './defaults.js'; | ||||
| import { | ||||
|   clearTableFilters, | ||||
|   closeFilterDrawer, | ||||
|   openFilterDrawer | ||||
| } from './helpers.js'; | ||||
| import { doQuickLogin } from './login.js'; | ||||
|  | ||||
| // Helper function to set the value of a specific table filter | ||||
| const setFilter = async (page, name: string, value: string) => { | ||||
|   await page.getByLabel('table-select-filters').click(); | ||||
|   await openFilterDrawer(page); | ||||
|  | ||||
|   await page.getByRole('button', { name: 'Add Filter' }).click(); | ||||
|   await page.getByPlaceholder('Select filter').click(); | ||||
|   await page.getByRole('option', { name: name, exact: true }).click(); | ||||
|   await page.getByPlaceholder('Select filter value').click(); | ||||
|   await page.getByRole('option', { name: value, exact: true }).click(); | ||||
|   await page.getByLabel('filter-drawer-close').click(); | ||||
| }; | ||||
|  | ||||
| // Helper function to clear table filters | ||||
| const clearFilters = async (page) => { | ||||
|   await page.getByLabel('table-select-filters').click(); | ||||
|   await page.getByRole('button', { name: 'Clear Filters' }).click(); | ||||
|   await page.getByLabel('filter-drawer-close').click(); | ||||
|   await closeFilterDrawer(page); | ||||
| }; | ||||
|  | ||||
| test('Tables - Filters', async ({ page }) => { | ||||
| @@ -30,14 +30,14 @@ test('Tables - Filters', async ({ page }) => { | ||||
|   await setFilter(page, 'Responsible', 'allaccess'); | ||||
|   await setFilter(page, 'Project Code', 'PRJ-NIM'); | ||||
|  | ||||
|   await clearFilters(page); | ||||
|   await clearTableFilters(page); | ||||
|  | ||||
|   // Head to the "part list" page | ||||
|   await page.goto(`${baseUrl}/part/category/index/parts/`); | ||||
|  | ||||
|   await setFilter(page, 'Assembly', 'Yes'); | ||||
|  | ||||
|   await clearFilters(page); | ||||
|   await clearTableFilters(page); | ||||
|  | ||||
|   // Head to the "purchase order list" page | ||||
|   await page.goto(`${baseUrl}/purchasing/index/purchaseorders/`); | ||||
| @@ -47,7 +47,7 @@ test('Tables - Filters', async ({ page }) => { | ||||
|   await setFilter(page, 'Assigned to me', 'No'); | ||||
|   await setFilter(page, 'Project Code', 'PRO-ZEN'); | ||||
|  | ||||
|   await clearFilters(page); | ||||
|   await clearTableFilters(page); | ||||
| }); | ||||
|  | ||||
| test('Tables - Columns', async ({ page }) => { | ||||
|   | ||||
		Reference in New Issue
	
	Block a user