<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Dylan Lacey</title>
    <description>The latest articles on DEV Community by Dylan Lacey (@dylanlacey).</description>
    <link>https://dev.to/dylanlacey</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F72214%2F7b1697d5-4ce4-4a21-87d6-d5bfdb73d1aa.jpeg</url>
      <title>DEV Community: Dylan Lacey</title>
      <link>https://dev.to/dylanlacey</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/dylanlacey"/>
    <language>en</language>
    <item>
      <title>You're It! Ersatz tagging in Cypress, using Saucectl.</title>
      <dc:creator>Dylan Lacey</dc:creator>
      <pubDate>Wed, 01 Jun 2022 08:01:11 +0000</pubDate>
      <link>https://dev.to/dylanlacey/youre-it-ersatz-tagging-in-cypress-using-saucectl-418</link>
      <guid>https://dev.to/dylanlacey/youre-it-ersatz-tagging-in-cypress-using-saucectl-418</guid>
      <description>&lt;h2&gt;
  
  
  How good is tagging?
&lt;/h2&gt;

&lt;p&gt;Rhetorical question; Tagging is &lt;em&gt;great&lt;/em&gt;; my favourite organisation system, in fact.  Test systems with tagging allow you the flexibility to organize your tests according to &lt;em&gt;what code&lt;/em&gt; they're testing, while running tests according to &lt;em&gt;what kind of test&lt;/em&gt; you're after.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tagging &amp;amp; Cypress
&lt;/h2&gt;

&lt;p&gt;Unfortunately, Cypress lacks native support for tagging, and this makes me very sad face emoji.&lt;/p&gt;

&lt;p&gt;Fortunately, with &lt;a href="https://github.com/saucelabs/saucectl"&gt;&lt;code&gt;saucectl&lt;/code&gt;&lt;/a&gt;, we can retrofit tagging functionality onto our Cypress tests, whether you're running locally in Docker mode, or remotely against Sauce Labs' cloud.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wait, what's saucectl?
&lt;/h2&gt;

&lt;p&gt;Let's go to the README!&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The saucectl command line interface orchestrates the tests in your framework, providing rich parallelization, test history filtering, and analytics in Sauce Labs.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Basically, saucectl is an amplifier for your tests.  It drags your Cypress, Playwright, TestCafe or Puppeteer kicking and screaming onto the Sauce Labs cloud, giving you delicious Sauce features (like analytics) along with all the goodies your framework has to offer.  Saucectl can &lt;em&gt;also&lt;/em&gt; run your tests with Docker, giving you a quick and reliable test environment.  It's a great solution for using Cypress with CI.&lt;/p&gt;

&lt;h1&gt;
  
  
  Our Approach
&lt;/h1&gt;

&lt;p&gt;In &lt;code&gt;config.yml&lt;/code&gt; (the config file for &lt;code&gt;saucectl&lt;/code&gt;), you can specify individual suites &lt;a href="https://docs.saucelabs.com/web-apps/automated-testing/cypress/yaml/#suites"&gt;see here&lt;/a&gt; which let you provide individual config options to Cypress.&lt;/p&gt;

&lt;p&gt;By passing the &lt;code&gt;testFiles&lt;/code&gt; option to Cypress, you can control which individual files it runs.  We're going to combine these two, to give us individually named test suites, each of which runs tests with a specific set of tags.&lt;/p&gt;

&lt;h2&gt;
  
  
  Can't you do that with Cypress alone?
&lt;/h2&gt;

&lt;p&gt;Yes, &lt;em&gt;technically&lt;/em&gt;, but I prefer this approach for... let's call them ideological reasons.&lt;/p&gt;

&lt;p&gt;While you &lt;em&gt;could&lt;/em&gt; have individual config files, and call &lt;code&gt;cypress run --config-file somesuite.json&lt;/code&gt; for each one, it's messy. If you make changes to one suite file, you have to make it across all of them, which is easy to forget.&lt;/p&gt;

