default spec file explanation that gets generated for component - jasmine

I created a component using ng generate component test and few files got generated. There is a file name test.component.spec.ts. It has this content.
import { async, ComponentFixture, TestBed } from '#angular/core/testing';
import { TestComponent } from './donations-pay.component';
describe('TestComponent ', () => {
let component: TestComponent ;
let fixture: ComponentFixture<TestComponent >;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ TestComponent]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(TestComponent );
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should be created', () => {
expect(component).toBeTruthy();
});
});
I am facing really hard time in understanding this. Kindly help me out.
Thank you

This can be confusing when you are getting started in Angular development. :)
A 'spec' file is used for doing unit testing of an Angular service, component, etc,
This is a CLI generated test file, set up for you by the Angular CLI when you generated the component TestComponent. Details are in the official Angular testing documentation, especially the section here.
The Angular testing docs are definitely the place to learn about testing, but there are other resources out there as well such as this.
I hope this helps.

Related

CDK AWS Lambda how to mock the asset provided

I am using jest for testing and I have a lambda function in my Stack I want to test.
Like this:
const lambda = new lambda.Function(this, "MyLambda", {
...
code: lambda.Code.fromAsset("../assets/lambda.zip"),
...
}
);
I want to test some of the properties but also if the lambda is in the stack. But when I run the test it complains that my lambda.zip doesn't exist. Which is fair enough, as it's built as part of the another build job.
Is there any way to inject or somehow mock the lambda's asset.
You can try using Code.fromInline() as it doesn't require any files on disk. Simply pass a dummy function code as a string.
lambda.Code.fromInline("exports.handler = async function(event, context) {}")
Based on kichik's idea to use Code.fromInline(), this worked for me:
import { InlineCode } from "#aws-cdk/aws-lambda"
jest.mock("#aws-cdk/aws-lambda", () => ({
...jest.requireActual("#aws-cdk/aws-lambda"),
Code: {
fromAsset: () => new InlineCode("foo"),
},
}))
Probably can be simplified with jest.spyOn, but I couldn't figure out how to make it work.
In a lot of simple scenarios, there's no need to bother with a complicated jest mocking.
beforeAll(() => {
Object.defineProperty(Code, 'fromAsset', {
value: () => Code.fromInline('test code'),
});
});

ServiceWorkers and Next.js: How would one integrate Service workers in production with a next.js app?

