master f4db2900475c cached
16 files
18.7 KB
5.1k tokens
12 symbols
1 requests
Download .txt
Repository: jevakallio/jest-clean-console-reporter
Branch: master
Commit: f4db2900475c
Files: 16
Total size: 18.7 KB

Directory structure:
gitextract_4w8gncq1/

├── .gitignore
├── .npmignore
├── LICENSE
├── README.md
├── example/
│   ├── jest.config.js
│   ├── package.json
│   └── src/
│       ├── print.js
│       ├── print.test.js
│       ├── square.js
│       └── square.test.js
├── lib/
│   ├── CleanConsoleReporter.js
│   ├── getLogGroupHeader.js
│   ├── getLogGroupKey.js
│   ├── getLogGroupSummary.js
│   └── index.js
└── package.json

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitignore
================================================
node_modules
projects
yarn-error.log
build
coverage
.DS_Store
/package-lock.json


================================================
FILE: .npmignore
================================================
docs/
example/

================================================
FILE: LICENSE
================================================
The MIT Licence (MIT)

Copyright 2021 Jani Eväkallio <jani.evakallio@gmail.com>

Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicence, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

================================================
FILE: README.md
================================================
# jest-clean-console-reporter (alpha)

_A custom Jest reporter to reduce `console` spam in your test output._

---

