From 7ebbaff2d2d1cdc7d4f36284f38f3653d486542a Mon Sep 17 00:00:00 2001 From: Paddy Mullen Date: Wed, 9 Nov 2022 12:00:55 -0500 Subject: [PATCH 1/9] feat: first pull in of a test from platform-sdk-mono --- examples/SubstitutionCast.spec.ts | 42 +++++++++++++++++++++++++++++++ jest.config.js | 2 +- 2 files changed, 43 insertions(+), 1 deletion(-) create mode 100644 examples/SubstitutionCast.spec.ts diff --git a/examples/SubstitutionCast.spec.ts b/examples/SubstitutionCast.spec.ts new file mode 100644 index 0000000..64b3d4e --- /dev/null +++ b/examples/SubstitutionCast.spec.ts @@ -0,0 +1,42 @@ +import { SubstitutionCast } from '@flatfile/configure' + + +describe('Cast Function tests ->', () => { + const makeCastAssert = (castFn: any) => { + const assertFn = (raw: any, output: any): void => { + expect(castFn(raw)).toBe(output) + } + return assertFn + } + const makeCastAssertException = (castFn: any) => { + const assertFn = (raw: any, error: string): void => { + expect(() => { + castFn(raw) + }).toThrow(error) + } + return assertFn + } + + test('SubstitutionCast works ', () => { + const numberSet = [ + ['1', 'one', 'un'], + ['2', 'two', 'dos'], + ] + + const SpanishNum = SubstitutionCast( + numberSet, + 2, + (val) => `Couldn't convert '${val}' to a spanish number` + ) + + const assertNC = makeCastAssert(SpanishNum) + const assertThrow = makeCastAssertException(SpanishNum) + + assertNC('1', 'un') + assertNC('two', 'dos') + assertThrow( + 'not a number', + "Couldn't convert 'not a number' to a spanish number" + ) + }) +}) diff --git a/jest.config.js b/jest.config.js index 997df40..f5c3e51 100644 --- a/jest.config.js +++ b/jest.config.js @@ -1,5 +1,5 @@ module.exports = { - roots: ['/src'], + roots: ['/src', '/examples'], testEnvironment: 'node', transform: { '^.+\\.tsx?$': 'ts-jest', From 0a0644feb4927a6c7714f1a54b5406a850a85972 Mon Sep 17 00:00:00 2001 From: Paddy Mullen Date: Wed, 9 Nov 2022 12:56:13 -0500 Subject: [PATCH 2/9] working use of sheet tester --- examples/SubstitutionCast.spec.ts | 106 +++++++++++++++++++----------- 1 file changed, 66 insertions(+), 40 deletions(-) diff --git a/examples/SubstitutionCast.spec.ts b/examples/SubstitutionCast.spec.ts index 64b3d4e..f161690 100644 --- a/examples/SubstitutionCast.spec.ts +++ b/examples/SubstitutionCast.spec.ts @@ -1,42 +1,68 @@ -import { SubstitutionCast } from '@flatfile/configure' - - -describe('Cast Function tests ->', () => { - const makeCastAssert = (castFn: any) => { - const assertFn = (raw: any, output: any): void => { - expect(castFn(raw)).toBe(output) - } - return assertFn - } - const makeCastAssertException = (castFn: any) => { - const assertFn = (raw: any, error: string): void => { - expect(() => { - castFn(raw) - }).toThrow(error) - } - return assertFn - } - - test('SubstitutionCast works ', () => { - const numberSet = [ - ['1', 'one', 'un'], - ['2', 'two', 'dos'], - ] - - const SpanishNum = SubstitutionCast( - numberSet, - 2, - (val) => `Couldn't convert '${val}' to a spanish number` - ) - - const assertNC = makeCastAssert(SpanishNum) - const assertThrow = makeCastAssertException(SpanishNum) - - assertNC('1', 'un') - assertNC('two', 'dos') - assertThrow( - 'not a number', - "Couldn't convert 'not a number' to a spanish number" - ) +import { Sheet, TextField, Workbook, SubstitutionCast } from '@flatfile/configure' +import { SheetTester } from '../src/utils/testing/SheetTester' + +const numberSet = [ + ['1', 'one', 'un'], + ['2', 'two', 'dos'], +] + +const SpanishNum = SubstitutionCast( + numberSet, + 2, + (val) => `Couldn't convert '${val}' to a spanish number` +) + +// note sheet must have same name as key in workbook it is shared as +const SubSheet = new Sheet( + 'SubSheet', + {numField: TextField({cast:SpanishNum})}) + +const TestWorkbook = new Workbook({ + name: `Test Workbook`, + namespace: 'test', + // saving SubSheet to workbook under key SubSheet + sheets: { SubSheet }, +}) + +describe('Workbook tests ->', () => { + const testSheet = new SheetTester(TestWorkbook, 'SubSheet') + test('Spanish number word works', async () => { + const inputRow = { numField: 'un'} + const expectedOutputRow = { numField: 'un'} + const res = await testSheet.testRecord(inputRow) + expect(res).toMatchObject(expectedOutputRow) }) + + + + +// describe('Cast Function tests ->', () => { +// const makeCastAssert = (castFn: any) => { +// const assertFn = (raw: any, output: any): void => { +// expect(castFn(raw)).toBe(output) +// } +// return assertFn +// } +// const makeCastAssertException = (castFn: any) => { +// const assertFn = (raw: any, error: string): void => { +// expect(() => { +// castFn(raw) +// }).toThrow(error) +// } +// return assertFn +// } + +// test('SubstitutionCast works ', () => { + + +// const assertNC = makeCastAssert(SpanishNum) +// const assertThrow = makeCastAssertException(SpanishNum) + +// assertNC('1', 'un') +// assertNC('two', 'dos') +// assertThrow( +// 'not a number', +// "Couldn't convert 'not a number' to a spanish number" +// ) +// }) }) From 2f230958910aaf460e7e79fe4ee7bb32d789db10 Mon Sep 17 00:00:00 2001 From: Paddy Mullen Date: Wed, 9 Nov 2022 12:57:08 -0500 Subject: [PATCH 3/9] working use of sheet tester --- examples/SubstitutionCast.spec.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/examples/SubstitutionCast.spec.ts b/examples/SubstitutionCast.spec.ts index f161690..2cf95df 100644 --- a/examples/SubstitutionCast.spec.ts +++ b/examples/SubstitutionCast.spec.ts @@ -33,6 +33,13 @@ describe('Workbook tests ->', () => { expect(res).toMatchObject(expectedOutputRow) }) + test('Convert to spanish number word works', async () => { + const inputRow = { numField: '1'} + const expectedOutputRow = { numField: 'un'} + const res = await testSheet.testRecord(inputRow) + expect(res).toMatchObject(expectedOutputRow) + }) + From 8dbdd2029ca983d8ef3d4665aa6cbf5226e0dce3 Mon Sep 17 00:00:00 2001 From: Paddy Mullen Date: Wed, 9 Nov 2022 13:03:24 -0500 Subject: [PATCH 4/9] WIP, actually using sheetTester --- examples/SubstitutionCast.spec.ts | 44 ++++++++----------------------- 1 file changed, 11 insertions(+), 33 deletions(-) diff --git a/examples/SubstitutionCast.spec.ts b/examples/SubstitutionCast.spec.ts index 2cf95df..49fa14c 100644 --- a/examples/SubstitutionCast.spec.ts +++ b/examples/SubstitutionCast.spec.ts @@ -34,42 +34,20 @@ describe('Workbook tests ->', () => { }) test('Convert to spanish number word works', async () => { - const inputRow = { numField: '1'} + const inputRow = { numField: 'un'} const expectedOutputRow = { numField: 'un'} const res = await testSheet.testRecord(inputRow) expect(res).toMatchObject(expectedOutputRow) - }) - - - -// describe('Cast Function tests ->', () => { -// const makeCastAssert = (castFn: any) => { -// const assertFn = (raw: any, output: any): void => { -// expect(castFn(raw)).toBe(output) -// } -// return assertFn -// } -// const makeCastAssertException = (castFn: any) => { -// const assertFn = (raw: any, error: string): void => { -// expect(() => { -// castFn(raw) -// }).toThrow(error) -// } -// return assertFn -// } - -// test('SubstitutionCast works ', () => { - - -// const assertNC = makeCastAssert(SpanishNum) -// const assertThrow = makeCastAssertException(SpanishNum) + const res2 = await testSheet.testRecord({ numField: 'two'}) + expect(res).toMatchObject({ numField: 'dos' }) + }) -// assertNC('1', 'un') -// assertNC('two', 'dos') -// assertThrow( -// 'not a number', -// "Couldn't convert 'not a number' to a spanish number" -// ) -// }) + // test('see how an error is handled ', async () => { + // // hold off for Paddy to fix + // const inputRow = { numField: 'not a number'} + // const expectedOutputRow = { numField: 'sadf'} + // const res = await testSheet.testRecord(inputRow) + // expect(res).toMatchObject(expectedOutputRow) + // }) }) From 456ad5afecf5186de994c1ea2fa0d5c988a22021 Mon Sep 17 00:00:00 2001 From: Paddy Mullen Date: Mon, 14 Nov 2022 10:35:42 -0500 Subject: [PATCH 5/9] feat: add descriptive comments --- examples/SubstitutionCast.spec.ts | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/examples/SubstitutionCast.spec.ts b/examples/SubstitutionCast.spec.ts index 49fa14c..71c5e75 100644 --- a/examples/SubstitutionCast.spec.ts +++ b/examples/SubstitutionCast.spec.ts @@ -1,21 +1,32 @@ import { Sheet, TextField, Workbook, SubstitutionCast } from '@flatfile/configure' import { SheetTester } from '../src/utils/testing/SheetTester' -const numberSet = [ + + + +const spanishNumeralSynonyms = [ ['1', 'one', 'un'], ['2', 'two', 'dos'], ] -const SpanishNum = SubstitutionCast( - numberSet, + +const SpanishNumeralCast = SubstitutionCast( + spanishNumeralSynonyms, 2, (val) => `Couldn't convert '${val}' to a spanish number` ) +// Substitution cast is used to coerce synonym values to a desired +// value. In this case '1', 'one', and 'un' are synonyms. If any of +// the three are found in a field using this cast, the value is set as +// the last item of the list. Substitution cast compares string case +// insensitive. + + // note sheet must have same name as key in workbook it is shared as const SubSheet = new Sheet( 'SubSheet', - {numField: TextField({cast:SpanishNum})}) + {numField: TextField({cast:SpanishNumeralCast})}) const TestWorkbook = new Workbook({ name: `Test Workbook`, @@ -25,11 +36,15 @@ const TestWorkbook = new Workbook({ }) describe('Workbook tests ->', () => { + // here we use Sheet tester const testSheet = new SheetTester(TestWorkbook, 'SubSheet') test('Spanish number word works', async () => { + // for this inputRow const inputRow = { numField: 'un'} + // we expect this output row const expectedOutputRow = { numField: 'un'} const res = await testSheet.testRecord(inputRow) + //call the expectation here expect(res).toMatchObject(expectedOutputRow) }) From a4e1b5e3bf610a2c4d5e16f485eed0f7eda88b21 Mon Sep 17 00:00:00 2001 From: Paddy Mullen Date: Tue, 15 Nov 2022 07:42:46 -0500 Subject: [PATCH 6/9] feat: sheetTester that cna test for info messages --- src/utils/testing/SheetTester.ts | 61 +++++++++++++++++++++++++++++--- 1 file changed, 56 insertions(+), 5 deletions(-) diff --git a/src/utils/testing/SheetTester.ts b/src/utils/testing/SheetTester.ts index e30aefb..6f3acb9 100644 --- a/src/utils/testing/SheetTester.ts +++ b/src/utils/testing/SheetTester.ts @@ -1,6 +1,5 @@ import _ from 'lodash' - -import { FlatfileRecords, FlatfileSession, IPayload } from '@flatfile/hooks' +import { IRecordInfo, TRecordData, TPrimitive, FlatfileRecords, FlatfileSession, IPayload } from '@flatfile/hooks' import { Workbook } from '@flatfile/configure' export class SheetTester { @@ -97,14 +96,66 @@ export class SheetTester { return transform(pkey, value) } - public async testRecord(recordBatch: {}) { - const transformedRecords = await this.transformRecords([recordBatch]) + public async testRecord(record: {}) { + const transformedRecords = await this.transformRecords([record]) return transformedRecords.records[0].value } - public async testRecords(recordBatch: any[]) { + public async testRecords(recordBatch: Record[]) { const transformedRecords = await this.transformRecords(recordBatch) return transformedRecords.records.map((r) => r.value) } + public async testMessage(record: {}) { + const transformedRecords = await this.transformRecords([record]) + return transformedRecords.records.map((r) => r.toJSON().info)[0] + } + public async testMessages(recordBatch: Record[]) { + const transformedRecords = await this.transformRecords(recordBatch) + return transformedRecords.records.map((r) => r.toJSON().info) + } + +} + +// export interface InfoObj { +// field: string +// message: string +// level: TRecordStageLevel +// stage: 'validate' | 'compute' +// } + +export type InfoObj = IRecordInfo, string | number> + +export const removeUndefineds = (obj:Record) => _.pickBy(obj, _.identity) +export const matchMessages = (messages:InfoObj[], field?:string, message?:string, level?:string): false| any[] => { + + const results = _.filter(messages, removeUndefineds({field,message,level})) + if (results.length > 0) { + return results + } + return false } + +export const matchSingleMessage = ( + messages:InfoObj[], field?:string, message?:string, level?:string): false| any => { + const results = matchMessages(messages, field, message, level) + if (results === false) { + return false + } + if (results.length === 1) { + return results[0] + } + if (results.length > 1) { + throw new Error("more than one message returned") + } + if (results.length === 0) { + //unreachable + return false + } + //unreachable + return false +} + +//use the match functions like +// const res = await testSheet.testMessage(inputRow) +// expect(matchSingleMessage(res, 'numField', 'more than 5', 'error')).toBeTruthy() From fc719a9460e7c8b45f27527361a1b62cf352d85e Mon Sep 17 00:00:00 2001 From: Paddy Mullen Date: Tue, 15 Nov 2022 14:51:13 -0500 Subject: [PATCH 7/9] initial constraint and test --- examples/constraintsExample.spec.ts | 76 +++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 examples/constraintsExample.spec.ts diff --git a/examples/constraintsExample.spec.ts b/examples/constraintsExample.spec.ts new file mode 100644 index 0000000..a861471 --- /dev/null +++ b/examples/constraintsExample.spec.ts @@ -0,0 +1,76 @@ +import { Sheet, TextField, Workbook, SubstitutionCast } from '@flatfile/configure' +import { SheetTester, matchSingleMessage } from '../src/utils/testing/SheetTester' + +/* +K. + +1. Conditionally Null - Set field to null + throw warning if another field has a certain value +Field A: select either -> "Apples" or "Oranges" or "Bananas" +Field B: if "Apples" or "Bananas" (or N number of items) is selected on Field A -> set Field B to null and throw warning on Field B if there was data was cleared out of Field B. Warning message should contain the data that was cleared incase the user want to add it somewhere else. + +2. Conditionally Required - Required if a different field has a certain value, otherwise set it to null +Field A: select either -> "Apples" or "Oranges" or "Bananas" +Field B: if "Apples" or "Bananas" (or N number of items) on Field A and Field B is empty --> throw error if required. + if "Oranges" is selected on Field A --> Set Field B to null and throw a warning if there was data that was cleared out of Field B. + +Is there a way we can standardize this to avoid tons of nested conditionals in our sheet? + + */ + + + +// note sheet must have same name as key in workbook it is shared as +const ConditionallyNullSheet = new Sheet( + 'ConditionallyNullSheet', + {a: TextField(), + b: TextField()}, + { + recordCompute: (record) => { + const [a,b] = [record.get('a'), record.get('b')] + if(a === "b_must_be_null") { + + record.set("b", null) + record.addWarning('b', `cleared 'b', was ${b}`) + } + + return record + }, + } + +) + +const TestWorkbook = new Workbook({ + name: `Test Workbook`, + namespace: 'test', + // saving SubSheet to workbook under key SubSheet + sheets: { ConditionallyNullSheet }, +}) + +describe('Workbook tests ->', () => { + // here we use Sheet tester + const testSheet = new SheetTester(TestWorkbook, 'ConditionallyNullSheet') + test('ConditionallyNullSheet test', async () => { + // for this inputRow + const inputRow = { a:'b_must_be_null', b:8 } + // we expect this output row + const expectedOutputRow = { a:'b_must_be_null', b:null } + const res = await testSheet.testRecord(inputRow) + const res2 = await testSheet.testMessage(inputRow) + + //use the match functions like + //console.log(res2) + expect(matchSingleMessage(res2, 'b', "cleared 'b', was 8", 'warn')).toBeTruthy() + + + expect(res).toMatchObject(expectedOutputRow) + }) + + test('ConditionallyNullSheet test2', async () => { + // for this inputRow + const inputRow = { a:'anything_else', b:8 } + // we expect this output row + const expectedOutputRow = { a:'anything_else', b:8 } + const res = await testSheet.testRecord(inputRow) + expect(res).toMatchObject(expectedOutputRow) + }) +}) From 0d72f396bbd5dc06f3e8d1044c6010099dab0353 Mon Sep 17 00:00:00 2001 From: Paddy Mullen Date: Tue, 15 Nov 2022 15:14:51 -0500 Subject: [PATCH 8/9] feat: SetValWhen RCChain --- examples/constraintsExample.spec.ts | 52 ++++++++++++++++++++++------- 1 file changed, 40 insertions(+), 12 deletions(-) diff --git a/examples/constraintsExample.spec.ts b/examples/constraintsExample.spec.ts index a861471..5885d81 100644 --- a/examples/constraintsExample.spec.ts +++ b/examples/constraintsExample.spec.ts @@ -1,8 +1,8 @@ +import { FlatfileRecord } from '@flatfile/hooks' import { Sheet, TextField, Workbook, SubstitutionCast } from '@flatfile/configure' import { SheetTester, matchSingleMessage } from '../src/utils/testing/SheetTester' /* -K. 1. Conditionally Null - Set field to null + throw warning if another field has a certain value Field A: select either -> "Apples" or "Oranges" or "Bananas" @@ -19,22 +19,43 @@ Is there a way we can standardize this to avoid tons of nested conditionals in o +const SetValWhen = ( + haystackField: string, needleValues: string | string[], targetField: string, val: any) => { + return (record: FlatfileRecord) => { + const [a, b] = [record.get(haystackField), record.get(targetField)] + let searchVals: string[]; + if (Array.isArray(needleValues)) { + searchVals = needleValues + } else { + searchVals = [needleValues] + } + //@ts-ignore + if (searchVals.includes(a)) { + record.set(targetField, val) + record.addWarning(targetField, `cleared '${targetField}', was ${b}`) + } + return record + } +} + +const RCChain = (...funcs:any) => { + return (record: FlatfileRecord) => { + for (const func of funcs) { + func(record) + } + } +} + + // note sheet must have same name as key in workbook it is shared as const ConditionallyNullSheet = new Sheet( 'ConditionallyNullSheet', {a: TextField(), b: TextField()}, { - recordCompute: (record) => { - const [a,b] = [record.get('a'), record.get('b')] - if(a === "b_must_be_null") { - - record.set("b", null) - record.addWarning('b', `cleared 'b', was ${b}`) - } - - return record - }, + recordCompute: RCChain( + SetValWhen('a', 'b_must_be_null', 'b', null), + SetValWhen('a', 'b_to_10', 'b', 10)) } ) @@ -58,7 +79,6 @@ describe('Workbook tests ->', () => { const res2 = await testSheet.testMessage(inputRow) //use the match functions like - //console.log(res2) expect(matchSingleMessage(res2, 'b', "cleared 'b', was 8", 'warn')).toBeTruthy() @@ -73,4 +93,12 @@ describe('Workbook tests ->', () => { const res = await testSheet.testRecord(inputRow) expect(res).toMatchObject(expectedOutputRow) }) + test('test set to 10 ', async () => { + // for this inputRow + const inputRow = { a:'b_to_10', b:10 } + // we expect this output row + const expectedOutputRow = { a:'b_to_10', b:10 } + const res = await testSheet.testRecord(inputRow) + expect(res).toMatchObject(expectedOutputRow) + }) }) From 2f767af07a732b8b9e93fcbbe54438a2adc69aa2 Mon Sep 17 00:00:00 2001 From: Paddy Mullen Date: Tue, 15 Nov 2022 15:28:55 -0500 Subject: [PATCH 9/9] feat: added RequiredWhen --- examples/constraintsExample.spec.ts | 77 +++++++++++++++++++++++++++-- 1 file changed, 74 insertions(+), 3 deletions(-) diff --git a/examples/constraintsExample.spec.ts b/examples/constraintsExample.spec.ts index 5885d81..39e9737 100644 --- a/examples/constraintsExample.spec.ts +++ b/examples/constraintsExample.spec.ts @@ -1,5 +1,5 @@ import { FlatfileRecord } from '@flatfile/hooks' -import { Sheet, TextField, Workbook, SubstitutionCast } from '@flatfile/configure' +import { Sheet, TextField, Workbook } from '@flatfile/configure' import { SheetTester, matchSingleMessage } from '../src/utils/testing/SheetTester' /* @@ -38,6 +38,26 @@ const SetValWhen = ( } } +const RequiredWhen = ( + switchField: string, switchVals: string | string[], targetField: string) => { + return (record: FlatfileRecord) => { + const [a, b] = [record.get(switchField), record.get(targetField)] + let searchVals: string[]; + if (Array.isArray(switchVals)) { + searchVals = switchVals + } else { + searchVals = [switchVals] + } + //@ts-ignore + if (searchVals.includes(a)) { + if(b === null) { + record.addWarning(targetField, ` '${targetField}' required`) + } + } + return record + } +} + const RCChain = (...funcs:any) => { return (record: FlatfileRecord) => { for (const func of funcs) { @@ -57,19 +77,28 @@ const ConditionallyNullSheet = new Sheet( SetValWhen('a', 'b_must_be_null', 'b', null), SetValWhen('a', 'b_to_10', 'b', 10)) } - +) + +const RequiredWhenSheet = new Sheet( + 'RequiredWhenSheet', + {a: TextField(), + b: TextField()}, + { + recordCompute: RequiredWhen('a', 'b_is_required', 'b') + } ) const TestWorkbook = new Workbook({ name: `Test Workbook`, namespace: 'test', // saving SubSheet to workbook under key SubSheet - sheets: { ConditionallyNullSheet }, + sheets: { ConditionallyNullSheet, RequiredWhenSheet}, }) describe('Workbook tests ->', () => { // here we use Sheet tester const testSheet = new SheetTester(TestWorkbook, 'ConditionallyNullSheet') + test('ConditionallyNullSheet test', async () => { // for this inputRow const inputRow = { a:'b_must_be_null', b:8 } @@ -81,6 +110,19 @@ describe('Workbook tests ->', () => { //use the match functions like expect(matchSingleMessage(res2, 'b', "cleared 'b', was 8", 'warn')).toBeTruthy() + expect(res).toMatchObject(expectedOutputRow) + }) + + test('ConditionallyNullSheet test', async () => { + // for this inputRow + const inputRow = { a:'b_must_be_null', b:8 } + // we expect this output row + const expectedOutputRow = { a:'b_must_be_null', b:null } + const res = await testSheet.testRecord(inputRow) + const res2 = await testSheet.testMessage(inputRow) + + //use the match functions like + expect(matchSingleMessage(res2, 'b', "cleared 'b', was 8", 'warn')).toBeTruthy() expect(res).toMatchObject(expectedOutputRow) }) @@ -101,4 +143,33 @@ describe('Workbook tests ->', () => { const res = await testSheet.testRecord(inputRow) expect(res).toMatchObject(expectedOutputRow) }) + + const rqTestSheet = new SheetTester(TestWorkbook, 'RequiredWhenSheet') + test('RequiredWhen test1', async () => { + // for this inputRow + const inputRow = { a:'wont trigger', b:null } + // we expect this output row + const expectedOutputRow = { a:'wont trigger', b:null } + const res = await rqTestSheet.testRecord(inputRow) + const res2 = await rqTestSheet.testMessage(inputRow) + + //use the match functions like + expect(matchSingleMessage(res2, 'b')).toBeFalsy() + + expect(res).toMatchObject(expectedOutputRow) + }) + + test('RequiredWhen test2', async () => { + // for this inputRow + const inputRow = { a:'b_is_required', b:null } + // we expect this output row + const expectedOutputRow = { a:'b_is_required', b:null } + const res = await rqTestSheet.testRecord(inputRow) + const res2 = await rqTestSheet.testMessage(inputRow) + + //use the match functions like + expect(matchSingleMessage(res2, 'b', "'b' required")).toBeTruthy() + + expect(res).toMatchObject(expectedOutputRow) + }) })