Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 40 additions & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,44 @@ module.exports = {
'*.config.js',
'*.config.ts',
],
overrides: [
{
files: ['**/*.cjs', 'scripts/**/*.js'],
rules: {
'@typescript-eslint/no-var-requires': 'off',
},
},
{
files: [
'**/__tests__/**/*.{ts,tsx}',
'**/*.test.{ts,tsx}',
'**/__mocks__/**/*.{ts,tsx}',
],
env: {
jest: true,
},
rules: {
'@typescript-eslint/no-var-requires': 'off',
},
},
{
files: ['scripts/**/*.mjs'],
rules: {
'no-console': 'off',
},
},
{
files: [
'tests/**/*.{ts,tsx}',
'packages/shared-tests/**/*.{ts,tsx}',
'apps/mobile/__tests__/**/*.{ts,tsx}',
'scripts/**/*.js',
],
rules: {
'no-console': 'off',
'@typescript-eslint/no-explicit-any': 'off',
'@typescript-eslint/no-non-null-assertion': 'off',
},
},
],
};
6 changes: 3 additions & 3 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,9 @@ on:
- '**/babel.config.*'
- '**/metro.config.*'
- '**/vite.config.*'
# Test-related scripts (if they change, tests might be affected)
- 'scripts/merge-coverage.js'
- 'scripts/format-ci-summary.mjs'
# Scripts and repo lint config (lint covers scripts/**)
- 'scripts/**'
- '.eslintrc.js'
# Workflow file itself
- '.github/workflows/test.yml'

Expand Down
5 changes: 0 additions & 5 deletions apps/mobile/__tests__/App.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,6 @@ jest.mock('@react-navigation/native', () => ({
}));

