Parse server unit test with mocha - javascript

So I've been following some tutorials about unit testing with Cloud Code. Here is how I organize my code base :
cloud/
ChatMessage/
model.js
update.js
ChatRoom/
model.js
update.js
test/
test.js
In my model.js files, I have Parse.Object subclass with helper functions. It looks like this
class ChatMessage extends Parse.Object {
constructor() {
super('ChatMessage')
}
// Some functions
}
Parse.Object.registerSubclass('ChatMessage', ChatMessage)
module.exports = ChatMessage
In my update.js files, I have the before/after save and cloud code functions :
function beforeSave(request, response) {
// Do stuff
}
Parse.Cloud.beforeSave('ChatMessage', function (request, response) {
beforeSave(request, response)
})
module.exports = {
beforeSave: beforeSave,
}
I've extracted the content of the beforeSave function for test purposes. It enables me to expose the before save function to mocha. For the record, all this works fine in production.
Now in my test I do this :
const ChatMessage = require('../cloud/ChatMessage/model.js')
const ChatMessageUpdate = require('../cloud/ChatMessage/update.js')
const expect = require('expect')
describe('ChatMessage', function () {
const request = {
user: new Parse.User(),
object: new ChatMessage()
}
const response = {
success: function () {},
error: function () {}
}
describe('creation', function () {
it('should fail when the author is undefined', function () {
ChatMessageUpdate.beforeSave(request, response)
expect(response.error).toHaveBeenCalled()
})
})
})
I mock the request and response object. And then I try to launch a test using my beforeSave function. And I get the following error :
class ChatRoom extends Parse.Object {
^
ReferenceError: Parse is not defined
A quick fix is to add this at the beginning of my model file like this :
const Parse = require('parse/node')
But Parse is already expose in /cloud so it seems stupid to me to copy paste this line in every file. What should I do ? More precisely : how do I have my object oriented structure conform to tests ?
Also, what would be the best code structure to test all my code with zero asynchronous test (for test performance) ?

You should define Parse in your test suite as a global

Create a file in your test folder called helper.js that looks like this:
// helper.js
global.Parse = require('parse/node');
This should automatically get included in any of your tests. If you only have one test just add that line to the top of your test file.

Related

custom global function not defined when testing in Jest; works fine when not testing

I have a custom, globally-scoped function in my Express app, foo. When running my Jest test scripts, this function is caught as undefined. Thus, any tests using them fail.
index.d.ts:
declare global{
function foo(): string;
}
export {};
src/Utils/index.ts:
global.foo = function foo(){
return "bar";
};
src/Modules/Example.module.ts:
export const test = () => {
// This will return bar, as expected, when developing.
// A reference error will only be thrown when running npm test.
return foo();
};
src/Modules/Example.test.ts:
import { test } from "./Example.module";
describe("modules/example", () => {
describe("test", () => {
it("returns bar", () => {
let bar = test();
expect(bar).toBe("bar");
});
});
});
Despite this not being an issue while developing, this test results in the error:
ReferenceError: foo is not defined.
export const test = () => {
return foo();
^
...
};
You can specify src/Utils/index.ts as a setup file, which Jest will load and execute before running tests. You can add it to your Jest configuration file (or create one if you don't have one):
Assuming a CJS-format Jest configuration, jest.config.js:
module.exports = {
// Your other configuration options
"setupFiles": ["<rootDir>/src/Utils/index.ts"]
};
It will look slightly different if you are using a JSON or TypeScript Jest configuration file.
However I don't recommend using global variables (even if you use them a lot). With a proper code editor setup, it is easy to import a function from another file.

Mocha finds global variable is undefined when extended by class

I have the following ES6 module from a Chromecast receiver that I would like to test using Mocha...
// SUT - app.js
import { Queue } from 'queue.js'
export const app = async () => {
const context = cast.framework.CastReceiverContext.getInstance();
const options = {};
options.queue = new Queue();
context.start(options);
}
This code runs inside a Chromecast user agent which provides access to the global cast object. This cast object exposes an api which in turn enables the JS thread to interact directly with the Chromecast CAF SDK. This cast variable is always available at run time.
The Queue class is slightly unusual because in order for it to work according to the CAF framework documentation, is must extend an abstract class from the framework cast.framework.QueueBase...
// queue.js
class Queue extends cast.framework.QueueBase {
initialize(){
// build queue here
}
}
Now I would like to write some unit tests to check my app function is correct. For example:
// app.test.js
import { app } from 'app.js';
it('should do some stuff', async function () {
// inject a mock cast object
global.cast = {
framework: {
QueueBase: class {},
CastReceiverContext: {
getInstance: () => {},
},
},
};
await app();
// Make some assertions
});
However, even though I am injecting a mock using global.cast, which is sufficient for all regular references to the cast object, in the case where a class is extending the injected cast object, apparently it is not yet available and I receive the following error:
ReferenceError: cast is not defined
I found an ugly hack to make this error disappear. If I place the following snippet above the class declaration then I can inject the mock at runtime and it not only works for Mocha but also for execution on the Chromecast device....
try {
// The following line throws in Mocha's node environment
// but executes fine on the Chromecast device
if (cast) {
}
} catch {
global.cast = {
framework: {
QueueBase: class {},
},
};
}
export class Queue extends cast.framework.QueueBase {
...
However, I would like to find a better solution so that I don't have to pollute my production code with this hack which is only there to allow me to run tests.
My .mocharc.yml file looks like this:
require:
- '#babel/register'
- 'ignore-styles'
- 'jsdom-global/register'
- 'babel-polyfill'
... and my command to run tests is:
mocha --recursive --use_strict
finally, my .babelrc file looks like this:
{
"presets": [
[
"#babel/preset-env"
]
],
"plugins": [
"inline-svg",
"import-graphql"
]
}
Static imports are always evaluated first, so the order of operations is roughly:
import { Queue } from 'queue.js'
class Queue extends cast.framework.QueueBase { // ReferenceError outside Chromecast!
initialize(){
// build queue here
}
}
global.cast = {
framework: {
QueueBase: class {},
CastReceiverContext: {
getInstance: () => {},
},
},
};
You can see that the mock is created after the reference to cast in app.js.
The only reliable way to run the mock creation before importing the app module is using a dynamic import:
// app.test.js
it('should do some stuff', async function () {
// inject a mock cast object
global.cast = {
framework: {
QueueBase: class {},
CastReceiverContext: {
getInstance: () => {},
},
},
};
const { app } = await import('app.js');
await app();
// Make some assertions
delete global.cast;
});
If you prefer not to repeat the mock creation and the import in every test, you can move both out of the test definition:
// app.test.js
// inject a mock cast object
global.cast = {
framework: {
QueueBase: class {},
CastReceiverContext: {
getInstance: () => {},
},
},
};
const { app } = await import('app.js');
it('should do some stuff', async function () {
await app();
// Make some assertions
});
// Optionally clean up the mock after all tests
after(() => delete global.cast);

Cypress POM approach | How to read fixtures data in POM_Page.js file in cypress

I am new to cypress and trying to create framework on POM.
I have followed these steps for creating framework
Created Object repository with file named 'locators.json', data in that file looks like
this
{
"RxClaims_LoginPage":
{
"UserName":"#f0",
"Password":"#f1",
"SignInButton":"#xlg0003"
}
}
Under Integration>Examples I have created tests named OR_Approch.js which look like
/// <reference types="Cypress" />
import ORLoginPage from '../PageObjects/OR_Login'
describe('OR_Approach',function(){
it('RxClaimsLogin', function() {
const login = new ORLoginPage();
cy.visit('/')
cy.wait(2000)
login.EnterData_In_UserName()
login.Password()
login.SignInButton()
})
})
3.And I have created one other folder under Integration>POM which consists all the POMs - one of them named OR_Login.js looks like
class ORLoginPage{
EnterData_In_UserName()
{
cy.fixture('example').then(function (dataJson) {
this.testData = dataJson;
})
cy.fixture('locators').then(function (oRdata) {
this.objRep = oRdata;
})
cy.enterDatainTextBox(this.objRep.RxClaims_LoginPage.UserName,this.testData.UserName)
return this
}
Password(){
return 'cy.enterDatainTextBox(this.objRep.RxClaims_LoginPage.Password,this.testData.Password)'
}
SignInButton(){
return 'cy.clickOnObject(this.objRep.RxClaims_LoginPage.SignInButton)'
}
}
export default ORLoginPage;
Under Support commands.js consists custom methods which looks like this
Cypress.Commands.add('enterDatainTextBox', (textBoxElePath, textValue) => {
cy.get(textBoxElePath).type(textValue)
})
So my question is I want to access locators.js data for all the functions in OR_Login.js. I have tried beforeEach method for test files which works fine but when i use it in any class like OR_Login.js it does not work. Please suggest some way so data for fixtures can be read in POM class files.
The beforeEach() way will not work in OR_Login.js because it is not a test file, and mocha is not executing that file.
Instead, you can simply import the JSON file as a variable to OR_Login.js and use that variable.
// OR_Login.js
const locators = require('./path/to/locators.json');
class ORLoginPage {
...
Password(){
return cy.enterDatainTextBox(locators.RxClaims_LoginPage.Password,locators.Password)
}
...
}
If you have dynamic data that will change based on the class, you could also create your class with a constructor that would point it to the correct data in the locators.json file.
All of that being said, Cypress strongly urges you not to use POM with Cypress. If you do choose to continue using the POM with Cypress, I'd highly recommend that all of your functions you execute in a test are contained within the class that executes the code (instead of mixing cy. and your class in the test), as well as look into how you can better chain your command off of one another, so that they are executed in the same cypress command chain.
The problem with using POM with Cypress, it works against the Mocha hooks like beforeEach() which are very useful for readable tests.
But classes have constructors which might be used to fill your this.testData.
class ORLoginPage{
constructor() {
cy.fixture('example').then(function (dataJson) {
this.testData = dataJson;
})
cy.fixture('locators').then(function (oRdata) {
this.objRep = oRdata;
})
}
EnterData_In_UserName() {
cy.enterDatainTextBox(this.objRep.RxClaims_LoginPage.UserName, this.testData.UserName)
return this
}
Password() {
...
}
SignInButton(){
...
}
}
export default ORLoginPage;
The cy.fixture() is async because it reads a file, so it may be better to use an init() method.
This will be useful for any async commands you have in the POM.
class ORLoginPage{
init() {
return new Promise((resolve, reject) => {
cy.fixture('example').then(function (exampleData) {
this.testData = exampleData
cy.fixture('locators').then(function (locatorData) {
this.objRep = locatorData;
resolve(true)
})
})
}
}
...
/// <reference types="Cypress" />
import ORLoginPage from '../PageObjects/OR_Login'
describe('OR_Approach',function(){
it('RxClaimsLogin', async function() { // add async to the test
const login = new ORLoginPage();
await login.init()
cy.visit('/')
cy.wait(2000)
login.EnterData_In_UserName()
login.Password()
await login.SignInButton() // may be useful to await other async methods
})
})

Jest mock window object before importing a dependency

I need to have a value set in the window object before a dependency is imported. Say I have this code
// foo.test.js
import { dependency } from './foo'
describe('...', () => {
it('...', () => {
// use dependency
})
})
But for dependency to be imported I need to have a value defined in window.myValues
// foo.js
export const dependency = {
key: window.myValue.nestedValue
}
That code will give me an error when importing the file because window.myValue.nestedValue is trying to access the property nestedValue of undefined.
How can I get that done?
Edit
Following christianeide's answer below I get the following error
● Test suite failed to run
TypeError: Cannot convert undefined or null to object
2 | delete global.window.myValue
3 | global.window = Object.create(window)
> 4 | global.window.myValue = {
| ^
5 | nestedValue: 'someValue'
6 | }
7 | }
at module.exports (jest.setup.js:4:17)
at node_modules/#jest/core/build/runGlobalHook.js:82:17
at ScriptTransformer.requireAndTranspileModule (node_modules/#jest/transform/build/ScriptTransformer.js:684:24)
at node_modules/#jest/core/build/runGlobalHook.js:72:27
at pEachSeries (node_modules/p-each-series/index.js:8:9)
at async _default (node_modules/#jest/core/build/runGlobalHook.js:58:5)
at async runJest (node_modules/#jest/core/build/runJest.js:345:5)
es6 imports are "hoisted", meaning wherever you write them in the code, they'll get processed before the importing module is executed, so the imported module is always executed before the importing module. In your case, this means foo.js executes before foo.test.js, so even if you properly mock your property of window in your tests, foo.js will not see your mock.
You can get around this by using require in your tests to import foo.js after the property of window has been mocked.
// foo.test.js
window.myValue = { nestedValue: MOCK_NESTED_VALUE };
const { dependency } = require('./foo');
describe('...', () => {
it('...', () => {
// use dependency
})
})
As other answers have pointed out, if myValue is one of existing "sytem" properties of window such as window.location, you might have to delete it first. When deleting, don't forget to back it up so that you can restore it when cleaning up after your test.
Try assigning values to global.window.
Something like this:
delete global.window.myValue;
global.window = Object.create(window);
global.window.myValue = {
nestedValue: 'someValue',
};
This can be done inside jest.setup.js, but you could also probably define the values inside foo.test.js
I was able to append properties on the window object by creating a setup.js:
global.propertyA = () => {};
global.nestedPropertyB = {
propertyC: () => {}
};
And setting the file in setupFilesAfterEnv in my jest.config.js file:
module.exports = {
setupFilesAfterEnv: ['<rootDir>/tests/js/setup.js']
}
I have solved such an issue with jest.mock: as it's hoisted above import statements, it's possible to execute any code before and after importing an actual module.
In my case I needed to test some functionality and set node env to a value, different from 'test', and, as I needed this substitution only for the time of import, I set its value back to 'test' before returning the actual module. Of course, it may vary depending on use cases - it's just an example of executing code before and after importing is done. Also i tried to use a getter for a particular module field, and it also worked.
import ApiBase from "../ApiBase";
jest.mock('../ApiBase', () => {
process.env.NODE_ENV = '';
const actual = jest.requireActual('../ApiBase');
process.env.NODE_ENV = 'test';
return actual;
});

TypeError: "exports" is read-only => When exporting a module with method calling another method

I'm new to webpack and this module bundler stuff and I'm currently just experimenting around what's possible and what isn't.
Here, I'm trying the following:
//appECommerce.js
import eCommerceLogic from './lib/eCommerceLogic.js'
//eCommerceLogic.js
import name from './eCommerceJSExportTests.js';
module.exports = {
productnamesOnclick: function(){
$("#AJAXproductnames").on("click",function(){
getProductnameElements()
})
},
productNamesGetter: function(){
async function getProductnameElements(){
let productNameElements = document.getElementsByClassName('customProductCardName')
console.log("result is ", productNameElements)
let test = await name.commonAJAXCall()
console.log(test)
}
}
}
//eCommerceJSExportTests.js
module.exports = {
commonAJAXCall: function() {
//return "helloExport"
return $.get('https://jsonplaceholder.typicode.com/todos/1', {
}).then((response) => {
response = JSON.stringify(response)
console.log(response)
console.log("AJAX happened")
return response
})
}
}
So basically, I just want to know why I get this error (see title)^^
Furthermore though, I'd also like to know three specific things:
1) Is it possible to have module B import from module C and then export to module A, where the content of module B's import eventually consists of both its "own" code and the code imported from module C, since module B makes use of the imported properties and methods from module C there?
2) In code which is exported like shown above, is it actually possible to have method A containing a call to method B?
3) Can code which attaches event listeners to the DOM even be exported?
Try This:
export { toBeExported as toBeExported };

Categories

Resources