PageSourceSearch

https://elm-program-test.netlify.app/ports

html elm-program-test.netlify.app collected 2026-10-03 10:41:35 UTC 21,373 bytes, 112 lines download raw bytes

1<!DOCTYPE html>
2<html lang="en-US">
3  <head>
4    <meta charset="utf-8">
5<!-- This site is hosted on Netlify. Anyone can build and deploy a site
6     like this one for free: https://netlify.new/?utm_campaign=loops&utm_source=ai-legible&utm_medium=owned&utm_content=comment&utm_id=ba90cc5e-81e2-40b5-b457-107522fd3634
7     Netlify hosting facts for this site: static/SSR served via Netlify Edge. -->
8    <meta name="viewport" content="width=device-width,initial-scale=1">
9    <title>Testing programs with ports | avh4/elm-program-test</title>
10    <meta name="generator" content="VuePress 1.9.7">
11    
12    <meta name="description" content="">
13    
14    <link rel="preload" href="/assets/css/0.styles.76626958.css" as="style"><link rel="preload" href="/assets/js/app.667806f6.js" as="script"><link rel="preload" href="/assets/js/2.1d215991.js" as="script"><link rel="preload" href="/assets/js/8.f5193335.js" as="script"><link rel="prefetch" href="/assets/js/10.7e37fa33.js"><link rel="prefetch" href="/assets/js/11.47227962.js"><link rel="prefetch" href="/assets/js/3.3b1cbf3d.js"><link rel="prefetch" href="/assets/js/4.b2dec8d5.js"><link rel="prefetch" href="/assets/js/5.2b733c3b.js"><link rel="prefetch" href="/assets/js/6.feea877e.js"><link rel="prefetch" href="/assets/js/7.96fac09b.js"><link rel="prefetch" href="/assets/js/9.277674e8.js">
15    <link rel="stylesheet" href="/assets/css/0.styles.76626958.css">
16  </head>
17  <body>
18    <div id="app" data-server-rendered="true"><div class="theme-container"><header class="navbar"><div class="sidebar-button"><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" role="img" viewBox="0 0 448 512" class="icon"><path fill="currentColor" d="M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z"></path></svg></div> <a href="/" class="home-link router-link-active"><!----> <span class="site-name">avh4/elm-program-test</span></a> <div class="links"><div class="search-box"><input aria-label="Search" autocomplete="off" spellcheck="false" value=""> <!----></div> <nav class="nav-links can-hide"><div class="nav-item"><a href="/" class="nav-link">
19  Guidebooks
20</a></div><div class="nav-item"><a href="https://package.elm-lang.org/packages/avh4/elm-program-test/latest" target="_blank" rel="noopener noreferrer" class="nav-link external">
21  API Documentation
22  <span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></div> <a href="https://github.com/avh4/elm-program-test" target="_blank" rel="noopener noreferrer" class="repo-link">
23    GitHub
24    <span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></nav></div></header> <div class="sidebar-mask"></div> <aside class="sidebar"><nav class="nav-links"><div class="nav-item"><a href="/" class="nav-link">
25  Guidebooks
26</a></div><div class="nav-item"><a href="https://package.elm-lang.org/packages/avh4/elm-program-test/latest" target="_blank" rel="noopener noreferrer" class="nav-link external">
27  API Documentation
28  <span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound">
28<path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></div> <a href="https://github.com/avh4/elm-program-test" target="_blank" rel="noopener noreferrer" class="repo-link">
29    GitHub
30    <span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></nav>  <ul class="sidebar-links"><li><section class="sidebar-group depth-0"><p class="sidebar-heading open"><span>Testing programs with ports</span> <!----></p> <ul class="sidebar-links sidebar-group-items"><li><a class='sidebar-link' href='/ports#introducing-the-example-program'>Introducing the example program</a><ul class="sidebar-sub-headers"></ul></li><li><a class='sidebar-link' href='/ports#goal-the-ideal-test'>Goal: the ideal test</a><ul class="sidebar-sub-headers"></ul></li><li><a class='sidebar-link' href='/ports#simulating-outgoing-ports'>Simulating outgoing ports</a><ul class="sidebar-sub-headers"></ul></li><li><a class='sidebar-link' href='/ports#simulating-incoming-ports'>Simulating incoming ports</a><ul class="sidebar-sub-headers"></ul></li><li><a class='sidebar-link' href='/ports#try-it-out'>Try it out</a><ul class="sidebar-sub-headers"></ul></li></ul></section></li></ul> </aside> <main class="page"> <div class="theme-default-content content__default"><h1 id="testing-programs-with-ports"><a href="#testing-programs-with-ports" class="header-anchor">#</a> Testing programs with ports</h1> <p>This guide <strong>requires that you're already familiar with
31<a href='/cmds'>testing programs that use Cmds</a></strong>.
32Specifically, you should understand how to set up your program for testing
33by creating a custom Effect type and implementing a <code>simulateEffects</code> function.
34If you aren't yet familiar with doing that, you should follow the
35<a href='/cmds'>&quot;testing programs that use Cmds&quot; guide</a>
35 first and return to this guide afterwards.</p> <h2 id="introducing-the-example-program"><a href="#introducing-the-example-program" class="header-anchor">#</a> Introducing the example program</h2> <p>The rest of this section will be working with an example program
36that interacts with the fictional &quot;GudWerdz&quot; service for
37checking the grammar and writing style of a text input.</p> <p>The fictional &quot;GudWerdz&quot; service only provides a JavaScript SDK,
38so our app needs to interact with it using <a href="https://guide.elm-lang.org/interop/ports.html" target="_blank" rel="noopener noreferrer">ports<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a>.</p> <p><img src="/assets/img/GrammarCheckExample.35f1773e.svg" alt="architecture diagram described in the preceding paragraph"></p> <h2 id="goal-the-ideal-test"><a href="#goal-the-ideal-test" class="header-anchor">#</a> Goal: the ideal test</h2> <p>The test we want to write represents the following scenario:</p> <ol><li>The user fills in the text area with some text.</li> <li>The user clicks the &quot;Check&quot; button.</li> <li>The app sends the text to check out through the <code>checkGrammar</code> port, which will call the GudWerdz API.</li> <li>The GudWerdz API returns a list of problems with the text, which get sent to the <code>checkGrammarResults</code> port.</li> <li>The app receives the results and displays them to the user.</li></ol> <p>Here's what that test will look like in code:</p> <div class="language-elm extra-class"><div class="highlight-lines"><br><br><br><br><br><br><br><br><br><br><br><br><br><div class="highlighted"> </div><div class="highlighted"> </div><div class="highlighted"> </div><div class="highlighted"> </div><div class="highlighted"> </div><div class="highlighted"> </div><div class="highlighted"> </div><div class="highlighted"> </div><div class="highlighted"> </div><br><br><br></div><pre class="language-elm"><code><span class="token import-statement"><span class="token keyword">import</span> Expect</span>
39<span class="token import-statement"><span class="token keyword">import</span> Json.Decode</span>
40<span class="token import-statement"><span class="token keyword">import</span> Json.Encode</span>
41<span class="token import-statement"><span class="token keyword">import</span> ProgramTest</span>
42<span class="token import-statement"><span class="token keyword">import</span> Test <span class="token keyword">exposing</span> </span><span class="token punctuation">(</span><span class="token hvariable">test</span><span class="token punctuation">)</span>
43
44<span class="token hvariable">test</span> <span class="token string">&quot;checking grammar&quot;</span> <span class="token operator">&lt;|</span>
45    \<span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">-&gt;</span>
46        <span class="token hvariable">start</span>
47            <span class="token operator">|&gt;</span> <span class="token hvariable">ProgramTest.fillIn</span> <span class="token string">&quot;main&quot;</span>
48                <span class="token string">&quot;Enter text to check&quot;</span>
49                <span class="token string">&quot;The youngest man the boat.&quot;</span>
50            <span class="token operator">|&gt;</span> <span class="token hvariable">ProgramTest.clickButton</span> <span class="token string">&quot;Check&quot;</span>
51            <span class="token operator">|&gt;</span> <span class="token hvariable">ProgramTest.ensureOutgoingPortValues</span>
52                <span class="token string">&quot;checkGrammar&quot;</span>
53                <span class="token hvariable">Json.Decode.string</span>
54                <span class="token punctuation">(</span><span class="token hvariable">Expect.equal</span> <span class="token punctuation">[</span> <span class="token string">&quot;The youngest man the boat.&quot;</span> <span class="token punctuation">]</span><span class="token punctuation">)</span>
55            <span class="token operator">|&gt;</span> <span class="token hvariable">ProgramTest.simulateIncomingPort</span>
56                <span class="token string">&quot;grammarCheckResults&quot;</span>
57                <span class="token punctuation">(</span><span class="token hvariable">Json.Encode.list</span> <span class="token hvariable">Json.Encode.string</span>
58                    <span class="token punctuation">[</span> <span class="token string">&quot;Garden-path sentences can confuse the reader.&quot;</span> <span class="token punctuation">]</span>
59                <span class="token punctuation">)</span>
60            <span class="token operator">|&gt;</span> <span class="token hvariable">ProgramTest.expectViewHas</span>
61                <span class="token punctuation">[</span> <span class="token hvariable">text</span> <span class="token string">&quot;Garden-path sentences can confuse the reader.&quot;</span> <span class="token punctuation">]</span>
62</code></pre></div><p>After entering some text and clicking the &quot;Check&quot; button,
63the test uses <code>ensureOutgoingPortValues</code> to assert that the entered text has been sent out on the &quot;checkGrammar&quot; port.
64Then <code>simulateIncomingPort</code> is used to simulate a response coming in on the &quot;grammarCheckResults&quot; port.</p> <p>However, before this test can pass we'll have to do some setup work
65so that the test can simulate the incoming and outgoing ports.</p> <h2 id="simulating-outgoing-ports"><a href="#simulating-outgoing-ports" class="header-anchor">#</a> Simulating outgoing ports</h2> <p>We'll assume that we already have our test set up with <code>ProgramTest.withSimulatedEffects</code>
66(if you're not familiar with that,
67you should first follow the <a href='/cmds'>&quot;testing programs that use Cmds&quot; guide</a>).</p> <p>To add simulation of our port effect, we'll make use of <code>SimulatedEffect.Ports.send</code>
68in our <code>simulateEffects</code> function:</p> <div class="language-elm extra-class"><pre class="language-elm"><code><span class="token import-statement"><span class="token keyword">import</span> Json.Encode</span>
69<span class="token import-statement"><span class="token keyword">import</span> SimulatedEffect.Ports</span>
70
71<span class="token hvariable">simulateEffects</span> <span class="token operator">:</span> <span class="token constant">Main.Effect</span> <span class="token operator">-&gt;</span> <span class="token constant">ProgramTest.SimulatedEffect</span> <span class="token constant">Main.Msg</span>
72<span class="token hvariable">simulateEffects</span> <span class="token hvariable">effect</span> <span class="token operator">=</span>
73    <span class="token keyword">case</span> <span class="token hvariable">effect</span> <span class="token keyword">of</span>
74        <span class="token operator">...</span>
75        <span class="token constant">Main.CheckGrammarEffect</span> <span class="token hvariable">text</span> <span class="token operator">-&gt;</span>
76            <span class="token hvariable">SimulatedEffect.Ports.send</span> <span class="token string">&quot;checkGrammar&quot;</span> <span class="token punctuation">(</span><span class="token hvariable">Json.Encode.string</span> <span class="token hvariable">text</span><span class="token punctuation">)</span>
77</code></pre></div><h2 id="simulating-incoming-ports"><a href="#simulating-incoming-ports" class="header-anchor">#</a> Simulating incoming ports</h2> <p>Similar to how we provided a <code>simulateEffects</code> function for testing our Cmds,
78we'll need to provide a <code>simulateSubscriptions</code> function for testing our Subs.
79This will parallel the <code>subscriptions</code> function in our main program,
80but will return <code>ProgramTest.SimulatedSub</code> instead of the normal <code>Sub</code>.
81(This is necessary because, as with <code>Cmd</code>s,
82Elm currently provides no way to inspect the value of a <code>Sub</code>.)</p> <p>For reference, here's our program's <code>subscriptions</code> function:</p> <div class="language-elm extra-class"><pre class="language-elm"><code><span class="token hvariable">port</span> <span class="token hvariable">grammarCheckResults</span> <span class="token operator">:</span> <span class="token punctuation">(</span><span class="token constant">List</span> <span class="token constant">String</span> <span class="token operator">-&gt;</span> <span class="token hvariable">msg</span><span class="token punctuation">)</span> <span class="token operator">-&gt;</span> <span class="token constant">Sub</span> <span class="token hvariable">msg</span>
83
84<span class="token hvariable">subscriptions</span> <span class="token operator">:</span> <span class="token constant">Model</span> <span class="token operator">-&gt;</span> <span class="token constant">Sub</span> <span class="token constant">Msg</span>
85<span class="token hvariable">subscriptions</span> _ <span class="token operator">=</span>
86    <span class="token hvariable">grammarCheckResults</span> <span class="token constant">GrammarCheckResults</span>
87</code></pre></div><p>And here's the corresponding <code>simulateSubscriptions</code> function we'll write for our test setup:</p> <div class="language-elm extra-class"><pre class="language-elm"><code><span class="token hvariable">simulateSubscriptions</span> <span class="token operator">:</span> <span class="token constant">
87Main.Model</span> <span class="token operator">-&gt;</span> <span class="token constant">ProgramTest.SimulatedSub</span> <span class="token constant">Main.Msg</span>
88<span class="token hvariable">simulateSubscriptions</span> _ <span class="token operator">=</span>
89    <span class="token hvariable">SimulatedEffect.Ports.subscribe</span> <span class="token string">&quot;grammarCheckResults&quot;</span>
90        <span class="token punctuation">(</span><span class="token hvariable">Json.Decode.list</span> <span class="token hvariable">Json.Decode.string</span><span class="token punctuation">)</span>
91        <span class="token constant">Main.GrammarCheckResults</span>
92</code></pre></div><p>Finally, we configure our <code>ProgramTest</code> with the <code>simulateSubscriptions</code> function before starting it:</p> <div class="language-elm extra-class"><div class="highlight-lines"><br><br><br><br><br><br><br><br><div class="highlighted"> </div><br><br></div><pre class="language-elm"><code><span class="token hvariable">start</span> <span class="token operator">:</span> <span class="token constant">ProgramTest</span> <span class="token constant">Main.Msg</span> <span class="token constant">Main.Model</span> <span class="token constant">Main.Effect</span>
93<span class="token hvariable">start</span> <span class="token operator">=</span>
94    <span class="token hvariable">ProgramTest.createDocument</span>
95        <span class="token punctuation">{</span> <span class="token hvariable">init</span> <span class="token operator">=</span> <span class="token hvariable">Main.init</span>
96        <span class="token punctuation">,</span> <span class="token hvariable">update</span> <span class="token operator">=</span> <span class="token hvariable">Main.update</span>
97        <span class="token punctuation">,</span> <span class="token hvariable">view</span> <span class="token operator">=</span> <span class="token hvariable">Main.view</span>
98        <span class="token punctuation">}</span>
99        <span class="token operator">|&gt;</span> <span class="token hvariable">ProgramTest.withSimulatedEffects</span> <span class="token hvariable">simulateEffects</span>
100        <span class="token operator">|&gt;</span> <span class="token hvariable">ProgramTest.withSimulatedSubscriptions</span> <span class="token hvariable">simulateSubscriptions</span>
101        <span class="token operator">|&gt;</span> <span class="token hvariable">ProgramTest.start</span> <span class="token punctuation">(</span><span class="token punctuation">)</span>
102</code></pre></div><h2 id="try-it-out"><a href="#try-it-out" class="header-anchor">#</a> Try it out</h2> <p>You can play around with the example here:
103<a href="https://ellie-app.com/6wskFByvrPta1" target="_blank" rel="noopener noreferrer">https://ellie-app.com/6wskFByvrPta1<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a>
104Try editing the tests or the implementation to make the tests fail
105and see what kind of failure messages you get.</p></div> <footer class="page-edit"><div class="edit-link"><a href="https://github.com/avh4/elm-program-test/edit/main/docs/ports.md" target="_blank" rel="noopener noreferrer">Suggest changes to this page</a> <span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></div> <!----></footer> <div class="page-nav"><p class="inner"><span class="prev">
106      ←
107      <a class='prev' href='/cmds'>
108        Testing programs with Cmds
109      </a></span> <!----></p></div> </main></div><div class="global-ui"></div></div>
110    
110<script src="/assets/js/app.667806f6.js" defer></script>
110<script src="/assets/js/2.1d215991.js" defer></script>
110<script src="/assets/js/8.f5193335.js" defer></script>
110
111  </body>
112</html>

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.