【Vue3 Composition API】簡易電卓実装

Vue.js

Vue3の Composition API で、ブラウザ上で動作する簡易電卓を実装してみます。電卓実装は新入社員のプログラミング課題として課す会社も多いと思います。SIerに就職内定した先輩がひいこら言いながらC言語でやっていたことを思い出します。それに比べれば、Vue3ならかなり楽に実装できると思います。

前提条件

  • Ubuntu24.04 (WSL2) 上で作業しています
  • nvm インストール済
  • node.js v22.3.0 を使っています
  • Vue3 の Composition API を使います
  • Google Chrome上でのみ動作確認しています

nvmのインストールについては次の記事をご覧ください。

仕様概要

1.部品

 1-1.スクリーン(2行)

   1行目:メッセージと演算子を表示(初期値空文字列)

   2行目:入力および演算結果を表示(初期値0)

       ・12桁まで入力可

       ・12桁超の演算結果は8桁の有効数字で表示

       ・マウスホバーでメッセージ表示

       ・クリック時に値をクリップボードにコピー

 1-2.ボタン

   1-2-1.数字ボタン

     0~9、00の11個。各値を入力する。

   1-2-2.小数点ボタン

     「.」の1個。小数点を入力する。

   1-2-3.符号反転ボタン

     「+/-」の1個。数値の+と-を反転させる。

   1-2-4.演算子ボタン

     +、ー、×、÷、%(剰余)の5個。

     各演算を予約する。

   1-2-5.演算実行ボタン

     =の1個。演算を実行する。

   1-2-6.全消去ボタン

     「AC」の1個。初期状態に戻す。

2.レイアウト

  下図の通り。

プロジェクト作成

新規プロジェクト「vue3-calculator」を作成します。

npm init vue@^3

「Project name:」の項目でプロジェクト名を入力します。

プロジェクトフォルダ「vue3-calculator」に入って依存パッケージをインストールします。

cd vue3-calculator
npm install

開発サーバーを起動します。

npm run dev

WEBブラウザで http://localhost:5173/ にアクセスします。

Vue3のデフォルトページが表示されました。

簡易電卓実装

▼「src/App.vue」を編集

※script、template、styleをそれぞれタブで分けていますが1ファイル内に全て記述します。

  • script
  • template
  • style
<script setup>
import {ref} from 'vue';

var value = 0;
var buffer = '';
const screenValue = ref(value.toString());
const limitNumberOfDigits = 12;
const operator = ref('');
const isError = ref(false);
const isPointed = ref(false);

const inputNumber = (n) => {
  if (buffer.length < limitNumberOfDigits) {
    appendBuffer(n);
  } else {
    isError.value = true;
    setTimeout(() => { isError.value = false }, 200);
  }
  screenValue.value = buffer;
};

const appendBuffer = (n) => {
  if ((n === '0' || n === '00') && (buffer === '' ||  buffer === '0')) {
    buffer = '0';
  } else if (buffer === '0') {
    buffer = n;
  } else {
    buffer += n;
  }
};

const switchSign = () => {
  if (buffer === '') {
    value *= (-1);
    screenValue.value = value.toString();
  } else {
    buffer = (parseFloat(buffer) * (-1)).toString();
    screenValue.value = buffer;
  }
};

const calculate = () => {
  switch (operator.value) {
    case '+':
      value += parseFloat(buffer);
      break;
    case '-':
      value -= parseFloat(buffer);
      break;
    case '×':
      value *= parseFloat(buffer);
      break;
    case '÷':
      value /= parseFloat(buffer);
      break;
    case '%':
      value %= parseFloat(buffer);
      break;
    default:
  }
  screenValue.value = value.toString().length > limitNumberOfDigits
    ? value.toPrecision(limitNumberOfDigits - 4)
    : value.toString();
};

const setOperator = (o) => {
  if (operator.value.length > 0 && buffer.length > 0) {
    calculate();
  } else if (buffer.length > 0) {
    value = parseFloat(buffer);
  }
  operator.value = o;
  buffer = '';
  isPointed.value = false;
};

const appendPoint = () => {
  buffer += (buffer.length === 0 ? '0' : '')
         + (isPointed.value ? '' : '.');
  screenValue.value = buffer;
  isPointed.value = true;
};

