Cypress - Loop looking for data and refresh if not found - cypress

I need to loop looking for an item in a table and if it's not found, click a refresh button to reload the table. I know I can't use a simple while loop due to the asynchronous nature of cypress. Is there another way to accomplish something like this.
I tried to tweak an example from another post but no luck. Here's my failed attempt.
let arry = []
for (let i = 0; i < 60; i++) { arry.push(i) }
cy.wrap(arry).each(() => {
cy.get('table[class*="MyTableClass"]').then(function($lookForTheItemInTheTable) {
if($lookForTheItemInTheTable.find("MySearchValue")) {
return true
}
else {
cy.get('a[class*="selRefreshTable"]').click()
cy.wait(2000)
}
})
})

Cypress is bundled with lodash. Instead of using a for loop, you can the _.times(). However, I wouldn't recommend this for your situation as you do not know how many times you would like to reiterate.
You'll want to use the cypress-recurse plugin and use it like this example in that repo:
import { recurse } from 'cypress-recurse'
it('gets 7 after 50 iterations or 30 seconds', () => {
recurse(
() => cy.task('randomNumber'), // actions you want to iterate
(n) => n === 7, // until this condition is satisfied
{ // options to pass along
log: true,
limit: 50, // max number of iterations
timeout: 30000, // time limit in ms
delay: 300 // delay before next iteration, ms
},
)
})
Even with the above mentioned, there may be a simplified approach to solving your problem with setting up your app to have the table always display what you are seeking on the first try.

Related

Cypress Conditionnal loop

I'd like to make a conditional loop where I can execute a certain action (In this case, clicking a button) until I meet a certain condition (In this case, finding a year in a date-picker).
I have found a work around where I loop 250 time until I find the year I am looking for, but I don't like this approach. Is there a better way to do it ?
export default (placeholder, day, month, year) => {
describe("Select a date", () => {
it("Select a year", () => {
cy.get('input[data-placeholder="'+ placeholder +'"].mat-datepicker-input').parent().next().click();
cy.get('button[aria-label="Choose month and year"]').click();
cy.get('div.mat-calendar-content').then((content) => {
cy.wrap(content).as('content');
if (!(content.text().includes(year))) {
var array = Array.from({length:250},(v,k)=>k+1);
for (let i = 0; i < array.length; i++) {
cy.get('#content', {log: false}).then((tmp) => {
if (!(tmp.text().includes(year))) {
previous();
}
});
}
}
});
cy.contains(year).click();
});
});
}
You are doing a lot of complicated stuff on a control that is already tested by Angular Material library.
Why don't you just type in your date
cy.get(`input[data-placeholder="${placeholder}"]`)
.type('01-01-2022')
.blur()

redux-toolkit -- Type error: "unit" is read-only

I am using react-redux and redux-toolkit for this project. I want to add item to the cart. If there is already a same item in cart, just increment the unit. The error occurs at the slice, so I will just show the slice here.
const CartListSlice = createSlice({
name: 'cartItem',
initialState,
reducers: {
addToCart: (state, action) => {
let alreadyExist = false;
// get a copy of it to avoid mutating the state
let copyState = current(state.cartItem).slice();
// loop throught the cart to check if that item is already exist in the cart
copyState.map(item => {
if (item.cartItem._id === action.payload._id) {
alreadyExist = true;
item.unit += 1 // <--- Error occurs here
}
})
// If the item is not exist in the cart, put it in the cart and along with its unit
if (alreadyExist === false) {
state.cartItem.push({
cartItem: action.payload,
unit: 1
});
}
},
}
});
I get a type error telling me that unit is read-only.
How can I update the "unit" variable so that it increments whenever it is supposed to.
In React Toolkit's createSlice, you can modify the state directly and even should do so. So don't create a copy, just modify it.
In fact, this error might in some way stem from making that copy with current.
See the "Writing Reducers with Immer" documentation page on this
Meanwhile, a suggestion:
const CartListSlice = createSlice({
name: 'cartItem',
initialState,
reducers: {
addToCart: (state, action) => {
const existingItem = state.find(item => item.cartItem._id === action.payload._id)
if (existingItem) {
item.unit += 1
} else {
state.push({
cartItem: action.payload,
unit: 1
});
}
},
}
});
You don't need line:
let copyState = current(state.cartItem).slice();
Instead of copyState, just use state.cartItem.map
As #phry said, you should mutate state directly, because redux-toolkit is using immerJS in the background which takes care of mutations.

How to implement subscriptions in graphql.js?

