Use react-testing-library instead of enzyme for useDebouncedCallback

This commit is contained in:
Florian Duros 2022-11-17 12:01:31 +01:00
parent 266716e888
commit d5e076bc3d
No known key found for this signature in database
GPG key ID: 9700AA5870258A0B
3 changed files with 94 additions and 64 deletions

View file

@ -61,6 +61,7 @@
"@matrix-org/react-sdk-module-api": "^0.0.3", "@matrix-org/react-sdk-module-api": "^0.0.3",
"@sentry/browser": "^6.11.0", "@sentry/browser": "^6.11.0",
"@sentry/tracing": "^6.11.0", "@sentry/tracing": "^6.11.0",
"@testing-library/react-hooks": "^8.0.1",
"@types/geojson": "^7946.0.8", "@types/geojson": "^7946.0.8",
"@types/ua-parser-js": "^0.7.36", "@types/ua-parser-js": "^0.7.36",
"await-lock": "^2.1.0", "await-lock": "^2.1.0",

View file

@ -14,70 +14,85 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
// eslint-disable-next-line deprecate/import import { renderHook } from "@testing-library/react-hooks";
import { mount } from "enzyme";
import { sleep } from "matrix-js-sdk/src/utils";
import React from "react";
import { act } from "react-dom/test-utils";
import { useDebouncedCallback } from "../../src/hooks/spotlight/useDebouncedCallback"; import { useDebouncedCallback } from "../../src/hooks/spotlight/useDebouncedCallback";
function DebouncedCallbackComponent({ enabled, params, callback }) {
useDebouncedCallback(enabled, callback, params);
return <div>
{ JSON.stringify(params) }
</div>;
}
describe("useDebouncedCallback", () => { describe("useDebouncedCallback", () => {
beforeAll(() => jest.useFakeTimers());
afterAll(() => jest.useRealTimers());
function render(enabled: boolean, callback: (...params: any) => void, params: any) {
return renderHook(
({ enabled, callback, params }) => useDebouncedCallback(enabled, callback, params),
{ initialProps: {
enabled,
callback,
params,
} });
}
it("should be able to handle empty parameters", async () => { it("should be able to handle empty parameters", async () => {
// When
const params = []; const params = [];
const callback = jest.fn(); const callback = jest.fn();
render(true, callback, params);
jest.advanceTimersByTime(1);
const wrapper = mount(<DebouncedCallbackComponent callback={callback} enabled={true} params={[]} />); // Then
await act(async () => { expect(callback).toHaveBeenCalledTimes(0);
await sleep(1);
wrapper.setProps({ enabled: true, params, callback });
return act(() => sleep(500));
});
expect(wrapper.text()).toContain(JSON.stringify(params)); // When
jest.advanceTimersByTime(500);
// Then
expect(callback).toHaveBeenCalledTimes(1); expect(callback).toHaveBeenCalledTimes(1);
}); });
it("should call the callback with the parameters", async () => { it("should call the callback with the parameters", async () => {
// When
const params = ["USER NAME"]; const params = ["USER NAME"];
const callback = jest.fn(); const callback = jest.fn();
render(true, callback, params);
jest.advanceTimersByTime(500);
const wrapper = mount(<DebouncedCallbackComponent callback={callback} enabled={true} params={[]} />); // Then
await act(async () => { expect(callback).toHaveBeenCalledTimes(1);
await sleep(1); expect(callback).toHaveBeenCalledWith(...params);
wrapper.setProps({ enabled: true, params, callback }); });
return act(() => sleep(500));
});
expect(wrapper.text()).toContain(JSON.stringify(params)); it("should call the callback with the parameters when parameters change during the timeout", async () => {
// When
const params = ["USER NAME"];
const callback = jest.fn();
const { rerender } = render(true, callback, []);
jest.advanceTimersByTime(1);
rerender({ enabled: true, callback, params });
jest.advanceTimersByTime(500);
// Then
expect(callback).toHaveBeenCalledTimes(1); expect(callback).toHaveBeenCalledTimes(1);
expect(callback).toHaveBeenCalledWith(...params); expect(callback).toHaveBeenCalledWith(...params);
}); });
it("should handle multiple parameters", async () => { it("should handle multiple parameters", async () => {
// When
const params = [4, 8, 15, 16, 23, 42]; const params = [4, 8, 15, 16, 23, 42];
const callback = jest.fn(); const callback = jest.fn();
const { rerender } = render(true, callback, []);
const wrapper = mount(<DebouncedCallbackComponent callback={callback} enabled={true} params={[]} />); jest.advanceTimersByTime(1);
await act(async () => { rerender({ enabled: true, callback, params });
await sleep(1); jest.advanceTimersByTime(500);
wrapper.setProps({ enabled: true, params, callback });
return act(() => sleep(500));
});
expect(wrapper.text()).toContain(JSON.stringify(params)); // Then
expect(callback).toHaveBeenCalledTimes(1); expect(callback).toHaveBeenCalledTimes(1);
expect(callback).toHaveBeenCalledWith(...params); expect(callback).toHaveBeenCalledWith(...params);
}); });
it("should debounce quick changes", async () => { it("should debounce quick changes", async () => {
// When
const queries = [ const queries = [
"U", "U",
"US", "US",
@ -95,23 +110,24 @@ describe("useDebouncedCallback", () => {
]; ];
const callback = jest.fn(); const callback = jest.fn();
const wrapper = mount(<DebouncedCallbackComponent callback={callback} enabled={true} params={[]} />); const { rerender } = render(true, callback, []);
await act(async () => { jest.advanceTimersByTime(1);
await sleep(1);
for (const query of queries) {
wrapper.setProps({ enabled: true, params: [query], callback });
await sleep(50);
}
return act(() => sleep(500));
});
for (const query of queries) {
rerender({ enabled: true, callback, params: [query] });
jest.advanceTimersByTime(50);
}
jest.advanceTimersByTime(500);
// Then
const query = queries[queries.length - 1]; const query = queries[queries.length - 1];
expect(wrapper.text()).toContain(JSON.stringify(query));
expect(callback).toHaveBeenCalledTimes(1); expect(callback).toHaveBeenCalledTimes(1);
expect(callback).toHaveBeenCalledWith(query); expect(callback).toHaveBeenCalledWith(query);
}); });
it("should not debounce slow changes", async () => { it("should not debounce slow changes", async () => {
// When
const queries = [ const queries = [
"U", "U",
"US", "US",
@ -129,23 +145,23 @@ describe("useDebouncedCallback", () => {
]; ];
const callback = jest.fn(); const callback = jest.fn();
const wrapper = mount(<DebouncedCallbackComponent callback={callback} enabled={true} params={[]} />); const { rerender } = render(true, callback, []);
await act(async () => { jest.advanceTimersByTime(1);
await sleep(1); for (const query of queries) {
for (const query of queries) { rerender({ enabled: true, callback, params: [query] });
wrapper.setProps({ enabled: true, params: [query], callback }); jest.advanceTimersByTime(200);
await sleep(200); }
}
return act(() => sleep(500));
});
jest.advanceTimersByTime(500);
// Then
const query = queries[queries.length - 1]; const query = queries[queries.length - 1];
expect(wrapper.text()).toContain(JSON.stringify(query));
expect(callback).toHaveBeenCalledTimes(queries.length); expect(callback).toHaveBeenCalledTimes(queries.length);
expect(callback).toHaveBeenCalledWith(query); expect(callback).toHaveBeenCalledWith(query);
}); });
it("should not call the callback if its disabled", async () => { it("should not call the callback if its disabled", async () => {
// When
const queries = [ const queries = [
"U", "U",
"US", "US",
@ -163,18 +179,16 @@ describe("useDebouncedCallback", () => {
]; ];
const callback = jest.fn(); const callback = jest.fn();
const wrapper = mount(<DebouncedCallbackComponent callback={callback} enabled={false} params={[]} />); const { rerender } = render(false, callback, []);
await act(async () => { jest.advanceTimersByTime(1);
await sleep(1); for (const query of queries) {
for (const query of queries) { rerender({ enabled: false, callback, params: [query] });
wrapper.setProps({ enabled: false, params: [query], callback }); jest.advanceTimersByTime(200);
await sleep(200); }
}
return act(() => sleep(500));
});
const query = queries[queries.length - 1]; jest.advanceTimersByTime(500);
expect(wrapper.text()).toContain(JSON.stringify(query));
// Then
expect(callback).toHaveBeenCalledTimes(0); expect(callback).toHaveBeenCalledTimes(0);
}); });
}); });

View file

@ -2205,6 +2205,14 @@
lodash "^4.17.15" lodash "^4.17.15"
redent "^3.0.0" redent "^3.0.0"
"@testing-library/react-hooks@^8.0.1":
version "8.0.1"
resolved "https://registry.yarnpkg.com/@testing-library/react-hooks/-/react-hooks-8.0.1.tgz#0924bbd5b55e0c0c0502d1754657ada66947ca12"
integrity sha512-Aqhl2IVmLt8IovEVarNDFuJDVWVvhnr9/GCU6UUnrYXwgDFF9h2L2o2P9KBni1AST5sT6riAyoukFLyjQUgD/g==
dependencies:
"@babel/runtime" "^7.12.5"
react-error-boundary "^3.1.0"
"@testing-library/react@^12.1.5": "@testing-library/react@^12.1.5":
version "12.1.5" version "12.1.5"
resolved "https://registry.yarnpkg.com/@testing-library/react/-/react-12.1.5.tgz#bb248f72f02a5ac9d949dea07279095fa577963b" resolved "https://registry.yarnpkg.com/@testing-library/react/-/react-12.1.5.tgz#bb248f72f02a5ac9d949dea07279095fa577963b"
@ -8185,6 +8193,13 @@ react-dom@17.0.2:
object-assign "^4.1.1" object-assign "^4.1.1"
scheduler "^0.20.2" scheduler "^0.20.2"
react-error-boundary@^3.1.0:
version "3.1.4"
resolved "https://registry.yarnpkg.com/react-error-boundary/-/react-error-boundary-3.1.4.tgz#255db92b23197108757a888b01e5b729919abde0"
integrity sha512-uM9uPzZJTF6wRQORmSrvOIgt4lJ9MC1sNgEOj2XGsDTRE4kmpWxg7ENK9EWNKJRMAOY9z0MuF4yIfl6gp4sotA==
dependencies:
"@babel/runtime" "^7.12.5"
react-focus-lock@^2.5.1: react-focus-lock@^2.5.1:
version "2.9.1" version "2.9.1"
resolved "https://registry.yarnpkg.com/react-focus-lock/-/react-focus-lock-2.9.1.tgz#094cfc19b4f334122c73bb0bff65d77a0c92dd16" resolved "https://registry.yarnpkg.com/react-focus-lock/-/react-focus-lock-2.9.1.tgz#094cfc19b4f334122c73bb0bff65d77a0c92dd16"