Testcafe doesn't wait text to be rendered - internationalization

+ expected - actual
-sign.up
+Sign up now
Im having this issue while asserting a text. It works if I add a wait delay before. Even though Im using visibilityCheck:true on this selector, it won't wait.
Is there something I can do to avoid manually adding waits?
test("Loads the app in english by default", async (t) => {
await t.wait(20000);
const signupText = await signUpPage.signupLink.with({
visibilityCheck: true,
})();
await t.expect(signupText.textContent).eql("Sign up now ");
});

You can use assertion timeout:
await t.expect(Selector('h1').innerText).eql('text', 'check element text', { timeout: timeoutValue });

Related

Expecting a Promise *not* to complete, in Jest

I have the following need to test whether something does not happen.
While testing something like that may be worth a discussion (how long wait is long enough?), I hope there would exist a better way in Jest to integrate with test timeouts. So far, I haven't found one, but let's begin with the test.
test ('User information is not distributed to a project where the user is not a member', async () => {
// Write in 'userInfo' -> should NOT turn up in project 1.
//
await collection("userInfo").doc("xyz").set({ displayName: "blah", photoURL: "https://no-such.png" });
// (firebase-jest-testing 0.0.3-beta.3)
await expect( eventually("projects/1/userInfo/xyz", o => !!o, 800 /*ms*/) ).resolves.toBeUndefined();
// ideally:
//await expect(prom).not.toComplete; // ..but with cancelling such a promise
}, 9999 /*ms*/ );
The eventually returns a Promise and I'd like to check that:
within the test's normal timeout...
such a Promise does not complete (resolve or reject)
Jest provides .resolves and .rejects but nothing that would combine the two.
Can I create the anticipated .not.toComplete using some Jest extension mechanism?
Can I create a "run just before the test would time out" (with ability to make the test pass or fail) trigger?
I think the 2. suggestion might turn handy, and can create a feature request for such, but let's see what comments this gets..
Edit: There's a further complexity in that JS Promises cannot be cancelled from outside (but they can time out, from within).
I eventually solved this with a custom matcher:
/*
* test-fns/matchers/timesOut.js
*
* Usage:
* <<
* expect(prom).timesOut(500);
* <<
*/
import { expect } from '#jest/globals'
expect.extend({
async timesOut(prom, ms) { // (Promise of any, number) => { message: () => string, pass: boolean }
// Wait for either 'prom' to complete, or a timeout.
//
const [resolved,error] = await Promise.race([ prom, timeoutMs(ms) ])
.then(x => [x])
.catch(err => [undefined,err] );
const pass = (resolved === TIMED_OUT);
return pass ? {
message: () => `expected not to time out in ${ms}ms`,
pass: true
} : {
message: () => `expected to time out in ${ms}ms, but ${ error ? `rejected with ${error}`:`resolved with ${resolved}` }`,
pass: false
}
}
})
const timeoutMs = (ms) => new Promise((resolve) => { setTimeout(resolve, ms); })
.then( _ => TIMED_OUT);
const TIMED_OUT = Symbol()
source
The good side is, this can be added to any Jest project.
The down side is, one needs to separately mention the delay (and guarantee Jest's time out does not happen before).
Makes the question's code become:
await expect( eventually("projects/1/userInfo/xyz") ).timesOut(300)
Note for Firebase users:
Jest does not exit to OS level if Firestore JS SDK client listeners are still active. You can prevent it by unsubscribing to them in afterAll - but this means keeping track of which listeners are alive and which not. The firebase-jest-testing library does this for you, under the hood. Also, this will eventually ;) get fixed by Firebase.

Async/Await to simulate busy server that delay update to client browser

