@lokalise/playwright-reporters 1.7.0 → 1.8.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.
@@ -17,10 +17,31 @@ export declare class Elastic {
17
17
  private readonly elasticToken;
18
18
  constructor(analyticsConfig: ElasticConfig);
19
19
  setNativeRequestContext(): Promise<void>;
20
+ /**
21
+ * This method saves a flaky test data to Elasticsearch.
22
+ */
20
23
  saveFlakyData(flake: ReturnType<typeof formFlakyTestData>): Promise<void>;
24
+ /**
25
+ * This method saves a failed test data to Elasticsearch.
26
+ */
21
27
  saveFailedData(failure: ReturnType<typeof formFailedTestData>): Promise<void>;
28
+ /**
29
+ * This method saves the test run summary to Elasticsearch.
30
+ */
22
31
  saveBuildData(build: ReturnType<typeof formBuildData>): Promise<void>;
32
+ /**
33
+ * This method saves the browser usage data to Elasticsearch.
34
+ */
23
35
  saveBrowserUsageData(usageData: ReturnType<typeof formBrowserUsageData>): Promise<void>;
36
+ /**
37
+ * This method retrieves the last test run data from Elasticsearch.
38
+ * It is used for analytics of the main branch, where the index only contains data from the main branch.
39
+ */
24
40
  getLastRunDataByTime(): Promise<string | null>;
41
+ /**
42
+ * This method retrieves the last test run data from Elasticsearch.
43
+ * It is used for analytics of the feature branches, where the index contains data from all PREnv.
44
+ * It is therefore necessary to filter the data by the PREnv ID as well as the project name.
45
+ */
25
46
  getLastRunDataByPrenvIdAndProject(prenvId: string, projects?: string[]): Promise<string | null>;
26
47
  }
@@ -21,6 +21,9 @@ class Elastic {
21
21
  }),
22
22
  });
23
23
  }
