@truedat/auth 4.44.2 → 4.44.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # Changelog
2
2
 
3
+ ## [4.44.4] 2022-05-19
4
+
5
+ ### Changed
6
+
7
+ - Use `lowerDeburrTrim` function from `@truedat/core`
8
+
3
9
  ## [4.43.3] 2022-05-04
4
10
 
5
11
  ### Changed
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@truedat/auth",
3
- "version": "4.44.2",
3
+ "version": "4.44.5",
4
4
  "description": "Truedat Web Auth",
5
5
  "sideEffects": false,
6
6
  "jsnext:main": "src/index.js",
@@ -16,26 +16,26 @@
16
16
  "scripts": {
17
17
  "clean": "rimraf yarn-error.log",
18
18
  "debug": "node --inspect-brk node_modules/.bin/jest --runInBand",
19
- "test": "jest --coverage",
20
- "test:watch": "jest --watch",
19
+ "test": "TZ=UTC jest --coverage",
20
+ "test:watch": "TZ=UTC jest --watch",
21
21
  "eslint": "eslint src/**",
22
22
  "eslint:fix": "eslint --fix src/**"
23
23
  },
24
24
  "devDependencies": {
25
- "@babel/cli": "^7.14.8",
26
- "@babel/core": "^7.15.0",
27
- "@babel/plugin-proposal-class-properties": "^7.14.5",
28
- "@babel/plugin-proposal-object-rest-spread": "^7.14.7",
29
- "@babel/plugin-proposal-optional-chaining": "^7.14.5",
25
+ "@babel/cli": "^7.17.10",
26
+ "@babel/core": "^7.18.0",
27
+ "@babel/plugin-proposal-class-properties": "^7.17.12",
28
+ "@babel/plugin-proposal-object-rest-spread": "^7.18.0",
29
+ "@babel/plugin-proposal-optional-chaining": "^7.17.12",
30
30
  "@babel/plugin-syntax-dynamic-import": "^7.8.3",
31
- "@babel/plugin-transform-modules-commonjs": "^7.15.0",
32
- "@babel/preset-env": "^7.15.0",
33
- "@babel/preset-react": "^7.14.5",
34
- "@testing-library/jest-dom": "^5.14.1",
31
+ "@babel/plugin-transform-modules-commonjs": "^7.18.0",
32
+ "@babel/preset-env": "^7.18.0",
33
+ "@babel/preset-react": "^7.17.12",
34
+ "@testing-library/jest-dom": "^5.16.4",
35
35
  "@testing-library/react": "^12.0.0",
36
36
  "@testing-library/user-event": "^13.2.1",
37
- "@truedat/test": "4.44.2",
38
- "babel-jest": "^27.0.6",
37
+ "@truedat/test": "4.44.5",
38
+ "babel-jest": "^28.1.0",
39
39
  "babel-plugin-dynamic-import-node": "^2.3.3",
40
40
  "babel-plugin-lodash": "^3.3.4",
41
41
  "babel-plugin-react-intl": "^5.1.18",
@@ -44,7 +44,8 @@
44
44
  "enzyme-adapter-react-16": "^1.15.6",
45
45
  "enzyme-to-json": "^3.6.2",
46
46
  "identity-obj-proxy": "^3.0.0",
47
- "jest": "^27.0.6",
47
+ "jest": "^28.1.0",
48
+ "jest-environment-jsdom": "^28.1.0",
48
49
  "react": "^16.14.0",
49
50
  "react-dom": "^16.14.0",
50
51
  "redux-saga-test-plan": "^4.0.4",
@@ -52,6 +53,8 @@
52
53
  "semantic-ui-react": "^2.0.3"
53
54
  },
54
55
  "jest": {
56
+ "maxWorkers": "50%",
57
+ "testTimeout": 10000,
55
58
  "moduleDirectories": [
56
59
  "<rootDir>/src",
57
60
  "../../node_modules"
@@ -84,13 +87,13 @@
84
87
  ]
85
88
  },
