Skip to content

Commit

Permalink
feat(sharedBehavior): adds possibility to pass function in
Browse files Browse the repository at this point in the history
`includeExamplesFor` and `itBehavesLike`
  • Loading branch information
stalniy committed Dec 10, 2020
1 parent 044c72f commit 3e2196f
Show file tree
Hide file tree
Showing 4 changed files with 52 additions and 15 deletions.
6 changes: 4 additions & 2 deletions interface.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@ interface GetLazyVar {
(name: string): any;
}

type AnyFunction = (...args: any[]) => any;

export const get: GetLazyVar;
export function def(name: string, implementation: () => any): void;
export function sharedExamplesFor(summary: string, implementation: (...vars: any[]) => void): void;
export function itBehavesLike(summary: string, ...vars: any[]): void;
export function includeExamplesFor(summary: string, ...vars: any[]): void;
export function itBehavesLike(summary: string | AnyFunction, ...vars: any[]): void;
export function includeExamplesFor(summary: string | AnyFunction, ...vars: any[]): void;
21 changes: 14 additions & 7 deletions lib/interface.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
const { Metadata } = require('./metadata');
const Variable = require('./variable');
const parseMessage = require('./parse_message');
const { parseMessage, humanize } = require('./parse_message');

module.exports = (context, tracker, options) => {
const get = (varName) => Variable.evaluate(varName, { in: tracker.currentContext });
Expand Down Expand Up @@ -46,14 +46,21 @@ module.exports = (context, tracker, options) => {
}

function includeExamplesFor(name, ...args) {
Metadata.ensureDefinedOn(tracker.currentlyDefinedSuite)
.runExamplesFor(name, args);
const meta = Metadata.ensureDefinedOn(tracker.currentlyDefinedSuite);

if (typeof name === 'function') {
name(...args);
} else {
meta.runExamplesFor(name, args);
}
}

function itBehavesLike(name, ...args) {
context.describe(`behaves like ${name}`, () => {
args.unshift(name);
includeExamplesFor.apply(tracker.currentlyDefinedSuite, args);
function itBehavesLike(...args) {
const nameOrFn = args[0];
const title = typeof nameOrFn === 'string' ? nameOrFn : humanize(fn.name || 'this');

context.describe(`behaves like ${title}`, () => {
includeExamplesFor(...args);
});
}

Expand Down
27 changes: 21 additions & 6 deletions lib/parse_message.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,31 @@
module.exports = function parseMessage(fn) {
function parseMessage(fn) {
const matches = fn.toString().match(/is\.expected\.(\s+(?=\.)|.)+/g);

if (!matches) {
return '';
}

const prefixLength = 'is.expected.'.length;
const body = matches.reduce((message, chunk) => message.concat(chunk.trim()
.slice(prefixLength)
.replace(/[\s.]+/g, ' ')
.replace(/([a-z])([A-Z])/g, (_, before, letter) => `${before} ${letter.toLowerCase()}`)
.replace(/ and /g, ', ')), []);
const body = matches.reduce((message, chunk) => {
const cleanChunk = chunk.trim()
.slice(prefixLength)
.replace(/[\s.]+/g, ' ');
const humanized = humanize(cleanChunk).replace(/ and /g, ', ');
message.push(humanized);
return message;
}, []);

return `is expected ${body.join(', ')}`;
};

function humanize(value) {
return value.replace(
/([a-z])([A-Z])/g,
(_, before, letter) => `${before} ${letter.toLowerCase()}`
);
}

module.exports = {
parseMessage,
humanize,
};
13 changes: 13 additions & 0 deletions spec/shared_behavior_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ describe('Shared behavior', function() {
var includeError;
var examples = spy();
var args = [{}, {}];
var fnDefinition = spy();

try {
includeExamplesFor('__non_existing')
Expand All @@ -28,6 +29,7 @@ describe('Shared behavior', function() {

sharedExamplesFor('__call', examples);
includeExamplesFor('__call', args[0], args[1]);
includeExamplesFor(fnDefinition, args[0]);

it('throws error when trying to include non-existing shared examples', function() {
expect(includeError.message).to.match(/not defined shared behavior/)
Expand All @@ -36,21 +38,32 @@ describe('Shared behavior', function() {
it('calls registered shared examples with specified arguments', function() {
expect(examples).to.have.been.called.with.exactly(args[0], args[1]);
});

it('accepts function as the 1st argument and call it', () => {
expect(fnDefinition).to.have.been.called.with.exactly(args[0]);
})
});

describe('`itBehavesLike`', function() {
var examples = spy();
var args = [{}, {}];
var spiedDescribe = spy.on(global, 'describe');
var fnBehavior = spy();

sharedExamplesFor('__Collection', examples);
itBehavesLike('__Collection', args[0], args[1]);
spy.restore(global, 'describe');

itBehavesLike(fnBehavior, args[0]);

it('includes examples in a nested context', function() {
expect(spiedDescribe).to.have.been.called.with('behaves like __Collection');
expect(examples).to.have.been.called.with.exactly(args[0], args[1]);
});

it('accepts behavior defined in function', function () {
expect(fnBehavior).to.have.been.called.with(args[0]);
});
});

describe('`sharedExamplesFor` scoping', function() {
Expand Down

0 comments on commit 3e2196f

Please sign in to comment.