-
Notifications
You must be signed in to change notification settings - Fork 27.4k
feat(*): implement more granular pending task tracking #16603
feat(*): implement more granular pending task tracking #16603
Conversation
Some work is still needed, but I would like confirm we are itnerested in this change, before investing more time. TODO:
In its current state, the PR does not introduce any change in external behavior, but adds internal features that could be built on top of. Here are my suggested public changes (in an attempts to address #14336 in a backwards compatible way):
|
This sound like a good idea. |
BTW, there remains one more unsolved problem with those tasks, namely timeouts and intervals have separate queues that have to be flushed separately. That's why it's difficult to unit-test logic that mixes timeouts and intervals. Another mention of this issue: #10525 (comment) |
Tackling that would be a much bigger change (and one we probably won't get to 😁). |
src/ng/browser.js
Outdated
function decOutstandingRequestCount(taskType) { | ||
taskType = taskType || DEFAULT_TASK_TYPE; | ||
if (outstandingRequestCounts[taskType]) outstandingRequestCounts[taskType]--; | ||
if (outstandingRequestCounts[ALL_TASKS_TYPE]) outstandingRequestCounts[ALL_TASKS_TYPE]--; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I feel like this should go into the if (outstandingRequestCounts[taskType])
and not be it's own if
. That way if taskType
is invalid or not set it won't decrement outstandingRequestCounts[ALL_TASKS_TYPE]
...?
637f13b
to
672e65b
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's a lot of code change ...
I can't say I reviewed everything - mostly focussed on the docs.
So the second parameter from whenStable was removed intentionally because the API would have been too complex, right? I guess Protractor could still use the 2nd argument to browser.notifyWhenNoOutstandingRequests()
to ignore some task types ($timeout, cough) on startup. Both browser and whenStable are private APIs anyway.
I guess at this point it doesn't make much sense to make them public.
src/ngMock/angular-mocks.js
Outdated
* These include tasks scheduled via `$evalAsync()` indirectly (such as {@link $q} promises). | ||
* | ||
* <div class="alert alert-info"> | ||
* Periodic tasks scheduled via {@link $interval} use a different queue and are flushed by |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
are flushed -> are not flushed
src/ngMock/angular-mocks.js
Outdated
* `$flushPendingTasks()`. Use {@link ngMock.$interval#flush $interval.flush([millis])} instead. | ||
* </div> | ||
* | ||
* @param {number=} delay - The number of milliseconds to flush. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should it be noted that this only applies to the $timeout?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What do you mean? It doesn't apply to $timeout
only (I have listed the types of tasks flushed above).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I mean "the number milliseconds to flush" only applies to the $timeout queue. This might have been obvious when flush() was on $timeout, but now the flush() also flushes applyAsync and evalAsync and they don't have a "timeline" like timeout does. At least I would expect if I put in a delay of 0 or no delay that applyAsync and evalAsync would still be flushed, but timeouts wouldn't
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point. I'll mention that. I will ensure there are tests that verify the expected behavior (no delay flushes everything, 0 delay flushes stuff with 0 delay only, etc.).
src/ngMock/angular-mocks.js
Outdated
* account by `$verifyNoPendingTasks()`. | ||
* </div> | ||
* | ||
* @param {string=} taskType - The type of tasks to check for. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@returns
is missing. Should also include that it throws when tasks are pending.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It doesn't really return anything atm 😁
+1 for documenting the thrown error.
src/ngMock/angular-mocks.js
Outdated
* These include tasks scheduled via `$evalAsync()` indirectly (such as {@link $q} promises). | ||
* | ||
* <div class="alert alert-info"> | ||
* Periodic tasks scheduled via {@link $interval} use a different queue and are not taken into |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we mention that it's impossible to verify that no intervals are running? "uses a different queue" makes it sound a bit like the possibility exists.
src/ngMock/angular-mocks.js
Outdated
* The types of tasks that are flushed include: | ||
* | ||
* - Pending timeouts (via {@link $timeout}). | ||
* - Pending tasks scheduled via {@link ng.$rootScope.Scope#$applyAsync}. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This must be {@link ng.$rootScope.Scope#$applyAsync $applyAsync}
or similar, otherwise it'll show $rootScope.Scope
as the name.
Same in the line below.
@@ -4,10 +4,18 @@ var $intervalMinErr = minErr('$interval'); | |||
|
|||
/** @this */ | |||
function $IntervalProvider() { | |||
this.$get = ['$rootScope', '$window', '$q', '$$q', '$browser', | |||
function($rootScope, $window, $q, $$q, $browser) { | |||
this.$get = ['$$intervalFactory', '$window', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I assume we don't care that this is "breaking" the decoration of $interval by including a private service?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why does it break it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Now that you ask ... doesn't look like it breaks anything (and it relied on a private service before)
'If you only want to verify pending timeouts, use ' + | ||
'`$verifyNoPendingTasks(\'$timeout\')` instead.'; | ||
|
||
throw new Error('Deferred tasks to flush (' + pendingTasks.length + '):\n ' + |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we should document that this fn will throw this error. I know that it's not new, but since we're at it ...
src/ngMock/angular-mocks.js
Outdated
* Verifies that there are no pending tasks that need to be flushed. | ||
* You can check for a specific type of tasks only, by specifying a `taskType`. | ||
* | ||
* See {@link $verifyNoPendingsTasks} for more info. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
verifyNoPendingsTasks -> verifyNoPendingTasks
why does this not fail in the docs processor? because it's not public
The reason we dropped the |
@Narretz, addressed feedback. Thx for taking a look ❤️ |
Ok, but if you only think about AngularJS then e.g. Protractor could provide a different API for AngularJS apps. In fact, in the docs I can't really see anything related to timeouts passed into https://www.protractortest.org/#/api?view=ProtractorBrowser.prototype.waitForAngularEnabled However, they can still do it without the API being public. |
Yeah, protractor is still not using Angular's new |
Protractor uses testability or browser via the injector: https://github.com/angular/protractor/blob/ed955e56a839d7f69da43acb6755763220d3681d/lib/clientsidescripts.js#L78-L87 Now if they wanted to use granular tracking they'd have to add some more conditions because granular tracking is only available via browser. I think we should simply give whenStable the second argument back to make this easier. |
3c7e596
to
f14938b
Compare
We discussed this "offline" and decided that changing the testability API does not have to be part of this PR. Let's get this merged and discuss this with the Protractor team. |
outstandingRequestCallbacks.push(callback); | ||
} | ||
}; | ||
// TODO(vojta): prefix this method with $$ ? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's probably a bit late for this TODO now :-)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hope never dies 😃
test/ngMock/angular-mocksSpec.js
Outdated
callback = jasmine.createSpy('callback'); | ||
}); | ||
|
||
it('should immediatelly run the callback if no pending tasks', function() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
typo => immediately
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
and below...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
😱
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Generally LGTM - a few typos to fix and a couple of questions to answer.
Previously, all pending async tasks (tracked via `$browser`) are treated the same. I.e. things like `$$testability.whenStable()` and `ngMock#$timeout.verifyNoPendingTasks()` take all tasks into account. Yet, in some cases we might be interested in specific tasks only. For example, if one wants to verify there are no pending `$timeout`s, they don't care if there are other pending tasks, such as `$http` requests. Similarly, one might want to get notified when all `$http` requests have completed and does not care about pending promises. This commit adds support for more granular task tracking, by enabling callers to specify the type of task that is being added/removed from the queue and enabling listeners to be triggered when specific types of tasks are completed (even if there are more pending tasks of different types). The change is backwards compatible. I.e. calling the affected methods with no explicit task-type, behaves the same as before. Related to angular#14336.
…internal implementation
…rval` This avoids code/logic duplication and helps the implementations stay in-sync.
…ngMock/$browser` This avoids code/logic duplication and helps the implementations stay in-sync.
`$flushPendingTasks([delay])` allows flushing all pending tasks (or up to a specific delay). This includes `$timeout`s, `$q` promises and tasks scheduled via `$rootScope.$applyAsync()` and `$rootScope.$evalAsync()`. (ATM, it only flushes tasks scheduled via `$browser.defer()`, which does not include `$http` requests and `$route` transitions.) `$verifyNoPendingTasks([taskType])` allows verifying that there are no pending tasks (in general or of a specific type). This includes tasks flushed by `$flushPendingTasks()` as well as pending `$http` requests and in-progress `$route` transitions. Background: `ngMock/$timeout` has `flush()` and `verifyNoPendingTasks()` methods, but they take all kinds of tasks into account which is confusing. For example, `$timeout.verifyNoPendingTasks()` can fail (even if there are no pending timeouts) because of an unrelated pending `$http` request. This behavior is retained for backwards compatibility, but the new methods are more generic (and thus less confusing) and also allow more fine-grained control (when appropriate). Closes angular#14336
…priate For historical reasons, `$timeout.verifyNoPendingTasks()` throws if there is any type of task pending (even if it is not a timeout). When calling `$timeout.verifyNoPendingTasks()`, the user is most likely interested in verifying pending timeouts only, which is now possible with `$verifyNoPendingTasks('$timeout')`. To raise awareness of `$verifyNoPendingTasks()`, it is mentioned in the error message thrown by `$timeoutverifyNoPendingTasks()` if none of the pending tasks is a timeout.
…` and `ngMock/$browser`
Thx for the review, @petebacondarwin. I think I've addressed all feedback (either by making the changes or trying to be entertaining 😛). |
f14938b
to
86b0c03
Compare
You were just about entertaining enough to distract me from requesting you make any more changes. Which I think means I am happy to let you merge this. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🚀
* angular-route: Update types and docs for `IRoute` * angular-mocks: Update types and docs for 1.7 Related to angular/angular.js#16603 and angular/angular.js#16640.
…telyTyped#27473) * angular-route: Update types and docs for `IRoute` * angular-mocks: Update types and docs for 1.7 Related to angular/angular.js#16603 and angular/angular.js#16640.
What kind of change does this PR introduce? (Bug fix, feature, docs update, ...)
Refactoring/Internal feature.
What is the current behavior? (You can also link to an open issue here)
Previously, all pending async tasks (tracked via
$browser
) are treated the same. I.e. things like$$testability.whenStable()
andngMock#$timeout.verifyNoPendingTasks()
take all tasks into account.Yet, in some cases we might be interested in specific tasks only. For example, if one wants to verify there are no pending
$timeout
s, they don't care if there are other pending tasks, such as$http
requests. Similarly, one might want to get notified when all$http
requests have completed and does not care about pending promises.What is the new behavior (if this is a feature change)?
This PR adds support for more granular task tracking, by enabling callers to specify the type of task that is being added/removed from the queue and enabling listeners to be triggered when specific types of tasks are completed (even if there are more pending tasks of different types).
The change is backwards compatible. I.e. calling the affected methods with no explicit task-type, behaves the same as before.
UPDATE: This PR now implements the API suggested in #16603 (comment).
Does this PR introduce a breaking change?
No.
Please check if the PR fulfills these requirements
Other information:
Closes #14336.