Skip to content

Commit

Permalink
Add ability to pass arguments to browser in runJS (#45)
Browse files Browse the repository at this point in the history
  • Loading branch information
calebeby authored Mar 4, 2021
1 parent beb1914 commit 732fbff
Show file tree
Hide file tree
Showing 5 changed files with 78 additions and 5 deletions.
24 changes: 24 additions & 0 deletions .changeset/proud-numbers-grin.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
---
'test-mule': minor
---

Now it is possible to pass variables to the browser in runJS:

```js
import { withBrowser } from 'test-mule';

test(
'runJS example with argument',
withBrowser(async ({ utils, screen }) => {
// element is an ElementHandle (pointer to an element in the browser)
const element = await screen.getByText(/button/i);
// we can pass element into runJS and the default exported function can access it as an Element
await utils.runJS(
`
export default (element) => console.log(element);
`,
[element],
);
}),
);
```
21 changes: 21 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -441,6 +441,27 @@ test(
);
```

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:

```js
import { withBrowser } from 'test-mule';

test(
'runJS example with argument',
withBrowser(async ({ utils, screen }) => {
// element is an ElementHandle (pointer to an element in the browser)
const element = await screen.getByText(/button/i);
// we can pass element into runJS and the default exported function can access it as an Element
await utils.runJS(
`
export default (element) => console.log(element);
`,
[element],
);
}),
);
```

#### `TestMuleUtils.loadJS(jsPath: string): Promise<void>`

Load a JS (or TS, JSX) file into the browser. Pass a path that will be resolved from your test file.
Expand Down
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

15 changes: 11 additions & 4 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,9 @@ export interface TestMuleUtils {
* including TS/JSX modules, and it supports resolving from node_modules,
* and relative paths from the test file.
* The code string supports top-level await to wait for a Promise to resolve.
* You can pass an array of variables to be passed into the browser as the 2nd parameter.
*/
runJS(code: string): Promise<void>;
runJS(code: string, args?: unknown[]): Promise<void>;

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

const runJS: TestMuleUtils['runJS'] = async (code) => {
const runJS: TestMuleUtils['runJS'] = async (code, args) => {
const encodedCode = encodeURIComponent(code);
// This uses the testPath as the url so that if there are relative imports
// in the inline code, the relative imports are resolved relative to the test file
const url = `http://localhost:${port}/${testPath}?inline-code=${encodedCode}`;
const res = (await safeEvaluate(
runJS,
`import(${JSON.stringify(url)})
.then(m => {})
new Function(
'...args',
`return import(${JSON.stringify(url)})
.then(async m => {
if (m.default) await m.default(...args)
})
.catch(e =>
e instanceof Error
? { message: e.message, stack: e.stack }
: e)`,
) as () => any,
...(Array.isArray(args) ? (args as any) : []),
)) as undefined | { message: string; stack: string };
if (res === undefined) return;
if (typeof res !== 'object') throw res;
Expand Down
21 changes: 21 additions & 0 deletions tests/utils/runJS.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,27 @@ test(
}),
);

test(
'allows passing ElementHandles and serializable values into browser',
withBrowser(async ({ utils, screen }) => {
const heading = await createHeading({ utils, screen });

await utils.runJS(
`
export default (heading, object) => {
if (heading.outerHTML !== "<h1>I'm a heading</h1>") {
throw new Error('element was not passed correctly')
}
if (object.some.serializable.value !== false) {
throw new Error('object was not passed correctly')
}
}
`,
[heading, { some: { serializable: { value: false } } }],
);
}),
);

describe('Waiting for Promises in executed code', () => {
it(
'should not wait for non-exported promises',
Expand Down

0 comments on commit 732fbff

Please sign in to comment.