I am trying to create a component with Vue.js. My component is currently defined like this:
MyComponent.vue
<template id="my-template">
<div>
<button class="btn" v-on:click="increment">increment</button>
</div>
</template>
<script type="text/javascript">
Vue.component('incrementer', {
template: '#my-template',
props: {
i: {
type: Number,
default: 1,
}
},
data: function() {
return {
count: 0
}
},
methods: {
increment: function() {
this.count = this.count + this.i;
}
}
});
</script>
I am trying to create some automated tests for this component. In an attempt to do this, I have the following:
my-component.spec.js
const MyComponent = require('../src/MyComponent.vue');
describe('my-component', function() {
// Inspect the raw component options
it('has a created hook', () => {
expect(typeof MyComponent .created).toBe('function')
});
});
I am trying to run this test via Jasmine through Gulp. In Gulp, my test task looks like this:
gulpfile.js
gulp.task('test', ['build'], function() {
return gulp.src(['test/**/*spec.js'])
.pipe(jasmine({
timeout: 10000,
includeStackTrace: false,
color: false
}))
;
});
When this task gets executed, I receive the following error:
(function (exports, require, module, __filename, __dirname) { <template id="my-template">
^
SyntaxError: Unexpected token <
I don't understand why I'm receiving this error. What do I need to do to test a component in Vue.js via Jasmine?
Thank you!
According to Vue Docs:
In terms of code structure for testing, you don’t have to do anything special in your components to make them testable. Just export the raw options
When you test that component, all you have to do is import the object along with Vue to make many common assertions
So in you MyComponent.vue file:
<template>
<div>
<button class="btn" v-on:click="increment">increment</button>
</div>
</template>
<script type="text/javascript">
export default {
props: {
i: {
type: Number,
default: 1,
}
},
data: function() {
return {
count: 0
}
},
methods: {
increment: function() {
this.count = this.count + this.i;
}
}
}
</script>
Then in your my-component.spec.js:
const Vue = reuqire("vue")
const MyComponent = require('../src/MyComponent.vue');
describe('my-component', function() {
// Inspect the raw component options
it('has a created hook', () => {
expect(typeof MyComponent.created).toBe('function')
});
});
Related
I try to use Mixins with Vue.js. But I encounter several issues with them :/
This is my current code for my two test modules :
ErrorBaseMixin.vue
<script>
import ErrorAlert from './ErrorAlert';
export const ErrorBaseMixin = {
data() {
return {
// Errors management
error_display: true,
error_data: {
level: "warning",
time: 0,
status: 200,
message: ""
}
}
},
methods: {
// ------------------------------------------------------------------------
// Errors management functions
// ------------------------------------------------------------------------
error_function_show_error: function() {
try {
this.$refs.error_component.launch();
}
catch {}
},
callback_error_catched: function(e) {
if(e.message === 'Network Error'){
this.error_data.message = "<strong>There was a network error :</strong> The connection is broken or the server is not started.";
this.error_data.level = "danger";
}
else {
this.error_data.message = "An error occured : " + e.message;
this.error_data.level = "warning";
}
this.error_function_show_error();
},
},
components: {
ErrorAlert
}
}
export default ErrorBaseMixin;
</script>
Test.vue
<template>
<ErrorAlert
:error_display="error_display"
:error="error_data"
ref="error_component"
/>
</div>
</template>
<script lang="js">
import {ErrorBaseMixin} from '../../../parts/ErrorBaseMixin.vue';
export default {
mixins: [ErrorBaseMixin],
name: 'Test_elt',
created() {
this.REST_ADDR = "test/test";
},
data() {
return {
field: {
id: '55',
name: 'test'
}
}
},
methods: {
}
}
</script>
But when I compile the last module, I have the following errors in my browser console :
[Vue warn]: Property or method "error_data" is not defined on the
instance but referenced during render. Make sure that this property is
reactive, either in the data option or for class-based components, by
initializing the property.
[Vue warn]: Unknown custom element: - did you register
the component correctly? For recursive components, make sure to
provide the "name" option.
But... Everything is working fine. So I don't understand why I have these errors
You must change ErrorBaseMixin.vue to ErrorBaseMixin.js:
import ErrorAlert from './ErrorAlert';
const ErrorBaseMixin = {
data() {
return {
// Errors management
error_display: true,
error_data: {
level: "warning",
time: 0,
status: 200,
message: ""
}
}
},
methods: {
// ------------------------------------------------------------------------
// Errors management functions
// ------------------------------------------------------------------------
error_function_show_error: function() {
try {
this.$refs.error_component.launch();
}
catch {}
},
callback_error_catched: function(e) {
if(e.message === 'Network Error'){
this.error_data.message = "<strong>There was a network error :</strong> The connection is broken or the server is not started.";
this.error_data.level = "danger";
}
else {
this.error_data.message = "An error occured : " + e.message;
this.error_data.level = "warning";
}
this.error_function_show_error();
},
},
components: {
ErrorAlert
}
}
export default ErrorBaseMixin;
And then import in your component:
import {ErrorBaseMixin} from '../../../parts/ErrorBaseMixin.js';
export default {
mixins: [ErrorBaseMixin],
...
Note: Take care how import and export, I have changed the way.
Here is some problem with methods,
my component can't access to methods. May i need to pass methods like prop to component ?
here is my html:
<guests v-bind="guests"></guests>
here is component in my js file
var guestsComponent = Vue.component("guests", {
props: ['adultCount', 'childCount'],
template: `
<div class="guests-total">
<div>
<a #click="decrementAdult"></a>
<a #click="incrementAdult"></a>
<input type="text" placeholder="adults"/> {{adultCount}}
</div>
</div>
`
});
and here in the same js file my vue init and methods
var app = new Vue({
el: "#search",
components: {
"guests": guestsComponent
},
data() {
return {
guests: {
adultCount: 0,
childCount: 0
}
};
},
methods: {
decrementAdult() {
this.guests.adultCount++
},
incrementAdult() {
this.guests.adultCount--
}
}
});
I can access to data without problem when i use the props but i don't know how i can pass methods like props or this is needed?
here is error on console:
ReferenceError: decrementAdult is not defined
at o.eval (eval at xa (vue.min.js:NaN), <anonymous>:3:88)
at o.fn._render (vue.min.js?f6b5:6)
at o.eval (vue.min.js?f6b5:6)
at St.get (vue.min.js?f6b5:6)
at new St (vue.min.js?f6b5:6)
at o.hn.$mount (vue.min.js?f6b5:6)
at o.hn.$mount (vue.min.js?f6b5:6)
at init (vue.min.js?f6b5:6)
at eval (vue.min.js?f6b5:6)
at b (vue.min.js?f6b5:6)
Since the click events are done in the child component guests you should emit an event to the parent component and handle it there like :
....
<a #click="$emit('decrement-adult')"></a>
...
in the parent component do :
<guests v-bind="guests" #decrement-adult="decrementAdult"></guests>
I ran into a similar 'method1' is not defined error from the following code section:
methods: {
method1(){
console.log("running method1()");
},
method2(){
method1();
},
...
The problem was that the reference to method1() should have included the this keyword like so:
export default {
name: 'TuringMachine',
props: {
msg: String,
},
data() {
},
methods: {
method1(){
console.log("running method1()");
},
method2(){
this.method1();
}
}
}
Hope this helps anyone with the same issue.
I am in the process of integrating AmazonPay into a React SPA. The classic integration relies on script tags and callbacks (docs).
Here is one example from the button widget:
<head>
<script type='text/javascript'>
window.onAmazonLoginReady = function() {
amazon.Login.setClientId('CLIENT-ID');
};
window.onAmazonPaymentsReady = function() {
showButton();
};
</script>
<script async="async" src='https://static-na.payments-amazon.com/OffAmazonPayments/us/sandbox/js/Widgets.js'>
</script>
</head>
<body>
. . .
<div id="AmazonPayButton">
</div>
...
<script type="text/javascript">
function showButton(){
var authRequest;
OffAmazonPayments.Button("AmazonPayButton", "SELLER-ID", {
type: "TYPE",
color: "COLOR",
size: "SIZE",
authorization: function() {
loginOptions = {scope: "SCOPES",
popup: "POPUP-PARAMETER"};
authRequest = amazon.Login.authorize (loginOptions,
"REDIRECT-URL");
},
onError: function(error) {
// your error handling code.
// alert("The following error occurred: "
// + error.getErrorCode()
// + ' - ' + error.getErrorMessage());
}
});
};
</script>
. . .
<script type="text/javascript">
document.getElementById('Logout').onclick = function() {
amazon.Login.logout();
};
</script>
</body>
When using React, the div with id="AmazonPayButton" isn't on the page until React mounts the div, causing the window.showButton() function to fail.
To circumvent this issue, I've wrapped the function showButton() definition inside window.showButton():
window.onAmazonPaymentsReady = function() {
window.showButton = function () {
var authRequest;
// eslint-disable-next-line no-undef
OffAmazonPayments.Button("AmazonPayButton", "%REACT_APP_AMAZON_SELLER_ID_SANDBOX%", {
type: "PwA",
color: "Gold",
size: "medium",
authorization: function () {
loginOptions = {
scope: "profile postal_code payments:widget payments:shipping_address",
popup: true
};
authRequest = amazon.Login.authorize(loginOptions, "%PUBLIC_URL%/pay-with-amazon");
},
onError: function (error) {
console.log(error.toString())
}
});
};
};
The component which contains the AmazonPay div can now be called on componentDidMount:
import React, {Component} from 'react'
class AmazonMethod extends Component {
componentDidMount () {
window.showButton()
}
render() { return <div id="AmazonPayButton"></div>}
}
export default AmazonMethod
I am confused how to access the onError callback from inside my React component. How do I listen for the callback and respond appropriately?
This question applies to AddressWidget and WalletWidget as well; they all rely on script tag callbacks.
Update:
I've written a post which summarizes how to integrate AmazonPay with client side React.
Why don't you just pass in a function to your showButton function in componentDidMount that onError can call?
Something like this:
window.onAmazonPaymentsReady = function() {
window.showButton = function (errorFunc) {
var authRequest;
// eslint-disable-next-line no-undef
OffAmazonPayments.Button("AmazonPayButton", "%REACT_APP_AMAZON_SELLER_ID_SANDBOX%", {
type: "PwA",
color: "Gold",
size: "medium",
authorization: function () {
loginOptions = {
scope: "profile postal_code payments:widget payments:shipping_address",
popup: true
};
authRequest = amazon.Login.authorize(loginOptions, "%PUBLIC_URL%/pay-with-amazon");
},
onError: function (error) {
console.log(error.toString())
errorFunc(error)
}
});
};
};
import React, {Component} from 'react'
class AmazonMethod extends Component {
componentDidMount () {
window.showButton(this.errorFunc)
}
errorFunc = (error) => {
console.log(error);
this.setState({
amazonError: error
});
}
render() { return <div id="AmazonPayButton"></div>}
}
export default AmazonMethod
I have two demos for same library under repo, with demo.
The main difference is that, one is for browser use and the other is for node use.
However browser one will have error
index.js:1 Uncaught SyntaxError: Identifier 'HeaderComp' has already been declared
What is the main cause?
Update:
Please keep in mind I do not declare a variable twice! I also tried to add console log at the top to ensure the script is executed once!
var HeaderComp = {
name: 'HeaderComp',
template: `
Back
{{ r.title }}
`,
mixins: [VueTopDown.VueTopDownItem],
computed: {
routes () {
return [
{ href: '/page', title: 'Page' },
{ href: '/hello-vue', title: 'HelloVue' }
]
}
}
}
var FooterComp = {
name: 'FooterComp',
template: `{{ vueFooter }}`,
mixins: [VueTopDown.VueTopDownItem],
data () {
return {
vueFooter: 'This is Vue Footer'
}
}
}
var ContentComp = {
name: 'ContentComp',
template: ``,
mixins: [VueTopDown.VueTopDownItem],
computed: {
innerHTML () {
var root = document.createElement('div')
root.innerHTML = this[VTDConstants.OUTER_HTML]
return root.querySelector('*').innerHTML
}
}
}
var HelloVue = {
template: `Hello Vue`,
props: ['clazz'],
inheritAttrs: false
}
var Page = {
template: ``,
props: ['clazz', 'innerHTML'],
inheritAttrs: false
}
var router = new VueRouter([
{ path: '/hello-vue', component: HelloVue },
{ path: '/page', component: Page },
{ path: '*', redirect: '/page' }
])
var inst = new Vue({
router,
mixins: [VueTopDown.VueTopDown],
components: {
HeaderComp: HeaderComp,
FooterComp,
ContentComp
},
data () {
return {
[VueTopDown.VTDConstants]: {
'header': HeaderComp,
'footer': FooterComp,
'.content': ContentComp
}
}
}
})
inst.$mount('#app')
Also keep in mind that similar code works fine in node environment but fails in browser!
Doesn't occur if commenting out inst.$mount('#app')
Expect
The expected behavior of browser should be same as that of node.
I have a problem that passing functions to components is not working the way it's specified in the documentation.
This is in my app.js
methods: {
updateAnswer: function(question) {
console.log('question: '+question);
}
}
This is in my html-file:
<multiplechoice class="question counterIncrement counterShow active" id="q2" whenanswered="{{ updateAnswer('1') }}"></multiplechoice>
This is in my components.js file:
props: [
'whenanswered'
],
ready: function() {
this.whenanswered();
},
I have already tried this:
props: [
{ name: 'whenanswered', type: Function}
];
but still no luck.
This is in my console when I load the page:
Uncaught TypeError: this.whenanswered is not a function
Any help would be very much appreciated :)
You could do this:
<multiplechoice class="question counterIncrement counterShow active" id="q2" :whenanswered="updateAnswer('1')"></multiplechoice>
Without the ':'(same as v-bind) like you did will only send a string and not evaluate. Even with those {{ }}.
But remember that your updateAnswerfunction should return a function. Since your prop will execute updateAnswer('1') and your multiplechoiceactually expects a function that will be executed when it wants.
methods: {
whenanswered: function(question) { // or whenanswered (question) { for ES6
return function () { ... } // or () => {...} for ES6
}
}
A fiddle would help, but basically, you need:
methods: {
whenanswered: function(question) {
...
}
}
if you wanna call that function. A prop is just a string, not a function.
Example:
<div id="app">
Loading...
<data-table on-load="{{onChildLoaded}}"></data-table>
</div>
new Vue({
el: "#app",
methods: {
onChildLoaded: function (msg) {
console.log(msg);
}
},
components: {
'data-table': {
template: 'Loaded',
props: ['onLoad'],
ready: function () {
this.onLoad('the child has now finished loading!')
}
}
}
});