 So this session is on test driving a jQuery plug-in. Just a disclaimer, while this is about test driving a jQuery plug-in, this is also about test driving any JavaScript. There wouldn't be much of a difference. And there's also going to be a bunch of functional take on how I'm going to try and do things. So I'm kind of trying to push in a bunch of different things into this talk. It's a demo, so I'm going to be live coding pretty much throughout. So if you get scared seeing code on the screen, probably this session is not for you. So with that disclaimer, I'm going to get started. I'm going to talk about for the next 90 minutes, this is basically what we want to cover. We're going to quickly spend the first five minutes just doing the basic setup. So we have all the prerequisites that we need. In terms of WebStorm, NPM, Jasmine, Karma, these are a bunch of tools that I'm going to be using. So we'll spend about five minutes, show you how trivial it is to set this up and get up and running. Then onwards, I'm going to try and basically... So I built a jQuery plug-in for Commence. And that's what basically we want to try and do is how to test drive a Commence plug-in. So if you have any website and you want to have Commence in there and you want to use jQuery to do displaying your Commence and other kinds of things, that's basically what we're going to try and do. And then we're going to try and show you how to move towards an MBVM kind of a design pattern for a jQuery plug-in, which I think not many people talk about that, but that's something that I want to try and attempt in this particular live demo. And I'm going to also show you how to stub out Ajax calls so that you can pretty much run your tests without, you know, depending on the server lying around for doing basic stuff, basic testing. All right, is that cool with everyone? So that's a quick snapshot of the actual plug-in that I was talking about. So this is if you use Confengine, which is our submission system, basically the plug-in that is there is built using this. So I'm going to try and obviously we're not going to get all the way through doing every single functionality in this. There's a lot of functionality going on, but you will probably see how we can approach this particular problem if you were to build nested comments, if you were to build private comments, other kinds of features, a whole bunch of different things. So that's basically what I'm trying to tackle. How many people are familiar with test-driven development? Just a quick show of hands. Okay, awesome. And how many people are, you know, doing test-driven development on your JavaScript? Just show up your hands. These are the guys, you know, if you ask any questions, these are the guys who will answer it, because I don't know much. Let's, for the benefit of everyone, let's quickly touch upon what is test-driven development. What's the idea behind test-driven development? So we start with an automated test. We start with a automated test, not a suite of automated tests. We start with a automated test. We run the test, and what do we expect to see? We expect the test to fail, right, because there's nothing. All you started with is just a thing. And sometimes, however, you will find that your test passes. So what do you do then? Be suspicious. Something's wrong. Most probably, your test is wrong, right, because it can't just pass without actually having it. So ideally, you would expect it to fail, and then you want to make a little change to the code. What do we mean by little? Few seconds, few minutes at the max worth of work, not few days or weeks worth of work, right? That's one of the big mince conceptions. People write a test, and then next three months, they're writing code. That's not a test-driven development cycle. The whole point is quick feedback, very short feedback cycles. So we write a test, we make a little change, we run the test. If it fails, we continue making changes. If it passes, what do we do? Go back and write another test, right? And then three months later, you come back and say, you know, this TDD thing is a total crap. It doesn't work. Why? Yeah, you pretty much kind of go and become DHH at that point. And you don't quite understand what TDD is. Sorry, DHH, but that's true. So when you have a passing test, the important step that people often forget is that you need to refactor. And in fact, a large portion of what I'm going to be doing is actually showing you how cool refactoring is and how cool refactoring can make your design and simplify things. So refactoring is essentially an important step of what we call as a reflexive design. So it essentially helps you, you have some little code, and you look at it and you say, you know what, geez, there is duplication in it, or this is not communicative. This is not simplistic. Can I improve the design to make it better? Right? So it's very easy for anyone to hack a jQuery plugin, you know, 15 minutes, max is what it takes. But actually to write a good one that anyone can understand and anyone can contribute and modify actually takes a lot more effort. And that's where I think refactoring becomes an extremely important step. And I'm going to spend a whole bunch of time just refactoring code. So we always refactor code when we have passing tests, because we want to make sure that when you refactor code, you don't break any functionality. And so we refactor, if you break, you go make a little change. But the idea is that you refactor, your test should always be green at that point. And then you repeat the cycle. That completes one TDD microcycle, right? And this is essentially what we're going to try and do for building a jQuery plugin. You can do this for all kinds of things, but that's basically the nutshell idea behind test-driven development. Right? Everyone on the same page? So I meant this to be more like a, you know, program along kind of a session. So I have set up a GitHub repo and you can download the project and get started. It has just basic structure in it. So we're going to try and do that. So the first step is whatever, create a folder where you want this thing to be. Git clone this repo. And then once you've got, you've done the Git clone, you're going to get the JQ comment demo. And post that, we're going to do an NPM update. So I'm going to go back for the benefit of people who couldn't see the Git repo. It's actually a bad idea to fade these things out. So let me not do that real quick. That should fix it. So Git clone that repo. I'm going to go into that NPM update. I have done some of these basic steps on my machine. So I'm not actually doing it right now. I was not sure if the Internet connectivity will hold up. So if you do an NPM update, it's going to bring in all the dependencies that you need. So there's already a JSON file that the package that JSON file is there. So it's going to bring in all the dependencies. There's one thing that we will be using Karma. And if you want to kind of run things from the command line, it's a good idea to actually install the Karma CLI. And generally we install it at a global scope so that it's accessible anywhere. And this is especially important if you want to run things through your CI server, any kind of Jenkins or whatever. So it would need access to Karma to run this. And then we're going to do Karma in it. So I'm going to start showing from this point onwards. Let me mirror my screen quick. Get rid of that. That chops off my screen which is not very good. This one's luckily that should not be too much of a problem. So we want to do a Karma in it step at this stage. So I'm going to go through the project. I have already checked out. I have these files in there. So if you're following along, this is essentially what you will see on your machine at this point. And then I'm going to do Karma in it. And it's going to ask me what framework you want to use, your testing framework. So in this case I'm going to use Jasmine. It says do you want to basically use required JS to manage your dependencies now. Capture what all browsers. This is again a cool feature where you can run your tests against a whole bunch of browsers and make sure that it works in all as you're building stuff. So I'm going to say Chrome. You can tab out. I'm going to say Firefox, Safari, and Phantom JS, or Phantom JS, which is more for a headless browser. It's a headless browser. So I'm going to run all my tests against those four browsers right now. It says specify any location where you're going to include your source or test file. In this case, I'm going to say lib slash star star slash star dot JS. So I might have some libs that I want to include. Then I might have JS slash star star slash star dot JS. And then, yeah, it's saying that no file exists. That's okay. And then the last is I generally put all my specs or my, I don't like to call them tests, but if you will under this directory. So those are basically the three. It's going to one again. The spec does not exist. We're going to create all of these things. So those are three things I want to include. I don't want to exclude anything at this stage. I don't want karma to auto run the test. We're going to run it ourselves. And then that's pretty much it. It's produced a basic configuration file for you. So let's go in to PHP storm, which is what I'm using, but you could be using web storm or any other ID of your choice. You will see that it's added karma config.js file. So whatever we put in libs here, you can come back later and change those things. It's added jasmin, which is the framework we're going to use, a bunch of other configuration and the browsers that you asked to use. Everyone's cool with that? So what this gives us is this gives us the ability to basically now run the tests. We obviously don't have any at this stage. So we're going to go ahead and create one very basic one. So I'm going to say create, let's say, I'm going to call this as a command spec.js. Let's call it commence spec.js. Yes, go ahead and add it to the repo. And what do I need? I need to describe what my spec is going to look. So I just have a bunch of little live templates put in, so it speeds up the development. So this is basically jQuery comment login. Don't have any bars at this point, no setup required at this point. And usually the very first test we want to make sure is everything's hooked up and ready to go, so it should find jQuery. So I'm going to say expect $0 to be null. This is, again, what we're using is jasmin syntax here, just to kind of do the verification for us. So this very basic test, what it's trying to see is, okay, I'm set up, do I have jQuery available so that I can proceed from here? So when we try to run this, the simple way to run this is basically click on this and I mean you can right click on this and say run as spec. And notice there it started all the browsers that we wanted. So you'll see it started Chrome, it started Safari, it started Firefox, and Fandom.js, which is a headless browser you wouldn't see, but it ran against all of those. And there it is, it says all our tests are green at this point. So in all these browsers, we are able to find the handle to the jQuery. This is pretty much, we're going to delete this at a later point, but this is a good starting point, this tells us that we're all set up. Is everyone with me so far? Very simple, just getting started. Now next thing what we want to do is we want to basically write a spec which says that, okay, so actually let's switch back real quick. Let's talk about some of the actual features of what we're going to do. So create a symbol spec, we just did that, and we ran Karma test. Another way to run Karma test, I'm going to show you that a little later. But before we get into anything important, let's have a quick commercial break. So my name is Naresh, I live in Mumbai. I was a partner at a company called Industrial Logic where we built eLearning for people. One of the things we take pride in is every Google employee used to go through this eLearning. I left the company, I started another company called Adventure Labs where we built games for kids to learn mental arithmetic, so that's fun. A bunch of other stuff. I run a lot of conferences. That's something that I've been doing for the last 11 years. I've been running a lot of conferences. These are all more of side projects in some sense. This is more for kind of building a community here. So a shameless plug for the functional content that is coming up. If you're interested in functional programming, this is the second functional conference that we're going to have. You can look at the videos from last year. I have a little startup called Confingent which basically builds all the management software for a conference or signing up and things like that. That's basically what drives a lot of these conferences and things like that. In the past I've worked for these companies either as an employee or as a consultant, so that's kind of my quick background about the places that I come from. It's a pretty long list. So that's enough about me. Let's come back to the project. We'll talk about what we're going to try and do. So what we're trying to do is basically build a plugin which has some of these features and many more, but today hopefully we can try and get to these features, which they should be able to accept comments either as a JSON. So you give it as data. If you've seen most jQuery plugins, they either accept a JSON as a data and then it does whatever it needs to do. Or you can give it a URL and it'll do a call for you and fetch the data. So we're going to try and do both of those in this case. What you also want to do is you want to show the most recent comment on the top. That's one of the features we want to build today. You want to have the ability to mark comments as private and only the author of the comment can see. In our case, the author and the program committee can see, but here we're going to not bring the committee into it. We're going to keep it pretty much like if it's private, only the author can see it. So some functionality to show this is real and then you should have the ability to like a comment and things like that and so forth. So that's what we're going to try and get to today. Everyone with me? So what's the first scenario? The simplest thing I can think of is we have no comments to display and we just need to show something like be the first one to add a comment. That's the first spec we're going to try and get to. If you guys want to come in, there are a few more chairs over here that will help other people come in after you. I'm going to try and see if I can fix my screen real quick because this is not the best. This is probably better. Can you see the entire screen now? Yeah, that's much better. Make sure you can see that. Okay, fantastic. I will increase the font size of this as we get into this because this is where we'll be primarily working. And I think that's pretty much what we care about at this stage. So let's write the next spec. What is the next spec? Should display a nice message when no comments are available? Right? So what do we expect? Now this is where we need to do the basic setup of our plugin itself and create that so that's what we're going to do in this step. And we want to expect that, you know, whatever our... So I'm assuming we're going to have a div which says hash comment as the ID, comment as the ID, comment as the ID. And we're going to plonk in whatever we get. We're going to build and put it into that div ID, comments ID. Right? So let's quickly set that up. So one way to do that is there is something called set fixture and you can pass in a bunch of HTML into it which will just create your basic fixture that you need and we need something like div ID equals comments. This is where my comment should go. We're saying this is what is the prerequisite before we get started. So set that fixture and then you should be able to say var commands equals $ and then here what we want to say is comments. What can we do? We basically want to now see what's actually in the comment after we run our plugin, right? So that's our expectations. We are saying the comments.html should be what? Be the first one to comment something like that. All right? Now we need to actually set up our plugin. So we're going to say comments. So you can call any method which is a jQuery method now on this. So we're going to say jq comments which is our method and this is basically invoking the plugin at this point and it should do whatever it needs to do and then this is what we should get back. Now obviously we don't have this jq comments because we've not created anything. So let's go ahead and add a folder. This is where we're going to get our JS into it and I'm going to add... So I have a little plugin thing here. It's called jQuery.commands. And so what it does is basically creates as per, you know, some recommendations from jQuery community around how you actually create your jQuery plugin. So you want to wrap your whatever function you have inside an executable, inside anonymous function so that it doesn't leak scope or things like that. Bigger text. Oh, sorry. All right, sorry about that. Is this visible? This is fine. Maybe a little bit bigger. Okay, so we've got ourselves an anonymous function inside which we are adding a single thing. We're not going to call this this. We're going to give this our actual name, jq commands. That's what we called it. And here we can do whatever we need to do and then basically return this so that you can chain your jQuery plugins. That's just the basic setup. If you wanted, you could write a test saying that you can actually chain up. So you could put an expectation saying that it returns the container back when you call the jQuery method. So you could do things like that. I think because I'm just using a template I wouldn't probably write a test for that but you sure could write one for that. So here what we want to do is basically we are saying this dot append. In this case we're going to put whatever we want to append into this. And we're going to see how we're going to evolve this. So that should be pretty much what we need. Actually we don't need another step because we've already said include everything in the lib, include everything in the js folder. Again I'm going to increase the font of this so you can actually see what's going on here. We've asked it to already include these things. Our plugin should be included. Let's run this. And we expect it to fail. Sure enough, it did fail. So the first test still continues to work. The second one says that can't find references to set fixture. That is because we need to add another. So the set fixture comes from another plugin that I'm using. That's basically, let's get this guy. It's called Jasmine jQuery. So Jasmine jQuery is a plugin which will provide those methods for us. And now it says expected undefined to be the first one to comment. So now everything's hooked in. We're not obviously doing anything inside our plugin, so it's giving an undefined. And we expect it to basically give that comment out. So what's the way to make this work? So inside our actual jQuery plugin, we're going to just put in that statement here for now. Be the first one to comment. Because there's nothing else right now you can do. It's going to hard code that. See if the test passes. Undefined to be comment. So, whoa. Oh. Sorry. Thanks. That actually works. Now you will notice that it's running Safari a bunch of times. That seems to be a weird problem with Safari, where it's opened a whole bunch of tabs. Every time it shuts down, it opens the tabs again, even if you tell it to start a new browser. So I'm just going to close that. So it's going to have just one Safari, one Phantom, one Chrome and one thing. So there are eight tests because there are two into four browsers. Right. Okay, so far good. Very trivial, basic stuff. No fun. I mean, this is any kid can do this, but this is we're going to try and make this more interesting. But this gives you the basic idea around how we are approaching this. Right. Let's go into the next spec and try and make it a little bit more interesting. So this is when you actually have a comment and you want to display a comment. And we're going to start with just one single comment and then we're going to build it to multiple comments and other kinds of things. So I'm just going to take a copy of this. And say should display a single comment from data. Now, obviously, as the moment we do this, we notice that there's some duplication which we can get rid of. We will come to that in a minute. And here I want to pass in basically to start with, I'm going to just pass in, let's say data colon. I'm going to make a little bit of a jump over here. I'm going to introduce the data colon and then pass in JSON. I could have taken a more smaller step and just passed in the JSON as is and then added it into, you know, but I'm trying to make this more like an options that you're passing in because these are different options that you're passing into the jQuery plugin. So this is again another JSON where let's say we say I have some kind of a message which is let's say first comment. And then I have, let's say a user who basically added that, you know, comment. So what we are saying is we are saying jQuery, you can take an optional data option which has an actual jQuery, sorry, a JSON comment that could be derived from the server. In this case, we are directly giving it to it. And then what do we expect here? Something different. We're going to start simple. I'm going to say first comment-narration, right? We're going to then later go in and put some structure to it and make it look much better, but this is, let's take more baby steps here. Sure enough, I run the test. It always returns be the first one to comment, but we are actually expecting something different. So what do we need to do here? We are taking in ops into our function here right now, right? And then, again, the recommended way of doing this whenever you get some options, you have a default and then you map it onto that so that you're providing the ability for people to have the defaults and extend those defaults by passing in. So I'm going to just say var defaults. What's the default? Data with some empty JSON is the default. We're going to add more options as we go along, right? Now we only have one option, which is the data option. And then you're going to say var options equals $.extend and you're going to say defaults and then take the options. This will ensure that if you override any of them, it'll, if you, if you pass in data, it'll override else. It'll give you the default, whatever you set in. So that's like a nice little trick here that you can do. And now we are always sure to have some kind of a JSON, either an actual JSON or an empty JSON, right? So here we can check if what? What do we want to check? There are two conditions here, right? One condition is if nothing was passed in, which means that the data JSON is empty, right? So options.data is empty, right? So we can check. I don't know what's the best way to do this, but has own property and I could pass in message. If it has own property, then I want to do something else. I want to just stick that in. So if it has then this.append, what do I want to append? I've got options.data.message plus iphone plus options.data.user, right? If this, this, else, that, and return. Let's see what happens. Sure enough, the test passes, right? This is not beautiful code. I'm just showing you very basic steps of how we're going to go and then we're going to step back. So what do we need to do now? Now we want to look at it and say, hey, how can I improve the design of this? Even though we've written just like four lines of code, we still have a lot of scope for improving the design over here. So we want to do that and we're going to start with our tests. Remember we talked about this duplication between these guys, right? Why do we need to duplicate this over and over again? So we're going to try and move this up into our before each and we're going to try and move these guys up into the before each section. So you don't need that here. The only thing is you'll have to pull out this work outside, so it's accessible. So there we have got rid of some duplication and simplified this a little bit. Then the test still works. No problem. What can be done here to simplify this? Or this is simple enough for now. I would say it is not the best, but it's simple enough for now. When we write the next functionality, this is going to make this more complicated and at that point we will come back and refactor this, right? So what's the next test you would write? It's the next spec you would write. Passing in multiple comments, that's going to push us down a certain path. So let's try to do that. So actually, before we go there, actually what we had said is that we would probably like to put some structure to this, right? If you look at a typical comment that I showed you in the diagram here, wherever it is, there is some kind of a structure. So there's a header of the comment. There's a body of the comment. There's a bunch of other things. So let's try and put some structure in so that, you know, we kind of start building something real quality stuff in right away, right? So what I actually want to do is I'm going to change the expectation here. And instead of saying this .html, we can say . to contain element. So to contain text is one thing we can do which just replaces that. So we don't have to, we don't have to do .html internally. This should still work. Let's make sure. Yep. What if we are just smoking up stuff and none of this is actually going to work, right? So generally like that's a question that always comes up in my mind. And I always prefer to have some kind of example .html that I start building along with my plugin. So as I build my plugin, I also have an .html which actually does it. And once in a while I go and look at actually is it rendering the way I want it, which kind of gives you the confidence that, okay, this is actually real, not, you know, just all up in the air. So let's go and, you know, I have a basic dummy .html. So we're going to try and add that in here. So we want to include. What all do we want to include here? Increase the font size. Why do I forget that always? Okay. So I want to include here my .js and this is .jquery commence. That one thing I need to add, then I just say commence.jquery. What did I name this again? Let's go make sure it's called commence. So here and then we are at this stage passing in a kind of array to it. So let's pass the same thing over here just to see what actually shows up. So I'm just going to copy this from here, put it in my .html. Okay. So if I go now to another .jquery instance, which is running at a local host, or you can run it through a file, .jquery demo example. So there, sure enough, it does show first comment the way we want it. So, you know, our tests are actually quite realistic. Once in a while, still it might make sense to go and look at this and build the example along. For example, who will use your plugin, have an example ready to go. Right? Everyone's with me so far? So we've done some basic refactoring here. We are still left with some more. We are saying that we want to put some more structure to this like a header and a body. So for our comment, and then we're going to build it out that way. So right here, what we can do is we can say expect. Expect commands.toContainElement. And so if I don't have anything, I expect it to contain an element called div.empty. So I want to put something so I can do nice CSS on top of it. So I want to say, you know, an element called div.empty should be present and then it should contain, you know, inside that div. So I can say .fine and then I can pass in this CSS selector into it. So I'm saying find this div.empty and that should contain a text. Be the first one to comment. All right? Similarly here, I would want a kind of slight different structure. So I'm going to just copy that. So comment.contain. So now it should basically have a div with comment as a class on it. And then how many of these would you expect now? This.size. What would you expect the size of this to be? Size to be one, right? Because we only provided one comment. You should only have one. It should not do something else. And then lastly we want to make sure that this guy.contain.text. So if I run this, it gives an error. That's interesting. Yup. So find and then there is a parenthesis missing over here. Good. Okay, first test is passing. Then the next two tests are failing because we made some structure changes which is not reflected in the code yet. So let's go back and make those changes in the code. So what would we want to do here? We are saying that when we show this actual message, we want to put some structure to it. And similarly we want to put some structure when we are going to do the empty one. So let's tackle the empty one that's easier to handle. And then, so we're going to say this, let's say, empty equals, what do we do next? Add class, right? What's a class name? Empty.append, which is essentially what's down here. Add an empty div and then add the comments inside that. Whatever text we want. And this empty is what we want to then append to our container. Same thing here but a little bit more complicated. So we're going to create a div, let's call it header. That's the header. And this should contain what? In our case it should contain the option.data.user. And similarly we're going to create another one which is going to be body. And this should contain the message. And lastly we want to create a comment itself, which is a div which contains comments, if I can type. And then this would basically append first the header and then append the body. And finally we can then simply say append the comment. Let's run the tests. Still one thing failing. So it's expected this whatever object but it got first comment, of course. Why did it get first comment? So it says to contain. Somewhere I think we've not updated the tests. Oops, my bad. This should have said that it contains one comment. And then we could say dot header. Space dot header to contain the name of the person. And then dot body to contain first comment. We made some structural changes. We're back with the test passing, right? Is there any problem you see with this approach that we're doing here? Is this fine? Some repetitions are there. There could be refactored, right? But more importantly is like going and looking at these selectors and doing this. Is that a good idea? At this stage I would think it is okay because otherwise it would be an overkill. But once your plugin starts getting very complicated then trying to do something like this might be not a good idea because you're now trying very closely to the structure. Maybe you want to have, pull that out. So that's where we are going to head to is basically separating out a view and then trying to do things on the view, separating out the model, trying to do things on the model where you don't mix up the structure and the actual data, right? So that's where we want to go and that's the problem we have right now over here, right? But for now this is not too complicated so we can live with this. Once we start doing a completely nested structure, we start doing adding lot more things. We start adding events to it. This will become extremely difficult to maintain, okay? So again we are just building step by step so it is okay for now but don't go back and think this is the best way to write your Jasmine tests. All right, so all tests passing. Let's go back to our guy over here and see what happens now. Let's put a little CSS on it so it looks a little pretty. So just going to create a directory CSS and just going to add a style sheet. What do we call this? jquery.commands, keeping the convention. And I just have this handy comments over here that I'm just going to plunk in because I don't want to spend time writing CSS right now. All it does is it just gives a little border and color and things like that. And then in our example we want to add the CSS so we can actually load it from our HTML. And that would basically be jquery.commands. So if we now went back to this guy, refreshed it, you see some colors and borders added to it, okay? This is very basic but kind of taking us towards the direction where we want to go. Now there is specific tools available if you want to do specifically CSS related tests. So there is a unit testing framework for just doing CSS related stuff. That's kind of out of scope right now. I'm not going to go into that but just want to mention that it does exist. Let's go back to our thing and that's where the fun is in the next test is when we want to make the leap and convert this into something more interesting. So I'm going to just shut these guys off. All right, what's the next test we want to do? Multiple comments, right? Multiple comments would make sense. Display should display multiple comments from data. So here we have, now again we're going to start duplicating some of these things. So I was thinking what we could do is just take this out, create a variable called first comment, something like that. And basically pull this guy out and then here you're just referring it to as first comment. Similarly here you can now refer to this as first comment. But now we want to pass multiple, right? So we want to convert this simple stupid JSON that we had into an array of JSON because that's what your server is going to return an array of JSON, how many ever JSONs you're going to have. So let's convert this into an array. Let's do this guy as well. And here we're going to create another JSON which is the second guy. So this is again message second comment. And then the other thing was user colon some other name. So now we are passing in an array of JSON objects as comments, as data into this. And then what do we expect here? We expect size to be two. We expect the first header to have this. And then we expect the last in this case because we only have two to be Jack. Everyone with me so far? Let's run this and we have test failing. Both the tests are failing because we did change the structure to an array instead of just a JSON. So let's go back and fix a couple of things real quick here. Let's make this an array as well before we forget that. And let's go back to our actual plugin. And here we understand that this is now going to be an array. So the default is an empty array. Nothing's in there. Let me just write. And then here now the options you're going to get the default options. This options guy is now not going to have data which has own property, but it's going to be an array. So this is going to basically now what we can check and which actually I like better is that now we can check if that data size. If the length of the array is not equal to 0, then do this else do something else. So if the length actually I like to flip this now. So if it is equal to 0, which means you didn't pass in, it's an empty array, then do this else do whatever you need to do. Now in this case earlier it was very trivial. We could simply just add one. Now we need to do something more than this. So let's extract this guy as a method. I'm going to pull this guy out at the global scope right now because I'm trying to move these things out of the plugin itself. And what do we call this guy? This is basically build, comment, HTML. So what that did is basically pull this guy out here. It's not a recommended thing to just put these in the global scope. It's just an intermediate step. We're going to fix that. But what it did is basically pull this thing out and it has a few more issues. So what we want to do is instead of passing options, you're going to pass in a specific comment. So instead of doing that, let me just rename that to C and then this will become C dot whatever. C dot message and C. So from here what we're going to do is now I could say dot data. But data is actually an array. It's not a single comment. So we actually need to now loop over this data and construct this thing. So we're going to have some kind of inner HTML element which I'm going to build. I'm going to tell you in a minute why I need to do that. So let's get ourselves some kind of a div and in this div basically I want to loop over all my options dot data. So I'm going to say options dot data dot for each if that works and that takes a funk which takes the comment. That takes the comment and what do I do? I basically say inner HTML dot append. What do I append? This guy. But not the entire options instead each comment. That's going to basically append this whole thing and this and then what do I have here? I have inner HTML dot I have to do that to get out. I don't need the div. I just need the body of it. So it's going to get that out. Am I done at this stage with this basic stuff building comments HTML and passing the other nice thing you'll notice is we are passing dollar to it instead of assuming the dollar is there. This ensures that tomorrow you flip from jQuery to something else. You will pass that all along and then you could do whatever changes we get to that part but we kind of going down in the path that this should be kind of completely decoupled from what library itself you're using. And so you should have the ability to pass in its kind of dependency injection, which is a big word, but that's what we're trying to do. Instead of assuming that there's a dollar and I always can access the dollar from wherever. So the test does pass. No brainer. But what this help does understand is that you know what this building HTML business does not actually belong to your actual jQuery. Right. So that's something we want to pull out of this. Right. And there's some more stuff that we are doing in here which actually does not belong to the plug in itself that can be pulled out and it's quite independent of the plug in itself. Right. So what would you pull this out into? Would you pull this out? That's also doing some HTML ish stuff and it's nothing to do with jQuery per se. Right. So we could technically pull this out into its own global function and we could call that as an empty command HTML. Can we keep it in there is what I'm trying to do is move this to a view class because this really belongs on the view. A view object if you will just move it all on the view and let the view handle the HTML portions of it instead of the plug in itself having to deal with the HTML. Into .html file. That's another thing. So you could have it in the HTML file and just include that is what you're saying. Right. Look it up as and this included. That's that's also possible where I'm going with this is trying to make it mostly like pretty self contained functional in nature. But that's a valid approach as well. So this basically pulled it out. It does whatever it needs to do. And here you will notice that now I don't need the I could inline that thing pretty easily. Is everyone with me so far? I don't like this this dot append twice. So I would like to basically say war HTML content if I can type. And here I want to basically take this guy assign it to the HTML content. Same thing I want to do here is take this guy assign it to the HTML content. And then only once basically do this dot append HTML content. What that basically means is that this whole section that we have now could actually be pulled out. And this would return you whatever HTML based on the options that you've given to it. And then that's simply what you're going to append or that's what you're going to bind to your container and return. So I'm going to pull this out as another global method. We're going to call that as to HTML. It's got a typo which we're going to fix. So it's pulled out another function which basically does the construction of the HTML. And now let's look at our code and code plugin code itself. Is this much easier to understand and maintain? This is the core of what your plugin is doing essentially. Obviously we'll add few more things into it. But rest all stuff can now be converted into actual objects. And then you could start modeling this in terms of some kind of an MBVM pattern or whatever pattern you want to use. Let's run the test. It's been a while. All the tests are still passing. I said once in a while it's a good idea to go and check how does our UI look. It looks pretty good. Actually let's do one little quick thing over here. So this example has only one comment. I want to give it the second comment and see how it actually would show up. Just to be sure we are not smoking something in our own world. Sure enough. Still works. All right. Now we can pretty much close this for the time being. We could go ahead and convert these into... So one of the things you will see is that we are passing the dollar to all of these things. And you could essentially convert this into a view class at this point. Which would have a bunch of private methods and one public method that exposes which is basically the two HTML method. And it's going to give you the final HTML, do whatever it needs to do with it. So that's one step we can take. We can go into that direction. The other thing is we can say, okay, what happens if we start adding some logic now? Like the logic of maybe private should not be visible. Only certain things should be visible or things like that. So any opinions which direction we should go at the next test or further refactor this into making this as a view class. Refactor. All right. So I'm going to go with that because as I said earlier, TDD is a lot more fun if you just refactor it all the time. So what do we want to do here? So notice this two HTML method. This would essentially be on some kind of a view class, right? So let's create some kind of a comment var comments view equals, I'm going to say new. I don't have any comment view as of now, but I'm assuming somewhere there will be a comment view reminder. And I'm going to pass the dollar and the options to it for the time being, keeping the same. And then this method that we have, we're going to call this on comments view dot two HTML, which probably would not need to take anything. Yep. So let's create our function. Let's give that a name comment view and this guy is taking in the option and the dollar. And we need to just do this dot options equals options and this dot. Now, I mean I could do this, but I'm going to just say take you from now. We have that and then that gives us the object. And then this two HTML should now belong on the prototype of the comment view, right? So we're going to say comment view dot prototype dot two HTML equals this function which doesn't take any of these parameters. It instead would say this dot options. It would say it wouldn't even take this guy would say this dot take you this dot take you. The sooner we did this refactoring is actually better because otherwise you would end up spending a lot of time kind of if you were to match down a much stronger parts than this. That's it. Now obviously it's going to say I don't understand this because you're inside the for each. So at that point we will have to basically hold the reference of our object which is our self equals this. And then we can say pass this guy can still call these methods which are in the global scope. The next step would be to kind of bring them also underneath this guy. Let's run the test everything still passes. So one small refactoring we kind of start pulling out a view and moving this into the view, right? And these other methods which belong on the global scope we don't like that. We're going to pull those in also into this guy. So far everyone's with me the kind of approach we are taking is this too slow for you? I've done this before so it's very slow for me but I'm kind of making sure people follow in. Let's do this guy. Now only till here. So that now goes under it takes the dollar and it can do whatever it needs to do with the dollar and here. Now what do you call this? Let's actually the one thing that is very confusing in JavaScript, especially if you program in multiple languages is this whole this thing you don't know when is what. So one of the conventions I use is just right at the top call self. So then onwards I know what I'm referring to. So then I can say self dot this method that should be pretty much it to make that work. Yeah sure enough. And then the next thing we need to do is bring this guy also with an R scope. This now takes dollar and C which is cool and then here it's complaining that I can't find what you're referring to. So it's self dot this and C that's it. So that's pushed everything into a view and if you notice this is really building a bunch of, you know, what you're passing in is some kind of a comment model class. You could probably build this out as a comment object which essentially holds whatever information it needs. But at this stage it would be premature to do that because the comment itself has no functionality other than just being a dumb data holder. So let's not jump there but I'm just kind of hinting that this is an indication that hey this come kind of a model lying out here that could be pulled out. And then I think we are in pretty good shape as far as our plugin code itself looks. Yeah. And then all the HTML portion pushed out to the view. So next thing is let's write another tests and that would make it a little bit more interesting than what we've been doing so far. So the next test I want to do is let's say you want to actually sort your comments based on the order based on the most recent one on the top. That's there's now going to be some logic that's going to start kicking in. So what do we need to do? We are saying that should display most recent comments on the top. And so if you had the same guy, you had the same guy, you had the first comment and the second comment. Now somewhere in the comment itself you will need a date thing that would say when it was posted or whatever which our server obviously knows. Our server is going to return that we're going to take that to then decide the order in which. So let's say for the purpose of this we're going to pass in a date to this guy which is last updated time. And I'm just going to pass in an ISO string which the server is going to return easiest way to remember an ISO string is to copy it from somewhere. That gives us an ISO string of 2014, June 19th, whatever doesn't matter. And so that's good. All of these guys are good here now on this object. We obviously need to first and then, oh no, what am I doing? I don't need this. I already have a comment. I now need this guy to have last updated whatever we put here except that this date probably would be a more recent date. So right now if you see the order in which it's actually displaying here, it shows the first comment and the second comment. Let's try and flip that around to see if our logic actually works. So the first comment is actually updated on 619. So let's make this 719. This is a more later comment, latest comment. Actually at this stage, I would argue that the first test is not really relevant anymore. So we could see the first test as in this particular test here that we have should display multiple comments. We're actually making it a more specific spec. So we could actually get rid of that one and make it more specific, which is should display the most recent comment on top. We've got the first comment. We've got the second comment, which is later. It should be true. And now this should be Jack while this should be run the test expected to fail because we have no logic to show it in a reverse order or whatever the latest order. So now we need to go and build that logic to show it in a certain order with me so far. So what do we need to do now? You guys are going to drive this from here. The point is, is it simple enough for someone else to jump in now and kind of take it forward, right? That's like the litmus test. Is your design simple enough? Is your design easy to understand? Can I bring someone in now and ask them to kind of take over and drive from here? That's to me an important aspect of trying to keep it simple. So I'm not going to risk that on the stage. I'll just continue doing it myself. What do we need to do now? You guys can guide me. So obviously somewhere where we are building the comments somewhere where we are basically looping over the comments. Now something needs to happen before we start looping over our comments. So we need some kind of a sort feature which is going to sort the comments based on the latest time. And then once you sort, then you simply loop over it and then you do. So it should be fairly easy to add a sort to the data. Now this is where I would say that we kind of doing something more with just the data that we have. And at this point you probably want to pull this out into some kind of a comments object and do all of that sorting functionality, other kinds of filtering functionality that we're going to add into that. But we're going to start right now here and then move it out there because you know that this works and just make it work inside the context of this. So somewhere here we basically want to sort our data based on updated date. Last updated. So what do we do? Is there a sort function? Yes. This.options.data.can I say sort and that takes a function. This is going to take a and b and it's going to sort and return you back something, right? No? I don't remember my sort so you guys have to help me. Sorry. Return a dot. Good. Last updated because it's a string right now and you want to convert it into some kind of a date, right? Because what we've passed in from the server is this. We'll keep that in mind. So let's pause on this. Where is our a? Our a is here. So let's say var first or a last updated equals. Can I say date dot parse and take a dot last updated? That's going to give me a date back, right? And I need to do the same for b dot last. So I've got these a and b. Now what can I do? Now I can. Can I directly subtract or I need to say get time or something like that? Well, we'll try. That's one of the fun of TDD is you can actually learn, right? I don't know. Let's just try because worst case is I'll run the test and I'll see it right away. So it gives me an opportunity to say, well, maybe I could just be able to subtract these guys out. Last updated. Now I am doing this wrong. So this should be a last updated and this should be b last updated. I'm not sure what the order should be. Should it be b minus a? It should be update. I have nasty names here. It's going to rename that last updated. So this sort is going to return me what? A sorted thing. Can I just then say dot for each on it? Yeah. Trying to get a little bit of field for the functional style of doing things. Sustaining them and so I don't know if this will work. Let's run. It doesn't. What does it say? It says expected blah, blah, blah. First to contain jack, but it did not contain jack. It contained nourish. So that must be our order here, our sorting order. So maybe flipping the order might just work. I don't even need the get time. That's something interesting. But you know just flipping the order. I know we're trying to get the one which is the latest on the top. So that minus the new one and that's going to give us. Let's so this is not going to change because we don't have now. That's interesting. This should have failed because we are not passing in the last whatever in the HTML. We're not passing the last updated time that ideally should have bombed here. But we can be pretty sure that the server is going to return this because that's the API of the server. So we're not going to put some checks for it. But anyway, now what we have here is is we're going to basically sorting it and then applying some kind of, you know, building appending it into each of our HTML. At this stage, you can see that there is a bunch of things that we are doing which is specific to each comment that we are getting. And we're also trying to pull out things from the comment itself. So this might be the time when you might want to create something like a function comment, which takes in C and then you can say this dot message equals C dot message. This dot user is user and then this dot last updated is date dot parse C dot last updated. And then your comment could also have a function thing on it comment dot prototype dot compare compare true. And that would basically be a function which would do something. And this logic that we wrote here. So a we don't need this parse because we've already done that now and simply we could say here now compare return. We need to take other in here. We have to say other dot last updated minus this dot last updated. Are you guys with me making that move here? And so wherever we are actually getting this C guy from that should end up becoming the object itself. So where is that? So here we will have to create an object and then simply ask the call that function. But I'm wondering where would be the right place to create that object. Because here we have simply taken all the data and passed it to the true XML method. In the true XML method or maybe much before where is our constructor right here. No, this is kind of weird you don't want your declaration at the bottom. Here we're just holding on to the options. So instead of holding on to the options how about holding on to all the comments and array of comment objects. And so this might be a good place to do the conversion. So you could simply and we're not we're not using anything else in the options right now. So we could convert this into I'm going to leave this for a minute. I'm going to say this dot comments equals. So I've got the options. I can say options dot map and I can take a funk into this. What I'm trying to do inside this map is basically take each of the object that I get converted into the object into the comment object and then store that into the comment. So what do I need to do here? Simply return. I'll have to take something here. Oh, sorry. Good point options dot data dot map. And then that should basically give me each of the object or each of the data option. And then I can simply say new comment and pass in the seat. Fair enough. That's going to give me comments and now I can work with comments instead of working with options. So let's get rid of that. And now we have to go back and see where we are actually using comments. So here this will basically become comments. Is that correct? And same thing here. It's going to become comments. And then I'm going to say comments dot sort. Do I need to say comments dot sort? Why not sort it right when I build it? Right? Why do it? Because it's not going to change once it's built. Right? So I could just sort it right there. I might need the ordering. Well, if I did, then I would store it. But in this case, at least I don't have a use case that I would require the original one. So I'm just thinking I might just sort it and keep it in the sorted order. So it just becomes easier to display. Yeah, that might be the default. You could override that by passing in an option. Like what sorting thing? Like that's something we've not really gone into. But the options thing that we provided, you could pass an option saying, do you want to show it sorted? Do you want to show it in the original order? So that's a simple option that could be passed in from your plugin. Yeah, so I could do the move the sort over here. This would get a lot cleaner down there. And there, down here, all I would have to then do is for each and then call comments dot, you know, view on that. And then I would be done. I only have seven minutes left. So I'm kind of trying to just talk through a little bit and not actually do it. But so that you see, I've got a comment now, which is really more like a model. It doesn't really have any view related logic in it is going to do some sorting. It's going to do some other functionality in there. We talked about private, which we couldn't get to. So private would be another, you know, option. Another thing that would be passed to the comment. And then the comment would decide whether this is viewable or not, depending on whether the private flag is set. So the comment itself will start doing a bunch of interesting things for us. We would have the comment view, which does all the HTML related stuff, which would probably become a lot smaller because all of this functionality would get moved into the respective places. So the only place where you will see the jQuery itself leaking into is your comment view. And then your model and other guys would not have any jQuery reference at all. Right? So if we had more time, we could obviously go on and kind of do that. I'm going to quickly jump in and I'm going to show you something that I have ready here, just so you can see what this could evolve to. Oh, we didn't even get to Ajax. Let me quickly talk about that, right? So here I have something where I can quickly show you how would you stub out an Ajax call. So let's say that, you know, we are going to pass in comments. Instead of data, we're going to pass in a URL. Right? So if we pass in a URL, we are saying this is the expected response which we were earlier passing in directly. Now we are saying that's the expected response. And that's something that we are saying, jasmine.ajax.stubrequest, this get request thing. And return, you could do 200, you could do 400, you could do different statuses, and you could just stringify your JSON and pass that in. So once you do this, the rest of it should work as normal. The one other thing you would have to do for this to work is basically you'll have to say jasmine.ajax.install and jasmine.ajax.uninstall at the end of it. If you're trying to do just this, without the Ajax stubbing out, you might run into cross domain issues and things like that because now you're trying to post something from local host to some other thing and it'll complain. So this actually gets you to work around all of those problems always because it's just going to stub out the call. So it's very simple. Like all I have to do is say jasmine.ajax.install and then I have to set jasmine.stub the URL and what response I want to return. I mean it's pretty trivial. What you also need to do which is important to remember is in your karma. Let's get this big. You will have to say jasmine.ajax. Just that little change over there and the rest of the things will work as normal. And let's quickly look at what does this guy look like after trying to do all of this stuff. And this is also available. This code that I'm showing you is also available on the same URL that I showed you on the GitHub. Just knock off the demo portion at the end in the URL and you should get this example. This is all fully checked in. So basically we have a comment. Notice we were kind of already here at this point. Now we added the private as well. The comparative method is on this. And then we also pulled out another model called comments which would basically do the map and then the sort. So the comments would hold on to all of those things. There is something called authorized. Is this user authorized to view this comment or not? And then pretty much everything else is what we have seen so far. So the other thing is that when you get JSON from the URL, if you pass in the URL, you'll essentially have to pass in a callback because otherwise your test might run through it and it might not get the result. So what you'll end up having to do from here. So if you notice this is where we were kind of going. So you have a new comments view. Then you're going to say apply comments. You're going to pass in the option and you're going to pass in a callback. So whenever it's successful or failed, the Ajax request is going to basically call the callback which is going to append whatever response you're going to give to the container. So this is the only place where we are touching the DOM or we're doing anything with the DOM. Everything above this doesn't even touch the DOM. And that's one of the things of kind of trying to make it immutable and having only side effect at the last step. Everything above this is essentially a pure function. So it doesn't do anything with the DOM. It essentially builds kind of a semi-virtual DOM and operates inside that which is easy to test, which is easy to do whatever you want to do. Plug in a different library instead of jQuery. It should still all work as normal and then pull that out as a separate thing. So yeah, I have two minutes left but that's kind of a quick run through of where this would end up going. And yeah, that's pretty much it. So I'm sorry we couldn't actually do a live demo of the Ajax thing but I hope you get the idea of how trivial it is to do Ajax stubbing out. All right, so two minutes for questions. What's the difference between spy and stub? So that's actually one of the common misconceptions that people have is they always get confused between what's spy, what's stub, what's mock, what's... So there are different kinds of fakes. So we call them generically as ways of faking something. So to me, a stub, which is what we are doing right now, so we're saying don't go to the server, stub the call out to the server and just return this particular data or JSON or whatever. So that stubbing is when I want something and I'm just going to cut that out and I'm going to return the data right away. Spying is essentially where I expect it to do something on behalf of me, like for example send out an email. So I'm going to say spy that method and just in the end I want to verify that actually did you call the method with the right parameter? Did you call send email with the right user name, with the right email address in the body? So that's spying. So you don't... One is stubbing out when you want to get some data. Spy and mock both are kind of, you know, I want somebody to do something on my behalf and I'm going to just set the expectation and then verify. Mock is you have to set the expectation beforehand while spies you don't set the expectation you just verify in the end. All right. Any other questions? Yeah. How do you estimate? That's a different topic. I have not estimated in the last eight years of my career. So you're asking the wrong person. So if they ask me to estimate, this is one of the reasons why I don't work for big companies because I don't know how to estimate. I don't think anyone knows how to estimate. Everyone's just throwing a dice and throwing a number out there. So that's the honest answer. How do you estimate? No, throw a dice and pick up a number. Because if I would have done the similar plugin ten times before, yes, I know how long it's going to take and I can give you an estimate. But you know what? I've never done this before. And I don't know. I can give you a guess, but the problem with giving guesses is then you get held accountable for giving a guess. And that's a very dicey position to be in. All right. Last question. This goes back to Dan North and all of his... So Dan North is a person who coined the term behavior-driven development. And his point is that when you use the word test-driven development, people think about it as a thing about testing. But as you saw, this is not so much about testing. This is more about driving the design and flushing out the behavior of what you want. Right? The point is that the terminology test-driven development makes people think that this is about testing. So he kind of coined a new terminology called behavior-driven development, which confuses the heck out of people, saying, what's behavior? What is test? It's just that test-driven development had the test connotation, so behavior-driven is better. Now, I have a paper that I had written which basically goes into more details about talking about different styles of doing test-driven development. In the paper, if you look up as aftars of test-driven development, you will find that I list a whole bunch of different styles. So behavior-driven development kind of fits into the outside-in way of doing test-driven development, which basically means that we're going to look from the business perspective, from the user's perspective, and kind of drive the system from outside-in. You know, are we building the right product? Two, are we building the product right? Kind of a thing. When you're doing more of the traditional what people think of test-driven development, they kind of flush out the system inside out. So you might start with the core of your system and start flushing it out. Behavior-driven development also kind of has become more of a methodology now in some sense, where it talks about how collaboration should happen between people and all of that stuff. So I think it's kind of getting bigger than what the original intent was. But to your question, outside-in or inside-out? All right. All right, thank you very much. Hope you had something to learn from it. Hope you had something to learn from it.