&lt;p&gt;With this solution, all your Cypress config stays as is, and only the tests being executed are changed.  Even better, control of which tests run is maintained in the &lt;em&gt;infrastructure&lt;/em&gt; configuration, and infrastructure is likely to have the most significant impact over what 'flavours' of test you want to run.&lt;/p&gt;

&lt;h2&gt;
  
  
  How we're implementing tagging
&lt;/h2&gt;

&lt;p&gt;This solution makes use of &lt;em&gt;globbing&lt;/em&gt;, a way of selecting a group of files. It's pretty common; Common enough to have a &lt;a href="https://en.wikipedia.org/wiki/Glob_(programming)"&gt;Wikipedia&lt;/a&gt; page, and you've likely seen at least one glob before.&lt;/p&gt;

&lt;p&gt;We're going to alter our tests so each test suite is configured to run a set of globs corresponding to our tagged tests.&lt;/p&gt;

&lt;h2&gt;
  
  
  Example
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Suite Files
&lt;/h3&gt;

&lt;p&gt;Say this is our test suite.  Each file contains tests that should only run on a specific browser (or combination of browsers).  We've named them so each filename includes the relevant tags, separated by underscores, like &lt;code&gt;_chromeOnly&lt;/code&gt; or &lt;code&gt;_ieOnly&lt;/code&gt;.  These could just as easily be &lt;code&gt;_ui&lt;/code&gt; or &lt;code&gt;_FrenchLanguage&lt;/code&gt; or anything you want to tag by.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;specs/
  ui/
    login_chromeOnly_spec.js
    login_firefoxOnly_spec.js
  accounting/
    jsMathBugFix_ieOnly_spec.js
    chromiumCantCount_edgeOnly_chromeOnly_spec.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Config.yml
&lt;/h3&gt;

&lt;p&gt;Here's the suites component of our &lt;code&gt;config.yml&lt;/code&gt;. Each suite has a unique glob for testFiles which finds all tests in your specs folder which include the respective tag/s.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  suites:
    - name: "Where There's Smoke There's"
      browser: "firefox"
      platformName: "Windows 10"
      config:
        testFiles: ["specs/**/*firefoxOnly*_spec.js"]

    - name: "Cream on"
      browser: "chrome"
      platformName: "Windows 10"
      config:
        testFiles: ["specs/**/*chromeOnly*_spec.js"]

    - name: "Livin' on the"
      browser: "edge"
      platformName: "Windows 10"
      config:
        testFiles: ["specs/**/*edgeOnly*_spec.js", "specs/**/*ieOnly*_spec.js"]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  How it works
&lt;/h3&gt;

&lt;p&gt;Each glob starts with &lt;code&gt;specs/**/*&lt;/code&gt;.  This glob means "&lt;em&gt;start in the specs folder (&lt;code&gt;specs/&lt;/code&gt;), then look in any subfolder(&lt;code&gt;**&lt;/code&gt;), for any characters at all(&lt;code&gt;*&lt;/code&gt;) followed by an underscore(`&lt;/em&gt;`)".  &lt;/p&gt;

&lt;p&gt;Let's look at how the first glob continues.  After &lt;code&gt;specs/**/*&lt;/code&gt; we have &lt;code&gt;firefoxOnly*_spec.js&lt;/code&gt;.  This means "&lt;em&gt;find anything containing the phrase &lt;code&gt;firefoxOnly&lt;/code&gt;, followed by zero or more of any character(*), followed by `_spec.js&lt;/em&gt;&lt;code&gt;".  For our tests, that only matches &lt;/code&gt;specs/ui/login_firefoxOnly_spec.js`.&lt;/p&gt;

