cypress-io / cypress

Fast, easy and reliable testing for anything that runs in a browser.
https://cypress.io
MIT License
46.41k stars 3.14k forks source link

feature request: Component testing for StencilJS #24054

Open Samuel-Schober-USU opened 1 year ago

Samuel-Schober-USU commented 1 year ago

What would you like?

Since more and more companies are using web components and StencilJS is often used for building them, it would be awesome if Cypress could support also StencilJS components in their component testing.

Why is this needed?

Our company is currently using the Puppeteer based approach for testing our web components, but since we are already testing our applications with Cypress, we would love to have it harmonized and use Cypress everywhere.

Maybe you can also have some future cooperation with the Stencil developers so they support Cypress testing as a default instead of Puppeteer.

Other

No response

chris-kt commented 1 year ago

VERY interested in this, as well. Seem like the lift may not be massive but the value would be tremendous for those invested in web component architecture.

lmiller1990 commented 1 year ago

Here's a simple example showing how to make a mount function for Web Components: https://github.com/lmiller1990/cypress-web-components-example

We can no doubt do something similar for Stencil!

pedroresende commented 1 year ago

Any news about this ?

lmiller1990 commented 1 year ago

@pedroresende Sure is, I'm working on a better way to add support for third party libraries to our onboarding workflow. You can follow that here: https://github.com/cypress-io/cypress/issues/25637

