2020年2月28日 星期五

[Git] 客製化Git Hooks管理專案



所謂的Git Hooks指的是以腳本等方式設定執行動作,當git指令執行時,會自動觸發這些腳本的機制,時機可能是在git指令之前或是之後。根據git指令又可以分為server side和client slide兩大類:commit和merge觸發的是client side,server side則是在網路上收到推上去的commit(pushed commit)。

hooks 存放在目錄 /.git/hooks/ 裡,執行 git init 初始化時,git自動在目錄裡產生一些shell script範本,你也可以用Ruby或python等實作。範例可以直接使用,但要記得把副檔名的 .sample 拿掉。



如果需要自己新建hook,請確認一下檔案的執行權限已經開啟。
chmod +x [filePath]

常見的Hooks
Client Side
以下為4個與commit相關的hook:
  • 在commit之前會執行 pre-commit,可以運用在執行功能驗證、單元測試或是coding style的檢查,若有任何錯誤發生時,這個commit會被捨棄不執行。也可以下指令 git commit --no-verify 略過hook的執行。
  • prepare-commit-msg,這個hook對於一般commit比較不實用,多在commit訊息模板、merge commit、squash commits、amended commits等自動產生commit訊息的類型。
  • commit-msg需要commit訊息的模板檔案路徑作為參數,如果錯誤發生,git會停止進行這個commit,來確保所有的commit訊息都符合需要的格式。
  • commit結束後,會執行 post-commit ,不需要任何參數,你可以用 git log -1 HEAD 來確認剛剛commit的內容,或者通知其他類似的訊息。


Server Side
  • server端收到push時,首先執行 pre-receive ,如果錯誤發生,所有的commit將不會被受理。可以用來檢查有沒有任何non-fast-forwards。
  • updatepre-receive 有點相似。
  • post-receive 會在整個push過程結束後執行,可以用來通知專案成員或是其他服務的更新等。這個hook不會影響push過程,但在hook執行完之前,用戶端和伺服器端會持續連線,因此要避免在腳本中執行時間較長的工作。

參考資料:
https://git-scm.com/book/zh-tw/v2/Customizing-Git-Git-Hooks

2020年2月27日 星期四

[JavaScript] 安裝Jest單元測試


Jest為javascript的前端測試工具,安裝快速且不需複雜的設定,執行速度快,所有測項可以同時進行讓效能最大化,並可以搭配用在vue.js、React等進行UI component的測試。

安裝指令

npm install --save-dev jest
npm的安裝教學可以參考「教學」。


實作

以簡單的JavaScript程式來測試執行,將傳入的陣列合併成字串回傳:
// combineArray.js
const combineArray = (array, separator = ',') => {
        if (!Array.isArray(array)) {
                return new Error('Invalid input array');
        }
        if (typeof separator !== 'string') {
                return new Error('Invalid input separator');
        }

        return array.join(separator)
};

module.exports = combineArray;
測試的副檔名 *.test.js
// combineArray.test.js
const combineArray = require('./combineArray');

test('Combine array: [1, 2]', () => {
        expect(combineArray([1,2])).toBe('1,2');
});
  • 每個一測試檔案都必須至少有一個test(),第一個參數是名稱;第二個參數是執行expect實際測試內容的function。[參考]
  • expect()常用的方式expect(A).toBe(B),也就是期望A會等於B。[參考]

package.json新增內容:
{
  "scripts": {
    "test": "jest"
  }
}

執行測試

npm run test
所有的*.test.js都會跑過一次,並顯示結果,包含有多少測試項目以及成功和失敗的數量。



根據程式碼的複雜程度可以寫出許多測試項目,為了方便辨識測試內容,describe()可以將測項分類、建立群組,.toThrow()判斷錯誤發生的情形等等:
const combineArray = require('./combineArray');

describe('Combine array', () => {
        const inputArray = [1, 2];

        test('Default', () => {
                expect(combineArray([])).toBe('');
                expect(combineArray(inputArray)).toBe('1,2');
                expect(combineArray(inputArray, undefined)).toBe('1,2');
        });
        test('With custom separator', () => {
                expect(combineArray(inputArray, ' ')).toBe('1 2');
                expect(combineArray(inputArray, '*')).toBe('1*2');
                expect(combineArray(inputArray, '-')).toBe('1-2');
        });

        describe('With invalid input', () => {
                test.each(
                        [undefined, null, 1, 'string', {}]
                )('Input array: %s', (item) => {
                        expect(() => {
                                combineArray(item);
                        }).toThrow(new Error('Invalid input array'));
                });
                test.each(
                        [null, 1, [], {}]
                )('Input separator: %s', (item) => {
                        expect(() => {
                                combineArray(inputArray, item);
                        }).toThrow(new Error('Invalid input separator'));
                });
        });
});
執行結果


若有任何不符合預期的結果,console上會寫出哪一個測項失敗,預期的輸出和輸入為何。測試項目寫得越完備越好,往後即使更新主程式,也可以再以指令執行測試,確保程式運作如你所預期。

更多的方法可以前往官網參考:
https://jestjs.io/

2020年2月25日 星期二

[Vue.js] computed的set()和get()使用方式


一般情況下,為了避免在 template 中寫入過度複雜的計算,可以選擇放到 computed
<template>
    <div v-text="colorList.join(',')" />
</template>

<script>
export default {
    data() {
        return {
            colorList: ['blue', 'green', 'red'],
        };
    },
};
</script>
// Recommend
<template>
    <div v-text="displayColorList" />
</template>

<script>
export default {
    data() {
        return {
            colorList: ['blue', 'green', 'red'],
        };
    },
    computed: {
        displayColorList() {
            return this.colorList.join(',');
        },
    },
};
</script>
預設的情況下computed只有 get() ,必須要有一個return值,會根據相依的值動態計算。computeddata很像,但如果要直接修改值,必須加上 set()

如果firstNamelastName被修改時,會觸發 set() ,第一個參數為這次觸發更新輸入的值。參考範例:
<template>
    <div>
        <input v-model="firstName">
        <input v-model="lastName">
        <input v-text="fullName" />
    </div>
</template>

<script>
export default {
    data() {
        return {
            fullName: '',
        };
    },
    computed: {
        firstName: {
            get() {
                return this.fullName.split(' ')[0] || '';
            },
            set(firstName) {
                this.fullName = `${firstName} ${this.lastName}`;
            },
        },
        lastName: {
            get() {
                return this.fullName.split(' ')[1] || '';
            },
            set(lastName) {
                this.fullName = `${this.firstName} ${lastName}`;
            },
        },
    },
};
</script>