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!
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>
)
}
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.
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()
})
})
Is it possible to use a Modal without a trigger? I will open and close it via state.
For example, I want to use onClick on an input field(with a file name) to open the modal with a file chooser and then edit the name of the choosen file in the input field. All this in a nested modal...
Looks much simpler if I will have both modals in a parent component without the triggers, and I will display/hide them via open={true/false}
Thanks
Yes it is. Don't set the prop trigger (it is not required) and just provide the open value from state/props.
class container extends Component {
state = {
isParentOpen: false,
isChildOpen: false
}
handleClick = () => {
this.setState({
isParentOpen: !this.state.isOpen
});
}
handleFocus = () => {
this.setState({
isChildOpen: true
});
}
render() {
return (
<div>
<Modal
open={this.state.isParentOpen}
size="large"
>
...
<Input onFocus={this.handleFocus} />
</Modal>
<Modal
open={this.state.isChildOpen}
size="small"
>
...
</Modal>
<Button onClick={this.handleClick} />
</div>
);
}
}
(You can nest Modal if you want to)
Pass a prop to the modal component and fire handleOpen according to the prop on ComponentDidMount. This will allow the modal to be closed.
class ModalContainer extends Component {
componentDidMount() {
const { startOpen } = this.props;
if (startOpen) {
this.handleOpen();
}
}
handleOpen = () => this.setState({ modalOpen: true });
handleClose = () => this.setState({ modalOpen: false });
render() {
return (
<Modal open={this.state.modalOpen} onClose={this.handleClose} />
)
}