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,144 @@
|
|
|
1
|
+
import { Tool, CallToolRequest, CallToolResult, TextContent } from '@modelcontextprotocol/sdk/types.js'
|
|
2
|
+
|
|
3
|
+
import { CucumberStudioApiClient } from '../api/client.js'
|
|
4
|
+
import { Project } from '../api/types.js'
|
|
5
|
+
import { safeExecute } from '../utils/errors.js'
|
|
6
|
+
import { validateInput, ProjectIdSchema, ListParamsSchema, convertToApiParams } from '../utils/validation.js'
|
|
7
|
+
|
|
8
|
+
export class ProjectTools {
|
|
9
|
+
constructor(private apiClient: CucumberStudioApiClient) {}
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Get all available MCP tools for projects
|
|
13
|
+
*/
|
|
14
|
+
getTools(): Tool[] {
|
|
15
|
+
return [
|
|
16
|
+
{
|
|
17
|
+
name: 'cucumberstudio_list_projects',
|
|
18
|
+
description: 'List all projects accessible to the authenticated user',
|
|
19
|
+
inputSchema: {
|
|
20
|
+
type: 'object',
|
|
21
|
+
properties: {
|
|
22
|
+
pagination: {
|
|
23
|
+
type: 'object',
|
|
24
|
+
properties: {
|
|
25
|
+
page: { type: 'number', minimum: 1, description: 'Page number' },
|
|
26
|
+
pageSize: { type: 'number', minimum: 1, maximum: 100, description: 'Number of items per page' },
|
|
27
|
+
},
|
|
28
|
+
additionalProperties: false,
|
|
29
|
+
},
|
|
30
|
+
filter: {
|
|
31
|
+
type: 'object',
|
|
32
|
+
properties: {
|
|
33
|
+
name: { type: 'string', description: 'Filter projects by name' },
|
|
34
|
+
},
|
|
35
|
+
additionalProperties: false,
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
additionalProperties: false,
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
name: 'cucumberstudio_get_project',
|
|
43
|
+
description: 'Get detailed information about a specific project',
|
|
44
|
+
inputSchema: {
|
|
45
|
+
type: 'object',
|
|
46
|
+
properties: {
|
|
47
|
+
projectId: { type: 'string', description: 'The ID of the project to retrieve' },
|
|
48
|
+
},
|
|
49
|
+
required: ['projectId'],
|
|
50
|
+
additionalProperties: false,
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
]
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Handle tool calls for project-related operations
|
|
58
|
+
*/
|
|
59
|
+
async handleToolCall(request: CallToolRequest): Promise<CallToolResult> {
|
|
60
|
+
switch (request.params.name) {
|
|
61
|
+
case 'cucumberstudio_list_projects':
|
|
62
|
+
return this.listProjects(request.params.arguments)
|
|
63
|
+
|
|
64
|
+
case 'cucumberstudio_get_project':
|
|
65
|
+
return this.getProject(request.params.arguments as { projectId: string })
|
|
66
|
+
|
|
67
|
+
default:
|
|
68
|
+
throw new Error(`Unknown tool: ${request.params.name}`)
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
async listProjects(args: unknown = {}): Promise<CallToolResult> {
|
|
73
|
+
return safeExecute(async () => {
|
|
74
|
+
const params = validateInput(ListParamsSchema, args, 'list_projects')
|
|
75
|
+
const apiParams = convertToApiParams(params)
|
|
76
|
+
|
|
77
|
+
const response = await this.apiClient.getProjects(apiParams)
|
|
78
|
+
|
|
79
|
+
const projects = Array.isArray(response.data) ? response.data : [response.data]
|
|
80
|
+
const projectList = projects.map((project: Project) => {
|
|
81
|
+
const attrs = project.attributes as Record<string, unknown>
|
|
82
|
+
return {
|
|
83
|
+
id: project.id,
|
|
84
|
+
name: (attrs.name as string) || 'Unknown',
|
|
85
|
+
description: (attrs.description as string) || '',
|
|
86
|
+
created_at: attrs.created_at as string,
|
|
87
|
+
updated_at: attrs.updated_at as string,
|
|
88
|
+
}
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
return {
|
|
92
|
+
content: [
|
|
93
|
+
{
|
|
94
|
+
type: 'text',
|
|
95
|
+
text: JSON.stringify(
|
|
96
|
+
{
|
|
97
|
+
projects: projectList,
|
|
98
|
+
meta: response.meta || {},
|
|
99
|
+
total_count: projectList.length,
|
|
100
|
+
},
|
|
101
|
+
null,
|
|
102
|
+
2,
|
|
103
|
+
),
|
|
104
|
+
} as TextContent,
|
|
105
|
+
],
|
|
106
|
+
}
|
|
107
|
+
}, 'listing projects')
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
async getProject(args: { projectId: string }): Promise<CallToolResult> {
|
|
111
|
+
return safeExecute(async () => {
|
|
112
|
+
const projectId = validateInput(ProjectIdSchema, args.projectId, 'get_project')
|
|
113
|
+
|
|
114
|
+
const response = await this.apiClient.getProject(projectId)
|
|
115
|
+
|
|
116
|
+
const project = response.data
|
|
117
|
+
const projectDetails = {
|
|
118
|
+
id: project.id,
|
|
119
|
+
type: project.type,
|
|
120
|
+
name: project.attributes.name,
|
|
121
|
+
description: project.attributes.description || '',
|
|
122
|
+
created_at: project.attributes.created_at,
|
|
123
|
+
updated_at: project.attributes.updated_at,
|
|
124
|
+
relationships: project.relationships || {},
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
return {
|
|
128
|
+
content: [
|
|
129
|
+
{
|
|
130
|
+
type: 'text',
|
|
131
|
+
text: JSON.stringify(
|
|
132
|
+
{
|
|
133
|
+
project: projectDetails,
|
|
134
|
+
included: response.included || [],
|
|
135
|
+
},
|
|
136
|
+
null,
|
|
137
|
+
2,
|
|
138
|
+
),
|
|
139
|
+
} as TextContent,
|
|
140
|
+
],
|
|
141
|
+
}
|
|
142
|
+
}, 'getting project details')
|
|
143
|
+
}
|
|
144
|
+
}
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
import { Tool, CallToolRequest, CallToolResult, TextContent } from '@modelcontextprotocol/sdk/types.js'
|
|
2
|
+
import { z } from 'zod'
|
|
3
|
+
|
|
4
|
+
import { CucumberStudioApiClient } from '../api/client.js'
|
|
5
|
+
import { Scenario } from '../api/types.js'
|
|
6
|
+
import { safeExecute } from '../utils/errors.js'
|
|
7
|
+
import {
|
|
8
|
+
validateInput,
|
|
9
|
+
ProjectIdSchema,
|
|
10
|
+
ScenarioIdSchema,
|
|
11
|
+
ListParamsSchema,
|
|
12
|
+
convertToApiParams,
|
|
13
|
+
} from '../utils/validation.js'
|
|
14
|
+
|
|
15
|
+
const FindByTagsSchema = z.object({
|
|
16
|
+
projectId: ProjectIdSchema,
|
|
17
|
+
tags: z.string().min(1, 'Tags parameter is required'),
|
|
18
|
+
pagination: z
|
|
19
|
+
.object({
|
|
20
|
+
page: z.number().int().min(1).optional(),
|
|
21
|
+
pageSize: z.number().int().min(1).max(100).optional(),
|
|
22
|
+
})
|
|
23
|
+
.optional(),
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
export class ScenarioTools {
|
|
27
|
+
constructor(private apiClient: CucumberStudioApiClient) {}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Get all available MCP tools for scenarios
|
|
31
|
+
*/
|
|
32
|
+
getTools(): Tool[] {
|
|
33
|
+
return [
|
|
34
|
+
{
|
|
35
|
+
name: 'cucumberstudio_list_scenarios',
|
|
36
|
+
description: 'List all scenarios in a project',
|
|
37
|
+
inputSchema: {
|
|
38
|
+
type: 'object',
|
|
39
|
+
properties: {
|
|
40
|
+
projectId: { type: 'string', description: 'The ID of the project' },
|
|
41
|
+
pagination: {
|
|
42
|
+
type: 'object',
|
|
43
|
+
properties: {
|
|
44
|
+
page: { type: 'number', minimum: 1, description: 'Page number' },
|
|
45
|
+
pageSize: { type: 'number', minimum: 1, maximum: 100, description: 'Number of items per page' },
|
|
46
|
+
},
|
|
47
|
+
additionalProperties: false,
|
|
48
|
+
},
|
|
49
|
+
filter: {
|
|
50
|
+
type: 'object',
|
|
51
|
+
properties: {
|
|
52
|
+
name: { type: 'string', description: 'Filter scenarios by name' },
|
|
53
|
+
tags: { type: 'string', description: 'Filter scenarios by tags' },
|
|
54
|
+
},
|
|
55
|
+
additionalProperties: false,
|
|
56
|
+
},
|
|
57
|
+
},
|
|
58
|
+
required: ['projectId'],
|
|
59
|
+
additionalProperties: false,
|
|
60
|
+
},
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
name: 'cucumberstudio_get_scenario',
|
|
64
|
+
description: 'Get detailed information about a specific scenario',
|
|
65
|
+
inputSchema: {
|
|
66
|
+
type: 'object',
|
|
67
|
+
properties: {
|
|
68
|
+
projectId: { type: 'string', description: 'The ID of the project' },
|
|
69
|
+
scenarioId: { type: 'string', description: 'The ID of the scenario to retrieve' },
|
|
70
|
+
},
|
|
71
|
+
required: ['projectId', 'scenarioId'],
|
|
72
|
+
additionalProperties: false,
|
|
73
|
+
},
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
name: 'cucumberstudio_find_scenarios_by_tags',
|
|
77
|
+
description: 'Find scenarios by tags in a project',
|
|
78
|
+
inputSchema: {
|
|
79
|
+
type: 'object',
|
|
80
|
+
properties: {
|
|
81
|
+
projectId: { type: 'string', description: 'The ID of the project' },
|
|
82
|
+
tags: { type: 'string', description: 'Tags to search for (comma-separated)' },
|
|
83
|
+
pagination: {
|
|
84
|
+
type: 'object',
|
|
85
|
+
properties: {
|
|
86
|
+
page: { type: 'number', minimum: 1, description: 'Page number' },
|
|
87
|
+
pageSize: { type: 'number', minimum: 1, maximum: 100, description: 'Number of items per page' },
|
|
88
|
+
},
|
|
89
|
+
additionalProperties: false,
|
|
90
|
+
},
|
|
91
|
+
},
|
|
92
|
+
required: ['projectId', 'tags'],
|
|
93
|
+
additionalProperties: false,
|
|
94
|
+
},
|
|
95
|
+
},
|
|
96
|
+
]
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Handle tool calls for scenario-related operations
|
|
101
|
+
*/
|
|
102
|
+
async handleToolCall(request: CallToolRequest): Promise<CallToolResult> {
|
|
103
|
+
switch (request.params.name) {
|
|
104
|
+
case 'cucumberstudio_list_scenarios':
|
|
105
|
+
return this.listScenarios(request.params.arguments)
|
|
106
|
+
|
|
107
|
+
case 'cucumberstudio_get_scenario':
|
|
108
|
+
return this.getScenario(request.params.arguments)
|
|
109
|
+
|
|
110
|
+
case 'cucumberstudio_find_scenarios_by_tags':
|
|
111
|
+
return this.findScenariosByTags(request.params.arguments)
|
|
112
|
+
|
|
113
|
+
default:
|
|
114
|
+
throw new Error(`Unknown tool: ${request.params.name}`)
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
async listScenarios(args: unknown): Promise<CallToolResult> {
|
|
119
|
+
return safeExecute(async () => {
|
|
120
|
+
const projectId = validateInput(ProjectIdSchema, (args as Record<string, unknown>)?.projectId, 'list_scenarios')
|
|
121
|
+
const listParams = validateInput(ListParamsSchema, args, 'list_scenarios')
|
|
122
|
+
const apiParams = convertToApiParams(listParams)
|
|
123
|
+
|
|
124
|
+
const response = await this.apiClient.getScenarios(projectId, apiParams)
|
|
125
|
+
|
|
126
|
+
const scenarios = Array.isArray(response.data) ? response.data : [response.data]
|
|
127
|
+
const scenarioList = scenarios.map((scenario: Scenario) => ({
|
|
128
|
+
id: scenario.id,
|
|
129
|
+
name: scenario.attributes.name,
|
|
130
|
+
description: scenario.attributes.description || '',
|
|
131
|
+
definition: scenario.attributes.definition || '',
|
|
132
|
+
folder_id: scenario.attributes.folder_id,
|
|
133
|
+
created_at: scenario.attributes.created_at,
|
|
134
|
+
updated_at: scenario.attributes.updated_at,
|
|
135
|
+
}))
|
|
136
|
+
|
|
137
|
+
return {
|
|
138
|
+
content: [
|
|
139
|
+
{
|
|
140
|
+
type: 'text',
|
|
141
|
+
text: JSON.stringify(
|
|
142
|
+
{
|
|
143
|
+
scenarios: scenarioList,
|
|
144
|
+
meta: response.meta || {},
|
|
145
|
+
total_count: scenarioList.length,
|
|
146
|
+
},
|
|
147
|
+
null,
|
|
148
|
+
2,
|
|
149
|
+
),
|
|
150
|
+
} as TextContent,
|
|
151
|
+
],
|
|
152
|
+
}
|
|
153
|
+
}, 'listing scenarios')
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
async getScenario(args: unknown): Promise<CallToolResult> {
|
|
157
|
+
return safeExecute(async () => {
|
|
158
|
+
const projectId = validateInput(ProjectIdSchema, (args as Record<string, unknown>)?.projectId, 'get_scenario')
|
|
159
|
+
const scenarioId = validateInput(ScenarioIdSchema, (args as Record<string, unknown>)?.scenarioId, 'get_scenario')
|
|
160
|
+
|
|
161
|
+
const response = await this.apiClient.getScenario(projectId, scenarioId)
|
|
162
|
+
|
|
163
|
+
const scenario = response.data
|
|
164
|
+
const scenarioDetails = {
|
|
165
|
+
id: scenario.id,
|
|
166
|
+
type: scenario.type,
|
|
167
|
+
name: scenario.attributes.name,
|
|
168
|
+
description: scenario.attributes.description || '',
|
|
169
|
+
definition: scenario.attributes.definition || '',
|
|
170
|
+
folder_id: scenario.attributes.folder_id,
|
|
171
|
+
created_at: scenario.attributes.created_at,
|
|
172
|
+
updated_at: scenario.attributes.updated_at,
|
|
173
|
+
relationships: scenario.relationships || {},
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
return {
|
|
177
|
+
content: [
|
|
178
|
+
{
|
|
179
|
+
type: 'text',
|
|
180
|
+
text: JSON.stringify(
|
|
181
|
+
{
|
|
182
|
+
scenario: scenarioDetails,
|
|
183
|
+
included: response.included || [],
|
|
184
|
+
},
|
|
185
|
+
null,
|
|
186
|
+
2,
|
|
187
|
+
),
|
|
188
|
+
} as TextContent,
|
|
189
|
+
],
|
|
190
|
+
}
|
|
191
|
+
}, 'getting scenario details')
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
async findScenariosByTags(args: unknown): Promise<CallToolResult> {
|
|
195
|
+
return safeExecute(async () => {
|
|
196
|
+
const { projectId, tags, pagination } = validateInput(FindByTagsSchema, args, 'find_scenarios_by_tags')
|
|
197
|
+
const apiParams = convertToApiParams({ pagination })
|
|
198
|
+
|
|
199
|
+
const response = await this.apiClient.findScenariosByTag(projectId, tags, apiParams)
|
|
200
|
+
|
|
201
|
+
const scenarios = Array.isArray(response.data) ? response.data : [response.data]
|
|
202
|
+
const scenarioList = scenarios.map((scenario: Scenario) => ({
|
|
203
|
+
id: scenario.id,
|
|
204
|
+
name: scenario.attributes.name,
|
|
205
|
+
description: scenario.attributes.description || '',
|
|
206
|
+
definition: scenario.attributes.definition || '',
|
|
207
|
+
folder_id: scenario.attributes.folder_id,
|
|
208
|
+
created_at: scenario.attributes.created_at,
|
|
209
|
+
updated_at: scenario.attributes.updated_at,
|
|
210
|
+
}))
|
|
211
|
+
|
|
212
|
+
return {
|
|
213
|
+
content: [
|
|
214
|
+
{
|
|
215
|
+
type: 'text',
|
|
216
|
+
text: JSON.stringify(
|
|
217
|
+
{
|
|
218
|
+
scenarios: scenarioList,
|
|
219
|
+
search_tags: tags,
|
|
220
|
+
meta: response.meta || {},
|
|
221
|
+
total_count: scenarioList.length,
|
|
222
|
+
},
|
|
223
|
+
null,
|
|
224
|
+
2,
|
|
225
|
+
),
|
|
226
|
+
} as TextContent,
|
|
227
|
+
],
|
|
228
|
+
}
|
|
229
|
+
}, 'finding scenarios by tags')
|
|
230
|
+
}
|
|
231
|
+
}
|