This is a pretty simple question.
How to implement subscriptions in graphql?
I'm asking specifically for when using graphql.js constructors like below ?
I could not find a clean/simple implementation.
There is another question here, but it deals with relay.js - i don't want to unnecessarily increase the nr of external dependencies in my app.
What i have:
module.exports = function (database){
return new GraphQLSchema(
{ query: RootQuery(database)
, mutation: RootMutation(database)
, subscription: RootSubscription(database) -- i can see this in graphiql - see below
}
);
}
function RootSubscription(database){
return new GraphQLObjectType(
{ name: "RootSubscriptionType"
, fields:
{ getCounterEvery2Seconds:
{ type: new GraphQLNonNull(GraphQLInt)
, args :
{ id: { type: GraphQLString }
}
, subscribe(parent, args, context){
// this subscribe function is never called .. why?
const iterator = simpleIterator()
return iterator
}
}
}
}
)
}
I learned that i need a subscribe() which must return an iterator from this github issue.
And here is a simple async iterator. All this iterator does - is to increase and return the counter every 2 seconds. When it reaches 10 it stops.
function simpleIterator(){
return {
[ Symbol.asyncIterator ]: () => {
let i = 0
return {
next: async function(){
i++
await delay(2000)
if(i > 10){
return { done: true }
}
return {
value: i,
done: false
}
}
}
}
}
}
When i run the graphiql subscription, it returns null for some reason:
I'm piecing together code from multiple sources - wasting time and hacking it basically. Can you help me figure this one out?
Subscriptions are such a big feature, where are they properly documented? Where is that snippet of code which you just copy paste - like queries are for example - look here.
Also, i can't use an example where the schema is separate - as a string/from a file. I already created my schema as javascript constructors. Now since im trying to add subscriptions i can't just move back to using a schema as a string. Requires rewriting the entire project. Or can i actually have both? Thanks :)

Frame differs in running all test from running only one test, why?

If I run all tests for one epic at once only the first test passes. The other tests fail because the frame differs. But every test singly run passes.
I could not find any related problem to this nether found something in the RxJS not the redux observable docs.
I thought there could be some kind of a reset function on the TestScheduler but there isn't.
One of my test (they all look pretty simular):
test('should fail if e-mail is missing', () => {
testScheduler.run(({ hot, expectObservable }) => {
const action$ = new ActionsObservable(
hot('-a', {
a: login('', 'secret')
})
);
const output$ = epic(action$, null, null);
expectObservable(output$).toBe('-a', {
a: failure(
formErrors.credentialsEmpty(['email', 'password'])
)
});
});
});
I expect the frame of output marble to be 1 but it is 2.
The output of a failing test:
Array [
Object {
- "frame": 1,
+ "frame": 2,
"notification": Notification {
"error": undefined,
"hasValue": true,
"kind": "N",
"value": Object {
edit
I could get around that behaviour by creating one TestScheduler instance per test but I am not sure if I am supposed to do it this way.
Stumbled across this today. I think creating one new TestScheduler per test is probably a good idea. It doesn't seem to have a noticeable impact between tests - and that way you're sure that the state is reset between tests.
One other workaround is to do testScheduler.frame = 0 in a beforeEach - but I opted to just create it from scratch each time.

Get All Pages with an Apollo Query

Suppose I have 500 rows in my database. I want to get 100 pages of 50 rows. Here is an example of a fetchMore request.
const fetchNextPage = (props) => {
props.Query.fetchMore({
query: gql(getRows),
variables: {
skip: props.Query.rows.length,
},
updateQuery: (previousResult, next) => {
return {
...previousResult,
rows: [...previousResult.rows, ...next.fetchMoreResult.rows],
};
},
});
}
What I'm unsure about is...
How I can fetch all pages without additional user action?
Since I know the total number of needed pages how can I send them in parallel?
You can actually do that with one operation. Using aliases you can request the same field multiple times with different arguments.
Here is the official explanation about aliases.
In your case it would be something similar to:
query GetAllPages {
page1rows: rows(skip: 0, limit: 50) { # "skip" and "limit" are just regular variable names
#...rowFields
}
page2rows: rows(skip: 50, limit: 50) {
#...rowFields
}
#... etc.
}
In this example page1rows and page2rows are aliases for the rows field. You can choose other aliases.
Note that skip and limit are nothing special, they are plain variables and their use depends on the schema and resolvers on the server. I see in your code you use skip, if you know you'll always get 50 rows then limit is redundant.
The response should be something like:
{
"data": {
"page1rows": [
//... rows
],
"page2rows": [
//... rows
],
//... etc.
}
}
That way works without additional user action, you get all pages at once and there's no need to use fetchMore.

Resources