&lt;p&gt;The second glob is &lt;code&gt;specs/**/*chromeOnly*_spec.js&lt;/code&gt;, so it matches every file in every subfolder of &lt;code&gt;specs&lt;/code&gt; which contains &lt;code&gt;chromeOnly&lt;/code&gt;, followed by zero or more of any character, followed by &lt;code&gt;_spec.js&lt;/code&gt;.  In our case, that's &lt;code&gt;specs/ui/login_chromeOnly_spec.js&lt;/code&gt; AND &lt;code&gt;specs/accounting/chromiumCantCount_edgeOnly_chromeOnly_spec.js&lt;/code&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Did you notice?
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;testFiles&lt;/code&gt; parameter doesn't take a glob directly; it takes an &lt;em&gt;array&lt;/em&gt; of globs.  That lets us pass in multiple entries, which is how suite three works. We're able to pass in two entries, one for any test tagged &lt;code&gt;edgeOnly&lt;/code&gt; and one for any tagged &lt;code&gt;ieOnly&lt;/code&gt;.  It will match any file in &lt;em&gt;either&lt;/em&gt; of those blobs; in this case &lt;code&gt;specs/accounting/jsMathBugFix_ieOnly_spec.js&lt;/code&gt; and &lt;code&gt;chromiumCantCount_edgeOnly_chromeOnly_spec.js&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Additionally, test files can have &lt;em&gt;multiple tags&lt;/em&gt;.  This is really useful when you want to run some tests in multiple scenarios; Say you have a limited set of functional tests that are also used to ensure deploys go well; You could tag them &lt;code&gt;_functional_postDeploy_spec.js&lt;/code&gt; and they'd run when using &lt;em&gt;either&lt;/em&gt; the &lt;code&gt;_functional&lt;/code&gt; tag or the &lt;code&gt;_postDeploy&lt;/code&gt; one.&lt;/p&gt;

&lt;h1&gt;
  
  
  And that's it!
&lt;/h1&gt;

&lt;p&gt;A small change to how you name and select tests, and you're left with a more flexible testing system.  And of course, you can still run a default suite with &lt;em&gt;no&lt;/em&gt; tags, and make sure you're running everything.&lt;/p&gt;

&lt;p&gt;Happy Testing!&lt;/p&gt;

&lt;h4&gt;
  
  
  Credits
&lt;/h4&gt;

&lt;p&gt;Photo by &lt;a href="https://unsplash.com/@angelekamp?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText"&gt;Angèle Kamp&lt;br&gt;
&lt;/a&gt; on &lt;a href="https://unsplash.com/photos/KaeaUITiWnc?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditShareLink"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;

</description>
      <category>saucelabs</category>
      <category>cypress</category>
      <category>tagging</category>
      <category>organisation</category>
    </item>
    <item>
      <title>Better Debugging Output: The Console.Assert Way!</title>
      <dc:creator>Dylan Lacey</dc:creator>
      <pubDate>Wed, 13 Apr 2022 13:24:23 +0000</pubDate>
      <link>https://dev.to/dylanlacey/better-debugging-output-the-consoleassert-way-16k2</link>
      <guid>https://dev.to/dylanlacey/better-debugging-output-the-consoleassert-way-16k2</guid>
      <description>&lt;h1&gt;
  
  
  Debuggers?  In MY Console?
&lt;/h1&gt;

