From b5540d164943fbb13184b00b02e65f8e529e7d35 Mon Sep 17 00:00:00 2001 From: aurelien-reeves Date: Wed, 7 Jul 2021 10:09:56 +0200 Subject: [PATCH 1/4] Add an unindent tagged template --- .../helpers/gherkin_document_parser_spec.ts | 103 +++---- src/formatter/helpers/issue_helpers_spec.ts | 267 +++++++++--------- src/formatter/progress_formatter_spec.ts | 122 ++++---- .../javascript_snippet_syntax_spec.ts | 67 ++--- src/formatter/usage_formatter_spec.ts | 76 ++--- test/formatter_helpers.ts | 46 +++ 6 files changed, 365 insertions(+), 316 deletions(-) diff --git a/src/formatter/helpers/gherkin_document_parser_spec.ts b/src/formatter/helpers/gherkin_document_parser_spec.ts index fd4b54f3c..cbf9bdd5e 100644 --- a/src/formatter/helpers/gherkin_document_parser_spec.ts +++ b/src/formatter/helpers/gherkin_document_parser_spec.ts @@ -12,6 +12,7 @@ import { } from '../../../test/gherkin_helpers' import * as messages from '@cucumber/messages' import IGherkinDocument = messages.GherkinDocument +import { unindent } from '../../../test/formatter_helpers' describe('GherkinDocumentParser', () => { describe('getGherkinStepMap', () => { @@ -315,64 +316,64 @@ async function parseGherkinDocument(data: string): Promise { } async function withBackgroundAndScenario(): Promise { - return await parseGherkinDocument(`\ -Feature: a feature - Background: - Given a setup step - - Scenario: - When a regular step -`) + return await parseGherkinDocument(unindent` + Feature: a feature + Background: + Given a setup step + + Scenario: + When a regular step + `) } async function withBackgroundAndScenarioOutline(): Promise { - return await parseGherkinDocument(`\ -Feature: a feature - Background: - Given a setup step - - Scenario Outline: - When a templated step with - Examples: - | word | - | foo | - | bar | -`) + return await parseGherkinDocument(unindent` + Feature: a feature + Background: + Given a setup step + + Scenario Outline: + When a templated step with + Examples: + | word | + | foo | + | bar | + `) } async function withBackgroundAndRuleWithExamples(): Promise { - return await parseGherkinDocument(`\ -Feature: a feature - Background: - Given a setup step - - Rule: a rule - Example: an example - When a regular step - Then an assertion - - Example: another example - When a regular step - Then an assertion -`) + return await parseGherkinDocument(unindent` + Feature: a feature + Background: + Given a setup step + + Rule: a rule + Example: an example + When a regular step + Then an assertion + + Example: another example + When a regular step + Then an assertion + `) } async function withBackgroundAndRuleWithBackgroundAndExamples(): Promise { - return await parseGherkinDocument(`\ -Feature: a feature - Background: - Given a feature-level setup step - - Rule: a rule - Background: - Given a rule-level setup step - - Example: an example - When a regular step - Then an assertion - - Example: another example - When a regular step - Then an assertion -`) + return await parseGherkinDocument(unindent` + Feature: a feature + Background: + Given a feature-level setup step + + Rule: a rule + Background: + Given a rule-level setup step + + Example: an example + When a regular step + Then an assertion + + Example: another example + When a regular step + Then an assertion + `) } diff --git a/src/formatter/helpers/issue_helpers_spec.ts b/src/formatter/helpers/issue_helpers_spec.ts index 1a63a3f71..b219eeff3 100644 --- a/src/formatter/helpers/issue_helpers_spec.ts +++ b/src/formatter/helpers/issue_helpers_spec.ts @@ -3,7 +3,7 @@ import { expect } from 'chai' import getColorFns from '../get_color_fns' import { formatIssue } from './issue_helpers' import figures from 'figures' -import { getTestCaseAttempts } from '../../../test/formatter_helpers' +import { getTestCaseAttempts, unindent } from '../../../test/formatter_helpers' import { getBaseSupportCodeLibrary } from '../../../test/fixtures/steps' import FormatterBuilder from '../builder' @@ -37,211 +37,212 @@ describe('IssueHelpers', () => { describe('with a failed step', () => { it('prints the scenario', async () => { // Arrange - const sourceData = `\ -Feature: my feature - Scenario: my scenario - Given a passing step - When a failing step - Then a passing step -` + const sourceData = unindent` + Feature: my feature + Scenario: my scenario + Given a passing step + When a failing step + Then a passing step + ` // Act const output = await testFormatIssue(sourceData) // Assert - expect(output).to.eql(`\ -1) Scenario: my scenario # a.feature:2 - ${figures.tick} Given a passing step # steps.ts:29 - ${figures.cross} When a failing step # steps.ts:9 - error - - Then a passing step # steps.ts:29 - -`) + expect(output).to.eql(unindent` + 1) Scenario: my scenario # a.feature:2 + ${figures.tick} Given a passing step # steps.ts:29 + ${figures.cross} When a failing step # steps.ts:9 + error + - Then a passing step # steps.ts:29 + + `) }) }) describe('with an ambiguous step', () => { it('returns the formatted scenario', async () => { // Arrange - const sourceData = `\ -Feature: my feature - Scenario: my scenario - Given a passing step - When an ambiguous step - Then a passing step -` + const sourceData = unindent` + Feature: my feature + Scenario: my scenario + Given a passing step + When an ambiguous step + Then a passing step + ` // Act const output = await testFormatIssue(sourceData) // Assert - expect(output).to.eql(`\ -1) Scenario: my scenario # a.feature:2 - ${figures.tick} Given a passing step # steps.ts:29 - ${figures.cross} When an ambiguous step - Multiple step definitions match: - an ambiguous step - steps.ts:13 - /an? ambiguous step/ - steps.ts:14 - - Then a passing step # steps.ts:29 - -`) + expect(output).to.eql(unindent` + 1) Scenario: my scenario # a.feature:2 + ${figures.tick} Given a passing step # steps.ts:29 + ${figures.cross} When an ambiguous step + Multiple step definitions match: + an ambiguous step - steps.ts:13 + /an? ambiguous step/ - steps.ts:14 + - Then a passing step # steps.ts:29 + + `) }) }) describe('with an undefined step', () => { it('returns the formatted scenario', async () => { // Arrange - const sourceData = `\ -Feature: my feature - Scenario: my scenario - Given a passing step - When an undefined step - Then a passing step -` + const sourceData = unindent` + Feature: my feature + Scenario: my scenario + Given a passing step + When an undefined step + Then a passing step + ` // Act const output = await testFormatIssue(sourceData) // Assert - expect(output).to.eql(`\ -1) Scenario: my scenario # a.feature:2 - ${figures.tick} Given a passing step # steps.ts:29 - ? When an undefined step - Undefined. Implement with the following snippet: + expect(output).to.eql(unindent` + 1) Scenario: my scenario # a.feature:2 + ${figures.tick} Given a passing step # steps.ts:29 + ? When an undefined step + Undefined. Implement with the following snippet: - When('an undefined step', function () { - // Write code here that turns the phrase above into concrete actions - return 'pending'; - }); + When('an undefined step', function () { + // Write code here that turns the phrase above into concrete actions + return 'pending'; + }); - - Then a passing step # steps.ts:29 + - Then a passing step # steps.ts:29 -`) + `) }) }) describe('with a pending step', () => { it('returns the formatted scenario', async () => { // Arrange - const sourceData = `\ -Feature: my feature - Scenario: my scenario - Given a passing step - When a pending step - Then a passing step -` + const sourceData = unindent` + Feature: my feature + Scenario: my scenario + Given a passing step + When a pending step + Then a passing step + ` // Act const output = await testFormatIssue(sourceData) // Assert - expect(output).to.eql(`\ -1) Scenario: my scenario # a.feature:2 - ${figures.tick} Given a passing step # steps.ts:29 - ? When a pending step # steps.ts:16 - Pending - - Then a passing step # steps.ts:29 - -`) + expect(output).to.eql(unindent` + 1) Scenario: my scenario # a.feature:2 + ${figures.tick} Given a passing step # steps.ts:29 + ? When a pending step # steps.ts:16 + Pending + - Then a passing step # steps.ts:29 + + `) }) }) describe('step with data table', () => { it('returns the formatted scenario', async () => { // Arrange - const sourceData = `\ -Feature: my feature - Scenario: my scenario - Given a passing step - When a pending step - Then a passing step - |aaa|b|c| - |d|e|ff| - |gg|h|iii| -` + const sourceData = unindent` + Feature: my feature + Scenario: my scenario + Given a passing step + When a pending step + Then a passing step + |aaa|b|c| + |d|e|ff| + |gg|h|iii| + ` // Act const output = await testFormatIssue(sourceData) // Assert - expect(output).to.eql(`\ -1) Scenario: my scenario # a.feature:2 - ${figures.tick} Given a passing step # steps.ts:29 - ? When a pending step # steps.ts:16 - Pending - - Then a passing step # steps.ts:29 - | aaa | b | c | - | d | e | ff | - | gg | h | iii | - -`) + expect(output).to.eql(unindent` + 1) Scenario: my scenario # a.feature:2 + ${figures.tick} Given a passing step # steps.ts:29 + ? When a pending step # steps.ts:16 + Pending + - Then a passing step # steps.ts:29 + | aaa | b | c | + | d | e | ff | + | gg | h | iii | + + `) }) }) describe('step with doc string', () => { it('returns the formatted scenario', async () => { // Arrange - const sourceData = `\ -Feature: my feature - Scenario: my scenario - Given a passing step - When a pending step - Then a passing step - """ - this is a multiline - doc string - - :-) - """ -` + const sourceData = unindent` + Feature: my feature + Scenario: my scenario + Given a passing step + When a pending step + Then a passing step + """ + this is a multiline + doc string + + :-) + """ + ` // Act const output = await testFormatIssue(sourceData) // Assert - expect(output).to.eql(`\ -1) Scenario: my scenario # a.feature:2 - ${figures.tick} Given a passing step # steps.ts:29 - ? When a pending step # steps.ts:16 - Pending - - Then a passing step # steps.ts:29 - """ - this is a multiline - doc string - - :-) - """ - -`) + expect(output).to.eql(unindent` + 1) Scenario: my scenario # a.feature:2 + ${figures.tick} Given a passing step # steps.ts:29 + ? When a pending step # steps.ts:16 + Pending + - Then a passing step # steps.ts:29 + """ + this is a multiline + doc string + + :-) + """ + + `) }) }) describe('step with attachment text', () => { it('prints the scenario', async () => { // Arrange - const sourceData = `\ -Feature: my feature - Scenario: my scenario - Given attachment step1 - When attachment step2 - Then a passing step -` + const sourceData = unindent` + Feature: my feature + Scenario: my scenario + Given attachment step1 + When attachment step2 + Then a passing step + ` + // Act const output = await testFormatIssue(sourceData) // Assert - expect(output).to.eql(`\ -1) Scenario: my scenario # a.feature:2 - ${figures.tick} Given attachment step1 # steps.ts:35 - Attachment (text/plain): Some info - Attachment (application/json) - Attachment (image/png) - ${figures.cross} When attachment step2 # steps.ts:41 - Attachment (text/plain): Other info - error - - Then a passing step # steps.ts:29 - -`) + expect(output).to.eql(unindent` + 1) Scenario: my scenario # a.feature:2 + ${figures.tick} Given attachment step1 # steps.ts:35 + Attachment (text/plain): Some info + Attachment (application/json) + Attachment (image/png) + ${figures.cross} When attachment step2 # steps.ts:41 + Attachment (text/plain): Other info + error + - Then a passing step # steps.ts:29 + + `) }) }) }) diff --git a/src/formatter/progress_formatter_spec.ts b/src/formatter/progress_formatter_spec.ts index 6786f6c19..2480fe392 100644 --- a/src/formatter/progress_formatter_spec.ts +++ b/src/formatter/progress_formatter_spec.ts @@ -1,7 +1,7 @@ import { afterEach, beforeEach, describe, it } from 'mocha' import { expect } from 'chai' import { getBaseSupportCodeLibrary } from '../../test/fixtures/steps' -import { testFormatter } from '../../test/formatter_helpers' +import { testFormatter, unindent } from '../../test/formatter_helpers' import figures from 'figures' import FakeTimers, { InstalledClock } from '@sinonjs/fake-timers' import timeMethods from '../time' @@ -21,21 +21,21 @@ describe('ProgressFormatter', () => { // Arrange const sources = [ { - data: `\ -Feature: a - Scenario: a1 - Given an ambiguous step - Scenario: a2 - Given a failing step - Scenario: a3 - Given a pending step - Scenario: a4 - Given a passing step - Scenario: a5 - Given a skipped step - Scenario: a6 - Given an undefined step -`, + data: unindent` + Feature: a + Scenario: a1 + Given an ambiguous step + Scenario: a2 + Given a failing step + Scenario: a3 + Given a pending step + Scenario: a4 + Given a passing step + Scenario: a5 + Given a skipped step + Scenario: a6 + Given an undefined step + `, uri: 'a.feature', }, ] @@ -49,60 +49,60 @@ Feature: a }) // Assert - expect(output).to.eql(`\ -AFP.-U + expect(output).to.eql(unindent` + AFP.-U -Failures: + Failures: -1) Scenario: a1 # a.feature:2 - ${figures.cross} Given an ambiguous step - Multiple step definitions match: - an ambiguous step - steps.ts:13 - /an? ambiguous step/ - steps.ts:14 + 1) Scenario: a1 # a.feature:2 + ${figures.cross} Given an ambiguous step + Multiple step definitions match: + an ambiguous step - steps.ts:13 + /an? ambiguous step/ - steps.ts:14 -2) Scenario: a2 # a.feature:4 - ${figures.cross} Given a failing step # steps.ts:9 - error + 2) Scenario: a2 # a.feature:4 + ${figures.cross} Given a failing step # steps.ts:9 + error -3) Scenario: a6 # a.feature:12 - ? Given an undefined step - Undefined. Implement with the following snippet: + 3) Scenario: a6 # a.feature:12 + ? Given an undefined step + Undefined. Implement with the following snippet: - Given('an undefined step', function () { - // Write code here that turns the phrase above into concrete actions - return 'pending'; - }); + Given('an undefined step', function () { + // Write code here that turns the phrase above into concrete actions + return 'pending'; + }); -Warnings: + Warnings: -1) Scenario: a3 # a.feature:6 - ? Given a pending step # steps.ts:16 - Pending + 1) Scenario: a3 # a.feature:6 + ? Given a pending step # steps.ts:16 + Pending -6 scenarios (1 failed, 1 ambiguous, 1 undefined, 1 pending, 1 skipped, 1 passed) -6 steps (1 failed, 1 ambiguous, 1 undefined, 1 pending, 1 skipped, 1 passed) - -`) + 6 scenarios (1 failed, 1 ambiguous, 1 undefined, 1 pending, 1 skipped, 1 passed) + 6 steps (1 failed, 1 ambiguous, 1 undefined, 1 pending, 1 skipped, 1 passed) + + `) }) it('handles rule/example results', async () => { // Arrange const sources = [ { - data: `\ -Feature: feature - Rule: rule1 - Example: example1 - Given a passing step - - Example: example2 - Given a passing step - - Rule: rule2 - Example: example1 - Given a passing step -`, + data: unindent` + Feature: feature + Rule: rule1 + Example: example1 + Given a passing step + + Example: example2 + Given a passing step + + Rule: rule2 + Example: example1 + Given a passing step + `, uri: 'a.feature', }, ] @@ -116,12 +116,12 @@ Feature: feature }) // Assert - expect(output).to.eql(`\ -... + expect(output).to.eql(unindent` + ... -3 scenarios (3 passed) -3 steps (3 passed) - -`) + 3 scenarios (3 passed) + 3 steps (3 passed) + + `) }) }) diff --git a/src/formatter/step_definition_snippet_builder/javascript_snippet_syntax_spec.ts b/src/formatter/step_definition_snippet_builder/javascript_snippet_syntax_spec.ts index 17978db03..65555924b 100644 --- a/src/formatter/step_definition_snippet_builder/javascript_snippet_syntax_spec.ts +++ b/src/formatter/step_definition_snippet_builder/javascript_snippet_syntax_spec.ts @@ -8,6 +8,7 @@ import { CucumberExpressionGenerator, ParameterTypeRegistry, } from '@cucumber/cucumber-expressions' +import { unindent } from '../../../test/formatter_helpers' function generateExpressions(text: string): readonly GeneratedExpression[] { const parameterTypeRegistry = new ParameterTypeRegistry() @@ -34,11 +35,11 @@ describe('JavascriptSnippetSyntax', () => { const result = syntax.build(buildOptions) // Assert - expect(result).to.eql(`\ -functionName('{string} def {string}', function (string, string2, callback) { - // comment - callback(null, 'pending'); -});`) + expect(result).to.eql(unindent` + functionName('{string} def {string}', function (string, string2, callback) { + // comment + callback(null, 'pending'); + });`) }) }) @@ -57,11 +58,11 @@ functionName('{string} def {string}', function (string, string2, callback) { const result = syntax.build(buildOptions) // Assert - expect(result).to.eql(`\ -functionName('{string} def {string}', function *(string, string2) { - // comment - return 'pending'; -});`) + expect(result).to.eql(unindent` + functionName('{string} def {string}', function *(string, string2) { + // comment + return 'pending'; + });`) }) }) @@ -80,11 +81,11 @@ functionName('{string} def {string}', function *(string, string2) { const result = syntax.build(buildOptions) // Assert - expect(result).to.eql(`\ -functionName('{string} def {string}', function (string, string2) { - // comment - return 'pending'; -});`) + expect(result).to.eql(unindent` + functionName('{string} def {string}', function (string, string2) { + // comment + return 'pending'; + });`) }) }) @@ -103,11 +104,11 @@ functionName('{string} def {string}', function (string, string2) { const result = syntax.build(buildOptions) // Assert - expect(result).to.eql(`\ -functionName('{string} def {string}', function (string, string2) { - // comment - return 'pending'; -});`) + expect(result).to.eql(unindent` + functionName('{string} def {string}', function (string, string2) { + // comment + return 'pending'; + });`) }) }) @@ -126,11 +127,11 @@ functionName('{string} def {string}', function (string, string2) { const result = syntax.build(buildOptions) // Assert - expect(result).to.eql(`\ -functionName('pattern\\'', function () { - // comment - return 'pending'; -});`) + expect(result).to.eql(unindent` + functionName('pattern\\'', function () { + // comment + return 'pending'; + });`) }) }) @@ -149,14 +150,14 @@ functionName('pattern\\'', function () { const result = syntax.build(buildOptions) // Assert - expect(result).to.eql(`\ -functionName('{int} {int}', function (int, int2) { -// functionName('{int} {float}', function (int, float) { -// functionName('{float} {int}', function (float, int) { -// functionName('{float} {float}', function (float, float2) { - // comment - return 'pending'; -});`) + expect(result).to.eql(unindent` + functionName('{int} {int}', function (int, int2) { + // functionName('{int} {float}', function (int, float) { + // functionName('{float} {int}', function (float, int) { + // functionName('{float} {float}', function (float, float2) { + // comment + return 'pending'; + });`) }) }) }) diff --git a/src/formatter/usage_formatter_spec.ts b/src/formatter/usage_formatter_spec.ts index b394f347f..8da9b02ca 100644 --- a/src/formatter/usage_formatter_spec.ts +++ b/src/formatter/usage_formatter_spec.ts @@ -3,7 +3,7 @@ import { expect } from 'chai' import FakeTimers, { InstalledClock } from '@sinonjs/fake-timers' import timeMethods from '../time' import { getUsageSupportCodeLibrary } from '../../test/fixtures/usage_steps' -import { testFormatter } from '../../test/formatter_helpers' +import { testFormatter, unindent } from '../../test/formatter_helpers' describe('UsageFormatter', () => { let clock: InstalledClock @@ -41,17 +41,17 @@ describe('UsageFormatter', () => { }) // Assert - expect(output).to.eql(`\ -┌────────────────┬──────────┬───────────────────┐ -│ Pattern / Text │ Duration │ Location │ -├────────────────┼──────────┼───────────────────┤ -│ abc │ UNUSED │ usage_steps.ts:11 │ -├────────────────┼──────────┼───────────────────┤ -│ /def?/ │ UNUSED │ usage_steps.ts:16 │ -├────────────────┼──────────┼───────────────────┤ -│ ghi │ UNUSED │ usage_steps.ts:25 │ -└────────────────┴──────────┴───────────────────┘ -`) + expect(output).to.eql(unindent` + ┌────────────────┬──────────┬───────────────────┐ + │ Pattern / Text │ Duration │ Location │ + ├────────────────┼──────────┼───────────────────┤ + │ abc │ UNUSED │ usage_steps.ts:11 │ + ├────────────────┼──────────┼───────────────────┤ + │ /def?/ │ UNUSED │ usage_steps.ts:16 │ + ├────────────────┼──────────┼───────────────────┤ + │ ghi │ UNUSED │ usage_steps.ts:25 │ + └────────────────┴──────────┴───────────────────┘ + `) }) }) @@ -77,19 +77,19 @@ describe('UsageFormatter', () => { }) // Assert - expect(output).to.eql(`\ -┌────────────────┬──────────┬───────────────────┐ -│ Pattern / Text │ Duration │ Location │ -├────────────────┼──────────┼───────────────────┤ -│ abc │ UNUSED │ usage_steps.ts:11 │ -├────────────────┼──────────┼───────────────────┤ -│ /def?/ │ - │ usage_steps.ts:16 │ -│ de │ - │ a.feature:4 │ -│ def │ - │ a.feature:3 │ -├────────────────┼──────────┼───────────────────┤ -│ ghi │ UNUSED │ usage_steps.ts:25 │ -└────────────────┴──────────┴───────────────────┘ -`) + expect(output).to.eql(unindent` + ┌────────────────┬──────────┬───────────────────┐ + │ Pattern / Text │ Duration │ Location │ + ├────────────────┼──────────┼───────────────────┤ + │ abc │ UNUSED │ usage_steps.ts:11 │ + ├────────────────┼──────────┼───────────────────┤ + │ /def?/ │ - │ usage_steps.ts:16 │ + │ de │ - │ a.feature:4 │ + │ def │ - │ a.feature:3 │ + ├────────────────┼──────────┼───────────────────┤ + │ ghi │ UNUSED │ usage_steps.ts:25 │ + └────────────────┴──────────┴───────────────────┘ + `) }) }) @@ -112,19 +112,19 @@ describe('UsageFormatter', () => { }) // Assert - expect(output).to.eql(`\ -┌────────────────┬──────────┬───────────────────┐ -│ Pattern / Text │ Duration │ Location │ -├────────────────┼──────────┼───────────────────┤ -│ /def?/ │ 1.50ms │ usage_steps.ts:16 │ -│ def │ 2ms │ a.feature:3 │ -│ de │ 1ms │ a.feature:4 │ -├────────────────┼──────────┼───────────────────┤ -│ abc │ UNUSED │ usage_steps.ts:11 │ -├────────────────┼──────────┼───────────────────┤ -│ ghi │ UNUSED │ usage_steps.ts:25 │ -└────────────────┴──────────┴───────────────────┘ -`) + expect(output).to.eql(unindent` + ┌────────────────┬──────────┬───────────────────┐ + │ Pattern / Text │ Duration │ Location │ + ├────────────────┼──────────┼───────────────────┤ + │ /def?/ │ 1.50ms │ usage_steps.ts:16 │ + │ def │ 2ms │ a.feature:3 │ + │ de │ 1ms │ a.feature:4 │ + ├────────────────┼──────────┼───────────────────┤ + │ abc │ UNUSED │ usage_steps.ts:11 │ + ├────────────────┼──────────┼───────────────────┤ + │ ghi │ UNUSED │ usage_steps.ts:25 │ + └────────────────┴──────────┴───────────────────┘ + `) }) }) }) diff --git a/test/formatter_helpers.ts b/test/formatter_helpers.ts index f947e5846..96b37c004 100644 --- a/test/formatter_helpers.ts +++ b/test/formatter_helpers.ts @@ -173,3 +173,49 @@ export function normalizeSummaryDuration(output: string): string { '' ) } + +/** + * Removes the indentation of the template based on the indentation of the first line. + * It removes the first new-line character so it is not needed for it to be escaped. + * + * @example + * if (true) { // This block illustrate indentation issues in source code + * const unindented = unindent` + * Expected output + * Expected indented output + * Another line + * ` + * // is the same as the following: + * const expected = `\ + * Expected output + * Expected indented output + * Another line + * ` + * expect(unindented).to.eql(expected) + * } + */ +export function unindent( + strings: TemplateStringsArray, + ...expressionValues: string[] +): string { + let finalString = strings[0].replace(/^\n/, '') + + const numberOfSpaceToRemove = finalString.search(/\S|$/) + const spacesToRemove = Array(numberOfSpaceToRemove + 1).join(' ') + + for (let i = 1; i < strings.length; i++) { + finalString += expressionValues[i - 1].toUpperCase() + finalString += strings[i] + } + + return finalString + .split('\n') + .map((line) => { + if (line.startsWith(spacesToRemove)) { + return line.replace(spacesToRemove, '') + } + + return line + }) + .join('\n') +} From c9cd2482a7ec7253aa380eb9417950ccd2122551 Mon Sep 17 00:00:00 2001 From: aurelien-reeves Date: Mon, 12 Jul 2021 14:39:18 +0200 Subject: [PATCH 2/4] Rename 'unindented' into 'reindented' --- .../helpers/gherkin_document_parser_spec.ts | 10 +++---- src/formatter/helpers/issue_helpers_spec.ts | 30 +++++++++---------- src/formatter/progress_formatter_spec.ts | 10 +++---- .../javascript_snippet_syntax_spec.ts | 14 ++++----- src/formatter/usage_formatter_spec.ts | 8 ++--- test/formatter_helpers.ts | 6 ++-- 6 files changed, 39 insertions(+), 39 deletions(-) diff --git a/src/formatter/helpers/gherkin_document_parser_spec.ts b/src/formatter/helpers/gherkin_document_parser_spec.ts index cbf9bdd5e..b77d5587c 100644 --- a/src/formatter/helpers/gherkin_document_parser_spec.ts +++ b/src/formatter/helpers/gherkin_document_parser_spec.ts @@ -12,7 +12,7 @@ import { } from '../../../test/gherkin_helpers' import * as messages from '@cucumber/messages' import IGherkinDocument = messages.GherkinDocument -import { unindent } from '../../../test/formatter_helpers' +import { reindent } from '../../../test/formatter_helpers' describe('GherkinDocumentParser', () => { describe('getGherkinStepMap', () => { @@ -316,7 +316,7 @@ async function parseGherkinDocument(data: string): Promise { } async function withBackgroundAndScenario(): Promise { - return await parseGherkinDocument(unindent` + return await parseGherkinDocument(reindent` Feature: a feature Background: Given a setup step @@ -327,7 +327,7 @@ async function withBackgroundAndScenario(): Promise { } async function withBackgroundAndScenarioOutline(): Promise { - return await parseGherkinDocument(unindent` + return await parseGherkinDocument(reindent` Feature: a feature Background: Given a setup step @@ -342,7 +342,7 @@ async function withBackgroundAndScenarioOutline(): Promise { } async function withBackgroundAndRuleWithExamples(): Promise { - return await parseGherkinDocument(unindent` + return await parseGherkinDocument(reindent` Feature: a feature Background: Given a setup step @@ -359,7 +359,7 @@ async function withBackgroundAndRuleWithExamples(): Promise { } async function withBackgroundAndRuleWithBackgroundAndExamples(): Promise { - return await parseGherkinDocument(unindent` + return await parseGherkinDocument(reindent` Feature: a feature Background: Given a feature-level setup step diff --git a/src/formatter/helpers/issue_helpers_spec.ts b/src/formatter/helpers/issue_helpers_spec.ts index b219eeff3..1717e8439 100644 --- a/src/formatter/helpers/issue_helpers_spec.ts +++ b/src/formatter/helpers/issue_helpers_spec.ts @@ -3,7 +3,7 @@ import { expect } from 'chai' import getColorFns from '../get_color_fns' import { formatIssue } from './issue_helpers' import figures from 'figures' -import { getTestCaseAttempts, unindent } from '../../../test/formatter_helpers' +import { getTestCaseAttempts, reindent } from '../../../test/formatter_helpers' import { getBaseSupportCodeLibrary } from '../../../test/fixtures/steps' import FormatterBuilder from '../builder' @@ -37,7 +37,7 @@ describe('IssueHelpers', () => { describe('with a failed step', () => { it('prints the scenario', async () => { // Arrange - const sourceData = unindent` + const sourceData = reindent` Feature: my feature Scenario: my scenario Given a passing step @@ -49,7 +49,7 @@ describe('IssueHelpers', () => { const output = await testFormatIssue(sourceData) // Assert - expect(output).to.eql(unindent` + expect(output).to.eql(reindent` 1) Scenario: my scenario # a.feature:2 ${figures.tick} Given a passing step # steps.ts:29 ${figures.cross} When a failing step # steps.ts:9 @@ -63,7 +63,7 @@ describe('IssueHelpers', () => { describe('with an ambiguous step', () => { it('returns the formatted scenario', async () => { // Arrange - const sourceData = unindent` + const sourceData = reindent` Feature: my feature Scenario: my scenario Given a passing step @@ -75,7 +75,7 @@ describe('IssueHelpers', () => { const output = await testFormatIssue(sourceData) // Assert - expect(output).to.eql(unindent` + expect(output).to.eql(reindent` 1) Scenario: my scenario # a.feature:2 ${figures.tick} Given a passing step # steps.ts:29 ${figures.cross} When an ambiguous step @@ -91,7 +91,7 @@ describe('IssueHelpers', () => { describe('with an undefined step', () => { it('returns the formatted scenario', async () => { // Arrange - const sourceData = unindent` + const sourceData = reindent` Feature: my feature Scenario: my scenario Given a passing step @@ -103,7 +103,7 @@ describe('IssueHelpers', () => { const output = await testFormatIssue(sourceData) // Assert - expect(output).to.eql(unindent` + expect(output).to.eql(reindent` 1) Scenario: my scenario # a.feature:2 ${figures.tick} Given a passing step # steps.ts:29 ? When an undefined step @@ -123,7 +123,7 @@ describe('IssueHelpers', () => { describe('with a pending step', () => { it('returns the formatted scenario', async () => { // Arrange - const sourceData = unindent` + const sourceData = reindent` Feature: my feature Scenario: my scenario Given a passing step @@ -135,7 +135,7 @@ describe('IssueHelpers', () => { const output = await testFormatIssue(sourceData) // Assert - expect(output).to.eql(unindent` + expect(output).to.eql(reindent` 1) Scenario: my scenario # a.feature:2 ${figures.tick} Given a passing step # steps.ts:29 ? When a pending step # steps.ts:16 @@ -149,7 +149,7 @@ describe('IssueHelpers', () => { describe('step with data table', () => { it('returns the formatted scenario', async () => { // Arrange - const sourceData = unindent` + const sourceData = reindent` Feature: my feature Scenario: my scenario Given a passing step @@ -164,7 +164,7 @@ describe('IssueHelpers', () => { const output = await testFormatIssue(sourceData) // Assert - expect(output).to.eql(unindent` + expect(output).to.eql(reindent` 1) Scenario: my scenario # a.feature:2 ${figures.tick} Given a passing step # steps.ts:29 ? When a pending step # steps.ts:16 @@ -181,7 +181,7 @@ describe('IssueHelpers', () => { describe('step with doc string', () => { it('returns the formatted scenario', async () => { // Arrange - const sourceData = unindent` + const sourceData = reindent` Feature: my feature Scenario: my scenario Given a passing step @@ -199,7 +199,7 @@ describe('IssueHelpers', () => { const output = await testFormatIssue(sourceData) // Assert - expect(output).to.eql(unindent` + expect(output).to.eql(reindent` 1) Scenario: my scenario # a.feature:2 ${figures.tick} Given a passing step # steps.ts:29 ? When a pending step # steps.ts:16 @@ -219,7 +219,7 @@ describe('IssueHelpers', () => { describe('step with attachment text', () => { it('prints the scenario', async () => { // Arrange - const sourceData = unindent` + const sourceData = reindent` Feature: my feature Scenario: my scenario Given attachment step1 @@ -231,7 +231,7 @@ describe('IssueHelpers', () => { const output = await testFormatIssue(sourceData) // Assert - expect(output).to.eql(unindent` + expect(output).to.eql(reindent` 1) Scenario: my scenario # a.feature:2 ${figures.tick} Given attachment step1 # steps.ts:35 Attachment (text/plain): Some info diff --git a/src/formatter/progress_formatter_spec.ts b/src/formatter/progress_formatter_spec.ts index 2480fe392..a05e3fc7e 100644 --- a/src/formatter/progress_formatter_spec.ts +++ b/src/formatter/progress_formatter_spec.ts @@ -1,7 +1,7 @@ import { afterEach, beforeEach, describe, it } from 'mocha' import { expect } from 'chai' import { getBaseSupportCodeLibrary } from '../../test/fixtures/steps' -import { testFormatter, unindent } from '../../test/formatter_helpers' +import { testFormatter, reindent } from '../../test/formatter_helpers' import figures from 'figures' import FakeTimers, { InstalledClock } from '@sinonjs/fake-timers' import timeMethods from '../time' @@ -21,7 +21,7 @@ describe('ProgressFormatter', () => { // Arrange const sources = [ { - data: unindent` + data: reindent` Feature: a Scenario: a1 Given an ambiguous step @@ -49,7 +49,7 @@ describe('ProgressFormatter', () => { }) // Assert - expect(output).to.eql(unindent` + expect(output).to.eql(reindent` AFP.-U Failures: @@ -90,7 +90,7 @@ describe('ProgressFormatter', () => { // Arrange const sources = [ { - data: unindent` + data: reindent` Feature: feature Rule: rule1 Example: example1 @@ -116,7 +116,7 @@ describe('ProgressFormatter', () => { }) // Assert - expect(output).to.eql(unindent` + expect(output).to.eql(reindent` ... 3 scenarios (3 passed) diff --git a/src/formatter/step_definition_snippet_builder/javascript_snippet_syntax_spec.ts b/src/formatter/step_definition_snippet_builder/javascript_snippet_syntax_spec.ts index 65555924b..175d3f7d3 100644 --- a/src/formatter/step_definition_snippet_builder/javascript_snippet_syntax_spec.ts +++ b/src/formatter/step_definition_snippet_builder/javascript_snippet_syntax_spec.ts @@ -8,7 +8,7 @@ import { CucumberExpressionGenerator, ParameterTypeRegistry, } from '@cucumber/cucumber-expressions' -import { unindent } from '../../../test/formatter_helpers' +import { reindent } from '../../../test/formatter_helpers' function generateExpressions(text: string): readonly GeneratedExpression[] { const parameterTypeRegistry = new ParameterTypeRegistry() @@ -35,7 +35,7 @@ describe('JavascriptSnippetSyntax', () => { const result = syntax.build(buildOptions) // Assert - expect(result).to.eql(unindent` + expect(result).to.eql(reindent` functionName('{string} def {string}', function (string, string2, callback) { // comment callback(null, 'pending'); @@ -58,7 +58,7 @@ describe('JavascriptSnippetSyntax', () => { const result = syntax.build(buildOptions) // Assert - expect(result).to.eql(unindent` + expect(result).to.eql(reindent` functionName('{string} def {string}', function *(string, string2) { // comment return 'pending'; @@ -81,7 +81,7 @@ describe('JavascriptSnippetSyntax', () => { const result = syntax.build(buildOptions) // Assert - expect(result).to.eql(unindent` + expect(result).to.eql(reindent` functionName('{string} def {string}', function (string, string2) { // comment return 'pending'; @@ -104,7 +104,7 @@ describe('JavascriptSnippetSyntax', () => { const result = syntax.build(buildOptions) // Assert - expect(result).to.eql(unindent` + expect(result).to.eql(reindent` functionName('{string} def {string}', function (string, string2) { // comment return 'pending'; @@ -127,7 +127,7 @@ describe('JavascriptSnippetSyntax', () => { const result = syntax.build(buildOptions) // Assert - expect(result).to.eql(unindent` + expect(result).to.eql(reindent` functionName('pattern\\'', function () { // comment return 'pending'; @@ -150,7 +150,7 @@ describe('JavascriptSnippetSyntax', () => { const result = syntax.build(buildOptions) // Assert - expect(result).to.eql(unindent` + expect(result).to.eql(reindent` functionName('{int} {int}', function (int, int2) { // functionName('{int} {float}', function (int, float) { // functionName('{float} {int}', function (float, int) { diff --git a/src/formatter/usage_formatter_spec.ts b/src/formatter/usage_formatter_spec.ts index 8da9b02ca..b83fc5c3c 100644 --- a/src/formatter/usage_formatter_spec.ts +++ b/src/formatter/usage_formatter_spec.ts @@ -3,7 +3,7 @@ import { expect } from 'chai' import FakeTimers, { InstalledClock } from '@sinonjs/fake-timers' import timeMethods from '../time' import { getUsageSupportCodeLibrary } from '../../test/fixtures/usage_steps' -import { testFormatter, unindent } from '../../test/formatter_helpers' +import { testFormatter, reindent } from '../../test/formatter_helpers' describe('UsageFormatter', () => { let clock: InstalledClock @@ -41,7 +41,7 @@ describe('UsageFormatter', () => { }) // Assert - expect(output).to.eql(unindent` + expect(output).to.eql(reindent` ┌────────────────┬──────────┬───────────────────┐ │ Pattern / Text │ Duration │ Location │ ├────────────────┼──────────┼───────────────────┤ @@ -77,7 +77,7 @@ describe('UsageFormatter', () => { }) // Assert - expect(output).to.eql(unindent` + expect(output).to.eql(reindent` ┌────────────────┬──────────┬───────────────────┐ │ Pattern / Text │ Duration │ Location │ ├────────────────┼──────────┼───────────────────┤ @@ -112,7 +112,7 @@ describe('UsageFormatter', () => { }) // Assert - expect(output).to.eql(unindent` + expect(output).to.eql(reindent` ┌────────────────┬──────────┬───────────────────┐ │ Pattern / Text │ Duration │ Location │ ├────────────────┼──────────┼───────────────────┤ diff --git a/test/formatter_helpers.ts b/test/formatter_helpers.ts index 96b37c004..4afa266c8 100644 --- a/test/formatter_helpers.ts +++ b/test/formatter_helpers.ts @@ -180,7 +180,7 @@ export function normalizeSummaryDuration(output: string): string { * * @example * if (true) { // This block illustrate indentation issues in source code - * const unindented = unindent` + * const reindented = reindent` * Expected output * Expected indented output * Another line @@ -191,10 +191,10 @@ export function normalizeSummaryDuration(output: string): string { * Expected indented output * Another line * ` - * expect(unindented).to.eql(expected) + * expect(reindented).to.eql(expected) * } */ -export function unindent( +export function reindent( strings: TemplateStringsArray, ...expressionValues: string[] ): string { From e9666fbc07335ec3fd67504dce452fca97a5ff8f Mon Sep 17 00:00:00 2001 From: aurelien-reeves Date: Tue, 13 Jul 2021 09:22:00 +0200 Subject: [PATCH 3/4] Transform the tag template to a regular function --- .../helpers/gherkin_document_parser_spec.ts | 90 +++++----- src/formatter/helpers/issue_helpers_spec.ts | 168 ++++++++++-------- src/formatter/progress_formatter_spec.ts | 72 ++++---- .../javascript_snippet_syntax_spec.ts | 78 ++++---- src/formatter/usage_formatter_spec.ts | 74 ++++---- test/formatter_helpers.ts | 35 ++-- 6 files changed, 278 insertions(+), 239 deletions(-) diff --git a/src/formatter/helpers/gherkin_document_parser_spec.ts b/src/formatter/helpers/gherkin_document_parser_spec.ts index b77d5587c..51016c999 100644 --- a/src/formatter/helpers/gherkin_document_parser_spec.ts +++ b/src/formatter/helpers/gherkin_document_parser_spec.ts @@ -316,64 +316,72 @@ async function parseGherkinDocument(data: string): Promise { } async function withBackgroundAndScenario(): Promise { - return await parseGherkinDocument(reindent` - Feature: a feature - Background: - Given a setup step + return await parseGherkinDocument( + reindent(` + Feature: a feature + Background: + Given a setup step - Scenario: - When a regular step + Scenario: + When a regular step `) + ) } async function withBackgroundAndScenarioOutline(): Promise { - return await parseGherkinDocument(reindent` - Feature: a feature - Background: - Given a setup step - - Scenario Outline: - When a templated step with - Examples: - | word | - | foo | - | bar | + return await parseGherkinDocument( + reindent(` + Feature: a feature + Background: + Given a setup step + + Scenario Outline: + When a templated step with + Examples: + | word | + | foo | + | bar | `) + ) } async function withBackgroundAndRuleWithExamples(): Promise { - return await parseGherkinDocument(reindent` - Feature: a feature - Background: - Given a setup step + return await parseGherkinDocument( + reindent(` + Feature: a feature + Background: + Given a setup step - Rule: a rule - Example: an example - When a regular step - Then an assertion + Rule: a rule + Example: an example + When a regular step + Then an assertion - Example: another example - When a regular step - Then an assertion + Example: another example + When a regular step + Then an assertion `) + ) } async function withBackgroundAndRuleWithBackgroundAndExamples(): Promise { - return await parseGherkinDocument(reindent` - Feature: a feature - Background: - Given a feature-level setup step - - Rule: a rule + return await parseGherkinDocument( + reindent(` + Feature: a feature Background: - Given a rule-level setup step + Given a feature-level setup step - Example: an example - When a regular step - Then an assertion + Rule: a rule + Background: + Given a rule-level setup step - Example: another example - When a regular step - Then an assertion + Example: an example + When a regular step + Then an assertion + + Example: another example + When a regular step + Then an assertion `) + ) } diff --git a/src/formatter/helpers/issue_helpers_spec.ts b/src/formatter/helpers/issue_helpers_spec.ts index 1717e8439..43eb638cf 100644 --- a/src/formatter/helpers/issue_helpers_spec.ts +++ b/src/formatter/helpers/issue_helpers_spec.ts @@ -37,119 +37,127 @@ describe('IssueHelpers', () => { describe('with a failed step', () => { it('prints the scenario', async () => { // Arrange - const sourceData = reindent` + const sourceData = reindent(` Feature: my feature Scenario: my scenario Given a passing step When a failing step Then a passing step - ` + `) // Act const output = await testFormatIssue(sourceData) // Assert - expect(output).to.eql(reindent` - 1) Scenario: my scenario # a.feature:2 - ${figures.tick} Given a passing step # steps.ts:29 - ${figures.cross} When a failing step # steps.ts:9 - error - - Then a passing step # steps.ts:29 + expect(output).to.eql( + reindent(` + 1) Scenario: my scenario # a.feature:2 + ${figures.tick} Given a passing step # steps.ts:29 + ${figures.cross} When a failing step # steps.ts:9 + error + - Then a passing step # steps.ts:29 `) + ) }) }) describe('with an ambiguous step', () => { it('returns the formatted scenario', async () => { // Arrange - const sourceData = reindent` + const sourceData = reindent(` Feature: my feature Scenario: my scenario Given a passing step When an ambiguous step Then a passing step - ` + `) // Act const output = await testFormatIssue(sourceData) // Assert - expect(output).to.eql(reindent` - 1) Scenario: my scenario # a.feature:2 - ${figures.tick} Given a passing step # steps.ts:29 - ${figures.cross} When an ambiguous step - Multiple step definitions match: - an ambiguous step - steps.ts:13 - /an? ambiguous step/ - steps.ts:14 - - Then a passing step # steps.ts:29 + expect(output).to.eql( + reindent(` + 1) Scenario: my scenario # a.feature:2 + ${figures.tick} Given a passing step # steps.ts:29 + ${figures.cross} When an ambiguous step + Multiple step definitions match: + an ambiguous step - steps.ts:13 + /an? ambiguous step/ - steps.ts:14 + - Then a passing step # steps.ts:29 `) + ) }) }) describe('with an undefined step', () => { it('returns the formatted scenario', async () => { // Arrange - const sourceData = reindent` + const sourceData = reindent(` Feature: my feature Scenario: my scenario Given a passing step When an undefined step Then a passing step - ` + `) // Act const output = await testFormatIssue(sourceData) // Assert - expect(output).to.eql(reindent` - 1) Scenario: my scenario # a.feature:2 - ${figures.tick} Given a passing step # steps.ts:29 - ? When an undefined step - Undefined. Implement with the following snippet: + expect(output).to.eql( + reindent(` + 1) Scenario: my scenario # a.feature:2 + ${figures.tick} Given a passing step # steps.ts:29 + ? When an undefined step + Undefined. Implement with the following snippet: - When('an undefined step', function () { - // Write code here that turns the phrase above into concrete actions - return 'pending'; - }); + When('an undefined step', function () { + // Write code here that turns the phrase above into concrete actions + return 'pending'; + }); - - Then a passing step # steps.ts:29 + - Then a passing step # steps.ts:29 `) + ) }) }) describe('with a pending step', () => { it('returns the formatted scenario', async () => { // Arrange - const sourceData = reindent` + const sourceData = reindent(` Feature: my feature Scenario: my scenario Given a passing step When a pending step Then a passing step - ` + `) // Act const output = await testFormatIssue(sourceData) // Assert - expect(output).to.eql(reindent` - 1) Scenario: my scenario # a.feature:2 - ${figures.tick} Given a passing step # steps.ts:29 - ? When a pending step # steps.ts:16 - Pending - - Then a passing step # steps.ts:29 + expect(output).to.eql( + reindent(` + 1) Scenario: my scenario # a.feature:2 + ${figures.tick} Given a passing step # steps.ts:29 + ? When a pending step # steps.ts:16 + Pending + - Then a passing step # steps.ts:29 `) + ) }) }) describe('step with data table', () => { it('returns the formatted scenario', async () => { // Arrange - const sourceData = reindent` + const sourceData = reindent(` Feature: my feature Scenario: my scenario Given a passing step @@ -158,30 +166,32 @@ describe('IssueHelpers', () => { |aaa|b|c| |d|e|ff| |gg|h|iii| - ` + `) // Act const output = await testFormatIssue(sourceData) // Assert - expect(output).to.eql(reindent` - 1) Scenario: my scenario # a.feature:2 - ${figures.tick} Given a passing step # steps.ts:29 - ? When a pending step # steps.ts:16 - Pending - - Then a passing step # steps.ts:29 - | aaa | b | c | - | d | e | ff | - | gg | h | iii | + expect(output).to.eql( + reindent(` + 1) Scenario: my scenario # a.feature:2 + ${figures.tick} Given a passing step # steps.ts:29 + ? When a pending step # steps.ts:16 + Pending + - Then a passing step # steps.ts:29 + | aaa | b | c | + | d | e | ff | + | gg | h | iii | `) + ) }) }) describe('step with doc string', () => { it('returns the formatted scenario', async () => { // Arrange - const sourceData = reindent` + const sourceData = reindent(` Feature: my feature Scenario: my scenario Given a passing step @@ -193,56 +203,60 @@ describe('IssueHelpers', () => { :-) """ - ` + `) // Act const output = await testFormatIssue(sourceData) // Assert - expect(output).to.eql(reindent` - 1) Scenario: my scenario # a.feature:2 - ${figures.tick} Given a passing step # steps.ts:29 - ? When a pending step # steps.ts:16 - Pending - - Then a passing step # steps.ts:29 - """ - this is a multiline - doc string - - :-) - """ - - `) + expect(output).to.eql( + reindent(` + 1) Scenario: my scenario # a.feature:2 + ${figures.tick} Given a passing step # steps.ts:29 + ? When a pending step # steps.ts:16 + Pending + - Then a passing step # steps.ts:29 + """ + this is a multiline + doc string + + :-) + """ + + `) + ) }) }) describe('step with attachment text', () => { it('prints the scenario', async () => { // Arrange - const sourceData = reindent` + const sourceData = reindent(` Feature: my feature Scenario: my scenario Given attachment step1 When attachment step2 Then a passing step - ` + `) // Act const output = await testFormatIssue(sourceData) // Assert - expect(output).to.eql(reindent` - 1) Scenario: my scenario # a.feature:2 - ${figures.tick} Given attachment step1 # steps.ts:35 - Attachment (text/plain): Some info - Attachment (application/json) - Attachment (image/png) - ${figures.cross} When attachment step2 # steps.ts:41 - Attachment (text/plain): Other info - error - - Then a passing step # steps.ts:29 + expect(output).to.eql( + reindent(` + 1) Scenario: my scenario # a.feature:2 + ${figures.tick} Given attachment step1 # steps.ts:35 + Attachment (text/plain): Some info + Attachment (application/json) + Attachment (image/png) + ${figures.cross} When attachment step2 # steps.ts:41 + Attachment (text/plain): Other info + error + - Then a passing step # steps.ts:29 `) + ) }) }) }) diff --git a/src/formatter/progress_formatter_spec.ts b/src/formatter/progress_formatter_spec.ts index a05e3fc7e..e59012d48 100644 --- a/src/formatter/progress_formatter_spec.ts +++ b/src/formatter/progress_formatter_spec.ts @@ -21,7 +21,7 @@ describe('ProgressFormatter', () => { // Arrange const sources = [ { - data: reindent` + data: reindent(` Feature: a Scenario: a1 Given an ambiguous step @@ -35,7 +35,7 @@ describe('ProgressFormatter', () => { Given a skipped step Scenario: a6 Given an undefined step - `, + `), uri: 'a.feature', }, ] @@ -49,48 +49,50 @@ describe('ProgressFormatter', () => { }) // Assert - expect(output).to.eql(reindent` - AFP.-U + expect(output).to.eql( + reindent(` + AFP.-U - Failures: + Failures: - 1) Scenario: a1 # a.feature:2 - ${figures.cross} Given an ambiguous step - Multiple step definitions match: - an ambiguous step - steps.ts:13 - /an? ambiguous step/ - steps.ts:14 + 1) Scenario: a1 # a.feature:2 + ${figures.cross} Given an ambiguous step + Multiple step definitions match: + an ambiguous step - steps.ts:13 + /an? ambiguous step/ - steps.ts:14 - 2) Scenario: a2 # a.feature:4 - ${figures.cross} Given a failing step # steps.ts:9 - error + 2) Scenario: a2 # a.feature:4 + ${figures.cross} Given a failing step # steps.ts:9 + error - 3) Scenario: a6 # a.feature:12 - ? Given an undefined step - Undefined. Implement with the following snippet: + 3) Scenario: a6 # a.feature:12 + ? Given an undefined step + Undefined. Implement with the following snippet: - Given('an undefined step', function () { - // Write code here that turns the phrase above into concrete actions - return 'pending'; - }); + Given('an undefined step', function () { + // Write code here that turns the phrase above into concrete actions + return 'pending'; + }); - Warnings: + Warnings: - 1) Scenario: a3 # a.feature:6 - ? Given a pending step # steps.ts:16 - Pending + 1) Scenario: a3 # a.feature:6 + ? Given a pending step # steps.ts:16 + Pending - 6 scenarios (1 failed, 1 ambiguous, 1 undefined, 1 pending, 1 skipped, 1 passed) - 6 steps (1 failed, 1 ambiguous, 1 undefined, 1 pending, 1 skipped, 1 passed) - + 6 scenarios (1 failed, 1 ambiguous, 1 undefined, 1 pending, 1 skipped, 1 passed) + 6 steps (1 failed, 1 ambiguous, 1 undefined, 1 pending, 1 skipped, 1 passed) + `) + ) }) it('handles rule/example results', async () => { // Arrange const sources = [ { - data: reindent` + data: reindent(` Feature: feature Rule: rule1 Example: example1 @@ -102,7 +104,7 @@ describe('ProgressFormatter', () => { Rule: rule2 Example: example1 Given a passing step - `, + `), uri: 'a.feature', }, ] @@ -116,12 +118,14 @@ describe('ProgressFormatter', () => { }) // Assert - expect(output).to.eql(reindent` - ... + expect(output).to.eql( + reindent(` + ... - 3 scenarios (3 passed) - 3 steps (3 passed) - + 3 scenarios (3 passed) + 3 steps (3 passed) + `) + ) }) }) diff --git a/src/formatter/step_definition_snippet_builder/javascript_snippet_syntax_spec.ts b/src/formatter/step_definition_snippet_builder/javascript_snippet_syntax_spec.ts index 175d3f7d3..f3a8bea4a 100644 --- a/src/formatter/step_definition_snippet_builder/javascript_snippet_syntax_spec.ts +++ b/src/formatter/step_definition_snippet_builder/javascript_snippet_syntax_spec.ts @@ -35,11 +35,13 @@ describe('JavascriptSnippetSyntax', () => { const result = syntax.build(buildOptions) // Assert - expect(result).to.eql(reindent` - functionName('{string} def {string}', function (string, string2, callback) { - // comment - callback(null, 'pending'); - });`) + expect(result).to.eql( + reindent(` + functionName('{string} def {string}', function (string, string2, callback) { + // comment + callback(null, 'pending'); + });`) + ) }) }) @@ -58,11 +60,13 @@ describe('JavascriptSnippetSyntax', () => { const result = syntax.build(buildOptions) // Assert - expect(result).to.eql(reindent` - functionName('{string} def {string}', function *(string, string2) { - // comment - return 'pending'; - });`) + expect(result).to.eql( + reindent(` + functionName('{string} def {string}', function *(string, string2) { + // comment + return 'pending'; + });`) + ) }) }) @@ -81,11 +85,13 @@ describe('JavascriptSnippetSyntax', () => { const result = syntax.build(buildOptions) // Assert - expect(result).to.eql(reindent` - functionName('{string} def {string}', function (string, string2) { - // comment - return 'pending'; - });`) + expect(result).to.eql( + reindent(` + functionName('{string} def {string}', function (string, string2) { + // comment + return 'pending'; + });`) + ) }) }) @@ -104,11 +110,13 @@ describe('JavascriptSnippetSyntax', () => { const result = syntax.build(buildOptions) // Assert - expect(result).to.eql(reindent` - functionName('{string} def {string}', function (string, string2) { - // comment - return 'pending'; - });`) + expect(result).to.eql( + reindent(` + functionName('{string} def {string}', function (string, string2) { + // comment + return 'pending'; + });`) + ) }) }) @@ -127,11 +135,13 @@ describe('JavascriptSnippetSyntax', () => { const result = syntax.build(buildOptions) // Assert - expect(result).to.eql(reindent` - functionName('pattern\\'', function () { - // comment - return 'pending'; - });`) + expect(result).to.eql( + reindent(` + functionName('pattern\\'', function () { + // comment + return 'pending'; + });`) + ) }) }) @@ -150,14 +160,16 @@ describe('JavascriptSnippetSyntax', () => { const result = syntax.build(buildOptions) // Assert - expect(result).to.eql(reindent` - functionName('{int} {int}', function (int, int2) { - // functionName('{int} {float}', function (int, float) { - // functionName('{float} {int}', function (float, int) { - // functionName('{float} {float}', function (float, float2) { - // comment - return 'pending'; - });`) + expect(result).to.eql( + reindent(` + functionName('{int} {int}', function (int, int2) { + // functionName('{int} {float}', function (int, float) { + // functionName('{float} {int}', function (float, int) { + // functionName('{float} {float}', function (float, float2) { + // comment + return 'pending'; + });`) + ) }) }) }) diff --git a/src/formatter/usage_formatter_spec.ts b/src/formatter/usage_formatter_spec.ts index b83fc5c3c..10a848e58 100644 --- a/src/formatter/usage_formatter_spec.ts +++ b/src/formatter/usage_formatter_spec.ts @@ -41,17 +41,19 @@ describe('UsageFormatter', () => { }) // Assert - expect(output).to.eql(reindent` - ┌────────────────┬──────────┬───────────────────┐ - │ Pattern / Text │ Duration │ Location │ - ├────────────────┼──────────┼───────────────────┤ - │ abc │ UNUSED │ usage_steps.ts:11 │ - ├────────────────┼──────────┼───────────────────┤ - │ /def?/ │ UNUSED │ usage_steps.ts:16 │ - ├────────────────┼──────────┼───────────────────┤ - │ ghi │ UNUSED │ usage_steps.ts:25 │ - └────────────────┴──────────┴───────────────────┘ + expect(output).to.eql( + reindent(` + ┌────────────────┬──────────┬───────────────────┐ + │ Pattern / Text │ Duration │ Location │ + ├────────────────┼──────────┼───────────────────┤ + │ abc │ UNUSED │ usage_steps.ts:11 │ + ├────────────────┼──────────┼───────────────────┤ + │ /def?/ │ UNUSED │ usage_steps.ts:16 │ + ├────────────────┼──────────┼───────────────────┤ + │ ghi │ UNUSED │ usage_steps.ts:25 │ + └────────────────┴──────────┴───────────────────┘ `) + ) }) }) @@ -77,19 +79,21 @@ describe('UsageFormatter', () => { }) // Assert - expect(output).to.eql(reindent` - ┌────────────────┬──────────┬───────────────────┐ - │ Pattern / Text │ Duration │ Location │ - ├────────────────┼──────────┼───────────────────┤ - │ abc │ UNUSED │ usage_steps.ts:11 │ - ├────────────────┼──────────┼───────────────────┤ - │ /def?/ │ - │ usage_steps.ts:16 │ - │ de │ - │ a.feature:4 │ - │ def │ - │ a.feature:3 │ - ├────────────────┼──────────┼───────────────────┤ - │ ghi │ UNUSED │ usage_steps.ts:25 │ - └────────────────┴──────────┴───────────────────┘ + expect(output).to.eql( + reindent(` + ┌────────────────┬──────────┬───────────────────┐ + │ Pattern / Text │ Duration │ Location │ + ├────────────────┼──────────┼───────────────────┤ + │ abc │ UNUSED │ usage_steps.ts:11 │ + ├────────────────┼──────────┼───────────────────┤ + │ /def?/ │ - │ usage_steps.ts:16 │ + │ de │ - │ a.feature:4 │ + │ def │ - │ a.feature:3 │ + ├────────────────┼──────────┼───────────────────┤ + │ ghi │ UNUSED │ usage_steps.ts:25 │ + └────────────────┴──────────┴───────────────────┘ `) + ) }) }) @@ -112,19 +116,21 @@ describe('UsageFormatter', () => { }) // Assert - expect(output).to.eql(reindent` - ┌────────────────┬──────────┬───────────────────┐ - │ Pattern / Text │ Duration │ Location │ - ├────────────────┼──────────┼───────────────────┤ - │ /def?/ │ 1.50ms │ usage_steps.ts:16 │ - │ def │ 2ms │ a.feature:3 │ - │ de │ 1ms │ a.feature:4 │ - ├────────────────┼──────────┼───────────────────┤ - │ abc │ UNUSED │ usage_steps.ts:11 │ - ├────────────────┼──────────┼───────────────────┤ - │ ghi │ UNUSED │ usage_steps.ts:25 │ - └────────────────┴──────────┴───────────────────┘ + expect(output).to.eql( + reindent(` + ┌────────────────┬──────────┬───────────────────┐ + │ Pattern / Text │ Duration │ Location │ + ├────────────────┼──────────┼───────────────────┤ + │ /def?/ │ 1.50ms │ usage_steps.ts:16 │ + │ def │ 2ms │ a.feature:3 │ + │ de │ 1ms │ a.feature:4 │ + ├────────────────┼──────────┼───────────────────┤ + │ abc │ UNUSED │ usage_steps.ts:11 │ + ├────────────────┼──────────┼───────────────────┤ + │ ghi │ UNUSED │ usage_steps.ts:25 │ + └────────────────┴──────────┴───────────────────┘ `) + ) }) }) }) diff --git a/test/formatter_helpers.ts b/test/formatter_helpers.ts index 15b72dc72..ed904099e 100644 --- a/test/formatter_helpers.ts +++ b/test/formatter_helpers.ts @@ -175,17 +175,18 @@ export function normalizeSummaryDuration(output: string): string { } /** - * Removes the indentation of the template based on the indentation of the first line. - * It removes the first new-line character so it is not needed for it to be escaped. + * Removes the indentation of the given string based on the indentation of its first line. + * + * It removes the first line if it is empty so it is not needed for it to be escaped. * * @example - * if (true) { // This block illustrate indentation issues in source code - * const reindented = reindent` + * { + * const reindented = reindent(` * Expected output * Expected indented output * Another line - * ` - * // is the same as the following: + * `) + * * const expected = `\ * Expected output * Expected indented output @@ -194,23 +195,17 @@ export function normalizeSummaryDuration(output: string): string { * expect(reindented).to.eql(expected) * } */ -export function reindent( - strings: TemplateStringsArray, - ...expressionValues: string[] -): string { - let finalString = strings[0].replace(/^\n/, '') - - const numberOfSpaceToRemove = finalString.search(/\S|$/) +export function reindent(original: string): string { + const lines = original.replace(/^\n/, '').split(`\n`) + const numberOfSpaceToRemove = lines[0].search(/\S|$/) const spacesToRemove = Array(numberOfSpaceToRemove + 1).join(' ') - for (let i = 1; i < strings.length; i++) { - finalString += expressionValues[i - 1].toUpperCase() - finalString += strings[i] - } + return lines + .map((line, index) => { + if (index === lines.length - 1 && line.trim() === '') { + return '' + } - return finalString - .split('\n') - .map((line) => { if (line.startsWith(spacesToRemove)) { return line.replace(spacesToRemove, '') } From 734278e4dfb9879e0fde49a013abeb92471edbb7 Mon Sep 17 00:00:00 2001 From: aurelien-reeves Date: Tue, 20 Jul 2021 09:34:10 +0200 Subject: [PATCH 4/4] Use npm package reindent-template-literals --- package.json | 1 + .../helpers/gherkin_document_parser_spec.ts | 2 +- src/formatter/helpers/issue_helpers_spec.ts | 3 +- src/formatter/progress_formatter_spec.ts | 3 +- .../javascript_snippet_syntax_spec.ts | 2 +- src/formatter/usage_formatter_spec.ts | 3 +- test/formatter_helpers.ts | 41 ------------------- yarn.lock | 5 +++ 8 files changed, 14 insertions(+), 46 deletions(-) diff --git a/package.json b/package.json index c3d6e0ef1..33923800f 100644 --- a/package.json +++ b/package.json @@ -245,6 +245,7 @@ "mustache": "4.2.0", "nyc": "15.1.0", "prettier": "2.3.2", + "reindent-template-literals": "^1.0.0", "semver": "7.3.5", "sinon": "11.1.1", "sinon-chai": "3.7.0", diff --git a/src/formatter/helpers/gherkin_document_parser_spec.ts b/src/formatter/helpers/gherkin_document_parser_spec.ts index 51016c999..8bea18ee7 100644 --- a/src/formatter/helpers/gherkin_document_parser_spec.ts +++ b/src/formatter/helpers/gherkin_document_parser_spec.ts @@ -12,7 +12,7 @@ import { } from '../../../test/gherkin_helpers' import * as messages from '@cucumber/messages' import IGherkinDocument = messages.GherkinDocument -import { reindent } from '../../../test/formatter_helpers' +import { reindent } from 'reindent-template-literals' describe('GherkinDocumentParser', () => { describe('getGherkinStepMap', () => { diff --git a/src/formatter/helpers/issue_helpers_spec.ts b/src/formatter/helpers/issue_helpers_spec.ts index 43eb638cf..61c5dc4e3 100644 --- a/src/formatter/helpers/issue_helpers_spec.ts +++ b/src/formatter/helpers/issue_helpers_spec.ts @@ -3,7 +3,8 @@ import { expect } from 'chai' import getColorFns from '../get_color_fns' import { formatIssue } from './issue_helpers' import figures from 'figures' -import { getTestCaseAttempts, reindent } from '../../../test/formatter_helpers' +import { getTestCaseAttempts } from '../../../test/formatter_helpers' +import { reindent } from 'reindent-template-literals' import { getBaseSupportCodeLibrary } from '../../../test/fixtures/steps' import FormatterBuilder from '../builder' diff --git a/src/formatter/progress_formatter_spec.ts b/src/formatter/progress_formatter_spec.ts index e59012d48..d7078e624 100644 --- a/src/formatter/progress_formatter_spec.ts +++ b/src/formatter/progress_formatter_spec.ts @@ -1,7 +1,8 @@ import { afterEach, beforeEach, describe, it } from 'mocha' import { expect } from 'chai' import { getBaseSupportCodeLibrary } from '../../test/fixtures/steps' -import { testFormatter, reindent } from '../../test/formatter_helpers' +import { testFormatter } from '../../test/formatter_helpers' +import { reindent } from 'reindent-template-literals' import figures from 'figures' import FakeTimers, { InstalledClock } from '@sinonjs/fake-timers' import timeMethods from '../time' diff --git a/src/formatter/step_definition_snippet_builder/javascript_snippet_syntax_spec.ts b/src/formatter/step_definition_snippet_builder/javascript_snippet_syntax_spec.ts index f3a8bea4a..18fcf7781 100644 --- a/src/formatter/step_definition_snippet_builder/javascript_snippet_syntax_spec.ts +++ b/src/formatter/step_definition_snippet_builder/javascript_snippet_syntax_spec.ts @@ -8,7 +8,7 @@ import { CucumberExpressionGenerator, ParameterTypeRegistry, } from '@cucumber/cucumber-expressions' -import { reindent } from '../../../test/formatter_helpers' +import { reindent } from 'reindent-template-literals' function generateExpressions(text: string): readonly GeneratedExpression[] { const parameterTypeRegistry = new ParameterTypeRegistry() diff --git a/src/formatter/usage_formatter_spec.ts b/src/formatter/usage_formatter_spec.ts index 10a848e58..752611bce 100644 --- a/src/formatter/usage_formatter_spec.ts +++ b/src/formatter/usage_formatter_spec.ts @@ -3,7 +3,8 @@ import { expect } from 'chai' import FakeTimers, { InstalledClock } from '@sinonjs/fake-timers' import timeMethods from '../time' import { getUsageSupportCodeLibrary } from '../../test/fixtures/usage_steps' -import { testFormatter, reindent } from '../../test/formatter_helpers' +import { testFormatter } from '../../test/formatter_helpers' +import { reindent } from 'reindent-template-literals' describe('UsageFormatter', () => { let clock: InstalledClock diff --git a/test/formatter_helpers.ts b/test/formatter_helpers.ts index ed904099e..6b1005de4 100644 --- a/test/formatter_helpers.ts +++ b/test/formatter_helpers.ts @@ -173,44 +173,3 @@ export function normalizeSummaryDuration(output: string): string { '' ) } - -/** - * Removes the indentation of the given string based on the indentation of its first line. - * - * It removes the first line if it is empty so it is not needed for it to be escaped. - * - * @example - * { - * const reindented = reindent(` - * Expected output - * Expected indented output - * Another line - * `) - * - * const expected = `\ - * Expected output - * Expected indented output - * Another line - * ` - * expect(reindented).to.eql(expected) - * } - */ -export function reindent(original: string): string { - const lines = original.replace(/^\n/, '').split(`\n`) - const numberOfSpaceToRemove = lines[0].search(/\S|$/) - const spacesToRemove = Array(numberOfSpaceToRemove + 1).join(' ') - - return lines - .map((line, index) => { - if (index === lines.length - 1 && line.trim() === '') { - return '' - } - - if (line.startsWith(spacesToRemove)) { - return line.replace(spacesToRemove, '') - } - - return line - }) - .join('\n') -} diff --git a/yarn.lock b/yarn.lock index 4c39c0b43..969b81292 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3431,6 +3431,11 @@ regexpp@^3.0.0, regexpp@^3.1.0: resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.1.0.tgz#206d0ad0a5648cffbdb8ae46438f3dc51c9f78e2" integrity sha512-ZOIzd8yVsQQA7j8GCSlPGXwg5PfmA1mrq0JP4nGhh54LaKN3xdai/vHUDu74pKwV8OxseMS65u2NImosQcSD0Q== +reindent-template-literals@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/reindent-template-literals/-/reindent-template-literals-1.0.0.tgz#7c6fa92f825f342515ea6e1c9e0cbe2d8671ce3c" + integrity sha512-q5ttxEAkS6dWL3oGyQS5wnvwZc2zzOpE679uZLAS0izTk6c1LkC7S4q6ca6gq/hL2Cokw1ozInee/mkPsXW4WA== + release-zalgo@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/release-zalgo/-/release-zalgo-1.0.0.tgz#09700b7e5074329739330e535c5a90fb67851730"