Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[BUGFIX beta] Bring back Ember.Test.waiters. #13716

Merged
merged 1 commit into from
Jun 21, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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]
]);
});