&lt;p&gt;Using the console to debug applications &lt;a href="https://medium.com/@benlmsc/stop-using-console-log-d281a900dedb" rel="noopener noreferrer"&gt;seems&lt;/a&gt; &lt;a href="https://dev.to/dogealgo/why-you-should-not-use-console-log-for-debugging-4pgg"&gt;to&lt;/a&gt; &lt;a href="https://daily.dev/blog/stop-using-console-log" rel="noopener noreferrer"&gt;make&lt;/a&gt; &lt;a href="https://medium.datadriveninvestor.com/stopping-using-console-log-and-start-using-your-browsers-debugger-62bc893d93ff" rel="noopener noreferrer"&gt;people&lt;/a&gt; &lt;a href="https://dev.to/songthamtung/stop-console-logging-this-is-how-to-use-chrome-to-debug-javascript-48nm"&gt;big&lt;/a&gt; &lt;a href="https://codesource.io/debugging-your-javascript-code-console-log-vs-debugger/" rel="noopener noreferrer"&gt;mad&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The thing I find objectionable about the "Don't use Console!" story can be summed up by this quote:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;many developers regularly use the console object’s log method in a way that &lt;em&gt;I think is incorrect&lt;/em&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;See, the thing is, despite having (and perhaps one day sharing) rebuttals for many of the admittedly well made points, the truth is that &lt;em&gt;I simply don't care if you think I'm wrong and ultimately you can't stop me.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I &lt;em&gt;like&lt;/em&gt; using console debugging despite knowing of "Better" solutions.  This series is going to explore features of the JS Console I don't see as often as the venerable &lt;code&gt;console.log()&lt;/code&gt;, in the hopes that you'll like it too.  This time, we're making our logging less verbose, with &lt;code&gt;console.assert()&lt;/code&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  Console.assert
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Console.assert?
&lt;/h2&gt;

&lt;p&gt;Yes!&lt;/p&gt;

&lt;p&gt;You call &lt;code&gt;console.assert()&lt;/code&gt; with a boolean condition and a message; The message is only output if the condition evaluates to false.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;someMsg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;All is Well&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;There has been An Incident.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;// someMsg = "Calamity! Perfidy! Tragedy!"&lt;/span&gt;
&lt;span class="c1"&gt;// Output: &lt;/span&gt;
&lt;span class="nx"&gt;Assertion&lt;/span&gt; &lt;span class="nx"&gt;failed&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;There&lt;/span&gt; &lt;span class="nx"&gt;has&lt;/span&gt; &lt;span class="nx"&gt;been&lt;/span&gt; &lt;span class="nx"&gt;An&lt;/span&gt; &lt;span class="nx"&gt;Incident&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Why is this better?
&lt;/h2&gt;

&lt;p&gt;Logging can give us different &lt;em&gt;kinds&lt;/em&gt; of information.  Some log data is &lt;em&gt;structural&lt;/em&gt; and lets us know &lt;em&gt;what code&lt;/em&gt; is executing.&lt;/p&gt;

&lt;p&gt;Some log data is &lt;em&gt;operational&lt;/em&gt; and lets us know &lt;em&gt;what data&lt;/em&gt; is being executed on.&lt;/p&gt;

&lt;p&gt;Typically, I want all structural log data when I'm debugging, so I can fully trace my application flow.  Contrariwise, I only want operational data that relates to the error I'm tracking down.  If an error-like condition hasn't occurred, operational log data often doesn't tell me anything.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Beauty of Asserting False
&lt;/h2&gt;

&lt;p&gt;I usually only want output if the &lt;em&gt;expected&lt;/em&gt; behaviour doesn't happen, so semantically, this makes sense.  I tell the JS engine what I want, and it only alerts me if it doesn't happen.  Mentally, I only have to think about what "correct" looks like, not what might be "incorrect".&lt;/p&gt;

&lt;h1&gt;
  
  
  Make the output better
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Auto-append object values
&lt;/h2&gt;

&lt;p&gt;Another &lt;code&gt;console.assert&lt;/code&gt; option is to pass a condition and an array of objects.  If the condition is false, the objects are stringified, appended together and output.  This can save you writing a custom error message, which is 🧑🏼‍🍳💋.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;good_investments&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Mines&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Urchins&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;New Fangled Electricity&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;bad_investments&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Radium Health Tonics&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bad_investments&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;bad_investments&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Output:&lt;/span&gt;
&lt;span class="nx"&gt;Assertion&lt;/span&gt; &lt;span class="nx"&gt;failed&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Radium&lt;/span&gt; &lt;span class="nx"&gt;Health&lt;/span&gt; &lt;span class="nx"&gt;Tonics&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Output Formatting
&lt;/h2&gt;

