Password Management

Password Management

Managing passwords is often a challenging task unless you have a eidetic memory or lazy enough to click on forget username/password. I believe the neither of those is gonna end up reading this post and trust me, you are not one of ‘em either! So, when it comes to managing passwords, the first thing that comes to our mind is the best password management app in the app store or play store. Apparently, when we search it, we will end up in getting to know about the app that is listed in most of the sites and most reviews etc. So obviously, any person would not be needing to ask some security expert to decide on what app to choose. Now, am I a security expert? Hell no! I am not against the apps that are listed as the best in any point in time, however I am not someone who believes that the news is always true. Infact, the truth is what the media say it is. And this applies to websites as well. Now, before we get into the actual password management details, lets split things up into discrete pieces, so that we could better understand the need for password management.Why do we need to protect our passwords? Coz’ if someone else gets access to our password, they might tamper with our identityLet’s call this someone ‘X’Does X really need your password? Unless, you are a friend/enemy of X, X wouldn’tDo I know X personally? Well, most probably no, coz’ X is a company most of the times and X being a person seldom happensAs you see, you cannot be a possible victim unless its X’s personal vendetta or you are on your way to becoming a celebrity/millionaire Therefore, its unlikely for X to target an individual and lot likely to target a company which has strong user base (One reason why big companies are falling victim to attacks by X). Also, as all of your credentials is stored in one single infrastructure, though the data is encrypted, you still are a victim as you have given all your data to X. Its just a matter of time before X finds a way to decrypt Well, if that’s the case, what else can we do. Do I mean that there is no actual protection? YES!But we can definitely work on minimizing the risk of saving all our details under one single infrastructure and maybe placing partial data in a place which is secure and the remaining in an unsecured location. The reason I say unsecured is coz’ this is where the protection actually happens. The last thing they would search is their own backyard. No wants to steal data that is open to everyone. That ain’t stealing anyways! The phrase That is where they least expect to hit ‘em from just doesn’t apply for X just coz’ there are lot of other better places for them to hit and they would never know if that’s worth it If this theory sounds good to you, let's start practicals now. There is an existing password manager which already fits the above theory. You can find the website here Considering the above theory, you can safely assume that the app needs some basic understanding of the belowHow to create a github repository (private repo recommended) How to generate a PGP key pair (online generator here)*** Recommended optionsNote: The longer the passphrase, the more secure you password is and its ok to have the expires option set to NeverOnce you are done with the above 2 steps, it's time to install the app from the Compatible Clients section in https://www.passwordstore.org/ site After installation you would be required to enter the git repo and PGP key pair details before using the app Once repo and key details are entered you are good to go!Just to clear things about how this app ties to our theory, we need to understand a little more about how this app works. The repo you create is basically the database where you store your encrypted passwords. That would mean, each user has separate database (git repo) and it is in github (The world’s largest host of source code). Now, the password that you encrypt needs the key and the passphrase in order to be decoded; which again you have it in your local file system. In other words, you have the keys and passphrase in your app data folder and not in any server. As you see, you have now separated the key (PGP file) and the lock (git repo) and the only possible way to get access to your passwords is to get access to your key, passphrase and github repo! That would mean, hacking any one of these has no impact whatsoever!As a side note, its better to have a password hint in the password section instead of the actual password in the appThat said, this obviously is not an easy password management practice, but once setup its worth more than the money you spend on any subscription based or paid password management service. And the best part; you can have as many fields you want for any website/app by adding new keys as key value pairs Now, how do I know if this is the best/right way to manage passwords?Well, I don’t. It’s just my way 🙂

Verify record in HTML table

Verify record in HTML table

