Skip to content

Commit 9d3ef6d

Browse files
refactor(vue3): migrate IdentificationFactors to script setup ts
Signed-off-by: Vitor Mattos <1079143+vitormattos@users.noreply.github.com>
1 parent bf0dcba commit 9d3ef6d

2 files changed

Lines changed: 205 additions & 79 deletions

File tree

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
/*
2+
* SPDX-FileCopyrightText: 2026 LibreSign contributors
3+
* SPDX-License-Identifier: AGPL-3.0-or-later
4+
*/
5+
6+
import { beforeEach, describe, expect, it, vi } from 'vitest'
7+
import { mount } from '@vue/test-utils'
8+
9+
import IdentificationFactors from '../../../views/Settings/IdentificationFactors.vue'
10+
11+
const useConfigureCheckStoreMock = vi.fn()
12+
13+
vi.mock('@nextcloud/l10n', () => ({
14+
t: vi.fn((_app: string, text: string) => text),
15+
translate: vi.fn((_app: string, text: string) => text),
16+
translatePlural: vi.fn((_app: string, singular: string, plural: string, count: number) => (count === 1 ? singular : plural)),
17+
n: vi.fn((_app: string, singular: string, plural: string, count: number) => (count === 1 ? singular : plural)),
18+
getLanguage: vi.fn(() => 'en'),
19+
getLocale: vi.fn(() => 'en'),
20+
isRTL: vi.fn(() => false),
21+
}))
22+
23+
vi.mock('../../../store/configureCheck.js', () => ({
24+
useConfigureCheckStore: (...args: unknown[]) => useConfigureCheckStoreMock(...args),
25+
}))
26+
27+
const OCP = {
28+
AppConfig: {
29+
setValue: vi.fn(),
30+
},
31+
}
32+
33+
;(globalThis as typeof globalThis & { OCP: typeof OCP }).OCP = OCP
34+
35+
describe('IdentificationFactors.vue', () => {
36+
beforeEach(() => {
37+
vi.clearAllMocks()
38+
useConfigureCheckStoreMock.mockReturnValue({
39+
isNoneEngine: false,
40+
identifyMethods: [
41+
{
42+
name: 'email',
43+
friendly_name: 'Email',
44+
enabled: true,
45+
can_create_account: true,
46+
signatureMethods: {
47+
sms: { enabled: false, label: 'SMS' },
48+
email: { enabled: true, label: 'Email' },
49+
},
50+
},
51+
{
52+
name: 'phone',
53+
friendly_name: 'Phone',
54+
enabled: false,
55+
signatureMethods: {
56+
sms: { enabled: true, label: 'SMS' },
57+
},
58+
},
59+
],
60+
})
61+
})
62+
63+
function createWrapper() {
64+
return mount(IdentificationFactors, {
65+
global: {
66+
stubs: {
67+
NcSettingsSection: { template: '<div><slot /></div>' },
68+
NcCheckboxRadioSwitch: { template: '<div><slot /></div>' },
69+
},
70+
},
71+
})
72+
}
73+
74+
it('initializes the selected signature method from the enabled option', () => {
75+
const wrapper = createWrapper()
76+
77+
expect(wrapper.vm.identifyMethods[0].signatureMethodEnabled).toBe('email')
78+
expect(wrapper.vm.identifyMethods[1].signatureMethodEnabled).toBe('sms')
79+
})
80+
81+
it('persists only enabled methods and removes display labels before saving', () => {
82+
const wrapper = createWrapper()
83+
84+
wrapper.vm.save()
85+
86+
expect(OCP.AppConfig.setValue).toHaveBeenCalledTimes(1)
87+
expect(OCP.AppConfig.setValue).toHaveBeenCalledWith(
88+
'libresign',
89+
'identify_methods',
90+
JSON.stringify([
91+
{
92+
name: 'email',
93+
friendly_name: 'Email',
94+
enabled: true,
95+
can_create_account: true,
96+
signatureMethods: {
97+
sms: { enabled: false },
98+
email: { enabled: true },
99+
},
100+
signatureMethodEnabled: 'email',
101+
},
102+
]),
103+
)
104+
})
105+
})