MERN noob here. Trying to learn Async/Await by simulating a busy server where client browser only get the update > 3 seconds later (i will manually refresh localhost:3000, after 3 seconds. I only need help on Node.js/ server side for this question)
Could you help rectify codes below? Kindly avoid proposing other ways/methods but show me how to achieve using below example. Thanks in advance.
const app = require('express')()
async function getData() {
//Purpose: to simulate a busy server that returns data back to browser, after 3 seconds delay
await setTimeout(() => {
return 'After waiting 3 seconds, return this sentense as the required data to the browser.'
}, 3000);
}
app.get('/', async (req, res) => {
try {
const data = await getData()
await res.status(200).send(`${data}`)
} catch (err) {
await res.status(400).send(`Unable to get data. Error message, "${err}"`)
}
})
app.listen(3000)
The problem here is that setTimeout doesn't return a promise so you can't use await with it. It just executes the given function after 3 seconds. You can get what you want by wrapping it in a Promise like this:
const app = require('express')()
function getData() {
//Purpose: to simulate a busy server that returns data back to browser, after 3 seconds delay
return new Promise((resolve) => {
setTimeout(() => {
resolve('After waiting 3 seconds, return this sentense as the required data to the browser.');
}, 3000);
});
}
app.get('/', async (req, res) => {
try {
const data = await getData()
await res.status(200).send(`${data}`)
} catch (err) {
await res.status(400).send(`Unable to get data. Error message, "${err}"`)
}
})
app.listen(3008)
Note that you need a return statement to return the promise inside of getData. You didn't have a return statement originally which means the function returns undefined (or if marked as async it gives a Promise that resolves to undefined).
Here we don't need to use async/await because you're not needing to use await until in the app.get. Using async/await in getData could be added but it would be redundant.
Realize that aync/await uses Promises - it's just an easy way to work with Promises. So you can't await on anything but a Promise. Note that async really just means "this function returns a Promise and we'll wrap any result in a Promise if it isn't already a Promise". So you cannot use async/await without having a Promise at some point.
But if you really want to use async/await for some reason maybe this example would help you:
async function getData() {
//Purpose: to simulate a busy server that returns data back to browser, after 3 seconds delay
const result = await new Promise((resolve) => {
setTimeout(() => {
resolve('After waiting 3 seconds, return this sentense as the required data to the browser.');
}, 3000);
});
console.log('We are done waiting 3 seconds');
return result; // <-- this returns a Promise that resolves to the result string
}

Protractor test + manage order of execution

I have a Protractor test which looks like
pageObject1.method1();
pageObject1.method2();
pageObject2.method1();
expect(pageObject2.method2());
let allDataList = pageObject2.method3();
expect(allDataList.includes('test1')).toBeTruthy();
How do I make sure that the call to pageObject2.method3() happens before the next expect call? method3() returns an array of the text of all span elements.
This case you need to use promises
Way-1:
await pageObject1.method2();
await pageObject2.method1();
expect(pageObject2.method2());
let allDataList = await pageObject2.method3();
expect(allDataList.includes('test1')).toBeTruthy();
Description On await
Way-2:
pageObject1.method2().then(function() {
pageObject2.method1().then(function() {
expect(pageObject2.method2());
pageObject2.method3().then(function(allDataList) {
expect(allDataList.includes('test1')).toBeTruthy();
});
});
});

Integration test redux-observable that makes multiple ajax requests with debounce