24
+ /**
25
+ * This method saves a flaky test data to Elasticsearch.
26
+ */
24
27
  async saveFlakyData(flake) {
25
28
  try {
26
29
  await this.request.post(`/${this.flakyTestIndex}/_doc`, { data: flake });
@@ -29,6 +32,9 @@ class Elastic {
29
32
  (0, logger_1.logger)(error);
30
33
  }
31
34
  }
35
+ /**
36
+ * This method saves a failed test data to Elasticsearch.
37
+ */
32
38
  async saveFailedData(failure) {
33
39
  try {
34
40
  await this.request.post(`/${this.failedTestIndex}/_doc`, {
@@ -39,6 +45,9 @@ class Elastic {
39
45
  (0, logger_1.logger)(error);
40
46
  }
41
47
  }
48
+ /**
49
+ * This method saves the test run summary to Elasticsearch.
50
+ */
42
51
  async saveBuildData(build) {
43
52
  try {
44
53
  await this.request.post(`/${this.testRunIndex}/_doc`, { data: build });
@@ -47,6 +56,9 @@ class Elastic {
47
56
  (0, logger_1.logger)(error);
48
57
  }
49
58
  }
59
+ /**
60
+ * This method saves the browser usage data to Elasticsearch.
61
+ */
50
62
  async saveBrowserUsageData(usageData) {
51
63
  try {
52
64
  await this.request.post(`/${this.browserUsageIndex}/_doc`, {
@@ -57,6 +69,10 @@ class Elastic {
57
69
  (0, logger_1.logger)(error);
58
70
  }
59
71
  }
72
+ /**
73
+ * This method retrieves the last test run data from Elasticsearch.
74
+ * It is used for analytics of the main branch, where the index only contains data from the main branch.
75
+ */
60
76
  async getLastRunDataByTime() {
61
77
  try {
62
78
  const response = await this.request.post(`/${this.testRunIndex}/_search`, {
@@ -79,6 +95,11 @@ class Elastic {
79
95
  return null;
80
96
  }
81
97
  }
98
+ /**
99
+ * This method retrieves the last test run data from Elasticsearch.
100
+ * It is used for analytics of the feature branches, where the index contains data from all PREnv.
101
+ * It is therefore necessary to filter the data by the PREnv ID as well as the project name.
102
+ */
82
103
  async getLastRunDataByPrenvIdAndProject(prenvId, projects) {
83
104
  try {
84
105
  const response = await this.request.post(`/${this.testRunIndex}/_search`, {
@@ -16,11 +16,21 @@ export default class AnalyticsReporter implements Reporter {
16
16
  private readonly isMainPREnv;
17
17
  private workerCount;
18
18
  constructor(options: ReporterOptions);
19
+ /**
20
+ * This method is used to determine if the browser data should be saved for a given test.
21
+ * The browser data is saved only if the following conditions are met:
22
+ * - The browserUsageIndex is provided in the options.
23
+ * - The test project name does not match any of the projects in the ignoreProjects list.
24
+ * @param test The TestCase for which the browser data is to be saved.
25
+ */
19
26
  private shouldSaveBrowserData;
20
27
  onBegin(config: FullConfig, suite: Suite): Promise<this | undefined>;
21
28
  onStepBegin(test: TestCase, result: TestResult, step: TestStep): this | undefined;
22
29
  onStepEnd(test: TestCase, result: TestResult, step: TestStep): this | undefined;
23
30
  onTestEnd(test: TestCase, result: TestResult): Promise<this | undefined>;
31
+ /**
32
+ * This method is used to log the analytics data in the console for debugging purposes.
33
+ */
24
34
  private onEndDebug;
25
35
  onEnd(result: FullResult): Promise<void>;
26
36
  }
@@ -27,6 +27,13 @@ class AnalyticsReporter {
27
27
  this.isMainPREnv = (0, helpers_1.isMainPREnv)(options);
28
28
  this.elastic = new elastic_1.Elastic(options);
29
29
  }
30
+ /**
31
+ * This method is used to determine if the browser data should be saved for a given test.
32
+ * The browser data is saved only if the following conditions are met:
33
+ * - The browserUsageIndex is provided in the options.
34
+ * - The test project name does not match any of the projects in the ignoreProjects list.
35
+ * @param test The TestCase for which the browser data is to be saved.
36
+ */
30
37
  shouldSaveBrowserData(test) {
31
38
  const testProjectName = test?.parent?.project()?.name;
32
39
  const isIgnoredProject = this.options.ignoreProjects?.some((project) => testProjectName?.includes(project));
@@ -126,6 +133,9 @@ class AnalyticsReporter {
126
133
  console.error("Could not perform 'onTestEnd' analytics step", error);
127
134
  }
128
135
  }
136
+ /**
137
+ * This method is used to log the analytics data in the console for debugging purposes.
138
+ */
129
139
  onEndDebug(buildData, browserUsageData) {
130
140
  (0, transform_1.calculateRuntime)(this.runStartTime);
131
141
  (0, transform_1.calculateRetriedTests)(this.listOfFlakyTests);
@@ -148,6 +158,12 @@ class AnalyticsReporter {
148
158
  }
149
159
  return projectData;
150
160
  })();
161
+ /**
162
+ * This function is used to get the last stored build data.
163
+ *
164
+ * - If the prenvId is provided, it will return the last build data for the given prenvId.
165
+ * - If the prenvId is not provided, it will return the last build data by time.
166
+ */
151
167
  const getLastBuildData = async () => {
152
168
  if (this.options.prenvId) {
153
169
  const listOfProjectNames = Object.entries(projectData)
@@ -2,11 +2,11 @@ import { type FullResult, type TestCase, type TestResult, type TestStep } from '
2
2
  import type { TestAnalyticsData, BrowserUsageData } from './report.types';
3
3
  import type AnalyticsReporter from './reporter';
4
4
  export declare const calculateRuntime: (startTime: number) => void;
5
- export declare const calculateRetriedTests: (listOfNonPassingTests: AnalyticsReporter['listOfNonPassingTests']) => void;
6
- export declare const calculateHooksDuration: (steps: TestStep[], hook: 'before' | 'after') => number;
5
+ export declare const calculateRetriedTests: (listOfNonPassingTests: AnalyticsReporter["listOfNonPassingTests"]) => void;
6
+ export declare const calculateHooksDuration: (steps: TestStep[], hook: "before" | "after") => number;
7
7
  export declare const getTeamNames: (test: TestCase) => {
8
8
  type: string;
9
- description?: string | undefined;
9
+ description?: string;
10
10
  }[];
11
11
  export declare const formFailedTestData: (test: TestCase, result: TestResult, testRunId: string, prenvId?: string) => {
12
12
  prenvId?: string | undefined;
@@ -15,7 +15,7 @@ export declare const formFailedTestData: (test: TestCase, result: TestResult, te
15
15
  name: string;
16
16
  team: string | null;
17
17
  testRunId: string;
18
- status: "failed" | "interrupted" | "passed" | "timedOut" | "skipped";
18
+ status: "skipped" | "failed" | "interrupted" | "passed" | "timedOut";
19
19
  hooksDuration: number;
20
20
  beforeHookDuration: number;
21
21
  afterHookDuration: number;
@@ -32,7 +32,7 @@ export declare const formFlakyTestData: (test: TestCase, result: TestResult, tes
32
32
  name: string;
33
33
  team: string | null;
34
34
  testRunId: string;
35
- status: "failed" | "interrupted" | "passed" | "timedOut" | "skipped";
35
+ status: "skipped" | "failed" | "interrupted" | "passed" | "timedOut";
36
36
  hooksDuration: number;
37
37
  beforeHookDuration: number;
38
38
  afterHookDuration: number;
@@ -43,7 +43,7 @@ export declare const formFlakyTestData: (test: TestCase, result: TestResult, tes
43
43
  };
44
44
  };
45
45
  export declare const formBuildData: (args: {
46
- result: Pick<FullResult, 'status'>;
46
+ result: Pick<FullResult, "status">;
47
47
  startTime: number;
48
48
  endTime: number;
49
49
  numberOfTests: number;
@@ -71,10 +71,18 @@ export declare const formBuildData: (args: {
71
71
  buildId: string;
72
72
  prenvId: string | undefined;
73
73
  };
74
- export declare const addTeamCount: (teamStats: TeamStats, ownerTestAnnotations: TestCase['annotations']) => {
74
+ export declare const addTeamCount: (teamStats: TeamStats, ownerTestAnnotations: TestCase["annotations"]) => {
75
75
  [x: string]: number;
76
76
  };
77
- export declare const getBuildIdWithPrefix: (lastRunData: string | null, currentBuildId: string) => string;
77
+ /**
78
+ * This function is used to compute the build id with prefix.
79
+ * The prefix is required to differentiate between different builds in case the PREnv is reset and the build id starts from 1 again.
80
+ * The prefix is incremented by 1 when the current build id is less than the last build id.
81
+ * @param previousBuildId - The last build id with prefix
82
+ * @param currentBuildId - The current build id without prefix
83
+ * @returns The new build id with prefix
84
+ */
85
+ export declare const getBuildIdWithPrefix: (previousBuildId: string | null, currentBuildId: string) => string;
78
86
  export declare const formBrowserUsageData: (args: {
79
87
  startTime: number;
80
88
  testRunId: string;
@@ -92,6 +100,4 @@ export declare const formBrowserUsageData: (args: {
92
100
  browserUsageFromRequest: number;
93
101
  browserUsageAfterCreation: number;
94
102
  };
95
- type TeamName = string;
96
- export type TeamStats = Record<TeamName, number>;
97
- export {};
103
+ export type TeamStats = Record<string, number>;
@@ -100,8 +100,18 @@ const addTeamCount = (teamStats, ownerTestAnnotations) => {
100
100
  return testOwnerCount;
101
101
  };
102
102
  exports.addTeamCount = addTeamCount;
103
- const getBuildIdWithPrefix = (lastRunData, currentBuildId) => {
104
- const lastBuildIdWithPrefix = lastRunData ?? '0001-00001';
103
+ /**
104
+ * This function is used to compute the build id with prefix.
105
+ * The prefix is required to differentiate between different builds in case the PREnv is reset and the build id starts from 1 again.
106
+ * The prefix is incremented by 1 when the current build id is less than the last build id.
107
+ * @param previousBuildId - The last build id with prefix
108
+ * @param currentBuildId - The current build id without prefix
109
+ * @returns The new build id with prefix
110
+ */
111
+ const getBuildIdWithPrefix = (previousBuildId, currentBuildId) => {
112
+ // If the previous build does not exist, for instance on a new index, the default value is set.
113
+ const lastBuildIdWithPrefix = previousBuildId ?? '0001-00001';
114
+ // We are getting the prefix from the last stored build id, or setting the default value.
105
115
  const previousPrefix = (() => {
106
116
  const defaultPrefix = '0001';
107
117
  if (lastBuildIdWithPrefix.includes('-')) {
@@ -109,12 +119,15 @@ const getBuildIdWithPrefix = (lastRunData, currentBuildId) => {
109
119
  }
110
120
  return defaultPrefix;
111
121
  })();
122
+ // We are getting the last build id from the last stored build id.
112
123
  const lastBuildId = (() => {
113
124
  if (lastBuildIdWithPrefix.includes('-')) {
114
125
  return lastBuildIdWithPrefix.split('-').at(1);
115
126
  }
116
127
  return lastBuildIdWithPrefix;
117
128
  })();
129
+ // We are comparing the current build id with the last build id.
130
+ // If the current build id is less than the last build id, we are incrementing the prefix by 1, as it indicates the PREnv reset.
118
131
  if (Number(currentBuildId) < Number(lastBuildId)) {
119
132
  const newPrefix = String(Number(previousPrefix) + 1).padStart(4, '0');
120
133
  return `${newPrefix}-${currentBuildId.padStart(5, '0')}`;
@@ -122,7 +135,6 @@ const getBuildIdWithPrefix = (lastRunData, currentBuildId) => {
122
135
  return `${previousPrefix}-${currentBuildId.padStart(5, '0')}`;
123
136
  };
124
137
  exports.getBuildIdWithPrefix = getBuildIdWithPrefix;
125
- // eslint-disable-next-line max-statements
126
138
  const formBrowserUsageData = (args) => {
127
139
  let browserUsageFromRequest = 0;
128
140
  let browserUsageAfterCreation = 0;
package/dist/index.d.ts CHANGED
@@ -3,4 +3,5 @@ import PermissionsReporter, { definePermissionsReporterConfig } from './permissi
3
3
  import RetryReporter, { defineRetryReporterConfig } from './retry/reporter';
4
4
  import StepDurationReporter, { defineStepDurationReporterConfig } from './stepDuration/reporter';
5
5
  import TimelineReporter, { defineTimelineReporterConfig } from './timeline/reporter';
6
- export { AnalyticsReporter, defineAnalyticsReporterConfig, RetryReporter, defineRetryReporterConfig, TimelineReporter, defineTimelineReporterConfig, StepDurationReporter, defineStepDurationReporterConfig, PermissionsReporter, definePermissionsReporterConfig };
6
+ import TranslationReporter, { defineTranslationReporterConfig } from './translation/reporter';
7
+ export { AnalyticsReporter, defineAnalyticsReporterConfig, RetryReporter, defineRetryReporterConfig, TimelineReporter, defineTimelineReporterConfig, StepDurationReporter, defineStepDurationReporterConfig, PermissionsReporter, definePermissionsReporterConfig, TranslationReporter, defineTranslationReporterConfig, };
package/dist/index.js CHANGED
@@ -23,7 +23,7 @@ var __importStar = (this && this.__importStar) || function (mod) {
23
23
  return result;
24
24
  };
25
25
  Object.defineProperty(exports, "__esModule", { value: true });
26
- exports.definePermissionsReporterConfig = exports.PermissionsReporter = exports.defineStepDurationReporterConfig = exports.StepDurationReporter = exports.defineTimelineReporterConfig = exports.TimelineReporter = exports.defineRetryReporterConfig = exports.RetryReporter = exports.defineAnalyticsReporterConfig = exports.AnalyticsReporter = void 0;
26
+ exports.defineTranslationReporterConfig = exports.TranslationReporter = exports.definePermissionsReporterConfig = exports.PermissionsReporter = exports.defineStepDurationReporterConfig = exports.StepDurationReporter = exports.defineTimelineReporterConfig = exports.TimelineReporter = exports.defineRetryReporterConfig = exports.RetryReporter = exports.defineAnalyticsReporterConfig = exports.AnalyticsReporter = void 0;
27
27
  const reporter_1 = __importStar(require("./analytics/reporter"));
28
28
  exports.AnalyticsReporter = reporter_1.default;
29
29
  Object.defineProperty(exports, "defineAnalyticsReporterConfig", { enumerable: true, get: function () { return reporter_1.defineAnalyticsReporterConfig; } });
@@ -39,3 +39,6 @@ Object.defineProperty(exports, "defineStepDurationReporterConfig", { enumerable:
39
39
  const reporter_5 = __importStar(require("./timeline/reporter"));
40
40
  exports.TimelineReporter = reporter_5.default;
41
41
  Object.defineProperty(exports, "defineTimelineReporterConfig", { enumerable: true, get: function () { return reporter_5.defineTimelineReporterConfig; } });
42
+ const reporter_6 = __importStar(require("./translation/reporter"));
43
+ exports.TranslationReporter = reporter_6.default;
44
+ Object.defineProperty(exports, "defineTranslationReporterConfig", { enumerable: true, get: function () { return reporter_6.defineTranslationReporterConfig; } });
@@ -0,0 +1,15 @@
1
+ import type { BaseTranslationReport } from './reporter';
2
+ export type ElasticOptions = {
3
+ dryRun: boolean;
4
+ debug: boolean;
5
+ translationIndex: string;
6
+ elasticUrl: string;
7
+ elasticToken: string;
8
+ };
9
+ export declare class Elastic {
10
+ private readonly options;
11
+ private request;
12
+ constructor(options: ElasticOptions);
13
+ setRequestContext(): Promise<void>;
14
+ saveTranslationsData(testData: BaseTranslationReport): Promise<void>;
15
+ }
@@ -0,0 +1,32 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Elastic = void 0;
4
+ const node_http2_1 = require("node:http2");
5
+ const test_1 = require("@playwright/test");
6
+ class Elastic {
7
+ constructor(options) {
8
+ this.options = options;
9
+ }
10
+ async setRequestContext() {
11
+ this.request = await test_1.request.newContext({
12
+ baseURL: this.options.elasticUrl,
13
+ ignoreHTTPSErrors: true,
14
+ extraHTTPHeaders: {
15
+ Authorization: `ApiKey ${this.options.elasticToken}`,
16
+ },
17
+ });
18
+ }
19
+ async saveTranslationsData(testData) {
20
+ try {
21
+ const response = await this.request.post(`/${this.options.translationIndex}/_doc`, {
22
+ data: testData,
23
+ });
24
+ (0, test_1.expect)(response.status()).toEqual(node_http2_1.constants.HTTP_STATUS_CREATED);
25
+ }
26
+ catch (error) {
27
+ console.error('Failed to save Shopify translation data');
28
+ console.error(error);
29
+ }
30
+ }
31
+ }
32
+ exports.Elastic = Elastic;
@@ -0,0 +1,33 @@
1
+ import type { Reporter, TestCase, TestResult, TestStep } from '@playwright/test/reporter';
2
+ import type { ElasticOptions } from './elastic';
3
+ export declare enum ShopifySteps {
4
+ importing = "Start IMPORTING content",
5
+ translating = "Start TRANSLATING content",
6
+ publishing = "Start PUBLISHING content"
7
+ }
8
+ export type BaseTranslationReport = {
9
+ totalImportDuration: number;
10
+ totalTranslationDuration: number;
11
+ totalPublishDuration: number;
12
+ totalImportSteps: number;
13
+ totalTranslationSteps: number;
14
+ totalPublishingSteps: number;
15
+ name: string;
16
+ duration: number;
17
+ testRunId: string;
18
+ result: string;
19
+ timestamp_: number;
20
+ };
21
+ export type TranslationsReportData = {
22
+ data: Record<string, BaseTranslationReport>;
23
+ };
24
+ export default class TranslationReporter implements Reporter {
25
+ private readonly elastic;
26
+ private readonly options;
27
+ readonly listOfTests: TranslationsReportData;
28
+ constructor(options: ElasticOptions);
29
+ onBegin(): Promise<void>;
30
+ onStepEnd(test: TestCase, result: TestResult, step: TestStep): void;
31
+ onTestEnd(test: TestCase, result: TestResult): Promise<void>;
32
+ }
33
+ export declare const defineTranslationReporterConfig: (options: ConstructorParameters<typeof TranslationReporter>[0]) => ElasticOptions;
@@ -0,0 +1,101 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.defineTranslationReporterConfig = exports.ShopifySteps = void 0;
4
+ const faker_1 = require("@faker-js/faker");
5
+ const elastic_1 = require("./elastic");
6
+ var ShopifySteps;
7
+ (function (ShopifySteps) {
8
+ ShopifySteps["importing"] = "Start IMPORTING content";
9
+ ShopifySteps["translating"] = "Start TRANSLATING content";
10
+ ShopifySteps["publishing"] = "Start PUBLISHING content";
11
+ })(ShopifySteps || (exports.ShopifySteps = ShopifySteps = {}));
12
+ // eslint-disable-next-line import/no-default-export
13
+ class TranslationReporter {
14
+ constructor(options) {
15
+ this.options = options;
16
+ this.listOfTests = { data: {} };
17
+ this.elastic = new elastic_1.Elastic(options);
18
+ }
19
+ async onBegin() {
20
+ try {
21
+ if (!this.options.dryRun) {
22
+ await this.elastic.setRequestContext();
23
+ }
24
+ }
25
+ catch (error) {
26
+ console.error("Could not perform 'onBegin' analytics step", error);
27
+ }
28
+ }
29
+ // eslint-disable-next-line max-statements
30
+ onStepEnd(test, result, step) {
31
+ try {
32
+ const testProjectName = test?.parent?.project()?.name;
33
+ if (!testProjectName?.toLowerCase().includes('shopify')) {
34
+ return;
35
+ }
36
+ const stepName = step.title;
37
+ const testId = `${test.id}-${result.retry ?? 0}`;
38
+ // Ensure that the data object for this test ID exists
39
+ if (!this.listOfTests.data[testId]) {
40
+ this.listOfTests.data[testId] = {
41
+ totalImportDuration: 0,
42
+ totalPublishDuration: 0,
43
+ totalTranslationDuration: 0,
44
+ totalImportSteps: 0,
45
+ totalTranslationSteps: 0,
46
+ totalPublishingSteps: 0,
47
+ name: test.title,
48
+ duration: 0,
49
+ testRunId: faker_1.faker.string.uuid(),
50
+ result: '',
51
+ timestamp_: 0,
52
+ };
53
+ }
54
+ if (stepName.toLowerCase().includes(ShopifySteps.importing.toLowerCase())) {
55
+ // Directly accumulate the duration of the importing step
56
+ this.listOfTests.data[testId].totalImportDuration += step.duration;
57
+ this.listOfTests.data[testId].totalImportSteps++;
58
+ }
59
+ if (stepName.toLowerCase().includes(ShopifySteps.translating.toLowerCase())) {
60
+ this.listOfTests.data[testId].totalTranslationSteps++;
61
+ this.listOfTests.data[testId].totalTranslationDuration += step.duration;
62
+ }
63
+ if (stepName.toLowerCase().includes(ShopifySteps.publishing.toLowerCase())) {
64
+ this.listOfTests.data[testId].totalPublishingSteps++;
65
+ this.listOfTests.data[testId].totalPublishDuration += step.duration;
66
+ }
67
+ }
68
+ catch (error) {
69
+ console.error("Could not perform 'onStepEnd' analytics step", error);
70
+ }
71
+ }
72
+ async onTestEnd(test, result) {
73
+ try {
74
+ const testProjectName = test?.parent?.project()?.name;
75
+ if (!testProjectName?.toLowerCase().includes('shopify')) {
76
+ return;
77
+ }
78
+ const testId = `${test.id}-${result.retry ?? 0}`;
79
+ // No need to calculate totalImportDuration here; it's already accumulated
80
+ this.listOfTests.data[testId] = {
81
+ ...this.listOfTests.data[testId],
82
+ timestamp_: result.startTime.getTime(),
83
+ name: test.title,
84
+ result: result.status,
85
+ duration: result.duration,
86
+ };
87
+ if (this.options.debug) {
88
+ console.log(this.listOfTests.data[testId]);
89
+ }
90
+ if (!this.options.dryRun) {
91
+ await this.elastic.saveTranslationsData(this.listOfTests.data[testId]);
92
+ }
93
+ }
94
+ catch (error) {
95
+ console.error("Could not perform 'onTestEnd' analytics step", error);
96
+ }
97
+ }
98
+ }
99
+ exports.default = TranslationReporter;
100
+ const defineTranslationReporterConfig = (options) => options;
101
+ exports.defineTranslationReporterConfig = defineTranslationReporterConfig;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,162 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ Object.defineProperty(exports, "__esModule", { value: true });
26
+ const test_1 = __importStar(require("@playwright/test"));
27
+ const reporter_1 = __importStar(require("../reporter"));
28
+ test_1.default.describe('TranslationReporter Unit Tests', () => {
29
+ let reporter;
30
+ test_1.default.beforeEach(async () => {
31
+ reporter = new reporter_1.default({
32
+ dryRun: true, // Use dryRun to prevent requests to Elastic
33
+ debug: false,
34
+ elasticToken: '',
35
+ elasticUrl: '',
36
+ translationIndex: '',
37
+ });
38
+ });
39
+ test_1.default.describe('onStepEnd', () => {
40
+ // eslint-disable-next-line max-statements
41
+ (0, test_1.default)('should correctly accumulate data for importing step', () => {
42
+ const testCase = {
43
+ id: 'test-id',
44
+ title: 'Shopify Import Test',
45
+ parent: {
46
+ project: () => ({ name: 'shopify' }),
47
+ },
48
+ };
49
+ const testResult = {
50
+ retry: 0,
51
+ };
52
+ const testStep1 = {
53
+ title: reporter_1.ShopifySteps.importing,
54
+ duration: 50,
55
+ };
56
+ const testStep2 = {
57
+ title: reporter_1.ShopifySteps.importing,
58
+ duration: 50,
59
+ };
60
+ const testStep3 = {
61
+ title: reporter_1.ShopifySteps.importing,
62
+ duration: 100,
63
+ };
64
+ const testStep4 = {
65
+ title: reporter_1.ShopifySteps.translating,
66
+ duration: 300,
67
+ };
68
+ const testStep5 = {
69
+ title: reporter_1.ShopifySteps.translating,
70
+ duration: 200,
71
+ };
72
+ const testStep6 = {
73
+ title: reporter_1.ShopifySteps.publishing,
74
+ duration: 600,
75
+ };
76
+ reporter.onStepEnd(testCase, testResult, testStep1);
77
+ reporter.onStepEnd(testCase, testResult, testStep2);
78
+ reporter.onStepEnd(testCase, testResult, testStep3);
79
+ reporter.onStepEnd(testCase, testResult, testStep4);
80
+ reporter.onStepEnd(testCase, testResult, testStep5);
81
+ reporter.onStepEnd(testCase, testResult, testStep6);
82
+ const testId = `${testCase.id}-${testResult.retry ?? 0}`;
83
+ const resultData = reporter.listOfTests.data[testId];
84
+ (0, test_1.expect)(resultData).toMatchObject({
85
+ totalImportDuration: 200,
86
+ totalTranslationDuration: 500,
87
+ totalPublishDuration: 600,
88
+ totalImportSteps: 3,
89
+ totalTranslationSteps: 2,
90
+ totalPublishingSteps: 1,
91
+ name: 'Shopify Import Test',
92
+ duration: 0,
93
+ testRunId: test_1.expect.any(String),
94
+ result: '',
95
+ timestamp_: 0,
96
+ });
97
+ });
98
+ (0, test_1.default)('should not accumulate data for non-Shopify tests', () => {
99
+ const testCase = {
100
+ id: 'test-id',
101
+ title: 'Non-Shopify Test',
102
+ parent: {
103
+ project: () => ({ name: 'other-project' }),
104
+ },
105
+ };
106
+ const testResult = {
107
+ retry: 0,
108
+ };
109
+ const testStep = {
110
+ title: 'Some Step',
111
+ duration: 400,
112
+ };
113
+ reporter.onStepEnd(testCase, testResult, testStep);
114
+ const testId = `${testCase.id}-${testResult.retry ?? 0}`;
115
+ const resultData = reporter.listOfTests.data[testId];
116
+ (0, test_1.expect)(resultData).toBeUndefined();
117
+ });
118
+ });
119
+ test_1.default.describe('onTestEnd', () => {
120
+ (0, test_1.default)('should correctly finalize and store test data', async () => {
121
+ const testCase = {
122
+ id: 'test-id',
123
+ title: 'Shopify Final Test',
124
+ parent: {
125
+ project: () => ({ name: 'shopify' }),
126
+ },
127
+ };
128
+ const testResult = {
129
+ retry: 0,
130
+ startTime: new Date(),
131
+ status: 'passed',
132
+ duration: 600,
133
+ };
134
+ await reporter.onTestEnd(testCase, testResult);
135
+ const testId = `${testCase.id}-${testResult.retry ?? 0}`;
136
+ const resultData = reporter.listOfTests.data[testId];
137
+ (0, test_1.expect)(resultData.name).toBe(testCase.title);
138
+ (0, test_1.expect)(resultData.result).toBe(testResult.status);
139
+ (0, test_1.expect)(resultData.duration).toBe(testResult.duration);
140
+ (0, test_1.expect)(resultData.timestamp_).toBe(testResult.startTime.getTime());
141
+ });
142
+ (0, test_1.default)('should not store data for non-Shopify tests', async () => {
143
+ const testCase = {
144
+ id: 'non-shopify-test-id',
145
+ title: 'Non-Shopify Test',
146
+ parent: {
147
+ project: () => ({ name: 'other-project' }),
148
+ },
149
+ };
150
+ const testResult = {
151
+ retry: 0,
152
+ startTime: new Date(),
153
+ status: 'passed',
154
+ duration: 600,
155
+ };
156
+ await reporter.onTestEnd(testCase, testResult);
157
+ const testId = `${testCase.id}-${testResult.retry ?? 0}`;
158
+ const resultData = reporter.listOfTests.data[testId];
159
+ (0, test_1.expect)(resultData).toBeUndefined();
160
+ });
161
+ });
162
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lokalise/playwright-reporters",
3
- "version": "1.7.0",
3
+ "version": "1.8.0",
4
4
  "scripts": {
5
5
  "lint:eslint": "eslint --cache . --ext .js,.cjs,.ts",
6
6
  "lint:ts": "tsc --noEmit",
@@ -45,37 +45,44 @@
45
45
  "./permissions": {
46
46
  "import": "./dist/permissions/reporter.js",
47
47
  "require": "./dist/permissions/reporter.js"
48
+ },
49
+ "./translation": {
50
+ "import": "./dist/translation/reporter.js",
51
+ "require": "./dist/translation/reporter.js"
48
52
  }
49
53
  },
50
54
  "publishConfig": {
51
55
  "access": "public"
52
56
  },
53
57
  "devDependencies": {
54
- "@commitlint/cli": "19.3.0",
58
+ "@commitlint/cli": "19.4.0",
55
59
  "@commitlint/config-conventional": "19.2.2",
56
- "@commitlint/prompt-cli": "19.3.1",
57
- "@lokalise/eslint-config-frontend": "^4.6.0",
60
+ "@commitlint/prompt-cli": "19.4.0",
61
+ "@lokalise/eslint-config-frontend": "^4.6.1",
58
62
  "@lokalise/prettier-config": "^1.0.1",
59
63
  "@semantic-release/changelog": "6.0.3",
60
64
  "@semantic-release/commit-analyzer": "13.0.0",
61
65
  "@semantic-release/git": "10.0.1",
62
- "@semantic-release/github": "10.0.6",
66
+ "@semantic-release/github": "10.1.6",
63
67
  "@semantic-release/npm": "12.0.1",
64
- "@semantic-release/release-notes-generator": "13.0.0",
65
- "@types/lodash": "^4.17.5",
66
- "@types/node": "^20.14.2",
68
+ "@semantic-release/release-notes-generator": "14.0.1",
69
+ "@types/lodash": "^4.17.7",
70
+ "@types/node": "^22.3.0",
67
71
  "eslint-config-prettier": "^9.1.0",
68
- "eslint-plugin-prettier": "^5.1.3",
69
- "husky": "9.0.11",
70
- "prettier": "^3.3.2",
71
- "semantic-release": "23.1.1",
72
- "typescript": "5.4.5"
72
+ "eslint-plugin-prettier": "^5.2.1",
73
+ "husky": "9.1.4",
74
+ "prettier": "^3.3.3",
75
+ "semantic-release": "24.0.0",
76
+ "typescript": "5.5.4"
73
77
  },
74
78
  "dependencies": {
75
79
  "@faker-js/faker": "^8.4.1",
76
- "@playwright/test": "^1.44.1",
80
+ "@playwright/test": "^1.46.0",
77
81
  "fastest-levenshtein": "^1.0.16",
78
82
  "lodash": "^4.17.21"
79
83
  },
84
+ "overrides": {
85
+ "conventional-changelog-conventionalcommits": ">= 8.0.0"
86
+ },
80
87
  "prettier": "@lokalise/prettier-config"
81
88
  }