Sessions is temporarily moving to YouTube, check out all our new videos here.

The Future of Ember Testing: the Beheading of jQuery

Miguel Camba speaking at Ember London in April, 2017
Great talks, fired to your inbox 👌
No junk, no spam, just great talks. Unsubscribe any time.

About this talk

A glimpse at the future of what Ember testing would look like without jQuery and what makes it possible.


Miguel Camba: Whenever you want. Well, hi. I'm about to talk about some small hobby of mine that I've been playing with the last weeks. My hobby is to kill jQuery. And for doing that, it happens to be a pretty hard task if you look for the big picture. There is a lot of small movements [00:00:30] that led to the very moment we're living, which is the days of today. This week is the first week we can actually not use jQuery on an application easily. So far it was [special 00:00:43], you could use it, but you could not test your application, which makes you basically not use it. It's a no-go game. If you cannot use your application, if you cannot test your application, no matter how easy the rest is, you cannot go with [00:01:00] this path. So for doing that, I wrote this blog post. I wrote it two days ago and I'm basically going to give ... I'm going to read this thing, but I'm going to live code a small demo so you see how things are different, what are the addons that actually enable you to write without jQuery and so you can do the same thing with your own apps or addons. One of the main advantages [00:01:30] I mentioned in this blog post is that usually you need to consider whether or not you want to remove jQuery for the good reasons or just because it's fancy to not use jQuery. If your application is mostly for desktop, probably you shouldn't worry that much. It's going to load fast, it's going to run fast. So far, at the moment, removing jQuery is not so so easy. So [00:02:00] if you want to do it, at least be aware that there is going to be stones on the way. If you are going to develop on a mobile application, then it start to make sense, because we are talking about shaving between half a second and one second and a half on the initial render time with a regular Ember application. Which it may be a fairly important deal going, for example, from five seconds [00:02:30] to four seconds it's a 20% improvement. And the next thing is, you should probably do it if you're building addons, because the main or the most paying ... The biggest paying point doing this thing is that you're likely using at the very least like six, ten addons in your application, probably more, and if you ... Many of them use jQuery one way or another. Meaning that, even if you don't use jQuery on [00:03:00] your application your own, probably some of your addons do. And we need to try to fix this problem within the community. So, only addons that happen to have a good reason to use jQuery actually do. Let's start with this application. This is a very small application I wrote for this thing. It's very ugly, but it has only two routes. It has the application route with nothing inside. There is the signup, which is not a real [00:03:30] signup, but it's going to make a adjunct request to the [inaudible 00:03:35]. Register, I mean create a user with your user name with [with a new password 00:03:39] and password confirmation you put. And it's going to transition you to your password. So, if I type here something like ... Actually doesn't even has to be an email, so ... Now if I sign up, I have a artificial delay, so error request takes like one second and a half. You see that the loading ... You have a loading [00:04:00] substate and then you enter. I'm going to repeat this thing because it's ... You type something and when you register this [inaudible 00:04:07] is going to show a [loading 00:04:08] state, then you're going to transition to the dashboard. The dashboard has a loading substate, where you actually log more data, and then you show it. Loading ... I actually ... Because this site already exist, so, again ... Loading, what's going on? Audience: [inaudible 00:04:30] Miguel Camba: [00:04:30] Oh actually, no, what is this thing? Audience: [inaudible 00:04:35] Miguel Camba: No I don't think I'm validating that. So ... It works one time, I don't know it isn't going to work this other one. Audience: [inaudible 00:04:45] Miguel Camba: I know the email is wrong but it's not validating anything. So, I'm going to try this. Audience: [inaudible 00:04:53] Miguel Camba: Ah, maybe I am validating [00:05:00] things. Or maybe it's because the browser is smart enough to actually check this thing for you, I dunno. So you saw that the button shows a log in thing, and then you see another big loading message and you go to the dashboard. So, let's see how do we remove jQuery. The main thing is Ember doesn't use jQuery as much as you may think. Internally, Ember only uses jQuery for three things. [inaudible 00:05:26] [inaudible 00:05:27] components, that's something [00:05:30] we have seen many times. The second one it's the event dispatcher, the event dispatcher is what makes you your event name after, sorry, your method's name after events in components to be called. So, if you define a click method on a component, this method is going to be invoked whenever you click on the component, and it's magic, some Ember magic. Another one it's you ... If you have an action, [00:06:00] and you type [inaudible 00:06:02] action [foo 00:06:03] or something, whenever you click on this thing, this action is going to invoke and you going to receive the event as the first [argument 00:06:09]. Unless you pass other arguments, in which case, it's going to be the second, or the third, depending on the number of arguments you pass. And the third one is testing, click, filln, all these kind of helpers use jQuery internally. So in order to remove an application from jQuery, we need to stop using the things that use jQuery. [00:06:30] So, the first one is event dispatcher, and it's something that is [very easy to remember 00:06:32]. So how do we replace it? Lucky enough, Robert Jackson has created this nice add-on, named ember-native-dom-event-dispatcher, and in study this add-on basically replaces the event dispatcher that relies on jQuery with another event dispatcher that doesn't use jQuery at all. This is mostly painless for you, the only difference that all your actions and methods are going to [00:07:00] receive dom events instead of jQuery events. For the most of it you won't notice a difference, because they are pretty interpretable. There is small nuances the main one being if you want to check if an event has been default prevented in native event you do ... You use the default preventive property, which is [inaudible 00:07:21] and in jQuery you need to do ... You need to call the [is 00:07:26] default prevented with parenths because its a method. But it's, [00:07:30] for the most of it you won't see a difference. So let's go to this application. This application called jQuery App, no wonder, it's very similar, sorry, very small. I'm going to code and open this. Here it is. It's very simple, the templates, application, by example, [design for example 00:07:52] that's it. Similar ... simple application. I'm going to instal this add-on, so Ember instal this. [00:08:00] And I want to make you aware that I'm using the latest version of Ember, sorry ... Yes the latest version of Ember source because, oh I'm not. This is the latest one, so darn, going to update one beta. You need to be aware that you need be at least in this version. You cannot be in a version below this one. And I just installed the [00:08:30] ember-native-dom-event-dispatcher so we're almost good to go. Another thing I want to notice is ... Make you aware is I also using the latest [verta 00:08:42] of Ember CLI, 2.13. And this a nice feature in this version of CLI, it brings Babel 6. Babel 6 is the finally, the ... We are on the latest version of Babel. One [00:09:00] of the cool features that Ember CLI Babel bundles Ember preset [M 00:09:06] my default, so we can tell our application is going to run in this browser, this browser, and this browser. And Babel is going to not [transpile 00:09:15] the things that need to be [transpiled 00:09:17] for this browser. By example, you say your application runs only in Chrome, pretty much nothing is going to be [transpiled 00:09:25], only modules. The rest is going to remain the same. So this is [00:09:30] my small trick, in development I use Chrome, pretty much all the time. So I am going to open this new configuration file called 'targets'. And I going to say that, by example, I'm going to do browsers, this is the versions that Ember supports by default, so your application by default supports the same targets Ember supports. But in my case, my application is going ... Not going to run in [nine nine 00:09:58], so I going to remove [00:10:00] it. And it's going to run on the lightest version of evergreen browsers. So I'm going to do something, because in development I don't want to [transpile 00:10:07] as much things, so ... Browsers equals this. And I'm going to do this thing, and given that we can rely this is [everscreen 00:10:28] file, we can rely on environment variables, [00:10:30] if in this case, it's process.env.EMBER if this is development. I'm going to say that browsers is only Chrome. [00:11:00] And that means that in development I'm not going to [transpile 00:11:04] pretty much anything, I'm going to have native [sync 00:11:06] awake, I'm not going to [transpile 00:11:09] arrow functions, everything is going to be so much nicer to be back. So, once we have this thing, I think we need to restart. And we need to go to the Ember CLI build now, and given that, sorry, yes this one. And given that now we [00:11:30] don't have ... we have the new event dispatcher that doesn't rely on jQuery, we can remove jQuery. And for removing jQuery, this is the actual line that does the thing ... I don't remember the exact syntax so I'm going to check for another project. Oh I actually have the [block 00:11:47] open so I going to rely on my own wisdom. There it is, it's ... Bender files jQuery, no that's never remember this thing. [00:12:00] That's it. You pass this option and you are without jQuery. But things are going to break. So, you see, it's complaining because jQuery is not there. So what is using jQuery on our application, and chances are there is a lot of things using jQuery in our applications, even if we are not very aware. So let makes this thing bigger. In this case [00:12:30] it's here. We are using a version of [H ref two 00:12:34] Who knows the [oven 00:12:35]? Okay. So, we are working on removing jQuery from the most programme addons, and we ... there is a new version of this add-on that doesn't use jQuery internally, so let's update. [H ref two 00:12:47] H ref here, it's 2.12.0 so stop, [turn 00:12:58] [00:13:00] yes ... Installing ... And now this should be good to go. Loading. Okay, it's complaining because the ajex is, cannot read properly ajex is undefined. Why? Because our application has Ember ajex. If we remove jQuery, we need to remove Ember ajex because, obviously, it's relying on jQuery ajex inside. So if we remove jQuery ajex, [00:13:30] that also means we need to replace it with something else because the X-H-R interface for making requests is [ninemar 00:13:38]. So we're going to use the new [fetz 00:13:40] and for that we're going to remove Ember ajex and we're going to instal Ember ... No this one, Ember instal, Ember fetch. This is very nice add-on that basically gives you a [poly feel 00:14:00] [00:14:00] from the fetch, so if you ... Even if you are on a browser that supports the [poly feel 00:14:09] you, for now, have to use ... sorry, if you are in a browser that has native [fetz 00:14:15] for now you need to use the [poly feel 00:14:17] if you use [MRAS 00:14:18], because [MRAS 00:14:19] cannot mock a fetch request yet, although it's not so far in the future. We are going to do it. And given that we also need to instrument, [00:14:30] for now you need to use my own fork of this thing, so ... Fetch, as again this is pretty cutting edge, so [my own 00:14:37] [get hab 00:14:38] Ember fetch and I need to get my brand [his make 00:14:43] it wait. I need to [jarn 00:14:52] again, E-S. [00:15:00] Reloading, reloading. Come on. Okay, now it's complaining that we are using the ajex service, and we remove it so we need to use fetch. Let's find where we're using ajex [hold on 00:15:18] ... here. We are going to remove this, we're going to import fetch from fetch. [00:15:30] And instead of these [to get 00:15:32] ajex we are going to do fetch, we are going to keep this thing, going to do fetch method post data is now body, this small difference, and you want to do then ... When you use fetch you don't get the [jacent 00:15:57] right away, you get a response object and you need to call [Jason 00:16:01] [00:16:00] on it. So, response and with this you do 'response.jason' there's a type, there. And we did it, we're using fetch now. I think there is another place, so we're going to remove this and ... [00:16:30] Fetch this, and then same thing R, 'R.Jason' and we are okay. Seems to be marvellous working. This a fail network, where was it? Oh because we are on a user that doesn't exist anymore. So ... There it is, sign [00:17:00] up. So now is, [inaudible 00:17:02] applications that are not ready with a topic, so now we are ... The application is supposed to work. Let's type my email again. It does work, okay, it works. But now we have to test it, and this is where things become interesting. Let's go to test, so test. And ... I need to make this thing smaller ... Okay, everything breaks because of [00:17:30] the same thing. [App to dollar 00:17:31] everything in Ember uses jQuery for testing, so every helper remains, so if you use find, you're using jQuery, if you use click, you're using jQuery, if you use filln it's just jQuery. We need a set of helpers that allows you to use jQuery ... To do the same thing we are used to, but they don't rely on jQuery internally. And that's why I created this project called 'Ember native dom helpers'. [00:18:00] This is a set of helpers, and I'm going to assume a little bit ... That it gives you this set of helpers, again, there. It gives you a helper click, tap, fill-in, find, find all, that I have split find and find all in two different things for one reason. Find always has key events and a few nice things, a few new things like tap, tap is new, it's for touch events. And, [00:18:30] I think it's not ... Yes we can visit, we can do this also, you can also import from here things like query URL, and all the same thing you have in Ember plus a few more. And neither of these relies on jQuery. So the thing is, we can go to our test, and our test is pretty simple, test, use the app test. And we see that we are using find, filling, filling, filling, click, and [we also 00:19:00] something. [00:19:00] So we want to use our helpers, same names, from a different source. So let's go into import, and we are going to import click, fill in, visit, and I think that's all we use, so from Ember native, sorry? Audience: [inaudible 00:19:28] Miguel Camba: [00:19:30] A yes, find, thank you. Find. Ember native dom, woops, dom helpers. And I need to instal it, so ... Ember instal this. Clean the server, instal the server, should be fast. Starting. And now the applications, [00:20:00] the tests, should be green. Sorry, test. [Expect to talking in jason 00:20:22] I think we are passing something, I need to [00:20:30] check ... [I.t. 00:20:31] log in so server.login equals through somethings but in my jason, I guess. So we get data, data attributes, okay. From where do we get this message? [not recatch 00:20:55]. Audience: [inaudible 00:21:10] Miguel Camba: [00:21:00] Yes. Audience: [inaudible 00:21:12] Miguel Camba: Yes just one. Audience: [inaudible 00:21:16] Miguel Camba: Okay I dunno why it's doing this thing. Oh, I do know ... I think I know why. Maybe this one? Let me see. [00:21:30] No, not this one. Either way, let's assume it's passing, I dunno why it's not passing, we are going ... There is something else that we can do thanks to these helpers. Are you familiar with the sync await syntax? The idea is, each one of these helpers, unlike the Ember ones ... Actually the Ember ones, some of them do, returns a promise that is the result [00:22:00] of calling the wait helper. The wait helper is a helper that renders a promise that fulfils when everything settles. What is everything settles? It means there is no pending request, there is nothing else to run in the run loop, no timers, and no waiters. That means there is nothing yet to be done so you cannot start things. So, with this you can do, instead of a sync await, sorry, instead of using the classic [alan ten 00:22:29], [00:22:30] you can do, mark the function as a sync. And then do await the visit. You can stop doing this thing, you can remove this, you can make your test [marks 00:22:47] more readable. Wait, actually, await all of them. And this is a new way of doing testing in Ember [00:23:00] that is much more nicer. The thing is, this is [ES link 00:23:06] complaining, E-S is complaining because it's saying "what is this a- what is this [async 00:23:11] thing?" So you need to go to the [ES lint 00:23:14] and say that you are using 2017. So, all of this is fine, I promise and ... Why is the jason breaking? Killing me, probably some kind- Audience: [inaudible 00:23:30] Miguel Camba: [00:23:30] Sorry? Yes I'm doing something wrong there. Fetch. Audience: [inaudible 00:23:38] Miguel Camba: This one. Audience: [inaudible 00:23:42] Miguel Camba: Oh. From ... That must be it. [inaudible 00:23:56] of what? Audience: [inaudible 00:23:59] Miguel Camba: Oh [CS lint 00:24:00] [00:24:00] fantastic, I need to restart. Server is not defined. Audience: [Test 00:24:13] server is open. Miguel Camba: Okay, that's ... Yes. [ES lin 00:24:19], no. [ES lint 00:24:22] test globals, I think it's globals ... Globals [00:24:30] ... Server ... True. I dunno if exist changes or not. Audience: [inaudible 00:24:45] Miguel Camba: Yes, it's [inaudible 00:24:48] server. Okay it's passing. So now let me undo what I did on the test just make sure this thing work before. So this is with the old [00:25:00] style. And let's do everything again, you put [a wait on 00:25:05] things. And it is still pass. So that's pretty much everything you have to know about it. This new way of testing, that I think [deals 00:25:14] much more readable results because this syntax is nice. And it's very explicit that you are [wait for 00:25:21] things because pretty much anything, or any action, you perform may or may not trigger something [asyncronous 00:25:28]. Maybe you have filln, [00:25:30] usually filln doesn't do anything, but sometimes you have a filln that has an auto-complete, so it does trigger something to the server, so you need to wait for it. But something that is nice is this actually enables somethings that were impossible to test before. And one of them is this one. Let's go to the page again. Imagine you what to [assert 00:25:51] that the text you see during the [loads up 00:25:55] state while you are transitioning to the dashboard is the thing you expect it to be. [00:26:00] So I click on dashboard user one. Loading data please be patient. You want to [serve 00:26:05] that. Before if we tried to do this, and I'm going to. Let's duplicate this test. Actually, yes, we're going to duplicate this test; I'm going to say 'check loading substate' and I'm actually going to do something different, I'm going to copy the old style, [00:26:30] this, redo. I'm going to ... Enter. And now I want to assert here [below 00:26:49] the substate. So assert [below the 00:26:51] substate is something like this: you need to assert that the dashboard load substate [00:27:00] header because it's the name of the thing you want to target. The text content, remember, this is not longer using jQuery so you not use text, you use test content. Test content [and there is a few 00:27:09] are the difference between dom APIs and jQuery APIs but usually it is very simple. If you try to run this thing, and I going to run this again at test filter this, just run only this, it's going to break. It's going to break, why? Because [00:27:30] it cannot find this substate. Why it cannot find the substate? Because Ember, when you do and then, is going to wait for everything to finish. Everything. And that includes a lot of substates, so by the time you actually go to the [and then 00:27:45], there is nothing for you to test; this thing has gone. So the good thing of the sync await syntax is you are explicit about waiting, what do you wait or what you do not wait for. So if we transform this one as well to [00:28:00] this syntax, let's do it here ... So far the same thing. This is going to fail just as the previous one. Loading ... And it fails. What happens if we don't await for click? Let's say we let promise, or let's say like pending [00:28:30] click, is this thing. There is a new helper in [nem atest 00:28:35] helpers, not this one, this one, called wait until. This is brand new. And to this helper you pass an option, sorry not an option, a function that is going to pull the dom until this thing is true. But it's not tied to run loop, to the pending promises, or to the pending request. So you can await for arbitrary things to happen even if there is still more things going on. [00:29:00] So we're going to [repay 00:29:01] the same thing, we're going to await for something I know that is in the [lowest 00:29:09] substate to appear. I'm going to import the 'wait until'... And we're going wait until you pass a function and it's going to wait until this find actually finds something. And, after we have found [00:29:30] and we are asserting that this thing in the [loading 00:29:33] substate is there, then we can decide "now I want to await for everything else to finish". So now, this is going to pass because we are pulling the dom every ten milliseconds in this 'wait until' until this element appears. Now, we are in the [loading 00:29:55] substate, I prefer assertions from the [load 00:29:57] substate, and then I wait for the click which [00:30:00] is the thing I didn't wait before. And by the time that this wait finished, I am really inside the dashboard. So this is not only give you a nicer syntax, it allows you to test things that you couldn't test before, and on the way you can actually get rid of jQuery for free because it's not really needed anymore, if you don't use it. Just be aware that the result of addons that still require jQuery internally, more often than not, not for good reasons, just because it was there, but it ... Baby [00:30:30] steps we're going to try to get remove jQuery from the ... From being a default. And instead of being a opt-out, like right now, eventually it will be a opt-in. And you also get this nice testing thing, which is, I mean I'm pretty excited about this thing. And I'm done.