const clearAll = () => {
  value = 0;
  buffer = '';
  screenValue.value = '0';
  operator.value = '';
  isPointed.value = false;
};

const equals = () => {
  if (buffer.length > 0 && operator.value.length > 0) {
    calculate();
    operator.value = '';
    buffer = '';
    isPointed.value = false;
  }
};

const copyMessage = ref('');
const copyScreen = () => {
  navigator.clipboard.writeText(screenValue.value)
  .then(
    () => {
      copyMessage.value = 'C O P I E D ❤';
      setTimeout(() => { copyMessage.value = ''; }, 500);
    },
    () => {
      copyMessage.value = 'C O P Y - F A I L E D 🚫';
      setTimeout(() => { copyMessage.value = ''; }, 500);
    }
  );
};

const canCopy = () => {
  copyMessage.value = 'C L I C K - TO - C O P Y 😊';
};

const cannotCopy = () => {
  copyMessage.value = '';
};
</script>
<template>
  <div id="calculator">
    <div id="screen" :class="{'error': isError}">
      <div class="subscreen">
        <p class="copy-message">{{ copyMessage }}</p>
        <p class="operator">{{ operator }}</p>
      </div>
      <p class="digits" @click="copyScreen" @mouseover="canCopy" @mouseout="cannotCopy">{{ screenValue }}</p>
    </div>
    <button class="function b-ac" @click="clearAll">AC</button>
    <button class="operator b-ss" @click="switchSign()">+/-</button>
    <button class="operator b-mod" @click="setOperator('%')">%</button>
    <button class="operator b-div" @click="setOperator('÷')">÷</button><br>
    <button class="number b-7" @click="inputNumber('7')">7</button>
    <button class="number b-8" @click="inputNumber('8')">8</button>
    <button class="number b-9" @click="inputNumber('9')">9</button>
    <button class="operator b-mul" @click="setOperator('×')">×</button><br>
    <button class="number b-4" @click="inputNumber('4')">4</button>
    <button class="number b-5" @click="inputNumber('5')">5</button>
    <button class="number b-6" @click="inputNumber('6')">6</button>
    <button class="operator b-sub" @click="setOperator('-')">-</button><br>
    <button class="number b-1" @click="inputNumber('1')">1</button>
    <button class="number b-2" @click="inputNumber('2')">2</button>
    <button class="number b-3" @click="inputNumber('3')">3</button>
    <button class="operator b-add" @click="setOperator('+')">+</button><br>
    <button class="number b-0" @click="inputNumber('0')">0</button>
    <button class="number b-00" @click="inputNumber('00')">00</button> 
    <button class="number b-dot" @click="appendPoint()">.</button>
    <button class="operator b-eq" @click="equals()">=</button>
  </div>
</template>
<style>
#calculator {
  border: 1px #000000 solid;
  border-radius: 6px 6px 6px 6px;
  width: 252px;
  height: 380px;
  background-color: #444444;
}

#screen {
  border: 1px #000000 solid;
  border-radius: 6px 6px 6px 6px;
  background-color: #cceeff;
  width: 206px;
  height: 64px;
  margin-left: 20px;
  margin-top: 20px;
  padding: 0px;
}

#screen.error {
  background-color: #ff6666;
}

#screen p.operator {
  display: flex;
  width: 50px;
  height: 16px;
  margin-left: 0px;
  margin-top: 4px;
  padding-left: 12px;
  padding-right: 12px;
  align-items: center;
  justify-content: flex-end;
  font-weight: bold;
  font-size: 16px;
  color: #000000;
}

#screen p.digits {
  display: flex;
  width: 100%;
  height: 48px;
  margin-left: 0px;
  margin-top: 0px;
  padding-left: 12px;
  padding-right: 12px;
  padding-top: 0px;
  padding-bottom: 0px;
  align-items: center;
  justify-content: flex-end;
  font-weight: bold;
  font-size: 30px;
  color: #000000;
  font-family: 'Ds-Digit';
}

.subscreen {
  display: flex;
  flex-shrink: 0;
  overflow: clip;
  justify-content: flex-end;
}

.copy-message {
  font-family: 'Ds-Digit';
  font-size: 12px;
  color: #000000;
}

