imlil 1.0.1

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.
Files changed (175) hide show
  1. package/.eslintrc.cjs +40 -0
  2. package/DOCS.md +63 -0
  3. package/README.md +160 -0
  4. package/agentTestSandbox/cli-test-zone/README.md +0 -0
  5. package/agentTestSandbox/cli-test-zone/imlil.blueprint.json +5 -0
  6. package/agentTestSandbox/cli-test-zone/notes-warning.md +3 -0
  7. package/agentTestSandbox/cli-test-zone/package.json +0 -0
  8. package/agentTestSandbox/cli-test-zone/public/index.html +0 -0
  9. package/agentTestSandbox/cli-test-zone/src/App.js +0 -0
  10. package/agentTestSandbox/cli-test-zone/src/App.jsx +29 -0
  11. package/agentTestSandbox/cli-test-zone/src/__tests__/App.test.jsx +48 -0
  12. package/agentTestSandbox/cli-test-zone/src/components/AddTodo.js +0 -0
  13. package/agentTestSandbox/cli-test-zone/src/components/Navigation/Navigation.jsx +48 -0
  14. package/agentTestSandbox/cli-test-zone/src/components/Navigation/__tests__/Navigation.module.test.js +45 -0
  15. package/agentTestSandbox/cli-test-zone/src/components/Navigation/__tests__/Navigation.test.jsx +47 -0
  16. package/agentTestSandbox/cli-test-zone/src/components/Navigation.js +0 -0
  17. package/agentTestSandbox/cli-test-zone/src/components/TodoItem/TodoItem.jsx +41 -0
  18. package/agentTestSandbox/cli-test-zone/src/components/TodoItem/__tests__/TodoItem.test.jsx +65 -0
  19. package/agentTestSandbox/cli-test-zone/src/components/TodoItem.js +0 -0
  20. package/agentTestSandbox/cli-test-zone/src/components/TodoList/TodoList.module.css +62 -0
  21. package/agentTestSandbox/cli-test-zone/src/components/TodoList.js +0 -0
  22. package/agentTestSandbox/cli-test-zone/src/index.js +0 -0
  23. package/agentTestSandbox/cli-test-zone/src/pages/About.js +0 -0
  24. package/agentTestSandbox/cli-test-zone/src/pages/Home.js +0 -0
  25. package/agentTestSandbox/cli-test-zone/src/store/TodoContext.js +0 -0
  26. package/agentTestSandbox/cli-test-zone/src/styles/Todo.css +0 -0
  27. package/agentTestSandbox/cli-test-zone/src/styles/index.css +0 -0
  28. package/agentTestSandbox/cli-test-zone/src/utils/__tests__/localStorage.test.js +48 -0
  29. package/agentTestSandbox/cli-test-zone/src/utils/localStorage.js +38 -0
  30. package/agentTestSandbox/parallel-test/.env.example +0 -0
  31. package/agentTestSandbox/parallel-test/.eslintrc.json +0 -0
  32. package/agentTestSandbox/parallel-test/.github/workflows/__tests__/workflows.test.ts +115 -0
  33. package/agentTestSandbox/parallel-test/.github/workflows/cd.yml +0 -0
  34. package/agentTestSandbox/parallel-test/.github/workflows/ci.yml +4 -0
  35. package/agentTestSandbox/parallel-test/.imlil/plan-2026-02-08.md +186 -0
  36. package/agentTestSandbox/parallel-test/.prettierrc +0 -0
  37. package/agentTestSandbox/parallel-test/Dockerfile +0 -0
  38. package/agentTestSandbox/parallel-test/README.md +3 -0
  39. package/agentTestSandbox/parallel-test/ast.json +74 -0
  40. package/agentTestSandbox/parallel-test/docker-compose.yml +4 -0
  41. package/agentTestSandbox/parallel-test/jest.config.js +61 -0
  42. package/agentTestSandbox/parallel-test/k8s/__tests__/deployment.test.ts +168 -0
  43. package/agentTestSandbox/parallel-test/k8s/frontend-deployment.yaml +4 -0
  44. package/agentTestSandbox/parallel-test/nginx/nginx.conf +0 -0
  45. package/agentTestSandbox/parallel-test/package.json +50 -0
  46. package/agentTestSandbox/parallel-test/prisma/__tests__/schema.test.ts +176 -0
  47. package/agentTestSandbox/parallel-test/prisma/schema.prisma +109 -0
  48. package/agentTestSandbox/parallel-test/server/__tests__/controllers/dashboard.controller.test.ts +127 -0
  49. package/agentTestSandbox/parallel-test/server/__tests__/index.test.ts +60 -0
  50. package/agentTestSandbox/parallel-test/server/__tests__/models/user.model.test.ts +111 -0
  51. package/agentTestSandbox/parallel-test/server/config/__tests__/swagger.test.ts +128 -0
  52. package/agentTestSandbox/parallel-test/server/config/database.ts +0 -0
  53. package/agentTestSandbox/parallel-test/server/config/redis.ts +0 -0
  54. package/agentTestSandbox/parallel-test/server/config/swagger.ts +0 -0
  55. package/agentTestSandbox/parallel-test/server/controllers/__tests__/auth.controller.test.ts +178 -0
  56. package/agentTestSandbox/parallel-test/server/controllers/__tests__/user.controller.test.ts +105 -0
  57. package/agentTestSandbox/parallel-test/server/controllers/auth.controller.ts +148 -0
  58. package/agentTestSandbox/parallel-test/server/controllers/dashboard.controller.ts +137 -0
  59. package/agentTestSandbox/parallel-test/server/controllers/user.controller.ts +161 -0
  60. package/agentTestSandbox/parallel-test/server/index.ts +62 -0
  61. package/agentTestSandbox/parallel-test/server/middleware/__tests__/auth.middleware.test.ts +74 -0
  62. package/agentTestSandbox/parallel-test/server/middleware/auth.middleware.ts +55 -0
  63. package/agentTestSandbox/parallel-test/server/middleware/error.middleware.ts +0 -0
  64. package/agentTestSandbox/parallel-test/server/middleware/validation.middleware.ts +0 -0
  65. package/agentTestSandbox/parallel-test/server/models/analytics.model.ts +0 -0
  66. package/agentTestSandbox/parallel-test/server/models/profile.model.ts +0 -0
  67. package/agentTestSandbox/parallel-test/server/models/user.model.ts +78 -0
  68. package/agentTestSandbox/parallel-test/server/routes/auth.routes.ts +0 -0
  69. package/agentTestSandbox/parallel-test/server/routes/dashboard.routes.ts +0 -0
  70. package/agentTestSandbox/parallel-test/server/routes/user.routes.ts +0 -0
  71. package/agentTestSandbox/parallel-test/src/App.tsx +0 -0
  72. package/agentTestSandbox/parallel-test/src/__tests__/config.test.ts +127 -0
  73. package/agentTestSandbox/parallel-test/src/__tests__/index.test.tsx +36 -0
  74. package/agentTestSandbox/parallel-test/src/__tests__/setup.test.ts +34 -0
  75. package/agentTestSandbox/parallel-test/src/__tests__/setupTest.test.ts +44 -0
  76. package/agentTestSandbox/parallel-test/src/components/common/Button/Button.tsx +80 -0
  77. package/agentTestSandbox/parallel-test/src/components/common/Button/__tests__/Button.test.tsx +75 -0
  78. package/agentTestSandbox/parallel-test/src/components/common/Card/Card.tsx +0 -0
  79. package/agentTestSandbox/parallel-test/src/components/common/Input/Input.tsx +0 -0
  80. package/agentTestSandbox/parallel-test/src/components/common/Table/Table.tsx +0 -0
  81. package/agentTestSandbox/parallel-test/src/components/features/Authentication/LoginForm.tsx +75 -0
  82. package/agentTestSandbox/parallel-test/src/components/features/Authentication/RegisterForm.tsx +0 -0
  83. package/agentTestSandbox/parallel-test/src/components/features/Authentication/__tests__/LoginForm.test.tsx +101 -0
  84. package/agentTestSandbox/parallel-test/src/components/features/Dashboard/AnalyticsChart.tsx +0 -0
  85. package/agentTestSandbox/parallel-test/src/components/features/Dashboard/DashboardStats.tsx +81 -0
  86. package/agentTestSandbox/parallel-test/src/components/features/Dashboard/__tests__/DashboardStats.test.tsx +122 -0
  87. package/agentTestSandbox/parallel-test/src/components/layouts/Header.tsx +70 -0
  88. package/agentTestSandbox/parallel-test/src/components/layouts/MainLayout.tsx +0 -0
  89. package/agentTestSandbox/parallel-test/src/components/layouts/Sidebar.tsx +0 -0
  90. package/agentTestSandbox/parallel-test/src/components/layouts/__tests__/MainLayout.test.tsx +65 -0
  91. package/agentTestSandbox/parallel-test/src/hooks/__tests__/useAuth.test.ts +75 -0
  92. package/agentTestSandbox/parallel-test/src/hooks/useApi.ts +0 -0
  93. package/agentTestSandbox/parallel-test/src/hooks/useAuth.ts +54 -0
  94. package/agentTestSandbox/parallel-test/src/hooks/useTheme.ts +0 -0
  95. package/agentTestSandbox/parallel-test/src/index.tsx +0 -0
  96. package/agentTestSandbox/parallel-test/src/services/__tests__/api.service.test.ts +48 -0
  97. package/agentTestSandbox/parallel-test/src/services/analytics.service.ts +0 -0
  98. package/agentTestSandbox/parallel-test/src/services/api.service.ts +59 -0
  99. package/agentTestSandbox/parallel-test/src/services/api.ts +0 -0
  100. package/agentTestSandbox/parallel-test/src/services/auth.service.ts +0 -0
  101. package/agentTestSandbox/parallel-test/src/services/user.service.ts +0 -0
  102. package/agentTestSandbox/parallel-test/src/store/__tests__/store.test.ts +60 -0
  103. package/agentTestSandbox/parallel-test/src/store/index.ts +23 -0
  104. package/agentTestSandbox/parallel-test/src/store/slices/authSlice.ts +0 -0
  105. package/agentTestSandbox/parallel-test/src/store/slices/dashboardSlice.ts +0 -0
  106. package/agentTestSandbox/parallel-test/src/store/slices/userSlice.ts +0 -0
  107. package/agentTestSandbox/parallel-test/src/types/auth.types.ts +0 -0
  108. package/agentTestSandbox/parallel-test/src/types/dashboard.types.ts +0 -0
  109. package/agentTestSandbox/parallel-test/src/types/user.types.ts +0 -0
  110. package/agentTestSandbox/parallel-test/src/utils/constants.ts +0 -0
  111. package/agentTestSandbox/parallel-test/src/utils/formatters.ts +0 -0
  112. package/agentTestSandbox/parallel-test/src/utils/validation.ts +0 -0
  113. package/agentTestSandbox/parallel-test/src/views/Dashboard.tsx +0 -0
  114. package/agentTestSandbox/parallel-test/src/views/Login.tsx +31 -0
  115. package/agentTestSandbox/parallel-test/src/views/Profile.tsx +0 -0
  116. package/agentTestSandbox/parallel-test/src/views/Register.tsx +0 -0
  117. package/agentTestSandbox/parallel-test/src/views/Settings.tsx +0 -0
  118. package/agentTestSandbox/parallel-test/src/views/__tests__/Login.test.tsx +62 -0
  119. package/agentTestSandbox/parallel-test/src/vite-env.d.ts +1 -0
  120. package/agentTestSandbox/parallel-test/tailwind.config.js +0 -0
  121. package/agentTestSandbox/parallel-test/tests/integration/api/auth.test.ts +120 -0
  122. package/agentTestSandbox/parallel-test/tests/unit/components/Button.test.tsx +35 -0
  123. package/agentTestSandbox/parallel-test/tests/unit/config/jest.config.test.js +62 -0
  124. package/agentTestSandbox/parallel-test/tests/unit/config/jest.setup.test.js +52 -0
  125. package/agentTestSandbox/parallel-test/tests/unit/infrastructure/__tests__/docker-config.test.ts +107 -0
  126. package/agentTestSandbox/parallel-test/tsconfig.json +0 -0
  127. package/agentTestSandbox/zone2/Makefile +58 -0
  128. package/agentTestSandbox/zone2/README.md +0 -0
  129. package/agentTestSandbox/zone2/docs/API.md +0 -0
  130. package/agentTestSandbox/zone2/docs/CONTRIBUTING.md +0 -0
  131. package/agentTestSandbox/zone2/imlil.blueprint.json +5 -0
  132. package/agentTestSandbox/zone2/notes-warning.md +3 -0
  133. package/agentTestSandbox/zone2/src/calculator.c +0 -0
  134. package/agentTestSandbox/zone2/src/calculator.h +0 -0
  135. package/agentTestSandbox/zone2/src/core/__tests__/test_memory.c +89 -0
  136. package/agentTestSandbox/zone2/src/core/memory.c +60 -0
  137. package/agentTestSandbox/zone2/src/display.c +0 -0
  138. package/agentTestSandbox/zone2/src/display.h +0 -0
  139. package/agentTestSandbox/zone2/src/input_handler.c +0 -0
  140. package/agentTestSandbox/zone2/src/input_handler.h +0 -0
  141. package/agentTestSandbox/zone2/src/main.c +0 -0
  142. package/agentTestSandbox/zone2/src/utils/error_handling.c +0 -0
  143. package/agentTestSandbox/zone2/src/utils/error_handling.h +0 -0
  144. package/agentTestSandbox/zone2/src/utils/input.c +0 -0
  145. package/agentTestSandbox/zone2/src/utils/input.h +0 -0
  146. package/agentTestSandbox/zone2/src/utils/math_utils.c +0 -0
  147. package/agentTestSandbox/zone2/src/utils/math_utils.h +0 -0
  148. package/agentTestSandbox/zone2/src/utils/stack.c +0 -0
  149. package/agentTestSandbox/zone2/src/utils/stack.h +0 -0
  150. package/agentTestSandbox/zone2/src/utils.c +34 -0
  151. package/agentTestSandbox/zone2/tests/__tests__/test_makefile.c +58 -0
  152. package/agentTestSandbox/zone2/tests/calculator_tests.c +0 -0
  153. package/agentTestSandbox/zone2/tests/input_handler_tests.c +0 -0
  154. package/agentTestSandbox/zone2/tests/math_utils_tests.c +0 -0
  155. package/agentTestSandbox/zone2/tests/test_calculator.c +0 -0
  156. package/agentTestSandbox/zone2/tests/test_input.c +0 -0
  157. package/agentTestSandbox/zone2/tests/test_stack.c +0 -0
  158. package/agentTestSandbox/zone2/tests/test_utils.c +8 -0
  159. package/bin/cli.js +369 -0
  160. package/imlil.config.js +22 -0
  161. package/index.js +0 -0
  162. package/jest.config.js +5 -0
  163. package/package.json +45 -0
  164. package/src/__tests__/cli.test.js +5 -0
  165. package/src/actions/Action.js +125 -0
  166. package/src/agents/Agent.js +64 -0
  167. package/src/agents/Operator.js +147 -0
  168. package/src/agents/ScrumAgent.js +74 -0
  169. package/src/agents/SupervisorAgent.js +198 -0
  170. package/src/agents/ValidatorAgent.js +48 -0
  171. package/src/agents/coder.js +208 -0
  172. package/src/agents/worker.js +52 -0
  173. package/src/utils/db.js +40 -0
  174. package/src/utils/embedapi.js +19 -0
  175. package/test-api.js +24 -0
