testit-adapter-playwright 4.1.0 → 4.1.1-TMS-5.7

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/dist/labels.d.ts CHANGED
@@ -46,6 +46,9 @@ export declare enum Extensions {
46
46
  }
47
47
  type Parameters = Record<string, string>;
48
48
  export declare class testit {
49
+ private static isMetadataAttachment;
50
+ private static readAttachmentBuffer;
51
+ private static mergeMetadataAttachments;
49
52
  private static addMetadataAttachment;
50
53
  static addAttachment(name: string, content: Buffer | string, options: ContentType | string | Pick<AttachmentOptions, "contentType">): Promise<void>;
51
54
  private static mapParams;
package/dist/labels.js CHANGED
@@ -6,6 +6,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.testit = exports.Extensions = exports.ContentType = void 0;
7
7
  const crypto_1 = require("crypto");
8
8
  const test_1 = __importDefault(require("@playwright/test"));
9
+ const testit_js_commons_1 = require("testit-js-commons");
10
+ const metadata_store_1 = require("./metadata-store");
9
11
  const utils_1 = require("./utils");
10
12
  var ContentType;
11
13
  (function (ContentType) {
@@ -34,10 +36,45 @@ var Extensions;
34
36
  Extensions["MD"] = ".md";
35
37
  })(Extensions || (exports.Extensions = Extensions = {}));
36
38
  class testit {
39
+ static isMetadataAttachment(attachment) {
40
+ return (attachment.contentType === "application/vnd.tms.metadata+json" ||
41
+ attachment.name === "tms-metadata.json");
42
+ }
43
+ static readAttachmentBuffer(attachment) {
44
+ if (attachment.body) {
45
+ return attachment.body;
46
+ }
47
+ if (!attachment.path) {
48
+ return undefined;
49
+ }
50
+ try {
51
+ return testit_js_commons_1.Utils.readBufferSync(attachment.path);
52
+ }
53
+ catch {
54
+ return undefined;
55
+ }
56
+ }
57
+ static mergeMetadataAttachments() {
58
+ let merged = {};
59
+ for (const attachment of test_1.default.info().attachments) {
60
+ if (!this.isMetadataAttachment(attachment)) {
61
+ continue;
62
+ }
63
+ const body = this.readAttachmentBuffer(attachment);
64
+ if (!body) {
65
+ continue;
66
+ }
67
+ merged = { ...merged, ...JSON.parse(body.toString()) };
68
+ }
69
+ return merged;
70
+ }
37
71
  static async addMetadataAttachment(metadata) {
72
+ const info = test_1.default.info();
73
+ (0, metadata_store_1.patchTestMetadataForRun)({ testId: info.testId, file: info.file, titlePath: info.titlePath, title: info.title }, metadata);
74
+ const merged = { ...this.mergeMetadataAttachments(), ...metadata };
38
75
  await test_1.default.info().attach("tms-metadata.json", {
39
76
  contentType: "application/vnd.tms.metadata+json",
40
- body: Buffer.from(JSON.stringify(metadata), "utf8"),
77
+ body: Buffer.from(JSON.stringify(merged), "utf8"),
41
78
  });
42
79
  }
43
80
  static async addAttachment(name, content, options) {
@@ -0,0 +1,24 @@
1
+ import type { MetadataMessage } from "./labels";
2
+ export declare function metadataKey(file: string, titlePath: string[]): string;
3
+ export type MetadataRunContext = {
4
+ testId: string;
5
+ file: string;
6
+ titlePath: string[];
7
+ title: string;
8
+ };
9
+ export declare function metadataKeys(ctx: MetadataRunContext): string[];
10
+ /** In-process metadata from testit.*(); attachments alone are unreliable in the reporter. */
11
+ export declare function patchTestMetadataForRun(ctx: MetadataRunContext, patch: MetadataMessage): void;
12
+ export declare function resolveTestMetadata(ctx: {
13
+ testId: string;
14
+ file: string;
15
+ titlePath: string[];
16
+ title: string;
17
+ }): MetadataMessage | undefined;
18
+ export declare function releaseTestMetadata(ctx: {
19
+ testId: string;
20
+ file: string;
21
+ titlePath: string[];
22
+ title: string;
23
+ }): void;
24
+ export declare function applyMetadataTo(target: MetadataMessage, source: MetadataMessage): void;
@@ -0,0 +1,107 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.metadataKey = metadataKey;
7
+ exports.metadataKeys = metadataKeys;
8
+ exports.patchTestMetadataForRun = patchTestMetadataForRun;
9
+ exports.resolveTestMetadata = resolveTestMetadata;
10
+ exports.releaseTestMetadata = releaseTestMetadata;
11
+ exports.applyMetadataTo = applyMetadataTo;
12
+ const path_1 = __importDefault(require("path"));
13
+ const testit_js_commons_1 = require("testit-js-commons");
14
+ const STORE_KEY = Symbol.for("testit.playwright.metadata");
15
+ function metadataMap() {
16
+ const g = globalThis;
17
+ if (!g[STORE_KEY]) {
18
+ g[STORE_KEY] = new Map();
19
+ }
20
+ return g[STORE_KEY];
21
+ }
22
+ function metadataKey(file, titlePath) {
23
+ return `${path_1.default.resolve(file)}\0${titlePath.join("\0")}`;
24
+ }
25
+ function metadataKeys(ctx) {
26
+ const fullTitle = ctx.titlePath.join(" › ");
27
+ return [
28
+ ctx.testId,
29
+ metadataKey(ctx.file, ctx.titlePath),
30
+ metadataKey(ctx.file, [ctx.title]),
31
+ `hash:${testit_js_commons_1.Utils.getHash(ctx.title)}`,
32
+ `hash:${testit_js_commons_1.Utils.getHash(fullTitle)}`,
33
+ ];
34
+ }
35
+ /** In-process metadata from testit.*(); attachments alone are unreliable in the reporter. */
36
+ function patchTestMetadataForRun(ctx, patch) {
37
+ for (const key of metadataKeys(ctx)) {
38
+ const map = metadataMap();
39
+ map.set(key, { ...(map.get(key) ?? {}), ...patch });
40
+ }
41
+ }
42
+ function peekTestMetadata(key) {
43
+ return metadataMap().get(key);
44
+ }
45
+ function resolveTestMetadata(ctx) {
46
+ let merged = {};
47
+ let found = false;
48
+ for (const key of metadataKeys(ctx)) {
49
+ const chunk = peekTestMetadata(key);
50
+ if (!chunk) {
51
+ continue;
52
+ }
53
+ merged = { ...merged, ...chunk };
54
+ found = true;
55
+ }
56
+ return found ? merged : undefined;
57
+ }
58
+ function releaseTestMetadata(ctx) {
59
+ const map = metadataMap();
60
+ for (const key of metadataKeys(ctx)) {
61
+ map.delete(key);
62
+ }
63
+ }
64
+ function applyMetadataTo(target, source) {
65
+ if (source.externalId) {
66
+ target.externalId = source.externalId;
67
+ }
68
+ if (source.displayName) {
69
+ target.displayName = source.displayName;
70
+ }
71
+ if (source.title) {
72
+ target.title = source.title;
73
+ }
74
+ if (source.description) {
75
+ target.description = source.description;
76
+ }
77
+ if (source.labels) {
78
+ target.labels = source.labels;
79
+ }
80
+ if (source.tags) {
81
+ target.tags = source.tags;
82
+ }
83
+ if (source.links) {
84
+ target.links = source.links;
85
+ }
86
+ if (source.namespace) {
87
+ target.namespace = source.namespace;
88
+ }
89
+ if (source.classname) {
90
+ target.classname = source.classname;
91
+ }
92
+ if (source.addLinks) {
93
+ target.addLinks = source.addLinks;
94
+ }
95
+ if (source.addMessage) {
96
+ target.addMessage = source.addMessage;
97
+ }
98
+ if (source.params) {
99
+ target.params = source.params;
100
+ }
101
+ if (source.workItemIds) {
102
+ target.workItemIds = source.workItemIds;
103
+ }
104
+ if (source.externalKey) {
105
+ target.externalKey = source.externalKey;
106
+ }
107
+ }
@@ -27,6 +27,7 @@ declare class TmsReporter implements Reporter {
27
27
  onTestBegin(test: TestCase): void;
28
28
  onTestEnd(test: TestCase, result: TestResult): void;
29
29
  private _processAttachmentsWithExtensions;
30
+ private metadataContext;
30
31
  onStepBegin(test: TestCase, _result: TestResult, step: TestStep): void;
31
32
  onEnd(): Promise<void>;
32
33
  addSkippedResults(): Promise<void>;
@@ -34,6 +35,11 @@ declare class TmsReporter implements Reporter {
34
35
  printsToStdio(): boolean;
35
36
  private getDictionariesByTest;
36
37
  private getAutotestData;
38
+ private isMetadataAttachment;
39
+ /** Playwright copies inline attach to disk — reporter often gets path only, not body. */
40
+ private readAttachmentBuffer;
41
+ /** Each testit.*() call may add a separate tms-metadata.json; merge all of them. */
42
+ private applyMetadataAttachments;
37
43
  private loadTest;
38
44
  }
39
45
  export default TmsReporter;
package/dist/reporter.js CHANGED
@@ -19,6 +19,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
19
19
  Object.defineProperty(exports, "__esModule", { value: true });
20
20
  const testit_js_commons_1 = require("testit-js-commons");
21
21
  const converter_1 = require("./converter");
22
+ const metadata_store_1 = require("./metadata-store");
22
23
  const utils_1 = require("./utils");
23
24
  const path_1 = __importDefault(require("path"));
24
25
  const testit_js_commons_2 = require("testit-js-commons");
@@ -66,6 +67,17 @@ class TmsReporter {
66
67
  _processAttachmentsWithExtensions(result) {
67
68
  return result.attachments.map(utils_1.processAttachmentExtensions);
68
69
  }
70
+ metadataContext(test) {
71
+ const titlePath = typeof test.titlePath === "function"
72
+ ? test.titlePath()
73
+ : [test.title];
74
+ return {
75
+ testId: test.id,
76
+ file: test.location.file,
77
+ titlePath,
78
+ title: test.title,
79
+ };
80
+ }
69
81
  onStepBegin(test, _result, step) {
70
82
  if (!this.testCache.includes(test)) {
71
83
  return;
@@ -148,6 +160,9 @@ class TmsReporter {
148
160
  externalKey: test.title,
149
161
  };
150
162
  for (const attachment of result.attachments) {
163
+ if (this.isMetadataAttachment(attachment)) {
164
+ continue;
165
+ }
151
166
  if (!attachment.body) {
152
167
  if (attachment.path && attachment.name !== "screenshot") {
153
168
  let content;
@@ -170,49 +185,6 @@ class TmsReporter {
170
185
  }
171
186
  continue;
172
187
  }
173
- if (attachment.contentType === "application/vnd.tms.metadata+json") {
174
- const metadata = JSON.parse(attachment.body.toString());
175
- if (metadata.externalId) {
176
- autotestData.externalId = metadata.externalId;
177
- }
178
- if (metadata.displayName) {
179
- autotestData.displayName = metadata.displayName;
180
- }
181
- if (metadata.title) {
182
- autotestData.title = metadata.title;
183
- }
184
- if (metadata.description) {
185
- autotestData.description = metadata.description;
186
- }
187
- if (metadata.labels) {
188
- autotestData.labels = metadata.labels;
189
- }
190
- if (metadata.tags) {
191
- autotestData.tags = metadata.tags;
192
- }
193
- if (metadata.links) {
194
- autotestData.links = metadata.links;
195
- }
196
- if (metadata.namespace) {
197
- autotestData.namespace = metadata.namespace;
198
- }
199
- if (metadata.classname) {
200
- autotestData.classname = metadata.classname;
201
- }
202
- if (metadata.addLinks) {
203
- autotestData.addLinks = metadata.addLinks;
204
- }
205
- if (metadata.addMessage) {
206
- autotestData.addMessage = metadata.addMessage;
207
- }
208
- if (metadata.params) {
209
- autotestData.params = metadata.params;
210
- }
211
- if (metadata.workItemIds) {
212
- autotestData.workItemIds = metadata.workItemIds;
213
- }
214
- continue;
215
- }
216
188
  if (attachment.name.match(utils_1.stepAttachRegexp)) {
217
189
  const step = this.attachmentStepsCache.find((step) => step.title === attachment.name);
218
190
  try {
@@ -228,35 +200,84 @@ class TmsReporter {
228
200
  }
229
201
  }
230
202
  }
203
+ this.applyMetadataAttachments(autotestData, result.attachments);
204
+ const stored = (0, metadata_store_1.resolveTestMetadata)(this.metadataContext(test));
205
+ if (stored) {
206
+ (0, metadata_store_1.applyMetadataTo)(autotestData, stored);
207
+ testit_js_commons_2.logger.debug("[playwright] metadata from store", {
208
+ title: test.title,
209
+ namespace: autotestData.namespace,
210
+ classname: autotestData.classname,
211
+ });
212
+ }
231
213
  return autotestData;
232
214
  }
215
+ isMetadataAttachment(attachment) {
216
+ return (attachment.contentType === "application/vnd.tms.metadata+json" ||
217
+ attachment.name === "tms-metadata.json");
218
+ }
219
+ /** Playwright copies inline attach to disk — reporter often gets path only, not body. */
220
+ readAttachmentBuffer(attachment) {
221
+ if (attachment.body) {
222
+ return attachment.body;
223
+ }
224
+ if (!attachment.path) {
225
+ return undefined;
226
+ }
227
+ try {
228
+ return testit_js_commons_1.Utils.readBufferSync(attachment.path);
229
+ }
230
+ catch (err) {
231
+ if (err?.code === "ENOENT") {
232
+ return undefined;
233
+ }
234
+ throw err;
235
+ }
236
+ }
237
+ /** Each testit.*() call may add a separate tms-metadata.json; merge all of them. */
238
+ applyMetadataAttachments(autotestData, attachments) {
239
+ let merged = {};
240
+ for (const attachment of attachments) {
241
+ if (!this.isMetadataAttachment(attachment)) {
242
+ continue;
243
+ }
244
+ const body = this.readAttachmentBuffer(attachment);
245
+ if (!body) {
246
+ continue;
247
+ }
248
+ merged = { ...merged, ...JSON.parse(body.toString()) };
249
+ }
250
+ (0, metadata_store_1.applyMetadataTo)(autotestData, merged);
251
+ }
233
252
  async loadTest(test, result) {
234
253
  testit_js_commons_2.logger.debug("[playwright] loadTest", { title: test.title, status: result.status });
235
- const autotestData = await this.getAutotestData(test, result);
236
- const origin = await this.strategy.client.autoTests.getAutotestByExternalId(autotestData.externalId);
237
- if (!origin) {
254
+ try {
255
+ const autotestData = await this.getAutotestData(test, result);
238
256
  const dictionaries = this.getDictionariesByTest(test);
239
257
  const pathNamespace = dictionaries.slice(0, -1).join(path_1.default.sep);
240
258
  const pathClassname = dictionaries[dictionaries.length - 1];
241
- // Prefer testit.namespace / testit.classname from attachments; file path is the default only when missing.
259
+ // Prefer testit.namespace / testit.classname from metadata; file path only when missing.
242
260
  if (pathNamespace.length > 0 && autotestData.namespace == null) {
243
261
  autotestData.namespace = pathNamespace;
244
262
  }
245
263
  if (pathClassname?.length && autotestData.classname == null) {
246
264
  autotestData.classname = pathClassname;
247
265
  }
266
+ const autotest = converter_1.Converter.convertTestCaseToAutotestPost(autotestData);
267
+ const rawSteps = result.steps?.length
268
+ ? result.steps
269
+ : [...this.stepsMap.keys()].filter((step) => this.stepsMap.get(step) === test);
270
+ const stepResults = converter_1.Converter.convertTestStepsToSteps(rawSteps, this.attachmentsMap);
271
+ result.status = (0, utils_1.getTestStatus)(test);
272
+ autotest.steps = converter_1.Converter.convertTestStepsToShortSteps(rawSteps);
273
+ await this.strategy.loadAutotest(autotest, converter_1.Converter.convertStatus(result.status, test.expectedStatus));
274
+ const autotestResult = converter_1.Converter.convertAutotestPostToAutotestResult(autotestData, test, result);
275
+ autotestResult.stepResults = stepResults;
276
+ await this.strategy.loadTestRun([autotestResult]);
277
+ }
278
+ finally {
279
+ (0, metadata_store_1.releaseTestMetadata)(this.metadataContext(test));
248
280
  }
249
- const autotest = converter_1.Converter.convertTestCaseToAutotestPost(autotestData);
250
- const rawSteps = result.steps?.length
251
- ? result.steps
252
- : [...this.stepsMap.keys()].filter((step) => this.stepsMap.get(step) === test);
253
- const stepResults = converter_1.Converter.convertTestStepsToSteps(rawSteps, this.attachmentsMap);
254
- result.status = (0, utils_1.getTestStatus)(test);
255
- autotest.steps = converter_1.Converter.convertTestStepsToShortSteps(rawSteps);
256
- await this.strategy.loadAutotest(autotest, converter_1.Converter.convertStatus(result.status, test.expectedStatus));
257
- const autotestResult = converter_1.Converter.convertAutotestPostToAutotestResult(autotestData, test, result);
258
- autotestResult.stepResults = stepResults;
259
- await this.strategy.loadTestRun([autotestResult]);
260
281
  }
261
282
  }
262
283
  exports.default = TmsReporter;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "testit-adapter-playwright",
3
- "version": "4.1.0",
3
+ "version": "4.1.1-TMS-5.7",
4
4
  "description": "Playwright adapter for Test IT",
5
5
  "license": "Apache-2.0",
6
6
  "author": {
@@ -40,7 +40,7 @@
40
40
  "prettier": "^3.0.1"
41
41
  },
42
42
  "dependencies": {
43
- "testit-js-commons": "4.1.0"
43
+ "testit-js-commons": "4.1.1-TMS-5.7"
44
44
  },
45
45
  "files": [
46
46
  "dist"