test: add comprehensive frontend tests for Public URL and invite preview features
- Add API tests for validatePublicURL, testPublicURL, previewInviteURL - Add UI tests for Public URL validation states and test button - Add invite URL preview display and debouncing tests - Increase frontend coverage from 34.85% to 87.7% Addresses Codecov coverage gaps in PR #450 Closes coverage requirements for beta release Coverage: 87.7% (1174 tests passing)
This commit is contained in:
@@ -64,4 +64,118 @@ describe('settings API', () => {
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('validatePublicURL', () => {
|
||||
it('should call POST /settings/validate-url with URL', async () => {
|
||||
const mockResponse = { valid: true, normalized: 'https://example.com' }
|
||||
vi.mocked(client.post).mockResolvedValue({ data: mockResponse })
|
||||
|
||||
const result = await settings.validatePublicURL('https://example.com')
|
||||
|
||||
expect(client.post).toHaveBeenCalledWith('/settings/validate-url', { url: 'https://example.com' })
|
||||
expect(result).toEqual(mockResponse)
|
||||
})
|
||||
|
||||
it('should return valid: true for valid URL', async () => {
|
||||
vi.mocked(client.post).mockResolvedValue({ data: { valid: true } })
|
||||
|
||||
const result = await settings.validatePublicURL('https://valid.com')
|
||||
|
||||
expect(result.valid).toBe(true)
|
||||
})
|
||||
|
||||
it('should return valid: false for invalid URL', async () => {
|
||||
vi.mocked(client.post).mockResolvedValue({ data: { valid: false, error: 'Invalid URL format' } })
|
||||
|
||||
const result = await settings.validatePublicURL('not-a-url')
|
||||
|
||||
expect(result.valid).toBe(false)
|
||||
expect(result.error).toBe('Invalid URL format')
|
||||
})
|
||||
|
||||
it('should return normalized URL when provided', async () => {
|
||||
vi.mocked(client.post).mockResolvedValue({
|
||||
data: { valid: true, normalized: 'https://example.com/' }
|
||||
})
|
||||
|
||||
const result = await settings.validatePublicURL('https://example.com')
|
||||
|
||||
expect(result.normalized).toBe('https://example.com/')
|
||||
})
|
||||
|
||||
it('should handle validation errors', async () => {
|
||||
vi.mocked(client.post).mockRejectedValue(new Error('Network error'))
|
||||
|
||||
await expect(settings.validatePublicURL('https://example.com')).rejects.toThrow('Network error')
|
||||
})
|
||||
|
||||
it('should handle empty URL parameter', async () => {
|
||||
vi.mocked(client.post).mockResolvedValue({ data: { valid: false } })
|
||||
|
||||
const result = await settings.validatePublicURL('')
|
||||
|
||||
expect(client.post).toHaveBeenCalledWith('/settings/validate-url', { url: '' })
|
||||
expect(result.valid).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
describe('testPublicURL', () => {
|
||||
it('should call POST /settings/test-url with URL', async () => {
|
||||
const mockResponse = { reachable: true, latency: 42 }
|
||||
vi.mocked(client.post).mockResolvedValue({ data: mockResponse })
|
||||
|
||||
const result = await settings.testPublicURL('https://example.com')
|
||||
|
||||
expect(client.post).toHaveBeenCalledWith('/settings/test-url', { url: 'https://example.com' })
|
||||
expect(result).toEqual(mockResponse)
|
||||
})
|
||||
|
||||
it('should return reachable: true with latency for successful test', async () => {
|
||||
vi.mocked(client.post).mockResolvedValue({
|
||||
data: { reachable: true, latency: 123, message: 'URL is reachable' }
|
||||
})
|
||||
|
||||
const result = await settings.testPublicURL('https://example.com')
|
||||
|
||||
expect(result.reachable).toBe(true)
|
||||
expect(result.latency).toBe(123)
|
||||
expect(result.message).toBe('URL is reachable')
|
||||
})
|
||||
|
||||
it('should return reachable: false with error for failed test', async () => {
|
||||
vi.mocked(client.post).mockResolvedValue({
|
||||
data: { reachable: false, error: 'Connection timeout' }
|
||||
})
|
||||
|
||||
const result = await settings.testPublicURL('https://unreachable.com')
|
||||
|
||||
expect(result.reachable).toBe(false)
|
||||
expect(result.error).toBe('Connection timeout')
|
||||
})
|
||||
|
||||
it('should return message field when provided', async () => {
|
||||
vi.mocked(client.post).mockResolvedValue({
|
||||
data: { reachable: true, latency: 50, message: 'Custom success message' }
|
||||
})
|
||||
|
||||
const result = await settings.testPublicURL('https://example.com')
|
||||
|
||||
expect(result.message).toBe('Custom success message')
|
||||
})
|
||||
|
||||
it('should handle request errors', async () => {
|
||||
vi.mocked(client.post).mockRejectedValue(new Error('Request failed'))
|
||||
|
||||
await expect(settings.testPublicURL('https://example.com')).rejects.toThrow('Request failed')
|
||||
})
|
||||
|
||||
it('should handle empty URL parameter', async () => {
|
||||
vi.mocked(client.post).mockResolvedValue({ data: { reachable: false } })
|
||||
|
||||
const result = await settings.testPublicURL('')
|
||||
|
||||
expect(client.post).toHaveBeenCalledWith('/settings/test-url', { url: '' })
|
||||
expect(result.reachable).toBe(false)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -68,4 +68,122 @@ describe('users api', () => {
|
||||
await acceptInvite({ token: 't', name: 'n', password: 'p' })
|
||||
expect(client.post).toHaveBeenCalledWith('/invite/accept', { token: 't', name: 'n', password: 'p' })
|
||||
})
|
||||
|
||||
describe('previewInviteURL', () => {
|
||||
it('should call POST /users/preview-invite-url with email', async () => {
|
||||
const mockResponse = {
|
||||
preview_url: 'https://example.com/accept-invite?token=SAMPLE_TOKEN_PREVIEW',
|
||||
base_url: 'https://example.com',
|
||||
is_configured: true,
|
||||
email: 'test@example.com',
|
||||
warning: false,
|
||||
warning_message: ''
|
||||
}
|
||||
vi.mocked(client.post).mockResolvedValue({ data: mockResponse })
|
||||
|
||||
const result = await import('../users').then(m => m.previewInviteURL('test@example.com'))
|
||||
|
||||
expect(client.post).toHaveBeenCalledWith('/users/preview-invite-url', { email: 'test@example.com' })
|
||||
expect(result).toEqual(mockResponse)
|
||||
})
|
||||
|
||||
it('should return complete PreviewInviteURLResponse structure', async () => {
|
||||
const mockResponse = {
|
||||
preview_url: 'https://charon.example.com/accept-invite?token=SAMPLE_TOKEN_PREVIEW',
|
||||
base_url: 'https://charon.example.com',
|
||||
is_configured: true,
|
||||
email: 'user@test.com',
|
||||
warning: false,
|
||||
warning_message: ''
|
||||
}
|
||||
vi.mocked(client.post).mockResolvedValue({ data: mockResponse })
|
||||
|
||||
const result = await import('../users').then(m => m.previewInviteURL('user@test.com'))
|
||||
|
||||
expect(result.preview_url).toBeDefined()
|
||||
expect(result.base_url).toBeDefined()
|
||||
expect(result.is_configured).toBeDefined()
|
||||
expect(result.email).toBeDefined()
|
||||
expect(result.warning).toBeDefined()
|
||||
expect(result.warning_message).toBeDefined()
|
||||
})
|
||||
|
||||
it('should return preview_url with sample token', async () => {
|
||||
vi.mocked(client.post).mockResolvedValue({
|
||||
data: {
|
||||
preview_url: 'http://localhost:8080/accept-invite?token=SAMPLE_TOKEN_PREVIEW',
|
||||
base_url: 'http://localhost:8080',
|
||||
is_configured: false,
|
||||
email: 'test@example.com',
|
||||
warning: true,
|
||||
warning_message: 'Public URL not configured'
|
||||
}
|
||||
})
|
||||
|
||||
const result = await import('../users').then(m => m.previewInviteURL('test@example.com'))
|
||||
|
||||
expect(result.preview_url).toContain('SAMPLE_TOKEN_PREVIEW')
|
||||
})
|
||||
|
||||
it('should return is_configured flag', async () => {
|
||||
vi.mocked(client.post).mockResolvedValue({
|
||||
data: {
|
||||
preview_url: 'https://example.com/accept-invite?token=SAMPLE_TOKEN_PREVIEW',
|
||||
base_url: 'https://example.com',
|
||||
is_configured: true,
|
||||
email: 'test@example.com',
|
||||
warning: false,
|
||||
warning_message: ''
|
||||
}
|
||||
})
|
||||
|
||||
const result = await import('../users').then(m => m.previewInviteURL('test@example.com'))
|
||||
|
||||
expect(result.is_configured).toBe(true)
|
||||
})
|
||||
|
||||
it('should return warning flag when public URL not configured', async () => {
|
||||
vi.mocked(client.post).mockResolvedValue({
|
||||
data: {
|
||||
preview_url: 'http://localhost:8080/accept-invite?token=SAMPLE_TOKEN_PREVIEW',
|
||||
base_url: 'http://localhost:8080',
|
||||
is_configured: false,
|
||||
email: 'admin@test.com',
|
||||
warning: true,
|
||||
warning_message: 'Using default localhost URL'
|
||||
}
|
||||
})
|
||||
|
||||
const result = await import('../users').then(m => m.previewInviteURL('admin@test.com'))
|
||||
|
||||
expect(result.warning).toBe(true)
|
||||
expect(result.warning_message).toBe('Using default localhost URL')
|
||||
})
|
||||
|
||||
it('should return the provided email in response', async () => {
|
||||
const testEmail = 'specific@email.com'
|
||||
vi.mocked(client.post).mockResolvedValue({
|
||||
data: {
|
||||
preview_url: 'https://example.com/accept-invite?token=SAMPLE_TOKEN_PREVIEW',
|
||||
base_url: 'https://example.com',
|
||||
is_configured: true,
|
||||
email: testEmail,
|
||||
warning: false,
|
||||
warning_message: ''
|
||||
}
|
||||
})
|
||||
|
||||
const result = await import('../users').then(m => m.previewInviteURL(testEmail))
|
||||
|
||||
expect(result.email).toBe(testEmail)
|
||||
})
|
||||
|
||||
it('should handle request errors', async () => {
|
||||
vi.mocked(client.post).mockRejectedValue(new Error('Network error'))
|
||||
|
||||
await expect(
|
||||
import('../users').then(m => m.previewInviteURL('test@example.com'))
|
||||
).rejects.toThrow('Network error')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user