mirror of https://github.com/nextcloud/server.git
Port dav calendar settings page to Vue.js
- 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>pull/27008/head
parent
46dbc8fa98
commit
70edda0342
@ -0,0 +1 @@
|
||||
**/*.spec.js
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -0,0 +1,24 @@
|
||||
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')
|
@ -0,0 +1,116 @@
|
||||
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()
|
||||
})
|
||||
})
|
@ -0,0 +1,127 @@
|
||||
<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>
|
@ -0,0 +1,146 @@
|
||||
// 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>
|
||||
`;
|
@ -0,0 +1,34 @@
|
||||
/**
|
||||
* @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',
|
||||
},
|
||||
}
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue