Issue
I have the following page object model representing a widget in my app
/**
* Contains common actions for all widgets
*/
export default abstract class AbstractWidget {
private widgetId: number;
elements = {
widget: () => cy.getDataTestId(`widgetbox-container-${this.widgetId}`),
};
constructor(widgetId: number) {
this.widgetId = widgetId;
}
}
And I have a concrete class extending this class where I would like to append extra elements specific to this widget to the elements
property
import AbstractWidget from './AbstractWidget';
export default class SpecificWidget extends AbstractWidget {
elements = {
...super.elements,
assetSearch: () => cy.getDataTestId('assetSearch'),
};
constructor(widgetId: number) {
super(widgetId);
}
}
however, when I try to spread the elements from the abstract super class
elements = {
...super.elements,
assetSearch: () => cy.getDataTestId('assetSearch'),
};
it results in the typescript error
TS2340: Only public and protected methods of the base class are accessible via the super keyword.
What am I doing wrong?
Solution
This seems to be a result of this issue
Accessing property on super should be a type error #35314 Nov 24, 2019
If you follow the TS Playground link, it opens with the latest TS version and console.log(super.n)
shows an error. If you change the version to 3.7.5
the error goes away.
But if you look at the Handbook Overriding Methods, the equivalent access for methods is permitted, so the above change seems to have created an anomaly
class Base {
greet() {
console.log("Hello, world!");
}
}
class Derived extends Base {
greet(name?: string) {
if (name === undefined) {
super.greet();
} else {
console.log(`Hello, ${name.toUpperCase()}`);
}
}
}
Specifically in Cypress you can either use this.elements
instead of super.elements
, since the derived class inherits the base class element property:
Testing (with string return values instead of Chainers)
export default class SpecificWidget extends AbstractWidget {
elements = {
...this.elements,
assetSearch: () => 'assetSearch',
}
constructor(widgetId: number) {
super(widgetId)
}
}
export default abstract class AbstractWidget {
private widgetId: number
elements = {
widget: () => `widgetbox-container-${this.widgetId}`,
}
constructor(widgetId: number) {
this.widgetId = widgetId
}
}
const specific = new SpecificWidget(1)
expect(specific.elements.widget()).to.eq('widgetbox-container-1')
expect(specific.elements.assetSearch()).to.eq('assetSearch')
Or you can create a "helper method" in the base class
export default abstract class AbstractWidget {
private widgetId: number
elements = {
widget: () => `widgetbox-container-${this.widgetId}`,
}
getElements() {
return this.elements
}
constructor(widgetId: number) {
this.widgetId = widgetId
}
}
export default class SpecificWidget extends AbstractWidget {
elements = {
...super.getElements(),
assetSearch: () => 'assetSearch',
}
constructor(widgetId: number) {
super(widgetId)
}
}
An additional note, returning Chainers like this
widget: () => cy.getDataTestId(`widgetbox-container-${this.widgetId}`),
can result in unexpected problems if the returned Chainer gets invalidated due to page changes.
See Variables and aliases - Return values
Return Values
You cannot assign or work with the return values of any Cypress command. Commands are enqueued and run asynchronously.
It's ok if you intend to chain immediately with no page actions between calling widgit()
and using the result, but that means anyone using the class has to remember that caveat.
Cypress made a change to aliases to defaullt to type: query
specifically for this problem.
If an alias becomes stale when used, the chainer is re-run to get a fresh copy of the query result. But this won't happen automatically with your page-object methods.
Answered By - TesterDick
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.