Skip to content

Commit

Permalink
fix: remove some CT functions and props (#24419)
Browse files Browse the repository at this point in the history
* fix: remove mountHook function for React

* fix: remove `unmount` from @cypress/react exports

* fix: remove `unmount` from the @cypress/react readme

* fix: don't alias React components on mount

* fix: remove `mountCallback` from Vue adapters

* fix: remove style injection utility functions from `mount-utils`

* fix: fix React tests

* fix: fix more React tests

* fix: fix more tests

* fix: fix screenshot test styles

* fix: update documentation around mount-utils styles; fix tests

* fix: update Vue docs to use `props` key rather than `propsData`

* fix: add test styles back in

* update unmount test and export getContainerEl for back compat

* better errors

* docs

* error for unmount

* test for error

* fix last test

* adjust language to reflect removed methods

* one last deprecation

* fix error

* wip - [skip ci]

* use proxy to catch errors

* deprecate alias

* update tests

* update on link

* use on links properly

* revert changes

Co-authored-by: Lachlan Miller <lachlan.miller.1990@outlook.com>
  • Loading branch information
astone123 and lmiller1990 committed Nov 4, 2022
1 parent f39eb1c commit 294985f
Show file tree
Hide file tree
Showing 56 changed files with 495 additions and 916 deletions.
28 changes: 5 additions & 23 deletions npm/mount-utils/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,7 @@ All the functionality used to create the first party Mount adapters is available

In addition, we recommend that Mount Adapters:

- receive a second argument that extends `StyleOptions` from `@cypress/mount-utils`
- calls `injectStylesBeforeElement` from `@cypress/mount-utils` before mounting the component
- calls `setupHooks` to register the required lifecycle hooks for `@cypress/mount-utils` to work

This will let the user inject styles `<style>...</style>` and stylesheets `<link rel="stylesheet">`, which is very useful for developing components.
- call `setupHooks` to register the required lifecycle hooks for `@cypress/mount-utils` to work

### Example Mount Adapter: Web Components

Expand All @@ -39,9 +35,7 @@ Here's a simple yet realistic example of Mount Adapter targeting Web Components.
import {
ROOT_SELECTOR,
setupHooks,
injectStylesBeforeElement,
getContainerEl,
StyleOptions
getContainerEl
} from "@cypress/mount-utils";

Cypress.on("run:start", () => {
Expand Down Expand Up @@ -69,8 +63,7 @@ function maybeRegisterComponent<T extends CustomElementConstructor>(
}

export function mount(
webComponent: CustomElementConstructor,
options?: Partial<StyleOptions>
webComponent: CustomElementConstructor
): Cypress.Chainable {
// Get root selector defined in `cypress/support.component-index.html
const $root = document.querySelector(ROOT_SELECTOR)!;
Expand All @@ -83,9 +76,6 @@ export function mount(
/// Register Web Component
maybeRegisterComponent(name, webComponent);

// Inject user styles before mounting the component
injectStylesBeforeElement(options ?? {}, document, getContainerEl())

// Render HTML containing component.
$root.innerHTML = `<${name} id="root"></${name}>`;

Expand All @@ -100,8 +90,7 @@ export function mount(
return cy.wrap(document.querySelector("#root"), { log: false });
}

// Setup Cypress lifecycle hooks. This tears down any styles
// injected by injectStylesBeforeElement, etc.
// Setup Cypress lifecycle hooks.
setupHooks();
```

Expand Down Expand Up @@ -131,14 +120,7 @@ export class WebCounter extends HTMLElement {

describe('web-component.cy.ts', () => {
it('playground', () => {
cy.mount(WebCounter, {
styles: `
button {
background: lightblue;
color: white;
}
`
})
cy.mount(WebCounter)
})
})
```
Expand Down
226 changes: 40 additions & 186 deletions npm/mount-utils/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,42 +1,3 @@
/**
* Additional styles to inject into the document.
* A component might need 3rd party libraries from CDN,
* local CSS files and custom styles.
*/
export interface StyleOptions {
/**
* Creates <link href="..." /> element for each stylesheet
* @alias stylesheet
*/
stylesheets: string | string[]
/**
* Creates <link href="..." /> element for each stylesheet
* @alias stylesheets
*/
stylesheet: string | string[]
/**
* Creates <style>...</style> element and inserts given CSS.
* @alias styles
*/
style: string | string[]
/**
* Creates <style>...</style> element for each given CSS text.
* @alias style
*/
styles: string | string[]
/**
* Loads each file and creates a <style>...</style> element
* with the loaded CSS
* @alias cssFile
*/
cssFiles: string | string[]
/**
* Single CSS file to load into a <style></style> element
* @alias cssFile
*/
cssFile: string | string[]
}

export const ROOT_SELECTOR = '[data-cy-root]'

export const getContainerEl = (): HTMLElement => {
Expand All @@ -49,154 +10,12 @@ export const getContainerEl = (): HTMLElement => {
throw Error(`No element found that matches selector ${ROOT_SELECTOR}. Please add a root element with data-cy-root attribute to your "component-index.html" file so that Cypress can attach your component to the DOM.`)
}

/**
* Remove any style or extra link elements from the iframe placeholder
* left from any previous test
*
*/
export function cleanupStyles () {
const styles = document.body.querySelectorAll('[data-cy=injected-style-tag]')

styles.forEach((styleElement) => {
if (styleElement.parentElement) {
styleElement.parentElement.removeChild(styleElement)
export function checkForRemovedStyleOptions (mountingOptions: Record<string, any>) {
for (const key of ['cssFile', 'cssFiles', 'style', 'styles', 'stylesheet', 'stylesheets'] as const) {
if (mountingOptions[key]) {
Cypress.utils.throwErrByPath('mount.removed_style_mounting_options', key)
}
})

const links = document.body.querySelectorAll('[data-cy=injected-stylesheet]')

links.forEach((link) => {
if (link.parentElement) {
link.parentElement.removeChild(link)
}
})
}

/**
* Insert links to external style resources.
*/
function insertStylesheets (
stylesheets: string[],
document: Document,
el: HTMLElement | null,
) {
stylesheets.forEach((href) => {
const link = document.createElement('link')

link.type = 'text/css'
link.rel = 'stylesheet'
link.href = href
link.dataset.cy = 'injected-stylesheet'
document.body.insertBefore(link, el)
})
}

/**
* Inserts a single stylesheet element
*/
function insertStyles (styles: string[], document: Document, el: HTMLElement | null) {
styles.forEach((style) => {
const styleElement = document.createElement('style')

styleElement.dataset.cy = 'injected-style-tag'
styleElement.appendChild(document.createTextNode(style))
document.body.insertBefore(styleElement, el)
})
}

function insertSingleCssFile (
cssFilename: string,
document: Document,
el: HTMLElement | null,
log?: boolean,
) {
return cy.readFile(cssFilename, { log }).then((css) => {
const style = document.createElement('style')

style.appendChild(document.createTextNode(css))
document.body.insertBefore(style, el)
})
}

/**
* Reads the given CSS file from local file system
* and adds the loaded style text as an element.
*/
function insertLocalCssFiles (
cssFilenames: string[],
document: Document,
el: HTMLElement | null,
log?: boolean,
) {
return Cypress.Promise.mapSeries(cssFilenames, (cssFilename) => {
return insertSingleCssFile(cssFilename, document, el, log)
})
}

/**
* Injects custom style text or CSS file or 3rd party style resources
* into the given document.
*/
export const injectStylesBeforeElement = (
options: Partial<StyleOptions & { log: boolean }>,
document: Document,
el: HTMLElement | null,
): HTMLElement => {
if (!el) return

// first insert all stylesheets as Link elements
let stylesheets: string[] = []

if (typeof options.stylesheet === 'string') {
stylesheets.push(options.stylesheet)
} else if (Array.isArray(options.stylesheet)) {
stylesheets = stylesheets.concat(options.stylesheet)
}

if (typeof options.stylesheets === 'string') {
options.stylesheets = [options.stylesheets]
}

if (options.stylesheets) {
stylesheets = stylesheets.concat(options.stylesheets)
}

insertStylesheets(stylesheets, document, el)

// insert any styles as <style>...</style> elements
let styles: string[] = []

if (typeof options.style === 'string') {
styles.push(options.style)
} else if (Array.isArray(options.style)) {
styles = styles.concat(options.style)
}

if (typeof options.styles === 'string') {
styles.push(options.styles)
} else if (Array.isArray(options.styles)) {
styles = styles.concat(options.styles)
}

insertStyles(styles, document, el)

// now load any css files by path and add their content
// as <style>...</style> elements
let cssFiles: string[] = []

if (typeof options.cssFile === 'string') {
cssFiles.push(options.cssFile)
} else if (Array.isArray(options.cssFile)) {
cssFiles = cssFiles.concat(options.cssFile)
}

if (typeof options.cssFiles === 'string') {
cssFiles.push(options.cssFiles)
} else if (Array.isArray(options.cssFiles)) {
cssFiles = cssFiles.concat(options.cssFiles)
}

return insertLocalCssFiles(cssFiles, document, el, options.log)
}

export function setupHooks (optionalCallback?: Function) {
Expand All @@ -220,6 +39,41 @@ export function setupHooks (optionalCallback?: Function) {
// @ts-ignore
Cypress.on('test:before:run', () => {
optionalCallback?.()
cleanupStyles()
})
}

/**
* Remove any style or extra link elements from the iframe placeholder
* left from any previous test
*
* Removed as of Cypress 11.0.0
* @see https://on.cypress.io/migration-11-0-0-component-testing-updates
*/
export function cleanupStyles () {
Cypress.utils.throwErrByPath('mount.cleanup_styles')
}

/**
* Additional styles to inject into the document.
* A component might need 3rd party libraries from CDN,
* local CSS files and custom styles.
*
* Removed as of Cypress 11.0.0.
* @see https://on.cypress.io/migration-11-0-0-component-testing-updates
*/
export type StyleOptions = unknown

/**
* Injects custom style text or CSS file or 3rd party style resources
* into the given document.
*
* Removed as of Cypress 11.0.0.
* @see https://on.cypress.io/migration-11-0-0-component-testing-updates
*/
export const injectStylesBeforeElement = (
options: Partial<StyleOptions & { log: boolean }>,
document: Document,
el: HTMLElement | null,
) => {
Cypress.utils.throwErrByPath('mount.inject_styles_before_element')
}
24 changes: 2 additions & 22 deletions npm/react/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,6 @@ For more information, please check the official docs for [running Cypress](https

- `mount` is the most important function, allows to mount a given React component as a mini web application and interact with it using Cypress commands
- `createMount` factory function that creates new `mount` function with default options
- `unmount` removes previously mounted component, mostly useful to test how the component cleans up after itself
- `mountHook` mounts a given React Hook in a test component for full testing, see `hooks` example

## Examples

Expand Down Expand Up @@ -65,20 +63,7 @@ it('looks right', () => {
})
```

### Extra styles

You can pass additional styles, css files and external stylesheets to load, see [docs/styles.md](./docs/styles.md) for the full list of options.

```js
const todo = {
id: '123',
title: 'Write more tests',
}
mount(<Todo todo={todo} />, {
stylesheets: [
'https://cdnjs.cloudflare.com/ajax/libs/bulma/0.7.2/css/bulma.css',
],
})
See [docs/styles.md](./docs/styles.md) for full list of options.
```
You may also specify the `ReactDOM` package to use. This can be useful in complex monorepo setups that have different versions of React and React DOM installed. If you see an error relating to [mismatching versions of React or React DOM](https://reactjs.org/warnings/invalid-hook-call-warning.html#mismatching-versions-of-react-and-react-dom), this may be the solution. You can do this using the `ReactDom` option:
Expand All @@ -87,12 +72,7 @@ You may also specify the `ReactDOM` package to use. This can be useful in comple
// if you have multiple versions of ReactDom in your monorepo
import ReactDom from 'react-dom'
mount(<Todo todo={todo} />, {
stylesheets: [
'https://cdnjs.cloudflare.com/ajax/libs/bulma/0.7.2/css/bulma.css',
],
ReactDom
})
mount(<Todo todo={todo} />, { reactDom: ReactDom })
```

## Compatibility
Expand Down
7 changes: 1 addition & 6 deletions npm/react/cypress/component/advanced/hooks/README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,3 @@
# testing React hooks

- [counter-with-hooks.spec.js](counter-with-hooks.spec.js) and [counter2-with-hooks.spec.js](counter2-with-hooks.spec.js) test React components that uses hooks
- [use-counter.spec.js](use-counter.spec.js) shows how to test a React hook using `mountHook` function

![Hook test](images/hook.png)

Note: hooks are mounted inside a test component following the approach shown in [react-hooks-testing-library](https://github.com/testing-library/react-hooks-testing-library/blob/master/src/pure.js)
- [counter-with-hooks.spec.js](counter-with-hooks.spec.js) and [counter2-with-hooks.spec.js](counter2-with-hooks.spec.js) test React components that uses hooks
Binary file not shown.
25 changes: 0 additions & 25 deletions npm/react/cypress/component/advanced/hooks/use-counter.cy.jsx

This file was deleted.

Loading

0 comments on commit 294985f

Please sign in to comment.