Fix tests & add MOVE

Signed-off-by: John Molakvoæ (skjnldsv) <skjnldsv@protonmail.com>
This commit is contained in:
John Molakvoæ (skjnldsv) 2018-11-09 13:07:58 +01:00
parent 0f72d40d46
commit 17e8723adc
No known key found for this signature in database
GPG Key ID: 60C25B8C072916CF
9 changed files with 547 additions and 3027 deletions

3375
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -31,8 +31,8 @@
"lint:fix": "eslint --ext .js,.vue src tests --fix",
"stylelint": "stylelint src",
"stylelint:fix": "stylelint src --fix",
"test": "jest",
"test:coverage": "jest --coverage"
"test": "mocha-webpack --webpack-config webpack.test.js --require tests/setup.js \"tests/js/**/*.spec.js\"",
"test:watch": "mocha-webpack -w --webpack-config webpack.test.js --require tests/setup.js \"tests/js/**/*.spec.js\""
},
"dependencies": {
"@babel/polyfill": "^7.0.0",
@ -66,8 +66,8 @@
"@babel/preset-env": "^7.1.5",
"@vue/test-utils": "^1.0.0-beta.25",
"babel-eslint": "^8.2.5",
"babel-jest": "^23.6.0",
"babel-loader": "^8.0.4",
"chai": "^4.2.0",
"css-loader": "^0.28.11",
"eslint": "^4.19.1",
"eslint-config-standard": "^11.0.0",
@ -79,42 +79,21 @@
"eslint-plugin-standard": "^3.1.0",
"eslint-plugin-vue": "^4.5.0",
"file-loader": "^2.0.0",
"jest": "^23.6.0",
"jest-serializer-vue": "^2.0.2",
"jsdom": "^13.0.0",
"jsdom-global": "^3.0.2",
"mocha": "^5.2.0",
"mocha-webpack": "^2.0.0-beta.0",
"node-sass": "^4.10.0",
"prettier-eslint": "^8.8.2",
"sass-loader": "^7.1.0",
"stylelint": "^8.4.0",
"stylelint-config-recommended-scss": "^3.2.0",
"stylelint-scss": "^3.4.0",
"stylelint-webpack-plugin": "^0.10.5",
"vue-jest": "^2.6.0",
"vue-loader": "^15.4.2",
"vue-template-compiler": "^2.5.17",
"webpack": "^4.25.1",
"webpack-cli": "^3.1.2",
"webpack-merge": "^4.1.4"
},
"jest": {
"moduleFileExtensions": [
"js",
"vue"
],
"moduleNameMapper": {
"^@/(.*)$": "<rootDir>/src/$1"
},
"transform": {
"^.+\\.js$": "babel-jest",
"^.+\\.vue$": "vue-jest"
},
"snapshotSerializers": [
"<rootDir>/node_modules/jest-serializer-vue"
],
"globals": {
"t": true,
"n": true,
"OC": true,
"OCA": true
}
"webpack-merge": "^4.1.4",
"webpack-node-externals": "^1.7.2"
}
}

View File

