@tiden/reporter-commons 0.1.0 → 0.2.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,46 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.2.0
4
+
5
+ - **`rootDir` / `TIDEN_ROOT_DIR` option**, plus `normalizeSpecPath` and `resolveRootDir` on the
6
+ `/internal` entry point: the base a case signature's spec-file segment is resolved against,
7
+ defaulting to `process.cwd()`. Consumed by `@tiden/vitest-reporter` and `@tiden/jest-reporter`.
8
+ Env is read directly by `resolveRootDir` rather than through `OptionsResolver`, whose composed
9
+ result never reaches a framework reporter — resolving it any other way leaves `TIDEN_ROOT_DIR`
10
+ silently ignored while looking configured.
11
+ - **Fixed a polynomial ReDoS in `normalizeSpecPath`.** The root's trailing slashes were trimmed
12
+ with `/\/+$/`, which backtracks quadratically on a root of repeated slashes — and the root is
13
+ configurable (`rootDir` / `TIDEN_ROOT_DIR`). Measured at 546ms for a 40k-character root against
14
+ 0ms for the scan that replaces it. Caught by CodeQL (`js/polynomial-redos`) before release; same
15
+ class as the step-marker parser fixed in 0.1.1.
16
+ - **Fixed: `TIDEN_FALLBACK` was silently ignored.** It was declared in `EnvEnum`, `EnvType` and the
17
+ env validation schema, but `envToConfig` never mapped it into `ConfigType` — so the value
18
+ validated and was then dropped. It decides what happens when `tiden` mode cannot start, which is
19
+ the disable path below.
20
+ - **A reporter that stops MID-RUN says so too.** `FallbackCoordinator.disable()` turned reporting
21
+ off after an upstream (and fallback) failure while logging only the error, not the consequence:
22
+ the suite carried on, every later result went nowhere, and the run ended green. A present-but-
23
+ rejected token reached the same silent failure as a missing one by a different path.
24
+ - **The credential list is no longer appended to every disabled message.** It is stated where a
25
+ missing setting is a live suspect, and omitted where the error above is the answer — a message
26
+ padded with the same tail every time stops being read.
27
+ - **`buildReporters` says why it disabled the reporter.** Both disabling paths — a config
28
+ `mode`/`fallback` of `off`, and a `tiden` mode that cannot start because one of the four
29
+ required settings is missing — used to set `disabled = true` with no output. A disabled reporter
30
+ is indistinguishable from a working one, which cost a user an hour. Each branch now announces
31
+ itself through `logReporterDisabled`, naming the reason, the remedy, and the credentials
32
+ `createRunReporter` requires. Deliberately `logger.log`, not `logDebug`.
33
+
34
+ ## 0.1.1
35
+
36
+ - **Fixed a polynomial ReDoS in `extractAndCleanStep`** (CodeQL `js/polynomial-redos`, high).
37
+ Its single regex spanned both the `TidenExpRes:` and `TidenData:` markers and needed an
38
+ ambiguous `\s*:?\s*` prefix: for a run of N spaces there are O(N) ways to split them across
39
+ the two `\s*`, so a step name containing a long run of spaces backtracked polynomially.
40
+ Replaced with index-based slicing, matching what upstream `qase-javascript-commons` moved to.
41
+ - Two bugs fixed along the way: a single marker with no counterpart was silently ignored (the
42
+ old regex required both), and `.` dropped multiline marker values.
43
+
3
44
  ## 0.1.0
4
45
 
5
46
  Initial Tiden fork release of `@tiden/reporter-commons`.
