6.3 處理事件

要做出人們想要的東西,您必須傾聽(tīng)客戶的意見(jiàn)秘血。顧客可能并不總是對(duì)的,但在路易斯的面包店评甜,每位員工都知道他們必須始終傾聽(tīng)顧客的意見(jiàn)——或者至少讓顧客感到被傾聽(tīng)灰粮。

從業(yè)務(wù)角度來(lái)看,客戶的輸入推動(dòng)了產(chǎn)品決策忍坷。例如粘舟,它可以幫助面包店生產(chǎn)更多客戶想要的產(chǎn)品熔脂,減少客戶不需要的產(chǎn)品。從軟件的角度來(lái)看柑肴,用戶輸入會(huì)導(dǎo)致應(yīng)用程序做出反應(yīng)霞揉,改變其狀態(tài)并顯示新結(jié)果。

在瀏覽器中運(yùn)行的應(yīng)用程序不會(huì)直接接收數(shù)字或字符串等輸入晰骑。相反适秩,它們處理事件。當(dāng)用戶單擊硕舆、鍵入和滾動(dòng)時(shí)秽荞,他們會(huì)觸發(fā)事件。這些事件包括有關(guān)用戶交互的詳細(xì)信息岗宣,例如他們提交的表單內(nèi)容或單擊的按鈕蚂会。

在本節(jié)中,您將學(xué)習(xí)如何處理測(cè)試中的事件并準(zhǔn)確模擬用戶與應(yīng)用程序交互的方式耗式。通過(guò)精確表示用戶的輸入胁住,您將獲得更可靠的測(cè)試,因?yàn)樗鼈儗⒏愃朴谶\(yùn)行時(shí)發(fā)生的情況刊咳。

要查看事件的工作原理并了解如何測(cè)試它們彪见,您將向應(yīng)用程序添加一個(gè)新表單,該表單允許用戶將項(xiàng)目添加到庫(kù)存中娱挨。然后余指,您將使您的應(yīng)用程序在用戶與其交互時(shí)驗(yàn)證表單,并為這些交互編寫(xiě)更多測(cè)試跷坝。

首先酵镜,向 index.html 添加一個(gè)包含兩個(gè)字段的表單:一個(gè)用于項(xiàng)目名稱,另一個(gè)用于其數(shù)量柴钻。

<!DOCTYPE html>
<html lang="en">
  < !-- ... -->
 
  <body>
    < !-- ... -->
 
    <form id="add-item-form">
      <input
        type="text"
        name="name"
        placeholder="Item name"
      >
      <input
        type="number"
        name="quantity"
        placeholder="Quantity"
      >
      <button type="submit">Add to inventory</button>         ?
    </form>
    <script src="bundle.js"></script>
  </body>
</html>

? 導(dǎo)致表單被提交淮韭,觸發(fā)提交事件

在 domController.js 文件中,創(chuàng)建一個(gè)名為 handleAddItem 的函數(shù)贴届。 這個(gè)函數(shù)將接收一個(gè)事件作為它的第一個(gè)參數(shù)靠粪,檢索提交的值,調(diào)用 addItem 來(lái)更新庫(kù)存毫蚓,然后調(diào)用 updateItemList 來(lái)更新 DOM占键。

// ...
 
const handleAddItem = event => {
  event.preventDefault();                                   ?
 
  const { name, quantity } = event.target.elements;
 
  addItem(name.value, parseInt(quantity.value, 10));        ?
 
  updateItemList(data.inventory);
};

? 阻止頁(yè)面重新加載,因?yàn)樗J(rèn)情況下

? 因?yàn)閝uantity字段值是一個(gè)字符串元潘,所以我們需要使用parseInt將其轉(zhuǎn)換為數(shù)字畔乙。

注意 默認(rèn)情況下,當(dāng)用戶提交表單時(shí)翩概,瀏覽器將重新加載頁(yè)面啸澡。 調(diào)用事件的 preventDefault 方法將取消默認(rèn)行為袖订,導(dǎo)致瀏覽器無(wú)法重新加載頁(yè)面。

最后嗅虏,為了在用戶提交新項(xiàng)目時(shí)調(diào)用 handleAddItem,您需要將提交事件的事件偵聽(tīng)器附加到表單上沐。

