Skip to content
This repository has been archived by the owner on Jun 19, 2022. It is now read-only.

Commit

Permalink
Use pickles to a larger degree
Browse files Browse the repository at this point in the history
This has some benefits, like that we no longer have to construct
examples from outlines ourselves and tags are correctly inherited and
applied.

This also happens to fix #196 and #237.
  • Loading branch information
badeball committed May 16, 2020
1 parent 155d732 commit e010dea
Show file tree
Hide file tree
Showing 9 changed files with 133 additions and 244 deletions.
90 changes: 14 additions & 76 deletions lib/createTestFromScenario.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,47 +7,35 @@ const {
} = require("./resolveStepDefinition");
const { generateCucumberJson } = require("./cukejson/generateCucumberJson");

const replaceParameterTags = (rowData, text) =>
Object.keys(rowData).reduce(
(value, key) => value.replace(new RegExp(`<${key}>`, "g"), rowData[key]),
text
);

// eslint-disable-next-line func-names
const stepTest = function(state, stepDetails, exampleRowData) {
const stepTest = function(state, stepDetails) {
cy.then(() => state.onStartStep(stepDetails))
.then(() =>
resolveAndRunStepDefinition.call(
this,
stepDetails,
replaceParameterTags,
exampleRowData,
state.feature.name
)
resolveAndRunStepDefinition.call(this, stepDetails, state.feature.name)
)
.then(() => state.onFinishStep(stepDetails, statuses.PASSED));
};

const runTest = (scenario, stepsToRun, rowData) => {
const indexedSteps = stepsToRun.map((step, index) =>
const runTest = pickle => {
const indexedSteps = pickle.steps.map((step, index) =>
Object.assign({}, step, { index })
);

// eslint-disable-next-line func-names
it(scenario.name, function() {
it(pickle.name, function() {
const state = window.testState;
return cy
.then(() => state.onStartScenario(scenario, indexedSteps))
.then(() => state.onStartScenario(pickle, indexedSteps))
.then(() =>
resolveAndRunBeforeHooks.call(this, scenario.tags, state.feature.name)
resolveAndRunBeforeHooks.call(this, pickle.tags, state.feature.name)
)
.then(() =>
indexedSteps.forEach(step => stepTest.call(this, state, step, rowData))
indexedSteps.forEach(step => stepTest.call(this, state, step))
)
.then(() =>
resolveAndRunAfterHooks.call(this, scenario.tags, state.feature.name)
resolveAndRunAfterHooks.call(this, pickle.tags, state.feature.name)
)
.then(() => state.onFinishScenario(scenario));
.then(() => state.onFinishScenario(pickle));
});
};

Expand All @@ -63,11 +51,7 @@ const writeCucumberJsonFile = json => {
cy.writeFile(outFile, json, { log: false });
};

const createTestFromScenarios = (
scenariosToRun,
backgroundSection,
testState
) => {
const createTestFromPickles = (pickles, testState) => {
// eslint-disable-next-line func-names, prefer-arrow-callback
before(function() {
cy.then(() => testState.onStartTest());
Expand All @@ -87,54 +71,8 @@ const createTestFromScenarios = (
Cypress.on("fail", failHandler);
});

scenariosToRun.forEach(section => {
if (section.examples) {
section.examples.forEach(example => {
const exampleValues = [];
const exampleLocations = [];

example.tableBody.forEach((row, rowIndex) => {
exampleLocations[rowIndex] = row.location;
example.tableHeader.cells.forEach((header, headerIndex) => {
exampleValues[rowIndex] = Object.assign(
{},
exampleValues[rowIndex],
{
[header.value]: row.cells[headerIndex].value
}
);
});
});

exampleValues.forEach((rowData, index) => {
// eslint-disable-next-line prefer-arrow-callback
const scenarioName = replaceParameterTags(rowData, section.name);
const uniqueScenarioName = `${scenarioName} (example #${index + 1})`;
const exampleSteps = section.steps.map(step => {
const newStep = Object.assign({}, step);
newStep.text = replaceParameterTags(rowData, newStep.text);
return newStep;
});

const stepsToRun = backgroundSection
? backgroundSection.steps.concat(exampleSteps)
: exampleSteps;

const scenarioExample = Object.assign({}, section, {
name: uniqueScenarioName,
example: exampleLocations[index]
});

runTest.call(this, scenarioExample, stepsToRun, rowData);
});
});
} else {
const stepsToRun = backgroundSection
? backgroundSection.steps.concat(section.steps)
: section.steps;

runTest.call(this, section, stepsToRun);
}
pickles.forEach(pickle => {
runTest.call(this, pickle);
});

// eslint-disable-next-line func-names, prefer-arrow-callback
Expand All @@ -149,5 +87,5 @@ const createTestFromScenarios = (
};

module.exports = {
createTestFromScenarios
createTestFromScenarios: createTestFromPickles
};
70 changes: 19 additions & 51 deletions lib/createTestsFromFeature.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,62 +2,30 @@ const { CucumberDataCollector } = require("./cukejson/cucumberDataCollector");
const { createTestFromScenarios } = require("./createTestFromScenario");
const { shouldProceedCurrentStep, getEnvTags } = require("./tagsHelper");

const createTestsFromFeature = (filePath, source, feature) => {
const flatten = collection =>
collection.reduce((acum, element) => [].concat(acum).concat(element));

const createTestsFromFeature = (filePath, source, feature, pickles) => {
const testState = new CucumberDataCollector(filePath, source, feature);
const featureTags = testState.feature.tags;
const hasEnvTags = !!getEnvTags();
const sectionsWithTags = testState.feature.children.filter(
section => section.tags && section.tags.length
);
const envTags = getEnvTags();

const sectionsWithTagsExist = sectionsWithTags.length > 0;
let tagFilter = null;

let everythingShouldRun = false;
let featureShouldRun = false;
let taggedScenarioShouldRun = false;
let anyFocused = false;
if (hasEnvTags) {
featureShouldRun = shouldProceedCurrentStep(featureTags);
taggedScenarioShouldRun = testState.feature.children.some(
section =>
section.tags &&
section.tags.length &&
shouldProceedCurrentStep(section.tags.concat(featureTags))
);
} else if (!sectionsWithTagsExist) {
everythingShouldRun = true;
} else {
anyFocused = sectionsWithTags.some(section =>
section.tags.find(t => t.name === "@focus")
);
if (anyFocused) {
taggedScenarioShouldRun = true;
} else {
everythingShouldRun = true;
}
}
const tagsUsedInTests = flatten(pickles.map(pickle => pickle.tags)).map(
tag => tag.name
);

// eslint-disable-next-line prefer-arrow-callback
if (everythingShouldRun || featureShouldRun || taggedScenarioShouldRun) {
const backgroundSection = testState.feature.children.find(
section => section.type === "Background"
);
const otherSections = testState.feature.children.filter(
section => section.type !== "Background"
);
const scenariosToRun = otherSections.filter(section => {
let shouldRun;
if (anyFocused) {
shouldRun = section.tags.find(t => t.name === "@focus");
} else {
shouldRun =
everythingShouldRun ||
shouldProceedCurrentStep(section.tags.concat(featureTags)); // Concat handles inheritance of tags from feature
}
return shouldRun;
});
createTestFromScenarios(scenariosToRun, backgroundSection, testState);
if (tagsUsedInTests.includes("@focus")) {
tagFilter = "@focus";
} else if (envTags) {
tagFilter = envTags;
}

const picklesToRun = pickles.filter(
pickle => !tagFilter || shouldProceedCurrentStep(pickle.tags, tagFilter)
);

createTestFromScenarios(picklesToRun, testState);
};

module.exports = {
Expand Down
34 changes: 18 additions & 16 deletions lib/cukejson/cucumberDataCollector.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ class CucumberDataCollector {
constructor(uri, source, feature) {
this.feature = feature;
this.scenarioSteps = {};
this.runScenarios = {};
this.runPickles = {};
this.runTests = {};
this.stepResults = {};
this.testError = null;
Expand Down Expand Up @@ -33,22 +33,22 @@ class CucumberDataCollector {
}
};

this.onStartScenario = (scenario, stepsToRun) => {
this.currentScenario = scenario;
this.onStartScenario = (pickle, stepsToRun) => {
this.currentScenario = pickle;
this.currentStep = 0;
this.stepResults = {};
this.scenarioSteps[scenario.name] = stepsToRun;
this.scenarioSteps[pickle.name] = stepsToRun;
this.testError = null;

stepsToRun.forEach(step => {
this.stepResults[step.index] = { status: statuses.PENDING };
});
this.runScenarios[scenario.name] = scenario;
this.runPickles[pickle.name] = pickle;
};

this.onFinishScenario = scenario => {
this.markStillPendingStepsAsSkipped(scenario);
this.recordScenarioResult(scenario);
this.onFinishScenario = pickle => {
this.markStillPendingStepsAsSkipped(pickle);
this.recordScenarioResult(pickle);
};

this.onStartStep = step => {
Expand Down Expand Up @@ -88,10 +88,12 @@ class CucumberDataCollector {
return duration;
};

this.formatTestCase = scenario => {
const line = scenario.example
? scenario.example.line
: scenario.location.line;
function last(collection) {
return collection[collection.length - 1];
}

this.formatTestCase = pickle => {
const { line } = last(pickle.locations);
return {
sourceLocation: { uri, line }
};
Expand All @@ -109,8 +111,8 @@ class CucumberDataCollector {
});
};

this.markStillPendingStepsAsSkipped = scenario => {
this.runTests[scenario.name] = Object.keys(this.stepResults).map(key => {
this.markStillPendingStepsAsSkipped = pickle => {
this.runTests[pickle.name] = Object.keys(this.stepResults).map(key => {
const result = this.stepResults[key];
return Object.assign({}, result, {
status:
Expand All @@ -120,8 +122,8 @@ class CucumberDataCollector {
});
});
};
this.recordScenarioResult = scenario => {
this.runTests[scenario.name].result = this.anyStepsHaveFailed(scenario)
this.recordScenarioResult = pickle => {
this.runTests[pickle.name].result = this.anyStepsHaveFailed(pickle)
? statuses.FAILED
: statuses.PASSED;
};
Expand Down
Loading

0 comments on commit e010dea

Please sign in to comment.