Today's applications run on the web, and they're only getting more complicated. The old "toss a few lines of JavaScript on a page and pray it doesn't break" approach just doesn't cut it any more. To be successful developing today's rich web applications, we need rigorous, professional software development techniques. From test-driven development, to continuous integration, to automated cross-browser testing, this session covers what it takes to build JavaScript that lasts.
 All right. Is the sound on? Can you hear me? Okay, let's get started. The question I want to talk about today, the question I want to try to start to answer today is how can you stop your code from killing your company? That's kind of an important question, don't you think? We've all experienced other people's code that was in the process of killing our dreams and hopes and wishes, if not the company. So what I want to do in this session is talk about what you can do with your code, if not to save your company, at least to prevent you being the one to blame for a dime. It is, of course, true that technical quality has nothing at all to do with business success. Good business is what makes a company. However, what I've seen is that over time, about five to nine years, bad code will kill companies. Because what happens is in five to nine years, the code is so crifty and so hard to work with that you can no longer maintain it. That even the most simple change, like changing a little color or a font or moving things around by a few pixels, takes three months. Anything more substantial is just impossible. How many of you have been in that kind of situation? Maybe different details, but yeah, a lot of us have been there and I think half of you are afraid to raise your hands in public. It's not fun. So what happens, the company decides to rewrite and rewrites kill companies because rewrites mean that you're spending all your time and effort and valuable energy reproducing what's already been done. So meanwhile, you're falling behind the competition, your customers are getting angry and you can't ship anything until you've reproduced absolutely everything that came before, which is always more than you thought it was when you started. That's what we call bug for bug compatibility, right? It's not really done until it breaks just like the old one did. So bad technology breaks the company and that's what I want to talk about today. This is my definition of good design. Good design minimizes the time required to create, modify, and maintain the software while achieving its business goals. And in software development, most software development, we spend far more time in maintenance and modification than we do in initial creation. So a professional developer, one who doesn't wish to kill his company, you know, the kind that hasn't burned out yet, a professional developer is going to want to focus on minimizing the cost of maintenance because we spend so much more time in maintenance than we do in initial build. Now, a lot of the popular stuff you see online, you know, the GWIS frameworks and, you know, you can produce a to-do list in just five lines of code. Don't ask if I would have anything more complicated. But to-do list, yes, we can do that. They all focus on how can we slam out code quickly. But I think that's a mistake. I think what really matters is what it takes to maintain code for three years, five years, 10 years, 20 years. There's code out there that's been alive for 20 years. Someday, somebody is going to have the perhaps misfortune of maintaining code you wrote 20 years from now in JavaScript, no less. All right. So what causes designs to get worse? Anybody? Anybody? Time. Typing. You type on the keyboard, you make your design worse because the fact is, is you can't predict everything. So even if you made a perfect design for today, which most of us can't, and I include myself in that category, I don't even try anymore. Even if you made a perfect design for today, something's going to happen next month, next year, that's going to make that design inappropriate for the situation. So typing on the keyboard and time actually creates technical debt. The only way to fix that technical debt is to improve the design of your code. The way to do that is through refactoring. Refactoring is a necessary step to prevent your code from killing your company. So my name is James Shore. I'm a programmer. I've been doing agile software development since 1999 and extreme programming since 2000. And you can tell by that phrase, I used to do extreme programming that I am an XP bigot because I had a lot of fun with it and a lot of success. So I'm very much into test-driven development and continuous design and refactoring and continuous integration and all that stuff. And a couple of years ago, I got involved with JavaScript. I wanted to do some significant work in JavaScript, not just the sort of fiddly stuff that I've done before. And of course, the way I wanted to do that was applying the XP, the Agile Engineering Practices of Test-Driven Development and so forth. And that I learned, you know, I had to figure out some stuff. I had to learn about stuff that other people had done. And last year, I did a Kickstarter about that experience, about test-driven development. And I have a screencast called Let's Code JavaScript where I talk about doing test-driven development and other rigorous practices. So that screencast in this talk is about rigorous professional JavaScript that is creating software that prioritizes maintainability over ease of initial creation. I'm going to use a very simple example to illustrate this concept. You could probably all do this in your sleep, but could you do it in a way that was maintainable? That's the question. So what this is is a classic validation form. You press the submit button. I want that text field to turn pink. You put something in it. The text field press the submit button. It takes you to the next page. So how would you program this in a way that was maintainable, that was easier to maintain than it was to originally build? That's what we're going to talk about today. Please ask questions throughout. Don't plan on stopping at the end, but I do plan on stopping anytime I see somebody have a hand raised. I have a fair amount of material to cover here, and some of it I will probably just zip right through if nobody stops me. So don't hesitate to ask questions. You've all had beer, and if you hadn't, it's available. So that should help make it easier to ask questions, I hope. I've been in Finland. It's worse there. All right. The other thing is there's live coding ahead. That means two things. One, something is going to go terribly, terribly wrong, and I'm going to look dumb on stage. I'm used to that. I do a screencast. And second, I'm going to miss stuff. I'm going to type something, and it's just not going to work despite my notes and everything else. So I'm really going to rely on you to tell me when I make a mistake. So consider this pair programming with 70 people. All right. So we want to enable refactoring. We want to prioritize maintainability over initial creation. There's lots of ways to do this. I'm going to talk about five techniques in today's session. A build automation, static code analysis, continuous integrate. Well, you can read. First, build automation. On any significant project I do, I have a goal to be able to check the code out from source control. You all use source control, right? Yes. Yes. Yes. Good. Check the source code out from source control. Disconnect the network cable. Press build and see it work. This is very important because there are way too many projects out there. I would say the vast majority of projects out there where if you were to go away for a year and come back, nobody would be able to build the code. Nobody would be able to ship the code. It would be dead, dead and gone. There is a sort of sad example of that recently. There is a popular indie game called Haunts. Has anybody heard of this? It was kick started for $28,000, which isn't really enough for an indie game, I don't think. The person who did the kick starter wasn't a programmer, so he hired a programmer. Inevitably ran out of money because $28,000 is not enough to develop a game. But people in the community said, oh, we'll take over the project. We really like this project and we want it to succeed and some of us are programmers. We'll take the code and finish the game. So he took the source code and put it up on GitHub and nobody could compile it. And that's because the code had been checked in, but all the dependencies hadn't been checked in. Those were supposed to come down over the network, but this was in go and the way the programmer wrote it, I don't think this is Go's fault, the way the programmer wrote it is that it would pull down the latest version of the libraries when it pulled down the code. And those latest versions weren't compatible. And last I heard this game, even though people wanted to develop it, they couldn't even get it to compile. This happens in big companies, too. Huge amounts of investment and money into a project. People go away for a little while, they come back, they can't build the project. I'm sure some of you have experienced it, although maybe not to that degree. How many of you have come into it as a new employee into a company, sat down at your workstation and then spent a couple of weeks or at least a couple of days trying to figure out how to get the stuff working? Wouldn't it be nice if you could check out the code, disconnect the network cable and have everything work? That would be awesome. So that's my first step. Whenever I start a new project, I want some sort of automation in place so that I can build and eventually deploy the app with one button, maybe two. My tool of choice for that is Jake in JavaScript. There is another tool called Grunt. How many of you are using Grunt right now? Just a handful. How many of you are using Jake? Less. Yeah, that's sort of the story of my life. I like Jake because it is basically just JavaScript with a thin wrapper over it. Grunt is more configuration-y in that it's more declarative. It's not just JavaScript. I haven't actually used it, though, so if one of you who's used it wants to correct me, feel free. Anyway, now for the live coding part. We're going to install Jake and see if we can get this working. So I'm going to pull this down off of NPM. Maybe. How many of you have used Node? Node.js. Node.js is a pretty neat language or not language. Pretty neat tool kit framework, engine plus framework. And it's good for a lot of things other than writing IRC servers. You can also write command line tools with it. And it has a very nice package manager, NPM, which stands for not a package manager or I'm not called a package manager, something like that. Anyway, NPM is a way of downloading all kinds of stuff, including Jake. So once you install Node, if you type in NPM install Jake, it will put Jake locally in a Node modules directory. Can everybody see this okay? Okay. It's kind of an odd room. All right. So now that we have Node, now it wants you to install it globally. I hate installing stuff globally because if I install it globally, it's not in my repository. If it's not in my repository, I can't check it in. If I can't check it in, then when you get it out, the code won't work. And we want the code to work, right? So instead, I'm going to write myself a little shell script that is going to run Jake for me. So now if you were to check out this code and type Jake, you would see the exact same thing on your screen that I do. Assuming you install Node. All right. So it's saying that I didn't have a Jake file. So let's make a Jake file. Okay. So the way this works is Jake is a build tool. It's very similar to rake, if you're familiar with rake, which is in turn sort of inspired by make the Unix build tool. And the way it works is you specify tasks, for example, task example, and provide functions to that task. And when you run that task from the command line, whatever you put in the function will run. It's very simple and straightforward. So if I say Jake example, and I did this right, it should say hi. And there it is. Right here at the bottom. You can also provide descriptions so that when we run Jake dash t, it will list all the tasks. So you can see it says Jake example. This is an example task. And the most importantly for a build tool, you can provide dependencies. So if I put, and you can put in any number, that's an array. If I put dependencies here, it will run, let's see, it will run the dependency task before it runs the example task, which is useful for all sorts of things, like saying, well, before we deploy, we want to run our tests and we want to link our code, for example. And it will only run each dependency once. So here if I type Jake example, it should say I'm a dependency and then hi, which it did. You're going to notice me do that a lot. I'll say it should do this because I've worked with computers long enough to know that half the time the computer wants to beat me upside the head and say, no, that's not going to happen. So I'm always predicting what's going to happen so that when I'm wrong, I know to stop and look and figure out what's going on. There's one more thing you can do with Jake and that is provide a default task. Whatever your default task is, we'll have that depend on example in this case with no function. Whatever your default is, is what Jake will run if you provide no parameters. So there we go. So that's build automation. From here, you can do anything that you can do with node. And this is why I like Jake is because you can literally do anything. And the more complicated your project gets, the more specialized your needs get, which is and tools like grunt, I fear will start to break down. Now, I haven't used grunt so I can't say that that's actually true. But I like these sort of thinner tools because they give you more power. They give you more options. So any questions about automated builds before we move on to the next thing? Do I think it's good to actually check these tools in? Yeah, absolutely. So Jake, I will check in Jake to my source code repository. It's in the node modules directory. So by default, it'll just get checked in for you. Now NPM does have a few little flaws in that it also it puts all the sort of built stuff right next to the stuff that's downloaded. So if you're doing cross-box, cross-OS development, like if you have a Windows box and a Mac box, you have to be sure to exclude those binaries that NPM creates for you. But yeah, absolutely. I check everything in because the NPM servers are going to go down or you're going to be on an airplane or something else will happen. How many of you heard about what happened to the Ruby gem servers? What happened to them? Yeah, some people uploaded malware to the Ruby gem repository and then it went down for a couple of days as I recall. So I would rather not depend on that. Now of course I get stuff out of NPM to begin with but once I have it, it's in my computer and also or in my repository. And also if you're talking about a lifetime of 5, 10, 20 years, there's no guarantee that the NPM servers are going to be around in 15 years or that the specific version of the software you're looking for is going to be around. So check in the code you use. I've actually worked with teams that checked in virtual machines of their entire operating environment. That's a little paranoid. The only reason I don't do it is because they're gigabytes in size and Git doesn't like that. All right, any other questions about build automation? You might notice by the way I'm paranoid. Everything will go wrong when you're programming eventually. And so I do a lot of stuff that other people wouldn't so that those things won't go wrong. Would I check in test runners? Absolutely. In fact I'm going to demonstrate that later on. The question was would I use test runners from a make file? And the answer is absolutely. What I want is to be able to do everything that matters like that. So let's move on to the next thing. Static code analysis. JavaScript is famous for having some special characteristics. And Gary Bernstein, if you haven't seen it, I think everybody has at this point, has a brilliant talk called Watt. This is a picture from Watt. It's hilarious. Go watch it. If you haven't, if you've seen it, go watch it again. It's hilarious. And you know, you're going to do things like use double equals instead of triple equals. You're going to misspell your variables. You're going to leave out the bar keyword. You're going to forget to put in a semicolon so JavaScript will be helpful and put it in for you in the wrong place. This stuff happens in JavaScript. So it's very useful to do static code analysis, which is simply running a tool that looks at your source code without running it. Hence the static and analyze it for errors. Static code analysis. The most popular tool for this or the two most popular tools are JS Lint by Douglas Crockford and JS Hint, the little less curmudgeonly version of JS Lint that gives you more control over its options. So I like to use JS Hint. I'm going to go ahead and pull that up now. And to save us all a bunch of time, what I'm going to do is just skip ahead to the part of my repository that has that already installed. So now in my build, you can see that by default, I'm going to lint my code. Now, JS Hint works on raw source code. So I've written a little runner that takes a list of files and processes the files for you. All the source code that I'm sharing with you that I'm showing today is available on GitHub and I'll give you the URL at the end of the talk so you can get that lint runner at that time. So the way this works is you pass JS Hint a list of options telling it what you want to check for and it will check for those things. So now if I build my code, it will say your jake file has no errors that I can find. Your build's okay. If I come along and let's say I've forgotten the var keyword here, it would tell me, well, actually it doesn't like my spaces and tabs, but it would also tell me that pass does not define. I took out the var keyword. So use lint, it will save you, if you're doing an automated build using lint as trivial, if you're not doing an automated build, then it's kind of like, I don't want to go to the JS Hint site every time and paste my code. So this is part of why builds are useful. Once you get in the habit, it's really just code, code, code, tab, up arrow, enter, tab. If you're more ambitious than I am, you can probably get your IDE to run it for you, but this is fast enough for me. Any questions about linting before I move on? This is pretty basic stuff. All right. Next, continuous integration. When you're working on a team, it's important to synchronize your work now and again. If you don't, then Paula will use start, will write a function called start, and Pradeep will call a function called go, and when their code comes together, it won't work. Now, this bringing everything together is called integration. You all know this. Big projects, especially in the 90s, would put off integration until the end of the project. This was considered best practice. I'm not sure why. And then, even though everything was very carefully documented, it's, of course, not possible to document absolutely everything perfectly, and so things wouldn't fit together right. Maybe the functions would have the right name, but the behavior would be a little different than people expected, et cetera. So those projects could take weeks or even months to integrate. How many of you have been on a project like that? Not very many anymore. I have. I was on a project that had, it was just 10 developers, but each, or maybe it was eight, anyway, one or two developers were working on each of the distributed machines. We were using Corba. And we got all the way up to the end. This was a project to build a robotics control tool for Intel. If we didn't sell it to Intel, we were going out of business as a company. So that was the goal. We got the point where we were about to take it to Intel to do a demo. So we said, hey, let's have all these different machines that are supposed to talk over the network. Let's make sure they work together. We turned it all on for the very first time. Nothing happened. Well, sparks flew, but other than that, nothing worked. That company no longer makes robotics control software. So yeah, so extreme programming introduced the idea of continuous integration. You've probably heard about continuous integration. Good chances are good that everything you've heard about continuous integration is wrong. This is kind of a hobby horse of mine. So if you ask questions on this, I'll happily spend the next 36 minutes talking about it. So just warning you. Continuous integration is really two things. First, integrate frequently so we don't get to the point where we try to integrate and it takes forever to integrate, to get it to work. Second, when you have integrated your code, the code must work. It's pretty straightforward. You may have heard of something called a continuous integration tool, Jenkins, I don't know what's popular these days. Anybody? Travis? Team city? Yes. But that's not continuous integration. This right here is continuous integration. Integrate frequently. Make sure your integrated code is known good. That's it. Most tools fail on both counts. They don't require you to integrate at any particular interval. And when you do an integration, they don't make sure it's known good. What they do is tell you, oops, you screwed up. The integration didn't work. But that's not continuous integration. Continuous integration is integrating code that's good. This is really easy to do. It's really easy to do. All those tools do. They have a whole lot of management to tell you about all the times the build broke and to inform people and they run the build for you. But running the build is not hard. Just change over to the box that you want to run the build on and run the build. Takes about five seconds. And I guarantee you that if you're maintaining a continuous integration server, you've spent about 30 years worth of running the build manually in time of setting it up, not to mention maintaining it. If you want to do continuous integration without a tool, there's just a few things you need to do. First, integrate locally. So pull the code. Make sure it all works together. Then push it to the integration machine, where some independent machine that you're going to use to make sure the code works on everybody's machine, not just yours. And then make sure the build works. And then promote it to the integration branch. Most integration tools get this backwards. They promote it to the integration branch and then they say, did it work? All you have to do is switch the order of that and you're guaranteed never to have a broken build. It's just that simple. And I'm flabbergasted that tools do it in the other way. Of course, if you have a system where it's literally impossible to have a broken build failing, I mean, we're programmers, we're people, something always goes wrong, right? But if you have a system like this where it's very difficult to have a broken build, I guess there would be no need for all the emails and the charts and everything else that the tools give you. So maybe that's why they don't bother. Anyway, here's one way to do it using Git. You can have multiple branches, branch for each development box. You pull the code down when you are ready to integrate, merge it in with a code that you've written locally. Make sure that works and then push it back to the integration branch, integration box in a private branch. Test it there, merge it, and there you go. Any questions about this? I've already ranted probably longer than I should. One person's left. You on this side on the other hand are trapped. There is no door over there. And I know that most of you are not going to want to walk in front of all these people. So thank you for sitting over there. Also, if there's a fire, sorry. All right. Any questions about continuous integration before I move on? This one usually causes some complaints. So either you're nice or you're tired of hearing me talk about it. One of the two. Going, going, good. All right. We all know how much trouble cross-browser compatibility can be. Opera is being held up. It's for opera. I work with Opera. I worked on a project so that my first really significant JavaScript project was called Ruboo. It was a tool for doing prediction on agile teams about when things would be done. And I programmed, it was heavy JavaScript. It involved SVG and so forth. It's sort of a single-page app. And I thought to myself, I've got to get a minimum viable product out the door. I need to try to figure out how to make money building this thing. I'm going to not worry about IE8 right now. And so I built it. I used test-driven development. I did, you know, pretty thorough testing on it. And then when the time came to try to get IE8 support in, the test didn't work. And it was a big deal to try to fix it. So I never did get IE8 support in there. So I guess the moral of the story is this. If you want to support a particular browser, do it from the beginning. It's easier to fix the little things that come along than try to fix all the problems that happen at once. It's sort of similar to integration. It's easier to solve these things as you go than to save them all up for the end and then spend a couple of weeks or months trying to fix it. So test against all the browsers all the time. That's the best way to save yourself time in trouble. My tool of choice is called Karma. It used to be called Testacular. You can see why they changed the name. It was only just in the last couple of months that they did that. How many of you have used Karma or Testacular? Well, the rest of you are in for a treat. This thing is awesome. Let's see if I can bring it up. Let's go ahead and reset things here. Okay. So I've moved the code forward to a point where Karma is installed. You asked me about, somebody over here asked me about checking in test runners. Yes. Here it is. It's checked in. What Karma allows you to do is it runs a little web server on your box, which I'm going to start by typing Jake Karma. And once you've started that server, you take your browser, any browser, and you point it at the Karma server. That captures the browser. So now when you want to run your tests, Karma will take your tests, ship them down to the browser you've captured, and run them on that machine or on that browser. So now if I run Jake, and if everything's working, it will run all my tests in Firefox from the command line. Let me show you what's going on here. Here's my build. You can see I've got the Karma server running here. If you want the details, again, the source code will be available on GitHub. To actually run the tests, I, well, I call the run test function, which again you can find on GitHub. The actual tests are using Mocha. So what I'm doing here is I'm describing an example. I'm saying it should pass, and I'm expecting true to be equal to true. If I change this to false, and I run the tests again, now I'll get an error message. Pretty straightforward. The cool thing is that you can capture any number of browsers with Karma. So I've got Firefox running, but let's also run Safari. So bring this up, point it at Karma. They didn't change the title yet. Run Jake. Now that test is running on two browsers. It's failing both times. Let's go and make that pass. There we go. Now it's running on two browsers. But of course I want to support Chrome. So I'll bring up Chrome. There we go. Now it's running on three browsers. But you know what? That's not good enough. We need Internet Explorer to work. So I'm going to pull up IE8 in a VM because you can point anything on your network at the Karma server. I'm going to pull up IE8 in a VM, point it at JamesShore.local, which is the name of the host OS that's running Karma. And now, come on IE, you can do it. This was working. There we go. Live code. Always dangerous. So now I just ran all my tests against all four browsers. But of course I want to support IE9. So I'm going to bring that up in another VM. SSDs are awesome, by the way. Well, come on. So now we can point this at JamesShore.local. And there we go. Now we're running against five browsers with one button and a fraction of a second. Of course I want to run against IE10 as well. I think you get the idea, but this is so cool. I'm going to do it anyway. All right. IE10, please work. Microsoft. There we go. Come on. I don't know. There we go. I might be running a few too many VMs for this poor computer. There we go. Now we're running six browsers simultaneously. But of course we want to support mobile browsers, right? So let's run the iOS simulator as well. There we go. Karma's run. It's got Karma captured. And there we go. IE8, IE9, IE10, Safari, Chrome, Firefox, and Safari on iOS. All tested. One command. That's Karma. I love Karma. Also, this will work with real hardware. I've run my iPad against this over my local network as well. Any questions about this? Can you start the VMs on a build server? Well, keep in mind I don't use build servers. I think they're a waste of time. And one of the reasons they're a waste of time is that launching these VMs manually is really easy. Figuring out how to launch them on an automated build server is going to take me way more time than I'll ever save by having a build server run builds for me. So, no, I haven't figured that out. On the previous project, I did try to launch everything automatically. And actually one of the reasons I didn't support IE8 was because I couldn't figure out how to get IE8 to start for me. So, I haven't, I'm sure it's possible. Karma will launch browsers for you, but it won't launch IE for you if it's not installed on the OS. Can you run Karma in a headless scripted manner? You could, you can point any browser at it, including something like PhantomJS, if you wanted to. However, the advantage of Karma is that you're running against the actual browsers that your users are going to use in production. So, that is much more powerful than, than running a headless browser. Now, I do use headless browsers for certain types of smoke testing, where I want to make sure that not just my unit tests work, but the actual entire application is wired up correctly, HTML and everything. I use PhantomJS for that, but I don't use Karma with it. I just, I just use PhantomJS directly. Did that answer your question? Okay. Any other questions or comments regarding cross browser testing? Okay. Let's, there is one more thing I want to show you. And that is, if you've got a nice build script, which we're starting to get there, you can also set this up so that it looks at the output of Karma and checks to see if you're running the browsers you think you are. So, I'm just going to go ahead and jump ahead. Actually, no. I think I've got it here. Let's, let's hope I didn't just break everything. It still works. Good. All right. So, up here at the top, I've got this required, required browsers array. So, now what I can do, it's a little out of date, is I've got this coded so that it will check the output of Karma every time I run the build and make sure that those browsers are listed. So, if I were to come along and quit, say, Chrome and then run my build, it will tell me, hey, you didn't test Chrome. It's, that's the sort of thing that an automated build will give you because we all make dumb mistakes like that, right? We're working away and we forgot to run IE or something like that. It looks like everything's good. Two hours later, we realize our mistake and we've got some annoying debugging to do. So, the question is, does Karma depend on a particular framework? I'm using Mocha, but Karma is test framework agnostic. It has built-in support for Mocha and Jasmine, last time I checked, and supposedly you can write a bridge for any test framework you like. Fifth thing that I use to prevent my code from killing my company is front-end test-driven development. The best way I know of, so, of course, if you want to be able to refactor your code, you need to be able to run tests against it, and otherwise, every time you make a change, you're going to be testing it manually. The best way I know of to get automated tests is to use test-driven development. Now, if you are successful creating tests using some other technique, more power to you, but I have a lot of luck with test-driven development. The challenge with that, of course, is that in JavaScript, a lot of your code is really UI heavy, and most of these people who talk about test-driven development like to use examples like the bowling game or stuff that is not about something that does a lot of UI work and talks to a remote server and hardly does anything else in the middle. So, you need UI-specific test patterns. Now, I'm going to be discussing this more tomorrow. At 1340, in room two, I have a session called Test-Driven JavaScript Master Class. I'll be going into much more detail on front-end test-driven development with JavaScript, but today I want to go into just sort of the overview of it. Briefly, what we're going to do is we are going to test drive this code, so this simple validation form. The way we're going to do that is we're going to have our unit tests simulate some events that will stimulate the production code, and then we'll inspect the DOM to see that the right thing came out. Now, there are other ways of doing testing as well, and I'll be talking about those tomorrow, but we're just going to take that as given for this session. Now, test-driven development, how many of you are familiar with test-driven development? Almost everybody. Awesome. So, as a really quick recap, one thing that I do see people make a mistake on, sometimes people think test-driven development means write a whole bunch of tests and then write the code to make those tests pass, and if that's the way you do it, there is a more fun way of doing test-driven development than that, because that's kind of painful, because once you've written all those tests and get to writing the code, you realize your tests are wrong, and that is taking really small test steps. Test-driven development done well involves very small steps. First step is to think about what you want your code to do next, and then think of what test you could write that would fail until you've written that code. That's the hard part. Then you write that test, and you see it fail. That's a red bar. You write the production code, see it pass. That's a green bar, and then you make your code better. Even a little tiny bit as good as you know how today, and if you can't think of any way to make it better, that's fine too. And you keep doing that until the code is as good as you know how to make it, and then you move on. I see somebody yawning over here, so I will move on, sir. You've all heard about this. Okay, arrange, act, assert. This is how you'll typically do it. In JavaScript, you often need to reset. This is the pirate pattern of test-driven development. Yarrr, matey. Come on, you all want to say it. Yarrr. So what you're going to do is create DOM elements, run your production code, check the DOM elements, and then erase them. You need to take them out of your document body. Otherwise, bad things will happen. So here's what we have. How many of you, I don't know who's in the audience, so how many of you are pretty familiar with DOMs already? About two-thirds. Well, for those of you, this will be very much a refresher. For some of you, this might be useful. So we've got this page that we're going to be testing. We want to check the submit button. Internally in the browser, this is arranged in what's called the document object model, which is a whole bunch of objects arranged in a tree structure. Every node, every HTML tag in your document has an object, and they are arranged according to their hierarchy. So what we want to do is we want to take action when somebody clicks the submit link down in our A tag in that DOM. The way we do that is we do element.addEventListener. We can say what kind of event we want to listen for, in this case, click, and then we pass in a function, and the browser will attach that function to the DOM. So when an event occurs, say the user clicks the submit button, then the browser starts doing this whole event thing. That's terribly complicated. First, there's three phases, and then an extra step. First phase, capture phase, it will go through and run all the event handlers that have been assigned, that have been registered on all the elements that are apparent of the one that's been clicked, if they've been registered to run during the capture phase. Then it goes into the target phase. It runs the event handlers on the element itself, so it will run the one we pass in. Then it goes into the bubble phase, where it will do the same thing in reverse. It will run all the event handlers that have been registered to run in the bubble phase. Then finally, the browser will take its default action. That is completely dependent on the browser, but you can typically expect that clicking a link will cause the browser to go to a new URL, unless it's IE, and then who knows. In summary, there's two things. Event handlers execute in three phases, capturing phase, target phase, bubbling phase, and then the default action occurs. You can cancel the event handling by calling event.stoppropagation. You can cancel the default action by calling event.preventdefault. This matters because what we want to do is when that link is clicked, if the field is empty, that is, if it's not valid, we want to stop the browser from following the link. We'll be using this event.preventdefault. So how do we test this code? Let's do it. You, sir, who was yawning. What should we do next? Yeah, we need to, well, so we need to, yeah, the test-driven development cycle is think about what we want to do next and then write a test that will force us to write that code. So we need to think of something really small that we'll need to do next. What I'm going to do is I'm going to, I'm sorry for putting you on the spot. I'll buy you a beer afterwards. I'm going to go ahead and fast forward just a little bit. But not very much. And what I've done is I've just brought in the example HTML. So now we can see the page actually appear in the browser. But if we don't put anything in here, it's supposed to turn pink, but we haven't gotten any code yet, so it goes to the destination page. So that's what we need to fix. Let me just make sure everything's still working. Yeah, good. So what I've got in the page is I've got it set up so that on load it will set up the validation. So there's a function here called example.initializeValidation. That function does not exist yet. So my very first step, remember, very small steps in test-driven development. My very first step would be to write a test that fails until we write a do-nothing initializeValidation function. And that should be pretty easy. All we need to do is run example.initializeValidation like that. Did I do that right? Looks like it. There it fails. See, it says example is undefined. So I'm going to go in and I'll need to make the example JavaScript code. And we'll set up our global namespace. You could use something like requirejs or browserify. And I do, but that is more complicated than I wanted to get into for this talk. And it doesn't like the white space. Let's try this again. Now it's saying it can't find the property or method initializeValidation. So we're on the right track here. So I'll write initializeValidation. Again, do nothing function. That should pass if I did it right. Did I do it right? No. Example is not defined. Actually, no, in this case, it's just a linter complaining. I need to tell it that example is a legal global variable. There we go. Okay. What's next? We've got the validation initializing, but obviously it's not doing anything. Where should we take it from here? Anybody? Anybody? Bueller? No. All right. It's my fault. I called you out and now nobody's willing to talk. Well, what I'm going to do next is I'm going to make sure that we want this field to turn pink. Now, one thing we could do is we could write our code to actually change the background color of the field, but that would be coupling our JavaScript code to our view. So what I'm going to do instead is have it set a CSS class that we can set to any color we want in our CSS. So I'm going to say that we set, that it sets a CSS class when the field is empty. Now, that does mean that we're, oops, we'll need that back. We have to do a bunch of stuff here. Before we get started, we're going to need to set up our DOM because by default, Karma doesn't have anything in the DOM. So the way I'm going to do that is I'm going to add the fields, and I really hope I do this right. I'm counting on you guys telling me if I make a mistake here. Okay. Did I do that right? What did you say? I think that's right, isn't it? Before and, what did you say? Type for input. Thank you. Well, I really should do it properly, shouldn't I? It doesn't work. See, this is why I need to do this stuff because I make mistakes all the time. Yeah, I think the input tag is just a regular tag, right? Right. That's what I mean. It's just a standalone tag. Well, let's see what happens here. All right. So now we need to get our text field set up. We're going to pull that out of our document. I have to admit normally I'd use jQuery, but I wanted to try to make things simpler here. So we'll pull out our text field, and we'll pull out our submit link. And we'll need to declare both of these variables. So there we've arranged our test. We are acting here. We need to assert, and we need to reset. So after each, we're going to remove those values. Remove child, text field, and submit link. All right. Will this work? I have no idea. Let's run the test and find out. Hey, there it goes. And I'm going to tell it, oh, and it's complaining about Firefox and Chrome. Let me go fix my version numbers here. What I want to see happen is it say OK down at the bottom. Still complaining about Chrome not being tested. See, this is why it's useful to have the required browser in there. I had completely forgotten that I turned off Chrome. There we go. Now everything's working. Okay. So our test appears to be working. So we need to set the CSS class field when the field is empty. Let's go ahead and put this initialized validation up here in our before each to basically our setup function. How can I check to see that it sets the CSS field or class when we click the link and the field is empty? Come on. Have more beer. Sorry? Class name? Well, I'm not sure what you meant by that. I think what we need to do here is click the link and then check that the text field has the class we're looking for. So expect the class attribute on that text field to equal some value, which I'm going to actually just hard code to example dash required. So example and I'm going to put that in a constant like that. So this should fail saying it got no but expected example required. So to make this pass, I'm going to do something simple and dumb. I'm just going to set the value of that class. So I'll say sex field dot set attribute. Am I doing that right? Yeah. Set attribute class to example dot required field class like that. Oh, and we'll need to pass those in. So pass it in there and pass it in here. There we go. Is everybody still with me? When there's silence like this, I can't tell if I'm going too slow or if I'm going too fast. So everybody good? You'll ask questions if you're lost, right? You'll shout go faster if I'm going too slow, right? You'll say awesome if things are just right. Cool. Okay. Now we're getting in somewhere. Okay. So we've got our first test. It sets the CSS class when the field is empty. This is obviously wrong because it's going to set that class every time no matter what. So that's the first loop around the cycle or rather the second time around the test driven development cycle. So now we're going to say does not set or it clears CSS class when field is not empty. Actually, let's just say it does not set it. I'm not going to try to make this terribly complete and correct. It's just an example. So what we're going to do here is we're going to say text field dot value. We're going to set it to something that is not empty. We're going to click that link and then we're going to expect that class to be no. That should fail and it does. It says a expected example required to equal no. We can fix that by saying if text field dot value is truthy. In other words, if there's nothing there, we'll set the attribute. Did I get that backwards? No. What am I doing wrong here? Honestly, I can't see it so I need your help. It says expected example required to equal no and it's not. Checking for the value. Well, we don't have a whole lot of time left. So what I'm going to do is I'm just going to jump back to the other code I've already prepared. Tell you what, I hate to do it but I'm just going to jump straight to the conclusion. Did you remember to put an actual value in the box? I think I did. But now, live coding, two and a half minutes left. I've screwed everything up. Let me see if I can get it back to working again. There we go. Okay. So here is the final bit of code. I wish we had time for me to step through it piece by piece but I think you got the idea. What we have now is that we check to see that it follows the link when the field is not empty, doesn't follow the link when the field is empty and it sets the CSS class when the field is empty and we can make sure this works on every browser by running our build except it doesn't work on IE8 because IE8 doesn't have support for the add event listener method we're using in this code. So that tells us if we were doing this as we go, we see that right away we can either choose to exclude IE8 entirely or use something like jQuery that abstracts out those details for us. So to wrap up because we're out of time I'm afraid. Professional JavaScript development involves building for the long term. In other words, what you're going to be doing here is prioritizing maintenance, maintenance ability over ease of initial build. That means doing stuff that takes a little bit of time and after set up like an automated build, like linting, like test room development, like karma. So what I've shown here is a simple example with the submit link. You can find this code at the link I'm about to share with you. There's five techniques here. Automated build, JS hint, continuous integration, automated cross browser tests, and front end test driven development. I'm sorry we didn't have more time to go into more detail on the test driven development example. We will go into more detail tomorrow in the test driven JavaScript master class and I'm happy to answer any questions people might have afterwards. One last thing, you might have heard people tell you, well we don't have time to do testing, we haven't gotten around to it yet. Well I have here a whole bunch of round to it. So when somebody on your team tells you, I haven't gotten around to it yet, you can say, here you go. Oops, that was aimed at you. Just as thanks for letting me pick on you. So come on up and pick up some round to it. I've got hundreds of them and give them away to your colleagues. Everybody who says, I haven't got time to do that, I can't get around to it right now, you say, well here you go. Here's a round to it. Thank you. All right. Source code is on GitHub. You can find continuous integration source at the Automotopia repository. The test code I was sharing here at the LL10 repository. I have a screencast on test driven JavaScript where we cover all this and much more. It is the most in depth screencast you'll find on this material. And there's a coupon for 20% off the first month while you're at the conference. So if you sign up in the next couple of days, use this coupon, NDC Oslo 2013. Happy to talk to you further. Thank you for coming. Yes, please take however many you want.
 All right. Is the sound on? Can you hear me? Okay, let's get started.  The question I want to talk about today, the question I want to try to start to answer  today is how can you stop your code from killing your company? That's kind of an important  question, don't you think? We've all experienced other people's code that was in the process  of killing our dreams and hopes and wishes, if not the company. So what I want to do in  this session is talk about what you can do with your code, if not to save your company,  at least to prevent you being the one to blame for a dime. It is, of course, true that technical  quality has nothing at all to do with business success. Good business is what makes a company.  However, what I've seen is that over time, about five to nine years, bad code will kill  companies. Because what happens is in five to nine years, the code is so crifty and  so hard to work with that you can no longer maintain it. That even the most simple change,  like changing a little color or a font or moving things around by a few pixels, takes  three months. Anything more substantial is just impossible. How many of you have been  in that kind of situation? Maybe different details, but yeah, a lot of us have been there  and I think half of you are afraid to raise your hands in public. It's not fun. So what  happens, the company decides to rewrite and rewrites kill companies because rewrites mean  that you're spending all your time and effort and valuable energy reproducing what's already  been done. So meanwhile, you're falling behind the competition, your customers are getting  angry and you can't ship anything until you've reproduced absolutely everything that came  before, which is always more than you thought it was when you started. That's what we call  bug for bug compatibility, right? It's not really done until it breaks just like the  old one did. So bad technology breaks the company and that's what I want to talk about  today. This is my definition of good design. Good design minimizes the time required to  create, modify, and maintain the software while achieving its business goals. And in  software development, most software development, we spend far more time in maintenance and  modification than we do in initial creation. So a professional developer, one who doesn't  wish to kill his company, you know, the kind that hasn't burned out yet, a professional  developer is going to want to focus on minimizing the cost of maintenance because we spend so  much more time in maintenance than we do in initial build. Now, a lot of the popular  stuff you see online, you know, the GWIS frameworks and, you know, you can produce a to-do list  in just five lines of code. Don't ask if I would have anything more complicated. But  to-do list, yes, we can do that. They all focus on how can we slam out code quickly.  But I think that's a mistake. I think what really matters is what it takes to maintain  code for three years, five years, 10 years, 20 years. There's code out there that's been  alive for 20 years. Someday, somebody is going to have the perhaps misfortune of maintaining  code you wrote 20 years from now in JavaScript, no less.  All right. So what causes designs to get worse? Anybody? Anybody? Time. Typing. You type on  the keyboard, you make your design worse because the fact is, is you can't predict everything.  So even if you made a perfect design for today, which most of us can't, and I include myself  in that category, I don't even try anymore. Even if you made a perfect design for today,  something's going to happen next month, next year, that's going to make that design inappropriate  for the situation. So typing on the keyboard and time actually creates technical debt.  The only way to fix that technical debt is to improve the design of your code. The way  to do that is through refactoring. Refactoring is a necessary step to prevent your code from  killing your company. So my name is James Shore. I'm a programmer.  I've been doing agile software development since 1999 and extreme programming since 2000.  And you can tell by that phrase, I used to do extreme programming that I am an XP bigot  because I had a lot of fun with it and a lot of success. So I'm very much into test-driven  development and continuous design and refactoring and continuous integration and all that stuff.  And a couple of years ago, I got involved with JavaScript. I wanted to do some significant  work in JavaScript, not just the sort of fiddly stuff that I've done before. And of course,  the way I wanted to do that was applying the XP, the Agile Engineering Practices of Test-Driven  Development and so forth. And that I learned, you know, I had to figure out some stuff.  I had to learn about stuff that other people had done. And last year, I did a Kickstarter  about that experience, about test-driven development. And I have a screencast called  Let's Code JavaScript where I talk about doing test-driven development and other rigorous  practices. So that screencast in this talk is about rigorous professional JavaScript  that is creating software that prioritizes maintainability over ease of initial creation.  I'm going to use a very simple example to illustrate this concept. You could probably  all do this in your sleep, but could you do it in a way that was maintainable? That's  the question. So what this is is a classic validation form. You press the submit button.  I want that text field to turn pink. You put something in it. The text field press the  submit button. It takes you to the next page. So how would you program this in a way that  was maintainable, that was easier to maintain than it was to originally build? That's what  we're going to talk about today. Please ask questions throughout. Don't plan on stopping  at the end, but I do plan on stopping anytime I see somebody have a hand raised. I have  a fair amount of material to cover here, and some of it I will probably just zip right  through if nobody stops me. So don't hesitate to ask questions. You've all had beer, and  if you hadn't, it's available. So that should help make it easier to ask questions, I hope.  I've been in Finland. It's worse there. All right. The other thing is there's live coding  ahead. That means two things. One, something is going to go terribly, terribly wrong, and  I'm going to look dumb on stage. I'm used to that. I do a screencast. And second, I'm  going to miss stuff. I'm going to type something, and it's just not going to work despite my  notes and everything else. So I'm really going to rely on you to tell me when I make a mistake.  So consider this pair programming with 70 people. All right. So we want to enable refactoring.  We want to prioritize maintainability over initial creation. There's lots of ways to  do this. I'm going to talk about five techniques in today's session. A build automation, static  code analysis, continuous integrate. Well, you can read. First, build automation. On  any significant project I do, I have a goal to be able to check the code out from source  control. You all use source control, right? Yes. Yes. Yes. Good. Check the source code  out from source control. Disconnect the network cable. Press build and see it work. This is  very important because there are way too many projects out there. I would say the vast majority  of projects out there where if you were to go away for a year and come back, nobody would  be able to build the code. Nobody would be able to ship the code. It would be dead, dead  and gone. There is a sort of sad example of that recently. There is a popular indie game  called Haunts. Has anybody heard of this? It was kick started for $28,000, which isn't  really enough for an indie game, I don't think. The person who did the kick starter  wasn't a programmer, so he hired a programmer. Inevitably ran out of money because $28,000  is not enough to develop a game. But people in the community said, oh, we'll take over  the project. We really like this project and we want it to succeed and some of us are programmers.  We'll take the code and finish the game. So he took the source code and put it up on GitHub  and nobody could compile it. And that's because the code had been checked in, but all the  dependencies hadn't been checked in. Those were supposed to come down over the network,  but this was in go and the way the programmer wrote it, I don't think this is Go's fault,  the way the programmer wrote it is that it would pull down the latest version of the  libraries when it pulled down the code. And those latest versions weren't compatible.  And last I heard this game, even though people wanted to develop it, they couldn't even  get it to compile. This happens in big companies, too. Huge amounts of investment and money  into a project. People go away for a little while, they come back, they can't build the  project. I'm sure some of you have experienced it, although maybe not to that degree. How  many of you have come into it as a new employee into a company, sat down at your workstation  and then spent a couple of weeks or at least a couple of days trying to figure out how  to get the stuff working? Wouldn't it be nice if you could check out the code, disconnect  the network cable and have everything work? That would be awesome. So that's my first  step. Whenever I start a new project, I want some sort of automation in place so that I  can build and eventually deploy the app with one button, maybe two.  My tool of choice for that is Jake in JavaScript. There is another tool called Grunt. How many  of you are using Grunt right now? Just a handful. How many of you are using Jake? Less. Yeah,  that's sort of the story of my life. I like Jake because it is basically just JavaScript  with a thin wrapper over it. Grunt is more configuration-y in that it's more declarative.  It's not just JavaScript. I haven't actually used it, though, so if one of you who's used  it wants to correct me, feel free. Anyway, now for the live coding part. We're going  to install Jake and see if we can get this working. So I'm going to pull this down off  of NPM. Maybe. How many of you have used Node? Node.js. Node.js is a pretty neat language  or not language. Pretty neat tool kit framework, engine plus framework. And it's good for a  lot of things other than writing IRC servers. You can also write command line tools with  it. And it has a very nice package manager, NPM, which stands for not a package manager  or I'm not called a package manager, something like that. Anyway, NPM is a way of downloading  all kinds of stuff, including Jake. So once you install Node, if you type in NPM install  Jake, it will put Jake locally in a Node modules directory. Can everybody see this  okay? Okay. It's kind of an odd room. All right. So now that we have Node, now it wants  you to install it globally. I hate installing stuff globally because if I install it globally,  it's not in my repository. If it's not in my repository, I can't check it in. If I can't  check it in, then when you get it out, the code won't work. And we want the code to work,  right? So instead, I'm going to write myself a little shell script that is going to run  Jake for me. So now if you were to check out this code and type Jake, you would see the  exact same thing on your screen that I do. Assuming you install Node. All right. So it's  saying that I didn't have a Jake file. So let's make a Jake file. Okay. So the way this  works is Jake is a build tool. It's very similar to rake, if you're familiar with rake, which  is in turn sort of inspired by make the Unix build tool. And the way it works is you specify  tasks, for example, task example, and provide functions to that task. And when you run that  task from the command line, whatever you put in the function will run. It's very simple  and straightforward. So if I say Jake example, and I did this right, it should say hi. And  there it is. Right here at the bottom. You can also provide descriptions so that when  we run Jake dash t, it will list all the tasks. So you can see it says Jake example. This  is an example task. And the most importantly for a build tool, you can provide dependencies.  So if I put, and you can put in any number, that's an array. If I put dependencies here,  it will run, let's see, it will run the dependency task before it runs the example task, which  is useful for all sorts of things, like saying, well, before we deploy, we want to run our  tests and we want to link our code, for example. And it will only run each dependency once.  So here if I type Jake example, it should say I'm a dependency and then hi, which it  did. You're going to notice me do that a lot. I'll say it should do this because I've worked  with computers long enough to know that half the time the computer wants to beat me upside  the head and say, no, that's not going to happen. So I'm always predicting what's going  to happen so that when I'm wrong, I know to stop and look and figure out what's going  on. There's one more thing you can do with Jake and that is provide a default task. Whatever  your default task is, we'll have that depend on example in this case with no function.  Whatever your default is, is what Jake will run if you provide no parameters. So there  we go. So that's build automation. From here, you can do anything that you can do with node.  And this is why I like Jake is because you can literally do anything. And the more complicated  your project gets, the more specialized your needs get, which is and tools like grunt,  I fear will start to break down. Now, I haven't used grunt so I can't say that that's actually  true. But I like these sort of thinner tools because they give you more power. They give  you more options. So any questions about automated builds before we move on to the next thing?  Do I think it's good to actually check these tools in? Yeah, absolutely. So Jake, I will  check in Jake to my source code repository. It's in the node modules directory. So by  default, it'll just get checked in for you. Now NPM does have a few little flaws in that  it also it puts all the sort of built stuff right next to the stuff that's downloaded.  So if you're doing cross-box, cross-OS development, like if you have a Windows box and a Mac box,  you have to be sure to exclude those binaries that NPM creates for you. But yeah, absolutely.  I check everything in because the NPM servers are going to go down or you're going to be  on an airplane or something else will happen. How many of you heard about what happened to  the Ruby gem servers? What happened to them? Yeah, some people uploaded malware to the  Ruby gem repository and then it went down for a couple of days as I recall. So I would  rather not depend on that. Now of course I get stuff out of NPM to begin with but once  I have it, it's in my computer and also or in my repository. And also if you're talking  about a lifetime of 5, 10, 20 years, there's no guarantee that the NPM servers are going  to be around in 15 years or that the specific version of the software you're looking for  is going to be around. So check in the code you use. I've actually worked with teams that  checked in virtual machines of their entire operating environment. That's a little paranoid.  The only reason I don't do it is because they're gigabytes in size and Git doesn't like that.  All right, any other questions about build automation? You might notice by the way I'm  paranoid. Everything will go wrong when you're programming eventually. And so I do a lot of  stuff that other people wouldn't so that those things won't go wrong. Would I check in test  runners? Absolutely. In fact I'm going to demonstrate that later on. The question was  would I use test runners from a make file? And the answer is absolutely. What I want is  to be able to do everything that matters like that. So let's move on to the next thing.  Static code analysis. JavaScript is famous for having some special characteristics. And  Gary Bernstein, if you haven't seen it, I think everybody has at this point, has a brilliant  talk called Watt. This is a picture from Watt. It's hilarious. Go watch it. If you haven't,  if you've seen it, go watch it again. It's hilarious. And you know, you're going to do  things like use double equals instead of triple equals. You're going to misspell your variables.  You're going to leave out the bar keyword. You're going to forget to put in a semicolon  so JavaScript will be helpful and put it in for you in the wrong place. This stuff happens  in JavaScript. So it's very useful to do static code analysis, which is simply running a tool  that looks at your source code without running it. Hence the static and analyze it for errors.  Static code analysis. The most popular tool for this or the two most popular tools are  JS Lint by Douglas Crockford and JS Hint, the little less curmudgeonly version of JS  Lint that gives you more control over its options. So I like to use JS Hint. I'm going  to go ahead and pull that up now. And to save us all a bunch of time, what I'm going to  do is just skip ahead to the part of my repository that has that already installed.  So now in my build, you can see that by default, I'm going to lint my code. Now, JS Hint works  on raw source code. So I've written a little runner that takes a list of files and processes  the files for you. All the source code that I'm sharing with you that I'm showing today  is available on GitHub and I'll give you the URL at the end of the talk so you can get  that lint runner at that time. So the way this works is you pass JS Hint a list of options  telling it what you want to check for and it will check for those things. So now if I build  my code, it will say your jake file has no errors that I can find. Your build's okay.  If I come along and let's say I've forgotten the var keyword here, it would tell me, well,  actually it doesn't like my spaces and tabs, but it would also tell me that pass does not  define. I took out the var keyword. So use lint, it will save you, if you're doing an  automated build using lint as trivial, if you're not doing an automated build, then it's  kind of like, I don't want to go to the JS Hint site every time and paste my code. So  this is part of why builds are useful. Once you get in the habit, it's really just code,  code, code, tab, up arrow, enter, tab. If you're more ambitious than I am, you can probably  get your IDE to run it for you, but this is fast enough for me. Any questions about linting  before I move on? This is pretty basic stuff. All right. Next, continuous integration. When  you're working on a team, it's important to synchronize your work now and again. If you  don't, then Paula will use start, will write a function called start, and Pradeep will  call a function called go, and when their code comes together, it won't work. Now, this  bringing everything together is called integration. You all know this. Big projects, especially  in the 90s, would put off integration until the end of the project. This was considered  best practice. I'm not sure why. And then, even though everything was very carefully  documented, it's, of course, not possible to document absolutely everything perfectly,  and so things wouldn't fit together right. Maybe the functions would have the right name,  but the behavior would be a little different than people expected, et cetera. So those projects  could take weeks or even months to integrate. How many of you have been on a project like  that? Not very many anymore. I have. I was on a project that had, it was just 10 developers,  but each, or maybe it was eight, anyway, one or two developers were working on each of  the distributed machines. We were using Corba. And we got all the way up to the end. This  was a project to build a robotics control tool for Intel. If we didn't sell it to Intel,  we were going out of business as a company. So that was the goal. We got the point where  we were about to take it to Intel to do a demo. So we said, hey, let's have all these  different machines that are supposed to talk over the network. Let's make sure they work  together. We turned it all on for the very first time. Nothing happened. Well, sparks  flew, but other than that, nothing worked. That company no longer makes robotics control  software. So yeah, so extreme programming introduced the idea of continuous integration.  You've probably heard about continuous integration. Good chances are good that everything you've  heard about continuous integration is wrong. This is kind of a hobby horse of mine. So  if you ask questions on this, I'll happily spend the next 36 minutes talking about it.  So just warning you. Continuous integration is really two things. First, integrate frequently  so we don't get to the point where we try to integrate and it takes forever to integrate,  to get it to work. Second, when you have integrated your code, the code must work. It's pretty  straightforward. You may have heard of something called a continuous integration tool, Jenkins,  I don't know what's popular these days. Anybody? Travis? Team city? Yes. But that's not continuous  integration. This right here is continuous integration. Integrate frequently. Make sure  your integrated code is known good. That's it. Most tools fail on both counts. They don't  require you to integrate at any particular interval. And when you do an integration,  they don't make sure it's known good. What they do is tell you, oops, you screwed up.  The integration didn't work. But that's not continuous integration. Continuous integration  is integrating code that's good. This is really easy to do. It's really easy to do. All those  tools do. They have a whole lot of management to tell you about all the times the build  broke and to inform people and they run the build for you. But running the build is not  hard. Just change over to the box that you want to run the build on and run the build.  Takes about five seconds. And I guarantee you that if you're maintaining a continuous  integration server, you've spent about 30 years worth of running the build manually  in time of setting it up, not to mention maintaining it. If you want to do continuous integration  without a tool, there's just a few things you need to do. First, integrate locally. So  pull the code. Make sure it all works together. Then push it to the integration machine, where  some independent machine that you're going to use to make sure the code works on everybody's  machine, not just yours. And then make sure the build works. And then promote it to the  integration branch. Most integration tools get this backwards. They promote it to the  integration branch and then they say, did it work? All you have to do is switch the  order of that and you're guaranteed never to have a broken build. It's just that simple.  And I'm flabbergasted that tools do it in the other way. Of course, if you have a system  where it's literally impossible to have a broken build failing, I mean, we're programmers,  we're people, something always goes wrong, right? But if you have a system like this  where it's very difficult to have a broken build, I guess there would be no need for  all the emails and the charts and everything else that the tools give you. So maybe that's  why they don't bother. Anyway, here's one way to do it using Git. You can have multiple  branches, branch for each development box. You pull the code down when you are ready  to integrate, merge it in with a code that you've written locally. Make sure that works  and then push it back to the integration branch, integration box in a private branch. Test  it there, merge it, and there you go. Any questions about this? I've already ranted  probably longer than I should. One person's left. You on this side on the other hand are  trapped. There is no door over there. And I know that most of you are not going to want  to walk in front of all these people. So thank you for sitting over there. Also, if there's  a fire, sorry. All right. Any questions about continuous integration before I move on? This  one usually causes some complaints. So either you're nice or you're tired of hearing me  talk about it. One of the two. Going, going, good. All right. We all know how much trouble  cross-browser compatibility can be. Opera is being held up. It's for opera. I work with  Opera. I worked on a project so that my first really significant JavaScript project was  called Ruboo. It was a tool for doing prediction on agile teams about when things would be  done. And I programmed, it was heavy JavaScript. It involved SVG and so forth. It's sort of  a single-page app. And I thought to myself, I've got to get a minimum viable product out  the door. I need to try to figure out how to make money building this thing. I'm going  to not worry about IE8 right now. And so I built it. I used test-driven development.  I did, you know, pretty thorough testing on it. And then when the time came to try to  get IE8 support in, the test didn't work. And it was a big deal to try to fix it. So  I never did get IE8 support in there. So I guess the moral of the story is this. If  you want to support a particular browser, do it from the beginning. It's easier to fix  the little things that come along than try to fix all the problems that happen at once.  It's sort of similar to integration. It's easier to solve these things as you go than  to save them all up for the end and then spend a couple of weeks or months trying to fix  it. So test against all the browsers all the time. That's the best way to save yourself  time in trouble. My tool of choice is called Karma. It used to be called Testacular. You  can see why they changed the name. It was only just in the last couple of months that  they did that. How many of you have used Karma or Testacular? Well, the rest of you are in  for a treat. This thing is awesome. Let's see if I can bring it up. Let's go ahead and  reset things here. Okay. So I've moved the code forward to a point where Karma is installed.  You asked me about, somebody over here asked me about checking in test runners. Yes. Here  it is. It's checked in. What Karma allows you to do is it runs a little web server on your  box, which I'm going to start by typing Jake Karma. And once you've started that server,  you take your browser, any browser, and you point it at the Karma server. That captures  the browser. So now when you want to run your tests, Karma will take your tests, ship them  down to the browser you've captured, and run them on that machine or on that browser. So  now if I run Jake, and if everything's working, it will run all my tests in Firefox from the  command line. Let me show you what's going on here. Here's my build. You can see I've  got the Karma server running here. If you want the details, again, the source code will be  available on GitHub. To actually run the tests, I, well, I call the run test function, which  again you can find on GitHub. The actual tests are using Mocha. So what I'm doing here is  I'm describing an example. I'm saying it should pass, and I'm expecting true to be equal to  true. If I change this to false, and I run the tests again, now I'll get an error message.  Pretty straightforward. The cool thing is that you can capture any number of browsers  with Karma. So I've got Firefox running, but let's also run Safari. So bring this up, point  it at Karma. They didn't change the title yet. Run Jake. Now that test is running on two  browsers. It's failing both times. Let's go and make that pass. There we go. Now it's  running on two browsers. But of course I want to support Chrome. So I'll bring up Chrome.  There we go. Now it's running on three browsers. But you know what? That's not good enough.  We need Internet Explorer to work. So I'm going to pull up IE8 in a VM because you can point  anything on your network at the Karma server. I'm going to pull up IE8 in a VM, point it  at JamesShore.local, which is the name of the host OS that's running Karma. And now,  come on IE, you can do it. This was working. There we go. Live code. Always dangerous. So  now I just ran all my tests against all four browsers. But of course I want to support  IE9. So I'm going to bring that up in another VM. SSDs are awesome, by the way. Well, come  on. So now we can point this at JamesShore.local. And there we go. Now we're running against  five browsers with one button and a fraction of a second. Of course I want to run against  IE10 as well. I think you get the idea, but this is so cool. I'm going to do it anyway.  All right. IE10, please work. Microsoft. There we go. Come on. I don't know. There we go.  I might be running a few too many VMs for this poor computer. There we go. Now we're  running six browsers simultaneously. But of course we want to support mobile browsers,  right? So let's run the iOS simulator as well. There we go. Karma's run. It's got Karma  captured. And there we go. IE8, IE9, IE10, Safari, Chrome, Firefox, and Safari on iOS.  All tested. One command. That's Karma. I love Karma. Also, this will work with real hardware.  I've run my iPad against this over my local network as well. Any questions about this?  Can you start the VMs on a build server? Well, keep in mind I don't use build servers. I think  they're a waste of time. And one of the reasons they're a waste of time is that launching these  VMs manually is really easy. Figuring out how to launch them on an automated build server  is going to take me way more time than I'll ever save by having a build server run builds  for me. So, no, I haven't figured that out. On the previous project, I did try to launch  everything automatically. And actually one of the reasons I didn't support IE8 was because  I couldn't figure out how to get IE8 to start for me. So, I haven't, I'm sure it's possible.  Karma will launch browsers for you, but it won't launch IE for you if it's not installed  on the OS. Can you run Karma in a headless scripted manner? You could, you can point  any browser at it, including something like PhantomJS, if you wanted to. However, the advantage  of Karma is that you're running against the actual browsers that your users are going  to use in production. So, that is much more powerful than, than running a headless browser.  Now, I do use headless browsers for certain types of smoke testing, where I want to make  sure that not just my unit tests work, but the actual entire application is wired up  correctly, HTML and everything. I use PhantomJS for that, but I don't use Karma with it.  I just, I just use PhantomJS directly. Did that answer your question? Okay. Any other  questions or comments regarding cross browser testing? Okay. Let's, there is one more thing  I want to show you. And that is, if you've got a nice build script, which we're starting  to get there, you can also set this up so that it looks at the output of Karma and checks  to see if you're running the browsers you think you are. So, I'm just going to go ahead  and jump ahead. Actually, no. I think I've got it here. Let's, let's hope I didn't just  break everything. It still works. Good. All right. So, up here at the top, I've got this  required, required browsers array. So, now what I can do, it's a little out of date,  is I've got this coded so that it will check the output of Karma every time I run the build  and make sure that those browsers are listed. So, if I were to come along and quit, say,  Chrome and then run my build, it will tell me, hey, you didn't test Chrome. It's, that's  the sort of thing that an automated build will give you because we all make dumb mistakes  like that, right? We're working away and we forgot to run IE or something like that. It  looks like everything's good. Two hours later, we realize our mistake and we've got some  annoying debugging to do.  So, the question is, does Karma depend on a particular framework? I'm using Mocha, but  Karma is test framework agnostic. It has built-in support for Mocha and Jasmine, last time I  checked, and supposedly you can write a bridge for any test framework you like.  Fifth thing that I use to prevent my code from killing my company is front-end test-driven  development.  The best way I know of, so, of course, if you want to be able to refactor your code,  you need to be able to run tests against it, and otherwise, every time you make a change,  you're going to be testing it manually. The best way I know of to get automated tests  is to use test-driven development. Now, if you are successful creating tests using some  other technique, more power to you, but I have a lot of luck with test-driven development.  The challenge with that, of course, is that in JavaScript, a lot of your code is really  UI heavy, and most of these people who talk about test-driven development like to use  examples like the bowling game or stuff that is not about something that does a lot of  UI work and talks to a remote server and hardly does anything else in the middle.  So, you need UI-specific test patterns. Now, I'm going to be discussing this more tomorrow.  At 1340, in room two, I have a session called Test-Driven JavaScript Master Class. I'll  be going into much more detail on front-end test-driven development with JavaScript, but  today I want to go into just sort of the overview of it.  Briefly, what we're going to do is we are going to test drive this code, so this simple  validation form. The way we're going to do that is we're going to have our unit tests  simulate some events that will stimulate the production code, and then we'll inspect the  DOM to see that the right thing came out. Now, there are other ways of doing testing  as well, and I'll be talking about those tomorrow, but we're just going to take that as given  for this session.  Now, test-driven development, how many of you are familiar with test-driven development?  Almost everybody. Awesome. So, as a really quick recap, one thing that I do see people  make a mistake on, sometimes people think test-driven development means write a whole bunch of tests  and then write the code to make those tests pass, and if that's the way you do it, there  is a more fun way of doing test-driven development than that, because that's kind of painful,  because once you've written all those tests and get to writing the code, you realize your  tests are wrong, and that is taking really small test steps. Test-driven development  done well involves very small steps. First step is to think about what you want your  code to do next, and then think of what test you could write that would fail until you've  written that code. That's the hard part. Then you write that test, and you see it fail. That's  a red bar. You write the production code, see it pass. That's a green bar, and then you  make your code better. Even a little tiny bit as good as you know how today, and if you  can't think of any way to make it better, that's fine too. And you keep doing that until  the code is as good as you know how to make it, and then you move on. I see somebody yawning  over here, so I will move on, sir. You've all heard about this. Okay, arrange, act, assert.  This is how you'll typically do it. In JavaScript, you often need to reset. This is the pirate  pattern of test-driven development. Yarrr, matey. Come on, you all want to say it. Yarrr.  So what you're going to do is create DOM elements, run your production code, check the DOM elements,  and then erase them. You need to take them out of your document body. Otherwise, bad  things will happen. So here's what we have. How many of you, I don't know who's in the  audience, so how many of you are pretty familiar with DOMs already? About two-thirds. Well,  for those of you, this will be very much a refresher. For some of you, this might be  useful. So we've got this page that we're going to be testing. We want to check the submit  button. Internally in the browser, this is arranged in what's called the document object  model, which is a whole bunch of objects arranged in a tree structure. Every node, every HTML  tag in your document has an object, and they are arranged according to their hierarchy.  So what we want to do is we want to take action when somebody clicks the submit link down  in our A tag in that DOM. The way we do that is we do element.addEventListener. We can  say what kind of event we want to listen for, in this case, click, and then we pass in a  function, and the browser will attach that function to the DOM. So when an event occurs,  say the user clicks the submit button, then the browser starts doing this whole event  thing. That's terribly complicated. First, there's three phases, and then an extra step.  First phase, capture phase, it will go through and run all the event handlers that have been  assigned, that have been registered on all the elements that are apparent of the one  that's been clicked, if they've been registered to run during the capture phase. Then it goes  into the target phase. It runs the event handlers on the element itself, so it will run the  one we pass in. Then it goes into the bubble phase, where it will do the same thing in  reverse. It will run all the event handlers that have been registered to run in the bubble  phase. Then finally, the browser will take its default action. That is completely dependent  on the browser, but you can typically expect that clicking a link will cause the browser  to go to a new URL, unless it's IE, and then who knows. In summary, there's two things.  Event handlers execute in three phases, capturing phase, target phase, bubbling phase, and then  the default action occurs. You can cancel the event handling by calling event.stoppropagation.  You can cancel the default action by calling event.preventdefault. This matters because  what we want to do is when that link is clicked, if the field is empty, that is, if it's not  valid, we want to stop the browser from following the link. We'll be using this event.preventdefault.  So how do we test this code? Let's do it. You, sir, who was yawning. What should we  do next? Yeah, we need to, well, so we need to, yeah,  the test-driven development cycle is think about what we want to do next and then write  a test that will force us to write that code. So we need to think of something really small  that we'll need to do next. What I'm going to do is I'm going to, I'm  sorry for putting you on the spot. I'll buy you a beer afterwards. I'm going to go ahead  and fast forward just a little bit. But not very much. And what I've done is I've just  brought in the example HTML. So now we can see the page actually appear in the browser.  But if we don't put anything in here, it's supposed to turn pink, but we haven't gotten  any code yet, so it goes to the destination page. So that's what we need to fix. Let me  just make sure everything's still working. Yeah, good.  So what I've got in the page is I've got it set up so that on load it will set up the  validation. So there's a function here called example.initializeValidation. That function  does not exist yet. So my very first step, remember, very small steps in test-driven  development. My very first step would be to write a test that fails until we write a do-nothing  initializeValidation function. And that should be pretty easy. All we need to do is run example.initializeValidation  like that. Did I do that right? Looks like it. There it fails. See, it says example is  undefined. So I'm going to go in and I'll need to make the example JavaScript code. And  we'll set up our global namespace. You could use something like requirejs or browserify.  And I do, but that is more complicated than I wanted to get into for this talk. And it  doesn't like the white space. Let's try this again. Now it's saying it can't find the property  or method initializeValidation. So we're on the right track here. So I'll write initializeValidation.  Again, do nothing function. That should pass if I did it right. Did I do it right? No.  Example is not defined. Actually, no, in this case, it's just a linter complaining. I need  to tell it that example is a legal global variable. There we go. Okay. What's next?  We've got the validation initializing, but obviously it's not doing anything. Where should  we take it from here? Anybody? Anybody? Bueller? No. All right. It's my fault. I called you  out and now nobody's willing to talk. Well, what I'm going to do next is I'm going to  make sure that we want this field to turn pink. Now, one thing we could do is we could  write our code to actually change the background color of the field, but that would be coupling  our JavaScript code to our view. So what I'm going to do instead is have it set a CSS class  that we can set to any color we want in our CSS. So I'm going to say that we set, that  it sets a CSS class when the field is empty. Now, that does mean that we're, oops, we'll  need that back. We have to do a bunch of stuff here. Before we get started, we're going to  need to set up our DOM because by default, Karma doesn't have anything in the DOM. So  the way I'm going to do that is I'm going to add the fields, and I really hope I do  this right. I'm counting on you guys telling me if I make a mistake here. Okay. Did I do  that right? What did you say? I think that's right, isn't it? Before and, what did you  say? Type for input. Thank you. Well, I really should do it properly, shouldn't I? It doesn't  work. See, this is why I need to do this stuff because I make mistakes all the time. Yeah,  I think the input tag is just a regular tag, right? Right. That's what I mean. It's just  a standalone tag. Well, let's see what happens here. All right. So now we need to get our  text field set up. We're going to pull that out of our document. I have to admit normally  I'd use jQuery, but I wanted to try to make things simpler here. So we'll pull out our  text field, and we'll pull out our submit link. And we'll need to declare both of these  variables. So there we've arranged our test. We are acting here. We need to assert, and  we need to reset. So after each, we're going to remove those values. Remove child, text  field, and submit link. All right. Will this work? I have no idea. Let's run the test and  find out. Hey, there it goes. And I'm going to tell it, oh, and it's complaining about  Firefox and Chrome. Let me go fix my version numbers here. What I want to see happen is  it say OK down at the bottom. Still complaining about Chrome not being tested. See, this is  why it's useful to have the required browser in there. I had completely forgotten that  I turned off Chrome. There we go. Now everything's working. Okay. So our test appears to be  working. So we need to set the CSS class field when the field is empty. Let's go ahead and  put this initialized validation up here in our before each to basically our setup function.  How can I check to see that it sets the CSS field or class when we click the link and  the field is empty? Come on. Have more beer. Sorry? Class name? Well, I'm not sure what  you meant by that. I think what we need to do here is click the link and then check that  the text field has the class we're looking for. So expect the class attribute on that  text field to equal some value, which I'm going to actually just hard code to example  dash required. So example and I'm going to put that in a constant like that. So this  should fail saying it got no but expected example required. So to make this pass, I'm  going to do something simple and dumb. I'm just going to set the value of that class.  So I'll say sex field dot set attribute. Am I doing that right? Yeah. Set attribute  class to example dot required field class like that. Oh, and we'll need to pass those in.  So pass it in there and pass it in here. There we go. Is everybody still with me? When there's  silence like this, I can't tell if I'm going too slow or if I'm going too fast. So everybody  good? You'll ask questions if you're lost, right? You'll shout go faster if I'm going  too slow, right? You'll say awesome if things are just right. Cool. Okay. Now we're getting  in somewhere. Okay. So we've got our first test. It sets the CSS class when the field  is empty. This is obviously wrong because it's going to set that class every time no  matter what. So that's the first loop around the cycle or rather the second time around  the test driven development cycle. So now we're going to say does not set or it clears  CSS class when field is not empty. Actually, let's just say it does not set it. I'm not  going to try to make this terribly complete and correct. It's just an example. So what  we're going to do here is we're going to say text field dot value. We're going to set  it to something that is not empty. We're going to click that link and then we're going to  expect that class to be no. That should fail and it does. It says a expected example required  to equal no. We can fix that by saying if text field dot value is truthy. In other words,  if there's nothing there, we'll set the attribute. Did I get that backwards? No. What am I doing  wrong here? Honestly, I can't see it so I need your help. It says expected example required  to equal no and it's not. Checking for the value. Well, we don't have a whole lot of  time left. So what I'm going to do is I'm just going to jump back to the other code  I've already prepared. Tell you what, I hate to do it but I'm just going to jump straight  to the conclusion. Did you remember to put an actual value in the box? I think I did.  But now, live coding, two and a half minutes left. I've screwed everything up. Let me see  if I can get it back to working again. There we go. Okay. So here is the final bit of code.  I wish we had time for me to step through it piece by piece but I think you got the idea.  What we have now is that we check to see that it follows the link when the field is not  empty, doesn't follow the link when the field is empty and it sets the CSS class when the  field is empty and we can make sure this works on every browser by running our build except  it doesn't work on IE8 because IE8 doesn't have support for the add event listener method  we're using in this code. So that tells us if we were doing this as we go, we see that  right away we can either choose to exclude IE8 entirely or use something like jQuery  that abstracts out those details for us. So to wrap up because we're out of time I'm  afraid. Professional JavaScript development involves building for the long term. In other  words, what you're going to be doing here is prioritizing maintenance, maintenance ability  over ease of initial build. That means doing stuff that takes a little bit of time and  after set up like an automated build, like linting, like test room development, like  karma. So what I've shown here is a simple example with the submit link. You can find  this code at the link I'm about to share with you. There's five techniques here. Automated  build, JS hint, continuous integration, automated cross browser tests, and front end test driven  development. I'm sorry we didn't have more time to go into more detail on the test driven  development example. We will go into more detail tomorrow in the test driven JavaScript  master class and I'm happy to answer any questions people might have afterwards. One last thing,  you might have heard people tell you, well we don't have time to do testing, we haven't  gotten around to it yet. Well I have here a whole bunch of round to it. So when somebody  on your team tells you, I haven't gotten around to it yet, you can say, here you go. Oops,  that was aimed at you. Just as thanks for letting me pick on you. So come on up and pick up  some round to it. I've got hundreds of them and give them away to your colleagues. Everybody  who says, I haven't got time to do that, I can't get around to it right now, you say,  well here you go. Here's a round to it. Thank you. All right. Source code is on GitHub. You  can find continuous integration source at the Automotopia repository. The test code  I was sharing here at the LL10 repository. I have a screencast on test driven JavaScript  where we cover all this and much more. It is the most in depth screencast you'll find  on this material. And there's a coupon for 20% off the first month while you're at the  conference. So if you sign up in the next couple of days, use this coupon, NDC Oslo 2013.  Happy to talk to you further. Thank you for coming. Yes, please take however many you  want.
