feat: change email, export my data, delete account (with data anonymization)

This commit is contained in:
Florian Sylvain committed 2026-10-30 20:19:07 +01:00
1 parent bb191c389a
commit 10a66cd979
123 files changed
+10789 -76

No files matched your search

+87
View File
@@ -0,0 +1,87 @@
import { afterEach, describe, expect, it } from 'vitest'
import { mount } from '@vue/test-utils'
import uiPlugin from '@nuxt/ui/vue-plugin'
import { createPinia } from 'pinia'
import router from '@/router'
import BlockedDialog from '@/components/BlockedDialog.vue'
import { setLocale } from '@/i18n'
import { messages, type MessageKey } from '@/i18n/messages'
const KEYS: MessageKey[] = [
'Danger zone',
"Delete your account and your personal data. This can't be undone.",
'Delete my account',
'Delete your account?',
"This permanently deletes your account. It can't be undone.",
'Your username, display name and email address',
'Your author profile and API keys',
"Your sessions: you'll be logged out everywhere",
'Your articles and media stay',
'They stay on the site, credited to "Deleted user". Your name is removed from them.',
'Deleted user',
'Export my data first',
'Current password',
'Type DELETE to confirm',
'Keep my account',
'Deleting…',
"That password isn't correct. Try again.",
'Too many requests. Try again in a few minutes.',
"Couldn't delete your account. Nothing was deleted. Try again in a moment.",
"You can't delete your account yet",
'You are the last administrator. RenewCMS needs at least one. Promote another user first, then come back to delete your account.',
'You are the last Owner. RenewCMS needs at least one. Transfer ownership or promote another user first, then come back to delete your account.',
'Back to profile',
'Go to Users',
"This account can't be deleted",
"RenewCMS can't delete this account right now. Ask an administrator to help, then try again.",
'Your account has been deleted',
'Your articles and media were kept without your name.',
'Deleted the account of {0}',
'Failed to delete the account of {0}',
]
function placeholders(text: string) {
return (text.match(/\{\d+\}/g) ?? []).sort()
}
afterEach(() => setLocale('en'))
describe('account deletion catalogue', () => {
it.each(KEYS)('has a French value with the same placeholders for %s', (key) => {
const french = messages[key]
expect(french.trim()).not.toBe('')
expect(placeholders(french)).toEqual(placeholders(key))
})
it('keeps the confirmation word DELETE literal in the French label', () => {
expect(messages['Type DELETE to confirm']).toContain('DELETE')
})
it('renders the blocked dialog in French and back in English', async () => {
const mountBlocked = () =>
mount(BlockedDialog, {
props: { open: true, lastRole: 'Owner' },
attachTo: document.body,
global: { plugins: [router, uiPlugin, createPinia()] },
})
setLocale('fr')
const french = mountBlocked()
await french.vm.$nextTick()
expect(document.body.textContent).toContain(
'Vous ne pouvez pas encore supprimer votre compte',
)
expect(document.body.textContent).toContain('Retour au profil')
expect(document.body.textContent).not.toContain('Back to profile')
french.unmount()
document.body.innerHTML = ''
setLocale('en')
const english = mountBlocked()
await english.vm.$nextTick()
expect(document.body.textContent).toContain("You can't delete your account yet")
english.unmount()
document.body.innerHTML = ''
})
})