So, watched Angie Jones live stream,
an automating session,
where she was automating this Tic Tac Toe game with Java and
WebDriver.
And I think it's really useful to see multiple approaches automate in the same
application because it gives you a feel for options,
different approaches and to see pretty much the similarity
between different automating tools.
Because the more we see different programming languages,
the more we get a feel - they're all relatively similar and we lose a lot
of fear in terms of programming. So I'm going to try and automate this,
but I'm going to do something different and automate it in the browser itself.
So the first thing we have to do when we're learning any application to automate
it, is look at the application itself. So here's the game.
I've started playing it and I've inspected the important parts of the
DOM just to get a feel for how this works. So I'm clicking on the spaces.
Let's click on the top left this time, which is this one.
And we can see that there's a class underneath there and I click on it and it
has the X. So everything that we need is in the DOM.
I don't need to access anything in memory when I'm going to automate this,
which is useful.
And so I click on it and we can see here that the board has an extra
class.
And down here there is a restart div over the
top of this, which is currently "display: blocks;". So we can display it.
I click on that and it is, "display: none;".
So I know that the game is essentially finished when the
restart is displayed.
So just to hone in on this with some CSS
selectors, because we're going to need some of these. So I can find out,
"restart".
I can find that element in the DOM.
So I'm just going to make a quick note of that.
So making a quick note of that in my notes.
This is the "restart" div overlay
and the board is the div "board".
I don't think I'll probably use that,
but what I want is access to all the squares. So if I say div.square,
then I can access all the squares that are in there.
So that's going to be useful to know. So now I want to try and do is figure out,
can I actually click on this? So if I pick div.square.
Let's go into the console. In JavaScript,
what I want to do is find the document and then find the selector.
So I will say querySelector, I'm going to say querySelectorAll.
So that's going to use a CSS query to return everything.
And I want to return all these squares.
So div.square that returns a node list of nine.
So now I have all the squares on the screen, right?
So I can find these. So let's see if clicking on it works.
So I'm going to pick,
all the squares and choose the one on the top left.
And I'm going to issue a click event and that doesn't work,
so I can't click on the squares.
So let me just inspect this and look at the Event
Listeners. I can see there's no Event Listener for the,
click. But there's one for mousedown.
So what I'm going to do is I'm going to take that first one
and I am going to dispatch an Event to it and
it's going to be a new Event and it will be a
mousedown Event.
There we go. So I can click on things using a mousedown Event.
That's good to know. Let me just take that and put it in our cheat sheet.
So we've got div.restart div.square, mousedown,
what else will we need to do? So we'll need to be able to restart the game.
Let me just get the game in a finished position.
There we go. So "board tie". So now if I do div.restart
or go, that's the full thing.
So in the console I'm just going to say document.querySelector,
just to return one value. And I want to return the element for the div.restart.
I'm going to try and issue a click on that.
So that's how I can restart the game.
There we go.
I'm just making notes of all the kind of,
rough commands I'm going to need to do this. Now,
one of the things that's probably useful is to know whether the game needs to
restart or not. So in here,
what we've got is the style and it's the display,
display. So I can access the display style,
which is currently none.
And if I look at the display style now it's "block".
So I can tell whether we need to restart the game or not based on the display
style for the restart.
Other things that would be useful to know is where have we gone anything in
here? So at the moment there's one square selected.
The current selector that I've got is
all the squares,
but I can't tell from that whether they're selected or not.
What's interesting is that each one of these has
a div underneath it and that div has a class
as to whether it's selected or not. So in here,
if I do div.square
and then I want the div underneath that, then there's nine of those.
And it's the hidden secret div under there.
But what I want are the divs,
which do not have a class of X and
which do not have a class of O.
So there,
I've now selected all the non selected squares, all the squares,
that don't have an X or a 0 in it. So there's 1 of 8 there.
If I put an X in here and run this now we've got
2 of 6. So there's six positions I could put in. One,
two, three, four, five, six.
So now I have selected all the items which don't have something in it.
That's going to be useful to know so I can find them all.
So I can't actually dispatch events there.
Let's have a quick look at the dispatch event again.
So the dispatch Event is currently working on the
top. So this is saying find all the squares,
find the first square - dispatch an Event.
What I want to do is find the first square
with a div directly below it,
which does not have an X or an 0 in it. And that's what this does.
But if I dispatch an event new mousedown to it,
does this actually select it?
So this will be this here sending a mousedown Event.
So that sent the mousedown Event, but it hasn't triggered in the system.
So I could either,
instead of sending it directly to this div,
I could send it to the parentElement,
which we've already seen will respond to that event or I
could send it to the div itself,
but I would have to,
bubble up the Event.
So I have to say "bubbles" and then
"true". So that's, so let me just change this as 0.
So it will always be the first one and we're not sending it to the parent,
we're sending it to the Event itself.
But "bubbles" means send it to this element and let it bubble up the chain so it
will eventually get sent to the parent. So I've got a choice.
I can either do this or this to actually
click on one of the squares that doesn't have something in it.
Do I need to know anything else? That's probably good enough to start.
So let's write a bot, right? What I'm going to do,
is I'm going to go into the snippets view
and create a new snippet. I'll call this tic tac toe, rename
tic tac toe.
And I'll put this code on just, or something.
So let me just make a note of where we're at.
I'll get rid of this set of notes by putting them in my, code.
Right, so now I've got everything I need, pretty much to hand.
So the first thing I'm going to do is I'm going to create a bot that is simply
going to restart the game for me.
So I need a function and I'll call this playGame.
But to start with, all this is going to do is restart the game.
So it will say if, if we need to restart.
So if the restart, let's say,
var restart and this is checking the overlay div.
So document.querySelector ("div.restart") gets the overlay div.
Then I'll say if restart.style.display==)
and it was block or none. If it equals "block",
then we need to restart the game.
So I will say restart.click because this can take a click Event.
So if I now have nothing to run this function,
what I can do is I can save var playBot and that's
going to equal a window.setInterval,
which means it's going to run this function (playGame) every
500 milliseconds. And every time I create a bot,
I like to make sure that I can stop the bot.
It's going to say window.clearInterval
because otherwise it would just run continually, (playBot).
And I'm going to add that as a comment. So what we've got here is,
let's see if it works. So run it. There's no syntax errors.
If I play the game
Now, you see I'm not clicking again to restart the game so I have something,
running in the background, which is detecting that we need to restart the game,
finding the restart thing and clicking on it.
So I have a bot that is resetting the game for me.
So let me just paste this into the Console and now that bot is stopped.
So now I have to actually click to restart the game, right?
So we've started writing a little bot here that can play the game.
So the thing "else' here now is }else{ I need to play the game.
So basically I've got code that says if we need to restart, we can restart.
Otherwise I need to play the game.
So what I'm going to do is I'm going to take from my notes the code that
says - find all the things that are
not selected.
So we'll call this var squares.
If I could spell that would be good. And what this is doing,
this is saying, it's what this is saying is, using this querySelector,
which, remember finds everything that doesn't have an X or an O in it.
So find all the squares that I can click on.
Then what I need to be able to do is randomly choose one.
So in JavaScript, say this returned 6,
what I would want to do is Math.floor
then (math.random),
which returns a value from zero to one and then I want to times that by
nine.
So this should continually give me a random
integer from inclusively zero to eight.
Math.floor turns the floating point into an integer.
So now if I take this.
Now you can see what I do is I jump between the sources view where my
JavaScript is and the console where I'm typing things.
So squares instead of nine, I want
squares.length. Let's just double check.
I'm pretty sure it's length - Console - .length.
There we go. So I have found all the squares that don't have anything in it.
I know how long it is.
So this is going to be the var item and then
what I will say is squares and then the index is item.
And then I want to send a click event to it.
So we know how to do that. And let me just copy out the code.
So I'm going to choose the parent element then dispatch an Event( 'mousedown').
So this in theory should,
hopefully if I've done this right, iterate round,
play the game every 500 milliseconds.
If there's a restart div on screen, click it to start the next game.
If not, then find all the squares that have not been selected.
Randomly select one of them, then
click on it to play the game. So that seems remarkably simple.
Let's run this, see what happens? I've still got the clearInterval bot.
Okay, so now we've written a bot that will just keep playing the game.
It's rubbish because it's randomly selecting stuff.
I think the game has some AI to actually play it better.
But you can see that rather than a random selection of the
square.
If we had an algorithm in here to choose a square that made sense in the
current layout,
then we can have something that plays the game within the browser itself.
So most of the time when I'm automating these
type of systems is spent working out - how can I make it
do the things I want?
What states am I using in the application itself
to work with? Then experimenting with code in the Console.
So I'll use the console to try things out, see if it works.
Then use the snippets to build up basic code to play the game.
And there you go. So now we've automated tic tac toe,
badly in terms of the algorithm, in the browser.
So if you find this useful,
I have a course on automating in the browser using JavaScript at
testautomationu, which you can find at testautomationu.applitools.com
and I'll have links in the description or wherever you found this video to
Angie's video, the testautomationu course, and my website eviltester.com.
So make sure you subscribe to me on my mailing list at YouTube wherever, right?
If this is useful, hope it is.
But we can do quite a lot of tactical automating within the browser
to automate games and applications with very minor amounts of code once we know
what we're doing.