Skip to content
This repository has been archived by the owner on Nov 6, 2020. It is now read-only.

Commit

Permalink
Adding additional unit tests and new integration tests (#47)
Browse files Browse the repository at this point in the history
* Add additional unit tests (gitcontext, userinfo)

* Adding integration tests
  • Loading branch information
Jeff Young committed Sep 9, 2016
1 parent d15acda commit 0203102
Show file tree
Hide file tree
Showing 29 changed files with 971 additions and 15 deletions.
13 changes: 12 additions & 1 deletion .vscode/launch.json
Expand Up @@ -23,7 +23,18 @@
"stopOnEntry": false,
"sourceMaps": true,
"outDir": "${workspaceRoot}/out/test",
"preLaunchTask": "publishbuild"
"preLaunchTask": "publishall"
},
{
"name": "Launch Integration Tests",
"type": "extensionHost",
"request": "launch",
"runtimeExecutable": "${execPath}",
"args": ["--extensionDevelopmentPath=${workspaceRoot}", "--extensionTestsPath=${workspaceRoot}/out/test-integration" ],
"stopOnEntry": false,
"sourceMaps": true,
"outDir": "${workspaceRoot}/out/test-integration",
"preLaunchTask": "publishall"
}
]
}
2 changes: 1 addition & 1 deletion .vscode/tasks.json
Expand Up @@ -17,7 +17,7 @@
"tasks": [
{
"isBuildCommand": true,
"taskName": "publishbuild",
"taskName": "publishall",
"showOutput": "always"
},
{
Expand Down
5 changes: 4 additions & 1 deletion .vscodeignore
@@ -1,12 +1,15 @@
.vscode/**
typings/**
out/test/**
out/test-integration/**
test/**
test-integration/**
src/**
**/*.map
.gitignore
tsconfig.json
gulpfile.js
tsd.json
tslint.json
team-extension.log
team-extension.log
**/*.zip
8 changes: 8 additions & 0 deletions CONTRIBUTING.md
Expand Up @@ -90,6 +90,9 @@ These are the clients used to talk to the services (see Services below). The cl
### Helpers
These are classes used to define constants, a logger, settings (configuration), strings and various utility functions.

### Info
These are classes used to hold data about particular objects (credentials, repository and user).

### Services
All of the communication to Team Services should be done via services found in this folder. These services should not know anything about the client-side types used to manipulate the Visual Studio Code UI. The Q Promise APIs found in the vso-node-api package is the model used in this extension.

Expand Down Expand Up @@ -122,3 +125,8 @@ This project has adopted the [Microsoft Open Source Code of Conduct](https://ope
We welcome pull requests! Fork this repo and send us your contributions. Go [here](https://help.github.com/articles/using-pull-requests/) to get familiar with GitHub pull requests.

Before submitting your request, ensure that both `gulp` and `gulp test` succeed.

**UPDATE**: With a recent commit, integration tests were added under the *test-integration* folder. These tests are run by the CI build and the results are reported back to any pull request as a "build check". The
integration tests are not runnable outside of the CI build without setting up additional infrastructure. As such, it isn't required that a contributor run these tests before submitting the pull request.
However, if an issue arises that breaks the integration tests, please file an issue and I'll follow up as quickly as possible. Note that the build for this repo is set to build every night and runs unit
and integration tests at that time.
38 changes: 35 additions & 3 deletions gulpfile.js
Expand Up @@ -6,6 +6,16 @@ var gulp = require('gulp'),
var exec = require('child_process').exec;
var tslint = require('gulp-tslint');
var del = require('del');
var argv = require('yargs').argv;

// Default to list reporter when run directly.
// CI build can pass 'reporter=junit' to create JUnit results files
var reporterUnitTest = { reporter: 'list' };
var reporterIntegrationTest = { reporter: 'list' };
if (argv.reporter === "junit") {
reporterUnitTest = { reporter: 'mocha-junit-reporter', reporterOptions: { mochaFile: 'out/test/results/test-unittestresults.xml'} } ;
reporterIntegrationTest = { reporter: 'mocha-junit-reporter', reporterOptions: { mochaFile: 'out/test/results/test-integrationtestresults.xml'} } ;
}

function errorHandler(err) {
console.error(err.message);
Expand All @@ -26,7 +36,14 @@ gulp.task('tslint-test', function () {
.on('error', errorHandler);
});

gulp.task('clean', ['tslint-src', 'tslint-test'], function (done) {
gulp.task('tslint-test-integration', function () {
return gulp.src(['./test-integration/**/*.ts'])
.pipe(tslint())
.pipe(tslint.report('prose', { emitError: true}))
.on('error', errorHandler);
});

gulp.task('clean', ['tslint-src', 'tslint-test', 'tslint-test-integration'], function (done) {
return del(['out/**', '!out', '!out/src/credentialstore/linux', '!out/src/credentialstore/osx', '!out/src/credentialstore/win32'], done);
});

Expand All @@ -45,14 +62,29 @@ gulp.task('publishbuild', ['build'], function () {
.pipe(gulp.dest('./out/src/credentialstore/bin/win32'));
});

