Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Component testing doesn't work with Vue 3.4's defineModel #29522

Open
OliJeffery opened this issue May 15, 2024 · 5 comments
Open

Component testing doesn't work with Vue 3.4's defineModel #29522

OliJeffery opened this issue May 15, 2024 · 5 comments
Labels
CT Issue related to component testing npm: @cypress/vue @cypress/vue package issues

Comments

@OliJeffery
Copy link

OliJeffery commented May 15, 2024

Current behavior

defineModel is stable as of Vue 3.4 so we recently refactored a lot of our code to take advantage of it. However, this has broken our component testing. Previously, we were able to pass modelValue as a prop when mounting a component in a component test, like so:

cy.mount(BasicEditor, {
      props: {
        modelValue: {
          ...rest_of_the_obect
        },
      },
    });

and the test would mount the component with the stub data we gave it.

Since moving to defineModel (specifically for this case const rule = defineModel<Object>({ required: true });), it first gives us the error defineModel is not defined. We can get around this by importing defineModel from Vue in the component, though we shouldn't have to as like defineEmits and defineProps, defineModel is automatically available inside <script setup>.

Adding that import in gets us around the initial error, but then we get an error that rule is undefined.

The component itself isn't broken and works in both production and Cypress e2e testing. I've tried upgrading to the latest version of Cypress (13.90 at the time of writing) but get the same results.

Desired behavior

There should be a way of mounting a component that makes use of defineModel and allows you to pass stub data to it. You should also not have to import defineModel.

Test code to reproduce

Test:

import BasicEditor from "@/components/dashboard/config/rules/BasicEditor.vue";
import { createTestingPinia } from "@pinia/testing";
import { setActivePinia } from "pinia";
import { useNetworkStore } from "@/stores/network";
describe("BasicEditor.vue", () => {
  beforeEach(() => {
    const pinia = createTestingPinia({
      createSpy: () => {
        cy.spy();
      },
    });
    setActivePinia(pinia);
  });

  it('does not render any fmc-type="loading" elements', () => {
    const network = useNetworkStore();
    network.getConfigData = () => {
      return {};
    };
    cy.mount(BasicEditor, {
      props: {
        modelValue: {
          editorConfig: {
            alertIdFields: [],
            conditionsArray: [],
            customFields: [],
            fromTable: null,
            groupFields: [],
            postGroupConditions: [],
            selectedFields: [],
          },
          enabled: false,
          modified: "2024-04-12T13:47:37.465",
          pythonCode: null,
          ruleName: null,
          sqlQuery: null,
          tab: 0,
          active: false,
        },
      },
    });
    cy.get('[fmc-type="loading"]').should("not.exist");
  });
});

Partial component script:

<script setup lang="ts">

const rule = defineModel<Object>({ required: true });

const props = defineProps({
  initialQueryName: String,
  extended: [String, Boolean],
  editAccess: Boolean,
});

console.log(rule.value)

</script>

Cypress Version

13.9.0

Node version

20.11.1

Operating System

macOs 11.6

Debug Logs

