cypress 10.6.0 → 10.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/angular/CHANGELOG.md +31 -0
- package/angular/README.md +43 -112
- package/angular/dist/index.js +8 -6
- package/angular/dist/mount.d.ts +1 -0
- package/angular/package.json +6 -8
- package/mount-utils/package.json +5 -1
- package/package.json +10 -4
- package/react/dist/createMount.d.ts +5 -2
- package/react/dist/cypress-react.cjs.js +80 -115
- package/react/dist/cypress-react.esm-bundler.js +66 -101
- package/react/dist/mount.d.ts +1 -0
- package/react/dist/mountHook.d.ts +1 -0
- package/react/dist/types.d.ts +1 -0
- package/react/package.json +2 -6
- package/react18/dist/cypress-react.cjs.js +59 -88
- package/react18/dist/cypress-react.esm-bundler.js +45 -74
- package/react18/dist/index.d.ts +1 -0
- package/react18/package.json +2 -2
- package/svelte/CHANGELOG.md +0 -0
- package/svelte/README.md +83 -0
- package/svelte/dist/cypress-svelte.cjs.js +213 -0
- package/svelte/dist/cypress-svelte.esm-bundler.js +209 -0
- package/svelte/dist/index.d.ts +1 -0
- package/svelte/dist/mount.d.ts +30 -0
- package/svelte/package.json +43 -0
- package/types/cypress-type-helpers.d.ts +3 -1
- package/types/cypress.d.ts +43 -4
- package/vue/dist/cypress-vue.cjs.js +30 -38
- package/vue/dist/cypress-vue.esm-bundler.js +30 -38
- package/vue/dist/index.d.ts +1 -0
- package/vue/package.json +2 -7
- package/vue2/dist/cypress-vue2.cjs.js +53 -84
- package/vue2/dist/cypress-vue2.esm-bundler.js +53 -84
- package/vue2/dist/index.d.ts +1 -0
- package/vue2/package.json +2 -5
- package/vue2/dist/cypress-vue2.browser.js +0 -20197
@@ -9,46 +9,19 @@ import ReactDOM from 'react-dom/client';
|
|
9
9
|
import * as React from 'react';
|
10
10
|
import 'react-dom';
|
11
11
|
|
12
|
-
|
13
|
-
Copyright (c) Microsoft Corporation.
|
14
|
-
|
15
|
-
Permission to use, copy, modify, and/or distribute this software for any
|
16
|
-
purpose with or without fee is hereby granted.
|
17
|
-
|
18
|
-
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
19
|
-
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
20
|
-
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
21
|
-
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
22
|
-
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
23
|
-
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
24
|
-
PERFORMANCE OF THIS SOFTWARE.
|
25
|
-
***************************************************************************** */
|
26
|
-
|
27
|
-
var __assign = function() {
|
28
|
-
__assign = Object.assign || function __assign(t) {
|
29
|
-
for (var s, i = 1, n = arguments.length; i < n; i++) {
|
30
|
-
s = arguments[i];
|
31
|
-
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];
|
32
|
-
}
|
33
|
-
return t;
|
34
|
-
};
|
35
|
-
return __assign.apply(this, arguments);
|
36
|
-
};
|
37
|
-
|
38
|
-
var cachedDisplayNames = new WeakMap();
|
12
|
+
const cachedDisplayNames = new WeakMap();
|
39
13
|
/**
|
40
14
|
* Gets the display name of the component when possible.
|
41
15
|
* @param type {JSX} The type object returned from creating the react element.
|
42
16
|
* @param fallbackName {string} The alias, or fallback name to use when the name cannot be derived.
|
43
17
|
* @link https://github.com/facebook/react-devtools/blob/master/backend/getDisplayName.js
|
44
18
|
*/
|
45
|
-
function getDisplayName(type, fallbackName) {
|
46
|
-
|
47
|
-
var nameFromCache = cachedDisplayNames.get(type);
|
19
|
+
function getDisplayName(type, fallbackName = 'Unknown') {
|
20
|
+
const nameFromCache = cachedDisplayNames.get(type);
|
48
21
|
if (nameFromCache != null) {
|
49
22
|
return nameFromCache;
|
50
23
|
}
|
51
|
-
|
24
|
+
let displayName = null;
|
52
25
|
// The displayName property is not guaranteed to be a string.
|
53
26
|
// It's only safe to use for our purposes if it's a string.
|
54
27
|
// github.com/facebook/react-devtools/issues/803
|
@@ -60,13 +33,13 @@ function getDisplayName(type, fallbackName) {
|
|
60
33
|
}
|
61
34
|
// Facebook-specific hack to turn "Image [from Image.react]" into just "Image".
|
62
35
|
// We need displayName with module name for error reports but it clutters the DevTools.
|
63
|
-
|
36
|
+
const match = displayName.match(/^(.*) \[from (.*)\]$/);
|
64
37
|
if (match) {
|
65
|
-
|
66
|
-
|
38
|
+
const componentName = match[1];
|
39
|
+
const moduleName = match[2];
|
67
40
|
if (componentName && moduleName) {
|
68
41
|
if (moduleName === componentName ||
|
69
|
-
moduleName.startsWith(componentName
|
42
|
+
moduleName.startsWith(`${componentName}.`)) {
|
70
43
|
displayName = componentName;
|
71
44
|
}
|
72
45
|
}
|
@@ -225,13 +198,13 @@ function setupHooks(optionalCallback) {
|
|
225
198
|
/**
|
226
199
|
* Inject custom style text or CSS file or 3rd party style resources
|
227
200
|
*/
|
228
|
-
|
229
|
-
return
|
230
|
-
|
201
|
+
const injectStyles = (options) => {
|
202
|
+
return () => {
|
203
|
+
const el = getContainerEl();
|
231
204
|
return injectStylesBeforeElement(options, document, el);
|
232
205
|
};
|
233
206
|
};
|
234
|
-
|
207
|
+
let mountCleanup;
|
235
208
|
/**
|
236
209
|
* Create an `mount` function. Performs all the non-React-version specific
|
237
210
|
* behavior related to mounting. The React-version-specific code
|
@@ -241,41 +214,40 @@ var mountCleanup;
|
|
241
214
|
* This is designed to be consumed by `npm/react{16,17,18}`, and other React adapters,
|
242
215
|
* or people writing adapters for third-party, custom adapters.
|
243
216
|
*/
|
244
|
-
|
245
|
-
if (options === void 0) { options = {}; }
|
217
|
+
const makeMountFn = (type, jsx, options = {}, rerenderKey, internalMountOptions) => {
|
246
218
|
if (!internalMountOptions) {
|
247
219
|
throw Error('internalMountOptions must be provided with `render` and `reactDom` parameters');
|
248
220
|
}
|
249
221
|
mountCleanup = internalMountOptions.cleanup;
|
250
222
|
// Get the display name property via the component constructor
|
251
223
|
// @ts-ignore FIXME
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
? jsxComponentName
|
224
|
+
const componentName = getDisplayName(jsx.type, options.alias);
|
225
|
+
const displayName = options.alias || componentName;
|
226
|
+
const jsxComponentName = `<${componentName} ... />`;
|
227
|
+
const message = options.alias
|
228
|
+
? `${jsxComponentName} as "${options.alias}"`
|
257
229
|
: jsxComponentName;
|
258
230
|
return cy
|
259
231
|
.then(injectStyles(options))
|
260
|
-
.then(
|
232
|
+
.then(() => {
|
261
233
|
var _a, _b, _c;
|
262
|
-
|
263
|
-
|
234
|
+
const reactDomToUse = internalMountOptions.reactDom;
|
235
|
+
const el = getContainerEl();
|
264
236
|
if (!el) {
|
265
237
|
throw new Error([
|
266
|
-
|
238
|
+
`[@cypress/react] 🔥 Hmm, cannot find root element to mount the component. Searched for ${ROOT_SELECTOR}`,
|
267
239
|
].join(' '));
|
268
240
|
}
|
269
|
-
|
241
|
+
const key = rerenderKey !== null && rerenderKey !== void 0 ? rerenderKey :
|
270
242
|
// @ts-ignore provide unique key to the the wrapped component to make sure we are rerendering between tests
|
271
243
|
(((_c = (_b = (_a = Cypress === null || Cypress === void 0 ? void 0 : Cypress.mocha) === null || _a === void 0 ? void 0 : _a.getRunner()) === null || _b === void 0 ? void 0 : _b.test) === null || _c === void 0 ? void 0 : _c.title) || '') + Math.random();
|
272
|
-
|
273
|
-
key
|
244
|
+
const props = {
|
245
|
+
key,
|
274
246
|
};
|
275
|
-
|
247
|
+
const reactComponent = React.createElement(options.strict ? React.StrictMode : React.Fragment, props, jsx);
|
276
248
|
// since we always surround the component with a fragment
|
277
249
|
// let's get back the original component
|
278
|
-
|
250
|
+
const userComponent = reactComponent.props.children;
|
279
251
|
internalMountOptions.render(reactComponent, el, reactDomToUse);
|
280
252
|
if (options.log !== false) {
|
281
253
|
Cypress.log({
|
@@ -284,7 +256,7 @@ var makeMountFn = function (type, jsx, options, rerenderKey, internalMountOption
|
|
284
256
|
message: [message],
|
285
257
|
// @ts-ignore
|
286
258
|
$el: el.children.item(0),
|
287
|
-
consoleProps:
|
259
|
+
consoleProps: () => {
|
288
260
|
return {
|
289
261
|
// @ts-ignore protect the use of jsx functional components use ReactNode
|
290
262
|
props: jsx.props,
|
@@ -298,10 +270,10 @@ var makeMountFn = function (type, jsx, options, rerenderKey, internalMountOption
|
|
298
270
|
// Separate alias and returned value. Alias returns the component only, and the thenable returns the additional functions
|
299
271
|
cy.wrap(userComponent, { log: false })
|
300
272
|
.as(displayName)
|
301
|
-
.then(
|
273
|
+
.then(() => {
|
302
274
|
return cy.wrap({
|
303
275
|
component: userComponent,
|
304
|
-
rerender:
|
276
|
+
rerender: (newComponent) => makeMountFn('rerender', newComponent, options, key, internalMountOptions),
|
305
277
|
unmount: internalMountOptions.unmount,
|
306
278
|
}, { log: false });
|
307
279
|
})
|
@@ -319,16 +291,16 @@ var makeMountFn = function (type, jsx, options, rerenderKey, internalMountOption
|
|
319
291
|
* This is designed to be consumed by `npm/react{16,17,18}`, and other React adapters,
|
320
292
|
* or people writing adapters for third-party, custom adapters.
|
321
293
|
*/
|
322
|
-
|
323
|
-
return cy.then(
|
294
|
+
const makeUnmountFn = (options) => {
|
295
|
+
return cy.then(() => {
|
324
296
|
var _a;
|
325
|
-
|
297
|
+
const wasUnmounted = mountCleanup === null || mountCleanup === void 0 ? void 0 : mountCleanup();
|
326
298
|
if (wasUnmounted && options.log) {
|
327
299
|
Cypress.log({
|
328
300
|
name: 'unmount',
|
329
301
|
type: 'parent',
|
330
302
|
message: [(_a = options.boundComponentMessage) !== null && _a !== void 0 ? _a : 'Unmounted component'],
|
331
|
-
consoleProps:
|
303
|
+
consoleProps: () => {
|
332
304
|
return {
|
333
305
|
description: 'Unmounts React component',
|
334
306
|
parent: getContainerEl().parentNode,
|
@@ -342,7 +314,7 @@ var makeUnmountFn = function (options) {
|
|
342
314
|
// Cleanup before each run
|
343
315
|
// NOTE: we cannot use unmount here because
|
344
316
|
// we are not in the context of a test
|
345
|
-
|
317
|
+
const preMountCleanup = () => {
|
346
318
|
mountCleanup === null || mountCleanup === void 0 ? void 0 : mountCleanup();
|
347
319
|
};
|
348
320
|
// Side effects from "import { mount } from '@cypress/<my-framework>'" are annoying, we should avoid doing this
|
@@ -576,29 +548,28 @@ createToken('GTE0', '^\\s*>=\\s*0\\.0\\.0\\s*$');
|
|
576
548
|
createToken('GTE0PRE', '^\\s*>=\\s*0\\.0\\.0-0\\s*$');
|
577
549
|
});
|
578
550
|
|
579
|
-
|
580
|
-
|
551
|
+
// @ts-expect-error
|
552
|
+
let root;
|
553
|
+
const cleanup = () => {
|
581
554
|
if (root) {
|
582
555
|
root.unmount();
|
583
556
|
return true;
|
584
557
|
}
|
585
558
|
return false;
|
586
559
|
};
|
587
|
-
function mount(jsx, options, rerenderKey) {
|
588
|
-
|
589
|
-
var internalOptions = {
|
560
|
+
function mount(jsx, options = {}, rerenderKey) {
|
561
|
+
const internalOptions = {
|
590
562
|
reactDom: ReactDOM,
|
591
|
-
render:
|
563
|
+
render: (reactComponent, el) => {
|
592
564
|
root = ReactDOM.createRoot(el);
|
593
565
|
return root.render(reactComponent);
|
594
566
|
},
|
595
|
-
unmount
|
596
|
-
cleanup
|
567
|
+
unmount,
|
568
|
+
cleanup,
|
597
569
|
};
|
598
|
-
return makeMountFn('mount', jsx,
|
570
|
+
return makeMountFn('mount', jsx, Object.assign({ ReactDom: ReactDOM }, options), rerenderKey, internalOptions);
|
599
571
|
}
|
600
|
-
function unmount(options) {
|
601
|
-
if (options === void 0) { options = { log: true }; }
|
572
|
+
function unmount(options = { log: true }) {
|
602
573
|
return makeUnmountFn(options);
|
603
574
|
}
|
604
575
|
|
package/react18/dist/index.d.ts
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
/// <reference types="cypress" />
|
2
|
+
/// <reference types="cypress" />
|
2
3
|
import React from 'react';
|
3
4
|
import type { MountOptions, UnmountArgs } from '@cypress/react';
|
4
5
|
export declare function mount(jsx: React.ReactNode, options?: MountOptions, rerenderKey?: string): Cypress.Chainable<import("@cypress/react").MountReturn>;
|
package/react18/package.json
CHANGED
@@ -4,7 +4,7 @@
|
|
4
4
|
"description": "Test React components using Cypress",
|
5
5
|
"main": "dist/cypress-react.cjs.js",
|
6
6
|
"scripts": {
|
7
|
-
"build": "rimraf dist && rollup -c rollup.config.
|
7
|
+
"build": "rimraf dist && rollup -c rollup.config.mjs",
|
8
8
|
"postbuild": "node ../../scripts/sync-exported-npm-with-cli.js",
|
9
9
|
"build-prod": "yarn build",
|
10
10
|
"watch": "yarn build --watch --watch.exclude ./dist/**/*"
|
@@ -20,7 +20,7 @@
|
|
20
20
|
"react-dom": "^16",
|
21
21
|
"rollup": "^2.38.5",
|
22
22
|
"rollup-plugin-typescript2": "^0.29.0",
|
23
|
-
"typescript": "^4.
|
23
|
+
"typescript": "^4.7.4"
|
24
24
|
},
|
25
25
|
"peerDependencies": {
|
26
26
|
"@types/react": "^18",
|
File without changes
|
package/svelte/README.md
ADDED
@@ -0,0 +1,83 @@
|
|
1
|
+
# @cypress/svelte
|
2
|
+
|
3
|
+
Mount Svelte components in the open source [Cypress.io](https://www.cypress.io/) test runner **v10.7.0+**
|
4
|
+
|
5
|
+
> **Note:** This package is bundled with the `cypress` package and should not need to be installed separately. See the [Svelte Component Testing Docs](https://docs.cypress.io/guides/component-testing/quickstart-svelte#Configuring-Component-Testing) for mounting Svelte components. Installing and importing `mount` from `@cypress/svelte` should only be used for advanced use-cases.
|
6
|
+
|
7
|
+
## Install
|
8
|
+
|
9
|
+
- Requires Svelte >= 3
|
10
|
+
- Requires Cypress v10.7.0 or later
|
11
|
+
- Requires [Node](https://nodejs.org/en/) version 12 or above
|
12
|
+
|
13
|
+
```sh
|
14
|
+
npm install --save-dev @cypress/svelte
|
15
|
+
```
|
16
|
+
|
17
|
+
## Run
|
18
|
+
|
19
|
+
Open cypress test runner
|
20
|
+
```
|
21
|
+
npx cypress open --component
|
22
|
+
```
|
23
|
+
|
24
|
+
If you need to run test in CI
|
25
|
+
```
|
26
|
+
npx cypress run --component
|
27
|
+
```
|
28
|
+
|
29
|
+
For more information, please check the official docs for [running Cypress](https://on.cypress.io/guides/getting-started/opening-the-app#Quick-Configuration) and for [component testing](https://on.cypress.io/guides/component-testing/writing-your-first-component-test).
|
30
|
+
|
31
|
+
## Example
|
32
|
+
|
33
|
+
```js
|
34
|
+
import { mount } from '@cypress/svelte'
|
35
|
+
import HelloWorld from './HelloWorld.svelte'
|
36
|
+
|
37
|
+
describe('HelloWorld component', () => {
|
38
|
+
it('works', () => {
|
39
|
+
mount(HelloWorld)
|
40
|
+
// now use standard Cypress commands
|
41
|
+
cy.contains('Hello World!').should('be.visible')
|
42
|
+
})
|
43
|
+
})
|
44
|
+
```
|
45
|
+
|
46
|
+
## Options
|
47
|
+
|
48
|
+
In most cases, the component already imports its own styles, thus it looks "right" during the test. If you need another CSS, the simplest way is to import it from the spec file:
|
49
|
+
|
50
|
+
```js
|
51
|
+
// src/HelloWorld.svelte
|
52
|
+
import './styles/main.css'
|
53
|
+
import HelloWorld from './HelloWorld.svelte'
|
54
|
+
|
55
|
+
it('looks right', () => {
|
56
|
+
// styles are applied
|
57
|
+
mount(HelloWorld)
|
58
|
+
})
|
59
|
+
```
|
60
|
+
|
61
|
+
> Note: Global styles can be imported in your component support file, allowing the styles to apply to all mounted components.
|
62
|
+
|
63
|
+
## Compatibility
|
64
|
+
|
65
|
+
| @cypress/svelte | cypress |
|
66
|
+
| -------------- | ------- |
|
67
|
+
| >= v1 | >= v10 |
|
68
|
+
|
69
|
+
## Development
|
70
|
+
|
71
|
+
Run `yarn build` to compile and sync packages to the `cypress` cli package.
|
72
|
+
|
73
|
+
Run `yarn cy:open` to open Cypress component testing against real-world examples.
|
74
|
+
|
75
|
+
Run `yarn test` to execute headless Cypress tests.
|
76
|
+
|
77
|
+
## License
|
78
|
+
|
79
|
+
[](https://github.com/cypress-io/cypress/blob/master/LICENSE)
|
80
|
+
|
81
|
+
This project is licensed under the terms of the [MIT license](/LICENSE).
|
82
|
+
|
83
|
+
## [Changelog](./CHANGELOG.md)
|
@@ -0,0 +1,213 @@
|
|
1
|
+
|
2
|
+
/**
|
3
|
+
* @cypress/svelte v0.0.0-development
|
4
|
+
* (c) 2022 Cypress.io
|
5
|
+
* Released under the MIT License
|
6
|
+
*/
|
7
|
+
|
8
|
+
'use strict';
|
9
|
+
|
10
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
11
|
+
|
12
|
+
const ROOT_SELECTOR = '[data-cy-root]';
|
13
|
+
const getContainerEl = () => {
|
14
|
+
const el = document.querySelector(ROOT_SELECTOR);
|
15
|
+
if (el) {
|
16
|
+
return el;
|
17
|
+
}
|
18
|
+
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.`);
|
19
|
+
};
|
20
|
+
/**
|
21
|
+
* Remove any style or extra link elements from the iframe placeholder
|
22
|
+
* left from any previous test
|
23
|
+
*
|
24
|
+
*/
|
25
|
+
function cleanupStyles() {
|
26
|
+
const styles = document.body.querySelectorAll('[data-cy=injected-style-tag]');
|
27
|
+
styles.forEach((styleElement) => {
|
28
|
+
if (styleElement.parentElement) {
|
29
|
+
styleElement.parentElement.removeChild(styleElement);
|
30
|
+
}
|
31
|
+
});
|
32
|
+
const links = document.body.querySelectorAll('[data-cy=injected-stylesheet]');
|
33
|
+
links.forEach((link) => {
|
34
|
+
if (link.parentElement) {
|
35
|
+
link.parentElement.removeChild(link);
|
36
|
+
}
|
37
|
+
});
|
38
|
+
}
|
39
|
+
/**
|
40
|
+
* Insert links to external style resources.
|
41
|
+
*/
|
42
|
+
function insertStylesheets(stylesheets, document, el) {
|
43
|
+
stylesheets.forEach((href) => {
|
44
|
+
const link = document.createElement('link');
|
45
|
+
link.type = 'text/css';
|
46
|
+
link.rel = 'stylesheet';
|
47
|
+
link.href = href;
|
48
|
+
link.dataset.cy = 'injected-stylesheet';
|
49
|
+
document.body.insertBefore(link, el);
|
50
|
+
});
|
51
|
+
}
|
52
|
+
/**
|
53
|
+
* Inserts a single stylesheet element
|
54
|
+
*/
|
55
|
+
function insertStyles(styles, document, el) {
|
56
|
+
styles.forEach((style) => {
|
57
|
+
const styleElement = document.createElement('style');
|
58
|
+
styleElement.dataset.cy = 'injected-style-tag';
|
59
|
+
styleElement.appendChild(document.createTextNode(style));
|
60
|
+
document.body.insertBefore(styleElement, el);
|
61
|
+
});
|
62
|
+
}
|
63
|
+
function insertSingleCssFile(cssFilename, document, el, log) {
|
64
|
+
return cy.readFile(cssFilename, { log }).then((css) => {
|
65
|
+
const style = document.createElement('style');
|
66
|
+
style.appendChild(document.createTextNode(css));
|
67
|
+
document.body.insertBefore(style, el);
|
68
|
+
});
|
69
|
+
}
|
70
|
+
/**
|
71
|
+
* Reads the given CSS file from local file system
|
72
|
+
* and adds the loaded style text as an element.
|
73
|
+
*/
|
74
|
+
function insertLocalCssFiles(cssFilenames, document, el, log) {
|
75
|
+
return Cypress.Promise.mapSeries(cssFilenames, (cssFilename) => {
|
76
|
+
return insertSingleCssFile(cssFilename, document, el, log);
|
77
|
+
});
|
78
|
+
}
|
79
|
+
/**
|
80
|
+
* Injects custom style text or CSS file or 3rd party style resources
|
81
|
+
* into the given document.
|
82
|
+
*/
|
83
|
+
const injectStylesBeforeElement = (options, document, el) => {
|
84
|
+
if (!el)
|
85
|
+
return;
|
86
|
+
// first insert all stylesheets as Link elements
|
87
|
+
let stylesheets = [];
|
88
|
+
if (typeof options.stylesheet === 'string') {
|
89
|
+
stylesheets.push(options.stylesheet);
|
90
|
+
}
|
91
|
+
else if (Array.isArray(options.stylesheet)) {
|
92
|
+
stylesheets = stylesheets.concat(options.stylesheet);
|
93
|
+
}
|
94
|
+
if (typeof options.stylesheets === 'string') {
|
95
|
+
options.stylesheets = [options.stylesheets];
|
96
|
+
}
|
97
|
+
if (options.stylesheets) {
|
98
|
+
stylesheets = stylesheets.concat(options.stylesheets);
|
99
|
+
}
|
100
|
+
insertStylesheets(stylesheets, document, el);
|
101
|
+
// insert any styles as <style>...</style> elements
|
102
|
+
let styles = [];
|
103
|
+
if (typeof options.style === 'string') {
|
104
|
+
styles.push(options.style);
|
105
|
+
}
|
106
|
+
else if (Array.isArray(options.style)) {
|
107
|
+
styles = styles.concat(options.style);
|
108
|
+
}
|
109
|
+
if (typeof options.styles === 'string') {
|
110
|
+
styles.push(options.styles);
|
111
|
+
}
|
112
|
+
else if (Array.isArray(options.styles)) {
|
113
|
+
styles = styles.concat(options.styles);
|
114
|
+
}
|
115
|
+
insertStyles(styles, document, el);
|
116
|
+
// now load any css files by path and add their content
|
117
|
+
// as <style>...</style> elements
|
118
|
+
let cssFiles = [];
|
119
|
+
if (typeof options.cssFile === 'string') {
|
120
|
+
cssFiles.push(options.cssFile);
|
121
|
+
}
|
122
|
+
else if (Array.isArray(options.cssFile)) {
|
123
|
+
cssFiles = cssFiles.concat(options.cssFile);
|
124
|
+
}
|
125
|
+
if (typeof options.cssFiles === 'string') {
|
126
|
+
cssFiles.push(options.cssFiles);
|
127
|
+
}
|
128
|
+
else if (Array.isArray(options.cssFiles)) {
|
129
|
+
cssFiles = cssFiles.concat(options.cssFiles);
|
130
|
+
}
|
131
|
+
return insertLocalCssFiles(cssFiles, document, el, options.log);
|
132
|
+
};
|
133
|
+
function setupHooks(optionalCallback) {
|
134
|
+
// Consumed by the framework "mount" libs. A user might register their own mount in the scaffolded 'commands.js'
|
135
|
+
// file that is imported by e2e and component support files by default. We don't want CT side effects to run when e2e
|
136
|
+
// testing so we early return.
|
137
|
+
// System test to verify CT side effects do not pollute e2e: system-tests/test/e2e_with_mount_import_spec.ts
|
138
|
+
if (Cypress.testingType !== 'component') {
|
139
|
+
return;
|
140
|
+
}
|
141
|
+
// When running component specs, we cannot allow "cy.visit"
|
142
|
+
// because it will wipe out our preparation work, and does not make much sense
|
143
|
+
// thus we overwrite "cy.visit" to throw an error
|
144
|
+
Cypress.Commands.overwrite('visit', () => {
|
145
|
+
throw new Error('cy.visit from a component spec is not allowed');
|
146
|
+
});
|
147
|
+
// @ts-ignore
|
148
|
+
Cypress.on('test:before:run', () => {
|
149
|
+
optionalCallback === null || optionalCallback === void 0 ? void 0 : optionalCallback();
|
150
|
+
cleanupStyles();
|
151
|
+
});
|
152
|
+
}
|
153
|
+
|
154
|
+
const DEFAULT_COMP_NAME = 'unknown';
|
155
|
+
let componentInstance;
|
156
|
+
const cleanup = () => {
|
157
|
+
componentInstance === null || componentInstance === void 0 ? void 0 : componentInstance.$destroy();
|
158
|
+
};
|
159
|
+
// Extract the component name from the object passed to mount
|
160
|
+
const getComponentDisplayName = (Component) => {
|
161
|
+
if (Component.name) {
|
162
|
+
const [_, match] = /Proxy\<(\w+)\>/.exec(Component.name) || [];
|
163
|
+
return match || Component.name;
|
164
|
+
}
|
165
|
+
return DEFAULT_COMP_NAME;
|
166
|
+
};
|
167
|
+
/**
|
168
|
+
* Mounts a Svelte component inside the Cypress browser
|
169
|
+
*
|
170
|
+
* @param {SvelteConstructor<T>} Component Svelte component being mounted
|
171
|
+
* @param {MountReturn<T extends SvelteComponent>} options options to customize the component being mounted
|
172
|
+
* @returns Cypress.Chainable<MountReturn>
|
173
|
+
*
|
174
|
+
* @example
|
175
|
+
* import Counter from './Counter.svelte'
|
176
|
+
* import { mount } from 'cypress/svelte'
|
177
|
+
*
|
178
|
+
* it('should render', () => {
|
179
|
+
* mount(Counter, { props: { count: 42 } })
|
180
|
+
* cy.get('button').contains(42)
|
181
|
+
* })
|
182
|
+
*/
|
183
|
+
function mount(Component, options = {}) {
|
184
|
+
return cy.then(() => {
|
185
|
+
const target = getContainerEl();
|
186
|
+
injectStylesBeforeElement(options, document, target);
|
187
|
+
const ComponentConstructor = (Component.default || Component);
|
188
|
+
componentInstance = new ComponentConstructor(Object.assign({ target }, options));
|
189
|
+
// by waiting, we are delaying test execution for the next tick of event loop
|
190
|
+
// and letting hooks and component lifecycle methods to execute mount
|
191
|
+
return cy.wait(0, { log: false }).then(() => {
|
192
|
+
if (options.log) {
|
193
|
+
const mountMessage = `<${getComponentDisplayName(Component)} ... />`;
|
194
|
+
Cypress.log({
|
195
|
+
name: 'mount',
|
196
|
+
message: [mountMessage],
|
197
|
+
}).snapshot('mounted').end();
|
198
|
+
}
|
199
|
+
})
|
200
|
+
.wrap({ component: componentInstance }, { log: false });
|
201
|
+
});
|
202
|
+
}
|
203
|
+
// Side effects from "import { mount } from '@cypress/<my-framework>'" are annoying, we should avoid doing this
|
204
|
+
// by creating an explicit function/import that the user can register in their 'component.js' support file,
|
205
|
+
// such as:
|
206
|
+
// import 'cypress/<my-framework>/support'
|
207
|
+
// or
|
208
|
+
// import { registerCT } from 'cypress/<my-framework>'
|
209
|
+
// registerCT()
|
210
|
+
// Note: This would be a breaking change
|
211
|
+
setupHooks(cleanup);
|
212
|
+
|
213
|
+
exports.mount = mount;
|