Jest testing a class in window - reactjs

I'm super lost how to do this.
We've got a class in window for Loqate called window.pca.Addresses
Then in our code we run this snippet of code in a react method:
const shippingControl = new pca.Address(shippingFields, { key: process.env.LOQATE_API_KEY });
window.setTimeout(() => {
shippingControl.load();
}, 3000);
My question is how do I write a jest test to mock the Address class in the window and then also add the load() method so we can test if it has been fired or not.
I've tried to spyOn the window object as both global and window as well as mocking it with jest.mock() which also hasn't worked.
I'm wondering if anyone else can guide me on how to do this?
Many thanks

You can add beforeEach to your test something like that:
beforeEach(() => {
Object.defineProperty(window, "pca", {
value: {
Addresses: { load: jest.fn(() => null) }
},
writable: true
});
});
You can test if it has been fired like this:
expect(window.pca.Addresses.load).toHaveBeenCalledTimes(1);

Related

ExtJS cannot override methods statics

I wanted to extend Ext.Action class due to add my own methods, but I've encouraged problem Cannot override method statics on Core.app.Action instance.
I know that is a problem connected to overriding constructor. But don't know how to solve it other way.
Here is code of my component
Ext.define('Core.app.Action', {
extend: 'Ext.Action',
iconCls: 'icon-add',
currentData: undefined,
constructor: function(config){
config = Ext.applyIf(config || {}, this);
this.callParent([config])
},
handler: function (widget, event) {
Ext.Msg.alert("Name:", this.currentData.data.company)
},
getActionId: function () {
return this.actionId
},
setCurrentData: function (data) {
this.currentData = data
this.disable()
if (data.data.actions) {
this.currentData.data.actions.forEach(action => {
if (action === this.actionId) this.enable()
})
}
}
})
And there is a way how I create instance of mentioned class
Ext.create('Core.app.Action', {
actionId: 'D',
text: "Action D",
})
ExtJS is not throwing errors when I remove constructor from my class, but I can't also use methods such as handler or setCurrentData.
I've also tried overriding the component, but failed.
Answers in solution below weren't helpful in this case.
Reusable Action in Ext JS MVC

React & Jest global.confirm not updating between tests

I have an existing application that I am trying to write tests for. I have a component function that uses a window.confirm box to get user input (not my code) like this:
if (window.confirm('Are you sure to unassign?')) {
NetworkWebAPIUtils.unassignSeat(this.state.seatnetwork.seat.id, this.state.seatnetwork.network.id, this.props.rank);
}
I am trying to write tests for both paths:
it('should call endpoint when user selects yes', function () {
global.confirm = jest.fn(() => true);
seatUpdateRow.instance().handleChangeAssigned({target: {value: true}});
expect(NetworkWebAPIUtils.unassignSeat.mock.calls.length).toBe(1);
expect(NetworkWebAPIUtils.unassignSeat.mock.calls[0].length).toBe(3);
expect(NetworkWebAPIUtils.unassignSeat.mock.calls[0][0]).toBe(1234);
expect(NetworkWebAPIUtils.unassignSeat.mock.calls[0][1]).toBe(5678);
expect(NetworkWebAPIUtils.unassignSeat.mock.calls[0][2]).toBe(1);
});
it('should not call endpoint when user selects no', function () {
global.confirm = jest.fn(() => false);
seatUpdateRow.instance().handleChangeAssigned({target: {value: true}});
expect(NetworkWebAPIUtils.unassignSeat).not.toHaveBeenCalled();
});
The problem is that global.confirm will not update for the second test. If I set the first test to false then it obviously fails but the second one passes. If I set the first to true then the first passes but the second fails because global.confirm = jest.fn(() => false) doesn't actually cause window.confirm to evaluate to false. If I comment out the first then the second passes just fine.
I have tried mocking window.confirm and I get the exact same behavior.
It was an obvious issue. I forgot to clear the mock calls from NetworkWebAPIUtils.unassignSeat.
afterEach(function () {
NetworkWebAPIUtils.unassignSeat.mockClear();
});

Angular 1.x/2 Hybrid, karma tests not bootstrapping ng1 app

