Saleor 2697 tests for updating products (#1060)

* updating products

* updating products

* test for updating
This commit is contained in:
Karolina Rakoczy 2021-04-21 10:02:48 +02:00 committed by GitHub
parent b89e3533d6
commit 4013105844
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
18 changed files with 380 additions and 48 deletions

View file

@ -7,6 +7,7 @@ export function createCategory(name, slug = name) {
}
category{
id
name
}
}
}`;

View file

@ -83,15 +83,34 @@ export function updateChannelPriceInVariant(variantId, channelId) {
} `;
return cy.sendRequestWithQuery(mutation);
}
export function createProduct(attributeId, name, productType, category) {
export function createProduct(
attributeId,
name,
productType,
category,
collectionId,
description
) {
const collection = getValueWithDefault(
collectionId,
`collections:["${collectionId}"]`
);
const descriptionLine = getValueWithDefault(
description,
`description:"{\\"blocks\\":[{\\"type\\":\\"paragraph\\",\\"data\\":{\\"text\\":\\"${description}\\"}}]}"`
);
const mutation = `mutation{
productCreate(input:{
attributes:[{
id:"${attributeId}"
}]
name:"${name}"
slug:"${name}"
seo:{title:"${name}" description:""}
productType:"${productType}"
category:"${category}"
${collection}
${descriptionLine}
}){
product{
id
@ -177,6 +196,7 @@ export function createTypeProduct(
}
productType{
id
name
}
}
} `;

View file

@ -1,7 +1,41 @@
export function getProductDetails(productId, channelId) {
import { getValueWithDefault } from "../utils/Utils";
export function getProductDetails(productId, channelId, auth = "token") {
const privateMetadataLine = getValueWithDefault(
auth === "auth",
`privateMetadata{key value}`
);
const query = `fragment BasicProductFields on Product {
id
name
attributes{
attribute{
id
name
}
}
category{
id
name
}
collections{
id
name
}
description
seoTitle
slug
seoDescription
rating
metadata{
key
value
}
${privateMetadataLine}
productType{
id
name
}
}
fragment Price on TaxedMoney {
@ -33,5 +67,5 @@ export function getProductDetails(productId, channelId) {
availableForPurchase
}
}`;
return cy.sendRequestWithQuery(query, "token");
return cy.sendRequestWithQuery(query, auth);
}

View file

@ -13,5 +13,6 @@ export const PRODUCT_DETAILS = {
descriptionInput: "[data-test-id='description']",
ratingInput: "[name='rating']",
skuInput: "[name='sku']",
collectionRemoveButtons: '[data-test-id="collectionRemove"]',
variantRow: "[data-test-id='product-variant-row']"
};

View file

@ -4,5 +4,6 @@ export const BUTTON_SELECTORS = {
confirm: '[data-test="button-bar-confirm"]',
goBackButton: "[data-test-id='app-header-back-button']",
checkbox: "[type='checkbox']",
selectOption: "[data-test*='select-option']"
selectOption: "[data-test*='select-option']",
deleteButton: '[data-test="button-bar-delete"]'
};

View file

@ -21,7 +21,7 @@ import { isProductVisibleInSearchResult } from "../utils/storeFront/storeFrontPr
describe("Collections", () => {
const startsWith = "CyCollections-";
const name = `${startsWith}${faker.random.number()}`;
const name = `${startsWith}${faker.datatype.number()}`;
let attribute;
let productType;
@ -35,6 +35,7 @@ describe("Collections", () => {
productsUtils.deleteProductsStartsWith(startsWith);
deleteCollectionsStartsWith(startsWith);
deleteShippingStartsWith(startsWith);
channelsUtils.deleteChannelsStartsWith(startsWith);
channelsUtils
.getDefaultChannel()

View file

@ -9,7 +9,7 @@ import {
fillUpPriceList,
priceInputLists
} from "../../steps/catalog/products/priceList";
import { fillUpCommonFieldsForProductType } from "../../steps/catalog/products/productSteps";
import { fillUpCommonFieldsForAllProductTypes } from "../../steps/catalog/products/productSteps";
import { selectChannelInDetailsPages } from "../../steps/channelsSteps";
import { urlList } from "../../url/urlList";
import {
@ -69,7 +69,7 @@ describe("Create product", () => {
productOrganization: { productType: randomName },
attribute
};
fillUpCommonFieldsForProductType(productData).then(
fillUpCommonFieldsForAllProductTypes(productData).then(
productOrgResp => (productData.productOrganization = productOrgResp)
);
cy.addAliasToGraphRequest("ProductDetails");
@ -96,7 +96,7 @@ describe("Create product", () => {
productOrganization: { productType: randomName },
attribute
};
fillUpCommonFieldsForProductType(productData).then(
fillUpCommonFieldsForAllProductTypes(productData).then(
productOrgResp => (productData.productOrganization = productOrgResp)
);
selectChannelInDetailsPages();

View file

@ -0,0 +1,142 @@
import faker from "faker";
import { createCollection } from "../../apiRequests/Collections";
import { getProductDetails } from "../../apiRequests/storeFront/ProductDetails";
import { PRODUCT_DETAILS } from "../../elements/catalog/products/product-details";
import { BUTTON_SELECTORS } from "../../elements/shared/button-selectors";
import { metadataForms } from "../../steps/catalog/metadataSteps";
import { fillUpCommonFieldsForAllProductTypes } from "../../steps/catalog/products/productSteps";
import { productDetailsUrl } from "../../url/urlList";
import { getDefaultChannel } from "../../utils/channelsUtils";
import { deleteCollectionsStartsWith } from "../../utils/collectionsUtils";
import { expectCorrectProductInformation } from "../../utils/products/checkProductInfo";
import {
createCategory,
createProductInChannel,
createTypeAttributeAndCategoryForProduct,
deleteProductsStartsWith
} from "../../utils/products/productsUtils";
describe("Update products", () => {
const startsWith = "Cy-";
const name = `${startsWith}${faker.random.number()}`;
const description = faker.lorem.sentences(2);
let defaultChannel;
let collection;
let product;
let attribute;
before(() => {
cy.clearSessionData().loginUserViaRequest();
deleteProductsStartsWith(startsWith);
deleteCollectionsStartsWith(startsWith);
getDefaultChannel()
.then(channel => {
defaultChannel = channel;
createCollection(name);
})
.then(collectionResp => {
collection = collectionResp;
createTypeAttributeAndCategoryForProduct(name);
})
.then(({ attribute: attributeResp, category, productType }) => {
attribute = attributeResp;
createProductInChannel({
attributeId: attribute.id,
categoryId: category.id,
productTypeId: productType.id,
channelId: defaultChannel.id,
name,
collectionId: collection.id,
description
});
})
.then(({ product: productResp }) => {
product = productResp;
});
});
it("Should update product", () => {
const updatedName = `${startsWith}${faker.random.number()}`;
let updatedCategory;
let updatedCollection;
createCategory(updatedName)
.then(categoryResp => {
updatedCategory = categoryResp;
createCollection(updatedName);
})
.then(collectionResp => {
updatedCollection = collectionResp;
const productData = {
generalInfo: {
name: updatedName,
description: faker.lorem.sentence(),
rating: 3
},
seo: {
slug: updatedName,
title: "newTitle",
description: "New description."
},
metadata: {
private: {
metadataForm: metadataForms.private,
name: "newPrivate",
value: "value1"
},
public: {
metadataForm: metadataForms.public,
name: "newPublic",
value: "value2"
}
},
productOrganization: {
category: updatedCategory.name,
collection: updatedCollection.name
}
};
cy.visit(productDetailsUrl(product.id));
cy.get(PRODUCT_DETAILS.collectionRemoveButtons).click();
fillUpCommonFieldsForAllProductTypes(productData, false);
cy.addAliasToGraphRequest("UpdatePrivateMetadata");
cy.addAliasToGraphRequest("UpdateMetadata");
cy.addAliasToGraphRequest("ProductUpdate");
cy.get(BUTTON_SELECTORS.confirm).click();
cy.get(PRODUCT_DETAILS.confirmationMsg)
.should("be.visible")
.then(() => {
cy.wait("@ProductUpdate");
cy.wait("@UpdateMetadata");
cy.wait("@UpdatePrivateMetadata");
productData.productOrganization.productType = name;
productData.attribute = attribute;
cy.loginUserViaRequest("token");
})
.then(() => {
getProductDetails(product.id, defaultChannel.slug, "auth").its(
"body.data.product"
);
})
.then(resp => {
expectCorrectProductInformation(resp, productData);
});
});
});
it("should delete product", () => {
cy.visit(productDetailsUrl(product.id));
cy.addAliasToGraphRequest("ProductDelete");
cy.get(BUTTON_SELECTORS.deleteButton)
.click()
.get(BUTTON_SELECTORS.submit)
.click();
cy.wait("@ProductDelete");
cy.loginUserViaRequest("token")
.then(() => {
getProductDetails(product.id, defaultChannel.slug).its("body.data");
})
.then(
productResp =>
expect(productResp.product, "Check if product exist").to.be.null
);
});
});

View file

@ -6,7 +6,8 @@ export const metadataForms = {
};
export function addMetadataField({ metadataForm, name, value }) {
cy.get(metadataForm)
return cy
.get(metadataForm)
.find(METADATA_FORM.addFieldButton)
.click()
.get(metadataForm)

View file

@ -40,28 +40,48 @@ function updateProductMenageInChannel(productUrl, menageSelector) {
.click()
.wait("@ProductChannelListingUpdate");
}
export function fillUpCommonFieldsForProductType({
export function fillUpCommonFieldsForAllProductTypes(
{ generalInfo, seo, metadata, productOrganization },
createMode = true
) {
return fillUpAllCommonFieldsInCreateAndUpdate({ generalInfo, seo, metadata })
.then(() => {
if (createMode) {
fillUpProductOrganization(productOrganization);
} else {
fillUpCollectionAndCategory({
category: productOrganization.category,
collection: productOrganization.collection
});
}
})
.then(productOrgResp => productOrgResp);
}
export function fillUpAllCommonFieldsInCreateAndUpdate({
generalInfo,
seo,
metadata,
productOrganization
metadata
}) {
fillUpProductGeneralInfo(generalInfo);
editSeoSettings(seo);
addMetadataField(metadata.public);
addMetadataField(metadata.private);
return fillUpProductOrganization(productOrganization).then(
productOrgResp => productOrgResp
);
return fillUpProductGeneralInfo(generalInfo)
.then(() => {
editSeoSettings(seo);
})
.then(() => {
addMetadataField(metadata.public);
})
.then(() => {
addMetadataField(metadata.private);
});
}
export function fillUpProductGeneralInfo({ name, description, rating }) {
cy.get(PRODUCT_DETAILS.productNameInput)
return cy
.get(PRODUCT_DETAILS.productNameInput)
.click()
.type(name)
.clearAndType(name)
.get(PRODUCT_DETAILS.descriptionInput)
.type(description)
.clearAndType(description)
.get(PRODUCT_DETAILS.ratingInput)
.type(rating);
.clearAndType(rating);
}
export function fillUpProductOrganization({
productType,
@ -72,8 +92,17 @@ export function fillUpProductOrganization({
return fillAutocompleteSelect(PRODUCT_DETAILS.productTypeInput, productType)
.then(selected => {
organization.productType = selected;
fillAutocompleteSelect(PRODUCT_DETAILS.categoryInput, category);
fillUpCollectionAndCategory({ category, collection });
})
.then(collectionAndCategoryResp => {
organization.category = collectionAndCategoryResp.category;
organization.collection = collectionAndCategoryResp.collection;
return organization;
});
}
export function fillUpCollectionAndCategory({ category, collection }) {
const organization = {};
return fillAutocompleteSelect(PRODUCT_DETAILS.categoryInput, category)
.then(selected => {
organization.category = selected;
fillAutocompleteSelect(PRODUCT_DETAILS.collectionInput, collection);

View file

@ -3,9 +3,9 @@ export function editSeoSettings({ slug, title, description }) {
cy.get(SEO_FORM.editSeoSettings)
.click()
.get(SEO_FORM.slugInput)
.type(slug)
.clearAndType(slug)
.get(SEO_FORM.titleInput)
.type(title)
.clearAndType(title)
.get(SEO_FORM.descriptionInput)
.type(description, { delay: 0 });
.clearAndType(description, { delay: 0 });
}

View file

@ -6,7 +6,7 @@ export function fillAutocompleteSelect(selectSelector, option) {
.get(BUTTON_SELECTORS.selectOption)
.should("be.visible");
if (option) {
cy.get(selectSelector).type(option);
cy.get(selectSelector).clearAndType(option);
cy.contains(BUTTON_SELECTORS.selectOption, option).click();
cy.wrap(option).as("option");
} else {

View file

@ -1,3 +1,9 @@
Cypress.Commands.add("getTextFromElement", element =>
cy.get(element).invoke("text")
);
Cypress.Commands.add("clearAndType", { prevSubject: true }, (subject, text) => {
cy.wrap(subject)
.clear()
.type(text);
});

View file

@ -27,37 +27,58 @@ export function expectCorrectProductVariantInformation(
);
}
function expectCorrectGeneralInformation(productResp, generalInfo) {
softExpect(productResp.name).to.be.eq(generalInfo.name);
softExpect(productResp.description).includes(generalInfo.description);
softExpect(productResp.rating).to.be.eq(generalInfo.rating);
softExpect(productResp.name, "Check product name").to.be.eq(generalInfo.name);
softExpect(productResp.description, "Check product description").includes(
generalInfo.description
);
softExpect(productResp.rating, "Check product rate").to.be.eq(
generalInfo.rating
);
}
function expectCorrectSeoInfo(productResp, seo) {
softExpect(productResp.slug).to.be.eq(seo.slug);
softExpect(productResp.seoTitle).to.be.eq(seo.title);
softExpect(productResp.seoDescription).to.be.eq(seo.description);
softExpect(productResp.slug, "Check product slug").to.be.eq(seo.slug);
softExpect(productResp.seoTitle, "Check product seo title").to.be.eq(
seo.title
);
softExpect(
productResp.seoDescription,
"Check product seo description"
).to.be.eq(seo.description);
}
function expectCorrectMetadataInfo(metadataResp, expectedMetadata) {
softExpect(
expect(metadataResp).to.have.length(1),
softExpect(metadataResp[0].key).to.be.eq(expectedMetadata.name),
softExpect(metadataResp[0].value).to.be.eq(expectedMetadata.value)
expect(metadataResp, "Check metadata fields length").to.have.length(1),
softExpect(metadataResp[0].key, "Check product metadata key").to.be.eq(
expectedMetadata.name
),
softExpect(metadataResp[0].value, "Check product metadata value").to.be.eq(
expectedMetadata.value
)
);
}
function expectCorrectProductOrgInfo(productResp, productOrganization) {
softExpect(productResp.productType.name).to.be.eq(
softExpect(productResp.productType.name, "Check product type name").to.be.eq(
productOrganization.productType
);
softExpect(productResp.category.name).to.be.eq(productOrganization.category);
softExpect(productResp.category.name, "Check category name").to.be.eq(
productOrganization.category
);
softExpect(
expect(productResp.collections).to.have.length(1),
softExpect(productResp.collections[0].name).to.be.eq(
productOrganization.collection
)
expect(
productResp.collections,
"Check length of assigned collections"
).to.have.length(1),
softExpect(
productResp.collections[0].name,
"Check collection name"
).to.be.eq(productOrganization.collection)
);
}
function expectCorrectAttribute(attributes, attribute) {
softExpect(
expect(attributes).to.have.length(1),
softExpect(attributes[0].attribute.name).to.be.eq(attribute.name)
softExpect(attributes[0].attribute.name, "Check attribute name").to.be.eq(
attribute.name
)
);
}

View file

@ -13,11 +13,20 @@ export function createProductInChannel({
price = 1,
isPublished = true,
isAvailableForPurchase = true,
visibleInListings = true
visibleInListings = true,
collectionId = null,
description = null
}) {
let product;
let variants;
return createProduct(attributeId, name, productTypeId, categoryId)
return createProduct(
attributeId,
name,
productTypeId,
categoryId,
collectionId,
description
)
.then(productResp => {
product = productResp;
productRequest.updateChannelInProduct({
@ -81,9 +90,23 @@ export function createCategory(name) {
.createCategory(name)
.its("body.data.categoryCreate.category");
}
export function createProduct(attributeId, name, productTypeId, categoryId) {
export function createProduct(
attributeId,
name,
productTypeId,
categoryId,
collectionId,
description
) {
return productRequest
.createProduct(attributeId, name, productTypeId, categoryId)
.createProduct(
attributeId,
name,
productTypeId,
categoryId,
collectionId,
description
)
.its("body.data.productCreate.product");
}
export function updateProduct(productId, input) {

View file

@ -84,6 +84,7 @@ export interface MultiAutocompleteSelectFieldProps
helperText?: string;
label?: string;
disabled?: boolean;
testId?: string;
fetchChoices?: (value: string) => void;
onChange: (event: React.ChangeEvent<any>) => void;
}
@ -107,6 +108,7 @@ const MultiAutocompleteSelectFieldComponent: React.FC<MultiAutocompleteSelectFie
placeholder,
value,
disabled,
testId,
fetchChoices,
onChange,
onFetchMore,
@ -212,6 +214,7 @@ const MultiAutocompleteSelectFieldComponent: React.FC<MultiAutocompleteSelectFie
</Typography>
<IconButton
data-test-id={testId ? `${testId}Remove` : "remove"}
className={classes.chipClose}
disabled={value.disabled}
onClick={() => handleSelect(value.value)}
@ -229,6 +232,7 @@ const MultiAutocompleteSelectFieldComponent: React.FC<MultiAutocompleteSelectFie
const MultiAutocompleteSelectField: React.FC<MultiAutocompleteSelectFieldProps> = ({
choices,
fetchChoices,
testId,
...props
}) => {
const [query, setQuery] = React.useState("");
@ -238,6 +242,7 @@ const MultiAutocompleteSelectField: React.FC<MultiAutocompleteSelectFieldProps>
<DebounceAutocomplete debounceFn={fetchChoices}>
{debounceFn => (
<MultiAutocompleteSelectFieldComponent
testId={testId}
choices={choices}
{...props}
fetchChoices={debounceFn}

View file

@ -196,6 +196,7 @@ const ProductOrganization: React.FC<ProductOrganizationProps> = props => {
onChange={onCollectionChange}
fetchChoices={fetchCollections}
data-test="collections"
testId="collection"
{...fetchMoreCollections}
/>
</CardContent>

View file

@ -952,6 +952,7 @@ exports[`Storyshots Attributes / Attributes selected 1`] = `
</div>
<button
class="MuiButtonBase-root-id MuiIconButton-root-id MultiAutocompleteSelectField-chipClose-id"
data-test-id="remove"
tabindex="0"
type="button"
>
@ -985,6 +986,7 @@ exports[`Storyshots Attributes / Attributes selected 1`] = `
</div>
<button
class="MuiButtonBase-root-id MuiIconButton-root-id MultiAutocompleteSelectField-chipClose-id"
data-test-id="remove"
tabindex="0"
type="button"
>
@ -2490,6 +2492,7 @@ exports[`Storyshots Generics / Account Permission Groups Widget default 1`] = `
</div>
<button
class="MuiButtonBase-root-id MuiIconButton-root-id MultiAutocompleteSelectField-chipClose-id MuiIconButton-disabled-id MuiButtonBase-disabled-id"
data-test-id="remove"
disabled=""
tabindex="-1"
type="button"
@ -2524,6 +2527,7 @@ exports[`Storyshots Generics / Account Permission Groups Widget default 1`] = `
</div>
<button
class="MuiButtonBase-root-id MuiIconButton-root-id MultiAutocompleteSelectField-chipClose-id"
data-test-id="remove"
tabindex="0"
type="button"
>
@ -2625,6 +2629,7 @@ exports[`Storyshots Generics / Account Permission Groups Widget error 1`] = `
</div>
<button
class="MuiButtonBase-root-id MuiIconButton-root-id MultiAutocompleteSelectField-chipClose-id MuiIconButton-disabled-id MuiButtonBase-disabled-id"
data-test-id="remove"
disabled=""
tabindex="-1"
type="button"
@ -2659,6 +2664,7 @@ exports[`Storyshots Generics / Account Permission Groups Widget error 1`] = `
</div>
<button
class="MuiButtonBase-root-id MuiIconButton-root-id MultiAutocompleteSelectField-chipClose-id"
data-test-id="remove"
tabindex="0"
type="button"
>
@ -9432,6 +9438,7 @@ exports[`Storyshots Generics / Multiple select with autocomplete interactive 1`]
</div>
<button
class="MuiButtonBase-root-id MuiIconButton-root-id MultiAutocompleteSelectField-chipClose-id"
data-test-id="remove"
tabindex="0"
type="button"
>
@ -9547,6 +9554,7 @@ exports[`Storyshots Generics / Multiple select with autocomplete interactive wit
</div>
<button
class="MuiButtonBase-root-id MuiIconButton-root-id MultiAutocompleteSelectField-chipClose-id"
data-test-id="remove"
tabindex="0"
type="button"
>
@ -9662,6 +9670,7 @@ exports[`Storyshots Generics / Multiple select with autocomplete interactive wit
</div>
<button
class="MuiButtonBase-root-id MuiIconButton-root-id MultiAutocompleteSelectField-chipClose-id"
data-test-id="remove"
tabindex="0"
type="button"
>
@ -9777,6 +9786,7 @@ exports[`Storyshots Generics / Multiple select with autocomplete interactive wit
</div>
<button
class="MuiButtonBase-root-id MuiIconButton-root-id MultiAutocompleteSelectField-chipClose-id"
data-test-id="remove"
tabindex="0"
type="button"
>
@ -21233,6 +21243,7 @@ exports[`Storyshots Shipping zones details / Settings Card default 1`] = `
</div>
<button
class="MuiButtonBase-root-id MuiIconButton-root-id MultiAutocompleteSelectField-chipClose-id"
data-test-id="remove"
tabindex="0"
type="button"
>
@ -21338,6 +21349,7 @@ exports[`Storyshots Shipping zones details / Settings Card default 1`] = `
</div>
<button
class="MuiButtonBase-root-id MuiIconButton-root-id MultiAutocompleteSelectField-chipClose-id"
data-test-id="remove"
tabindex="0"
type="button"
>
@ -21371,6 +21383,7 @@ exports[`Storyshots Shipping zones details / Settings Card default 1`] = `
</div>
<button
class="MuiButtonBase-root-id MuiIconButton-root-id MultiAutocompleteSelectField-chipClose-id"
data-test-id="remove"
tabindex="0"
type="button"
>
@ -147987,6 +148000,7 @@ exports[`Storyshots Views / Pages / Page details default 1`] = `
</div>
<button
class="MuiButtonBase-root-id MuiIconButton-root-id MultiAutocompleteSelectField-chipClose-id"
data-test-id="remove"
tabindex="0"
type="button"
>
@ -148882,6 +148896,7 @@ exports[`Storyshots Views / Pages / Page details form errors 1`] = `
</div>
<button
class="MuiButtonBase-root-id MuiIconButton-root-id MultiAutocompleteSelectField-chipClose-id"
data-test-id="remove"
tabindex="0"
type="button"
>
@ -181078,6 +181093,7 @@ exports[`Storyshots Views / Products / Product edit form errors 1`] = `
</div>
<button
class="MuiButtonBase-root-id MuiIconButton-root-id MultiAutocompleteSelectField-chipClose-id"
data-test-id="remove"
tabindex="0"
type="button"
>
@ -182031,6 +182047,7 @@ exports[`Storyshots Views / Products / Product edit form errors 1`] = `
</div>
<button
class="MuiButtonBase-root-id MuiIconButton-root-id MultiAutocompleteSelectField-chipClose-id"
data-test-id="collectionRemove"
tabindex="0"
type="button"
>
@ -182806,6 +182823,7 @@ exports[`Storyshots Views / Products / Product edit limits reached 1`] = `
</div>
<button
class="MuiButtonBase-root-id MuiIconButton-root-id MultiAutocompleteSelectField-chipClose-id"
data-test-id="remove"
tabindex="0"
type="button"
>
@ -183800,6 +183818,7 @@ exports[`Storyshots Views / Products / Product edit limits reached 1`] = `
</div>
<button
class="MuiButtonBase-root-id MuiIconButton-root-id MultiAutocompleteSelectField-chipClose-id"
data-test-id="collectionRemove"
tabindex="0"
type="button"
>
@ -184575,6 +184594,7 @@ exports[`Storyshots Views / Products / Product edit no limits 1`] = `
</div>
<button
class="MuiButtonBase-root-id MuiIconButton-root-id MultiAutocompleteSelectField-chipClose-id"
data-test-id="remove"
tabindex="0"
type="button"
>
@ -185523,6 +185543,7 @@ exports[`Storyshots Views / Products / Product edit no limits 1`] = `
</div>
<button
class="MuiButtonBase-root-id MuiIconButton-root-id MultiAutocompleteSelectField-chipClose-id"
data-test-id="collectionRemove"
tabindex="0"
type="button"
>
@ -186995,6 +187016,7 @@ exports[`Storyshots Views / Products / Product edit no product attributes 1`] =
</div>
<button
class="MuiButtonBase-root-id MuiIconButton-root-id MultiAutocompleteSelectField-chipClose-id"
data-test-id="collectionRemove"
tabindex="0"
type="button"
>
@ -187770,6 +187792,7 @@ exports[`Storyshots Views / Products / Product edit no stock and no variants 1`]
</div>
<button
class="MuiButtonBase-root-id MuiIconButton-root-id MultiAutocompleteSelectField-chipClose-id"
data-test-id="remove"
tabindex="0"
type="button"
>
@ -188632,6 +188655,7 @@ exports[`Storyshots Views / Products / Product edit no stock and no variants 1`]
</div>
<button
class="MuiButtonBase-root-id MuiIconButton-root-id MultiAutocompleteSelectField-chipClose-id"
data-test-id="collectionRemove"
tabindex="0"
type="button"
>
@ -189407,6 +189431,7 @@ exports[`Storyshots Views / Products / Product edit no stock, no variants and no
</div>
<button
class="MuiButtonBase-root-id MuiIconButton-root-id MultiAutocompleteSelectField-chipClose-id"
data-test-id="remove"
tabindex="0"
type="button"
>
@ -190189,6 +190214,7 @@ exports[`Storyshots Views / Products / Product edit no stock, no variants and no
</div>
<button
class="MuiButtonBase-root-id MuiIconButton-root-id MultiAutocompleteSelectField-chipClose-id"
data-test-id="collectionRemove"
tabindex="0"
type="button"
>
@ -190964,6 +190990,7 @@ exports[`Storyshots Views / Products / Product edit no variants 1`] = `
</div>
<button
class="MuiButtonBase-root-id MuiIconButton-root-id MultiAutocompleteSelectField-chipClose-id"
data-test-id="remove"
tabindex="0"
type="button"
>
@ -191968,6 +191995,7 @@ exports[`Storyshots Views / Products / Product edit no variants 1`] = `
</div>
<button
class="MuiButtonBase-root-id MuiIconButton-root-id MultiAutocompleteSelectField-chipClose-id"
data-test-id="collectionRemove"
tabindex="0"
type="button"
>
@ -192743,6 +192771,7 @@ exports[`Storyshots Views / Products / Product edit when data is fully loaded 1`
</div>
<button
class="MuiButtonBase-root-id MuiIconButton-root-id MultiAutocompleteSelectField-chipClose-id"
data-test-id="remove"
tabindex="0"
type="button"
>
@ -193691,6 +193720,7 @@ exports[`Storyshots Views / Products / Product edit when data is fully loaded 1`
</div>
<button
class="MuiButtonBase-root-id MuiIconButton-root-id MultiAutocompleteSelectField-chipClose-id"
data-test-id="collectionRemove"
tabindex="0"
type="button"
>
@ -195422,6 +195452,7 @@ exports[`Storyshots Views / Products / Product edit when product has no images 1
</div>
<button
class="MuiButtonBase-root-id MuiIconButton-root-id MultiAutocompleteSelectField-chipClose-id"
data-test-id="remove"
tabindex="0"
type="button"
>
@ -196370,6 +196401,7 @@ exports[`Storyshots Views / Products / Product edit when product has no images 1
</div>
<button
class="MuiButtonBase-root-id MuiIconButton-root-id MultiAutocompleteSelectField-chipClose-id"
data-test-id="collectionRemove"
tabindex="0"
type="button"
>
@ -197145,6 +197177,7 @@ exports[`Storyshots Views / Products / Product edit when product has no variants
</div>
<button
class="MuiButtonBase-root-id MuiIconButton-root-id MultiAutocompleteSelectField-chipClose-id"
data-test-id="remove"
tabindex="0"
type="button"
>
@ -198149,6 +198182,7 @@ exports[`Storyshots Views / Products / Product edit when product has no variants
</div>
<button
class="MuiButtonBase-root-id MuiIconButton-root-id MultiAutocompleteSelectField-chipClose-id"
data-test-id="collectionRemove"
tabindex="0"
type="button"
>
@ -198924,6 +198958,7 @@ exports[`Storyshots Views / Products / Product edit with channels 1`] = `
</div>
<button
class="MuiButtonBase-root-id MuiIconButton-root-id MultiAutocompleteSelectField-chipClose-id"
data-test-id="remove"
tabindex="0"
type="button"
>
@ -199872,6 +199907,7 @@ exports[`Storyshots Views / Products / Product edit with channels 1`] = `
</div>
<button
class="MuiButtonBase-root-id MuiIconButton-root-id MultiAutocompleteSelectField-chipClose-id"
data-test-id="collectionRemove"
tabindex="0"
type="button"
>
@ -228077,6 +228113,7 @@ exports[`Storyshots Views / Shipping / Shipping zone details default 1`] = `
</div>
<button
class="MuiButtonBase-root-id MuiIconButton-root-id MultiAutocompleteSelectField-chipClose-id"
data-test-id="remove"
tabindex="0"
type="button"
>
@ -228110,6 +228147,7 @@ exports[`Storyshots Views / Shipping / Shipping zone details default 1`] = `
</div>
<button
class="MuiButtonBase-root-id MuiIconButton-root-id MultiAutocompleteSelectField-chipClose-id"
data-test-id="remove"
tabindex="0"
type="button"
>
@ -228215,6 +228253,7 @@ exports[`Storyshots Views / Shipping / Shipping zone details default 1`] = `
</div>
<button
class="MuiButtonBase-root-id MuiIconButton-root-id MultiAutocompleteSelectField-chipClose-id"
data-test-id="remove"
tabindex="0"
type="button"
>
@ -228248,6 +228287,7 @@ exports[`Storyshots Views / Shipping / Shipping zone details default 1`] = `
</div>
<button
class="MuiButtonBase-root-id MuiIconButton-root-id MultiAutocompleteSelectField-chipClose-id"
data-test-id="remove"
tabindex="0"
type="button"
>
@ -229185,6 +229225,7 @@ exports[`Storyshots Views / Shipping / Shipping zone details form errors 1`] = `
</div>
<button
class="MuiButtonBase-root-id MuiIconButton-root-id MultiAutocompleteSelectField-chipClose-id"
data-test-id="remove"
tabindex="0"
type="button"
>
@ -229218,6 +229259,7 @@ exports[`Storyshots Views / Shipping / Shipping zone details form errors 1`] = `
</div>
<button
class="MuiButtonBase-root-id MuiIconButton-root-id MultiAutocompleteSelectField-chipClose-id"
data-test-id="remove"
tabindex="0"
type="button"
>
@ -229323,6 +229365,7 @@ exports[`Storyshots Views / Shipping / Shipping zone details form errors 1`] = `
</div>
<button
class="MuiButtonBase-root-id MuiIconButton-root-id MultiAutocompleteSelectField-chipClose-id"
data-test-id="remove"
tabindex="0"
type="button"
>
@ -229356,6 +229399,7 @@ exports[`Storyshots Views / Shipping / Shipping zone details form errors 1`] = `
</div>
<button
class="MuiButtonBase-root-id MuiIconButton-root-id MultiAutocompleteSelectField-chipClose-id"
data-test-id="remove"
tabindex="0"
type="button"
>
@ -234193,6 +234237,7 @@ exports[`Storyshots Views / Staff / Staff member details default 1`] = `
</div>
<button
class="MuiButtonBase-root-id MuiIconButton-root-id MultiAutocompleteSelectField-chipClose-id MuiIconButton-disabled-id MuiButtonBase-disabled-id"
data-test-id="remove"
disabled=""
tabindex="-1"
type="button"
@ -234227,6 +234272,7 @@ exports[`Storyshots Views / Staff / Staff member details default 1`] = `
</div>
<button
class="MuiButtonBase-root-id MuiIconButton-root-id MultiAutocompleteSelectField-chipClose-id"
data-test-id="remove"
tabindex="0"
type="button"
>