UNPKG

can

Version:

MIT-licensed, client-side, JavaScript framework that makes building rich web applications easy.

5,268 lines 157 kB
<!DOCTYPE html>
<!--####################################################################
	THIS IS A GENERATED FILE -- ANY CHANGES MADE WILL BE OVERWRITTEN

	INSTEAD CHANGE:
	source: [object Object]
	@page guides/atm
######################################################################## -->
<html lang="en">
<head>
	<meta charset="utf-8">
	<title>CanJS - ATM Guide</title>
	<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
	
		<link rel="stylesheet" type="text/css" href="../static/bundles/bit-docs-site/static.css">
		<link rel="shortcut icon" sizes="16x16 24x24 32x32 48x48 64x64" href="/docs/images/canjs_favicon.ico">
		<link rel="apple-touch-icon" sizes="57x57" href="../../docs/images/canjs_favicon_57x57.png">
		<link rel="apple-touch-icon-precomposed" sizes="57x57" href="../../docs/images/canjs_favicon_57x57.png">
		<link rel="apple-touch-icon" sizes="72x72" href="../../docs/images/canjs_favicon_72x72.png">
		<link rel="apple-touch-icon" sizes="114x114" href="../../docs/images/canjs_favicon_114x114.png">
		<link rel="apple-touch-icon" sizes="120x120" href="../../docs/images/canjs_favicon_128x128.png">
		<link rel="apple-touch-icon" sizes="144x144" href="../../docs/images/canjs_favicon_144x144.png">
		<link rel="apple-touch-icon" sizes="152x152" href="../../docs/images/canjs_favicon_152x152.png">
		<meta content="yes" name="apple-mobile-web-app-capable">
	  	<meta name="apple-mobile-web-app-status-bar-style" content="white-translucent">
	
	
		<script>
			(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
				(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
				m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
			})(window,document,'script','https://www.google-analytics.com/analytics.js','ga');

			ga('create', 'UA-2302003-11', 'auto');
			ga('send', 'pageview');
		</script>
	
</head>
	<body>
		<input type="checkbox" id="nav-trigger" class="nav-trigger"/>
		<label for="nav-trigger">Menu</label>
	  	<div id="everything">
  <div id="left" class="column">
    <div class="top-left">
      
          <div class="brand">
	<div class="logo">
		<a href="../../index.html" alt="CanJS"></a>
		<div class="dropdown project-dropdown">
			<a href="https://donejs.com/">DoneJS</a>
			<a href="http://stealjs.com/">StealJS</a>
			<a href="http://jquerypp.com/">jQuery ++</a>
			<a href="https://funcunit.com/">FuncUnit</a>
			<a href="http://documentjs.com/">DocumentJS</a>
		</div>
	</div>
	<div class="version">
		<div class="version-number">
			3.0.0
		</div>
		<div class="dropdown version-dropdown">
			
				<a href="https://v2.canjs.com">2.3.27</a>
			
		</div>
	</div>
</div>

<div class="search-bar">
	<p>
		&nbsp;
	</p>
</div>

      
    </div>
    <div class="bottom-left">
       <div class="social-side-container">
        <ul class="social-side">
  <li>
    <a class="header-mobile github" href="https://github.com/canjs/canjs" target="_blank"><img class="social-icon-small" src="../../docs/images/github.png">Github</a>
  </li>
  <li>
    <a class="header-mobile twitter" href="https://twitter.com/canjs" target="_blank"><img class="social-icon-small" src="../../docs/images/twitter.png">Twitter</a>
  </li>
</ul>
<ul class="social-side">
  <li>
    <a class="header-mobile" href="https://gitter.im/canjs/canjs" target="_blank">Chat</a>
  </li>
  <li>
    <a class="header-mobile" href="http://forums.donejs.com/c/canjs" target="_blank">Forum</a>
  </li>
</ul>

      </div>
        
            
	<ul>
		
			
				
					<li class="
           						parent
           						expanded">
						<a class="page"
							href="../guides.html"
							title="Welcome to CanJS! These guides are here to help you develop and improve your relationship with CanJS. After all, picking a JavaScript framework is a commitment.  We want CanJS to be the
framework you marry.  This page helps you know how advance through the different stages of this
relationship:">
							Guides
						</a>
						
							
	<ul>
		
			
				
					<li>
						<span>introduction</span>
						
	<ul>
		
			
		
			
				
					<li class="
           						
           						">
						<a class="page"
							href="mission.html"
							title="Learn about CanJS&#x27;s mission, why it matters, and how we&#x27;ve worked (and will keep working) to accomplish it.">
							Mission
						</a>
						
					</li>
				
			
		
			
				
					<li class="
           						
           						">
						<a class="page"
							href="technical.html"
							title="">
							Technical Highlights
						</a>
						
					</li>
				
			
		
			
		
			
				
					<li class="
           						
           						">
						<a class="page"
							href="who-uses-canjs.html"
							title="">
							Who uses CanJS?
						</a>
						
					</li>
				
			
		
	</ul>


					</li>
				
			
		
			
				
					<li>
						<span>experiment</span>
						
	<ul>
		
			
				
					<li class="
           						
           						">
						<a class="page"
							href="chat.html"
							title="This guide walks through building real time chat application with CanJS&#x27;s Core libraries.  It takes about 30 minutes to complete.">
							Chat Guide
						</a>
						
					</li>
				
			
		
			
				
					<li class="
           						
           						">
						<a class="page"
							href="todomvc.html"
							title="This guide walks through building a slightly modified version of TodoMVC with CanJS&#x27;s Core libraries and can-fixture. It takes about 1 hour to complete.">
							TodoMVC Guide
						</a>
						
					</li>
				
			
		
			
				
					<li class="current
           						parent
           						expanded">
						<a class="page"
							href="atm.html"
							title="This guide walks through building and testing an ATM application with CanJS&#x27;s
Core libraries.  It teaches how to do test driven development (TDD)
and manage complex state.  It takes about 2 hours to complete.">
							ATM Guide
						</a>
						
							

						
					</li>
				
			
		
			
				
					<li class="
           						
           						">
						<a class="page"
							href="setup.html"
							title="CanJS is packaged in multiple ways so that it can fit into any development workflow. Learn how to setup CanJS in different environments.">
							Setting up CanJS
						</a>
						
					</li>
				
			
		
	</ul>


					</li>
				
			
		
			
				
					<li>
						<span>commitment</span>
						
	<ul>
		
			
				
					<li class="
           						
           						">
						<a class="page"
							href="api.html"
							title="This page walks through how to use and understand CanJS&#x27;s API documentation.">
							API Guide
						</a>
						
					</li>
				
			
		
			
				
					<li class="
           						
           						">
						<a class="page"
							href="examples.html"
							title="">
							Examples
						</a>
						
					</li>
				
			
		
			
				
					<li class="
           						
           						">
						<a class="page"
							href="../roadmap.html"
							title="Learn about CanJS&#x27;s future plans and how we make them, and how you can influence them.">
							Roadmap
						</a>
						
					</li>
				
			
		
			
				
					<li class="
           						
           						">
						<a class="page"
							href="../migrate-3.html"
							title="">
							Migrating to 3.0
						</a>
						
					</li>
				
			
		
	</ul>


					</li>
				
			
		
			
				
					<li>
						<span>contribute</span>
						
	<ul>
		
			
				
					<li class="
           						
           						">
						<a class="page"
							href="contributing/bug-report.html"
							title="Learn how to submit a bug report.">
							Bug Report
						</a>
						
					</li>
				
			
		
			
				
					<li class="
           						
           						">
						<a class="page"
							href="contributing/code.html"
							title="Learn how contribute a code change to CanJS.">
							Code
						</a>
						
					</li>
				
			
		
			
				
					<li class="
           						
           						">
						<a class="page"
							href="contributing/documentation.html"
							title="Learn how to improve CanJS&#x27;s site and documentation.">
							Documentation
						</a>
						
					</li>
				
			
		
			
				
					<li class="
           						
           						">
						<a class="page"
							href="contributing/evangelism.html"
							title="Learn about resources that can help you spread the word about CanJS.">
							Evangelism
						</a>
						
					</li>
				
			
		
			
				
					<li class="
           						
           						">
						<a class="page"
							href="contributing/feature-suggestion.html"
							title="Learn how to suggest a feature.">
							Feature Suggestion
						</a>
						
					</li>
				
			
		
			
				
					<li class="
           						
           						">
						<a class="page"
							href="contributing/releases.html"
							title="Release and hosting information for CanJS maintainers.">
							Releases
						</a>
						
					</li>
				
			
		
	</ul>


					</li>
				
			
		
	</ul>


						
					</li>
				
			
		
			
				
					<li class="
           						
           						">
						<a class="page"
							href="../can-core.html"
							title="The best, most hardened and generally useful libraries in CanJS.">
							Core
						</a>
						
					</li>
				
			
		
			
				
					<li class="
           						
           						">
						<a class="page"
							href="../can-ecosystem.html"
							title="Useful libraries that extend or add important features to the core collection.">
							Ecosystem
						</a>
						
					</li>
				
			
		
			
				
					<li class="
           						
           						">
						<a class="page"
							href="../can-infrastructure.html"
							title="Utility libraries that power the core and ecosystem collection.">
							Infrastructure
						</a>
						
					</li>
				
			
		
			
				
					<li class="
           						
           						">
						<a class="page"
							href="../can-legacy.html"
							title="Former libraries that we still accept patches for, but are not under active development.">
							Legacy
						</a>
						
					</li>
				
			
		
	</ul>


        
    </div>
  </div>
  <div id="right" class="column">
    <div class="top-right">
      <div class="top-right-top">
	<ul class="top-right-bitovi">
	  	<li class="dropdown">
	    	<a href="http://bitovi.com" class="bitovi icon-bits">Bitovi</a>
	    	<ul class="dropdown-menu">
	      		<li><a href="http://bitovi.com">Bitovi.com</a></li>
	      		<li><a href="http://bitovi.com/blog/">Blog</a></li>
	      		<li><a href="http://bitovi.com/consulting/">Consulting</a></li>
	      		<li><a href="http://bitovi.com/training/">Training</a></li>
	      		<li><a href="http://bitovi.com/open-source/">Open Source</a></li>
	    	</ul>
	  	</li>
	</ul>
	<div class="brand">
		<div class="logo">
			<a href="../../index.html" alt="CanJS"></a>
		</div>
	</div>
	<ul class="top-right-links">
  <li>
    <a href="https://gitter.im/canjs/canjs">Chat</a>
  </li>
  <li>
    <a href="http://forums.donejs.com/c/canjs">Forum</a>
  </li>
  <li>
    <a class="github-button nav-social" href="https://github.com/canjs/canjs" data-count-href="/canjs/canjs/stargazers" data-count-api="/repos/canjs/canjs#stargazers_count">Star</a>
  </li>
  <li>
    <a href="https://twitter.com/canjs" class="twitter-follow-button nav-social" data-show-count="true" data-show-screen-name="false">Follow @canjs</a><script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>
  </li>
</ul>
</div>
<div class="breadcrumb">
	
		<li><a href="../../index.html">CanJS</a></li> /
	
		<li><a href="../guides.html">Guides</a></li> /
	
	
	<li><a href="atm.html">ATM Guide</a></li>
	
	<li class="breadcrumb-dropdown">/ <a> On this page</a>
	  <ul class="on-this-page"></ul>
	</li>
	<div class="nav-toggle" title="Back to top"></div>
</div>


    </div>
    <div class="bottom-right">
      <article>
  <section class="title">
	<div class="page-type">
		<h1>ATM Guide</h1>
		<div>page</div>
	</div>
	
  
	<section class="description">
    <p>This guide walks through building and <strong>testing</strong> an ATM application with CanJS's
<a href="../can-core.html" title="The best, most hardened and generally useful libraries in CanJS.">Core libraries</a>.  It teaches how to do test driven development (TDD)
and manage complex state.  It takes about 2 hours to complete.</p>

</section>

  
	
</section>
<section class="on-this-page-table">
</section>
<section class="title-footer">
	
	
		<ul class="title-links">
			<!-- <li><a href="#">docco</a></li> -->
			<li><a href="//github.com/canjs/canjs/tree/v3.0.0/docs/can-guides/experiment/atm/atm.md">source</a></li>
			<!-- <li><a href="#">download</a></li> -->
			<!-- <li><a href="#">tests</a></li> -->
		</ul>
	
</section>










  
    <section class="body">
    <h2>Overview</h2>
