Cypress Component Testing for Stencil
Cypress component testing which leverages Stencil's JSX ⚡
Getting started
-
Install this package by running the following in your Stencil project;
npm install -D cypress-ct-stencil vite@4.5.2
-
If not installed, install cypress:
npm install -D cypress
-
Open Cypress:
-
npx cypress open
-
Select "Component Testing" and follow the onscreen instructions.
-
-
Add
cypress-ct-stencil
to yourcypress.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: ['./'] } }), }, ...
-
Go to your
component.ts
file, usually located incypress/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
-
At the root of your
cypress
folder, add atsconfig.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
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
// 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
// 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
// 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
// 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
// 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:
// 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)
// 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!');
});