86
89
  "dependencies": {
87
- "@truedat/core": "4.44.2",
90
+ "@truedat/core": "4.44.5",
88
91
  "auth0-js": "^9.12.2",
89
92
  "immutable": "^4.0.0-rc.12",
90
93
  "jwt-decode": "^2.2.0",
91
94
  "lodash": "^4.17.21",
92
95
  "path-to-regexp": "^1.7.0",
93
- "prop-types": "^15.7.2",
96
+ "prop-types": "^15.8.1",
94
97
  "query-string": "^6.11.0",
95
98
  "react-hook-form": "^7.30.0",
96
99
  "react-intl": "^5.20.10",
@@ -106,5 +109,5 @@
106
109
  "react-dom": ">= 16.8.6 < 17",
107
110
  "semantic-ui-react": ">= 0.88.2 < 2.1"
108
111
  },
109
- "gitHead": "ca0c5fffcba96736f7a2054f3c37789da8c30a9e"
112
+ "gitHead": "5a339468198c803592b285eddd0dd0c0b0eced93"
110
113
  }
@@ -1,21 +1,22 @@
1
- import _ from "lodash/fp";
2
- import React, { useEffect, useCallback } from "react";
1
+ import React, { useEffect } from "react";
3
2
  import PropTypes from "prop-types";
4
- import { useSelector, useDispatch } from "react-redux";
3
+ import { connect } from "react-redux";
5
4
  import { Dimmer, Loader } from "semantic-ui-react";
6
-
7
5
  import { searchGroups, clearGroupsSearch } from "../routines";
8
6
 
