Redux Promise Middleware + Redux Mock Store Testing - unit-testing

I tried to test an action creator that returns a promise, using also redux mock store.
import promiseMiddleware from 'redux-promise-middleware';
import nock from 'nock';
import configureStore from 'redux-mock-store';
import { domain, port } from '../../config/environment';
import { GET_ITEMS_START,
GET_ITEMS_SUCCESS } from '../../constants/items';
import { getItems } from './items';
const promise = promiseMiddleware({
promiseTypeSuffixes: ['START', 'SUCCESS', 'ERROR']
});
describe('Get Items', () => {
it('should create GET_ITEMS_SUCCESS action after successfully getting items', (done) => {
nock(`${domain}:${port}`)
.get('/api/items')
.reply(200, {
_id: '1',
text: 'Make Eggs',
completed: false
});
const expectedActions = [
{ type: GET_ITEMS_START },
{ type: GET_ITEMS_SUCCESS, payload: {
data: { _id: '1', text: 'Make Eggs', completed: false }
}}
];
const store = mockStore({}, expectedActions, done);
store.dispatch(getItems());
});
});
and here is my action creator code
export function getItems() {
return {
type: GET_ITEMS,
payload: {
promise: axios.get(`${domain}:${port}/api/items`)
}
};
}
but the result is mismatch because the promise resolved a deep nested objects
Error: Expected { payload: { config: { headers: {}, method: 'get', timeout: 0, transformRequest: [ [Function] ], transformResponse: [ [Function] ], url: 'http://localhost:3000/api/items', withCredentials: undefined }, data: { _id: '1', completed: false, text: 'Make Eggs' }, headers: {}, status: 200, statusText: 'OK' }, type: 'GET_ITEMS_SUCCESS' } to equal { payload: { data: { _id: '1', completed: false, text: 'Make Eggs' } }, type: 'GET_ITEMS_SUCCESS' }
+ expected - actual
{
"payload": {
- "config": {
- "headers": {}
- "method": "get"
- "timeout": 0
- "transformRequest": [
- [Function]
- ]
- "transformResponse": [
- [Function]
- ]
- "url": "http://localhost:3000/api/items"
- "withCredentials": [undefined]
- }
"data": {
"_id": "1"
"completed": false
"text": "Make Eggs"
}
- "headers": {}
- "status": 200
- "statusText": "OK"
}
"type": "GET_ITEMS_SUCCESS"
}
I obviously don't want to copy all of those deep nested properties into my test suite.
Is there a better way of doing this?

If you're writing a unit test, you probably don't need to call the actual REST API. Instead you can mock axios and make it return some fake data which won't be so deep.

Related

API Gateway - Return XML or JSON

