From 07a2486ef4c5230554483f6334d30a1ef5291292 Mon Sep 17 00:00:00 2001 From: Sammy Jelin Date: Wed, 11 Jan 2017 14:25:06 -0800 Subject: [PATCH] feat(typescript): add types --- README.md | 18 +++++++++ package.json | 1 + spec/@types_jasminewd2.d.ts | 73 ++++++++++++++++++++++++++++++++++ spec/asyncAwaitAdapterSpec.ts | 16 ++------ spec/asyncAwaitErrorSpec.ts | 16 ++------ spec/{common.js => common.ts} | 75 ++++++++++++++++++++--------------- tsconfig.json | 2 +- 7 files changed, 142 insertions(+), 59 deletions(-) create mode 100644 spec/@types_jasminewd2.d.ts rename spec/{common.js => common.ts} (51%) diff --git a/README.md b/README.md index a27d4ed..87f461f 100644 --- a/README.md +++ b/README.md @@ -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` --------------------------- diff --git a/package.json b/package.json index 79c7bc3..d6ed1a1 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/spec/@types_jasminewd2.d.ts b/spec/@types_jasminewd2.d.ts new file mode 100644 index 0000000..dc584b6 --- /dev/null +++ b/spec/@types_jasminewd2.d.ts @@ -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 + +declare function it(expectation: string, assertion?: () => Promise, timeout?: number): void; +declare function fit(expectation: string, assertion?: () => Promise, timeout?: number): void; +declare function xit(expectation: string, assertion?: () => Promise, timeout?: number): void; +declare function beforeEach(action: () => Promise, timeout?: number): void; +declare function afterEach(action: () => Promise, timeout?: number): void; +declare function beforeAll(action: () => Promise, timeout?: number): void; +declare function afterAll(action: () => Promise, timeout?: number): void; + +declare namespace jasmine { + // The global `Promise` type is too strict and kinda wrong + interface Promise { + then(onFulfill?: (value: T) => U | Promise, onReject?: (error: any) => U | Promise): Promise; + } + + interface Matchers { + toBe(expected: any, expectationFailOutput?: any): Promise; + toEqual(expected: any, expectationFailOutput?: any): Promise; + toMatch(expected: string | RegExp | Promise, expectationFailOutput?: any): Promise; + toBeDefined(expectationFailOutput?: any): Promise; + toBeUndefined(expectationFailOutput?: any): Promise; + toBeNull(expectationFailOutput?: any): Promise; + toBeNaN(): Promise; + toBeTruthy(expectationFailOutput?: any): Promise; + toBeFalsy(expectationFailOutput?: any): Promise; + toHaveBeenCalled(): Promise; + toHaveBeenCalledWith(...params: any[]): Promise; + toHaveBeenCalledTimes(expected: number | Promise): Promise; + toContain(expected: any, expectationFailOutput?: any): Promise; + toBeLessThan(expected: number | Promise, expectationFailOutput?: any): Promise; + toBeLessThanOrEqual(expected: number | Promise, expectationFailOutput?: any): Promise; + toBeGreaterThan(expected: number | Promise, expectationFailOutput?: any): Promise; + toBeGreaterThanOrEqual(expected: number | Promise, expectationFailOutput?: any): Promise; + toBeCloseTo(expected: number | Promise, precision?: any, expectationFailOutput?: any): Promise; + toThrow(expected?: any): Promise; + toThrowError(message?: string | RegExp | Promise): Promise; + toThrowError(expected?: new (...args: any[]) => Error | Promise Error>, message?: string | RegExp | Promise): Promise; + } + + 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): AsyncCustomMatcher; + } + + interface AsyncCustomMatcher { + compare(actual: T, expected: T): AsyncCustomMatcherResult; + compare(actual: any, expected: any): AsyncCustomMatcherResult; + } + + interface AsyncCustomMatcherResult { + pass: boolean | Promise; + message?: string; + } +} diff --git a/spec/asyncAwaitAdapterSpec.ts b/spec/asyncAwaitAdapterSpec.ts index 6c095c8..54f177f 100644 --- a/spec/asyncAwaitAdapterSpec.ts +++ b/spec/asyncAwaitAdapterSpec.ts @@ -1,15 +1,5 @@ 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 @@ -17,7 +7,7 @@ declare var jasmine; * 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() { @@ -54,7 +44,7 @@ describe('webdriverJS Jasmine adapter', function() { let beforeEachMsg: string; beforeEach(function() { - jasmine.addMatchers(common.getMatchers()); + jasmine.addMatchers(getMatchers()); }); beforeEach(async function() { diff --git a/spec/asyncAwaitErrorSpec.ts b/spec/asyncAwaitErrorSpec.ts index 3a70155..37adebc 100644 --- a/spec/asyncAwaitErrorSpec.ts +++ b/spec/asyncAwaitErrorSpec.ts @@ -1,14 +1,4 @@ -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 @@ -16,7 +6,7 @@ declare var jasmine; * work regardless of if the WebDriver Control Flow is disabled. */ -const fakeDriver = common.getFakeDriver(); +const fakeDriver = getFakeDriver(); /* jshint esversion: 6 */ describe('Timeout cases', function() { @@ -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) { diff --git a/spec/common.js b/spec/common.ts similarity index 51% rename from spec/common.js rename to spec/common.ts index 4ab4964..45640c2 100644 --- a/spec/common.js +++ b/spec/common.ts @@ -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 { return flow.execute(function() { - return webdriver.promise.when(1111); + return wdpromise.when(1111); }, 'getBigNumber'); }, - getSmallNumber: function() { + getSmallNumber: function(): wdpromise.Promise { return flow.execute(function() { - return webdriver.promise.when(11); + return wdpromise.when(11); }, 'getSmallNumber'); }, - getDecimalNumber: function() { + getDecimalNumber: function(): wdpromise.Promise { 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 wdpromise.Promise}>> { 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 }; @@ -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() }; @@ -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): Promise; + toBeDisplayed(): Promise; + } + } +} + + +export function isPending(managedPromise: wdpromise.Promise) { + return (managedPromise as any).state_ === 'pending'; }; diff --git a/tsconfig.json b/tsconfig.json index 9d274dd..ec87d58 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -6,7 +6,7 @@ "sourceMap": false, "declaration": false, "removeComments": false, - "noImplicitAny": false, + "noImplicitAny": true, "outDir": "built_spec" }, "include": [