In test environment, keydown event no longer triggered by keyCode after updated vue-test-utils to version 1.0.0-beta.26 or above, example:
Vue
<div #keydown="onKeydown"></div>
Scripts
public onKeydown (event: KeyboardEvent) {
if(event.keyCode === 13) {
this.keyName = 'enter'
} else if(event.keyCode === 67) {
this.keyName = 'c'
}
}
Previous version, all test PASS with keyCode as parameter.
// version 1.0.0-beta.25
const wrapper = shallowMount(HelloWorld)
wrapper.find('div').trigger('keydown', { keyCode: 13 })
expect(wrapper.vm.keyName).toBe('enter') // PASS
wrapper.find('div').trigger('keydown', { keyCode: '67' })
expect(wrapper.vm.keyName).toBe('c') // PASS
After version updated, my test now FAILED.
Tried use keydown.enter, it work for enter key but doesn't work for 'c' key.
// version 1.0.0-beta.26
wrapper.find('div').trigger('keydown.enter')
expect(wrapper.vm.keyName).toBe('enter') // PASS
wrapper.find('div').trigger('keydown.c')
expect(wrapper.vm.keyName).toBe('c') // FAILED
Any suggestion how to trigger keydown event with a-z key?
You should pass key as an option. From the official docs
it('Magic character "a" sets quantity to 13', () => {
const wrapper = mount(QuantityComponent)
wrapper.trigger('keydown', {
key: 'a'
})
expect(wrapper.vm.quantity).toBe(13)
})
Related
According to a prior SO answer, you can implement getPriority for a forge viewer Tool. And according to another SO answer extending the ToolInterface does not work. Hence, me not extending the ToolInterface implementing my Tool like so:
class MyCustomExtension extends Autodesk.Viewing.Extension {
constructor(viewer, options) {
super(viewer, options);
this.theiaUtil = new TheiaUtil(this);
}
getPriority() {
console.log("Theia#getPriority called! ", (this.getPriority && this.getPriority() || 0));
return 100000;
}
...
}
My tool's priority is returned as 0 in the ToolController, although it shouldn't:
function getPriority(tool)
{
return tool.getPriority instanceof Function && tool.getPriority() || 0;
}
I don't know why this function returns 0 as tool.getPriority instanceof Function returns true if I call MyCustomExtension.getPriority myself.
Note that ToolInterface is implemented like so:
function ToolInterface()
{
this.names = [ "unnamed" ];
this.getNames = function() { return this.names; };
this.getName = function() { return this.names[0]; };
this.getPriority = function() { return 0; };
this.register = function() {};
this.deregister = function() {};
this.activate = function(name, viewerApi) {};
this.deactivate = function(name) {};
this.update = function(highResTimestamp) { return false; };
this.handleSingleClick = function( event, button ) { return false; };
this.handleDoubleClick = function( event, button ) { return false; };
this.handleSingleTap = function( event ) { return false; };
this.handleDoubleTap = function( event ) { return false; };
// ...
}
Because of that, simply extending the ToolInterface class won't work because all these properties and functions added to the instance in the constructor will take precedence over your actual class methods. This is also likely the reason why you're seeing the priority value returned as zero - when you call myTool.getPriority(), you are not actually calling your getPriority method, but rather the default function which was assigned to this.getPriority in ToolInterface's constructor.
To work around this issue I would recommend explicitly deleting the corresponding fields in your class' constructor (something I explain in my blog post on implementing custom Forge Viewer tools):
class DrawTool extends Autodesk.Viewing.ToolInterface {
constructor() {
super();
this.names = ['box-drawing-tool', 'sphere-drawing-tool'];
// Hack: delete functions defined *on the instance* of the tool.
// We want the tool controller to call our class methods instead.
delete this.register;
delete this.deregister;
delete this.activate;
delete this.deactivate;
delete this.getPriority;
delete this.handleMouseMove;
delete this.handleButtonDown;
delete this.handleButtonUp;
delete this.handleSingleClick;
}
register() {
console.log('DrawTool registered.');
}
deregister() {
console.log('DrawTool unregistered.');
}
activate(name, viewer) {
console.log('DrawTool activated.');
}
deactivate(name) {
console.log('DrawTool deactivated.');
}
getPriority() {
return 42; // Or feel free to use any number higher than 0 (which is the priority of all the default viewer tools)
}
// ...
}
TL;DR: Activate the tool in button click event from a toolbar button instead of the extension's load method.
class MyExtension extends Autodesk.Viewing.Extension {
...
onToolbarCreated(toolbar) {
const MyToolName = 'My.Tool.Name'
let button = new Autodesk.Viewing.UI.Button('my-tool-button');
button.onClick = (e) => {
const controller = this.viewer.toolController;
if (controller.isToolActivated(MyToolName)) {
controller.deactivateTool(MyToolName);
button.setState(Autodesk.Viewing.UI.Button.State.INACTIVE);
} else {
controller.activateTool(MyToolName);
button.setState(Autodesk.Viewing.UI.Button.State.ACTIVE);
}
};
}
...
}
I activated the tool instantly after registering it in the Extension's load method. Petr Broz's github repo from his blog post loads the tool from a button in the toolbar. So I moved the activation of the tool to a button click in the toolbar which worked for me.
I am performing an authentication module where I when I click the sign in button , I am verifying user present is MySQL db or not . I am dispatching the function in here in sign in page
Basically when I dispatch it , the null state of the rSignedIn is not changed immediately after dispatch function. I am completely using react hooks. Please help me solve this , I have been trying this for three days.
But the rSignedIn state value updates when I click the login button again, in general , the when I use the state value using the useSelector the value is updated the second the time when the handleLogin() is invoked
//Sign in Page
...
...
const status=useSelector((state)=>state);
...
...
const handleLogin=(event)=>{
dispatch(LoginUser(loginData));
console.log(status.auth.rSignedIn);
if(status.auth.rSignedIn){
console.log("LOGIN success");
History.push('/');
}else{
console.log("LoginFailed") ;
}
}
this is the action index page where I sent a request to MySQL db , then if there is a response I am dispatching it else an error.
export const LoginUser=(loginData)=>async(dispatch)=>{
await mysqlDB.post('/fetch/retreive',loginData)
.then((response)=>dispatch({type:ActionTypes.LOGIN_SUCCESS,payload:response.data}))
.catch((error)=>dispatch({type:ActionTypes.LOGIN_FAILED}))
}
This is my Reducer for this :
const initialState = {
gSignedIn:null,
userId:null,
registered:null,
data:null,
rSignedIn:null,
}
export default (state=initialState,action)=>{
switch (action.type){
case ActionTypes.GSIGN_IN:
return {...state,gSignedIn:true,userId: action.payload};
case ActionTypes.GSIGN_OUT:
return {...state,gSignedIn:false,userId:null};
case ActionTypes.REGISTER_SUCCESS:
return {...state,registered:true,data: action.payload};
case ActionTypes.REGISTER_FAILED:
return {...state,registered:false,data:null};
case ActionTypes.LOGIN_SUCCESS:
return {...state,rSignedIn:true,data: action.payload};
case ActionTypes.LOGIN_FAILED:
return {...state,rSignedIn:false,data:null};
case ActionTypes.LOGOUT:
return {...state,rSignedIn:false,data:null};
default:
return state;
}
};
dispatch will not update your state value immediately. State value is bound by closure and will only update in your next render cycle.
You can either use history.push within your action or make use of useEffect
const handleLogin=(event)=>{
dispatch(LoginUser(loginData, History));
}
...
export const LoginUser=(loginData, history)=>async(dispatch)=>{
await mysqlDB.post('/fetch/retreive',loginData)
.then((response)=>{
dispatch({type:ActionTypes.LOGIN_SUCCESS,payload:response.data}));
history.push('/')
}
.catch((error)=>{
dispatch({type:ActionTypes.LOGIN_FAILED}))
}
}
With the useEffect, you need to run the it only on change and not on initial render
const initialRender = useRef(true);
useEffect(() => {
if(!initialRender.current) {
if(state.auth.rSignedIn) {
history.push('/');
} else {
console.log(not signed in);
}
} else {
initialRender.current = false;
}
}, [state.auth.rSignedIn])
I want to race between a redux action and an event channel using redux-saga.
export function * createEventChannel () {
return eventChannel(emit => {
MyModule.addEventListener(MyModule.MY_EVENT, emit)
return () => { MyModule.removeEventListner(MyModule.MY_EVENT, emit)}
})
}
....
function * raceWithActionAndEvent() {
const channel = yield call(createEventChannel)
// I want to race between Redux Action: 'MY_ACTION' and channel here
}
This should do it:
export function* raceWithActionAndEvent() {
const channel = yield call(createEventChannel);
const winner = yield race({
channel: take(channel),
action: take(MY_ACTION),
});
if (winner.action) {
// then the action was dispatched first
}
if (winner.channel) {
// then the channel emitted first
}
}
In my opinion the code is quite readable. You set up a race between two takes and act on whichever wins.
Please note,createEventChannel doesn't need to be a generator function (like you have in the original question)
I have a requirement like saving two previous login details.
I am done with it. But my view will update only on refresh.But scope values are updated.
Tried with scope.apply,digest,timeout. But nothing seems to work here.
$scope.loginUserName=localStorage.getItem("loginUserName");
$scope.userName=localStorage.getItem("userName");
$scope.mobileNumber=localStorage.getItem("mobileNumber");
$scope.loginData = {};
$scope.userLogin = function(loginData) {
userService.userLogin(loginData).then(function(success) {
var res=success.message;
if(res==='success'){
if(localStorage.getItem("userName1")==null || localStorage.getItem("userName1") == success.firstName){
localStorage.setItem("userName1",success.firstName);
localStorage.setItem("loginUserName",success.firstName);
}else if(localStorage.getItem("userName2")==null || localStorage.getItem("userName2") == success.firstName ){
localStorage.setItem("userName2",success.firstName);
localStorage.setItem("loginUserName",success.firstName);
}
localStorage.setItem("userName",success.firstName);
$scope.userName=success.firstName;
$scope.mobileNumber = success.mobileNumber;
$scope.loginData = {};
$state.go('app.home');
}else{
$scope.message ='Wrong pin.Try again or click Forgot password to reset it.';
}
},function(error){
});
};
$scope.loginPerson = function(mobileNumber,userName){
localStorage.setItem("loginUserName",userName);
// here userName is updating,but not reflecting in view
$scope.loginUserName=localStorage.getItem("loginUserName");
//setTimeout(function(){ $scope.$apply(); });
console.log("In loginPerson:"+userName);
$state.go('app.start');
}
start.html
<span ng-if="loginUserName !=null">
<p class="startP">Enter pin for {{loginUserName}}
<i class="icon ion-chevron-down" ui-sref="app.loginOptions">
</i></p>
</span>
State
//Here is the state details,I have same controller for two state.
.state('app.loginOptions', {
url: '/loginOptions',
views: {
'menuContent': {
templateUrl: 'templates/loginOptions.html',
controller:'LoginCtrl'
}
}
})
.state('app.start',{
url:'/start',
views:{
'menuContent':{
templateUrl:'templates/start.html',
controller:'LoginCtrl'
}
}
EDIT
I have used within object also,But nothing is changed.
step 1) please use angular copy while get data from localstorage to $scope $scope.xyz=angular.copy(localstorage.get('key'))
after implement step 1 then not work use $scope.$apply(); after set value in $scope.
try to use loginUserName as a property of an object instead a property of scope directly. Sometimes angularjs fail to update view for these values.
Like
$scope.data={
loginUserName:""
};
Then inside your function
$scope.userLogin = function(loginData) {
...
$scope.data.loginUserName=localStorage.getItem("loginUserName");
// To check it
console.log($scope.data);
}
html
<span ng-if="data.loginUserName !=null">
...
</span>
Update
Change the loginPerson function like below.
$scope.loginPerson = function(mobileNumber,userName){
localStorage.setItem("loginUserName",userName);
$scope.data.loginUserName=localStorage.getItem("loginUserName");
console.log("In loginPerson:"+userName);
console.log($scope.data);
}
I got this function (in the document class)
public function kyz(event:KeyboardEvent):void{
trace(event.keyCode);
switch (event.keyCode){
case 65:{
if (ppm.currentFrame<200 || ppm.currentFrame>300) {
ppm.gotoAndStop(301);
ssm.gotoAndStop(302);
llm.gotoAndStop(302);
mmm.gotoAndStop(302);
myTimer.stop();
pd.play();
}else {
pd.play();
ppm.gotoAndPlay(10);
tlrnc-=10;
}
break;
}
case 68:{
if (ssm.currentFrame<200 || ssm.currentFrame>300) {
ssm.gotoAndStop(301);
ppm.gotoAndStop(302);
llm.gotoAndStop(302);
mmm.gotoAndStop(302);
myTimer.stop();
mTimer.stop();
sd.play();
}else {
sd.play();
ssm.gotoAndPlay(10);
tlrnc-=10;
}
break;
}
case 74:{
if (llm.currentFrame<200 || llm.currentFrame>300) {
llm.gotoAndStop(301);
ppm.gotoAndStop(302);
ssm.gotoAndStop(302);
mmm.gotoAndStop(302);
myTimer.stop();
mTimer.stop();
ld.play();
}else {
ld.play();
llm.gotoAndPlay(10);
tlrnc-=10;
}
break;
}
case 76:{
if (mmm.currentFrame<200 || mmm.currentFrame>300) {
mmm.gotoAndStop(301);
ppm.gotoAndStop(302);
ssm.gotoAndStop(302);
llm.gotoAndStop(302);
myTimer.stop();
mTimer.stop();
md.play();
}else {
md.play();
mmm.gotoAndPlay(10);
tlrnc-=10;
}
break;
}
}
}
that receives key event's and do stuff. now I'm trying to pass they keyCode from another function (to avoid changing the whole code for adding click functionality) got any suggestions?
you could dispatch a KeyboardEvent
stage.dispatchEvent(
new KeyboardEvent(KeyboardEvent.KEY_DOWN, true, false, myCharCode, myKeyCode));
just add the needed keyCode as a parameter
You cannot send any Keyboard or Mouse event without interactivity with user. You can handle this in another private function:
private function keyDownHandler(event : KeyboardEvent) : void {
this.handleEvent(event.keyCode);
}
private function handleEvent(keyCode : uint) : void {
//some actions
}
And when you need to make specific actions, you can just call handleEvent function without user side interactivity.