Skip to content

Commit

Permalink
[BUGFIX beta] Bring back Ember.Test.waiters.
Browse files Browse the repository at this point in the history
Brings back `Ember.Test.waiters` and adds a feature flag for exposing
`Ember.Test.checkWaiters`.
  • Loading branch information
rwjblue committed Jun 20, 2016
1 parent a75be5c commit c381acb
Show file tree
Hide file tree
Showing 6 changed files with 232 additions and 4 deletions.
5 changes: 5 additions & 0 deletions FEATURES.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,3 +58,8 @@ to stay in line with ES standards (see [RFC](https://github.com/emberjs/rfcs/blo
Ember.String.isHtmlSafe(plainString); // false
Ember.String.isHtmlSafe(safeString); // true
```

* `ember-testing-check-waiters`

Expose a simple mechanism for test tooling to determine if all foreign async has been
handled before continueing the test. Replaces the intimate API `Ember.Test.waiters` (with a deprecation).
3 changes: 2 additions & 1 deletion features.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
"ember-runtime-computed-uniq-by": true,
"ember-improved-instrumentation": null,
"ember-runtime-enumerable-includes": null,
"ember-string-ishtmlsafe": null
"ember-string-ishtmlsafe": null,
"ember-testing-check-waiters": null
}
}
13 changes: 12 additions & 1 deletion packages/ember-testing/lib/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,17 @@ import TestPromise, {
resolve
} from './test/promise';
import {
checkWaiters,
registerWaiter,
unregisterWaiter
unregisterWaiter,
generateDeprecatedWaitersArray
} from './test/waiters';

import {
getAdapter,
setAdapter
} from './test/adapter';
import isEnabled from 'ember-metal/features';

/**
This is a container for an assortment of testing related functionality:
Expand Down Expand Up @@ -56,6 +59,10 @@ const Test = {
unregisterWaiter
};

if (isEnabled('ember-testing-check-waiters')) {
Test.checkWaiters = checkWaiters;
}

/**
Used to allow ember-testing to communicate with a specific testing
framework.
Expand All @@ -81,4 +88,8 @@ Object.defineProperty(Test, 'adapter', {
set: setAdapter
});

Object.defineProperty(Test, 'waiters', {
get: generateDeprecatedWaitersArray
});

export default Test;
34 changes: 34 additions & 0 deletions packages/ember-testing/lib/test/waiters.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
import isEnabled from 'ember-metal/features';
import { deprecate } from 'ember-metal/debug';

const contexts = [];
const callbacks = [];

Expand Down Expand Up @@ -68,6 +71,19 @@ export function unregisterWaiter(context, callback) {
callbacks.splice(i, 1);
}

/**
Iterates through each registered test waiter, and invokes
its callback. If any waiter returns false, this method will return
true indicating that the waiters have not settled yet.
This is generally used internally from the acceptance/integration test
infrastructure.
@public
@for Ember.Test
@static
@method checkWaiters
*/
export function checkWaiters() {
if (!callbacks.length) {
return false;
Expand All @@ -90,3 +106,21 @@ function indexOf(context, callback) {
}
return -1;
}

export function generateDeprecatedWaitersArray() {
deprecate(
'Usage of `Ember.Test.waiters` is deprecated. Please refactor to `Ember.Test.checkWaiters`.',
!isEnabled('ember-testing-check-waiters'),
{ until: '2.8.0', id: 'ember-testing.test-waiters' }
);

let array = new Array(callbacks.length);
for (let i = 0; i < callbacks.length; i++) {
let context = contexts[i];
let callback = callbacks[i];

array[i] = [context, callback];
}

return array;
}
10 changes: 8 additions & 2 deletions packages/ember-testing/tests/helpers_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -302,7 +302,10 @@ QUnit.test('`wait` respects registerWaiters', function(assert) {
equal(counter, 0, 'unregistered waiter was not checked');
equal(otherWaiter(), true, 'other waiter is still registered');
})
.finally(done);
.finally(() => {
unregisterWaiter(otherWaiter);
done();
});
});

QUnit.test('`visit` advances readiness.', function() {
Expand Down Expand Up @@ -530,7 +533,10 @@ QUnit.test('`wait` respects registerWaiters with optional context', function() {
}).then(function() {
equal(obj.counter, 0, 'the unregistered waiter should still be at 0');
equal(otherWaiter(), true, 'other waiter should still be registered');
});
})
.finally(() => {
unregisterWaiter(otherWaiter);
});
});

QUnit.test('`wait` does not error if routing has not begun', function() {
Expand Down
171 changes: 171 additions & 0 deletions packages/ember-testing/tests/test/waiters-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
import isEnabled from 'ember-metal/features';
import {
registerWaiter,
unregisterWaiter,
checkWaiters,
generateDeprecatedWaitersArray
} from 'ember-testing/test/waiters';

class Waiters {
constructor() {
this._waiters = [];
}

add() {
this._waiters.push([...arguments]);
}

register() {
this.forEach((...args) => {
registerWaiter(...args);
});
}

unregister() {
this.forEach((...args) => {
unregisterWaiter(...args);
});
}

forEach(callback) {
for (let i = 0; i < this._waiters.length; i++) {
let args = this._waiters[i];

callback(...args);
}
}

check() {
this.register();
let result = checkWaiters();
this.unregister();

return result;
}
}

QUnit.module('ember-testing: waiters', {
setup() {
this.waiters = new Waiters();
},

teardown() {
this.waiters.unregister();
}
});

QUnit.test('registering a waiter', function(assert) {
assert.expect(2);

let obj = { foo: true };

this.waiters.add(obj, function() {
assert.ok(this.foo, 'has proper `this` context');
return true;
});

this.waiters.add(function() {
assert.ok(true, 'is called');
return true;
});

this.waiters.check();
});

QUnit.test('unregistering a waiter', function(assert) {
assert.expect(2);

let obj = { foo: true };

this.waiters.add(obj, function() {
assert.ok(true, 'precond - waiter with context is registered');
return true;
});

this.waiters.add(function() {
assert.ok(true, 'precond - waiter without context is registered');
return true;
});


this.waiters.check();
this.waiters.unregister();

checkWaiters();
});

QUnit.test('checkWaiters returns false if all waiters return true', function(assert) {
assert.expect(3);

this.waiters.add(function() {
assert.ok(true, 'precond - waiter is registered');

return true;
});

this.waiters.add(function() {
assert.ok(true, 'precond - waiter is registered');

return true;
});

assert.notOk(this.waiters.check(), 'checkWaiters returns true if all waiters return true');
});

QUnit.test('checkWaiters returns true if any waiters return false', function(assert) {
assert.expect(3);

this.waiters.add(function() {
assert.ok(true, 'precond - waiter is registered');

return true;
});

this.waiters.add(function() {
assert.ok(true, 'precond - waiter is registered');

return false;
});

assert.ok(this.waiters.check(), 'checkWaiters returns false if any waiters return false');
});

QUnit.test('checkWaiters short circuits after first falsey waiter', function(assert) {
assert.expect(2);

this.waiters.add(function() {
assert.ok(true, 'precond - waiter is registered');

return false;
});

this.waiters.add(function() {
assert.notOk(true, 'waiter should not be called');
});

assert.ok(this.waiters.check(), 'checkWaiters returns false if any waiters return false');
});

QUnit.test('generateDeprecatedWaitersArray provides deprecated access to waiters array', function(assert) {
let waiter1 = () => {};
let waiter2 = () => {};

this.waiters.add(waiter1);
this.waiters.add(waiter2);

this.waiters.register();

let waiters;
if (isEnabled('ember-testing-check-waiters')) {
expectDeprecation(function() {
waiters = generateDeprecatedWaitersArray();
}, /Usage of `Ember.Test.waiters` is deprecated/);
} else {
waiters = generateDeprecatedWaitersArray();
}

assert.deepEqual(waiters, [
[null, waiter1],
[null, waiter2]
]);
});

0 comments on commit c381acb

Please sign in to comment.