diff --git a/packages/nc-gui/package-lock.json b/packages/nc-gui/package-lock.json index 65135237bc..ab8faa11d2 100644 --- a/packages/nc-gui/package-lock.json +++ b/packages/nc-gui/package-lock.json @@ -30,7 +30,7 @@ "leaflet.markercluster": "^1.5.3", "locale-codes": "^1.3.1", "monaco-editor": "^0.33.0", - "nocodb-sdk": "0.107.0", + "nocodb-sdk": "file:../nocodb-sdk", "papaparse": "^5.3.2", "pinia": "^2.0.33", "qrcode": "^1.5.1", @@ -111,7 +111,6 @@ }, "../nocodb-sdk": { "version": "0.107.0", - "extraneous": true, "license": "AGPL-3.0-or-later", "dependencies": { "axios": "^0.21.1", @@ -8776,6 +8775,7 @@ "version": "1.15.1", "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.1.tgz", "integrity": "sha512-yLAMQs+k0b2m7cVxpS1VKJVvoz7SS9Td1zss3XRwXj+ZDH00RJgnuLx7E44wx02kQLrdM3aOOy+FpzS7+8OizA==", + "devOptional": true, "funding": [ { "type": "individual", @@ -12294,21 +12294,8 @@ } }, "node_modules/nocodb-sdk": { - "version": "0.107.0", - "resolved": "https://registry.npmjs.org/nocodb-sdk/-/nocodb-sdk-0.107.0.tgz", - "integrity": "sha512-g/+oIwUY3T3e6RHGxmKKW7AShIfFeYnzxedFXiZVGKFUwKX2PAuKuaq+zdfirKyuEuSZYQq2fAzcdDOWTNUU/w==", - "dependencies": { - "axios": "^0.21.1", - "jsep": "^1.3.6" - } - }, - "node_modules/nocodb-sdk/node_modules/axios": { - "version": "0.21.4", - "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz", - "integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==", - "dependencies": { - "follow-redirects": "^1.14.0" - } + "resolved": "../nocodb-sdk", + "link": true }, "node_modules/node-abi": { "version": "3.23.0", @@ -24810,7 +24797,8 @@ "follow-redirects": { "version": "1.15.1", "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.1.tgz", - "integrity": "sha512-yLAMQs+k0b2m7cVxpS1VKJVvoz7SS9Td1zss3XRwXj+ZDH00RJgnuLx7E44wx02kQLrdM3aOOy+FpzS7+8OizA==" + "integrity": "sha512-yLAMQs+k0b2m7cVxpS1VKJVvoz7SS9Td1zss3XRwXj+ZDH00RJgnuLx7E44wx02kQLrdM3aOOy+FpzS7+8OizA==", + "devOptional": true }, "form-data": { "version": "4.0.0", @@ -27360,22 +27348,22 @@ } }, "nocodb-sdk": { - "version": "0.107.0", - "resolved": "https://registry.npmjs.org/nocodb-sdk/-/nocodb-sdk-0.107.0.tgz", - "integrity": "sha512-g/+oIwUY3T3e6RHGxmKKW7AShIfFeYnzxedFXiZVGKFUwKX2PAuKuaq+zdfirKyuEuSZYQq2fAzcdDOWTNUU/w==", + "version": "file:../nocodb-sdk", "requires": { + "@typescript-eslint/eslint-plugin": "^4.0.1", + "@typescript-eslint/parser": "^4.0.1", "axios": "^0.21.1", - "jsep": "^1.3.6" - }, - "dependencies": { - "axios": { - "version": "0.21.4", - "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz", - "integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==", - "requires": { - "follow-redirects": "^1.14.0" - } - } + "cspell": "^4.1.0", + "eslint": "^7.8.0", + "eslint-config-prettier": "^6.11.0", + "eslint-plugin-eslint-comments": "^3.2.0", + "eslint-plugin-functional": "^3.0.2", + "eslint-plugin-import": "^2.22.0", + "eslint-plugin-prettier": "^4.0.0", + "jsep": "^1.3.6", + "npm-run-all": "^4.1.5", + "prettier": "^2.1.1", + "typescript": "^4.0.2" } }, "node-abi": { diff --git a/packages/nc-gui/package.json b/packages/nc-gui/package.json index 49d269d03e..847017b499 100644 --- a/packages/nc-gui/package.json +++ b/packages/nc-gui/package.json @@ -54,7 +54,7 @@ "leaflet.markercluster": "^1.5.3", "locale-codes": "^1.3.1", "monaco-editor": "^0.33.0", - "nocodb-sdk": "0.107.0", + "nocodb-sdk": "file:../nocodb-sdk", "papaparse": "^5.3.2", "pinia": "^2.0.33", "qrcode": "^1.5.1", diff --git a/packages/nocodb-sdk/package-lock.json b/packages/nocodb-sdk/package-lock.json index e967c4ad68..2490203172 100644 --- a/packages/nocodb-sdk/package-lock.json +++ b/packages/nocodb-sdk/package-lock.json @@ -1,12 +1,12 @@ { "name": "nocodb-sdk", - "version": "0.107.0-beta.1", + "version": "0.107.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "nocodb-sdk", - "version": "0.107.0-beta.1", + "version": "0.107.0", "license": "AGPL-3.0-or-later", "dependencies": { "axios": "^0.21.1", diff --git a/packages/nocodb/package-lock.json b/packages/nocodb/package-lock.json index ab8892041c..f0e16168b1 100644 --- a/packages/nocodb/package-lock.json +++ b/packages/nocodb/package-lock.json @@ -83,7 +83,7 @@ "nc-lib-gui": "0.107.0", "nc-plugin": "^0.1.3", "ncp": "^2.0.0", - "nocodb-sdk": "0.107.0", + "nocodb-sdk": "file:../nocodb-sdk", "nodemailer": "^6.4.10", "object-hash": "^3.0.0", "os-locale": "^6.0.2", @@ -191,7 +191,6 @@ }, "../nocodb-sdk": { "version": "0.107.0", - "extraneous": true, "license": "AGPL-3.0-or-later", "dependencies": { "axios": "^0.21.1", @@ -13207,13 +13206,8 @@ } }, "node_modules/nocodb-sdk": { - "version": "0.107.0", - "resolved": "https://registry.npmjs.org/nocodb-sdk/-/nocodb-sdk-0.107.0.tgz", - "integrity": "sha512-g/+oIwUY3T3e6RHGxmKKW7AShIfFeYnzxedFXiZVGKFUwKX2PAuKuaq+zdfirKyuEuSZYQq2fAzcdDOWTNUU/w==", - "dependencies": { - "axios": "^0.21.1", - "jsep": "^1.3.6" - } + "resolved": "../nocodb-sdk", + "link": true }, "node_modules/node-abort-controller": { "version": "3.1.1", @@ -28485,12 +28479,22 @@ "integrity": "sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg==" }, "nocodb-sdk": { - "version": "0.107.0", - "resolved": "https://registry.npmjs.org/nocodb-sdk/-/nocodb-sdk-0.107.0.tgz", - "integrity": "sha512-g/+oIwUY3T3e6RHGxmKKW7AShIfFeYnzxedFXiZVGKFUwKX2PAuKuaq+zdfirKyuEuSZYQq2fAzcdDOWTNUU/w==", + "version": "file:../nocodb-sdk", "requires": { + "@typescript-eslint/eslint-plugin": "^4.0.1", + "@typescript-eslint/parser": "^4.0.1", "axios": "^0.21.1", - "jsep": "^1.3.6" + "cspell": "^4.1.0", + "eslint": "^7.8.0", + "eslint-config-prettier": "^6.11.0", + "eslint-plugin-eslint-comments": "^3.2.0", + "eslint-plugin-functional": "^3.0.2", + "eslint-plugin-import": "^2.22.0", + "eslint-plugin-prettier": "^4.0.0", + "jsep": "^1.3.6", + "npm-run-all": "^4.1.5", + "prettier": "^2.1.1", + "typescript": "^4.0.2" } }, "node-abort-controller": { diff --git a/packages/nocodb/package.json b/packages/nocodb/package.json index 85f5af76c0..27fc8429f0 100644 --- a/packages/nocodb/package.json +++ b/packages/nocodb/package.json @@ -114,7 +114,7 @@ "nc-lib-gui": "0.107.0", "nc-plugin": "^0.1.3", "ncp": "^2.0.0", - "nocodb-sdk": "0.107.0", + "nocodb-sdk": "file:../nocodb-sdk", "nodemailer": "^6.4.10", "object-hash": "^3.0.0", "os-locale": "^6.0.2", @@ -201,4 +201,4 @@ "coverageDirectory": "../coverage", "testEnvironment": "node" } -} \ No newline at end of file +} diff --git a/packages/nocodb/src/Noco.ts b/packages/nocodb/src/Noco.ts index e6c1fea34a..942ce717f6 100644 --- a/packages/nocodb/src/Noco.ts +++ b/packages/nocodb/src/Noco.ts @@ -1,4 +1,5 @@ -// import * as Sentry from '@sentry/node'; +import Sentry, { Handlers } from '@sentry/node'; +import { Logger } from '@nestjs/common'; import { NestFactory } from '@nestjs/core'; import clear from 'clear'; import * as express from 'express'; @@ -6,20 +7,26 @@ import NcToolGui from 'nc-lib-gui'; import { IoAdapter } from '@nestjs/platform-socket.io'; import requestIp from 'request-ip'; import cookieParser from 'cookie-parser'; +import { T } from 'nc-help'; +import { v4 as uuidv4 } from 'uuid'; +import dotenv from 'dotenv'; import { AppModule } from './app.module'; - import { NC_LICENSE_KEY } from './constants'; import Store from './models/Store'; +import { MetaTable } from './utils/globals'; import type { IEventEmitter } from './modules/event-emitter/event-emitter.interface'; import type { Express } from 'express'; import type * as http from 'http'; +dotenv.config(); + export default class Noco { private static _this: Noco; private static ee: boolean; public static readonly env: string = '_noco'; private static _httpServer: http.Server; private static _server: Express; + private static logger = new Logger(Noco.name); public static get dashboardUrl(): string { let siteUrl = `http://localhost:${process.env.PORT || 8080}`; @@ -42,7 +49,6 @@ export default class Noco { public readonly metaMgrv2: any; public env: string; - private ncToolApi; private config: any; private requestContext: any; @@ -102,6 +108,8 @@ export default class Noco { const nestApp = await NestFactory.create(AppModule); + this.initSentry(nestApp); + nestApp.useWebSocketAdapter(new IoAdapter(httpServer)); nestApp.use(requestIp.mw()); @@ -115,6 +123,9 @@ export default class Noco { const dashboardPath = process.env.NC_DASHBOARD_URL || '/dashboard'; server.use(NcToolGui.expressMiddleware(dashboardPath)); server.get('/', (_req, res) => res.redirect(dashboardPath)); + + this.initSentryErrorHandler(server); + return nestApp.getHttpAdapter().getInstance(); } @@ -125,4 +136,56 @@ export default class Noco { public static get server(): Express { return Noco._server; } + + public static async initJwt(): Promise { + if (this.config?.auth?.jwt) { + if (!this.config.auth.jwt.secret) { + let secret = ( + await Noco._ncMeta.metaGet('', '', MetaTable.STORE, { + key: 'nc_auth_jwt_secret', + }) + )?.value; + if (!secret) { + await Noco._ncMeta.metaInsert('', '', MetaTable.STORE, { + key: 'nc_auth_jwt_secret', + value: (secret = uuidv4()), + }); + } + this.config.auth.jwt.secret = secret; + } + + this.config.auth.jwt.options = this.config.auth.jwt.options || {}; + if (!this.config.auth.jwt.options?.expiresIn) { + this.config.auth.jwt.options.expiresIn = + process.env.NC_JWT_EXPIRES_IN ?? '10h'; + } + } + let serverId = ( + await Noco._ncMeta.metaGet('', '', MetaTable.STORE, { + key: 'nc_server_id', + }) + )?.value; + if (!serverId) { + await Noco._ncMeta.metaInsert('', '', MetaTable.STORE, { + key: 'nc_server_id', + value: (serverId = T.id), + }); + } + process.env.NC_SERVER_UUID = serverId; + } + + private static initSentryErrorHandler(router) { + if (process.env.NC_SENTRY_DSN) { + router.use(Handlers.errorHandler()); + } + } + + private static initSentry(router) { + if (process.env.NC_SENTRY_DSN) { + Sentry.init({ dsn: process.env.NC_SENTRY_DSN }); + + // The request handler must be the first middleware on the app + router.use(Handlers.requestHandler()); + } + } } diff --git a/packages/nocodb/src/app.module.ts b/packages/nocodb/src/app.module.ts index 5c9c908ccf..e87e17a37e 100644 --- a/packages/nocodb/src/app.module.ts +++ b/packages/nocodb/src/app.module.ts @@ -1,35 +1,25 @@ -import { Inject, Module, RequestMethod } from '@nestjs/common'; +import { Module, RequestMethod } from '@nestjs/common'; import { APP_FILTER } from '@nestjs/core'; import { BullModule } from '@nestjs/bull'; import { EventEmitterModule as NestJsEventEmitter } from '@nestjs/event-emitter'; -import { Connection } from './connection/connection'; import { GlobalExceptionFilter } from './filters/global-exception/global-exception.filter'; -import NcPluginMgrv2 from './helpers/NcPluginMgrv2'; import { GlobalMiddleware } from './middlewares/global/global.middleware'; import { GuiMiddleware } from './middlewares/gui/gui.middleware'; import { PublicMiddleware } from './middlewares/public/public.middleware'; import { DatasModule } from './modules/datas/datas.module'; -import { IEventEmitter } from './modules/event-emitter/event-emitter.interface'; import { EventEmitterModule } from './modules/event-emitter/event-emitter.module'; import { AuthService } from './services/auth.service'; import { UsersModule } from './modules/users/users.module'; -import { MetaService } from './meta/meta.service'; -import Noco from './Noco'; import { TestModule } from './modules/test/test.module'; import { GlobalModule } from './modules/global/global.module'; import { HookHandlerService } from './services/hook-handler.service'; import { LocalStrategy } from './strategies/local.strategy'; import { AuthTokenStrategy } from './strategies/authtoken.strategy/authtoken.strategy'; import { BaseViewStrategy } from './strategies/base-view.strategy/base-view.strategy'; -import NcConfigFactory from './utils/NcConfigFactory'; -import NcUpgrader from './version-upgrader/NcUpgrader'; import { MetasModule } from './modules/metas/metas.module'; -import NocoCache from './cache/NocoCache'; import { JobsModule } from './modules/jobs/jobs.module'; -import type { - MiddlewareConsumer, - OnApplicationBootstrap, -} from '@nestjs/common'; +import { AppInitService } from './services/app-init.service'; +import type { MiddlewareConsumer } from '@nestjs/common'; @Module({ imports: [ @@ -60,15 +50,10 @@ import type { AuthTokenStrategy, BaseViewStrategy, HookHandlerService, + AppInitService, ], }) -export class AppModule implements OnApplicationBootstrap { - constructor( - private readonly connection: Connection, - private readonly metaService: MetaService, - @Inject('IEventEmitter') private readonly eventEmitter: IEventEmitter, - ) {} - +export class AppModule { // Global Middleware configure(consumer: MiddlewareConsumer) { consumer @@ -79,30 +64,4 @@ export class AppModule implements OnApplicationBootstrap { .apply(GlobalMiddleware) .forRoutes({ path: '*', method: RequestMethod.ALL }); } - - // app init - async onApplicationBootstrap(): Promise { - process.env.NC_VERSION = '0105004'; - - await NocoCache.init(); - - await this.connection.init(); - - await NcConfigFactory.metaDbCreateIfNotExist(this.connection.config); - - await this.metaService.init(); - - // todo: remove - // temporary hack - Noco._ncMeta = this.metaService; - Noco.config = this.connection.config; - Noco.eventEmitter = this.eventEmitter; - - // init plugin manager - await NcPluginMgrv2.init(Noco.ncMeta); - await Noco.loadEEState(); - - // run upgrader - await NcUpgrader.upgrade({ ncMeta: Noco._ncMeta }); - } } diff --git a/packages/nocodb/src/modules/global/global.module.ts b/packages/nocodb/src/modules/global/global.module.ts index 12dd650325..b7aa2d6005 100644 --- a/packages/nocodb/src/modules/global/global.module.ts +++ b/packages/nocodb/src/modules/global/global.module.ts @@ -1,19 +1,27 @@ import { Global, Module } from '@nestjs/common'; -import { JwtModule, JwtService } from '@nestjs/jwt'; import { ExtractJwt } from 'passport-jwt'; +import { + AppInitService, + appInitServiceProvider, +} from '../../services/app-init.service'; import { SocketGateway } from '../../gateways/socket.gateway'; import { Connection } from '../../connection/connection'; import { GlobalGuard } from '../../guards/global/global.guard'; import { MetaService } from '../../meta/meta.service'; +import Noco from '../../Noco'; import { JwtStrategy } from '../../strategies/jwt.strategy'; -import NcConfigFactory from '../../utils/NcConfigFactory'; import { UsersService } from '../../services/users/users.service'; import type { Provider } from '@nestjs/common'; export const JwtStrategyProvider: Provider = { provide: JwtStrategy, - useFactory: async (usersService: UsersService) => { - const config = await NcConfigFactory.make(); + useFactory: async ( + usersService: UsersService, + appInitService: AppInitService, + ) => { + const config = appInitService.appConfig; + + await Noco.initJwt(); const options = { // ignoreExpiration: false, @@ -26,13 +34,14 @@ export const JwtStrategyProvider: Provider = { return new JwtStrategy(options, usersService); }, - inject: [UsersService], + inject: [UsersService, AppInitService], }; @Global() @Module({ imports: [], providers: [ + appInitServiceProvider, Connection, MetaService, UsersService, @@ -41,6 +50,7 @@ export const JwtStrategyProvider: Provider = { SocketGateway, ], exports: [ + AppInitService, Connection, MetaService, JwtStrategyProvider, diff --git a/packages/nocodb/src/services/app-init.service.spec.ts b/packages/nocodb/src/services/app-init.service.spec.ts new file mode 100644 index 0000000000..ac84d33dfb --- /dev/null +++ b/packages/nocodb/src/services/app-init.service.spec.ts @@ -0,0 +1,19 @@ +import { Test } from '@nestjs/testing'; +import { AppInitService } from './app-init.service'; +import type { TestingModule } from '@nestjs/testing'; + +describe('AppInitService', () => { + let service: AppInitService; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + providers: [AppInitService], + }).compile(); + + service = module.get(AppInitService); + }); + + it('should be defined', () => { + expect(service).toBeDefined(); + }); +}); diff --git a/packages/nocodb/src/services/app-init.service.ts b/packages/nocodb/src/services/app-init.service.ts new file mode 100644 index 0000000000..b305425347 --- /dev/null +++ b/packages/nocodb/src/services/app-init.service.ts @@ -0,0 +1,68 @@ +import { Inject, Injectable } from '@nestjs/common'; +import NocoCache from '../cache/NocoCache'; +import { Connection } from '../connection/connection'; +import NcPluginMgrv2 from '../helpers/NcPluginMgrv2'; +import { MetaService } from '../meta/meta.service'; +import Noco from '../Noco'; +import NcConfigFactory from '../utils/NcConfigFactory'; +import NcUpgrader from '../version-upgrader/NcUpgrader'; +import type { IEventEmitter } from '../modules/event-emitter/event-emitter.interface'; +import type { Provider } from '@nestjs/common'; + +export class AppInitService { + private readonly config: any; + + constructor(config) { + this.config = config; + } + + get appConfig(): any { + return this.config; + } +} + +export const appInitServiceProvider: Provider = { + provide: AppInitService, + // initialize app, + // 1. init cache + // 2. init db connection and create if not exist + // 3. init meta and set to Noco + // 4. init jwt + // 5. init plugin manager + // 6. run upgrader + useFactory: async ( + connection: Connection, + metaService: MetaService, + eventEmitter: IEventEmitter, + ) => { + process.env.NC_VERSION = '0105004'; + + await NocoCache.init(); + + await connection.init(); + + await NcConfigFactory.metaDbCreateIfNotExist(connection.config); + + await metaService.init(); + + // todo: remove + // temporary hack + Noco._ncMeta = metaService; + Noco.config = connection.config; + Noco.eventEmitter = eventEmitter; + + // init jwt secret + await Noco.initJwt(); + + // init plugin manager + await NcPluginMgrv2.init(Noco.ncMeta); + await Noco.loadEEState(); + + // run upgrader + await NcUpgrader.upgrade({ ncMeta: Noco._ncMeta }); + + // todo: move app config to app-init service + return new AppInitService(connection.config); + }, + inject: [Connection, MetaService, 'IEventEmitter'], +}; diff --git a/packages/nocodb/src/utils/NcConfigFactory.ts b/packages/nocodb/src/utils/NcConfigFactory.ts index 5fd54e6676..5a9222d998 100644 --- a/packages/nocodb/src/utils/NcConfigFactory.ts +++ b/packages/nocodb/src/utils/NcConfigFactory.ts @@ -100,7 +100,7 @@ export default class NcConfigFactory { ncConfig.auth = { jwt: { - secret: process.env.NC_AUTH_JWT_SECRET ?? 'temporary-key', + secret: process.env.NC_AUTH_JWT_SECRET, }, }; @@ -421,7 +421,7 @@ export default class NcConfigFactory { if (process.env.NC_AUTH_ADMIN_SECRET) { config.auth = { masterKey: { - secret: process.env.NC_AUTH_ADMIN_SECRET ?? 'temporary-key', + secret: process.env.NC_AUTH_ADMIN_SECRET, }, }; } else if (process.env.NC_NO_AUTH) { @@ -436,7 +436,7 @@ export default class NcConfigFactory { dbAlias: process.env.NC_AUTH_JWT_DB_ALIAS || config.envs['_noco'].db[0].meta.dbAlias, - secret: process.env.NC_AUTH_JWT_SECRET ?? 'temporary-key', + secret: process.env.NC_AUTH_JWT_SECRET, }, }; } @@ -536,7 +536,7 @@ export default class NcConfigFactory { if (process.env.NC_AUTH_ADMIN_SECRET) { config.auth = { masterKey: { - secret: process.env.NC_AUTH_ADMIN_SECRET ?? 'temporary-key', + secret: process.env.NC_AUTH_ADMIN_SECRET, }, }; } else if (process.env.NC_NO_AUTH) { @@ -551,7 +551,7 @@ export default class NcConfigFactory { dbAlias: process.env.NC_AUTH_JWT_DB_ALIAS || config.envs['_noco'].db[0].meta.dbAlias, - secret: process.env.NC_AUTH_JWT_SECRET ?? 'temporary-key', + secret: process.env.NC_AUTH_JWT_SECRET, }, }; } diff --git a/tests/playwright/pages/Dashboard/common/Cell/SelectOptionCell.ts b/tests/playwright/pages/Dashboard/common/Cell/SelectOptionCell.ts index 219d03ba2c..924060afcc 100644 --- a/tests/playwright/pages/Dashboard/common/Cell/SelectOptionCell.ts +++ b/tests/playwright/pages/Dashboard/common/Cell/SelectOptionCell.ts @@ -118,7 +118,7 @@ export class SelectOptionCellPageObject extends BasePage { index, columnHeader, option, - multiSelect, + multiSelect = false, }: { index: number; columnHeader: string; @@ -134,10 +134,18 @@ export class SelectOptionCellPageObject extends BasePage { await selectCell.locator('.ant-select-selection-search-input').type(option); - await selectCell.locator('.ant-select-selection-search-input').press('Enter'); + // await selectCell.locator('.ant-select-selection-search-input').press('Enter'); + + // Wait for update api call + const saveRowAction = () => selectCell.locator('.ant-select-selection-search-input').press('Enter'); + await this.waitForResponse({ + uiAction: saveRowAction, + requestUrlPathToMatch: 'api/v1/db/data/noco/', + httpMethodsToMatch: ['PATCH'], + responseJsonMatcher: resJson => String(resJson?.[columnHeader]).includes(String(option)), + }); if (multiSelect) await selectCell.locator('.ant-select-selection-search-input').press('Escape'); - // todo: wait for update api call } async verifySelectedOptions({ diff --git a/tests/playwright/pages/Dashboard/common/Toolbar/index.ts b/tests/playwright/pages/Dashboard/common/Toolbar/index.ts index 6f756f21f1..afcaa3e824 100644 --- a/tests/playwright/pages/Dashboard/common/Toolbar/index.ts +++ b/tests/playwright/pages/Dashboard/common/Toolbar/index.ts @@ -66,6 +66,7 @@ export class ToolbarPage extends BasePage { // Wait for the menu to close if (menuOpen) await this.fields.get().waitFor({ state: 'hidden' }); + else await this.fields.get().waitFor({ state: 'visible' }); } async clickFindRowByScanButton() { diff --git a/tests/playwright/tests/db/undo-redo.spec.ts b/tests/playwright/tests/db/undo-redo.spec.ts index c798910d7a..0665df93c4 100644 --- a/tests/playwright/tests/db/undo-redo.spec.ts +++ b/tests/playwright/tests/db/undo-redo.spec.ts @@ -178,6 +178,10 @@ test.describe('Undo Redo', () => { await dashboard.closeTab({ title: 'Team & Auth' }); await dashboard.treeView.openTable({ title: 'numberBased' }); + // hack: wait for grid to load + // https://github.com/nocodb/nocodb/actions/runs/5025773509/jobs/9013176970 + await page.waitForTimeout(1000); + await verifyFieldsOrder(['Number', 'Decimal', 'Currency']); // Hide Decimal