Gowhich

Durban's Blog

ab post请求测试

1
ab -c 200 -n 1000 -T 'application/x-www-form-urlencoded' -p postdata.txt http://domain/test.php

以前不知道的你,很震惊

postdata.txt的内容类似如下

1
third=1&third_params={"uid":"12345","openid":"12345","name":"12345","gender":"0","iconurl":"12345"}&idfa=12345&device_imei12345&app_client_version=2.3.2.0&platform=android

这里注意下-c和-n的参数,两个参数的值是成倍数关系的,如果这样设置

1
ab -c 2 -n 4 -T 'application/x-www-form-urlencoded' -p postdata.txt http://domain/test.php

会提示参数错误

类似如下

1
ab: wrong number of arguments

-n和-c参数说明

1
2
3
-n:在测试会话中所执行的请求个数。默认时,仅执行一个请求。

-c:一次产生的请求个数。默认是一次一个。

第一步:

1
2
3
4
#Gitlab安装路径
cd /home/git/gitlab
#进入Rails控制台
sudo -u git -H bundle exec rails console production

第二步:

1
sudo gitlab-rails console

or

1
sudo gitlab-rake rails console

第三步:找到对应的用户直接修改

1
2
3
4
user = User.find_by(email: 'xx@xx')
user.password = 'secret_pass'
user.password_confirmation = 'secret_pass'
user.save

如果不知道具体的邮箱,可以通过find来查找

1
user = User.find(1)

然后重新执行如上修改密码的步骤。

今天无意间在使用sublime text 3的时候发现一个很不错的主题,超赞的。

名称是Material Theme:

安装完后,按照说明进行配置完你会发现,界面超赞的。

插件地址:https://packagecontrol.io/packages/Material%20Theme

插件主页:http://equinusocio.github.io/material-theme/

喜欢的同学可以前去参观哦。

插件一:

Advanced Cookie Manager

插件二:

AutoProxy

插件三:

Charles Proxy Auto-configuration

插件四:

Firebug

插件五:

JSON-handle

插件六:

JSONView

插件七:

User-Agent Switcher

插件八:

View Cookies

后续继续更新

使用react的情况下,一般也会用到redux,那么对于这种在react中使用redux的情况,写测试的时候就不能只用之前的文章里面介绍的方式写了。

来看下组件例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
import React, { Component, findDOMNode } from 'react'
import { connect } from 'react-redux'
import { increase, decrease, fetchItems } from '../../actions/counter'

class Home extends Component {

render() {
const {
item,
number,
isFetching,
increase,
decrease,
fetchItems
} = this.props;

return (
<div className='container'>
<div className='row'>
Some state changes:
{number}
<p>Item Length : {item.length}</p>
<p>Request Item State:{!isFetching ? '已完成' : '获取中'}</p>
</div>
<div className='btn-group'>
<button className='btn btn-primary btn-xs'
onClick={() => increase(1)}>Increase</button>
<button className='btn btn-primary btn-xs'
onClick={() => decrease(1)}>Decrease</button>
<button className='btn btn-primary btn-xs'
onClick={() => fetchItems(item)}>FetchItems</button>
</div>
</div>
)
}

componentDidMount() {
// const { item, fetchItems } = this.props;
// fetchItems(item)
}

componentDidUpdate() {
console.log('did update');
}
}

export default connect(
state => {
const { counter } = state;
return {
number: counter.number,
item: counter.item || [],
isFetching: counter.isFetching || false
}
}, {
increase,
decrease,
fetchItems
}
)(Home)

这里涉及到了actions,那么代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
import {
INCREASE,
DECREASE,
REQUEST_DATA,
FETCH_DATA,
RECEIVE_DATA,
ERROR_DATA
} from '../constants';

import Utils from 'utils';
import $ from 'jquery';

const token = Utils.get_token();

require('babel-polyfill');

export function increase(n) {
return {
type: INCREASE,
amount: n
}
}

export function decrease(n) {
return {
type: DECREASE,
amount: n
}
}

