Enhance Aurelia model to handle dynamically loaded HTML - aurelia

I have this view:
<template>
<require from="../customElements/spinner.html"></require>
<div class="sub-submenu-wrapper">
<div show.bind="success">
<div class="sub-submenu-header">
<a href="#/roommanage">
<div class="sub-submenu-header-top">
<i class="fa fa-chevron-left sub-submenu-header-top-icon"></i>
</div>
</a>
<div class="sub-submenu-header-bottom">
<h2>Manage Rooms</h2>
</div>
</div>
<div class="sub-submenu-content">
<div class="managerooms-wrapper">
<table id="tableData" class="stripe row-border cell-border"></table>
</div>
</div>
</div>
<div class="sub-submenu-loading" show.bind="!success">
<spinner></spinner>
</div>
</div>
And here is the model:
import {inject} from 'aurelia-framework';
import {HttpClient} from 'aurelia-fetch-client';
import {Router} from 'aurelia-router';
import 'isomorphic-fetch';
import {appState} from '../appState';
import 'jquery';
import 'datatables.net';
#inject(HttpClient, Router, appState)
export class ManageRooms{
tableData = [];
success = false;
constructor(http, router, appstate){
this.http = http;
this.router = router;
this.jsonobj = {
'operation':'getrooms'
};
}
attached(){
this.http.fetch('assets/api/api.php', {
method: 'post',
body: JSON.stringify(this.jsonobj)
})
.then(response => response.json())
.then(data => {
if (data.error==='true') {
console.log(data.errortype);
}else {
for(var i = 0; i < data.length; i++){
this.tableData.push(
{
0: "<div>"+data[i].roomno+"</div>",
1: "&nbsp"+data[i].roomtype,
2: "<div>"+(data[i].booked ? 'Yes':'No')+"</div>",
3: "<div class='actionButtonsArea'>"
+"<a href='1'><i class='fa fa-eye fa-bot'></i></a>"
+"<a href='1'><i class='fa fa-edit fa-bot'></i></a>"
+"<a href='3'><i class='fa fa-remove fa-bot'></i></a>"
+"<a href='4'><i class='fa fa-trash-o fa-bot'></i></a>"
+"</div>"
}
);
}
$('#tableData').DataTable({
autoWidth: false,
data: this.tableData,
columns: [
{title: "Room No"},
{title: "Type"},
{title: "Occupied"},
{title: "Action"}
]
}
);
this.success = true;
}
})
.catch(response =>{
});
}
detached(){
$('#tableData').DataTable().destroy("remove");
}
}
In summary, I am making a fetch call, getting some data, making a table from it. What i want is to make the HTML data(i.e. table) that I am inserting bindable or connected to my view model. I found some information about Aurelia's enhancement here: http://ilikekillnerds.com/2016/01/enhancing-at-will-using-aurelias-templating-engine-enhance-api/ but i have trouble figuring out how to integrate this with my own code.

Overview
Prerequisite: have some already-working aurelia app
Import the TemplatingEngine
Insert your dynamic html into the DOM (e.g. via .innerHTML)
Call TemplatingEngine.enhance(config) with proper configuration
Details
Prerequisite - Create the skeleton:
au new enhance_test
I'm using the minimal example, using the skeleton provided in Aurelia CLI.
The actual code:
app.html:
<template>
<h1>${message}</h1>
<div id="content-div"></div>
</template>
app.ts:
import {autoinject, TemplatingEngine} from 'aurelia-framework';
#autoinject
export class App {
message = 'Hello World!';
constructor(private _templEngine: TemplatingEngine) { }
attached() {
let contentDivElem = document.getElementById("content-div");
contentDivElem.innerHTML = "<button>TEST ${message}</button>"; //inserting your dynamic HTML
let elemView = this._templEngine.enhance({ element: contentDivElem, bindingContext: this}); //binding the element to the instance of App
}
}

Related

get value from child component Vuejs

