Test multiple cases based on propsdata in vuejs - javascript

I have a component that has a a button that only shows when an article.link prop is not empty. I want to write a test to check the button is rendered when the article.link is not empty and another one for when it is empty
my component looks like so :
a.btn.plutus_btn-primary.round( v-if="hasArticleLink" target='_blank' :href="articleLink") Start Shopping Now
hasArticleLink is a computed property that return true when the link is not empty.
The unit test I wrote looks like this :
it("should not renders the link button when article doesn't have a link", () => {
wrapper = mount(MerchandisingArticle, {
propsData: {
article: {
link: ""
}
}
});
expect(wrapper.find("a").exists()).toBe(false);
});
it("renders the linked button when article has link", () => {
wrapper = mount(MerchandisingArticle, {
propsData: {
article: {
link: "https://google.com"
}
}
});
expect(wrapper.find("a").exists()).toBe(true);
});
that works just fine but I was wondering if there is a better way to test these reversed cases as I think this one is kinda duplicated since I have to mount the component in every it bloc ? Any help is appreciated!

I Think it's good to mount the component before every test, because this way there will be no sideeffects to a new test from a previous one.
But you could do something like this to not duplicate code:
const expectedValues = [
{ link: '', expects: false },
{ link: 'www.google.com', expects: true },
....
]
expectedValues.forEach(({ link, expects }) => {
it(`should evaluate ${expects} for link:${link} `, () => {
const wrapper = mount(MerchandisingArticle, {
propsData: {
article: {
link
}
}
})
expect(wrapper.find("a").exists()).to.equal(expects);
})
})
This way you can also easily add new test cases.

Related

How to add nested functions javascript in custom commands for Nightwatch testing- forEach -loop through elements

Hi I am new to javascript and Nightwatch, I am a manual tester who started doing automation about 6 months ago.
I am writing test cases for checking the details of a product, with collapsible menus. Pressing + button will open and display a list of elements, when closing with the same button, it closes the list, and shows a counter with the number of items on the list.
I have a function that is correctly doing this procedure, but I have it written on the test. I would like to use it in the Page where I have all elements and functions related to that page. And I would like to call that function from the test. I have been able to do this, but not with cases with nested functions, because I do not know how to write it.
These are my pages:
loginPage.js;
productPage.js;
productFuntionalityListPage.js;
This is my test:
module.exports = {
'Buy a Product with Bank Account': function (browser) {
const login = browser.page.loginPage();
const productList = browser.page.productPage();
const productFunctionalityList = browser.page.productFuntionalityListPage();
login
.navigate()
.checkLoginPage();
productList
.getAProduct()
//------------------------------------------Features--------------------------------------
//function to click on each button for functionalities and wait for list to appear
function displayFunctionsList(elems) {
elems.value.forEach(function (element) {
browser.elementIdClick(element.ELEMENT)
//wait for list to appear
.waitForElementVisible('.list_of_items')
.pause(2000)
})
}
// click on each function and wait for list to appear
browser.elements('css selector', '.expand_collapse_btn', displayFunctionsList, 5000)
browser.useCss()
// close each function
function closeFunctionsList(elems) {
elems.value.forEach(function (element) {
browser.elementIdClick(element.ELEMENT)
//after click close wait for count to appear
.waitForElementVisible("input[data-id='counter']")
.pause(2000)
})
}
browser.elements('css selector', '.expand_collapse_btn', closeFunctionsList, 2000)
browser.end()
}
}
This is working correctly.
Below it's what I have tried and does not work:
Page:
productFuntionalityListPage.js
module.exports = {
elements: {
counterOfItemsInList: {
locatorStrategy: 'css selector'
selector: "input[data-id='counter']",
},
expandCollapseBtn: {
locateStrategy: 'css selector',
selector: '.expand_collapse_btn',
},
listOfItems: {
locateStrategy: 'css selector',
selector: '.list_of_items',
}
},
commands: [{
displayFunctionsList: function () {
function displayFunctionsList(elems) {
elems.value.forEach(function (element) {
this.elementIdClick(element.ELEMENT)
//wait for list to appear
.waitForElementVisible('#listOfItems')
.pause(2000)
})
}
this.elements('css selector', '#expandCollapseBtn', displayFunctionsList, 5000)
},
closeFunctionsList: function () {
function closeFunctionsList(elems) {
elems.value.forEach(function (element) {
this.elementIdClick(element.ELEMENT)
//wait for list to appear
.waitForElementVisible('#counterOfItemsInList')
.pause(2000)
})
}
this.elements('css selector', '#expandCollapseBtn', closeFunctionsList, 5000)
}
}]
}
Test calling function from page:
module.exports = {
'Buy a Product with Bank Account': function (browser) {
const login = browser.page.loginPage();
const productList = browser.page.productPage();
const productFunctionalityList = browser.page.productFuntionalityListPage();
login
.navigate()
.checkLoginPage();
productList
.getAProduct()
//------------------------------------------Features--------------------------------------
//calling displayFunctionsList from productFuntionalityListPage.js
productFunctionalityList.displayFunctionsList()
//calling closeFunctionsList from productFuntionalityListPage.js
productFunctionalityList.closeFunctionsList()
browser.end()
}
}
Result after running the test above:
Error:
TypeError: this.elements is not a function
- writing an ES6 async test case? - keep in mind that commands return a Promise;
- writing unit tests? - make sure to specify "unit_tests_mode=true" in your config.
Could anyone please help me adding these functions as custom commands in the productFuntionalityListPage.js and call these functions from the test itself? Not sure what's wrong, because of my lack of javascript and nightwatch knowledge.
Try passing browser as a variable when calling the function like this -
##Test page##
//Example call
gmail.selectEmail(browser, 'browser authentication')
And then the method in the pageObject -
##Page Object##
//Example Method
selectEmail(browser, searchValue){
browser.blah(searchValue);
browser.blah
browser.blah
};
Its slightly messy way of getting it to work but this has saved my bacon a few times