I have a node API Gateway stack and a Node Lambda. I've been trying to get API gateway to return content-type: application/xml OR application/json depending on the request (returnType=xml or returnType=json).
I have tried adding response models and that didn't work. BinaryTypes didn't work either. I have gotten it to do either application/json OR application/xml but I can't get it to do both. Is what I'm trying to do even possible? or should I create two separate endpoints?
This example always returns application/json.
Here is my lambda:
exports.handler = async function (event, context, callback) {
var format = event.format;
if (!format) {
callback(Error("[BadRequest] missing parameters"));
}
const promise = new Promise(function (resolve, reject) {
https
.get("exampleendpoint.com", (res) => {
let body = "";
res.on("data", (chunk) => {
body += chunk;
});
res.on("end", () => {
var results = JSON.parse(body);
if (format && format.toUpperCase() === "XML") {
var response = {
statusCode: 200,
headers: { "content-type": "application/xml" },
body:
'<?xml version="1.0" encoding="UTF-8"?><result>' +
OBJtoXML(results) +
"</result>",
};
resolve(response);
} else {
var response = {
statusCode: 200,
headers: { "content-type": "application/json" },
body: JSON.stringify(results),
};
resolve(response);
}
});
})
.on("error", (e) => {
var response = {
statusCode: 500,
body: "",
errorMessage: "Error from example.com",
};
resolve(response);
});
});
return promise;
};
Here is my api gateway code:
const epqApi = new gateway.RestApi(this, "restApi", {
restApiName: "resultsApi",
cloudWatchRole: true,
description: "Calls the service for the app",
endpointTypes: [gateway.EndpointType.REGIONAL],
deployOptions: {
stageName: "prod",
loggingLevel: gateway.MethodLoggingLevel.OFF,
dataTraceEnabled: false,
},
});
const epqResource = epqApi.root.addResource("v1");
const epqIntegration: gateway.LambdaIntegration =
new gateway.LambdaIntegration(generatePqsResultFunction, {
proxy: false,
allowTestInvoke: true,
passthroughBehavior: gateway.PassthroughBehavior.NEVER,
contentHandling: gateway.ContentHandling.CONVERT_TO_TEXT,
requestTemplates: {
"application/json": `{
"format":"$input.params(\'format\')"
}`,
},
integrationResponses: [
{
statusCode: "200",
responseParameters: {
"method.response.header.Access-Control-Allow-Origin": "'*'",
},
responseTemplates: {
"application/json": "$input.path('$.body')",
"application/xml": "$input.path('$.body')",
},
},
{
statusCode: "400",
selectionPattern: "^\\[BadRequest\\].*",
responseParameters: {
"method.response.header.Access-Control-Allow-Origin": "'*'",
},
responseTemplates: {
"application/javascript":
"#set($inputRoot = $input.path('$')) {\"errorMessage\" : \"$input.path('$.errorMessage')\"}",
},
},
],
});
epqResource.addMethod("GET", epqIntegration, {
requestParameters: {
//all params need to be in here, even if they are not required
"method.request.querystring.x": false,
"method.request.querystring.y": false,
"method.request.querystring.units": false,
"method.request.querystring.format": false,
"method.request.querystring.wkid": false,
"method.request.querystring.includeDate": false,
},
methodResponses: [
// Successful response from the integration
{
statusCode: "200",
responseParameters: {
"method.response.header.Access-Control-Allow-Origin": true,
},
},
{
statusCode: "400",
responseParameters: {
"method.response.header.Access-Control-Allow-Origin": true,
},
},
],
});
}

Remove nested relations from API explorer (swagger)

