2024年8月21日 星期三
2024年3月22日 星期五
[jest]Software Testing
此篇要教各位如何去撰寫前端的測試流程,測試在CI/CD流程內是不可或缺的一環
例如下方的pipeline圖
- jest-environment-jsdom:一般也指測試的執行環境(environment),目的是模擬瀏覽器的行為、API,讓開發者能在 Node.js 的環境下模擬瀏覽器的操作
- jest:是一個用來執行 JavaScript 測試的框架(JavaScript Test Framework),又稱 test runner,它讓開發者能夠執行測試、撰寫斷言,提供的 API 像是 expect、describe、test、toBe、toEqual 等等。其他這類的 JS testing frameworks 如 Vitest、mochajs、Jasmine 等等
- @testing-library/jest-dom:原本 Jest 就有提供許多不同的 matchers(例如,toBe()、toEqual()等等),@testing-library/jest-dom則是擴充了更多可以在 Jest 中使用的 matchers,讓開發者可以使用toBeInTheDocument()` 等這類和 DOM 有關的 matchers。
- @testing-library/react:基於 @testing-library/dom,它讓開發者可以把 React 元件 render 到 DOM 上,像是 render、screen、rerender、unmount 等等。不需要搭配 Jest 才能使用。其他這類的工具如 Enzyme。
- @testing-library/user-event:模擬使用者的操作來測試使用者與 UI 的互動。相較於 @testing-library/dom 中的 fireEvent 更能模擬使用者的行為。
- @babel/plugin-transform-modules-commonjs:ESM轉譯成CJS的套件,如果有使用到第三方套件則必須載入。
2024年2月21日 星期三
[Gitlab]CI-流程建置
CI(Continuous Integration)是Devops的軟體開發流程,主要針對程式碼變更後的自動建置和測試之後,定期將變更合併置主要Repository,CI的關鍵目標是能更快發現和解決問題、改善軟體品質還有減少驗證和釋出軟體更新所需的時間。
此篇文章將教你如何建立Gitlab CI流程
環境說明:
- Gitlab (self-managed)
- Gitlab Runner
- Docker (windows)
首先在Docker上安裝Gitlab-Runner,輸入以下指令安裝Docker
2024年2月16日 星期五
[React]環境部署
|
項次 |
部署位置 |
地端/雲端 |
部署方式 |
說明 |
|
1 |
Windows
Server(iis) |
地 |
手動 |
本地VM,測試環境或者待管Server時會使用 |
|
2 |
Azure |
雲 |
手動 |
透過VS Code或FTP去部署 |
|
3 |
Azure |
雲 |
自動 |
透過CI/CD方式去部署 |
|
|
|
|
|
|
- Window Server(iis)
1.先設定iis站台的URL Rewrite (SPA網站都可參考此設定)
//Web.config
更多資訊可參考保哥此篇文章說明
2.使用Terminal執行語法npm run build,以產生實體檔(檔案預設放在dist
3.手動搬移檔案至Server
- Azure (手動上傳)
- VS Code部署
- VS Code部署
- 安裝Azure App Service Extension
- 使用語法npm run build產生實體檔(檔案預設放在dist資料夾)
- 使用滑鼠右鍵對選dist資料夾中的Deploy to Web App…
- 點選Sign in Azure
- 選擇登入Azure帳號
- 登入成功後選擇建立Azure App Service段落所建立App Service
- 點選Deploy以部署到 App Service
- 佈署成功可點選右下角的Browse WebSite查看(須過一段時間才能正常顯示)
- FTP部署
- FTP部署
- 使用語法npm run build產生實體檔(檔案預設放在dist資料夾)
- 從Azure的App Service頁面點選左側的Deployment center。
- 點選FTP credentials就可以看到FTP的連線設定
- 使用FTP工具(例.Filezilla)連線並將欲部署的檔案更新上去。
- Azure (CI/CD)
因CI/CD會被SourceControl的不同影響,待有實作時再回補說明
[React] 開發環境建立
依據以下步驟建立React開發環境,本篇文章使用的是vite來建立專案,而不是之前介紹的CRA。
先新建一個專案資料夾,並透過VS Code開啟
npm create vite@latest
npm install
安裝完成後,再輸入以下指令就可以偵錯網站
npm run dev
2023年1月30日 星期一
[React]實作自定義的Component(密碼輸入框)
這篇主要說明如何開發自定義的Component,讓多個頁面都可同時使用,範例程式碼可參考這裡。
最常見的自定義元件大概就是輸入框、讀取動畫或視窗...這類的。因為之前有實作過登入頁,那就以密碼輸入框來教大家如何實作。
因示範專案使用的是MUI,建議各位在開發任何元件前都可確認有沒有官方範例,有的話就可以省下大量的時間去實作。
而這功能確實有找到官方範例,那就可以直接抓程式碼下來使用!
首先先在src/components/text裡新增Password.tsx,並將官方範例貼上去。
Password.tsx
----------------------------------------------------------------------------------------------------------------
import { IconButton, InputAdornment, TextField } from "@mui/material";
import { useState } from "react";
export default function Password(props: passwordProps) {
//顯示密碼Flag
const [showPassword, setShowPassword] = useState(false);
//眼睛點選事件
const handleClickShowPassword = () => setShowPassword((show) => !show);
//取消預設點選事件
const handleMouseDownPassword = (event: React.MouseEvent<HTMLButtonElement>) => {
event.preventDefault();
};
return (
<TextField
error={props.ErrorMsg !== ''}
margin="normal"
required
fullWidth
name={props.Name}//"password"
label={props.Label}//"Password"
id={props.ID}//"password"
autoComplete="current-password"
helperText={props.ErrorMsg !== '' ? props.ErrorMsg : ""}
InputProps={{
type: showPassword ? 'text' : 'password',
endAdornment:
<InputAdornment position="end" >
<IconButton
aria-label="toggle password visibility"
onClick={handleClickShowPassword}
onMouseDown={handleMouseDownPassword}
edge="end"
>
{showPassword ? <VisibilityOff /> : <Visibility />}
</IconButton>
</InputAdornment>
}
}
/>
);
}
interface passwordProps {
ID:string
Name: string
Label:string
ErrorMsg?: string;
}
----------------------------------------------------------------------------------------------------------------
主要定義幾個屬性
ID:元件ID。Name:元件Name,有包Form的可透過FormData.get({name})抓到值。Label:元件Label。ErrorMsg:顯示錯誤訊息。
再將Login的原先密碼欄位改成使用<Password/>
Login.tsx
----------------------------------------------------------------------------------------------------------------
import Password from '../components/text/Password';
省略...
{/*<TextField*/}
{/* error={LoginMsg !== ''}*/}
{/* margin="normal"*/}
{/* required*/}
{/* fullWidth*/}
{/* name="password"*/}
{/* label={t('Login.Password')}*/}
{/* type="password"*/}
{/* id="password"*/}
{/* autoComplete="current-password"*/}
{/* helperText={LoginMsg !== '' ? LoginMsg:""}*/}
{/*/>*/}
<Password ID="password" Name="password" Label={t('Login.Password')} ErrorMsg={LoginMsg} />
省略...
----------------------------------------------------------------------------------------------------------------
2023年1月4日 星期三
[React]Redux說明&導入
此篇主要說明Redux,範例程式碼可參考這裡。
基本上Redux的功用就是管理狀態以及畫面與資料分開管理,與之前加上登入機制所使用的Context功能大同小異,優於Context的地方是在有多個state要管理時會優於React原生的Context。
p.s. Redux和Context API可混用,需頻繁操作的state可以用Redux,反之則用Context。
主要核心元件為redux和react-redux,redux是函式庫本體,react-redux則是React綁定使用套件。
Redux組成主要有三個元素
1.action
定義操作state方法,會傳入reducer。
2.reducer
保管State並針對傳入的action去對state做動作。
3.store
接下來我們就直接用計數器範例來說明各元素的功能以及作用
開始前先用npm安裝套件語法如下
//安裝redux和react-redux
npm install --save redux react-redux
//安裝react-redux的Typescript套件
npm install --save --dev @types/react-redux
程式碼部分我們先建立reducer部分,新增reducers資料夾和counter檔案(src/reducers/counter.ts)
counter.ts裡面有Action類別定義、Actions和reducer本體,程式碼如下
counter.ts
-------------------------------------------------------------------------------------------------------------------
const initState = {
count: 0
};
// #region 定義Action類別
//增加
const INCREMENT = 'INCREMENT';
//減少
const DECREMENT = 'DECREMENT';
// #endregion
// #region 匯出Action
//增加
export const countPlus = { type: INCREMENT }
//減少
export const countMinus = { type: DECREMENT }
// #endregion
// #region reducer本體
const counterReducer = (state = initState, action: any) => {
//依照action的類型作判斷
switch (action.type) {
case INCREMENT:
return { count: state.count + 1 };
case DECREMENT:
return { count: state.count - 1 };
default:
return state;
}
};
// #endergion
export default counterReducer;
-------------------------------------------------------------------------------------------------------------------
簡單說明,定義以及匯出Action主要是集中管理狀態的處理,對後續維護以及測試有許多幫助。
而reducer本體則是依照傳入的action去操作state並回傳最新的State,使之更新view。
再來建立集中管理reducer的store,新增stores資料夾和configureStore檔案(src/stores/configureStore.ts)
configureStore.ts
-------------------------------------------------------------------------------------------------------------------
import counterReducer from '../reducers/counter';
const rootReducer = combineReducers({
counterReducer,
});
const store = createStore(rootReducer);
export default store;
-------------------------------------------------------------------------------------------------------------------
如果只有一個reducer的可以直接createStore,但預想未來專案可能會有多個reducer,所以中間多一個combineReducers,有新增時在填入。
再來要修改index.tsx讓所有Component可以使用,需要使用react-redux的Provider來包覆住APP。
//index.tsx(紅字部分為新增)
-------------------------------------------------------------------------------------------------------------------
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import './i18n';
import { Provider } from 'react-redux';
import store from './stores/configureStore';
const root = ReactDOM.createRoot(
document.getElementById('root') as HTMLElement
);
root.render(
<React.StrictMode>
<Provider store={store}>
<App />
</Provider>
</React.StrictMode>
);
// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals(console.log);
-------------------------------------------------------------------------------------------------------------------
透過Provider的store讓全系統都可使用Redux。
前面這樣定義就完成了,開始撰寫計數器,新增counter.tsx頁面
//counter.tsx
-------------------------------------------------------------------------------------------------------------------
import { useSelector,useDispatch } from 'react-redux';
import { countMinus, countPlus } from '../reducers/counter';
export function Counter() {
//透過useDispatch來使用action
const dispatch = useDispatch();
//透過useSelector去取得State
const count: number = useSelector(state => (state as any).counterReducer.count);
return (
<React.Fragment>
<h1>Counter</h1>
<h3> Redux</h3>
<p aria-live="polite">Current count: <strong>{count}</strong></p>
<button style={{ margin: '5px' }}
type="button"
className="btn btn-primary btn-lg"
onClick={() => { dispatch(countPlus); }}>
Increment
</button>
<button style={{ margin: '5px' }}
type="button"
className="btn btn-primary btn-lg"
onClick={() => { dispatch(countMinus); }}>
Decrement
</button>
</React.Fragment>
);
}
-------------------------------------------------------------------------------------------------------------------
說明一下程式碼,
操作部分使用useDispatch來取得dispatch,在按鈕時在使用dispatch傳入定義好的action操作state。
而畫面更新部分則是使用useSelector取代useState,如果state更新則有使用到useSelector的Component也會更新。
實際就可以來Run看看畫面,看起來是可以正常加減。
p.s.因為是全域的state,所以要注意如果切頁再回來值不會變回0。
這樣就完成了資料與畫面分離的部分,對於未來的維護上應該也就簡單許多,建議大家還是要使用Redux。
一樣在結尾也說明一下其實還有更多的應用沒有一起說明,例如操作資料庫或是用redux-logger來記錄每次操作state...等等,後續如果有用到時再分享給大家,謝謝!
[ASP.Net]GridView自訂分頁處理
ASP.Net的GirdView在每次分頁處理時都會在重新抓取所有資料在進行分頁,遇到大量資料或是設計不良的情況下,在切換時頁面會卡住,造成使用者體驗不佳。 可以透過GirdView內建的參數以及SQL OFFSET語法來替代原先的分頁處理,以下為步驟說明 1.asp...