I have a redux-observable epic that polls an endpoint, getting progress updates until the progress is 100%. The polling interval is acheived using debounceTime like so:
function myEpic(action$, store, dependencies) {
return action$.ofType('PROCESSING')
.do(action => console.log(`RECEIVED ACTION: ${JSON.stringify(action)}`))
.debounceTime(1000, dependencies.scheduler)
.mergeMap(action => (
dependencies.ajax({ url: action.checkUrl })
.map((resp) => {
if (parseInt(resp.progress, 10) === 100) {
return { type: 'SUCCESS' };
}
return { checkUrl: resp.check_url, progress: resp.progress, type: 'PROCESSING' };
})));
}
This works fine but I'd like to write an integration test that tests the state of the store when progress is at 25%, then at 50%, then at 100%.
In my integration tests I can set dependencies.scheduler to be new VirtualTimeScheduler().
This is how I'm trying to do it at the moment (using jest):
describe('my integration test', () => {
const scheduler = new VirtualTimeScheduler();
beforeEach(() => {
// Fake ajax responses
const ajax = (request) => {
console.log(`FAKING REQUEST FOR URL: ${request.url}`);
if (request.url === '/check_url_1') {
return Observable.of({ progress: 25, check_url: '/check_url_2' });
} else if (request.url === '/check_url_2') {
return Observable.of({ progress: 50, check_url: '/check_url_3' });
} else if (request.url === '/check_url_3') {
return Observable.of({ progress: 100 });
}
return null;
};
store = configureStore(defaultState, { ajax, scheduler });
});
it('should update the store properly after each call', () => {
store.dispatch({ checkUrl: '/check_url_1', progress: 0, type: 'PROCESSING' });
scheduler.flush();
console.log('CHECK CORRECT STATE FOR PROGRESS 25');
scheduler.flush();
console.log('CHECK CORRECT STATE FOR PROGRESS 50');
scheduler.flush();
console.log('CHECK CORRECT STATE FOR PROGRESS 100');
});
});
My expected output would be:
RECEIVED ACTION: {"checkUrl":"/check_url_1","progress":0,"type":"PROCESSING"}
FAKING REQUEST FOR URL: /check_url_1
CHECK CORRECT STATE FOR PROGRESS 25
RECEIVED ACTION: {"checkUrl":"/check_url_2","progress":25,"type":"PROCESSING"}
FAKING REQUEST FOR URL: /check_url_2
CHECK CORRECT STATE FOR PROGRESS 50
RECEIVED ACTION: {"checkUrl":"/check_url_3","progress":50,"type":"PROCESSING"}
# CHECK CORRECT STATE FOR PROGRESS 100
But instead the output I get is
RECEIVED ACTION: {"checkUrl":"/check_url_1","progress":0,"type":"PROCESSING","errors":null}
FAKING REQUEST FOR URL: /check_url_1
RECEIVED ACTION: {"checkUrl":"/check_url_2","progress":25,"type":"PROCESSING","errors":null}
CHECK CORRECT STATE FOR PROGRESS 25%
CHECK CORRECT STATE FOR PROGRESS 50%
CHECK CORRECT STATE FOR PROGRESS 100%
At which time the test finishes. I'm configuring the store so that I can mock ajax requests and the scheduler used for debounceTime like as recommended here
So my question is how can I test the state of my store after each of the three ajax requests?
Interestingly enough, I played around with your code and am fairly confident you just found a bug in the debounceTime operator, which causes the apparent swallowing the scheduled debounce. The bad news is that even if that bug is fixed, you're code still wouldn't do what you're looking for order wise.
Bear with me as shit is about to get real:
Epic receives action PROCESSING and schedules debounce, yielding execution to your test
Your test calls scheduler.flush() and the VirtualScheduler executes the scheduled debounce work, which will pass along the original PROCESSING action to the mergeMap
Fake ajax is made, which synchronously emits a response
Response is mapped to the second PROCESSING action
Your epic emits that second action synchronously
The second action is recursively received by your epic and given to the debounce
The debounceTime operator now schedules that second action on the VirtualScheduler but the debounceTime operator is in the middle of executing the previously scheduled work still from the first action.
The call stack unwinds a bunch up until it reaches inside the previously scheduled debounce work from the first action that had just next()'d the first action. The rxjs code for debounceTime then sets this.lastValue = null and this.hasValue = false This is the rxjs bug, it needs to be done before nexting into the destination
The stack unwinds some more to the running flush() method of the VirtualScheduler, which now dequeues the second scheduled debounced action because it was added the scheduled work array synchronously, before this the flushing finished. Remember, we've only called scheduler.flush() ONCE so far, which is the function we're in back in at this point.
The second scheduled debounce work is run, but this.hasValue === false because the first scheduled one set it, so the debounceTime operator does not emit anything.
Stack unwinds to our first scheduler.flush()
We console.log('CHECK CORRECT STATE FOR PROGRESS 25')
All the other scheduler.flush() calls do nothing as there's nothing scheduled.
This is technically a bug, but it's not surprising that no one has run into it since running debounce synchronously without any delay defeats the point of it, except when you're testing, of course. This ticket is basically the same thing and OJ says RxJS doesn't make reentrancy guarantees, but I that might be up for debate in this case. I've filed a PR with the fix to discuss
Remember, this bug wouldn't have solved your underlying question about the ordering, but would have prevented the actions from being swallowed.
Off the top of my head I'm not sure how you would do what you'd like to do specifically if you want to maintain 100% synchronous behavior (VirtualScheduler). You'd need some way of yielding to your test in between debounces. For me when and if I write integration tests I mock out very little, if anything. e.g. let the debounces actually debounce either naturally or by mocking out setTimeout to advance them quicker but still keeping them async which will yield back to your test allowing you to check the state, but making your test also async.
For anyone wanting to reproduce, here's the StackBlitz code I used
The answer was to re-write the test asynchronously. Also note-worthy is that I had to mock the ajax requests by returning an Observable.fromPromise rather than just a regular Observable.of, otherwise they would still get swallowed up by the debounce. Something along these lines (using jest):
describe('my integration test', () => {
const scheduler = new VirtualTimeScheduler();
beforeEach(() => {
// Fake ajax responses
const ajax = request => (
Observable.fromPromise(new Promise((resolve) => {
if (request.url === '/check_url_1') {
resolve({ response: { progress: 25, check_url: '/check_url_2' } });
} else if (request.url === '/check_url_2') {
resolve({ response: { progress: 50, check_url: '/check_url_3' } });
} else {
resolve({ response: { progress: 100 } });
}
}))
);
store = configureStore(defaultState, { ajax, timerInterval: 1 });
});
it('should update the store properly after each call', (done) => {
let i = 0;
store.subscribe(() => {
switch (i) {
case 0:
console.log('CHECK CORRECT STATE FOR PROGRESS 0');
break;
case 1:
console.log('CHECK CORRECT STATE FOR PROGRESS 25');
break;
case 2:
console.log('CHECK CORRECT STATE FOR PROGRESS 50');
break;
case 3:
console.log('CHECK CORRECT STATE FOR PROGRESS 100');
done();
break;
default:
}
i += 1;
});
store.dispatch({ checkUrl: '/check_url_1', progress: 0, type: 'PROCESSING' });
});
});
I also set the timer interval to 1 by passing it as a dependency. In my epic I set it like this: .debounceTime(dependencies.timerInterval || 1000)