現(xiàn)在您有了一個(gè)提交項(xiàng)目的表單皮服,您不再需要在 main.js 文件中手動(dòng)調(diào)用 addItem 和 updateItemList。 相反参咙,您可以替換此文件的全部?jī)?nèi)容龄广,并使其僅將事件偵聽(tīng)器附加到表單。

const { handleAddItem } = require("./domController");
 
const form = document.getElementById("add-item-form");
form.addEventListener("submit", handleAddItem);           ?

? 每當(dāng)用戶提交表單時(shí)調(diào)用 handleAddItem

在這些更改之后蕴侧,您應(yīng)該有一個(gè)能夠動(dòng)態(tài)地將項(xiàng)目添加到庫(kù)存中的應(yīng)用程序择同。 要查看它的運(yùn)行情況,請(qǐng)執(zhí)行 npm run build 以重新生成 bundle.js净宵、 npx http-server ./ 以提供 index.html 并訪問(wèn) localhost:8080敲才,就像您之前所做的那樣。

現(xiàn)在择葡,考慮一下您將如何測(cè)試剛剛添加的代碼紧武。

一種可能性是為 handleAddItem 函數(shù)本身添加一個(gè)測(cè)試。 該測(cè)試將創(chuàng)建一個(gè)類似事件的對(duì)象并將其作為參數(shù)傳遞給 handleAddItem敏储,如下所示阻星。

const { updateItemList, handleAddItem } = require("./domController");
 
// ...

describe("handleAddItem", () => {
  test("adding items to the page", () => {
    const event = {                                                ?
      preventDefault: jest.fn(),
      target: {
        elements: {
          name: { value: "cheesecake" },
          quantity: { value: "6" }
        }
      }
    };
 
    handleAddItem(event);                                          ?
 
    expect(event.preventDefault.mock.calls).toHaveLength(1);       ?
 
    const itemList = document.getElementById("item-list");
    expect(getByText(itemList, "cheesecake - Quantity: 6"))        ?
      .toBeInTheDocument();
  });
});

? 創(chuàng)建一個(gè)復(fù)制事件接口的對(duì)象

? 練習(xí)handleAddItem函數(shù)

? 檢查表單的默認(rèn)重新加載是否已被阻止

? 檢查 itemList 是否包含具有預(yù)期文本的節(jié)點(diǎn)

為了通過(guò)之前的測(cè)試,您必須對(duì)事件的屬性進(jìn)行逆向工程已添,從頭開(kāi)始構(gòu)建它妥箕。

這種技術(shù)的問(wèn)題之一是它沒(méi)有考慮頁(yè)面中的任何實(shí)際輸入元素。因?yàn)槟约簶?gòu)建了事件更舞,所以您可以為名稱和數(shù)量包含任意值畦幢。例如,如果您嘗試從 index.html 中刪除輸入元素疏哗,即使您的應(yīng)用程序可能無(wú)法運(yùn)行呛讲,該測(cè)試仍會(huì)通過(guò)。

因?yàn)檫@個(gè)測(cè)試是直接調(diào)用handleAddItem的返奉,如圖6.4所示贝搁,所以它并不關(guān)心它是否作為submit事件的監(jiān)聽(tīng)器附加到表單上。例如芽偏,如果您嘗試從 main.js 中刪除對(duì) addEventListener 的調(diào)用雷逆,則此測(cè)試將繼續(xù)通過(guò)。同樣污尉,您發(fā)現(xiàn)了另一種情況膀哲,在這種情況下往产,您的應(yīng)用程序無(wú)法運(yùn)行但您的測(cè)試會(huì)通過(guò)。


圖6-4

正如您剛剛所做的那樣某宪,手動(dòng)構(gòu)建事件有助于快速迭代并在構(gòu)建偵聽(tīng)器時(shí)單獨(dú)測(cè)試它們仿村。但是,當(dāng)談到創(chuàng)建可靠的保證時(shí)兴喂,這種技術(shù)是不夠的蔼囊。此單元測(cè)試僅涵蓋 handleAddItem 函數(shù)本身,因此無(wú)法保證當(dāng)用戶觸發(fā)真實(shí)事件時(shí)應(yīng)用程序會(huì)正常工作衣迷。

為了創(chuàng)建更可靠的保證畏鼓,最好創(chuàng)建一個(gè)真實(shí)的事件實(shí)例,并使用節(jié)點(diǎn)的 dispatchEvent 方法通過(guò) DOM 節(jié)點(diǎn)調(diào)度它壶谒。