package/README.md CHANGED
@@ -9,7 +9,7 @@ See the [root README](https://github.com/qase-tms/tiden-javascript/blob/main/REA
9
9
  ## Who uses this
10
10
 
11
11
  This package is **not meant to be installed or configured directly** in a test project — it's a
12
- dependency of framework-specific reporters (currently [`@tiden/playwright-reporter`](https://github.com/qase-tms/tiden-javascript/blob/main/playwright/README.md)).
12
+ dependency of framework-specific reporters ([`@tiden/playwright-reporter`](https://github.com/qase-tms/tiden-javascript/blob/main/playwright/README.md), [`@tiden/vitest-reporter`](https://github.com/qase-tms/tiden-javascript/blob/main/vitest/README.md), and [`@tiden/jest-reporter`](https://github.com/qase-tms/tiden-javascript/blob/main/jest/README.md)).
13
13
  Install and configure the reporter for your framework instead; it composes this package's config
14
14
  loader, env resolver, and API transport internally.
15
15
 
@@ -32,6 +32,10 @@ export declare const configValidationSchema: {
32
32
  type: string;
33
33
  nullable: boolean;
34
34
  };
35
+ rootDir: {
36
+ type: string;
37
+ nullable: boolean;
38
+ };
35
39
  statusMapping: {
36
40
  type: string;
37
41
  nullable: boolean;
@@ -35,6 +35,10 @@ exports.configValidationSchema = {
35
35
  type: 'string',
36
36
  nullable: true,
37
37
  },
38
+ rootDir: {
39
+ type: 'string',
40
+ nullable: true,
41
+ },
38
42
  statusMapping: {
39
43
  type: 'object',
40
44
  nullable: true,
@@ -8,6 +8,7 @@ export declare enum EnvEnum {
8
8
  environment = "TIDEN_ENVIRONMENT",
9
9
  captureLogs = "TIDEN_CAPTURE_LOGS",
10
10
  rootSuite = "TIDEN_ROOT_SUITE",
11
+ rootDir = "TIDEN_ROOT_DIR",
11
12
  statusMapping = "TIDEN_STATUS_MAPPING"
12
13
  }
13
14
  /**
@@ -12,6 +12,7 @@ var EnvEnum;
12
12
  EnvEnum["environment"] = "TIDEN_ENVIRONMENT";
13
13
  EnvEnum["captureLogs"] = "TIDEN_CAPTURE_LOGS";
14
14
  EnvEnum["rootSuite"] = "TIDEN_ROOT_SUITE";
15
+ EnvEnum["rootDir"] = "TIDEN_ROOT_DIR";
15
16
  EnvEnum["statusMapping"] = "TIDEN_STATUS_MAPPING";
16
17
  })(EnvEnum || (exports.EnvEnum = EnvEnum = {}));
17
18
  /**
@@ -9,10 +9,16 @@ const writer_1 = require("../writer");
9
9
  */
10
10
  const envToConfig = (env) => ({
11
11
  mode: env[env_enum_1.EnvEnum.mode],
12
+ // TIDEN_FALLBACK was declared in EnvEnum, EnvType and the validation schema
13
+ // but never mapped here, so setting it did nothing: the value validated, then
14
+ // vanished. It decides what happens when `tiden` mode cannot start, which is
15
+ // exactly the silent-disable path the reporter now announces.
16
+ fallback: env[env_enum_1.EnvEnum.fallback],
12
17
  debug: env[env_enum_1.EnvEnum.debug],
13
18
  environment: env[env_enum_1.EnvEnum.environment],
14
19
  captureLogs: env[env_enum_1.EnvEnum.captureLogs],
15
20
  rootSuite: env[env_enum_1.EnvEnum.rootSuite],
21
+ rootDir: env[env_enum_1.EnvEnum.rootDir],
16
22
  statusMapping: env[env_enum_1.EnvEnum.statusMapping] ?
17
23
  Object.fromEntries(env[env_enum_1.EnvEnum.statusMapping].split(',').map(item => {
18
24
  const [from, to] = item.split('=');
@@ -8,6 +8,7 @@ export interface EnvType {
8
8
  [EnvEnum.environment]?: string;
9
9
  [EnvEnum.captureLogs]?: boolean;
10
10
  [EnvEnum.rootSuite]?: string;
11
+ [EnvEnum.rootDir]?: string;
11
12
  [EnvEnum.statusMapping]?: string;
12
13
  [EnvTidenEnum.product]?: string;
13
14
  [EnvTidenEnum.uploadAttachments]?: boolean;
@@ -36,6 +36,10 @@ exports.envValidationSchema = {
36
36
  type: 'string',
37
37
  nullable: true,
38
38
  },
39
+ [env_enum_1.EnvEnum.rootDir]: {
40
+ type: 'string',
41
+ nullable: true,
42
+ },
39
43
  [env_enum_1.EnvEnum.statusMapping]: {
40
44
  type: 'string',
41
45
  nullable: true,
@@ -6,3 +6,5 @@ export type { FileSuiteNode } from './suite-file';
6
6
  export { parseTidenIdsFromString } from './ids-parser';
7
7
  export { filterPositiveIds } from './filter-positive-ids';
8
8
  export { normalizeSuitePart } from './suite-normalizer';
9
+ export { normalizeSpecPath } from './spec-path';
10
+ export { resolveRootDir } from './spec-path';
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.normalizeSuitePart = exports.filterPositiveIds = exports.parseTidenIdsFromString = exports.getFile = exports.extractAndCleanStep = exports.removeTidenIdsFromTitle = void 0;
3
+ exports.resolveRootDir = exports.normalizeSpecPath = exports.normalizeSuitePart = exports.filterPositiveIds = exports.parseTidenIdsFromString = exports.getFile = exports.extractAndCleanStep = exports.removeTidenIdsFromTitle = void 0;
4
4
  var title_1 = require("./title");
5
5
  Object.defineProperty(exports, "removeTidenIdsFromTitle", { enumerable: true, get: function () { return title_1.removeTidenIdsFromTitle; } });
6
6
  var step_parser_1 = require("./step-parser");
@@ -13,3 +13,7 @@ var filter_positive_ids_1 = require("./filter-positive-ids");
13
13
  Object.defineProperty(exports, "filterPositiveIds", { enumerable: true, get: function () { return filter_positive_ids_1.filterPositiveIds; } });
14
14
  var suite_normalizer_1 = require("./suite-normalizer");
15
15
  Object.defineProperty(exports, "normalizeSuitePart", { enumerable: true, get: function () { return suite_normalizer_1.normalizeSuitePart; } });
16
+ var spec_path_1 = require("./spec-path");
17
+ Object.defineProperty(exports, "normalizeSpecPath", { enumerable: true, get: function () { return spec_path_1.normalizeSpecPath; } });
18
+ var spec_path_2 = require("./spec-path");
19
+ Object.defineProperty(exports, "resolveRootDir", { enumerable: true, get: function () { return spec_path_2.resolveRootDir; } });
@@ -0,0 +1,12 @@
1
+ export declare function normalizeSpecPath(fullPath: string, cwd?: string): string;
2
+ /**
3
+ * The base a spec-file segment is resolved against, in precedence order:
4
+ * an explicit `rootDir` option, then `TIDEN_ROOT_DIR`, then undefined
5
+ * (meaning `normalizeSpecPath` falls back to `process.cwd()`).
6
+ *
7
+ * Env is read here rather than through the usual config pipeline because that
8
+ * pipeline merges env inside `OptionsResolver`, whose result never reaches a
9
+ * framework reporter — so a framework-side setting would silently ignore its
10
+ * own environment variable.
11
+ */
12
+ export declare function resolveRootDir(explicit?: string | undefined): string | undefined;
@@ -0,0 +1,55 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.normalizeSpecPath = normalizeSpecPath;
4
+ exports.resolveRootDir = resolveRootDir;
5
+ const env_enum_1 = require("../env/env-enum");
6
+ /**
7
+ * Normalizes an absolute spec-file path to a project-relative one, for use as
8
+ * the leading segment of a case signature.
9
+ *
10
+ * Kept as a SINGLE segment with its slashes intact — it is not split on `/`.
11
+ * That matches Playwright's `titlePath()` (which yields the spec file as one
12
+ * element) and the app-side vitest CI transform, so a case reported by the
13
+ * reporter and the same case reported by CI resolve to one identity.
14
+ *
15
+ * Path-shaped only: no filesystem access, and an id that does not live under
16
+ * `cwd` (a Vite virtual module, for instance) is returned unchanged rather
17
+ * than forced into a relative form.
18
+ */
19
+ const SLASH = '/'.charCodeAt(0);
20
+ function normalizeSpecPath(fullPath, cwd = process.cwd()) {
21
+ const normalized = fullPath.replace(/\\/g, '/');
22
+ const root = rootPrefix(cwd);
23
+ return normalized.startsWith(root) ? normalized.slice(root.length) : normalized;
24
+ }
25
+ /**
26
+ * The root as a `/`-terminated prefix, with any trailing slashes collapsed to
27
+ * exactly one.
28
+ *
29
+ * Trimmed by scanning rather than with `/\/+$/`: that pattern backtracks
30
+ * quadratically on a root of many slashes, and the root can come from
31
+ * configuration (`rootDir` / `TIDEN_ROOT_DIR`). Same class of defect as the
32
+ * step-marker parser fixed in commons 0.1.1 — keep this regex-free.
33
+ */
34
+ function rootPrefix(cwd) {
35
+ const normalized = cwd.replace(/\\/g, '/');
36
+ let end = normalized.length;
37
+ while (end > 0 && normalized.charCodeAt(end - 1) === SLASH) {
38
+ end -= 1;
39
+ }
40
+ return normalized.slice(0, end) + '/';
41
+ }
42
+ /**
43
+ * The base a spec-file segment is resolved against, in precedence order:
44
+ * an explicit `rootDir` option, then `TIDEN_ROOT_DIR`, then undefined
45
+ * (meaning `normalizeSpecPath` falls back to `process.cwd()`).
46
+ *
47
+ * Env is read here rather than through the usual config pipeline because that
48
+ * pipeline merges env inside `OptionsResolver`, whose result never reaches a
49
+ * framework reporter — so a framework-side setting would silently ignore its
50
+ * own environment variable.
51
+ */
52
+ function resolveRootDir(explicit) {
53
+ const fromEnv = process.env[env_enum_1.EnvEnum.rootDir];
54
+ return explicit ?? (fromEnv !== undefined && fromEnv !== '' ? fromEnv : undefined);
55
+ }
@@ -7,5 +7,10 @@ export interface ExtractedStep {
7
7
  * Parses a step string for inline `TidenExpRes:` (expected result) and `TidenData:`
8
8
  * (data) markers. Returns the extracted parts and the input string with markers
9
9
  * removed. If no markers are present, returns nulls and the original string.
10
+ *
11
+ * Index-based rather than regex-matched: a single pattern spanning both markers
12
+ * needs an ambiguous `\s*:?\s*` prefix, which backtracks polynomially on a step
13
+ * name containing a long run of spaces (CodeQL js/polynomial-redos). Slicing is
14
+ * linear, and it also keeps multiline marker values intact, which `.` did not.
10
15
  */
11
16
  export declare function extractAndCleanStep(input: string): ExtractedStep;
@@ -1,28 +1,47 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.extractAndCleanStep = extractAndCleanStep;
4
+ const EXPECTED_RESULT_MARKER = 'TidenExpRes:';
5
+ const DATA_MARKER = 'TidenData:';
6
+ /**
7
+ * Drops the marker's optional second colon and the surrounding whitespace, so
8
+ * that both `TidenExpRes: value` and `TidenExpRes:: value` yield `value`.
9
+ *
10
+ * Anchored and always matchable, so it cannot backtrack.
11
+ */
12
+ function readMarkerValue(raw) {
13
+ return raw.replace(/^\s*:?\s*/, '').trim();
14
+ }
4
15
  /**
5
16
  * Parses a step string for inline `TidenExpRes:` (expected result) and `TidenData:`
6
17
  * (data) markers. Returns the extracted parts and the input string with markers
7
18
  * removed. If no markers are present, returns nulls and the original string.
19
+ *
20
+ * Index-based rather than regex-matched: a single pattern spanning both markers
21
+ * needs an ambiguous `\s*:?\s*` prefix, which backtracks polynomially on a step
22
+ * name containing a long run of spaces (CodeQL js/polynomial-redos). Slicing is
23
+ * linear, and it also keeps multiline marker values intact, which `.` did not.
8
24
  */
9
25
  function extractAndCleanStep(input) {
10
- let expectedResult = null;
11
- let data = null;
12
- let cleanedString = input;
13
- const hasExpectedResult = input.includes('TidenExpRes:');
14
- const hasData = input.includes('TidenData:');
15
- if (hasExpectedResult || hasData) {
16
- const regex = /TidenExpRes:\s*:?\s*(.*?)\s*(?=TidenData:|$)TidenData:\s*:?\s*(.*)?/;
17
- const match = input.match(regex);
18
- if (match) {
19
- expectedResult = match[1]?.trim() ?? null;
20
- data = match[2]?.trim() ?? null;
21
- cleanedString = input
22
- .replace(/TidenExpRes:\s*:?\s*.*?(?=TidenData:|$)/, '')
23
- .replace(/TidenData:\s*:?\s*.*/, '')
24
- .trim();
25
- }
26
+ const expectedResultIndex = input.indexOf(EXPECTED_RESULT_MARKER);
27
+ const dataIndex = input.indexOf(DATA_MARKER);
28
+ if (expectedResultIndex === -1 && dataIndex === -1) {
29
+ return { expectedResult: null, data: null, cleanedString: input };
26
30
  }
27
- return { expectedResult, data, cleanedString };
31
+ // Each marker's value runs until the next marker, or to the end of the input.
32
+ const expectedResultEnd = dataIndex > expectedResultIndex ? dataIndex : input.length;
33
+ const dataEnd = expectedResultIndex > dataIndex ? expectedResultIndex : input.length;
34
+ const expectedResult = expectedResultIndex === -1
35
+ ? null
36
+ : readMarkerValue(input.slice(expectedResultIndex + EXPECTED_RESULT_MARKER.length, expectedResultEnd));
37
+ const data = dataIndex === -1
38
+ ? ''
39
+ : readMarkerValue(input.slice(dataIndex + DATA_MARKER.length, dataEnd));
40
+ const markerIndexes = [expectedResultIndex, dataIndex].filter((index) => index !== -1);
41
+ return {
42
+ expectedResult,
43
+ // an omitted data value stays `null`, matching the long-standing contract
44
+ data: data === '' ? null : data,
45
+ cleanedString: input.slice(0, Math.min(...markerIndexes)).trim(),
46
+ };
28
47
  }
@@ -25,6 +25,17 @@ export type OptionsType = {
25
25
  debug?: boolean | undefined;
26
26
  environment?: string | undefined;
27
27
  rootSuite?: string | undefined;
28
+ /**
29
+ * Base directory the spec-file segment of a case signature is resolved
30
+ * against. Defaults to `process.cwd()`.
31
+ *
32
+ * Set it when the runner's working directory is not the root you want that
33
+ * segment measured from — a monorepo running vitest from a sub-package, for
34
+ * instance. The signature is matched byte-for-byte server-side, so every
35
+ * producer reporting into one product must agree on this base or the same
36
+ * test lands as two cases.
37
+ */
38
+ rootDir?: string | undefined;
28
39
  statusMapping?: Record<string, string> | undefined;
29
40
  logging?: RecursivePartial<LoggingOptionsType> | undefined;
30
41
  tiden?: RecursivePartial<TidenOptionsType> | undefined;
@@ -41,6 +41,16 @@ export declare class FallbackCoordinator {
41
41
  * disabled or the operation produced no value.
42
42
  */
43
43
  run<T>(op: (reporter: InternalReporterInterface) => Promise<T>, opName: string): Promise<T | undefined>;
44
+ /**
45
+ * Turning the reporter off MID-RUN, after an upstream (and fallback) failure.
46
+ *
47
+ * The failure itself is logged by the caller, but the consequence was not:
48
+ * the suite carried on, every later result went nowhere, and the run ended
49
+ * green. That is the same silent-failure symptom the construction-time
50
+ * announcements in `tiden.ts` were added to remove, reached by a different
51
+ * path — a token that is present but rejected, rather than one that is
52
+ * missing. Guarded by `this.disabled`, so it says it once.
53
+ */
44
54
  private disable;
45
55
  private activateFallback;
46
56
  private runOnFallback;
@@ -83,10 +83,22 @@ class FallbackCoordinator {
83
83
  return this.runOnFallback(op, opName);
84
84
  }
85
85
  }
86
+ /**
87
+ * Turning the reporter off MID-RUN, after an upstream (and fallback) failure.
88
+ *
89
+ * The failure itself is logged by the caller, but the consequence was not:
90
+ * the suite carried on, every later result went nowhere, and the run ended
91
+ * green. That is the same silent-failure symptom the construction-time
92
+ * announcements in `tiden.ts` were added to remove, reached by a different
93
+ * path — a token that is present but rejected, rather than one that is
94
+ * missing. Guarded by `this.disabled`, so it says it once.
95
+ */
86
96
  disable() {
87
97
  if (this.disabled)
88
98
  return;
89
99
  this.disabled = true;
100
+ this.logger.log('reporter disabled — the error above stopped it mid-run, and nothing further ' +
101
+ 'from this suite will be reported to Tiden. Results already sent are unaffected.');
90
102
  if (!this.onDisabledFired) {
91
103
  this.onDisabledFired = true;
92
104
  this.callbacks.onDisabled?.();
package/dist/tiden.d.ts CHANGED
@@ -31,6 +31,17 @@ export declare class TidenReporter implements ReporterInterface {
31
31
  */
32
32
  private constructor();
33
33
  private buildLogger;
34
+ /**
35
+ * Announces that this reporter will send nothing, and why.
36
+ *
37
+ * A disabled reporter is indistinguishable from a working one: the suite
38
+ * runs, the tests pass, and nothing is reported. A field user concluded the
39
+ * reporter was broken and spent an hour on it before finding `mode: 'off'`
40
+ * in their own jest.config.js — the reporter was merely inert, and had said
41
+ * so nowhere. This is deliberately `log`, not `logDebug`: a message only
42
+ * visible once you already suspect the problem does not solve it.
43
+ */
44
+ private logReporterDisabled;
34
45
  private buildReporters;
35
46
  private persistInitialState;
36
47
  static getInstance(options: OptionsType): TidenReporter;
package/dist/tiden.js CHANGED
@@ -87,6 +87,28 @@ class TidenReporter {
87
87
  }
88
88
  return new logger_1.Logger(opts);
89
89
  }
90
+ /**
91
+ * Announces that this reporter will send nothing, and why.
92
+ *
93
+ * A disabled reporter is indistinguishable from a working one: the suite
94
+ * runs, the tests pass, and nothing is reported. A field user concluded the
95
+ * reporter was broken and spent an hour on it before finding `mode: 'off'`
96
+ * in their own jest.config.js — the reporter was merely inert, and had said
97
+ * so nowhere. This is deliberately `log`, not `logDebug`: a message only
98
+ * visible once you already suspect the problem does not solve it.
99
+ */
100
+ logReporterDisabled(reason, remedy, credentialsRelevant = false) {
101
+ // The credentials sentence is opt-in, not a suffix on every message. Telling
102
+ // someone who set `mode: "off"` on purpose that "tiden" mode needs an API
103
+ // token answers a question they did not ask, and padding every message with
104
+ // the same tail is how a message stops being read.
105
+ const credentials = credentialsRelevant
106
+ ? ' "tiden" mode also requires tiden.api.token, tiden.product and' +
107
+ ' tiden.api.baseUrl (TIDEN_API_TOKEN, TIDEN_PRODUCT_ID, TIDEN_BASE_URL).'
108
+ : '';
109
+ this.logger.log(`reporter disabled — nothing will be reported to Tiden, because ${reason}. ` +
110
+ `To enable it, ${remedy}.${credentials}`);
111
+ }
90
112
  buildReporters(factory, resolved) {
91
113
  let upstream;
92
114
  let fallbackReporter;
@@ -98,11 +120,15 @@ class TidenReporter {
98
120
  catch (error) {
99
121
  if (error instanceof disabled_exception_1.DisabledException) {
100
122
  disabled = true;
123
+ this.logReporterDisabled(`mode is "${resolved.effectiveMode}"`, 'set mode to "tiden" (or TIDEN_MODE=tiden) to report this run',
124
+ // Enabling it is the next thing they will do, and it needs all four.
125
+ true);
101
126
  }
102
127
  else {
103
128
  this.logger.logError('Unable to create upstream reporter:', error);
104
129
  if (resolved.composed.fallback === undefined) {
105
130
  disabled = true;
131
+ this.logReporterDisabled('the upstream reporter could not be created and no fallback is configured', 'fix the error above');
106
132
  return { upstream, fallback: fallbackReporter, disabled, useFallbackFromStart: false };
107
133
  }
108
134
  upstreamFailed = true;
@@ -113,13 +139,18 @@ class TidenReporter {
113
139
  }
114
140
  catch (error) {
115
141
  if (error instanceof disabled_exception_1.DisabledException) {
116
- if (upstreamFailed)
142
+ if (upstreamFailed) {
117
143
  disabled = true;
144
+ this.logReporterDisabled('the upstream reporter could not be created (see the error above) and ' +
145
+ `fallback is "${resolved.effectiveFallback}"`, 'fix the error above, or set a fallback mode', true);
146
+ }
118
147
  }
119
148
  else {
120
149
  this.logger.logError('Unable to create fallback reporter:', error);
121
- if (upstreamFailed && upstream === undefined)
150
+ if (upstreamFailed && upstream === undefined) {
122
151
  disabled = true;
152
+ this.logReporterDisabled('neither the upstream nor the fallback reporter could be created', 'fix the errors above');
153
+ }
123
154
  }
124
155
  }
125
156
  const useFallbackFromStart = upstreamFailed && fallbackReporter !== undefined;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tiden/reporter-commons",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Tiden JS Reporters",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",