mirror of https://github.com/nocodb/nocodb
Raju Udava
2 years ago
6 changed files with 651 additions and 20 deletions
@ -0,0 +1,131 @@ |
|||||||
|
import { ColumnType, UITypes } from 'nocodb-sdk'; |
||||||
|
|
||||||
|
const rowMixedValue = (column: ColumnType, index: number) => { |
||||||
|
// 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, 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 = [ |
||||||
|
'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, |
||||||
|
]; |
||||||
|
|
||||||
|
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: |
||||||
|
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: |
||||||
|
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]; |
||||||
|
case UITypes.SingleSelect: |
||||||
|
return singleSelect[index % singleSelect.length]; |
||||||
|
case UITypes.MultiSelect: |
||||||
|
return multiSelect[index % multiSelect.length]; |
||||||
|
default: |
||||||
|
return `test-${index}`; |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
export { rowMixedValue }; |
@ -0,0 +1,417 @@ |
|||||||
|
import { test } from '@playwright/test'; |
||||||
|
import { DashboardPage } from '../pages/Dashboard'; |
||||||
|
import setup from '../setup'; |
||||||
|
import { ToolbarPage } from '../pages/Dashboard/common/Toolbar'; |
||||||
|
import { UITypes } from 'nocodb-sdk'; |
||||||
|
import { Api } from 'nocodb-sdk'; |
||||||
|
import { rowMixedValue } from '../setup/xcdb-records'; |
||||||
|
|
||||||
|
let dashboard: DashboardPage, toolbar: ToolbarPage; |
||||||
|
let context: any; |
||||||
|
let api: Api<any>; |
||||||
|
let records = []; |
||||||
|
const skipList = { |
||||||
|
Number: ['is null', 'is not null', 'is blank', 'is not blank'], |
||||||
|
Decimal: ['is null', 'is not null', 'is blank', 'is not blank'], |
||||||
|
Percent: ['is null', 'is not null', 'is blank', 'is not blank'], |
||||||
|
Currency: ['is null', 'is not null', 'is blank', 'is not blank'], |
||||||
|
Rating: ['is null', 'is not null', 'is blank', 'is not blank'], |
||||||
|
}; |
||||||
|
|
||||||
|
// define validateRowArray function
|
||||||
|
async function validateRowArray(param) { |
||||||
|
const { rowCount } = param; |
||||||
|
await dashboard.grid.verifyTotalRowCount({ count: rowCount }); |
||||||
|
|
||||||
|
// const { sequence, length, totalRowCount, column } = param;
|
||||||
|
//
|
||||||
|
// await dashboard.grid.verifyTotalRowCount({ count: totalRowCount });
|
||||||
|
//
|
||||||
|
// for (let j = 0; j < length; j++) {
|
||||||
|
// await dashboard.grid.cell.verify({
|
||||||
|
// index: j,
|
||||||
|
// columnHeader: column,
|
||||||
|
// value: sequence,
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
} |
||||||
|
|
||||||
|
async function verifyFilter(param: { column: string; opType: string; value?: string; result: { rowCount: number } }) { |
||||||
|
// if opType was included in skip list, skip it
|
||||||
|
if (skipList[param.column]?.includes(param.opType)) { |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
await toolbar.clickFilter(); |
||||||
|
await toolbar.filter.add({ |
||||||
|
columnTitle: param.column, |
||||||
|
opType: param.opType, |
||||||
|
value: param.value, |
||||||
|
isLocallySaved: false, |
||||||
|
}); |
||||||
|
await toolbar.clickFilter(); |
||||||
|
|
||||||
|
// verify filtered rows
|
||||||
|
await validateRowArray({ |
||||||
|
rowCount: param.result.rowCount, |
||||||
|
}); |
||||||
|
|
||||||
|
// Reset filter
|
||||||
|
await toolbar.filter.reset(); |
||||||
|
} |
||||||
|
|
||||||
|
test.describe('Filter Tests: Numerical', () => { |
||||||
|
test.beforeEach(async ({ page }) => { |
||||||
|
context = await setup({ page }); |
||||||
|
dashboard = new DashboardPage(page, context.project); |
||||||
|
toolbar = dashboard.grid.toolbar; |
||||||
|
|
||||||
|
api = new Api({ |
||||||
|
baseURL: `http://localhost:8080/`, |
||||||
|
headers: { |
||||||
|
'xc-auth': context.token, |
||||||
|
}, |
||||||
|
}); |
||||||
|
|
||||||
|
const 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, |
||||||
|
}, |
||||||
|
]; |
||||||
|
|
||||||
|
try { |
||||||
|
const project = await api.project.read(context.project.id); |
||||||
|
const table = await api.base.tableCreate(context.project.id, project.bases?.[0].id, { |
||||||
|
table_name: 'numberBased', |
||||||
|
title: 'numberBased', |
||||||
|
columns: columns, |
||||||
|
}); |
||||||
|
|
||||||
|
const rowAttributes = []; |
||||||
|
for (let i = 0; i < 400; i++) { |
||||||
|
const 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 api.dbTableRow.bulkCreate('noco', context.project.id, table.id, rowAttributes); |
||||||
|
records = await api.dbTableRow.list('noco', context.project.id, table.id, { limit: 400 }); |
||||||
|
} catch (e) { |
||||||
|
console.error(e); |
||||||
|
} |
||||||
|
}); |
||||||
|
|
||||||
|
test('Filter: Number', async () => { |
||||||
|
// close 'Team & Auth' tab
|
||||||
|
await dashboard.closeTab({ title: 'Team & Auth' }); |
||||||
|
await dashboard.treeView.openTable({ title: 'numberBased' }); |
||||||
|
const dataType = 'Number'; |
||||||
|
|
||||||
|
const filterList = [ |
||||||
|
{ |
||||||
|
op: '=', |
||||||
|
value: '33', |
||||||
|
rowCount: records.list.filter(r => r[dataType] === 33).length, |
||||||
|
}, |
||||||
|
{ |
||||||
|
op: '!=', |
||||||
|
value: '33', |
||||||
|
rowCount: records.list.filter(r => r[dataType] !== 33).length, |
||||||
|
}, |
||||||
|
{ |
||||||
|
op: 'is null', |
||||||
|
value: '', |
||||||
|
rowCount: records.list.filter(r => r[dataType] === null).length, |
||||||
|
}, |
||||||
|
{ |
||||||
|
op: 'is not null', |
||||||
|
value: '', |
||||||
|
rowCount: records.list.filter(r => r[dataType] !== null).length, |
||||||
|
}, |
||||||
|
{ |
||||||
|
op: 'is blank', |
||||||
|
value: '', |
||||||
|
rowCount: records.list.filter(r => r[dataType] === null).length, |
||||||
|
}, |
||||||
|
{ |
||||||
|
op: 'is not blank', |
||||||
|
value: '', |
||||||
|
rowCount: records.list.filter(r => r[dataType] !== null).length, |
||||||
|
}, |
||||||
|
{ |
||||||
|
op: '>', |
||||||
|
value: '44', |
||||||
|
rowCount: records.list.filter(r => r[dataType] > 44 && r[dataType] != null).length, |
||||||
|
}, |
||||||
|
{ |
||||||
|
op: '>=', |
||||||
|
value: '44', |
||||||
|
rowCount: records.list.filter(r => r[dataType] >= 44 && r[dataType] != null).length, |
||||||
|
}, |
||||||
|
{ |
||||||
|
op: '<', |
||||||
|
value: '44', |
||||||
|
rowCount: records.list.filter(r => r[dataType] < 44 && r[dataType] != null).length, |
||||||
|
}, |
||||||
|
{ |
||||||
|
op: '<=', |
||||||
|
value: '44', |
||||||
|
rowCount: records.list.filter(r => r[dataType] <= 44 && r[dataType] != null).length, |
||||||
|
}, |
||||||
|
]; |
||||||
|
|
||||||
|
for (let i = 0; i < filterList.length; i++) { |
||||||
|
await verifyFilter({ |
||||||
|
column: dataType, |
||||||
|
opType: filterList[i].op, |
||||||
|
value: filterList[i].value, |
||||||
|
result: { rowCount: filterList[i].rowCount }, |
||||||
|
}); |
||||||
|
} |
||||||
|
}); |
||||||
|
|
||||||
|
test('Filter: Decimal', async () => { |
||||||
|
// close 'Team & Auth' tab
|
||||||
|
await dashboard.closeTab({ title: 'Team & Auth' }); |
||||||
|
await dashboard.treeView.openTable({ title: 'numberBased' }); |
||||||
|
const dataType = 'Decimal'; |
||||||
|
|
||||||
|
const filterList = [ |
||||||
|
{ |
||||||
|
op: '=', |
||||||
|
value: '33.3', |
||||||
|
rowCount: records.list.filter(r => r[dataType] === 33.3).length, |
||||||
|
}, |
||||||
|
{ |
||||||
|
op: '!=', |
||||||
|
value: '33.3', |
||||||
|
rowCount: records.list.filter(r => r[dataType] !== 33.3).length, |
||||||
|
}, |
||||||
|
{ |
||||||
|
op: 'is null', |
||||||
|
value: '', |
||||||
|
rowCount: records.list.filter(r => r[dataType] === null).length, |
||||||
|
}, |
||||||
|
{ |
||||||
|
op: 'is not null', |
||||||
|
value: '', |
||||||
|
rowCount: records.list.filter(r => r[dataType] !== null).length, |
||||||
|
}, |
||||||
|
{ |
||||||
|
op: 'is blank', |
||||||
|
value: '', |
||||||
|
rowCount: records.list.filter(r => r[dataType] === null).length, |
||||||
|
}, |
||||||
|
{ |
||||||
|
op: 'is not blank', |
||||||
|
value: '', |
||||||
|
rowCount: records.list.filter(r => r[dataType] !== null).length, |
||||||
|
}, |
||||||
|
{ |
||||||
|
op: '>', |
||||||
|
value: '44.26', |
||||||
|
rowCount: records.list.filter(r => r[dataType] > 44.26 && r[dataType] != null).length, |
||||||
|
}, |
||||||
|
{ |
||||||
|
op: '>=', |
||||||
|
value: '44.26', |
||||||
|
rowCount: records.list.filter(r => r[dataType] >= 44.26 && r[dataType] != null).length, |
||||||
|
}, |
||||||
|
{ |
||||||
|
op: '<', |
||||||
|
value: '44.26', |
||||||
|
rowCount: records.list.filter(r => r[dataType] < 44.26 && r[dataType] != null).length, |
||||||
|
}, |
||||||
|
{ |
||||||
|
op: '<=', |
||||||
|
value: '44.26', |
||||||
|
rowCount: records.list.filter(r => r[dataType] <= 44.26 && r[dataType] != null).length, |
||||||
|
}, |
||||||
|
]; |
||||||
|
|
||||||
|
for (let i = 0; i < filterList.length; i++) { |
||||||
|
await verifyFilter({ |
||||||
|
column: dataType, |
||||||
|
opType: filterList[i].op, |
||||||
|
value: filterList[i].value, |
||||||
|
result: { rowCount: filterList[i].rowCount }, |
||||||
|
}); |
||||||
|
} |
||||||
|
}); |
||||||
|
|
||||||
|
test('Filter: Percent', async () => { |
||||||
|
// close 'Team & Auth' tab
|
||||||
|
await dashboard.closeTab({ title: 'Team & Auth' }); |
||||||
|
await dashboard.treeView.openTable({ title: 'numberBased' }); |
||||||
|
const dataType = 'Percent'; |
||||||
|
|
||||||
|
const filterList = [ |
||||||
|
{ |
||||||
|
op: '=', |
||||||
|
value: '33', |
||||||
|
rowCount: records.list.filter(r => r[dataType] === 33).length, |
||||||
|
}, |
||||||
|
{ |
||||||
|
op: '!=', |
||||||
|
value: '33', |
||||||
|
rowCount: records.list.filter(r => r[dataType] !== 33).length, |
||||||
|
}, |
||||||
|
{ |
||||||
|
op: 'is null', |
||||||
|
value: '', |
||||||
|
rowCount: records.list.filter(r => r[dataType] === null).length, |
||||||
|
}, |
||||||
|
{ |
||||||
|
op: 'is not null', |
||||||
|
value: '', |
||||||
|
rowCount: records.list.filter(r => r[dataType] !== null).length, |
||||||
|
}, |
||||||
|
{ |
||||||
|
op: 'is blank', |
||||||
|
value: '', |
||||||
|
rowCount: records.list.filter(r => r[dataType] === null).length, |
||||||
|
}, |
||||||
|
{ |
||||||
|
op: 'is not blank', |
||||||
|
value: '', |
||||||
|
rowCount: records.list.filter(r => r[dataType] !== null).length, |
||||||
|
}, |
||||||
|
{ |
||||||
|
op: '>', |
||||||
|
value: '44', |
||||||
|
rowCount: records.list.filter(r => r[dataType] > 44 && r[dataType] != null).length, |
||||||
|
}, |
||||||
|
{ |
||||||
|
op: '>=', |
||||||
|
value: '44', |
||||||
|
rowCount: records.list.filter(r => r[dataType] >= 44 && r[dataType] != null).length, |
||||||
|
}, |
||||||
|
{ |
||||||
|
op: '<', |
||||||
|
value: '44', |
||||||
|
rowCount: records.list.filter(r => r[dataType] < 44 && r[dataType] != null).length, |
||||||
|
}, |
||||||
|
{ |
||||||
|
op: '<=', |
||||||
|
value: '44', |
||||||
|
rowCount: records.list.filter(r => r[dataType] <= 44 && r[dataType] != null).length, |
||||||
|
}, |
||||||
|
]; |
||||||
|
|
||||||
|
for (let i = 0; i < filterList.length; i++) { |
||||||
|
await verifyFilter({ |
||||||
|
column: dataType, |
||||||
|
opType: filterList[i].op, |
||||||
|
value: filterList[i].value, |
||||||
|
result: { rowCount: filterList[i].rowCount }, |
||||||
|
}); |
||||||
|
} |
||||||
|
}); |
||||||
|
|
||||||
|
test('Filter: Currency', async () => { |
||||||
|
// close 'Team & Auth' tab
|
||||||
|
await dashboard.closeTab({ title: 'Team & Auth' }); |
||||||
|
await dashboard.treeView.openTable({ title: 'numberBased' }); |
||||||
|
const dataType = 'Currency'; |
||||||
|
|
||||||
|
const filterList = [ |
||||||
|
{ |
||||||
|
op: '=', |
||||||
|
value: '33.3', |
||||||
|
rowCount: records.list.filter(r => r[dataType] === 33.3).length, |
||||||
|
}, |
||||||
|
{ |
||||||
|
op: '!=', |
||||||
|
value: '33.3', |
||||||
|
rowCount: records.list.filter(r => r[dataType] !== 33.3).length, |
||||||
|
}, |
||||||
|
{ |
||||||
|
op: 'is null', |
||||||
|
value: '', |
||||||
|
rowCount: records.list.filter(r => r[dataType] === null).length, |
||||||
|
}, |
||||||
|
{ |
||||||
|
op: 'is not null', |
||||||
|
value: '', |
||||||
|
rowCount: records.list.filter(r => r[dataType] !== null).length, |
||||||
|
}, |
||||||
|
{ |
||||||
|
op: 'is blank', |
||||||
|
value: '', |
||||||
|
rowCount: records.list.filter(r => r[dataType] === null).length, |
||||||
|
}, |
||||||
|
{ |
||||||
|
op: 'is not blank', |
||||||
|
value: '', |
||||||
|
rowCount: records.list.filter(r => r[dataType] !== null).length, |
||||||
|
}, |
||||||
|
{ |
||||||
|
op: '>', |
||||||
|
value: '44.26', |
||||||
|
rowCount: records.list.filter(r => r[dataType] > 44.26 && r[dataType] != null).length, |
||||||
|
}, |
||||||
|
{ |
||||||
|
op: '>=', |
||||||
|
value: '44.26', |
||||||
|
rowCount: records.list.filter(r => r[dataType] >= 44.26 && r[dataType] != null).length, |
||||||
|
}, |
||||||
|
{ |
||||||
|
op: '<', |
||||||
|
value: '44.26', |
||||||
|
rowCount: records.list.filter(r => r[dataType] < 44.26 && r[dataType] != null).length, |
||||||
|
}, |
||||||
|
{ |
||||||
|
op: '<=', |
||||||
|
value: '44.26', |
||||||
|
rowCount: records.list.filter(r => r[dataType] <= 44.26 && r[dataType] != null).length, |
||||||
|
}, |
||||||
|
]; |
||||||
|
|
||||||
|
for (let i = 0; i < filterList.length; i++) { |
||||||
|
await verifyFilter({ |
||||||
|
column: dataType, |
||||||
|
opType: filterList[i].op, |
||||||
|
value: filterList[i].value, |
||||||
|
result: { rowCount: filterList[i].rowCount }, |
||||||
|
}); |
||||||
|
} |
||||||
|
}); |
||||||
|
}); |
Loading…
Reference in new issue