Reactjs TypeError无法读取属性';州';未定义的

Reactjs TypeError无法读取属性';州';未定义的,reactjs,jestjs,enzyme,Reactjs,Jestjs,Enzyme,我一直在这条线上遇到这个错误,我不确定是什么导致了它,因为我对开玩笑/反应/酶还很陌生 const { from } = this.props.location.state || { from: { pathname: "/" } }; 每当我尝试运行此程序时,都会发生这种情况 import React from 'react'; import { shallow, mount, render } from '../../enzyme'; import Login from '../login

我一直在这条线上遇到这个错误,我不确定是什么导致了它,因为我对开玩笑/反应/酶还很陌生

const { from } = this.props.location.state || { from: { pathname: "/" } };
每当我尝试运行此程序时,都会发生这种情况

import React from 'react';
import { shallow, mount, render } from '../../enzyme';
import Login from '../login'

describe('Login Test Suite', () => {

    it('should render the form', () => {
        const wrapper = shallow(<Login />);

        expect(wrapper.find('form').exists()).toBe(true);
        expect(wrapper.find('#userName').length).toEqual(1);
        expect(wrapper.find('#password').length).toEqual(1);
    })
})

describe('userName Test Suite', () => {

    it('should change the state of the Login component', () => {

        const wrapper = shallow(<Login />);
        wrapper.find('#userName').simulate('blur',
            {
                target: { name: 'userName', value: 'adastest' }
            });

        expect(wrapper.state('userName')).toEqual('adastest');
    })
})

describe('Password Test Suite', () => {

    it('should change the state of the Login component', () => {

        const wrapper = mount(<Login />);
        wrapper.find('#password').simulate('blur',
            {
                target: { name: 'password', value: 'adastest' }
            });

        expect(wrapper.state('password')).toEqual('adastest');
    })
})
从“React”导入React;
从“../../enzyme”导入{shall,mount,render};
从“../Login”导入登录名
描述('登录测试套件',()=>{
它('应该呈现表单',()=>{
常量包装器=浅();
expect(wrapper.find('form').exists()).toBe(true);
expect(wrapper.find('#userName').length).toEqual(1);
expect(wrapper.find(“#password”).length.toEqual(1);
})
})
描述('用户名测试套件',()=>{
它('应该更改登录组件的状态',()=>{
常量包装器=浅();
wrapper.find(“#userName”).simulate('blur',
{
目标:{name:'userName',value:'adastest'}
});
expect(wrapper.state('userName')).toEqual('adastest');
})
})
描述('密码测试套件',()=>{
它('应该更改登录组件的状态',()=>{
const wrapper=mount();
wrapper.find(“#password”).simulate('blur',
{
目标:{name:'password',value:'adastest'}
});
expect(wrapper.state('password')).toEqual('adastest');
})
})
有什么建议/文件我应该查阅吗?多谢各位

这也是我的登录组件

import TextField from "@material-ui/core/TextField";
import clsx from "clsx";
import Button from "@material-ui/core/Button";
import { createMuiTheme, makeStyles } from "@material-ui/core/styles";
import { ThemeProvider } from "@material-ui/styles";
import { red, grey } from "@material-ui/core/colors";
import Paper from "@material-ui/core/Paper";
import "../App.css";
import { Route, Redirect } from "react-router-dom";

var tokenUi = require("./../token-ui.js");
var fetchUi = require("./../fetch-ui.js");

const theme = createMuiTheme({
  palette: {
    primary: { main: red[900] },
    secondary: { main: grey[900] }
  }
});

const useStyles = makeStyles(theme => ({
  margin: {
    margin: theme.spacing(1)
  },
  textField: {
    flexBasis: 200
  }
}));

const AuthCentralState = {
  isAuthenticated: false,
  jwt: "",
  async authenticate(user, pass, callback) {
    let loginInfo = { password: pass, username: user };
    try {
      var response = await fetchUi.post(
        "/adas/user/checkuser",
        JSON.stringify(loginInfo),
        false
      );
      this.jwt = await response.json();

      if (this.jwt.tokenId) {
        this.isAuthenticated = true;
        tokenUi.updateToken(this.jwt.tokenId);
      }
    } catch (error) {
      alert(`Error occurred: ${error.message}`);
    }

    setTimeout(callback, 300);
  }
};

export class Login extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      username: "",
      password: "",
      redirectToReferrer: false,
      showPassword: false
    };
    this.login = this.login.bind(this);
  }

  onChange = prop => event => {
    this.setState({ [prop]: event.target.value });
  };

  login = event => {
    event.preventDefault();

    var username = this.state.username;
    var password = this.state.password;

    AuthCentralState.authenticate(username, password, () => {
      this.setState({
        redirectToReferrer: true
      });
    }); 
  };

  handleClickShowPassword = () => {
    this.setState({ ...this.state, showPassword: !this.state.showPassword });
  };

  render() {
    const { from } = this.props.location.state || { from: { pathname: "/" } };
    const { redirectToReferrer } = this.state;
    if (redirectToReferrer === true) {
      this.props.history.push(from.pathname);
    }
    return (
      <div>
        <Paper elevation={2} className={useStyles.root} id="container">
          <ThemeProvider theme={theme}>
            <form onSubmit={this.login}>
              <TextField
                autoFocus
                id="outlined-multiline-flexible"
                label="Username"
                fullWidth
                onChange={this.onChange("username")}
                className={clsx(useStyles.margin, useStyles.textField)}
                margin="normal"
                variant="outlined"
              />
              <br />
              <TextField
                id="outlined-adornment-password"
                fullWidth
                onChange={this.onChange("password")}
                className={clsx(useStyles.margin, useStyles.textField)}
                variant="outlined"
                type={this.state.showPassword ? "text" : "password"}
                label="Password"
                // InputProps={{
                //   endAdornment: (
                //     <InputAdornment position="end">
                //       <IconButton
                //         edge="end"
                //         aria-label="Toggle password visibility"
                //         onClick={this.handleClickShowPassword}
                //       >
                //         {this.state.showPassword ? (
                //           <VisibilityOff />
                //         ) : (
                //           <Visibility />
                //         )}
                //       </IconButton>
                //     </InputAdornment>
                //   )
                // }}
              />
              <br />
              <Button type="submit">Log in</Button>
            </form>
          </ThemeProvider>
        </Paper>
      </div>
    );
  }
}
export default Login;