export function fetchData(item) {
return dispatch => {
return new Promise((resolve, reject) => {
let sql = `SELECT * FROM qeeniao.user limit 0,10`;
$.ajax({
url: '/proxy/admin/query',
dataType: 'json',
type: 'post',
data: {
access_token: token,
sql: sql
},
beforeSend: () => {
resolve(dispatch(requestData(item)));
},
success: (data) => {
resolve(dispatch(receiveData(item, data)));
},
error: (error) => {
console.log(error.message);
reject(dispatch(errorData(item, error)));
}
})
});

}
}

function errorData(item, error) {
return {
type: ERROR_DATA,
item: item || [],
error: error
}
}

function requestData(item) {
return {
type: REQUEST_DATA,
item: item || []
}
}

function receiveData(item, data) {
return {
type: RECEIVE_DATA,
item: (item || []).concat(data),
data: data
}
}

export function fetchItems(item = []) {
return (dispatch, getState) => {
return dispatch(fetchData(item))
}
}

这里又涉及到了contants,代码如下

1
2
3
4
5
6
export const INCREASE = 'INCREASE'
export const DECREASE = 'DECREASE'
export const FETCH_DATA = 'FETCH_DATA';
export const RECEIVE_DATA = 'RECEIVE_DATA';
export const REQUEST_DATA = 'REQUEST_DATA';
export const ERROR_DATA = 'ERROR_DATA';

到这里基本上一个简单的例子就出来了。让我们写个测试吧。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
import React from 'react';
import expect from 'expect';
import { counter } from '../../app/js/reducers';
import TestUtils from 'react-addons-test-utils';
import Home from '../../app/js/components/test/Home';
import { Provider } from 'react-redux';
import { createStore, combineReducers } from 'redux';

const reducer = combineReducers({
counter
})

const configureStore = (initialState) => {
const store = createStore(reducer, initialState);
return store;
}


describe('Home components', () => {
before('render and locate element ', function(){
const store = configureStore({});
const renderedComponent = TestUtils.renderIntoDocument(
<Provider store={store}>
<Home />
</Provider>
);

const container = TestUtils.findRenderedDOMComponentWithClass(
renderedComponent,
'container'
);

const row = TestUtils.findRenderedDOMComponentWithClass(
renderedComponent,
'row'
);

const btnGroup = TestUtils.findRenderedDOMComponentWithClass(
renderedComponent,
'btn-group'
)

this.container = container;
this.row = row;
this.btnGroup = btnGroup;
});

it('container should exist', function(){
expect(this.container).toExist();
});

it('container class name should be container', function(){
expect(this.container.getAttribute('class')).toBe('container');
});

it('row should exist', function() {
expect(this.row).toExist();
});

it('row class name should be row', function() {
expect(this.row.getAttribute('class')).toBe('row');
});

it('btnGroup should exist', function(){
expect(this.btnGroup).toExist();
})

it('btnGroup class name should be btn-group', function() {
expect(this.btnGroup.getAttribute('class')).toBe('btn-group');
});

})

我去,又牵扯到了reducers了,代码如下:

这里说下,我是把reducer做了一个目录的方式来调用。

reducers

–couner.js

–index.js

index.js的代码如下:

1
2
3
4
5
import counter from './counter'

export {
counter
} ;

counter.js的代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
import {
INCREASE,
DECREASE,
REQUEST_DATA,
FETCH_DATA,
RECEIVE_DATA,
ERROR_DATA
} from '../constants';

const initialState = {
number: 1
}

export default function counter(state = initialState, action) {
switch (action.type) {
case INCREASE:
return {
number: state.number + action.amount,
item: state.item || []
}
break;
case DECREASE:
return {
number: state.number - action.amount,
item: state.item || []
}
break;
case REQUEST_DATA:
return {
number: state.number,
isFetching: true,
item: action.item || []
}
break;
case FETCH_DATA:
return {
number: state.number,
isFetching: true,
item: action.item || []
}
break;
case ERROR_DATA:
return {
number: state.number,
isFetching: false,
item: action.item || [],
error: action.error
}
break;
case RECEIVE_DATA:
return {
number: state.number,
isFetching: false,
item: action.item || [],
data: action.data
}
break;
default:
return state;
}
}