&lt;p&gt;What if we want something &lt;em&gt;more&lt;/em&gt; then just a set of object values?  We could pre-format a message string, &lt;em&gt;or&lt;/em&gt;.... We could use &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/console#outputting_text_to_the_console" rel="noopener noreferrer"&gt;string substitutions&lt;/a&gt; to do it inline!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;good_investments&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Mines&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Urchins&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;New Fangled Electricity&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;bad_investment&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Radium Health Tonics, Inc.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;

&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;losses&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;pounds&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;150&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;reputation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;47&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;body_parts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Teeth&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Jaw&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;bad_investment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;%s has cost you %d Pounds and reduced your reputation by %f%&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;bad_investment&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;losses&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pounds&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;losses&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;reputation&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;//Output:&lt;/span&gt;
&lt;span class="nx"&gt;Assertion&lt;/span&gt; &lt;span class="nx"&gt;failed&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Radium&lt;/span&gt; &lt;span class="nx"&gt;Health&lt;/span&gt; &lt;span class="nx"&gt;Tonics&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Inc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt; &lt;span class="nx"&gt;has&lt;/span&gt; &lt;span class="nx"&gt;cost&lt;/span&gt; &lt;span class="nx"&gt;you&lt;/span&gt; &lt;span class="mi"&gt;150&lt;/span&gt; &lt;span class="nx"&gt;Pounds&lt;/span&gt; &lt;span class="nx"&gt;and&lt;/span&gt; &lt;span class="nx"&gt;reduced&lt;/span&gt; &lt;span class="nx"&gt;your&lt;/span&gt; &lt;span class="nx"&gt;reputation&lt;/span&gt; &lt;span class="nx"&gt;by&lt;/span&gt; &lt;span class="mi"&gt;47&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you're confused about the use of string placeholders (&lt;code&gt;%s&lt;/code&gt; and &lt;code&gt;%f&lt;/code&gt;), check out my rapid guide to the &lt;a href="https://dev.to/dylanlacey/cool-console-stuff-easy-object-inspection-14c4"&gt;Console method string substitutions&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Object String Substitution (My favourite bit)
&lt;/h2&gt;

&lt;p&gt;In fact, that same article shows off Object substitution, which is &lt;em&gt;extremely&lt;/em&gt; useful here.  Instead of:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Assertion failed: [object Object]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Object substitution lets &lt;code&gt;console.append&lt;/code&gt; return a nicely formatted message to the CLI... and an &lt;em&gt;expandable, clickable&lt;/em&gt; Object in the browser console!  Click it, and you'll go directly to the inspector to dig around and see what's gone wrong.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Your %O exceed %d pounds. See %o.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
    &lt;span class="nx"&gt;losses&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;losses&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pounds&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;losses&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;history&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;//Output:&lt;/span&gt;
&lt;span class="nx"&gt;Assertion&lt;/span&gt; &lt;span class="nx"&gt;failed&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Your&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Losses&lt;/span&gt; &lt;span class="nx"&gt;exceed&lt;/span&gt; &lt;span class="mi"&gt;150&lt;/span&gt; &lt;span class="nx"&gt;pounds&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt; &lt;span class="nx"&gt;See&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;History&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Wrap-up
&lt;/h1&gt;

&lt;p&gt;So that's the power of &lt;code&gt;console.assert&lt;/code&gt;!  Rich, compact debugging messages that show up only when needed, giving you ample ability to present and explore the relevant information.&lt;/p&gt;

&lt;p&gt;If you liked this, please hit the Dopamine Switches (🦄❤️) on the left, and subscribe to my writing for the next entry in the JS Console Debugging series!&lt;/p&gt;

&lt;h1&gt;
  
  
  Recognition
&lt;/h1&gt;