The code is almost done, I expect we can ship this before the end of Feb 2023. Then we can work on some Stencil integrations, without needing to make a PR against Cypress. There will be better documentation on how to integrate with Cypress (basically like https://github.com/lmiller1990/cypress-web-components-example, but once someone does it, everyone can re-use the integration).

I haven't used Stencil before, but it should be easy enough to integrate.

lmiller1990 commented 1 year ago

Hi @pedroresende @chris-kt @Samuel-Schober-USU, we have a public API for this feature coming in the next release.

If either of you is interested, you could either make your own one for Stencil now. If you want to work on it together or have any questions, please let me know.

dpunna-ut commented 11 months ago

Hi @pedroresende @chris-kt @Samuel-Schober-USU, we have a public API for this feature coming in the next release.

If either of you is interested, you could either make your own one for Stencil now. If you want to work on it together or have any questions, please let me know.

Hi @lmiller1990 - I'm looking into implement this for stencil js, it is currently not mounting the component using cypress. It would be good if you have any experience here

lmiller1990 commented 11 months ago

Sure, got a repository I can contribute to or take a look at? Happy to help out, I know this still really well, would love to try writing some Stencil components and tests.

dpunna-ut commented 11 months ago

Sure, got a repository I can contribute to or take a look at? Happy to help out, I know this still really well, would love to try writing some Stencil components and tests.

Thats great! I have already started implementing, however for some reason i can't see the mounting component in the html. As shown in the screenshots

(1) `test-file.tsx'

Screenshot 2023-08-03 at 10 27 06

(2) 'test-file.cy.tsx`

Screenshot 2023-08-03 at 10 27 12

(3) Actual test in cypress

Screenshot 2023-08-03 at 10 27 22
lmiller1990 commented 11 months ago

Hi! Can you please post a minimal repository I can clone and run? Screenshots aren't something I'm able to debug.

dpunna-ut commented 11 months ago

Hi! Can you please post a minimal repository I can clone and run? Screenshots aren't something I'm able to debug.

I managed to mount it by using your example function for mount

import { getContainerEl, setupHooks } from "@cypress/mount-utils";

function cleanup() {
  const root = getContainerEl();
  root.innerHTML = '';
}

export function mount(template) {
  const root = getContainerEl();
  root.innerHTML = template;

  // adds output to the command log
  return cy.wait(0, { log: false }).then(() => {
    Cypress.log({
      name: "mount",
      message: "Mounted component",
    });
  });
}

setupHooks(cleanup); 

It works if I do cy.mount(click me, however i'm not sure if I can pass the whole.ts` object to the mount? It looks the mount function for stencilJs cannot import the who file for the sourceCode, is that correct? If yes is there anyway we can do that?

Something like this cy.mount('<Button { ...defaultStatProps } />')

This is the code I used


import {
  defineCustomElements,
} from '../../../../dist/js/tk-components/loader';

import '../../../scss/ut_asset_toolkit.scss';

import { Button} from './button';

const defaultProps: Partial<Button> = {
  variant: 'destructive',
};

defineCustomElements(window);

describe('Mount button component', () => {
  beforeEach(() => {
      cy.mount(<Button { ...defaultProps } />)
    });
});

This is the ERROR:

The following error originated from your test code, not from Cypress.

  > Module parse failed: Unexpected token (10:18)

File was processed with these loaders:

 * ./node_modules/source-map-loader/dist/cjs.js

You may need an additional loader to handle the result of these loaders.

| import { Button} from './button';

|

> const defaultProps: Partial<Button> = {

|   variant: 'destructive',

| };
lmiller1990 commented 11 months ago

^ problem is it does not know how to pass TS, normally we will grab your config (webpack/vite/whatever) but I'm not sure what the most common/default is for Stencil JS development.

Please post a minimal reproduction I can run, I'll help out. How do your normally make a Stencil JS app? Anything is possible and I will do my best to get a production ready Stencil JS adapter working but I can't piece together something based on snippets, I need a repo I can clone and tinker with.

lmiller1990 commented 10 months ago

Taking a look... I'm trying to build something but getting

import { Component, Prop, h } from '@stencil/core';

Gives

 The requested module '/__cypress/src/node_modules/.vite/deps/@stencil_core.js?v=15a1a7dc' does not provide an export named 'Component'

Same issue in Node.js:

> const s =require('@stencil/core')
undefined
> s
{ h: [Function (anonymous)] }

I do not know much about how Stencil works, it looks like they don't use a standard dev-server, so we will need to make it work with Vite / webpack or write an integration for their own thing.

lmiller1990 commented 10 months ago

Same issue if you log Component in the Stencil starter. How does this work? Is there some kind of internal preprocessing? The docs suggest Stencil is a compiler - I guess this export is a compile time thing?

image

lmiller1990 commented 10 months ago

Maybe we can use their dev server: https://stenciljs.com/docs/dev-server-api

lmiller1990 commented 10 months ago

I tried but no luck yet

const { defineConfig } = require('cypress');
const devServer = require('@stencil/core/dev-server');
const compiler = require('@stencil/core/compiler');

module.exports = defineConfig({
  component: {
    devServer: async args => {
      const port = 5553;
      /** @type {import("@stencil/core/internal").StencilDevServerConfig} */
      const config = {
        port,
        root: '__cypress/src',
        logRequests: false
      };

      const logger = console;
      logger.createTimeSpan = () => {
        return {
          duration: 10000,
          finish: () => {
            return 2000
          },
        };
      };

      logger.getLevel = () => 4;
      devServer.start(config);

      return {
        port,
      };
    },
  },
});

How are you supposed to use the Stencil dev server?


Another alternative is to just build the prod bundle and test those. It's not really the fast feedback loop you'd want, but until we figure out how to integrate it, it's the best I can come up with. I got this working: image

import { MyComponent } from '../../dist/components/my-component'

import { getContainerEl, setupHooks } from "@cypress/mount-utils";

function cleanup() {
  // runs after each test
}

function mount(template, options = {}) {
  for (const [tag, comp] of Object.entries(options.components ?? {})) {
    customElements.define(tag, comp);
  }

  // get the root element to mount the component
  const root = getContainerEl();
  root.innerHTML = template;

  // adds output to the command log
  return cy.wait(0, { log: false }).then(() => {
    Cypress.log({
      name: "mount",
      message: "Mounted component",
    });
  });
}

setupHooks(cleanup);

describe('ComponentName.cy.jsx', () => {
  it('playground', () => {
    mount(`<my-component first="Stencil" last="'Don't call me a framework' JS"></my-component>`, {
      components: {
        'my-component': MyComponent
      }
    })
  })
})

To do this I had to do npm run build and import the dist'd output. It's just a web component, so I followed my own guide: https://github.com/lmiller1990/cypress-web-components-example/tree/main

Who is the best person to talk to on the Stencil team about how to use their dev server? Maybe @JessicaSachs can point me in the right direction.

junelau commented 8 months ago

hi @lmiller1990 - working off your example, I can get stencil's dev server started, but it closes within the stencil dev server startup process...

My devServer function in cypress.config.js is

devServer: async () => {
      const port = 3333;
      const config = {
        port,
        root: './'
      };

      const logger = stencilNode.createNodeLogger({process});

      const stencilServer = devServer.start(config, logger);

      return {
        port: port,
        close: {
          done: (await stencilServer).close(),
        },
      };
    },

Cypress' UI flickers to the "select browser" screen for a split second, and then I get a rather cryptic stack trace:

Error at S. (:4410:60024) at Object.onceWrapper (node:events:628:26) at S.emit (node:events:513:28) at S.emit (:4410:49620) at ChildProcess. (:4410:49047) at ChildProcess.emit (node:events:513:28) at emit (node:internal/child_process:937:14) at process.processTicksAndRejections (node:internal/process/task_queues:83:21)

While debugging, it seems to me that the stencil dev-server is shutting down due to the output "Debugger attached" being sent to stderr.

image

In console, I get:

dev server closed: 
lmiller1990 commented 8 months ago

I wonder why the Stencil dev server is dying. It's a bit of a black box - we'll need to find out how/why that isn't working. Do you have much knowledge of it? I had a quick look, but it looks like it isn't webpack or Vite based.

CoderIllusionist commented 4 months ago

@lmiller1990 I've created a package that integrates Stencil with Cypress' Component Testing :) link

lmiller1990 commented 4 months ago

Neat, I will try it out!