From 61aa5a755f7baa64bfff1b3b106ef547c50add0b Mon Sep 17 00:00:00 2001 From: Andrew Date: Tue, 29 Dec 2020 13:03:27 -0800 Subject: [PATCH 01/13] WIP: Add sorting options [gh-1138] --- api/controller/contributions.ts | 2 ++ api/models/entity/Contribution.ts | 3 ++- .../ContributionsTable/ContributionsTable.js | 5 ++++- app/src/state/ducks/contributions.js | 12 +++++++++++- 4 files changed, 19 insertions(+), 3 deletions(-) diff --git a/api/controller/contributions.ts b/api/controller/contributions.ts index 1d4aeb75f..a61da0fc8 100644 --- a/api/controller/contributions.ts +++ b/api/controller/contributions.ts @@ -232,6 +232,7 @@ export class GetContributionsDto implements IGetContributionAttrs { export async function getContributions(request: IRequest, response: Response, next: Function) { try { + // TODO: update for additional fields checkCurrentUser(request); const getContributionsDto = Object.assign(new GetContributionsDto(), { ...request.body, @@ -249,6 +250,7 @@ export async function getContributions(request: IRequest, response: Response, ne } return response.status(200).send(JSON.stringify(contributions)); } catch (err) { + console.log(request.body, err); if (process.env.NODE_ENV === 'production' && err.message !== 'No token set') { bugsnagClient.notify(err); } diff --git a/api/models/entity/Contribution.ts b/api/models/entity/Contribution.ts index 63ee5a6c9..508883cdc 100644 --- a/api/models/entity/Contribution.ts +++ b/api/models/entity/Contribution.ts @@ -661,7 +661,8 @@ export async function getContributionsByGovernmentIdAsync( } }; if (sort) { - if (!['date', 'status', 'campaignId', 'matchAmount', 'amount'].includes(sort.field)) { + // TODO: update here + if (!['date', 'status', 'campaignId', 'matchAmount', 'amount', 'oaeType'].includes(sort.field)) { throw new Error('Sort.field must be one of date, status, matchAmount, amount or campaignid'); } diff --git a/app/src/Pages/Portal/Contributions/ContributionsTable/ContributionsTable.js b/app/src/Pages/Portal/Contributions/ContributionsTable/ContributionsTable.js index 7de7912da..494a344e3 100644 --- a/app/src/Pages/Portal/Contributions/ContributionsTable/ContributionsTable.js +++ b/app/src/Pages/Portal/Contributions/ContributionsTable/ContributionsTable.js @@ -259,15 +259,18 @@ class ContributionsTable extends React.Component { data={contributionList} onOrderChange={(item, direction) => { const column = columns(isGovAdmin)[item]; + console.log(item, direction, column); let sortOptions = {}; if (column) { + const sortDirection = direction.toUpperCase(); sortOptions = { sort: { field: column.field, - direction: direction.toUpperCase(), + direction: sortDirection, // TODO: is this actually toggling? }, }; } + // TODO: sort here: updateFilter(sortOptions); fetchList(); }} diff --git a/app/src/state/ducks/contributions.js b/app/src/state/ducks/contributions.js index 872bed9ba..5a61726fb 100644 --- a/app/src/state/ducks/contributions.js +++ b/app/src/state/ducks/contributions.js @@ -196,9 +196,19 @@ export function updateFilter(newFilterOptions) { (filterOptions.page * filterOptions.perPage) / newFilterOptions.perPage ); } + const existingSortField = filterOptions.sort; + const newSortField = newFilterOptions.sort; + if ( + existingSortField.field === newSortField.field && + existingSortField.direction === newSortField.direction + ) { + const isAsc = existingSortField.direction === 'ASC'; + newFilterOptions.sort.direction = isAsc ? 'DESC' : 'ASC'; + } Object.entries(filterOptions).forEach(([key, value]) => { - if (Object.prototype.hasOwnProperty.call(newFilterOptions, key)) + if (Object.prototype.hasOwnProperty.call(newFilterOptions, key)) { filterOptions[key] = newFilterOptions[key]; + } }); dispatch(actionCreators.filter.update(filterOptions)); }; From c4e5fa9936b2848827545fa7186e91771c2c6eaa Mon Sep 17 00:00:00 2001 From: Andrew Date: Tue, 29 Dec 2020 19:00:57 -0800 Subject: [PATCH 02/13] wip: debugging match error --- api/controller/contributions.ts | 2 ++ api/models/entity/Contribution.ts | 5 ++++- api/services/contributionService.ts | 4 +++- .../Contributions/ContributionsTable/ContributionsTable.js | 3 --- app/src/state/ducks/contributions.js | 2 ++ app/src/state/ducks/matches.js | 4 ++-- 6 files changed, 13 insertions(+), 7 deletions(-) diff --git a/api/controller/contributions.ts b/api/controller/contributions.ts index a61da0fc8..8d87a06a2 100644 --- a/api/controller/contributions.ts +++ b/api/controller/contributions.ts @@ -524,7 +524,9 @@ export async function getMatchesByContributionId(request: IRequest, response: Re currentUserId: request.currentUser.id }); await checkDto(getContributionMatchesDto); + console.log('what is wrong?', getContributionMatchesDto); const matches = await getMatchResultAsync(getContributionMatchesDto); + console.log(matches); return response.status(200).send(matches); } catch (err) { return response.status(422).json({ message: err.message }); diff --git a/api/models/entity/Contribution.ts b/api/models/entity/Contribution.ts index 508883cdc..32b72fc09 100644 --- a/api/models/entity/Contribution.ts +++ b/api/models/entity/Contribution.ts @@ -662,9 +662,12 @@ export async function getContributionsByGovernmentIdAsync( }; if (sort) { // TODO: update here - if (!['date', 'status', 'campaignId', 'matchAmount', 'amount', 'oaeType'].includes(sort.field)) { + if (!['date', 'status', 'campaignId', 'matchAmount', 'amount', 'oaeType', 'name'].includes(sort.field)) { throw new Error('Sort.field must be one of date, status, matchAmount, amount or campaignid'); } + if (sort.field === 'campaignId') { + sort.field = 'id'; + } if (!['ASC', 'DESC'].includes(sort.direction)) { throw new Error('Sort.direction must be one of ASC or DESC'); diff --git a/api/services/contributionService.ts b/api/services/contributionService.ts index bb17b8062..1d9163fc7 100644 --- a/api/services/contributionService.ts +++ b/api/services/contributionService.ts @@ -169,7 +169,7 @@ export interface IGetContributionOptions { from?: string; to?: string; sort?: { - field: 'campaignId' | 'status' | 'date'; + field: 'campaignId' | 'status' | 'date' | 'id'; direction: 'ASC' | 'DESC'; }; format?: 'json' | 'csv' | 'geoJson' | 'xml'; @@ -579,6 +579,7 @@ export async function getMatchResultAsync(attrs: GetMatchResultAttrs): Promise { { field: 'name', title: 'Name', - sorting: false, render: rowData => { if ( rowData.contributorType === 'individual' || @@ -84,7 +83,6 @@ const columns = isGovAdmin => { { field: 'amount', title: 'Contribution Amount', - sorting: false, type: 'currency', }, { @@ -104,7 +102,6 @@ const columns = isGovAdmin => { cols.splice(1, 0, { field: 'campaignId', title: 'Campaign', - sorting: false, render: rowData => { return rowData && rowData.campaign ? rowData.campaign.name diff --git a/app/src/state/ducks/contributions.js b/app/src/state/ducks/contributions.js index 5a61726fb..7d03583e2 100644 --- a/app/src/state/ducks/contributions.js +++ b/app/src/state/ducks/contributions.js @@ -199,6 +199,8 @@ export function updateFilter(newFilterOptions) { const existingSortField = filterOptions.sort; const newSortField = newFilterOptions.sort; if ( + existingSortField && + newSortField && existingSortField.field === newSortField.field && existingSortField.direction === newSortField.direction ) { diff --git a/app/src/state/ducks/matches.js b/app/src/state/ducks/matches.js index 30a135e8d..43439954d 100644 --- a/app/src/state/ducks/matches.js +++ b/app/src/state/ducks/matches.js @@ -133,7 +133,7 @@ export const getCurrentContributionMatch = state => { export const getCurrentMatchResults = state => { const currentMatches = getCurrentContributionMatch(state); const matches = []; - const results = currentMatches.results; + const results = (currentMatches || {}).results; const currentContribution = getCurrentContribution(state); let match = {}; let selectedMatchId = ''; @@ -172,7 +172,7 @@ export const getCurrentMatchResults = state => { } } } - if (currentMatches.matchStrength !== 'exact') { + if (results && (currentMatches || {}).matchStrength !== 'exact') { if (currentContribution.matchStrength === 'none') { matches.unshift({ id: results.none, From c7e981a48c20ec1c4095d4d5f02480e645ffa4e1 Mon Sep 17 00:00:00 2001 From: Andrew Date: Wed, 30 Dec 2020 13:14:40 -0800 Subject: [PATCH 03/13] fix error in match modal --- .../Contributions/ContributionsTable/ContributionsTable.js | 3 +-- app/src/components/ContributorMatchPicker/index.js | 5 ++++- app/src/state/ducks/matches.js | 1 + 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/app/src/Pages/Portal/Contributions/ContributionsTable/ContributionsTable.js b/app/src/Pages/Portal/Contributions/ContributionsTable/ContributionsTable.js index c931bab22..350096279 100644 --- a/app/src/Pages/Portal/Contributions/ContributionsTable/ContributionsTable.js +++ b/app/src/Pages/Portal/Contributions/ContributionsTable/ContributionsTable.js @@ -256,14 +256,13 @@ class ContributionsTable extends React.Component { data={contributionList} onOrderChange={(item, direction) => { const column = columns(isGovAdmin)[item]; - console.log(item, direction, column); let sortOptions = {}; if (column) { const sortDirection = direction.toUpperCase(); sortOptions = { sort: { field: column.field, - direction: sortDirection, // TODO: is this actually toggling? + direction: sortDirection, }, }; } diff --git a/app/src/components/ContributorMatchPicker/index.js b/app/src/components/ContributorMatchPicker/index.js index 993f715e0..7cc175f1d 100644 --- a/app/src/components/ContributorMatchPicker/index.js +++ b/app/src/components/ContributorMatchPicker/index.js @@ -148,7 +148,10 @@ class contributorMatchPicker extends React.Component { } render() { - const inPortland = this.props.matchObj.inPortland; + if (!this.props.matchObj) { + return
; + } + const inPortland = (this.props.matchObj || {}).inPortland; const { totalPages, currentPage, pages } = this.state; const page = !isEmpty(pages) ? pages[currentPage] : [{}]; const { diff --git a/app/src/state/ducks/matches.js b/app/src/state/ducks/matches.js index 43439954d..759f4de97 100644 --- a/app/src/state/ducks/matches.js +++ b/app/src/state/ducks/matches.js @@ -132,6 +132,7 @@ export const getCurrentContributionMatch = state => { export const getCurrentMatchResults = state => { const currentMatches = getCurrentContributionMatch(state); + console.log(currentMatches); const matches = []; const results = (currentMatches || {}).results; const currentContribution = getCurrentContribution(state); From da1baaf6746f0f88702ba9134dd8e34c948e5878 Mon Sep 17 00:00:00 2001 From: Andrew Date: Wed, 30 Dec 2020 15:25:58 -0800 Subject: [PATCH 04/13] clean up comments --- api/controller/contributions.ts | 2 -- api/models/entity/Contribution.ts | 1 - api/services/contributionService.ts | 3 ++- .../Contributions/ContributionsTable/ContributionsTable.js | 4 +--- app/src/state/ducks/contributions.js | 3 +-- 5 files changed, 4 insertions(+), 9 deletions(-) diff --git a/api/controller/contributions.ts b/api/controller/contributions.ts index 8d87a06a2..c2683d26e 100644 --- a/api/controller/contributions.ts +++ b/api/controller/contributions.ts @@ -232,7 +232,6 @@ export class GetContributionsDto implements IGetContributionAttrs { export async function getContributions(request: IRequest, response: Response, next: Function) { try { - // TODO: update for additional fields checkCurrentUser(request); const getContributionsDto = Object.assign(new GetContributionsDto(), { ...request.body, @@ -250,7 +249,6 @@ export async function getContributions(request: IRequest, response: Response, ne } return response.status(200).send(JSON.stringify(contributions)); } catch (err) { - console.log(request.body, err); if (process.env.NODE_ENV === 'production' && err.message !== 'No token set') { bugsnagClient.notify(err); } diff --git a/api/models/entity/Contribution.ts b/api/models/entity/Contribution.ts index 32b72fc09..b1d41a5d1 100644 --- a/api/models/entity/Contribution.ts +++ b/api/models/entity/Contribution.ts @@ -661,7 +661,6 @@ export async function getContributionsByGovernmentIdAsync( } }; if (sort) { - // TODO: update here if (!['date', 'status', 'campaignId', 'matchAmount', 'amount', 'oaeType', 'name'].includes(sort.field)) { throw new Error('Sort.field must be one of date, status, matchAmount, amount or campaignid'); } diff --git a/api/services/contributionService.ts b/api/services/contributionService.ts index 1d9163fc7..f72137ff1 100644 --- a/api/services/contributionService.ts +++ b/api/services/contributionService.ts @@ -592,12 +592,13 @@ export async function getMatchResultAsync(attrs: GetMatchResultAttrs): Promise { - if (Object.prototype.hasOwnProperty.call(newFilterOptions, key)) { + if (Object.prototype.hasOwnProperty.call(newFilterOptions, key)) filterOptions[key] = newFilterOptions[key]; - } }); dispatch(actionCreators.filter.update(filterOptions)); }; From e70740cb8684a442ffff1a10eb9dd800fb84a4ce Mon Sep 17 00:00:00 2001 From: Andrew Date: Wed, 30 Dec 2020 16:01:39 -0800 Subject: [PATCH 05/13] fix modal option if null --- api/services/contributionService.ts | 29 ++++++++++--------- .../ContributorMatchPicker/index.js | 21 +++++++++++++- 2 files changed, 36 insertions(+), 14 deletions(-) diff --git a/api/services/contributionService.ts b/api/services/contributionService.ts index f72137ff1..a57f4588f 100644 --- a/api/services/contributionService.ts +++ b/api/services/contributionService.ts @@ -581,19 +581,22 @@ export async function getMatchResultAsync(attrs: GetMatchResultAttrs): Promise { } // Switch color and symbol based on matchStrength const matchIcon = getMatchIcon(matchStrength, inPortland); + + if (!currentMatchId) { + return ( +

+ Contributor {matchIcon}{' '} + {currentMatchId && ( + + {matchSelectedText} + + )} +

+ ); + } return ( // eslint-disable-next-line jsx-a11y/no-noninteractive-element-interactions

showModal({ component: 'MatchPickerForm', From a56330f533493c691451772f540a5a0373f4bac0 Mon Sep 17 00:00:00 2001 From: Andrew Date: Wed, 30 Dec 2020 16:17:29 -0800 Subject: [PATCH 06/13] rm console.logs --- api/controller/contributions.ts | 2 -- api/services/contributionService.ts | 2 -- app/src/state/ducks/matches.js | 1 - 3 files changed, 5 deletions(-) diff --git a/api/controller/contributions.ts b/api/controller/contributions.ts index c2683d26e..1d4aeb75f 100644 --- a/api/controller/contributions.ts +++ b/api/controller/contributions.ts @@ -522,9 +522,7 @@ export async function getMatchesByContributionId(request: IRequest, response: Re currentUserId: request.currentUser.id }); await checkDto(getContributionMatchesDto); - console.log('what is wrong?', getContributionMatchesDto); const matches = await getMatchResultAsync(getContributionMatchesDto); - console.log(matches); return response.status(200).send(matches); } catch (err) { return response.status(422).json({ message: err.message }); diff --git a/api/services/contributionService.ts b/api/services/contributionService.ts index a57f4588f..0ffa4137a 100644 --- a/api/services/contributionService.ts +++ b/api/services/contributionService.ts @@ -579,7 +579,6 @@ export async function getMatchResultAsync(attrs: GetMatchResultAttrs): Promise { export const getCurrentMatchResults = state => { const currentMatches = getCurrentContributionMatch(state); - console.log(currentMatches); const matches = []; const results = (currentMatches || {}).results; const currentContribution = getCurrentContribution(state); From dc22087f3f08bc5c2ad09f755272502f1c28dabb Mon Sep 17 00:00:00 2001 From: Andrew Date: Wed, 30 Dec 2020 20:50:58 -0800 Subject: [PATCH 07/13] add advanced expenditure filtering --- api/models/entity/Expenditure.ts | 3 ++- .../Portal/Expenses/ExpensesTable/ExpensesTable.js | 3 --- app/src/state/ducks/expenditures.js | 12 ++++++++++++ 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/api/models/entity/Expenditure.ts b/api/models/entity/Expenditure.ts index 10e4bb6b1..d843d0090 100644 --- a/api/models/entity/Expenditure.ts +++ b/api/models/entity/Expenditure.ts @@ -389,7 +389,8 @@ export async function getExpendituresByGovernmentIdAsync( }; if (sort) { - if (!['date', 'status', 'campaignId'].includes(sort.field)) { + if (!['date', 'status', 'campaignId', 'amount'].includes(sort.field)) { + console.log(sort.field); throw new Error('Sort.field must be one of date, status or campaignId'); } diff --git a/app/src/Pages/Portal/Expenses/ExpensesTable/ExpensesTable.js b/app/src/Pages/Portal/Expenses/ExpensesTable/ExpensesTable.js index 00fa757b6..09ad6e86b 100644 --- a/app/src/Pages/Portal/Expenses/ExpensesTable/ExpensesTable.js +++ b/app/src/Pages/Portal/Expenses/ExpensesTable/ExpensesTable.js @@ -79,13 +79,11 @@ const columns = isGovAdmin => [ { field: 'name', title: 'Name', - sorting: false, }, { field: 'amount', title: 'Amount', type: 'currency', - sorting: false, }, { field: 'paymentMethod', @@ -95,7 +93,6 @@ const columns = isGovAdmin => [ ? rowData.paymentMethod.replace(/_/g, ' ') : ''; }, - sorting: false, }, { field: 'status', diff --git a/app/src/state/ducks/expenditures.js b/app/src/state/ducks/expenditures.js index 4b0ad37db..e94468d4a 100644 --- a/app/src/state/ducks/expenditures.js +++ b/app/src/state/ducks/expenditures.js @@ -164,10 +164,22 @@ export function updateFilter(newFilterOptions) { (filterOptions.page * filterOptions.perPage) / newFilterOptions.perPage ); } + const existingSortField = filterOptions.sort; + const newSortField = newFilterOptions.sort; + if ( + existingSortField && + newSortField && + existingSortField.field === newSortField.field && + existingSortField.direction === newSortField.direction + ) { + const isAsc = existingSortField.direction === 'ASC'; + newFilterOptions.sort.direction = isAsc ? 'DESC' : 'ASC'; + } Object.entries(filterOptions).forEach(([key, value]) => { if (Object.prototype.hasOwnProperty.call(newFilterOptions, key)) filterOptions[key] = newFilterOptions[key]; }); + console.log(filterOptions); dispatch(actionCreators.filter.update(filterOptions)); }; } From 079ca8b51ba5aee4311cb2fe926bf7fa1fa1aa98 Mon Sep 17 00:00:00 2001 From: Andrew Date: Wed, 30 Dec 2020 20:53:00 -0800 Subject: [PATCH 08/13] rm console.log --- app/src/state/ducks/expenditures.js | 1 - 1 file changed, 1 deletion(-) diff --git a/app/src/state/ducks/expenditures.js b/app/src/state/ducks/expenditures.js index e94468d4a..eeccca3ee 100644 --- a/app/src/state/ducks/expenditures.js +++ b/app/src/state/ducks/expenditures.js @@ -179,7 +179,6 @@ export function updateFilter(newFilterOptions) { if (Object.prototype.hasOwnProperty.call(newFilterOptions, key)) filterOptions[key] = newFilterOptions[key]; }); - console.log(filterOptions); dispatch(actionCreators.filter.update(filterOptions)); }; } From 645635e573f8c7eacf51ef42c3f2394cc536a271 Mon Sep 17 00:00:00 2001 From: Andrew Date: Sat, 2 Jan 2021 14:43:00 -0800 Subject: [PATCH 09/13] clean up, trigger rebuild --- api/controller/expenditures.ts | 2 +- api/models/entity/Expenditure.ts | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/api/controller/expenditures.ts b/api/controller/expenditures.ts index abea3c2c6..9fd1ee157 100644 --- a/api/controller/expenditures.ts +++ b/api/controller/expenditures.ts @@ -166,7 +166,7 @@ export async function getExpenditures(request: IRequest, response: Response, nex currentUserId: request.currentUser.id }); await checkDto(getExpendituresDto); - + const expenditures = await getExpendituresAsync(getExpendituresDto); if (expenditures.csv) { response.type('text/csv'); diff --git a/api/models/entity/Expenditure.ts b/api/models/entity/Expenditure.ts index d843d0090..325a562c8 100644 --- a/api/models/entity/Expenditure.ts +++ b/api/models/entity/Expenditure.ts @@ -390,7 +390,6 @@ export async function getExpendituresByGovernmentIdAsync( if (sort) { if (!['date', 'status', 'campaignId', 'amount'].includes(sort.field)) { - console.log(sort.field); throw new Error('Sort.field must be one of date, status or campaignId'); } From b6a05bb205ad964077d9f73f38f9d84bca274762 Mon Sep 17 00:00:00 2001 From: Andrew Date: Sun, 3 Jan 2021 11:30:55 -0800 Subject: [PATCH 10/13] rm trailing white space --- api/controller/expenditures.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/api/controller/expenditures.ts b/api/controller/expenditures.ts index 9fd1ee157..2c3f5dc84 100644 --- a/api/controller/expenditures.ts +++ b/api/controller/expenditures.ts @@ -166,7 +166,6 @@ export async function getExpenditures(request: IRequest, response: Response, nex currentUserId: request.currentUser.id }); await checkDto(getExpendituresDto); - const expenditures = await getExpendituresAsync(getExpendituresDto); if (expenditures.csv) { response.type('text/csv'); From 5c1e26366ec3b6a94e97bff48fc8cda084315965 Mon Sep 17 00:00:00 2001 From: Andrew Date: Sun, 3 Jan 2021 22:21:36 -0800 Subject: [PATCH 11/13] add docker login script --- scripts/test.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/scripts/test.sh b/scripts/test.sh index 674a3beab..a8fb9f2ad 100755 --- a/scripts/test.sh +++ b/scripts/test.sh @@ -4,5 +4,7 @@ set -e export PATH=$PATH:$HOME/.local/bin +echo "$DOCKER_PASSWORD" | docker login -u "$DOCKER_USERNAME" --password-stdin + sh scripts/api-test.sh sh scripts/app-test.sh From 4cd7f6bb0fee19a8b4ed0e81fa993561f5845e34 Mon Sep 17 00:00:00 2001 From: Andrew Date: Sun, 3 Jan 2021 22:31:32 -0800 Subject: [PATCH 12/13] test change with docker login --- api/test/entities/Expenditure.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/test/entities/Expenditure.spec.ts b/api/test/entities/Expenditure.spec.ts index 5bfbb76f0..f8edd4687 100644 --- a/api/test/entities/Expenditure.spec.ts +++ b/api/test/entities/Expenditure.spec.ts @@ -30,7 +30,7 @@ describe('Expenditure', () => { await truncateAll(); }); - it('isDefined Columns', async () => { + xit('isDefined Columns', async () => { const newRecord = new Expenditure(); await newRecord.validateAsync(); expect(newRecord.errors.length).to.equal(10); From 97d1f9ce9d38f3e1df7e0ee5047dac4a758762d6 Mon Sep 17 00:00:00 2001 From: Andrew Date: Sun, 3 Jan 2021 22:48:11 -0800 Subject: [PATCH 13/13] rm x --- api/test/entities/Expenditure.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/test/entities/Expenditure.spec.ts b/api/test/entities/Expenditure.spec.ts index f8edd4687..5bfbb76f0 100644 --- a/api/test/entities/Expenditure.spec.ts +++ b/api/test/entities/Expenditure.spec.ts @@ -30,7 +30,7 @@ describe('Expenditure', () => { await truncateAll(); }); - xit('isDefined Columns', async () => { + it('isDefined Columns', async () => { const newRecord = new Expenditure(); await newRecord.validateAsync(); expect(newRecord.errors.length).to.equal(10);