Remember back in the day when [Jest](https://jestjs.io/) used to swallow `console.log` messages by default, and it was basically impossible to figure out why your code was failing under test? \***Sigh\***. Those were the days.

Now that Jest prints all console messages, whether from our own code, third-party framework code, or the test runner itself, we have the opposite problem: **It's easy to lose actually important warnings among noise.**

The `jest-clean-console-reporter` reporter collects all console messages written by the test process, and allows you to group them by known error warning types, or ignore them outright.

![Demo](docs/demo.png)

## Stating the obvious

**The best way to remove warnings in your code is to address their root cause.**

This reporter is probably best used when filtering out spammy library warnings that are otherwise tricky or impossible to get rid of, or ignoring intentional user-facing warnings of your own project.

## Usage

Install with your favorite package manager:

```sh
npm install --save-dev jest-clean-console-reporter
```

You'll also need Jest 25.1 or later installed in your project.

### Configuration

```js
// List known warnings you want to group or suppress. See docs below.
// Tip: This is just an array, you can import it from an external file
const rules = [
  {
    match: /^You are using the simple \(heuristic\) fragment matcher/,
    group: "Apollo: You are using the simple (heuristic) fragment matcher.",
  },
  {
    match: /^Heuristic fragment matching going on/,
    group: null, // ignore outright
  },
  {
    match: /^Warning: An update to (\w*) inside a test was not wrapped in act/,
    group: "React: Act warnings",
    keep: true, // include in summary, but also keep raw console output
  },
];

// Add reporters to your jest config
module.exports = {
  // ...

  reporters: [
    // Add jest-clean-console-reporter. This takes place of the
    // default reporter, and behaves identically otherwise
    ["jest-clean-console-reporter", { rules: rules }],

    // Overriding config.reporters wipes out default reporters, so
    // we need to restore the summary reporter.
    //
    // NOTE: For jest 26.6.1 or older, this file is located at
    // @jest/reporters/build/summary_reporter
    "@jest/reporters/build/SummaryReporter",
  ],
};
```

## Options

Pass options to the reporter in your jest configuration as follows:

### Using Jest 26.6.2 or newer

```js
const jestConfig = {
  reporters: [
    ["jest-clean-console-reporter", options], // <--
    "@jest/reporters/build/SummaryReporter",
  ],
};
```

### Using Jest 25.1.0-26.6.1

```js
const jestConfig = {
  reporters: [
    ["jest-clean-console-reporter", options], // <--
    "@jest/reporters/build/summary_reporter",
  ],
};
```

### `options.rules`

Rules tell the reporter which console messages should be filtered, and how
they should be grouped in the summary.

Each rule has three options, `match`, `group` and (optionally) `keep`.

#### `rule.match : RegExp | string | (message, level, origin) => boolean`

`match` is either a regular expression, a string, or a predicate function:

- `RegExp`: Matches console message against this regular expression.
- `string`: Matches console message against this string, first using literal `===` comparison, and falls back to regular expression comparison using `message.match(match)`. The latter is not recommended, but useful if you need to serialize your regular expressions.
- A predicate function that's called with `match(message, level)` where
  - `message` is the full console message
  - `level` is the log level (error, warning, log etc..).
  - `origin` is the stack trace string for this error. Useful if you want to ignore all errors from a certain library, for example. Note that this string can contain newlines, so any regexes used to match it should use the `/g` flag.
  - To match this message, the predicate may return any truthy value.

Rules are matched in order, from top down. A message that is not matched by any rule will be displayed in the Jest test output as normal.

Matched messages are grouped according to the `group` property:

#### `rule.group : string | null | (message, level, matcher) => string | null`

`group` is either a string, a formatter function, or null:

- `string`: Matched messages are grouped by this literal string
- `null`: Matched message is ignored.
- Formatter function that's called with `group(message, level, matcher)` where
  - `message` is the full console message.
  - `level` is the log level (error, warning, log etc..).
  - `matcher` is the original matcher used to match this message. This can be useful if you want to e.g. execute the regular expression for capture groups.
  - The value returned by this function is used as capture key. If the function returns `null`, the message is ignored.

#### `rule.keep: boolean`

Setting `keep: true` option allows you to keep the original console output for this group intact, while also displaying it in the test run summary.

### options.levels

Define which log levels to display in the summary at the end of the test run:

Default: `["error", "warn", "info", "debug", "log"]`

These levels only affect the summary display, and have no effect on whether messages are matched. For that, see [Can I ignore all messages of certain log level?](#can-i-ignore-all-messages-of-certain-log-level).

## Never Asked Questions

Here are some questions nobody has ever asked, but might be helpful anyway.

### Can I ignore all messages of certain log level?

Yes. Use the second parameter passed to a function macher:

```js
{ match: (message, level) => level === "log", group: null }
```

### Can I ignore all messages, period?

Yes, but it's probably a bad idea:

```js
{ matcher: () => true, group: null }
```

### Can I group multiple error messages into the same bucket?

Yes, just give them the same group key:

```js
[
  {
    match: /^Warning: componentWillMount has been renamed/,
    group: "React componentWill* deprecation warnings",
  },
  {
    match: /^Warning: componentWillReceiveProps has been renamed/,
    group: "React componentWill* deprecation warnings",
  },
];
```

### Can I temporarily let a certain message through?

Yes, just set the rule's `keep` property to `true`:

```js
{
  match: /^Warning: An update to (\w*) inside a test was not wrapped in act/,
  group: "An update to (Component) inside a test was not wrapped in act",
  keep: true
}
```

### Can I use this with .json config?

You can use string matchers, which are first compared to the message as literal strings, and failing that, attempted to test against the message as regular expressions:

```json
{
  "match": "Warning: An update to \\w* inside a test was not wrapped in act",
  "group": "An update to (Component) inside a test was not wrapped in act"
}
```

### Can I parameterize log groups using regex capture groups?

You can use the grouping function, where the original matcher is provided as a third argument.

```js
{
    match: /^Warning: An update to (\w*) inside a test was not wrapped in act/,
    group: (message, _level, matcher) => {
      // Note: String.matchAll requires Node 12.x or higher
      const [match] = message.matchAll(matcher);
      return `React: An update to ${match[1]} was not wrapped in act.`;
    }
}
```

### Can I ignore random `console.error`s from a specific library?

Yes, `console.error` comes with an `origin` property that contains the full stack trace
at the time of logging, which you should be able to use to filter per library, or even per file and line!

The origin may not be available for other log types, so check it before you use it.

```js
  {
    match: (_message, _type, origin) =>
      origin && /node_modules\/rc-form\/lib\/createBaseForm/g.test(origin),
    group: 'rc-form validation warnings'
  },
```

### Can I help make this library better?

Yes, see [Contibuting](#contributing).

## Contributing

This software is very much at alpha stage. If you want to help, here are a few things that could be helpful:

- [ ] Write tests
- [ ] Convert project to TypeScript
- [ ] NPM scripts for workflows
- [ ] Don't override DefaultReporter
  - [ ] When I started hacking on this, I couldn't immediately see a way to suppress `result.console` from being printed by the DefaultReporter, so I overrode it. This is annoying, because it can't be used with other custom reporters now. But it occurs to me, that maybe if a custom reporter is installed in the pipeline **before** the default reporter, perhaps we could filter out console messages there. Tradeoff here would be that we would nuke the console for ALL other reporters, whereas now we only do it for DefaultReporter.
- [ ] Feature ideas
  - [ ] Allow to fail the test suite after a certain threshold is passed, e.g. `rule.failAfter`.
  - [ ] Provide better summary that shows errors by file/test (behind an option flag)
  - [ ] Show deltas (+/- change) since last run (would require caching)
- [ ] Known issues
  - [ ] When running Jest with a single file parameter (e.g. `jest src/file.js`), the reporter is not activated

## License

[MIT](LICENSE)


================================================
FILE: example/jest.config.js
================================================
const rules = [
  {
    match: /^Received invalid input:/,
    group: "Received invalid input",
    keep: true,
  },
  {
    match: /^Received a non-integer number:/,
    group: "Received a non-integer number",
  },
  {
    match: /^Received value:/,
    group: null,
  },
];

module.exports = {
  reporters: [
    ["jest-clean-console-reporter", { rules }],
    "@jest/reporters/build/SummaryReporter",
  ],
  testMatch: ["<rootDir>/src/**/*.test.js"],
};


================================================
FILE: example/package.json
================================================
{
  "name": "jest-clean-console-reporter-test-app",
  "version": "1.0.0",
  "main": "index.js",
  "author": "Jani Eväkallio",
  "license": "MIT",
  "scripts": {
    "test": "jest --config jest.config.js"
  },
  "devDependencies": {
    "jest": "^26.6.3",
    "jest-clean-console-reporter": "latest"
  }
}


================================================
FILE: example/src/print.js
================================================
"use strict";

module.exports = function print(n) {
  console.info("Received value: " + n);

  // throws when "n" is not a number
  return n.toFixed(2);
};


================================================
FILE: example/src/print.test.js
================================================
const print = require("./print");

describe("print", () => {
  it("prints a number", () => {
    expect(print(25 / 2)).toBe("12.50");
  });

  it("crashes when passed a non-number", () => {
    // this test will fail
    expect(() => print("100")).toThrow();
  });
});


================================================
FILE: example/src/square.js
================================================
module.exports = function square(n) {
  // info
  console.info("Received value: " + n);

  if (typeof n !== "number") {
    // this error is preserved with rule.keep
    console.error("Received invalid input: " + n);
    return NaN;
  }

  if (!Number.isInteger(n)) {
    // warning
    console.warn("Received a non-integer number: " + n);
  }

  return n * n;
};


================================================
FILE: example/src/square.test.js
================================================
const square = require("./square");

describe("square", () => {
  it("squares an integer", () => {
    expect(square(3)).toBe(9);
  });

  it("squares a non-integer number", () => {
    expect(square(2.5)).toBe(6.25);
  });

  it("fails to square a non-number", () => {
    expect(square("bob")).toBe(NaN);
  });
});


================================================
FILE: lib/CleanConsoleReporter.js
================================================
/* global require module */

const { DefaultReporter } = require("@jest/reporters");
const getLogGroupKey = require("./getLogGroupKey");
const getLogGroupSummary = require("./getLogGroupSummary");

/**
 * Overrides Jest's default reporter to filter out known console messages,
 * and prints a summary at the end of the test run.
 */
class CleanConsoleReporter extends DefaultReporter {
  constructor(globalConfig, options = {}) {
    super(globalConfig);
    this.rules = options.rules || [];
    this.levels = options.levels || ["error", "warn", "info", "debug", "log"];
    this.logs = new Map();
    this.ignored = 0;
  }

  // Override DefaultReporter method
  printTestFileHeader(testPath, config, result) {
    // Strip out known console messages before passing to base implementation
    const filteredResult = {
      ...result,
      console: this.filterOutKnownMessages(result.console),
    };

    DefaultReporter.prototype.printTestFileHeader.call(
      this,
      testPath,
      config,
      filteredResult
    );
  }

  filterOutKnownMessages(consoleBuffer = []) {
    const rules = this.rules;
    const retain = [];

    for (const frame of consoleBuffer) {
      // Check if this a known type message
      const [key, keep] = getLogGroupKey(rules, frame);
      if (key) {
        this.groupMessageByKey(frame.type, key);
        if (keep) {
          retain.push(frame);
        }
      } else if (key === null) {
        this.ignored++;
      } else {
        retain.push(frame);
      }
    }

    // Based implementation expects undefined instead of empty array
    return retain.length ? retain : undefined;
  }

  groupMessageByKey(type, key) {
    // this.logs : Map<string, Map<string, number>>
    let level = this.logs.get(type);
    if (!level) {
      this.logs.set(type, (level = new Map()));
    }

    level.set(key, (level.get(key) || 0) + 1);
  }

  onRunStart(...args) {
    DefaultReporter.prototype.onRunStart.call(this, ...args);
  }

  onRunComplete(...args) {
    const summary = getLogGroupSummary(this.logs, this.levels, this.ignored);
    if (summary) {
      summary.forEach(this.log);
    }

    DefaultReporter.prototype.onRunComplete.call(this, ...args);
  }
}

module.exports = CleanConsoleReporter;


================================================
FILE: lib/getLogGroupHeader.js
================================================
/* global require, module */

const chalk = require("chalk");

// Explicitly reset for these messages since they can get written out in the
// middle of error logging
const ERROR_TEXT = "ERROR";
const WARN_TEXT = "WARN";
const LOG_TEXT = "LOG";
const INFO_TEXT = "INFO";
const DEBUG_TEXT = "DEBUG";

const statusByType = {
  error: chalk.supportsColor
    ? chalk.reset.bold.red(` ${ERROR_TEXT}`.padEnd(7))
    : ERROR_TEXT,
  warn: chalk.supportsColor
    ? chalk.reset.bold.yellow(` ${WARN_TEXT}`.padEnd(7))
    : WARN_TEXT,
  log: chalk.supportsColor
    ? chalk.reset.bold.cyan(` ${LOG_TEXT}`.padEnd(7))
    : LOG_TEXT,
  info: chalk.supportsColor
    ? chalk.reset.bold.blue(` ${INFO_TEXT}`.padEnd(7))
    : INFO_TEXT,
  debug: chalk.supportsColor
    ? chalk.reset.bold.magenta(` ${DEBUG_TEXT}`.padEnd(7))
    : DEBUG_TEXT,
};

const formatCount = (count) => {
  const chars = count.toString();
  const chalked = chalk.bold(chars);
  const formatChars = chalked.length - chars.length;
  return `${chalked}`.padEnd(5 + formatChars, " ");
};

const formatMessage = (key) => {
  const truncated = key.length > 100 ? `${key.substring(0, 100)}...` : key;
  const singleline = truncated.replace(/[\r\n]+/g, " ");
  const highlighted = singleline.replace("@TODO", chalk.bold("@TODO"));

  return highlighted;
};

const getLogGroupHeader = (type, key, count) => {
  const status = statusByType[type] || type.toUpperCase();
  return `${status} ${formatCount(count)} ${formatMessage(key)}`;
};

const getSkippedHeader = (count) => {
  const label = " SKIP".padEnd(7);
  const message = `${label} ${formatCount(count)} messages were filtered`;
  return chalk.supportsColor ? chalk.reset.gray(message) : message;
};

module.exports = { getLogGroupHeader, getSkippedHeader };


================================================
FILE: lib/getLogGroupKey.js
================================================
/* global module */
const matchWith = (matcher, message, type, origin) => {
  if (matcher instanceof RegExp) {
    return matcher.test(message);
  }
  if (typeof matcher === "string") {
    if (matcher === message) {
      return true;
    } else {
      return message.match(matcher);
    }
  }
  if (typeof matcher === "function") {
    return matcher(message, type, origin);
  }

  throw new Error("Filter must be a string, function or a regular expression");
};

const formatMessage = (formatter, message, type, matcher) => {
  if (typeof formatter === "undefined") {
    return null;
  }

  if (typeof formatter === "function") {
    return formatter(message, type, matcher);
  }

  if (typeof formatter === "string") {
    return formatter;
  }

  if (formatter === null) {
    return null;
  }

  return message;
};

const getLogGroupKey = (rules, { message, type, origin }) => {
  for (let { match: matcher, group: formatter, keep = false } of rules) {
    if (matchWith(matcher, message, type, origin)) {
      return [formatMessage(formatter, message, type, matcher), keep];
    }
  }

  return [];
};

module.exports = getLogGroupKey;


================================================
FILE: lib/getLogGroupSummary.js
================================================
/* global require module */

const chalk = require("chalk");
const { getLogGroupHeader, getSkippedHeader } = require("./getLogGroupHeader");

const orderBy = ([aKey, aCount], [bKey, bCount]) => {
  // count descending
  if (aCount > bCount) return -1;
  if (bCount > aCount) return 1;

  // key ascending
  if (aKey < bKey) return -1;
  if (bKey < aKey) return 1;

  // should never happen since keys are unique
  return 0;
};

const getLogGroupSummary = (logs, levels, ignored) => {
  if (logs.size === 0 && !ignored) {
    return null;
  }

  const lines = [`\n ${chalk.bold("\u25cf ")} Suppressed console messages:\n`];
  levels.forEach((type) => {
    const level = logs.get(type);
    if (level) {
      const entries = [...level.entries()].sort(orderBy);
      for (let [key, count] of entries) {
        lines.push(getLogGroupHeader(type, key, count));
      }
    }
  });

  if (ignored) {
    lines.push(getSkippedHeader(ignored));
  }

  return lines;
};

module.exports = getLogGroupSummary;


================================================
FILE: lib/index.js
================================================
/* global require module */
module.exports = require("./CleanConsoleReporter");


================================================
FILE: package.json
================================================
{
  "name": "jest-clean-console-reporter",
  "version": "0.3.0",
  "description": "Jest reporter for grouping and filtering intrusive console warnings",
  "main": "lib/index.js",
  "repository": "https://github.com/jevakallio/jest-clean-console-reporter",
  "author": "Jani Eväkallio",
  "license": "MIT",
  "scripts": {
    "prepare": "npm-install-peers"
  },
  "dependencies": {
    "chalk": ">=3.0.0"
  },
  "peerDependencies": {
    "jest": ">=25.1.0"
  },
  "devDependencies": {
    "npm-install-peers": "^1.2.1"
  }
}
Download .txt
gitextract_4w8gncq1/

├── .gitignore
├── .npmignore
├── LICENSE
├── README.md
├── example/
│   ├── jest.config.js
│   ├── package.json
│   └── src/
│       ├── print.js
│       ├── print.test.js
│       ├── square.js
│       └── square.test.js
├── lib/
│   ├── CleanConsoleReporter.js
│   ├── getLogGroupHeader.js
│   ├── getLogGroupKey.js
│   ├── getLogGroupSummary.js
│   └── index.js
└── package.json
Download .txt
SYMBOL INDEX (12 symbols across 2 files)

FILE: lib/CleanConsoleReporter.js
  class CleanConsoleReporter (line 11) | class CleanConsoleReporter extends DefaultReporter {
    method constructor (line 12) | constructor(globalConfig, options = {}) {
    method printTestFileHeader (line 21) | printTestFileHeader(testPath, config, result) {
    method filterOutKnownMessages (line 36) | filterOutKnownMessages(consoleBuffer = []) {
    method groupMessageByKey (line 59) | groupMessageByKey(type, key) {
    method onRunStart (line 69) | onRunStart(...args) {
    method onRunComplete (line 73) | onRunComplete(...args) {

FILE: lib/getLogGroupHeader.js
  constant ERROR_TEXT (line 7) | const ERROR_TEXT = "ERROR";
  constant WARN_TEXT (line 8) | const WARN_TEXT = "WARN";
  constant LOG_TEXT (line 9) | const LOG_TEXT = "LOG";
  constant INFO_TEXT (line 10) | const INFO_TEXT = "INFO";
  constant DEBUG_TEXT (line 11) | const DEBUG_TEXT = "DEBUG";
Condensed preview — 16 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (21K chars).
[
  {
    "path": ".gitignore",
    "chars": 81,
    "preview": "node_modules\nprojects\nyarn-error.log\nbuild\ncoverage\n.DS_Store\n/package-lock.json\n"
  },
  {
    "path": ".npmignore",
    "chars": 14,
    "preview": "docs/\nexample/"
  },
  {
    "path": "LICENSE",
    "chars": 1103,
    "preview": "The MIT Licence (MIT)\n\nCopyright 2021 Jani Eväkallio <jani.evakallio@gmail.com>\n\nPermission is hereby granted, free of c"
  },
  {
    "path": "README.md",
    "chars": 9272,
    "preview": "# jest-clean-console-reporter (alpha)\n\n_A custom Jest reporter to reduce `console` spam in your test output._\n\n---\n\nReme"
  },
  {
    "path": "example/jest.config.js",
    "chars": 457,
    "preview": "const rules = [\n  {\n    match: /^Received invalid input:/,\n    group: \"Received invalid input\",\n    keep: true,\n  },\n  {"
  },
  {
    "path": "example/package.json",
    "chars": 305,
    "preview": "{\n  \"name\": \"jest-clean-console-reporter-test-app\",\n  \"version\": \"1.0.0\",\n  \"main\": \"index.js\",\n  \"author\": \"Jani Eväkal"
  },
  {
    "path": "example/src/print.js",
    "chars": 156,
    "preview": "\"use strict\";\n\nmodule.exports = function print(n) {\n  console.info(\"Received value: \" + n);\n\n  // throws when \"n\" is not"
  },
  {
    "path": "example/src/print.test.js",
    "chars": 269,
    "preview": "const print = require(\"./print\");\n\ndescribe(\"print\", () => {\n  it(\"prints a number\", () => {\n    expect(print(25 / 2)).t"
  },
  {
    "path": "example/src/square.js",
    "chars": 364,
    "preview": "module.exports = function square(n) {\n  // info\n  console.info(\"Received value: \" + n);\n\n  if (typeof n !== \"number\") {\n"
  },
  {
    "path": "example/src/square.test.js",
    "chars": 317,
    "preview": "const square = require(\"./square\");\n\ndescribe(\"square\", () => {\n  it(\"squares an integer\", () => {\n    expect(square(3))"
  },
  {
    "path": "lib/CleanConsoleReporter.js",
    "chars": 2253,
    "preview": "/* global require module */\n\nconst { DefaultReporter } = require(\"@jest/reporters\");\nconst getLogGroupKey = require(\"./g"
  },
  {
    "path": "lib/getLogGroupHeader.js",
    "chars": 1769,
    "preview": "/* global require, module */\n\nconst chalk = require(\"chalk\");\n\n// Explicitly reset for these messages since they can get"
  },
  {
    "path": "lib/getLogGroupKey.js",
    "chars": 1146,
    "preview": "/* global module */\nconst matchWith = (matcher, message, type, origin) => {\n  if (matcher instanceof RegExp) {\n    retur"
  },
  {
    "path": "lib/getLogGroupSummary.js",
    "chars": 1003,
    "preview": "/* global require module */\n\nconst chalk = require(\"chalk\");\nconst { getLogGroupHeader, getSkippedHeader } = require(\"./"
  },
  {
    "path": "lib/index.js",
    "chars": 80,
    "preview": "/* global require module */\nmodule.exports = require(\"./CleanConsoleReporter\");\n"
  },
  {
    "path": "package.json",
    "chars": 524,
    "preview": "{\n  \"name\": \"jest-clean-console-reporter\",\n  \"version\": \"0.3.0\",\n  \"description\": \"Jest reporter for grouping and filter"
  }
]

About this extraction

This page contains the full source code of the jevakallio/jest-clean-console-reporter GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 16 files (18.7 KB), approximately 5.1k tokens, and a symbol index with 12 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!