@ -113,7 +113,6 @@ import Vue from 'vue'
import VTooltip from 'v-tooltip'
import debounce from 'debounce'
import Contact from '../models/contact'
import rfcProps from '../models/rfcProps.js'
import ContactProperty from './ContactDetails/ContactDetailsProperty'
@ -145,7 +144,7 @@ export default {
type: Boolean,
default: true
},
uid: {
contactKey: {
type: String,
default: undefined
}
@ -276,7 +275,7 @@ export default {
*/
addressbooksOptions() {
return this.addressbooks
.filter(addressbook => addressbook.readOnly)
.filter(addressbook => !addressbook.readOnly)
.map(addressbook => {
return {
id: addressbook.id,
@ -290,22 +289,22 @@ export default {
return this.$store.getters.getAddressbooks
},
contact() {
return this.$store.getters.getContact(this.uid)
return this.$store.getters.getContact(this.contactKey)
}
},
watch: {
contact: function() {
if (this.uid) {
this.selectContact(this.uid)
if (this.contactKey) {
this.selectContact(this.contactKey)
}
}
},
beforeMount() {
// load the desired data if we already selected a contact
if (this.uid) {
this.selectContact(this.uid)
if (this.contactKey) {
this.selectContact(this.contactKey)
}
},
@ -314,12 +313,10 @@ export default {
* Executed on the 'updatedcontact' event
* Send the local clone of contact to the store
*/
updateContact() {
async updateContact() {
this.loadingUpdate = true
this.$store.dispatch('updateContact', this.contact)
.then(() => {
this.loadingUpdate = false
})
await this.$store.dispatch('updateContact', this.contact)
this.loadingUpdate = false
},
/**
@ -343,12 +340,12 @@ export default {
* Fetch updated data if necessary
* Scroll to the selected contact if exists
*
* @param {string} uid the contact uid
* @param {string} key the contact key
*/
selectContact(uid) {
selectContact(key) {
// local version of the contact
this.loadingData = true
let contact = this.$store.getters.getContact(uid)
let contact = this.$store.getters.getContact(key)
if (contact) {
// if contact exists AND if exists on server
@ -356,11 +353,10 @@ export default {
this.$store.dispatch('fetchFullContact', { contact })
.then(() => {
// create empty contact and copy inner data
let localContact = new Contact(
'BEGIN:VCARD\nUID:' + contact.uid + '\nEND:VCARD',
contact.addressbook
let localContact = Object.assign(
Object.create(Object.getPrototypeOf(contact)),
contact
)
localContact.updateContact(contact.jCal)
this.localContact = localContact
this.loadingData = false
})
@ -373,9 +369,9 @@ export default {
} else {
// create empty contact and copy inner data
// wait for an update to really push the contact on the server!
this.localContact = new Contact(
'BEGIN:VCARD\nUID:' + contact.uid + '\nEND:VCARD',
contact.addressbook
this.localContact = Object.assign(
Object.create(Object.getPrototypeOf(contact)),
contact
)
this.loadingData = false
}
@ -404,20 +400,30 @@ export default {
*
* @param {string} addressbookId the desired addressbook ID
*/
moveContactToAddressbook(addressbookId) {
async moveContactToAddressbook(addressbookId) {
let addressbook = this.addressbooks.find(search => search.id === addressbookId)
this.loadingUpdate = true
// TODO Properly implement the MOVE request
if (addressbook) {
this.$store.dispatch('moveContactToAddressbook', {
// we need to use the store contact, not the local contact
// using this.contact and not this.localContact
contact: this.contact,
addressbook
}).then(() => {
this.updateContact()
try {
const contact = await this.$store.dispatch('moveContactToAddressbook', {
// we need to use the store contact, not the local contact
// using this.contact and not this.localContact
contact: this.contact,
addressbook
})
// select the contact again
this.$router.push({
name: 'contact',
params: {
selectedGroup: this.$route.params.selectedGroup,
selectedContact: contact.key
}
})
this.loadingUpdate = false
})
} catch (error) {
console.error(error)
this.loadingUpdate = false
}
}
},

View File

@ -75,7 +75,6 @@ export default {
editingName: false,
copied: false,
copySuccess: true,
readOnly: this.addressbook.readOnly,
toggleEnabledLoading: false,
deleteAddressbookLoading: false,
renameLoading: false,
@ -107,7 +106,7 @@ export default {
]
// check if addressbook is readonly
if (!this.readOnly) {
if (!this.addressbook.readOnly) {
menu.push({
icon: this.renameLoading ? 'icon-loading-small' : 'icon-rename',
// check if editing name

View File

@ -425,31 +425,21 @@ const actions = {
* @param {Object} data destructuring object
* @param {Contact} data.contact the contact to move
* @param {Object} data.addressbook the addressbook to move the contact to
* @returns {Contact} the new contact object
*/
async moveContactToAddressbook(context, { contact, addressbook }) {
// only local move if the contact doesn't exists on the server
if (contact.dav) {
// TODO: implement proper move
// await contact.dav.move(addressbook.dav)
// .catch((error) => {
// console.error(error)
// OC.Notification.showTemporary(t('contacts', 'An error occurred'))
// })
let vData = ICAL.stringify(contact.vCard.jCal)
let newDav
await addressbook.dav.createVCard(vData)
.then((response) => { newDav = response })
.catch((error) => { throw error })
await contact.dav.delete()
.catch((error) => {
console.error(error)
OC.Notification.showTemporary(t('contacts', 'An error occurred'))
})
await Vue.set(contact, 'dav', newDav)
try {
await contact.dav.move(addressbook.dav)
} catch (error) {
throw error
}
}
await context.commit('deleteContactFromAddressbook', contact)
await context.commit('updateContactAddressbook', { contact, addressbook })
await context.commit('addContactToAddressbook', contact)
return contact
}
}

View File

@ -152,12 +152,27 @@ const mutations = {
*/
updateContactAddressbook(state, { contact, addressbook }) {
if (state.contacts[contact.key] && contact instanceof Contact) {
// replace contact object data
// replace contact object data by creating a new contact
let oldKey = contact.key
let newContact = new Contact(contact.dav.data, addressbook)
Vue.set(state.contacts, newContact.key, newContact)
// hijack reference
var newContact = contact
// delete old key, cut reference
Vue.delete(state.contacts, oldKey)
// replace addressbook
Vue.set(newContact, 'addressbook', addressbook)
// set new key, re-assign reference
Vue.set(state.contacts, newContact.key, newContact)
// Update sorted contacts list, replace at exact same position
let index = state.sortedContacts.findIndex(search => search.key === oldKey)
state.sortedContacts[index] = {
key: newContact.key,
value: newContact[state.orderKey]
}
} else {
console.error('Error while replacing the addressbook of following contact', contact)
}
@ -218,7 +233,7 @@ const mutations = {
const getters = {
getContacts: state => state.contacts,
getSortedContacts: state => state.sortedContacts,
getContact: (state) => (uid) => state.contacts[uid],
getContact: (state) => (key) => state.contacts[key],
getOrderKey: state => state.orderKey
}

View File

@ -39,7 +39,7 @@
<content-list :list="contactsList" :contacts="contacts" :loading="loading"
:search-query="searchQuery" />
<!-- main contacts details -->
<contact-details :loading="loading" :uid="selectedContact" />
<contact-details :loading="loading" :contact-key="selectedContact" />
</template>
</div>
</div>

27
tests/setup.js Normal file
View File

@ -0,0 +1,27 @@
/*
* @copyright 2018 Christoph Wurst <christoph@winzerhof-wurst.at>
*
* @author 2018 Christoph Wurst <christoph@winzerhof-wurst.at>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
require('jsdom-global')()
global.expect = require('chai').expect
global.OC = {
getLocale: () => 'en'
}

9
webpack.test.js Normal file
View File

@ -0,0 +1,9 @@
const merge = require('webpack-merge')
const common = require('./webpack.common.js')
const nodeExternals = require('webpack-node-externals')
module.exports = merge(common, {
mode: 'development',
devtool: 'inline-cheap-module-source-map',
externals: [nodeExternals()]
})