I have custom function:
activate(seconds: number) {
of(true)
.pipe(delay(seconds))
.subscribe(
() => {
console.log("Run...");
},
(error) => console.log(error)
);
}
And I call this like:
activate(4);
Why I get console.log("Run..."); instantly, wihtout delay?
delay takes milliseconds as parameter you should do
delay(4000)
Related
We are using segment in our application and i need to implement an E2E test in order to verify the number of segment calls, i must be sure that every event will be called only once.
I've been searching for a while, i've found this command that verifies the number of api calls:
Cypress.Commands.add(`verifyCallCount`, (alias, expectedNumberOfCalls) => {
const resolvedAlias = alias[0] === `#` ? alias.substring(1) : alias;
cy.get(`${resolvedAlias}.all`, { timeout: 20000 }).then((calls) => {
cy.wrap(calls.length).should(`equal`, expectedNumberOfCalls);
});
});
I use this command after waiting for the api call:
cy.wait(`#${eventAlias}`, { timeout: 20000 })
.then((interception) => {
return JSON.parse(interception.request.body);
})
.then(() => cy.verifyCallCount(eventAlias, 1););
Here is also the place where i add my alias for the api call.
beforeEach(() => {
cy.intercept('POST', 'https://api.segment.io/v1', (req) => {
const body = JSON.parse(req.body);
if (body.hasOwnProperty('type') && body.type === SampleEvent) {
req.alias = eventAlias;
}
});
});
});
Using this approach, when i run the test on local environment, it passes without any problem. but the same test fails on github's actions. and this is the error:
AssertionError: Timed out retrying after 10000ms: Expected to find element: `eventAlias.all`, but never found it.
I think that the .get() command is not being executed after .wait(), i tried to change the order of the commands, but it's not helping.
How can i fix this problem in github actions?
Is there any other way to verify the number of api calls in cypress?
I appreciate any help, thank you.
The answer you used from here Verify number of times request was made is wrong.
The line const resolvedAlias = alias[0] === '#' ? alias.substring(1) : alias removes the initial #, but it needs to be kept.
Also the timeout in cy.get('${resolvedAlias}.all', { timeout: 20000 }) has no effect, it doesn't wait 20 seconds for all calls to happen.
In your test scenario there may be 0, 1, or 2 calls. You want to fail if there is 0 calls or 2 calls, and pass if there is exactly 1 call.
This is enough to fail if there is 0 calls
cy.wait(`#${eventAlias}`, { timeout: 20000 })
To fail if there are 2 calls, you must use a hard wait, then verify the call count
cy.wait(`#${eventAlias}`, { timeout: 20_000 })
cy.wait(2_000) // wait an interval for any extra call to occur
cy.get(`#${eventAlias}.all`)
.its('length')
.should(`equal`, 1); // if two calls happened in interval, fail here
I notice you mention github actions. I had similar problems when testing an API call in CI, the test runs much slower and cause flakiness.
I suggest mocking the response to get better, more consistent performance from your test.
Ref: Controlling the response
As a bonus, there is no need for any long timeout because your mock replies immediately.
beforeEach(() => {
cy.intercept('POST', 'https://api.segment.io/v1', (req) => {
const body = JSON.parse(req.body);
if (body.hasOwnProperty('type') && body.type === SampleEvent) {
req.alias = eventAlias;
// here send mock response without any network delay
req.reply({
headers: {
Set-Cookie: 'newUserName=Peter Pan;'
},
statusCode: 201,
body: {
name: 'Peter Pan'
}
})
}
});
});
})
it('tests there is only a single POST from app', () => {
cy.wait(`#${eventAlias}`)
cy.wait(100)
cy.get(`#${eventAlias}.all`).then((calls) => {
cy.wrap(calls.length).should(`equal`, 1);
});
})
Your goal is to ensure only 1 API call.
You will need the test to wait and see if a 2nd call occurs.
it('accurately test that only one API call happens', () => {
const numOfRequests = 1
cy.intercept('**/api/*', cy.spy().as('api-spy'))
cy.visit('/');
cy.wait(1000)
cy.get('#api-spy').its('callCount').should('equal', numOfRequests)
})
I tested with a simple page that deliberately calls twice, with a delay 100ms between calls,
<script>
fetch('api/1')
setTimeout(() => fetch('api/2'), 100) // delayed 2nd fetch we want to test for
</script>
Without the hard wait the test gives me a false pass.
I also tried inverting the logic, but it still needs a hard wait to test correctly
cy.intercept('**/api/*', cy.spy().as('api-spy'))
cy.visit('/');
cy.wait(1000)
cy.get('#api-spy').its('callCount')
.should('not.equal', 0)
.and('not.equal', 2) // false pass without hard wait
})
Counting inside the routeHandler that checks body.type
2nd alias for call count
before(() => {
cy.wrap(0).as('eventCount')
})
beforeEach(() => {
cy.intercept('POST', 'https://api.segment.io/v1', (req) => {
const body = JSON.parse(req.body);
if (body.hasOwnProperty('type') && body.type === SampleEvent) {
req.alias = eventAlias;
cy.get('#eventCount').then(count => {
cy.wrap(count + 1).as('eventCount')
})
}
});
});
});
it('checks the count', () => {
cy.visit('/');
cy.wait(1000)
cy.get('#eventCount')
.should('equal', 1)
})
Incrementing a global
let eventCount = 0;
beforeEach(() => {
cy.intercept('POST', 'https://api.segment.io/v1', (req) => {
const body = JSON.parse(req.body);
if (body.hasOwnProperty('type') && body.type === SampleEvent) {
req.alias = eventAlias;
eventCount += 1
}
});
});
});
it('checks the count', () => {
cy.visit('/');
cy.wait(1000)
.then(() => {
cy.wrap(eventCount)
.should('equal', 1)
})
})
When you want to get all of the alias calls, you will need to use # to signify the alias. So the custom command will need to be updated.
Cypress.Commands.add(`verifyCallCount`, (registeredAlias, expectedNumberOfCalls) => {
if(alias[0] !== '#') {
throw new Error ('alias does not start with '#')
}
cy.get(`${registeredAlias}.all`, { timeout: 20000 }).then((calls) => {
cy.wrap(calls.length).should(`equal`, expectedNumberOfCalls);
});
});
Usage
cy.intercept('call').as('call')
// some action to trigger call
cy.wait('#call')
// some other actions
cy.verifyCallCount('#call')
Is there any other way to verify the number of api calls in cypress?
This is a concise way to count the api calls and wait for them to finish.
You can pass a cy.spy() in as a "response" and you can use that to count the number of times the intercept was hit.
Using .should() in the Cypress assertion will wait until the expected number of requests to come back.
it('test', () => {
const numOfRequests = 5;
cy.intercept('https://api.segment.io/v1', cy.spy().as('api-spy'));
// Do something to trigger 5 requests
cy.get('#api-spy').its('callCount').should('equal', numOfRequests);
});
If there are a sequence of different endpoints you are waiting for such as /v1/login followed by a /v1/getData etc, the URL in the cy.intercept may need to use a wildcard.
For example:
cy.intercept('https://api.segment.io/v1/**')
i am new to cypress and i am trying to check if the element exists on a page once the api call is finished.
i do a http post to url 'things/thing1' and once this api finishes i want to check if span element is present on page.
i have tried something like below.
const setUp = () => {
cy.apiPatchSomethings(something1)
.then(() => {
cy.reload();
});
}
describe('Suite name', () => {
before(() => {
setUp();
});
it('test case', () => {
cy.contains('span');
}
});
the above code doesnt work. even before span element is seen on page it checks for span element.
if i use cy.wait(10000) like below it works
it('test case', () => {
cy.wait(10000);
cy.contains('span');
});
but i dont want to use cy.wait. is there some other way to solve this. could someone help me with this. thanks.
Cypress command cy.contains() when called with a single argument is looking for content,
Syntax
cy.contains(content)
cy.contains(content, options)
cy.contains(selector, content)
cy.contains(selector, content, options)
but I'm guessing you are looking for a span element, so use
cy.get('span')
or
cy.contains('span', 'my-content-in-span')
Assuming that's not the problem, just some arbitrary sample code...
Your can modify the setup function to return a promise, in order to wait for the reload.
const setUp = () => {
return cy.apiPatchSomethings(something1) // need a return here
.then(() => {
return new Cypress.Promise(resolve => { // inner return also
cy.reload()
resolve(true) // resolve will signal reload is finished
})
});
}
Because setup() is invoked inside before() Cypress will wait for the promise to resolve before proceeding.
Please don't add extra waits or timeouts, which is too often suggested. This will only lead to flaky tests.
Note if you don't mind ditching the setup() function, it becomes a lot simpler
describe('Suite name', () => {
before(() => {
cy.apiPatchSomethings(something1)
.then(() => cy.reload() ); // all commands here will be completed
// before the tests start
});
it('test case', () => {
cy.contains('span', 'my-content-in-span');
}
});
1.You can wait for span to be visible. The default timeout that cypress provides is 4 seconds.
cy.contains('span').should('be.visible')
2.If you want to give a custom timeout(eg. 10 sec) specific to this command, you can use:
cy.contains('span', { timeout: 10000 }).should('be.visible')
3.If you want to increase the timeout globally you mention this in your cypress.json file:
"defaultCommandTimeout": 10000
and, then just use:
cy.contains('span').should('be.visible')
Now, all your commands will have a default timeout for 10 seconds.
I am making an app in Laravel using Vue.js. I would like to wait two seconds when a method is triggered and then execute a store action. However, when I implement this I receive an error.
Here is my code:
.listen('TeamLeaving', e => {
setTimeout(function() {
axios.get('/api/team/' + e.team.id + '/pulse').then(response => {
if (response.data === 0) {
// here is where it messes up
this.$store.commit('team/REMOVE_TEAM', e.team)
}
})
}, 2000)
// this.$store.commit('team/REMOVE_TEAM', e.team);
})
However I get an error:
Uncaught (in promise) TypeError: Cannot read property 'commit' of undefined
When I do the commit outside of the setTimeout it works just fine. So I am assuming there is a problem inside the setTimeout. Could someone help me maneuver this?
This post might help you: how to set timeout in a vueJs method
The important bit:
this in anonymous function is attached to that anonymous function
not to your main function
You can try this:
.listen('TeamLeaving', (e) => {
let vm = this;
setTimeout(function () {
axios.get('/api/team/'+ e.team.id + '/pulse')
.then(response => {
if (response.data === 0) {
//here is where it messes up
vm.$store.commit('team/REMOVE_TEAM', e.team)
}
});
}, 2000);
// this.$store.commit('team/REMOVE_TEAM', e.team);
});
I'm running echo server and redis. Private channels work perfectly, and messaging I have built for it works. Now I'm trying to get the whisper to work for the typing status as well but no luck. Does whisper require a pusher to work?
What I have tried on keyup (jquery)
Echo.private(chat- + userid)
.whisper('typing',{e: 'i am is typing...'});
console.log('key up'); // this one works so the keyup is triggered
then I'm of course listening the channel what I am whispering into:
Echo.private(chat- + userid).listenForWhisper('typing', (e) => {
console.log(e + ' this is typing');
});
But I get absolutely nothing anywhere. (debugging on at the echo server, nothing on console etc) Any help how to get this to work would be much appreciated.
Your input event:
$('input').on('keydown', function(){
let channel = Echo.private('chat')
setTimeout( () => {
channel.whisper('typing', {
user: userid,
typing: true
})
}, 300)
})
Your listening event:
Echo.private('chat')
.listenForWhisper('typing', (e) => {
e.typing ? $('.typing').show() : $('.typing').hide()
})
setTimeout( () => {
$('.typing').hide()
}, 1000)
Of course you have to have setup the authentication for this channel ahead of time to ensure that the trusted parties have access:
Broadcast::channel('chat', function ($user) {
return Auth::check();
});
Where $user will be the userid we passed to the user param in our object on the front end.
This is what my ReactJS componentDidMount looks like.
Your listening event.
componentDidMount() {
let timer; // timer variable to be cleared every time the user whispers
Echo.join('chatroom')
.here(...)
.joining(...)
.leaving(...)
.listen(...)
}).listenForWhisper('typing', (e) => {
this.setState({
typing: e.name
});
clearTimeout(timer); // <-- clear
// Take note of the 'clearTimeout' before setting another 'setTimeout' timer.
// This will clear previous timer that will make your typing status
// 'blink' if not cleared.
timer = setTimeout(() => {
this.setState({
typing: null
});
}, 500);
});
}
I am trying to dispatch 'SET_MIDDLEWARE_TYPE' action with payload.type = 'observable', wait 5 seconds and then make an API call. Currently, the SET_MIDDLEWARE_TYPE action is not being dispatched. If I remove the delay and the mergeMap, it dispatch that action.
Expected:
FETCH_USER_WITH_OBSERVABLE_REQUEST --> SET_MIDDLEWARE_TYPE (wait 5 seconds) --> FETCH_USER_WITH_OBSERVABLE_SUCCESS
Actual:
FETCH_USER_WITH_OBSERVABLE_REQUEST --> (wait 5 seconds) --> FETCH_USER_WITH_OBSERVABLE_SUCCESS
How can I obtained the expected behavior?
Code:
import { Observable } from 'rxjs';
import { githubApi } from './api';
const githubEpic = action$ =>
// Take every request to fetch a user
action$.ofType('FETCH_USER_WITH_OBSERVABLES_REQUEST')
.mapTo(({ type: 'SET_MIDDLEWARE_TYPE', payload: { type: 'observable'} }))
// Delay execution by 5 seconds
.delay(5000)
// Make API call
.mergeMap(action => {
// Create an observable for the async call
return Observable.from(githubApi.getUser('sriverag'))
// Map the response to the SUCCESS action
.map((result) => {
return {
type: 'FETCH_USER_WITH_OBSERVABLES_SUCCESS',
payload: { result } ,
};
});
});
export default githubEpic;
You are abandoning the SET_MIDDLEWARE_TYPE action when you perform the mergeMap. Notice the parameter that you pass into the mergeMap called action? That cannot make it any further downstream unless you propagate it.
You will need to prepend it to the outgoing stream. I suggest you do the following instead:
import { Observable } from 'rxjs';
import { githubApi } from './api';
const githubEpic = action$ =>
// Take every request to fetch a user
action$.ofType('FETCH_USER_WITH_OBSERVABLES_REQUEST')
.mapTo(({ type: 'SET_MIDDLEWARE_TYPE', payload: { type: 'observable'} }))
// Delay execution by 5 seconds
.delay(5000)
// Make API call
.mergeMap(action =>
// Async call
Observable.from(githubApi.getUser('sriverag'))
// Map the response to the SUCCESS action
.map(result => ({
type: 'FETCH_USER_WITH_OBSERVABLES_SUCCESS',
payload: { result }
}))
// Prepend the original action to your async stream so that it gets
// forwarded out of the epic
.startWith(action)
);
export default githubEpic;