Code Management

Virtual & Reality

Version Control

Local git

mkdir workshop

cd workshop

git init

vi README.md

git add .

git commit -am "First commit"

20,0000,0000

Mutiple Projects

All in one

和Windows的基础代码很像,Google的20亿行代码是用来驱动整个Google服务的,他们是一个整体!

All in two indeed

DataEye/static

DataEye/dejs

{
  "name": "dejs",
  "version": "0.5.21",
  "description": "A collection of utility libraries used by other DataEye JS projects.",
  "repository": {
    "type": "git",
    "url": "git+https://github.com/DataEye/dejs.git"
  },
  "keywords": [
    "dataeye",
    "utils",
    "react"
  ],
  "scripts": {
    "build": "gulp",
    "cover": "cat coverage/*/lcov.info | codecov",
    "prepare": "npm install react lodash highcharts superagent superagent-mocker numeral immutable",
    "test": "karma start karma.conf.js"
  },
  "files": [
    "lib"
  ],
  "author": "simonxu",
  "license": "BSD-3-Clause",
  "installable": true,
  "peerDependencies": {
    "react": ">=0.14.0",
    "lodash": "4.x",
    "superagent": "*",
    "superagent-mocker": "*",
    "numeral": "*",
    "immutable": "*"
  },
  "devDependencies": {
    "babel-core": "6.x.x",
    "babel-eslint": "^4.1.6",
    "babel-preset-es2015": "^6.3.13",
    "babel-preset-react": "^6.3.13",
    "babelify": "^7.2.0",
    "browserify": "^13.0.0",
    "browserify-istanbul": "^0.2.1",
    "codecov.io": "0.1.6",
    "eslint": "^1.9.0",
    "eslint-config-airbnb": "^1.0.0",
    "eslint-plugin-react": "^3.8.0",
    "gulp": "^3.9.0",
    "gulp-babel": "^6.1.1",
    "isparta": "^4.0.0",
    "jasmine-core": "^2.4.1",
    "karma": "^0.13.19",
    "karma-browserify": "^4.4.2",
    "karma-chrome-launcher": "^0.2.2",
    "karma-coverage": "^0.5.3",
    "karma-jasmine": "^0.3.6",
    "react-addons-test-utils": "0.14.x"
  },
  "bugs": {
    "url": "https://github.com/DataEye/dejs/issues"
  },
  "homepage": "https://github.com/DataEye/dejs#readme",
  "main": "index.js",
  "prepublish": [
    "build"
  ]
}

npm

Branches

We love it

Under Control

// 查看当前分支比master多了哪些提交
git log master..

// 查看master分支比当前分支多了哪些提交
git log ..master


// diff两个分支
git diff develop feature_sort
// 合并分支
git merge feature_xx

// 执行rebase
git pull --rebase origin develop

SVN 

Workflow

stable & temporary

Stable branches

develop & master

Temporary branches

feature & bug & release

Code Review

发现BUG,提高代码质量,促进知识共享,提升团队技术水平

Commits

Review

Continuous Integration

提高软件质量,降低项目风险

Unit Test

import ajax, {get, post, ajaxSetup, FORM_TYPE, TEXT_TYPE} from '../../src/ajax'

const TIMEOUT = 20

let globalErrorTriggered = false
let globalCompeteTriggered = false
let globalSuccessTriggered = false

ajaxSetup({
  contextPath: '/testing',
  timeout: TIMEOUT,
  ajaxComplete: function(err, res) {
    globalCompeteTriggered = true
  },
  ajaxError: function(err, res) {
    globalErrorTriggered = true
  },
  ajaxSuccess: function(result, res) {
    globalSuccessTriggered = true
  }
})

