@onewelcome/react-lib-components 1.6.0 → 1.7.0

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.
Files changed (34) hide show
  1. package/README.md +4 -4
  2. package/dist/Form/FileUpload/FileItem/FileItem.d.ts +17 -0
  3. package/dist/Form/FileUpload/FileUpload.d.ts +26 -0
  4. package/dist/Icon/Icon.d.ts +4 -1
  5. package/dist/ProgressBar/ProgressBar.d.ts +2 -1
  6. package/dist/_BaseStyling_/BaseStyling.d.ts +3 -0
  7. package/dist/hooks/useUploadFile.d.ts +22 -0
  8. package/dist/react-lib-components.cjs.development.js +60 -53
  9. package/dist/react-lib-components.cjs.development.js.map +1 -1
  10. package/dist/react-lib-components.cjs.production.min.js +1 -1
  11. package/dist/react-lib-components.cjs.production.min.js.map +1 -1
  12. package/dist/react-lib-components.esm.js +60 -53
  13. package/dist/react-lib-components.esm.js.map +1 -1
  14. package/dist/util/helper.d.ts +5 -0
  15. package/package.json +24 -21
  16. package/src/Form/FileUpload/FileItem/FileItem.modules.scss +75 -0
  17. package/src/Form/FileUpload/FileItem/FileItem.test.tsx +103 -0
  18. package/src/Form/FileUpload/FileItem/FileItem.tsx +141 -0
  19. package/src/Form/FileUpload/FileUpload.module.scss +106 -0
  20. package/src/Form/FileUpload/FileUpload.test.tsx +374 -0
  21. package/src/Form/FileUpload/FileUpload.tsx +251 -0
  22. package/src/Form/Input/Input.module.scss +0 -1
  23. package/src/Icon/Icon.module.scss +12 -0
  24. package/src/Icon/Icon.tsx +4 -1
  25. package/src/ProgressBar/ProgressBar.module.scss +11 -9
  26. package/src/ProgressBar/ProgressBar.test.tsx +21 -0
  27. package/src/ProgressBar/ProgressBar.tsx +7 -2
  28. package/src/Typography/Typography.module.scss +2 -2
  29. package/src/_BaseStyling_/BaseStyling.tsx +9 -3
  30. package/src/hooks/useUploadFile.test.ts +211 -0
  31. package/src/hooks/useUploadFile.tsx +136 -0
  32. package/src/mixins.module.scss +24 -5
  33. package/src/util/helper.test.tsx +156 -1
  34. package/src/util/helper.tsx +33 -0
@@ -56,3 +56,24 @@ describe("ref should work", () => {
56
56
  render(<ExampleComponent propagateRef={refCheck} />, { container });
57
57
  });
58
58
  });
59
+
60
+ describe("ProgressBar should change styles depending on props", () => {
61
+ it("should show a progress when 'completed' prop is provided", () => {
62
+ const { ProgressBarComponent } = createProgressBar(defaultParams => ({
63
+ ...defaultParams,
64
+ completed: 40
65
+ }));
66
+
67
+ const bar = ProgressBarComponent.querySelector(".bar");
68
+ expect(bar).toHaveClass("w-40");
69
+ });
70
+
71
+ it("should show a loading effect when 'completed' prop is not provided", () => {
72
+ const { ProgressBarComponent } = createProgressBar(defaultParams => ({
73
+ ...defaultParams
74
+ }));
75
+
76
+ const bar = ProgressBarComponent.querySelector(".bar");
77
+ expect(bar).toHaveClass("loading-state");
78
+ });
79
+ });
@@ -20,16 +20,21 @@ import classes from "./ProgressBar.module.scss";
20
20
 
21
21
  export interface Props extends Omit<ComponentPropsWithRef<"span">, "children"> {
22
22
  placeholderText: string;
23
+ completed?: number;
23
24
  }
24
25
 
