From 8612da67ab65b79d202e5721be4608d5f268bf0b Mon Sep 17 00:00:00 2001 From: Raju Udava <86527202+dstala@users.noreply.github.com> Date: Fri, 3 Feb 2023 16:58:50 +0530 Subject: [PATCH 1/7] test: base data for filters to run on Signed-off-by: Raju Udava <86527202+dstala@users.noreply.github.com> --- packages/nocodb/tests/unit/factory/row.ts | 156 ++++++++++++++++-- .../tests/unit/rest/tests/filter.test.ts | 97 +++++++++++ 2 files changed, 242 insertions(+), 11 deletions(-) create mode 100644 packages/nocodb/tests/unit/rest/tests/filter.test.ts diff --git a/packages/nocodb/tests/unit/factory/row.ts b/packages/nocodb/tests/unit/factory/row.ts index 78540c943d..9b33a4d278 100644 --- a/packages/nocodb/tests/unit/factory/row.ts +++ b/packages/nocodb/tests/unit/factory/row.ts @@ -24,13 +24,124 @@ const rowValue = (column: ColumnType, index: number) => { } }; -const getRow = async (context, {project, table, id}) => { +const rowMixedValue = (column: ColumnType, index: number) => { + console.log(column.uidt, index); + + // Array of country names + const countries = [ + 'Afghanistan', + 'Albania', + '', + 'Andorra', + 'Angola', + 'Antigua and Barbuda', + 'Argentina', + null, + 'Armenia', + 'Australia', + 'Austria', + '', + null, + ]; + + // Array of sample random paragraphs (comma separated list of cities and countries). Not more than 200 characters + const longText = [ + 'Aberdeen, United Kingdom', + 'Abidjan, Côte d’Ivoire', + 'Abuja, Nigeria', + '', + 'Addis Ababa, Ethiopia', + 'Adelaide, Australia', + 'Ahmedabad, India', + 'Albuquerque, United States', + null, + 'Alexandria, Egypt', + 'Algiers, Algeria', + 'Allahabad, India', + '', + null, + ]; + + // Array of random integers, not more than 10000 + const numbers = [33, 456, 54, 267, 34, 8754, 3234, 44, 33, null]; + + // Array of random sample email strings (not more than 100 characters) + const emails = [ + 'jbutt@gmail.com', + 'josephine_darakjy@darakjy.org', + 'art@venere.org', + '', + null, + 'donette.foller@cox.net', + 'simona@morasca.com', + 'mitsue_tollner@yahoo.com', + 'leota@hotmail.com', + 'sage_wieser@cox.net', + '', + null, + ]; + + // Array of random sample phone numbers + const phoneNumbers = [ + '1-541-754-3010', + '504-621-8927', + '810-292-9388', + '856-636-8749', + '907-385-4412', + '513-570-1893', + '419-503-2484', + '773-573-6914', + '', + null, + ]; + + // Array of random sample URLs + const urls = [ + 'https://www.google.com', + 'https://www.facebook.com', + 'https://www.youtube.com', + 'https://www.amazon.com', + 'https://www.wikipedia.org', + 'https://www.twitter.com', + 'https://www.instagram.com', + 'https://www.linkedin.com', + 'https://www.reddit.com', + 'https://www.tiktok.com', + 'https://www.pinterest.com', + 'https://www.netflix.com', + 'https://www.microsoft.com', + 'https://www.apple.com', + '', + null, + ]; + + switch (column.uidt) { + case UITypes.Number: + return numbers[index % numbers.length]; + case UITypes.SingleLineText: + return countries[index % countries.length]; + case UITypes.Email: + return emails[index % emails.length]; + case UITypes.PhoneNumber: + return phoneNumbers[index % phoneNumbers.length]; + case UITypes.LongText: + return longText[index % longText.length]; + case UITypes.Date: + return '2020-01-01'; + case UITypes.URL: + return urls[index % urls.length]; + default: + return `test-${index}`; + } +}; + +const getRow = async (context, { project, table, id }) => { const response = await request(context.app) .get(`/api/v1/db/data/noco/${project.id}/${table.id}/${id}`) .set('xc-auth', context.token); - if(response.status !== 200) { - return undefined + if (response.status !== 200) { + return undefined; } return response.body; @@ -119,18 +230,19 @@ const createBulkRows = async ( { project, table, - values + values, }: { project: Project; table: Model; values: any[]; - }) => { - await request(context.app) - .post(`/api/v1/db/data/bulk/noco/${project.id}/${table.id}`) - .set('xc-auth', context.token) - .send(values) - .expect(200); } +) => { + await request(context.app) + .post(`/api/v1/db/data/bulk/noco/${project.id}/${table.id}`) + .set('xc-auth', context.token) + .send(values) + .expect(200); +}; // Links 2 table rows together. Will create rows if ids are not provided const createChildRow = async ( @@ -174,6 +286,26 @@ const createChildRow = async ( return row; }; +// Mixed row attributes +const generateMixedRowAttributes = ({ + columns, + index = 0, +}: { + columns: ColumnType[]; + index?: number; +}) => + columns.reduce((acc, column) => { + if ( + column.uidt === UITypes.LinkToAnotherRecord || + column.uidt === UITypes.ForeignKey || + column.uidt === UITypes.ID + ) { + return acc; + } + acc[column.title!] = rowMixedValue(column, index); + return acc; + }, {}); + export { createRow, getRow, @@ -181,5 +313,7 @@ export { getOneRow, listRow, generateDefaultRowAttributes, - createBulkRows + generateMixedRowAttributes, + createBulkRows, + rowMixedValue, }; diff --git a/packages/nocodb/tests/unit/rest/tests/filter.test.ts b/packages/nocodb/tests/unit/rest/tests/filter.test.ts new file mode 100644 index 0000000000..eaf56ae8bc --- /dev/null +++ b/packages/nocodb/tests/unit/rest/tests/filter.test.ts @@ -0,0 +1,97 @@ +import 'mocha'; +import init from '../../init'; +import { createProject } from '../../factory/project'; +import Project from '../../../../src/lib/models/Project'; +import { createTable } from '../../factory/table'; +import { isSqlite } from '../../init/db'; +import { UITypes } from 'nocodb-sdk'; +import { + createBulkRows, + generateDefaultRowAttributes, + generateMixedRowAttributes, + rowMixedValue, + listRow, +} from '../../factory/row'; +import Model from '../../../../src/lib/models/Model'; +import { expect } from 'chai'; + +// Test case list + +function filterTests() { + let context; + let project: Project; + let table: Model; + let unfilteredRecords: any[] = []; + + // prepare data for test cases + beforeEach(async function () { + context = await init(); + project = await createProject(context); + table = await createTable(context, project, { + table_name: 'textBased', + title: 'TextBased', + columns: [ + { + column_name: 'Id', + title: 'Id', + uidt: UITypes.ID, + }, + { + column_name: 'SingleLineText', + title: 'SingleLineText', + uidt: UITypes.SingleLineText, + }, + { + column_name: 'MultiLineText', + title: 'MultiLineText', + uidt: UITypes.LongText, + }, + { + column_name: 'Email', + title: 'Email', + uidt: UITypes.Email, + }, + { + column_name: 'Phone', + title: 'Phone', + uidt: UITypes.PhoneNumber, + }, + { + column_name: 'Url', + title: 'Url', + uidt: UITypes.URL, + }, + ], + }); + + const columns = await table.getColumns(); + + let rowAttributes = []; + for (let i = 0; i < 400; i++) { + let row = { + SingleLineText: rowMixedValue(columns[1], i), + MultiLineText: rowMixedValue(columns[2], i), + Email: rowMixedValue(columns[3], i), + Phone: rowMixedValue(columns[4], i), + Url: rowMixedValue(columns[5], i), + }; + rowAttributes.push(row); + } + + await createBulkRows(context, { + project, + table, + values: rowAttributes, + }); + unfilteredRecords = await listRow({ project, table }); + + // verify length of unfiltered records to be 400 + expect(unfilteredRecords.length).to.equal(400); + }); + + it('Type: Single Line Text', async () => {}); +} + +export default function () { + describe('Filters', filterTests); +} From ab6706d76e9643077a0356c1c9c3722fcbf02d6d Mon Sep 17 00:00:00 2001 From: Raju Udava <86527202+dstala@users.noreply.github.com> Date: Fri, 3 Feb 2023 17:59:28 +0530 Subject: [PATCH 2/7] test: filter UT for single line text Signed-off-by: Raju Udava <86527202+dstala@users.noreply.github.com> --- packages/nocodb/tests/unit/factory/row.ts | 2 - .../tests/unit/rest/tests/filter.test.ts | 99 ++++++++++++++++++- 2 files changed, 97 insertions(+), 4 deletions(-) diff --git a/packages/nocodb/tests/unit/factory/row.ts b/packages/nocodb/tests/unit/factory/row.ts index 9b33a4d278..484fc60b24 100644 --- a/packages/nocodb/tests/unit/factory/row.ts +++ b/packages/nocodb/tests/unit/factory/row.ts @@ -25,8 +25,6 @@ const rowValue = (column: ColumnType, index: number) => { }; const rowMixedValue = (column: ColumnType, index: number) => { - console.log(column.uidt, index); - // Array of country names const countries = [ 'Afghanistan', diff --git a/packages/nocodb/tests/unit/rest/tests/filter.test.ts b/packages/nocodb/tests/unit/rest/tests/filter.test.ts index eaf56ae8bc..dc89848231 100644 --- a/packages/nocodb/tests/unit/rest/tests/filter.test.ts +++ b/packages/nocodb/tests/unit/rest/tests/filter.test.ts @@ -14,6 +14,7 @@ import { } from '../../factory/row'; import Model from '../../../../src/lib/models/Model'; import { expect } from 'chai'; +import request from 'supertest'; // Test case list @@ -21,6 +22,7 @@ function filterTests() { let context; let project: Project; let table: Model; + let columns: any[]; let unfilteredRecords: any[] = []; // prepare data for test cases @@ -64,7 +66,7 @@ function filterTests() { ], }); - const columns = await table.getColumns(); + columns = await table.getColumns(); let rowAttributes = []; for (let i = 0; i < 400; i++) { @@ -89,7 +91,100 @@ function filterTests() { expect(unfilteredRecords.length).to.equal(400); }); - it('Type: Single Line Text', async () => {}); + async function retrieveRecordsAndValidate( + filter: { + comparison_op: string; + value: string; + fk_column_id: any; + status: string; + logical_op: string; + }, + expectedRecords: any[] + ) { + // retrieve filtered records + const response = await request(context.app) + .get(`/api/v1/db/data/noco/${project.id}/${table.id}`) + .set('xc-auth', context.token) + .query({ + filterArrJson: JSON.stringify([filter]), + }) + .expect(200); + + // validate + expect(response.body.pageInfo.totalRows).to.equal(expectedRecords.length); + response.body.list.forEach((row, index) => { + expect(row[columns[1].title] !== expectedRecords[index].SingleLineText); + }); + } + + it('Type: Single Line Text', async () => { + // filter types to be verified + // eq, neq, null, notnull, empty, notempty, like, nlike + + const filter = { + fk_column_id: columns[1].id, + status: 'create', + logical_op: 'and', + comparison_op: 'eq', + value: 'Afghanistan', + }; + + let expectedRecords = unfilteredRecords.filter( + (record) => record.SingleLineText === 'Afghanistan' + ); + await retrieveRecordsAndValidate(filter, expectedRecords); + + // filter: neq + filter.comparison_op = 'neq'; + expectedRecords = unfilteredRecords.filter( + (record) => record.SingleLineText !== 'Afghanistan' + ); + await retrieveRecordsAndValidate(filter, expectedRecords); + + // filter: null + filter.comparison_op = 'null'; + expectedRecords = unfilteredRecords.filter( + (record) => record.SingleLineText === null + ); + await retrieveRecordsAndValidate(filter, expectedRecords); + + // filter: notnull + filter.comparison_op = 'notnull'; + expectedRecords = unfilteredRecords.filter( + (record) => record.SingleLineText !== null + ); + await retrieveRecordsAndValidate(filter, expectedRecords); + + // filter: empty + filter.comparison_op = 'empty'; + expectedRecords = unfilteredRecords.filter( + (record) => record.SingleLineText === '' + ); + await retrieveRecordsAndValidate(filter, expectedRecords); + + // filter: notempty + filter.comparison_op = 'notempty'; + expectedRecords = unfilteredRecords.filter( + (record) => record.SingleLineText !== '' + ); + //TBD await retrieveRecordsAndValidate(filter, expectedRecords); + + // filter: like + filter.comparison_op = 'like'; + filter.value = 'Au'; + expectedRecords = unfilteredRecords.filter((record) => + record.SingleLineText?.includes('Au') + ); + await retrieveRecordsAndValidate(filter, expectedRecords); + + // filter: nlike + filter.comparison_op = 'nlike'; + filter.value = 'Au'; + expectedRecords = unfilteredRecords.filter( + (record) => !record.SingleLineText?.includes('Au') + ); + await retrieveRecordsAndValidate(filter, expectedRecords); + }); } export default function () { From 3d476125e53b3a838f3baaa59398490f598cc5a3 Mon Sep 17 00:00:00 2001 From: Raju Udava <86527202+dstala@users.noreply.github.com> Date: Fri, 3 Feb 2023 22:30:27 +0530 Subject: [PATCH 3/7] test: filter for numeric types Signed-off-by: Raju Udava <86527202+dstala@users.noreply.github.com> --- packages/nocodb/tests/unit/factory/row.ts | 25 +- .../tests/unit/rest/tests/filter.test.ts | 884 +++++++++++++++++- 2 files changed, 860 insertions(+), 49 deletions(-) diff --git a/packages/nocodb/tests/unit/factory/row.ts b/packages/nocodb/tests/unit/factory/row.ts index 484fc60b24..7ed81a64b4 100644 --- a/packages/nocodb/tests/unit/factory/row.ts +++ b/packages/nocodb/tests/unit/factory/row.ts @@ -61,7 +61,22 @@ const rowMixedValue = (column: ColumnType, index: number) => { ]; // Array of random integers, not more than 10000 - const numbers = [33, 456, 54, 267, 34, 8754, 3234, 44, 33, null]; + const numbers = [33, null, 456, 333, 267, 34, 8754, 3234, 44, 33, null]; + const decimals = [ + 33.3, + 456.34, + 333.3, + null, + 267.5674, + 34.0, + 8754.0, + 3234.547, + 44.2647, + 33.98, + null, + ]; + const duration = [10, 20, 30, 40, 50, 60, null, 70, 80, 90, null]; + const rating = [0, 1, 2, 3, null, 0, 4, 5, 0, 1, null]; // Array of random sample email strings (not more than 100 characters) const emails = [ @@ -115,7 +130,15 @@ const rowMixedValue = (column: ColumnType, index: number) => { switch (column.uidt) { case UITypes.Number: + case UITypes.Percent: return numbers[index % numbers.length]; + case UITypes.Decimal: + case UITypes.Currency: + return decimals[index % decimals.length]; + case UITypes.Duration: + return duration[index % duration.length]; + case UITypes.Rating: + return rating[index % rating.length]; case UITypes.SingleLineText: return countries[index % countries.length]; case UITypes.Email: diff --git a/packages/nocodb/tests/unit/rest/tests/filter.test.ts b/packages/nocodb/tests/unit/rest/tests/filter.test.ts index dc89848231..3d2fd52d65 100644 --- a/packages/nocodb/tests/unit/rest/tests/filter.test.ts +++ b/packages/nocodb/tests/unit/rest/tests/filter.test.ts @@ -16,14 +16,66 @@ import Model from '../../../../src/lib/models/Model'; import { expect } from 'chai'; import request from 'supertest'; +const debugMode = true; + // Test case list -function filterTests() { - let context; - let project: Project; - let table: Model; - let columns: any[]; - let unfilteredRecords: any[] = []; +async function retrieveRecordsAndValidate( + filter: { + comparison_op: string; + value: string; + fk_column_id: any; + status: string; + logical_op: string; + }, + expectedRecords: any[], + title: string +) { + // retrieve filtered records + const response = await request(context.app) + .get(`/api/v1/db/data/noco/${project.id}/${table.id}`) + .set('xc-auth', context.token) + .query({ + filterArrJson: JSON.stringify([filter]), + }) + .expect(200); + + // validate + if (debugMode) { + if (response.body.pageInfo.totalRows !== expectedRecords.length) { + console.log(`Failed for filter: ${JSON.stringify(filter)}`); + console.log(`Expected: ${expectedRecords.length}`); + console.log(`Actual: ${response.body.pageInfo.totalRows}`); + throw new Error('fix me!'); + } + response.body.list.forEach((row, index) => { + if (row[title] !== expectedRecords[index][title]) { + console.log(`Failed for filter: ${JSON.stringify(filter)}`); + console.log(`Expected: ${expectedRecords[index][title]}`); + console.log(`Actual: ${row[title]}`); + throw new Error('fix me!'); + } + }); + } else { + expect(response.body.pageInfo.totalRows).to.equal(expectedRecords.length); + response.body.list.forEach((row, index) => { + expect(row[title] !== expectedRecords[index][title]); + }); + } +} + +let context; +let project: Project; +let table: Model; +let columns: any[]; +let unfilteredRecords: any[] = []; + +function filterTextBased() { + // let context; + // let project: Project; + // let table: Model; + // let columns: any[]; + // let unfilteredRecords: any[] = []; // prepare data for test cases beforeEach(async function () { @@ -91,36 +143,55 @@ function filterTests() { expect(unfilteredRecords.length).to.equal(400); }); - async function retrieveRecordsAndValidate( - filter: { - comparison_op: string; - value: string; - fk_column_id: any; - status: string; - logical_op: string; - }, - expectedRecords: any[] - ) { - // retrieve filtered records - const response = await request(context.app) - .get(`/api/v1/db/data/noco/${project.id}/${table.id}`) - .set('xc-auth', context.token) - .query({ - filterArrJson: JSON.stringify([filter]), - }) - .expect(200); - - // validate - expect(response.body.pageInfo.totalRows).to.equal(expectedRecords.length); - response.body.list.forEach((row, index) => { - expect(row[columns[1].title] !== expectedRecords[index].SingleLineText); - }); - } + // async function retrieveRecordsAndValidate( + // filter: { + // comparison_op: string; + // value: string; + // fk_column_id: any; + // status: string; + // logical_op: string; + // }, + // expectedRecords: any[] + // ) { + // // retrieve filtered records + // const response = await request(context.app) + // .get(`/api/v1/db/data/noco/${project.id}/${table.id}`) + // .set('xc-auth', context.token) + // .query({ + // filterArrJson: JSON.stringify([filter]), + // }) + // .expect(200); + // + // // validate + // if (debugMode) { + // if (response.body.pageInfo.totalRows !== expectedRecords.length) { + // console.log(`Failed for filter: ${JSON.stringify(filter)}`); + // console.log(`Expected: ${expectedRecords.length}`); + // console.log(`Actual: ${response.body.pageInfo.totalRows}`); + // throw new Error('fix me!'); + // } + // response.body.list.forEach((row, index) => { + // if (row[columns[1].title] !== expectedRecords[index].SingleLineText) { + // console.log(`Failed for filter: ${JSON.stringify(filter)}`); + // console.log(`Expected: ${expectedRecords[index].SingleLineText}`); + // console.log(`Actual: ${row[columns[1].title]}`); + // throw new Error('fix me!'); + // } + // }); + // } else { + // expect(response.body.pageInfo.totalRows).to.equal(expectedRecords.length); + // response.body.list.forEach((row, index) => { + // expect(row[columns[1].title] !== expectedRecords[index].SingleLineText); + // }); + // } + // } it('Type: Single Line Text', async () => { // filter types to be verified // eq, neq, null, notnull, empty, notempty, like, nlike + const dataType = 'SingleLineText'; + const filter = { fk_column_id: columns[1].id, status: 'create', @@ -130,63 +201,780 @@ function filterTests() { }; let expectedRecords = unfilteredRecords.filter( - (record) => record.SingleLineText === 'Afghanistan' + (record) => record[dataType] === 'Afghanistan' ); - await retrieveRecordsAndValidate(filter, expectedRecords); + await retrieveRecordsAndValidate(filter, expectedRecords, dataType); // filter: neq filter.comparison_op = 'neq'; expectedRecords = unfilteredRecords.filter( - (record) => record.SingleLineText !== 'Afghanistan' + (record) => record[dataType] !== 'Afghanistan' ); - await retrieveRecordsAndValidate(filter, expectedRecords); + await retrieveRecordsAndValidate(filter, expectedRecords, dataType); // filter: null filter.comparison_op = 'null'; expectedRecords = unfilteredRecords.filter( - (record) => record.SingleLineText === null + (record) => record[dataType] === null ); - await retrieveRecordsAndValidate(filter, expectedRecords); + await retrieveRecordsAndValidate(filter, expectedRecords, dataType); // filter: notnull filter.comparison_op = 'notnull'; expectedRecords = unfilteredRecords.filter( - (record) => record.SingleLineText !== null + (record) => record[dataType] !== null ); - await retrieveRecordsAndValidate(filter, expectedRecords); + await retrieveRecordsAndValidate(filter, expectedRecords, dataType); // filter: empty filter.comparison_op = 'empty'; expectedRecords = unfilteredRecords.filter( - (record) => record.SingleLineText === '' + (record) => record[dataType] === '' ); - await retrieveRecordsAndValidate(filter, expectedRecords); + await retrieveRecordsAndValidate(filter, expectedRecords, dataType); // filter: notempty filter.comparison_op = 'notempty'; expectedRecords = unfilteredRecords.filter( - (record) => record.SingleLineText !== '' + (record) => record[dataType] !== '' ); - //TBD await retrieveRecordsAndValidate(filter, expectedRecords); + //TBD await retrieveRecordsAndValidate(filter, expectedRecords, dataType); // filter: like filter.comparison_op = 'like'; filter.value = 'Au'; expectedRecords = unfilteredRecords.filter((record) => - record.SingleLineText?.includes('Au') + record[dataType]?.includes('Au') ); - await retrieveRecordsAndValidate(filter, expectedRecords); + await retrieveRecordsAndValidate(filter, expectedRecords, dataType); // filter: nlike filter.comparison_op = 'nlike'; filter.value = 'Au'; expectedRecords = unfilteredRecords.filter( - (record) => !record.SingleLineText?.includes('Au') + (record) => !record[dataType]?.includes('Au') + ); + await retrieveRecordsAndValidate(filter, expectedRecords, dataType); + }); + + it('Type: LongText', async () => { + // filter types to be verified + // eq, neq, null, notnull, empty, notempty, like, nlike + + const dataType = 'MultiLineText'; + + const filter = { + fk_column_id: columns[2].id, + status: 'create', + logical_op: 'and', + comparison_op: 'eq', + value: 'Aberdeen, United Kingdom', + }; + + let expectedRecords = unfilteredRecords.filter( + (record) => record[dataType] === 'Aberdeen, United Kingdom' + ); + await retrieveRecordsAndValidate(filter, expectedRecords, dataType); + + // filter: neq + filter.comparison_op = 'neq'; + expectedRecords = unfilteredRecords.filter( + (record) => record[dataType] !== 'Aberdeen, United Kingdom' + ); + await retrieveRecordsAndValidate(filter, expectedRecords, dataType); + + // filter: null + filter.comparison_op = 'null'; + expectedRecords = unfilteredRecords.filter( + (record) => record[dataType] === null + ); + await retrieveRecordsAndValidate(filter, expectedRecords, dataType); + + // filter: notnull + filter.comparison_op = 'notnull'; + expectedRecords = unfilteredRecords.filter( + (record) => record[dataType] !== null + ); + await retrieveRecordsAndValidate(filter, expectedRecords, dataType); + + // filter: empty + filter.comparison_op = 'empty'; + expectedRecords = unfilteredRecords.filter( + (record) => record[dataType] === '' + ); + await retrieveRecordsAndValidate(filter, expectedRecords, dataType); + + // filter: notempty + filter.comparison_op = 'notempty'; + expectedRecords = unfilteredRecords.filter( + (record) => record[dataType] !== '' + ); + //TBD await retrieveRecordsAndValidate(filter, expectedRecords, dataType); + + // filter: like + filter.comparison_op = 'like'; + filter.value = 'abad'; + expectedRecords = unfilteredRecords.filter((record) => + record[dataType]?.includes('abad') + ); + await retrieveRecordsAndValidate(filter, expectedRecords, dataType); + + // filter: nlike + filter.comparison_op = 'nlike'; + filter.value = 'abad'; + expectedRecords = unfilteredRecords.filter( + (record) => !record[dataType]?.includes('abad') + ); + await retrieveRecordsAndValidate(filter, expectedRecords, dataType); + }); + + it('Type: Email', async () => { + // filter types to be verified + // eq, neq, null, notnull, empty, notempty, like, nlike + + const dataType = 'Email'; + + const filter = { + fk_column_id: columns[3].id, + status: 'create', + logical_op: 'and', + comparison_op: 'eq', + value: 'leota@hotmail.com', + }; + + let expectedRecords = unfilteredRecords.filter( + (record) => record[dataType] === 'leota@hotmail.com' + ); + await retrieveRecordsAndValidate(filter, expectedRecords, dataType); + + // filter: neq + filter.comparison_op = 'neq'; + expectedRecords = unfilteredRecords.filter( + (record) => record[dataType] !== 'leota@hotmail.com' + ); + await retrieveRecordsAndValidate(filter, expectedRecords, dataType); + + // filter: null + filter.comparison_op = 'null'; + expectedRecords = unfilteredRecords.filter( + (record) => record[dataType] === null + ); + await retrieveRecordsAndValidate(filter, expectedRecords, dataType); + + // filter: notnull + filter.comparison_op = 'notnull'; + expectedRecords = unfilteredRecords.filter( + (record) => record[dataType] !== null + ); + await retrieveRecordsAndValidate(filter, expectedRecords, dataType); + + // filter: empty + filter.comparison_op = 'empty'; + expectedRecords = unfilteredRecords.filter( + (record) => record[dataType] === '' + ); + await retrieveRecordsAndValidate(filter, expectedRecords, dataType); + + // filter: notempty + filter.comparison_op = 'notempty'; + expectedRecords = unfilteredRecords.filter( + (record) => record[dataType] !== '' + ); + //TBD await retrieveRecordsAndValidate(filter, expectedRecords, dataType); + + // filter: like + filter.comparison_op = 'like'; + filter.value = 'cox.net'; + expectedRecords = unfilteredRecords.filter((record) => + record[dataType]?.includes('cox.net') + ); + await retrieveRecordsAndValidate(filter, expectedRecords, dataType); + + // filter: nlike + filter.comparison_op = 'nlike'; + filter.value = 'cox.net'; + expectedRecords = unfilteredRecords.filter( + (record) => !record[dataType]?.includes('cox.net') + ); + await retrieveRecordsAndValidate(filter, expectedRecords, dataType); + }); + + it('Type: Phone', async () => { + // filter types to be verified + // eq, neq, null, notnull, empty, notempty, like, nlike + + const dataType = 'Phone'; + + const filter = { + fk_column_id: columns[4].id, + status: 'create', + logical_op: 'and', + comparison_op: 'eq', + value: '504-621-8927', + }; + + let expectedRecords = unfilteredRecords.filter( + (record) => record[dataType] === '504-621-8927' + ); + await retrieveRecordsAndValidate(filter, expectedRecords, dataType); + + // filter: neq + filter.comparison_op = 'neq'; + expectedRecords = unfilteredRecords.filter( + (record) => record[dataType] !== '504-621-8927' + ); + await retrieveRecordsAndValidate(filter, expectedRecords, dataType); + + // filter: null + filter.comparison_op = 'null'; + expectedRecords = unfilteredRecords.filter( + (record) => record[dataType] === null + ); + await retrieveRecordsAndValidate(filter, expectedRecords, dataType); + + // filter: notnull + filter.comparison_op = 'notnull'; + expectedRecords = unfilteredRecords.filter( + (record) => record[dataType] !== null + ); + await retrieveRecordsAndValidate(filter, expectedRecords, dataType); + + // filter: empty + filter.comparison_op = 'empty'; + expectedRecords = unfilteredRecords.filter( + (record) => record[dataType] === '' + ); + await retrieveRecordsAndValidate(filter, expectedRecords, dataType); + + // filter: notempty + filter.comparison_op = 'notempty'; + expectedRecords = unfilteredRecords.filter( + (record) => record[dataType] !== '' + ); + //TBD await retrieveRecordsAndValidate(filter, expectedRecords, dataType); + + // filter: like + filter.comparison_op = 'like'; + filter.value = '504'; + expectedRecords = unfilteredRecords.filter((record) => + record[dataType]?.includes('504') + ); + await retrieveRecordsAndValidate(filter, expectedRecords, dataType); + + // filter: nlike + filter.comparison_op = 'nlike'; + filter.value = '504'; + expectedRecords = unfilteredRecords.filter( + (record) => !record[dataType]?.includes('504') + ); + await retrieveRecordsAndValidate(filter, expectedRecords, dataType); + }); + + it('Type: URL', async () => { + // filter types to be verified + // eq, neq, null, notnull, empty, notempty, like, nlike + + const dataType = 'Url'; + + const filter = { + fk_column_id: columns[5].id, + status: 'create', + logical_op: 'and', + comparison_op: 'eq', + value: 'https://www.youtube.com', + }; + + let expectedRecords = unfilteredRecords.filter( + (record) => record[dataType] === 'https://www.youtube.com' + ); + await retrieveRecordsAndValidate(filter, expectedRecords, dataType); + + // filter: neq + filter.comparison_op = 'neq'; + expectedRecords = unfilteredRecords.filter( + (record) => record[dataType] !== 'https://www.youtube.com' + ); + await retrieveRecordsAndValidate(filter, expectedRecords, dataType); + + // filter: null + filter.comparison_op = 'null'; + expectedRecords = unfilteredRecords.filter( + (record) => record[dataType] === null + ); + await retrieveRecordsAndValidate(filter, expectedRecords, dataType); + + // filter: notnull + filter.comparison_op = 'notnull'; + expectedRecords = unfilteredRecords.filter( + (record) => record[dataType] !== null + ); + await retrieveRecordsAndValidate(filter, expectedRecords, dataType); + + // filter: empty + filter.comparison_op = 'empty'; + expectedRecords = unfilteredRecords.filter( + (record) => record[dataType] === '' + ); + await retrieveRecordsAndValidate(filter, expectedRecords, dataType); + + // filter: notempty + filter.comparison_op = 'notempty'; + expectedRecords = unfilteredRecords.filter( + (record) => record[dataType] !== '' + ); + //TBD await retrieveRecordsAndValidate(filter, expectedRecords, dataType); + + // filter: like + filter.comparison_op = 'like'; + filter.value = 'e.com'; + expectedRecords = unfilteredRecords.filter((record) => + record[dataType]?.includes('e.com') + ); + await retrieveRecordsAndValidate(filter, expectedRecords, dataType); + + // filter: nlike + filter.comparison_op = 'nlike'; + filter.value = 'e.com'; + expectedRecords = unfilteredRecords.filter( + (record) => !record[dataType]?.includes('e.com') + ); + await retrieveRecordsAndValidate(filter, expectedRecords, dataType); + }); +} + +function filterNumberBased() { + // prepare data for test cases + beforeEach(async function () { + context = await init(); + project = await createProject(context); + table = await createTable(context, project, { + table_name: 'numberBased', + title: 'numberBased', + columns: [ + { + column_name: 'Id', + title: 'Id', + uidt: UITypes.ID, + }, + { + column_name: 'Number', + title: 'Number', + uidt: UITypes.Number, + }, + { + column_name: 'Decimal', + title: 'Decimal', + uidt: UITypes.Decimal, + }, + { + column_name: 'Currency', + title: 'Currency', + uidt: UITypes.Currency, + }, + { + column_name: 'Percent', + title: 'Percent', + uidt: UITypes.Percent, + }, + { + column_name: 'Duration', + title: 'Duration', + uidt: UITypes.Duration, + }, + { + column_name: 'Rating', + title: 'Rating', + uidt: UITypes.Rating, + }, + ], + }); + + columns = await table.getColumns(); + + let rowAttributes = []; + for (let i = 0; i < 400; i++) { + let row = { + Number: rowMixedValue(columns[1], i), + Decimal: rowMixedValue(columns[2], i), + Currency: rowMixedValue(columns[3], i), + Percent: rowMixedValue(columns[4], i), + Duration: rowMixedValue(columns[5], i), + Rating: rowMixedValue(columns[6], i), + }; + rowAttributes.push(row); + } + + await createBulkRows(context, { + project, + table, + values: rowAttributes, + }); + unfilteredRecords = await listRow({ project, table }); + + // verify length of unfiltered records to be 400 + expect(unfilteredRecords.length).to.equal(400); + }); + + it('Type: Number', async () => { + // filter types to be verified + // eq, neq, null, notnull, like, nlike, >, >=, <, <= + + const dataType = 'Number'; + + const filter = { + fk_column_id: columns[1].id, + status: 'create', + logical_op: 'and', + comparison_op: 'eq', + value: '33', + }; + + let expectedRecords = unfilteredRecords.filter( + (record) => record[dataType] === 33 + ); + await retrieveRecordsAndValidate(filter, expectedRecords, dataType); + + // filter: neq + filter.comparison_op = 'neq'; + expectedRecords = unfilteredRecords.filter( + (record) => record[dataType] !== 33 + ); + await retrieveRecordsAndValidate(filter, expectedRecords, dataType); + + // filter: null + filter.comparison_op = 'null'; + expectedRecords = unfilteredRecords.filter( + (record) => record[dataType] === null + ); + await retrieveRecordsAndValidate(filter, expectedRecords, dataType); + + // filter: notnull + filter.comparison_op = 'notnull'; + expectedRecords = unfilteredRecords.filter( + (record) => record[dataType] !== null + ); + await retrieveRecordsAndValidate(filter, expectedRecords, dataType); + + // filter: > + filter.comparison_op = 'gt'; + filter.value = '44'; + expectedRecords = unfilteredRecords.filter( + (record) => record[dataType] > 44 && record[dataType] !== null + ); + await retrieveRecordsAndValidate(filter, expectedRecords, dataType); + + // filter: >= + filter.comparison_op = 'gte'; + expectedRecords = unfilteredRecords.filter( + (record) => record[dataType] >= 44 && record[dataType] !== null + ); + await retrieveRecordsAndValidate(filter, expectedRecords, dataType); + + // filter: < + filter.comparison_op = 'lt'; + expectedRecords = unfilteredRecords.filter( + (record) => record[dataType] < 44 && record[dataType] !== null + ); + await retrieveRecordsAndValidate(filter, expectedRecords, dataType); + + // filter: <= + filter.comparison_op = 'lte'; + expectedRecords = unfilteredRecords.filter( + (record) => record[dataType] <= 44 && record[dataType] !== null + ); + await retrieveRecordsAndValidate(filter, expectedRecords, dataType); + }); + + it('Type: Decimal', async () => { + // filter types to be verified + // eq, neq, null, notnull, like, nlike, >, >=, <, <= + + const dataType = 'Decimal'; + + const filter = { + fk_column_id: columns[2].id, + status: 'create', + logical_op: 'and', + comparison_op: 'eq', + value: '33.3', + }; + + let expectedRecords = unfilteredRecords.filter( + (record) => parseFloat(record[dataType]) === 33.3 + ); + await retrieveRecordsAndValidate(filter, expectedRecords, dataType); + + // filter: neq + filter.comparison_op = 'neq'; + expectedRecords = unfilteredRecords.filter( + (record) => parseFloat(record[dataType]) !== 33.3 + ); + await retrieveRecordsAndValidate(filter, expectedRecords, dataType); + + // filter: null + filter.comparison_op = 'null'; + expectedRecords = unfilteredRecords.filter( + (record) => record[dataType] === null + ); + await retrieveRecordsAndValidate(filter, expectedRecords, dataType); + + // filter: notnull + filter.comparison_op = 'notnull'; + expectedRecords = unfilteredRecords.filter( + (record) => record[dataType] !== null + ); + await retrieveRecordsAndValidate(filter, expectedRecords, dataType); + + // filter: > + filter.comparison_op = 'gt'; + filter.value = '44.26'; + expectedRecords = unfilteredRecords.filter( + (record) => + parseFloat(record[dataType]) > 44.26 && record[dataType] !== null + ); + await retrieveRecordsAndValidate(filter, expectedRecords, dataType); + + // filter: >= + filter.comparison_op = 'gte'; + expectedRecords = unfilteredRecords.filter( + (record) => + parseFloat(record[dataType]) >= 44.26 && record[dataType] !== null + ); + await retrieveRecordsAndValidate(filter, expectedRecords, dataType); + + // filter: < + filter.comparison_op = 'lt'; + expectedRecords = unfilteredRecords.filter( + (record) => + parseFloat(record[dataType]) < 44.26 && record[dataType] !== null + ); + await retrieveRecordsAndValidate(filter, expectedRecords, dataType); + + // filter: <= + filter.comparison_op = 'lte'; + expectedRecords = unfilteredRecords.filter( + (record) => + parseFloat(record[dataType]) <= 44.26 && record[dataType] !== null + ); + await retrieveRecordsAndValidate(filter, expectedRecords, dataType); + }); + + it('Type: Currency', async () => { + // filter types to be verified + // eq, neq, null, notnull, like, nlike, >, >=, <, <= + + const dataType = 'Currency'; + + const filter = { + fk_column_id: columns[3].id, + status: 'create', + logical_op: 'and', + comparison_op: 'eq', + value: '33.3', + }; + + let expectedRecords = unfilteredRecords.filter( + (record) => parseFloat(record[dataType]) === 33.3 + ); + await retrieveRecordsAndValidate(filter, expectedRecords, dataType); + + // filter: neq + filter.comparison_op = 'neq'; + expectedRecords = unfilteredRecords.filter( + (record) => parseFloat(record[dataType]) !== 33.3 + ); + await retrieveRecordsAndValidate(filter, expectedRecords, dataType); + + // filter: null + filter.comparison_op = 'null'; + expectedRecords = unfilteredRecords.filter( + (record) => record[dataType] === null + ); + await retrieveRecordsAndValidate(filter, expectedRecords, dataType); + + // filter: notnull + filter.comparison_op = 'notnull'; + expectedRecords = unfilteredRecords.filter( + (record) => record[dataType] !== null + ); + await retrieveRecordsAndValidate(filter, expectedRecords, dataType); + + // filter: > + filter.comparison_op = 'gt'; + filter.value = '44.26'; + expectedRecords = unfilteredRecords.filter( + (record) => + parseFloat(record[dataType]) > 44.26 && record[dataType] !== null + ); + await retrieveRecordsAndValidate(filter, expectedRecords, dataType); + + // filter: >= + filter.comparison_op = 'gte'; + expectedRecords = unfilteredRecords.filter( + (record) => + parseFloat(record[dataType]) >= 44.26 && record[dataType] !== null + ); + await retrieveRecordsAndValidate(filter, expectedRecords, dataType); + + // filter: < + filter.comparison_op = 'lt'; + expectedRecords = unfilteredRecords.filter( + (record) => + parseFloat(record[dataType]) < 44.26 && record[dataType] !== null + ); + await retrieveRecordsAndValidate(filter, expectedRecords, dataType); + + // filter: <= + filter.comparison_op = 'lte'; + expectedRecords = unfilteredRecords.filter( + (record) => + parseFloat(record[dataType]) <= 44.26 && record[dataType] !== null + ); + await retrieveRecordsAndValidate(filter, expectedRecords, dataType); + }); + + it('Type: Percent', async () => { + // filter types to be verified + // eq, neq, null, notnull, like, nlike, >, >=, <, <= + + const dataType = 'Percent'; + + const filter = { + fk_column_id: columns[4].id, + status: 'create', + logical_op: 'and', + comparison_op: 'eq', + value: '33', + }; + + let expectedRecords = unfilteredRecords.filter( + (record) => record[dataType] === 33 + ); + await retrieveRecordsAndValidate(filter, expectedRecords, dataType); + + // filter: neq + filter.comparison_op = 'neq'; + expectedRecords = unfilteredRecords.filter( + (record) => record[dataType] !== 33 + ); + await retrieveRecordsAndValidate(filter, expectedRecords, dataType); + + // filter: null + filter.comparison_op = 'null'; + expectedRecords = unfilteredRecords.filter( + (record) => record[dataType] === null + ); + await retrieveRecordsAndValidate(filter, expectedRecords, dataType); + + // filter: notnull + filter.comparison_op = 'notnull'; + expectedRecords = unfilteredRecords.filter( + (record) => record[dataType] !== null + ); + await retrieveRecordsAndValidate(filter, expectedRecords, dataType); + + // filter: > + filter.comparison_op = 'gt'; + filter.value = '44'; + expectedRecords = unfilteredRecords.filter( + (record) => record[dataType] > 44 && record[dataType] !== null + ); + await retrieveRecordsAndValidate(filter, expectedRecords, dataType); + + // filter: >= + filter.comparison_op = 'gte'; + expectedRecords = unfilteredRecords.filter( + (record) => record[dataType] >= 44 && record[dataType] !== null + ); + await retrieveRecordsAndValidate(filter, expectedRecords, dataType); + + // filter: < + filter.comparison_op = 'lt'; + expectedRecords = unfilteredRecords.filter( + (record) => record[dataType] < 44 && record[dataType] !== null + ); + await retrieveRecordsAndValidate(filter, expectedRecords, dataType); + + // filter: <= + filter.comparison_op = 'lte'; + expectedRecords = unfilteredRecords.filter( + (record) => record[dataType] <= 44 && record[dataType] !== null + ); + await retrieveRecordsAndValidate(filter, expectedRecords, dataType); + }); + + it('Type: Rating', async () => { + // filter types to be verified + // eq, neq, null, notnull, like, nlike, >, >=, <, <= + + const dataType = 'Rating'; + + const filter = { + fk_column_id: columns[6].id, + status: 'create', + logical_op: 'and', + comparison_op: 'eq', + value: '3', + }; + + let expectedRecords = unfilteredRecords.filter( + (record) => record[dataType] === 3 + ); + await retrieveRecordsAndValidate(filter, expectedRecords, dataType); + + // filter: neq + filter.comparison_op = 'neq'; + expectedRecords = unfilteredRecords.filter( + (record) => record[dataType] !== 3 + ); + await retrieveRecordsAndValidate(filter, expectedRecords, dataType); + + // filter: null + filter.comparison_op = 'null'; + expectedRecords = unfilteredRecords.filter( + (record) => record[dataType] === null + ); + await retrieveRecordsAndValidate(filter, expectedRecords, dataType); + + // filter: notnull + filter.comparison_op = 'notnull'; + expectedRecords = unfilteredRecords.filter( + (record) => record[dataType] !== null + ); + await retrieveRecordsAndValidate(filter, expectedRecords, dataType); + + // filter: > + filter.comparison_op = 'gt'; + filter.value = '2'; + expectedRecords = unfilteredRecords.filter( + (record) => record[dataType] > 2 && record[dataType] !== null + ); + await retrieveRecordsAndValidate(filter, expectedRecords, dataType); + + // filter: >= + filter.comparison_op = 'gte'; + expectedRecords = unfilteredRecords.filter( + (record) => record[dataType] >= 2 && record[dataType] !== null + ); + await retrieveRecordsAndValidate(filter, expectedRecords, dataType); + + // filter: < + filter.comparison_op = 'lt'; + expectedRecords = unfilteredRecords.filter( + (record) => record[dataType] < 2 && record[dataType] !== null + ); + await retrieveRecordsAndValidate(filter, expectedRecords, dataType); + + // filter: <= + filter.comparison_op = 'lte'; + expectedRecords = unfilteredRecords.filter( + (record) => record[dataType] <= 2 && record[dataType] !== null ); - await retrieveRecordsAndValidate(filter, expectedRecords); + await retrieveRecordsAndValidate(filter, expectedRecords, dataType); }); } export default function () { - describe('Filters', filterTests); + describe('Filter: Text based', filterTextBased); + describe('Filter: Numerical', filterNumberBased); } From d792297551b5c77fa39fa7957638cf75920f589d Mon Sep 17 00:00:00 2001 From: Raju Udava <86527202+dstala@users.noreply.github.com> Date: Sat, 4 Feb 2023 12:44:12 +0530 Subject: [PATCH 4/7] test: filter file cleanup Signed-off-by: Raju Udava <86527202+dstala@users.noreply.github.com> --- .../tests/unit/rest/tests/filter.test.ts | 958 ++++-------------- 1 file changed, 215 insertions(+), 743 deletions(-) diff --git a/packages/nocodb/tests/unit/rest/tests/filter.test.ts b/packages/nocodb/tests/unit/rest/tests/filter.test.ts index 3d2fd52d65..cdcbd75b93 100644 --- a/packages/nocodb/tests/unit/rest/tests/filter.test.ts +++ b/packages/nocodb/tests/unit/rest/tests/filter.test.ts @@ -3,15 +3,8 @@ import init from '../../init'; import { createProject } from '../../factory/project'; import Project from '../../../../src/lib/models/Project'; import { createTable } from '../../factory/table'; -import { isSqlite } from '../../init/db'; import { UITypes } from 'nocodb-sdk'; -import { - createBulkRows, - generateDefaultRowAttributes, - generateMixedRowAttributes, - rowMixedValue, - listRow, -} from '../../factory/row'; +import { createBulkRows, rowMixedValue, listRow } from '../../factory/row'; import Model from '../../../../src/lib/models/Model'; import { expect } from 'chai'; import request from 'supertest'; @@ -28,9 +21,94 @@ async function retrieveRecordsAndValidate( status: string; logical_op: string; }, - expectedRecords: any[], title: string ) { + let expectedRecords = []; + let toFloat = false; + if (['Number', 'Decimal', 'Currency', 'Percent', 'Rating'].includes(title)) { + toFloat = true; + } + + // case for all comparison operators + switch (filter.comparison_op) { + case 'eq': + expectedRecords = unfilteredRecords.filter( + (record) => + (toFloat ? parseFloat(record[title]) : record[title]) === + (toFloat ? parseFloat(filter.value) : filter.value) + ); + break; + case 'neq': + expectedRecords = unfilteredRecords.filter( + (record) => + (toFloat ? parseFloat(record[title]) : record[title]) !== + (toFloat ? parseFloat(filter.value) : filter.value) + ); + break; + case 'null': + expectedRecords = unfilteredRecords.filter( + (record) => record[title] === null + ); + break; + case 'notnull': + expectedRecords = unfilteredRecords.filter( + (record) => record[title] !== null + ); + break; + case 'empty': + expectedRecords = unfilteredRecords.filter( + (record) => record[title] === '' + ); + break; + case 'notempty': + expectedRecords = unfilteredRecords.filter( + (record) => record[title] !== '' + ); + break; + case 'like': + expectedRecords = unfilteredRecords.filter((record) => + record[title]?.includes(filter.value) + ); + break; + case 'nlike': + expectedRecords = unfilteredRecords.filter( + (record) => !record[title]?.includes(filter.value) + ); + break; + case 'gt': + expectedRecords = unfilteredRecords.filter( + (record) => + (toFloat ? parseFloat(record[title]) : record[title]) > + (toFloat ? parseFloat(filter.value) : filter.value) && + record[title] !== null + ); + break; + case 'gte': + expectedRecords = unfilteredRecords.filter( + (record) => + (toFloat ? parseFloat(record[title]) : record[title]) >= + (toFloat ? parseFloat(filter.value) : filter.value) && + record[title] !== null + ); + break; + case 'lt': + expectedRecords = unfilteredRecords.filter( + (record) => + (toFloat ? parseFloat(record[title]) : record[title]) < + (toFloat ? parseFloat(filter.value) : filter.value) && + record[title] !== null + ); + break; + case 'lte': + expectedRecords = unfilteredRecords.filter( + (record) => + (toFloat ? parseFloat(record[title]) : record[title]) <= + (toFloat ? parseFloat(filter.value) : filter.value) && + record[title] !== null + ); + break; + } + // retrieve filtered records const response = await request(context.app) .get(`/api/v1/db/data/noco/${project.id}/${table.id}`) @@ -70,13 +148,23 @@ let table: Model; let columns: any[]; let unfilteredRecords: any[] = []; -function filterTextBased() { - // let context; - // let project: Project; - // let table: Model; - // let columns: any[]; - // let unfilteredRecords: any[] = []; +async function verifyFilters(dataType, columnId, filterList) { + const filter = { + fk_column_id: columnId, + status: 'create', + logical_op: 'and', + comparison_op: '', + value: '', + }; + + for (let i = 0; i < filterList.length; i++) { + filter.comparison_op = filterList[i].comparison_op; + filter.value = filterList[i].value; + await retrieveRecordsAndValidate(filter, dataType); + } +} +function filterTextBased() { // prepare data for test cases beforeEach(async function () { context = await init(); @@ -143,402 +231,74 @@ function filterTextBased() { expect(unfilteredRecords.length).to.equal(400); }); - // async function retrieveRecordsAndValidate( - // filter: { - // comparison_op: string; - // value: string; - // fk_column_id: any; - // status: string; - // logical_op: string; - // }, - // expectedRecords: any[] - // ) { - // // retrieve filtered records - // const response = await request(context.app) - // .get(`/api/v1/db/data/noco/${project.id}/${table.id}`) - // .set('xc-auth', context.token) - // .query({ - // filterArrJson: JSON.stringify([filter]), - // }) - // .expect(200); - // - // // validate - // if (debugMode) { - // if (response.body.pageInfo.totalRows !== expectedRecords.length) { - // console.log(`Failed for filter: ${JSON.stringify(filter)}`); - // console.log(`Expected: ${expectedRecords.length}`); - // console.log(`Actual: ${response.body.pageInfo.totalRows}`); - // throw new Error('fix me!'); - // } - // response.body.list.forEach((row, index) => { - // if (row[columns[1].title] !== expectedRecords[index].SingleLineText) { - // console.log(`Failed for filter: ${JSON.stringify(filter)}`); - // console.log(`Expected: ${expectedRecords[index].SingleLineText}`); - // console.log(`Actual: ${row[columns[1].title]}`); - // throw new Error('fix me!'); - // } - // }); - // } else { - // expect(response.body.pageInfo.totalRows).to.equal(expectedRecords.length); - // response.body.list.forEach((row, index) => { - // expect(row[columns[1].title] !== expectedRecords[index].SingleLineText); - // }); - // } - // } - it('Type: Single Line Text', async () => { - // filter types to be verified - // eq, neq, null, notnull, empty, notempty, like, nlike - - const dataType = 'SingleLineText'; - - const filter = { - fk_column_id: columns[1].id, - status: 'create', - logical_op: 'and', - comparison_op: 'eq', - value: 'Afghanistan', - }; - - let expectedRecords = unfilteredRecords.filter( - (record) => record[dataType] === 'Afghanistan' - ); - await retrieveRecordsAndValidate(filter, expectedRecords, dataType); - - // filter: neq - filter.comparison_op = 'neq'; - expectedRecords = unfilteredRecords.filter( - (record) => record[dataType] !== 'Afghanistan' - ); - await retrieveRecordsAndValidate(filter, expectedRecords, dataType); - - // filter: null - filter.comparison_op = 'null'; - expectedRecords = unfilteredRecords.filter( - (record) => record[dataType] === null - ); - await retrieveRecordsAndValidate(filter, expectedRecords, dataType); - - // filter: notnull - filter.comparison_op = 'notnull'; - expectedRecords = unfilteredRecords.filter( - (record) => record[dataType] !== null - ); - await retrieveRecordsAndValidate(filter, expectedRecords, dataType); - - // filter: empty - filter.comparison_op = 'empty'; - expectedRecords = unfilteredRecords.filter( - (record) => record[dataType] === '' - ); - await retrieveRecordsAndValidate(filter, expectedRecords, dataType); - - // filter: notempty - filter.comparison_op = 'notempty'; - expectedRecords = unfilteredRecords.filter( - (record) => record[dataType] !== '' - ); - //TBD await retrieveRecordsAndValidate(filter, expectedRecords, dataType); - - // filter: like - filter.comparison_op = 'like'; - filter.value = 'Au'; - expectedRecords = unfilteredRecords.filter((record) => - record[dataType]?.includes('Au') - ); - await retrieveRecordsAndValidate(filter, expectedRecords, dataType); - - // filter: nlike - filter.comparison_op = 'nlike'; - filter.value = 'Au'; - expectedRecords = unfilteredRecords.filter( - (record) => !record[dataType]?.includes('Au') - ); - await retrieveRecordsAndValidate(filter, expectedRecords, dataType); + let filterList = [ + { comparison_op: 'eq', value: 'Afghanistan' }, + { comparison_op: 'neq', value: 'Afghanistan' }, + { comparison_op: 'null', value: '' }, + { comparison_op: 'notnull', value: '' }, + { comparison_op: 'empty', value: '' }, + // { comparison_op: 'notempty', value: '' }, + { comparison_op: 'like', value: 'Au' }, + { comparison_op: 'nlike', value: 'Au' }, + ]; + await verifyFilters('SingleLineText', columns[1].id, filterList); }); - it('Type: LongText', async () => { - // filter types to be verified - // eq, neq, null, notnull, empty, notempty, like, nlike - - const dataType = 'MultiLineText'; - - const filter = { - fk_column_id: columns[2].id, - status: 'create', - logical_op: 'and', - comparison_op: 'eq', - value: 'Aberdeen, United Kingdom', - }; - - let expectedRecords = unfilteredRecords.filter( - (record) => record[dataType] === 'Aberdeen, United Kingdom' - ); - await retrieveRecordsAndValidate(filter, expectedRecords, dataType); - - // filter: neq - filter.comparison_op = 'neq'; - expectedRecords = unfilteredRecords.filter( - (record) => record[dataType] !== 'Aberdeen, United Kingdom' - ); - await retrieveRecordsAndValidate(filter, expectedRecords, dataType); - - // filter: null - filter.comparison_op = 'null'; - expectedRecords = unfilteredRecords.filter( - (record) => record[dataType] === null - ); - await retrieveRecordsAndValidate(filter, expectedRecords, dataType); - - // filter: notnull - filter.comparison_op = 'notnull'; - expectedRecords = unfilteredRecords.filter( - (record) => record[dataType] !== null - ); - await retrieveRecordsAndValidate(filter, expectedRecords, dataType); - - // filter: empty - filter.comparison_op = 'empty'; - expectedRecords = unfilteredRecords.filter( - (record) => record[dataType] === '' - ); - await retrieveRecordsAndValidate(filter, expectedRecords, dataType); - - // filter: notempty - filter.comparison_op = 'notempty'; - expectedRecords = unfilteredRecords.filter( - (record) => record[dataType] !== '' - ); - //TBD await retrieveRecordsAndValidate(filter, expectedRecords, dataType); - - // filter: like - filter.comparison_op = 'like'; - filter.value = 'abad'; - expectedRecords = unfilteredRecords.filter((record) => - record[dataType]?.includes('abad') - ); - await retrieveRecordsAndValidate(filter, expectedRecords, dataType); - - // filter: nlike - filter.comparison_op = 'nlike'; - filter.value = 'abad'; - expectedRecords = unfilteredRecords.filter( - (record) => !record[dataType]?.includes('abad') - ); - await retrieveRecordsAndValidate(filter, expectedRecords, dataType); + it('Type: Multi Line Text', async () => { + let filterList = [ + { comparison_op: 'eq', value: 'Aberdeen, United Kingdom' }, + { comparison_op: 'neq', value: 'Aberdeen, United Kingdom' }, + { comparison_op: 'null', value: '' }, + { comparison_op: 'notnull', value: '' }, + { comparison_op: 'empty', value: '' }, + // { comparison_op: 'notempty', value: '' }, + { comparison_op: 'like', value: 'abad' }, + { comparison_op: 'nlike', value: 'abad' }, + ]; + await verifyFilters('MultiLineText', columns[2].id, filterList); }); it('Type: Email', async () => { - // filter types to be verified - // eq, neq, null, notnull, empty, notempty, like, nlike - - const dataType = 'Email'; - - const filter = { - fk_column_id: columns[3].id, - status: 'create', - logical_op: 'and', - comparison_op: 'eq', - value: 'leota@hotmail.com', - }; - - let expectedRecords = unfilteredRecords.filter( - (record) => record[dataType] === 'leota@hotmail.com' - ); - await retrieveRecordsAndValidate(filter, expectedRecords, dataType); - - // filter: neq - filter.comparison_op = 'neq'; - expectedRecords = unfilteredRecords.filter( - (record) => record[dataType] !== 'leota@hotmail.com' - ); - await retrieveRecordsAndValidate(filter, expectedRecords, dataType); - - // filter: null - filter.comparison_op = 'null'; - expectedRecords = unfilteredRecords.filter( - (record) => record[dataType] === null - ); - await retrieveRecordsAndValidate(filter, expectedRecords, dataType); - - // filter: notnull - filter.comparison_op = 'notnull'; - expectedRecords = unfilteredRecords.filter( - (record) => record[dataType] !== null - ); - await retrieveRecordsAndValidate(filter, expectedRecords, dataType); - - // filter: empty - filter.comparison_op = 'empty'; - expectedRecords = unfilteredRecords.filter( - (record) => record[dataType] === '' - ); - await retrieveRecordsAndValidate(filter, expectedRecords, dataType); - - // filter: notempty - filter.comparison_op = 'notempty'; - expectedRecords = unfilteredRecords.filter( - (record) => record[dataType] !== '' - ); - //TBD await retrieveRecordsAndValidate(filter, expectedRecords, dataType); - - // filter: like - filter.comparison_op = 'like'; - filter.value = 'cox.net'; - expectedRecords = unfilteredRecords.filter((record) => - record[dataType]?.includes('cox.net') - ); - await retrieveRecordsAndValidate(filter, expectedRecords, dataType); - - // filter: nlike - filter.comparison_op = 'nlike'; - filter.value = 'cox.net'; - expectedRecords = unfilteredRecords.filter( - (record) => !record[dataType]?.includes('cox.net') - ); - await retrieveRecordsAndValidate(filter, expectedRecords, dataType); + let filterList = [ + { comparison_op: 'eq', value: 'leota@hotmail.com' }, + { comparison_op: 'neq', value: 'leota@hotmail.com' }, + { comparison_op: 'null', value: '' }, + { comparison_op: 'notnull', value: '' }, + { comparison_op: 'empty', value: '' }, + // { comparison_op: 'notempty', value: '' }, + { comparison_op: 'like', value: 'cox.net' }, + { comparison_op: 'nlike', value: 'cox.net' }, + ]; + await verifyFilters('Email', columns[3].id, filterList); }); it('Type: Phone', async () => { - // filter types to be verified - // eq, neq, null, notnull, empty, notempty, like, nlike - - const dataType = 'Phone'; - - const filter = { - fk_column_id: columns[4].id, - status: 'create', - logical_op: 'and', - comparison_op: 'eq', - value: '504-621-8927', - }; - - let expectedRecords = unfilteredRecords.filter( - (record) => record[dataType] === '504-621-8927' - ); - await retrieveRecordsAndValidate(filter, expectedRecords, dataType); - - // filter: neq - filter.comparison_op = 'neq'; - expectedRecords = unfilteredRecords.filter( - (record) => record[dataType] !== '504-621-8927' - ); - await retrieveRecordsAndValidate(filter, expectedRecords, dataType); - - // filter: null - filter.comparison_op = 'null'; - expectedRecords = unfilteredRecords.filter( - (record) => record[dataType] === null - ); - await retrieveRecordsAndValidate(filter, expectedRecords, dataType); - - // filter: notnull - filter.comparison_op = 'notnull'; - expectedRecords = unfilteredRecords.filter( - (record) => record[dataType] !== null - ); - await retrieveRecordsAndValidate(filter, expectedRecords, dataType); - - // filter: empty - filter.comparison_op = 'empty'; - expectedRecords = unfilteredRecords.filter( - (record) => record[dataType] === '' - ); - await retrieveRecordsAndValidate(filter, expectedRecords, dataType); - - // filter: notempty - filter.comparison_op = 'notempty'; - expectedRecords = unfilteredRecords.filter( - (record) => record[dataType] !== '' - ); - //TBD await retrieveRecordsAndValidate(filter, expectedRecords, dataType); - - // filter: like - filter.comparison_op = 'like'; - filter.value = '504'; - expectedRecords = unfilteredRecords.filter((record) => - record[dataType]?.includes('504') - ); - await retrieveRecordsAndValidate(filter, expectedRecords, dataType); - - // filter: nlike - filter.comparison_op = 'nlike'; - filter.value = '504'; - expectedRecords = unfilteredRecords.filter( - (record) => !record[dataType]?.includes('504') - ); - await retrieveRecordsAndValidate(filter, expectedRecords, dataType); + let filterList = [ + { comparison_op: 'eq', value: '504-621-8927' }, + { comparison_op: 'neq', value: '504-621-8927' }, + { comparison_op: 'null', value: '' }, + { comparison_op: 'notnull', value: '' }, + { comparison_op: 'empty', value: '' }, + // { comparison_op: 'notempty', value: '' }, + { comparison_op: 'like', value: '504' }, + { comparison_op: 'nlike', value: '504' }, + ]; + await verifyFilters('Phone', columns[4].id, filterList); }); - it('Type: URL', async () => { - // filter types to be verified - // eq, neq, null, notnull, empty, notempty, like, nlike - - const dataType = 'Url'; - - const filter = { - fk_column_id: columns[5].id, - status: 'create', - logical_op: 'and', - comparison_op: 'eq', - value: 'https://www.youtube.com', - }; - - let expectedRecords = unfilteredRecords.filter( - (record) => record[dataType] === 'https://www.youtube.com' - ); - await retrieveRecordsAndValidate(filter, expectedRecords, dataType); - - // filter: neq - filter.comparison_op = 'neq'; - expectedRecords = unfilteredRecords.filter( - (record) => record[dataType] !== 'https://www.youtube.com' - ); - await retrieveRecordsAndValidate(filter, expectedRecords, dataType); - - // filter: null - filter.comparison_op = 'null'; - expectedRecords = unfilteredRecords.filter( - (record) => record[dataType] === null - ); - await retrieveRecordsAndValidate(filter, expectedRecords, dataType); - - // filter: notnull - filter.comparison_op = 'notnull'; - expectedRecords = unfilteredRecords.filter( - (record) => record[dataType] !== null - ); - await retrieveRecordsAndValidate(filter, expectedRecords, dataType); - - // filter: empty - filter.comparison_op = 'empty'; - expectedRecords = unfilteredRecords.filter( - (record) => record[dataType] === '' - ); - await retrieveRecordsAndValidate(filter, expectedRecords, dataType); - - // filter: notempty - filter.comparison_op = 'notempty'; - expectedRecords = unfilteredRecords.filter( - (record) => record[dataType] !== '' - ); - //TBD await retrieveRecordsAndValidate(filter, expectedRecords, dataType); - - // filter: like - filter.comparison_op = 'like'; - filter.value = 'e.com'; - expectedRecords = unfilteredRecords.filter((record) => - record[dataType]?.includes('e.com') - ); - await retrieveRecordsAndValidate(filter, expectedRecords, dataType); - - // filter: nlike - filter.comparison_op = 'nlike'; - filter.value = 'e.com'; - expectedRecords = unfilteredRecords.filter( - (record) => !record[dataType]?.includes('e.com') - ); - await retrieveRecordsAndValidate(filter, expectedRecords, dataType); + it('Type: Url', async () => { + let filterList = [ + { comparison_op: 'eq', value: 'https://www.youtube.com' }, + { comparison_op: 'neq', value: 'https://www.youtube.com' }, + { comparison_op: 'null', value: '' }, + { comparison_op: 'notnull', value: '' }, + { comparison_op: 'empty', value: '' }, + // { comparison_op: 'notempty', value: '' }, + { comparison_op: 'like', value: 'e.com' }, + { comparison_op: 'nlike', value: 'e.com' }, + ]; + await verifyFilters('Url', columns[5].id, filterList); }); } @@ -616,361 +376,73 @@ function filterNumberBased() { }); it('Type: Number', async () => { - // filter types to be verified - // eq, neq, null, notnull, like, nlike, >, >=, <, <= - - const dataType = 'Number'; - - const filter = { - fk_column_id: columns[1].id, - status: 'create', - logical_op: 'and', - comparison_op: 'eq', - value: '33', - }; - - let expectedRecords = unfilteredRecords.filter( - (record) => record[dataType] === 33 - ); - await retrieveRecordsAndValidate(filter, expectedRecords, dataType); - - // filter: neq - filter.comparison_op = 'neq'; - expectedRecords = unfilteredRecords.filter( - (record) => record[dataType] !== 33 - ); - await retrieveRecordsAndValidate(filter, expectedRecords, dataType); - - // filter: null - filter.comparison_op = 'null'; - expectedRecords = unfilteredRecords.filter( - (record) => record[dataType] === null - ); - await retrieveRecordsAndValidate(filter, expectedRecords, dataType); - - // filter: notnull - filter.comparison_op = 'notnull'; - expectedRecords = unfilteredRecords.filter( - (record) => record[dataType] !== null - ); - await retrieveRecordsAndValidate(filter, expectedRecords, dataType); - - // filter: > - filter.comparison_op = 'gt'; - filter.value = '44'; - expectedRecords = unfilteredRecords.filter( - (record) => record[dataType] > 44 && record[dataType] !== null - ); - await retrieveRecordsAndValidate(filter, expectedRecords, dataType); - - // filter: >= - filter.comparison_op = 'gte'; - expectedRecords = unfilteredRecords.filter( - (record) => record[dataType] >= 44 && record[dataType] !== null - ); - await retrieveRecordsAndValidate(filter, expectedRecords, dataType); - - // filter: < - filter.comparison_op = 'lt'; - expectedRecords = unfilteredRecords.filter( - (record) => record[dataType] < 44 && record[dataType] !== null - ); - await retrieveRecordsAndValidate(filter, expectedRecords, dataType); - - // filter: <= - filter.comparison_op = 'lte'; - expectedRecords = unfilteredRecords.filter( - (record) => record[dataType] <= 44 && record[dataType] !== null - ); - await retrieveRecordsAndValidate(filter, expectedRecords, dataType); + let filterList = [ + { comparison_op: 'eq', value: '33' }, + { comparison_op: 'neq', value: '33' }, + { comparison_op: 'null', value: '' }, + { comparison_op: 'notnull', value: '' }, + { comparison_op: 'gt', value: '44' }, + { comparison_op: 'gte', value: '44' }, + { comparison_op: 'lt', value: '44' }, + { comparison_op: 'lte', value: '44' }, + ]; + await verifyFilters('Number', columns[1].id, filterList); }); it('Type: Decimal', async () => { - // filter types to be verified - // eq, neq, null, notnull, like, nlike, >, >=, <, <= - - const dataType = 'Decimal'; - - const filter = { - fk_column_id: columns[2].id, - status: 'create', - logical_op: 'and', - comparison_op: 'eq', - value: '33.3', - }; - - let expectedRecords = unfilteredRecords.filter( - (record) => parseFloat(record[dataType]) === 33.3 - ); - await retrieveRecordsAndValidate(filter, expectedRecords, dataType); - - // filter: neq - filter.comparison_op = 'neq'; - expectedRecords = unfilteredRecords.filter( - (record) => parseFloat(record[dataType]) !== 33.3 - ); - await retrieveRecordsAndValidate(filter, expectedRecords, dataType); - - // filter: null - filter.comparison_op = 'null'; - expectedRecords = unfilteredRecords.filter( - (record) => record[dataType] === null - ); - await retrieveRecordsAndValidate(filter, expectedRecords, dataType); - - // filter: notnull - filter.comparison_op = 'notnull'; - expectedRecords = unfilteredRecords.filter( - (record) => record[dataType] !== null - ); - await retrieveRecordsAndValidate(filter, expectedRecords, dataType); - - // filter: > - filter.comparison_op = 'gt'; - filter.value = '44.26'; - expectedRecords = unfilteredRecords.filter( - (record) => - parseFloat(record[dataType]) > 44.26 && record[dataType] !== null - ); - await retrieveRecordsAndValidate(filter, expectedRecords, dataType); - - // filter: >= - filter.comparison_op = 'gte'; - expectedRecords = unfilteredRecords.filter( - (record) => - parseFloat(record[dataType]) >= 44.26 && record[dataType] !== null - ); - await retrieveRecordsAndValidate(filter, expectedRecords, dataType); - - // filter: < - filter.comparison_op = 'lt'; - expectedRecords = unfilteredRecords.filter( - (record) => - parseFloat(record[dataType]) < 44.26 && record[dataType] !== null - ); - await retrieveRecordsAndValidate(filter, expectedRecords, dataType); - - // filter: <= - filter.comparison_op = 'lte'; - expectedRecords = unfilteredRecords.filter( - (record) => - parseFloat(record[dataType]) <= 44.26 && record[dataType] !== null - ); - await retrieveRecordsAndValidate(filter, expectedRecords, dataType); + let filterList = [ + { comparison_op: 'eq', value: '33.3' }, + { comparison_op: 'neq', value: '33.3' }, + { comparison_op: 'null', value: '' }, + { comparison_op: 'notnull', value: '' }, + { comparison_op: 'gt', value: '44.26' }, + { comparison_op: 'gte', value: '44.26' }, + { comparison_op: 'lt', value: '44.26' }, + { comparison_op: 'lte', value: '44.26' }, + ]; + await verifyFilters('Decimal', columns[2].id, filterList); }); it('Type: Currency', async () => { - // filter types to be verified - // eq, neq, null, notnull, like, nlike, >, >=, <, <= - - const dataType = 'Currency'; - - const filter = { - fk_column_id: columns[3].id, - status: 'create', - logical_op: 'and', - comparison_op: 'eq', - value: '33.3', - }; - - let expectedRecords = unfilteredRecords.filter( - (record) => parseFloat(record[dataType]) === 33.3 - ); - await retrieveRecordsAndValidate(filter, expectedRecords, dataType); - - // filter: neq - filter.comparison_op = 'neq'; - expectedRecords = unfilteredRecords.filter( - (record) => parseFloat(record[dataType]) !== 33.3 - ); - await retrieveRecordsAndValidate(filter, expectedRecords, dataType); - - // filter: null - filter.comparison_op = 'null'; - expectedRecords = unfilteredRecords.filter( - (record) => record[dataType] === null - ); - await retrieveRecordsAndValidate(filter, expectedRecords, dataType); - - // filter: notnull - filter.comparison_op = 'notnull'; - expectedRecords = unfilteredRecords.filter( - (record) => record[dataType] !== null - ); - await retrieveRecordsAndValidate(filter, expectedRecords, dataType); - - // filter: > - filter.comparison_op = 'gt'; - filter.value = '44.26'; - expectedRecords = unfilteredRecords.filter( - (record) => - parseFloat(record[dataType]) > 44.26 && record[dataType] !== null - ); - await retrieveRecordsAndValidate(filter, expectedRecords, dataType); - - // filter: >= - filter.comparison_op = 'gte'; - expectedRecords = unfilteredRecords.filter( - (record) => - parseFloat(record[dataType]) >= 44.26 && record[dataType] !== null - ); - await retrieveRecordsAndValidate(filter, expectedRecords, dataType); - - // filter: < - filter.comparison_op = 'lt'; - expectedRecords = unfilteredRecords.filter( - (record) => - parseFloat(record[dataType]) < 44.26 && record[dataType] !== null - ); - await retrieveRecordsAndValidate(filter, expectedRecords, dataType); - - // filter: <= - filter.comparison_op = 'lte'; - expectedRecords = unfilteredRecords.filter( - (record) => - parseFloat(record[dataType]) <= 44.26 && record[dataType] !== null - ); - await retrieveRecordsAndValidate(filter, expectedRecords, dataType); + let filterList = [ + { comparison_op: 'eq', value: '33.3' }, + { comparison_op: 'neq', value: '33.3' }, + { comparison_op: 'null', value: '' }, + { comparison_op: 'notnull', value: '' }, + { comparison_op: 'gt', value: '44.26' }, + { comparison_op: 'gte', value: '44.26' }, + { comparison_op: 'lt', value: '44.26' }, + { comparison_op: 'lte', value: '44.26' }, + ]; + await verifyFilters('Decimal', columns[3].id, filterList); }); it('Type: Percent', async () => { - // filter types to be verified - // eq, neq, null, notnull, like, nlike, >, >=, <, <= - - const dataType = 'Percent'; - - const filter = { - fk_column_id: columns[4].id, - status: 'create', - logical_op: 'and', - comparison_op: 'eq', - value: '33', - }; - - let expectedRecords = unfilteredRecords.filter( - (record) => record[dataType] === 33 - ); - await retrieveRecordsAndValidate(filter, expectedRecords, dataType); - - // filter: neq - filter.comparison_op = 'neq'; - expectedRecords = unfilteredRecords.filter( - (record) => record[dataType] !== 33 - ); - await retrieveRecordsAndValidate(filter, expectedRecords, dataType); - - // filter: null - filter.comparison_op = 'null'; - expectedRecords = unfilteredRecords.filter( - (record) => record[dataType] === null - ); - await retrieveRecordsAndValidate(filter, expectedRecords, dataType); - - // filter: notnull - filter.comparison_op = 'notnull'; - expectedRecords = unfilteredRecords.filter( - (record) => record[dataType] !== null - ); - await retrieveRecordsAndValidate(filter, expectedRecords, dataType); - - // filter: > - filter.comparison_op = 'gt'; - filter.value = '44'; - expectedRecords = unfilteredRecords.filter( - (record) => record[dataType] > 44 && record[dataType] !== null - ); - await retrieveRecordsAndValidate(filter, expectedRecords, dataType); - - // filter: >= - filter.comparison_op = 'gte'; - expectedRecords = unfilteredRecords.filter( - (record) => record[dataType] >= 44 && record[dataType] !== null - ); - await retrieveRecordsAndValidate(filter, expectedRecords, dataType); - - // filter: < - filter.comparison_op = 'lt'; - expectedRecords = unfilteredRecords.filter( - (record) => record[dataType] < 44 && record[dataType] !== null - ); - await retrieveRecordsAndValidate(filter, expectedRecords, dataType); - - // filter: <= - filter.comparison_op = 'lte'; - expectedRecords = unfilteredRecords.filter( - (record) => record[dataType] <= 44 && record[dataType] !== null - ); - await retrieveRecordsAndValidate(filter, expectedRecords, dataType); + let filterList = [ + { comparison_op: 'eq', value: '33' }, + { comparison_op: 'neq', value: '33' }, + { comparison_op: 'null', value: '' }, + { comparison_op: 'notnull', value: '' }, + { comparison_op: 'gt', value: '44' }, + { comparison_op: 'gte', value: '44' }, + { comparison_op: 'lt', value: '44' }, + { comparison_op: 'lte', value: '44' }, + ]; + await verifyFilters('Percent', columns[4].id, filterList); }); it('Type: Rating', async () => { - // filter types to be verified - // eq, neq, null, notnull, like, nlike, >, >=, <, <= - - const dataType = 'Rating'; - - const filter = { - fk_column_id: columns[6].id, - status: 'create', - logical_op: 'and', - comparison_op: 'eq', - value: '3', - }; - - let expectedRecords = unfilteredRecords.filter( - (record) => record[dataType] === 3 - ); - await retrieveRecordsAndValidate(filter, expectedRecords, dataType); - - // filter: neq - filter.comparison_op = 'neq'; - expectedRecords = unfilteredRecords.filter( - (record) => record[dataType] !== 3 - ); - await retrieveRecordsAndValidate(filter, expectedRecords, dataType); - - // filter: null - filter.comparison_op = 'null'; - expectedRecords = unfilteredRecords.filter( - (record) => record[dataType] === null - ); - await retrieveRecordsAndValidate(filter, expectedRecords, dataType); - - // filter: notnull - filter.comparison_op = 'notnull'; - expectedRecords = unfilteredRecords.filter( - (record) => record[dataType] !== null - ); - await retrieveRecordsAndValidate(filter, expectedRecords, dataType); - - // filter: > - filter.comparison_op = 'gt'; - filter.value = '2'; - expectedRecords = unfilteredRecords.filter( - (record) => record[dataType] > 2 && record[dataType] !== null - ); - await retrieveRecordsAndValidate(filter, expectedRecords, dataType); - - // filter: >= - filter.comparison_op = 'gte'; - expectedRecords = unfilteredRecords.filter( - (record) => record[dataType] >= 2 && record[dataType] !== null - ); - await retrieveRecordsAndValidate(filter, expectedRecords, dataType); - - // filter: < - filter.comparison_op = 'lt'; - expectedRecords = unfilteredRecords.filter( - (record) => record[dataType] < 2 && record[dataType] !== null - ); - await retrieveRecordsAndValidate(filter, expectedRecords, dataType); - - // filter: <= - filter.comparison_op = 'lte'; - expectedRecords = unfilteredRecords.filter( - (record) => record[dataType] <= 2 && record[dataType] !== null - ); - await retrieveRecordsAndValidate(filter, expectedRecords, dataType); + let filterList = [ + { comparison_op: 'eq', value: '3' }, + { comparison_op: 'neq', value: '3' }, + { comparison_op: 'null', value: '' }, + { comparison_op: 'notnull', value: '' }, + { comparison_op: 'gt', value: '2' }, + { comparison_op: 'gte', value: '2' }, + { comparison_op: 'lt', value: '2' }, + { comparison_op: 'lte', value: '2' }, + ]; + await verifyFilters('Rating', columns[6].id, filterList); }); } From 2adb05879534b216eeedd62a5e3c2cab221ae129 Mon Sep 17 00:00:00 2001 From: Raju Udava <86527202+dstala@users.noreply.github.com> Date: Sat, 4 Feb 2023 12:48:53 +0530 Subject: [PATCH 5/7] test: filter for duration column Signed-off-by: Raju Udava <86527202+dstala@users.noreply.github.com> --- .../tests/unit/rest/tests/filter.test.ts | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/packages/nocodb/tests/unit/rest/tests/filter.test.ts b/packages/nocodb/tests/unit/rest/tests/filter.test.ts index cdcbd75b93..315fe226ab 100644 --- a/packages/nocodb/tests/unit/rest/tests/filter.test.ts +++ b/packages/nocodb/tests/unit/rest/tests/filter.test.ts @@ -25,7 +25,11 @@ async function retrieveRecordsAndValidate( ) { let expectedRecords = []; let toFloat = false; - if (['Number', 'Decimal', 'Currency', 'Percent', 'Rating'].includes(title)) { + if ( + ['Number', 'Decimal', 'Currency', 'Percent', 'Duration', 'Rating'].includes( + title + ) + ) { toFloat = true; } @@ -431,6 +435,20 @@ function filterNumberBased() { await verifyFilters('Percent', columns[4].id, filterList); }); + it('Type: Duration', async () => { + let filterList = [ + { comparison_op: 'eq', value: '10' }, + { comparison_op: 'neq', value: '10' }, + { comparison_op: 'null', value: '' }, + { comparison_op: 'notnull', value: '' }, + { comparison_op: 'gt', value: '50' }, + { comparison_op: 'gte', value: '50' }, + { comparison_op: 'lt', value: '50' }, + { comparison_op: 'lte', value: '50' }, + ]; + await verifyFilters('Duration', columns[5].id, filterList); + }); + it('Type: Rating', async () => { let filterList = [ { comparison_op: 'eq', value: '3' }, From f47dcc5ad764089213ce36d61ee9c152f33f372d Mon Sep 17 00:00:00 2001 From: Raju Udava <86527202+dstala@users.noreply.github.com> Date: Sat, 4 Feb 2023 16:32:39 +0530 Subject: [PATCH 6/7] test: select filters Signed-off-by: Raju Udava <86527202+dstala@users.noreply.github.com> --- packages/nocodb/tests/unit/factory/row.ts | 29 +++++ .../tests/unit/rest/tests/filter.test.ts | 109 ++++++++++++++++++ 2 files changed, 138 insertions(+) diff --git a/packages/nocodb/tests/unit/factory/row.ts b/packages/nocodb/tests/unit/factory/row.ts index 7ed81a64b4..4e4a656614 100644 --- a/packages/nocodb/tests/unit/factory/row.ts +++ b/packages/nocodb/tests/unit/factory/row.ts @@ -128,6 +128,31 @@ const rowMixedValue = (column: ColumnType, index: number) => { null, ]; + const singleSelect = [ + 'jan', + 'feb', + 'mar', + 'apr', + 'may', + 'jun', + 'jul', + 'aug', + 'sep', + 'oct', + 'nov', + 'dec', + null, + ]; + + const multiSelect = [ + 'jan,feb,mar', + 'apr,may,jun', + 'jul,aug,sep', + 'oct,nov,dec', + 'jan,feb,mar', + null, + ]; + switch (column.uidt) { case UITypes.Number: case UITypes.Percent: @@ -151,6 +176,10 @@ const rowMixedValue = (column: ColumnType, index: number) => { return '2020-01-01'; case UITypes.URL: return urls[index % urls.length]; + case UITypes.SingleSelect: + return singleSelect[index % singleSelect.length]; + case UITypes.MultiSelect: + return multiSelect[index % multiSelect.length]; default: return `test-${index}`; } diff --git a/packages/nocodb/tests/unit/rest/tests/filter.test.ts b/packages/nocodb/tests/unit/rest/tests/filter.test.ts index 315fe226ab..3c83a22d04 100644 --- a/packages/nocodb/tests/unit/rest/tests/filter.test.ts +++ b/packages/nocodb/tests/unit/rest/tests/filter.test.ts @@ -111,6 +111,32 @@ async function retrieveRecordsAndValidate( record[title] !== null ); break; + case 'anyof': + expectedRecords = unfilteredRecords.filter((record) => { + const values = filter.value.split(','); + const recordValue = record[title]?.split(','); + return values.some((value) => recordValue?.includes(value)); + }); + break; + case 'nanyof': + expectedRecords = unfilteredRecords.filter((record) => { + const values = filter.value.split(','); + const recordValue = record[title]?.split(','); + return !values.some((value) => recordValue?.includes(value)); + }); + break; + case 'allof': + expectedRecords = unfilteredRecords.filter((record) => { + const values = filter.value.split(','); + return values.every((value) => record[title]?.includes(value)); + }); + break; + case 'nallof': + expectedRecords = unfilteredRecords.filter((record) => { + const values = filter.value.split(','); + return !values.every((value) => record[title]?.includes(value)); + }); + break; } // retrieve filtered records @@ -464,7 +490,90 @@ function filterNumberBased() { }); } +function filterSelectBased() { + // prepare data for test cases + beforeEach(async function () { + context = await init(); + project = await createProject(context); + table = await createTable(context, project, { + table_name: 'selectBased', + title: 'selectBased', + columns: [ + { + column_name: 'Id', + title: 'Id', + uidt: UITypes.ID, + }, + { + column_name: 'SingleSelect', + title: 'SingleSelect', + uidt: UITypes.SingleSelect, + dtxp: "'jan','feb','mar', 'apr', 'may', 'jun', 'jul', 'aug', 'sep', 'oct', 'nov', 'dec'", + }, + { + column_name: 'MultiSelect', + title: 'MultiSelect', + uidt: UITypes.MultiSelect, + dtxp: "'jan','feb','mar', 'apr', 'may', 'jun', 'jul', 'aug', 'sep', 'oct', 'nov', 'dec'", + }, + ], + }); + + columns = await table.getColumns(); + + let rowAttributes = []; + for (let i = 0; i < 400; i++) { + let row = { + SingleSelect: rowMixedValue(columns[1], i), + MultiSelect: rowMixedValue(columns[2], i), + }; + rowAttributes.push(row); + } + + await createBulkRows(context, { + project, + table, + values: rowAttributes, + }); + unfilteredRecords = await listRow({ project, table }); + + // verify length of unfiltered records to be 400 + expect(unfilteredRecords.length).to.equal(400); + }); + + it('Type: Single select', async () => { + let filterList = [ + { comparison_op: 'eq', value: 'jan' }, + { comparison_op: 'neq', value: 'jan' }, + { comparison_op: 'null', value: '' }, + { comparison_op: 'notnull', value: '' }, + { comparison_op: 'like', value: 'j' }, + { comparison_op: 'nlike', value: 'j' }, + { comparison_op: 'anyof', value: 'jan,feb,mar' }, + { comparison_op: 'nanyof', value: 'jan,feb,mar' }, + ]; + await verifyFilters('SingleSelect', columns[1].id, filterList); + }); + + it('Type: Multi select', async () => { + let filterList = [ + { comparison_op: 'eq', value: 'jan,feb,mar' }, + { comparison_op: 'neq', value: 'jan,feb,mar' }, + { comparison_op: 'null', value: '' }, + { comparison_op: 'notnull', value: '' }, + { comparison_op: 'like', value: 'jan' }, + { comparison_op: 'nlike', value: 'jan' }, + { comparison_op: 'anyof', value: 'jan,feb,mar' }, + { comparison_op: 'nanyof', value: 'jan,feb,mar' }, + { comparison_op: 'allof', value: 'jan,feb,mar' }, + { comparison_op: 'nallof', value: 'jan,feb,mar' }, + ]; + await verifyFilters('MultiSelect', columns[2].id, filterList); + }); +} + export default function () { describe('Filter: Text based', filterTextBased); describe('Filter: Numerical', filterNumberBased); + describe('Filter: Select based', filterSelectBased); } From d40fdc9292a32b24c7ab6c07e4c2e3eee68fc869 Mon Sep 17 00:00:00 2001 From: Raju Udava <86527202+dstala@users.noreply.github.com> Date: Sat, 4 Feb 2023 16:36:13 +0530 Subject: [PATCH 7/7] test: enable UT for filter tests Signed-off-by: Raju Udava <86527202+dstala@users.noreply.github.com> --- packages/nocodb/tests/unit/rest/index.test.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/nocodb/tests/unit/rest/index.test.ts b/packages/nocodb/tests/unit/rest/index.test.ts index 8227a1b5e4..e7954e4a35 100644 --- a/packages/nocodb/tests/unit/rest/index.test.ts +++ b/packages/nocodb/tests/unit/rest/index.test.ts @@ -7,6 +7,7 @@ import tableTests from './tests/table.test'; import tableRowTests from './tests/tableRow.test'; import viewRowTests from './tests/viewRow.test'; import attachmentTests from './tests/attachment.test'; +import filterTest from './tests/filter.test'; function restTests() { authTests(); @@ -17,6 +18,7 @@ function restTests() { viewRowTests(); columnTypeSpecificTests(); attachmentTests(); + filterTest(); } export default function () {