Jasmine toHaveBeenCalledWith doesn't work with fetch API and URLSearchParams from es6 - http

I wrote a test for a POST call with Fetch API. To check if I send good parameters I user jasmine toHaveBeenCalledWith and that's work fine with a string body.
When I change this string to URLSearchParams object from es6 not Angular. The test becomes red however the expected call and the call are exactly equals.
Have you got an idea?
Test:
...
mockHttp = {post: null} as Http;
let headers = new Headers();
headers.append('Content-Type', 'application/x-www-form-urlencoded');
let searchParams = new URLSearchParams();
searchParams.set("refresh_token", token.refreshToken);
searchParams.set("grant_type", "refresh_token");
searchParams.set("client_id", "DriverApp");
expect(mockHttp.post).toHaveBeenCalledWith("ENDPOINT_API_URL_REFRESH_TOKEN", searchParams, {headers: headers})
...

Use custom equality tester of jasmine :
In the test file add this function :
function customEquality(expected: any, value: any): boolean {
return JSON.stringify(expected).trim() === JSON.stringify(value).trim();
}
In a beforeEach add this line: jasmine.addCustomEqualityTester(customEquality);
And use normally toHaveBeenCalledWith

Related

oak on Deno - How do I build a URL to a route?

I come from a land of ASP.NET Core. Having fun learning a completely new stack.
I'm used to being able to:
name a route "orders"
give it a path like /customer-orders/{id}
register it
use the routing system to build a URL for my named route
An example of (4) might be to pass a routeName and then routeValues which is an object like { id = 193, x = "y" } and the routing system can figure out the URL /customer-orders/193?x=y - notice how it just appends extraneous key-vals as params.
Can I do something like this in oak on Deno?? Thanks.
Update: I am looking into some functions on the underlying regexp tool the routing system uses. It doesn't seem right that this often used feature should be so hard/undiscoverable/inaccessible.
https://github.com/pillarjs/path-to-regexp#compile-reverse-path-to-regexp
I'm not exactly sure what you mean by "building" a URL, but the URL associated to the incoming request is defined by the requesting client, and is available in each middleware callback function's context parameter at context.request.url as an instance of the URL class.
The documentation provides some examples of using a router and the middleware callback functions that are associated to routes in Oak.
Here's an example module which demonstrates accessing the URL-related data in a request:
so-74635313.ts:
import { Application, Router } from "https://deno.land/x/oak#v11.1.0/mod.ts";
const router = new Router({ prefix: "/customer-orders" });
router.get("/:id", async (ctx, next) => {
// An instance of the URL class:
const { url } = ctx.request;
// An instance of the URLSearchParams class:
const { searchParams } = url;
// A string:
const { id } = ctx.params;
const serializableObject = {
id,
// Iterate all the [key, value] entries and collect into an array:
searchParams: [...searchParams.entries()],
// A string representation of the full request URL:
url: url.href,
};
// Respond with the object as JSON data:
ctx.response.body = serializableObject;
ctx.response.type = "application/json";
// Log the object to the console:
console.log(serializableObject);
await next();
});
const app = new Application();
app.use(router.routes());
app.use(router.allowedMethods());
function printStartupMessage({ hostname, port, secure }: {
hostname: string;
port: number;
secure?: boolean;
}): void {
if (!hostname || hostname === "0.0.0.0") hostname = "localhost";
const address =
new URL(`http${secure ? "s" : ""}://${hostname}:${port}/`).href;
console.log(`Listening at ${address}`);
console.log("Use ctrl+c to stop");
}
app.addEventListener("listen", printStartupMessage);
await app.listen({ port: 8000 });
In a terminal shell (I'll call it shell A), the program is started:
% deno run --allow-net so-74635313.ts
Listening at http://localhost:8000/
Use ctrl+c to stop
Then, in another shell (I'll call it shell B), a network request is sent to the server at the route described in your question — and the response body (JSON text) is printed below the command:
% curl 'http://localhost:8000/customer-orders/193?x=y'
{"id":"193","searchParams":[["x","y"]],"url":"http://localhost:8000/customer-orders/193?x=y"}
Back in shell A, the output of the console.log statement can be seen:
{
id: "193",
searchParams: [ [ "x", "y" ] ],
url: "http://localhost:8000/customer-orders/193?x=y"
}
ctrl + c is used to send an interrupt signal (SIGINT) to the deno process and stop the server.
I am fortunately working with a React developer today!
Between us, we've found the .url(routeName, ...) method on the Router instance and that does exactly what I need!
Here's the help for it:
/** Generate a URL pathname for a named route, interpolating the optional
* params provided. Also accepts an optional set of options. */
Here's it in use in context:
export const routes = new Router()
.get(
"get-test",
"/test",
handleGetTest,
);
function handleGetTest(context: Context) {
console.log(`The URL for the test route is: ${routes.url("get-test")}`);
}
// The URL for the test route is: /test

Deno - How to fetch data from distant API or URL?

I'm wondering how I can get data from other servers and API with deno ? Everything in the documentation teach me about making http servers and read files from local source. But I can't find anything useful about reading something on the network.
How can read JSON data from the Stripe API ? Or if I want to read a HTML file with text inside ?
Thank you for your time!
I am just giving you an example of the GET request for fetching repositories of Github.
You can change the URL and Request Configuration as per your need.
In the code given below, I am calling another API of Github. By using the fetch() method you can do that.
fetch() method first takes the URL as the first parameter and the next parameter is RequestInit which takes the request method type, headers, body, etc and at the end returning JSON response of that API call.
const githubResponse = async (): Promise<any> => {
const response = await fetch("https://api.github.com/search/repositories?q=android", {
method: "GET",
headers: {
"Content-Type": "application/json",
},
});
return response.json(); // For JSON Response
// return response.text(); // For HTML or Text Response
}
console.log(await githubResponse());
I have written the above code in a ts file named Testing.ts . So, you can run the above code by the command given below:
deno run --allow-net Testing.ts
Next, I am giving you a sample POST request code:
const githubResponse = async (): Promise<any> => {
const body: URLSearchParams = new URLSearchParams({
q: "AvijitKarmakar",
});
const response = await fetch("https://api.github.com/search/repositories", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: body
});
return response.json();
// return response.text(); // For HTML or Text Response
}
console.log(await githubResponse());
You can see that I have created a body object and passed it in the RequestInit through the body parameter and also changed the request method type to POST.
You'll need to do a HTTP Request, for that in Deno you use fetch, the same Web API the browsers use.
To read JSON response:
const res = await fetch('https://api.stripe.com');
const data = await res.json();
If you want HTML:
const res = await fetch('https://example.com');
const html = await res.text();
// Now you can use some HTML parsing lib
fetch requires the --allow-net flag.
Deno strives to be as close to the existent browser API as possible.
That means, you can use fetch. Example:
// fetch-kitten.ts
fetch("https://placekitten.com/200/300").then(async (d) =>
Deno.writeFile("kitten.jpg", new Uint8Array(await d.arrayBuffer()))
);
CLI:
deno run --allow-net --allow-write fetch-kitten.ts
Reference

Loopback 4: Test problems with Sinon and injections

We have trying to do a test with loopback. The test involve to call the google API and we want to mock it with Sinon.
The Controller:
[...]
In the constructor:
#inject('services.OAuth2Service')
private oauth2Service: OAuth2Service
[...]
In the endpoint:
#post('/user-accounts/authenticate/oauth2', {
async authenticateOauth2(
#requestBody() oauthRequest: OAuthId,
#inject(RestBindings.Http.REQUEST) _req: Request,
): Promise<AccessToken> {
const email = await this.oauth2Service.getOAuth2User(oauthRequest); // The method to mock.
....
}
The test:
it('oauth2 authentication with google', async () => {
//Create a spy for the getOAuth2User function
inject.getter('services.OAuth2Service');
var oauth2Service: OAuth2Service;
var setOauthSpy = sinon.spy(oauth2Service, "getOAuth2User"); // Error: Variable 'oauth2Service' is used before being assigned
const res = await client
.post('/user-accounts/authenticate/oauth2')
.set('urlTenant', TEST_TENANT_URL1A)
.set('userType', TEST_USERTYPE1)
.send({
code: TEST_GOOGLE_AUTH2_CODE_KO,
providerId: TEST_GOOGLE_PROVIDER,
redirectUri: TEST_GOOGLE_REDIRECT_URI,
})
.expect(401);
expect(res.body.error.message).to.equal('The credentials are not correct.');
setOauthSpy.restore();
});
How can we test this method? how can we test an endpoint who involves an injection in the constructor in loopback? Please, we need any help.
I see two options:
Before running the test, create a loopback context, bind your stub to "services.OAuth2Service" and use that context to create the controller you want to test.
default value (probably not what you want)
In the place where you use #inject, you provide a default value (and possibly indicate the dependency is optional), e.g. like this:
#inject('services.OAuth2Service', { optional: true })
private oauth2Service: OAuth2Service = mockOAuth2Service,
In other places this might come handy for you, but you should probably not pollute your default production code with defaults to test objects.

HTTP GET and POST, how to implement in Angular 2 to be able to list, add and remove objects

Okay, so I am new to working with HTTP and actually getting some data from the server. Been sifting through a lot of tutorials, examples and questions asked here, but I am not finding what I want. All tutorials I've found only shows how to retrieve and add some data.
So based on those examples I've managed to retrieve data:
service:
getCases(){
return this.http.get('someUrl');
}
Case component constructor:
this._service.getCases()
.map((res: Response) => res.json())
.subscribe(cases => this.cases = cases);
Adding cases
service:
public saveCase(case: case) {
let body = JSON.stringify(case);
let headers = new Headers({ 'Content-Type': 'application/json' });
let options = new RequestOptions({ headers: headers });
return this.http.post('someUrl', body, options)
.map(this.extractData)
.catch(this.handleError)
.subscribe(case => this.cases.push(case));
}
Case Component:
saveCase() {
let case = new Case(this.id, this.name, this.date)
this._service.saveCase(case);
this.name = '';
}
Okay, so I have and Array "Cases" which contains Case objects. Getting the data from the server displays the cases like I want them to. When I add a new case it gets sent to the server, but how do I get the Array updated when I add a new Case. Because now the new case appears only after I refresh the browser.
Second question is that the user can click a case and it then routes to a detail list where the user can add steps and feedback. If it matters, case has the attributes id, name, date and an array of steps, at this point the array is empty. The step object is it's own class and the object contains an array of feedback. Feedback is also an own class and the object has two attributes, which are both strings. So it's all nested. When I click the case, it does route to the detail page, but there the case name should be printed and it doesn't. It also shows my button for adding steps, but it does nothing. Obviously I'm missing something in my methods, but I have no clue to as what to do. As a comment I can say that before adding the http in my code it all worked as it should. Here are the methods, that are probably missing something:
Case Component:
gotoDetail(case: Case) {
this._router.navigate(['CaseDetail', {"id": case.name}]);
}
Service:
public getById(id: string): Case {
for (let case of this.cases) {
if (case.id === id) {
return case;
}
}
return null;
}
Then there is the matter of syntax for removing cases, haven't found an example that works for me yet, I've tried a bunch... among others the example links provided by #shershen below. None works. The original methods I have, that should be changed to work with http:
Component:
removeSearchCase(case: Case) {
this._service.removeCase(case);
}
Service:
public removeCase(value: Case): void {
let index = this.cases.indexOf(value);
this.cases.splice(index, 1);
}
So the case removal is with post.
And about the backend I can say as much that I only have the following three posts and gets:
getCases (GET), saveCase (also works as updating the case)(POST) and removeCase (POST).
It's hard to debug without sample demo, however the descriptions quite detailed. I am adding some points that may fix the problem while improving the code structure:
First, you should move the request subscription/observing into the service methods; that will encapsulate the request handling logic in service layer:
//service.ts
#Injectable()
export class service {
getCases(){
if (!this.request) {
this.request = this.http.get('/assets/data.json')
.map((response: Response) => response.json())
.map((data: string[]) => {
this.request = null;
return this.names = data;
});
}
return this.request;
}
}
Second, you need to create an instance of your service in your Component's constructor instead of using it as a static method of the service:
//component.ts
import {MyService} from 'PATH_TO_YOUR_SERVICE';
class CaseComponent {
constructor(private _service : MyService){
//other stuff..
}
getData(){
this._service.getCases()
}
}
Additional references:
Official "Getting and Saving Data with HTTP"
Service example with Observables (with Firebase, but still)
Simple service in Angular2 seed project
I think you should put your cases Array in the CaseComponent:
case.component.ts:
cases: Case[];
constructor(private caseService: CaseService){}
getCases() {
this.caseService.getCases()
.subscribe(cases => this.cases = cases);
}
saveCase() {
let case = new Case(this.id, this.name, this.date);
this.caseService.saveCase(case)
.subscribe(case => this.cases = [...this.cases, case]);
}
case.service.ts:
getCases() {
return this.http.get(this.casesUrl)
.map(this.extractData)
.catch(this.handleError);
}
saveCase (case: Case) {
let body = JSON.stringify({ case });
let headers = new Headers({ 'Content-Type': 'application/json' });
let options = new RequestOptions({ headers: headers });
return this.http.post(this.casesUrl, body, options)
.map(this.extractData)
.catch(this.handleError);
}
Then try to change "name" to "id" in gotoDetail:
gotoDetail(case: Case) {
this._router.navigate(['CaseDetail', {"id": case.id}]);
}

create a synchronous http.get()

Im trying to handle a login via promises and http.get but i fail so hard I get following error :
Object doesn't support property or method 'toPromise'
My code is :
return this.http.get('http://localhost:5000/login/', {
headers: authHeader
}).map((response) => {
return response.json()
}).toPromise(null);
ive got it from :
https://github.com/johnpapa/angular2-there-and-back-again/blob/master/src/app/core/character.service.ts
UPDATE :
JohnPapa updated his project my friends
https://github.com/johnpapa/angular2-there-and-back-again/blob/master/app/core/character.service.ts
I wonder if you actually use promise since the HTTP support of Angular relies on Observables.
To get the response, you simply need to return the observable for your call:
getSomething() {
return this.http.get('http://localhost:5000/login/', {
headers: authHeader
}).map((response) => {
return response.json()
})
}
When calling the method, you can then register callbacks using the subscribe method:
getSomething().subscribe(
data => handleData(data),
err => reject(err));
If you really want to use promises (with the toPromise method), you should import this:
import 'rxjs/Rx';
See this issue for more details: https://github.com/angular/angular/issues/5632#issuecomment-167026172.
Otherwise, FYI calls aren't synchronous regarding HTTP in browsers...
Hope it helps you,
Thierry
If you want, you can use a TypeScript wrapper for sync-request library.
This TypeScript strongly-typed, fluent wrapper library is ts-sync-request.
ts-sync-request on npm
With this library, you can make sync http calls like below:
Your TypeScript classes:
class Request
{
Email: string;
}
class Response
{
isValid: boolean;
}
Install package in project:
npm i ts-sync-request
Then
import { SyncRequestClient } from 'ts-sync-request/dist'
GET:
let email = "jdoe#xyz.com";
let url = "http://localhost:59039/api/Movies/validateEmail/" + email;
var response = new SyncRequestClient()
.addHeader("Authorization", "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1NDc2OTg1MzgsIm5iZiI6MTU0NzY5NDIxOCwiaHR0cDovL3NjaGVtYXMueG1sc29hcC5vcmcvd3MvMjAwNS8wNS9pZGVudGl0eS9jbGFpbXMvbmFtZSI6InN0cmluZyIsImh0dHA6Ly9zY2hlbWFzLm1pY3Jvc29mdC5jb20vd3MvMjAwOC8wNi9pZGVudGl0eS9jbGFpbXMvcm9sZSI6InN0cmluZyIsIkRPQiI6IjEvMTcvMjAxOSIsImlzcyI6InlvdXIgYXBwIiwiYXVkIjoidGhlIGNsaWVudCBvZiB5b3VyIGFwcCJ9.qxFdcdAVKG2Idcsk_tftnkkyB2vsaQx5py1KSMy3fT4")
.get<Response>(url);
POST:
let url = "http://localhost:59039/api/Movies/validateEmailPost";
let request = new Request();
request.Email = "jdoe#xyz.com";
var response = new SyncRequestClient()
.addHeader("Authorization", "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1NDc2OTg1MzgsIm5iZiI6MTU0NzY5NDIxOCwiaHR0cDovL3NjaGVtYXMueG1sc29hcC5vcmcvd3MvMjAwNS8wNS9pZGVudGl0eS9jbGFpbXMvbmFtZSI6InN0cmluZyIsImh0dHA6Ly9zY2hlbWFzLm1pY3Jvc29mdC5jb20vd3MvMjAwOC8wNi9pZGVudGl0eS9jbGFpbXMvcm9sZSI6InN0cmluZyIsIkRPQiI6IjEvMTcvMjAxOSIsImlzcyI6InlvdXIgYXBwIiwiYXVkIjoidGhlIGNsaWVudCBvZiB5b3VyIGFwcCJ9.qxFdcdAVKG2Idcsk_tftnkkyB2vsaQx5py1KSMy3fT4")
.post<Request, Response>(url, request);
Hope this helps.

Resources