mirror of
https://github.com/cypress-io/cypress.git
synced 2026-01-16 04:00:01 -06:00
fix: improve handling of userland injected styles in component testing (#16024)
* feat(npm/react): do not clear head between tests * add a shared mount utils library * add readme * update dependencies * add mount utils to circle * change module Co-authored-by: Barthélémy Ledoux <bart@cypress.io>
This commit is contained in:
13
circle.yml
13
circle.yml
@@ -1237,6 +1237,15 @@ jobs:
|
||||
path: npm/react/test_results
|
||||
- store-npm-logs
|
||||
|
||||
npm-mount-utils:
|
||||
<<: *defaults
|
||||
steps:
|
||||
- attach_workspace:
|
||||
at: ~/
|
||||
- run:
|
||||
name: Build
|
||||
command: yarn workspace @cypress/mount-utils build
|
||||
- store-npm-logs
|
||||
|
||||
npm-create-cypress-tests:
|
||||
<<: *defaults
|
||||
@@ -1865,6 +1874,9 @@ linux-workflow: &linux-workflow
|
||||
- npm-react:
|
||||
requires:
|
||||
- build
|
||||
- npm-mount-utils:
|
||||
requires:
|
||||
- build
|
||||
- npm-create-cypress-tests:
|
||||
requires:
|
||||
- build
|
||||
@@ -1880,6 +1892,7 @@ linux-workflow: &linux-workflow
|
||||
- npm-eslint-plugin-dev
|
||||
- npm-create-cypress-tests
|
||||
- npm-react
|
||||
- npm-mount-utils
|
||||
- npm-vue
|
||||
- npm-design-system
|
||||
- npm-webpack-batteries-included-preprocessor
|
||||
|
||||
0
npm/mount-utils/CHANGELOG.md
Normal file
0
npm/mount-utils/CHANGELOG.md
Normal file
10
npm/mount-utils/README.md
Normal file
10
npm/mount-utils/README.md
Normal file
@@ -0,0 +1,10 @@
|
||||
# @cypress/mount-utils
|
||||
|
||||
> **Note** this package is not meant to be used outside of cypress component testing.
|
||||
|
||||
This librares exports some shared types and utility functions designed to build adapters for components frameworks.
|
||||
|
||||
It is used in:
|
||||
|
||||
- [`@cypress/react`](https://github.com/cypress-io/cypress/tree/develop/npm/react)
|
||||
- [`@cypress/vue`](https://github.com/cypress-io/cypress/tree/develop/npm/vue)
|
||||
28
npm/mount-utils/package.json
Normal file
28
npm/mount-utils/package.json
Normal file
@@ -0,0 +1,28 @@
|
||||
{
|
||||
"name": "@cypress/mount-utils",
|
||||
"version": "0.0.0-development",
|
||||
"description": "Shared utilities for the various component testing adapters",
|
||||
"main": "dist/index.js",
|
||||
"scripts": {
|
||||
"build": "tsc",
|
||||
"build-prod": "tsc",
|
||||
"watch": "tsc -w"
|
||||
},
|
||||
"dependencies": {},
|
||||
"devDependencies": {
|
||||
"typescript": "^4.2.3"
|
||||
},
|
||||
"files": [
|
||||
"dist"
|
||||
],
|
||||
"license": "MIT",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/cypress-io/cypress.git"
|
||||
},
|
||||
"homepage": "https://github.com/cypress-io/cypress/tree/master/npm/mount-utils#readme",
|
||||
"bugs": "https://github.com/cypress-io/cypress/issues/new?template=1-bug-report.md",
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,66 @@
|
||||
import { StyleOptions } from './mount'
|
||||
/**
|
||||
* 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_ID = '__cy_root'
|
||||
|
||||
/**
|
||||
* 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)
|
||||
}
|
||||
})
|
||||
|
||||
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.
|
||||
@@ -14,6 +76,7 @@ function insertStylesheets (
|
||||
link.type = 'text/css'
|
||||
link.rel = 'stylesheet'
|
||||
link.href = href
|
||||
link.dataset.cy = 'injected-stylesheet'
|
||||
document.body.insertBefore(link, el)
|
||||
})
|
||||
}
|
||||
@@ -25,6 +88,7 @@ function insertStyles (styles: string[], document: Document, el: HTMLElement | n
|
||||
styles.forEach((style) => {
|
||||
const styleElement = document.createElement('style')
|
||||
|
||||
styleElement.dataset.cy = 'injected-style-tag'
|
||||
styleElement.appendChild(document.createTextNode(style))
|
||||
document.body.insertBefore(styleElement, el)
|
||||
})
|
||||
@@ -124,3 +188,19 @@ export const injectStylesBeforeElement = (
|
||||
|
||||
return insertLocalCssFiles(cssFiles, document, el, options.log)
|
||||
}
|
||||
|
||||
export function setupHooks (optionalCallback?: Function) {
|
||||
// When running component specs, we cannot allow "cy.visit"
|
||||
// because it will wipe out our preparation work, and does not make much sense
|
||||
// thus we overwrite "cy.visit" to throw an error
|
||||
Cypress.Commands.overwrite('visit', () => {
|
||||
throw new Error(
|
||||
'cy.visit from a component spec is not allowed',
|
||||
)
|
||||
})
|
||||
|
||||
beforeEach(() => {
|
||||
optionalCallback?.()
|
||||
cleanupStyles()
|
||||
})
|
||||
}
|
||||
52
npm/mount-utils/tsconfig.json
Normal file
52
npm/mount-utils/tsconfig.json
Normal file
@@ -0,0 +1,52 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
/* Basic Options */
|
||||
"target": "es2015" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', or 'ESNEXT'. */,
|
||||
"module": "esnext" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */,
|
||||
"skipLibCheck": true,
|
||||
"lib": [
|
||||
"es2015",
|
||||
"dom"
|
||||
] /* Specify library files to be included in the compilation: */,
|
||||
"allowJs": true /* Allow javascript files to be compiled. */,
|
||||
// "checkJs": true, /* Report errors in .js files. */
|
||||
// "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */
|
||||
"declaration": true /* Generates corresponding '.d.ts' file. */,
|
||||
// "sourceMap": true, /* Generates corresponding '.map' file. */
|
||||
// "outFile": "./", /* Concatenate and emit output to single file. */
|
||||
"outDir": "dist" /* Redirect output structure to the directory. */,
|
||||
// "rootDir": "src", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
|
||||
// "removeComments": true, /* Do not emit comments to output. */
|
||||
// "noEmit": true, /* Do not emit outputs. */
|
||||
// "importHelpers": true, /* Import emit helpers from 'tslib'. */
|
||||
// "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */
|
||||
// "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */
|
||||
|
||||
/* Strict Type-Checking Options */
|
||||
"strict": false /* Enable all strict type-checking options. */,
|
||||
// "noImplicitAny": true,
|
||||
|
||||
/* Module Resolution Options */
|
||||
// "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
|
||||
// "baseUrl": "./", /* Base directory to resolve non-absolute module names. */
|
||||
// "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
|
||||
// "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */
|
||||
// "typeRoots": [], /* List of folders to include type definitions from. */
|
||||
"types": ["cypress"] /* Type declaration files to be included in compilation. */,
|
||||
"allowSyntheticDefaultImports": true /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */,
|
||||
// "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */
|
||||
|
||||
/* Source Map Options */
|
||||
// "sourceRoot": "./", /* Specify the location where debugger should locate TypeScript files instead of source locations. */
|
||||
// "mapRoot": "./", /* Specify the location where debugger should locate map files instead of generated locations. */
|
||||
// "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */
|
||||
// "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */
|
||||
|
||||
/* Experimental Options */
|
||||
// "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */
|
||||
// "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */
|
||||
"esModuleInterop": true
|
||||
},
|
||||
"include": ["src"],
|
||||
"exclude": ["node_modules", "*.js"]
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
import * as React from 'react'
|
||||
import { mount } from '@cypress/react'
|
||||
import styled, { ThemeProvider } from 'styled-components'
|
||||
|
||||
const lightest = '#FFFEFD'
|
||||
const light = '#FEFCF1'
|
||||
const darker = '#C49A03'
|
||||
const darkest = '#382E0A'
|
||||
|
||||
export const theme = {
|
||||
primaryDark: darkest,
|
||||
primaryLight: lightest,
|
||||
primaryLightDarker: light,
|
||||
primaryHover: darker,
|
||||
}
|
||||
|
||||
const styledComponentsStyle = 'margin-bottom:1rem'
|
||||
const Line = styled.div`
|
||||
${styledComponentsStyle}
|
||||
`
|
||||
|
||||
export const SearchResults = (props) => {
|
||||
return (
|
||||
<div>
|
||||
{props.results.map((result) => {
|
||||
return (
|
||||
<Line>
|
||||
{result.title}
|
||||
</Line>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const mountComponent = ({ results }, options) => {
|
||||
return mount(
|
||||
<ThemeProvider theme={theme}>
|
||||
<div style={{ margin: '6rem', maxWidth: '105rem' }}>
|
||||
<SearchResults results={results} />
|
||||
</div>
|
||||
</ThemeProvider>,
|
||||
options,
|
||||
)
|
||||
}
|
||||
|
||||
const inlineStyle = 'body { background: blue; }'
|
||||
const bulmaCDN = 'https://cdnjs.cloudflare.com/ajax/libs/bulma/0.7.2/css/bulma.css'
|
||||
|
||||
describe('SearchResults', () => {
|
||||
it('should inject styles into <head>', () => {
|
||||
mountComponent({
|
||||
results: [{ title: 'Org 1' }, { title: 'Org 2' }],
|
||||
},
|
||||
{
|
||||
stylesheets: [bulmaCDN],
|
||||
style: inlineStyle,
|
||||
})
|
||||
|
||||
cy.get('link').should('exist')
|
||||
cy.get('link').should('have.attr', 'href', bulmaCDN)
|
||||
})
|
||||
|
||||
it('style-components injected styles from previous test should not be cleaned up \
|
||||
but styles and stylesheets in mount should be', () => {
|
||||
// style-components injected style should NOT have bene cleaned up
|
||||
cy.get('style').should('contain.text', styledComponentsStyle)
|
||||
|
||||
// cleaned up inline <style> from previous test
|
||||
cy.get('style').should('not.contain.text', inlineStyle)
|
||||
|
||||
// cleaned up bulma CDN link from previous test
|
||||
cy.get('link').should('not.exist')
|
||||
|
||||
mountComponent({
|
||||
results: [{ title: 'Org 1' }, { title: 'Org 2' }],
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -17,6 +17,7 @@
|
||||
"watch": "yarn build --watch --watch.exclude ./dist/**/*"
|
||||
},
|
||||
"dependencies": {
|
||||
"@cypress/mount-utils": "0.0.0-development",
|
||||
"@cypress/webpack-preprocessor": "0.0.0-development",
|
||||
"debug": "4.3.2",
|
||||
"find-webpack": "2.2.1",
|
||||
|
||||
@@ -24,6 +24,7 @@ function createEntry (options) {
|
||||
external: [
|
||||
'react',
|
||||
'react-dom',
|
||||
'@cypress/mount-utils',
|
||||
],
|
||||
plugins: [
|
||||
resolve(), commonjs(),
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
export function setupHooks (unmount: (opts: { log: boolean }) => void) {
|
||||
// When running component specs, we cannot allow "cy.visit"
|
||||
// because it will wipe out our preparation work, and does not make much sense
|
||||
// thus we overwrite "cy.visit" to throw an error
|
||||
Cypress.Commands.overwrite('visit', () => {
|
||||
throw new Error(
|
||||
'cy.visit from a component spec is not allowed',
|
||||
)
|
||||
})
|
||||
|
||||
/**
|
||||
* Remove any style or extra link elements from the iframe placeholder
|
||||
* left from any previous test
|
||||
*
|
||||
*/
|
||||
function cleanupStyles () {
|
||||
const styles = document.body.querySelectorAll('style')
|
||||
|
||||
styles.forEach((styleElement) => {
|
||||
if (styleElement.parentElement) {
|
||||
styleElement.parentElement.removeChild(styleElement)
|
||||
}
|
||||
})
|
||||
|
||||
const links = document.body.querySelectorAll('link[rel=stylesheet]')
|
||||
|
||||
links.forEach((link) => {
|
||||
if (link.parentElement) {
|
||||
link.parentElement.removeChild(link)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
unmount({ log: false })
|
||||
cleanupStyles()
|
||||
})
|
||||
}
|
||||
@@ -1,10 +1,12 @@
|
||||
import * as React from 'react'
|
||||
import * as ReactDOM from 'react-dom'
|
||||
import getDisplayName from './getDisplayName'
|
||||
import { injectStylesBeforeElement } from './utils'
|
||||
import { setupHooks } from './hooks'
|
||||
|
||||
const ROOT_ID = '__cy_root'
|
||||
import {
|
||||
injectStylesBeforeElement,
|
||||
StyleOptions,
|
||||
ROOT_ID,
|
||||
setupHooks,
|
||||
} from '@cypress/mount-utils'
|
||||
|
||||
/**
|
||||
* Inject custom style text or CSS file or 3rd party style resources
|
||||
@@ -107,17 +109,11 @@ export const mount = (jsx: React.ReactNode, options: MountOptions = {}) => {
|
||||
})
|
||||
}
|
||||
|
||||
let initialInnerHtml = ''
|
||||
|
||||
Cypress.on('run:start', () => {
|
||||
initialInnerHtml = document.head.innerHTML
|
||||
})
|
||||
|
||||
/**
|
||||
* Removes the mounted component. Notice this command automatically
|
||||
* queues up the `unmount` into Cypress chain, thus you don't need `.then`
|
||||
* to call it.
|
||||
* @see https://github.com/bahmutov/@cypress/react/tree/main/cypress/component/basic/unmount
|
||||
* @see https://github.com/cypress-io/cypress/tree/develop/npm/react/cypress/component/basic/unmount
|
||||
* @example
|
||||
```
|
||||
import { mount, unmount } from '@cypress/react'
|
||||
@@ -150,9 +146,7 @@ Cypress.on('test:before:run', () => {
|
||||
const el = document.getElementById(ROOT_ID)
|
||||
|
||||
if (el) {
|
||||
const wasUnmounted = ReactDOM.unmountComponentAtNode(el)
|
||||
|
||||
document.head.innerHTML = initialInnerHtml
|
||||
ReactDOM.unmountComponentAtNode(el)
|
||||
}
|
||||
})
|
||||
|
||||
@@ -198,45 +192,6 @@ export interface ReactModule {
|
||||
source: string
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 interface MountReactComponentOptions {
|
||||
alias: string
|
||||
ReactDom: typeof ReactDOM
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
import { mount, mountCallback } from '@cypress/vue'
|
||||
import RedBox from './RedBox.vue'
|
||||
|
||||
const tailwindCdnLink = 'https://unpkg.com/tailwindcss@^1.0/dist/tailwind.min.css'
|
||||
|
||||
const inlineStyle = 'body { background: blue; }'
|
||||
|
||||
describe('RedBox 1', () => {
|
||||
const template = '<red-box :status="true" />'
|
||||
const options = {
|
||||
@@ -11,14 +15,17 @@ describe('RedBox 1', () => {
|
||||
},
|
||||
// you can inject additional styles to be downloaded
|
||||
//
|
||||
style: inlineStyle,
|
||||
stylesheets: [
|
||||
// you can use external links
|
||||
'https://unpkg.com/tailwindcss@^1.0/dist/tailwind.min.css',
|
||||
tailwindCdnLink,
|
||||
],
|
||||
}
|
||||
|
||||
it('displays red Hello RedBox', () => {
|
||||
mount({ template }, options)
|
||||
// shoud have injected the inline styling.
|
||||
cy.get('style').should('contain.text', inlineStyle)
|
||||
|
||||
cy.contains('Hello RedBox')
|
||||
cy.get('[data-cy=box]')
|
||||
@@ -38,12 +45,19 @@ describe('RedBox 2', () => {
|
||||
},
|
||||
stylesheets: [
|
||||
// you can use external links
|
||||
'https://unpkg.com/tailwindcss@^1.0/dist/tailwind.min.css',
|
||||
tailwindCdnLink,
|
||||
],
|
||||
}
|
||||
|
||||
beforeEach(mountCallback({ template }, options))
|
||||
beforeEach(() => {
|
||||
// should clean up links inserted via mounting options before each test.
|
||||
cy.get('link').should('not.exist')
|
||||
mount({ template }, options)
|
||||
})
|
||||
|
||||
it('displays Goodbye RedBox', () => {
|
||||
// cleaned up inline <style> from previous test
|
||||
cy.get('style').should('not.contain.text', inlineStyle)
|
||||
cy.contains('Goodbye RedBox')
|
||||
})
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
"test-ci": "node ../../scripts/run-ct-examples.js --examplesList=./examples.env"
|
||||
},
|
||||
"dependencies": {
|
||||
"@cypress/mount-utils": "0.0.0-development",
|
||||
"@vue/test-utils": "^1.1.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -24,6 +24,7 @@ function createEntry (options) {
|
||||
external: [
|
||||
'vue',
|
||||
'@vue/test-utils',
|
||||
'@cypress/mount-utils',
|
||||
'@cypress/webpack-dev-server',
|
||||
],
|
||||
plugins: [
|
||||
|
||||
@@ -7,14 +7,16 @@ import {
|
||||
Wrapper,
|
||||
enableAutoDestroy,
|
||||
} from '@vue/test-utils'
|
||||
|
||||
const ROOT_ID = '__cy_root'
|
||||
import {
|
||||
injectStylesBeforeElement,
|
||||
StyleOptions,
|
||||
ROOT_ID,
|
||||
setupHooks,
|
||||
} from '@cypress/mount-utils'
|
||||
|
||||
const defaultOptions: (keyof MountOptions)[] = [
|
||||
'vue',
|
||||
'extensions',
|
||||
'style',
|
||||
'stylesheets',
|
||||
]
|
||||
|
||||
const registerGlobalComponents = (Vue, options) => {
|
||||
@@ -224,37 +226,6 @@ interface MountOptions {
|
||||
*/
|
||||
vue: unknown
|
||||
|
||||
/**
|
||||
* CSS style string to inject when mounting the component
|
||||
*
|
||||
* @memberof MountOptions
|
||||
* @example
|
||||
* const style = `
|
||||
* .todo.done {
|
||||
* text-decoration: line-through;
|
||||
* color: gray;
|
||||
* }`
|
||||
* mount(Todo, { style })
|
||||
*/
|
||||
style: string
|
||||
|
||||
/**
|
||||
* Stylesheet(s) urls to inject as `<link ... />` elements when
|
||||
* mounting the component
|
||||
*
|
||||
* @memberof MountOptions
|
||||
* @example
|
||||
* const template = '...'
|
||||
* const stylesheets = '/node_modules/tailwindcss/dist/tailwind.min.css'
|
||||
* mount({ template }, { stylesheets })
|
||||
*
|
||||
* @example
|
||||
* const template = '...'
|
||||
* const stylesheets = ['https://cdn.../lib.css', 'https://lib2.css']
|
||||
* mount({ template }, { stylesheets })
|
||||
*/
|
||||
stylesheets: string | string[]
|
||||
|
||||
/**
|
||||
* Extra Vue plugins, mixins, local components to register while
|
||||
* mounting this component
|
||||
@@ -268,7 +239,7 @@ interface MountOptions {
|
||||
/**
|
||||
* Utility type for union of options passed to "mount(..., options)"
|
||||
*/
|
||||
type MountOptionsArgument = Partial<ComponentOptions & MountOptions & VueTestUtilsConfigOptions>
|
||||
type MountOptionsArgument = Partial<ComponentOptions & MountOptions & StyleOptions & VueTestUtilsConfigOptions>
|
||||
|
||||
// when we mount a Vue component, we add it to the global Cypress object
|
||||
// so here we extend the global Cypress namespace and its Cypress interface
|
||||
@@ -307,21 +278,20 @@ function failTestOnVueError (err, vm, info) {
|
||||
window.top.onerror(err)
|
||||
}
|
||||
|
||||
let initialInnerHtml = ''
|
||||
|
||||
Cypress.on('run:start', () => {
|
||||
initialInnerHtml = document.head.innerHTML
|
||||
})
|
||||
|
||||
function registerAutoDestroy ($destroy: () => void) {
|
||||
Cypress.on('test:before:run', () => {
|
||||
$destroy()
|
||||
document.head.innerHTML = initialInnerHtml
|
||||
})
|
||||
}
|
||||
|
||||
enableAutoDestroy(registerAutoDestroy)
|
||||
|
||||
const injectStyles = (options: StyleOptions) => {
|
||||
const el = document.getElementById(ROOT_ID)
|
||||
|
||||
return injectStylesBeforeElement(options, document, el)
|
||||
}
|
||||
|
||||
/**
|
||||
* Mounts a Vue component inside Cypress browser.
|
||||
* @param {object} component imported from Vue file
|
||||
@@ -352,6 +322,18 @@ export const mount = (
|
||||
.window({
|
||||
log: false,
|
||||
})
|
||||
.then(() => {
|
||||
const { style, stylesheets, stylesheet, styles, cssFiles, cssFile } = optionsOrProps
|
||||
|
||||
injectStyles({
|
||||
style,
|
||||
stylesheets,
|
||||
stylesheet,
|
||||
styles,
|
||||
cssFiles,
|
||||
cssFile,
|
||||
})
|
||||
})
|
||||
.then((win) => {
|
||||
const localVue = createLocalVue()
|
||||
|
||||
@@ -376,28 +358,6 @@ export const mount = (
|
||||
|
||||
let el = document.getElementById(ROOT_ID)
|
||||
|
||||
if (typeof options.stylesheets === 'string') {
|
||||
options.stylesheets = [options.stylesheets]
|
||||
}
|
||||
|
||||
if (Array.isArray(options.stylesheets)) {
|
||||
options.stylesheets.forEach((href) => {
|
||||
const link = document.createElement('link')
|
||||
|
||||
link.type = 'text/css'
|
||||
link.rel = 'stylesheet'
|
||||
link.href = href
|
||||
el.append(link)
|
||||
})
|
||||
}
|
||||
|
||||
if (options.style) {
|
||||
const style = document.createElement('style')
|
||||
|
||||
style.appendChild(document.createTextNode(options.style))
|
||||
el.append(style)
|
||||
}
|
||||
|
||||
const componentNode = document.createElement('div')
|
||||
|
||||
el.append(componentNode)
|
||||
@@ -431,3 +391,5 @@ export const mountCallback = (
|
||||
) => {
|
||||
return () => mount(component, options)
|
||||
}
|
||||
|
||||
setupHooks()
|
||||
|
||||
21
yarn.lock
21
yarn.lock
@@ -14087,7 +14087,7 @@ detect-node@^2.0.4:
|
||||
resolved "https://registry.yarnpkg.com/detect-node/-/detect-node-2.0.4.tgz#014ee8f8f669c5c58023da64b8179c083a28c46c"
|
||||
integrity sha512-ZIzRpLJrOj7jjP2miAtgqIfmzbxa4ZOr5jJc601zklsfEx9oTzmmj2nVpIPRpNlRTIh8lc1kyViIY7BWSGNmKw==
|
||||
|
||||
detect-port-alt@1.1.6, detect-port-alt@^1.1.6:
|
||||
detect-port-alt@1.1.6:
|
||||
version "1.1.6"
|
||||
resolved "https://registry.yarnpkg.com/detect-port-alt/-/detect-port-alt-1.1.6.tgz#24707deabe932d4a3cf621302027c2b266568275"
|
||||
integrity sha512-5tQykt+LqfJFBEYaDITx7S7cR7mJ/zQmLXZ2qt5w04ainYZw6tBf9dBunMjVeVOdYVRUzUOE4HkY5J7+uttb5Q==
|
||||
@@ -33168,23 +33168,6 @@ superagent@^3.8.3:
|
||||
qs "^6.5.1"
|
||||
readable-stream "^2.3.5"
|
||||
|
||||
superagent@^5.1.0:
|
||||
version "5.3.1"
|
||||
resolved "https://registry.yarnpkg.com/superagent/-/superagent-5.3.1.tgz#d62f3234d76b8138c1320e90fa83dc1850ccabf1"
|
||||
integrity sha512-wjJ/MoTid2/RuGCOFtlacyGNxN9QLMgcpYLDQlWFIhhdJ93kNscFonGvrpAHSCVjRVj++DGCglocF7Aej1KHvQ==
|
||||
dependencies:
|
||||
component-emitter "^1.3.0"
|
||||
cookiejar "^2.1.2"
|
||||
debug "^4.1.1"
|
||||
fast-safe-stringify "^2.0.7"
|
||||
form-data "^3.0.0"
|
||||
formidable "^1.2.2"
|
||||
methods "^1.1.2"
|
||||
mime "^2.4.6"
|
||||
qs "^6.9.4"
|
||||
readable-stream "^3.6.0"
|
||||
semver "^7.3.2"
|
||||
|
||||
supertest-session@4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/supertest-session/-/supertest-session-4.0.0.tgz#3b442cbc37ede15a4acf7f8c570b836d880f8a40"
|
||||
@@ -36342,7 +36325,7 @@ webpack@4.44.2:
|
||||
watchpack "^1.7.4"
|
||||
webpack-sources "^1.4.1"
|
||||
|
||||
webpack@^4.0.0, webpack@^4.18.1, webpack@^4.35.3, webpack@^4.44.1, webpack@^4.44.2:
|
||||
webpack@^4.0.0, webpack@^4.18.1, webpack@^4.44.1, webpack@^4.44.2:
|
||||
version "4.46.0"
|
||||
resolved "https://registry.yarnpkg.com/webpack/-/webpack-4.46.0.tgz#bf9b4404ea20a073605e0a011d188d77cb6ad542"
|
||||
integrity sha512-6jJuJjg8znb/xRItk7bkT0+Q7AHCYjjFnvKIWQPkNIOyRqoCGvkOs0ipeQzrqz4l5FtN5ZI/ukEHroeX/o1/5Q==
|
||||
|
||||
Reference in New Issue
Block a user