diff --git a/.changeset/thick-tools-flash.md b/.changeset/thick-tools-flash.md new file mode 100644 index 00000000000..c3489278504 --- /dev/null +++ b/.changeset/thick-tools-flash.md @@ -0,0 +1,5 @@ +--- +"saleor-dashboard": minor +--- + +Adding e2e tests for attributes diff --git a/playwright/data/commonLocators.ts b/playwright/data/commonLocators.ts index 6dc0871f58e..315a573423e 100644 --- a/playwright/data/commonLocators.ts +++ b/playwright/data/commonLocators.ts @@ -5,4 +5,6 @@ export const LOCATORS = { infoBanner: "[data-test-type=\"info\"]", dataGridTable: "[data-testid=\"data-grid-canvas\"]", deleteButton: "[data-test-id=\"button-bar-delete\"]", + bulkDeleteButton: "[data-test-id=\"bulk-delete-button\"]", + loader: "[data-test-id=\"loader\"]", }; diff --git a/playwright/data/e2eTestData.ts b/playwright/data/e2eTestData.ts index 7489efa4ec6..7f253dbc895 100644 --- a/playwright/data/e2eTestData.ts +++ b/playwright/data/e2eTestData.ts @@ -1,3 +1,53 @@ +export const ATTRIBUTES = { + productAttributeWithValuesToBeUpdated: { + id: "QXR0cmlidXRlOjczMg==", + name: "e2e product attribute to be updated", + valueToBeDeleted: "e2e product attribute value to be deleted", + valueToBeUpdated: "e2e product attribute value to be updated", + }, + contentAttributeWithValuesToBeUpdated: { + id: "QXR0cmlidXRlOjczMw==", + name: "e2e content attribute to be updated", + valueToBeDeleted: "e2e content attribute value to be deleted", + valueToBeUpdated: "e2e content attribute value to be updated", + }, + productAttributeToBeDeleted: { + id: "QXR0cmlidXRlOjczNA==", + name: "e2e product attribute to be deleted", + }, + contentAttributeToBeDeleted: { + id: "QXR0cmlidXRlOjczNQ==", + name: "e2e content attribute to be deleted", + }, + attributesToBeBulkDeleted: { + names: [ + "e2e attribute to be bulk deleted 1/3", + "e2e attribute to be bulk deleted 2/3", + "e2e attribute to be bulk deleted 3/3", + ], + }, + attributesToBeUpdated: + [ {name: + "e2e product attribute to be updated 1", + id: "QXR0cmlidXRlOjc0MA==" + }, + { name: "e2e content attribute to be updated 2", id: "QXR0cmlidXRlOjczOQ==" } + ], + attributeTypesWithAbilityToAddValues:{names: + ["DROPDOWN","MULTISELECT","SWATCH",] + }, + attributeTypesWithoutAbilityToAddValues: { + names: + ["FILE", + "NUMERIC", + "RICH_TEXT", + "PLAIN_TEXT", + "BOOLEAN", + "DATE", + "DATE_TIME",] + }, + attributeReferencesEntities: {names: ["PAGE", "PRODUCT", "PRODUCT_VARIANT"]}, + } export const VOUCHERS = { vouchers: { voucherToBeEditedWithFreeShipping: { diff --git a/playwright/pages/attributesPage.ts b/playwright/pages/attributesPage.ts index ab235ff8274..8468b4c64c4 100644 --- a/playwright/pages/attributesPage.ts +++ b/playwright/pages/attributesPage.ts @@ -1,11 +1,20 @@ import { BasePage } from "@pages/basePage"; import { AddValueDialog } from "@pages/dialogs/addValueDialog"; +import { DeleteAttributesInBulkDialog } from "@pages/dialogs/deleteAttributesInBulkDialog"; +import { DeleteAttributeDialog } from "@pages/dialogs/deleteAttributeDialog"; +import { DeleteAttributeValueDialog } from "@pages/dialogs/deleteAttributeValueDialog"; +import { EditAttributeValueDialog } from "@pages/dialogs/editAttributeValueDialog"; + + import type { Page } from "@playwright/test"; +import { URL_LIST } from "@data/url"; -export class AttributesPage { - readonly page: Page; +export class AttributesPage extends BasePage { readonly addValueDialog: AddValueDialog; - readonly basePage: BasePage; + readonly deleteAttributesInBulkDialog: DeleteAttributesInBulkDialog; + readonly deleteAttributeDialog: DeleteAttributeDialog; + readonly deleteAttributeValueDialog: DeleteAttributeValueDialog; + readonly editAttributeValueDialog: EditAttributeValueDialog; constructor( page: Page, @@ -25,30 +34,125 @@ export class AttributesPage { readonly attributeCodeInput = page .getByTestId("attribute-code-input") .locator("input"), + readonly bulkDeleteAttributesDialog = page.getByTestId("attribute-bulk-delete-dialog"), + readonly deleteSingleAttributeDialog = page.getByTestId("delete-single-attr-dialog"), + readonly dialog = page.getByRole("dialog"), + readonly deleteAttributeValueButton = page.getByTestId("delete-attribute-value-button"), + readonly attributeValueRows = page.getByTestId("attributes-rows"), + readonly attributeValueName = page.getByTestId("attribute-value-name"), + readonly deleteAttrValueDialog = page.getByTestId("delete-attribute-value-dialog"), + readonly editAttrValueDialog = page.getByTestId("edit-attribute-value-dialog"), + readonly attrValuesSection = page.getByTestId("attribute-values-section"), + readonly attrEntityTypeSelect = page.locator(`[id="mui-component-select-entityType"]`), + readonly attrVisibleInStorefrontSwitch = page.locator(`[name = "visibleInStorefront"]`), + readonly metadataSectionAccordionButton = page.getByTestId("metadata-item").getByTestId("expand"), + readonly metadataAddFieldButton = page.getByTestId("metadata-item").getByTestId("add-field"), + readonly metadataKeyInput = page.getByTestId("metadata-key-input").first(), + readonly metadataValueInput = page.getByTestId("metadata-value-input").first(), ) { - this.page = page; + super(page) this.addValueDialog = new AddValueDialog(page); - this.basePage = new BasePage(page); + this.deleteAttributesInBulkDialog = new DeleteAttributesInBulkDialog(page); + this.deleteAttributeDialog = new DeleteAttributeDialog(page); + this.deleteAttributeValueDialog = new DeleteAttributeValueDialog(page); + this.editAttributeValueDialog = new EditAttributeValueDialog(page); + + } + + async gotoListView() { + await this.page.goto(URL_LIST.attributes); + await this.createAttributeButton.waitFor({ + state: "visible", + timeout: 10000, + }); + await this.gridCanvas.waitFor({ + state: "visible", + timeout: 10000, + }); + } +async assertSearchResultsVisibility(searchText:string) { + // Wait for all elements containing searchText to appear + const elements = await this.page.$$('text=' + searchText); + + // Wait for all elements not containing searchText to disappear + const otherElements = await this.page.$$('text!=' + searchText); + + // Assert that all elements containing searchText are visible + for (const element of elements) { + await element.waitForElementState('visible'); + } + + // Assert that all elements not containing searchText are not visible + for (const element of otherElements) { + await element.waitForElementState('hidden'); + } +} + async searchForAttribute(attributeName: string) { + await this.searchInputListView.click(); + } + async gotoExistingAttributePage(attributeId: string, attributeName: string) { + const existingAttributeUrl = `${URL_LIST.attributes}${attributeId}`; + await console.log( + `Navigates to existing attribute page: ${existingAttributeUrl}`, + ); + await this.page.goto(existingAttributeUrl); + await this.pageHeader.getByText(attributeName).waitFor({ + state: "visible", + timeout: 30000, + }); + } async clickCreateAttributeButton() { await this.createAttributeButton.click(); } + async fillAttributeSlug(attributeSlug: string) { + await this.attributeCodeInput.fill(attributeSlug); + } async clickValueRequiredCheckbox() { await this.valueRequiredCheckbox.click(); } async clickSaveButton() { await this.saveButton.click(); } - async selectAttributeType(attributeType = "DROPDOWN") { + async selectAttributeType(attributeType: string) { + await this.page.getByTestId(attributeType).click(); + } + async selectAttributeInputType(attributeType: string) { await this.attributeSelect.click(); await this.page.getByTestId(`select-field-option-${attributeType}`).click(); } async clickAssignAttributeValueButton() { await this.assignAttributeValueButton.click(); } - + async clickDeleteAttrValueButton(attrName: string) { + await this.attributeValueRows.filter({ hasText: attrName }).locator(this.deleteAttributeValueButton).click(); + } + async clickOnExistingAttrValue(attrName: string) { + await this.attributeValueRows.filter({ hasText: attrName }).click(); + await this.editAttrValueDialog.waitFor({ + state: "visible", + timeout: 30000, + }); + } async typeAttributeDefaultLabel(attributeDefaultLabel: string) { await this.attributeDefaultLabelInput.fill(attributeDefaultLabel); } + async selectAttributeEntityType(entityType: string) { + await this.attrEntityTypeSelect.click(); + await this.page.getByTestId(`select-field-option-${entityType}`).click(); + } + async changeAttributeVisibility() { + await this.attrVisibleInStorefrontSwitch.click(); + } + async expandMetadataSection() { + await this.metadataSectionAccordionButton.first().click(); + } + async addMetadataField() { + await this.metadataAddFieldButton.click(); + } + async fillMetadataFields(key: string, value: string) { + await this.metadataKeyInput.fill(key); + await this.metadataValueInput.fill(value); + } } diff --git a/playwright/pages/basePage.ts b/playwright/pages/basePage.ts index 83f63ed0209..da9e6eb15e1 100644 --- a/playwright/pages/basePage.ts +++ b/playwright/pages/basePage.ts @@ -20,6 +20,7 @@ export class BasePage { readonly errorBanner = page.locator(LOCATORS.errorBanner), readonly saveButton = page.locator(LOCATORS.saveButton), readonly infoBanner = page.locator(LOCATORS.infoBanner), + readonly loader = page.locator(LOCATORS.loader), readonly previousPagePaginationButton = page.getByTestId( "button-pagination-back", ), @@ -28,6 +29,7 @@ export class BasePage { "button-pagination-next", ), readonly searchInputListView = page.getByTestId("search-input"), + readonly emptyDataGridListView = page.getByTestId("empty-data-grid-text"), ) { this.page = page; } @@ -46,6 +48,7 @@ export class BasePage { async clickFilterButton() { await this.filterButton.click(); } + async clickBulkDeleteGridRowsButton() { await this.bulkDeleteGridRowsButton.click(); } @@ -105,6 +108,12 @@ export class BasePage { "No error banner should be visible", ).not.toBeVisible(); } + async resizeWindow(w: number, h: number) { + await this.page.setViewportSize({width: w, height: h,}); + } + async clickOnSpecificPositionOnPage(x:number, y: number){ + await this.page.mouse.click(x,y) +} async getRandomInt(max: number) { return Math.floor(Math.random() * (max + 1)); diff --git a/playwright/pages/configurationPage.ts b/playwright/pages/configurationPage.ts index 902d35c0526..5e6511565d1 100644 --- a/playwright/pages/configurationPage.ts +++ b/playwright/pages/configurationPage.ts @@ -36,7 +36,6 @@ export class ConfigurationPage { ) { this.page = page; } - async openShippingMethods() { await this.shippingMethodsButton.click(); } diff --git a/playwright/pages/dialogs/addValueDialog.ts b/playwright/pages/dialogs/addValueDialog.ts index 2cee65d2dcb..cfe20a741e8 100644 --- a/playwright/pages/dialogs/addValueDialog.ts +++ b/playwright/pages/dialogs/addValueDialog.ts @@ -11,7 +11,7 @@ export class AddValueDialog { this.page = page; } - async typeAndSaveAttributeValue(value = "XXL") { + async typeAndSaveAttributeValue(value: string = "XXL") { await this.nameInput.fill(value); await this.saveButton.click(); } diff --git a/playwright/pages/dialogs/deleteAttributeDialog.ts b/playwright/pages/dialogs/deleteAttributeDialog.ts new file mode 100644 index 00000000000..b7a408206b3 --- /dev/null +++ b/playwright/pages/dialogs/deleteAttributeDialog.ts @@ -0,0 +1,18 @@ +import type { Page } from "@playwright/test"; + +export class DeleteAttributeDialog { + readonly page: Page; + + constructor( + page: Page, + readonly deleteButton = page.getByTestId("submit"), + readonly cancelButton = page.getByTestId("back"), + readonly deleteAttributesDialogText = page.getByTestId("delete-single-attr-dialog-text"), + ) { + this.page = page; + } + + async deleteAttribute() { + await this.deleteButton.click(); + } +} diff --git a/playwright/pages/dialogs/deleteAttributeValueDialog.ts b/playwright/pages/dialogs/deleteAttributeValueDialog.ts new file mode 100644 index 00000000000..c8842782468 --- /dev/null +++ b/playwright/pages/dialogs/deleteAttributeValueDialog.ts @@ -0,0 +1,18 @@ +import type { Page } from "@playwright/test"; + +export class DeleteAttributeValueDialog { + readonly page: Page; + + constructor( + page: Page, + readonly deleteButton = page.getByTestId("submit"), + readonly cancelButton = page.getByTestId("back"), + readonly deleteAttributesDialogText = page.getByTestId("delete-attribute-value-dialog-text"), + ) { + this.page = page; + } + + async deleteAttributeValue() { + await this.deleteButton.click(); + } +} diff --git a/playwright/pages/dialogs/deleteAttributesInBulkDialog.ts b/playwright/pages/dialogs/deleteAttributesInBulkDialog.ts new file mode 100644 index 00000000000..29f907a0fe7 --- /dev/null +++ b/playwright/pages/dialogs/deleteAttributesInBulkDialog.ts @@ -0,0 +1,18 @@ +import type { Page } from "@playwright/test"; + +export class DeleteAttributesInBulkDialog { + readonly page: Page; + + constructor( + page: Page, + readonly deleteButton = page.getByTestId("submit"), + readonly cancelButton = page.getByTestId("back"), + readonly deleteAttributesDialogText = page.getByTestId("delete-attr-from-list-dialog-text"), + ) { + this.page = page; + } + + async deleteSelectedAttributes() { + await this.deleteButton.click(); + } +} diff --git a/playwright/pages/dialogs/editAttributeValueDialog.ts b/playwright/pages/dialogs/editAttributeValueDialog.ts new file mode 100644 index 00000000000..c0726689397 --- /dev/null +++ b/playwright/pages/dialogs/editAttributeValueDialog.ts @@ -0,0 +1,24 @@ +import type { Page } from "@playwright/test"; +import faker from "faker"; + + +export class EditAttributeValueDialog { + readonly page: Page; + + constructor( + page: Page, + readonly saveButton = page.getByTestId("submit"), + readonly cancelButton = page.getByTestId("back"), + readonly valueInput = page.getByTestId("value-name").locator("input"), + ) { + this.page = page; + } + + async provideNewAttributeValue(newValue: string = faker.lorem.word(5)) { + await this.valueInput.clear(); + await this.valueInput.fill(newValue); + } + async saveNewAttributeValue() { + await this.saveButton.click(); + } +} diff --git a/playwright/tests/attributes.spec.ts b/playwright/tests/attributes.spec.ts index ee6234f8b0d..8940997c2cb 100644 --- a/playwright/tests/attributes.spec.ts +++ b/playwright/tests/attributes.spec.ts @@ -1,48 +1,157 @@ -import { URL_LIST } from "@data/url"; import { AttributesPage } from "@pages/attributesPage"; +import { ATTRIBUTES } from "@data/e2eTestData"; import { ConfigurationPage } from "@pages/configurationPage"; import { expect, test } from "@playwright/test"; +import faker from "faker"; test.use({ storageState: "./playwright/.auth/admin.json" }); -test("TC: SALEOR_34 User should be able to create Dropdown attribute, required, Product attribute @e2e", async ({ - page, -}) => { - const configurationPage = new ConfigurationPage(page); - const attributesPage = new AttributesPage(page); - const attributeDefaultLabel = `attribute default label - ${await attributesPage.basePage.getRandomInt( - 1000000, - )}`; - - await page.goto(URL_LIST.configuration); - - await configurationPage.openAttributes(); - await attributesPage.clickCreateAttributeButton(); - await attributesPage.typeAttributeDefaultLabel(attributeDefaultLabel); - await attributesPage.selectAttributeType("DROPDOWN"); - await attributesPage.clickAssignAttributeValueButton(); - await attributesPage.addValueDialog.typeAndSaveAttributeValue(); - await attributesPage.clickSaveButton(); - await attributesPage.basePage.expectSuccessBanner(); - await expect(await attributesPage.attributesRows.count()).toEqual(1); - await expect(attributesPage.valueRequiredCheckbox).toBeEnabled(); - expect(await attributesPage.valueRequiredCheckbox.isChecked()).toBeTruthy(); +let attributesPage: AttributesPage; +let configurationPage: ConfigurationPage; + +test.beforeEach(({ page }) => { + attributesPage = new AttributesPage(page); + configurationPage = new ConfigurationPage(page); }); -test("TC: SALEOR_35 User should be able to create Plain Text attribute, not required, Product attribute @e2e", async ({ - page, -}) => { - const attributesPage = new AttributesPage(page); - const attributeDefaultLabel = `attribute default label - ${await attributesPage.basePage.getRandomInt( - 1000000, - )}`; - - await page.goto(URL_LIST.addAttributes); - - await attributesPage.typeAttributeDefaultLabel(attributeDefaultLabel); - await attributesPage.selectAttributeType("PLAIN_TEXT"); - await attributesPage.clickValueRequiredCheckbox(); + +const attributeClasses = ["PRODUCT_TYPE", "PAGE_TYPE"]; +for (const attr of attributeClasses) { +const SALEOR_124_uuid = faker.datatype.uuid(); +for (const type of ATTRIBUTES.attributeTypesWithAbilityToAddValues.names) { + const uniqueSlug = `${attr}-${type}-${SALEOR_124_uuid}`; + test(`TC: SALEOR_124 User should be able to create ${attr} ${type} attribute with ability to add values, required, public @e2e @attributes`, async () => { + await configurationPage.gotoConfigurationView(); + await configurationPage.openAttributes(); + await attributesPage.clickCreateAttributeButton(); + await attributesPage.selectAttributeType(attr); + await attributesPage.typeAttributeDefaultLabel(`${attr} - ${type}`); + await attributesPage.fillAttributeSlug(uniqueSlug); + await attributesPage.selectAttributeInputType(type); + await expect(attributesPage.attrValuesSection).toBeVisible(); + await attributesPage.clickAssignAttributeValueButton(); + await attributesPage.addValueDialog.typeAndSaveAttributeValue(); + await attributesPage.clickSaveButton(); + await attributesPage.expectSuccessBanner(); + await expect(await attributesPage.attributesRows.count()).toEqual(1); + await expect(attributesPage.valueRequiredCheckbox).toBeEnabled(); + await expect(attributesPage.attrVisibleInStorefrontSwitch).toBeChecked(); + await expect(attributesPage.valueRequiredCheckbox).toBeChecked(); + }); +} +} + +const SALEOR_125_uuid = faker.datatype.uuid(); +for (const attr of attributeClasses) { + for (const type of ATTRIBUTES.attributeTypesWithoutAbilityToAddValues.names) { + const uniqueSlug = `${attr}-${type}-${SALEOR_125_uuid}`; + test(`TC: SALEOR_125 User should be able to create ${attr} ${type} attribute without ability to add values, NOT required, private @e2e @attributes`, async () => { + await configurationPage.gotoConfigurationView(); + await configurationPage.openAttributes(); + await attributesPage.clickCreateAttributeButton(); + await attributesPage.selectAttributeType(attr); + await attributesPage.typeAttributeDefaultLabel(`${attr} - ${type}`); + await attributesPage.fillAttributeSlug(uniqueSlug); + await attributesPage.selectAttributeInputType(type); + await expect(attributesPage.attrValuesSection).not.toBeVisible(); + await expect(attributesPage.assignAttributeValueButton).not.toBeVisible(); + await attributesPage.clickValueRequiredCheckbox(); + await attributesPage.changeAttributeVisibility(); + await attributesPage.clickSaveButton(); + await attributesPage.expectSuccessBanner(); + await expect(attributesPage.valueRequiredCheckbox).toBeEnabled(); + await expect(attributesPage.attrVisibleInStorefrontSwitch).not.toBeChecked(); + await expect(attributesPage.valueRequiredCheckbox).not.toBeChecked(); + }); + } +} + +const SALEOR_126_uuid = faker.datatype.uuid(); +for (const attr of attributeClasses) { + for (const entity of ATTRIBUTES.attributeReferencesEntities.names) + { + const uniqueSlug = `${attr}-${entity}-${SALEOR_126_uuid}`; + test(`TC: SALEOR_126 User should be able to create ${attr} References attribute for ${entity}, NOT required, public @e2e @attributes`, async () => { + await configurationPage.gotoConfigurationView(); + await configurationPage.openAttributes(); + await attributesPage.clickCreateAttributeButton(); + await attributesPage.selectAttributeType(attr); + await attributesPage.typeAttributeDefaultLabel(`${attr} - REFERENCES for ${entity}`); + await attributesPage.fillAttributeSlug(uniqueSlug); + await attributesPage.selectAttributeInputType("REFERENCE"); + await attributesPage.selectAttributeEntityType(entity); + await attributesPage.clickValueRequiredCheckbox(); + await attributesPage.clickSaveButton(); + await attributesPage.expectSuccessBanner(); + await expect(attributesPage.valueRequiredCheckbox).toBeEnabled(); + await expect(attributesPage.attrVisibleInStorefrontSwitch).toBeChecked(); + await expect(attributesPage.valueRequiredCheckbox).not.toBeChecked(); + }); + } +} + +const productAttrWithValues = { id: ATTRIBUTES.productAttributeWithValuesToBeUpdated.id, name: ATTRIBUTES.productAttributeWithValuesToBeUpdated.name, valueToBeDeleted: ATTRIBUTES.productAttributeWithValuesToBeUpdated.valueToBeDeleted, valueToBeUpdated: ATTRIBUTES.productAttributeWithValuesToBeUpdated.valueToBeUpdated } +const contentAttrWithValues = { id: ATTRIBUTES.contentAttributeWithValuesToBeUpdated.id, name: ATTRIBUTES.contentAttributeWithValuesToBeUpdated.name, valueToBeDeleted: ATTRIBUTES.contentAttributeWithValuesToBeUpdated.valueToBeDeleted, valueToBeUpdated: ATTRIBUTES.contentAttributeWithValuesToBeUpdated.valueToBeUpdated } +const attributesWithValuesToBeUpdated = [productAttrWithValues, contentAttrWithValues]; +for (const attribute of attributesWithValuesToBeUpdated) { + test(`TC: SALEOR_127 User should be able to update attribute values in existing ${attribute.name} attribute @e2e @attributes`, async () => { + await attributesPage.gotoExistingAttributePage(attribute.id, attribute.name) + await attributesPage.clickDeleteAttrValueButton(attribute.valueToBeDeleted); + await expect(attributesPage.dialog).toBeVisible(); + await attributesPage.deleteAttributeValueDialog.deleteAttributeValue(); + await attributesPage.clickOnExistingAttrValue(attribute.valueToBeUpdated); + await expect(attributesPage.editAttrValueDialog).toBeVisible(); + await attributesPage.editAttributeValueDialog.provideNewAttributeValue(`updated value for ${attribute.name}`); + await attributesPage.editAttributeValueDialog.saveNewAttributeValue(); + await attributesPage.clickAssignAttributeValueButton(); + await attributesPage.addValueDialog.typeAndSaveAttributeValue(`new value for ${attribute.name}`); + await attributesPage.clickSaveButton(); + await attributesPage.expectSuccessBanner(); + await expect(attributesPage.attrValuesSection).not.toContainText(attribute.valueToBeDeleted); + await expect(attributesPage.attrValuesSection).toContainText(`updated value for ${attribute.name}`); + await expect(attributesPage.attrValuesSection).toContainText(`new value for ${attribute.name}`); +});} + +for (const attr of ATTRIBUTES.attributesToBeUpdated) { + test(`TC: SALEOR_128 User should be able to edit existing ${attr.name} attribute @e2e @attributes`, async () => { + await attributesPage.gotoExistingAttributePage(attr.id, attr.name); + await attributesPage.attributeDefaultLabelInput.clear(); + await attributesPage.typeAttributeDefaultLabel(`updated ${attr.name}`); + await attributesPage.expandMetadataSection(); + await attributesPage.metadataAddFieldButton.click(); + await attributesPage.fillMetadataFields("new key", "new value"); await attributesPage.clickSaveButton(); - await attributesPage.basePage.expectSuccessBanner(); - await expect(attributesPage.valueRequiredCheckbox).toBeEnabled(); - expect(await attributesPage.valueRequiredCheckbox.isChecked()).toBeFalsy(); -}); + await attributesPage.expectSuccessBanner(); + await expect(attributesPage.attributeSelect.getByRole('button')).toHaveAttribute('aria-disabled', 'true'); + await expect(attributesPage.metadataKeyInput).toHaveValue("new key"); + await expect(attributesPage.metadataValueInput).toHaveValue("new value"); + await expect(attributesPage.attributeDefaultLabelInput).toHaveValue(`updated ${attr.name}`); +});}; + +const productAttribute = { id: ATTRIBUTES.productAttributeToBeDeleted.id, name: ATTRIBUTES.productAttributeToBeDeleted.name } +const contentAttribute = { id: ATTRIBUTES.contentAttributeToBeDeleted.id, name: ATTRIBUTES.contentAttributeToBeDeleted.name } +const attributesToBeDeleted = [productAttribute, contentAttribute]; +for (const attribute of attributesToBeDeleted) { + test(`TC: SALEOR_129 Delete a single ${attribute.name} @e2e @attributes`, async () => { + await attributesPage.gotoExistingAttributePage(attribute.id, attribute.name); + await attributesPage.clickDeleteButton(); + await attributesPage.dialog.waitFor({ + state: "visible", + timeout: 10000, + }); + await attributesPage.deleteAttributeDialog.deleteAttribute(); + await attributesPage.waitForGrid() + await expect(attributesPage.gridCanvas).not.toContainText(attribute.name); + }); +} + +test("TC: SALEOR_130 Bulk delete attributes @e2e @attributes", async () => { + await attributesPage.gotoListView(); + await attributesPage.resizeWindow(800,600); + await attributesPage.typeInSearchOnListView("e2e attribute to be bulk deleted") + await expect(attributesPage.loader).toBeVisible({ timeout: 30000}); + await expect(attributesPage.loader).not.toBeVisible({ timeout: 30000}); + await attributesPage.clickOnSpecificPositionOnPage(60, 136) + await attributesPage.clickBulkDeleteGridRowsButton(); + await attributesPage.deleteAttributesInBulkDialog.deleteSelectedAttributes(); + await expect(attributesPage.emptyDataGridListView).toBeVisible(); + }); diff --git a/src/attributes/components/AttributeBulkDeleteDialog/AttributeBulkDeleteDialog.tsx b/src/attributes/components/AttributeBulkDeleteDialog/AttributeBulkDeleteDialog.tsx index fe79c026af4..4964a5648a6 100644 --- a/src/attributes/components/AttributeBulkDeleteDialog/AttributeBulkDeleteDialog.tsx +++ b/src/attributes/components/AttributeBulkDeleteDialog/AttributeBulkDeleteDialog.tsx @@ -33,11 +33,13 @@ const AttributeBulkDeleteDialog: React.FC = ({ description: "dialog title", })} variant="delete" + data-test-id="attribute-bulk-delete-dialog" > = ({ id="h1rPPg" defaultMessage="Are you sure you want to delete {attributeName}?" description="dialog content" + data-test-id="delete-single-attr-dialog-text" values={{ attributeName: {name}, }} diff --git a/src/attributes/components/AttributeValueDeleteDialog/AttributeValueDeleteDialog.tsx b/src/attributes/components/AttributeValueDeleteDialog/AttributeValueDeleteDialog.tsx index 6f3b5faef4c..e7eb28c3784 100644 --- a/src/attributes/components/AttributeValueDeleteDialog/AttributeValueDeleteDialog.tsx +++ b/src/attributes/components/AttributeValueDeleteDialog/AttributeValueDeleteDialog.tsx @@ -41,6 +41,7 @@ const AttributeValueDeleteDialog: React.FC = ({ {useName ? ( = ({ const formErrors = getFormErrors(["name"], errors); return ( - + {attributeValue === null ? ( = ({ const numberOfColumns = isSwatch ? 5 : 4; return ( - + = ({ /> )} - + {value?.slug ?? } @@ -197,6 +200,7 @@ const AttributeValues: React.FC = ({ diff --git a/src/components/Datagrid/Datagrid.tsx b/src/components/Datagrid/Datagrid.tsx index 2becb09a843..79b3531dac0 100644 --- a/src/components/Datagrid/Datagrid.tsx +++ b/src/components/Datagrid/Datagrid.tsx @@ -453,7 +453,12 @@ export const Datagrid: React.FC = ({ if (loading) { return ( - + ); diff --git a/src/components/Metadata/MetadataCardTable.tsx b/src/components/Metadata/MetadataCardTable.tsx index df49a1a7555..9b3af2b4c0c 100644 --- a/src/components/Metadata/MetadataCardTable.tsx +++ b/src/components/Metadata/MetadataCardTable.tsx @@ -83,6 +83,7 @@ export const MetadataCardTable = ({ ) : ( ) : (