25
26
  const ProgressBarComponent: ForwardRefRenderFunction<HTMLSpanElement, Props> = (
26
- { placeholderText, ...rest }: Props,
27
+ { placeholderText, completed, ...rest }: Props,
27
28
  ref
28
29
  ) => {
29
30
  return (
30
31
  <span {...rest} ref={ref} role="progressbar">
31
32
  <span className={classes["progress-bar"]}>
32
- <span className={classes["bar"]} />
33
+ <span
34
+ className={`${classes["bar"]} ${
35
+ completed ? classes[`w-${5 * Math.round(completed / 5)}`] : classes["loading-state"]
36
+ }`}
37
+ />
33
38
  </span>
34
39
  <Typography className={classes["placeholder"]} spacing={{ marginBottom: 0 }} variant="body">
35
40
  {placeholderText}
@@ -37,11 +37,11 @@
37
37
  }
38
38
 
39
39
  &h3 {
40
- @include fontProperties(var(--font-family), var(--font-size-h3), 400, 1.16666);
40
+ @include fontProperties(var(--font-family), var(--font-size-h3), 500, 1.16666);
41
41
  }
42
42
 
43
43
  &h4 {
44
- @include fontProperties(var(--font-family), var(--font-size-h4), 700, 1.2);
44
+ @include fontProperties(var(--font-family), var(--font-size-h4), 500, 1.2);
45
45
  }
46
46
 
47
47
  &body {
@@ -26,6 +26,8 @@ interface CSSProperties {
26
26
  colorPrimary?: string;
27
27
  colorSecondary?: string;
28
28
  colorTertiary?: string;
29
+ lightPink?: string;
30
+ vividViolet?: string;
29
31
  defaultLineHeight?: string;
30
32
  buttonBorderRadius?: string;
31
33
  buttonBorderWidth?: string;
@@ -39,6 +41,7 @@ interface CSSProperties {
39
41
  inputBorderWidthFocus?: string;
40
42
  inputBorderStyle?: string;
41
43
  inputBackgroundColor?: string;
44
+ dragBorderStyle?: string;
42
45
  modalShadowColor?: string;
43
46
  modalBackgroundColor?: string;
44
47
  modalHeaderBackgroundColor?: string;
@@ -91,6 +94,8 @@ export const BaseStyling = ({ children, properties = {} }: Props) => {
91
94
  colorPrimary: "#9e006b",
92
95
  colorSecondary: "#003b5e",
93
96
  colorTertiary: "#ff1e4e",
97
+ lightPink: "#9E006B1A",
98
+ vividViolet: "#9E006B",
94
99
  defaultLineHeight: "26px",
95
100
  buttonBorderRadius: "20px",
96
101
  buttonBorderWidth: "2px",
@@ -134,15 +139,16 @@ export const BaseStyling = ({ children, properties = {} }: Props) => {
134
139
  warning: "#ff6105",
135
140
  light: "#FFF",
136
141
  grey: "#c3c3c7",
137
- fontFamily: "'Red Hat Display', sans-serif",
138
- fontFamilyCode: "'Red Hat Mono', monospace",
142
+ fontFamily: "Roboto, sans-serif",
143
+ fontFamilyCode: "'Roboto Mono', monospace",
139
144
  fontSize: "1rem",
140
145
  fontSizeH1: "2.5rem",
141
146
  fontSizeH2: "1.625rem",
142
147
  fontSizeH3: "1.5rem",
143
148
  fontSizeH4: "1.25rem",
144
149
  fontSizeSub: ".75rem",
145
- fontSizeCode: "1rem"
150
+ fontSizeCode: "1rem",
151
+ dragBorderStyle: "dashed"
146
152
  };
147
153
 
148
154
  /** We need a loading state, because otherwise you see the colors flash from the default to the possible overridden ones. */
@@ -0,0 +1,211 @@
1
+ import { useUploadFile } from "./useUploadFile";
2
+ import { renderHook } from "@testing-library/react-hooks";
3
+ import { waitFor } from "@testing-library/react";
4
+ import { FileType } from "../Form/FileUpload/FileUpload";
5
+
6
+ const DONE = 4;
7
+
8
+ const mockXhrRequest = (status: number, readyState?: number, response?: Object) => {
9
+ return {
10
+ open: jest.fn(),
11
+ send: jest.fn(),
12
+ addEventListener: jest.fn(),
13
+ onprogress: jest.fn(),
14
+ responseText: response,
15
+ onreadystatechange: jest.fn(),
16
+ getResponseHeader: jest.fn(),
17
+ upload: {
18
+ addEventListener: jest.fn()
19
+ },
20
+ DONE,
21
+ setRequestHeader: jest.fn(),
22
+ readyState: readyState || 4,
23
+ status
24
+ };
25
+ };
26
+
27
+ type mockRequestParams = Parameters<typeof mockXhrRequest>;
28
+
29
+ const progressData = {
30
+ loaded: 12,
31
+ total: 100
32
+ };
33
+
34
+ const requestInfo = { url: "https://www.test.io", withCredentials: true };
35
+
36
+ const file = {
37
+ name: "test.txt",
38
+ data: new File([""], "test.txt"),
39
+ size: 5,
40
+ type: ""
41
+ };
42
+
43
+ const setupXhrEnvironment = (mockParams: mockRequestParams) => {
44
+ const mock = mockXhrRequest(...mockParams);
45
+ const mockClass = () => mock;
46
+
47
+ // @ts-ignore
48
+ window.XMLHttpRequest = jest.fn().mockImplementation(mockClass);
49
+
50
+ return mock;
51
+ };
52
+
53
+ const expectReadyStateInfo = async (
54
+ files: FileType[],
55
+ mock: ReturnType<typeof mockXhrRequest>,
56
+ response: { code: number; body: { message: string } }
57
+ ) => {
58
+ const { result } = renderHook(() =>
59
+ useUploadFile(files, { ...requestInfo, responseErrorPath: "body.message" })
60
+ );
61
+
62
+ expect(mock.addEventListener).toHaveBeenCalled();
63
+ const [[, readystatechange]] = mock.addEventListener.mock.calls;
64
+ await waitFor(() => readystatechange());
65
+ const currentFile = result.current.updatedFiles[0];
66
+ expect(currentFile.status).toEqual("error");
67
+ expect(currentFile.error).toEqual(response.body.message);
68
+ };
69
+
70
+ describe("it should perform upload", () => {
71
+ it("should register the correct progress", async () => {
72
+ const mock = setupXhrEnvironment([200]);
73
+ const { result } = renderHook(() => useUploadFile([file], requestInfo));
74
+ expect(result.current).toBeDefined();
75
+ expect(mock.upload.addEventListener).toHaveBeenCalled();
76
+ const [[, progress]] = mock.upload.addEventListener.mock.calls;
77
+ await waitFor(() => progress(progressData));
78
+
79
+ const targetFile = result.current.updatedFiles[0];
80
+ expect(targetFile.name).toEqual(file.name);
81
+ expect(targetFile.progress).toEqual(12);
82
+ });
83
+
84
+ it("should contain a file with status of retry", async () => {
85
+ const mock = setupXhrEnvironment([0, DONE]);
86
+ const files2 = [
87
+ {
88
+ name: "test2.txt",
89
+ data: new File([""], "test2.txt"),
90
+ size: 5,
91
+ type: ""
92
+ }
93
+ ];
94
+ const { result } = renderHook(() => useUploadFile(files2, requestInfo));
95
+ expect(result.current).toBeDefined();
96
+ expect(mock.addEventListener).toHaveBeenCalled();
97
+ const [[, readystatechange]] = mock.addEventListener.mock.calls;
98
+ await waitFor(() => readystatechange());
99
+ expect(result.current.updatedFiles[0].status).toEqual("retry");
100
+ });
101
+
102
+ it("should contain a file with status of error", async () => {
103
+ const response = { code: 404, body: { message: "Error test" } };
104
+ const mock = setupXhrEnvironment([404, DONE, response]);
105
+ const files3 = [
106
+ {
107
+ name: "test3.txt",
108
+ data: new File([""], "test3.txt"),
109
+ size: 5,
110
+ type: ""
111
+ }
112
+ ];
113
+ await expectReadyStateInfo(files3, mock, response);
114
+ });
115
+
116
+ it("should contain a file with status of server error", async () => {
117
+ const response = { code: 500, body: { message: "Error test" } };
118
+ const mock = setupXhrEnvironment([500, DONE, response]);
119
+ const files6 = [
120
+ {
121
+ name: "test6.txt",
122
+ data: new File([""], "test3.txt"),
123
+ size: 5,
124
+ type: ""
125
+ }
126
+ ];
127
+ await expectReadyStateInfo(files6, mock, response);
128
+ });
129
+
130
+ it("should contain a file with status of success", async () => {
131
+ const mock = setupXhrEnvironment([200, DONE]);
132
+ const files3 = [
133
+ {
134
+ name: "test3.txt",
135
+ data: new File([""], "test3.txt"),
136
+ size: 5,
137
+ type: ""
138
+ }
139
+ ];
140
+ const { result } = renderHook(() => useUploadFile(files3, requestInfo));
141
+ expect(result.current).toBeDefined();
142
+ expect(mock.addEventListener).toHaveBeenCalled();
143
+ const [[, readystatechange]] = mock.addEventListener.mock.calls;
144
+ await waitFor(() => readystatechange());
145
+ const currentFile = result.current.updatedFiles[0];
146
+ expect(currentFile.status).toEqual("completed");
147
+ });
148
+ });
149
+
150
+ describe("should return data according to the parameters", () => {
151
+ it("should call custom callbacks", async () => {
152
+ const onComplete = jest.fn();
153
+ const onProgress = jest.fn();
154
+ const mock = setupXhrEnvironment([200, DONE]);
155
+ const files4 = [
156
+ {
157
+ name: "test4.txt",
158
+ data: new File([""], "test3.txt"),
159
+ size: 5,
160
+ type: ""
161
+ }
162
+ ];
163
+ renderHook(() => useUploadFile(files4, requestInfo, { onProgress, onComplete }));
164
+
165
+ const [[, progress]] = mock.upload.addEventListener.mock.calls;
166
+ await waitFor(() => progress(progressData));
167
+
168
+ const [[, readystatechange]] = mock.addEventListener.mock.calls;
169
+ await waitFor(() => readystatechange());
170
+
171
+ expect(mock.addEventListener).toHaveBeenCalled();
172
+ expect(onComplete).toHaveBeenCalled();
173
+ expect(onComplete).toHaveBeenCalled();
174
+ });
175
+
176
+ it("should add the correct error message", async () => {
177
+ const mock = setupXhrEnvironment([0, DONE]);
178
+ const files5 = [
179
+ {
180
+ name: "test5.txt",
181
+ data: new File([""], "test5.txt"),
182
+ size: 5,
183
+ type: ""
184
+ }
185
+ ];
186
+ const error = "Network error";
187
+ const { result } = renderHook(() =>
188
+ useUploadFile(files5, { ...requestInfo, networkErrorText: error })
189
+ );
190
+ const [[, readystatechange]] = mock.addEventListener.mock.calls;
191
+ await waitFor(() => readystatechange());
192
+
193
+ expect(mock.addEventListener).toHaveBeenCalled();
194
+ const file = result.current.updatedFiles[0];
195
+ expect(file.error).toEqual(error);
196
+ });
197
+ });
198
+
199
+ describe("useFileUpload hook should not fire when url and array of files is not present", () => {
200
+ it("should return an empty array if url is empty", () => {
201
+ const { result } = renderHook(() => useUploadFile([], { url: "https://yoohoo.co" }));
202
+
203
+ expect(result.current.uploadingFiles.length).toBe(0);
204
+ });
205
+
206
+ it("should return an empty array if url is empty", () => {
207
+ const { result } = renderHook(() => useUploadFile([file], { url: "" }));
208
+
209
+ expect(result.current.uploadingFiles.length).toBe(0);
210
+ });
211
+ });
@@ -0,0 +1,136 @@
1
+ /*
2
+ * Copyright 2022 OneWelcome B.V.
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+
17
+ import { FileType } from "../Form/FileUpload/FileUpload";
18
+ import { useEffect, useState } from "react";
19
+ import { getValueByPath } from "../util/helper";
20
+
21
+ export interface UploadResponseType {
22
+ fileList: FileType[];
23
+ status: XMLHttpRequest["status"];
24
+ }
25
+ export interface UseUploadFileCallback {
26
+ onComplete?: (response: UploadResponseType) => void;
27
+ onProgress?: Function;
28
+ }
29
+
30
+ export interface UploadFileRequestParams {
31
+ url: string;
32
+ headers?: Headers;
33
+ responseErrorPath?: string;
34
+ networkErrorText?: string;
35
+ withCredentials?: boolean;
36
+ }
37
+
38
+ export const useUploadFile = (
39
+ files: FileType[],
40
+ request: UploadFileRequestParams,
41
+ callbacks?: UseUploadFileCallback
42
+ ) => {
43
+ const { url, headers, withCredentials, networkErrorText, responseErrorPath } = request;
44
+
45
+ const { onComplete, onProgress } = callbacks || {};
46
+
47
+ const [uploadingFiles, setUploadingFiles] = useState<FileType[]>([]);
48
+ const [updatedFiles, setUpdatedFiles] = useState<FileType[]>([...files]);
49
+
50
+ const getUpdatedList = (
51
+ fileName: string,
52
+ fileStatus: FileType["status"],
53
+ progress?: number,
54
+ error?: string
55
+ ) => {
56
+ return files.map(file => {
57
+ if (file.name === fileName) {
58
+ file.progress = progress;
59
+ file.error = error;
60
+ file.status = fileStatus;
61
+ }
62
+ return { ...file };
63
+ });
64
+ };
65
+
66
+ const recordProgress = (e: ProgressEvent<XMLHttpRequestEventTarget>, fileName: string) => {
67
+ const progress = (e.loaded / e.total) * 100;
68
+ const updatedData = getUpdatedList(fileName, "uploading", progress);
69
+ setUpdatedFiles(updatedData);
70
+ onProgress && onProgress(fileName, progress);
71
+ };
72
+
73
+ const getFileStatus = (
74
+ requestStatus: XMLHttpRequest["status"],
75
+ responseText: XMLHttpRequest["responseText"]
76
+ ) => {
77
+ let fileStatus: FileType["status"] = undefined;
78
+ let error = "";
79
+ if (requestStatus >= 200 && requestStatus < 400) {
80
+ fileStatus = "completed";
81
+ } else if (requestStatus === 0) {
82
+ fileStatus = "retry";
83
+ error =
84
+ networkErrorText || "Network error. Check internet connection and retry uploading the file";
85
+ } else if (requestStatus >= 400 && requestStatus < 500) {
86
+ const response = responseText && JSON.parse(JSON.stringify(responseText));
87
+ fileStatus = "error";
88
+ error = responseErrorPath ? getValueByPath(response, responseErrorPath) : "Bad request";
89
+ } else if (requestStatus >= 500) {
90
+ const response = responseText && JSON.parse(JSON.stringify(responseText));
91
+ fileStatus = "error";
92
+ error = responseErrorPath ? getValueByPath(response, responseErrorPath) : "Server Error";
93
+ }
94
+ return { fileStatus, error };
95
+ };
96
+
97
+ const handleOnComplete = (xhr: XMLHttpRequest, fileName: string) => {
98
+ const { status, readyState, responseText } = xhr;
99
+ if (readyState === xhr.DONE) {
100
+ const { fileStatus, error } = getFileStatus(status, responseText);
101
+ const updatedList = getUpdatedList(fileName, fileStatus, undefined, error);
102
+ setUpdatedFiles(updatedList);
103
+ const response = { fileList: updatedList, status };
104
+ setUploadingFiles(prevState => prevState.filter(selected => selected.name === fileName));
105
+ onComplete && onComplete(response);
106
+ }
107
+ };
108
+
109
+ const uploadFile = (file: FileType) => {
110
+ const xhr = new XMLHttpRequest();
111
+ xhr.upload.addEventListener("progress", e => recordProgress(e, file.name));
112
+ xhr.addEventListener("readystatechange", () => handleOnComplete(xhr, file.name));
113
+ headers && headers.forEach((value, key) => xhr.setRequestHeader(key, value));
114
+ withCredentials && (xhr.withCredentials = true);
115
+ xhr.open("POST", url, true);
116
+ const formData = new FormData();
117
+ formData.append("file", file.data as File);
118
+ formData.append("name", file.name);
119
+ xhr.send(formData);
120
+ };
121
+
122
+ useEffect(() => {
123
+ if (!url || !files.length) {
124
+ return;
125
+ }
126
+
127
+ files.forEach(file => {
128
+ if (!file.status && !uploadingFiles.find(selected => selected.name === file.name)) {
129
+ setUploadingFiles(prevState => [...prevState, file]);
130
+ uploadFile(file);
131
+ }
132
+ });
133
+ }, [url, files]);
134
+
135
+ return { updatedFiles, setUpdatedFiles, uploadingFiles };
136
+ };
@@ -156,17 +156,26 @@
156
156
  }
157
157
  }
158
158
 
159
- @mixin outline {
159
+ @mixin outline(
160
+ $color: var(--light-grey-border),
161
+ $style: var(--input-border-style),
162
+ $width: var(--input-border-width),
163
+ $borderRadius: var(--input-border-radius),
164
+ $backgroundColor: ""
165
+ ) {
160
166
  .outline {
161
167
  pointer-events: none;
162
168
  position: absolute;
163
169
  margin: 0;
164
170
  padding: 0;
165
171
  inset: 0;
166
- border-color: var(--light-grey-border);
167
- border-style: var(--input-border-style);
168
- border-width: var(--input-border-width);
169
- border-radius: var(--input-border-radius);
172
+ border-color: $color;
173
+ border-style: $style;
174
+ border-width: $width;
175
+ border-radius: $borderRadius;
176
+ @if ($backgroundColor != "") {
177
+ background-color: $backgroundColor;
178
+ }
170
179
  @include transition(all, 0.2s, ease-in-out);
171
180
  }
172
181
  }
@@ -249,3 +258,13 @@
249
258
  transition-duration: 0.1ms;
250
259
  }
251
260
  }
261
+
262
+ @mixin width-size($size) {
263
+ .w-#{$size} {
264
+ width: $size * 1%;
265
+ }
266
+ }
267
+
268
+ @for $i from 1 through 100 {
269
+ @include width-size($i + 5);
270
+ }
@@ -16,7 +16,15 @@
16
16
 
17
17
  import React, { useCallback, useEffect, useState } from "react";
18
18
  import { fireEvent, waitFor } from "@testing-library/dom";
19
- import { generateID, filterProps, debounce, throttle } from "./helper";
19
+ import {
20
+ generateID,
21
+ filterProps,
22
+ debounce,
23
+ throttle,
24
+ areArraysDifferent,
25
+ getValueByPath,
26
+ isEqual
27
+ } from "./helper";
20
28
  import { render } from "@testing-library/react";
21
29
 
22
30
  /* Generate an ID of 20 characters with a string woven in */
@@ -165,3 +173,150 @@ describe("throttling works", () => {
165
173
  expect(exampleFunction).not.toHaveBeenCalledTimes(10);
166
174
  });
167
175
  });
176
+
177
+ describe("areArraysDifferent works as expected", () => {
178
+ it("should return true for different arrays", () => {
179
+ const arr1 = [
180
+ {
181
+ name: "test1"
182
+ },
183
+ {
184
+ name: "test2"
185
+ }
186
+ ];
187
+
188
+ const arr2 = [
189
+ {
190
+ name: "test1"
191
+ },
192
+ {
193
+ name: "test3"
194
+ }
195
+ ];
196
+
197
+ const result = areArraysDifferent(arr1, arr2, "name");
198
+ expect(result).toBe(true);
199
+ });
200
+
201
+ it("should return false for arrays with same values", () => {
202
+ const arr1 = [
203
+ {
204
+ name: "test1"
205
+ }
206
+ ];
207
+
208
+ const arr2 = [
209
+ {
210
+ name: "test1"
211
+ }
212
+ ];
213
+
214
+ const result = areArraysDifferent(arr1, arr2, "name");
215
+ expect(result).toBe(false);
216
+ });
217
+
218
+ it("should return false for falsy values", () => {
219
+ const arr1 = [
220
+ {
221
+ name: "test1"
222
+ }
223
+ ];
224
+
225
+ const arr2 = [
226
+ {
227
+ label: "test1"
228
+ }
229
+ ];
230
+
231
+ const result = areArraysDifferent(arr1, arr2, "name");
232
+ expect(result).toBe(true);
233
+ });
234
+ });
235
+
236
+ describe("return correct values from getValueByPath", () => {
237
+ it("should return the correct value form a multi layered object", () => {
238
+ const val = "test";
239
+ const obj = {
240
+ firstNode: {
241
+ secondNode: {
242
+ thirdNode: {
243
+ val
244
+ }
245
+ }
246
+ }
247
+ };
248
+
249
+ const result = getValueByPath(obj, "firstNode.secondNode.thirdNode.val");
250
+ expect(result).toBe(val);
251
+ });
252
+ });
253
+
254
+ describe("verifies if isEqual returns the correct value", () => {
255
+ it("should return true for equal values objects", () => {
256
+ const obj1 = {
257
+ name1: "test1",
258
+ name2: {
259
+ val: "test2"
260
+ }
261
+ };
262
+
263
+ const obj2 = {
264
+ name1: "test1",
265
+ name2: {
266
+ val: "test2"
267
+ }
268
+ };
269
+
270
+ const res = isEqual(obj1, obj2);
271
+ expect(res).toBe(true);
272
+ });
273
+
274
+ it("should return false for unequal values objects", () => {
275
+ const obj1 = {
276
+ name1: "test1",
277
+ name2: {
278
+ val: "test2"
279
+ }
280
+ };
281
+
282
+ const obj2 = {
283
+ name1: "test1"
284
+ };
285
+
286
+ const res = isEqual(obj1, obj2);
287
+ expect(res).toBe(false);
288
+ });
289
+
290
+ it("should return false for falsy values", () => {
291
+ const obj1 = {
292
+ name1: "test1",
293
+ name2: {
294
+ val: "test2"
295
+ }
296
+ };
297
+ const obj2 = null;
298
+
299
+ const res = isEqual(obj1, obj2);
300
+ expect(res).toBe(false);
301
+ });
302
+
303
+ it("should return false for different types", () => {
304
+ const obj1 = [
305
+ {
306
+ name1: "test1",
307
+ name2: {
308
+ val: "test2"
309
+ }
310
+ }
311
+ ];
312
+ const obj2 = {
313
+ name1: "test1",
314
+ name2: {
315
+ val: "test2"
316
+ }
317
+ };
318
+
319
+ const res = isEqual(obj1, obj2);
320
+ expect(res).toBe(false);
321
+ });
322
+ });