Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update docs for Cypress.require() #5020

Merged
merged 13 commits into from
Feb 13, 2023
66 changes: 28 additions & 38 deletions docs/api/commands/origin.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -406,54 +406,35 @@ into the callback.

### Dependencies / Sharing Code

[ES module dynamic `import()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import#dynamic_imports)
and/or
Within the `cy.origin()` callback,
[`Cypress.require()`](/api/cypress-api/require) can be utilized to include
[npm](https://www.npmjs.com/) packages and other files. It is functionally the
same as using
[CommonJS `require()`](https://nodejs.org/en/knowledge/getting-started/what-is-require/)
can be used within the callback to include [npm](https://www.npmjs.com/)
packages and other files.
in browser-targeted code.
emilyrohrbough marked this conversation as resolved.
Show resolved Hide resolved

:::caution

Using `import()` and `require()` within the callback requires enabling the
[`experimentalOriginDependencies`](/guides/references/experiments) flag in the
Cypress configuration and using version `5.15.0` or greater of the
[`@cypress/webpack-preprocessor`](https://github.com/cypress-io/cypress/tree/master/npm/webpack-preprocessor).
The `@cypress/webpack-preprocessor` is included in Cypress by default, but if
your project installs its own version in the Cypress configuration, make sure it
is version `5.15.0` or greater.

If using an older version of the webpack or a different preprocessor, you'll see
an error that includes the following text:

_Using `require()` or `import()` to include dependencies requires enabling the
`experimentalOriginDependencies` flag and using the latest version of
`@cypress/webpack-preprocessor`._

:::
Note that it is not possible to use
[CommonJS `require()`](https://nodejs.org/en/knowledge/getting-started/what-is-require/)
or
[ES module `import()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import#dynamic_imports)
within the callback.

:::caution

Using `require()` or `import()` within the callback from a `node_modules` plugin
is not currently supported. We anticipate adding support with issue
[#24976](https://github.com/cypress-io/cypress/issues/24976).
Using `Cypress.require()` within the callback requires enabling the
[`experimentalOriginDependencies`](/guides/references/experiments) option in the
Cypress configuration.

:::

Read more in the [`Cypress.require()` doc](/api/cypress-api/require) itself.

#### Example

```js
// ES modules
cy.origin('somesite.com', async () => {
const _ = await import('lodash')
const utils = await import('../support/utils')

// ... use lodash and utils ...
})

// CommonJS
cy.origin('somesite.com', () => {
const _ = require('lodash')
const utils = require('../support/utils')
const _ = Cypress.require('lodash')
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

one can still use Cypress._ here if they wanted to right?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, they can

const utils = Cypress.require('../support/utils')

// ... use lodash and utils ...
})
Expand Down Expand Up @@ -496,7 +477,7 @@ before(() => {
// calls in this spec. put it in your support file to make them available to
// all specs
cy.origin('somesite.com', () => {
require('../support/commands')
Cypress.require('../support/commands')
})
})

Expand All @@ -519,7 +500,7 @@ before(() => {
cy.origin('somesite.com', () => {
// makes commands defined in this file available to all callbacks
// for somesite.com
require('../support/commands')
Cypress.require('../support/commands')
})
})

Expand Down Expand Up @@ -568,9 +549,18 @@ There are other testing scenarios which are not currently covered by
However, `<iframe>` support is on [our roadmap](/guides/references/roadmap) for
inclusion in a future version of Cypress.

## History

| Version | Changes |
| ---------------------------------------------- | ----------------------------------------------------------------------------------------------------------- |
| [12.6.0](/guides/references/changelog#10-7-0) | Support for `Cypress.require()` added and support for CommonJS `require()` and ES module `import()` removed |
| [10.11.0](/guides/references/changelog#10-7-0) | Support for CommonJS `require()` and ES module `import()` added and support for `Cypress.require()` removed |
| [10.7.0](/guides/references/changelog#10-7-0) | Support for `Cypress.require()` added |

## See also

- [Easily test multi-domain workflows with cy.origin](https://cypress.io/blog/2022/04/25/cypress-9-6-0-easily-test-multi-domain-workflows-with-cy-origin/)
- [Custom Commands](/api/cypress-api/custom-commands)
- [`Cypress.require()`](/api/cypress-api/require)
emilyrohrbough marked this conversation as resolved.
Show resolved Hide resolved
- [`cy.session()`](/api/commands/session)
- [`cy.visit()`](/api/commands/visit)
159 changes: 159 additions & 0 deletions docs/api/cypress-api/require.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
---
title: Cypress.require
e2eSpecific: true
---

`Cypress.require` enables utilizing dependencies within the
[`cy.origin()`](/api/commands/origin) callback function. It is used to require
modules such as [npm](https://www.npmjs.com/) packages and other local files.

It is functionally the same as using
[CommonJS `require()`](https://nodejs.org/en/knowledge/getting-started/what-is-require/)
in browser-targeted code.

## Syntax

```js
Cypress.require(moduleNameOrPath)
```

## Usage

**<Icon name="check-circle" color="green"></Icon> Correct Usage**

```js
cy.origin('example.com', () => {
const _ = Cypress.require('lodash')
const utils = Cypress.require('./utils')

// ... use lodash and utils ...
})
```

**<Icon name="exclamation-triangle" color="red"></Icon> Incorrect Usage**

```js
// `Cypress.require()` cannot be used outside the `cy.origin()` callback.
// Use CommonJS `require()` instead
const _ = Cypress.require('lodash')

cy.origin('example.com', async () => {
// `require()` and `import()` cannot be used inside the `cy.origin()` callback.
// Use `Cypress.require()` instead
const _ = require('lodash')
const utils = await import('./utils')
})
```

See
[`cy.origin()` Dependencies / Sharing Code](/api/commands/origin#Dependencies-Sharing-Code)
for more example usage.

## TypeScript

There are couple ways to have types inferred for the dependency required.

### Casting

```js
cy.origin('example.com', async () => {
const _ = Cypress.require('lodash') as typeof import('lodash')

// lodash methods are properly typed
_.map([1, 2, 3], (num: number) => {
// ...
})
})
```

### Generic

```js
import type { LoDashStatic } from 'lodash'

cy.origin('example.com', async () => {
const _ = Cypress.require < LoDashStatic > 'lodash'

// lodash methods are properly typed
_.map([1, 2, 3], (num: number) => {
// ...
})
})
```

## Limitations / Requirements

- `Cypress.require` only works when called within the
[`cy.origin()`](/api/commands/origin) callback function. It will error if used
elsewhere.
- Only the folowing file extensions are supported for dependencies required
(either directly or indirectly):
- `.js`
- `.jsx`
- `.ts`
- `.tsx`
- `.mjs`
- `.json`
- `.coffee`
- `Cypress.require('dependency-name')` must on one line as a continuous string:

<!-- prettier-ignore-start -->
```js
// ✅ GOOD
Cypress.require('lodash')

// ❌ BAD
const { require } = Cypress
require('lodash')

// ❌ BAD
Cypress
.require('lodash')

// ❌ BAD
Cypress . require('lodash')

// ❌ BAD
Cypress.require(
'lodash'
)
```
<!-- prettier-ignore-end -->

## Why not `require` or `import`?

Cypress runs test code in the browser, so any dependencies have to be bundled
with the test code before it is run. This is accomplished for the majority of
test code via a [preprocessor](/api/plugins/preprocessors-api). However, due to
the way that [`cy.origin()`](/api/commands/origin) works, the callback passed to
it is extracted from the test code where it is defined and run in a different
context. This also separates it from any dependencies that are included in the
test code bundle.

To solve this, we replace all instances of `Cypress.require` with `require` at
runtime and bundle just the callback with any dependencies required within it.

Using `Cypress.require` in the test code instead of `require` or `import` keeps
the dependencies required within the callback from being bundled with the rest
of the test code. We cannot bring them along with the callback to where it is
run, so it would waste execution time and memory to bundle its dependencies in
the original preprocessing. The dependencies are only bundled with the code that
needs them when we preprocess the callback itself.

Additionally, we tried a solution previously that allowed use of `require` and
`import`, but found that it hurt performance not only for spec files using them
within the `cy.origin()` callback, but even for spec files that did not use
them. That solution was also preprocessor-dependent and only worked for the
webpack preprocessor. Using `Cypress.require` is preprocessor-agnostic.

## History

| Version | Changes |
| ---------------------------------------------- | --------------------------------------------------------------------------------------------------- |
| [12.6.0](/guides/references/changelog#10-7-0) | `Cypress.require` added and support for using CommonJS `require()` and ES module `import()` removed |
| [10.11.0](/guides/references/changelog#10-7-0) | `Cypress.require` removed in favor of CommonJS `require()` and ES module `import()` |
| [10.7.0](/guides/references/changelog#10-7-0) | `Cypress.require` added |

## See also

- [`cy.origin()`](/api/commands/origin)
12 changes: 11 additions & 1 deletion docs/guides/references/changelog.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,16 @@
title: Changelog
---

## 12.6.0

_Released 02/14/2023_

**Features:**

- Added `Cypress.require()` for including dependencies within the `cy.origin()`
callback. Removed support for `require()` and `import()` within the callback.
Addresses [#24976](https://github.com/cypress-io/cypress/issues/24976).

## 12.5.1

_Released 02/2/2023_
Expand Down Expand Up @@ -1238,7 +1248,7 @@ _Released 8/15/2022_
- In the 'Create Blank Spec' dialog, pressing enter or return when the focus is
on the spec path input field will create the blank spec. Fixes
[#21815](https://github.com/cypress-io/cypress/issues/21815).
- Fixes React 18 unmount component handling to resolve the
- Fixes React 18 unmount component handling to resolve the
`lastMountedReactDom.unmountComponentAtNode is not a function` error. Fixes
[#23081](https://github.com/cypress-io/cypress/issues/23081).
- Fixes a regression introduced in Cypress 10.0 where Cypress no longer
Expand Down