button {
  border: 1px #000000 solid;
  border-radius: 4px 4px 4px 4px;
  width:36px;
  height: 36px;
  margin-left: 20px;
  margin-top: 20px;
  font-weight: bold;
  font-size: 16px;
}

button.number {
  background-color: #666666;
  color: #ffffff;
}

button.number:hover {
  background-color: #999999;
}

button.number:active {
  background-color: #ff9999;
}

button.operator {
  background-color: #ffcc00;
  color: #000000;
}

button.operator:hover {
  background-color: #ffee00;
}

button.operator:active {
  background-color: #ff9900;
}

button.function {
  background-color: #ff6666;
  color: #ffffff;
}

button.function:hover {
  background-color: #ff9999;
}
</style>

※実際のソースコードはGithubリポジトリに置いておきました。

vue-calculator/src/App.vue at main · macocci7/vue-calculator
A calculator app in Vue.js. Contribute to macocci7/vue-calculator development by creating an account on GitHub.

コードのざっくり解説

▼変数

  • 「buffer」(string、初期値空文字列):ボタン入力
  • 「value」(float、初期値ゼロ):演算結果
  • 「screenValue」(ref、初期値ゼロ):スクリーンに表示する数字
  • 「operator」(ref、初期値空文字列):演算子
  • 「isPointed」(ref、初期値false):小数点が入力されたかのフラグ

▼メソッド

  • inputNumber(n):数字(0~9、00)の入力を処理
  • appendBuffer(n):入力された数字を「buffer」に追加
  • switchSign():符号反転
  • calculate():演算処理(+、ー、×、÷、%)
  • setOperator(o):演算子予約処理
  • appendPoint():小数点付加処理
  • clearAll():全消去処理
  • equals():演算実行処理(「=」押下時)
  • copyScreen():スクリーンの値コピー処理
  • canCopy():スクリーンにmouseoverした際の処理
  • cannotCopy():スクリーンからmouseoutした際の処理

▼構成について

演算結果、ボタン入力、スクリーン表示とそれぞれ変数を分けている理由は、

コードを読めば判ると思いますが、スクリーンに表示する数字が、

演算結果の場合と、ボタン入力の場合との2ケースがあるからです。

演算結果はその性質上、浮動小数点数として扱っていますが、

ボタン入力とスクリーン表示は、「0.00」等の入力を許容するために文字列にしています。

小数点入力をフラグで制御している理由は、「0..1」や「1.2.3.4」のように2個目以降の小数点入力をさせないためです。

電卓用フォントの取得と設定

次のサイトにアクセスします。

DS-Digital Font | dafont.com
DS-Digital Font | dafont.com

画面右側の「DOWNLOAD」ボタンをクリックします。

ダウンロードしたZIPファイルを展開して、

「DS-DIGIT.TTF」を、プロジェクトフォルダ内の「src/assets/fonts/」にコピーします。

「src/assets/font.css」を新規作成します。

@font-face {
    font-family: 'Ds-Digit';
    font-style: normal;
    font-weight: 400;
    font-display: swap;
    src: url('@/assets/fonts/DS-DIGIT.TTF') format('truetype');
  }

「src/assets/main.css」に次の1行を追記します。

@import './font.css';

自動テスト

今回の自動テストのスタックは、簡単なコンポーネントテスト用に「Vitest」を採用しています。

Vitest
Next generation testing framework powered by Vite

まずはテストコード用のフォルダ「tests」をプロジェクトトップに新規作成し、その中に「App.test.js」を新規作成します。

App.test.js
import { shallowMount } from '@vue/test-utils';
import App from '@/App.vue';
import { describe, expect, test } from 'vitest';

