Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat!: update cucumber format parsing #284

Merged
merged 24 commits into from
Nov 25, 2024
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 50 additions & 9 deletions src/cucumber-runner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,27 @@ import type { CucumberRunnerConfig } from './types';
import * as utils from './utils';
import { NodeContext } from 'sauce-testrunner-utils/lib/types';

function buildArgs(runCfg: CucumberRunnerConfig, cucumberBin: string) {
export function buildArgs(runCfg: CucumberRunnerConfig, cucumberBin: string) {
const paths: string[] = [];
runCfg.suite.options.paths.forEach((p) => {
paths.push(path.join(runCfg.projectPath, p));
});
const procArgs = [
cucumberBin,
...paths,
'--publish-quiet', // Deprecated in 9.4.0. Will be removed in 11.0.0 or later.
'--force-exit',
'--require-module',
'ts-node/register',
// NOTE: The Cucumber formatter (--format) setting uses the "type":"path" format.
// If the "path" is not provided, the output defaults to stdout.
// Cucumber supports only one stdout formatter; if multiple are specified,
// it will prioritize the last one listed.
// To ensure the Sauce test report file is always generated and not overridden
// by a user-specified stdout formatter, set the output to a file using the --format option
// and configure the --format-options flag to specify the outputFile.
// Both settings must be properly configured to reliably generate the file.
'--format',
'@saucelabs/cucumber-reporter',
'"@saucelabs/cucumber-reporter":"sauce-test-report.json"',
'--format-options',
JSON.stringify(buildFormatOption(runCfg)),
];
Expand Down Expand Up @@ -50,15 +57,12 @@ function buildArgs(runCfg: CucumberRunnerConfig, cucumberBin: string) {
procArgs.push('-t');
procArgs.push(tag);
});

runCfg.suite.options.format?.forEach((format) => {
procArgs.push('--format');
const opts = format.split(':');
if (opts.length === 2) {
procArgs.push(`${opts[0]}:${path.join(runCfg.assetsDir, opts[1])}`);
} else {
procArgs.push(format);
}
procArgs.push(normalizeFormat(format, runCfg.assetsDir));
});

if (runCfg.suite.options.parallel) {
procArgs.push('--parallel');
procArgs.push(runCfg.suite.options.parallel.toString(10));
Expand All @@ -67,6 +71,43 @@ function buildArgs(runCfg: CucumberRunnerConfig, cucumberBin: string) {
return procArgs;
}

/**
* Normalizes a Cucumber-js format string.
*
* For structured inputs (`key:value` or `"key:value"`), returns a string in the
* form `"key":"value"`, with the asset directory prepended to relative paths.
* For simple inputs (e.g., `usage`), returns the input as-is.
*
* @param {string} format - The input format string. Examples include:
* - `"key:value"`
* - `"key":"value"`
* - `key:value`
* - `usage`
* @param {string} assetDir - The directory to prepend to the value for relative paths.
* @returns {string} The normalized format string. For structured inputs, it returns
* a string in the form `"key":"value"`. For simple inputs, it
* returns the input unchanged.
*
* Example:
* - Input: `"html":"formatter/report.html"`, `"/project/assets"`
* Output: `"html":"/project/assets/formatter/report.html"`
* - Input: `"usage"`, `"/project/assets"`
* Output: `"usage"`
*/
export function normalizeFormat(format: string, assetDir: string): string {
// Checks if the format is structured; if not, returns it unchanged.
const match = format.match(/^"?([^:]+):"?([^"]+)"?$/);
tianfeng92 marked this conversation as resolved.
Show resolved Hide resolved
if (!match) {
return format;
}

let [, key, value] = match;
key = key.replaceAll('"', '');
value = value.replaceAll('"', '');
const updatedPath = path.join(assetDir, value);
return `"${key}":"${updatedPath}"`;
}

export async function runCucumber(
nodeBin: string,
runCfg: CucumberRunnerConfig,
Expand Down
61 changes: 61 additions & 0 deletions tests/unit/src/cucumber-runner.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
const { buildArgs, normalizeFormat } = require('../../../src/cucumber-runner');

describe('buildArgs', () => {
const cucumberBin = '/usr/local/bin/cucumber';

it('should build correct arguments with basic configuration', () => {
const runCfg = {
sauce: {
metadata: {},
},
projectPath: '/project',
assetsDir: '/project/assets',
suite: {
options: {
paths: ['features/test.feature'],
},
},
};

const result = buildArgs(runCfg, cucumberBin);

expect(result).toEqual([
cucumberBin,
'/project/features/test.feature',
'--force-exit',
'--require-module',
'ts-node/register',
'--format',
'"@saucelabs/cucumber-reporter":"sauce-test-report.json"',
'--format-options',
'{"upload":false,"outputFile":"/project/assets/sauce-test-report.json"}',
]);
});
});

describe('normalizeFormat', () => {
const assetDir = '/project/assets';

it('should normalize formats with both quoted format type and path', () => {
expect(normalizeFormat(`"html":"formatter/report.html"`, assetDir)).toBe(
`"html":"/project/assets/formatter/report.html"`,
);
});

it('should normalize formats with only one pair of quote', () => {
expect(normalizeFormat(`"html:formatter/report.html"`, assetDir)).toBe(
`"html":"/project/assets/formatter/report.html"`,
);
});

it('should normalize formats with no quotes', () => {
expect(normalizeFormat(`html:formatter/report.html`, assetDir)).toBe(
`"html":"/project/assets/formatter/report.html"`,
);
});

it('should return simple strings as-is', () => {
expect(normalizeFormat(`"usage"`, assetDir)).toBe('"usage"');
expect(normalizeFormat(`usage`, assetDir)).toBe('usage');
});
});
Loading