diff --git a/.gitignore b/.gitignore index 73461007f0..d70c8479e3 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ +.DS_Store + *.js !/*.js !bin/nativescript.js diff --git a/.travis.yml b/.travis.yml index 81e4797d9e..02bd53c25e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,7 +9,7 @@ install: - npm install - grunt pack --no-color script: -- 'true' +- npm test git: submodules: false deploy: diff --git a/Gruntfile.js b/Gruntfile.js index 26988ffd53..0f48d46272 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -56,7 +56,10 @@ module.exports = function(grunt) { watch: { devall: { files: ["lib/**/*.ts", 'test/**/*.ts'], - tasks: ['ts:devall'], + tasks: [ + 'ts:devall', + 'shell:npm_test' + ], options: { atBegin: true, interrupt: true @@ -67,7 +70,8 @@ module.exports = function(grunt) { shell: { options: { stdout: true, - stderr: true + stderr: true, + failOnError: true }, build_package: { @@ -81,7 +85,12 @@ module.exports = function(grunt) { })() } } + }, + + npm_test: { + command: "npm test" } + }, copy: { @@ -125,8 +134,6 @@ module.exports = function(grunt) { "clean", "ts:release_build", - //"shell:ci_unit_tests", - "set_package_version", "shell:build_package", diff --git a/package.json b/package.json index 56b693ce5d..8fa856746c 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,7 @@ }, "main": "./lib/nativescript-cli.js", "scripts": { - "test": "echo \"Error: no test specified\" && exit 1" + "test": "node_modules\\.bin\\_mocha --ui mocha-fibers --recursive --reporter spec --require test/test-bootstrap.js --timeout 15000 test/" }, "repository": { "type": "git", @@ -24,29 +24,32 @@ "dependencies": { "fibers": "https://github.com/icenium/node-fibers/tarball/master", "filesize": "2.0.3", - "progress-stream": "0.5.0", "log4js": "0.6.9", + "mkdirp": "0.3.5", + "npm": "1.4.10", "osenv": "0.1.0", + "progress-stream": "0.5.0", + "properties-parser": "0.2.3", + "rimraf": "2.2.6", + "semver": "3.0.1", + "shelljs": "0.3.0", "tabtab": "https://github.com/tailsu/node-tabtab/tarball/master", "underscore": "1.5.2", "unzip": "0.1.9", - "yargs": "1.2.2", - "npm": "1.4.10", - "properties-parser": "0.2.3", "watchr": "2.4.11", - "rimraf": "2.2.6", - "mkdirp": "0.3.5", - "shelljs": "0.3.0", - "semver": "3.0.1" + "yargs": "1.2.2" }, "analyze": true, "devDependencies": { "grunt": "0.4.2", - "grunt-ts": "1.11.2", "grunt-contrib-clean": "0.5.0", + "grunt-contrib-copy": "0.5.0", "grunt-contrib-watch": "0.5.3", "grunt-shell": "0.6.4", - "grunt-contrib-copy": "0.5.0" + "grunt-ts": "1.11.2", + "mocha": "1.21.4", + "mocha-fibers": "https://github.com/tailsu/mocha-fibers/tarball/master", + "should": "4.0.4" }, "license": "Apache-2.0", "engines": { diff --git a/test/.d.ts b/test/.d.ts new file mode 100644 index 0000000000..b9a278d22c --- /dev/null +++ b/test/.d.ts @@ -0,0 +1,4 @@ +/// +/// +/// + diff --git a/test/definitions/mocha.d.ts b/test/definitions/mocha.d.ts new file mode 100644 index 0000000000..64255a7ba1 --- /dev/null +++ b/test/definitions/mocha.d.ts @@ -0,0 +1,110 @@ +// Type definitions for mocha 1.17.1 +// Project: http://visionmedia.github.io/mocha/ +// Definitions by: Kazi Manzur Rashid , otiai10 +// Definitions: https://github.com/borisyankov/DefinitelyTyped + +interface Mocha { + // Setup mocha with the given setting options. + setup(options: MochaSetupOptions): Mocha; + + //Run tests and invoke `fn()` when complete. + run(callback?: () => void): void; + + // Set reporter as function + reporter(reporter: () => void): Mocha; + + // Set reporter, defaults to "dot" + reporter(reporter: string): Mocha; + + // Enable growl support. + growl(): Mocha +} + +interface MochaSetupOptions { + //milliseconds to wait before considering a test slow + slow?: number; + + // timeout in milliseconds + timeout?: number; + + // ui name "bdd", "tdd", "exports" etc + ui?: string; + + //array of accepted globals + globals?: any[]; + + // reporter instance (function or string), defaults to `mocha.reporters.Dot` + reporter?: any; + + // bail on the first test failure + bail?: Boolean; + + // ignore global leaks + ignoreLeaks?: Boolean; + + // grep string or regexp to filter tests with + grep?: any; +} + +interface MochaDone { + (error?: Error): void; +} + +declare var mocha: Mocha; + +declare var describe : { + (description: string, spec: () => void): void; + only(description: string, spec: () => void): void; + skip(description: string, spec: () => void): void; + timeout(ms: number): void; +} + +// alias for `describe` +declare var context : { + (contextTitle: string, spec: () => void): void; + only(contextTitle: string, spec: () => void): void; + skip(contextTitle: string, spec: () => void): void; + timeout(ms: number): void; +} + +declare var it: { + (expectation: string, assertion?: () => void): void; + (expectation: string, assertion?: (done: MochaDone) => void): void; + only(expectation: string, assertion?: () => void): void; + only(expectation: string, assertion?: (done: MochaDone) => void): void; + skip(expectation: string, assertion?: () => void): void; + skip(expectation: string, assertion?: (done: MochaDone) => void): void; + timeout(ms: number): void; +}; + +declare function before(action: () => void): void; + +declare function before(action: (done: MochaDone) => void): void; + +declare function setup(action: () => void): void; + +declare function setup(action: (done: MochaDone) => void): void; + +declare function after(action: () => void): void; + +declare function after(action: (done: MochaDone) => void): void; + +declare function teardown(action: () => void): void; + +declare function teardown(action: (done: MochaDone) => void): void; + +declare function beforeEach(action: () => void): void; + +declare function beforeEach(action: (done: MochaDone) => void): void; + +declare function suiteSetup(action: () => void): void; + +declare function suiteSetup(action: (done: MochaDone) => void): void; + +declare function afterEach(action: () => void): void; + +declare function afterEach(action: (done: MochaDone) => void): void; + +declare function suiteTeardown(action: () => void): void; + +declare function suiteTeardown(action: (done: MochaDone) => void): void; diff --git a/test/definitions/should.d.ts b/test/definitions/should.d.ts new file mode 100644 index 0000000000..8b4c21fc1e --- /dev/null +++ b/test/definitions/should.d.ts @@ -0,0 +1,118 @@ +// Type definitions for should.js 3.1.2 +// Project: https://github.com/visionmedia/should.js +// Definitions by: Alex Varju , Maxime LUCE +// Definitions: https://github.com/borisyankov/DefinitelyTyped + +interface Object { + should: ShouldAssertion; +} + +interface ShouldAssertion { + // basic grammar + a: ShouldAssertion; + an: ShouldAssertion; + and: ShouldAssertion; + be: ShouldAssertion; + have: ShouldAssertion; + with: ShouldAssertion; + of: ShouldAssertion; + not: ShouldAssertion; + + // validators + arguments: ShouldAssertion; + empty: ShouldAssertion; + ok: ShouldAssertion; + true: ShouldAssertion; + false: ShouldAssertion; + NaN: ShouldAssertion; + Infinity: ShouldAssertion; + Array: ShouldAssertion; + Object: ShouldAssertion; + String: ShouldAssertion; + Boolean: ShouldAssertion; + Number: ShouldAssertion; + Error: ShouldAssertion; + Function: ShouldAssertion; + eql(expected: any, description?: string): ShouldAssertion; + equal(expected: any, description?: string): ShouldAssertion; + within(start: number, finish: number, description?: string): ShouldAssertion; + approximately(value: number, delta: number, description?: string): ShouldAssertion; + type(expected: any, description?: string): ShouldAssertion; + instanceof(constructor: Function, description?: string): ShouldAssertion; + above(n: number, description?: string): ShouldAssertion; + below(n: number, description?: string): ShouldAssertion; + match(other: {}, description?: string): ShouldAssertion; + match(other: (val: any) => any, description?: string): ShouldAssertion; + match(regexp: RegExp, description?: string): ShouldAssertion; + match(other: any, description?: string): ShouldAssertion; + matchEach(other: {}, description?: string): ShouldAssertion; + matchEach(other: (val: any) => any, description?: string): ShouldAssertion; + matchEach(regexp: RegExp, description?: string): ShouldAssertion; + matchEach(other: any, description?: string): ShouldAssertion; + length(n: number, description?: string): ShouldAssertion; + property(name: string, description?: string): ShouldAssertion; + property(name: string, val: any, description?: string): ShouldAssertion; + properties(names: string[]): ShouldAssertion; + properties(name: string): ShouldAssertion; + properties(descriptor: any): ShouldAssertion; + properties(...properties: string[]): ShouldAssertion; + ownProperty(name: string, description?: string): ShouldAssertion; + contain(obj: any): ShouldAssertion; + containEql(obj: any): ShouldAssertion; + containDeep(obj: any): ShouldAssertion; + keys(...allKeys: string[]): ShouldAssertion; + keys(allKeys: string[]): ShouldAssertion; + header(field: string, val?: string): ShouldAssertion; + status(code: number): ShouldAssertion; + json: ShouldAssertion; + html: ShouldAssertion; + startWith(expected: string, message?: any): ShouldAssertion; + endWith(expected: string, message?: any): ShouldAssertion; + throw(message?: any): ShouldAssertion; + + // deprecated + include(obj: any, description?: string): ShouldAssertion; + includeEql(obj: any[], description?: string): ShouldAssertion; + + // aliases + exactly(expected: any, description?: string): ShouldAssertion; + instanceOf(constructor: Function, description?: string): ShouldAssertion; + throwError(message?: any): ShouldAssertion; + lengthOf(n: number, description?: string): ShouldAssertion; + key(key: string): ShouldAssertion; + haveOwnProperty(name: string, description?: string): ShouldAssertion; + greaterThan(n: number, description?: string): ShouldAssertion; + lessThan(n: number, description?: string): ShouldAssertion; +} + +interface ShouldInternal { + // should.js's extras + exist(actual: any): void; + exists(actual: any): void; + not: ShouldInternal; +} + +interface Internal extends ShouldInternal { + (obj: any): ShouldAssertion; + + // node.js's assert functions + fail(actual: any, expected: any, message: string, operator: string): void; + assert(value: any, message: string): void; + ok(value: any, message?: string): void; + equal(actual: any, expected: any, message?: string): void; + notEqual(actual: any, expected: any, message?: string): void; + deepEqual(actual: any, expected: any, message?: string): void; + notDeepEqual(actual: any, expected: any, message?: string): void; + strictEqual(actual: any, expected: any, message?: string): void; + notStrictEqual(actual: any, expected: any, message?: string): void; + throws(block: any, error?: any, message?: string): void; + doesNotThrow(block: any, message?: string): void; + ifError(value: any): void; + inspect(value: any, obj: any): any; +} + +declare var should: Internal; + +declare module "should" { + export = should; +} diff --git a/test/platform-service.ts b/test/platform-service.ts new file mode 100644 index 0000000000..4c12ac216f --- /dev/null +++ b/test/platform-service.ts @@ -0,0 +1,29 @@ +/// + +import PlatformServiceLib = require('../lib/services/platform-service'); +import NodePackageManagerLib = require('../lib/node-package-manager'); +import ProjectLib = require('../lib/services/project-service'); +import stubs = require('./stubs'); + +import yok = require('../lib/common/yok'); + +require('should'); + +var testInjector = new yok.Yok(); +testInjector.register('platformService', PlatformServiceLib.PlatformService); +testInjector.register('errors', stubs.ErrorsStub); +testInjector.register('fs', stubs.FileSystemStub); +testInjector.register('logger', stubs.LoggerStub); +testInjector.register('npm', stubs.NPMStub); +testInjector.register('projectData', stubs.ProjectDataStub); +testInjector.register('platformsData', stubs.PlatformsDataStub); + +describe('PlatformService', function(){ + describe('#updatePlatforms()', function(){ + it('should fail when no services provided', function(){ + var platformService = testInjector.resolve('platformService'); + (function(){return platformService.updatePlatforms().wait(); }).should.throw(); + + }) + }) +}); diff --git a/test/stubs.ts b/test/stubs.ts new file mode 100644 index 0000000000..46656cf2b9 --- /dev/null +++ b/test/stubs.ts @@ -0,0 +1,172 @@ +/// + +import Future = require("fibers/future"); +import util = require("util"); +import path = require("path"); + +export class LoggerStub implements ILogger { + setLevel(level: string): void {} + fatal(...args: string[]): void {} + error(...args: string[]): void {} + warn(...args: string[]): void {} + info(...args: string[]): void {} + debug(...args: string[]): void {} + trace(...args: string[]): void {} + + public output = ""; + + out(...args: string[]): void { + this.output += util.format.apply(null, args) + "\n"; + } + + write(...args: string[]): void { } +} + +export class FileSystemStub implements IFileSystem { + zipFiles(zipFile: string, files: string[], zipPathCallback: (path: string) => string): IFuture { + return undefined; + } + + unzip(zipFile: string, destination: string): IFuture { + return undefined; + } + exists(path: string): IFuture { + return Future.fromResult(true); + } + + deleteFile(path:string):IFuture { + return undefined; + } + + deleteDirectory(directory: string): IFuture { + return undefined; + } + + getFileSize(path:string):IFuture { + return undefined; + } + + futureFromEvent(eventEmitter: any, event: string): IFuture { + return undefined; + } + + createDirectory(path:string):IFuture { + return undefined; + } + + readDirectory(path:string):IFuture { + return undefined; + } + + readFile(filename:string):IFuture { + return undefined; + } + + readText(filename:string, encoding?:string):IFuture { + return undefined; + } + + readJson(filename:string, encoding?:string):IFuture { + return Future.fromResult({}); + } + + writeFile(filename: string, data: any, encoding?: string): IFuture { + return undefined; + } + + writeJson(filename: string, data: any, space?: string, encoding?: string): IFuture { + return undefined; + } + + copyFile(sourceFileName:string, destinationFileName:string):IFuture { + return undefined; + } + + openFile(filename: string): void { } + + createReadStream(path:string, options?:{flags?: string; encoding?: string; fd?: string; mode?: number; bufferSize?: number}): any { + return undefined; + } + + createWriteStream(path:string, options?:{flags?: string; encoding?: string; string?: string}): any { + return undefined; + } + + chmod(path: string, mode: number): IFuture { + return undefined; + } + + getUniqueFileName(baseName: string): IFuture { + return undefined; + } + + getFsStats(path: string): IFuture { + return undefined; + } + + isEmptyDir(directoryPath: string): IFuture { + return undefined; + } + + ensureDirectoryExists(directoryPath: string): IFuture { + return undefined; + } + + rename(oldPath: string, newPath: string): IFuture { + return undefined; + } +} + +export class ErrorsStub implements IErrors { + private impl: IErrors = new (require("../lib/common/errors").Errors)(); + + fail(formatStr:string, ...args: any[]): void; + fail(opts:{formatStr?: string; errorCode?: number; suppressCommandHelp?: boolean}, ...args: any[]): void; + + fail(...args: any[]) { + throw args; + } + + beginCommand(action:() => void, printHelpCommand: () => void) { + throw new Error("not supported"); + } + + verifyHeap(message: string): void { + + } +} + +export class NPMStub implements INodePackageManager { + get cache(): string { + return undefined; + } + + load(config?: any): IFuture { + return undefined; + } + + install(packageName: string, pathToSave?: string, version?: string): IFuture { + return undefined; + } +} + +export class ProjectDataStub implements IProjectData { + public projectDir: string; + public platformsDir: string; + public projectFilePath: string; + public projectId: string; + public projectName: string; +} + +export class PlatformsDataStub implements IPlatformsData { + public get platformsNames(): string[] { + return undefined; + } + + public getPlatformData(platform: string): IPlatformData { + return undefined; + } +} + + + diff --git a/test/test-bootstrap.ts b/test/test-bootstrap.ts new file mode 100644 index 0000000000..21358bba2e --- /dev/null +++ b/test/test-bootstrap.ts @@ -0,0 +1,6 @@ +global._ = require("underscore"); +global.$injector = require("../lib/common/yok").injector; + +process.on('exit', (code: number) => { + require("fibers/future").assertNoFutureLeftBehind(); +});