mirror of
https://github.com/nextcloud/server.git
synced 2026-05-28 04:32:30 -04:00
refactor(settings): switch wipe dialog to confirm
Signed-off-by: Peter Ringelmann <peter.ringelmann@nextcloud.com>
This commit is contained in:
parent
1d035b4cfc
commit
bbdb7d01b8
3 changed files with 27 additions and 83 deletions
|
|
@ -16,10 +16,16 @@ vi.hoisted(() => {
|
|||
|
||||
import type { IToken } from '../store/authtoken.ts'
|
||||
|
||||
// Mock @nextcloud/dialogs so the wipe action's showConfirmation call resolves
|
||||
// synchronously in tests. Hoisted so it's installed before AuthToken.vue imports.
|
||||
const showConfirmationMock = vi.hoisted(() => vi.fn())
|
||||
vi.mock('@nextcloud/dialogs', () => ({
|
||||
showConfirmation: showConfirmationMock,
|
||||
}))
|
||||
|
||||
import NcNoteCard from '@nextcloud/vue/components/NcNoteCard'
|
||||
import AuthToken from './AuthToken.vue'
|
||||
import AuthTokenDeleteDialog from './AuthTokenDeleteDialog.vue'
|
||||
import AuthTokenWipeDialog from './AuthTokenWipeDialog.vue'
|
||||
import { TokenType, useAuthTokenStore } from '../store/authtoken.ts'
|
||||
import { detect } from '../utils/userAgentDetect.ts'
|
||||
|
||||
|
|
@ -143,33 +149,27 @@ describe('AuthToken wipe flow', () => {
|
|||
vi.clearAllMocks()
|
||||
})
|
||||
|
||||
it('does not call wipeToken when the wipe action is triggered (dialog opens first)', async () => {
|
||||
it('does not call wipeToken when the user rejects the confirmation', async () => {
|
||||
showConfirmationMock.mockResolvedValueOnce(false)
|
||||
const token = makeToken()
|
||||
const wrapper = mountAuthToken(token)
|
||||
const store = useAuthTokenStore()
|
||||
|
||||
;(wrapper.vm as unknown as { wipe: () => void }).wipe()
|
||||
await wrapper.vm.$nextTick()
|
||||
await (wrapper.vm as unknown as { wipe: () => Promise<void> }).wipe()
|
||||
|
||||
const dialog = wrapper.findComponent(AuthTokenWipeDialog)
|
||||
expect(dialog.exists()).toBe(true)
|
||||
expect(dialog.props('open')).toBe(true)
|
||||
expect(showConfirmationMock).toHaveBeenCalledTimes(1)
|
||||
expect(store.wipeToken).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('calls wipeToken only after the dialog emits confirm', async () => {
|
||||
it('calls wipeToken when the user accepts the confirmation', async () => {
|
||||
showConfirmationMock.mockResolvedValueOnce(true)
|
||||
const token = makeToken()
|
||||
const wrapper = mountAuthToken(token)
|
||||
const store = useAuthTokenStore()
|
||||
|
||||
;(wrapper.vm as unknown as { wipe: () => void }).wipe()
|
||||
await wrapper.vm.$nextTick()
|
||||
|
||||
const dialog = wrapper.findComponent(AuthTokenWipeDialog)
|
||||
dialog.vm.$emit('confirm')
|
||||
dialog.vm.$emit('update:open', false)
|
||||
await wrapper.vm.$nextTick()
|
||||
await (wrapper.vm as unknown as { wipe: () => Promise<void> }).wipe()
|
||||
|
||||
expect(showConfirmationMock).toHaveBeenCalledTimes(1)
|
||||
expect(store.wipeToken).toHaveBeenCalledTimes(1)
|
||||
expect(store.wipeToken).toHaveBeenCalledWith(token)
|
||||
})
|
||||
|
|
|
|||
|
|
@ -87,11 +87,6 @@
|
|||
:token="token"
|
||||
:open.sync="deleteDialogOpen"
|
||||
@confirm="confirmDelete" />
|
||||
<AuthTokenWipeDialog
|
||||
v-if="wipeDialogOpen"
|
||||
:token="token"
|
||||
:open.sync="wipeDialogOpen"
|
||||
@confirm="confirmWipe" />
|
||||
</tr>
|
||||
</template>
|
||||
|
||||
|
|
@ -100,6 +95,7 @@ import type { PropType } from 'vue'
|
|||
import type { IToken } from '../store/authtoken.ts'
|
||||
|
||||
import { mdiAndroid, mdiAppleIos, mdiAppleSafari, mdiCellphone, mdiCheck, mdiFirefox, mdiGoogleChrome, mdiKeyOutline, mdiMicrosoftEdge, mdiMonitor, mdiTablet, mdiWeb } from '@mdi/js'
|
||||
import { showConfirmation } from '@nextcloud/dialogs'
|
||||
import { translate as t } from '@nextcloud/l10n'
|
||||
import { defineComponent } from 'vue'
|
||||
import NcActionButton from '@nextcloud/vue/components/NcActionButton'
|
||||
|
|
@ -110,7 +106,6 @@ import NcDateTime from '@nextcloud/vue/components/NcDateTime'
|
|||
import NcIconSvgWrapper from '@nextcloud/vue/components/NcIconSvgWrapper'
|
||||
import NcTextField from '@nextcloud/vue/components/NcTextField'
|
||||
import AuthTokenDeleteDialog from './AuthTokenDeleteDialog.vue'
|
||||
import AuthTokenWipeDialog from './AuthTokenWipeDialog.vue'
|
||||
import { TokenType, useAuthTokenStore } from '../store/authtoken.ts'
|
||||
import { detect } from '../utils/userAgentDetect.ts'
|
||||
|
||||
|
|
@ -137,7 +132,6 @@ export default defineComponent({
|
|||
name: 'AuthToken',
|
||||
components: {
|
||||
AuthTokenDeleteDialog,
|
||||
AuthTokenWipeDialog,
|
||||
NcActions,
|
||||
NcActionButton,
|
||||
NcActionCheckbox,
|
||||
|
|
@ -166,7 +160,6 @@ export default defineComponent({
|
|||
newName: '',
|
||||
oldName: '',
|
||||
deleteDialogOpen: false,
|
||||
wipeDialogOpen: false,
|
||||
mdiCheck,
|
||||
TokenType,
|
||||
}
|
||||
|
|
@ -306,13 +299,18 @@ export default defineComponent({
|
|||
this.authTokenStore.renameToken(this.token, this.newName)
|
||||
},
|
||||
|
||||
wipe() {
|
||||
async wipe() {
|
||||
this.actionOpen = false
|
||||
this.wipeDialogOpen = true
|
||||
},
|
||||
|
||||
confirmWipe() {
|
||||
this.authTokenStore.wipeToken(this.token)
|
||||
const confirmed = await showConfirmation({
|
||||
name: t('settings', 'Confirm wipe'),
|
||||
text: t('settings', 'Do you really want to wipe your data from this device?'),
|
||||
labelConfirm: t('settings', 'Wipe device'),
|
||||
labelReject: t('settings', 'Cancel'),
|
||||
severity: 'warning',
|
||||
})
|
||||
if (confirmed) {
|
||||
this.authTokenStore.wipeToken(this.token)
|
||||
}
|
||||
},
|
||||
},
|
||||
})
|
||||
|
|
|
|||
|
|
@ -1,54 +0,0 @@
|
|||
<!--
|
||||
- SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors
|
||||
- SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
-->
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { IDialogButton } from '@nextcloud/dialogs'
|
||||
import type { IToken } from '../store/authtoken.ts'
|
||||
|
||||
import { translate as t } from '@nextcloud/l10n'
|
||||
import { computed } from 'vue'
|
||||
import NcDialog from '@nextcloud/vue/components/NcDialog'
|
||||
|
||||
defineProps<{
|
||||
/** The token to wipe. Kept for prop-shape parity with AuthTokenDeleteDialog. */
|
||||
token: IToken
|
||||
/** Whether the dialog is open */
|
||||
open: boolean
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
'update:open': [open: boolean]
|
||||
confirm: []
|
||||
}>()
|
||||
|
||||
const buttons = computed<IDialogButton[]>(() => [
|
||||
{
|
||||
label: t('settings', 'Cancel'),
|
||||
variant: 'tertiary',
|
||||
callback: () => emit('update:open', false),
|
||||
},
|
||||
{
|
||||
label: t('settings', 'Wipe device'),
|
||||
variant: 'error',
|
||||
callback: () => {
|
||||
emit('confirm')
|
||||
emit('update:open', false)
|
||||
},
|
||||
},
|
||||
])
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<NcDialog
|
||||
:open="open"
|
||||
:name="t('settings', 'Confirm wipe')"
|
||||
:buttons="buttons"
|
||||
size="normal"
|
||||
@update:open="emit('update:open', $event)">
|
||||
<p>
|
||||
{{ t('settings', 'Do you really want to wipe your data from this device?') }}
|
||||
</p>
|
||||
</NcDialog>
|
||||
</template>
|
||||
Loading…
Reference in a new issue