@democratize-quality/mcp-server 1.1.2 → 1.1.3
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/README.md +2 -1
- package/package.json +2 -1
- package/src/chatmodes//360/237/214/220 api-planner.chatmode.md" +111 -16
- package/src/tools/api/api-generator.js +170 -5
- package/src/tools/api/api-planner.js +1026 -10
- package/src/tools/api/prompts/generation-prompts.js +131 -27
- package/src/tools/api/prompts/healing-prompts.js +56 -4
- package/src/tools/api/prompts/orchestrator.js +5 -4
- package/src/tools/api/prompts/validation-rules.js +90 -2
- package/src/utils/agentInstaller.js +1 -1
package/README.md
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
**The fastest way to get started** - Install with intelligent testing agents for GitHub Copilot:
|
|
14
14
|
|
|
15
15
|
```bash
|
|
16
|
-
npx @democratize-quality/mcp-server --agents
|
|
16
|
+
npx @democratize-quality/mcp-server@latest --agents
|
|
17
17
|
```
|
|
18
18
|
|
|
19
19
|
**What this does:**
|
|
@@ -929,6 +929,7 @@ MCP_FEATURES_ENABLEDEBUGMODE=true node mcpServer.js
|
|
|
929
929
|
- 📖 [Getting Started Guide](docs/getting-started.md) - Complete setup walkthrough
|
|
930
930
|
- 🔧 [Tool Reference](docs/api/tool-reference.md) - Detailed tool documentation
|
|
931
931
|
- 🎯 [API Tools Usage Guide](docs/api_tools_usage.md) - Advanced examples and patterns
|
|
932
|
+
- 🔷 [GraphQL Support Guide](GRAPHQL-SUPPORT.md) - GraphQL testing capabilities and features
|
|
932
933
|
- �💻 [Developer Guide](docs/development/adding-tools.md) - Extend the server
|
|
933
934
|
- ⚙️ [Configuration Guide](docs/development/configuration.md) - Advanced settings
|
|
934
935
|
- � [Examples](docs/examples/) - Real-world usage examples
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@democratize-quality/mcp-server",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.3",
|
|
4
4
|
"main": "mcpServer.js",
|
|
5
5
|
"bin": {
|
|
6
6
|
"democratize-quality-mcp": "cli.js",
|
|
@@ -75,6 +75,7 @@
|
|
|
75
75
|
"chrome-launcher": "^1.2.0",
|
|
76
76
|
"chrome-remote-interface": "^0.33.3",
|
|
77
77
|
"express": "^5.1.0",
|
|
78
|
+
"graphql": "^16.11.0",
|
|
78
79
|
"json-rpc-2.0": "^1.7.1",
|
|
79
80
|
"yaml": "^2.8.1",
|
|
80
81
|
"zod": "^4.0.10"
|
|
@@ -200,7 +200,106 @@ await tools.api_planner({
|
|
|
200
200
|
|
|
201
201
|
---
|
|
202
202
|
|
|
203
|
-
##
|
|
203
|
+
## � Working with Schema Files
|
|
204
|
+
|
|
205
|
+
### ⚠️ CRITICAL: GraphQL SDL Files (.graphql, .gql)
|
|
206
|
+
|
|
207
|
+
**For GraphQL Schema Definition Language (SDL) files, ALWAYS use `schemaPath` parameter:**
|
|
208
|
+
|
|
209
|
+
```javascript
|
|
210
|
+
// ✅ CORRECT: Use schemaPath
|
|
211
|
+
await tools.api_planner({
|
|
212
|
+
schemaPath: "./schema.graphql", // File path
|
|
213
|
+
apiBaseUrl: "https://api.github.com/graphql",
|
|
214
|
+
outputPath: "./test-plan.md"
|
|
215
|
+
})
|
|
216
|
+
|
|
217
|
+
// ❌ WRONG: Don't try to read and pass file content
|
|
218
|
+
// The tool ONLY accepts schemaPath or schemaUrl parameters
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
**Why schemaPath is Required for SDL:**
|
|
222
|
+
1. **Reads full file** - No truncation or summarization issues
|
|
223
|
+
2. **Automatic conversion** - Tool converts SDL → introspection JSON automatically
|
|
224
|
+
3. **Creates reusable file** - Saves `schema.json` alongside `schema.graphql` for future use
|
|
225
|
+
4. **No parse errors** - Full file is read without truncation
|
|
226
|
+
|
|
227
|
+
**What Happens:**
|
|
228
|
+
```
|
|
229
|
+
User provides: schema.graphql (SDL file)
|
|
230
|
+
↓
|
|
231
|
+
Tool reads full file via schemaPath
|
|
232
|
+
↓
|
|
233
|
+
Detects SDL format automatically
|
|
234
|
+
↓
|
|
235
|
+
Converts SDL → Introspection JSON using graphql library
|
|
236
|
+
↓
|
|
237
|
+
Saves as schema.json (same directory)
|
|
238
|
+
↓
|
|
239
|
+
Uses introspection to generate test plan
|
|
240
|
+
↓
|
|
241
|
+
Both files available for future use
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
**Example with User:**
|
|
245
|
+
```
|
|
246
|
+
User: "Generate test plan from schema.graphql"
|
|
247
|
+
Assistant: "I'll use schemaPath to read your GraphQL SDL file and convert it automatically."
|
|
248
|
+
|
|
249
|
+
await tools.api_planner({
|
|
250
|
+
schemaPath: "./schema.graphql", // ← Use path, not content!
|
|
251
|
+
apiBaseUrl: "https://api.github.com/graphql",
|
|
252
|
+
outputPath: "./github-test-plan.md"
|
|
253
|
+
})
|
|
254
|
+
|
|
255
|
+
// Tool automatically:
|
|
256
|
+
// ✓ Reads full file (no truncation)
|
|
257
|
+
// ✓ Converts SDL to introspection JSON
|
|
258
|
+
// ✓ Saves schema.json
|
|
259
|
+
// ✓ Generates test plan
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
### 📂 Other Schema File Types
|
|
263
|
+
|
|
264
|
+
**OpenAPI/Swagger Files (.json, .yaml, .yml):**
|
|
265
|
+
- Use `schemaPath` for local files
|
|
266
|
+
- Use `schemaUrl` for remote URLs
|
|
267
|
+
|
|
268
|
+
```javascript
|
|
269
|
+
// Local OpenAPI file:
|
|
270
|
+
await tools.api_planner({
|
|
271
|
+
schemaPath: "./openapi.json"
|
|
272
|
+
})
|
|
273
|
+
```
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
### 🔍 Detecting Schema Type
|
|
277
|
+
|
|
278
|
+
**When user mentions file:**
|
|
279
|
+
- `*.graphql` or `*.gql` → **MUST use schemaPath** (SDL conversion)
|
|
280
|
+
- `*.json` → Use schemaPath (JSON)
|
|
281
|
+
- `*.yaml` or `*.yml` → Use schemaPath (YAML)
|
|
282
|
+
- URL ending in `/graphql` → Use schemaUrl (introspection)
|
|
283
|
+
- URL ending in `.json` or `.yaml` → Use schemaUrl (fetch)
|
|
284
|
+
|
|
285
|
+
### 🎯 Best Practice Guidelines
|
|
286
|
+
|
|
287
|
+
**DO:**
|
|
288
|
+
- ✅ Use `schemaPath` for all local schema files
|
|
289
|
+
- ✅ Use `schemaPath` for GraphQL SDL files (`.graphql`, `.gql`)
|
|
290
|
+
- ✅ Use `schemaPath` for large files (>1MB)
|
|
291
|
+
- ✅ Use `schemaUrl` for remote APIs with introspection
|
|
292
|
+
- ✅ Let tool auto-convert SDL to introspection JSON
|
|
293
|
+
- ✅ Reuse generated `.json` files in future runs
|
|
294
|
+
|
|
295
|
+
**DON'T:**
|
|
296
|
+
- ❌ Try to read file content and pass it to the tool
|
|
297
|
+
- ❌ Manually convert SDL before using tool (tool does it automatically)
|
|
298
|
+
- ❌ Delete generated `.json` files (they're reusable)
|
|
299
|
+
|
|
300
|
+
---
|
|
301
|
+
|
|
302
|
+
## �🔄 Quality Check Loop (Playwright-Style)
|
|
204
303
|
|
|
205
304
|
### 1. Generate Plan
|
|
206
305
|
```javascript
|
|
@@ -265,14 +364,9 @@ User asks about the output?
|
|
|
265
364
|
|
|
266
365
|
### Scenario 1: Local Schema File
|
|
267
366
|
```javascript
|
|
268
|
-
//
|
|
269
|
-
const schemaContent = await tools.readFile({
|
|
270
|
-
path: "./openapi.json"
|
|
271
|
-
})
|
|
272
|
-
|
|
273
|
-
// Then pass content to api_planner
|
|
367
|
+
// Use schemaPath for local files
|
|
274
368
|
await tools.api_planner({
|
|
275
|
-
|
|
369
|
+
schemaPath: "./openapi.json",
|
|
276
370
|
apiBaseUrl: "https://api.example.com",
|
|
277
371
|
validateEndpoints: true
|
|
278
372
|
})
|
|
@@ -350,8 +444,9 @@ for (const service of services) {
|
|
|
350
444
|
|
|
351
445
|
## 🎯 Parameter Reference
|
|
352
446
|
|
|
353
|
-
### Required Parameters:
|
|
354
|
-
- `schemaUrl`
|
|
447
|
+
### Required Parameters (One of):
|
|
448
|
+
- `schemaUrl` - URL to fetch schema (e.g., "https://api.example.com/swagger.json")
|
|
449
|
+
- `schemaPath` - Local file path (e.g., "./schema.graphql", "./openapi.json")
|
|
355
450
|
|
|
356
451
|
### Optional Parameters (Common):
|
|
357
452
|
- `apiBaseUrl` - **FULL URL** to override base URL from schema (e.g., `"https://api-staging.example.com/v2"` for staging environment)
|
|
@@ -403,8 +498,8 @@ for (const service of services) {
|
|
|
403
498
|
**Symptoms:** "Failed to parse schema" error
|
|
404
499
|
**Solution:**
|
|
405
500
|
1. Verify schema URL is accessible
|
|
406
|
-
2. Check schema format (OpenAPI 3.0/Swagger 2.0)
|
|
407
|
-
3. Try using `
|
|
501
|
+
2. Check schema format (OpenAPI 3.0/Swagger 2.0/GraphQL SDL)
|
|
502
|
+
3. Try using `schemaPath` for local files instead of schemaUrl
|
|
408
503
|
|
|
409
504
|
---
|
|
410
505
|
|
|
@@ -842,12 +937,12 @@ await tools.api_planner({
|
|
|
842
937
|
|
|
843
938
|
<example>
|
|
844
939
|
Context: Developer wants to create a test plan from API schema content.
|
|
845
|
-
user: 'Generate a test plan from this OpenAPI schema:
|
|
940
|
+
user: 'Generate a test plan from this OpenAPI schema file: openapi.json'
|
|
846
941
|
assistant: 'I'll generate a comprehensive test plan from your OpenAPI schema using the api_planner tool.'
|
|
847
942
|
|
|
848
|
-
// IMMEDIATE RESPONSE - Generate from schema
|
|
943
|
+
// IMMEDIATE RESPONSE - Generate from schema file:
|
|
849
944
|
await tools.api_planner({
|
|
850
|
-
|
|
945
|
+
schemaPath: "./openapi.json",
|
|
851
946
|
schemaType: "auto",
|
|
852
947
|
includeAuth: true,
|
|
853
948
|
includeSecurity: true,
|
|
@@ -856,4 +951,4 @@ await tools.api_planner({
|
|
|
856
951
|
})
|
|
857
952
|
</example>
|
|
858
953
|
|
|
859
|
-
**Key Principle: Use api_planner tool first, always.
|
|
954
|
+
**Key Principle: Use api_planner tool first, always. Use schemaPath for local files, schemaUrl for remote schemas.**
|
|
@@ -252,7 +252,8 @@ class ApiGeneratorTool extends ToolBase {
|
|
|
252
252
|
title: '',
|
|
253
253
|
description: '',
|
|
254
254
|
baseUrl: '',
|
|
255
|
-
sections: []
|
|
255
|
+
sections: [],
|
|
256
|
+
isGraphQL: false // NEW: Track if this is a GraphQL test plan
|
|
256
257
|
};
|
|
257
258
|
|
|
258
259
|
const lines = content.split('\n');
|
|
@@ -371,6 +372,11 @@ class ApiGeneratorTool extends ToolBase {
|
|
|
371
372
|
}
|
|
372
373
|
}
|
|
373
374
|
}
|
|
375
|
+
|
|
376
|
+
// NEW: Detect if test plan is GraphQL-based
|
|
377
|
+
testPlan.isGraphQL = testPlan.sections.some(section =>
|
|
378
|
+
section.scenarios.some(scenario => scenario.isGraphQL)
|
|
379
|
+
);
|
|
374
380
|
|
|
375
381
|
return testPlan;
|
|
376
382
|
}
|
|
@@ -379,6 +385,18 @@ class ApiGeneratorTool extends ToolBase {
|
|
|
379
385
|
try {
|
|
380
386
|
const data = JSON.parse(content);
|
|
381
387
|
|
|
388
|
+
// NEW: Detect GraphQL structure
|
|
389
|
+
if (data.query || data.mutation || data.subscription) {
|
|
390
|
+
scenario.isGraphQL = true;
|
|
391
|
+
scenario.graphql = {
|
|
392
|
+
query: data.query || data.mutation || data.subscription,
|
|
393
|
+
variables: data.variables || {}
|
|
394
|
+
};
|
|
395
|
+
// Also store in data for backward compatibility
|
|
396
|
+
scenario.data = data;
|
|
397
|
+
return;
|
|
398
|
+
}
|
|
399
|
+
|
|
382
400
|
if (context === 'request') {
|
|
383
401
|
// This is request body data
|
|
384
402
|
scenario.data = data;
|
|
@@ -439,7 +457,9 @@ class ApiGeneratorTool extends ToolBase {
|
|
|
439
457
|
expectedStatus: scenario.expectedStatus,
|
|
440
458
|
requestBody: scenario.requestBody,
|
|
441
459
|
expectedBody: scenario.expectedBody,
|
|
442
|
-
steps: scenario.steps
|
|
460
|
+
steps: scenario.steps,
|
|
461
|
+
isGraphQL: scenario.isGraphQL, // NEW: GraphQL flag
|
|
462
|
+
graphql: scenario.graphql // NEW: GraphQL query/variables
|
|
443
463
|
})),
|
|
444
464
|
language: options.language,
|
|
445
465
|
isTypeScript: isTS,
|
|
@@ -516,7 +536,9 @@ class ApiGeneratorTool extends ToolBase {
|
|
|
516
536
|
expectedStatus: scenario.expectedStatus,
|
|
517
537
|
requestBody: scenario.requestBody,
|
|
518
538
|
expectedBody: scenario.expectedBody,
|
|
519
|
-
steps: scenario.steps
|
|
539
|
+
steps: scenario.steps,
|
|
540
|
+
isGraphQL: scenario.isGraphQL, // NEW: GraphQL flag
|
|
541
|
+
graphql: scenario.graphql // NEW: GraphQL query/variables
|
|
520
542
|
})),
|
|
521
543
|
language: options.language,
|
|
522
544
|
isTypeScript: isTS,
|
|
@@ -699,8 +721,34 @@ ${this._generatePlaywrightScenarioTest(scenario, options, testPlan.baseUrl)}
|
|
|
699
721
|
}
|
|
700
722
|
testCode += '\n';
|
|
701
723
|
});
|
|
724
|
+
} else if (scenario.isGraphQL) {
|
|
725
|
+
// NEW: Handle GraphQL requests
|
|
726
|
+
const endpoint = scenario.endpoint || '/graphql';
|
|
727
|
+
const fullUrl = endpoint.startsWith('http') ? endpoint : `\${baseUrl}${endpoint}`;
|
|
728
|
+
|
|
729
|
+
testCode += ` // GraphQL Request\n`;
|
|
730
|
+
testCode += ` const response = await request.post('${fullUrl}', {\n`;
|
|
731
|
+
testCode += ` headers: ${JSON.stringify(scenario.headers, null, 8)},\n`;
|
|
732
|
+
testCode += ` data: {\n`;
|
|
733
|
+
testCode += ` query: \`${scenario.graphql.query.replace(/`/g, '\\`')}\`,\n`;
|
|
734
|
+
if (scenario.graphql.variables && Object.keys(scenario.graphql.variables).length > 0) {
|
|
735
|
+
testCode += ` variables: ${JSON.stringify(scenario.graphql.variables, null, 10)}\n`;
|
|
736
|
+
}
|
|
737
|
+
testCode += ` }\n`;
|
|
738
|
+
testCode += ` });\n\n`;
|
|
739
|
+
|
|
740
|
+
// Add validations for GraphQL
|
|
741
|
+
if (scenario.expect.status) {
|
|
742
|
+
testCode += ` expect(response.status()).toBe(${scenario.expect.status});\n`;
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
if (scenario.expect.body) {
|
|
746
|
+
testCode += ` \n`;
|
|
747
|
+
testCode += ` const responseData = await response.json();\n`;
|
|
748
|
+
testCode += ` ${this._generateGraphQLPlaywrightValidation('responseData', scenario.expect.body)}\n`;
|
|
749
|
+
}
|
|
702
750
|
} else {
|
|
703
|
-
// Single request
|
|
751
|
+
// Single REST request
|
|
704
752
|
let endpoint = scenario.endpoint;
|
|
705
753
|
|
|
706
754
|
// Replace path parameters if any
|
|
@@ -776,6 +824,52 @@ ${this._generatePlaywrightScenarioTest(scenario, options, testPlan.baseUrl)}
|
|
|
776
824
|
return validation.trimEnd(); // Remove trailing newline
|
|
777
825
|
}
|
|
778
826
|
|
|
827
|
+
_generateGraphQLPlaywrightValidation(dataVar, expectedBody) {
|
|
828
|
+
let validation = '';
|
|
829
|
+
|
|
830
|
+
// GraphQL responses have either data or errors
|
|
831
|
+
validation += ` // GraphQL response structure validation\n`;
|
|
832
|
+
validation += ` expect(${dataVar}).toBeDefined();\n`;
|
|
833
|
+
|
|
834
|
+
// Check if we're expecting success (data) or error (errors)
|
|
835
|
+
if (expectedBody && typeof expectedBody === 'object') {
|
|
836
|
+
if (expectedBody.data !== undefined) {
|
|
837
|
+
validation += ` expect(${dataVar}).toHaveProperty('data');\n`;
|
|
838
|
+
validation += ` expect(${dataVar}.errors).toBeUndefined();\n`;
|
|
839
|
+
|
|
840
|
+
// Validate data structure
|
|
841
|
+
if (typeof expectedBody.data === 'object' && expectedBody.data !== null) {
|
|
842
|
+
Object.entries(expectedBody.data).forEach(([key, value]) => {
|
|
843
|
+
validation += ` expect(${dataVar}.data).toHaveProperty('${key}');\n`;
|
|
844
|
+
|
|
845
|
+
// Type checks for nested values
|
|
846
|
+
if (value === 'string') {
|
|
847
|
+
validation += ` expect(typeof ${dataVar}.data.${key}).toBe('string');\n`;
|
|
848
|
+
} else if (value === 'number') {
|
|
849
|
+
validation += ` expect(typeof ${dataVar}.data.${key}).toBe('number');\n`;
|
|
850
|
+
} else if (value === 'boolean') {
|
|
851
|
+
validation += ` expect(typeof ${dataVar}.data.${key}).toBe('boolean');\n`;
|
|
852
|
+
} else if (Array.isArray(value)) {
|
|
853
|
+
validation += ` expect(Array.isArray(${dataVar}.data.${key})).toBe(true);\n`;
|
|
854
|
+
} else if (typeof value === 'object' && value !== null) {
|
|
855
|
+
validation += ` expect(typeof ${dataVar}.data.${key}).toBe('object');\n`;
|
|
856
|
+
}
|
|
857
|
+
});
|
|
858
|
+
}
|
|
859
|
+
} else if (expectedBody.errors !== undefined) {
|
|
860
|
+
validation += ` expect(${dataVar}).toHaveProperty('errors');\n`;
|
|
861
|
+
validation += ` expect(Array.isArray(${dataVar}.errors)).toBe(true);\n`;
|
|
862
|
+
validation += ` expect(${dataVar}.errors.length).toBeGreaterThan(0);\n`;
|
|
863
|
+
}
|
|
864
|
+
} else {
|
|
865
|
+
// Generic validation when no specific expectation
|
|
866
|
+
validation += ` // Verify GraphQL response has either data or errors\n`;
|
|
867
|
+
validation += ` expect(${dataVar}.data !== undefined || ${dataVar}.errors !== undefined).toBe(true);\n`;
|
|
868
|
+
}
|
|
869
|
+
|
|
870
|
+
return validation.trimEnd();
|
|
871
|
+
}
|
|
872
|
+
|
|
779
873
|
_generatePlaywrightHelpers(testPlan, options) {
|
|
780
874
|
const isTS = options.language === 'typescript';
|
|
781
875
|
|
|
@@ -1274,8 +1368,33 @@ describe('${testPlan.title} API Tests', () => {
|
|
|
1274
1368
|
}
|
|
1275
1369
|
testCode += '\n';
|
|
1276
1370
|
});
|
|
1371
|
+
} else if (scenario.isGraphQL) {
|
|
1372
|
+
// NEW: Handle GraphQL requests
|
|
1373
|
+
const endpoint = scenario.endpoint || '/graphql';
|
|
1374
|
+
|
|
1375
|
+
testCode += ` // GraphQL Request\n`;
|
|
1376
|
+
testCode += ` const response = await apiUtils.makeRequest({\n`;
|
|
1377
|
+
testCode += ` method: 'POST',\n`;
|
|
1378
|
+
testCode += ` url: '${endpoint}',\n`;
|
|
1379
|
+
testCode += ` headers: ${JSON.stringify(scenario.headers, null, 8)},\n`;
|
|
1380
|
+
testCode += ` data: {\n`;
|
|
1381
|
+
testCode += ` query: \`${scenario.graphql.query.replace(/`/g, '\\`')}\`,\n`;
|
|
1382
|
+
if (scenario.graphql.variables && Object.keys(scenario.graphql.variables).length > 0) {
|
|
1383
|
+
testCode += ` variables: ${JSON.stringify(scenario.graphql.variables, null, 10)}\n`;
|
|
1384
|
+
}
|
|
1385
|
+
testCode += ` }\n`;
|
|
1386
|
+
testCode += ` });\n\n`;
|
|
1387
|
+
|
|
1388
|
+
// Add validations for GraphQL
|
|
1389
|
+
if (scenario.expect.status) {
|
|
1390
|
+
testCode += ` expect(response.status).toBe(${scenario.expect.status});\n`;
|
|
1391
|
+
}
|
|
1392
|
+
|
|
1393
|
+
if (scenario.expect.body) {
|
|
1394
|
+
testCode += ` ${this._generateGraphQLJestValidation('response.data', scenario.expect.body)}\n`;
|
|
1395
|
+
}
|
|
1277
1396
|
} else {
|
|
1278
|
-
// Single request
|
|
1397
|
+
// Single REST request
|
|
1279
1398
|
testCode += ` const response = await apiUtils.makeRequest({\n`;
|
|
1280
1399
|
testCode += ` method: '${scenario.method}',\n`;
|
|
1281
1400
|
testCode += ` url: '${scenario.endpoint}',\n`;
|
|
@@ -1323,6 +1442,52 @@ describe('${testPlan.title} API Tests', () => {
|
|
|
1323
1442
|
return validation;
|
|
1324
1443
|
}
|
|
1325
1444
|
|
|
1445
|
+
_generateGraphQLJestValidation(dataVar, expectedBody) {
|
|
1446
|
+
let validation = '';
|
|
1447
|
+
|
|
1448
|
+
// GraphQL responses have either data or errors
|
|
1449
|
+
validation += ` // GraphQL response structure validation\n`;
|
|
1450
|
+
validation += ` expect(${dataVar}).toBeDefined();\n`;
|
|
1451
|
+
|
|
1452
|
+
// Check if we're expecting success (data) or error (errors)
|
|
1453
|
+
if (expectedBody && typeof expectedBody === 'object') {
|
|
1454
|
+
if (expectedBody.data !== undefined) {
|
|
1455
|
+
validation += ` expect(${dataVar}).toHaveProperty('data');\n`;
|
|
1456
|
+
validation += ` expect(${dataVar}.errors).toBeUndefined();\n`;
|
|
1457
|
+
|
|
1458
|
+
// Validate data structure
|
|
1459
|
+
if (typeof expectedBody.data === 'object' && expectedBody.data !== null) {
|
|
1460
|
+
Object.entries(expectedBody.data).forEach(([key, value]) => {
|
|
1461
|
+
validation += ` expect(${dataVar}.data).toHaveProperty('${key}');\n`;
|
|
1462
|
+
|
|
1463
|
+
// Type checks for nested values
|
|
1464
|
+
if (value === 'string') {
|
|
1465
|
+
validation += ` expect(typeof ${dataVar}.data.${key}).toBe('string');\n`;
|
|
1466
|
+
} else if (value === 'number') {
|
|
1467
|
+
validation += ` expect(typeof ${dataVar}.data.${key}).toBe('number');\n`;
|
|
1468
|
+
} else if (value === 'boolean') {
|
|
1469
|
+
validation += ` expect(typeof ${dataVar}.data.${key}).toBe('boolean');\n`;
|
|
1470
|
+
} else if (Array.isArray(value)) {
|
|
1471
|
+
validation += ` expect(Array.isArray(${dataVar}.data.${key})).toBe(true);\n`;
|
|
1472
|
+
} else if (typeof value === 'object' && value !== null) {
|
|
1473
|
+
validation += ` expect(typeof ${dataVar}.data.${key}).toBe('object');\n`;
|
|
1474
|
+
}
|
|
1475
|
+
});
|
|
1476
|
+
}
|
|
1477
|
+
} else if (expectedBody.errors !== undefined) {
|
|
1478
|
+
validation += ` expect(${dataVar}).toHaveProperty('errors');\n`;
|
|
1479
|
+
validation += ` expect(Array.isArray(${dataVar}.errors)).toBe(true);\n`;
|
|
1480
|
+
validation += ` expect(${dataVar}.errors.length).toBeGreaterThan(0);\n`;
|
|
1481
|
+
}
|
|
1482
|
+
} else {
|
|
1483
|
+
// Generic validation when no specific expectation
|
|
1484
|
+
validation += ` // Verify GraphQL response has either data or errors\n`;
|
|
1485
|
+
validation += ` expect(${dataVar}.data !== undefined || ${dataVar}.errors !== undefined).toBe(true);\n`;
|
|
1486
|
+
}
|
|
1487
|
+
|
|
1488
|
+
return validation;
|
|
1489
|
+
}
|
|
1490
|
+
|
|
1326
1491
|
_generateJestUtils(testPlan, options) {
|
|
1327
1492
|
const isTS = options.language === 'typescript';
|
|
1328
1493
|
|