I am working with "next": "^9.3.2" and integrated a service worker (including this just in case someone else has a similar question):
File structure:
pages
public
static
serviceWorker.js
server
index.js
In server/index.js
async function start() {
const dev = process.env.NODE_ENV !== 'production';
const app = nextJS({ dev });
const server = express();
....
server.get('/serviceWorker.js', (req, res) => {
res.sendFile(path.resolve(__dirname, '../public', 'serviceWorker.js'));
});
/* later */
if (process.env.NODE_ENV === 'production') {
server.use(express.static('.next/static'));
server.get('/service-worker.js', (req, res) => {
res.sendFile(path.resolve(__dirname, 'public', 'serviceWorker.js'));
});
In public/serviceWorker.js
var currentCaches = {
css: 'CSS',
images: 'images'
};
const cacheFiles = {
css: [
// 'http://localhost:8016/semantic-ui-css/semantic.min.css',
// 'http://localhost:8016/font-awesome/css/font-awesome.min.css',
// 'http://localhost:8016/leaflet/dist/leaflet.css',
// 'http://localhost:8016/esri-leaflet-geocoder/dist/esri-leaflet-geocoder.css',
// 'http://localhost:8016/styles/styles.css',
// 'http://localhost:8016/leaflet-routing-machine/dist/leaflet-routing-machine.css'
],
images: [
// 'https://raw.githubusercontent.com/pointhi/leaflet-color-markers/master/img/marker-icon-2x-green.png',
// 'https://raw.githubusercontent.com/pointhi/leaflet-color-markers/master/img/marker-icon-2x-red.png',
// 'https://cdnjs.cloudflare.com/ajax/libs/leaflet/0.7.7/images/marker-shadow.png',
// 'http://localhost:8016/public/static/media/search#2x.png',
// 'http://localhost:8016/public/static/uploads/profile-avatars/placeholder.jpg'
]
};
self.addEventListener('install', function(event) {
console.log('Hello world from the Service Worker 🤙');
event.waitUntil(
Promise.all([
caches.open(currentCaches.css).then(cache => {
return cache.addAll(cacheFiles.css);
}),
caches.open(currentCaches.images).then(cache => {
return cache.addAll(cacheFiles.images);
})
])
);
});
Right now I am declaring the css paths in that object above* like I would in my _app.js file:
import 'semantic-ui-css/semantic.min.css';
import 'font-awesome/css/font-awesome.min.css';
import 'leaflet/dist/leaflet.css';
import 'esri-leaflet-geocoder/dist/esri-leaflet-geocoder.css';
import '../styles/styles.scss';
import 'leaflet-routing-machine/dist/leaflet-routing-machine.css';
Figure this would apply to the images too
So my question is since next.js spits out a static/css on a production build:
.next
cache
server
static
chunks
css < -----
476a94f2.d9a9e468.chunk.css
dbd51271.19268786.chunk.css
styles.9ca4e15c.chunk.css
How would one specifically have the serviceWorker know what these file names would be (as well as images, fonts, svg's etc)? As i'm assuming the numbers are to help with caching!
Thanks!
Generally speaking, you need some way of integrating with your web app's build process if you want to get a list of hashed URLs for use within your service worker.
Given that you're using Next.js, a plugin like next-offline can help by doing two things:
Integrating with your build process to get a list of your hashed URLs.
Generating the entirety of your service worker for you, using workbox-precaching under the hood to ensure that your URLs are properly cached and kept up to date.
You can implement something similar yourself if you'd prefer not to use next-offline, but you'd need to use something like next-assets-manifest to obtain the list of hashed URLs, write your own service worker, and figure out how to inject those URLs into the service worker.

NativeScript vue, vuex and urlhandler

Edit
I'm using https://github.com/hypery2k/nativescript-urlhandler to open a deep link within my app - using NativeScript vue, and vuex. It seems that in order to get at the methods needed to do routing [$navigateTo etc] this plugin needs to be set up slightly differently from the examples given in docs.
import Vue from "nativescript-vue";
import Vuex from "vuex";
Vue.use(Vuex);
import { handleOpenURL } from 'nativescript-urlhandler';
new Vue({
mounted() {
handleOpenURL( (appURL) => {
console.log(appURL)
// Settings is the variable that equals the component - in this case settings.
this.$navigateTo(Settings);
});
},
render: h => h("frame", [h(Home)]),
store: ccStore
}).$start();
handleOpenURL needs to be called within Mounted - then you can parse out the appURL and reference the page (component) that you wish to navigate to. I have been advised against calling handleOpenURL from within router - but I'm not sure why, and it works without error - and I have access to the methods for routing... so if anyone knows if this is a bad idea - please let me know :) Thanks!
All the stuff below that I wrote before has probably confused things - I'm referencing components within my vuex store to make them easily available from the router.
This is based on a solution by https://github.com/Spacarar - it can be found here: https://github.com/geodav-tech/vue-nativescript-router-example. It's a great solution because you don't have to include every single component within each component to use in navigation - it gives an almost vue router like experience.
I'm using https://github.com/hypery2k/nativescript-urlhandler to open a deep link within my app - however, I'm having problems opening the link.
In my app.js file, I have the following:
import Vue from "nativescript-vue";
import Vuex from "vuex";
Vue.use(Vuex);
....
import { handleOpenURL } from 'nativescript-urlhandler';
import ccStore from './store/store';
handleOpenURL(function(appURL) {
// I have hardwired 'Settings' in for testing purposes - but this would be the appURL
ccStore.dispatch('openAppURL', 'Settings');
});
....
new Vue({
render: h => h("frame", [h(Home)]),
store: ccStore
}).$start();
I'm storing the route state within vuex, and have various methods which work (clicking on a link loads the component). However, handleOpenURL exists outside of vue... so I've had to access vuex directly from within the handleOpenURL method. I've created an action specifically for this case - openAppURL.. it does exactly the same thing as my other methods (although I've consolidated it).
When clicking on an app link, I am NOT taken to the page within the app. I have put a console log within openAppURL and can see it is being called, and the correct route object is returned... it just doesn't open the page. The SetTimeOut is used because nextTick isn't available from within vuex.
I am at a loss on how to get the page to appear...
const ccStore = new Vuex.Store({
state: {
user: {
authToken: null,
refreshToken: null,
},
routes: [
{
name: "Home",
component: Home
},
{
name: "Log In",
component: Login
},
...
],
currentRoute: {
//INITIALIZE THIS WITH YOUR HOME PAGE
name: "Home",
component: Home //COMPONENT
},
history: [],
},
mutations: {
navigateTo(state, newRoute, options) {
state.history.push({
route: newRoute,
options
});
},
},
actions: {
openAppURL({state, commit}, routeName ) {
const URL = state.routes[state.routes.map( (route) => {
return route.name;
}).indexOf(routeName)];
return setTimeout(() => {
commit('navigateTo', URL, { animated: false, clearHistory: true });
}, 10000);
},
....
}
etc....
I have been advised to post my findings as the answer and mark it as correct. In order to use nativescript-urlhandler with vue, you must initialise the handler from within vue's mounted life cycle hook. Please see above for greater detail.
import Vue from "nativescript-vue";
import Vuex from "vuex";
Vue.use(Vuex);
import Settings from "~/components/Settings";
import { handleOpenURL } from 'nativescript-urlhandler';
new Vue({
mounted() {
handleOpenURL( (appURL) => {
console.log(appURL) // here you can get your appURL path etc and map it to a component... eg path === 'Settings. In this example I've just hardwired it in.
this.$navigateTo(Settings);
});
},
render: h => h("frame", [h(Home)]),
store: ccStore
}).$start();

Using classes in different .ts files in jasmine

I am writting simple test in jasmine framework. I have following files stored in one folder:
maintest.ts
helper.ts
Workflow1.ts
Workflow2.ts
Workflow files have content as following (example):
import {element, by, browser, protractor} from "protractor";
import {Helper} from "../../helper";
export class Workflow1/2
{
static Foo1() {
let element1;
let element2;
describe('check all fields', function () {
it('check foobar', function () {
element1.isVisible();
});
it('check foobar2', function () {
element2.isVisible();
});
}
static Foo2() {
let element3;
let element4;
describe('check all fields', function () {
it('check foobar', function () {
element4.isVisible();
});
it('check foobar2', function () {
element3.isVisible();
});
}
}
And the maintest.ts is:
import {browser} from "protractor";
import {Helper} from "./helper";
import {Workflow1} from "./Workflow1";
import {Workflow2} from "./Workflow2";
describe ('Regression Tests', function() {
beforeAll(function () {
console.log('====================Start');
});
describe('Basic workflow', function () {
Workflow1.Foo1();
Workflow1.Foo2();
Workflow2.Foo2();
Workflow2.Foo2();
});
});
but when I run it, nothing has run correctly - I get this error:
Error: Error while waiting for Protractor to sync with the page: "window.angular is undefined. This could be either because this is a non-angular page or because your test involves client-side navigation, which can interfere with Protractor's bootstrapping. See http://git.io/v4gXM for details"
but if I comment:
//Workflow1.Foo2();
//Workflow2.Foo2();
//Workflow2.Foo2();
the Workflow1.Foo1 works perfectly fine.
Can't I use different methods from different files? It works with helper, where I have login and logout methods...
I think I got this. My code was 'quite' long with different describes, when I minimalized it to 2, it started working :)
EDIT: As I mentioned in comment below, each method in Workflow1 and Workflow2 files must have at least one describe and at least one it inside - having only describe without it throws error

Testing a method that is subscribed to an observable - Angular 2

I want to test a method inside of an Angular 2 component that is subscribed to an observable that is returned from a method in a service. Here is the code for the service method in summary:
public create(user: User): Observable<any> {
return this.http.post(this._api.create,
JSON.stringify(user), {
headers: this.apiConfig.getApiHeaders()
}).map((res: Response) => res.json());
}
It's easy to unit test this method because it returns an observable so I can just subscribe to it. But I want to test the method in the component that is already subscribed to this:
public onSubmit(user: User): void {
this._authentication.create(user).subscribe((token) => {
localStorage.setItem('token', token);
this.router.navigate(['/Home']);
});
}
Heres my spec so far but when I try to spyOn the localStorage.setItem it comes back as not being called. My understanding is it's probably checking to see if it's been called before it's actually been called.
it('Should login a user and on success store a token in localStorage',
injectAsync([TestComponentBuilder], (tcb) => {
return tcb.createAsync(Login).then((fixture) => {
let instance = fixture.debugElement.componentInstance;
localStorage.clear();
spyOn(localStorage, 'setItem');
instance.onSubmit({userId: 'some#email.com', password: 'password', siteName: 'sample'});
expect(localStorage.setItem).toHaveBeenCalled();
});
})
);
I'm wondering if I need to mock out the this._authentication.create method to return a new observable with a mock response in it?
After more research a few articles indicated that I do need to mock out the service and return an Observable.of() which runs synchronously to solve the problem, ill copy the code below. This however still doesn't work, I've been working on this most of the day, I don't feel this should be that hard, any help appreciated.
class MockAuthentication extends Authentication {
public create(user: Object): Observable<any> {
return Observable.of({'test': 'test'});
}
}
Ok so it's taken me most of the day but I finally cracked it. Instead of using the injectAsync and TestComponentBuilder to set up the spec I just need to use inject and inject the component in just like you do a service. This seems fine because I don't need to test anything in the view like events.
Heres the final spec that does work:
it('Should set token in localStorage, set the new user,
and navigate to home page on succesful login',
inject([Login], (login) => {
login.router.config([ { path: '/', name: 'Home', component: Home }]);
spyOn(localStorage, 'setItem');
spyOn(login._currentUser, 'set');
spyOn(login.router, 'navigate');
login.onSubmit({ userId: 'some#email.com', password: 'password', siteName: 'sample' });
expect(localStorage.setItem).toHaveBeenCalledWith('token', 'newToken');
expect(login._currentUser.set).toHaveBeenCalledWith({ 'test': 'one' });
expect(login.router.navigate).toHaveBeenCalledWith(['/Home']);
}));
Hope this might help someone in the future.
I guess you want to inject a mock Router instance to your component and then after navigate(['/Home']) was called on the mock Router, you check if localStorage.setItem(...) was called.
See my gist here.
Basically you can do several things here. First of all, stub your http call (I'm guessing from a service) with a simple observable response of the token (or other response) you want.
service.stub.ts
export class MyStub {
public create(user: User): Observable<User> {
return Observable.of('insert test token here');
}
// other stubbed methods ...
}
And then inside your test:
myComp.spec.ts
let comp: MyComponent;
let fixture: ComponentFixture<MyComponent>;
let sst: ServiceStub;
describe('MyComponent', () => {
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [MyComponent],
schemas: [NO_ERRORS_SCHEMA]
}).overrideComponent(OnboardFacilityNewComponent, {
set: {
providers: [
{ provide: MyService, useClass: ServiceStub },
]
}
})
.compileComponents()
.then(() => {
fixture = TestBed.createComponent(MyComponent);
comp = fixture.componentInstance;
st = fixture.debugElement.injector.get(MyService);
});
}));
it('should submit new onboardFacility', fakeAsync(() => {
const sst = spyOn(sst, 'create').and.returnValue(
Observable.of('some token here')
);
comp.onSubmit(testUser);
fixture.detectChanges();
expect(comp.token).toEqual('some token here');
expect(spy.calls.any()).toEqual(true);
}));
});
Here, you can simply replace actual data with test data to test the behavior of your testing, rather then your testbed, your services, localStorage, etc. Obviously the test I wrote here assumes you would store the token returned from your service in your component, rather then localStorage (though there is a way to do that), but I'm just simply to show the concept rather then your specific use case.
In your use case you'll also need to the stub the router, which you can learn how to do here.

Resources