Skip to content

Commit

Permalink
Prettify stylish output with chalk
Browse files Browse the repository at this point in the history
  • Loading branch information
Alesia Vysotskaya committed Aug 24, 2021
1 parent eee9ac8 commit 03e8ae4
Show file tree
Hide file tree
Showing 28 changed files with 73 additions and 44 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@
"mock-fs": "4.11.0",
"nyc": "15.1.0",
"sinon": "9.0.1",
"strip-ansi": "6.0.0",
"terminal-link": "2.1.1",
"typescript": "4.1.5"
},
Expand Down
5 changes: 4 additions & 1 deletion src/__tests__/rules/rule-test-base.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { assert } from "chai";
import { readAndParseFile } from "../../linter";
import stripAnsi from "strip-ansi";

const _ = require("lodash");

Expand All @@ -14,7 +15,9 @@ export function createRuleTest(rule, messageTemplate) {
});
return readAndParseFile(`src/__tests__/rules/${featureFile}`/*, "utf8"*/)
.then(({ feature, file }) => {
assert.sameDeepMembers(rule.run(feature, file, configuration), expectedErrors);
const actual = rule.run(feature, file, configuration);
actual?.forEach(error => error.message = stripAnsi(error.message));
assert.sameDeepMembers(actual, expectedErrors);
});
};
}
Expand Down
2 changes: 2 additions & 0 deletions src/__tests__/rulesdir/rulesdir.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import * as path from "path";
import { expect } from "chai";
import * as linter from "../../linter";
import * as configParser from "../../config/config-parser";
import stripAnsi from "strip-ansi";

