 Hello and welcome to test all the things with intern during this hour-long workshop. I'm going to give you I'm going to introduce you to the intern testing tool describe some of its capabilities and show you how to use it to test a simple react application in a couple different ways This workshop doesn't assume that you have much experience writing tests or really any experience But you should be familiar with JavaScript or TypeScript If you'd like to follow along Open a terminal and make sure that you have at least node and npm in your path You'll also need Java installed if you want to run some of the tests that we write later The tests aren't actually written in Java, but one of the tools used by intern Selenium is written in Java So you need that to actually run those So first off a little bit about myself and what I do I am a software engineer at site pin At site pin. We are the co-creators and maintainers of the dojo toolkit and We're also the developers of the new modern dojo framework Along with several other open-source projects including intern We also run the first TypeScript focused conference TS conf which is on its third year this year. I Am also the lead maintainer of intern So, you know, I try to keep things up to date keep, you know manage releases If you have feature requests or bugs those will likely eventually land with me. So It keeps me busy and then in my spare time. I really like to work with home automation like I have Basically, I have a couple of home automation hubs that I've set up and I wire up like all the lights in my house and Electrical, you know switches. I have a variety of sensors for like temperature and humidity and pressure and all that Just sort of scattered around and you know, I come up with interesting ways to use that So like on a good day, I don't have to touch any light switches in the house It's it's a learning process though. So, you know, that doesn't always work But but it's really awesome when it does it's it's neat to see code You know working with the physical world So what are we going to do today? The goal here is to give An overview of intern and kind of show you Some of the things you can do with intern. So we're going to you know set it up write several kinds of tests But it's only an hour. So we can't go into a lot of depth So i'm mostly just going to be giving quick flybys of how to write A few different kinds of tests And you know again, just show you some of the things that you can do So first off, let's get into What actually is intern? So obviously it's a testing tool It was originally created for the dojo toolkit Several years ago when dojo was making its switch to amd And heavier use of asynchronous code things like promises When you know back in the early days there and so Then you know one of the site pen engineers then created intern to be a more effective way to test that kind of code Probably interns key differentiator right now is that it supports the full testing life cycle So with intern out of the box, you can write unit test. You can write function functional tests And full site integration tests And we'll go into the difference in those a little bit more in a couple of slides Another differentiator is that intern really focuses on testing in real browsers So it can use i mean it can run test in node and that's what it does by default But it also tries to make it easy to run your tests in browsers My intern can also collect code coverage So you know to help you know how well your unit test your exercising your your application code And it's it's extensible. It's pretty easy to write plugins for and there are a couple of existing plugins It we'll look at later on uh today And it's pretty easy to plug intern into ci systems as well So when we talk about unit tests and functional tests, uh unit tests are the ones that Probably most people who write tests are familiar with those are tests that Load Application code and just call functions in it directly You know instantiate classes work with them and look at the output again just directly from the code And we like to say those those test code from the developer's perspective The other major kind of test Is functional tests Functional tests actually sort of sit outside the application. They don't load application code directly They don't they don't see it or touch it instead Uh functional tests act a lot like a user and they act they exercise, you know, sort of the user level functionality of an application So a functional test might uh tell a browser to load Uh an application page like you know to load some web page Then it might look on the page and find certain elements It might find a button and tell the browser to click that button and then look at some output Or, you know, some text in a field to see if it changed So functional test test code from a user's perspective instead of from a developer's perspective so When we talk about how we organize tests in intern we we have three sort of broad terms there At the top level are suites. These are groups of tests that are, you know, it's a logical grouping So very frequently a suite Is uh is created for a particular module. Let's say so Um, you know, you're testing the button module in your application So you would make a button suite and then put all the tests for button in there Uh, but you know, you don't have to have just one suite profile that can be multiple Um, and you can nest suites. So if you want to have finer grain groupings, uh, you can you can do that Tests are the things that actually exercise code or maybe exercise the browser Uh and make assertions So I mean the test is is the thing that's actually doing the work and then, uh Test can be asynchronous so they can work with async code You just return a promise in intern. That's that's the basic async unit And within tests, uh The checks you're actually making are called assertions So when you do something like, you know, you you you call a function and look at its output You're going to make an assertion about its output and you're going to check and see that something Uh is true. Maybe maybe you call a function that's supposed to append something to an array You get back the array and you would assert that the length of the array was one bigger than it started out Uh, intern provides the chi library, which is a popular third party assertion library And it gives you a lot of these high level assertions like Asserting that the length of something is something you expect or asserting that a value is true or is null or things like that Um, you can also just write your own assertions basically anything You know, the only thing an assertion has to do is throw an exception if there's a problem So if you if you check a condition and it's bad, you would throw an exception Otherwise, you know, you don't And when you use something like chi, it's just uh, it's making that process a little cleaner and easier to read So we provide that, uh, but intern is very flexible about actually What you use there So let's kind of get started on this and jump right in here and take a look at what our test application is And you can see this down here. We're going to be getting something from this intern tutorial repository So first off, let's just clone that We'll go in there and we're checking out the OpenJS world tutorial branch So we have that and then we're just going to install our app It's a standard, you know, node application created with create react app. Well, I guess it's a react application Uh, so we can just run npm install. It should also work with yarn I just use npm all the time So once that installs, we will be able to run npm start and that will start up the application In a in a development server and open it up in a browser here And then we'll be able to take a look at it and you know See what it does As soon as this finishes installing It's not npm is never the fastest thing Okay And we're good So we've cloned our repo Install our thing we're running start And so here we go our application in all of its glory Is A calculator It's you know A standard simple little multifunction calculator 7 plus 3 equals 10 Hopefully it's good. It's not a thoroughly tested calculator, which you know is fine for our purposes But it makes a nice It's it's nice and interactive and has easily extractable logic So let's take a little closer look at what's in our flashy calculator here Real quick just because it makes testing a little bit easier Um, okay, so in here we have You know the standard react layout we have a public directory with our static assets We have A source directory that has all of our code in it And in our code here, uh, we have Components and just a couple other things We don't have any further breakdown in our code because this is a very simple application We don't need a components directory in a you know, etc So we have our capitalized components here. We have two Other modules we have common which is just some typescript types And I think one constant and then we have this logic Module here Which is where the the logic that actually, you know handles things like plus and minus and equals lives So let's take a quick look at our code So if we look at our calculator code here It is a react component. It's a functional component So we just we're exporting a function that is our component Within that we're using some react hooks here To create some state variables and some things to update them So we have this, you know value total in operation We have this button args thing that we've made down here and that is just All of these these six variables up here. We're just sticking them in an object. We will Use these with the logic that actually manages our buttons This button handlers thing is just Binding the names of keys on the keyboard to particular button handlers so that we can also use the keyboard To do things as well as click buttons The button handlers themselves come from this button handlers constant And there's just one per button. So ac goes with the ac button divide goes with divide Times goes with times etc We can see down here. We have a couple of you know event handlers We bind the the keyboard handler to the entire window Which is not maybe what you do in the real world, but for this tutorial app, it's fine And then we create our calculator component and you know, it's just It just contains a simple, you know a wrapper div and then within that The display and the grid of buttons and then you know the buttons get passed A callback and the current operation Okay, so we have the basic idea of our calculator here, you know, I mean this is What the component looks like You know the buttons and the display are similar kinds of things Let's take a quick look at the logic module So this is where our button handlers live and basically in here we're importing a constant Which is just a list of button names and then we're importing a couple of types And so we're just exporting from here our handlers and that's just an object that is Basically binding the name of the button to its handler and so the handlers are all just functions that take Uh a button args object and that we saw in the The calculator component Uh the button args Are this thing which is just all of these state variables and they're uh, they're updaters So each of these handlers takes a button args and then just pulls out the properties that it cares about With destructuring here and then does whatever it needs to do to update the calculator state So all clear will set The displayed value to zero and it will you know clear out any pending operation plus minus divide Just mix that Dot will look at the value and if it has a decimal in it I will do nothing if it doesn't have a decimal in it dot will add a decimal to the end of the number and you know The rest of our of our button functions are in here So this is what Kind of what we're working with here and what we're going to test and so in our case We're just assuming, you know, we have an application You know, nobody wrote test while it was being made but now We're going to write some tests to you know prevent regressions all right, so Before we can write test we've got to get intern in here and kind of make sure that that's working And so to get intern we're just going to npm install it Oops saved dev While that's installing let's go ahead and edit our intern config file The intern config file is Uh, just a json file this you know, it contains intern configuration options And we need one of these in our project to tell intern when we run it that hey, this is an intern project Uh, and we're going to set it up right now to point to where we eventually plan to put some unit test suites So we'll say test unit Any ts file under there Note that we can use globs here for the suites list and also that We can put ts files directly in here Because when we are running our tests in node Intern can take type script directly. You don't have to pre compile it So this is our simple config file. We just finished installing intern up here So let's actually check that it worked. So let's run npx intern When you install intern you get an intern, you know binary script In your node modules bin directory so we can just npx intern Excuse me and It ran and so this is the expected output in this case because we have no test yet So it ran tested zero platforms nothing passed nothing failed So we have intern Uh, the next thing to look at is interns interfaces intern provides three of them Uh, they're all functionally equivalent. They just are different styles that you can use when writing your tests So the first one is the tdd style. Uh, this one is just very straightforward You create a suite with a suite function. You create a test with a test function And you make assertions in general with the assert syntax You know assertion Yeah, so that's that's basically the tdd interface And you can see at the bottom of the tdd section here that This const suite test equals intern dot get plugin business. That is how you load an interface in a test Um, you know the the interfaces exist as plugins and intern and so you're just requesting Uh, this plugins exports from intern So that's how you get that and the same thing with assertions in this case. We're getting them from chai Yeah, so the bdd interface is uh, another one. It's you know behavior driven development is what bdd is tdd is test driven development um So the bdd interface is exactly the same as the tdd interface. It just uses describe and it instead of suite and test and the idea there is just more It it encourages sort of an english for you know, like a language flow when you talk about the suites and tests So you're describing You know a resource a module a suite and it should do something and it should do something else and you know That sort of thing and it is common with bdd to use the expect syntax for your assertions Uh, chai supports both of these it provides an assert Um, and a set of assert functions basically and it also provides a set of expect functions Um, and these again are are functionally equivalent. It's just one of you know, they're different styles So with bdd it's normal to use expect you don't have to you can you can use assert with bdd and expect with tdd It's pretty flexible And then the third interface is the object interface This one is is kind of an intern unique one Where you don't use describe and enter suite and tests instead Rather than using functions to make your suites and tests You you create your entire suite in an object It's a very declarative interface So you make your suite in an object and when you've constructed this object you pass it to this register suite function And then that goes ahead and takes care of processing that into runnable suites and tests And with object, um, again the assertion syntax is pretty flexible Usually I've seen people use assert with it. You could again totally use expect with it Um, all three of the tests in taxes, uh, support suite lifecycle functions. So with all of them you can use, uh Before before each after and after each So before each and after each run before and after every test in a suite Before and after Run Once at the you know before runs at the beginning of the suite before any test of run And after runs at the end of the suite after all tests have run So these are functions you can use to do setup and cleanup for individual tests or entire suites All three, uh, all three tests interfaces also support async functionality You just return a promise from a test and you're async You can also declare tests to be async if you want to use async await For any of these So let's go on and actually write a unit test So when we're going to write a unit test The first thing we need to do is create The typescript config file when the first thing we're going to do Is create a typescript config file in the test directory Because the tests are going to need actually first we're going to make a test directory Then we're going to may write a ts config file in there because the tests are going to use a slightly different set They're going to use slightly different options than The ts config file that the rest of the app uses We're going to inherit from the base one because most of the options will be the same Oops spelled that wrong All right, so the options that we're going to change Uh are We're going to set our module type to common j s And we are going to disable this isolated module setting that uh That the react ts config uses And we're going to load the intern types because intern declares one global that uh, we want to have available So that we can do things like intern dot get plugin Uh types intern All right, uh the isolated modules Basically, we're disabling that so that we can write suites that aren't technically modules I mean we might want to write a suite that doesn't import or export anything. It's completely fine But if we don't disable that setting typescript will be sad about it All right, and then we need to include our test files In you know with this ts config. So we're just going to say anything under the test directory Is handled by this ts config file oops. I spelled config wrong too. There we go All right, so we got our ts config file Let's update our intern dot json file. We need to get that ready too We're going to add the ts config that we just created To this node section. And so what we're doing here is You know, I said earlier intern can process typescript by default and it can and it uses the project default ts config uh by default and so uh, but that's not the one we want to use we want to use our tests one So we're going to say node ts config test ts config dot json ts config goes in this little node block to make it very explicit that it only applies to a node environment Intern like I said can run in browsers and it can run in node You use the same config file for both. I mean you don't have to but it's pretty common to And so within this config file You need to tell You know intern needs to know Which things apply to browsers and which things apply to node And uh in this case, we just wanted to make it very clear that ts config is really only a node thing Browsers, there's no typescript processing functionality for browsers All right, so we got that We've updated our config file. Now we should actually write the test So let's see we need to make the unit directory and test because we said we were going to put our tests in test unit Uh intern's pretty Flexible about this. You don't have to put your tests in a test directory or in test unit You could put them in your application directory and call them, you know dot spec files if you want it or something um I normally just put them in a test directory because it seems cleaner to keep your test resources separate from your app, but It's very uh You know, it's very flexible about how you actually do that All right, so let's edit test unit. Uh, we're going to test the logic Uh module So vi testing the logic So let's go ahead and import our interfaces We're going to use the tdd interface. So suite and tests, uh Equals intern dot get plugin Interface td And then we're going to import the assert Uh The assert interface from chai Okay, so we have these So now we're going to make a suite and we're going to call it logic And we are going to write a test for the ac button handler So let's go ahead and call our test ac all right Well, the first thing that we notice is that we're going to have to import the ac button handler to be able to test it So let's import the button handlers from Source logic, okay, so we have that Button handlers dot ac Just try to call that. Oh, yeah ac takes an argument We saw earlier that it needs something of type button handler args Whoops, so let's just go ahead and import that interface to button handler args from That's one of the things that's in that common module All right, so let's make an args. It's a type button handler args And so button handler args takes a value It takes set value Then we'll just do this Then it also takes a total Which can be null or a number and then we'll just have set total And then it takes an operation which can be an operation Just a string or null and then we'll have set operation And it takes an operation Which we should also import from up here So all we've done here is made basically a mock of our button handler args Uh, and we are just You know they they have the value of the total in the operation state variables We have the setters and they're just going to update those variables in the state So this will make it very easy for us to pass in something that uh You know that's under our control and then look at how it changed after we've called the function So we're going to pass this in and now type script is happy about it Okay, so now we can call our function and we're passing it something and we're you know We actually want to run a test. So what is ac supposed to do? If we go look at it, it's supposed to clear the value and clear the operation So let's check that that happens So first we're going to set the value to Something and we're going to set the operation to something We're going to call this function and then we're going to look and see if they actually Got uh got cleared. So we're going to assert that Um That our value Is zero and we are going to assert That our operation Is null And so if you notice when I you know as it quickly popped up when I typed assert and hit period There's there are many many assertions that you can make Using chai and again The the main value of all these I mean these are things you can do yourself with just code and if statements The value you get from using these assertions in chai is that um It's more compact and easier to read and also chai will construct Friendly uh or at least useful exception messages for you So we'll look at that in a second. So we made this test All right, we've made our assertions. We called our function. So let's actually go down here and run it So we'll just run in turn. We've already pointed at our test. So it should run our tests and it did In the node environment we ran The logic suite the ac tests Uh one test passed If we wanted to see what happens if it fails Let's jump over to this ac code real quick and you know make it do something wrong So let's say it sets the value to negative one Okay So if we run our test now It's going to run and it's going to fail and the failure we get is oh, there's an assertion error We expected negative one to equal zero So this is chai constructing a nice sort of useful error message for us Uh we could make that even more Potentially contextual if we want we can add a message here at the end um So maybe uh incorrect Hey, you know Or we could say expected ac to clear value So now if you run the test I mean it's still going to fail but now we get an even more contextual message here expected You know expect ac to clear value. We expected negative one to equal zero So chai lets you make you know meaningful meaningful assertion messages So let's just fix that and make sure that our test passes again And so this is our first unit test and it's running in node and there you go Okay, so we ran it in node, but one of our big things is being able to run tests in the browser um There's a problem though, which is that we can't run typescript directly in the browser So what are we going to do for that? We got a couple of different options when you're using a react app or something that's normally built with webpack Webpack is probably the most straightforward way to go about that The goal there is just to bundle up your typescript and everything it needs for its test Into a javascript bundle that we can load into the browser So to do that We're going to make a minimal webpack config and we're going to make a couple other little updates to our project And then we're going to build that and run that So first let's uh, we're going to need the ts loader since we're going to be webpacking typescript code We need that We're also going to need the webpack cli We've already got webpack from react scripts Um, and you know normally you shouldn't normally rely too much on you know Transitory or transient package dependencies like we're doing like we're going to do but it's just a little simpler in this case So we're just going to install the cli And it will work with The webpack that we already have So let's install the webpack cli and the ts loader And while those are going we're going to uh edit Our web we're going to create actually a webpack config And in our webpack config We're going to import resolve because we're going to have to resolve a couple of paths We are going to export our config All right, so load development Because webpack needs that now for our entry For our test in general, it's going to be most efficient to just build all of your unit tests that you plan to run Into a single package There are other ways you could do this you could build them into separate packages and use you know like vendor chunking or some such For our case, we only have one test But if you do have you know, even a moderate number of tests, it's entirely possible to just build them into one bundle So that they'll run So we're going to run test unit logic dot ts So this is going to be our entry point For you know, what we're going to do with it is we are going to compile it to a file name called unit dot js Because we're assuming this is just all of our unit tests The path is going to be Uh, we're going to put it in tests in the root in the project root directory Okay We have to set up, uh What we need to handle typescript So for this we're going to say all right, uh, if we have a ts file Or a tsx file, uh, we're going to use The ts loader We're going to pass it a couple of options We're going to tell it most importantly that uh, it needs to use This config file That is our test ts config file dot json We also want to change another compiler option Specifically just for webpack And that one is no emit The react config which our test config is inheriting from Enabled this no emit option For our own purposes, we don't want that so we're going to turn that off here And this will just allow this simple webpack config to build our stuff So we close this we close this we close rules And then we just have to tell webpack that you can resolve typescript modules or typescript files ts tsx dot js dot jsx So none of this is really so much intern specific as it is its webpack But yeah, this is this is sort of the minimal minimal webpack config for our simple react example here All right, so we got this Wait, we don't want that semicolon. So now we have a webpack config um We install ts loader We install webpack cli. Let's make sure that our webpack build works Uh, no mpx webpack um config tests Dot js So if we set things up, right this will run And it will give us something in that test directory And it did So now we can build our unit tests Okay, now we need to tell intern that it should run our tests in a browser So we're going to edit our intern config again And in our intern config, we're going to add a new browser section And in this browser section, we are going to tell it Which suites we want to run in the browser And they will be tests unit dot js Then we're going to move this suites Declaration into the node object. So the deal with that is When suites is up here, it applies to both the node and browser environment. So when you're running in node You will get whatever suites are here and any suites in this node block If you're running in a browser, you get whatever the base suites are plus any other suites in browser We're just going to stick this in node. This means that We are only going to run These suites in node, which is fine because node is the only one that can process the type script And we're only going to run these suites in the browser So now we've told intern what we want to run in the browser Now we need to tell it what browsers we want to run these things in For that we use an environments property The environments property just tells intern what environments we want to run our tests in By default there's one environment and it's node We're going to keep doing that and we're also going to add another environment chrome Okay, so we have two environments Let's go ahead and run our test npx intern So now what we should see happen is we're we're seeing this listening on local host That's intern starting a test server For the browser to load the test from And then we see it starting a browser and we see all this stuff happening This stuff that is happening Is intern running feature tests in the browser To try to see if there are any any Uh bits of functionality in the browser that are maybe operating in a non-standard way or that are broken or something Uh because different browsers don't always work Exactly the same way Intern tries to look for some of those differences and deal with them behind the scenes So that you don't have to make a lot of fiddly adjustments to your tests That can be a little bit annoying sometimes though. So if you want If you're sure that you're using a modern browser that's probably implementing, you know Current js specs Pretty closely you can disable those feature tests And for that we can just use a slightly more Uh, you know longer syntax for specifying our environment and we can say browser, chrome Fix session capabilities We can set it to true false or no detect Either one of these will you know for our purposes should be fine. We'll just set it to false Uh no detect Basically sets some uh, some internal properties if it happens to know something about a browser that might be broken like, you know ie 10 Doesn't support this feature intern. Maybe doesn't even need to test for that. It just it's like, oh, this is ie 10 We know this is broken. That's what no detect does but in general chrome Should be fine So we're just going to disable that and the whole purpose of disabling this right here is just it makes uh Our tests run a bit faster in the browser So now if we run our tests We will see them run in node here very quickly and we'll see our browser pop up. It will run the tests Um, and it was much quicker that time. There was none of the you know, the feature test running beforehand All right, so this is pretty cool now. We're running our tests in node. We're running our tests in the browser The intern tries to make it easy to run your tests kind of everywhere Uh, so in addition to you know, what we're doing now We could add other browsers. So say We could run firefox Okay So now if we run our tests We will see The node test happen We will see firefox pop up and our test will run in firefox So that's pretty sweet. So we can run in different browsers We can also run on cloud services So if we wanted to in this case, we could say, all right We're going to use this tunnel variable To use a different Basically a different target. So what the tunnel is tunnels are a thing provided by An intern support library called dig dug and tunnels are what intern uses to talk to remote browsers So by default, we're using the selenium tunnel that uses a local copy of selenium To talk to chrome and firefox and you know, the browsers you have installed locally We can also hook up to cloud testing services Um, and we can use a null tunnel, which basically tells intern Don't don't do anything. You know, I've I'm running a web driver server or selenium sort of externally on my own Just talk to it. Don't try to start your own But let's try uh, let's try using browser stack just to kind of show That you don't really, you know, it's pretty easy to do that. So we're changing, you know Sort of one line of config here And we're saying we want to use browser stack Um, we're also going to set up Some capabilities here that are browser stack specific just to make it A little easier to see them. I was going to show them in the the browser stack dashboard You don't have to do this But we can Whoops I can look up exactly what those are again, uh, let's set our build name to um, whoops We'll set our build name to ojsw and let's set our project name to Intern tutorial and again, these are browser stack specific Properties that we're setting Um, this capabilities property are like, uh, these are settings that get passed to Whatever, uh, browser or remote session that we are starting So that's just what goes in capabilities. It's kind of a random metadata grab bag Um, so here we're saying we're going to run test on browser stack and This again entirely optional. It just makes it a little easier for us to see What, uh, what's going on with browser stack Another thing to keep in mind is when you use any of these cloud services, you do need to have an account So you'll need your own credentials to do that this little r test thing that I'm throwing in the beginning here Is just a script that I have that loads up some of my credentials into environment variables So we're going to go ahead and run this And we will see it, you know, it's tunnel downloading it's downloading the browser stack tunnel executable It runs a node test and then startup is a little bit slower here than it was for the local tests Um, but you can see that popped up very quickly You know actually relatively quickly and so it was a remote session chrome 83 on windows If we go over here and we refresh The dashboard that I have open We don't see any builds Why don't we see any builds? Uh, oh there we go Okay, so this is our our test run that's running right now And we can see we had chrome and it ran and we had firefox and it's still running And so with browser stack just as an example and the other services work similarly You can give a browser name and it just picks the most recent version and usually on windows You can also specify browser versions Or ranges of versions if you want to run on multiple versions and you can also specify the the os that you want to run test on So you can do all of that just you know within this intern config So this is us running tests on browser stack So that is pretty sweet that we can do that except for our firefox test which Finished But our session didn't quite end Oh, there it goes. Okay firefox a little slow to finish there anyway So uh, so as I said, you know all of this this tunnel system is provided by By dig dug. That's the name of the intern package that that handles that and it has again several tunnels So there are a couple more tests we can do as well Actually, I mean they're a functional test. We haven't done any of those yet. So uh For functional tests, you know, I mentioned those before the big deal about those is they involve browsers but they don't run in browsers and so this is just Sort of an overview of what our functional testing api looks like You can see here that we're we're getting this remote object passed into the tests All tests in intern are past something they're they're past a a test with a capital t object well an instance of test But usually you don't need anything on that so we don't you know, it's not usually shown in this case though We do need this remote property from the test And so the remote is what's going to give us access to our functional testing api The functional testing api is provided by an intern package called leadfoot It's called that because when it was originally created it was a lot faster than any other web driver api It's not not much of a difference now, but it's still called leadfoot But uh, so leadfoot's api is an asynchronous fluent, you know a chained api So basically from from remote you will call something like get to load a web page Once you have a page you might call like fine by css selector or fine by, you know name There's a variety of fine functions To locate elements. That's the thing you might want to do Once you have an element you can interact with it with like, you know clicking And then you might also I mean you can get information from elements Like in this example we're getting visible text You'll also notice that there's this end thing sitting in the middle of the command chain What that is is because Excuse me, um, so in our command chain You need to find elements and interact with them, right? but All of these commands are in a single chain So how do you decide what you were interacting with? In turn in a single chain maintains a context element And the context element is kind of the the basis for What any following commands are going to you know work from So if you want to interact with an element you you find it And then it becomes the context element and then you you know any actions you take that are element specific Like clicking or getting text or something Those are Done on the context element The context so in turn can can manage multiple context elements and they're stored in a stack and so You know when you when you do something it's on the context element If you want to sort of back up and work on a previous context element or just get rid of the whole context and just do something With reference to the entire page you call end and end just pop something off the stack So that's kind of what's going on there is so we find an element We do something with it We call end to wipe that back off the stack and then we find a new element And we do something with it And then we can make assertions We don't need to have an end at the end of the command chain because that whole context business is Specific to a single command chain So you know once once we return this thing We're done with that Another thing to really Keep in mind is that the command api is asynchronous So the end of that command chain. I mean it's it's a promise chain basically or a promise like chain So for in turn to wait for that to finish you need to make sure to return that chain So Yeah, that's that's the thing that I've seen and done myself forgotten to do Because then your test starts to look great It's like oh it passed and then you'll see weird errors popping up later on in the testing process because those promises started failing or something And it's it's not good. So you always want to return a command chain but That whole thing can become a little bit easier if you use a single weight So here's the same set of commands Using the functional test api, but just using async await instead of the fluent command chaining stuff And for this you can see we we call a get To get a page and we just await it and like when we look for an element You know the fine by css selector method returns that element and it always does here We're just awaiting it. So, you know when that asynchronous call resolves we get the element out of it Then we can call a method directly on the element. So here we can you know get the button and then Call click on the button. We don't have to deal with The context stack or popping things off of it or whatever. So it's a little more verbose because we have weights everywhere But It is I think a good deal cleaner to see you know to to deal to see what's going on. It's it's A little more straightforward to work with so this is just another way of using the functional api that Can potentially be cleaner, but you know either way does work Okay, so let's write a functional test So to write a functional test we are going to make a directory and test called functional And we're going to write a functional test just for our whole app So we're going to call test functional app.ts and we're going to do the whole um The whole importing of our sweet and test but again sweet test No, not from I'm going to remember that one of these days get plug-in interface dot td assert Plug-in chime All right, and then we say sweet app And we say test Uh, we're going to test adding two numbers that's going to be our first test So that'll involve some button clicking And uh and looking at the output add two numbers Okay So we get a remote object Okay, so in our test the thing we're going to need to do is well the first thing we're going to want to do is load our test page Yeah, we're going to call this async so we can use async await And we're going to say remote dot get our test page Then we're going to want to get some buttons and click them So if we're going to add two numbers, let's let's add one plus two and make sure that they equal three So let's say one equals remote dot find by css selector Buttons one so for this app If we just go over and inspect it If I can see where my inspector is here Um We can just go look at our buttons and they all have you know their class names our button dash the name of the button Okay, so we got the one button Let's go ahead and await one dot click all right and just Because this will run really fast when we run it. Let's go ahead and throw in a sleep in here Just for for us humans so that we can see What uh, what's going on When we're clicking these buttons If we don't I mean we don't you wouldn't normally put this sleep in here and generally sleeps and tests are not a thing You should do But in this case like I said, it's just just for the humans to be able to see this happening because otherwise It just goes by very fast All right, so oh and in between clicking one and two we need to uh, we need to click plus Okay So we click one we click plus we click two now we need to click equals Okay, and now we need to read the display And see if it has the value we expect And the display is just called it uses the class display Value equals weight display dot get text Whoops Get visible text All right, and now we're going to assert equal Value and one plus two should hopefully equal three All right, so we have a functional test now our functional test gets a page Uh, it clicks some buttons and then it reads the value from the display And again, this is you know all sort of from a human centric perspective the way the things we have available to do Are in general what a person looking at this web app would be able to do We also need to make I think one more change to our intern json file Um, well, we need to make two changes one. We need to tell this thing that we have some functional suites so those are functional Even though functional suites only applied in node. We don't actually put them in the node object That's just kind of historical inconsistency at the moment Um, but yeah functional suites only apply to node if you load this config in a browser the browser The the intern in the browser is smart enough to know that it can't run functional tests So it just ignores this We also want to specify a functional timeouts Uh, this is necessary because it didn't used to be necessary, but it has become necessary and I Just haven't put a default in intern yet Um, but the idea is when you do these functional commands like gets and finds and such, um There are timeouts so that if you know, you make one of these calls and it takes too long There's a timeout. This is managed not by intern but externally by selenium or web driver And so, uh They used to have reasonable defaults in for most browsers now there's zero And so with a zero timeout most of your things will start to time out pretty quickly So it's good to set some defaults for these functional timeouts But again future version of intern will do that This particular thing automatically All right, so we have functional suites. We have functional timeouts Um, we have our tests. Let's see if it actually works Oh, yeah, let's also, uh disable This browser stack bit Another interesting thing is interns Config file is JSON, but it's sort of an extended JSON that supports comments So you can comment things out in an intern config Let's also comment out One of our browsers here Again, just to make this a little bit faster So if we run our tests now, we're going to see our unit test run in node We're going to see firefox pop up. We'll see our unit test result happen And then we're seeing our buttons being clicked right now For our functional test And so even with the one second waits that was a little snappy So if we open it up again Just to kind of catch it again We see browser now we see it click one click plus click two Click equals and we're done. And so we see this test pass so that is Pretty simple functional test and just like our unit test We can run our functional test on browser stack or on sauce labs or whatever on different combinations of browsers and operating system So we can basically ensure that our application is behaving the way that we want In all the combinations of things That we need it to run in in addition to You know basic functional test intern has a couple of you know pre-built plugins to run other kinds of you know functional tests async Excuse me accessibility and visual regression tests And I mean these are pretty simple implementations of these tests But they are enough to handle simple cases and they again kind of work out of the box there So just to give you a quick taste for what those look like Uh, let's go ahead and install the packages we would need for those Let's grab the intern Y And the intern visual plugin So again the accessibility one is looking for accessibility checks. So like um, you know Missing alt tags and an image element for example or things like that It uses you know, there are a couple of different External tools intern uses to do that one is axe, which is a local library one is tenon Which is a cloud service that you need an account on The other you know our visual plugin test here does simple visual regression testing And so it will Um, basically take a snapshot of your page and then later when you run test It will compare a rendering of your page against that snapshot And if it differs that will tell you So our you know quick and dirty test for these will look pretty much like what's on the slide there Let's just call this one a11 y check Uh, remote wait axe.check We need to pass it the remote and we need to give it the source url That we want to test which is the same thing we're using for our functional test up there HTTP localhost 3000 We do need to import that axe in deals so that we have it Actually, I think the the uh, I think the import is called services From Y And then this is uh services dot axe Okay So this is an accessibility test and let's go ahead and throw in our visual regression test too Uh, and in this one this one's a little interesting because we have a helper Function that actually just writes the whole test for it. You know that creates the test function for us So from this plug in we are going to grab visual test Okay, and so here we're just going to call this visual test And it takes a url, which again the same as this one and it takes This missing baseline property which is telling it what to do sort of in the initial case If there's no baseline should it, you know, whine about that or should it just take a new snapshot? So we're telling it take a new snapshot. We got the parentheses there. Okay, so now we've added 811 y or accessibility and visual regression tests and again these are just Very basic tests using these these two, uh already existing plugins that exist for intern So if we run these tests Let's just go ahead and do that both of them Actually the accessibility test will fail the visual test should pass by default The accessibility test is going to fail for the same reason that react has been whining about us this whole time This missing alt text So if we go look up here, we see that our accessibility test failed a violation was logged But our visual test down here was fine So let's go take a quick look at our At what happened there. So we have this uh accessibility report So if we go look in this accessibility report We see that there is an html file. This is our report If we open this What did I do? Oh, there we go. Sorry. It was firefox if we if we open our report here. We see that it's telling us what was wrong that uh The app logo image Doesn't have alt text So if we go into our app Where we have our image sure enough, there's no alt text and again eslint was whining about this the entire time too so It probably would have caught that with our current tooling, but you can certainly imagine situations where it wouldn't So if we just add an intern logo alt tag tag there and we run our test again We will see that our accessibility check passes So that passed As far as the visual test goes that's testing visual code. So um, you know, we haven't seen any violations with that yet If we go induce if we change some visual aspect of our Application for example, let's go change the size of our little intern logo down there And let's do that in uh Actually, let's just remove it Okay, so now the logo is gone So now let's run our tests and see what happens Okay So we can see it doing the functional test the a11y test was fine, but now our visual regression test failed Okay So we have you know, we can see up here error failed visual regression We have this visual test directory that gets generated by the uh The visual regression plugin here and it has a report So we can open that report Uh, not the a11y report the visual test report And if we open that we see down here This red vertical, you know line business that Is what it looks like when the visual regression test is highlighting The problem and so the problem is uh right here and in this case, it's that thing is gone So again, it's a it's a simple visual regression test It's not, you know, you You won't be able to use it for super complex situations But again, it is sort of an example of what you can do with intern just with writing fairly, you know Straightforward easy to use plugins So hopefully, um, this has given you A reasonable overview and I know it wasn't it wasn't in great depth, but hopefully you've gotten sort of a sense Of what you can do with intern and at least how to get started with it and how to approach using intern And so here we have a number of resources We have links to Various places where intern information is the intern.io is the main website With links to all of the sub projects and it has all the you know project and api documentation We have the intern is the main repo The intern visual plugin and a11y are in their own repos there Chai js is api is what we use for assertions And uh, you know, we mentioned browser stack and so all of the web services have their own versions of this But this is where browser stack describes the different capabilities you can use Like we passed in that that project name And build name there are a variety of other properties you can Send in to browser stack And they're described there If you have questions or you need help There are a couple of good resources. There's the Gitter channel for the intern I am Always on there so you can always ask questions there And you can also always ask questions on stack overflow using the tag intern So hopefully this has Been an informative workshop and if you have any questions feel free to ask Thank you