Skip to content

Commit

Permalink
feat: add mechanism for reusable scenarios, #198
Browse files Browse the repository at this point in the history
  • Loading branch information
stanlee974 committed Aug 6, 2023
1 parent 19f153b commit 3927124
Show file tree
Hide file tree
Showing 32 changed files with 504 additions and 54 deletions.
71 changes: 71 additions & 0 deletions packages/docs/docs/03-wordings/03-reusable-scenarios.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';

# Reusable scenarios

:::caution
**Only available for developers**

Reusable scenarios cannot be used in conjunction with each other
:::

## Create file with extension .playbook.feature
File with this extension contains reusable scenarios.

```gherkin title='uuv/e2e/playbook/template.playbook.feature'
@Ignore //Only used with playwright
Feature: Template
Scenario: I go to town list
When I visit path "https://e2e-test-quest.github.io/weather-app/"
And Within a button named "Get started"
And I click
Scenario: I select douala
When I visit path "https://e2e-test-quest.github.io/weather-app/"
And Within a button named "Get started"
And I click
And I reset context
And Within a list named "Available Towns"
And Within a list item named "Douala"
And I click
And I reset context
```

## Create a file with extension .playbooked.feature
Files with this extension contains scenarios using reusable scenarios.

```gherkin title='uuv/e2e/playbook/weatherApp.playbooked.feature'
@Ignore //Only used with playwright
Feature: Feature using reusable scenarios
Scenario: vital check on first page
Given I go to town list
Then I should see a title named "Nothing to display"
Scenario: vital check on second page
Given I select douala
Then Within the element with aria-label "Weather of Douala"
And I should see a title named "Douala"
And I should see an element with content "min: 10.8 °c"
```

## Launch script to generate executable feature
this script replace playbook scenarios name by playbook scenarios steps.

<Tabs>
<TabItem value="Npm" label="Npm">

```shell
npx uuv playbook
```

</TabItem>
<TabItem value="Yarn" label="Yarn">

```shell
yarn uuv playbook
```

</TabItem>
</Tabs>
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';

# Scénarios réutilisables

:::caution
**Disponible seulement pour les développeurs**

Les scénarios réutilisables ne peuvent être utilisés entre eux.
:::

## Créer un fichier avec l'extension .playbook.feature
Les fichiers avec cette extension contiennent des scénarios réutilisables

```gherkin title='uuv/e2e/playbook/template.playbook.feature'
@Ignore //Seulement utile avec playwright
#language: fr
Fonctionnalité: Template
Scénario: j'aille à la liste des villes
Lorsque je visite l'Url "https://e2e-test-quest.github.io/weather-app/"
Et je vais à l'intérieur de bouton nommé "Get started"
Et je clique
Scénario: je sélectionne douala
Lorsque je visite l'Url "https://e2e-test-quest.github.io/weather-app/"
Et je vais à l'intérieur de bouton nommé "Get started"
Et je clique
Et je reinitialise le contexte
Et je vais à l'intérieur de liste nommée "Available Towns"
Et je vais à l'intérieur de élément de liste nommé "Douala"
Et je clique
Et je reinitialise le contexte
```

## Créer un fichier avec l'extension .playbooked.feature
Les fichiers avec cet extension contiennent des scénarios utilisant les scénarios réutilisables

```gherkin title='uuv/e2e/playbook/weatherApp.playbooked.feature'
@Ignore //Seulement utile avec playwright
#language: fr
Fonctionnalité: Fonctionnalité utilisant les scénarios réutilisables
Scénario: vérification vital de la première page
Etant donné que j'aille à la liste des villes
Alors je dois voir un titre nommé "Nothing to display"
Scénario: vérification vital de la seconde page
Etant donné que je sélectionne douala
Lorsque je vais à l'intérieur de l'élément ayant pour aria-label "Weather of Douala"
Alors je dois voir un titre nommé "Douala"
Et je dois voir un élément qui contient "min: 10.8 °c"
```

## Lancement du script de génération de feature éxecutable
Ce script remplace les noms de scénarios des playbooks par les étapes de scénarios des playbooks.

<Tabs>
<TabItem value="Npm" label="Npm">

```shell
npx uuv playbook
```

</TabItem>
<TabItem value="Yarn" label="Yarn">

```shell
yarn uuv playbook
```

