Please add ids to perspective components for writing tests

I don’t know why this was overlooked during the design of Perspective :face_with_raised_eyebrow: , but this is caused major heartaches when trying to write tests. Currently there is no way to add an id to a component in Perspective and when trying to write tests using Selenium IDE is just fails miserably. I come from the web world and it has been instilled in me to always give an id to elements so you can easily reference them during tests. Nothing I’ve tried allows me to add an id to the component so that I can reference them, I’ve even tried creating an id in the props which did nothing :frowning: What testing framework are others using with success?

This feature has existed since the initial release of Perspective but hasn’t been widely publicized. In newer versions of 8.1 it is a little bit easier to find.

Within the META area of a component property editor, adding a domId Property with a value of the ID you want to add will allow you to set whatever ID you wish.

Garth

8 Likes

Thank you very much for the information. I really wish they would make something like that a little easier to find and also add ids automatically to components that we could then just overwrite.

@Paul.Scott can this be added to the documentation? This is a huge item for integrators that is not really called out anywhere.

This is essentially the first step in any UI testing for perspective, and its not very readily available

1 Like

Certainly, but it looks like we already added domId to the manual back in May: Meta Properties

2 Likes

Ahh! I was searching in 8.0 documentation, not 8.1! Thanks Paul!

1 Like

That’s a good point. I have no idea if domId already existed in 8.0. I’ll figure it out, and update the 8.0 docs if that’s the case.

2 Likes

I can verify the’ve existed since Alpha 8.0.

@kgamble Can you please provide some examples or point me to some resources of how this assists with UI testing? I’m in a position where I regularly have to verify that connections to tags are correct and would like to find some better methods.

1 Like

By setting the domId property of a component, you’re able to interact with the component via automated testing by targeting the supplied id, versus targeting via either CSS selectors or XPATH. For example, we use Selenium/WebDriver for our internal testing and by supplying domIds to important elements, we can use something like this:

self.driver.find_element(*(By.ID, 'my_dropdown')).click()

as opposed to something like this…

# this uses indexing, which is fragile
self.driver.find_elements(*(By.CSS_SELECTOR, 'button.button-primary'))[0].click()
# and XPATH is more precise, but is even more fragile in most instances
self.driver.find_element(*(By.XPATH, '//*[@id="app-container"]/div/div[2]/div/div[2]/button'))
1 Like

Exactly what I would have said! Except here is a slightly more filled out example in python with chrome web driver

import time
from selenium import webdriver

driver = webdriver.Chrome()
driver.get('http://localhost/data/perspective/client/Test')

time.sleep(2)

button = driver.find_element_by_id("myExampleButton")

button.click()

driver.quit()
1 Like

Separate question though, is it possible to get any of the react state props from the component once a reference is achieved? I know in some cases (like a button) you can get certain values like “Text” because you render it to the DOM. However for custom properties that we create, is it possible for us to access those? Or would it be a feature request to ask for an access control type for properties that puts it in the Dom for testing?

Example case:

  1. I use selenium to UI test a screen and verify that when I click a button that it updates a custom property of something else
  2. To update the property I click on the button with selenium
  3. To verify the property changed, I get access to the other component and look for the custom property that we updated.

I tried doing things like setting access settings, and digging around through the button object through debugging, but I was unable to find anything.

it’s going to require you to run browser-side JS:

driver.execute_script("return __client.page.pageProps.root.value.value._data")

And the script involved is going to be 100% index based. So… I really advise against this.

Notice that root is an attribute of the page? So you can drill down through component hierarchy, but the moment you change the hierarchical structure of the View your script will fall apart. This also only works for viewing properties which have “Public” or “Protected” access. And as far as custom properties go they are not exposed in the DOM because they are just server-side data and don’t directly pertain to appearance or data.

I see what you are saying, does seem a little sketchy.

In regards to this, if it does directly pertain to the data relevant on screen, would it be a valid feature request to allow the ability to expose certain custom props to the Dom for testing? For example if I am running a test to verify that a page is pulling in 10 rows of data and they all have the right values, currently I can only do that by iterating through the rows and looking at the text in each cell by order

