Failed to feed selector to a ClientFunction - javascript

Please take a look at the structure below.
Is there any way to get 'Example 1' working? The idea is to avoid storing a 'css selector string' in a 'test' class.
MyAccount.js
import { Selector} from "testcafe";
export class MyAccount {
constructor() {
this.box = {
item_1: Selector("#item01");
item_2: Selector("#item02");
}
}
}
clientFunctions.js
import { ClientFunction } from 'testcafe';
export const scrollInto = ClientFunction((selector) => {
var element = window.document.querySelector(selector);
element.scrollIntoView();
});
EXAMPLE 1. (FAILED)
import { MyAccount } from "../MyAccount";
import { scrollInto } from "../clientFunctions";
const myAccount = new MyAccount();
fixture("Feature A").page(process.env.url);
test("Test 01", async t => {
await scrollInto(myAccount.box.item_1);
});
EXAMPLE 2. (PASSED)
import { MyAccount } from "../MyAccount";
import { scrollInto } from "../clientFunctions";
const myAccount = new MyAccount();
fixture("Feature A").page(process.env.url);
test("Test 01", async t => {
await scrollInto("#item01");
});

The problem is that the browser's querySelector method doesn't work with the TestCafe Selector API. Please change the MyAccount class in the following way to make your example work:
export class MyAccount {
constructor() {
this.box = {
item_1: "#item01",
item_2: "#item02"
}
}
}

You can pass a Selector into a ClientFunction through the dependencies option and override it later by calling with method.

Related

getting error _LoginPage.default is not a constructor

this is my code
/// \<reference types = "cypress" /\>
class LoginPage
{
visit()
{
cy.visit("https://ec2-35-179-99-242.eu-west-2.compute.amazonaws.com:2021/")
}
username(name)
{
const field = cy.get('[id=UserName]')
field.clear()
field.type(name)
return this
}
Password(pwd)
{
const pass = cy.get('[id=Password]')
pass.clear()
pass.type(pwd)
return this
}
Submit()
{
const button = cy.get('[type=submit]')
button.click()
}
}
export default LoginPage
/// \<reference types = "cypress" /\>
import LoginPage from './PageObject/LoginPage'
it('valid test', function()
{
const Login = new LoginPage()
Login.visit()
Login.username('arslan')
Login.Password('123')
Login.Submit()
})
i make object of Login class
const Login = new LoginPage()
but getting error
getting error _LoginPage.default is not a constructor
Try using a named export
export class LoginPage {
visit() {
cy.visit("https://ec2-35-179-99-242.eu-west-2.compute.amazonaws.com:2021/")
}
...
}
and import like this
import { LoginPage } from './PageObject/LoginPage'
You need to use function reserved name before all your functions names or declare the functions like a const using an arrow function like:
function visit()
{
cy.visit("https://ec2-35-179-99-242.eu-west-2.compute.amazonaws.com:2021/")
}
or
const visit = () =>
{
cy.visit("https://ec2-35-179-99-242.eu-west-2.compute.amazonaws.com:2021/")
}

Error is being thrown claiming that a method created in an axios class is not a function

