Skip to content
Permalink
Browse files
fix(protractor): a Target's name can use the "{0}" token, same as the…
… locator

affects: serenity-js
  • Loading branch information
jan-molak committed May 13, 2017
1 parent 8d7edbd commit 6a0329100baf934f6f4762f85d033ea8ee1c929d
Showing 3 changed files with 24 additions and 41 deletions.
@@ -32,6 +32,12 @@ describe ('Target', () => {
expect(target.called('"Sign Up" button').toString()).to.equal('the "Sign Up" button');
});

it ('can have its name resolved at a later stage', () => {
const target = Target.the('"{0}" button').located(byLocator);

expect(target.of('Sign Up').toString()).to.equal('the "Sign Up" button');
});

it ('can have a CSS locator defined using tokens to be resolved at a later stage', () => {
const
byLocatorTemplate = webdriver.By.css('{0}#sign-up.{1}'),
@@ -86,12 +92,4 @@ describe ('Target', () => {
target.of('some-replacement');
}).to.throw('by.model("checkbox") is not a webdriver-compatible locator so you won\'t be able to use token replacement with it');
});

describe ('TargetBuilder', () => {
it ('is easy to identify', () => {
const target = Target.the('sign up form');

expect(target.toString()).to.equal('TargetBuilder for the sign up form');
});
});
});
@@ -1,14 +1,15 @@
import { ElementArrayFinder, ElementFinder } from 'protractor';
import { Locator } from 'protractor/built/locators';
import { describeAs } from '../../../serenity/recording/activity_description';

export class Target {

static the(name: string) {
return new TargetBuilder(name);
}
static the = (name: string) => ({
located: (byLocator: Locator): Target => new Target(name, byLocator),
})

of(...tokenReplacements: string[]): Target {
return new Target(this.name, new Interpolated(this.locator).with(tokenReplacements));
return new Target(describeAs(this.name, ...tokenReplacements), new Interpolated(this.locator).with(tokenReplacements));
}

called(newName: string): Target {
@@ -38,6 +39,9 @@ class Interpolated {

public with(tokenReplacements: string[]) {

const unescaped = template => template.replace(/\\{(\d+)\\}/, '{$1}');
const asString = (locator: Locator): string => unescaped(`${(locator as any).value}`);

// note: ProtractorBy is not compatible with WebdriverBy https://github.com/angular/protractor/issues/3508
if (! this.canBeInterpolated()) {
throw new Error(this.locator.toString() +
@@ -46,34 +50,10 @@ class Interpolated {

const cloned: any = Object.assign({}, this.locator);
cloned.__proto__ = Object.getPrototypeOf(this.locator);
cloned.value = this.interpolated((this.locator as any).value, tokenReplacements);
cloned.value = describeAs(asString(this.locator), ...tokenReplacements);

return cloned;
}

private canBeInterpolated(): boolean {
return !! (this.locator as any).value;
}

private interpolated(template: string, replacements: string[]) {
const
argToken = /\\?\{(\d+)\\?\}/g,
interpolator = (token: string, field: number) => replacements[ field ];

return template.replace(argToken, interpolator);
}
}

export class TargetBuilder {

constructor(private name: string) {
}

located(byLocator: Locator): Target {
return new Target(this.name, byLocator);
}

toString(): string {
return `TargetBuilder for the ${ this.name }`;
}
private canBeInterpolated = (): boolean => !! (this.locator as any).value;
}
@@ -13,9 +13,14 @@ export function describeAs(template: string, ...parameters: any[]): string {
const first: any = parameters[0];

switch (true) {
case parameters.length > 1: return interpolateArguments(template, parameters);
case isActor(first): return includeActorName(interpolateArguments(template, parameters), first);
default: return interpolateFields(template, first);
case parameters.length > 1 || typeof first !== 'object':
return interpolateArguments(template, parameters);

case isActor(first):
return includeActorName(interpolateArguments(template, parameters), first);

default:
return interpolateFields(template, first);
}
}

0 comments on commit 6a03291

Please sign in to comment.