I currently have a Hybrid Angular app (2.4.9 and 1.5.0) using angular-cli. Currently, when running our application, we are able to bootstrap the 1.5 app correctly:
// main.ts
import ...
platformBrowserDynamic().bootstrapModule(AppModule).then(platformRef => {
angular.element(document).ready(() => {
const upgrade = platformRef.injector.get(UpgradeModule) as UpgradeModule;
upgrade.bootstrap(document.body, ['myApp'], {strictDi: true});
});
});
However, in our test.ts file:
// test.ts
// This file is required by karma.conf.js and loads recursively all the .spec and framework files
import ...;
declare var __karma__: any;
declare var require: any;
__karma__.loaded = function () {};
getTestBed().initTestEnvironment(
BrowserDynamicTestingModule,
// I'm assuming that I need to call 'boostrapModule()' somehow here...
platformBrowserDynamicTesting()
);
const context = require.context('./', true, /\.spec\.ts$/);
context.keys().map(context);
__karma__.start();
I'm not exactly sure how to bootstrap our 1.5 application into the test environment, all I've gotten is Module 'myApp' is not available!, and my Google skills have failed trying to find an example.
I was hoping the bounty I added last night would mean I could log on this morning to a nice solution laid out for me. Alas, it did not. So instead I spent the day cruising around many SO answers and github issues getting it to work. I'm sorry I did not keep track of everything that helped me to credit them, but here is my solution. It is probably not ideal, but it is working so far so I hope it is a good start.
This github issue indicates that downgradeComponent isn't going to work for now, so I went with what I assume is an older technique using UpgradeAdapter. Note that this technique does not use initTestEnvironment. Here are the relevant snippets, with some explanations below:
// downgrade.ts:
export const componentsToDowngrade = {
heroDetail: HeroDetailComponent,
...
};
export function downgradeForApp() {
forOwn(componentsToDowngrade, (component, name) => {
app.directive(name!, downgradeComponent({ component }));
});
}
// main.ts:
downgradeForApp();
platformBrowser().bootstrapModuleFactory(AppModuleNgFactory).then((platformRef) => {
...
});
// test.ts:
require("../src/polyfills.ts");
require("zone.js/dist/proxy");
require('zone.js/dist/sync-test');
require("zone.js/dist/mocha-patch");
// test-helper.ts
let upgradeAdapterRef: UpgradeAdapterRef;
const upgradeAdapter = new UpgradeAdapter(AppModule);
forEach(componentsToDowngrade, (component, selectorName) => {
angular.module("app").directive(
selectorName!,
upgradeAdapter.downgradeNg2Component(component) as any,
);
});
export function useAdaptedModule() {
beforeEach(() => {
upgradeAdapterRef = upgradeAdapter.registerForNg1Tests(["app"]);
});
}
export function it(expectation: string, callback: () => void) {
test(expectation, (done) => {
inject(() => { }); // triggers some kind of needed initialization
upgradeAdapterRef.ready(() => {
try {
callback();
done();
} catch (ex) { done(ex); }
});
});
}
// hero-detail.component.spec.ts
import { it, useAdaptedModule } from "test-helpers/sd-app-helpers";
describe("", () => {
useAdaptedModule();
it("behaves as expected", () => { ... });
});
A few of the highlights from that code:
I downgrade components differently for tests than for the app, so I made a DRY list of them in downgrade.ts
I downgrade components for the main app from main.ts by calling downgradeForApp() as shown above (used with AOT for a production bundle), and also main-jit.ts, not shown above (used for development)
I showed the imports I needed to add to start integrating Angular components into my AngularJS tests. You may need more/different ones depending e.g. on whether your tests are asynchronous, or you use Jasmine instead of Mocha.
At the beginning of each test that needs to use downgraded components, I "bootstrap" things with useAdaptedModule() instead of beforeEach(angular.mock.module("app"));
I import an alternative it from my helpers, which wraps the it provided by Mocha. None of my tests are asynchronous; if you have some that are it may require tweaking. I do not know how it may need to be adapted for Jasmine.
A caveat: Instantiating the component must happen within an it callback so that it happens within upgradeAdapterRef.ready(...). Trying to do it within a beforeEach is too soon.

Jasmine: Testing promises. Is there something like $provide.value() for Angular2?

I'm trying to test a promise in Angular2, something like this:
this.fooService.getData().then(data => this.fooData = data);
This article shows how to test promises for Angular 1.
I know I can do this:
beforeEachProviders(() => [
provide(FooService, {useClass: fooService})
]);
But I'm hoping $provide.value is some other functionality I can use because I keep getting this error 'undefined' is not an object (evaluating 'this.fooService.getData().then')
It doesn't like the then.
You can use fakeAsync and flushMicrotasks or tick to pass the time in angular/core/testing.
One of my test might look something like
it('should set the active module', <any>fakeAsync(() => {
let expected = { id: 'id1' };
jasmine.createSpyObj<FooService>('FooService'['getData']);
spyService.fooService.and.callFake(() => {
return Promise.resolve(expected);
});
let component = new FooComponent(fooService);
component.getData();
flushMicrotasks();
expect(component.fooData).toEqual(expected);
}));

Angular 1.5 component unit testing with webpack

I'm trying to test a component with controller, with some bindings:
class AppSpecificController {
constructor() {
this.text = this.initialText ? this.initialText : 'No initial text has been specified.';
}
}
export const AppSpecificComponent = {
bindings: {
'initialText': '<bindInitialText'
},
templateUrl: '/app/components/app-specific/app-specific.html',
controller: AppSpecificController,
controllerAs: 'appSpecific'
};
export default AppSpecificComponent;
In my unit test file I don't want to load the complete application, just the things I need. So I figured to mock a module or just create a new one named something with mock, add the component to that module, and load that module:
import {AppSpecificComponent} from './app-specific.component';
describe('AppSpecificComponent', () => {
let controller;
let scope;
let $componentController;
beforeEach(() => {
angular.module('mock-module', [])
.component('appSpecific', AppSpecificComponent);
// this fails
module('mock-module');
inject((_$componentController_, $rootScope) => {
scope = $rootScope.$new();
$componentController = _$componentController_;
});
controller = $componentController('appSpecific', {$scope: scope}, {initialText: 'Some text'});
});
it('should test something', () => {
expect(true).toBeTruthy();
});
});
Creating the module mock-module is fine, but loading it fails, and obviously injecting stuff in the not-so-much-loaded-module fails, as well as creating a controller on which I can start testing. It would be nice to be able to test the components individually, separate from the application in which it runs.
Just using the new operator for the class AppSpecificController doesn't work since then the bindings you receive from the component are not there:
// fails, no bindings available in controller
controller = new AppSpecificController();
I found the answer somewhere else on StackOverflow, not sure where anymore. The answer however I was looking for:
angular.mock.module($provide => {
$provide.controller('SomeController', () => { ... });
$provide.constant('someConstant', 'some constant');
});

Resources