From d1e96501ec5a0b0cf1db3b010e42b21cd4e4f19a Mon Sep 17 00:00:00 2001 From: olav Date: Wed, 16 Mar 2022 13:51:01 +0100 Subject: [PATCH] refactor: test useFeaturesFilter (#795) --- frontend/package.json | 3 +- .../useFeaturesFilter.test.ts.snap | 213 ++++++++++++++++++ frontend/src/hooks/useFeaturesFilter.test.ts | 129 +++++++++++ frontend/yarn.lock | 24 +- 4 files changed, 365 insertions(+), 4 deletions(-) create mode 100644 frontend/src/hooks/__snapshots__/useFeaturesFilter.test.ts.snap create mode 100644 frontend/src/hooks/useFeaturesFilter.test.ts diff --git a/frontend/package.json b/frontend/package.json index ccd6d50e9e..8b4bb87268 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -44,6 +44,7 @@ "@testing-library/dom": "8.11.3", "@testing-library/jest-dom": "5.16.2", "@testing-library/react": "12.1.4", + "@testing-library/react-hooks": "^7.0.2", "@testing-library/user-event": "13.5.0", "@types/debounce": "1.2.1", "@types/deep-diff": "1.0.1", @@ -80,8 +81,8 @@ "react-scripts": "4.0.3", "react-test-renderer": "17.0.2", "react-timeago": "6.2.1", - "semver": "^7.3.5", "sass": "1.49.9", + "semver": "^7.3.5", "swr": "1.2.2", "typescript": "4.6.2" }, diff --git a/frontend/src/hooks/__snapshots__/useFeaturesFilter.test.ts.snap b/frontend/src/hooks/__snapshots__/useFeaturesFilter.test.ts.snap new file mode 100644 index 0000000000..e977f3ff06 --- /dev/null +++ b/frontend/src/hooks/__snapshots__/useFeaturesFilter.test.ts.snap @@ -0,0 +1,213 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`useFeaturesFilter constraints 1`] = ` +Object { + "filter": Object { + "project": "*", + "query": "xyz", + }, + "filtered": Array [ + Object { + "archived": false, + "createdAt": "22006-01-02T15:04:05Z", + "description": "1", + "enabled": false, + "environments": Array [], + "impressionData": false, + "lastSeenAt": "2006-01-02T15:04:05Z", + "name": "1", + "project": "a", + "stale": false, + "strategies": Array [ + Object { + "constraints": Array [], + "id": "1", + "name": "1", + "parameters": Object {}, + }, + Object { + "constraints": Array [], + "id": "1", + "name": "1", + "parameters": Object {}, + }, + Object { + "constraints": Array [ + Object { + "contextName": "", + "operator": "IN", + }, + Object { + "contextName": "", + "operator": "IN", + "value": "xyz", + }, + Object { + "contextName": "", + "operator": "IN", + "values": Array [ + "xyz", + ], + }, + ], + "id": "1", + "name": "1", + "parameters": Object {}, + }, + ], + "type": "1", + "variants": Array [], + }, + ], + "setFilter": [Function], +} +`; + +exports[`useFeaturesFilter empty 1`] = ` +Object { + "filter": Object { + "project": "*", + }, + "filtered": Array [], + "setFilter": [Function], +} +`; + +exports[`useFeaturesFilter equal 1`] = ` +Object { + "filter": Object { + "project": "*", + }, + "filtered": Array [ + Object { + "archived": false, + "createdAt": "22006-01-02T15:04:05Z", + "description": "1", + "enabled": false, + "environments": Array [], + "impressionData": false, + "lastSeenAt": "2006-01-02T15:04:05Z", + "name": "1", + "project": "1", + "stale": false, + "strategies": Array [], + "type": "1", + "variants": Array [], + }, + Object { + "archived": false, + "createdAt": "22006-01-02T15:04:05Z", + "description": "1", + "enabled": false, + "environments": Array [], + "impressionData": false, + "lastSeenAt": "2006-01-02T15:04:05Z", + "name": "1", + "project": "1", + "stale": false, + "strategies": Array [], + "type": "1", + "variants": Array [], + }, + Object { + "archived": false, + "createdAt": "22006-01-02T15:04:05Z", + "description": "1", + "enabled": false, + "environments": Array [], + "impressionData": false, + "lastSeenAt": "2006-01-02T15:04:05Z", + "name": "1", + "project": "1", + "stale": false, + "strategies": Array [], + "type": "1", + "variants": Array [], + }, + ], + "setFilter": [Function], +} +`; + +exports[`useFeaturesFilter project 1`] = ` +Object { + "filter": Object { + "project": "2", + }, + "filtered": Array [ + Object { + "archived": false, + "createdAt": "22006-01-02T15:04:05Z", + "description": "1", + "enabled": false, + "environments": Array [], + "impressionData": false, + "lastSeenAt": "2006-01-02T15:04:05Z", + "name": "1", + "project": "2", + "stale": false, + "strategies": Array [], + "type": "1", + "variants": Array [], + }, + Object { + "archived": false, + "createdAt": "22006-01-02T15:04:05Z", + "description": "1", + "enabled": false, + "environments": Array [], + "impressionData": false, + "lastSeenAt": "2006-01-02T15:04:05Z", + "name": "1", + "project": "2", + "stale": false, + "strategies": Array [], + "type": "1", + "variants": Array [], + }, + ], + "setFilter": [Function], +} +`; + +exports[`useFeaturesFilter query 1`] = ` +Object { + "filter": Object { + "project": "*", + "query": "bc", + }, + "filtered": Array [ + Object { + "archived": false, + "createdAt": "22006-01-02T15:04:05Z", + "description": "1", + "enabled": false, + "environments": Array [], + "impressionData": false, + "lastSeenAt": "2006-01-02T15:04:05Z", + "name": "1", + "project": "abc", + "stale": false, + "strategies": Array [], + "type": "1", + "variants": Array [], + }, + Object { + "archived": false, + "createdAt": "22006-01-02T15:04:05Z", + "description": "1", + "enabled": false, + "environments": Array [], + "impressionData": false, + "lastSeenAt": "2006-01-02T15:04:05Z", + "name": "1", + "project": "abcd", + "stale": false, + "strategies": Array [], + "type": "1", + "variants": Array [], + }, + ], + "setFilter": [Function], +} +`; diff --git a/frontend/src/hooks/useFeaturesFilter.test.ts b/frontend/src/hooks/useFeaturesFilter.test.ts new file mode 100644 index 0000000000..85620e4eb1 --- /dev/null +++ b/frontend/src/hooks/useFeaturesFilter.test.ts @@ -0,0 +1,129 @@ +import { renderHook, act } from '@testing-library/react-hooks'; +import { useFeaturesFilter } from 'hooks/useFeaturesFilter'; +import { IFeatureToggle } from 'interfaces/featureToggle'; +import { IConstraint, IFeatureStrategy } from 'interfaces/strategy'; + +test('useFeaturesFilter empty', () => { + const { result } = renderHook(() => useFeaturesFilter([])); + + expect(result.current.filtered.length).toEqual(0); + expect(result.current).toMatchSnapshot(); +}); + +test('useFeaturesFilter equal', () => { + const { result } = renderHook(() => + useFeaturesFilter([ + mockFeatureToggle(), + mockFeatureToggle(), + mockFeatureToggle(), + ]) + ); + + expect(result.current.filtered.length).toEqual(3); + expect(result.current).toMatchSnapshot(); +}); + +test('useFeaturesFilter project', () => { + const { result } = renderHook(() => + useFeaturesFilter([ + mockFeatureToggle({ project: '1' }), + mockFeatureToggle({ project: '2' }), + mockFeatureToggle({ project: '2' }), + mockFeatureToggle({ project: '3' }), + ]) + ); + + act(() => { + result.current.setFilter({ project: '2' }); + }); + + expect(result.current.filtered.length).toEqual(2); + expect(result.current).toMatchSnapshot(); +}); + +test('useFeaturesFilter query', () => { + const { result } = renderHook(() => + useFeaturesFilter([ + mockFeatureToggle({ project: 'a' }), + mockFeatureToggle({ project: 'ab' }), + mockFeatureToggle({ project: 'abc' }), + mockFeatureToggle({ project: 'abcd' }), + ]) + ); + + act(() => { + result.current.setFilter({ project: '*', query: 'bc' }); + }); + + expect(result.current.filtered.length).toEqual(2); + expect(result.current).toMatchSnapshot(); +}); + +test('useFeaturesFilter constraints', () => { + const { result } = renderHook(() => + useFeaturesFilter([ + mockFeatureToggle({ + project: 'a', + strategies: [ + mockFeatureStrategy(), + mockFeatureStrategy(), + mockFeatureStrategy({ + constraints: [ + mockConstraint(), + mockConstraint({ value: 'xyz' }), + mockConstraint({ values: ['xyz'] }), + ], + }), + ], + }), + ]) + ); + + act(() => { + result.current.setFilter({ project: '*', query: 'xyz' }); + }); + + expect(result.current.filtered.length).toEqual(1); + expect(result.current).toMatchSnapshot(); +}); + +const mockFeatureToggle = ( + overrides?: Partial +): IFeatureToggle => { + return { + name: '1', + description: '1', + type: '1', + project: '1', + archived: false, + enabled: false, + stale: false, + impressionData: false, + strategies: [], + variants: [], + environments: [], + createdAt: '22006-01-02T15:04:05Z', + lastSeenAt: '2006-01-02T15:04:05Z', + ...overrides, + }; +}; + +const mockFeatureStrategy = ( + overrides?: Partial +): IFeatureStrategy => { + return { + id: '1', + name: '1', + constraints: [], + parameters: {}, + ...overrides, + }; +}; + +const mockConstraint = (overrides?: Partial): IConstraint => { + return { + contextName: '', + operator: 'IN', + ...overrides, + }; +}; diff --git a/frontend/yarn.lock b/frontend/yarn.lock index c699e94ae8..f62779f0d3 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -1805,6 +1805,17 @@ lodash "^4.17.15" redent "^3.0.0" +"@testing-library/react-hooks@^7.0.2": + version "7.0.2" + resolved "https://registry.yarnpkg.com/@testing-library/react-hooks/-/react-hooks-7.0.2.tgz#3388d07f562d91e7f2431a4a21b5186062ecfee0" + integrity sha512-dYxpz8u9m4q1TuzfcUApqi8iFfR6R0FaMbr2hjZJy1uC8z+bO/K4v8Gs9eogGKYQop7QsrBTFkv/BCF7MzD2Cg== + dependencies: + "@babel/runtime" "^7.12.5" + "@types/react" ">=16.9.0" + "@types/react-dom" ">=16.9.0" + "@types/react-test-renderer" ">=16.9.0" + react-error-boundary "^3.1.0" + "@testing-library/react@12.1.4": version "12.1.4" resolved "https://registry.yarnpkg.com/@testing-library/react/-/react-12.1.4.tgz#09674b117e550af713db3f4ec4c0942aa8bbf2c0" @@ -2038,7 +2049,7 @@ dependencies: "@types/react" "*" -"@types/react-dom@17.0.13": +"@types/react-dom@17.0.13", "@types/react-dom@>=16.9.0": version "17.0.13" resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-17.0.13.tgz#a3323b974ee4280070982b3112351bb1952a7809" integrity sha512-wEP+B8hzvy6ORDv1QBhcQia4j6ea4SFIBttHYpXKPFZRviBvknq0FRh3VrIxeXUmsPkwuXVZrVGG7KUVONmXCQ== @@ -2062,7 +2073,7 @@ "@types/history" "*" "@types/react" "*" -"@types/react-test-renderer@17.0.1": +"@types/react-test-renderer@17.0.1", "@types/react-test-renderer@>=16.9.0": version "17.0.1" resolved "https://registry.npmjs.org/@types/react-test-renderer/-/react-test-renderer-17.0.1.tgz" integrity sha512-3Fi2O6Zzq/f3QR9dRnlnHso9bMl7weKCviFmfF6B4LS1Uat6Hkm15k0ZAQuDz+UBq6B3+g+NM6IT2nr5QgPzCw== @@ -2092,7 +2103,7 @@ "@types/scheduler" "*" csstype "^3.0.2" -"@types/react@17.0.40": +"@types/react@17.0.40", "@types/react@>=16.9.0": version "17.0.40" resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.40.tgz#dc010cee6254d5239a138083f3799a16638e6bad" integrity sha512-UrXhD/JyLH+W70nNSufXqMZNuUD2cXHu6UjCllC6pmOQgBX4SGXOH8fjRka0O0Ee0HrFxapDD8Bwn81Kmiz6jQ== @@ -10051,6 +10062,13 @@ react-dom@17.0.2: object-assign "^4.1.1" 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-error-overlay@^6.0.9: version "6.0.9" resolved "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.9.tgz"