Skip to content

Commit

Permalink
Added functionalities for tags
Browse files Browse the repository at this point in the history
  • Loading branch information
szikszail committed Jan 4, 2018
1 parent 41f5a7e commit 4b2d288
Show file tree
Hide file tree
Showing 4 changed files with 233 additions and 2 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@
**/dist
.idea
*.log
*.*~
*.*~
package-lock.json
62 changes: 62 additions & 0 deletions lib/builtIn/RemoveDuplicates.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,72 @@
'use strict';

const DefaultConfig = require('../DefaultConfig');
const {Tag} = require('gherkin-assembler').AST;

/**
* @typedef {Object} RemoveDuplicatesConfiguration
* @property {boolean} processTags
* @property {boolean} processRows
* @property {boolean} verbose
*/
const DEFAULT_CONFIG = {
processTags: true,
processRows: false,
verbose: true
};

const removeDuplicates = array => Array.from(new Set(array));

class RemoveDuplicates extends DefaultConfig {
constructor(config) {
super();
this.config = Object.assign({}, DEFAULT_CONFIG, config || {});

this.logTag = this._getLogger(this.config.processTags);
this.logRow = this._getLogger(this.config.processRows);
}

_getLogger(enabled) {
return this.config.verbose ? (...args) => console[enabled ? 'log' : 'warn'].apply(console, args) : () => null;
}

_hasTag(element, tagName) {
if (!element.tags || !element.tags.length) {
return false;
}
return element.tags.some(tag => tag.name === tagName);
}

_filterTags(element, parent) {
if (element.tags && element.tags.length) {
const ownTags = element.tags.filter(tag => {
if (this._hasTag(parent, tag.name)) {
this.logTag(`The ${tag.name} presents on feature too on "${element.keyword} ${element.name}" in "${parent.keyword} ${parent.name}"`);
return false;
}
return true;
});
const tagNames = ownTags.map(tag => tag.name);
const uniqueTagNames = removeDuplicates(tagNames);
if (tagNames.length !== uniqueTagNames) {
uniqueTagNames.filter(tag => {
return tagNames.reduce((n, name) => n + (name === tag ? 1 : 0), 0) > 1;
}).forEach(tag => {
this.logTag(`The ${tag} presents multiple times on "${element.keyword} ${element.name}" in "${parent.keyword} ${parent.name}"`);
});
}
if (this.config.processTags) {
element.tags = uniqueTagNames.map(tag => new Tag(tag));
}
}
}

onScenario(scenario, parent) {
this._filterTags(scenario, parent);
}

onScenarioOutline(scenarioOutline, parent) {
this._filterTags(scenarioOutline, parent);
}
}

Expand Down
169 changes: 169 additions & 0 deletions test/builtIn/RemoveDuplicates.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
'use strict';

const {resolve} = require('path');
const {Tag} = require('gherkin-assembler').AST;
const RemoveDuplicates = require(resolve('lib/builtIn/RemoveDuplicates.js'));
const API = require(resolve('lib'));
const expect = require('chai').expect;
const sinon = require('sinon');

