How to call a customhelper in CodeCeptjs? - protractor

How to call a method defined in customhelper?
MycustomHelper
const { Helper } = codeceptjs;
const chai = require('chai');
const chaiAsPromised = require('chai-as-promised');
chai.use(chaiAsPromised);
const expect = chai.expect;
class MyHelper extends Helper {
async JavaScriptclick(LocatorVale) {
await browser.waitForAngularEnabled(true);
browser.executeScript("arguments[0].click();", element(by.xpath(LocatorVale)));
}
}
module.exports = MyHelper;
Code.js
helpers: {
MyHelper: {
require: './myhelper_helper.js',
},
Stepfile.js
Scenario('Add a new user group', (I, MyHelper) => {
MyHelper.JavaScriptclick(‘.badged-button.mat-raised-button.mat-primar')
});
If I execute the above code, I get the below error
Add a new user group:
Object of type MyHelper is not defined in container
Please help me to resolve this issue. I need to click on a button, but I.click is not able to locate the button. So I had to use javascriptclick in this case. However I.executescript is also not working. Hence I need to use native protractor commands as it works only this way.

There are no need to call helper by it's name.
All methods are added in actor (I) object
As said in documentation:
Methods of Helper class will be available in tests in I object. This abstracts test scenarios from the implementation and allows easily switching between backends.
https://codecept.io/helpers/#extending-codeceptjs-with-custom-helopers
So, in test:
Scenario('Add a new user group', (I) => {
I.JavaScriptclick(‘.badged-button.mat-raised-button.mat-primar')
});

Related

How can I mock aws-sdk with jest?

I am trying to mock aws-sdk with jest. Actually I only care about one function. How can I do this? I have read the docs about mocking classes with jest, but the docs are complicated and I don't quite understand them.
Here is my best attempt:
handler.test.js
'use strict';
const aws = require('aws-sdk');
const { handler } = require('../../src/rotateSecret/index');
jest.mock('aws-sdk');
const event = {
SecretId: 'test',
ClientRequestToken: 'ccc',
Step: 'createSecret',
};
describe('rotateSecret', () => {
it.only('should not get or put a secret', async () => {
aws.SecretsManager.mockImplementation(() => ({
getSecretValue: () => ({}),
}));
expect.assertions(1);
await handler(event);
// You can see what I am trying to do here but it doesn't work
expect(aws.SecretsManager.getSecretManager).not.toHaveBeenCalled();
});
});
handler.js
exports.handler = async (event) => {
const secretsManager = new aws.SecretsManager();
const secret = await secretsManager.describeSecret({ SecretId: event.SecretId }).promise();
if (someCondition) {
console.log("All conditions not met");
return;
}
return secretsManager.getSecretValue(someParams)
};
Okay so the way I would approach this is as follows:
AWS-SDK Mock
Create an actual mock for aws-sdk and put it in __mocks__/aws-sdk.js file at the root of your project
// __mocks__/aws-sdk.js
class AWS {
static SecretsManager = class {
describeSecret = jest.fn(() =>{
return { promise: ()=> Promise.resolve({ ARN: "custom-arn1", Name: "describeSec" })}
});
getSecretValue = jest.fn(() =>{
return {promise: ()=> Promise.resolve({ ARN: "custom-arn2", Name: "getSecretVal" })
});
};
}
module.exports = AWS;
I have used static before SecretsManager because AWS class is never instantiated yet it wants access to SecretsManager class.
Inside SecretsManager, I have defined 2 functions and stubbed them using jest.fn.
Now same stuff as you have done in your test file:
jest.mock('aws-sdk');
How to Test
To test if your mock functions are called, thats the tricky part (so i will detail that at the end of this post).
Better approach would be to assert against the end result of your main function after all processing is finished.
Assertions
Back in your test file, I would simply invoke the handler with the await (as you already have) and then assert against the final result like so:
// test.js
describe("rotateSecret", () => {
it.only("should not get or put a secret", async () => {
const event = {name:"event"};
const result = await handler(event);
expect(result).toEqual("whatever-your-function-is-expected-to-return");
});
});
Testing Secret Manager's function invocations
For this you will need to tweak your main handler.js file itself and will need to take out invocation of secrets Manager from the main function body like so:
const secretsManager = new aws.SecretsManager(); // <---- Declare it in outer scope
exports.handler = async (event) => {
const secret = await secretsManager
.describeSecret({ SecretId: event.SecretId })
.promise();
if (someCondition) {
console.log("All conditions not met");
return;
}
return secretsManager.getSecretValue(someParams);
};
Then back in your test.js file, you will need to similarly declare the SecretsManager invocation before you initiate your handler function like so:
//test.js
describe("rotateSecret", () => {
const secretsManager = new aws.SecretsManager(); // <---- Declare it in outer scope
it.only("should not get or put a secret", async () => {
const event = {name:"event"};
await handler(event);
// Now you can make assertions on function invocations
expect(secretsManager.describeSecret).toHaveBeenCalled();
// OR check if passed args were correct
expect(secretsManager.describeSecret).toHaveBeenCalledWith({
SecretId: event.SecretId,
});
});
});
This will allow you to make assertions on function invocation as well the args that were passed.
The reason I declare it outside function scope is to tell Jest that secretsManager should be existing somewhere in global scope and it should be used from there.
Previously, we had it declared inside the function scope, so Jest would invoke it but we weren't able to get access to it.
We couldn't directly reference it like this AWS.SecretsManager.getSecretManager because getSecretManager method is only available after you instantiate the SecretsManager class (and even if you did that, you will get a new instance of the class which won't help with any assertions).
Downside of __mocks__/aws.js fake module
Obvious issue is - you are stubbing the function on every single call and maybe you won't want that.
Perhaps you only want to stub it out once for a specific test but for the rest of them you want it to run normal.
In that case, you should not create __mocks__ folder.
Instead, create a one-time fake BUT make sure your SecretsManager invocation is in the outside scope in your test file as before.
//test.js
const aws = require("aws-sdk");
describe("rotateSecret", () => {
// Declare it in outer scope
const secretsManager = new aws.SecretsManager();
it.only("should not get or put a secret", async () => {
const event = {name:"event"};
// Create a mock for this instance ONLY
secretsManager.describeSecret = jest.fn().mockImplementationOnce(()=>Promise.resolve("fake-values"));
await handler(event);
expect(secretsManager.describeSecret).toHaveBeenCalled();
expect(secretsManager.describeSecret).toHaveBeenCalledWith({
SecretId: event.SecretId,
});
});
});

How to integrate Api Gateway and Step Fucnctions in CDK?

I have a statemachine.
const task1 = new sfn.Task(this, 'Assign Case', {
task: new tasks.InvokeFunction(Lambda1),
});
const task2 = new sfn.Task(this, 'Close Case', {
task: new tasks.InvokeFunction(Lambda2),
});
const chain = sfn.Chain.start(task1)
.next(task2);
const StateMachine = new sfn.StateMachine(this, `StateMachine`, {
definition: chain
});
And I need to call this statemachine from Api-gateway resource.I have used the below code and it throws an error like 'statemacine is not assignable to paramaeter of type AwsIntegrationProps'
const resource = this.api.root.addResource(path);
resource.addMethod(method, new apigw.AwsIntegration(handler), { apiKeyRequired: true });
//handler is above statemachine
My api gateway integration request looks like this in console.
You should use apigateway.LambdaIntegration which extends AwsIntegration.
export declare class LambdaIntegration extends AwsIntegration {
private readonly handler;
private readonly enableTest;
constructor(handler: lambda.IFunction, options?: LambdaIntegrationOptions);
bind(method: Method): void;
}
For example :
const getBookIntegration = new apigateway.LambdaIntegration(getBookHandler);
Later, use the lambdaIntegration when creating a new method:
book.addMethod('GET', getBookIntegration);
More about LambdaIntegration.
The error 'statemacine is not assignable to paramaeter of type AwsIntegrationProps' is referring to you instantiation.
The AwsIntegration class takes an AwsIntegrationProps struct as input.
new AwsIntegration(props: AwsIntegrationProps)
Getting API Gateway to kickoff Step Functions directly is a little strange. I found this Creating a Step Functions API Using API Gateway tutorial helpful. The State Machine ARN is passed in the request body of the call, so you need to pass a request template if you don't want to require the user to specify the State Machine.
resource.addMethod(
method,
new apigw.AwsIntegration({
handler: 'states',
action: 'StartExecution',
options: {
requestTemplates: {
'application/json': `{
"stateMachineArn": "${handler.ref}",
"input": "$util.escapeJavaScript($input.body)"
}`
},
},
}),
{ apiKeyRequired: true }
);
(Note: I'm translating my code from Python so I'm not 100% on the strings in TypeScript.)
I also filled in credentialsRole, passthroughBehavior, and integrationResponses to the options to get mine setup the way I wanted.
check here:
api to state
and here:
state machine

What's the correct Protractor's syntax for Page Objects?

I've come across different types of syntax for Protractor's Page Objects and I was wondering, what's their background and which way is suggested.
This is the official PageObject syntax from Protractor's tutorial. I like it the most, because it's clear and readable:
use strict;
var AngularHomepage = function() {
var nameInput = element(by.model('yourName'));
var greeting = element(by.binding('yourName'));
this.get = function() {
browser.get('http://www.angularjs.org');
};
this.setName = function(name) {
nameInput.sendKeys(name);
};
this.getGreeting = function() {
return greeting.getText();
};
};
module.exports = AngularHomepage;
However, I've also found this kind:
'use strict';
var AngularPage = function () {
browser.get('http://www.angularjs.org');
};
AngularPage.prototype = Object.create({}, {
todoText: { get: function () { return element(by.model('todoText')); }},
addButton: { get: function () { return element(by.css('[value="add"]')); }},
yourName: { get: function () { return element(by.model('yourName')); }},
greeting: { get: function () { return element(by.binding('yourName')).getText(); }},
todoList: { get: function () { return element.all(by.repeater('todo in todos')); }},
typeName: { value: function (keys) { return this.yourName.sendKeys(keys); }} ,
todoAt: { value: function (idx) { return this.todoList.get(idx).getText(); }},
addTodo: { value: function (todo) {
this.todoText.sendKeys(todo);
this.addButton.click();
}}
});
module.exports = AngularPage;
What are the pros/cons of those two approaches (apart from readability)? Is the second one up-to-date? I've seen that WebdriverIO uses that format.
I've also heard from one guy on Gitter that the first entry is inefficient. Can someone explain to me why?
Page Object Model framework becomes popular mainly because of:
Less code duplicate
Easy to maintain for long
High readability
So, generally we develop test framework(pom) for our convenience based on testing scope and needs by following suitable framework(pom) patterns. There are NO such rules which says that, strictly we should follow any framework.
NOTE: Framework is, to make our task easy, result oriented and effective
In your case, 1st one looks good and easy. And it does not leads to confusion or conflict while in maintenance phase of it.
Example: 1st case-> element locator's declaration happens at top of each page. It would be easy to change in case any element locator changed in future.
Whereas in 2nd case, locators declared in block level(scatter across the page). It would be a time taking process to identify and change the locators if required in future.
So, Choose which one you feel comfortable based on above points.
I prefer to use ES6 class syntax (http://es6-features.org/#ClassDefinition). Here, i prepared some simple example how i work with page objects using ES6 classes and some helpful tricks.
var Page = require('../Page')
var Fragment = require('../Fragment')
class LoginPage extends Page {
constructor() {
super('/login');
this.emailField = $('input.email');
this.passwordField = $('input.password');
this.submitButton = $('button.login');
this.restorePasswordButton = $('button.restore');
}
login(username, password) {
this.email.sendKeys(username);
this.passwordField.sendKeys(password);
this.submit.click();
}
restorePassword(email) {
this.restorePasswordButton.click();
new RestorePasswordModalWindow().submitEmail(email);
}
}
class RestorePasswordModalWindow extends Fragment {
constructor() {
//Passing element that will be used as this.fragment;
super($('div.modal'));
}
submitEmail(email) {
//This how you can use methods from super class, just example - it is not perfect.
this.waitUntilAppear(2000, 'Popup should appear before manipulating');
//I love to use fragments, because they provides small and reusable parts of page.
this.fragment.$('input.email').sendKeys(email);
this.fragment.$('button.submit')click();
this.waitUntilDisappear(2000, 'Popup should disappear before manipulating');
}
}
module.exports = LoginPage;
// Page.js
class Page {
constructor(url){
//this will be part of page to add to base URL.
this.url = url;
}
open() {
//getting baseURL from params object in config.
browser.get(browser.params.baseURL + this.url);
return this; // this will allow chaining methods.
}
}
module.exports = Page;
// Fragment.js
class Fragment {
constructor(fragment) {
this.fragment = fragment;
}
//Example of some general methods for all fragments. Notice that default method parameters will work only in node.js 6.x
waitUntilAppear(timeout=5000, message) {
browser.wait(this.EC.visibilityOf(this.fragment), timeout, message);
}
waitUntilDisappear(timeout=5000, message) {
browser.wait(this.EC.invisibilityOf(this.fragment), timeout, message);
}
}
module.exports = Fragment;
// Then in your test:
let loginPage = new LoginPage().open(); //chaining in action - getting LoginPage instance in return.
loginPage.restorePassword('batman#gmail.com'); // all logic is hidden in Fragment object
loginPage.login('superman#gmail.com')

Not able to encrypt a string with a public key in Protractor

I am trying call the encrypt function mentioned below:
var encryptor = require("./jsencrypt.js");
this.encrypt = function () {
var key="LxVtiqZV6g2D493gDBfG0BfV6sAhteG6hOCAu48qO00Z99OpiaIG5vZxVtiqZV8C7bpwIDAQAB";
encryptor = new JSEncrypt();
encryptor.setPublicKey(key);
var newString = encryptor.encrypt('Password');
console.log("Encrypted password =",newString);
}
Initially I was getting Reference Error for undefined JSEncrypt.
So I downoaded jsencrypt.js file and added var encryptor = require("./jsencrypt.js");at the begining. Now I am getting following error:
Message:
ReferenceError: navigator is not defined
Stacktrace:
ReferenceError: navigator is not defined
at e:\Praveen Data\Projects\ECP\CentralRegistryUI\TestScripts\Utils\jsencrypt.js:73:13
at Object.<anonymous> (e:\Praveen Data\Projects\ECP\CentralRegistryUI\TestScripts\Utils\jsencrypt.js:4342:3)
at require (module.js:385:17)
Tried using windows.navigator in jsencrypt.js, but didn't work.
Protractor tests are not run in browser environment but in node.js, because of that navigator object is not available there. JSEncrypt relies on it to work on the client side across different browsers and versions.
It's referenced in many places in the JSEncrypt code so my best bet would be to either switch to a server side encryption library that would work for you or if not possible mock a global navigator json object with all expected properties/methods as if it was a Chrome browser - node.js runs on chrome's js engine so should work fine.
One of my colleague helped me with the solution.
So here I have a function for encryption:
this.initializeEncryptedPassword = () => {
//console.log("before calling encrypt... ");
browser.executeScript(() => {
//console.log("Starting to return encryptor...");
return window.loginEncryptor.encrypt(window.loginPassword);
}).then((encryptedPassword) => {
this.encryptedPassword = encryptedPassword;
});
//console.log("after calling encrypt...");
}
This function is being called by:
export default class Encryptor {
constructor($window, $http) {
'ngInject';
this.encryptor = new $window.JSEncrypt();
//Need to use HTTP here instead of resource since the resource does not return plain text.
//Getting Public Key by hitting a rest uri.
$http({method: "GET", url: "/xyz/authenticate"}).success((item) => {
this.encryptor.setPublicKey(item);
//set the current encryptor on the window so that testing can use it
$window.loginEncryptor = this.encryptor;
});
}
encryptPassword(credentials) {
credentials.password = this.encryptor.encrypt(credentials.password);
}
}
Hope this help others.
before require('jsencrypt') you can write first:
const { JSDOM } = require('jsdom');
const jsdom = new JSDOM('<!doctype html><html><body></body></html>');
const { window } = jsdom;
global.window = window;
global.document = window.document;
global.navigator ={userAgent: 'node.js'};
const { JSEncrypt } = require('jsencrypt')
You can mock by doing the following:
global.navigator = { appName: 'protractor' };
global.window = {};
const JSEncrypt = require('JSEncrypt').default;

Error destroying Backbone Relational model generated with Coffeescript

I have a situation which has to be simple to solve, I would guess. Surely people are using Backbone Relational with CoffeeScript out there...
Here's a model I have:
class MyCompany.Models.Establishment extends Backbone.RelationalModel
defaults:
name: null
class MyCompany.Collections.EstablishmentsCollection extends Backbone.Collection
model: MyCompany.Models.Establishment
url: '/establishments'
I haven't even added any relationships yet, just extended RelationalModel. Now through the console when I issue a destroy on an instance of the model it successfully destroys the model on the server, but when complete it fails with the trace:
Uncaught TypeError: Object #<Establishment> has no method 'getCollection'
_.extend.unregister
Backbone.Events.trigger
Backbone.RelationalModel.Backbone.Model.extend.trigger
_.extend.destroy.options.success
jQuery.extend._Deferred.deferred.resolveWith
done
jQuery.ajaxTransport.send.callback
It's dying in line 235 of backbone-relational.js 0.4.0 because "this" is the model, I guess, instead of whatever it's supposed to be, and the model doesn't have a method "getCollection".
Any ideas what I'm doing wrong, or should I report a bug? For reference, here's the Javascript coffee generates:
(function() {
var __hasProp = Object.prototype.hasOwnProperty, __extends = function(child, parent) {
for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; }
function ctor() { this.constructor = child; }
ctor.prototype = parent.prototype;
child.prototype = new ctor;
child.__super__ = parent.prototype;
return child;
};
MyCompany.Models.Establishment = (function() {
__extends(Establishment, Backbone.RelationalModel);
function Establishment() {
Establishment.__super__.constructor.apply(this, arguments);
}
Establishment.prototype.defaults = {
name: null
};
return Establishment;
})();
MyCompany.Collections.EstablishmentsCollection = (function() {
__extends(EstablishmentsCollection, Backbone.Collection);
function EstablishmentsCollection() {
EstablishmentsCollection.__super__.constructor.apply(this, arguments);
}
EstablishmentsCollection.prototype.model = MyCompany.Models.Establishment;
EstablishmentsCollection.prototype.url = '/establishments';
return EstablishmentsCollection;
})();
}).call(this);
You need to update the underlying Backbone.js version. Here's why:
Your error comes from this having the wrong value when unregister is called. unregister is called in response to an event bound from register:
model.bind( 'destroy', this.unregister, this );
That third argument sets the context. But that feature was only added as recently as Backbone 0.5.2, as the changelog shows.