I actually have issue with following code :
var promise = element(by.id("closeNotification")).isPresent(); // point A
console.log(promise);
promise.then((message) => {
element(by.id("closeNotification")).click();
browser.sleep(3000);
}, (errorMessage) => { // Point B
browser.refresh();
});
Here at point A, if element is present, program runs smoothly. But if element is not present, an error is thrown. At point B i have tried to handle the promise if it is rejected. Please help me with the condition if element is not present.
Thanks in advance.
move the click into if block, only when element is present, then execute click
var promise = element(by.id("closeNotification")).isPresent(); // point A
promise.then((present) => {
if(present) { // only click when present == true
element(by.id("closeNotification")).click();
}
browser.sleep(3000);
}, (errorMessage) => { // Point B
browser.refresh();
});
Related
Brief logic is the next: after clicking on 'li' element, request is sent, and based on response value of 'contacts', it should select if it's greater than 0, once such element is find, i need to break each loop. But currently, despite I set value, which should break each loop on next iteration (returns false). count[] has been restored with old values, what's an issue?
cy.get('div[id=company_select]').then($el => {
const count = []
cy.wrap($el).click().find('li').each((element, index) => {
cy.intercept('GET', '**/company/view-json**').as('getCompanyProps')
cy.log(count.length)
if (count.length > 0) {
return false
} else {
cy.wrap(element).click().wait('#getCompanyProps').then((interception) => {
if (interception.response.body.contacts.length === 0) {
cy.wrap($el).find('button[vs__clear]').click()
} else {
count.push(1)
cy.log(count.length)
}
})
}
})
})
You can't early-exit with return false in this scenario, but you can use count to prevent inner execution after body.contacts.length > 0.
cy.intercept('GET', '**/company/view-json**').as('getCompanyProps') // this can be here
cy.get('div[id=company_select]').then($el => {
const count = []
cy.wrap(count).as('count') // make an alias value of the count variable
cy.wrap($el).click().find('li')
.each((element, index) => {
cy.get('#count').then(count => { // retrieve count asynchronously
if (count.length === 0) { // no result yet?
cy.wrap(element).click().wait('#getCompanyProps')
.then((interception) => {
if (interception.response.body.contacts.length === 0) {
cy.wrap($el).find('button[vs__clear]').click()
} else {
count.push(1)
console.log(count.length)
}
})
}
})
})
})
The reason for this behaviour is the mixture of asynchronous commands like .wait('#getCompanyProps') and synchronous code for checking the early exit.
If you use console.log() instead of cy.log() to debug the values, you'll see the logs before the early exit all run before the logs after count.push(1).
If I understand your question correctly, you wish to know why on the second pass of your cypress each loop cy.wrap($el).click().find('li').each((element, index) => { the cy.log(count.length) is equal to 0.
Even though further down inside another then loop cy.wrap(element).click().wait('#getCompanyProps').then((interception) => { you increase count by count.push(1) and right after cy.log(count.length) which returns 1.
The short answer is scope. If you change a variable within a cypress loop to return that variable you need to add something like this after .then( () =>{ cy.wrap(count)} My solution is below but I also changed the position of your const count = [] if you want to know why I suggest reading What is the difference between 'let' and 'const' ECMAScript 2015 (ES6)?
const count = []
cy.intercept('GET', '**/company/view-json**').as('getCompanyProps')
cy.get('div[id="company_select"]')
.then( ($el) => {
cy.wrap($el)
.click()
.find('li')
.each( ($el) => {
if (count.length === 0){
cy.wrap($el)
.click()
.wait('#getCompanyProps')
.then((interception) => {
if (interception.response.body.contacts.length === 0) {
cy.wrap($el)
.find('button[vs__clear]')
.click()
} else {
count.push(1)
cy.log(count.length)
}
})
}
})
.then( () =>{
cy.wrap(count).as('count')
})
})
cy.get('#count')
.then( (count) =>{
cy.log(count.length)
})
Using these RxJS tools: BehaviorSubject, Subscribe and Next
Please refer to the this codesandbox, and look at the console to see a visual: https://codesandbox.io/s/fancy-bird-0m81p
You will notice the object "C" value in the subscription is "1 stream behind
Consider the the following code:
const initialState = { a: 1, b: 2, c: 3 };
const Store$ = new BehaviorSubject(initialState);
const StoreUpdates$ = Store$.pipe(
scan((acc, curr) => {
return Object.assign({}, acc, curr);
}, initialState),
share()
);
export const updateStore = update => {
Store$.next(update);
};
StoreUpdates$.pipe(
distinctUntilChanged((p, n) => {
return p.b === n.b;
})
).subscribe(store => {
Store$.next({ c: Math.random() });
});
StoreUpdates$.subscribe(store => {
console.log("Subscription Check:: Notice issue here", store);
});
When you call the updateStore function, in the console.log you will notice that the C value, which is updated in a next call within a subscription, appears in the first iteration and the older value appears in the last iteration. So somehow it looks the next.call within the subscription happens "Before"
I believe the codesandox will illustrate and make it more clear.
How do I maintain the correct order of events so that the latest update appears last in the subscription?
For the moment a non-ideal way of solving this issue is to wrap the next statement within the subscribe in a timeout. If anyone has a better solution please do post.
StoreUpdates$.pipe(
distinctUntilChanged((p, n) => {
return p.b === n.b;
})
).subscribe(store => {
setTimeout(() => {
Store$.next({ c: Math.random() });
},0)
});
"vue-rx": "^6.1.0",
"rxjs": "^6.4.0",
"vue": "^2.5.17",
I'm new in vue-rx and rxjs,But when I see several demo of rx, I'm quite interested in this.So I want to use it in my project which posts a request when attribute num will not change anymore
[
{
id: 0,
name: 'giftA',
num: 0 // will turn to 1,2,3,4,5,...after running `send({id: 0})` function 1,2,3,4,5,...times
},
{
id: 1,
name: 'giftB',
num: 0
},
...
]
And Here is my solution:
using $watchAsObservable to watch the change of sendCalledTimes, and then using mergeMap to post the request.
the variable sendCalledTimes is a number which will sendCalledTimes++ when called send function, And after posting the request, reset this to sendCalledTimes = 0.
So that $watchAsObservable('sendCalledTimes')(vue-rx) will execute every three seconds, and will reduce request times in my project. But i think it's still not good because it just like a timer and can't watch weather num of each object in the Array changes. The good example should be like this search example.
data() {
return {
sendCalledTimes: 0,
giftArr: []
}
},
created() {
this.$watchAsObservable('sendCalledTimes').pipe(
pluck('newValue'),
filter(val => val > 0),
debounceTime(3000),
// if `sendCalledTimes` is the same number as previous
// will not execute follows
// distinctUntilChanged(),
mergeMap(
(val) => this.requestSendGift()
),
).subscribe(
(val) => { }
)
},
methods: {
send (obj) {
let pushFlag = true
for (const gift in this.giftArr) {
if (gift.id === obj.id) {
gift.num++
pushFlag = false
break
}
}
if (pushFlag) {
this.giftArr.push(obj)
}
// observable
this.sendCalledTimes++
},
async requestSendGift () {
for (const gift in this.giftArr) {
// example for post a request to store each gift
await axios({
data: gift,
type: 'post',
url: '...'
}).then(res => { ... })
}
// reset `this.sendCalledTimes`
this.sendCalledTimes = 0
}
}
Also since vue-rx doesn't have many examples on github, so i need help to solve creating good subscription for this situation.
I have tried this, but failed:
data () {
return {
giftArr: []
}
},
subscriptions: {
test: from(this.giftArr) // console.log(this.$observables.test) throw an error: typeError: Cannot read property 'giftArr' of undefined
},
It would be greatly appreciated if anyone can help me to solve this question.
It's a little unclear from your question exactly what you're trying to do, but I've created an example based on what I believe to be your intent.
I made some assumptions:
You have a 'gifts' array that represents all of the gifts that will ever exist.
You want to make updates to that array.
Every time you make an update to the array, you want to see the update in the form of an Observable emitting an event.
Use a Subject
I think what you want is a Subject.
const gift$ = new Subject();
Make it Emit on Updates
And you would set it up to emit every time you increment num or add a new gift.
function addGift(gift) {
gifts.push(gift);
gift$.next(gift);
}
function incrementGift(gift) {
gift.num++;
gift$.next(gift);
}
All together it could look something like this:
import { Subject } from 'rxjs';
const gift$ = new Subject();
const gifts = [{ id: 0, name: 'giftA', num: 0 }, { id: 1, name: 'giftB', num: 0 }];
function addGift(gift) {
gifts.push(gift);
gift$.next(gift);
}
function incrementGift(gift) {
gift.num++;
gift$.next(gift);
}
function sendGift(newGift) {
const currentGift = gifts.find(g => g.id === newGift.id);
currentGift ? incrementGift(currentGift) : addGift(newGift);
}
gift$.subscribe(update => {
console.log(gifts);
console.log(update);
});
// You should see an initial logging of 'gifts' and update will be 'undefined' at first. Then you'll see a log for every 'sendGift'.
sendGift({ id: 0 });
sendGift({ id: 3, name: 'giftC', num: 0 });
StackBlitz
I tried reactive form valueChanges but valueChanges method doesn't return input field name which has changed.
I thought code like this. but I think this is not smart way. Because I have to compare every each input field. so I need more smart way.
// get init view data from local storage
this.localstorageService.getPlaceDetail().subscribe(data => {
this.initPlaceDetail = data;
// watch changed value
this.editPlaceForm.valueChanges.subscribe(chengedVal => {
if (chengedVal['ja_name'] !== this.initPlaceDetail.languages.ja.name.text) {
this.changedJA = true;
}
if (chengedVal['ja_romaji'] !== this.initPlaceDetail.languages.ja.name.romaji) {
this.changedJA = true;
}
// ...... I have to check all input fields??
});
});
I'm adding form controls from an array and something like this worked for me. Just reference the item you need instead of expecting the valueChanges observable to pass it to you.
myFields.forEach(item => {
const field = new FormControl("");
field.setValue(item.value);
field.valueChanges.subscribe(v => {
console.log(item.name + " is now " + v);
});
});
This is my way to get changed control in form.
I shared for whom concerned.
Method to get list control changed values
private getChangedProperties(form): any[] {
let changedProperties = [];
Object.keys(form.controls).forEach((name) => {
let currentControl = form.controls[name];
if (currentControl.dirty)
changedProperties.push({ name: name, value: currentControl.value });
});
return changedProperties;
}
If you only want to get latest changed control you can use
var changedProps = this.getChangedProperties(this.ngForm.form);
var latestChanged = changedProps.reduce((acc, item) => {
if (this.changedProps.find(c => c.name == item.name && c.value == item.value) == undefined) {
acc.push(item);
}
return acc;
}, []);
Instead of listening to whole form changes you can listen to value changes event for each form control as shown in below code:
this.myForm.get('ja_name').valueChanges.subscribe(val => {
this.formattedMessage = `My name is ${val}.`;
});
Coming from the Promise world, I can implement a queue function that returns a Promise that won't execute until the previous Promise resolves.
var promise = Promise.resolve();
var i = 0;
function promiseQueue() {
return promise = promise.then(() => {
return Promise.resolve(++i);
});
}
promiseQueue().then(result => {
console.log(result); // 1
});
promiseQueue().then(result => {
console.log(result); // 2
});
promiseQueue().then(result => {
console.log(result); // 3
});
// -> 1, 2, 3
I'm trying to recreate this queue-like function using Observables.
var obs = Rx.Observable.of(undefined);
var j = 0;
function obsQueue() {
return obs = obs.flatMap(() => {
return Rx.Observable.of(++j);
});
}
obsQueue().subscribe(result => {
console.log(result); // 1
});
obsQueue().subscribe(result => {
console.log(result); // 3
});
obsQueue().subscribe(result => {
console.log(result); // 6
});
// -> 1, 3, 6
Every time I subscribe, it re-executes the history of the Observable, since at the time of subscription the "current Observable" is actually an Observable which emits multiple values, rather than the Promise that just waits until the last execution has completed.
flatMap isn't the answer for this use case, and nearly all the "chain" and "queue" answers I can find online are about chaining several Observables that are part of one overall Observable, where flatMap is the correct answer.
How can I go about creating the above Promise queue function using Observables?
For context, this queue function is being used in a dialog service, which dictates only one dialog can be shown at a time. If multiple calls are made to show different dialogs, they only appear one at a time in the order that they were called.
If you change:
return obs = obs.flatMap...
With
return obs.flatMap...
You will see the same output as you do with promises (1, 2, 3).
To chain observables such that the next one is not executed until the previous one is complete, use the concat operator
let letters$ = Rx.Observable.from(['a','b','c']);
let numbers$ = Rx.Observable.from([1,2,3]);
let romans$ = Rx.Observable.from(['I','II','III']);
letters$.concat(numbers$).concat(romans$).subscribe(e=>console.log(e));
//or...
Rx.Observable.concat(letters$,numbers$,romans$).subscribe(e=>console.log(e));
// results...
a b c 1 2 3 I II III
Live demo
Figured it out! May not be quite as elegant as the Promise chain, and I'm definitely open to suggestions to clean it up.
var trigger = undefined;
function obsQueue() {
if (!trigger || trigger.isStopped) {
trigger = new Rx.Subject();
return createObservable(trigger);
} else {
var lastTrigger = trigger;
var newTrigger = trigger = new Rx.Subject();
return lastTrigger.last().mergeMap(() => {
return createObservable(newTrigger);
});
}
}
var j = 0;
function createObservable(trigger) {
// In my use case, this creates and shows a dialog and returns an
// observable that emits and completes when an option is selected.
// We want to make sure we only create the next dialog when the previous
// one is closed.
console.log('creating');
return new Rx.Observable.of(++j).finally(() => {
trigger.next();
trigger.complete();
});
}
obsQueue().subscribe(result => {
console.log('first', result);
});
obsQueue().subscribe(result => {
console.log('second', result);
});
obsQueue().subscribe(result => {
console.log('third', result);
});
var timer = setTimeout(() => {
obsQueue().subscribe(result => {
console.log('fourth', result);
});
}, 1000);
// Output:
// creating
// first 1
// creating
// second 2
// creating
// third 3
// creating
// fourth 4
Rather than try to figure out how to chain them in order, I have each observable create its own trigger to let the next observable know when to create itself.
If all the triggers have been completed (setTimeout case, we queue up another one later), then the queue starts again.