Skip to content

Commit

Permalink
feat(typescript): add types
Browse files Browse the repository at this point in the history
  • Loading branch information
sjelin committed Jan 13, 2017
1 parent 27b4850 commit 07a2486
Show file tree
Hide file tree
Showing 7 changed files with 142 additions and 59 deletions.
18 changes: 18 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,24 @@ describe('tests with webdriver', function() {
})
```

TypeScript
----------

For the typings related to the changes in the global jasmine variables (e.g.
allowing `it()` blocks to return a promise), we publish the package
`@types/jasminewd2`. If you are writing tests using jasminewd (including
Protractor tests), be sure to include `@types/jasminewd2` in your
`devDependencies`, as these global type modifications are ***not*** bundled with
the `jasminewd2` npm module.

jasminewd also exports one function directly: `init`. Unfortunately, we do not
publish typings for this function. If you call this function directly (e.g. you
are a Protractor dev), you should simply do:

```ts
require('jasminewd2').init(controlFlow);
```

`async` functions / `await`
---------------------------

Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
"selenium-webdriver": "3.0.1"
},
"devDependencies": {
"@types/jasmine": "^2.5.40",
"@types/node": "^6.0.56",
"@types/selenium-webdriver": "^2.53.38",
"jasmine": "2.4.1",
Expand Down
73 changes: 73 additions & 0 deletions spec/@types_jasminewd2.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
// This is jasminewd's internal version of @types/jasminewd2. If you need types
// for jasminewd2, please use @types/jasminewd2 instead

// Type definitions for jasminewd2
// Project https://github.com/angular/jasminewd
// Definitions by: Sammy Jelin <https://github.com/sjelin>

declare function it(expectation: string, assertion?: () => Promise<void>, timeout?: number): void;
declare function fit(expectation: string, assertion?: () => Promise<void>, timeout?: number): void;
declare function xit(expectation: string, assertion?: () => Promise<void>, timeout?: number): void;
declare function beforeEach(action: () => Promise<void>, timeout?: number): void;
declare function afterEach(action: () => Promise<void>, timeout?: number): void;
declare function beforeAll(action: () => Promise<void>, timeout?: number): void;
declare function afterAll(action: () => Promise<void>, timeout?: number): void;

declare namespace jasmine {
// The global `Promise` type is too strict and kinda wrong
interface Promise<T> {
then<U>(onFulfill?: (value: T) => U | Promise<U>, onReject?: (error: any) => U | Promise<U>): Promise<U>;
}

interface Matchers {
toBe(expected: any, expectationFailOutput?: any): Promise<void>;
toEqual(expected: any, expectationFailOutput?: any): Promise<void>;
toMatch(expected: string | RegExp | Promise<string | RegExp>, expectationFailOutput?: any): Promise<void>;
toBeDefined(expectationFailOutput?: any): Promise<void>;
toBeUndefined(expectationFailOutput?: any): Promise<void>;
toBeNull(expectationFailOutput?: any): Promise<void>;
toBeNaN(): Promise<void>;
toBeTruthy(expectationFailOutput?: any): Promise<void>;
toBeFalsy(expectationFailOutput?: any): Promise<void>;
toHaveBeenCalled(): Promise<void>;
toHaveBeenCalledWith(...params: any[]): Promise<void>;
toHaveBeenCalledTimes(expected: number | Promise<number>): Promise<void>;
toContain(expected: any, expectationFailOutput?: any): Promise<void>;
toBeLessThan(expected: number | Promise<number>, expectationFailOutput?: any): Promise<void>;
toBeLessThanOrEqual(expected: number | Promise<number>, expectationFailOutput?: any): Promise<void>;
toBeGreaterThan(expected: number | Promise<number>, expectationFailOutput?: any): Promise<void>;
toBeGreaterThanOrEqual(expected: number | Promise<number>, expectationFailOutput?: any): Promise<void>;
toBeCloseTo(expected: number | Promise<number>, precision?: any, expectationFailOutput?: any): Promise<void>;
toThrow(expected?: any): Promise<void>;
toThrowError(message?: string | RegExp | Promise<string | RegExp>): Promise<void>;
toThrowError(expected?: new (...args: any[]) => Error | Promise<new (...args: any[]) => Error>, message?: string | RegExp | Promise<string | RegExp>): Promise<void>;
}

function addMatchers(matchers: AsyncCustomMatcherFactories): void;

interface Env {
addMatchers(matchers: AsyncCustomMatcherFactories): void;
}

interface Spec {
addMatchers(matchers: AsyncCustomMatcherFactories): void;
}

interface AsyncCustomMatcherFactories {
[index: string]: AsyncCustomMatcherFactory;
}

interface AsyncCustomMatcherFactory {
(util: MatchersUtil, customEqualityTesters: Array<CustomEqualityTester>): AsyncCustomMatcher;
}

interface AsyncCustomMatcher {
compare<T>(actual: T, expected: T): AsyncCustomMatcherResult;
compare(actual: any, expected: any): AsyncCustomMatcherResult;
}

interface AsyncCustomMatcherResult {
pass: boolean | Promise<boolean>;
message?: string;
}
}
16 changes: 3 additions & 13 deletions spec/asyncAwaitAdapterSpec.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,13 @@
import {promise as wdpromise, WebElement} from 'selenium-webdriver';
const common = require('./common');

declare function expect(actual: any): any;
declare function describe(description: string, tests: Function): void;
declare function it(description: string, test?: Function, timeout?: number): any;
declare function xit(description: string, test?: Function, timeout?: number): any;
declare function beforeEach(setup: Function): void;
declare function beforeAll(setup: Function): void;
declare function afterEach(setup: Function): void;
declare function afterAll(setup: Function): void;
declare var jasmine;
import {getFakeDriver, getMatchers} from './common.js';

/**
* This file is very similar to adapterSpec.ts, but we use async/await instead
* of the WebDriver Control Flow for synchronization. These tests are desgined
* to work regardless of if the WebDriver Control Flow is disabled.
*/

const fakeDriver = common.getFakeDriver();
const fakeDriver = getFakeDriver();

/* jshint esversion: 6 */
describe('webdriverJS Jasmine adapter plain', function() {
Expand Down Expand Up @@ -54,7 +44,7 @@ describe('webdriverJS Jasmine adapter', function() {
let beforeEachMsg: string;

beforeEach(function() {
jasmine.addMatchers(common.getMatchers());
jasmine.addMatchers(getMatchers());
});

beforeEach(async function() {
Expand Down
16 changes: 3 additions & 13 deletions spec/asyncAwaitErrorSpec.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,12 @@
const common = require('./common');

declare function expect(actual: any): any;
declare function describe(description: string, tests: Function): void;
declare function it(description: string, test?: Function, timeout?: number): any;
declare function xit(description: string, test?: Function, timeout?: number): any;
declare function beforeEach(setup: Function): void;
declare function beforeAll(setup: Function): void;
declare function afterEach(setup: Function): void;
declare function afterAll(setup: Function): void;
declare var jasmine;
import {getFakeDriver, getMatchers} from './common.js';

/**
* This file is very similar to errorSpec.ts, but we use async/await instead of
* the WebDriver Control Flow for synchronization. These tests are desgined to
* work regardless of if the WebDriver Control Flow is disabled.
*/

const fakeDriver = common.getFakeDriver();
const fakeDriver = getFakeDriver();

/* jshint esversion: 6 */
describe('Timeout cases', function() {
Expand All @@ -38,7 +28,7 @@ describe('Timeout cases', function() {

describe('things that should fail', function() {
beforeEach(function() {
jasmine.addMatchers(common.getMatchers());
jasmine.addMatchers(getMatchers());
});

it('should pass errors from done callback', function(done) {
Expand Down
75 changes: 43 additions & 32 deletions spec/common.js → spec/common.ts
Original file line number Diff line number Diff line change
@@ -1,110 +1,110 @@
var webdriver = require('selenium-webdriver');
import {promise as wdpromise, WebElement} from 'selenium-webdriver';

var flow = webdriver.promise.controlFlow();
const flow = wdpromise.controlFlow();
require('../index.js').init(flow);

exports.getFakeDriver = function() {
export function getFakeDriver() {
return {
controlFlow: function() {
return flow;
},
sleep: function(ms) {
sleep: function(ms: number) {
return flow.timeout(ms);
},
setUp: function() {
return flow.execute(function() {
return webdriver.promise.when('setup done');
return wdpromise.when('setup done');
}, 'setUp');
},
getValueA: function() {
return flow.execute(function() {
return webdriver.promise.delayed(500).then(function() {
return webdriver.promise.when('a');
return wdpromise.delayed(500).then(function() {
return wdpromise.when('a');
});
}, 'getValueA');
},
getOtherValueA: function() {
return flow.execute(function() {
return webdriver.promise.when('a');
return wdpromise.when('a');
}, 'getOtherValueA');
},
getValueB: function() {
return flow.execute(function() {
return webdriver.promise.when('b');
return wdpromise.when('b');
}, 'getValueB');
},
getBigNumber: function() {
getBigNumber: function(): wdpromise.Promise<number> {
return flow.execute(function() {
return webdriver.promise.when(1111);
return wdpromise.when(1111);
}, 'getBigNumber');
},
getSmallNumber: function() {
getSmallNumber: function(): wdpromise.Promise<number> {
return flow.execute(function() {
return webdriver.promise.when(11);
return wdpromise.when(11);
}, 'getSmallNumber');
},
getDecimalNumber: function() {
getDecimalNumber: function(): wdpromise.Promise<number> {
return flow.execute(function() {
return webdriver.promise.when(3.14159);
return wdpromise.when(3.14159);
}, 'getDecimalNumber');
},
getDisplayedElement: function() {
return flow.execute(function() {
return webdriver.promise.when({
return wdpromise.when({
isDisplayed: function() {
return webdriver.promise.when(true);
return wdpromise.when(true);
}
});
}, 'getDisplayedElement');
},
getHiddenElement: function() {
return flow.execute(function() {
return webdriver.promise.when({
return wdpromise.when({
isDisplayed: function() {
return webdriver.promise.when(false);
return wdpromise.when(false);
}
});
}, 'getHiddenElement');
},
getValueList: function() {
getValueList: function(): wdpromise.Promise<Array<{getText: () => wdpromise.Promise<string>}>> {
return flow.execute(function() {
return webdriver.promise.when([{
return wdpromise.when([{
getText: function() {
return flow.execute(function() { return webdriver.promise.when('a');});
return flow.execute(function() { return wdpromise.when('a');});
}
}, {
getText: function() {
return flow.execute(function() { return webdriver.promise.when('b');});
return flow.execute(function() { return wdpromise.when('b');});
}
}, {
getText: function() {
return flow.execute(function() { return webdriver.promise.when('c');});
return flow.execute(function() { return wdpromise.when('c');});
}
}, {
getText: function() {
return flow.execute(function() { return webdriver.promise.when('d');});
return flow.execute(function() { return wdpromise.when('d');});
}
}]);
}, 'getValueList');
},
displayedElement: {
isDisplayed: function() {
return webdriver.promise.when(true);
return wdpromise.when(true);
}
},
hiddenElement: {
isDisplayed: function() {
return webdriver.promise.when(false);
return wdpromise.when(false);
}
}
};
};

exports.getMatchers = function() {
export function getMatchers() {
return {
toBeLotsMoreThan: function() {
return {
compare: function(actual, expected) {
compare: function(actual: number, expected: number) {
return {
pass: actual > expected + 100
};
Expand All @@ -114,7 +114,7 @@ exports.getMatchers = function() {
// Example custom matcher returning a promise that resolves to true/false.
toBeDisplayed: function() {
return {
compare: function(actual, expected) {
compare: function(actual: WebElement, expected: void) {
return {
pass: actual.isDisplayed()
};
Expand All @@ -124,6 +124,17 @@ exports.getMatchers = function() {
};
};

exports.isPending = function(managedPromise) {
return managedPromise.state_ === 'pending';
// declare custom matcher types
declare global {
namespace jasmine {
interface Matchers {
toBeLotsMoreThan(expected: number | Promise<number>): Promise<void>;
toBeDisplayed(): Promise<void>;
}
}
}


export function isPending(managedPromise: wdpromise.Promise<any>) {
return (managedPromise as any).state_ === 'pending';
};
2 changes: 1 addition & 1 deletion tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"sourceMap": false,
"declaration": false,
"removeComments": false,
"noImplicitAny": false,
"noImplicitAny": true,
"outDir": "built_spec"
},
"include": [
Expand Down

0 comments on commit 07a2486

Please sign in to comment.