 inside of our page objects. And we can't pre-allocate objects to describe the page. And even if we could, we wouldn't want to because we could have 1,000 items in our list. And who wants to spend time waiting for 1,000 items to be pre-allocated when we're probably only going to look at one or two of them, right? What we're really looking for is an easy way to access items in the list. And we can do that two ways. We can either access items in the list sequentially, or we can do a search and look for items in non-sequential manner. So before we can talk about how to build a page object for a list, we should first investigate how do we use lists. So lists tend to have some kind of metadata around them. And this metadata helps users understand how it helps users interpret the information that is in the list. So metadata includes things like how many items are in the list, or what are the headers for the list. Also, lists tend to provide back to the user a common structure. So the items in the list are usually structured very similarly. We don't have lots of different types of items being returned back. They all pretty much look the same. They have the same fields within them. And there's a countable number of items. So we don't get an infinite number of items back. We can count them and we can iterate through them. Generally, we like to be able to read the information in a list that makes it easier if we're trying to find a particular value or a particular item in the list. We can read items in the list. And lastly, we like to be able to interact with the items in the list. So what I mean by interact with them, list items are just summaries of results that are provided back to the user. And usually, there might be a link or something in that list item. And if we click on that link, we can get more detail about the item that's being summarized in the list. So I'd like to introduce to you a pattern called the item list pattern. And it encourages users to think about the problem of building page objects for lists of items using two participant classes, a container class and an item class. So in this pattern, we use the iterator pattern to help enumerate items. We use something called template locators. And then we also use another design pattern called the factory method pattern. So we'll start off by looking at what is a container. So the container class has two main responsibilities. The first responsibility is that it provides access to the list metadata. So these are things like how many items are in the list. The second responsibility of the container class is that it gives us access to items in the list. It doesn't tell us anything about particular items in the list, it just lets us have access to a particular item. The item class, on the other hand, represents a single item in the list. So if you're looking, if you want to query the item, you would get an item class object and you can ask that item, what is your value? And it would also be nice if that item class could be switched to represent another item in the list without creating a new object. Because that way we can quickly go through the list. So what we'll describe is an abstract container class and an abstract item class. And then from the container in the item class, we'll create two concrete classes that actually implement a list. So the container class, as I mentioned before, has two main goals or responsibilities. The first responsibility is that it allows the user to access list metadata. So one of my favorite questions to ask a container is, how many items do you think are in the list? Usually on the website there's a little part, maybe above the list or maybe below the list, where it actually lists the number of items in the list. And so if you compare those two, they're not always the same. And that's a good sign that you have trouble. Second responsibility of the container class is to provide access to items in the list. And there's two ways we can provide access to items in a list. The first way is we can provide sequential access to items by providing an iterator. I think everybody's pretty familiar with the iterator pattern, but we'll go over it just for those who are not. So iterators allow users to get sequential access to elements in a collection of items without having to know anything about the structure of the element or the structure of the collection. So a lot of languages actually provide a way to create iterators in them. In Python, you can use the iterator protocol. The iterator protocol in Python just asks that you create a method called iter. And that returns back what is referred to as an iterable object. In Python, an iterable object just means that you have a method called next. And every time you call the next method, it returns back to you the next element in the collection. In our implementation here, we keep track of the current item. And every time we call next, we increment the current item by one. And then we do a search to retrieve that item in the list. And then when we're finished iterating through the list, we raise a stop iteration exception. Question? This is, I guess, the iterator will not provide you with a random search, but we will talk about different ways that you can, I guess, pseudo-randomly search through the list. So the next topic is non-sequential access to the list. And by non-sequential, I mean that we can search for items by position, where we can say give me the fifth element in the list. And so this serves to your question about random access to items in the list. Say again. No, but that is something that you can implement in the pattern. So the pattern is kind of general, so you can add in your own methods for searching for items. And so being able to search for items by some kind of string, it's kind of touching on the next topic, which is searching by property. So we can say that an item has some kind of value, and we want to search for items that match a certain property, match a value with a property. So along the way, in this container class, we never actually talk about, in this abstract container class, we never actually talk about what the item looks like. We only describe, we only say that there is an item object out there, and the container is just responsible for returning that item object. And the reason we do this is because the item that's going to be returned is really specific to the container that is specific to the implementation of the container class. What I mean by that is that if we're searching on eBay in an eBay list or an eBay container, then we're going to want to return back an item that is an eBay list item. If we're searching on Amazon and we get back lists of items, we want to return back an Amazon list item. And the same goes for the HubZero websites where I work. We search for support tickets or resources. We return back a list that matches the container. And so when we have this kind of situation where we can't describe the actual object that we want to create inside of our abstract class, we should start thinking about using the factory method pattern. So the factory method pattern describes a way where we can define an interface for creating objects without actually defining which class is going to be instantiated. We leave that up to the subclasses of our abstract class. So for example, in our abstract container object, all we can do is define that there is an item class and that that item class takes some arguments. And then in our getItemByPosition function, or our factory method, we actually instantiate whatever the item class is, if you leave it blank and it ends up being none, then you get a none object. But whatever that item class is, we instantiate that with a set of arguments that were provided to us by the subclass of our container. And we also give it an item number. So what this means for the implementation is that inside of our concrete class, in this example, tools list class, we have to set the value for an item class. We have to describe what kind of item we want returned back to the user. And in this case, we're going to return back a tools item object. And the tools item object is just a subclass of the regular item class. So we kind of covered the container, how a container works. Let's move on to talk about how an item object works. So if you remember in the beginning, we had four things that we wanted our item object to be able to do. The first thing we wanted the item object to be able to do was represent a single item in the list. And we can achieve this by just associating some kind of numeric value with our item object. Next, we wanted item objects to be able to be updated dynamically. And so this is where we start to run into a little bit of trouble. Inside of our page object, we're used to sending locators that are hard-coded. So we may have a whole dictionary of locators, and we hard-code each string in here. If we wanted to refer to the first item in this list, we would hard-code a one into our locators. And if we wanted to refer to the second item in the list, we would hard-code a two into our locators. The problem is that this doesn't match up with what we wanted in the beginning. We wanted not to have to create an object for every item in the list. That makes our automation script slow. What we really be interested in is if we can somehow figure out a way to just put an n value in here. And then later on, we can substitute in a value of our choice that is linked to which item in the list we want to represent. So that's the idea behind a concept called template locators. I first learned about template locators from developer Adam Goucher of element34.ca. And he had a video online where he described that you can hand your page object a string with a template inside of it as the locator. Now, of course, this is an invalid locator, right? But whenever you want to use your locator, all you have to do is format that string with the values that you're interested in putting in there. And then you get a valid locator. So we can apply this to our item object. Instead of passing in a locator dictionary with hard-coded locators in there, what we really want to do is pass in a template, a dictionary of locator templates. And then any time we want to update which item our object points to, we can call this update item number. We give it an item number. And then that item number is substituted into the templates, our locator templates. And we get new locators that represent our object. The next two parts cannot be implemented inside of our abstract item class because they're specific to the type of object that we're creating. The third requirement we had for our item objects was that we wanted our item objects to have some kind of value. So our usual interface for doing things, if we have a checkbox on a web page, then we would call the find element by ID or whatever your locator style is. And then we would ask, if we wanted to get the value of that HTML element, then we would ask, is it selected or if it's text box, we would ask for the text or something like that. And instead of doing that, we should be thinking about using page objects that are a little bit lower level. So if you have a checkbox on a page, then you should create a page object that represents the text box and then knows how to set and get the value of that text box or checkbox or whatever. So similarly, if we wanted to set the value, we could use an accessor function or a property to set the value of our checkbox. This same idea applies to items in a list. So in the previous case, we were working with very simple page object that represented a checkbox. But if you have an item in a list, you may have an aggregate of page objects. And so we would still like to be able to say, what is the value of this list item? And the easiest way for me to think about the value of the list item is just to summarize all of the pieces of the list, all the readable data within the list in a dictionary and call those properties of that list item. So what we have here is a function. We call value. It's just a simple getter function. And we fill it with the different elements that are inside of our list item. So in a tools item list item, we may have a title, some details, and an alias that all represent properties of the tool that is being represented by that list item. And we can just go ahead and grab the values of, so for links, we just want the link text. And for details, details is a text, probably like a read only text field or something. And we'll just get the value of that text field. So this kind of function is actually used when we do searching, if we search by property, we would be able to say we want a list item where the title is equal to x. Or if you say it starts with this, then if it starts with a particular letter, you can say your title, you could write a function that says your title starts with this particular letter. So I'm a big advocate of nearly all page objects being able to say this is my value or having a setter function to say set this as my value, because it really makes your automation scripts easier to use. The last piece for the item class is that we want to be able to interact with the item. And this is only natural. I mean, this is the reason why we build page objects. Page objects represent a piece of a web page, and it provides access to the services of that web page. So if we have links on our web page, it's only natural for us to want to actually click on those links or go somewhere. So we can provide little helper functions in order to expose the service of clicking on that link. So that's the basics of the item list pattern. Of course, the original problem that we were trying to solve was that we don't know how many items actually come back in a list. And we were able to solve that by one, not hard coding our locators, use the locator templates. Two, don't pre-allocate your elements. Use something like the factory method pattern to allow you to get elements on demand. Three, we wanted easy access to the elements in our list. And so we allowed objects to be updateable. So the last two, we wanted to be able to access items in the list, so we provided an iterator for sequential access, and then we also provided some search functions for non-sequential access. So another area where I found difficulties with building page objects is iframes. So in iframes, I guess we'll start off with the motivation, which is on the Hub Zero websites, we ask users to upload data and contribute resources to our website. That's the only way our websites can continue to grow is if we have user participation. And part of the process of uploading a resource, we ask the users to describe the type of resource that they're uploading. And so this is a field from the web form where we ask the user to describe what resource they're actually uploading. And this particular one is just a simple text area field. It was very easy for me to write a page object for it, and I was happy when I got it done like five minutes later. All right, so recently our web developers started adding a more complicated field in order to allow people to upload or describe the resources that they're uploading. This is using a CK editor. For those of you familiar with CK editor, it actually runs inside of an iframe. And I thought that when I first saw this, I said, hey, this is wonderful because it allows our users to add rich text to their descriptions. And then when I started looking deeper into it, our users realized, oh, man, this thing's running in an iframe. So the least amount of work I'm going to have to do is to build another page object to deal with getting into the iframe and manipulating this widget. But then I realized that once I was inside the iframe, I could use the same page object as I was using before in my text area. And so that started getting me thinking, do I really have to build another page object if I already have one that works, except for the fact that I need to be inside of an iframe? So before we tackle that question, let's just review how iframes work. This is a web page that has two different iframes and three different input fields. The HTML for this web page is listed down here. And you can see that it has two items in this HTML page. The first item is an input field, i0. And it is located in something that we consider, we call the default context. This is where we work all the time when we're in Selenium. We're working inside the default context. We're not inside of any frames. Everything is good when you're searching for elements. The next item in this web page is an iframe. And the iframe is called frame one. And it sources this HTML page, innerpage.html. And the basic way iframes work is that they just refer to another HTML page. And then they load that up into a frame. So if we take a look at innerpage.html, we see that it too has two items in it. So it has an input field, i1. And i1 is considered to be inside of the frame one context. So that means that in order to work with it, we have to actually traverse an iframe in order to get into it. The other element inside of this web page is another iframe. Who does two iframes embedded within each other? I've seen it on some of our websites. So it has another iframe. Its ID is frame two. And it sources a web page called another page.html. So if we look inside the source of that, we see that there is an input field in there. And it is within the frame two context. Don't forget, frame two context is within the frame one context. So we have quite a conundrum here. We have to figure out how to operate this i2 object through all of these iframes. So if we wanted to build a page object for i0, very simple. We probably have a couple of getter and setter functions. I like to use the function value for my getter and setter function. So then I could just say element.value and get it or set it. We might also have another function for appending. And the append function does everything. It just writes values to the item. It doesn't clear it out or anything. So sometimes that's convenient. All right. Nothing special about building a page object for i0. Don't have to switch any context. When we get into working with the i1 object, we can use a text input object. But we're going to call it text one frame. So this is a page object that gets us into one frame. One iframe. And you'll see here that there's a little bit of extra code added here. So the first thing we do is we find the frame one element inside of our HTML DOM. And then we call switchToFrame. So we have to traverse the frame in order to get into the right context. And then we can operate. We can run our same three functions, find the element, clear it, send keys. That's kind of our core setter function. And then the last thing we want to do is switch back to the default context. Because if you don't switch back to the default context, then everything you search for afterwards is going to fail, unless it's also within the frame one context. So we actually have to do this for all of our functions, and I didn't have enough room on my slides to write all of this out. So I just put dot, dot, dot here. And you can imagine what the other functions look like. All right. Onto object i2, which I remind you is in two different iframes. So if we look at our setter method, we first traverse frame one, same way we did for the i1 object. But then we do it again. We traverse frame two. And then we run our core actions. And then we switch back to the default content. So we could go on and on with this, if you have three frames, four frames. Nobody wants to build page objects where all they're doing is copying the original page objects and then keep adding in all of these iframes. That's not the way I like to spend my Saturday afternoon. So just to summarize, when you're dealing with iframes, you have to enter and exit the context before you work with the item. And then you have to exit the context afterwards. If you have page objects, you probably have to manipulate all the methods in your page objects. And the question still remains, do we have to create new page objects, really? I mean, what we're really interested in doing is having a simple function that works when there's no iframes and performs the core actions. And then if we come across the same page objects within an iframe, we just want to wrap that one simple core function with some code that will enter the iframe, run the function, and then exit the iframe. And if we have to do it twice, we just wrap it again. So we enter the first frame, we then enter the second frame, we run our core actions, we exit the second frame, then exit the first frame. That's what we're really interested in. And that's the idea behind the iframe wrap design pattern. So the iframe wrap design pattern allows us to use old page objects. We're getting some code reuse out of this. It allows us to use our old page objects inside of iframe just by simply wrapping all the methods. Oh, by the way, it uses the decorator design pattern in order to accomplish this. So anybody not familiar with the decorator design pattern, the main goal is to attach additional responsibility onto an object, usually dynamically, and without changing the interface of that object. So we usually see this being applied at runtime. This is not something you tend to do before you build your object. The main advantage of doing something like the decorator pattern is that you can change a single object. You don't have to change the class where it will actually change all of the objects of that class. You can work with a single object and just change that one object. So if we had an object, little a, and it instantiated the class big a, and it has an attribute called f that points to some kind of function. If we wanted to wrap this page object, or this, I guess it's just an object, we want to wrap this object. What we would do is we would first build some kind of wrapper function, and we would break the connection between our attribute f and the function object, and then reassociate our attribute f with the wrapper function, and let the wrapper function call our original function. So it's a little bit of software wizardry going on here. We can apply this to our text page object. So similarly, we have a page object that represents the I1 object, the first input in the frame one context. If it has a value attribute that points to a function, what we can do is create a wrapper function around that that will eventually call our function. And this wrapper function takes care of getting into I frame context and exiting the I frame context. And then we kill the association between our value attribute and our function, and replace it with an association between our value attribute and our wrapper function. So how do you create decorated page objects? The first thing you have to do is you call a function something like I frame wrap. And the I frame wrap function takes in two parameters. The first parameter is the object that you are interested in wrapping. And the second parameter is just a list of frames that you have to go through before you actually get into where you can operate with the page object. So for our I0 object, we don't have any frames, so we don't need to wrap it. For our I1 object, we want to create a page object called text. And we have to traverse one frame in order to work with that page object. And for I2, we create an object, a text object, and we have to create two frames. So first, we have to traverse the I1 frame, and then second, we have to traverse the frame too. So within the I frame wrap method, it instantiates an object called I frame tracker. And the main purpose of I frame tracker is just to help us with getting into and out of I frame. There are two functions in here that are pretty interesting. The first function is a method called wrap callable attributes. So the purpose of wrap callable attributes is to figure out which attributes inside of our page object are actually callable or methods. And then it wraps those attributes. So inside of Python, there's actually a lot of attributes inside of your object. And some of them are data members. Some of them are callable attributes or functions. So it accepts a page object as its O parameter here. And then it just goes through the class, looks for callable attributes. And then once it finds one, it calls this other method called wrap attribute. So the wrap attribute method performs the actual wrapping. It creates a wrapper function. So the first thing it does is it switches into the appropriate I frame context. We've actually calculated which frame level we should be in up above. And then it calls your method, your original method. And then it switches back out of your I frame context. So you can already see that this is very similar to the code that we had to write manually when we were building page objects to represent objects inside of I frames. All right, lastly, let me note one more thing, this wrap attribute method actually returns the wrapper function. And so once we get that wrapper function back, we can start messing around with where this value attribute actually points to. We set the value attribute to point to our wrapper function and not our original function. Our original function will be called by our wrapper function. So now, how do you use an I frame wrapped object? Well, the answer is that you use it the exact same way you use a non-I frame wrapped object. So the decorator pattern describes the way that we can decorate methods of an object without changing its interface. So the way that we operate within the same way that we would ask for the value of an I zero or the input field that is in the default context just by asking for its value is the same way that we would ask for the value of a page object that has been wrapped inside of, that has been wrapped by I frames. So when you call these methods, the value method on objects I one and I two, the code automatically, because we've wrapped these methods with code to get into and out of the I frame, it automatically traverses the I frames and then grabs the value and then returns you back to the default context. So the same thing happens with your setter functions. You can set a value. It will go through, traverse the I frames, set the value of the underlying HTML element, and then return you back to the default context. So just to summarize, the I frame wrapped pattern. I frame wrapped pattern lets you take a page object that represents an HTML element and use it whether it is, I guess, it allows you to use the same object that you would have used outside of an I frame. You can just wrap the attributes and use it inside of your I frame. Also, it uses the decorator pattern in order to accomplish all the wrapping. So one more thing about I frames, there are actually a couple of gotchas. So no pattern is complete without gotchas. So the first gotcha is that not all your page objects attributes need to be wrapped. Things like your constructor don't have to wrap it. So you should keep a list. If you're going to use this pattern, you should keep a list of what attributes don't have to be wrapped. The majority of them may have to be wrapped, but you don't want to wrap methods that don't have to be wrapped because your code will stop working. The second thing is that in Python, if you have properties, properties are just accessors, getter, setter, delete kind of thing, functions. If you have properties, it's tricky to wrap them because in Python, the properties are associated with the class object and not with the instance of the class. So that means that if you want to wrap a property in Python, you have to first create a new class that's a copy of your original class. And then you wrap the properties of your new class and then you associate your new class with your page object. So it gets a little tricky when you're dealing with properties. I found that one out the hard way, so pro tip. So if you're interested in learning more about design patterns, there's an excellent book out there called Design Patterns. It's written by a group of guys called the Gang of Four. I think it's Gamma as the first author, and they usually just call it Gamma et al. There's two websites that I use a lot when I'm interested in learning about design patterns, oodesign.com and sourcemaking.com. And then I guess I'd like to give a special thanks to my team members back at Purdue University who have supported me as I've kind of come up with these different ways of building page objects. And my advisor in the School of Electrical and Computer Engineering, Sam Midkiff, who has also helped me kind of flesh out these different design patterns. So there's one design pattern we didn't get to talk about, and I feel like we're going to run out of time if we actually get into it. It's called the web form pattern. And it takes some of the ideas from these other patterns, but it really promotes a way of filling out web forms where instead of actually, let me see if we can get to some of the slides, the good slide, instead of building page objects for a web form where you hand values where you have a function like submit ticket. This is, by the way, a form for a support ticket. And you can fill out this form. When we build a page object, sometimes I see people with a method called submit ticket. And then you enter in all of the values you want in the form. You just put them as parameters. And instead of doing this type of interface, I try to promote an interface where you just shove all of your inputs to your form into a dictionary and hand it to a function like populate form. And populate form takes care of parsing the dictionary, figuring out that these are actually fields in your page. And these are the values that you want put into the page. And then you can kind of sum it all up with this or finish it all up with submit form method. So I'll be around after if you guys want to talk about that. Right now I want to open the floor up to questions that people have regarding the other two design patterns. Go ahead. Yeah, so the question was, are there any suggestions for how to create objects dynamically in other languages like Java? I'm not very familiar with Java. My feeling is that because Python and Java are kind of similar in their object orientedness, that a lot of these design patterns will actually translate over to Java. So I believe a lot of these design patterns were first explained in either small talk or C++. And so I can't speak to the specifics of how to dynamically create objects in Java. My guess is that there would be some kind of way, we were talking about the item list pattern, we were talking about dynamically creating our objects. So my guess is that there would be some kind of way to say that you have a class and just keep the name of the class and then instantiate that without physically writing out the name of the class by storing that name in a variable or a reference to the object that represents that class. OK. OK. So I'll have to look up reflection too because I saw that when I was reading about design patterns, I saw the word reflection. I wasn't really familiar with all the details around it. So I'll have to look that up as well. Other questions? Well, if you guys ever want to get in contact with me, the email is telldsk, if you have anything to tell me, telldsk at gmail. And I'll be roaming around here and you guys can come up and ask questions or whatever. Yeah, let's go, maybe it's quicker to do this. Oh, there we go. Telldsk at gmail.com. All right, thank you for your time.