一、例子
utils/cal.js
export const sum = (x, y) => {
return x + y
}
export const subtract = (x, y) => {
return x - y
}
import { sum } from '@/utils/cal'
const result = 3
const actual = sum(1, 2)
if (actual === result) {
console.log('测试通过')
} else {
console.log(`测试失败:1 + 2 期望得到${result},但是得到了${actual}`)
}
断言函数封装
import { sum, subtract } from '@/utils/cal'
expect(sum(1,2)).toBe(3) // 断言
function expect(actual) {
return {
toBe(result) {
if (actual !== result) {
console.log('测试失败')
} else {
console.log('测试通过')
}
}
}
}
import { sum, subtract } from '@/utils/cal'
test('sum', () => { // 测试用例
expect(sum(1, 2)).toBe(3) // 断言
})
test('subtract', () => { // 测试用例
// toBe 相等匹配
expect(subtract(2, 1)).toBe(1) // 断言
})
function test(message, callback) {
try {
callback()
} catch (e) {
console.log(`${message}:${err.message}`)
}
}
function expect(actual) {
return {
toBe(result) {
if (actual !== result) {
console.log('测试失败')
} else {
console.log('测试通过')
}
}
}
}
二、Jest
官网:https://jestjs.io
vue test util
https://vue-test-utils.vuejs.org/zh/guides/#%E5%B8%B8%E7%94%A8%E6%8A%80%E5%B7%A7
1、测试标题
test('标题的内容是 todos', () => {
const wrapper = mount(TodoApp)
// console.log(wrapper.find('h1').text())
expect(wrapper.find('h1').text()).toBe('todos')
})
2、测试列表长度
test('任务列表展示正常', async () => {
// 给定一份测试数据
const wrapper = shallowMount(TodoApp)
// 准备测试数据
wrapper.vm.todos = [
{ id: 1, text: 'eat', done: true },
{ id: 2, text: 'sleep', done: true },
{ id: 3, text: 'play', done: false }
]
// 等待视图更新
await Vue.nextTick()
// 测试:期望页面中的 dom 元素和数据是匹配的
// console.log(wrapper.findAll('[data-testid="todo-item"]').length)
// console.log(wrapper.vm.todos.length)
expect(wrapper.findAll('[data-testid="todo-item"]').length).toBe(wrapper.vm.todos.length)
})
3、添加任务
test('添加任务正常', async () => {
// 给定一份测试数据
const text = 'hello'
const wrapper = shallowMount(TodoApp)
const input = wrapper.find('[data-testid="new-todo"]')
// 给 input 输入内容
await input.setValue(text) // 内部集成了 nextTick
// 触发回车事件
await input.trigger('keyup.enter') // 等待事件触发,视图数据更新
// 断言:列表中多出了一个内容为 hello 的 todo-item
const list = wrapper.findAll('[data-testid="todo-text"]')
const last = list.at(list.length - 1)
expect(last.text()).toBe(text)
// console.log(input.element.value)
expect(input.element.value).toBe('')
// expect(input)
})
test('标题的内容是 todos', () => {
const wrapper = mount(TodoApp)
// console.log(wrapper.find('h1').text())
expect(wrapper.find('h1').text()).toBe('todos')
})
三、cors解决跨域
const express = require('express')
const cors = require('cors')
const app = express()
const port = 3000
// 服务端设置:开启 CORS 跨域资源请求处理,客户端什么也不用做
// app.use(cors({
// // origin: '*',
// origin: 'http://localhost:5501'
// }))
app.get('/', (req, res) => {
// 设置 HTTP 响应头
// res.setHeader('Access-Control-Allow-Origin', '*')
res.send('Hello World!')
})
app.listen(port, () => {
console.log(`Example app listening at http://localhost:${port}`)
})
四、中间层
和客户端一样,服务器也可以发请求,因为不是浏览器,所以服务端的请求没有同源策略限制
http.request('http://localhost:3000', res => {
let data = []
res.on('data', (chunk) => {
data.push(chunk)
})
res.on('end', () => {
console.log(data.toString())
})
})
五、TS
import request from '@/utils/request'
interface User {
email: string
password: string
}
export interface UserRes {
email: string
token: string
username: string
bio: string
image: string
}
interface LoginInput {
user: User
}
interface LoginData {
user: UserRes
}
export const login = (data: LoginInput) => {
// axios 中的 get、post、delete、put 等请求方法都支持通过泛型指定返回的 data 类型
return request.post<LoginData>('/api/users/login', data)
}
interface RegisterInput {
user: {
username: string
email: string
password: string
}
}
interface RegisterData {
user: UserRes
}
export const register = (data: RegisterInput) => {
return request.post<RegisterData>('/api/users/login', data)
}
login.vue
<template>
<div class="auth-page">
<div class="container page">
<div class="row">
<div class="col-md-6 offset-md-3 col-xs-12">
<h1 class="text-xs-center">Sign up</h1>
<p class="text-xs-center">
<a href="">Have an account?</a>
</p>
<ul class="error-messages">
<li>That email is already taken</li>
</ul>
<form @submit.prevent="handleSubmit">
<!-- <fieldset class="form-group">
<input
class="form-control form-control-lg"
type="text"
placeholder="Your Name"
/>
</fieldset> -->
<fieldset class="form-group">
<input
class="form-control form-control-lg"
type="text"
v-model="user.email"
placeholder="Email"
/>
</fieldset>
<fieldset class="form-group">
<input
v-model="user.password"
class="form-control form-control-lg"
type="password"
placeholder="Password"
/>
</fieldset>
<button class="btn btn-lg btn-primary pull-xs-right">
Sign up
</button>
</form>
</div>
</div>
</div>
</div>
</template>
<script lang="ts">
import { defineComponent, reactive } from 'vue'
import { login } from '@/api/user'
import { useRouter } from 'vue-router'
// import { useStore } from 'vuex'
// import { key } from '@/store'
import { useStore } from '@/store'
const useLogin = () => {
const router = useRouter() // 获取当前路由对象
const store = useStore()
// Composition API 中没有 this
const user = reactive({
email: '',
password: ''
})
const handleSubmit = async () => {
try {
const { data } = await login({
user
})
console.log('登录成功', data)
store.commit('setUser', {
user: data.user
})
// console.log(store.state.user)
// 登录成功,跳转到首页
router.push({
name: 'home'
})
} catch (err) {
console.log('登录失败', err)
}
}
return {
user,
handleSubmit
}
}
export default defineComponent({
name: 'LoginPage',
setup () {
return {
...useLogin()
}
}
})
</script>
store
import { InjectionKey } from 'vue'
import { createStore, Store, useStore as baseUseStore, createLogger } from 'vuex'
import { UserRes } from '@/api/user'
// define your typings for the store state
export interface State {
count: number
user: UserRes | null
}
// define injection key
export const key: InjectionKey<Store<State>> = Symbol('store')
// new Vuex.Store({ state... })
export const store = createStore<State>({
state: {
count: 0,
user: null
},
mutations: {
setUser (state, payload) {
state.user = payload.user
}
},
actions: {
},
modules: {
},
plugins: [createLogger()] // 控制台打印插件
})
// define your own `useStore` composition function
export function useStore () {
// 这里调用真正的 useStore
return baseUseStore(key)
}