Problem: UserEvent.type is not working. I have a simple test where I get the textbox, which works, but when trying to type in that textbox, the text does not display.
Most of the solutions I have seen to problems similar to this is to await the typing. I tried that and the text still does not display.
Versions:
"jest": "^28.1.3",
"#testing-library/jest-dom": "^5.16.4",
"#testing-library/react": "12.1.2",
"#testing-library/user-event": "^14.2.1",
Test:
test('LoginPage should handle events properly', async () => {
render(<LoginPage />)
const textbox = screen.getByRole(/textbox/i)
await userEvent.type(textbox, 'test')
screen.debug(textbox)
})
Output from screen.debug() after firing the typing:
<textarea
placeholder="enter your private key here."
/>
LoginPage Component:
import { useHistory } from 'react-router-dom'
import { useContext, useState, useEffect } from 'react'
import { ABOUT_URL } from '../../config'
import LoadingCover from '../../components/loadingCover/loadingCover'
import LoadingButton from '../../components/loadingButton/loadingButton'
import UserContext from '../../context/User'
const LoginPage = () => {
const history = useHistory()
const [isLoading, setIsLoading] = useState(false)
const [input, setInput] = useState<string>('')
const [errorMsg, setErrorMsg] = useState<string>('')
const [isButtonLoading, setButtonLoading] = useState<boolean>(false)
const userContext = useContext(UserContext)
useEffect(() => {
setErrorMsg('')
}, [input])
const handleInput = (event: any) => {
setInput(event.target.value)
}
const login = async () => {
setButtonLoading(true)
const hasSignedUp = await userContext.login(input)
setButtonLoading(false)
if (!hasSignedUp) {
setErrorMsg('Incorrect private key. Please try again.')
return
}
// need to conditionally get an airdrop if the user signed up but has
// not claimed an airdrop, and it's the same epoch as when they signed
// up
history.push('/')
}
return (
<div className="login-page">
<div className="left-column">
<img
src={require('../../../public/images/unirep-title-white.svg')}
/>
</div>
<div className="right-column">
<div className="close">
<img
id="unirep-icon"
src={require('../../../public/images/unirep-title.svg')}
/>
<img
id="close-icon"
src={require('../../../public/images/close.svg')}
onClick={() => history.push('/')}
/>
</div>
<div className="info">
<div className="title">Welcome back</div>
<p>
To enter the app, please use the private key you got
when you signed up.
</p>
<textarea
placeholder="enter your private key here."
onChange={handleInput}
/>
{errorMsg.length === 0 ? (
<div></div>
) : (
<div className="error">{errorMsg}</div>
)}
<div className="sign-in-btn" onClick={login}>
<LoadingButton
isLoading={isButtonLoading}
name="Sign in"
/>
</div>
<div className="notification">
Lost your private key? Hummm... we can't help you to
recover it, that's a lesson learned for you. Want to
restart to earn rep points?{' '}
<a
target="_blank"
href={`${ABOUT_URL}/alpha-invitation`}
>
Request an invitation code here.
</a>
</div>
<div className="go-to-signup">
Got an invitation code? Join here
</div>
</div>
</div>
{isLoading ? <LoadingCover /> : <div></div>}
</div>
)
}
export default LoginPage
Got the same issue and find a really simple solution on the Testing Library documentation :
Start your test with
const user = userEvent.setup()
then you call userEvent methods directly from user :
await user.type(textbox, 'test')
You have to setup directly on each test, the library recommend to avoid using beforeEach() for this.
Related
First thank you for reading my question and trying to help.
I'm trying to make a cookie accept footer and when user clicks on accept all I want to enable google analytics and google tag manager. But when user clicks on only necessary I only want google tag manager and I don't know how to make that.
This is my cookie footer :
import { useState } from "react";
import Cookies from 'js-cookie'
function FooterCookies() {
cosnt [ show, setShow ] = useState(true)
const content = {
text : 'This site uses services that uses cookies to deliver better experience and analyze traffic. You can learn more about the services we use at our ',
policy : 'Privacy Policy.'
}
const setCookies = () => {
Cookies.set('cookie', 'true', { expires: 2 })
}
return (
<>
{show ?
(
<div
className=" h-auto bg-black/30 fixed
bottom-0 text-cus-yellow rounded
py-4 px-8 flex justify-evenly items-center z-[100]
max75:flex-col max75:px-5 max75:py-3 max30:py-1 max30:px-1.5 max75:items-start">
<div className=" w-85% max70:text-sm flex items-center max75:w-100% max75:items-start max75:mb-3 " >
<p className=" text-[13px] pt-1">
{content.text}
<span className=" text-[16px] underline cursor-pointer ">
{content.policy}
</span>
</p>
</div>
<div className=" flex " >
<div onClick={() => setCookies()}>
<div onClick={() => setShow(false)}
className="border border-cus-yellow text-cus-yellow hover:bg-cus-yellow/60
transition-all hover:text-black w-28 h-80% bg-black/40 cursor-pointer
flex flex-col justify-center items-center whitespace-nowrap
duration-500 text-lg max75:text-base font-medium mx-2 px-1 py-2 max75:mx-0 max75:py-1 max75:px-0.5 "
>Accept All</div>
</div>
<div onClick={() => setShow(false)}
className="border border-cus-yellow text-cus-yellow hover:bg-cus-yellow/60
transition-all hover:text-black w-36 h-80% bg-black/40 cursor-pointer
flex flex-col justify-center items-center whitespace-nowrap
duration-500 text-lg max75:text-base font-medium mx-2 px-1 py-[13.76px] max75:mx-0 max75:ml-3 max75:px-0.5 max75:py-[8.75px] "
>Only Nessesary</div>
</div>
</div>
)
:
<></>
}
</>
)
}
This is my gtag.js :
export const GA_TRACKING_ID = 'XXXXXXXXX'
export const pageview = (url) => {
window.gtag('config', GA_TRACKING_ID, {
page_path: url,
})
}
export const event = ({ action, category, label, value }) => {
window.gtag('event', action, {
event_category: category,
event_label: label,
value: value,
})
}
This is myApp.js :
import '../styles/globals.css'
import 'tailwindcss/tailwind.css'
import { useEffect } from 'react'
import Script from 'next/script'
import { useRouter } from 'next/router'
import * as gtag from '../lib/gtag'
function MyApp({ Component, pageProps }) {
const router = useRouter()
useEffect(() => {
const handleRouteChange = (url) => {
gtag.pageview(url)
}
router.events.on('routeChangeComplete', handleRouteChange)
router.events.on('hashChangeComplete', handleRouteChange)
return () => {
router.events.off('routeChangeComplete', handleRouteChange)
router.events.off('hashChangeComplete', handleRouteChange)
}
}, [router.events])
return (
<>
<Script
strategy="afterInteractive"
src={`https://www.googletagmanager.com/gtag/js?id=${gtag.GA_TRACKING_ID}`}
/>
<Script
id="gtag-init"
strategy="afterInteractive"
dangerouslySetInnerHTML={{
__html: `
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', '${gtag.GA_TRACKING_ID}', {
page_path: window.location.pathname,
});
`,
}}
/>
<Script id="google-tag-manager" strategy="afterInteractive">
{`
(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
})(window,document,'script','dataLayer','XXXXXXXX');
`}
</Script>
<Component {...pageProps} />
</>
)
}
export default MyApp
This is my document.js :
import { Html, Head, Main, NextScript } from 'next/document'
import Script from 'next/script'
import { GA_TRACKING_ID } from '../lib/gtag'
export default function Document() {
return (
<Html>
<Head>
<Script
id='google tags manager'
dangerouslySetInnerHTML={{
__html:`(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
})(window,document,'script','dataLayer','XXXXXXXX');`,
}}
/>
<Script
async src={`https://www.googlemanager.com/gtag/js?id=${GA_TRACKING_ID}`}
/>
</Head>
<body>
<noscript
dangerouslySetInnerHTML={{
__html: `<iframe src="https://www.googletagmanager.com/ns.html?id=XXXXXXX"
height="0" width="0" style="display:none;visibility:hidden" />`,
}}
/>
<Main />
<NextScript />
<script
dangerouslySetInnerHTML={{
__html: `
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
`,
}}
/>
</body>
</Html>
)
}
i'm newbie in react js , and i want to have a form with select options
i want that when user click on each option , each option render different elements
class Resepy extends Component {
state = {
Resepy : 'default'
}
render() {
return = (
<div className="Resepy">
<form>
<select id="id_field1" name="field1" onChange={(e) => this.state.Resepy = "Burger"}>
<option value="default">Food type not selected</option>
<option value="burger" onClick={(e) => this.setState({ Resepy: 'Burger' })}>Burger</option>
<option value="pizza" onClick={(e) => this.setState({ Resepy: 'Pizza' })}>Pizza</option>
</select>
<div className="food">
{ this.state.Resepy === "burger" ? <div className="burger"></div> //can return any html
: <div className="default">default</div>
}
<div className="pizza"></div>
<div className="food-detail"></div>
</div>
<button type="submit">Add to tray</button>
</form>
</div>
);
}
}
export default Resepy;
General ternary operator used for more readable code.
Like this:
<form>//can be any element
{ codition == true ? <div>It is true</div> //can return any html
: <div>It is false</div>
}
</form>
Tested, working. Problem was with onClick method option cannot invoke that event.
class Resepy extends React.Component {
constructor(props){
super(props);
this.state = {
selected : 'default'
};
}
setSelected = (event) => {
let select = document.getElementById("id_field1");
this.setState({selected: select.value});
//document.getElementById("test").innerHTML = select.value;
}
render() {
return (
<div className="Resepy">
<h1>Something</h1>
<form>
<select id="id_field1" name="field1" onChange={this.setSelected}>
<option value="default">Food type not selected</option>
<option value="burger">Burger</option>
<option value="pizza">Pizza</option>
</select>
<div id="test"></div>
<div className="food">{
(this.state.selected === "default") ?
<div className="default">Default</div>
: (this.state.selected === "burger") ?
<div className="burger">Burger</div>
: <div className="pizza">Pizza</div>
}</div>
<button type="submit">Add to tray</button>
</form>
</div>
);
}
}
I have a hard time understanding you, but the most likely thing you could be trying to achieve with the following code from your original question:
<div className="burger" Resepy={this.state.Resepy === 'burger'}></div>
is:
<div className="food">
<div className={this.state.Resepy} />
</div>
Working example (but I am using Hooks instead of a class component):
const App = () => {
const [selected, setSelected] = React.useState('default')
const handleChange = (event) => {
setSelected(event.target.value)
}
return (
<div>
<select value={selected} onChange={handleChange}>
<option>default</option>
<option>burger</option>
<option>pizza</option>
</select>
<div className="food">
<div className={selected}>{selected}</div>
</div>
</div>
)
}
ReactDOM.render(<App />, document.getElementById('root'))
.default { color: gray; }
.burger { color: orange; }
.pizza { color: red; }
<script crossorigin src="https://unpkg.com/react#16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom#16/umd/react-dom.development.js"></script>
<div id="root"></div>
Now i want to render html elements depends on values , i tried this but it shows just [Object Object]
setSelected = (event) => {
let select = document.getElementById("id_field1");
document.getElementById("food").innerHTML =
select.value == "default" ?
<div className="default">Default</div>
: select.value == "Burger" ?
<div className="burger">Burger</div>
: <div className="pizza">Pizza</div>
}
I have a component that switch Language of a nuxtjs application using nuxt-i18n as follows
<template>
<div class="navbar-item has-dropdown is-hoverable">
<a class="navbar-link langpicker">{{ $t("language_picker") }} </a>
<div class="navbar-dropdown is-hidden-mobile">
<div>
<nuxt-link
v-if="currentLanguage != 'en'"
class="navbar-item"
:to="switchLocalePath('en')"
>
<img src="~/static/flags/us.svg" class="flagIcon" /> English
</nuxt-link>
<nuxt-link
v-if="currentLanguage != 'el'"
class="navbar-item"
:to="switchLocalePath('el')"
>
<img src="~/static/flags/el.svg" class="flagIcon" /> Ελληνικά
</nuxt-link>
</div>
</div>
</div>
</template>
<script>
export default {
name: "LangPicker",
computed: {
currentLanguage() {
return this.$i18n.locale || "en";
}
}
};
</script>
I want to write a Unit Test that test the correct language switch on 'nuxt-link' click.
So far I have the following
import { mount, RouterLinkStub } from "#vue/test-utils";
import LangPicker from "#/components/layout/LangPicker";
describe("LangPicker with locale en", () => {
let cmp;
beforeEach(() => {
cmp = mount(LangPicker, {
mocks: {
$t: msg => msg,
$i18n: { locale: "en" },
switchLocalePath: msg => msg
},
stubs: {
NuxtLink: RouterLinkStub
}
});
});
it("Trigger language", () => {
const el = cmp.findAll(".navbar-item")
});
});
cmp.find(".navbar-item") return an empty object.
I don't know how I must set up to "trigger" the click event.
const el = cmp.findAll(".navbar-item")[1].trigger("click");
make sure your find selector is correct.
const comp = cmp.find(".navbar-item");
comp.trigger('click');
you can use chrome dev tools selector utility.
Refer this link for detailed information.
I am trying to test a form submit.. it seems that trigger is not appropriate
1) calls store action login when the form is submitted
LoginPage.vue
TypeError: wrapper.find(...).trigger is not a function
at Context.<anonymous> (webpack:///test/unit/specs/pages/LoginPage.spec.js:35:28 <- index.js:50826:29)
my vue component to be tested
LoginPage.vue
<template>
<div class="container">
<div class="login-page">
<h1 class="title">Login to existing account</h1>
<form #submit.prevent="submit()" class="form form--login grid">
<div class="row">
<label for="login__email">Email</label>
<input type="text" id="login__email" class="input--block" v-model="email" v-on:keyup="clearErrorMessage" />
</div>
<div class="row">
<label for="login__password">Password</label>
<input type="password" id="login__password" class="input--block" v-model="password" v-on:keyup="clearErrorMessage" />
</div><!-- /.row -->
<div class="row">
<label></label>
<button id="submit" type="submit">Login</button>
</div><!-- /.row -->
<div v-show='hasError' class="row">
<label></label>
<p class="error">Invalid credentials</p>
</div>
</form>
</div><!-- /.login-page -->
</div>
</template>
<script>
import store from '#/vuex/store'
import { mapActions } from 'vuex'
import _ from 'underscore'
export default {
name: 'loginPage',
data () {
return {
email: 'john.doe#domain.com',
password: 'john123',
hasError: false
}
},
methods: _.extend({}, mapActions(['login']), {
clearErrorMessage () {
this.hasError = false
},
submit () {
return this.login({user: { email: this.email, password: this.password }})
.then((logged) => {
if (logged) {
this.$router.push('shoppinglists')
} else {
this.hasError = true
}
})
}
}),
store
}
</script>
LoginPage.spec.js
import LoginPage from '#/pages/LoginPage'
import Vue from 'vue'
import Vuex from 'vuex'
import sinon from 'sinon'
import { mount } from 'avoriaz'
Vue.use(Vuex)
describe('LoginPage.vue', () => {
let actions
let getters
let store
beforeEach(() => {
actions = {
login: sinon.stub()
}
getters = {
isAuthenticated: () => {
state => state.isAuthenticated
}
}
store = new Vuex.Store({
actions,
getters,
state: {
isAuthenticated: false,
currentUserId: ''
}
})
})
it('calls store action login when the form is submitted', (done) => {
const wrapper = mount(LoginPage, { store })
wrapper.find('#submit').trigger('submit')
wrapper.vm.$nextTick(() => {
expect(actions.login.calledOnce).to.equal(true)
done()
})
})
})
Should be trigger 'click' on the form tag !
it('calls store action login when the form is submitted', (done) => {
const wrapper = mount(LoginPage, { store })
const form = wrapper.find('form')[0]
form.trigger('submit')
wrapper.vm.$nextTick(() => {
expect(actions.login.calledOnce).to.equal(true)
done()
})
})
Hello I am trying to simulate an 'ended' event on and HTML5 audio element without success. Here is my component:
export class AudioPlayer extends Component {
constructor (props) {
super(props);
// Auto play turned off when component render for the first time
this.autoPlay = false;
this.shouldAudioAutoPlay = this.shouldAudioAutoPlay.bind(this);
}
componentDidMount () {
let audio = findDOMNode(this.player);
if (audio) {
console.log('in')
audio.addEventListener('ended', () => {
console.log('event dispatched')
this.props.audioFinished();
});
}
}
render () {
return (
<Media>
<div>
<div className="media-player">
<Player src={this.props.audioUrl.url}
autoPlay={false}
id="activeModuleAudio"
vendor="audio"
ref={(player) => { this.player = player }}
/>
</div>
<div className="ap-main">
<div className="ap-controls">
<PlayButton />
<ProgressBar enableUserInput={this.props.enableUserInput} audio={this.props.audioUrl} />
</div>
<a href={this.props.facebookUrl}><div className="ap-facebook-group" /></a>
</div>
</div>
</Media>
);
}
}
And here the test:
it('should dispatch an action when audio is finished', () => {
let mockedAudioFinishedAction = jest.fn();
let mockedEnableUserInputAction = jest.fn();
let mountedWrapper = mount(
<AudioPlayer
audioUrl={{ url: 'audio-url', autoPlay: true }}
facebookUrl="facebook-url" audioFinished={mockedAudioFinishedAction}
enableUserInput={mockedEnableUserInputAction}
/>
);
mountedWrapper.update();
mountedWrapper.update();
mountedWrapper.find(Player).simulate('ended');
expect(mockedAudioFinishedAction.mock.calls.length).toBe(1);
});
The simulate seems to not be dispatching the event,despite working on the browser. Any thoughts?
Thanks in advance!