準(zhǔn)確再現(xiàn)運(yùn)行時(shí)發(fā)生的事情的第一步是更新文檔的正文云矫,使其包含 index.html 中的標(biāo)記,正如我們之前所做的那樣汗菜。然后让禀,最好使用 require("./main") 執(zhí)行 main.js,以便它可以將 eventListener 附加到表單呵俏。如果您在再次使用 initialHTML 更新文檔正文后不運(yùn)行 main.js堆缘,其表單將不會(huì)附加事件偵聽(tīng)器。

此外普碎,您必須在需要 main.js 之前調(diào)用 jest.resetModules吼肥。否則,Jest 將從其緩存中獲取 ./main.js麻车,以防止它再次被執(zhí)行缀皱。

const fs = require("fs");
const initialHtml = fs.readFileSync("./index.html");
 
beforeEach(() => {
  document.body.innerHTML = initialHtml;
 
  jest.resetModules();                    ?
  require("./main");                      ?
});

? 這里你必須使用 jest.resetModules 因?yàn)椋駝t动猬,Jest 會(huì)緩存 main.js 并且它不會(huì)再次運(yùn)行啤斗。

? 您必須再次執(zhí)行 main.js,以便它可以在每次主體更改時(shí)將事件偵聽(tīng)器附加到表單赁咙。

既然您的文檔具有 index.html 中的內(nèi)容钮莲,并且 main.js 已將偵聽(tīng)器附加到表單,您就可以編寫(xiě)測(cè)試本身了彼水。 這個(gè)測(cè)試將填充頁(yè)面的輸入崔拥,創(chuàng)建一個(gè)類型為 submit 的事件,找到表單凤覆,并調(diào)用它的 dispatchEvent 方法链瓦。 分派事件后,它將檢查列表是否包含它剛剛添加的項(xiàng)目的條目。

const { screen, getByText } = require("@testing-library/dom");
 
// ...
 
test("adding items through the form", () => {
  screen.getByPlaceholderText("Item name").value = "cheesecake";
  screen.getByPlaceholderText("Quantity").value = "6";
 
  const event = new Event("submit");                               ?
  const form = document.getElementById("add-item-form");           ?
  form.dispatchEvent(event);
 
  const itemList = document.getElementById("item-list");
  expect(getByText(itemList, "cheesecake - Quantity: 6"))          ?
    .toBeInTheDocument();
});

? 創(chuàng)建一個(gè)“本地”事件實(shí)例慈俯,類型為 submit

? 通過(guò)頁(yè)面的表單調(diào)度事件

? 檢查分派的事件是否導(dǎo)致頁(yè)面包含具有預(yù)期文本的元素

這個(gè)測(cè)試(也顯示在圖 6.5 中)更準(zhǔn)確地代表了運(yùn)行時(shí)發(fā)生的情況渤刃。 因?yàn)樗姆秶戎暗臏y(cè)試更廣泛,所以這個(gè)測(cè)試在測(cè)試金字塔中更高贴膘,因此它的保證更可靠卖子。 例如,如果您嘗試從 index.html 中刪除輸入元素或從 main.js 中調(diào)用 addEventListener刑峡,則此測(cè)試將失敗揪胃,與前一個(gè)不同。


圖6-5
// ...
 
const validItems = ["cheesecake", "apple pie", "carrot cake"];
const handleItemName = event => {
  const itemName = event.target.value;
 
  const errorMsg = window.document.getElementById("error-msg");
 
  if (itemName === "") {
    errorMsg.innerHTML = "";
  } else if (!validItems.includes(itemName)) {
    errorMsg.innerHTML = `${itemName} is not a valid item.`;
  } else {
    errorMsg.innerHTML = `${itemName} is valid!`;
  }
};
 
// Don't forget to export `handleItemName`
module.exports = { updateItemList, handleAddItem, handleItemName };

現(xiàn)在氛琢,為了使 handleItemName 能夠顯示其消息,向 index.html 添加一個(gè)新的 p 標(biāo)簽随闪,其 id 為 error-msg阳似。

<!DOCTYPE html>
<html lang="en">
  < !-- ... -->
  <body>
    < !-- ... -->
    <p id="error-msg"></p>               ?
 
    <form id="add-item-form">
      < !-- ... -->
    </form>
    <script src="bundle.js"></script>
  </body>
</html>

