v3.0.0
Major Changes
-
#561
b565e0b
Thanks @calebeby! - Normalize whitespace in element accessible names ingetAccessibilityTree
. Markup with elements which have an accessible name that includes irregular whitespace, like non-breaking spaces, will now have a different output forgetAccessibilityTree
snapshots. Previously, the whitespace was included, now, whitespace is replaced with a single space. -
#535
dc6f81c
Thanks @calebeby! - Values exported fromrunJS
are now available in Node.For example:
test( 'receiving exported values from runJS', withBrowser(async ({ utils }) => { // Each export is available in the returned object. // Each export is wrapped in a JSHandle, meaning that it points to an in-browser object const { focusTarget, favoriteNumber } = await utils.runJS(` export const focusTarget = document.activeElement export const favoriteNumber = 20 `); // Serializable JSHandles can be unwrapped using JSONValue: console.log(await favoriteNumber.jsonValue()); // Logs "20" // A JSHandle<Element>, or ElementHandle is not serializable // But we can pass it back into the browser to use it (it will be unwrapped in the browser): await utils.runJS( ` // The import.meta.pleasantestArgs context object receives the parameters passed in below const [focusTarget] = import.meta.pleasantestArgs; console.log(focusTarget) // Logs the element in the browser `, // Passing the JSHandle in here passes it into the browser (unwrapped) in import.meta.pleasantestArgs [focusTarget], ); }), );
We've also introduced a utility function to make it easier to call
JSHandle
s that point to functions,makeCallableJSHandle
. This function takes aJSHandle<Function>
and returns a node function that calls the corresponding browser function, passing along the parameters, and returning the return value wrapped inPromise<JSHandle<T>>
:// new import: import { makeCallableJSHandle } from 'pleasantest'; test( 'calling functions with makeCallableJSHandle', withBrowser(async ({ utils }) => { const { displayFavoriteNumber } = await utils.runJS(` export const displayFavoriteNumber = (number) => { document.querySelector('.output').innerHTML = "Favorite number is: " + number } `); // displayFavoriteNumber is a JSHandle<Function> // (a pointer to a function in the browser) // so we cannot call it directly, so we wrap it in a node function first: const displayFavoriteNumberNode = makeCallableJSHandle( displayFavoriteNumber, ); // Note the added `await`. // Even though the original function was not async, the wrapped function is. // This is needed because the wrapped function needs to asynchronously communicate with the browser. await displayFavoriteNumberNode(42); }), );
For TypeScript users,
runJS
now accepts a new optional type parameter, to specify the exported types of the in-browser module that is passed in. The default value for this parameter isRecord<string, unknown>
(an object with string properties and unknown values). Note that this type does not includeJSHandles
, those are wrapped in the return type fromrunJS
automatically.Using the first example, the optional type would be:
test( 'receiving exported values from runJS', withBrowser(async ({ utils }) => { const { focusTarget, favoriteNumber } = await utils.runJS<{ focusTarget: Element; favoriteNumber: number; }>(` export const focusTarget = document.activeElement export const favoriteNumber = 20 `); }), );
Now
focusTarget
automatically has the typeJSHandle<Element>
andfavoriteNumber
automatically has the typeJSHandle<number>
. Without passing in the type parameter torunJS
, their types would both beJSHandle<unknown>
. -
#541
39085ac
Thanks @calebeby! -injectHTML
now executes script tags in the injected markup by default. This can be disabled by passing theexecuteScriptTags: false
option as the second parameter.For example, the script tag is now executed by default:
await utils.injectHTML( "<script>document.querySelector('div').textContent = 'changed'</script>", );
But by passing
executeScriptTags: false
, we can disable execution:await utils.injectHTML( "<script>document.querySelector('div').textContent = 'changed'</script>", { executeScriptTags: false }, );
-
#535
dc6f81c
Thanks @calebeby! - The way thatrunJS
receives parameters in the browser has changed. Now, parameters are available asimport.meta.pleasantestArgs
instead of through an automatically-called default export.For example, code that used to work like this:
test( 'old version of runJS parameters', withBrowser(async ({ utils }) => { // Pass a variable from node to the browser const url = isDev ? 'dev.example.com' : 'prod.example.com'; await utils.runJS( ` // Parameters get passed into the default-export function, which is called automatically export default (url) => { console.log(url) } `, // array of parameters passed here [url], ); }), );
Now should be written like this:
test( 'new version of runJS parameters', withBrowser(async ({ utils }) => { // Pass a variable from node to the browser const url = isDev ? 'dev.example.com' : 'prod.example.com'; await utils.runJS( ` // Parameters get passed as an array into this context variable, and we can destructure them const [url] = import.meta.pleasantestArgs console.log(url) // If we added a default exported function here, it would no longer be automatically called. `, // array of parameters passed here [url], ); }), );
This is a breaking change, because the previous mechanism for receiving parameters no longer works, and functions that are
default export
s from runJS are no longer called automatically. -
#506
7592994
Thanks @calebeby! - Drop support for Node 12 and 17