cypress-ct-stencil
Version:
Framework Definition for StencilJS with Cypress Component Testing
191 lines (133 loc) • 5.77 kB
Markdown
# Cypress Component Testing for Stencil
> Cypress component testing which leverages Stencil's JSX ⚡
## Getting started
1. Install this package by running the following in your Stencil project;
- `npm install -D cypress-ct-stencil vite@4.5.2`
2. If not installed, install cypress:
- `npm install -D cypress`
3. Open Cypress:
- `npx cypress open`
- Select "Component Testing" and follow the onscreen instructions.
4. Add `cypress-ct-stencil` to your `cypress.config.ts`;
```
import { defineConfig } from 'cypress';
import { defineConfig as defineConfigVite } from 'vite';
...
devServer: {
framework: 'cypress-ct-stencil-js' as any,
bundler: 'vite',
viteConfig: defineConfigVite({
optimizeDeps: {
exclude: ['./']
}
}),
},
...
```
5. Go to your `component.ts` file, usually located in `cypress/support` and add the following:
```
import { mount } from 'cypress-ct-stencil'
import { defineCustomElements } from '../../loader';
Cypress.Commands.add('mount', mount);
defineCustomElements();
// Optional, only add if you have global styling
// import '../../dist/project-name/project-name.css
```
6. At the root of your `cypress` folder, add a `tsconfig.json` and add the following:
```
{
"extends": "../node_modules/cypress-ct-stencil/tsconfig.cypress.json"
}
```
## Options
The two options can be set in the `mount` function itself as second parameter, or you can set a global env variable that acts as a default value.
| Property | envVariable | Default value | Description |
| ---------------- | ------------------------- | ------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| waitUntilVisible | CtStencilWaitUntilVisible | `false` | Recursively waits for all children to be visible (useful for when you have many slotted elements and visual testing). Does not work for elements in the `shadowRoot`. |
| log | CtStencilLog | `false` | Log when a node is about to mount |
#### Usage in test
```tsx
cy.mount(<my-component />, { waitUntilVisible: true, log: true });
```
## Example
Example with Stencil and Cypress is located in the `example` directory of this repo.
To install, navigate to the `example` directory and run:
`npm install`
Then run a Stencil build:
`npm run build`
Finally, open Cypress by running:
`npx cypress open`
## Usage
### Typings
Since we installed Cypress in our Stencil directory, all typings are available in our `.cy.tsx` files:

### Example usages
```tsx
// MyComponent.cy.tsx
import { h } from '@stencil/core';
it('Should mount correctly', () => {
cy.mount(<my-component first="John" last="Doe"></my-component>);
cy.get('my-component').should('exist');
});
```
#### Styles
```tsx
// MyComponent.cy.tsx
import { h } from '@stencil/core';
it('Should support inline styling', () => {
cy.mount(<my-component first="John" last="Doe" style={{ backgroundColor: 'red', width: '300px', height: '300px' }} />);
cy.get('my-component').should('have.css', 'background-color', 'rgb(255, 0, 0)').should('have.css', 'width', '300px').should('have.css', 'height', '300px');
});
```
#### Ref
```tsx
// MyComponent.cy.tsx
import { h } from '@stencil/core';
it('Should support refs', () => {
cy.mount(<my-component ref={(ref) => cy.get(ref as any).as('ref')} first="Test" last="Ref" />);
cy.get('@ref').should('exist');
});
```
#### Events
```tsx
// MyComponent.cy.tsx
import { h } from '@stencil/core';
it('Should capture events', () => {
const spyObj = { foo(e) {} };
cy.spy(spyObj, 'foo').as('customEventSpy');
cy.mount(<my-component onCustomEvent={spyObj.foo} first="Test" last="Event" />);
cy.get('my-component').shadow().find('div').click();
cy.get('@customEventSpy').should('be.calledOnceWith');
});
```
#### Array
```tsx
// MyComponent.cy.tsx
import { h } from '@stencil/core';
it('Should render multiple components via array', () => {
cy.mount([<my-component first="firstFirst" last="firstLast"></my-component>, <my-component first="secondFirst" last="secondLast"></my-component>]);
cy.get('my-component').should('have.length', 2);
cy.get('my-component').first().invoke('prop', 'first').should('eq', 'firstFirst');
cy.get('my-component').first().invoke('prop', 'last').should('eq', 'firstLast');
cy.get('my-component').last().invoke('prop', 'first').should('eq', 'secondFirst');
cy.get('my-component').last().invoke('prop', 'last').should('eq', 'secondLast');
});
```
#### Other elements:
```tsx
// MyComponent.cy.tsx
import { h } from '@stencil/core';
it('Should mount any html element', () => {
cy.mount(<p>This is a paragraph element!</p>);
cy.get('p').should('have.text', 'This is a paragraph element!');
});
```
#### String (not recommended as you lose typings)
```ts
// MyComponent.cy.tsx
import { h } from '@stencil/core';
it('Should mount any html element with a string', () => {
cy.mount(`<p>This is a paragraph element!</p>`);
cy.get('p').should('have.text', 'This is a paragraph element!');
});
```