mirror of
https://github.com/selfxyz/self.git
synced 2026-04-05 03:00:53 -04:00
Merge pull request #1541 from selfxyz/release/staging-2026-01-02
Release to Staging - 2026-01-02
This commit is contained in:
@@ -19,6 +19,9 @@ jest.mock('@/services/analytics', () => ({
|
||||
trackScreenView: jest.fn(),
|
||||
flush: jest.fn(),
|
||||
})),
|
||||
trackEvent: jest.fn(),
|
||||
trackScreenView: jest.fn(),
|
||||
flush: jest.fn(),
|
||||
}));
|
||||
|
||||
describe('navigation', () => {
|
||||
|
||||
@@ -11,16 +11,20 @@ import {
|
||||
checkAndUpdateRegistrationStates,
|
||||
getAlternativeCSCA,
|
||||
} from '@/proving/validateDocument';
|
||||
import analytics from '@/services/analytics';
|
||||
import { trackEvent } from '@/services/analytics';
|
||||
|
||||
// Mock the analytics module to avoid side effects in tests
|
||||
jest.mock('@/services/analytics', () => {
|
||||
// Create mock inside factory to avoid temporal dead zone
|
||||
const mockTrackEvent = jest.fn();
|
||||
return jest.fn(() => ({
|
||||
trackEvent: mockTrackEvent,
|
||||
}));
|
||||
});
|
||||
jest.mock('@/services/analytics', () => ({
|
||||
__esModule: true,
|
||||
default: jest.fn(() => ({
|
||||
trackEvent: jest.fn(),
|
||||
trackScreenView: jest.fn(),
|
||||
flush: jest.fn(),
|
||||
})),
|
||||
trackEvent: jest.fn(),
|
||||
trackScreenView: jest.fn(),
|
||||
flush: jest.fn(),
|
||||
}));
|
||||
|
||||
// Mock the passport data provider to avoid database operations
|
||||
const mockGetAllDocumentsDirectlyFromKeychain = jest.fn();
|
||||
@@ -153,8 +157,7 @@ describe('getAlternativeCSCA', () => {
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
// Get the mocked trackEvent from the analytics module
|
||||
const mockAnalytics = jest.mocked(analytics);
|
||||
mockTrackEvent = mockAnalytics().trackEvent as jest.Mock;
|
||||
mockTrackEvent = jest.mocked(trackEvent) as jest.Mock;
|
||||
});
|
||||
|
||||
it('should return public keys in Record format for Aadhaar with valid public keys', () => {
|
||||
@@ -245,8 +248,7 @@ describe('checkAndUpdateRegistrationStates', () => {
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
// Get the mocked trackEvent from the analytics module
|
||||
const mockAnalytics = jest.mocked(analytics);
|
||||
mockTrackEvent = mockAnalytics().trackEvent as jest.Mock;
|
||||
mockTrackEvent = jest.mocked(trackEvent) as jest.Mock;
|
||||
|
||||
mockGetState.mockReturnValue(
|
||||
buildState({
|
||||
|
||||
@@ -2,19 +2,17 @@
|
||||
// SPDX-License-Identifier: BUSL-1.1
|
||||
// NOTE: Converts to Apache-2.0 on 2029-06-11 per LICENSE.
|
||||
|
||||
import analytics from '@/services/analytics';
|
||||
import { trackEvent, trackScreenView } from '@/services/analytics';
|
||||
|
||||
// Mock the Segment client
|
||||
jest.mock('@/config/segment', () => ({
|
||||
createSegmentClient: jest.fn(() => ({
|
||||
track: jest.fn(),
|
||||
screen: jest.fn(),
|
||||
track: jest.fn().mockResolvedValue(undefined),
|
||||
flush: jest.fn().mockResolvedValue(undefined),
|
||||
})),
|
||||
}));
|
||||
|
||||
describe('analytics', () => {
|
||||
const { trackEvent, trackScreenView } = analytics();
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
@@ -38,7 +36,7 @@ describe('analytics', () => {
|
||||
});
|
||||
|
||||
it('should handle event tracking with null properties', () => {
|
||||
expect(() => trackEvent('test_event', null)).not.toThrow();
|
||||
expect(() => trackEvent('test_event', null as any)).not.toThrow();
|
||||
});
|
||||
|
||||
it('should handle event tracking with undefined properties', () => {
|
||||
@@ -87,7 +85,7 @@ describe('analytics', () => {
|
||||
|
||||
it('should handle invalid duration values gracefully', () => {
|
||||
const properties = {
|
||||
duration_seconds: 'not_a_number',
|
||||
duration_seconds: 'not_a_number' as any,
|
||||
};
|
||||
|
||||
expect(() => trackEvent('test_event', properties)).not.toThrow();
|
||||
@@ -144,6 +142,22 @@ describe('analytics', () => {
|
||||
|
||||
expect(() => trackEvent('test_event', properties)).not.toThrow();
|
||||
});
|
||||
|
||||
it('should NOT transform regular event names (only screen views get "Viewed" prefix)', () => {
|
||||
const consoleSpy = jest.spyOn(console, 'log').mockImplementation();
|
||||
|
||||
trackEvent('user_login', { method: 'google' });
|
||||
|
||||
expect(consoleSpy).toHaveBeenCalledWith(
|
||||
'[DEV: Analytics EVENT]',
|
||||
expect.objectContaining({
|
||||
name: 'user_login', // No "Viewed" prefix for regular events
|
||||
properties: expect.objectContaining({ method: 'google' }),
|
||||
}),
|
||||
);
|
||||
|
||||
consoleSpy.mockRestore();
|
||||
});
|
||||
});
|
||||
|
||||
describe('trackScreenView', () => {
|
||||
@@ -176,6 +190,98 @@ describe('analytics', () => {
|
||||
|
||||
expect(() => trackScreenView('test_screen', properties)).not.toThrow();
|
||||
});
|
||||
|
||||
it('should transform screen views to "Viewed ScreenName" format', () => {
|
||||
// Mock console.log to capture dev mode output
|
||||
const consoleSpy = jest.spyOn(console, 'log').mockImplementation();
|
||||
|
||||
trackScreenView('SplashScreen', { user_id: 123 });
|
||||
|
||||
expect(consoleSpy).toHaveBeenCalledWith(
|
||||
'[DEV: Analytics SCREEN]',
|
||||
expect.objectContaining({
|
||||
name: 'Viewed SplashScreen',
|
||||
properties: expect.objectContaining({ user_id: 123 }),
|
||||
}),
|
||||
);
|
||||
|
||||
consoleSpy.mockRestore();
|
||||
});
|
||||
|
||||
it('should transform screen names correctly without properties', () => {
|
||||
const consoleSpy = jest.spyOn(console, 'log').mockImplementation();
|
||||
|
||||
trackScreenView('DocumentNFCScanScreen');
|
||||
|
||||
expect(consoleSpy).toHaveBeenCalledWith(
|
||||
'[DEV: Analytics SCREEN]',
|
||||
expect.objectContaining({
|
||||
name: 'Viewed DocumentNFCScanScreen',
|
||||
properties: undefined,
|
||||
}),
|
||||
);
|
||||
|
||||
consoleSpy.mockRestore();
|
||||
});
|
||||
|
||||
it('should pass through properties unchanged', () => {
|
||||
const consoleSpy = jest.spyOn(console, 'log').mockImplementation();
|
||||
const properties = {
|
||||
referrer: 'home',
|
||||
user_id: 456,
|
||||
navigation_method: 'swipe',
|
||||
};
|
||||
|
||||
trackScreenView('SettingsScreen', properties);
|
||||
|
||||
expect(consoleSpy).toHaveBeenCalledWith(
|
||||
'[DEV: Analytics SCREEN]',
|
||||
expect.objectContaining({
|
||||
name: 'Viewed SettingsScreen',
|
||||
properties: expect.objectContaining(properties),
|
||||
}),
|
||||
);
|
||||
|
||||
consoleSpy.mockRestore();
|
||||
});
|
||||
|
||||
it('should call segment client with transformed event name in production', () => {
|
||||
// Temporarily mock __DEV__ to false for production testing
|
||||
const originalDev = (global as any).__DEV__;
|
||||
(global as any).__DEV__ = false;
|
||||
|
||||
try {
|
||||
// Reset modules first to clear the cache
|
||||
jest.resetModules();
|
||||
|
||||
// Get the mocked segment client factory after reset
|
||||
const segmentModule = require('@/config/segment');
|
||||
const mockTrack = jest.fn().mockResolvedValue(undefined);
|
||||
|
||||
// Set up the mock implementation before re-requiring analytics
|
||||
// This ensures the mock is properly configured when analytics module loads
|
||||
segmentModule.createSegmentClient.mockImplementation(() => ({
|
||||
track: mockTrack,
|
||||
flush: jest.fn().mockResolvedValue(undefined),
|
||||
}));
|
||||
|
||||
// Now re-require analytics to get a fresh segmentClient instance
|
||||
// that uses our mocked createSegmentClient
|
||||
const analyticsModule = require('@/services/analytics');
|
||||
|
||||
analyticsModule.trackScreenView('HomeScreen', { user_type: 'premium' });
|
||||
|
||||
expect(mockTrack).toHaveBeenCalledWith('Viewed HomeScreen', {
|
||||
user_type: 'premium',
|
||||
});
|
||||
} finally {
|
||||
// Restore original __DEV__ value
|
||||
(global as any).__DEV__ = originalDev;
|
||||
|
||||
// Reset modules again to restore original state for other tests
|
||||
jest.resetModules();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('edge cases', () => {
|
||||
|
||||
@@ -94,10 +94,9 @@ describe('Logging Service - Severity Updates', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('should update severity on root logger and all extended loggers when settings change', async () => {
|
||||
it('should update severity on root logger when settings change', async () => {
|
||||
// Clear any calls from initialization
|
||||
mockRootSetSeverity.mockClear();
|
||||
mockLoggerInstances.forEach(logger => logger.setSeverity.mockClear());
|
||||
|
||||
// Change the logging severity in the store
|
||||
useSettingStore.getState().setLoggingSeverity('debug');
|
||||
@@ -106,30 +105,21 @@ describe('Logging Service - Severity Updates', () => {
|
||||
await new Promise(resolve => setTimeout(resolve, 10));
|
||||
|
||||
// Verify root logger was updated
|
||||
// Extended loggers inherit severity from root logger automatically
|
||||
expect(mockRootSetSeverity).toHaveBeenCalledTimes(1);
|
||||
expect(mockRootSetSeverity).toHaveBeenCalledWith('debug');
|
||||
|
||||
// Verify each extended logger was updated
|
||||
mockLoggerInstances.forEach(logger => {
|
||||
expect(logger.setSeverity).toHaveBeenCalledTimes(1);
|
||||
expect(logger.setSeverity).toHaveBeenCalledWith('debug');
|
||||
});
|
||||
});
|
||||
|
||||
it('should update each specific extended logger individually', async () => {
|
||||
it('should update root logger severity which extends to all loggers', async () => {
|
||||
mockRootSetSeverity.mockClear();
|
||||
mockLoggerInstances.forEach(logger => logger.setSeverity.mockClear());
|
||||
|
||||
useSettingStore.getState().setLoggingSeverity('info');
|
||||
await new Promise(resolve => setTimeout(resolve, 10));
|
||||
|
||||
// Verify specific loggers by name
|
||||
const specificLoggers = ['APP', 'NFC', 'PASSPORT', 'PROOF'];
|
||||
specificLoggers.forEach(loggerName => {
|
||||
const logger = mockLoggerInstances.get(loggerName);
|
||||
expect(logger).toBeDefined();
|
||||
expect(logger?.setSeverity).toHaveBeenCalledWith('info');
|
||||
});
|
||||
// Verify root logger was updated
|
||||
// Extended loggers (APP, NFC, PASSPORT, PROOF, etc.) inherit from root
|
||||
expect(mockRootSetSeverity).toHaveBeenCalledTimes(1);
|
||||
expect(mockRootSetSeverity).toHaveBeenCalledWith('info');
|
||||
});
|
||||
|
||||
it('should update severity for all severity levels', async () => {
|
||||
@@ -142,24 +132,18 @@ describe('Logging Service - Severity Updates', () => {
|
||||
|
||||
for (const level of severityLevels) {
|
||||
mockRootSetSeverity.mockClear();
|
||||
mockLoggerInstances.forEach(logger => logger.setSeverity.mockClear());
|
||||
|
||||
useSettingStore.getState().setLoggingSeverity(level);
|
||||
await new Promise(resolve => setTimeout(resolve, 10));
|
||||
|
||||
// Verify root logger
|
||||
// Verify root logger was updated
|
||||
// Extended loggers inherit severity from root automatically
|
||||
expect(mockRootSetSeverity).toHaveBeenCalledWith(level);
|
||||
|
||||
// Verify all extended loggers
|
||||
mockLoggerInstances.forEach(logger => {
|
||||
expect(logger.setSeverity).toHaveBeenCalledWith(level);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
it('should not call setSeverity if severity has not changed', async () => {
|
||||
mockRootSetSeverity.mockClear();
|
||||
mockLoggerInstances.forEach(logger => logger.setSeverity.mockClear());
|
||||
|
||||
// Get current severity
|
||||
const currentSeverity = useSettingStore.getState().loggingSeverity;
|
||||
@@ -171,16 +155,10 @@ describe('Logging Service - Severity Updates', () => {
|
||||
|
||||
// Should not call setSeverity on root logger
|
||||
expect(mockRootSetSeverity).not.toHaveBeenCalled();
|
||||
|
||||
// Should not call setSeverity on any extended logger
|
||||
mockLoggerInstances.forEach(logger => {
|
||||
expect(logger.setSeverity).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
it('should handle rapid severity changes correctly', async () => {
|
||||
mockRootSetSeverity.mockClear();
|
||||
mockLoggerInstances.forEach(logger => logger.setSeverity.mockClear());
|
||||
|
||||
// Rapidly change severity multiple times
|
||||
useSettingStore.getState().setLoggingSeverity('debug');
|
||||
@@ -192,15 +170,10 @@ describe('Logging Service - Severity Updates', () => {
|
||||
await new Promise(resolve => setTimeout(resolve, 50));
|
||||
|
||||
// Should have been called 4 times (once per change)
|
||||
// Extended loggers inherit severity from root automatically
|
||||
expect(mockRootSetSeverity).toHaveBeenCalledTimes(4);
|
||||
|
||||
// The last call should be 'error'
|
||||
expect(mockRootSetSeverity).toHaveBeenLastCalledWith('error');
|
||||
|
||||
// Each extended logger should also have been called 4 times
|
||||
mockLoggerInstances.forEach(logger => {
|
||||
expect(logger.setSeverity).toHaveBeenCalledTimes(4);
|
||||
expect(logger.setSeverity).toHaveBeenLastCalledWith('error');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user