Skip to content

Commit 732fbff

Browse files
authored
Add ability to pass arguments to browser in runJS (#45)
1 parent beb1914 commit 732fbff

5 files changed

Lines changed: 78 additions & 5 deletions

File tree

.changeset/proud-numbers-grin.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
---
2+
'test-mule': minor
3+
---
4+
5+
Now it is possible to pass variables to the browser in runJS:
6+
7+
```js
8+
import { withBrowser } from 'test-mule';
9+
10+
test(
11+
'runJS example with argument',
12+
withBrowser(async ({ utils, screen }) => {
13+
// element is an ElementHandle (pointer to an element in the browser)
14+
const element = await screen.getByText(/button/i);
15+
// we can pass element into runJS and the default exported function can access it as an Element
16+
await utils.runJS(
17+
`
18+
export default (element) => console.log(element);
19+
`,
20+
[element],
21+
);
22+
}),
23+
);
24+
```

README.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -441,6 +441,27 @@ test(
441441
);
442442
```
443443

444+
To pass variables from the test environment into the browser, you can pass them as the 2nd parameter. Note that they must either be JSON-serializable or they can be a [`JSHandle`](https://pptr.dev/#?product=Puppeteer&version=v7.1.0&show=api-class-jshandle) or an [`ElementHandle`](https://pptr.dev/#?product=Puppeteer&version=v7.1.0&show=api-class-elementhandle). The arguments can be received in the browser as parameters to a default-exported function:
445+
446+
```js
447+
import { withBrowser } from 'test-mule';
448+
449+
test(
450+
'runJS example with argument',
451+
withBrowser(async ({ utils, screen }) => {
452+
// element is an ElementHandle (pointer to an element in the browser)
453+
const element = await screen.getByText(/button/i);
454+
// we can pass element into runJS and the default exported function can access it as an Element
455+
await utils.runJS(
456+
`
457+
export default (element) => console.log(element);
458+
`,
459+
[element],
460+
);
461+
}),
462+
);
463+
```
464+
444465
#### `TestMuleUtils.loadJS(jsPath: string): Promise<void>`
445466

446467
Load a JS (or TS, JSX) file into the browser. Pass a path that will be resolved from your test file.

package-lock.json

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/index.ts

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,9 @@ export interface TestMuleUtils {
2626
* including TS/JSX modules, and it supports resolving from node_modules,
2727
* and relative paths from the test file.
2828
* The code string supports top-level await to wait for a Promise to resolve.
29+
* You can pass an array of variables to be passed into the browser as the 2nd parameter.
2930
*/
30-
runJS(code: string): Promise<void>;
31+
runJS(code: string, args?: unknown[]): Promise<void>;
3132

3233
/** Set the contents of a new style tag */
3334
injectCSS(css: string): Promise<void>;
@@ -260,19 +261,25 @@ const createTab = async ({
260261
});
261262
};
262263

263-
const runJS: TestMuleUtils['runJS'] = async (code) => {
264+
const runJS: TestMuleUtils['runJS'] = async (code, args) => {
264265
const encodedCode = encodeURIComponent(code);
265266
// This uses the testPath as the url so that if there are relative imports
266267
// in the inline code, the relative imports are resolved relative to the test file
267268
const url = `http://localhost:${port}/${testPath}?inline-code=${encodedCode}`;
268269
const res = (await safeEvaluate(
269270
runJS,
270-
`import(${JSON.stringify(url)})
271-
.then(m => {})
271+
new Function(
272+
'...args',
273+
`return import(${JSON.stringify(url)})
274+
.then(async m => {
275+
if (m.default) await m.default(...args)
276+
})
272277
.catch(e =>
273278
e instanceof Error
274279
? { message: e.message, stack: e.stack }
275280
: e)`,
281+
) as () => any,
282+
...(Array.isArray(args) ? (args as any) : []),
276283
)) as undefined | { message: string; stack: string };
277284
if (res === undefined) return;
278285
if (typeof res !== 'object') throw res;

tests/utils/runJS.test.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,27 @@ test(
3232
}),
3333
);
3434

35+
test(
36+
'allows passing ElementHandles and serializable values into browser',
37+
withBrowser(async ({ utils, screen }) => {
38+
const heading = await createHeading({ utils, screen });
39+
40+
await utils.runJS(
41+
`
42+
export default (heading, object) => {
43+
if (heading.outerHTML !== "<h1>I'm a heading</h1>") {
44+
throw new Error('element was not passed correctly')
45+
}
46+
if (object.some.serializable.value !== false) {
47+
throw new Error('object was not passed correctly')
48+
}
49+
}
50+
`,
51+
[heading, { some: { serializable: { value: false } } }],
52+
);
53+
}),
54+
);
55+
3556
describe('Waiting for Promises in executed code', () => {
3657
it(
3758
'should not wait for non-exported promises',

0 commit comments

Comments
 (0)