@idealyst/cli 1.0.32 → 1.0.34
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/generators/api.js +28 -25
- package/dist/generators/api.js.map +1 -1
- package/dist/generators/index.js +33 -16
- package/dist/generators/index.js.map +1 -1
- package/dist/generators/native.js +51 -48
- package/dist/generators/native.js.map +1 -1
- package/dist/generators/shared.js +25 -22
- package/dist/generators/shared.js.map +1 -1
- package/dist/generators/utils.js +146 -118
- package/dist/generators/utils.js.map +1 -1
- package/dist/generators/web.js +33 -30
- package/dist/generators/web.js.map +1 -1
- package/dist/generators/workspace.js +24 -21
- package/dist/generators/workspace.js.map +1 -1
- package/dist/index.js +82 -44
- package/dist/index.js.map +1 -1
- package/dist/templates/api/README.md +207 -0
- package/dist/templates/api/__tests__/api.test.ts +26 -0
- package/dist/templates/api/env.example +12 -0
- package/dist/templates/api/jest.config.js +23 -0
- package/dist/templates/api/jest.setup.js +9 -0
- package/dist/templates/api/package.json +62 -0
- package/dist/templates/api/prisma/schema.prisma +21 -0
- package/dist/templates/api/src/context.ts +23 -0
- package/dist/templates/api/src/controllers/UserController.ts +102 -0
- package/dist/templates/api/src/index.ts +14 -0
- package/dist/templates/api/src/lib/controller.ts +90 -0
- package/dist/templates/api/src/lib/middleware.ts +170 -0
- package/dist/templates/api/src/middleware/auth.ts +75 -0
- package/dist/templates/api/src/middleware/common.ts +103 -0
- package/dist/templates/api/src/router/index.ts +130 -0
- package/dist/templates/api/src/server.ts +50 -0
- package/dist/templates/api/src/trpc.ts +28 -0
- package/dist/templates/api/tsconfig.json +44 -0
- package/dist/templates/native/.yarnrc.yml +19 -0
- package/dist/templates/native/App.tsx +23 -0
- package/dist/templates/native/README.md +86 -0
- package/dist/templates/native/__tests__/App.test.tsx +156 -0
- package/dist/templates/native/__tests__/components.test.tsx +300 -0
- package/dist/templates/native/app.json +5 -0
- package/dist/templates/native/babel.config.js +10 -0
- package/dist/templates/native/index.js +6 -0
- package/dist/templates/native/jest.config.js +21 -0
- package/dist/templates/native/jest.setup.js +12 -0
- package/dist/templates/native/metro.config.js +27 -0
- package/dist/templates/native/package.json +44 -0
- package/dist/templates/native/src/App-with-trpc.tsx +59 -0
- package/dist/templates/native/src/utils/trpc.ts +127 -0
- package/dist/templates/native/tsconfig.json +30 -0
- package/dist/templates/shared/README.md +109 -0
- package/dist/templates/shared/__tests__/shared.test.ts +39 -0
- package/dist/templates/shared/jest.config.js +22 -0
- package/dist/templates/shared/package.json +50 -0
- package/dist/templates/shared/rollup.config.js +43 -0
- package/dist/templates/shared/src/index.ts +1 -0
- package/dist/templates/shared/tsconfig.json +25 -0
- package/dist/templates/web/README.md +90 -0
- package/dist/templates/web/__tests__/App.test.tsx +342 -0
- package/dist/templates/web/__tests__/components.test.tsx +564 -0
- package/dist/templates/web/index.html +13 -0
- package/dist/templates/web/jest.config.js +27 -0
- package/dist/templates/web/jest.setup.js +24 -0
- package/dist/templates/web/package.json +66 -0
- package/dist/templates/web/src/App-with-trpc.tsx +67 -0
- package/dist/templates/web/src/App.tsx +15 -0
- package/dist/templates/web/src/main.tsx +25 -0
- package/dist/templates/web/src/utils/trpc.ts +93 -0
- package/dist/templates/web/tsconfig.json +27 -0
- package/dist/templates/web/vite.config.ts +69 -0
- package/dist/templates/workspace/.devcontainer/devcontainer.json +140 -0
- package/dist/templates/workspace/.devcontainer/docker-compose.yml +74 -0
- package/dist/templates/workspace/.devcontainer/post-create.sh +89 -0
- package/dist/templates/workspace/.dockerignore +151 -0
- package/dist/templates/workspace/.env.example +36 -0
- package/dist/templates/workspace/.env.production +56 -0
- package/dist/templates/workspace/.yarnrc.yml +26 -0
- package/dist/templates/workspace/DOCKER.md +0 -0
- package/dist/templates/workspace/Dockerfile +93 -0
- package/dist/templates/workspace/README.md +179 -0
- package/dist/templates/workspace/docker/nginx/prod.conf +238 -0
- package/dist/templates/workspace/docker/nginx.conf +131 -0
- package/dist/templates/workspace/docker/postgres/init.sql +41 -0
- package/dist/templates/workspace/docker/prometheus/prometheus.yml +52 -0
- package/dist/templates/workspace/docker-compose.prod.yml +146 -0
- package/dist/templates/workspace/docker-compose.yml +144 -0
- package/dist/templates/workspace/jest.config.js +20 -0
- package/dist/templates/workspace/package.json +35 -0
- package/dist/templates/workspace/scripts/docker/db-backup.sh +230 -0
- package/dist/templates/workspace/scripts/docker/deploy.sh +212 -0
- package/dist/templates/workspace/scripts/docker-build.sh +151 -0
- package/dist/templates/workspace/scripts/test-runner.js +120 -0
- package/dist/templates/workspace/setup.sh +205 -0
- package/dist/types.js +2 -1
- package/package.json +3 -3
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { router, publicProcedure } from '../trpc.js';
|
|
3
|
+
|
|
4
|
+
export const appRouter = router({
|
|
5
|
+
// Simple hello world procedure
|
|
6
|
+
hello: publicProcedure
|
|
7
|
+
.input(z.object({ name: z.string().optional() }))
|
|
8
|
+
.query(({ input }) => {
|
|
9
|
+
return {
|
|
10
|
+
greeting: `Hello ${input.name || 'World'}!`,
|
|
11
|
+
timestamp: new Date().toISOString(),
|
|
12
|
+
};
|
|
13
|
+
}),
|
|
14
|
+
|
|
15
|
+
// Health check procedure
|
|
16
|
+
health: publicProcedure.query(() => {
|
|
17
|
+
return {
|
|
18
|
+
status: 'OK',
|
|
19
|
+
timestamp: new Date().toISOString(),
|
|
20
|
+
version: '1.0.0',
|
|
21
|
+
};
|
|
22
|
+
}),
|
|
23
|
+
|
|
24
|
+
// Add your procedures here
|
|
25
|
+
// Example:
|
|
26
|
+
// users: userRouter,
|
|
27
|
+
// posts: postRouter,
|
|
28
|
+
|
|
29
|
+
// Example controller integration:
|
|
30
|
+
// Uncomment the lines below and create the corresponding controllers
|
|
31
|
+
|
|
32
|
+
// 1. Import your controllers at the top:
|
|
33
|
+
// import { userRouter } from '../controllers/UserController.js';
|
|
34
|
+
|
|
35
|
+
// 2. Add them to the router:
|
|
36
|
+
// users: userRouter,
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
// Export type definition of API
|
|
40
|
+
export type AppRouter = typeof appRouter;
|
|
41
|
+
|
|
42
|
+
/*
|
|
43
|
+
CONTROLLER & MIDDLEWARE SYSTEM USAGE:
|
|
44
|
+
|
|
45
|
+
This API template includes a controller and middleware system that works seamlessly with tRPC.
|
|
46
|
+
|
|
47
|
+
## Quick Start with Controllers:
|
|
48
|
+
|
|
49
|
+
1. Create a controller (see src/controllers/UserController.ts for example):
|
|
50
|
+
|
|
51
|
+
```typescript
|
|
52
|
+
import { z } from 'zod';
|
|
53
|
+
import { BaseController, controllerToRouter } from '../lib/controller.js';
|
|
54
|
+
import { requireAuth } from '../middleware/auth.js';
|
|
55
|
+
|
|
56
|
+
export class UserController extends BaseController {
|
|
57
|
+
getAll = this.createQueryWithMiddleware(
|
|
58
|
+
z.object({}),
|
|
59
|
+
[requireAuth],
|
|
60
|
+
async (input, ctx) => {
|
|
61
|
+
return await ctx.prisma.user.findMany();
|
|
62
|
+
}
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export const userRouter = controllerToRouter({
|
|
67
|
+
getAll: new UserController({} as any).getAll,
|
|
68
|
+
});
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
2. Add to main router:
|
|
72
|
+
|
|
73
|
+
```typescript
|
|
74
|
+
import { userRouter } from '../controllers/UserController.js';
|
|
75
|
+
|
|
76
|
+
export const appRouter = router({
|
|
77
|
+
users: userRouter,
|
|
78
|
+
// ... other routes
|
|
79
|
+
});
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## Available Middleware:
|
|
83
|
+
|
|
84
|
+
### Authentication:
|
|
85
|
+
- `requireAuth` - Requires Bearer token
|
|
86
|
+
- `requireRole(role)` - Requires specific role
|
|
87
|
+
- `requireAdmin` - Requires admin role
|
|
88
|
+
|
|
89
|
+
### Utility:
|
|
90
|
+
- `logger` - Request/response logging
|
|
91
|
+
- `rateLimit(max, window)` - Rate limiting
|
|
92
|
+
- `responseTime` - Adds response time header
|
|
93
|
+
- `requestId` - Adds unique request ID
|
|
94
|
+
- `errorHandler` - Centralized error handling
|
|
95
|
+
|
|
96
|
+
## Usage Examples:
|
|
97
|
+
|
|
98
|
+
```typescript
|
|
99
|
+
// Public endpoint
|
|
100
|
+
getPublicData = this.createQuery(schema, handler);
|
|
101
|
+
|
|
102
|
+
// Protected endpoint
|
|
103
|
+
getPrivateData = this.createQueryWithMiddleware(
|
|
104
|
+
schema,
|
|
105
|
+
[requireAuth],
|
|
106
|
+
handler
|
|
107
|
+
);
|
|
108
|
+
|
|
109
|
+
// Admin-only endpoint
|
|
110
|
+
adminAction = this.createMutationWithMiddleware(
|
|
111
|
+
schema,
|
|
112
|
+
[requireAuth, requireAdmin],
|
|
113
|
+
handler
|
|
114
|
+
);
|
|
115
|
+
|
|
116
|
+
// Multiple middleware
|
|
117
|
+
complexEndpoint = this.createQueryWithMiddleware(
|
|
118
|
+
schema,
|
|
119
|
+
[logger, rateLimit(10, 60000), requireAuth],
|
|
120
|
+
handler
|
|
121
|
+
);
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
This system provides:
|
|
125
|
+
✅ Type safety with tRPC
|
|
126
|
+
✅ Reusable middleware
|
|
127
|
+
✅ Clean controller organization
|
|
128
|
+
✅ Easy testing
|
|
129
|
+
✅ Consistent error handling
|
|
130
|
+
*/
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import express from 'express';
|
|
2
|
+
import cors from 'cors';
|
|
3
|
+
import { createExpressMiddleware } from '@trpc/server/adapters/express';
|
|
4
|
+
import { appRouter } from './router/index.js';
|
|
5
|
+
import { createContext } from './context.js';
|
|
6
|
+
import dotenv from 'dotenv';
|
|
7
|
+
|
|
8
|
+
// Load environment variables
|
|
9
|
+
dotenv.config();
|
|
10
|
+
|
|
11
|
+
const app = express();
|
|
12
|
+
const PORT = process.env.PORT || 3000;
|
|
13
|
+
|
|
14
|
+
// CORS configuration
|
|
15
|
+
app.use(cors({
|
|
16
|
+
origin: process.env.CORS_ORIGIN || 'http://localhost:3000',
|
|
17
|
+
credentials: true,
|
|
18
|
+
}));
|
|
19
|
+
|
|
20
|
+
// Health check endpoint
|
|
21
|
+
app.get('/health', (req, res) => {
|
|
22
|
+
res.json({ status: 'OK', timestamp: new Date().toISOString() });
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
// tRPC middleware
|
|
26
|
+
app.use(
|
|
27
|
+
'/trpc',
|
|
28
|
+
createExpressMiddleware({
|
|
29
|
+
router: appRouter,
|
|
30
|
+
createContext,
|
|
31
|
+
})
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
// Default route
|
|
35
|
+
app.get('/', (req, res) => {
|
|
36
|
+
res.json({
|
|
37
|
+
message: 'Welcome to {{projectName}} API',
|
|
38
|
+
endpoints: {
|
|
39
|
+
health: '/health',
|
|
40
|
+
trpc: '/trpc',
|
|
41
|
+
playground: '/trpc-playground' // Available in development
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
app.listen(PORT, () => {
|
|
47
|
+
console.log(`🚀 Server running on http://localhost:${PORT}`);
|
|
48
|
+
console.log(`📡 tRPC API available at http://localhost:${PORT}/trpc`);
|
|
49
|
+
console.log(`🏥 Health check at http://localhost:${PORT}/health`);
|
|
50
|
+
});
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { initTRPC } from '@trpc/server';
|
|
2
|
+
import { type Context } from './context.js';
|
|
3
|
+
import { ZodError } from 'zod';
|
|
4
|
+
|
|
5
|
+
const t = initTRPC.context<Context>().create({
|
|
6
|
+
errorFormatter({ shape, error }) {
|
|
7
|
+
return {
|
|
8
|
+
...shape,
|
|
9
|
+
data: {
|
|
10
|
+
...shape.data,
|
|
11
|
+
zodError:
|
|
12
|
+
error.cause instanceof ZodError ? error.cause.flatten() : null,
|
|
13
|
+
},
|
|
14
|
+
};
|
|
15
|
+
},
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
// Export reusable router and procedure helpers
|
|
19
|
+
export const router = t.router;
|
|
20
|
+
export const publicProcedure = t.procedure;
|
|
21
|
+
|
|
22
|
+
// You can create additional procedures with middleware here
|
|
23
|
+
// For example, a protected procedure that requires authentication:
|
|
24
|
+
// export const protectedProcedure = t.procedure.use(async ({ ctx, next }) => {
|
|
25
|
+
// // Add your authentication logic here
|
|
26
|
+
// // Example: check for valid session/token
|
|
27
|
+
// return next({ ctx: { ...ctx, user: { id: 'user-id' } } });
|
|
28
|
+
// });
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2020",
|
|
4
|
+
"module": "ESNext",
|
|
5
|
+
"moduleResolution": "node",
|
|
6
|
+
"allowSyntheticDefaultImports": true,
|
|
7
|
+
"esModuleInterop": true,
|
|
8
|
+
"allowJs": true,
|
|
9
|
+
"checkJs": false,
|
|
10
|
+
"jsx": "preserve",
|
|
11
|
+
"declaration": true,
|
|
12
|
+
"declarationMap": true,
|
|
13
|
+
"sourceMap": true,
|
|
14
|
+
"outDir": "./dist",
|
|
15
|
+
"rootDir": "./src",
|
|
16
|
+
"removeComments": false,
|
|
17
|
+
"strict": true,
|
|
18
|
+
"noImplicitAny": true,
|
|
19
|
+
"strictNullChecks": true,
|
|
20
|
+
"strictFunctionTypes": true,
|
|
21
|
+
"noImplicitThis": true,
|
|
22
|
+
"useUnknownInCatchVariables": true,
|
|
23
|
+
"noImplicitReturns": true,
|
|
24
|
+
"noFallthroughCasesInSwitch": true,
|
|
25
|
+
"noUncheckedIndexedAccess": true,
|
|
26
|
+
"exactOptionalPropertyTypes": true,
|
|
27
|
+
"noPropertyAccessFromIndexSignature": false,
|
|
28
|
+
"resolveJsonModule": true,
|
|
29
|
+
"isolatedModules": true,
|
|
30
|
+
"forceConsistentCasingInFileNames": true,
|
|
31
|
+
"skipLibCheck": true
|
|
32
|
+
},
|
|
33
|
+
"include": [
|
|
34
|
+
"src/**/*"
|
|
35
|
+
],
|
|
36
|
+
"exclude": [
|
|
37
|
+
"node_modules",
|
|
38
|
+
"dist",
|
|
39
|
+
"**/*.test.ts"
|
|
40
|
+
],
|
|
41
|
+
"ts-node": {
|
|
42
|
+
"esm": true
|
|
43
|
+
}
|
|
44
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
nodeLinker: "node-modules"
|
|
2
|
+
|
|
3
|
+
# Enable network for package downloads
|
|
4
|
+
enableNetwork: true
|
|
5
|
+
|
|
6
|
+
# Timeout for HTTP requests (in milliseconds)
|
|
7
|
+
httpTimeout: 60000
|
|
8
|
+
|
|
9
|
+
# Number of retry attempts for HTTP requests
|
|
10
|
+
httpRetry: 3
|
|
11
|
+
|
|
12
|
+
# Registry configuration
|
|
13
|
+
npmRegistryServer: "https://registry.yarnpkg.com"
|
|
14
|
+
|
|
15
|
+
# Enable progress bars
|
|
16
|
+
enableProgressBars: true
|
|
17
|
+
|
|
18
|
+
# Enable colors in output
|
|
19
|
+
enableColors: true
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import {
|
|
3
|
+
SafeAreaView,
|
|
4
|
+
} from 'react-native';
|
|
5
|
+
|
|
6
|
+
import { ExampleStackRouter } from '@idealyst/navigation/examples';
|
|
7
|
+
import { NavigatorProvider } from '@idealyst/navigation';
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
function App() {
|
|
11
|
+
|
|
12
|
+
const backgroundStyle = {
|
|
13
|
+
flex: 1,
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
return (
|
|
17
|
+
<SafeAreaView style={backgroundStyle}>
|
|
18
|
+
<NavigatorProvider route={ExampleStackRouter} />
|
|
19
|
+
</SafeAreaView>
|
|
20
|
+
);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export default App;
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# {{projectName}}
|
|
2
|
+
|
|
3
|
+
{{description}}
|
|
4
|
+
|
|
5
|
+
## Getting Started
|
|
6
|
+
|
|
7
|
+
This is a React Native application built with the Idealyst Framework.
|
|
8
|
+
|
|
9
|
+
### Prerequisites
|
|
10
|
+
|
|
11
|
+
- Node.js 18+
|
|
12
|
+
- Yarn
|
|
13
|
+
- React Native development environment
|
|
14
|
+
- Android Studio (for Android development)
|
|
15
|
+
- Xcode (for iOS development)
|
|
16
|
+
|
|
17
|
+
### Installation
|
|
18
|
+
|
|
19
|
+
Install dependencies:
|
|
20
|
+
```bash
|
|
21
|
+
yarn install
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
### Running the App
|
|
25
|
+
|
|
26
|
+
Start the Metro bundler:
|
|
27
|
+
```bash
|
|
28
|
+
yarn start
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Run on Android:
|
|
32
|
+
```bash
|
|
33
|
+
yarn android
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Run on iOS:
|
|
37
|
+
```bash
|
|
38
|
+
yarn ios
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
### Project Structure
|
|
42
|
+
|
|
43
|
+
```
|
|
44
|
+
{{projectName}}/
|
|
45
|
+
├── src/
|
|
46
|
+
│ └── App.tsx # Main app component
|
|
47
|
+
├── android/ # Android-specific code
|
|
48
|
+
├── ios/ # iOS-specific code
|
|
49
|
+
├── babel.config.js # Babel configuration
|
|
50
|
+
├── metro.config.js # Metro configuration
|
|
51
|
+
└── tsconfig.json # TypeScript configuration
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### Features
|
|
55
|
+
|
|
56
|
+
- **Idealyst Components**: Pre-built UI components
|
|
57
|
+
- **Idealyst Navigation**: Cross-platform navigation
|
|
58
|
+
- **Idealyst Theme**: Consistent theming system
|
|
59
|
+
- **TypeScript**: Full type safety
|
|
60
|
+
- **React Native 0.80.1**: Latest stable version
|
|
61
|
+
|
|
62
|
+
### Development
|
|
63
|
+
|
|
64
|
+
The app uses the Idealyst Framework for consistent UI and navigation across platforms.
|
|
65
|
+
|
|
66
|
+
Edit `src/App.tsx` to start building your application.
|
|
67
|
+
|
|
68
|
+
### Building for Production
|
|
69
|
+
|
|
70
|
+
Build Android APK:
|
|
71
|
+
```bash
|
|
72
|
+
yarn build:android
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### Testing
|
|
76
|
+
|
|
77
|
+
Run tests:
|
|
78
|
+
```bash
|
|
79
|
+
yarn test
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### Learn More
|
|
83
|
+
|
|
84
|
+
- [Idealyst Framework Documentation](https://github.com/your-username/idealyst-framework)
|
|
85
|
+
- [React Native Documentation](https://reactnative.dev/)
|
|
86
|
+
- [TypeScript Documentation](https://www.typescriptlang.org/)
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @format
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import React from 'react';
|
|
6
|
+
import ReactTestRenderer from 'react-test-renderer';
|
|
7
|
+
import App from '../App';
|
|
8
|
+
|
|
9
|
+
// Mock the NavigatorProvider to avoid complex navigation setup
|
|
10
|
+
jest.mock('@idealyst/navigation', () => ({
|
|
11
|
+
NavigatorProvider: ({ children }: { children?: React.ReactNode }) => (
|
|
12
|
+
React.createElement('View', { testID: 'navigator-provider' }, children || 'Navigator Content')
|
|
13
|
+
),
|
|
14
|
+
}));
|
|
15
|
+
|
|
16
|
+
jest.mock('@idealyst/navigation/examples', () => ({
|
|
17
|
+
ExampleStackRouter: {},
|
|
18
|
+
}));
|
|
19
|
+
|
|
20
|
+
describe('App Component', () => {
|
|
21
|
+
it('renders correctly', async () => {
|
|
22
|
+
let component: ReactTestRenderer.ReactTestRenderer;
|
|
23
|
+
|
|
24
|
+
await ReactTestRenderer.act(() => {
|
|
25
|
+
component = ReactTestRenderer.create(<App />);
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
expect(component!).toBeDefined();
|
|
29
|
+
expect(component!.toJSON()).toMatchSnapshot();
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it('renders without crashing', () => {
|
|
33
|
+
const tree = ReactTestRenderer.create(<App />);
|
|
34
|
+
expect(tree).toBeDefined();
|
|
35
|
+
expect(tree.root).toBeDefined();
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it('contains NavigatorProvider', () => {
|
|
39
|
+
const tree = ReactTestRenderer.create(<App />);
|
|
40
|
+
const navigatorProvider = tree.root.findByProps({ testID: 'navigator-provider' });
|
|
41
|
+
expect(navigatorProvider).toBeDefined();
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it('has proper SafeAreaView structure', () => {
|
|
45
|
+
const tree = ReactTestRenderer.create(<App />);
|
|
46
|
+
const safeAreaView = tree.root.findByType('SafeAreaView');
|
|
47
|
+
expect(safeAreaView).toBeDefined();
|
|
48
|
+
expect(safeAreaView.props.style).toEqual({ flex: 1 });
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
describe('Sample Component Tests', () => {
|
|
53
|
+
// Example of testing a simple functional component
|
|
54
|
+
const SimpleButton = ({ title, onPress, disabled = false }: {
|
|
55
|
+
title: string;
|
|
56
|
+
onPress: () => void;
|
|
57
|
+
disabled?: boolean;
|
|
58
|
+
}) => {
|
|
59
|
+
return React.createElement(
|
|
60
|
+
'TouchableOpacity',
|
|
61
|
+
{
|
|
62
|
+
testID: 'simple-button',
|
|
63
|
+
onPress: disabled ? undefined : onPress,
|
|
64
|
+
style: { opacity: disabled ? 0.5 : 1 }
|
|
65
|
+
},
|
|
66
|
+
React.createElement('Text', null, title)
|
|
67
|
+
);
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
it('renders button with correct title', () => {
|
|
71
|
+
const mockPress = jest.fn();
|
|
72
|
+
const tree = ReactTestRenderer.create(
|
|
73
|
+
<SimpleButton title="Test Button" onPress={mockPress} />
|
|
74
|
+
);
|
|
75
|
+
|
|
76
|
+
const textElement = tree.root.findByType('Text');
|
|
77
|
+
expect(textElement.children).toEqual(['Test Button']);
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it('handles press events', () => {
|
|
81
|
+
const mockPress = jest.fn();
|
|
82
|
+
const tree = ReactTestRenderer.create(
|
|
83
|
+
<SimpleButton title="Test Button" onPress={mockPress} />
|
|
84
|
+
);
|
|
85
|
+
|
|
86
|
+
const button = tree.root.findByProps({ testID: 'simple-button' });
|
|
87
|
+
ReactTestRenderer.act(() => {
|
|
88
|
+
button.props.onPress();
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
expect(mockPress).toHaveBeenCalledTimes(1);
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
it('disables button when disabled prop is true', () => {
|
|
95
|
+
const mockPress = jest.fn();
|
|
96
|
+
const tree = ReactTestRenderer.create(
|
|
97
|
+
<SimpleButton title="Disabled Button" onPress={mockPress} disabled={true} />
|
|
98
|
+
);
|
|
99
|
+
|
|
100
|
+
const button = tree.root.findByProps({ testID: 'simple-button' });
|
|
101
|
+
expect(button.props.onPress).toBeUndefined();
|
|
102
|
+
expect(button.props.style.opacity).toBe(0.5);
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
it('handles component state changes', () => {
|
|
106
|
+
const StatefulComponent = () => {
|
|
107
|
+
const [count, setCount] = React.useState(0);
|
|
108
|
+
|
|
109
|
+
return React.createElement(
|
|
110
|
+
'View',
|
|
111
|
+
{ testID: 'stateful-component' },
|
|
112
|
+
React.createElement('Text', { testID: 'count' }, count.toString()),
|
|
113
|
+
React.createElement(
|
|
114
|
+
'TouchableOpacity',
|
|
115
|
+
{
|
|
116
|
+
testID: 'increment-button',
|
|
117
|
+
onPress: () => setCount(c => c + 1)
|
|
118
|
+
},
|
|
119
|
+
React.createElement('Text', null, 'Increment')
|
|
120
|
+
)
|
|
121
|
+
);
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
const tree = ReactTestRenderer.create(<StatefulComponent />);
|
|
125
|
+
|
|
126
|
+
// Check initial state
|
|
127
|
+
const countText = tree.root.findByProps({ testID: 'count' });
|
|
128
|
+
expect(countText.children).toEqual(['0']);
|
|
129
|
+
|
|
130
|
+
// Simulate button press
|
|
131
|
+
const incrementButton = tree.root.findByProps({ testID: 'increment-button' });
|
|
132
|
+
ReactTestRenderer.act(() => {
|
|
133
|
+
incrementButton.props.onPress();
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
// Check updated state
|
|
137
|
+
expect(countText.children).toEqual(['1']);
|
|
138
|
+
});
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
describe('Sample Native Tests', () => {
|
|
142
|
+
it('should pass a basic test', () => {
|
|
143
|
+
expect(1 + 1).toBe(2);
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
it('should handle string operations', () => {
|
|
147
|
+
const greeting = 'Hello World';
|
|
148
|
+
expect(greeting).toContain('World');
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
it('should work with arrays', () => {
|
|
152
|
+
const items = [1, 2, 3];
|
|
153
|
+
expect(items).toHaveLength(3);
|
|
154
|
+
expect(items).toContain(2);
|
|
155
|
+
});
|
|
156
|
+
});
|