Though not very often encountered, this again is an interesting problem to solve in automation testing. It’s quite obvious to come across table validation as an automation tester and there are quite a lot of options to do that. The most common verifications includeVerifying a unique identifier in a columnVerifying a specific text (cell) in a table based on a unique identifierVerifying a specific row (multiple values) in a table based on multiple identifiers (whether row exists)Hosted table for testing here and HTML Table source hereThe basic solution, as discussed here, solves 80% of all the listed problems. However, that only works when the table has a unique identifier. The solution for points 1 and 2 are already part of the previous post. So, let’s see how to solve the next complex problem # 3 This is again not a complex one to understand, however quite complex to validate with just xpath. There is still an option to validate this with some basic xpath’s and handling the validation part in the script/code but we are not gonna see that here for the same reason mentioned in my previous post. And it’s way too costly in this case. To make things clear, the problem that we intend to solve here is We have a table with multiple columns and rows; and our task is to validate whether a record exist with 3 or more values in a specific order in a row, or in other words, under specific headers. And we intend to solve this using only xpath i.e., without iteration in the code. Let’s take on one of the complex records to find in the table. In our example, let’s plan to find the value CodePen under the header Sites and 2002 under the header Views and 4135 under the header Clicks For our initial test, let’s first assume that we already know the index of the header column for each of these values that we need to verify in which case, the xpath would be something like //tr[td[.='CodePen' and position()=1] and td[.='2002' and position()=2] and td[.='4135' and position()=3]]As you can see, the position() keyword comes in handy in this case. So, the actual code to validate these values would be String expectedValue1 = "CodePen"; String expectedValue2 = "2002"; String expectedValue3 = "4135";String derivedXpath = "//tr[td[.='" + expectedValue1 + "' and position()=1] and td[.='" + expectedValue2 + "' and position()=2] and td[.='" + expectedValue3 + "' and position()=3]]";boolean isElementPresent = driver.findElements(By.xpath(derivedXpath)).size() > 0;if (isElementPresent) { // Do something after test step is passed } else { // Do something after test step is failed }Moving on to the next part where you do not know the position of each of the headers from which you need to validate the value. If you have already read my previous post on verifying text in HTML table, you might probably have guessed that we can use the count() operator to find the index! And guess what, that is correct 🙂 However, this time instead of verifying the existence of the header, we will be using a different approach. We will still use the count() operator, but in conjunction with number() and boolean() operators to validate whether the header exists. The xpath to get the position of a particular header would be count(//th[.='Clicks']/preceding-sibling::th)+number(boolean(//th[.='Clicks']))The above xpath will get the count of preceding siblings with tag name ‘th’ and then adds the boolean value for the existence of the object. Basically, the number() operator returns 1 (true)/0 (false) based on the input passed into it and the boolean() operator returns true/false based on the object existence. In simple termsboolean(Object) – true // Object is any matching tag from the given xpathboolean(null) – false // null is no matching tag from the given xpathnumber(true) – 1number(false) – 0The new xpath would be //tr[td[.='CodePen' and position()=count(//th[.='Sites']/preceding-sibling::th)+number(boolean(//th[.='Sites']))] and td[.='2002' and position()=count(//th[.='Views']/preceding-sibling::th)+number(boolean(//th[.='Views']))] and td[.='4135' and position()=count(//th[.='Clicks']/preceding-sibling::th)+number(boolean(//th[.='Clicks']))]]Hope you get the idea. If so, all you gotta do is to parameterize the position values in the above code. The new code would now be String expectedHeader1 = "Sites"; String expectedHeader2 = "Views"; String expectedHeader3 = "Clicks";String expectedValue1 = "CodePen"; String expectedValue2 = "2002"; String expectedValue3 = "4135";String derivedXpath = "//tr[td[.='" + expectedValue1 + "' and position()=count(//th[.='" + expectedHeader1 + "']/preceding-sibling::th)+number(boolean(//th[.='" + expectedHeader1 + "']))] and td[.='" + expectedValue2 + "' and position()=count(//th[.='" + expectedHeader2 + "']/preceding-sibling::th)+number(boolean(//th[.='" + expectedHeader2 + "']))] and td[.='" + expectedValue3 + "' and position()=count(//th[.='" + expectedHeader3 + "']/preceding-sibling::th)+number(boolean(//th[.='" + expectedHeader3 + "']))]]";boolean isElementPresent = driver.findElements(By.xpath(derivedXpath)).size() > 0;if (isElementPresent) { // Do something after test step is passed } else { // Do something after test step is failed }It’s a quite complex xpath to derive when it comes to validating dynamic values with multiple headers and values, but considering the amount of time this xpath saves in execution, I would say “worth it!”