describe('builtIn.RemoveDuplicates', () => {
it('should be available through API', () => {
expect(API.builtIn.RemoveDuplicates).to.equal(RemoveDuplicates);
});

beforeEach(() => {
sinon.spy(console, 'log');
sinon.spy(console, 'warn');
});

afterEach(() => {
console.log.restore();
console.warn.restore();
});

describe('tag inheritance', () => {
it('should identify if a tag present on parent', () => {
const compiler = new RemoveDuplicates();
expect(compiler._hasTag({}, '@noTag'), 'identify if no tags field').to.be.false;
expect(compiler._hasTag({tags: []}, '@noTag'), 'identify if no tags at all').to.be.false;
expect(compiler._hasTag({tags: [new Tag('@found')]}, '@found'), 'identify if tag found').to.be.true;
expect(compiler._hasTag({tags: [new Tag('@notFound')]}, '@noTag'), 'identify if tag not found').to.be.false;
});

it('should remove tag if presents on parent', () => {
const compiler = new RemoveDuplicates();
const element = {tags: [new Tag('@onFeature')]};
const parent = {tags: [new Tag('@onFeature')]};

compiler._filterTags(element, parent);
expect(element.tags).to.have.length(0);
expect(parent.tags).to.have.length(1);
});

it('should not remove unique tag if not present on parent', () => {
const compiler = new RemoveDuplicates();
const element = {tags: [new Tag('@onScenario')]};
const parent = {tags: [new Tag('@onFeature')]};

compiler._filterTags(element, parent);
expect(element.tags).to.have.length(1);
expect(element.tags[0].name).to.equal('@onScenario');
});

describe('logging', () => {
it('should display warning if tag found on parent, but won\'t be removed', () => {
const compiler = new RemoveDuplicates({
processTags: false
});
const element = {tags: [new Tag('@onFeature')]};
const parent = {tags: [new Tag('@onFeature')]};

compiler._filterTags(element, parent);
expect(console.log.called, 'console.log called instead of console.warn').to.be.false;
expect(console.warn.called, 'console.warn is not called').to.be.true;
});

it('should display info message if tag found on parent and will be removed', () => {
const compiler = new RemoveDuplicates();
const element = {tags: [new Tag('@onFeature')]};
const parent = {tags: [new Tag('@onFeature')]};

compiler._filterTags(element, parent);
expect(console.warn.called, 'console.warn called instead of console.log').to.be.false;
expect(console.log.called, 'console.log is not called').to.be.true;
});

it('should not display anything if verbose turned off even if tag found on parent', () => {
const compiler = new RemoveDuplicates({
processTags: false,
verbose: false
});
const element = {tags: [new Tag('@onFeature')]};
const parent = {tags: [new Tag('@onFeature')]};

compiler._filterTags(element, parent);
expect(console.log.called, 'console.log is called').to.be.false;
expect(console.warn.called, 'console.warn is called').to.be.false;
})
});
});

describe('duplicate tags', () => {
it('should remove tag if it presents multiple times', () => {
const compiler = new RemoveDuplicates();
const element = {tags: [new Tag('@duplicate'), new Tag('@duplicate')]};
const parent = {tags: []};

compiler._filterTags(element, parent);
expect(element.tags).to.have.length(1);
expect(element.tags[0].name).to.equal('@duplicate');
});

it('should not remove unique tags', () => {
const compiler = new RemoveDuplicates();
const element = {tags: [new Tag('@duplicate'), new Tag('@notDuplicate')]};
const parent = {tags: []};

compiler._filterTags(element, parent);
expect(element.tags).to.have.length(2);
expect(element.tags[0].name).to.equal('@duplicate');
expect(element.tags[1].name).to.equal('@notDuplicate');
});

describe('logging', () => {
it('should display warning if tag found multiple times, but won\'t be removed', () => {
const compiler = new RemoveDuplicates({
processTags: false
});
const element = {tags: [new Tag('@duplicate'), new Tag('@duplicate')]};
const parent = {tags: []};

compiler._filterTags(element, parent);
expect(console.log.called, 'console.log called instead of console.warn').to.be.false;
expect(console.warn.called, 'console.warn is not called').to.be.true;
});

it('should display info message if tag found multiple times and will be remove', () => {
const compiler = new RemoveDuplicates();
const element = {tags: [new Tag('@duplicate'), new Tag('@duplicate')]};
const parent = {tags: []};

compiler._filterTags(element, parent);
expect(console.warn.called, 'console.warn called instead of console.log').to.be.false;
expect(console.log.called, 'console.log is not called').to.be.true;
});

it('should not display any message if verbose mode turned off even if duplicate tag found', () => {
const compiler = new RemoveDuplicates({
verbose: false
});
const element = {tags: [new Tag('@duplicate'), new Tag('@duplicate')]};
const parent = {tags: []};

compiler._filterTags(element, parent);
expect(console.log.called, 'console.log is called').to.be.false;
expect(console.warn.called, 'console.warn is called').to.be.false;
})
});
});

describe('combined usage of tags', () => {
it('should remove tags either duplicated or inherited too', () => {
const compiler = new RemoveDuplicates();

const parent = {tags: [new Tag('@inherited')]};
const element = {tags: [
new Tag('@duplicate'),
new Tag('@duplicate'),
new Tag('@notDuplicate'),
new Tag('@inherited')
]};

compiler._filterTags(element, parent);
expect(element.tags).to.have.length(2);
expect(element.tags[0].name).to.equal('@duplicate');
expect(element.tags[1].name).to.equal('@notDuplicate');
});
});
});
1 change: 0 additions & 1 deletion test/builtIn/Replacer.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ const expect = require('chai').expect;
const CONFIG = require('../data/config/replacer.json');
const API = require(path.resolve('lib'));


describe('builtIn.Replacer', () => {
it('should be available through API', () => {
expect(API.builtIn.Replacer).to.equal(Replacer);
Expand Down

0 comments on commit 4b2d288

Please sign in to comment.