qualyx 0.3.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.
Files changed (100) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +523 -0
  3. package/dist/cli/commands/init.d.ts +6 -0
  4. package/dist/cli/commands/init.d.ts.map +1 -0
  5. package/dist/cli/commands/init.js +124 -0
  6. package/dist/cli/commands/init.js.map +1 -0
  7. package/dist/cli/commands/list.d.ts +3 -0
  8. package/dist/cli/commands/list.d.ts.map +1 -0
  9. package/dist/cli/commands/list.js +122 -0
  10. package/dist/cli/commands/list.js.map +1 -0
  11. package/dist/cli/commands/run.d.ts +12 -0
  12. package/dist/cli/commands/run.d.ts.map +1 -0
  13. package/dist/cli/commands/run.js +160 -0
  14. package/dist/cli/commands/run.js.map +1 -0
  15. package/dist/cli/commands/schedule.d.ts +19 -0
  16. package/dist/cli/commands/schedule.d.ts.map +1 -0
  17. package/dist/cli/commands/schedule.js +240 -0
  18. package/dist/cli/commands/schedule.js.map +1 -0
  19. package/dist/cli/commands/validate.d.ts +3 -0
  20. package/dist/cli/commands/validate.d.ts.map +1 -0
  21. package/dist/cli/commands/validate.js +47 -0
  22. package/dist/cli/commands/validate.js.map +1 -0
  23. package/dist/cli/index.d.ts +3 -0
  24. package/dist/cli/index.d.ts.map +1 -0
  25. package/dist/cli/index.js +194 -0
  26. package/dist/cli/index.js.map +1 -0
  27. package/dist/core/claude-runner.d.ts +23 -0
  28. package/dist/core/claude-runner.d.ts.map +1 -0
  29. package/dist/core/claude-runner.js +196 -0
  30. package/dist/core/claude-runner.js.map +1 -0
  31. package/dist/core/config-loader.d.ts +137 -0
  32. package/dist/core/config-loader.d.ts.map +1 -0
  33. package/dist/core/config-loader.js +239 -0
  34. package/dist/core/config-loader.js.map +1 -0
  35. package/dist/core/executor.d.ts +75 -0
  36. package/dist/core/executor.d.ts.map +1 -0
  37. package/dist/core/executor.js +337 -0
  38. package/dist/core/executor.js.map +1 -0
  39. package/dist/core/index.d.ts +6 -0
  40. package/dist/core/index.d.ts.map +1 -0
  41. package/dist/core/index.js +7 -0
  42. package/dist/core/index.js.map +1 -0
  43. package/dist/core/prompt-builder.d.ts +24 -0
  44. package/dist/core/prompt-builder.d.ts.map +1 -0
  45. package/dist/core/prompt-builder.js +145 -0
  46. package/dist/core/prompt-builder.js.map +1 -0
  47. package/dist/core/retry-handler.d.ts +42 -0
  48. package/dist/core/retry-handler.d.ts.map +1 -0
  49. package/dist/core/retry-handler.js +126 -0
  50. package/dist/core/retry-handler.js.map +1 -0
  51. package/dist/index.d.ts +3 -0
  52. package/dist/index.d.ts.map +1 -0
  53. package/dist/index.js +16 -0
  54. package/dist/index.js.map +1 -0
  55. package/dist/integrations/email.d.ts +38 -0
  56. package/dist/integrations/email.d.ts.map +1 -0
  57. package/dist/integrations/email.js +216 -0
  58. package/dist/integrations/email.js.map +1 -0
  59. package/dist/integrations/index.d.ts +5 -0
  60. package/dist/integrations/index.d.ts.map +1 -0
  61. package/dist/integrations/index.js +5 -0
  62. package/dist/integrations/index.js.map +1 -0
  63. package/dist/integrations/jira.d.ts +68 -0
  64. package/dist/integrations/jira.d.ts.map +1 -0
  65. package/dist/integrations/jira.js +288 -0
  66. package/dist/integrations/jira.js.map +1 -0
  67. package/dist/integrations/slack.d.ts +66 -0
  68. package/dist/integrations/slack.d.ts.map +1 -0
  69. package/dist/integrations/slack.js +192 -0
  70. package/dist/integrations/slack.js.map +1 -0
  71. package/dist/integrations/teams.d.ts +72 -0
  72. package/dist/integrations/teams.d.ts.map +1 -0
  73. package/dist/integrations/teams.js +197 -0
  74. package/dist/integrations/teams.js.map +1 -0
  75. package/dist/reporters/console.d.ts +83 -0
  76. package/dist/reporters/console.d.ts.map +1 -0
  77. package/dist/reporters/console.js +299 -0
  78. package/dist/reporters/console.js.map +1 -0
  79. package/dist/reporters/html.d.ts +29 -0
  80. package/dist/reporters/html.d.ts.map +1 -0
  81. package/dist/reporters/html.js +105 -0
  82. package/dist/reporters/html.js.map +1 -0
  83. package/dist/storage/results.d.ts +61 -0
  84. package/dist/storage/results.d.ts.map +1 -0
  85. package/dist/storage/results.js +111 -0
  86. package/dist/storage/results.js.map +1 -0
  87. package/dist/storage/sqlite.d.ts +70 -0
  88. package/dist/storage/sqlite.d.ts.map +1 -0
  89. package/dist/storage/sqlite.js +240 -0
  90. package/dist/storage/sqlite.js.map +1 -0
  91. package/dist/types/index.d.ts +1239 -0
  92. package/dist/types/index.d.ts.map +1 -0
  93. package/dist/types/index.js +105 -0
  94. package/dist/types/index.js.map +1 -0
  95. package/package.json +75 -0
  96. package/templates/crontab.hbs +24 -0
  97. package/templates/github-schedule.yml.hbs +153 -0
  98. package/templates/prompt.md.hbs +147 -0
  99. package/templates/report.html.hbs +423 -0
  100. package/templates/slack-message.json.hbs +93 -0