</TabItem>
</Tabs>
1 change: 1 addition & 0 deletions packages/runner-commons/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
export * from "./step-definition-generator/common";
export * from "./step-definition-generator/generate-step-definitions";
export * from "./step-definition-generator/generate-step-definitions-documentation";
export * from "./step-definition-generator/generate-playbook-step-definitions";
export * from "./runner/step-definitions/_constant";
export * from "./runner/step-definitions/_i-context";
export * from "./runner/formatter/uuv-custom-formatter";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,12 @@ export class UuvCustomFormatter extends Formatter {
constructor() {
super();
}

override async parseCucumberJson(sourceFile, outputFile) {
console.info(`Start formatting file '${sourceFile}' into '${outputFile}'`);
const report = await this.helper.readFileIntoJson(sourceFile);
const gherkinDocumentJson = this.helper.getJsonFromArray(report, "gherkinDocument");
const cucumberReport = [];
const cucumberReport: Json[] = [];
gherkinDocumentJson.forEach(gherkinJson => {
let gherkinDocument;
try {
Expand All @@ -31,7 +32,7 @@ export class UuvCustomFormatter extends Formatter {
this.buildAndAddScenario(featureChild, report, background, feature, scenariosJson, undefined);
}
});
const rootJson = {
const rootJson: Json = {
comments: this.getComments(gherkinDocument.comments),
description: gherkinDocument.feature.description,
elements: scenariosJson,
Expand All @@ -42,7 +43,6 @@ export class UuvCustomFormatter extends Formatter {
uri: gherkinDocument.uri,
tags: this.getTags(gherkinDocument.feature.tags)
};
// @ts-ignore
cucumberReport.push(rootJson);
});
await this.validateReportSchema(report);
Expand All @@ -51,14 +51,13 @@ export class UuvCustomFormatter extends Formatter {
this.helper.writeFile(outputFile, reportString);
}

private buildAndAddScenario(child, report: string[], background: {}, feature, scenariosJson: any[], rule) {
let steps = [];
private buildAndAddScenario(child, report: string[], background: object, feature, scenariosJson: any[], rule) {
let steps: any[] = [];
let stepJson = {};
// Background
if (child.scenario === undefined) {
child.background.steps.forEach(step => {
stepJson = this.createStepJson(step, report, 0);
// @ts-ignore
steps.push(stepJson);
});
background = this.createScenarioJson(feature, child.background, steps, "background");
Expand All @@ -68,46 +67,40 @@ export class UuvCustomFormatter extends Formatter {
else if (!child.scenario.keyword.includes("Outline")) {
child.scenario.steps.forEach(step => {
stepJson = this.createStepJson(step, report, 0);
// @ts-ignore
steps.push(stepJson);
});
const scenario = this.createScenarioJson(feature, child.scenario, steps, "scenario");
if (rule) {
scenario.id = `${feature.name};${rule.name};${scenario.name}`;
}
if (Object.keys(background).length !== 0 && background !== undefined) {
// @ts-ignore
scenariosJson.push(background);
}
// @ts-ignore
scenariosJson.push(scenario);
}
// Scenario Outline
else if (child.scenario.examples[0].tableBody !== undefined) {
} /* Scenario Outline */ else if (child.scenario.examples[0].tableBody !== undefined) {
const numberOfExecutions = child.scenario.examples[0].tableBody.length;
const numberOfStepsEachExecution = child.scenario.steps.length;
let scenarioIndex = 0;
while (scenarioIndex < numberOfExecutions) {
let currentStep = 0;
steps = [];
while (currentStep < numberOfStepsEachExecution) {
stepJson = this.createStepJson(child.scenario.steps[currentStep], report, scenarioIndex);
currentStep++;
// @ts-ignore
steps.push(stepJson);
}
const scenario = this.createScenarioJson(feature, child.scenario, steps, "scenario", scenarioIndex);
if (rule) {
scenario.id = `${feature.name};${rule.name};${scenario.name}`;
}
if (Object.keys(background).length !== 0 && background !== undefined) {
// @ts-ignore
scenariosJson.push(background);
}
// @ts-ignore
scenariosJson.push(scenario);
scenarioIndex++;
const numberOfStepsEachExecution = child.scenario.steps.length;
let scenarioIndex = 0;
while (scenarioIndex < numberOfExecutions) {
let currentStep = 0;
steps = [];
while (currentStep < numberOfStepsEachExecution) {
stepJson = this.createStepJson(child.scenario.steps[currentStep], report, scenarioIndex);
currentStep++;
steps.push(stepJson);
}
const scenario = this.createScenarioJson(feature, child.scenario, steps, "scenario", scenarioIndex);
if (rule) {
scenario.id = `${feature.name};${rule.name};${scenario.name}`;
}
if (Object.keys(background).length !== 0 && background !== undefined) {
scenariosJson.push(background);
}
scenariosJson.push(scenario);
scenarioIndex++;
}
}
}
}

interface Json { comments: any[] | undefined; line: any; elements: any[]; name: any; description: any; id: any; keyword: any; uri: any; tags: any[] | undefined}
27 changes: 26 additions & 1 deletion packages/runner-commons/src/step-definition-generator/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
*/

import fs from "fs";
import glob from "glob";

export { fs };

Expand Down Expand Up @@ -43,7 +44,14 @@ export enum TEST_RUNNER_ENUM {

export enum STEP_DEFINITION_FILE_NAME {
BASE = "base-check-engine",
BY_ROLE = "based-role-check-engine"
BY_ROLE = "based-role-check-engine",
BY_SCENARIO_TEMPLATE = "_playbook-engine",
}

export enum UUV_ENVELOPE {
PLAYBOOK = "playbook.feature",
PLAYBOOKED = "playbooked.feature",
PLAYBOOKED_GEN = "playbooked.generated.feature",
}

export enum KEY_PRESS {
Expand Down Expand Up @@ -98,4 +106,21 @@ export class Common {
`[WRITE] ${generatedFile} written successfully`
);
}

static getFileList(dirName) {
let files : string[] = [];
const items = fs.readdirSync(dirName, { withFileTypes: true });
for (const item of items) {
if (item.isDirectory()) {
files = [
...files,
...(Common.getFileList(`${dirName}/${item.name}`)),
];
} else {
files.push(`${dirName}/${item.name}`);
}
}

return files;
}
}
Loading

0 comments on commit 3927124

Please sign in to comment.