From 2659e48c895c43175db487220baa298b8f6aab71 Mon Sep 17 00:00:00 2001 From: Gil Barbara Date: Sun, 2 May 2021 01:36:56 -0300 Subject: [PATCH] Fix race condition with rapid src changes --- package-lock.json | 65 ++++++++++++++++++++++++++ package.json | 1 + src/index.tsx | 7 +++ test/__snapshots__/index.spec.tsx.snap | 26 +++++++++++ test/index.spec.tsx | 65 +++++++++++++++++++++++++- 5 files changed, 162 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 2b54a42..6087ee0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,6 +12,7 @@ "react-from-dom": "^0.6.0" }, "devDependencies": { + "@gilbarbara/helpers": "^0.2.0", "@gilbarbara/tsconfig": "^0.1.0", "@size-limit/preset-small-lib": "^4.10.2", "@types/enzyme": "^3.10.8", @@ -685,6 +686,16 @@ "node": ">=8" } }, + "node_modules/@gilbarbara/helpers": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@gilbarbara/helpers/-/helpers-0.2.0.tgz", + "integrity": "sha512-HDp7eejkCH3YUnQExNBFzt9043aGt7a6Kg/g+onO37K5AzYaHK5mhWU21EQFwiqOnlIRXD8KGUR0MDYc45iVdQ==", + "dev": true, + "dependencies": { + "is-lite": "^0.8.0", + "isomorphic-fetch": "^3.0.0" + } + }, "node_modules/@gilbarbara/tsconfig": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/@gilbarbara/tsconfig/-/tsconfig-0.1.0.tgz", @@ -8164,6 +8175,12 @@ "node": ">=8" } }, + "node_modules/is-lite": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/is-lite/-/is-lite-0.8.0.tgz", + "integrity": "sha512-w/+euF/pELyAgLA6Pai8Vp3l2EMJygQx1wGkKf/Gu7/qTWxZvYFKrdVF4qbi9YdVmISzB/jE4Q7yO9alrHQa8A==", + "dev": true + }, "node_modules/is-negative-zero": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.1.tgz", @@ -8376,6 +8393,16 @@ "node": ">=0.10.0" } }, + "node_modules/isomorphic-fetch": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/isomorphic-fetch/-/isomorphic-fetch-3.0.0.tgz", + "integrity": "sha512-qvUtwJ3j6qwsF3jLxkZ72qCgjMysPzDfeV240JHiGZsANBYd+EEuu35v7dfrJ9Up0Ak07D7GGSkGhCHTqg/5wA==", + "dev": true, + "dependencies": { + "node-fetch": "^2.6.1", + "whatwg-fetch": "^3.4.1" + } + }, "node_modules/isstream": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", @@ -20071,6 +20098,12 @@ "iconv-lite": "0.4.24" } }, + "node_modules/whatwg-fetch": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.6.2.tgz", + "integrity": "sha512-bJlen0FcuU/0EMLrdbJ7zOnW6ITZLrZMIarMUVmdKtsGvZna8vxKYaexICWPfZ8qwf9fzNq+UEIZrnSaApt6RA==", + "dev": true + }, "node_modules/whatwg-mimetype": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz", @@ -20853,6 +20886,16 @@ } } }, + "@gilbarbara/helpers": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@gilbarbara/helpers/-/helpers-0.2.0.tgz", + "integrity": "sha512-HDp7eejkCH3YUnQExNBFzt9043aGt7a6Kg/g+onO37K5AzYaHK5mhWU21EQFwiqOnlIRXD8KGUR0MDYc45iVdQ==", + "dev": true, + "requires": { + "is-lite": "^0.8.0", + "isomorphic-fetch": "^3.0.0" + } + }, "@gilbarbara/tsconfig": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/@gilbarbara/tsconfig/-/tsconfig-0.1.0.tgz", @@ -26862,6 +26905,12 @@ "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", "dev": true }, + "is-lite": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/is-lite/-/is-lite-0.8.0.tgz", + "integrity": "sha512-w/+euF/pELyAgLA6Pai8Vp3l2EMJygQx1wGkKf/Gu7/qTWxZvYFKrdVF4qbi9YdVmISzB/jE4Q7yO9alrHQa8A==", + "dev": true + }, "is-negative-zero": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.1.tgz", @@ -27008,6 +27057,16 @@ "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", "dev": true }, + "isomorphic-fetch": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/isomorphic-fetch/-/isomorphic-fetch-3.0.0.tgz", + "integrity": "sha512-qvUtwJ3j6qwsF3jLxkZ72qCgjMysPzDfeV240JHiGZsANBYd+EEuu35v7dfrJ9Up0Ak07D7GGSkGhCHTqg/5wA==", + "dev": true, + "requires": { + "node-fetch": "^2.6.1", + "whatwg-fetch": "^3.4.1" + } + }, "isstream": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", @@ -36426,6 +36485,12 @@ "iconv-lite": "0.4.24" } }, + "whatwg-fetch": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.6.2.tgz", + "integrity": "sha512-bJlen0FcuU/0EMLrdbJ7zOnW6ITZLrZMIarMUVmdKtsGvZna8vxKYaexICWPfZ8qwf9fzNq+UEIZrnSaApt6RA==", + "dev": true + }, "whatwg-mimetype": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz", diff --git a/package.json b/package.json index 6b91a48..93a50e6 100644 --- a/package.json +++ b/package.json @@ -39,6 +39,7 @@ "react-from-dom": "^0.6.0" }, "devDependencies": { + "@gilbarbara/helpers": "^0.2.0", "@gilbarbara/tsconfig": "^0.1.0", "@size-limit/preset-small-lib": "^4.10.2", "@types/enzyme": "^3.10.8", diff --git a/src/index.tsx b/src/index.tsx index 5a141aa..69e3aed 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -315,6 +315,13 @@ export default class InlineSVG extends React.PureComponent { return response.text(); }) .then((content) => { + const { src: currentSrc } = this.props; + + // the current src don't match the previous one, skipping... + if (src !== currentSrc) { + return; + } + this.handleLoad(content); /* istanbul ignore else */ diff --git a/test/__snapshots__/index.spec.tsx.snap b/test/__snapshots__/index.spec.tsx.snap index 7b0d3f4..ec15af4 100644 --- a/test/__snapshots__/index.spec.tsx.snap +++ b/test/__snapshots__/index.spec.tsx.snap @@ -2206,6 +2206,32 @@ exports[`react-inlinesvg cached requests should skip the cache if \`cacheRequest `; +exports[`react-inlinesvg integration should handle race condition with fast src changes 1`] = ` + + + + + + + + + + +`; + exports[`react-inlinesvg with errors should trigger an error and render the fallback children if src is not found 1`] = `
diff --git a/test/index.spec.tsx b/test/index.spec.tsx index 720ebf9..67be390 100644 --- a/test/index.spec.tsx +++ b/test/index.spec.tsx @@ -1,4 +1,5 @@ import * as React from 'react'; +import { poll } from '@gilbarbara/helpers'; import { mount, ReactWrapper } from 'enzyme'; import fetchMock from 'jest-fetch-mock'; @@ -50,8 +51,6 @@ function setup({ onLoad, ...rest }: Props): Promise> { {...rest} />, ); - - return wrapper; }); } @@ -387,6 +386,68 @@ describe('react-inlinesvg', () => { }); }); + describe('integration', () => { + it('should handle race condition with fast src changes', async () => { + const mockOnLoad = jest.fn(); + + const wrapper = mount( + , + ); + + wrapper.setProps({ src: fixtures.url2 }); + + await poll(() => !!mockOnLoad.mock.calls.length); + wrapper.update(); + + expect(mockOnLoad).toHaveBeenCalledWith(fixtures.url2, false); + expect(wrapper.html()).toMatchSnapshot(); + }); + + it('should render multiple SVGs', async () => { + const mockOnLoad = jest.fn(); + + const multiSetup: () => Promise> = () => { + return new Promise((resolve) => { + const onLoad = (wrapper: any, ...rest: any[]) => { + mockOnLoad(...rest); + + if (mockOnLoad.mock.calls.length === 4) { + resolve(wrapper); + } + }; + + const wrapper = mount( +
+ onLoad(wrapper, ...args)} + /> + onLoad(wrapper, ...args)} + /> + onLoad(wrapper, ...args)} + /> + onLoad(wrapper, ...args)} + /> +
, + ); + }); + }; + + await multiSetup(); + expect(mockOnLoad).toHaveBeenCalledTimes(4); + }); + }); + describe('with errors', () => { beforeEach(() => { mockOnError.mockClear();