Cypress not intercepting request with render function vue

I'm trying to do a pretty simple intercept in Cypress using a Vue's application. My component has a setup method using render function as such:
setup() {
useInfiniteLoading({ runner: ... })
}
Then on my tests I do the following:
describe("List todo resource", () => {
it("Checks it loads more todos when scrolling to the bottom", function () {
cy.intercept('/todo').as('getTodos');
cy.visit("/todos");
cy.wait("#getTodos").then(({response}) => {
console.log(response);
})
})
})
When running the test I see that the intercept is not stubbing the response.
As you can see from the image the request makes a request to my actual server running locally and the response is stubed. The weird part is that in a previous test I have:
it("Checks the todo list gets updated when clicking on to resolve it (from true to false)", function () {
cy.visit("/todos");
const resolved = false;
const shouldHaveClass = resolved
? "mdi-checkbox-marked-outline"
: "mdi-checkbox-blank-outline";
cy.intercept("GET", "todo", {
fixture: "resources/todo/list.todo.json",
}).as("getTodos");
cy.intercept("PUT", "todo", {
body: { data: { ...this.updateTodoFixture.data, resolved } },
}).as("updateTodo");
cy.get(".todo-list-item__resolve")
.first()
.each((btn) => {
btn.click();
});
cy.get(".todo-list-item__resolve")
.first()
.should("satisfy", ($el) => {
const classList = Array.from($el[0].classList);
return classList.includes(shouldHaveClass);
});
});
And the response is stubbed using intercept as you can see from the previous screenshot. Is it possible that the previous test is affecting the next test? I have tried taking a look into "Intercept too soon" but no luck on trying to apply the fix described in the page.
Any idea on what could be causing the stub not to happen?

nuxt.js get default head in vue.js component

I am trying to get the head object that is configured by nuxt.config.js in a vue layout. In order to show the same title in an app bar as the page title.
I know that you can alter the page title with the head function in a vue component. But is it also possible to retrieve this information somehow?
<script>
export default {
data () {
return {
title: head.titleTemplate // possible?
}
},
head () {
// here it is possible to change it but how about getting it?
}
}
</script>
Another approach could be to get some data out of an page in the nuxt.config.js. But I think this is not how the hierarchy is structured.
Thanks for you help I am just starting to use javascript to code a website :)
(If I understand you correctly) You can use the changed callback to keep track of the latest meta info used (and thus the title).
Example:
head() {
return {
changed: (info) => {
this.title = info.title;
console.log(info, info.title);
},
};
},
data() {
return {
title: '',
};
},
In nuxt.config.js before export I have setted variable with a string of the title.
Then added it to the head section and create a new env section:
https://nuxtjs.org/api/configuration-env/
const title = `Site title`
export default {
head: {
title
},
env: {
title
}
}
This how I'm getting the title in any Vue component:
export default {
computed: {
title () {
return process.env.title
}
},
}
This helps you to keep your original title in process.env.title, even if you will want to change head.title dynamically.
Did anyone found a better solution maybe? :)

Apollo MockedProvider testing issues (my rendered component keeps disappearing)

