I am trying to create unit tests for the custom yup validation method by jest with correct and incorrect data.
function nextValueBigger(message = 'Some error') {
return this.test('nextValueBigger', (value, { path }) => {
const errors = value.map((item, index) => {
if (item?.from < item?.to) { return null; } // Successful validation, no error
return new ValidationError(
message,
null,
`${path}[${index}].to`,
);
}).filter(Boolean);
if (errors.length === 0) { return true; }
return new ValidationError(errors);
});
}
I tried with creating simulation of validation like this:
const data = [
{ from: 1, to: 2 },
{ from: 3, to: 4 },
{ from: 5, to: 6 },
];
const schema = Yup.object().shape({
item: Yup.array().of(Yup.object().shape({
from: Yup.number(),
to: Yup.number(),
}))
.nextValueBigger(),
});
it('should not found any errors', async () => {
const result = await schema.validateAt('items', { items: data }
expect(result).toEqual(true)
}
Related
My function getDocuments() in summary consists in that I pass some parameters in an array (like the path, the name of the document, if I want to section it by parts) and based on that array I return the content of each document through a loop (ForOf), the function I do it more than anything to save me too many lines of code, the problem is that it always throws me an error that I do not know what it is.
Can you help me? Please
Cloud function
export const employees = functions.https.onRequest((request, response) => {
corsHandler(request, response, async () => {
return await security.securityLayer(
{ _definedMethod: "GET", userValue: request.method },
{ _definedType: true, _definedLevel: [4], _definedSeconds: 12, userToken: request.header("_token") },
{ required: false },
{ required: false }
).then(async (answer) => {
if (answer.status === 400 || answer.status === 401) {
return response.status(answer.status).send(answer);
}
return await security.getDocuments([
{ collection: "Core/", documentName: "Centers", options: { idReturn: "centros", nestedProperties: [] } },
{
collection: "Core/", documentName: "Employees", options: {
idReturn: "personal",
nestedProperties: [
{ idReturn: "employees", name: "employee" },
{ idReturn: "submanager", name: "submanager" },
{ idReturn: "manager", name: "manager" }
],
},
},
], SPECIAL_CODE).then((documents) => response.status(documents.status).send(documents))
.catch(() => response.status(500).send(security.error500(SPECIAL_CODE, 2)));
}).catch(() => response.status(500).send(security.error500("SPECIAL_CODE", 1)));
});
});
async function
export async function getDocuments(
documents: {
collection: string,
documentName: string,
options: {
idReturn: string,
nestedProperties: {
idReturn: string,
name: string
}[]
}
}[],
code: string):
Promise<{ status: 201, code: string, subcode: number, devDescription: string, data: any }> {
const data: any = {};
const response: { devDescription: string, subcode: number } = { devDescription: "The document was found and retrieved successfully.", subcode: 1 };
if (documents.length > 1) {
response.devDescription = "Documents were found and obtained successfully.";
response.subcode = 2;
}
for (const iterator of documents) {
const docRef = { path: iterator.collection, name: iterator.documentName };
const options = { id: iterator.options.idReturn, nestedProperties: iterator.options.nestedProperties };
const doc = await database.collection(docRef.path).doc(docRef.name).get();
if (!doc.exists) {
data[options.id] = "The document " + docRef.name + " does not exist in the specified path: " + docRef.path;
if (documents.length === 1) {
response.devDescription = "The document was not found. Check the DATA for more information.";
response.subcode = 3;
} else {
response.devDescription = "One, several or all documents were not found. Check the DATA for more information.";
response.subcode = 3;
}
} else {
const docData: any = doc.data();
if (options.nestedProperties.length === 0) {
data[options.id] = docData;
} else {
for (const nested of options.nestedProperties) {
data[options.id][nested.idReturn] = _.get(docData, nested.name);
}
}
}
}
return { status: 201, code: code, subcode: response.subcode, devDescription: response.devDescription, data: data };
}
I was investigating and I saw that what was causing the error was obviously the loop (ForOf), to solve it I used the Promise.all() method, so the actual code that works for me is the following
export async function getDocuments(
documents: {
collection: string,
documentName: string,
path?: string,
options: {
idReturn: string,
nestedProperties: {
idReturn: string,
name: string
}[]
}
}[],
code: string):
Promise<{ status: number, code: string, subcode: number, devDescription: string, data: any }> {
const idPrimary: any = Object.values(
documents.reduce((c: any, v: any) => {
const k = v.options.idReturn;
c[k] = c[k] || [];
c[k].push(v);
return c;
}, {})
).reduce((c: any, v: any) => (v.length > 1 ? c.concat(v) : c), []);
if (idPrimary.length > 0) {
return {
status: 400, code: code, subcode: 0, data: idPrimary,
devDescription: "Some return IDs are repeated, check your code and replace the return IDs with unique IDs, for more information see the DATA section." };
}
const response: { devDescription: string, subcode: number } = { devDescription: "The document was found and retrieved successfully.", subcode: 1 };
const queries = [];
if (documents.length > 1) {
response.devDescription = "Documents were found and obtained successfully.";
response.subcode = 2;
}
documents.map((document) => {
if (document.path === undefined) {
document.path = document.collection + "/" + document.documentName;
}
});
for (const iterator of documents) {
queries.push(database.collection(iterator.collection).doc(iterator.documentName).get());
}
return Promise.all(queries).then((snapShot) => {
const data: any = {};
snapShot.forEach((doc) => {
const docProperties = documents.find((item) => item.path === doc.ref.path) ?? null;
if (!doc.exists) {
if (docProperties !== null) {
data[docProperties.options.idReturn] = "The document " + doc.id + " does not exist in the specified path: " + doc.ref.path;
if (documents.length === 1) {
response.devDescription = "The document was not found. Check the DATA for more information.";
response.subcode = 3;
} else {
response.devDescription = "One, several or all documents were not found. Check the DATA for more information.";
response.subcode = 3;
}
}
} else {
if (docProperties !== null) {
const docData: any = doc.data();
if (docProperties.options.nestedProperties.length === 0) {
data[docProperties.options.idReturn] = docData;
} else {
data[docProperties.options.idReturn] = {};
for (const nested of docProperties.options.nestedProperties) {
if (nested.name === undefined) {
data[docProperties.options.idReturn][nested.idReturn] = _.get(docData, nested.idReturn);
} else {
data[docProperties.options.idReturn][nested.idReturn] = _.get(docData, nested.name);
}
}
}
}
}
});
return { status: 201, code: code, subcode: response.subcode, devDescription: response.devDescription, data: data };
});
}
I have API query and getting the result and setting those in a state variable in Oncompleted method of API query, Now i am updating the same state variable in another api query "onCompleted method.
I am not able to access the result from state what i have set before in first api query and below is my code
Query 1:
const designHubQueryOnCompleted = designHubProject => {
if (designHubProject) {
const {
name,
spaceTypes
} = designHubProject;
updateState(draft => { // setting state here
draft.projectName = name;
draft.spaceTypes = (spaceTypes || []).map(po => {
const obj = getTargetObject(po);
return {
id: po.id,
name: obj.name,
category: obj.librarySpaceTypeCategory?.name,
description: obj.description,
warning: null // trying to modify this variable result in another query
};
});
});
}
};
const { projectDataLoading, projectDataError } = useProjectDataQuery(
projectNumber,
DESIGNHUB_PROJECT_SPACE_TYPES_MIN,
({ designHubProjects }) => designHubQueryOnCompleted(designHubProjects[0])
);
Query 2:
const {
// data: designhubProjectSpaceTypeWarnings,
loading: designhubProjectSpaceTypeWarningsLoading,
error: designhubProjectSpaceTypeWarningsError
} = useQuery(DESIGNHUB_PROJECT_LINKED_SPACETYPE_WARNINGS, {
variables: {
where: {
projectNumber: { eq: projectNumber }
}
},
onCompleted: data => {
const projectSpaceTypeWarnings = data.designHubProjectLinkedSpaceTypeWarnings[0];
const warnings = projectSpaceTypeWarnings.spaceTypeWarnings.reduce((acc, item) => {
const spaceTypeIdWithWarningState = {
spaceTypeId: item.spaceTypeProjectObjectId,
isInWarningState: item.isInWarningState
};
acc.push(spaceTypeIdWithWarningState);
return acc;
}, []);
console.log(state.spaceTypes); // trying to access the state here but getting empty array
if (state.spaceTypes.length > 0) {
const updatedSpaceTypes = state.spaceTypes;
updatedSpaceTypes.forEach(item => {
const spaceTypeWarning = { ...item };
spaceTypeWarning.warning = warnings?.filter(
w => w.spaceTypeId === spaceTypeWarning.id
).isInWarningState;
return spaceTypeWarning;
});
updateState(draft => {
draft.spaceTypes = updatedSpaceTypes;
});
}
}
});
Could any one please let me know where I am doing wrong with above code Or any other approach to modify the state, Many thanks in advance!!
I have 2 schemas
const schema = Schema({
headLine: {
type: String,
required: false
},
availableDays: [{
type: Schema.Types.ObjectId,
ref: AvailableDay
}]
}, {collection: 'providers', timestamps: true});
module.exports = mongoose.model("Provider", schema);
const schema = Schema({
day: {
type: String,
enum: ['Mondays','Tuesdays','Wednesdays','Thursdays','Fridays','Saturdays','Sundays']
},
timeFrom: String,
timeTo: String
}, {collection: 'availableDays', timestamps: true});
module.exports = mongoose.model("AvailableDay", schema);
Then in a route I call to a repository like this
router.get('/', async (req, res) => {
const match = {};
const sort = {};
const options = {};
// Arrange sort
if(req.query.sortBy){
const sortArray = JSON.parse(req.query.sortBy);
sortArray.map(e => sort[e[0]] = e[1] && e[1] === 'desc' ? -1 : 1);
options['sort'] = sort
}
// Get the pagination: limit how many, skip where it starts
if(req.query.limit) {
options['limit'] = parseInt(req.query.limit);
}
if(req.query.skip) {
options['skip'] = parseInt(req.query.skip);
}
const docs = await ProviderRepository.findBy(match, {}, options);
res.status(200).json(docs)
});
So what I need here is to filter providers for an AvailableDay monday and return the docs and count the total docs for pagination. I'm doing something like this without success
const findBy = async (params, projection = "", options = {}, callback) => {
const data = () => {
Provider.find(params, projection, options)
.populate([{path: 'user', match: {gender: 'F'}}]).exec((error, e) => {
if (error) {
console.log('error:', error)
return {error: error}; // returns error in json
}
return e.filter(i => i.user);
});
};
const total = await Provider.countDocuments(params).exec();
return {data(), total}
}
Thanks in advance
Use mongoose-aggregate-paginate-v2 and update your schema. If you use that package then you have to convert your queries from populate to aggregate style.
STEP 1: Update schema. Sample Schema:
const mongoose = require('mongoose');
const mongoosePaginate = require('mongoose-aggregate-paginate-v2');
const Schema = mongoose.Schema;
let definition = {
headLine: {
type: String,
required: false
},
availableDays: [{
type: Schema.Types.ObjectId,
ref: AvailableDay
}]
};
let options = {
collection: 'providers'
};
let providerSchema = new Schema(definition, options);
providerSchema.plugin(mongoosePaginate);
module.exports = mongoose.model('providers', providerSchema);
STEP 2: Update controller. Sample code in controller:
router.get('/', async (req, res) => {
const match = {}
const sort = {
// Fill it based on your sort logic.
}
const paginateOptions = {
page: req.query.page, // Page number like: 1, 2, 3...
limit: req.query.limit // Limit like: 10, 15, 20...
};
ProviderRepository
.findBy(match, {}, sort, paginateOptions)
.then(() => {
res.status(200).json(docs)
})
.catch(() => {
res.status(HTTP_ERROR_CODE).json({ "error": "Your error message" })
})
});
STEP 3: Update manager. Sample code in manager:
const findBy = (match, projection, sort, paginateOptions) => {
if (!paginateOptions) {
paginateOptions = {
pagination: false
};
}
let providerAggregate = providerSchema.aggregate([
{
$lookup: {
from: "availableDays",
let: { days: "$availableDays" },
pipeline: [
{
$match: {
$expr: {
$in: ["$$availableDays", "$day"]
}
}
}
],
as: "availableDays"
}
},
{
$lookup: {
from: "users", // I dont know the collection name
let: { user_id: "$user" }
pipeline: [
{
$match: {
"gender": 'F',
$expr: {
$eq: ["$_id", "$$user_id"]
}
}
}
],
as: "users"
}
}
{ $sort: sort }
]);
return providerSchema
.aggregatePaginate(providerAggregate, paginateOptions)
.then(res => {
return res;
})
.catch(err => {
throw err;
});
};
I'm calling 1 query and mutation. Mutation works fine, but when I get response from my query I need to redirect user to another page, but In my case, the function is triggered before I get response. How can I prevent this?
const renderData = async () => {
const currentUserId = await data?.signInUserSession?.idToken
?.payload?.sub;
const isAdmin = await data?.signInUserSession?.idToken?.payload[
"custom:role"
];
localStorage.setItem("userId", currentUserId);
if (
currentUserId !== null &&
currentUserId !== undefined &&
currentUserId !== ""
) {
Auth.currentSession().then((data) => {
setData({
variables: {
updateUserInput: {
id: currentUserId,
firstName: data.getIdToken().payload.given_name,
lastName: data.getIdToken().payload.family_name,
},
},
});
});
isCodeValid({
variables: {
validateUserVerificationCodeInput: {
user: {
id: currentUserId,
},
},
},
});
if (isAdmin === "admin" && isUserCodeValid) {
history.push("/managements");
} else if (
isUserCodeValid !== undefined &&
isUserCodeValid === true
) {
history.push("/verification");
} else if (isUserCodeValid) {
history.push("/stripe");
}
}
};
isUserCodeValid - is a response from query
useMutation has onCompleted and refetchQueries options for such cases. It is hard to write an exact solution for your case since not all code is visible but an example like below can help, I believe:
const [addProduct, { data, loading, error }] = useMutation(
createProductMutation
);
const onFinish = async (fieldNames) => {
await addSpending({
variables: { ...others, ...fieldNames},
refetchQueries: [{ query: calledQuery }],
onCompleted: (data) => {
// your logic
},
});
if (!error) {
form.resetFields();
onFinishSave(true);
}
};
I'm using an rxjs epic as a middleware for an async action in a react-redux app.
I'm trying to simulate an ajax request (through a dependency injection) and test the behavior of this epic based on the response.
This is my epic :
export const loginEpic = (action$, store$, { ajax }) => { // Ajax method is injected
return action$.ofType(LoginActions.LOGIN_PENDING).pipe(
mergeMap(action => {
if (action.mail.length === 0) {
return [ loginFailure(-1) ]; // This action is properly returned while testing
} else {
return ajax({ ... }).pipe(
mergeMap(response => {
if (response.code !== 0) {
console.log(response.code); // This is logged
return [ loginFailure(response.code) ]; // This action is expected
} else {
return [ loginSuccess() ];
}
}),
catchError(() => {
return [ loginFailure(-2) ];
})
);
}
})
);
};
This part test if the mail adress is empty and works just fine (Or at least just as expected):
it("empty mail address", () => {
testScheduler.run(({ hot, expectObservable }) => {
let action$ = new ActionsObservable(
hot("a", {
a: {
type: LoginActions.LOGIN_PENDING,
mail: ""
}
})
);
let output$ = loginEpic(action$, undefined, { ajax: () => ({}) });
expectObservable(output$).toBe("a", {
a: {
type: LoginActions.LOGIN_FAILURE,
code: -1
}
});
});
});
However, I have this second test that fails because the actual value is an empty array (There is no login failed returned):
it("wrong credentials", () => {
testScheduler.run(({ hot, cold, expectObservable }) => {
let action$ = new ActionsObservable(
hot("a", {
a: {
type: LoginActions.LOGIN_PENDING,
mail: "foo#bar.com"
}
})
);
let dependencies = {
ajax: () =>
from(
new Promise(resolve => {
let response = {
code: -3
};
resolve(response);
})
)
};
let output$ = loginEpic(action$, undefined, dependencies);
expectObservable(output$).toBe("a", {
a: {
type: LoginActions.LOGIN_FAILURE,
code: -3
}
});
});
});
Any idea on what I'm doing wrong / why this part returns an empty array (The console.log does actually log the code):
if (response.code !== 0) {
console.log(response.code);
return [ loginFailure(response.code) ];
}
While this part returns a populated array:
if (action.mail.length === 0) {
return [ loginFailure(-1) ];
}
I'm guessing the use of Promise is causing the test to actually be asynchronous. Try changing the stub of ajax to use of(response) instead of from