Files
ackify/webapp/tests/components/SignButton.test.ts
Benjamin 41e18c914f test(frontend): add comprehensive unit tests for stores, services and components
Add business logic tests to improve frontend code coverage and reliability:
- Pinia stores: auth, signatures, ui (87 tests)
- Services: checksumCalculator (19 tests)
- Components: SignButton, NotificationToast (21 tests)

Focus on critical business flows: authentication, signature management,
notification system, and file validation. All tests passing (143 total).
2025-11-24 01:04:41 +01:00

299 lines
7.9 KiB
TypeScript

// SPDX-License-Identifier: AGPL-3.0-or-later
import { describe, it, expect, beforeEach, vi } from 'vitest'
import { mount, flushPromises, VueWrapper } from '@vue/test-utils'
import { createPinia, setActivePinia } from 'pinia'
import SignButton from '@/components/SignButton.vue'
import { useAuthStore } from '@/stores/auth'
import { useSignatureStore } from '@/stores/signatures'
import { createI18n } from 'vue-i18n'
vi.mock('@/services/http')
const i18n = createI18n({
legacy: false,
locale: 'en',
messages: {
en: {
signButton: {
signing: 'Signing...',
confirmAction: 'Sign Document',
confirmed: 'Signed',
on: 'on',
error: {
missingDocId: 'Missing document ID',
authFailed: 'Authentication failed'
}
}
}
}
})
describe('SignButton Component', () => {
beforeEach(() => {
setActivePinia(createPinia())
vi.clearAllMocks()
delete (window as any).location
;(window as any).location = {
href: '',
pathname: '/document/123',
search: '?test=true'
}
})
const mountComponent = (props = {}): VueWrapper => {
return mount(SignButton, {
props,
global: {
plugins: [i18n]
}
})
}
describe('Business logic - Signature state', () => {
it('should detect when current user has signed', async () => {
const authStore = useAuthStore()
authStore.initialized = true
authStore.setUser({
id: 'user-123',
email: 'test@example.com',
name: 'Test User',
isAdmin: false
})
const wrapper = mountComponent({
docId: 'doc-123',
signatures: [
{
userEmail: 'test@example.com',
signedAt: '2024-01-15T10:00:00Z'
}
]
})
await flushPromises()
// Should show signed status (no button visible)
expect(wrapper.find('button').exists()).toBe(false)
expect(wrapper.find('.signed-status').exists()).toBe(true)
})
it('should not show signed status when different user has signed', async () => {
const authStore = useAuthStore()
authStore.setUser({
id: 'user-123',
email: 'test@example.com',
name: 'Test User',
isAdmin: false
})
const wrapper = mountComponent({
docId: 'doc-123',
signatures: [
{
userEmail: 'other@example.com',
signedAt: '2024-01-15T10:00:00Z'
}
]
})
await flushPromises()
// Should show button since user hasn't signed
expect(wrapper.find('button').exists()).toBe(true)
})
it('should detect signature change after successful sign action', async () => {
const authStore = useAuthStore()
const signatureStore = useSignatureStore()
authStore.initialized = true
authStore.setUser({
id: 'user-123',
email: 'test@example.com',
name: 'Test User',
isAdmin: false
})
vi.spyOn(signatureStore, 'createSignature').mockResolvedValueOnce({
id: 1,
docId: 'doc-123',
userSub: 'user-123',
userEmail: 'test@example.com',
signedAt: '2024-01-15T10:00:00Z',
payloadHash: 'hash123',
signature: 'sig123',
nonce: 'nonce123',
createdAt: '2024-01-15T10:00:00Z'
})
const wrapper = mountComponent({
docId: 'doc-123',
signatures: []
})
await flushPromises()
expect(wrapper.find('button').exists()).toBe(true)
// Sign the document
await wrapper.find('button').trigger('click')
await flushPromises()
// After signing, button should disappear (isSigned becomes true)
expect(wrapper.find('button').exists()).toBe(false)
})
})
describe('Business logic - Signature creation', () => {
it('should create signature when user is authenticated', async () => {
const authStore = useAuthStore()
const signatureStore = useSignatureStore()
authStore.initialized = true
authStore.setUser({
id: 'user-123',
email: 'test@example.com',
name: 'Test User',
isAdmin: false
})
const createSignatureSpy = vi.spyOn(signatureStore, 'createSignature').mockResolvedValueOnce({
id: 1,
docId: 'doc-123',
userSub: 'user-123',
userEmail: 'test@example.com',
userName: 'Test User',
signedAt: '2024-01-15T10:00:00Z',
payloadHash: 'hash123',
signature: 'sig123',
nonce: 'nonce123',
createdAt: '2024-01-15T10:00:00Z'
})
const wrapper = mountComponent({
docId: 'doc-123',
referer: 'https://example.com',
signatures: []
})
await wrapper.find('button').trigger('click')
await flushPromises()
expect(createSignatureSpy).toHaveBeenCalledWith({
docId: 'doc-123',
referer: 'https://example.com'
})
})
it('should emit signed event on successful signature', async () => {
const authStore = useAuthStore()
const signatureStore = useSignatureStore()
authStore.initialized = true
authStore.setUser({
id: 'user-123',
email: 'test@example.com',
name: 'Test User',
isAdmin: false
})
vi.spyOn(signatureStore, 'createSignature').mockResolvedValueOnce({
id: 1,
docId: 'doc-123',
userSub: 'user-123',
userEmail: 'test@example.com',
signedAt: '2024-01-15T10:00:00Z',
payloadHash: 'hash123',
signature: 'sig123',
nonce: 'nonce123',
createdAt: '2024-01-15T10:00:00Z'
})
const wrapper = mountComponent({
docId: 'doc-123',
signatures: []
})
await wrapper.find('button').trigger('click')
await flushPromises()
expect(wrapper.emitted('signed')).toBeTruthy()
expect(wrapper.emitted('signed')?.[0]).toEqual(['doc-123'])
})
it('should emit error event on signature creation failure', async () => {
const authStore = useAuthStore()
const signatureStore = useSignatureStore()
authStore.initialized = true
authStore.setUser({
id: 'user-123',
email: 'test@example.com',
name: 'Test User',
isAdmin: false
})
vi.spyOn(signatureStore, 'createSignature').mockRejectedValueOnce({
response: {
data: {
error: {
message: 'You have already signed this document'
}
}
}
})
const wrapper = mountComponent({
docId: 'doc-123',
signatures: []
})
await wrapper.find('button').trigger('click')
await flushPromises()
expect(wrapper.emitted('error')).toBeTruthy()
expect(wrapper.emitted('error')?.[0]).toEqual(['You have already signed this document'])
})
})
describe('Business logic - Authentication requirement', () => {
it('should redirect to OAuth login when not authenticated', async () => {
const authStore = useAuthStore()
authStore.initialized = true
const startOAuthLoginSpy = vi.spyOn(authStore, 'startOAuthLogin').mockResolvedValueOnce()
const wrapper = mountComponent({
docId: 'doc-123',
signatures: []
})
await wrapper.find('button').trigger('click')
await flushPromises()
expect(startOAuthLoginSpy).toHaveBeenCalledWith('/document/123?test=true')
})
})
describe('Button state', () => {
it('should disable button when no docId provided', () => {
const wrapper = mountComponent({
signatures: []
})
const button = wrapper.find('button')
expect(button.attributes('disabled')).toBeDefined()
})
it('should disable button when disabled prop is true', () => {
const wrapper = mountComponent({
docId: 'doc-123',
disabled: true,
signatures: []
})
const button = wrapper.find('button')
expect(button.attributes('disabled')).toBeDefined()
})
})
})