diff --git a/README.md b/README.md
index 8c4b04e..4b39824 100644
--- a/README.md
+++ b/README.md
@@ -45,13 +45,11 @@ See examples below for details.
See [cypress/integration/options-spec.js](cypress/integration/options-spec.js)
for examples of options.
-* `vue` - path or URL to the Vue library to load. By default, will
-try to load `../node_modules/vue/dist/vue.js`, but you can pass your
-own path or URL.
+* `mountId` - specify root Vue app mount element ID. Defaults to `app`.
```js
const options = {
- vue: 'https://unpkg.com/vue'
+ mountId: 'rootApp' // div#rootApp
}
beforeEach(mountVue(/* my Vue code */, options))
```
@@ -71,12 +69,25 @@ beforeEach(mountVue(/* my Vue code */, options))
place to load additional libraries, polyfills and styles.
```js
-const vue = '../node_modules/vue/dist/vue.js'
+const polyfill = '../node_modules/mypolyfill/dist/polyfill.js'
const options = {
- html: `
`
+ html: `
`
+}
+beforeEach(mountVue(/* my Vue code */, options))
+```
+
+* `vue` **[DEPRECATED]** - path or URL to the Vue library to load. By default, will
+try to load `../node_modules/vue/dist/vue.js`, but you can pass your
+own path or URL.
+
+```js
+const options = {
+ vue: 'https://unpkg.com/vue'
}
beforeEach(mountVue(/* my Vue code */, options))
```
+> #### Deprecation Warning
+> `vue` option has been deprecated. `node_modules/vue/dist/vue` is always used.
### Global Vue extensions
diff --git a/components/.babelrc b/components/.babelrc
new file mode 100644
index 0000000..02e7f24
--- /dev/null
+++ b/components/.babelrc
@@ -0,0 +1,5 @@
+{
+ "plugins": [
+ "transform-object-rest-spread"
+ ]
+}
\ No newline at end of file
diff --git a/components/Counter.vue b/components/Counter.vue
index bc353be..c8b6ae2 100644
--- a/components/Counter.vue
+++ b/components/Counter.vue
@@ -1,24 +1,31 @@
- Clicked: {{ $store.state.count }} times, count is {{ $store.getters.evenOrOdd }}.
+ Clicked: {{ count }} times, count is {{ evenOrOdd }}.
+
-
Increment if odd
Increment async
+
+ Set Count:
+
diff --git a/components/store.js b/components/store.js
index e001983..468106b 100644
--- a/components/store.js
+++ b/components/store.js
@@ -14,6 +14,9 @@ const state = {
// mutations must be synchronous and can be recorded by plugins
// for debugging purposes.
const mutations = {
+ set (state, value) {
+ state.count = value
+ },
increment (state) {
state.count++
},
diff --git a/cypress.json b/cypress.json
index 34d0e84..a3ea8df 100644
--- a/cypress.json
+++ b/cypress.json
@@ -1,6 +1,6 @@
{
"viewportWidth": 300,
- "viewportHeight": 100,
+ "viewportHeight": 120,
"videoRecording": false,
"projectId": "134ej7"
}
diff --git a/cypress/integration/counter-vuex-spec.js b/cypress/integration/counter-vuex-spec.js
index 51228e4..76926c1 100644
--- a/cypress/integration/counter-vuex-spec.js
+++ b/cypress/integration/counter-vuex-spec.js
@@ -7,20 +7,24 @@ import mountVue from '../..'
/* eslint-env mocha */
describe('Vuex Counter', () => {
+
+ // configure component
const extensions = {
plugins: [Vuex],
components: {
- counter: Counter
- },
+ Counter
+ }
}
+
+ // define component template
const template = ' '
- beforeEach(mountVue({template, store}, {extensions}))
- const getCount = () =>
- Cypress.vue.$store.state.count
+ // define count get and set helpers
+ const getCount = () => Cypress.vue.$store.state.count
+ const setCount = value => Cypress.vue.$store.commit('set', value)
- const setCount = value =>
- Cypress.vue.$set(Cypress.vue.$store.state, 'count', value)
+ // initialize a fresh Vue app before each test
+ beforeEach(mountVue({template, store}, {extensions}))
it('starts with zero', () => {
cy.contains('0 times')
@@ -37,15 +41,30 @@ describe('Vuex Counter', () => {
})
it('increments the counter if count is odd', () => {
- setCount(3)
+ setCount(3) // start with an odd number
cy.contains('odd')
- cy.contains('button', 'Increment if odd').click()
+ cy.contains('button', 'Increment if odd').as('btn').click()
+ cy.contains('even')
+ cy.get('@btn').click()
cy.contains('even')
})
it('asynchronously increments counter', () => {
const count = getCount()
+ // increment mutation is delayed by 1 second
+ // Cypress waits 4 seconds by default
cy.contains('button', 'Increment async').click()
cy.contains(`${count + 1} times`)
})
+
+ it('count is zero when input is cleared', () => {
+ cy.get('input').type(`{selectall}{backspace}`)
+ cy.contains('0 times')
+ }),
+
+ it('set count via input field', () => {
+ const count = 42
+ cy.get('input').type(`{selectall}{backspace}${count}`)
+ cy.contains(`${count} times`)
+ })
})
diff --git a/package.json b/package.json
index f0f8ed6..35f703a 100644
--- a/package.json
+++ b/package.json
@@ -71,6 +71,7 @@
"generateNotes": "github-post-release"
},
"devDependencies": {
+ "babel-plugin-transform-object-rest-spread": "6.26.0",
"ban-sensitive-files": "1.9.2",
"css-loader": "0.28.7",
"cypress": "1.4.1",
@@ -86,7 +87,6 @@
"semantic-action": "1.1.0",
"simple-commit-message": "3.3.2",
"standard": "10.0.3",
- "vue": "2.5.13",
"vue-loader": "13.6.1",
"vue-template-compiler": "2.5.13",
"vuex": "3.0.1"
@@ -100,6 +100,7 @@
},
"dependencies": {
"@cypress/webpack-preprocessor": "1.1.2",
- "common-tags": "1.6.0"
+ "common-tags": "1.6.0",
+ "vue": "2.5.13"
}
}
diff --git a/src/index.js b/src/index.js
index 1483a37..93bc350 100644
--- a/src/index.js
+++ b/src/index.js
@@ -1,5 +1,17 @@
+const Vue = require('vue/dist/vue')
const { stripIndent } = require('common-tags')
+// mountVue options
+const defaultOptions = ['html', 'vue', 'base', 'mountId', 'extensions']
+
+// default mount point element ID for root Vue instance
+const defaultMountId = 'app'
+
+const parentDocument = window.parent.document
+const projectName = Cypress.config('projectName')
+const appIframeId = `Your App: '${projectName}'`
+const appIframe = parentDocument.getElementById(appIframeId)
+
// having weak reference to styles prevents garbage collection
// and "losing" styles when the next test starts
const stylesCache = new Map()
@@ -23,10 +35,6 @@ const copyStyles = component => {
return
}
- const parentDocument = window.parent.document
- const projectName = Cypress.config('projectName')
- const appIframeId = `Your App: '${projectName}'`
- const appIframe = parentDocument.getElementById(appIframeId)
const head = appIframe.contentDocument.querySelector('head')
styles.forEach(style => {
head.appendChild(style)
@@ -42,59 +50,20 @@ const deleteCachedConstructors = component => {
Cypress._.values(component.components).forEach(deleteConstructor)
}
-const getVuePath = options =>
- options.vue || options.vuePath || '../node_modules/vue/dist/vue.js'
-
-const getVuexPath = options => options.vuex || options.vuexPath
-
const getPageHTML = options => {
- if (options.html) {
- return options.html
- }
- const vue = getVuePath(options)
- let vuex = getVuexPath(options)
- if (vuex) {
- vuex = ``
- }
-
- // note: add "base" tag to force loading static assets
- // from the server, not from the "spec" file URL
- if (options.base) {
- if (vue.startsWith('.')) {
- console.error(
- 'You are using base tag %s and relative Vue path %s',
- options.base,
- vue
- )
- console.error('the relative path might NOT work')
- console.error(
- 'maybe pass Vue url using "https://unpkg.com/vue/dist/vue.js"'
- )
- }
- return stripIndent`
-
-
-
-
-
-
-
-
-
- `
- }
-
- const vueHtml = stripIndent`
+ return (
+ options.html ||
+ stripIndent`
-
+
+ ${options.base ? ` ` : ''}
+
-
-
- ${vuex || ''}
+
`
- return vueHtml
+ )
}
const registerGlobalComponents = (Vue, options) => {
@@ -128,8 +97,7 @@ const installMixins = (Vue, options) => {
}
}
-const isOptionName = name =>
- ['vue', 'html', 'vuePath', 'base', 'extensions'].includes(name)
+const isOptionName = name => defaultOptions.includes(name)
const isOptions = object => Object.keys(object).every(isOptionName)
@@ -137,9 +105,8 @@ const isConstructor = object => object && object._compiled
const hasStore = ({ store }) => store && store._vm
-const forEachValue = (obj, fn) => {
+const forEachValue = (obj, fn) =>
Object.keys(obj).forEach(key => fn(obj[key], key))
-}
const resetStoreVM = (Vue, { store }) => {
// bind store public getters
@@ -177,38 +144,51 @@ const mountVue = (component, optionsOrProps = {}) => () => {
props = optionsOrProps
}
- const vueHtml = getPageHTML(options)
- const document = cy.state('document')
- document.write(vueHtml)
- document.close()
-
- // TODO: do not log out "its(Vue)" command
- // but it currently does not support it
- return cy
- .window({ log: false })
- .its('Vue')
- .then(Vue => {
- // refresh inner Vue instance of Vuex store
- if (hasStore(component)) {
- component.store = resetStoreVM(Vue, component)
- }
- installMixins(Vue, options)
- installPlugins(Vue, options)
- registerGlobalComponents(Vue, options)
- deleteCachedConstructors(component)
-
- if (isConstructor(component)) {
- const Cmp = Vue.extend(component)
- Cypress.vue = new Cmp(props).$mount('#app')
- copyStyles(Cmp)
- } else {
- const allOptions = Object.assign({}, component, { el: '#app' })
- Cypress.vue = new Vue(allOptions)
- copyStyles(component)
- }
-
- return Cypress.vue
- })
+ // display deprecation warnings
+ if (options.vue) {
+ console.warn(stripIndent`
+ [DEPRECATION]: 'vue' option has been deprecated.
+ 'node_modules/vue/dis/vue' is always used.
+ Please remove it from your 'mountVue' options.`)
+ }
+
+ // insert base app template
+ const doc = appIframe.contentDocument
+ doc.write(getPageHTML(options))
+ doc.close()
+
+ // get root Vue mount element
+ const mountId = options.mountId || defaultMountId
+ const el = doc.getElementById(mountId)
+
+ // set global Vue instance:
+ // 1. convenience for debugging in DevTools
+ // 2. some libraries might check for this global
+ appIframe.contentWindow.Vue = Vue
+
+ // refresh inner Vue instance of Vuex store
+ if (hasStore(component)) {
+ component.store = resetStoreVM(Vue, component)
+ }
+
+ // setup Vue instance
+ installMixins(Vue, options)
+ installPlugins(Vue, options)
+ registerGlobalComponents(Vue, options)
+ deleteCachedConstructors(component)
+
+ // create root Vue component
+ // and make it accessible via Cypress.vue
+ if (isConstructor(component)) {
+ const Cmp = Vue.extend(component)
+ Cypress.vue = new Cmp(props).$mount(el)
+ copyStyles(Cmp)
+ } else {
+ Cypress.vue = new Vue(component).$mount(el)
+ copyStyles(component)
+ }
+
+ return cy.wrap(Cypress.vue)
}
module.exports = mountVue