&lt;p&gt;Header image is by &lt;a href="https://unsplash.com/@sigmund" rel="noopener noreferrer"&gt;@Sigmund&lt;/a&gt;, used under the Unsplash License.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>debugging</category>
      <category>codequality</category>
    </item>
    <item>
      <title>Cool Console stuff: Easy object inspection</title>
      <dc:creator>Dylan Lacey</dc:creator>
      <pubDate>Wed, 06 Apr 2022 02:37:06 +0000</pubDate>
      <link>https://dev.to/dylanlacey/cool-console-stuff-easy-object-inspection-14c4</link>
      <guid>https://dev.to/dylanlacey/cool-console-stuff-easy-object-inspection-14c4</guid>
      <description>&lt;h1&gt;
  
  
  So here's a weird thing.
&lt;/h1&gt;

&lt;p&gt;The &lt;code&gt;console&lt;/code&gt; methods have &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/console#outputting_text_to_the_console" rel="noopener noreferrer"&gt;their own C-like&lt;/a&gt; String Substitution methods.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wait, String Substitution?
&lt;/h2&gt;

&lt;p&gt;Literally, substituting part of a string for something else.  In Programming Land, that usually means building strings via interpolation or formatting strings:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;guest&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Fortesque-Delcourt&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;calling_gift&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;a rather nice sherry&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s2"&gt;`Sir, Mr &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;guest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; has called. He's bought &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;guest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;calling_gift&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Output:&lt;/span&gt;
&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Sir, Mr Fortesque-Delcourt has called.  He's bought a rather nice sherry.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  OK so you can add strings, so what?
&lt;/h1&gt;

&lt;p&gt;You can build strings with Template Literals, or string concatenation, but the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/console#outputting_text_to_the_console" rel="noopener noreferrer"&gt;"&lt;em&gt;C-Like&lt;/em&gt;" formatting referred to in the docs&lt;/a&gt; means you can pass a templated string &lt;em&gt;and the replacements&lt;/em&gt; as arguments to &lt;code&gt;log&lt;/code&gt; method:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Sir, Mr %s has called. He's bought %s&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
    &lt;span class="nx"&gt;guest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
    &lt;span class="nx"&gt;guest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;calling_gift&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Output:&lt;/span&gt;
&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Sir, Mr Fortesque-Delcourt has called.  He's bought a rather nice sherry.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You're not restricted to substituting strings literally.  Instead, the templating engine tries to convert the passed object into the data type specified by the placeholder.&lt;/p&gt;

&lt;p&gt;Let's say we have an object representing the investment losses of a Victorian investor:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;losses&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;pounds&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;150&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;reputation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;45.7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;body_parts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Teeth&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
        &lt;span class="na"&gt;faith&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;The Markets&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;New-Fangled Steam&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here's what you get when you pass various types to the different placeholders:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Template String&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;losses.body_parts&lt;/th&gt;
&lt;th&gt;losses.pounds&lt;/th&gt;
&lt;th&gt;losses.reputation&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;"Lost: %s"&lt;/td&gt;
&lt;td&gt;"Lost: Teeth"&lt;/td&gt;
&lt;td&gt;"Lost: 150"&lt;/td&gt;
&lt;td&gt;"Lost: 45.7"&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;"Lost: %i"&lt;/td&gt;
&lt;td&gt;"Lost: NaN"&lt;/td&gt;
&lt;td&gt;"Lost: 150"&lt;/td&gt;
&lt;td&gt;"Lost: 45"&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;"Lost: %f"&lt;/td&gt;
&lt;td&gt;"Lost: NaN"&lt;/td&gt;
&lt;td&gt;"Lost: 150"&lt;/td&gt;
&lt;td&gt;"Lost: 45.7"&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h1&gt;
  
  
  Is this useful?
&lt;/h1&gt;

&lt;p&gt;No, not particularly.  It does let you see at a glance if you pass a String into an Integer or Float placeholder but that's hardly going to save the world.&lt;/p&gt;

&lt;p&gt;What &lt;em&gt;is&lt;/em&gt; useful is the Object placeholders, &lt;code&gt;%o&lt;/code&gt; and &lt;code&gt;%O&lt;/code&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  The Object Placeholders
&lt;/h1&gt;