I have Application model with following relation:
#belongsTo(() => Ido)
idoId: string;
export interface ApplicationRelations {
ido?: IdoWithRelations;
}
export type ApplicationWithRelations = Application & ApplicationRelations;
Application repository looks like this:
export class ApplicationRepository extends DefaultCrudRepository<
Application,
typeof Application.prototype.id,
ApplicationRelations
> {
public readonly user: BelongsToAccessor<
User,
typeof Application.prototype.id
>;
constructor(
#inject('datasources.db') dataSource: DbDataSource,
#repository.getter('UserRepository')
protected userRepositoryGetter: Getter<UserRepository>,
) {
super(Application, dataSource);
this.user = this.createBelongsToAccessorFor('user', userRepositoryGetter);
this.inclusionResolvers.delete('ido');
}
}
And the following relation in IDO model:
#hasMany(() => Application)
applications: Application[];
In post /ido in swagger i am getting this example for creating:
{
"applications": [
{
"status": "string",
"createdAt": 0,
"idoId": "string",
"userId": "string",
"ido": {
"applications": [
{
"status": "string",
"createdAt": 0,
"idoId": "string",
"userId": "string",
"ido": {
"applications": [
"string"
],
}
Is there any ways to remove the duplicate and kind of curricular relation for ido in application from swagger? Or this doesn't really matter and i can just manually delete all fields above the first application?
This answer was copied over from https://github.com/loopbackio/loopback-next/discussions/8536#discussioncomment-2655375.
OpenAPI/Swagger is at the REST layer, which means you'll need to look inside the respective REST Controller, which would have something similar to this:
#post('...')
function create(
#requestBody({
content: {
'application/json': {
schema: getModelSchemaRef(Application, {
title: 'NewApplication',
exclude: ['id'],
}),
},
},
})
)
You can modify it as such to exclude relations:
#post('...')
function create(
#requestBody({
content: {
'application/json': {
schema: getModelSchemaRef(Application, {
title: 'NewApplication',
exclude: ['id'],
+ includeRelations: false,
}),
},
},
})
)
An alternative is to use exclude.
More info can be found in the API docs: https://loopback.io/doc/en/lb4/apidocs.repository-json-schema.getjsonschemaref.html

Nuxt Fatal Error Error: [VuetifyLoaderPlugin Error] No matching rule for vue-loader found

After running npm run build, I get an error: Error: [VuetifyLoaderPlugin Error] No matching rule for vue-loader found.Make sure there is at least one root-level rule that uses vue-loader and VuetifyLoaderPlugin is applied after VueLoaderPlugin.
package.json
{
"name": "client",
"version": "1.0.0",
"private": true,
"scripts": {
"dev": "nuxt",
"build": "nuxt build",
"start": "nuxt start",
"generate": "nuxt generate"
},
"dependencies": {
"#nuxtjs/axios": "^5.13.6",
"core-js": "^3.15.1",
"dotenv": "^10.0.0",
"nuxt": "^2.15.7",
"nuxt-i18n": "^6.27.3",
"nuxt-mail": "^3.0.10",
"vuetify": "^2.5.5"
},
"devDependencies": {
"#fortawesome/fontawesome-free": "^5.15.3",
"#fortawesome/fontawesome-svg-core": "^1.2.35",
"#fortawesome/free-brands-svg-icons": "^5.15.3",
"#fortawesome/free-solid-svg-icons": "^5.15.3",
"#fortawesome/vue-fontawesome": "^2.0.2",
"#mdi/font": "^5.9.55",
"#nuxtjs/fontawesome": "^1.1.2",
"#nuxtjs/vuetify": "^1.12.1",
"eslint-config-prettier": "^8.3.0",
"font-awesome": "^4.7.0",
"material-design-icons-iconfont": "^6.1.0",
"prettier": "^2.3.2"
}
}
Here is my config nuxt.config.js file:
import colors from 'vuetify/es5/util/colors'
import i18n from './config/i18n'
export default {
// Global page headers: https://go.nuxtjs.dev/config-head
head: {
titleTemplate: '%s - client',
title: 'client',
htmlAttrs: {
lang: 'en'
},
meta: [
{ charset: 'utf-8' },
{ name: 'viewport', content: 'width=device-width, initial-scale=1' },
{ hid: 'description', name: 'description', content: '' },
{ name: 'format-detection', content: 'telephone=no' }
],
link: [
{ rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' }
]
},
// Global CSS: https://go.nuxtjs.dev/config-css
css: [
],
// Plugins to run before rendering page: https://go.nuxtjs.dev/config-plugins
plugins: [
],
// Auto import components: https://go.nuxtjs.dev/config-components
components: true,
// Modules for dev and build (recommended): https://go.nuxtjs.dev/config-modules
buildModules: [
[
'nuxt-i18n',
{
vueI18nLoader: true,
defaultLocale: 'hr',
locales: [
{
code: 'en',
name: 'Eng'
},
{
code: 'hr',
name: 'Hrv'
}
],
vueI18n: i18n
}
],
'#nuxtjs/vuetify',
'#nuxtjs/fontawesome'
],
fontawesome: {
icons: {
solid: true,
brands: true
}
},
// Modules: https://go.nuxtjs.dev/config-modules
modules: [
// https://go.nuxtjs.dev/axios
'#nuxtjs/axios',
'#nuxtjs/vuetify',
'nuxt-i18n',
['nuxt-mail', {
message: {
to: 'mislav0508#hotmail.com',
},
smtp: {
host: "smtp-mail.outlook.com",
port: 587,
secure: false, // true for 465, false for other ports
auth: {
user: "mislav0508#hotmail.com",
pass: process.env.EMAIL_PASS,
},
tls: {
rejectUnauthorized:false
}
},
}],
],
// Axios module configuration: https://go.nuxtjs.dev/config-axios
axios: {},
i18n: {
i18n: {
locales: ['hr', 'en'],
defaultLocale: 'hr',
vueI18n: {
fallbackLocale: 'hr',
messages: {
hr: {
welcome: 'Dobrodošli'
},
en: {
welcome: 'Welcome'
}
}
}
}
},
// Vuetify module configuration: https://go.nuxtjs.dev/config-vuetify
vuetify: {
customVariables: ['~/assets/variables.scss'],
theme: {
dark: false,
themes: {
dark: {
primary: colors.blue.darken2,
accent: colors.grey.darken3,
secondary: colors.amber.darken3,
info: colors.teal.lighten1,
warning: colors.amber.base,
error: colors.deepOrange.accent4,
success: colors.green.accent3
}
}
}
},
// Build Configuration: https://go.nuxtjs.dev/config-build
build: {
}
}
I've tried installing vuetify-loader and vue-loader and adding them to the nuxt.config.js file. However after that I get another error:
TypeError: loaderContext.emitError is not a function
Please help.

Loopback js can't read GET request params in the url

I have the following code:
Addresses.getorderHistory = function(address, baseToken, status, length, cb) {
console.log("lenght here -> " + length);
addressService.getorderHistory(address, baseToken, status, length, cb)
.catch(err => cb(err));
};
Addresses.remoteMethod('getorderHistory', {
http: { verb: 'get', path: '/:address/orderHistory' },
accepts: [
{ arg: "address", type: "string", required: true },
{ arg: "base_token", type: "string", required: true, 'http': {source: 'query'} },
{ arg: "status", type: "string", required: true, 'http': {source: 'query'} },
{ arg: "length", type: "string", required: true, 'http': {source: 'query'} }
],
returns: { type: 'object', root: true }
});
When I make a request like this
curl http://localhost:3001/<my root>/addresses/<my address>/orderHistory?base_token=<my token>&status=filled&length=100
It says
Error: status is a required argument
I can basically read address and baseToken values but I can't read status and length. What am I missing?

Store not getting loaded

I am using Sencha V2. I am trying to populate my list with the values in a Users.json file.
The code in my list file is
Ext.define('iPolis.view.personlist',{
extend:'Ext.List',
xtype: 'personlist',
requires: [
'Ext.List',
'Ext.form.FieldSet',
'Ext.Button'
],
config: {
fullscreen:true,
items: [
{
xtype:'toolbar',
docked:'top',
title:'iPolis',
items:[
{
ui:'back',
icon:'home',
iconCls:'home',
iconMask:true,
id: 'homebtn',
handler:function ()
{
}
},
]
},
{
xtype : 'list',
store : 'personListStore',
itemTpl : '<div class="contact">HI <br/> {name}</div>'
}
}
]
}
});
and the store is calling the file using:
Ext.define('iPolis.store.personListStore', {
extend: 'Ext.data.Store',
storeId:'personListStore',
model : 'iPolis.model.personListModel',
proxy : {
type : 'ajax',
url : '/users.json',
reader: {
type: 'json',
rootProperty: 'users'
}
},
autoLoad: true
});
The code for my json file is:
{
"users": [
{
"id": 1,
"name": "Ed Spencer",
"email": "ed#sencha.com"
},
{
"id": 2,
"name": "Abe Elias",
"email": "abe#sencha.com"
}
]
}
I am gettin a blank screen. I have tried everything but no data is displayed on the screen.
In your list the store is personStore but your trying to use personListStore.
Ext.define('iPolis.store.personListStore', {
extend: 'Ext.data.Store',
config:{
model : 'iPolis.model.personListModel',
storeId:'personListStore',
proxy : {
type : 'ajax',
url : '/users.json',
reader: {
type: 'json',
rootProperty: 'users'
}
},
autoLoad: true } });
try this may help you.