mirror of https://github.com/nocodb/nocodb
Wing-Kam Wong
2 years ago
38 changed files with 6486 additions and 104 deletions
@ -0,0 +1,167 @@
|
||||
name: "Release : Executables" |
||||
|
||||
on: |
||||
# Triggered manually |
||||
workflow_dispatch: |
||||
inputs: |
||||
tag: |
||||
description: "Timely version" |
||||
required: true |
||||
# Triggered by release-nightly-dev.yml / release-pr.yml |
||||
workflow_call: |
||||
inputs: |
||||
tag: |
||||
description: "Timely version" |
||||
required: true |
||||
type: string |
||||
secrets: |
||||
NC_GITHUB_TOKEN: |
||||
required: true |
||||
jobs: |
||||
build-executables: |
||||
runs-on: ubuntu-latest |
||||
steps: |
||||
- uses: actions/checkout@v3 |
||||
with: |
||||
token: ${{ secrets.NC_GITHUB_TOKEN }} |
||||
repository: 'nocodb/nocodb-timely' |
||||
- name: Cache node modules |
||||
id: cache-npm |
||||
uses: actions/cache@v3 |
||||
env: |
||||
cache-name: cache-node-modules |
||||
with: |
||||
# npm cache files are stored in `~/.npm` on Linux/macOS |
||||
path: ~/.npm |
||||
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }} |
||||
restore-keys: | |
||||
${{ runner.os }}-build-${{ env.cache-name }}- |
||||
${{ runner.os }}-build- |
||||
${{ runner.os }}- |
||||
- name: Cache pkg modules |
||||
id: cache-pkg |
||||
uses: actions/cache@v3 |
||||
env: |
||||
cache-name: cache-pkg |
||||
with: |
||||
# pkg cache files are stored in `~/.pkg-cache` |
||||
path: ~/.pkg-cache |
||||
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }} |
||||
restore-keys: | |
||||
${{ runner.os }}-build-${{ env.cache-name }}- |
||||
${{ runner.os }}-build- |
||||
${{ runner.os }}- |
||||
- name: Install QEMU and ldid |
||||
run: | |
||||
# Install qemu |
||||
sudo apt install qemu binfmt-support qemu-user-static |
||||
# install ldid |
||||
git clone https://github.com/daeken/ldid.git |
||||
cd ./ldid |
||||
./make.sh |
||||
sudo cp ./ldid /usr/local/bin |
||||
|
||||
- name: Update nocodb-timely |
||||
env: |
||||
TAG: ${{ github.event.inputs.tag || inputs.tag }} |
||||
run: | |
||||
npm i -E nocodb-daily@$TAG |
||||
|
||||
git config user.name 'github-actions[bot]' |
||||
git config user.email 'github-actions[bot]@users.noreply.github.com' |
||||
|
||||
git commit package.json -m "Update to $TAG" |
||||
git tag $TAG |
||||
git push --tags |
||||
|
||||
|
||||
- uses: actions/setup-node@v3 |
||||
with: |
||||
node-version: 16 |
||||
|
||||
- name : Install dependencies and build executables |
||||
run: | |
||||
# install npm dependendencies |
||||
npm i |
||||
|
||||
# Copy sqlite binaries |
||||
rsync -rvzhP ./binaries/binding/ ./node_modules/sqlite3/lib/binding/ |
||||
|
||||
# clean up code to optimize size |
||||
npx modclean --patterns="default:*" --ignore="nc-lib-gui-daily/**,dayjs/**,express-status-monitor/**,sqlite3/**" --run |
||||
|
||||
# build executables |
||||
npm run build |
||||
|
||||
mkdir ./mac-dist |
||||
mv ./dist/Noco-macos-arm64 ./mac-dist/ |
||||
mv ./dist/Noco-macos-x64 ./mac-dist/ |
||||
|
||||
# Compress executables |
||||
GZIP=-9 tar -czvf ./dist/Noco-linux-x64.tar.gz ./dist/Noco-linux-x64 |
||||
GZIP=-9 tar -czvf ./dist/Noco-win-x64.tar.gz ./dist/Noco-win-x64.exe |
||||
GZIP=-9 tar -czvf ./dist/Noco-linux-arm64.tar.gz ./dist/Noco-linux-arm64 |
||||
GZIP=-9 tar -czvf ./dist/Noco-win-arm64.tar.gz ./dist/Noco-win-arm64.exe |
||||
|
||||
- name: Upload executables(except mac executables) to release |
||||
uses: svenstaro/upload-release-action@v2 |
||||
with: |
||||
repo_token: ${{ secrets.NC_GITHUB_TOKEN }} |
||||
file: dist/** |
||||
tag: ${{ github.event.inputs.tag || inputs.tag }} |
||||
overwrite: true |
||||
file_glob: true |
||||
repo_name: nocodb/nocodb-timely |
||||
|
||||
- uses: actions/upload-artifact@master |
||||
with: |
||||
name: ${{ github.event.inputs.tag || inputs.tag }} |
||||
path: mac-dist |
||||
retention-days: 1 |
||||
|
||||
sign-mac-executables: |
||||
runs-on: macos-latest |
||||
needs: build-executables |
||||
steps: |
||||
|
||||
- uses: actions/download-artifact@master |
||||
with: |
||||
name: ${{ github.event.inputs.tag || inputs.tag }} |
||||
path: mac-dist |
||||
|
||||
- name: Sign macOS executables |
||||
run: | |
||||
/usr/bin/codesign --force -s - ./mac-dist/Noco-macos-arm64 -v |
||||
/usr/bin/codesign --force -s - ./mac-dist/Noco-macos-x64 -v |
||||
|
||||
- uses: actions/upload-artifact@master |
||||
with: |
||||
name: ${{ github.event.inputs.tag || inputs.tag }} |
||||
path: mac-dist |
||||
retention-days: 1 |
||||
|
||||
|
||||
publish-mac-executables: |
||||
needs: sign-mac-executables |
||||
runs-on: ubuntu-latest |
||||
steps: |
||||
- uses: actions/download-artifact@master |
||||
with: |
||||
name: ${{ github.event.inputs.tag || inputs.tag }} |
||||
path: mac-dist |
||||
- name: Compress files |
||||
run: | |
||||
GZIP=-9 tar -czvf ./mac-dist/Noco-macos-x64.tar.gz ./mac-dist/Noco-macos-x64 |
||||
GZIP=-9 tar -czvf ./mac-dist/Noco-macos-arm64.tar.gz ./mac-dist/Noco-macos-arm64 |
||||
|
||||
- name: Upload mac executables to release |
||||
uses: svenstaro/upload-release-action@v2 |
||||
with: |
||||
repo_token: ${{ secrets.NC_GITHUB_TOKEN }} |
||||
file: mac-dist/** |
||||
tag: ${{ github.event.inputs.tag || inputs.tag }} |
||||
overwrite: true |
||||
file_glob: true |
||||
repo_name: nocodb/nocodb-timely |
||||
|
||||
|
@ -0,0 +1,5 @@
|
||||
import crypto from 'crypto'; |
||||
|
||||
export function randomTokenString(): string { |
||||
return crypto.randomBytes(40).toString('hex'); |
||||
} |
@ -0,0 +1,23 @@
|
||||
## config.json |
||||
{ |
||||
"srcProject": "sample", |
||||
"dstProject": "sample-copy", |
||||
"baseURL": "http://localhost:8080", |
||||
"xc-auth": "Copy Auth Token" |
||||
} |
||||
- baseURL & xc-auth are common configurations for both import & export |
||||
|
||||
## Export |
||||
- `srcProject`: specify source project name to be exported. |
||||
- Export JSON file will be created as `srcProject.json` |
||||
- execute |
||||
`cd packages/nocodb/tests/export-import` |
||||
`node exportSchema.js` |
||||
|
||||
## Import |
||||
- `srcProject`: specify JSON file name to be imported (sans .JSON suffix) |
||||
- `dstProject`: new project name to be imported as |
||||
- Data will also be imported if `srcProject` exists in NocoDB. Note that, data import isn't via exported JSON |
||||
- execute |
||||
`cd packages/nocodb/tests/export-import` |
||||
`node importSchema.js` |
@ -0,0 +1,6 @@
|
||||
{ |
||||
"srcProject": "sample", |
||||
"dstProject": "sample-copy", |
||||
"baseURL": "http://localhost:8080", |
||||
"xc-auth": "Copy Auth Token" |
||||
} |
@ -0,0 +1,297 @@
|
||||
const Api = require('nocodb-sdk').Api; |
||||
const { UITypes } = require('nocodb-sdk'); |
||||
const jsonfile = require('jsonfile'); |
||||
|
||||
const GRID = 3, GALLERY = 2, FORM = 1; |
||||
|
||||
let ncMap = { /* id: name <string> */ }; |
||||
let tblSchema = []; |
||||
let api = {}; |
||||
let viewStore = { columns: {}, sort: {}, filter: {} }; |
||||
|
||||
let inputConfig = jsonfile.readFileSync(`config.json`) |
||||
let ncConfig = { |
||||
projectName: inputConfig.srcProject, |
||||
baseURL: inputConfig.baseURL, |
||||
headers: { |
||||
'xc-auth': `${inputConfig["xc-auth"]}` |
||||
} |
||||
}; |
||||
|
||||
|
||||
// helper routines
|
||||
// remove objects containing 0/ false/ null
|
||||
// fixme: how to handle when cdf (default value) is configured as 0/ null/ false
|
||||
function removeEmpty(obj) { |
||||
return Object.fromEntries( |
||||
Object.entries(obj) |
||||
.filter(([_, v]) => v != null && v != 0 && v != false) |
||||
.map(([k, v]) => [k, v === Object(v) ? removeEmpty(v) : v]) |
||||
); |
||||
} |
||||
|
||||
function addColumnSpecificData(c) { |
||||
// pick required fields to proceed further
|
||||
let col = removeEmpty( |
||||
(({ id, title, column_name, uidt, dt, pk, pv, rqd, dtxp, system }) => ({ |
||||
id, |
||||
title, |
||||
column_name, |
||||
uidt, |
||||
dt, |
||||
pk, |
||||
pv, |
||||
rqd, |
||||
dtxp, |
||||
system |
||||
}))(c) |
||||
); |
||||
|
||||
switch (c.uidt) { |
||||
case UITypes.Formula: |
||||
col.formula = c.colOptions.formula; |
||||
col.formula_raw = c.colOptions.formula_raw; |
||||
break; |
||||
case UITypes.LinkToAnotherRecord: |
||||
col[`colOptions`] = { |
||||
fk_model_id: c.fk_model_id, |
||||
fk_related_model_id: c.colOptions.fk_related_model_id, |
||||
fk_child_column_id: c.colOptions.fk_child_column_id, |
||||
fk_parent_column_id: c.colOptions.fk_parent_column_id, |
||||
type: c.colOptions.type |
||||
}; |
||||
break; |
||||
case UITypes.Lookup: |
||||
col[`colOptions`] = { |
||||
fk_model_id: c.fk_model_id, |
||||
fk_relation_column_id: c.colOptions.fk_relation_column_id, |
||||
fk_lookup_column_id: c.colOptions.fk_lookup_column_id |
||||
}; |
||||
break; |
||||
case UITypes.Rollup: |
||||
col[`colOptions`] = { |
||||
fk_model_id: c.fk_model_id, |
||||
fk_relation_column_id: c.colOptions.fk_relation_column_id, |
||||
fk_rollup_column_id: c.colOptions.fk_rollup_column_id, |
||||
rollup_function: c.colOptions.rollup_function |
||||
}; |
||||
break; |
||||
} |
||||
|
||||
return col; |
||||
} |
||||
|
||||
function addViewDetails(v) { |
||||
// pick required fields to proceed further
|
||||
let view = (({ id, title, type, show_system_fields, lock_type, order }) => ({ |
||||
id, |
||||
title, |
||||
type, |
||||
show_system_fields, |
||||
lock_type, |
||||
order |
||||
}))(v); |
||||
|
||||
// form view
|
||||
if (v.type === FORM) { |
||||
view.property = (({ |
||||
heading, |
||||
subheading, |
||||
success_msg, |
||||
redirect_after_secs, |
||||
email, |
||||
submit_another_form, |
||||
show_blank_form |
||||
}) => ({ |
||||
heading, |
||||
subheading, |
||||
success_msg, |
||||
redirect_after_secs, |
||||
email, |
||||
submit_another_form, |
||||
show_blank_form |
||||
}))(v.view); |
||||
} |
||||
|
||||
// gallery view
|
||||
else if (v.type === GALLERY) { |
||||
view.property = { |
||||
fk_cover_image_col_id: ncMap[v.view.fk_cover_image_col_id] |
||||
}; |
||||
} |
||||
|
||||
// gallery view doesn't share column information in api yet
|
||||
if (v.type !== GALLERY) { |
||||
if (v.type === GRID) |
||||
view.columns = viewStore.columns[v.id].map(a => |
||||
(({ id, width, order, show }) => ({ id, width, order, show }))(a) |
||||
); |
||||
if (v.type === FORM) |
||||
view.columns = viewStore.columns[v.id].map(a => |
||||
(({ id, order, show, label, help, description, required }) => ({ |
||||
id, |
||||
order, |
||||
show, |
||||
label, |
||||
help, |
||||
description, |
||||
required |
||||
}))(a) |
||||
); |
||||
|
||||
for (let i = 0; i < view.columns?.length; i++) |
||||
view.columns[i].title = ncMap[viewStore.columns[v.id][i].id]; |
||||
|
||||
// skip hm & mm columns
|
||||
view.columns = view.columns |
||||
?.filter(a => a.title?.includes('_nc_m2m_') === false) |
||||
.filter(a => a.title?.includes('nc_') === false); |
||||
} |
||||
|
||||
// filter & sort configurations
|
||||
if (v.type !== FORM) { |
||||
view.sort = viewStore.sort[v.id].map(a => |
||||
(({ fk_column_id, direction, order }) => ({ |
||||
fk_column_id, |
||||
direction, |
||||
order |
||||
}))(a) |
||||
); |
||||
view.filter = viewStore.filter[v.id].map(a => |
||||
(({ fk_column_id, logical_op, comparison_op, value, order }) => ({ |
||||
fk_column_id, |
||||
logical_op, |
||||
comparison_op, |
||||
value, |
||||
order |
||||
}))(a) |
||||
); |
||||
} |
||||
return view; |
||||
} |
||||
|
||||
// view data stored as is for quick access
|
||||
async function storeViewDetails(tableId) { |
||||
// read view data for each table
|
||||
let viewList = await api.dbView.list(tableId); |
||||
for (let j = 0; j < viewList.list.length; j++) { |
||||
let v = viewList.list[j]; |
||||
let viewDetails = []; |
||||
|
||||
// invoke view specific read to populate columns information
|
||||
if (v.type === FORM) viewDetails = (await api.dbView.formRead(v.id)).columns; |
||||
else if (v.type === GALLERY) viewDetails = await api.dbView.galleryRead(v.id); |
||||
else if (v.type === GRID) viewDetails = await api.dbView.gridColumnsList(v.id); |
||||
viewStore.columns[v.id] = viewDetails; |
||||
|
||||
// populate sort information
|
||||
let vSort = await api.dbTableSort.list(v.id); |
||||
viewStore.sort[v.id] = vSort.sorts.list; |
||||
|
||||
let vFilter = await api.dbTableFilter.read(v.id); |
||||
viewStore.filter[v.id] = vFilter; |
||||
} |
||||
} |
||||
|
||||
// mapping table for quick information access
|
||||
// store maps for tableId, columnId, viewColumnId & viewId to their names
|
||||
async function generateMapTbl(pId) { |
||||
const tblList = await api.dbTable.list(pId); |
||||
|
||||
for (let i = 0; i < tblList.list.length; i++) { |
||||
let tblId = tblList.list[i].id; |
||||
let tbl = await api.dbTable.read(tblId); |
||||
|
||||
// table ID <> name
|
||||
ncMap[tblId] = tbl.title; |
||||
|
||||
// column ID <> name
|
||||
tbl.columns.map(x => (ncMap[x.id] = x.title)); |
||||
|
||||
// view ID <> name
|
||||
tbl.views.map(x => (ncMap[x.id] = x.tn)); |
||||
|
||||
for (let i = 0; i < tbl.views.length; i++) { |
||||
let x = tbl.views[i]; |
||||
let viewColumns = []; |
||||
if (x.type === FORM) viewColumns = (await api.dbView.formRead(x.id)).columns; |
||||
else if (x.type === GALLERY) |
||||
viewColumns = (await api.dbView.galleryRead(x.id)).columns; |
||||
else if (x.type === GRID) viewColumns = await api.dbView.gridColumnsList(x.id); |
||||
|
||||
// view column ID <> name
|
||||
viewColumns?.map(a => (ncMap[a.id] = ncMap[a.fk_column_id])); |
||||
} |
||||
} |
||||
} |
||||
|
||||
// main
|
||||
//
|
||||
async function exportSchema() { |
||||
api = new Api(ncConfig); |
||||
|
||||
// fetch project details (id et.al)
|
||||
const x = await api.project.list(); |
||||
const p = x.list.find(a => a.title === ncConfig.projectName); |
||||
|
||||
await generateMapTbl(p.id); |
||||
|
||||
// read project
|
||||
const tblList = await api.dbTable.list(p.id); |
||||
|
||||
// for each table
|
||||
for (let i = 0; i < tblList.list.length; i++) { |
||||
let tblId = tblList.list[i].id; |
||||
await storeViewDetails(tblId); |
||||
|
||||
let tbl = await api.dbTable.read(tblId); |
||||
|
||||
// prepare schema
|
||||
let tSchema = { |
||||
id: tbl.id, |
||||
title: tbl.title, |
||||
table_name: tbl?.table_name, |
||||
columns: [...tbl.columns.map(c => addColumnSpecificData(c))] |
||||
.filter(a => a.title.includes('_nc_m2m_') === false) // mm
|
||||
.filter(a => a.title.includes(p.prefix) === false) // hm
|
||||
.filter( |
||||
a => !(a?.system === 1 && a.uidt === UITypes.LinkToAnotherRecord) |
||||
), |
||||
views: [...tbl.views.map(v => addViewDetails(v))] |
||||
}; |
||||
tblSchema.push(tSchema); |
||||
} |
||||
} |
||||
|
||||
(async () => { |
||||
await exportSchema(); |
||||
jsonfile.writeFileSync( |
||||
`${ncConfig.projectName.replace(/ /g, '_')}.json`, |
||||
tblSchema, |
||||
{ spaces: 2 } |
||||
); |
||||
})().catch(e => { |
||||
console.log(e); |
||||
}); |
||||
|
||||
/** |
||||
* @copyright Copyright (c) 2021, Xgene Cloud Ltd |
||||
* |
||||
* @author Raju Udava <sivadstala@gmail.com> |
||||
* |
||||
* @license GNU AGPL version 3 or any later version |
||||
* |
||||
* This program is free software: you can redistribute it and/or modify |
||||
* it under the terms of the GNU Affero General Public License as |
||||
* published by the Free Software Foundation, either version 3 of the |
||||
* License, or (at your option) any later version. |
||||
* |
||||
* This program is distributed in the hope that it will be useful, |
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
* GNU Affero General Public License for more details. |
||||
* |
||||
* You should have received a copy of the GNU Affero General Public License |
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
* |
||||
*/ |
@ -0,0 +1,537 @@
|
||||
// tbd
|
||||
// - formula dependency list
|
||||
// - nested lookup/ rollup
|
||||
|
||||
const Api = require('nocodb-sdk').Api; |
||||
const { UITypes } = require('nocodb-sdk'); |
||||
const jsonfile = require('jsonfile'); |
||||
|
||||
let inputConfig = jsonfile.readFileSync(`config.json`) |
||||
let ncConfig = { |
||||
srcProject: inputConfig.srcProject, |
||||
projectName: inputConfig.dstProject, |
||||
baseURL: inputConfig.baseURL, |
||||
headers: { |
||||
'xc-auth': `${inputConfig["xc-auth"]}` |
||||
} |
||||
}; |
||||
let ncIn = jsonfile.readFileSync(`${ncConfig.srcProject}.json`); |
||||
|
||||
let api = {}; |
||||
let ncProject = {}; |
||||
let link = []; |
||||
let lookup = []; |
||||
let rollup = []; |
||||
let formula = []; |
||||
|
||||
let rootLinks = []; |
||||
|
||||
// maps v1 table ID, v2 table ID & table title to table schema
|
||||
let ncTables = {}; |
||||
|
||||
|
||||
async function createBaseTables() { |
||||
console.log(`createBaseTables`); |
||||
for (let i = 0; i < ncIn.length; i++) { |
||||
let tblSchema = ncIn[i]; |
||||
let reducedColumnSet = tblSchema.columns.filter( |
||||
a => |
||||
a.uidt !== UITypes.LinkToAnotherRecord && |
||||
a.uidt !== UITypes.Lookup && |
||||
a.uidt !== UITypes.Rollup && |
||||
a.uidt !== UITypes.Formula |
||||
); |
||||
link.push( |
||||
...tblSchema.columns.filter(a => a.uidt === UITypes.LinkToAnotherRecord) |
||||
); |
||||
lookup.push(...tblSchema.columns.filter(a => a.uidt === UITypes.Lookup)); |
||||
rollup.push(...tblSchema.columns.filter(a => a.uidt === UITypes.Rollup)); |
||||
formula.push(...tblSchema.columns.filter(a => a.uidt === UITypes.Formula)); |
||||
formula.map(a => (a['table_id'] = tblSchema.id)); |
||||
|
||||
let tbl = await api.dbTable.create(ncProject.id, { |
||||
title: tblSchema.title, |
||||
table_name: tblSchema.title, |
||||
columns: reducedColumnSet.map(({ id, ...rest }) => ({ ...rest })) |
||||
}); |
||||
ncTables[tbl.title] = tbl; |
||||
ncTables[tbl.id] = tbl; |
||||
ncTables[tblSchema.id] = tbl; |
||||
} |
||||
} |
||||
|
||||
let linksCreated = []; |
||||
function isLinkCreated(pId, cId) { |
||||
let idx = linksCreated.findIndex(a => a.cId === pId && a.pId === cId); |
||||
if (idx === -1) { |
||||
linksCreated.push({ pId: pId, cId: cId }); |
||||
return false; |
||||
} |
||||
return true; |
||||
} |
||||
|
||||
// retrieve nc-view column ID from corresponding nc-column ID
|
||||
async function nc_getViewColumnId(viewId, viewType, ncColumnId) { |
||||
// retrieve view Info
|
||||
let viewDetails; |
||||
|
||||
if (viewType === 'form') |
||||
viewDetails = (await api.dbView.formRead(viewId)).columns; |
||||
else if (viewType === 'gallery') |
||||
viewDetails = (await api.dbView.galleryRead(viewId)).columns; |
||||
else viewDetails = await api.dbView.gridColumnsList(viewId); |
||||
|
||||
return viewDetails.find(x => x.fk_column_id === ncColumnId)?.id; |
||||
} |
||||
|
||||
async function createFormula() { |
||||
for (let i = 0; i < formula.length; i++) { |
||||
let tbl = await api.dbTableColumn.create(ncTables[formula[i].table_id].id, { |
||||
uidt: UITypes.Formula, |
||||
title: formula[i].title, |
||||
formula_raw: formula[i].formula_raw |
||||
}); |
||||
} |
||||
} |
||||
|
||||
async function createLinks() { |
||||
console.log(`createLinks`); |
||||
|
||||
for (let i = 0; i < link.length; i++) { |
||||
if ( |
||||
(link[i].colOptions.type === 'mm' && |
||||
false === |
||||
isLinkCreated( |
||||
link[i].colOptions.fk_parent_column_id, |
||||
link[i].colOptions.fk_child_column_id |
||||
)) || |
||||
link[i].colOptions.type === 'hm' |
||||
) { |
||||
let srcTbl = ncTables[link[i].colOptions.fk_model_id]; |
||||
let dstTbl = ncTables[link[i].colOptions.fk_related_model_id]; |
||||
|
||||
// create link
|
||||
let tbl = await api.dbTableColumn.create(srcTbl.id, { |
||||
uidt: UITypes.LinkToAnotherRecord, |
||||
title: link[i].title, |
||||
parentId: srcTbl.id, |
||||
childId: dstTbl.id, |
||||
type: link[i].colOptions.type |
||||
}); |
||||
ncTables[tbl.title] = tbl; |
||||
ncTables[tbl.id] = tbl; |
||||
ncTables[link[i].colOptions.fk_model_id] = tbl; |
||||
|
||||
// for data-link procedure later
|
||||
rootLinks.push({ linkColumn: link[i], linkSrcTbl: srcTbl }); |
||||
|
||||
// symmetry field update
|
||||
//
|
||||
let v2ColSchema = tbl.columns.find(x => x.title === link[i].title); |
||||
// read related table again after link is created
|
||||
dstTbl = await api.dbTable.read(dstTbl.id); |
||||
let v2SymmetricColumn = |
||||
link[i].colOptions.type === 'mm' |
||||
? dstTbl.columns.find( |
||||
x => |
||||
x.uidt === UITypes.LinkToAnotherRecord && |
||||
x?.colOptions.fk_parent_column_id === |
||||
v2ColSchema.colOptions.fk_child_column_id && |
||||
x?.colOptions.fk_child_column_id === |
||||
v2ColSchema.colOptions.fk_parent_column_id |
||||
) |
||||
: dstTbl.columns.find( |
||||
x => |
||||
x.uidt === UITypes.LinkToAnotherRecord && |
||||
x?.colOptions.fk_parent_column_id === |
||||
v2ColSchema.colOptions.fk_parent_column_id && |
||||
x?.colOptions.fk_child_column_id === |
||||
v2ColSchema.colOptions.fk_child_column_id |
||||
); |
||||
let v1SymmetricColumn = |
||||
link[i].colOptions.type === 'mm' |
||||
? link.find( |
||||
x => |
||||
x.colOptions.fk_parent_column_id === |
||||
link[i].colOptions.fk_child_column_id && |
||||
x.colOptions.fk_child_column_id === |
||||
link[i].colOptions.fk_parent_column_id && |
||||
x.colOptions.type === 'mm' |
||||
) |
||||
: link.find( |
||||
x => |
||||
x.colOptions.fk_parent_column_id === |
||||
link[i].colOptions.fk_parent_column_id && |
||||
x.colOptions.fk_child_column_id === |
||||
link[i].colOptions.fk_child_column_id && |
||||
x.colOptions.type === 'bt' |
||||
); |
||||
|
||||
tbl = await api.dbTableColumn.update(v2SymmetricColumn.id, { |
||||
...v2SymmetricColumn, |
||||
title: v1SymmetricColumn.title, |
||||
column_name: null |
||||
}); |
||||
ncTables[tbl.title] = tbl; |
||||
ncTables[tbl.id] = tbl; |
||||
ncTables[v1SymmetricColumn.colOptions.fk_model_id] = tbl; |
||||
} |
||||
} |
||||
} |
||||
|
||||
function get_v2Id(v1ColId) { |
||||
for (let i = 0; i < ncIn.length; i++) { |
||||
let tblSchema = ncIn[i]; |
||||
let colSchema = {}; |
||||
if ( |
||||
undefined !== (colSchema = tblSchema.columns.find(x => x.id === v1ColId)) |
||||
) { |
||||
let colName = colSchema.title; |
||||
let v2Tbl = ncTables[tblSchema.id]; |
||||
return v2Tbl.columns.find(y => y.title === colName)?.id; |
||||
} |
||||
} |
||||
} |
||||
|
||||
async function createLookup() { |
||||
console.log(`createLookup`); |
||||
|
||||
for (let i = 0; i < lookup.length; i++) { |
||||
let srcTbl = ncTables[lookup[i].colOptions.fk_model_id]; |
||||
let v2_fk_relation_column_id = get_v2Id( |
||||
lookup[i].colOptions.fk_relation_column_id |
||||
); |
||||
let v2_lookup_column_id = get_v2Id( |
||||
lookup[i].colOptions.fk_lookup_column_id |
||||
); |
||||
|
||||
if (v2_lookup_column_id) { |
||||
let tbl = await api.dbTableColumn.create(srcTbl.id, { |
||||
uidt: UITypes.Lookup, |
||||
title: lookup[i].title, |
||||
fk_relation_column_id: v2_fk_relation_column_id, |
||||
fk_lookup_column_id: v2_lookup_column_id |
||||
}); |
||||
ncTables[tbl.title] = tbl; |
||||
ncTables[tbl.id] = tbl; |
||||
ncTables[lookup[i].colOptions.fk_model_id] = tbl; |
||||
} |
||||
} |
||||
} |
||||
|
||||
async function createRollup() { |
||||
console.log(`createRollup`); |
||||
|
||||
for (let i = 0; i < rollup.length; i++) { |
||||
let srcTbl = ncTables[rollup[i].colOptions.fk_model_id]; |
||||
let v2_fk_relation_column_id = get_v2Id( |
||||
rollup[i].colOptions.fk_relation_column_id |
||||
); |
||||
let v2_rollup_column_id = get_v2Id( |
||||
rollup[i].colOptions.fk_rollup_column_id |
||||
); |
||||
|
||||
if (v2_rollup_column_id) { |
||||
let tbl = await api.dbTableColumn.create(srcTbl.id, { |
||||
uidt: UITypes.Rollup, |
||||
title: rollup[i].title, |
||||
column_name: rollup[i].title, |
||||
fk_relation_column_id: v2_fk_relation_column_id, |
||||
fk_rollup_column_id: v2_rollup_column_id, |
||||
rollup_function: rollup[i].colOptions.rollup_function |
||||
}); |
||||
ncTables[tbl.title] = tbl; |
||||
ncTables[tbl.id] = tbl; |
||||
ncTables[rollup[i].colOptions.fk_model_id] = tbl; |
||||
} |
||||
} |
||||
} |
||||
|
||||
async function configureGrid() { |
||||
console.log(`configureGrid`); |
||||
|
||||
for (let i = 0; i < ncIn.length; i++) { |
||||
let tblSchema = ncIn[i]; |
||||
let tblId = ncTables[tblSchema.id].id; |
||||
let gridList = tblSchema.views.filter(a => a.type === 3); |
||||
let srcTbl = await api.dbTable.read(tblId); |
||||
|
||||
const view = await api.dbView.list(tblId); |
||||
|
||||
// create / rename view
|
||||
for (let gridCnt = 0; gridCnt < gridList.length; gridCnt++) { |
||||
let viewCreated = {}; |
||||
// rename first view; default view already created
|
||||
if (gridCnt === 0) { |
||||
viewCreated = await api.dbView.update(view.list[0].id, { |
||||
title: gridList[gridCnt].title |
||||
}); |
||||
} |
||||
// create new views
|
||||
else { |
||||
viewCreated = await api.dbView.gridCreate(tblId, { |
||||
title: gridList[gridCnt].title |
||||
}); |
||||
} |
||||
|
||||
// retrieve view Info
|
||||
let viewId = viewCreated.id; |
||||
let viewDetails = await api.dbView.gridColumnsList(viewId); |
||||
|
||||
// column visibility
|
||||
for ( |
||||
let colCnt = 0; |
||||
colCnt < gridList[gridCnt].columns.length; |
||||
colCnt++ |
||||
) { |
||||
let ncColumnId = srcTbl.columns.find( |
||||
a => a.title === gridList[gridCnt].columns[colCnt].title |
||||
)?.id; |
||||
// let ncViewColumnId = await nc_getViewColumnId( viewCreated.id, "grid", ncColumnId )
|
||||
let ncViewColumnId = viewDetails.find( |
||||
x => x.fk_column_id === ncColumnId |
||||
)?.id; |
||||
// column order & visibility
|
||||
await api.dbViewColumn.update(viewCreated.id, ncViewColumnId, { |
||||
show: gridList[gridCnt].columns[colCnt].show, |
||||
order: gridList[gridCnt].columns[colCnt].order |
||||
}); |
||||
await api.dbView.gridColumnUpdate(ncViewColumnId, { |
||||
width: gridList[gridCnt].columns[colCnt].width |
||||
}); |
||||
} |
||||
|
||||
// sort
|
||||
for (let sCnt = 0; sCnt < gridList[gridCnt].sort.length; sCnt++) { |
||||
let sColName = tblSchema.columns.find( |
||||
a => gridList[gridCnt].sort[sCnt].fk_column_id === a.id |
||||
).title; |
||||
await api.dbTableSort.create(viewId, { |
||||
fk_column_id: srcTbl.columns.find(a => a.title === sColName)?.id, |
||||
direction: gridList[gridCnt].sort[sCnt].direction |
||||
}); |
||||
} |
||||
|
||||
// filter
|
||||
for (let fCnt = 0; fCnt < gridList[gridCnt].filter.length; fCnt++) { |
||||
let fColName = tblSchema.columns.find( |
||||
a => gridList[gridCnt].sort[fCnt].fk_column_id === a.id |
||||
).title; |
||||
await api.dbTableFilter.create(viewId, { |
||||
...gridList[gridCnt].filter[fCnt], |
||||
fk_column_id: srcTbl.columns.find(a => a.title === fColName)?.id |
||||
}); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
async function configureGallery() { |
||||
console.log(`configureGallery`); |
||||
|
||||
for (let i = 0; i < ncIn.length; i++) { |
||||
let tblSchema = ncIn[i]; |
||||
let tblId = ncTables[tblSchema.id].id; |
||||
let galleryList = tblSchema.views.filter(a => a.type === 2); |
||||
for (let cnt = 0; cnt < galleryList.length; cnt++) { |
||||
const viewCreated = await api.dbView.galleryCreate(tblId, { |
||||
title: galleryList[cnt].title |
||||
}); |
||||
} |
||||
} |
||||
} |
||||
|
||||
async function configureForm() { |
||||
console.log(`configureForm`); |
||||
|
||||
for (let i = 0; i < ncIn.length; i++) { |
||||
let tblSchema = ncIn[i]; |
||||
let tblId = ncTables[tblSchema.id].id; |
||||
let formList = tblSchema.views.filter(a => a.type === 1); |
||||
let srcTbl = await api.dbTable.read(tblId); |
||||
|
||||
for (let formCnt = 0; formCnt < formList.length; formCnt++) { |
||||
const formData = { |
||||
title: formList[formCnt].title, |
||||
...formList[formCnt].property |
||||
}; |
||||
const viewCreated = await api.dbView.formCreate(tblId, formData); |
||||
|
||||
// column visibility
|
||||
for ( |
||||
let colCnt = 0; |
||||
colCnt < formList[formCnt].columns.length; |
||||
colCnt++ |
||||
) { |
||||
let ncColumnId = srcTbl.columns.find( |
||||
a => a.title === formList[formCnt].columns[colCnt].title |
||||
)?.id; |
||||
let ncViewColumnId = await nc_getViewColumnId( |
||||
viewCreated.id, |
||||
'form', |
||||
ncColumnId |
||||
); |
||||
// column order & visibility
|
||||
await api.dbView.formColumnUpdate(ncViewColumnId, { |
||||
show: formList[formCnt].columns[colCnt].show, |
||||
order: formList[formCnt].columns[colCnt].order, |
||||
label: formList[formCnt].columns[colCnt].label, |
||||
description: formList[formCnt].columns[colCnt].description, |
||||
required: formList[formCnt].columns[colCnt].required |
||||
}); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
async function restoreBaseData() { |
||||
console.log(`restoreBaseData`); |
||||
|
||||
for (let i = 0; i < ncIn.length; i++) { |
||||
let tblSchema = ncIn[i]; |
||||
let tblId = ncTables[tblSchema.id].id; |
||||
let pk = tblSchema.columns.find(a => a.pk).title; |
||||
|
||||
let moreRecords = true; |
||||
let offset = 0, |
||||
limit = 25; |
||||
|
||||
while (moreRecords) { |
||||
let recList = await api.dbTableRow.list( |
||||
'nc', |
||||
ncConfig.srcProject, |
||||
tblSchema.title, |
||||
{}, |
||||
{ |
||||
query: { limit: limit, offset: offset } |
||||
} |
||||
); |
||||
moreRecords = !recList.pageInfo.isLastPage; |
||||
offset += limit; |
||||
|
||||
for (let recCnt = 0; recCnt < recList.list.length; recCnt++) { |
||||
let record = await api.dbTableRow.read( |
||||
'nc', |
||||
ncConfig.srcProject, |
||||
tblSchema.title, |
||||
recList.list[recCnt][pk] |
||||
); |
||||
|
||||
// post-processing on the record
|
||||
for (const [key, value] of Object.entries(record)) { |
||||
let table = ncTables[tblId]; |
||||
// retrieve datatype
|
||||
const dt = table.columns.find(x => x.title === key)?.uidt; |
||||
if (dt === UITypes.LinkToAnotherRecord) delete record[key]; |
||||
if (dt === UITypes.Lookup) delete record[key]; |
||||
if (dt === UITypes.Rollup) delete record[key]; |
||||
} |
||||
await api.dbTableRow.create( |
||||
'nc', |
||||
ncConfig.projectName, |
||||
tblSchema.title, |
||||
record |
||||
); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
async function restoreLinks() { |
||||
console.log(`restoreLinks`); |
||||
|
||||
for (let i = 0; i < rootLinks.length; i++) { |
||||
let pk = rootLinks[i].linkSrcTbl.columns.find(a => a.pk).title; |
||||
let moreRecords = true; |
||||
let offset = 0, |
||||
limit = 25; |
||||
|
||||
while (moreRecords) { |
||||
let recList = await api.dbTableRow.list( |
||||
'nc', |
||||
ncConfig.srcProject, |
||||
rootLinks[i].linkSrcTbl.title, |
||||
{}, |
||||
{ |
||||
query: { limit: limit, offset: offset } |
||||
} |
||||
); |
||||
moreRecords = !recList.pageInfo.isLastPage; |
||||
offset += limit; |
||||
|
||||
for (let recCnt = 0; recCnt < recList.list.length; recCnt++) { |
||||
let record = await api.dbTableRow.read( |
||||
'nc', |
||||
ncConfig.srcProject, |
||||
rootLinks[i].linkSrcTbl.title, |
||||
recList.list[recCnt][pk] |
||||
); |
||||
let linkField = record[rootLinks[i].linkColumn.title]; |
||||
if (linkField.length) { |
||||
await api.dbTableRow.nestedAdd( |
||||
'nc', |
||||
ncConfig.projectName, |
||||
rootLinks[i].linkSrcTbl.title, |
||||
record[pk], |
||||
rootLinks[i].linkColumn.colOptions.type, |
||||
encodeURIComponent(rootLinks[i].linkColumn.title), |
||||
linkField[0][pk] |
||||
); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
async function importSchema() { |
||||
api = new Api(ncConfig); |
||||
|
||||
const x = await api.project.list(); |
||||
const p = x.list.find(a => a.title === ncConfig.projectName); |
||||
if (p) await api.project.delete(p.id); |
||||
ncProject = await api.project.create({ title: ncConfig.projectName }); |
||||
|
||||
await createBaseTables(); |
||||
await createLinks(); |
||||
await createLookup(); |
||||
await createRollup(); |
||||
await createFormula(); |
||||
|
||||
// configure views
|
||||
await configureGrid(); |
||||
await configureGallery(); |
||||
await configureForm(); |
||||
|
||||
// restore data only if source project exists
|
||||
const p2 = x.list.find(a => a.title === ncConfig.srcProject); |
||||
if (p2 !== undefined) { |
||||
await restoreBaseData(); |
||||
await restoreLinks(); |
||||
} |
||||
} |
||||
(async () => { |
||||
await importSchema(); |
||||
console.log('completed'); |
||||
})().catch(e => console.log(e)); |
||||
|
||||
/** |
||||
* @copyright Copyright (c) 2021, Xgene Cloud Ltd |
||||
* |
||||
* @author Raju Udava <sivadstala@gmail.com> |
||||
* |
||||
* @license GNU AGPL version 3 or any later version |
||||
* |
||||
* This program is free software: you can redistribute it and/or modify |
||||
* it under the terms of the GNU Affero General Public License as |
||||
* published by the Free Software Foundation, either version 3 of the |
||||
* License, or (at your option) any later version. |
||||
* |
||||
* This program is distributed in the hope that it will be useful, |
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
* GNU Affero General Public License for more details. |
||||
* |
||||
* You should have received a copy of the GNU Affero General Public License |
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
* |
||||
*/ |
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,17 @@
|
||||
version: "2.1" |
||||
|
||||
services: |
||||
pg96: |
||||
image: postgres:9.6 |
||||
restart: always |
||||
environment: |
||||
POSTGRES_PASSWORD: password |
||||
ports: |
||||
- 5432:5432 |
||||
volumes: |
||||
- ../../packages/nocodb/tests/pg-cy-quick:/docker-entrypoint-initdb.d |
||||
healthcheck: |
||||
test: ["CMD-SHELL", "pg_isready -U postgres"] |
||||
interval: 10s |
||||
timeout: 5s |
||||
retries: 5 |
Binary file not shown.
@ -0,0 +1,358 @@
|
||||
import { |
||||
isTestSuiteActive, |
||||
roles, |
||||
} from "../../support/page_objects/projectConstants"; |
||||
import { loginPage, projectsPage } from "../../support/page_objects/navigation"; |
||||
import { mainPage } from "../../support/page_objects/mainPage"; |
||||
|
||||
// normal fields
|
||||
let records = { |
||||
Name: "Movie-1", |
||||
Notes: "Good", |
||||
Status: "Todo", |
||||
Tags: "Jan", |
||||
Phone: "123123123", |
||||
Email: "a@b.com", |
||||
URL: "www.a.com", |
||||
Number: "1", |
||||
Value: "$1.00", |
||||
Percent: "0.01", |
||||
Duration: "60", |
||||
}; |
||||
|
||||
// links/ computed fields
|
||||
let records2 = { |
||||
Done: true, |
||||
Date: "2022-05-31", |
||||
Rating: "1", |
||||
Actor: ["Actor1", "Actor2"], |
||||
"Status (from Actor)": ["Todo", "In progress"], |
||||
RollUp: "128", |
||||
Computation: "4.04", |
||||
Producer: ["P1", "P2"] |
||||
}; |
||||
|
||||
function openWebhook(index) { |
||||
cy.get(".nc-btn-webhook").should("exist").click(); |
||||
cy.get(".nc-hook").eq(index).click({ force: true }); |
||||
} |
||||
|
||||
// to be invoked after open
|
||||
function verifyWebhook(config) { |
||||
cy.get(".nc-text-field-hook-title") |
||||
.find('input').then(($element) => { |
||||
expect($element[0].value).to.have.string(config.title) |
||||
}) |
||||
cy.get(".nc-text-field-hook-event") |
||||
.find('.v-select__selection') |
||||
.contains(config.event) |
||||
.should('exist') |
||||
cy.get(".nc-text-field-hook-notification-type") |
||||
.find('.v-select__selection') |
||||
.contains(config.notification) |
||||
.should('exist') |
||||
cy.get('.nc-select-hook-url-method') |
||||
.find('.v-select__selection') |
||||
.contains(config.type) |
||||
.should('exist') |
||||
cy.get(".nc-text-field-hook-url-path") |
||||
.find('input').then(($element) => { |
||||
expect($element[0].value).to.have.string(config.url) |
||||
}) |
||||
cy.get(".nc-icon-hook-navigate-left").click({force:true}) |
||||
} |
||||
|
||||
export const genTest = (apiType, dbType) => { |
||||
if (!isTestSuiteActive(apiType, dbType)) return; |
||||
describe(`Webhook`, () => { |
||||
before(() => { |
||||
cy.task("copyFile") |
||||
loginPage.signIn(roles.owner.credentials); |
||||
projectsPage.openProject("sample"); |
||||
}); |
||||
|
||||
after(() => {}); |
||||
|
||||
it("Verify Data types", () => { |
||||
cy.openTableTab("Film", 3); |
||||
|
||||
// normal cells
|
||||
for (let [key, value] of Object.entries(records)) { |
||||
mainPage.getCell(key, 1).contains(value).should("exist"); |
||||
} |
||||
|
||||
// checkbox
|
||||
mainPage |
||||
.getCell("Done", 1) |
||||
.find(".mdi-check-circle-outline") |
||||
.should(records2.Done ? "exist" : "not.exist"); |
||||
|
||||
// date
|
||||
|
||||
// rating
|
||||
mainPage |
||||
.getCell("Rating", 1) |
||||
.find("button.mdi-star") |
||||
.should("have.length", records2.Rating); |
||||
|
||||
// LinkToAnotherRecord
|
||||
mainPage.getCell("Actor", 1).scrollIntoView(); |
||||
cy.get( |
||||
':nth-child(1) > [data-col="Actor"] > .nc-virtual-cell > .v-lazy > .d-100 > .chips > :nth-child(1) > .v-chip__content > .name' |
||||
) |
||||
.contains(records2.Actor[0]) |
||||
.should("exist"); |
||||
cy.get( |
||||
':nth-child(1) > [data-col="Actor"] > .nc-virtual-cell > .v-lazy > .d-100 > .chips > :nth-child(2) > .v-chip__content > .name' |
||||
) |
||||
.contains(records2.Actor[1]) |
||||
.should("exist"); |
||||
// mainPage.getCell("Actor", 1).find(".nc-virtual-cell > .v-lazy > .d-100 > .chips").eq(0).contains("Actor1").should('exist')
|
||||
// mainPage.getCell("Actor", 1).find(".nc-virtual-cell > .v-lazy > .d-100 > .chips").eq(1).contains("Actor2").should('exist')
|
||||
|
||||
// lookup
|
||||
mainPage.getCell("Status (from Actor)", 1).scrollIntoView(); |
||||
cy.get( |
||||
':nth-child(1) > [data-col="Status (from Actor)"] > .nc-virtual-cell > .v-lazy > .d-flex > :nth-child(1) > .v-chip__content > div > .set-item' |
||||
) |
||||
.contains(records2["Status (from Actor)"][0]) |
||||
.should("exist"); |
||||
cy.get( |
||||
':nth-child(1) > [data-col="Status (from Actor)"] > .nc-virtual-cell > .v-lazy > .d-flex > :nth-child(2) > .v-chip__content > div > .set-item' |
||||
) |
||||
.contains(records2["Status (from Actor)"][1]) |
||||
.should("exist"); |
||||
|
||||
// rollup
|
||||
mainPage.getCell("RollUp", 1).scrollIntoView(); |
||||
// cy.get(':nth-child(1) > [data-col="RollUp"] > .nc-virtual-cell > .v-lazy > span').contains(records2.RollUp).should('exist')
|
||||
cy.get(`:nth-child(1) > [data-col="RollUp"] > .nc-virtual-cell`) |
||||
.contains(records2.RollUp) |
||||
.should("exist"); |
||||
|
||||
// formula
|
||||
mainPage.getCell("Computation", 1).scrollIntoView(); |
||||
cy.get( |
||||
`:nth-child(1) > [data-col="Computation"] > .nc-virtual-cell` |
||||
) |
||||
.contains(records2.Computation) |
||||
.should("exist"); |
||||
|
||||
// ltar hm relation
|
||||
mainPage.getCell("Producer", 1).scrollIntoView(); |
||||
cy.get( |
||||
':nth-child(1) > [data-col="Producer"] > .nc-virtual-cell > .v-lazy > .d-100 > .chips > :nth-child(1) > .v-chip__content > .name' |
||||
) |
||||
.contains(records2.Producer[0]) |
||||
.should("exist"); |
||||
cy.get( |
||||
':nth-child(1) > [data-col="Producer"] > .nc-virtual-cell > .v-lazy > .d-100 > .chips > :nth-child(2) > .v-chip__content > .name' |
||||
) |
||||
.contains(records2.Producer[1]) |
||||
.should("exist"); |
||||
|
||||
cy.closeTableTab("Film"); |
||||
}); |
||||
|
||||
it("Verify Views & Shared base", () => { |
||||
cy.openTableTab("Film", 3); |
||||
cy.get('.nc-form-view-item').eq(0) |
||||
.click({ force: true }) |
||||
|
||||
// Header & description should exist
|
||||
cy.get(".nc-form") |
||||
.find('[placeholder="Form Title"]') |
||||
.contains("FormTitle") |
||||
.should("exist"); |
||||
cy.get(".nc-form") |
||||
.find('[placeholder="Add form description"]') |
||||
.contains("FormDescription") |
||||
.should("exist"); |
||||
|
||||
// modified column name & help text
|
||||
cy.get(".nc-field-wrapper").eq(0) |
||||
.find('.nc-field-labels') |
||||
.contains("DisplayName") |
||||
.should('exist') |
||||
cy.get(".nc-field-wrapper").eq(0) |
||||
.find('.nc-hint') |
||||
.contains('HelpText') |
||||
.should('exist') |
||||
|
||||
cy.get(".nc-field-wrapper").eq(1) |
||||
.find('.nc-field-labels') |
||||
.contains("Email") |
||||
.should('exist') |
||||
|
||||
// add message
|
||||
cy.get(".nc-form > .mx-auto") |
||||
.find("textarea").then(($element) => { |
||||
expect($element[0].value).to.have.string("Thank you for submitting the form!") |
||||
}) |
||||
|
||||
// submit another form button
|
||||
cy.get(".nc-form > .mx-auto") |
||||
.find('[type="checkbox"]') |
||||
.eq(0) |
||||
.should('be.checked') |
||||
// "New form after 5 seconds" button
|
||||
cy.get(".nc-form > .mx-auto") |
||||
.find('[type="checkbox"]') |
||||
.eq(1) |
||||
.should('be.checked') |
||||
// email me
|
||||
cy.get(".nc-form > .mx-auto") |
||||
.find('[type="checkbox"]') |
||||
.eq(2) |
||||
.should('not.be.checked') |
||||
|
||||
cy.closeTableTab("Film"); |
||||
}); |
||||
|
||||
it("Verify Webhooks", () => { |
||||
cy.openTableTab("Actor", 25); |
||||
openWebhook(0) |
||||
verifyWebhook({ |
||||
title: "Webhook-1", |
||||
event: "After Insert", |
||||
notification: "URL", |
||||
type: "POST", |
||||
url: "http://localhost:9090/hook", |
||||
condition: false |
||||
}) |
||||
cy.get("body").type("{esc}"); |
||||
|
||||
openWebhook(1) |
||||
verifyWebhook({ |
||||
title: "Webhook-2", |
||||
event: "After Update", |
||||
notification: "URL", |
||||
type: "POST", |
||||
url: "http://localhost:9090/hook", |
||||
condition: false |
||||
}) |
||||
cy.get("body").type("{esc}"); |
||||
|
||||
openWebhook(2) |
||||
verifyWebhook({ |
||||
title: "Webhook-3", |
||||
event: "After Delete", |
||||
notification: "URL", |
||||
type: "POST", |
||||
url: "http://localhost:9090/hook", |
||||
condition: false |
||||
}) |
||||
cy.get("body").type("{esc}"); |
||||
|
||||
cy.closeTableTab("Actor"); |
||||
|
||||
}); |
||||
|
||||
it("Pagination", () => { |
||||
cy.openTableTab("Actor", 25); |
||||
|
||||
cy.get(".nc-pagination").should("exist"); |
||||
|
||||
// verify > pagination option
|
||||
mainPage.getPagination(">").click(); |
||||
mainPage |
||||
.getPagination(2) |
||||
.should("have.class", "v-pagination__item--active"); |
||||
|
||||
// verify < pagination option
|
||||
mainPage.getPagination("<").click(); |
||||
mainPage |
||||
.getPagination(1) |
||||
.should("have.class", "v-pagination__item--active"); |
||||
|
||||
cy.closeTableTab("Actor"); |
||||
}); |
||||
|
||||
it("Verify Fields, Filter & Sort", () => { |
||||
cy.openTableTab("Actor", 25); |
||||
cy.get(".nc-grid-view-item").eq(1).click() |
||||
|
||||
cy.get(".nc-grid-header-cell").contains('Name').should("be.visible"); |
||||
cy.get(".nc-grid-header-cell").contains('Notes').should("be.visible"); |
||||
cy.get(".nc-grid-header-cell").contains('Attachments').should("not.be.visible"); |
||||
cy.get(".nc-grid-header-cell").contains('Status').should("be.visible"); |
||||
cy.get(".nc-grid-header-cell").contains('Film').should("be.visible"); |
||||
|
||||
cy.get(".nc-fields-menu-btn").click(); |
||||
cy.getActiveMenu().find(`[type="checkbox"]`).eq(0).should('be.checked') |
||||
cy.getActiveMenu().find(`[type="checkbox"]`).eq(1).should('be.checked') |
||||
cy.getActiveMenu().find(`[type="checkbox"]`).eq(2).should('not.be.checked') |
||||
cy.getActiveMenu().find(`[type="checkbox"]`).eq(3).should('be.checked') |
||||
cy.getActiveMenu().find(`[type="checkbox"]`).eq(4).should('be.checked') |
||||
cy.get(".nc-fields-menu-btn").click(); |
||||
|
||||
cy.get(".nc-sort-menu-btn").click(); |
||||
cy.get(".nc-sort-field-select").eq(0) |
||||
.contains('Name') |
||||
.should("exist"); |
||||
cy.get(".nc-sort-dir-select").eq(0) |
||||
.contains('A -> Z') |
||||
.should("exist"); |
||||
cy.get(".nc-sort-menu-btn").click(); |
||||
|
||||
cy.get(".nc-filter-menu-btn").click(); |
||||
cy.get(".nc-filter-field-select").eq(0) |
||||
.contains('Name') |
||||
.should("exist"); |
||||
cy.get(".nc-filter-operation-select").eq(0) |
||||
.contains('is like') |
||||
.should("exist"); |
||||
|
||||
cy.get(".nc-filter-field-select").eq(1) |
||||
.contains('Name') |
||||
.should("exist"); |
||||
cy.get(".nc-filter-operation-select").eq(1) |
||||
.contains('is like') |
||||
.should("exist"); |
||||
cy.get(".nc-filter-menu-btn").click(); |
||||
|
||||
cy.closeTableTab("Actor"); |
||||
}); |
||||
|
||||
it("Views, bt relation", () => { |
||||
cy.openTableTab("Producer", 3) |
||||
cy.get('.nc-grid-view-item').should('have.length', 4) |
||||
cy.get('.nc-form-view-item').should('have.length', 4) |
||||
cy.get('.nc-gallery-view-item').should('have.length', 3) |
||||
|
||||
// LinkToAnotherRecord hm relation
|
||||
mainPage.getCell("FilmRead", 1).scrollIntoView(); |
||||
cy.get( |
||||
':nth-child(1) > [data-col="FilmRead"] > .nc-virtual-cell > .v-lazy > .d-100 > .chips > :nth-child(1) > .v-chip__content > .name' |
||||
) |
||||
.contains('Movie-1') |
||||
.should("exist"); |
||||
|
||||
cy.closeTableTab("Producer") |
||||
}) |
||||
}); |
||||
}; |
||||
|
||||
genTest("rest", "xcdb"); |
||||
|
||||
/** |
||||
* @copyright Copyright (c) 2021, Xgene Cloud Ltd |
||||
* |
||||
* @author Raju Udava <sivadstala@gmail.com> |
||||
* |
||||
* @license GNU AGPL version 3 or any later version |
||||
* |
||||
* This program is free software: you can redistribute it and/or modify |
||||
* it under the terms of the GNU Affero General Public License as |
||||
* published by the Free Software Foundation, either version 3 of the |
||||
* License, or (at your option) any later version. |
||||
* |
||||
* This program is distributed in the hope that it will be useful, |
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
* GNU Affero General Public License for more details. |
||||
* |
||||
* You should have received a copy of the GNU Affero General Public License |
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
* |
||||
*/ |
@ -0,0 +1,36 @@
|
||||
let t9a = require("../common/9a_QuickTest"); |
||||
const { |
||||
setCurrentMode, |
||||
} = require("../../support/page_objects/projectConstants"); |
||||
|
||||
// use 0 as mode to execute individual files (debug mode, skip pre-configs)
|
||||
// use 1 mode if noco.db doesnt contain user credentials (full run over GIT)
|
||||
|
||||
const nocoTestSuite = (apiType, dbType) => { |
||||
setCurrentMode(apiType, dbType); |
||||
t9a.genTest(apiType, dbType); |
||||
}; |
||||
|
||||
nocoTestSuite("rest", "xcdb"); |
||||
|
||||
/** |
||||
* @copyright Copyright (c) 2021, Xgene Cloud Ltd |
||||
* |
||||
* @author Raju Udava <sivadstala@gmail.com> |
||||
* |
||||
* @license GNU AGPL version 3 or any later version |
||||
* |
||||
* This program is free software: you can redistribute it and/or modify |
||||
* it under the terms of the GNU Affero General Public License as |
||||
* published by the Free Software Foundation, either version 3 of the |
||||
* License, or (at your option) any later version. |
||||
* |
||||
* This program is distributed in the hope that it will be useful, |
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
* GNU Affero General Public License for more details. |
||||
* |
||||
* You should have received a copy of the GNU Affero General Public License |
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
* |
||||
*/ |
Loading…
Reference in new issue