1 // 引用vue和需要測試的組件 2 import Vue from 'vue' 3 import HelloWorld from '@/components/HelloWorld' 4 // 創建測試套件,一個測試組件寫一個測試套件 5 describe('HelloWorld.vue', () => { 6 // 測試用例,用來測試不同的方法或者顯示的內容 7 it('should render correct contents', () => { 8 const Constructor = Vue.extend(HelloWorld) 9 const vm = new Constructor().$mount() 10 // 判斷頁面中是否有msg所渲染出來的內容 11 // 等價document.querySelector('.hello h1') 12 expect(vm.$el.querySelector('.hello h1').textContent) 13 .to.equal('Welcome to Your Vue.js App') 14 }) 15 })
模仿微博的自動化測試代碼:
1 import Vue from 'vue' 2 import SineWeibo from '@/components/SinaWeibo' 3 import {mount} from 'vue-test-utils' 4 // 創建測試套件 5 describe('SinaaWeibo.vue',()=>{ 6 // 創建測試實例------點擊發布按鈕,發布新內容&&個人微博數量+1 7 it('點擊發布按鈕,發布新內容&&個人微博數量+1',()=>{ 8 // 找到要測試的組件實例,進行掛載 9 const wrapper=mount(SineWeibo); 10 // 找到發表微博的輸入內容 11 const textArea=wrapper.find('.weibo-publish-wrapper textarea') 12 // 發布按鈕 13 const buttonOfPublish = wrapper.find('.weibo-publish-wrapper button') 14 // weiboNews是從mock中獲取的數據,是微博的初始值 15 const lengthOfWeiboNews = wrapper.vm.weiboNews.length; 16 // 右邊的關注/粉絲/微博和其數量:2是微博數 17 const countOfMyWeibo=wrapper.vm.profileData[2].num; 18 // 模擬新的微博內容 19 wrapper.setData({newWeiboContent:{ 20 imgUrl: '../../static/image/profile.jpg', 21 name: 'Lee_tanghui', 22 resource: '剛剛 來自 網頁版微博', 23 content: '歡迎來到我的微博', 24 images: [] 25 }}) 26 // 觸發發布按鈕事件 27 buttonOfPublish.trigger('click') 28 // 獲取增加后的微博條數,和右邊的微博數量 29 const lengthOfWeiboNewsAfterPublish=wrapper.vm.weiboNews.length; 30 const countOfMyWeiboAfterPublish=wrapper.vm.profileData[2].num 31 //斷言: 發布后的微博條數是在原來的基礎上+1; 32 expect(lengthOfWeiboNewsAfterPublish).to.equal(lengthOfWeiboNews + 1); 33 //斷言: 個人微博數量是在原來的基礎上+1; 34 expect(countOfMyWeiboAfterPublish).to.equal(countOfMyWeibo + 1); 35 }) 36 // 測試實例:當文本框無內容時候,不能發表微博到列表,且彈出提示框 37 it('當文本框中無內容時, 不能發布空微博到微博列表, 且彈出提示框',()=>{ 38 const wrapper=mount(SineWeibo); 39 // 找到發布框內容 40 const textArea = wrapper.find('.weibo-publish-wrapper textarea'); 41 // 找到發布按鈕 42 const buttonOfPublish = wrapper.find('.weibo-publish-wrapper button'); 43 // 獲取下方微博條數 44 const lengthOfWeiboNews = wrapper.vm.weiboNews.length; 45 // 獲取右邊微博數 46 const countOfMyWeibo = wrapper.vm.profileData[2].num; 47 // 設置發表微博,但是content的內容為空 48 //設置textArea的綁定數據為空 49 wrapper.setData({newWeiboContent: { 50 imgUrl: '../../static/image/profile.jpg', 51 name: 'Lee_tanghui', 52 resource: '剛剛 來自 網頁版微博', 53 content: '', 54 images: [] 55 }}); 56 // 觸發發布按鈕 57 buttonOfPublish.trigger('click'); 58 // 獲取發表后的下方微博條數 59 const lengthOfWeiboNewsAfterPublish = wrapper.vm.weiboNews.length; 60 // 獲取發表后的下右邊微博數 61 const countOfMyWeiboAfterPublish = wrapper.vm.profileData[2].num; 62 // 斷言:發表前后的微博條數是相等的 63 //斷言: 沒有發布新內容 64 expect(lengthOfWeiboNewsAfterPublish).to.equal(lengthOfWeiboNews); 65 //斷言: 個人微博數量不變 66 expect(countOfMyWeiboAfterPublish).to.equal(countOfMyWeibo); 67 }); 68 69 70 // 測試實例:當點擊"關注", 個人頭像下關注的數量會增加1個, 且按鈕內字體變成"取消關注 71 it('當點擊"關注", 個人頭像下關注的數量會增加1個, 且按鈕內字體變成"取消關注',()=>{ 72 // 獲取wrapper 73 const wrapper = mount(SineWeibo); 74 // 獲取“關注”button 75 const buttonOfAddAttendion = wrapper.find('.add'); 76 // 獲取右邊的關注數量 77 const countOfMyAttention = wrapper.vm.profileData[0].num; 78 // 觸發“關注”button 79 buttonOfAddAttendion.trigger('click'); 80 // 獲取右邊的關注數量 81 const countOfMyAttentionAfterClick = wrapper.vm.profileData[0].num; 82 // 斷言1:右邊的關注數量等於原來的+1; 83 expect(countOfMyAttentionAfterClick).to.equal(countOfMyAttention + 1); 84 // 斷言2:button的text變為“取消關注” 85 expect(buttonOfAddAttendion.text()).to.equal('取消關注'); 86 87 }) 88 89 90 // 測試實例:當點擊"取消關注", 個人頭像下關注的數量會減少1個, 且按鈕內字體變成"關注 91 it('當點擊"取消關注", 個人頭像下關注的數量會減少1個, 且按鈕內字體變成"關注',()=>{ 92 const wrapper=mount(SineWeibo) 93 // 找到“取消關注”按鈕 94 const buttonOfUnAttendion = wrapper.find('.cancel'); 95 // 獲取右邊關注人數 96 const countOfMyAttention = wrapper.vm.profileData[0].num; 97 // 觸發“取消關注”按鈕的點擊事件 98 buttonOfUnAttendion.trigger('click'); 99 // 獲取右邊關注人數 100 const countOfMyAttentionAfterClick = wrapper.vm.profileData[0].num; 101 // 斷言1:右邊的人數為原來的人數-1 102 expect(countOfMyAttentionAfterClick).to.equal(countOfMyAttention - 1); 103 // 斷言2:cancel的text變成"關注" 104 expect(buttonOfUnAttendion.text()).to.equal('關注'); 105 }) 106 107 it('當點擊"收藏"時, 我的收藏會增加1個數量, 且按鈕內文字變成"已收藏"', () => { 108 const wrapper = mount(SineWeibo); 109 const buttonOfCollect = wrapper.find('.uncollectedWeibo'); 110 const countOfMyCollect = Number(wrapper.find('.collect-num span').text()); 111 112 //觸發點擊事件 113 buttonOfCollect.trigger('click'); 114 const countOfMyCollectAfterClick = Number(wrapper.find('.collect-num span').text()); 115 116 //斷言: 我的收藏數量會加1 117 expect(countOfMyCollectAfterClick).to.equal(countOfMyCollect + 1); 118 //斷言: 按鈕內文字變成已收藏 119 expect(buttonOfCollect.text()).to.equal('已收藏'); 120 }) 121 122 it('當點擊"已收藏"時, 我的收藏會減少1個數量, 且按鈕內文字變成"收藏"', () => { 123 const wrapper = mount(SineWeibo); 124 const buttonOfUnCollect = wrapper.find('.collectedWeibo'); 125 const countOfMyCollect = Number(wrapper.find('.collect-num span').text()); 126 127 //觸發點擊事件 128 buttonOfUnCollect.trigger('click'); 129 const countOfMyCollectAfterClick = Number(wrapper.find('.collect-num span').text()); 130 131 //斷言: 我的收藏數量會減1 132 expect(countOfMyCollectAfterClick).to.equal(countOfMyCollect - 1 ); 133 //斷言: 按鈕內文字變成已收藏 134 expect(buttonOfUnCollect.text()).to.equal('收藏'); 135 }); 136 137 it('當點擊"已收藏"時, 我的收藏會減少1個數量, 且按鈕內文字變成"收藏"', () => { 138 const wrapper = mount(SineWeibo); 139 const buttonOfUnCollect = wrapper.find('.collectedWeibo'); 140 const countOfMyCollect = Number(wrapper.find('.collect-num span').text()); 141 142 //觸發點擊事件 143 buttonOfUnCollect.trigger('click'); 144 const countOfMyCollectAfterClick = Number(wrapper.find('.collect-num span').text()); 145 146 //斷言: 我的收藏數量會減1 147 expect(countOfMyCollectAfterClick).to.equal(countOfMyCollect - 1 ); 148 //斷言: 按鈕內文字變成已收藏 149 expect(buttonOfUnCollect.text()).to.equal('收藏'); 150 }); 151 152 it('當點擊"贊", 我的贊會增加1個數量, 且按鈕內文字變成"取消贊"', () => { 153 const wrapper = mount(SineWeibo); 154 const buttonOfLike = wrapper.find('.dislikedWeibo'); 155 const countOfMyLike = Number(wrapper.find('.like-num span').text()); 156 157 //觸發點擊事件 158 buttonOfLike.trigger('click'); 159 const countOfMyLikeAfterClick = Number(wrapper.find('.like-num span').text()); 160 161 //斷言: 我的贊會增加1個數量 162 expect(countOfMyLikeAfterClick).to.equal(countOfMyLike + 1); 163 //斷言: 按鈕內文字變成取消贊 164 expect(buttonOfLike.text()).to.equal('取消贊'); 165 }); 166 167 it('當點擊"取消贊", 我的贊會減少1個數量, 且按鈕內文字變成"贊"', () => { 168 const wrapper = mount(SineWeibo); 169 const buttonOfDislike = wrapper.find('.likedWeibo'); 170 const countOfMyLike = Number(wrapper.find('.like-num span').text()); 171 172 //觸發點擊事件 173 buttonOfDislike.trigger('click'); 174 const countOfMyLikeAfterClick = Number(wrapper.find('.like-num span').text()); 175 176 //斷言: 我的贊會增加1個數量 177 expect(countOfMyLikeAfterClick).to.equal(countOfMyLike - 1); 178 //斷言: 按鈕內文字變成取消贊 179 expect(buttonOfDislike.text()).to.equal('贊'); 180 }); 181 })
模仿微博的vue 代碼:
<template>
<div class="weibo-page">
<nav>
<span class="weibo-logo"></span>
<div class="search-wrapper">
<input type="text" placeholder="大家正在搜: 李棠輝的文章好贊!">
<img v-if="!iconActive" @mouseover="mouseOverToIcon" src="../../static/image/search.png" alt="搜索icon">
<img v-if="iconActive" @mouseout="mouseOutToIcon" src="../../static/image/search-active.png" alt="搜索icon">
</div>
</nav>
<div class="main-container">
<aside class="aside-nav">
<ul>
<li :class="{ active: isActives[indexOfContent] }" v-for="(content, indexOfContent) in asideTab" :key="indexOfContent" @click="tabChange(indexOfContent)">
<span>{{content}}</span>
<span class="count">
<span v-if="indexOfContent === 1" class="collect-num">
(
<span>{{collectNum}}</span>
)
</span>
<span v-if="indexOfContent === 2" class="like-num">
(
<span>{{likeNum}}</span>
)
</span>
</span>
</li>
</ul>
</aside>
<main class="weibo-content">
<div class="weibo-publish-wrapper">
<img src="../../static/image/tell-people.png"></img>
<textarea v-model="newWeiboContent.content"></textarea>
<button @click="publishNewWeiboContent">發布</button>
</div>
<div
class="weibo-news"
v-for="(news, indexOfNews) in weiboNews"
:key="indexOfNews">
<div class="content-wrapper">
<div class="news-title">
<div class="news-title__left">
<img :src="news.imgUrl">
<div class="title-text">
<div class="title-name">{{news.name}}</div>
<div class="title-time">{{news.resource}}</div>
</div>
</div>
<button
class="news-title__right add"
v-if="news.attention === false"
@click="attention(indexOfNews)">
<i class="fa fa-plus"></i>
關注
</button>
<button
class="news-title__right cancel"
v-if="news.attention === true"
@click="unAttention(indexOfNews)">
<i class="fa fa-close"></i>
取消關注
</button>
</div>
<div class="news-content">{{news.content}}</div>
<div class="news-image" v-if="news.images.length">
<img
v-for="(img, indexOfImg) in news.images"
:key="indexOfImg"
:src="img">
</div>
</div>
<ul class="news-panel">
<li
:class="{uncollectedWeibo: !news.collect, collectedWeibo: news.collect}"
@click="handleCollect(indexOfNews)">
<i class="fa fa-star-o" :class="{collected: news.collect }"></i>
{{news.collect ? "已收藏" : '收藏'}}
</li>
<li>
<i class="fa fa-external-link"></i>
轉發
</li>
<li>
<i class="fa fa-commenting-o"></i>
評論
</li>
<li
:class="{dislikedWeibo: !news.like, likedWeibo: news.like}"
@click="handleLike(indexOfNews)">
<i class="fa fa-thumbs-o-up" :class="{liked: news.like}"></i>
{{news.like ? '取消贊' : '贊'}}
</li>
</ul>
</div>
</main>
<aside class="aside-right">
<div class="profile-wrapper">
<div class="profile-top">
<img src="../../static/image/profile.jpg">
</div>
<div class="profile-bottom">
<div class="profile-name">Lee_tanghui</div>
<ul class="profile-info">
<li
v-for="(profile, indexOfProfile) in profileData"
:key="indexOfProfile">
<div class="number">{{profile.num}}</div>
<div class="text">{{profile.text}}</div>
</li>
</ul>
</div>
</div>
</aside>
</div>
<footer>
Wish you like my blog! --- LITANGHUI
</footer>
</div>
</template>
<script>
//引入假數據
import * as mockData from '../mock-data.js'
export default {
created() {
//模擬獲取數據
this.profileData = mockData.profileData;
this.weiboNews = mockData.weiboNews;
this.collectNum = mockData.collectNum;
this.likeNum = mockData.likeNum;
},
data() {
return {
iconActive: false,
asideTab: ["首頁", "我的收藏", "我的贊"],
isActives: [true, false, false],
profileData: [],
weiboNews: [],
collectNum: 0,
likeNum: 0,
newWeiboContent: {
imgUrl: '../../static/image/profile.jpg',
name: 'Lee_tanghui',
resource: '剛剛 來自 網頁版微博',
attention:false,
content: '',
images: []
},
}
},
methods: {
mouseOverToIcon() {
this.iconActive = true;
},
mouseOutToIcon() {
this.iconActive = false;
},
tabChange(indexOfContent) {
this.isActives.forEach((item, index) => {
index === indexOfContent ?
this.$set(this.isActives, index, true) :
this.$set(this.isActives, index, false);
})
},
publishNewWeiboContent() {
if(!this.newWeiboContent.content) {
alert('請輸入內容!')
return;
}
const newWeibo = JSON.parse(JSON.stringify(this.newWeiboContent));
this.weiboNews.unshift(newWeibo);
this.newWeiboContent.content = '';
this.profileData[2].num++;
},
attention(index) {
this.weiboNews[index].attention = true;
this.profileData[0].num++;
},
unAttention(index) {
this.weiboNews[index].attention = false;
this.profileData[0].num--;
},
handleCollect(index) {
this.weiboNews[index].collect = !this.weiboNews[index].collect;
this.weiboNews[index].collect ?
this.collectNum++ :
this.collectNum--;
},
handleLike(index) {
this.weiboNews[index].like = !this.weiboNews[index].like;
this.weiboNews[index].like ?
this.likeNum++ :
this.likeNum--;
}
}
}
</script>
<style lang="less">
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
.weibo-page {
display: flex;
flex-direction: column;
}
nav {
display: flex;
height: 60px;
line-height: 60px;
background-color: #f0f0f0;
align-items: center;
.weibo-logo {
margin-left: 13%;
width: 100px;
height: 28px;
background: url("../../static/image/WB_logo.png") no-repeat;
cursor: pointer;
}
.search-wrapper {
display: flex;
position: relative;
width: 25%;
input {
width: 100%;
height: 24px;
padding-left: 10px;
}
img {
position: absolute;
right: 10px;
top: 4px;
width: 20px;
height: 20px;
cursor: pointer;
}
}
}
.main-container {
display: flex;
justify-content: center;
background-color: #000;
padding-top: 16px;
color: #fff;
.aside-nav {
width: 11.31%;
ul {
list-style: none;
li {
height: 34px;
line-height: 34px;
padding-left: 10px;
cursor: pointer;
font-weight: bold;
background-color: rgb(19, 19, 19);
}
li:hover {
background-color: rgb(66, 66, 66);
}
.active {
background-color: rgb(66, 66, 66);
}
}
}
.weibo-content {
width: 45.41%;
color: #000;
.weibo-publish-wrapper {
background: #fff;
position: relative;
padding: 10px 10px 54px 10px;
textarea {
display: block;
width: 100%;
height: 95px;
padding: 10px;
}
button {
position: absolute;
right: 10px;
bottom: 10px;
width: 82px;
height: 30px;
background: #ff8140;
border: 1px solid #f77c3d;
color: #fff;
box-shadow: 0px 1px 2px rgba(0, 0, 0, 0.25);
cursor: pointer;
}
}
.weibo-news {
background: #fff;
margin: 10px 0;
.content-wrapper {
padding: 20px 20px 4px 20px;
}
.news-title {
display: flex;
justify-content: space-between;
.news-title__left {
display: flex;
img {
width: 50px;
height: 50px;
vertical-align: text-top;
}
.title-text {
display: flex;
flex-direction: column;
justify-content: space-around;
margin-left: 10px;
}
.title-name {
color: #333;
font-size: 14px;
font-weight: 700;
}
.title-time {
color: #808080;
margin-bottom: 2px;
font-size: 12px;
}
}
.news-title__right {
align-self: center;
width: 58px;
height: 22px;
line-height: 22px;
background: #fff;
border: 1px solid #d9d9d9;
cursor: pointer;
img {
position: relative;
left: 1px;
top: 2px;
}
i {
color: #ff8140;
}
}
.cancel {
width: 86px;
}
}
.news-content {
padding: 10px 0 10px 60px;
font-size: 14px;
}
.news-image {
padding: 10px 0 0 60px;
img {
width: 110px;
height: 110px;
margin-right: 10px;
}
}
.news-panel {
display: flex;
list-style: none;
border-top: 1px solid #f2f2f5;
li {
width: 25%;
height: 22px;
line-height: 22px;
margin: 7px 0;
text-align: center;
border-left: 1px solid #f2f2f5;
color: #808080;
cursor: pointer;
.collected {
color: #ff8140;
}
.like {
color: #ff8140;
}
}
}
}
}
.aside-right {
width: 17.41%;
margin-left: 10px;
.profile-top {
position: relative;
height: 75px;
background: url('../../static/image/profile-bg.jpg');
img {
position: absolute;
bottom: -30px;;
left: 50%;
margin-left: -30px;
width: 60px;
height: 60px;
border-radius: 50%;
}
}
.profile-bottom {
height: 118px;
padding-top: 30px;
background: #fff;
color: #000;
.profile-name {
text-align: center;
font-weight: 700;
}
.profile-info {
display: flex;
list-style: none;
padding-top: 10px;
li {
width: 33.333%;
border-left: 1px solid #f2f2f5;
cursor: pointer;
div {
text-align: center;
}
.number{
font-weight: 700;
}
}
}
}
}
}
footer {
height: 100px;
line-height: 100px;
text-align: center;
font-size: 12px;
font-weight:700;
}
</style>
