how can I test a component that have a function that use async await? - jasmine

I have a problem.
I have a function that use a async await to get data, but in the moment that I try test it. I don't know how to do that.
I tried this
this is my component
async loadTodos() {
try {
const todosData = await this.testService.loadTodos();
this.todos = todosData;
console.log('posts', todosData);
} catch (error) {
console.log(error);
}
}
this is my service file
export class TestService {
constructor(private readonly http: HttpClient) {}
async loadTodos(): Promise<any[]> {
return this.http
.get<any[]>('https://jsonplaceholder.typicode.com/todos')
.toPromise();
}
}
and finally this is my test
it('test', () => {
const response2 = [
{
userId: 1,
id: 1,
title: 'delectus aut autem',
completed: false,
},
{
userId: 1,
id: 2,
title: 'quis ut nam facilis et officia qui',
completed: false,
},
];
testServiceSpy.loadTodos.and.returnValue(of(response2).toPromise());
component.loadTodos();
expect(component.todos).toEqual(response2);
});
I don't have error in the sintax, but in the terminal I see this.
TypeError: Cannot read properties of undefined (reading 'loadTodos')

This indicates that testServiceSpy is null. What you can do is check that you're setting it in the beforeEach or do something like this.
You will also need to make your test itself async and await the call to the component.
it('test', async () => {
// grab the instance of the TestService
const serviceSpy = TestBed.inject(TestService) as jasmine.SpyObj<TestService>;
const response2 = [
{
userId: 1,
id: 1,
title: 'delectus aut autem',
completed: false,
},
{
userId: 1,
id: 2,
title: 'quis ut nam facilis et officia qui',
completed: false,
},
];
serviceSpy.loadTodos.and.returnValue(of(response2).toPromise());
// await the call to the component
await component.loadTodos();
expect(component.todos).toEqual(response2);
});

Related

How to enable graphql subscription in loopback 4 with openapi-to-graphql

as per the title, I am having problem trying to enable graphql subscription in my loopback 4 application.
Here is my code that I've done so far.
index.ts
export async function main(options: ApplicationConfig = {}) {
const app = new BackendLb4Application(options)
await app.boot()
await app.start()
const url = app.restServer.url;
const oas: Oas3 = <Oas3><unknown>await app.restServer.getApiSpec()
const {schema} = await createGraphQLSchema(oas, {
operationIdFieldNames: true,
baseUrl: url,
createSubscriptionsFromCallbacks: true,
})
const handler = graphqlHTTP( (request:any, response:any, graphQLParams: any) => ({
schema,
pretty: true,
graphiql: true
}))
app.mountExpressRouter(graphqlPath, handler);
const pubsub = new PubSub()
const ws = createServer(app);
ws.listen(PORT, () => {
new SubscriptionServer(
{
execute,
subscribe,
schema,
onConnect: (params: any, socket: any, ctx: any) => {
console.log(params, 'here on onconnect')
// Add pubsub to context to be used by GraphQL subscribe field
return { pubsub }
}
},
{
server: ws,
path: '/subscriptions'
}
)
})
return app
}
Here is my schema
type Subscription {
"""
Equivalent to PATCH onNotificationUpdate
"""
postRequestQueryCallbackUrlApiNotification(secondInputInput: SecondInputInput): String
"""
Equivalent to PATCH onNotificationUpdate
"""
postRequestQueryCallbackUrlOnNotificationUpdate(firstInputInput: FirstInputInput): String
}
Here is an example of my controller
#patch('/notification-update', {
operationId: 'notificationUpdate',
description: '**GraphQL notificationUpdate**',
callbacks:[ {
onNotificationUpdate: {
//'{$request.query.callbackUrl}/onNotificationUpdate': {
post: {
requestBody: {
operationId: 'notificationUpdateCallback',
description: 'rasjad',
content: {
'application/json': {
schema: {
title: "firstInput",
type: 'object',
properties: {
userData: {
type: "string"
}
}
}
}
}
},
responses: {
'200': {
description: 'response to subscription',
}
}
}
},
// }
}],
responses: {
'200': {
description: 'Notification PATCH success count',
content: {'application/json': {schema: CountSchema}},
},
},
})
async updateAll(
#requestBody({
content: {
'application/json': {
schema: getModelSchemaRef(Notification, {partial: true}),
},
},
})
notification: Notification,
#param.where(Notification) where?: Where<Notification>,
): Promise<Count> {
return this.notificationRepository.update(notification, where);
}
Ive defined the callbacks object in my controller which will then create a subscription in my schema. Tested it out on graphiql but did not work.
I am not sure where to go from here. Do I need a custom resolver or something? Not sure.
Appreciate it if anyone could help on this.
Just in case someone else is looking to do the same thing.
I switched out graphqlHTTP with Apollo Server to create my graphql server.
So my final index.ts looks like this.
export async function main(options: ApplicationConfig = {}) {
const lb4Application = new BackendLb4Application(options)
await lb4Application.boot()
await lb4Application.migrateSchema()
await lb4Application.start()
const url = lb4Application.restServer.url;
const graphqlPath = '/graphql'
// Get the OpenApiSpec
const oas: Oas3 = <Oas3><unknown>await lb4Application.restServer.getApiSpec()
// Create GraphQl Schema from OpenApiSpec
const {schema} = await createGraphQLSchema(oas, {
strict: false,
viewer: true,
baseUrl: url,
headers: {
'X-Origin': 'GraphQL'
},
createSubscriptionsFromCallbacks: true,
customResolvers: {
"lb4-title": {
"your-path":{
patch: (obj, args, context, info) => {
const num = Math.floor(Math.random() * 10);
pubsub.publish("something", { yourMethodName: {count: num} }).catch((err: any) => {
console.log(err)
})
return {count: 1}
}
}
}
},
customSubscriptionResolvers: {
"lb4-title" : {
"yourMethodName": {
post: {
subscribe: () => pubsub.asyncIterator("something"),
resolve: (obj: any, args: any, context, info) => {
console.log(obj, 'obj')
}
}
}
}
}
})
const app = express();
const server = new ApolloServer({
schema,
plugins: [{
async serverWillStart() {
return {
async drainServer() {
subscriptionServers.close();
}
};
}
}],
})
const subscriptionServers = SubscriptionServer.create(
{
// This is the `schema` we just created.
schema,
// These are imported from `graphql`.
execute,
subscribe,
},
{
server: lb4Application.restServer.httpServer?.server,
path: server.graphqlPath,
//path: server.graphqlPath,
}
);
await server.start();
server.applyMiddleware({ app, path: "/" });
lb4Application.mountExpressRouter('/graphql', app);
return lb4Application
}
Also you will need to define the callbacks object in your controller like so.
#patch('/something-update', {
operationId: 'somethingUpdate',
description: '**GraphQL somethingUpdate**',
callbacks:[
{
yourMethodName: {
post: {
responses: {
'200': {
description: 'response to subscription',
content: {'application/json': {schema: CountSchema}},
}
}
}
},
}
],
responses: {
'200': {
description: 'Something PATCH success count',
content: {'application/json': {schema: CountSchema}},
},
},
})
async updateAll(
#requestBody({
content: {
'application/json': {
schema: getModelSchemaRef(Something, {partial: true}),
},
},
})
something: Something,
#param.where(Something) where?: Where<Something>,
): Promise<Count> {
return this.somethingRepository.updateAll(something, where);
}
And that is it. You can test it out from the GraphQL Playground and play around with the subscriptions.
For the time being, I am fine with defining customResolvers and customSubscriptionResolvers but I'm pretty sure I can automate this two objects from the controllers.
Cheers!