James_Shore_-_Rigorous__Professional_JavaScript-68334907_0_1103.png James_Shore_-_Rigorous__Professional_JavaScript-68334907_1104_4871.png James_Shore_-_Rigorous__Professional_JavaScript-68334907_4872_6407.png James_Shore_-_Rigorous__Professional_JavaScript-68334907_6408_6647.png James_Shore_-_Rigorous__Professional_JavaScript-68334907_6648_7943.png James_Shore_-_Rigorous__Professional_JavaScript-68334907_7944_8315.png James_Shore_-_Rigorous__Professional_JavaScript-68334907_8316_8759.png James_Shore_-_Rigorous__Professional_JavaScript-68334907_8760_16019.png James_Shore_-_Rigorous__Professional_JavaScript-68334907_16020_16175.png James_Shore_-_Rigorous__Professional_JavaScript-68334907_16176_16247.png James_Shore_-_Rigorous__Professional_JavaScript-68334907_16248_16835.png James_Shore_-_Rigorous__Professional_JavaScript-68334907_16836_19127.png James_Shore_-_Rigorous__Professional_JavaScript-68334907_19128_19607.png James_Shore_-_Rigorous__Professional_JavaScript-68334907_19608_19739.png James_Shore_-_Rigorous__Professional_JavaScript-68334907_19740_20231.png James_Shore_-_Rigorous__Professional_JavaScript-68334907_20232_20687.png James_Shore_-_Rigorous__Professional_JavaScript-68334907_20688_21263.png James_Shore_-_Rigorous__Professional_JavaScript-68334907_21264_22883.png James_Shore_-_Rigorous__Professional_JavaScript-68334907_22884_24695.png James_Shore_-_Rigorous__Professional_JavaScript-68334907_24696_25067.png James_Shore_-_Rigorous__Professional_JavaScript-68334907_25068_25391.png James_Shore_-_Rigorous__Professional_JavaScript-68334907_25392_25439.png James_Shore_-_Rigorous__Professional_JavaScript-68334907_25440_25667.png James_Shore_-_Rigorous__Professional_JavaScript-68334907_25668_26219.png James_Shore_-_Rigorous__Professional_JavaScript-68334907_26220_27515.png James_Shore_-_Rigorous__Professional_JavaScript-68334907_27516_27575.png James_Shore_-_Rigorous__Professional_JavaScript-68334907_27576_29423.png James_Shore_-_Rigorous__Professional_JavaScript-68334907_29424_29483.png James_Shore_-_Rigorous__Professional_JavaScript-68334907_29484_31175.png James_Shore_-_Rigorous__Professional_JavaScript-68334907_31176_35495.png James_Shore_-_Rigorous__Professional_JavaScript-68334907_35496_38603.png James_Shore_-_Rigorous__Professional_JavaScript-68334907_38604_41687.png James_Shore_-_Rigorous__Professional_JavaScript-68334907_41688_41915.png James_Shore_-_Rigorous__Professional_JavaScript-68334907_41916_44171.png James_Shore_-_Rigorous__Professional_JavaScript-68334907_44172_44219.png James_Shore_-_Rigorous__Professional_JavaScript-68334907_44220_44783.png James_Shore_-_Rigorous__Professional_JavaScript-68334907_44784_44987.png James_Shore_-_Rigorous__Professional_JavaScript-68334907_44988_45791.png James_Shore_-_Rigorous__Professional_JavaScript-68334907_45792_46535.png James_Shore_-_Rigorous__Professional_JavaScript-68334907_46536_47951.png James_Shore_-_Rigorous__Professional_JavaScript-68334907_47952_48743.png James_Shore_-_Rigorous__Professional_JavaScript-68334907_48744_49883.png James_Shore_-_Rigorous__Professional_JavaScript-68334907_49884_50483.png James_Shore_-_Rigorous__Professional_JavaScript-68334907_50484_50615.png James_Shore_-_Rigorous__Professional_JavaScript-68334907_50616_51059.png James_Shore_-_Rigorous__Professional_JavaScript-68334907_51060_55151.png James_Shore_-_Rigorous__Professional_JavaScript-68334907_55152_57695.png James_Shore_-_Rigorous__Professional_JavaScript-68334907_57696_59399.png James_Shore_-_Rigorous__Professional_JavaScript-68334907_59400_65519.png James_Shore_-_Rigorous__Professional_JavaScript-68334907_65520_66239.png James_Shore_-_Rigorous__Professional_JavaScript-68334907_66240_66311.png James_Shore_-_Rigorous__Professional_JavaScript-68334907_66312_66515.png James_Shore_-_Rigorous__Professional_JavaScript-68334907_66516_67811.png James_Shore_-_Rigorous__Professional_JavaScript-68334907_67812_70967.png James_Shore_-_Rigorous__Professional_JavaScript-68334907_70968_71615.png James_Shore_-_Rigorous__Professional_JavaScript-68334907_71616_76571.png James_Shore_-_Rigorous__Professional_JavaScript-68334907_76572_79979.png James_Shore_-_Rigorous__Professional_JavaScript-68334907_79980_80531.png James_Shore_-_Rigorous__Professional_JavaScript-68334907_80532_81443.png James_Shore_-_Rigorous__Professional_JavaScript-68334907_81444_86999.png James_Shore_-_Rigorous__Professional_JavaScript-68334907_87000_87155.png James_Shore_-_Rigorous__Professional_JavaScript-68334907_87156_87563.png James_Shore_-_Rigorous__Professional_JavaScript-68334907_87564_88727.png James_Shore_-_Rigorous__Professional_JavaScript-68334907_88728_88955.png James_Shore_-_Rigorous__Professional_JavaScript-68334907_88956_89123.png James_Shore_-_Rigorous__Professional_JavaScript-68334907_89124_89219.png James_Shore_-_Rigorous__Professional_JavaScript-68334907_89220_89447.png James_Shore_-_Rigorous__Professional_JavaScript-68334907_89448_90731.png James_Shore_-_Rigorous__Professional_JavaScript-68334907_90732_91810.png