-
Notifications
You must be signed in to change notification settings - Fork 23
example data hook #52
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
7e1f352
cbde009
16be29d
efd4983
c3d9fd6
81ceaee
a4001f5
3d5298b
0888557
fd54de0
957fb1b
2bde1a5
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,75 @@ | ||
| import { TextField, Sheet, Workbook } from '@flatfile/configure' | ||
| import { SheetTester } from '../../../../src/utils/testing/SheetTester' | ||
| import { format } from 'date-fns' | ||
|
|
||
| //This is an example of storing the data hook outside your sheet as a helper function that can be referenced by multiple fields in your sheet. | ||
| //Here we define the function | ||
| const formatDate = | ||
| (update: string) => | ||
| (value: string): string => { | ||
| try { | ||
| return format(new Date(value), update) | ||
| } catch (err) { | ||
| return value | ||
| } | ||
| } | ||
|
|
||
| //Here we call the function in the compute hook and specify the format that should be used when reformatting the dates | ||
| const testSheet = new Sheet('Test Sheet', { | ||
| joinDate: TextField({ | ||
| compute: formatDate('MM/dd/yyyy'), | ||
| }), | ||
| }) | ||
|
|
||
| //Here we | ||
| const TestWorkbook = new Workbook({ | ||
| name: 'Test Workbook', | ||
| namespace: 'Test', | ||
| sheets: { testSheet }, | ||
| }) | ||
|
|
||
| describe('Workbook tests ->', () => { | ||
| // here we use Sheet tester | ||
| const testSheet = new SheetTester(TestWorkbook, 'testSheet') | ||
| test('Change Date format with written month', async () => { | ||
| // for this inputRow | ||
| const inputRow = { joinDate: 'Nov 12, 2020' } | ||
| console.log(new Date(inputRow.joinDate)) | ||
| // we expect this output row | ||
| const expectedOutputRow = { joinDate: '11/12/2020' } | ||
| const res = await testSheet.testRecord(inputRow) | ||
| //call the expectation here | ||
| expect(res).toMatchObject(expectedOutputRow) | ||
| }) | ||
|
|
||
| //additional tests for additional cases | ||
| test('Change Date Format with dashes', async () => { | ||
| const inputRow = { joinDate: '12-31-2020' } | ||
| console.log(new Date(inputRow.joinDate)) | ||
| const expectedOutputRow = { joinDate: '12/31/2020' } | ||
| const res = await testSheet.testRecord(inputRow) | ||
| expect(res).toMatchObject(expectedOutputRow) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Stylistic thing, I don't like defining the expected input and output so far apart. Instead I would have the last lines as this or even Generally for a bunch of test cases, you want the important code that differs between the cases to jump out at you, the boilerplate should fade away. |
||
| }) | ||
|
|
||
| test('Change date format day-month-year', async () => { | ||
| const inputRow = { joinDate: '14 Jan 2022' } | ||
| const expectedOutputRow = { joinDate: '01/14/2022' } | ||
| const res = await testSheet.testRecord(inputRow) | ||
| expect(res).toMatchObject(expectedOutputRow) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ideally we would have a method on sheetTester so you can test an individual field, not even caring about the name of the field. so you could just say |
||
| }) | ||
|
|
||
| test('Change date with period delimiters', async () => { | ||
| const inputRow = { joinDate: '12.31.2022' } | ||
| const expectedOutputRow = { joinDate: '12/31/2022' } | ||
| const res = await testSheet.testRecord(inputRow) | ||
| expect(res).toMatchObject(expectedOutputRow) | ||
| }) | ||
|
|
||
| //This format tool does have some limitations, so knowing what some of those are so they can be handled appropriately elsewhere can be valuable | ||
| test('Date format this hook cannot handle', async () => { | ||
| const inputRow = { joinDate: '12242022' } | ||
| const expectedOutputRow = { joinDate: '12242022' } | ||
| const res = await testSheet.testRecord(inputRow) | ||
| expect(res).toMatchObject(expectedOutputRow) | ||
| }) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would change your format function for this example, so that if it gets an unexpected input, it throws the error. Don't swallow errors and continue processing. If any field hook throws an exception, the platform-sdk, will keep the original value of the field (double check the original value is kept, not the value to that point), and set a message on that cell of the error thrown. I would rewrite your compute function so that it throws an error on unexpected input, then use
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Take a look here https://github.com/FlatFilers/platform-sdk-starter/blob/main/examples/fields/SmartDateField.spec.ts#L136-L159 but make it simpler and more explicit for this field |
||
| }) | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,62 @@ | ||
| import { TextField, Sheet, Workbook } from '@flatfile/configure' | ||
| import { SheetTester } from '../../../src/utils/testing/SheetTester' | ||
|
|
||
| const removeExtraSpaces = new Sheet('removeExtraSpaces', { | ||
| //here we set up a field | ||
| departmentName: TextField({ | ||
| //we define a compute hook | ||
| compute: (value: any) => { | ||
| //matches two or more whitespace characters and replaces them with one space | ||
| return value.replace(/\s{2,}/g, ' ') | ||
| }, | ||
| }), | ||
| }) | ||
|
|
||
| const TestWorkbook = new Workbook({ | ||
| name: 'Test Workbook', | ||
| namespace: 'Test', | ||
| sheets: { removeExtraSpaces }, | ||
| }) | ||
|
|
||
| describe('Workbook tests ->', () => { | ||
| // here we use Sheet tester | ||
| const testSheet = new SheetTester(TestWorkbook, 'removeExtraSpaces') | ||
| test('Remove Extra Leading Spaces', async () => { | ||
| // for this inputRow | ||
| const inputRow = { departmentName: ' asdf' } | ||
| // we expect this output row | ||
| const expectedOutputRow = { departmentName: ' asdf' } | ||
| const res = await testSheet.testRecord(inputRow) | ||
| //call the expectation here | ||
| expect(res).toMatchObject(expectedOutputRow) | ||
| }) | ||
|
|
||
| //additional tests for additional cases | ||
| test('Test set of numbers', async () => { | ||
| const inputRow = { departmentName: '123456789123456' } | ||
| const expectedOutputRow = { departmentName: '123456789123456' } | ||
| const res = await testSheet.testRecord(inputRow) | ||
| expect(res).toMatchObject(expectedOutputRow) | ||
| }) | ||
|
|
||
| test('Test trailing spaces', async () => { | ||
| const inputRow = { departmentName: 'asdf ' } | ||
| const expectedOutputRow = { departmentName: 'asdf ' } | ||
| const res = await testSheet.testRecord(inputRow) | ||
| expect(res).toMatchObject(expectedOutputRow) | ||
| }) | ||
|
|
||
| test('Test multiple instances of extra spaces', async () => { | ||
| const inputRow = { departmentName: ' as df' } | ||
| const expectedOutputRow = { departmentName: ' as df' } | ||
| const res = await testSheet.testRecord(inputRow) | ||
| expect(res).toMatchObject(expectedOutputRow) | ||
| }) | ||
|
|
||
| test('Test set of numbers', async () => { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. please change the name. this isn't a set of numbers |
||
| const inputRow = { departmentName: 'Paddy Mullen' } | ||
| const expectedOutputRow = { departmentName: 'Paddy Mullen' } | ||
| const res = await testSheet.testRecord(inputRow) | ||
| expect(res).toMatchObject(expectedOutputRow) | ||
| }) | ||
| }) | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,49 @@ | ||
| import { TextField, Sheet, Workbook } from '@flatfile/configure' | ||
| import { SheetTester } from '../../../../src/utils/testing/SheetTester' | ||
|
|
||
| //This hook checks for and removes special characters in a string using RegEx. | ||
| const removeSymbolsSheet = new Sheet('removeSymbolsSheet', { | ||
| //set up your field | ||
| zipCode: TextField({ | ||
| //define a compute hook | ||
| compute: (value: any) => { | ||
| //add the regular expression you'll be using to search for invalid characters, then remove those characters | ||
| return value.replace(/[*;/{}\[\]"_#'^><|]/g, '') | ||
| }, | ||
| }), | ||
| }) | ||
|
|
||
| const TestWorkbook = new Workbook({ | ||
| name: 'Test Workbook', | ||
| namespace: 'Test', | ||
| sheets: { removeSymbolsSheet }, | ||
| }) | ||
|
|
||
| describe('Workbook tests ->', () => { | ||
| // here we use Sheet tester | ||
| const testSheet = new SheetTester(TestWorkbook, 'removeSymbolsSheet') | ||
| test('Remove Extra Symbols', async () => { | ||
| // for this inputRow | ||
| const inputRow = { zipCode: '234**23' } | ||
| // we expect this output row | ||
| const expectedOutputRow = { zipCode: '23423' } | ||
| const res = await testSheet.testRecord(inputRow) | ||
| //call the expectation here | ||
| expect(res).toMatchObject(expectedOutputRow) | ||
| }) | ||
|
|
||
| //additional tests for additional cases | ||
| test('Test set of numbers', async () => { | ||
| const inputRow = { zipCode: '^^55555' } | ||
| const expectedOutputRow = { zipCode: '55555' } | ||
| const res = await testSheet.testRecord(inputRow) | ||
| expect(res).toMatchObject(expectedOutputRow) | ||
| }) | ||
|
|
||
| test('Test trailing spaces', async () => { | ||
| const inputRow = { zipCode: 'M4B [1G5]' } | ||
| const expectedOutputRow = { zipCode: 'M4B 1G5' } | ||
| const res = await testSheet.testRecord(inputRow) | ||
| expect(res).toMatchObject(expectedOutputRow) | ||
| }) | ||
| }) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,75 @@ | ||
| import { Message, TextField, Sheet, Workbook } from '@flatfile/configure' | ||
| import { SheetTester } from '../../../../src/utils/testing/SheetTester' | ||
|
|
||
| const testSheet = new Sheet('Test Sheet', { | ||
| cellPhone: TextField({ | ||
| label: 'Cell Phone Number', | ||
| validate: (value: string) => { | ||
| /*the below regex matches these phone number types: | ||
| 123-456-7890 | ||
| (123) 456-7890 | ||
| 123 456 7890 | ||
| 123.456.7890 | ||
| +91 (123) 456-7890 */ | ||
| const regex = /^(\+\d{1,2}\s)?\(?\d{3}\)?[\s.-]\d{3}[\s.-]\d{4}$/ | ||
| if (!value.match(regex)) { | ||
| return [ | ||
| new Message( | ||
| `${value} is an invalid phone number`, | ||
| 'error', | ||
| 'validate' | ||
| ), | ||
| ] | ||
| } | ||
| }, | ||
| }), | ||
| }) | ||
|
|
||
| const TestWorkbook = new Workbook({ | ||
| name: 'Test Workbook', | ||
| namespace: 'Test', | ||
| sheets: { testSheet }, | ||
| }) | ||
|
|
||
| describe('Workbook tests ->', () => { | ||
| // here we use Sheet tester | ||
| const testSheet = new SheetTester(TestWorkbook, 'testSheet') | ||
| test('Reformat phone number with international code', async () => { | ||
| // for this inputRow | ||
| const inputRow = { cellPhone: '15555555555' } | ||
| // we expect this output row | ||
| const expectedOutputRow = { cellPhone: '+1 (555) 555-5555' } | ||
| const res = await testSheet.testRecord(inputRow) | ||
| //call the expectation here | ||
| expect(res).toMatchObject(expectedOutputRow) | ||
| }) | ||
|
|
||
| //additional tests for additional cases | ||
| test('Remove letters, number too short', async () => { | ||
| const inputRow = { cellPhone: '12321 ggg' } | ||
| const expectedOutputRow = { cellPhone: 'Invalid phone number' } | ||
| const res = await testSheet.testRecord(inputRow) | ||
| expect(res).toMatchObject(expectedOutputRow) | ||
| }) | ||
|
|
||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @paddymul this hook is waiting on the ability to test the messages that are returned, since it's a Validate hook. That's why my tests are incomplete here. |
||
| // test('Test trailing spaces', async () => { | ||
| // const inputRow = { cellPhone: 'asdf ' } | ||
| // const expectedOutputRow = { cellPhone: 'asdf ' } | ||
| // const res = await testSheet.testRecord(inputRow) | ||
| // expect(res).toMatchObject(expectedOutputRow) | ||
| // }) | ||
|
|
||
| // test('Test multiple instances of extra spaces', async () => { | ||
| // const inputRow = { cellPhone: ' as df' } | ||
| // const expectedOutputRow = { cellPhone: ' as df' } | ||
| // const res = await testSheet.testRecord(inputRow) | ||
| // expect(res).toMatchObject(expectedOutputRow) | ||
| // }) | ||
|
|
||
| // test('Test set of numbers', async () => { | ||
| // const inputRow = { cellPhone: 'Paddy Mullen' } | ||
| // const expectedOutputRow = { cellPhone: 'Paddy Mullen' } | ||
| // const res = await testSheet.testRecord(inputRow) | ||
| // expect(res).toMatchObject(expectedOutputRow) | ||
| // }) | ||
| }) | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,51 @@ | ||
| import { NumberField, TextField, Sheet, Workbook } from '@flatfile/configure' | ||
| import { SheetTester } from '../../../src/utils/testing/SheetTester' | ||
|
|
||
| //this is a basic example of how to modify a field based on other fields | ||
| const testSheet = new Sheet( | ||
| 'Test Sheet', | ||
| { | ||
| paymentStatus: TextField(), | ||
| amount: NumberField(), | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Side note. we are encouraging all Fields to be called with at least an empty object |
||
| }, | ||
| { | ||
| //because this hook is comparing multiple fields and both modifying a field and adding a message, it must be run as a recordHook rather than a fieldHook | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would recommend: Adding a message at the same time shouldn't be promoted as a reason to use |
||
| recordCompute: (record, session, logger) => { | ||
| if (record.get('paymentStatus') && !record.get('amount')) { | ||
| record.set('paymentStatus', '') | ||
| record.addWarning( | ||
| 'paymentStatus', | ||
| 'Amount must be greater than 0 to select a Payment Status' | ||
| ) | ||
| } | ||
| }, | ||
| } | ||
| ) | ||
|
|
||
| const TestWorkbook = new Workbook({ | ||
| name: 'Test Workbook', | ||
| namespace: 'Test', | ||
| sheets: { testSheet }, | ||
| }) | ||
|
|
||
| describe('Workbook tests ->', () => { | ||
| // here we use Sheet tester | ||
| const testSheet = new SheetTester(TestWorkbook, 'testSheet') | ||
| test('Empty the Cell', async () => { | ||
| // for this inputRow | ||
| const inputRow = { paymentStatus: 'Paid', amount: null } | ||
| // we expect this output row | ||
| const expectedOutputRow = { paymentStatus: '', amount: null } | ||
| const res = await testSheet.testRecord(inputRow) | ||
| //call the expectation here | ||
| expect(res).toMatchObject(expectedOutputRow) | ||
| }) | ||
|
|
||
| //additional tests for additional cases | ||
| test('test value in paymentStatus', async () => { | ||
| const inputRow = { paymentStatus: 'Paid', amount: 2500 } | ||
| const expectedOutputRow = { paymentStatus: 'Paid', amount: 2500 } | ||
| const res = await testSheet.testRecord(inputRow) | ||
| expect(res).toMatchObject(expectedOutputRow) | ||
| }) | ||
| }) | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,76 @@ | ||
| import { Sheet, Workbook, BooleanField, TextField } from '@flatfile/configure' | ||
| import { SheetTester } from '../../../src/utils/testing/SheetTester' | ||
|
|
||
| //this Data Hook can be used to update the contents of a cell based on whether or not another cell passes RegEx validation. | ||
| //This method of setting fields at validations at the top of a sheet allows you to set all RegEx and messages in one easily referencable place. | ||
| const FIELDS = { | ||
| accountId: { | ||
| regex: /^.{1,20}$/, | ||
| message: 'Account Number must be 20 characters or less', | ||
| }, | ||
| } | ||
|
|
||
| const testSheet = new Sheet( | ||
| 'Test Sheet', | ||
| { | ||
| accountId: TextField(), | ||
| accountStatus: BooleanField(), | ||
| }, | ||
| { | ||
| recordCompute: (record) => { | ||
| //ensuring the accountId is returned as a string | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. because accountID is a I would recommend explicitly handling the |
||
| const accountNum = record.get('accountId') as string | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @paddymul I needed to ensure I was returning the accountId field as a string here otherwise TypeScript was concerned that the TPrimitive type could be a number, boolean, string, or null. Is there a better way to ensure this is a string to be able to test properly? |
||
| //here we reference the fields we defined at the top, the RegEx, and the error message we want to display if the field does not pass its RegEx. | ||
| if (FIELDS['accountId'].regex.test(accountNum) === false) { | ||
| record.set('accountStatus', false) | ||
| record.addError('accountId', FIELDS.accountId.message) | ||
| } else { | ||
| record.set('accountStatus', true) | ||
| } | ||
| }, | ||
| } | ||
| ) | ||
|
|
||
| const TestWorkbook = new Workbook({ | ||
| name: 'Test Workbook', | ||
| namespace: 'Test', | ||
| sheets: { testSheet }, | ||
| }) | ||
|
|
||
| describe('Workbook tests ->', () => { | ||
| // here we use Sheet tester | ||
| const testSheet = new SheetTester(TestWorkbook, 'testSheet') | ||
| test('21 Characters', async () => { | ||
| // for this inputRow | ||
| const inputRow = { accountId: '1234567890asdfghjkjhg', accountStatus: true } | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. These are tests that acutally need |
||
| // we expect this output row | ||
| const expectedOutputRow = { | ||
| accountId: '1234567890asdfghjkjhg', | ||
| accountStatus: false, | ||
| } | ||
| const res = await testSheet.testRecord(inputRow) | ||
| //call the expectation here | ||
| expect(res).toMatchObject(expectedOutputRow) | ||
| }) | ||
|
|
||
| //additional tests for additional cases | ||
| test('19 Characters', async () => { | ||
| const inputRow = { accountId: '1234567890asdfghjkj', accountStatus: true } | ||
| const expectedOutputRow = { | ||
| accountId: '1234567890asdfghjkj', | ||
| accountStatus: true, | ||
| } | ||
| const res = await testSheet.testRecord(inputRow) | ||
| expect(res).toMatchObject(expectedOutputRow) | ||
| }) | ||
|
|
||
| test('Test Unexpected Characters', async () => { | ||
| const inputRow = { accountId: 'd sdfl &&^$% 23', accountStatus: true } | ||
| const expectedOutputRow = { | ||
| accountId: 'd sdfl &&^$% 23', | ||
| accountStatus: true, | ||
| } | ||
| const res = await testSheet.testRecord(inputRow) | ||
| expect(res).toMatchObject(expectedOutputRow) | ||
| }) | ||
| }) | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think this code actually runs. it looks like a function that returns a function. Are you sure this is what you wanted?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
oh, I see, that's waht you meant to do. Well done, you should mention this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll be honest, this is code from @hansjhoffman that I rewrote for this example so I don't know how to best explain it 😆
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is also an example that I might remove with the launch of SmartDateField, since that would cover the basically same formatting changes this is capable of handling.