? 將根據(jù)項(xiàng)目名稱是否有效向用戶顯示反饋的元素

如果您想單獨(dú)測(cè)試 handleItemName 函數(shù),作為練習(xí)铐伴,您可以嘗試為其編寫(xiě)單元測(cè)試撮奏,就像我們之前為 handleAddItem 函數(shù)所做的那樣。您可以在本書(shū) GitHub 存儲(chǔ)庫(kù)的第 6/3_handling_events/1_handling_raw_events 文件夾中找到如何編寫(xiě)此測(cè)試的完整示例当宴,網(wǎng)址為 https://github.com/lucasfcosta/testing-javascript-applications畜吊。

注意 如前所述,對(duì)這些函數(shù)進(jìn)行單元測(cè)試在您迭代時(shí)會(huì)很有用户矢,但分派實(shí)際事件的測(cè)試要可靠得多玲献。考慮到這兩種測(cè)試高度重疊并且需要相似數(shù)量的代碼梯浪,如果您必須選擇一種捌年,我建議您堅(jiān)持使用使用元素的 dispatchEvent 的測(cè)試。

如果您愿意編寫(xiě)處理程序函數(shù)而不在整個(gè)過(guò)程中單獨(dú)測(cè)試它們挂洛,那么編寫(xiě)僅使用 dispatchEvent 的測(cè)試可能會(huì)更好礼预。

驗(yàn)證工作的最后一步是附加一個(gè)事件偵聽(tīng)器,該偵聽(tīng)器處理在項(xiàng)目名稱的輸入中發(fā)生的輸入事件虏劲。更新您的 main.js托酸,并添加以下代碼。

const { handleAddItem, handleItemName } = require("./domController");
 
// ...
 
const itemInput = document.querySelector(`input[name="name"]`);
itemInput.addEventListener("input", handleItemName);               ?

? 使用 handleItemName 處理來(lái)自 itemInput 的輸入事件

提示要查看此新功能柒巫,請(qǐng)不要忘記在使用 npx http-server ./ 服務(wù)之前通過(guò)運(yùn)行 npm run build 來(lái)重建 bundle.js励堡。

現(xiàn)在您的驗(yàn)證功能可以正常工作,請(qǐng)為其編寫(xiě)測(cè)試吻育。 此測(cè)試必須設(shè)置輸入的值并通過(guò)輸入節(jié)點(diǎn)分派輸入事件念秧。 派發(fā)事件后,它應(yīng)該檢查文檔是否包含成功消息布疼。

// ...
 
describe("item name validation", () => {
  test("entering valid item names ", () => {
    const itemField = screen.getByPlaceholderText("Item name");
    itemField.value = "cheesecake";
    const inputEvent = new Event("input");                ?
 
    itemField.dispatchEvent(inputEvent);                  ?
 
    expect(screen.getByText("cheesecake is valid!"))      ?
      .toBeInTheDocument();
  });
});

? 使用類型輸入創(chuàng)建事件的“本機(jī)”實(shí)例

? 通過(guò)項(xiàng)目名稱的字段調(diào)度事件

? 檢查頁(yè)面是否包含預(yù)期的反饋信息

作為練習(xí)摊趾,嘗試為不愉快的路徑編寫(xiě)一個(gè)測(cè)試币狠。此測(cè)試應(yīng)輸入無(wú)效的項(xiàng)目名稱,通過(guò)項(xiàng)目名稱字段調(diào)度事件砾层,并檢查文檔是否包含錯(cuò)誤消息漩绵。

回到我們的應(yīng)用程序需求——當(dāng)商品名稱無(wú)效時(shí)顯示錯(cuò)誤消息非常好,但是肛炮,如果我們不禁止用戶提交表單止吐,他們?nèi)匀豢梢詫o(wú)效商品添加到庫(kù)存中。我們也沒(méi)有任何驗(yàn)證來(lái)防止用戶在未指定數(shù)量的情況下提交表單侨糟,從而導(dǎo)致顯示 NaN碍扔。

為了防止這些無(wú)效操作的發(fā)生,您需要重構(gòu)處理程序秕重。不是只偵聽(tīng)發(fā)生在項(xiàng)目名稱字段上的輸入事件不同,而是偵聽(tīng)發(fā)生在表單子項(xiàng)上的所有輸入事件。然后溶耘,表單將檢查其子項(xiàng)的值并決定是否應(yīng)禁用提交按鈕二拐。