at rule.value.editorConfig.fromTable.tag (http://localhost:8080/__cypress/src/src/components/dashboard/config/rules/BasicEditor.vue?t=1715773860619:290:18)
    at callWithErrorHandling (http://localhost:8080/__cypress/src/node_modules/.vite/deps/chunk-AIL5YJOT.js?v=32d27e9b:1858:33)
    at ReactiveEffect.getter [as fn] (http://localhost:8080/__cypress/src/node_modules/.vite/deps/chunk-AIL5YJOT.js?v=32d27e9b:3547:22)
    at ReactiveEffect.run (http://localhost:8080/__cypress/src/node_modules/.vite/deps/chunk-AIL5YJOT.js?v=32d27e9b:396:19)
    at doWatch (http://localhost:8080/__cypress/src/node_modules/.vite/deps/chunk-AIL5YJOT.js?v=32d27e9b:3646:26)
    at watch (http://localhost:8080/__cypress/src/node_modules/.vite/deps/chunk-AIL5YJOT.js?v=32d27e9b:3471:10)
    at setup (http://localhost:8080/__cypress/src/src/components/dashboard/config/rules/BasicEditor.vue?t=1715773860619:289:5)
    at callWithErrorHandling (http://localhost:8080/__cypress/src/node_modules/.vite/deps/chunk-AIL5YJOT.js?v=32d27e9b:1858:19)
    at setupStatefulComponent (http://localhost:8080/__cypress/src/node_modules/.vite/deps/chunk-AIL5YJOT.js?v=32d27e9b:9258:25)
    at setupComponent (http://localhost:8080/__cypress/src/node_modules/.vite/deps/chunk-AIL5YJOT.js?v=32d27e9b:9219:36)
From previous event:
    at Promise.longStackTracesCaptureStackTrace [as _captureStackTrace] (http://localhost:8080/__cypress/runner/cypress_runner.js:3486:19)
    at Promise._then (http://localhost:8080/__cypress/runner/cypress_runner.js:1239:17)
    at Promise._passThrough (http://localhost:8080/__cypress/runner/cypress_runner.js:4110:17)
    at Promise.lastly.Promise.finally (http://localhost:8080/__cypress/runner/cypress_runner.js:4119:17)
    at Object.onRunnableRun (http://localhost:8080/__cypress/runner/cypress_runner.js:163586:53)
    at $Cypress.action (http://localhost:8080/__cypress/runner/cypress_runner.js:41042:28)
    at Runnable.run (http://localhost:8080/__cypress/runner/cypress_runner.js:146174:13)
    at next (http://localhost:8080/__cypress/runner/cypress_runner.js:155959:10)
    at <unknown> (http://localhost:8080/__cypress/runner/cypress_runner.js:156003:5)
    at timeslice (http://localhost:8080/__cypress/runner/cypress_runner.js:146514:27)

Other

No response

@jennifer-shehane jennifer-shehane added CT Issue related to component testing npm: @cypress/vue @cypress/vue package issues labels May 15, 2024
@thevladisss
Copy link

What bundler tool are you using? From my experience I can say that you might want to configure your bundler to opt-in this feature.

Would be also nice to see your cypress dev server configuration

@OliJeffery
Copy link
Author

OliJeffery commented May 17, 2024

What bundler tool are you using? From my experience I can say that you might want to configure your bundler to opt-in this feature.

I'm using Vite 5 (which is needed to use Vue 3.4). Also using yarn rather than npm.

Would be also nice to see your cypress dev server configuration

import { defineConfig } from "cypress";

export default defineConfig({
  env: {
    redacted
  },
  viewportHeight: 1280,
  viewportWidth: 1920,
  chromeWebSecurity: false,
  defaultCommandTimeout: 30000, // For timing out on cy.get etc.
  responseTimeout: 180000, // For loading a page.
  video: false,
  videoCompression: false,
  scrollBehavior: false,
  screenshotOnRunFailure: true,
  watchForFileChanges: false,
  fixturesFolder: "tests/cypress/support/modules/fixtures",
  e2e: {
    setupNodeEvents(on, config) {
      on("before:browser:launch", (browser = {}, launchOptions) => {
        if (browser.name === "edge" && browser.isHeadless) {
          const width = 2000;
          const height = 2000;
          console.info(
            `Setting the browser window size to ${width} x ${height} because of Edge bug.`
          );
          launchOptions.args.push(`--window-size=${width},${height}`);
        }
        return launchOptions;
      });
      return Object.assign({}, config, {
        screenshotsFolder: "tests/cypress/screenshots/e2e",
        videosFolder: "tests/cypress/videos/e2e",
        supportFile: "tests/cypress/support/index.js",
      });
    },
    baseUrl: "redacted",
    specPattern: [
      "tests/cypress/e2e/users/*.cy.{js,jsx,ts,tsx}",
      "tests/cypress/e2e/config/playgrounds/datasourcesPlayground.cy.{js,jsx,ts,tsx}",
      "tests/cypress/e2e/config/playgrounds/joinsPlayground.cy.{js,jsx,ts,tsx}",
      "tests/cypress/e2e/config/playgrounds/rulesPlayground.cy.{js,jsx,ts,tsx}",
      "tests/cypress/e2e/data/accountLedger/*.cy.{js,jsx,ts,tsx}",
      "tests/cypress/e2e/data/alerts/*.cy.{js,jsx,ts,tsx}",
      "tests/cypress/e2e/data/contracts/*.cy.{js,jsx,ts,tsx}",
      "tests/cypress/e2e/data/products/*.cy.{js,jsx,ts,tsx}",
      "tests/cypress/e2e/config/datasources/*.cy.{js,jsx,ts,tsx}",
      "tests/cypress/e2e/config/joins/*.cy.{js,jsx,ts,tsx}",
      "tests/cypress/e2e/config/rules/*.cy.{js,jsx,ts,tsx}",
      "tests/cypress/e2e/general/*.cy.{js,jsx,ts,tsx}",
      "tests/cypress/e2e/reporting/*.cy.{js,jsx,ts,tsx}",
      "tests/cypress/e2e/aws/*.cy.{js,jsx,ts,tsx}",
    ],
    supportFile: "tests/cypress/support/index.js",
    experimentalRunAllSpecs: true,
  },

  component: {
    indexHtmlFile: "tests/cypress/support/component-index.html",
    supportFile: "tests/cypress/support/component.js",
    devServer: {
      framework: "vue",
      bundler: "vite",
    },
    specPattern: "tests/cypress/component/**/*.cy.{js,jsx,ts,tsx}",
    screenshotsFolder: "tests/cypress/screenshots/component",
    videosFolder: "tests/cypress/videos/component",
  },
});

@thevladisss
Copy link

What bundler tool are you using? From my experience I can say that you might want to configure your bundler to opt-in this feature.

I'm using Vite 5 (which is needed to use Vue 3.4). Also using yarn rather than npm.

Would be also nice to see your cypress dev server configuration

import { defineConfig } from "cypress";

export default defineConfig({
  env: {
    redacted
  },
  viewportHeight: 1280,
  viewportWidth: 1920,
  chromeWebSecurity: false,
  defaultCommandTimeout: 30000, // For timing out on cy.get etc.
  responseTimeout: 180000, // For loading a page.
  video: false,
  videoCompression: false,
  scrollBehavior: false,
  screenshotOnRunFailure: true,
  watchForFileChanges: false,
  fixturesFolder: "tests/cypress/support/modules/fixtures",
  e2e: {
    setupNodeEvents(on, config) {
      on("before:browser:launch", (browser = {}, launchOptions) => {
        if (browser.name === "edge" && browser.isHeadless) {
          const width = 2000;
          const height = 2000;
          console.info(
            `Setting the browser window size to ${width} x ${height} because of Edge bug.`
          );
          launchOptions.args.push(`--window-size=${width},${height}`);
        }
        return launchOptions;
      });
      return Object.assign({}, config, {
        screenshotsFolder: "tests/cypress/screenshots/e2e",
        videosFolder: "tests/cypress/videos/e2e",
        supportFile: "tests/cypress/support/index.js",
      });
    },
    baseUrl: "redacted",
    specPattern: [
      "tests/cypress/e2e/users/*.cy.{js,jsx,ts,tsx}",
      "tests/cypress/e2e/config/playgrounds/datasourcesPlayground.cy.{js,jsx,ts,tsx}",
      "tests/cypress/e2e/config/playgrounds/joinsPlayground.cy.{js,jsx,ts,tsx}",
      "tests/cypress/e2e/config/playgrounds/rulesPlayground.cy.{js,jsx,ts,tsx}",
      "tests/cypress/e2e/data/accountLedger/*.cy.{js,jsx,ts,tsx}",
      "tests/cypress/e2e/data/alerts/*.cy.{js,jsx,ts,tsx}",
      "tests/cypress/e2e/data/contracts/*.cy.{js,jsx,ts,tsx}",
      "tests/cypress/e2e/data/products/*.cy.{js,jsx,ts,tsx}",
      "tests/cypress/e2e/config/datasources/*.cy.{js,jsx,ts,tsx}",
      "tests/cypress/e2e/config/joins/*.cy.{js,jsx,ts,tsx}",
      "tests/cypress/e2e/config/rules/*.cy.{js,jsx,ts,tsx}",
      "tests/cypress/e2e/general/*.cy.{js,jsx,ts,tsx}",
      "tests/cypress/e2e/reporting/*.cy.{js,jsx,ts,tsx}",
      "tests/cypress/e2e/aws/*.cy.{js,jsx,ts,tsx}",
    ],
    supportFile: "tests/cypress/support/index.js",
    experimentalRunAllSpecs: true,
  },

  component: {
    indexHtmlFile: "tests/cypress/support/component-index.html",
    supportFile: "tests/cypress/support/component.js",
    devServer: {
      framework: "vue",
      bundler: "vite",
    },
    specPattern: "tests/cypress/component/**/*.cy.{js,jsx,ts,tsx}",
    screenshotsFolder: "tests/cypress/screenshots/component",
    videosFolder: "tests/cypress/videos/component",
  },
});

It appears like you your config is inferred from the one you use for your app bundling.
So your configuration should be basically there.

export default defineConfig({
  plugins: [
    vue({
      script: {
        defineModel: true
      }
    })
  ]
});

Try to follow the code snippet above. This might come in handy as well:
https://github.com/vitejs/vite-plugin-vue/tree/main/packages/plugin-vue

@OliJeffery
Copy link
Author

That works, thanks!

@OliJeffery
Copy link
Author

(Though should probably be available by default in a future release as it's a stable feature in Vue now)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
CT Issue related to component testing npm: @cypress/vue @cypress/vue package issues
Projects
None yet
Development

No branches or pull requests

3 participants