好了,到这里,相关的一些配置可以在浏览下这篇文章:React 0.14 mocha组件单元测试(一)

先来一个简单的小组件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import React, { Component } from 'react'


class Bar extends Component {
render() {
return (
<div className='bar'>
<h5>And I am Bar!</h5>
</div>
);
}
}

export default Bar;

好了,到这里就足够了,来看看测试如何写:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import React from 'react';
import expect from 'expect';
import TestUtils from 'react-addons-test-utils';
import Bar from '../../app/js/components/test/Bar';

describe('Bar components', () => {
before('render and locate element ', function() {
const renderedComponent = TestUtils.renderIntoDocument(
<Bar/>
);

const bar = TestUtils.findRenderedDOMComponentWithClass(
renderedComponent,
'bar'
);

this.bar = bar;
});

it('bar should exist', function() {
expect(this.bar).toExist();
});

it('bar should be closed', function() {
expect(this.bar.getAttribute('class')).toBe('bar');
});
})

在运行前,做个简单的配置:

package.json

在scripts中添加

1
2
"test": "NODE_PATH=./app/js/lib ./node_modules/mocha/bin/mocha --compilers js:babel-register --recursive --require ./test/setup.js",
"test:watch": "npm test -- --watch"

添加NODE_PATH的原因是我要加载自己的lib库,不然在执行mocha的时候,会因为某些组建中调用了自己的库,而导致找不到对一个恩lib而报错。

这里还添加了一个setup.js,这个主要是为了解决类似window这类的问题,毕竟react渲染是需要dom的嘛。

setup.js代码如下:

1
2
3
4
5
import { jsdom } from 'jsdom';

global.document = jsdom('<!doctype html><html><body></body></html>')
global.window = document.defaultView
global.navigator = global.window.navigator

终于搞了一个大大的router:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
ReactDOM.render((
<Router history={browserHistory}>
<Route path='/' component={PanelContainer} onEnter={Public.reactCheckLogin}>
<IndexRoute component={DiagramListShow} />
<Route path='panel/dataShow' component={DiagramListShow}/>
<Route path='panel/diagramShow' component={DiagramShow}>
<IndexRoute component={ApiDiagram}/>
<Route path='apisum' component={ApiDiagram}/>
<Route path='opensum' component={ OpenSum }/>
<Route path='dayregister' component={ DayRegister }/>
<Route path='activitysum' component={ ActivitySum }/>
<Route path='recordsum' component={ RecordSum }/>
<Route path='startsum' component={ StartSum }/>
<Route path='downloadsum' component={ DownloadSum }/>
<Route path='recordmoney' component={ RecordMoney }/>
<Route path='recordnotice' component={ RecordNotice }/>
<Route path='recordmulticurrency' component={ RecordMultiCurrency }/>
<Route path='financeClickSum' component={ FinanceClickSum }/>
<Route path='*' component={NoMatch}/>
</Route>
</Route>
<Route path='/user' component={Container} onEnter={Public.reactCheckLogin}>
<Route path='userinfo' component={UserInfo}/>
</Route>
<Route path='/manager' component={Container} onEnter={Public.reactCheckLogin}>
<Route path='taskManage' component={TaskManage}/>
<Route path='sqlManage' component={SqlManage}/>
<Route path='oauthManage' component={oauthManage} />
</Route>
<Route path='/operation' component={Container} onEnter={Public.reactCheckLogin}>
<Route path='typeinfo' component={Typeinfo}/>
<Route path='listinfo' component={Listinfo}/>
<Route path='financeNotice' component={FinanceNotice}/>
</Route>
<Route path='/panel/login' component={LoginRender} />
</Router>
), document.getElementById('main-content'));

整理完后,感觉整个网站清晰多了.

这里有几个两点,一点是终于有办法可以判断进入某个路由的时候是否登录了。第二个两点是,之前的版本是在url后面添加hash,看起来很乱,这个版本的更新,从url上你完全看不出这个是用hash实现的。

日期转时间戳

1
2
3
var date = '2016/05/09';
date = [date, '00:00:00'].join(' ')
date = Math.round(new Date(date).valueOf() / 1000);

