Video details

Solving Wordle with Python & Selenium - & then running it in GitHub Actions-Michael Mintz #SeConf 22

Python
08.04.2022
English

In 2022, the Wordle game took over the world by storm. As curious automation engineers, it is our duty to find an automated solution to solve Wordle for us. I took on that responsibility and created a working Wordle-solver using Python and Selenium. As a bonus, I got that solution to run in GitHub Actions.
In this presentation/demonstration, you'll learn how all the pieces come together to make this happen. You'll learn some Python, some Selenium, some pytest, some SeleniumBase, some things about Shadow-DOM, and getting your scripts to run in GitHub Actions. -------- The "Why?" Section: 1. Why automate Wordle? Wordle is a game, and games help up learn. In the case of playing Wordle, we play because it's fun to solve puzzles. In the case of automating Wordle, that's an even bigger puzzle to solve because there are more components involved (algorithms, Python, Selenium, Shadow-DOM, etc).
2. Why run the solution in GitHub Actions afterwards? GitHub Actions is one of many available CI/CD systems. Given that it's free to use and already part of GitHub, that makes it the most accessible to people looking to try this out for themselves. People could just as easily run their scripts in Jenkins if they already have access to that. The reason why a CI/CD system was included at all in this demonstration is because automating games gives us a fun excuse to learn more about CI/CD in general (Continuous Integration / Continuous Deployment), which is often the final destination of many automated scripts that test engineers write.
3. Why SeleniumBase? SeleniumBase offers many simplifications and enhancements to the standard Python Selenium bindings. SeleniumBase also also has smart-waiting, reports, dashboards, pytest command-line options for customizing tests, other APIs to simplify Selenium commands, and multiple other enhancements. And best of all, SeleniumBase is free and open source on GitHub, which means it's accessible to all. -------- This is a follow-up to my popular post on DEV: https://dev.to/mintzworld/solving-the-wordle-game-using-python-and-selenium-1aei ...which was a follow-up to my YouTube video: https://www.youtube.com/embed/wSvehy4u_xw?feature=oembed (Since those were created, the New York Times acquired Wordle, and there have been a few changes to URLs, etc. The code has already been updated as needed.)
More details: https://confengine.com/conferences/selenium-conf-2022/proposal/16748
Conference Link: https://2022.seleniumconf.in

Transcript

