I got "Unresolved variable" using Svelte with WebStorm - webstorm

WebStorm works really bad with Svelte. I'm new in Svelte though may be I didn't miss smth?
I have a store:
import { writable } from 'svelte/store'
import type { IQuestion } from 'src/utils/db'
interface IQuestionStore {
activeQuestion: null | IQuestion
isQuestionShowing: boolean
isAnswered: boolean
}
const createStore = () => {
const { subscribe, update } = writable<IQuestionStore>({
activeQuestion: null,
isQuestionShowing: false,
isAnswered: false
})
return {
subscribe,
answer: () => update((state) => ({ ...state, isAnswered: true })),
show: (activeQuestion: IQuestion) =>
update((state) => ({ ...state, activeQuestion, isQuestionShowing: true })),
close: () =>
setTimeout(() => {
update((state) => ({ ...state, isQuestionShowing: false, isAnswered: false }))
}, 1000)
}
}
export const questionStore = createStore()
And then in component, when I try to get isAnswered I get an error in WebStorm "Unresolved variable isAnswered". By the way VSCode doesn't highlight it like an error.
<script lang="ts">
import { questionStore } from '../store/questionStore'
console.log($questionStore.isAnswered)
</script>
If I convert store to this it works well:
interface IQuestionStore {
activeQuestion: null | IQuestion
isQuestionShowing: boolean
isAnswered: boolean
}
export const questionStore = writable<IQuestionStore>({
activeQuestion: null,
isQuestionShowing: false,
isAnswered: false
})
And! I also have an another store which looks the same in general, just other names. And it works well too! WTF? May be I'm missing something? Or may be I need to make some fixes in WebStorm to force it work well with Svelte?

Related

vitest failed to mock quasar

