Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
846c3b5
Fix failing e2e due to invalid assertions
witoszekdev Jan 28, 2026
9a60d87
fix: add missing test-id in datagrid
witoszekdev Jan 28, 2026
f6b1de2
fix: add new fragment for updating order lines, to re-fetch stale data
witoszekdev Jan 28, 2026
7aa927f
fix: add missing fetch for changed shipping method, separate props
witoszekdev Jan 28, 2026
64fb341
Merge branch 'main' into fix-e2e-notifiactions-check
witoszekdev Jan 29, 2026
070ea13
revert changes
witoszekdev Jan 29, 2026
3c7a98b
fix: gift card assertions
witoszekdev Jan 29, 2026
c72fa4a
add command to delete auth
witoszekdev Jan 29, 2026
b3577f7
fix: add missing data-test-id
witoszekdev Jan 29, 2026
8c26119
fix: change assertion for channel availability text
witoszekdev Jan 29, 2026
9aec35f
fix: remove assertion for copied to clipboard
witoszekdev Jan 29, 2026
a0d4de4
add changeset
witoszekdev Jan 30, 2026
756060c
add missing waitfor, add scroll into view
witoszekdev Jan 30, 2026
34eb8a1
fix test await usage, add more waitfor
witoszekdev Jan 30, 2026
a1bb071
update assertion for permissions, add waitfor url in shipping methods
witoszekdev Jan 30, 2026
8631732
add watfor for new metadata edition, update assertions and selectors
witoszekdev Jan 30, 2026
24c8dfb
add skill
witoszekdev Jan 30, 2026
56680a1
move skill to plugin
witoszekdev Jan 30, 2026
3ee63df
fix taxes page test failing
witoszekdev Jan 30, 2026
401baa8
Delete claude plugin - for separate PR
witoszekdev Jan 30, 2026
6a88f22
fix: metadata save failure - check mutation
witoszekdev Jan 30, 2026
ca16b6a
Merge branch 'main' into fix-e2e-notifiactions-check
witoszekdev Jan 30, 2026
44da815
delete skill - moved to separate PR
witoszekdev Jan 30, 2026
73a7cea
fix locator taxes
witoszekdev Jan 30, 2026
bc08fc7
Merge branch 'main' into fix-e2e-notifiactions-check
witoszekdev Feb 3, 2026
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
5 changes: 5 additions & 0 deletions .changeset/tidy-deserts-fix.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"saleor-dashboard": patch
---

Fixed data-fetching in Order details page. Now when line items are changed, order summary (total, shipping price, etc.) are updated. Previously these values were not updated and displayed stale data.
5 changes: 5 additions & 0 deletions .changeset/wicked-olives-take.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"saleor-dashboard": patch
---

Fixed e2e tests: updated assertions for notifications, datagrid, gift cards
6 changes: 6 additions & 0 deletions docs/running-tests.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,9 @@ You are ready to run Playwright commands like:
```shell
pnpm run e2e
```

Note that if you have changed `BASE_URL` to run tests on different environment you will need to clear login data which is stored locally in `playwright/.auth` folder:

```
pnpm run e2e:clean-auth
```
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
"dev": "vite --host",
"dev:no-strict": "cross-env VITE_DISABLE_STRICT_MODE=true vite --host",
"e2e": "pnpm exec playwright test --grep \"#e2e\"",
"e2e:clean-auth": "rm -rf ./playwright/.auth",
"e2e:ui": "pnpm exec playwright test --ui",
"extract-messages": "formatjs extract 'src/**/*.{ts,tsx}' --ignore '**/*.d.ts' --out-file locale/defaultMessages.json --format scripts/formatter.cjs",
"fetch-local-schema": "node scripts/fetch-local-schema.cjs",
Expand Down
3 changes: 3 additions & 0 deletions playwright/data/copy.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
export const AVAILABILITY = {
/** Used for product pages with new AvailabilityCard */
in1Of: "In 1 of",
/** Used for voucher/discount pages with old ChannelsAvailabilityCard */
in1OutOf: "In 1 out of",
};
8 changes: 8 additions & 0 deletions playwright/pages/pageElements/metadataSeoPage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,12 +56,20 @@ export class MetadataSeoPage {
privateMetaName = privateMetaDataName,
privateMetaValue = privateMetaDataValue,
) {
// Wait for public metadata section to be visible before expanding
await this.publicMetaSection.waitFor({ state: "visible", timeout: 2_000 });
await this.clickMetadataSectionExpandButton();
await this.addMetaButton.waitFor({ state: "visible", timeout: 1_000 });
await this.addMetaButton.click();
await this.metaDataNameInput.waitFor({ state: "visible", timeout: 1_000 });
await this.metaDataNameInput.fill(metaName);
await this.metadataValueField.fill(metaValue);
// Wait for private metadata section before expanding
await this.privateMetaSection.waitFor({ state: "visible", timeout: 1_000 });
await this.clickPrivateMetadataSectionExpandButton();
await this.addPrivateMetaButton.waitFor({ state: "visible", timeout: 1_000 });
await this.addPrivateMetaButton.click();
await this.privateMetaDataNameInput.waitFor({ state: "visible", timeout: 1_000 });
await this.privateMetaDataNameInput.fill(privateMetaName);
await this.privateMetadataValueField.fill(privateMetaValue);
}
Expand Down
1 change: 1 addition & 0 deletions playwright/pages/pageElements/rightSideDetailsSection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,7 @@ export class RightSideDetailsPage extends BasePage {
}