Long term that feels fairly in-scalable incase you guys happened to change the names of any of the classes, the we hide a column, or if they are reordered, we now may not be able to see the data we are checking against?

It would allow for full end to end testing as opposed to just making sure a button is showing up and can be clicked

Is there a better way to test this sort of scenario? Or a reason why we would not want to do this? Or if it is just genuinely not possible for you guys to allow this level of property exposure to the Dom

The problem is you're using a front-end testing tool to test back-end data. It's extremely unlikely we would add other properties to the DOM because of the limited usefulness compared to the performance risk of exposing potentially MANY more properties (you might have one or two properties on your button, but UserX has two dozen Datasets polling regularly). On top of that, we'd have to expect work around security settings applied to those properties so that we only inject properties set to be public or protected.

How would you test Datasets?

This is something I'd brought up internally in the past. I feel like there's a small subset of users who would appreciate access to our automation repo or even a way to bundle it with the release either as part of the Perspective Module or as a separate Module. The reason we haven't done this so far is it would require us to essentially dedicate resources to maintaining and updating another module for a subset of users. This costs us time, money, and manpower and would be one more thing we would need to run through quality processes each release.

1 Like

Actually, Cody’s script above can be adapted to be a bit less fragile without too much trouble.

client = __client
mounts = __client.mounts
mainViewId = `${mounts.center.viewPath}@${mounts.center.code}`
mainView = client.page.views.get(mainViewId)
mainView.custom.read("key") // or readArray, readDataset, etc

Still somewhat fragile, but not as bad. PropertyTree on the frontend has various methods to read values from the model as different types. Custom properties are currently ‘synced’ to the frontend, though as Cody mentioned, that default may be changed in the future, since outside of automated testing there’s little point in synchronizing custom properties to the frontend.

In regards to the example, I see that you got the mainViewId but how would you do this for other items than the view itself? Would it be possible to use similar syntax to grab specific components?

In the example of just dropping a button on a page, how would one generate the id of that button for reference? Or would you be limited to only accessing custom props on the main view?

I see where digging through the component children I do eventually find it, just not sure if there is a cleaner way that I am unaware of.

Definitely if this is moved, it would at least be a nice optional property to change from default, to allow for this style of testing down the road.

You could continue to drill down into the view (it has a .root property, which is the base of a tree of component instances. You will have to drill though that tree by index, essentially; you could write helper functions that search that tree for paths, but I don’t see any convenient utils exposed on the frontend.

I was reviewing this old topic, as I am in the same scenario where I am trying to validate some data that gets provided to the front end in tests. I hit this comment and was curious, what type of tool would even be able to test this type of backend data out of Ignition?

For example, if I have a perspective screen that is running a function to return a list of objects, and lets say that each of those objects needs to read a tag value (something Ignition specific is really the point there). How would you recommend automatically testing that?

If it was pure python, I can just make sure its compatible outside of jython and write pytests for it, which we have done, but in the case that we need to evaluate with built in Ignition (or say Sepasoft) functionality.

Aside from writing a module, the only thing I can think of is essentially rebuilding pytest to work in Ignition without the premise of __file__ since you can't grab that most of the time (if at all, if I remember correctly)

The cleanest thing I can think of, is building simplified perspective screens that act as a unit test for functionality, but then we need to rely on the ability to serialize that data into something visualized, or accessing component props.

Which per this comment, I actually did add some logic to a Perspective focused selenium test library, but it is not fully fleshed out, and I am not sure if its the right answer long term.

EDIT: As a general note, the ability to integrate typical devops practices in Ignition are definitely headed in the write direction (i.e. changing .bin files into text based files) . I look forward to stuff like this being more trivial than needed to write fully customized libraries and modules just to handle CI/CD

The way we actually test that is... by "dogfooding" Perspective. From your example:

We would either bind labels against the list of tag values or use messaging to write to Labels, and then verify the labels have the expected values (after first-off checking the labels do NOT have the expected values). In many ways we do use a front-end testing tool to verify at least some back-end functionality.