export const ProtectedRoute = ({ component: Component, ...rest }) => (
  <Route
    {...rest}
    render={props =>
      tokenUi.getToken() ? (
        <Component {...props} />
      ) : (
        <Redirect
          to={{ pathname: "/login", state: { from: props.location } }}
        />
      )
    }
  />
);
从“@material ui/core/TextField”导入TextField;
从“clsx”导入clsx;
从“@物料界面/核心/按钮”导入按钮;
从“@material ui/core/styles”导入{createMuiTheme,makeStyles}”;
从“@material ui/styles”导入{ThemeProvider}”;
从“@material ui/core/colors”导入{红色,灰色}”;
从“@material ui/core/Paper”导入纸张;
导入“./App.css”;
从“react router dom”导入{Route,Redirect};
var tokenUi=require(“../../tokenUi.js”);
var fetchUi=require(“../../fetchUi.js”);
const theme=createMuiTheme({
调色板:{
主:{main:red[900]},
次要:{main:grey[900]}
}
});
const useStyles=makeStyles(主题=>({
保证金:{
页边空白:主题。间距(1)
},
文本字段:{
弹性基准:200
}
}));
常量AuthCentralState={
I认证:错误,
jwt:“,
异步身份验证(用户、传递、回调){
让loginInfo={密码:pass,用户名:user};
试一试{
var response=wait fetchUi.post(
“/adas/user/checkuser”,
JSON.stringify(loginInfo),
假的
);
this.jwt=await response.json();
if(this.jwt.tokenId){
this.isAuthenticated=true;
tokenUi.updateToken(this.jwt.tokenId);
}
}捕获(错误){
警报(`Error occurrent:${Error.message}`);
}
setTimeout(回调,300);
}
};
导出类登录扩展React.Component{
建造师(道具){
超级(道具);
此.state={
用户名:“”,
密码:“”,
转发者:错,
showPassword:false
};
this.login=this.login.bind(this);
}
onChange=prop=>event=>{
this.setState({[prop]:event.target.value});
};
登录=事件=>{
event.preventDefault();
var username=this.state.username;
var password=this.state.password;
AuthCentralState.authenticate(用户名、密码,()=>{
这是我的国家({
真的吗
});
}); 
};
handleClickShowPassword=()=>{
this.setState({…this.state,showPassword:!this.state.showPassword});
};
render(){
const{from}=this.props.location.state | |{from:{pathname:“/”};
const{redirectToReferrer}=this.state;
如果(重定向到转发程序===真){
this.props.history.push(from.pathname);
}
返回(


登录 ); } } 导出默认登录; export const ProtectedRoute=({component:component,…rest})=>( tokenUi.getToken()( ) : ( ) } /> );
您需要提供
react router
上下文才能访问
道具位置。根据
react router
文档,您可以使用
MemoryRouter
组件实现这一点

从示例中可以看出:

//app.test.js
测试(“单击筛选链接更新产品查询参数”,()=>{
让历史、地点;
渲染(
{
历史=历史;
位置=位置;
返回null;
}}
/>
,
节点
);
行动(()=>{
//示例:单击一个目标/产品?id=1234
});
//关于url的断言
expect(location.pathname).toBe(“/products”);
const searchParams=新的URLSearchParams(location.search);
expect(searchParams.has(“id”).toBe(true);
expect(searchParams.get(“id”)).toEqual(“1234”);
});

登录
是在没有任何
道具的情况下安装的
,因此我猜是错误的。
location
来自哪里?A
props
您是从父组件定义还是从父组件定义props?向您展示
登录
组件请
道具。位置
来自React路由器,但在测试中您会进行浅装载,因此不可用。您必须手动传递位置道具
// app.test.js
test("clicking filter links updates product query params", () => {
  let history, location;
  render(
    <MemoryRouter initialEntries={["/my/initial/route"]}>
      <App />
      <Route
        path="*"
        render={({ history, location }) => {
          history = history;
          location = location;
          return null;
        }}
      />
    </MemoryRouter>,
    node
  );

  act(() => {
    // example: click a <Link> to /products?id=1234
  });

  // assert about url
  expect(location.pathname).toBe("/products");
  const searchParams = new URLSearchParams(location.search);
  expect(searchParams.has("id")).toBe(true);
  expect(searchParams.get("id")).toEqual("1234");
});