From 58048366caa2e2986aa1bc833822a2f57cb26da8 Mon Sep 17 00:00:00 2001 From: User Date: Fri, 17 Jan 2025 14:52:03 +0100 Subject: [PATCH 1/5] OBPIH-6696 add elements to receiving table and edit modal --- src/pages/receiving/components/EditModal.ts | 12 ++++++++++++ src/pages/receiving/components/EditModalTable.ts | 4 ++++ src/pages/receiving/components/ReceivingTable.ts | 7 +++++++ 3 files changed, 23 insertions(+) diff --git a/src/pages/receiving/components/EditModal.ts b/src/pages/receiving/components/EditModal.ts index 5bcbbe4..8044145 100644 --- a/src/pages/receiving/components/EditModal.ts +++ b/src/pages/receiving/components/EditModal.ts @@ -26,6 +26,18 @@ class EditModal extends BasePageModel { get cancelButton() { return this.modal.getByRole('button', { name: 'Cancel' }); } + + get addLineButton() { + return this.modal.getByRole('button', { name: 'Add line' }); + } + + 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 685ff4a..2249df4 100644 --- a/src/pages/receiving/components/EditModalTable.ts +++ b/src/pages/receiving/components/EditModalTable.ts @@ -35,6 +35,10 @@ class Row extends BasePageModel { this.expiryDatePickerField = new DatePicker(page, 'Expiry', row); this.quantityShippedField = new TextField(page, 'Quantity shipped', row); } + + get quantityShipped() { + return this.row.getByRole('spinbutton'); + } } export default EditModalTable; diff --git a/src/pages/receiving/components/ReceivingTable.ts b/src/pages/receiving/components/ReceivingTable.ts index c5c2310..27ea8d5 100644 --- a/src/pages/receiving/components/ReceivingTable.ts +++ b/src/pages/receiving/components/ReceivingTable.ts @@ -23,6 +23,13 @@ class ReceivingTable extends BasePageModel { getColumnHeader(columnName: string) { return this.table.locator('.table-header').getByText(columnName); } + + getCellValue(row: number, column: string) { + return this.table + .getByRole('row') + .nth(row) + .getByRole('cell', { name: column }); + } } class Row extends BasePageModel { From afbaa408537f161bef57bce1a9d9a89371723218 Mon Sep 17 00:00:00 2001 From: User Date: Fri, 17 Jan 2025 14:52:48 +0100 Subject: [PATCH 2/5] add tests for edits when receiving --- src/tests/receiving/editsInReceiving.test.ts | 359 +++++++++++++++++++ 1 file changed, 359 insertions(+) create mode 100644 src/tests/receiving/editsInReceiving.test.ts diff --git a/src/tests/receiving/editsInReceiving.test.ts b/src/tests/receiving/editsInReceiving.test.ts new file mode 100644 index 0000000..3f19ba2 --- /dev/null +++ b/src/tests/receiving/editsInReceiving.test.ts @@ -0,0 +1,359 @@ +import { ShipmentType } from '@/constants/ShipmentType'; +import { expect, test } from '@/fixtures/fixtures'; +import { StockMovementResponse } from '@/types'; +import { formatDate, getDateByOffset, getToday } from '@/utils/DateUtils'; +import UniqueIdentifier from '@/utils/UniqueIdentifier'; + +test.describe('Edit items in the middle of receipt', () => { + let STOCK_MOVEMENT: StockMovementResponse; + const description = 'some description'; + const dateRequested = getToday(); + const uniqueIdentifier = new UniqueIdentifier(); + + test.beforeEach( + async ({ + supplierLocationService, + stockMovementService, + mainProductService, + otherProductService, + }) => { + const supplierLocation = await supplierLocationService.getLocation(); + const PRODUCT_ONE = await mainProductService.getProduct(); + const PRODUCT_TWO = await otherProductService.getProduct(); + + STOCK_MOVEMENT = await stockMovementService.createInbound({ + originId: supplierLocation.id, + description, + dateRequested, + }); + + await stockMovementService.addItemsToInboundStockMovement( + STOCK_MOVEMENT.id, + [ + { + productId: PRODUCT_ONE.id, + quantity: 20, + lotNumber: uniqueIdentifier.generateUniqueString('lot'), + expirationDate: getDateByOffset(new Date(), 3), + }, + { productId: PRODUCT_TWO.id, quantity: 10 }, + ] + ); + + await stockMovementService.sendInboundStockMovement(STOCK_MOVEMENT.id, { + shipmentType: ShipmentType.AIR, + }); + } + ); + + test.afterEach(async ({ stockMovementShowPage, stockMovementService }) => { + await stockMovementShowPage.goToPage(STOCK_MOVEMENT.id); + const isRollbackLastReceiptButtonVisible = + await stockMovementShowPage.rollbackLastReceiptButton.isVisible(); + const isRollbackButtonVisible = + await stockMovementShowPage.rollbackButton.isVisible(); + + // due to failed test, shipment might not be received which will not show the button + if (isRollbackLastReceiptButtonVisible) { + await stockMovementShowPage.rollbackLastReceiptButton.click(); + } + + if (isRollbackButtonVisible) { + await stockMovementShowPage.rollbackButton.click(); + } + + await stockMovementService.deleteStockMovement(STOCK_MOVEMENT.id); + }); + + test('Edit item qty on receiving page', async ({ + stockMovementShowPage, + receivingPage, + }) => { + await test.step('Go to stock movement show page', async () => { + await stockMovementShowPage.goToPage(STOCK_MOVEMENT.id); + await stockMovementShowPage.isLoaded(); + }); + + await test.step('Go to shipment receiving page', async () => { + await stockMovementShowPage.receiveButton.click(); + await receivingPage.receivingStep.isLoaded(); + }); + + await test.step('Open edit modal for 1st item and edit qty to higher value', async () => { + await receivingPage.receivingStep.table.row(1).editButton.click(); + await receivingPage.receivingStep.editModal.isLoaded(); + await receivingPage.receivingStep.editModal.table + .row(0) + .quantityShipped.fill('50'); + await receivingPage.receivingStep.editModal.informationAboutEditedQtyNotMatchingShippedQty.isVisible(); + await receivingPage.receivingStep.editModal.saveButton.click(); + await receivingPage.receivingStep.isLoaded(); + }); + + await test.step('Open edit modal for 2nd item and edit qty to lower value', async () => { + await receivingPage.receivingStep.table.row(2).editButton.click(); + await receivingPage.receivingStep.editModal.isLoaded(); + await receivingPage.receivingStep.editModal.table + .row(0) + .quantityShipped.fill('2'); + await receivingPage.receivingStep.editModal.informationAboutEditedQtyNotMatchingShippedQty.isVisible(); + await receivingPage.receivingStep.editModal.saveButton.click(); + await receivingPage.receivingStep.isLoaded(); + }); + + await test.step('Assert shipped qty after edits for both items', async () => { + await expect( + receivingPage.receivingStep.table.getCellValue(1, 'Shipped') + ).toContainText('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(); + }); + + await test.step('Go to check page and receive shipment', async () => { + await receivingPage.nextButton.click(); + await receivingPage.checkStep.isLoaded(); + await receivingPage.checkStep.receiveShipmentButton.click(); + await stockMovementShowPage.isLoaded(); + }); + }); + + test('Assert validation on using exp date without lot for item with lot', async ({ + stockMovementShowPage, + receivingPage, + }) => { + await test.step('Go to stock movement show page', async () => { + await stockMovementShowPage.goToPage(STOCK_MOVEMENT.id); + await stockMovementShowPage.isLoaded(); + }); + + await test.step('Go to shipment receiving page', async () => { + await stockMovementShowPage.receiveButton.click(); + await receivingPage.receivingStep.isLoaded(); + }); + + await test.step('Open edit modal for 1st item and clear lot field', 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.'); + }); + }); + + test('Assert validation on using exp date without lot for item without lot', async ({ + stockMovementShowPage, + receivingPage, + }) => { + await test.step('Go to stock movement show page', async () => { + await stockMovementShowPage.goToPage(STOCK_MOVEMENT.id); + await stockMovementShowPage.isLoaded(); + }); + + await test.step('Go to shipment receiving page', async () => { + await stockMovementShowPage.receiveButton.click(); + await receivingPage.receivingStep.isLoaded(); + }); + + await test.step('Open edit modal for 2nd item and add exp date without lot', async () => { + 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(); + 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.'); + }); + }); + + test('Assert validation on using exp date without lot for item with lot on splitted line', async ({ + stockMovementShowPage, + receivingPage, + }) => { + await test.step('Go to stock movement show page', async () => { + await stockMovementShowPage.goToPage(STOCK_MOVEMENT.id); + await stockMovementShowPage.isLoaded(); + }); + + await test.step('Go to shipment receiving page', async () => { + await stockMovementShowPage.receiveButton.click(); + await receivingPage.receivingStep.isLoaded(); + }); + + await test.step('Open edit modal for 1st item and clear lot field', 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) + .quantityShipped.fill('10'); + await receivingPage.receivingStep.editModal.table + .row(1) + .quantityShipped.fill('10'); + await receivingPage.receivingStep.editModal.table + .row(1) + .expiryDatePickerField.fill(getDateByOffset(new Date(), 5)); + await receivingPage.receivingStep.editModal.saveButton.click(); + await receivingPage.receivingStep.editModal.table + .row(1) + .lotNumberField.assertHasError(); + await receivingPage.receivingStep.editModal.table + .row(1) + .lotNumberField.textbox.hover(); + await expect( + receivingPage.receivingStep.editModal.table.row(1).lotNumberField + .tooltip + ).toContainText('Items with an expiry date must also have a lot number.'); + }); + }); + + test('Add lot and exp date for item in the middle of receipt', async ({ + stockMovementShowPage, + receivingPage, + }) => { + const lot = 'add-lot-test'; + const expDate = getDateByOffset(new Date(), 5); + + await test.step('Go to stock movement show page', async () => { + await stockMovementShowPage.goToPage(STOCK_MOVEMENT.id); + await stockMovementShowPage.isLoaded(); + }); + + await test.step('Go to shipment receiving page', async () => { + await stockMovementShowPage.receiveButton.click(); + await receivingPage.receivingStep.isLoaded(); + }); + + await test.step('Open edit modal for item without lot and add 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) + .lotNumberField.textbox.fill(lot); + await receivingPage.receivingStep.editModal.table + .row(0) + .expiryDatePickerField.fill(expDate); + await receivingPage.receivingStep.editModal.saveButton.click(); + await receivingPage.receivingStep.isLoaded(); + }); + + await test.step('Assert added lot and exp date on receive page', async () => { + await expect( + receivingPage.receivingStep.table.getCellValue(2, 'Lot/Serial No.') + ).toContainText(lot); + await expect( + receivingPage.receivingStep.table.getCellValue(2, 'Expiration date') + ).toContainText(formatDate(expDate, 'MM/DD/YYYY')); + }); + + await test.step('Select item to receive', async () => { + await receivingPage.receivingStep.isLoaded(); + await receivingPage.receivingStep.table + .row(2) + .receivingNowField.textbox.fill('10'); + }); + + await test.step('Go to check page and receive shipment', async () => { + await receivingPage.nextButton.click(); + await receivingPage.checkStep.isLoaded(); + await receivingPage.checkStep.receiveShipmentButton.click(); + await stockMovementShowPage.isLoaded(); + }); + }); + + test('Split line into 2 lots', async ({ + stockMovementShowPage, + receivingPage, + }) => { + const lot = 'add-lot-test'; + const expDate = getDateByOffset(new Date(), 5); + + await test.step('Go to stock movement show page', async () => { + await stockMovementShowPage.goToPage(STOCK_MOVEMENT.id); + await stockMovementShowPage.isLoaded(); + }); + + await test.step('Go to shipment receiving page', async () => { + await stockMovementShowPage.receiveButton.click(); + await receivingPage.receivingStep.isLoaded(); + }); + + 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) + .quantityShipped.fill('15'); + await receivingPage.receivingStep.editModal.table + .row(1) + .lotNumberField.textbox.fill(lot); + await receivingPage.receivingStep.editModal.table + .row(1) + .expiryDatePickerField.fill(expDate); + await receivingPage.receivingStep.editModal.table + .row(1) + .quantityShipped.fill('5'); + await receivingPage.receivingStep.editModal.saveButton.click(); + await receivingPage.receivingStep.isLoaded(); + }); + + await test.step('Assert added lot and exp date and shipped qty on receive page', async () => { + await expect( + receivingPage.receivingStep.table.getCellValue(2, 'Lot/Serial No.') + ).toContainText(lot); + await expect( + receivingPage.receivingStep.table.getCellValue(2, 'Expiration date') + ).toContainText(formatDate(expDate, 'MM/DD/YYYY')); + await expect( + receivingPage.receivingStep.table.getCellValue(1, 'Shipped') + ).toContainText('15'); + 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'); + }); + + await test.step('Go to check page and receive shipment', async () => { + await receivingPage.nextButton.click(); + await receivingPage.checkStep.isLoaded(); + await receivingPage.checkStep.receiveShipmentButton.click(); + await stockMovementShowPage.isLoaded(); + }); + }); +}); From fa80a422efe659a3140ddebf9772a20fec7bb254 Mon Sep 17 00:00:00 2001 From: User Date: Thu, 23 Jan 2025 12:05:11 +0100 Subject: [PATCH 3/5] OBPIH-6696 remove not needed selector --- src/pages/receiving/components/EditModalTable.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/pages/receiving/components/EditModalTable.ts b/src/pages/receiving/components/EditModalTable.ts index 2249df4..d905497 100644 --- a/src/pages/receiving/components/EditModalTable.ts +++ b/src/pages/receiving/components/EditModalTable.ts @@ -3,6 +3,7 @@ import { Locator, Page } from '@playwright/test'; import DatePicker from '@/components/DatePicker'; import TextField from '@/components/TextField'; import BasePageModel from '@/pages/BasePageModel'; +import { filter } from 'lodash'; class EditModalTable extends BasePageModel { constructor(page: Page) { @@ -35,10 +36,6 @@ class Row extends BasePageModel { this.expiryDatePickerField = new DatePicker(page, 'Expiry', row); this.quantityShippedField = new TextField(page, 'Quantity shipped', row); } - - get quantityShipped() { - return this.row.getByRole('spinbutton'); - } } export default EditModalTable; From 4ec8ce60ea1e62e6445131106e8db797c744db95 Mon Sep 17 00:00:00 2001 From: User Date: Thu, 23 Jan 2025 12:06:34 +0100 Subject: [PATCH 4/5] OBPIH-6696 improve qty shipped input in test --- src/tests/receiving/editsInReceiving.test.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/tests/receiving/editsInReceiving.test.ts b/src/tests/receiving/editsInReceiving.test.ts index 3f19ba2..2deeb46 100644 --- a/src/tests/receiving/editsInReceiving.test.ts +++ b/src/tests/receiving/editsInReceiving.test.ts @@ -84,7 +84,7 @@ test.describe('Edit items in the middle of receipt', () => { await receivingPage.receivingStep.editModal.isLoaded(); await receivingPage.receivingStep.editModal.table .row(0) - .quantityShipped.fill('50'); + .quantityShippedField.numberbox.fill('50'); await receivingPage.receivingStep.editModal.informationAboutEditedQtyNotMatchingShippedQty.isVisible(); await receivingPage.receivingStep.editModal.saveButton.click(); await receivingPage.receivingStep.isLoaded(); @@ -95,7 +95,7 @@ test.describe('Edit items in the middle of receipt', () => { await receivingPage.receivingStep.editModal.isLoaded(); await receivingPage.receivingStep.editModal.table .row(0) - .quantityShipped.fill('2'); + .quantityShippedField.numberbox.fill('2'); await receivingPage.receivingStep.editModal.informationAboutEditedQtyNotMatchingShippedQty.isVisible(); await receivingPage.receivingStep.editModal.saveButton.click(); await receivingPage.receivingStep.isLoaded(); @@ -212,10 +212,10 @@ test.describe('Edit items in the middle of receipt', () => { await receivingPage.receivingStep.editModal.addLineButton.click(); await receivingPage.receivingStep.editModal.table .row(0) - .quantityShipped.fill('10'); + .quantityShippedField.numberbox.fill('10'); await receivingPage.receivingStep.editModal.table .row(1) - .quantityShipped.fill('10'); + .quantityShippedField.numberbox.fill('10'); await receivingPage.receivingStep.editModal.table .row(1) .expiryDatePickerField.fill(getDateByOffset(new Date(), 5)); @@ -310,7 +310,7 @@ test.describe('Edit items in the middle of receipt', () => { await receivingPage.receivingStep.editModal.addLineButton.click(); await receivingPage.receivingStep.editModal.table .row(0) - .quantityShipped.fill('15'); + .quantityShippedField.numberbox.fill('15'); await receivingPage.receivingStep.editModal.table .row(1) .lotNumberField.textbox.fill(lot); @@ -319,7 +319,7 @@ test.describe('Edit items in the middle of receipt', () => { .expiryDatePickerField.fill(expDate); await receivingPage.receivingStep.editModal.table .row(1) - .quantityShipped.fill('5'); + .quantityShippedField.numberbox.fill('5'); await receivingPage.receivingStep.editModal.saveButton.click(); await receivingPage.receivingStep.isLoaded(); }); From 5a00883201d1a3b78c985e41d1c102f50a840d11 Mon Sep 17 00:00:00 2001 From: User Date: Thu, 23 Jan 2025 13:58:05 +0100 Subject: [PATCH 5/5] OBPIH-6696 remove unused import --- src/pages/receiving/components/EditModalTable.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/pages/receiving/components/EditModalTable.ts b/src/pages/receiving/components/EditModalTable.ts index d905497..685ff4a 100644 --- a/src/pages/receiving/components/EditModalTable.ts +++ b/src/pages/receiving/components/EditModalTable.ts @@ -3,7 +3,6 @@ import { Locator, Page } from '@playwright/test'; import DatePicker from '@/components/DatePicker'; import TextField from '@/components/TextField'; import BasePageModel from '@/pages/BasePageModel'; -import { filter } from 'lodash'; class EditModalTable extends BasePageModel { constructor(page: Page) {