diff --git a/package.json b/package.json index 41936c96..04f87076 100644 --- a/package.json +++ b/package.json @@ -9,8 +9,7 @@ "@capire/common": "file:common", "@capire/fiori": "file:fiori", "@capire/orders": "file:orders", - "@capire/reviews": "file:reviews", - "@capire/tests": "file:test" + "@capire/reviews": "file:reviews" }, "devDependencies": { "chai": "^4.2.0", diff --git a/test/_cds_tests.js b/test/_cds_tests.js deleted file mode 100644 index 664d1472..00000000 --- a/test/_cds_tests.js +++ /dev/null @@ -1,151 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// -// This is included with cds in newer versions -// -//////////////////////////////////////////////////////////////////////////// - -const { resolve, dirname } = require('path') -const cwd = process.cwd() - -// harmonizing jest and mocha -const is_mocha = !global.test -const is_jest = !!global.test -if (is_jest) { // it's jest - global.before = (msg,fn) => global.beforeAll(fn||msg) - global.after = (msg,fn) => global.afterAll(fn||msg) -} else { // it's mocha - global.beforeAll = global.before - global.afterAll = global.after - global.test = global.it -} - -/** - * Test kit for jest or mocha testing, which can be used statically - * via the getters for chai, expect and assert or through a server - * started with .launch(...). - */ -class CDSTestKit { - - /** - * Creates a new instance serving projects from subfolders under the root - * specified by a sequence of path components which are concatenated with - * path.resolve(). - */ - for (...paths) { - const tk = new CDSTestKit - tk.root = resolve (...paths) - return tk - } - - /** Lazily loads and returns an instance of chai */ - get chai() { - const chai = require('chai') - chai.use (require('chai-as-promised')) - chai.use (require('chai-subset')) - Object.defineProperty (CDSTestKit.prototype, 'chai', {value:chai}) - return chai - } - get expect(){ return this.chai.expect } - get assert(){ return this.chai.assert } - - /** - * Launches a cds server with arbitrary port and returns a subclass which - * also acts as an axios lookalike, providing methods to send requests. - */ - launch (project, ...args) { - - // Setting up test server - const console = global.console, logs=[] - const axios = require('axios').default - const lazy = this, test = { - - GET: (path,...etc) => axios.get (test.url+path,...etc) .catch(_error), - PUT: (path,...etc) => axios.put (test.url+path,...etc) .catch(_error), - POST: (path,...etc) => axios.post (test.url+path,...etc) .catch(_error), - PATCH: (path,...etc) => axios.patch (test.url+path,...etc) .catch(_error), - DELETE: (path,...etc) => axios.delete (test.url+path,...etc) .catch(_error), - - get: (path,...etc) => axios.get (test.url+path,...etc) .catch(_error), - put: (path,...etc) => axios.put (test.url+path,...etc) .catch(_error), - post: (path,...etc) => axios.post (test.url+path,...etc) .catch(_error), - patch: (path,...etc) => axios.patch (test.url+path,...etc) .catch(_error), - delete: (path,...etc) => axios.delete (test.url+path,...etc) .catch(_error), - - /** Lazily loads and returns an instance of chai */ - get chai(){ return lazy.chai }, - get expect(){ return lazy.expect }, - get assert(){ return lazy.assert }, - } - - // launch cds server... - before (`launching cds server for ${project}...`, done => { - - const cds = require('@sap/cds'), { isdir } = cds.utils - - let cmd = 'run' - if (project.startsWith('cds ')) [ cmd, project ] = [ project.slice(4), args.shift() ] - if (!args.length) args = ['--in-memory?'] - - // Supporting .launch () - if (cmd === 'run') { - if (isdir(project)) ; //> all fine - else if (isdir(resolve(this.root,project))) project = resolve(this.root,project) - else try { project = dirname (require.resolve(project+'/package.json')) } - catch(e) { throw cds.error (`Cannot resolve project folder for '${project}'`) } - } - - if (!process.env.CDS_TEST_VERBOSE) global.console = { __proto__: global.console, logs, - time: ()=>{}, timeEnd: (...args)=> logs.push(args), - debug: (...args)=> logs.push(args), - log: (...args)=> logs.push(args), - warn: (...args)=> logs.push(args), - error: (...args)=> logs.push(args), - dump(){ for (let each of logs) console.log (...each) }, - } - - // return done (new Error(11)) - process.env.PORT = '0' - const p = cds.exec (cmd, project, ...args) // TODO w/ @sap/cds@3.33.3: , '--port', '0') - if (p && 'catch' in p) p.catch (e => { - if (is_mocha) console.error(e) - done(e) - }) - - cds.once('listening', ({ server, url }) => { - Object.assign (test,{server,url}) - done() - }) - }) - - // shutdown cds server... - after (done => { - if (global.console !== console) global.console = console - if (cwd !== process.cwd()) process.chdir(cwd) - test.server ? test.server.close (done) : done() - process.emit('shutdown') - }) - - function _error (e) { - if (!e.response) throw e - if (!e.response.data) throw e - if (!e.response.data.error) throw e - const { code, message } = e.response.data.error - throw new Error (code && code !== 'null' ? `${code} - ${message}` : message) - } - - return test - } - -} - -// eslint-disable-next-line no-global-assign -require = (mod) => { - try { return module.require(mod) } - catch(e) { if (e.code === 'MODULE_NOT_FOUND') throw new Error (` - Failed to load required package '${mod}'. Please add it thru: - npm add -D ${mod === 'chai' ? 'chai chai-as-promised chai-subset' : mod} - `) } -} - -module.exports = new CDSTestKit -exports.root = __dirname diff --git a/test/capire.js b/test/capire.js deleted file mode 100644 index d5145ad6..00000000 --- a/test/capire.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = require('./_cds_tests').for(__dirname,'..') \ No newline at end of file diff --git a/test/cds.js b/test/cds.js new file mode 100644 index 00000000..bca7bf00 --- /dev/null +++ b/test/cds.js @@ -0,0 +1,158 @@ +const cds = module.exports = require('@sap/cds/lib') +if (!cds.test) { // monkey patching cds + + const { resolve, dirname } = require('path') + const cwd = process.cwd() + + // harmonizing jest and mocha + const is_mocha = !global.test + const is_jest = !!global.test + if (is_jest) { // it's jest + global.before = (msg,fn) => global.beforeAll(fn||msg) + global.after = (msg,fn) => global.afterAll(fn||msg) + } else { // it's mocha + global.beforeAll = global.before + global.afterAll = global.after + global.test = global.it + } + + // eslint-disable-next-line no-global-assign + require = (mod) => { + try { return module.require(mod) } + catch(e) { if (e.code === 'MODULE_NOT_FOUND') throw new Error (` + Failed to load required package '${mod}'. Please add it thru: + npm add -D ${mod === 'chai' ? 'chai chai-as-promised chai-subset' : mod} + `) } + } + + + + class Test { + + /** + * Launches a cds server with arbitrary port and returns a subclass which + * also acts as an axios lookalike, providing methods to send requests. + * @returns {Test} + */ + static run (cmd, ...args) { + + // Setting up test server + const console = global.console, logs=[] + const axios = require('axios').default + const test = {__proto__:Test.run, + + GET: (path,...etc) => axios.get (test.url+path,...etc) .catch(_error), + PUT: (path,...etc) => axios.put (test.url+path,...etc) .catch(_error), + POST: (path,...etc) => axios.post (test.url+path,...etc) .catch(_error), + PATCH: (path,...etc) => axios.patch (test.url+path,...etc) .catch(_error), + DEL: (path,...etc) => axios.delete (test.url+path,...etc) .catch(_error), + + get: (path,...etc) => axios.get (test.url+path,...etc) .catch(_error), + put: (path,...etc) => axios.put (test.url+path,...etc) .catch(_error), + post: (path,...etc) => axios.post (test.url+path,...etc) .catch(_error), + patch: (path,...etc) => axios.patch (test.url+path,...etc) .catch(_error), + delete: (path,...etc) => axios.delete (test.url+path,...etc) .catch(_error), + + } + + // launch cds server... + before (`launching ${cmd} ${args.join(' ')}...`, done => { + + // const cds = require('../index'), + const { isdir } = cds.utils + if (!args.length) { + let project = cmd; cmd = 'run' + if (isdir(project)) ; //> all fine + // Supporting .launch () + else if (isdir(resolve(project))) project = resolve(project) + else try { project = dirname (require.resolve(project+'/package.json')) } + catch(e) { throw cds.error (`Cannot resolve project folder for '${project}' in '${process.cwd()}'`) } + args.push (project, '--in-memory?') + } + + if (!process.env.CDS_TEST_VERBOSE) global.console = { __proto__: global.console, logs, + time: ()=>{}, timeEnd: (...args)=> logs.push(args), + debug: (...args)=> logs.push(args), + log: (...args)=> logs.push(args), + warn: (...args)=> logs.push(args), + error: (...args)=> logs.push(args), + dump(){ for (let each of logs) console.log (...each) }, + } + + // return done (new Error(11)) + process.env.PORT = '0' + const p = cds.exec (cmd, ...args) // TODO w/ @sap/cds@3.33.3: , '--port', '0') + if (p && 'catch' in p) p.catch (e => { + if (is_mocha) console.error(e) + done(e) + }) + + cds.once('listening', ({ server, url }) => { + Object.assign (test,{server,url}) + done() + }) + }) + + // shutdown cds server... + after (done => { + if (global.console !== console) global.console = console + if (cwd !== process.cwd()) process.chdir(cwd) + test.server ? test.server.close (done) : done() + process.emit('shutdown') + }) + + function _error (e) { + if (!e.response) throw e + if (!e.response.data) throw e + if (!e.response.data.error) throw e + const { code, message } = e.response.data.error + throw new Error (code && code !== 'null' ? `${code} - ${message}` : message) + } + + return test + } + + /** + * Serving projects from subfolders under the root specified by a sequence + * of path components which are concatenated with path.resolve(). + */ + in (...paths) { + process.chdir (resolve (...paths)) + return this + } + + /** + * Switch on/off console log output. + */ + verbose(v) { + process.env.CDS_TEST_VERBOSE = v + return this + } + + /** Lazily loads and returns an instance of chai */ + get chai() { + const chai = require('chai') + chai.use (require('chai-subset')) + chai.use (require('chai-as-promised')) + Object.defineProperty (this, 'chai', {value:chai}) + return chai + } + get expect(){ return this.chai.expect } + get assert(){ return this.chai.assert } + } + + + /** + * Test kit for jest or mocha testing, which can be used statically + * via the getters for chai, expect and assert or through a server + * started with cds.test(...). + * @type Test.run & Test + */ + Object.defineProperties (Test.run, Object.getOwnPropertyDescriptors (Test.prototype)) + Object.defineProperty (cds, 'test', {value:Test.run}) + const cds_load = cds.load; cds.load = (models,o)=>{ + if (typeof models === 'string') models = models.split(',') + return cds_load.call(cds,models,o) + } + +} diff --git a/test/cds.ql.test.js b/test/cds.ql.test.js index 6b3b277a..ce82dc11 100644 --- a/test/cds.ql.test.js +++ b/test/cds.ql.test.js @@ -1,5 +1,4 @@ -const { expect } = require('./capire') -const cds = require('@sap/cds') +const cds = require('./cds'),{ expect } = cds.test const CQL = ([cql]) => cds.parse.cql(cql) const Foo = { name: 'Foo' } const Books = { name: 'capire.bookshop.Books' } diff --git a/test/consuming-services.test.js b/test/consuming-services.test.js index 54a8410b..4f308e11 100644 --- a/test/consuming-services.test.js +++ b/test/consuming-services.test.js @@ -1,23 +1,17 @@ -const { expect } = require('./capire') -const cds = require('@sap/cds') - -const cwd = process.cwd() -before (()=> process.chdir(__dirname)) -after(()=> process.chdir(cwd)) +const cds = require('./cds') +const { expect } = cds.test ( + 'serve', 'AdminService', '--from', '@capire/bookshop,@capire/common', '--in-memory' +).in(__dirname) describe('Consuming Services locally', () => { // - before('bootstrap db and services', async () => { - const model = await cds.load(['@capire/bookshop', '@capire/common']) - await cds.deploy(model).to('sqlite::memory:') - const { AdminService } = await cds.serve('AdminService').from(model) + it('bootrapped the database successfully', ()=>{ + const { AdminService } = cds.services const { Authors } = AdminService.entities expect(AdminService).not.to.be.undefined expect(Authors).not.to.be.undefined }) - it('bootrapped the database successfully', ()=>{}) - it('supports targets as strings or reflected defs', async () => { const AdminService = await cds.connect.to('AdminService') const { Authors } = AdminService.entities diff --git a/test/custom-handlers.test.js b/test/custom-handlers.test.js index 8c2f95d3..b2562644 100644 --- a/test/custom-handlers.test.js +++ b/test/custom-handlers.test.js @@ -1,14 +1,11 @@ -const cwd = process.cwd() +const { GET, POST, expect } = require('./cds').test('bookshop').in(__dirname,'..') const is_jest = !!global.test if (is_jest) { // it's jest global.before = (msg,fn) => global.beforeAll(fn||msg) global.after = (msg,fn) => global.afterAll(fn||msg) } -before (()=> process.chdir(__dirname)) -after (()=> process.chdir(cwd)) describe('Custom Handlers', () => { - const { GET, POST, expect } = require('./capire').launch('bookshop') it('should reject out-of-stock orders', async () => { await expect( diff --git a/test/hello-world.test.js b/test/hello-world.test.js index 34ea78bb..d6d32974 100644 --- a/test/hello-world.test.js +++ b/test/hello-world.test.js @@ -1,6 +1,7 @@ +const cds = require ('./cds') +const { GET, expect } = cds.test('serve','hello/world.cds').in(__dirname,'..') describe('Hello world!', () => { - const { GET, expect } = require('./capire').launch('cds serve', __dirname+'/../hello/world.cds', '') it('should say hello with class impl', async () => { const {data} = await GET `/say/hello(to='world')` diff --git a/test/hierarchical-data.test.js b/test/hierarchical-data.test.js index 8a867d9a..1c8451cb 100644 --- a/test/hierarchical-data.test.js +++ b/test/hierarchical-data.test.js @@ -1,6 +1,5 @@ const cwd = process.cwd(); process.chdir (__dirname) //> only for internal CI/CD@SAP -const {expect} = require('./capire') -const cds = require ('@sap/cds') +const cds = require ('./cds'), {expect} = cds.test // monkey patching older releases: if (!cds.compile.cdl) cds.compile.cdl = cds.parse diff --git a/test/localized-data.test.js b/test/localized-data.test.js index fd57add6..2f614ffe 100644 --- a/test/localized-data.test.js +++ b/test/localized-data.test.js @@ -1,5 +1,7 @@ +const cds = require ('./cds') +const { GET, expect } = cds.test ('serve', __dirname+'/localized-data.cds', '--in-memory') + describe('Localized Data', () => { - const { GET, expect } = require('./capire').launch('cds serve',__dirname+'/localized-data.cds') it('serves localized $metadata documents', async () => { const { data } = await GET`/browse/$metadata?sap-language=de` diff --git a/test/messaging.test.js b/test/messaging.test.js index 3cd207b4..632b093f 100644 --- a/test/messaging.test.js +++ b/test/messaging.test.js @@ -1,6 +1,5 @@ const cwd = process.cwd(); process.chdir (__dirname) //> only for internal CI/CD@SAP -const {expect} = require('./capire') -const cds = require ('@sap/cds') +const cds = require ('./cds'), {expect} = cds.test const _model = '@capire/reviews' diff --git a/test/odata.test.js b/test/odata.test.js index 4fb532a0..ca89a20d 100644 --- a/test/odata.test.js +++ b/test/odata.test.js @@ -1,5 +1,6 @@ +const { GET, expect } = require('./cds').test('bookshop').in(__dirname,'..') + describe('OData Protocol', () => { - const { GET, expect } = require('./capire').launch('bookshop') it('serves $metadata documents in v4', async () => { const { headers, status, data } = await GET`/browse/$metadata` diff --git a/test/package.json b/test/package.json deleted file mode 100644 index b6fe7d28..00000000 --- a/test/package.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "name": "@capire/tests", - "version": "1.0.0", - "main": "capire.js" -} \ No newline at end of file