1(window.webpackJsonp=window.webpackJsonp||[]).push([[8],{399:function(t,s,a){t.exports=a.p+"assets/img/GrammarCheckExample.35f1773e.svg"},410:function(t,s,a){"use strict";a.r(s);var e=a(56),r=Object(e.a)({},(function(){var t=this,s=t.$createElement,e=t._self._c||s;return e("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[e("h1",{attrs:{id:"testing-programs-with-ports"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#testing-programs-with-ports"}},[t._v("#")]),t._v(" Testing programs with ports")]),t._v(" "),e("p",[t._v("This guide "),e("strong",[t._v("requires that you're already familiar with\n"),e("RouterLink",{attrs:{to:"/cmds.html"}},[t._v("testing programs that use Cmds")])],1),t._v(".\nSpecifically, you should understand how to set up your program for testing\nby creating a custom Effect type and implementing a "),e("code",[t._v("simulateEffects")]),t._v(" function.\nIf you aren't yet familiar with doing that, you should follow the\n"),e("RouterLink",{attrs:{to:"/cmds.html"}},[t._v('"testing programs that use Cmds" guide')]),t._v(" first and return to this guide afterwards.")],1),t._v(" "),e("h2",{attrs:{id:"introducing-the-example-program"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#introducing-the-example-program"}},[t._v("#")]),t._v(" Introducing the example program")]),t._v(" "),e("p",[t._v('The rest of this section will be working with an example program\nthat interacts with the fictional "GudWerdz" service for\nchecking the grammar and writing style of a text input.')]),t._v(" "),e("p",[t._v('The fictional "GudWerdz" service only provides a JavaScript SDK,\nso our app needs to interact with it using '),e("a",{attrs:{href:"https://guide.elm-lang.org/interop/ports.html",target:"_blank",rel:"noopener noreferrer"}},[t._v("ports"),e("OutboundLink")],1),t._v(".")]),t._v(" "),e("p",[e("img",{attrs:{src:a(399),alt:"architecture diagram described in the preceding paragraph"}})]),t._v(" "),e("h2",{attrs:{id:"goal-the-ideal-test"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#goal-the-ideal-test"}},[t._v("#")]),t._v(" Goal: the ideal test")]),t._v(" "),e("p",[t._v("The test we want to write represents the following scenario:")]),t._v(" "),e("ol",[e("li",[t._v("The user fills in the text area with some text.")]),t._v(" "),e("li",[t._v('The user clicks the "Check" button.')]),t._v(" "),e("li",[t._v("The app sends the text to check out through the "),e("code",[t._v("checkGrammar")]),t._v(" port, which will call the GudWerdz API.")]),t._v(" "),e("li",[t._v("The GudWerdz API returns a list of problems with the text, which get sent to the "),e("code",[t._v("checkGrammarResults")]),t._v(" port.")]),t._v(" "),e("li",[t._v("The app receives the results and displays them to the user.")])]),t._v(" "),e("p",[t._v("Here's what that test will look like in code:")]),t._v(" "),e("div",{staticClass:"language-elm extra-class"},[e("div",{staticClass:"highlight-lines"},[e("br"),e("br"),e("br"),e("br"),e("br"),e("br"),e("br"),e("br"),e("br"),e("br"),e("br"),e("br"),e("br"),e("div",{staticClass:"highlighted"},[t._v("Â ")]),e("div",{staticClass:"highlighted"},[t._v("Â ")]),e("div",{staticClass:"highlighted"},[t._v("Â ")]),e("div",{staticClass:"highlighted"},[t._v("Â ")]),e("div",{staticClass:"highlighted"},[t._v("Â ")]),e("div",{staticClass:"highlighted"},[t._v("Â ")]),e("div",{staticClass:"highlighted"},[t._v("Â ")]),e("div",{staticClass:"highlighted"},[t._v("Â ")]),e("div",{staticClass:"highlighted"},[t._v("Â ")]),e("br"),e("br"),e("br")]),e("pre",{pre:!0,attrs:{class:"language-elm"}},[e("code",[e("span",{pre:!0,attrs:{class:"token import-statement"}},[e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" Expect")]),t._v("\n"),e("span",{pre:!0,attrs:{class:"token import-statement"}},[e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" Json.Decode")]),t._v("\n"),e("span",{pre:!0,attrs:{class:"token import-statement"}},[e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" Json.Encode")]),t._v("\n"),e("span",{pre:!0,attrs:{class:"token import-statement"}},[e("span",{pre:!0,attrs:{class:"token keyword"}}
1,[t._v("import")]),t._v(" ProgramTest")]),t._v("\n"),e("span",{pre:!0,attrs:{class:"token import-statement"}},[e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" Test "),e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("exposing")]),t._v(" ")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),e("span",{pre:!0,attrs:{class:"token hvariable"}},[t._v("test")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n"),e("span",{pre:!0,attrs:{class:"token hvariable"}},[t._v("test")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token string"}},[t._v('"checking grammar"')]),t._v(" "),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<|")]),t._v("\n \\"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("->")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token hvariable"}},[t._v("start")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|>
1")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token hvariable"}},[t._v("ProgramTest.fillIn")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token string"}},[t._v('"main"')]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token string"}},[t._v('"Enter text to check"')]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token string"}},[t._v('"The youngest man the boat."')]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|>")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token hvariable"}},[t._v("ProgramTest.clickButton")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token string"}},[t._v('"Check"')]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|>")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token hvariable"}},[t._v("ProgramTest.ensureOutgoingPortValues")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token string"}},[t._v('"checkGrammar"')]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token hvariable"}},[t._v("Json.Decode.string")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),e("span",{pre:!0,attrs:{class:"token hvariable"}},[t._v("Expect.equal")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token string"}},[t._v('"The youngest man the boat."')]),t._v(" "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|>
1")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token hvariable"}},[t._v("ProgramTest.simulateIncomingPort")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token string"}},[t._v('"grammarCheckResults"')]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),e("span",{pre:!0,attrs:{class:"token hvariable"}},[t._v("Json.Encode.list")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token hvariable"}},[t._v("Json.Encode.string")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token string"}},[t._v('"Garden-path sentences can confuse the reader."')]),t._v(" "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|>")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token hvariable"}},[t._v("ProgramTest.expectViewHas")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token hvariable"}},[t._v("text")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token string"}},[t._v('"Garden-path sentences can confuse the reader."')]),t._v(" "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v("\n")])])]),e("p",[t._v('After entering some text and clicking the "Check" button,\nthe test uses '),e("code",[t._v("ensureOutgoingPortValues")]),t._v(' to assert that the entered text has been sent out on the "checkGrammar" port.\nThen '),e("code",[t._v("simulateIncomingPort")]),t._v(' is used to simulate a response coming in on the "grammarCheckResults" port.')]),t._v(" "),e("p",[t._v("However, before this test can pass we'll have to do some setup work\nso that the test can simulate the incoming and outgoing ports.")]),t._v(" "),e("h2",{attrs:{id:"simulating-outgoing-ports"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#simulating-outgoing-ports"}},[t._v("#")]),t._v(" Simulating outgoing ports")]),t._v(" "),e("p",[t._v("We'll assume that we already have our test set up with "),e("code",[t._v("ProgramTest.withSimulatedEffects")]),t._v("\n(if you're not familiar with that,\nyou should first follow the "),e("RouterLink",{attrs:{to:"/cmds.html"}},[t._v('"testing programs that use Cmds" guide')]),t._v(").")],1),t._v(" "),e("p",[t._v("To add simulation of our port effect, we'll make use of "),e("code",[t._v("SimulatedEffect.Ports.send")]),t._v("\nin our "),e("code",[t._v("simulateEffects")]),t._v(" function:")]),t._v(" "),e("div",{staticClass:"language-elm extra-class"},[e("pre",{pre:!0,attrs:{class:"language-elm"}},[e("code",[e("span",{pre:!0,attrs:{class:"token import-statement"}},[e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" Json.Encode")]),t._v("\n"),e("span",{pre:!0,attrs:{class:"token import-statement"}},[e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" SimulatedEffect.Ports")]),t._v("\n\n"),e("span",{pre:!0,attrs:{class:"token hvariable"}},[t._v("simulateEffects")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token constant"}},[t._v("Main.Effect")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("->")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token constant"}},[t._v("ProgramTest.SimulatedEffect")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token constant"}},[t._v("Main.Msg")]),t._v("\n"),e("span",{pre:!0,attrs:{class:"token hvariable"}},[t._v("simulateEffects")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token hvariable"}},[t._v("effect")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("case")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token hvariable"}},[t._v("effect")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("of")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("...")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token constant"}},[t._v("Main.CheckGrammarEffect")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token hvariable"}},[t._v("text")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("->")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token hvariable"}},[t._v("SimulatedEffect.Ports.send")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token string"}},[t._v('"checkGrammar"')]),t._v(" "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),e("span",{pre:!0,attrs:{class:"token hvariable"}},[t._v("Json.Encode.string")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token hvariable"}},[t._v("text")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n")])])]),e("h2",{attrs:{id:"simulating-incoming-ports"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#simulating-incoming-ports"}},[t._v("#")]),t._v(" Simulating incoming ports")]),t._v(" "),e("p",[t._v("Similar to how we provided a "),e("code",[t._v("simulateEffe
1cts")]),t._v(" function for testing our Cmds,\nwe'll need to provide a "),e("code",[t._v("simulateSubscriptions")]),t._v(" function for testing our Subs.\nThis will parallel the "),e("code",[t._v("subscriptions")]),t._v(" function in our main program,\nbut will return "),e("code",[t._v("ProgramTest.SimulatedSub")]),t._v(" instead of the normal "),e("code",[t._v("Sub")]),t._v(".\n(This is necessary because, as with "),e("code",[t._v("Cmd")]),t._v("s,\nElm currently provides no way to inspect the value of a "),e("code",[t._v("Sub")]),t._v(".)")]),t._v(" "),e("p",[t._v("For reference, here's our program's "),e("code",[t._v("subscriptions")]),t._v(" function:")]),t._v(" "),e("div",{staticClass:"language-elm extra-class"},[e("pre",{pre:!0,attrs:{class:"language-elm"}},[e("code",[e("span",{pre:!0,attrs:{class:"token hvariable"}},[t._v("port")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token hvariable"}},[t._v("grammarCheckResults")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),e("span",{pre:!0,attrs:{class:"token constant"}},[t._v("List")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token constant"}},[t._v("String")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("->")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token hvariable"}},[t._v("msg")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("->")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token constant"}},[t._v("Sub")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token hvariable"}},[t._v("msg")]),t._v("\n\n"),e("span",{pre:!0,attrs:{class:"token hvariable"}},[t._v("subscriptions")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token constant"}},[t._v("Model")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("->")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token constant"}},[t._v("Sub")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token constant"}},[t._v("Msg")]),t._v("\n"),e("span",{pre:!0,attrs:{class:"token hvariable"}},[t._v("subscriptions")]),t._v(" _ "),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token hvariable"}},[t._v("grammarCheckResults")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token constant"}},[t._v("GrammarCheckResults")]),t._v("\n")])])]),e("p",[t._v("And here's the corresponding "),e("code",[t._v("simulateSubscriptions")]),t._v(" function we'll write for our test setup:")]),t._v(" "),e("div",{staticClass:"language-elm extra-class"},[e("pre",{pre:!0,attrs:{class:"language-elm"}},[e("code",[e("span",{pre:!0,attrs:{class:"token hvariable"}},[t._v("simulateSubscriptions")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token constant"}},[t._v("Main.Model")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("->")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token constant"}},[t._v("ProgramTest.SimulatedSub")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token constant"}},[t._v("Main.Msg")]),t._v("\n"),e("span",{pre:!0,attrs:{class:"token hvariable"}},[t._v("simulateSubscriptions")]),t._v(" _ "),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token hvariable"}},[t._v("SimulatedEffect.Ports.subscribe")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token string"}},[t._v('"grammarCheckResults"')]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),e("span",{pre:!0,attrs:{class:"token hvariable"}},[t._v("Json.Decode.list")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token hvariable"}},[t._v("Json.Decode.string")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token constant"}},[t._v("Main.GrammarCheckResults")]),t._v("\n")])])]),e("p",[t._v("Finally, we configure our "),e("code",[t._v("ProgramTest")
1]),t._v(" with the "),e("code",[t._v("simulateSubscriptions")]),t._v(" function before starting it:")]),t._v(" "),e("div",{staticClass:"language-elm extra-class"},[e("div",{staticClass:"highlight-lines"},[e("br"),e("br"),e("br"),e("br"),e("br"),e("br"),e("br"),e("br"),e("div",{staticClass:"highlighted"},[t._v("Â ")]),e("br"),e("br")]),e("pre",{pre:!0,attrs:{class:"language-elm"}},[e("code",[e("span",{pre:!0,attrs:{class:"token hvariable"}},[t._v("start")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token constant"}},[t._v("ProgramTest")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token constant"}},[t._v("Main.Msg")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token constant"}},[t._v("Main.Model")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token constant"}},[t._v("Main.Effect")]),t._v("\n"),e("span",{pre:!0,attrs:{class:"token hvariable"}},[t._v("start")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token hvariable"}},[t._v("ProgramTest.createDocument")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token hvariable"}},[t._v("init")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token hvariable"}},[t._v("Main.init")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token hvariable"}},[t._v("update")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token hvariable"}},[t._v("Main.update")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token hvariable"}},[t._v("view")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token hvariable"}},[t._v("Main.view")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|>
1")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token hvariable"}},[t._v("ProgramTest.withSimulatedEffects")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token hvariable"}},[t._v("simulateEffects")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|>")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token hvariable"}},[t._v("ProgramTest.withSimulatedSubscriptions")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token hvariable"}},[t._v("simulateSubscriptions")]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|>")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token hvariable"}},[t._v("ProgramTest.start")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n")])])]),e("h2",{attrs:{id:"try-it-out"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#try-it-out"}},[t._v("#")]),t._v(" Try it out")]),t._v(" "),e("p",[t._v("You can play around with the example here:\n"),e("a",{attrs:{href:"https://ellie-app.com/6wskFByvrPta1",target:"_blank",rel:"noopener noreferrer"}},[t._v("https://ellie-app.com/6wskFByvrPta1"),e("OutboundLink")],1),t._v("\nTry editing the tests or the implementation to make the tests fail\nand see what kind of failure messages you get.")])])}),[],!1,null,null,null);s.default=r.exports}}]);
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.