diff --git a/packages/test/.gitignore b/packages/test/.gitignore new file mode 100644 index 000000000..1be219988 --- /dev/null +++ b/packages/test/.gitignore @@ -0,0 +1,4 @@ +node_modules +yarn-error.log +.env +src2 \ No newline at end of file diff --git a/packages/test/.npmignore b/packages/test/.npmignore new file mode 100644 index 000000000..461574b2f --- /dev/null +++ b/packages/test/.npmignore @@ -0,0 +1,7 @@ +node_modules +*.log +docs +__tests__ +tsconfig.json +src +.fatherrc.ts \ No newline at end of file diff --git a/packages/test/package.json b/packages/test/package.json new file mode 100644 index 000000000..a0e9a6beb --- /dev/null +++ b/packages/test/package.json @@ -0,0 +1,18 @@ +{ + "name": "@nocobase/test", + "version": "0.4.0-alpha.7", + "main": "lib/index.js", + "types": "./lib/index.d.ts", + "license": "MIT", + "scripts": {}, + "dependencies": { + "@nocobase/server": "^0.4.0-alpha.7", + "@types/supertest": "^2.0.11", + "mockjs": "^1.1.0", + "mysql2": "^2.1.0", + "pg": "^8.3.3", + "pg-hstore": "^2.3.3", + "supertest": "^6.1.6" + }, + "gitHead": "f0b335ac30f29f25c95d7d137655fa64d8d67f1e" +} diff --git a/packages/test/src/__tests__/mockDatabase.test.ts b/packages/test/src/__tests__/mockDatabase.test.ts new file mode 100644 index 000000000..bbb60d1e1 --- /dev/null +++ b/packages/test/src/__tests__/mockDatabase.test.ts @@ -0,0 +1,16 @@ +import { mockDatabase } from "../"; + +describe('mock databasea', () => { + it('mock databasea', async () => { + const db = mockDatabase(); + db.table({ + name: 'tests', + fields: [ + { type: 'string', name: 'name' }, + ], + }); + expect(db.getModel('tests').getTableName()).toBe('_test_mockDatabase_tests'); + await db.sync(); + await db.close(); + }); +}); diff --git a/packages/test/src/__tests__/mockServer.test.ts b/packages/test/src/__tests__/mockServer.test.ts new file mode 100644 index 000000000..8d9e7a224 --- /dev/null +++ b/packages/test/src/__tests__/mockServer.test.ts @@ -0,0 +1,34 @@ +import { mockServer, MockServer } from "../"; + +describe('mock server', () => { + let api: MockServer; + + beforeEach(() => { + api = mockServer({ + dataWrapping: false, + }); + api.resourcer.registerActionHandlers({ + list: async (ctx, next) => { + ctx.body = [1, 2]; + await next(); + }, + }); + api.resourcer.define({ + name: 'test', + }); + }); + + afterEach(async () => { + return api.destroy(); + }); + + it('agent', async () => { + const response = await api.agent().get('/test'); + expect(response.body).toEqual([1, 2]); + }); + + it('resource', async () => { + const response = await api.resource('test').list(); + expect(response.body).toEqual([1, 2]); + }); +}); diff --git a/packages/test/src/index.ts b/packages/test/src/index.ts new file mode 100644 index 000000000..63badf75a --- /dev/null +++ b/packages/test/src/index.ts @@ -0,0 +1,2 @@ +export * from './mockDatabase'; +export * from './mockServer'; diff --git a/packages/test/src/mockDatabase.ts b/packages/test/src/mockDatabase.ts new file mode 100644 index 000000000..4854e2322 --- /dev/null +++ b/packages/test/src/mockDatabase.ts @@ -0,0 +1,40 @@ +import merge from 'deepmerge'; +import Database, { DatabaseOptions } from '@nocobase/database'; + +export function generatePrefixByPath() { + const { id } = require.main; + const key = id + .replace(`${process.env.PWD}/packages`, '') + .replace(/src\/__tests__/g, '') + .replace('.test.ts', '') + .replace(/[^\w]/g, '_') + .replace(/_+/g, '_'); + return key +} + +export function getConfig(config = {}, options?: any): DatabaseOptions { + return merge({ + username: process.env.DB_USER, + password: process.env.DB_PASSWORD, + database: process.env.DB_DATABASE, + host: process.env.DB_HOST, + port: process.env.DB_PORT, + dialect: process.env.DB_DIALECT, + logging: process.env.DB_LOG_SQL === 'on', + sync: { + force: true, + alter: { + drop: true, + }, + }, + hooks: { + beforeDefine(model, options) { + options.tableName = `${generatePrefixByPath()}_${options.tableName || options.name.plural}`; + }, + }, + }, config || {}, options) as any; +}; + +export function mockDatabase(options?: DatabaseOptions): Database { + return new Database(getConfig(options)); +} diff --git a/packages/test/src/mockServer.ts b/packages/test/src/mockServer.ts new file mode 100644 index 000000000..9c1cc8aa5 --- /dev/null +++ b/packages/test/src/mockServer.ts @@ -0,0 +1,74 @@ +import qs from 'qs'; +import supertest from 'supertest'; +import Application, { ApplicationOptions } from '@nocobase/server'; +import { ActionParams } from '@nocobase/resourcer'; +import { getConfig } from './mockDatabase'; + +interface Resource { + get: (params?: ActionParams) => Promise; + list: (params?: ActionParams) => Promise; + create: (params?: ActionParams) => Promise; + update: (params?: ActionParams) => Promise; + destroy: (params?: ActionParams) => Promise; + [name: string]: (params?: ActionParams) => Promise; +} + +export class MockServer extends Application { + agent() { + return supertest.agent(this.callback()); + } + + resource(name: string) { + const agent = this.agent(); + const keys = name.split('.'); + const prefix = this.resourcer.options.prefix; + const proxy = new Proxy({}, { + get(target, method: string, receiver) { + return (params: ActionParams = {}) => { + const { + associatedKey, + resourceKey, + values = {}, + file, + ...restParams + } = params; + let url = prefix; + if (keys.length > 1) { + url = `/${keys[0]}/${associatedKey}/${keys[1]}}` + } else { + url = `/${name}`; + } + url += `:${method as string}`; + if (resourceKey) { + url += `/${resourceKey}`; + } + console.log('request url: ' + url); + switch (method) { + case 'upload': + return agent + .post(`${url}?${qs.stringify(restParams)}`) + .attach('file', file) + .field(values); + case 'list': + case 'get': + return agent.get(`${url}?${qs.stringify(restParams)}`); + default: + return agent + .post(`${url}?${qs.stringify(restParams)}`) + .send(values); + } + }; + }, + }); + return proxy as Resource; + } +} + +export function mockServer(options?: ApplicationOptions) { + return new MockServer({ + ...options, + database: getConfig(options?.database), + }); +} + +export default mockServer; diff --git a/yarn.lock b/yarn.lock index 90f958d05..94497bb89 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3604,6 +3604,11 @@ resolved "https://registry.npmjs.org/@types/content-disposition/-/content-disposition-0.5.4.tgz#de48cf01c79c9f1560bcfd8ae43217ab028657f8" integrity sha512-0mPF08jn9zYI0n0Q/Pnz7C4kThdSt+6LD4amsrYDDpgBfrVWa3TcCOxKX1zkGgYniGagRv8heN2cbh+CAn+uuQ== +"@types/cookiejar@*": + version "2.1.2" + resolved "https://registry.npmjs.org/@types/cookiejar/-/cookiejar-2.1.2.tgz#66ad9331f63fe8a3d3d9d8c6e3906dd10f6446e8" + integrity sha512-t73xJJrvdTjXrn4jLS9VSGRbz0nUY3cl2DMGDU48lKl+HR9dbbjW2A9r3g40VA++mQpy6uuHg33gy7du2BKpog== + "@types/cookies@*": version "0.7.7" resolved "https://registry.npmjs.org/@types/cookies/-/cookies-0.7.7.tgz#7a92453d1d16389c05a5301eef566f34946cfd81" @@ -4026,6 +4031,21 @@ resolved "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz#20f18294f797f2209b5f65c8e3b5c8e8261d127c" integrity sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw== +"@types/superagent@*": + version "4.1.12" + resolved "https://registry.npmjs.org/@types/superagent/-/superagent-4.1.12.tgz#fad68c6712936892ad24cf94f2f7a07cc749fd0f" + integrity sha512-1GQvD6sySQPD6p9EopDFI3f5OogdICl1sU/2ij3Esobz/RtL9fWZZDPmsuv7eiy5ya+XNiPAxUcI3HIUTJa+3A== + dependencies: + "@types/cookiejar" "*" + "@types/node" "*" + +"@types/supertest@^2.0.11": + version "2.0.11" + resolved "https://registry.npmjs.org/@types/supertest/-/supertest-2.0.11.tgz#2e70f69f220bc77b4f660d72c2e1a4231f44a77d" + integrity sha512-uci4Esokrw9qGb9bvhhSVEjd6rkny/dk5PK/Qz4yxKiyppEI+dOPlNrZBahE3i+PoKFYyDxChVXZ/ysS/nrm1Q== + dependencies: + "@types/superagent" "*" + "@types/unist@*", "@types/unist@^2.0.0", "@types/unist@^2.0.2", "@types/unist@^2.0.3": version "2.0.6" resolved "https://registry.npmjs.org/@types/unist/-/unist-2.0.6.tgz#250a7b16c3b91f672a24552ec64678eeb1d3a08d" @@ -7348,11 +7368,6 @@ dayjs@1.x, dayjs@^1.9.1: resolved "https://registry.npmjs.org/dayjs/-/dayjs-1.10.6.tgz#288b2aa82f2d8418a6c9d4df5898c0737ad02a63" integrity sha512-AztC/IOW4L1Q41A86phW5Thhcrco3xuAA+YX/BLpLWWjRcTj5TOt/QImBLmCKlrF7u7k47arTnOyL6GnbG8Hvw== -debounce@^1.2.0: - version "1.2.1" - resolved "https://registry.npmjs.org/debounce/-/debounce-1.2.1.tgz#38881d8f4166a5c5848020c11827b834bcb3e0a5" - integrity sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug== - debug-fabulous@1.X: version "1.1.0" resolved "https://registry.npmjs.org/debug-fabulous/-/debug-fabulous-1.1.0.tgz#af8a08632465224ef4174a9f06308c3c2a1ebc8e" @@ -7895,11 +7910,6 @@ dva@^2.6.0-beta.20: react-router-dom "^5.1.2" redux "^4.0.1" -easy-bem@^1.1.1: - version "1.1.1" - resolved "https://registry.npmjs.org/easy-bem/-/easy-bem-1.1.1.tgz#1bfcc10425498090bcfddc0f9c000aba91399e03" - integrity sha512-GJRqdiy2h+EXy6a8E6R+ubmqUM08BK0FWNq41k24fup6045biQ8NXxoXimiwegMQvFFV3t1emADdGNL1TlS61A== - ecc-jsbn@~0.1.1: version "0.1.2" resolved "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9" @@ -16250,15 +16260,6 @@ react-image-lightbox@^5.1.4: prop-types "^15.7.2" react-modal "^3.11.1" -react-indiana-drag-scroll@^2.0.1: - version "2.0.1" - resolved "https://registry.npmjs.org/react-indiana-drag-scroll/-/react-indiana-drag-scroll-2.0.1.tgz#8785488a9e9c789b0e1c092e60d03f05a789d428" - integrity sha512-xZQXfFf9pHLOL6/AvTFiGZCB4Vu8wO8DjEaO+xRVkSki2bEZSFfS8eeKG0OkTzcYVrXzc5aHAD1Mfl3EG+EFMA== - dependencies: - classnames "^2.2.6" - debounce "^1.2.0" - easy-bem "^1.1.1" - react-intl@3.12.1: version "3.12.1" resolved "https://registry.npmjs.org/react-intl/-/react-intl-3.12.1.tgz#e9a783ea20302e9da25e4eda59e5593a43d2ec80" @@ -18447,6 +18448,14 @@ supertest@^6.1.3: methods "^1.1.2" superagent "^6.1.0" +supertest@^6.1.6: + version "6.1.6" + resolved "https://registry.npmjs.org/supertest/-/supertest-6.1.6.tgz#6151c518f4c5ced2ac2aadb9f96f1bf8198174c8" + integrity sha512-0hACYGNJ8OHRg8CRITeZOdbjur7NLuNs0mBjVhdpxi7hP6t3QIbOzLON5RTUmZcy2I9riuII3+Pr2C7yztrIIg== + dependencies: + methods "^1.1.2" + superagent "^6.1.0" + supports-color@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7"