Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/playwright.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ concurrency:

jobs:
test:
timeout-minutes: 60
timeout-minutes: 90
runs-on: ubuntu-latest
permissions:
id-token: write
Expand Down
37 changes: 35 additions & 2 deletions src/api/PutawayService.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
import { APIRequestContext } from '@playwright/test';

import BaseServiceModel from '@/api/BaseServiceModel';
import { PUTAWAY_API } from '@/constants/apiUrls';
import { ApiResponse, PutawayCandidate } from '@/types';
import {
PUTAWAY_API,
PUTAWAY_BY_ID,
STOCK_TRANSFER_BY_ID,
} from '@/constants/apiUrls';
import { ApiResponse, PutawayCandidate, PutawayResponse } from '@/types';
import { parseRequestToJSON } from '@/utils/ServiceUtils';

class PutawayService extends BaseServiceModel {
Expand All @@ -24,6 +28,35 @@ class PutawayService extends BaseServiceModel {
);
}
}

/**
Returns null when the putaway order does not exist (anymore).
*/
async getPutaway(
orderId: string
): Promise<ApiResponse<PutawayResponse> | null> {
const apiResponse = await this.request.get(PUTAWAY_BY_ID(orderId));
if (!apiResponse.ok()) {
return null;
}
return await parseRequestToJSON(apiResponse);
}

/**
Putaway orders are transfer orders under the hood, so they are deleted
through the stock transfer API. The server only allows deleting orders
that have not been completed yet.
*/
async deletePutawayOrder(orderId: string) {
const apiResponse = await this.request.delete(
STOCK_TRANSFER_BY_ID(orderId)
);
if (!apiResponse.ok()) {
throw new Error(
`Problem deleting putaway order ${orderId}: ${apiResponse.status()}`
);
}
}
}

export default PutawayService;
2 changes: 0 additions & 2 deletions src/api/StockMovementService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,6 @@ class StockMovementService extends BaseServiceModel {
}

