diff options
author | François Freitag <mail@franek.fr> | 2021-05-16 17:56:50 +0200 |
---|---|---|
committer | François Freitag <mail@franek.fr> | 2021-06-05 11:22:25 +0200 |
commit | 70edda034201b9d7c3d38bb714dfb35324b2841c (patch) | |
tree | 7dbdc91f8b99d82c1e805d657c8fcbafce7a0ece /apps/dav/src | |
parent | 46dbc8fa988176e4a431cafcbae6674fb613c899 (diff) | |
download | nextcloud-server-70edda034201b9d7c3d38bb714dfb35324b2841c.tar.gz nextcloud-server-70edda034201b9d7c3d38bb714dfb35324b2841c.zip |
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>
Diffstat (limited to 'apps/dav/src')
-rw-r--r-- | apps/dav/src/settings.js | 24 | ||||
-rw-r--r-- | apps/dav/src/views/CalDavSettings.spec.js | 116 | ||||
-rw-r--r-- | apps/dav/src/views/CalDavSettings.vue | 127 | ||||
-rw-r--r-- | apps/dav/src/views/__snapshots__/CalDavSettings.spec.js.snap | 146 |
4 files changed, 413 insertions, 0 deletions
diff --git a/apps/dav/src/settings.js b/apps/dav/src/settings.js new file mode 100644 index 00000000000..a81e11c5257 --- /dev/null +++ b/apps/dav/src/settings.js @@ -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') diff --git a/apps/dav/src/views/CalDavSettings.spec.js b/apps/dav/src/views/CalDavSettings.spec.js new file mode 100644 index 00000000000..3f9254a3010 --- /dev/null +++ b/apps/dav/src/views/CalDavSettings.spec.js @@ -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() + }) +}) diff --git a/apps/dav/src/views/CalDavSettings.vue b/apps/dav/src/views/CalDavSettings.vue new file mode 100644 index 00000000000..4b33b2b45bb --- /dev/null +++ b/apps/dav/src/views/CalDavSettings.vue @@ -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> diff --git a/apps/dav/src/views/__snapshots__/CalDavSettings.spec.js.snap b/apps/dav/src/views/__snapshots__/CalDavSettings.spec.js.snap new file mode 100644 index 00000000000..fca9ceae155 --- /dev/null +++ b/apps/dav/src/views/__snapshots__/CalDavSettings.spec.js.snap @@ -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> +`; |