Skip to content

Commit

Permalink
Add support for a TAP formatter (#5062)
Browse files Browse the repository at this point in the history
* Add support for a TAP formatter

* Update snapshots

* Remove unnecessary spread usage

Signed-off-by: John Cline <2057878+clinejj@users.noreply.github.com>
  • Loading branch information
clinejj committed Jan 16, 2021
1 parent 7f788e8 commit e9e5692
Show file tree
Hide file tree
Showing 8 changed files with 253 additions and 5 deletions.
1 change: 1 addition & 0 deletions docs/user-guide/usage/options.md
Expand Up @@ -48,6 +48,7 @@ Options are:
- `compact`
- `json` (default for Node API)
- `string` (default for CLI)
- `tap`
- `unix`
- `verbose`

Expand Down
2 changes: 1 addition & 1 deletion lib/__tests__/__snapshots__/cli.test.js.snap
Expand Up @@ -96,7 +96,7 @@ exports[`CLI --help 1`] = `
--formatter, -f [default: \\"string\\"]
The output formatter: \\"compact\\", \\"json\\", \\"string\\", \\"unix\\" or \\"verbose\\".
The output formatter: \\"compact\\", \\"json\\", \\"string\\", \\"tap\\", \\"unix\\" or \\"verbose\\".
--custom-formatter
Expand Down
2 changes: 1 addition & 1 deletion lib/__tests__/standalone-formatter.test.js
Expand Up @@ -45,7 +45,7 @@ it('standalone with invalid formatter option', () => {
}).catch((err) => {
expect(err).toEqual(
new Error(
'You must use a valid formatter option: "compact", "json", "string", "unix", "verbose" or a function',
'You must use a valid formatter option: "compact", "json", "string", "tap", "unix", "verbose" or a function',
),
);
});
Expand Down
200 changes: 200 additions & 0 deletions lib/formatters/__tests__/tapFormatter.test.js
@@ -0,0 +1,200 @@
'use strict';

const prepareFormatterOutput = require('./prepareFormatterOutput');
const tapFormatter = require('../tapFormatter');

describe('tapFormatter', () => {
let actualTTY;
let actualColumns;

beforeAll(() => {
actualTTY = process.stdout.isTTY;
actualColumns = process.stdout.columns;
});

afterAll(() => {
process.stdout.isTTY = actualTTY;
process.stdout.columns = actualColumns;
});

it('outputs no warnings', () => {
const results = [
{
source: 'path/to/file.css',
errored: false,
warnings: [],
deprecations: [],
invalidOptionWarnings: [],
},
];

const output = tapFormatter(results);

expect(output).toBe(
`
TAP version 13
1..1
ok 1 - path/to/file.css
`.trimStart(),
);
});

it('outputs warnings', () => {
const results = [
{
source: 'path/to/file.css',
errored: true,
warnings: [
{
line: 1,
column: 1,
rule: 'bar',
severity: 'error',
text: 'Unexpected foo',
},
{
line: 10,
column: 1,
rule: 'bar2',
severity: 'error',
text: 'Unexpected foo 2',
},
],
deprecations: [],
invalidOptionWarnings: [],
},
];

const output = prepareFormatterOutput(results, tapFormatter);

expect(output).toBe(
`
TAP version 13
1..1
not ok 1 - path/to/file.css
---
messages:
- message: "Unexpected foo"
severity: error
data:
line: 1
column: 1
ruleId: bar
- message: "Unexpected foo 2"
severity: error
data:
line: 10
column: 1
ruleId: bar2
---
`.trim(),
);
});

it('outputs warnings without stdout `TTY`', () => {
process.stdout.isTTY = false;

const results = [
{
source: 'path/to/file.css',
errored: true,
warnings: [
{
line: 1,
column: 1,
rule: 'bar',
severity: 'error',
text: 'Unexpected foo',
},
],
deprecations: [],
invalidOptionWarnings: [],
},
];

const output = prepareFormatterOutput(results, tapFormatter);

expect(output).toBe(
`
TAP version 13
1..1
not ok 1 - path/to/file.css
---
messages:
- message: "Unexpected foo"
severity: error
data:
line: 1
column: 1
ruleId: bar
---
`.trim(),
);
});

it('output warnings with more than 80 characters and `process.stdout.columns` equal 90 characters', () => {
// For Windows tests
process.stdout.isTTY = true;
process.stdout.columns = 90;

const results = [
{
source: 'path/to/file.css',
errored: true,
warnings: [
{
line: 1,
column: 1,
rule: 'bar-very-very-very-very-very-long',
severity: 'error',
text:
'Unexpected very very very very very very very very very very very very very long foo',
},
],
deprecations: [],
invalidOptionWarnings: [],
},
];

const output = prepareFormatterOutput(results, tapFormatter);

expect(output).toBe(
`
TAP version 13
1..1
not ok 1 - path/to/file.css
---
messages:
- message: "Unexpected very very very very very very very very very very very very very long foo"
severity: error
data:
line: 1
column: 1
ruleId: bar-very-very-very-very-very-long
---
`.trim(),
);
});

it('handles ignored file', () => {
const results = [
{
source: 'file.css',
warnings: [],
deprecations: [],
invalidOptionWarnings: [],
ignored: true,
},
];

const output = prepareFormatterOutput(results, tapFormatter);

expect(output).toBe(
`
TAP version 13
1..${results.length}
ok 1 - ignored ${results[0].source}
`.trim(),
);
});
});
1 change: 1 addition & 0 deletions lib/formatters/index.js
Expand Up @@ -6,6 +6,7 @@ module.exports = {
compact: importLazy(() => require('./compactFormatter'))(),
json: importLazy(() => require('./jsonFormatter'))(),
string: importLazy(() => require('./stringFormatter'))(),
tap: importLazy(() => require('./tapFormatter'))(),
unix: importLazy(() => require('./unixFormatter'))(),
verbose: importLazy(() => require('./verboseFormatter'))(),
};
39 changes: 39 additions & 0 deletions lib/formatters/tapFormatter.js
@@ -0,0 +1,39 @@
'use strict';

/**
* @type {import('stylelint').Formatter}
*/
const tapFormatter = (results) => {
let lines = [`TAP version 13\n1..${results.length}`];

results.forEach((result, index) => {
lines.push(
`${result.errored ? 'not ok' : 'ok'} ${index + 1} - ${result.ignored ? 'ignored ' : ''}${
result.source
}`,
);

if (result.warnings.length > 0) {
lines.push('---', 'messages:');

result.warnings.forEach((warning) => {
lines.push(
` - message: "${warning.text}"`,
` severity: ${warning.severity}`,
` data:`,
` line: ${warning.line}`,
` column: ${warning.column}`,
` ruleId: ${warning.rule}`,
);
});

lines.push('---');
}
});

lines.push('');

return lines.join('\n');
};

module.exports = tapFormatter;
4 changes: 2 additions & 2 deletions lib/utils/__tests__/getFormatterOptionsText.test.js
Expand Up @@ -3,8 +3,8 @@
const getFormatterOptionsText = require('../getFormatterOptionsText');

it('getFormatterOptionsText', () => {
expect(getFormatterOptionsText()).toBe('"compact", "json", "string", "unix", "verbose"');
expect(getFormatterOptionsText()).toBe('"compact", "json", "string", "tap", "unix", "verbose"');
expect(getFormatterOptionsText({ useOr: true })).toBe(
'"compact", "json", "string", "unix" or "verbose"',
'"compact", "json", "string", "tap", "unix" or "verbose"',
);
});
9 changes: 8 additions & 1 deletion types/stylelint/index.d.ts
Expand Up @@ -91,7 +91,14 @@ declare module 'stylelint' {
returnValue?: StylelintStandaloneReturnValue,
) => string;

export type FormatterIdentifier = 'compact' | 'json' | 'string' | 'unix' | 'verbose' | Formatter;
export type FormatterIdentifier =
| 'compact'
| 'json'
| 'string'
| 'tap'
| 'unix'
| 'verbose'
| Formatter;

export type CustomSyntax = string | Syntax;

Expand Down

0 comments on commit e9e5692

Please sign in to comment.