Disclaimer; I'm a bit new to react-testing-library (been using an admittedly older version of Enzyme) and the Apollo Query/MockedProvider components (been using the client via a JS service object), so this may be a stupid question...
I have a component that receives a list of countries for which I am trying to write tests. What I would like to do is something like:
import React from 'react';
import { MockedProvider } from '#apollo/react-testing';
import { render, act } from '#testing-library/react';
import wait from 'waait';
import Countries, { countryQuery } from './Countries';
import { isTerminating } from 'apollo-link/lib/linkUtils';
const mockCountryName = 'sample country';
const mocks = [
{
request: {
query: countryQuery,
vairables: {},
},
result: {
data: {
countries: [{ name: mockCountryName }],
},
},
},
];
describe('when working with the countries component', () => {
describe('and the component is loading', () => {
let component;
beforeAll(async (done) => {
await act(async () => {
component = render(
<MockedProvider mocks={[]}>
<Countries />
</MockedProvider>
);
});
done();
});
it('should have a title', () => {
expect(component.getByText('Countries Component')).not.toBeUndefined();
});
it('should have a loading status', () => {
expect(component.getByText('Loading...')).not.toBeUndefined();
});
});
});
When this runs, the second test (about loading status) fails as it looks like the component is only a body tag at that point. I tried changing beforeAll to beforeEach, but that just produced a component that had an Error indicator. I put some console.log statements in my component, and this is what they are showing me:
console.log src/components/Countries.js:45
Loading is: true
console.log src/components/Countries.js:46
Error is: undefined
console.log src/components/Countries.js:45
Loading is: false
console.log src/components/Countries.js:46
Error is: Error: Network error: No more mocked responses for the query: {
countries {
name
phone
__typename
}
}
, variables: {}
I'm wondering if it does not like the empty array passed in as the mocks property for the MockedProvider. But every example I've seen does it that way, so...
As an experiment, I added a second set of test to the spec file to see if it was just a weird timing issue with the component that was causing the issue. Here's the second test:
describe('and the component has data', () => {
let component;
beforeAll(async (done) => {
await act(async () => {
component = render(
<MockedProvider mocks={mocks} addTypename={false}>
<Countries />
</MockedProvider>
);
await wait(0);
});
done();
});
it('should have a title', () => {
expect(component.getByText('Countries Component')).not.toBeUndefined();
});
it('should have a loading status', () => {
expect(component.getByText(mockCountryName)).not.toBeUndefined();
});
});
This has the same problem; the first test works (if I reorder the test, the one that first always works) but the second one fails, and the component seems to be an empty body tag.
Is there a way to make this type of test structure work? I don't like the idea of having to put everything into a single test, let alone the setup code for the component.
Thanks!
I'm not sure if it's the best approach, but I think I found a workaround.
First, the empty array/loading issue was not an issue; I traced everything back to testing-library resetting/re-rendering the component between tests.
Here's what I did:
describe('and the component is loading', () => {
let component, pageTitle, loadingMessage;
beforeAll(async (done) => {
await act(async () => {
component = render(
<MockedProvider mocks={[]}>
<Countries />
</MockedProvider>
);
pageTitle = component.getByText(mockPageTitle);
loadingMessage = component.getByText(mockLoadingMessage);
});
done();
});
it('should have a title', () => {
expect(pageTitle).not.toBeUndefined();
});
it('should have a loading status', () => {
expect(loadingMessage).not.toBeUndefined();
});
});
Instead of trying to call component.getTextBy in each test, I moved them into the beforeAll, and assigned the output to variables. Each test uses the variables for their tests. I also wrote a test for my Routes component, and I was still able to call fireEvent.click() on the components.
I would be very interested in any feedback from anyone who has a more experience with testing-library on this. It seems better than what I had, but I want to make sure it's really the best approach.
Thanks.

VueJS - manipulate DOM after computed

I'm getting posts and comments from an API, using Vuex. Right now, I have:
mounted () {
this.$store.dispatch('getComments', this.post);
},
computed: {
comments () {
return this.$store.getters.orderedComments;
},
The comments body is a string that contains HTML tags. I need to strip out the href attribute from a tags of a given class.
cleanUnwantedLinks () {
const link = document.getElementsByClassName('link-class')[0];
link.removeAttribute('href');
}
I'm not sure how to call cleanUnwantedLinks. It should be called just after the component is mounted (when I have the comments already). Is there a way to do it?
If you will return a promise from your getComments action, you could do:
this.$store
.dispatch('getComments', this.post)
.then(() => {
cleanUnwantedLinks()
/*
Can also try wrapping your call in $nextTick
this.$nextTick(() => { cleanUnwantedLinks() })
*/
});

Categories

Resources