cucumberstudio-mcp 1.1.0
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/.env.example +36 -0
- package/.github/workflows/pr-checks.yml +41 -0
- package/.github/workflows/release.yml +194 -0
- package/.prettierignore +26 -0
- package/.prettierrc +14 -0
- package/CLAUDE.md +140 -0
- package/Dockerfile +50 -0
- package/Dockerfile.dev +31 -0
- package/LICENSE +21 -0
- package/README.md +395 -0
- package/build/api/client.d.ts +49 -0
- package/build/api/client.d.ts.map +1 -0
- package/build/api/client.js +204 -0
- package/build/api/client.js.map +1 -0
- package/build/api/types.d.ts +113 -0
- package/build/api/types.d.ts.map +1 -0
- package/build/api/types.js +2 -0
- package/build/api/types.js.map +1 -0
- package/build/config/settings.d.ts +123 -0
- package/build/config/settings.d.ts.map +1 -0
- package/build/config/settings.js +97 -0
- package/build/config/settings.js.map +1 -0
- package/build/constants.d.ts +16 -0
- package/build/constants.d.ts.map +1 -0
- package/build/constants.js +24 -0
- package/build/constants.js.map +1 -0
- package/build/generated/version.d.ts +3 -0
- package/build/generated/version.d.ts.map +1 -0
- package/build/generated/version.js +5 -0
- package/build/generated/version.js.map +1 -0
- package/build/index.d.ts +3 -0
- package/build/index.d.ts.map +1 -0
- package/build/index.js +81 -0
- package/build/index.js.map +1 -0
- package/build/mcp-server.d.ts +6 -0
- package/build/mcp-server.d.ts.map +1 -0
- package/build/mcp-server.js +263 -0
- package/build/mcp-server.js.map +1 -0
- package/build/tools/action-words.d.ts +18 -0
- package/build/tools/action-words.d.ts.map +1 -0
- package/build/tools/action-words.js +191 -0
- package/build/tools/action-words.js.map +1 -0
- package/build/tools/projects.d.ts +19 -0
- package/build/tools/projects.d.ts.map +1 -0
- package/build/tools/projects.js +123 -0
- package/build/tools/projects.js.map +1 -0
- package/build/tools/scenarios.d.ts +18 -0
- package/build/tools/scenarios.d.ts.map +1 -0
- package/build/tools/scenarios.js +194 -0
- package/build/tools/scenarios.js.map +1 -0
- package/build/tools/test-runs.d.ts +21 -0
- package/build/tools/test-runs.d.ts.map +1 -0
- package/build/tools/test-runs.js +324 -0
- package/build/tools/test-runs.js.map +1 -0
- package/build/transports/http.d.ts +38 -0
- package/build/transports/http.d.ts.map +1 -0
- package/build/transports/http.js +381 -0
- package/build/transports/http.js.map +1 -0
- package/build/transports/index.d.ts +22 -0
- package/build/transports/index.d.ts.map +1 -0
- package/build/transports/index.js +10 -0
- package/build/transports/index.js.map +1 -0
- package/build/transports/stdio.d.ts +13 -0
- package/build/transports/stdio.d.ts.map +1 -0
- package/build/transports/stdio.js +24 -0
- package/build/transports/stdio.js.map +1 -0
- package/build/utils/errors.d.ts +10 -0
- package/build/utils/errors.d.ts.map +1 -0
- package/build/utils/errors.js +35 -0
- package/build/utils/errors.js.map +1 -0
- package/build/utils/logger-constants.d.ts +15 -0
- package/build/utils/logger-constants.d.ts.map +1 -0
- package/build/utils/logger-constants.js +16 -0
- package/build/utils/logger-constants.js.map +1 -0
- package/build/utils/logger.d.ts +55 -0
- package/build/utils/logger.d.ts.map +1 -0
- package/build/utils/logger.js +113 -0
- package/build/utils/logger.js.map +1 -0
- package/build/utils/validation.d.ts +89 -0
- package/build/utils/validation.d.ts.map +1 -0
- package/build/utils/validation.js +78 -0
- package/build/utils/validation.js.map +1 -0
- package/docker-compose.yml +20 -0
- package/eslint.config.js +97 -0
- package/package.json +92 -0
- package/scripts/generate-version.js +31 -0
- package/src/api/client.ts +286 -0
- package/src/api/types.ts +137 -0
- package/src/config/settings.ts +113 -0
- package/src/constants.ts +29 -0
- package/src/index.ts +99 -0
- package/src/mcp-server.ts +342 -0
- package/src/tools/action-words.ts +240 -0
- package/src/tools/projects.ts +144 -0
- package/src/tools/scenarios.ts +231 -0
- package/src/tools/test-runs.ts +400 -0
- package/src/transports/http.ts +467 -0
- package/src/transports/index.ts +26 -0
- package/src/transports/stdio.ts +28 -0
- package/src/utils/errors.ts +45 -0
- package/src/utils/logger-constants.ts +18 -0
- package/src/utils/logger.ts +150 -0
- package/src/utils/validation.ts +94 -0
- package/test/api/client-with-msw.test.ts +122 -0
- package/test/api/client.test.ts +326 -0
- package/test/api/types.test.ts +88 -0
- package/test/config/settings.test.ts +204 -0
- package/test/mocks/data/action-words.ts +40 -0
- package/test/mocks/data/index.ts +13 -0
- package/test/mocks/data/projects.ts +38 -0
- package/test/mocks/data/scenarios.ts +53 -0
- package/test/mocks/data/test-runs.ts +101 -0
- package/test/mocks/handlers/action-words.ts +52 -0
- package/test/mocks/handlers/index.ts +10 -0
- package/test/mocks/handlers/projects.ts +45 -0
- package/test/mocks/handlers/scenarios.ts +72 -0
- package/test/mocks/handlers/test-runs.ts +106 -0
- package/test/mocks/server.ts +26 -0
- package/test/setup/vitest.setup.ts +18 -0
- package/test/tools/coverage-boost.test.ts +252 -0
- package/test/tools/projects.test.ts +290 -0
- package/test/tools/tools-basic.test.ts +146 -0
- package/test/transports/http-basic.test.ts +87 -0
- package/test/transports/http-simple.test.ts +33 -0
- package/test/transports/stdio.test.ts +73 -0
- package/test/utils/errors.test.ts +117 -0
- package/test/utils/validation.test.ts +261 -0
- package/tsconfig.build.json +8 -0
- package/tsconfig.json +27 -0
- package/vitest.config.ts +43 -0
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest'
|
|
2
|
+
|
|
3
|
+
describe('API Types', () => {
|
|
4
|
+
it('should be able to import types module', async () => {
|
|
5
|
+
const types = await import('@/api/types.js')
|
|
6
|
+
expect(types).toBeDefined()
|
|
7
|
+
})
|
|
8
|
+
|
|
9
|
+
it('should define type interfaces correctly', () => {
|
|
10
|
+
// Test that we can create objects matching the expected types
|
|
11
|
+
const mockApiResponse = {
|
|
12
|
+
data: [],
|
|
13
|
+
meta: {
|
|
14
|
+
total: 0,
|
|
15
|
+
page: 1,
|
|
16
|
+
pageSize: 10,
|
|
17
|
+
},
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const mockProject = {
|
|
21
|
+
id: 1,
|
|
22
|
+
name: 'Test Project',
|
|
23
|
+
description: 'A test project',
|
|
24
|
+
created_at: '2024-01-01T00:00:00Z',
|
|
25
|
+
updated_at: '2024-01-01T00:00:00Z',
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const mockScenario = {
|
|
29
|
+
id: 1,
|
|
30
|
+
name: 'Test Scenario',
|
|
31
|
+
description: 'A test scenario',
|
|
32
|
+
project_id: 1,
|
|
33
|
+
folder_id: null,
|
|
34
|
+
tags: ['test'],
|
|
35
|
+
created_at: '2024-01-01T00:00:00Z',
|
|
36
|
+
updated_at: '2024-01-01T00:00:00Z',
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const mockActionWord = {
|
|
40
|
+
id: 1,
|
|
41
|
+
name: 'Test Action Word',
|
|
42
|
+
description: 'A test action word',
|
|
43
|
+
project_id: 1,
|
|
44
|
+
tags: ['test'],
|
|
45
|
+
created_at: '2024-01-01T00:00:00Z',
|
|
46
|
+
updated_at: '2024-01-01T00:00:00Z',
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const mockTestRun = {
|
|
50
|
+
id: 1,
|
|
51
|
+
name: 'Test Run',
|
|
52
|
+
project_id: 1,
|
|
53
|
+
status: 'passed',
|
|
54
|
+
created_at: '2024-01-01T00:00:00Z',
|
|
55
|
+
updated_at: '2024-01-01T00:00:00Z',
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const mockBuild = {
|
|
59
|
+
id: 1,
|
|
60
|
+
name: 'Test Build',
|
|
61
|
+
project_id: 1,
|
|
62
|
+
status: 'success',
|
|
63
|
+
created_at: '2024-01-01T00:00:00Z',
|
|
64
|
+
updated_at: '2024-01-01T00:00:00Z',
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const mockError = {
|
|
68
|
+
message: 'Test error',
|
|
69
|
+
code: 'TEST_ERROR',
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const mockListParams = {
|
|
73
|
+
page: 1,
|
|
74
|
+
pageSize: 10,
|
|
75
|
+
tags: ['test'],
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Verify objects can be created (TypeScript compilation test)
|
|
79
|
+
expect(mockApiResponse).toBeDefined()
|
|
80
|
+
expect(mockProject).toBeDefined()
|
|
81
|
+
expect(mockScenario).toBeDefined()
|
|
82
|
+
expect(mockActionWord).toBeDefined()
|
|
83
|
+
expect(mockTestRun).toBeDefined()
|
|
84
|
+
expect(mockBuild).toBeDefined()
|
|
85
|
+
expect(mockError).toBeDefined()
|
|
86
|
+
expect(mockListParams).toBeDefined()
|
|
87
|
+
})
|
|
88
|
+
})
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest'
|
|
2
|
+
|
|
3
|
+
import { ConfigManager, configManager } from '../../src/config/settings.js'
|
|
4
|
+
import { SERVER_VERSION } from '../../src/constants.js'
|
|
5
|
+
|
|
6
|
+
// Mock dotenv to prevent loading actual .env file
|
|
7
|
+
vi.mock('dotenv', () => ({
|
|
8
|
+
config: vi.fn(() => ({ parsed: {} })),
|
|
9
|
+
}))
|
|
10
|
+
|
|
11
|
+
describe('ConfigManager', () => {
|
|
12
|
+
let originalEnv: NodeJS.ProcessEnv
|
|
13
|
+
|
|
14
|
+
beforeEach(() => {
|
|
15
|
+
originalEnv = { ...process.env }
|
|
16
|
+
// Clear ALL cucumber studio and MCP environment variables
|
|
17
|
+
Object.keys(process.env).forEach((key) => {
|
|
18
|
+
if (key.startsWith('CUCUMBERSTUDIO_') || key.startsWith('MCP_') || key.startsWith('LOG_')) {
|
|
19
|
+
delete process.env[key]
|
|
20
|
+
}
|
|
21
|
+
})
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
afterEach(() => {
|
|
25
|
+
process.env = originalEnv
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
describe('loadFromEnvironment', () => {
|
|
29
|
+
it('should load valid configuration from environment variables', () => {
|
|
30
|
+
process.env.CUCUMBERSTUDIO_ACCESS_TOKEN = 'test-token'
|
|
31
|
+
process.env.CUCUMBERSTUDIO_CLIENT_ID = 'test-client-id'
|
|
32
|
+
process.env.CUCUMBERSTUDIO_UID = 'test-uid'
|
|
33
|
+
|
|
34
|
+
const manager = new ConfigManager()
|
|
35
|
+
const config = manager.loadFromEnvironment()
|
|
36
|
+
|
|
37
|
+
expect(config).toEqual({
|
|
38
|
+
cucumberStudio: {
|
|
39
|
+
baseUrl: 'https://studio.cucumberstudio.com/api',
|
|
40
|
+
accessToken: 'test-token',
|
|
41
|
+
clientId: 'test-client-id',
|
|
42
|
+
uid: 'test-uid',
|
|
43
|
+
},
|
|
44
|
+
server: {
|
|
45
|
+
name: 'cucumberstudio-mcp',
|
|
46
|
+
version: SERVER_VERSION,
|
|
47
|
+
transport: 'stdio', // Default value
|
|
48
|
+
port: 3000,
|
|
49
|
+
host: '0.0.0.0',
|
|
50
|
+
},
|
|
51
|
+
logging: {
|
|
52
|
+
level: 'info', // Default value
|
|
53
|
+
logApiResponses: false, // Default value
|
|
54
|
+
logRequestBodies: false,
|
|
55
|
+
logResponseBodies: false, // Default value
|
|
56
|
+
transport: 'stderr',
|
|
57
|
+
filePath: undefined,
|
|
58
|
+
},
|
|
59
|
+
})
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
it('should use custom base URL when provided', () => {
|
|
63
|
+
process.env.CUCUMBERSTUDIO_ACCESS_TOKEN = 'test-token'
|
|
64
|
+
process.env.CUCUMBERSTUDIO_CLIENT_ID = 'test-client-id'
|
|
65
|
+
process.env.CUCUMBERSTUDIO_UID = 'test-uid'
|
|
66
|
+
process.env.CUCUMBERSTUDIO_BASE_URL = 'https://custom-api.example.com'
|
|
67
|
+
|
|
68
|
+
const manager = new ConfigManager()
|
|
69
|
+
const config = manager.loadFromEnvironment()
|
|
70
|
+
|
|
71
|
+
expect(config.cucumberStudio.baseUrl).toBe('https://custom-api.example.com')
|
|
72
|
+
})
|
|
73
|
+
|
|
74
|
+
it('should use custom server configuration when provided', () => {
|
|
75
|
+
process.env.CUCUMBERSTUDIO_ACCESS_TOKEN = 'test-token'
|
|
76
|
+
process.env.CUCUMBERSTUDIO_CLIENT_ID = 'test-client-id'
|
|
77
|
+
process.env.CUCUMBERSTUDIO_UID = 'test-uid'
|
|
78
|
+
process.env.MCP_SERVER_NAME = 'custom-server'
|
|
79
|
+
process.env.MCP_SERVER_VERSION = '2.0.0'
|
|
80
|
+
process.env.MCP_TRANSPORT = 'http'
|
|
81
|
+
process.env.MCP_PORT = '8080'
|
|
82
|
+
process.env.MCP_HOST = '0.0.0.0'
|
|
83
|
+
|
|
84
|
+
const manager = new ConfigManager()
|
|
85
|
+
const config = manager.loadFromEnvironment()
|
|
86
|
+
|
|
87
|
+
expect(config.server).toEqual({
|
|
88
|
+
name: 'custom-server',
|
|
89
|
+
version: '2.0.0',
|
|
90
|
+
transport: 'http',
|
|
91
|
+
port: 8080,
|
|
92
|
+
host: '0.0.0.0',
|
|
93
|
+
})
|
|
94
|
+
})
|
|
95
|
+
|
|
96
|
+
it('should throw error when access token is missing', () => {
|
|
97
|
+
// Only set other required fields, leave access token missing
|
|
98
|
+
process.env.CUCUMBERSTUDIO_CLIENT_ID = 'test-client-id'
|
|
99
|
+
process.env.CUCUMBERSTUDIO_UID = 'test-uid'
|
|
100
|
+
|
|
101
|
+
const manager = new ConfigManager()
|
|
102
|
+
|
|
103
|
+
expect(() => manager.loadFromEnvironment()).toThrow(
|
|
104
|
+
/Configuration validation failed.*CUCUMBERSTUDIO_ACCESS_TOKEN/,
|
|
105
|
+
)
|
|
106
|
+
})
|
|
107
|
+
|
|
108
|
+
it('should throw error when client ID is missing', () => {
|
|
109
|
+
process.env.CUCUMBERSTUDIO_ACCESS_TOKEN = 'test-token'
|
|
110
|
+
process.env.CUCUMBERSTUDIO_UID = 'test-uid'
|
|
111
|
+
|
|
112
|
+
const manager = new ConfigManager()
|
|
113
|
+
|
|
114
|
+
expect(() => manager.loadFromEnvironment()).toThrow(/Configuration validation failed.*CUCUMBERSTUDIO_CLIENT_ID/)
|
|
115
|
+
})
|
|
116
|
+
|
|
117
|
+
it('should throw error when UID is missing', () => {
|
|
118
|
+
process.env.CUCUMBERSTUDIO_ACCESS_TOKEN = 'test-token'
|
|
119
|
+
process.env.CUCUMBERSTUDIO_CLIENT_ID = 'test-client-id'
|
|
120
|
+
|
|
121
|
+
const manager = new ConfigManager()
|
|
122
|
+
|
|
123
|
+
expect(() => manager.loadFromEnvironment()).toThrow(/Configuration validation failed.*CUCUMBERSTUDIO_UID/)
|
|
124
|
+
})
|
|
125
|
+
|
|
126
|
+
it('should throw error when base URL is invalid', () => {
|
|
127
|
+
process.env.CUCUMBERSTUDIO_ACCESS_TOKEN = 'test-token'
|
|
128
|
+
process.env.CUCUMBERSTUDIO_CLIENT_ID = 'test-client-id'
|
|
129
|
+
process.env.CUCUMBERSTUDIO_UID = 'test-uid'
|
|
130
|
+
process.env.CUCUMBERSTUDIO_BASE_URL = 'invalid-url'
|
|
131
|
+
|
|
132
|
+
const manager = new ConfigManager()
|
|
133
|
+
|
|
134
|
+
expect(() => manager.loadFromEnvironment()).toThrow()
|
|
135
|
+
})
|
|
136
|
+
|
|
137
|
+
it('should throw error when transport is invalid', () => {
|
|
138
|
+
process.env.CUCUMBERSTUDIO_ACCESS_TOKEN = 'test-token'
|
|
139
|
+
process.env.CUCUMBERSTUDIO_CLIENT_ID = 'test-client-id'
|
|
140
|
+
process.env.CUCUMBERSTUDIO_UID = 'test-uid'
|
|
141
|
+
process.env.MCP_TRANSPORT = 'invalid-transport'
|
|
142
|
+
|
|
143
|
+
const manager = new ConfigManager()
|
|
144
|
+
|
|
145
|
+
expect(() => manager.loadFromEnvironment()).toThrow()
|
|
146
|
+
})
|
|
147
|
+
|
|
148
|
+
it('should throw error when port is invalid', () => {
|
|
149
|
+
process.env.CUCUMBERSTUDIO_ACCESS_TOKEN = 'test-token'
|
|
150
|
+
process.env.CUCUMBERSTUDIO_CLIENT_ID = 'test-client-id'
|
|
151
|
+
process.env.CUCUMBERSTUDIO_UID = 'test-uid'
|
|
152
|
+
process.env.MCP_PORT = 'invalid-port'
|
|
153
|
+
|
|
154
|
+
const manager = new ConfigManager()
|
|
155
|
+
|
|
156
|
+
expect(() => manager.loadFromEnvironment()).toThrow()
|
|
157
|
+
})
|
|
158
|
+
})
|
|
159
|
+
|
|
160
|
+
describe('getConfig', () => {
|
|
161
|
+
it('should return config after loading', () => {
|
|
162
|
+
process.env.CUCUMBERSTUDIO_ACCESS_TOKEN = 'test-token'
|
|
163
|
+
process.env.CUCUMBERSTUDIO_CLIENT_ID = 'test-client-id'
|
|
164
|
+
process.env.CUCUMBERSTUDIO_UID = 'test-uid'
|
|
165
|
+
|
|
166
|
+
const manager = new ConfigManager()
|
|
167
|
+
const config1 = manager.loadFromEnvironment()
|
|
168
|
+
const config2 = manager.getConfig()
|
|
169
|
+
|
|
170
|
+
expect(config2).toEqual(config1)
|
|
171
|
+
})
|
|
172
|
+
|
|
173
|
+
it('should throw error when config not loaded', () => {
|
|
174
|
+
const manager = new ConfigManager()
|
|
175
|
+
|
|
176
|
+
expect(() => manager.getConfig()).toThrow('Configuration not loaded. Call loadFromEnvironment() first.')
|
|
177
|
+
})
|
|
178
|
+
})
|
|
179
|
+
|
|
180
|
+
describe('validate', () => {
|
|
181
|
+
it('should return true when config is valid', () => {
|
|
182
|
+
process.env.CUCUMBERSTUDIO_ACCESS_TOKEN = 'test-token'
|
|
183
|
+
process.env.CUCUMBERSTUDIO_CLIENT_ID = 'test-client-id'
|
|
184
|
+
process.env.CUCUMBERSTUDIO_UID = 'test-uid'
|
|
185
|
+
|
|
186
|
+
const manager = new ConfigManager()
|
|
187
|
+
manager.loadFromEnvironment()
|
|
188
|
+
|
|
189
|
+
expect(manager.validate()).toBe(true)
|
|
190
|
+
})
|
|
191
|
+
|
|
192
|
+
it('should return false when config not loaded', () => {
|
|
193
|
+
const manager = new ConfigManager()
|
|
194
|
+
|
|
195
|
+
expect(manager.validate()).toBe(false)
|
|
196
|
+
})
|
|
197
|
+
})
|
|
198
|
+
|
|
199
|
+
describe('singleton instance', () => {
|
|
200
|
+
it('should export a singleton instance', () => {
|
|
201
|
+
expect(configManager).toBeInstanceOf(ConfigManager)
|
|
202
|
+
})
|
|
203
|
+
})
|
|
204
|
+
})
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
export const mockActionWords = [
|
|
2
|
+
{
|
|
3
|
+
id: 1,
|
|
4
|
+
name: 'Login with username and password',
|
|
5
|
+
description: 'Authenticate user with provided credentials',
|
|
6
|
+
project_id: 1,
|
|
7
|
+
tags: ['authentication', 'login'],
|
|
8
|
+
parameters: [
|
|
9
|
+
{ name: 'username', type: 'string' },
|
|
10
|
+
{ name: 'password', type: 'string' },
|
|
11
|
+
],
|
|
12
|
+
created_at: '2024-01-01T00:00:00Z',
|
|
13
|
+
updated_at: '2024-01-15T10:30:00Z',
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
id: 2,
|
|
17
|
+
name: 'Search for product',
|
|
18
|
+
description: 'Search for a product in the catalog',
|
|
19
|
+
project_id: 1,
|
|
20
|
+
tags: ['search', 'catalog'],
|
|
21
|
+
parameters: [{ name: 'product_name', type: 'string' }],
|
|
22
|
+
created_at: '2024-01-02T00:00:00Z',
|
|
23
|
+
updated_at: '2024-01-16T11:30:00Z',
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
id: 3,
|
|
27
|
+
name: 'Add product to cart',
|
|
28
|
+
description: 'Add a product to shopping cart',
|
|
29
|
+
project_id: 1,
|
|
30
|
+
tags: ['cart', 'shopping'],
|
|
31
|
+
parameters: [
|
|
32
|
+
{ name: 'product_id', type: 'number' },
|
|
33
|
+
{ name: 'quantity', type: 'number' },
|
|
34
|
+
],
|
|
35
|
+
created_at: '2024-01-03T00:00:00Z',
|
|
36
|
+
updated_at: '2024-01-17T12:30:00Z',
|
|
37
|
+
},
|
|
38
|
+
]
|
|
39
|
+
|
|
40
|
+
export const mockActionWord = mockActionWords[0]
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// Export all mock data for easy importing
|
|
2
|
+
export * from './projects.js'
|
|
3
|
+
export * from './scenarios.js'
|
|
4
|
+
export * from './action-words.js'
|
|
5
|
+
export * from './test-runs.js'
|
|
6
|
+
|
|
7
|
+
// Consolidated export for convenience
|
|
8
|
+
export const mockData = {
|
|
9
|
+
projects: () => import('./projects.js'),
|
|
10
|
+
scenarios: () => import('./scenarios.js'),
|
|
11
|
+
actionWords: () => import('./action-words.js'),
|
|
12
|
+
testRuns: () => import('./test-runs.js'),
|
|
13
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
export const mockProjects = {
|
|
2
|
+
data: [
|
|
3
|
+
{
|
|
4
|
+
id: '1',
|
|
5
|
+
type: 'projects',
|
|
6
|
+
attributes: {
|
|
7
|
+
name: 'E-commerce Platform',
|
|
8
|
+
description: 'Main e-commerce testing project',
|
|
9
|
+
created_at: '2024-01-01T00:00:00Z',
|
|
10
|
+
updated_at: '2024-01-15T10:30:00Z',
|
|
11
|
+
},
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
id: '2',
|
|
15
|
+
type: 'projects',
|
|
16
|
+
attributes: {
|
|
17
|
+
name: 'Mobile App Backend',
|
|
18
|
+
description: 'API testing for mobile application',
|
|
19
|
+
created_at: '2024-01-05T00:00:00Z',
|
|
20
|
+
updated_at: '2024-01-20T14:45:00Z',
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
id: '3',
|
|
25
|
+
type: 'projects',
|
|
26
|
+
attributes: {
|
|
27
|
+
name: 'Authentication Service',
|
|
28
|
+
description: 'User authentication and authorization testing',
|
|
29
|
+
created_at: '2024-01-10T00:00:00Z',
|
|
30
|
+
updated_at: '2024-01-25T09:15:00Z',
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
],
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export const mockProject = {
|
|
37
|
+
data: mockProjects.data[0],
|
|
38
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
export const mockScenarios = [
|
|
2
|
+
{
|
|
3
|
+
id: 1,
|
|
4
|
+
name: 'User Login',
|
|
5
|
+
description: 'Test successful user login flow',
|
|
6
|
+
project_id: 1,
|
|
7
|
+
folder_id: null,
|
|
8
|
+
tags: ['authentication', 'login', 'critical'],
|
|
9
|
+
created_at: '2024-01-01T00:00:00Z',
|
|
10
|
+
updated_at: '2024-01-15T10:30:00Z',
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
id: 2,
|
|
14
|
+
name: 'Product Search',
|
|
15
|
+
description: 'Search for products in catalog',
|
|
16
|
+
project_id: 1,
|
|
17
|
+
folder_id: 10,
|
|
18
|
+
tags: ['search', 'catalog', 'core'],
|
|
19
|
+
created_at: '2024-01-02T00:00:00Z',
|
|
20
|
+
updated_at: '2024-01-16T11:30:00Z',
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
id: 3,
|
|
24
|
+
name: 'Checkout Process',
|
|
25
|
+
description: 'Complete purchase flow',
|
|
26
|
+
project_id: 1,
|
|
27
|
+
folder_id: 11,
|
|
28
|
+
tags: ['checkout', 'payment', 'critical'],
|
|
29
|
+
created_at: '2024-01-03T00:00:00Z',
|
|
30
|
+
updated_at: '2024-01-17T12:30:00Z',
|
|
31
|
+
},
|
|
32
|
+
]
|
|
33
|
+
|
|
34
|
+
export const mockFolders = [
|
|
35
|
+
{
|
|
36
|
+
id: 10,
|
|
37
|
+
name: 'Core Features',
|
|
38
|
+
project_id: 1,
|
|
39
|
+
parent_id: null,
|
|
40
|
+
created_at: '2024-01-01T00:00:00Z',
|
|
41
|
+
updated_at: '2024-01-01T00:00:00Z',
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
id: 11,
|
|
45
|
+
name: 'Payment Flow',
|
|
46
|
+
project_id: 1,
|
|
47
|
+
parent_id: 10,
|
|
48
|
+
created_at: '2024-01-01T00:00:00Z',
|
|
49
|
+
updated_at: '2024-01-01T00:00:00Z',
|
|
50
|
+
},
|
|
51
|
+
]
|
|
52
|
+
|
|
53
|
+
export const mockScenario = mockScenarios[0]
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
export const mockTestRuns = [
|
|
2
|
+
{
|
|
3
|
+
id: 1,
|
|
4
|
+
name: 'Regression Test Suite',
|
|
5
|
+
project_id: 1,
|
|
6
|
+
execution_environment_id: 1,
|
|
7
|
+
status: 'passed',
|
|
8
|
+
statuses: {
|
|
9
|
+
passed: 45,
|
|
10
|
+
failed: 2,
|
|
11
|
+
skipped: 3,
|
|
12
|
+
undefined: 0,
|
|
13
|
+
blocked: 0,
|
|
14
|
+
},
|
|
15
|
+
created_at: '2024-01-15T09:00:00Z',
|
|
16
|
+
updated_at: '2024-01-15T11:30:00Z',
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
id: 2,
|
|
20
|
+
name: 'Smoke Test',
|
|
21
|
+
project_id: 1,
|
|
22
|
+
execution_environment_id: 2,
|
|
23
|
+
status: 'failed',
|
|
24
|
+
statuses: {
|
|
25
|
+
passed: 20,
|
|
26
|
+
failed: 5,
|
|
27
|
+
skipped: 0,
|
|
28
|
+
undefined: 0,
|
|
29
|
+
blocked: 1,
|
|
30
|
+
},
|
|
31
|
+
created_at: '2024-01-16T14:00:00Z',
|
|
32
|
+
updated_at: '2024-01-16T15:45:00Z',
|
|
33
|
+
},
|
|
34
|
+
]
|
|
35
|
+
|
|
36
|
+
export const mockTestExecutions = [
|
|
37
|
+
{
|
|
38
|
+
id: 1,
|
|
39
|
+
test_run_id: 1,
|
|
40
|
+
scenario_id: 1,
|
|
41
|
+
status: 'passed',
|
|
42
|
+
description: 'User login test execution',
|
|
43
|
+
created_at: '2024-01-15T09:15:00Z',
|
|
44
|
+
updated_at: '2024-01-15T09:45:00Z',
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
id: 2,
|
|
48
|
+
test_run_id: 1,
|
|
49
|
+
scenario_id: 2,
|
|
50
|
+
status: 'failed',
|
|
51
|
+
description: 'Product search test execution',
|
|
52
|
+
error_message: 'Search API timeout',
|
|
53
|
+
created_at: '2024-01-15T09:45:00Z',
|
|
54
|
+
updated_at: '2024-01-15T10:15:00Z',
|
|
55
|
+
},
|
|
56
|
+
]
|
|
57
|
+
|
|
58
|
+
export const mockBuilds = [
|
|
59
|
+
{
|
|
60
|
+
id: 1,
|
|
61
|
+
name: 'Release v2.1.0',
|
|
62
|
+
project_id: 1,
|
|
63
|
+
status: 'success',
|
|
64
|
+
test_runs_count: 5,
|
|
65
|
+
scenarios_count: 150,
|
|
66
|
+
created_at: '2024-01-15T00:00:00Z',
|
|
67
|
+
updated_at: '2024-01-15T12:00:00Z',
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
id: 2,
|
|
71
|
+
name: 'Hotfix v2.0.1',
|
|
72
|
+
project_id: 1,
|
|
73
|
+
status: 'failed',
|
|
74
|
+
test_runs_count: 2,
|
|
75
|
+
scenarios_count: 50,
|
|
76
|
+
created_at: '2024-01-14T00:00:00Z',
|
|
77
|
+
updated_at: '2024-01-14T08:30:00Z',
|
|
78
|
+
},
|
|
79
|
+
]
|
|
80
|
+
|
|
81
|
+
export const mockExecutionEnvironments = [
|
|
82
|
+
{
|
|
83
|
+
id: 1,
|
|
84
|
+
name: 'Production',
|
|
85
|
+
project_id: 1,
|
|
86
|
+
description: 'Production environment testing',
|
|
87
|
+
created_at: '2024-01-01T00:00:00Z',
|
|
88
|
+
updated_at: '2024-01-01T00:00:00Z',
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
id: 2,
|
|
92
|
+
name: 'Staging',
|
|
93
|
+
project_id: 1,
|
|
94
|
+
description: 'Staging environment for pre-production testing',
|
|
95
|
+
created_at: '2024-01-01T00:00:00Z',
|
|
96
|
+
updated_at: '2024-01-01T00:00:00Z',
|
|
97
|
+
},
|
|
98
|
+
]
|
|
99
|
+
|
|
100
|
+
export const mockTestRun = mockTestRuns[0]
|
|
101
|
+
export const mockBuild = mockBuilds[0]
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { http, HttpResponse } from 'msw'
|
|
2
|
+
|
|
3
|
+
import { mockActionWords, mockActionWord } from '../data/action-words.js'
|
|
4
|
+
|
|
5
|
+
const BASE_URL = 'https://api.example.com'
|
|
6
|
+
|
|
7
|
+
export const actionWordHandlers = [
|
|
8
|
+
// List action words in a project
|
|
9
|
+
http.get(`${BASE_URL}/projects/:projectId/actionwords`, ({ params, request }) => {
|
|
10
|
+
const accessToken = request.headers.get('access-token')
|
|
11
|
+
|
|
12
|
+
if (!accessToken || accessToken !== 'token') {
|
|
13
|
+
return HttpResponse.json({ error: 'Unauthorized' }, { status: 401 })
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const projectId = Number(params.projectId)
|
|
17
|
+
const projectActionWords = mockActionWords.filter((a) => a.project_id === projectId)
|
|
18
|
+
|
|
19
|
+
// Handle query parameters for filtering
|
|
20
|
+
const url = new URL(request.url)
|
|
21
|
+
const tags = url.searchParams.get('tags')
|
|
22
|
+
|
|
23
|
+
let filteredActionWords = projectActionWords
|
|
24
|
+
|
|
25
|
+
if (tags) {
|
|
26
|
+
const tagList = tags.split(',')
|
|
27
|
+
filteredActionWords = filteredActionWords.filter((actionWord) =>
|
|
28
|
+
tagList.some((tag) => actionWord.tags.includes(tag.trim())),
|
|
29
|
+
)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return HttpResponse.json(filteredActionWords)
|
|
33
|
+
}),
|
|
34
|
+
|
|
35
|
+
// Get specific action word
|
|
36
|
+
http.get(`${BASE_URL}/projects/:projectId/actionwords/:id`, ({ params, request }) => {
|
|
37
|
+
const accessToken = request.headers.get('access-token')
|
|
38
|
+
|
|
39
|
+
if (!accessToken || accessToken !== 'token') {
|
|
40
|
+
return HttpResponse.json({ error: 'Unauthorized' }, { status: 401 })
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const actionWordId = Number(params.id)
|
|
44
|
+
const actionWord = mockActionWords.find((a) => a.id === actionWordId)
|
|
45
|
+
|
|
46
|
+
if (!actionWord) {
|
|
47
|
+
return HttpResponse.json({ error: 'Action word not found' }, { status: 404 })
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return HttpResponse.json(actionWord)
|
|
51
|
+
}),
|
|
52
|
+
]
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { actionWordHandlers } from './action-words.js'
|
|
2
|
+
import { projectHandlers } from './projects.js'
|
|
3
|
+
import { scenarioHandlers } from './scenarios.js'
|
|
4
|
+
import { testRunHandlers } from './test-runs.js'
|
|
5
|
+
|
|
6
|
+
// Combine all handlers
|
|
7
|
+
export const handlers = [...projectHandlers, ...scenarioHandlers, ...actionWordHandlers, ...testRunHandlers]
|
|
8
|
+
|
|
9
|
+
// Export individual handler groups for selective testing
|
|
10
|
+
export { projectHandlers, scenarioHandlers, actionWordHandlers, testRunHandlers }
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { http, HttpResponse } from 'msw'
|
|
2
|
+
|
|
3
|
+
import { mockProjects, mockProject } from '../data/projects.js'
|
|
4
|
+
|
|
5
|
+
const BASE_URL = 'https://api.example.com'
|
|
6
|
+
|
|
7
|
+
export const projectHandlers = [
|
|
8
|
+
// List all projects
|
|
9
|
+
http.get(`${BASE_URL}/projects`, ({ request }) => {
|
|
10
|
+
const accessToken = request.headers.get('access-token')
|
|
11
|
+
|
|
12
|
+
if (!accessToken || accessToken !== 'token') {
|
|
13
|
+
return HttpResponse.json({ error: 'Unauthorized' }, { status: 401 })
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
return HttpResponse.json(mockProjects)
|
|
17
|
+
}),
|
|
18
|
+
|
|
19
|
+
// Get specific project
|
|
20
|
+
http.get(`${BASE_URL}/projects/:id`, ({ params, request }) => {
|
|
21
|
+
const accessToken = request.headers.get('access-token')
|
|
22
|
+
|
|
23
|
+
if (!accessToken || accessToken !== 'token') {
|
|
24
|
+
return HttpResponse.json({ error: 'Unauthorized' }, { status: 401 })
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const projectId = String(params.id)
|
|
28
|
+
const project = mockProjects.data.find((p: any) => p.id === projectId)
|
|
29
|
+
|
|
30
|
+
if (!project) {
|
|
31
|
+
return HttpResponse.json({ error: 'Project not found' }, { status: 404 })
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return HttpResponse.json({ data: project })
|
|
35
|
+
}),
|
|
36
|
+
|
|
37
|
+
// Error scenarios for testing
|
|
38
|
+
http.get(`${BASE_URL}/projects/999`, () => {
|
|
39
|
+
return HttpResponse.json({ error: 'Project not found' }, { status: 404 })
|
|
40
|
+
}),
|
|
41
|
+
|
|
42
|
+
http.get(`${BASE_URL}/projects/error`, () => {
|
|
43
|
+
return HttpResponse.json({ error: 'Internal server error' }, { status: 500 })
|
|
44
|
+
}),
|
|
45
|
+
]
|