src/views/Settings/IdentificationFactors.vue

Lines changed: 100 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -43,94 +43,115 @@
4343
</div>
4444
</NcSettingsSection>
4545
</template>
46-
<script>
46+
<script setup lang="ts">
4747
import NcCheckboxRadioSwitch from '@nextcloud/vue/components/NcCheckboxRadioSwitch'
4848
import NcSettingsSection from '@nextcloud/vue/components/NcSettingsSection'
4949
import { t } from '@nextcloud/l10n'
50+
import { computed, onMounted, watch } from 'vue'
5051
5152
import { useConfigureCheckStore } from '../../store/configureCheck.js'
5253
53-
export default {
54+
defineOptions({
5455
name: 'IdentificationFactors',
55-
components: {
56-
NcCheckboxRadioSwitch,
57-
NcSettingsSection,
58-
},
59-
setup() {
60-
const configureCheckStore = useConfigureCheckStore()
61-
return { t, configureCheckStore }
62-
},
63-
computed: {
64-
description() {
65-
// TRANSLATORS Name of a section at "Administration Settings" of LibreSign that an admin can configure the ways that a person will be identified when accessing the link to sign a document.
66-
return t('libresign', 'Ways to identify a person who will sign a document.')
67-
},
68-
isNoneEngine() {
69-
return this.configureCheckStore.isNoneEngine
70-
},
71-
identifyMethods() {
72-
return this.configureCheckStore.identifyMethods
73-
},
74-
},
75-
mounted() {
76-
this.updateSignatureMethodsEnabled()
77-
},
78-
watch: {
79-
identifyMethods: {
80-
handler() {
81-
this.updateSignatureMethodsEnabled()
82-
},
83-
deep: true,
84-
},
85-
},
86-
methods: {
87-
updateSignatureMethodsEnabled() {
88-
this.identifyMethods.forEach((identifyMethod) => {
89-
if (!Object.hasOwn(identifyMethod, 'signatureMethodEnabled')) {
90-
identifyMethod.signatureMethodEnabled = ''
91-
}
92-
if (identifyMethod.signatureMethodEnabled.length === 0) {
93-
const signatureMethodEnabled = Object.keys(identifyMethod.signatureMethods)
94-
.reduce((signatureMethodEnabled, signatureMethodName) => {
95-
if (signatureMethodEnabled.length === 0 && identifyMethod.signatureMethods[signatureMethodName].enabled) {
96-
signatureMethodEnabled = signatureMethodName
97-
}
98-
return signatureMethodEnabled
99-
}, identifyMethod.signatureMethodEnabled)
100-
if (signatureMethodEnabled.length > 0) {
101-
identifyMethod.signatureMethodEnabled = signatureMethodEnabled
102-
} else {
103-
identifyMethod.signatureMethodEnabled = Object.keys(identifyMethod.signatureMethods)[0]
56+
})
57+
58+
type SignatureMethod = {
59+
enabled: boolean
60+
label?: string
61+
}
62+
63+
type IdentifyMethod = {
64+
name: string
65+
friendly_name: string
66+
enabled: boolean
67+
can_create_account?: boolean
68+
mandatory?: boolean
69+
signatureMethods: Record<string, SignatureMethod>
70+
signatureMethodEnabled?: string
71+
}
72+
73+
type ConfigureCheckStore = {
74+
isNoneEngine: boolean
75+
identifyMethods: IdentifyMethod[]
76+
}
77+
78+
type AppConfigGlobal = {
79+
AppConfig: {
80+
setValue: (app: string, key: string, value: string) => void
81+
}
82+
}
83+
84+
const configureCheckStore = useConfigureCheckStore() as ConfigureCheckStore
85+
86+
const description = computed(() => {
87+
// TRANSLATORS Name of a section at "Administration Settings" of LibreSign that an admin can configure the ways that a person will be identified when accessing the link to sign a document.
88+
return t('libresign', 'Ways to identify a person who will sign a document.')
89+
})
90+
91+
const isNoneEngine = computed(() => configureCheckStore.isNoneEngine)
92+
const identifyMethods = computed(() => configureCheckStore.identifyMethods)
93+
94+
function updateSignatureMethodsEnabled() {
95+
identifyMethods.value.forEach((identifyMethod) => {
96+
if (!Object.hasOwn(identifyMethod, 'signatureMethodEnabled')) {
97+
identifyMethod.signatureMethodEnabled = ''
98+
}
99+
100+
if (identifyMethod.signatureMethodEnabled.length === 0) {
101+
const selectedSignatureMethod = Object.keys(identifyMethod.signatureMethods)
102+
.reduce((currentSelection, signatureMethodName) => {
103+
if (currentSelection.length === 0 && identifyMethod.signatureMethods[signatureMethodName].enabled) {
104+
return signatureMethodName
104105
}
105-
}
106-
Object.keys(identifyMethod.signatureMethods).forEach(signatureMethodName => {
107-
identifyMethod.signatureMethods[signatureMethodName].enabled
108-
= identifyMethod.signatureMethodEnabled === signatureMethodName
109-
})
110-
})
111-
},
112-
save() {
113-
this.updateSignatureMethodsEnabled()
114-
// Get only enabled
115-
let props = this.identifyMethods.filter(identifyMethod => identifyMethod.enabled)
116-
// Remove label from signature method, we don't need to save this
117-
props = JSON.parse(JSON.stringify(props))
118-
.map(identifyMethod => {
119-
Object.keys(identifyMethod.signatureMethods).forEach(id => {
120-
Object.keys(identifyMethod.signatureMethods[id]).forEach(signatureMethdoPropName => {
121-
if (signatureMethdoPropName === 'label') {
122-
delete identifyMethod.signatureMethods[id][signatureMethdoPropName]
123-
}
124-
})
125-
})
126-
return identifyMethod
127-
})
128-
OCP.AppConfig.setValue('libresign', 'identify_methods',
129-
JSON.stringify(props),
130-
)
131-
},
132-
},
106+
return currentSelection
107+
}, identifyMethod.signatureMethodEnabled)
108+
109+
identifyMethod.signatureMethodEnabled = selectedSignatureMethod.length > 0
110+
? selectedSignatureMethod
111+
: Object.keys(identifyMethod.signatureMethods)[0]
112+
}
113+
114+
Object.keys(identifyMethod.signatureMethods).forEach((signatureMethodName) => {
115+
identifyMethod.signatureMethods[signatureMethodName].enabled = identifyMethod.signatureMethodEnabled === signatureMethodName
116+
})
117+
})
133118
}
119+
120+
function save() {
121+
updateSignatureMethodsEnabled()
122+
123+
const props = JSON.parse(JSON.stringify(
124+
identifyMethods.value.filter((identifyMethod) => identifyMethod.enabled),
125+
)) as IdentifyMethod[]
126+
127+
props.forEach((identifyMethod) => {
128+
Object.keys(identifyMethod.signatureMethods).forEach((id) => {
129+
delete identifyMethod.signatureMethods[id].label
130+
})
131+
})
132+
133+
;(globalThis as typeof globalThis & { OCP: AppConfigGlobal }).OCP.AppConfig.setValue(
134+
'libresign',
135+
'identify_methods',
136+
JSON.stringify(props),
137+
)
138+
}
139+
140+
onMounted(() => {
141+
updateSignatureMethodsEnabled()
142+
})
143+
144+
watch(identifyMethods, () => {
145+
updateSignatureMethodsEnabled()
146+
}, { deep: true })
147+
148+
defineExpose({
149+
description,
150+
isNoneEngine,
151+
identifyMethods,
152+
updateSignatureMethodsEnabled,
153+
save,
154+
})
134155
</script>
135156
<style lang="scss" scoped>
136157
.settings-section{

0 commit comments

Comments
 (0)