describe('lib/ajax', () => {
  beforeEach(() => {
    globalErrorTriggered = false
    globalCompeteTriggered = false
    globalSuccessTriggered = false
    jasmine.Ajax.install()
    jasmine.clock().install()
  })

  afterEach(() => {
    jasmine.Ajax.uninstall()
    jasmine.clock().uninstall()
  })

  it('ajaxError should work', (done) => {
    let json = {
      statusCode: 410
    }
    jasmine.Ajax.stubRequest(/\/testing.+/).andReturn({
      'status': 410,
      'responseText': JSON.stringify(json),
      'responseHeaders': {
        'Content-Type': 'application/json'
      }
    })

    ajax({
      url: '/',
      global: true,
      error: () => {
        expect(globalErrorTriggered).toBe(true)
      },
      complete: () => {
        expect(globalErrorTriggered).toBe(true)
        done()
      }
    })
  })

  it('get method should get plain text', (done) => {
    jasmine.Ajax.stubRequest(/\/testing.+/).andReturn({
      'status': 200,
      'responseText': 'success',
      'responseHeaders': {
        'Content-Type': 'text/plain'
      }
    })

    get('/', function(result) {
      expect(result).toBe('success')
      done()
    })
  })

  it('post method should get serialized content', (done) => {
    let json = {
      statusCode: 200
    }
    jasmine.Ajax.stubRequest(/\/testing.+/).andReturn({
      'status': 200,
      'responseText': JSON.stringify(json),
      'responseHeaders': {
        'Content-Type': 'application/json'
      }
    })

    post('/', {formData: 1}, function(body, res) {
      expect(body.statusCode).toBe(json.statusCode)
      done()
    })
  })

  it('ajax post with form data should work', (done) => {
    let json = {
      statusCode: 200
    }
    jasmine.Ajax.stubRequest(/\/testing.+/).andReturn({
      'status': 200,
      'responseText': JSON.stringify(json),
      'responseHeaders': {
        'Content-Type': 'application/json'
      }
    })

    post('/', {form: 1}, function(body, res) {
      expect(body.statusCode).toBe(json.statusCode)
      expect(res.req._data).not.toBeNull()
      done()
    })
  })

  it('ajax post without form data also work', (done) => {
    let json = {
      statusCode: 200
    }
    jasmine.Ajax.stubRequest(/\/testing.+/).andReturn({
      'status': 200,
      'responseText': JSON.stringify(json),
      'responseHeaders': {
        'Content-Type': 'application/json'
      }
    })

    post('/', function(body, res) {
      expect(body.statusCode).toBe(json.statusCode)
      expect(res.req._data).toBeFalsy()
      done()
    })
  })

  it('shoud return a promise if no hanlder supplied', (done) => {
    let json = {
      statusCode: 200
    }
    jasmine.Ajax.stubRequest(/\/testing.+/).andReturn({
      'status': 200,
      'responseText': JSON.stringify(json),
      'responseHeaders': {
        'Content-Type': 'application/json'
      }
    })
    let request = ajax({
      url: '/',
      global: false,
      data: {withDataField: 1}
    })
    expect(typeof request.then).toBe('function')
    request.then(function(res) {
      expect(res.body.statusCode).toBe(json.statusCode)
      done()
    })
  })

  it('complete handler should work', (done) => {
    let json = {
      statusCode: 200
    }
    jasmine.Ajax.stubRequest(/\/testing.+/).andReturn({
      'status': 200,
      'responseText': JSON.stringify(json),
      'responseHeaders': {
        'Content-Type': 'application/json'
      }
    })

    let i = 0

    ajax({
      url: '/',
      method: 'get',
      global: true,
      headers: {
        'Content-Type': TEXT_TYPE
      },
      body: {withBodyField: 1},
      success: () => {
        i += 1
        expect(globalSuccessTriggered).toBe(true)
      },
      complete: () => {
        expect(i).toBe(1)
        done()
      }
    })
  })

  it('error handler should work', (done) => {
    let json = {
      statusCode: 404
    }
    jasmine.Ajax.stubRequest(/\/testing.+/).andReturn({
      'status': 404,
      'responseText': JSON.stringify(json),
      'responseHeaders': {
        'Content-Type': 'application/json'
      }
    })

    let i = 0

    ajax({
      url: '/',
      global: false,
      headers: {
        'Content-Type': FORM_TYPE
      },
      method: 'post',
      error: () => {
        i += 1
      },
      complete: () => {
        expect(i).toBe(1)
        done()
      }
    })
  })

  it('fail handler should work', (done) => {
    let json = {
      statusCode: 404
    }
    jasmine.Ajax.stubRequest(/\/testing.+/).andReturn({
      'status': 404,
      'responseText': JSON.stringify(json),
      'responseHeaders': {
        'Content-Type': 'application/json'
      }
    })

    let i = 0

    ajax({
      url: '/',
      global: false,
      headers: {
        'Content-Type': FORM_TYPE
      },
      method: 'post',
      fail: () => {
        i += 1
      },
      complete: () => {
        expect(i).toBe(1)
        done()
      }
    })
  })

  it('should support custom timeout', () => {
    let result = null
    let req = ajax({
      url: '/',
      global: false,
      complete: () => {}
    })

    jasmine.Ajax.requests.mostRecent().responseTimeout()
    jasmine.clock().tick(TIMEOUT)
    expect(req.timedout).toBe(true)
  })

  // TODO withCredentials
})

隔离程序最小单元对其进行正确性测试,避免意外惊喜

Coverage

没有明显错误的代码

明显没有错误的代码

vs

Best Practice - Commits

  • 尽早提交
  • 尽快提交
  • 经常提交

Avoid

Best Practice - Be social

GitHub
UpSource

npm

codecov
Semaphoreci

The End

Code Management

By simon xu

Code Management

how we do front end code management in DataEye

  • 1,327