Cypress- How can we list key names under certain key (not values of these keys) - automated-tests

I want to store key names under a certain key. Here is an example:
{"widget": {
"debug": "on",
"window": {
"title": "Sample Konfabulator Widget",
"name": "main_window",
"width": 500,
"height": 500
},
"image": {
"src": "Images/Sun.png",
"name": "sun1",
"hOffset": 250,
"vOffset": 250,
"alignment": "center"
}
}}
There is no problem to reach end values like "sun1". I want to reach the key names under "image" as "src", "name" ...; and store them as an array. I don't need their values. How can I do that? I'm parsing response in "then" structure; so this type of answer would be great!
Thanks

The image tag is deeply nested in the outer object.
This is how I would approach it
cy.request(...)
.then(data => {
const imageKeys = Cypress._.keys(data.widget.image)
expect(imageKeys).to.deep.eq(['src', 'name', 'hOffset', 'vOffset', 'alignment'])
})
You can also chain commands,
cy.request(...)
.its('widget.image')
.then(Cypress._.keys)
.should(imageKeys => {
expect(imageKeys).to.deep.eq(['src', 'name', 'hOffset', 'vOffset', 'alignment'])
})

JavaScript has Object.keys(), which will return an array containing the keys in that object.
const myObj = {
foo: true,
bar: false,
baz: 'string'
}
cy.then(() => {
const keys = Object.keys(myObj);
console.log(keys) // ['foo', 'bar', 'baz']
});
If you needed both keys and values, you can use Object.entries().

Related

passing reactive data object as argument in function Vue 3 composition API

