Browse Source

refactor/Refactored all of the unit test to make all tests async, improved some factory, now test_meta database is dropped and created before running each tests, added unit tests for view rows and fixed Noco import order

pull/3358/head
Muhammed Mustafa 2 years ago
parent
commit
d01e0d28de
  1. 4
      packages/nocodb/package.json
  2. 36
      packages/nocodb/tests/unit/rest/index.test.ts
  3. 7
      packages/nocodb/tests/unit/rest/init/index.ts
  4. 149
      packages/nocodb/tests/unit/rest/tests/auth.test.ts
  5. 31
      packages/nocodb/tests/unit/rest/tests/factory/column.ts
  6. 6
      packages/nocodb/tests/unit/rest/tests/factory/project.ts
  7. 3
      packages/nocodb/tests/unit/rest/tests/factory/row.ts
  8. 18
      packages/nocodb/tests/unit/rest/tests/factory/table.ts
  9. 163
      packages/nocodb/tests/unit/rest/tests/project.test.ts
  10. 318
      packages/nocodb/tests/unit/rest/tests/table.test.ts
  11. 18
      packages/nocodb/tests/unit/rest/tests/tableRow.test.ts
  12. 537
      packages/nocodb/tests/unit/rest/tests/viewRow.test.ts

4
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", "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: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", "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 TS_NODE_PROJECT=./tests/unit/tsconfig.json mocha -r ts-node/register tests/unit/rest/index.test.ts --recursive --timeout 50000 --exit", "local:test:rest": "cross-env TS_NODE_PROJECT=./tests/unit/tsconfig.json mocha -r ts-node/register tests/unit/rest/index.test.ts --recursive --timeout 300000 --exit --delay",
"test:rest": "cross-env TS_NODE_PROJECT=./tests/unit/tsconfig.json mocha -r ts-node/register tests/unit/rest/index.test.ts --recursive --timeout 50000 --exit", "test:rest": "cross-env TS_NODE_PROJECT=./tests/unit/tsconfig.json mocha -r ts-node/register tests/unit/rest/index.test.ts --recursive --timeout 300000 --exit --delay",
"test1": "run-s build test:*", "test1": "run-s build test:*",
"test:lint": "tslint --project . && prettier \"src/**/*.ts\" --list-different", "test:lint": "tslint --project . && prettier \"src/**/*.ts\" --list-different",
"test:unit": "nyc --silent ava", "test:unit": "nyc --silent ava",

36
packages/nocodb/tests/unit/rest/index.test.ts

@ -4,13 +4,39 @@ import projectTests from './tests/project.test';
import tableTests from './tests/table.test'; import tableTests from './tests/table.test';
import tableRowTests from './tests/tableRow.test'; import tableRowTests from './tests/tableRow.test';
import viewRowTests from './tests/viewRow.test'; import viewRowTests from './tests/viewRow.test';
import knex from 'knex';
import { dbName } from './dbConfig';
process.env.NODE_ENV = 'test'; process.env.NODE_ENV = 'test';
process.env.TEST = 'test'; process.env.TEST = 'test';
process.env.NC_DISABLE_CACHE = 'true'; process.env.NC_DISABLE_CACHE = 'true';
authTests(); const setupTestMetaDb = async () => {
projectTests(); const knexClient = knex({
tableTests(); client: 'mysql2',
tableRowTests(); connection: {
viewRowTests(); host: 'localhost',
port: 3306,
user: 'root',
password: 'password',
},
});
try {
await knexClient.raw(`DROP DATABASE ${dbName}`);
} catch (e) {}
await knexClient.raw(`CREATE DATABASE ${dbName}`);
}
(async function() {
await setupTestMetaDb();
authTests();
projectTests();
tableTests();
tableRowTests();
viewRowTests();
run();
})();

7
packages/nocodb/tests/unit/rest/init/index.ts

@ -1,11 +1,12 @@
import { dbConfig, dbName, sakilaDbName } from '../dbConfig';
import express from 'express'; import express from 'express';
import knex from 'knex';
import { Noco } from '../../../../src/lib';
import { dbConfig, dbName, sakilaDbName } from '../dbConfig';
import cleanupMeta from './cleanupMeta'; import cleanupMeta from './cleanupMeta';
import {cleanUpSakila, resetAndSeedSakila} from './cleanupSakila'; import {cleanUpSakila, resetAndSeedSakila} from './cleanupSakila';
import { createUser } from '../tests/factory/user'; import { createUser } from '../tests/factory/user';
import knex from 'knex';
import Noco from '../../../../src/lib';
let server; let server;
const knexClient = knex(dbConfig); const knexClient = knex(dbConfig);

149
packages/nocodb/tests/unit/rest/tests/auth.test.ts

