haven-cypress-integration 2.0.0 → 2.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "haven-cypress-integration",
3
- "version": "2.0.0",
3
+ "version": "2.0.1",
4
4
  "description": "Seamless Cypress integration with HAVEN test case management",
5
5
  "main": "index.js",
6
6
  "bin": {
@@ -243,6 +243,13 @@ if [ -f "${REPORT_HTML}" ]; then
243
243
  cp "${REPORT_HTML}" "/shared/test-logs/report.html" || echo "Failed to copy HTML to shared volume"
244
244
  fi
245
245
 
246
+ # Copy raw mochawesome reports for EC2 host to upload
247
+ if [ -d "${MOCHAWESOME_DIR}" ]; then
248
+ echo "Copying mochawesome reports to shared volume for EC2 upload..."
249
+ mkdir -p /shared/mochawesome-report
250
+ cp -r "${MOCHAWESOME_DIR}"/* /shared/mochawesome-report/ || echo "Failed to copy mochawesome reports"
251
+ fi
252
+
246
253
  # Post results to HAVEN API
247
254
  echo "Running syncCypressResults.js"
248
255
  PLAN_ID="${PLAN_ID}" RUN_ID="${RUN_ID}" TEST_ENVIRONMENT="${TEST_ENVIRONMENT}" node /haven/syncCypressResults.js "${MERGED_JSON}" || echo "Result sync script finished with warnings"
@@ -7,6 +7,51 @@ const automationCasesPath = "/shared/automation-cases.json";
7
7
  const postBaseUrlPath = "/shared/result-post-url.txt";
8
8
  const triggeredByPath = "/shared/triggered-by.txt";
9
9
 
10
+ // Cache for spec file tag mappings
11
+ const specTagsCache = new Map();
12
+
13
+ /**
14
+ * Parse a Cypress spec file to extract test title -> automation ID mappings
15
+ * Looks for patterns like: it('Test title', { tags: ['@TC-AUTO-xxx'] }, () => {
16
+ */
17
+ function parseSpecFileForTags(specFilePath) {
18
+ if (specTagsCache.has(specFilePath)) {
19
+ return specTagsCache.get(specFilePath);
20
+ }
21
+
22
+ const titleToAutomationIds = new Map();
23
+
24
+ try {
25
+ const content = fs.readFileSync(specFilePath, 'utf-8');
26
+
27
+ // Match it() calls with tags - handles both single and double quotes
28
+ // Pattern: it('title' or it("title", { tags: [...] }
29
+ const itRegex = /it\s*\(\s*(['"`])([^'"`]+)\1\s*,\s*\{\s*tags\s*:\s*\[([^\]]+)\]/g;
30
+
31
+ let match;
32
+ while ((match = itRegex.exec(content)) !== null) {
33
+ const testTitle = match[2];
34
+ const tagsStr = match[3];
35
+
36
+ // Extract TC-AUTO tags from the tags array string
37
+ const automationIds = [];
38
+ const tagMatches = tagsStr.match(/@?TC-AUTO-[\w.-]+/g) || [];
39
+ tagMatches.forEach(tag => {
40
+ automationIds.push(tag.replace(/['"]/g, ''));
41
+ });
42
+
43
+ if (automationIds.length > 0) {
44
+ titleToAutomationIds.set(testTitle, automationIds);
45
+ }
46
+ }
47
+ } catch (err) {
48
+ console.warn(`Could not parse spec file ${specFilePath}:`, err.message);
49
+ }
50
+
51
+ specTagsCache.set(specFilePath, titleToAutomationIds);
52
+ return titleToAutomationIds;
53
+ }
54
+
10
55
  const baseUrl = fs.readFileSync(postBaseUrlPath, "utf-8").trim();
11
56
  let triggeredBy = null;
12
57
  try {
@@ -72,13 +117,35 @@ function parseMochawesomeJson(jsonPath) {
72
117
  }
73
118
  }
74
119
 
75
- function walkSuites(suite) {
76
- collectTests(suite.tests);
77
- (suite.suites || []).forEach(walkSuites);
120
+ function walkSuites(suite, specFilePath) {
121
+ // Get spec file path from suite or use passed-in path
122
+ const currentSpecPath = suite.fullFile || suite.file || specFilePath;
123
+ collectTests(suite.tests, currentSpecPath);
124
+ (suite.suites || []).forEach(s => walkSuites(s, currentSpecPath));
78
125
  }
79
126
 
80
- function collectTests(tests) {
127
+ function collectTests(tests, specFilePath) {
128
+ // Get tag mappings from spec file if available
129
+ let specTagMap = new Map();
130
+ if (specFilePath) {
131
+ // Try multiple possible paths for the spec file
132
+ const possiblePaths = [
133
+ specFilePath,
134
+ path.resolve(specFilePath),
135
+ path.resolve('/app', specFilePath),
136
+ path.resolve(process.cwd(), specFilePath)
137
+ ];
138
+
139
+ for (const tryPath of possiblePaths) {
140
+ if (fs.existsSync(tryPath)) {
141
+ specTagMap = parseSpecFileForTags(tryPath);
142
+ break;
143
+ }
144
+ }
145
+ }
146
+
81
147
  (tests || []).forEach((test) => {
148
+ const testTitle = test.title;
82
149
  const titleCombined =
83
150
  typeof test.fullTitle === "string"
84
151
  ? test.fullTitle
@@ -86,8 +153,38 @@ function parseMochawesomeJson(jsonPath) {
86
153
  ? test.title.join(" ")
87
154
  : test.title;
88
155
 
89
- // Extract automation IDs from title
90
- const automationIdMatch = titleCombined.match(/@?TC-AUTO-[\w.-]+/g) || [];
156
+ // Extract automation IDs from title first
157
+ let automationIdMatch = titleCombined.match(/@?TC-AUTO-[\w.-]+/g) || [];
158
+
159
+ // If not found in title, try looking up from spec file tags
160
+ if (automationIdMatch.length === 0 && specTagMap.has(testTitle)) {
161
+ automationIdMatch = specTagMap.get(testTitle);
162
+ }
163
+
164
+ // Also check mochawesome context for automation IDs (added via addContext)
165
+ if (test.context && automationIdMatch.length === 0) {
166
+ const contextArray = Array.isArray(test.context) ? test.context : [test.context];
167
+ for (const ctx of contextArray) {
168
+ if (ctx && ctx.title === 'automationIds' && Array.isArray(ctx.value)) {
169
+ automationIdMatch = ctx.value.filter(id => id.includes('TC-AUTO'));
170
+ break;
171
+ }
172
+ // Handle string context that might contain JSON
173
+ if (typeof ctx === 'string') {
174
+ try {
175
+ const parsed = JSON.parse(ctx);
176
+ if (parsed.title === 'automationIds' && Array.isArray(parsed.value)) {
177
+ automationIdMatch = parsed.value.filter(id => id.includes('TC-AUTO'));
178
+ break;
179
+ }
180
+ } catch (e) {
181
+ // Not JSON, check if it's a direct automation ID
182
+ const match = ctx.match(/@?TC-AUTO-[\w.-]+/g);
183
+ if (match) automationIdMatch = match;
184
+ }
185
+ }
186
+ }
187
+ }
91
188
 
92
189
  const status =
93
190
  test.state === "passed" || test.pass === true
@@ -104,7 +201,7 @@ function parseMochawesomeJson(jsonPath) {
104
201
  });
105
202
  }
106
203
 
107
- (combinedResults.results || []).forEach(walkSuites);
204
+ (combinedResults.results || []).forEach(suite => walkSuites(suite, null));
108
205
  return { results, foundIds };
109
206
  }
110
207