Remove enzyme and react-test-renderer (#2788)

This commit is contained in:
Krzysztof Żuraw 2022-12-07 11:20:09 +01:00 committed by GitHub
parent 8634aa4542
commit 422feec2b7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 12082 additions and 6055 deletions

View file

@ -18,6 +18,14 @@
"react-hooks", "react-hooks",
"react-refresh" "react-refresh"
], ],
"overrides": [
{
"files": ["src/**/*.test.*"],
"rules": {
"react-refresh/only-export-components": "off"
}
}
],
"rules": { "rules": {
"react-refresh/only-export-components": "warn", "react-refresh/only-export-components": "warn",
"@typescript-eslint/adjacent-overload-signatures": "error", "@typescript-eslint/adjacent-overload-signatures": "error",

17698
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -113,12 +113,15 @@
"@release-it/bumper": "^2.0.0", "@release-it/bumper": "^2.0.0",
"@saleor/app-sdk": "~0.23.0", "@saleor/app-sdk": "~0.23.0",
"@sentry/webpack-plugin": "^1.14.0", "@sentry/webpack-plugin": "^1.14.0",
"@storybook/react": "^5.1.9",
"@testing-library/jest-dom": "^5.16.5",
"@testing-library/react": "^12.1.5", "@testing-library/react": "^12.1.5",
"@testing-library/react-hooks": "^8.0.1", "@testing-library/react-hooks": "^8.0.1",
"@testing-library/user-event": "^14.4.3",
"@types/apollo-upload-client": "^17.0.0", "@types/apollo-upload-client": "^17.0.0",
"@types/color-convert": "^2.0.0", "@types/color-convert": "^2.0.0",
"@types/enzyme": "^3.10.8",
"@types/fuzzaldrin": "^2.1.2", "@types/fuzzaldrin": "^2.1.2",
"@types/jest": "^26.0.14",
"@types/jscodeshift": "^0.11.3", "@types/jscodeshift": "^0.11.3",
"@types/lodash-es": "^4.17.3", "@types/lodash-es": "^4.17.3",
"@types/pollyjs__adapter-node-http": "^2.0.1", "@types/pollyjs__adapter-node-http": "^2.0.1",
@ -131,13 +134,11 @@
"@types/react-router-dom": "^4.3.4", "@types/react-router-dom": "^4.3.4",
"@types/react-sortable-hoc": "^0.7.1", "@types/react-sortable-hoc": "^0.7.1",
"@types/react-sortable-tree": "^0.3.15", "@types/react-sortable-tree": "^0.3.15",
"@types/react-test-renderer": "^16.9.5",
"@types/semver-compare": "^1.0.1", "@types/semver-compare": "^1.0.1",
"@types/url-join": "^4.0.0", "@types/url-join": "^4.0.0",
"@types/webappsec-credential-management": "^0.5.1", "@types/webappsec-credential-management": "^0.5.1",
"@typescript-eslint/eslint-plugin": "^5.41.0", "@typescript-eslint/eslint-plugin": "^5.41.0",
"@typescript-eslint/parser": "^5.41.0", "@typescript-eslint/parser": "^5.41.0",
"@wojtekmaj/enzyme-adapter-react-17": "^0.6.7",
"babel-core": "^7.0.0-bridge.0", "babel-core": "^7.0.0-bridge.0",
"babel-jest": "^23.6.0", "babel-jest": "^23.6.0",
"babel-loader": "^8.0.6", "babel-loader": "^8.0.6",
@ -145,20 +146,22 @@
"core-js": "^3.7.0", "core-js": "^3.7.0",
"cross-env": "^6.0.3", "cross-env": "^6.0.3",
"dotenv": "^10.0.0", "dotenv": "^10.0.0",
"enzyme": "^3.11.0",
"enzyme-to-json": "^3.6.1",
"esbuild-loader": "^2.18.0", "esbuild-loader": "^2.18.0",
"file-loader": "^5.0.2", "file-loader": "^5.0.2",
"fork-ts-checker-webpack-plugin": "^3.1.1", "fork-ts-checker-webpack-plugin": "^3.1.1",
"graphql-request": "^3.7.0", "graphql-request": "^3.7.0",
"identity-obj-proxy": "^3.0.0", "identity-obj-proxy": "^3.0.0",
"jest": "^26.6.3",
"jest-canvas-mock": "^2.4.0",
"jest-file": "^1.0.0",
"jest-localstorage-mock": "^2.4.3",
"jscodeshift": "^0.13.0", "jscodeshift": "^0.13.0",
"lint-staged": "^10.5.1", "lint-staged": "^10.5.1",
"mocha-junit-reporter": "^2.0.2", "mocha-junit-reporter": "^2.0.2",
"mochawesome": "^7.0.1", "mochawesome": "^7.0.1",
"mochawesome-merge": "^4.2.1", "mochawesome-merge": "^4.2.1",
"mochawesome-report-generator": "^6.0.1", "mochawesome-report-generator": "^6.0.1",
"react-test-renderer": "^16.12.0", "prettier": "^1.19.1",
"regenerator-runtime": "^0.11.1", "regenerator-runtime": "^0.11.1",
"register-service-worker": "^1.7.2", "register-service-worker": "^1.7.2",
"release-it": "^14.5.0", "release-it": "^14.5.0",
@ -213,7 +216,7 @@
}, },
"jest": { "jest": {
"resetMocks": false, "resetMocks": false,
"setupFiles": [ "setupFilesAfterEnv": [
"jest-canvas-mock", "jest-canvas-mock",
"jest-localstorage-mock", "jest-localstorage-mock",
"<rootDir>/testUtils/setup.ts" "<rootDir>/testUtils/setup.ts"
@ -288,7 +291,8 @@
"cy:run:critical:parallel": "cypress run --record --env grepTags=@critical --parallel --tag Critical", "cy:run:critical:parallel": "cypress run --record --env grepTags=@critical --parallel --tag Critical",
"cy:run:allEnv:parallel": "cypress run --record --env grepTags=@allEnv --parallel", "cy:run:allEnv:parallel": "cypress run --record --env grepTags=@allEnv --parallel",
"cy:run:stable:parallel": "cypress run --record --env grepTags=@critical --parallel --tag Stable", "cy:run:stable:parallel": "cypress run --record --env grepTags=@critical --parallel --tag Stable",
"test": "TZ=UTC NODE_ENV=test jest src/", "test": "jest src/",
"test:watch": "jest --watch src/",
"lint": "npx eslint \"src/**/*.@(tsx|ts|jsx|js)\" --fix ; npx prettier --check \"src/**/*.@(tsx|ts|jsx|js)\" --write", "lint": "npx eslint \"src/**/*.@(tsx|ts|jsx|js)\" --fix ; npx prettier --check \"src/**/*.@(tsx|ts|jsx|js)\" --write",
"postbuild": "node scripts/removeSourcemaps.js", "postbuild": "node scripts/removeSourcemaps.js",
"postinstall": "node scripts/patchReactVirtualized.js", "postinstall": "node scripts/patchReactVirtualized.js",

View file

@ -1,6 +1,6 @@
import { ThemeProvider } from "@saleor/macaw-ui"; import { ThemeProvider } from "@saleor/macaw-ui";
import { render, screen } from "@testing-library/react";
import React from "react"; import React from "react";
import renderer, { ReactTestRendererJSON } from "react-test-renderer";
import { TimezoneProvider } from "../Timezone"; import { TimezoneProvider } from "../Timezone";
import Date from "./Date"; import Date from "./Date";
@ -8,50 +8,60 @@ import Date from "./Date";
const testDate = "2018-04-07"; const testDate = "2018-04-07";
const expectedDate = "Apr 7, 2018"; const expectedDate = "Apr 7, 2018";
test("Render plain date with timezone GMT-11", () => { describe("Date", () => {
const date = renderer.create( it("Render plain date with timezone GMT-11", () => {
<ThemeProvider> // Arrange & Act
<TimezoneProvider value="Pacific/Midway"> render(
<Date date={testDate} plain /> <ThemeProvider>
</TimezoneProvider> <TimezoneProvider value="Pacific/Midway">
</ThemeProvider>, <Date date={testDate} plain />
); </TimezoneProvider>
expect(date.toJSON()).toEqual(expectedDate); </ThemeProvider>,
}); );
// Assert
expect(screen.queryByText(expectedDate)).toBeInTheDocument();
});
test("Render plain date with timezone GMT+13", () => { it("Render plain date with timezone GMT+13", () => {
const date = renderer.create( // Arrange & Act
<ThemeProvider> render(
<TimezoneProvider value="Pacific/Tongatapu"> <ThemeProvider>
<Date date={testDate} plain /> <TimezoneProvider value="Pacific/Tongatapu">
</TimezoneProvider> <Date date={testDate} plain />
</ThemeProvider>, </TimezoneProvider>
); </ThemeProvider>,
expect(date.toJSON()).toEqual(expectedDate); );
}); // Assert
expect(screen.queryByText(expectedDate)).toBeInTheDocument();
});
test("Render humanized date with timezone GMT-11", () => { it("Render humanized date with timezone GMT-11", () => {
const date = renderer.create( // Arrange & Act
<ThemeProvider> render(
<TimezoneProvider value="Pacific/Midway"> <ThemeProvider>
<Date date={testDate} /> <TimezoneProvider value="Pacific/Midway">
</TimezoneProvider> <Date date={testDate} />
</ThemeProvider>, </TimezoneProvider>
); </ThemeProvider>,
expect((date.toJSON() as ReactTestRendererJSON).props.dateTime).toEqual( );
testDate, // Assert
); expect(screen.queryByTestId<HTMLTimeElement>("dateTime").dateTime).toEqual(
}); testDate,
);
});
test("Render humanized date with timezone GMT+13", () => { it("Render humanized date with timezone GMT+13", () => {
const date = renderer.create( // Arrange & Act
<ThemeProvider> render(
<TimezoneProvider value="Pacific/Tongatapu"> <ThemeProvider>
<Date date={testDate} /> <TimezoneProvider value="Pacific/Tongatapu">
</TimezoneProvider> <Date date={testDate} />
</ThemeProvider>, </TimezoneProvider>
); </ThemeProvider>,
expect((date.toJSON() as ReactTestRendererJSON).props.dateTime).toEqual( );
testDate, // Assert
); expect(screen.queryByTestId<HTMLTimeElement>("dateTime").dateTime).toEqual(
testDate,
);
});
}); });

View file

@ -28,7 +28,7 @@ export const Date: React.FC<DateProps> = ({ date, plain }) => {
localizeDate(date) localizeDate(date)
) : ( ) : (
<Tooltip title={localizeDate(date)}> <Tooltip title={localizeDate(date)}>
<time dateTime={date}> <time dateTime={date} data-test-id="dateTime">
{getHumanized(date, locale, currentDate)} {getHumanized(date, locale, currentDate)}
</time> </time>
</Tooltip> </Tooltip>

View file

@ -1,17 +1,13 @@
import useForm from "@saleor/hooks/useForm"; import useForm from "@saleor/hooks/useForm";
import Wrapper from "@test/wrapper"; import Wrapper from "@test/wrapper";
import Adapter from "@wojtekmaj/enzyme-adapter-react-17"; import { render, screen } from "@testing-library/react";
import { configure, mount } from "enzyme"; import userEvent from "@testing-library/user-event";
import React from "react"; import React from "react";
import { props } from "./fixtures"; import { props } from "./fixtures";
import Metadata from "./Metadata"; import Metadata from "./Metadata";
configure({ adapter: new Adapter() }); const Component = () => {
const expandButton = 'data-test-id="expand"';
const Component: React.FC = () => {
const { change, data } = useForm(props.data, jest.fn()); const { change, data } = useForm(props.data, jest.fn());
return ( return (
@ -21,140 +17,89 @@ const Component: React.FC = () => {
); );
}; };
const getFirstExpandIcon = () => screen.getAllByTestId("expand")[0];
describe("Metadata editor", () => { describe("Metadata editor", () => {
it("can expand field", () => { it("can expand field", async () => {
const wrapper = mount(<Component />); // Arrange
render(<Component />);
const expandDataEl = "data-test-expanded"; const user = userEvent.setup();
const isExpandedAttribute = "data-test-expanded";
expect( const editor = screen.getAllByTestId("metadata-editor")[0];
wrapper // Assert
.find(`[${expandDataEl}]`) expect(editor).toHaveAttribute(isExpandedAttribute, "false");
.first() // Act
.prop(expandDataEl), await user.click(getFirstExpandIcon());
).toEqual(false); // Assert
wrapper expect(editor).toHaveAttribute(isExpandedAttribute, "true");
.find(`[${expandButton}]`)
.first()
.simulate("click");
expect(
wrapper
.find(`[${expandDataEl}]`)
.first()
.prop(expandDataEl),
).toEqual(true);
}); });
it("can edit field name", () => { it("can edit field name", async () => {
const wrapper = mount(<Component />); // Arrange
render(<Component />);
const inputNameSelector = '[name="name:1"] input'; const user = userEvent.setup();
// Act
// Expand to reveal fields await user.click(getFirstExpandIcon());
wrapper // Arrange
.find(`[${expandButton}]`) const input = screen.getByRole("textbox", {
.first() name: /name:0/i,
.simulate("click"); });
// Assert
expect( expect(input).toHaveValue(props.data.metadata[0].key);
wrapper // Act
.find(inputNameSelector) await user.type(input, " with new name");
.first() // Assert
.prop("value"), expect(input).toHaveValue("key with new name");
).toEqual(props.data.metadata[1].key);
wrapper
.find(inputNameSelector)
.first()
.simulate("change", { target: { name: "name:1", value: "x" } });
expect(
wrapper
.find(inputNameSelector)
.first()
.prop("value"),
).toEqual("x");
}); });
it("can edit field value", () => { it("can edit field value", async () => {
const wrapper = mount(<Component />); // Arrange
render(<Component />);
const inputNameSelector = '[name="value:1"] textarea'; const user = userEvent.setup();
// Act
// Expand to reveal fields await user.click(getFirstExpandIcon());
wrapper // Arrange
.find(`[${expandButton}]`) const input = screen.getByRole("textbox", { name: /value:0/i });
.first() // Assert
.simulate("click"); expect(input).toHaveValue(props.data.metadata[0].value);
// Act
expect( await user.type(input, " with new field value");
wrapper // Assert
.find(inputNameSelector) expect(input).toHaveValue("value with new field value");
.first()
.prop("value"),
).toEqual(props.data.metadata[1].value);
wrapper
.find(inputNameSelector)
.first()
.simulate("change", { target: { name: "value:1", value: "x" } });
expect(
wrapper
.find(inputNameSelector)
.first()
.prop("value"),
).toEqual("x");
}); });
it("can delete field", () => { it("can delete field", async () => {
const wrapper = mount(<Component />); // Arrange
render(<Component />);
const fieldSelector = 'tr[data-test-id="field"]'; const user = userEvent.setup();
const deleteButtonSelector = '[data-test-id*="delete-field"]'; // Act
await user.click(getFirstExpandIcon());
// Expand to reveal fields // Assert
wrapper expect(screen.getAllByTestId("field")).toHaveLength(
.find(`[${expandButton}]`)
.first()
.simulate("click");
expect(wrapper.find(fieldSelector).length).toEqual(
props.data.metadata.length, props.data.metadata.length,
); );
// Act
wrapper await user.click(screen.getByTestId("delete-field-0"));
.find(deleteButtonSelector) // Assert
.first() expect(screen.getAllByTestId("field")).toHaveLength(
.simulate("click");
expect(wrapper.find(fieldSelector).length).toEqual(
props.data.metadata.length - 1, props.data.metadata.length - 1,
); );
}); });
it("can add field", () => { it("can add field", async () => {
const wrapper = mount(<Component />); // Arrange
render(<Component />);
const fieldSelector = 'tr[data-test-id="field"]'; const user = userEvent.setup();
const addButtonSelector = '[data-test-id="add-field"]'; // Act
await user.click(getFirstExpandIcon());
// Expand to reveal fields // Assert
wrapper expect(screen.getAllByTestId("field")).toHaveLength(
.find(`[${expandButton}]`)
.first()
.simulate("click");
expect(wrapper.find(fieldSelector).length).toEqual(
props.data.metadata.length, props.data.metadata.length,
); );
// Act
wrapper await user.click(screen.getAllByTestId("add-field")[0]);
.find(addButtonSelector) // Assert
.first() expect(screen.getAllByTestId("field")).toHaveLength(
.simulate("click");
expect(wrapper.find(fieldSelector).length).toEqual(
props.data.metadata.length + 1, props.data.metadata.length + 1,
); );
}); });

View file

@ -156,6 +156,9 @@ const MetadataCard: React.FC<MetadataCardProps> = ({
input: classes.nameInput, input: classes.nameInput,
}, },
}} }}
inputProps={{
"aria-label": `${nameInputPrefix}${nameSeparator}${fieldIndex}`,
}}
name={`${nameInputPrefix}${nameSeparator}${fieldIndex}`} name={`${nameInputPrefix}${nameSeparator}${fieldIndex}`}
fullWidth fullWidth
onChange={onChange} onChange={onChange}
@ -169,6 +172,9 @@ const MetadataCard: React.FC<MetadataCardProps> = ({
root: classes.input, root: classes.input,
}, },
}} }}
inputProps={{
"aria-label": `${valueInputPrefix}${nameSeparator}${fieldIndex}`,
}}
multiline multiline
name={`${valueInputPrefix}${nameSeparator}${fieldIndex}`} name={`${valueInputPrefix}${nameSeparator}${fieldIndex}`}
fullWidth fullWidth

View file

@ -214,6 +214,7 @@ const SingleAutocompleteSelectFieldContent: React.FC<SingleAutocompleteSelectFie
className={classes.content} className={classes.content}
ref={anchor} ref={anchor}
data-test-id="autocomplete-dropdown" data-test-id="autocomplete-dropdown"
aria-label="autocomplete-dropdown"
> >
{choices.length > 0 || displayCustomValue ? ( {choices.length > 0 || displayCustomValue ? (
<> <>

View file

@ -2,24 +2,20 @@ import placeholderImage from "@assets/images/placeholder255x255.png";
import { channelsList } from "@saleor/channels/fixtures"; import { channelsList } from "@saleor/channels/fixtures";
import { collections } from "@saleor/collections/fixtures"; import { collections } from "@saleor/collections/fixtures";
import { fetchMoreProps, limits } from "@saleor/fixtures"; import { fetchMoreProps, limits } from "@saleor/fixtures";
import * as _useNavigator from "@saleor/hooks/useNavigator";
import { product as productFixture } from "@saleor/products/fixtures"; import { product as productFixture } from "@saleor/products/fixtures";
import { taxClasses } from "@saleor/taxes/fixtures";
import { warehouseList } from "@saleor/warehouses/fixtures"; import { warehouseList } from "@saleor/warehouses/fixtures";
import Wrapper from "@test/wrapper"; import Wrapper from "@test/wrapper";
import { configure, mount } from "enzyme"; import { fireEvent, render, screen, waitFor } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import React from "react"; import React from "react";
import { MemoryRouter } from "react-router-dom";
import ProductUpdatePage, { ProductUpdatePageProps } from "./ProductUpdatePage"; import ProductUpdatePage, { ProductUpdatePageProps } from "./ProductUpdatePage";
const product = productFixture(placeholderImage); const product = productFixture(placeholderImage);
import * as _useNavigator from "@saleor/hooks/useNavigator";
import { taxClasses } from "@saleor/taxes/fixtures";
import Adapter from "@wojtekmaj/enzyme-adapter-react-17";
import { act } from "react-dom/test-utils";
import { MemoryRouter } from "react-router-dom";
configure({ adapter: new Adapter() });
const onSubmit = jest.fn(); const onSubmit = jest.fn();
const useNavigator = jest.spyOn(_useNavigator, "default"); const useNavigator = jest.spyOn(_useNavigator, "default");
jest.mock("@saleor/components/RichTextEditor/RichTextEditor"); jest.mock("@saleor/components/RichTextEditor/RichTextEditor");
@ -28,25 +24,18 @@ jest.mock("@saleor/utils/richText/useRichText");
* Mocking glide library. We do want to test only if page renders, grid itself has dedicated tests. * Mocking glide library. We do want to test only if page renders, grid itself has dedicated tests.
*/ */
jest.mock("@glideapps/glide-data-grid", () => { jest.mock("@glideapps/glide-data-grid", () => {
const { forwardRef } = jest.requireActual("react"); const { forwardRef } = jest.requireActual<typeof import("react")>("react");
const dataGrid = jest.requireActual<
typeof import("@glideapps/glide-data-grid")
>("@glideapps/glide-data-grid");
return { return {
...jest.requireActual("@glideapps/glide-data-grid"), ...dataGrid,
__esModule: true, __esModule: true,
default: forwardRef((_: any, ref: any) => <div ref={ref} />), default: forwardRef((_: any, ref: any) => <div ref={ref} />),
}; };
}); });
(global as any).document.createRange = () => ({
// eslint-disable-next-line
setStart: () => {},
// eslint-disable-next-line
setEnd: () => {},
commonAncestorContainer: {
nodeName: "BODY",
ownerDocument: document,
},
});
const props: ProductUpdatePageProps = { const props: ProductUpdatePageProps = {
channels: channelsList, channels: channelsList,
variantListErrors: [], variantListErrors: [],
@ -89,54 +78,44 @@ const props: ProductUpdatePageProps = {
attributeValues: [], attributeValues: [],
}; };
const selectors = {
dropdown: `[data-test-id="autocomplete-dropdown"]`,
empty: `[data-test-type="empty"]`,
input: `[data-test-id="attribute-value"] input`,
};
describe("Product details page", () => { describe("Product details page", () => {
useNavigator.mockImplementation(); useNavigator.mockImplementation();
// DataEditor.mockImplementation();
it("can select empty option on attribute", async () => { it("can select empty option on attribute", async () => {
const component = mount( // Arrange
render(
<MemoryRouter> <MemoryRouter>
<Wrapper> <Wrapper>
<ProductUpdatePage {...props} /> <ProductUpdatePage {...props} />
</Wrapper> </Wrapper>
</MemoryRouter>, </MemoryRouter>,
); );
expect(component.find(selectors.dropdown).exists()).toBeFalsy(); const user = userEvent.setup();
const attributeInput = screen.getAllByRole("textbox")[1];
component // Assert
.find(selectors.input) expect(attributeInput).toHaveAttribute(
.first() "aria-labelledby",
.simulate("click"); "downshift-0-label",
);
expect(component.find(selectors.dropdown).exists()).toBeTruthy(); // Act
await user.click(attributeInput);
expect(component.find(selectors.empty).exists()); // Assert
expect(screen.queryByTestId("autocomplete-dropdown")).toBeInTheDocument();
component // Arrange
.find(selectors.empty) const emptyOption = screen.queryAllByTestId(
.first() "single-autocomplete-select-option",
.simulate("click"); )[0];
// Assert
expect( expect(emptyOption).toBeInTheDocument();
component // Act
.find(selectors.input) await user.click(emptyOption);
.first() // Assert
.prop("value"), expect(attributeInput).toHaveValue("");
).toEqual(""); // Act
await waitFor(() =>
await act(async () => { fireEvent.submit(screen.getByTestId("product-update-form")),
component );
.find("form") // Assert
.first()
.simulate("submit");
// wait for async function to complete
await new Promise(process.nextTick);
});
expect(onSubmit.mock.calls[0][0].attributes[0].value.length).toEqual(0); expect(onSubmit.mock.calls[0][0].attributes[0].value.length).toEqual(0);
}); });
}); });

View file

@ -322,7 +322,7 @@ const ProductUpdateForm: React.FC<ProductUpdateFormProps> = ({
); );
return ( return (
<form onSubmit={props.submit}> <form onSubmit={props.submit} data-test-id="product-update-form">
<DatagridChangeStateContext.Provider value={datagrid}> <DatagridChangeStateContext.Provider value={datagrid}>
<RichTextContext.Provider value={richText}> <RichTextContext.Provider value={richText}>
{children(props)} {children(props)}

View file

@ -1,3 +1,5 @@
import "@testing-library/jest-dom";
import { configure } from "@testing-library/react"; import { configure } from "@testing-library/react";
document.getElementById = () => document.createElement("div"); document.getElementById = () => document.createElement("div");
@ -11,4 +13,6 @@ window.__SALEOR_CONFIG__ = {
APP_TEMPLATE_GALLERY_PATH: "/template-gallery", APP_TEMPLATE_GALLERY_PATH: "/template-gallery",
}; };
process.env.TZ = "UTC";
configure({ testIdAttribute: "data-test-id" }); configure({ testIdAttribute: "data-test-id" });