//以上是我觉得兼容性比较强的

时间戳转日期

1
new Date().toLocaleString()

结合上几篇博文,今天学习下,如果来优化一下我们的状态指示器。

前面几篇文章,在renderLoadingView方法中,直接使用了一个View,然后加了一个简单的字符串进行提示,看起来还是简陋的很。

修改之后,这个方法的代码如下:

1
2
3
4
5
renderLoadingView:function(){
return (
<LoadingView />
);
},

这个LoadingView方法,是根据官方教程进行改写的,其中有个小提示,里面用到了react-timer-mixin这个库,还是提交安装比较好,不然后面还要安装之后再重新启动。

重要的问题就上面这些了,下面把LoadingView整个代码贴到下面:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
'use strict';

var React = require('react-native');

var {
ActivityIndicatorIOS,
StyleSheet,
View
} = React;

var TimerMixin = require('react-timer-mixin');

var LoadingView = React.createClass({
mixins:[TimerMixin],
getInitialState:function(){
return {
animating:true
}
},
setToggleTimeout:function(){
this.setTimeout(
() => {
this.setState({animating:!this.state.animating});
this.setToggleTimeout();
},
1200
);
},
componentDidMount:function(){
this.setToggleTimeout();
},
render:function(){
return (
<View>
<ActivityIndicatorIOS
animating={this.state.animating}
style={[styles.centering, {height:80}]}
size="large" />
</View>
);
}
});

var styles = StyleSheet.create({
centering: {
alignItems: 'center',
justifyContent: 'center',
marginTop:65
}
});

module.exports = LoadingView;

好了,样式啥的我都调整好了,你就贴进去,在根据自己的需求修改下吧。

上一篇文章中我们了解了一下,简单的添加列表,但是列表添加了,点击列表没有什么反应啊,这不就没啥作用了,这里记录下如何实现点击的效果。

React Native中有个组件叫做TouchableHighlight,一看这名字就知道干啥的了,然后我们在这上面加一个touch的事件。

这里我把上一节的renderMovie的方法代码列到这里:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
renderMovie:function(movie){
return (
<TouchableHighlight onPress={() => this._pressRow(movie.id)}>
<View style={styles.container} key={movie.id}>
<Image
source={{uri:movie.posters.thumbnail}}
style={styles.thumbnail} />
<View style={styles.rightContainer}>
<Text style={styles.title} numberOfLines={1}>
{movie.title}
</Text>
<Text style={styles.year}>
{movie.year}
</Text>
</View>
<View style={styles.separator}></View>
</View>
</TouchableHighlight>
);
},

然后我们给这个onPress添加一个指定的事件函数

1
2
3
4
5
6
_pressRow:function(rowID: number){
this.props.navigator.push({
title:'详情',
component:MovieView
})
}

这里的组件MovieView,我加在了另外一个文件里面,代码如下:【自己可以进行引入】

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
'use strict';

var React = require('react-native');

var {
StyleSheet,
Text,
View
} = React;

var MovieView = React.createClass({
render:function(){
return (
<View style={styles.container}>
<Text>Movie View</Text>
</View>
);
}
});

var styles = StyleSheet.create({
container:{
flex:1,
backgroundColor:'#fff'
}
});

module.exports = MovieView;

好了,运行吧,可以跳转了,还是蛮简单的。

添加了这些,还有个问题,就是要如何才能修改按下去时背景色的颜色,其实只要加入这行代码就好了:

underlayColor='rgba(24,36,35,0.1)'

最终我的renderMovie代码就成了如下的样子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
renderMovie:function(movie){
return (
<TouchableHighlight underlayColor='rgba(24,36,35,0.1)' onPress={() => this._pressRow(movie.id)}>
<View style={styles.container} key={movie.id}>
<Image
source={{uri:movie.posters.thumbnail}}
style={styles.thumbnail} />
<View style={styles.rightContainer}>
<Text style={styles.title} numberOfLines={1}>
{movie.title}
</Text>
<Text style={styles.year}>
{movie.year}
</Text>
</View>
<View style={styles.separator}></View>
</View>
</TouchableHighlight>
);
},
0%