Verify text in HTML table

Verify text in HTML table

For any automation tester, this is a common task/problem to solve. Almost every automation project will have a verification point, which involves verifying a value in a table Before we move on to the solution for our problem, let us create a sample HTML table, so that the work is not just theoretical.Hosted table for testing here and HTML Table source hereAssuming that you already know which column to loop through, and all you need to check is that whether the identifier exist, the normal approach would be to get the column that you need to check the value for, and then loop through the values until you find the expected value that you are looking for. Pretty straight forward. However, sometimes it gets a little complex where you need to find the index of the header and then find the value in that column. Something like below WebElement table1HeaderRow = driver.findElement(By.xpath("//table/thead/tr")); List<WebElement> headerValues = table1HeaderRow.findElements(By.tagName("th"));String expectedHeader = "Clicks"; String expectedValue = "2961";int i = 1; for(WebElement e : headerValues) { if(e.getText() == expectedHeader) break; i++; } // variable i will have the matching header here // now verify value in all rows in this columnList<WebElement> values = driver.findElements(By.xpath("//table/tbody/tr/td[" + i + "]")); // manipulate xpath of the matching column and get all values from the matching column boolean match = false; // default match to false, so that no match will be considered failurefor(WebElement e : values) { if(e.getText() == expectedValue) { match = true; break; } } if (match) { // Do something after test step is passed } else { // Do something after test step is failed }Let's see the problem with the above code, before we move on to the possibly different solution. One of the significant problem with the above code is the execution time. Though we know the header and value, we still are verifying each element in the column (header) and row (value) to verify its existence, instead of directly searching the DOM for the presence of the element. Facts and Assumptions We have two values here. One is the header and the other is the value in the header column. In most of the cases, the value will be dynamic, which means, we would not be able to derive the xpath before execution, and has to be derived during runtime. Therefore, we will assume that the value is always dynamic and the header may/may-not be dynamic. Let’s look at both the cases one-step at a time. Static Header and Dynamic Value Here, let’s assume that the header column is something that we already know. In this case where we already know the index of the header column (column number of header) we can do something like below List<WebElement> values = driver.findElements(By.xpath("//table/tbody/tr/td[3]"));Since, we do not have to search for the header column; we already skipped one loop in the code. So what’s next? Let’s see if we can skip the next loop. During the execution, once we find the value (2961) to be verified, the xpath could now be manipulated as //table/tbody/tr/td[3 and .='2961']The code can now be changed as boolean isElementPresent = driver.findElements(By.xpath("//table/tbody/tr/td[3 and .='2961']")).size() > 0;As you can see, the variable isElementPresent will now have the validation result without looping through all the values Dynamic Header and Dynamic Value What happens if we do not know the index of the header column and we need to find the header column number dynamically? Here is where things get interesting! As xpath does not directly support indexing, we would need to tweak our xpath with interesting features that xpath already provides. Let’s start by tackling the index. Xpath provides an option to find the index of the current node by manipulating the count() method. We can get the index of the node that we are searching for using the below xpath. count(//table/thead/tr/th[.='Clicks']/preceding-sibling::th)+1The above xpath will give the index of the Clicks column. Since, the index starts with 0, we are incrementing the index by 1 to find out the actual index (3). We can now get the derived xpath by using the xpath as //tbody/tr/td[count(//table/thead/tr/th[.='Clicks']/preceding-sibling::th)+1 and .='2961']However, there is a caveat in this xpath. The index of the first header will be returned as 1 and the index of a non-existing header will also return 1 i.e., count(//table/thead/tr/th[.='Sites']/preceding-sibling::th)+1returns 1 and count(//table/thead/tr/th[.='Rank']/preceding-sibling::th)+1will also return 1 In other words, the below xpath’s //tbody/tr/td[count(//table/thead/tr/th[.='Sites']/preceding-sibling::th)+1 and .='LinkedIn']//tbody/tr/td[count(//table/thead/tr/th[.='Rank']/preceding-sibling::th)+1 and .='LinkedIn']Will return the same node (row # 4 in column 1), however, the second xpath (with Rank header) does not exist That brings us to the next problem which is to verify whether the actual heading exist before deriving the index. Now, how do we do that? The trick is to find the header column first, then navigate to it's ancestor, then to the ancestor's sibling and derive actual index (above index manipulation code) appended by value. Sounds confusing, but it’s worth a try! The new xpath would be //table/thead/tr/th[.='Clicks']/ancestor::thead/following-sibling::tbody/tr/td[count(//table/thead/tr/th[.='Clicks']/preceding-sibling::th)+1 and .='2961']The above xpath first searches for the existence of the actual heading that we are searching for (Clicks column), then moves up to the 'thead' tag (ancestor) and then moves down to the 'tbody' sibling (following-sibling), then takes the header index by manipulating the count() operator followed by the condition with the value (2961) To check the correctness of the xpath, let’s validate the above xpath for a different value for a different header. To validate row # 4 in column 1, the xpath would be //table/thead/tr/th[.='Sites']/ancestor::thead/following-sibling::tbody/tr/td[count(//table/thead/tr/th[.='Sites']/preceding-sibling::th)+1 and .='LinkedIn']Now changing the header value to Rank which does not exist, let’s try again with the below xpath //table/thead/tr/th[.='Rank']/ancestor::thead/following-sibling::tbody/tr/td[count(//table/thead/tr/th[.='Rank']/preceding-sibling::th)+1 and .='LinkedIn']The above xpath will not return any node as Rank header does not exist. Finally, the new code would now be String expectedHeader = "Clicks"; String expectedValue = "2961";String derivedXpath = "//table/thead/tr/th[.='" + expectedHeader + "']/ancestor::thead/following-sibling::tbody/tr/td[count(//table/thead/tr/th[.='" + expectedHeader + "']/preceding-sibling::th)+1 and .='" + expectedValue + "']";boolean isElementPresent = driver.findElements(By.xpath(derivedXpath)).size() > 0;if (isElementPresent) { // Do something after test step is passed } else { // Do something after test step is failed }And that’s how we deal with dynamic headers and dynamic values in HTML table. Ending Note: If you have to validate specific values in a table, just remember that you don't have to loop through the entire table! Hungry for more. Bon appetit!