async deleteStockMovement(id: string) {
// request.delete does not throw on HTTP error statuses, and a swallowed
// failed delete leaves the stock movement behind for the next tests
const apiResponse = await this.request.delete(STOCK_MOVEMENT_BY_ID(id));
if (!apiResponse.ok()) {
throw new Error(
Expand Down
6 changes: 6 additions & 0 deletions src/constants/apiUrls.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,12 @@ export const STOCK_MOVEMENT_ITEMS = (id: string) =>

// PUTAWAY
export const PUTAWAY_API = `${API}/putaways`;
export const PUTAWAY_BY_ID = (id: string) => `${PUTAWAY_API}/${id}`;

// STOCK TRANSFER
export const STOCK_TRANSFER_API = `${API}/stockTransfers`;
export const STOCK_TRANSFER_BY_ID = (id: string) =>
`${STOCK_TRANSFER_API}/${id}`;

// PARTIAL RECEIVING
export const PARTIAL_RECEIVING_BY_ID = (id: string) =>
Expand Down
16 changes: 16 additions & 0 deletions src/pages/putaway/CreatePutawayPage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,22 @@ class CreatePutawayPage extends BasePageModel {
return this.page.getByTestId('start-putaway').nth(0);
}

/**
Clicks "Start Putaway" and returns the id of the pending putaway order
created by the click, so that the test can delete the putaway via API
even when it fails before completing it.
*/
async startPutaway(): Promise<string> {
const createResponsePromise = this.page.waitForResponse(
(response) =>
response.url().includes('/api/putaways') &&
response.request().method() === 'POST'
);
await this.startPutawayButton.click();
const createResponse = await createResponsePromise;
return (await createResponse.json()).data.id;
}

get showByStockMovementFilter() {
return this.page.getByTestId('show-by-button');
}
Expand Down
2 changes: 1 addition & 1 deletion src/pages/putaway/components/StartPutawayTable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ class Row extends BasePageModel {
return this.row.getByTestId('table-cell').nth(8);
}

get currentdBin() {
get currentBin() {
return this.row.getByTestId('table-cell').nth(9);
}

Expand Down
26 changes: 15 additions & 11 deletions src/pages/stockMovementShow/StockMovementShowPage.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { expect, Page } from '@playwright/test';
import { expect, Locator, Page } from '@playwright/test';

import { STOCK_MOVEMENT_URL } from '@/constants/applicationUrls';
import BasePageModel from '@/pages/BasePageModel';
Expand Down Expand Up @@ -112,25 +112,29 @@ class StockMovementShowPage extends BasePageModel {
await this.deleteButton.click();
}

async openReceiptsTab() {
// tab content is fetched once per page load, so when it fails to render,
// re-clicking the tab never refetches it — only a reload does
// tab content is fetched once per page load, so when it fails to render,
// re-clicking the tab never refetches it — only a reload does
private async openTab(
tab: Locator,
tabContent: { isLoaded: () => Promise<void> }
) {
let reloadOnRetry = false;
await expect(async () => {
if (reloadOnRetry) {
await this.page.reload();
}
reloadOnRetry = true;
await this.receiptTab.click();
await this.receiptListTable.isLoaded();
}).toPass({ timeout: 20000, intervals: [500, 1000, 2000] });
await tab.click();
await tabContent.isLoaded();
}).toPass({ timeout: 45000, intervals: [500, 1000, 2000] });
}

async openReceiptsTab() {
await this.openTab(this.receiptTab, this.receiptListTable);
}

async openDocumentsTab() {
await expect(async () => {
await this.documentTab.click();
await this.documentsListTable.isLoaded();
}).toPass({ timeout: 20000, intervals: [500, 1000, 2000] });
await this.openTab(this.documentTab, this.documentsListTable);
}
}

Expand Down
47 changes: 31 additions & 16 deletions src/tests/putaway/addCommentToPutaway.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { ShipmentType } from '@/constants/ShipmentType';
import { expect, test } from '@/fixtures/fixtures';
import { Product } from '@/generated/ProductCodes.generated';
import { StockMovementResponse } from '@/types';
import { cleanupPendingPutaways } from '@/utils/putawayUtils';
import RefreshCachesUtils from '@/utils/RefreshCaches';
import {
deleteReceivedShipment,
Expand All @@ -12,6 +13,7 @@ import {

test.describe('Add comment to Putaway', () => {
let STOCK_MOVEMENT: StockMovementResponse;
let PUTAWAY_ORDER_IDS: string[] = [];

test.beforeEach(
async ({
Expand All @@ -20,6 +22,7 @@ test.describe('Add comment to Putaway', () => {
productService,
receivingService,
}) => {
PUTAWAY_ORDER_IDS = [];
const supplierLocation = await supplierLocationService.getLocation();
STOCK_MOVEMENT = await stockMovementService.createInbound({
originId: supplierLocation.id,
Expand Down Expand Up @@ -57,21 +60,33 @@ test.describe('Add comment to Putaway', () => {
);

test.afterEach(
async ({
stockMovementShowPage,
stockMovementService,
navbar,
transactionListPage,
oldViewShipmentPage,
}) => {
await navbar.configurationButton.click();
await navbar.transactions.click();
await transactionListPage.table.row(1).actionsButton.click();
await transactionListPage.table.deleteButton.click();
await expect(transactionListPage.successMessage).toBeVisible();
await transactionListPage.table.row(1).actionsButton.click();
await transactionListPage.table.deleteButton.click();
await expect(transactionListPage.successMessage).toBeVisible();
async (
{
stockMovementShowPage,
stockMovementService,
navbar,
transactionListPage,
oldViewShipmentPage,
putawayService,
},
testInfo
) => {
const { allPutawaysCompleted } = await cleanupPendingPutaways({
putawayService,
putawayOrderIds: PUTAWAY_ORDER_IDS,
testInfo,
});

if (allPutawaysCompleted) {
await navbar.configurationButton.click();
await navbar.transactions.click();
await transactionListPage.table.row(1).actionsButton.click();
await transactionListPage.table.deleteButton.click();
await expect(transactionListPage.successMessage).toBeVisible();
await transactionListPage.table.row(1).actionsButton.click();
await transactionListPage.table.deleteButton.click();
await expect(transactionListPage.successMessage).toBeVisible();
}

await deleteReceivedShipment({
stockMovementShowPage,
Expand Down Expand Up @@ -115,7 +130,7 @@ test.describe('Add comment to Putaway', () => {

await test.step('Start putaway', async () => {
await createPutawayPage.table.row(0).checkbox.click();
await createPutawayPage.startPutawayButton.click();
PUTAWAY_ORDER_IDS.push(await createPutawayPage.startPutaway());
await createPutawayPage.startStep.isLoaded();
});

Expand Down
47 changes: 31 additions & 16 deletions src/tests/putaway/assertAttemptToEditCompletedPutaway.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { ShipmentType } from '@/constants/ShipmentType';
import { expect, test } from '@/fixtures/fixtures';
import { Product } from '@/generated/ProductCodes.generated';
import { StockMovementResponse } from '@/types';
import { cleanupPendingPutaways } from '@/utils/putawayUtils';
import RefreshCachesUtils from '@/utils/RefreshCaches';
import {
deleteReceivedShipment,
Expand All @@ -12,6 +13,7 @@ import {

test.describe('Assert attempt to edit completed putaway', () => {
let STOCK_MOVEMENT: StockMovementResponse;
let PUTAWAY_ORDER_IDS: string[] = [];

test.beforeEach(
async ({
Expand All @@ -20,6 +22,7 @@ test.describe('Assert attempt to edit completed putaway', () => {
productService,
receivingService,
}) => {
PUTAWAY_ORDER_IDS = [];
const supplierLocation = await supplierLocationService.getLocation();
STOCK_MOVEMENT = await stockMovementService.createInbound({
originId: supplierLocation.id,
Expand Down Expand Up @@ -57,21 +60,33 @@ test.describe('Assert attempt to edit completed putaway', () => {
);

test.afterEach(
async ({
stockMovementShowPage,
stockMovementService,
navbar,
transactionListPage,
oldViewShipmentPage,
}) => {
await navbar.configurationButton.click();
await navbar.transactions.click();
await transactionListPage.table.row(1).actionsButton.click();
await transactionListPage.table.deleteButton.click();
await expect(transactionListPage.successMessage).toBeVisible();
await transactionListPage.table.row(1).actionsButton.click();
await transactionListPage.table.deleteButton.click();
await expect(transactionListPage.successMessage).toBeVisible();
async (
{
stockMovementShowPage,
stockMovementService,
navbar,
transactionListPage,
oldViewShipmentPage,
putawayService,
},
testInfo
) => {
const { allPutawaysCompleted } = await cleanupPendingPutaways({
putawayService,
putawayOrderIds: PUTAWAY_ORDER_IDS,
testInfo,
});

if (allPutawaysCompleted) {
await navbar.configurationButton.click();
await navbar.transactions.click();
await transactionListPage.table.row(1).actionsButton.click();
await transactionListPage.table.deleteButton.click();
await expect(transactionListPage.successMessage).toBeVisible();
await transactionListPage.table.row(1).actionsButton.click();
await transactionListPage.table.deleteButton.click();
await expect(transactionListPage.successMessage).toBeVisible();
}

await deleteReceivedShipment({
stockMovementShowPage,
Expand Down Expand Up @@ -104,7 +119,7 @@ test.describe('Assert attempt to edit completed putaway', () => {

await test.step('Start putaway', async () => {
await createPutawayPage.table.row(0).checkbox.click();
await createPutawayPage.startPutawayButton.click();
PUTAWAY_ORDER_IDS.push(await createPutawayPage.startPutaway());
await createPutawayPage.startStep.isLoaded();
});

Expand Down
Loading
Loading