&lt;p&gt;If you call &lt;code&gt;console.log&lt;/code&gt; while combining an Object with a String (eg &lt;code&gt;console.log("Your Object, Sir" + obj)&lt;/code&gt;, you're likely to get back the stunningly unhelpful default &lt;code&gt;toString&lt;/code&gt; output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[object Object]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is both unhelpful and gross... However!  If you use one of the Object placeholders, you'll get &lt;em&gt;something&lt;/em&gt; that represents an enumeration of the properties and their values.  What &lt;em&gt;kind&lt;/em&gt; of something depends on the operator, and what kind of console you're using.&lt;/p&gt;

&lt;h2&gt;
  
  
  Node.js CLI
&lt;/h2&gt;

&lt;p&gt;From the CLI, calling &lt;code&gt;console.log&lt;/code&gt; with the uppercase &lt;code&gt;%O&lt;/code&gt; operator will generate an inline enumeration of properties.&lt;/p&gt;

&lt;p&gt;Using the lowercase &lt;code&gt;%o&lt;/code&gt; will enumerate the object's properties over multiple lines.&lt;/p&gt;

&lt;h3&gt;
  
  
  Inline (with %O)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;%O&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;losses&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;//Output:&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;pounds&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;150&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;reputation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;45.7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;body_parts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Teeth&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Faith&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;The Markets&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;New-Fangled Steam&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Multi-Line (with %o)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;%o&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;losses&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;//Output:&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;pounds&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;150&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;reputation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;45.7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;body_parts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Teeth&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;Faith&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;The Markets&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;New Fangled Steam&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Browser Console
&lt;/h2&gt;

&lt;p&gt;In the Chrome console, things act sort of backwards.  Here, the lowercase "%o" operator will return an =inline= enumeration:&lt;/p&gt;

&lt;h3&gt;
  
  
  Inline (with %o)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;%o&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;losses&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;// Output:&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;pounds&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;150&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;reputation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;45.7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;body_parts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Teeth&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;Faith&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;The Markets&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;New-Fangled Steam&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Object Linking (with %O)
&lt;/h3&gt;

&lt;p&gt;The cool part, however, is with &lt;code&gt;%O&lt;/code&gt;.  Using the uppercase format, the output string will contain only the object's &lt;em&gt;name&lt;/em&gt;... Which is clickable, taking you to the inspector to let you poke around!&lt;/p&gt;

&lt;p&gt;Let's say our &lt;code&gt;losses&lt;/code&gt; object is an instance of the &lt;code&gt;Losses&lt;/code&gt; class, and it also contains some sort of &lt;code&gt;history&lt;/code&gt; object.  We can create a rich, readable debugging message which gives us easy access to object internals, but doesn't clutter up the console:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Your %O exceed %d pounds. See %O.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
    &lt;span class="nx"&gt;losses&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;losses&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pounds&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;losses&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;history&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;//Output:&lt;/span&gt;
&lt;span class="nx"&gt;Your&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Losses&lt;/span&gt; &lt;span class="nx"&gt;exceed&lt;/span&gt; &lt;span class="mi"&gt;150&lt;/span&gt; &lt;span class="nx"&gt;pounds&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt; &lt;span class="nx"&gt;See&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;History&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Wrap-up
&lt;/h1&gt;

&lt;p&gt;So that's the power of Console String Substitution!  Easy formatting for terse but richly informative console messages.&lt;/p&gt;

&lt;p&gt;If you liked this, please hit the Dopamine Switches (🦄❤️) on the left, and subscribe to my writing for the next entry in the JS Console Debugging series!&lt;/p&gt;

&lt;h1&gt;
  
  
  Recognition
&lt;/h1&gt;

&lt;p&gt;Header image is by &lt;a href="https://unsplash.com/@sigmund" rel="noopener noreferrer"&gt;@Sigmund&lt;/a&gt;, used under the Unsplash License.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>debugging</category>
      <category>console</category>
    </item>
  </channel>
</rss>