JavaScript: Array Manipulation

JavaScript: Array Manipulation

JavaScript offers different ways to manipulate arrays. However, there are methods that changes the original array and other methods that don’t. The methods that changes original array is called MUTATION. Let’s take a look at most common methods to manipulate arrays in JavaScript Now, that we know that there are methods that mutates the original array, it’s considered best practice to assign the array to const, if you know that the array that you use is not going to get mutated. It’s wise to use let otherwise. It is not a mandate that you need to use const to declare an array that will not be mutated, however, it’s easy for your colleague to understand that the array which is declared as const is not going to get changed anywhere in the code; which is why it’s considered best practice As usual, if you wanna try the examples in this post, I recommend codepen.io, but you are free to make your own choice. The most common interactions that we usually have with arrays areAdd item to array Remove item from array Update and item in the arrayAll the above mentioned actions can be done by both mutating and non-mutating methods. Instead of explaining each of the methods subjectively, I felt it would be better understood with a table. Here we go! testArray: [a, b, c, d, e, f, g, h, i];Code Original Array Processed Array Returned Val Is Mutated Action MethodtestArray.push('j') [a,b,c,d,e,f,g,h,i] [a,b,c,d,e,f,g,h,i,j] 10 true add push()testArray.unshift('z') [a,b,c,d,e,f,g,h,i,j] [z,a,b,c,d,e,f,g,h,i,j] 11 true add unshift()testArray.concat('k') [z,a,b,c,d,e,f,g,h,i,j] [z,a,b,c,d,e,f,g,h,i,j] [z,a,b,c,d,e,f,g,h,i,j,k] false add concat()['y', ...testArray, 'l'] [z,a,b,c,d,e,f,g,h,i,j] [z,a,b,c,d,e,f,g,h,i,j] [y,z,a,b,c,d,e,f,g,h,i,j,l] false add ...testArray.pop() [z,a,b,c,d,e,f,g,h,i,j] [z,a,b,c,d,e,f,g,h,i] j true remove pop()testArray.shift() [z,a,b,c,d,e,f,g,h,i] [a,b,c,d,e,f,g,h,i] z true remove shift()testArray.splice(0, 2) [a,b,c,d,e,f,g,h,i] [c,d,e,f,g,h,i] [a,b] true remove splice()testArray.filter(a => a!== 'c') [c,d,e,f,g,h,i] [c,d,e,f,g,h,i] [d,e,f,g,h,i] false remove filter()testArray.slice(1, 6) [c,d,e,f,g,h,i] [c,d,e,f,g,h,i] [d,e,f,g,h] false remove slice()testArray.slice(2) [c,d,e,f,g,h,i] [c,d,e,f,g,h,i] [e,f,g,h,i] false remove slice()testArray.splice(2, 1,30, 31) [c,d,e,f,g,h,i] [c,d,30,31,f,g,h,i] [e] true update splice()testArray.map(x => x ==='d' ? 29 : x) [c,d,30,31,f,g,h,i] [c,d,30,31,f,g,h,i] [c,29,30,31,f,g,h,i] false update map()I hope the above table is self-explanatory, however if you need to check the values of the testArray with real code, feel free to visit programmatic output where the exact same table is derived programmatically. Codepen pin link

