diff --git a/packages/nocodb/package.json b/packages/nocodb/package.json index de36092d42..8da331f019 100644 --- a/packages/nocodb/package.json +++ b/packages/nocodb/package.json @@ -21,8 +21,8 @@ "local:test:graphql": "cross-env DATABASE_URL=mysql://root:password@localhost:3306/sakila TS_NODE_PROJECT=tsconfig.json mocha -r ts-node/register src/__tests__/graphql.test.ts --recursive --timeout 10000 --exit", "test:graphql": "cross-env TS_NODE_PROJECT=tsconfig.json mocha -r ts-node/register src/__tests__/graphql.test.ts --recursive --timeout 10000 --exit", "test:grpc": "cross-env TS_NODE_PROJECT=tsconfig.json mocha -r ts-node/register src/__tests__/grpc.test.ts --recursive --timeout 10000 --exit", - "local:test:rest": "cross-env mocha -r ts-node/register src/__tests__/unit/rest/index.test.ts --recursive --timeout 10000 --exit", - "test:rest": "cross-env mocha -r ts-node/register src/__tests__/unit/rest/index.test.ts --recursive --timeout 10000 --exit", + "local:test:rest": "cross-env TS_NODE_PROJECT=./src/__tests__/tsconfig.json mocha -r ts-node/register src/__tests__/unit/rest/index.test.ts --recursive --timeout 10000 --exit", + "test:rest": "cross-env TS_NODE_PROJECT=./src/__tests__/tsconfig.json mocha -r ts-node/register src/__tests__/unit/rest/index.test.ts --recursive --timeout 10000 --exit", "test1": "run-s build test:*", "test:lint": "tslint --project . && prettier \"src/**/*.ts\" --list-different", "test:unit": "nyc --silent ava", diff --git a/packages/nocodb/src/__tests__/tsconfig.json b/packages/nocodb/src/__tests__/tsconfig.json new file mode 100644 index 0000000000..dbeabd3d6a --- /dev/null +++ b/packages/nocodb/src/__tests__/tsconfig.json @@ -0,0 +1,70 @@ +{ + "compilerOptions": { + "skipLibCheck": true, + "composite": true, + "target": "es2017", + "outDir": "build/main", + "rootDir": "src", + "moduleResolution": "node", + "module": "commonjs", + "declaration": true, + "inlineSourceMap": true, + "esModuleInterop": true + /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */, + "allowJs": false, + // "strict": true /* Enable all strict type-checking options. */, + + /* Strict Type-Checking Options */ + // "noImplicitAny": true /* Raise error on expressions and declarations with an implied 'any' type. */, + // "strictNullChecks": true /* Enable strict null checks. */, + // "strictFunctionTypes": true /* Enable strict checking of function types. */, + // "strictPropertyInitialization": true /* Enable strict checking of property initialization in classes. */, + // "noImplicitThis": true /* Raise error on 'this' expressions with an implied 'any' type. */, + // "alwaysStrict": true /* Parse in strict mode and emit "use strict" for each source file. */, + "resolveJsonModule": true, + /* Additional Checks */ + "noUnusedLocals": false + /* Report errors on unused locals. */, + "noUnusedParameters": false + /* Report errors on unused parameters. */, + "noImplicitReturns": false + /* Report error when not all code paths in function return a value. */, + "noFallthroughCasesInSwitch": false + /* Report errors for fallthrough cases in switch statement. */, + /* Debugging Options */ + "traceResolution": false + /* Report module resolution log messages. */, + "listEmittedFiles": false + /* Print names of generated files part of the compilation. */, + "listFiles": false + /* Print names of files part of the compilation. */, + "pretty": true + /* Stylize errors and messages using color and context. */, + /* Experimental Options */ + // "experimentalDecorators": true /* Enables experimental support for ES7 decorators. */, + // "emitDecoratorMetadata": true /* Enables experimental support for emitting type metadata for decorators. */, + + "lib": [ + "es2017" + ], + "types": [ + "mocha", "node" + ], + "typeRoots": [ + "node_modules/@types", + "src/types" + ] + }, + "include": [ + "src/**/*.ts", +// "src/lib/xgene/migrations/*.js", + "src/**/*.json" + ], + "exclude": [ + "node_modules/**", + "node_modules", + "../../../xc-lib-private/**", + "../../../xc-lib-private" + ], + "compileOnSave": false +} diff --git a/packages/nocodb/src/__tests__/unit/rest/init/cleanupMeta.ts b/packages/nocodb/src/__tests__/unit/rest/init/cleanupMeta.ts index 30a5791616..44a195fe10 100644 --- a/packages/nocodb/src/__tests__/unit/rest/init/cleanupMeta.ts +++ b/packages/nocodb/src/__tests__/unit/rest/init/cleanupMeta.ts @@ -2,23 +2,25 @@ import Model from '../../../../lib/models/Model'; import Project from '../../../../lib/models/Project'; import { orderedMetaTables } from '../../../../lib/utils/globals'; -const dropTablesAllProjects = async (knexClient) => { +const dropTablesAllNonExternalProjects = async (knexClient) => { const projects = await Project.list({}); const userCreatedTableNames = []; await Promise.all( - projects.map(async (project) => { - await project.getBases(); - const base = project.bases && project.bases[0]; - if (!base) return; + projects + .filter((project) => project.is_meta) + .map(async (project) => { + await project.getBases(); + const base = project.bases && project.bases[0]; + if (!base) return; - const models = await Model.list({ - project_id: project.id, - base_id: base.id, - }); - models.forEach((model) => { - userCreatedTableNames.push(model.table_name); - }); - }) + const models = await Model.list({ + project_id: project.id, + base_id: base.id, + }); + models.forEach((model) => { + userCreatedTableNames.push(model.table_name); + }); + }) ); await Promise.all( @@ -40,7 +42,7 @@ const cleanupMetaTables = async (knexClient) => { export default async function (knexClient) { try { - await dropTablesAllProjects(knexClient); + await dropTablesAllNonExternalProjects(knexClient); await cleanupMetaTables(knexClient); } catch (e) { console.error('cleanupMeta', e); diff --git a/packages/nocodb/src/__tests__/unit/rest/tests/helpers/project.ts b/packages/nocodb/src/__tests__/unit/rest/tests/helpers/project.ts index 49613c4103..809f923057 100644 --- a/packages/nocodb/src/__tests__/unit/rest/tests/helpers/project.ts +++ b/packages/nocodb/src/__tests__/unit/rest/tests/helpers/project.ts @@ -42,7 +42,7 @@ const createSharedBase = async (app, token, project, sharedBaseArgs = {}) => { }); }; -const createExternalProject = async (context) => { +const createSakilaProject = async (context) => { const response = await request(context.app) .post('/api/v1/db/meta/projects/') .set('xc-auth', context.token) @@ -62,4 +62,4 @@ const createProject = async (context, projectArgs = defaultProjectValue) => { return project; }; -export { createProject, createSharedBase, createExternalProject }; +export { createProject, createSharedBase, createSakilaProject }; diff --git a/packages/nocodb/src/__tests__/unit/rest/tests/tableRow.test.ts b/packages/nocodb/src/__tests__/unit/rest/tests/tableRow.test.ts index 00e10ecf40..6d2e37b555 100644 --- a/packages/nocodb/src/__tests__/unit/rest/tests/tableRow.test.ts +++ b/packages/nocodb/src/__tests__/unit/rest/tests/tableRow.test.ts @@ -1,147 +1,181 @@ -// import { expect } from 'chai'; import 'mocha'; -import { createExternalProject } from './helpers/project'; +import { createSakilaProject } from './helpers/project'; import Model from '../../../../lib/models/Model'; import init from '../init'; +import request from 'supertest'; +import { ColumnType } from 'nocodb-sdk'; + +const isColumnsCorrectInResponse = (response, columns: ColumnType[]) => { + const responseColumnsListStr = Object.keys(response.body.list[0]) + .sort() + .join(','); + const customerColumnsListStr = columns + .map((c) => c.title) + .sort() + .join(','); + + return responseColumnsListStr === customerColumnsListStr; +}; function tableTest() { let context; let project; + let customerTable: Model; + let customerColumns; beforeEach(async function () { context = await init(); - project = await createExternalProject(context); - }); + project = await createSakilaProject(context); - // it('Get table data list', async function () { - // const rowCount = 10; - // await createRows(rowCount); - - // const response = await request(app) - // .get(`/api/v1/db/data/noco/${project.id}/${table.id}`) - // .set('xc-auth', token) - // .send({}) - // .expect(200); - - // if (response.body.list.length !== rowCount) { - // throw new Error('Wrong number of rows'); - // } - // }); - - // it('Get table data list with required columns', async function () { - // const rowCount = 10; - // await createRows(rowCount); - // const newTitleColumn = columns.find((col) => col.title === 'New Title'); - // const visibleColumns = [newTitleColumn.title]; - - // const response = await request(app) - // .get(`/api/v1/db/data/noco/${project.id}/${table.id}`) - // .set('xc-auth', token) - // .query({ - // fields: visibleColumns, - // }) - // .expect(200); - - // if (response.body.list.length !== rowCount) { - // throw new Error('Wrong number of rows'); - // } - - // const sameArrayContent = (a: Array, b: Array) => { - // return a.length === b.length && a.every((v, i) => v === b[i]); - // }; - - // if (!sameArrayContent(Object.keys(response.body.list[0]), visibleColumns)) { - // console.error(Object.keys(response.body.list[0]), visibleColumns); - // throw new Error('Wrong column value'); - // } - // }); - - // it('Get desc sorted table data list with required columns', async function () { - // const rowCount = 10; - // await createRows(rowCount); - // const newTitleColumn = columns.find((col) => col.title === 'New Title'); - // const visibleColumns = [newTitleColumn.title]; - // const sortInfo = [{ fk_column_id: newTitleColumn.id, direction: 'desc' }]; - - // const response = await request(app) - // .get(`/api/v1/db/data/noco/${project.id}/${table.id}`) - // .set('xc-auth', token) - // .query({ - // fields: visibleColumns, - // sortArrJson: JSON.stringify(sortInfo), - // }) - // .expect(200); - - // if (response.body.list.length !== rowCount) { - // throw new Error('Wrong number of rows'); - // } - - // const sameArrayContent = (a: Array, b: Array) => { - // return a.length === b.length && a.every((v, i) => v === b[i]); - // }; - - // if (!sameArrayContent(Object.keys(response.body.list[0]), visibleColumns)) { - // console.error(Object.keys(response.body.list[0]), visibleColumns); - // throw new Error('Wrong column value'); - // } - - // if ( - // response.body.list[0][newTitleColumn.title] !== 'test-9' || - // response.body.list[response.body.list.length - 1][ - // newTitleColumn.title - // ] !== 'test-0' - // ) { - // throw new Error('Wrong sort'); - // } - // }); - - // it('Get asc sorted table data list with required columns', async function () { - // const rowCount = 10; - // await createRows(rowCount); - // const newTitleColumn = columns.find((col) => col.title === 'New Title'); - // const visibleColumns = [newTitleColumn.title]; - // const sortInfo = [{ fk_column_id: newTitleColumn.id, direction: 'asc' }]; - - // const response = await request(app) - // .get(`/api/v1/db/data/noco/${project.id}/${table.id}`) - // .set('xc-auth', token) - // .query({ - // fields: visibleColumns, - // sortArrJson: JSON.stringify(sortInfo), - // }) - // .expect(200); - - // if (response.body.list.length !== rowCount) { - // throw new Error('Wrong number of rows'); - // } - - // const sameArrayContent = (a: Array, b: Array) => { - // return a.length === b.length && a.every((v, i) => v === b[i]); - // }; - - // if (!sameArrayContent(Object.keys(response.body.list[0]), visibleColumns)) { - // console.error(Object.keys(response.body.list[0]), visibleColumns); - // throw new Error('Wrong column value'); - // } - - // if ( - // response.body.list[0][newTitleColumn.title] !== 'test-0' || - // response.body.list[response.body.list.length - 1][ - // newTitleColumn.title - // ] !== 'test-9' - // ) { - // throw new Error('Wrong sort'); - // } - // }); - - it('Get actors', async () => { - const actorTable = await Model.getByIdOrName({ + customerTable = await Model.getByIdOrName({ project_id: project.id, base_id: project.bases[0].id, - table_name: 'actor', + table_name: 'customer', }); - console.log(actorTable); + customerColumns = await customerTable.getColumns(); + }); + + it('Get table data list', async function () { + const response = await request(context.app) + .get(`/api/v1/db/data/noco/${project.id}/${customerTable.id}`) + .set('xc-auth', context.token) + .send({}) + .expect(200); + + if (response.body.list.length !== 25) { + throw new Error('Wrong number of rows'); + } + + if (!isColumnsCorrectInResponse(response, customerColumns)) { + throw new Error('Wrong columns'); + } + }); + + it('Get table data list with required columns', async function () { + const requiredColumns = customerColumns.filter((_, index) => index < 3); + + const response = await request(context.app) + .get(`/api/v1/db/data/noco/${project.id}/${customerTable.id}`) + .set('xc-auth', context.token) + .query({ + fields: requiredColumns.map((c) => c.title), + }) + .expect(200); + + if (response.body.list.length !== 25) { + throw new Error('Wrong number of rows'); + } + + if (!isColumnsCorrectInResponse(response, requiredColumns)) { + throw new Error('Wrong columns'); + } + }); + + it('Get desc sorted table data list with required columns', async function () { + const firstNameColumn = customerColumns.find( + (col) => col.title === 'FirstName' + ); + const visibleColumns = [firstNameColumn]; + const sortInfo = [{ fk_column_id: firstNameColumn.id, direction: 'desc' }]; + + const response = await request(context.app) + .get(`/api/v1/db/data/noco/${project.id}/${customerTable.id}`) + .set('xc-auth', context.token) + .query({ + fields: visibleColumns.map((c) => c.title), + sortArrJson: JSON.stringify(sortInfo), + }) + .expect(200); + const pageInfo = response.body.pageInfo; + + if (response.body.list.length !== 25) { + throw new Error('Wrong number of rows'); + } + + if (!isColumnsCorrectInResponse(response, visibleColumns)) { + console.log(response.body.list); + throw new Error('Wrong columns'); + } + + if (response.body.list[0][firstNameColumn.title] !== 'ZACHARY') { + console.log(response.body.list); + throw new Error('Wrong sort'); + } + + const lastPageOffset = + Math.trunc(pageInfo.totalRows / pageInfo.pageSize) * pageInfo.pageSize; + const lastPageResponse = await request(context.app) + .get(`/api/v1/db/data/noco/${project.id}/${customerTable.id}`) + .set('xc-auth', context.token) + .query({ + fields: visibleColumns.map((c) => c.title), + sortArrJson: JSON.stringify(sortInfo), + offset: lastPageOffset, + }) + .expect(200); + + if ( + lastPageResponse.body.list[lastPageResponse.body.list.length - 1][ + firstNameColumn.title + ] !== 'AARON' + ) { + console.log(lastPageOffset, lastPageResponse.body.list); + throw new Error('Wrong sort on last page'); + } + }); + + it('Get asc sorted table data list with required columns', async function () { + const firstNameColumn = customerColumns.find( + (col) => col.title === 'FirstName' + ); + const visibleColumns = [firstNameColumn]; + const sortInfo = [{ fk_column_id: firstNameColumn.id, direction: 'asc' }]; + + const response = await request(context.app) + .get(`/api/v1/db/data/noco/${project.id}/${customerTable.id}`) + .set('xc-auth', context.token) + .query({ + fields: visibleColumns.map((c) => c.title), + sortArrJson: JSON.stringify(sortInfo), + }) + .expect(200); + const pageInfo = response.body.pageInfo; + + if (response.body.list.length !== 25) { + throw new Error('Wrong number of rows'); + } + + if (!isColumnsCorrectInResponse(response, visibleColumns)) { + console.log(response.body.list); + throw new Error('Wrong columns'); + } + + if (response.body.list[0][firstNameColumn.title] !== 'AARON') { + console.log(response.body.list); + throw new Error('Wrong sort'); + } + + const lastPageOffset = + Math.trunc(pageInfo.totalRows / pageInfo.pageSize) * pageInfo.pageSize; + const lastPageResponse = await request(context.app) + .get(`/api/v1/db/data/noco/${project.id}/${customerTable.id}`) + .set('xc-auth', context.token) + .query({ + fields: visibleColumns.map((c) => c.title), + sortArrJson: JSON.stringify(sortInfo), + offset: lastPageOffset, + }) + .expect(200); + + if ( + lastPageResponse.body.list[lastPageResponse.body.list.length - 1][ + firstNameColumn.title + ] !== 'ZACHARY' + ) { + console.log(lastPageOffset, lastPageResponse.body.list); + throw new Error('Wrong sort on last page'); + } }); }