Socket.io sends two messages

I'm trying to setup socket.io and here is part of my server.js
const app = require('express')();
const http = require('http').Server(app);
const io = require('socket.io')(http, { path: '/websocket', origins:'*:*' });
io.on('connection', (socket) => {
socket.send('Hi');
socket.on('message', (message) => {
console.log(message);
socket.emit('hello', `New: ${message}`);
});
console.log('a user connected');
});
http.listen(3030, function(){
console.log('listening on *:3030');
});
and my simple client:
var socket = io('https://*******.com', {
secure: true,
path: '/websocket'
});
const input = document.getElementById('text');
const button = document.getElementById('button');
const msg = document.getElementById('msg');
button.onclick = () => {
socket.emit('message', input.value);
socket.on('hello', (text) => {
const el = document.createElement('p');
el.innerHTML = text;
msg.appendChild(el);
})
}
And if I'll click for third time I receive a 3 messages back and so on. What I'm doing wrong? I wish to send message to the server and receive modified message back.
I'm new in web sockets.
Any help appreciated.
P.S. socket.io v2.0.1
You are adding a socket.on() event handler each time the button is clicked. So, after the button has been clicked twice, you have duplicate socket.on() event handlers. When the event comes back, your two event handlers will each get called and you will think you are getting duplicate messages. Actually, it's just one message, but with duplicate event handlers.
You pretty much never want to add an event handler inside another event handler because that leads to this sort of build-up of duplicate event handlers. You don't describe (in words) exactly what you're code is trying to do so I don't know exactly what alternative to suggest. Usually, you set up the event handlers first, just once, when the socket is connected and then you will never get duplicate handlers.
So, perhaps it's as simple as changing this:
button.onclick = () => {
socket.emit('message', input.value);
socket.on('hello', (text) => {
const el = document.createElement('p');
el.innerHTML = text;
msg.appendChild(el);
})
}
to this:
button.onclick = () => {
socket.emit('message', input.value);
}
socket.on('hello', (text) => {
const el = document.createElement('p');
el.innerHTML = text;
msg.appendChild(el);
});
If you are using Angular and (probably) embedding the Socket in a Service (simpleton) you are creating a persistent listener in ngOnInit every time you load a page.
You need to create some kind of flag to know if the listener was already created in the Service from another instance of your page.

Resources