Get first object from array of object vuejs

How can i get array of object first object id?
here's my arrray's content
0: {id: 1, user_id: 1, user2_id: 2, created_at: "2021-03-22T16:37:10.000000Z",…}
1: {id: 7, user_id: 1, user2_id: 3, created_at: "2021-03-24T16:24:47.000000Z",…}
2: {id: 8, user_id: 1, user2_id: 1, created_at: "2021-03-24T18:19:21.000000Z",…
<script>
export default
{
data(){
return{
convs_id: [],
convs: [],
}
},
created(){
this.fetchConversation();
this.convs_id = this.convs[0].id;
console.log(this.convs_id);
},
methods:
{
fetchConversation()
{
axios.get('getConvs').then(response=>{
this.convs = response.data;
});
}
}
}
this.convs gets populated when the call to get the data has resolved. So in order to use it you have to wait for that promise to resolve.
To be able to wait on a promise, you have to return it. So fetchConversation() needs to return axios.get() (which is the promise you'll be waiting on):
methods:{
fetchConversation() {
return axios.get('getConvs').then(response => {
this.convs = response.data;
});
}
}
Now, that fetchConversation() returns the promise, you have two ways of waiting on it: either make created async and use await:
async created() {
await this.fetchConversation();
console.log(this.convs[0]);
}
or call .then() method on the promise:
created() {
this.fetchConversation().then(() => {
console.log(this.convs[0]);
})
}
In request success callback extract the first item :
export default
{
data(){
return {
convs_id: null,
convs: [],
}
},
created(){
this.fetchConversation();
},
methods:
{
fetchConversation()
{
axios.get('getConvs').then(response=>{
this.convs = response.data;
let [firstConvs]=this.convs;// es6 syntax of destructing the array
this.convs_id = firstConvs.id;
});
}
}
}

Return resolved promise

I have a function in my strapi where I map the data and run another query inside.
async search(ctx) {
const authors = [
{
id: 1,
name: "author 1"
},
{
id: 2,
name: "author 2"
},
]
authors.map((data, i) => {
var books = this.getBooks(data.id)
console.log(books)
// some stuffs here
})
},
getBooks: async function (authorId) {
return await strapi.query('books').find({ userId: authorId })
}
The console.log('books') display Promise { <pending> }. I'm not very familiar with the promise stuff but I tried something like below but guess it's not the right way. It's still returning the same promise pending.
getBooks: async function (authorId) {
const books = await strapi.query('books').find({ userId: authorId })
return Promise.resolve(books)
}
Found this discussion here . It appears that map doesn't work with promises. I switched to for loop as mentioned in the discussion and it's working now.
async search(ctx) {
const authors = [
{
id: 1,
name: "author 1"
},
{
id: 2,
name: "author 2"
},
]
for (let data of authors) {
const books = await strapi.query('books').find({ userId: data.id })
console.log(books)
// some stuffs here
}
}

How to emit once the observable data variable is not NULL

I'm new to RxJS, and I'm trying to figure out how to observe the data when it become available. I'm using Nuxt SSR and I'm fetching data from Firebase. The initial post value is set to null, and once the data object become available, it should run the head() function only once. I get this type error.
Cannot read property 'pipe' of null
If I initiate post: {}, as empty object, I get this type error.
post$.pipe is not a function
Appreciate if I can get some help or guidance.
// page\:post.vue
<script>
import { mapState, mapActions } from 'vuex'
import { take } from 'rxjs/operators'
export default {
fetch() {
this.fetchPost()
},
computed: {
...mapState('posts', ['post']),
},
methods: {
...mapActions('posts', ['fetchPost']),
},
head() {
const post$ = this.post
post$.pipe(take(1)).subscribe((post) => {
return {
title: this.post.title,
link: [{ rel: 'canonical', href: this.post.canonical }],
meta: [
{ hid: 'name', itemprop: 'name', content: this.post.title },
{
hid: 'description',
itemprop: 'description',
content: this.post.content,
},
],
}
})
},
}
</script>
// store\posts.js
export const state = () => ({
post: null,
})
export const mutations = {
setPost(state, payload) {
state.post = payload
},
}
export const actions = {
async fetchPost({ commit }, key) {
const doc = await postsCollection.doc(key).get()
if (doc.exists) commit('setPost', doc.dat())
},
}
Edit
Using Subject. However, there is still issue where the meta tags are generated before the post data is set.
// page\:post.vue
<script>
import { mapState, mapActions } from 'vuex'
import { take } from 'rxjs/operators'
export default {
fetch() {
this.fetchPost()
},
computed: {
...mapState('posts', ['post']),
},
methods: {
...mapActions('posts', ['fetchPost']),
},
head() {
const postSubject = new Subject()
const post = postSubject.asObservable()
postSubject.next(this.post)
post.subscribe((post) => {
return {
title: post.title,
link: [{ rel: 'canonical', href: post.canonical }],
meta: [
{ hid: 'name', itemprop: 'name', content: post.title },
{
hid: 'description',
itemprop: 'description',
content: post.content,
},
],
}
})
},
}
</script>
// store\posts.js
export const state = () => ({
post: null,
})
export const mutations = {
setPost(state, payload) {
state.post = payload
},
}
export const actions = {
async fetchPost({ commit }, key) {
const doc = await postsCollection.doc(key).get()
if (doc.exists) commit('setPost', doc.dat())
},
}
You need to subscribe to an Observable. As I understood, in your case this.post is not type of an Observable.
As this.post is populated at some point of time, you need to subscribe to an observable which should emit data when you say this.post is now populated with data. For that you can use a Subject.
See this example: link

Ngrx unit test jasmine reducer how to compare state

I've done this simple test
// Mocks
const loginResponseData: LoginResponseDto = {
auth: { id: 1, username: 'me', roles: [] },
token: {
token: 'abc',
expirationEpochSeconds: 12345
}
};
describe('AuthReducer', () => {
describe('loginSuccess', () => {
it('should show loginResponseData state', () => {
const createAction = loginSuccess({ payload: loginResponseData });
const result = reducer(initialState, createAction);
console.log('AUTH', result);
// How Can I test this?
//expect(result).toEqual(loginResponseData);
});
});
});
export const initialState: State = {
error: null,
loading: false,
registered: false,
payload: null
};
const authReducer = createReducer(
initialState,
on(AuthActions.loginSuccess, (state, { payload }) => {
return {
...state,
error: null,
loading: false,
payload
};
})
);
How I can test result with loginResponseData?
result of a reducer is a new state.
You need to share code of your reducer for the right answer. Or to share what console.log outputs.
Because in your question the code is correct
describe('AuthReducer', () => {
describe('loginSuccess', () => {
it('should show loginResponseData state', () => {
const actionPayload: LoginResponseDto = {
auth: { id: 1, username: 'me', roles: [] },
token: {
token: 'abc',
expirationEpochSeconds: 12345
}
};
// for the test it's fine to have an empty object
const initialState: any = {
};
// check what should be changed
const expectedState = {
payload: {
auth: { id: 1, username: 'me', roles: [] },
token: {
token: 'abc',
expirationEpochSeconds: 12345
},
},
error: null,
loading: false,
};
const createAction = loginSuccess({ payload: loginResponseData });
// returns updated state we should compare against expected one.
const actualState = reducer(initialState, createAction);
// assertions
expect(actualState).toEqual(expectedState);
});
});
});

Resources