首先將 handleItemName 重命名為 checkFormValues 并使其驗(yàn)證表單的兩個(gè)字段中的值。

// ...
 
const validItems = ["cheesecake", "apple pie", "carrot cake"];
const checkFormValues = () => {
  const itemName = document.querySelector(`input[name="name"]`).value;
  const quantity = document.querySelector(`input[name="quantity"]`).value;
 
  const itemNameIsEmpty = itemName === "";
  const itemNameIsInvalid = !validItems.includes(itemName);
  const quantityIsEmpty = quantity === "";
 
  const errorMsg = window.document.getElementById("error-msg");
  if (itemNameIsEmpty) {
    errorMsg.innerHTML = "";
  } else if (itemNameIsInvalid) {
    errorMsg.innerHTML = `${itemName} is not a valid item.`;
  } else {
    errorMsg.innerHTML = `${itemName} is valid!`;
  }
 
  const submitButton = document.querySelector(`button[type="submit"]`);
  if (itemNameIsEmpty || itemNameIsInvalid || quantityIsEmpty) {           ?
    submitButton.disabled = true;
  } else {
    submitButton.disabled = false;
  }
};
 
// Don't forget to update your exports!
module.exports = { updateItemList, handleAddItem, checkFormValues };

? 禁用或啟用表單的提交輸入凳兵,取決于表單字段中的值是否有效

現(xiàn)在更新 main.js百新,而不是將 handleItemName 附加到名稱輸入,而是將新的 checkFormValues 附加到您的表單庐扫。 這個(gè)新的偵聽(tīng)器將響應(yīng)從表單子項(xiàng)冒泡的任何輸入事件饭望。

form.addEventListener("input", checkFormValues);                 ?
 
// Run `checkFormValues` once to see if the initial state is valid
checkFormValues();

? checkFormValues 函數(shù)現(xiàn)在將處理表單中觸發(fā)的任何輸入事件,包括將從表單的子級(jí)冒泡的輸入事件聚蝶。

注意要查看應(yīng)用程序的工作情況杰妓,請(qǐng)?jiān)谔峁┓?wù)之前使用 npm run build 重建它,正如我們?cè)诒菊轮卸啻瓮瓿傻哪菢印?/p>

鑒于您已保留用戶輸入無(wú)效項(xiàng)目名稱時(shí)出現(xiàn)的錯(cuò)誤消息碘勉,項(xiàng)目名稱驗(yàn)證的先前測(cè)試應(yīng)繼續(xù)通過(guò)巷挥。但是,如果您嘗試重新運(yùn)行它們验靡,您會(huì)發(fā)現(xiàn)它們失敗了倍宾。

提示要僅運(yùn)行 main.test.js 中的測(cè)試,您可以將 main.test.js 作為第一個(gè)參數(shù)傳遞給 jest 命令胜嗓。

如果您從 node_modules 文件夾運(yùn)行 jest高职,您的命令應(yīng)該類似于 ./node_modules/.bin/jest main.test.js。

如果你添加了一個(gè) NPM 腳本來(lái)運(yùn)行 Jest辞州,例如 test怔锌,你應(yīng)該運(yùn)行 npm run test -- main.test.js。

這些測(cè)試失敗是因?yàn)槟{(diào)度的事件不會(huì)冒泡。例如埃元,當(dāng)通過(guò) item name 字段調(diào)度 input 事件時(shí)涝涤,它不會(huì)觸發(fā)任何附加到其父級(jí)的偵聽(tīng)器,包括附加到表單的偵聽(tīng)器岛杀。因?yàn)楸韱伪O(jiān)聽(tīng)器沒(méi)有被執(zhí)行阔拳,它不會(huì)向頁(yè)面添加任何錯(cuò)誤信息,導(dǎo)致你的測(cè)試失敗类嗤。

要通過(guò)使事件冒泡來(lái)修復(fù)您的測(cè)試糊肠,您必須在實(shí)例化事件時(shí)傳遞一個(gè)額外的參數(shù)。此附加參數(shù)應(yīng)包含名為氣泡的屬性遗锣,其值為 true货裹。使用此選項(xiàng)創(chuàng)建的事件將冒泡并觸發(fā)附加到元素父級(jí)的偵聽(tīng)器。

// ...
 
