Skip to content

Commit

Permalink
Add possibility to execute Selenium-style JS in Playwright tests
Browse files Browse the repository at this point in the history
  • Loading branch information
vkepin committed Dec 18, 2024
1 parent 7b21f68 commit 2cef838
Show file tree
Hide file tree
Showing 2 changed files with 58 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,19 @@
package org.vividus.ui.web.playwright.action;

import java.util.List;
import java.util.regex.Pattern;

import com.microsoft.playwright.Page;

import org.vividus.ui.web.action.JavascriptActions;
import org.vividus.ui.web.playwright.UiContext;

public class PlaywrightJavascriptActions implements JavascriptActions
{
private static final Pattern PLAYWRIGHT_DECORATED_PATTERN = Pattern.compile("^(?:async\\s*)?"
+ "(?:\\(\\s*(?:\\{[^}]*\\}|\\[[^\\]]*\\]|[^,()]+(?:,\\s*[^,()]+)?)?\\s*\\)|[^()\\s]+)\\s*"
+ "=>\\s*(?:.|\\{|\\n)");

private final UiContext uiContext;

public PlaywrightJavascriptActions(UiContext uiContext)
Expand All @@ -34,10 +41,21 @@ public PlaywrightJavascriptActions(UiContext uiContext)
@Override
public <T> T executeScript(String script, Object... args)
{
if (args.length == 0)
{
return (T) uiContext.getCurrentPage().evaluate("async () => {%n%s%n}".formatted(script));
}
return (T) uiContext.getCurrentPage().evaluate(script, List.of(args));
boolean hasArgs = args.length > 0;
String playwrightScript = isPlaywrightDecorated(script) ? script : decorateScript(script, hasArgs);
Page currentPage = uiContext.getCurrentPage();
return (T) (hasArgs ? currentPage.evaluate(playwrightScript, List.of(args))
: currentPage.evaluate(playwrightScript));
}

private boolean isPlaywrightDecorated(String script)
{
return PLAYWRIGHT_DECORATED_PATTERN.matcher(script).find();
}

private String decorateScript(String script, boolean hasArgs)
{
String playwrightFormat = hasArgs ? "arguments => {%n%s%n}" : "async () => {%n%s%n}";
return playwrightFormat.formatted(script);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
Expand All @@ -35,6 +37,8 @@
class PlaywrightJavascriptActionsTests
{
private static final String JS_SCRIPT = "document.querySelector('[name=\"vividus-logo\"]').remove()";
private static final String NO_ARGS_FORMAT = "async () => {%n%s%n}";
private static final String ARGS_FORMAT = "arguments => {%n%s%n}";

@Mock private UiContext uiContext;
@InjectMocks private PlaywrightJavascriptActions playwrightJavascriptActions;
Expand All @@ -45,17 +49,44 @@ void shouldExecuteScript()
var page = mock(Page.class);
when(uiContext.getCurrentPage()).thenReturn(page);
playwrightJavascriptActions.executeScript(JS_SCRIPT);
verify(page).evaluate("async () => {%n%s%n}".formatted(JS_SCRIPT));
verify(page).evaluate(NO_ARGS_FORMAT.formatted(JS_SCRIPT));
}

@Test
void shouldExecuteScriptWithArgument()
{
var page = mock(Page.class);
var script = "script";
var arg = "arg";
when(uiContext.getCurrentPage()).thenReturn(page);
playwrightJavascriptActions.executeScript(script, arg);
verify(page).evaluate(script, List.of(arg));
playwrightJavascriptActions.executeScript(JS_SCRIPT, arg);
verify(page).evaluate(ARGS_FORMAT.formatted(JS_SCRIPT), List.of(arg));
}

@ParameterizedTest
@ValueSource(strings = {
"num => num",
"object => object.foo",
"(button, from) => button.textContent.substring(from)",
"(button, from ) => button.textContent.substring(from)",
"( button, from) => button.textContent.substring(from)",
"(button,from) => button.textContent.substring(from)",
"o => o.button1.textContent + o.button2.textContent",
"({button1, button2}) => button1.textContent + button2.textContent",
"({ button1, button2 }) => button1.textContent + button2.textContent",
"( {button1, button2} ) => button1.textContent + button2.textContent",
"( { button1, button2 } ) => button1.textContent + button2.textContent",
"([b1, b2]) => b1.textContent + b2.textContent",
"( [b1, b2] ) => b1.textContent + b2.textContent",
"( [ b1, b2 ] ) => b1.textContent + b2.textContent",
"x => x.button1.textContent + x.list[0].textContent + String(x.foo)",
"() => {\\n const response = (() => asd) await fetch(location.href);\\n return response.status;\\n}",
"async () => {\\n const response = (() => asd) await fetch(location.href);\\n return response.status;\\n}"
})
void shouldNotDecorateScript(String script)
{
var page = mock(Page.class);
when(uiContext.getCurrentPage()).thenReturn(page);
playwrightJavascriptActions.executeScript(script);
verify(page).evaluate(script);
}
}

0 comments on commit 2cef838

Please sign in to comment.