Welcome, everyone, to the solving burden with Python and Selenium and running that in Gradle GitHub actions by Michael Mens. We are glad Michael no join us today. All right, thanks, Nudi. Hello, everybody, and welcome to Selenium Conference India 2022. In today's presentation, I'll be using Python and Selenium in order to solve Wordl and there are a few different versions of the Wordle website. So if anyone is not familiar with Wordl, it pretty much looks like this. You get to guess letters and figure out what the correct word is. And if you get a yellow letter, it means that the letter is not in the correct place. And if you get a green letter, it means that it is in the correct place. So I'm going to start a screen share so that you can start seeing everything that I see. This is what Word all looks like. You have yellow letters and you have green letters and you have to guess the correct word. And it gives you hints by telling you what letters are in the correct place and which letters are in the incorrect place. So we can use automation to solve Word. So if we do Pytest PY, it's going to run a Python program that guesses Wordal for you. So it's going to load up the page with Selenium and then it's going to start guessing words and it's going to take the hints that it has there and try to figure out the correct word. So here it's now on the fourth try. And there you have it. It solved it in five tries. Bluff. So if you're wondering what that script looks like, this is the Word old test. It's some simple Python, but essentially it's going to open up the New York Times website, which has the Wordlegame, and then it's going to initialize the Word list, which basically searches through a preexisting list of words so that you can see that it can generate a full list from that and use that to make guesses. Now, the full word of list does not include all possible five letter words. They've basically pruned the list in order to make it so that maybe bad five letter words don't show up as possible solutions. That way it's still kid friendly, et cetera. So if anyone is wondering how that script works, there is a Python web automation framework that I created called Selenium Base that essentially allows you to work with Shadow Dom more easily click elements. It automatically waits for page elements to fully load before interacting with them so that you don't need additional sleeps or waits in the code. This pledge base is available on GitHub. It's open source so you can see all the code if you are interested. Particular examples that we run are found in the Selenium based Examples folder and there's hundreds of different examples. And in particular here, the ones that we are going for are the Word old test and basically it goes through it uses the algorithm to determine whether or not the yellow letter is there or a green letter so that it can guess the correct word based on the hints that it had. I'll run it one more time just so that you can see it in action. The selenium based tests run with pytest. So I can do Pytest PY and it'll automatically spin up the web browser and start playing Wordl. You can see here it's guessing letters and here we go once again. It guessed it in five tries, but every time it runs, it runs a little bit differently because it's using a randomized algorithm. Now, about a month ago, the website had a full redesign and they got rid of all their shadow Dom elements, which basically is a page within the page. So in order to show you how it looked on the original site, we can use the web archive in order to see the original Wordle page before they changed it. So if we do Pytestarchive test PY, it's going to open up the web archive, which is basically a copy of all past websites so that you can see how the Word old game did in previous renditions. So it's going to be a different word than you saw before because it's using an existing version of the website that was there previously. So it guest ample and three tries there. And if you're wondering exactly how that website looks underneath, we're going to open it with the web archive so that you can see the shadow Dom that's there. So I'm going to right click and inspect the element which basically allows you to see the HTML source for the page. If you can see clearly here, I'm going to expand it so that you can see a little more clearly. Inside this website. There are these shadow root elements and shadow root is basically the shadow document object model, which basically has a website inside a website and interacting with these shadow elements. In order to do that, you need Selenium Four and you need the latest version of Chrome or Chromium browser. And from there you're able to interact with the shadow Dom elements in order to click the buttons and pretty much interact with the page. Now the shadow routes definitely complicate the interaction with a web page because you have to specifically jump into the shadow route before interacting with it. So if you wanted to say, click a specific button, I'm just going to exit out of there. You need to basically interact using the shadow route because you'll switch in and then you'll click inside. So if we were to go into the script that interacts with the shadow Dom, we have here the test vertical archive test. You'll see that it uses a special syntax such as here, to interact with the shadow Dom elements more easily than you would if you were going to directly use a Selenium command to do that. So it uses the shadow selector. It's basically a unique selector, it's a Selenium base. But essentially it'll go to this selector. It will jump into the shadow route that is there and then it will look inside for another element. And then it will let you jump inside the shadow route again so that you can click the game icon. For instance, here, this lets you close the initial screen that pops up whenever you go to Wordl for the first time. So this is interacting with shadow root elements using Wordle. So now that we have that, let's jump back into the example that we have here. So essentially, if you weren't using Selenium base to get to the shadow route, you'd need to use a command such as this to click it and it gets much longer because you have to individually pierce the shadow route elements. So first you could do something with Python driver find element by CSS selector, the game app. And then you could dot shadow and shadow root will then take you inside the first level shadow root. And then from there you'd have to find another element inside of it and then keep going through the shadow route so that it becomes a significantly long line. However, with Selenium base it simplifies the interaction with shadow roots so that this giant line here can be done entirely with just a self dot click line, self dot click game app. And then it pierces through the shadow route and lets you basically interact. I'm going to run that test again so that you can see it interacting with the particular shadow routes from the Word of Archive website. So it's loading in now. And this is the version that has the shadow root and it's picking a random word from the past. These are some terrible words here that it's guessing. Let's see, guess that in three tries I'll run it again, just that you can see that it's going to pick a completely random version of Wordal from the past and every time it runs it's going to get different words in order to get to the final word. And let's see, try number four, shell. Okay, so here the Word was shell and it took four different tries to guess that. So if you're wondering how to run any of these example scripts that you see here, all that is accessible from the Slening Base website where you'll find examples like this and many others. So let's see the one that we just ran here with the shadow root, that is the Wordle Archive test and I'll just walk through how the whole script works. So first it's going to generate a random word from the past using the existing format that is set up for it. So if you look inside this page here, you'll see that it uses the web archive which basically allows you to see a past version of a website. If say a website later changes, you can go back to a very specific date. So here 2022. This one was April 17 and it had a timestamp from that day and used the Wordle website. So it basically went through and found the past worldwide in order to get to that. So I generate a URL that looks like this from here so that I can get to the past Wordal version, and then I initialize the Wordlist and then I basically pick a random word. So I do word equals random choice, self wordlist. In Python, this random choice essentially lets me pick a random item from a list of words and the selfdoubt wordlist I already pre calculated at the beginning, which basically has all possible five letter words that will be available when you're guessing Wordal. So random choice gave me a random word and when I started guessing, I basically mapped out the various letters inside Wordle so that I can click the buttons. And after I go through a round of guesses, I basically try to calculate what letters are yellow and what letters are green because that will tell me whether or not I've guessed the correct letter or a letter is not in the correct place. And it goes through this algorithm at the top here where I modify the Word list, where essentially, based on the hints that it gives me, I prune out words that are no longer possible because the letter isn't there or it's not in the right place, et cetera. And therefore I keep shortening the list of guesses that I have until eventually I keep clicking through and there are no other guesses that I can do. And I'll eventually reach the correct word, hopefully in the six attempts that I have. So I'm going to run the Word old test on the latest version of the New York Times website and it's going to run a little faster because it doesn't have to deal with the Shadow Dom because they changed the website. So here I guess lipid it saw that the L was not in the correct place, so it's going to use those hints in order to try to guess the correct word. And as you can see here, it took those hints and we basically solved Wordle and you can run it every time, and every time it's going to run a little different because it's using a randomized word generator that basically will slip through all words that are still valid. All right, so let's go back to the main thing. I just want to make sure I'm covering all the main things that I want to cover in the presentation. Why are we automating wordl? Well, it's fun to use Selenium to play games and solve puzzles that aren't necessarily around test automation, because Selenium isn't just for automating testing of websites, it's used well for basically automating all sorts of tests. That pretty much anything that you want to automate on the web, anything where there's a web page, you can interact with the elements and essentially make anything possible. Now that we have Word all running, we can now take the script and run it inside GitHub Actions. So now that we have that, let's take a look at one of the GitHub actions that I have running that's actually solving Wordle. You'll see here in the MDM Sleniumbased examples, under GitHub you have GitHub Actions and if you click on, say, the top one, you'll see that the script has been running on Ubuntu, macOS, Windows, et cetera. So let's see if we click on one of them and we jump all the way to the word old test, you'll see that the Word guest was bluff and it got it in five attempts. So this is an example of using GitHub Actions in order to run wildly. And if you're wondering how exactly to generate the script for this, let's see that GitHub Actions file will look like this for instance, for Python, python package YML and the YML file represents YAML format, which is the format that you'll likely be using when creating automation to run in GitHub Actions. So to create that file, you could set it to run on a Cron schedule, which is what I did, which basically means it will run at the minute of every hour and it will run on the main branch. And you can specify all the various OS that you want to run on such as Ubuntu 20.04, Mac, OS, Windows, etc. E and the Python versions. So right now I'm running that script on 37383 9310. You set up the matrix, you'd install dependencies and the main one is Slutting base, which is part of the requirements file. And you run Python set up by y install from the Get clone and you'd also install Google Chrome. You can also install Edge and run it there as well. And once you've installed the drivers and you can do that with the commands such as Slinging base, install Chrome driver or slamming base, install Edge driver or Gecko driver, et cetera, those will download the automatically the Selenium based web drivers that you need in order to interact with the web browsers that you're spinning up. So from that script, there's multiple different examples here and the one in particular, it's the Word old test and I run that at the very end. And in case they can't get it in the six guesses that it has, well, it's not a total guess because it's using hints. So it's very educated guesses where it prunes out the words. A command such as reruns equals five. It's basically a Pytest option which will allow you to rerun a script if it fails in the case that Word will, because there's a lot of randomization involved, if it's like a really tough word to guess, then I make sure that there's plenty of room to get that word. So that when it actually runs in GitHub Actions, you can see all right, I want to know how it's running on the latest version of Windows and it runs the script. And let's see here. It took six attempts to get bluff, et cetera. So this is GitHub Actions, and it's really easy to just work from an existing YAML file. In order to set this up, I'm going to go into the chat box and see if I can answer people's questions. Can you provide the GitHub repo link? Absolutely. So the GitHub repo link for Selenium base, I'm just going to copy that and post that into the chat. So that is the link to the main GitHub page. If you're wondering where all these GitHub actions are running. So that is just this link here. I'm going to copy that into there. So that's the GitHub Actions that's basically running all those wordle solver tests on continuous integration. And GitHub Actions is one of many different continuous integration systems. Jenkins is another popular one. There's GitLab here. We're using GitHub Actions because it's completely free for open source projects. So if anyone wants to run their own workflows on a Cron schedule, they can easily do that for free. And if people aren't sure how to create a new workflow, you can go into Actions new workflow, and I'll even show you how to get started so that you can start creating your Python packages, et cetera, that you might want in order to basically create a whole GitHub actions workflow for that. And you can use, like, Anaconda, there's a Python application there's published Python Package, or just Python Package, which is the simple one that I use, and it helps you get set up. But if you're looking to run something more advanced, you can use the existing scripts that I've already created. And if you're wondering just a little bit more about the GitHub actions for the Sunny Bass examples, all that can be found in the let's see, I've got an MD Men's get up folder, sunnybase examples. And if you go into Githubworkflows, this is the general location that you'll find your GitHub action scripts. That's where you'll find your Python Package YML file, and you'll immediately get the script there where you can set the Cron schedule, and you can set how many parallel tests to run at the same time, such as here. You basically copy paste the script and then modify it. You can easily make changes so that everything is already set up and ready to run. It automatically takes care of installing web browsers for you for your tests, as well as showing you how to structure a test run. And if you're wondering, you've probably been looking and seeing that we're using Pytest to run all the scripts, and Pytest is essentially Python unit testing framework. I know I'm going a little bit out of order, jumping around, like getting the main examples and going back taking a step back and saying, oh, by the way, Pytest is used to run all the scripts that you see here. And it's probably Python's most powerful unit testing framework. It gives you a nice structure and gives you output so that you can see what's going on and you can even generate a fancy report. So let's say I run the script again and I want to add a few additional command line options such as dash dashboard and HTML equals report HTML. If I run the world test now, it's going to create a dashboard and a full test report of everything so that you can see exactly how everything looks as a test report. And pytest HTML reports is a common pytest feature. So I'm running it again, but this time I use the advanced commandline options so that I can generate the report HTML file. So if I open that, you'll see that it's generated basically a dashboard. It was one test that ran and it passed and you get a nice little pie chart and it shows you that it ran the word old test and it took a duration of 26.67 seconds. And it also shows you useful things such as in your environment, what your platform was, which was Mac OS. Here the Pytest version that you had, the different plugins that you had. So this right here is Pytesthml with the Selenium based dashboard included. You'll see the Pytest HTML version and other plugins such as Exist, which lets you run Pytest tests multi threaded. There are also other plugins that can be used such as rerun Failures, which will let you rerun failing tests. It's going to make it a little bigger because you probably can't see it when it's small. Rerun Failures, which lets you rerun a failing test. You can use Pi test ordering to set the order of tests, et cetera. And of course there's Selenium Base, which is the Pytest plugin that lets you do browser automation and everything like that. So it ran one test and it gave you the output there. And if you're wondering a little bit more, a more simple example of Selenium base, since we jumped right into some of the most complex tests that you'll have, if we wanted to do a much more simpler test such as let's go to test Swaglabs. I think they are one of those. So Swag Labs is the sauce demo website. It's one of the sponsors of Selenium. Comp. They have a nice ecommerce website where you can essentially log in, add an item to a cart and then check out. So if you're wondering how Selenium Base works, there's lots of simple methods such as type, so that you can type into a CSS selector, which is the default, which makes it easier, so you don't have to specify whether or not it's XPath or CSS. It'll auto detect and then it will go through. I'll show you what the website looks like just so that you can see how Sauce demo works. It's a pretty easy website for basically doing all sorts of things on an ecommerce website. It gives you the login information at the bottom, standard user and then the password for all users and secret sauce. You log in. You can add an item to a cart. Once you've added the item to a cart, you should be able to click on the cart and you'll be able to check out. Then you can just type in some fake data and doesn't really check that. The main idea is that it's a simple website that you can use for practice testing and you can see you've added an item to the shopping cart and you can click Finish and then it says thank you for your order. And you can use Selenium Base to automate that as well, just to take it down from Shadow Dom and complex Word solving. It's more deterministic because the website is the same every time. It's not like a new item in like a new list of items every day. So if I run Pytest test Swag Labs and they'll just do a simple test just so that you can see Selenium Base handling all sorts of things, so it's going to log in, it's going to add an item to a cart, it's going to check out and verify. One of the cool things that you can do with Selenium Base is slow the test down with demo mode so that you can see exactly what the test is doing as it runs. So if I do Pytest and then the test I want to run and do demo as a Pytest command line option, it's basically going to highlight the various items that are on the page that it's going to interact with. You can see that it's highlighting the fields that's going to interact with. And whenever there's an assertion, it's going to show at the bottom of the screen, like CSS selector or here at the bottom the inventory item that it's checking. So you can actually see not only where it's clicking, where it's typing, but you can see the assertions that are being made so that you can say, oh, if you add an item to a cart that the text says remove on the button instead of add to cart. So it makes it really easy to see what tests are doing, which is very convenient. And you can see that demo mode, it slows everything down, but it shows you what your test is doing so that you can see everything that's going on. So here it's verifying that various buttons are there like cancel. And here it's going to slowly type in the fields that are there and you can see that's verifying all that. That Verifies, that the test, all the things T shirt appeared in the cart and it's showing all that that's there. So demo mode is one of many available command line options that you can use alongside your Selenium based tests in order to get more out of the test. If you're wondering about additional options, let's just go over to the main GitHub page. You can see the Pytest options for Selenium base. You can easily change the browser. So as you saw before, Chrome is the default browser. If you don't specify it, you can change the browser so that it runs with Edge, Firefox, even Safari. Let's see, you can multi thread the tests by using Dash and this is using some built in Pytest multiheading. So if you have a lot of tests that you need to run at the same time, pytest multi threading is the way to do that. What you saw earlier was the dashboard with the HTML report that I showed about 1510 minutes ago that basically can give you a full report of all your tests after it finishes running. There are lots of ways to debug tests such as using the PDB option, which means that if a test happens to fail, it will pause and then give you a debugger so that you can do live debugging. Right then this will let you rerun failing tests. If you want to do that, there's a recorder option so that it can record tests and I don't know if I have time to show you that, but the key thing here is if you want to use one of these Selenium servers from our sponsors, such as browser stacks, slabs, lambda test, etc. You can run any of your pipes tests on one of their Selenium grids by specifying the grid server, the key and the IP address and maybe the port that you'll need. It could be 80, maybe sometimes it's four four four. And if there's authentication that is in the key now essentially let you run your tests on browser stack, sauce labs, lambda test, etc. E and utilize all of their advanced features. There's also Slimming based mobile mode which will kick off Chromiums mobile device emulator, so that if you have a test that has a mobile version of a website, you can just do that and it will use default mobile device metrics. So if I just do, let's see, pytestmyfirst mobile that is going to go back to the Sauce demo site, but it's going to use the mobile version which as you can see, it's slightly different from before. And you'll be able to run a mobile test, not just the regular web test, just by adding the mobile as a command line option. And of course there's a lot more options that you didn't get to see here, but there is a full list on the Selenium base page on GitHub. The dashboard looks like this. So if there's a lot of tests, you can see your failing tests, your passing tests. Here's what it looks like with some failing tests at the top and there's logs so that if there is a test failure. You can see what went wrong. And we didn't run any failing tests right now, but I'm just going to do a quick, one more quick suite of two second test so that you can see what the logs look like when a test fails. And it'll take screenshots and all that and give you all the info. So here we have a dashboard HTML. So if we were to open that inside a new window, you can see that here it ran four tests where two failed and two passed. And inside the data folder you'll have things such as basic test info, which shows you the test that you ran, the last page that it was on, the browser it used, the driver and the date that it ran with my time zone, et cetera. And then the stack trace. And the most important thing, it will have the screenshot right here that you can click on it and see what went wrong. All right, let me go back to the chat. So we have Q and A. Let's see, we have a question from Vishnu in the Word puzzle. How is it identifying yellow color and proceeding further? And how is it identifying green color? Okay, so inside the website when you're running vertical, I'm just going to quickly get that back so that we can see chat window is blocking the screen. Oh, there we go. Okay, so on the website itself, I'm going to show you how that looks. So if we go to Wordle, it's going to quickly jump directly to it. If you guessed a word such as stuff, I think the answer was bluff. And we're going to do enter. If we right click and inspect the element, it will actually tell you whether or not it was a green tile or a yellow tile. If you inspect it inside there, it will be in the HTML. Data state equals correct. That is the main thing there. So if you have a word in the correct place or a letter in the correct place, it's going to show a data state property equals correct. Now if it is in the wrong place, it'll say or the letters not there at all. I go here, data state equals absent because there is no T in the word. So if I were to let's do blunt where letters aren't going to be in the correct place, that's not a good one because let's do here we go. There's a yellow one. So here the US in the incorrect place. So you can do data state equals present. And inside the HTML, based on that, you can say, okay, the letter U is not in the correct place, but it is in the Word. So the CSS selector for that would be data state present there. All right, let's see back to Q and A. Any more questions? Technical? All right, well, we're going to do one more quick thing. For the recorder so that you can see how the recorder works. Let's see. Here we go. Let's move that out of the way. We do Space Recorder. It's going to start the recorder desktop app. And let's say we want to do a quick recording. Let's do Saucedemo.com and we're going to click Record. It's going to go here. And if I copy Standard User in here, and I copy Secret Sauce in here, and I do a login, and I'm going to add three items to the cart. And then once that's set, I'm going to click on the cart item and I can verify that things are there. So basically I could verify that that says it's 1599, that's 49 and that's that. And then I am going to click check out. I'm going to type in Hello Selenium, and then 12345 continue. And then I'm going to click finish. And I want to verify that it says thank you for your order. So I'm going to switch into Assert Element mode, and then once I am done recording the actions, I can type C and it's going to generate the recorded script. So this is just one quick way where you can use the Selenium based recorder to generate a full script. So you saw here that I went to this website, it saw that we clicked the item and then it generated the script. So that will allow you to quickly play back the script that you just saw so that you can quickly create tests. And if you need to generate a new test on the fly, you can use the recorder to do that. So here you have it, playing back a recording. So Selenium based recorder is sort of like Selenium IDE, except it's Python based. Let's see if I can answer anyone's question. So that pretty much covers everything. Hi Niddy, how are you doing? Thanks for the session, Michael. Anyone have any questions? I didn't get to cover everything. But as you saw that you can use Selenium base and Python and Selenium to solve Wordle and interact with the website easily. Interact with Shadow Dom. And you can generate scripts easily with the recorder and use additional command line options so that you can see reports and et cetera. Or Demo mode so that you can see exactly what your test is doing. And you can easily connect to a grid server for a browser's, Daxo Slabs, Lambda test, etc. And our sponsors here, so that you can run your Selenium based Python tests on one of their grids. Thanks Michael, for sharing your experience with us today.