loopback-workspace
Version:
**⚠️ LoopBack 3 is in Maintenance LTS mode, only critical bugs and critical security fixes will be provided. (See [Module Long Term Support Policy](#module-long-term-support-policy) below.)**
337 lines (301 loc) • 11 kB
JavaScript
// Copyright IBM Corp. 2014,2019. All Rights Reserved.
// Node module: loopback-workspace
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT
;
const async = require('async');
const fs = require('fs-extra');
const app = require('../');
const request = require('supertest');
const TestDataBuilder = require('./helpers/test-data-builder');
const Workspace = app.models.Workspace;
const ConfigFile = app.models.ConfigFile;
const expect = require('chai').expect;
const support = require('../test/support');
const givenBasicWorkspace = support.givenBasicWorkspace;
const givenEmptySandbox = support.givenEmptySandbox;
const resetWorkspace = support.resetWorkspace;
const SANDBOX = support.SANDBOX;
const findAllEntities = support.findAllEntities;
const expectFileExists = support.expectFileExists;
const getPath = support.getPath;
const toNames = support.toNames;
const setWorkspaceToSandboxDir = support.setWorkspaceToSandboxDir;
const createSandboxDir = support.createSandboxDir;
const expectFileNotExists = support.expectFileNotExists;
describe('Workspace', function() {
describe('Workspace.availableLBVersions()', function() {
it('Get an array of supported loopback versions', function(done) {
request(app).get('/api/workspaces/loopback-versions').expect(200, function(err, res) {
if (err) return done(err);
const versions = res.body.versions;
expect(versions.length).to.eql(1);
done();
});
});
});
describe('Workspace.getAvailableTemplates(callback)', function() {
it('Get an array of available template names.', function(done) {
Workspace.getAvailableTemplates(function(err, templates) {
expect(templates).to.have.members([
'api-server',
'empty-server',
'hello-world',
'notes',
]);
done();
});
});
});
describe('Workspace.describeAvailableTemplates(cb)', function() {
it('returns an expected list', function(done) {
Workspace.describeAvailableTemplates(function(err, templates) {
if (err) return done(err);
expect(templates).to.eql([
{
name: 'api-server',
description: 'A LoopBack API server with local User auth',
supportedLBVersions: ['3.x'],
},
{
name: 'empty-server',
description: 'An empty LoopBack API, without any configured ' +
'models or datasources',
supportedLBVersions: ['3.x'],
},
{
description: 'A project containing a controller, ' +
'including a single vanilla Message and a single remote method',
name: 'hello-world',
supportedLBVersions: ['3.x'],
},
{
description: 'A project containing a basic working example, ' +
'including a memory database',
name: 'notes',
supportedLBVersions: ['3.x'],
},
]);
done();
});
});
});
describe('Workspace.addComponent(options, cb)', function() {
beforeEach(resetWorkspace);
beforeEach(givenEmptySandbox);
it('should add the static files', function(done) {
Workspace.addComponent({
template: 'api-server',
root: true,
}, function(err) {
if (err) return done(err);
expectFileExists(getPath('server/server.js'));
expectFileExists(getPath('server/boot/authentication.js'));
done();
});
});
it('should provide a hook for custom of `cp -r`', function(done) {
const calls = [];
const ncp = Workspace.copyRecursive;
Workspace.copyRecursive = function(src, dest, cb) {
calls.push([src, dest]);
process.nextTick(cb);
};
Workspace.addComponent(
{
template: 'api-server',
root: true,
},
function(err) {
Workspace.copyRecursive = ncp;
if (err) return done(err);
// eslint-disable-next-line no-unused-expressions
expect(calls).to.be.not.empty;
done();
},
);
});
});
describe('Workspace.createFromTemplate(templateName, callback)', function() {
beforeEach(givenBasicWorkspace);
beforeEach(findAllEntities);
beforeEach(function(done) {
// TODO(ritch) this should not be required...
// there is most likely an issue with loading into cache in parallel
const test = this;
app.models.DataSourceDefinition.find(function(err, defs) {
if (err) return done(err);
test.dataSources = defs;
done();
});
});
it('should create a set of facets', function() {
const facetNames = toNames(this.facets);
expect(facetNames).to.have.members([
'common',
'server',
]);
});
it('should not create a set of model definitions', function() {
// eslint-disable-next-line no-unused-expressions
expect(this.models).to.be.empty;
});
it('should create a set of data source definitions', function() {
const dataSourceNames = toNames(this.dataSources);
expect(dataSourceNames).to.contain('db');
});
describe('generated package.json', function() {
let pkg;
before(function() {
pkg = fs.readJsonSync(SANDBOX + '/package.json');
});
it('should set correct name', function() {
// project name is hard-coded in support.js as 'sandbox'
expect(pkg.name).to.equal('sandbox');
});
it('should set correct description', function() {
expect(pkg.description).to.equal('sandbox');
});
it('should set dummy repository', function() {
expect(pkg.repository).to.be.an('object');
});
});
});
describe('project.listAvailableConnectors(cb)', function() {
let connectors;
before(function(done) {
Workspace.listAvailableConnectors(function(err, list) {
connectors = list;
done(err);
});
});
it('should include Memory connector', function() {
const names = connectors.map(function(it) { return it.name; });
expect(names).to.contain('memory');
});
it('should have installed flag', function() {
const installed = connectors.filter(function(it) {
return it.installed === true;
}).map(function(it) {
return it.name;
});
const expectedInstalled = ['memory', 'mail'];
expect(installed).to.contain.members(expectedInstalled);
});
it('should include base model in metadata', function() {
const meta = findByName('memory');
expect(meta).to.have.property('baseModel', 'PersistedModel');
});
function findByName(name) {
return connectors.filter(function(c) {
return c.name === name;
})[0];
}
});
describe('Workspace.isValidDir(cb)', function() {
beforeEach(resetWorkspace);
beforeEach(givenEmptySandbox);
it('returns no errors for a valid workspace dir', function(done) {
givenBasicWorkspace(function(err) {
if (err) return done(err);
Workspace.isValidDir(function(err) {
// the test passes when no error is reported
done(err);
});
});
});
it('should fail when the directory is empty', function(done) {
Workspace.isValidDir(function(err) {
expect(err && err.message)
.to.match(/Invalid workspace: no facets found/);
done();
});
});
it('should fail when a json file is malformed', function(done) {
fs.writeFileSync(SANDBOX + '/package.json', '{malformed}', 'utf-8');
Workspace.isValidDir(function(err) {
expect(err && err.message)
.to.match(/Cannot parse package.json/);
done();
});
});
});
describe('Workspace.copyGitignore(templatesDir, dest, cb)', function() {
beforeEach(givenBasicWorkspace);
it('generates `.gitignore` properly', function(done) {
fs.exists(SANDBOX + '/.gitignore', function(exists) {
// eslint-disable-next-line no-unused-expressions
expect(exists).to.be.ok;
done();
});
});
});
describe('Workspace.loadWorkspace(dir, cb)', function() {
const TEST_PATH = '/some/test/dir';
it('sets WORKSPACE_DIR env variable', function(done) {
Workspace.loadWorkspace(TEST_PATH, function() {
expect(process.env.WORKSPACE_DIR).to.equal(TEST_PATH);
done();
});
});
});
describe('Workspace.getWorkspace(cb)', function() {
it('returns the value of the WORKSPACE_DIR env variable', function(done) {
Workspace.getWorkspace(function(err, path) {
expect(path).to.equal(process.env.WORKSPACE_DIR);
done();
});
});
});
describe('Multi-project workspace support', function() {
before(givenEmptySandbox);
after(setWorkspaceToSandboxDir);
it('switches workspace while creating two apps in the same dir',
function(done) {
async.series([
function(next) {
createSandboxDir(SANDBOX + '/noteapp', next);
},
function(next) {
Workspace.loadWorkspace(SANDBOX + '/noteapp', function() {
expect(process.env.WORKSPACE_DIR).to.equal(SANDBOX + '/noteapp');
next();
});
},
function(next) {
app.models.Workspace.createFromTemplate('notes', 'noteapp', {},
function(err) {
if (err) return done(err);
expectFileExists(getPath('server/server.js'));
expectFileExists(getPath('server/boot/authentication.js'));
expectFileExists(getPath('common/models/note.js'));
expectFileExists(getPath('common/models/note.json'));
next();
});
},
function(next) {
createSandboxDir(SANDBOX + '/helloworldapp', next);
},
function(next) {
Workspace.loadWorkspace(SANDBOX + '/helloworldapp', function() {
expect(process.env.WORKSPACE_DIR).to.equal(SANDBOX + '/helloworldapp');
next();
});
},
function(next) {
app.models.Workspace.createFromTemplate('hello-world', 'helloworldapp', {},
function(err) {
if (err) return done(err);
expectFileExists(getPath('server/server.js'));
expectFileExists(getPath('server/boot/authentication.js'));
expectFileExists(getPath('common/models/message.js'));
expectFileExists(getPath('common/models/message.json'));
expectFileNotExists(getPath('common/models/note.js'));
expectFileNotExists(getPath('common/models/note.json'));
next();
});
},
], done);
});
});
});