Cookie Policy

Cookie Policy

Ever wondered why do websites have no decline option in their cookie policy popup? Well, lets’ see. Today, almost all sites in the internet uses cookies that is stored temporarily in the system cache to ease authentication of the same user in the same system. I’m talking about well-established sites that makes some use of the information in your local machine (THE COOKIE!). Why do I need it? As per GDPR, every website that tracks their visitors should have the visitors consent to use their data to better manage what they need. So if you are tracking your website users and making decisions based on their user data, then ‘YES’, you have to force the user to accept your cookie policy. Do I need it? Before we get into the fun part, we gotta understand what this cookie policy can do. Let’s say you own a blogging portal with ‘Disqus’ comment system enabled. Obviously, any visitor need to have a ‘Disqus’ account in order to comment. This means, like it or not ‘Disqus’ can track your visitors on which sites they visit and which site they comment and maybe manipulate your data based on their need. Now, let’s get into the fun part. As long as you plan to use your visitor’s data and manipulate something from it, you gotta have the cookie policy in your site. However, what do you think you can do with your visitor’s data? As soon as that question is asked, we start to think big! Maybe we can find users locations. “Ok, then what?” If you are just planning or started a blog for yourself like me, all we have is our petty subscription list, which is a list of email ids’ that will be almost empty for atleast couple of months! What can possibly be done with that except sending emails about new posts, which again seldom occurs? Therefore, my view of adding a cookie policy is, if you are just starting a blog; just make sure that you are not adding any tracking 3rd party code that tracks your blogs visitor. That means, in case of WordPress websites, the plug-ins that you use in your site may need you to register in order to use it, but not for your visitors. That is it. You have a long way to go before you even think about adding the cookie policy. Just focus on adding new and useful posts. After all, who wants to accept a policy that has no choices?