This is my LoggedUser component which return the name of the logged user and its scope. the name will be displayed in the side bar and the scope will be used to display countries whom under the user's scope
<template>
{{ message }}
</template>
<script lang="ts">
import { onMounted, ref } from 'vue';
export default {
name: "LoggedUser",
setup() {
const message = ref('You are not logged in!');
const scope = ref ('');
onMounted(async () => {
let token = '??';
const response = await fetch('https://localhost:44391/api/Auth/User', {
headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' +
token },
credentials: 'include'
});
const content = await response.json();
message.value = `Hi ${content.name}`;
scope.value = `${content.scope}`;
});
return {
message,
scope
}
}
}
{{message}}is used in the sideBar component but i need scope in my Home.vue to use it in a test.
Here is my Home.vue component
<template>
<div class="container w-75" v-show="showGrid">
<search-bar v-show="searchbar"></search-bar>
<div class="row" style="width:900px; height:900px; padding-left:200px">
<div class="col-md-4" v-for="country of countries" v-bind:key="country">
<div class="card p-3" style="cursor:pointer">
<router-link :to="{ path: '/FetchData', query: { query: country.countryName }}">
<div class="d-flex flex-row mb-3">
<div class="d-flex flex-column ml-2"><span>{{country.countryId}}</span></div>
</div>
<h6 style="text-align:left">{{country.countryName}}</h6>
</router-link>
<div class="d-flex justify-content-between install mt-3">
</div>
</div>
<br /><span v-if="!countries"><img src="../assets/loader.gif" /></span><br />
</div>
this is the vue part. I want to test user scope == country scope
<script>
import axios from 'axios'
import SearchBar from './SearchBar.vue'
import SideBar from './SideBar.vue'
import LoggedUser from './LoggedUser.vue'
import swal from 'sweetalert';
import '#trevoreyre/autocomplete-vue/dist/style.css'
export default {
name: "Home",
components: {
SearchBar,
SideBar,
LoggedUser
},
data() {
return {
countries: [],
showGrid: true,
}
},
methods: {
getCountries() {
let country = this.$route.query.query
if (!country) {
axios.get("https://localhost:44391/api/Pho/GetCountries")
.then(res => this.countries = res.data)
} else {
axios.get("https://localhost:44391/api/Pho/GetCountries?country=" + this.$route.query.query)
.then(res => this.countries = res.data);
this.searchbar = false;
}
},
I need to get scope value in Home.vue from LoggedUser.vue. How could i do it?
you have multiple options, one to put the api call to home, second option would be to use composition api and share a common state between these components and the third option would be to use a store management tool such as pinia or vuex.
Composition api would probably be the simplest solution. You basically just need to set a variable outside of the function that will be used in setup, short code example:
const cart = ref({})
function useCart () {
// super complicated cart logic
return {
cart: computed(() => cart.value)
}
}
you still need to adjust this snippet to your needs it was just to show you a way
you can watch this talk by Vanessa Otto to hear more about how it works: https://www.youtube.com/watch?v=MgtQ9t74mhw

(Vue) I have problems reusing references from a composable function

I hope it is okay that I included my full code. Otherwise it would be difficult to understand my question.
I have made a composable function for my Vue application, which purpose is to fetch a collection of documents from a database.
The composable looks like this:
import { ref, watchEffect } from 'vue'
import { projectFirestore } from '../firebase/config'
const getCollection = (collection, query) => {
const documents = ref(null)
const error = ref(null)
let collectionRef = projectFirestore.collection(collection)
.orderBy('createdAt')
if (query) {
collectionRef = collectionRef.where(...query)
}
const unsub = collectionRef.onSnapshot(snap => {
let results = []
snap.docs.forEach(doc => {
doc.data().createdAt && results.push({ ...doc.data(), id: doc.id })
})
documents.value = results
error.value = null
}, (err) => {
console.log(err.message)
document.value = null
error.value = 'could not fetch data'
})
watchEffect((onInvalidate) =>{
onInvalidate(() => unsub());
});
return {
documents,
error
}
}
export default getCollection
Then I have a component where I store the data from the database
<template>
<div v-for="playlist in playlists" :key="playlist.id">
<div class="single">
<div class="thumbnail">
<img :src="playlist.coverUrl">
</div>
<div class="info">
<h3>{‌{ playlist.title }}</h3>
<p>created by {‌{ playlist.userName }}</p>
</div>
<div class="song-number">
<p>{‌{ playlist.songs.length }} songs</p>
</div>
</div>
</div>
</template>
<script>
export default {
// receiving props
props: ['playlists'],
}
</script>
And finally, I output the data inside the main Home component, where I use the documents and error reference from the composable file.
<template>
<div class="home">
<div v-if="error" class="error">Could not fetch the data</div>
<div v-if="documents">
<ListView :playlists="documents" />
</div>
</div>
</template>
<script>
import ListView from '../components/ListView.vue'
import getCollection from '../composables/getCollection'
export default {
name: 'Home',
components: { ListView },
setup() {
const { error, documents } = getCollection('playlists')
return { error, documents }
}
}
</script>
That is all well and good.
But now I wish to add data from a second collection called "books", and the idea is to use the same composable to fetch the data from that collection as well,
but the problem is that inside the Home component, I cannot use the references twice.
I cannot write:
<template>
<div class="home">
<div v-if="error" class="error">Could not fetch the data</div>
<div v-if="documents">
<ListView :playlists="documents" />
<ListView2 :books="documents" />
</div>
</div>
</template>
export default {
name: 'Home',
components: { ListView, ListView2 },
setup() {
const { error, documents } = getCollection('playlists')
const { error, documents } = getCollection('books')
return { error, documents }
}
}
This will give me an error because I reference documents and error twice.
So what I tried was to nest these inside the components themselves
Example:
<template>
<div v-for="playlist in playlists" :key="playlist.id">
<div class="single">
<div class="thumbnail">
<img :src="playlist.coverUrl">
</div>
<div class="title">
{{ playlist.title }}
</div>
<div class="description">
{{ playlist.description }}
</div>
<div>
<router-link :to="{ name: 'PlaylistDetails', params: { id: playlist.id }}">Edit</router-link>
</div>
</div>
</div>
</template>
<script>
import getCollection from '../composables/getCollection'
export default {
setup() {
const { documents, error } = getCollection('playlists')
return {
documents,
error
}
}
}
</script>
This does not work either.
I will just get a 404 error if I try to view this component.
So what is the correct way of writing this?
Try out to rename the destructed fields like :
const { error : playlistsError, documents : playlists } = getCollection('playlists')
const { error : booksError, documents : books } = getCollection('books')
return { playlistsError, playlists , booksError , books }

Get URL query parameters in Vue 3 on Component

i am new to Vue JS, i must say i really love this platform. I started using it just 3 days back. I am just trying to get the URL query parameter and i am using vue-router as well. Here is how i have it:
http://localhost:8001/login?id=1
Here is how my controller look like.
<template>
<section class="contact-info-area">
<div class="container">
<div class="row">
<div class="col-lg-12">
<section class="write-massage-area">
<div class="mb-5"></div>
<div class="row justify-content-center">
<div class="col-lg-5">
<div class="write-massage-content">
<div class="write-massage-title text-center">
<h3 class="title">Login {{ $route.query.id }}</h3> <!-- THIS IS WORKING -->
</div>
<div class="write-massage-input">
<form #submit.prevent="onSubmitForm">
<div class="row">
<div class="col-lg-12">
<div class="input-box mt-10">
<input type="text" placeholder="Email" v-model="form['email']">
<ErrorMessage :validationStatus="v$.form.email"></ErrorMessage>
</div>
</div>
<div class="col-lg-12">
<div class="input-box mt-10">
<input type="text" placeholder="Password" v-model="form['password']">
</div>
</div>
<div class="col-lg-12">
<div class="input-box mt-10 text-center">
<button type="submit" class="main-btn main-btn-2">Login</button>
</div>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
<div class="mb-5"></div>
</section>
</div>
</div>
</div>
</section>
</template>
<script>
import { ref } from 'vue'
import useVuelidate from '#vuelidate/core'
import { required, minLength, email } from '#vuelidate/validators'
import ErrorMessage from '../components/ErrorMessage'
export default {
name: "login",
components: {
ErrorMessage
},
created(){
},
setup(){
const form = ref({})
const rules = {
form: {
email: {
required,
email
},
password: {
required,
minLength : minLength(5)
}
}
}
const v$ = useVuelidate(rules, { form })
function onSubmitForm(){
console.log(this.$route.query.id) **<!-- THIS IS NOT WORKING -->**
v$.value.$touch()
console.log(form.value)
}
return { form, onSubmitForm, v$}
}
}
</script>
here on the above code. On button submit i am going to a function called onSubmitForm, there i am using console.log(this.$route.query.id) but this is giving a below error :
Uncaught TypeError: Cannot read property 'query' of undefined
at Proxy.onSubmitForm (Login.vue?a55b:84)
Why this is happening? I am seeing that in the vue document as well, they mentioned in the same way. What i am missing in this?
Thank you!
You can call useRoute to access the query params...
<script setup>
import { useRoute } from 'vue-router'
const route = useRoute()
console.log(route.query)
</script>
If you use parameters and your endpoint looks something like this:
{
path: '/inspect_detail/:id',
name: 'inspect_detail',
component: function() {
return import ( '../views/Inspect_detail.vue')
},
params: true
},
and you are routing like this:
<router-link :to="{ name: 'inspect_detail', params: { id: akey }}">...</router-link>
then you can pickup the values like this:
<script>
import { useRoute } from 'vue-router';
export default {
setup(){
const route = useRoute()
console.log( route.params );
}
}
</script>
Bit late but if you want query params to work on page refresh you have to wait for the router to get ready as its asynchronous. The router wont be ready if its a page refresh. The router will be ready if its navigation from another page in that case the query params will be available from the begining.
<script setup>
import { onMounted } from 'vue'
import { useRouter, useRoute } from 'vue-router'
const router = useRouter()
const route = useRoute()
//just to demonstrate
console.log(route.query)
// If it is a page refresh it will print null.
// The router wont be ready if its a page refresh. In that case the query wont be available.
// The router will be ready if its navigation from another page in that case the query params will be available
onMounted(() => {
getUrlQueryParams()
});
getUrlQueryParams = async () => {
//router is async so we wait for it to be ready
await router.isReady()
//once its ready we can access the query params
console.log(route.query)
});
</script>
I hadn't done frontend in a couple of months and had to refresh on Vue 3 and route query properties, and this question came up first.
Now the memory jog has kicked in, I believe the correct way is to pass the props from the router to the component as shown in the examples here https://router.vuejs.org/guide/essentials/passing-props.html
Essentially, call your route
{
path: "/login",
name: "Login",
props: route => ({ id: route.query.id }),
component: () => import(/* webpackChunkName: "login" */ "../views/Login.vue"),
},
to be able to access the id field.
Alternately, you can sent the whole lot with props: route => ({ query: route.query })
Pick it up as a prop in your view/component
export default {
name: "login",
components: {
ErrorMessage
},
created(){
},
props: {
id: {
type: String,
default: "",
}
}
setup(props){
const form = ref({})
const rules = {
form: {
email: {
required,
email
},
password: {
required,
minLength : minLength(5)
}
}
}
const v$ = useVuelidate(rules, { form })
function onSubmitForm(){
console.log(props.id)
v$.value.$touch()
console.log(form.value)
}
return { form, onSubmitForm, v$}
}
}

VueJS display dynamic modal component

I have posts and replys s.t. replies belong to posts via the attribute reply.posts_id.
I am attempting to show the reply form as a modal for the user to enter a reply. However, I want to create a generic Modal component that I can use everywhere with content that is specified in another component built for a specific context.
Reply to post is the first place I woul like this to work.
Currently, the Vuex correctly returns Modal visible:true when the reply button is clicked, but the modal does not render and I get the error message showing that the Modal component is not found:
Unknown custom element: <ModalReplyForm> - did you register the component correctly? For recursive components, make sure to provide the "name" option.
I am using vuex to manage the visibility of the modal. Here are the relevant files:
store.js:
import Vue from 'vue'
import Vuex from 'vuex'
import axios from 'axios'
...
Vue.use(Vuex)
export default new Vuex.Store({
state: {
status: '',
...
modalVisible: false,
modalComponent: null
},
mutations: {
...
showModal(state, componentName) {
console.log('showing the modal')
state.modalVisible = true;
state.modalComponent = componentName;
},
hideModal(state) {
console.log('hiding the modal')
state.modalVisible = false;
}
},
actions: {
...
}
},
getters: {
isAuthenticated: state => !!state.user,
authStatus: state => state.status,
user: state => state.user,
token: state => state.token,
posts: state => {
return state.posts;
}
...
}
})
App.vue
<template>
<div id="app">
<app-modal></app-modal>
<NavigationBar />
<div class="container mt-20">
<router-view />
</div>
<vue-snotify></vue-snotify>
</div>
</template>
<script>
import AppModal from '#/components/global/AppModal';
import NavigationBar from '#/components/layout/NavigationBar'
export default {
name: "App",
components: {
AppModal,
NavigationBar
}
};
</script>
<style>
body {
background-color: #f7f7f7;
}
.is-danger {
color: #9f3a38;
}
</style>
Post.vue (houses the button to call the reply modal):
<template>
<div class="row ui dividing header news">
<!-- Label -->
<div class="m-1 col-md-2 ui image justify-content-center align-self-center">
<img v-if="post.avatar_url" :src="post.avatar_url" class="mini rounded"/>
<v-gravatar v-else :email="post.email" class="mini thumbnail rounded image rounded-circle z-depth-1-half"/>
</div>
<!-- Excerpt -->
<div class="col-md-9 excerpt">
...
<!-- Feed footer -->
<div class="feed-footer row">
<div class="small"> {{ post.created_at | timeAgo }}</div>
<button type="button" flat color="green" #click="showModal('ModalReplyForm')">
<i class="fa fa-reply" ></i>
...
<div v-show="postOwner(post)" class="">
<button type="button" flat color="grey" #click="deletePost(post.id)">
<i class="fa fa-trash " ></i>
</button>
</div>
</div>
</div>
</div>
</template>
<script>
import { mapMutations } from 'vuex';
import PostsService from '../../services/PostsService'
import RepliesService from '../../services/RepliesService'
import Replies from '#/components/Reply/Replies'
import ReplyForm from '#/components/Reply/ReplyForm'
export default {
name: "Post",
props: {
post: {
type: Object,
required: true
}
},
components: {
Replies,
ReplyForm
},
computed: {
me() {
return this.$store.getters.user
}
},
methods: {
...mapMutations(['showModal']),
...
}
};
</script>
AppModal.vue - generic Modal component
<template>
<div class="c-appModal">
<div class="c-appModal__overlay" v-if="visible"></div>
<div class="c-appModal__content" v-if="visible" #click.self="hideModal"></div>
<div class="c-appModal__innerContent">
<component :is="component"></component>
</div>
</div>
</template>
<script>
import Vue from 'vue';
import { mapState, mapMutations } from 'vuex';
export default {
name: 'AppModal',
data() {
return {
component: null
}
},
computed: {
...mapState({
visible: 'modalVisible',
modalComponent: 'modalComponent'
}),
},
methods: {
...mapMutations(['hideModal'])
},
watch: {
modalComponent(componentName) {
if (!componentName) return;
Vue.component(componentName, () => import(`#/components/modals/${componentName}`));
this.component = componentName;
}
},
created() {
const escapeHandler = (e) => {
if (e.key === 'Escape' && this.visible) {
this.hideModal();
}
};
document.addEventListener('keydown', escapeHandler);
this.$once('hook:destroyed', () => {
document.removeEventListener('keydown', escapeHandler);
});
},
};
</script>
ModalReplyForm - specific reply modal content
<template>
<div>
<div class="c-modalReply">
<div>
<label for="reply">Your comment</label>
<div class="field">
<textarea name="reply" v-model="reply" rows="2" placeholder="Compose reply"></textarea>
</div>
</div>
<button class="c-modalReply__cancel" #click="hideModal">Cancel</button>
<button class="c-modalReply__post" :disabled="!isFormValid" #click="createReply">Reply</button>
</div>
</div>
</template>
<script>
import RepliesService from '#/services/RepliesService'
import { mapMutations } from 'vuex';
export default {
name: "ModalReplyForm",
// props: {
// post: {
// type: Object,
// required: true
// }
// },
data() {
return {
reply: ""
};
},
computed: {
isFormValid() {
return !!this.reply;
},
currentGroup() {
return this.$store.getters.currentPost;
}
},
methods: {
...mapMutations([
'hideModal'
]),
async createReply () {
let result = await RepliesService.addReply({
reply: {
body: this.reply,
postId: this.post.id
}
});
this.$emit("reply-created");
this.hideModal();
}
}
};
</script>
Unknown custom element: - did you register the
component correctly? For recursive components, make sure to provide
the "name" option.
This message says that you never imported/defined ModalReplyForm, which you have not.
In my own generic modal, I ended up having to import all the components that might appear within the modal itself.
If you add a:
import ModalReportForm from ...
and a:
components: {
ModalReplyForm
}
to AppModal.vue, the modal should then do what you expect.

