I want to implement page object and to be more specific want to read element declared in function in it's prototype.
Tried different ways and those are working fine, however curious if implementation is possible this way also.
Spec File
var login_page = require('../pages/login_page');
describe('login page and properties', function () {
it('verify application launch with URL', function () {
browser.get('appUrl').then(function () {
browser.getTitle().then(function (appTitle) {
expect(appTitle).toBe('Protractor practice website');
});
});
login_page.enterUsername();
});
});
login_page.js:
var login_page = function() {
this.username = element(by.id('username'));
};
login_page.prototype.enterUsername = function() {
this.username.sendKeys('anyString');
};
module.exports = new login_page();
Error:
> protractor conf.js
[22:05:42] E/configParser - Error code: 105
[22:05:42] E/configParser - Error message: failed loading configuration file conf.js
[22:05:42] E/configParser - ReferenceError: element is not defined
Async / await
Think about using async / await. You'll need to change your config SELENIUM_PROMISE_MANAGER: false. This will unchain all the thenables.
Element not defined
Why is element not defined? Probably because the module is loaded before the global object element is defined. Consider instead creating the new object in a beforeAll so we know that element does exist.
Fixes with async / await and element not defined
Spec File:
const LoginPage = require('../pages/login_page').LoginPage;
let login_page = null;
describe('login page and properties', () => {
beforeAll(() => {
login_page = new LoginPage();
});
it('verify application launch with URL', async () => {
await browser.get('appUrl');
const appTitle = await browser.getTitle();
expect(appTitle).toBe('Protractor practice website');
await login_page.enterUsername();
});
});
login_page.js:
If you use JavaScript classes, you don't have to use prototypes.
export class LoginPage {
username = element(by.id('username'));
/**
* enter the user name.
* returns the promise to send keys
*/
async enterUsername() {
return this.username.sendKeys('anyString');
}
}
Related
I don't understand why my spy is not being used. I have used this code elsewhere and it has worked fine.
Here is my test:
const {DocumentEngine} = require('../documentEngine')
const fileUtils = require('../utils/fileUtils')
const request = {...}
const fieldConfig = {...}
test('If the Carbone addons file is not found, context is built with the carboneAddons property as an empty object', async () => {
const expectedResult = {
carboneAddons: {},
}
const fileExistSpy = jest
.spyOn(fileUtils, 'checkFileExists')
.mockResolvedValue(false)
const result = await docEngine.buildContext(request, fieldConfig)
expect(fileExistSpy).toHaveBeenCalledTimes(1)
})
Here is the code that it is being tested:
async function buildContextForLocalResources(request, fieldConfig) {
/* other code */
const addonFormatters = await getCarboneAddonFormatters()
const context = {
sourceJson,
addonFormatters,
documentFormat,
documentTemplateId,
documentTemplateFile,
responseType,
jsonTransformContext
}
return context
}
async function getCarboneAddonFormatters() {
const addOnPath = path.resolve(
docConfig.DOC_GEN_RESOURCE_LOCATION,
'library/addon-formatters.js'
)
if (await checkFileExists(addOnPath)) {
logger.info('Formatters found and are being used')
const {formatters} = require(addOnPath)
return formatters
}
logger.info('No formatters were found')
return {}
}
This is the code from my fileUtils file:
const fs = require('fs/promises')
async function checkFileExists(filePath) {
try {
await fs.stat(filePath)
return true
} catch (e) {
return false
}
}
My DocumentEngine class calls the buildContext function which in turn calls the its method getCarboneAddonFormatters. The fileUtils is outside of DocumentEngine class in a utilities folder. The original code I had this working on was TypeScript as opposed to this which is just NodeJS Javascript. The config files for both are the same. When I try to step through the code (VSCode debugger), as soon as I hit the line with await fs.stat(filePath) in the checkFileExists function, it kicks me out of the test and moves on to the next test - no error messages or warnings.
I've spent most of the day trying to figure this out. I don't think I need to do an instance wrapper for the documentEngine, because checkFileExists is not a class member, and that looks like a React thing...
Any help in getting this to work would be appreciated.
I need to stub hubspot module in order to test my apis.
I need to test this code:
const createCompany = async company => {
const hubspotClient = new hubspot.Client({
apiKey: HUBSPOT_KEY
});
//stuff
const companyObj = {
properties: {
//my properties
}
};
return await hubspotClient.crm.companies.basicApi.create(companyObj);
};
Here They show how to stub a class and access a method through its instance, but in my case I have multiple properties like .crm.companies.basicApi.create().
I tried doing:
getDataStub = sinon
.stub(hubspot.Client.prototype, 'crm.companies.BasicApi.create')
.resolves(fakeResponse);
But it doesn't work and it says TypeError: Cannot stub non-existent own property crm.companies.basicApi.create.
Do you have any hint on how to fix that?
You can create a HubspotClient instance outside of createCompany. Then export that instance so in the test file you could stub methods of it.
const hubspotClient = new hubspot.Client({
apiKey: HUBSPOT_KEY
});
// ...
const createCompany = async company => {
// ...
return await hubspotClient.crm.companies.basicApi.create(companyObj);
}
// ...
stub(hubspotClient.crm.companies.basicApi, 'create');
I am writing a typeScript program which hits an external API. In the process of writing tests for this program, I have been unable to correctly mock-out the dependency on the external API in a way that allows me to inspect the values passed to the API itself.
A simplified version of my code that hits the API is as follows:
const api = require("api-name")();
export class DataManager {
setup_api = async () => {
const email = "email#website.ext";
const password = "password";
try {
return api.login(email, password);
} catch (err) {
throw new Error("Failure to log in: " + err);
}
};
My test logic is as follows:
jest.mock("api-name", () => () => {
return {
login: jest.fn().mockImplementation(() => {
return "200 - OK. Log in successful.";
}),
};
});
import { DataManager } from "../../core/dataManager";
const api = require("api-name")();
describe("DataManager.setup_api", () => {
it("should login to API with correct parameters", async () => {
//Arrange
let manager: DataManager = new DataManager();
//Act
const result = await manager.setup_api();
//Assert
expect(result).toEqual("200 - OK. Log in successful.");
expect(api.login).toHaveBeenCalledTimes(1);
});
});
What I find perplexing is that the test assertion which fails is only expect(api.login).toHaveBeenCalledTimes(1). Which means the API is being mocked, but I don't have access to the original mock. I think this is because the opening line of my test logic is replacing login with a NEW jest.fn() when called. Whether or not that's true, I don't know how to prevent it or to get access to the mock function-which I want to do because I am more concerned with the function being called with the correct values than it returning something specific.
I think my difficulty in mocking this library has to do with the way it's imported: const api = require("api-name")(); where I have to include an opening and closing parenthesis after the require statement. But I don't entirely know what that means, or what the implications of it are re:testing.
I came across an answer in this issue thread for ts-jest. Apparently, ts-jest does NOT "hoist" variables which follow the naming pattern mock*, as regular jest does. As a result, when you try to instantiate a named mock variable before using the factory parameter for jest.mock(), you get an error that you cannot access the mock variable before initialization.
Per the previously mentioned thread, the jest.doMock() method works in the same way as jest.mock(), save for the fact that it is not "hoisted" to the top of the file. Thus, you can create variables prior to mocking out the library.
Thus, a working solution is as follows:
const mockLogin = jest.fn().mockImplementation(() => {
return "Mock Login Method Called";
});
jest.doMock("api-name", () => () => {
return {
login: mockLogin,
};
});
import { DataManager } from "../../core/dataManager";
describe("DataManager.setup_api", () => {
it("should login to API with correct parameters", async () => {
//Arrange
let manager: DataManager = new DataManager();
//Act
const result = await manager.setup_api();
//Assert
expect(result).toEqual("Mock Login Method Called");
expect(mockLogin).toHaveBeenCalledWith("email#website.ext", "password");
});
});
Again, this is really only relevant when using ts-jest, as using babel to transform your jest typescript tests WILL support the correct hoisting behavior. This is subject to change in the future, with updates to ts-jest, but the jest.doMock() workaround seems good enough for the time being.
I have the vanilla probot event function from the docs that comments on new issues:
const probotApp = app => {
app.on("issues.opened", async context => {
const params = context.issue({ body: "Hello World!" });
return context.github.issues.createComment(params);
});
}
This works fine.
I refactor the code into a separate file:
index.js
const { createComment } = require("./src/event/probot.event");
const probotApp = app => {
app.on("issues.opened", createComment);
}
probot.event.js
module.exports.createComment = async context => {
const params = context.issue({ body: "Hello World!" });
return context.github.issues.createComment(params);
};
But I receive this error:
ERROR (event): handler is not a function
TypeError: handler is not a function
at C:\Users\User\probot\node_modules\#octokit\webhooks\dist-node\index.js:103:14
at processTicksAndRejections (internal/process/task_queues.js:97:5)
at async Promise.all (index 0)
When I create a test as recommended in the docs with a fixture and mock the event webhook call with nock this works fine. But when I create a real issue on GitHub this error is thrown.
How can I refactor the code into a separate file without causing the error?
This was my mistake.
This is the whole probot.event.js file:
module.exports.createComment = async context => {
const params = context.issue({ body: "Hello World!" });
return context.github.issues.createComment(params);
};
module.exports = app => {
// some other event definitions
}
By defining module.exports = app I overwrote the previous module.export. The createComment function was therefore never exported.
Removing module.exports = app = { ... } fixed it!
For example, if I have main.js calling a defined in src/lib/a.js, and function a calls node-uuid.v1, how can I stub node-uuid.v1 when testing main.js?
main.js
const a = require("./src/lib/a").a
const main = () => {
return a()
}
module.exports = main
src/lib/a.js
const generateUUID = require("node-uuid").v1
const a = () => {
let temp = generateUUID()
return temp
}
module.exports = {
a
}
tests/main-test.js
const assert = require("assert")
const main = require("../main")
const sinon = require("sinon")
const uuid = require("node-uuid")
describe('main', () => {
it('should return a newly generated uuid', () => {
sinon.stub(uuid, "v1").returns("121321")
assert.equal(main(), "121321")
})
})
The sinon.stub(...) statement doesn't stub uuid.v1 for src/lib/a.js as the above test fails.
Is there a way to globally a library function so that it does the specified behavior whenever it gets called?
You should configure the stub before importing the main module. In this way the module will call the stub instead of the original function.
const assert = require("assert")
const sinon = require("sinon")
const uuid = require("node-uuid")
describe('main', () => {
it('should return a newly generated uuid', () => {
sinon.stub(uuid, "v1").returns("121321")
const main = require("../main")
assert.equal(main(), "121321")
})
})
Bear in mind that node-uuid is deprecated as you can see by this warning
[Deprecation warning: The use of require('uuid') is deprecated and
will not be supported after version 3.x of this module. Instead, use
require('uuid/[v1|v3|v4|v5]') as shown in the examples below.]
About how to stub that for testing would be a bit more harder than before as actually there is no an easy way to mock a standalone function using sinon
Creating a custom module
//custom uuid
module.exports.v1 = require('uuid/v1');
Requiring uuid from the custom module in your project
const uuid = require('<path_to_custom_module>');
Sinon.stub(uuid, 'v1').returns('12345');