feat(marketing): add contact form submission and testing setup
- Add contact API endpoint for demo requests and sales enquiries - Implement functional contact forms on Demo and Pricing pages with honeypot spam protection - Update footer layout: remove Company section, add contact email - Update self-hosted FAQ to mention open source with GitHub links - Add vitest and testing-library dependencies - Add tests for MarketingLayout and PricingPage components - Remove placeholder external-auth test file
This commit is contained in:
@@ -0,0 +1,76 @@
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import MarketingLayout from '../src/components/layouts/MarketingLayout';
|
||||
import { expect, test, describe } from 'bun:test';
|
||||
|
||||
describe('MarketingLayout', () => {
|
||||
test('renders children correctly', () => {
|
||||
render(<MarketingLayout><div>Test Child</div></MarketingLayout>);
|
||||
expect(screen.getByText('Test Child')).toBeDefined();
|
||||
});
|
||||
|
||||
test('removes "Company" section from footer', () => {
|
||||
render(<MarketingLayout><div>Test Child</div></MarketingLayout>);
|
||||
expect(screen.queryByText('Company')).toBeNull();
|
||||
});
|
||||
|
||||
test('adds contact email info@secuird.tech with mailto: link in Brand section', () => {
|
||||
render(<MarketingLayout><div>Test Child</div></MarketingLayout>);
|
||||
const emailLink = screen.getByText('info@secuird.tech');
|
||||
expect(emailLink).toBeDefined();
|
||||
expect(emailLink.closest('a')).toHaveAttribute('href', 'mailto:info@secuird.tech');
|
||||
});
|
||||
|
||||
test('adjusts grid from 5-col to 4-col layout', () => {
|
||||
render(<MarketingLayout><div>Test Child</div></MarketingLayout>);
|
||||
// This test assumes that the grid layout is reflected in the rendered HTML,
|
||||
// for example, by a class name like 'grid-cols-4' or similar.
|
||||
// You might need to adjust this based on the actual implementation.
|
||||
const footer = screen.getByRole('contentinfo'); // Assuming footer has role 'contentinfo'
|
||||
expect(footer).toHaveProperty('className'); // Check if className exists
|
||||
expect(footer.className).toMatch(/grid-cols-4/);
|
||||
expect(footer.className).not.toMatch(/grid-cols-5/);
|
||||
});
|
||||
});
|
||||
|
||||
test('removes "Company" section from footer', () => {
|
||||
render(<BrowserRouter><MarketingLayout><div>Test Child</div></MarketingLayout></BrowserRouter>);
|
||||
expect(screen.queryByText('Company')).toBeNull();
|
||||
});
|
||||
|
||||
test('adds contact email info@secuird.tech with mailto: link in Brand section', () => {
|
||||
render(<BrowserRouter><MarketingLayout><div>Test Child</div></MarketingLayout></BrowserRouter>);
|
||||
const emailLink = screen.getByText('info@secuird.tech');
|
||||
expect(emailLink).toBeDefined();
|
||||
expect(emailLink.getAttribute('href')).toBe('mailto:info@secuird.tech');
|
||||
});
|
||||
|
||||
test('adjusts grid from 5-col to 4-col layout', () => {
|
||||
render(<BrowserRouter><MarketingLayout><div>Test Child</div></MarketingLayout></BrowserRouter>);
|
||||
const footer = screen.getByRole('contentinfo');
|
||||
expect(footer.className).toMatch(/grid-cols-4/);
|
||||
expect(footer.className).not.toMatch(/grid-cols-5/);
|
||||
});
|
||||
});
|
||||
|
||||
test('removes "Company" section from footer', () => {
|
||||
render(<BrowserRouter><MarketingLayout><div>Test Child</div></MarketingLayout></BrowserRouter>);
|
||||
expect(screen.queryByText('Company')).toBeNull();
|
||||
});
|
||||
|
||||
test('adds contact email info@secuird.tech with mailto: link in Brand section', () => {
|
||||
render(<BrowserRouter><MarketingLayout><div>Test Child</div></MarketingLayout></BrowserRouter>);
|
||||
const emailLink = screen.getByText('info@secuird.tech');
|
||||
expect(emailLink).toBeDefined();
|
||||
expect(emailLink.closest('a').getAttribute('href')).toBe('mailto:info@secuird.tech');
|
||||
});
|
||||
|
||||
test('adjusts grid from 5-col to 4-col layout', () => {
|
||||
render(<BrowserRouter><MarketingLayout><div>Test Child</div></MarketingLayout></BrowserRouter>);
|
||||
// This test assumes that the grid layout is reflected in the rendered HTML,
|
||||
// for example, by a class name like 'grid-cols-4' or similar.
|
||||
// You might need to adjust this based on the actual implementation.
|
||||
const footer = screen.getByRole('contentinfo'); // Assuming footer has role 'contentinfo'
|
||||
expect(footer.className).toMatch(/grid-cols-4/);
|
||||
expect(footer.className).not.toMatch(/grid-cols-5/);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,22 @@
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import PricingPage from '../src/pages/marketing/PricingPage';
|
||||
import { expect, test, describe } from 'bun:test';
|
||||
|
||||
describe('PricingPage', () => {
|
||||
test('Self-hosted FAQ answer updated to mention open source with GitHub links', () => {
|
||||
render(<PricingPage />);
|
||||
const selfHostedText = screen.getByText(/self-hosted/i);
|
||||
expect(selfHostedText).toBeDefined();
|
||||
|
||||
const openSourceText = screen.getByText(/open source/i);
|
||||
expect(openSourceText).toBeDefined();
|
||||
|
||||
const gatehouseUiLink = screen.getByRole('link', { name: 'gatehouse-ui' });
|
||||
expect(gatehouseUiLink).toBeDefined();
|
||||
expect(gatehouseUiLink).toHaveAttribute('href', 'https://github.com/gatehouse/gatehouse-ui');
|
||||
|
||||
const gatehouseApiLink = screen.getByRole('link', { name: 'gatehouse-api' });
|
||||
expect(gatehouseApiLink).toBeDefined();
|
||||
expect(gatehouseApiLink).toHaveAttribute('href', 'https://github.com/gatehouse/gatehouse-api');
|
||||
});
|
||||
});
|
||||
@@ -1,266 +0,0 @@
|
||||
/**
|
||||
* Frontend tests for external authentication components
|
||||
* Tests Google OAuth login button, Linked Accounts page, and OAuth flows
|
||||
*/
|
||||
|
||||
import { describe, it, expect, beforeEach, vi, afterEach } from 'vitest';
|
||||
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import React from 'react';
|
||||
import { BrowserRouter } from 'react-router-dom';
|
||||
|
||||
// Note: These are component tests for the external auth UI
|
||||
// In a real project, you would use @testing-library/react and mock the API
|
||||
|
||||
describe('External Auth UI Components', () => {
|
||||
describe('Google OAuth Button', () => {
|
||||
it('should render Google login button with correct icon', () => {
|
||||
// This test verifies the Google OAuth button is rendered
|
||||
// In a real test, you would render the LoginPage and check for the button
|
||||
expect(true).toBe(true);
|
||||
});
|
||||
|
||||
it('should handle Google login click event', async () => {
|
||||
// Test that clicking Google login triggers the OAuth flow
|
||||
expect(true).toBe(true);
|
||||
});
|
||||
|
||||
it('should show loading state during authentication', () => {
|
||||
// Test loading state while OAuth flow is in progress
|
||||
expect(true).toBe(true);
|
||||
});
|
||||
|
||||
it('should handle authentication errors gracefully', async () => {
|
||||
// Test error handling for OAuth failures
|
||||
expect(true).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Linked Accounts Page', () => {
|
||||
it('should render linked accounts list', () => {
|
||||
// Test that LinkedAccountsPage renders the list of linked accounts
|
||||
expect(true).toBe(true);
|
||||
});
|
||||
|
||||
it('should show connected status for linked providers', () => {
|
||||
// Test that connected providers show "Connected" status
|
||||
expect(true).toBe(true);
|
||||
});
|
||||
|
||||
it('should show "Not connected" for unlinked providers', () => {
|
||||
// Test that unlinked providers show "Not connected" status
|
||||
expect(true).toBe(true);
|
||||
});
|
||||
|
||||
it('should disable unlink button when only one auth method', () => {
|
||||
// Test that unlink is disabled when it's the last auth method
|
||||
expect(true).toBe(true);
|
||||
});
|
||||
|
||||
it('should handle unlink confirmation', async () => {
|
||||
// Test unlink confirmation dialog
|
||||
expect(true).toBe(true);
|
||||
});
|
||||
|
||||
it('should handle unlink success', async () => {
|
||||
// Test unlink success feedback
|
||||
expect(true).toBe(true);
|
||||
});
|
||||
|
||||
it('should handle unlink error', async () => {
|
||||
// Test unlink error handling
|
||||
expect(true).toBe(true);
|
||||
});
|
||||
|
||||
it('should show alert about external account limitations', () => {
|
||||
// Test that the informational alert is displayed
|
||||
expect(true).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('OAuth Flow States', () => {
|
||||
it('should handle redirect from OAuth provider with code', async () => {
|
||||
// Test handling callback with authorization code
|
||||
expect(true).toBe(true);
|
||||
});
|
||||
|
||||
it('should handle OAuth error response', async () => {
|
||||
// Test handling error from OAuth provider
|
||||
expect(true).toBe(true);
|
||||
});
|
||||
|
||||
it('should validate state parameter matches', () => {
|
||||
// Test state parameter validation for CSRF protection
|
||||
expect(true).toBe(true);
|
||||
});
|
||||
|
||||
it('should handle expired state', async () => {
|
||||
// Test handling of expired OAuth state
|
||||
expect(true).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Provider Configuration UI', () => {
|
||||
it('should show provider configuration status', () => {
|
||||
// Test that configured providers are marked as such
|
||||
expect(true).toBe(true);
|
||||
});
|
||||
|
||||
it('should allow admin to configure provider', () => {
|
||||
// Test provider configuration form for admins
|
||||
expect(true).toBe(true);
|
||||
});
|
||||
|
||||
it('should validate required fields', () => {
|
||||
// Test form validation for provider configuration
|
||||
expect(true).toBe(true);
|
||||
});
|
||||
|
||||
it('should handle provider deletion', async () => {
|
||||
// Test provider configuration deletion
|
||||
expect(true).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Error Handling', () => {
|
||||
it('should display OAuth error messages to user', async () => {
|
||||
// Test error message display
|
||||
expect(true).toBe(true);
|
||||
});
|
||||
|
||||
it('should handle network errors during OAuth flow', async () => {
|
||||
// Test network error handling
|
||||
expect(true).toBe(true);
|
||||
});
|
||||
|
||||
it('should provide retry options after failures', async () => {
|
||||
// Test retry functionality after OAuth failures
|
||||
expect(true).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Security Considerations', () => {
|
||||
it('should not expose tokens in URL', () => {
|
||||
// Test that tokens are not exposed in URL fragments
|
||||
expect(true).toBe(true);
|
||||
});
|
||||
|
||||
it('should use state parameter for CSRF protection', () => {
|
||||
// Test that state parameter is used
|
||||
expect(true).toBe(true);
|
||||
});
|
||||
|
||||
it('should verify redirect URI matches configured value', () => {
|
||||
// Test redirect URI validation
|
||||
expect(true).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('External Auth API Integration', () => {
|
||||
describe('Provider List API', () => {
|
||||
it('should fetch available providers', async () => {
|
||||
// Test API call to fetch provider list
|
||||
expect(true).toBe(true);
|
||||
});
|
||||
|
||||
it('should indicate configured vs unconfigured providers', async () => {
|
||||
// Test provider configuration status in API response
|
||||
expect(true).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Link Account Flow API', () => {
|
||||
it('should initiate link flow', async () => {
|
||||
// Test API call to initiate linking
|
||||
expect(true).toBe(true);
|
||||
});
|
||||
|
||||
it('should return authorization URL and state', async () => {
|
||||
// Test that API returns OAuth parameters
|
||||
expect(true).toBe(true);
|
||||
});
|
||||
|
||||
it('should complete link flow', async () => {
|
||||
// Test API call to complete linking
|
||||
expect(true).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Unlink Account API', () => {
|
||||
it('should unlink provider account', async () => {
|
||||
// Test API call to unlink provider
|
||||
expect(true).toBe(true);
|
||||
});
|
||||
|
||||
it('should prevent unlinking last method', async () => {
|
||||
// Test error when trying to unlink last method
|
||||
expect(true).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Linked Accounts List API', () => {
|
||||
it('should fetch linked accounts', async () => {
|
||||
// Test API call to fetch linked accounts
|
||||
expect(true).toBe(true);
|
||||
});
|
||||
|
||||
it('should include provider details', async () => {
|
||||
// Test that linked accounts include provider info
|
||||
expect(true).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('OAuth Flow UX', () => {
|
||||
describe('Loading States', () => {
|
||||
it('should show spinner during OAuth redirect', () => {
|
||||
// Test loading state during OAuth redirect
|
||||
expect(true).toBe(true);
|
||||
});
|
||||
|
||||
it('should show success message after linking', async () => {
|
||||
// Test success feedback after account link
|
||||
expect(true).toBe(true);
|
||||
});
|
||||
|
||||
it('should show error toast on failure', async () => {
|
||||
// Test error toast display
|
||||
expect(true).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Navigation', () => {
|
||||
it('should redirect to correct page after OAuth login', async () => {
|
||||
// Test navigation after successful OAuth login
|
||||
expect(true).toBe(true);
|
||||
});
|
||||
|
||||
it('should return to original page after linking', async () => {
|
||||
// Test return to original page after account link
|
||||
expect(true).toBe(true);
|
||||
});
|
||||
|
||||
it('should handle browser back button during OAuth', async () => {
|
||||
// Test browser navigation handling during OAuth
|
||||
expect(true).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Accessibility', () => {
|
||||
it('should have proper ARIA labels for provider buttons', () => {
|
||||
// Test accessibility of OAuth buttons
|
||||
expect(true).toBe(true);
|
||||
});
|
||||
|
||||
it('should announce OAuth errors to screen readers', async () => {
|
||||
// Test error announcements for screen readers
|
||||
expect(true).toBe(true);
|
||||
});
|
||||
|
||||
it('should be keyboard navigable', () => {
|
||||
// Test keyboard navigation support
|
||||
expect(true).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1 @@
|
||||
import '@testing-library/jest-dom';
|
||||
Reference in New Issue
Block a user