describe("item name validation", () => {
  test("entering valid item names ", () => {
    const itemField = screen.getByPlaceholderText("Item name");
    itemField.value = "cheesecake";
    const inputEvent = new Event("input", { bubbles: true });    ?
 
    itemField.dispatchEvent(inputEvent);                         ?
 
    expect(screen.getByText("cheesecake is valid!")).toBeInTheDocument();
  });
});
 
// ...

? 創(chuàng)建一個(gè)帶有類型輸入的 Event 的“原生”實(shí)例精偿,它可以向上冒泡到元素的父元素泪酱,通過(guò)它分派它

? 通過(guò)項(xiàng)目名稱的字段調(diào)度事件。因?yàn)槭录?bubble 屬性設(shè)置為 true还最,所以它會(huì)冒泡到表單,觸發(fā)它的監(jiān)聽(tīng)器毡惜。

為了避免手動(dòng)實(shí)例化和分派事件拓轻,dom-testing-library 包含一個(gè)名為 fireEvent 的實(shí)用程序。

使用 fireEvent经伙,您可以準(zhǔn)確模擬多種不同類型的事件扶叉,包括提交表單、按鍵和更新字段帕膜。由于 fireEvent 處理在特定組件上觸發(fā)事件時(shí)您需要執(zhí)行的所有操作,因此它可以幫助您編寫(xiě)更少的代碼,而不必?fù)?dān)心觸發(fā)事件時(shí)發(fā)生的所有事情巨税。

例如鸵隧,通過(guò)使用 fireEvent 而不是手動(dòng)創(chuàng)建輸入事件,您可以避免必須為項(xiàng)目名稱設(shè)置字段的 value 屬性荒典。 fireEvent 函數(shù)知道輸入事件會(huì)更改通過(guò)其調(diào)度的組件的值酪劫。因此,它將為您處理更改值寺董。

更新表單驗(yàn)證的測(cè)試覆糟,以便它們使用 dom-testing-library 中的 fireEvent 實(shí)用程序。

// ...
 
const { screen, getByText, fireEvent } = require("@testing-library/dom");
 
// ...
 
describe("item name validation", () => {
  test("entering valid item names ", () => {
    const itemField = screen.getByPlaceholderText("Item name");
 
    fireEvent.input(itemField, {                  ?
      target: { value: "cheesecake" },
      bubbles: true
    });
 
    expect(screen.getByText("cheesecake is valid!")).toBeInTheDocument();
  });
});

? 不是創(chuàng)建一個(gè)事件然后調(diào)度它遮咖,而是使用 fireEvent.input 在字段上觸發(fā)一個(gè)項(xiàng)目名稱的輸入事件滩字。

提示 如果您需要更準(zhǔn)確地模擬用戶事件,例如用戶以一定的速度打字,您可以使用用戶事件庫(kù)麦箍,該庫(kù)也是由 testing-library 組織制作的漓藕。

例如,當(dāng)您有使用去抖動(dòng)驗(yàn)證的字段時(shí)内列,此庫(kù)特別有用:僅在用戶停止輸入后的特定時(shí)間觸發(fā)的驗(yàn)證撵术。

您可以在 https://github.com/testing-library/user-event 查看@testing-library/user-event 的完整文檔。

作為練習(xí)话瞧,嘗試更新所有其他測(cè)試嫩与,以便它們使用 fireEvent。我還建議與庫(kù)存管理器處理不同類型的交互并對(duì)其進(jìn)行測(cè)試交排。例如划滋,您可以嘗試在用戶雙擊項(xiàng)目列表中的姓名時(shí)刪除項(xiàng)目。

