RequireJS and mocha have some problem working together.
I figured this is because mocha does not wait for requireJS's asynchronous operations to finish and decides testing is done.
As a hot fix I wrapped requireJS 's loading calls in mocha's it() calls.
Somehow mocha knows when I add a callback, that it should wait for the asynchronous methods to finish.
But I'd like to know whether there is no other, more convenient setup than the one I'm using now. The current setup isn't really nice nor flexible.
This is my test.coffee script:
describe 'Ink', ->
describe '#constructor', ->
it 'should return an Ink instance', ( done ) ->
requirejs [ "build/ink/core/Ink" ], ->
# commence testing
a = new Ink( '<div></div>' )
assert.equal( new Ink instanceof Ink, false )
assert.equal( new Ink instanceof window.jQuery, true )
done()
describe 'Mixin', ->
f : ( Mixin ) ->
# test mixin
class A
constructor : ( #a ) ->
class m extends Mixin
constructor : () -> #mixin_prop = 42
increment : ( arg ) -> return arg + 1
class B extends A
Mixin.mixin( m, # )
b = new B()
return b
it 'should chain the constructor', ( done ) ->
requirejs [ "build/ink/core/Mixin" ], ( Mixin ) ->
b = f( Mixin )
assert.equal( b.mixin_prop, 42 )
done()
it 'should add the methods from the mixin to the new class', ( done ) ->
requirejs [ "build/ink/core/Mixin" ], ( Mixin ) ->
b = f( Mixin )
assert.equal( b.increment( 42 ), 42 )
done()
I initialize my modules in beforeEach, and use a callback to trigger async:
describe...
var Module
beforeEach(function(callback){
requirejs
Module = loadedFile
callback(); // suites will now run
})
I have a bootstrap here: https://github.com/clubajax/mocha-bootstrap
Mocha provides a done callback to the function you invoke with it, and it works nicely for this purpose. Here's an example of how I'm currently using it--note that I'm using require to load my test config as well, and obviously this is straight JS, not CoffeeScript, but it should obtain.
define([
'chai',
'SystemUnderTest'
], function(chai, SystemUnderTest) {
var expect = chai.expect;
describe('A functioning system', function() {
it('knows when to foo', function(done) {
sut = new SystemUnderTest();
expect(sut.foo()).to.be.ok;
done();
});
});
So mocha's support for async testing, which you might 'normally' use to test async services, can also be used to support testing of asynchronously loaded modules.
Mocha's async documentation
I haven't used requirejs in my tests, but I think the before function might help:
describe 'Mixin - ', ->
before (done) ->
console.log dateFormat(new Date(), "HH:MM:ss");
requirejs [ "build/ink/core/Ink" ], ->
# commence testing
a = new Ink( '<div></div>' )
assert.equal( new Ink instanceof Ink, false )
assert.equal( new Ink instanceof window.jQuery, true )
done()
beforeEach ->
....
describe ...
Related
As part of the preparation for the "Asynchronous Module Loading" enablement in FLP, one of its prerequisites is making sure that no eval call is triggered due to the HTML document being served with the response header Content-Security-Policy and its directive script-src omitting unsafe-eval.
Is there a feature in UI5 that logs all eval calls in runtime triggered by incorrectly addressed modules?
Resolution
Open the browser console (F12) and ensure that "Warnings" logs are visible.
Start the app with the UI5 config URL parameter sap-ui-xx-debugModuleLoading=true and make sure that the debug mode is not unnecessarily enabled.
In the browser console, filter by "(using eval)".
Example
Given
sap.ui.define([
"sap/ui/core/mvc/Controller",
"sap/ui/comp/library",
], function(Controller, sapUiCompLib) {
// ...
const ValueHelpRangeOperation = sapUiCompLib.valuehelpdialog.ValueHelpRangeOperation; // enum from module "sap/ui/comp/library"
const ValueHelpDialog = sapUiCompLib.valuehelpdialog.ValueHelpDialog; // control from module "sap/ui/comp/valuehelpdialog/ValueHelpDialog"
return Controller.extend("my.controller.MyController", {
onSomething: function () {
const vhDialog = new ValueHelpDialog(/*...*/);
// ...
},
// ...
});
});
Logs
Solution
Declare the missing dependency to "sap/ui/comp/valuehelpdialog/ValueHelpDialog".
No need to declare transitive dependencies that were also logged ( : ...).
sap.ui.define([
"sap/ui/core/mvc/Controller",
"sap/ui/comp/library",
"sap/ui/comp/valuehelpdialog/ValueHelpDialog" // <-- Declare the module dependency
], function(Controller, sapUiCompLib, ValueHelpDialog) {
// ...
const ValueHelpRangeOperation = sapUiCompLib.valuehelpdialog.ValueHelpRangeOperation; // enum from module "sap/ui/comp/library"
// const ValueHelpDialog = sapUiCompLib.valuehelpdialog.ValueHelpDialog; <-- Remove
return Controller.extend("my.controller.MyController", {
onSomething: function () {
const vhDialog = new ValueHelpDialog(/*...*/);
// ...
},
// ...
});
});
Module name to declare as dependency can be seen in the UI5 API reference:
I'm struggling to perform a test with jest concerning an axios api call
here is my API call, that works perfectly within my program
import axios from 'axios';
import crypto from 'crypto';
import { prop } from 'ramda';
const baseUrl = 'http://gateway.marvel.com:80';
const uri = '/v1/public/characters';
const charactersUrl = baseUrl + uri;
const timestamp = [Math.round(+new Date() / 1000)];
const privateApi = 'XXX';
const publicApi = 'XXX';
const concatenatedString = timestamp.concat(privateApi, publicApi).join('');
const hash = crypto.createHash('md5').update(`${concatenatedString}`).digest('hex');
const charactersApi = () =>
axios
.get(charactersUrl, {
params: {
ts: timestamp,
apikey: publicApi,
hash,
},
})
.then(prop('data'));
export default charactersApi;
When I'm trying to test it, that way:
import axiosMock from 'axios';
import charactersApi from '../marvelApi';
jest.mock('axios', () => ({
get: jest.fn(),
}));
describe('tools | marvelApi', () => {
const piece = { name: '3D-MAN' };
axiosMock.get.mockResolvedValueOnce({ data: piece });
it('should get the character', () => {
return charactersApi().then(elem => {
expect(elem.name).toEqual('3D-MAN');
});
});
});
I get the following message from jest
TypeError: Cannot read property 'then' of undefined
16 |
17 | const charactersApi = () =>
> 18 | axios
| ^
19 | .get(charactersUrl, {
20 | params: {
21 | ts: timestamp,
at charactersApi (src/tools/marvelApi.js:18:3)
at Object.<anonymous> (src/tools/tests/marvelApi.test.js:13:12)
What I have tried
A common error is to forget the return statement within the function that contain the request API, in my case it's done correctly (first piece of code -> charactersApi()) source1, source2
I also tried to return a Promise from the mocked Axios as I have seen on another SO ticket
jest.mock('axios', () => ({
get: jest.fn(() => Promise.resolve()),
}));
I think my axios mock is not correct, because the struggle comes from the test while the production version work well
Any thoughts ?
You can spy on the "axios.get" calls and resolve them to a fixed (mocked) value:
/**
* #jest-environment jsdom
*/
const axios = require('axios')
beforeAll(() => {
jest.spyOn(axios, 'get').mockImplementation()
})
afterAll(() => {
jest.restoreAllMocks()
})
it('returns the mocked response', async () => {
axios.get.mockResolvedValue({ data: 'foo' })
const res = await axios.get('https://api.github.com')
expect(res).toEqual({ data: 'foo' })
})
You shouldn't use jest.mock because it mocks a module that your imported code may be using. As far as I know, it doesn't affect the current module's imports (and you import axios as a part of your test).
Recommended solution
I strongly discourage you from spying/mocking axios directly. See my argumentation below.
You're mocking implementation details of axios. In other words, you take the axios.get function and throw it away, alongside any internal logic it may have, and replace it with a hard mock. This means your test no longer uses axios, instead it uses an emptied mocked shell of axios. This makes your test different from your actual code, which, in turn, decreases the confidence such a test gives you.
You're coupling your mocks with a specific request client (axios). Such an approach is not a long-term investment, as you're writing axios-specific mocks. You can't reuse such mocks for requests made by other clients (i.e. window.fetch, Apollo, etc.), because they have their own implementation details (i.e. window.fetch has no .get() to spy on), which only encourages you to write more implementation-specific logic in tests.
You can learn more about the disadvantages of direct mocking of request clients in the Stop mocking fetch article by Kent C. Dodds. It uses window.fetch mocks as an example, but you may replace it with ANY_REQUEST_CLIENT when reading.
I highly recommend using tools like Mock Service Worker (MSW) that will encourage you to write abstracted mocks that don't rely on any request clients (you can use them no matter how your tested code makes a request) and can even be reused across different testing levels (the same mocks for Jest, Storybook, or Cypress).
Here's how your test would look like with MSW:
import { rest } from 'msw'
import { setupServer } from 'msw/node'
import charactersApi from '../marvelApi';
const server = setupServer(
rest.get('http://gateway.marvel.com:80/v1/public/characters', (req, res, ctx) => {
return res(ctx.json({
data: {
name: '3D-MAN'
}
}))
})
)
beforeAll(() => server.listen()
afterAll(() => server.close())
describe('tools | marvelApi', () => {
it('should get the character', () => {
return charactersApi().then(elem => {
expect(elem.name).toEqual('3D-MAN')
})
})
})
Notice how there are no details about how the request is made, only which request to intercept and mock its response.
You can follow a detailed tutorial on how to Get started with MSW. There's also a great video on API mocking and what problems MSW solves.
Framework used - Protractor
BDD - Cucumber
Language - Typescript
Now i have implemented the framework and a test scenario is also running fine with protractor.
But the problem i am facing is when i write another cucumber scenario my test fails saying 'A session is either terminated or not started'
The above failure is because when my first cucumber scenario starts the appium server starts with in my config and at the end i close the server/driver
Now i have written another test scenario, since cucumber is independent of each scenario , when the sec starts it does not do the config again. Now i need a beforeTest method to call.
So i am not sure how to implement that in typescript,as i am new to it.
Tried the same concept of java way but not working out. There where examples for javascript but still did not help me out.
Tried creating a new util folder and placing my beforeTest inside that but the function is not calling there
Tried to use beforeLaunch()with in my config file, but still does not work out
my config file: config.ts
export let config: Config = {
allScriptsTimeout: 40000,
getPageTimeout: 40000,
setDefaultTimeout: 60000,
defaultTimeoutInterval: 30000,
specs: [
// '../../utils/beforeEach.ts',
'../../features/*.feature',
],
onPrepare: () => {
Reporter.createDirectory(jsonReports);
tsNode.register({
project: './tsconfig.json'
});
},
multiCapabilities: [
androidPixel2XLCapability,
// iPhoneXCapability
],
framework: 'custom',
frameworkPath: require.resolve('protractor-cucumber-framework'),
cucumberOpts: {
compiler: "ts:ts-node/register",
glue: ["steps"],
format: [
"json:./reports/json/cucumber_report.json",
],
require: ['supports/timeout.js', '../../stepdefinitions/*.ts'],
tags: "#firstPurchasePopup",
},
seleniumAddress: serverAddress,
onComplete: () => {
Reporter.createHTMLReport();
},
// =====
// Hooks
// =====
beforeTest: function () {
},
beforeLaunch(){
console.log("Before");
seleniumAddress: 'http://localhost:4723/wd/hub';
},
afterLaunch() {
console.log("After");
},
};
my other beforeEach.ts:
This is not working but what i tired and was not working.
import {After, AfterAll, Before} from "cucumber";
const serverAddress = 'http://localhost:4723/wd/hub';
import {beforeEach, afterEach, describe} from "selenium-webdriver/testing";
beforeEach(function () {
console.log("Before");
});
// });
afterEach(function () {
console.log("Before");
});
// let beforeEach: () => void;
// beforeEach = () => {
// console.log("Before Test");
// // config.multiCapabilities;
// seleniumAddress: serverAddress;
// };
//
// let afterEach: () => void;
// afterEach = () => {
// console.log("After Test");
// };
This is my feature file: bonus.feature
this is my feature file:
Background:
Given I launch the app
Then I should see the popup window for the Bonus
And I verify the UI
Then I tap on ok button
And The popup window should not be seen
#firstPurchasePopup
Scenario: firstPurchasePopup new join button
When I tap on the 'New ' button
And The popup window should not be seen
Then I navigate back from join page to home page
Then The popup window should not be seen
Then I close the app
#firstPurchasePopup
Scenario: firstPurchasePopup login button
And I tap on log in button on the initial screen
Then I navigate back from login page to home page
And The popup window should not be seen
Then I close the app
I expect my the scenario what i have written to execute both one after the other , like execute Scenario: firstPurchasePopup new join button which it does . But when it launches the app again for the sec Scenario: firstPurchasePopup login button does not work as the driver is not started again, since it was closed in prev one.
to start it i need to create beforeTest which i am facing difficutly to write the code
I haven't used Protractor with Cucumber, but I have used Cucumber & Typescript together. I resolved the problem by having a file cucumber.js in a root that is being loaded at the very beginning by default and looks like that:
var settings = "features/**/*.feature -r step-definitions/**/*.ts -r hooks/**/*.ts -r support/**/*.ts "
module.exports = {
"default": settings
}
However, I think in your case the solution would be adding a path to hooks file to config.cucumberOpts.require list instead to config.specs one.
Did you try it?
#All
Thanks for your inputs #mhyphenated
I figured out that the rather than using inside the config, i tried using the before and after in the hooks.ts ,also other than calling the server i was not actually calling the android driver, as below and that worked
beforeTest: function () {
beforeTest: function () {
},
beforeLaunch(){
console.log("Before");
seleniumAddress: 'http://localhost:4723/wd/hub';
},
hooks.ts
import { AndroidDriver } from "appium/node_modules/appium-android-driver";
let driver:AndroidDriver, defaultCaps;
driver = new AndroidDriver();
Before(function () {
// This hook will be executed before all scenarios
browser.ignoreSynchronization = false;
browser.manage().timeouts().implicitlyWait(500);
let defaultCaps = config.multiCapabilities[0];
console.log("defaultCaps = ", defaultCaps );
driver.createSession(defaultCaps);
driver.defaultWebviewName();
});
I'm new to Sails and I'm trying to figure out the best/proper method for returning a standard object for every API response.
The container our front-end requires is:
{
"success": true/false,
"session": true/false,
"errors": [],
"payload": []
}
Currently, I’m overwriting the blueprint actions in each controller like this example (which just seems so very, very wrong):
find : function( req, res ){
var id = req.param( 'id' );
Foo.findOne( { id : id } ).exec( function( err, aFoo ){
res.json(
AppSvc.jsonReply(
req,
[],
aFoo
), 200
);
});
}
And in AppSvc.js:
jsonReply : function( req, errors, data ){
return {
success : ( errors && errors.length ? false : true ),
session : ( req.session.authenticated === true ),
errors : ( errors && errors.length ? errors : [] ),
payload : ( data ? data : [] )
};
}
Additionally, I’ve had to modify each res.json() method for each default response (badRequest, notFound,etc). Again, this feels so wrong.
So, how do I properly funnel all API responses into a standard container?
Sails custom responses are great for this.
If you look at the blueprint code, you'll see that each one calls res.ok when it's done: https://github.com/balderdashy/sails/blob/master/lib/hooks/blueprints/actions/find.js#L63
You can add your own file - ok.js - to api/responses/ - which will override the default built in handler.
https://github.com/balderdashy/sails/blob/master/lib/hooks/responses/defaults/ok.js <- just copy and paste this to start, and adapt as you need.
i have coffee script class that defined a method named 'isOutdated', right it only output some text, i need to use this method in another method, but it always complained 'isOutdated is not defined', don't know why,
here is my code:
class A
...
isOutdated: (last, curr) ->
'hell..o'
run: (callback) ->
#store.findOne config, {name : 'a' }, {}, {}, (err, results) =>
return callback err, null if err
callback null, isOutdated 'last', 'curr'
and here is what it complaint:
[ReferenceError: isOutdated is not defined]
If you want to reference the isOutdated method then you need to say
callback null, #isOutdated 'last', 'curr'
# -------------^
Just isOutdated is looking for a local variable, not the method.