I am having vue3 app with vite and vitest and trying to mock the Quasar useQuasar composable which I am using in my custom Composable like:
// useLoginRequestBuilder.ts
import { makeUserAuthentication } from "#/main/factories"
import { useQuasar } from "quasar"
export function useLoginRequestBuilder() {
const $q = useQuasar()
async function login() {
try {
$q.loading.show()
const auth = makeUserAuthentication()
return await auth.signinRedirect()
} catch (e) {
console.log(e)
$q.loading.hide()
$q.notify({
color: "red-4",
textColor: "white",
icon: "o_warning",
message: "Login Failed!",
})
}
}
return {
login,
}
}
and I am trying to mock quasar in tests like:
// useLoginRequestBuilder.spec.ts
import { useLoginRequestBuilder } from "#/main/builders"
vi.mock("quasar", () => ({ // <--- this is not really mocking quasar
useQuasar: () => ({
loading: {
show: () => true,
hide: () => true,
},
}),
}))
const spyAuth = vi.fn(() => Promise.resolve(true))
vi.mock("#/main/factories", () => ({
makeUserAuthentication: () => ({
signinRedirect: () => spyAuth(),
}),
}))
describe("test useLoginRequestBuilder", () => {
test("should call signinRedirect", async () => {
const { login } = useLoginRequestBuilder()
const sut = await login()
expect(sut).toBe(true)
})
})
vi.mock("quasar"... is failing to mock quasar and I am getting below error. That means, it failed to mock and failed to get the $q.loading.... object.
TypeError: Cannot read properties of undefined (reading 'loading')
I understand that there is a separate testing lib for quasar, here but I think this is not really the case here.
Bordering on a necro-post, but I had a similar issue that the mocking factory wasn't creating the plugins being used in non-Vue components, and had to mock each call individually in the end.
Though I'd add it here for anyone else
vitest.mock("quasar", () => vi.fn()); // this doesn't mock out calls
// use individual mocks as below
import { Loading } from "quasar";
vi.spyOn(Loading, "show").mockImplementation(() => vi.fn());
vi.spyOn(Loading, "hide").mockImplementation(() => vi.fn());

Vitest gives no output

I'm new to testing and im trying to write some unit tests for my Vue app. The problem is that vitest givesno output and I cant figure out what is wrong. Any help would be apriciated.
describe('UserForm', () => {
it('renders component properly', async () => {
const viewId = "123"
render(UserForm, {
props: {
open: true
}
})
const view = await screen.findByText('Kontrahent')
expect(view.id).toBe(viewId)
})
})
I run the test with this command
vitest --environment jsdom
Have you tried to do something like:
import { mount } from "#vue/test-utils";
// in the describe
const wrapper = mount(Login, {
props: {
open: true
},
});
it("mounts the component", () => {
expect(wrapper.html()).toContain("Kontrahent");
});

Cannot test AsyncTypeahead from react-bootstrap-typeahead with Enzyme

I am trying to test AsyncTypeahead from react-bootstrap-typeahead.
I have a very simple test component :
class AsyncTypeahead2 extends Component<Props, State> {
constructor(props: Props) {
super(props);
this.state = {
isLoading: false,
};
}
render() {
return ( <AsyncTypeahead
isLoading={this.state.isLoading}
onSearch={query => {
this.setState({isLoading: true});
fetch("http://www.myHTTPenpoint.com")
.then(resp => resp.json())
.then(json => this.setState({
isLoading: false,
options: json.items,
}));
}}
options={this.state.options}
labelKey={option => `${option.stateName}`}
/> )
}
}
const url = "http://www.myHTTPenpoint.com"
fetchMock
.reset()
.get(
url,
{
items: [
{id:1, stateName:"Alaska"},
{id:2, stateName:"Alabama"}
]
},
);
(Note that the URL is mocked to return two elements)
When I run this in my storybook it looks fine :
But if I want to test it (with Enzyme) it does not recognise the < li > items that pop up.
let Compoment =
<div>Basic AsyncTypeahead Example
<AsyncTypeahead2/>
</div>
const wrapper = mount(Compoment);
let json = wrapper.html();
let sel = wrapper.find(".rbt-input-main").at(0)
sel.simulate('click');
sel.simulate('change', { target: { value: "al" } });
expect(wrapper.find(".rbt-input-main").at(0).getElement().props.value).toBe("al")
expect(wrapper.find(".dropdown-item").length).toBe(2) //but get just 1 element "Type to Search..."
Instead of finding two "dropdown-item" items there is just one item with the text "Type to Search...".
Is the AynchTypeahead not updating the DOM correctly with respect to Enzyme?
<AsyncTypeahead> is asynchronous. On the other hand simulate() is synchronous. So at the time you get to expect() AsyncTypeahead not even started to populate the dropdown with <li> elements. You need to wait for it.
It's not specified, but it looks like you are using fetch-mock package.
There is the flush function which
Returns a Promise that resolves once all fetches handled by fetch-mock have resolved
So this:
...
sel.simulate('click');
sel.simulate('change', { target: { value: "al" } });
await fetchMock.flush() // !!!
expect(wrapper.find(".rbt-input-main").at(0).getElement().props.value).toBe("al")
expect(wrapper.find(".dropdown-item").length).toBe(2)
should work.
...But probably it won't. Because
fetchMock.mock(...)
fetch(...)
await fetchMock.flush()
does work, but
fetchMock.mock(...)
setTimeout(() => fetch(...), 0)
await fetchMock.flush()
does not. await fetchMock.flush() returns right away if there was no call of fetch. And probably there won't be. Because <AsyncTypeahead> debounces.
(By the way, you can also try to mock fetch on a per-test basis. Just in case.)
So I see two options:
Use something else instead of fetch-mock package. Where you can resolve your own Promises on mocked requests completion.
https://tech.travelaudience.com/how-to-test-asynchronous-data-fetching-on-a-react-component-ff2ee7433d71
import waitUntil from 'async-wait-until';
...
test("test name", async () => {
let Compoment = <AsyncTypeahead2/>
...
await waitUntil(() => wrapper.state().isLoading === false);
// or even
// await waitUntil(() => wrapper.find(".dropdown-item").length === 2, timeout);
expect(...)
})
This options if not pretty. But maybe it's your only option - there is not only the fetch-mock you should worry about. setState also asynchronous... and it looks like there is no pretty way to check when it's done updating the state and the DOM without changing the real code (which is quite undesirable).
The exact solution to my problem is in the following code (copy and paste into a JS file to see it work).
Things to note :
I needed to use the waitUntil function from the async-wait-until library. fetch-mock on its own does not provide the functionality to test async code.
I needed to add an ugly hack at global.document.createRange because of some tooltip issue with react-bootstrap-typeahead and jest.
use waitUntil to wait on changes on the internal state of the component
It is very important to call wrapper.update() to update the DOM afterwards.
..
import React, {Component} from 'react';
import waitUntil from 'async-wait-until';
import {mount} from "enzyme";
import fetchMock from "fetch-mock";
import {AsyncTypeahead} from "react-bootstrap-typeahead";
describe('Autocomplete Tests ', () => {
test(' Asynch AutocompleteInput ', async () => {
class AsyncTypeaheadExample extends Component<Props, State> {
constructor(props: Props) {
super(props);
this.state = {
isLoading: false,
finished: false
};
}
render() {
return (<AsyncTypeahead
isLoading={this.state.isLoading}
onSearch={query => {
this.setState({isLoading: true});
fetch("http://www.myHTTPenpoint.com")
.then(resp => resp.json())
.then(json => this.setState({
isLoading: false,
options: json.items,
finished: true
}));
}}
options={this.state.options}
labelKey={option => `${option.stateName}`}
/>)
}
}
const url = "http://www.myHTTPenpoint.com"
fetchMock
.reset()
.get(
url,
{
items: [
{id: 1, stateName: "Alaska"},
{id: 2, stateName: "Alabama"}
]
},
);
let Compoment =
<AsyncTypeaheadExample/>
// ugly hacky patch to fix some tooltip bug
// https://github.com/mui-org/material-ui/issues/15726
global.document.createRange = () => ({
setStart: () => {
},
setEnd: () => {
},
commonAncestorContainer: {
nodeName: 'BODY',
ownerDocument: document,
},
});
let wrapper = mount(Compoment);
let sel = wrapper.find(".rbt-input-main").at(0)
sel.simulate('click');
sel.simulate('change', {target: {value: "al"}});
expect(wrapper.find(".rbt-input-main").at(0).getElement().props.value).toBe("al")
//now the async stuff is happening ...
await waitUntil(() => {
return wrapper.state().finished === true;
}, 3000); //wait about 3 seconds
wrapper.update() //need to update the DOM!
expect(wrapper.find(".dropdown-item").length).toBe(2) //but get just 1 element "Type to Search..."
})
});
UPDATE
I can also compare on wrapper items rather than doing a direct comparison on the state :
//now the async stuff is happening ...
await waitUntil(() => {
wrapper.update() //need to update the DOM!
return wrapper.find(".dropdown-item").length > 1
}, 3000); //wait about 3 seconds
This is probably better because it means i dont need to know about the component internals.

How can i coverage a promise response with Jasmine and Karma

I have a function that returns and treats a promise, I need to cover the return that is inside then but I don't know how I can do this, I'm currently trying as follows:
confirmRemoveUser(user: IUser) {
this.modalService
.open('Confirma a exclusão do usuário selecionado?', {
titleText: 'Confirmando exclusão',
confirmButtonText: 'Sim',
cancelButtonText: 'Cancelar',
closeButtonText: 'Fechar',
buttonType: 'danger'
})
.result.then(
(result: BentoModalConfirmationCloseReason) => {
if (result === BentoModalConfirmationCloseReason.Confirm) {
if (this.removeUser(user)) {
this.toastService.open('Usuário excluído com sucesso!', { type: 'success', close: true });
} else {
this.toastService.open('Falha ao excluir o usuário!', { type: 'warning', close: true, duration: 0 });
}
}
}
);
}
I'm currently using callthrough () and imagine that with some parameter I can get the promise but I don't know how:
it('Given_ConfirmRemoveUser_When_UserStepIsCalled_Then_UserIsRemoved', (done) => {
component.selectedJob = {
};
component.selectedArea = {
};
component.users = [{
}];
spyOn(modalService, 'open').withArgs('This is modal msg').and.callThrough();
component.confirmRemoveUser(component.users[0]);
expect(modalService.open).toHaveBeenCalled();
done();
});
And my coverage is like the image below:
Image here!
UPDATE
New Error
Your test should work when it is rewritten as follows:
it('Given_ConfirmRemoveUser_When_UserStepIsCalled_Then_UserIsRemoved', (done) => {
spyOn(modalService, 'open').and.returnValue(Promise.resolve(BentoModalConfirmationCloseReason.Confirm));
spyOn(toastService, 'open').and.stub();
component.confirmRemoveUser(component.users[0])
.then(r => {
expect(toastService.open).toHaveBeenCalled();
done();
})
.catch(e => fail(e));
});
You probably also want to know what will be displayed in the toast. Therefore it makes sense to rather use expect(toastService.open).toHaveBeenCalledWith(?);.
UPDATE
Above solution only works if confirmRemoveUser would return a Promise.
confirmRemoveUser(user: IUser) {
return this.modalService
...
In your case, the use of the done function does not make sense. You need to use async and await.
it('Given_ConfirmRemoveUser_When_UserStepIsCalled_Then_UserIsRemoved', async () => {
spyOn(modalService, 'open').and.returnValue(Promise.resolve(BentoModalConfirmationCloseReason.Confirm));
spyOn(toastService, 'open').and.stub();
await component.confirmRemoveUser(component.users[0]);
expect(toastService.open).toHaveBeenCalled();
});
The same can be achieved with fakeAsync and flush.
import { fakeAsync, flush } from '#angular/core/testing';
...
it('Given_ConfirmRemoveUser_When_UserStepIsCalled_Then_UserIsRemoved', fakeAsync(() => {
spyOn(modalService, 'open').and.returnValue(Promise.resolve(BentoModalConfirmationCloseReason.Confirm));
spyOn(toastService, 'open').and.stub();
component.confirmRemoveUser(component.users[0]);
flush();
expect(toastService.open).toHaveBeenCalled();
}));

How can I pass a boolean to an expect statement for testing using mocha/chai?

Using Vue CLI I have a unit test that I am trying to check for a true/false that looks like this:
describe('The thing', () => {
it('must be available.', () => {
const available = true
const wrapper = shallowMount(MyVueComponent, {
propsData: { available },
})
expect(wrapper).to.be.true
})
})
When I run npm run test:unit
I get the following:
AssertionError: expected { Object (isFunctionalComponent, _emitted, ...) } to be true
If I just check the value of available, then it's all good. But that seems like I'm doing it wrong.
Other tests I have written are working fine as I am checking for a text value:
describe('The thing', () => {
it('should have a name.', () => {
const name = 'Hal'
const wrapper = shallowMount(MyVueComponent, {
propsData: { name },
})
expect(wrapper.text()).to.include(name)
})
})
I am not sure how to check that the available is a boolean and it must be true. Any suggestions would be appreciated!
EDIT
This is what my Vue component looks like:
export default {
name: 'MyVueComponent',
props: {
name: String
},
data: function() {
return {
available: true,
}
},
}
EDIT 2
This seems to work in my unit test:
describe('The thing', () => {
it('must be available.', () => {
const available = true
const wrapper = shallowMount(MyVueComponent, {
propsData: { available },
})
expect(wrapper.vm.available).to.be.true
})
})
However, it is looking at my actual component in my /src directory. If I change the data values from true to false my tests come out correctly. I'm not sure how to have the data stay at the test level. So if I were to change const available = false, my test should fail--but it does not.
EDIT 3
It seems like this works (to access the data object):
describe("The thing", () => {
it("must be available.", () => {
const defaultData = MyVueComponent.data();
// const wrapper = shallowMount(MyVueComponent, {});
expect(defaultData.available).to.be.true;
});
});
But it still seems not right that I'm referencing my actual code, and not sticking within the unit tests.
You want to check the received prop, which you can do with wrapper.props()
describe('The thing', () => {
it('must be available.', () => {
const available = true
const wrapper = shallowMount(MyVueComponent, {
propsData: { available },
})
expect(wrapper.props().available).to.be.true
// or:
// expect(wrapper.props().available).to.equal(available)
})
})
Chai's .to.be.true and .to.equal use === so there is no need to separately check that it is indeed a Boolean, but if you prefer the "expressiveness" of it, you can check it too:
expect(wrapper.props().available).to.be.a('boolean')