How do I increment automatically a variable when calling a Cypress method? - javascript

createUserPatch is an API custom command to create a new User.
You can see that I have created a variable "A" inside it.
The variable is used in the body emails part [a]+'freddie.doe#example.com','type': 'work','primary': true}]
I want to find a way to automatically increase the variable "A" whenever I call the command createUserPatch.
Cypress.Commands.add('createUserPatch', (user) => {
var A = 1;
cy.request({
method: 'POST',
url: '/scim/v2/users',
qs: {
key : Cypress.env('apiKey')
},
body :{
schemas: user.schemas,
userName: user.userName,
emails: [{ 'value': [A]+'freddie.doe#example.com','type': 'work','primary': true}],
name : 'Maurice'
}
}).then(res => {
return res.body;
});
});
I use this command in the test below in a before each.
let user = {
schemas:'["urn:ietf:params:scim:schemas:core:2.0:User"]',
userName: 'John',
userId: null,
groupID: null
};
describe('Deactivating a user ', () => {
beforeEach(() => {
cy.createUserPatch(user).then((newUser) => {
return user.userId = newUser.id;
});
});
....
Each time I run this test.
I want the value of the email to be increased.
First test -> 0freddie.doe#example.com
Second test -> 1freddie.doe#example.com
Third test -> 2freddie.doe#example.com
etc...

Cypress clears variables between tests, but a few ways to preserve data have been suggested in other questions (e.g write data to file).
Since you already use Cypress.env(name) to access environment variables you could use Cypress.env(name, value) to track the prefix.
Cypress.Commands.add('createUserPatch', (user) => {
let prefix = Cypress.env('emailprefix') || 0; // first time it may be undefined
// so "|| 0" initializes it
Cypress.env('emailprefix', ++prefix); // increment and save
cy.request({
...
emails: [{ 'value': prefix+'freddie.doe#example.com','type': 'work','primary': true}],
Note, the prefix value will be preserved between runs, which may or may not be what you want.
To clear it between runs, add a before() which resets the value
before(() => Cypress.env('emailprefix', 0) );
beforeEach(() => {
cy.createUserPatch().then(console.log)
})

Related

Custom merge function is not being called after updating field with cache.modify

I have written a custom merge function for the field products on type Session. It seems the merge function is only being called when I initialise the object Session:1 with its products, and not when I update products later using cache.modify.
My merge function:
const client = new ApolloClient({
uri: 'http://localhost:8081/graphql',
cache: new InMemoryCache({
typePolicies: {
Session: {
fields: {
products: {
merge (existing, incoming) {
// this is only being called on useQuery(HydrateSession), not useMutation(UpsertProduct)
console.log('existing', JSON.stringify(existing, null, 2))
console.log('incoming', JSON.stringify(incoming, null, 2))
// remove duplicates when latestProduct has the same id as an existing product — [..., latestProduct]
if (incoming.filter(p => p.id === incoming[incoming.length - 1].id).length > 1) return existing
return incoming
}
}
}
}
}
})
})
Initialisation of Session:
const HydrateSession = gql`
query {
session {
id
products {
id
}
}
}
`
...
useQuery(HydrateSession)
Updating products later using cache.modify:
const UpsertProduct = gql`
mutation UpsertProduct($product: ProductInput!) {
upsertProduct(product: $product) {
id
}
}
`
...
const [upsertProductMutation] = useMutation(UpsertProduct)
const onClick = async () => {
await upsertProductMutation({
variables: {
product: {
id: 2
}
},
update: (cache, mutationResult) => {
cache.modify({
id: 'Session:1',
fields: {
products: previous => [...previous, mutationResult.data.upsertProduct]
}
})
}
})
}
I have a full working example here https://github.com/jsindos/apollo-play, run npm i and then start two separate processes with npm start and npm serve. After clicking the button triggering the mutation, the merge function is not run (as seen by the absence of console.log statements in the console).
modify circumvents any merge functions you've defined, which means that fields are always overwritten with exactly the values you specify.
https://www.apollographql.com/docs/react/caching/cache-interaction/#using-cachemodify
Reading documentation is a good thing.

Jest - How do I reset object state for each test?

I am new to Jest, and I am trying to figure out how to to reset the test object after each test.
Current Code
describe.only('POST request - missing entry', () => {
// newBlog is the "test" object
let newBlog = {
title: 'Test Title',
author: 'Foo Bar',
url: 'www.google.com',
likes: 100
}
test('sets "likes" field to 0 when missing', async () => {
delete newBlog.likes // propagates to next test
console.log(newBlog)
})
test('returns 400 error when "title" and "url" fields are missing', async () => {
console.log(newBlog)
})
})
Objective: I am writing test using jest to test for bad POST request. i.e. my POST request will intentionally have missing fields for each test.
likes field will be omitted from first test while title, url field will be missing from second test. Goal is to write newBlog object only once rather than rewriting objects for each tests.
Problem Main issue here is that the result of first test propagates to next test, i.e. when removing likes field for first test, it stays like that and starts the second test without having likes field.
I want to know how I can reset the content of object for each test.
Attempts So far, I tried few things:
I used BeforeEach to reset the newBlog in following manner:
beforeEach(() => {
let newBlog = {
title: 'Test Title',
author: 'Foo Bar',
url: 'www.google.com',
likes: 100
}
return newBlog
})
However, above code does not work since newBlog is in different scope so each test does not recognize newBlog variable.
I also used AfterEach to reset in following manner:
afterEach(() => {
jest.clearAllMocks()
})
This time, it ran but gave me the same results as first code snippet.
I would like to know how to reset objects for each test as many of the solution discussed in stackoverflow seems to focus on resetting functions rather than objects.
Thank you for your help in advance.
Try something like this, declare the variable in the describe and reset it in the beforeEach:
describe.only('POST request - missing entry', () => {
// newBlog is the "test" object
let newBlog;
beforeEach(() => {
newBlog = {
title: 'Test Title',
author: 'Foo Bar',
url: 'www.google.com',
likes: 100
}
});
test('sets "likes" field to 0 when missing', async () => {
delete newBlog.likes // propagates to next test
console.log(newBlog)
})
test('returns 400 error when "title" and "url" fields are missing', async () => {
console.log(newBlog)
})
})
You were correct in needing to use Jest's beforeEach() function; however, the only things that are returnable from beforeEach() are promises and generators—returning newBlog from beforeEach() does nothing. What I would do is create a local variable in the describe function and have beforeEach() reassign that variable before each test runs as seen below.
fdescribe('POST request - missing entry', () => {
let newBlog;
beforeEach(() => {
newBlog = {
title: 'Test Title',
author: 'Foo Bar',
url: 'www.google.com',
likes: 100
}
});
test('sets "likes" field to 0 when missing', async () => {
delete newBlog.likes; // remove likes key from copied object
console.log(newBlog);
});
test('returns 400 error when "title" and "url" fields are missing', async () => {
delete newBlog.title;
delete newBlog.url;
console.log(newBlog);
});
})
Additionally, jest.clearAllMocks() clears all calls and instances of a mocked function which will not reset the newBlog variable in the way you want to use it here.
I usually deep copy the test object in this kind of scenarios. So I have a helper function (you can use e.g. Lodash's deepClone function too)
const deepCopy = (obj: Object) => JSON.parse(JSON.stringify(obj));
In your tests you create a new deep copy of the wanted test object instead of mutating it's state like this:
test('sets "likes" field to 0 when missing', async () => {
let testObj = deepCopy(newBlog)
delete testObj.likes // propagates to next test
console.log(testObj)
})

graphql passing dynamic data to mutation

haven't used graphql or mongodb previously. What is the proper way to pass objects for the update mutation?
Since the only other way i see to pass multiple dynamically appearing parameters is to use input type which is appears to be a bit ineffective to me (in terms of how it looks in the code, especially with bigger objects), i just pass the possible values themselves. however in this case i need to dynamically construct updateObject, which again, going to get messy for the bigger models.
for example now i did:
Mutation: {
updateHub: async (_, { id, url, ports, enabled }) => {
const query = {'_id': id};
const updateFields = {
...(url? {url: url} : null),
...(ports? {ports: ports} : null),
...(enabled? {enabled: enabled} : null)
};
const result = await HubStore.findByIdAndUpdate(query, updateFields);
return {
success: !result ? false : true,
message: 'updated',
hub: result
};
}
}
any advise on the better way to handle this?
thanks!
It appears your code could benefit from using ES6 spread syntax -- it would permit you to deal with an arbitrary number of properties from your args object without the need for serial tertiary statements.
Mutation: {
updateHub: async (_, { id, ...restArgs } ) => {
const query = {'_id': id};
const updateFields = { ...restArgs };
const result = await HubStore.findByIdAndUpdate(query, updateFields);
return {
success: !result ? false : true,
message: 'updated',
hub: result
};
}
}
If for some reason you need to explicitly set the undefined properties to null in your object, you could possibly use some a config obj and method like defaults from the lodash library as shown below:
import { defaults } from 'lodash';
const nullFill = { url: null, ports: null, enabled: null }; // include any other properties that may be needed
Mutation: {
updateHub: async (_, { id, ...restArgs } ) => {
const query = {'_id': id};
const updateFields = defaults(restArgs, nullFill);
const result = await HubStore.findByIdAndUpdate(query, updateFields);
return {
success: !result ? false : true,
message: 'updated',
hub: result
};
}
}
Also, FWIW, I would consider placing the dynamic arguments that could be potentially be updated on its own input type, such as HubInput in this case, as suggested in the graphql docs. Below I've shown how this might work with your mutation. Note that because nothing on HubInput is flagged as requird (!) you are able to pass a dynamic collection of properties to update. Also note that if you take this appraoch you will need to properly destructure your args object initially in your mutation, something like { id, input }.
input HubInput {
url: String
ports: // whatever this type is, like [String]
enabled: Boolean
// ...Anything else that might need updating
}
type UpdateHubPayload {
success: Boolean
message: String
hub: Hub // assumes you have defined a type Hub
}
updateHub(id: Int, input: HubInput!): UpdateHubPayload

Sequelizejs - Then result issue

I'm work with NodeJS and Sequelize. I have the following issue:
Read the Settings Table:
Settings.findOne({where: {
user_id: data
}})
.then(settings => {
// Next request
});
I need to save the settings.device (Example) outside of the .then block.
But if I do that like
var device;
Settings.findOne({where: {
user_id: data
}})
.then(settings => {
device = settings.device;
});
It doesn't work.
Already the error output undefined
The output in .then result block with console.log(settings.device); works perfect.
Update
I need it like:
var array = [];
// Get Settings from table
Settings.findOne({where: {
user_id: data
}})
.then(settings => {
// Get token from other Table
Example.findOne({where: {
user_id: data
}})
.then(example => {
// push to array
array.push({
"user_id" : data,
"device":settings.device, // output: settings.device is undefined
"token": example.token
});
});
});
// Send array to client
This is really a question of how to handle multiple resolved values in a Promise chain. You can search for that and see lots of great examples on how to handle it. For example, you could return an array or object in each then handler, or re-assign values to higher-scoped variables (as you're doing with settings). I've used both methods heavily in the past, and the resulting code is obviously inelegant and not fun to write.
However, async/await is readily available in Node and simplifies your code quite a bit:
const array = [];
// Get settings.
const settings = await Settings.findOne({where: {
user_id: data
}});
// Get token.
const example = await Example.findOne({where: {
user_id: data
}});
array.push({
user_id : data,
device: settings.device,
token: example.token
});
Sequelize return the model object you can get value by dataValue
console.log(settings.dataValues.device);
or if you want lean data
Settings.findOne({where: {
user_id: data,
raw:true,
}})
.then(settings => {
device = settings.device;
console.log(device);
});

Axios conditional parameters

axios.get(globalConfig.FAKE_API, {
params: {
phone: this.phone,
mail: this.mail
},
})
.then((resp) => {
this.req = resp.data
})
.catch((err) => {
console.log(err)
})
Is there any way I can make conditional parameters when doing GET / POST requests with Axios? For example, if my mail parameter is empty, i won't send an empty parameter, like: someurl.com?phone=12312&mail=
Either you can maintain a variable of params before making a request and only add key if it has data like:
const params = {}
if (this.mail) { params.mail = this.mail }
or you can do like below, we write normal js code between ...() the parenthesis. We are adding a ternary condition.
axios.get(globalConfig.FAKE_API, {
params: {
phone: this.phone,
...(this.mail ? { mail: this.mail } : {})
},
})
Reghav Garg's idea looks neat at first glance, but with more than one optional parameter, I'm afraid it will get messy.
You could simply use one of the common utility libraries like underscore or lodash and utilize their filter function:
const allParams = {
mail: this.mail,
phone: this.phone,
// ...
};
axios.get(globalConfig.FAKE_API, {
// 'Pick' takes only those elements from the object
// for which the callback function returns true
//
// Double negation will convert any value to its boolean value,
// so null becomes false etc.
params: _.pick(allParams, (value, key) => { return !!value; })
})

Categories

Resources