@ -11,178 +11,151 @@ function authTests() {
context = await init(); context = await init();
}); });
it('Signup with valid email', function (done) { it('Signup with valid email', async () => {
request(context.app) const response = await request(context.app)
.post('/api/v1/auth/user/signup') .post('/api/v1/auth/user/signup')
.send({ email: 'new@example.com', password: defaultUserArgs.password }) .send({ email: 'new@example.com', password: defaultUserArgs.password })
.expect(200, (err, res) => { .expect(200)
if (err) {
expect(res.status).to.equal(400); const token = response.body.token;
} else { expect(token).to.be.a('string');
const token = res.body.token;
expect(token).to.be.a('string');
}
done();
});
}); });
it('Signup with invalid email', (done) => { it('Signup with invalid email', async () => {
request(context.app) await request(context.app)
.post('/api/v1/auth/user/signup') .post('/api/v1/auth/user/signup')
.send({ email: 'test', password: defaultUserArgs.password }) .send({ email: 'test', password: defaultUserArgs.password })
.expect(400, done); .expect(400);
}); });
it('Signup with invalid passsword', (done) => { it('Signup with invalid passsword', async () => {
request(context.app) await request(context.app)
.post('/api/v1/auth/user/signup') .post('/api/v1/auth/user/signup')
.send({ email: defaultUserArgs.email, password: 'weakpass' }) .send({ email: defaultUserArgs.email, password: 'weakpass' })
.expect(400, done); .expect(400);
}); });
it('Signin with valid credentials', function (done) { it('Signin with valid credentials', async () => {
request(context.app) const response = await request(context.app)
.post('/api/v1/auth/user/signin') .post('/api/v1/auth/user/signin')
.send({ .send({
email: defaultUserArgs.email, email: defaultUserArgs.email,
password: defaultUserArgs.password, password: defaultUserArgs.password,
}) })
.expect(200, async function (err, res) { .expect(200);
if (err) { const token = response.body.token;
console.log(res.error); expect(token).to.be.a('string');
return done(err);
}
const token = res.body.token;
expect(token).to.be.a('string');
// todo: Verify token
done();
});
}); });
it('Signup without email and password', (done) => { it('Signup without email and password', async () => {
request(context.app) await request(context.app)
.post('/api/v1/auth/user/signin') .post('/api/v1/auth/user/signin')
// pass empty data in request // pass empty data in await request
.send({}) .send({})
.expect(400, done); .expect(400);
}); });
it('Signin with invalid credentials', function (done) { it('Signin with invalid credentials', async () => {
request(context.app) await request(context.app)
.post('/api/v1/auth/user/signin') .post('/api/v1/auth/user/signin')
.send({ email: 'abc@abc.com', password: defaultUserArgs.password }) .send({ email: 'abc@abc.com', password: defaultUserArgs.password })
.expect(400, done); .expect(400);
}); });
it('Signin with invalid password', function (done) { it('Signin with invalid password', async () => {
request(context.app) await request(context.app)
.post('/api/v1/auth/user/signin') .post('/api/v1/auth/user/signin')
.send({ email: defaultUserArgs.email, password: 'wrongPassword' }) .send({ email: defaultUserArgs.email, password: 'wrongPassword' })
.expect(400, done); .expect(400);
}); });
it('me without token', function (done) { it('me without token', async () => {
request(context.app) const response = await request(context.app)
.get('/api/v1/auth/user/me') .get('/api/v1/auth/user/me')
.unset('xc-auth') .unset('xc-auth')
.expect(200, (err, res) => { .expect(200);
if (err) {
console.log(err, res); if (!response.body?.roles?.guest) {
done(err); return new Error('User should be guest');
return; }
}
if (!res.body?.roles?.guest) {
done('User should be guest');
return;
}
done();
});
}); });
it('me with token', function (done) { it('me with token', async () => {
request(context.app) const response = await request(context.app)
.get('/api/v1/auth/user/me') .get('/api/v1/auth/user/me')
.set('xc-auth', context.token) .set('xc-auth', context.token)
.expect(200, function (err, res) { .expect(200);
if (err) {
return done(err); const email = response.body.email;
} expect(email).to.equal(defaultUserArgs.email);
const email = res.body.email;
expect(email).to.equal(defaultUserArgs.email);
done();
});
}); });
it('Forgot password with a non-existing email id', function (done) { it('Forgot password with a non-existing email id', async () => {
request(context.app) await request(context.app)
.post('/api/v1/auth/password/forgot') .post('/api/v1/auth/password/forgot')
.send({ email: 'nonexisting@email.com' }) .send({ email: 'nonexisting@email.com' })
.expect(400, done); .expect(400);
}); });
// todo: fix mailer issues // todo: fix mailer issues
// it('Forgot password with an existing email id', function () {}); // it('Forgot password with an existing email id', function () {});
it('Change password', function (done) { it('Change password', async () => {
request(context.app) await request(context.app)
.post('/api/v1/auth/password/change') .post('/api/v1/auth/password/change')
.set('xc-auth', context.token) .set('xc-auth', context.token)
.send({ .send({
currentPassword: defaultUserArgs.password, currentPassword: defaultUserArgs.password,
newPassword: 'NEW' + defaultUserArgs.password, newPassword: 'NEW' + defaultUserArgs.password,
}) })
.expect(200, done); .expect(200);
}); });
it('Change password - after logout', function (done) { it('Change password - after logout', async () => {
request(context.app) await request(context.app)
.post('/api/v1/auth/password/change') .post('/api/v1/auth/password/change')
.unset('xc-auth') .unset('xc-auth')
.send({ .send({
currentPassword: defaultUserArgs.password, currentPassword: defaultUserArgs.password,
newPassword: 'NEW' + defaultUserArgs.password, newPassword: 'NEW' + defaultUserArgs.password,
}) })
.expect(500, function (_err, _res) { .expect(401);
done();
});
}); });
// todo: // todo:
it('Reset Password with an invalid token', function (done) { it('Reset Password with an invalid token', async () => {
request(context.app) await request(context.app)
.post('/api/v1/auth/password/reset/someRandomValue') .post('/api/v1/auth/password/reset/someRandomValue')
.send({ email: defaultUserArgs.email }) .send({ email: defaultUserArgs.email })
.expect(400, done); .expect(400);
}); });
it('Email validate with an invalid token', function (done) { it('Email validate with an invalid token', async () => {
request(context.app) await request(context.app)
.post('/api/v1/auth/email/validate/someRandomValue') .post('/api/v1/auth/email/validate/someRandomValue')
.send({ email: defaultUserArgs.email }) .send({ email: defaultUserArgs.email })
.expect(400, done); .expect(400);
}); });
// todo: // todo:
// it('Email validate with a valid token', function (done) { // it('Email validate with a valid token', async () => {
// // request(context.app) // // await request(context.app)
// // .post('/auth/email/validate/someRandomValue') // // .post('/auth/email/validate/someRandomValue')
// // .send({email: EMAIL_ID}) // // .send({email: EMAIL_ID})
// // .expect(500, done); // // .expect(500, done);
// }); // });
// todo: // todo:
// it('Forgot password validate with a valid token', function (done) { // it('Forgot password validate with a valid token', async () => {
// // request(context.app) // // await request(context.app)
// // .post('/auth/token/validate/someRandomValue') // // .post('/auth/token/validate/someRandomValue')
// // .send({email: EMAIL_ID}) // // .send({email: EMAIL_ID})
// // .expect(500, done); // // .expect(500, done);
// }); // });
// todo: // todo:
// it('Reset Password with an valid token', function (done) { // it('Reset Password with an valid token', async () => {
// // request(context.app) // // await request(context.app)
// // .post('/auth/password/reset/someRandomValue') // // .post('/auth/password/reset/someRandomValue')
// // .send({password: 'anewpassword'}) // // .send({password: 'anewpassword'})
// // .expect(500, done); // // .expect(500, done);

31
packages/nocodb/tests/unit/rest/tests/factory/column.ts