describe("App.vue", () => {
    test("The initial value of digits is zero", () => {
        const wrapper = shallowMount(App, {});
        expect(wrapper.find(".digits").text()).toBe("0");
    });

    test("Number keys can input numbers", async () => {
        const wrapper = shallowMount(App, {});
        await wrapper.find(".b-9").trigger("click");
        expect(wrapper.find(".digits").text()).toBe("9");
        await wrapper.find(".b-8").trigger("click");
        expect(wrapper.find(".digits").text()).toBe("98");
        await wrapper.find(".b-7").trigger("click");
        expect(wrapper.find(".digits").text()).toBe("987");
        await wrapper.find(".b-6").trigger("click");
        expect(wrapper.find(".digits").text()).toBe("9876");
        await wrapper.find(".b-5").trigger("click");
        expect(wrapper.find(".digits").text()).toBe("98765");
        await wrapper.find(".b-4").trigger("click");
        expect(wrapper.find(".digits").text()).toBe("987654");
        await wrapper.find(".b-3").trigger("click");
        expect(wrapper.find(".digits").text()).toBe("9876543");
        await wrapper.find(".b-2").trigger("click");
        expect(wrapper.find(".digits").text()).toBe("98765432");
        await wrapper.find(".b-1").trigger("click");
        expect(wrapper.find(".digits").text()).toBe("987654321");
        await wrapper.find(".b-0").trigger("click");
        expect(wrapper.find(".digits").text()).toBe("9876543210");
        await wrapper.find(".b-00").trigger("click");
        expect(wrapper.find(".digits").text()).toBe("987654321000");
    });

    test("Number keys cannot input numbers more than the upper limit", async () => {
        const wrapper = shallowMount(App, {});
        await wrapper.find(".b-1").trigger("click");
        await wrapper.find(".b-2").trigger("click");
        await wrapper.find(".b-3").trigger("click");
        await wrapper.find(".b-4").trigger("click");
        await wrapper.find(".b-5").trigger("click");
        await wrapper.find(".b-6").trigger("click");
        await wrapper.find(".b-7").trigger("click");
        await wrapper.find(".b-8").trigger("click");
        await wrapper.find(".b-9").trigger("click");
        await wrapper.find(".b-0").trigger("click");
        await wrapper.find(".b-1").trigger("click");
        await wrapper.find(".b-2").trigger("click");
        await wrapper.find(".b-3").trigger("click");
        expect(wrapper.find(".digits").text()).toBe("123456789012");
    });

    test("AC button can reset digits to zero", async () => {
        const wrapper = shallowMount(App, {});
        await wrapper.find(".b-9").trigger("click");
        await wrapper.find(".b-8").trigger("click");
        await wrapper.find(".b-7").trigger("click");
        await wrapper.find(".b-6").trigger("click");
        await wrapper.find(".b-5").trigger("click");
        await wrapper.find(".b-4").trigger("click");
        await wrapper.find(".b-3").trigger("click");
        await wrapper.find(".b-2").trigger("click");
        await wrapper.find(".b-1").trigger("click");
        await wrapper.find(".b-0").trigger("click");
        await wrapper.find(".b-00").trigger("click");
        expect(wrapper.find(".digits").text()).toBe("987654321000");
        await wrapper.find(".b-ac").trigger("click");
        expect(wrapper.find(".digits").text()).toBe("0");
    });

    test("Dot button can input point", async () => {
        const wrapper = shallowMount(App, {});
        await wrapper.find(".b-dot").trigger("click");
        await wrapper.find(".b-1").trigger("click");
        expect(wrapper.find(".digits").text()).toBe("0.1");
        await wrapper.find(".b-ac").trigger("click");
        await wrapper.find(".b-0").trigger("click");
        await wrapper.find(".b-dot").trigger("click");
        await wrapper.find(".b-2").trigger("click");
        expect(wrapper.find(".digits").text()).toBe("0.2");
        await wrapper.find(".b-ac").trigger("click");
        await wrapper.find(".b-1").trigger("click");
        await wrapper.find(".b-dot").trigger("click");
        await wrapper.find(".b-2").trigger("click");
        expect(wrapper.find(".digits").text()).toBe("1.2");
    });

    test("Dot button cannot input points twice", async () => {
        const wrapper = shallowMount(App, {});
        await wrapper.find(".b-dot").trigger("click");
        await wrapper.find(".b-dot").trigger("click");
        await wrapper.find(".b-1").trigger("click");
        expect(wrapper.find(".digits").text()).toBe("0.1");
        await wrapper.find(".b-ac").trigger("click");
        await wrapper.find(".b-1").trigger("click");
        await wrapper.find(".b-dot").trigger("click");
        await wrapper.find(".b-dot").trigger("click");
        await wrapper.find(".b-2").trigger("click");
        expect(wrapper.find(".digits").text()).toBe("1.2");
        await wrapper.find(".b-ac").trigger("click");
        await wrapper.find(".b-1").trigger("click");
        await wrapper.find(".b-dot").trigger("click");
        await wrapper.find(".b-2").trigger("click");
        await wrapper.find(".b-dot").trigger("click");
        await wrapper.find(".b-3").trigger("click");
        expect(wrapper.find(".digits").text()).toBe("1.23");
    });

    test("0 button cannot input 01", async () => {
        const wrapper = shallowMount(App, {});
        await wrapper.find(".b-0").trigger("click");
        expect(wrapper.find(".digits").text()).toBe("0");
        await wrapper.find(".b-ac").trigger("click");
        await wrapper.find(".b-0").trigger("click");
        await wrapper.find(".b-0").trigger("click");
        expect(wrapper.find(".digits").text()).toBe("0");
        await wrapper.find(".b-ac").trigger("click");
        await wrapper.find(".b-0").trigger("click");
        await wrapper.find(".b-1").trigger("click");
        expect(wrapper.find(".digits").text()).toBe("1");
        await wrapper.find(".b-ac").trigger("click");
        await wrapper.find(".b-0").trigger("click");
        await wrapper.find(".b-0").trigger("click");
        await wrapper.find(".b-1").trigger("click");
        expect(wrapper.find(".digits").text()).toBe("1");
        await wrapper.find(".b-ac").trigger("click");
        await wrapper.find(".b-0").trigger("click");
        await wrapper.find(".b-0").trigger("click");
        await wrapper.find(".b-dot").trigger("click");
        await wrapper.find(".b-1").trigger("click");
        expect(wrapper.find(".digits").text()).toBe("0.1");
    });

    test("00 button cannot input 001", async () => {
        const wrapper = shallowMount(App, {});
        await wrapper.find(".b-00").trigger("click");
        expect(wrapper.find(".digits").text()).toBe("0");
        await wrapper.find(".b-ac").trigger("click");
        await wrapper.find(".b-00").trigger("click");
        await wrapper.find(".b-00").trigger("click");
        expect(wrapper.find(".digits").text()).toBe("0");
        await wrapper.find(".b-ac").trigger("click");
        await wrapper.find(".b-00").trigger("click");
        await wrapper.find(".b-1").trigger("click");
        expect(wrapper.find(".digits").text()).toBe("1");
        await wrapper.find(".b-ac").trigger("click");
        await wrapper.find(".b-00").trigger("click");
        await wrapper.find(".b-00").trigger("click");
        await wrapper.find(".b-1").trigger("click");
        expect(wrapper.find(".digits").text()).toBe("1");
        await wrapper.find(".b-ac").trigger("click");
        await wrapper.find(".b-00").trigger("click");
        await wrapper.find(".b-00").trigger("click");
        await wrapper.find(".b-dot").trigger("click");
        await wrapper.find(".b-1").trigger("click");
        expect(wrapper.find(".digits").text()).toBe("0.1");
    });

    test("+/- button can switch sign", async () => {
        const wrapper = shallowMount(App, {});
        await wrapper.find(".b-1").trigger("click");
        await wrapper.find(".b-ss").trigger("click");
        expect(wrapper.find(".digits").text()).toBe("-1");
        await wrapper.find(".b-ss").trigger("click");
        expect(wrapper.find(".digits").text()).toBe("1");
    });

    test("+ button can add numbers", async () => {
        const wrapper = shallowMount(App, {});
        await wrapper.find(".b-add").trigger("click");
        await wrapper.find(".b-1").trigger("click");
        expect(wrapper.find(".digits").text()).toBe("1");
        await wrapper.find(".b-add").trigger("click");
        expect(wrapper.find(".digits").text()).toBe("1");
        await wrapper.find(".b-2").trigger("click");
        await wrapper.find(".b-add").trigger("click");
        expect(wrapper.find(".digits").text()).toBe("3");
        await wrapper.find(".b-ac").trigger("click");
        await wrapper.find(".b-3").trigger("click");
        await wrapper.find(".b-add").trigger("click");
        await wrapper.find(".b-4").trigger("click");
        await wrapper.find(".b-add").trigger("click");
        expect(wrapper.find(".digits").text()).toBe("7");
    });

    test("- button can subtract numbers", async () => {
        const wrapper = shallowMount(App, {});
        await wrapper.find(".b-sub").trigger("click");
        await wrapper.find(".b-1").trigger("click");
        expect(wrapper.find(".digits").text()).toBe("1");
        await wrapper.find(".b-sub").trigger("click");
        expect(wrapper.find(".digits").text()).toBe("-1");
        await wrapper.find(".b-2").trigger("click");
        await wrapper.find(".b-sub").trigger("click");
        expect(wrapper.find(".digits").text()).toBe("-3");
        await wrapper.find(".b-ac").trigger("click");
        await wrapper.find(".b-3").trigger("click");
        await wrapper.find(".b-sub").trigger("click");
        await wrapper.find(".b-4").trigger("click");
        await wrapper.find(".b-sub").trigger("click");
        expect(wrapper.find(".digits").text()).toBe("-1");
    });

    test("x button can multiply numbers", async () => {
        const wrapper = shallowMount(App, {});
        await wrapper.find(".b-mul").trigger("click");
        await wrapper.find(".b-1").trigger("click");
        expect(wrapper.find(".digits").text()).toBe("1");
        await wrapper.find(".b-mul").trigger("click");
        expect(wrapper.find(".digits").text()).toBe("0");
        await wrapper.find(".b-ac").trigger("click");
        await wrapper.find(".b-2").trigger("click");
        await wrapper.find(".b-mul").trigger("click");
        await wrapper.find(".b-3").trigger("click");
        await wrapper.find(".b-mul").trigger("click");
        expect(wrapper.find(".digits").text()).toBe("6");
    });

    test("÷ button can divide numbers", async () => {
        const wrapper = shallowMount(App, {});
        await wrapper.find(".b-div").trigger("click");
        await wrapper.find(".b-1").trigger("click");
        expect(wrapper.find(".digits").text()).toBe("1");
        await wrapper.find(".b-div").trigger("click");
        expect(wrapper.find(".digits").text()).toBe("0");
        await wrapper.find(".b-ac").trigger("click");
        await wrapper.find(".b-3").trigger("click");;
        await wrapper.find(".b-div").trigger("click");
        await wrapper.find(".b-2").trigger("click");
        await wrapper.find(".b-div").trigger("click");
        expect(wrapper.find(".digits").text()).toBe("1.5");
    });

    test("% button can calculate remainder", async () => {
        const wrapper = shallowMount(App, {});
        await wrapper.find(".b-mod").trigger("click");
        await wrapper.find(".b-5").trigger("click");
        expect(wrapper.find(".digits").text()).toBe("5");
        await wrapper.find(".b-mod").trigger("click");
        expect(wrapper.find(".digits").text()).toBe("0");
        await wrapper.find(".b-ac").trigger("click");
        await wrapper.find(".b-5").trigger("click");
        await wrapper.find(".b-mod").trigger("click");
        await wrapper.find(".b-3").trigger("click");
        await wrapper.find(".b-mod").trigger("click");
        expect(wrapper.find(".digits").text()).toBe("2");
    });

    test("= button can perform operations", async () => {
        const wrapper = shallowMount(App, {});
        await wrapper.find(".b-eq").trigger("click");
        expect(wrapper.find(".digits").text()).toBe("0");
        await wrapper.find(".b-1").trigger("click");
        expect(wrapper.find(".digits").text()).toBe("1");
        await wrapper.find(".b-ac").trigger("click");
        await wrapper.find(".b-1").trigger("click");
        await wrapper.find(".b-add").trigger("click");
        await wrapper.find(".b-2").trigger("click");
        await wrapper.find(".b-eq").trigger("click");
        expect(wrapper.find(".digits").text()).toBe("3");
        await wrapper.find(".b-ac").trigger("click");
        await wrapper.find(".b-3").trigger("click");
        await wrapper.find(".b-sub").trigger("click");
        await wrapper.find(".b-4").trigger("click");
        await wrapper.find(".b-eq").trigger("click");
        expect(wrapper.find(".digits").text()).toBe("-1");
        await wrapper.find(".b-ac").trigger("click");
        await wrapper.find(".b-5").trigger("click");
        await wrapper.find(".b-mul").trigger("click");
        await wrapper.find(".b-6").trigger("click");
        await wrapper.find(".b-eq").trigger("click");
        expect(wrapper.find(".digits").text()).toBe("30");
        await wrapper.find(".b-ac").trigger("click");
        await wrapper.find(".b-7").trigger("click");
        await wrapper.find(".b-div").trigger("click");
        await wrapper.find(".b-8").trigger("click");
        await wrapper.find(".b-eq").trigger("click");
        expect(wrapper.find(".digits").text()).toBe("0.875");
        await wrapper.find(".b-ac").trigger("click");
        await wrapper.find(".b-9").trigger("click");
        await wrapper.find(".b-mod").trigger("click");
        await wrapper.find(".b-5").trigger("click");
        await wrapper.find(".b-eq").trigger("click");
        expect(wrapper.find(".digits").text()).toBe("4");
    });

    test("Results exceeding 12 digits are displayed with 8 significant figures", async () => {
        const wrapper = shallowMount(App, {});
        await wrapper.find(".b-1").trigger("click");
        await wrapper.find(".b-2").trigger("click");
        await wrapper.find(".b-3").trigger("click");
        await wrapper.find(".b-4").trigger("click");
        await wrapper.find(".b-5").trigger("click");
        await wrapper.find(".b-6").trigger("click");
        await wrapper.find(".b-7").trigger("click");
        await wrapper.find(".b-8").trigger("click");
        await wrapper.find(".b-9").trigger("click");
        await wrapper.find(".b-0").trigger("click");
        await wrapper.find(".b-1").trigger("click");
        await wrapper.find(".b-2").trigger("click");
        await wrapper.find(".b-mul").trigger("click");
        await wrapper.find(".b-1").trigger("click");
        await wrapper.find(".b-0").trigger("click");
        await wrapper.find(".b-eq").trigger("click");
        expect(wrapper.find(".digits").text()).toBe("1.2345679e+12");
    });

    test("Mouseover on the screen causes message displayed", async () => {
        const wrapper = shallowMount(App, {});
        expect(wrapper.find(".subscreen").text()).toBe("");
        await wrapper.find(".digits").trigger("mouseover");
        expect(wrapper.find(".subscreen").text()).toBe("C L I C K - TO - C O P Y 😊");
    });

    if (navigator.clipboard) {
        it("Click on the screen causes digits copied to the clip board", async () => {
            const wrapper = shallowMount(App, {});
            expect(wrapper.find(".subscreen").text()).toBe("");
            await wrapper.find(".digits").trigger("click");
            expect(wrapper.find(".subscreen").text()).toBe("C O P I E D ❤");
        });
    }
});

