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);
});
}
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/**')
My Protractor test fails with below error
Error: Timeout - Async callback was not invoked within timeout specified by jasmine.DEFAULT_TIMEOUT_INTERVAL.
This is my spec file.
let common=require('./Objects/common.js')
describe('Cloud testing', function() {
it('Cloudtest1', function() {
let EC=protractor.ExpectedConditions;
browser.waitForAngularEnabled(false);
browser.get(common.loginURL);
common.txtUserName.sendKeys('aqaasdas#hkm.com');
common.txtPword.sendKeys('asdasd##$');
common.btnLogin.click();
browser.wait(EC.visibilityOf(element(by.xpath("//a[#class='btn btn-success']"))));
element(by.xpath("//a[#class='btn btn-success']")).click();
common.btnCrtPcr.click();
});
});
Any help is appreciated, I tried answers to similar questions posted here but nothing works for me. I'm running the latest Protractor and Chrome versions.
Have you tried by asyn test? Sometimes even backend response or browser performance may affect the test cases.
Refer: https://jasmine.github.io/2.0/introduction.html#section-Asynchronous_Support
describe("Your module", function() {
var originalTimeout;
beforeEach(function() {
originalTimeout = jasmine.DEFAULT_TIMEOUT_INTERVAL;
jasmine.DEFAULT_TIMEOUT_INTERVAL = 10000;
});
it("takes a long time", function(done) {
let EC=protractor.ExpectedConditions;
browser.waitForAngularEnabled(false);
browser.get(common.loginURL);
common.txtUserName.sendKeys('aqaasdas#hkm.com');
common.txtPword.sendKeys('asdasd##$');
common.btnLogin.click();
browser.wait(EC.visibilityOf(element(by.xpath("//a[#class='btn btn-success']"))));
element(by.xpath("//a[#class='btn btn-success']")).click();
common.btnCrtPcr.click();
});
afterEach(function() {
jasmine.DEFAULT_TIMEOUT_INTERVAL = originalTimeout;
});
});
Your callback was later than your Jasmins default timout time. You might want to use async/await in your it block even though you have used expected condition.
it('Cloudtest1', async () => {
let EC=protractor.ExpectedConditions;
browser.waitForAngularEnabled(false);
browser.get(common.loginURL);
await common.txtUserName.sendKeys('aqaasdas#hkm.com');
await common.txtPword.sendKeys('asdasd##$');
await common.btnLogin.click();
browser.wait(EC.visibilityOf(element(by.xpath("//a[#class='btn btn-success']"))));
element(by.xpath("//a[#class='btn btn-success']")).click();
await common.btnCrtPcr.click();
});
Suggestion: Perform action in page object or before each/all. Use it block for expect statement.
example:
describe('When user logged Into account ', () => {
beforeAll(async () => {
await browser.waitForAngularEnabled(false);
await loginPO.login();
});
it('Browser title should be displayed.', async () => {
expect(await browser.getTitle()).toBe('test');
});
});
The way i am polling tasks for async POST call, is it correct??? Because program control doesn't enter 'while' loop in spec file. Please help!
Previous query: How to return a value from Cypress custom command
beforeEach(function () {
cy.server()
cy.route('POST', '/rest/hosts').as("hosts")
})
it('Create Host', function () {
let ts =''
let regex = /Ok|Error|Warning/mg
// Some cypress commands to create a host. POST call is made when I create a host. I want to poll
// task for this Asynchronous POST call.
cy.wait("#hosts").then(function (xhr) {
expect(xhr.status).to.eq(202)
token = xhr.request.headers["X-Auth-Token"]
NEWURL = Cypress.config().baseUrl + xhr.response.body.task
})
while((ts.match(regex)) === null) {
cy.pollTask(NEWURL, token).then(taskStatus => {
ts= taskStatus
})
}
})
-------------------------
//In Commands.js file, I have written a function to return taskStatus, which I am using it in spec
file above
Commands.js -
Cypress.Commands.add("pollTask", (NEWURL, token) => {
cy.request({
method: 'GET',
url: NEWURL ,
failOnStatusCode: false,
headers: {
'x-auth-token': token
}
}).as('fetchTaskDetails')
cy.get('#fetchTaskDetails').then(function (response) {
const taskStatus = response.body.task.status
cy.log('task status: ' + taskStatus)
cy.wrap(taskStatus)
})
})
You can't use while/for loops with cypress because of the async nature of cypress. Cypress doesn't wait for everything to complete in the loop before starting the loop again. You can however do recursive functions instead and that waits for everything to complete before it hits the method/function again.
Here is a simple example to explain this. You could check to see if a button is visible, if it is visible you click it, then check again to see if it is still visible, and if it is visible you click it again, but if it isn't visible it won't click it. This will repeat, the button will continue to be clicked until the button is no longer visible. Basically the method/function is called over and over until the conditional is no longer met, which accomplishes the same thing as a loop, but actually works with cypress.
clickVisibleButton = () => {
cy.get( 'body' ).then( $mainContainer => {
const isVisible = $mainContainer.find( '#idOfElement' ).is( ':visible' );
if ( isVisible ) {
cy.get( '#idOfElement' ).click();
this.clickVisibleButton();
}
} );
}
Then obviously call the this.clickVisibleButton() in your test. I'm using typescript and this method is setup in a class, but you could do this as a regular function as well.
With recursion, you can simulate loops.
Add this to your custom commands file (/cypress/support/commands.js):
Cypress.Commands.add('recursionLoop', {times: 'optional'}, function (fn, times) {
if (typeof times === 'undefined') {
times = 0;
}
cy.then(() => {
const result = fn(++times);
if (result !== false) {
cy.recursionLoop(fn, times);
}
});
});
On your tests, just define a function that does what you want for one iteration, and return false if you don't want to iterate again.
cy.recursionLoop(times => {
cy.wait(1000);
console.log(`Iteration: ${times}`);
console.log('Here goes your code.');
return times < 5;
});
while loop is not working for me, so as a workaround I did a for loop, a sort of while loop with a timeout of retries
let found = false
const timeout = 10000
for(let i = 0; i<timeout && !found;i++){
if(..){
// exiting from the loop
found = true
}
}
it is not helpful for everyone, I know.
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 am writing tests for back-end which uses MongoDB and Elasticsearch. The problem is that without wrapping test with setTimeout test fails, and it looks like elasticsearch can't index mock data into db before test. Here is the code:
let elasticSearch = require('elasticsearch');
let elasticClient = new elasticSearch.Client({
host: 'localhost:9200'
});
let app = require('./dist/app'); //path to my application
let supertest = require('supertest');
before((done) => {
elasticClient.index(elasticMockData, function() {
done();
});
});
beforeEach(() => {
request = supertest(app);
});
it('should get data from elastic', () => {
setTimeout(() => { // if I comment this timeout, test will fail
request.get('/api/elastic')
.end((err, res) => {
expect(res.body.hits.hits.length).not.to.equal(0);
})
}, 1000); // if I comment this timeout, test will fail
});
I think you will agree that timeout is not an elegant and nice solution, which slows every test to 1 second or more. Maybe, am I missing something?
Found a solution, maybe it will be useful for someone.
According to Elasticsearch docs:
By default, the document will be available for get() actions immediately, but will only be available for searching after an index refresh (which can happen automatically or manually).
So, in this case, done() should be called within another callback function:
before((done) => {
elasticClient.index(elasticMockData, function() {
elasticClient.indices.refresh(function (err: any, res: any) {
if (err) {
return;
}
done();
});
});
});