@ -1,7 +1,12 @@
import { UITypes } from 'nocodb-sdk'; import { UITypes } from 'nocodb-sdk';
import request from 'supertest'; import request from 'supertest';
import Column from '../../../../../src/lib/models/Column'; import Column from '../../../../../src/lib/models/Column';
import FormViewColumn from '../../../../../src/lib/models/FormViewColumn';
import GalleryViewColumn from '../../../../../src/lib/models/GalleryViewColumn';
import GridViewColumn from '../../../../../src/lib/models/GridViewColumn';
import Model from '../../../../../src/lib/models/Model'; import Model from '../../../../../src/lib/models/Model';
import Project from '../../../../../src/lib/models/Project';
import View from '../../../../../src/lib/models/View';
const defaultColumns = [ const defaultColumns = [
{ {
@ -122,7 +127,7 @@ const createRollupColumn = async (
relatedTableName, relatedTableName,
relatedTableColumnTitle, relatedTableColumnTitle,
}: { }: {
project: any; project: Project;
title: string; title: string;
rollupFunction: string; rollupFunction: string;
table: Model; table: Model;
@ -130,9 +135,10 @@ const createRollupColumn = async (
relatedTableColumnTitle: string; relatedTableColumnTitle: string;
} }
) => { ) => {
const childBases = await project.getBases();
const childTable = await Model.getByIdOrName({ const childTable = await Model.getByIdOrName({
project_id: project.id, project_id: project.id,
base_id: project.bases[0].id, base_id: childBases[0].id!,
table_name: relatedTableName, table_name: relatedTableName,
}); });
const childTableColumns = await childTable.getColumns(); const childTableColumns = await childTable.getColumns();
@ -168,16 +174,17 @@ const createLookupColumn = async (
relatedTableName, relatedTableName,
relatedTableColumnTitle, relatedTableColumnTitle,
}: { }: {
project: any; project: Project;
title: string; title: string;
table: Model; table: Model;
relatedTableName: string; relatedTableName: string;
relatedTableColumnTitle: string; relatedTableColumnTitle: string;
} }
) => { ) => {
const childBases = await project.getBases();
const childTable = await Model.getByIdOrName({ const childTable = await Model.getByIdOrName({
project_id: project.id, project_id: project.id,
base_id: project.bases[0].id, base_id: childBases[0].id!,
table_name: relatedTableName, table_name: relatedTableName,
}); });
const childTableColumns = await childTable.getColumns(); const childTableColumns = await childTable.getColumns();
@ -234,10 +241,26 @@ const createLtarColumn = async (
return ltarColumn; return ltarColumn;
}; };
const updateViewColumn = async (context, {view, column, attr}: {column: Column, view: View, attr: any}) => {
const res = await request(context.app)
.patch(`/api/v1/db/meta/views/${view.id}/columns/${column.id}`)
.set('xc-auth', context.token)
.send({
...attr,
});
const updatedColumn: FormViewColumn | GridViewColumn | GalleryViewColumn = (await view.getColumns()).find(
(column) => column.id === column.id
)!;
return updatedColumn;
}
export { export {
defaultColumns, defaultColumns,
createColumn, createColumn,
createRollupColumn, createRollupColumn,
createLookupColumn, createLookupColumn,
createLtarColumn, createLtarColumn,
updateViewColumn
}; };

6
packages/nocodb/tests/unit/rest/tests/factory/project.ts

@ -1,4 +1,5 @@
import request from 'supertest'; import request from 'supertest';
import Project from '../../../../../src/lib/models/Project';
import { sakilaDbName } from '../../dbConfig'; import { sakilaDbName } from '../../dbConfig';
const externalProjectConfig = { const externalProjectConfig = {
@ -48,7 +49,8 @@ const createSakilaProject = async (context) => {
.set('xc-auth', context.token) .set('xc-auth', context.token)
.send(externalProjectConfig); .send(externalProjectConfig);
const project = response.body; const project: Project = await Project.getByTitleOrId(response.body.id);
return project; return project;
}; };
@ -58,7 +60,7 @@ const createProject = async (context, projectArgs = defaultProjectValue) => {
.set('xc-auth', context.token) .set('xc-auth', context.token)
.send(projectArgs); .send(projectArgs);
const project = response.body; const project: Project = await Project.getByTitleOrId(response.body.id);
return project; return project;
}; };

3
packages/nocodb/tests/unit/rest/tests/factory/row.ts

@ -46,9 +46,10 @@ const listRow = async ({
sortArr?: Sort[]; sortArr?: Sort[];
}; };
}) => { }) => {
const bases = await project.getBases();
const baseModel = await Model.getBaseModelSQL({ const baseModel = await Model.getBaseModelSQL({
id: table.id, id: table.id,
dbDriver: NcConnectionMgrv2.get(project.bases[0]), dbDriver: NcConnectionMgrv2.get(bases[0]!),
}); });
const ignorePagination = !options; const ignorePagination = !options;

18
packages/nocodb/tests/unit/rest/tests/factory/table.ts

@ -1,5 +1,6 @@
import request from 'supertest'; import request from 'supertest';
import Model from '../../../../../src/lib/models/Model'; import Model from '../../../../../src/lib/models/Model';
import Project from '../../../../../src/lib/models/Project';
import { defaultColumns } from './column'; import { defaultColumns } from './column';
const defaultTableValue = { const defaultTableValue = {
@ -18,12 +19,23 @@ const createTable = async (context, project, args = {}) => {
return table; return table;
}; };
const getTable = async ({project, name}: {project, name: string}) => { const getTable = async ({project, name}: {project: Project, name: string}) => {
const bases = await project.getBases();
return await Model.getByIdOrName({ return await Model.getByIdOrName({
project_id: project.id, project_id: project.id,
base_id: project.bases[0].id, base_id: bases[0].id!,
table_name: name, table_name: name,
}); });
} }
export { createTable, getTable }; const getAllTables = async ({project}: {project: Project}) => {
const bases = await project.getBases();
const tables = await Model.list({
project_id: project.id,
base_id: bases[0].id!,
});
return tables;
}
export { createTable, getTable, getAllTables };

163
packages/nocodb/tests/unit/rest/tests/project.test.ts

@ -1,9 +1,9 @@
import 'mocha'; import 'mocha';
import request from 'supertest'; import request from 'supertest';
import init from '../init/index';
import { createProject, createSharedBase } from './factory/project'; import { createProject, createSharedBase } from './factory/project';
import { beforeEach } from 'mocha'; import { beforeEach } from 'mocha';
import { Exception } from 'handlebars'; import { Exception } from 'handlebars';
import init from '../init/index';
import Project from '../../../../src/lib/models/Project'; import Project from '../../../../src/lib/models/Project';
function projectTest() { function projectTest() {
@ -16,72 +16,64 @@ function projectTest() {
project = await createProject(context); project = await createProject(context);
}); });
it('Get project info', function (done) { it('Get project info', async () => {
request(context.app) await request(context.app)
.get(`/api/v1/db/meta/projects/${project.id}/info`) .get(`/api/v1/db/meta/projects/${project.id}/info`)
.set('xc-auth', context.token) .set('xc-auth', context.token)
.send({}) .send({})
.expect(200, done); .expect(200);
}); });
// todo: Test by creating models under project and check if the UCL is working // todo: Test by creating models under project and check if the UCL is working
it('UI ACL', (done) => { it('UI ACL', async () => {
request(context.app) await request(context.app)
.get(`/api/v1/db/meta/projects/${project.id}/visibility-rules`) .get(`/api/v1/db/meta/projects/${project.id}/visibility-rules`)
.set('xc-auth', context.token) .set('xc-auth', context.token)
.send({}) .send({})
.expect(200, done); .expect(200);
}); });
// todo: Test creating visibility set // todo: Test creating visibility set
it('List projects', function (done) { it('List projects', async () => {
request(context.app) const response = await request(context.app)
.get('/api/v1/db/meta/projects/') .get('/api/v1/db/meta/projects/')
.set('xc-auth', context.token) .set('xc-auth', context.token)
.send({}) .send({})
.expect(200, (err, res) => { .expect(200);
if (err) done(err);
else if (res.body.list.length !== 1) done('Should list only 1 project'); if (response.body.list.length !== 1) new Error('Should list only 1 project');
else if (!res.body.pageInfo) done('Should have pagination info'); if (!response.body.pageInfo) new Error('Should have pagination info');
else {
done();
}
});
}); });
it('Create project', function (done) { it('Create project', async () => {
request(context.app) const response = await request(context.app)
.post('/api/v1/db/meta/projects/') .post('/api/v1/db/meta/projects/')
.set('xc-auth', context.token) .set('xc-auth', context.token)
.send({ .send({
title: 'Title1', title: 'Title1',
}) })
.expect(200, async (err, res) => { .expect(200);
if (err) return done(err);
const newProject = await Project.getByTitleOrId(res.body.id);
if (!newProject) return done('Project not created');
done(); const newProject = await Project.getByTitleOrId(response.body.id);
}); if (!newProject) return new Error('Project not created');
}); });
it('Create projects with existing title', function (done) { it('Create projects with existing title', async () => {
request(context.app) await request(context.app)
.post(`/api/v1/db/meta/projects/`) .post(`/api/v1/db/meta/projects/`)
.set('xc-auth', context.token) .set('xc-auth', context.token)
.send({ .send({
title: project.title, title: project.title,
}) })
.expect(400, done); .expect(400);
}); });
// todo: fix passport user role popluation bug // todo: fix passport user role popluation bug
// it('Delete project', async (done) => { // it('Delete project', async async () => {
// const toBeDeletedProject = await createProject(app, token, { // const toBeDeletedProject = await createProject(app, token, {
// title: 'deletedTitle', // title: 'deletedTitle',
// }); // });
// request(app) // await request(app)
// .delete('/api/v1/db/meta/projects/${toBeDeletedProject.id}') // .delete('/api/v1/db/meta/projects/${toBeDeletedProject.id}')
// .set('xc-auth', token) // .set('xc-auth', token)
// .send({ // .send({
@ -89,58 +81,48 @@ function projectTest() {
// }) // })
// .expect(200, async (err) => { // .expect(200, async (err) => {
// // console.log(res); // // console.log(res);
// if (err) return done(err); //
// const deletedProject = await Project.getByTitleOrId( // const deletedProject = await Project.getByTitleOrId(
// toBeDeletedProject.id // toBeDeletedProject.id
// ); // );
// if (deletedProject) return done('Project not delete'); // if (deletedProject) return new Error('Project not delete');
// done(); // new Error();
// }); // });
// }); // });
it('Read project', (done) => { it('Read project', async () => {
request(context.app) const response = await request(context.app)
.get(`/api/v1/db/meta/projects/${project.id}`) .get(`/api/v1/db/meta/projects/${project.id}`)
.set('xc-auth', context.token) .set('xc-auth', context.token)
.send() .send()
.expect(200, (err, res) => { .expect(200);
if (err) return done(err);
if (res.body.id !== project.id) return done('Got the wrong project');
done(); if (response.body.id !== project.id) return new Error('Got the wrong project');
});
}); });
it('Update projects', function (done) { it('Update projects', async () => {
request(context.app) await request(context.app)
.patch(`/api/v1/db/meta/projects/${project.id}`) .patch(`/api/v1/db/meta/projects/${project.id}`)
.set('xc-auth', context.token) .set('xc-auth', context.token)
.send({ .send({
title: 'NewTitle', title: 'NewTitle',
}) })
.expect(200, async (err) => { .expect(200);
if (err) {
done(err); const newProject = await Project.getByTitleOrId(project.id);
return; if (newProject.title !== 'NewTitle') {
} return new Error('Project not updated');
const newProject = await Project.getByTitleOrId(project.id); }
if (newProject.title !== 'NewTitle') { });
done('Project not updated');
return;
}
done();
});
});
it('Update projects with existing title', async function () { it('Update projects with existing title', async function () {
const newProject = await createProject(context, { const newProject = await createProject(context, {
title: 'NewTitle1', title: 'NewTitle1',
}); });
return await request(context.app)
await request(context.app)
.patch(`/api/v1/db/meta/projects/${project.id}`) .patch(`/api/v1/db/meta/projects/${project.id}`)
.set('xc-auth', context.token) .set('xc-auth', context.token)
.send({ .send({
@ -149,50 +131,42 @@ function projectTest() {
.expect(400); .expect(400);
}); });
it('Create project shared base', (done) => { it('Create project shared base', async () => {
request(context.app) await request(context.app)
.post(`/api/v1/db/meta/projects/${project.id}/shared`) .post(`/api/v1/db/meta/projects/${project.id}/shared`)
.set('xc-auth', context.token) .set('xc-auth', context.token)
.send({ .send({
roles: 'viewer', roles: 'viewer',
password: 'test', password: 'test',
}) })
.expect(200, async (err) => { .expect(200);
if (err) return done(err);
const updatedProject = await Project.getByTitleOrId(project.id);
if ( const updatedProject = await Project.getByTitleOrId(project.id);
!updatedProject.uuid ||
updatedProject.roles !== 'viewer' ||
updatedProject.password !== 'test'
) {
return done('Shared base not configured properly');
}
done(); if (
}); !updatedProject.uuid ||
updatedProject.roles !== 'viewer' ||
updatedProject.password !== 'test'
) {
return new Error('Shared base not configured properly');
}
}); });
it('Created project shared base should have only editor or viewer role', (done) => { it('Created project shared base should have only editor or viewer role', async () => {
request(context.app) await request(context.app)
.post(`/api/v1/db/meta/projects/${project.id}/shared`) .post(`/api/v1/db/meta/projects/${project.id}/shared`)
.set('xc-auth', context.token) .set('xc-auth', context.token)
.send({ .send({
roles: 'commenter', roles: 'commenter',
password: 'test', password: 'test',
}) })
.expect(200, async (err) => { .expect(200);
if (err) return done(err);
const updatedProject = await Project.getByTitleOrId(project.id);
if (updatedProject.roles === 'commenter') { const updatedProject = await Project.getByTitleOrId(project.id);
return done('Shared base not configured properly');
}
done(); if (updatedProject.roles === 'commenter') {
}); return new Error('Shared base not configured properly');
}
}); });
it('Updated project shared base should have only editor or viewer role', async () => { it('Updated project shared base should have only editor or viewer role', async () => {
@ -206,6 +180,7 @@ function projectTest() {
password: 'test', password: 'test',
}) })
.expect(200); .expect(200);
const updatedProject = await Project.getByTitleOrId(project.id); const updatedProject = await Project.getByTitleOrId(project.id);
if (updatedProject.roles === 'commenter') { if (updatedProject.roles === 'commenter') {
@ -262,29 +237,29 @@ function projectTest() {
// todo: Do compare api test // todo: Do compare api test
it('Meta diff sync', (done) => { it('Meta diff sync', async () => {
request(context.app) await request(context.app)
.get(`/api/v1/db/meta/projects/${project.id}/meta-diff`) .get(`/api/v1/db/meta/projects/${project.id}/meta-diff`)
.set('xc-auth', context.token) .set('xc-auth', context.token)
.send() .send()
.expect(200, done); .expect(200);
}); });
it('Meta diff sync', (done) => { it('Meta diff sync', async () => {
request(context.app) await request(context.app)
.post(`/api/v1/db/meta/projects/${project.id}/meta-diff`) .post(`/api/v1/db/meta/projects/${project.id}/meta-diff`)
.set('xc-auth', context.token) .set('xc-auth', context.token)
.send() .send()
.expect(200, done); .expect(200);
}); });
// todo: improve test. Check whether the all the actions are present in the response and correct as well // todo: improve test. Check whether the all the actions are present in the response and correct as well
it('Meta diff sync', (done) => { it('Meta diff sync', async () => {
request(context.app) await request(context.app)
.get(`/api/v1/db/meta/projects/${project.id}/audits`) .get(`/api/v1/db/meta/projects/${project.id}/audits`)
.set('xc-auth', context.token) .set('xc-auth', context.token)
.send() .send()
.expect(200, done); .expect(200);
}); });
} }

318
packages/nocodb/tests/unit/rest/tests/table.test.ts

@ -1,10 +1,10 @@
// import { expect } from 'chai'; // import { expect } from 'chai';
import 'mocha'; import 'mocha';
import request from 'supertest'; import request from 'supertest';
import { createTable } from './factory/table'; import init from '../init';
import { createTable, getAllTables } from './factory/table';
import { createProject } from './factory/project'; import { createProject } from './factory/project';
import { defaultColumns } from './factory/column'; import { defaultColumns } from './factory/column';
import init from '../init';
import Model from '../../../../src/lib/models/Model'; import Model from '../../../../src/lib/models/Model';
function tableTest() { function tableTest() {
@ -19,22 +19,18 @@ function tableTest() {
table = await createTable(context, project); table = await createTable(context, project);
}); });
it('Get table list', function (done) { it('Get table list', async function () {
request(context.app) const response = await request(context.app)
.get(`/api/v1/db/meta/projects/${project.id}/tables`) .get(`/api/v1/db/meta/projects/${project.id}/tables`)
.set('xc-auth', context.token) .set('xc-auth', context.token)
.send({}) .send({})
.expect(200, (err, res) => { .expect(200);
if (err) return done(err);
if (res.body.list.length !== 1) return done('Wrong number of tables'); if (response.body.list.length !== 1) return new Error('Wrong number of tables');
done();
});
}); });
it('Create table', function (done) { it('Create table', async function () {
request(context.app) const response = await request(context.app)
.post(`/api/v1/db/meta/projects/${project.id}/tables`) .post(`/api/v1/db/meta/projects/${project.id}/tables`)
.set('xc-auth', context.token) .set('xc-auth', context.token)
.send({ .send({
@ -42,35 +38,29 @@ function tableTest() {
title: 'new_title_2', title: 'new_title_2',
columns: defaultColumns, columns: defaultColumns,
}) })
.expect(200, async (err, res) => { .expect(200);
if (err) return done(err);
const tables = await getAllTables({ project });
const tables = await Model.list({ if (tables.length !== 2) {
project_id: project.id, return new Error('Tables is not be created');
base_id: project.bases[0].id, }
});
if (tables.length !== 2) { if (response.body.columns.length !== defaultColumns.length) {
return done('Tables is not be created'); return new Error('Columns not saved properly');
} }
if (res.body.columns.length !== defaultColumns.length) { if (
done('Columns not saved properly'); !(
} response.body.table_name.startsWith(project.prefix) &&
response.body.table_name.endsWith('table2')
if ( )
!( ) {
res.body.table_name.startsWith(project.prefix) && return new Error('table name not configured properly');
res.body.table_name.endsWith('table2') }
)
) {
done('table name not configured properly');
}
done();
});
}); });
it('Create table with no table name', function (done) { it('Create table with no table name', async function () {
request(context.app) const response = await request(context.app)
.post(`/api/v1/db/meta/projects/${project.id}/tables`) .post(`/api/v1/db/meta/projects/${project.id}/tables`)
.set('xc-auth', context.token) .set('xc-auth', context.token)
.send({ .send({
@ -78,35 +68,28 @@ function tableTest() {
title: 'new_title', title: 'new_title',
columns: defaultColumns, columns: defaultColumns,
}) })
.expect(400, async (err, res) => { .expect(400);
if (err) return done(err);
if (
if ( !response.text.includes(
!res.text.includes( 'Missing table name `table_name` property in request body'
'Missing table name `table_name` property in request body' )
) ) {
) { console.error(response.text);
console.error(res.text); return new Error('Wrong api response');
return done('Wrong api response'); }
}
const tables = await getAllTables({ project });
const tables = await Model.list({ if (tables.length !== 1) {
project_id: project.id, console.log(tables);
base_id: project.bases[0].id, return new Error(
}); `Tables should not be created, tables.length:${tables.length}`
if (tables.length !== 1) { );
console.log(tables); }
return done(
`Tables should not be created, tables.length:${tables.length}`
);
}
done();
});
}); });
it('Create table with same table name', function (done) { it('Create table with same table name', async function () {
request(context.app) const response = await request(context.app)
.post(`/api/v1/db/meta/projects/${project.id}/tables`) .post(`/api/v1/db/meta/projects/${project.id}/tables`)
.set('xc-auth', context.token) .set('xc-auth', context.token)
.send({ .send({
@ -114,28 +97,21 @@ function tableTest() {
title: 'New_title', title: 'New_title',
columns: defaultColumns, columns: defaultColumns,
}) })
.expect(400, async (err, res) => { .expect(400);
if (err) return done(err);
if (!response.text.includes('Duplicate table name')) {
if (!res.text.includes('Duplicate table name')) { console.error(response.text);
console.error(res.text); return new Error('Wrong api response');
return done('Wrong api response'); }
}
const tables = await getAllTables({ project });
const tables = await Model.list({ if (tables.length !== 1) {
project_id: project.id, return new Error('Tables should not be created');
base_id: project.bases[0].id, }
});
if (tables.length !== 1) {
return done('Tables should not be created');
}
done();
});
}); });
it('Create table with same title', function (done) { it('Create table with same title', async function () {
request(context.app) const response = await request(context.app)
.post(`/api/v1/db/meta/projects/${project.id}/tables`) .post(`/api/v1/db/meta/projects/${project.id}/tables`)
.set('xc-auth', context.token) .set('xc-auth', context.token)
.send({ .send({
@ -143,28 +119,21 @@ function tableTest() {
title: table.title, title: table.title,
columns: defaultColumns, columns: defaultColumns,
}) })
.expect(400, async (err, res) => { .expect(400);
if (err) return done(err);
if (!response.text.includes('Duplicate table alias')) {
if (!res.text.includes('Duplicate table alias')) { console.error(response.text);
console.error(res.text); return new Error('Wrong api response');
return done('Wrong api response'); }
}
const tables = await getAllTables({ project });
const tables = await Model.list({ if (tables.length !== 1) {
project_id: project.id, return new Error('Tables should not be created');
base_id: project.bases[0].id, }
});
if (tables.length !== 1) {
return done('Tables should not be created');
}
done();
});
}); });
it('Create table with title length more than the limit', function (done) { it('Create table with title length more than the limit', async function () {
request(context.app) const response = await request(context.app)
.post(`/api/v1/db/meta/projects/${project.id}/tables`) .post(`/api/v1/db/meta/projects/${project.id}/tables`)
.set('xc-auth', context.token) .set('xc-auth', context.token)
.send({ .send({
@ -172,28 +141,22 @@ function tableTest() {
title: 'new_title', title: 'new_title',
columns: defaultColumns, columns: defaultColumns,
}) })
.expect(400, async (err, res) => { .expect(400);
if (err) return done(err);
if (!response.text.includes('Table name exceeds ')) {
if (!res.text.includes('Table name exceeds ')) { console.error(response.text);
console.error(res.text); return new Error('Wrong api response');
return done('Wrong api response'); }
}
const tables = await getAllTables({ project });
const tables = await Model.list({ if (tables.length !== 1) {
project_id: project.id, return new Error('Tables should not be created');
base_id: project.bases[0].id, }
});
if (tables.length !== 1) {
return done('Tables should not be created');
}
done();
});
}); });
it('Create table with title having leading white space', function (done) { it('Create table with title having leading white space', async function () {
request(context.app) const response = await request(context.app)
.post(`/api/v1/db/meta/projects/${project.id}/tables`) .post(`/api/v1/db/meta/projects/${project.id}/tables`)
.set('xc-auth', context.token) .set('xc-auth', context.token)
.send({ .send({
@ -201,113 +164,90 @@ function tableTest() {
title: 'new_title', title: 'new_title',
columns: defaultColumns, columns: defaultColumns,
}) })
.expect(400, async (err, res) => { .expect(400);
if (err) return done(err);
if (
if ( !response.text.includes(
!res.text.includes( 'Leading or trailing whitespace not allowed in table names'
'Leading or trailing whitespace not allowed in table names' )
) ) {
) { console.error(response.text);
console.error(res.text); return new Error('Wrong api response');
return done('Wrong api response'); }
}
const tables = await getAllTables({ project });
const tables = await Model.list({ if (tables.length !== 1) {
project_id: project.id, return new Error('Tables should not be created');
base_id: project.bases[0].id, }
});
if (tables.length !== 1) {
return done('Tables should not be created');
}
done();
});
}); });
it('Update table', function (done) { it('Update table', async function () {
request(context.app) const response = await request(context.app)
.patch(`/api/v1/db/meta/tables/${table.id}`) .patch(`/api/v1/db/meta/tables/${table.id}`)
.set('xc-auth', context.token) .set('xc-auth', context.token)
.send({ .send({
project_id: project.id, project_id: project.id,
table_name: 'new_title', table_name: 'new_title',
}) })
.expect(200, async (err) => { .expect(200);
if (err) return done(err); const updatedTable = await Model.get(table.id);
const updatedTable = await Model.get(table.id);
if (!updatedTable.table_name.endsWith('new_title')) { if (!updatedTable.table_name.endsWith('new_title')) {
return done('Table was not updated'); return new Error('Table was not updated');
} }
done();
});
}); });
it('Delete table', function (done) { it('Delete table', async function () {
request(context.app) const response = await request(context.app)
.delete(`/api/v1/db/meta/tables/${table.id}`) .delete(`/api/v1/db/meta/tables/${table.id}`)
.set('xc-auth', context.token) .set('xc-auth', context.token)
.send({}) .send({})
.expect(200, async (err) => { .expect(200);
if (err) return done(err);
const tables = await Model.list({
project_id: project.id,
base_id: project.bases[0].id,
});
if (tables.length !== 0) { const tables = await getAllTables({ project });
return done('Table is not deleted');
}
done(); if (tables.length !== 0) {
}); return new Error('Table is not deleted');
}
}); });
// todo: Check the condtion where the table being deleted is being refered by multiple tables // todo: Check the condtion where the table being deleted is being refered by multiple tables
// todo: Check the if views are also deleted // todo: Check the if views are also deleted
it('Get table', function (done) { it('Get table', async function () {
request(context.app) const response = await request(context.app)
.get(`/api/v1/db/meta/tables/${table.id}`) .get(`/api/v1/db/meta/tables/${table.id}`)
.set('xc-auth', context.token) .set('xc-auth', context.token)
.send({}) .send({})
.expect(200, async (err, res) => { .expect(200);
if (err) return done(err);
if (res.body.id !== table.id) done('Wrong table');
done(); if (response.body.id !== table.id) new Error('Wrong table');
});
}); });
// todo: flaky test, order condition is sometimes not met // todo: flaky test, order condition is sometimes not met
it('Reorder table', function (done) { it('Reorder table', async function () {
const newOrder = table.order === 0 ? 1 : 0; const newOrder = table.order === 0 ? 1 : 0;
request(context.app) const response = await request(context.app)
.post(`/api/v1/db/meta/tables/${table.id}/reorder`) .post(`/api/v1/db/meta/tables/${table.id}/reorder`)
.set('xc-auth', context.token) .set('xc-auth', context.token)
.send({ .send({
order: newOrder, order: newOrder,
}) })
.expect(200, done); .expect(200);
// .expect(200, async (err) => { // .expect(200, async (err) => {
// if (err) return done(err); // if (err) return new Error(err);
// const updatedTable = await Model.get(table.id); // const updatedTable = await Model.get(table.id);
// console.log(Number(updatedTable.order), newOrder); // console.log(Number(updatedTable.order), newOrder);
// if (Number(updatedTable.order) !== newOrder) { // if (Number(updatedTable.order) !== newOrder) {
// return done('Reordering failed'); // return new Error('Reordering failed');
// } // }
// done(); // new Error();
// }); // });
}); });
} }
export default function () { export default async function () {
describe('Table', tableTest); describe('Table', tableTest);
} }

18
packages/nocodb/tests/unit/rest/tests/tableRow.test.ts

@ -1,6 +1,6 @@
import 'mocha'; import 'mocha';
import { createProject, createSakilaProject } from './factory/project';
import init from '../init'; import init from '../init';
import { createProject, createSakilaProject } from './factory/project';
import request from 'supertest'; import request from 'supertest';
import { ColumnType, UITypes } from 'nocodb-sdk'; import { ColumnType, UITypes } from 'nocodb-sdk';
import { import {
@ -1167,22 +1167,6 @@ function tableTest() {
} }
}); });
it('Delete table row', async function () {
const table = await createTable(context, project);
const row = await createRow(context, { project, table });
await request(context.app)
.delete(`/api/v1/db/data/noco/${project.id}/${table.id}/${row['Id']}`)
.set('xc-auth', context.token)
.expect(200);
const deleteRow = await getRow(context, {project, table, id: row['Id']});
if (deleteRow && Object.keys(deleteRow).length > 0) {
console.log(deleteRow);
throw new Error('Wrong delete');
}
});
it('Delete table row with foreign key contraint', async function () { it('Delete table row with foreign key contraint', async function () {
const table = await createTable(context, project); const table = await createTable(context, project);
const relatedTable = await createTable(context, project, { const relatedTable = await createTable(context, project, {

537
packages/nocodb/tests/unit/rest/tests/viewRow.test.ts

@ -1,6 +1,6 @@
import 'mocha'; import 'mocha';
import { createProject, createSakilaProject } from './factory/project';
import init from '../init'; import init from '../init';
import { createProject, createSakilaProject } from './factory/project';
import request from 'supertest'; import request from 'supertest';
import Project from '../../../../src/lib/models/Project'; import Project from '../../../../src/lib/models/Project';
import Model from '../../../../src/lib/models/Model'; import Model from '../../../../src/lib/models/Model';
@ -8,8 +8,11 @@ import { createTable, getTable } from './factory/table';
import View from '../../../../src/lib/models/View'; import View from '../../../../src/lib/models/View';
import { ColumnType, UITypes, ViewType, ViewTypes } from 'nocodb-sdk'; import { ColumnType, UITypes, ViewType, ViewTypes } from 'nocodb-sdk';
import { createView } from './factory/view'; import { createView } from './factory/view';
import { createLookupColumn, createRollupColumn } from './factory/column'; import { createColumn, createLookupColumn, createLtarColumn, createRollupColumn, updateViewColumn } from './factory/column';
import Audit from '../../../../src/lib/models/Audit'; import Audit from '../../../../src/lib/models/Audit';
import Column from '../../../../src/lib/models/Column';
import GalleryView from '../../../../src/lib/models/GalleryView';
import { createRelation, createRow, getOneRow, getRow } from './factory/row';
const isColumnsCorrectInResponse = (row, columns: ColumnType[]) => { const isColumnsCorrectInResponse = (row, columns: ColumnType[]) => {
const responseColumnsListStr = Object.keys(row).sort().join(','); const responseColumnsListStr = Object.keys(row).sort().join(',');
@ -33,7 +36,6 @@ function viewRowTests() {
beforeEach(async function () { beforeEach(async function () {
context = await init(); context = await init();
sakilaProject = await createSakilaProject(context); sakilaProject = await createSakilaProject(context);
project = await createProject(context); project = await createProject(context);
customerTable = await getTable({project: sakilaProject, name: 'customer'}) customerTable = await getTable({project: sakilaProject, name: 'customer'})
@ -609,13 +611,8 @@ function viewRowTests() {
it('Find one sorted data list with required columns grid', async function () { it('Find one sorted data list with required columns grid', async function () {
await testFindOneSortedDataWithRequiredColumns(ViewTypes.GRID); await testFindOneSortedDataWithRequiredColumns(ViewTypes.GRID);
}); });
const testFindOneSortedFilteredNestedFieldsDataWithRollup = async (viewType: ViewTypes) => { const testFindOneSortedFilteredNestedFieldsDataWithRollup = async (viewType: ViewTypes) => {
const view = await createView(context, {
title: 'View',
table: customerTable,
type: viewType
});
const rollupColumn = await createRollupColumn(context, { const rollupColumn = await createRollupColumn(context, {
project: sakilaProject, project: sakilaProject,
title: 'Number of rentals', title: 'Number of rentals',
@ -624,7 +621,18 @@ function viewRowTests() {
relatedTableName: 'rental', relatedTableName: 'rental',
relatedTableColumnTitle: 'RentalDate', relatedTableColumnTitle: 'RentalDate',
}); });
const view = await createView(context, {
title: 'View',
table: customerTable,
type: viewType
});
await updateViewColumn(context, {
column: rollupColumn,
view: view,
attr: {show: true},
})
const paymentListColumn = (await customerTable.getColumns()).find( const paymentListColumn = (await customerTable.getColumns()).find(
(c) => c.title === 'Payment List' (c) => c.title === 'Payment List'
); );
@ -693,7 +701,7 @@ function viewRowTests() {
.expect(200); .expect(200);
if (ascResponse.body[rollupColumn.title] !== 12) { if (ascResponse.body[rollupColumn.title] !== 12) {
console.log(ascResponse.body); console.log('response.body',ascResponse.body);
throw new Error('Wrong filter'); throw new Error('Wrong filter');
} }
@ -708,13 +716,518 @@ function viewRowTests() {
} }
// todo: gallery view doesnt seem to support rollup // todo: gallery view doesnt seem to support rollup
// it.only('Find one sorted filtered view with nested fields data list with a rollup column in customer table FORM', async function () { // it.only('Find one sorted filtered view with nested fields data list with a rollup column in customer table GALLERY', async function () {
// await testFindOneSortedFilteredNestedFieldsDataWithRollup(ViewTypes.GALLERY);
// });
// it('Find one sorted filtered view with nested fields data list with a rollup column in customer table FORM', async function () {
// await testFindOneSortedFilteredNestedFieldsDataWithRollup(ViewTypes.FORM); // await testFindOneSortedFilteredNestedFieldsDataWithRollup(ViewTypes.FORM);
// }); // });
it('Find one sorted filtered view with nested fields data list with a rollup column in customer table GRID', async function () { it('Find one view sorted filtered view with nested fields data list with a rollup column in customer table GRID', async function () {
await testFindOneSortedFilteredNestedFieldsDataWithRollup(ViewTypes.GRID); await testFindOneSortedFilteredNestedFieldsDataWithRollup(ViewTypes.GRID);
}); });
const testGroupDescSorted = async (viewType: ViewTypes) => {
const view = await createView(context, {
title: 'View',
table: customerTable,
type: viewType
});
const firstNameColumn = customerColumns.find(
(col) => col.title === 'FirstName'
);
const rollupColumn = await createRollupColumn(context, {
project: sakilaProject,
title: 'Rollup',
rollupFunction: 'count',
table: customerTable,
relatedTableName: 'rental',
relatedTableColumnTitle: 'RentalDate',
});
const visibleColumns = [firstNameColumn];
const sortInfo = `-FirstName, +${rollupColumn.title}`;
const response = await request(context.app)
.get(
`/api/v1/db/data/noco/${sakilaProject.id}/${customerTable.id}/views/${view.id}/groupby`
)
.set('xc-auth', context.token)
.query({
fields: visibleColumns.map((c) => c.title),
sort: sortInfo,
column_name: firstNameColumn.column_name,
})
.expect(200);
if (
response.body.list[4]['first_name'] !== 'WILLIE' ||
response.body.list[4]['count'] !== 2
)
throw new Error('Wrong groupby');
}
it('Groupby desc sorted and with rollup view data list with required columns GRID', async function () {
await testGroupDescSorted(ViewTypes.GRID);
});
it('Groupby desc sorted and with rollup view data list with required columns FORM', async function () {
await testGroupDescSorted(ViewTypes.FORM);
});
it('Groupby desc sorted and with rollup view data list with required columns GALLERY', async function () {
await testGroupDescSorted(ViewTypes.GALLERY);
});
const testGroupWithOffset = async (viewType: ViewTypes) => {
const view = await createView(context, {
title: 'View',
table: customerTable,
type: viewType
});
const firstNameColumn = customerColumns.find(
(col) => col.title === 'FirstName'
);
const rollupColumn = await createRollupColumn(context, {
project: sakilaProject,
title: 'Rollup',
rollupFunction: 'count',
table: customerTable,
relatedTableName: 'rental',
relatedTableColumnTitle: 'RentalDate',
});
const visibleColumns = [firstNameColumn];
const sortInfo = `-FirstName, +${rollupColumn.title}`;
const response = await request(context.app)
.get(
`/api/v1/db/data/noco/${sakilaProject.id}/${customerTable.id}/views/${view.id}/groupby`
)
.set('xc-auth', context.token)
.query({
fields: visibleColumns.map((c) => c.title),
sort: sortInfo,
column_name: firstNameColumn.column_name,
offset: 4,
})
.expect(200);
if (
response.body.list[0]['first_name'] !== 'WILLIE' ||
response.body.list[0]['count'] !== 2
)
throw new Error('Wrong groupby');
}
it('Groupby desc sorted and with rollup view data list with required columns GALLERY', async function () {
await testGroupWithOffset(ViewTypes.GALLERY);
});
it('Groupby desc sorted and with rollup view data list with required columns FORM', async function () {
await testGroupWithOffset(ViewTypes.FORM);
});
it('Groupby desc sorted and with rollup view data list with required columns GRID', async function () {
await testGroupWithOffset(ViewTypes.GRID);
});
const testCount = async (viewType: ViewTypes) => {
const view = await createView(context, {
title: 'View',
table: customerTable,
type: viewType
});
const response = await request(context.app)
.get(`/api/v1/db/data/noco/${sakilaProject.id}/${customerTable.id}/views/${view.id}/count`)
.set('xc-auth', context.token)
.expect(200);
if(response.body.count !== 599) {
throw new Error('Wrong count');
}
}
it('Count view data list with required columns GRID', async function () {
await testCount(ViewTypes.GRID);
});
it('Count view data list with required columns FORM', async function () {
await testCount(ViewTypes.FORM);
});
it('Count view data list with required columns GALLERY', async function () {
await testCount(ViewTypes.GALLERY);
});
const testReadViewRow = async (viewType: ViewTypes) => {
const view = await createView(context, {
title: 'View',
table: customerTable,
type: viewType
});
const listResponse = await request(context.app)
.get(`/api/v1/db/data/noco/${sakilaProject.id}/${customerTable.id}/views/${view.id}`)
.set('xc-auth', context.token)
.expect(200);
const row = listResponse.body.list[0];
const readResponse = await request(context.app)
.get(
`/api/v1/db/data/noco/${sakilaProject.id}/${customerTable.id}/views/${view.id}/${row['CustomerId']}`
)
.set('xc-auth', context.token)
.expect(200);
if (
row['CustomerId'] !== readResponse.body['CustomerId'] ||
row['FirstName'] !== readResponse.body['FirstName']
) {
throw new Error('Wrong read');
}
}
it('Read view row GALLERY', async function () {
await testReadViewRow(ViewTypes.GALLERY);
})
it('Read view row FORM', async function () {
await testReadViewRow(ViewTypes.FORM);
})
it('Read view row GRID', async function () {
await testReadViewRow(ViewTypes.GRID);
})
const testUpdateViewRow = async (viewType: ViewTypes) => {
const table = await createTable(context, project);
const row = await createRow(context, { project, table });
const view = await createView(context, {
title: 'View',
table: table,
type: viewType
});
const updateResponse = await request(context.app)
.patch(`/api/v1/db/data/noco/${project.id}/${table.id}/views/${view.id}/${row['Id']}`)
.set('xc-auth', context.token)
.send({
title: 'Updated',
})
.expect(200);
if (updateResponse.body['Title'] !== 'Updated') {
throw new Error('Wrong update');
}
}
it('Update view row GALLERY', async function () {
await testUpdateViewRow(ViewTypes.GALLERY);
})
it('Update view row GRID', async function () {
await testUpdateViewRow(ViewTypes.GRID);
})
it('Update view row FORM', async function () {
await testUpdateViewRow(ViewTypes.FORM);
})
const testUpdateViewRowWithValidationAndInvalidData = async (viewType: ViewTypes) => {
const table = await createTable(context, project);
const emailColumn = await createColumn(context, table, {
title: 'Email',
column_name: 'email',
uidt: UITypes.Email,
meta: {
validate: true,
},
});
const view = await createView(context, {
title: 'View',
table: table,
type: viewType
});
const row = await createRow(context, { project, table });
await request(context.app)
.patch(`/api/v1/db/data/noco/${project.id}/${table.id}/views/${view.id}/${row['Id']}`)
.set('xc-auth', context.token)
.send({
[emailColumn.column_name]: 'invalidemail',
})
.expect(400);
}
it('Update view row with validation and invalid data GALLERY', async function () {
await testUpdateViewRowWithValidationAndInvalidData(ViewTypes.GALLERY);
})
it('Update view row with validation and invalid data GRID', async function () {
await testUpdateViewRowWithValidationAndInvalidData(ViewTypes.GRID);
})
it('Update view row with validation and invalid data FORM', async function () {
await testUpdateViewRowWithValidationAndInvalidData(ViewTypes.FORM);
})
// todo: Test webhooks of before and after update
// todo: Test with form view
const testUpdateViewRowWithValidationAndValidData = async (viewType: ViewTypes) => {
const table = await createTable(context, project);
const emailColumn = await createColumn(context, table, {
title: 'Email',
column_name: 'email',
uidt: UITypes.Email,
meta: {
validate: true,
},
});
const view = await createView(context, {
title: 'View',
table: table,
type: viewType
});
const row = await createRow(context, { project, table });
const response = await request(context.app)
.patch(`/api/v1/db/data/noco/${project.id}/${table.id}/views/${view.id}/${row['Id']}`)
.set('xc-auth', context.token)
.send({
[emailColumn.column_name]: 'valid@example.com',
})
.expect(200);
const updatedRow = await getRow(
context,
{project,
table,
id: response.body['Id']}
);
if (updatedRow[emailColumn.title] !== 'valid@example.com') {
throw new Error('Wrong update');
}
}
it('Update view row with validation and valid data GALLERY', async function () {
await testUpdateViewRowWithValidationAndValidData(ViewTypes.GALLERY);
})
it('Update view row with validation and valid data GRID', async function () {
await testUpdateViewRowWithValidationAndValidData(ViewTypes.GRID);
})
it('Update view row with validation and valid data FORM', async function () {
await testUpdateViewRowWithValidationAndValidData(ViewTypes.FORM);
})
const testDeleteViewRow = async (viewType: ViewTypes) => {
const table = await createTable(context, project);
const row = await createRow(context, { project, table });
const view = await createView(context, {
title: 'View',
table: table,
type: viewType
});
await request(context.app)
.delete(`/api/v1/db/data/noco/${project.id}/${table.id}/views/${view.id}/${row['Id']}`)
.set('xc-auth', context.token)
.expect(200);
const deleteRow = await getRow(context, {project, table, id: row['Id']});
if (deleteRow && Object.keys(deleteRow).length > 0) {
console.log(deleteRow);
throw new Error('Wrong delete');
}
}
it('Delete view row GALLERY', async function () {
await testDeleteViewRow(ViewTypes.GALLERY);
})
it('Delete view row GRID', async function () {
await testDeleteViewRow(ViewTypes.GRID);
})
it('Delete view row FORM', async function () {
await testDeleteViewRow(ViewTypes.FORM);
})
const testDeleteViewRowWithForiegnKeyConstraint = async (viewType: ViewTypes) => {
const table = await createTable(context, project);
const relatedTable = await createTable(context, project, {
table_name: 'Table2',
title: 'Table2_Title',
});
const ltarColumn = await createLtarColumn(context, {
title: 'Ltar',
parentTable: table,
childTable: relatedTable,
type: 'hm',
});
const view = await createView(context, {
title: 'View',
table: table,
type: viewType
});
const row = await createRow(context, { project, table });
await createRelation(context, {
project,
table,
childTable: relatedTable,
column: ltarColumn,
type: 'hm',
rowId: row['Id'],
});
const response = await request(context.app)
.delete(`/api/v1/db/data/noco/${project.id}/${table.id}/views/${view.id}/${row['Id']}`)
.set('xc-auth', context.token)
.expect(200);
const deleteRow = await getRow(context, {project, table, id: row['Id']});
if (!deleteRow) {
throw new Error('Should not delete');
}
if (
!(response.body.message[0] as string).includes(
'is a LinkToAnotherRecord of'
)
) {
throw new Error('Should give ltar foreign key error');
}
}
it('Delete view row with ltar foreign key constraint GALLERY', async function () {
await testDeleteViewRowWithForiegnKeyConstraint(ViewTypes.GALLERY);
})
it('Delete view row with ltar foreign key constraint GRID', async function () {
await testDeleteViewRowWithForiegnKeyConstraint(ViewTypes.GRID);
})
it('Delete view row with ltar foreign key constraint FORM', async function () {
await testDeleteViewRowWithForiegnKeyConstraint(ViewTypes.FORM);
})
const testViewRowExists = async (viewType: ViewTypes) => {
const row = await getOneRow(context, {
project: sakilaProject,
table: customerTable,
});
const view = await createView(context, {
title: 'View',
table: customerTable,
type: viewType
});
const response = await request(context.app)
.get(
`/api/v1/db/data/noco/${sakilaProject.id}/${customerTable.id}/views/${view.id}/${row['CustomerId']}/exist`
)
.set('xc-auth', context.token)
.expect(200);
if (!response.body) {
throw new Error('Should exist');
}
}
it('Exist should be true view row when it exists GALLERY', async function () {
await testViewRowExists(ViewTypes.GALLERY);
});
it('Exist should be true view row when it exists GRID', async function () {
await testViewRowExists(ViewTypes.GRID);
})
it('Exist should be true view row when it exists FORM', async function () {
await testViewRowExists(ViewTypes.FORM);
})
const testViewRowNotExists = async (viewType: ViewTypes) => {
const view = await createView(context, {
title: 'View',
table: customerTable,
type: viewType
});
const response = await request(context.app)
.get(
`/api/v1/db/data/noco/${sakilaProject.id}/${customerTable.id}/views/${view.id}/invalid-id/exist`
)
.set('xc-auth', context.token)
.expect(200);
if (response.body) {
throw new Error('Should not exist');
}
}
it('Exist should be false view row when it does not exist GALLERY', async function () {
await testViewRowNotExists(ViewTypes.GALLERY);
})
it('Exist should be false view row when it does not exist GRID', async function () {
await testViewRowNotExists(ViewTypes.GRID);
})
it('Exist should be false view row when it does not exist FORM', async function () {
await testViewRowNotExists(ViewTypes.FORM);
})
it('Export csv GRID', async function () {
const view = await createView(context, {
title: 'View',
table: customerTable,
type: ViewTypes.GRID
});
const response = await request(context.app)
.get(`/api/v1/db/data/noco/${sakilaProject.id}/${customerTable.title}/views/${view.id}/export/csv`)
.set('xc-auth', context.token)
.expect(200);
if(!response['header']['content-disposition'].includes("View-export.csv")){
console.log(response['header']['content-disposition']);
throw new Error('Wrong file name');
}
if(!response.text){
throw new Error('Wrong export');
}
})
it('Export excel GRID', async function () {
const view = await createView(context, {
title: 'View',
table: customerTable,
type: ViewTypes.GRID
});
const response = await request(context.app)
.get(`/api/v1/db/data/noco/${sakilaProject.id}/${customerTable.title}/views/${view.id}/export/excel`)
.set('xc-auth', context.token)
.expect(200);
if(!response['header']['content-disposition'].includes("View-export.xlsx")){
console.log(response['header']['content-disposition']);
throw new Error('Wrong file name');
}
if(!response.text){
throw new Error('Wrong export');
}
})
} }
export default function () { export default function () {

Loading…
Cancel
Save