@@ -0,0 +1,127 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+
4
+ describe('Project Configuration Files', () => {
5
+ describe('package.json', () => {
6
+ let packageJson: any;
7
+
8
+ beforeAll(() => {
9
+ packageJson = JSON.parse(fs.readFileSync(path.resolve(__dirname, '../../package.json'), 'utf8'));
10
+ });
11
+
12
+ test('should have required fields', () => {
13
+ expect(packageJson).toHaveProperty('name');
14
+ expect(packageJson).toHaveProperty('version');
15
+ expect(packageJson).toHaveProperty('scripts');
16
+ expect(packageJson).toHaveProperty('dependencies');
17
+ expect(packageJson).toHaveProperty('devDependencies');
18
+ });
19
+
20
+ test('should have essential development scripts', () => {
21
+ expect(packageJson.scripts).toHaveProperty('start');
22
+ expect(packageJson.scripts).toHaveProperty('build');
23
+ expect(packageJson.scripts).toHaveProperty('test');
24
+ expect(packageJson.scripts).toHaveProperty('lint');
25
+ });
26
+ });
27
+
28
+ describe('.gitignore', () => {
29
+ let gitignore: string;
30
+
31
+ beforeAll(() => {
32
+ gitignore = fs.readFileSync(path.resolve(__dirname, '../../.gitignore'), 'utf8');
33
+ });
34
+
35
+ test('should contain essential ignore patterns', () => {
36
+ const requiredPatterns = [
37
+ 'node_modules',
38
+ 'dist',
39
+ 'build',
40
+ '.env',
41
+ 'coverage',
42
+ '.DS_Store'
43
+ ];
44
+
45
+ requiredPatterns.forEach(pattern => {
46
+ expect(gitignore).toMatch(new RegExp(pattern, 'i'));
47
+ });
48
+ });
49
+ });
50
+
51
+ describe('.env.example', () => {
52
+ let envExample: string;
53
+
54
+ beforeAll(() => {
55
+ envExample = fs.readFileSync(path.resolve(__dirname, '../../.env.example'), 'utf8');
56
+ });
57
+
58
+ test('should contain required environment variables', () => {
59
+ const requiredVars = [
60
+ 'NODE_ENV',
61
+ 'PORT',
62
+ 'DATABASE_URL',
63
+ 'REDIS_URL',
64
+ 'JWT_SECRET'
65
+ ];
66
+
67
+ requiredVars.forEach(variable => {
68
+ expect(envExample).toMatch(new RegExp(`^${variable}=`, 'm'));
69
+ });
70
+ });
71
+ });
72
+
73
+ describe('.eslintrc.json', () => {
74
+ let eslintConfig: any;
75
+
76
+ beforeAll(() => {
77
+ eslintConfig = JSON.parse(fs.readFileSync(path.resolve(__dirname, '../../.eslintrc.json'), 'utf8'));
78
+ });
79
+
80
+ test('should have essential ESLint configuration', () => {
81
+ expect(eslintConfig).toHaveProperty('extends');
82
+ expect(eslintConfig.extends).toContain('airbnb');
83
+ expect(eslintConfig).toHaveProperty('rules');
84
+ expect(eslintConfig).toHaveProperty('env');
85
+ });
86
+ });
87
+
88
+ describe('.prettierrc', () => {
89
+ let prettierConfig: any;
90
+
91
+ beforeAll(() => {
92
+ prettierConfig = JSON.parse(fs.readFileSync(path.resolve(__dirname, '../../.prettierrc'), 'utf8'));
93
+ });
94
+
95
+ test('should have standard Prettier configuration', () => {
96
+ expect(prettierConfig).toHaveProperty('semi');
97
+ expect(prettierConfig).toHaveProperty('singleQuote');
98
+ expect(prettierConfig).toHaveProperty('tabWidth');
99
+ expect(prettierConfig).toHaveProperty('printWidth');
100
+ });
101
+ });
102
+
103
+ describe('tsconfig.json', () => {
104
+ let tsConfig: any;
105
+
106
+ beforeAll(() => {
107
+ tsConfig = JSON.parse(fs.readFileSync(path.resolve(__dirname, '../../tsconfig.json'), 'utf8'));
108
+ });
109
+
110
+ test('should have essential TypeScript configuration', () => {
111
+ expect(tsConfig).toHaveProperty('compilerOptions');
112
+ expect(tsConfig.compilerOptions).toHaveProperty('target');
113
+ expect(tsConfig.compilerOptions).toHaveProperty('module');
114
+ expect(tsConfig.compilerOptions).toHaveProperty('strict');
115
+ expect(tsConfig.compilerOptions).toHaveProperty('esModuleInterop');
116
+ expect(tsConfig.compilerOptions).toHaveProperty('skipLibCheck');
117
+ expect(tsConfig.compilerOptions).toHaveProperty('forceConsistentCasingInFileNames');
118
+ });
119
+
120
+ test('should include and exclude appropriate paths', () => {
121
+ expect(tsConfig).toHaveProperty('include');
122
+ expect(tsConfig).toHaveProperty('exclude');
123
+ expect(tsConfig.include).toContain('src/**/*');
124
+ expect(tsConfig.exclude).toContain('node_modules');
125
+ });
126
+ });
127
+ });
@@ -0,0 +1,36 @@
1
+ import { render } from '@testing-library/react';
2
+ import React from 'react';
3
+ import ReactDOM from 'react-dom/client';
4
+ import App from '../App';
5
+
6
+ // Mock ReactDOM
7
+ jest.mock('react-dom/client', () => ({
8
+ createRoot: jest.fn(() => ({
9
+ render: jest.fn(),
10
+ })),
11
+ }));
12
+
13
+ describe('Application Entry Point', () => {
14
+ beforeEach(() => {
15
+ // Clear the document body before each test
16
+ document.body.innerHTML = '';
17
+ // Create root element
18
+ const root = document.createElement('div');
19
+ root.id = 'root';
20
+ document.body.appendChild(root);
21
+ });
22
+
23
+ it('should render the App component in the root element', () => {
24
+ // Import the index file
25
+ require('../index');
26
+
27
+ // Verify that createRoot was called with the root element
28
+ expect(ReactDOM.createRoot).toHaveBeenCalledWith(
29
+ document.getElementById('root')
30
+ );
31
+
32
+ // Verify that render was called with the App component
33
+ const root = ReactDOM.createRoot(document.getElementById('root'));
34
+ expect(root.render).toHaveBeenCalled();
35
+ });
36
+ });
@@ -0,0 +1,34 @@
1
+ import { jest } from '@jest/globals';
2
+
3
+ describe('Jest Configuration', () => {
4
+ // Test if Jest is properly installed and configured
5
+ test('Jest should be properly configured', () => {
6
+ expect(jest).toBeDefined();
7
+ });
8
+
9
+ // Test if the test environment is working
10
+ test('Test environment should be properly set up', () => {
11
+ const sum = (a: number, b: number) => a + b;
12
+ expect(sum(1, 2)).toBe(3);
13
+ });
14
+
15
+ // Test if test runner can handle async operations
16
+ test('Async operations should work', async () => {
17
+ const asyncFunction = async () => 'test';
18
+ const result = await asyncFunction();
19
+ expect(result).toBe('test');
20
+ });
21
+
22
+ // Test if TypeScript configuration is working
23
+ test('TypeScript configuration should work', () => {
24
+ interface TestInterface {
25
+ value: string;
26
+ }
27
+
28
+ const testObject: TestInterface = {
29
+ value: 'test'
30
+ };
31
+
32
+ expect(testObject.value).toBe('test');
33
+ });
34
+ });
@@ -0,0 +1,44 @@
1
+ import { describe, expect, test } from '@jest/globals';
2
+
3
+ describe('Jest Testing Environment Setup', () => {
4
+ test('Jest is properly configured', () => {
5
+ expect(true).toBe(true);
6
+ });
7
+
8
+ test('Testing environment has required globals', () => {
9
+ expect(typeof describe).toBe('function');
10
+ expect(typeof test).toBe('function');
11
+ expect(typeof expect).toBe('function');
12
+ });
13
+
14
+ // Test that we can import React components
15
+ test('React environment is properly set up', () => {
16
+ const React = require('react');
17
+ expect(React).toBeDefined();
18
+ expect(typeof React.createElement).toBe('function');
19
+ });
20
+
21
+ // Test basic DOM manipulation capabilities
22
+ test('JSDOM environment is properly configured', () => {
23
+ document.body.innerHTML = '<div id="test">Test</div>';
24
+ const element = document.getElementById('test');
25
+ expect(element).not.toBeNull();
26
+ expect(element?.textContent).toBe('Test');
27
+ });
28
+
29
+ // Test that we can handle async operations
30
+ test('Async testing capabilities work', async () => {
31
+ const asyncFunction = () => Promise.resolve('success');
32
+ const result = await asyncFunction();
33
+ expect(result).toBe('success');
34
+ });
35
+
36
+ // Test TypeScript compilation
37
+ test('TypeScript types are properly recognized', () => {
38
+ interface TestInterface {
39
+ prop: string;
40
+ }
41
+ const testObject: TestInterface = { prop: 'test' };
42
+ expect(testObject.prop).toBe('test');
43
+ });
44
+ });
@@ -0,0 +1,80 @@
1
+ import React, { ButtonHTMLAttributes } from 'react';
2
+ import classNames from 'classnames';
3
+
4
+ export interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
5
+ variant?: 'primary' | 'secondary' | 'outline' | 'danger';
6
+ size?: 'sm' | 'md' | 'lg';
7
+ isLoading?: boolean;
8
+ fullWidth?: boolean;
9
+ }
10
+
11
+ export const Button: React.FC<ButtonProps> = ({
12
+ children,
13
+ className,
14
+ variant = 'primary',
15
+ size = 'md',
16
+ isLoading = false,
17
+ fullWidth = false,
18
+ disabled,
19
+ ...props
20
+ }) => {
21
+ const baseStyles = 'inline-flex items-center justify-center rounded-md font-medium transition-colors focus:outline-none focus:ring-2 focus:ring-offset-2';
22
+
23
+ const variants = {
24
+ primary: 'bg-blue-600 text-white hover:bg-blue-700 focus:ring-blue-500',
25
+ secondary: 'bg-gray-200 text-gray-900 hover:bg-gray-300 focus:ring-gray-500',
26
+ outline: 'border-2 border-gray-300 text-gray-700 hover:bg-gray-50 focus:ring-gray-500',
27
+ danger: 'bg-red-600 text-white hover:bg-red-700 focus:ring-red-500',
28
+ };
29
+
30
+ const sizes = {
31
+ sm: 'px-3 py-1.5 text-sm',
32
+ md: 'px-4 py-2 text-base',
33
+ lg: 'px-6 py-3 text-lg',
34
+ };
35
+
36
+ const buttonClasses = classNames(
37
+ baseStyles,
38
+ variants[variant],
39
+ sizes[size],
40
+ {
41
+ 'opacity-75 cursor-not-allowed': disabled || isLoading,
42
+ 'w-full': fullWidth,
43
+ },
44
+ className
45
+ );
46
+
47
+ return (
48
+ <button
49
+ className={buttonClasses}
50
+ disabled={disabled || isLoading}
51
+ {...props}
52
+ >
53
+ {isLoading ? (
54
+ <svg
55
+ className="animate-spin -ml-1 mr-2 h-4 w-4 text-current"
56
+ xmlns="http://www.w3.org/2000/svg"
57
+ fill="none"
58
+ viewBox="0 0 24 24"
59
+ >
60
+ <circle
61
+ className="opacity-25"
62
+ cx="12"
63
+ cy="12"
64
+ r="10"
65
+ stroke="currentColor"
66
+ strokeWidth="4"
67
+ />
68
+ <path
69
+ className="opacity-75"
70
+ fill="currentColor"
71
+ d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
72
+ />
73
+ </svg>
74
+ ) : null}
75
+ {children}
76
+ </button>
77
+ );
78
+ };
79
+
80
+ export default Button;
@@ -0,0 +1,75 @@
1
+ import React from 'react';
2
+ import { render, fireEvent, screen } from '@testing-library/react';
3
+ import '@testing-library/jest-dom';
4
+ import Button from '../Button';
5
+
6
+ describe('Button Component', () => {
7
+ // Basic rendering tests
8
+ it('renders button with correct text', () => {
9
+ render(<Button>Click me</Button>);
10
+ expect(screen.getByText('Click me')).toBeInTheDocument();
11
+ });
12
+
13
+ // Props tests
14
+ it('applies variant classes correctly', () => {
15
+ const { rerender } = render(<Button variant="primary">Primary Button</Button>);
16
+ expect(screen.getByRole('button')).toHaveClass('btn-primary');
17
+
18
+ rerender(<Button variant="secondary">Secondary Button</Button>);
19
+ expect(screen.getByRole('button')).toHaveClass('btn-secondary');
20
+ });
21
+
22
+ it('applies size classes correctly', () => {
23
+ const { rerender } = render(<Button size="sm">Small Button</Button>);
24
+ expect(screen.getByRole('button')).toHaveClass('btn-sm');
25
+
26
+ rerender(<Button size="lg">Large Button</Button>);
27
+ expect(screen.getByRole('button')).toHaveClass('btn-lg');
28
+ });
29
+
30
+ // Disabled state test
31
+ it('handles disabled state correctly', () => {
32
+ render(<Button disabled>Disabled Button</Button>);
33
+ const button = screen.getByRole('button');
34
+ expect(button).toBeDisabled();
35
+ expect(button).toHaveClass('btn-disabled');
36
+ });
37
+
38
+ // Click handler test
39
+ it('calls onClick handler when clicked', () => {
40
+ const handleClick = jest.fn();
41
+ render(<Button onClick={handleClick}>Click me</Button>);
42
+
43
+ fireEvent.click(screen.getByText('Click me'));
44
+ expect(handleClick).toHaveBeenCalledTimes(1);
45
+ });
46
+
47
+ // Loading state test
48
+ it('shows loading state when isLoading prop is true', () => {
49
+ render(<Button isLoading>Loading Button</Button>);
50
+ expect(screen.getByRole('button')).toHaveClass('btn-loading');
51
+ expect(screen.getByTestId('loading-spinner')).toBeInTheDocument();
52
+ });
53
+
54
+ // Full width test
55
+ it('applies full width class when fullWidth prop is true', () => {
56
+ render(<Button fullWidth>Full Width Button</Button>);
57
+ expect(screen.getByRole('button')).toHaveClass('w-full');
58
+ });
59
+
60
+ // Icon button test
61
+ it('renders with icon correctly', () => {
62
+ render(
63
+ <Button startIcon={<span data-testid="start-icon">→</span>}>
64
+ Icon Button
65
+ </Button>
66
+ );
67
+ expect(screen.getByTestId('start-icon')).toBeInTheDocument();
68
+ });
69
+
70
+ // Accessibility test
71
+ it('maintains accessibility attributes', () => {
72
+ render(<Button aria-label="Accessible Button">Click me</Button>);
73
+ expect(screen.getByRole('button')).toHaveAttribute('aria-label', 'Accessible Button');
74
+ });
75
+ });
@@ -0,0 +1,75 @@
1
+ import React from 'react';
2
+ import { useForm } from 'react-hook-form';
3
+ import { yupResolver } from '@hookform/resolvers/yup';
4
+ import * as yup from 'yup';
5
+ import { useDispatch } from 'react-redux';
6
+ import { Button } from '../../common/Button/Button';
7
+ import { Input } from '../../common/Input/Input';
8
+ import { useAuth } from '../../../hooks/useAuth';
9
+ import { login } from '../../../store/slices/authSlice';
10
+
11
+ interface LoginFormData {
12
+ email: string;
13
+ password: string;
14
+ }
15
+
16
+ const loginSchema = yup.object({
17
+ email: yup
18
+ .string()
19
+ .email('Please enter a valid email')
20
+ .required('Email is required'),
21
+ password: yup
22
+ .string()
23
+ .min(8, 'Password must be at least 8 characters')
24
+ .required('Password is required'),
25
+ });
26
+
27
+ export const LoginForm: React.FC = () => {
28
+ const dispatch = useDispatch();
29
+ const { loginUser } = useAuth();
30
+
31
+ const {
32
+ register,
33
+ handleSubmit,
34
+ formState: { errors, isSubmitting },
35
+ } = useForm<LoginFormData>({
36
+ resolver: yupResolver(loginSchema),
37
+ });
38
+
39
+ const onSubmit = async (data: LoginFormData) => {
40
+ try {
41
+ const response = await loginUser(data);
42
+ dispatch(login(response));
43
+ } catch (error) {
44
+ console.error('Login failed:', error);
45
+ }
46
+ };
47
+
48
+ return (
49
+ <form onSubmit={handleSubmit(onSubmit)} className="space-y-4">
50
+ <div>
51
+ <Input
52
+ type="email"
53
+ label="Email"
54
+ error={errors.email?.message}
55
+ {...register('email')}
56
+ />
57
+ </div>
58
+ <div>
59
+ <Input
60
+ type="password"
61
+ label="Password"
62
+ error={errors.password?.message}
63
+ {...register('password')}
64
+ />
65
+ </div>
66
+ <Button
67
+ type="submit"
68
+ disabled={isSubmitting}
69
+ className="w-full"
70
+ >
71
+ {isSubmitting ? 'Logging in...' : 'Login'}
72
+ </Button>
73
+ </form>
74
+ );
75
+ };
@@ -0,0 +1,101 @@
1
+ import React from 'react';
2
+ import { render, screen, fireEvent, waitFor } from '@testing-library/react';
3
+ import { Provider } from 'react-redux';
4
+ import { createStore } from '@reduxjs/toolkit';
5
+ import { BrowserRouter } from 'react-router-dom';
6
+ import LoginForm from '../LoginForm';
7
+ import { authSlice } from '../../../../store/slices/authSlice';
8
+
9
+ // Mock the auth service
10
+ jest.mock('../../../../services/auth.service', () => ({
11
+ login: jest.fn(() => Promise.resolve({ token: 'fake-token' }))
12
+ }));
13
+
14
+ const mockStore = createStore(authSlice.reducer);
15
+
16
+ const renderLoginForm = () => {
17
+ render(
18
+ <Provider store={mockStore}>
19
+ <BrowserRouter>
20
+ <LoginForm />
21
+ </BrowserRouter>
22
+ </Provider>
23
+ );
24
+ };
25
+
26
+ describe('LoginForm Component', () => {
27
+ beforeEach(() => {
28
+ jest.clearAllMocks();
29
+ });
30
+
31
+ test('renders login form with required fields', () => {
32
+ renderLoginForm();
33
+
34
+ expect(screen.getByLabelText(/email/i)).toBeInTheDocument();
35
+ expect(screen.getByLabelText(/password/i)).toBeInTheDocument();
36
+ expect(screen.getByRole('button', { name: /sign in/i })).toBeInTheDocument();
37
+ });
38
+
39
+ test('displays validation errors for empty fields', async () => {
40
+ renderLoginForm();
41
+
42
+ const submitButton = screen.getByRole('button', { name: /sign in/i });
43
+ fireEvent.click(submitButton);
44
+
45
+ await waitFor(() => {
46
+ expect(screen.getByText(/email is required/i)).toBeInTheDocument();
47
+ expect(screen.getByText(/password is required/i)).toBeInTheDocument();
48
+ });
49
+ });
50
+
51
+ test('displays validation error for invalid email format', async () => {
52
+ renderLoginForm();
53
+
54
+ const emailInput = screen.getByLabelText(/email/i);
55
+ fireEvent.change(emailInput, { target: { value: 'invalid-email' } });
56
+
57
+ const submitButton = screen.getByRole('button', { name: /sign in/i });
58
+ fireEvent.click(submitButton);
59
+
60
+ await waitFor(() => {
61
+ expect(screen.getByText(/invalid email format/i)).toBeInTheDocument();
62
+ });
63
+ });
64
+
65
+ test('submits form with valid data', async () => {
66
+ renderLoginForm();
67
+
68
+ const emailInput = screen.getByLabelText(/email/i);
69
+ const passwordInput = screen.getByLabelText(/password/i);
70
+ const submitButton = screen.getByRole('button', { name: /sign in/i });
71
+
72
+ fireEvent.change(emailInput, { target: { value: 'test@example.com' } });
73
+ fireEvent.change(passwordInput, { target: { value: 'Password123!' } });
74
+ fireEvent.click(submitButton);
75
+
76
+ await waitFor(() => {
77
+ expect(screen.queryByText(/email is required/i)).not.toBeInTheDocument();
78
+ expect(screen.queryByText(/password is required/i)).not.toBeInTheDocument();
79
+ });
80
+ });
81
+
82
+ test('displays error message on failed login', async () => {
83
+ const mockError = new Error('Invalid credentials');
84
+ jest.spyOn(console, 'error').mockImplementation(() => {});
85
+ require('../../../../services/auth.service').login.mockRejectedValueOnce(mockError);
86
+
87
+ renderLoginForm();
88
+
89
+ const emailInput = screen.getByLabelText(/email/i);
90
+ const passwordInput = screen.getByLabelText(/password/i);
91
+ const submitButton = screen.getByRole('button', { name: /sign in/i });
92
+
93
+ fireEvent.change(emailInput, { target: { value: 'test@example.com' } });
94
+ fireEvent.change(passwordInput, { target: { value: 'Password123!' } });
95
+ fireEvent.click(submitButton);
96
+
97
+ await waitFor(() => {
98
+ expect(screen.getByText(/invalid credentials/i)).toBeInTheDocument();
99
+ });
100
+ });
101
+ });
@@ -0,0 +1,81 @@
1
+ import React from 'react';
2
+ import { useSelector } from 'react-redux';
3
+ import { Card } from '@/components/common/Card/Card';
4
+ import { useApi } from '@/hooks/useApi';
5
+ import type { RootState } from '@/store';
6
+ import { formatNumber, formatCurrency } from '@/utils/formatters';
7
+
8
+ interface StatCardProps {
9
+ title: string;
10
+ value: string | number;
11
+ change: number;
12
+ icon: React.ReactNode;
13
+ }
14
+
15
+ const StatCard: React.FC<StatCardProps> = ({ title, value, change, icon }) => (
16
+ <Card className="p-6">
17
+ <div className="flex items-center justify-between">
18
+ <div>
19
+ <p className="text-gray-500 text-sm font-medium">{title}</p>
20
+ <h3 className="text-2xl font-bold mt-2">{value}</h3>
21
+ <p className={`text-sm mt-2 ${change >= 0 ? 'text-green-500' : 'text-red-500'}`}>
22
+ {change >= 0 ? '+' : ''}{change}% from last month
23
+ </p>
24
+ </div>
25
+ <div className="p-3 bg-blue-100 rounded-full">
26
+ {icon}
27
+ </div>
28
+ </div>
29
+ </Card>
30
+ );
31
+
32
+ export const DashboardStats: React.FC = () => {
33
+ const { data: statsData, isLoading, error } = useApi('/api/dashboard/stats');
34
+ const { currentPeriod } = useSelector((state: RootState) => state.dashboard);
35
+
36
+ if (isLoading) return <div>Loading statistics...</div>;
37
+ if (error) return <div>Error loading statistics</div>;
38
+
39
+ const stats = [
40
+ {
41
+ title: 'Total Revenue',
42
+ value: formatCurrency(statsData?.revenue || 0),
43
+ change: statsData?.revenueChange || 0,
44
+ icon: <svg className="w-6 h-6 text-blue-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
45
+ <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 8c-1.657 0-3 .895-3 2s1.343 2 3 2 3 .895 3 2-1.343 2-3 2m0-8c1.11 0 2.08.402 2.599 1M12 8V7m0 1v8m0 0v1m0-1c-1.11 0-2.08-.402-2.599-1M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
46
+ </svg>
47
+ },
48
+ {
49
+ title: 'Active Users',
50
+ value: formatNumber(statsData?.activeUsers || 0),
51
+ change: statsData?.userChange || 0,
52
+ icon: <svg className="w-6 h-6 text-blue-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
53
+ <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 4.354a4 4 0 110 5.292M15 21H3v-1a6 6 0 0112 0v1zm0 0h6v-1a6 6 0 00-9-5.197M13 7a4 4 0 11-8 0 4 4 0 018 0z" />
54
+ </svg>
55
+ },
56
+ {
57
+ title: 'Conversion Rate',
58
+ value: `${statsData?.conversionRate || 0}%`,
59
+ change: statsData?.conversionChange || 0,
60
+ icon: <svg className="w-6 h-6 text-blue-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
61
+ <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M13 7h8m0 0v8m0-8l-8 8-4-4-6 6" />
62
+ </svg>
63
+ },
64
+ {
65
+ title: 'Average Order Value',
66
+ value: formatCurrency(statsData?.avgOrderValue || 0),
67
+ change: statsData?.avgOrderChange || 0,
68
+ icon: <svg className="w-6 h-6 text-blue-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
69
+ <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M16 11V7a4 4 0 00-8 0v4M5 9h14l1 12H4L5 9z" />
70
+ </svg>
71
+ }
72
+ ];
73
+
74
+ return (
75
+ <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
76
+ {stats.map((stat, index) => (
77
+ <StatCard key={index} {...stat} />
78
+ ))}
79
+ </div>
80
+ );
81
+ };