gulp.task('publishall', ['publishbuild'], function () {
gulp.src(['./test/contexts/testrepos/**/*'])
.pipe(gulp.dest('./out/test/contexts/testrepos'));
gulp.src(['./test/contexts/testrepos/**/*'])
.pipe(gulp.dest('./out/test/contexts/testrepos'));
});

//Tests will fail with MODULE_NOT_FOUND if I try to run 'publishBuild' before test target
//gulp.task('test', ['publishBuild'], function() {
gulp.task('test', function() {
return gulp.src(['out/test/**/*.js'], {read: false})
.pipe(mocha({reporter: 'list'}))
.pipe(mocha(reporterUnitTest))
.on('error', errorHandler);
});

gulp.task('test-integration', function() {
return gulp.src(['out/test-integration/**/*.js'], {read: false})
.pipe(mocha(reporterIntegrationTest))
.on('error', errorHandler);
});

gulp.task('test-all', ['test', 'test-integration'], function() { });

gulp.task('packageonly', function (cb) {
exec('vsce package', function (err, stdout, stderr) {
console.log(stdout);
Expand All @@ -61,7 +93,7 @@ gulp.task('packageonly', function (cb) {
});
});

gulp.task('package', ['publishbuild'], function (cb) {
gulp.task('package', ['publishall'], function (cb) {
exec('vsce package', function (err, stdout, stderr) {
console.log(stdout);
console.log(stderr);
Expand Down
5 changes: 3 additions & 2 deletions package.json
Expand Up @@ -115,7 +115,6 @@
]
},
"scripts": {
"vscode:prepublish": "node ./node_modules/vscode/bin/compile",
"compile": "node ./node_modules/vscode/bin/compile -watch -p ./",
"postinstall": "node ./node_modules/vscode/bin/install"
},
Expand All @@ -126,10 +125,12 @@
"gulp-mocha": "^2.2.0",
"gulp-tslint": "^3.2.0",
"gulp-util": "^3.0.7",
"mocha-junit-reporter": "^1.12.0",
"should": "^8.1.1",
"tslint": "^2.5.0",
"typescript": "^1.7.5",
"vscode": "^0.11.x"
"vscode": "^0.11.x",
"yargs": "^5.0.0"
},
"dependencies": {
"applicationinsights": "0.15.16",
Expand Down
27 changes: 23 additions & 4 deletions src/contexts/gitcontext.ts
Expand Up @@ -25,14 +25,27 @@ export class GitContext {
private _isTeamServicesUrl: boolean = false;
private _isTeamFoundationServer: boolean = false;

constructor(rootPath: string) {
//When gitDir is provided, rootPath is the path to the Git repo
constructor(rootPath: string, gitDir?: string) {
if (rootPath) {
this._gitFolder = Utils.FindGitFolder(rootPath);
//If gitDir, use rootPath as the .git folder
if (gitDir) {
this._gitFolder = rootPath;
gri._changeGitDir(gitDir);
} else {
this._gitFolder = Utils.FindGitFolder(rootPath);
}

if (this._gitFolder != null) {
// With parse-git-config, cwd is the directory containing the path, .git/config, you want to sync
this._gitParentFolder = path.dirname(this._gitFolder);
this._gitConfig = pgc.sync({ cwd: this._gitParentFolder});
let syncObj: any = { cwd: this._gitParentFolder };
//If gitDir, send pgc the exact path to the config file to use
if (gitDir) {
syncObj = { path: path.join(this._gitFolder, "config") };
}
this._gitConfig = pgc.sync(syncObj);

/* tslint:disable:quotemark */
let remote: any = this._gitConfig['remote "origin"'];
/* tslint:enable:quotemark */
Expand All @@ -41,7 +54,12 @@ export class GitContext {
}
this._gitOriginalRemoteUrl = remote.url;

this._gitRepoInfo = gri(this._gitFolder);
if (gitDir) {
this._gitRepoInfo = gri(this._gitParentFolder);
} else {
this._gitRepoInfo = gri(this._gitFolder);
}

this._gitCurrentBranch = this._gitRepoInfo.branch;
this._gitCurrentRef = "refs/heads/" + this._gitCurrentBranch;

Expand All @@ -63,6 +81,7 @@ export class GitContext {
this._isTeamFoundationServer = true;
this._gitRemoteUrl = this._gitOriginalRemoteUrl;
if (purl.protocol.toLowerCase() === "ssh:") {
this._isSsh = true;
// TODO: No support yet for SSH on-premises (no-op the extension)
this._isTeamFoundationServer = false;
}
Expand Down
4 changes: 4 additions & 0 deletions src/team-extension.ts
Expand Up @@ -26,6 +26,10 @@ import { UserInfo } from "./info/userinfo";
var os = require("os");
var path = require("path");

/* tslint:disable:no-unused-variable */
import Q = require("q");
/* tslint:enable:no-unused-variable */

export class TeamExtension {
private _teamServicesStatusBarItem: StatusBarItem;
private _buildStatusBarItem: StatusBarItem;
Expand Down
78 changes: 78 additions & 0 deletions test-integration/clients/teamservicesclient.integration.test.ts
@@ -0,0 +1,78 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
"use strict";

import { Mocks } from "../helpers-integration/mocks";
import { TestSettings } from "../helpers-integration/testsettings";

import { CredentialManager } from "../../src/helpers/credentialmanager";
import { TeamServerContext } from "../../src/contexts/servercontext";
import { QTeamServicesApi } from "../../src/clients/teamservicesclient";

var chai = require("chai");
/* tslint:disable:no-unused-variable */
var expect = chai.expect;
/* tslint:enable:no-unused-variable */
var assert = chai.assert;
chai.should();

describe("TeamServicesClient-Integration", function() {
this.timeout(TestSettings.SuiteTimeout()); //http://mochajs.org/#timeouts

var credentialManager: CredentialManager = new CredentialManager();
var ctx: TeamServerContext = Mocks.TeamServerContext(TestSettings.RemoteRepositoryUrl());

before(function() {
return credentialManager.StoreCredentials(TestSettings.Account(), TestSettings.AccountUser(), TestSettings.Password());
});
beforeEach(function() {
return credentialManager.GetCredentialHandler(ctx, undefined);
});
//afterEach(function() { });
after(function() {
return credentialManager.RemoveCredentials(TestSettings.Account());
});

it("should verify repositoryClient.getVstsInfo", function(done) {
this.timeout(TestSettings.TestTimeout()); //http://mochajs.org/#timeouts

let repositoryClient: QTeamServicesApi = new QTeamServicesApi(TestSettings.RemoteRepositoryUrl(), [CredentialManager.GetCredentialHandler()]);
repositoryClient.getVstsInfo().then(
function (repoInfo) {
//console.log(repoInfo);
assert.isNotNull(repoInfo, "repoInfo was null when it shouldn't have been");
assert.equal(repoInfo.serverUrl, TestSettings.AccountUrl());
assert.equal(repoInfo.collection.name, TestSettings.CollectionName());
assert.equal(repoInfo.repository.remoteUrl, TestSettings.RemoteRepositoryUrl());
assert.equal(repoInfo.repository.id, TestSettings.RepositoryId());
expect(repoInfo.repository.name).to.equal(TestSettings.RepositoryName());
done();
},
function (err) {
done(err);
}
);
});

it("should verify accountClient.connect", function(done) {
this.timeout(TestSettings.TestTimeout()); //http://mochajs.org/#timeouts

let accountClient: QTeamServicesApi = new QTeamServicesApi(TestSettings.AccountUrl(), [CredentialManager.GetCredentialHandler()]);
accountClient.connect().then(
function (settings) {
//console.log(settings);
assert.isNotNull(settings, "settings was null when it shouldn't have been");
assert.isNotNull(settings.providerDisplayName);
assert.isNotNull(settings.customDisplayName);
assert.isNotNull(settings.authorizedUser.providerDisplayName);
assert.isNotNull(settings.authorizedUser.customDisplayName);
done();
},
function (err) {
done(err);
}
);
});
});
51 changes: 51 additions & 0 deletions test-integration/helpers-integration/mocks.ts
@@ -0,0 +1,51 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
"use strict";

import { TeamServerContext } from "../../src/contexts/servercontext";
import { RepositoryInfo } from "../../src/info/repositoryinfo";
import { TestSettings } from "./testsettings";

export class Mocks {

public static TeamServerContext(repositoryUrl: string) : TeamServerContext {
return new TeamServerContext(repositoryUrl);
}

public static RepositoryInfo() : RepositoryInfo {
let repositoryInfo : any = {
"serverUrl":"undefined",
"collection":{
"id":"undefined",
"name":"undefined",
"url":"undefined"
},
"repository":{
"id":"undefined",
"name":"undefined",
"url":"undefined",
"project":{
"id":"undefined",
"name":"undefined",
"url":"undefined",
"state":1,
"revision":115
},
"remoteUrl":"undefined"
}
};
repositoryInfo.serverUrl = TestSettings.AccountUrl();
repositoryInfo.collection.name = TestSettings.CollectionName();
repositoryInfo.collection.url = TestSettings.AccountUrl() + "/_apis/projectCollections/" + TestSettings.CollectionGuid();
repositoryInfo.repository.id = TestSettings.RepositoryId();
repositoryInfo.repository.name = TestSettings.RepositoryName();
repositoryInfo.repository.url = TestSettings.AccountUrl() + "/DefaultCollection/_apis/git/repositories/" + TestSettings.RepositoryId();
repositoryInfo.repository.project.name = TestSettings.TeamProject();
repositoryInfo.repository.project.url = TestSettings.AccountUrl() + "/DefaultCollection/_apis/projects/" + + TestSettings.ProjectGuid();
repositoryInfo.repository.remoteUrl = TestSettings.RemoteRepositoryUrl();

return new RepositoryInfo(repositoryInfo);
}
}

0 comments on commit 0203102

Please sign in to comment.