0

We are creating a library for frequently used functions in our Protractor/TypeScript project, and encountered a problem with scoping.

This is an excerpt from the TypeScript. Our problem occurs when we run the application and call for example clickBreadcrumb. The clickBreadcrumb function attempts to access the clickRepeaterElement.byName function.

export class Lib {
    public clickBreadcrumb = {
        byText(breadcrumbText: string) {
            // This statement fails. TypeError: Cannot read property 'byName' of undefined
            this.clickRepeaterElement.byName('bcItem in vm.breadCrumbs', breadcrumbText);
        }
    };

    public clickRepeaterElement = {
        byName(repeaterText:string, elementToClick:string, parentElement?:protractor.ElementFinder): void {
            // Click the elementToClick.
        },
        byIndex(repeaterText: string, index: number) {
            // Click element at the index.
        },
    };
}

WebStorm resolves clickRepeaterElement.byName, which essentially signals to us that this should work. But when we actually run the test, we get the following error:

TypeError: Cannot read property 'byName' of undefined

Coming from a C# background this was unexpected. How can we adjust the pattern so that this will resolve as we expect? Thanks for your help.

Ken Palmer
  • 2,117
  • 5
  • 30
  • 52

1 Answers1

2

Javascript has weird rules when it comes to this.

In your case this points to the byText function, not the class.

I would rewrite it this way:

export class Lib {
  public clickBreadcrumb = {
    byText: this.byText
  };

  public clickRepeaterElement = {
    byName: this.byName,
    byIndex: this.byIndex,
  };

  private byText(breadcrumbText: string) {
    // this is now Lib
    this.clickRepeaterElement.byName('bcItem in vm.breadCrumbs', breadcrumbText);
  }
  private byName(repeaterText: string, elementToClick: string, parentElement ? : protractor.ElementFinder): void {
    // Click the elementToClick.
  }
  private byIndex(repeaterText: string, index: number) {
    // Click element at the index.
  }

}

You can also use bind to make sure that the context of the methods have the correct value of this.

Update:

Regarding the multiple implementations question. I would propose you make use of the classes in TypeScript to structure the code a little bit differently.

export class Lib {
  public clickBreadcrumb = new Breadcrumb(this);

  public clickRepeaterElement = new Repeater(this);

}

export class Breadcrumb {
  constructor(private lib: Lib) {}

  public byText(breadcrumbText: string) {
    this.lib.clickRepeaterElement.byName('bcItem in vm.breadCrumbs', breadcrumbText);
  }

}

export class Repeater {
  constructor(private lib: Lib) {}

  public byName(repeaterText: string, elementToClick: string, parentElement ? : protractor.ElementFinder): void {
    // Click the elementToClick.
  }

  public byIndex(repeaterText: string, index: number) {
    // Click element at the index.
  }

  public byText(test: string) {
    // some other implementation
  }
}

You can also send smaller parts of the library in places, instead of sending Lib everywhere. It will also allow for better concern separation if/when your library grows.

toskv
  • 26,120
  • 7
  • 64
  • 66
  • I can not test that, let me know if it works or not. :) – toskv Dec 29 '15 at 15:32
  • Thanks! We're reviewing this now. – Ken Palmer Dec 29 '15 at 15:35
  • the this rules in javascript differ a lot from other languages, maybe this will help demystify it a bit http://stackoverflow.com/questions/3127429/how-does-the-this-keyword-work. – toskv Dec 29 '15 at 15:38
  • That's a great link. So with the approach you're suggesting, how would we employ the byText and byName and byIndex operations to other functions? We have multiple functions that need to use operations like byIndex, but their implementation would be totally different from the operations that clickRepeaterElement needs. – Ken Palmer Dec 29 '15 at 16:44
  • 1
    @KenPalmer check the update to the answer, I think that would be a lot easier to just structure the code in classes/functionalities. It allows for multiple implementations and makes it a lot clearer what this means. :) – toskv Dec 29 '15 at 17:02