Using the Playwright script recorder
Summary
This feature is a library of functions in the Grouper Java API that could use a headless browser to operate the Grouper UI (or other UIs).
The libraries under consideration are Selenium and Playwright.
A POC was attempted for Selenium to use a headless browser in the Grouper container and it was not successful.
A POC for Playwright was successful, so that is the choice.
Playwright is:
Backed by Microsoft
Several years old
Can record scripts in Java
Uses CSS selectors and the DOM
Waits for DOM elements to be available automatically
Easily handles Duo Universal Prompt
Is lightweight, there are no server components to install, is only a handful of jars
Can emulate several browsers
Example of a script recording
I installed node and playwright on my workstation
install nodejs
mchyzer@wal-vl216-dhcp085 opt % npx playwright install
mchyzer@wal-vl216-dhcp085 opt % npx playwright codegen https://grouper.apps.upenn.eduThat last command pops up the recorder
Grouper library of Grouper UI operations
We can create and maintain a set of UI operations people might want to take advantage of. Note, above in the GSH script, the authentication (including two-step) and URL will need to be handled, e.g.
page.navigate("https://grouper.apps.upenn.edu");
page.getByLabel("Username").click();
page.getByLabel("Username").fill("mchyzer");
page.getByLabel("Username").press("Tab");
page.getByLabel("Password", new Page.GetByLabelOptions().setExact(true)).click();
page.getByLabel("Password", new Page.GetByLabelOptions().setExact(true)).fill(GrouperConfig.retrieveConfig().propertyValueStringRequired("mchyzer.pass"));
page.getByLabel("Password", new Page.GetByLabelOptions().setExact(true)).press("Enter");
page.getByRole(AriaRole.BUTTON, new Page.GetByRoleOptions().setName("No, other people use this")).click();
Then some operation can occur
public static void addMemberToGroup(Page page, Group group, Subject subject) {
page.locator("[href='UiV2Main.index?operation=UiV2Group.viewGroup&groupId=" + group.getId() + "']").first().click();
page.locator("#show-add-block").click();
page.getByRole(AriaRole.LINK, new Page.GetByRoleOptions().setName("import a list of members")).click();
page.getByLabel("Copy/paste a list of member").check();
page.locator("#entityListId").click();
page.locator("#entityListId").fill(subject.getId());
page.locator("#searchEntitySourceId").selectOption(subject.getSourceId());
page.getByRole(AriaRole.BUTTON, new Page.GetByRoleOptions().setName("Submit")).click();
page.getByRole(AriaRole.LINK, new Page.GetByRoleOptions().setName("OK")).click();
}
CSS selectors of the Grouper UI DOM
We can adjust the HTML attributes in the Grouper UI so it is easier to use tools like Playwright. As the Grouper UI changes (screens are changed, screen flows adjusted, etc), these methods will be changed as well so it will not break scripts that use the Grouper API methods.
Expected use of Playwright
An institution could write a GSH template UI, WS, or daemon, which uses Playwright to do sanity testing on the UI or other functions in Grouper. i.e. The Grouper Java API could have some helper methods like the one above which change memberships, make things provisionable, kick off daemons, etc. These scripts could also include using the Grouper API to change changes in LDAP, SQL, WS, etc to see that provisioning occurred successfully. When Grouper is upgraded or other changes made, the sanity script could be run to make sure basic parts of Grouper are functioning.
Security and setup
It is better to keep Grouper thin and keep testing libraries out of Grouper. But there is also a lot of value to have a library of methods so that each institution is not recording their own scripts, and more things than just the browser emulation is available (e.g. anything in the Grouper Java API, and SQL / LDAP / Provisioning DAO's are available).
pom.xml
<!-- https://mvnrepository.com/artifact/com.microsoft.playwright/playwright -->
<dependency>
<groupId>com.microsoft.playwright</groupId>
<artifactId>playwright</artifactId>
<version>1.43.0</version>
</dependency>
Some options:
Include Playwright jars and Grouper API library in container
If an institution does not want to use it, then just do not use it
Include Playwright jars and Grouper API library in container, but an ENV variable would move the Playright jars out of the lib directory
If the ENV variable is set to hobble Playwright, calls would get runtime errors
Multiple containers could be available for each version, a container with playwright, or a container without playwright
This is not inline with Grouper training where we recommend the same derived image to all institutional envs
There is overhead in having multiple containers that the Grouper team wants to avoid
Include Playwright jars and Grouper API library in container but not in the lib directory, but an ENV variable would move the Playright jars into the lib directory
If the ENV variable is not set to enable Playwright, calls would get runtime errors
Playwright jars are in an OSGI plugin so they are only loaded if configured (via ENV var)
If the ENV variable is not set to enable Playwright, calls would get runtime errors
Note, this is a more complex programming model and might have unintended limitations
Playwright jars are not included in the container, but instructions to add them to a derived image are included
If the jars are not added to the derived image, calls would get runtime errors
Do not include Playwright in Grouper, leave this to external tools