jasminewd2
Advanced tools
Comparing version 0.1.1 to 2.0.0
# Changelog for jasminewd2 | ||
# 2.0.0 | ||
(Skipping 1.x because `0.0.1` was originally accidently published as `1.0.0`.) | ||
## Breaking changes | ||
- ([fae803c](https://github.com/angular/protractor/commit/fae803cd294e5413523d37bdaa282a9f96cd65a1)) | ||
pass webdriver instance into `init()` instead of using `require()` (#83) | ||
So where as before you would write: | ||
```js | ||
require('jasminewd').init(webdriver.promise.controlFlow()); | ||
``` | ||
Now you will write: | ||
```js | ||
require('jasminewd').init(webdriver.promise.controlFlow(), webdriver); | ||
``` | ||
This removes the dependency on `selenium-webdriver` and protects jasminewd from having a | ||
different webdriver instance than Protractor, which could be a huge problem if they had different | ||
control flow settings. | ||
This is a breaking change because it changes the API for the `init` function. | ||
I also removed the dependency on jasmine, which didn't do anything anyway. Maybe it should have | ||
been a peerDependency but those are deprecated. | ||
## Features | ||
- ([171cbde](https://github.com/angular/protractor/commit/171cbde22f307bd3cc35c4c1785f171392dca8da)) | ||
Added types (though you'll have to wait for `@types/jasminewd2` to use them) (#79) | ||
- ([27b4850](https://github.com/angular/protractor/commit/27b485019589cd662ee69e7920893ffa50774b97)) | ||
Support `SELENIUM_PROMISE_MANAGER=0` (#72) | ||
There are three major ways this was done in this change: | ||
* In `callWhenIdle`, if `flow.isIdle` is not defined, we assume we are working with a | ||
`SimpleScheduler` instance, and so the flow is effectively idle. | ||
* In `initJasmineWd`, if `flow.reset` is not defined, we assume we are working with a | ||
`SimpleScheduler` instance, and so don't bother resetting the flow. | ||
* In `wrapInControlFlow`, we use `flow.promise` to create a new promise if possible. Since | ||
`new webdriver.promise.Promise()` would have always made a `ManagedPromise`, but `flow.promise` | ||
will do the right thing. | ||
* In `wrapCompare`, we avoid the webdriver library entirely, and never instance any extra | ||
promises. Using `webdriver.promise.when` and `webdriver.promise.all` could have been a problem | ||
if our instance of `webdriver` had the control flow turned on, but another instance somewhere | ||
did not (or even the same instance, but just at a different point in time). Instead we use the | ||
new `maybePromise` tool, which is a mess but is also exactly what we want. | ||
* In `specs/*`, we replace `webdriver.promise.fulfilled` with `webdriver.promise.when`. | ||
* In `specs/*`, a new version of `adapterSpec.js` and `errorSpec.js` are created: | ||
`asyncAwaitAdapterSpec.ts` and `asyncAwaitErrorSpec.ts`. | ||
I also also fixed a minor bug where we weren't correctly checking for promises inside an array of | ||
expected results. Before we had: | ||
```js | ||
expected = Array.prototype.slice.call(arguments, 0); | ||
... | ||
webdriver.promise.isPromise(expected); | ||
``` | ||
I thought about it for a little while, and there's no way that's correct. `expected` is an | ||
`Array<any>`, there's no way it has a `.then` function. | ||
Closes https://github.com/angular/jasminewd/issues/69 | ||
## Bug Fixes | ||
- ([369a249](https://github.com/angular/protractor/commit/369a2499189fbcdc541f354cfede49dba9335e6b)) | ||
Don't rely on `webdriver.promise` functions (#82) | ||
While we support `SELENIUM_PROMISE_MANAGER=0` already, we rely on `SimpleScheduler` and some other | ||
utility functions which will be going away after the control flow has been fully deprecated. This | ||
commit allows jasminewd to work without those utility functions, and even allows people to pass | ||
jasminewd their own custom scheduler implementation. | ||
This does not fix our tests, which will also break when those utility functions go away. See | ||
https://github.com/angular/jasminewd/issues/81 | ||
Closes https://github.com/angular/jasminewd/issues/80 | ||
# 0.1.1 | ||
@@ -4,0 +92,0 @@ |
167
index.js
@@ -7,3 +7,5 @@ /** | ||
var webdriver = require('selenium-webdriver'); | ||
var WebElement; // Equal to webdriver.WebElement | ||
var idleEventName = 'idle'; // Equal to webdriver.promise.ControlFlow.EventType.IDLE | ||
var maybePromise = require('./maybePromise'); | ||
@@ -53,13 +55,13 @@ /** | ||
/** | ||
* Calls a function once the control flow is idle | ||
* @param {webdriver.promise.ControlFlow} flow The Web Driver control flow | ||
* @param {!Function} fn The function to call | ||
* Calls a function once the scheduler is idle. If the scheduler does not support the idle API, | ||
* calls the function immediately. See scheduler.md#idle-api for details. | ||
* | ||
* @param {Object} scheduler The scheduler to wait for. | ||
* @param {!Function} fn The function to call. | ||
*/ | ||
function callWhenIdle(flow, fn) { | ||
if (flow.isIdle()) { | ||
function callWhenIdle(scheduler, fn) { | ||
if (!scheduler.once || !scheduler.isIdle || scheduler.isIdle()) { | ||
fn(); | ||
} else { | ||
flow.once(webdriver.promise.ControlFlow.EventType.IDLE, function() { | ||
fn(); | ||
}); | ||
scheduler.once(idleEventName, function() { fn(); }); | ||
} | ||
@@ -70,5 +72,11 @@ } | ||
/** | ||
* Wraps a function so it runs inside a webdriver.promise.ControlFlow and | ||
* waits for the flow to complete before continuing. | ||
* @param {!webdriver.promise.ControlFlow} flow The WebDriver control flow. | ||
* Wraps a function so it runs inside a scheduler's `execute()` block. | ||
* | ||
* In the most common case, this means wrapping in a `webdriver.promise.ControlFlow` instance | ||
* to wait for the control flow to complete one task before starting the next. See scheduler.md | ||
* for details. | ||
* | ||
* @param {!Object} scheduler See scheduler.md for details. | ||
* @param {!Function} newPromise Makes a new promise using whatever implementation the scheduler | ||
* prefers. | ||
* @param {!Function} globalFn The function to wrap. | ||
@@ -78,3 +86,3 @@ * @param {!string} fnName The name of the function being wrapped (e.g. `'it'`). | ||
*/ | ||
function wrapInControlFlow(flow, globalFn, fnName) { | ||
function wrapInScheduler(scheduler, newPromise, globalFn, fnName) { | ||
return function() { | ||
@@ -90,4 +98,4 @@ var driverError = new Error(); | ||
flow.execute(function controlFlowExecute() { | ||
return new webdriver.promise.Promise(function(fulfill, reject) { | ||
scheduler.execute(function schedulerExecute() { | ||
return newPromise(function(fulfill, reject) { | ||
function wrappedReject(err) { | ||
@@ -108,3 +116,3 @@ var wrappedErr = new Error(err); | ||
var ret = testFn(); | ||
if (webdriver.promise.isPromise(ret)) { | ||
if (maybePromise.isPromise(ret)) { | ||
ret.then(fulfill, wrappedReject); | ||
@@ -115,5 +123,5 @@ } else { | ||
} | ||
}, flow); | ||
}); | ||
}, 'Run ' + fnName + description + ' in control flow').then( | ||
callWhenIdle.bind(null, flow, done), function(err) { | ||
callWhenIdle.bind(null, scheduler, done), function(err) { | ||
if (!err) { | ||
@@ -124,3 +132,3 @@ err = new Error('Unknown Error'); | ||
err.stack = err.stack + '\nFrom asynchronous test: \n' + driverError.stack; | ||
callWhenIdle(flow, done.fail.bind(done, err)); | ||
callWhenIdle(scheduler, done.fail.bind(done, err)); | ||
} | ||
@@ -166,8 +174,13 @@ ); | ||
/** | ||
* Initialize the JasmineWd adapter with a particlar webdriver instance. We | ||
* pass webdriver here instead of using require() in order to ensure Protractor | ||
* and Jasminews are using the same webdriver instance. | ||
* @param {Object} flow. The ControlFlow to wrap tests in. | ||
* Initialize the JasmineWd adapter with a particlar scheduler, generally a webdriver control flow. | ||
* | ||
* @param {Object=} scheduler The scheduler to wrap tests in. See scheduler.md for details. | ||
* Defaults to a mock scheduler that calls functions immediately. | ||
* @param {Object=} webdriver The result of `require('selenium-webdriver')`. Passed in here rather | ||
* than required by jasminewd directly so that jasminewd can't end up up with a different version | ||
* of `selenium-webdriver` than your tests use. If not specified, jasminewd will still work, but | ||
* it won't check for `WebElement` instances in expect() statements and could cause control flow | ||
* problems if your tests are using an old version of `selenium-webdriver` (e.g. version 2.53.0). | ||
*/ | ||
function initJasmineWd(flow) { | ||
function initJasmineWd(scheduler, webdriver) { | ||
if (jasmine.JasmineWdInitialized) { | ||
@@ -178,18 +191,57 @@ throw Error('JasmineWd already initialized when init() was called'); | ||
global.it = wrapInControlFlow(flow, global.it, 'it'); | ||
global.fit = wrapInControlFlow(flow, global.fit, 'fit'); | ||
global.beforeEach = wrapInControlFlow(flow, global.beforeEach, 'beforeEach'); | ||
global.afterEach = wrapInControlFlow(flow, global.afterEach, 'afterEach'); | ||
global.beforeAll = wrapInControlFlow(flow, global.beforeAll, 'beforeAll'); | ||
global.afterAll = wrapInControlFlow(flow, global.afterAll, 'afterAll'); | ||
// On timeout, the flow should be reset. This will prevent webdriver tasks | ||
// from overflowing into the next test and causing it to fail or timeout | ||
// as well. This is done in the reporter instead of an afterEach block | ||
// to ensure that it runs after any afterEach() blocks with webdriver tasks | ||
// get to complete first. | ||
jasmine.getEnv().addReporter(new OnTimeoutReporter(function() { | ||
console.warn('A Jasmine spec timed out. Resetting the WebDriver Control Flow.'); | ||
flow.reset(); | ||
})); | ||
// Pull information from webdriver instance | ||
if (webdriver) { | ||
WebElement = webdriver.WebElement || WebElement; | ||
idleEventName = ( | ||
webdriver.promise && | ||
webdriver.promise.ControlFlow && | ||
webdriver.promise.ControlFlow.EventType && | ||
webdriver.promise.ControlFlow.EventType.IDLE | ||
) || idleEventname; | ||
} | ||
// Default to mock scheduler | ||
if (!scheduler) { | ||
scheduler = { execute: function(fn) { | ||
return Promise.resolve().then(fn); | ||
} }; | ||
} | ||
// Figure out how we're getting new promises | ||
var newPromise; | ||
if (typeof scheduler.promise == 'function') { | ||
newPromise = scheduler.promise.bind(scheduler); | ||
} else if (webdriver && webdriver.promise && webdriver.promise.ControlFlow && | ||
(scheduler instanceof webdriver.promise.ControlFlow) && | ||
(webdriver.promise.USE_PROMISE_MANAGER !== false)) { | ||
newPromise = function(resolver) { | ||
return new webdriver.promise.Promise(resolver, scheduler); | ||
}; | ||
} else { | ||
newPromise = function(resolver) { | ||
return new Promise(resolver); | ||
}; | ||
} | ||
// Wrap functions | ||
global.it = wrapInScheduler(scheduler, newPromise, global.it, 'it'); | ||
global.fit = wrapInScheduler(scheduler, newPromise, global.fit, 'fit'); | ||
global.beforeEach = wrapInScheduler(scheduler, newPromise, global.beforeEach, 'beforeEach'); | ||
global.afterEach = wrapInScheduler(scheduler, newPromise, global.afterEach, 'afterEach'); | ||
global.beforeAll = wrapInScheduler(scheduler, newPromise, global.beforeAll, 'beforeAll'); | ||
global.afterAll = wrapInScheduler(scheduler, newPromise, global.afterAll, 'afterAll'); | ||
// Reset API | ||
if (scheduler.reset) { | ||
// On timeout, the flow should be reset. This will prevent webdriver tasks | ||
// from overflowing into the next test and causing it to fail or timeout | ||
// as well. This is done in the reporter instead of an afterEach block | ||
// to ensure that it runs after any afterEach() blocks with webdriver tasks | ||
// get to complete first. | ||
jasmine.getEnv().addReporter(new OnTimeoutReporter(function() { | ||
console.warn('A Jasmine spec timed out. Resetting the WebDriver Control Flow.'); | ||
scheduler.reset(); | ||
})); | ||
} | ||
} | ||
@@ -199,3 +251,3 @@ | ||
global.expect = function(actual) { | ||
if (actual instanceof webdriver.WebElement) { | ||
if (WebElement && (actual instanceof WebElement)) { | ||
throw Error('expect called with WebElement argument, expected a Promise. ' + | ||
@@ -210,2 +262,6 @@ 'Did you mean to use .getText()?'); | ||
* expected values, as well as the `pass` property of the result. | ||
* | ||
* Wrapped matchers will return either `undefined` or a promise which resolves | ||
* when the matcher is complete, depending on if the matcher had to resolve any | ||
* promises. | ||
*/ | ||
@@ -220,12 +276,8 @@ jasmine.Expectation.prototype.wrapCompare = function(name, matcherFactory) { | ||
if (!webdriver.promise.isPromise(expectation.actual) && | ||
!webdriver.promise.isPromise(expected)) { | ||
compare(expectation.actual, expected); | ||
} else { | ||
webdriver.promise.when(expectation.actual).then(function(actual) { | ||
return webdriver.promise.all(expected).then(function(expected) { | ||
return compare(actual, expected); | ||
}); | ||
// Return either undefined or a promise of undefined | ||
return maybePromise(expectation.actual, function(actual) { | ||
return maybePromise.all(expected, function(expected) { | ||
return compare(actual, expected); | ||
}); | ||
} | ||
}); | ||
@@ -245,8 +297,5 @@ function compare(actual, expected) { | ||
if (webdriver.promise.isPromise(result.pass)) { | ||
return webdriver.promise.when(result.pass).then(compareDone); | ||
} else { | ||
return compareDone(result.pass); | ||
} | ||
return maybePromise(result.pass, compareDone); | ||
// compareDone always returns undefined | ||
function compareDone(pass) { | ||
@@ -285,9 +334,5 @@ var message = ''; | ||
var result = matcher.compare.apply(null, args); | ||
if (webdriver.promise.isPromise(result.pass)) { | ||
result.pass = result.pass.then(function(pass) { | ||
return !pass; | ||
}); | ||
} else { | ||
result.pass = !result.pass; | ||
} | ||
result.pass = maybePromise(result.pass, function(pass) { | ||
return !pass; | ||
}); | ||
return result; | ||
@@ -294,0 +339,0 @@ } |
@@ -14,9 +14,13 @@ { | ||
"author": "Julie Ralph <ju.ralph@gmail.com>", | ||
"dependencies": { | ||
"devDependencies": { | ||
"@types/jasmine": "^2.5.40", | ||
"@types/node": "^6.0.56", | ||
"@types/selenium-webdriver": "^2.53.38", | ||
"jasmine": "2.4.1", | ||
"selenium-webdriver": "3.0.1" | ||
}, | ||
"devDependencies": { | ||
"jshint": "^2.9.4", | ||
"typescript": "^2.0.10" | ||
"selenium-webdriver": "3.0.1", | ||
"tslint": "^4.2.0", | ||
"tslint-eslint-rules": "^3.2.3", | ||
"typescript": "^2.0.10", | ||
"vrsource-tslint-rules": "^4.0.0" | ||
}, | ||
@@ -30,4 +34,6 @@ "repository": { | ||
"jshint": "jshint index.js spec", | ||
"tsc": "tsc -t ES2015 spec/asyncAwaitSpec.ts", | ||
"pretest": "npm run jshint && npm run tsc", | ||
"tslint": "tslint spec/*.ts", | ||
"lint": "npm run jshint && npm run tslint", | ||
"tsc": "tsc; cp spec/*.js built_spec", | ||
"pretest": "npm run lint && npm run tsc", | ||
"test": "scripts/test.sh" | ||
@@ -39,3 +45,3 @@ }, | ||
}, | ||
"version": "0.1.1" | ||
"version": "2.0.0" | ||
} |
@@ -31,9 +31,8 @@ jasminewd [![Build Status](https://travis-ci.org/angular/jasminewd.svg?branch=master)](https://travis-ci.org/angular/jasminewd) | ||
Assumes selenium-webdriver as a peer dependency. | ||
In your setup: | ||
```js | ||
// In your setup. | ||
var JasmineRunner = require('jasmine'); | ||
var jrunner = new JasmineRunner(); | ||
require('jasminewd2'); | ||
var webdriver = require('selenium-webdriver'); | ||
@@ -45,7 +44,11 @@ global.driver = new webdriver.Builder(). | ||
require('jasminewd2').init(driver.controlFlow(), webdriver); | ||
jrunner.projectBaseDir = ''; | ||
jrunner.execute(['**/*_spec.js']); | ||
``` | ||
// In your tests | ||
In your tests: | ||
```js | ||
describe('tests with webdriver', function() { | ||
@@ -66,2 +69,20 @@ it('will wait until webdriver is done', 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, webdriver); | ||
``` | ||
`async` functions / `await` | ||
@@ -75,2 +96,3 @@ --------------------------- | ||
still use them, but if you do then you will have to use `await`/Promises for | ||
almost all your synchronization. See `spec/asyncAwaitSpec.ts` for details. | ||
almost all your synchronization. See `spec/asyncAwaitAdapterSpec.ts` and | ||
`spec/asyncAwaitErrorSpec.ts` for examples. |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
32106
0
7
382
0
95
10
- Removedjasmine@2.4.1
- Removedselenium-webdriver@3.0.1
- Removedadm-zip@0.4.16(transitive)
- Removedbalanced-match@1.0.2(transitive)
- Removedbrace-expansion@1.1.11(transitive)
- Removedconcat-map@0.0.1(transitive)
- Removedexit@0.1.2(transitive)
- Removedfs.realpath@1.0.0(transitive)
- Removedglob@3.2.117.2.3(transitive)
- Removedinflight@1.0.6(transitive)
- Removedinherits@2.0.4(transitive)
- Removedjasmine@2.4.1(transitive)
- Removedjasmine-core@2.4.1(transitive)
- Removedlru-cache@2.7.3(transitive)
- Removedminimatch@0.3.03.1.2(transitive)
- Removedonce@1.4.0(transitive)
- Removedos-tmpdir@1.0.2(transitive)
- Removedpath-is-absolute@1.0.1(transitive)
- Removedrimraf@2.7.1(transitive)
- Removedsax@1.4.1(transitive)
- Removedselenium-webdriver@3.0.1(transitive)
- Removedsigmund@1.0.1(transitive)
- Removedtmp@0.0.30(transitive)
- Removedwrappy@1.0.2(transitive)
- Removedxml2js@0.4.23(transitive)
- Removedxmlbuilder@11.0.1(transitive)