async selectCustomer(customer = "allison.freeman@example.com") {
await this.selectCustomerOption.locator(`text=${customer}`).waitFor({ state: "visible" });
await this.selectCustomerOption.locator(`text=${customer}`).click();
await this.waitForDOMToFullyLoad();
}
Expand Down
9 changes: 8 additions & 1 deletion playwright/pages/permissionGroupDetailsPage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@ export class PermissionGroupDetailsPage extends BasePage {
return this.page.locator(`[data-test-id="permission-checkbox-${permission}"] input`);
}

permissionGroupCheckboxWrapper(permission: string) {
return this.page.locator(`[data-test-id="permission-checkbox-${permission}"]`);
}

async fillPermissionGroupNameInput(name: string) {
await this.permissionGroupNameInput.fill(name);
}
Expand All @@ -51,7 +55,10 @@ export class PermissionGroupDetailsPage extends BasePage {
}

async selectPermissionGroup(permission: string) {
await this.permissionGroupListItem.filter({ hasText: permission }).first().click();
const checkboxWrapper = this.permissionGroupCheckboxWrapper(permission);

await checkboxWrapper.waitFor({ state: "visible" });
await checkboxWrapper.click();
}

async clickAssignMembersButton() {
Expand Down
4 changes: 1 addition & 3 deletions playwright/pages/productPage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,7 @@ export class ProductPage extends BasePage {
constructor(
page: Page,
readonly productsNames = page.getByTestId("name"),
readonly productAvailableInChannelsText = page.getByTestId(
"product-available-in-channels-text",
),
readonly productAvailableInChannelsText = page.getByTestId("availability-card"),
readonly createProductButton = page.getByTestId("add-product"),
readonly cogShowMoreButtonButton = page.getByTestId("show-more-button"),
readonly exportButton = page.getByTestId("export"),
Expand Down
26 changes: 18 additions & 8 deletions playwright/pages/taxesPage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { URL_LIST } from "@data/url";
import { AddCountriesDialog } from "@dialogs/addCountriesDialog";
import { MetadataSeoPage } from "@pageElements/metadataSeoPage";
import { BasePage } from "@pages/basePage";
import type { Page } from "@playwright/test";
import { expect, type Page } from "@playwright/test";

export class TaxesPage extends BasePage {
readonly page: Page;
Expand Down Expand Up @@ -38,9 +38,7 @@ export class TaxesPage extends BasePage {
readonly searchTaxClassInput = page.getByTestId("search-tax-class-input").locator("input"),
readonly searchedCountryRows = page.getByTestId("country-rows"),

readonly searchTaxCountryInput = page
.getByTestId("search-tax-countries-input")
.locator("input"),
readonly searchTaxCountryInput = page.getByRole("textbox", { name: "Search tax countries" }),
readonly taxClassNameInput = page.getByTestId("class-name-input").locator("input"),
readonly noTaxRateInput = page.getByTestId("No Taxes").locator("input"),
readonly defaultRateInput = page.getByTestId("Country default rate").locator("input"),
Expand Down Expand Up @@ -105,17 +103,29 @@ export class TaxesPage extends BasePage {

async typeSearchedTaxCountryName(taxCountryName: string) {
await this.searchTaxCountryInput.fill(taxCountryName);
// Verify the input value is set before checking filtered results
await expect(this.searchTaxCountryInput).toHaveValue(taxCountryName);
// Wait for search results to filter
await this.searchedCountryRows
.filter({ hasText: taxCountryName })
.waitFor({ state: "visible", timeout: 10000 });
}

async typeTaxRateInSearchedCountryRow(taxCountryName: string, taxRateValue: string) {
await this.searchedCountryRows
.filter({ hasText: taxCountryName })
.locator("input")
.fill(taxRateValue);
const countryRow = this.searchedCountryRows.filter({ hasText: taxCountryName });

await countryRow.waitFor({ state: "visible", timeout: 10000 });

const input = countryRow.locator("input");

await input.waitFor({ state: "visible", timeout: 5000 });
await input.fill(taxRateValue);
}

async clickCreateClassButton() {
await this.createClassButton.click();
// Wait for the new tax class form to load
await this.taxClassNameInput.waitFor({ state: "visible" });
}

async selectTaxCalculationMethod(method: string) {
Expand Down
2 changes: 1 addition & 1 deletion playwright/tests/attributes.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ for (const attr of attributeClasses) {
await attributesPage.addValueDialog.typeAndSaveAttributeValue();
await attributesPage.clickSaveButton();
await attributesPage.expectSuccessBanner();
await expect(await attributesPage.attributesRows.count()).toEqual(1);
await expect(attributesPage.attributesRows).toHaveCount(1);
await attributesPage.valueRequiredCheckbox.waitFor({
state: "visible",
timeout: 10000,
Expand Down
5 changes: 2 additions & 3 deletions playwright/tests/customers.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -222,12 +222,11 @@ test("TC: SALEOR_207 Issue a new gift card for the customer #e2e #customer", asy
await customersPage.issueGiftCardDialog.typeCustomTag(faker.lorem.word());
await customersPage.issueGiftCardDialog.typeNote(faker.lorem.sentences(3));
await customersPage.issueGiftCardDialog.clickIssueButton();
await customersPage.expectSuccessBanner();
await giftCardsPage.expectSuccessBanner({ message: "Gift card created" });
await expect(giftCardsPage.issueGiftCardDialog.cardCode).toBeVisible();

await giftCardsPage.issueGiftCardDialog.clickCopyCodeButton();
await giftCardsPage.expectSuccessBanner();
await giftCardsPage.expectSuccessBanner({ message: "Copied to clipboard" });
await giftCardsPage.issueGiftCardDialog.clickOkButton();
await giftCardsPage.expectElementIsHidden(giftCardsPage.giftCardDialog);
await giftCardsPage.expectSuccessBanner({ message: "Successfully created gift card" });
});
10 changes: 8 additions & 2 deletions playwright/tests/discounts.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -344,9 +344,15 @@ for (const promotion of promotionsWithRules) {
await discounts.gotoExistingDiscount(promotion.id);
await discounts.ruleSection.waitFor({
state: "visible",
timeout: 50000,
timeout: 15000,
});
await discounts.clickDeleteRuleButton(`${promotion.type} rule: ${rule.name}`);
const deleteButton = discounts.existingRule
.locator(discounts.ruleLabelWithActions)
.filter({ hasText: `${promotion.type} rule: ${rule.name}` })
.locator(discounts.deleteRuleButton);

await deleteButton.waitFor({ state: "visible", timeout: 10000 });
await deleteButton.click();
await expect(discounts.deleteRuleModal).toBeVisible({ timeout: 10000 });
await discounts.deleteRuleDialog.clickConfirmDeleteButton();
await discounts.expectSuccessBanner();
Expand Down
6 changes: 3 additions & 3 deletions playwright/tests/giftCards.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,14 @@ test("TC: SALEOR_105 Issue gift card #e2e #gift", async () => {
await giftCardsPage.issueGiftCardDialog.clickRequiresActivationCheckbox();
await giftCardsPage.issueGiftCardDialog.clickIssueButton();
await expect(giftCardsPage.issueGiftCardDialog.cardCode).toBeVisible();
await giftCardsPage.expectSuccessBanner({ message: "Gift card created" });

const code = (await giftCardsPage.issueGiftCardDialog.cardCode.innerText()).slice(-4);

await giftCardsPage.issueGiftCardDialog.clickCopyCodeButton();
await giftCardsPage.expectSuccessBanner();
await giftCardsPage.expectSuccessBanner({ message: "Copied to clipboard" });
await giftCardsPage.issueGiftCardDialog.clickOkButton();
await giftCardsPage.giftCardDialog.waitFor({ state: "hidden" });
await giftCardsPage.expectSuccessBanner({ message: "Successfully created gift card" });
await giftCardsPage.gotoGiftCardsListView();
await giftCardsPage.gridCanvas
.getByText(`Code ending with ${code}`)
Expand All @@ -43,13 +43,13 @@ test("TC: SALEOR_106 Issue gift card with specific customer and expiry date #e2e
await giftCardsPage.issueGiftCardDialog.clickSendToCustomerCheckbox();
await giftCardsPage.issueGiftCardDialog.selectCustomer("e2e-customer to-be-activated");
await giftCardsPage.issueGiftCardDialog.clickIssueButton();
await giftCardsPage.expectSuccessBanner({ message: "Gift card created" });
await expect(giftCardsPage.issueGiftCardDialog.cardCode).toBeVisible();

const fullCode = await giftCardsPage.issueGiftCardDialog.cardCode.innerText();

await giftCardsPage.issueGiftCardDialog.clickOkButton();
await giftCardsPage.giftCardDialog.waitFor({ state: "hidden" });
await giftCardsPage.expectSuccessBanner({ message: "Successfully created gift card" });
await giftCardsPage.gotoGiftCardsListView();
await giftCardsPage.searchAndFindRowIndexes(fullCode);
expect(
Expand Down
2 changes: 2 additions & 0 deletions playwright/tests/orders.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@ test("TC: SALEOR_76 Create order with transaction flow activated #e2e #order", a
await ordersPage.rightSideDetailsPage.selectCustomer();
await expect(ordersPage.addressDialog.existingAddressRadioButton).toBeVisible();
await ordersPage.addressDialog.clickConfirmButton();
await ordersPage.addShippingCarrierLink.waitFor({ state: "visible", timeout: 30000 });
await ordersPage.addShippingCarrierLink.scrollIntoViewIfNeeded();
await ordersPage.clickAddShippingCarrierButton();
await ordersPage.shippingAddressDialog.pickAndConfirmShippingMethod();
await ordersPage.clickFinalizeButton();
Expand Down
2 changes: 1 addition & 1 deletion playwright/tests/product.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ test("TC: SALEOR_46 As an admin, I should be able to update a product by uploadi
await expect(
productPage.productAvailableInChannelsText,
"Label copy shows 1 out of 7 channels ",
).toContainText(AVAILABILITY.in1OutOf);
).toContainText(AVAILABILITY.in1Of);
expect(
await productPage.productImage.count(),
"Newly added single image should be present",
Expand Down
11 changes: 9 additions & 2 deletions playwright/tests/shippingMethods.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ test("TC: SALEOR_32 Add price rate to shipping method - with excluded zip codes
await shippingRatesPage.expectSuccessBanner();
await shippingRatesPage.excludedProductsRows.waitFor({ state: "visible" });
await expect(shippingRatesPage.excludedProductsRows).toContainText("Bean Juice");
await expect(await shippingRatesPage.assignedPostalCodesRows.count()).toEqual(1);
await expect(shippingRatesPage.assignedPostalCodesRows).toHaveCount(1);
});
test("TC: SALEOR_33 Add weight rate to shipping method - with included zip codes and excluded product #shipping-method #e2e", async () => {
await shippingMethodsPage.gotoExistingShippingMethod(
Expand Down Expand Up @@ -102,10 +102,17 @@ test("TC: SALEOR_35 Delete a single shipping rate from its details page #shippin
await shippingMethodsPage.clickDeleteShippingRateButton();
await shippingMethodsPage.deleteShippingMethodDialog.clickDeleteButton();
await shippingMethodsPage.expectSuccessBanner();
await shippingMethodsPage.page.waitForURL(
url => url.pathname.includes("/shipping/") && !url.pathname.includes(shippingRateId),
{ timeout: 30000 },
);
// Wait for the rate row to disappear from the table after deletion
await expect(
shippingMethodsPage.weightBasedRatesSection.getByRole("link", { name: weightBasedRate }),
).toBeHidden({ timeout: 30000 });
await expect(shippingMethodsPage.weightBasedRatesSection).toContainText(
"No shipping rates found",
);
await expect(shippingMethodsPage.weightBasedRatesSection).not.toContainText(weightBasedRate);
});
test("TC: SALEOR_36 Delete shipping zones in bulk #shipping-method #e2e", async () => {
const shippingZone1 = SHIPPING_METHODS.shippingMethodToBeBulkDeleted1.name;
Expand Down
24 changes: 21 additions & 3 deletions playwright/tests/taxes.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,22 +47,40 @@ test("TC: SALEOR_117 Add new country and tax rates to it #taxes #e2e", async ()
await taxesPage.addCountriesDialog.checkAndSaveSingleCountry(
COUNTRIES.countryToBeAddedInTaxes.name,
);
expect(await taxesPage.countriesListRow.first()).toHaveText(
await expect(taxesPage.countriesListRow.first()).toHaveText(
COUNTRIES.countryToBeAddedInTaxes.name,
);
await taxesPage.typeAllTaxRatesForCountry("23", "0", "16", "7", "21", "19");
await taxesPage.clickSaveButton();
await taxesPage.expectSuccessBanner();
});
test("TC: SALEOR_118 Add new class with metadata and set tax rate for single country #taxes #e2e", async () => {
test("TC: SALEOR_118 Add new class with metadata and set tax rate for single country #taxes #e2e", async ({
page,
}) => {
await taxesPage.gotoChannelsTabUrl();
await taxesPage.clickTaxClassTab();
await taxesPage.clickCreateClassButton();
expect(await taxesPage.taxClassNameInput).toHaveValue("New tax class");
await expect(taxesPage.taxClassNameInput).toHaveValue("New tax class");
await taxesPage.typeTaxClassName("Automation test tax class");
await taxesPage.typeSearchedTaxCountryName("United States of America");
await taxesPage.typeTaxRateInSearchedCountryRow("United States of America", "20");
await taxesPage.metadataSeoPage.publicMetaSection.waitFor({ state: "attached", timeout: 10000 });
await taxesPage.metadataSeoPage.publicMetaSection.scrollIntoViewIfNeeded();
await taxesPage.metadataSeoPage.expandAndAddAllMetadata();

// Ensure save button is enabled after metadata changes
await expect(taxesPage.saveButton).toBeEnabled();

// Wait for the GraphQL mutation when save is clicked, then verify success
const responsePromise = page.waitForResponse(
resp =>
resp.url().includes("/graphql/") &&
resp.request().postDataJSON()?.operationName === "TaxClassCreate",
{ timeout: 10000 },
);

await taxesPage.clickSaveButton();
await responsePromise;

await taxesPage.expectSuccessBanner();
});
1 change: 1 addition & 0 deletions src/components/notifications/Toast.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ export const Toast = ({ id, type, title, description, action }: ToastProps) => {

return (
<Box
data-test-type={type}
Copy link

Copilot AI Feb 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The attribute name data-test-type is inconsistent with the naming convention used throughout the codebase. All other test identifiers use data-test-id. Consider using data-test-id="toast" and differentiating by the type value, or use a more specific name like data-test-id={toast-${type}} to maintain consistency with the rest of the codebase.

Copilot uses AI. Check for mistakes.
display="flex"
alignItems="flex-start"
gap={3}
Expand Down
66 changes: 66 additions & 0 deletions src/fragments/orders.ts
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,72 @@ export const fragmentOrderLine = gql`
}
`;

// Slim fragment for order line mutations - includes only data that changes
// when lines are added/updated/deleted (lines + pricing + shipping if no longer required)
export const fragmentOrderLinesUpdate = gql`
fragment OrderLinesUpdate on Order {
id
lines {
...OrderLine
}
subtotal {
gross {
...Money
}
net {
...Money
}
}
total {
gross {
...Money
}
net {
...Money
}
tax {
...Money
}
}
undiscountedTotal {
gross {
...Money
}
net {
...Money
}
}
# Shipping can change when lines are deleted/added
# (shippingMethod, shippingPrice, shippingMethodName are reset when isShippingRequired becomes false)
isShippingRequired
shippingMethod {
id
}
shippingPrice {
gross {
amount
currency
}
}
shippingMethodName
collectionPointName
# Available shipping methods can change based on order contents (weight, items)
shippingMethods {
id
name
price {
...Money
}
active
message
}
# Discounts shown in order summary
discounts {
...OrderDiscount
}
}
`;

export const fragmentOrderLineMetadata = gql`
fragment OrderLineMetadata on OrderLine {
metadata {
Expand Down
Loading
Loading