在本節(jié)之后埃篓,您應(yīng)該能夠編寫(xiě)測(cè)試來(lái)驗(yàn)證用戶將與您的頁(yè)面進(jìn)行的交互处坪。盡管手動(dòng)構(gòu)建事件以便在迭代時(shí)獲得快速反饋是可以的,但這不是創(chuàng)建最可靠質(zhì)量保證的那種測(cè)試架专。相反同窘,為了更準(zhǔn)確地模擬用戶的行為——因此,創(chuàng)造更可靠的保證——你可以使用 dispatchEvent 調(diào)度本機(jī)事件或使用第三方庫(kù)來(lái)使這個(gè)過(guò)程更方便部脚。當(dāng)涉及到捕獲錯(cuò)誤時(shí)想邦,這種相似性將使您的測(cè)試更有價(jià)值,并且因?yàn)槟鷽](méi)有嘗試手動(dòng)重現(xiàn)事件的界面委刘,它們將導(dǎo)致更少的維護(hù)開(kāi)銷(xiāo)丧没。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市锡移,隨后出現(xiàn)的幾起案子呕童,更是在濱河造成了極大的恐慌,老刑警劉巖淆珊,帶你破解...
    沈念sama閱讀 216,372評(píng)論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件夺饲,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡施符,警方通過(guò)查閱死者的電腦和手機(jī)钞支,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)操刀,“玉大人烁挟,你說(shuō)我怎么就攤上這事」强樱” “怎么了撼嗓?”我有些...
    開(kāi)封第一講書(shū)人閱讀 162,415評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵柬采,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我且警,道長(zhǎng)粉捻,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,157評(píng)論 1 292
  • 正文 為了忘掉前任斑芜,我火速辦了婚禮肩刃,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘杏头。我一直安慰自己盈包,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,171評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布醇王。 她就那樣靜靜地躺著呢燥,像睡著了一般。 火紅的嫁衣襯著肌膚如雪寓娩。 梳的紋絲不亂的頭發(fā)上叛氨,一...
    開(kāi)封第一講書(shū)人閱讀 51,125評(píng)論 1 297
  • 那天,我揣著相機(jī)與錄音棘伴,去河邊找鬼寞埠。 笑死,一個(gè)胖子當(dāng)著我的面吹牛焊夸,可吹牛的內(nèi)容都是我干的畸裳。 我是一名探鬼主播,決...
    沈念sama閱讀 40,028評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼淳地,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了帅容?” 一聲冷哼從身側(cè)響起颇象,我...
    開(kāi)封第一講書(shū)人閱讀 38,887評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎并徘,沒(méi)想到半個(gè)月后遣钳,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,310評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡麦乞,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,533評(píng)論 2 332
  • 正文 我和宋清朗相戀三年蕴茴,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片姐直。...
    茶點(diǎn)故事閱讀 39,690評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡倦淀,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出声畏,到底是詐尸還是另有隱情撞叽,我是刑警寧澤姻成,帶...
    沈念sama閱讀 35,411評(píng)論 5 343
  • 正文 年R本政府宣布,位于F島的核電站愿棋,受9級(jí)特大地震影響科展,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜糠雨,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,004評(píng)論 3 325
  • 文/蒙蒙 一才睹、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧甘邀,春花似錦琅攘、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,659評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至测摔,卻和暖如春置济,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背锋八。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,812評(píng)論 1 268
  • 我被黑心中介騙來(lái)泰國(guó)打工浙于, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人挟纱。 一個(gè)月前我還...
    沈念sama閱讀 47,693評(píng)論 2 368
  • 正文 我出身青樓羞酗,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親紊服。 傳聞我的和親對(duì)象是個(gè)殘疾皇子檀轨,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,577評(píng)論 2 353

推薦閱讀更多精彩內(nèi)容

  • 1. 組件的data為什么必須是函數(shù)? 組件中的 data 寫(xiě)成一個(gè)函數(shù)欺嗤,數(shù)據(jù)以函數(shù)返回值形式定義参萄,這樣每復(fù)用一次...
    郭先生_515閱讀 983評(píng)論 0 12
  • 事件流 JavaScript與HTML之間的交互是通過(guò)事件實(shí)現(xiàn)的。事件煎饼,就是文檔或?yàn)g覽器窗口中發(fā)生的一些特定的交互...
    DHFE閱讀 827評(píng)論 0 3
  • ??JavaScript 與 HTML 之間的交互是通過(guò)事件實(shí)現(xiàn)的吆玖。 ??事件筒溃,就是文檔或?yàn)g覽器窗口中發(fā)生的一些特...
    霜天曉閱讀 3,490評(píng)論 1 11
  • Vue真是太好了 壹萬(wàn)多字的Vue知識(shí)點(diǎn) 超詳細(xì)! 9 ?1??、Vue和其他兩大框架的區(qū)別 Angular 學(xué)習(xí)...
    三千繁夢(mèng)閱讀 336評(píng)論 0 0
  • ?1??沾乘、Vue和其他兩大框架的區(qū)別 Angular 學(xué)習(xí)成本太高 React 代碼可讀性差 Vue 學(xué)習(xí)成本較低...
    藍(lán)海00閱讀 74,027評(píng)論 40 1,231