describe("rulesdir CLI option", function () {
it("loads additional rules from specified directories", function () {
Expand All @@ -13,6 +14,7 @@ describe("rulesdir CLI option", function () {
const featureFile = path.join(__dirname, "simple.features");
return linter.lint([featureFile], config, additionalRulesDirs)
.then((results) => {
results?.forEach(result=> result.errors?.forEach(error => error.message = stripAnsi(error.message)));
expect(results).to.deep.equal([
{
errors: [
Expand Down
2 changes: 2 additions & 0 deletions src/formatters/json.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { Result } from "../types";
import stripAnsi from "strip-ansi";

export function printResults(results: Result[]) {
results.forEach(result=> result.errors?.forEach(error => error.message = stripAnsi(error.message)));
process.stdout.write(JSON.stringify(results));
}
7 changes: 4 additions & 3 deletions src/rules/allowed-tags.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Examples, Feature, ResultError, Scenario, Tag } from "../types";
import chalk from "chalk";

const _ = require("lodash");

Expand Down Expand Up @@ -46,10 +47,10 @@ function isAllowed(tag, allowedTags, allowedPatterns) {
|| allowedPatterns.some((pattern) => pattern.test(tag.name));
}

function createError(node: Feature | Scenario | Examples, tag: Tag) {
function createError(node: Feature | Scenario | Examples, tag: Tag): ResultError {
return {
message: `Not allowed tag ${tag.name} on ${node.keyword}`,
message: `Not allowed tag ${chalk.yellow(tag.name)} on ${chalk.cyan(node.keyword)}`,
rule: name,
line: tag.location?.line,
line: tag.location?.line || 0,
};
}
3 changes: 2 additions & 1 deletion src/rules/file-name.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Feature, ResultError } from "../types";
import chalk from "chalk";

const path = require("path");
const _ = require("lodash");
Expand Down Expand Up @@ -29,7 +30,7 @@ export function run(feature: Feature, file, configuration): ResultError[] {
return [];
}
return [{
message: `File names should be written in ${style} e.g. "${expected}.feature"`,
message: `File names should be written in ${style} e.g. "${chalk.cyan(`${expected}.feature`)}"`,
rule: name,
line: 0,
}];
Expand Down
3 changes: 2 additions & 1 deletion src/rules/indentation.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import * as gherkinUtils from "./utils/gherkin";
import { Feature, ResultError, Step, Tag } from "../types";
import chalk from "chalk";

const _ = require("lodash");

Expand Down Expand Up @@ -47,7 +48,7 @@ export function run(feature: Feature, unused, configuration): ResultError[] {
// indentation we need to subtract 1
if (parsedLocation.column - 1 !== mergedConfiguration[type]) {
errors.push({
message: `Wrong indentation for "${type
message: `Wrong indentation for "${chalk.cyan(type)
}", expected indentation level of ${mergedConfiguration[type]
}, but got ${parsedLocation.column - 1}`,
rule: name,
Expand Down
6 changes: 4 additions & 2 deletions src/rules/keywords-in-logical-order.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Feature, FeatureChild, ResultError, RuleChild, Step } from "../types";
import * as gherkinUtils from "./utils/gherkin";
import chalk from "chalk";

export const name = "keywords-in-logical-order";

Expand Down Expand Up @@ -40,8 +41,9 @@ function checkNode(child: FeatureChild | RuleChild, feature: Feature, errors: Re

function createError(step: Step, maxKeyword: string) {
return {
message: `Step "${step.keyword}${step.text}" should not appear after step using keyword ${maxKeyword}`,
message: `Step "${chalk.yellow(`${step.keyword}${step.text}`)
}" should not appear after step using keyword ${chalk.cyan(maxKeyword)}`,
rule: name,
line: step.location?.line || -1,
line: step.location?.line || 0,
};
}
4 changes: 3 additions & 1 deletion src/rules/name-length.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Background, Feature, FeatureChild, ResultError, RuleChild, Scenario } from "../types";
import chalk from "chalk";

const _ = require("lodash");

Expand Down Expand Up @@ -45,7 +46,8 @@ function checkNode(child: FeatureChild | RuleChild,
function test(nameString, location, configuration, type, errors) {
if (nameString && (nameString.length > configuration[type])) {
errors.push({
message: `${type} name is too long. Length of ${nameString.length} is longer than the maximum allowed: ${configuration[type]}`,
message: `${chalk.cyan(type)} name is too long. Length of ${nameString.length
} is longer than the maximum allowed: ${configuration[type]}`,
rule: name,
line: location.line,
});
Expand Down
2 changes: 1 addition & 1 deletion src/rules/no-background-only-scenario.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,6 @@ function createError(background: Background): ResultError {
return {
message: "Backgrounds are not allowed when there is just one scenario.",
rule: name,
line: background.location?.line || -1,
line: background.location?.line || 0,
};
}
5 changes: 3 additions & 2 deletions src/rules/no-dupe-feature-names.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Feature, File, ResultError } from "../types";
import chalk from "chalk";

export const name = "no-dupe-feature-names";
const features = [];
Expand All @@ -13,9 +14,9 @@ export function run(feature: Feature, file: File): ResultError[] {
const dupes = features[feature.name].files.join(", ");
features[feature.name].files.push(file.relativePath);
errors.push({
message: `Feature name is already used in: ${dupes}`,
message: `Feature name is already used in: ${chalk.underline(dupes)}`,
rule: name,
line: feature.location?.line || -1,
line: feature.location?.line || 0,
});
} else {
features[feature.name] = { files: [file.relativePath] };
Expand Down
5 changes: 3 additions & 2 deletions src/rules/no-dupe-scenario-names.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Feature, File, ResultError, Scenario } from "../types";
import chalk from "chalk";

export const name = "no-dupe-scenario-names";
export const availableConfigs = [
Expand Down Expand Up @@ -36,9 +37,9 @@ function checkScenario(scenario: Scenario, file: File, errors: ResultError[]) {
line: scenario.location?.line,
});
errors.push({
message: `Scenario name is already used in: ${dupes}`,
message: `Scenario name is already used in: ${chalk.underline(dupes)}`,
rule: name,
line: scenario.location?.line || -1,
line: scenario.location?.line || 0,
});
} else {
scenarios[scenario.name] = {
Expand Down
5 changes: 3 additions & 2 deletions src/rules/no-duplicate-tags.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Examples, Feature, ResultError, Scenario } from "../types";
import chalk from "chalk";

const _ = require("lodash");

Expand Down Expand Up @@ -29,9 +30,9 @@ function verifyTags(node: Feature | Scenario | Examples, errors: ResultError[])
if (!_.includes(failedTagNames, tag.name)) {
if (_.includes(uniqueTagNames, tag.name)) {
errors.push({
message: `Duplicate tags are not allowed: ${tag.name}`,
message: `Duplicate tags are not allowed: ${chalk.yellow(tag.name)}`,
rule: name,
line: tag.location?.line || -1,
line: tag.location?.line || 0,
});
failedTagNames.push(tag.name);
} else {
Expand Down
2 changes: 1 addition & 1 deletion src/rules/no-empty-background.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,6 @@ function createError(background: Background): ResultError {
return {
message: "Empty backgrounds are not allowed.",
rule: name,
line: background.location?.line || -1,
line: background.location?.line || 0,
};
}
2 changes: 1 addition & 1 deletion src/rules/no-examples-in-scenarios.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export function run(feature: Feature): ResultError[] {
errors.push({
message: 'Cannot use "Examples" in a "Scenario", use a "Scenario Outline" instead',
rule: name,
line: child.scenario.location?.line || -1,
line: child.scenario.location?.line || 0,
});
}
}
Expand Down
9 changes: 5 additions & 4 deletions src/rules/no-homogenous-tags.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Examples, Feature, ResultError, Scenario } from "../types";
import chalk from "chalk";

const _ = require("lodash");

Expand All @@ -25,9 +26,9 @@ export function run(feature: Feature): ResultError[] {
errors.push({
message: `${"All Examples of a Scenario Outline have the same tag(s), " +
"they should be defined on the Scenario Outline instead: "}${
homogenousExampleTags.join(", ")}`,
chalk.yellow(homogenousExampleTags.join(", "))}`,
rule: name,
line: scenario.location?.line || -1,
line: scenario.location?.line || 0,
});
}
}
Expand All @@ -37,9 +38,9 @@ export function run(feature: Feature): ResultError[] {
errors.push({
message: `${"All Scenarios on this Feature have the same tag(s), " +
"they should be defined on the Feature instead: "}${
homogenousTags.join(", ")}`,
chalk.yellow(homogenousTags.join(", "))}`,
rule: name,
line: feature.location?.line || -1,
line: feature.location?.line || 0,
});
}
return errors;
Expand Down
2 changes: 1 addition & 1 deletion src/rules/no-partially-commented-tag-lines.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ function checkTags(node: Feature | Scenario | Examples, errors: ResultError[]) {
errors.push({
message: "Partially commented tag lines not allowed",
rule: name,
line: tag.location?.line || -1,
line: tag.location?.line || 0,
});
}
});
Expand Down
3 changes: 2 additions & 1 deletion src/rules/no-restricted-patterns.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Feature, FeatureChild, ResultError, RuleChild } from "../types";
import * as gherkinUtils from "./utils/gherkin";
import chalk from "chalk";

export const name = "no-restricted-patterns";
export const availableConfigs = {
Expand Down Expand Up @@ -106,7 +107,7 @@ function check(node, property, pattern, language, errors) {
// white space before and after, unlike steps
if (item.trim().match(pattern)) {
errors.push({
message: `${type} ${property}: "${item.trim()}" matches restricted pattern "${pattern}"`,
message: `${type} ${property}: "${chalk.cyan(item.trim())}" matches restricted pattern "${chalk.yellow(pattern)}"`,
rule: name,
line: node.location.line,
});
Expand Down
3 changes: 2 additions & 1 deletion src/rules/no-restricted-tags.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Feature, ResultError, Tag } from "../types";
import * as gherkinUtils from "./utils/gherkin";
import chalk from "chalk";

const _ = require("lodash");

Expand Down Expand Up @@ -39,7 +40,7 @@ function checkTags(node, language, forbiddenTags, forbiddenPatterns, errors) {
node.tags.forEach(tag => {
if (isForbidden(tag, forbiddenTags, forbiddenPatterns)) {
errors.push({
message: `Forbidden tag ${tag.name} on ${nodeType}`,
message: `Forbidden tag ${chalk.yellow(tag.name)} on ${chalk.cyan(nodeType)}`,
rule: name,
line: tag.location.line,
});
Expand Down
2 changes: 1 addition & 1 deletion src/rules/no-scenario-outlines-without-examples.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export function run(feature: Feature): ResultError[] {
errors.push({
message: "Scenario Outline does not have any Examples",
rule: name,
line: scenario.location?.line || -1,
line: scenario.location?.line || 0,
});
}
}
Expand Down
4 changes: 3 additions & 1 deletion src/rules/no-superfluous-tags.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Feature, ResultError } from "../types";
import * as gherkinUtils from "./utils/gherkin";
import chalk from "chalk";

const _ = require("lodash");

Expand Down Expand Up @@ -27,7 +28,8 @@ function checkTags(child, parent, language, errors: ResultError[]) {
const parentType = gherkinUtils.getNodeType(parent, language);
superfluousTags.forEach(tag => {
errors.push({
message: `Tag duplication between ${childType} and its corresponding ${parentType}: ${tag.name}`,
message: `Tag duplication between ${chalk.cyan(childType)
} and its corresponding ${chalk.cyan(parentType)}: ${chalk.yellow(tag.name)}`,
rule: name,
line: tag.location.line,
});
Expand Down
2 changes: 1 addition & 1 deletion src/rules/no-unnamed-scenarios.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ export function run(feature: Feature): ResultError[] {
errors.push({
message: "Missing Scenario name",
rule: name,
line: child.scenario.location?.line || -1,
line: child.scenario.location?.line || 0,
});
}
});
Expand Down
5 changes: 3 additions & 2 deletions src/rules/no-unused-variables.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Feature, ResultError } from "../types";
import chalk from "chalk";

export const name = "no-unused-variables";

Expand Down Expand Up @@ -67,7 +68,7 @@ export function run(feature: Feature): ResultError[] {
for (const exampleVariable in examplesVariables) {
if (!scenarioVariables[exampleVariable]) {
errors.push({
message: `Examples table variable "${exampleVariable}" is not used in any step`,
message: `Examples table variable "${chalk.yellow(exampleVariable)}" is not used in any step`,
rule: name,
line: examplesVariables[exampleVariable],
});
Expand All @@ -76,7 +77,7 @@ export function run(feature: Feature): ResultError[] {
for (const scenarioVariable in scenarioVariables) {
if (!examplesVariables[scenarioVariable]) {
errors.push({
message: `Step variable "${scenarioVariable}" does not exist in the examples table`,
message: `Step variable "${chalk.yellow(scenarioVariable)}" does not exist in the examples table`,
rule: name,
line: scenarioVariables[scenarioVariable],
});
Expand Down
4 changes: 3 additions & 1 deletion src/rules/one-space-between-tags.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Examples, Feature, ResultError, Scenario } from "../types";
import chalk from "chalk";

const _ = require("lodash");

Expand Down Expand Up @@ -32,7 +33,8 @@ function testTags(node: Feature | Scenario | Examples, errors: ResultError[]) {
errors.push({
line: tags[i].location.line,
rule: name,
message: `There is more than one space between the tags ${tags[i].name} and ${tags[i + 1].name}`,
message: `There is more than one space between the tags ${chalk.yellow(tags[i].name)
} and ${chalk.yellow(tags[i + 1].name)}`,
});
}
});
Expand Down
5 changes: 3 additions & 2 deletions src/rules/required-tags.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Feature, ResultError, Tag } from "../types";
import * as gherkinUtils from "./utils/gherkin";
import chalk from "chalk";

const _ = require("lodash");

Expand All @@ -18,7 +19,7 @@ export function run(feature: Feature, unused, config): ResultError[] {
feature.children?.forEach((child) => {
if (child.scenario) {
const type = gherkinUtils.getNodeType(child.scenario, feature.language);
const line = child.scenario.location?.line || -1;
const line = child.scenario.location?.line || 0;
const scenarioTags = child.scenario?.tags || [];
// Check each Scenario for the required tags
mergedConfig.tags.forEach(tag => {
Expand All @@ -38,7 +39,7 @@ function checkTagExists(requiredTag: string, ignoreUntagged: boolean, scenarioTa

function createError(requiredTag: string, scenarioType: string, scenarioLine: number) {
return {
message: `No tag found matching ${requiredTag} for ${scenarioType}`,
message: `No tag found matching ${chalk.yellow(requiredTag)} for ${chalk.cyan(scenarioType)}`,
rule: name,
line: scenarioLine,
};
Expand Down

0 comments on commit 03e8ae4

Please sign in to comment.