diff --git a/npm/vue2/src/index.ts b/npm/vue2/src/index.ts index b021e9459d02..f73be7d74400 100644 --- a/npm/vue2/src/index.ts +++ b/npm/vue2/src/index.ts @@ -67,6 +67,17 @@ const installMixins = (Vue, options) => { } } +const registerGlobalDirectives = (Vue, options) => { + const directives = + Cypress._.get(options, 'extensions.directives') + + if (Cypress._.isPlainObject(directives)) { + Object.keys(directives).forEach((name) => { + Vue.directive(name, directives[name]) + }) + } +} + const hasStore = ({ store }: { store: any }) => Boolean(store && store._vm) const forEachValue = (obj: Record, fn: (value: T, key: string) => void) => { @@ -125,6 +136,10 @@ type VueFilters = { [key: string]: (value: string) => string } +type VueDirectives = { + [key: string]: Function | Object +} + type VueMixin = unknown type VueMixins = VueMixin | VueMixin[] @@ -209,6 +224,27 @@ interface MountOptionsExtensions { * @memberof MountOptionsExtensions */ plugins?: VuePlugins + + /** + * Optional Vue directives to install while mounting the component + * + * @memberof MountOptionsExtensions + * @see https://github.com/cypress-io/cypress/tree/develop/npm/vue#examples + * @example + * const directives = { + * custom: { + * name: 'custom', + * bind (el, binding) { + * el.dataset['custom'] = binding.value + * }, + * unbind (el) { + * el.removeAttribute('data-custom') + * }, + * }, + * } + * mount(Hello, { extensions: { directives }}) + */ + directives?: VueDirectives } /** @@ -382,6 +418,7 @@ export const mount = ( installFilters(localVue, options) installMixins(localVue, options) installPlugins(localVue, options, props) + registerGlobalDirectives(localVue, options) registerGlobalComponents(localVue, options) props.attachTo = componentNode diff --git a/packages/app/cypress/e2e/runner/reporter-ct-mount-hover.cy.ts b/packages/app/cypress/e2e/runner/reporter-ct-mount-hover.cy.ts index 54e2349cba1e..e48055f7e349 100644 --- a/packages/app/cypress/e2e/runner/reporter-ct-mount-hover.cy.ts +++ b/packages/app/cypress/e2e/runner/reporter-ct-mount-hover.cy.ts @@ -4,7 +4,7 @@ type ProjectDirs = typeof fixtureDirs const PROJECTS: {projectName: ProjectDirs[number], test: string}[] = [ { projectName: 'angular-14', test: 'app.component' }, - { projectName: 'vueclivue2-configured', test: 'HelloWorld.cy' }, + // TODO: Flaky. { projectName: 'vueclivue2-configured', test: 'HelloWorld.cy' }, { projectName: 'react-vite-ts-configured', test: 'App.cy' }, { projectName: 'react18', test: 'App.cy' }, { projectName: 'create-react-app-configured', test: 'App.cy' }, diff --git a/system-tests/projects/vueclivue2-configured/README.md b/system-tests/projects/vueclivue2-configured/README.md index bc0d1a3428ae..ddf2b517ed9b 100644 --- a/system-tests/projects/vueclivue2-configured/README.md +++ b/system-tests/projects/vueclivue2-configured/README.md @@ -1,4 +1,4 @@ -# vueclivue2-unconfigured +# vueclivue2-configured ## Project setup ``` diff --git a/system-tests/projects/vueclivue2-configured/src/components/GlobalComponentWithCustomDirective.vue b/system-tests/projects/vueclivue2-configured/src/components/GlobalComponentWithCustomDirective.vue new file mode 100644 index 000000000000..a2f149e295f7 --- /dev/null +++ b/system-tests/projects/vueclivue2-configured/src/components/GlobalComponentWithCustomDirective.vue @@ -0,0 +1,14 @@ + + + diff --git a/system-tests/projects/vueclivue2-configured/src/components/HelloWorld.cy.js b/system-tests/projects/vueclivue2-configured/src/components/HelloWorld.cy.js index 6226c3920657..c6d1d9636427 100644 --- a/system-tests/projects/vueclivue2-configured/src/components/HelloWorld.cy.js +++ b/system-tests/projects/vueclivue2-configured/src/components/HelloWorld.cy.js @@ -1,5 +1,7 @@ import { mount } from 'cypress/vue2' import HelloWorld from './HelloWorld.vue' +import GlobalComponentWithCustomDirective from './GlobalComponentWithCustomDirective.vue' +import custom from '../directive' describe('', () => { it('contains the default slot in its h1', () => { @@ -9,8 +11,43 @@ describe('', () => { propsData: { msg: slotContent, }, + extensions: { + components: { + // stubbing for simplicity, this smoke test does not depend on + // GlobalComponent + GlobalComponentWithCustomDirective: { + render: h => h('div') + } + }, + } }) cy.contains('h1', slotContent) }) + + it('Vue2 custom directive should work ', () => { + mount(GlobalComponentWithCustomDirective, { + extensions: { + directives: { custom }, + }, + }) + + cy.get('.child').should('have.attr', 'data-custom', 'testing123') + }) + + it('Vue2 custom directive should work in nested component', () => { + const slotContent = 'Welcome to testing in Vue CLI' + + mount(HelloWorld, { + propsData: { + msg: slotContent, + }, + extensions: { + components: { GlobalComponentWithCustomDirective }, + directives: { custom }, + }, + }) + + cy.get('.child').should('have.attr', 'data-custom', 'testing123') + }) }) diff --git a/system-tests/projects/vueclivue2-configured/src/components/HelloWorld.vue b/system-tests/projects/vueclivue2-configured/src/components/HelloWorld.vue index f8e7e8a5e9f5..8002b0ce7ded 100644 --- a/system-tests/projects/vueclivue2-configured/src/components/HelloWorld.vue +++ b/system-tests/projects/vueclivue2-configured/src/components/HelloWorld.vue @@ -25,6 +25,7 @@
  • vue-loader
  • awesome-vue
  • + diff --git a/system-tests/projects/vueclivue2-configured/src/directive.js b/system-tests/projects/vueclivue2-configured/src/directive.js new file mode 100644 index 000000000000..fdb22b0d0962 --- /dev/null +++ b/system-tests/projects/vueclivue2-configured/src/directive.js @@ -0,0 +1,9 @@ +export default { + name: 'custom', + bind (el, binding) { + el.dataset['custom'] = binding.value + }, + unbind (el) { + el.removeAttribute('data-custom') + }, +} diff --git a/system-tests/projects/vueclivue2-configured/src/main.js b/system-tests/projects/vueclivue2-configured/src/main.js index 418f3996d1f6..535c6cdce3aa 100644 --- a/system-tests/projects/vueclivue2-configured/src/main.js +++ b/system-tests/projects/vueclivue2-configured/src/main.js @@ -1,8 +1,14 @@ import Vue from 'vue' import App from './App.vue' +import GlobalComponentWithCustomDirective from './components/GlobalComponentWithCustomDirective.vue' +import custom from './directive' Vue.config.productionTip = false +Vue.component('GlobalComponentWithCustomDirective', GlobalComponentWithCustomDirective) +Vue.directive('custom', custom) new Vue({ - render: function (h) { return h(App) }, + render (h) { + return h(App) + }, }).$mount('#app') diff --git a/tooling/v8-snapshot/cache/dev-linux/snapshot-meta.cache.json b/tooling/v8-snapshot/cache/dev-linux/snapshot-meta.cache.json index 739b6ea8c2ba..0b1863a1f6da 100644 --- a/tooling/v8-snapshot/cache/dev-linux/snapshot-meta.cache.json +++ b/tooling/v8-snapshot/cache/dev-linux/snapshot-meta.cache.json @@ -3528,4 +3528,4 @@ ], "deferredHashFile": "yarn.lock", "deferredHash": "ce60d1d6cc0c77f20dcfc741105a5c53593752f814bf3cab816a3024faf68bfc" -} \ No newline at end of file +}