I'm writing CY test and was trying to solve it by myself for couple of hours but unsuccessfully. Could you please help me here a bit :)
Each time I run the test I'm getting new URL, e.g.
https://website.com/en/info/is/here/
And I need to save only
/en/info/is/here/ (so without domain name)
I need to compare it later with another href.
Could you please advise me the way how to do it or at least the direction? Thanks a lot!
The cy.location() command gives you named parts, so from the example pathname is the part you need
cy.visit('http://localhost:8000/app/index.html?q=dan#/users/123/edit')
cy.location().should((loc) => {
..
cy.wrap(loc.pathname).as('url1')
...
})
If you have search or hash as well
cy.visit('http://localhost:8000/app/index.html?q=dan#/users/123/edit')
cy.location().should((loc) => {
..
cy.wrap(loc.pathname + loc.search + loc.hash).as('url1')
...
})
You can use .split() on the URL string.
Where you save it depends on the location of it's use.
Inside one test:
let pathname
cy.url().then((url) => url.split('/').slice(3)).as('pathname1')
...
cy.get('#pathname1').then(pathname1 => {
expect(pathname1).to.eq(pathname2)
})
Between tests:
let pathname1
it('gets first pathname', () => {
cy.url().then((url) => pathname1 = url.split('/').slice(3))
})
it('uses first pathname', () => {
expect(pathname1).to.eq(pathname2)
})
Use the URL interface to parse your string (also used by cy.location)
const urlString = 'https://website.com/en/info/is/here/'
const url = new URL(urlString)
const pathname = url.pathname // yields "/en/info/is/here/"
You can use the following :
let firstUrl = null;
let secondUrl = null;
cy.url().then(url => {
firstUrl = url;
});
/* sometimes later */
cy.url().then(url => {
secondUrl = url;
});
/* sometimes later */
expect(firstUrl).to.equal(secondUrl)
If you want to just compare some part of these URL I recommend you using a regex expressions.
You can use the javascript split to do this:
let partUrl
cy.url().then((url) => {
partUrl = url.split('com')[1] //saves /en/info/is/here/
})
You could use .replace() and the Cypress baseUrl value, and then store that value in a Cypress environment variable.
cy.url().then((url) => {
Cypress.env('someUrl', url.replace(Cypress.config('baseUrl'), '');
}).then(() => {
cy.log(Cypress.env('someUrl'));
})
Related
I am new to webdriver io and I want to get all clickable elements using webdriver io and iterate through them. I came across 'browser.findElements' API, but could not get it to work. Can anyone provide me a sample code ?
var assert = require('assert');
var homePage = require("../../pages/home_page");
describe('Keyboard friendly home page', () => {
it('User should be able to navigate using tab',() => {
browser.url(homePage.url);
elements = browser.findElements("div");
clickableElements = [];
elements.forEach(element => {
if (element.isDiplayed() && element.isClickable()) {
clickableElements.push(element);
}
});
clickableElements.array.forEach(element => {
console.log(elemtent.getText() + "is clickable");
});
});
});
There might be two problems with your example:
incorrect use of findElements, see documentation; you can use $$ command where you pass only a selector, no need to pass a location strategy as well
the last forEach loop should look like this:
clickableElements.forEach(element => {
console.log(elemtent.getText() + "is clickable");
});
I'm trying to use i18n-js to translate some strings into other languages. If I have my code in normal code, it works. Ex:
//Displays "Something" (no quotes) where I want it
<Text> translate("Something"); </Text>
But if I put it inside an array or object, then call it later, it stops working and shows a missing message instead of the text I want translated. Ex:
const messages = {
something: translate("Something"),
// other translations...
}
// later on
// Displays "[missing "en.Something" translation]" (no quotes) where I want it
<Text> messages.something </Text>
The following is my code for my translate function, as well as the config for i18n. I'm using lodash-memoize, but that is unrelated to the issue. I've already checked that the text being passed to i18n.t() is the same (including type) no matter if it's in normal code or in the array, but it still doesn't return the correct thing. I have some error checking written up to avoid getting the missing message on screen, but that still doesn't fix the issue that it can't find the translation.
export const translationGetters = ({
en: () => require('./translations/en.json'),
es: () => require('./translations/es.json')
});
export const translate = memoize(
(key, config) => {
text = i18n.t(key, config)
return text
},
(key, config) => (config ? key + JSON.stringify(config) : key)
);
export const setI18nConfig = () => {
// fallback if no available language fits
const fallback = { languageTag: "en", isRTL: false };
const { languageTag, isRTL } =
RNLocalize.findBestAvailableLanguage(Object.keys(translationGetters)) ||
fallback;
// clear translation cache
translate.cache.clear();
// update layout direction
I18nManager.forceRTL(isRTL);
// set i18n-js config
i18n.translations = { [languageTag]: translationGetters[languageTag]() };
i18n.locale = languageTag;
};
I have no idea where to go on this. Any advice would be appreciated!
Same problem here, workaround is to return array/object from inside a function:
Don't work
export const translations = [i18.t('path')]
Works
export function getTranslations() {
const translations = [i18.t('path')]
return translations
}
I am currently working with a API that does not return JSON. To get around this, I take the response and push it to a array ( while formatting it to remove any indentation and split each number in the response ). I then use this array of 183 numbers and run a for loop against an array with 183 characters to generate an object ( with custom key value pairs ) from the response.
Where things get confusing is when I start to use the data in my HTML. Usually you can just say <p>{data.overallRank}</p> but I am getting the error that the object is undefined. This makes sense because the data = {} was not created until the function ran.
After searching for a solution, I cam across svelte await blocks. You can read on them here and look at the tutorial : https://svelte.dev/tutorial/await-blocks
After trying to implement this feature, I have the following code.
let playerStats = []
let proxy = "https://cors-anywhere.herokuapp.com/"
let url = proxy + "https://secure.runescape.com/m=hiscore_oldschool/index_lite.ws?player=Hess"
const data = {};
let promise = getPlayer();
async function getPlayer() {
return await fetch(url).then((response) => response.text())
.then((data) => {
return data;
});
}
getPlayer().then((playerData) => {
// format data
playerStats.push(playerData.replace(/\n/ig, ",").split(','));
console.log(playerStats);
// Begin object generation
// names array shortened
let names = ["overallRank", "overallLvl", "overallXP", "attRank", ]
const data = {};
for (var i = 0; i < playerStats[0].length; i++) {
data[names[i]] = playerStats[0][i];
}
console.log(data);
});
<main>
{#await promise}
<p>Search for a Player...</p>
{:then data}
<p>The data is {data}</p>
{/await}
</main>
I suggest throwing this code in a svelte editor which you can find here: https://svelte.dev/tutorial/await-blocks
The issue with this code is that it is printing out the data from the return data, which returns the unformatted data and not the object.
I want to return the object that is created after the second function getplayer().then()... so I can use that object throughout my HTML.
I hope I explained things well and thank you in advance for any help.
It is returning the formatted data because that what is returned by the promise function. In order to get the formatted data, you have to add the formatting to the chain of promise
async function getPlayer() {
return await fetch(url)
.then((response) => response.text())
.then((playerData) => {
// here your transformation
// do not forget to actually return something
return data;
});
You were actually very close to sorting it out, just a bit of confusion regarding how promises work I believe.
All you need to do is format your data within the block where the data is handled following the fetch & decode operations:
async function getPlayer() {
return await fetch(url)
.then((response) => response.text())
.then((data) => {
return formatData(data);
});
}
Your formatData() function is essentially there already, you just need minor changes in your code:
function formatData(playerData) {
playerStats.push(playerData.replace(/\n/ig, ",").split(','));
console.log(playerStats);
// Begin object generation
// names array shortened
let names = ["overallRank", "overallLvl", "overallXP", "attRank", ]
const data = {};
for (var i = 0; i < playerStats[0].length; i++) {
data[names[i]] = playerStats[0][i];
}
console.log(data);
return data;
}
Finally, you do not need to explicitly declare a promise to use it in an {#await} block, you know getPlayer() returns a promise, so you can directly use that instead:
<main>
{#await getPlayer()}
<p>Search for a Player...</p>
{:then data}
<p>Overall Rank: {data.overallRank}</p>
{/await}
</main>
See functioning REPL
My production code looks like:
exports.convertWord = number => { /* some logic here */ }
exports.methodUnderTest = () => {
return exports.convertWord(1);
}
Test code:
const mockConvertToWord = sinon.stub();
mockConvertToWord.withArgs(1).returns('one');
fileUnderTest.convertWord = mockConvertToWord;
const result = fileUnderTest.methodUnderTest();
expect(result).toBeEqual('one');
Test above is green. I expect my test will break if I change prod code to this:
exports.convertWord = number => { /* some logic here */ }
exports.methodUnderTest = () => {
return exports.convertWord(1, 'another arg');
}
but it's not. Sinon works fine even when I pass extra params which I didn't point in withArgs method. How can I tell sinon to return value only when method has been called with exact number of params?
stub
One way to do this is to use stub.callsFake(fakeFunction):
mockConvertToWord.callsFake((...args) => args.length === 1 && args[0] === 1 ? 'one' : undefined);
An alternative approach with a stub is to use a sinon.assert to make sure the stub was called with the epected arguments as noted by #deerawan.
mock
Another approach is to use a mock:
const mock = sinon.mock(fileUnderTest);
mock.expects('convertWord').withExactArgs(1).returns("one");
const result = fileUnderTest.methodUnderTest();
expect(result).toBeEqual('one');
mock.verify();
Another alternative, perhaps you can try to check the call of convertToWord like
...
expect(result).toBeEqual('one');
// check the function
sinon.assert.alwaysCalledWithExactly(mockConvertToWord, '1');
Ref:
https://sinonjs.org/releases/v6.3.4/assertions/#sinonassertalwayscalledwithexactlyspy-arg1-arg2-
Hope it helps
I am trying to work with the youtube API.
In order to get the icons for the first nth videos I have to make a request.
I was thinking to make a for loop and inside that loop there would be the request.
The problem with this approach is that I am getting the responses with the wrong order and completely random.
So my question :
is there a way to make a for loop wait for a response? I am also able to work with the RxJS operators but I don't know what I should search for
Thanks in advance
You could leverage the Observable.forJoin method. In this case, the "global" callback will be called when all requests have ended.
Here is a sample:
Observable.forkJoin([
this.http.get('/req1').map(res => res.json()),
this.http.get('/req2').map(res => res.json()),
(...)
]).subscribe(results => {
// Called when all requests have ended
var result1 = results[0];
var result2 = results[1];
(...)
});
In your particular use case, you can leverage in addition the flatMap operator:
this.http.get('/videos').map(res => res.json())
.flatMap(videos => {
return Observable.forkJoin(videos.map((video) => {
return this.http.get(`/video/${video.id}/icon`)
.map(res => res.json());
});
}).subscribe(results => {
// all icons received here
});
So I ended up using something like this.
searchVideo( videoIdArray ) {
let observableBatch = [];
let data;
let i;
let videosTempArray: Array<Video>=[];
for(i=0;i<videoIdArray.length;i++){
let videoTemp: Video= {};
videosTempArray.push(videoTemp);
}
videosTempArray.forEach(( videoTemp, key ) => {
observableBatch.push( this.http.get(BASE_URL_VIDEO + '?part=statistics%2Csnippet' + '&id=' + videoIdArray[key].videoId + '&key=' + API_TOKEN)
.map((res: Response) => {
res.json();
// console.log(key);
data = res.json();
videosTempArray[key].channelId=data.items[0].snippet.channelId;
videosTempArray[key].tags=data.items[0].snippet.tags;
videosTempArray[key].views=data.items[0].statistics.viewCount;
videosTempArray[key].likes=data.items[0].statistics.likeCount;
videosTempArray[key].dislikes=data.items[0].statistics.dislikeCount;
return videosTempArray[key];
}
)
);
});
return Observable.forkJoin(observableBatch);
}
thanks for the help!!!