9
- export const GroupsSearchLoader = ({ query, hideLoading }) => {
10
- const loading = useSelector(_.get("groupsSearchLoading"));
11
- const dispatch = useDispatch();
12
- const stableDispatch = useCallback(dispatch, []);
7
+ export const GroupsSearchLoader = ({
8
+ query,
9
+ hideLoading,
10
+ searchGroups,
11
+ clearGroupsSearch,
12
+ loading,
13
+ }) => {
13
14
  useEffect(() => {
14
- stableDispatch(searchGroups({ query }));
15
+ searchGroups({ query });
15
16
  return () => {
16
- stableDispatch(clearGroupsSearch());
17
+ clearGroupsSearch();
17
18
  };
18
- }, [stableDispatch, query]);
19
+ }, [query, searchGroups, clearGroupsSearch]);
19
20
  return loading && !hideLoading ? (
20
21
  <Dimmer active inverted>
21
22
  <Loader size="massive" inverted />
@@ -25,7 +26,16 @@ export const GroupsSearchLoader = ({ query, hideLoading }) => {
25
26
 
26
27
  GroupsSearchLoader.propTypes = {
27
28
  query: PropTypes.string,
28
- hideLoading: PropTypes.bool
29
+ hideLoading: PropTypes.bool,
30
+ loading: PropTypes.bool,
31
+ searchGroups: PropTypes.func,
32
+ clearGroupsSearch: PropTypes.func,
29
33
  };
30
34
 
31
- export default GroupsSearchLoader;
35
+ export const mapStateToProps = ({ groupsSearchLoading }) => ({
36
+ loading: groupsSearchLoading,
37
+ });
38
+
39
+ export default connect(null, { searchGroups, clearGroupsSearch })(
40
+ GroupsSearchLoader
41
+ );
@@ -1,16 +1,15 @@
1
1
  import React from "react";
2
- import { shallow } from "enzyme";
2
+ import { render } from "@truedat/test/render";
3
3
  import GroupsSearchLoader from "../GroupsSearchLoader";
4
4
 
5
- jest.mock("react-redux", () => ({
6
- ...jest.requireActual("react-redux"),
7
- useDispatch: jest.fn(),
8
- useSelector: jest.fn(selector => selector({ groupsSearchLoading: false }))
9
- }));
5
+ const renderOpts = { state: { groupsSearchLoading: false } };
10
6
 
11
7
  describe("<GroupsSearchLoader />", () => {
12
8
  it("matches the latest snapshot", () => {
13
- const wrapper = shallow(<GroupsSearchLoader query="abc" />);
14
- expect(wrapper).toMatchSnapshot();
9
+ const { container } = render(
10
+ <GroupsSearchLoader query="abc" />,
11
+ renderOpts
12
+ );
13
+ expect(container).toMatchSnapshot();
15
14
  });
16
15
  });
@@ -1,3 +1,3 @@
1
1
  // Jest Snapshot v1, https://goo.gl/fbAQLP
2
2
 
3
- exports[`<GroupsSearchLoader /> matches the latest snapshot 1`] = `""`;
3
+ exports[`<GroupsSearchLoader /> matches the latest snapshot 1`] = `<div />`;
@@ -32,7 +32,7 @@ export const Roles = ({ roles, rolesLoading }) => (
32
32
 
33
33
  Roles.propTypes = {
34
34
  roles: PropTypes.array,
35
- rolesLoading: PropTypes.bool
35
+ rolesLoading: PropTypes.bool,
36
36
  };
37
37
 
38
38
  const mapStateToProps = ({ roles, rolesLoading }) => ({ roles, rolesLoading });
@@ -1,29 +1,13 @@
1
1
  import React from "react";
2
- import { intl } from "@truedat/test/intl-stub";
3
- import { shallow } from "enzyme";
4
- import { createStore } from "redux";
5
- import { Provider } from "react-redux";
2
+ import { render } from "@truedat/test/render";
6
3
  import RoleSelector from "../RoleSelector";
7
4
 
8
- jest.mock("react-redux", () => ({
9
- ...jest.requireActual("react-redux"),
10
- useSelector: jest.fn(selector =>
11
- selector({ roles: [{ name: "test1", id: 1 }] })
12
- )
13
- }));
14
-
15
- // workaround for enzyme issue with React.useContext
16
- // see https://github.com/airbnb/enzyme/issues/2176#issuecomment-532361526
17
- jest.spyOn(React, "useContext").mockImplementation(() => intl);
5
+ const renderOpts = { state: { roles: [{ name: "test1", id: 1 }] } };
18
6
 
19
7
  describe("<RoleSelector />", () => {
20
8
  it("matches the latest snapshot", () => {
21
- const store = createStore(() => ({}));
22
- const wrapper = shallow(
23
- <Provider store={store}>
24
- <RoleSelector />
25
- </Provider>
26
- );
27
- expect(wrapper).toMatchSnapshot();
9
+ const props = { onChange: jest.fn() };
10
+ const { container } = render(<RoleSelector {...props} />, renderOpts);
11
+ expect(container).toMatchSnapshot();
28
12
  });
29
13
  });
@@ -1,16 +1,16 @@
1
1
  import React from "react";
2
- import { shallowWithIntl } from "@truedat/test/intl-stub";
3
- import { Roles } from "../Roles";
2
+ import { render } from "@truedat/test/render";
3
+ import Roles from "../Roles";
4
4
 
5
5
  describe("<Roles />", () => {
6
6
  const roles = [
7
7
  { id: 3, is_default: false, name: "Data Owner" },
8
- { id: 2, is_default: false, name: "Data Q" }
8
+ { id: 2, is_default: false, name: "Data Q" },
9
9
  ];
10
- const props = { roles };
10
+ const state = { roles };
11
11
 
12
12
  it("matches the latest snapshot", () => {
13
- const wrapper = shallowWithIntl(<Roles {...props} />);
14
- expect(wrapper).toMatchSnapshot();
13
+ const { container } = render(<Roles />, { state });
14
+ expect(container).toMatchSnapshot();
15
15
  });
16
16
  });
@@ -1,35 +1,54 @@
1
1
  // Jest Snapshot v1, https://goo.gl/fbAQLP
2
2
 
3
3
  exports[`<RoleSelector /> matches the latest snapshot 1`] = `
4
- <ContextProvider
5
- value={
6
- Object {
7
- "store": Object {
8
- "@@observable": [Function],
9
- "dispatch": [Function],
10
- "getState": [Function],
11
- "replaceReducer": [Function],
12
- "subscribe": [Function],
13
- },
14
- "subscription": Subscription {
15
- "handleChangeWrapper": [Function],
16
- "listeners": Object {
17
- "notify": [Function],
18
- },
19
- "onStateChange": [Function],
20
- "parentSub": undefined,
21
- "store": Object {
22
- "@@observable": [Function],
23
- "dispatch": [Function],
24
- "getState": [Function],
25
- "replaceReducer": [Function],
26
- "subscribe": [Function],
27
- },
28
- "unsubscribe": null,
29
- },
30
- }
31
- }
32
- >
33
- <Connect(RoleSelector) />
34
- </ContextProvider>
4
+ <div>
5
+ <div
6
+ class="field"
7
+ >
8
+ <div
9
+ aria-expanded="false"
10
+ class="ui search selection dropdown"
11
+ role="combobox"
12
+ >
13
+ <input
14
+ aria-autocomplete="list"
15
+ autocomplete="off"
16
+ class="search"
17
+ tabindex="0"
18
+ type="text"
19
+ value=""
20
+ />
21
+ <div
22
+ aria-atomic="true"
23
+ aria-live="polite"
24
+ class="divider default text"
25
+ role="alert"
26
+ >
27
+ Search Roles…
28
+ </div>
29
+ <i
30
+ aria-hidden="true"
31
+ class="dropdown icon"
32
+ />
33
+ <div
34
+ class="menu transition"
35
+ role="listbox"
36
+ >
37
+ <div
38
+ aria-checked="false"
39
+ aria-selected="true"
40
+ class="selected item"
41
+ role="option"
42
+ style="pointer-events: all;"
43
+ >
44
+ <span
45
+ class="text"
46
+ >
47
+ test1
48
+ </span>
49
+ </div>
50
+ </div>
51
+ </div>
52
+ </div>
53
+ </div>
35
54
  `;
@@ -1,65 +1,99 @@
1
1
  // Jest Snapshot v1, https://goo.gl/fbAQLP
2
2
 
3
3
  exports[`<Roles /> matches the latest snapshot 1`] = `
4
- <Segment>
5
- <Header
6
- as="h2"
4
+ <div>
5
+ <div
6
+ class="ui segment"
7
7
  >
8
- <Button
9
- as={
10
- Object {
11
- "$$typeof": Symbol(react.forward_ref),
12
- "displayName": "Link",
13
- "propTypes": Object {
14
- "innerRef": [Function],
15
- "onClick": [Function],
16
- "replace": [Function],
17
- "target": [Function],
18
- "to": [Function],
19
- },
20
- "render": [Function],
21
- }
22
- }
23
- content={
24
- <Memo(MemoizedFormattedMessage)
25
- id="roles.create"
26
- />
27
- }
28
- floated="right"
29
- primary={true}
30
- to="/roles/new"
31
- />
32
- <Icon
33
- as="i"
34
- circular={true}
35
- name="student"
36
- />
37
- <HeaderContent>
38
- <MemoizedFormattedMessage
39
- id="roles.header"
8
+ <h2
9
+ class="ui header"
10
+ >
11
+ <a
12
+ class="ui primary right floated button"
13
+ href="/roles/new"
14
+ role="button"
15
+ >
16
+ Create new role
17
+ </a>
18
+ <i
19
+ aria-hidden="true"
20
+ class="student circular icon"
40
21
  />
41
- <HeaderSubheader>
42
- <MemoizedFormattedMessage
43
- id="roles.subheader"
44
- />
45
- </HeaderSubheader>
46
- </HeaderContent>
47
- </Header>
48
- <Connect(RoleCards)
49
- roles={
50
- Array [
51
- Object {
52
- "id": 3,
53
- "is_default": false,
54
- "name": "Data Owner",
55
- },
56
- Object {
57
- "id": 2,
58
- "is_default": false,
59
- "name": "Data Q",
60
- },
61
- ]
62
- }
63
- />
64
- </Segment>
22
+ <div
23
+ class="content"
24
+ >
25
+ Roles
26
+ <div
27
+ class="sub header"
28
+ >
29
+ Manage roles
30
+ </div>
31
+ </div>
32
+ </h2>
33
+ <div
34
+ class="ui cards"
35
+ >
36
+ <div
37
+ class="ui card"
38
+ >
39
+ <div
40
+ class="content"
41
+ >
42
+ <a
43
+ class="header"
44
+ href="/roles/3"
45
+ >
46
+ Data Owner
47
+ </a>
48
+ </div>
49
+ <div
50
+ class="extra content"
51
+ >
52
+ <div
53
+ class="ui actions"
54
+ >
55
+ <button
56
+ class="ui red basic icon button"
57
+ >
58
+ <i
59
+ aria-hidden="true"
60
+ class="trash icon"
61
+ />
62
+ </button>
63
+ </div>
64
+ </div>
65
+ </div>
66
+ <div
67
+ class="ui card"
68
+ >
69
+ <div
70
+ class="content"
71
+ >
72
+ <a
73
+ class="header"
74
+ href="/roles/2"
75
+ >
76
+ Data Q
77
+ </a>
78
+ </div>
79
+ <div
80
+ class="extra content"
81
+ >
82
+ <div
83
+ class="ui actions"
84
+ >
85
+ <button
86
+ class="ui red basic icon button"
87
+ >
88
+ <i
89
+ aria-hidden="true"
90
+ class="trash icon"
91
+ />
92
+ </button>
93
+ </div>
94
+ </div>
95
+ </div>
96
+ </div>
97
+ </div>
98
+ </div>
65
99
  `;
@@ -1,21 +1,22 @@
1
- import _ from "lodash/fp";
2
- import React, { useEffect, useCallback } from "react";
1
+ import React, { useEffect } from "react";
3
2
  import PropTypes from "prop-types";
4
- import { useSelector, useDispatch } from "react-redux";
3
+ import { connect } from "react-redux";
5
4
  import { Dimmer, Loader } from "semantic-ui-react";
6
-
7
5
  import { searchUsers, clearUsersSearch } from "../routines";
8
6
 
9
- export const UsersSearchLoader = ({ query, hideLoading }) => {
10
- const loading = useSelector(_.get("usersSearchLoading"));
11
- const dispatch = useDispatch();
12
- const stableDispatch = useCallback(dispatch, []);
7
+ export const UsersSearchLoader = ({
8
+ query,
9
+ hideLoading,
10
+ searchUsers,
11
+ clearUsersSearch,
12
+ loading,
13
+ }) => {
13
14
  useEffect(() => {
14
- stableDispatch(searchUsers({ query }));
15
+ searchUsers({ query });
15
16
  return () => {
16
- stableDispatch(clearUsersSearch());
17
+ clearUsersSearch();
17
18
  };
18
- }, [stableDispatch, query]);
19
+ }, [searchUsers, clearUsersSearch, query]);
19
20
  return loading && !hideLoading ? (
20
21
  <Dimmer active inverted>
21
22
  <Loader size="massive" inverted />
@@ -25,7 +26,16 @@ export const UsersSearchLoader = ({ query, hideLoading }) => {
25
26
 
26
27
  UsersSearchLoader.propTypes = {
27
28
  query: PropTypes.string,
28
- hideLoading: PropTypes.bool
29
+ hideLoading: PropTypes.bool,
30
+ searchUsers: PropTypes.func,
31
+ clearUsersSearch: PropTypes.func,
32
+ loading: PropTypes.bool,
29
33
  };
30
34
 
31
- export default UsersSearchLoader;
35
+ export const mapStateToProps = ({ usersSearchLoading: loading }) => ({
36
+ loading,
37
+ });
38
+
39
+ export default connect(mapStateToProps, { searchUsers, clearUsersSearch })(
40
+ UsersSearchLoader
41
+ );
@@ -1,31 +1,13 @@
1
1
  import React from "react";
2
- import { intl } from "@truedat/test/intl-stub";
3
- import { shallow } from "enzyme";
4
-
5
- import { createStore } from "redux";
6
- import { Provider } from "react-redux";
7
-
2
+ import { render } from "@truedat/test/render";
8
3
  import UserSelector from "../UserSelector";
9
4
 
10
- jest.mock("react-redux", () => ({
11
- ...jest.requireActual("react-redux"),
12
- useSelector: jest.fn(selector =>
13
- selector({ users: [{ full_name: "test1", id: 1 }] })
14
- )
15
- }));
16
-
17
- // workaround for enzyme issue with React.useContext
18
- // see https://github.com/airbnb/enzyme/issues/2176#issuecomment-532361526
19
- jest.spyOn(React, "useContext").mockImplementation(() => intl);
5
+ const renderOpts = { users: [{ full_name: "test1", id: 1 }] };
20
6
 
21
7
  describe("<UserSelector />", () => {
22
8
  it("matches the latest snapshot", () => {
23
- const store = createStore(() => ({}));
24
- const wrapper = shallow(
25
- <Provider store={store}>
26
- <UserSelector />
27
- </Provider>
28
- );
29
- expect(wrapper).toMatchSnapshot();
9
+ const props = { onChange: jest.fn() };
10
+ const { container } = render(<UserSelector {...props} />, renderOpts);
11
+ expect(container).toMatchSnapshot();
30
12
  });
31
13
  });
@@ -1,16 +1,12 @@
1
1
  import React from "react";
2
- import { shallow } from "enzyme";
2
+ import { render } from "@truedat/test/render";
3
3
  import UsersSearchLoader from "../UsersSearchLoader";
4
4
 
5
- jest.mock("react-redux", () => ({
6
- ...jest.requireActual("react-redux"),
7
- useDispatch: jest.fn(),
8
- useSelector: jest.fn(selector => selector({ usersSearchLoading: false }))
9
- }));
5
+ const renderOpts = { state: { usersSearchLoading: false } };
10
6
 
11
7
  describe("<UsersSearchLoader />", () => {
12
8
  it("matches the latest snapshot", () => {
13
- const wrapper = shallow(<UsersSearchLoader query="abc" />);
14
- expect(wrapper).toMatchSnapshot();
9
+ const { container } = render(<UsersSearchLoader query="abc" />, renderOpts);
10
+ expect(container).toMatchSnapshot();
15
11
  });
16
12
  });
@@ -1,35 +1,46 @@
1
1
  // Jest Snapshot v1, https://goo.gl/fbAQLP
2
2
 
3
3
  exports[`<UserSelector /> matches the latest snapshot 1`] = `
4
- <ContextProvider
5
- value={
6
- Object {
7
- "store": Object {
8
- "@@observable": [Function],
9
- "dispatch": [Function],
10
- "getState": [Function],
11
- "replaceReducer": [Function],
12
- "subscribe": [Function],
13
- },
14
- "subscription": Subscription {
15
- "handleChangeWrapper": [Function],
16
- "listeners": Object {
17
- "notify": [Function],
18
- },
19
- "onStateChange": [Function],
20
- "parentSub": undefined,
21
- "store": Object {
22
- "@@observable": [Function],
23
- "dispatch": [Function],
24
- "getState": [Function],
25
- "replaceReducer": [Function],
26
- "subscribe": [Function],
27
- },
28
- "unsubscribe": null,
29
- },
30
- }
31
- }
32
- >
33
- <Connect(UserSelector) />
34
- </ContextProvider>
4
+ <div>
5
+ <div
6
+ class="field"
7
+ >
8
+ <div
9
+ aria-expanded="false"
10
+ class="ui search selection dropdown"
11
+ role="combobox"
12
+ >
13
+ <input
14
+ aria-autocomplete="list"
15
+ autocomplete="off"
16
+ class="search"
17
+ tabindex="0"
18
+ type="text"
19
+ value=""
20
+ />
21
+ <div
22
+ aria-atomic="true"
23
+ aria-live="polite"
24
+ class="divider default text"
25
+ role="alert"
26
+ >
27
+ Search users…
28
+ </div>
29
+ <i
30
+ aria-hidden="true"
31
+ class="dropdown icon"
32
+ />
33
+ <div
34
+ class="menu transition"
35
+ role="listbox"
36
+ >
37
+ <div
38
+ class="message"
39
+ >
40
+ No results found.
41
+ </div>
42
+ </div>
43
+ </div>
44
+ </div>
45
+ </div>
35
46
  `;
@@ -1,3 +1,3 @@
1
1
  // Jest Snapshot v1, https://goo.gl/fbAQLP
2
2
 
3
- exports[`<UsersSearchLoader /> matches the latest snapshot 1`] = `""`;
3
+ exports[`<UsersSearchLoader /> matches the latest snapshot 1`] = `<div />`;
@@ -1,9 +1,7 @@
1
1
  import _ from "lodash/fp";
2
- import { lowerDeburr } from "@truedat/core/services/sort";
2
+ import { lowerDeburrTrim } from "@truedat/core/services/sort";
3
3
 
4
- const lowerDeburrTrim = _.flow(_.trim, lowerDeburr);
5
-
6
- export const filterUsers = usersFilter =>
4
+ export const filterUsers = (usersFilter) =>
7
5
  lowerDeburrTrim(usersFilter)
8
6
  ? _.filter(
9
7
  _.flow(