From 856c9e58c0ebc1d5b5ce17ef963587ee7ae81d7c Mon Sep 17 00:00:00 2001 From: Alan Nadolny Date: Mon, 5 Oct 2026 17:35:58 +0200 Subject: [PATCH 01/19] OBPIH-8025 unskip assert bin location field test --- src/pages/receiving/components/EditModal.ts | 6 +- .../receiving/components/EditModalTable.ts | 19 +++-- .../receiving/components/ReceivingTable.ts | 5 ++ src/pages/receiving/steps/ReceivingStep.ts | 9 +++ .../receiving/assertBinLocationField.test.ts | 73 ++++++++++++------- 5 files changed, 76 insertions(+), 36 deletions(-) diff --git a/src/pages/receiving/components/EditModal.ts b/src/pages/receiving/components/EditModal.ts index 80441451..b006aa41 100644 --- a/src/pages/receiving/components/EditModal.ts +++ b/src/pages/receiving/components/EditModal.ts @@ -12,7 +12,7 @@ class EditModal extends BasePageModel { } get modal() { - return this.page.locator('.ReactModal__Content'); + return this.page.getByTestId('receiving-edit-line-item-modal'); } async isLoaded() { @@ -20,7 +20,7 @@ class EditModal extends BasePageModel { } get saveButton() { - return this.modal.getByRole('button', { name: 'Save' }); + return this.modal.getByRole('button', { name: 'Save', exact: true }); } get cancelButton() { @@ -28,7 +28,7 @@ class EditModal extends BasePageModel { } get addLineButton() { - return this.modal.getByRole('button', { name: 'Add line' }); + return this.modal.getByTestId('add-new-record'); } get informationAboutEditedQtyNotMatchingShippedQty() { diff --git a/src/pages/receiving/components/EditModalTable.ts b/src/pages/receiving/components/EditModalTable.ts index fb7520b1..e81a9999 100644 --- a/src/pages/receiving/components/EditModalTable.ts +++ b/src/pages/receiving/components/EditModalTable.ts @@ -10,15 +10,18 @@ class EditModalTable extends BasePageModel { } get table() { - return this.page.locator('#modalForm'); + return this.page.getByTestId('edit-modal-receiving-table'); } get rows() { return this.table.getByRole('row'); } + // rows are 1-indexed by convention (row 1 is the first item), matching the + // rest of the receiving page objects. The header row doesn't carry + // role="row", so it doesn't occupy index 0 on its own. row(index: number) { - return new Row(this.page, this.rows.nth(index)); + return new Row(this.page, this.rows.nth(index - 1)); } } @@ -26,14 +29,18 @@ class Row extends BasePageModel { row: Locator; lotNumberField: TextField; expiryDatePickerField: DatePicker; - quantityShippedField: TextField; + receivingNowField: TextField; constructor(page: Page, row: Locator) { super(page); this.row = row; - this.lotNumberField = new TextField(page, 'Lot', row); - this.expiryDatePickerField = new DatePicker(page, 'Expiry', row); - this.quantityShippedField = new TextField(page, 'Quantity shipped', row); + this.lotNumberField = new TextField(page, 'Lot/SN', row); + this.expiryDatePickerField = new DatePicker(page, 'Exp. Date', row); + this.receivingNowField = new TextField(page, 'Receiving now', row); + } + + get binLocationSelect() { + return this.row.locator('[aria-label="Location"]'); } get clearProductSelect() { diff --git a/src/pages/receiving/components/ReceivingTable.ts b/src/pages/receiving/components/ReceivingTable.ts index e3ebb36b..9edcc98a 100644 --- a/src/pages/receiving/components/ReceivingTable.ts +++ b/src/pages/receiving/components/ReceivingTable.ts @@ -52,6 +52,11 @@ class Row extends BasePageModel { return this.row.getByRole('button', { name: 'Comment' }); } + // visible only when the "Show Putaway" switch is on + get binLocationSelect() { + return this.row.locator('[aria-label="Location"]'); + } + getItem(name: string) { return this.row.locator('[aria-label="Product"]').getByText(name); } diff --git a/src/pages/receiving/steps/ReceivingStep.ts b/src/pages/receiving/steps/ReceivingStep.ts index 78215a49..a9acfe6b 100644 --- a/src/pages/receiving/steps/ReceivingStep.ts +++ b/src/pages/receiving/steps/ReceivingStep.ts @@ -53,6 +53,15 @@ class ReceivingStep extends BasePageModel { return this.confirmReceivingDialog.getByRole('button', { name: 'Yes' }); } + get showPutawaySwitch() { + return this.page.getByTestId('show-putaway-switch'); + } + + async enableShowPutaway() { + await this.showPutawaySwitch.locator('.slider').click(); + await expect(this.showPutawaySwitch.getByRole('checkbox')).toBeChecked(); + } + get saveAndExitButton() { return this.page.getByRole('button', { name: 'Save & Exit' }); } diff --git a/src/tests/receiving/assertBinLocationField.test.ts b/src/tests/receiving/assertBinLocationField.test.ts index 4504eda8..2a5d586f 100644 --- a/src/tests/receiving/assertBinLocationField.test.ts +++ b/src/tests/receiving/assertBinLocationField.test.ts @@ -6,7 +6,7 @@ import { StockMovementResponse } from '@/types'; import BinLocationUtils from '@/utils/BinLocationUtils'; import { deleteShipment } from '@/utils/shipmentUtils'; -test.describe.skip('Assert bin location not clearable', () => { +test.describe('Assert bin location not clearable', () => { let STOCK_MOVEMENT: StockMovementResponse; test.beforeEach( @@ -39,11 +39,7 @@ test.describe.skip('Assert bin location not clearable', () => { ); test.afterEach( - async ({ - stockMovementService, - locationService, - mainLocationService, - }) => { + async ({ stockMovementService, locationService, mainLocationService }) => { await deleteShipment({ stockMovementService, STOCK_MOVEMENT }); const receivingBin = AppConfig.instance.receivingBinPrefix + STOCK_MOVEMENT.identifier; @@ -59,6 +55,9 @@ test.describe.skip('Assert bin location not clearable', () => { stockMovementShowPage, receivingPage, }) => { + const receivingBin = + AppConfig.instance.receivingBinPrefix + STOCK_MOVEMENT.identifier; + await test.step('Go to stock movement show page', async () => { await stockMovementShowPage.goToPage(STOCK_MOVEMENT.id); await stockMovementShowPage.isLoaded(); @@ -69,11 +68,16 @@ test.describe.skip('Assert bin location not clearable', () => { await receivingPage.receivingStep.isLoaded(); }); + await test.step('Show putaway location column', async () => { + await receivingPage.receivingStep.enableShowPutaway(); + }); + await test.step('Assert bin location cant be cleared', async () => { + const binLocationSelect = + receivingPage.receivingStep.table.row(1).binLocationSelect; + await expect(binLocationSelect).toHaveText(receivingBin); await expect( - receivingPage.receivingStep.table - .row(1) - .binLocationSelect.locator('.react-select__clear-indicator') + binLocationSelect.locator('.react-select__clear-indicator') ).toBeHidden(); }); @@ -83,32 +87,47 @@ test.describe.skip('Assert bin location not clearable', () => { await receivingPage.receivingStep.editModal.addLineButton.click(); await receivingPage.receivingStep.editModal.table .row(1) - .quantityShippedField.numberbox.fill('5'); + .receivingNowField.numberbox.fill('5'); + await receivingPage.receivingStep.editModal.table + .row(2) + .lotNumberField.textbox.fill('E2E-split-lot'); await receivingPage.receivingStep.editModal.table - .row(0) - .quantityShippedField.numberbox.fill('5'); + .row(2) + .receivingNowField.numberbox.fill('5'); + }); + + await test.step('Assert bin location field content in edit modal', async () => { + for (const row of [1, 2]) { + const binLocationSelect = + receivingPage.receivingStep.editModal.table.row( + row + ).binLocationSelect; + await expect(binLocationSelect).toHaveText(receivingBin); + await expect( + binLocationSelect.locator('.react-select__clear-indicator') + ).toBeHidden(); + } + }); + + await test.step('Save split lines', async () => { await receivingPage.receivingStep.editModal.saveButton.click(); + await expect(receivingPage.receivingStep.editModal.modal).toBeHidden(); }); await test.step('Assert bin location field content after split line', async () => { - const receivingBin = - AppConfig.instance.receivingBinPrefix + STOCK_MOVEMENT.identifier; + // After a split, the table shows the replaced row (1), the changes + // toggle row (2) and the split lines (3 and 4) await expect( receivingPage.receivingStep.table.row(1).binLocationSelect ).toHaveText(receivingBin); - await expect( - receivingPage.receivingStep.table.row(2).binLocationSelect - ).toHaveText(receivingBin); - await expect( - receivingPage.receivingStep.table - .row(1) - .binLocationSelect.locator('.react-select__clear-indicator') - ).toBeHidden(); - await expect( - receivingPage.receivingStep.table - .row(2) - .binLocationSelect.locator('.react-select__clear-indicator') - ).toBeHidden(); + for (const row of [3, 4]) { + const binLocationSelect = + receivingPage.receivingStep.table.row(row).binLocationSelect; + await expect(binLocationSelect).toHaveText(receivingBin); + await expect( + binLocationSelect.locator('.react-select__clear-indicator') + ).toBeHidden(); + } }); }); }); From cd9e958bd0333e658fb9468bb98d04c655b556ac Mon Sep 17 00:00:00 2001 From: Alan Nadolny Date: Tue, 6 Oct 2026 13:51:43 +0200 Subject: [PATCH 02/19] OBPIH-8025 Unskip assert creation of goods receipt note --- .../assertCreationOfGoodsReceiptNote.test.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/tests/receiving/assertCreationOfGoodsReceiptNote.test.ts b/src/tests/receiving/assertCreationOfGoodsReceiptNote.test.ts index ac51d7c2..8389b056 100644 --- a/src/tests/receiving/assertCreationOfGoodsReceiptNote.test.ts +++ b/src/tests/receiving/assertCreationOfGoodsReceiptNote.test.ts @@ -8,7 +8,7 @@ import { pageContainsValues } from '@/utils/pageUtils'; import { deleteShipment } from '@/utils/shipmentUtils'; import { captureRowValues } from '@/utils/tableUtils'; -test.describe.skip('Assert Goods Receipt Note is created and opened', () => { +test.describe('Assert Goods Receipt Note is created and opened', () => { let STOCK_MOVEMENT: StockMovementResponse; test.beforeEach( @@ -86,16 +86,15 @@ test.describe.skip('Assert Goods Receipt Note is created and opened', () => { await receivingPage.receivingStep.isLoaded(); await receivingPage.receivingStep.table .row(1) - .receivingNowField.textbox.fill('10'); + .receivingNowField.numberbox.fill('10'); await receivingPage.receivingStep.table .row(2) - .receivingNowField.textbox.fill('10'); + .receivingNowField.numberbox.fill('10'); }); await test.step('Go to check page and finish receipt', async () => { await receivingPage.nextButton.click(); await receivingPage.checkStep.isLoaded(); - await receivingPage.checkStep.isLoaded(); const rowCount = await receivingPage.checkStep.table.itemRows.count(); expect(rowCount).toBeGreaterThan(0); expectedValues = await captureRowValues( @@ -133,13 +132,12 @@ test.describe.skip('Assert Goods Receipt Note is created and opened', () => { await receivingPage.receivingStep.isLoaded(); await receivingPage.receivingStep.table .row(1) - .receivingNowField.textbox.fill('10'); + .receivingNowField.numberbox.fill('10'); }); await test.step('Go to check page and finish 2nd receipt', async () => { await receivingPage.nextButton.click(); await receivingPage.checkStep.isLoaded(); - await receivingPage.checkStep.isLoaded(); await receivingPage.checkStep.receiveShipmentButton.click(); await stockMovementShowPage.isLoaded(); }); @@ -164,6 +162,8 @@ test.describe.skip('Assert Goods Receipt Note is created and opened', () => { await test.step('Rollback shipment received in 2 receipts', async () => { await stockMovementShowPage.isLoaded(); await stockMovementShowPage.rollbackLastReceiptButton.click(); + await stockMovementShowPage.isLoaded(); + await expect(stockMovementShowPage.statusTag).toHaveText('Receiving'); }); }); }); From e2be0abe604deae4d9ca27aaca749afdb42428d4 Mon Sep 17 00:00:00 2001 From: Alan Nadolny Date: Tue, 6 Oct 2026 14:22:04 +0200 Subject: [PATCH 03/19] OBPIH-8025 Unskip assert creation of receiving bin --- src/tests/receiving/assertCreationOfReceivingBin.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tests/receiving/assertCreationOfReceivingBin.test.ts b/src/tests/receiving/assertCreationOfReceivingBin.test.ts index ed290462..d77bb3a5 100644 --- a/src/tests/receiving/assertCreationOfReceivingBin.test.ts +++ b/src/tests/receiving/assertCreationOfReceivingBin.test.ts @@ -9,7 +9,7 @@ import { StockMovementResponse } from '@/types'; import BinLocationUtils from '@/utils/BinLocationUtils'; import { deleteShipment } from '@/utils/shipmentUtils'; -test.describe.skip('Assert creation of receiving bin', () => { +test.describe('Assert creation of receiving bin', () => { test.describe.configure({ timeout: 60000 }); //timeout has been added for this test to make sure that the content on bin location tab will load as it can include a lot of data let STOCK_MOVEMENT: StockMovementResponse; From c937640abfadfde5f50a17a8e2d5836bc23dfcec Mon Sep 17 00:00:00 2001 From: Alan Nadolny Date: Tue, 6 Oct 2026 15:42:23 +0200 Subject: [PATCH 04/19] OBPIH-8025 Unskip assert quantity inputs --- .../receiving/components/EditModalTable.ts | 2 +- src/tests/receiving/assertQtyInputs.test.ts | 211 ++++++++++-------- 2 files changed, 125 insertions(+), 88 deletions(-) diff --git a/src/pages/receiving/components/EditModalTable.ts b/src/pages/receiving/components/EditModalTable.ts index e81a9999..3c4771a7 100644 --- a/src/pages/receiving/components/EditModalTable.ts +++ b/src/pages/receiving/components/EditModalTable.ts @@ -35,7 +35,7 @@ class Row extends BasePageModel { super(page); this.row = row; this.lotNumberField = new TextField(page, 'Lot/SN', row); - this.expiryDatePickerField = new DatePicker(page, 'Exp. Date', row); + this.expiryDatePickerField = new DatePicker(page, 'Exp Date', row); this.receivingNowField = new TextField(page, 'Receiving now', row); } diff --git a/src/tests/receiving/assertQtyInputs.test.ts b/src/tests/receiving/assertQtyInputs.test.ts index d38e5e28..2732130f 100644 --- a/src/tests/receiving/assertQtyInputs.test.ts +++ b/src/tests/receiving/assertQtyInputs.test.ts @@ -8,7 +8,7 @@ import BinLocationUtils from '@/utils/BinLocationUtils'; import { formatDate, getDateByOffset } from '@/utils/DateUtils'; import { deleteShipment } from '@/utils/shipmentUtils'; -test.describe.skip('Assert if quantity inputs remain when split lines', () => { +test.describe('Assert if quantity inputs remain when split lines', () => { let STOCK_MOVEMENT: StockMovementResponse; test.beforeEach( @@ -62,11 +62,14 @@ test.describe.skip('Assert if quantity inputs remain when split lines', () => { } ); + // After a split, the receiving table shows the replaced row (sum of the + // split lines, read-only), the changes toggle row and the split lines + // (original line first) right below the edited item. test('Assert quantity input after split line', async ({ stockMovementShowPage, receivingPage, }) => { - const lot = 'add-lot-test'; + const lot = `E2E-lot-${STOCK_MOVEMENT.identifier}`; const expDate = getDateByOffset(new Date(), 5); await test.step('Go to stock movement show page', async () => { @@ -80,112 +83,133 @@ test.describe.skip('Assert if quantity inputs remain when split lines', () => { }); await test.step('Autofill receiving quantity', async () => { - await receivingPage.receivingStep.isLoaded(); await receivingPage.receivingStep.autofillQuantitiesButton.click(); + await expect( + receivingPage.receivingStep.table.row(1).receivingNowField.numberbox + ).toHaveValue('50'); + await expect( + receivingPage.receivingStep.table.row(2).receivingNowField.numberbox + ).toHaveValue('100'); + await expect( + receivingPage.receivingStep.table.row(3).receivingNowField.numberbox + ).toHaveValue('200'); }); await test.step('Open edit modal for item and split line', async () => { await receivingPage.receivingStep.table.row(2).editButton.click(); await receivingPage.receivingStep.editModal.isLoaded(); - await receivingPage.receivingStep.editModal.addLineButton.click(); await receivingPage.receivingStep.editModal.table - .row(0) - .quantityShippedField.numberbox.fill('100'); + .row(1) + .receivingNowField.numberbox.fill('50'); + await receivingPage.receivingStep.editModal.addLineButton.click(); await receivingPage.receivingStep.editModal.table - .row(0) + .row(2) .lotNumberField.textbox.fill(lot); await receivingPage.receivingStep.editModal.table - .row(0) - .expiryDatePickerField.fill(expDate); - await receivingPage.receivingStep.editModal.table - .row(1) - .quantityShippedField.numberbox.fill('50'); - await receivingPage.receivingStep.editModal.addLineButton.click(); + .row(2) + .expiryDatePickerField.fillWithFormat(expDate, DateFormat.DISPLAY); await receivingPage.receivingStep.editModal.table .row(2) - .quantityShippedField.numberbox.fill('50'); + .receivingNowField.numberbox.fill('50'); await receivingPage.receivingStep.editModal.saveButton.click(); + await expect(receivingPage.receivingStep.editModal.modal).toBeHidden(); await receivingPage.receivingStep.isLoaded(); }); - await test.step('Assert quantity input before split line', async () => { + await test.step('Assert quantity inputs after split line', async () => { await expect( - receivingPage.receivingStep.table.row(1).receivingNowField.textbox + receivingPage.receivingStep.table.row(1).receivingNowField.numberbox ).toHaveValue('50'); await expect( - receivingPage.receivingStep.table.row(2).receivingNowField.textbox - ).toBeEmpty(); - await expect( - receivingPage.receivingStep.table.row(3).receivingNowField.textbox - ).toBeEmpty(); + receivingPage.receivingStep.table.getCellValue(2, 'Receiving now') + ).toHaveText('100'); await expect( - receivingPage.receivingStep.table.row(4).receivingNowField.textbox - ).toBeEmpty(); + receivingPage.receivingStep.table.row(4).receivingNowField.numberbox + ).toHaveValue('50'); await expect( - receivingPage.receivingStep.table.row(5).receivingNowField.textbox - ).toHaveValue('100'); + receivingPage.receivingStep.table.row(5).receivingNowField.numberbox + ).toHaveValue('50'); await expect( - receivingPage.receivingStep.table.getCellValue(2, 'Lot/Serial No.') + receivingPage.receivingStep.table.getCellValue(5, 'Lot/SN') ).toContainText(lot); await expect( - receivingPage.receivingStep.table.getCellValue(2, 'Expiration date') - ).toContainText(formatDate(expDate, DateFormat.DEFAULT)); + receivingPage.receivingStep.table.getCellValue(5, 'Exp Date') + ).toContainText(formatDate(expDate, DateFormat.DISPLAY)); + await expect( + receivingPage.receivingStep.table.row(6).receivingNowField.numberbox + ).toHaveValue('200'); }); - await test.step('Autofill quantity after split line', async () => { - await receivingPage.receivingStep.isLoaded(); + await test.step('Autofill quantity after split line does not override inputs', async () => { await receivingPage.receivingStep.autofillQuantitiesButton.click(); await expect( - receivingPage.receivingStep.table.row(2).receivingNowField.textbox - ).toHaveValue('100'); + receivingPage.receivingStep.table.row(1).receivingNowField.numberbox + ).toHaveValue('50'); await expect( - receivingPage.receivingStep.table.row(3).receivingNowField.textbox + receivingPage.receivingStep.table.row(4).receivingNowField.numberbox ).toHaveValue('50'); await expect( - receivingPage.receivingStep.table.row(4).receivingNowField.textbox + receivingPage.receivingStep.table.row(5).receivingNowField.numberbox ).toHaveValue('50'); + await expect( + receivingPage.receivingStep.table.row(6).receivingNowField.numberbox + ).toHaveValue('200'); }); - await test.step('Edit another line', async () => { - await receivingPage.receivingStep.table.row(5).editButton.click(); + await test.step('Split another line', async () => { + await receivingPage.receivingStep.table.row(6).editButton.click(); await receivingPage.receivingStep.editModal.isLoaded(); await receivingPage.receivingStep.editModal.table - .row(0) + .row(1) + .receivingNowField.numberbox.fill('150'); + await receivingPage.receivingStep.editModal.addLineButton.click(); + await receivingPage.receivingStep.editModal.table + .row(2) .lotNumberField.textbox.fill(lot); await receivingPage.receivingStep.editModal.table - .row(0) - .expiryDatePickerField.fill(expDate); + .row(2) + .expiryDatePickerField.fillWithFormat(expDate, DateFormat.DISPLAY); + await receivingPage.receivingStep.editModal.table + .row(2) + .receivingNowField.numberbox.fill('50'); await receivingPage.receivingStep.editModal.saveButton.click(); + await expect(receivingPage.receivingStep.editModal.modal).toBeHidden(); await receivingPage.receivingStep.isLoaded(); + }); + + await test.step('Assert quantity inputs after splitting another line', async () => { await expect( - receivingPage.receivingStep.table.row(1).receivingNowField.textbox + receivingPage.receivingStep.table.row(1).receivingNowField.numberbox ).toHaveValue('50'); await expect( - receivingPage.receivingStep.table.row(2).receivingNowField.textbox - ).toHaveValue('100'); + receivingPage.receivingStep.table.getCellValue(2, 'Receiving now') + ).toHaveText('100'); await expect( - receivingPage.receivingStep.table.row(3).receivingNowField.textbox + receivingPage.receivingStep.table.row(4).receivingNowField.numberbox ).toHaveValue('50'); await expect( - receivingPage.receivingStep.table.row(4).receivingNowField.textbox + receivingPage.receivingStep.table.row(5).receivingNowField.numberbox ).toHaveValue('50'); await expect( - receivingPage.receivingStep.table.row(5).receivingNowField.textbox - ).toBeEmpty(); + receivingPage.receivingStep.table.getCellValue(6, 'Receiving now') + ).toHaveText('200'); await expect( - receivingPage.receivingStep.table.getCellValue(5, 'Lot/Serial No.') - ).toContainText(lot); + receivingPage.receivingStep.table.row(8).receivingNowField.numberbox + ).toHaveValue('150'); + await expect( + receivingPage.receivingStep.table.row(9).receivingNowField.numberbox + ).toHaveValue('50'); await expect( - receivingPage.receivingStep.table.getCellValue(5, 'Expiration date') - ).toContainText(formatDate(expDate, DateFormat.DEFAULT)); + receivingPage.receivingStep.table.getCellValue(9, 'Lot/SN') + ).toContainText(lot); }); }); - test('Assert quantity input after split line whe use save and exit', async ({ + test('Assert quantity input after split line when use save and exit', async ({ stockMovementShowPage, receivingPage, }) => { - const lot = 'add-lot-test'; + const lot = `E2E-lot-${STOCK_MOVEMENT.identifier}`; const expDate = getDateByOffset(new Date(), 5); await test.step('Go to stock movement show page', async () => { @@ -198,81 +222,94 @@ test.describe.skip('Assert if quantity inputs remain when split lines', () => { await receivingPage.receivingStep.isLoaded(); }); - await test.step('Autofill quantity for items', async () => { - await receivingPage.receivingStep.isLoaded(); + await test.step('Fill quantity for items and save and exit', async () => { await receivingPage.receivingStep.table .row(2) - .receivingNowField.textbox.fill('200'); + .receivingNowField.numberbox.fill('100'); await receivingPage.receivingStep.table .row(3) - .receivingNowField.textbox.fill('100'); + .receivingNowField.numberbox.fill('200'); await receivingPage.receivingStep.saveAndExitButton.click(); + await stockMovementShowPage.isLoaded(); }); await test.step('Return to receipt', async () => { await stockMovementShowPage.receiveButton.click(); await receivingPage.receivingStep.isLoaded(); await expect( - receivingPage.receivingStep.table.row(1).receivingNowField.textbox + receivingPage.receivingStep.table.row(1).receivingNowField.numberbox ).toBeEmpty(); await expect( - receivingPage.receivingStep.table.row(2).receivingNowField.textbox - ).toHaveValue('200'); - await expect( - receivingPage.receivingStep.table.row(3).receivingNowField.textbox + receivingPage.receivingStep.table.row(2).receivingNowField.numberbox ).toHaveValue('100'); + await expect( + receivingPage.receivingStep.table.row(3).receivingNowField.numberbox + ).toHaveValue('200'); }); - await test.step('Open edit modal for item without quantity input', async () => { + await test.step('Open edit modal for item without quantity input and split line', async () => { await receivingPage.receivingStep.table.row(1).editButton.click(); await receivingPage.receivingStep.editModal.isLoaded(); - await receivingPage.receivingStep.editModal.addLineButton.click(); + await expect( + receivingPage.receivingStep.editModal.table.row(1).receivingNowField + .numberbox + ).toBeEmpty(); await receivingPage.receivingStep.editModal.table - .row(0) - .quantityShippedField.numberbox.fill('25'); + .row(1) + .receivingNowField.numberbox.fill('25'); + await receivingPage.receivingStep.editModal.addLineButton.click(); await receivingPage.receivingStep.editModal.table - .row(0) + .row(2) .lotNumberField.textbox.fill(lot); await receivingPage.receivingStep.editModal.table - .row(0) - .expiryDatePickerField.fill(expDate); + .row(2) + .expiryDatePickerField.fillWithFormat(expDate, DateFormat.DISPLAY); await receivingPage.receivingStep.editModal.table - .row(1) - .quantityShippedField.numberbox.fill('25'); + .row(2) + .receivingNowField.numberbox.fill('25'); await receivingPage.receivingStep.editModal.saveButton.click(); + await expect(receivingPage.receivingStep.editModal.modal).toBeHidden(); await receivingPage.receivingStep.isLoaded(); }); - await test.step('Assert quantity input after split line', async () => { + await test.step('Assert quantity inputs after split line', async () => { await expect( - receivingPage.receivingStep.table.row(1).receivingNowField.textbox - ).toBeEmpty(); - await expect( - receivingPage.receivingStep.table.row(2).receivingNowField.textbox - ).toBeEmpty(); + receivingPage.receivingStep.table.getCellValue(1, 'Receiving now') + ).toHaveText('50'); await expect( - receivingPage.receivingStep.table.row(3).receivingNowField.textbox - ).toHaveValue('200'); + receivingPage.receivingStep.table.row(3).receivingNowField.numberbox + ).toHaveValue('25'); await expect( - receivingPage.receivingStep.table.row(4).receivingNowField.textbox - ).toHaveValue('100'); + receivingPage.receivingStep.table.row(4).receivingNowField.numberbox + ).toHaveValue('25'); await expect( - receivingPage.receivingStep.table.getCellValue(1, 'Lot/Serial No.') + receivingPage.receivingStep.table.getCellValue(4, 'Lot/SN') ).toContainText(lot); await expect( - receivingPage.receivingStep.table.getCellValue(1, 'Expiration date') - ).toContainText(formatDate(expDate, DateFormat.DEFAULT)); + receivingPage.receivingStep.table.getCellValue(4, 'Exp Date') + ).toContainText(formatDate(expDate, DateFormat.DISPLAY)); + await expect( + receivingPage.receivingStep.table.row(5).receivingNowField.numberbox + ).toHaveValue('100'); + await expect( + receivingPage.receivingStep.table.row(6).receivingNowField.numberbox + ).toHaveValue('200'); }); - await test.step('Autofill quantity after split line', async () => { - await receivingPage.receivingStep.isLoaded(); + await test.step('Autofill quantity after split line does not override inputs', async () => { await receivingPage.receivingStep.autofillQuantitiesButton.click(); await expect( - receivingPage.receivingStep.table.row(1).receivingNowField.textbox + receivingPage.receivingStep.table.row(3).receivingNowField.numberbox ).toHaveValue('25'); await expect( - receivingPage.receivingStep.table.row(2).receivingNowField.textbox + receivingPage.receivingStep.table.row(4).receivingNowField.numberbox ).toHaveValue('25'); + await expect( + receivingPage.receivingStep.table.row(5).receivingNowField.numberbox + ).toHaveValue('100'); + await expect( + receivingPage.receivingStep.table.row(6).receivingNowField.numberbox + ).toHaveValue('200'); }); }); }); From 900eec99fde7e8686f80f6f957598b87b842da69 Mon Sep 17 00:00:00 2001 From: Alan Nadolny Date: Tue, 6 Oct 2026 15:59:24 +0200 Subject: [PATCH 05/19] OBPIH-8025 Unskip cancel remaining quantity --- src/pages/receiving/steps/CheckStep.ts | 4 +- .../receiving/cancelRemainingQty.test.ts | 86 ++++++++++--------- 2 files changed, 49 insertions(+), 41 deletions(-) diff --git a/src/pages/receiving/steps/CheckStep.ts b/src/pages/receiving/steps/CheckStep.ts index 761950d6..efcb000a 100644 --- a/src/pages/receiving/steps/CheckStep.ts +++ b/src/pages/receiving/steps/CheckStep.ts @@ -63,7 +63,9 @@ class CheckStep extends BasePageModel { } get backToEditButton() { - return this.page.getByRole('button', { name: 'Back to edit' }); + return this.page + .locator('.submit-buttons') + .getByRole('button', { name: 'Back to Receive' }); } } diff --git a/src/tests/receiving/cancelRemainingQty.test.ts b/src/tests/receiving/cancelRemainingQty.test.ts index 5f6c28c4..9aa00678 100644 --- a/src/tests/receiving/cancelRemainingQty.test.ts +++ b/src/tests/receiving/cancelRemainingQty.test.ts @@ -6,7 +6,7 @@ import { StockMovementResponse } from '@/types'; import BinLocationUtils from '@/utils/BinLocationUtils'; import { deleteShipment } from '@/utils/shipmentUtils'; -test.describe.skip('Cancel qty in the middle of receipt', () => { +test.describe('Cancel qty in the middle of receipt', () => { let STOCK_MOVEMENT: StockMovementResponse; test.beforeEach( @@ -75,27 +75,27 @@ test.describe.skip('Cancel qty in the middle of receipt', () => { await receivingPage.receivingStep.isLoaded(); await receivingPage.receivingStep.table .row(1) - .receivingNowField.textbox.fill('50'); + .receivingNowField.numberbox.fill('50'); await receivingPage.receivingStep.table .row(2) - .receivingNowField.textbox.fill('10'); + .receivingNowField.numberbox.fill('10'); }); await test.step('Go to check page and assert qty remaining', async () => { await receivingPage.nextButton.click(); await receivingPage.checkStep.isLoaded(); await expect( - receivingPage.checkStep.table.getCellValue(1, 'Remaining') - ).toHaveText('50'); + receivingPage.checkStep.table.getCellValue(1, 'Status') + ).toHaveText('50 remaining'); await expect( - receivingPage.checkStep.table.getCellValue(2, 'Remaining') - ).toHaveText('0'); + receivingPage.checkStep.table.getCellValue(2, 'Status') + ).toHaveText('Complete'); await expect( receivingPage.checkStep.table.row(1).cancelRemainingCheckbox ).toBeEnabled(); await expect( receivingPage.checkStep.table.row(2).cancelRemainingCheckbox - ).toBeDisabled(); + ).toBeHidden(); }); await test.step('Select cancel remaining qty checkbox and receive shipment', async () => { @@ -145,15 +145,15 @@ test.describe.skip('Cancel qty in the middle of receipt', () => { await receivingPage.receivingStep.isLoaded(); await receivingPage.receivingStep.table .row(1) - .receivingNowField.textbox.fill('50'); + .receivingNowField.numberbox.fill('50'); }); await test.step('Go to check page and finish 1st receipt', async () => { await receivingPage.nextButton.click(); await receivingPage.checkStep.isLoaded(); await expect( - receivingPage.checkStep.table.getCellValue(1, 'Remaining') - ).toHaveText('50'); + receivingPage.checkStep.table.getCellValue(1, 'Status') + ).toHaveText('50 remaining'); await expect( receivingPage.checkStep.table.row(1).cancelRemainingCheckbox ).toBeEnabled(); @@ -178,21 +178,21 @@ test.describe.skip('Cancel qty in the middle of receipt', () => { await receivingPage.receivingStep.isLoaded(); await receivingPage.receivingStep.table .row(1) - .receivingNowField.textbox.fill('25'); + .receivingNowField.numberbox.fill('25'); await receivingPage.receivingStep.table .row(2) - .receivingNowField.textbox.fill('0'); + .receivingNowField.numberbox.fill('0'); }); await test.step('Go to check page and assert qty remaining', async () => { await receivingPage.nextButton.click(); await receivingPage.checkStep.isLoaded(); await expect( - receivingPage.checkStep.table.getCellValue(1, 'Remaining') - ).toHaveText('10'); + receivingPage.checkStep.table.getCellValue(1, 'Status') + ).toHaveText('25 remaining'); await expect( - receivingPage.checkStep.table.getCellValue(2, 'Remaining') - ).toHaveText('25'); + receivingPage.checkStep.table.getCellValue(2, 'Status') + ).toHaveText('10 remaining'); await expect( receivingPage.checkStep.table.row(1).cancelRemainingCheckbox ).toBeEnabled(); @@ -221,27 +221,32 @@ test.describe.skip('Cancel qty in the middle of receipt', () => { await test.step('Assert canceled qty on stock movement show page', async () => { await expect(stockMovementShowPage.statusTag).toHaveText('Received'); await stockMovementShowPage.openReceiptsTab(); - await expect( - stockMovementShowPage.receiptListTable.row(2).quantityCanceled - ).toHaveText('10'); - await expect( - stockMovementShowPage.receiptListTable.row(2).quantityReceived - ).toHaveText('0'); + // the 1st receipt holds a line for every item (rows 1-2), so the + // 2nd receipt lines are rows 3-4 await expect( stockMovementShowPage.receiptListTable.row(3).quantityCanceled ).toHaveText('25'); await expect( stockMovementShowPage.receiptListTable.row(3).quantityReceived ).toHaveText('25'); + await expect( + stockMovementShowPage.receiptListTable.row(4).quantityCanceled + ).toHaveText('10'); + await expect( + stockMovementShowPage.receiptListTable.row(4).quantityReceived + ).toHaveText('0'); }); await test.step('Rollback shipment received in 2 receipts', async () => { await stockMovementShowPage.isLoaded(); await stockMovementShowPage.rollbackLastReceiptButton.click(); + // wait for the rollback to finish, so it doesn't race with the cleanup + await stockMovementShowPage.isLoaded(); + await expect(stockMovementShowPage.statusTag).toHaveText('Receiving'); }); }); - test('Assert remaining qty and disabled cancel remaining checkbox when receive qty bigger than shipped', async ({ + test('Assert over received qty and no cancel remaining checkbox when receive qty bigger than shipped', async ({ stockMovementShowPage, receivingPage, }) => { @@ -259,27 +264,27 @@ test.describe.skip('Cancel qty in the middle of receipt', () => { await receivingPage.receivingStep.isLoaded(); await receivingPage.receivingStep.table .row(1) - .receivingNowField.textbox.fill('200'); + .receivingNowField.numberbox.fill('200'); await receivingPage.receivingStep.table .row(2) - .receivingNowField.textbox.fill('100'); + .receivingNowField.numberbox.fill('100'); }); await test.step('Go to check page and assert qty remaining', async () => { await receivingPage.nextButton.click(); await receivingPage.checkStep.isLoaded(); await expect( - receivingPage.checkStep.table.getCellValue(1, 'Remaining') - ).toHaveText('-100'); + receivingPage.checkStep.table.getCellValue(1, 'Status') + ).toHaveText('100 over'); await expect( - receivingPage.checkStep.table.getCellValue(2, 'Remaining') - ).toHaveText('-90'); + receivingPage.checkStep.table.getCellValue(2, 'Status') + ).toHaveText('90 over'); await expect( receivingPage.checkStep.table.row(1).cancelRemainingCheckbox - ).toBeDisabled(); + ).toBeHidden(); await expect( receivingPage.checkStep.table.row(2).cancelRemainingCheckbox - ).toBeDisabled(); + ).toBeHidden(); }); await test.step('Receive shipment', async () => { @@ -323,21 +328,21 @@ test.describe.skip('Cancel qty in the middle of receipt', () => { await receivingPage.receivingStep.isLoaded(); await receivingPage.receivingStep.table .row(1) - .receivingNowField.textbox.fill('50'); + .receivingNowField.numberbox.fill('50'); await receivingPage.receivingStep.table .row(2) - .receivingNowField.textbox.fill('5'); + .receivingNowField.numberbox.fill('5'); }); await test.step('Go to check page and assert qty remaining', async () => { await receivingPage.nextButton.click(); await receivingPage.checkStep.isLoaded(); await expect( - receivingPage.checkStep.table.getCellValue(1, 'Remaining') - ).toHaveText('50'); + receivingPage.checkStep.table.getCellValue(1, 'Status') + ).toHaveText('50 remaining'); await expect( - receivingPage.checkStep.table.getCellValue(2, 'Remaining') - ).toHaveText('5'); + receivingPage.checkStep.table.getCellValue(2, 'Status') + ).toHaveText('5 remaining'); await expect( receivingPage.checkStep.table.row(1).cancelRemainingCheckbox ).toBeEnabled(); @@ -394,7 +399,7 @@ test.describe.skip('Cancel qty in the middle of receipt', () => { await receivingPage.receivingStep.isLoaded(); await receivingPage.receivingStep.table .row(1) - .receivingNowField.textbox.fill('50'); + .receivingNowField.numberbox.fill('50'); }); await test.step('Go to check page', async () => { @@ -446,7 +451,7 @@ test.describe.skip('Cancel qty in the middle of receipt', () => { await receivingPage.receivingStep.isLoaded(); await receivingPage.receivingStep.table .row(2) - .receivingNowField.textbox.fill('2'); + .receivingNowField.numberbox.fill('2'); }); await test.step('Go to check page and cancel qty remaining', async () => { @@ -489,6 +494,7 @@ test.describe.skip('Cancel qty in the middle of receipt', () => { await test.step('Rollback shipment received in 2 receipts', async () => { await stockMovementShowPage.isLoaded(); await stockMovementShowPage.rollbackLastReceiptButton.click(); + await stockMovementShowPage.isLoaded(); }); }); }); From d170a3ce0874423adaa1b630df6aae350aba4fc3 Mon Sep 17 00:00:00 2001 From: Alan Nadolny Date: Tue, 6 Oct 2026 17:34:52 +0200 Subject: [PATCH 06/19] OBPIH-8025 Unskip edit bin location when receive --- .../receiving/components/ReceivingTable.ts | 43 +++++++++ .../editBinLocationWhenReceive.test.ts | 88 +++++++++++-------- 2 files changed, 93 insertions(+), 38 deletions(-) diff --git a/src/pages/receiving/components/ReceivingTable.ts b/src/pages/receiving/components/ReceivingTable.ts index 9edcc98a..9b436cad 100644 --- a/src/pages/receiving/components/ReceivingTable.ts +++ b/src/pages/receiving/components/ReceivingTable.ts @@ -32,6 +32,31 @@ class ReceivingTable extends BasePageModel { getCellValue(row: number, column: string) { return this.rows.nth(row - 1).locator(`[aria-label="${column}"]`); } + + // "Autofill" select in the Location column header (visible only when the + // "Show Putaway" switch is on) + get locationAutofillSelect() { + return this.table.getByTestId('location-autofill'); + } + + // shown when the autofill would overwrite a location the user has changed + get autofillLocationConfirmDialog() { + return this.page.locator('.react-confirm-alert'); + } + + get acceptAutofillLocationConfirmDialog() { + return this.autofillLocationConfirmDialog.getByRole('button', { + name: 'Yes', + }); + } + + async autofillLocation(option: string) { + await this.locationAutofillSelect.click(); + await this.page + .getByTestId('custom-select-dropdown-menu') + .getByText(option, { exact: true }) + .click(); + } } class Row extends BasePageModel { @@ -57,6 +82,24 @@ class Row extends BasePageModel { return this.row.locator('[aria-label="Location"]'); } + // the selected value only, without the screen reader text react-select + // renders next to it while focused + get selectedBinLocation() { + return this.binLocationSelect.locator('.react-select__single-value'); + } + + getBinLocation(binLocation: string) { + return this.page + .getByTestId('custom-select-dropdown-menu') + .getByText(binLocation, { exact: true }); + } + + async selectBinLocation(binLocation: string) { + await this.binLocationSelect.click(); + await this.page.keyboard.type(binLocation); + await this.getBinLocation(binLocation).click(); + } + getItem(name: string) { return this.row.locator('[aria-label="Product"]').getByText(name); } diff --git a/src/tests/receiving/editBinLocationWhenReceive.test.ts b/src/tests/receiving/editBinLocationWhenReceive.test.ts index dd52885c..7f563be8 100644 --- a/src/tests/receiving/editBinLocationWhenReceive.test.ts +++ b/src/tests/receiving/editBinLocationWhenReceive.test.ts @@ -8,7 +8,7 @@ import BinLocationUtils from '@/utils/BinLocationUtils'; import { deleteShipment } from '@/utils/shipmentUtils'; import UniqueIdentifier from '@/utils/UniqueIdentifier'; -test.describe.skip('Edit Bin Location when receive inbound stock movement', () => { +test.describe('Edit Bin Location when receive inbound stock movement', () => { test.describe.configure({ timeout: 60000 }); //timeout has been added for this test to make sure that the content on bin location tab will load as it can include a lot of data let STOCK_MOVEMENT: StockMovementResponse; @@ -130,15 +130,16 @@ test.describe.skip('Edit Bin Location when receive inbound stock movement', () = }); await test.step('Edit bin when receive item', async () => { - await receivingPage.receivingStep.isLoaded(); - await receivingPage.receivingStep.table.row(1).binLocationSelect.click(); + await receivingPage.receivingStep.enableShowPutaway(); await receivingPage.receivingStep.table .row(1) - .getBinLocation(binLocationName) - .click(); + .selectBinLocation(binLocationName); + await expect( + receivingPage.receivingStep.table.row(1).selectedBinLocation + ).toHaveText(binLocationName); await receivingPage.receivingStep.table .row(1) - .receivingNowField.textbox.fill('10'); + .receivingNowField.numberbox.fill('10'); }); await test.step('Go to check page', async () => { @@ -146,8 +147,10 @@ test.describe.skip('Edit Bin Location when receive inbound stock movement', () = await receivingPage.checkStep.isLoaded(); }); - await test.step('Finish receipt of item', async () => { - await receivingPage.checkStep.isLoaded(); + await test.step('Assert bin on check page and finish receipt of item', async () => { + await expect( + receivingPage.checkStep.table.getCellValue(1, 'Location') + ).toHaveText(binLocationName); await receivingPage.checkStep.receiveShipmentButton.click(); await stockMovementShowPage.isLoaded(); }); @@ -162,14 +165,19 @@ test.describe.skip('Edit Bin Location when receive inbound stock movement', () = await stockMovementShowPage.packingListTable.row(1).product.click(); await productShowPage.inStockTab.click(); await productShowPage.inStockTabSection.isLoaded(); + // the product has stock in other bins too, so the row order isn't fixed + const rowIndex = + await productShowPage.inStockTabSection.getRowIndexByBinLocation( + binLocationName + ); await expect( - productShowPage.inStockTabSection.row(2).binLocation + productShowPage.inStockTabSection.row(rowIndex).binLocation ).toHaveText(binLocationName); }); }); }); -test.describe.skip('Edit Bin Location to bin with zone when receive inbound stock movement', () => { +test.describe('Edit Bin Location to bin with zone when receive inbound stock movement', () => { test.describe.configure({ timeout: 60000 }); //timeout has been added for this test to make sure that the content on bin location tab will load as it can include a lot of data let STOCK_MOVEMENT: StockMovementResponse; @@ -333,19 +341,16 @@ test.describe.skip('Edit Bin Location to bin with zone when receive inbound stoc }); await test.step('Edit bin when receive item', async () => { - await receivingPage.receivingStep.isLoaded(); - await receivingPage.receivingStep.table.row(1).binLocationSelect.click(); - await receivingPage.receivingStep.table - .row(1) - .getZoneLocation(zoneLocationName) - .isVisible(); + await receivingPage.receivingStep.enableShowPutaway(); await receivingPage.receivingStep.table .row(1) - .getBinLocation(binLocationName) - .click(); + .selectBinLocation(binLocationName); + await expect( + receivingPage.receivingStep.table.row(1).selectedBinLocation + ).toHaveText(binLocationName); await receivingPage.receivingStep.table .row(1) - .receivingNowField.textbox.fill('10'); + .receivingNowField.numberbox.fill('10'); }); await test.step('Go to check page', async () => { @@ -353,12 +358,10 @@ test.describe.skip('Edit Bin Location to bin with zone when receive inbound stoc await receivingPage.checkStep.isLoaded(); }); - await test.step('Assert zone and bin on check page and finish receipt of item', async () => { - await receivingPage.checkStep.isLoaded(); - const zoneAndBin = `${zoneLocationName}: ${binLocationName}`; + await test.step('Assert bin on check page and finish receipt of item', async () => { await expect( - receivingPage.checkStep.table.getCellValue(1, 'Bin Location') - ).toHaveText(zoneAndBin); + receivingPage.checkStep.table.getCellValue(1, 'Location') + ).toHaveText(binLocationName); await receivingPage.checkStep.receiveShipmentButton.click(); await stockMovementShowPage.isLoaded(); }); @@ -374,17 +377,22 @@ test.describe.skip('Edit Bin Location to bin with zone when receive inbound stoc await stockMovementShowPage.packingListTable.row(1).product.click(); await productShowPage.inStockTab.click(); await productShowPage.inStockTabSection.isLoaded(); + // the product has stock in other bins too, so the row order isn't fixed + const rowIndex = + await productShowPage.inStockTabSection.getRowIndexByBinLocation( + binLocationName + ); await expect( - productShowPage.inStockTabSection.row(2).zoneLocation + productShowPage.inStockTabSection.row(rowIndex).zoneLocation ).toHaveText(zoneLocationName); await expect( - productShowPage.inStockTabSection.row(2).binLocation + productShowPage.inStockTabSection.row(rowIndex).binLocation ).toHaveText(binLocationName); }); }); }); -test.describe.skip('Edit Bin Location when receive for all lines', () => { +test.describe('Edit Bin Location when receive for all lines', () => { test.describe.configure({ timeout: 60000 }); //timeout has been added for this test to make sure that the content on bin location tab will load as it can include a lot of data let STOCK_MOVEMENT: StockMovementResponse; @@ -504,18 +512,23 @@ test.describe.skip('Edit Bin Location when receive for all lines', () => { await receivingPage.receivingStep.isLoaded(); }); - await test.step('Edit bin when receive item', async () => { - await receivingPage.receivingStep.isLoaded(); - await receivingPage.receivingStep.table.row(0).binLocationSelect.click(); + await test.step('Edit bin for the first line and fill it down to all lines', async () => { + await receivingPage.receivingStep.enableShowPutaway(); await receivingPage.receivingStep.table - .row(0) - .getBinLocation(binLocationName) - .click(); + .row(1) + .selectBinLocation(binLocationName); + await receivingPage.receivingStep.table.autofillLocation( + 'Fill down from top row' + ); + await expect( + receivingPage.receivingStep.table.autofillLocationConfirmDialog + ).toBeVisible(); + await receivingPage.receivingStep.table.acceptAutofillLocationConfirmDialog.click(); await expect( - receivingPage.receivingStep.table.row(1).binLocationSelect + receivingPage.receivingStep.table.row(1).selectedBinLocation ).toHaveText(binLocationName); await expect( - receivingPage.receivingStep.table.row(2).binLocationSelect + receivingPage.receivingStep.table.row(2).selectedBinLocation ).toHaveText(binLocationName); }); @@ -526,12 +539,11 @@ test.describe.skip('Edit Bin Location when receive for all lines', () => { }); await test.step('Finish receipt of item', async () => { - await receivingPage.checkStep.isLoaded(); await expect( - receivingPage.checkStep.table.getCellValue(1, 'Bin Location') + receivingPage.checkStep.table.getCellValue(1, 'Location') ).toHaveText(binLocationName); await expect( - receivingPage.checkStep.table.getCellValue(2, 'Bin Location') + receivingPage.checkStep.table.getCellValue(2, 'Location') ).toHaveText(binLocationName); await receivingPage.checkStep.receiveShipmentButton.click(); await stockMovementShowPage.isLoaded(); From 964a22cf3a20c9777c963b87d253c0f12a43034f Mon Sep 17 00:00:00 2001 From: Alan Nadolny Date: Wed, 7 Oct 2026 13:37:08 +0200 Subject: [PATCH 07/19] OBPIH-8025 Unskip edit original line quantity to 0 --- .../receiving/components/EditModalTable.ts | 8 +- .../components/ReceiptsTable.ts | 4 + .../receiving/editOriginalLineQtyTo0.test.ts | 192 +++++++----------- 3 files changed, 82 insertions(+), 122 deletions(-) diff --git a/src/pages/receiving/components/EditModalTable.ts b/src/pages/receiving/components/EditModalTable.ts index 3c4771a7..7837926e 100644 --- a/src/pages/receiving/components/EditModalTable.ts +++ b/src/pages/receiving/components/EditModalTable.ts @@ -43,12 +43,16 @@ class Row extends BasePageModel { return this.row.locator('[aria-label="Location"]'); } + get productSelect() { + return this.row.locator('[aria-label="Product"]'); + } + get clearProductSelect() { - return this.row.locator('.react-select__clear-indicator'); + return this.productSelect.locator('.react-select__clear-indicator'); } async getProductSelect(name: string) { - await this.row + await this.productSelect .getByTestId('custom-select-element') .getByRole('textbox') .fill(name); diff --git a/src/pages/stockMovementShow/components/ReceiptsTable.ts b/src/pages/stockMovementShow/components/ReceiptsTable.ts index b3cbb0bc..7cca4d5b 100644 --- a/src/pages/stockMovementShow/components/ReceiptsTable.ts +++ b/src/pages/stockMovementShow/components/ReceiptsTable.ts @@ -26,6 +26,10 @@ class ReceiptsListTable extends BasePageModel { row(index: number) { return new Row(this.page, this.rows.nth(index)); } + + getRowByText(text: string) { + return new Row(this.page, this.rows.filter({ hasText: text })); + } } class Row extends BasePageModel { diff --git a/src/tests/receiving/editOriginalLineQtyTo0.test.ts b/src/tests/receiving/editOriginalLineQtyTo0.test.ts index 98536a10..1ae77949 100644 --- a/src/tests/receiving/editOriginalLineQtyTo0.test.ts +++ b/src/tests/receiving/editOriginalLineQtyTo0.test.ts @@ -8,7 +8,7 @@ import { getDateByOffset } from '@/utils/DateUtils'; import { deleteShipment } from '@/utils/shipmentUtils'; import UniqueIdentifier from '@/utils/UniqueIdentifier'; -test.describe.skip('Edit qty of original line to 0', () => { +test.describe('Edit qty of original line to 0', () => { let STOCK_MOVEMENT: StockMovementResponse; const uniqueIdentifier = new UniqueIdentifier(); const lot = uniqueIdentifier.generateUniqueString('lot'); @@ -88,97 +88,77 @@ test.describe.skip('Edit qty of original line to 0', () => { await receivingPage.receivingStep.isLoaded(); }); - await test.step('Open edit modal for item with lot', async () => { - await receivingPage.receivingStep.table.row(5).checkbox.check(); - await receivingPage.receivingStep.table.row(5).editButton.click(); + await test.step('Open edit modal for item with lot and move its qty to a line without lot', async () => { + await receivingPage.receivingStep.table.row(3).editButton.click(); await receivingPage.receivingStep.editModal.isLoaded(); - await receivingPage.receivingStep.editModal.addLineButton.click(); - await receivingPage.receivingStep.editModal.table - .row(0) - .quantityShippedField.numberbox.fill('0'); await receivingPage.receivingStep.editModal.table .row(1) - .quantityShippedField.numberbox.fill('200'); + .receivingNowField.numberbox.fill('0'); + await receivingPage.receivingStep.editModal.addLineButton.click(); + await receivingPage.receivingStep.editModal.table + .row(2) + .receivingNowField.numberbox.fill('200'); await receivingPage.receivingStep.editModal.saveButton.click(); + await expect(receivingPage.receivingStep.editModal.modal).toBeHidden(); await receivingPage.receivingStep.isLoaded(); }); - await test.step('Assert line with qty 0 is disabled', async () => { - await expect( - receivingPage.receivingStep.table.getCellValue(5, 'Shipped') - ).toContainText('0'); + // The original line received with 0 is not listed among the changes, so + // the item with lot shows the replaced row (3), the changes toggle row (4) + // and only the new line without lot (5). + await test.step('Assert original line with qty 0 is not visible', async () => { + await expect(receivingPage.receivingStep.table.rows).toHaveCount(8); await expect( - receivingPage.receivingStep.table.getCellValue(5, 'Received') - ).toContainText('0'); + receivingPage.receivingStep.table.getCellValue(3, 'Lot/SN') + ).toContainText(lot); await expect( - receivingPage.receivingStep.table.getCellValue(5, 'To receive') - ).toContainText('0'); + receivingPage.receivingStep.table.getCellValue(3, 'Receiving now') + ).toHaveText('200'); await expect( - receivingPage.receivingStep.table.row(5).receivingNowField.textbox - ).toBeDisabled(); + receivingPage.receivingStep.table.row(5).receivingNowField.numberbox + ).toHaveValue('200'); await expect( - receivingPage.receivingStep.table.getCellValue(6, 'To receive') - ).toContainText('200'); - await expect(receivingPage.receivingStep.table.rows).toHaveCount(8); + receivingPage.receivingStep.table.getCellValue(5, 'Lot/SN') + ).toBeEmpty(); }); - await test.step('Select items to receive using checkboxes', async () => { - await receivingPage.receivingStep.isLoaded(); - await receivingPage.receivingStep.table.row(4).checkbox.check(); - await receivingPage.receivingStep.table.row(6).checkbox.check(); - await expect( - receivingPage.receivingStep.table.row(4).receivingNowField.textbox - ).toHaveValue('50'); - await expect( - receivingPage.receivingStep.table.row(6).receivingNowField.textbox - ).toHaveValue('200'); + await test.step('Input receiving qty for item without lot', async () => { + await receivingPage.receivingStep.table + .row(6) + .receivingNowField.numberbox.fill('50'); }); - await test.step('Assert lot and exp date on rows', async () => { + await test.step('Assert lot on check step', async () => { await receivingPage.nextButton.click(); await receivingPage.checkStep.isLoaded(); + await expect(receivingPage.checkStep.table.rows).toHaveCount(4); await expect( - receivingPage.checkStep.table.getCellValue(1, 'Lot/Serial No.') - ).toBeEmpty(); + receivingPage.checkStep.table.getCellValue(3, 'Receiving now') + ).toHaveText('200'); await expect( - receivingPage.checkStep.table.getCellValue(1, 'Expiration date') + receivingPage.checkStep.table.getCellValue(3, 'Lot/SN') ).toBeEmpty(); await expect( - receivingPage.checkStep.table.getCellValue(2, 'Lot/Serial No.') - ).toBeEmpty(); + receivingPage.checkStep.table.getCellValue(4, 'Receiving now') + ).toHaveText('50'); await expect( - receivingPage.checkStep.table.getCellValue(2, 'Expiration date') + receivingPage.checkStep.table.getCellValue(4, 'Lot/SN') ).toBeEmpty(); }); - await test.step('Return to receiving step and assert orininal line is not visible', async () => { + await test.step('Return to receiving step and assert original line is not visible', async () => { await receivingPage.checkStep.backToEditButton.click(); await receivingPage.receivingStep.isLoaded(); - await expect(receivingPage.receivingStep.table.rows).toHaveCount(7); - await expect( - receivingPage.receivingStep.table.row(5).checkbox - ).toBeChecked(); - await expect( - receivingPage.receivingStep.table.row(6).checkbox - ).toBeChecked(); - await expect( - receivingPage.receivingStep.table.row(5).receivingNowField.textbox - ).toHaveValue('50'); + await expect(receivingPage.receivingStep.table.rows).toHaveCount(8); await expect( - receivingPage.receivingStep.table.row(6).receivingNowField.textbox + receivingPage.receivingStep.table.row(5).receivingNowField.numberbox ).toHaveValue('200'); await expect( - receivingPage.receivingStep.table.getCellValue(5, 'Lot/Serial No.') + receivingPage.receivingStep.table.getCellValue(5, 'Lot/SN') ).toBeEmpty(); await expect( - receivingPage.receivingStep.table.getCellValue(5, 'Expiration date') - ).toBeEmpty(); - await expect( - receivingPage.receivingStep.table.getCellValue(6, 'Lot/Serial No.') - ).toBeEmpty(); - await expect( - receivingPage.receivingStep.table.getCellValue(6, 'Expiration date') - ).toBeEmpty(); + receivingPage.receivingStep.table.row(6).receivingNowField.numberbox + ).toHaveValue('50'); }); await test.step('Receive shipment', async () => { @@ -188,30 +168,24 @@ test.describe.skip('Edit qty of original line to 0', () => { await stockMovementShowPage.isLoaded(); }); - await test.step('Assert received lines on Receipt tab', async () => { + await test.step('Assert nothing was received with lot on Receipt tab', async () => { await stockMovementShowPage.openReceiptsTab(); - await expect(stockMovementShowPage.receiptListTable.rows).toHaveCount(3); await expect( - stockMovementShowPage.receiptListTable.row(1).serialLotNumber - ).not.toHaveText(lot); - await expect( - stockMovementShowPage.receiptListTable.row(2).serialLotNumber - ).not.toHaveText(lot); + stockMovementShowPage.receiptListTable.getRowByText(lot) + .quantityReceived + ).toHaveText('0'); }); await test.step('Assert received lines on Packing list', async () => { await stockMovementShowPage.openPackingListTab(); await expect( - stockMovementShowPage.packingListTable.row(1).lotNumber - ).not.toHaveText(lot); - await expect( - stockMovementShowPage.packingListTable.row(2).lotNumber - ).not.toHaveText(lot); + stockMovementShowPage.packingListTable.rows.filter({ hasText: lot }) + ).toHaveCount(0); }); }); }); -test.describe.skip('Edit original line to other product in the middle of receipt', () => { +test.describe('Edit original line to other product in the middle of receipt', () => { let STOCK_MOVEMENT: StockMovementResponse; test.beforeEach( @@ -270,65 +244,43 @@ test.describe.skip('Edit original line to other product in the middle of receipt await receivingPage.receivingStep.isLoaded(); }); - await test.step('Open edit modal for item', async () => { + await test.step('Open edit modal for item and change product of new line', async () => { const PRODUCT_FIVE = await productService.getProduct(Product.FIVE); await receivingPage.receivingStep.table.row(1).editButton.click(); await receivingPage.receivingStep.editModal.isLoaded(); - await receivingPage.receivingStep.editModal.addLineButton.click(); - await receivingPage.receivingStep.editModal.table - .row(0) - .quantityShippedField.numberbox.fill('0'); await receivingPage.receivingStep.editModal.table .row(1) - .clearProductSelect.click(); + .receivingNowField.numberbox.fill('0'); + await receivingPage.receivingStep.editModal.addLineButton.click(); await receivingPage.receivingStep.editModal.table - .row(1) - .quantityShippedField.numberbox.fill('10'); + .row(2) + .clearProductSelect.click(); await receivingPage.receivingStep.editModal.table - .row(1) + .row(2) .getProductSelect(PRODUCT_FIVE.name); + await receivingPage.receivingStep.editModal.table + .row(2) + .receivingNowField.numberbox.fill('10'); await receivingPage.receivingStep.editModal.saveButton.click(); + await expect(receivingPage.receivingStep.editModal.modal).toBeHidden(); await receivingPage.receivingStep.isLoaded(); }); - await test.step('Assert line with qty 0 is disabled', async () => { + await test.step('Assert original line is replaced by line with other product', async () => { const PRODUCT_FOUR = await productService.getProduct(Product.FOUR); const PRODUCT_FIVE = await productService.getProduct(Product.FIVE); - await expect( - receivingPage.receivingStep.table.row(1).checkbox - ).toBeDisabled(); + await expect(receivingPage.receivingStep.table.rows).toHaveCount(3); await expect( receivingPage.receivingStep.table.getCellValue(1, 'Product') - ).toHaveText(PRODUCT_FOUR.name); + ).toContainText(PRODUCT_FOUR.name); await expect( - receivingPage.receivingStep.table.getCellValue(1, 'Shipped') - ).toContainText('0'); - await expect( - receivingPage.receivingStep.table.getCellValue(1, 'Received') - ).toContainText('0'); - await expect( - receivingPage.receivingStep.table.getCellValue(1, 'To receive') - ).toContainText('0'); - await expect( - receivingPage.receivingStep.table.row(1).receivingNowField.textbox - ).toBeDisabled(); - await expect( - receivingPage.receivingStep.table.row(1).commentField.textbox - ).toBeDisabled(); + receivingPage.receivingStep.table.getCellValue(1, 'Receiving now') + ).toHaveText('10'); await expect( - receivingPage.receivingStep.table.getCellValue(2, 'Product') - ).toHaveText(PRODUCT_FIVE.name); + receivingPage.receivingStep.table.getCellValue(3, 'Product') + ).toContainText(PRODUCT_FIVE.name); await expect( - receivingPage.receivingStep.table.getCellValue(2, 'To receive') - ).toContainText('10'); - await expect(receivingPage.receivingStep.table.rows).toHaveCount(3); - }); - - await test.step('Select item to receive using checkboxes', async () => { - await receivingPage.receivingStep.isLoaded(); - await receivingPage.receivingStep.table.row(2).checkbox.check(); - await expect( - receivingPage.receivingStep.table.row(2).receivingNowField.textbox + receivingPage.receivingStep.table.row(3).receivingNowField.numberbox ).toHaveValue('10'); }); @@ -337,9 +289,11 @@ test.describe.skip('Edit original line to other product in the middle of receipt await receivingPage.nextButton.click(); await receivingPage.checkStep.isLoaded(); await expect( - receivingPage.checkStep.table.getCellValue(1, 'Product') - ).toHaveText(PRODUCT_FIVE.name); - await expect(receivingPage.receivingStep.table.rows).toHaveCount(2); + receivingPage.checkStep.table.getCellValue(3, 'Product') + ).toContainText(PRODUCT_FIVE.name); + await expect( + receivingPage.checkStep.table.getCellValue(3, 'Receiving now') + ).toHaveText('10'); }); await test.step('Receive shipment', async () => { @@ -356,10 +310,8 @@ test.describe.skip('Edit original line to other product in the middle of receipt ).toHaveText(PRODUCT_FOUR.name); await stockMovementShowPage.openReceiptsTab(); await expect( - stockMovementShowPage.receiptListTable.row(1).product - ).toHaveText(PRODUCT_FIVE.name); - await expect( - stockMovementShowPage.receiptListTable.row(1).quantityReceived + stockMovementShowPage.receiptListTable.getRowByText(PRODUCT_FIVE.name) + .quantityReceived ).toHaveText('10'); }); }); From 7913b670739b98e6543b1ff3b96e1cd5f41282ab Mon Sep 17 00:00:00 2001 From: Alan Nadolny Date: Wed, 7 Oct 2026 15:19:37 +0200 Subject: [PATCH 08/19] OBPIH-8025 Unskip edit in receiving --- src/tests/receiving/editsInReceiving.test.ts | 222 ++++++++++--------- 1 file changed, 120 insertions(+), 102 deletions(-) diff --git a/src/tests/receiving/editsInReceiving.test.ts b/src/tests/receiving/editsInReceiving.test.ts index 9a9dd4d0..43746752 100644 --- a/src/tests/receiving/editsInReceiving.test.ts +++ b/src/tests/receiving/editsInReceiving.test.ts @@ -9,7 +9,7 @@ import { formatDate, getDateByOffset, getToday } from '@/utils/DateUtils'; import { deleteShipment } from '@/utils/shipmentUtils'; import UniqueIdentifier from '@/utils/UniqueIdentifier'; -test.describe.skip('Edit items in the middle of receipt', () => { +test.describe('Edit items in the middle of receipt', () => { let STOCK_MOVEMENT: StockMovementResponse; const description = 'some description'; const dateRequested = getToday(); @@ -86,10 +86,10 @@ test.describe.skip('Edit items in the middle of receipt', () => { await receivingPage.receivingStep.table.row(1).editButton.click(); await receivingPage.receivingStep.editModal.isLoaded(); await receivingPage.receivingStep.editModal.table - .row(0) - .quantityShippedField.numberbox.fill('50'); - await receivingPage.receivingStep.editModal.informationAboutEditedQtyNotMatchingShippedQty.isVisible(); + .row(1) + .receivingNowField.numberbox.fill('50'); await receivingPage.receivingStep.editModal.saveButton.click(); + await expect(receivingPage.receivingStep.editModal.modal).toBeHidden(); await receivingPage.receivingStep.isLoaded(); }); @@ -97,25 +97,26 @@ test.describe.skip('Edit items in the middle of receipt', () => { await receivingPage.receivingStep.table.row(2).editButton.click(); await receivingPage.receivingStep.editModal.isLoaded(); await receivingPage.receivingStep.editModal.table - .row(0) - .quantityShippedField.numberbox.fill('2'); - await receivingPage.receivingStep.editModal.informationAboutEditedQtyNotMatchingShippedQty.isVisible(); + .row(1) + .receivingNowField.numberbox.fill('2'); await receivingPage.receivingStep.editModal.saveButton.click(); + await expect(receivingPage.receivingStep.editModal.modal).toBeHidden(); await receivingPage.receivingStep.isLoaded(); }); - await test.step('Assert shipped qty after edits for both items', async () => { + await test.step('Assert receiving qty and status after edits for both items', async () => { await expect( - receivingPage.receivingStep.table.getCellValue(1, 'Shipped') - ).toContainText('50'); + receivingPage.receivingStep.table.row(1).receivingNowField.numberbox + ).toHaveValue('50'); await expect( - receivingPage.receivingStep.table.getCellValue(2, 'Shipped') - ).toContainText('2'); - }); - - await test.step('Autofill receiving qty for both items', async () => { - await receivingPage.receivingStep.isLoaded(); - await receivingPage.receivingStep.autofillQuantitiesButton.click(); + receivingPage.receivingStep.table.getCellValue(1, 'Status') + ).toHaveText('30 over'); + await expect( + receivingPage.receivingStep.table.row(2).receivingNowField.numberbox + ).toHaveValue('2'); + await expect( + receivingPage.receivingStep.table.getCellValue(2, 'Status') + ).toHaveText('8 remaining'); }); await test.step('Go to check page and receive shipment', async () => { @@ -126,7 +127,7 @@ test.describe.skip('Edit items in the middle of receipt', () => { }); }); - test('Assert validation on using exp date without lot for item with lot', async ({ + test('Assert lot of original line cannot be edited for item with lot', async ({ stockMovementShowPage, receivingPage, }) => { @@ -140,23 +141,13 @@ test.describe.skip('Edit items in the middle of receipt', () => { await receivingPage.receivingStep.isLoaded(); }); - await test.step('Open edit modal for 1st item and clear lot field', async () => { + await test.step('Open edit modal for 1st item and assert lot field is disabled', async () => { await receivingPage.receivingStep.table.row(1).editButton.click(); await receivingPage.receivingStep.editModal.isLoaded(); - await receivingPage.receivingStep.editModal.table - .row(0) - .lotNumberField.textbox.clear(); - await receivingPage.receivingStep.editModal.saveButton.click(); - await receivingPage.receivingStep.editModal.table - .row(0) - .lotNumberField.assertHasError(); - await receivingPage.receivingStep.editModal.table - .row(0) - .lotNumberField.textbox.hover(); await expect( - receivingPage.receivingStep.editModal.table.row(0).lotNumberField - .tooltip - ).toContainText('Items with an expiry date must also have a lot number.'); + receivingPage.receivingStep.editModal.table.row(1).lotNumberField + .textbox + ).toBeDisabled(); }); }); @@ -178,20 +169,33 @@ test.describe.skip('Edit items in the middle of receipt', () => { await receivingPage.receivingStep.table.row(2).editButton.click(); await receivingPage.receivingStep.editModal.isLoaded(); await receivingPage.receivingStep.editModal.table - .row(0) - .expiryDatePickerField.fill(getDateByOffset(new Date(), 5)); - await receivingPage.receivingStep.editModal.saveButton.click(); - await receivingPage.receivingStep.isLoaded(); + .row(1) + .receivingNowField.numberbox.fill('10'); + await receivingPage.receivingStep.editModal.table + .row(1) + .expiryDatePickerField.fillWithFormat( + getDateByOffset(new Date(), 5), + DateFormat.DISPLAY + ); + }); + + await test.step('Assert validation on exp date without lot', async () => { await receivingPage.receivingStep.editModal.table - .row(0) - .lotNumberField.assertHasError(); + .row(1) + .expiryDatePickerField.assertHasError(); await receivingPage.receivingStep.editModal.table - .row(0) - .lotNumberField.textbox.hover(); + .row(1) + .expiryDatePickerField.textbox.hover(); + await expect( + receivingPage.receivingStep.editModal.table + .row(1) + .expiryDatePickerField.tooltip.filter({ + hasText: 'Cannot enter an expiration date without a lot number', + }) + ).toBeVisible(); await expect( - receivingPage.receivingStep.editModal.table.row(0).lotNumberField - .tooltip - ).toContainText('Items with an expiry date must also have a lot number.'); + receivingPage.receivingStep.editModal.saveButton + ).toBeDisabled(); }); }); @@ -209,38 +213,51 @@ test.describe.skip('Edit items in the middle of receipt', () => { await receivingPage.receivingStep.isLoaded(); }); - await test.step('Open edit modal for 1st item and clear lot field', async () => { + await test.step('Open edit modal for 1st item and split line with exp date without lot', async () => { await receivingPage.receivingStep.table.row(1).editButton.click(); await receivingPage.receivingStep.editModal.isLoaded(); - await receivingPage.receivingStep.editModal.addLineButton.click(); - await receivingPage.receivingStep.editModal.table - .row(0) - .quantityShippedField.numberbox.fill('10'); await receivingPage.receivingStep.editModal.table .row(1) - .quantityShippedField.numberbox.fill('10'); + .receivingNowField.numberbox.fill('10'); + await receivingPage.receivingStep.editModal.addLineButton.click(); await receivingPage.receivingStep.editModal.table - .row(1) - .expiryDatePickerField.fill(getDateByOffset(new Date(), 5)); - await receivingPage.receivingStep.editModal.saveButton.click(); + .row(2) + .receivingNowField.numberbox.fill('10'); await receivingPage.receivingStep.editModal.table - .row(1) - .lotNumberField.assertHasError(); + .row(2) + .expiryDatePickerField.fillWithFormat( + getDateByOffset(new Date(), 5), + DateFormat.DISPLAY + ); + }); + + await test.step('Assert validation on exp date without lot', async () => { await receivingPage.receivingStep.editModal.table - .row(1) - .lotNumberField.textbox.hover(); + .row(2) + .expiryDatePickerField.assertHasError(); + await receivingPage.receivingStep.editModal.table + .row(2) + .expiryDatePickerField.textbox.hover(); await expect( - receivingPage.receivingStep.editModal.table.row(1).lotNumberField - .tooltip - ).toContainText('Items with an expiry date must also have a lot number.'); + receivingPage.receivingStep.editModal.table + .row(2) + .expiryDatePickerField.tooltip.filter({ + hasText: 'Cannot enter an expiration date without a lot number', + }) + ).toBeVisible(); + await expect( + receivingPage.receivingStep.editModal.saveButton + ).toBeDisabled(); }); }); + // The lot of the original line can't be changed, so a lot is added on a + // new line, while the original line is received with 0. test('Add lot and exp date for item in the middle of receipt', async ({ stockMovementShowPage, receivingPage, }) => { - const lot = 'add-lot-test'; + const lot = uniqueIdentifier.generateUniqueString('new-lot'); const expDate = getDateByOffset(new Date(), 5); await test.step('Go to stock movement show page', async () => { @@ -253,33 +270,38 @@ test.describe.skip('Edit items in the middle of receipt', () => { await receivingPage.receivingStep.isLoaded(); }); - await test.step('Open edit modal for item without lot and add lot and exp date', async () => { + await test.step('Open edit modal for item without lot and add line with lot and exp date', async () => { await receivingPage.receivingStep.table.row(2).editButton.click(); await receivingPage.receivingStep.editModal.isLoaded(); await receivingPage.receivingStep.editModal.table - .row(0) + .row(1) + .receivingNowField.numberbox.fill('0'); + await receivingPage.receivingStep.editModal.addLineButton.click(); + await receivingPage.receivingStep.editModal.table + .row(2) .lotNumberField.textbox.fill(lot); await receivingPage.receivingStep.editModal.table - .row(0) - .expiryDatePickerField.fill(expDate); + .row(2) + .expiryDatePickerField.fillWithFormat(expDate, DateFormat.DISPLAY); + await receivingPage.receivingStep.editModal.table + .row(2) + .receivingNowField.numberbox.fill('10'); await receivingPage.receivingStep.editModal.saveButton.click(); + await expect(receivingPage.receivingStep.editModal.modal).toBeHidden(); await receivingPage.receivingStep.isLoaded(); }); + // item rows: replaced row (2), changes toggle row (3), new line (4) await test.step('Assert added lot and exp date on receive page', async () => { await expect( - receivingPage.receivingStep.table.getCellValue(2, 'Lot/Serial No.') + receivingPage.receivingStep.table.getCellValue(4, 'Lot/SN') ).toContainText(lot); await expect( - receivingPage.receivingStep.table.getCellValue(2, 'Expiration date') - ).toContainText(formatDate(expDate, DateFormat.DEFAULT)); - }); - - await test.step('Select item to receive', async () => { - await receivingPage.receivingStep.isLoaded(); - await receivingPage.receivingStep.table - .row(2) - .receivingNowField.textbox.fill('10'); + receivingPage.receivingStep.table.getCellValue(4, 'Exp Date') + ).toContainText(formatDate(expDate, DateFormat.DISPLAY)); + await expect( + receivingPage.receivingStep.table.row(4).receivingNowField.numberbox + ).toHaveValue('10'); }); await test.step('Go to check page and receive shipment', async () => { @@ -294,7 +316,7 @@ test.describe.skip('Edit items in the middle of receipt', () => { stockMovementShowPage, receivingPage, }) => { - const lot = 'add-lot-test'; + const lot = uniqueIdentifier.generateUniqueString('new-lot'); const expDate = getDateByOffset(new Date(), 5); await test.step('Go to stock movement show page', async () => { @@ -310,46 +332,42 @@ test.describe.skip('Edit items in the middle of receipt', () => { await test.step('Open edit modal for item with lot and split into 2 lots', async () => { await receivingPage.receivingStep.table.row(1).editButton.click(); await receivingPage.receivingStep.editModal.isLoaded(); - await receivingPage.receivingStep.editModal.addLineButton.click(); - await receivingPage.receivingStep.editModal.table - .row(0) - .quantityShippedField.numberbox.fill('15'); await receivingPage.receivingStep.editModal.table .row(1) + .receivingNowField.numberbox.fill('15'); + await receivingPage.receivingStep.editModal.addLineButton.click(); + await receivingPage.receivingStep.editModal.table + .row(2) .lotNumberField.textbox.fill(lot); await receivingPage.receivingStep.editModal.table - .row(1) - .expiryDatePickerField.fill(expDate); + .row(2) + .expiryDatePickerField.fillWithFormat(expDate, DateFormat.DISPLAY); await receivingPage.receivingStep.editModal.table - .row(1) - .quantityShippedField.numberbox.fill('5'); + .row(2) + .receivingNowField.numberbox.fill('5'); await receivingPage.receivingStep.editModal.saveButton.click(); + await expect(receivingPage.receivingStep.editModal.modal).toBeHidden(); await receivingPage.receivingStep.isLoaded(); }); - await test.step('Assert added lot and exp date and shipped qty on receive page', async () => { + // item rows: replaced row (1), changes toggle row (2), original line (3), + // new line (4) + await test.step('Assert added lot and exp date and receiving qty on receive page', async () => { await expect( - receivingPage.receivingStep.table.getCellValue(2, 'Lot/Serial No.') - ).toContainText(lot); + receivingPage.receivingStep.table.getCellValue(1, 'Receiving now') + ).toHaveText('20'); await expect( - receivingPage.receivingStep.table.getCellValue(2, 'Expiration date') - ).toContainText(formatDate(expDate, DateFormat.DEFAULT)); + receivingPage.receivingStep.table.row(3).receivingNowField.numberbox + ).toHaveValue('15'); await expect( - receivingPage.receivingStep.table.getCellValue(1, 'Shipped') - ).toContainText('15'); + receivingPage.receivingStep.table.getCellValue(4, 'Lot/SN') + ).toContainText(lot); await expect( - receivingPage.receivingStep.table.getCellValue(2, 'Shipped') - ).toContainText('5'); - }); - - await test.step('Select splitted item to receive', async () => { - await receivingPage.receivingStep.isLoaded(); - await receivingPage.receivingStep.table - .row(1) - .receivingNowField.textbox.fill('15'); - await receivingPage.receivingStep.table - .row(2) - .receivingNowField.textbox.fill('5'); + receivingPage.receivingStep.table.getCellValue(4, 'Exp Date') + ).toContainText(formatDate(expDate, DateFormat.DISPLAY)); + await expect( + receivingPage.receivingStep.table.row(4).receivingNowField.numberbox + ).toHaveValue('5'); }); await test.step('Go to check page and receive shipment', async () => { From d837a5baa7c2ca537ff006559f38d95a37211564 Mon Sep 17 00:00:00 2001 From: Alan Nadolny Date: Wed, 7 Oct 2026 15:19:51 +0200 Subject: [PATCH 09/19] OBPIH-8025 Unskip lot and expiry system update on receiving --- .../lotExpirySystemUpdateOnReceiving.test.ts | 47 +++++++++++++++---- 1 file changed, 37 insertions(+), 10 deletions(-) diff --git a/src/tests/receiving/lotExpirySystemUpdateOnReceiving.test.ts b/src/tests/receiving/lotExpirySystemUpdateOnReceiving.test.ts index ffc849e0..cb063dcc 100644 --- a/src/tests/receiving/lotExpirySystemUpdateOnReceiving.test.ts +++ b/src/tests/receiving/lotExpirySystemUpdateOnReceiving.test.ts @@ -9,7 +9,7 @@ import { formatDate, getDateByOffset, getToday } from '@/utils/DateUtils'; import { deleteShipment } from '@/utils/shipmentUtils'; import UniqueIdentifier from '@/utils/UniqueIdentifier'; -test.describe.skip('Lot number system expiry date modification on receiving workflow', () => { +test.describe('Lot number system expiry date modification on receiving workflow', () => { const STOCK_MOVEMENTS: StockMovementResponse[] = []; test.afterEach( @@ -118,9 +118,16 @@ test.describe.skip('Lot number system expiry date modification on receiving work }); await test.step('Update expiration date of selected item lot', async () => { + // receiving now is required in the edit modal await receivingPage.receivingStep.editModal.table - .row(0) - .expiryDatePickerField.fill(UPDATED_EXPIRY_DATE_NEW_LOT); + .row(1) + .receivingNowField.numberbox.fill('10'); + await receivingPage.receivingStep.editModal.table + .row(1) + .expiryDatePickerField.fillWithFormat( + UPDATED_EXPIRY_DATE_NEW_LOT, + DateFormat.DISPLAY + ); await receivingPage.receivingStep.editModal.saveButton.click(); }); @@ -168,7 +175,7 @@ test.describe.skip('Lot number system expiry date modification on receiving work }); }); - test.describe.skip('Update existing lot', () => { + test.describe('Update existing lot', () => { const TEST_INPUT_STOCK_EXISTING_LOT = { lotNumber: 'lot', expirationDate: getDateByOffset(getToday(), 3), @@ -328,9 +335,16 @@ test.describe.skip('Lot number system expiry date modification on receiving work }); await test.step('Update expiration date of selected item lot', async () => { + // receiving now is required in the edit modal + await receivingPage.receivingStep.editModal.table + .row(1) + .receivingNowField.numberbox.fill('10'); await receivingPage.receivingStep.editModal.table - .row(0) - .expiryDatePickerField.fill(UPDATED_EXPIRY_DATE); + .row(1) + .expiryDatePickerField.fillWithFormat( + UPDATED_EXPIRY_DATE, + DateFormat.DISPLAY + ); await receivingPage.receivingStep.editModal.saveButton.click(); }); @@ -342,12 +356,17 @@ test.describe.skip('Lot number system expiry date modification on receiving work }); await test.step('Cancel update expiry date popup', async () => { - await receivingPage.receivingStep.updateExpiryDatePopup.noButton.click(); + await receivingPage.receivingStep.updateExpiryDatePopup.cancelButton.click(); await expect( receivingPage.receivingStep.updateExpiryDatePopup.tableDialog ).toBeHidden(); }); + await test.step('Close edit modal left open after canceling popup', async () => { + await receivingPage.receivingStep.editModal.cancelButton.click(); + await expect(receivingPage.receivingStep.editModal.modal).toBeHidden(); + }); + await test.step('Autofill all quantities of receving items', async () => { await receivingPage.receivingStep.autofillQuantitiesButton.click(); }); @@ -442,9 +461,16 @@ test.describe.skip('Lot number system expiry date modification on receiving work }); await test.step('Update expiration date of selected item lot', async () => { + // receiving now is required in the edit modal + await receivingPage.receivingStep.editModal.table + .row(1) + .receivingNowField.numberbox.fill('10'); await receivingPage.receivingStep.editModal.table - .row(0) - .expiryDatePickerField.fill(UPDATED_EXPIRY_DATE); + .row(1) + .expiryDatePickerField.fillWithFormat( + UPDATED_EXPIRY_DATE, + DateFormat.DISPLAY + ); await receivingPage.receivingStep.editModal.saveButton.click(); }); @@ -456,10 +482,11 @@ test.describe.skip('Lot number system expiry date modification on receiving work }); await test.step('Confirm update expiry date popup', async () => { - await receivingPage.receivingStep.updateExpiryDatePopup.yesButton.click(); + await receivingPage.receivingStep.updateExpiryDatePopup.confirmButton.click(); await expect( receivingPage.receivingStep.updateExpiryDatePopup.tableDialog ).toBeHidden(); + await expect(receivingPage.receivingStep.editModal.modal).toBeHidden(); }); await test.step('Autofill all quantities of receving items', async () => { From fcdbc7f3caf7b61db490e77013b1bb7e743c8e2e Mon Sep 17 00:00:00 2001 From: Alan Nadolny Date: Wed, 7 Oct 2026 15:20:01 +0200 Subject: [PATCH 10/19] OBPIH-8025 Unskip receive to hold bin --- src/components/NewAlertPopup.ts | 15 +++++--- src/pages/receiving/steps/ReceivingStep.ts | 6 ++- src/tests/receiving/receiveToHoldBin.test.ts | 40 +++++++++++--------- 3 files changed, 38 insertions(+), 23 deletions(-) diff --git a/src/components/NewAlertPopup.ts b/src/components/NewAlertPopup.ts index ccff43ce..b1159723 100644 --- a/src/components/NewAlertPopup.ts +++ b/src/components/NewAlertPopup.ts @@ -3,20 +3,25 @@ import { Page } from '@playwright/test'; import BasePageModel from '@/pages/BasePageModel'; class NewAlertPopup extends BasePageModel { - constructor(page: Page) { + private confirmLabel: string; + private cancelLabel: string; + + constructor(page: Page, confirmLabel = 'Yes', cancelLabel = 'No') { super(page); + this.confirmLabel = confirmLabel; + this.cancelLabel = cancelLabel; } get tableDialog() { return this.page.getByTestId('modal-with-table'); } - get yesButton() { - return this.tableDialog.getByRole('button', { name: 'Yes' }); + get confirmButton() { + return this.tableDialog.getByRole('button', { name: this.confirmLabel }); } - get noButton() { - return this.tableDialog.getByRole('button', { name: 'No' }); + get cancelButton() { + return this.tableDialog.getByRole('button', { name: this.cancelLabel }); } } diff --git a/src/pages/receiving/steps/ReceivingStep.ts b/src/pages/receiving/steps/ReceivingStep.ts index a9acfe6b..4e0c1074 100644 --- a/src/pages/receiving/steps/ReceivingStep.ts +++ b/src/pages/receiving/steps/ReceivingStep.ts @@ -21,7 +21,7 @@ class ReceivingStep extends BasePageModel { super(page); this.table = new ReceivingTable(page); this.editModal = new EditModal(page); - this.updateExpiryDatePopup = new NewAlertPopup(page); + this.updateExpiryDatePopup = new NewAlertPopup(page, 'Ok', 'Cancel'); this.fileHandler = new FileHandler(page); this.autosaveStatus = new AutosaveStatus(page); } @@ -96,6 +96,10 @@ class ReceivingStep extends BasePageModel { ); } + get resetSortingButton() { + return this.page.getByRole('button', { name: 'Reset sorting' }); + } + get orderSelect() { return this.page.getByTestId('custom-select-ordering'); } diff --git a/src/tests/receiving/receiveToHoldBin.test.ts b/src/tests/receiving/receiveToHoldBin.test.ts index ce96004b..71b1a938 100644 --- a/src/tests/receiving/receiveToHoldBin.test.ts +++ b/src/tests/receiving/receiveToHoldBin.test.ts @@ -7,7 +7,7 @@ import BinLocationUtils from '@/utils/BinLocationUtils'; import { deleteShipment } from '@/utils/shipmentUtils'; import UniqueIdentifier from '@/utils/UniqueIdentifier'; -test.describe.skip('Receive item into hold bin', () => { +test.describe('Receive item into hold bin', () => { test.describe.configure({ timeout: 60000 }); //timeout has been added for this test to make sure that the content on bin location tab will load as it can include a lot of data let STOCK_MOVEMENT: StockMovementResponse; @@ -99,15 +99,16 @@ test.describe.skip('Receive item into hold bin', () => { }); await test.step('Edit bin when receive item', async () => { - await receivingPage.receivingStep.isLoaded(); - await receivingPage.receivingStep.table.row(1).binLocationSelect.click(); + await receivingPage.receivingStep.enableShowPutaway(); await receivingPage.receivingStep.table .row(1) - .getBinLocation(holdBinLocationName) - .click(); + .selectBinLocation(holdBinLocationName); + await expect( + receivingPage.receivingStep.table.row(1).selectedBinLocation + ).toHaveText(holdBinLocationName); await receivingPage.receivingStep.table .row(1) - .receivingNowField.textbox.fill('10'); + .receivingNowField.numberbox.fill('10'); }); await test.step('Go to check page', async () => { @@ -115,8 +116,10 @@ test.describe.skip('Receive item into hold bin', () => { await receivingPage.checkStep.isLoaded(); }); - await test.step('Finish receipt of item', async () => { - await receivingPage.checkStep.isLoaded(); + await test.step('Assert hold bin on check page and finish receipt of item', async () => { + await expect( + receivingPage.checkStep.table.getCellValue(1, 'Location') + ).toHaveText(holdBinLocationName); await receivingPage.checkStep.receiveShipmentButton.click(); await stockMovementShowPage.isLoaded(); }); @@ -132,15 +135,18 @@ test.describe.skip('Receive item into hold bin', () => { await stockMovementShowPage.packingListTable.row(1).product.click(); await productShowPage.inStockTab.click(); await productShowPage.inStockTabSection.isLoaded(); - await expect( - productShowPage.inStockTabSection.row(2).binLocation - ).toHaveText(holdBinLocationName); - await expect( - productShowPage.inStockTabSection.row(2).row - ).toHaveAttribute('title', 'This bin has been restricted'); - await expect( - productShowPage.inStockTabSection.row(2).inventoryInformation - ).toHaveText('Hold'); + // the product has stock in other bins too, so the row order isn't fixed + const rowIndex = + await productShowPage.inStockTabSection.getRowIndexByBinLocation( + holdBinLocationName + ); + const row = productShowPage.inStockTabSection.row(rowIndex); + await expect(row.binLocation).toHaveText(holdBinLocationName); + await expect(row.row).toHaveAttribute( + 'title', + 'This bin has been restricted' + ); + await expect(row.inventoryInformation).toHaveText('Hold'); }); }); }); From 11335c80dbfde725ea901c537611e31df85b3b69 Mon Sep 17 00:00:00 2001 From: Alan Nadolny Date: Wed, 7 Oct 2026 15:20:19 +0200 Subject: [PATCH 11/19] OBPIH-8025 Unskip sort by alphabetical order and remain inputs --- ...ByAlphabeticalOrderAndRemainInputs.test.ts | 70 +++++++++++-------- 1 file changed, 39 insertions(+), 31 deletions(-) diff --git a/src/tests/receiving/sortByAlphabeticalOrderAndRemainInputs.test.ts b/src/tests/receiving/sortByAlphabeticalOrderAndRemainInputs.test.ts index 393470e7..412e116a 100644 --- a/src/tests/receiving/sortByAlphabeticalOrderAndRemainInputs.test.ts +++ b/src/tests/receiving/sortByAlphabeticalOrderAndRemainInputs.test.ts @@ -6,7 +6,7 @@ import BinLocationUtils from '@/utils/BinLocationUtils'; import { getDateByOffset, getToday } from '@/utils/DateUtils'; import { deleteShipment } from '@/utils/shipmentUtils'; -test.describe.skip('Apply sorting by alphabetical order and remain inputs', () => { +test.describe('Apply sorting by alphabetical order and remain inputs', () => { let STOCK_MOVEMENT: StockMovementResponse; const TODAY = getToday(); const EXPECTED_DELIVERY_DATE = getDateByOffset(TODAY, 1); @@ -115,56 +115,64 @@ test.describe.skip('Apply sorting by alphabetical order and remain inputs', () = await test.step('Input receiving qty for all lines', async () => { await receivingPage.receivingStep.table .row(1) - .receivingNowField.textbox.fill('50'); + .receivingNowField.numberbox.fill('50'); await receivingPage.receivingStep.table .row(2) - .receivingNowField.textbox.fill('50'); + .receivingNowField.numberbox.fill('150'); await receivingPage.receivingStep.table .row(3) - .receivingNowField.textbox.fill('50'); + .receivingNowField.numberbox.fill('100'); + await receivingPage.receivingStep.autosaveStatus.isSaved(); }); - await test.step('Change ordering to alphabetical and assert order', async () => { - const item = await productService.getProduct(Product.FIVE); - await receivingPage.receivingStep.table.row(3).getItem(item.name).hover(); - await expect(receivingPage.tooltip).toContainText(item.name); - await expect(receivingPage.receivingStep.orderSelect).toBeVisible(); + // sorting by product name ascending reverses the shipment order: + // E2E-product-five, E2E-product-four, E2E-product-three + await test.step('Sort by product name and assert order and inputs', async () => { + const PRODUCT_THREE = await productService.getProduct(Product.THREE); + const PRODUCT_FIVE = await productService.getProduct(Product.FIVE); await expect( - receivingPage.receivingStep.orderSelect.locator( - '.react-select__clear-indicator' - ) - ).toBeHidden(); - await receivingPage.receivingStep.orderSelect.click(); - await receivingPage.receivingStep.getOrder('Alphabetical Order').click(); - await receivingPage.receivingStep.table.row(1).getItem(item.name).hover(); - await expect(receivingPage.tooltip).toContainText(item.name); + receivingPage.receivingStep.table.row(3).getItem(PRODUCT_FIVE.name) + ).toBeVisible(); + await receivingPage.receivingStep.table + .getColumnHeader('Product') + .click(); await expect( - receivingPage.receivingStep.table.row(1).receivingNowField.textbox - ).toHaveValue('50'); + receivingPage.receivingStep.table.row(1).getItem(PRODUCT_FIVE.name) + ).toBeVisible(); await expect( - receivingPage.receivingStep.table.row(2).receivingNowField.textbox - ).toHaveValue('50'); + receivingPage.receivingStep.table.row(3).getItem(PRODUCT_THREE.name) + ).toBeVisible(); + await expect( + receivingPage.receivingStep.table.row(1).receivingNowField.numberbox + ).toHaveValue('100'); await expect( - receivingPage.receivingStep.table.row(3).receivingNowField.textbox + receivingPage.receivingStep.table.row(2).receivingNowField.numberbox + ).toHaveValue('150'); + await expect( + receivingPage.receivingStep.table.row(3).receivingNowField.numberbox ).toHaveValue('50'); }); await test.step('Go to check page and assert applied order', async () => { - const item = await productService.getProduct(Product.FIVE); + const PRODUCT_FIVE = await productService.getProduct(Product.FIVE); await receivingPage.nextButton.click(); await receivingPage.checkStep.isLoaded(); - await receivingPage.checkStep.table.row(1).getItem(item.name).hover(); - await expect(receivingPage.tooltip).toContainText(item.name); + await expect( + receivingPage.checkStep.table.row(1).getItem(PRODUCT_FIVE.name) + ).toBeVisible(); }); - await test.step('Go back to receive page and change order to shipment', async () => { - const item = await productService.getProduct(Product.FIVE); + await test.step('Go back to receive page and reset sorting to shipment order', async () => { + const PRODUCT_FIVE = await productService.getProduct(Product.FIVE); await receivingPage.checkStep.backToEditButton.click(); await receivingPage.receivingStep.isLoaded(); - await receivingPage.receivingStep.orderSelect.click(); - await receivingPage.receivingStep.getOrder('Shipment Order').click(); - await receivingPage.receivingStep.table.row(3).getItem(item.name).hover(); - await expect(receivingPage.tooltip).toContainText(item.name); + await receivingPage.receivingStep.resetSortingButton.click(); + await expect( + receivingPage.receivingStep.table.row(3).getItem(PRODUCT_FIVE.name) + ).toBeVisible(); + await expect( + receivingPage.receivingStep.table.row(3).receivingNowField.numberbox + ).toHaveValue('100'); }); await test.step('Receive shipment', async () => { From c6ce953bc492a0c99e7238ec1418014cb1c20156 Mon Sep 17 00:00:00 2001 From: Alan Nadolny Date: Wed, 7 Oct 2026 15:20:34 +0200 Subject: [PATCH 12/19] OBPIH-8025 Unskip validations on edit and receive --- .../receiving/validationsOnEditAndReceive.test.ts | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/tests/receiving/validationsOnEditAndReceive.test.ts b/src/tests/receiving/validationsOnEditAndReceive.test.ts index e93dfec5..a761d6cd 100644 --- a/src/tests/receiving/validationsOnEditAndReceive.test.ts +++ b/src/tests/receiving/validationsOnEditAndReceive.test.ts @@ -7,7 +7,7 @@ import BinLocationUtils from '@/utils/BinLocationUtils'; import { getToday } from '@/utils/DateUtils'; import { deleteShipment } from '@/utils/shipmentUtils'; -test.describe.skip('Assert validation on try to receive not yet shipped inbound', () => { +test.describe('Assert validation on try to receive not yet shipped inbound', () => { let STOCK_MOVEMENT: StockMovementResponse; const description = 'some description'; const dateRequested = getToday(); @@ -65,7 +65,7 @@ test.describe.skip('Assert validation on try to receive not yet shipped inbound' }); }); -test.describe.skip('Validations on edit and receive inbound stock movement', () => { +test.describe('Validations on edit and receive inbound stock movement', () => { let STOCK_MOVEMENT: StockMovementResponse; const description = 'some description'; const dateRequested = getToday(); @@ -141,7 +141,7 @@ test.describe.skip('Validations on edit and receive inbound stock movement', () await receivingPage.receivingStep.isLoaded(); await receivingPage.receivingStep.table .row(1) - .receivingNowField.textbox.fill('5'); + .receivingNowField.numberbox.fill('5'); }); await test.step('Go to Check page', async () => { @@ -175,7 +175,7 @@ test.describe.skip('Validations on edit and receive inbound stock movement', () await receivingPage.receivingStep.isLoaded(); await receivingPage.receivingStep.table .row(1) - .receivingNowField.textbox.fill('5'); + .receivingNowField.numberbox.fill('5'); }); await test.step('Go to Check page', async () => { @@ -203,6 +203,9 @@ test.describe.skip('Validations on edit and receive inbound stock movement', () await test.step('Rollback shipment received in 2 receipts', async () => { await stockMovementShowPage.isLoaded(); await stockMovementShowPage.rollbackLastReceiptButton.click(); + // wait for the rollback to finish, so it doesn't race with the cleanup + await stockMovementShowPage.isLoaded(); + await expect(stockMovementShowPage.statusTag).toHaveText('Receiving'); }); }); @@ -224,7 +227,7 @@ test.describe.skip('Validations on edit and receive inbound stock movement', () await receivingPage.receivingStep.isLoaded(); await receivingPage.receivingStep.table .row(1) - .receivingNowField.textbox.fill('10'); + .receivingNowField.numberbox.fill('10'); }); await test.step('Go to Check page', async () => { From 00389098b8dd47bbf0d9d1fc3ce70dc606b2368d Mon Sep 17 00:00:00 2001 From: Alan Nadolny Date: Wed, 7 Oct 2026 16:25:21 +0200 Subject: [PATCH 13/19] OBPIH-8025 Fix failing tests --- src/components/FormField.ts | 3 +- src/pages/receiving/components/CheckTable.ts | 17 ++++ .../assertCreationOfGoodsReceiptNote.test.ts | 5 +- .../receiving/cancelRemainingQty.test.ts | 79 ++++++++++++------- .../validationsOnEditAndReceive.test.ts | 4 +- 5 files changed, 77 insertions(+), 31 deletions(-) diff --git a/src/components/FormField.ts b/src/components/FormField.ts index 54f88965..7791285f 100644 --- a/src/components/FormField.ts +++ b/src/components/FormField.ts @@ -40,7 +40,8 @@ class FormField extends BasePageModel { } async assertHasError() { - expect(await this.hasError()).toBeTruthy(); + // the error can show up a moment after the value is entered, so retry + await expect.poll(() => this.hasError()).toBeTruthy(); } async assertFieldWithErrorIsVisible(error: string) { diff --git a/src/pages/receiving/components/CheckTable.ts b/src/pages/receiving/components/CheckTable.ts index 66198efe..7519d242 100644 --- a/src/pages/receiving/components/CheckTable.ts +++ b/src/pages/receiving/components/CheckTable.ts @@ -35,6 +35,19 @@ class CheckTable extends BasePageModel { return new Row(this.page, this.itemRows.nth(index)); } + // the order of the items isn't guaranteed to match the receiving step, so + // find the row by its product code instead of its position + rowByProductCode(productCode: string) { + return new Row( + this.page, + this.rows.filter({ + has: this.page + .locator('[aria-label="Code"]') + .getByText(productCode, { exact: true }), + }) + ); + } + getColumnHeader(columnName: string) { return this.table .locator('.rt-thead') @@ -62,6 +75,10 @@ class Row extends BasePageModel { return this.row.locator('[aria-label="Code"]'); } + get status() { + return this.row.locator('[aria-label="Status"]'); + } + get cancelRemainingCheckbox() { return this.row .locator('[data-column-id="cancelRemaining"]') diff --git a/src/tests/receiving/assertCreationOfGoodsReceiptNote.test.ts b/src/tests/receiving/assertCreationOfGoodsReceiptNote.test.ts index 8389b056..a63812da 100644 --- a/src/tests/receiving/assertCreationOfGoodsReceiptNote.test.ts +++ b/src/tests/receiving/assertCreationOfGoodsReceiptNote.test.ts @@ -162,7 +162,10 @@ test.describe('Assert Goods Receipt Note is created and opened', () => { await test.step('Rollback shipment received in 2 receipts', async () => { await stockMovementShowPage.isLoaded(); await stockMovementShowPage.rollbackLastReceiptButton.click(); - await stockMovementShowPage.isLoaded(); + // wait for the rollback to finish, so it doesn't race with the cleanup + await expect( + stockMovementShowPage.rollbackReceiptInformationMessage + ).toContainText('Successfully rolled back last receipt'); await expect(stockMovementShowPage.statusTag).toHaveText('Receiving'); }); }); diff --git a/src/tests/receiving/cancelRemainingQty.test.ts b/src/tests/receiving/cancelRemainingQty.test.ts index 9aa00678..d2bef34d 100644 --- a/src/tests/receiving/cancelRemainingQty.test.ts +++ b/src/tests/receiving/cancelRemainingQty.test.ts @@ -8,6 +8,8 @@ import { deleteShipment } from '@/utils/shipmentUtils'; test.describe('Cancel qty in the middle of receipt', () => { let STOCK_MOVEMENT: StockMovementResponse; + let PRODUCT_ONE_CODE: string; + let PRODUCT_TWO_CODE: string; test.beforeEach( async ({ @@ -18,6 +20,8 @@ test.describe('Cancel qty in the middle of receipt', () => { const supplierLocation = await supplierLocationService.getLocation(); const PRODUCT_ONE = await productService.getProduct(Product.ONE); const PRODUCT_TWO = await productService.getProduct(Product.TWO); + PRODUCT_ONE_CODE = PRODUCT_ONE.productCode; + PRODUCT_TWO_CODE = PRODUCT_TWO.productCode; STOCK_MOVEMENT = await stockMovementService.createInbound({ originId: supplierLocation.id, @@ -85,25 +89,28 @@ test.describe('Cancel qty in the middle of receipt', () => { await receivingPage.nextButton.click(); await receivingPage.checkStep.isLoaded(); await expect( - receivingPage.checkStep.table.getCellValue(1, 'Status') + receivingPage.checkStep.table.rowByProductCode(PRODUCT_ONE_CODE).status ).toHaveText('50 remaining'); await expect( - receivingPage.checkStep.table.getCellValue(2, 'Status') + receivingPage.checkStep.table.rowByProductCode(PRODUCT_TWO_CODE).status ).toHaveText('Complete'); await expect( - receivingPage.checkStep.table.row(1).cancelRemainingCheckbox + receivingPage.checkStep.table.rowByProductCode(PRODUCT_ONE_CODE) + .cancelRemainingCheckbox ).toBeEnabled(); await expect( - receivingPage.checkStep.table.row(2).cancelRemainingCheckbox + receivingPage.checkStep.table.rowByProductCode(PRODUCT_TWO_CODE) + .cancelRemainingCheckbox ).toBeHidden(); }); await test.step('Select cancel remaining qty checkbox and receive shipment', async () => { await receivingPage.checkStep.table - .row(1) + .rowByProductCode(PRODUCT_ONE_CODE) .cancelRemainingCheckbox.check(); await expect( - receivingPage.checkStep.table.row(1).cancelRemainingCheckbox + receivingPage.checkStep.table.rowByProductCode(PRODUCT_ONE_CODE) + .cancelRemainingCheckbox ).toBeChecked(); await receivingPage.checkStep.receiveShipmentButton.click(); await stockMovementShowPage.isLoaded(); @@ -152,10 +159,11 @@ test.describe('Cancel qty in the middle of receipt', () => { await receivingPage.nextButton.click(); await receivingPage.checkStep.isLoaded(); await expect( - receivingPage.checkStep.table.getCellValue(1, 'Status') + receivingPage.checkStep.table.rowByProductCode(PRODUCT_ONE_CODE).status ).toHaveText('50 remaining'); await expect( - receivingPage.checkStep.table.row(1).cancelRemainingCheckbox + receivingPage.checkStep.table.rowByProductCode(PRODUCT_ONE_CODE) + .cancelRemainingCheckbox ).toBeEnabled(); await receivingPage.checkStep.receiveShipmentButton.click(); await stockMovementShowPage.isLoaded(); @@ -188,31 +196,35 @@ test.describe('Cancel qty in the middle of receipt', () => { await receivingPage.nextButton.click(); await receivingPage.checkStep.isLoaded(); await expect( - receivingPage.checkStep.table.getCellValue(1, 'Status') + receivingPage.checkStep.table.rowByProductCode(PRODUCT_ONE_CODE).status ).toHaveText('25 remaining'); await expect( - receivingPage.checkStep.table.getCellValue(2, 'Status') + receivingPage.checkStep.table.rowByProductCode(PRODUCT_TWO_CODE).status ).toHaveText('10 remaining'); await expect( - receivingPage.checkStep.table.row(1).cancelRemainingCheckbox + receivingPage.checkStep.table.rowByProductCode(PRODUCT_ONE_CODE) + .cancelRemainingCheckbox ).toBeEnabled(); await expect( - receivingPage.checkStep.table.row(2).cancelRemainingCheckbox + receivingPage.checkStep.table.rowByProductCode(PRODUCT_TWO_CODE) + .cancelRemainingCheckbox ).toBeEnabled(); }); await test.step('Select cancel remaining qty checkbox and receive shipment', async () => { await receivingPage.checkStep.table - .row(1) + .rowByProductCode(PRODUCT_ONE_CODE) .cancelRemainingCheckbox.check(); await receivingPage.checkStep.table - .row(2) + .rowByProductCode(PRODUCT_TWO_CODE) .cancelRemainingCheckbox.check(); await expect( - receivingPage.checkStep.table.row(1).cancelRemainingCheckbox + receivingPage.checkStep.table.rowByProductCode(PRODUCT_ONE_CODE) + .cancelRemainingCheckbox ).toBeChecked(); await expect( - receivingPage.checkStep.table.row(2).cancelRemainingCheckbox + receivingPage.checkStep.table.rowByProductCode(PRODUCT_TWO_CODE) + .cancelRemainingCheckbox ).toBeChecked(); await receivingPage.checkStep.receiveShipmentButton.click(); await stockMovementShowPage.isLoaded(); @@ -241,7 +253,9 @@ test.describe('Cancel qty in the middle of receipt', () => { await stockMovementShowPage.isLoaded(); await stockMovementShowPage.rollbackLastReceiptButton.click(); // wait for the rollback to finish, so it doesn't race with the cleanup - await stockMovementShowPage.isLoaded(); + await expect( + stockMovementShowPage.rollbackReceiptInformationMessage + ).toContainText('Successfully rolled back last receipt'); await expect(stockMovementShowPage.statusTag).toHaveText('Receiving'); }); }); @@ -274,16 +288,18 @@ test.describe('Cancel qty in the middle of receipt', () => { await receivingPage.nextButton.click(); await receivingPage.checkStep.isLoaded(); await expect( - receivingPage.checkStep.table.getCellValue(1, 'Status') + receivingPage.checkStep.table.rowByProductCode(PRODUCT_ONE_CODE).status ).toHaveText('100 over'); await expect( - receivingPage.checkStep.table.getCellValue(2, 'Status') + receivingPage.checkStep.table.rowByProductCode(PRODUCT_TWO_CODE).status ).toHaveText('90 over'); await expect( - receivingPage.checkStep.table.row(1).cancelRemainingCheckbox + receivingPage.checkStep.table.rowByProductCode(PRODUCT_ONE_CODE) + .cancelRemainingCheckbox ).toBeHidden(); await expect( - receivingPage.checkStep.table.row(2).cancelRemainingCheckbox + receivingPage.checkStep.table.rowByProductCode(PRODUCT_TWO_CODE) + .cancelRemainingCheckbox ).toBeHidden(); }); @@ -338,26 +354,30 @@ test.describe('Cancel qty in the middle of receipt', () => { await receivingPage.nextButton.click(); await receivingPage.checkStep.isLoaded(); await expect( - receivingPage.checkStep.table.getCellValue(1, 'Status') + receivingPage.checkStep.table.rowByProductCode(PRODUCT_ONE_CODE).status ).toHaveText('50 remaining'); await expect( - receivingPage.checkStep.table.getCellValue(2, 'Status') + receivingPage.checkStep.table.rowByProductCode(PRODUCT_TWO_CODE).status ).toHaveText('5 remaining'); await expect( - receivingPage.checkStep.table.row(1).cancelRemainingCheckbox + receivingPage.checkStep.table.rowByProductCode(PRODUCT_ONE_CODE) + .cancelRemainingCheckbox ).toBeEnabled(); await expect( - receivingPage.checkStep.table.row(2).cancelRemainingCheckbox + receivingPage.checkStep.table.rowByProductCode(PRODUCT_TWO_CODE) + .cancelRemainingCheckbox ).toBeEnabled(); }); await test.step('Select cancel all remaining button', async () => { await receivingPage.checkStep.cancelAllRemainingButton.click(); await expect( - receivingPage.checkStep.table.row(1).cancelRemainingCheckbox + receivingPage.checkStep.table.rowByProductCode(PRODUCT_ONE_CODE) + .cancelRemainingCheckbox ).toBeChecked(); await expect( - receivingPage.checkStep.table.row(2).cancelRemainingCheckbox + receivingPage.checkStep.table.rowByProductCode(PRODUCT_TWO_CODE) + .cancelRemainingCheckbox ).toBeChecked(); await receivingPage.checkStep.receiveShipmentButton.click(); await stockMovementShowPage.isLoaded(); @@ -494,7 +514,10 @@ test.describe('Cancel qty in the middle of receipt', () => { await test.step('Rollback shipment received in 2 receipts', async () => { await stockMovementShowPage.isLoaded(); await stockMovementShowPage.rollbackLastReceiptButton.click(); - await stockMovementShowPage.isLoaded(); + // wait for the rollback to finish, so it doesn't race with the cleanup + await expect( + stockMovementShowPage.rollbackReceiptInformationMessage + ).toContainText('Successfully rolled back last receipt'); }); }); }); diff --git a/src/tests/receiving/validationsOnEditAndReceive.test.ts b/src/tests/receiving/validationsOnEditAndReceive.test.ts index a761d6cd..56119dea 100644 --- a/src/tests/receiving/validationsOnEditAndReceive.test.ts +++ b/src/tests/receiving/validationsOnEditAndReceive.test.ts @@ -204,7 +204,9 @@ test.describe('Validations on edit and receive inbound stock movement', () => { await stockMovementShowPage.isLoaded(); await stockMovementShowPage.rollbackLastReceiptButton.click(); // wait for the rollback to finish, so it doesn't race with the cleanup - await stockMovementShowPage.isLoaded(); + await expect( + stockMovementShowPage.rollbackReceiptInformationMessage + ).toContainText('Successfully rolled back last receipt'); await expect(stockMovementShowPage.statusTag).toHaveText('Receiving'); }); }); From 7c6e8998404397e7137ec370f1a04812bd4cfa8b Mon Sep 17 00:00:00 2001 From: Alan Nadolny Date: Thu, 8 Oct 2026 15:46:36 +0200 Subject: [PATCH 14/19] OBPIH-8025 Fix failing tests --- src/tests/receiving/cancelRemainingQty.test.ts | 13 +++++++------ src/tests/receiving/editOriginalLineQtyTo0.test.ts | 9 ++++++--- src/tests/receiving/receiveInbound.test.ts | 3 ++- 3 files changed, 15 insertions(+), 10 deletions(-) diff --git a/src/tests/receiving/cancelRemainingQty.test.ts b/src/tests/receiving/cancelRemainingQty.test.ts index d2bef34d..6ae1e2e9 100644 --- a/src/tests/receiving/cancelRemainingQty.test.ts +++ b/src/tests/receiving/cancelRemainingQty.test.ts @@ -233,19 +233,20 @@ test.describe('Cancel qty in the middle of receipt', () => { await test.step('Assert canceled qty on stock movement show page', async () => { await expect(stockMovementShowPage.statusTag).toHaveText('Received'); await stockMovementShowPage.openReceiptsTab(); - // the 1st receipt holds a line for every item (rows 1-2), so the - // 2nd receipt lines are rows 3-4 + // lines with nothing received or canceled are not listed, so the 1st + // receipt shows only the 1st item (row 1) and the 2nd receipt lines are + // rows 2-3 await expect( - stockMovementShowPage.receiptListTable.row(3).quantityCanceled + stockMovementShowPage.receiptListTable.row(2).quantityCanceled ).toHaveText('25'); await expect( - stockMovementShowPage.receiptListTable.row(3).quantityReceived + stockMovementShowPage.receiptListTable.row(2).quantityReceived ).toHaveText('25'); await expect( - stockMovementShowPage.receiptListTable.row(4).quantityCanceled + stockMovementShowPage.receiptListTable.row(3).quantityCanceled ).toHaveText('10'); await expect( - stockMovementShowPage.receiptListTable.row(4).quantityReceived + stockMovementShowPage.receiptListTable.row(3).quantityReceived ).toHaveText('0'); }); diff --git a/src/tests/receiving/editOriginalLineQtyTo0.test.ts b/src/tests/receiving/editOriginalLineQtyTo0.test.ts index 1ae77949..27fa288f 100644 --- a/src/tests/receiving/editOriginalLineQtyTo0.test.ts +++ b/src/tests/receiving/editOriginalLineQtyTo0.test.ts @@ -170,10 +170,13 @@ test.describe('Edit qty of original line to 0', () => { await test.step('Assert nothing was received with lot on Receipt tab', async () => { await stockMovementShowPage.openReceiptsTab(); + // lines with nothing received or canceled are not listed await expect( - stockMovementShowPage.receiptListTable.getRowByText(lot) - .quantityReceived - ).toHaveText('0'); + stockMovementShowPage.receiptListTable.row(1).row + ).toBeVisible(); + await expect( + stockMovementShowPage.receiptListTable.getRowByText(lot).row + ).toBeHidden(); }); await test.step('Assert received lines on Packing list', async () => { diff --git a/src/tests/receiving/receiveInbound.test.ts b/src/tests/receiving/receiveInbound.test.ts index e1a9ac04..bb819af0 100644 --- a/src/tests/receiving/receiveInbound.test.ts +++ b/src/tests/receiving/receiveInbound.test.ts @@ -188,7 +188,8 @@ test.describe('Receive inbound stock movement', () => { await expect(receivingPage.checkStep.destinationField).toHaveText( destinationName ); - await expect(receivingPage.checkStep.shippedOnField).toHaveText( + // shown with the time of shipping, which the test doesn't control + await expect(receivingPage.checkStep.shippedOnField).toContainText( formatDate(TODAY, DateFormat.DISPLAY) ); }); From 2f366ec78dccd3ffc2cebbff8a809893dfbc8e06 Mon Sep 17 00:00:00 2001 From: Alan Nadolny Date: Thu, 8 Oct 2026 17:43:44 +0200 Subject: [PATCH 15/19] OBPIH-8025 Fix for tooltip wrapper --- src/pages/receiving/steps/CheckStep.ts | 4 ++++ src/pages/receiving/steps/ReceivingStep.ts | 6 ++++++ ...rollbackLastReceiptWhenPutawayCreated.test.ts | 4 +++- src/tests/receiving/assertQtyInputs.test.ts | 16 ++++++++++++---- src/tests/receiving/assertRecipientField.test.ts | 2 +- src/tests/receiving/cancelRemainingQty.test.ts | 8 +++++--- .../receiving/editBinLocationWhenReceive.test.ts | 4 +++- .../receiving/editOriginalLineQtyTo0.test.ts | 2 +- .../lotExpirySystemUpdateOnReceiving.test.ts | 16 ++++++++++++---- src/tests/receiving/receiveInbound.test.ts | 8 ++++++-- ...eiveInboundWithoutPickAndPutawayStock.test.ts | 4 +++- ...ortByAlphabeticalOrderAndRemainInputs.test.ts | 6 ++++-- .../receiving/validationsOnDeliverOnDate.test.ts | 8 ++++++-- 13 files changed, 66 insertions(+), 22 deletions(-) diff --git a/src/pages/receiving/steps/CheckStep.ts b/src/pages/receiving/steps/CheckStep.ts index efcb000a..c6e4de7b 100644 --- a/src/pages/receiving/steps/CheckStep.ts +++ b/src/pages/receiving/steps/CheckStep.ts @@ -52,6 +52,8 @@ class CheckStep extends BasePageModel { .locator('.item-details__value'); } + // the button is wrapped in a tooltip, which can intercept the click once the + // tooltip is shown, so the tests click it with { force: true } get cancelAllRemainingButton() { return this.page.getByRole('button', { name: 'Cancel All Remaining' }); } @@ -62,6 +64,8 @@ class CheckStep extends BasePageModel { .getByText('Must occur on or after Actual Shipping Date'); } + // the button is wrapped in a tooltip, which can intercept the click once the + // tooltip is shown, so the tests click it with { force: true } get backToEditButton() { return this.page .locator('.submit-buttons') diff --git a/src/pages/receiving/steps/ReceivingStep.ts b/src/pages/receiving/steps/ReceivingStep.ts index 4e0c1074..b0d3a59b 100644 --- a/src/pages/receiving/steps/ReceivingStep.ts +++ b/src/pages/receiving/steps/ReceivingStep.ts @@ -37,6 +37,8 @@ class ReceivingStep extends BasePageModel { await this.page.waitForResponse(PARTIAL_RECEIVING_API_PATTERN); } + // the button is wrapped in a tooltip, which can intercept the click once the + // tooltip is shown, so the tests click it with { force: true } get autofillQuantitiesButton() { return this.page.getByRole('button', { name: 'Autofill quantities' }); } @@ -62,6 +64,8 @@ class ReceivingStep extends BasePageModel { await expect(this.showPutawaySwitch.getByRole('checkbox')).toBeChecked(); } + // the button is wrapped in a tooltip, which can intercept the click once the + // tooltip is shown, so the tests click it with { force: true } get saveAndExitButton() { return this.page.getByRole('button', { name: 'Save & Exit' }); } @@ -96,6 +100,8 @@ class ReceivingStep extends BasePageModel { ); } + // the button is wrapped in a tooltip, which can intercept the click once the + // tooltip is shown, so the tests click it with { force: true } get resetSortingButton() { return this.page.getByRole('button', { name: 'Reset sorting' }); } diff --git a/src/tests/putaway/rollbackLastReceiptWhenPutawayCreated.test.ts b/src/tests/putaway/rollbackLastReceiptWhenPutawayCreated.test.ts index d456c9c9..8333861d 100644 --- a/src/tests/putaway/rollbackLastReceiptWhenPutawayCreated.test.ts +++ b/src/tests/putaway/rollbackLastReceiptWhenPutawayCreated.test.ts @@ -161,7 +161,9 @@ test.describe('Rollback last receipt behavior when putaway created', () => { await test.step('Receive sm', async () => { await stockMovementShowPage.receiveButton.click(); await receivingPage.receivingStep.isLoaded(); - await receivingPage.receivingStep.autofillQuantitiesButton.click(); + await receivingPage.receivingStep.autofillQuantitiesButton.click({ + force: true, + }); await receivingPage.nextButton.click(); await receivingPage.checkStep.isLoaded(); await receivingPage.checkStep.receiveShipmentButton.click(); diff --git a/src/tests/receiving/assertQtyInputs.test.ts b/src/tests/receiving/assertQtyInputs.test.ts index 2732130f..0e166782 100644 --- a/src/tests/receiving/assertQtyInputs.test.ts +++ b/src/tests/receiving/assertQtyInputs.test.ts @@ -83,7 +83,9 @@ test.describe('Assert if quantity inputs remain when split lines', () => { }); await test.step('Autofill receiving quantity', async () => { - await receivingPage.receivingStep.autofillQuantitiesButton.click(); + await receivingPage.receivingStep.autofillQuantitiesButton.click({ + force: true, + }); await expect( receivingPage.receivingStep.table.row(1).receivingNowField.numberbox ).toHaveValue('50'); @@ -141,7 +143,9 @@ test.describe('Assert if quantity inputs remain when split lines', () => { }); await test.step('Autofill quantity after split line does not override inputs', async () => { - await receivingPage.receivingStep.autofillQuantitiesButton.click(); + await receivingPage.receivingStep.autofillQuantitiesButton.click({ + force: true, + }); await expect( receivingPage.receivingStep.table.row(1).receivingNowField.numberbox ).toHaveValue('50'); @@ -229,7 +233,9 @@ test.describe('Assert if quantity inputs remain when split lines', () => { await receivingPage.receivingStep.table .row(3) .receivingNowField.numberbox.fill('200'); - await receivingPage.receivingStep.saveAndExitButton.click(); + await receivingPage.receivingStep.saveAndExitButton.click({ + force: true, + }); await stockMovementShowPage.isLoaded(); }); @@ -297,7 +303,9 @@ test.describe('Assert if quantity inputs remain when split lines', () => { }); await test.step('Autofill quantity after split line does not override inputs', async () => { - await receivingPage.receivingStep.autofillQuantitiesButton.click(); + await receivingPage.receivingStep.autofillQuantitiesButton.click({ + force: true, + }); await expect( receivingPage.receivingStep.table.row(3).receivingNowField.numberbox ).toHaveValue('25'); diff --git a/src/tests/receiving/assertRecipientField.test.ts b/src/tests/receiving/assertRecipientField.test.ts index 322275c5..e937d0dc 100644 --- a/src/tests/receiving/assertRecipientField.test.ts +++ b/src/tests/receiving/assertRecipientField.test.ts @@ -121,7 +121,7 @@ test.describe.skip('Assert recipient field when receive', () => { await test.step('Go backward and assert recipient field', async () => { const USER = await mainUserService.getUser(); - await receivingPage.checkStep.backToEditButton.click(); + await receivingPage.checkStep.backToEditButton.click({ force: true }); await receivingPage.receivingStep.isLoaded(); await expect( receivingPage.receivingStep.table.getCellValue(1, 'Recipient') diff --git a/src/tests/receiving/cancelRemainingQty.test.ts b/src/tests/receiving/cancelRemainingQty.test.ts index 6ae1e2e9..dbcd2f5b 100644 --- a/src/tests/receiving/cancelRemainingQty.test.ts +++ b/src/tests/receiving/cancelRemainingQty.test.ts @@ -371,7 +371,9 @@ test.describe('Cancel qty in the middle of receipt', () => { }); await test.step('Select cancel all remaining button', async () => { - await receivingPage.checkStep.cancelAllRemainingButton.click(); + await receivingPage.checkStep.cancelAllRemainingButton.click({ + force: true, + }); await expect( receivingPage.checkStep.table.rowByProductCode(PRODUCT_ONE_CODE) .cancelRemainingCheckbox @@ -432,7 +434,7 @@ test.describe('Cancel qty in the middle of receipt', () => { }); await test.step('Go to backward to receiving page and forward again', async () => { - await receivingPage.checkStep.backToEditButton.click(); + await receivingPage.checkStep.backToEditButton.click({ force: true }); await receivingPage.nextButton.click(); await receivingPage.checkStep.isLoaded(); await expect( @@ -487,7 +489,7 @@ test.describe('Cancel qty in the middle of receipt', () => { }); await test.step('Go to backward to receiving page and forward again', async () => { - await receivingPage.checkStep.backToEditButton.click(); + await receivingPage.checkStep.backToEditButton.click({ force: true }); await receivingPage.nextButton.click(); await receivingPage.checkStep.isLoaded(); await expect( diff --git a/src/tests/receiving/editBinLocationWhenReceive.test.ts b/src/tests/receiving/editBinLocationWhenReceive.test.ts index 7f563be8..8dcd2f04 100644 --- a/src/tests/receiving/editBinLocationWhenReceive.test.ts +++ b/src/tests/receiving/editBinLocationWhenReceive.test.ts @@ -533,7 +533,9 @@ test.describe('Edit Bin Location when receive for all lines', () => { }); await test.step('Autofill qty and go to check page', async () => { - await receivingPage.receivingStep.autofillQuantitiesButton.click(); + await receivingPage.receivingStep.autofillQuantitiesButton.click({ + force: true, + }); await receivingPage.nextButton.click(); await receivingPage.checkStep.isLoaded(); }); diff --git a/src/tests/receiving/editOriginalLineQtyTo0.test.ts b/src/tests/receiving/editOriginalLineQtyTo0.test.ts index 27fa288f..9a7e524a 100644 --- a/src/tests/receiving/editOriginalLineQtyTo0.test.ts +++ b/src/tests/receiving/editOriginalLineQtyTo0.test.ts @@ -147,7 +147,7 @@ test.describe('Edit qty of original line to 0', () => { }); await test.step('Return to receiving step and assert original line is not visible', async () => { - await receivingPage.checkStep.backToEditButton.click(); + await receivingPage.checkStep.backToEditButton.click({ force: true }); await receivingPage.receivingStep.isLoaded(); await expect(receivingPage.receivingStep.table.rows).toHaveCount(8); await expect( diff --git a/src/tests/receiving/lotExpirySystemUpdateOnReceiving.test.ts b/src/tests/receiving/lotExpirySystemUpdateOnReceiving.test.ts index cb063dcc..19170b30 100644 --- a/src/tests/receiving/lotExpirySystemUpdateOnReceiving.test.ts +++ b/src/tests/receiving/lotExpirySystemUpdateOnReceiving.test.ts @@ -143,7 +143,9 @@ test.describe('Lot number system expiry date modification on receiving workflow' }); await test.step('Autofill all quantities of receving items', async () => { - await receivingPage.receivingStep.autofillQuantitiesButton.click(); + await receivingPage.receivingStep.autofillQuantitiesButton.click({ + force: true, + }); }); await test.step('Go to next step', async () => { @@ -252,7 +254,9 @@ test.describe('Lot number system expiry date modification on receiving workflow' }); await test.step('Autofill all quantities of receving items', async () => { - await receivingPage.receivingStep.autofillQuantitiesButton.click(); + await receivingPage.receivingStep.autofillQuantitiesButton.click({ + force: true, + }); }); await test.step('Go to next step', async () => { @@ -368,7 +372,9 @@ test.describe('Lot number system expiry date modification on receiving workflow' }); await test.step('Autofill all quantities of receving items', async () => { - await receivingPage.receivingStep.autofillQuantitiesButton.click(); + await receivingPage.receivingStep.autofillQuantitiesButton.click({ + force: true, + }); }); await test.step('Go to next step', async () => { @@ -490,7 +496,9 @@ test.describe('Lot number system expiry date modification on receiving workflow' }); await test.step('Autofill all quantities of receving items', async () => { - await receivingPage.receivingStep.autofillQuantitiesButton.click(); + await receivingPage.receivingStep.autofillQuantitiesButton.click({ + force: true, + }); }); await test.step('Go to next step', async () => { diff --git a/src/tests/receiving/receiveInbound.test.ts b/src/tests/receiving/receiveInbound.test.ts index bb819af0..27f726ba 100644 --- a/src/tests/receiving/receiveInbound.test.ts +++ b/src/tests/receiving/receiveInbound.test.ts @@ -118,7 +118,9 @@ test.describe('Receive inbound stock movement', () => { await test.step('Select all items to receive', async () => { await receivingPage.receivingStep.isLoaded(); - await receivingPage.receivingStep.autofillQuantitiesButton.click(); + await receivingPage.receivingStep.autofillQuantitiesButton.click({ + force: true, + }); }); await test.step('Go to Check page', async () => { @@ -300,7 +302,9 @@ test.describe('Receive inbound stock movement', () => { }); await test.step('Click on Save and Exit button', async () => { - await receivingPage.receivingStep.saveAndExitButton.click(); + await receivingPage.receivingStep.saveAndExitButton.click({ + force: true, + }); await stockMovementShowPage.isLoaded(); }); diff --git a/src/tests/receiving/receiveInboundWithoutPickAndPutawayStock.test.ts b/src/tests/receiving/receiveInboundWithoutPickAndPutawayStock.test.ts index 2d1a79da..cd6ae722 100644 --- a/src/tests/receiving/receiveInboundWithoutPickAndPutawayStock.test.ts +++ b/src/tests/receiving/receiveInboundWithoutPickAndPutawayStock.test.ts @@ -91,7 +91,9 @@ test.describe('Receive inbound stock movement in location without pick and putaw await test.step('Autofill receiving qty', async () => { await receivingPage.receivingStep.isLoaded(); - await receivingPage.receivingStep.autofillQuantitiesButton.click(); + await receivingPage.receivingStep.autofillQuantitiesButton.click({ + force: true, + }); }); await test.step('Go to and assert checking page is visible', async () => { diff --git a/src/tests/receiving/sortByAlphabeticalOrderAndRemainInputs.test.ts b/src/tests/receiving/sortByAlphabeticalOrderAndRemainInputs.test.ts index 412e116a..f3bd3093 100644 --- a/src/tests/receiving/sortByAlphabeticalOrderAndRemainInputs.test.ts +++ b/src/tests/receiving/sortByAlphabeticalOrderAndRemainInputs.test.ts @@ -164,9 +164,11 @@ test.describe('Apply sorting by alphabetical order and remain inputs', () => { await test.step('Go back to receive page and reset sorting to shipment order', async () => { const PRODUCT_FIVE = await productService.getProduct(Product.FIVE); - await receivingPage.checkStep.backToEditButton.click(); + await receivingPage.checkStep.backToEditButton.click({ force: true }); await receivingPage.receivingStep.isLoaded(); - await receivingPage.receivingStep.resetSortingButton.click(); + await receivingPage.receivingStep.resetSortingButton.click({ + force: true, + }); await expect( receivingPage.receivingStep.table.row(3).getItem(PRODUCT_FIVE.name) ).toBeVisible(); diff --git a/src/tests/receiving/validationsOnDeliverOnDate.test.ts b/src/tests/receiving/validationsOnDeliverOnDate.test.ts index 920d4785..20d03167 100644 --- a/src/tests/receiving/validationsOnDeliverOnDate.test.ts +++ b/src/tests/receiving/validationsOnDeliverOnDate.test.ts @@ -66,7 +66,9 @@ test.describe.skip('Validations on edit Deliver On Date when receiving shipment' }); await test.step('Autofill qty and go to check page', async () => { - await receivingPage.receivingStep.autofillQuantitiesButton.click(); + await receivingPage.receivingStep.autofillQuantitiesButton.click({ + force: true, + }); await receivingPage.nextButton.click(); }); @@ -98,7 +100,9 @@ test.describe.skip('Validations on edit Deliver On Date when receiving shipment' }); await test.step('Autofill qty and go to check page', async () => { - await receivingPage.receivingStep.autofillQuantitiesButton.click(); + await receivingPage.receivingStep.autofillQuantitiesButton.click({ + force: true, + }); await receivingPage.nextButton.click(); }); From d31dbcb5765a5a139a6802e789b61cc735896719 Mon Sep 17 00:00:00 2001 From: Alan Nadolny Date: Fri, 9 Oct 2026 13:51:23 +0200 Subject: [PATCH 16/19] OBPIH-8025 Usage new data-testid and remove force clicks --- src/pages/receiving/components/CheckTable.ts | 2 +- src/pages/receiving/components/EditModal.ts | 8 ----- .../receiving/components/EditModalTable.ts | 10 ++++-- .../receiving/components/ReceivingTable.ts | 10 ++++-- src/pages/receiving/steps/CheckStep.ts | 35 +++++++------------ src/pages/receiving/steps/ReceivingStep.ts | 10 ++---- ...lbackLastReceiptWhenPutawayCreated.test.ts | 4 +-- .../receiving/assertBinLocationField.test.ts | 30 ++++++---------- .../assertCreationOfGoodsReceiptNote.test.ts | 4 +-- src/tests/receiving/assertQtyInputs.test.ts | 16 +++------ .../receiving/assertRecipientField.test.ts | 2 +- .../receiving/cancelRemainingQty.test.ts | 8 ++--- .../editBinLocationWhenReceive.test.ts | 4 +-- .../receiving/editOriginalLineQtyTo0.test.ts | 2 +- .../lotExpirySystemUpdateOnReceiving.test.ts | 16 +++------ src/tests/receiving/receiveInbound.test.ts | 8 ++--- ...eInboundWithoutPickAndPutawayStock.test.ts | 4 +-- ...ByAlphabeticalOrderAndRemainInputs.test.ts | 6 ++-- .../validationsOnDeliverOnDate.test.ts | 8 ++--- src/utils/shipmentUtils.ts | 14 +++++--- 20 files changed, 72 insertions(+), 129 deletions(-) diff --git a/src/pages/receiving/components/CheckTable.ts b/src/pages/receiving/components/CheckTable.ts index 7519d242..6d96299f 100644 --- a/src/pages/receiving/components/CheckTable.ts +++ b/src/pages/receiving/components/CheckTable.ts @@ -50,7 +50,7 @@ class CheckTable extends BasePageModel { getColumnHeader(columnName: string) { return this.table - .locator('.rt-thead') + .getByTestId('table-header') .getByText(columnName, { exact: true }); } diff --git a/src/pages/receiving/components/EditModal.ts b/src/pages/receiving/components/EditModal.ts index b006aa41..6613f578 100644 --- a/src/pages/receiving/components/EditModal.ts +++ b/src/pages/receiving/components/EditModal.ts @@ -30,14 +30,6 @@ class EditModal extends BasePageModel { get addLineButton() { return this.modal.getByTestId('add-new-record'); } - - get informationAboutEditedQtyNotMatchingShippedQty() { - return this.modal - .locator('.font-weight-bold font-red-ob') - .getByText( - 'The total edited quantity does not match the original quantity shipped.' - ); - } } export default EditModal; diff --git a/src/pages/receiving/components/EditModalTable.ts b/src/pages/receiving/components/EditModalTable.ts index 7837926e..282363ea 100644 --- a/src/pages/receiving/components/EditModalTable.ts +++ b/src/pages/receiving/components/EditModalTable.ts @@ -43,12 +43,16 @@ class Row extends BasePageModel { return this.row.locator('[aria-label="Location"]'); } + get clearBinLocationButton() { + return this.binLocationSelect.getByTestId('custom-select-clear'); + } + get productSelect() { return this.row.locator('[aria-label="Product"]'); } get clearProductSelect() { - return this.productSelect.locator('.react-select__clear-indicator'); + return this.productSelect.getByTestId('custom-select-clear'); } async getProductSelect(name: string) { @@ -58,8 +62,8 @@ class Row extends BasePageModel { .fill(name); await this.page .getByTestId('custom-select-dropdown-menu') - .locator('.react-select__option') - .nth(0) + .getByText(name) + .first() .click(); } } diff --git a/src/pages/receiving/components/ReceivingTable.ts b/src/pages/receiving/components/ReceivingTable.ts index 9b436cad..781d0404 100644 --- a/src/pages/receiving/components/ReceivingTable.ts +++ b/src/pages/receiving/components/ReceivingTable.ts @@ -25,7 +25,7 @@ class ReceivingTable extends BasePageModel { getColumnHeader(columnName: string) { return this.table - .locator('.rt-thead') + .getByTestId('table-header') .getByText(columnName, { exact: true }); } @@ -41,7 +41,7 @@ class ReceivingTable extends BasePageModel { // shown when the autofill would overwrite a location the user has changed get autofillLocationConfirmDialog() { - return this.page.locator('.react-confirm-alert'); + return this.page.getByTestId('confirm-modal'); } get acceptAutofillLocationConfirmDialog() { @@ -85,7 +85,11 @@ class Row extends BasePageModel { // the selected value only, without the screen reader text react-select // renders next to it while focused get selectedBinLocation() { - return this.binLocationSelect.locator('.react-select__single-value'); + return this.binLocationSelect.getByTestId('custom-select-value'); + } + + get clearBinLocationButton() { + return this.binLocationSelect.getByTestId('custom-select-clear'); } getBinLocation(binLocation: string) { diff --git a/src/pages/receiving/steps/CheckStep.ts b/src/pages/receiving/steps/CheckStep.ts index c6e4de7b..3158fe89 100644 --- a/src/pages/receiving/steps/CheckStep.ts +++ b/src/pages/receiving/steps/CheckStep.ts @@ -21,10 +21,14 @@ class CheckStep extends BasePageModel { }); } + // the buttons are shown both above the table and at the bottom of the page, + // so use the ones at the bottom + get wizardButtons() { + return this.page.getByTestId('wizard-page-buttons'); + } + get receiveShipmentButton() { - return this.page - .locator('.submit-buttons') - .getByRole('button', { name: 'Complete Receipt' }); + return this.wizardButtons.getByRole('button', { name: 'Complete Receipt' }); } get shimpentInformation() { @@ -32,44 +36,29 @@ class CheckStep extends BasePageModel { } get originField() { - return this.shimpentInformation - .locator('.item-details__field') - .filter({ hasText: 'Origin:' }) - .locator('.item-details__value'); + return this.shimpentInformation.getByTestId('confirm-receipt-origin'); } get destinationField() { - return this.shimpentInformation - .locator('.item-details__field') - .filter({ hasText: 'Destination:' }) - .locator('.item-details__value'); + return this.shimpentInformation.getByTestId('confirm-receipt-destination'); } get shippedOnField() { - return this.shimpentInformation - .locator('.item-details__field') - .filter({ hasText: 'Shipped on:' }) - .locator('.item-details__value'); + return this.shimpentInformation.getByTestId('confirm-receipt-shipped-on'); } - // the button is wrapped in a tooltip, which can intercept the click once the - // tooltip is shown, so the tests click it with { force: true } get cancelAllRemainingButton() { return this.page.getByRole('button', { name: 'Cancel All Remaining' }); } get validationOnDeliveredOnPastDatePopup() { return this.page - .locator('.s-alert-box-inner') + .getByTestId('notification') .getByText('Must occur on or after Actual Shipping Date'); } - // the button is wrapped in a tooltip, which can intercept the click once the - // tooltip is shown, so the tests click it with { force: true } get backToEditButton() { - return this.page - .locator('.submit-buttons') - .getByRole('button', { name: 'Back to Receive' }); + return this.wizardButtons.getByRole('button', { name: 'Back to Receive' }); } } diff --git a/src/pages/receiving/steps/ReceivingStep.ts b/src/pages/receiving/steps/ReceivingStep.ts index b0d3a59b..ad0069ca 100644 --- a/src/pages/receiving/steps/ReceivingStep.ts +++ b/src/pages/receiving/steps/ReceivingStep.ts @@ -37,8 +37,6 @@ class ReceivingStep extends BasePageModel { await this.page.waitForResponse(PARTIAL_RECEIVING_API_PATTERN); } - // the button is wrapped in a tooltip, which can intercept the click once the - // tooltip is shown, so the tests click it with { force: true } get autofillQuantitiesButton() { return this.page.getByRole('button', { name: 'Autofill quantities' }); } @@ -60,12 +58,10 @@ class ReceivingStep extends BasePageModel { } async enableShowPutaway() { - await this.showPutawaySwitch.locator('.slider').click(); + await this.showPutawaySwitch.getByTestId('switch-toggle').click(); await expect(this.showPutawaySwitch.getByRole('checkbox')).toBeChecked(); } - // the button is wrapped in a tooltip, which can intercept the click once the - // tooltip is shown, so the tests click it with { force: true } get saveAndExitButton() { return this.page.getByRole('button', { name: 'Save & Exit' }); } @@ -94,14 +90,12 @@ class ReceivingStep extends BasePageModel { get validationOnEditFieldsThroughImport() { return this.page - .locator('.s-alert-box-inner') + .getByTestId('notification') .getByText( 'You can only import the Receiving Now and the Comment fields. To make other changes, please use the edit line feature. You can then export and import the template again.' ); } - // the button is wrapped in a tooltip, which can intercept the click once the - // tooltip is shown, so the tests click it with { force: true } get resetSortingButton() { return this.page.getByRole('button', { name: 'Reset sorting' }); } diff --git a/src/tests/putaway/rollbackLastReceiptWhenPutawayCreated.test.ts b/src/tests/putaway/rollbackLastReceiptWhenPutawayCreated.test.ts index 8333861d..d456c9c9 100644 --- a/src/tests/putaway/rollbackLastReceiptWhenPutawayCreated.test.ts +++ b/src/tests/putaway/rollbackLastReceiptWhenPutawayCreated.test.ts @@ -161,9 +161,7 @@ test.describe('Rollback last receipt behavior when putaway created', () => { await test.step('Receive sm', async () => { await stockMovementShowPage.receiveButton.click(); await receivingPage.receivingStep.isLoaded(); - await receivingPage.receivingStep.autofillQuantitiesButton.click({ - force: true, - }); + await receivingPage.receivingStep.autofillQuantitiesButton.click(); await receivingPage.nextButton.click(); await receivingPage.checkStep.isLoaded(); await receivingPage.checkStep.receiveShipmentButton.click(); diff --git a/src/tests/receiving/assertBinLocationField.test.ts b/src/tests/receiving/assertBinLocationField.test.ts index 2a5d586f..a191ca45 100644 --- a/src/tests/receiving/assertBinLocationField.test.ts +++ b/src/tests/receiving/assertBinLocationField.test.ts @@ -73,12 +73,9 @@ test.describe('Assert bin location not clearable', () => { }); await test.step('Assert bin location cant be cleared', async () => { - const binLocationSelect = - receivingPage.receivingStep.table.row(1).binLocationSelect; - await expect(binLocationSelect).toHaveText(receivingBin); - await expect( - binLocationSelect.locator('.react-select__clear-indicator') - ).toBeHidden(); + const row = receivingPage.receivingStep.table.row(1); + await expect(row.binLocationSelect).toHaveText(receivingBin); + await expect(row.clearBinLocationButton).toBeHidden(); }); await test.step('Split lines', async () => { @@ -98,14 +95,10 @@ test.describe('Assert bin location not clearable', () => { await test.step('Assert bin location field content in edit modal', async () => { for (const row of [1, 2]) { - const binLocationSelect = - receivingPage.receivingStep.editModal.table.row( - row - ).binLocationSelect; - await expect(binLocationSelect).toHaveText(receivingBin); - await expect( - binLocationSelect.locator('.react-select__clear-indicator') - ).toBeHidden(); + const editModalRow = + receivingPage.receivingStep.editModal.table.row(row); + await expect(editModalRow.binLocationSelect).toHaveText(receivingBin); + await expect(editModalRow.clearBinLocationButton).toBeHidden(); } }); @@ -121,12 +114,9 @@ test.describe('Assert bin location not clearable', () => { receivingPage.receivingStep.table.row(1).binLocationSelect ).toHaveText(receivingBin); for (const row of [3, 4]) { - const binLocationSelect = - receivingPage.receivingStep.table.row(row).binLocationSelect; - await expect(binLocationSelect).toHaveText(receivingBin); - await expect( - binLocationSelect.locator('.react-select__clear-indicator') - ).toBeHidden(); + const tableRow = receivingPage.receivingStep.table.row(row); + await expect(tableRow.binLocationSelect).toHaveText(receivingBin); + await expect(tableRow.clearBinLocationButton).toBeHidden(); } }); }); diff --git a/src/tests/receiving/assertCreationOfGoodsReceiptNote.test.ts b/src/tests/receiving/assertCreationOfGoodsReceiptNote.test.ts index a63812da..ab36513f 100644 --- a/src/tests/receiving/assertCreationOfGoodsReceiptNote.test.ts +++ b/src/tests/receiving/assertCreationOfGoodsReceiptNote.test.ts @@ -118,7 +118,7 @@ test.describe('Assert Goods Receipt Note is created and opened', () => { .row(7) .downloadButton.click(); const popup = await popupPromise; - await expect(popup.locator('.title')).toHaveText('Goods Receipt Note'); + await expect(popup.getByTestId('document-title')).toHaveText('Goods Receipt Note'); expect(await pageContainsValues(popup, expectedValues)).toBeTruthy(); await popup.close(); }); @@ -154,7 +154,7 @@ test.describe('Assert Goods Receipt Note is created and opened', () => { .row(7) .downloadButton.click(); const popup = await popupPromise; - await expect(popup.locator('.title')).toHaveText('Goods Receipt Note'); + await expect(popup.getByTestId('document-title')).toHaveText('Goods Receipt Note'); expect(await pageContainsValues(popup, expectedValues)).toBeTruthy(); await popup.close(); }); diff --git a/src/tests/receiving/assertQtyInputs.test.ts b/src/tests/receiving/assertQtyInputs.test.ts index 0e166782..2732130f 100644 --- a/src/tests/receiving/assertQtyInputs.test.ts +++ b/src/tests/receiving/assertQtyInputs.test.ts @@ -83,9 +83,7 @@ test.describe('Assert if quantity inputs remain when split lines', () => { }); await test.step('Autofill receiving quantity', async () => { - await receivingPage.receivingStep.autofillQuantitiesButton.click({ - force: true, - }); + await receivingPage.receivingStep.autofillQuantitiesButton.click(); await expect( receivingPage.receivingStep.table.row(1).receivingNowField.numberbox ).toHaveValue('50'); @@ -143,9 +141,7 @@ test.describe('Assert if quantity inputs remain when split lines', () => { }); await test.step('Autofill quantity after split line does not override inputs', async () => { - await receivingPage.receivingStep.autofillQuantitiesButton.click({ - force: true, - }); + await receivingPage.receivingStep.autofillQuantitiesButton.click(); await expect( receivingPage.receivingStep.table.row(1).receivingNowField.numberbox ).toHaveValue('50'); @@ -233,9 +229,7 @@ test.describe('Assert if quantity inputs remain when split lines', () => { await receivingPage.receivingStep.table .row(3) .receivingNowField.numberbox.fill('200'); - await receivingPage.receivingStep.saveAndExitButton.click({ - force: true, - }); + await receivingPage.receivingStep.saveAndExitButton.click(); await stockMovementShowPage.isLoaded(); }); @@ -303,9 +297,7 @@ test.describe('Assert if quantity inputs remain when split lines', () => { }); await test.step('Autofill quantity after split line does not override inputs', async () => { - await receivingPage.receivingStep.autofillQuantitiesButton.click({ - force: true, - }); + await receivingPage.receivingStep.autofillQuantitiesButton.click(); await expect( receivingPage.receivingStep.table.row(3).receivingNowField.numberbox ).toHaveValue('25'); diff --git a/src/tests/receiving/assertRecipientField.test.ts b/src/tests/receiving/assertRecipientField.test.ts index e937d0dc..322275c5 100644 --- a/src/tests/receiving/assertRecipientField.test.ts +++ b/src/tests/receiving/assertRecipientField.test.ts @@ -121,7 +121,7 @@ test.describe.skip('Assert recipient field when receive', () => { await test.step('Go backward and assert recipient field', async () => { const USER = await mainUserService.getUser(); - await receivingPage.checkStep.backToEditButton.click({ force: true }); + await receivingPage.checkStep.backToEditButton.click(); await receivingPage.receivingStep.isLoaded(); await expect( receivingPage.receivingStep.table.getCellValue(1, 'Recipient') diff --git a/src/tests/receiving/cancelRemainingQty.test.ts b/src/tests/receiving/cancelRemainingQty.test.ts index dbcd2f5b..6ae1e2e9 100644 --- a/src/tests/receiving/cancelRemainingQty.test.ts +++ b/src/tests/receiving/cancelRemainingQty.test.ts @@ -371,9 +371,7 @@ test.describe('Cancel qty in the middle of receipt', () => { }); await test.step('Select cancel all remaining button', async () => { - await receivingPage.checkStep.cancelAllRemainingButton.click({ - force: true, - }); + await receivingPage.checkStep.cancelAllRemainingButton.click(); await expect( receivingPage.checkStep.table.rowByProductCode(PRODUCT_ONE_CODE) .cancelRemainingCheckbox @@ -434,7 +432,7 @@ test.describe('Cancel qty in the middle of receipt', () => { }); await test.step('Go to backward to receiving page and forward again', async () => { - await receivingPage.checkStep.backToEditButton.click({ force: true }); + await receivingPage.checkStep.backToEditButton.click(); await receivingPage.nextButton.click(); await receivingPage.checkStep.isLoaded(); await expect( @@ -489,7 +487,7 @@ test.describe('Cancel qty in the middle of receipt', () => { }); await test.step('Go to backward to receiving page and forward again', async () => { - await receivingPage.checkStep.backToEditButton.click({ force: true }); + await receivingPage.checkStep.backToEditButton.click(); await receivingPage.nextButton.click(); await receivingPage.checkStep.isLoaded(); await expect( diff --git a/src/tests/receiving/editBinLocationWhenReceive.test.ts b/src/tests/receiving/editBinLocationWhenReceive.test.ts index 8dcd2f04..7f563be8 100644 --- a/src/tests/receiving/editBinLocationWhenReceive.test.ts +++ b/src/tests/receiving/editBinLocationWhenReceive.test.ts @@ -533,9 +533,7 @@ test.describe('Edit Bin Location when receive for all lines', () => { }); await test.step('Autofill qty and go to check page', async () => { - await receivingPage.receivingStep.autofillQuantitiesButton.click({ - force: true, - }); + await receivingPage.receivingStep.autofillQuantitiesButton.click(); await receivingPage.nextButton.click(); await receivingPage.checkStep.isLoaded(); }); diff --git a/src/tests/receiving/editOriginalLineQtyTo0.test.ts b/src/tests/receiving/editOriginalLineQtyTo0.test.ts index 9a7e524a..27fa288f 100644 --- a/src/tests/receiving/editOriginalLineQtyTo0.test.ts +++ b/src/tests/receiving/editOriginalLineQtyTo0.test.ts @@ -147,7 +147,7 @@ test.describe('Edit qty of original line to 0', () => { }); await test.step('Return to receiving step and assert original line is not visible', async () => { - await receivingPage.checkStep.backToEditButton.click({ force: true }); + await receivingPage.checkStep.backToEditButton.click(); await receivingPage.receivingStep.isLoaded(); await expect(receivingPage.receivingStep.table.rows).toHaveCount(8); await expect( diff --git a/src/tests/receiving/lotExpirySystemUpdateOnReceiving.test.ts b/src/tests/receiving/lotExpirySystemUpdateOnReceiving.test.ts index 19170b30..cb063dcc 100644 --- a/src/tests/receiving/lotExpirySystemUpdateOnReceiving.test.ts +++ b/src/tests/receiving/lotExpirySystemUpdateOnReceiving.test.ts @@ -143,9 +143,7 @@ test.describe('Lot number system expiry date modification on receiving workflow' }); await test.step('Autofill all quantities of receving items', async () => { - await receivingPage.receivingStep.autofillQuantitiesButton.click({ - force: true, - }); + await receivingPage.receivingStep.autofillQuantitiesButton.click(); }); await test.step('Go to next step', async () => { @@ -254,9 +252,7 @@ test.describe('Lot number system expiry date modification on receiving workflow' }); await test.step('Autofill all quantities of receving items', async () => { - await receivingPage.receivingStep.autofillQuantitiesButton.click({ - force: true, - }); + await receivingPage.receivingStep.autofillQuantitiesButton.click(); }); await test.step('Go to next step', async () => { @@ -372,9 +368,7 @@ test.describe('Lot number system expiry date modification on receiving workflow' }); await test.step('Autofill all quantities of receving items', async () => { - await receivingPage.receivingStep.autofillQuantitiesButton.click({ - force: true, - }); + await receivingPage.receivingStep.autofillQuantitiesButton.click(); }); await test.step('Go to next step', async () => { @@ -496,9 +490,7 @@ test.describe('Lot number system expiry date modification on receiving workflow' }); await test.step('Autofill all quantities of receving items', async () => { - await receivingPage.receivingStep.autofillQuantitiesButton.click({ - force: true, - }); + await receivingPage.receivingStep.autofillQuantitiesButton.click(); }); await test.step('Go to next step', async () => { diff --git a/src/tests/receiving/receiveInbound.test.ts b/src/tests/receiving/receiveInbound.test.ts index 27f726ba..bb819af0 100644 --- a/src/tests/receiving/receiveInbound.test.ts +++ b/src/tests/receiving/receiveInbound.test.ts @@ -118,9 +118,7 @@ test.describe('Receive inbound stock movement', () => { await test.step('Select all items to receive', async () => { await receivingPage.receivingStep.isLoaded(); - await receivingPage.receivingStep.autofillQuantitiesButton.click({ - force: true, - }); + await receivingPage.receivingStep.autofillQuantitiesButton.click(); }); await test.step('Go to Check page', async () => { @@ -302,9 +300,7 @@ test.describe('Receive inbound stock movement', () => { }); await test.step('Click on Save and Exit button', async () => { - await receivingPage.receivingStep.saveAndExitButton.click({ - force: true, - }); + await receivingPage.receivingStep.saveAndExitButton.click(); await stockMovementShowPage.isLoaded(); }); diff --git a/src/tests/receiving/receiveInboundWithoutPickAndPutawayStock.test.ts b/src/tests/receiving/receiveInboundWithoutPickAndPutawayStock.test.ts index cd6ae722..2d1a79da 100644 --- a/src/tests/receiving/receiveInboundWithoutPickAndPutawayStock.test.ts +++ b/src/tests/receiving/receiveInboundWithoutPickAndPutawayStock.test.ts @@ -91,9 +91,7 @@ test.describe('Receive inbound stock movement in location without pick and putaw await test.step('Autofill receiving qty', async () => { await receivingPage.receivingStep.isLoaded(); - await receivingPage.receivingStep.autofillQuantitiesButton.click({ - force: true, - }); + await receivingPage.receivingStep.autofillQuantitiesButton.click(); }); await test.step('Go to and assert checking page is visible', async () => { diff --git a/src/tests/receiving/sortByAlphabeticalOrderAndRemainInputs.test.ts b/src/tests/receiving/sortByAlphabeticalOrderAndRemainInputs.test.ts index f3bd3093..412e116a 100644 --- a/src/tests/receiving/sortByAlphabeticalOrderAndRemainInputs.test.ts +++ b/src/tests/receiving/sortByAlphabeticalOrderAndRemainInputs.test.ts @@ -164,11 +164,9 @@ test.describe('Apply sorting by alphabetical order and remain inputs', () => { await test.step('Go back to receive page and reset sorting to shipment order', async () => { const PRODUCT_FIVE = await productService.getProduct(Product.FIVE); - await receivingPage.checkStep.backToEditButton.click({ force: true }); + await receivingPage.checkStep.backToEditButton.click(); await receivingPage.receivingStep.isLoaded(); - await receivingPage.receivingStep.resetSortingButton.click({ - force: true, - }); + await receivingPage.receivingStep.resetSortingButton.click(); await expect( receivingPage.receivingStep.table.row(3).getItem(PRODUCT_FIVE.name) ).toBeVisible(); diff --git a/src/tests/receiving/validationsOnDeliverOnDate.test.ts b/src/tests/receiving/validationsOnDeliverOnDate.test.ts index 20d03167..920d4785 100644 --- a/src/tests/receiving/validationsOnDeliverOnDate.test.ts +++ b/src/tests/receiving/validationsOnDeliverOnDate.test.ts @@ -66,9 +66,7 @@ test.describe.skip('Validations on edit Deliver On Date when receiving shipment' }); await test.step('Autofill qty and go to check page', async () => { - await receivingPage.receivingStep.autofillQuantitiesButton.click({ - force: true, - }); + await receivingPage.receivingStep.autofillQuantitiesButton.click(); await receivingPage.nextButton.click(); }); @@ -100,9 +98,7 @@ test.describe.skip('Validations on edit Deliver On Date when receiving shipment' }); await test.step('Autofill qty and go to check page', async () => { - await receivingPage.receivingStep.autofillQuantitiesButton.click({ - force: true, - }); + await receivingPage.receivingStep.autofillQuantitiesButton.click(); await receivingPage.nextButton.click(); }); diff --git a/src/utils/shipmentUtils.ts b/src/utils/shipmentUtils.ts index e341d92f..c58d9dcb 100644 --- a/src/utils/shipmentUtils.ts +++ b/src/utils/shipmentUtils.ts @@ -1,5 +1,6 @@ import { expect } from '@playwright/test'; +import InventoryService from '@/api/InventoryService'; import PutawayService from '@/api/PutawayService'; import ReceivingService from '@/api/ReceivingService'; import StockMovementService from '@/api/StockMovementService'; @@ -78,15 +79,18 @@ export async function deleteShipment({ await stockMovementService.deleteStockMovement(STOCK_MOVEMENT.id); // The server clears receiving-bin stock asynchronously after the stock - // movement is deleted, so a putaway candidate from this shipment can stay - // visible for a while; wait until it is gone so the next test (or the + // movement is deleted (a queued job that falls behind when many tests run + // one after another), so a putaway candidate from this shipment can stay + // visible for over a minute. Refresh the availability of the location right + // away and then wait until the candidate is gone, so the next test (or the // clean-state validation) does not see a phantom row. - const putawayService = new PutawayService( - stockMovementService.getRequestContext() - ); + const request = stockMovementService.getRequestContext(); + const putawayService = new PutawayService(request); + const inventoryService = new InventoryService(request); const destinationId = STOCK_MOVEMENT.destination?.id ?? AppConfig.instance.locations.main.readId(); + await inventoryService.refreshProductAvailability(destinationId); await expect .poll( async () => { From ceafab51e97989c0784da0cba57a5bb2234c31e3 Mon Sep 17 00:00:00 2001 From: Alan Nadolny Date: Fri, 9 Oct 2026 13:53:56 +0200 Subject: [PATCH 17/19] OBPIH-8025 Unskip assert recipient field --- src/pages/receiving/components/CheckTable.ts | 4 + .../receiving/components/ReceivingTable.ts | 19 ++++ .../receiving/assertRecipientField.test.ts | 103 ++++++++---------- 3 files changed, 66 insertions(+), 60 deletions(-) diff --git a/src/pages/receiving/components/CheckTable.ts b/src/pages/receiving/components/CheckTable.ts index 6d96299f..999485a5 100644 --- a/src/pages/receiving/components/CheckTable.ts +++ b/src/pages/receiving/components/CheckTable.ts @@ -79,6 +79,10 @@ class Row extends BasePageModel { return this.row.locator('[aria-label="Status"]'); } + get recipient() { + return this.row.locator('[aria-label="Recipient"]'); + } + get cancelRemainingCheckbox() { return this.row .locator('[data-column-id="cancelRemaining"]') diff --git a/src/pages/receiving/components/ReceivingTable.ts b/src/pages/receiving/components/ReceivingTable.ts index 781d0404..145b7e20 100644 --- a/src/pages/receiving/components/ReceivingTable.ts +++ b/src/pages/receiving/components/ReceivingTable.ts @@ -23,6 +23,19 @@ class ReceivingTable extends BasePageModel { return new Row(this.page, this.rows.nth(index - 1)); } + // the order of the items depends on the sorting, so find the row by its + // product code instead of its position + rowByProductCode(productCode: string) { + return new Row( + this.page, + this.rows.filter({ + has: this.page + .locator('[aria-label="Code"]') + .getByText(productCode, { exact: true }), + }) + ); + } + getColumnHeader(columnName: string) { return this.table .getByTestId('table-header') @@ -107,6 +120,12 @@ class Row extends BasePageModel { getItem(name: string) { return this.row.locator('[aria-label="Product"]').getByText(name); } + + // shown only when at least one item has a recipient, and read-only (it can + // only be changed in the edit modal) + get recipient() { + return this.row.locator('[aria-label="Recipient"]'); + } } export default ReceivingTable; diff --git a/src/tests/receiving/assertRecipientField.test.ts b/src/tests/receiving/assertRecipientField.test.ts index 322275c5..14bee209 100644 --- a/src/tests/receiving/assertRecipientField.test.ts +++ b/src/tests/receiving/assertRecipientField.test.ts @@ -6,8 +6,11 @@ import { StockMovementResponse } from '@/types'; import BinLocationUtils from '@/utils/BinLocationUtils'; import { deleteShipment } from '@/utils/shipmentUtils'; -test.describe.skip('Assert recipient field when receive', () => { +test.describe('Assert recipient field when receive', () => { let STOCK_MOVEMENT: StockMovementResponse; + let PRODUCT_FOUR_CODE: string; + let PRODUCT_FIVE_CODE: string; + let USER_NAME: string; test.beforeEach( async ({ @@ -20,6 +23,9 @@ test.describe.skip('Assert recipient field when receive', () => { const PRODUCT_FOUR = await productService.getProduct(Product.FOUR); const PRODUCT_FIVE = await productService.getProduct(Product.FIVE); const USER = await mainUserService.getUser(); + PRODUCT_FOUR_CODE = PRODUCT_FOUR.productCode; + PRODUCT_FIVE_CODE = PRODUCT_FIVE.productCode; + USER_NAME = USER.name; STOCK_MOVEMENT = await stockMovementService.createInbound({ originId: supplierLocation.id, @@ -64,11 +70,27 @@ test.describe.skip('Assert recipient field when receive', () => { } ); - test('Assert recipient field filled and disabled', async ({ + test('Assert recipient field filled and read-only', async ({ stockMovementShowPage, receivingPage, - mainUserService, }) => { + // the recipient is shown as plain text in the receiving table, it can be + // changed only in the edit modal + const assertRecipientsOnReceivingStep = async () => { + const table = receivingPage.receivingStep.table; + await expect( + table.rowByProductCode(PRODUCT_FOUR_CODE).recipient + ).toHaveText(USER_NAME); + await expect( + table.rowByProductCode(PRODUCT_FIVE_CODE).recipient + ).toBeEmpty(); + await expect( + table + .rowByProductCode(PRODUCT_FOUR_CODE) + .recipient.getByTestId('custom-select-element') + ).toBeHidden(); + }; + await test.step('Go to stock movement show page', async () => { await stockMovementShowPage.goToPage(STOCK_MOVEMENT.id); await stockMovementShowPage.isLoaded(); @@ -79,64 +101,39 @@ test.describe.skip('Assert recipient field when receive', () => { await receivingPage.receivingStep.isLoaded(); }); - await test.step('Assert recipient field disabled and filled', async () => { - const USER = await mainUserService.getUser(); - await expect( - receivingPage.receivingStep.table.getCellValue(1, 'Recipient') - ).toBeEmpty(); - await receivingPage.receivingStep.table - .row(1) - .recipientField.isDisabled(); - await expect( - receivingPage.receivingStep.table.getCellValue(2, 'Recipient') - ).toHaveText(USER.name); - await receivingPage.receivingStep.table.row(2).recipientField.click(); - await receivingPage.receivingStep.table - .row(2) - .recipientField.getByTestId('custom-select-dropdown-menu') - .isHidden(); + await test.step('Assert recipient field on receiving step', async () => { + await receivingPage.assertColumnHeaderIsVisibleOnReceivingStep( + 'Recipient' + ); + await assertRecipientsOnReceivingStep(); }); await test.step('Fill partial qty for items', async () => { await receivingPage.receivingStep.table - .row(1) - .receivingNowField.textbox.fill('5'); + .rowByProductCode(PRODUCT_FOUR_CODE) + .receivingNowField.numberbox.fill('5'); await receivingPage.receivingStep.table - .row(2) - .receivingNowField.textbox.fill('5'); + .rowByProductCode(PRODUCT_FIVE_CODE) + .receivingNowField.numberbox.fill('5'); await receivingPage.nextButton.click(); await receivingPage.checkStep.isLoaded(); }); - await test.step('Fill partial qty for items', async () => { - const USER = await mainUserService.getUser(); - await receivingPage.checkStep.isLoaded(); + await test.step('Assert recipient field on check step', async () => { await expect( - receivingPage.checkStep.table.getCellValue(1, 'Recipient') - ).toBeEmpty(); + receivingPage.checkStep.table.rowByProductCode(PRODUCT_FOUR_CODE) + .recipient + ).toHaveText(USER_NAME); await expect( - receivingPage.checkStep.table.getCellValue(2, 'Recipient') - ).toHaveText(USER.name); + receivingPage.checkStep.table.rowByProductCode(PRODUCT_FIVE_CODE) + .recipient + ).toBeEmpty(); }); await test.step('Go backward and assert recipient field', async () => { - const USER = await mainUserService.getUser(); await receivingPage.checkStep.backToEditButton.click(); await receivingPage.receivingStep.isLoaded(); - await expect( - receivingPage.receivingStep.table.getCellValue(1, 'Recipient') - ).toBeEmpty(); - await receivingPage.receivingStep.table - .row(1) - .recipientField.isDisabled(); - await expect( - receivingPage.receivingStep.table.getCellValue(2, 'Recipient') - ).toHaveText(USER.name); - await receivingPage.receivingStep.table.row(2).recipientField.click(); - await receivingPage.receivingStep.table - .row(2) - .recipientField.getByTestId('custom-select-dropdown-menu') - .isHidden(); + await assertRecipientsOnReceivingStep(); }); await test.step('Finish 1st receipt', async () => { @@ -147,23 +144,9 @@ test.describe.skip('Assert recipient field when receive', () => { }); await test.step('Start 2nd receipt and assert recipient field', async () => { - const USER = await mainUserService.getUser(); await stockMovementShowPage.receiveButton.click(); await receivingPage.receivingStep.isLoaded(); - await expect( - receivingPage.receivingStep.table.getCellValue(1, 'Recipient') - ).toBeEmpty(); - await receivingPage.receivingStep.table - .row(1) - .recipientField.isDisabled(); - await expect( - receivingPage.receivingStep.table.getCellValue(2, 'Recipient') - ).toHaveText(USER.name); - await receivingPage.receivingStep.table.row(2).recipientField.click(); - await receivingPage.receivingStep.table - .row(2) - .recipientField.getByTestId('custom-select-dropdown-menu') - .isHidden(); + await assertRecipientsOnReceivingStep(); }); }); }); From 5b49f0bcc9f53fcd1785831cfbcce9bfc3244a43 Mon Sep 17 00:00:00 2001 From: Alan Nadolny Date: Fri, 9 Oct 2026 14:12:12 +0200 Subject: [PATCH 18/19] OBPIH-8025 Unskip validations on deliver on date --- src/components/DatePicker.ts | 40 ++++++++++++++ src/pages/receiving/steps/CheckStep.ts | 6 -- .../validationsOnDeliverOnDate.test.ts | 55 +++++++++---------- 3 files changed, 67 insertions(+), 34 deletions(-) diff --git a/src/components/DatePicker.ts b/src/components/DatePicker.ts index b20b126b..ed61fd44 100644 --- a/src/components/DatePicker.ts +++ b/src/components/DatePicker.ts @@ -1,6 +1,15 @@ +import { expect } from '@playwright/test'; + import FormField from '@/components/FormField'; import { formatDate } from '@/utils/DateUtils'; +const ordinal = (day: number) => { + if (day % 100 >= 11 && day % 100 <= 13) { + return `${day}th`; + } + return `${day}${{ 1: 'st', 2: 'nd', 3: 'rd' }[day % 10] ?? 'th'}`; +}; + class DatePicker extends FormField { get datePickerPopup() { return this.page.locator('.react-datepicker'); @@ -15,6 +24,37 @@ class DatePicker extends FormField { await this.page.keyboard.press('Enter'); } + /** + For date fields that open a calendar instead of taking typed input (the + field itself is a button). Picks the day in the calendar, which keeps the + time that was selected before. + */ + async selectDate(date: Date) { + await this.field.getByRole('button').first().click(); + await expect(this.page.getByRole('option').first()).toBeVisible(); + // e.g. "Choose Saturday, October 10th, 2026" + const day = this.page.getByRole('option', { + name: `Choose ${formatDate(date, 'dddd, MMMM')} ${ordinal( + date.getDate() + )}, ${date.getFullYear()}`, + }); + // the calendar opens on the selected month, so move to the month of the + // date when it isn't shown (days of the neighbouring months are listed too) + if (!(await day.isVisible())) { + const monthButton = date > new Date() ? 'Next Month' : 'Previous Month'; + await this.page.getByRole('button', { name: monthButton }).click(); + } + await day.click(); + } + + // fields that don't show the error under them show it in a tooltip on hover + async assertErrorTooltip(message: string) { + await this.field.hover(); + await expect( + this.page.getByRole('tooltip').getByText(message) + ).toBeVisible(); + } + async fillWithFormat(date: Date, format: string) { // TODO: This is temporary solution until we figure out the sluggishness of the date picker await this.page.waitForTimeout(1000); diff --git a/src/pages/receiving/steps/CheckStep.ts b/src/pages/receiving/steps/CheckStep.ts index 3158fe89..ca97e1a5 100644 --- a/src/pages/receiving/steps/CheckStep.ts +++ b/src/pages/receiving/steps/CheckStep.ts @@ -51,12 +51,6 @@ class CheckStep extends BasePageModel { return this.page.getByRole('button', { name: 'Cancel All Remaining' }); } - get validationOnDeliveredOnPastDatePopup() { - return this.page - .getByTestId('notification') - .getByText('Must occur on or after Actual Shipping Date'); - } - get backToEditButton() { return this.wizardButtons.getByRole('button', { name: 'Back to Receive' }); } diff --git a/src/tests/receiving/validationsOnDeliverOnDate.test.ts b/src/tests/receiving/validationsOnDeliverOnDate.test.ts index 920d4785..fc7a1540 100644 --- a/src/tests/receiving/validationsOnDeliverOnDate.test.ts +++ b/src/tests/receiving/validationsOnDeliverOnDate.test.ts @@ -4,10 +4,10 @@ import { expect, test } from '@/fixtures/fixtures'; import { Product } from '@/generated/ProductCodes.generated'; import { StockMovementResponse } from '@/types'; import BinLocationUtils from '@/utils/BinLocationUtils'; -import { formatDate, getDateByOffset } from '@/utils/DateUtils'; +import { getDateByOffset } from '@/utils/DateUtils'; import { deleteShipment } from '@/utils/shipmentUtils'; -test.describe.skip('Validations on edit Deliver On Date when receiving shipment', () => { +test.describe('Validations on edit Deliver On Date when receiving shipment', () => { let STOCK_MOVEMENT: StockMovementResponse; test.beforeEach( @@ -68,22 +68,29 @@ test.describe.skip('Validations on edit Deliver On Date when receiving shipment' await test.step('Autofill qty and go to check page', async () => { await receivingPage.receivingStep.autofillQuantitiesButton.click(); await receivingPage.nextButton.click(); + await receivingPage.checkStep.isLoaded(); }); await test.step('Edit Delivered on Date on check page to future date', async () => { - await receivingPage.checkStep.isLoaded(); - await receivingPage.checkStep.deliveredOnDateField.fillWithFormat( - getDateByOffset(new Date(), 1), - 'MM/DD/YYYY HH:mm:ss Z' + await receivingPage.checkStep.deliveredOnDateField.selectDate( + getDateByOffset(new Date(), 1) ); await receivingPage.checkStep.deliveredOnDateField.assertHasError(); + await receivingPage.checkStep.deliveredOnDateField.assertErrorTooltip( + 'Delivery date cannot be in the future' + ); await expect( - receivingPage.checkStep.deliveredOnDateField.errorMessage - ).toContainText('The date cannot be in the future'); + receivingPage.checkStep.receiveShipmentButton + ).toBeDisabled(); + }); + + await test.step('Change Delivered on Date back to today', async () => { + await receivingPage.checkStep.deliveredOnDateField.selectDate(new Date()); + await expect(receivingPage.checkStep.receiveShipmentButton).toBeEnabled(); }); }); - test.skip('Assert validation on try to edit Delivered on Date to past date', async ({ + test('Assert validation on try to edit Delivered on Date to date before shipped date', async ({ stockMovementShowPage, receivingPage, }) => { @@ -100,29 +107,21 @@ test.describe.skip('Validations on edit Deliver On Date when receiving shipment' await test.step('Autofill qty and go to check page', async () => { await receivingPage.receivingStep.autofillQuantitiesButton.click(); await receivingPage.nextButton.click(); + await receivingPage.checkStep.isLoaded(); }); - await test.step('Edit Delivered on Date on check page to past date', async () => { - const pastDate = getDateByOffset(new Date(), -1); - // the date picker re-renders the committed value with seconds zeroed, - // so fill with :00 to make the value comparable after the commit - pastDate.setSeconds(0, 0); - await receivingPage.checkStep.isLoaded(); - await receivingPage.checkStep.deliveredOnDateField.fillWithFormat( - pastDate, - 'MM/DD/YYYY HH:mm:ss Z' + await test.step('Edit Delivered on Date on check page to date before shipped date', async () => { + // the shipment was shipped today, when the test data was created + await receivingPage.checkStep.deliveredOnDateField.selectDate( + getDateByOffset(new Date(), -1) + ); + await receivingPage.checkStep.deliveredOnDateField.assertHasError(); + await receivingPage.checkStep.deliveredOnDateField.assertErrorTooltip( + 'Delivery date cannot be before the shipped date' ); - // when the receive request fires before the date picker commits the - // value, the shipment gets received with the original valid date and - // the expected validation never appears - await expect( - receivingPage.checkStep.deliveredOnDateField.textbox - ).toHaveValue(formatDate(pastDate, 'MM/DD/YYYY HH:mm:ss Z')); - await receivingPage.checkStep.isLoaded(); - await receivingPage.checkStep.receiveShipmentButton.click(); await expect( - receivingPage.checkStep.validationOnDeliveredOnPastDatePopup - ).toBeVisible(); + receivingPage.checkStep.receiveShipmentButton + ).toBeDisabled(); }); }); }); From 64cd43f1aacd1e1266a942e69e0f57785eebb44f Mon Sep 17 00:00:00 2001 From: Alan Nadolny Date: Fri, 9 Oct 2026 14:22:23 +0200 Subject: [PATCH 19/19] OBPIH-8025 Fix pdf warnings --- src/utils/pdfUtils.ts | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/utils/pdfUtils.ts b/src/utils/pdfUtils.ts index b62eafa6..85a7b78f 100644 --- a/src/utils/pdfUtils.ts +++ b/src/utils/pdfUtils.ts @@ -1,4 +1,5 @@ import fs from 'node:fs'; +import path from 'node:path'; import { getDocument } from 'pdfjs-dist/legacy/build/pdf.js'; import type { @@ -51,9 +52,19 @@ const MAX_ROW_GAP = 25; */ const MAX_CELL_LINE_GAP = 12; +/** + The PDFs use standard fonts that aren't embedded, so pdf.js needs its own + font data to substitute them (otherwise it warns on every document). +*/ +const STANDARD_FONT_DATA_URL = `${path.join( + path.dirname(require.resolve('pdfjs-dist/package.json')), + 'standard_fonts' +)}${path.sep}`; + const loadPdfDocument = (filePath: string): Promise => { const data = new Uint8Array(fs.readFileSync(filePath)); - return getDocument({ data }).promise; + return getDocument({ data, standardFontDataUrl: STANDARD_FONT_DATA_URL }) + .promise; }; const getPageNumbers = (doc: PDFDocumentProxy): number[] =>