jest.mock('@react-navigation/native-stack', () => {
const React = require('react');
return {
createNativeStackNavigator: () => ({
Navigator: ({ children }: any) => children,
Expand Down Expand Up @@ -131,7 +130,6 @@ jest.mock('@beakerstack/shared/components/auth/ProtectedRoute', () => ({
// Mock form components to avoid StyleSheet.create() native bridge issues in tests
jest.mock('@beakerstack/shared/components/forms/FormInput.native', () => ({
FormInput: ({ label, value, onChange, ...props }: any) => {
const React = require('react');
const { TextInput, View, Text } = require('react-native');
return (
<View>
Expand All @@ -144,7 +142,6 @@ jest.mock('@beakerstack/shared/components/forms/FormInput.native', () => ({

jest.mock('@beakerstack/shared/components/forms/FormButton.native', () => ({
FormButton: ({ title, onPress, ...props }: any) => {
const React = require('react');
const { TouchableOpacity, Text } = require('react-native');
return (
<TouchableOpacity onPress={onPress} {...props}>
Expand All @@ -156,13 +153,11 @@ jest.mock('@beakerstack/shared/components/forms/FormButton.native', () => ({

jest.mock('@beakerstack/shared/components/forms/FormError.native', () => ({
FormError: ({ message }: any) => {
const React = require('react');
const { Text } = require('react-native');
return message ? <Text>{message}</Text> : null;
},
}));

import React from 'react';
import { render } from '@testing-library/react-native';
import { describe, it, expect } from '@jest/globals';
import App from '../App';
Expand Down
2 changes: 1 addition & 1 deletion apps/mobile/__tests__/components/AvatarUpload.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ describe('AvatarUpload.native', () => {
});

it('renders with current avatar URL', () => {
const { UNSAFE_getByType } = render(
render(
<AvatarUpload
currentAvatarUrl='https://example.com/avatar.jpg'
onUploadComplete={mockOnUploadComplete}
Expand Down
15 changes: 4 additions & 11 deletions apps/mobile/__tests__/navigation/AppNavigator.test.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
import React from 'react';
import { render } from '@testing-library/react-native';
import { NavigationContainer } from '@react-navigation/native';
import { AppNavigator } from '../../src/navigation/AppNavigator';
import { useFeatureFlags } from '../../src/config/featureFlags';

Expand All @@ -11,7 +9,6 @@ jest.mock('../../src/config/featureFlags', () => ({

// Mock screens
jest.mock('../../src/screens/HomeScreen', () => {
const React = require('react');
const { View, Text } = require('react-native');
return () => (
<View testID='home-screen'>
Expand All @@ -21,7 +18,6 @@ jest.mock('../../src/screens/HomeScreen', () => {
});

jest.mock('../../src/screens/LoginScreen', () => {
const React = require('react');
const { View, Text } = require('react-native');
return () => (
<View testID='login-screen'>
Expand All @@ -31,7 +27,6 @@ jest.mock('../../src/screens/LoginScreen', () => {
});

jest.mock('../../src/screens/SignupScreen', () => {
const React = require('react');
const { View, Text } = require('react-native');
return () => (
<View testID='signup-screen'>
Expand All @@ -41,7 +36,6 @@ jest.mock('../../src/screens/SignupScreen', () => {
});

jest.mock('../../src/screens/DashboardScreen', () => {
const React = require('react');
const { View, Text } = require('react-native');
return () => (
<View testID='dashboard-screen'>
Expand All @@ -51,7 +45,6 @@ jest.mock('../../src/screens/DashboardScreen', () => {
});

jest.mock('../../src/screens/ProfileScreen', () => {
const React = require('react');
const { View, Text } = require('react-native');
return () => (
<View testID='profile-screen'>
Expand Down Expand Up @@ -84,30 +77,30 @@ describe('AppNavigator', () => {

it('exposes navigation ref in dev mode', () => {
const originalDev = __DEV__;
// @ts-ignore
// @ts-expect-error test-only assignment to global __DEV__
global.__DEV__ = true;

render(<AppNavigator />);

// Navigation ref should be exposed to global scope in dev mode
expect((global as any).navigationRef).toBeDefined();

// @ts-ignore
// @ts-expect-error test-only assignment to global __DEV__
global.__DEV__ = originalDev;
});

it.skip('does not expose navigation ref in production', () => {
// Skip - __DEV__ is read-only in Jest environment
const originalDev = __DEV__;
// @ts-ignore
// @ts-expect-error test-only assignment to global __DEV__
global.__DEV__ = false;

render(<AppNavigator />);

// Navigation ref should not be exposed in production
expect((global as any).navigationRef).toBeUndefined();

// @ts-ignore
// @ts-expect-error test-only assignment to global __DEV__
global.__DEV__ = originalDev;
});
});
2 changes: 1 addition & 1 deletion apps/mobile/__tests__/screens/LoginScreen.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -256,7 +256,7 @@ describe('LoginScreen', () => {

it('redirects to Dashboard if already authenticated', async () => {
const mockClient = createMockSupabaseClient(true);
const { getByText } = renderWithProviders(
renderWithProviders(
<LoginScreen navigation={mockNavigation} />,
mockClient
);
Expand Down
3 changes: 0 additions & 3 deletions apps/mobile/__tests__/screens/ProfileScreen.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,6 @@ jest.mock(
'@beakerstack/shared/components/profile/ProfileHeader.native',
() => ({
ProfileHeader: ({ profile }: any) => {
const React = require('react');
const { View, Text } = require('react-native');
return (
<View testID='profile-header'>
Expand All @@ -110,7 +109,6 @@ jest.mock(

jest.mock('@beakerstack/shared/components/profile/ProfileStats.native', () => ({
ProfileStats: ({ profile }: any) => {
const React = require('react');
const { View, Text } = require('react-native');
return profile ? (
<View testID='profile-stats'>
Expand All @@ -124,7 +122,6 @@ jest.mock(
'@beakerstack/shared/components/profile/ProfileEditor.native',
() => ({
ProfileEditor: ({ user }: any) => {
const React = require('react');
const { View, Text } = require('react-native');
return (
<View testID='profile-editor'>
Expand Down
2 changes: 1 addition & 1 deletion apps/mobile/__tests__/screens/SignupScreen.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -300,7 +300,7 @@ describe('SignupScreen', () => {

it('redirects to Dashboard if already authenticated', async () => {
const mockClient = createMockSupabaseClient(true);
const { getByText } = renderWithProviders(
renderWithProviders(
<SignupScreen navigation={mockNavigation} />,
mockClient
);
Expand Down
2 changes: 0 additions & 2 deletions apps/mobile/src/lib/__tests__/supabase.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,6 @@ describe('supabase.ts', () => {
(global as any).__DEV__ = false;
Platform.OS = 'ios';

// eslint-disable-next-line @typescript-eslint/no-var-requires
const { supabase } = require('../supabase');

expect(mockCreateClient).toHaveBeenCalledWith(
Expand All @@ -92,7 +91,6 @@ describe('supabase.ts', () => {
});

it('should export supabase client', () => {
// eslint-disable-next-line @typescript-eslint/no-var-requires
const module = require('../supabase');

expect(module).toHaveProperty('supabase');
Expand Down
7 changes: 4 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -93,12 +93,13 @@
"test:coverage:shared": "cd packages/shared-tests && npm run test:coverage",
"test:coverage:merge": "node scripts/merge-coverage.js",
"test:unit:scripts": "node --test scripts/__tests__/rename-project.test.mjs scripts/__tests__/detect-repo-identity.test.mjs scripts/__tests__/setup-aws-discover.test.mjs scripts/__tests__/setup-dotenv.test.mjs scripts/__tests__/setup-full-logging.test.mjs scripts/__tests__/setup-manifest-github-sync.test.mjs scripts/__tests__/setup-secret-input.test.mjs scripts/__tests__/setup-supabase-pick-recommend.test.mjs && ./scripts/pr-preview/check-preview-deploy-needed.sh --self-test",
"lint": "npm run lint:mobile; npm run lint:web; npm run lint:shared",
"lint": "npm run lint:mobile; npm run lint:web; npm run lint:shared; npm run lint:repo",
"lint:mobile": "eslint apps/mobile/src --ext ts,tsx --report-unused-disable-directives",
"lint:web": "eslint apps/web/src --ext ts,tsx --report-unused-disable-directives",
"lint:shared": "eslint packages/shared/src --ext ts,tsx --report-unused-disable-directives",
"lint:strict": "npm run lint:mobile -- --max-warnings 0 && npm run lint:web -- --max-warnings 0 && npm run lint:shared -- --max-warnings 0",
"lint:fix": "eslint apps/mobile/src apps/web/src packages/shared/src --ext ts,tsx --fix",
"lint:repo": "eslint scripts tests packages/shared-tests packages/test-utils/src apps/mobile/__tests__ --ext ts,tsx,js,mjs,cjs --report-unused-disable-directives",
"lint:strict": "npm run lint:mobile -- --max-warnings 0 && npm run lint:web -- --max-warnings 0 && npm run lint:shared -- --max-warnings 0 && npm run lint:repo -- --max-warnings 0",
"lint:fix": "eslint apps/mobile/src apps/web/src packages/shared/src scripts tests packages/shared-tests packages/test-utils/src apps/mobile/__tests__ --ext ts,tsx,js,mjs,cjs --fix",
"pretype-check": "cd packages/shared && npm run build",
"type-check": "npm run type-check:mobile; npm run type-check:web; npm run type-check:shared",
"type-check:mobile": "cd apps/mobile && npx tsc --noEmit",
Expand Down
2 changes: 1 addition & 1 deletion packages/shared-tests/__mocks__/react-router-dom.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export const MemoryRouter = ({ children }: { children: ReactNode }) => (
<div>{children}</div>
);

export const useNavigate = () => (path: string) => {
export const useNavigate = () => (_path: string) => {
/* noop for tests */
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,7 @@ describe('FormInput (Native)', () => {
);

const input = screen.getByPlaceholderText('Enter username');
// react-native-web renders TextInput - need to trigger onChangeText
// For react-native-web, we can use the native event
const nativeEvent = { target: { value: 'newvalue' } };
// react-native-web renders TextInput - fire changeText directly
fireEvent(input, 'changeText', 'newvalue');

expect(mockOnChange).toHaveBeenCalledWith('newvalue');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,13 @@ jest.mock('react-native', () => {

// Mock UserMenu
jest.mock('@beakerstack/shared/components/navigation/UserMenu.native', () => ({
UserMenu: ({ user, profile }: { user: any; profile: any }) => (
<div data-testid='user-menu'>User Menu</div>
),
UserMenu: ({
user: _user,
profile: _profile,
}: {
user: any;
profile: any;
}) => <div data-testid='user-menu'>User Menu</div>,
}));

// react-native-svg is mocked via moduleNameMapper
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React from 'react';
import { render, screen, waitFor } from '@testing-library/react';
import { BrowserRouter, MemoryRouter } from 'react-router-dom';
import { BrowserRouter } from 'react-router-dom';
import '@testing-library/jest-dom';
import { AppHeader } from '@beakerstack/shared/components/navigation/AppHeader.web';
import { AuthProvider } from '@beakerstack/shared/contexts/AuthContext';
Expand All @@ -27,22 +27,6 @@ const createMockSupabaseClient = (): SupabaseClient => {
} as unknown as SupabaseClient;
};

const renderWithProviders = (
component: React.ReactElement,
initialEntries: string[] = ['/']
) => {
const mockClient = createMockSupabaseClient();
return render(
<MemoryRouter initialEntries={initialEntries}>
<AuthProvider supabaseClient={mockClient}>
<ProfileProvider supabaseClient={mockClient}>
{component}
</ProfileProvider>
</AuthProvider>
</MemoryRouter>
);
};

describe('AppHeader (Web)', () => {
beforeEach(() => {
jest.clearAllMocks();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -279,7 +279,7 @@ describe('AvatarUpload (Native)', () => {
it('handles Android platform', async () => {
const { Platform } = require('react-native');
Platform.OS = 'android';
// @ts-ignore
// @ts-expect-error test-only assignment to global __DEV__
global.__DEV__ = true;

const { launchImageLibraryAsync } = require('expo-image-picker');
Expand Down Expand Up @@ -608,7 +608,7 @@ describe('AvatarUpload (Native)', () => {
it('fixes URL for Android emulator in dev mode', () => {
const { Platform } = require('react-native');
Platform.OS = 'android';
// @ts-ignore
// @ts-expect-error test-only assignment to global __DEV__
global.__DEV__ = true;

render(
Expand All @@ -626,7 +626,7 @@ describe('AvatarUpload (Native)', () => {
expect(images.length).toBeGreaterThan(0);

Platform.OS = 'ios';
// @ts-ignore
// @ts-expect-error test-only assignment to global __DEV__
global.__DEV__ = false;
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -253,7 +253,7 @@ describe('AvatarUpload', () => {
uploadedUrl: newAvatarUrl, // New uploaded URL
});

const { rerender } = render(
render(
<AvatarUpload
currentAvatarUrl={oldAvatarUrl}
onUploadComplete={mockOnUploadComplete}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { describe, it, expect, jest, beforeEach } from '@jest/globals';
import { render, screen, waitFor, fireEvent } from '@testing-library/react';
import { render, screen } from '@testing-library/react';
import '@testing-library/jest-dom';
import { ProfileAvatar } from '@beakerstack/shared/components/profile/ProfileAvatar.native';
import type { UserProfile } from '@beakerstack/shared/types/profile';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,13 +50,8 @@ jest.mock('@beakerstack/shared/components/forms/FormError.native', () => ({
}));

// Mock AvatarUpload component
let mockOnUploadComplete: ((url: string) => Promise<void>) | null = null;
let mockOnRemove: (() => Promise<void>) | null = null;

jest.mock('@beakerstack/shared/components/profile/AvatarUpload.native', () => ({
AvatarUpload: ({ onUploadComplete, onRemove }: any) => {
mockOnUploadComplete = onUploadComplete;
mockOnRemove = onRemove;
return (
<div data-testid='avatar-upload'>
<button
Expand Down
Loading
Loading