- Drop reliance on deprecated global jQuery object. - Allow testing user interactions. - Use newer technology stack. --- Test user interactions with the groupware dav settings Add infrastructure to test Vue components: - Use recommended libraries: - https://vuejs.org/v2/guide/testing.html#Recommendations - Use jest-dom for robust assertions on the DOM state - Use user-event to be more representative of user actions - Code is transpiled by Jest, with the help of vue-jest. Ignore test files for no-unpublished-import. Prevent ESLint from flagging: ``` /home/runner/work/server/server/apps/dav/src/views/CalDavSettings.spec.js Error: 1:24 error "@testing-library/vue" is not published node/no-unpublished-import Error: 2:23 error "@testing-library/user-event" is not published node/no-unpublished-import ``` Signed-off-by: François Freitag <mail@franek.fr>tags/v22.0.0beta4
**/*.spec.js |
clean: | clean: | ||||
rm -rf apps/accessibility/js/ | rm -rf apps/accessibility/js/ | ||||
rm -rf apps/comments/js/ | rm -rf apps/comments/js/ | ||||
rm -rf apps/dav/js/ | |||||
rm -rf apps/files/js/dist/ | rm -rf apps/files/js/dist/ | ||||
rm -rf apps/files_sharing/js/dist/ | rm -rf apps/files_sharing/js/dist/ | ||||
rm -rf apps/files_trashbin/js/ | rm -rf apps/files_trashbin/js/ | ||||
clean-git: clean | clean-git: clean | ||||
git checkout -- apps/accessibility/js/ | git checkout -- apps/accessibility/js/ | ||||
git checkout -- apps/comments/js/ | git checkout -- apps/comments/js/ | ||||
git checkout -- apps/dav/js/ | |||||
git checkout -- apps/files/js/dist/ | git checkout -- apps/files/js/dist/ | ||||
git checkout -- apps/files_sharing/js/dist/ | git checkout -- apps/files_sharing/js/dist/ | ||||
git checkout -- apps/files_trashbin/js/ | git checkout -- apps/files_trashbin/js/ |
!function(e){var t={};function n(o){if(t[o])return t[o].exports;var r=t[o]={i:o,l:!1,exports:{}};return e[o].call(r.exports,r,r.exports,n),r.l=!0,r.exports}n.m=e,n.c=t,n.d=function(e,t,o){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:o})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var o=Object.create(null);if(n.r(o),Object.defineProperty(o,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var r in e)n.d(o,r,function(t){return e[t]}.bind(null,r));return o},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="/js/",n(n.s=706)}({706:function(e,n){ | |||||
!function(e){var t={};function n(o){if(t[o])return t[o].exports;var r=t[o]={i:o,l:!1,exports:{}};return e[o].call(r.exports,r,r.exports,n),r.l=!0,r.exports}n.m=e,n.c=t,n.d=function(e,t,o){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:o})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var o=Object.create(null);if(n.r(o),Object.defineProperty(o,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var r in e)n.d(o,r,function(t){return e[t]}.bind(null,r));return o},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="/js/",n(n.s=721)}({721:function(e,n){ | |||||
/** | /** | ||||
* @copyright Copyright (c) 2020 John Molakvoæ <skjnldsv@protonmail.com> | * @copyright Copyright (c) 2020 John Molakvoæ <skjnldsv@protonmail.com> | ||||
* | * |
!function(e){var n={};function t(o){if(n[o])return n[o].exports;var i=n[o]={i:o,l:!1,exports:{}};return e[o].call(i.exports,i,i.exports,t),i.l=!0,i.exports}t.m=e,t.c=n,t.d=function(e,n,o){t.o(e,n)||Object.defineProperty(e,n,{enumerable:!0,get:o})},t.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},t.t=function(e,n){if(1&n&&(e=t(e)),8&n)return e;if(4&n&&"object"==typeof e&&e&&e.__esModule)return e;var o=Object.create(null);if(t.r(o),Object.defineProperty(o,"default",{enumerable:!0,value:e}),2&n&&"string"!=typeof e)for(var i in e)t.d(o,i,function(n){return e[n]}.bind(null,i));return o},t.n=function(e){var n=e&&e.__esModule?function(){return e.default}:function(){return e};return t.d(n,"a",n),n},t.o=function(e,n){return Object.prototype.hasOwnProperty.call(e,n)},t.p="/js/",t(t.s=429)}({429:function(e,n,t){"use strict";t.r(n);t(430),t(431),t(432),t(433); | |||||
!function(e){var n={};function t(o){if(n[o])return n[o].exports;var i=n[o]={i:o,l:!1,exports:{}};return e[o].call(i.exports,i,i.exports,t),i.l=!0,i.exports}t.m=e,t.c=n,t.d=function(e,n,o){t.o(e,n)||Object.defineProperty(e,n,{enumerable:!0,get:o})},t.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},t.t=function(e,n){if(1&n&&(e=t(e)),8&n)return e;if(4&n&&"object"==typeof e&&e&&e.__esModule)return e;var o=Object.create(null);if(t.r(o),Object.defineProperty(o,"default",{enumerable:!0,value:e}),2&n&&"string"!=typeof e)for(var i in e)t.d(o,i,function(n){return e[n]}.bind(null,i));return o},t.n=function(e){var n=e&&e.__esModule?function(){return e.default}:function(){return e};return t.d(n,"a",n),n},t.o=function(e,n){return Object.prototype.hasOwnProperty.call(e,n)},t.p="/js/",t(t.s=437)}({437:function(e,n,t){"use strict";t.r(n);t(438),t(439),t(440),t(441); | |||||
/** | /** | ||||
* @copyright Copyright (c) 2016 Roeland Jago Douma <roeland@famdouma.nl> | * @copyright Copyright (c) 2016 Roeland Jago Douma <roeland@famdouma.nl> | ||||
* | * | ||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||||
* | * | ||||
*/ | */ | ||||
window.OCA.Comments=OCA.Comments},430:function(e,n){ | |||||
window.OCA.Comments=OCA.Comments},438:function(e,n){ | |||||
/** | /** | ||||
* Copyright (c) 2016 Vincent Petry <pvince81@owncloud.com> | * Copyright (c) 2016 Vincent Petry <pvince81@owncloud.com> | ||||
* | * | ||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||||
* | * | ||||
*/ | */ | ||||
OCA.Comments||(OCA.Comments={})},431:function(e,n){var t;t=Handlebars.template,(OCA.Comments.Templates=OCA.Comments.Templates||{}).filesplugin=t({compiler:[8,">= 4.3.0"],main:function(e,n,t,o,i){var s,r=null!=n?n:e.nullContext||{},a=e.hooks.helperMissing,l=e.escapeExpression,m=e.lookupProperty||function(e,n){if(Object.prototype.hasOwnProperty.call(e,n))return e[n]};return'<a class="action action-comment permanent" title="'+l("function"==typeof(s=null!=(s=m(t,"countMessage")||(null!=n?m(n,"countMessage"):n))?s:a)?s.call(r,{name:"countMessage",hash:{},data:i,loc:{start:{line:1,column:50},end:{line:1,column:66}}}):s)+'" href="#">\n\t<img class="svg" src="'+l("function"==typeof(s=null!=(s=m(t,"iconUrl")||(null!=n?m(n,"iconUrl"):n))?s:a)?s.call(r,{name:"iconUrl",hash:{},data:i,loc:{start:{line:2,column:23},end:{line:2,column:34}}}):s)+'"/>\n</a>\n'},useData:!0})},432:function(e,o){_.extend(OC.Files.Client,{PROPERTY_COMMENTS_UNREAD:"{"+OC.Files.Client.NS_OWNCLOUD+"}comments-unread"}),OCA.Comments=_.extend({},OCA.Comments),OCA.Comments||(OCA.Comments={}),OCA.Comments.FilesPlugin={ignoreLists:["trashbin","files.public"],_formatCommentCount:e=>OCA.Comments.Templates.filesplugin({count:e,countMessage:n("comments","%n unread comment","%n unread comments",e),iconUrl:OC.imagePath("core","actions/comment")}),attach(e){const o=this;if(this.ignoreLists.indexOf(e.id)>=0)return;const i=e._getWebdavProperties;e._getWebdavProperties=function(){const e=i.apply(this,arguments);return e.push(OC.Files.Client.PROPERTY_COMMENTS_UNREAD),e},e.filesClient.addFileInfoParser((function(e){const n={},t=e.propStat[0].properties[OC.Files.Client.PROPERTY_COMMENTS_UNREAD];return _.isUndefined(t)||""===t||(n.commentsUnread=parseInt(t,10)),n})),e.$el.addClass("has-comments");const s=e._createRow;e._createRow=function(e){const n=s.apply(this,arguments);return e.commentsUnread&&n.attr("data-comments-unread",e.commentsUnread),n},e.fileActions.registerAction({name:"Comment",displayName(e){if(e&&e.$file){const t=parseInt(e.$file.data("comments-unread"),10);if(t>=0)return n("comments","1 new comment","{unread} new comments",t,{unread:t})}return t("comments","Comment")},mime:"all",order:-140,iconClass:"icon-comment",permissions:OC.PERMISSION_READ,type:OCA.Files.FileActions.TYPE_INLINE,render(e,n,t){const i=t.$file.data("comments-unread");if(i){const e=$(o._formatCommentCount(i));return t.$file.find("a.name>span.fileactions").append(e),e}return""},actionHandler(e,n){n.$file.find(".action-comment").tooltip("hide"),OCA.Files.Sidebar.setActiveTab("comments"),OCA.Files.Sidebar.open("/"+e)}});const r=e.elementToFile;e.elementToFile=function(e){const n=r.apply(this,arguments),t=e.data("comments-unread");return t&&(n.commentsUnread=t),n}}},OC.Plugins.register("OCA.Files.FileList",OCA.Comments.FilesPlugin)},433:function(e,n){OCA.Comments.ActivityTabViewPlugin={prepareModelForDisplay(e,n,t){if("comments"===e.get("app")&&"comments"===e.get("type")&&"ActivityTabView"===t&&(n.addClass("comment"),e.get("message")&&this._isLong(e.get("message")))){n.addClass("collapsed");const e=$("<div>").addClass("message-overlay");n.find(".activitymessage").after(e),n.on("click",this._onClickCollapsedComment)}},_onClickCollapsedComment(e){let n=$(e.target);n.is(".comment")||(n=n.closest(".comment")),n.removeClass("collapsed")},_isLong:e=>e.length>250||(e.match(/\n/g)||[]).length>1},OC.Plugins.register("OCA.Activity.RenderingPlugins",OCA.Comments.ActivityTabViewPlugin)}}); | |||||
OCA.Comments||(OCA.Comments={})},439:function(e,n){var t;t=Handlebars.template,(OCA.Comments.Templates=OCA.Comments.Templates||{}).filesplugin=t({compiler:[8,">= 4.3.0"],main:function(e,n,t,o,i){var s,r=null!=n?n:e.nullContext||{},a=e.hooks.helperMissing,l=e.escapeExpression,m=e.lookupProperty||function(e,n){if(Object.prototype.hasOwnProperty.call(e,n))return e[n]};return'<a class="action action-comment permanent" title="'+l("function"==typeof(s=null!=(s=m(t,"countMessage")||(null!=n?m(n,"countMessage"):n))?s:a)?s.call(r,{name:"countMessage",hash:{},data:i,loc:{start:{line:1,column:50},end:{line:1,column:66}}}):s)+'" href="#">\n\t<img class="svg" src="'+l("function"==typeof(s=null!=(s=m(t,"iconUrl")||(null!=n?m(n,"iconUrl"):n))?s:a)?s.call(r,{name:"iconUrl",hash:{},data:i,loc:{start:{line:2,column:23},end:{line:2,column:34}}}):s)+'"/>\n</a>\n'},useData:!0})},440:function(e,o){_.extend(OC.Files.Client,{PROPERTY_COMMENTS_UNREAD:"{"+OC.Files.Client.NS_OWNCLOUD+"}comments-unread"}),OCA.Comments=_.extend({},OCA.Comments),OCA.Comments||(OCA.Comments={}),OCA.Comments.FilesPlugin={ignoreLists:["trashbin","files.public"],_formatCommentCount:e=>OCA.Comments.Templates.filesplugin({count:e,countMessage:n("comments","%n unread comment","%n unread comments",e),iconUrl:OC.imagePath("core","actions/comment")}),attach(e){const o=this;if(this.ignoreLists.indexOf(e.id)>=0)return;const i=e._getWebdavProperties;e._getWebdavProperties=function(){const e=i.apply(this,arguments);return e.push(OC.Files.Client.PROPERTY_COMMENTS_UNREAD),e},e.filesClient.addFileInfoParser((function(e){const n={},t=e.propStat[0].properties[OC.Files.Client.PROPERTY_COMMENTS_UNREAD];return _.isUndefined(t)||""===t||(n.commentsUnread=parseInt(t,10)),n})),e.$el.addClass("has-comments");const s=e._createRow;e._createRow=function(e){const n=s.apply(this,arguments);return e.commentsUnread&&n.attr("data-comments-unread",e.commentsUnread),n},e.fileActions.registerAction({name:"Comment",displayName(e){if(e&&e.$file){const t=parseInt(e.$file.data("comments-unread"),10);if(t>=0)return n("comments","1 new comment","{unread} new comments",t,{unread:t})}return t("comments","Comment")},mime:"all",order:-140,iconClass:"icon-comment",permissions:OC.PERMISSION_READ,type:OCA.Files.FileActions.TYPE_INLINE,render(e,n,t){const i=t.$file.data("comments-unread");if(i){const e=$(o._formatCommentCount(i));return t.$file.find("a.name>span.fileactions").append(e),e}return""},actionHandler(e,n){n.$file.find(".action-comment").tooltip("hide"),OCA.Files.Sidebar.setActiveTab("comments"),OCA.Files.Sidebar.open("/"+e)}});const r=e.elementToFile;e.elementToFile=function(e){const n=r.apply(this,arguments),t=e.data("comments-unread");return t&&(n.commentsUnread=t),n}}},OC.Plugins.register("OCA.Files.FileList",OCA.Comments.FilesPlugin)},441:function(e,n){OCA.Comments.ActivityTabViewPlugin={prepareModelForDisplay(e,n,t){if("comments"===e.get("app")&&"comments"===e.get("type")&&"ActivityTabView"===t&&(n.addClass("comment"),e.get("message")&&this._isLong(e.get("message")))){n.addClass("collapsed");const e=$("<div>").addClass("message-overlay");n.find(".activitymessage").after(e),n.on("click",this._onClickCollapsedComment)}},_onClickCollapsedComment(e){let n=$(e.target);n.is(".comment")||(n=n.closest(".comment")),n.removeClass("collapsed")},_isLong:e=>e.length>250||(e.match(/\n/g)||[]).length>1},OC.Plugins.register("OCA.Activity.RenderingPlugins",OCA.Comments.ActivityTabViewPlugin)}}); | |||||
//# sourceMappingURL=comments.js.map | //# sourceMappingURL=comments.js.map |
* @author Georg Ehrke <oc.list@georgehrke.com> | * @author Georg Ehrke <oc.list@georgehrke.com> | ||||
* @author Julius Härtl <jus@bitgrid.net> | * @author Julius Härtl <jus@bitgrid.net> | ||||
* @author Thomas Citharel <nextcloud@tcit.fr> | * @author Thomas Citharel <nextcloud@tcit.fr> | ||||
* @author François Freitag <mail@franek.fr> | |||||
* | * | ||||
* @license GNU AGPL version 3 or any later version | * @license GNU AGPL version 3 or any later version | ||||
* | * | ||||
namespace OCA\DAV\Settings; | namespace OCA\DAV\Settings; | ||||
use OCA\DAV\AppInfo\Application; | |||||
use OCP\AppFramework\Http\TemplateResponse; | use OCP\AppFramework\Http\TemplateResponse; | ||||
use OCP\IConfig; | use OCP\IConfig; | ||||
use OCP\AppFramework\Services\IInitialState; | |||||
use OCP\Settings\ISettings; | use OCP\Settings\ISettings; | ||||
class CalDAVSettings implements ISettings { | class CalDAVSettings implements ISettings { | ||||
/** @var IConfig */ | /** @var IConfig */ | ||||
private $config; | private $config; | ||||
/** @var IInitialState */ | |||||
private $initialState; | |||||
/** | /** | ||||
* CalDAVSettings constructor. | * CalDAVSettings constructor. | ||||
* | * | ||||
* @param IConfig $config | * @param IConfig $config | ||||
* @param IInitialState $initialState | |||||
*/ | */ | ||||
public function __construct(IConfig $config) { | |||||
public function __construct(IConfig $config, IInitialState $initialState) { | |||||
$this->config = $config; | $this->config = $config; | ||||
$this->initialState = $initialState; | |||||
} | } | ||||
/** | |||||
* @return TemplateResponse | |||||
*/ | |||||
public function getForm() { | |||||
$parameters = [ | |||||
'send_invitations' => $this->config->getAppValue('dav', 'sendInvitations', 'yes'), | |||||
'generate_birthday_calendar' => $this->config->getAppValue('dav', 'generateBirthdayCalendar', 'yes'), | |||||
'send_reminders_notifications' => $this->config->getAppValue('dav', 'sendEventReminders', 'yes'), | |||||
'send_reminders_notifications_push' => $this->config->getAppValue('dav', 'sendEventRemindersPush', 'no'), | |||||
public function getForm(): TemplateResponse { | |||||
$defaults = [ | |||||
'sendInvitations' => 'yes', | |||||
'generateBirthdayCalendar' => 'yes', | |||||
'sendEventReminders' => 'yes', | |||||
'sendEventRemindersPush' => 'no', | |||||
]; | ]; | ||||
return new TemplateResponse('dav', 'settings-admin-caldav', $parameters); | |||||
foreach ($defaults as $key => $default) { | |||||
$value = $this->config->getAppValue(Application::APP_ID, $key, $default); | |||||
$this->initialState->provideInitialState($key, $value === 'yes'); | |||||
} | |||||
return new TemplateResponse(Application::APP_ID, 'settings-admin-caldav'); | |||||
} | } | ||||
/** | /** |
import Vue from 'vue' | |||||
import { loadState } from '@nextcloud/initial-state' | |||||
import { translate } from '@nextcloud/l10n' | |||||
import CalDavSettings from './views/CalDavSettings' | |||||
Vue.prototype.$t = translate | |||||
const View = Vue.extend(CalDavSettings) | |||||
const CalDavSettingsView = new View({ | |||||
name: 'CalDavSettingsView', | |||||
data() { | |||||
return { | |||||
sendInvitations: loadState('dav', 'sendInvitations'), | |||||
generateBirthdayCalendar: loadState( | |||||
'dav', | |||||
'generateBirthdayCalendar' | |||||
), | |||||
sendEventReminders: loadState('dav', 'sendEventReminders'), | |||||
sendEventRemindersPush: loadState('dav', 'sendEventRemindersPush'), | |||||
} | |||||
}, | |||||
}) | |||||
CalDavSettingsView.$mount('#settings-admin-caldav') |
import axios from '@nextcloud/axios' | |||||
import { render } from '@testing-library/vue' | |||||
import userEvent from '@testing-library/user-event' | |||||
import CalDavSettings from './CalDavSettings' | |||||
// eslint-disable-next-line no-unused-vars | |||||
import { generateUrl } from '@nextcloud/router' | |||||
jest.mock('@nextcloud/axios') | |||||
jest.mock('@nextcloud/router', () => { | |||||
return { | |||||
generateUrl(url) { | |||||
return url | |||||
}, | |||||
} | |||||
}) | |||||
describe('CalDavSettings', () => { | |||||
const originalOC = global.OC | |||||
const originalOCP = global.OCP | |||||
beforeEach(() => { | |||||
global.OC = { requestToken: 'secret' } | |||||
global.OCP = { | |||||
AppConfig: { | |||||
setValue: jest.fn(), | |||||
}, | |||||
} | |||||
}) | |||||
afterAll(() => { | |||||
global.OC = originalOC | |||||
global.OCP = originalOCP | |||||
}) | |||||
test('interactions', async() => { | |||||
const TLUtils = render( | |||||
CalDavSettings, | |||||
{ | |||||
data() { | |||||
return { | |||||
sendInvitations: true, | |||||
generateBirthdayCalendar: true, | |||||
sendEventReminders: true, | |||||
sendEventRemindersPush: true, | |||||
} | |||||
}, | |||||
}, | |||||
Vue => { | |||||
Vue.prototype.$t = jest.fn((app, text) => text) | |||||
} | |||||
) | |||||
expect(TLUtils.container).toMatchSnapshot() | |||||
const sendInvitations = TLUtils.getByLabelText( | |||||
'Send invitations to attendees' | |||||
) | |||||
expect(sendInvitations).toBeChecked() | |||||
const generateBirthdayCalendar = TLUtils.getByLabelText( | |||||
'Automatically generate a birthday calendar' | |||||
) | |||||
expect(generateBirthdayCalendar).toBeChecked() | |||||
const sendEventReminders = TLUtils.getByLabelText( | |||||
'Send notifications for events' | |||||
) | |||||
expect(sendEventReminders).toBeChecked() | |||||
const sendEventRemindersPush = TLUtils.getByLabelText( | |||||
'Enable notifications for events via push' | |||||
) | |||||
expect(sendEventRemindersPush).toBeChecked() | |||||
await userEvent.click(sendInvitations) | |||||
expect(sendInvitations).not.toBeChecked() | |||||
expect(OCP.AppConfig.setValue).toHaveBeenCalledWith( | |||||
'dav', | |||||
'sendInvitations', | |||||
'no' | |||||
) | |||||
OCP.AppConfig.setValue.mockClear() | |||||
await userEvent.click(sendInvitations) | |||||
expect(sendInvitations).toBeChecked() | |||||
expect(OCP.AppConfig.setValue).toHaveBeenCalledWith( | |||||
'dav', | |||||
'sendInvitations', | |||||
'yes' | |||||
) | |||||
axios.post.mockImplementationOnce((uri) => { | |||||
expect(uri).toBe('/apps/dav/disableBirthdayCalendar') | |||||
return Promise.resolve() | |||||
}) | |||||
await userEvent.click(generateBirthdayCalendar) | |||||
axios.post.mockImplementationOnce((uri) => { | |||||
expect(uri).toBe('/apps/dav/enableBirthdayCalendar') | |||||
return Promise.resolve() | |||||
}) | |||||
await userEvent.click(generateBirthdayCalendar) | |||||
expect(generateBirthdayCalendar).toBeEnabled() | |||||
OCP.AppConfig.setValue.mockClear() | |||||
await userEvent.click(sendEventReminders) | |||||
expect(sendEventReminders).not.toBeChecked() | |||||
expect(OCP.AppConfig.setValue).toHaveBeenCalledWith( | |||||
'dav', | |||||
'sendEventReminders', | |||||
'no' | |||||
) | |||||
expect(sendEventRemindersPush).toBeDisabled() | |||||
OCP.AppConfig.setValue.mockClear() | |||||
await userEvent.click(sendEventReminders) | |||||
expect(sendEventReminders).toBeChecked() | |||||
expect(OCP.AppConfig.setValue).toHaveBeenCalledWith( | |||||
'dav', | |||||
'sendEventReminders', | |||||
'yes' | |||||
) | |||||
expect(sendEventRemindersPush).toBeEnabled() | |||||
}) | |||||
}) |
<template> | |||||
<div class="section"> | |||||
<h2>{{ $t('dav', 'Calendar server') }}</h2> | |||||
<!-- Can use v-html as: | |||||
- $t passes the translated string through DOMPurify.sanitize, | |||||
- replacement strings are not user-controlled. --> | |||||
<!-- eslint-disable-next-line vue/no-v-html --> | |||||
<p class="settings-hint" v-html="hint" /> | |||||
<p> | |||||
<input | |||||
id="caldavSendInvitations" | |||||
v-model="sendInvitations" | |||||
type="checkbox" | |||||
class="checkbox"> | |||||
<label for="caldavSendInvitations"> | |||||
{{ $t('dav', 'Send invitations to attendees') }} | |||||
</label> | |||||
<br> | |||||
<!-- Can use v-html as: | |||||
- $t passes the translated string through DOMPurify.sanitize, | |||||
- replacement strings are not user-controlled. --> | |||||
<!-- eslint-disable-next-line vue/no-v-html --> | |||||
<em v-html="sendInvitationsHelpText" /> | |||||
</p> | |||||
<p> | |||||
<input | |||||
id="caldavGenerateBirthdayCalendar" | |||||
v-model="generateBirthdayCalendar" | |||||
type="checkbox" | |||||
class="checkbox"> | |||||
<label for="caldavGenerateBirthdayCalendar"> | |||||
{{ $t('dav', 'Automatically generate a birthday calendar') }} | |||||
</label> | |||||
<br> | |||||
<em> | |||||
{{ $t('dav', 'Birthday calendars will be generated by a background job.') }} | |||||
</em> | |||||
<br> | |||||
<em> | |||||
{{ $t('dav', 'Hence they will not be available immediately after enabling but will show up after some time.') }} | |||||
</em> | |||||
</p> | |||||
<p> | |||||
<input | |||||
id="caldavSendEventReminders" | |||||
v-model="sendEventReminders" | |||||
type="checkbox" | |||||
class="checkbox"> | |||||
<label for="caldavSendEventReminders"> | |||||
{{ $t('dav', 'Send notifications for events') }} | |||||
</label> | |||||
<br> | |||||
<!-- Can use v-html as: | |||||
- $t passes the translated string through DOMPurify.sanitize, | |||||
- replacement strings are not user-controlled. --> | |||||
<!-- eslint-disable-next-line vue/no-v-html --> | |||||
<em v-html="sendEventRemindersHelpText" /> | |||||
<br> | |||||
<em> | |||||
{{ $t('dav', 'Notifications are sent via background jobs, so these must occur often enough.') }} | |||||
</em> | |||||
</p> | |||||
<p> | |||||
<input | |||||
id="caldavSendEventRemindersPush" | |||||
v-model="sendEventRemindersPush" | |||||
type="checkbox" | |||||
class="checkbox" | |||||
:disabled="!sendEventReminders"> | |||||
<label for="caldavSendEventRemindersPush"> | |||||
{{ $t('dav', 'Enable notifications for events via push') }} | |||||
</label> | |||||
</p> | |||||
</div> | |||||
</template> | |||||
<script> | |||||
import axios from '@nextcloud/axios' | |||||
import { generateUrl } from '@nextcloud/router' | |||||
export default { | |||||
name: 'CalDavSettings', | |||||
computed: { | |||||
hint() { | |||||
const translated = this.$t( | |||||
'dav', | |||||
'Also install the {calendarappstoreopen}Calendar app{linkclose}, or {calendardocopen}connect your desktop & mobile for syncing ↗{linkclose}.', | |||||
) | |||||
return translated | |||||
.replace('{calendarappstoreopen}', '<a target="_blank" href="../apps/office/calendar">') | |||||
.replace('{calendardocopen}', '<a target="_blank" :href="userSyncCalendarsUrl" rel="noreferrer noopener">') | |||||
.replace(/\{linkclose\}/g, '</a>') | |||||
}, | |||||
sendInvitationsHelpText() { | |||||
const translated = this.$t('dav', 'Please make sure to properly set up {emailopen}the email server{linkclose}.') | |||||
return translated | |||||
.replace('{emailopen}', '<a href="../admin#mail_general_settings">') | |||||
.replace('{linkclose}', '</a>') | |||||
}, | |||||
sendEventRemindersHelpText() { | |||||
const translated = this.$t('dav', 'Please make sure to properly set up {emailopen}the email server{linkclose}.') | |||||
return translated | |||||
.replace('{emailopen}', '<a href="../admin#mail_general_settings">') | |||||
.replace('{linkclose}', '</a>') | |||||
}, | |||||
}, | |||||
watch: { | |||||
generateBirthdayCalendar(value) { | |||||
const baseUrl = value ? '/apps/dav/enableBirthdayCalendar' : '/apps/dav/disableBirthdayCalendar' | |||||
axios.post(generateUrl(baseUrl)) | |||||
}, | |||||
sendInvitations(value) { | |||||
OCP.AppConfig.setValue( | |||||
'dav', | |||||
'sendInvitations', | |||||
value ? 'yes' : 'no' | |||||
) | |||||
}, | |||||
sendEventReminders(value) { | |||||
OCP.AppConfig.setValue('dav', 'sendEventReminders', value ? 'yes' : 'no') | |||||
}, | |||||
sendEventRemindersPush(value) { | |||||
OCP.AppConfig.setValue('dav', 'sendEventRemindersPush', value ? 'yes' : 'no') | |||||
}, | |||||
}, | |||||
} | |||||
</script> |
// Jest Snapshot v1, https://goo.gl/fbAQLP | |||||
exports[`CalDavSettings interactions 1`] = ` | |||||
<div> | |||||
<div | |||||
class="section" | |||||
> | |||||
<h2> | |||||
Calendar server | |||||
</h2> | |||||
<p | |||||
class="settings-hint" | |||||
> | |||||
Also install the | |||||
<a | |||||
href="../apps/office/calendar" | |||||
target="_blank" | |||||
> | |||||
Calendar app | |||||
</a> | |||||
, or | |||||
<a | |||||
:href="userSyncCalendarsUrl" | |||||
rel="noreferrer noopener" | |||||
target="_blank" | |||||
> | |||||
connect your desktop & mobile for syncing ↗ | |||||
</a> | |||||
. | |||||
</p> | |||||
<p> | |||||
<input | |||||
class="checkbox" | |||||
id="caldavSendInvitations" | |||||
type="checkbox" | |||||
/> | |||||
<label | |||||
for="caldavSendInvitations" | |||||
> | |||||
Send invitations to attendees | |||||
</label> | |||||
<br /> | |||||
<em> | |||||
Please make sure to properly set up | |||||
<a | |||||
href="../admin#mail_general_settings" | |||||
> | |||||
the email server | |||||
</a> | |||||
. | |||||
</em> | |||||
</p> | |||||
<p> | |||||
<input | |||||
class="checkbox" | |||||
id="caldavGenerateBirthdayCalendar" | |||||
type="checkbox" | |||||
/> | |||||
<label | |||||
for="caldavGenerateBirthdayCalendar" | |||||
> | |||||
Automatically generate a birthday calendar | |||||
</label> | |||||
<br /> | |||||
<em> | |||||
Birthday calendars will be generated by a background job. | |||||
</em> | |||||
<br /> | |||||
<em> | |||||
Hence they will not be available immediately after enabling but will show up after some time. | |||||
</em> | |||||
</p> | |||||
<p> | |||||
<input | |||||
class="checkbox" | |||||
id="caldavSendEventReminders" | |||||
type="checkbox" | |||||
/> | |||||
<label | |||||
for="caldavSendEventReminders" | |||||
> | |||||
Send notifications for events | |||||
</label> | |||||
<br /> | |||||
<em> | |||||
Please make sure to properly set up | |||||
<a | |||||
href="../admin#mail_general_settings" | |||||
> | |||||
the email server | |||||
</a> | |||||
. | |||||
</em> | |||||
<br /> | |||||
<em> | |||||
Notifications are sent via background jobs, so these must occur often enough. | |||||
</em> | |||||
</p> | |||||
<p> | |||||
<input | |||||
class="checkbox" | |||||
id="caldavSendEventRemindersPush" | |||||
type="checkbox" | |||||
/> | |||||
<label | |||||
for="caldavSendEventRemindersPush" | |||||
> | |||||
Enable notifications for events via push | |||||
</label> | |||||
</p> | |||||
</div> | |||||
</div> | |||||
`; |
* @copyright 2017, Georg Ehrke <oc.list@georgehrke.com> | * @copyright 2017, Georg Ehrke <oc.list@georgehrke.com> | ||||
* | * | ||||
* @author Georg Ehrke <oc.list@georgehrke.com> | * @author Georg Ehrke <oc.list@georgehrke.com> | ||||
* @author François Freitag <mail@franek.fr> | |||||
* | * | ||||
* @license GNU AGPL version 3 or any later version | * @license GNU AGPL version 3 or any later version | ||||
* | * | ||||
script('dav', 'settings-admin-caldav'); | script('dav', 'settings-admin-caldav'); | ||||
/** @var \OCP\IL10N $l */ | |||||
/** @var array $_ */ | |||||
?> | ?> | ||||
<form id="CalDAV" class="section"> | |||||
<h2><?php p($l->t('Calendar server')); ?></h2> | |||||
<p class="settings-hint"> | |||||
<?php print_unescaped(str_replace( | |||||
[ | |||||
'{calendarappstoreopen}', | |||||
'{calendardocopen}', | |||||
'{linkclose}', | |||||
], | |||||
[ | |||||
'<a target="_blank" href="../apps/office/calendar">', | |||||
'<a target="_blank" href="' . link_to_docs('user-sync-calendars') . '" rel="noreferrer noopener">', | |||||
'</a>', | |||||
], | |||||
$l->t('Also install the {calendarappstoreopen}Calendar app{linkclose}, or {calendardocopen}connect your desktop & mobile for syncing ↗{linkclose}.') | |||||
)); ?> | |||||
</p> | |||||
<p> | |||||
<input type="checkbox" name="caldav_send_invitations" id="caldavSendInvitations" class="checkbox" | |||||
<?php ($_['send_invitations'] === 'yes') ? print_unescaped('checked="checked"') : null ?>/> | |||||
<label for="caldavSendInvitations"><?php p($l->t('Send invitations to attendees')); ?></label> | |||||
<br> | |||||
<em> | |||||
<?php print_unescaped(str_replace( | |||||
[ | |||||
'{emailopen}', | |||||
'{linkclose}', | |||||
], | |||||
[ | |||||
'<a href="../admin#mail_general_settings">', | |||||
'</a>', | |||||
], | |||||
$l->t('Please make sure to properly set up {emailopen}the email server{linkclose}.') | |||||
)); ?> | |||||
</em> | |||||
</p> | |||||
<p> | |||||
<input type="checkbox" name="caldav_generate_birthday_calendar" id="caldavGenerateBirthdayCalendar" class="checkbox" | |||||
<?php ($_['generate_birthday_calendar'] === 'yes') ? print_unescaped('checked="checked"') : null ?>/> | |||||
<label for="caldavGenerateBirthdayCalendar"><?php p($l->t('Automatically generate a birthday calendar')); ?></label> | |||||
<br> | |||||
<em><?php p($l->t('Birthday calendars will be generated by a background job.')); ?></em><br> | |||||
<em><?php p($l->t('Hence they will not be available immediately after enabling but will show up after some time.')); ?></em> | |||||
</p> | |||||
<p> | |||||
<input type="checkbox" name="caldav_send_reminders_notifications" id="caldavSendRemindersNotifications" class="checkbox" | |||||
<?php ($_['send_reminders_notifications'] === 'yes') ? print_unescaped('checked="checked"') : null ?>/> | |||||
<label for="caldavSendRemindersNotifications"><?php p($l->t('Send notifications for events')); ?></label> | |||||
<br> | |||||
<em> | |||||
<?php print_unescaped(str_replace( | |||||
[ | |||||
'{emailopen}', | |||||
'{linkclose}', | |||||
], | |||||
[ | |||||
'<a href="../admin#mail_general_settings">', | |||||
'</a>', | |||||
], | |||||
$l->t('Please make sure to properly set up {emailopen}the email server{linkclose}.') | |||||
)); ?> | |||||
</em> | |||||
<br> | |||||
<em><?php p($l->t('Notifications are sent via background jobs, so these must occur often enough.')); ?></em> | |||||
</p> | |||||
<p> | |||||
<input type="checkbox" name="caldav_send_reminders_notifications_push" id="caldavSendRemindersNotificationsPush" class="checkbox" | |||||
<?php ($_['send_reminders_notifications_push'] === 'yes') ? print_unescaped('checked="checked"') : null ?> | |||||
<?php ($_['send_reminders_notifications'] === 'yes') ? null : print_unescaped('disabled="disabled"') ?> /> | |||||
<label for="caldavSendRemindersNotificationsPush"><?php p($l->t('Enable notifications for events via push')); ?></label> | |||||
</p> | |||||
</form> | |||||
<div id="settings-admin-caldav"></div> |
use OCA\DAV\Settings\CalDAVSettings; | use OCA\DAV\Settings\CalDAVSettings; | ||||
use OCP\IConfig; | use OCP\IConfig; | ||||
use OCP\AppFramework\Services\IInitialState; | |||||
use Test\TestCase; | use Test\TestCase; | ||||
class CalDAVSettingsTest extends TestCase { | class CalDAVSettingsTest extends TestCase { | ||||
/** @var IConfig|\PHPUnit\Framework\MockObject\MockObject */ | /** @var IConfig|\PHPUnit\Framework\MockObject\MockObject */ | ||||
private $config; | private $config; | ||||
/** @var OCP\AppFramework\Services\IInitialState|\PHPUnit\Framework\MockObject\MockObject */ | |||||
private $initialState; | |||||
/** @var CalDAVSettings */ | /** @var CalDAVSettings */ | ||||
private $settings; | private $settings; | ||||
parent::setUp(); | parent::setUp(); | ||||
$this->config = $this->createMock(IConfig::class); | $this->config = $this->createMock(IConfig::class); | ||||
$this->settings = new CalDAVSettings($this->config); | |||||
$this->initialState = $this->createMock(IInitialState::class); | |||||
$this->settings = new CalDAVSettings($this->config, $this->initialState); | |||||
} | } | ||||
public function testGetForm() { | public function testGetForm() { | ||||
$this->config->method('getAppValue') | |||||
->withConsecutive( | |||||
['dav', 'sendInvitations', 'yes'], | |||||
['dav', 'generateBirthdayCalendar', 'yes'], | |||||
['dav', 'sendEventReminders', 'yes'], | |||||
['dav', 'sendEventRemindersPush', 'no'], | |||||
) | |||||
->will($this->onConsecutiveCalls('yes', 'no', 'yes', 'yes')); | |||||
$this->initialState->method('provideInitialState') | |||||
->withConsecutive( | |||||
['sendInvitations', true], | |||||
['generateBirthdayCalendar', false], | |||||
['sendEventReminders', true], | |||||
['sendEventRemindersPush', true], | |||||
); | |||||
$result = $this->settings->getForm(); | $result = $this->settings->getForm(); | ||||
$this->assertInstanceOf('OCP\AppFramework\Http\TemplateResponse', $result); | $this->assertInstanceOf('OCP\AppFramework\Http\TemplateResponse', $result); |
/** | |||||
* @copyright Copyright (c) 2021 François Freitag <mail@franek.fr> | |||||
* | |||||
* @author François Freitag <mail@franek.fr> | |||||
* | |||||
* @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/>. | |||||
* | |||||
*/ | |||||
const path = require('path') | |||||
module.exports = { | |||||
entry: { | |||||
'settings-admin-caldav': path.join(__dirname, 'src', 'settings.js'), | |||||
}, | |||||
output: { | |||||
path: path.resolve(__dirname, './js'), | |||||
publicPath: '/js/', | |||||
filename: '[name].js', | |||||
}, | |||||
} |
!function(e){var n={};function t(r){if(n[r])return n[r].exports;var o=n[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,t),o.l=!0,o.exports}t.m=e,t.c=n,t.d=function(e,n,r){t.o(e,n)||Object.defineProperty(e,n,{enumerable:!0,get:r})},t.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},t.t=function(e,n){if(1&n&&(e=t(e)),8&n)return e;if(4&n&&"object"==typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(t.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:e}),2&n&&"string"!=typeof e)for(var o in e)t.d(r,o,function(n){return e[n]}.bind(null,o));return r},t.n=function(e){var n=e&&e.__esModule?function(){return e.default}:function(){return e};return t.d(n,"a",n),n},t.o=function(e,n){return Object.prototype.hasOwnProperty.call(e,n)},t.p="/js/",t(t.s=155)}({155:function(e,n,r){ | |||||
!function(e){var n={};function t(r){if(n[r])return n[r].exports;var o=n[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,t),o.l=!0,o.exports}t.m=e,t.c=n,t.d=function(e,n,r){t.o(e,n)||Object.defineProperty(e,n,{enumerable:!0,get:r})},t.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},t.t=function(e,n){if(1&n&&(e=t(e)),8&n)return e;if(4&n&&"object"==typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(t.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:e}),2&n&&"string"!=typeof e)for(var o in e)t.d(r,o,function(n){return e[n]}.bind(null,o));return r},t.n=function(e){var n=e&&e.__esModule?function(){return e.default}:function(){return e};return t.d(n,"a",n),n},t.o=function(e,n){return Object.prototype.hasOwnProperty.call(e,n)},t.p="/js/",t(t.s=179)}({179:function(e,n,r){ | |||||
/** | /** | ||||
* @copyright Copyright (c) 2016 John Molakvoæ <skjnldsv@protonmail.com> | * @copyright Copyright (c) 2016 John Molakvoæ <skjnldsv@protonmail.com> | ||||
* | * |
!function(e){var t={};function r(n){if(t[n])return t[n].exports;var o=t[n]={i:n,l:!1,exports:{}};return e[n].call(o.exports,o,o.exports,r),o.l=!0,o.exports}r.m=e,r.c=t,r.d=function(e,t,n){r.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:n})},r.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},r.t=function(e,t){if(1&t&&(e=r(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var n=Object.create(null);if(r.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)r.d(n,o,function(t){return e[t]}.bind(null,o));return n},r.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return r.d(t,"a",t),t},r.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},r.p="/js/",r(r.s=467)}({467:function(e,t){ | |||||
!function(e){var t={};function r(n){if(t[n])return t[n].exports;var o=t[n]={i:n,l:!1,exports:{}};return e[n].call(o.exports,o,o.exports,r),o.l=!0,o.exports}r.m=e,r.c=t,r.d=function(e,t,n){r.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:n})},r.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},r.t=function(e,t){if(1&t&&(e=r(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var n=Object.create(null);if(r.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)r.d(n,o,function(t){return e[t]}.bind(null,o));return n},r.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return r.d(t,"a",t),t},r.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},r.p="/js/",r(r.s=482)}({482:function(e,t){ | |||||
/** | /** | ||||
* @copyright Copyright (c) 2019 John Molakvoæ <skjnldsv@protonmail.com> | * @copyright Copyright (c) 2019 John Molakvoæ <skjnldsv@protonmail.com> | ||||
* | * |
[ | [ | ||||
'@babel/preset-env', | '@babel/preset-env', | ||||
{ | { | ||||
modules: false, | |||||
useBuiltIns: false, | useBuiltIns: false, | ||||
}, | }, | ||||
], | ], |
'.idea', | '.idea', | ||||
'.jshintrc', | '.jshintrc', | ||||
'.mailmap', | '.mailmap', | ||||
'.npmignore', | |||||
'.php_cs.dist', | '.php_cs.dist', | ||||
'.scrutinizer.yml', | '.scrutinizer.yml', | ||||
'.tag', | '.tag', |