In my react application, I'm defining an Axios class with a bunch of methods, but the methods are not being recognized as functions and throwing an error. Showing is easier than explaining so... I have 3 files involved...
http-common.js has this:
import axios from 'axios';
export default axios.create({
baseURL: "http://localhost:5000/api/v1/tours",
headers: {
"Content-type": "application/json"
}
});
tours.js has this:
import http from "../http-common";
class ToursDataService {
getAll(page = 0) {
return http.get(`?page=${page}`);
}
}
export default ToursDataService
tours-list.js has this... which calls the function "getAll" in retrieveTours.
import React, { useState, useEffect } from "react";
import ToursDataService from "../services/tours";
const ToursList = props => {
const [tours, setTours] = useState([]);
useEffect(() => {
retrieveTours();
}, []);
const retrieveTours = () => {
ToursDataService.getAll()
.then(response => {
setTours(response.data.tours)
})
.catch( e => {
console.log(e);
});
}
The console claims that getAll is not a function. Why? Can anyone explain?
scheduler.development.js:173 Uncaught TypeError: _services_tours__WEBPACK_IMPORTED_MODULE_1__.default.getAll is not a function
at retrieveTours (tour-list.js:12:1)
getAll() is not a static method so you'd need to create an instance of ToursDataService...
const svc = new ToursDataService(); // create an instance
// ...
svc.getAll() // call the method on the instance
.then(...)
or make the method static
class ToursDataService {
static getAll(page = 0) {
return http.get("", { params: { page } });
}
}
Alternately, don't use classes at all since you don't appear to be encapsulating anything. You might as well just export the getAll function on its own
// tours.js
export const getAll = (page = 0) => http.get("", { params: { page } });
and
import { getAll } from "../services/tours";

How can I modify the value of state in pinia in vue3 component test and affect the component?

Using vue-test-utils to test the component using pinia, I need to modify the value of the state stored in pinia, but I have tried many methods to no avail. The original component and store files are as follows.
// HelloWorld.vue
<template>
<h1>{{ title }}</h1>
</template>
<script>
import { useTestStore } from "#/stores/test";
import { mapState } from "pinia";
export default {
name: "HelloWorld",
computed: {
...mapState(useTestStore, ["title"]),
},
};
</script>
// #/stores/test.js
import { defineStore } from "pinia";
export const useTestStore = defineStore("test", {
state: () => {
return { title: "hhhhh" };
},
});
The following methods have been tried.
Import the store used within the component to the test code and make changes directly, but the changes cannot affect the component.
// test.spec.js
import { mount } from "#vue/test-utils";
import { createTestingPinia } from "#pinia/testing";
import HelloWorld from "#/components/HelloWorld.vue";
import { useTestStore } from "#/stores/test";
test("pinia in component test", () => {
const wrapper = mount(HelloWorld, {
global: {
plugins: [createTestingPinia()],
},
});
const store = useTestStore();
store.title = "xxxxx";
console.log(wrapper.text()) //"hhhhh";
});
Using the initialState in an attempt to overwrite the contents of the original store, but again without any effect.
// test.spec.js
import { mount } from "#vue/test-utils";
import { createTestingPinia } from "#pinia/testing";
import HelloWorld from "#/components/HelloWorld.vue";
test("pinia in component test", () => {
const wrapper = mount(HelloWorld, {
global: {
plugins: [createTestingPinia({ initialState: { title: "xxxxx" } })],
},
});
console.log(wrapper.text()) //"hhhhh";
});
Modify the TestingPinia object passed to global.plugins in the test code, but again has no effect.
// test.spec.js
import { mount } from "#vue/test-utils";
import { createTestingPinia } from "#pinia/testing";
import HelloWorld from "#/components/HelloWorld.vue";
test("pinia in component test", () => {
const pinia = createTestingPinia();
pinia.state.value.title = "xxxxx";
const wrapper = mount(HelloWorld, {
global: {
plugins: [pinia],
},
});
console.log(wrapper.text()) //"hhhhh";
});
Use global.mocks to mock the states used in the component, but this only works for the states passed in with setup() in the component, while the ones passed in with mapState() have no effect.
// test.spec.js
import { mount } from "#vue/test-utils";
import { createTestingPinia } from "#pinia/testing";
import HelloWorld from "#/components/HelloWorld.vue";
test("pinia in component test", () => {
const wrapper = mount(HelloWorld, {
global: {
plugins: [createTestingPinia()],
mocks: { title: "xxxxx" },
},
});
console.log(wrapper.text()) //"hhhhh"
});
This has been resolved using jest.mock().
import { mount } from "#vue/test-utils";
import { createPinia } from "pinia";
import HelloWorld from "#/components/HelloWorld.vue";
jest.mock("#/stores/test", () => {
const { defineStore } = require("pinia");
const useTestStore = defineStore("test", { state: () => ({ title: "xxxxx" }) });
return { useTestStore };
});
test("pinia in component test", () => {
const wrapper = mount(HelloWorld, {
global: { plugins: [createPinia()] },
});
expect(wrapper.text()).toBe("xxxxx");
});
Thanks to Red Panda for this topic. I use "testing-library", and "vue-testing-library" instead of "vue-test-utils" and "jest", but the problem is the same - couldn't change pinia initial data of the store.
I finally found a solution for this issue without mocking the function.
When you $patch data, you just need to await for it. Somehow it helps. My code looks like this and it totally works:
Popup.test.js
import { render, screen } from '#testing-library/vue'
import { createTestingPinia } from '#pinia/testing'
import { popup } from '#/store1/popup/index'
import Popup from '../../components/Popup/index.vue'
describe('Popup component', () => {
test('displays popup with group component', async () => {
render(Popup, {
global: { plugins: [createTestingPinia()] }
})
const store = popup()
await store.$patch({ popupData: 'new name' })
screen.debug()
})
})
OR you can set initialState using this scheme:
import { render, screen } from '#testing-library/vue'
import { createTestingPinia } from '#pinia/testing'
import { popup } from '#/store1/popup/index'
import Popup from '../../components/Popup/index.vue'
test('displays popup with no inner component', async () => {
const { getByTestId } = render(Popup, {
global: {
plugins: [
createTestingPinia({
initialState: {
popup: {
popupData: 'new name'
}
}
})
]
}
})
const store = popup()
screen.debug()
})
Where popup in initialState - is the imported pinia store from #/store1/popup. You can specify any of them there the same way.
Popup.vue
<script>
import { defineAsyncComponent, markRaw } from 'vue'
import { mapState, mapActions } from 'pinia'
import { popup } from '#/store1/popup/index'
export default {
data () {
return {}
},
computed: {
...mapState(popup, ['popupData'])
},
....
I'm working on a project using Vue 3 with composition API styling.
Composition API is used for both components and defining my store.
Here is my store
player.js
import { defineStore } from 'pinia'
import { ref, reactive } from 'vue'
export const usePlayerStore = defineStore('player',()=>{
const isMainBtnGameClicked = ref(false)
return { isMainBtnGameClicked }
})
MyComponent.vue
//import { usePlayerStore } from '...'
const playerStore = usePlayerStore()
playerStore.isMainBtnGameClicked = true
isMainBtnGameClicked from my store is updated properly.
You can also update variables from components by passing them by reference to the pinia store. It's working in my project.
For sake of saving future me many hours of trouble, there is a non-obvious thing in play here - the event loop. Vue reactivity relies on the event loop running to trigger the cascade of state changes.
When you mount/shallowMount/render a component with vue-test-utils, there is no event loop running automatically. You have to trigger it manually for the reactivity to fire, e.g.
await component.vm.$nextTick;
If you don't want to mess around with ticks, you have to mock the store state/getters/etc. (which the docs strongly lean toward, without explaining the necessity). Here OP mocked the whole store.
See also: Vue-test-utils: using $nextTick multiple times in a single test

Use exported value in setup of Vue Composition API

In a plain js file the code looks like
export default async function exportData() {
const { data } = await store
.dispatch('fetchData')
const { bookings } = data
const booking = bookings.length ? bookings[0]._id : ''
const event = {
bookingID: booking
}
// other methods and variables
return {
.....
}
}
inside the vue file
import exportData from './exportData'
export default {
setup() {
const {
fetchEvents,
isEventActive,
} = exportData()
fetchEvents()
}
}
the problem is in vue components the values from exportData gets undefined, gets error fetchEvents is not a function when the export is asynchronous. works well if not asynchronous. what is the workaround here??
You can try to declare fetchEvents,isEventActive methods in plan js file without wrapping it inside any function
const fetchEvents = () => {
//body
};
const isEventActive = () => {
//body
};
and export them as
export {fetchEvents, isEventActive};
now use them
import {fetchEvents,isEventActive} from 'path-to-js-file'
export default {
setup() {
fetchEvents()
isEventActive()
}
}

proxyquire TypeError when not replacing every function in a module

I'm trying to use proxyquire to unit test my Redux reducers. I need to replace the functionality of one function in my test but keep the original functionality of the other, which is possible according to proxyquire's docs.
formsReducer.test.js:
import { expect } from 'chai';
import * as types from '../constants/actionTypes';
import testData from '../data/TestData';
import proxyquire from 'proxyquire';
describe('Forms Reducer', () => {
describe('types.UPDATE_PRODUCT', () => {
it('should get new form blueprints when the product changes', () => {
//arrange
const initialState = {
blueprints: [ testData.ipsBlueprint ],
instances: [ testData.basicFormInstance ]
};
//use proxyquire to stub call to formsHelper.getFormsByProductId
const formsReducerProxy = proxyquire.noCallThru().load('./formsReducer', {
'../utils/FormsHelper': {
getFormsByProductId: () => { return initialState.blueprints; }
}
}).default;
const action = {
type: types.UPDATE_PRODUCT,
stateOfResidence: testData.alabamaObject,
product: testData.basicProduct
};
//act
const newState = formsReducerProxy(initialState, action);
//assert
expect(newState.blueprints).to.be.an('array');
expect(newState.blueprints).to.equal(initialState.blueprints);
});
});
});
formsReducer.js:
import * as types from '../constants/actionTypes';
import objectAssign from 'object-assign';
import initialState from './initialState';
import formsHelper from '../utils/FormsHelper';
export default function formsReducer(state = initialState.forms, action) {
switch (action.type) {
case types.UPDATE_PRODUCT: {
let formBlueprints = formsHelper.getFormsByProductId(action.product.id);
formBlueprints = formsHelper.addOrRemoveMnDisclosure(formBlueprints, action.stateOfResidence.id);
return objectAssign({}, state, {blueprints: formBlueprints, instances: []});
}
}
I need to replace the functionality of formsHelper.getFormsByProductId() but keep the original functionality of formsHelper.addOrRemoveMnDisclosure() - as you can see in the proxyquire block I'm only replacing the getFormsByProductId() function. However, when I do this get the following error: TypeError: _FormsHelper2.default.addOrRemoveMnDisclosure is not a function. Looks to be a problem either with babel or with my export default for FormHelper.
The export for the FormsHelper looks like this:
export default class FormsHelper { ...methods and whatnot }.
How can I fix this problem?

Categories

Resources