Angular 4: Using mockRespond with RxJS Observables - ajax

I've recently built an application that works and I'm trying to build a test. My service fetches items from an API backend:
export class CatfactService {
constructor(private http: Http) {}
getFacts() {
const url = "http://www.catfact.info/api/v1/facts.json";
return this.http.get(url).map(this.extractData)
.catch(this.handleError);
}
Inside my component I'm able to subscribe to the API response. The result of the facts variable is the response details from the API:
ngOnInit() {
this.counter = this.start;
this.service.getFacts().subscribe((facts) => {
this.results = facts.facts;
});
}
Now, I'm building a test for the service, and strangely the subscribe method gets the argument, but rather than the argument being the response data, it returns a promise that ultimately resolves to the mocked values.
import {
TestBed,
inject,
fakeAsync,
tick
} from '#angular/core/testing';
import {
CatfactService
} from './catfact.service';
import {
HttpModule,
Http,
BaseRequestOptions,
XHRBackend,
ResponseOptions
} from '#angular/http';
import {
MockBackend
} from '#angular/http/testing';
describe('CatfactService', () => {
beforeEach(() => {
TestBed.configureTestingModule({
imports: [HttpModule],
providers: [
CatfactService,
MockBackend,
BaseRequestOptions,
{
provide: Http,
useFactory: (backend, options) => new Http(backend, options),
deps: [MockBackend, BaseRequestOptions]
}
],
imports: [
HttpModule
]
});
});
it('should return reasonable json', inject([CatfactService, MockBackend], fakeAsync((service: CatfactService, mockBackend) => {
const mockResponse = {
data: [{
id: 0,
details: 'All cats are lions'
},
{
id: 1,
details: 'Video 1'
},
{
id: 2,
details: 'Video 2'
},
{
id: 3,
details: 'Video 3'
},
]
};
mockBackend.connections.subscribe(connection => {
connection.mockRespond(new Response(JSON.stringify(mockResponse)));
});
service.getFacts().subscribe((facts) => {
facts.then((facts2) => {
expect(facts2.length).toBe(4);
expect(facts2[0].details).toEqual("All cats are lions");
});
});
tick();
})));
});
The fact that calling the subscribe method returns the actual response in the actual application, but a promise in the test, leads me to believe I've set up the mocking of the data incorrectly in the application.
I'm using the following versions of Angular:
ng -v
_ _ ____ _ ___
/ \ _ __ __ _ _ _| | __ _ _ __ / ___| | |_ _|
/ △ \ | '_ \ / _` | | | | |/ _` | '__| | | | | | |
/ ___ \| | | | (_| | |_| | | (_| | | | |___| |___ | |
/_/ \_\_| |_|\__, |\__,_|_|\__,_|_| \____|_____|___|
|___/
#angular/cli: 1.0.2
node: 7.9.0
os: darwin x64
#angular/common: 4.1.1
#angular/compiler: 4.1.1
#angular/core: 4.1.1
#angular/forms: 4.1.1
#angular/http: 4.1.1
#angular/platform-browser: 4.1.1
#angular/platform-browser-dynamic: 4.1.1
#angular/router: 4.1.1
#angular/cli: 1.0.2
#angular/compiler-cli: 4.1.1
The whole project is up on GitHub here: https://github.com/kenmazaika/AngularTesting

Here is a fixed version of the spec. The main issue was that you weren't importing the angular Response.
import { TestBed, inject, fakeAsync, tick } from '#angular/core/testing';
import { CatfactService } from './catfact.service';
import { HttpModule, Http, BaseRequestOptions, XHRBackend, ResponseOptions, Response, RequestOptions } from '#angular/http';
import { MockBackend } from '#angular/http/testing';
describe('CatfactService', () => {
beforeEach(() => {
TestBed.configureTestingModule({
imports: [HttpModule],
providers: [
CatfactService,
MockBackend,
BaseRequestOptions,
{
provide: Http,
useFactory: (backend, options) => new Http(backend, options),
deps: [MockBackend, BaseRequestOptions]
}
]
});
});
it('should return reasonable json', inject([CatfactService, MockBackend], fakeAsync((service: CatfactService, mockBackend) => {
const mockResponse = {
data: [
{ id: 0, details: 'All cats are lions' },
{ id: 1, details: 'Video 1' },
{ id: 2, details: 'Video 2' },
{ id: 3, details: 'Video 3' },
]
};
mockBackend.connections.subscribe(connection => {
connection.mockRespond(new Response(
new ResponseOptions({
body: [
{ id: 0, details: 'All cats are lions' },
{ id: 1, details: 'Video 1' },
{ id: 2, details: 'Video 2' },
{ id: 3, details: 'Video 3' },
]
})));
});
service.getFacts().subscribe((facts) => {
expect(facts.length).toBe(4);
expect(facts[0].details).toEqual("All cats are lions");
});
tick();
})));
});

Related

Prisma2: How to solve n +1 Problem with Paljs

thx for any help.
Im using at the frontend the apollo-client and at the backend graphql-nexus,prisma2 and graphql-yoga server.
I want to solve the n + 1 problem with #paljs/plugins.
At the frontend I have a query posts like:
query posts{
posts {
id
favoritedBy(where: { id: { equals: $currentUserId } }) {
id
}
author {
id
avatar {
id
}
}
link {
id
}
games {
id
}
tags {
id
}
likes(where: { user: { id: { equals: $currentUserId } } }) {
id
}
}
}
Posts resolver:
import { PrismaSelect } from '#paljs/plugins'
export const posts = queryField('posts', {
type: 'Post',
list: true,
args: {
...
},
resolve: async (_parent, args, { prisma, request }, info) => {
const select = new PrismaSelect(info).value
let opArgs: FindManyPostArgs = {
take: 10,
orderBy: {
[args.orderBy]: 'desc',
},
...select
}
const post = await prisma.post.findMany(opArgs)
//The result I want to return with the "sub-models" like likes, author tags...
console.log(JSON.stringify(post, undefined, 2))
return post
},
})
I logging the queries
const prisma = new PrismaClient({
log: ['query'],
})
My Problem: With PrismaSelect, I have 5 queries more than without and If I check the request-time at the frontend I need 300-400ms longer with PrismaSelect. So what I'm doing wrong?
I saw in the #paljs/plugins doc the select in the context. Maybe that is my mistake. How can I use the select in the context?
Here ist my Context:
import { PrismaClient, PrismaClientOptions } from '#prisma/client'
import { PubSub } from 'graphql-yoga'
import { PrismaDelete, onDeleteArgs } from '#paljs/plugins'
class Prisma extends PrismaClient {
constructor(options?: PrismaClientOptions) {
super(options)
}
async onDelete(args: onDeleteArgs) {
const prismaDelete = new PrismaDelete(this)
await prismaDelete.onDelete(args)
}
}
export const prisma = new PrismaClient({
log: ['query'],
})
export const pubsub = new PubSub()
export interface Context {
prisma: PrismaClient
request: any
pubsub: PubSub
}
export function createContext(request: any): Context {
return { prisma, request, pubsub }
}
You need to know that to use my PrismaSelect plugin you need to remove the nexus-prisma-plugin package and use my Pal.js CLI to create your CRUD and ObjectType for nexus and using #paljs/nexus plugin to add in mackSchema function
import { makeSchema } from '#nexus/schema';
import * as types from './graphql';
import { paljs } from '#paljs/nexus'; // import our plugin
export const schema = makeSchema({
types,
plugins: [paljs()],// here our plugin don't use nexus-prisma-plugin
outputs: {
schema: __dirname + '/generated/schema.graphql',
typegen: __dirname + '/generated/nexus.ts',
},
typegenAutoConfig: {
sources: [
{
source: require.resolve('./context'),
alias: 'Context',
},
],
contextType: 'Context.Context',
},
});
Now add this type to your Context
export interface Context {
prisma: PrismaClient
request: any
pubsub: PubSub
select: any // here our select type
}
export function createContext(request: any): Context {
// our paljs plugin will add select object before resolver
return { prisma, request, pubsub, select: {} }
}
after you add our plugin your query will log like this
extendType({
type: 'Query',
definition(t) {
t.field('findOneUser', {
type: 'User',
nullable: true,
args: {
where: arg({
type: 'UserWhereUniqueInput',
nullable: false,
}),
},
resolve(_, { where }, { prisma, select }) {
// our plugin add select object into context for you
return prisma.user.findOne({
where,
...select,
});
},
});
},
});
Can you please try to use my pal c command to start an example from my list and try your schema and make tests with it
It is working, thx Ahmed your plugin is AWESOME!!!!!
I changed my Post-Object from
const Post = objectType({
name: 'Post',
definition(t) {
t.model.id()
t.model.authorId()
t.model.tags()
t.model.games()
t.model.link()
t.model.report()
t.model.notifications()
t.model.author()
t.model.favoritedBy({
filtering: {
id: true,
},
})
t.model.likes({
filtering: {
user: true,
},
})
}
})
to
const Post = objectType({
name: 'Post',
definition(t) {
t.string('id')
t.field('tags', {
nullable: false,
list: [true],
type: 'Tag',
resolve(parent: any) {
return parent['tags']
},
})
t.field('games', {
list: [true],
type: 'Game',
resolve(parent: any) {
return parent['games']
},
})
t.field('link', {
type: 'Link',
nullable: true,
resolve(parent: any) {
return parent['link']
},
})
t.field('notifications', {
list: [true],
type: 'Notification',
resolve(parent: any) {
return parent['notifications']
},
})
t.field('author', {
nullable: false,
type: 'User',
resolve(parent: any) {
return parent['author']
},
})
t.field('favoritedBy', {
nullable: false,
list: [true],
type: 'User',
args: {
where: 'UserWhereInput',
},
resolve(parent: any) {
return parent['favoritedBy']
},
})
t.field('likes', {
list: [true],
type: 'Like',
args: {
where: 'LikeWhereInput',
},
resolve(parent: any) {
return parent['likes']
},
})
},
})
And I also used the nexus-prisma-plugin and paljs-plugin at the same time

Gatsby createPages with GraphQL returns Object: null prototype when Graphiql returns a string

I'm setting up createPages in Gatsby using the slug pulled through Graphql from Strapi.
Here's my query:
query {
allStrapiPosts {
nodes {
slug
}
}
}
GraphiQL returns:
{
"data": {
"allStrapiPosts": {
"nodes": [
{
"slug": "/pizza-struggles-today"
},
{
"slug": "/raining-in-paradise"
},
{
"slug": "/blog-title"
}
]
}
}
}
My gatsby-node.js:
const path = require("path")
exports.createPages = async ({ graphql, actions }) => {
const { createPage } = actions
const { data } = await graphql(`
query {
allStrapiPosts {
nodes {
slug
}
}
}
`)
console.log(data)
data.allStrapiPosts.nodes.forEach(({ node }) => {
createPage({
path: `blog${node.slug}`,
component: path.resolve("./src/Templates/Post-Template.js"),
context: {
slug: node.slug,
},
})
})
}
Here's the errors from the terminal:
[Object: null prototype] {
allStrapiPosts: [Object: null prototype] {
nodes: [
[Object: null prototype],
[Object: null prototype],
[Object: null prototype]
]
}
}
ERROR #11321 PLUGIN
"gatsby-node.js" threw an error while running the createPages lifecycle:
Cannot read property 'slug' of undefined
17 | data.allStrapiPosts.nodes.forEach(({ node }) => {
18 | createPage({
> 19 | path: `blog${node.slug}`,
| ^
20 | component: path.resolve("./src/Templates/Post-Template.js"),
21 | context: {
22 | slug: node.slug,
File: gatsby-node.js:19:25
TypeError: Cannot read property 'slug' of undefined
- gatsby-node.js:19
/Users/jeffstahlnecker/Developer/mxc-blog/gatsby-node.js:19:25
- Array.forEach
- gatsby-node.js:17 Object.exports.createPages
/Users/jeffstahlnecker/Developer/mxc-blog/gatsby-node.js:17:29
- task_queues.js:94 processTicksAndRejections
internal/process/task_queues.js:94:5
failed createPages - 0.046s
My queries in other locations in Gatsby work well. For example I have a similar query creating a list of blog posts, that works without a problem.
Spent a few hours trying to troubleshoot this one last night. Any and all help is appreciated.
From gastby-config.js:
{
resolve: `gatsby-source-strapi`,
options: {
apiURL: `https://cms.mxc.org:443`,
queryLimit: 1000, // Default to 100
contentTypes: [`posts`],
//If using single types place them in this array.
// singleTypes: [`home-page`, `contact`],
// Possibility to login with a strapi user, when content types are not publically available (optional).
loginData: {
identifier: "",
password: "",
},
},
},

GraphQL Error: Field "image" must not have a selection since type "String" has no subfields

Description
I am new and learning GatsbyJs/NetlifyCM/GraphQL so I am sorry if I come across as stupid. I, however, have been trying to fix this issue for 5 days now and am at my wits end. I am trying to implement NetlifyCMS into a GatsbyJS blog starter. I seem to have everything working except the images. Graphql is querying the image URL as "img/image.jpg" which is what is set in the MD file, and that works fine. However, when I make a new post with NetlifyCMS it sets the URL to "/img/image.jpg" and graphql can't seem to find it. The extra slash in front causes the error. If I remove the slash OR put a dot in front of the slash, "./img/image.jpg" it builds fine which leads me to believe relative paths may be working? I have read issue 4123, issue 13938, issue 5990, As well as multiple other similar issues and blogs and still, am unable to get it to work. I have tried implementing gatsby-remark-relative-images and gatsby-plugin-netlify-cms but neither seems to fix it.
Any help would be appreciated. I am newer at web development and very new to gatsby/graphql. Thank you.
Steps to reproduce
remove or add a / in front of the image URL in the MD file. Here is a link to the repo to clone/download https://github.com/AaronCuddeback/blog.aaroncuddeback.com
Environment
System:
OS: Windows 10
CPU: (16) x64 Intel(R) Core(TM) i9-9900KF CPU # 3.60GHz
Binaries:
Node: 12.13.0 - C:\Program Files\nodejs\node.EXE
npm: 6.13.0 - ~\AppData\Roaming\npm\npm.CMD
Languages:
Python: 2.7.17 - /usr/bin/python
Browsers:
Edge: 44.18362.387.0
npmPackages:
gatsby: 2.18.2 => 2.18.2
gatsby-image: 2.2.33 => 2.2.33
gatsby-plugin-canonical-urls: 2.1.15 => 2.1.15
gatsby-plugin-emotion: 4.1.15 => 4.1.15
gatsby-plugin-feed: 2.3.21 => 2.3.21
gatsby-plugin-google-analytics: 2.1.28 => 2.1.28
gatsby-plugin-netlify: ^2.1.25 => 2.1.25
gatsby-plugin-netlify-cms: ^4.1.28 => 4.1.28
gatsby-plugin-netlify-cms-paths: ^1.3.0 => 1.3.0
gatsby-plugin-postcss: 2.1.15 => 2.1.15
gatsby-plugin-react-helmet: 3.1.15 => 3.1.15
gatsby-plugin-sharp: 2.3.2 => 2.3.2
gatsby-plugin-sitemap: 2.2.21 => 2.2.21
gatsby-plugin-typescript: 2.1.19 => 2.1.19
gatsby-remark-abbr: 2.0.0 => 2.0.0
gatsby-remark-copy-linked-files: 2.1.30 => 2.1.30
gatsby-remark-images: 3.1.33 => 3.1.33
gatsby-remark-prismjs: 3.3.24 => 3.3.24
gatsby-remark-relative-images: ^0.2.3 => 0.2.3
gatsby-remark-responsive-iframe: 2.2.27 => 2.2.27
gatsby-remark-smartypants: 2.1.16 => 2.1.16
gatsby-source-filesystem: 2.1.38 => 2.1.38
gatsby-transformer-json: 2.2.19 => 2.2.19
gatsby-transformer-remark: 2.6.37 => 2.6.37
gatsby-transformer-sharp: 2.3.5 => 2.3.5
gatsby-transformer-yaml: 2.2.17 => 2.2.17
File Contents:
gatsby-config.js:
const path = require('path');
module.exports = {
siteMetadata: {
title: 'Ghost',
description: 'The professional publishing platform',
siteUrl: 'https://gatsby-casper.netlify.com', // full path to blog - no ending slash
},
mapping: {
'MarkdownRemark.frontmatter.author': 'AuthorYaml',
},
plugins: [
{
resolve: 'gatsby-source-filesystem',
options: {
path: `${__dirname}/static/img`,
name: 'uploads',
},
},
{
resolve: 'gatsby-source-filesystem',
options: {
path: `${__dirname}/src/content`,
name: 'content',
},
},
{
resolve: `gatsby-plugin-netlify-cms-paths`,
options: {
cmsConfig: `/static/admin/config.yml`,
},
},
'gatsby-plugin-sitemap',
'gatsby-plugin-sharp',
{
resolve: 'gatsby-transformer-remark',
options: {
plugins: [
`gatsby-plugin-netlify-cms-paths`,
{
resolve: `gatsby-remark-relative-images`,
options: {
name: 'uploads', // Must match the source name ^
},
},
{
resolve: 'gatsby-remark-images',
options: {
maxWidth: 1170,
quality: 90,
},
},
{
resolve: 'gatsby-remark-responsive-iframe',
options: {
wrapperStyle: 'margin-bottom: 1rem',
},
},
'gatsby-remark-prismjs',
'gatsby-remark-copy-linked-files',
'gatsby-remark-smartypants',
'gatsby-remark-abbr',
],
},
},
'gatsby-transformer-json',
{
resolve: 'gatsby-plugin-canonical-urls',
options: {
siteUrl: 'https://gatsby-casper.netlify.com',
},
},
'gatsby-plugin-emotion',
'gatsby-plugin-typescript',
'gatsby-transformer-sharp',
'gatsby-plugin-react-helmet',
'gatsby-transformer-yaml',
'gatsby-plugin-feed',
{
resolve: 'gatsby-plugin-postcss',
options: {
postCssPlugins: [require('postcss-color-function'), require('cssnano')()],
},
},
{
resolve: `gatsby-plugin-google-analytics`,
options: {
trackingId: 'UA-XXXX-Y',
// Puts tracking script in the head instead of the body
head: true,
// IP anonymization for GDPR compliance
anonymize: true,
// Disable analytics for users with `Do Not Track` enabled
respectDNT: true,
// Avoids sending pageview hits from custom paths
exclude: ['/preview/**'],
// Specifies what percentage of users should be tracked
sampleRate: 100,
// Determines how often site speed tracking beacons will be sent
siteSpeedSampleRate: 10,
},
},
`gatsby-plugin-netlify-cms`,
`gatsby-plugin-netlify`,
],
};
gatsby-node.js
const path = require('path');
const _ = require('lodash');
const { fmImagesToRelative } = require('gatsby-remark-relative-images');
exports.onCreateNode = ({ node, actions, getNode }) => {
const { createNodeField } = actions;
fmImagesToRelative(node);
// Sometimes, optional fields tend to get not picked up by the GraphQL
// interpreter if not a single content uses it. Therefore, we're putting them
// through `createNodeField` so that the fields still exist and GraphQL won't
// trip up. An empty string is still required in replacement to `null`.
switch (node.internal.type) {
case 'MarkdownRemark': {
const { permalink, layout, primaryTag } = node.frontmatter;
const { relativePath } = getNode(node.parent);
let slug = permalink;
if (!slug) {
slug = `/${relativePath.replace('.md', '')}/`;
}
// Used to generate URL to view this content.
createNodeField({
node,
name: 'slug',
value: slug || '',
});
// Used to determine a page layout.
createNodeField({
node,
name: 'layout',
value: layout || '',
});
createNodeField({
node,
name: 'primaryTag',
value: primaryTag || '',
});
}
}
};
exports.createPages = async ({ graphql, actions }) => {
const { createPage } = actions;
const result = await graphql(`
{
allMarkdownRemark(
limit: 2000
sort: { fields: [frontmatter___date], order: ASC }
filter: { frontmatter: { draft: { ne: true } } }
) {
edges {
node {
excerpt
timeToRead
frontmatter {
title
tags
date
draft
image {
childImageSharp {
fluid(maxWidth: 3720) {
aspectRatio
base64
sizes
src
srcSet
}
}
publicURL
}
author {
id
bio
avatar {
children {
... on ImageSharp {
fixed(quality: 90) {
src
}
}
}
}
}
}
fields {
layout
slug
}
}
}
}
allAuthorYaml {
edges {
node {
id
}
}
}
}
`);
if (result.errors) {
console.error(result.errors);
throw new Error(result.errors);
}
// Create post pages
const posts = result.data.allMarkdownRemark.edges;
// Create paginated index
const postsPerPage = 6;
const numPages = Math.ceil(posts.length / postsPerPage);
Array.from({ length: numPages }).forEach((_, i) => {
createPage({
path: i === 0 ? '/' : `/${i + 1}`,
component: path.resolve('./src/templates/index.tsx'),
context: {
limit: postsPerPage,
skip: i * postsPerPage,
numPages,
currentPage: i + 1,
},
});
});
posts.forEach(({ node }, index) => {
const { slug, layout } = node.fields;
const prev = index === 0 ? null : posts[index - 1].node;
const next = index === posts.length - 1 ? null : posts[index + 1].node;
createPage({
path: slug,
// This will automatically resolve the template to a corresponding
// `layout` frontmatter in the Markdown.
//
// Feel free to set any `layout` as you'd like in the frontmatter, as
// long as the corresponding template file exists in src/templates.
// If no template is set, it will fall back to the default `post`
// template.
//
// Note that the template has to exist first, or else the build will fail.
component: path.resolve(`./src/templates/${layout || 'post'}.tsx`),
context: {
// Data passed to context is available in page queries as GraphQL variables.
slug,
prev,
next,
primaryTag: node.frontmatter.tags ? node.frontmatter.tags[0] : '',
},
});
});
// Create tag pages
const tagTemplate = path.resolve('./src/templates/tags.tsx');
const tags = _.uniq(
_.flatten(
result.data.allMarkdownRemark.edges.map(edge => {
return _.castArray(_.get(edge, 'node.frontmatter.tags', []));
}),
),
);
tags.forEach(tag => {
createPage({
path: `/tags/${_.kebabCase(tag)}/`,
component: tagTemplate,
context: {
tag,
},
});
});
// Create author pages
const authorTemplate = path.resolve('./src/templates/author.tsx');
result.data.allAuthorYaml.edges.forEach(edge => {
createPage({
path: `/author/${_.kebabCase(edge.node.id)}/`,
component: authorTemplate,
context: {
author: edge.node.id,
},
});
});
};
exports.onCreateWebpackConfig = ({ stage, actions }) => {
// adds sourcemaps for tsx in dev mode
if (stage === `develop` || stage === `develop-html`) {
actions.setWebpackConfig({
devtool: 'eval-source-map',
});
}
};

How to write unit test case for async method in react?

I am trying to write a unit test case for an async function which having for loop inside and in each iteration it calls Axios "get" method.
I am new to unit test cases, and I know how to write it for simple async function, but for the complicated case, I need some help. Thanks in advance.
export const someMethod = ({ collections }) => {
return dispatch => {
if (collections.length > 0) {
for (let index = 0; index < collections.length; index++) {
const collection = collections[index];
const { libraryUrl, bookUrl } = collection;
const containerId = Math.random().toString();
dispatch(getBook({ boolURL: bookUrl, libraryURL: libraryUrl, libraryId }));
dispatch({
type: ASSESSMENT.BOOK.ADD,
payload: { boolURL: bookUrl, libraryId }
});
}
}
};
};
Here is the solution:
actionCreators.ts:
export const ASSESSMENT = {
BOOK: {
ADD: 'ADD',
GET: 'GET'
}
};
export const getBook = data => ({ type: ASSESSMENT.BOOK.GET, payload: { data } });
export const someMethod = ({ collections }) => {
return dispatch => {
if (collections.length > 0) {
for (let index = 0; index < collections.length; index++) {
const collection = collections[index];
const { libraryUrl, bookUrl, libraryId } = collection;
const containerId = Math.random().toString();
dispatch(getBook({ boolURL: bookUrl, libraryURL: libraryUrl, libraryId }));
dispatch({
type: ASSESSMENT.BOOK.ADD,
payload: { boolURL: bookUrl, libraryId }
});
}
}
};
};
actionCreators.spec.ts:
import { someMethod, ASSESSMENT } from './actionCreators';
import createMockStore from 'redux-mock-store';
import thunk, { ThunkDispatch } from 'redux-thunk';
import { AnyAction } from 'redux';
const middlewares = [thunk];
const mockStore = createMockStore<any, ThunkDispatch<any, any, AnyAction>>(middlewares);
describe('someMethod', () => {
it('t1', () => {
const intialState = {};
const store = mockStore(intialState);
const collections = [
{ libraryUrl: 'aa', bookUrl: 'a', libraryId: '1' },
{ libraryUrl: 'bb', bookUrl: 'b', libraryId: '2' }
];
const expectedActions = [
{
type: ASSESSMENT.BOOK.GET,
payload: {
data: {
boolURL: collections[0].bookUrl,
libraryURL: collections[0].libraryUrl,
libraryId: collections[0].libraryId
}
}
},
{ type: ASSESSMENT.BOOK.ADD, payload: { boolURL: collections[0].bookUrl, libraryId: collections[0].libraryId } },
{
type: ASSESSMENT.BOOK.GET,
payload: {
data: {
boolURL: collections[1].bookUrl,
libraryURL: collections[1].libraryUrl,
libraryId: collections[1].libraryId
}
}
},
{ type: ASSESSMENT.BOOK.ADD, payload: { boolURL: collections[1].bookUrl, libraryId: collections[1].libraryId } }
];
store.dispatch(someMethod({ collections }));
expect(store.getActions()).toEqual(expectedActions);
});
});
Unit test result with coverage report:
PASS src/stackoverflow/58012116/actionCreators.spec.ts
someMethod
✓ t1 (6ms)
-------------------|----------|----------|----------|----------|-------------------|
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s |
-------------------|----------|----------|----------|----------|-------------------|
All files | 100 | 50 | 100 | 100 | |
actionCreators.ts | 100 | 50 | 100 | 100 | 12 |
-------------------|----------|----------|----------|----------|-------------------|
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 5.523s
Here is the completed demo:https://github.com/mrdulin/jest-codelab/tree/master/src/stackoverflow/58012116

Ionic2,Jasmine,Karma unit test

We are setting up a unit test framework for our Ionic2 project
while running npm test , we are faced with the following error.
I give below the src/.ts and src/.spec.ts alongwith the cmd window error
Please help resolve
Chrome 55.0.2883 (Windows 8.1 0.0.0) ERROR: Error{rejection: 'Failed
to load app.html', promise : ZoneAwarePromise{__zone_symbol__state: 0,
__zone_symbol__value: 'Failed to load app.html'}, z one: Zone{_properties: Object{}, _parent: null, _name: '',
_zoneDelegate: ZoneDelegate{_t askCounts: ..., zone: ..., _parentDelegate: ..., _forkZS: ..., _forkDlgt: ..., _interceptZS: .. ., _interceptDlgt: ..., _invokeZS: ..., _invokeDlgt: ...,
_handleErrorZS: ..., _handleErrorDlgt : ..., _scheduleTaskZS: ..., _scheduleTaskDlgt: ..., _invokeTaskZS: ..., _invokeTaskDlgt: ...,
_cancelTaskZS: ..., _cancelTaskDlgt: ..., _hasTaskZS: ..., _hasTaskDlgt: ...}}, task: ZoneTask{ runCount: 1, type: 'microTask', zone: Zone{_properties: ..., _parent: ..., _name: ..., _zoneDel egate:
...}, source: 'Promise.then', data: undefined, scheduleFn: undefined,
cancelFn: null, ca llback: function () { ... }, invoke: function () {
... }}}
Chrome 55.0.2883 (Windows 8.1 0.0.0) DashboardService should return a
non empty array FAILED TypeError: Cannot read property 'assertPresent'
of undefined at resetFakeAsyncZone
(C:/Users/user/AppData/Local/Temp/karma-typescript-bundle
-66485eOt1ZMUGTWZ.js:143884:22) at Object. (C:/Users/user/AppData/Local/Temp/karma-typescript-bundle
-66485eOt1ZMUGTWZ.js:144536:13) Error: ProxyZoneSpec is needed for the async() test helper but could not be found. Plea se make sure that
your environment includes zone.js/dist/proxy.js at runInTestZone
(C:/Users/user/AppData/Local/Temp/karma-typescript-bundle-6648
5eOt1ZMUGTWZ.js:143659:19) at Object.
(C:/Users/user/AppData/Local/Temp/karma-typescript-bundle
-66485eOt1ZMUGTWZ.js:143633:17) TypeError: Cannot read property 'getData' of null at Object.
(src/pages/dashboard/dashboard.spec.ts:47:35 <- src/pages/das
hboard/dashboard.spec.js:40:31) Chrome 55.0.2883 (Windows 8.1 0.0.0):
Executed 1 of 1 (1 FAILED) ERROR (0.019 secs / 0.003 secs
import { async, ComponentFixture, TestBed } from '#angular/core/testing';
import { FormsModule, ReactiveFormsModule } from '#angular/forms';
import { App, Config, Form, IonicModule, Keyboard, DomController, MenuController, NavController, Platform ,NavParams} from 'ionic-angular';
import { DashboardPage } from './dashboard';
import { ConfigMock } from '../../mock';
import { AzureDatasync } from '../../app/providers/azure-datasync';
let dashboard = null;
let navCtrl: NavController;
let navParams: NavParams;
let datasync: AzureDatasync;
let fixture: ComponentFixture<DashboardPage> = null;
let instance: any = null;
describe('DashboardService', () => {
// beforeEach(() => {
// dashboard = new DashboardPage(navCtrl,navParams,datasync);
// console.log(dashboard);
// });
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [DashboardPage],
providers: [
App, DomController, Form, Keyboard, MenuController, NavController, Platform,
{provide: Config, useClass: ConfigMock},
],
imports: [
FormsModule,
IonicModule,
ReactiveFormsModule,
],
})
.compileComponents().then(() => {
fixture = TestBed.createComponent(DashboardPage);
console.log(fixture);
instance = fixture;
console.log(instance);
fixture.detectChanges();
});
}));
it('should return a non empty array', () => {
let result = dashboard.getData();
console.log(result);
expect(Array.isArray(result)).toBeTruthy;
expect(result.length).toBeGreaterThan(0);
}
);
});
import { Component, OnInit } from '#angular/core';
import { NavController, NavParams } from 'ionic-angular';
import { AzureDatasync } from '../../app/providers/azure-datasync';
import 'zone.js/dist/async-test';
/*
Generated class for the Dashboard page.
See http://ionicframework.com/docs/v2/components/#navigation for more info on
Ionic pages and navigation.
*/
#Component({
selector: 'page-dashboard',
templateUrl: 'dashboard.html'
})
export class DashboardPage implements OnInit {
todays: any ;
ticklers: any;
cases: any;
constructor(public navCtrl: NavController, public navParams: NavParams, public datasync: AzureDatasync) {}
ngOnInit(){
this.getData();
}
getData() {
this.todays = [
{type: "abc", name: "test", duration: "9.30 AM - 10.00 AM"},
{type: "def", name: "test2", duration: "12.45 AM - 3.10 PM"}
];
this.ticklers = [
{name: "abc", description: "A Court "},
{name: "def", description: "dd"}
];
this.cases = [
{name: "Msh", duedate: "Due on 7th Dec", priority: "PsI", timeduration: "24hrs", imgurl:"_blank.png"},
{name: "ss Smith", duedate: "Due on 11th Dec", priority: "Pris", timeduration: "30 mins ", imgurl:"./person_blank.png"}
];
}
}
I was having the same issue with Jasmine and Karma.
In my karma.conf I loaded some additional files into the browser:
// list of files / patterns to load in the browser
files: [
'../node_modules/zone.js/dist/zone.js', // 'Uncaught ReferenceError: Zone is not defined'
'../node_modules/zone.js/dist/proxy.js', // 'TypeError: Cannot read property 'assertPresent' of undefined'
'../node_modules/zone.js/dist/sync-test.js', // 'TypeError: Cannot read property 'assertPresent' of undefined'
'../node_modules/zone.js/dist/jasmine-patch.js', // 'TypeError: Cannot read property 'assertPresent' of undefined'
// ANY OTHER FILES TO LOAD FOR YOUR TESTS
],

Resources