diff --git a/.circleci/config.yml b/.circleci/config.yml index fa497104e..de544eec2 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -88,7 +88,7 @@ jobs: name: Perform testing # We're running these tests in band to avoid errors where the circleci # test runner runs out of RAM trying to run them all in parallel. - command: npm run test -- --ci --runInBand --testResultsProcessor="jest-junit" + command: npm run test -- --ci --runInBand --reporters=default --reporters=jest-junit - store_test_results: path: reports/junit - store_artifacts: diff --git a/package-lock.json b/package-lock.json index a0cc8d1aa..051507565 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17548,9 +17548,9 @@ } }, "hyphenate-style-name": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/hyphenate-style-name/-/hyphenate-style-name-1.0.2.tgz", - "integrity": "sha1-MRYKNpMK2vH8BMYHT360FGXU7Es=", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/hyphenate-style-name/-/hyphenate-style-name-1.0.3.tgz", + "integrity": "sha512-EcuixamT82oplpoJ2XU4pDtKGWQ7b00CD9f1ug9IaQ3p1bkHMiKCZ9ut9QDI6qsa6cpUuB+A/I+zLtdNK4n2DQ==", "dev": true }, "iconv-lite": { @@ -27837,15 +27837,27 @@ "dev": true }, "react": { - "version": "16.8.6", - "resolved": "https://registry.npmjs.org/react/-/react-16.8.6.tgz", - "integrity": "sha512-pC0uMkhLaHm11ZSJULfOBqV4tIZkx87ZLvbbQYunNixAAvjnC+snJCg0XQXn9VIsttVsbZP/H/ewzgsd5fxKXw==", + "version": "16.9.0-alpha.0", + "resolved": "https://registry.npmjs.org/react/-/react-16.9.0-alpha.0.tgz", + "integrity": "sha512-y4bu7rJvtnPPsIwOj7sp5Y2SqlOb0jFupfkdjWxxn8ZeqzUARgpR9wJBUVwW1/QosVdOblmApjo/j6iiAXnebA==", "dev": true, "requires": { "loose-envify": "^1.1.0", "object-assign": "^4.1.1", "prop-types": "^15.6.2", - "scheduler": "^0.13.6" + "scheduler": "^0.14.0-alpha.0" + }, + "dependencies": { + "scheduler": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.14.0.tgz", + "integrity": "sha512-9CgbS06Kki2f4R9FjLSITjZo5BZxPsryiRNyL3LpvrM9WxcVmhlqAOc9E+KQbeI2nqej4JIIbOsfdL51cNb4Iw==", + "dev": true, + "requires": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1" + } + } } }, "react-codemirror2": { @@ -28782,15 +28794,27 @@ } }, "react-dom": { - "version": "16.8.6", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.8.6.tgz", - "integrity": "sha512-1nL7PIq9LTL3fthPqwkvr2zY7phIPjYrT0jp4HjyEQrEROnw4dG41VVwi/wfoCneoleqrNX7iAD+pXebJZwrwA==", + "version": "16.9.0-alpha.0", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.9.0-alpha.0.tgz", + "integrity": "sha512-BQ5gN42yIPuTnBvE6K9vSjNfDRpSNcYCs2sUx9XR5VaWKwlHTt3G6qIWK6zdXy8TYKb1+IxpsAI0RtbRdXQZ2A==", "dev": true, "requires": { "loose-envify": "^1.1.0", "object-assign": "^4.1.1", "prop-types": "^15.6.2", - "scheduler": "^0.13.6" + "scheduler": "^0.14.0-alpha.0" + }, + "dependencies": { + "scheduler": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.14.0.tgz", + "integrity": "sha512-9CgbS06Kki2f4R9FjLSITjZo5BZxPsryiRNyL3LpvrM9WxcVmhlqAOc9E+KQbeI2nqej4JIIbOsfdL51cNb4Iw==", + "dev": true, + "requires": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1" + } + } } }, "react-error-overlay": { @@ -29041,9 +29065,9 @@ "integrity": "sha512-d1ttgGGRrjvntUeStKOGO4tsJy2P7hQ+T98DzdTa8QFEx8//0+/IPc1TUtFk0GVTUQJXG66cQw7EknwDRmRLfg==" }, "react-responsive": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/react-responsive/-/react-responsive-5.0.0.tgz", - "integrity": "sha512-oEimZ0FTCC3/pjGDEBHOz06nWbBNDIbMGOdRYp6K9SBUmrqgNAX77hTiqvmRQeLyI97zz4F4kiaFRxFspDxE+w==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/react-responsive/-/react-responsive-7.0.0.tgz", + "integrity": "sha512-RukaKD+UI/MIR+P8eUgVGURfiCafRvvcVnq41scT0eEQWHwDGliH/OAlrwIr1oyz8aKLGroZa+U8mTZV5ihPfA==", "dev": true, "requires": { "hyphenate-style-name": "^1.0.0", @@ -29122,22 +29146,32 @@ "integrity": "sha512-rxlZtZk5t6Y3gqqpaZ1lxY3RqlQcBU5uGsSoZj/hbF3ZweDqPbFHDkczT4emAxeaw37OD96RAAoayFGFQZCdWg==" }, "react-test-renderer": { - "version": "16.8.6", - "resolved": "https://registry.npmjs.org/react-test-renderer/-/react-test-renderer-16.8.6.tgz", - "integrity": "sha512-H2srzU5IWYT6cZXof6AhUcx/wEyJddQ8l7cLM/F7gDXYyPr4oq+vCIxJYXVGhId1J706sqziAjuOEjyNkfgoEw==", + "version": "16.9.0-alpha.0", + "resolved": "https://registry.npmjs.org/react-test-renderer/-/react-test-renderer-16.9.0-alpha.0.tgz", + "integrity": "sha512-eDl0oVFo6PGY1wpYFs0ezBpZhOgVce5TSta9UPLanshTi4z8NhlM6IgO8KBdioQ5H5/pmyGxOVtpUxJOt19NAQ==", "dev": true, "requires": { "object-assign": "^4.1.1", "prop-types": "^15.6.2", - "react-is": "^16.8.6", - "scheduler": "^0.13.6" + "react-is": "^16.9.0-alpha.0", + "scheduler": "^0.14.0-alpha.0" }, "dependencies": { "react-is": { - "version": "16.8.6", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.8.6.tgz", - "integrity": "sha512-aUk3bHfZ2bRSVFFbbeVS4i+lNPZr3/WM5jT2J5omUVV1zzcs1nAaf3l51ctA5FFvCRbhrH0bdAsRRQddFJZPtA==", + "version": "16.9.0-alpha.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.9.0-alpha.0.tgz", + "integrity": "sha512-psl0ePLTFliYfwcbwvimLgTNN156ZdeWB4zvP7dV/6lTAqWMHFfidg/mSZ2fFgE1LMNN8ZJOLl2DfZ8yg+3ETA==", "dev": true + }, + "scheduler": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.14.0.tgz", + "integrity": "sha512-9CgbS06Kki2f4R9FjLSITjZo5BZxPsryiRNyL3LpvrM9WxcVmhlqAOc9E+KQbeI2nqej4JIIbOsfdL51cNb4Iw==", + "dev": true, + "requires": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1" + } } } }, diff --git a/package.json b/package.json index 1512fceba..6fbdbf57c 100644 --- a/package.json +++ b/package.json @@ -286,16 +286,16 @@ "pstree.remy": "^1.1.6", "pym.js": "^1.3.2", "raw-loader": "^0.5.1", - "react": "^16.8.6", + "react": "^16.9.0-alpha.0", "react-copy-to-clipboard": "^5.0.1", "react-dev-utils": "^9.0.0", - "react-dom": "^16.8.6", + "react-dom": "^16.9.0-alpha.0", "react-error-overlay": "^5.1.6", "react-final-form": "4.0.2", "react-popper": "^1.3.2", "react-relay": "^4.0.0", - "react-responsive": "^5.0.0", - "react-test-renderer": "^16.8.6", + "react-responsive": "^7.0.0", + "react-test-renderer": "^16.9.0-alpha.0", "react-timeago": "^4.1.9", "react-transition-group": "^2.5.0", "react-with-state-props": "^2.0.4", diff --git a/src/core/client/account/test/resetPassword.spec.tsx b/src/core/client/account/test/resetPassword.spec.tsx index 1389b3d08..77df5fccd 100644 --- a/src/core/client/account/test/resetPassword.spec.tsx +++ b/src/core/client/account/test/resetPassword.spec.tsx @@ -2,6 +2,7 @@ import sinon from "sinon"; import { GQLResolver } from "coral-framework/schema"; import { + act, createAccessToken, CreateTestRendererParams, replaceHistoryLocation, @@ -52,11 +53,13 @@ it("renders form", async () => { }) .once(); - await waitForElement(() => - within(root).getByText("Reset your password", { - exact: false, - }) - ); + await act(async () => { + await waitForElement(() => + within(root).getByText("Reset your password", { + exact: false, + }) + ); + }); expect(within(root).toJSON()).toMatchSnapshot(); restMock.verify(); }); @@ -91,11 +94,13 @@ it("renders error from server", async () => { }) ); - await waitForElement(() => - within(root).getByText(code, { - exact: false, - }) - ); + await act(async () => { + await waitForElement(() => + within(root).getByText(code, { + exact: false, + }) + ); + }); restMock.verify(); } }); @@ -126,36 +131,42 @@ it("submits form", async () => { }) .once(); - await waitForElement(() => - within(root).getByText("Reset your password", { - exact: false, - }) - ); - + await act(async () => { + await waitForElement(() => + within(root).getByText("Reset your password", { + exact: false, + }) + ); + }); const form = within(root).getByType("form"); const textField = within(root).getByLabelText("Password"); // Submit an empty form. - form.props.onSubmit(); + act(() => { + form.props.onSubmit(); + }); within(root).getByText("field is required", { exact: false, }); // Password too short. - textField.props.onChange("test"); + act(() => { + textField.props.onChange("test"); + }); within(root).getByText("Password must contain at least 8 characters", { exact: false, }); // Submit valid form. - textField.props.onChange("testtest"); - form.props.onSubmit(); - - await waitForElement(() => - within(root).getByText("successfully", { - exact: false, - }) - ); + await act(async () => { + textField.props.onChange("testtest"); + form.props.onSubmit(); + await waitForElement(() => + within(root).getByText("successfully", { + exact: false, + }) + ); + }); restMock.verify(); }); diff --git a/src/core/client/admin/test/auth/accountCompletion.spec.tsx b/src/core/client/admin/test/auth/accountCompletion.spec.tsx index a51eaa6af..3a7e96137 100644 --- a/src/core/client/admin/test/auth/accountCompletion.spec.tsx +++ b/src/core/client/admin/test/auth/accountCompletion.spec.tsx @@ -1,6 +1,7 @@ import { pureMerge } from "coral-common/utils"; import { GQLResolver } from "coral-framework/schema"; import { + act, createAccessToken, createResolversStub, CreateTestRendererParams, @@ -117,11 +118,13 @@ it("do not render createPassword view when local auth is disabled", async () => }), }); - await wait(() => - expect(window.location.toString()).toBe( - "http://localhost/admin/moderate/reported" - ) - ); + await act(async () => { + await wait(() => + expect(window.location.toString()).toBe( + "http://localhost/admin/moderate/reported" + ) + ); + }); }); it("complete account", async () => { @@ -137,9 +140,11 @@ it("complete account", async () => { }, }), }); - await wait(() => - expect(window.location.toString()).toBe( - "http://localhost/admin/moderate/reported" - ) - ); + await act(async () => { + await wait(() => + expect(window.location.toString()).toBe( + "http://localhost/admin/moderate/reported" + ) + ); + }); }); diff --git a/src/core/client/admin/test/auth/redirectLoggedIn.spec.tsx b/src/core/client/admin/test/auth/redirectLoggedIn.spec.tsx index 879e6f330..baeacdfab 100644 --- a/src/core/client/admin/test/auth/redirectLoggedIn.spec.tsx +++ b/src/core/client/admin/test/auth/redirectLoggedIn.spec.tsx @@ -3,6 +3,7 @@ import { QueryToModerationQueuesResolver, } from "coral-framework/schema"; import { + act, createAccessToken, createQueryResolverStub, createResolversStub, @@ -55,11 +56,13 @@ async function createTestRenderer( it("redirect when already logged in", async () => { await createTestRenderer(); - await wait(() => - expect(window.location.toString()).toBe( - "http://localhost/admin/moderate/reported" - ) - ); + await act(async () => { + await wait(() => + expect(window.location.toString()).toBe( + "http://localhost/admin/moderate/reported" + ) + ); + }); }); it("redirect to redirectPath when already logged in", async () => { @@ -68,9 +71,11 @@ it("redirect to redirectPath when already logged in", async () => { localRecord.setValue("/admin/moderate/pending", "redirectPath"); }, }); - await wait(() => - expect(window.location.toString()).toBe( - "http://localhost/admin/moderate/pending" - ) - ); + await act(async () => { + await wait(() => + expect(window.location.toString()).toBe( + "http://localhost/admin/moderate/pending" + ) + ); + }); }); diff --git a/src/core/client/admin/test/auth/redirectLoggedOut.spec.tsx b/src/core/client/admin/test/auth/redirectLoggedOut.spec.tsx index 3ee3a7518..fd5c1f081 100644 --- a/src/core/client/admin/test/auth/redirectLoggedOut.spec.tsx +++ b/src/core/client/admin/test/auth/redirectLoggedOut.spec.tsx @@ -5,6 +5,7 @@ import { QueryToModerationQueuesResolver, } from "coral-framework/schema"; import { + act, createQueryResolverStub, createResolversStub, CreateTestRendererParams, @@ -50,10 +51,12 @@ async function createTestRenderer( it("redirect when not logged in", async () => { const { context } = await createTestRenderer(); - await wait(() => { - expect(lookup(context.relayEnvironment, LOCAL_ID)!.redirectPath).toBe( - "/admin/moderate/reported" - ); - expect(window.location.toString()).toBe("http://localhost/admin/login"); + await act(async () => { + await wait(() => { + expect(lookup(context.relayEnvironment, LOCAL_ID)!.redirectPath).toBe( + "/admin/moderate/reported" + ); + expect(window.location.toString()).toBe("http://localhost/admin/login"); + }); }); }); diff --git a/src/core/client/admin/test/auth/signInWithEmail.spec.tsx b/src/core/client/admin/test/auth/signInWithEmail.spec.tsx index 6753eaba2..d063246c8 100644 --- a/src/core/client/admin/test/auth/signInWithEmail.spec.tsx +++ b/src/core/client/admin/test/auth/signInWithEmail.spec.tsx @@ -3,6 +3,7 @@ import sinon from "sinon"; import { pureMerge } from "coral-common/utils"; import { GQLResolver } from "coral-framework/schema"; import { + act, createAccessToken, createResolversStub, CreateTestRendererParams, @@ -12,6 +13,7 @@ import { within, } from "coral-framework/testHelpers"; +import { ReactTestInstance } from "react-test-renderer"; import create from "../create"; import { emptyModerationQueues, @@ -47,10 +49,14 @@ async function createTestRenderer( }, }); - const form = await waitForElement(() => - within(testRenderer.root).getByType("form") - ); - return { testRenderer, form, context }; + let form: ReactTestInstance; + + await act(async () => { + form = await waitForElement(() => + within(testRenderer.root).getByType("form") + ); + }); + return { testRenderer, form: form!, context }; } it("renders sign in form", async () => { @@ -150,12 +156,16 @@ it("submits form successfully", async () => { const historyMock = sinon.mock(window.history); - form.props.onSubmit(); + act(() => { + form.props.onSubmit(); + }); const submitButton = within(form).getByText("Sign in with Email", { selector: "button", }); expect(submitButton.props.disabled).toBe(true); - await wait(() => expect(submitButton.props.disabled).toBe(false)); + await act(async () => { + await wait(() => expect(submitButton.props.disabled).toBe(false)); + }); expect(location.toString()).toMatchSnapshot(); restMock.verify(); historyMock.verify(); diff --git a/src/core/client/admin/test/community/community.spec.tsx b/src/core/client/admin/test/community/community.spec.tsx index a0321605b..3e9773f51 100644 --- a/src/core/client/admin/test/community/community.spec.tsx +++ b/src/core/client/admin/test/community/community.spec.tsx @@ -8,6 +8,7 @@ import { QueryToUsersResolver, } from "coral-framework/schema"; import { + act, createQueryResolverStub, createResolversStub, CreateTestRendererParams, @@ -101,17 +102,14 @@ it("filter by role", async () => { const selectField = within(container).getByLabelText("Search by role"); const commentersOption = within(selectField).getByText("Commenters"); - TestRenderer.act(() => { + await act(async () => { selectField.props.onChange({ target: { value: commentersOption.props.value.toString() }, }); - // TODO: Fix act warnings until await Promise.resolve(); - // or whatever comes out at https://github.com/facebook/react/issues/14769 + await waitForElement(() => + within(container).getByText("We could not find anyone", { exact: false }) + ); }); - - await waitForElement(() => - within(container).getByText("We could not find anyone", { exact: false }) - ); }); it("can't change viewer role", async () => { @@ -149,7 +147,7 @@ it("change user role", async () => { selector: "tr", }); - TestRenderer.act(() => { + act(() => { within(userRow) .getByLabelText("Change role") .props.onClick(); @@ -159,7 +157,7 @@ it("change user role", async () => { "A dropdown to change the user role" ); - TestRenderer.act(() => { + act(() => { within(popup) .getByText("Staff", { selector: "button" }) .props.onClick(); @@ -223,13 +221,11 @@ it("load more", async () => { }), }); const loadMore = within(container).getByText("Load More"); - TestRenderer.act(() => { + await act(async () => { loadMore.props.onClick(); + // Wait for load more to disappear. + await waitUntilThrow(() => within(container).getByText("Load More")); }); - - // Wait for load more to disappear. - await waitUntilThrow(() => within(container).getByText("Load More")); - // Make sure third user was added. within(container).getByText(users.commenters[1].username!); }); @@ -256,16 +252,15 @@ it("filter by search", async () => { }); const form = findParentWithType(searchField, "form")!; - TestRenderer.act(() => { + await act(async () => { searchField.props.onChange({ target: { value: "search" }, }); form.props.onSubmit(); + await waitForElement(() => + within(container).getByText("could not find anyone", { exact: false }) + ); }); - - await waitForElement(() => - within(container).getByText("could not find anyone", { exact: false }) - ); }); it("filter by status", async () => { @@ -293,17 +288,14 @@ it("filter by status", async () => { ); const bannedOption = within(statusField).getByText("Banned"); - TestRenderer.act(() => { + await act(async () => { statusField.props.onChange({ target: { value: bannedOption.props.value.toString() }, }); - // TODO: Fix act warnings until await Promise.resolve(); - // or whatever comes out at https://github.com/facebook/react/issues/14769 + await waitForElement(() => + within(container).getByText("We could not find anyone", { exact: false }) + ); }); - - await waitForElement(() => - within(container).getByText("We could not find anyone", { exact: false }) - ); }); it("can't change staff, moderator and admin status", async () => { @@ -373,9 +365,11 @@ it("ban user", async () => { expect(within(modal).toJSON()).toMatchSnapshot(); - within(modal) - .getByText("Ban User") - .props.onClick(); + TestRenderer.act(() => { + within(modal) + .getByText("Ban User") + .props.onClick(); + }); within(userRow).getByText("Banned"); expect(resolvers.Mutation!.banUser!.called).toBe(true); }); diff --git a/src/core/client/admin/test/moderate/__snapshots__/moderate.spec.tsx.snap b/src/core/client/admin/test/moderate/__snapshots__/moderate.spec.tsx.snap index a97508d3b..1da5240e6 100644 --- a/src/core/client/admin/test/moderate/__snapshots__/moderate.spec.tsx.snap +++ b/src/core/client/admin/test/moderate/__snapshots__/moderate.spec.tsx.snap @@ -1196,27 +1196,6 @@ exports[`reported queue rejects comment in reported queue: dangling 1`] = ` `; -exports[`reported queue renders empty reported queue 1`] = ` -
-
-
-
- Nicely done! There are no more reported comments to moderate. -
-
-
-
-`; - exports[`reported queue renders reported queue with comments 1`] = `
{ const openSearchBar = async (testRenderer: ReactTestRenderer) => { - const searchBar = await waitForElement(() => - within(testRenderer.root).getByTestID("moderate-searchBar-container") + await act(async () => { + await waitForElement(() => + within(testRenderer.root).getByTestID("moderate-searchBar-container") + ); + }); + const searchBar = within(testRenderer.root).getByTestID( + "moderate-searchBar-container" ); const textField = within(searchBar).getByLabelText( "Search or jump to story..." ); const form = findParentWithType(textField, "form")!; - textField.props.onFocus({}); + act(() => textField.props.onFocus({})); return { searchBar, textField, form }; }; describe("all stories", () => { it("renders search bar", async () => { - const { testRenderer } = await createTestRenderer(); - const searchBar = await waitForElement(() => - within(testRenderer.root).getByTestID("moderate-searchBar-container") - ); - expect(within(searchBar).toJSON()).toMatchSnapshot(); + let searchBar: ReactTestInstance; + await act(async () => { + const { testRenderer } = await createTestRenderer(); + searchBar = await waitForElement(() => + within(testRenderer.root).getByTestID("moderate-searchBar-container") + ); + }); + expect(within(searchBar!).toJSON()).toMatchSnapshot(); }); describe("active", () => { @@ -109,17 +118,21 @@ describe("search bar", () => { ); expect(within(searchBar).toJSON()).toMatchSnapshot(); - // Search for sth. - textField.props.onChange(query); - form.props.onSubmit(); + await act(async () => { + // Search for sth. + textField.props.onChange(query); + form.props.onSubmit(); - // Ensure no results message is shown. - await wait(() => - within(searchBar).getByText("No results", { exact: false }) - ); + // Ensure no results message is shown. + await wait(() => + within(searchBar).getByText("No results", { exact: false }) + ); + }); - // Blurring should close the listbox. - textField.props.onBlur({}); + act(() => { + // Blurring should close the listbox. + textField.props.onBlur({}); + }); expect(within(searchBar).queryByText("No results")).toBeNull(); }); it("search with actual results", async () => { @@ -142,24 +155,28 @@ describe("search bar", () => { testRenderer ); - // Search for sth. - textField.props.onChange(query); - form.props.onSubmit(); - const story = storyConnection.edges[0].node; - // Find the story in the search results. - const storyOption = findParentWithType( - await waitForElement(() => - within(searchBar).getByText(story.metadata!.title!, { - exact: false, - }) - ), - "li" - )!; + let storyOption: ReactTestInstance; + + await act(async () => { + // Search for sth. + textField.props.onChange(query); + form.props.onSubmit(); + + // Find the story in the search results. + storyOption = findParentWithType( + await waitForElement(() => + within(searchBar).getByText(story.metadata!.title!, { + exact: false, + }) + ), + "li" + )!; + }); // Go to story. - storyOption.props.onClick({ button: 0, preventDefault: noop }); + storyOption!.props.onClick({ button: 0, preventDefault: noop }); // Expect a routing request was made to the right url. expect(transitionControl.history[0].pathname).toBe( @@ -188,22 +205,25 @@ describe("search bar", () => { testRenderer ); - // Search for sth. - textField.props.onChange(query); - form.props.onSubmit(); + let seeAllOption: ReactTestInstance; + await act(async () => { + // Search for sth. + textField.props.onChange(query); + form.props.onSubmit(); - // Find see all options in the search results. - const seeAllOption = findParentWithType( - await waitForElement(() => - within(searchBar).getByText("See all results", { exact: false }) - ), - "li" - )!; + // Find see all options in the search results. + seeAllOption = findParentWithType( + await waitForElement(() => + within(searchBar).getByText("See all results", { exact: false }) + ), + "li" + )!; + }); - expect(within(seeAllOption).toJSON()).toMatchSnapshot(); + expect(within(seeAllOption!).toJSON()).toMatchSnapshot(); // Go to story. - seeAllOption.props.onClick({ button: 0, preventDefault: noop }); + seeAllOption!.props.onClick({ button: 0, preventDefault: noop }); // Expect a routing request was made to the right url. expect(transitionControl.history[0].pathname).toBe("/admin/stories"); @@ -218,20 +238,22 @@ describe("search bar", () => { ); }); it("renders search bar", async () => { - const { testRenderer } = await createTestRenderer({ - resolvers: createResolversStub({ - Query: { - story: () => stories[0], - }, - }), + await act(async () => { + const { testRenderer } = await createTestRenderer({ + resolvers: createResolversStub({ + Query: { + story: () => stories[0], + }, + }), + }); + const searchBar = await waitForElement(() => + within(testRenderer.root).getByTestID("moderate-searchBar-container") + ); + const textField = within(searchBar).getByLabelText( + "Search or jump to story..." + ); + expect(textField.props.placeholder).toBe(stories[0].metadata!.title); }); - const searchBar = await waitForElement(() => - within(testRenderer.root).getByTestID("moderate-searchBar-container") - ); - const textField = within(searchBar).getByLabelText( - "Search or jump to story..." - ); - expect(textField.props.placeholder).toBe(stories[0].metadata!.title); }); it("shows moderate all option", async () => { const { @@ -266,46 +288,58 @@ describe("search bar", () => { describe("tab bar", () => { it("renders tab bar (empty queues)", async () => { - const { testRenderer } = await createTestRenderer(); - const { getByTestID } = within(testRenderer.root); - await waitForElement(() => getByTestID("moderate-container")); - expect(toJSON(getByTestID("moderate-tabBar-container"))).toMatchSnapshot(); + await act(async () => { + const { testRenderer } = await createTestRenderer(); + const { getByTestID } = within(testRenderer.root); + await waitForElement(() => getByTestID("moderate-container")); + expect( + toJSON(getByTestID("moderate-tabBar-container")) + ).toMatchSnapshot(); + }); }); it("should not show moderate story link in comment cards", async () => { - const { testRenderer } = await createTestRenderer(); - const { getByTestID } = within(testRenderer.root); - await waitForElement(() => getByTestID("moderate-container")); - expect(within(testRenderer.root).queryByText("Moderate Story")).toBeNull(); + await act(async () => { + const { testRenderer } = await createTestRenderer(); + const { getByTestID } = within(testRenderer.root); + await waitForElement(() => getByTestID("moderate-container")); + expect( + within(testRenderer.root).queryByText("Moderate Story") + ).toBeNull(); + }); }); }); describe("moderating specific story", () => { it("passes storyID to the endpoints", async () => { replaceHistoryLocation(`http://localhost/admin/moderate/${stories[0].id}`); - await createTestRenderer({ - resolvers: createResolversStub({ - Query: { - moderationQueues: ({ variables }) => { - expectAndFail(variables.storyID).toBe(stories[0].id); - return emptyModerationQueues; + await act(async () => { + await createTestRenderer({ + resolvers: createResolversStub({ + Query: { + moderationQueues: ({ variables }) => { + expectAndFail(variables.storyID).toBe(stories[0].id); + return emptyModerationQueues; + }, + comments: ({ variables }) => { + expectAndFail(variables.storyID).toBe(stories[0].id); + return emptyRejectedComments; + }, }, - comments: ({ variables }) => { - expectAndFail(variables.storyID).toBe(stories[0].id); - return emptyRejectedComments; - }, - }, - }), + }), + }); }); }); }); describe("reported queue", () => { it("renders empty reported queue", async () => { - const { testRenderer } = await createTestRenderer(); - const { getByTestID } = within(testRenderer.root); - - await waitForElement(() => getByTestID("moderate-container")); - expect(toJSON(getByTestID("moderate-main-container"))).toMatchSnapshot(); + await act(async () => { + const { testRenderer } = await createTestRenderer(); + const { getByText } = within(testRenderer.root); + await waitForElement(() => + getByText("no more reported", { exact: false }) + ); + }); }); it("renders empty pending queue", async () => { @@ -334,142 +368,192 @@ describe("reported queue", () => { }); it("renders reported queue with comments", async () => { - const { testRenderer } = await createTestRenderer({ - resolvers: createResolversStub({ - Query: { - moderationQueues: () => - pureMerge(emptyModerationQueues, { - reported: { - count: 2, - comments: createQueryResolverStub< - ModerationQueueToCommentsResolver - >(({ variables }) => { - expectAndFail(variables).toEqual({ first: 5 }); - return { - edges: [ - { - node: reportedComments[0], - cursor: reportedComments[0].createdAt, + await act(async () => { + const { testRenderer } = await createTestRenderer({ + resolvers: createResolversStub({ + Query: { + moderationQueues: () => + pureMerge(emptyModerationQueues, { + reported: { + count: 2, + comments: createQueryResolverStub< + ModerationQueueToCommentsResolver + >(({ variables }) => { + expectAndFail(variables).toEqual({ first: 5 }); + return { + edges: [ + { + node: reportedComments[0], + cursor: reportedComments[0].createdAt, + }, + { + node: reportedComments[1], + cursor: reportedComments[1].createdAt, + }, + ], + pageInfo: { + endCursor: reportedComments[1].createdAt, + hasNextPage: false, }, - { - node: reportedComments[1], - cursor: reportedComments[1].createdAt, - }, - ], - pageInfo: { - endCursor: reportedComments[1].createdAt, - hasNextPage: false, - }, - }; - }) as any, - }, - }), - }, - }), + }; + }) as any, + }, + }), + }, + }), + }); + const { getByTestID } = within(testRenderer.root); + await waitForElement(() => getByTestID("moderate-container")); + expect(toJSON(getByTestID("moderate-main-container"))).toMatchSnapshot(); }); - const { getByTestID } = within(testRenderer.root); - await waitForElement(() => getByTestID("moderate-container")); - expect(toJSON(getByTestID("moderate-main-container"))).toMatchSnapshot(); }); it("renders reported queue with comments", async () => { - const { testRenderer } = await createTestRenderer({ - resolvers: createResolversStub({ - Query: { - moderationQueues: () => - pureMerge(emptyModerationQueues, { - reported: { - count: 2, - comments: createQueryResolverStub< - ModerationQueueToCommentsResolver - >(({ variables }) => { - expectAndFail(variables).toEqual({ first: 5 }); - return { - edges: [ - { - node: reportedComments[0], - cursor: reportedComments[0].createdAt, + await act(async () => { + const { testRenderer } = await createTestRenderer({ + resolvers: createResolversStub({ + Query: { + moderationQueues: () => + pureMerge(emptyModerationQueues, { + reported: { + count: 2, + comments: createQueryResolverStub< + ModerationQueueToCommentsResolver + >(({ variables }) => { + expectAndFail(variables).toEqual({ first: 5 }); + return { + edges: [ + { + node: reportedComments[0], + cursor: reportedComments[0].createdAt, + }, + { + node: reportedComments[1], + cursor: reportedComments[1].createdAt, + }, + ], + pageInfo: { + endCursor: reportedComments[1].createdAt, + hasNextPage: false, }, - { - node: reportedComments[1], - cursor: reportedComments[1].createdAt, - }, - ], - pageInfo: { - endCursor: reportedComments[1].createdAt, - hasNextPage: false, - }, - }; - }), - }, - }), - }, - }), + }; + }), + }, + }), + }, + }), + }); + const { getByTestID } = within(testRenderer.root); + await waitForElement(() => getByTestID("moderate-container")); + expect(toJSON(getByTestID("moderate-main-container"))).toMatchSnapshot(); }); - const { getByTestID } = within(testRenderer.root); - await waitForElement(() => getByTestID("moderate-container")); - expect(toJSON(getByTestID("moderate-main-container"))).toMatchSnapshot(); }); it("show details of comment with flags", async () => { - const { testRenderer } = await createTestRenderer({ - resolvers: createResolversStub({ - Query: { - moderationQueues: () => - pureMerge(emptyModerationQueues, { - reported: { - count: 1, - comments: createQueryResolverStub< - ModerationQueueToCommentsResolver - >(({ variables }) => { - expectAndFail(variables).toEqual({ first: 5 }); - return { - edges: [ - { - node: reportedComments[0], - cursor: reportedComments[0].createdAt, + await act(async () => { + const { testRenderer } = await createTestRenderer({ + resolvers: createResolversStub({ + Query: { + moderationQueues: () => + pureMerge(emptyModerationQueues, { + reported: { + count: 1, + comments: createQueryResolverStub< + ModerationQueueToCommentsResolver + >(({ variables }) => { + expectAndFail(variables).toEqual({ first: 5 }); + return { + edges: [ + { + node: reportedComments[0], + cursor: reportedComments[0].createdAt, + }, + ], + pageInfo: { + endCursor: reportedComments[0].createdAt, + hasNextPage: false, }, - ], - pageInfo: { - endCursor: reportedComments[0].createdAt, - hasNextPage: false, - }, - }; - }), - }, - }), - }, - }), - }); - const { getByTestID } = within(testRenderer.root); - const reported = await waitForElement(() => - getByTestID(`moderate-comment-${reportedComments[0].id}`) - ); - expect( - within(reported).queryByText( + }; + }), + }, + }), + }, + }), + }); + const { getByTestID } = within(testRenderer.root); + const reported = await waitForElement(() => + getByTestID(`moderate-comment-${reportedComments[0].id}`) + ); + expect( + within(reported).queryByText( + reportedComments[0].flags.nodes[0].additionalDetails! + ) + ).toBeNull(); + within(reported) + .getByText("Details", { selector: "button" }) + .props.onClick(); + within(reported).getByText( reportedComments[0].flags.nodes[0].additionalDetails! - ) - ).toBeNull(); - within(reported) - .getByText("Details", { selector: "button" }) - .props.onClick(); - within(reported).getByText( - reportedComments[0].flags.nodes[0].additionalDetails! - ); + ); + }); }); it("shows a moderate story", async () => { - const { - testRenderer, - context: { transitionControl }, - } = await createTestRenderer({ - resolvers: createResolversStub({ - Query: { - moderationQueues: () => - pureMerge(emptyModerationQueues, { - reported: { - count: 2, - comments: createQueryResolverStub< - ModerationQueueToCommentsResolver - >(({ variables }) => { + await act(async () => { + const { + testRenderer, + context: { transitionControl }, + } = await createTestRenderer({ + resolvers: createResolversStub({ + Query: { + moderationQueues: () => + pureMerge(emptyModerationQueues, { + reported: { + count: 2, + comments: createQueryResolverStub< + ModerationQueueToCommentsResolver + >(({ variables }) => { + expectAndFail(variables).toEqual({ first: 5 }); + return { + edges: [ + { + node: reportedComments[0], + cursor: reportedComments[0].createdAt, + }, + { + node: reportedComments[1], + cursor: reportedComments[1].createdAt, + }, + ], + pageInfo: { + endCursor: reportedComments[1].createdAt, + hasNextPage: false, + }, + }; + }) as any, + }, + }), + }, + }), + }); + const moderateStory = await waitForElement( + () => within(testRenderer.root).getAllByText("Moderate Story")[0] + ); + transitionControl.allowTransition = false; + moderateStory.props.onClick({}); + // Expect a routing request was made to the right url. + expect(transitionControl.history[0].pathname).toBe( + `/admin/moderate/${reportedComments[0].story.id}` + ); + }); + }); + it("renders reported queue with comments and load more", async () => { + await act(async () => { + const moderationQueuesStub = pureMerge(emptyModerationQueues, { + reported: { + count: 2, + comments: createQueryResolverStub( + ({ variables, callCount }) => { + switch (callCount) { + case 0: expectAndFail(variables).toEqual({ first: 5 }); return { edges: [ @@ -484,273 +568,237 @@ describe("reported queue", () => { ], pageInfo: { endCursor: reportedComments[1].createdAt, + hasNextPage: true, + }, + }; + default: + expectAndFail(variables).toEqual({ + first: 10, + after: reportedComments[1].createdAt, + }); + return { + edges: [ + { + node: reportedComments[2], + cursor: reportedComments[2].createdAt, + }, + ], + pageInfo: { + endCursor: reportedComments[2].createdAt, hasNextPage: false, }, }; - }) as any, - }, - }), - }, - }), - }); - const moderateStory = await waitForElement( - () => within(testRenderer.root).getAllByText("Moderate Story")[0] - ); - transitionControl.allowTransition = false; - moderateStory.props.onClick({}); - // Expect a routing request was made to the right url. - expect(transitionControl.history[0].pathname).toBe( - `/admin/moderate/${reportedComments[0].story.id}` - ); - }); - it("renders reported queue with comments and load more", async () => { - const moderationQueuesStub = pureMerge(emptyModerationQueues, { - reported: { - count: 2, - comments: createQueryResolverStub( - ({ variables, callCount }) => { - switch (callCount) { - case 0: - expectAndFail(variables).toEqual({ first: 5 }); - return { - edges: [ - { - node: reportedComments[0], - cursor: reportedComments[0].createdAt, - }, - { - node: reportedComments[1], - cursor: reportedComments[1].createdAt, - }, - ], - pageInfo: { - endCursor: reportedComments[1].createdAt, - hasNextPage: true, - }, - }; - default: - expectAndFail(variables).toEqual({ - first: 10, - after: reportedComments[1].createdAt, - }); - return { - edges: [ - { - node: reportedComments[2], - cursor: reportedComments[2].createdAt, - }, - ], - pageInfo: { - endCursor: reportedComments[2].createdAt, - hasNextPage: false, - }, - }; + } } - } - ) as any, - }, - }); - - const { testRenderer } = await createTestRenderer({ - resolvers: createResolversStub({ - Query: { - moderationQueues: () => moderationQueuesStub, + ) as any, }, - }), + }); + + const { testRenderer } = await createTestRenderer({ + resolvers: createResolversStub({ + Query: { + moderationQueues: () => moderationQueuesStub, + }, + }), + }); + const moderateContainer = await waitForElement(() => + within(testRenderer.root).getByTestID("moderate-container") + ); + + const { getByText, getAllByTestID, getByTestID } = within( + moderateContainer + ); + + // Get previous count of comments. + const previousCount = getAllByTestID(/^moderate-comment-.*$/).length; + + const loadMore = await waitForElement(() => getByText("Load More")); + loadMore.props.onClick(); + + // Wait for load more to disappear. + await waitUntilThrow(() => getByText("Load More")); + + // Verify we have one more item now. + const comments = getAllByTestID(/^moderate-comment-.*$/); + expect(comments.length).toBe(previousCount + 1); + + // Verify last one added was our new one + expect(comments[comments.length - 1].props["data-testid"]).toBe( + `moderate-comment-${reportedComments[2].id}` + ); + + // Snapshot of added comment. + expect( + toJSON(getByTestID(`moderate-comment-${reportedComments[2].id}`)) + ).toMatchSnapshot(); }); - const moderateContainer = await waitForElement(() => - within(testRenderer.root).getByTestID("moderate-container") - ); - - const { getByText, getAllByTestID, getByTestID } = within( - moderateContainer - ); - - // Get previous count of comments. - const previousCount = getAllByTestID(/^moderate-comment-.*$/).length; - - const loadMore = await waitForElement(() => getByText("Load More")); - loadMore.props.onClick(); - - // Wait for load more to disappear. - await waitUntilThrow(() => getByText("Load More")); - - // Verify we have one more item now. - const comments = getAllByTestID(/^moderate-comment-.*$/); - expect(comments.length).toBe(previousCount + 1); - - // Verify last one added was our new one - expect(comments[comments.length - 1].props["data-testid"]).toBe( - `moderate-comment-${reportedComments[2].id}` - ); - - // Snapshot of added comment. - expect( - toJSON(getByTestID(`moderate-comment-${reportedComments[2].id}`)) - ).toMatchSnapshot(); }); it("approves comment in reported queue", async () => { - const approveCommentStub = createMutationResolverStub< - MutationToApproveCommentResolver - >(({ variables }) => { - expectAndFail(variables).toMatchObject({ - commentID: reportedComments[0].id, - commentRevisionID: reportedComments[0].revision.id, + await act(async () => { + const approveCommentStub = createMutationResolverStub< + MutationToApproveCommentResolver + >(({ variables }) => { + expectAndFail(variables).toMatchObject({ + commentID: reportedComments[0].id, + commentRevisionID: reportedComments[0].revision.id, + }); + return { + comment: { + id: reportedComments[0].id, + status: GQLCOMMENT_STATUS.APPROVED, + }, + moderationQueues: pureMerge(emptyModerationQueues, { + reported: { + count: 1, + }, + }), + }; }); - return { - comment: { - id: reportedComments[0].id, - status: GQLCOMMENT_STATUS.APPROVED, + + const moderationQueuesStub = pureMerge(emptyModerationQueues, { + reported: { + count: 2, + comments: createQueryResolverStub( + ({ variables }) => { + expectAndFail(variables).toEqual({ first: 5 }); + return { + edges: [ + { + node: reportedComments[0], + cursor: reportedComments[0].createdAt, + }, + { + node: reportedComments[1], + cursor: reportedComments[1].createdAt, + }, + ], + pageInfo: { + endCursor: reportedComments[1].createdAt, + hasNextPage: false, + }, + }; + } + ) as any, }, - moderationQueues: pureMerge(emptyModerationQueues, { - reported: { - count: 1, + }); + + const { testRenderer } = await createTestRenderer({ + resolvers: createResolversStub({ + Query: { + moderationQueues: () => moderationQueuesStub, + }, + Mutation: { + approveComment: approveCommentStub, }, }), - }; + }); + + const testID = `moderate-comment-${reportedComments[0].id}`; + const { getByTestID } = within(testRenderer.root); + const comment = await waitForElement(() => getByTestID(testID)); + + const ApproveButton = await waitForElement(() => + within(comment).getByLabelText("Approve") + ); + ApproveButton.props.onClick(); + + // Snapshot dangling state of comment. + expect(toJSON(comment)).toMatchSnapshot("dangling"); + + // Wait until comment is gone. + await waitUntilThrow(() => getByTestID(testID)); + + expect(approveCommentStub.called).toBe(true); + + // Count should have been updated. + expect( + toJSON(getByTestID("moderate-navigation-reported-count")) + ).toMatchSnapshot("count should be 1"); }); - - const moderationQueuesStub = pureMerge(emptyModerationQueues, { - reported: { - count: 2, - comments: createQueryResolverStub( - ({ variables }) => { - expectAndFail(variables).toEqual({ first: 5 }); - return { - edges: [ - { - node: reportedComments[0], - cursor: reportedComments[0].createdAt, - }, - { - node: reportedComments[1], - cursor: reportedComments[1].createdAt, - }, - ], - pageInfo: { - endCursor: reportedComments[1].createdAt, - hasNextPage: false, - }, - }; - } - ) as any, - }, - }); - - const { testRenderer } = await createTestRenderer({ - resolvers: createResolversStub({ - Query: { - moderationQueues: () => moderationQueuesStub, - }, - Mutation: { - approveComment: approveCommentStub, - }, - }), - }); - - const testID = `moderate-comment-${reportedComments[0].id}`; - const { getByTestID } = within(testRenderer.root); - const comment = await waitForElement(() => getByTestID(testID)); - - const ApproveButton = await waitForElement(() => - within(comment).getByLabelText("Approve") - ); - ApproveButton.props.onClick(); - - // Snapshot dangling state of comment. - expect(toJSON(comment)).toMatchSnapshot("dangling"); - - // Wait until comment is gone. - await waitUntilThrow(() => getByTestID(testID)); - - expect(approveCommentStub.called).toBe(true); - - // Count should have been updated. - expect( - toJSON(getByTestID("moderate-navigation-reported-count")) - ).toMatchSnapshot("count should be 1"); }); it("rejects comment in reported queue", async () => { - const rejectCommentStub = createMutationResolverStub< - MutationToRejectCommentResolver - >(({ variables }) => { - expectAndFail(variables).toMatchObject({ - commentID: reportedComments[0].id, - commentRevisionID: reportedComments[0].revision.id, + await act(async () => { + const rejectCommentStub = createMutationResolverStub< + MutationToRejectCommentResolver + >(({ variables }) => { + expectAndFail(variables).toMatchObject({ + commentID: reportedComments[0].id, + commentRevisionID: reportedComments[0].revision.id, + }); + return { + comment: { + id: reportedComments[0].id, + status: GQLCOMMENT_STATUS.REJECTED, + }, + moderationQueues: pureMerge(emptyModerationQueues, { + reported: { + count: 1, + }, + }), + }; }); - return { - comment: { - id: reportedComments[0].id, - status: GQLCOMMENT_STATUS.REJECTED, - }, - moderationQueues: pureMerge(emptyModerationQueues, { - reported: { - count: 1, + + const { testRenderer } = await createTestRenderer({ + resolvers: createResolversStub({ + Query: { + moderationQueues: () => + pureMerge(emptyModerationQueues, { + reported: { + count: 2, + comments: createQueryResolverStub< + ModerationQueueToCommentsResolver + >(({ variables }) => { + expectAndFail(variables).toEqual({ first: 5 }); + return { + edges: [ + { + node: reportedComments[0], + cursor: reportedComments[0].createdAt, + }, + { + node: reportedComments[1], + cursor: reportedComments[1].createdAt, + }, + ], + pageInfo: { + endCursor: reportedComments[1].createdAt, + hasNextPage: false, + }, + }; + }) as any, + }, + }), + }, + Mutation: { + rejectComment: rejectCommentStub, }, }), - }; + }); + + const testID = `moderate-comment-${reportedComments[0].id}`; + const { getByTestID } = within(testRenderer.root); + const comment = await waitForElement(() => getByTestID(testID)); + + const RejectButton = await waitForElement(() => + within(comment).getByLabelText("Reject") + ); + RejectButton.props.onClick(); + + // Snapshot dangling state of comment. + expect(toJSON(comment)).toMatchSnapshot("dangling"); + + // Wait until comment is gone. + await waitUntilThrow(() => getByTestID(testID)); + + expect(rejectCommentStub.called).toBe(true); + + // Count should have been updated. + expect( + toJSON(getByTestID("moderate-navigation-reported-count")) + ).toMatchSnapshot("count should be 1"); }); - - const { testRenderer } = await createTestRenderer({ - resolvers: createResolversStub({ - Query: { - moderationQueues: () => - pureMerge(emptyModerationQueues, { - reported: { - count: 2, - comments: createQueryResolverStub< - ModerationQueueToCommentsResolver - >(({ variables }) => { - expectAndFail(variables).toEqual({ first: 5 }); - return { - edges: [ - { - node: reportedComments[0], - cursor: reportedComments[0].createdAt, - }, - { - node: reportedComments[1], - cursor: reportedComments[1].createdAt, - }, - ], - pageInfo: { - endCursor: reportedComments[1].createdAt, - hasNextPage: false, - }, - }; - }) as any, - }, - }), - }, - Mutation: { - rejectComment: rejectCommentStub, - }, - }), - }); - - const testID = `moderate-comment-${reportedComments[0].id}`; - const { getByTestID } = within(testRenderer.root); - const comment = await waitForElement(() => getByTestID(testID)); - - const RejectButton = await waitForElement(() => - within(comment).getByLabelText("Reject") - ); - RejectButton.props.onClick(); - - // Snapshot dangling state of comment. - expect(toJSON(comment)).toMatchSnapshot("dangling"); - - // Wait until comment is gone. - await waitUntilThrow(() => getByTestID(testID)); - - expect(rejectCommentStub.called).toBe(true); - - // Count should have been updated. - expect( - toJSON(getByTestID("moderate-navigation-reported-count")) - ).toMatchSnapshot("count should be 1"); }); }); diff --git a/src/core/client/admin/test/stories/stories.spec.tsx b/src/core/client/admin/test/stories/stories.spec.tsx index 65a7d876f..824993ba4 100644 --- a/src/core/client/admin/test/stories/stories.spec.tsx +++ b/src/core/client/admin/test/stories/stories.spec.tsx @@ -1,8 +1,8 @@ import { noop } from "lodash"; -import TestRenderer from "react-test-renderer"; import { pureMerge } from "coral-common/utils"; import { + act, createMutationResolverStub, createResolversStub, CreateTestRendererParams, @@ -92,10 +92,11 @@ it("goes to moderation when clicking on title", async () => { transitionControl.allowTransition = false; const story = storyConnection.edges[0].node; - within(container) - .getByText(story.metadata!.title!) - .props.onClick({ button: 0, preventDefault: noop }); - + act(() => { + within(container) + .getByText(story.metadata!.title!) + .props.onClick({ button: 0, preventDefault: noop }); + }); // Expect a routing request was made to the right url. expect(transitionControl.history[0].pathname).toBe( `/admin/moderate/${story.id}` @@ -122,17 +123,14 @@ it("filter by status", async () => { const selectField = within(container).getByLabelText("Search by status"); const closedOption = within(selectField).getByText("Closed Stories"); - TestRenderer.act(() => { + await act(async () => { selectField.props.onChange({ target: { value: closedOption.props.value.toString() }, }); - // TODO: Fix act warnings until await Promise.resolve(); - // or whatever comes out at https://github.com/facebook/react/issues/14769 + await waitForElement(() => + within(container).getByText("could not find any", { exact: false }) + ); }); - - await waitForElement(() => - within(container).getByText("could not find any", { exact: false }) - ); }); it("change story status", async () => { @@ -185,11 +183,11 @@ it("change story status", async () => { ); /** CLOSE STORY */ - TestRenderer.act(() => { + act(() => { changeStatusButton.props.onClick(); }); - TestRenderer.act(() => { + act(() => { within(popup) .getByText("Closed", { selector: "button" }) .props.onClick(); @@ -199,11 +197,11 @@ it("change story status", async () => { expect(closeStory.called).toBe(true); /** OPEN STORY */ - TestRenderer.act(() => { + act(() => { changeStatusButton.props.onClick(); }); - TestRenderer.act(() => { + act(() => { within(popup) .getByText("Open", { selector: "button" }) .props.onClick(); @@ -244,13 +242,12 @@ it("load more", async () => { }), }); const loadMore = within(container).getByText("Load More"); - TestRenderer.act(() => { + await act(async () => { loadMore.props.onClick(); + // Wait for load more to disappear. + await waitUntilThrow(() => within(container).getByText("Load More")); }); - // Wait for load more to disappear. - await waitUntilThrow(() => within(container).getByText("Load More")); - // Make sure third user was added. within(container).getByText(stories[2].metadata!.title!); }); @@ -278,16 +275,15 @@ it("filter by search", async () => { ); const form = findParentWithType(searchField, "form")!; - TestRenderer.act(() => { + await act(async () => { searchField.props.onChange({ target: { value: "search" }, }); form.props.onSubmit(); + await waitForElement(() => + within(container).getByText("could not find any", { exact: false }) + ); }); - - await waitForElement(() => - within(container).getByText("could not find any", { exact: false }) - ); }); it("use searchFilter from url", async () => { diff --git a/src/core/client/auth/test/forgotPassword.spec.tsx b/src/core/client/auth/test/forgotPassword.spec.tsx index 7cebfed52..3d39552b7 100644 --- a/src/core/client/auth/test/forgotPassword.spec.tsx +++ b/src/core/client/auth/test/forgotPassword.spec.tsx @@ -1,9 +1,9 @@ -import { act } from "react-test-renderer"; import sinon from "sinon"; import { pureMerge } from "coral-common/utils"; import { GQLResolver } from "coral-framework/schema"; import { + act, createResolversStub, CreateTestRendererParams, wait, @@ -134,14 +134,14 @@ it("submits form successfully", async () => { }) .once(); - act(() => { + await act(async () => { emailField.props.onChange("hans@test.com"); form!.props.onSubmit(); + await waitForElement(() => + within(testRenderer.root).getByText("Check Your Email", { exact: false }) + ); }); - await waitForElement(() => - within(testRenderer.root).getByText("Check Your Email", { exact: false }) - ); within(testRenderer.root) .getByText("Close") .props.onClick(); diff --git a/src/core/client/framework/testHelpers/act.ts b/src/core/client/framework/testHelpers/act.ts new file mode 100644 index 000000000..38dfbf01f --- /dev/null +++ b/src/core/client/framework/testHelpers/act.ts @@ -0,0 +1,7 @@ +import TestRenderer from "react-test-renderer"; + +export default function act( + callback: () => Promise | void | undefined +): Promise | void { + return TestRenderer.act(callback as any) as any; +} diff --git a/src/core/client/framework/testHelpers/index.ts b/src/core/client/framework/testHelpers/index.ts index 7a9b9d109..62225877d 100644 --- a/src/core/client/framework/testHelpers/index.ts +++ b/src/core/client/framework/testHelpers/index.ts @@ -10,6 +10,7 @@ export { } from "./removeFragmentRefs"; export { default as createUUIDGenerator } from "./createUUIDGenerator"; export * from "./denormalize"; +export { default as act } from "./act"; export { default as limitSnapshotTo } from "./limitSnapshotTo"; export { default as within } from "./within"; export { default as wait } from "./wait"; diff --git a/src/core/client/stream/test/comments/stream/ignoreUser.spec.tsx b/src/core/client/stream/test/comments/stream/ignoreUser.spec.tsx index cc74aafce..fff29dbc4 100644 --- a/src/core/client/stream/test/comments/stream/ignoreUser.spec.tsx +++ b/src/core/client/stream/test/comments/stream/ignoreUser.spec.tsx @@ -1,6 +1,7 @@ import { pureMerge } from "coral-common/utils"; import { GQLResolver } from "coral-framework/schema"; import { + act, createResolversStub, CreateTestRendererParams, waitForElement, @@ -69,23 +70,29 @@ it("ignore user", async () => { const username = within(comment).getByText(firstCommentAuthor.username!, { selector: "button", }); - username.props.onClick(); + act(() => { + username.props.onClick(); + }); const ignoreButton = within(comment).getByText("Ignore", { selector: "button", }); - ignoreButton.props.onClick(); + act(() => { + ignoreButton.props.onClick(); + }); within(comment).getByText("Cancel", { selector: "button", }); - within(comment) - .getByText("Ignore", { - selector: "button", - }) - .props.onClick(); - // Check for a tombstone - await waitForElement(() => - within(tabPane).getByText("This comment is hidden", { exact: false }) - ); + await act(async () => { + within(comment) + .getByText("Ignore", { + selector: "button", + }) + .props.onClick(); + // Check for a tombstone + await waitForElement(() => + within(tabPane).getByText("This comment is hidden", { exact: false }) + ); + }); }); it("render stream with ignored user", async () => { diff --git a/src/core/client/ui/components/MatchMedia/__snapshots__/MatchMedia.spec.tsx.snap b/src/core/client/ui/components/MatchMedia/__snapshots__/MatchMedia.spec.tsx.snap index 88670e33f..516f3ae04 100644 --- a/src/core/client/ui/components/MatchMedia/__snapshots__/MatchMedia.spec.tsx.snap +++ b/src/core/client/ui/components/MatchMedia/__snapshots__/MatchMedia.spec.tsx.snap @@ -1,38 +1,35 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`map new speech prop to older aural prop 1`] = ` -
Hello World
-
+ `; exports[`renders correctly 1`] = ` -
Hello World
-
+ `; exports[`renders less than and great than correctly 1`] = ` -
Hello World
-
+ `;