I am new to Vue and although I could find way around most problems I've encountered, this one has been bugging me for last two days and just cannot find solution. Any help much appreciated, thanks in advance!
I've got following code:
<template>
<header><h1>HomeShop</h1></header>
<main>
<section>
<Item-Card
v-for="(item, index) in items"
:key="index"
:item="item.item"
:check="item.check"
#item-checked="checkClicked(index, item.id, items)"
#item-deleted="deleteClicked(item.id, items)"
/>
<Add-New-Item #item-submited="newItemCard" />
</section>
</main>
<button #click="$log(JSON.stringify(items, null, 1))">Log</button>
</template>
<script setup>
import { reactive, toRefs } from "vue";
import axios from "axios";
import _ from "lodash";
//data + explicit expression
const state = reactive({
items: [],
});
const { items } = toRefs(state);
//fce
const checkClicked = (index, id, items) => {
if (items[index].check === false) {
items[index].check = true;
items[index].checkTime = _.now();
items = _.sortBy(items, ["check", "checkTime"])
console.log(JSON.stringify(items, null, 1));
axios.patch("http://localhost:3000/items/" + id, { check: true });
} else {
items[index].check = false;
delete items[index].checkTime;
axios.patch("http://localhost:3000/items/" + id, { check: false });
}
};
I pass my data object array (items) as argument to checkClicked method. This function changes check status and adds checkTime timestamp based on which I sort the array (using lodash method sortBy). From within the checkClicked method I log the items array with expected correct result:
[
{
"item": "2",
"check": false,
"id": 2
},
{
"item": "3",
"check": false,
"id": 3
},
{
"item": "4",
"check": false,
"id": 4
},
{
"item": "5",
"check": true,
"id": 5,
"checkTime": 1671719753796
},
{
"item": "1",
"check": true,
"id": 1,
"checkTime": 1671719755363
}
]
However, when I log items from outside the method (the custom function $log at template) I get following result:
[
{
"item": "1",
"check": true,
"id": 1,
"checkTime": 1671719755363
},
{
"item": "2",
"check": false,
"id": 2
},
{
"item": "3",
"check": false,
"id": 3
},
{
"item": "4",
"check": false,
"id": 4
},
{
"item": "5",
"check": true,
"id": 5,
"checkTime": 1671719753796
}
]
How do I manipulate the reactive data object array items from inside the function checkClicked? My intention is to sort the data array each time timestamp checkTime is added (that is what the checkClicked function does).
My understanding is that passing the data object array items as argument into function creates separate instance of the array, that is why I am getting two different results while loging the array. However I cannot find solution how to manipulate the real items from inside the function checkClicked.
You are right. You are only changing the parameter variable items in the function. See my other answer for the clarification.
Pay also attention to the following:
When you assign a new value to your reactive proxy object, you can lose the reactivity.
Here it is:
items = _.sortBy(items, ["check", "checkTime"])
I guess, this line could also remove reactivity from the array.
There are ways to fix it, by passing the array through vue reactivity system again.
But, this causes too much work for vue to full recalculate the array items and is not efficient.
My way to provide sorted results is to use a Computed Property.
Like this:
<Item-Card v-for="(item, index) in sortedItems">
and then
const sortedItems = computed(() => {
return _.sortBy(items, ["check", "checkTime"])
})
The advantage of using a computed property is that, it will be recalculated by Vue Reactivity System only when your items array changes.
It looks like you only change the local variable items in the checkClicked() function. I have build a test playground to check it.
You have to change the line items = []; to this.items = []; if you want to change the property, not the parameter variable.
See my second answer for the right sorting solution.
const { ref, reactive, createApp, toRefs } = Vue;
const data = [
{
"item": "1",
"check": true,
"id": 1,
"checkTime": 1671719755363
},
{
"item": "2",
"check": false,
"id": 2
},
{
"item": "3",
"check": false,
"id": 3
}
]
const App = {
methods: {
clear(items) {
items = [];
}
},
setup() {
//data + explicit expression
const state = reactive({
items: data,
});
const { items } = toRefs(state);
return {
items
}
}
}
const app = createApp(App)
app.mount('#app')
<div id="app">
<div v-for="(item, index) in items"
:key="index"
:item="item.item"
:check="item.check"> #{{index}}: {{item.item}}
</div>
<button #click="clear(items)">clear</button>
</div>
<script src="https://unpkg.com/vue#3/dist/vue.global.prod.js">
</script>
sending update with solution I got at another forum. I feel dumbed I would not figured that out in two days on my own, whatever. Although advice from Tolbxela can be right track it brings as well more problems so in the end I prefer the more cleaner way:
"I can reference the state declared within . i.e. don’t pass it as a value to the function."
Thats it. Simply not passing the data object array into function, just reference it right away from within the function. So the correct code goes like this:
<template>
<header><h1>HomeShop</h1></header>
<main>
<section>
<Item-Card
v-for="(item, index) in items"
:key="item.id"
:item="item.item"
:check="item.check"
#item-checked="checkClicked(index, item.id)"
#item-deleted="deleteClicked(item.id, items)"
/>
<Add-New-Item #item-submited="newItemCard" />
</section>
</main>
<button #click="$log(JSON.stringify(sortedItems, null, 1))">Log</button>
</template>
<script setup>
import { reactive, toRefs, computed } from "vue";
import axios from "axios";
import _ from "lodash";
//data + explicit expression
const state = reactive({
items: [],
});
const { items } = toRefs(state);
//fce
const checkClicked = (index, id) => {
if (state.items[index].check === false) {
state.items[index].check = true;
state.items[index].checkTime = _.now();
state.items = _.sortBy(state.items, ["check", "checkTime"])

ConditionalExpression DynamoDb Insert

I have the below data schema for my DynamoDb table. I am trying to append list subscribers on the condition if input = name (ex: input = my-topic2). There can be many maps in the “topics” list and I need to search for the map where the name = input and from there add the subscriber to that topic.
{
“server-id”: “123345678”,
“server-name”: “my-server”
“topics”: [
{
“name”: “my-topic”,
“subscribers”: []
},
{
“name”: “my-topic2”,
“subscribers”: [] //This is what I need to append on a condition that the input = “my-topic2”
}
]
}
I have the current following paeans I am using which appends “my-topic” subscribers.
params = {
ExpressionAttributeNames: {
"#T": "topics",
"#S": "subscribers"
},
ExpressionAttributeValues: {
":vals": [
message.author.id
]
},
Key: {
'server-id': serverID
},
ReturnValues: "ALL_NEW",
TableName: tableName,
UpdateExpression: "SET #T[0].#S = list_append(#T[0].#S, :vals)"
};

Pacts: Matching rule for non-empty map (or a field which is not null) needed

I need help with writing my consumer Pacts using pact-jvm (https://github.com/DiUS/pact-jvm).
My problem is I have a field which is a list (an array) of maps. Each map can have elements of different types (strings or sub-maps), eg.
"validatedAnswers": [
{
"type": "typeA",
"answers": {
"favourite_colour": "Blue",
"correspondence_address": {
"line_1": "Main St",
"postcode": "1A 2BC",
"town": "London"
}
}
},
{
"type": "typeB",
"answers": {
"first_name": "Firstname",
"last_name": "Lastname",
}
}
]
but we're only interested in some of those answers.
NOTE: The above is only an example showing the structure of validatedAnswers. Each answers map has dozens of elements.
What we really need is this: https://github.com/pact-foundation/pact-specification/issues/38, but it's planned for v.4. In the meantime we're trying a different approach. What I'm attempting to do now is to specify that each element of the list is a non-empty map. Another approach is to specify that each element of the list is not null. Can any of this be done using Groovy DSL?
This:
new PactBuilder().serviceConsumer('A').hasPactWith('B')
.port(findAvailablePort()).uponReceiving(...)
.willRespondWith(status: 200, headers: ['Content-Type': 'application/json'])
.withBody {
validatedAnswers minLike(1) {
type string()
answers {
}
}
}
doesn't work because it mean answers is expected to be empty ("Expected an empty Map but received Map( [...] )", see also https://github.com/DiUS/pact-jvm/issues/298).
So what I would like to do is something like this:
.withBody {
validatedAnswers minLike(1) {
type string()
answers Matchers.map()
}
}
or:
validatedAnswers minLike(1) {
type string()
answers {
keyLike 'title', notNull()
}
}
or:
validatedAnswers minLike(1) {
type string()
answers notNull()
}
Can it be done?
I would create two separate tests for this, one test for each of the different response shapes and have a provider state for each e.g. given there are type b answers.
This way when you verify on provider side, it will only send those two field types.
The union of the two examples gives a contract that allows both.
You can do it without DSL, sample Groovy script:
class ValidateAnswers {
static main(args) {
/* Array with some samples */
List<Map> answersList = [
[
type: 'typeA',
answers: [
favourite_colour: 'Blue',
correspondence_address: [
line_1: 'Main St',
postcode: '1A 2BC',
town: 'London'
]
]
],
[
type: 'typeB',
answers: [
first_name: 'Firstname',
last_name: "Lastname"
]
],
[
type: 'typeC',
answers: null
],
[
type: 'typeD'
],
[
type: 'typeE',
answers: [:]
]
]
/* Iterating through all elements in list above */
for (answer in answersList) {
/* Print result of checking */
println "$answer.type is ${validAnswer(answer) ? 'valid' : 'not valid'}"
}
}
/**
* Method to recursive iterate through Map's.
* return true only if value is not an empty Map and it key is 'answer'.
*/
static Boolean validAnswer(Map map, Boolean result = false) {
map.each { key, value ->
if (key == 'answers') {
result = value instanceof Map && value.size() > 0
} else if (value instanceof Map) {
validAnswer(value as Map, false)
}
}
return result
}
}
Output is:
typeA is valid
typeB is valid
typeC is not valid
typeD is not valid
typeE is not valid

Observable contains array of ID to call another observable

I have a data structure in firebase
{
"name": "Sample",
"category": ["123456", "789012"]
}
The array of category contains ID which refers to documents in another collection. I can get the above document as Observable. What I really what as the end result is the below data structure
{
"name": "Sample"
"category": [
{
"name": "Category 1"
},
{
"name": "Category 2"
}
]
}
How can I bring this data? I don't think switchMap works for this. If so, can someone give an example of that?
You can try using flatMap and forkJoin. FlatMap allows you to chain multiple async requests together and forkJoin allows you to wait for all observables to return a value before continuing.
And you could wright something like this:
var finalData;
firstRequest('sample').flatMap((data) => {
// assuming data = { name: "Sample", catagory: [ "23123", "31321", ... ] }
finalData = data;
var observables = [];
data.catagory.forEach((c) => {
observable.push(secondRequest(c));
});
return forkJoin(observables);
}).flatMap((results) => {
// assuming results is an array like [ { name: "Catagory 1", ... } ]
finalData.category = results;
return finalData;
});

DyanamoDB SCAN with nested attribute

Can I scan DynamoDB by 'order.shortCode', in the given example. The console is indicating I can't with dot notation, and I can't find any documentation on it.
{
"key2": "cj11b1ygp0000jcgubpe5mso3",
"order": {
"amount": 74.22,
"dateCreated": "2017-04-02T19:15:33-04:00",
"orderNumber": "cj11b1ygp0000jcgubpe5mso3",
"shortCode": "SJLLDE"
},
"skey2": "SJLLDE"
}
To scan by a nested attribute, you should use ExpressionAttributeNames parameter to pass each path component (i.e. order and shortCode) separately into FilterExpression like shown below:
var params = {
TableName: 'YOUR_TABLE_NAME',
FilterExpression: "#order.#shortCode = :shortCodeValue",
ExpressionAttributeNames: {
'#order': 'order',
"#shortCode": "shortCode"
},
ExpressionAttributeValues: {
':shortCodeValue': 'SJLLDE'
}
};
dynamodbDoc.scan(params, function(err, data) {
});
Here is a link to documentation explaining this:
http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Expressions.ExpressionAttributeNames.html#Expressions.ExpressionAttributeNames.NestedAttributes

Resources