@@ -0,0 +1,216 @@
1
+ import { createTransport } from 'nodemailer';
2
+ /**
3
+ * Email notification integration for Qualyx test results.
4
+ */
5
+ export class EmailNotifier {
6
+ config;
7
+ transporter;
8
+ constructor(config) {
9
+ this.config = config;
10
+ this.transporter = createTransport({
11
+ host: config.smtp_host,
12
+ port: config.smtp_port,
13
+ secure: config.smtp_secure,
14
+ auth: {
15
+ user: config.smtp_user,
16
+ pass: config.smtp_pass,
17
+ },
18
+ });
19
+ }
20
+ /**
21
+ * Determine if a notification should be sent based on run result.
22
+ */
23
+ shouldNotify(runResult) {
24
+ const hasFailed = runResult.failed > 0;
25
+ if (hasFailed && this.config.on_failure) {
26
+ return true;
27
+ }
28
+ if (!hasFailed && this.config.on_success) {
29
+ return true;
30
+ }
31
+ return false;
32
+ }
33
+ /**
34
+ * Build the email subject.
35
+ */
36
+ buildSubject(runResult, organizationName) {
37
+ const status = runResult.failed > 0 ? 'FAILED' : 'PASSED';
38
+ const prefix = this.config.subject_prefix;
39
+ return `${prefix} Test Run ${status} - ${organizationName} (${runResult.passed}/${runResult.totalTests} passed)`;
40
+ }
41
+ /**
42
+ * Build the email HTML body.
43
+ */
44
+ buildHtmlBody(runResult, organizationName, reportUrl) {
45
+ const hasFailed = runResult.failed > 0;
46
+ const statusColor = hasFailed ? '#e74c3c' : '#2ecc71';
47
+ const statusText = hasFailed ? 'FAILED' : 'PASSED';
48
+ const passRate = runResult.totalTests > 0
49
+ ? Math.round((runResult.passed / runResult.totalTests) * 100)
50
+ : 0;
51
+ const failedTests = runResult.results.filter(r => r.status === 'failed');
52
+ const failedTestsHtml = failedTests.length > 0
53
+ ? `
54
+ <h3 style="color: #e74c3c;">Failed Tests</h3>
55
+ <ul>
56
+ ${failedTests.map(t => `
57
+ <li>
58
+ <strong>${t.ruleName}</strong> (${t.appName})
59
+ <br><span style="color: #666;">${t.error || 'Unknown error'}</span>
60
+ </li>
61
+ `).join('')}
62
+ </ul>
63
+ `
64
+ : '';
65
+ const reportLink = reportUrl
66
+ ? `<p><a href="${reportUrl}" style="color: #3498db;">View Full Report</a></p>`
67
+ : '';
68
+ return `
69
+ <!DOCTYPE html>
70
+ <html>
71
+ <head>
72
+ <meta charset="utf-8">
73
+ <style>
74
+ body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; margin: 0; padding: 20px; background: #f5f5f5; }
75
+ .container { max-width: 600px; margin: 0 auto; background: white; border-radius: 8px; overflow: hidden; box-shadow: 0 2px 4px rgba(0,0,0,0.1); }
76
+ .header { background: ${statusColor}; color: white; padding: 20px; text-align: center; }
77
+ .header h1 { margin: 0; font-size: 24px; }
78
+ .content { padding: 20px; }
79
+ .stats { display: flex; justify-content: space-around; margin: 20px 0; }
80
+ .stat { text-align: center; }
81
+ .stat-value { font-size: 32px; font-weight: bold; }
82
+ .stat-label { color: #666; font-size: 14px; }
83
+ .passed { color: #2ecc71; }
84
+ .failed { color: #e74c3c; }
85
+ .skipped { color: #f39c12; }
86
+ .footer { padding: 20px; background: #f9f9f9; text-align: center; color: #666; font-size: 12px; }
87
+ </style>
88
+ </head>
89
+ <body>
90
+ <div class="container">
91
+ <div class="header">
92
+ <h1>Test Run ${statusText}</h1>
93
+ <p>${organizationName} - ${runResult.environment}</p>
94
+ </div>
95
+ <div class="content">
96
+ <div class="stats">
97
+ <div class="stat">
98
+ <div class="stat-value">${runResult.totalTests}</div>
99
+ <div class="stat-label">Total Tests</div>
100
+ </div>
101
+ <div class="stat">
102
+ <div class="stat-value passed">${runResult.passed}</div>
103
+ <div class="stat-label">Passed</div>
104
+ </div>
105
+ <div class="stat">
106
+ <div class="stat-value failed">${runResult.failed}</div>
107
+ <div class="stat-label">Failed</div>
108
+ </div>
109
+ <div class="stat">
110
+ <div class="stat-value">${passRate}%</div>
111
+ <div class="stat-label">Pass Rate</div>
112
+ </div>
113
+ </div>
114
+ ${failedTestsHtml}
115
+ ${reportLink}
116
+ <p style="color: #666; font-size: 14px;">
117
+ Duration: ${formatDuration(runResult.duration)}<br>
118
+ Run ID: ${runResult.runId.slice(0, 8)}...<br>
119
+ Started: ${new Date(runResult.startedAt).toLocaleString()}
120
+ </p>
121
+ </div>
122
+ <div class="footer">
123
+ Sent by Qualyx - AI-powered QA automation
124
+ </div>
125
+ </div>
126
+ </body>
127
+ </html>
128
+ `;
129
+ }
130
+ /**
131
+ * Build plain text email body.
132
+ */
133
+ buildTextBody(runResult, organizationName, reportUrl) {
134
+ const status = runResult.failed > 0 ? 'FAILED' : 'PASSED';
135
+ const passRate = runResult.totalTests > 0
136
+ ? Math.round((runResult.passed / runResult.totalTests) * 100)
137
+ : 0;
138
+ const failedTests = runResult.results.filter(r => r.status === 'failed');
139
+ const failedTestsText = failedTests.length > 0
140
+ ? `\nFailed Tests:\n${failedTests.map(t => ` - ${t.ruleName} (${t.appName}): ${t.error || 'Unknown error'}`).join('\n')}\n`
141
+ : '';
142
+ return `
143
+ Qualyx Test Run ${status}
144
+ ========================
145
+ Organization: ${organizationName}
146
+ Environment: ${runResult.environment}
147
+
148
+ Summary:
149
+ Total: ${runResult.totalTests}
150
+ Passed: ${runResult.passed}
151
+ Failed: ${runResult.failed}
152
+ Skipped: ${runResult.skipped}
153
+ Pass Rate: ${passRate}%
154
+ Duration: ${formatDuration(runResult.duration)}
155
+ ${failedTestsText}
156
+ Run ID: ${runResult.runId}
157
+ Started: ${new Date(runResult.startedAt).toLocaleString()}
158
+ ${reportUrl ? `\nView Report: ${reportUrl}` : ''}
159
+ `.trim();
160
+ }
161
+ /**
162
+ * Send notification email.
163
+ */
164
+ async send(runResult, organizationName, reportUrl) {
165
+ if (!this.shouldNotify(runResult)) {
166
+ return;
167
+ }
168
+ const mailOptions = {
169
+ from: this.config.from,
170
+ to: this.config.to.join(', '),
171
+ subject: this.buildSubject(runResult, organizationName),
172
+ text: this.buildTextBody(runResult, organizationName, reportUrl),
173
+ html: this.buildHtmlBody(runResult, organizationName, reportUrl),
174
+ };
175
+ await this.transporter.sendMail(mailOptions);
176
+ }
177
+ /**
178
+ * Verify SMTP connection.
179
+ */
180
+ async verify() {
181
+ try {
182
+ await this.transporter.verify();
183
+ return true;
184
+ }
185
+ catch {
186
+ return false;
187
+ }
188
+ }
189
+ }
190
+ /**
191
+ * Send an email notification for a test run.
192
+ */
193
+ export async function sendEmailNotification(runResult, config, reportUrl) {
194
+ const emailConfig = config.notifications?.email;
195
+ if (!emailConfig) {
196
+ return;
197
+ }
198
+ const notifier = new EmailNotifier(emailConfig);
199
+ await notifier.send(runResult, config.organization.name, reportUrl);
200
+ }
201
+ /**
202
+ * Format duration in human-readable format.
203
+ */
204
+ function formatDuration(ms) {
205
+ if (ms < 1000) {
206
+ return `${ms}ms`;
207
+ }
208
+ const seconds = Math.floor(ms / 1000);
209
+ if (seconds < 60) {
210
+ return `${seconds}s`;
211
+ }
212
+ const minutes = Math.floor(seconds / 60);
213
+ const remainingSeconds = seconds % 60;
214
+ return `${minutes}m ${remainingSeconds}s`;
215
+ }
216
+ //# sourceMappingURL=email.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"email.js","sourceRoot":"","sources":["../../src/integrations/email.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ export { SlackNotifier, sendSlackNotification } from './slack.js';
2
+ export { EmailNotifier, sendEmailNotification } from './email.js';
3
+ export { TeamsNotifier, sendTeamsNotification } from './teams.js';
4
+ export { JiraIntegration, processJiraIssues } from './jira.js';
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/integrations/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AAClE,OAAO,EAAE,aAAa,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AAClE,OAAO,EAAE,aAAa,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AAClE,OAAO,EAAE,eAAe,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAC"}
@@ -0,0 +1,5 @@
1
+ export { SlackNotifier, sendSlackNotification } from './slack.js';
2
+ export { EmailNotifier, sendEmailNotification } from './email.js';
3
+ export { TeamsNotifier, sendTeamsNotification } from './teams.js';
4
+ export { JiraIntegration, processJiraIssues } from './jira.js';
5
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/integrations/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AAClE,OAAO,EAAE,aAAa,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AAClE,OAAO,EAAE,aAAa,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AAClE,OAAO,EAAE,eAAe,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAC"}
@@ -0,0 +1,68 @@
1
+ import type { TestResult, JiraConfig, QualyxConfig, RunResult } from '../types/index.js';
2
+ interface JiraIssue {
3
+ id: string;
4
+ key: string;
5
+ self: string;
6
+ }
7
+ interface JiraSearchResult {
8
+ issues: Array<{
9
+ id: string;
10
+ key: string;
11
+ fields: {
12
+ summary: string;
13
+ status: {
14
+ name: string;
15
+ };
16
+ };
17
+ }>;
18
+ total: number;
19
+ }
20
+ /**
21
+ * Jira integration for creating issues on test failures.
22
+ */
23
+ export declare class JiraIntegration {
24
+ private config;
25
+ private authHeader;
26
+ constructor(config: JiraConfig);
27
+ /**
28
+ * Search for existing open issues related to a test failure.
29
+ */
30
+ findExistingIssue(testResult: TestResult): Promise<JiraSearchResult['issues'][0] | null>;
31
+ /**
32
+ * Create a new Jira issue for a failed test.
33
+ */
34
+ createIssue(testResult: TestResult, runId: string): Promise<JiraIssue>;
35
+ /**
36
+ * Add a comment to an existing issue about a re-failure.
37
+ */
38
+ addComment(issueKey: string, testResult: TestResult, runId: string): Promise<void>;
39
+ /**
40
+ * Build Atlassian Document Format (ADF) description for the issue.
41
+ */
42
+ private buildDescription;
43
+ /**
44
+ * Build ADF comment body for re-failure.
45
+ */
46
+ private buildCommentBody;
47
+ /**
48
+ * Escape special characters for JQL queries.
49
+ */
50
+ private escapeJql;
51
+ /**
52
+ * Process a failed test result - create issue or add comment.
53
+ */
54
+ processFailedTest(testResult: TestResult, runId: string): Promise<{
55
+ action: 'created' | 'commented';
56
+ issueKey: string;
57
+ } | null>;
58
+ }
59
+ /**
60
+ * Process test failures and create/update Jira issues.
61
+ */
62
+ export declare function processJiraIssues(runResult: RunResult, config: QualyxConfig): Promise<Array<{
63
+ testId: string;
64
+ action: 'created' | 'commented';
65
+ issueKey: string;
66
+ }>>;
67
+ export {};
68
+ //# sourceMappingURL=jira.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jira.d.ts","sourceRoot":"","sources":["../../src/integrations/jira.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAEzF,UAAU,SAAS;IACjB,EAAE,EAAE,MAAM,CAAC;IACX,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;CACd;AAED,UAAU,gBAAgB;IACxB,MAAM,EAAE,KAAK,CAAC;QACZ,EAAE,EAAE,MAAM,CAAC;QACX,GAAG,EAAE,MAAM,CAAC;QACZ,MAAM,EAAE;YACN,OAAO,EAAE,MAAM,CAAC;YAChB,MAAM,EAAE;gBACN,IAAI,EAAE,MAAM,CAAC;aACd,CAAC;SACH,CAAC;KACH,CAAC,CAAC;IACH,KAAK,EAAE,MAAM,CAAC;CACf;AA2CD;;GAEG;AACH,qBAAa,eAAe;IAC1B,OAAO,CAAC,MAAM,CAAa;IAC3B,OAAO,CAAC,UAAU,CAAS;gBAEf,MAAM,EAAE,UAAU;IAM9B;;OAEG;IACG,iBAAiB,CAAC,UAAU,EAAE,UAAU,GAAG,OAAO,CAAC,gBAAgB,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;IAyB9F;;OAEG;IACG,WAAW,CAAC,UAAU,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,CAAC;IAiD5E;;OAEG;IACG,UAAU,CAAC,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAuBxF;;OAEG;IACH,OAAO,CAAC,gBAAgB;IAgGxB;;OAEG;IACH,OAAO,CAAC,gBAAgB;IAgCxB;;OAEG;IACH,OAAO,CAAC,SAAS;IAIjB;;OAEG;IACG,iBAAiB,CAAC,UAAU,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,MAAM,EAAE,SAAS,GAAG,WAAW,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;CAkBtI;AAED;;GAEG;AACH,wBAAsB,iBAAiB,CACrC,SAAS,EAAE,SAAS,EACpB,MAAM,EAAE,YAAY,GACnB,OAAO,CAAC,KAAK,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,SAAS,GAAG,WAAW,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC,CA6BvF"}
@@ -0,0 +1,288 @@
1
+ /**
2
+ * Jira integration for creating issues on test failures.
3
+ */
4
+ export class JiraIntegration {
5
+ config;
6
+ authHeader;
7
+ constructor(config) {
8
+ this.config = config;
9
+ // Basic auth with email:api_token
10
+ this.authHeader = `Basic ${Buffer.from(`${config.email}:${config.api_token}`).toString('base64')}`;
11
+ }
12
+ /**
13
+ * Search for existing open issues related to a test failure.
14
+ */
15
+ async findExistingIssue(testResult) {
16
+ const jql = `project = "${this.config.project_key}" AND summary ~ "${this.escapeJql(testResult.ruleId)}" AND status not in (Done, Closed, Resolved) ORDER BY created DESC`;
17
+ const url = new URL('/rest/api/3/search', this.config.base_url);
18
+ url.searchParams.set('jql', jql);
19
+ url.searchParams.set('maxResults', '1');
20
+ url.searchParams.set('fields', 'summary,status');
21
+ const response = await fetch(url.toString(), {
22
+ method: 'GET',
23
+ headers: {
24
+ 'Authorization': this.authHeader,
25
+ 'Accept': 'application/json',
26
+ },
27
+ });
28
+ if (!response.ok) {
29
+ const errorText = await response.text();
30
+ throw new Error(`Jira search failed: ${response.status} ${errorText}`);
31
+ }
32
+ const result = (await response.json());
33
+ return result.issues.length > 0 ? result.issues[0] : null;
34
+ }
35
+ /**
36
+ * Create a new Jira issue for a failed test.
37
+ */
38
+ async createIssue(testResult, runId) {
39
+ const summary = `[Qualyx] ${testResult.appName} - ${testResult.ruleName} failed`;
40
+ const description = this.buildDescription(testResult, runId);
41
+ const payload = {
42
+ fields: {
43
+ project: {
44
+ key: this.config.project_key,
45
+ },
46
+ summary,
47
+ description,
48
+ issuetype: {
49
+ name: this.config.issue_type,
50
+ },
51
+ },
52
+ };
53
+ // Add labels if configured
54
+ if (this.config.labels?.length) {
55
+ payload.fields.labels = [...this.config.labels, 'qualyx', 'automated-test'];
56
+ }
57
+ else {
58
+ payload.fields.labels = ['qualyx', 'automated-test'];
59
+ }
60
+ // Add components if configured
61
+ if (this.config.components?.length) {
62
+ payload.fields.components = this.config.components.map(name => ({ name }));
63
+ }
64
+ const url = new URL('/rest/api/3/issue', this.config.base_url);
65
+ const response = await fetch(url.toString(), {
66
+ method: 'POST',
67
+ headers: {
68
+ 'Authorization': this.authHeader,
69
+ 'Content-Type': 'application/json',
70
+ 'Accept': 'application/json',
71
+ },
72
+ body: JSON.stringify(payload),
73
+ });
74
+ if (!response.ok) {
75
+ const errorText = await response.text();
76
+ throw new Error(`Jira issue creation failed: ${response.status} ${errorText}`);
77
+ }
78
+ return (await response.json());
79
+ }
80
+ /**
81
+ * Add a comment to an existing issue about a re-failure.
82
+ */
83
+ async addComment(issueKey, testResult, runId) {
84
+ const payload = {
85
+ body: this.buildCommentBody(testResult, runId),
86
+ };
87
+ const url = new URL(`/rest/api/3/issue/${issueKey}/comment`, this.config.base_url);
88
+ const response = await fetch(url.toString(), {
89
+ method: 'POST',
90
+ headers: {
91
+ 'Authorization': this.authHeader,
92
+ 'Content-Type': 'application/json',
93
+ 'Accept': 'application/json',
94
+ },
95
+ body: JSON.stringify(payload),
96
+ });
97
+ if (!response.ok) {
98
+ const errorText = await response.text();
99
+ throw new Error(`Jira comment failed: ${response.status} ${errorText}`);
100
+ }
101
+ }
102
+ /**
103
+ * Build Atlassian Document Format (ADF) description for the issue.
104
+ */
105
+ buildDescription(testResult, runId) {
106
+ return {
107
+ type: 'doc',
108
+ version: 1,
109
+ content: [
110
+ {
111
+ type: 'heading',
112
+ content: [
113
+ {
114
+ type: 'text',
115
+ text: 'Test Failure Details',
116
+ },
117
+ ],
118
+ },
119
+ {
120
+ type: 'paragraph',
121
+ content: [
122
+ { type: 'text', text: 'App: ', marks: [{ type: 'strong' }] },
123
+ { type: 'text', text: testResult.appName },
124
+ ],
125
+ },
126
+ {
127
+ type: 'paragraph',
128
+ content: [
129
+ { type: 'text', text: 'Rule ID: ', marks: [{ type: 'strong' }] },
130
+ { type: 'text', text: testResult.ruleId },
131
+ ],
132
+ },
133
+ {
134
+ type: 'paragraph',
135
+ content: [
136
+ { type: 'text', text: 'Severity: ', marks: [{ type: 'strong' }] },
137
+ { type: 'text', text: testResult.severity.toUpperCase() },
138
+ ],
139
+ },
140
+ {
141
+ type: 'paragraph',
142
+ content: [
143
+ { type: 'text', text: 'Run ID: ', marks: [{ type: 'strong' }] },
144
+ { type: 'text', text: runId },
145
+ ],
146
+ },
147
+ {
148
+ type: 'paragraph',
149
+ content: [
150
+ { type: 'text', text: 'Failed at: ', marks: [{ type: 'strong' }] },
151
+ { type: 'text', text: testResult.completedAt },
152
+ ],
153
+ },
154
+ {
155
+ type: 'heading',
156
+ content: [
157
+ {
158
+ type: 'text',
159
+ text: 'Error',
160
+ },
161
+ ],
162
+ },
163
+ {
164
+ type: 'codeBlock',
165
+ content: [
166
+ {
167
+ type: 'text',
168
+ text: testResult.error || 'No error message available',
169
+ },
170
+ ],
171
+ },
172
+ {
173
+ type: 'heading',
174
+ content: [
175
+ {
176
+ type: 'text',
177
+ text: 'Steps Executed',
178
+ },
179
+ ],
180
+ },
181
+ ...testResult.steps.map(step => ({
182
+ type: 'paragraph',
183
+ content: [
184
+ {
185
+ type: 'text',
186
+ text: step.status === 'passed' ? '✓ ' : '✗ ',
187
+ },
188
+ {
189
+ type: 'text',
190
+ text: step.step,
191
+ },
192
+ ...(step.error ? [
193
+ { type: 'text', text: ` - ${step.error}`, marks: [{ type: 'em' }] },
194
+ ] : []),
195
+ ],
196
+ })),
197
+ ],
198
+ };
199
+ }
200
+ /**
201
+ * Build ADF comment body for re-failure.
202
+ */
203
+ buildCommentBody(testResult, runId) {
204
+ return {
205
+ type: 'doc',
206
+ version: 1,
207
+ content: [
208
+ {
209
+ type: 'paragraph',
210
+ content: [
211
+ { type: 'text', text: 'Test failed again ', marks: [{ type: 'strong' }] },
212
+ { type: 'text', text: `at ${testResult.completedAt}` },
213
+ ],
214
+ },
215
+ {
216
+ type: 'paragraph',
217
+ content: [
218
+ { type: 'text', text: 'Run ID: ' },
219
+ { type: 'text', text: runId },
220
+ ],
221
+ },
222
+ {
223
+ type: 'codeBlock',
224
+ content: [
225
+ {
226
+ type: 'text',
227
+ text: testResult.error || 'No error message available',
228
+ },
229
+ ],
230
+ },
231
+ ],
232
+ };
233
+ }
234
+ /**
235
+ * Escape special characters for JQL queries.
236
+ */
237
+ escapeJql(str) {
238
+ return str.replace(/[\\'"]/g, '\\$&');
239
+ }
240
+ /**
241
+ * Process a failed test result - create issue or add comment.
242
+ */
243
+ async processFailedTest(testResult, runId) {
244
+ if (!this.config.create_issues || testResult.status !== 'failed') {
245
+ return null;
246
+ }
247
+ // Check for existing open issue
248
+ const existingIssue = await this.findExistingIssue(testResult);
249
+ if (existingIssue) {
250
+ // Add comment to existing issue
251
+ await this.addComment(existingIssue.key, testResult, runId);
252
+ return { action: 'commented', issueKey: existingIssue.key };
253
+ }
254
+ // Create new issue
255
+ const newIssue = await this.createIssue(testResult, runId);
256
+ return { action: 'created', issueKey: newIssue.key };
257
+ }
258
+ }
259
+ /**
260
+ * Process test failures and create/update Jira issues.
261
+ */
262
+ export async function processJiraIssues(runResult, config) {
263
+ const jiraConfig = config.integrations?.jira;
264
+ if (!jiraConfig) {
265
+ return [];
266
+ }
267
+ const jira = new JiraIntegration(jiraConfig);
268
+ const results = [];
269
+ const failedTests = runResult.results.filter(r => r.status === 'failed');
270
+ for (const testResult of failedTests) {
271
+ try {
272
+ const result = await jira.processFailedTest(testResult, runResult.runId);
273
+ if (result) {
274
+ results.push({
275
+ testId: testResult.ruleId,
276
+ action: result.action,
277
+ issueKey: result.issueKey,
278
+ });
279
+ }
280
+ }
281
+ catch (error) {
282
+ // Log error but continue processing other failures
283
+ console.error(`Failed to process Jira issue for ${testResult.ruleId}:`, error);
284
+ }
285
+ }
286
+ return results;
287
+ }
288
+ //# sourceMappingURL=jira.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jira.js","sourceRoot":"","sources":["../../src/integrations/jira.ts"],"names":[],"mappings":"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"}