diff --git a/gulpfile.coffee b/gulpfile.coffee index 3fd920c2..4b741030 100644 --- a/gulpfile.coffee +++ b/gulpfile.coffee @@ -2,7 +2,6 @@ path = require('path') gulp = require('gulp') coffee = require('gulp-coffee') inlinesource = require('gulp-inline-source') -mocha = require('gulp-mocha') shell = require('gulp-shell') packageJSON = require('./package.json') @@ -25,12 +24,6 @@ gulp.task 'coffee', -> .pipe(coffee(bare: true, header: true)) .pipe(gulp.dest(OPTIONS.directories.build)) -gulp.task 'test', -> - gulp.src(OPTIONS.files.tests, read: false) - .pipe(mocha({ - reporter: 'spec' - })) - gulp.task 'build', gulp.series [ 'coffee', 'pages' diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json index fc05a717..cc10fe59 100644 --- a/npm-shrinkwrap.json +++ b/npm-shrinkwrap.json @@ -376,6 +376,21 @@ "resolved": "https://registry.npmjs.org/@types/caseless/-/caseless-0.12.2.tgz", "integrity": "sha512-6ckxMjBBD8URvjB6J3NcnuAn5Pkl7t3TizAg+xdlzzQGSPSmBcXf8KoIH0ua/i+tio+ZRUHEXp0HEmvaR4kt0w==" }, + "@types/chai": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.1.7.tgz", + "integrity": "sha512-2Y8uPt0/jwjhQ6EiluT0XCri1Dbplr0ZxfFXUz+ye13gaqE8u5gL5ppao1JrUYr9cIip5S6MvQzBS7Kke7U9VA==", + "dev": true + }, + "@types/chai-as-promised": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/@types/chai-as-promised/-/chai-as-promised-7.1.1.tgz", + "integrity": "sha512-dberBxQW/XWv6BMj0su1lV9/C9AUx5Hqu2pisuS6S4YK/Qt6vurcj/BmcbEsobIWWCQzhesNY8k73kIxx4X7Mg==", + "dev": true, + "requires": { + "@types/chai": "*" + } + }, "@types/chokidar": { "version": "1.7.5", "resolved": "https://registry.npmjs.org/@types/chokidar/-/chokidar-1.7.5.tgz", @@ -415,6 +430,12 @@ "@types/node": "*" } }, + "@types/ejs": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/@types/ejs/-/ejs-2.6.3.tgz", + "integrity": "sha512-/F+qQ0Fr0Dr1YvHjX+FCvbba4sQ27RdCPDqmP/si0e1v1GOkbQ3VRBvZPSQM7NoQ3iz3SyiJVscCP2f0vKuIhQ==", + "dev": true + }, "@types/event-stream": { "version": "3.3.34", "resolved": "https://registry.npmjs.org/@types/event-stream/-/event-stream-3.3.34.tgz", @@ -530,6 +551,12 @@ "@types/node": "*" } }, + "@types/mocha": { + "version": "5.2.7", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-5.2.7.tgz", + "integrity": "sha512-NYrtPht0wGzhwe9+/idPaBB+TqkY9AhTvOLMkThm0IoEfLaiVQZwBwyJ5puCkO3AUCWrmcoePjp2mbFocKy4SQ==", + "dev": true + }, "@types/mz": { "version": "0.0.32", "resolved": "https://registry.npmjs.org/@types/mz/-/mz-0.0.32.tgz", @@ -599,6 +626,12 @@ } } }, + "@types/rewire": { + "version": "2.5.28", + "resolved": "https://registry.npmjs.org/@types/rewire/-/rewire-2.5.28.tgz", + "integrity": "sha512-uD0j/AQOa5le7afuK+u+woi8jNKF1vf3DN0H7LCJhft/lNNibUr7VcAesdgtWfEKveZol3ZG1CJqwx2Bhrnl8w==", + "dev": true + }, "@types/rimraf": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/@types/rimraf/-/rimraf-2.0.2.tgz", @@ -620,6 +653,12 @@ "integrity": "sha512-7kUdtJtUylvyISJbe9FMcvMTjRdP0EvNDO1WbT0lT22k/IPBiPRTpmWaKu5HTWLCGLQRWVHrzVHZktTDvvR23g==", "dev": true }, + "@types/sinon": { + "version": "7.0.13", + "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-7.0.13.tgz", + "integrity": "sha512-d7c/C/+H/knZ3L8/cxhicHUiTDxdgap0b/aNJfsmLwFu/iOP17mdgbQsbHA3SJmrzsjD0l3UEE5SN4xxuz5ung==", + "dev": true + }, "@types/stream-to-promise": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/@types/stream-to-promise/-/stream-to-promise-2.2.0.tgz", @@ -6226,12 +6265,6 @@ "lodash": "^4.17.2" } }, - "growl": { - "version": "1.9.2", - "resolved": "https://registry.npmjs.org/growl/-/growl-1.9.2.tgz", - "integrity": "sha1-Dqd0NxXbjY3ixe3hd14bRayFwC8=", - "dev": true - }, "gulp": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/gulp/-/gulp-4.0.2.tgz", @@ -6296,127 +6329,6 @@ "through2": "~2.0.0" } }, - "gulp-mocha": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/gulp-mocha/-/gulp-mocha-2.2.0.tgz", - "integrity": "sha1-HOXrpLlLQMdDav7DxJgsjuqJQZI=", - "dev": true, - "requires": { - "gulp-util": "^3.0.0", - "mocha": "^2.0.1", - "plur": "^2.1.0", - "resolve-from": "^1.0.0", - "temp": "^0.8.3", - "through": "^2.3.4" - }, - "dependencies": { - "commander": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.3.0.tgz", - "integrity": "sha1-/UMOiJgy7DU7ms0d4hfBHLPu+HM=", - "dev": true - }, - "debug": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz", - "integrity": "sha1-+HBX6ZWxofauaklgZkE3vFbwOdo=", - "dev": true, - "requires": { - "ms": "0.7.1" - } - }, - "diff": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-1.4.0.tgz", - "integrity": "sha1-fyjS657nsVqX79ic5j3P2qPMur8=", - "dev": true - }, - "escape-string-regexp": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.2.tgz", - "integrity": "sha1-Tbwv5nTnGUnK8/smlc5/LcHZqNE=", - "dev": true - }, - "glob": { - "version": "3.2.11", - "resolved": "https://registry.npmjs.org/glob/-/glob-3.2.11.tgz", - "integrity": "sha1-Spc/Y1uRkPcV0QmH1cAP0oFevj0=", - "dev": true, - "requires": { - "inherits": "2", - "minimatch": "0.3" - } - }, - "lru-cache": { - "version": "2.7.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.7.3.tgz", - "integrity": "sha1-bUUk6LlV+V1PW1iFHOId1y+06VI=", - "dev": true - }, - "minimatch": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.3.0.tgz", - "integrity": "sha1-J12O2qxPG7MyZHIInnlJyDlGmd0=", - "dev": true, - "requires": { - "lru-cache": "2", - "sigmund": "~1.0.0" - } - }, - "mocha": { - "version": "2.5.3", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-2.5.3.tgz", - "integrity": "sha1-FhvlvetJZ3HrmzV0UFC2IrWu/Fg=", - "dev": true, - "requires": { - "commander": "2.3.0", - "debug": "2.2.0", - "diff": "1.4.0", - "escape-string-regexp": "1.0.2", - "glob": "3.2.11", - "growl": "1.9.2", - "jade": "0.26.3", - "mkdirp": "0.5.1", - "supports-color": "1.2.0", - "to-iso-string": "0.0.2" - } - }, - "ms": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz", - "integrity": "sha1-nNE8A62/8ltl7/3nzoZO6VIBcJg=", - "dev": true - }, - "resolve-from": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-1.0.1.tgz", - "integrity": "sha1-Jsv+k10a7uq7Kbw/5a6wHpPUQiY=", - "dev": true - }, - "rimraf": { - "version": "2.2.8", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.2.8.tgz", - "integrity": "sha1-5Dm+Kq7jJzIZUnMPmaiSnk/FBYI=", - "dev": true - }, - "supports-color": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-1.2.0.tgz", - "integrity": "sha1-/x7R5hFp0Gs88tWI4YixjYhH4X4=", - "dev": true - }, - "temp": { - "version": "0.8.3", - "resolved": "https://registry.npmjs.org/temp/-/temp-0.8.3.tgz", - "integrity": "sha1-4Ma8TSa5AxJEEOT+2BEDAU38H1k=", - "dev": true, - "requires": { - "os-tmpdir": "^1.0.0", - "rimraf": "~2.2.6" - } - } - } - }, "gulp-shell": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/gulp-shell/-/gulp-shell-0.5.2.tgz", @@ -7288,12 +7200,6 @@ "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.0.tgz", "integrity": "sha512-M4Sjn6N/+O6/IXSJseKqHoFc+5FdGJ22sXqnjTpdZweHK64MzEPAyQZyEU3R/KRv2GLoa7nNtg/C2Ev6m7z+eA==" }, - "irregular-plurals": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/irregular-plurals/-/irregular-plurals-1.4.0.tgz", - "integrity": "sha1-LKmwM2UREYVUEvFr5dd8YqRYp2Y=", - "dev": true - }, "is-absolute": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-absolute/-/is-absolute-1.0.0.tgz", @@ -7662,30 +7568,6 @@ "is-object": "^1.0.1" } }, - "jade": { - "version": "0.26.3", - "resolved": "https://registry.npmjs.org/jade/-/jade-0.26.3.tgz", - "integrity": "sha1-jxDXl32NefL2/4YqgbBRPMslaGw=", - "dev": true, - "requires": { - "commander": "0.6.1", - "mkdirp": "0.3.0" - }, - "dependencies": { - "commander": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-0.6.1.tgz", - "integrity": "sha1-+mihT2qUXVTbvlDYzbMyDp47GgY=", - "dev": true - }, - "mkdirp": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.0.tgz", - "integrity": "sha1-G79asbqCevI1dRQ0kEJkVfSB/h4=", - "dev": true - } - } - }, "js-tokens": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", @@ -13483,15 +13365,6 @@ "xmldom": "0.1.x" } }, - "plur": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/plur/-/plur-2.1.2.tgz", - "integrity": "sha1-dIJFLBoPUI4+NE6uwxLJHCncZVo=", - "dev": true, - "requires": { - "irregular-plurals": "^1.0.0" - } - }, "posix-character-classes": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", @@ -15665,12 +15538,6 @@ "resolved": "https://registry.npmjs.org/shellwords/-/shellwords-0.1.1.tgz", "integrity": "sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww==" }, - "sigmund": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz", - "integrity": "sha1-P/IfGYytIXX587eBhT/ZTQ0ZtZA=", - "dev": true - }, "signal-exit": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", @@ -16661,12 +16528,6 @@ "integrity": "sha1-uDVx+k2MJbguIxsG46MFXeTKGkc=", "dev": true }, - "to-iso-string": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/to-iso-string/-/to-iso-string-0.0.2.tgz", - "integrity": "sha1-TcGeZk38y+Jb2NtQiwDG2hWCVdE=", - "dev": true - }, "to-object-path": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", diff --git a/package.json b/package.json index 4a3a71d8..723a6d9d 100644 --- a/package.json +++ b/package.json @@ -46,8 +46,8 @@ "package": "npm run build:fast && npm run build:standalone && npm run build:installer", "release": "ts-node --type-check -P automation/tsconfig.json automation/run.ts release", "pretest": "npm run build", - "test": "gulp test", - "test:fast": "npm run build:fast && gulp test", + "test": "mocha -r ts-node/register tests/**/*.spec.ts", + "test:fast": "npm run build:fast && npm run test", "ci": "npm run test && catch-uncommitted", "watch": "gulp watch", "prettify": "prettier --write \"{lib,tests,automation,typings}/**/*.ts\" --config ./node_modules/resin-lint/config/.prettierrc", @@ -82,22 +82,28 @@ "@oclif/parser": "^3.7.3", "@types/archiver": "2.1.2", "@types/bluebird": "3.5.21", + "@types/chai": "^4.1.7", + "@types/chai-as-promised": "^7.1.1", "@types/chokidar": "^1.7.5", "@types/common-tags": "1.4.0", "@types/dockerode": "2.5.6", + "@types/ejs": "^2.6.3", "@types/fs-extra": "7.0.0", "@types/is-root": "1.0.0", "@types/lodash": "4.14.112", "@types/mixpanel": "2.14.0", "@types/mkdirp": "0.5.2", + "@types/mocha": "^5.2.7", "@types/mz": "0.0.32", "@types/net-keepalive": "^0.4.0", "@types/node": "10.14.5", "@types/prettyjson": "0.0.28", "@types/raven": "2.5.1", "@types/request": "2.48.1", + "@types/rewire": "^2.5.28", "@types/rimraf": "^2.0.2", "@types/shell-escape": "^0.2.0", + "@types/sinon": "^7.0.13", "@types/stream-to-promise": "2.2.0", "@types/tar-stream": "1.6.0", "@types/through2": "2.0.33", @@ -110,7 +116,6 @@ "gulp": "^4.0.1", "gulp-coffee": "^2.2.0", "gulp-inline-source": "^2.1.0", - "gulp-mocha": "^2.0.0", "gulp-shell": "^0.5.2", "mocha": "^6.2.0", "pkg": "^4.4.0", diff --git a/tests/auth/server.spec.js b/tests/auth/server.spec.js deleted file mode 100644 index 9d92e1fe..00000000 --- a/tests/auth/server.spec.js +++ /dev/null @@ -1,164 +0,0 @@ -const chai = require('chai'); -const chaiAsPromised = require('chai-as-promised'); -const request = require('request'); -const sinon = require('sinon'); -const Promise = require('bluebird'); -const path = require('path'); -const fs = require('fs'); -const ejs = require('ejs'); -const server = require('../../build/auth/server'); -const utils = require('../../build/auth/utils'); -const tokens = require('./tokens.json'); - -chai.use(chaiAsPromised); - -let options = { - port: 3000, - path: '/auth' -}; - -let getPage = function(name) { - let pagePath = path.join(__dirname, '..', '..', 'build', 'auth', 'pages', `${name}.ejs`); - let tpl = fs.readFileSync(pagePath, {encoding: 'utf8'}); - let compiledTpl = ejs.compile(tpl); - return server.getContext(name) - .then(context => compiledTpl(context)); -}; - -describe('Server:', function() { - - it('should get 404 if posting to an unknown path', function(done) { - let promise = server.awaitForToken(options); - chai.expect(promise).to.be.rejectedWith('Unknown path or verb'); - - return request.post(`http://localhost:${options.port}/foobarbaz`, { - form: { - token: tokens.johndoe.token - } - } - , function(error, response, body) { - chai.expect(error).to.not.exist; - chai.expect(response.statusCode).to.equal(404); - chai.expect(body).to.equal('Not found'); - return done(); - }); - }); - - it('should get 404 if not using the correct verb', function(done) { - let promise = server.awaitForToken(options); - chai.expect(promise).to.be.rejectedWith('Unknown path or verb'); - - return request.get(`http://localhost:${options.port}${options.path}`, { - form: { - token: tokens.johndoe.token - } - } - , function(error, response, body) { - chai.expect(error).to.not.exist; - chai.expect(response.statusCode).to.equal(404); - chai.expect(body).to.equal('Not found'); - return done(); - }); - }); - - describe('given the token authenticates with the server', function() { - - beforeEach(function() { - this.loginIfTokenValidStub = sinon.stub(utils, 'loginIfTokenValid'); - return this.loginIfTokenValidStub.returns(Promise.resolve(true)); - }); - - afterEach(function() { - return this.loginIfTokenValidStub.restore(); - }); - - return it('should eventually be the token', function(done) { - let promise = server.awaitForToken(options); - chai.expect(promise).to.eventually.equal(tokens.johndoe.token); - - return request.post(`http://localhost:${options.port}${options.path}`, { - form: { - token: tokens.johndoe.token - } - } - , function(error, response, body) { - chai.expect(error).to.not.exist; - chai.expect(response.statusCode).to.equal(200); - return getPage('success').then(function(expectedBody) { - chai.expect(body).to.equal(expectedBody); - return done(); - }); - }); - }); - }); - - return describe('given the token does not authenticate with the server', function() { - - beforeEach(function() { - this.loginIfTokenValidStub = sinon.stub(utils, 'loginIfTokenValid'); - return this.loginIfTokenValidStub.returns(Promise.resolve(false)); - }); - - afterEach(function() { - return this.loginIfTokenValidStub.restore(); - }); - - it('should be rejected', function(done) { - let promise = server.awaitForToken(options); - chai.expect(promise).to.be.rejectedWith('Invalid token'); - - return request.post(`http://localhost:${options.port}${options.path}`, { - form: { - token: tokens.johndoe.token - } - } - , function(error, response, body) { - chai.expect(error).to.not.exist; - chai.expect(response.statusCode).to.equal(401); - return getPage('error').then(function(expectedBody) { - chai.expect(body).to.equal(expectedBody); - return done(); - }); - }); - }); - - it('should be rejected if no token', function(done) { - let promise = server.awaitForToken(options); - chai.expect(promise).to.be.rejectedWith('No token'); - - return request.post(`http://localhost:${options.port}${options.path}`, { - form: { - token: '' - } - } - , function(error, response, body) { - chai.expect(error).to.not.exist; - chai.expect(response.statusCode).to.equal(401); - return getPage('error').then(function(expectedBody) { - chai.expect(body).to.equal(expectedBody); - return done(); - }); - }); - }); - - return it('should be rejected if token is malformed', function(done) { - let promise = server.awaitForToken(options); - chai.expect(promise).to.be.rejectedWith('Invalid token'); - - return request.post(`http://localhost:${options.port}${options.path}`, { - form: { - token: 'asdf' - } - } - , function(error, response, body) { - chai.expect(error).to.not.exist; - chai.expect(response.statusCode).to.equal(401); - return getPage('error').then(function(expectedBody) { - chai.expect(body).to.equal(expectedBody); - return done(); - }); - }); - }); - }); -}); - diff --git a/tests/auth/server.spec.ts b/tests/auth/server.spec.ts new file mode 100644 index 00000000..7b8ec9f2 --- /dev/null +++ b/tests/auth/server.spec.ts @@ -0,0 +1,193 @@ +import * as Promise from 'bluebird'; +import * as chai from 'chai'; +import chaiAsPromised = require('chai-as-promised'); +import * as ejs from 'ejs'; +import * as fs from 'fs'; +import * as path from 'path'; +import * as request from 'request'; +import * as sinon from 'sinon'; + +// TODO: Convert server code to Typescript so it can have a declaration file +// @ts-ignore +import * as server from '../../build/auth/server'; + +// TODO: Convert utils code to Typescript so it can have a declaration file +// @ts-ignore +import * as utils from '../../build/auth/utils'; +import tokens from './tokens'; + +chai.use(chaiAsPromised); + +const { expect } = chai; + +const options = { + port: 3000, + path: '/auth', +}; + +const getPage = function(name: string): Promise { + const pagePath = path.join( + __dirname, + '..', + '..', + 'build', + 'auth', + 'pages', + `${name}.ejs`, + ); + const tpl = fs.readFileSync(pagePath, { encoding: 'utf8' }); + const compiledTpl = ejs.compile(tpl); + return server.getContext(name).then((context: any) => compiledTpl(context)); +}; + +describe('Server:', function() { + it('should get 404 if posting to an unknown path', function(done) { + const promise = server.awaitForToken(options); + expect(promise).to.be.rejectedWith('Unknown path or verb'); + + return request.post( + `http://localhost:${options.port}/foobarbaz`, + { + form: { + token: tokens.johndoe.token, + }, + }, + function(error, response, body) { + expect(error).to.not.exist; + expect(response.statusCode).to.equal(404); + expect(body).to.equal('Not found'); + return done(); + }, + ); + }); + + it('should get 404 if not using the correct verb', function(done) { + const promise = server.awaitForToken(options); + expect(promise).to.be.rejectedWith('Unknown path or verb'); + + return request.get( + `http://localhost:${options.port}${options.path}`, + { + form: { + token: tokens.johndoe.token, + }, + }, + function(error, response, body) { + expect(error).to.not.exist; + expect(response.statusCode).to.equal(404); + expect(body).to.equal('Not found'); + return done(); + }, + ); + }); + + describe('given the token authenticates with the server', function() { + beforeEach(function() { + this.loginIfTokenValidStub = sinon.stub(utils, 'loginIfTokenValid'); + return this.loginIfTokenValidStub.returns(Promise.resolve(true)); + }); + + afterEach(function() { + return this.loginIfTokenValidStub.restore(); + }); + + return it('should eventually be the token', function(done) { + const promise = server.awaitForToken(options); + expect(promise).to.eventually.equal(tokens.johndoe.token); + + return request.post( + `http://localhost:${options.port}${options.path}`, + { + form: { + token: tokens.johndoe.token, + }, + }, + function(error, response, body) { + expect(error).to.not.exist; + expect(response.statusCode).to.equal(200); + return getPage('success').then(function(expectedBody) { + expect(body).to.equal(expectedBody); + return done(); + }); + }, + ); + }); + }); + + return describe('given the token does not authenticate with the server', function() { + beforeEach(function() { + this.loginIfTokenValidStub = sinon.stub(utils, 'loginIfTokenValid'); + return this.loginIfTokenValidStub.returns(Promise.resolve(false)); + }); + + afterEach(function() { + return this.loginIfTokenValidStub.restore(); + }); + + it('should be rejected', function(done) { + const promise = server.awaitForToken(options); + expect(promise).to.be.rejectedWith('Invalid token'); + + return request.post( + `http://localhost:${options.port}${options.path}`, + { + form: { + token: tokens.johndoe.token, + }, + }, + function(error, response, body) { + expect(error).to.not.exist; + expect(response.statusCode).to.equal(401); + return getPage('error').then(function(expectedBody) { + expect(body).to.equal(expectedBody); + return done(); + }); + }, + ); + }); + + it('should be rejected if no token', function(done) { + const promise = server.awaitForToken(options); + expect(promise).to.be.rejectedWith('No token'); + + return request.post( + `http://localhost:${options.port}${options.path}`, + { + form: { + token: '', + }, + }, + function(error, response, body) { + expect(error).to.not.exist; + expect(response.statusCode).to.equal(401); + return getPage('error').then(function(expectedBody) { + expect(body).to.equal(expectedBody); + return done(); + }); + }, + ); + }); + + return it('should be rejected if token is malformed', function(done) { + const promise = server.awaitForToken(options); + expect(promise).to.be.rejectedWith('Invalid token'); + + return request.post( + `http://localhost:${options.port}${options.path}`, + { + form: { + token: 'asdf', + }, + }, + function(error, response, body) { + expect(error).to.not.exist; + expect(response.statusCode).to.equal(401); + return getPage('error').then(function(expectedBody) { + expect(body).to.equal(expectedBody); + return done(); + }); + }, + ); + }); + }); +}); diff --git a/tests/auth/tokens.json b/tests/auth/tokens.json deleted file mode 100644 index 6b9eb4ad..00000000 --- a/tests/auth/tokens.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "johndoe": { - "token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VybmFtZSI6ImpvaG5kb2UxIiwiZW1haWwiOiJqb2huZG9lQGpvaG5kb2UuY29tIiwiZ2l0bGFiX2lkIjoxMzI1LCJzb2NpYWxfc2VydmljZV9hY2NvdW50IjpudWxsLCJoYXNQYXNzd29yZFNldCI6dHJ1ZSwibmVlZHNQYXNzd29yZFJlc2V0IjpmYWxzZSwicHVibGljX2tleSI6ZmFsc2UsImZlYXR1cmVzIjpbXSwiaWQiOjEzNDQsImludGVyY29tVXNlckhhc2giOiJlMDM3NzhkZDI5ZTE1NzQ0NWYyNzJhY2M5MjExNzBjZjI4MTBiNjJmNTAyNjQ1MjY1Y2MzNDlkNmRlZGEzNTI0IiwicGVybWlzc2lvbnMiOltdLCJpYXQiOjE0MjY3ODMzMTJ9.v5bmh9HwyUZu8zhh1rA79mTL-1jzDOO8eUr_lVaBwhg", - "data": { - "email": "johndoe@johndoe.com", - "username": "johndoe1", - "id": 1344 - } - }, - "janedoe": { - "token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpZCI6MTUyLCJ1c2VybmFtZSI6ImphbmVkb2UiLCJlbWFpbCI6ImphbmVkb2VAYXNkZi5jb20iLCJzb2NpYWxfc2VydmljZV9hY2NvdW50IjpudWxsLCJoYXNfZGlzYWJsZWRfbmV3c2xldHRlciI6dHJ1ZSwiaGFzUGFzc3dvcmRTZXQiOnRydWUsIm5lZWRzUGFzc3dvcmRSZXNldCI6ZmFsc2UsInB1YmxpY19rZXkiOmZhbHNlLCJmZWF0dXJlcyI6W10sImludGVyY29tVXNlckhhc2giOiIwYjRmOWViNDRiMzcxZjBlMzI4ZWY1ZmUwM2FkN2ViMmY1ZjcyZGQ0MThlZjIzMTQ5ZDUyODcwOTY1NThjZTAzIiwicGVybWlzc2lvbnMiOltdLCJpYXQiOjE0MzUzMjAyNjN9.jVzUFu58vzdJFctR8ulyjGL0Em1kjIZSbSxX2SeU03Y", - "data": { - "email": "janedoe@asdf.com", - "username": "janedoe", - "id": 152 - } - } -} diff --git a/tests/auth/tokens.ts b/tests/auth/tokens.ts new file mode 100644 index 00000000..b649a120 --- /dev/null +++ b/tests/auth/tokens.ts @@ -0,0 +1,20 @@ +export default { + johndoe: { + token: + 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VybmFtZSI6ImpvaG5kb2UxIiwiZW1haWwiOiJqb2huZG9lQGpvaG5kb2UuY29tIiwiZ2l0bGFiX2lkIjoxMzI1LCJzb2NpYWxfc2VydmljZV9hY2NvdW50IjpudWxsLCJoYXNQYXNzd29yZFNldCI6dHJ1ZSwibmVlZHNQYXNzd29yZFJlc2V0IjpmYWxzZSwicHVibGljX2tleSI6ZmFsc2UsImZlYXR1cmVzIjpbXSwiaWQiOjEzNDQsImludGVyY29tVXNlckhhc2giOiJlMDM3NzhkZDI5ZTE1NzQ0NWYyNzJhY2M5MjExNzBjZjI4MTBiNjJmNTAyNjQ1MjY1Y2MzNDlkNmRlZGEzNTI0IiwicGVybWlzc2lvbnMiOltdLCJpYXQiOjE0MjY3ODMzMTJ9.v5bmh9HwyUZu8zhh1rA79mTL-1jzDOO8eUr_lVaBwhg', + data: { + email: 'johndoe@johndoe.com', + username: 'johndoe1', + id: 1344, + }, + }, + janedoe: { + token: + 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpZCI6MTUyLCJ1c2VybmFtZSI6ImphbmVkb2UiLCJlbWFpbCI6ImphbmVkb2VAYXNkZi5jb20iLCJzb2NpYWxfc2VydmljZV9hY2NvdW50IjpudWxsLCJoYXNfZGlzYWJsZWRfbmV3c2xldHRlciI6dHJ1ZSwiaGFzUGFzc3dvcmRTZXQiOnRydWUsIm5lZWRzUGFzc3dvcmRSZXNldCI6ZmFsc2UsInB1YmxpY19rZXkiOmZhbHNlLCJmZWF0dXJlcyI6W10sImludGVyY29tVXNlckhhc2giOiIwYjRmOWViNDRiMzcxZjBlMzI4ZWY1ZmUwM2FkN2ViMmY1ZjcyZGQ0MThlZjIzMTQ5ZDUyODcwOTY1NThjZTAzIiwicGVybWlzc2lvbnMiOltdLCJpYXQiOjE0MzUzMjAyNjN9.jVzUFu58vzdJFctR8ulyjGL0Em1kjIZSbSxX2SeU03Y', + data: { + email: 'janedoe@asdf.com', + username: 'janedoe', + id: 152, + }, + }, +}; diff --git a/tests/auth/utils.spec.js b/tests/auth/utils.spec.ts similarity index 61% rename from tests/auth/utils.spec.js rename to tests/auth/utils.spec.ts index fb3a8667..cd9c26c4 100644 --- a/tests/auth/utils.spec.js +++ b/tests/auth/utils.spec.ts @@ -1,77 +1,72 @@ -const chai = require('chai'); -const sinon = require('sinon'); -const url = require('url'); -const Promise = require('bluebird'); +import * as Promise from 'bluebird'; +import * as chai from 'chai'; +import rewire = require('rewire'); +import * as sinon from 'sinon'; +import * as url from 'url'; +import tokens from './tokens'; -const tokens = require('./tokens.json'); - -const rewire = require('rewire'); -let utils = rewire('../../build/auth/utils'); -let balena = utils.__get__('balena'); +const utils = rewire('../../build/auth/utils'); +const balena = utils.__get__('balena'); describe('Utils:', function() { - describe('.getDashboardLoginURL()', function() { - it('should eventually be a valid url', () => - utils.getDashboardLoginURL('https://127.0.0.1:3000/callback').then(loginUrl => - chai.expect(() => url.parse(loginUrl)).to.not.throw(Error) - ) - ); - + utils + .getDashboardLoginURL('https://127.0.0.1:3000/callback') + .then((loginUrl: string) => + chai.expect(() => url.parse(loginUrl)).to.not.throw(Error), + )); it('should eventually contain an https protocol', () => Promise.props({ dashboardUrl: balena.settings.get('dashboardUrl'), - loginUrl: utils.getDashboardLoginURL('https://127.0.0.1:3000/callback')}).then(function({ dashboardUrl, loginUrl }) { - let { protocol } = url.parse(loginUrl); + loginUrl: utils.getDashboardLoginURL('https://127.0.0.1:3000/callback'), + }).then(function({ dashboardUrl, loginUrl }) { + const { protocol } = url.parse(loginUrl); return chai.expect(protocol).to.equal(url.parse(dashboardUrl).protocol); - }) - ); + })); it('should correctly escape a callback url without a path', () => Promise.props({ dashboardUrl: balena.settings.get('dashboardUrl'), - loginUrl: utils.getDashboardLoginURL('http://127.0.0.1:3000')}).then(function({ dashboardUrl, loginUrl }) { - let expectedUrl = `${dashboardUrl}/login/cli/http%253A%252F%252F127.0.0.1%253A3000`; + loginUrl: utils.getDashboardLoginURL('http://127.0.0.1:3000'), + }).then(function({ dashboardUrl, loginUrl }) { + const expectedUrl = `${dashboardUrl}/login/cli/http%253A%252F%252F127.0.0.1%253A3000`; return chai.expect(loginUrl).to.equal(expectedUrl); - }) - ); + })); return it('should correctly escape a callback url with a path', () => Promise.props({ dashboardUrl: balena.settings.get('dashboardUrl'), - loginUrl: utils.getDashboardLoginURL('http://127.0.0.1:3000/callback')}).then(function({ dashboardUrl, loginUrl }) { - let expectedUrl = `${dashboardUrl}/login/cli/http%253A%252F%252F127.0.0.1%253A3000%252Fcallback`; + loginUrl: utils.getDashboardLoginURL('http://127.0.0.1:3000/callback'), + }).then(function({ dashboardUrl, loginUrl }) { + const expectedUrl = `${dashboardUrl}/login/cli/http%253A%252F%252F127.0.0.1%253A3000%252Fcallback`; return chai.expect(loginUrl).to.equal(expectedUrl); - }) - ); + })); }); return describe('.loginIfTokenValid()', function() { - it('should eventually be false if token is undefined', function() { - let promise = utils.loginIfTokenValid(undefined); + const promise = utils.loginIfTokenValid(undefined); return chai.expect(promise).to.eventually.be.false; }); it('should eventually be false if token is null', function() { - let promise = utils.loginIfTokenValid(null); + const promise = utils.loginIfTokenValid(null); return chai.expect(promise).to.eventually.be.false; }); it('should eventually be false if token is an empty string', function() { - let promise = utils.loginIfTokenValid(''); + const promise = utils.loginIfTokenValid(''); return chai.expect(promise).to.eventually.be.false; }); it('should eventually be false if token is a string containing only spaces', function() { - let promise = utils.loginIfTokenValid(' '); + const promise = utils.loginIfTokenValid(' '); return chai.expect(promise).to.eventually.be.false; }); describe('given the token does not authenticate with the server', function() { - beforeEach(function() { this.balenaAuthIsLoggedInStub = sinon.stub(balena.auth, 'isLoggedIn'); return this.balenaAuthIsLoggedInStub.returns(Promise.resolve(false)); @@ -82,33 +77,40 @@ describe('Utils:', function() { }); it('should eventually be false', function() { - let promise = utils.loginIfTokenValid(tokens.johndoe.token); + const promise = utils.loginIfTokenValid(tokens.johndoe.token); return chai.expect(promise).to.eventually.be.false; }); describe('given there was a token already', function() { - beforeEach(() => balena.auth.loginWithToken(tokens.janedoe.token)); return it('should preserve the old token', () => - balena.auth.getToken().then(function(originalToken) { - chai.expect(originalToken).to.equal(tokens.janedoe.token); - return utils.loginIfTokenValid(tokens.johndoe.token);}).then(balena.auth.getToken).then(currentToken => chai.expect(currentToken).to.equal(tokens.janedoe.token)) - ); + balena.auth + .getToken() + .then(function(originalToken: string) { + chai.expect(originalToken).to.equal(tokens.janedoe.token); + return utils.loginIfTokenValid(tokens.johndoe.token); + }) + .then(balena.auth.getToken) + .then((currentToken: string) => + chai.expect(currentToken).to.equal(tokens.janedoe.token), + )); }); return describe('given there was no token', function() { - beforeEach(() => balena.auth.logout()); return it('should stay without a token', () => - utils.loginIfTokenValid(tokens.johndoe.token).then(() => balena.auth.isLoggedIn()).then(isLoggedIn => chai.expect(isLoggedIn).to.equal(false)) - ); + utils + .loginIfTokenValid(tokens.johndoe.token) + .then(() => balena.auth.isLoggedIn()) + .then((isLoggedIn: boolean) => + chai.expect(isLoggedIn).to.equal(false), + )); }); }); return describe('given the token does authenticate with the server', function() { - beforeEach(function() { this.balenaAuthIsLoggedInStub = sinon.stub(balena.auth, 'isLoggedIn'); return this.balenaAuthIsLoggedInStub.returns(Promise.resolve(true)); @@ -119,7 +121,7 @@ describe('Utils:', function() { }); return it('should eventually be true', function() { - let promise = utils.loginIfTokenValid(tokens.johndoe.token); + const promise = utils.loginIfTokenValid(tokens.johndoe.token); return chai.expect(promise).to.eventually.be.true; }); }); diff --git a/tests/utils/ignore.spec.js b/tests/utils/ignore.spec.js deleted file mode 100644 index b7000eb4..00000000 --- a/tests/utils/ignore.spec.js +++ /dev/null @@ -1,88 +0,0 @@ -const chai = require('chai'); -const _ = require('lodash'); -const path = require('path'); - -let { expect } = chai; - -const { FileIgnorer, IgnoreFileType } = require('../../build/utils/ignore'); - -describe('File ignorer', function() { - - it('should detect ignore files', function() { - let f = new FileIgnorer(`.${path.sep}`); - expect(f.getIgnoreFileType('.gitignore')).to.equal(IgnoreFileType.GitIgnore); - expect(f.getIgnoreFileType('.dockerignore')).to.equal(IgnoreFileType.DockerIgnore); - expect(f.getIgnoreFileType('./.gitignore')).to.equal(IgnoreFileType.GitIgnore); - expect(f.getIgnoreFileType('./.dockerignore')).to.equal(IgnoreFileType.DockerIgnore); - - // gitignore files can appear in subdirectories, but dockerignore files cannot - expect(f.getIgnoreFileType('./subdir/.gitignore')).to.equal(IgnoreFileType.GitIgnore); - expect(f.getIgnoreFileType('./subdir/.dockerignore')).to.equal(null); - expect(f.getIgnoreFileType('./subdir/subdir2/.gitignore')).to.equal(IgnoreFileType.GitIgnore); - - expect(f.getIgnoreFileType('file')).to.equal(null); - return expect(f.getIgnoreFileType('./file')).to.equal(null); - }); - - it('should filter files from the root directory', function() { - - let ignore = new FileIgnorer(`.${path.sep}`); - ignore.gitIgnoreEntries = [ - { pattern: '*.ignore', filePath: '.gitignore' } - ]; - ignore.dockerIgnoreEntries = [ - { pattern: '*.ignore2', filePath: '.dockerignore' } - ]; - let files = [ - 'a', - 'a/b', - 'a/b/c', - 'file.ignore', - 'file2.ignore', - 'file.ignore2', - 'file2.ignore' - ]; - - return expect(_.filter(files, ignore.filter.bind(ignore))).to.deep.equal([ - 'a', - 'a/b', - 'a/b/c' - ]); - }); - - return it('should filter files from subdirectories', function() { - - let ignore = new FileIgnorer(`.${path.sep}`); - ignore.gitIgnoreEntries = [ - { pattern: '*.ignore', filePath: 'lib/.gitignore' } - ]; - let files = [ - 'test.ignore', - 'root.ignore', - 'lib/normal-file', - 'lib/should.ignore', - 'lib/thistoo.ignore' - ]; - expect(_.filter(files, ignore.filter.bind(ignore))).to.deep.equal([ - 'test.ignore', - 'root.ignore', - 'lib/normal-file' - ]); - - ignore.gitIgnoreEntries = [ - { pattern: '*.ignore', filePath: './lib/.gitignore' } - ]; - files = [ - 'test.ignore', - 'root.ignore', - 'lib/normal-file', - 'lib/should.ignore', - 'lib/thistoo.ignore' - ]; - return expect(_.filter(files, ignore.filter.bind(ignore))).to.deep.equal([ - 'test.ignore', - 'root.ignore', - 'lib/normal-file' - ]); - }); -}); diff --git a/tests/utils/ignore.spec.ts b/tests/utils/ignore.spec.ts new file mode 100644 index 00000000..1af050c9 --- /dev/null +++ b/tests/utils/ignore.spec.ts @@ -0,0 +1,98 @@ +import { expect } from 'chai'; +import * as _ from 'lodash'; +import * as path from 'path'; +import { FileIgnorer, IgnoreFileType } from '../../build/utils/ignore'; + +// Note that brack notation is used intentionally when accessing private members +// of the FileIgnorer class to prevent a Typescript compilation error (this +// behaviour is by design: see +// https://github.com/microsoft/TypeScript/issues/19335 ) +describe('File ignorer', function() { + it('should detect ignore files', function() { + const f = new FileIgnorer(`.${path.sep}`); + expect(f.getIgnoreFileType('.gitignore')).to.equal( + IgnoreFileType.GitIgnore, + ); + expect(f.getIgnoreFileType('.dockerignore')).to.equal( + IgnoreFileType.DockerIgnore, + ); + expect(f.getIgnoreFileType('./.gitignore')).to.equal( + IgnoreFileType.GitIgnore, + ); + expect(f.getIgnoreFileType('./.dockerignore')).to.equal( + IgnoreFileType.DockerIgnore, + ); + + // gitignore files can appear in subdirectories, but dockerignore files cannot + expect(f.getIgnoreFileType('./subdir/.gitignore')).to.equal( + IgnoreFileType.GitIgnore, + ); + expect(f.getIgnoreFileType('./subdir/.dockerignore')).to.equal(null); + expect(f.getIgnoreFileType('./subdir/subdir2/.gitignore')).to.equal( + IgnoreFileType.GitIgnore, + ); + + expect(f.getIgnoreFileType('file')).to.equal(null); + return expect(f.getIgnoreFileType('./file')).to.equal(null); + }); + + it('should filter files from the root directory', function() { + const ignore = new FileIgnorer(`.${path.sep}`); + ignore['gitIgnoreEntries'] = [ + { pattern: '*.ignore', filePath: '.gitignore' }, + ]; + ignore['dockerIgnoreEntries'] = [ + { pattern: '*.ignore2', filePath: '.dockerignore' }, + ]; + const files = [ + 'a', + 'a/b', + 'a/b/c', + 'file.ignore', + 'file2.ignore', + 'file.ignore2', + 'file2.ignore', + ]; + + return expect(_.filter(files, ignore.filter.bind(ignore))).to.deep.equal([ + 'a', + 'a/b', + 'a/b/c', + ]); + }); + + return it('should filter files from subdirectories', function() { + const ignore = new FileIgnorer(`.${path.sep}`); + ignore['gitIgnoreEntries'] = [ + { pattern: '*.ignore', filePath: 'lib/.gitignore' }, + ]; + let files = [ + 'test.ignore', + 'root.ignore', + 'lib/normal-file', + 'lib/should.ignore', + 'lib/thistoo.ignore', + ]; + expect(_.filter(files, ignore.filter.bind(ignore))).to.deep.equal([ + 'test.ignore', + 'root.ignore', + 'lib/normal-file', + ]); + + ignore['gitIgnoreEntries'] = [ + { pattern: '*.ignore', filePath: './lib/.gitignore' }, + ]; + files = [ + 'test.ignore', + 'root.ignore', + 'lib/normal-file', + 'lib/should.ignore', + 'lib/thistoo.ignore', + ]; + return expect(_.filter(files, ignore.filter.bind(ignore))).to.deep.equal([ + 'test.ignore', + 'root.ignore', + 'lib/normal-file', + ]); + }); +}); diff --git a/tsconfig.json b/tsconfig.json index d9333e29..c80f81f5 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,5 +1,6 @@ { "compilerOptions": { + "declaration": true, "module": "commonjs", "target": "es2017", "outDir": "build",