diff --git a/packages/nocodb/package.json b/packages/nocodb/package.json index 8145e0e1ef..97493a8f58 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 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 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 300000 --exit --delay", "test1": "run-s build test:*", "test:lint": "tslint --project . && prettier \"src/**/*.ts\" --list-different", "test:unit": "nyc --silent ava", diff --git a/packages/nocodb/tests/unit/rest/index.test.ts b/packages/nocodb/tests/unit/rest/index.test.ts index 786ad60eaa..850018aa27 100644 --- a/packages/nocodb/tests/unit/rest/index.test.ts +++ b/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 tableRowTests from './tests/tableRow.test'; import viewRowTests from './tests/viewRow.test'; +import knex from 'knex'; +import { dbName } from './dbConfig'; process.env.NODE_ENV = 'test'; process.env.TEST = 'test'; process.env.NC_DISABLE_CACHE = 'true'; -authTests(); -projectTests(); -tableTests(); -tableRowTests(); -viewRowTests(); \ No newline at end of file +const setupTestMetaDb = async () => { + const knexClient = knex({ + client: 'mysql2', + connection: { + 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(); +})(); \ No newline at end of file diff --git a/packages/nocodb/tests/unit/rest/init/index.ts b/packages/nocodb/tests/unit/rest/init/index.ts index 43d22c991a..1919fbf85b 100644 --- a/packages/nocodb/tests/unit/rest/init/index.ts +++ b/packages/nocodb/tests/unit/rest/init/index.ts @@ -1,11 +1,12 @@ -import { dbConfig, dbName, sakilaDbName } from '../dbConfig'; import express from 'express'; +import knex from 'knex'; +import { Noco } from '../../../../src/lib'; + +import { dbConfig, dbName, sakilaDbName } from '../dbConfig'; import cleanupMeta from './cleanupMeta'; import {cleanUpSakila, resetAndSeedSakila} from './cleanupSakila'; import { createUser } from '../tests/factory/user'; -import knex from 'knex'; -import Noco from '../../../../src/lib'; let server; const knexClient = knex(dbConfig); diff --git a/packages/nocodb/tests/unit/rest/tests/auth.test.ts b/packages/nocodb/tests/unit/rest/tests/auth.test.ts index 8d074d0a0a..09c7b9052b 100644 --- a/packages/nocodb/tests/unit/rest/tests/auth.test.ts +++ b/packages/nocodb/tests/unit/rest/tests/auth.test.ts @@ -11,178 +11,151 @@ function authTests() { context = await init(); }); - it('Signup with valid email', function (done) { - request(context.app) + it('Signup with valid email', async () => { + const response = await request(context.app) .post('/api/v1/auth/user/signup') .send({ email: 'new@example.com', password: defaultUserArgs.password }) - .expect(200, (err, res) => { - if (err) { - expect(res.status).to.equal(400); - } else { - const token = res.body.token; - expect(token).to.be.a('string'); - } - done(); - }); + .expect(200) + + const token = response.body.token; + expect(token).to.be.a('string'); }); - it('Signup with invalid email', (done) => { - request(context.app) + it('Signup with invalid email', async () => { + await request(context.app) .post('/api/v1/auth/user/signup') .send({ email: 'test', password: defaultUserArgs.password }) - .expect(400, done); + .expect(400); }); - it('Signup with invalid passsword', (done) => { - request(context.app) + it('Signup with invalid passsword', async () => { + await request(context.app) .post('/api/v1/auth/user/signup') .send({ email: defaultUserArgs.email, password: 'weakpass' }) - .expect(400, done); + .expect(400); }); - it('Signin with valid credentials', function (done) { - request(context.app) + it('Signin with valid credentials', async () => { + const response = await request(context.app) .post('/api/v1/auth/user/signin') .send({ email: defaultUserArgs.email, password: defaultUserArgs.password, }) - .expect(200, async function (err, res) { - if (err) { - console.log(res.error); - return done(err); - } - const token = res.body.token; - expect(token).to.be.a('string'); - // todo: Verify token - done(); - }); + .expect(200); + const token = response.body.token; + expect(token).to.be.a('string'); }); - it('Signup without email and password', (done) => { - request(context.app) + it('Signup without email and password', async () => { + await request(context.app) .post('/api/v1/auth/user/signin') - // pass empty data in request + // pass empty data in await request .send({}) - .expect(400, done); + .expect(400); }); - it('Signin with invalid credentials', function (done) { - request(context.app) + it('Signin with invalid credentials', async () => { + await request(context.app) .post('/api/v1/auth/user/signin') .send({ email: 'abc@abc.com', password: defaultUserArgs.password }) - .expect(400, done); + .expect(400); }); - it('Signin with invalid password', function (done) { - request(context.app) + it('Signin with invalid password', async () => { + await request(context.app) .post('/api/v1/auth/user/signin') .send({ email: defaultUserArgs.email, password: 'wrongPassword' }) - .expect(400, done); + .expect(400); }); - it('me without token', function (done) { - request(context.app) + it('me without token', async () => { + const response = await request(context.app) .get('/api/v1/auth/user/me') .unset('xc-auth') - .expect(200, (err, res) => { - if (err) { - console.log(err, res); - done(err); - return; - } - - if (!res.body?.roles?.guest) { - done('User should be guest'); - return; - } - - done(); - }); + .expect(200); + + if (!response.body?.roles?.guest) { + return new Error('User should be guest'); + } }); - it('me with token', function (done) { - request(context.app) + it('me with token', async () => { + const response = await request(context.app) .get('/api/v1/auth/user/me') .set('xc-auth', context.token) - .expect(200, function (err, res) { - if (err) { - return done(err); - } - const email = res.body.email; - expect(email).to.equal(defaultUserArgs.email); - done(); - }); + .expect(200); + + const email = response.body.email; + expect(email).to.equal(defaultUserArgs.email); }); - it('Forgot password with a non-existing email id', function (done) { - request(context.app) + it('Forgot password with a non-existing email id', async () => { + await request(context.app) .post('/api/v1/auth/password/forgot') .send({ email: 'nonexisting@email.com' }) - .expect(400, done); + .expect(400); }); // todo: fix mailer issues // it('Forgot password with an existing email id', function () {}); - it('Change password', function (done) { - request(context.app) + it('Change password', async () => { + await request(context.app) .post('/api/v1/auth/password/change') .set('xc-auth', context.token) .send({ currentPassword: defaultUserArgs.password, newPassword: 'NEW' + defaultUserArgs.password, }) - .expect(200, done); + .expect(200); }); - it('Change password - after logout', function (done) { - request(context.app) + it('Change password - after logout', async () => { + await request(context.app) .post('/api/v1/auth/password/change') .unset('xc-auth') .send({ currentPassword: defaultUserArgs.password, newPassword: 'NEW' + defaultUserArgs.password, }) - .expect(500, function (_err, _res) { - done(); - }); + .expect(401); }); // todo: - it('Reset Password with an invalid token', function (done) { - request(context.app) + it('Reset Password with an invalid token', async () => { + await request(context.app) .post('/api/v1/auth/password/reset/someRandomValue') .send({ email: defaultUserArgs.email }) - .expect(400, done); + .expect(400); }); - it('Email validate with an invalid token', function (done) { - request(context.app) + it('Email validate with an invalid token', async () => { + await request(context.app) .post('/api/v1/auth/email/validate/someRandomValue') .send({ email: defaultUserArgs.email }) - .expect(400, done); + .expect(400); }); // todo: - // it('Email validate with a valid token', function (done) { - // // request(context.app) + // it('Email validate with a valid token', async () => { + // // await request(context.app) // // .post('/auth/email/validate/someRandomValue') // // .send({email: EMAIL_ID}) // // .expect(500, done); // }); // todo: - // it('Forgot password validate with a valid token', function (done) { - // // request(context.app) + // it('Forgot password validate with a valid token', async () => { + // // await request(context.app) // // .post('/auth/token/validate/someRandomValue') // // .send({email: EMAIL_ID}) // // .expect(500, done); // }); // todo: - // it('Reset Password with an valid token', function (done) { - // // request(context.app) + // it('Reset Password with an valid token', async () => { + // // await request(context.app) // // .post('/auth/password/reset/someRandomValue') // // .send({password: 'anewpassword'}) // // .expect(500, done); diff --git a/packages/nocodb/tests/unit/rest/tests/factory/column.ts b/packages/nocodb/tests/unit/rest/tests/factory/column.ts index 345e0a9879..08b4b55cde 100644 --- a/packages/nocodb/tests/unit/rest/tests/factory/column.ts +++ b/packages/nocodb/tests/unit/rest/tests/factory/column.ts @@ -1,7 +1,12 @@ import { UITypes } from 'nocodb-sdk'; import request from 'supertest'; 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 Project from '../../../../../src/lib/models/Project'; +import View from '../../../../../src/lib/models/View'; const defaultColumns = [ { @@ -122,7 +127,7 @@ const createRollupColumn = async ( relatedTableName, relatedTableColumnTitle, }: { - project: any; + project: Project; title: string; rollupFunction: string; table: Model; @@ -130,9 +135,10 @@ const createRollupColumn = async ( relatedTableColumnTitle: string; } ) => { + const childBases = await project.getBases(); const childTable = await Model.getByIdOrName({ project_id: project.id, - base_id: project.bases[0].id, + base_id: childBases[0].id!, table_name: relatedTableName, }); const childTableColumns = await childTable.getColumns(); @@ -168,16 +174,17 @@ const createLookupColumn = async ( relatedTableName, relatedTableColumnTitle, }: { - project: any; + project: Project; title: string; table: Model; relatedTableName: string; relatedTableColumnTitle: string; } ) => { + const childBases = await project.getBases(); const childTable = await Model.getByIdOrName({ project_id: project.id, - base_id: project.bases[0].id, + base_id: childBases[0].id!, table_name: relatedTableName, }); const childTableColumns = await childTable.getColumns(); @@ -234,10 +241,26 @@ const createLtarColumn = async ( 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 { defaultColumns, createColumn, createRollupColumn, createLookupColumn, createLtarColumn, + updateViewColumn }; diff --git a/packages/nocodb/tests/unit/rest/tests/factory/project.ts b/packages/nocodb/tests/unit/rest/tests/factory/project.ts index 809f923057..92141b5352 100644 --- a/packages/nocodb/tests/unit/rest/tests/factory/project.ts +++ b/packages/nocodb/tests/unit/rest/tests/factory/project.ts @@ -1,4 +1,5 @@ import request from 'supertest'; +import Project from '../../../../../src/lib/models/Project'; import { sakilaDbName } from '../../dbConfig'; const externalProjectConfig = { @@ -48,7 +49,8 @@ const createSakilaProject = async (context) => { .set('xc-auth', context.token) .send(externalProjectConfig); - const project = response.body; + const project: Project = await Project.getByTitleOrId(response.body.id); + return project; }; @@ -58,7 +60,7 @@ const createProject = async (context, projectArgs = defaultProjectValue) => { .set('xc-auth', context.token) .send(projectArgs); - const project = response.body; + const project: Project = await Project.getByTitleOrId(response.body.id); return project; }; diff --git a/packages/nocodb/tests/unit/rest/tests/factory/row.ts b/packages/nocodb/tests/unit/rest/tests/factory/row.ts index c82c80eaaa..8f6d9dd920 100644 --- a/packages/nocodb/tests/unit/rest/tests/factory/row.ts +++ b/packages/nocodb/tests/unit/rest/tests/factory/row.ts @@ -46,9 +46,10 @@ const listRow = async ({ sortArr?: Sort[]; }; }) => { + const bases = await project.getBases(); const baseModel = await Model.getBaseModelSQL({ id: table.id, - dbDriver: NcConnectionMgrv2.get(project.bases[0]), + dbDriver: NcConnectionMgrv2.get(bases[0]!), }); const ignorePagination = !options; diff --git a/packages/nocodb/tests/unit/rest/tests/factory/table.ts b/packages/nocodb/tests/unit/rest/tests/factory/table.ts index 0addff789a..e5dd3bc747 100644 --- a/packages/nocodb/tests/unit/rest/tests/factory/table.ts +++ b/packages/nocodb/tests/unit/rest/tests/factory/table.ts @@ -1,5 +1,6 @@ import request from 'supertest'; import Model from '../../../../../src/lib/models/Model'; +import Project from '../../../../../src/lib/models/Project'; import { defaultColumns } from './column'; const defaultTableValue = { @@ -18,12 +19,23 @@ const createTable = async (context, project, args = {}) => { 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({ project_id: project.id, - base_id: project.bases[0].id, + base_id: bases[0].id!, 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 }; diff --git a/packages/nocodb/tests/unit/rest/tests/project.test.ts b/packages/nocodb/tests/unit/rest/tests/project.test.ts index f42e3b0bdc..0eed89e8f5 100644 --- a/packages/nocodb/tests/unit/rest/tests/project.test.ts +++ b/packages/nocodb/tests/unit/rest/tests/project.test.ts @@ -1,9 +1,9 @@ import 'mocha'; import request from 'supertest'; +import init from '../init/index'; import { createProject, createSharedBase } from './factory/project'; import { beforeEach } from 'mocha'; import { Exception } from 'handlebars'; -import init from '../init/index'; import Project from '../../../../src/lib/models/Project'; function projectTest() { @@ -16,72 +16,64 @@ function projectTest() { project = await createProject(context); }); - it('Get project info', function (done) { - request(context.app) + it('Get project info', async () => { + await request(context.app) .get(`/api/v1/db/meta/projects/${project.id}/info`) .set('xc-auth', context.token) .send({}) - .expect(200, done); + .expect(200); }); // todo: Test by creating models under project and check if the UCL is working - it('UI ACL', (done) => { - request(context.app) + it('UI ACL', async () => { + await request(context.app) .get(`/api/v1/db/meta/projects/${project.id}/visibility-rules`) .set('xc-auth', context.token) .send({}) - .expect(200, done); + .expect(200); }); // todo: Test creating visibility set - it('List projects', function (done) { - request(context.app) + it('List projects', async () => { + const response = await request(context.app) .get('/api/v1/db/meta/projects/') .set('xc-auth', context.token) .send({}) - .expect(200, (err, res) => { - if (err) done(err); - else if (res.body.list.length !== 1) done('Should list only 1 project'); - else if (!res.body.pageInfo) done('Should have pagination info'); - else { - done(); - } - }); + .expect(200); + + if (response.body.list.length !== 1) new Error('Should list only 1 project'); + if (!response.body.pageInfo) new Error('Should have pagination info'); }); - it('Create project', function (done) { - request(context.app) + it('Create project', async () => { + const response = await request(context.app) .post('/api/v1/db/meta/projects/') .set('xc-auth', context.token) .send({ title: 'Title1', }) - .expect(200, async (err, res) => { - if (err) return done(err); - - const newProject = await Project.getByTitleOrId(res.body.id); - if (!newProject) return done('Project not created'); + .expect(200); - 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) { - request(context.app) + it('Create projects with existing title', async () => { + await request(context.app) .post(`/api/v1/db/meta/projects/`) .set('xc-auth', context.token) .send({ title: project.title, }) - .expect(400, done); + .expect(400); }); // todo: fix passport user role popluation bug - // it('Delete project', async (done) => { + // it('Delete project', async async () => { // const toBeDeletedProject = await createProject(app, token, { // title: 'deletedTitle', // }); - // request(app) + // await request(app) // .delete('/api/v1/db/meta/projects/${toBeDeletedProject.id}') // .set('xc-auth', token) // .send({ @@ -89,58 +81,48 @@ function projectTest() { // }) // .expect(200, async (err) => { // // console.log(res); - // if (err) return done(err); + // // const deletedProject = await Project.getByTitleOrId( // toBeDeletedProject.id // ); - // if (deletedProject) return done('Project not delete'); + // if (deletedProject) return new Error('Project not delete'); - // done(); + // new Error(); // }); // }); - it('Read project', (done) => { - request(context.app) + it('Read project', async () => { + const response = await request(context.app) .get(`/api/v1/db/meta/projects/${project.id}`) .set('xc-auth', context.token) .send() - .expect(200, (err, res) => { - if (err) return done(err); - - if (res.body.id !== project.id) return done('Got the wrong project'); + .expect(200); - done(); - }); + if (response.body.id !== project.id) return new Error('Got the wrong project'); }); - it('Update projects', function (done) { - request(context.app) + it('Update projects', async () => { + await request(context.app) .patch(`/api/v1/db/meta/projects/${project.id}`) .set('xc-auth', context.token) .send({ title: 'NewTitle', }) - .expect(200, async (err) => { - if (err) { - done(err); - return; - } - const newProject = await Project.getByTitleOrId(project.id); - if (newProject.title !== 'NewTitle') { - done('Project not updated'); - return; - } - - done(); - }); - }); + .expect(200); + + const newProject = await Project.getByTitleOrId(project.id); + if (newProject.title !== 'NewTitle') { + return new Error('Project not updated'); + } + }); it('Update projects with existing title', async function () { const newProject = await createProject(context, { title: 'NewTitle1', }); - return await request(context.app) + + await request(context.app) .patch(`/api/v1/db/meta/projects/${project.id}`) .set('xc-auth', context.token) .send({ @@ -149,50 +131,42 @@ function projectTest() { .expect(400); }); - it('Create project shared base', (done) => { - request(context.app) + it('Create project shared base', async () => { + await request(context.app) .post(`/api/v1/db/meta/projects/${project.id}/shared`) .set('xc-auth', context.token) .send({ roles: 'viewer', password: 'test', }) - .expect(200, async (err) => { - if (err) return done(err); - - const updatedProject = await Project.getByTitleOrId(project.id); + .expect(200); - if ( - !updatedProject.uuid || - updatedProject.roles !== 'viewer' || - updatedProject.password !== 'test' - ) { - return done('Shared base not configured properly'); - } + const updatedProject = await Project.getByTitleOrId(project.id); - 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) => { - request(context.app) + it('Created project shared base should have only editor or viewer role', async () => { + await request(context.app) .post(`/api/v1/db/meta/projects/${project.id}/shared`) .set('xc-auth', context.token) .send({ roles: 'commenter', password: 'test', }) - .expect(200, async (err) => { - if (err) return done(err); - - const updatedProject = await Project.getByTitleOrId(project.id); + .expect(200); - if (updatedProject.roles === 'commenter') { - return done('Shared base not configured properly'); - } + const updatedProject = await Project.getByTitleOrId(project.id); - 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 () => { @@ -206,6 +180,7 @@ function projectTest() { password: 'test', }) .expect(200); + const updatedProject = await Project.getByTitleOrId(project.id); if (updatedProject.roles === 'commenter') { @@ -262,29 +237,29 @@ function projectTest() { // todo: Do compare api test - it('Meta diff sync', (done) => { - request(context.app) + it('Meta diff sync', async () => { + await request(context.app) .get(`/api/v1/db/meta/projects/${project.id}/meta-diff`) .set('xc-auth', context.token) .send() - .expect(200, done); + .expect(200); }); - it('Meta diff sync', (done) => { - request(context.app) + it('Meta diff sync', async () => { + await request(context.app) .post(`/api/v1/db/meta/projects/${project.id}/meta-diff`) .set('xc-auth', context.token) .send() - .expect(200, done); + .expect(200); }); // todo: improve test. Check whether the all the actions are present in the response and correct as well - it('Meta diff sync', (done) => { - request(context.app) + it('Meta diff sync', async () => { + await request(context.app) .get(`/api/v1/db/meta/projects/${project.id}/audits`) .set('xc-auth', context.token) .send() - .expect(200, done); + .expect(200); }); } diff --git a/packages/nocodb/tests/unit/rest/tests/table.test.ts b/packages/nocodb/tests/unit/rest/tests/table.test.ts index 02087ba5e6..a373e9a228 100644 --- a/packages/nocodb/tests/unit/rest/tests/table.test.ts +++ b/packages/nocodb/tests/unit/rest/tests/table.test.ts @@ -1,10 +1,10 @@ // import { expect } from 'chai'; import 'mocha'; 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 { defaultColumns } from './factory/column'; -import init from '../init'; import Model from '../../../../src/lib/models/Model'; function tableTest() { @@ -19,22 +19,18 @@ function tableTest() { table = await createTable(context, project); }); - it('Get table list', function (done) { - request(context.app) + it('Get table list', async function () { + const response = await request(context.app) .get(`/api/v1/db/meta/projects/${project.id}/tables`) .set('xc-auth', context.token) .send({}) - .expect(200, (err, res) => { - if (err) return done(err); + .expect(200); - if (res.body.list.length !== 1) return done('Wrong number of tables'); - - done(); - }); + if (response.body.list.length !== 1) return new Error('Wrong number of tables'); }); - it('Create table', function (done) { - request(context.app) + it('Create table', async function () { + const response = await request(context.app) .post(`/api/v1/db/meta/projects/${project.id}/tables`) .set('xc-auth', context.token) .send({ @@ -42,35 +38,29 @@ function tableTest() { title: 'new_title_2', columns: defaultColumns, }) - .expect(200, async (err, res) => { - if (err) return done(err); - - const tables = await Model.list({ - project_id: project.id, - base_id: project.bases[0].id, - }); - if (tables.length !== 2) { - return done('Tables is not be created'); - } - - if (res.body.columns.length !== defaultColumns.length) { - done('Columns not saved properly'); - } - - if ( - !( - res.body.table_name.startsWith(project.prefix) && - res.body.table_name.endsWith('table2') - ) - ) { - done('table name not configured properly'); - } - done(); - }); + .expect(200); + + const tables = await getAllTables({ project }); + if (tables.length !== 2) { + return new Error('Tables is not be created'); + } + + if (response.body.columns.length !== defaultColumns.length) { + return new Error('Columns not saved properly'); + } + + if ( + !( + response.body.table_name.startsWith(project.prefix) && + response.body.table_name.endsWith('table2') + ) + ) { + return new Error('table name not configured properly'); + } }); - it('Create table with no table name', function (done) { - request(context.app) + it('Create table with no table name', async function () { + const response = await request(context.app) .post(`/api/v1/db/meta/projects/${project.id}/tables`) .set('xc-auth', context.token) .send({ @@ -78,35 +68,28 @@ function tableTest() { title: 'new_title', columns: defaultColumns, }) - .expect(400, async (err, res) => { - if (err) return done(err); - - if ( - !res.text.includes( - 'Missing table name `table_name` property in request body' - ) - ) { - console.error(res.text); - return done('Wrong api response'); - } - - const tables = await Model.list({ - project_id: project.id, - base_id: project.bases[0].id, - }); - if (tables.length !== 1) { - console.log(tables); - return done( - `Tables should not be created, tables.length:${tables.length}` - ); - } - - done(); - }); + .expect(400); + + if ( + !response.text.includes( + 'Missing table name `table_name` property in request body' + ) + ) { + console.error(response.text); + return new Error('Wrong api response'); + } + + const tables = await getAllTables({ project }); + if (tables.length !== 1) { + console.log(tables); + return new Error( + `Tables should not be created, tables.length:${tables.length}` + ); + } }); - it('Create table with same table name', function (done) { - request(context.app) + it('Create table with same table name', async function () { + const response = await request(context.app) .post(`/api/v1/db/meta/projects/${project.id}/tables`) .set('xc-auth', context.token) .send({ @@ -114,28 +97,21 @@ function tableTest() { title: 'New_title', columns: defaultColumns, }) - .expect(400, async (err, res) => { - if (err) return done(err); - - if (!res.text.includes('Duplicate table name')) { - console.error(res.text); - return done('Wrong api response'); - } - - const tables = await Model.list({ - project_id: project.id, - base_id: project.bases[0].id, - }); - if (tables.length !== 1) { - return done('Tables should not be created'); - } - - done(); - }); + .expect(400); + + if (!response.text.includes('Duplicate table name')) { + console.error(response.text); + return new Error('Wrong api response'); + } + + const tables = await getAllTables({ project }); + if (tables.length !== 1) { + return new Error('Tables should not be created'); + } }); - it('Create table with same title', function (done) { - request(context.app) + it('Create table with same title', async function () { + const response = await request(context.app) .post(`/api/v1/db/meta/projects/${project.id}/tables`) .set('xc-auth', context.token) .send({ @@ -143,28 +119,21 @@ function tableTest() { title: table.title, columns: defaultColumns, }) - .expect(400, async (err, res) => { - if (err) return done(err); - - if (!res.text.includes('Duplicate table alias')) { - console.error(res.text); - return done('Wrong api response'); - } - - const tables = await Model.list({ - project_id: project.id, - base_id: project.bases[0].id, - }); - if (tables.length !== 1) { - return done('Tables should not be created'); - } - - done(); - }); + .expect(400); + + if (!response.text.includes('Duplicate table alias')) { + console.error(response.text); + return new Error('Wrong api response'); + } + + const tables = await getAllTables({ project }); + if (tables.length !== 1) { + return new Error('Tables should not be created'); + } }); - it('Create table with title length more than the limit', function (done) { - request(context.app) + it('Create table with title length more than the limit', async function () { + const response = await request(context.app) .post(`/api/v1/db/meta/projects/${project.id}/tables`) .set('xc-auth', context.token) .send({ @@ -172,28 +141,22 @@ function tableTest() { title: 'new_title', columns: defaultColumns, }) - .expect(400, async (err, res) => { - if (err) return done(err); - - if (!res.text.includes('Table name exceeds ')) { - console.error(res.text); - return done('Wrong api response'); - } - - const tables = await Model.list({ - project_id: project.id, - base_id: project.bases[0].id, - }); - if (tables.length !== 1) { - return done('Tables should not be created'); - } - - done(); - }); + .expect(400); + + if (!response.text.includes('Table name exceeds ')) { + console.error(response.text); + return new Error('Wrong api response'); + } + + const tables = await getAllTables({ project }); + if (tables.length !== 1) { + return new Error('Tables should not be created'); + } + }); - it('Create table with title having leading white space', function (done) { - request(context.app) + it('Create table with title having leading white space', async function () { + const response = await request(context.app) .post(`/api/v1/db/meta/projects/${project.id}/tables`) .set('xc-auth', context.token) .send({ @@ -201,113 +164,90 @@ function tableTest() { title: 'new_title', columns: defaultColumns, }) - .expect(400, async (err, res) => { - if (err) return done(err); - - if ( - !res.text.includes( - 'Leading or trailing whitespace not allowed in table names' - ) - ) { - console.error(res.text); - return done('Wrong api response'); - } - - const tables = await Model.list({ - project_id: project.id, - base_id: project.bases[0].id, - }); - if (tables.length !== 1) { - return done('Tables should not be created'); - } - - done(); - }); + .expect(400); + + if ( + !response.text.includes( + 'Leading or trailing whitespace not allowed in table names' + ) + ) { + console.error(response.text); + return new Error('Wrong api response'); + } + + const tables = await getAllTables({ project }); + if (tables.length !== 1) { + return new Error('Tables should not be created'); + } }); - it('Update table', function (done) { - request(context.app) + it('Update table', async function () { + const response = await request(context.app) .patch(`/api/v1/db/meta/tables/${table.id}`) .set('xc-auth', context.token) .send({ project_id: project.id, table_name: 'new_title', }) - .expect(200, async (err) => { - if (err) return done(err); - - const updatedTable = await Model.get(table.id); + .expect(200); + const updatedTable = await Model.get(table.id); - if (!updatedTable.table_name.endsWith('new_title')) { - return done('Table was not updated'); - } - - done(); - }); + if (!updatedTable.table_name.endsWith('new_title')) { + return new Error('Table was not updated'); + } }); - it('Delete table', function (done) { - request(context.app) + it('Delete table', async function () { + const response = await request(context.app) .delete(`/api/v1/db/meta/tables/${table.id}`) .set('xc-auth', context.token) .send({}) - .expect(200, async (err) => { - if (err) return done(err); - - const tables = await Model.list({ - project_id: project.id, - base_id: project.bases[0].id, - }); + .expect(200); - if (tables.length !== 0) { - return done('Table is not deleted'); - } + const tables = await getAllTables({ project }); - 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 if views are also deleted - it('Get table', function (done) { - request(context.app) + it('Get table', async function () { + const response = await request(context.app) .get(`/api/v1/db/meta/tables/${table.id}`) .set('xc-auth', context.token) .send({}) - .expect(200, async (err, res) => { - if (err) return done(err); - - if (res.body.id !== table.id) done('Wrong table'); + .expect(200); - done(); - }); + if (response.body.id !== table.id) new Error('Wrong table'); }); // 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; - request(context.app) + const response = await request(context.app) .post(`/api/v1/db/meta/tables/${table.id}/reorder`) .set('xc-auth', context.token) .send({ order: newOrder, }) - .expect(200, done); + .expect(200); // .expect(200, async (err) => { - // if (err) return done(err); + // if (err) return new Error(err); // const updatedTable = await Model.get(table.id); // console.log(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); } diff --git a/packages/nocodb/tests/unit/rest/tests/tableRow.test.ts b/packages/nocodb/tests/unit/rest/tests/tableRow.test.ts index 596130ccbe..78073f44ab 100644 --- a/packages/nocodb/tests/unit/rest/tests/tableRow.test.ts +++ b/packages/nocodb/tests/unit/rest/tests/tableRow.test.ts @@ -1,6 +1,6 @@ import 'mocha'; -import { createProject, createSakilaProject } from './factory/project'; import init from '../init'; +import { createProject, createSakilaProject } from './factory/project'; import request from 'supertest'; import { ColumnType, UITypes } from 'nocodb-sdk'; 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 () { const table = await createTable(context, project); const relatedTable = await createTable(context, project, { diff --git a/packages/nocodb/tests/unit/rest/tests/viewRow.test.ts b/packages/nocodb/tests/unit/rest/tests/viewRow.test.ts index 4a493e9469..36f2bc14d2 100644 --- a/packages/nocodb/tests/unit/rest/tests/viewRow.test.ts +++ b/packages/nocodb/tests/unit/rest/tests/viewRow.test.ts @@ -1,6 +1,6 @@ import 'mocha'; -import { createProject, createSakilaProject } from './factory/project'; import init from '../init'; +import { createProject, createSakilaProject } from './factory/project'; import request from 'supertest'; import Project from '../../../../src/lib/models/Project'; 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 { ColumnType, UITypes, ViewType, ViewTypes } from 'nocodb-sdk'; 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 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 responseColumnsListStr = Object.keys(row).sort().join(','); @@ -33,7 +36,6 @@ function viewRowTests() { beforeEach(async function () { context = await init(); - sakilaProject = await createSakilaProject(context); project = await createProject(context); 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 () { await testFindOneSortedDataWithRequiredColumns(ViewTypes.GRID); }); - + const testFindOneSortedFilteredNestedFieldsDataWithRollup = async (viewType: ViewTypes) => { - const view = await createView(context, { - title: 'View', - table: customerTable, - type: viewType - }); const rollupColumn = await createRollupColumn(context, { project: sakilaProject, title: 'Number of rentals', @@ -624,7 +621,18 @@ function viewRowTests() { relatedTableName: 'rental', 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( (c) => c.title === 'Payment List' ); @@ -693,7 +701,7 @@ function viewRowTests() { .expect(200); if (ascResponse.body[rollupColumn.title] !== 12) { - console.log(ascResponse.body); + console.log('response.body',ascResponse.body); throw new Error('Wrong filter'); } @@ -708,13 +716,518 @@ function viewRowTests() { } // 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); // }); - 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); }); + + 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 () {