※Githubリポジトリに置いておきました。

vue-calculator/tests/App.test.js at main ?? macocci7/vue-calculator
A calculator app in Vue.js. Contribute to macocci7/vue-calculator development by creating an account on GitHub.

自動テストを実行してみます。

npm run test:unit

オールグリーンです。

ブラウザでの動作確認

WEBブラウザで http://localhost:5173/ をリロードします。

簡易電卓が表示されました。

13桁目を入力しようとするとスクリーンが赤く点滅して入力できません。

が、符号反転は受け付けます。

12桁を超える演算結果は8桁の有効数字で表示されています。

1.7976931348623157×10³⁰⁸を超える演算結果は「Infinity」になります。

スクリーンにマウスホバーで「CLICK-TO-COPY😊」が表示されます。

スクリーンクリックで「9.7546106e+23」がクリップボードにコピーされました。

ざっと見た限り、演算も問題なくできており、

仕様通りの実装ができているようです。

今回作成したコードはGithubリポジトリに登録しておきました。

GitHub - macocci7/vue-calculator: A calculator app in Vue.js
A calculator app in Vue.js. Contribute to macocci7/vue-calculator development by creating an account on GitHub.

バグや問題点があれば、上記リポジトリの「Issues」で報告してください。

  • 0
  • 0
  • 0
  • 0

コメント

タイトルとURLをコピーしました