how to unittest a vuejs component update with methods using promise

I'm testing a Vue component with Jest. This is my working directory :
Here is my component Subscription.vue :
<template>
<div id="page">
<page-title icon="fa-list-alt">
<translate slot="title">Subscription</translate>
<translate slot="comment">Consult the detail of your subscription</translate>
</page-title>
<panel v-if="error">
<span slot="title">
<icon icon="fa-exclamation-triangle"></icon>
<translate>Error</translate>
</span>
{{ error }}
</panel>
<panel v-else-if="subscription_dict">
<span slot="title">{{ _subscription_address }}</span>
<div class="row" v-if="subscription_dict.product.hsi">
<div class="col-xs-12">
<subscription-hsi-panel
:hsi="subscription_dict.product.hsi"
:business="context.business">
</subscription-hsi-panel>
</div>
</div>
<div class="row" v-if="subscription_dict.product.itv">
<div class="col-xs-12">
<subscription-itv-panel
:itv="subscription_dict.product.itv">
</subscription-itv-panel>
</div>
</div>
<div class="row" v-if="subscription_dict.product.voip">
<div class="col-xs-6">
<panel icon="icon-voip">
<translate slot="title">Phone</translate>
telefon products
</panel>
</div>
</div>
</panel>
<div v-else class="text-center">
<i class="fa fa-spinner fa-pulse fa-3x fa-fw"></i>
</div>
<span v-if="subscription_dict"><b>subscription_dict.product.voip : </b>{{ subscription_dict.product.voip }}</br></span>
<span v-if="subscription_dict"><b>subscription_dict.product : </b>{{ subscription_dict.product }}</br></span>
</div>
</template>
<script>
import PageTitle from '../../core/components/PageTitle.vue'
import SubscriptionHsiPanel from './SubscriptionHsiPanel.vue'
import SubscriptionItvPanel from './SubscriptionItvPanel.vue'
import Panel from '../../core/components/Panel.vue'
import Icon from '../../core/components/Icon.vue'
import api from '../../core/api.js'
export default {
data() {
return {
subscription_dict: false,
error: false
}
},
props: ['requests_url', 'context'],
computed: {
_subscription_address() {
var sub_address = this.subscription_dict.subscription_address
return sub_address + ' - ' + this.subscription_dict.package.join(' - ')
}
},
created() {
this.get_subscription()
this.translate()
},
methods: {
get_subscription() {
let self = this
api.get_subscription(self.requests_url.subscriptions_request)
.then(function(response) {
self.subscription_dict = response
})
.catch(function(error) {
if(error) {
self.error = error
} else {
self.error = self.$gettext(
'We were not able to retrieve your subscription information!')
}
})
},
translate() {
this.$gettext('Bridge hsi')
this.$gettext('Bridge voip_biz')
this.$gettext('Router')
}
},
components: {
PageTitle,
Panel,
Icon,
SubscriptionHsiPanel,
SubscriptionItvPanel
}
}
</script>
<style lang="sass">
#import "../../core/css/tooltip"
.table
table-layout: fixed
> tbody > tr >
th
width: 33%
td
vertical-align: middle
</style>
And here is my test subscription.js:
import Vue from 'vue'
import translations from 'src/translations.json'
import GetTextPlugin from 'vue-gettext'
import VTooltip from 'v-tooltip'
import Subscription from 'src/subscription/components/Subscription'
jest.mock('../../core/api');
Vue.use(GetTextPlugin, {
availableLanguages: {
en: 'English',
fr: 'Français',
de: 'Deutsch',
},
defaultLanguage: 'fr',
translations: translations
})
Vue.use(VTooltip)
it('render when error', (done) => {
const renderer = require('vue-server-renderer').createRenderer()
const vm = new Vue({
el: document.createElement('div'),
render: h => h(Subscription, {
props: {
requests_url: {
subscriptions_request: 'error_empty_promise'
}
}
})
})
renderer.renderToString(vm, (err, str) => {
setImmediate(() => {
expect(str).toMatchSnapshot()
done()
})
})
})
The component's method get_subscription() use my API function get_subscription:
import axios from 'axios'
export default {
get_subscription(url) {
return axios.get(url, {
credentials: 'same-origin'
})
.then(function(response){
if(response.data.error){
return Promise.reject(response.data.error)
}else{
return Promise.resolve(response.data)
}
})
.catch(function(error) {
return Promise.reject(false)
})
}
}
For my test, I have mocked this function like this :
const promises_object = {
error_empty_promise: new Promise((resolve, reject) => {
process.nextTick(
() => reject(false)
)
})
}
export default {
get_subscription(url) {
return promises_object[url]
}
}
Now, in the test I render and compare against a snapshot. My issue is, I can't find a way to wait that the promise of get_subscription is reject before making the comparison.
The result is that my snapshot reflect the state of the component before the update of the DOM, which is done after the asynchronous call on the API.
Is there a way to tell jest to wait until the Promise is reject before calling expect(str).toMatchSnapshot() ?
Jest docs say
it expects the return value to be a Promise that is going to be
resolved.
You have a promise that is going to be rejected, so you need a Promise that resolves when your promise is rejected.
isRejected(rejectingPromise, someExpects) {
return new Promise((resolve) => {
rejectingPromise.then(
() => null, // if it resolves, fail
(err) => { // if it rejects, resolve
// Maybe do someExpects() here
resolve();
}
});
}