Is there a clean way for a mobx when() call to fire after a timeout even if the predicate hasn't been true?
I could set an observable after a setTimeout and check that in the predicate, but then I'd have more to clean up. when has an options parameter but I haven't found the documentation for it.
Use whenWithTimeout from mobx-utils package
If you are using MobX 4.0.0 or higher you can just pass timeout directly to when as its option (3rd parameter). It has been mentioned in the changelog
pass the optional params as an object...
when(predicate: () => boolean, effect?: () => void, options?);
when(() => true,
() => { ..do something },
{ timeout: 1000 }
)
Related
Im looking to use $auth inside my Nuxt project, specially inside an axios plugin.
Here is my code:
plugins/api.js
export default function ({ $axios }, inject) {
const api = $axios.create({
headers: {
common: {
Accept: 'text/plain, */*',
},
},
})
// Set baseURL to something different
api.setBaseURL('http://localhost:4100/')
// Inject to context as $api
inject('api', api)
}
Now the problem comes when I try to use $auth from #nuxtjs/auth-next package.
As stated in the docs:
This module globally injects $auth instance, meaning that you can
access it anywhere using this.$auth. For plugins, asyncData, fetch,
nuxtServerInit and Middleware, you can access it from context.$auth.
I tried the following:
This results in $auth being undefined
export default function ({ $axios, $auth }, inject) {
This one was near
export default function ({ $axios, app }, inject) {
console.log(app) //This one logs $auth in the object logged
console.log(app.$auth) // I don't understand why but this one returns undefined
My main goal here is to make use of this.$auth.strategy.token.get()and pass it (if the token exists of course) to the headers of every request made using this.$api
I have been looking for similar questions and answers but none has helped me to solve this, I could just add the token every time I write this.$api but that would increase the code unnecessarily.
Thanks in advance to all the people for your time and help.
EDIT:
Okay, now I made a test. and the next code is actually logging the $auth object correctly, it seems some time is needed to make it work but now Im afraid that using setTimeout could cause an error because I can't know exactly how much time is needed for $auth to be available.
export default function ({ $axios, app }, inject) {
setTimeout(() => {
console.log('After timeout', app.$auth)
}, 50)
EDIT 2:
So now I have made more tests, and using 0 milliseconds instead of 50 works too, so I will use setTimeout with 0 milliseconds for now, I hope anyone find a better solution or explain why $auth is not available before using setTimeout so I can decide what to do with my code.
EDIT 3:
After trying to wrap all my previous code inside setTimeout I noticed that the code fails, so that isn't a solution.
I have found a solution so I will post it so that every person that could have the same problem in the future can solve it.
It turns out that I could easily solve it using interceptors.
export default function ({ $axios, app }, inject) {
// At this point app.$auth is undefined. (Unless you use setTimeout but that is not a solution)
//Create axios instance
const api = $axios.create({
headers: {
common: {
Accept: 'application/json', //accept json
},
},
})
// Here is the magic, onRequest is an interceptor, so every request made will go trough this, and then we try to access app.$auth inside it, it is defined
api.onRequest((config) => {
// Here we check if user is logged in
if (app.$auth.loggedIn) {
// If the user is logged in we can now get the token, we get something like `Bearer yourTokenJ9F0JFODJ` but we only need the string without the word **Bearer**, So we split the string using the space as a separator and we access the second position of the array **[1]**
const token = app.$auth.strategy.token.get().split(' ')[1]
api.setToken(token, 'Bearer') // Here we specify the token and now it works!!
}
})
// Set baseURL to something different
api.setBaseURL('http://localhost:4100/')
// Inject to context as $api
inject('api', api)
}
Also Nuxt Auth itself has provided a solution for this issue:
https://auth.nuxtjs.org/recipes/extend/
I have an API automation test suite using Cypress and one of the issue I am facing in one of the test is to validate the response headers.
For some reason, I am not able to read the response headers using Cypress.
The code is below
cy.request({
method:'GET',
url:Cypress.env("Authorisationurl")+tokenId+'&decision=allow&acr_values=1',
followRedirect: false,
headers:{
'Accept': "/*"
}
}).then((response) => {
const rbody = (response.body);
cy.log(response.status)
//THIS GOT ASSERTED TO TRUE
expect(response.status).to.equal(302)
//OPTION1
cy.wrap(response.headers['X-Frame-Options']).then(() => {
return response.headers['X-Frame-Options'];
});
//OPTION2
return response.headers['X-Frame-Options']
//OPTION3
return response.headers
})
None of the above options gives me the header information. Infact I am confused with the order of execution too.
This is my output.
for the following code.
const rbody = (response.body);
cy.log(response.status)
cy.log(response)
expect(response.status).to.equal(302)
cy.log(response.headers)
cy.log(response.headers['X-Frame-Options'])
return response.headers['X-Frame-Options']
Also, not very sure what Object{9} indicates. Can anyone please explain what is happening here.
I am aware of Cypress flow of execution and the code is written in then block as a call back function.
Option 3 is very scary as it gives an error
cy.then() failed because you are mixing up async and sync code.
In your callback function you invoked 1 or more cy commands but then returned a synchronous value.
Cypress commands are asynchronous and it doesn't make sense to queue cy commands and yet return a synchronous value.
You likely forgot to properly chain the cy commands using another cy.then().
The value you synchronously returned was: Object{9}
Can anyone please help me here as in what is the correct way of doing it. I know Cypress is very quick and easy to use but to move away from Selenium, we need to make coding easier for developers with meaningful error message. Object{9} is not very helpful.
Also, Do I need to use Cy.log ? As the sequence of prints is not what I have written in the code. Thanks very much for your time on this.
Please use like this:
JSON.parse(JSON.stringify(response.headers))["X-Frame-Options"];
The "mixing async and sync code" message is basically saying you should keep the .then() callback simple.
But you can chain more than one .then() to run the async and sync code separately.
Use an alias to "return" the value. Since cy.request() is asynchronous, you will need to wait for the value and the alias pattern is the most straight-forward way to do this reliably.
WRT Object{9}, it's the result of the way Cypress logs complex objects.
Don't use cy.log() to debug things, use console.log().
cy.request( ... )
.then(response => {
expect(response.status).to.equal(200) // assert some properties
})
.then(response => response.headers) // convert response to response.headers
.as('headers')
cy.get('#headers')
.then(headers => {
console.log(headers)
})
Since this was originally posted a year ago and Cypress has had many versions since then, the behavior may have changed, however this works in Cypress 11.
You can access the response.headers array as you would normally expect, however the casing of the header name was not as expected, Postman reported the header as X-Frame-Options, but Cypress would only allow me to access it if I used a lower-cased version of the header name (x-frame-options).
cy.request({
url: '<Url>',
method: 'GET',
failOnStatusCode: false
})
.then((response) => {
expect(response.status).to.eq(200);
expect(response.headers["x-frame-options"]).to.equal("SameOrigin");
})
.its('body')
.then((response) => {
// Add your response testing here
});
});
Although I know this may not be considered as a best practice, but what I want to achieve is to silently delete a record from a database after the same was created throughout UI. In htat way I want to keep our test environment clear as much as possible and reduce the noise of test data.
After my tests create a new record by clicking over the UI, I wait for POST request to finish and then I would like to extract the id from the response (so I can reuse it to silently delete that record by calling the cy.request('DELETE', '/id')).
Here's a sample test I have put on as a showcase. I'm wondering why nothing is logged in this example.
it('GET cypress and log', () => {
cy.server()
.route('**/repos/cypress-io/cypress*')
.as('getSiteInfo');
cy.visit('https://www.cypress.io/dashboard');
cy.get('img[alt="Cypress.io"]')
.click()
.wait('#getSiteInfo')
.then((response) => {
cy.log(response.body)
})
})
As far as I can see from here https://docs.cypress.io/api/commands/wait.html#Alias this should be fine.
your code contains two problems.
First:
The click triggers a new page to be loaded but cypress does not wait until the PageLoad event is raised (because you do not use visit). On my PC the Request takes about 5 seconds until it is triggered after the click. So you should use wait(..., { timeout: 10000 }).
Second:
wait() yields the XHR object, not the response. So your code within then is not correct. Also the body is passed as object. So you should use JSON.stringify() to see the result in the command log.
This code works:
describe("asda", () => {
it('GET cypress and log', () => {
cy.server()
.route('**/repos/cypress-io/cypress*')
.as('getSiteInfo');
cy.visit('https://www.cypress.io/dashboard');
cy
.get('img[alt="Cypress.io"]')
.click()
.wait('#getSiteInfo', { timeout: 20000 })
.then((xhr) => {
cy.log(JSON.stringify(xhr.response.body))
})
})
})
Instead of route and server method, try intercept directly
I am using a customer express server with Next.js. It's running within a container. I am doing an http request with isomorphic-fetch to get data for my render. I'd like to do localhost when running on server and mysite.com when running on client. Not sure the best way to accomplish this. I can do it hackily by doing const isServer = typeof window === 'undefined' but that seems pretty bad.
Now (2020 Jan) it should be typeof window === 'undefined' since process.browser is deprecated
Refer to https://github.com/zeit/next.js/issues/5354#issuecomment-520305040
You can use process.browser to distinguish between server environment (NodeJS) and client environment (browser).
process.browser is true on the client and undefined on the server.
Since I don't like depending on odd third party things for this behavior (even though process.browser seems to come from Webpack), I think the preferred way to check is for presence of appContext.ctx.req like this:
async getInitialProps (appContext) {
if (appContext.ctx.req) // server?
{
//server stuff
}
else {
// client stuff
}
}
Source: https://github.com/zeit/next.js/issues/2946
One additional note is that componentDidMount() is always called on the browser. I often load the initial data set (seo content in getInitialProps(), then load more in depth data in the componentDidMount() method.
getServerSideProps and getStaticProps are added in Next 9.3(Mar 2020), and these functions are recommended.
If you're using Next.js 9.3 or newer, we recommend that you use getStaticProps or getServerSideProps instead of getInitialProps.
So no need to detect, just put server side stuff in getServerSideProps.
const MyPage = () => {
useEffect(() => {
// client side stuff
}, [])
return (
<div> ... </div>
)
}
MyPage.getServerSideProps = async () => {
// server side stuff
}
I'm trying to set a state from a fetch response, but it seems it takes a while to update the state.
What I learned is that fetch is quick until it comes to setState. There, it takes about 3 seconds to update.
fetch(ENDPOINT)
.then((response) => response.json())
.then((responseJson) => {
this.setState({product : responseJson.product, related: responseJson.related, ready: true});
})
.catch((error) => {
console.error(error);
}).done();
Any tips?
Thanks
setState is asynchronous.
From the docs of react itself if you see :-
setState() does not immediately mutate this.state but creates a pending state transition. Accessing this.state after calling this method can potentially return the existing value. There is no guarantee of synchronous operation of calls to setState and calls may be batched for performance gains.
You can read more about this in the documentation.
Also, you can check something similar here