Skip to content

Commit

Permalink
TWNTY-3316 - Add tests for modules/spreadsheet-import (#4219)
Browse files Browse the repository at this point in the history
Add tests for `modules/spreadsheet-import`

Co-authored-by: gitstart-twenty <[email protected]>
Co-authored-by: RubensRafael <[email protected]>
  • Loading branch information
3 people authored Feb 29, 2024
1 parent bc11cf8 commit 68a8502
Show file tree
Hide file tree
Showing 19 changed files with 1,236 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import { act, renderHook } from '@testing-library/react';
import { RecoilRoot, useRecoilState } from 'recoil';

import { useSpreadsheetImport } from '@/spreadsheet-import/hooks/useSpreadsheetImport';
import { spreadsheetImportState } from '@/spreadsheet-import/states/spreadsheetImportState';
import { StepType } from '@/spreadsheet-import/steps/components/UploadFlow';
import { RawData, SpreadsheetOptions } from '@/spreadsheet-import/types';

const Wrapper = ({ children }: { children: React.ReactNode }) => (
<RecoilRoot>{children}</RecoilRoot>
);
type SpreadsheetKey = 'spreadsheet_key';

export const mockedSpreadsheetOptions: SpreadsheetOptions<SpreadsheetKey> = {
isOpen: true,
onClose: () => {},
fields: [],
uploadStepHook: async () => [],
selectHeaderStepHook: async (headerValues: RawData, data: RawData[]) => ({
headerValues,
data,
}),
matchColumnsStepHook: async () => [],
rowHook: () => ({ spreadsheet_key: 'rowHook' }),
tableHook: () => [{ spreadsheet_key: 'tableHook' }],
onSubmit: async () => {},
allowInvalidSubmit: false,
customTheme: {},
maxRecords: 10,
maxFileSize: 50,
autoMapHeaders: true,
autoMapDistance: 1,
initialStepState: {
type: StepType.upload,
},
dateFormat: 'MM/DD/YY',
parseRaw: true,
rtl: false,
selectHeader: true,
};

describe('useSpreadsheetImport', () => {
it('should set isOpen to true, and update the options in the Recoil state', async () => {
const { result } = renderHook(
() => ({
useSpreadsheetImport: useSpreadsheetImport<SpreadsheetKey>(),
spreadsheetImportState: useRecoilState(spreadsheetImportState)[0],
}),
{
wrapper: Wrapper,
},
);
expect(result.current.spreadsheetImportState).toStrictEqual({
isOpen: false,
options: null,
});
act(() => {
result.current.useSpreadsheetImport.openSpreadsheetImport(
mockedSpreadsheetOptions,
);
});
expect(result.current.spreadsheetImportState).toStrictEqual({
isOpen: true,
options: mockedSpreadsheetOptions,
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { useState } from 'react';
import { act, renderHook } from '@testing-library/react';

import { useSpreadsheetImportInitialStep } from '@/spreadsheet-import/hooks/useSpreadsheetImportInitialStep';
import { StepType } from '@/spreadsheet-import/steps/components/UploadFlow';

describe('useSpreadsheetImportInitialStep', () => {
it('should return correct number for each step type', async () => {
const { result } = renderHook(() => {
const [step, setStep] = useState<StepType | undefined>();
const { initialStep } = useSpreadsheetImportInitialStep(step);
return { initialStep, setStep };
});

expect(result.current.initialStep).toBe(-1);

act(() => {
result.current.setStep(StepType.upload);
});

expect(result.current.initialStep).toBe(0);

act(() => {
result.current.setStep(StepType.selectSheet);
});

expect(result.current.initialStep).toBe(0);

act(() => {
result.current.setStep(StepType.selectHeader);
});

expect(result.current.initialStep).toBe(0);

act(() => {
result.current.setStep(StepType.matchColumns);
});

expect(result.current.initialStep).toBe(2);

act(() => {
result.current.setStep(StepType.validateData);
});

expect(result.current.initialStep).toBe(3);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { renderHook } from '@testing-library/react';

import { Providers } from '@/spreadsheet-import/components/Providers';
import { mockedSpreadsheetOptions } from '@/spreadsheet-import/hooks/__tests__/useSpreadsheetImport.test';
import { useSpreadsheetImportInternal } from '@/spreadsheet-import/hooks/useSpreadsheetImportInternal';

const Wrapper = ({ children }: { children: React.ReactNode }) => (
<Providers values={mockedSpreadsheetOptions}>{children}</Providers>
);

describe('useSpreadsheetImportInternal', () => {
it('should return the value provided by provider component', async () => {
const { result } = renderHook(() => useSpreadsheetImportInternal(), {
wrapper: Wrapper,
});

expect(result.current).toBe(mockedSpreadsheetOptions);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,209 @@
import {
Data,
Field,
Info,
RowHook,
TableHook,
} from '@/spreadsheet-import/types';
import { addErrorsAndRunHooks } from '@/spreadsheet-import/utils/dataMutations';

describe('addErrorsAndRunHooks', () => {
type FullData = Data<'name' | 'age' | 'country'>;
const requiredField: Field<'name'> = {
key: 'name',
label: 'Name',
validations: [{ rule: 'required' }],
icon: null,
fieldType: { type: 'input' },
};

const regexField: Field<'age'> = {
key: 'age',
label: 'Age',
validations: [
{ rule: 'regex', value: '\\d+', errorMessage: 'Regex error' },
],
icon: null,
fieldType: { type: 'input' },
};

const uniqueField: Field<'country'> = {
key: 'country',
label: 'Country',
validations: [{ rule: 'unique' }],
icon: null,
fieldType: { type: 'input' },
};

const functionValidationFieldTrue: Field<'email'> = {
key: 'email',
label: 'Email',
validations: [
{
rule: 'function',
isValid: () => true,
errorMessage: 'Field is invalid',
},
],
icon: null,
fieldType: { type: 'input' },
};

const functionValidationFieldFalse: Field<'email'> = {
key: 'email',
label: 'Email',
validations: [
{
rule: 'function',
isValid: () => false,
errorMessage: 'Field is invalid',
},
],
icon: null,
fieldType: { type: 'input' },
};

const validData: Data<'name' | 'age'> = { name: 'John', age: '30' };
const dataWithoutNameAndInvalidAge: Data<'name' | 'age'> = {
name: '',
age: 'Invalid',
};
const dataWithDuplicatedValue: FullData = {
name: 'Alice',
age: '40',
country: 'Brazil',
};

const data: Data<'name' | 'age'>[] = [
validData,
dataWithoutNameAndInvalidAge,
];

const basicError: Info = { message: 'Field is invalid', level: 'error' };
const nameError: Info = { message: 'Name Error', level: 'error' };
const ageError: Info = { message: 'Age Error', level: 'error' };
const regexError: Info = { message: 'Regex error', level: 'error' };
const requiredError: Info = { message: 'Field is required', level: 'error' };
const duplicatedError: Info = {
message: 'Field must be unique',
level: 'error',
};

const rowHook: RowHook<'name' | 'age'> = jest.fn((row, addError) => {
addError('name', nameError);
return row;
});
const tableHook: TableHook<'name' | 'age'> = jest.fn((table, addError) => {
addError(0, 'age', ageError);
return table;
});

it('should correctly call rowHook and tableHook and add errors', () => {
const result = addErrorsAndRunHooks(
data,
[requiredField, regexField],
rowHook,
tableHook,
);

expect(rowHook).toHaveBeenCalled();
expect(tableHook).toHaveBeenCalled();
expect(result[0].__errors).toStrictEqual({
name: nameError,
age: ageError,
});
});

it('should overwrite hook errors with validation errors', () => {
const result = addErrorsAndRunHooks(
data,
[requiredField, regexField],
rowHook,
tableHook,
);

expect(rowHook).toHaveBeenCalled();
expect(tableHook).toHaveBeenCalled();
expect(result[1].__errors).toStrictEqual({
name: requiredError,
age: regexError,
});
});

it('should add errors for required field', () => {
const result = addErrorsAndRunHooks(data, [requiredField]);

expect(result[1].__errors).toStrictEqual({
name: requiredError,
});
});

it('should add errors for regex field', () => {
const result = addErrorsAndRunHooks(data, [regexField]);

expect(result[1].__errors).toStrictEqual({
age: regexError,
});
});

it('should add errors for unique field', () => {
const result = addErrorsAndRunHooks(
[
dataWithDuplicatedValue,
dataWithDuplicatedValue,
] as unknown as FullData[],
[uniqueField],
);

expect(result[0].__errors).toStrictEqual({
country: duplicatedError,
});
expect(result[1].__errors).toStrictEqual({
country: duplicatedError,
});
});

it('should add errors for unique field with empty values', () => {
const result = addErrorsAndRunHooks(
[{ country: '' }, { country: '' }],
[uniqueField],
);

expect(result[0].__errors).toStrictEqual({
country: duplicatedError,
});
expect(result[1].__errors).toStrictEqual({
country: duplicatedError,
});
});

it('should not add errors for unique field with empty values if allowEmpty is true', () => {
const result = addErrorsAndRunHooks(
[{ country: '' }, { country: '' }],
[{ ...uniqueField, validations: [{ rule: 'unique', allowEmpty: true }] }],
);

expect(result[0].__errors).toBeUndefined();
expect(result[1].__errors).toBeUndefined();
});

it('should add errors for function validation if result is false', () => {
const result = addErrorsAndRunHooks(
[{ email: 'email' }],
[functionValidationFieldFalse],
);

expect(result[0].__errors).toStrictEqual({
email: basicError,
});
});

it('should not add errors for function validation if result is true', () => {
const result = addErrorsAndRunHooks(
[{ email: 'email' }],
[functionValidationFieldTrue],
);

expect(result[0].__errors).toBeUndefined();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { WorkSheet } from 'xlsx-ugnis';

import { exceedsMaxRecords } from '@/spreadsheet-import/utils/exceedsMaxRecords';

describe('exceedsMaxRecords', () => {
const maxRecords = 5;

it('should return true if the number of records exceeds the maximum limit', () => {
const workSheet: WorkSheet = {
'!ref': 'A1:A10',
};

const result = exceedsMaxRecords(workSheet, maxRecords);

expect(result).toBe(true);
});

it('should return false if the number of records does not exceed the maximum limit', () => {
const workSheet: WorkSheet = {
'!ref': 'A1:A4',
};

const result = exceedsMaxRecords(workSheet, maxRecords);

expect(result).toBe(false);
});

it('should return false if the number of records is equal to the maximum limit', () => {
const workSheet: WorkSheet = {
'!ref': 'A1:A5',
};

const result = exceedsMaxRecords(workSheet, maxRecords);

expect(result).toBe(false);
});

it('should return false if the worksheet does not have a defined range', () => {
const workSheet: WorkSheet = {};

const result = exceedsMaxRecords(workSheet, maxRecords);

expect(result).toBe(false);
});
});
Loading

0 comments on commit 68a8502

Please sign in to comment.