<p>Checkout the final app:</p>
<p><a class="jsbin-embed" href="http://jsbin.com/yayupo/10/embed?js,output">JS Bin on jsbin.com</a></p>
<p>Notice it has tests at the bottom of the <code>Output</code> tab.</p>
<p>Watch the following video to see it in action:</p>
<blockquote>
<p>TODO: VIDEO OF IT IN ACTION</p>
</blockquote>
<h2>Setup</h2>
<p>The easiest way to get started is to clone the following JSBin by clicking the <strong>JS Bin</strong> button on the top left:</p>
<p><a class="jsbin-embed" href="http://justinbmeyer.jsbin.com/meziyu/3/edit?html,js,output">JS Bin on jsbin.com</a></p>
<p>The JSBin is designed to run both the application and its tests in the <code>OUTPUT</code>
tab.  To set this up, the <code>HTML</code> tab:</p>
<ul>
<li><p>Loads QUnit for its testing library.  It also includes the <code>&lt;div id=&quot;qunit&quot;&gt;&lt;/div&gt;</code>
element where QUnit's test results will be written to.</p></li>
<li><p>Loads <a href="https://unpkg.com/can/dist/global/can.all.js">can.all.js</a>, which
is a script that includes all of CanJS core under a single global <code>can</code> namespace.</p>
<p>Generally speaking, you should not use the global can script and instead
should import things directly with a module loader like <a href="http://stealjs.com">StealJS</a>,
WebPack or Browserify.  Read <a href="setup.html" title="CanJS is packaged in multiple ways so that it can fit into any development workflow. Learn how to setup CanJS in different environments.">Setting up CanJS</a> on how to setup CanJS in a real app.</p></li>
<li><p>Includes the content for a <code>app-template</code> <a href="../can-stache.html" title="Live binding Mustache and Handlebars-compatible templates.">can-stache</a> template. This template
provides the title for the ATM app, and uses the <code>&lt;atm-machine&gt;</code> custom <a href="../can-component.html" title="Create a custom element that can be used to manage widgets or application logic.">can-component</a>
element that will eventually provide the ATM functionality.</p></li>
</ul>
<p>The <code>JS</code> tab is split into two sections:</p>
<ul>
<li><code>CODE</code> - The ATM's models, view-models and component code will go here.</li>
<li><code>TESTS</code> - The ATM's tests will go here.</li>
</ul>
<p>Normally, your application's code and test will be in separate files and loaded
by different html pages.  But we combine them here to fit within JSBin's limitations.</p>
<p>The <code>CODE</code> section is rendering the <code>app-template</code> with:</p>
<pre><code class="language-js">document.body.insertBefore(
    can.stache.from(&quot;app-template&quot;)({}),
    document.body.firstChild
);
</code></pre>
<p>The <code>TESTS</code> section is labeling which module will be tested:</p>
<pre><code class="language-js">QUnit.module(&quot;ATM system&quot;, {});
</code></pre>
<h2>Mock out switching between pages</h2>
<p>In this section, we will mock out which pages will be shown as the <code>state</code>
of the <code>ATM</code> changes.</p>
<p>Update the <code>HTML</code> tab to:</p>
<ul>
<li>Switch between different pages of the application as the <code>ATM</code> view-model's <code>state</code> property changes
with <a href="../can-stache.helpers.switch.html" title="">{{#switch expression}}</a>.</li>
</ul>
<pre><code class="language-html">&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;meta name=&quot;description&quot; content=&quot;CanJS 3.0 - ATM Guide - Setup&quot;&gt;
  &lt;meta charset=&quot;utf-8&quot;&gt;
  &lt;meta name=&quot;viewport&quot; content=&quot;width=device-width&quot;&gt;
  &lt;title&gt;JS Bin&lt;/title&gt;

&lt;/head&gt;
&lt;body&gt;

&lt;script type='text/stache' id='atm-template'&gt;
&lt;div class=&quot;screen&quot;&gt;
    &lt;div class=&quot;screen-content&quot;&gt;
        &lt;div class=&quot;screen-glass&quot;&gt;

{{#switch state}}
    {{#case &quot;readingCard&quot;}}
        &lt;h2&gt;Reading Card&lt;/h2&gt;
    {{/case}}

    {{#case &quot;readingPin&quot;}}
        &lt;h2&gt;Reading Pin&lt;/h2&gt;
    {{/case}}

    {{#case &quot;choosingTransaction&quot;}}
        &lt;h2&gt;Choose Transaction&lt;/h2&gt;
    {{/case}}

    {{#case  &quot;pickingAccount&quot;}}
        &lt;h2&gt;Pick Account&lt;/h2&gt;
    {{/case}}

    {{#case &quot;depositInfo&quot;}}
        &lt;h2&gt;Deposit&lt;/h2&gt;
    {{/case}}

    {{#case &quot;withdrawalInfo&quot;}}
        &lt;h2&gt;Withdraw&lt;/h2&gt;
    {{/case}}

    {{#case &quot;successfulTransaction&quot;}}
        &lt;h2&gt;Transaction Successful!&lt;/h2&gt;
    {{/case}}

    {{#case  &quot;printingReceipt&quot;}}
        &lt;h2&gt;Printing Receipt&lt;/h2&gt;
    {{/case}}

    {{#default}}
        &lt;h2&gt;Error&lt;/h2&gt;
        &lt;p&gt;Invalid state - {{state}}&lt;/p&gt;
    {{/default}}

{{/switch}}

        &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;
&lt;/script&gt;

&lt;script type='text/stache' id='app-template'&gt;
&lt;div class=&quot;title&quot;&gt;
    &lt;h1&gt;canATM&lt;/h1&gt;
&lt;/div&gt;
&lt;atm-machine/&gt;
&lt;/script&gt;

&lt;script src=&quot;https://code.jquery.com/jquery-2.2.4.js&quot;&gt;&lt;/script&gt;
&lt;script src=&quot;https://unpkg.com/can/dist/global/can.all.js&quot;&gt;&lt;/script&gt;

&lt;div id=&quot;qunit&quot;&gt;&lt;/div&gt;
&lt;link rel=&quot;stylesheet&quot; href=&quot;http://code.jquery.com/qunit/qunit-1.12.0.css&quot;&gt;
&lt;script src=&quot;http://code.jquery.com/qunit/qunit-1.12.0.js&quot;&gt;&lt;/script&gt;

&lt;/body&gt;
&lt;/html&gt;

</code></pre>
<p><span line-highlight='12-60,only'></span>
Update the <code>JavaScript</code> tab to:</p>
<ul>
<li>Create the <code>ATM</code> view-model with a <code>state</code> property initialized to <code>readingCard</code> with <a href="../can-define/map/map.html" title="Create observable objects.">can-define/map/map</a>.</li>
<li>Create an <code>&lt;atm-machine&gt;</code> custom element with <a href="../can-component.html" title="Create a custom element that can be used to manage widgets or application logic.">can-component</a>.</li>
</ul>
<pre><code class="language-js">// ========================================
// CODE
// ========================================

var ATM = can.DefineMap.extend({
    state: {type: &quot;string&quot;, value: &quot;readingCard&quot;}
});

can.Component.extend({
    tag: &quot;atm-machine&quot;,
    view: can.stache.from(&quot;atm-template&quot;),
    ViewModel: ATM,
});

document.body.insertBefore(
    can.stache.from(&quot;app-template&quot;)({}),
    document.body.firstChild
);

// ========================================
// TESTS
// ========================================

QUnit.module(&quot;ATM system&quot;, {});

</code></pre>
<p><span line-highlight='5-13,only'></span>
When complete, you should see the <strong>&quot;Reading Card&quot;</strong> title.</p>
<p>This step outlines the page transitions we're going to make the <code>state</code>
property transition between:</p>
<ul>
<li>readingCard</li>
<li>readingPin</li>
<li>choosingTransaction</li>
<li>pickingAccount</li>
<li>depositInfo</li>
<li>withdrawalInfo</li>
<li>successfulTransaction</li>
<li>printingReceipt</li>
</ul>
<p>Each of those states are present in the following state diagram.</p>
<p><img src="../../docs/can-guides/experiment/atm/1-pages-template/state-diagram.png"></p>
<p>We'll build out these pages once we build the <code>Card</code> and <code>Transaction</code> sub-models that will make building the ATM view model easier.</p>
<h2>Card tests</h2>
<p>In this section, we will:</p>
<ul>
<li>Design an API for an ATM <code>Card</code></li>
<li>Write out tests for the card.</li>
</ul>
<p>An ATM <code>Card</code> will take a card <code>number</code> and <code>pin</code>. It will start out as
having a <code>state</code> of  <code>&quot;unverified&quot;</code>. It will have a <code>verify</code> method
that will change the <code>state</code> to <code>&quot;verifying&quot;</code> and if the response is successful,
<code>state</code> will change to <code>&quot;verified&quot;</code>.</p>
<p>Update the <code>JS</code> tab to:</p>
<ul>
<li>Make the fake data request delay <code>1ms</code> by setting <a href="../can-fixture.delay.html" title="can-fixture.delay">delay</a> to <code>1</code> before every test and
restoring it to <code>2s</code> after every test runs.</li>
<li>Write a test that creates a valid card, calls <code>.verify()</code>, and asserts the <code>state</code> is <code>&quot;verified&quot;</code>.</li>
<li>Write a test that creates a invalid card, calls <code>.verify()</code>, and asserts the <code>state</code> is <code>&quot;invalid&quot;</code>.</li>
</ul>
<pre><code class="language-js">// ========================================
// CODE
// ========================================

var ATM = can.DefineMap.extend({
    state: {type: &quot;string&quot;, value: &quot;readingCard&quot;}
});

can.Component.extend({
    tag: &quot;atm-machine&quot;,
    view: can.stache.from(&quot;atm-template&quot;),
    ViewModel: ATM,
});

document.body.insertBefore(
    can.stache.from(&quot;app-template&quot;)({}),
    document.body.firstChild
);

// ========================================
// TESTS
// ========================================

QUnit.module(&quot;ATM system&quot;, {
    setup: function() {
        can.fixture.delay = 1;
    },
    teardown: function() {
        can.fixture.delay = 2000;
    }
});

QUnit.asyncTest(&quot;Valid Card&quot;, function() {

    var c = new Card({
        number: &quot;01234567890&quot;,
        pin: 1234
    });

    QUnit.equal(c.state, &quot;unverified&quot;);

    c.verify();

    QUnit.equal(c.state, &quot;verifying&quot;, &quot;card is verifying&quot;);

    c.on(&quot;state&quot;, function(ev, newVal) {

        QUnit.equal(newVal, &quot;verified&quot;, &quot;card is verified&quot;);

        QUnit.start();
    });
});

QUnit.asyncTest(&quot;Invalid Card&quot;, function() {

    var c = new Card({});

    QUnit.equal(c.state, &quot;unverified&quot;);

    c.verify();

    QUnit.equal(c.state, &quot;verifying&quot;, &quot;card is verifying&quot;);

    c.on(&quot;state&quot;, function(ev, newVal) {

        QUnit.equal(newVal, &quot;invalid&quot;, &quot;card is invalid&quot;);

        QUnit.start();
    });
});

</code></pre>
<p><span line-highlight='24-70,only'></span>
When complete you should have a breaking test.  Now lets make it pass.</p>
<h2>Card model</h2>
<p>In this section, we will:</p>
<ul>
<li>Implement the <code>Card</code> model so that all tests pass.</li>
</ul>
<p>Update the <code>JavaScript</code> tab to:</p>
<ul>
<li>Simulate the <code>/verifyCard</code> with <a href="../can-fixture.html" title="can-fixture intercepts an AJAX request and simulates the response with a file or function.">can-fixture</a>. It return a successful response if
the request body has a <code>number</code> and <code>pin</code> and a 400 if not.</li>
<li>Use <a href="../can-define/map/map.html" title="Create observable objects.">can-define/map/map</a> to define the <code>Card</code> model, including:
<ul>
<li>a <code>number</code> and  <code>pin</code> property.</li>
<li>a <code>state</code> property initialized to <code>unverified</code> that is not part of the card's <a href="../can-define.types.serialize.html" title="Defines custom serialization behavior for a property.">serialize</a>d  data.</li>
<li>a <code>verify</code> method that posts the card's data to <code>/verifyCard</code> and updates the <code>state</code>
accordingly.</li>
</ul></li>
</ul>
<pre><code class="language-js">// ========================================
// CODE
// ========================================

can.fixture({
    &quot;/verifyCard&quot;: function(request, response) {
        if (!request.data || !request.data.number || !request.data.pin) {
            response(400, {});
        } else {
            return {};
        }
    }
});
can.fixture.delay = 1000;

var Card = can.DefineMap.extend({
    number: &quot;string&quot;,
    pin: &quot;string&quot;,
    state: {
        value: &quot;unverified&quot;,
        serialize: false
    },
    verify: function() {

        this.state = &quot;verifying&quot;;

        var self = this;
        return can.ajax({
            type: &quot;POST&quot;,
            url: &quot;/verifyCard&quot;,
            data: this.serialize()
        }).then(
            function() {
                self.state = &quot;verified&quot;;
                return self;
            },
            function() {
                self.state = &quot;invalid&quot;;
                return self;
            });
    }
});

var ATM = can.DefineMap.extend({
    state: {type: &quot;string&quot;, value: &quot;readingCard&quot;}
});

can.Component.extend({
    tag: &quot;atm-machine&quot;,
    view: can.stache.from(&quot;atm-template&quot;),
    ViewModel: ATM,
});

document.body.insertBefore(
    can.stache.from(&quot;app-template&quot;)({}),
    document.body.firstChild
);

// ========================================
// TESTS
// ========================================

QUnit.module(&quot;ATM system&quot;, {
    setup: function() {
        can.fixture.delay = 1;
    },
    teardown: function() {
        can.fixture.delay = 2000;
    }
});

QUnit.asyncTest(&quot;Valid Card&quot;, function() {

    var c = new Card({
        number: &quot;01234567890&quot;,
        pin: 1234
    });

    QUnit.equal(c.state, &quot;unverified&quot;);

    c.verify();

    QUnit.equal(c.state, &quot;verifying&quot;, &quot;card is verifying&quot;);

    c.on(&quot;state&quot;, function(ev, newVal) {

        QUnit.equal(newVal, &quot;verified&quot;, &quot;card is verified&quot;);

        QUnit.start();
    });
});

QUnit.asyncTest(&quot;Invalid Card&quot;, function() {

    var c = new Card({});

    QUnit.equal(c.state, &quot;unverified&quot;);

    c.verify();

    QUnit.equal(c.state, &quot;verifying&quot;, &quot;card is verifying&quot;);

    c.on(&quot;state&quot;, function(ev, newVal) {

        QUnit.equal(newVal, &quot;invalid&quot;, &quot;card is invalid&quot;);

        QUnit.start();
    });
});

</code></pre>
<p><span line-highlight='5-42,only'></span>
When complete, all tests should pass.</p>
<p>In this step, you implemented a <code>Card</code> model that encapsulates the behavior of its own state.</p>
<h2>Deposit test</h2>
<p>In this section, we will:</p>
<ul>
<li>Design an API retrieving <code>Account</code>s.</li>
<li>Design an API for a <code>Deposit</code> type.</li>
<li>Write out tests for the <code>Deposit</code> type.</li>
</ul>
<p>An <code>Account</code> will have an <code>id</code>, <code>name</code>, and <code>balance</code>.  We'll use <a href="../can-connect.html" title="can-connect provides persisted data middleware. Use it to assemble powerful model layers for any JavaScript project.">can-connect</a> to add a
<a href="../can-connect/can/map/map.getList.html" title="Gets a list of instances of the map type.">getList</a> method that retrieves an account given a <code>card</code>.</p>
<p>A <code>Deposit</code> will take a <code>card</code>, an <code>amount</code>, and an <code>account</code>.  Deposits will start out having
a <code>state</code> of <code>&quot;invalid&quot;</code>.  When the deposit has a <code>card</code>, <code>amount</code> and <code>account</code>, the <code>state</code>
will change to <code>&quot;ready&quot;</code>.  Once the deposit is ready, the <code>.execute()</code> method will change the state
to <code>&quot;executing&quot;</code> and then to <code>&quot;executed&quot;</code> once the transaction completes.</p>
<p>Update the <code>JS</code> tab to:</p>
<ul>
<li>Create a <code>deposit</code> with an <code>amount</code> and a <code>card</code>.</li>
<li>Check that the <code>state</code> is <code>&quot;invalid&quot;</code> because there is no <code>account</code>.</li>
<li>Use <code>Account.getList</code> to get the accounts for the card and:
<ul>
<li>set the <code>deposit.accounts</code> to the first account.</li>
<li>remember the starting <code>balance</code>.</li>
</ul></li>
<li>Use <a href="../can-define/map/map.prototype.on.html" title="Add event handlers to a map.">on</a> to listen for <code>state</code> changes. When <code>state</code> is:
<ul>
<li><code>&quot;ready&quot;</code>, <code>.execute()</code> the transaction.</li>
<li><code>&quot;executed&quot;</code>, verify the new account balance.</li>
</ul></li>
</ul>
<pre><code class="language-js">// ========================================
// CODE
// ========================================

can.fixture({
    &quot;/verifyCard&quot;: function(request, response) {
        if (!request.data || !request.data.number || !request.data.pin) {
            response(400, {});
        } else {
            return {};
        }
    }
});
can.fixture.delay = 1000;

var Card = can.DefineMap.extend({
    number: &quot;string&quot;,
    pin: &quot;string&quot;,
    state: {
        value: &quot;unverified&quot;,
        serialize: false
    },
    verify: function() {

        this.state = &quot;verifying&quot;;

        var self = this;
        return can.ajax({
            type: &quot;POST&quot;,
            url: &quot;/verifyCard&quot;,
            data: this.serialize()
        }).then(
            function() {
                self.state = &quot;verified&quot;;
                return self;
            },
            function() {
                self.state = &quot;invalid&quot;;
                return self;
            });
    }
});

var ATM = can.DefineMap.extend({
    state: {type: &quot;string&quot;, value: &quot;readingCard&quot;}
});

can.Component.extend({
    tag: &quot;atm-machine&quot;,
    view: can.stache.from(&quot;atm-template&quot;),
    ViewModel: ATM,
});

document.body.insertBefore(
    can.stache.from(&quot;app-template&quot;)({}),
    document.body.firstChild
);

// ========================================
// TESTS
// ========================================

QUnit.module(&quot;ATM system&quot;, {
    setup: function() {
        can.fixture.delay = 1;
    },
    teardown: function() {
        can.fixture.delay = 2000;
    }
});

QUnit.asyncTest(&quot;Valid Card&quot;, function() {

    var c = new Card({
        number: &quot;01234567890&quot;,
        pin: 1234
    });

    QUnit.equal(c.state, &quot;unverified&quot;);

    c.verify();

    QUnit.equal(c.state, &quot;verifying&quot;, &quot;card is verifying&quot;);

    c.on(&quot;state&quot;, function(ev, newVal) {

        QUnit.equal(newVal, &quot;verified&quot;, &quot;card is verified&quot;);

        QUnit.start();
    });
});

QUnit.asyncTest(&quot;Invalid Card&quot;, function() {

    var c = new Card({});

    QUnit.equal(c.state, &quot;unverified&quot;);

    c.verify();

    QUnit.equal(c.state, &quot;verifying&quot;, &quot;card is verifying&quot;);

    c.on(&quot;state&quot;, function(ev, newVal) {

        QUnit.equal(newVal, &quot;invalid&quot;, &quot;card is invalid&quot;);

        QUnit.start();
    });
});

QUnit.asyncTest(&quot;Deposit&quot;, 6, function() {

    var card = new Card({
        number: &quot;0123456789&quot;,
        pin: &quot;1122&quot;
    });

    var deposit = new Deposit({
        amount: 100,
        card: card
    });

    equal(deposit.state, &quot;invalid&quot;);

    var startingBalance;

    Account.getList(card.serialize()).then(function(accounts) {
        QUnit.ok(true, &quot;got accounts&quot;);
        deposit.account = accounts[0];
        startingBalance = accounts[0].balance;
    });

    deposit.on(&quot;state&quot;, function(ev, newVal) {
        if (newVal === &quot;ready&quot;) {

            QUnit.ok(true, &quot;deposit is ready&quot;);
            deposit.execute();

        } else if (newVal === &quot;executing&quot;) {

            QUnit.ok(true, &quot;executing a deposit&quot;);

        } else if (newVal === &quot;executed&quot;) {

            QUnit.ok(true, &quot;executed a deposit&quot;);
            equal(deposit.account.balance, 100 + startingBalance);
            start();

        }
    });
});

</code></pre>
<p><span line-highlight='111-151,only'></span>
When complete, the <strong>Deposit</strong> test should run, but error because <em>Deposit is not defined</em>.</p>
<blockquote>
<p><strong>Optional:</strong> Challenge yourself by writing the <strong>Withdrawal</strong> test on your own.  How is it different than the <strong>Deposit</strong> test?</p>
</blockquote>
<h2>Transaction, Deposit, and Withdrawal models</h2>
<p>In this section, we will:</p>
<ul>
<li>Implement the <code>Account</code> model.</li>
<li>Implement a base <code>Transaction</code> model and extend it into a <code>Deposit</code> and
<code>Withdrawal</code> model.</li>
<li>Get the <strong>Deposit</strong> test to pass.</li>
</ul>
<p>Update the <code>JavaScript</code> tab to:</p>
<ul>
<li>Simulate <code>/accounts</code> to return <code>Account</code> data with <a href="../can-fixture.html" title="can-fixture intercepts an AJAX request and simulates the response with a file or function.">can-fixture</a>.</li>
<li>Simulate <code>/deposit</code> to always return a successful result.</li>
<li>Simulate <code>/withdrawal</code> to always return a successful result.</li>
<li>Define the <code>Account</code> model to:
<ul>
<li>have an <code>id</code> property</li>
<li>have a <code>balance</code> property</li>
<li>have a <code>name</code> property</li>
</ul></li>
<li>Define an <code>Account.List</code> type with <a href="../can-define/list/list.html" title="Create observable lists.">can-define/list/list</a></li>
<li>Connect <code>Account</code> and <code>Account.List</code> types to the restful <code>&quot;/accounts&quot;</code> endpoint using <a href="../can-connect/can/base-map/base-map.html" title="Create connection with many of the best behaviors in can-connect and hook it up to
a map.">can-connect/can/base-map/base-map</a>.</li>
<li>Define the <code>Transaction</code> model to:
<ul>
<li>have an <code>account</code> and <code>card</code> property.</li>
<li>have an <code>executing</code> and <code>executed</code> property that track if the transaction is executing or has executed.</li>
<li>have a <code>rejected</code> property that stores the error given for a failed transaction.</li>
<li>have an <strong>abstract</strong> <code>ready</code> property that <code>Deposit</code> and <code>Withdrawal</code> will implement to return <code>true</code>
when the transaction is in a state able to be executed.</li>
<li>have a <code>state</code> property that reads other stateful properties and returns a string representation
of the state.</li>
<li>have an <strong>abstract</strong> <code>executeStart</code> method that <code>Deposit</code> and <code>Withdrawal</code> will implement to
execute the transaction and return a <code>Promise</code> the resolves when the transaction completes.</li>
<li>have an <strong>abstract</strong> <code>executeEnd</code> method that <code>Deposit</code> and <code>Withdrawal</code> will implement to
update the transactions values (typically the <code>account</code> balance) if the transaction completed
successfully.</li>
<li>have an <code>execute</code> method that calls <code>.executeStart()</code> and <code>executeEnd()</code> and keeps the stateful
properties updated correctly.</li>
</ul></li>
<li>Define the <code>Deposit</code> model to:
<ul>
<li>have an <code>amount</code> property.</li>
<li>implement <code>ready</code> to return <code>true</code> when the amount is greater than <code>0</code> and there's an <code>account</code>
and <code>card</code>.</li>
<li>implement <code>executeStart</code> to <code>POST</code> the deposit information to <code>/deposit</code></li>
<li>implement <code>executeEnd</code> to update the account balance.</li>
</ul></li>
<li>Define the <code>Withdrawal</code> model to behave in the same way as <code>Deposit</code> except that
it <code>POST</code>s the withdrawal information to <code>/withdrawal</code>.</li>
</ul>
<pre><code class="language-js">// ========================================
// CODE
// ========================================

can.fixture({
    &quot;/verifyCard&quot;: function(request, response) {
        if (!request.data || !request.data.number || !request.data.pin) {
            response(400, {});
        } else {
            return {};
        }
    },
    &quot;/accounts&quot;: function() {
        return {
            data: [{
                balance: 100,
                id: 1,
                name: &quot;checking&quot;
            }, {
                balance: 10000,
                id: 2,
                name: &quot;savings&quot;
            }]
        };
    },
    &quot;/deposit&quot;: function() {
        return {};
    },
    &quot;/withdrawal&quot;: function() {
        return {};
    }
});
can.fixture.delay = 1000;

var Card = can.DefineMap.extend({
    number: &quot;string&quot;,
    pin: &quot;string&quot;,
    state: {
        value: &quot;unverified&quot;,
        serialize: false
    },
    verify: function() {

        this.state = &quot;verifying&quot;;

        var self = this;
        return can.ajax({
            type: &quot;POST&quot;,
            url: &quot;/verifyCard&quot;,
            data: this.serialize()
        }).then(
            function() {
                self.state = &quot;verified&quot;;
                return self;
            },
            function() {
                self.state = &quot;invalid&quot;;
                return self;
            });
    }
});

var Account = can.DefineMap.extend(&quot;Account&quot;, {
    id: &quot;number&quot;,
    balance: &quot;number&quot;,
    name: &quot;string&quot;
});
Account.List = can.DefineList.extend(&quot;AccountList&quot;, {
    &quot;*&quot;: Account
});

can.connect.baseMap({
    url: &quot;/accounts&quot;,
    Map: Account,
    List: Account.List,
    name: &quot;accounts&quot;
});

var Transaction = can.DefineMap.extend({
    account: Account,
    card: Card,
    executing: {
        type: &quot;boolean&quot;,
        value: false
    },
    executed: {
        type: &quot;boolean&quot;,
        value: false
    },
    rejected: &quot;any&quot;,
    get ready(){
        throw new Error(&quot;Transaction::ready must be provided by extended type&quot;);
    },
    get state() {
        if (this.rejected) {
            return &quot;rejected&quot;;
        }
        if (this.executed) {
            return &quot;executed&quot;;
        }
        if (this.executing) {
            return &quot;executing&quot;;
        }
        // make sure there's an amount, account, and card
        if (this.ready) {
            return &quot;ready&quot;;
        }
        return &quot;invalid&quot;;
    },
    executeStart: function(){
        throw new Error(&quot;Transaction::executeStart must be provided by extended type&quot;);
    },
    executeEnd: function(){
        throw new Error(&quot;Transaction::executeEnd must be provided by extended type&quot;);
    },
    execute: function() {
        if (this.state === &quot;ready&quot;) {

            this.executing = true;

            var def = this.executeStart(),
                self = this;

            def.then(function() {
                can.batch.start();
                self.set({
                    executing: false,
                    executed: true
                });
                self.executeEnd();
                can.batch.stop();
            }, function(reason){
                self.set({
                    executing: false,
                    executed: true,
                    rejected: reason
                });
            });
        }
    }
});

var Deposit = Transaction.extend({
    amount: &quot;number&quot;,
    get ready() {
        return this.amount &gt; 0 &amp;&amp;
            this.account &amp;&amp;
            this.card;
    },
    executeStart: function() {
        return can.ajax({
            type: &quot;POST&quot;,
            url: &quot;/deposit&quot;,
            data: {
                card: this.card.serialize(),
                accountId: this.account.id,
                amount: this.amount
            }
        });
    },
    executeEnd: function(data) {
        this.account.balance = this.account.balance + this.amount;
    }
});

var Withdrawal = Transaction.extend({
    amount: &quot;number&quot;,
    get ready() {
        return this.amount &gt; 0 &amp;&amp;
            this.account &amp;&amp;
            this.card;
    },
    executeStart: function() {
        return can.ajax({
            type: &quot;POST&quot;,
            url: &quot;/withdrawal&quot;,
            data: {
                card: this.card.serialize(),
                accountId: this.account.id,
                amount: this.amount
            }
        });
    },
    executeEnd: function(data) {
        this.account.balance = this.account.balance - this.amount;
    }
});

var ATM = can.DefineMap.extend({
    state: {type: &quot;string&quot;, value: &quot;readingCard&quot;}
});

can.Component.extend({
    tag: &quot;atm-machine&quot;,
    view: can.stache.from(&quot;atm-template&quot;),
    ViewModel: ATM,
});

document.body.insertBefore(
    can.stache.from(&quot;app-template&quot;)({}),
    document.body.firstChild
);

// ========================================
// TESTS
// ========================================

QUnit.module(&quot;ATM system&quot;, {
    setup: function() {
        can.fixture.delay = 1;
    },
    teardown: function() {
        can.fixture.delay = 2000;
    }
});

QUnit.asyncTest(&quot;Valid Card&quot;, function() {

    var c = new Card({
        number: &quot;01234567890&quot;,
        pin: 1234
    });

    QUnit.equal(c.state, &quot;unverified&quot;);

    c.verify();

    QUnit.equal(c.state, &quot;verifying&quot;, &quot;card is verifying&quot;);

    c.on(&quot;state&quot;, function(ev, newVal) {

        QUnit.equal(newVal, &quot;verified&quot;, &quot;card is verified&quot;);

        QUnit.start();
    });
});

QUnit.asyncTest(&quot;Invalid Card&quot;, function() {

    var c = new Card({});

    QUnit.equal(c.state, &quot;unverified&quot;);

    c.verify();

    QUnit.equal(c.state, &quot;verifying&quot;, &quot;card is verifying&quot;);

    c.on(&quot;state&quot;, function(ev, newVal) {

        QUnit.equal(newVal, &quot;invalid&quot;, &quot;card is invalid&quot;);

        QUnit.start();
    });
});

QUnit.asyncTest(&quot;Deposit&quot;, 6, function() {

    var card = new Card({
        number: &quot;0123456789&quot;,
        pin: &quot;1122&quot;
    });

    var deposit = new Deposit({
        amount: 100,
        card: card
    });

    equal(deposit.state, &quot;invalid&quot;);

    var startingBalance;

    Account.getList(card.serialize()).then(function(accounts) {
        QUnit.ok(true, &quot;got accounts&quot;);
        deposit.account = accounts[0];
        startingBalance = accounts[0].balance;
    });

    deposit.on(&quot;state&quot;, function(ev, newVal) {
        if (newVal === &quot;ready&quot;) {

            QUnit.ok(true, &quot;deposit is ready&quot;);
            deposit.execute();

        } else if (newVal === &quot;executing&quot;) {

            QUnit.ok(true, &quot;executing a deposit&quot;);

        } else if (newVal === &quot;executed&quot;) {

            QUnit.ok(true, &quot;executed a deposit&quot;);
            equal(deposit.account.balance, 100 + startingBalance);
            start();

        }
    });
});

</code></pre>
<p><span line-highlight='13-31,63-187,only'></span>
When complete, the <strong>Deposit</strong> tests will pass.</p>
<h2>Reading Card page and test</h2>
<p>In this section, we will:</p>
<ul>
<li>Allow the user to enter a card number and go to the <strong>Reading Pin</strong> page.</li>
<li>Add tests to <strong>ATM Basics</strong> test.</li>
</ul>
<p>Update the <code>HTML</code> tab to:</p>
<ul>
<li>Allow a user to call <code>cardNumber</code> with the <code>&lt;input&gt;</code>'s <code>value</code>.</li>
</ul>
<pre><code class="language-html">&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;meta name=&quot;description&quot; content=&quot;CanJS 3.0 - ATM Guide - Setup&quot;&gt;
  &lt;meta charset=&quot;utf-8&quot;&gt;
  &lt;meta name=&quot;viewport&quot; content=&quot;width=device-width&quot;&gt;
  &lt;title&gt;JS Bin&lt;/title&gt;

&lt;/head&gt;
&lt;body&gt;

&lt;script type='text/stache' id='atm-template'&gt;
&lt;div class=&quot;screen&quot;&gt;
    &lt;div class=&quot;screen-content&quot;&gt;
        &lt;div class=&quot;screen-glass&quot;&gt;

{{#switch state}}
    {{#case &quot;readingCard&quot;}}
        &lt;h2&gt;Reading Card&lt;/h2&gt;
        &lt;p&gt;Welcome to canATM where there are &lt;strong&gt;never&lt;/strong&gt;
          fees!&lt;/p&gt;
        &lt;/p&gt;
        &lt;p&gt;
            Enter Card Number:
            &lt;input name=&quot;card&quot; ($enter)=&quot;cardNumber(%element.value)&quot;/&gt;
        &lt;/p&gt;
    {{/case}}

    {{#case &quot;readingPin&quot;}}
        &lt;h2&gt;Reading Pin&lt;/h2&gt;
    {{/case}}

    {{#case &quot;choosingTransaction&quot;}}
        &lt;h2&gt;Choose Transaction&lt;/h2&gt;
    {{/case}}

    {{#case  &quot;pickingAccount&quot;}}
        &lt;h2&gt;Pick Account&lt;/h2&gt;
    {{/case}}

    {{#case &quot;depositInfo&quot;}}
        &lt;h2&gt;Deposit&lt;/h2&gt;
    {{/case}}

    {{#case &quot;withdrawalInfo&quot;}}
        &lt;h2&gt;Withdraw&lt;/h2&gt;
    {{/case}}

    {{#case &quot;successfulTransaction&quot;}}
        &lt;h2&gt;Transaction Successful!&lt;/h2&gt;
    {{/case}}

    {{#case  &quot;printingReceipt&quot;}}
        &lt;h2&gt;Printing Receipt&lt;/h2&gt;
    {{/case}}

    {{#default}}
        &lt;h2&gt;Error&lt;/h2&gt;
        &lt;p&gt;Invalid state - {{state}}&lt;/p&gt;
    {{/default}}

{{/switch}}

        &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;
&lt;/script&gt;

&lt;script type='text/stache' id='app-template'&gt;
&lt;div class=&quot;title&quot;&gt;
    &lt;h1&gt;canATM&lt;/h1&gt;
&lt;/div&gt;
&lt;atm-machine/&gt;
&lt;/script&gt;

&lt;script src=&quot;https://code.jquery.com/jquery-2.2.4.js&quot;&gt;&lt;/script&gt;
&lt;script src=&quot;https://unpkg.com/can/dist/global/can.all.js&quot;&gt;&lt;/script&gt;

&lt;div id=&quot;qunit&quot;&gt;&lt;/div&gt;
&lt;link rel=&quot;stylesheet&quot; href=&quot;http://code.jquery.com/qunit/qunit-1.12.0.css&quot;&gt;
&lt;script src=&quot;http://code.jquery.com/qunit/qunit-1.12.0.js&quot;&gt;&lt;/script&gt;

&lt;/body&gt;
&lt;/html&gt;

</code></pre>
<p><span line-highlight='20-26,only'></span>
Update the <code>JavaScript</code> tab to:</p>
<ul>
<li>Declare a <code>card</code> property.</li>
<li>Derive a <code>state</code> property that changes to <code>&quot;readingPin&quot;</code> when <code>card</code> is defined.</li>
<li>Add a <code>cardNumber</code> that creates a <code>card</code> with the <code>number</code> provided.</li>
</ul>
<pre><code class="language-js">// ========================================
// CODE
// ========================================

can.fixture({
    &quot;/verifyCard&quot;: function(request, response) {
        if (!request.data || !request.data.number || !request.data.pin) {
            response(400, {});
        } else {
            return {};
        }
    },
    &quot;/accounts&quot;: function() {
        return {
            data: [{
                balance: 100,
                id: 1,
                name: &quot;checking&quot;
            }, {
                balance: 10000,
                id: 2,
                name: &quot;savings&quot;
            }]
        };
    },
    &quot;/deposit&quot;: function() {
        return {};
    },
    &quot;/withdrawal&quot;: function() {
        return {};
    }
});
can.fixture.delay = 1000;

var Card = can.DefineMap.extend({
    number: &quot;string&quot;,
    pin: &quot;string&quot;,
    state: {
        value: &quot;unverified&quot;,
        serialize: false
    },
    verify: function() {

        this.state = &quot;verifying&quot;;

        var self = this;
        return can.ajax({
            type: &quot;POST&quot;,
            url: &quot;/verifyCard&quot;,
            data: this.serialize()
        }).then(
            function() {
                self.state = &quot;verified&quot;;
                return self;
            },
            function() {
                self.state = &quot;invalid&quot;;
                return self;
            });
    }
});

var Account = can.DefineMap.extend(&quot;Account&quot;, {
    id: &quot;number&quot;,
    balance: &quot;number&quot;,
    name: &quot;string&quot;
});
Account.List = can.DefineList.extend(&quot;AccountList&quot;, {
    &quot;*&quot;: Account
});

can.connect.baseMap({
    url: &quot;/accounts&quot;,
    Map: Account,
    List: Account.List,
    name: &quot;accounts&quot;
});

var Transaction = can.DefineMap.extend({
    account: Account,
    card: Card,
    executing: {
        type: &quot;boolean&quot;,
        value: false
    },
    executed: {
        type: &quot;boolean&quot;,
        value: false
    },
    rejected: &quot;any&quot;,
    get ready(){
        throw new Error(&quot;Transaction::ready must be provided by extended type&quot;);
    },
    get state() {
        if (this.rejected) {
            return &quot;rejected&quot;;
        }
        if (this.executed) {
            return &quot;executed&quot;;
        }
        if (this.executing) {
            return &quot;executing&quot;;
        }
        // make sure there's an amount, account, and card
        if (this.ready) {
            return &quot;ready&quot;;
        }
        return &quot;invalid&quot;;
    },
    executeStart: function(){
        throw new Error(&quot;Transaction::executeStart must be provided by extended type&quot;);
    },
    executeEnd: function(){
        throw new Error(&quot;Transaction::executeEnd must be provided by extended type&quot;);
    },
    execute: function() {
        if (this.state === &quot;ready&quot;) {

            this.executing = true;

            var def = this.executeStart(),
                self = this;

            def.then(function() {
                can.batch.start();
                self.set({
                    executing: false,
                    executed: true
                });
                self.executeEnd();
                can.batch.stop();
            }, function(reason){
                self.set({
                    executing: false,
                    executed: true,
                    rejected: reason
                });
            });
        }
    }
});

var Deposit = Transaction.extend({
    amount: &quot;number&quot;,
    get ready() {
        return this.amount &gt; 0 &amp;&amp;
            this.account &amp;&amp;
            this.card;
    },
    executeStart: function() {
        return can.ajax({
            type: &quot;POST&quot;,
            url: &quot;/deposit&quot;,
            data: {
                card: this.card.serialize(),
                accountId: this.account.id,
                amount: this.amount
            }
        });
    },
    executeEnd: function(data) {
        this.account.balance = this.account.balance + this.amount;
    }
});

var Withdrawal = Transaction.extend({
    amount: &quot;number&quot;,
    get ready() {
        return this.amount &gt; 0 &amp;&amp;
            this.account &amp;&amp;
            this.card;
    },
    executeStart: function() {
        return can.ajax({
            type: &quot;POST&quot;,
            url: &quot;/withdrawal&quot;,
            data: {
                card: this.card.serialize(),
                accountId: this.account.id,
                amount: this.amount
            }
        });
    },
    executeEnd: function(data) {
        this.account.balance = this.account.balance - this.amount;
    }
});

var ATM = can.DefineMap.extend({
    // stateful properties
    card: Card,

    // derived properties
    get state(){
        if(this.card) {
            return &quot;readingPin&quot;;
        }
        return &quot;readingCard&quot;;
    },
    // methods
    cardNumber: function(number) {
        this.card = new Card({
            number: number
        });
    }
});

can.Component.extend({
    tag: &quot;atm-machine&quot;,
    view: can.stache.from(&quot;atm-template&quot;),
    ViewModel: ATM,
});

document.body.insertBefore(
    can.stache.from(&quot;app-template&quot;)({}),
    document.body.firstChild
);

// ========================================
// TESTS
// ========================================

QUnit.module(&quot;ATM system&quot;, {
    setup: function() {
        can.fixture.delay = 1;
    },
    teardown: function() {
        can.fixture.delay = 2000;
    }
});

QUnit.asyncTest(&quot;Valid Card&quot;, function() {

    var c = new Card({
        number: &quot;01234567890&quot;,
        pin: 1234
    });

    QUnit.equal(c.state, &quot;unverified&quot;);

    c.verify();

    QUnit.equal(c.state, &quot;verifying&quot;, &quot;card is verifying&quot;);

    c.on(&quot;state&quot;, function(ev, newVal) {

        QUnit.equal(newVal, &quot;verified&quot;, &quot;card is verified&quot;);

        QUnit.start();
    });
});

QUnit.asyncTest(&quot;Invalid Card&quot;, function() {

    var c = new Card({});

    QUnit.equal(c.state, &quot;unverified&quot;);

    c.verify();

    QUnit.equal(c.state, &quot;verifying&quot;, &quot;card is verifying&quot;);

    c.on(&quot;state&quot;, function(ev, newVal) {

        QUnit.equal(newVal, &quot;invalid&quot;, &quot;card is invalid&quot;);

        QUnit.start();
    });
});

QUnit.asyncTest(&quot;Deposit&quot;, 6, function() {

    var card = new Card({
        number: &quot;0123456789&quot;,
        pin: &quot;1122&quot;
    });

    var deposit = new Deposit({
        amount: 100,
        card: card
    });

    equal(deposit.state, &quot;invalid&quot;);

    var startingBalance;

    Account.getList(card.serialize()).then(function(accounts) {
        QUnit.ok(true, &quot;got accounts&quot;);
        deposit.account = accounts[0];
        startingBalance = accounts[0].balance;
    });

    deposit.on(&quot;state&quot;, function(ev, newVal) {
        if (newVal === &quot;ready&quot;) {

            QUnit.ok(true, &quot;deposit is ready&quot;);
            deposit.execute();

        } else if (newVal === &quot;executing&quot;) {

            QUnit.ok(true, &quot;executing a deposit&quot;);

        } else if (newVal === &quot;executed&quot;) {

            QUnit.ok(true, &quot;executed a deposit&quot;);
            equal(deposit.account.balance, 100 + startingBalance);
            start();

        }
    });
});

QUnit.asyncTest(&quot;ATM basics&quot;, function() {

    var atm = new ATM();

    equal(atm.state, &quot;readingCard&quot;, &quot;starts at reading card state&quot;);

    atm.cardNumber(&quot;01233456789&quot;);

    equal(atm.state, &quot;readingPin&quot;, &quot;moves to reading card state&quot;);

    QUnit.start();
});

</code></pre>
<p><span line-highlight='190-205,313-325,only'></span>
When complete, you should be able to enter a card number and see the <strong>Reading Pin</strong>
page.</p>
<h2>Reading Pin page and test</h2>
<p>In this section, we will:</p>
<ul>
<li>Allow the user to enter a pin number and go to the <strong>Choosing Transaction</strong> page.</li>
<li>Add tests to <strong>ATM Basics</strong> test.</li>
</ul>
<p>Update the <code>HTML</code> tab to:</p>
<ul>
<li>Call <code>pinNumber</code> with the <code>&lt;input&gt;</code>'s <code>value</code>.</li>
<li>Disable the <code>&lt;input&gt;</code> while the pin is being verified.</li>
<li>Show a loading icon while the pin is being verified.</li>
</ul>
<pre><code class="language-html">&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;meta name=&quot;description&quot; content=&quot;CanJS 3.0 - ATM Guide - Setup&quot;&gt;
  &lt;meta charset=&quot;utf-8&quot;&gt;
  &lt;meta name=&quot;viewport&quot; content=&quot;width=device-width&quot;&gt;
  &lt;title&gt;JS Bin&lt;/title&gt;

&lt;/head&gt;
&lt;body&gt;

&lt;script type='text/stache' id='atm-template'&gt;
&lt;div class=&quot;screen&quot;&gt;
    &lt;div class=&quot;screen-content&quot;&gt;
        &lt;div class=&quot;screen-glass&quot;&gt;

{{#switch state}}
    {{#case &quot;readingCard&quot;}}
        &lt;h2&gt;Reading Card&lt;/h2&gt;
        &lt;p&gt;Welcome to canATM where there are &lt;strong&gt;never&lt;/strong&gt;
          fees!&lt;/p&gt;
        &lt;/p&gt;
        &lt;p&gt;
            Enter Card Number:
            &lt;input name=&quot;card&quot; ($enter)=&quot;cardNumber(%element.value)&quot;/&gt;
        &lt;/p&gt;
    {{/case}}

    {{#case &quot;readingPin&quot;}}
        &lt;h2&gt;Reading Pin&lt;/h2&gt;
        &lt;p&gt;
            Enter Pin Number:
            &lt;input name=&quot;pin&quot; type=&quot;password&quot;
                autofocus
                {{#is card.state &quot;verifying&quot;}}DISABLED{{/is}}
                ($enter)=&quot;pinNumber(%element.value)&quot;/&gt;

            {{#is card.state &quot;verifying&quot;}}
                &lt;div class='warn'&gt;
                    &lt;p&gt;
                    &lt;img src=&quot;http://v3.canjs.com/docs/images/loader.gif&quot;/&gt;
                    verifying
                    &lt;/p&gt;
                &lt;/div&gt;
            {{/is}}
        &lt;/p&gt;
        &lt;a href=&quot;javascript://&quot; ($click)=&quot;exit()&quot;&gt;exit&lt;/a&gt;
    {{/case}}

    {{#case &quot;choosingTransaction&quot;}}
        &lt;h2&gt;Choose Transaction&lt;/h2&gt;
    {{/case}}

    {{#case  &quot;pickingAccount&quot;}}
        &lt;h2&gt;Pick Account&lt;/h2&gt;
    {{/case}}

    {{#case &quot;depositInfo&quot;}}
        &lt;h2&gt;Deposit&lt;/h2&gt;
    {{/case}}

    {{#case &quot;withdrawalInfo&quot;}}
        &lt;h2&gt;Withdraw&lt;/h2&gt;
    {{/case}}

    {{#case &quot;successfulTransaction&quot;}}
        &lt;h2&gt;Transaction Successful!&lt;/h2&gt;
    {{/case}}

    {{#case  &quot;printingReceipt&quot;}}
        &lt;h2&gt;Printing Receipt&lt;/h2&gt;
    {{/case}}

    {{#default}}
        &lt;h2&gt;Error&lt;/h2&gt;
        &lt;p&gt;Invalid state - {{state}}&lt;/p&gt;
    {{/default}}

{{/switch}}

        &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;
&lt;/script&gt;

&lt;script type='text/stache' id='app-template'&gt;
&lt;div class=&quot;title&quot;&gt;
    &lt;h1&gt;canATM&lt;/h1&gt;
&lt;/div&gt;
&lt;atm-machine/&gt;
&lt;/script&gt;

&lt;script src=&quot;https://code.jquery.com/jquery-2.2.4.js&quot;&gt;&lt;/script&gt;
&lt;script src=&quot;https://unpkg.com/can/dist/global/can.all.js&quot;&gt;&lt;/script&gt;

&lt;div id=&quot;qunit&quot;&gt;&lt;/div&gt;
&lt;link rel=&quot;stylesheet&quot; href=&quot;http://code.jquery.com/qunit/qunit-1.12.0.css&quot;&gt;
&lt;script src=&quot;http://code.jquery.com/qunit/qunit-1.12.0.js&quot;&gt;&lt;/script&gt;

&lt;/body&gt;
&lt;/html&gt;

</code></pre>
<p><span line-highlight='31-47,only'></span>
Update the <code>ATM</code> view model in the <code>CODE</code> section of the <code>JavaScript</code> tab to:</p>
<ul>
<li>Define an <code>accountsPromise</code> property that will contain a list of accounts for the <code>card</code>.</li>
<li>Define a <code>transactions</code> property that will contain a list of transactions for this session.</li>
<li>Update <code>state</code> to be in the <code>&quot;choosingTransaction&quot;</code> state when the <code>card</code> is verified.</li>
<li>Define a <code>pinNumber</code> method that updates the <code>card</code>'s <code>pin</code>, calls <code>.verify()</code>,
and then initializes the <code>accountsPromise</code> and <code>transactions</code> property.</li>
</ul>
<p>Update the <code>TESTS</code> section of the <code>JavaScript</code> tab to:</p>
<ul>
<li>Test calling <code>pinNumber</code> moves the <code>state</code> to <code>&quot;choosingTransaction&quot;</code>.</li>
</ul>
<pre><code class="language-js">// ========================================
// CODE
// ========================================

can.fixture({
    &quot;/verifyCard&quot;: function(request, response) {
        if (!request.data || !request.data.number || !request.data.pin) {
            response(400, {});
        } else {
            return {};
        }
    },
    &quot;/accounts&quot;: function() {
        return {
            data: [{
                balance: 100,
                id: 1,
                name: &quot;checking&quot;
            }, {
                balance: 10000,
                id: 2,
                name: &quot;savings&quot;
            }]
        };
    },
    &quot;/deposit&quot;: function() {
        return {};
    },
    &quot;/withdrawal&quot;: function() {
        return {};
    }
});
can.fixture.delay = 1000;

var Card = can.DefineMap.extend({
    number: &quot;string&quot;,
    pin: &quot;string&quot;,
    state: {
        value: &quot;unverified&quot;,
        serialize: false
    },
    verify: function() {

        this.state = &quot;verifying&quot;;

        var self = this;
        return can.ajax({
            type: &quot;POST&quot;,
            url: &quot;/verifyCard&quot;,
            data: this.serialize()
        }).then(
            function() {
                self.state = &quot;verified&quot;;
                return self;
            },
            function() {
                self.state = &quot;invalid&quot;;
                return self;
            });
    }
});

var Account = can.DefineMap.extend(&quot;Account&quot;, {
    id: &quot;number&quot;,
    balance: &quot;number&quot;,
    name: &quot;string&quot;
});
Account.List = can.DefineList.extend(&quot;AccountList&quot;, {
    &quot;*&quot;: Account
});

can.connect.baseMap({
    url: &quot;/accounts&quot;,
    Map: Account,
    List: Account.List,
    name: &quot;accounts&quot;
});

var Transaction = can.DefineMap.extend({
    account: Account,
    card: Card,
    executing: {
        type: &quot;boolean&quot;,
        value: false
    },
    executed: {
        type: &quot;boolean&quot;,
        value: false
    },
    rejected: &quot;any&quot;,
    get ready(){
        throw new Error(&quot;Transaction::ready must be provided by extended type&quot;);
    },
    get state() {
        if (this.rejected) {
            return &quot;rejected&quot;;
        }
        if (this.executed) {
            return &quot;executed&quot;;
        }
        if (this.executing) {
            return &quot;executing&quot;;
        }
        // make sure there's an amount, account, and card
        if (this.ready) {
            return &quot;ready&quot;;
        }
        return &quot;invalid&quot;;
    },
    executeStart: function(){
        throw new Error(&quot;Transaction::executeStart must be provided by extended type&quot;);
    },
    executeEnd: function(){
        throw new Error(&quot;Transaction::executeEnd must be provided by extended type&quot;);
    },
    execute: function() {
        if (this.state === &quot;ready&quot;) {

            this.executing = true;

            var def = this.executeStart(),
                self = this;

            def.then(function() {
                can.batch.start();
                self.set({
                    executing: false,
                    executed: true
                });
                self.executeEnd();
                can.batch.stop();
            }, function(reason){
                self.set({
                    executing: false,
                    executed: true,
                    rejected: reason
                });
            });
        }
    }
});

var Deposit = Transaction.extend({
    amount: &quot;number&quot;,
    get ready() {
        return this.amount &gt; 0 &amp;&amp;
            this.account &amp;&amp;
            this.card;
    },
    executeStart: function() {
        return can.ajax({
            type: &quot;POST&quot;,
            url: &quot;/deposit&quot;,
            data: {
                card: this.card.serialize(),
                accountId: this.account.id,
                amount: this.amount
            }
        });
    },
    executeEnd: function(data) {
        this.account.balance = this.account.balance + this.amount;
    }
});

var Withdrawal = Transaction.extend({
    amount: &quot;number&quot;,
    get ready() {
        return this.amount &gt; 0 &amp;&amp;
            this.account &amp;&amp;
            this.card;
    },
    executeStart: function() {
        return can.ajax({
            type: &quot;POST&quot;,
            url: &quot;/withdrawal&quot;,
            data: {
                card: this.card.serialize(),
                accountId: this.account.id,
                amount: this.amount
            }
        });
    },
    executeEnd: function(data) {
        this.account.balance = this.account.balance - this.amount;
    }
});

var ATM = can.DefineMap.extend({
    // stateful properties
    card: Card,
    accountsPromise: &quot;any&quot;,
    transactions: can.DefineList,

    // derived properties
    get state(){
        if(this.card) {
            if (this.card.state === &quot;verified&quot;) {
                return &quot;choosingTransaction&quot;;
            }
            return &quot;readingPin&quot;;
        }
        return &quot;readingCard&quot;;
    },

    // methods
    cardNumber: function(number) {
        this.card = new Card({
            number: number
        });
    },
    pinNumber: function(pin) {
        var card = this.card;

        card.pin = pin;
        this.transactions = new can.DefineList();
        this.accountsPromise = card.verify().then(function(card) {

            return Account.getList(card.serialize());
        });
    },
    exit: function(){
        this.set({
            card: null,
            accountsPromise: null,
            transactions: null
        });
    }
});

can.Component.extend({
    tag: &quot;atm-machine&quot;,
    view: can.stache.from(&quot;atm-template&quot;),
    ViewModel: ATM,
});

document.body.insertBefore(
    can.stache.from(&quot;app-template&quot;)({}),
    document.body.firstChild
);

// ========================================
// TESTS
// ========================================

QUnit.module(&quot;ATM system&quot;, {
    setup: function() {
        can.fixture.delay = 1;
    },
    teardown: function() {
        can.fixture.delay = 2000;
    }
});

QUnit.asyncTest(&quot;Valid Card&quot;, function() {

    var c = new Card({
        number: &quot;01234567890&quot;,
        pin: 1234
    });

    QUnit.equal(c.state, &quot;unverified&quot;);

    c.verify();

    QUnit.equal(c.state, &quot;verifying&quot;, &quot;card is verifying&quot;);

    c.on(&quot;state&quot;, function(ev, newVal) {

        QUnit.equal(newVal, &quot;verified&quot;, &quot;card is verified&quot;);

        QUnit.start();
    });
});

QUnit.asyncTest(&quot;Invalid Card&quot;, function() {

    var c = new Card({});

    QUnit.equal(c.state, &quot;unverified&quot;);

    c.verify();

    QUnit.equal(c.state, &quot;verifying&quot;, &quot;card is verifying&quot;);

    c.on(&quot;state&quot;, function(ev, newVal) {

        QUnit.equal(newVal, &quot;invalid&quot;, &quot;card is invalid&quot;);

        QUnit.start();
    });
});

QUnit.asyncTest(&quot;Deposit&quot;, 6, function() {

    var card = new Card({
        number: &quot;0123456789&quot;,
        pin: &quot;1122&quot;
    });

    var deposit = new Deposit({
        amount: 100,
        card: card
    });

    equal(deposit.state, &quot;invalid&quot;);

    var startingBalance;

    Account.getList(card.serialize()).then(function(accounts) {
        QUnit.ok(true, &quot;got accounts&quot;);
        deposit.account = accounts[0];
        startingBalance = accounts[0].balance;
    });

    deposit.on(&quot;state&quot;, function(ev, newVal) {
        if (newVal === &quot;ready&quot;) {

            QUnit.ok(true, &quot;deposit is ready&quot;);
            deposit.execute();

        } else if (newVal === &quot;executing&quot;) {

            QUnit.ok(true, &quot;executing a deposit&quot;);

        } else if (newVal === &quot;executed&quot;) {

            QUnit.ok(true, &quot;executed a deposit&quot;);
            equal(deposit.account.balance, 100 + startingBalance);
            start();

        }
    });
});

QUnit.asyncTest(&quot;ATM basics&quot;, function() {

    var atm = new ATM();

    equal(atm.state, &quot;readingCard&quot;, &quot;starts at reading card state&quot;);

    atm.cardNumber(&quot;01233456789&quot;);

    equal(atm.state, &quot;readingPin&quot;, &quot;moves to reading card state&quot;);

    atm.pinNumber(&quot;1234&quot;);

    ok(atm.state, &quot;readingPin&quot;, &quot;remain in the reading pin state until verifyied&quot;);

    atm.on(&quot;state&quot;, function(ev, newVal) {

        if (newVal === &quot;choosingTransaction&quot;) {
            QUnit.ok(true, &quot;in choosingTransaction&quot;);
            QUnit.start();
        }
    });
});

</code></pre>
<p><span line-highlight='192-193,198-200,212-228,346-356,only'></span>
When complete, you should be able to enter a card and pin number and see the <strong>Choosing Transaction</strong>
page.</p>
<h2>Choosing Transaction page and test</h2>
<p>In this section, we will:</p>
<ul>
<li>Allow the user to pick a transaction type and go to the <strong>Picking Account</strong> page.</li>
<li>Add tests to <strong>ATM Basics</strong> test.</li>
</ul>
<p>Update the <code>HTML</code> tab to:</p>
<ul>
<li>Have buttons for choosing a deposit, withdrawal, or print a receipt and exit.</li>
</ul>
<pre><code class="language-html">&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;meta name=&quot;description&quot; content=&quot;CanJS 3.0 - ATM Guide - Setup&quot;&gt;
  &lt;meta charset=&quot;utf-8&quot;&gt;
  &lt;meta name=&quot;viewport&quot; content=&quot;width=device-width&quot;&gt;
  &lt;title&gt;JS Bin&lt;/title&gt;

&lt;/head&gt;
&lt;body&gt;

&lt;script type='text/stache' id='atm-template'&gt;
&lt;div class=&quot;screen&quot;&gt;
    &lt;div class=&quot;screen-content&quot;&gt;
        &lt;div class=&quot;screen-glass&quot;&gt;

{{#switch state}}
    {{#case &quot;readingCard&quot;}}
        &lt;h2&gt;Reading Card&lt;/h2&gt;
        &lt;p&gt;Welcome to canATM where there are &lt;strong&gt;never&lt;/strong&gt;
          fees!&lt;/p&gt;
        &lt;/p&gt;
        &lt;p&gt;
            Enter Card Number:
            &lt;input name=&quot;card&quot; ($enter)=&quot;cardNumber(%element.value)&quot;/&gt;
        &lt;/p&gt;
    {{/case}}

    {{#case &quot;readingPin&quot;}}
        &lt;h2&gt;Reading Pin&lt;/h2&gt;
        &lt;p&gt;
            Enter Pin Number:
            &lt;input name=&quot;pin&quot; type=&quot;password&quot;
                autofocus
                {{#is card.state &quot;verifying&quot;}}DISABLED{{/is}}
                ($enter)=&quot;pinNumber(%element.value)&quot;/&gt;

            {{#is card.state &quot;verifying&quot;}}
                &lt;div class='warn'&gt;
                    &lt;p&gt;
                    &lt;img src=&quot;http://v3.canjs.com/docs/images/loader.gif&quot;/&gt;
                    verifying
                    &lt;/p&gt;
                &lt;/div&gt;
            {{/is}}
        &lt;/p&gt;
        &lt;a href=&quot;javascript://&quot; ($click)=&quot;exit()&quot;&gt;exit&lt;/a&gt;
    {{/case}}

    {{#case &quot;choosingTransaction&quot;}}
        &lt;h2&gt;Choose Transaction&lt;/h2&gt;
        &lt;p&gt;What would you like to do?&lt;/p&gt;
        &lt;nav&gt;
            &lt;ul&gt;
                &lt;li ($click)=&quot;chooseDeposit()&quot;&gt;Deposit&lt;/li&gt;
                &lt;li ($click)=&quot;chooseWithdraw()&quot;&gt;Withdraw&lt;/li&gt;
                &lt;li ($click)=&quot;printReceiptAndExit()&quot;&gt;Exit&lt;/li&gt;
            &lt;/ul&gt;
        &lt;/nav&gt;
    {{/case}}

    {{#case  &quot;pickingAccount&quot;}}
        &lt;h2&gt;Pick Account&lt;/h2&gt;
    {{/case}}

    {{#case &quot;depositInfo&quot;}}
        &lt;h2&gt;Deposit&lt;/h2&gt;
    {{/case}}

    {{#case &quot;withdrawalInfo&quot;}}
        &lt;h2&gt;Withdraw&lt;/h2&gt;
    {{/case}}

    {{#case &quot;successfulTransaction&quot;}}
        &lt;h2&gt;Transaction Successful!&lt;/h2&gt;
    {{/case}}

    {{#case  &quot;printingReceipt&quot;}}
        &lt;h2&gt;Printing Receipt&lt;/h2&gt;
    {{/case}}

    {{#default}}
        &lt;h2&gt;Error&lt;/h2&gt;
        &lt;p&gt;Invalid state - {{state}}&lt;/p&gt;
    {{/default}}

{{/switch}}

        &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;
&lt;/script&gt;

&lt;script type='text/stache' id='app-template'&gt;
&lt;div class=&quot;title&quot;&gt;
    &lt;h1&gt;canATM&lt;/h1&gt;
&lt;/div&gt;
&lt;atm-machine/&gt;
&lt;/script&gt;

&lt;script src=&quot;https://code.jquery.com/jquery-2.2.4.js&quot;&gt;&lt;/script&gt;
&lt;script src=&quot;https://unpkg.com/can/dist/global/can.all.js&quot;&gt;&lt;/script&gt;

&lt;div id=&quot;qunit&quot;&gt;&lt;/div&gt;
&lt;link rel=&quot;stylesheet&quot; href=&quot;http://code.jquery.com/qunit/qunit-1.12.0.css&quot;&gt;
&lt;script src=&quot;http://code.jquery.com/qunit/qunit-1.12.0.js&quot;&gt;&lt;/script&gt;

&lt;/body&gt;
&lt;/html&gt;

</code></pre>
<p><span line-highlight='52-59,only'></span>
Update the <code>ATM</code> view model in the <code>CODE</code> section of the <code>JavaScript</code> tab to:</p>
<ul>
<li>Define a <code>currentTransaction</code> property that when set, adds the previous <code>currentTransaction</code>
to the list of <code>transactions</code>.</li>
<li>Update the <code>state</code> property to <code>&quot;pickingAccount&quot;</code> when there is a <code>currentTransaction</code>.</li>
<li>Update the <code>exit</code> method to clear the <code>currentTransaction</code> property.</li>
<li>Define <code>chooseDeposit</code> that creates a <code>Deposit</code> and sets it as the <code>currentTransaction</code>.</li>
<li>Define <code>chooseWithdraw</code> that creates a <code>Withdraw</code> and sets it as the <code>currentTransaction</code>.</li>
</ul>
<p>Update the <code>TESTS</code> section of the <code>JavaScript</code> tab to:</p>
<ul>
<li>Call <code>.chooseDeposit()</code> and verify the state moves to <code>&quot;pickingAccount&quot;</code>.</li>
</ul>
<pre><code class="language-js">// ========================================
// CODE
// ========================================

can.fixture({
    &quot;/verifyCard&quot;: function(request, response) {
        if (!request.data || !request.data.number || !request.data.pin) {
            response(400, {});
        } else {
            return {};
        }
    },
    &quot;/accounts&quot;: function() {
        return {
            data: [{
                balance: 100,
                id: 1,
                name: &quot;checking&quot;
            }, {
                balance: 10000,
                id: 2,
                name: &quot;savings&quot;
            }]
        };
    },
    &quot;/deposit&quot;: function() {
        return {};
    },
    &quot;/withdrawal&quot;: function() {
        return {};
    }
});
can.fixture.delay = 1000;

var Card = can.DefineMap.extend({
    number: &quot;string&quot;,
    pin: &quot;string&quot;,
    state: {
        value: &quot;unverified&quot;,
        serialize: false
    },
    verify: function() {

        this.state = &quot;verifying&quot;;

        var self = this;
        return can.ajax({
            type: &quot;POST&quot;,
            url: &quot;/verifyCard&quot;,
            data: this.serialize()
        }).then(
            function() {
                self.state = &quot;verified&quot;;
                return self;
            },
            function() {
                self.state = &quot;invalid&quot;;
                return self;
            });
    }
});

var Account = can.DefineMap.extend(&quot;Account&quot;, {
    id: &quot;number&quot;,
    balance: &quot;number&quot;,
    name: &quot;string&quot;
});
Account.List = can.DefineList.extend(&quot;AccountList&quot;, {
    &quot;*&quot;: Account
});

can.connect.baseMap({
    url: &quot;/accounts&quot;,
    Map: Account,
    List: Account.List,
    name: &quot;accounts&quot;
});

var Transaction = can.DefineMap.extend({
    account: Account,
    card: Card,
    executing: {
        type: &quot;boolean&quot;,
        value: false
    },
    executed: {
        type: &quot;boolean&quot;,
        value: false
    },
    rejected: &quot;any&quot;,
    get ready(){
        throw new Error(&quot;Transaction::ready must be provided by extended type&quot;);
    },
    get state() {
        if (this.rejected) {
            return &quot;rejected&quot;;
        }
        if (this.executed) {
            return &quot;executed&quot;;
        }
        if (this.executing) {
            return &quot;executing&quot;;
        }
        // make sure there's an amount, account, and card
        if (this.ready) {
            return &quot;ready&quot;;
        }
        return &quot;invalid&quot;;
    },
    executeStart: function(){
        throw new Error(&quot;Transaction::executeStart must be provided by extended type&quot;);
    },
    executeEnd: function(){
        throw new Error(&quot;Transaction::executeEnd must be provided by extended type&quot;);
    },
    execute: function() {
        if (this.state === &quot;ready&quot;) {

            this.executing = true;

            var def = this.executeStart(),
                self = this;

            def.then(function() {
                can.batch.start();
                self.set({
                    executing: false,
                    executed: true
                });
                self.executeEnd();
                can.batch.stop();
            }, function(reason){
                self.set({
                    executing: false,
                    executed: true,
                    rejected: reason
                });
            });
        }
    }
});

var Deposit = Transaction.extend({
    amount: &quot;number&quot;,
    get ready() {
        return this.amount &gt; 0 &amp;&amp;
            this.account &amp;&amp;
            this.card;
    },
    executeStart: function() {
        return can.ajax({
            type: &quot;POST&quot;,
            url: &quot;/deposit&quot;,
            data: {
                card: this.card.serialize(),
                accountId: this.account.id,
                amount: this.amount
            }
        });
    },
    executeEnd: function(data) {
        this.account.balance = this.account.balance + this.amount;
    }
});

var Withdrawal = Transaction.extend({
    amount: &quot;number&quot;,
    get ready() {
        return this.amount &gt; 0 &amp;&amp;
            this.account &amp;&amp;
            this.card;
    },
    executeStart: function() {
        return can.ajax({
            type: &quot;POST&quot;,
            url: &quot;/withdrawal&quot;,
            data: {
                card: this.card.serialize(),
                accountId: this.account.id,
                amount: this.amount
            }
        });
    },
    executeEnd: function(data) {
        this.account.balance = this.account.balance - this.amount;
    }
});

var ATM = can.DefineMap.extend({
    // stateful properties
    card: Card,
    accountsPromise: &quot;any&quot;,
    transactions: can.DefineList,
    currentTransaction: {
        set: function(newTransaction) {
            var currentTransaction = this.currentTransaction;
            if (this.transactions &amp;&amp; currentTransaction &amp;&amp;
                currentTransaction.state === &quot;executed&quot;) {

                this.transactions.push(currentTransaction);
            }
            return newTransaction;
        }
    },

    // derived properties
    get state(){

        if (this.currentTransaction) {
            return &quot;pickingAccount&quot;;
        }

        if(this.card) {
            if (this.card.state === &quot;verified&quot;) {
                return &quot;choosingTransaction&quot;;
            }
            return &quot;readingPin&quot;;
        }
        return &quot;readingCard&quot;;
    },

    // methods
    cardNumber: function(number) {
        this.card = new Card({
            number: number
        });
    },
    pinNumber: function(pin) {
        var card = this.card;

        card.pin = pin;
        this.transactions = new can.DefineList();
        this.accountsPromise = card.verify().then(function(card) {

            return Account.getList(card.serialize());
        });
    },
    exit: function(){
        this.set({
            card: null,
            accountsPromise: null,
            transactions: null,
            currentTransaction: null
        });
    },
    chooseDeposit: function() {
        this.currentTransaction = new Deposit({
            card: this.card
        });
    },
    chooseWithdraw: function() {
        this.currentTransaction = new Withdrawal({
            card: this.card
        });
    }
});

can.Component.extend({
    tag: &quot;atm-machine&quot;,
    view: can.stache.from(&quot;atm-template&quot;),
    ViewModel: ATM,
});

document.body.insertBefore(
    can.stache.from(&quot;app-template&quot;)({}),
    document.body.firstChild
);

// ========================================
// TESTS
// ========================================

QUnit.module(&quot;ATM system&quot;, {
    setup: function() {
        can.fixture.delay = 1;
    },
    teardown: function() {
        can.fixture.delay = 2000;
    }
});

QUnit.asyncTest(&quot;Valid Card&quot;, function() {

    var c = new Card({
        number: &quot;01234567890&quot;,
        pin: 1234
    });

    QUnit.equal(c.state, &quot;unverified&quot;);

    c.verify();

    QUnit.equal(c.state, &quot;verifying&quot;, &quot;card is verifying&quot;);

    c.on(&quot;state&quot;, function(ev, newVal) {

        QUnit.equal(newVal, &quot;verified&quot;, &quot;card is verified&quot;);

        QUnit.start();
    });
});

QUnit.asyncTest(&quot;Invalid Card&quot;, function() {

    var c = new Card({});

    QUnit.equal(c.state, &quot;unverified&quot;);

    c.verify();

    QUnit.equal(c.state, &quot;verifying&quot;, &quot;card is verifying&quot;);

    c.on(&quot;state&quot;, function(ev, newVal) {

        QUnit.equal(newVal, &quot;invalid&quot;, &quot;card is invalid&quot;);

        QUnit.start();
    });
});

QUnit.asyncTest(&quot;Deposit&quot;, 6, function() {

    var card = new Card({
        number: &quot;0123456789&quot;,
        pin: &quot;1122&quot;
    });

    var deposit = new Deposit({
        amount: 100,
        card: card
    });

    equal(deposit.state, &quot;invalid&quot;);

    var startingBalance;

    Account.getList(card.serialize()).then(function(accounts) {
        QUnit.ok(true, &quot;got accounts&quot;);
        deposit.account = accounts[0];
        startingBalance = accounts[0].balance;
    });

    deposit.on(&quot;state&quot;, function(ev, newVal) {
        if (newVal === &quot;ready&quot;) {

            QUnit.ok(true, &quot;deposit is ready&quot;);
            deposit.execute();

        } else if (newVal === &quot;executing&quot;) {

            QUnit.ok(true, &quot;executing a deposit&quot;);

        } else if (newVal === &quot;executed&quot;) {

            QUnit.ok(true, &quot;executed a deposit&quot;);
            equal(deposit.account.balance, 100 + startingBalance);
            start();

        }
    });
});

QUnit.asyncTest(&quot;ATM basics&quot;, function() {

    var atm = new ATM();

    equal(atm.state, &quot;readingCard&quot;, &quot;starts at reading card state&quot;);

    atm.cardNumber(&quot;01233456789&quot;);

    equal(atm.state, &quot;readingPin&quot;, &quot;moves to reading card state&quot;);

    atm.pinNumber(&quot;1234&quot;);

    ok(atm.state, &quot;readingPin&quot;, &quot;remain in the reading pin state until verifyied&quot;);

    atm.on(&quot;state&quot;, function(ev, newVal) {

        if (newVal === &quot;choosingTransaction&quot;) {

            QUnit.ok(true, &quot;in choosingTransaction&quot;);
            atm.chooseDeposit();

        } else if (newVal === &quot;pickingAccount&quot;) {

            QUnit.ok(true, &quot;in picking account state&quot;);
            QUnit.start();

        }
    });
});

</code></pre>
<p><span line-highlight='194-204,209-211,243,246-255,363,382-389,only'></span></p>
<blockquote>
<p>We will define <code>printReceiptAndExit</code> later!</p>
</blockquote>
<h2>Picking Account page and test</h2>
<p>In this section, we will:</p>
<ul>
<li>Allow the user to pick an account and go to either the  <strong>Deposit Info</strong> or
<strong>Withdrawal Info</strong> page.</li>
<li>Add tests to <strong>ATM Basics</strong> test.</li>
</ul>
<p>Update the <code>HTML</code> tab to:</p>
<ul>
<li>Write out a <em>&quot;Loading Accounts  ...&quot;</em> message while the accounts are loading.</li>
<li>Write out the accounts when loaded.</li>
<li>Call <code>chooseAccount()</code> when an account is clicked.</li>
</ul>
<pre><code class="language-html">&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;meta name=&quot;description&quot; content=&quot;CanJS 3.0 - ATM Guide - Setup&quot;&gt;
  &lt;meta charset=&quot;utf-8&quot;&gt;
  &lt;meta name=&quot;viewport&quot; content=&quot;width=device-width&quot;&gt;
  &lt;title&gt;JS Bin&lt;/title&gt;

&lt;/head&gt;
&lt;body&gt;

&lt;script type='text/stache' id='atm-template'&gt;
&lt;div class=&quot;screen&quot;&gt;
    &lt;div class=&quot;screen-content&quot;&gt;
        &lt;div class=&quot;screen-glass&quot;&gt;

{{#switch state}}
    {{#case &quot;readingCard&quot;}}
        &lt;h2&gt;Reading Card&lt;/h2&gt;
        &lt;p&gt;Welcome to canATM where there are &lt;strong&gt;never&lt;/strong&gt;
          fees!&lt;/p&gt;
        &lt;/p&gt;
        &lt;p&gt;
            Enter Card Number:
            &lt;input name=&quot;card&quot; ($enter)=&quot;cardNumber(%element.value)&quot;/&gt;
        &lt;/p&gt;
    {{/case}}

    {{#case &quot;readingPin&quot;}}
        &lt;h2&gt;Reading Pin&lt;/h2&gt;
        &lt;p&gt;
            Enter Pin Number:
            &lt;input name=&quot;pin&quot; type=&quot;password&quot;
                autofocus
                {{#is card.state &quot;verifying&quot;}}DISABLED{{/is}}
                ($enter)=&quot;pinNumber(%element.value)&quot;/&gt;

            {{#is card.state &quot;verifying&quot;}}
                &lt;div class='warn'&gt;
                    &lt;p&gt;
                    &lt;img src=&quot;http://v3.canjs.com/docs/images/loader.gif&quot;/&gt;
                    verifying
                    &lt;/p&gt;
                &lt;/div&gt;
            {{/is}}
        &lt;/p&gt;
        &lt;a href=&quot;javascript://&quot; ($click)=&quot;exit()&quot;&gt;exit&lt;/a&gt;
    {{/case}}

    {{#case &quot;choosingTransaction&quot;}}
        &lt;h2&gt;Choose Transaction&lt;/h2&gt;
        &lt;p&gt;What would you like to do?&lt;/p&gt;
        &lt;nav&gt;
            &lt;ul&gt;
                &lt;li ($click)=&quot;chooseDeposit()&quot;&gt;Deposit&lt;/li&gt;
                &lt;li ($click)=&quot;chooseWithdraw()&quot;&gt;Withdraw&lt;/li&gt;
                &lt;li ($click)=&quot;printReceiptAndExit()&quot;&gt;Exit&lt;/li&gt;
            &lt;/ul&gt;
        &lt;/nav&gt;
    {{/case}}

    {{#case  &quot;pickingAccount&quot;}}
        &lt;h2&gt;Pick Account&lt;/h2&gt;
        &lt;p&gt;Please pick your account:&lt;/p&gt;
        {{#if accountsPromise.isPending}}
            &lt;div class='warn'&gt;
                &lt;p&gt;
                    &lt;img src=&quot;http://v3.canjs.com/docs/images/loader.gif&quot;/&gt;
                    Loading Accounts ...
                &lt;/p&gt;
            &lt;/div&gt;
        {{else}}
            &lt;ul&gt;
                {{#each accountsPromise.value}}
                    &lt;li ($click)=&quot;chooseAccount(.)&quot;&gt;{{name}} - ${{balance}}&lt;/li&gt;
                {{/each}}
            &lt;/ul&gt;
        {{/if}}
    {{/case}}

    {{#case &quot;depositInfo&quot;}}
        &lt;h2&gt;Deposit&lt;/h2&gt;
    {{/case}}

    {{#case &quot;withdrawalInfo&quot;}}
        &lt;h2&gt;Withdraw&lt;/h2&gt;
    {{/case}}

    {{#case &quot;successfulTransaction&quot;}}
        &lt;h2&gt;Transaction Successful!&lt;/h2&gt;
    {{/case}}

    {{#case  &quot;printingReceipt&quot;}}
        &lt;h2&gt;Printing Receipt&lt;/h2&gt;
    {{/case}}

    {{#default}}
        &lt;h2&gt;Error&lt;/h2&gt;
        &lt;p&gt;Invalid state - {{state}}&lt;/p&gt;
    {{/default}}

{{/switch}}

        &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;
&lt;/script&gt;

&lt;script type='text/stache' id='app-template'&gt;
&lt;div class=&quot;title&quot;&gt;
    &lt;h1&gt;canATM&lt;/h1&gt;
&lt;/div&gt;
&lt;atm-machine/&gt;
&lt;/script&gt;

&lt;script src=&quot;https://code.jquery.com/jquery-2.2.4.js&quot;&gt;&lt;/script&gt;
&lt;script src=&quot;https://unpkg.com/can/dist/global/can.all.js&quot;&gt;&lt;/script&gt;

&lt;div id=&quot;qunit&quot;&gt;&lt;/div&gt;
&lt;link rel=&quot;stylesheet&quot; href=&quot;http://code.jquery.com/qunit/qunit-1.12.0.css&quot;&gt;
&lt;script src=&quot;http://code.jquery.com/qunit/qunit-1.12.0.js&quot;&gt;&lt;/script&gt;

&lt;/body&gt;
&lt;/html&gt;

</code></pre>
<p><span line-highlight='64-78,only'></span>
Update the <code>ATM</code> view model in the <code>CODE</code> section of the <code>JavaScript</code> tab to:</p>
<ul>
<li>Change <code>state</code> to check if the <code>currentTransaction</code> has an <code>account</code> and update the
value to <code>&quot;depositInfo&quot;</code> or <code>&quot;withdrawalInfo&quot;</code> depending on the type of the <code>currentTransaction</code>.</li>
<li>Add a <code>chooseAccount</code> method that sets the <code>currentTransaction</code>'s <code>account</code>.</li>
</ul>
<p>Update the <code>TESTS</code> section of the <code>JavaScript</code> tab to:</p>
<ul>
<li>Call <code>.chooseAccount()</code> with the first account loaded.</li>
<li>Verify the state changes to <code>&quot;depositInfo&quot;</code>.</li>
</ul>
<pre><code class="language-js">// ========================================
// CODE
// ========================================

can.fixture({
    &quot;/verifyCard&quot;: function(request, response) {
        if (!request.data || !request.data.number || !request.data.pin) {
            response(400, {});
        } else {
            return {};
        }
    },
    &quot;/accounts&quot;: function() {
        return {
            data: [{
                balance: 100,
                id: 1,
                name: &quot;checking&quot;
            }, {
                balance: 10000,
                id: 2,
                name: &quot;savings&quot;
            }]
        };
    },
    &quot;/deposit&quot;: function() {
        return {};
    },
    &quot;/withdrawal&quot;: function() {
        return {};
    }
});
can.fixture.delay = 1000;

var Card = can.DefineMap.extend({
    number: &quot;string&quot;,
    pin: &quot;string&quot;,
    state: {
        value: &quot;unverified&quot;,
        serialize: false
    },
    verify: function() {

        this.state = &quot;verifying&quot;;

        var self = this;
        return can.ajax({
            type: &quot;POST&quot;,
            url: &quot;/verifyCard&quot;,
            data: this.serialize()
        }).then(
            function() {
                self.state = &quot;verified&quot;;
                return self;
            },
            function() {
                self.state = &quot;invalid&quot;;
                return self;
            });
    }
});

var Account = can.DefineMap.extend(&quot;Account&quot;, {
    id: &quot;number&quot;,
    balance: &quot;number&quot;,
    name: &quot;string&quot;
});
Account.List = can.DefineList.extend(&quot;AccountList&quot;, {
    &quot;*&quot;: Account
});

can.connect.baseMap({
    url: &quot;/accounts&quot;,
    Map: Account,
    List: Account.List,
    name: &quot;accounts&quot;
});

var Transaction = can.DefineMap.extend({
    account: Account,
    card: Card,
    executing: {
        type: &quot;boolean&quot;,
        value: false
    },
    executed: {
        type: &quot;boolean&quot;,
        value: false
    },
    rejected: &quot;any&quot;,
    get ready(){
        throw new Error(&quot;Transaction::ready must be provided by extended type&quot;);
    },
    get state() {
        if (this.rejected) {
            return &quot;rejected&quot;;
        }
        if (this.executed) {
            return &quot;executed&quot;;
        }
        if (this.executing) {
            return &quot;executing&quot;;
        }
        // make sure there's an amount, account, and card
        if (this.ready) {
            return &quot;ready&quot;;
        }
        return &quot;invalid&quot;;
    },
    executeStart: function(){
        throw new Error(&quot;Transaction::executeStart must be provided by extended type&quot;);
    },
    executeEnd: function(){
        throw new Error(&quot;Transaction::executeEnd must be provided by extended type&quot;);
    },
    execute: function() {
        if (this.state === &quot;ready&quot;) {

            this.executing = true;

            var def = this.executeStart(),
                self = this;

            def.then(function() {
                can.batch.start();
                self.set({
                    executing: false,
                    executed: true
                });
                self.executeEnd();
                can.batch.stop();
            }, function(reason){
                self.set({
                    executing: false,
                    executed: true,
                    rejected: reason
                });
            });
        }
    }
});

var Deposit = Transaction.extend({
    amount: &quot;number&quot;,
    get ready() {
        return this.amount &gt; 0 &amp;&amp;
            this.account &amp;&amp;
            this.card;
    },
    executeStart: function() {
        return can.ajax({
            type: &quot;POST&quot;,
            url: &quot;/deposit&quot;,
            data: {
                card: this.card.serialize(),
                accountId: this.account.id,
                amount: this.amount
            }
        });
    },
    executeEnd: function(data) {
        this.account.balance = this.account.balance + this.amount;
    }
});

var Withdrawal = Transaction.extend({
    amount: &quot;number&quot;,
    get ready() {
        return this.amount &gt; 0 &amp;&amp;
            this.account &amp;&amp;
            this.card;
    },
    executeStart: function() {
        return can.ajax({
            type: &quot;POST&quot;,
            url: &quot;/withdrawal&quot;,
            data: {
                card: this.card.serialize(),
                accountId: this.account.id,
                amount: this.amount
            }
        });
    },
    executeEnd: function(data) {
        this.account.balance = this.account.balance - this.amount;
    }
});

var ATM = can.DefineMap.extend({
    // stateful properties
    card: Card,
    accountsPromise: &quot;any&quot;,
    transactions: can.DefineList,
    currentTransaction: {
        set: function(newTransaction) {
            var currentTransaction = this.currentTransaction;
            if (this.transactions &amp;&amp; currentTransaction &amp;&amp;
                currentTransaction.state === &quot;executed&quot;) {

                this.transactions.push(currentTransaction);
            }
            return newTransaction;
        }
    },

    // derived properties
    get state(){

        if (this.currentTransaction) {
            if (this.currentTransaction.account) {
                if (this.currentTransaction instanceof Deposit) {
                    return &quot;depositInfo&quot;;
                } else {
                    return &quot;withdrawalInfo&quot;;
                }
            }

            return &quot;pickingAccount&quot;;
        }

        if(this.card) {
            if (this.card.state === &quot;verified&quot;) {
                return &quot;choosingTransaction&quot;;
            }
            return &quot;readingPin&quot;;
        }
        return &quot;readingCard&quot;;
    },

    // methods
    cardNumber: function(number) {
        this.card = new Card({
            number: number
        });
    },
    pinNumber: function(pin) {
        var card = this.card;

        card.pin = pin;
        this.transactions = new can.DefineList();
        this.accountsPromise = card.verify().then(function(card) {

            return Account.getList(card.serialize());
        });
    },
    exit: function(){
        this.set({
            card: null,
            accountsPromise: null,
            transactions: null,
            currentTransaction: null
        });
    },
    chooseDeposit: function() {
        this.currentTransaction = new Deposit({
            card: this.card
        });
    },
    chooseWithdraw: function() {
        this.currentTransaction = new Withdrawal({
            card: this.card
        });
    },
    chooseAccount: function(account) {
        this.currentTransaction.account = account;
    }
});

can.Component.extend({
    tag: &quot;atm-machine&quot;,
    view: can.stache.from(&quot;atm-template&quot;),
    ViewModel: ATM,
});

document.body.insertBefore(
    can.stache.from(&quot;app-template&quot;)({}),
    document.body.firstChild
);

// ========================================
// TESTS
// ========================================

QUnit.module(&quot;ATM system&quot;, {
    setup: function() {
        can.fixture.delay = 1;
    },
    teardown: function() {
        can.fixture.delay = 2000;
    }
});

QUnit.asyncTest(&quot;Valid Card&quot;, function() {

    var c = new Card({
        number: &quot;01234567890&quot;,
        pin: 1234
    });

    QUnit.equal(c.state, &quot;unverified&quot;);

    c.verify();

    QUnit.equal(c.state, &quot;verifying&quot;, &quot;card is verifying&quot;);

    c.on(&quot;state&quot;, function(ev, newVal) {

        QUnit.equal(newVal, &quot;verified&quot;, &quot;card is verified&quot;);

        QUnit.start();
    });
});

QUnit.asyncTest(&quot;Invalid Card&quot;, function() {

    var c = new Card({});

    QUnit.equal(c.state, &quot;unverified&quot;);

    c.verify();

    QUnit.equal(c.state, &quot;verifying&quot;, &quot;card is verifying&quot;);

    c.on(&quot;state&quot;, function(ev, newVal) {

        QUnit.equal(newVal, &quot;invalid&quot;, &quot;card is invalid&quot;);

        QUnit.start();
    });
});

QUnit.asyncTest(&quot;Deposit&quot;, 6, function() {

    var card = new Card({
        number: &quot;0123456789&quot;,
        pin: &quot;1122&quot;
    });

    var deposit = new Deposit({
        amount: 100,
        card: card
    });

    equal(deposit.state, &quot;invalid&quot;);

    var startingBalance;

    Account.getList(card.serialize()).then(function(accounts) {
        QUnit.ok(true, &quot;got accounts&quot;);
        deposit.account = accounts[0];
        startingBalance = accounts[0].balance;
    });

    deposit.on(&quot;state&quot;, function(ev, newVal) {
        if (newVal === &quot;ready&quot;) {

            QUnit.ok(true, &quot;deposit is ready&quot;);
            deposit.execute();

        } else if (newVal === &quot;executing&quot;) {

            QUnit.ok(true, &quot;executing a deposit&quot;);

        } else if (newVal === &quot;executed&quot;) {

            QUnit.ok(true, &quot;executed a deposit&quot;);
            equal(deposit.account.balance, 100 + startingBalance);
            start();

        }
    });
});

QUnit.asyncTest(&quot;ATM basics&quot;, function() {

    var atm = new ATM();

    equal(atm.state, &quot;readingCard&quot;, &quot;starts at reading card state&quot;);

    atm.cardNumber(&quot;01233456789&quot;);

    equal(atm.state, &quot;readingPin&quot;, &quot;moves to reading card state&quot;);

    atm.pinNumber(&quot;1234&quot;);

    ok(atm.state, &quot;readingPin&quot;, &quot;remain in the reading pin state until verifyied&quot;);

    atm.on(&quot;state&quot;, function(ev, newVal) {

        if (newVal === &quot;choosingTransaction&quot;) {

            QUnit.ok(true, &quot;in choosingTransaction&quot;);
            atm.chooseDeposit();

        } else if (newVal === &quot;pickingAccount&quot;) {

            QUnit.ok(true, &quot;in picking account state&quot;);
            atm.accountsPromise.then(function(accounts){
                atm.chooseAccount(accounts[0]);
            });

        } else if (newVal === &quot;depositInfo&quot;) {

            QUnit.ok(true, &quot;in depositInfo state&quot;);
            QUnit.start();

        }
    });
});

</code></pre>
<p><span line-highlight='210-216,264-266,398-407,only'></span></p>
<h2>Deposit Info page and test</h2>
<p>In this section, we will:</p>
<ul>
<li>Allow the user to enter the amount of a deposit and go to the <strong>Successful Transaction</strong> page.</li>
<li>Add tests to <strong>ATM Basics</strong> test.</li>
</ul>
<p>Update the <code>HTML</code> tab to:</p>
<ul>
<li>Ask the user how much they would like to deposit into the account.</li>
<li>Update <code>currentTransaction.amount</code> with an <code>&lt;input&gt;</code>'s <code>value</code>.</li>
<li>If the transaction is executing, show a spinner.</li>
<li>If the transaction is not executed:
<ul>
<li>show a <strong>Deposit</strong> button that will be
active only once the transaction has a value.</li>
<li>show a <strong>cancel</strong> button that will clear this transaction.</li>
</ul></li>
</ul>
<pre><code class="language-html">&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;meta name=&quot;description&quot; content=&quot;CanJS 3.0 - ATM Guide - Setup&quot;&gt;
  &lt;meta charset=&quot;utf-8&quot;&gt;
  &lt;meta name=&quot;viewport&quot; content=&quot;width=device-width&quot;&gt;
  &lt;title&gt;JS Bin&lt;/title&gt;

&lt;/head&gt;
&lt;body&gt;

&lt;script type='text/stache' id='atm-template'&gt;
&lt;div class=&quot;screen&quot;&gt;
    &lt;div class=&quot;screen-content&quot;&gt;
        &lt;div class=&quot;screen-glass&quot;&gt;

{{#switch state}}
    {{#case &quot;readingCard&quot;}}
        &lt;h2&gt;Reading Card&lt;/h2&gt;
        &lt;p&gt;Welcome to canATM where there are &lt;strong&gt;never&lt;/strong&gt;
          fees!&lt;/p&gt;
        &lt;/p&gt;
        &lt;p&gt;
            Enter Card Number:
            &lt;input name=&quot;card&quot; ($enter)=&quot;cardNumber(%element.value)&quot;/&gt;
        &lt;/p&gt;
    {{/case}}

    {{#case &quot;readingPin&quot;}}
        &lt;h2&gt;Reading Pin&lt;/h2&gt;
        &lt;p&gt;
            Enter Pin Number:
            &lt;input name=&quot;pin&quot; type=&quot;password&quot;
                autofocus
                {{#is card.state &quot;verifying&quot;}}DISABLED{{/is}}
                ($enter)=&quot;pinNumber(%element.value)&quot;/&gt;

            {{#is card.state &quot;verifying&quot;}}
                &lt;div class='warn'&gt;
                    &lt;p&gt;
                    &lt;img src=&quot;http://v3.canjs.com/docs/images/loader.gif&quot;/&gt;
                    verifying
                    &lt;/p&gt;
                &lt;/div&gt;
            {{/is}}
        &lt;/p&gt;
        &lt;a href=&quot;javascript://&quot; ($click)=&quot;exit()&quot;&gt;exit&lt;/a&gt;
    {{/case}}

    {{#case &quot;choosingTransaction&quot;}}
        &lt;h2&gt;Choose Transaction&lt;/h2&gt;
        &lt;p&gt;What would you like to do?&lt;/p&gt;
        &lt;nav&gt;
            &lt;ul&gt;
                &lt;li ($click)=&quot;chooseDeposit()&quot;&gt;Deposit&lt;/li&gt;
                &lt;li ($click)=&quot;chooseWithdraw()&quot;&gt;Withdraw&lt;/li&gt;
                &lt;li ($click)=&quot;printReceiptAndExit()&quot;&gt;Exit&lt;/li&gt;
            &lt;/ul&gt;
        &lt;/nav&gt;
    {{/case}}

    {{#case  &quot;pickingAccount&quot;}}
        &lt;h2&gt;Pick Account&lt;/h2&gt;
        &lt;p&gt;Please pick your account:&lt;/p&gt;
        {{#if accountsPromise.isPending}}
            &lt;div class='warn'&gt;
                &lt;p&gt;
                    &lt;img src=&quot;http://v3.canjs.com/docs/images/loader.gif&quot;/&gt;
                    Loading Accounts ...
                &lt;/p&gt;
            &lt;/div&gt;
        {{else}}
            &lt;ul&gt;
                {{#each accountsPromise.value}}
                    &lt;li ($click)=&quot;chooseAccount(.)&quot;&gt;{{name}} - ${{balance}}&lt;/li&gt;
                {{/each}}
            &lt;/ul&gt;
        {{/if}}
    {{/case}}

    {{#case &quot;depositInfo&quot;}}
        &lt;h2&gt;Deposit&lt;/h2&gt;
        &lt;p&gt;
            How much would you like to deposit
            into {{currentTransaction.account.name}}
            (${{currentTransaction.account.balance}})?

            &lt;input name=&quot;deposit&quot; {($value)}=&quot;currentTransaction.amount&quot;/&gt;
        &lt;/p&gt;

        {{#eq currentTransaction.state &quot;executing&quot;}}
            &lt;div class='warn'&gt;
                &lt;p&gt;
                &lt;img src=&quot;http://v3.canjs.com/docs/images/loader.gif&quot;/&gt;
                executing
                &lt;/p&gt;
            &lt;/div&gt;
        {{else}}
            &lt;button ($click)=&quot;currentTransaction.execute()&quot;
                {{^eq currentTransaction.state &quot;ready&quot;}}DISABLED{{/eq}}&gt;
                Deposit
            &lt;/button&gt;
            &lt;a href=&quot;javascript://&quot; ($click)=&quot;removeTransaction()&quot;&gt;cancel&lt;/a&gt;
        {{/eq}}
    {{/case}}

    {{#case &quot;withdrawalInfo&quot;}}
        &lt;h2&gt;Withdraw&lt;/h2&gt;
    {{/case}}

    {{#case &quot;successfulTransaction&quot;}}
        &lt;h2&gt;Transaction Successful!&lt;/h2&gt;
    {{/case}}

    {{#case  &quot;printingReceipt&quot;}}
        &lt;h2&gt;Printing Receipt&lt;/h2&gt;
    {{/case}}

    {{#default}}
        &lt;h2&gt;Error&lt;/h2&gt;
        &lt;p&gt;Invalid state - {{state}}&lt;/p&gt;
    {{/default}}

{{/switch}}

        &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;
&lt;/script&gt;

&lt;script type='text/stache' id='app-template'&gt;
&lt;div class=&quot;title&quot;&gt;
    &lt;h1&gt;canATM&lt;/h1&gt;
&lt;/div&gt;
&lt;atm-machine/&gt;
&lt;/script&gt;

&lt;script src=&quot;https://code.jquery.com/jquery-2.2.4.js&quot;&gt;&lt;/script&gt;
&lt;script src=&quot;https://unpkg.com/can/dist/global/can.all.js&quot;&gt;&lt;/script&gt;

&lt;div id=&quot;qunit&quot;&gt;&lt;/div&gt;
&lt;link rel=&quot;stylesheet&quot; href=&quot;http://code.jquery.com/qunit/qunit-1.12.0.css&quot;&gt;
&lt;script src=&quot;http://code.jquery.com/qunit/qunit-1.12.0.js&quot;&gt;&lt;/script&gt;

&lt;/body&gt;
&lt;/html&gt;

</code></pre>
<p><span line-highlight='83-104,only'></span>
Update the <code>ATM</code> view model in the <code>JavaScript</code> tab to:</p>
<ul>
<li>Change <code>state</code> to <code>&quot;successfulTransaction&quot;</code> if the <code>currentTransaction</code> was executed.</li>
<li>Add a <code>removeTransaction</code> method that removes the <code>currentTransaction</code>, which will revert state
to <code>&quot;choosingTransaction&quot;</code>.</li>
</ul>
<p>Update the <code>ATM basics</code> test in the <code>JavaScript</code> tab to:</p>
<ul>
<li>Add an <code>amount</code> to the <code>currentTransaction</code>.</li>
<li>Make sure the <code>currentTransaction</code> is <code>ready</code> to be executed.</li>
<li>Execute the <code>currentTransaction</code> and make sure that the <code>state</code> stays as <code>&quot;depositInfo&quot;</code> until
the transaction is successful.</li>
<li>Verify the state changed to <code>&quot;successfulTransaction&quot;</code>.</li>
</ul>
<pre><code class="language-js">// ========================================
// CODE
// ========================================

can.fixture({
    &quot;/verifyCard&quot;: function(request, response) {
        if (!request.data || !request.data.number || !request.data.pin) {
            response(400, {});
        } else {
            return {};
        }
    },
    &quot;/accounts&quot;: function() {
        return {
            data: [{
                balance: 100,
                id: 1,
                name: &quot;checking&quot;
            }, {
                balance: 10000,
                id: 2,
                name: &quot;savings&quot;
            }]
        };
    },
    &quot;/deposit&quot;: function() {
        return {};
    },
    &quot;/withdrawal&quot;: function() {
        return {};
    }
});
can.fixture.delay = 1000;

var Card = can.DefineMap.extend({
    number: &quot;string&quot;,
    pin: &quot;string&quot;,
    state: {
        value: &quot;unverified&quot;,
        serialize: false
    },
    verify: function() {

        this.state = &quot;verifying&quot;;

        var self = this;
        return can.ajax({
            type: &quot;POST&quot;,
            url: &quot;/verifyCard&quot;,
            data: this.serialize()
        }).then(
            function() {
                self.state = &quot;verified&quot;;
                return self;
            },
            function() {
                self.state = &quot;invalid&quot;;
                return self;
            });
    }
});

var Account = can.DefineMap.extend(&quot;Account&quot;, {
    id: &quot;number&quot;,
    balance: &quot;number&quot;,
    name: &quot;string&quot;
});
Account.List = can.DefineList.extend(&quot;AccountList&quot;, {
    &quot;*&quot;: Account
});

can.connect.baseMap({
    url: &quot;/accounts&quot;,
    Map: Account,
    List: Account.List,
    name: &quot;accounts&quot;
});

var Transaction = can.DefineMap.extend({
    account: Account,
    card: Card,
    executing: {
        type: &quot;boolean&quot;,
        value: false
    },
    executed: {
        type: &quot;boolean&quot;,
        value: false
    },
    rejected: &quot;any&quot;,
    get ready(){
        throw new Error(&quot;Transaction::ready must be provided by extended type&quot;);
    },
    get state() {
        if (this.rejected) {
            return &quot;rejected&quot;;
        }
        if (this.executed) {
            return &quot;executed&quot;;
        }
        if (this.executing) {
            return &quot;executing&quot;;
        }
        // make sure there's an amount, account, and card
        if (this.ready) {
            return &quot;ready&quot;;
        }
        return &quot;invalid&quot;;
    },
    executeStart: function(){
        throw new Error(&quot;Transaction::executeStart must be provided by extended type&quot;);
    },
    executeEnd: function(){
        throw new Error(&quot;Transaction::executeEnd must be provided by extended type&quot;);
    },
    execute: function() {
        if (this.state === &quot;ready&quot;) {

            this.executing = true;

            var def = this.executeStart(),
                self = this;

            def.then(function() {
                can.batch.start();
                self.set({
                    executing: false,
                    executed: true
                });
                self.executeEnd();
                can.batch.stop();
            }, function(reason){
                self.set({
                    executing: false,
                    executed: true,
                    rejected: reason
                });
            });
        }
    }
});

var Deposit = Transaction.extend({
    amount: &quot;number&quot;,
    get ready() {
        return this.amount &gt; 0 &amp;&amp;
            this.account &amp;&amp;
            this.card;
    },
    executeStart: function() {
        return can.ajax({
            type: &quot;POST&quot;,
            url: &quot;/deposit&quot;,
            data: {
                card: this.card.serialize(),
                accountId: this.account.id,
                amount: this.amount
            }
        });
    },
    executeEnd: function(data) {
        this.account.balance = this.account.balance + this.amount;
    }
});

var Withdrawal = Transaction.extend({
    amount: &quot;number&quot;,
    get ready() {
        return this.amount &gt; 0 &amp;&amp;
            this.account &amp;&amp;
            this.card;
    },
    executeStart: function() {
        return can.ajax({
            type: &quot;POST&quot;,
            url: &quot;/withdrawal&quot;,
            data: {
                card: this.card.serialize(),
                accountId: this.account.id,
                amount: this.amount
            }
        });
    },
    executeEnd: function(data) {
        this.account.balance = this.account.balance - this.amount;
    }
});

var ATM = can.DefineMap.extend({
    // stateful properties
    card: Card,
    accountsPromise: &quot;any&quot;,
    transactions: can.DefineList,
    currentTransaction: {
        set: function(newTransaction) {
            var currentTransaction = this.currentTransaction;
            if (this.transactions &amp;&amp; currentTransaction &amp;&amp;
                currentTransaction.state === &quot;executed&quot;) {

                this.transactions.push(currentTransaction);
            }
            return newTransaction;
        }
    },

    // derived properties
    get state(){

        if (this.currentTransaction) {
            if (this.currentTransaction.state === &quot;executed&quot;) {
                return &quot;successfulTransaction&quot;;
            }

            if (this.currentTransaction.account) {
                if (this.currentTransaction instanceof Deposit) {
                    return &quot;depositInfo&quot;;
                } else {
                    return &quot;withdrawalInfo&quot;;
                }
            }

            return &quot;pickingAccount&quot;;
        }

        if(this.card) {
            if (this.card.state === &quot;verified&quot;) {
                return &quot;choosingTransaction&quot;;
            }
            return &quot;readingPin&quot;;
        }
        return &quot;readingCard&quot;;
    },

    // methods
    cardNumber: function(number) {
        this.card = new Card({
            number: number
        });
    },
    pinNumber: function(pin) {
        var card = this.card;

        card.pin = pin;
        this.transactions = new can.DefineList();
        this.accountsPromise = card.verify().then(function(card) {

            return Account.getList(card.serialize());
        });
    },
    exit: function(){
        this.set({
            card: null,
            accountsPromise: null,
            transactions: null,
            currentTransaction: null
        });
    },
    chooseDeposit: function() {
        this.currentTransaction = new Deposit({
            card: this.card
        });
    },
    chooseWithdraw: function() {
        this.currentTransaction = new Withdrawal({
            card: this.card
        });
    },
    chooseAccount: function(account) {
        this.currentTransaction.account = account;
    },
    removeTransaction: function() {
        this.currentTransaction = null;
    }
});

can.Component.extend({
    tag: &quot;atm-machine&quot;,
    view: can.stache.from(&quot;atm-template&quot;),
    ViewModel: ATM,
});

document.body.insertBefore(
    can.stache.from(&quot;app-template&quot;)({}),
    document.body.firstChild
);

// ========================================
// TESTS
// ========================================

QUnit.module(&quot;ATM system&quot;, {
    setup: function() {
        can.fixture.delay = 1;
    },
    teardown: function() {
        can.fixture.delay = 2000;
    }
});

QUnit.asyncTest(&quot;Valid Card&quot;, function() {

    var c = new Card({
        number: &quot;01234567890&quot;,
        pin: 1234
    });

    QUnit.equal(c.state, &quot;unverified&quot;);

    c.verify();

    QUnit.equal(c.state, &quot;verifying&quot;, &quot;card is verifying&quot;);

    c.on(&quot;state&quot;, function(ev, newVal) {

        QUnit.equal(newVal, &quot;verified&quot;, &quot;card is verified&quot;);

        QUnit.start();
    });
});

QUnit.asyncTest(&quot;Invalid Card&quot;, function() {

    var c = new Card({});

    QUnit.equal(c.state, &quot;unverified&quot;);

    c.verify();

    QUnit.equal(c.state, &quot;verifying&quot;, &quot;card is verifying&quot;);

    c.on(&quot;state&quot;, function(ev, newVal) {

        QUnit.equal(newVal, &quot;invalid&quot;, &quot;card is invalid&quot;);

        QUnit.start();
    });
});

QUnit.asyncTest(&quot;Deposit&quot;, 6, function() {

    var card = new Card({
        number: &quot;0123456789&quot;,
        pin: &quot;1122&quot;
    });

    var deposit = new Deposit({
        amount: 100,
        card: card
    });

    equal(deposit.state, &quot;invalid&quot;);

    var startingBalance;

    Account.getList(card.serialize()).then(function(accounts) {
        QUnit.ok(true, &quot;got accounts&quot;);
        deposit.account = accounts[0];
        startingBalance = accounts[0].balance;
    });

    deposit.on(&quot;state&quot;, function(ev, newVal) {
        if (newVal === &quot;ready&quot;) {

            QUnit.ok(true, &quot;deposit is ready&quot;);
            deposit.execute();

        } else if (newVal === &quot;executing&quot;) {

            QUnit.ok(true, &quot;executing a deposit&quot;);

        } else if (newVal === &quot;executed&quot;) {

            QUnit.ok(true, &quot;executed a deposit&quot;);
            equal(deposit.account.balance, 100 + startingBalance);
            start();

        }
    });
});

QUnit.asyncTest(&quot;ATM basics&quot;, function() {

    var atm = new ATM();

    equal(atm.state, &quot;readingCard&quot;, &quot;starts at reading card state&quot;);

    atm.cardNumber(&quot;01233456789&quot;);

    equal(atm.state, &quot;readingPin&quot;, &quot;moves to reading card state&quot;);

    atm.pinNumber(&quot;1234&quot;);

    ok(atm.state, &quot;readingPin&quot;, &quot;remain in the reading pin state until verifyied&quot;);

    atm.on(&quot;state&quot;, function(ev, newVal) {

        if (newVal === &quot;choosingTransaction&quot;) {

            QUnit.ok(true, &quot;in choosingTransaction&quot;);
            atm.chooseDeposit();

        } else if (newVal === &quot;pickingAccount&quot;) {

            QUnit.ok(true, &quot;in picking account state&quot;);
            atm.accountsPromise.then(function(accounts){
                atm.chooseAccount(accounts[0]);
            });

        } else if (newVal === &quot;depositInfo&quot;) {

            QUnit.ok(true, &quot;in depositInfo state&quot;);
            var currentTransaction = atm.currentTransaction;
            currentTransaction.amount = 120;
            QUnit.ok(currentTransaction.ready, &quot;we are ready to execute&quot;);
            currentTransaction.execute();
            QUnit.equal(atm.state, &quot;depositInfo&quot;, &quot;in deposit state until successful&quot;);

        } else if (newVal === &quot;successfulTransaction&quot;) {

            QUnit.ok(true, &quot;in successfulTransaction state&quot;);
            QUnit.start();

        }
    });
});

</code></pre>
<p><span line-highlight='189,210-212,271-273,381,412-423,only'></span>
When complete, you should be able to enter a deposit amount and see that
the transaction was successful.</p>
<h2>Withdrawal Info page</h2>
<p>In this section, we will:</p>
<ul>
<li>Allow the user to enter the amount of a withdrawal and go to the <strong>Successful Transaction</strong> page.</li>
</ul>
<p>Update the <code>HTML</code> tab to:</p>
<ul>
<li>Add a <strong>Withdraw</strong> page that works very similar to the <strong>Deposit</strong> page.</li>
</ul>
<pre><code class="language-html">&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;meta name=&quot;description&quot; content=&quot;CanJS 3.0 - ATM Guide - Setup&quot;&gt;
  &lt;meta charset=&quot;utf-8&quot;&gt;
  &lt;meta name=&quot;viewport&quot; content=&quot;width=device-width&quot;&gt;
  &lt;title&gt;JS Bin&lt;/title&gt;

&lt;/head&gt;
&lt;body&gt;

&lt;script type='text/stache' id='atm-template'&gt;
&lt;div class=&quot;screen&quot;&gt;
    &lt;div class=&quot;screen-content&quot;&gt;
        &lt;div class=&quot;screen-glass&quot;&gt;

{{#switch state}}
    {{#case &quot;readingCard&quot;}}
        &lt;h2&gt;Reading Card&lt;/h2&gt;
        &lt;p&gt;Welcome to canATM where there are &lt;strong&gt;never&lt;/strong&gt;
          fees!&lt;/p&gt;
        &lt;/p&gt;
        &lt;p&gt;
            Enter Card Number:
            &lt;input name=&quot;card&quot; ($enter)=&quot;cardNumber(%element.value)&quot;/&gt;
        &lt;/p&gt;
    {{/case}}

    {{#case &quot;readingPin&quot;}}
        &lt;h2&gt;Reading Pin&lt;/h2&gt;
        &lt;p&gt;
            Enter Pin Number:
            &lt;input name=&quot;pin&quot; type=&quot;password&quot;
                autofocus
                {{#is card.state &quot;verifying&quot;}}DISABLED{{/is}}
                ($enter)=&quot;pinNumber(%element.value)&quot;/&gt;

            {{#is card.state &quot;verifying&quot;}}
                &lt;div class='warn'&gt;
                    &lt;p&gt;
                    &lt;img src=&quot;http://v3.canjs.com/docs/images/loader.gif&quot;/&gt;
                    verifying
                    &lt;/p&gt;
                &lt;/div&gt;
            {{/is}}
        &lt;/p&gt;
        &lt;a href=&quot;javascript://&quot; ($click)=&quot;exit()&quot;&gt;exit&lt;/a&gt;
    {{/case}}

    {{#case &quot;choosingTransaction&quot;}}
        &lt;h2&gt;Choose Transaction&lt;/h2&gt;
        &lt;p&gt;What would you like to do?&lt;/p&gt;
        &lt;nav&gt;
            &lt;ul&gt;
                &lt;li ($click)=&quot;chooseDeposit()&quot;&gt;Deposit&lt;/li&gt;
                &lt;li ($click)=&quot;chooseWithdraw()&quot;&gt;Withdraw&lt;/li&gt;
                &lt;li ($click)=&quot;printReceiptAndExit()&quot;&gt;Exit&lt;/li&gt;
            &lt;/ul&gt;
        &lt;/nav&gt;
    {{/case}}

    {{#case  &quot;pickingAccount&quot;}}
        &lt;h2&gt;Pick Account&lt;/h2&gt;
        &lt;p&gt;Please pick your account:&lt;/p&gt;
        {{#if accountsPromise.isPending}}
            &lt;div class='warn'&gt;
                &lt;p&gt;
                    &lt;img src=&quot;http://v3.canjs.com/docs/images/loader.gif&quot;/&gt;
                    Loading Accounts ...
                &lt;/p&gt;
            &lt;/div&gt;
        {{else}}
            &lt;ul&gt;
                {{#each accountsPromise.value}}
                    &lt;li ($click)=&quot;chooseAccount(.)&quot;&gt;{{name}} - ${{balance}}&lt;/li&gt;
                {{/each}}
            &lt;/ul&gt;
        {{/if}}
    {{/case}}

    {{#case &quot;depositInfo&quot;}}
        &lt;h2&gt;Deposit&lt;/h2&gt;
        &lt;p&gt;
            How much would you like to deposit
            into {{currentTransaction.account.name}}
            (${{currentTransaction.account.balance}})?

            &lt;input name=&quot;deposit&quot; {($value)}=&quot;currentTransaction.amount&quot;/&gt;
        &lt;/p&gt;

        {{#eq currentTransaction.state &quot;executing&quot;}}
            &lt;div class='warn'&gt;
                &lt;p&gt;
                &lt;img src=&quot;http://v3.canjs.com/docs/images/loader.gif&quot;/&gt;
                executing
                &lt;/p&gt;
            &lt;/div&gt;
        {{else}}
            &lt;button ($click)=&quot;currentTransaction.execute()&quot;
                {{^eq currentTransaction.state &quot;ready&quot;}}DISABLED{{/eq}}&gt;
                Deposit
            &lt;/button&gt;
            &lt;a href=&quot;javascript://&quot; ($click)=&quot;removeTransaction()&quot;&gt;cancel&lt;/a&gt;
        {{/eq}}
    {{/case}}

    {{#case &quot;withdrawalInfo&quot;}}
        &lt;h2&gt;Withdraw&lt;/h2&gt;
        &lt;p&gt;
            How much would you like to withdraw
            from {{currentTransaction.account.name}}
            (${{currentTransaction.account.balance}})?

            &lt;input name=&quot;withdrawl&quot; {($value)}=&quot;currentTransaction.amount&quot;/&gt;
        &lt;/p&gt;
        {{#eq currentTransaction.state &quot;executing&quot;}}
            &lt;div class='warn'&gt;
                &lt;p&gt;
                &lt;img src=&quot;http://v3.canjs.com/docs/images/loader.gif&quot;/&gt;
                executing
                &lt;/p&gt;
            &lt;/div&gt;
        {{else}}
            &lt;button ($click)=&quot;currentTransaction.execute()&quot;
                {{^eq currentTransaction.state &quot;ready&quot;}}DISABLED{{/eq}}&gt;
                Withdraw
            &lt;/button&gt;
            &lt;a href=&quot;javascript://&quot; ($click)=&quot;removeTransaction()&quot;&gt;cancel&lt;/a&gt;
        {{/eq}}
    {{/case}}

    {{#case &quot;successfulTransaction&quot;}}
        &lt;h2&gt;Transaction Successful!&lt;/h2&gt;
    {{/case}}

    {{#case  &quot;printingReceipt&quot;}}
        &lt;h2&gt;Printing Receipt&lt;/h2&gt;
    {{/case}}

    {{#default}}
        &lt;h2&gt;Error&lt;/h2&gt;
        &lt;p&gt;Invalid state - {{state}}&lt;/p&gt;
    {{/default}}

{{/switch}}

        &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;
&lt;/script&gt;

&lt;script type='text/stache' id='app-template'&gt;
&lt;div class=&quot;title&quot;&gt;
    &lt;h1&gt;canATM&lt;/h1&gt;
&lt;/div&gt;
&lt;atm-machine/&gt;
&lt;/script&gt;

&lt;script src=&quot;https://code.jquery.com/jquery-2.2.4.js&quot;&gt;&lt;/script&gt;
&lt;script src=&quot;https://unpkg.com/can/dist/global/can.all.js&quot;&gt;&lt;/script&gt;

&lt;div id=&quot;qunit&quot;&gt;&lt;/div&gt;
&lt;link rel=&quot;stylesheet&quot; href=&quot;http://code.jquery.com/qunit/qunit-1.12.0.css&quot;&gt;
&lt;script src=&quot;http://code.jquery.com/qunit/qunit-1.12.0.js&quot;&gt;&lt;/script&gt;

&lt;/body&gt;
&lt;/html&gt;

</code></pre>
<p><span line-highlight='109-129,only'></span>
When complete, you should be able to enter a withdrawal amount and see that
the transaction was successful.</p>
<blockquote>
<p><strong>Optional:</strong> Challenge yourself by adding a test for the <code>withdrawalInfo</code> state of an atm instance.  Consider the progression of states needed to make it to the <code>withdrawalInfo</code> state.  How is it different from the <strong>ATM basics</strong> test we already have?</p>
</blockquote>
<h2>Transaction Successful page</h2>
<p>In this section, we will:</p>
<ul>
<li>Show the result of the transaction.</li>
</ul>
<p>Update the <code>HTML</code> tab to:</p>
<ul>
<li>List out the account balance.</li>
<li>Add buttons to:
<ul>
<li>start another transaction, or</li>
<li>print a receipt and exit the ATM. (<code>printReceiptAndExit</code> will be implemented in the next section)</li>
</ul></li>
</ul>
<pre><code class="language-html">&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;meta name=&quot;description&quot; content=&quot;CanJS 3.0 - ATM Guide - Setup&quot;&gt;
  &lt;meta charset=&quot;utf-8&quot;&gt;
  &lt;meta name=&quot;viewport&quot; content=&quot;width=device-width&quot;&gt;
  &lt;title&gt;JS Bin&lt;/title&gt;

&lt;/head&gt;
&lt;body&gt;

&lt;script type='text/stache' id='atm-template'&gt;
&lt;div class=&quot;screen&quot;&gt;
    &lt;div class=&quot;screen-content&quot;&gt;
        &lt;div class=&quot;screen-glass&quot;&gt;

{{#switch state}}
    {{#case &quot;readingCard&quot;}}
        &lt;h2&gt;Reading Card&lt;/h2&gt;
        &lt;p&gt;Welcome to canATM where there are &lt;strong&gt;never&lt;/strong&gt;
          fees!&lt;/p&gt;
        &lt;/p&gt;
        &lt;p&gt;
            Enter Card Number:
            &lt;input name=&quot;card&quot; ($enter)=&quot;cardNumber(%element.value)&quot;/&gt;
        &lt;/p&gt;
    {{/case}}

    {{#case &quot;readingPin&quot;}}
        &lt;h2&gt;Reading Pin&lt;/h2&gt;
        &lt;p&gt;
            Enter Pin Number:
            &lt;input name=&quot;pin&quot; type=&quot;password&quot;
                autofocus
                {{#is card.state &quot;verifying&quot;}}DISABLED{{/is}}
                ($enter)=&quot;pinNumber(%element.value)&quot;/&gt;

            {{#is card.state &quot;verifying&quot;}}
                &lt;div class='warn'&gt;
                    &lt;p&gt;
                    &lt;img src=&quot;http://v3.canjs.com/docs/images/loader.gif&quot;/&gt;
                    verifying
                    &lt;/p&gt;
                &lt;/div&gt;
            {{/is}}
        &lt;/p&gt;
        &lt;a href=&quot;javascript://&quot; ($click)=&quot;exit()&quot;&gt;exit&lt;/a&gt;
    {{/case}}

    {{#case &quot;choosingTransaction&quot;}}
        &lt;h2&gt;Choose Transaction&lt;/h2&gt;
        &lt;p&gt;What would you like to do?&lt;/p&gt;
        &lt;nav&gt;
            &lt;ul&gt;
                &lt;li ($click)=&quot;chooseDeposit()&quot;&gt;Deposit&lt;/li&gt;
                &lt;li ($click)=&quot;chooseWithdraw()&quot;&gt;Withdraw&lt;/li&gt;
                &lt;li ($click)=&quot;printReceiptAndExit()&quot;&gt;Exit&lt;/li&gt;
            &lt;/ul&gt;
        &lt;/nav&gt;
    {{/case}}

    {{#case  &quot;pickingAccount&quot;}}
        &lt;h2&gt;Pick Account&lt;/h2&gt;
        &lt;p&gt;Please pick your account:&lt;/p&gt;
        {{#if accountsPromise.isPending}}
            &lt;div class='warn'&gt;
                &lt;p&gt;
                    &lt;img src=&quot;http://v3.canjs.com/docs/images/loader.gif&quot;/&gt;
                    Loading Accounts ...
                &lt;/p&gt;
            &lt;/div&gt;
        {{else}}
            &lt;ul&gt;
                {{#each accountsPromise.value}}
                    &lt;li ($click)=&quot;chooseAccount(.)&quot;&gt;{{name}} - ${{balance}}&lt;/li&gt;
                {{/each}}
            &lt;/ul&gt;
        {{/if}}
    {{/case}}

    {{#case &quot;depositInfo&quot;}}
        &lt;h2&gt;Deposit&lt;/h2&gt;
        &lt;p&gt;
            How much would you like to deposit
            into {{currentTransaction.account.name}}
            (${{currentTransaction.account.balance}})?

            &lt;input name=&quot;deposit&quot; {($value)}=&quot;currentTransaction.amount&quot;/&gt;
        &lt;/p&gt;

        {{#eq currentTransaction.state &quot;executing&quot;}}
            &lt;div class='warn'&gt;
                &lt;p&gt;
                &lt;img src=&quot;http://v3.canjs.com/docs/images/loader.gif&quot;/&gt;
                executing
                &lt;/p&gt;
            &lt;/div&gt;
        {{else}}
            &lt;button ($click)=&quot;currentTransaction.execute()&quot;
                {{^eq currentTransaction.state &quot;ready&quot;}}DISABLED{{/eq}}&gt;
                Deposit
            &lt;/button&gt;
            &lt;a href=&quot;javascript://&quot; ($click)=&quot;removeTransaction()&quot;&gt;cancel&lt;/a&gt;
        {{/eq}}
    {{/case}}

    {{#case &quot;withdrawalInfo&quot;}}
        &lt;h2&gt;Withdraw&lt;/h2&gt;
        &lt;p&gt;
            How much would you like to withdraw
            from {{currentTransaction.account.name}}
            (${{currentTransaction.account.balance}})?

            &lt;input name=&quot;withdrawl&quot; {($value)}=&quot;currentTransaction.amount&quot;/&gt;
        &lt;/p&gt;
        {{#eq currentTransaction.state &quot;executing&quot;}}
            &lt;div class='warn'&gt;
                &lt;p&gt;
                &lt;img src=&quot;http://v3.canjs.com/docs/images/loader.gif&quot;/&gt;
                executing
                &lt;/p&gt;
            &lt;/div&gt;
        {{else}}
            &lt;button ($click)=&quot;currentTransaction.execute()&quot;
                {{^eq currentTransaction.state &quot;ready&quot;}}DISABLED{{/eq}}&gt;
                Withdraw
            &lt;/button&gt;
            &lt;a href=&quot;javascript://&quot; ($click)=&quot;removeTransaction()&quot;&gt;cancel&lt;/a&gt;
        {{/eq}}
    {{/case}}

    {{#case &quot;successfulTransaction&quot;}}
        &lt;h2&gt;Transaction Successful!&lt;/h2&gt;
        &lt;p&gt;
            {{currentTransaction.account.name}} has
            ${{currentTransaction.account.balance}}.
        &lt;/p&gt;
        &lt;p&gt;What would you like to do?&lt;/p&gt;
        &lt;nav&gt;
            &lt;ul&gt;
                &lt;li ($click)=&quot;removeTransaction()&quot;&gt;Another transaction&lt;/li&gt;
                &lt;li ($click)=&quot;printReceiptAndExit()&quot;&gt;Exit&lt;/li&gt;
            &lt;/ul&gt;
        &lt;/nav&gt;
    {{/case}}

    {{#case  &quot;printingReceipt&quot;}}
        &lt;h2&gt;Printing Receipt&lt;/h2&gt;
    {{/case}}

    {{#default}}
        &lt;h2&gt;Error&lt;/h2&gt;
        &lt;p&gt;Invalid state - {{state}}&lt;/p&gt;
    {{/default}}

{{/switch}}

        &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;
&lt;/script&gt;

&lt;script type='text/stache' id='app-template'&gt;
&lt;div class=&quot;title&quot;&gt;
    &lt;h1&gt;canATM&lt;/h1&gt;
&lt;/div&gt;
&lt;atm-machine/&gt;
&lt;/script&gt;

&lt;script src=&quot;https://code.jquery.com/jquery-2.2.4.js&quot;&gt;&lt;/script&gt;
&lt;script src=&quot;https://unpkg.com/can/dist/global/can.all.js&quot;&gt;&lt;/script&gt;

&lt;div id=&quot;qunit&quot;&gt;&lt;/div&gt;
&lt;link rel=&quot;stylesheet&quot; href=&quot;http://code.jquery.com/qunit/qunit-1.12.0.css&quot;&gt;
&lt;script src=&quot;http://code.jquery.com/qunit/qunit-1.12.0.js&quot;&gt;&lt;/script&gt;

&lt;/body&gt;
&lt;/html&gt;

</code></pre>
<p><span line-highlight='134-144,only'></span>
When complete, you should be able to make a deposit or withdrawal, see the updated account balance
and then start another transaction.</p>
<h2>Printing Recipe page and test</h2>
<p>In this section, we will:</p>
<ul>
<li>Make it possible to see a receipt of all transactions and exit the ATM.</li>
</ul>
<p>Update the <code>HTML</code> tab to:</p>
<ul>
<li>List out all the transactions the user has completed.</li>
<li>List out the final value of all accounts.</li>
</ul>
<pre><code class="language-html">&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;meta name=&quot;description&quot; content=&quot;CanJS 3.0 - ATM Guide - Setup&quot;&gt;
  &lt;meta charset=&quot;utf-8&quot;&gt;
  &lt;meta name=&quot;viewport&quot; content=&quot;width=device-width&quot;&gt;
  &lt;title&gt;JS Bin&lt;/title&gt;

&lt;/head&gt;
&lt;body&gt;

&lt;script type='text/stache' id='atm-template'&gt;
&lt;div class=&quot;screen&quot;&gt;
    &lt;div class=&quot;screen-content&quot;&gt;
        &lt;div class=&quot;screen-glass&quot;&gt;

{{#switch state}}
    {{#case &quot;readingCard&quot;}}
        &lt;h2&gt;Reading Card&lt;/h2&gt;
        &lt;p&gt;Welcome to canATM where there are &lt;strong&gt;never&lt;/strong&gt;
          fees!&lt;/p&gt;
        &lt;/p&gt;
        &lt;p&gt;
            Enter Card Number:
            &lt;input name=&quot;card&quot; ($enter)=&quot;cardNumber(%element.value)&quot;/&gt;
        &lt;/p&gt;
    {{/case}}

    {{#case &quot;readingPin&quot;}}
        &lt;h2&gt;Reading Pin&lt;/h2&gt;
        &lt;p&gt;
            Enter Pin Number:
            &lt;input name=&quot;pin&quot; type=&quot;password&quot;
                autofocus
                {{#is card.state &quot;verifying&quot;}}DISABLED{{/is}}
                ($enter)=&quot;pinNumber(%element.value)&quot;/&gt;

            {{#is card.state &quot;verifying&quot;}}
                &lt;div class='warn'&gt;
                    &lt;p&gt;
                    &lt;img src=&quot;http://v3.canjs.com/docs/images/loader.gif&quot;/&gt;
                    verifying
                    &lt;/p&gt;
                &lt;/div&gt;
            {{/is}}
        &lt;/p&gt;
        &lt;a href=&quot;javascript://&quot; ($click)=&quot;exit()&quot;&gt;exit&lt;/a&gt;
    {{/case}}

    {{#case &quot;choosingTransaction&quot;}}
        &lt;h2&gt;Choose Transaction&lt;/h2&gt;
        &lt;p&gt;What would you like to do?&lt;/p&gt;
        &lt;nav&gt;
            &lt;ul&gt;
                &lt;li ($click)=&quot;chooseDeposit()&quot;&gt;Deposit&lt;/li&gt;
                &lt;li ($click)=&quot;chooseWithdraw()&quot;&gt;Withdraw&lt;/li&gt;
                &lt;li ($click)=&quot;printReceiptAndExit()&quot;&gt;Exit&lt;/li&gt;
            &lt;/ul&gt;
        &lt;/nav&gt;
    {{/case}}

    {{#case  &quot;pickingAccount&quot;}}
        &lt;h2&gt;Pick Account&lt;/h2&gt;
        &lt;p&gt;Please pick your account:&lt;/p&gt;
        {{#if accountsPromise.isPending}}
            &lt;div class='warn'&gt;
                &lt;p&gt;
                    &lt;img src=&quot;http://v3.canjs.com/docs/images/loader.gif&quot;/&gt;
                    Loading Accounts ...
                &lt;/p&gt;
            &lt;/div&gt;
        {{else}}
            &lt;ul&gt;
                {{#each accountsPromise.value}}
                    &lt;li ($click)=&quot;chooseAccount(.)&quot;&gt;{{name}} - ${{balance}}&lt;/li&gt;
                {{/each}}
            &lt;/ul&gt;
        {{/if}}
    {{/case}}

    {{#case &quot;depositInfo&quot;}}
        &lt;h2&gt;Deposit&lt;/h2&gt;
        &lt;p&gt;
            How much would you like to deposit
            into {{currentTransaction.account.name}}
            (${{currentTransaction.account.balance}})?

            &lt;input name=&quot;deposit&quot; {($value)}=&quot;currentTransaction.amount&quot;/&gt;
        &lt;/p&gt;

        {{#eq currentTransaction.state &quot;executing&quot;}}
            &lt;div class='warn'&gt;
                &lt;p&gt;
                &lt;img src=&quot;http://v3.canjs.com/docs/images/loader.gif&quot;/&gt;
                executing
                &lt;/p&gt;
            &lt;/div&gt;
        {{else}}
            &lt;button ($click)=&quot;currentTransaction.execute()&quot;
                {{^eq currentTransaction.state &quot;ready&quot;}}DISABLED{{/eq}}&gt;
                Deposit
            &lt;/button&gt;
            &lt;a href=&quot;javascript://&quot; ($click)=&quot;removeTransaction()&quot;&gt;cancel&lt;/a&gt;
        {{/eq}}
    {{/case}}

    {{#case &quot;withdrawalInfo&quot;}}
        &lt;h2&gt;Withdraw&lt;/h2&gt;
        &lt;p&gt;
            How much would you like to withdraw
            from {{currentTransaction.account.name}}
            (${{currentTransaction.account.balance}})?

            &lt;input name=&quot;withdrawl&quot; {($value)}=&quot;currentTransaction.amount&quot;/&gt;
        &lt;/p&gt;
        {{#eq currentTransaction.state &quot;executing&quot;}}
            &lt;div class='warn'&gt;
                &lt;p&gt;
                &lt;img src=&quot;http://v3.canjs.com/docs/images/loader.gif&quot;/&gt;
                executing
                &lt;/p&gt;
            &lt;/div&gt;
        {{else}}
            &lt;button ($click)=&quot;currentTransaction.execute()&quot;
                {{^eq currentTransaction.state &quot;ready&quot;}}DISABLED{{/eq}}&gt;
                Withdraw
            &lt;/button&gt;
            &lt;a href=&quot;javascript://&quot; ($click)=&quot;removeTransaction()&quot;&gt;cancel&lt;/a&gt;
        {{/eq}}
    {{/case}}

    {{#case &quot;successfulTransaction&quot;}}
        &lt;h2&gt;Transaction Successful!&lt;/h2&gt;
        &lt;p&gt;
            {{currentTransaction.account.name}} has
            ${{currentTransaction.account.balance}}.
        &lt;/p&gt;
        &lt;p&gt;What would you like to do?&lt;/p&gt;
        &lt;nav&gt;
            &lt;ul&gt;
                &lt;li ($click)=&quot;removeTransaction()&quot;&gt;Another transaction&lt;/li&gt;
                &lt;li ($click)=&quot;printReceiptAndExit()&quot;&gt;Exit&lt;/li&gt;
            &lt;/ul&gt;
        &lt;/nav&gt;
    {{/case}}

    {{#case  &quot;printingReceipt&quot;}}
        &lt;h2&gt;Printing Receipt&lt;/h2&gt;
        &lt;h3&gt;Transactions&lt;/h3&gt;
        &lt;ul&gt;
            {{#if transactions.length}}
                {{#each transactions}}
                    &lt;li&gt;{{actionName(this)}} ${{amount}} {{actionPrep(this)}} {{account.name}}&lt;/li&gt;
                {{/each}}
            {{else}}
                &lt;li&gt;None&lt;/li&gt;
            {{/if}}
        &lt;/ul&gt;
        &lt;h3&gt;Accounts&lt;/h3&gt;
        &lt;ul&gt;
            {{#each accountsPromise.value}}
                &lt;li ($click)=&quot;chooseAccount(.)&quot;&gt;{{name}} - ${{balance}}&lt;/li&gt;
            {{/each}}
        &lt;/ul&gt;
        &lt;div class='warn'&gt;
            &lt;p&gt;
                &lt;img src=&quot;http://v3.canjs.com/docs/images/loader.gif&quot;/&gt;
                printing
            &lt;/p&gt;
        &lt;/div&gt;
    {{/case}}

    {{#default}}
        &lt;h2&gt;Error&lt;/h2&gt;
        &lt;p&gt;Invalid state - {{state}}&lt;/p&gt;
    {{/default}}

{{/switch}}

        &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;
&lt;/script&gt;

&lt;script type='text/stache' id='app-template'&gt;
&lt;div class=&quot;title&quot;&gt;
    &lt;h1&gt;canATM&lt;/h1&gt;
&lt;/div&gt;
&lt;atm-machine/&gt;
&lt;/script&gt;

&lt;script src=&quot;https://code.jquery.com/jquery-2.2.4.js&quot;&gt;&lt;/script&gt;
&lt;script src=&quot;https://unpkg.com/can/dist/global/can.all.js&quot;&gt;&lt;/script&gt;

&lt;div id=&quot;qunit&quot;&gt;&lt;/div&gt;
&lt;link rel=&quot;stylesheet&quot; href=&quot;http://code.jquery.com/qunit/qunit-1.12.0.css&quot;&gt;
&lt;script src=&quot;http://code.jquery.com/qunit/qunit-1.12.0.js&quot;&gt;&lt;/script&gt;

&lt;/body&gt;
&lt;/html&gt;

</code></pre>
<p><span line-highlight='149-170,only'></span>
Update the <code>ATM</code> view model in the <code>JavaScript</code> tab to:</p>
<ul>
<li>Add a <code>printingReceipt</code> and <code>receiptTime</code> property.</li>
<li>Change the <code>state</code> to <code>&quot;printingReceipt&quot;</code> when <code>printingReceipt</code> is true.</li>
<li>Make <code>.exit</code> set <code>printingReceipt</code> to <code>null</code>.</li>
<li>Add a <code>printReceiptAndExit</code> method that:
<ul>
<li>clears the current transaction, which will add the currentTransaction to the list of transactions.</li>
<li>sets <code>printingReceipt</code> to <code>true</code> for <code>printingReceipt</code> time.</li>
</ul></li>
</ul>
<p>Update the <code>ATM basics</code> test in the <code>JavaScript</code> tab to:</p>
<ul>
<li>Shorten the default <code>receiptTime</code> so the tests move quickly.</li>
<li>Call <code>printReceiptAndExit</code> and make sure that the <code>state</code> changes to <code>&quot;printingReceipt&quot;</code> and
then to <code>&quot;readingCard&quot;</code> and ensure that sensitive information is cleared from the atm.</li>
</ul>
<pre><code class="language-js">// ========================================
// CODE
// ========================================

can.fixture({
    &quot;/verifyCard&quot;: function(request, response) {
        if (!request.data || !request.data.number || !request.data.pin) {
            response(400, {});
        } else {
            return {};
        }
    },
    &quot;/accounts&quot;: function() {
        return {
            data: [{
                balance: 100,
                id: 1,
                name: &quot;checking&quot;
            }, {
                balance: 10000,
                id: 2,
                name: &quot;savings&quot;
            }]
        };
    },
    &quot;/deposit&quot;: function() {
        return {};
    },
    &quot;/withdrawal&quot;: function() {
        return {};
    }
});
can.fixture.delay = 1000;

var Card = can.DefineMap.extend({
    number: &quot;string&quot;,
    pin: &quot;string&quot;,
    state: {
        value: &quot;unverified&quot;,
        serialize: false
    },
    verify: function() {

        this.state = &quot;verifying&quot;;

        var self = this;
        return can.ajax({
            type: &quot;POST&quot;,
            url: &quot;/verifyCard&quot;,
            data: this.serialize()
        }).then(
            function() {
                self.state = &quot;verified&quot;;
                return self;
            },
            function() {
                self.state = &quot;invalid&quot;;
                return self;
            });
    }
});

var Account = can.DefineMap.extend(&quot;Account&quot;, {
    id: &quot;number&quot;,
    balance: &quot;number&quot;,
    name: &quot;string&quot;
});
Account.List = can.DefineList.extend(&quot;AccountList&quot;, {
    &quot;*&quot;: Account
});

can.connect.baseMap({
    url: &quot;/accounts&quot;,
    Map: Account,
    List: Account.List,
    name: &quot;accounts&quot;
});

var Transaction = can.DefineMap.extend({
    account: Account,
    card: Card,
    executing: {
        type: &quot;boolean&quot;,
        value: false
    },
    executed: {
        type: &quot;boolean&quot;,
        value: false
    },
    rejected: &quot;any&quot;,
    get ready(){
        throw new Error(&quot;Transaction::ready must be provided by extended type&quot;);
    },
    get state() {
        if (this.rejected) {
            return &quot;rejected&quot;;
        }
        if (this.executed) {
            return &quot;executed&quot;;
        }
        if (this.executing) {
            return &quot;executing&quot;;
        }
        // make sure there's an amount, account, and card
        if (this.ready) {
            return &quot;ready&quot;;
        }
        return &quot;invalid&quot;;
    },
    executeStart: function(){
        throw new Error(&quot;Transaction::executeStart must be provided by extended type&quot;);
    },
    executeEnd: function(){
        throw new Error(&quot;Transaction::executeEnd must be provided by extended type&quot;);
    },
    execute: function() {
        if (this.state === &quot;ready&quot;) {

            this.executing = true;

            var def = this.executeStart(),
                self = this;

            def.then(function() {
                can.batch.start();
                self.set({
                    executing: false,
                    executed: true
                });
                self.executeEnd();
                can.batch.stop();
            }, function(reason){
                self.set({
                    executing: false,
                    executed: true,
                    rejected: reason
                });
            });
        }
    }
});

var Deposit = Transaction.extend({
    amount: &quot;number&quot;,
    get ready() {
        return this.amount &gt; 0 &amp;&amp;
            this.account &amp;&amp;
            this.card;
    },
    executeStart: function() {
        return can.ajax({
            type: &quot;POST&quot;,
            url: &quot;/deposit&quot;,
            data: {
                card: this.card.serialize(),
                accountId: this.account.id,
                amount: this.amount
            }
        });
    },
    executeEnd: function(data) {
        this.account.balance = this.account.balance + this.amount;
    }
});

var Withdrawal = Transaction.extend({
    amount: &quot;number&quot;,
    get ready() {
        return this.amount &gt; 0 &amp;&amp;
            this.account &amp;&amp;
            this.card;
    },
    executeStart: function() {
        return can.ajax({
            type: &quot;POST&quot;,
            url: &quot;/withdrawal&quot;,
            data: {
                card: this.card.serialize(),
                accountId: this.account.id,
                amount: this.amount
            }
        });
    },
    executeEnd: function(data) {
        this.account.balance = this.account.balance - this.amount;
    }
});

var ATM = can.DefineMap.extend({
    // stateful properties
    card: Card,
    accountsPromise: &quot;any&quot;,
    transactions: can.DefineList,
    currentTransaction: {
        set: function(newTransaction) {
            var currentTransaction = this.currentTransaction;
            if (this.transactions &amp;&amp; currentTransaction &amp;&amp;
                currentTransaction.state === &quot;executed&quot;) {

                this.transactions.push(currentTransaction);
            }
            return newTransaction;
        }
    },
    printingReceipt: &quot;boolean&quot;,
    receiptTime: {
        value: 5000,
        type: &quot;number&quot;
    },

    // derived properties
    get state(){
        if (this.printingReceipt) {
            return &quot;printingReceipt&quot;;
        }
        if (this.currentTransaction) {
            if (this.currentTransaction.state === &quot;executed&quot;) {
                return &quot;successfulTransaction&quot;;
            }

            if (this.currentTransaction.account) {
                if (this.currentTransaction instanceof Deposit) {
                    return &quot;depositInfo&quot;;
                } else {
                    return &quot;withdrawalInfo&quot;;
                }
            }

            return &quot;pickingAccount&quot;;
        }

        if(this.card) {
            if (this.card.state === &quot;verified&quot;) {
                return &quot;choosingTransaction&quot;;
            }
            return &quot;readingPin&quot;;
        }
        return &quot;readingCard&quot;;
    },

    // methods
    cardNumber: function(number) {
        this.card = new Card({
            number: number
        });
    },
    pinNumber: function(pin) {
        var card = this.card;

        card.pin = pin;
        this.transactions = new can.DefineList();
        this.accountsPromise = card.verify().then(function(card) {

            return Account.getList(card.serialize());
        });
    },
    exit: function(){
        this.set({
            card: null,
            accountsPromise: null,
            transactions: null,
            currentTransaction: null,
            printingReceipt: null
        });
    },
    printReceiptAndExit: function() {
        this.currentTransaction = null;
        this.printingReceipt = true;
        var self = this;
        setTimeout(function() {
            self.exit();
        }, this.receiptTime);
    },
    chooseDeposit: function() {
        this.currentTransaction = new Deposit({
            card: this.card
        });
    },
    chooseWithdraw: function() {
        this.currentTransaction = new Withdrawal({
            card: this.card
        });
    },
    chooseAccount: function(account) {
        this.currentTransaction.account = account;
    },
    removeTransaction: function() {
        this.currentTransaction = null;
    }
});

can.Component.extend({
    tag: &quot;atm-machine&quot;,
    view: can.stache.from(&quot;atm-template&quot;),
    ViewModel: ATM,
});

document.body.insertBefore(
    can.stache.from(&quot;app-template&quot;)({}),
    document.body.firstChild
);

// ========================================
// TESTS
// ========================================

QUnit.module(&quot;ATM system&quot;, {
    setup: function() {
        can.fixture.delay = 1;
    },
    teardown: function() {
        can.fixture.delay = 2000;
    }
});

QUnit.asyncTest(&quot;Valid Card&quot;, function() {

    var c = new Card({
        number: &quot;01234567890&quot;,
        pin: 1234
    });

    QUnit.equal(c.state, &quot;unverified&quot;);

    c.verify();

    QUnit.equal(c.state, &quot;verifying&quot;, &quot;card is verifying&quot;);

    c.on(&quot;state&quot;, function(ev, newVal) {

        QUnit.equal(newVal, &quot;verified&quot;, &quot;card is verified&quot;);

        QUnit.start();
    });
});

QUnit.asyncTest(&quot;Invalid Card&quot;, function() {

    var c = new Card({});

    QUnit.equal(c.state, &quot;unverified&quot;);

    c.verify();

    QUnit.equal(c.state, &quot;verifying&quot;, &quot;card is verifying&quot;);

    c.on(&quot;state&quot;, function(ev, newVal) {

        QUnit.equal(newVal, &quot;invalid&quot;, &quot;card is invalid&quot;);

        QUnit.start();
    });
});

QUnit.asyncTest(&quot;Deposit&quot;, 6, function() {

    var card = new Card({
        number: &quot;0123456789&quot;,
        pin: &quot;1122&quot;
    });

    var deposit = new Deposit({
        amount: 100,
        card: card
    });

    equal(deposit.state, &quot;invalid&quot;);

    var startingBalance;

    Account.getList(card.serialize()).then(function(accounts) {
        QUnit.ok(true, &quot;got accounts&quot;);
        deposit.account = accounts[0];
        startingBalance = accounts[0].balance;
    });

    deposit.on(&quot;state&quot;, function(ev, newVal) {
        if (newVal === &quot;ready&quot;) {

            QUnit.ok(true, &quot;deposit is ready&quot;);
            deposit.execute();

        } else if (newVal === &quot;executing&quot;) {

            QUnit.ok(true, &quot;executing a deposit&quot;);

        } else if (newVal === &quot;executed&quot;) {

            QUnit.ok(true, &quot;executed a deposit&quot;);
            equal(deposit.account.balance, 100 + startingBalance);
            start();

        }
    });
});

QUnit.asyncTest(&quot;ATM basics&quot;, function() {

    var atm = new ATM();

    equal(atm.state, &quot;readingCard&quot;, &quot;starts at reading card state&quot;);

    atm.cardNumber(&quot;01233456789&quot;);

    equal(atm.state, &quot;readingPin&quot;, &quot;moves to reading card state&quot;);

    atm.pinNumber(&quot;1234&quot;);

    ok(atm.state, &quot;readingPin&quot;, &quot;remain in the reading pin state until verifyied&quot;);

    atm.on(&quot;state&quot;, function(ev, newVal) {

        if (newVal === &quot;choosingTransaction&quot;) {

            QUnit.ok(true, &quot;in choosingTransaction&quot;);
            atm.chooseDeposit();

        } else if (newVal === &quot;pickingAccount&quot;) {

            QUnit.ok(true, &quot;in picking account state&quot;);
            atm.accountsPromise.then(function(accounts){
                atm.chooseAccount(accounts[0]);
            });

        } else if (newVal === &quot;depositInfo&quot;) {

            QUnit.ok(true, &quot;in depositInfo state&quot;);
            var currentTransaction = atm.currentTransaction;
            currentTransaction.amount = 120;
            QUnit.ok(currentTransaction.ready, &quot;we are ready to execute&quot;);
            currentTransaction.execute();
            QUnit.equal(atm.state, &quot;depositInfo&quot;, &quot;in deposit state until successful&quot;);

        } else if (newVal === &quot;successfulTransaction&quot;) {

            QUnit.ok(true, &quot;in successfulTransaction state&quot;);
            atm.receiptTime = 100;
            atm.printReceiptAndExit();

        } else if (newVal === &quot;printingReceipt&quot;) {

            QUnit.ok(true, &quot;in printingReceipt state&quot;);

        } else if (newVal === &quot;readingCard&quot;) {

            QUnit.ok(true, &quot;in readingCard state&quot;);
            QUnit.ok(!atm.card, &quot;card is removed&quot;);
            QUnit.ok(!atm.transactions, &quot;transactions removed&quot;);
            QUnit.start();

        }
    });
});

</code></pre>
<p><span line-highlight='189,205-209,213-215,263,266-273,397,437-451,only'></span>
When complete, you have a working ATM!  Cha-ching!</p>
<script src="//static.jsbin.com/js/embed.min.js?3.39.18"></script>

</section>

  


<script type="text/javascript">
  var docObject = {"src":{"path":"docs/can-guides/experiment/atm/atm.md"},"description":"\nThis guide walks through building and __testing__ an ATM application with CanJS's\n[can-core Core libraries].  It teaches how to do test driven development (TDD)\nand manage complex state.  It takes about 2 hours to complete.\n","name":"guides/atm","title":"ATM Guide","type":"page","parent":"guides/experiment","order":3,"comment":" ","pathToRoot":"../.."};
</script>
</article>
	  
        <footer><p>CanJS is part of <a href="http://donejs.com" target="_blank">DoneJS</a>. Created and maintained by the core <a href="https://donejs.com/About.html#section=section_Team" target="_blank">DoneJS team</a> and <a href="http://bitovi.com" target="_blank">Bitovi</a>. <strong>Currently 3.0.0.</strong></p></footer>
	      </div>
  </div>
</div>

		
			<script>
				steal = {
				  	instantiated: {
				    	"bundles/bit-docs-site/static.css!$css" : null
				  	}
			  	};
			</script>
			<script type='text/javascript' data-main="bit-docs-site/static" src="../static/node_modules/steal/steal.production.js"></script>
		
		<script async defer src="https://buttons.github.io/buttons.js"></script>
	</body>
</html>