{"data":{"allMdx":{"nodes":[{"id":"729918f1-eef6-5167-bc49-f42e44ef2bc9","body":"var _excluded = [\"components\"];\nfunction _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }\nfunction _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }\nfunction _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }\n/* @jsxRuntime classic */\n/* @jsx mdx */\n\nvar _frontmatter = {};\nvar makeShortcode = function makeShortcode(name) {\n  return function MDXDefaultShortcode(props) {\n    console.warn(\"Component \" + name + \" was not imported, exported, or provided by MDXProvider as global scope\");\n    return mdx(\"div\", props);\n  };\n};\nvar AuthorBanner = makeShortcode(\"AuthorBanner\");\nvar FeedSearch = makeShortcode(\"FeedSearch\");\nvar Tags = makeShortcode(\"Tags\");\nvar FeedItems = makeShortcode(\"FeedItems\");\nvar layoutProps = {\n  _frontmatter: _frontmatter\n};\nvar MDXLayout = \"wrapper\";\nreturn function MDXContent(_ref) {\n  var components = _ref.components,\n    props = _objectWithoutProperties(_ref, _excluded);\n  return mdx(MDXLayout, _extends({}, layoutProps, props, {\n    components: components,\n    mdxType: \"MDXLayout\"\n  }), mdx(AuthorBanner, {\n    mdxType: \"AuthorBanner\"\n  }), mdx(\"p\", null, \"Welcome to my blog where I discuss Software Testing and Quality Assurance and my hobbies; smart home gadgets, woodworking, gardening, and DIY home improvement.\"), mdx(\"hr\", null), mdx(FeedSearch, {\n    mdxType: \"FeedSearch\"\n  }), mdx(Tags, {\n    mdxType: \"Tags\"\n  }), mdx(FeedItems, {\n    mdxType: \"FeedItems\"\n  }));\n}\n;\nMDXContent.isMDXComponent = true;","excerpt":"Welcome to my blog where I discuss Software Testing and Quality Assurance and my hobbies; smart home gadgets, woodworking, gardening, and…","fields":{"slug":"/","type":"pages"},"headings":[],"frontmatter":{"title":"","description":null,"link":null,"date":null,"tags":null,"draft":null,"hide":null,"cover":null}},{"id":"14d2868a-1dce-5d81-abcf-bc7adf43c066","body":"var _excluded = [\"components\"];\nfunction _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }\nfunction _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }\nfunction _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }\n/* @jsxRuntime classic */\n/* @jsx mdx */\n\nvar _frontmatter = {};\nvar makeShortcode = function makeShortcode(name) {\n  return function MDXDefaultShortcode(props) {\n    console.warn(\"Component \" + name + \" was not imported, exported, or provided by MDXProvider as global scope\");\n    return mdx(\"div\", props);\n  };\n};\nvar FeedItems = makeShortcode(\"FeedItems\");\nvar layoutProps = {\n  _frontmatter: _frontmatter\n};\nvar MDXLayout = \"wrapper\";\nreturn function MDXContent(_ref) {\n  var components = _ref.components,\n    props = _objectWithoutProperties(_ref, _excluded);\n  return mdx(MDXLayout, _extends({}, layoutProps, props, {\n    components: components,\n    mdxType: \"MDXLayout\"\n  }), mdx(\"h1\", {\n    \"id\": \"smart-home-and-iot\"\n  }, \"Smart Home and IoT\"), mdx(\"p\", null, \"Here you'll find reviews on smart home and Internet of Things gadgets I've tried and how to install, and wire (as applicable), them in your house.\"), mdx(FeedItems, {\n    filterByTags: ['smart-home', 'iot', 'unifi'],\n    mdxType: \"FeedItems\"\n  }));\n}\n;\nMDXContent.isMDXComponent = true;","excerpt":"Smart Home and IoT Here you'll find reviews on smart home and Internet of Things gadgets I've tried and how to install, and wire (as…","fields":{"slug":"/smart-home-iot/","type":"pages"},"headings":[{"value":"Smart Home and IoT","depth":1}],"frontmatter":{"title":"","description":null,"link":null,"date":null,"tags":null,"draft":null,"hide":null,"cover":null}},{"id":"2ffd9310-70b2-50ea-9218-d5aafbbab48e","body":"var _excluded = [\"components\"];\nfunction _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }\nfunction _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }\nfunction _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }\n/* @jsxRuntime classic */\n/* @jsx mdx */\n\nvar _frontmatter = {};\nvar makeShortcode = function makeShortcode(name) {\n  return function MDXDefaultShortcode(props) {\n    console.warn(\"Component \" + name + \" was not imported, exported, or provided by MDXProvider as global scope\");\n    return mdx(\"div\", props);\n  };\n};\nvar FeedItems = makeShortcode(\"FeedItems\");\nvar Embed = makeShortcode(\"Embed\");\nvar layoutProps = {\n  _frontmatter: _frontmatter\n};\nvar MDXLayout = \"wrapper\";\nreturn function MDXContent(_ref) {\n  var components = _ref.components,\n    props = _objectWithoutProperties(_ref, _excluded);\n  return mdx(MDXLayout, _extends({}, layoutProps, props, {\n    components: components,\n    mdxType: \"MDXLayout\"\n  }), mdx(\"h1\", {\n    \"id\": \"software-testing-and-quality-assurance\"\n  }, \"Software Testing and Quality Assurance\"), mdx(\"p\", null, \"I enjoy breaking software. I also enjoy writing software that breaks software. However, my favorite thing is teaching others how to become better software test engineers.\"), mdx(\"p\", null, \"I've compiled a list of tutorials on automated software testing tools and quality assurance techniques below. Check back often for new test automation tutorials and please reach out on Twitter if you have questions or have topic requests.\"), mdx(\"p\", null, \"You'll notice a lot of content on \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://nightwatchjs.org/\"\n  }, \"Nightwatch.js\"), \" which is my preferred browser automation framework. It's written in Node.js on top of Selenium, but has functionality that makes your tests more reliable out of the box.\"), mdx(\"h2\", {\n    \"id\": \"software-testing-articles-and-tutorials\"\n  }, \"Software Testing Articles and Tutorials\"), mdx(FeedItems, {\n    filterByTags: ['nightwatchjs', 'software testing', 'quality assurance'],\n    mdxType: \"FeedItems\"\n  }), mdx(\"h2\", {\n    \"id\": \"selenium-in-java\"\n  }, \"Selenium in Java\"), mdx(\"p\", null, \"Selenium Webdriver is a workhorse. Java is not my favorite language, but sometimes it is easier on your team to understand and help with testing if you write your tests in the same language the software being tested in written in.\"), mdx(\"p\", null, \"The tutorial below will show you how to write reliable and refactorable tests in Selenium by using dynamic waits and the page object model.\"), mdx(Embed, {\n    src: \"https://www.youtube.com/embed/N86y-XjHcqU\",\n    mdxType: \"Embed\"\n  }));\n}\n;\nMDXContent.isMDXComponent = true;","excerpt":"Software Testing and Quality Assurance I enjoy breaking software. I also enjoy writing software that breaks software. However, my favorite…","fields":{"slug":"/software-testing/","type":"pages"},"headings":[{"value":"Software Testing and Quality Assurance","depth":1},{"value":"Software Testing Articles and Tutorials","depth":2},{"value":"Selenium in Java","depth":2}],"frontmatter":{"title":"","description":null,"link":null,"date":null,"tags":null,"draft":null,"hide":null,"cover":null}},{"id":"ac2df9c1-2c46-50ff-8fcc-94118039b97a","body":"var _excluded = [\"components\"];\nfunction _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }\nfunction _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }\nfunction _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }\n/* @jsxRuntime classic */\n/* @jsx mdx */\n\nvar _frontmatter = {\n  \"title\": \"Adding Custom Commands to Nightwatch TypeScript Projects\",\n  \"cover\": \"./nightwatch-typescript-article-banner.jpg\",\n  \"date\": \"2023-04-21T00:00:00.000Z\",\n  \"description\": \"Learn how to extend Nightwatch's built-in automation commands with your own when working inside a TypeScript Nightwatch test project in this post.\",\n  \"tags\": [\"quality assurance\", \"software testing\", \"nightwatchjs\", \"typescript\", \"nightwatch\", \"post\"]\n};\nvar makeShortcode = function makeShortcode(name) {\n  return function MDXDefaultShortcode(props) {\n    console.warn(\"Component \" + name + \" was not imported, exported, or provided by MDXProvider as global scope\");\n    return mdx(\"div\", props);\n  };\n};\nvar Embed = makeShortcode(\"Embed\");\nvar layoutProps = {\n  _frontmatter: _frontmatter\n};\nvar MDXLayout = \"wrapper\";\nreturn function MDXContent(_ref) {\n  var components = _ref.components,\n    props = _objectWithoutProperties(_ref, _excluded);\n  return mdx(MDXLayout, _extends({}, layoutProps, props, {\n    components: components,\n    mdxType: \"MDXLayout\"\n  }), mdx(\"p\", null, \"It's been about a year since my last post about \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"../using-nightwatch-with-typescript/\"\n  }, \"using Nightwatch test automation with TypeScript\"), \" (instead of the JavaScript default). The Nightwatch developers have added significant features in that time making it even more convenient to get the benefits of TypeScript when working in Nightwatch Test Automation.\"), mdx(\"p\", null, \"This post will cover adding custom commands, one of the less documented areas, when working in a TypeScript Nightwatch project rather than a JS one. This is a bit trickier than when working in JavaScript because JavaScript has no type-checking whereas TypeScript does so one needs to append any custom commands not part of the Nightwatch API to the type definitions.\"), mdx(\"p\", null, \"In addition, this article will serve as an update to the original Nightwatch TypeScript how-to post with some of convenient new updates since the article was written. For example,\"), mdx(\"ul\", null, mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Automatic project scaffolding\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Execution of .ts tests without requiring \", mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"tsc\"), \" transpiling first.\")), mdx(\"h2\", {\n    \"id\": \"creating-a-typescript-nightwatch-test-suite-with-npm-init\"\n  }, \"Creating a TypeScript Nightwatch Test Suite (with npm init)\"), mdx(\"p\", null, \"One can now do a \\\"choose-your-own-adventure\\\" type of project setup when creating new Nightwatch projects through their scaffolding command \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"npm init nightwatch@latest\"), \". Where one can answer what kind of test project they want to create, which browsers to run on, and if they are doing so using a cloud test provider like SauceLabs or  BrowserStack. Everything is automatically downloaded and configured, including the nightwatch.conf.js file, to their selections.\"), mdx(\"p\", null, \"Here is an example of it getting a new \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://www.youtube.com/shorts/IJ_nBsPLslo\"\n  }, \"TypeScript project setup from scratch in under 60 seconds\"), \".\"), mdx(Embed, {\n    src: \"https://www.youtube.com/embed/IJ_nBsPLslo\",\n    mdxType: \"Embed\"\n  }), mdx(\"p\", null, \"Steps\"), mdx(\"ol\", null, mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"At a command line type \", mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"npm init nightwatch@latest\")), mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Affirmatively answer to proceed.\"), mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Select TypeScript\")), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-sh\"\n  }, \"===============================\\nNightwatch Configuration Wizard\\n===============================\\n\\nSetting up Nightwatch in C:\\\\test-automation\\\\nightwatch-typescript...\\n\\n? Select testing type to setup for your project End-to-End testing\\n? Select language + test runner variant \\n  JavaScript / default\\n> TypeScript / default\\n  JavaScript / Mocha\\n  JavaScript / CucumberJS\\n\")), mdx(\"p\", null, \"Differing from my earlier article, the new recommended project structure has all the tests, page objects, custom commands, and so on under the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"/nightwatch\"), \" folder.\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\"\n  }, \"nightwatch.config.js\\n/nightwatch\\n\\u2514\\u2500\\u2500\\u2500tsconfig.json\\n\\u2502\\n\\u2514\\u2500\\u2500\\u2500/page-objects\\n\\u2502   \\u2502\\n\\u2502   \\u2514\\u2500\\u2500\\u2500testPage1.ts\\n\\u2502   \\u2514\\u2500\\u2500\\u2500testPage2.ts\\n\\u2502\\n\\u2514\\u2500\\u2500\\u2500/tests\\n\\u2502   \\u2502\\n\\u2502   \\u2514\\u2500\\u2500\\u2500test1.ts\\n\\u2502   \\u2514\\u2500\\u2500\\u2500test2.ts\\n\\u2502\\n\\u2514\\u2500\\u2500\\u2500/commands\\n\\u2502   \\u2502\\n\\u2502   \\u2514\\u2500\\u2500\\u2500customWait.ts\\n\\u2502\\n\\u2514\\u2500\\u2500\\u2500/types\\n    \\u2502\\n    nightwatch.d.ts\\n\\n\")), mdx(\"p\", null, \"Default sample tests are included and don't need to be transpiled with \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"tsc\"), \" to JavaScript before running. To execute tests when setup in this project structure you can simply run \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"npx nightwatch\"), \" to run all your tests or \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"npx nightwatch -t ./nightwatch/tests/your-test-here.ts\"), \" for a specific test.\"), mdx(\"h2\", {\n    \"id\": \"writing-custom-commands-in-a-nightwatch-typescript-project\"\n  }, \"Writing Custom Commands in a Nightwatch TypeScript project\"), mdx(\"p\", null, \"One of the nice features about Nightwatch is how easy it is to extend the API with ones own custom commands. Commands in the directory that one adds in the path of their \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"custom_commands_path\"), \" property of \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"nightwatch.conf.js\"), \" will automatically be available on the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"browser\"), \" object as \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"browser.yourCommandHere()\"), \" when writing their tests. This takes advantage of the fact JavaScript doesn't do type checking. However, TypeScript is more structured in that it does type-checking which creates a conflict here since \\\"yourCommand\\\" is not part of \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://www.npmjs.com/package/@types/nightwatch\"\n  }, \"@types/nightwatch\"), \" so TypeScript alerts that the command does not exist. To correct this, one can leverage an overriding definition file like \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"nightwatch.d.ts\"), \" that would append the custom command to the existing Nightwatch types.\"), mdx(\"h3\", {\n    \"id\": \"step-1-create-your-custom-command\"\n  }, \"Step 1: Create your custom command\"), mdx(\"p\", null, \"The structure is very similar to the existing \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://nightwatchjs.org/guide/extending-nightwatch/adding-custom-commands.html#guide-container\"\n  }, \"custom command documentation\"), \", but adds some types as shown in this simple example\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-ts\"\n  }, \"// nightwatch/commands/waitForLoadScreen.ts\\nimport { NightwatchCLient, NightwatchExpectedResult } from 'nightwatch'\\n\\nexport default class WaitForLoadScreen {\\n  async command(\\n    this: NightwatchClient,\\n    maxWaitInMs: number\\n  ): Promise<NightwatchExpectResult> {\\n    return this.api.expect\\n      .element('#loadScreenOverlay')\\n      .to.not.be.visible.before(maxWaitInMs);\\n  }\\n}\\n\")), mdx(\"p\", null, \"This file should be stored in the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"./nightwatch/commands\"), \" folder with \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"nightwatch.conf.js\"), \" set to\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-js\"\n  }, \"// nightwatch.conf.js\\n//...\\ncustom_commands_path: ['nightwatch/commands'],\\n//...\\n\")), mdx(\"p\", null, \"If there was no type-checking, one would be able to use \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"browser.waitForLoadScreen(5000)\"), \", but since \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"waitForLoadScreen\"), \" is not a typed method of the Nightwatch API TypeScript will display an error alerting one that waitForLoadScreen does not exist on the browser object. So in step 2, let's fix that.\"), mdx(\"h3\", {\n    \"id\": \"step-2-add-a-definition-for-your-custom-command\"\n  }, \"Step 2: Add a definition for your Custom Command\"), mdx(\"p\", null, \"In the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"nightwatch/types\"), \" folder create a file called \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"nightwatch.d.ts\"), \". This will append your command to the existing NightwatchCustomCommands type definitions.\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-ts\"\n  }, \"// nightwatch/types/nightwatch.d.ts\\ndeclare module 'nightwatch' {\\n  export interface NightwatchCustomCommands {\\n    waitForLoadScreen(maxWaitInMs: number): Awaitable<this, null>;\\n  }\\n}\\n\")), mdx(\"h3\", {\n    \"id\": \"step-3-link-the-definition-file-through-tsconfig\"\n  }, \"Step 3: Link the definition file through tsconfig\"), mdx(\"p\", null, \"Open the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"./nightwatch/tsconfig.json\"), \" (there is one in the root project directory as well--leave that outer one alone)\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-js\"\n  }, \"// nightwatch/tsconfig.json\\n\\\"compilerOptions\\\": {\\n  // excluded for brevity\\n},\\n\\\"files\\\": [\\\"/types/nightwatch.d.ts\\\"]\\n\")), mdx(\"p\", null, \"Append the files property under compilerOptions as shown with the path to the definition file. Once all three steps are complete the custom command should be available to use without any TypeScript warnings.\"), mdx(\"h2\", {\n    \"id\": \"final-thoughts\"\n  }, \"Final Thoughts\"), mdx(\"p\", null, \"The Nightwatch TypeScript implementation has matured very nicely over the past year and should continue to progress with Nightwatch 3.0 right around the corner. Stay tuned for updates on their \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://github.com/nightwatchjs/nightwatch/releases\"\n  }, \"Nightwatch GitHub Releases\"), \" page for the latest.\"), mdx(\"p\", null, \"If you are a Nightwatch beginner be sure to watch my \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://youtube.com/playlist?list=PLLS_Ef55N6hmkt3-JlW40GAGpXSlp8t_D\"\n  }, \"Software Testing Playlist\")), mdx(\"p\", null, \"Please share the link to this article if you enjoyed it and if you have any questions or comments please reach out through my social links below \\uD83D\\uDC47\"));\n}\n;\nMDXContent.isMDXComponent = true;","excerpt":"It's been about a year since my last post about  using Nightwatch test automation with TypeScript  (instead of the JavaScript default). The…","fields":{"slug":"/nightwatch-typescript-custom-command-extensibility/","type":"posts"},"headings":[{"value":"Creating a TypeScript Nightwatch Test Suite (with npm init)","depth":2},{"value":"Writing Custom Commands in a Nightwatch TypeScript project","depth":2},{"value":"Step 1: Create your custom command","depth":3},{"value":"Step 2: Add a definition for your Custom Command","depth":3},{"value":"Step 3: Link the definition file through tsconfig","depth":3},{"value":"Final Thoughts","depth":2}],"frontmatter":{"title":"Adding Custom Commands to Nightwatch TypeScript Projects","description":"Learn how to extend Nightwatch's built-in automation commands with your own when working inside a TypeScript Nightwatch test project in this post.","link":null,"date":"2023-04-21T00:00:00.000Z","tags":["quality assurance","software testing","nightwatchjs","typescript","nightwatch","post"],"draft":null,"hide":null,"cover":{"childImageSharp":{"fluid":{"base64":"data:image/jpeg;base64,/9j/2wBDAAMCAgMCAgMDAwMEAwMEBQgFBQQEBQoHBwYIDAoMDAsKCwsNDhIQDQ4RDgsLEBYQERMUFRUVDA8XGBYUGBIUFRT/2wBDAQMEBAUEBQkFBQkUDQsNFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBT/wgARCAANABQDASIAAhEBAxEB/8QAFwAAAwEAAAAAAAAAAAAAAAAAAAYHBP/EABUBAQEAAAAAAAAAAAAAAAAAAAMA/9oADAMBAAIQAxAAAAHfOKqirJAyCD//xAAbEAACAgMBAAAAAAAAAAAAAAAEBQEDAgYTFf/aAAgBAQABBQIrW/MHGnNiW1XWK2GwMrKS6iuJbXODyf/EABoRAAICAwAAAAAAAAAAAAAAAAECAAMEESH/2gAIAQMBAT8BORao2YHZugT/xAAZEQACAwEAAAAAAAAAAAAAAAABAgADIRL/2gAIAQIBAT8BWmtjyMhrRcJn/8QAIBAAAgIBBQADAAAAAAAAAAAAAQIDEQAEEhMhMSIyUf/aAAgBAQAGPwKZY1bUOyduihV3ZDAIk+TcYZgaHeTaWSmeM9lfMlg05eEJ9jyE7sEhjRivtDbeCWuM7ADRuz+5/8QAHBABAAIDAQEBAAAAAAAAAAAAAREhADFBUWGx/9oACAEBAAE/IWeWoBGqWfbwZOBSdhz7+46EIA2ie+axwljL58ugvUYlySBhmxjJ9YgD0WO5/9oADAMBAAIAAwAAABDQz//EABsRAAIBBQAAAAAAAAAAAAAAAAERACFBYdHh/9oACAEDAQE/EDFZVkNQFSg55P/EABoRAQACAwEAAAAAAAAAAAAAAAEAEUFh0eH/2gAIAQIBAT8QFo2zb2IXVNez/8QAGxABAQEBAQEBAQAAAAAAAAAAAREhAEExYXH/2gAIAQEAAT8QSs/CalUj1RB5trmZFLeVP7wUMD6gQfWlX6O8zUwJjSHPX2vrM5XSFGdRZFLUUdE4dHDwCtKimpu+9//Z","aspectRatio":1.5,"src":"/static/22b9dc1f84ca47e09e6eec3207ccb83d/60a5f/nightwatch-typescript-article-banner.jpg","srcSet":"/static/22b9dc1f84ca47e09e6eec3207ccb83d/3e5eb/nightwatch-typescript-article-banner.jpg 192w,\n/static/22b9dc1f84ca47e09e6eec3207ccb83d/2880b/nightwatch-typescript-article-banner.jpg 384w,\n/static/22b9dc1f84ca47e09e6eec3207ccb83d/60a5f/nightwatch-typescript-article-banner.jpg 768w","srcWebp":"/static/22b9dc1f84ca47e09e6eec3207ccb83d/25278/nightwatch-typescript-article-banner.webp","srcSetWebp":"/static/22b9dc1f84ca47e09e6eec3207ccb83d/a278a/nightwatch-typescript-article-banner.webp 192w,\n/static/22b9dc1f84ca47e09e6eec3207ccb83d/2474b/nightwatch-typescript-article-banner.webp 384w,\n/static/22b9dc1f84ca47e09e6eec3207ccb83d/25278/nightwatch-typescript-article-banner.webp 768w","sizes":"(max-width: 768px) 100vw, 768px","presentationWidth":768,"presentationHeight":512}}}}},{"id":"7f7dfbff-bf24-5172-b6f2-90f7634b2b2f","body":"var _excluded = [\"components\"];\nfunction _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }\nfunction _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }\nfunction _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }\n/* @jsxRuntime classic */\n/* @jsx mdx */\n\nvar _frontmatter = {\n  \"title\": \"Best Websites for Practicing Test Automation\",\n  \"cover\": \"./best-sites-for-test-practice.jpg\",\n  \"date\": \"2023-02-10T00:00:00.000Z\",\n  \"description\": \"Looking for example demo sites to practice your test automation against with Selenium, Cypress, or NightwatchJS? Here is a list of the top demo websites for practicing your test automation skills against.\",\n  \"tags\": [\"quality assurance\", \"software testing\", \"nightwatchjs\", \"selenium\", \"post'\"]\n};\nvar layoutProps = {\n  _frontmatter: _frontmatter\n};\nvar MDXLayout = \"wrapper\";\nreturn function MDXContent(_ref) {\n  var components = _ref.components,\n    props = _objectWithoutProperties(_ref, _excluded);\n  return mdx(MDXLayout, _extends({}, layoutProps, props, {\n    components: components,\n    mdxType: \"MDXLayout\"\n  }), mdx(\"h2\", {\n    \"id\": \"why-use-demo-websites-for-test-automation-practice\"\n  }, \"Why use demo websites for test automation practice\"), mdx(\"p\", null, \"Before investing time in learning and building out a test suite against a new automation framework it is a good idea to try it out against different kinds of websites to ensure the framework performs reliably and is easy to work with. For example, if you are using Selenium currently, but are interesting in trying Cypress, Nightwatch, WebDriverIO, or Playwright try doing more than just the basic example against these sites. This way you don't get too invested to find out your chosen automation framework doesn't work on some important area you need to write automation for.\"), mdx(\"p\", null, \"You'll want to try out your test automation framework against things like:\"), mdx(\"ul\", null, mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"iFrames\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Modal dialogs\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"New tabs\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Multiple windows\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Asynchronous loading of content\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"File upload dialogs\")), mdx(\"p\", null, \"You may not have one or all of these scenarios in your current automated software testing project. These demo sites become useful to provide somewhere to practice skills around these harder scenarios if you don't get exposure in your normal everyday feature testing.\"), mdx(\"p\", null, \"I've compiled a list of the best websites I've come across to practice or demo test automation frameworks against in the list below. They include the harder to automate scenarios just mentioned.\"), mdx(\"h2\", {\n    \"id\": \"recommended-demo-websites-for-test-automation-practice\"\n  }, \"Recommended demo websites for test automation practice\"), mdx(\"ol\", null, mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"\\\"The Internet\\\" \", mdx(\"a\", {\n    parentName: \"li\",\n    \"href\": \"https://github.com/saucelabs/the-internet\"\n  }, \"github\"), \" or \", mdx(\"a\", {\n    parentName: \"li\",\n    \"href\": \"http://the-internet.herokuapp.com/\"\n  }, \"live site\"))), mdx(\"p\", null, \"This GitHub repository is a collection of common test automation scenarios including hard to automate situations; nested frames, shadow DOM, keypresses, and complicated DOMs.\"), mdx(\"ol\", {\n    \"start\": 2\n  }, mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"The \", mdx(\"em\", {\n    parentName: \"li\"\n  }, \"Automation Exercise\"), \" clothing store \", mdx(\"a\", {\n    parentName: \"li\",\n    \"href\": \"https://www.automationexercise.com/\"\n  }, \"automationexercise.com\"))), mdx(\"p\", null, \"This example clothing storefront has both a web front end to test against and APIs. The site even includes test cases to guide you.\"), mdx(\"ol\", {\n    \"start\": 3\n  }, mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"LetCode \", mdx(\"a\", {\n    parentName: \"li\",\n    \"href\": \"https://letcode.in/test\"\n  }, \"letcode.in\"))), mdx(\"p\", null, \"This site isolates examples around common DOM elements letting you practice on specific examples of web elements like inputs, tables, alerts, slides, calendars, and more.\"), mdx(\"ol\", {\n    \"start\": 4\n  }, mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"UI Test Automation Playground \", mdx(\"a\", {\n    parentName: \"li\",\n    \"href\": \"http://www.uitestingplayground.com/\"\n  }, \"uitestingplayground.com\"))), mdx(\"p\", null, \"Smaller site, but it contains edge cases for load delays, mouse over behavior, dynamic IDs, and automation issues arising from hidden layers.\"), mdx(\"ol\", {\n    \"start\": 5\n  }, mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"SwagLabs \", mdx(\"a\", {\n    parentName: \"li\",\n    \"href\": \"https://www.saucedemo.com/\"\n  }, \"saucedemo.com\"))), mdx(\"p\", null, \"Another demo web storefront useful for testing login and shopping cart flows. A key distinction on this site is that it has 4 different logins you can use for different experiences for the same site; normal, locked out, problem user, and performance glitch user. Maintained by the folks at SauceLabs.\"), mdx(\"ol\", {\n    \"start\": 6\n  }, mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Angular Banking Site \", mdx(\"a\", {\n    parentName: \"li\",\n    \"href\": \"https://www.globalsqa.com/angularJs-protractor/BankingProject\"\n  }, \"GlobalsQA\"))), mdx(\"p\", null, \"Very small example bank website written in Angular for testing your automation framework against a website written in Angular. The site has login scenarios as well as transaction listing, deposits, and withdrawl behavior workflows.\"), mdx(\"ol\", {\n    \"start\": 7\n  }, mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Forms sandbox \", mdx(\"a\", {\n    parentName: \"li\",\n    \"href\": \"https://automatenow.io/sandbox/\"\n  }, \"AutomateNow\"))), mdx(\"p\", null, \"Small, straight-forward single page site for practicing interactions with typical form controls, search, pop up dialogs, and a map.\"), mdx(\"ol\", {\n    \"start\": 8\n  }, mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Automation Practice \", mdx(\"a\", {\n    parentName: \"li\",\n    \"href\": \"https://qa-practice.netlify.app/\"\n  }, \"QA-Practice\"))), mdx(\"p\", null, \"Clean site that combines several different testing exercises; common web elements, an API with Swagger documentation, small ecommerce site, and a buggy web form you can use to challenge candidates to see how many bugs they can discover.\"), mdx(\"h2\", {\n    \"id\": \"recommended-api-testing-sites\"\n  }, \"Recommended API testing sites\"), mdx(\"p\", null, \"If you are learning SuperTest + Mocha for API Test automation or learning Postman the following sites are handy REST APIs to practice tests against.\"), mdx(\"ol\", null, mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Swagger Petstore \", mdx(\"a\", {\n    parentName: \"li\",\n    \"href\": \"https://petstore.swagger.io/\"\n  }, \"petstore.swagger.io\"))), mdx(\"p\", null, \"Manage an example pet store inventory through this API. This site shows off the usefulness in Swagger when documenting REST APIs, but you can also practice API testing with it.\"), mdx(\"ol\", {\n    \"start\": 2\n  }, mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Restful Booker \", mdx(\"a\", {\n    parentName: \"li\",\n    \"href\": \"https://restful-booker.herokuapp.com/apidoc/index.html\"\n  }, \"restful-booker.herokuapp.com\"))), mdx(\"p\", null, \"Test all kinds of CRUD (Create, Update, Delete) REST API scenarios using this example booking API.\"), mdx(\"ol\", {\n    \"start\": 3\n  }, mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"FakeRestAPI \", mdx(\"a\", {\n    parentName: \"li\",\n    \"href\": \"https://fakerestapi.azurewebsites.net/index.html\"\n  }, \"fakerestapi.azurewebsites.net\"))), mdx(\"p\", null, \"More practice REST endpoints around a book store inventory API. This one lacks Auth operations so you can't practice token creation. However, Restful Booker (above), does support this.\"), mdx(\"ol\", {\n    \"start\": 4\n  }, mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Star Wars API \", mdx(\"a\", {\n    parentName: \"li\",\n    \"href\": \"https://swapi.dev/\"\n  }, \"SWAPI\"))), mdx(\"p\", null, \"A well-documented public API for Star Wars data that can be used for practicing building your own Postman collection or automated API test suite. \"), mdx(\"h2\", {\n    \"id\": \"other-lists--more-reading\"\n  }, \"Other Lists / More Reading\"), mdx(\"ol\", null, mdx(\"li\", {\n    parentName: \"ol\"\n  }, mdx(\"a\", {\n    parentName: \"li\",\n    \"href\": \"https://github.com/BMayhew/awesome-sites-to-test-on\"\n  }, \"Awesome Sites to Test On\"))), mdx(\"p\", null, \"Butch Mayhew maintains a list of more sites categorized by security, mobile, performance, and web.\"), mdx(\"ol\", {\n    \"start\": 2\n  }, mdx(\"li\", {\n    parentName: \"ol\"\n  }, mdx(\"a\", {\n    parentName: \"li\",\n    \"href\": \"https://automationpanda.com/2021/12/29/want-to-practice-test-automation-try-these-demo-sites/\"\n  }, \"Automation Panda\"))), mdx(\"p\", null, \"Recommended sites by Andrew Knight.\"), mdx(\"ol\", {\n    \"start\": 3\n  }, mdx(\"li\", {\n    parentName: \"ol\"\n  }, mdx(\"a\", {\n    parentName: \"li\",\n    \"href\": \"../api-testing-with-nightwatch-supertest/\"\n  }, \"API Testing in Nightwatch.js\"))), mdx(\"p\", null, \"If you know of any other good sites I should include please reach out through my social links below \\uD83D\\uDC47\"));\n}\n;\nMDXContent.isMDXComponent = true;","excerpt":"Why use demo websites for test automation practice Before investing time in learning and building out a test suite against a new automation…","fields":{"slug":"/best-websites-for-practicing-test-automation/","type":"posts"},"headings":[{"value":"Why use demo websites for test automation practice","depth":2},{"value":"Recommended demo websites for test automation practice","depth":2},{"value":"Recommended API testing sites","depth":2},{"value":"Other Lists / More Reading","depth":2}],"frontmatter":{"title":"Best Websites for Practicing Test Automation","description":"Looking for example demo sites to practice your test automation against with Selenium, Cypress, or NightwatchJS? Here is a list of the top demo websites for practicing your test automation skills against.","link":null,"date":"2023-02-10T00:00:00.000Z","tags":["quality assurance","software testing","nightwatchjs","selenium","post'"],"draft":null,"hide":null,"cover":{"childImageSharp":{"fluid":{"base64":"data:image/jpeg;base64,/9j/2wBDAAMCAgMCAgMDAwMEAwMEBQgFBQQEBQoHBwYIDAoMDAsKCwsNDhIQDQ4RDgsLEBYQERMUFRUVDA8XGBYUGBIUFRT/2wBDAQMEBAUEBQkFBQkUDQsNFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBT/wgARCAAKABQDASIAAhEBAxEB/8QAGAAAAgMAAAAAAAAAAAAAAAAAAAUDBgf/xAAWAQEBAQAAAAAAAAAAAAAAAAADAgT/2gAMAwEAAhADEAAAAcRYV9gGeQVkH//EAB0QAAIBBAMAAAAAAAAAAAAAAAIDBAEREhMhMTP/2gAIAQEAAQUCG5hoeIuVXKP6y+EdD//EAB0RAAEDBQEAAAAAAAAAAAAAAAEAAgMEERKh4fD/2gAIAQMBAT8BmjMvuhNp3AWy11f/xAAaEQACAgMAAAAAAAAAAAAAAAABAwACESEx/9oACAECAQE/AWrNuQKsNZn/xAAfEAACAgEEAwAAAAAAAAAAAAABAgARAxASMUEzcYH/2gAIAQEABj8CRUA3N3FvHjehXAueNB8EI60X1P/EAB0QAQADAAEFAAAAAAAAAAAAAAEAESExQXGRseH/2gAIAQEAAT8h23wfU6okqy8sGRbXFPpidnZEpTWEOBkf/9oADAMBAAIAAwAAABD/AC//xAAaEQEAAgMBAAAAAAAAAAAAAAABACERUWGR/9oACAEDAQE/EKMQOi3uoAsHqP/EABkRAQADAQEAAAAAAAAAAAAAAAEAIWHB4f/aAAgBAgEBPxAUEBovSA01ns//xAAdEAEBAAICAwEAAAAAAAAAAAABEQAhMVFBgbHR/9oACAEBAAE/EDsuS74n1uHeDgZNKylYLrTcJGjR5z+PrHXq23pZzMUUSqjORuEi7UzVa5//2Q==","aspectRatio":2,"src":"/static/9f7333124c7c1c15d3155085a6ed37a7/60a5f/best-sites-for-test-practice.jpg","srcSet":"/static/9f7333124c7c1c15d3155085a6ed37a7/3e5eb/best-sites-for-test-practice.jpg 192w,\n/static/9f7333124c7c1c15d3155085a6ed37a7/2880b/best-sites-for-test-practice.jpg 384w,\n/static/9f7333124c7c1c15d3155085a6ed37a7/60a5f/best-sites-for-test-practice.jpg 768w","srcWebp":"/static/9f7333124c7c1c15d3155085a6ed37a7/25278/best-sites-for-test-practice.webp","srcSetWebp":"/static/9f7333124c7c1c15d3155085a6ed37a7/a278a/best-sites-for-test-practice.webp 192w,\n/static/9f7333124c7c1c15d3155085a6ed37a7/2474b/best-sites-for-test-practice.webp 384w,\n/static/9f7333124c7c1c15d3155085a6ed37a7/25278/best-sites-for-test-practice.webp 768w","sizes":"(max-width: 768px) 100vw, 768px","presentationWidth":768,"presentationHeight":382}}}}},{"id":"cafa8e5b-f819-5488-be2e-18563d87bebe","body":"var _excluded = [\"components\"];\nfunction _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }\nfunction _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }\nfunction _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }\n/* @jsxRuntime classic */\n/* @jsx mdx */\n\nvar _frontmatter = {\n  \"title\": \"Add API Testing to your Nightwatch Test Automation\",\n  \"cover\": \"./nightwatch-api-testing.jpg\",\n  \"date\": \"2023-01-13T00:00:00.000Z\",\n  \"description\": \"Learn how to add API testing to your existing selenium automated tests using the API testing plugin for Nightwatch.js\",\n  \"tags\": [\"quality assurance\", \"software testing\", \"nightwatchjs\", \"selenium\", \"post'\"]\n};\nvar layoutProps = {\n  _frontmatter: _frontmatter\n};\nvar MDXLayout = \"wrapper\";\nreturn function MDXContent(_ref) {\n  var components = _ref.components,\n    props = _objectWithoutProperties(_ref, _excluded);\n  return mdx(MDXLayout, _extends({}, layoutProps, props, {\n    components: components,\n    mdxType: \"MDXLayout\"\n  }), mdx(\"p\", null, \"When the software being tested exposes REST API endpoints it can be more efficient to test the APIs directly instead of going through the UI. If the business logic is in the API you can cut out the middle man, the browser, and make the calls directly--eliminating flakiness due to page load times for example. There are many solutions for testing REST APIs including.\"), mdx(\"ul\", null, mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Postman\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"SuperTest\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"RestAssured\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"SoapUI\")), mdx(\"h2\", {\n    \"id\": \"testing-rest-apis-from-your-selenium-test-projects\"\n  }, \"Testing REST APIs from your Selenium Test Projects\"), mdx(\"p\", null, \"The downside of these tools is they didn't, until recently, integrate with one's existing selenium UI automated test suite. The API tests would live in a separate framework or solution. Being able to test and interact with one's APIs from one's UI tests offers several advantages including\"), mdx(\"ul\", null, mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Better organization of one's tests, everything in one place\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Setup of pre/post conditions (state) for the UI tests through the API\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Assert results of UI interaction more efficiently at the API layer\")), mdx(\"p\", null, \"So, one can write test cases where it makes sense to, as pure API tests, or mix in API checks and calls within one's selenium tests.\"), mdx(\"p\", null, mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://github.com/nightwatchjs/nightwatch/releases/tag/v2.5.2\"\n  }, \"Nightwatch 2.5.2\"), \" integrated SuperTest into its selenium test framework allowing one to use SuperTest in their existing test suites. This tutorial will explore how Nightwatch and SuperTest work in practice when compared to other frameworks that offer similar capabilities, like Playwright.\"), mdx(\"h2\", {\n    \"id\": \"initial-thoughts\"\n  }, \"Initial Thoughts\"), mdx(\"p\", null, \"I've used \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://www.npmjs.com/package/supertest\"\n  }, \"SuperTest\"), \" off and on for years so I was happy to learn Nightwatch integrated it into its framework to add API testing. It's easy to use and has a nice fluent assertion style.\"), mdx(\"p\", null, \"I don't like the fact it needs to be imported as a plugin (\", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://github.com/nightwatchjs/nightwatch-plugin-apitesting\"\n  }, \"@nightwatch/apitesting\"), \"). I feel this should be a more core feature and included as default. In addition, when working with TypeScript, the Nightwatch plugin system and TypeScript types are still having growing pains.\"), mdx(\"p\", null, \"Importing the plugin and adding custom types is not difficult. This article will go over both, but it's something that just works out of the box with competing frameworks like Playwright. Still, this is a nice addition of functionality for quality engineers with an existing Nightwatch test suite and I'm happy to see continuous innovation being made.\"), mdx(\"p\", null, \"So, let's get started API testing.\"), mdx(\"h2\", {\n    \"id\": \"api-testing-in-nightwatch-selenium-projects\"\n  }, \"API Testing in Nightwatch Selenium Projects\"), mdx(\"p\", null, \"This tutorial will go through creating a Nightwatch TypeScript selenium test project, import the API Testing plugin, add type definitions for TypeScript support, and write example tests against some \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"../best-websites-for-practicing-test-automation/\"\n  }, \"practice REST APIs\"), \".\"), mdx(\"h3\", {\n    \"id\": \"initial-installation\"\n  }, \"Initial Installation\"), mdx(\"ol\", null, mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"From a command line run \", mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"npm init nightwatch@latest\")), mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"During the guided setup select end-to-end testing and TypeScript\"), mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Continue through accepting defaults for the remaining prompts.\"), mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Add the API testing plugin by running \", mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"npm install @nightwatch/apitesting @types/supertest --save-dev\"))), mdx(\"p\", null, \"This will create a default working Nightwatch TypeScript test project with sample tests under \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"/nightwatch/\")), mdx(\"p\", null, \"They can be executed using \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"npx nightwatch\")), mdx(\"h3\", {\n    \"id\": \"configuring-the-test-project\"\n  }, \"Configuring the Test Project\"), mdx(\"p\", null, \"Now that there is a working test project we can \", mdx(\"del\", {\n    parentName: \"p\"\n  }, \"add custom TypeScript types for API testing\"), \"(edit 1/24/22 now included in \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://github.com/nightwatchjs/nightwatch-plugin-apitesting/blob/main/nightwatch/types/index.d.ts\"\n  }, \"v0.2.2\"), \"), remove the sample tests, and create our own API tests.\"), mdx(\"ol\", null, mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Remove the sample tests out of \", mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"./nightwatch/\"), \", but leave \", mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"tsconfig.json\")), mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Create a folder \", mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"./nightwatch/tests\"), \" \"), mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Edit \", mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"./nightwatch.conf.js\")), mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Change \", mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"src_folders\"), \" to \", mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"src_folders: ['nightwatch/tests'],\")), mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Change \", mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"plugins\"), \" to \", mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"plugins: ['@nightwatch/apitest'],\")), mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Optionally, to prevent a browser from launching, useful in a pure API test suite, one can modify the start_session and start_process values to false.\")), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-js\"\n  }, \"// nightwatch.conf.js\\n// ...\\n  test_settings: {\\n    default: {\\n      // ...\\n      start_session: false,\\n      webdriver: {\\n        start_process: false,\\n        server_path: ''\\n      },\\n      \\n    },\\n  }\\n\")), mdx(\"p\", null, \"You can checkout the example solution with this preconfigured from my GitHub under \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://github.com/reallymello/nightwatchTutorials/tree/master/apiTesting\"\n  }, \"nightwatchTutorials/apiTesting\"), \" to follow along. \", mdx(\"em\", {\n    parentName: \"p\"\n  }, \"I'll try to keep this repository updated as updates occur to the API testing plugin over time.\")), mdx(\"h3\", {\n    \"id\": \"writing-your-first-api-test\"\n  }, \"Writing your first API Test\"), mdx(\"p\", null, \"The example test solution has example tests written against the Swagger PetStore and Restful Booker test playground APIs (see more on \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"../best-websites-for-practicing-test-automation/\"\n  }, \"Best Websites to Practice Test Automation\"), \").\"), mdx(\"p\", null, \"First, let's create a test against \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://petstore.swagger.io/#/store/getInventory\"\n  }, \"/store/inventory\"), \".\"), mdx(\"ol\", null, mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Create a file called petStore.ts under \", mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"./nightwatch/tests/\"))), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-js\"\n  }, \"// petStore.ts\\nimport { NightwatchBrowser, NightwatchTests } from \\\"nightwatch\\\";\\n// This line will add the api testing command types to the Nightwatch API\\nimport '@nightwatch/apitesting';\\n\\nconst petStoreTests: NightwatchTests = {\\n};\\n\\nexport default petStoreTests;\\n\")), mdx(\"p\", null, \"This will be the shell of your tests against the pet store API. It brings in the TypeScript types applicable for Nightwatch API tests. Now, let's create the test.\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-js\"\n  }, \"import { NightwatchBrowser, NightwatchTests } from \\\"nightwatch\\\";\\nimport '@nightwatch/apitesting';\\n\\nconst petStoreTests: NightwatchTests = {\\n    // You pass supertest into the test through the object. The test must be async/awaited\\n    'can GET count of sold inventory': async ({ supertest }: NightwatchBrowser) => {\\n      await supertest\\n        // Request can take a a baseUrl for a remote API or the entry point of a REST API, like Express()\\n        .request('https://petstore.swagger.io/v2')\\n        // After request, the syntax exactly matches supertest and chai\\n        .get('/store/inventory/')\\n        .expect(200)\\n        .expect('Content-Type', /json/)\\n        .then((response) => {\\n          expect(response.body.sold).to.be.greaterThan(0);\\n        });\\n    },\\n  };\\n  \\nexport default petStoreTests;\\n\")), mdx(\"p\", null, \"For those used to supertest this is very familiar. The one oddity I found was the await returns an \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://github.com/nightwatchjs/nightwatch-plugin-apitesting/issues/2\"\n  }, \"@nightwatch_element response\"), \" instead of the supertest response which requires one to do assertions against the response in the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"then\"), \" block.\"), mdx(\"p\", null, \"POST requests are very similar and one can easily put a JSON object inside the .send method as shown.\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-js\"\n  }, \"    'can POST a pet to the store': async ({ supertest }: NightwatchBrowser) => {\\n      await supertest\\n        .request('https://petstore.swagger.io/v2')\\n        .post('/store/order')\\n        .send({\\n          id: 0,\\n          petId: 31337,\\n          quantity: 1,\\n          shipDate: '2022-12-30T14:55:04.147Z',\\n          status: 'placed',\\n          complete: true,\\n        })\\n        .expect(200)\\n        .expect('Content-Type', /json/)\\n        .then((response) => {\\n          expect(response.body.id).to.be.greaterThan(0);\\n          expect(response.body.quantity).to.equal(1);\\n          expect(response.body.status).to.equal('placed');\\n        });\\n    },\\n\")), mdx(\"h3\", {\n    \"id\": \"more-advanced-api-testing\"\n  }, \"More Advanced API Testing\"), mdx(\"p\", null, \"The Restful Booker has a more realistic stateful database (that gets wiped every 10 minutes) so it is better to construct scenario-based API tests against. I have examples under \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://github.com/reallymello/nightwatchTutorials/blob/master/apiTesting/nightwatch/tests/restfulBooker.ts\"\n  }, \"/apiTesting/nightwatch/tests/restfulBooker.ts\"), \" that illustrate that including partial updates using the PATCH verb.\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-js\"\n  }, \"  'Can partially update booking': async ({ supertest }: NightwatchBrowser) => {\\n    await supertest\\n      .request(baseUrl)\\n      .patch(`/booking/${bookingId}`)\\n      .set('Content-type', 'application/json')\\n      .set('Cookie', `token=${authToken.token}`)\\n      .accept('application/json')\\n      .send({\\n        totalprice: 200,\\n        depositpaid: false,\\n        bookingdates: {\\n          checkin: '2023-03-31',\\n          checkout: '2023-04-15',\\n        },\\n        additionalneeds: 'an even-numbered floor',\\n      })\\n      .then((response: any) => {\\n        expect(response.body).to.have.property('depositpaid', false);\\n        expect(response.body).to.have.property(\\n          'additionalneeds',\\n          'an even-numbered floor'\\n        );\\n        expect(response.body).to.have.property('totalprice', 200);\\n        expect(response.body.bookingdates).to.have.property(\\n          'checkin',\\n          '2023-03-31'\\n        );\\n        expect(response.body.bookingdates).to.have.property(\\n          'checkout',\\n          '2023-04-15'\\n        );\\n      });\\n  },\\n\")), mdx(\"p\", null, \"Another oddity, I found (as of writing) \", mdx(\"del\", {\n    parentName: \"p\"\n  }, \"was no documented way of using supertest in the before pretest hook to do something like grab and store an authentication token to use in the tests later. I ended up importing superagent to do that in this test suite which worked out, but didn't seem polished.\")), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-js\"\n  }, \"import { NightwatchBrowser, NightwatchTests } from 'nightwatch';\\nimport superagent from 'superagent';\\n//...\\nlet authToken: token;\\n\\nconst bookerTests: NightwatchTests = {\\n  before: async () => {\\n    authToken = (\\n      await superagent.post(`${baseUrl}/auth`).send({\\n        username: 'admin',\\n        password: 'password123',\\n      })\\n    ).body;\\n  },\\n//...\\n\")), mdx(\"p\", null, mdx(\"em\", {\n    parentName: \"p\"\n  }, \"1/13/23 Update\"), \" - I learned you can use it in the before test hook off of the Nightwatch API object (aka browser or client). So one could rewrite the above example removing the need to import superagent using the style below.\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-ts\"\n  }, \"const bookerTests: NightwatchTests = {\\n  before: async (client: NightwatchBrowser) => {\\n    await client.supertest\\n      .request(baseUrl)\\n      .post('/auth')\\n      .send({\\n        username: 'admin',\\n        password: 'password123',\\n      })\\n      .expect(200)\\n      .expect('Content-type', 'application/json; charset=utf-8')\\n      .then((response: any) => {\\n        authToken = response.body;\\n      });\\n  },\\n\")), mdx(\"p\", null, \"Last, you can load up your API locally into supertest by passing it the entry point of your Express() application (or NestJS) which is useful for local testing. An example of that can be found under, \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://github.com/reallymello/nightwatchTutorials/blob/master/apiTesting/nightwatch/tests/restfulBookerLocal.ts\"\n  }, \"apiTesting/nightwatch/tests/restfulBookerLocal.ts\"), \".\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-js\"\n  }, \"const server = require('../../restfulBooker/restful-booker/app');\\n// ...\\n'Can GET ping': async ({ supertest }: NightwatchBrowser) => {\\n    await supertest.request(server).get('/ping').expect(201);\\n},\\n\")), mdx(\"h3\", {\n    \"id\": \"executing-the-tests\"\n  }, \"Executing the tests\"), mdx(\"p\", null, \"To run the tests, you can run all or specific tests using the standard Nightwatch command line syntax.\"), mdx(\"ul\", null, mdx(\"li\", {\n    parentName: \"ul\"\n  }, mdx(\"p\", {\n    parentName: \"li\"\n  }, mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"npx nightwatch\"), \" \")), mdx(\"li\", {\n    parentName: \"ul\"\n  }, mdx(\"p\", {\n    parentName: \"li\"\n  }, mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"npx nightwatch --test testPathHere\"), \" \"))), mdx(\"p\", null, \"You get the same nice test reporting that was added in Nightwatch 2.0.\"), mdx(\"span\", {\n    \"className\": \"gatsby-resp-image-wrapper\",\n    \"style\": {\n      \"position\": \"relative\",\n      \"display\": \"block\",\n      \"marginLeft\": \"auto\",\n      \"marginRight\": \"auto\",\n      \"maxWidth\": \"820px\"\n    }\n  }, \"\\n      \", mdx(\"span\", {\n    parentName: \"span\",\n    \"className\": \"gatsby-resp-image-background-image\",\n    \"style\": {\n      \"paddingBottom\": \"143.41463414634146%\",\n      \"position\": \"relative\",\n      \"bottom\": \"0\",\n      \"left\": \"0\",\n      \"backgroundImage\": \"url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAdCAYAAACqhkzFAAAACXBIWXMAABJ0AAASdAHeZh94AAAEV0lEQVRIx41V13bbWBDTa5J1EUVSJMXeRRWrV9uyvdlNTv7/f7CYkSXXnM0DDovEuXMBDG7L8hO4YQo3yuCnJXpJofdyFXTDCG6cIshqGE6AthPCCPMjghSGn+p9m9dLy0NLXwQZOvKHXoqOn+HK9HDZcQgXV4Rc/zK6uOA7hdx/Bv7W8sICcdGgHQUwohDtmF1EPq47nhaT6zUXuNCijuJcWMEFpQHTxQXR6vgh6psZsv4ISdWgGk/1OalH6I9nSOshLD+G1YtJRa5wuFWbVAnk/bXd0+JStNVNUzz9/IXl9hbr/QF3T9+xPzxhxfvV/h41F5gsNxjPVxhMFyiaMdJqAIe8C+xehCvLeylosMMh/1wNb5Czq2o4OWIwQZT3tYsuO/LYmeH4+Na2lc9v7SNO3OqWhUOrF8AsuErcQ68sIR37VYUO+fxq2vh6/Vzg+UPpRj7+HVrdICFfQ9gJ+cgSOHkGJ8tg59xSnsPyThwdO3gR5SSGd+5OOzTjCNvDI2bLLdbbO6x2B6x398SBvN4pfzkFq0dT3CzWGJGeyWLD5wlMN1R0CPGodN+Szm4fv+uH8+0Oi90ttvePWFAkxe4Ow8lCXSCqN5M5+nRBORjrIMgORZwuub62PbFNgGo2VeVEiGY8x2i61Oeo6NNKA1U2J+ReCkd5rb+1uz3l94QXUfIYF6EHt8hhpjHsMOG0uPhyZRIWhbHOHL0uIPggis12i+aGCnNuC85zv0JY1PDiQhGklRr5yuqpOO2urxDOjG6g7wVn2wiHD//+xHy1oyB3uH34Gw///MBss8NsvVdzyzvleLPH+vag9zG3LGKcPHguaHDL/elcecn7Y+WrIE8hO5O0CbKK0zJTlWVqRBThV7p8bejzpNicFLOMj8auuO2MU8FrJ6axDVsn4ws5VCinpt6LH0/F3nRoU3ZRzk5oZIqioLm7hPwmw+8w3rr+MTMlHI4W8c+8vuHQimPcf/+hVpFwEB5XNHQ9nOr2m5s59uRwx8DYPzzhQH6n5ND0ws+3bMUJJ+UJQybJZLXBaLbChsSLefvPnIkI9YhmZoBI4rhhdrbMhy2bjPjpZstOZgr5WEZuwEIDTshwtuRiS83FvDmmkYiTSU56n6jc8TzYNaM/ifQj8WBcNuQu0i5kPmVe5fkIGt9/CdXX0C07bD+rRlqwndKsjDKD49hxjkOvoImPHbjn1JGUkcVOePGhR7v0S/h5iYIBm7N4Q74SnjPClZx2Pv1oPBd9W8T7EGH0IY/JMsdl1NM8lE6tnLbgQXVhcXZfpfL/4XmWE1ScgqCsNKmjpkEyGCAeDhBweuREPI2ZdCXcvccbH7YdbiPh0AcBQo6ZnB0hC1keRem8TZM/6tCNc/ptDqfMYGacippHZb+AM2Da1KUuIAfVexHe40UUt6fF3CRHVg6Q5A3KPuOMhfQspjAmuz126f0Gr9OGW77MPBXFpCAGQ0GEkSKy8unY/FNR/gNWbg4iyjIsVwAAAABJRU5ErkJggg==')\",\n      \"backgroundSize\": \"cover\",\n      \"display\": \"block\"\n    }\n  }), \"\\n  \", mdx(\"img\", {\n    parentName: \"span\",\n    \"className\": \"gatsby-resp-image-image\",\n    \"alt\": \"Nightwatch API Test Output\",\n    \"title\": \"Nightwatch API Test Output\",\n    \"src\": \"/static/5d3542ddd14b06f843cf84dd6f9f5265/083f8/apiTestOutput.png\",\n    \"srcSet\": [\"/static/5d3542ddd14b06f843cf84dd6f9f5265/ad4a5/apiTestOutput.png 205w\", \"/static/5d3542ddd14b06f843cf84dd6f9f5265/74ab4/apiTestOutput.png 410w\", \"/static/5d3542ddd14b06f843cf84dd6f9f5265/083f8/apiTestOutput.png 820w\", \"/static/5d3542ddd14b06f843cf84dd6f9f5265/42ccd/apiTestOutput.png 897w\"],\n    \"sizes\": \"(max-width: 820px) 100vw, 820px\",\n    \"style\": {\n      \"width\": \"100%\",\n      \"height\": \"100%\",\n      \"margin\": \"0\",\n      \"verticalAlign\": \"middle\",\n      \"position\": \"absolute\",\n      \"top\": \"0\",\n      \"left\": \"0\"\n    },\n    \"loading\": \"lazy\"\n  }), \"\\n    \"), mdx(\"p\", null, \"The results also present nicely in the improved Nightwatch HTML test report.\"), mdx(\"span\", {\n    \"className\": \"gatsby-resp-image-wrapper\",\n    \"style\": {\n      \"position\": \"relative\",\n      \"display\": \"block\",\n      \"marginLeft\": \"auto\",\n      \"marginRight\": \"auto\",\n      \"maxWidth\": \"820px\"\n    }\n  }, \"\\n      \", mdx(\"span\", {\n    parentName: \"span\",\n    \"className\": \"gatsby-resp-image-background-image\",\n    \"style\": {\n      \"paddingBottom\": \"73.65853658536585%\",\n      \"position\": \"relative\",\n      \"bottom\": \"0\",\n      \"left\": \"0\",\n      \"backgroundImage\": \"url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAPCAYAAADkmO9VAAAACXBIWXMAABJ0AAASdAHeZh94AAACM0lEQVQ4y4WTW2/aQBCF/WO4CKRGPPDAQxGR+tCX/naktAEacWlF1FDsZX1b27tem5OZBVMSNcLSJ9uz3tmZc8bet69fMBwOMRgM0O120el00G633b3VauHu0x0m9/eYTCbo9Xoudr3e7/cxHo+JzxiNRvCm0ym+PzxgsVhgNpthPp9jtV5hvV5f2Gw2/7iKO1YrLJdLPM6e8OPxJzxbVcgLDWMMPrp4rSxLx63L20uF5VYiCEPsZQARS4gkxN+DjyiNofMMYrcjXhAGPrIoQpmpC5owKnXPJs/hxcrQ5gyFNiiKAjkFmSzLoLmyIof48wzx8oxECOR0sIljmOREkSbQcQRNhxva49V1hbquUVHrTF3V7v1NrHknrIudnnnNlhbGWpS6IEkMvGbD8Xh0WNIppxYYQ9VZ/vAKe3Xn9Tg8IIkksjhETlV6ObdGrRqjXUKlEmx/rfB7+YTteolECtIocS29pyBUdKBkEnkSObxA+BAiQHl2UucKcbBziaJDQEJnsHQYV2TNezSqM/aMxwaoswE1VVhqMoXKt6RHdZaiufiR397gpDqtubHJ0hTC9yHJQZUkrlJDlbLL7HrjvCXhcUnwf9gPLyPrD/s9zVhAmqSobImYYkqpk4uUiLk27iO0ppYLciqkYY2pOt5YVZb+nNMs8gfNXPLfcish7/cymnBJwyqlJEI3SwklFyRBSPGAKvdJEo7dSsiH0tgocJVOO6qIW+ZW2XE+sfmHbyVjWJZX7vlb0NWS/5MAAAAASUVORK5CYII=')\",\n      \"backgroundSize\": \"cover\",\n      \"display\": \"block\"\n    }\n  }), \"\\n  \", mdx(\"img\", {\n    parentName: \"span\",\n    \"className\": \"gatsby-resp-image-image\",\n    \"alt\": \"Nightwatch HTML Test Report\",\n    \"title\": \"Nightwatch HTML Test Report\",\n    \"src\": \"/static/cfe787d438b85b4510b2e5360760499f/083f8/htmlTestReport.png\",\n    \"srcSet\": [\"/static/cfe787d438b85b4510b2e5360760499f/ad4a5/htmlTestReport.png 205w\", \"/static/cfe787d438b85b4510b2e5360760499f/74ab4/htmlTestReport.png 410w\", \"/static/cfe787d438b85b4510b2e5360760499f/083f8/htmlTestReport.png 820w\", \"/static/cfe787d438b85b4510b2e5360760499f/8ffbc/htmlTestReport.png 1128w\"],\n    \"sizes\": \"(max-width: 820px) 100vw, 820px\",\n    \"style\": {\n      \"width\": \"100%\",\n      \"height\": \"100%\",\n      \"margin\": \"0\",\n      \"verticalAlign\": \"middle\",\n      \"position\": \"absolute\",\n      \"top\": \"0\",\n      \"left\": \"0\"\n    },\n    \"loading\": \"lazy\"\n  }), \"\\n    \"), mdx(\"h2\", {\n    \"id\": \"final-thoughts\"\n  }, \"Final Thoughts\"), mdx(\"p\", null, \"Nightwatch has been rapidly releasing interesting new features throughout 2022 and now into 2023 such as visual regression testing, component testing, enhancements to reporting and debugging, and improved mobile testing. Their implementation of API Testing is one of the newer additions and I think it shows promise, but it feels like it needs more polish compared to other popular frameworks like Playwright.\"), mdx(\"p\", null, \"Playwright's API testing\"), mdx(\"ul\", null, mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Is included by default (not a plugin)\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, mdx(\"del\", {\n    parentName: \"li\"\n  }, \"Has included TypeScript types\"), \" \", mdx(\"em\", {\n    parentName: \"li\"\n  }, \"now included in the Nightwatch plugin as well\")), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Has better documentation\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Has a handy \", mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"test.use\"), \" that let's you default tedious settings like baseUrl, headers, and authorization\")), mdx(\"p\", null, \"What Nightwatch does better\"), mdx(\"ul\", null, mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Better assertion style\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Nicer test output/HTML report\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Perhaps better support for local testing/mocking API requests\")), mdx(\"p\", null, \"Overall, the API testing capability added to Nightwatch is a useful addition and will hopefully improve in coming iterations.\"), mdx(\"p\", null, \"For further learning consider checking out some API testing books\"), mdx(\"ul\", null, mdx(\"li\", {\n    parentName: \"ul\"\n  }, mdx(\"a\", {\n    parentName: \"li\",\n    \"href\": \"https://amzn.to/3VX5xar\"\n  }, \"Testing Web APIs\")), mdx(\"li\", {\n    parentName: \"ul\"\n  }, mdx(\"a\", {\n    parentName: \"li\",\n    \"href\": \"https://amzn.to/3ipaxXJ\"\n  }, \"Learn API Testing\"))), mdx(\"p\", null, mdx(\"em\", {\n    parentName: \"p\"\n  }, \"As an Amazon Associate I earn from qualifying purchases made through links on this site (which helps support it, thank you!)\")));\n}\n;\nMDXContent.isMDXComponent = true;","excerpt":"When the software being tested exposes REST API endpoints it can be more efficient to test the APIs directly instead of going through the UI…","fields":{"slug":"/api-testing-with-nightwatch-supertest/","type":"posts"},"headings":[{"value":"Testing REST APIs from your Selenium Test Projects","depth":2},{"value":"Initial Thoughts","depth":2},{"value":"API Testing in Nightwatch Selenium Projects","depth":2},{"value":"Initial Installation","depth":3},{"value":"Configuring the Test Project","depth":3},{"value":"Writing your first API Test","depth":3},{"value":"More Advanced API Testing","depth":3},{"value":"Executing the tests","depth":3},{"value":"Final Thoughts","depth":2}],"frontmatter":{"title":"Add API Testing to your Nightwatch Test Automation","description":"Learn how to add API testing to your existing selenium automated tests using the API testing plugin for Nightwatch.js","link":null,"date":"2023-01-13T00:00:00.000Z","tags":["quality assurance","software testing","nightwatchjs","selenium","post'"],"draft":null,"hide":null,"cover":{"childImageSharp":{"fluid":{"base64":"data:image/jpeg;base64,/9j/2wBDAAMCAgMCAgMDAwMEAwMEBQgFBQQEBQoHBwYIDAoMDAsKCwsNDhIQDQ4RDgsLEBYQERMUFRUVDA8XGBYUGBIUFRT/2wBDAQMEBAUEBQkFBQkUDQsNFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBT/wgARCAAMABQDASIAAhEBAxEB/8QAGAAAAwEBAAAAAAAAAAAAAAAAAAYHAgX/xAAVAQEBAAAAAAAAAAAAAAAAAAACA//aAAwDAQACEAMQAAABldIQcxXWFkof/8QAGxAAAgMAAwAAAAAAAAAAAAAAAwQBAgUGExX/2gAIAQEAAQUCwePtki+Z1w+lWh19xjxAaRxQZ0xr/wD/xAAXEQADAQAAAAAAAAAAAAAAAAABAhEQ/9oACAEDAQE/AWEmf//EABcRAAMBAAAAAAAAAAAAAAAAAAACIQH/2gAIAQIBAT8BrCzKf//EACMQAAEDAgUFAAAAAAAAAAAAAAEAAhEDEgQUISIxBRNCgqH/2gAIAQEABj8CGKqMGSc4tkkTzCAFKmfQKNo08BCHToZl7u9xrdC2u+K5ztV//8QAGxAAAgMAAwAAAAAAAAAAAAAAAREAITFhkbH/2gAIAQEAAT8hR9YslFbss6gtjGlipPqChBAAVfI8haQh6wPsvbAKgp//2gAMAwEAAgADAAAAEFzv/8QAGBEAAgMAAAAAAAAAAAAAAAAAARARMUH/2gAIAQMBAT8QrtEr/8QAGhEBAAMAAwAAAAAAAAAAAAAAAQARITHB8P/aAAgBAgEBPxBBarGt91Aicp//xAAbEAEAAgMBAQAAAAAAAAAAAAABESEAMWGBUf/aAAgBAQABPxB5rfOKBsJqRsZ1eGikJFYItS2Ln3L943V7gX3FNNyTAV7IjmqxGXSyxwOMoUFEwaoD7n//2Q==","aspectRatio":1.6842105263157894,"src":"/static/c1591e1a57d42702a2a7d9e25086290c/60a5f/nightwatch-api-testing.jpg","srcSet":"/static/c1591e1a57d42702a2a7d9e25086290c/3e5eb/nightwatch-api-testing.jpg 192w,\n/static/c1591e1a57d42702a2a7d9e25086290c/2880b/nightwatch-api-testing.jpg 384w,\n/static/c1591e1a57d42702a2a7d9e25086290c/60a5f/nightwatch-api-testing.jpg 768w","srcWebp":"/static/c1591e1a57d42702a2a7d9e25086290c/25278/nightwatch-api-testing.webp","srcSetWebp":"/static/c1591e1a57d42702a2a7d9e25086290c/a278a/nightwatch-api-testing.webp 192w,\n/static/c1591e1a57d42702a2a7d9e25086290c/2474b/nightwatch-api-testing.webp 384w,\n/static/c1591e1a57d42702a2a7d9e25086290c/25278/nightwatch-api-testing.webp 768w","sizes":"(max-width: 768px) 100vw, 768px","presentationWidth":768,"presentationHeight":455}}}}},{"id":"80fb422a-de82-524a-8d14-d9245d090e55","body":"var _excluded = [\"components\"];\nfunction _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }\nfunction _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }\nfunction _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }\n/* @jsxRuntime classic */\n/* @jsx mdx */\n\nvar _frontmatter = {\n  \"title\": \"Testing Software using Decision Tables\",\n  \"cover\": \"./decision-tables-banner.jpg\",\n  \"date\": \"2022-10-30T00:00:00.000Z\",\n  \"description\": \"Decision tables are software testing tools used to ensure one fully captures the expected behaviors of features involving complex business rules. Once created, decision tables provide valuable documentation of the software's desired behavior that one can directly use to create their test cases with. Learn how to create and use decision tables for testing your next Agile story in this article.\",\n  \"tags\": [\"quality assurance\", \"software testing\", \"post\"]\n};\nvar makeShortcode = function makeShortcode(name) {\n  return function MDXDefaultShortcode(props) {\n    console.warn(\"Component \" + name + \" was not imported, exported, or provided by MDXProvider as global scope\");\n    return mdx(\"div\", props);\n  };\n};\nvar Embed = makeShortcode(\"Embed\");\nvar layoutProps = {\n  _frontmatter: _frontmatter\n};\nvar MDXLayout = \"wrapper\";\nreturn function MDXContent(_ref) {\n  var components = _ref.components,\n    props = _objectWithoutProperties(_ref, _excluded);\n  return mdx(MDXLayout, _extends({}, layoutProps, props, {\n    components: components,\n    mdxType: \"MDXLayout\"\n  }), mdx(\"p\", null, \"Decision tables excel at capturing use cases for software features involving complex business logic. When taking a \", mdx(\"em\", {\n    parentName: \"p\"\n  }, \"shift left\"), \" approach decision tables can be used to test the business requirements by making sure they are complete and make sense when combined. In addition, adding the decision table to the requirements creates clearer documentation about how the feature should work, before code is written, allows the engineers to write less expensive unit and integration tests while developing the software, and removes ambiguity around how a new feature should work.\"), mdx(\"p\", null, \"Watch the lesson video below or continue reading the full article below it to learn more about testing using decision tables.\"), mdx(Embed, {\n    src: \"https://www.youtube.com/embed/N8vgIgV3dQw\",\n    mdxType: \"Embed\"\n  }), mdx(\"h2\", {\n    \"id\": \"why-you-should-use-decision-tables\"\n  }, \"Why you should use Decision Tables\"), mdx(\"p\", null, \"Decision tables help foster communication and collaboration on agile teams because the process of creating one\"), mdx(\"ul\", null, mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Has the test engineer work with the business analyst to document all explicit and implied outcomes, decisions, in their requirements or spec (testing the requirements, shift left).\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Creates a blueprint for engineers to code against.\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Creates 1 to 1 test cases that are known before coding begins, preventing rework.\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Helps to prevent missed test cases or make informed decisions on what not to cover.\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Creates an artifact that can be used as documentation of how the feature should work in the future or serve as an oracle for test cases.\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Creates a visible list of how many tests are needed to cover a feature story, to track progress, share the work of automating the tests, or manually running them.\")), mdx(\"h2\", {\n    \"id\": \"when-to-use-decision-tables\"\n  }, \"When to use Decision Tables\"), mdx(\"p\", null, \"Some tools work better at different tasks than others. Software test engineers should keep decision tables in their software testing toolbox to use when it makes sense to do so.\"), mdx(\"blockquote\", null, mdx(\"p\", {\n    parentName: \"blockquote\"\n  }, \"So when does it make sense to use a decision table?\")), mdx(\"ul\", null, mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"When testing features with complex business logic or rules\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"When testing features where outputs are based on several input combinations\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"When testing features where combined inputs have many acceptable inputs that are not equivalence classes\")), mdx(\"p\", null, \"Some examples include testing tax filing logic, order bar for stock trading, and UI with multiple inputs and actions.\"), mdx(\"h2\", {\n    \"id\": \"when-not-to-use-decision-tables\"\n  }, \"When not to use Decision Tables\"), mdx(\"p\", null, \"Decision tables work best when testing applications that have inputs that can be mapped to discrete values and outcomes. A feature under test that doesn't map nicely in that way may not be a good fit. In addition, when you are testing a simple feature the overhead added by creating a decision table may not provide as much of a benefit versus the time spent creating it.\"), mdx(\"h2\", {\n    \"id\": \"how-to-create-decision-tables\"\n  }, \"How to Create Decision Tables\"), mdx(\"p\", null, \"Typically one would use a spreadsheet program like Excel or Google Sheets to build the decision table, but anything that can list data in table form would work.\"), mdx(\"p\", null, \"At a high level, one would start by determining all the conditions and their input values. Those are then mapped out into a matrix that covers all the possible combinations. Below the matrix of inputs would be the expected behavior that should occur for each \", mdx(\"em\", {\n    parentName: \"p\"\n  }, \"column\"), \" of values.\"), mdx(\"p\", null, \"Let's look at a generic and then specific example in this section to make that more clear.\"), mdx(\"p\", null, \"First, in the generic example, let's imagine a program we are testing that has 3 settings. The first two have can be on or off (true or false) and they default to true. The third option does not have a default setting so it can be null, but the user can select on or off (true or false).\"), mdx(\"p\", null, \"In the spreadsheet list the settings under a Conditions column and the values the conditions can take in an adjacent Values column as shown below.\"), mdx(\"table\", null, mdx(\"thead\", {\n    parentName: \"table\"\n  }, mdx(\"tr\", {\n    parentName: \"thead\"\n  }, mdx(\"th\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"Conditions\"), mdx(\"th\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"Values\"))), mdx(\"tbody\", {\n    parentName: \"table\"\n  }, mdx(\"tr\", {\n    parentName: \"tbody\"\n  }, mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"Setting 1\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"T, F\")), mdx(\"tr\", {\n    parentName: \"tbody\"\n  }, mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"Setting 2\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"T, F\")), mdx(\"tr\", {\n    parentName: \"tbody\"\n  }, mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"Setting 3\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"T, F, Null\")))), mdx(\"p\", null, \"Next, we need to figure out all the combinations of inputs we'd need to exhaustively test the program. To do that you multiply the count of values each setting has together.\"), mdx(\"p\", null, \"(Setting 1 Value Count) \", \"*\", \" (Setting 2 Value Count) \", \"*\", \" (Setting 3 Value Count)\"), mdx(\"p\", null, \"In our example that would be\"), mdx(\"p\", null, \"2 \", \"*\", \" 2 \", \"*\", \" 3 = 12\"), mdx(\"p\", null, \"So we would need 12 tests to exhaustively test this application. Now that we know the combinatorial test count we need to fill out the rest of the decision table with the test data values. We will create a column to the right of the Values column that will hold our values for each test.\"), mdx(\"p\", null, \"To fill in the values for our first condition, Setting 1, we will repeat the value for N number of columns based on the total test count (12) divided by the number of values (2). 12 divided by 2 is 6 so we will repeat T (true) for 6 columns and F (false) for the next 6.\"), mdx(\"table\", null, mdx(\"thead\", {\n    parentName: \"table\"\n  }, mdx(\"tr\", {\n    parentName: \"thead\"\n  }, mdx(\"th\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"Conditions\"), mdx(\"th\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"1\"), mdx(\"th\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"2\"), mdx(\"th\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"3\"), mdx(\"th\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"4\"), mdx(\"th\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"5\"), mdx(\"th\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"6\"), mdx(\"th\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"7\"), mdx(\"th\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"8\"), mdx(\"th\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"9\"), mdx(\"th\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"10\"), mdx(\"th\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"11\"), mdx(\"th\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"12\"))), mdx(\"tbody\", {\n    parentName: \"table\"\n  }, mdx(\"tr\", {\n    parentName: \"tbody\"\n  }, mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"Setting 1\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"T\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"T\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"T\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"T\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"T\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"T\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"F\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"F\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"F\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"F\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"F\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"F\")))), mdx(\"p\", null, \"For the next row, we divide the 6 we got in the previous step by the number of values in Setting 2, 2. 6 divided by 2 is 3 so we will repeat the values in next row every 3 columns.\"), mdx(\"table\", null, mdx(\"thead\", {\n    parentName: \"table\"\n  }, mdx(\"tr\", {\n    parentName: \"thead\"\n  }, mdx(\"th\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"Conditions\"), mdx(\"th\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"1\"), mdx(\"th\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"2\"), mdx(\"th\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"3\"), mdx(\"th\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"4\"), mdx(\"th\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"5\"), mdx(\"th\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"6\"), mdx(\"th\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"7\"), mdx(\"th\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"8\"), mdx(\"th\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"9\"), mdx(\"th\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"10\"), mdx(\"th\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"11\"), mdx(\"th\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"12\"))), mdx(\"tbody\", {\n    parentName: \"table\"\n  }, mdx(\"tr\", {\n    parentName: \"tbody\"\n  }, mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"Setting 1\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"T\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"T\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"T\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"T\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"T\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"T\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"F\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"F\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"F\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"F\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"F\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"F\")), mdx(\"tr\", {\n    parentName: \"tbody\"\n  }, mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"Setting 2\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"T\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"T\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"T\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"F\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"F\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"F\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"T\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"T\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"T\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"F\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"F\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"F\")))), mdx(\"p\", null, \"For the last setting we divide the 3 from the previous step by the number of values Setting 3 has, 3. 3 divided by 3 equals 1 so we will alternate T, F, or N (for null) in each column.\"), mdx(\"table\", null, mdx(\"thead\", {\n    parentName: \"table\"\n  }, mdx(\"tr\", {\n    parentName: \"thead\"\n  }, mdx(\"th\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"Conditions\"), mdx(\"th\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"1\"), mdx(\"th\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"2\"), mdx(\"th\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"3\"), mdx(\"th\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"4\"), mdx(\"th\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"5\"), mdx(\"th\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"6\"), mdx(\"th\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"7\"), mdx(\"th\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"8\"), mdx(\"th\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"9\"), mdx(\"th\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"10\"), mdx(\"th\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"11\"), mdx(\"th\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"12\"))), mdx(\"tbody\", {\n    parentName: \"table\"\n  }, mdx(\"tr\", {\n    parentName: \"tbody\"\n  }, mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"Setting 1\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"T\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"T\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"T\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"T\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"T\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"T\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"F\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"F\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"F\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"F\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"F\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"F\")), mdx(\"tr\", {\n    parentName: \"tbody\"\n  }, mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"Setting 2\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"T\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"T\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"T\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"F\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"F\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"F\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"T\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"T\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"T\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"F\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"F\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"F\")), mdx(\"tr\", {\n    parentName: \"tbody\"\n  }, mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"Setting 3\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"T\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"F\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"N\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"T\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"F\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"N\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"T\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"F\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"N\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"T\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"F\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"N\")))), mdx(\"p\", null, \"Now, our decision table has a mapping of all the values for each one of our test cases. We can use this data to conduct our tests. For example, for our first test, test 1, we'd set each program setting to T, true. For test 11, we'd run the test with every setting set F, false.\"), mdx(\"p\", null, \"All we are missing now is the expected conditions for each test or what the program is \", mdx(\"em\", {\n    parentName: \"p\"\n  }, \"supposed\"), \" to do when run with these values. This exercise ends up testing the requirement which might uncover missing or conflicting requirements before the code is even written which makes decision tables a great tool to aid in shift left agile teams.\"), mdx(\"p\", null, \"We will capture the expected effects or actions in an Actions section below the setting values in the table.\"), mdx(\"table\", null, mdx(\"thead\", {\n    parentName: \"table\"\n  }, mdx(\"tr\", {\n    parentName: \"thead\"\n  }, mdx(\"th\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"Conditions\"), mdx(\"th\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"1\"), mdx(\"th\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"2\"), mdx(\"th\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"3\"), mdx(\"th\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"4\"), mdx(\"th\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"5\"), mdx(\"th\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"6\"), mdx(\"th\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"7\"), mdx(\"th\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"8\"), mdx(\"th\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"9\"), mdx(\"th\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"10\"), mdx(\"th\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"11\"), mdx(\"th\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"12\"))), mdx(\"tbody\", {\n    parentName: \"table\"\n  }, mdx(\"tr\", {\n    parentName: \"tbody\"\n  }, mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"Setting 1\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"T\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"T\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"T\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"T\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"T\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"T\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"F\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"F\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"F\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"F\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"F\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"F\")), mdx(\"tr\", {\n    parentName: \"tbody\"\n  }, mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"Setting 2\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"T\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"T\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"T\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"F\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"F\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"F\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"T\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"T\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"T\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"F\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"F\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"F\")), mdx(\"tr\", {\n    parentName: \"tbody\"\n  }, mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"Setting 3\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"T\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"F\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"N\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"T\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"F\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"N\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"T\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"F\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"N\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"T\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"F\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"N\")), mdx(\"tr\", {\n    parentName: \"tbody\"\n  }, mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, mdx(\"strong\", {\n    parentName: \"td\"\n  }, \"Effects\")), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  })), mdx(\"tr\", {\n    parentName: \"tbody\"\n  }, mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"Enable Foo\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"Y\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"N\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"N\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"Y\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"N\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"N\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"N\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"N\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"N\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"Y\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"N\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"Y\")), mdx(\"tr\", {\n    parentName: \"tbody\"\n  }, mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"Enable Bar\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"Y\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"Y\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"Y\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"N\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"Y\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"Y\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"Y\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"N\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"N\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"N\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"N\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"N\")))), mdx(\"p\", null, \"As a final step you can collapse some columns, exclude certain combinations, that may be not reachable by an end user in practice or where one of the settings overrides the other settings yielding equivalent results though this can be risky since it assumes certain things about the implementation. Doing so can cut down on the number of test cases required to run at a potential risk of missed code coverage.\"), mdx(\"p\", null, \"Here is an example of a real decision table testing payroll direct deposit functionality where the file group, test mode setting, and whether or not it is triggered on demand, control behavior of direct deposit file consolidation.\"), mdx(\"span\", {\n    \"className\": \"gatsby-resp-image-wrapper\",\n    \"style\": {\n      \"position\": \"relative\",\n      \"display\": \"block\",\n      \"marginLeft\": \"auto\",\n      \"marginRight\": \"auto\",\n      \"maxWidth\": \"820px\"\n    }\n  }, \"\\n      \", mdx(\"span\", {\n    parentName: \"span\",\n    \"className\": \"gatsby-resp-image-background-image\",\n    \"style\": {\n      \"paddingBottom\": \"21.951219512195124%\",\n      \"position\": \"relative\",\n      \"bottom\": \"0\",\n      \"left\": \"0\",\n      \"backgroundImage\": \"url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAECAYAAACOXx+WAAAACXBIWXMAAA7DAAAOwwHHb6hkAAABBklEQVQY0z3N3UvCcBTG8f3/UDdFpLuJLiLCXEmbmy8ZooRuc85cmy8r36ChoUj4FuW3n7vo4sPDec6BI10WNWTjnpOHFGdZhWQ+Q8LIIOsa53pWzHmSBeE2jXx9Q0JVuXgqIYvuVFM5VtKxo0PeKUjRuMU88tksB+y3H/A7Zb+bEo1CVssFu/Wa7WrFt+Xyky+zm32y3Wz4WsyJJhN6vk8/CP5J5ZaNZlZR61UeX2xKnkPh0FkmesMWGnHmsgYFJYNRq5Fzm+i2hWrWY4db1TTjlILOM95rBc+vMBw3mc58kR5mw6EtvoeDd7pvIUOjyPgqRcd18ftdemGfduBjOU7MFGyx+wOrcQ3aHr2URgAAAABJRU5ErkJggg==')\",\n      \"backgroundSize\": \"cover\",\n      \"display\": \"block\"\n    }\n  }), \"\\n  \", mdx(\"img\", {\n    parentName: \"span\",\n    \"className\": \"gatsby-resp-image-image\",\n    \"alt\": \"decision table for direct deposit testing\",\n    \"title\": \"decision table for direct deposit testing\",\n    \"src\": \"/static/085c65c48b27ee64d960e102efcb954e/083f8/decisionTable.png\",\n    \"srcSet\": [\"/static/085c65c48b27ee64d960e102efcb954e/ad4a5/decisionTable.png 205w\", \"/static/085c65c48b27ee64d960e102efcb954e/74ab4/decisionTable.png 410w\", \"/static/085c65c48b27ee64d960e102efcb954e/083f8/decisionTable.png 820w\", \"/static/085c65c48b27ee64d960e102efcb954e/2d16a/decisionTable.png 1083w\"],\n    \"sizes\": \"(max-width: 820px) 100vw, 820px\",\n    \"style\": {\n      \"width\": \"100%\",\n      \"height\": \"100%\",\n      \"margin\": \"0\",\n      \"verticalAlign\": \"middle\",\n      \"position\": \"absolute\",\n      \"top\": \"0\",\n      \"left\": \"0\"\n    },\n    \"loading\": \"lazy\"\n  }), \"\\n    \"), mdx(\"p\", null, \"Red highlighting was used to indicate a defect discovered in testing of the feature. In addition, columns 6-8 are an example of column collapsing. They were collapsed because if the file group is not a direct deposit file group it doesn't matter what the other settings are set to so a decision was made that testing it once using the test in column 5 would provide sufficient coverage.\"), mdx(\"h2\", {\n    \"id\": \"how-to-use-a-decision-table\"\n  }, \"How to use a Decision Table\"), mdx(\"p\", null, \"Now that we've learned how to construct a decision table in the previous section we can use the values from the numbered test case columns to construct the inputs and expected results for our tests.\"), mdx(\"p\", null, \"For example, a given/when/then formatted test case for test 5 would be\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\"\n  }, \"Given Setting 1 is T\\nAnd   Setting 2 is F\\nAnd   Setting 3 is F\\nWhen I run the program\\nThen Enable Foo will be set to N\\nAnd  Enable Bar will be set to Y\\n\")), mdx(\"p\", null, \"Decision tables nicely map well into parameterized row fixtures in unit test frameworks that support the concept like nUnit. This allows you to write the test case once and just vary the input values passed in.\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-c#\"\n  }, \"[Test]\\n[TestCase(true, true, true, 'Y', 'Y')]\\n[TestCase(true, true, false, 'N', 'Y')]\\n[TestCase(true, true, null, 'N', 'Y')]\\npublic void ExampleTest(bool setting1, bool setting2, bool? setting3, \\n  char fooExpectation, char barExpectation)\\n{\\n    MyApp.changeSettings(setting1, setting2, setting3);\\n\\n    Assert.AreEqual(fooExpectation, MyApp.getFoo());\\n    Assert.AreEqual(barExpectation, MyApp.getBar());\\n}\\n\")), mdx(\"h2\", {\n    \"id\": \"more-reading\"\n  }, \"More Reading\"), mdx(\"p\", null, \"Below are some techniques that cover this and other software testing techniques. As an Amazon affiliate I earn from qualifying purchases made from these links which help support this site.\"), mdx(\"ul\", null, mdx(\"li\", {\n    parentName: \"ul\"\n  }, mdx(\"p\", {\n    parentName: \"li\"\n  }, mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://amzn.to/3ffqn5T\"\n  }, \"Pragmatic Software Testing\"))), mdx(\"li\", {\n    parentName: \"ul\"\n  }, mdx(\"p\", {\n    parentName: \"li\"\n  }, mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://amzn.to/3NhWbDL\"\n  }, \"A Practitioner's Guide to Software Test Design\"))), mdx(\"li\", {\n    parentName: \"ul\"\n  }, mdx(\"p\", {\n    parentName: \"li\"\n  }, mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://www.guru99.com/decision-table-testing.html\"\n  }, \"More examples on guru99\"))), mdx(\"li\", {\n    parentName: \"ul\"\n  }, mdx(\"p\", {\n    parentName: \"li\"\n  }, mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://www.methodsandtools.com/archive/archive.php?id=39\"\n  }, \"Another article from methods and tools\")))), mdx(\"p\", null, \"If you liked this article please share this link with others and feel free to reach out using one of the social links below \\uD83E\\uDD17\"));\n}\n;\nMDXContent.isMDXComponent = true;","excerpt":"Decision tables excel at capturing use cases for software features involving complex business logic. When taking a  shift left  approach…","fields":{"slug":"/testing-software-with-decision-tables/","type":"posts"},"headings":[{"value":"Why you should use Decision Tables","depth":2},{"value":"When to use Decision Tables","depth":2},{"value":"When not to use Decision Tables","depth":2},{"value":"How to Create Decision Tables","depth":2},{"value":"How to use a Decision Table","depth":2},{"value":"More Reading","depth":2}],"frontmatter":{"title":"Testing Software using Decision Tables","description":"Decision tables are software testing tools used to ensure one fully captures the expected behaviors of features involving complex business rules. Once created, decision tables provide valuable documentation of the software's desired behavior that one can directly use to create their test cases with. Learn how to create and use decision tables for testing your next Agile story in this article.","link":null,"date":"2022-10-30T00:00:00.000Z","tags":["quality assurance","software testing","post"],"draft":null,"hide":null,"cover":{"childImageSharp":{"fluid":{"base64":"data:image/jpeg;base64,/9j/2wBDAAMCAgMCAgMDAwMEAwMEBQgFBQQEBQoHBwYIDAoMDAsKCwsNDhIQDQ4RDgsLEBYQERMUFRUVDA8XGBYUGBIUFRT/2wBDAQMEBAUEBQkFBQkUDQsNFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBT/wgARCAAKABQDASIAAhEBAxEB/8QAFwAAAwEAAAAAAAAAAAAAAAAAAAMGBP/EABYBAQEBAAAAAAAAAAAAAAAAAAYBAv/aAAwDAQACEAMQAAAB0LW/NsSPDyL/xAAZEAEBAQEBAQAAAAAAAAAAAAADBAECBRL/2gAIAQEAAQUCLYj1qnGc/j0xWQOq7j57DJA3f//EABsRAAICAwEAAAAAAAAAAAAAAAECAAQDBRFB/9oACAEDAQE/AaOuFQt1y3kGFSOmf//EABsRAAEEAwAAAAAAAAAAAAAAAAEAAgQRBSFB/9oACAECAQE/AZuRMoNpgHUbvS//xAAhEAACAQMFAAMAAAAAAAAAAAABAgMABBESEyExMhVRYf/aAAgBAQAGPwLS08G4w8N2KWK3uDbqTK2oHGrrAqK6+QWEyxqxRgOOB+0JTDGZRjDlRmkBUEbnRFNmGP681//EABsQAQADAAMBAAAAAAAAAAAAAAEAESExQVFx/9oACAEBAAE/ISGUDyOiWu3OxAGPtx7vCAtun2LjSwjXG8wo3DoLqpcgpBZwAAn/2gAMAwEAAgADAAAAEAQf/8QAGhEBAQACAwAAAAAAAAAAAAAAAREAITFBUf/aAAgBAwEBPxBzAmjTsba+YI5EM//EABkRAQADAQEAAAAAAAAAAAAAAAEAESFBUf/aAAgBAgEBPxAiRHWPEqqPZotRP//EABsQAQEBAQEBAQEAAAAAAAAAAAERIQAxQVGx/9oACAEBAAE/EIJyuapvraQT+cK6EgSLGpZEDzTrcC3LyWQviZXnTzYpsMiVm5eA2hACaY/Uy/nexLgxQzAAA+B3/9k=","aspectRatio":2,"src":"/static/cf8bb346e1a99f327205ae90fd4a45ba/60a5f/decision-tables-banner.jpg","srcSet":"/static/cf8bb346e1a99f327205ae90fd4a45ba/3e5eb/decision-tables-banner.jpg 192w,\n/static/cf8bb346e1a99f327205ae90fd4a45ba/2880b/decision-tables-banner.jpg 384w,\n/static/cf8bb346e1a99f327205ae90fd4a45ba/60a5f/decision-tables-banner.jpg 768w","srcWebp":"/static/cf8bb346e1a99f327205ae90fd4a45ba/25278/decision-tables-banner.webp","srcSetWebp":"/static/cf8bb346e1a99f327205ae90fd4a45ba/a278a/decision-tables-banner.webp 192w,\n/static/cf8bb346e1a99f327205ae90fd4a45ba/2474b/decision-tables-banner.webp 384w,\n/static/cf8bb346e1a99f327205ae90fd4a45ba/25278/decision-tables-banner.webp 768w","sizes":"(max-width: 768px) 100vw, 768px","presentationWidth":768,"presentationHeight":382}}}}},{"id":"5c6ec25b-eec7-5956-9792-3dfdfd897412","body":"var _excluded = [\"components\"];\nfunction _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }\nfunction _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }\nfunction _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }\n/* @jsxRuntime classic */\n/* @jsx mdx */\n\nvar _frontmatter = {\n  \"title\": \"Nightwatch adds Visual Studio Code integration and Accessibility testing\",\n  \"date\": \"2022-10-04T00:00:00.000Z\",\n  \"link\": \"https://www.youtube.com/watch?v=nBv1neUu3Gs\",\n  \"tags\": [\"software testing\", \"nightwatch\", \"quality assurance\", \"link\"]\n};\nvar makeShortcode = function makeShortcode(name) {\n  return function MDXDefaultShortcode(props) {\n    console.warn(\"Component \" + name + \" was not imported, exported, or provided by MDXProvider as global scope\");\n    return mdx(\"div\", props);\n  };\n};\nvar Embed = makeShortcode(\"Embed\");\nvar layoutProps = {\n  _frontmatter: _frontmatter\n};\nvar MDXLayout = \"wrapper\";\nreturn function MDXContent(_ref) {\n  var components = _ref.components,\n    props = _objectWithoutProperties(_ref, _excluded);\n  return mdx(MDXLayout, _extends({}, layoutProps, props, {\n    components: components,\n    mdxType: \"MDXLayout\"\n  }), mdx(Embed, {\n    src: \"https://www.youtube.com/embed/nBv1neUu3Gs\",\n    mdxType: \"Embed\"\n  }), mdx(\"p\", null, \"Nightwatch.js recently added Visual Studio code integration so you can debug your selenium tests. In addition, they've included my \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://www.npmjs.com/package/nightwatch-axe-verbose\"\n  }, \"nightwatch-axe-verbose\"), \" plugin for accessibility testing as a default plugin now which I talk about at \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://www.youtube.com/watch?v=nBv1neUu3Gs&t=695s\"\n  }, \"11:35\")));\n}\n;\nMDXContent.isMDXComponent = true;","excerpt":"Nightwatch.js recently added Visual Studio code integration so you can debug your selenium tests. In addition, they've included my…","fields":{"slug":"/links/nightwatch-webinar-vscode-a11y/","type":"links"},"headings":[],"frontmatter":{"title":"Nightwatch adds Visual Studio Code integration and Accessibility testing","description":null,"link":"https://www.youtube.com/watch?v=nBv1neUu3Gs","date":"2022-10-04T00:00:00.000Z","tags":["software testing","nightwatch","quality assurance","link"],"draft":null,"hide":null,"cover":null}},{"id":"34a0dbec-23f9-5f89-bd1f-56e9d3dacd4e","body":"var _excluded = [\"components\"];\nfunction _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }\nfunction _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }\nfunction _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }\n/* @jsxRuntime classic */\n/* @jsx mdx */\n\nvar _frontmatter = {\n  \"title\": \"Automated Email Testing with Nightwatch and MailTrap\",\n  \"cover\": \"./nightwatch-mailtrap-banner.jpg\",\n  \"date\": \"2022-09-27T00:00:00.000Z\",\n  \"description\": \"This handy guide will show you how to perform email testing in your end-to-end test automation to verify email delivery, recipient, localized content, or follow links out of account verification/activation emails in your Nightwatch selenium test suite using the MailTrap email sandbox tool.\",\n  \"tags\": [\"quality assurance\", \"software testing\", \"email\", \"nightwatchjs\", \"nightwatch\", \"post\"]\n};\nvar layoutProps = {\n  _frontmatter: _frontmatter\n};\nvar MDXLayout = \"wrapper\";\nreturn function MDXContent(_ref) {\n  var components = _ref.components,\n    props = _objectWithoutProperties(_ref, _excluded);\n  return mdx(MDXLayout, _extends({}, layoutProps, props, {\n    components: components,\n    mdxType: \"MDXLayout\"\n  }), mdx(\"p\", null, mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://nightwatchjs.org\"\n  }, \"Nightwatch\"), \" is a browser automation test framework built on top of the Selenium Webdriver library popularly used for automating end-to-end testing in web browsers such as Chrome, Firefox, Safari, and Edge. Sometimes these scenarios trigger conditions that have to be verified outside the reach of browser automation such as checking emails.\"), mdx(\"blockquote\", null, mdx(\"p\", {\n    parentName: \"blockquote\"\n  }, \"Issues one may run into checking emails include\"), mdx(\"ul\", {\n    parentName: \"blockquote\"\n  }, mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Many web-based email services have measures in place to prevent misuse by bots like CAPTCHAs, 2FA, or client checks.\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Login management for QA team for shared email accounts is problematic\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Network security policies preventing use of internal SMTP servers\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Send limits\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Finite email address combinations\"))), mdx(\"p\", null, mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://mailtrap.io\"\n  }, \"MailTrap\"), \" is a service that provides an email sandbox one can programmatically check in their automated browser tests using a plugin for Nightwatch, \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://www.npmjs.com/package/nightwatch-mailtrap\"\n  }, \"nightwatch-mailtrap\"), \", that this article will discuss how to use if one were stuck trying to figure out how to test emails with selenium automation frameworks like Nightwatch.\"), mdx(\"h2\", {\n    \"id\": \"email-test-assertion-scenarios\"\n  }, \"Email Test Assertion Scenarios\"), mdx(\"p\", null, \"Many things can go wrong with email delivery which makes testing it important.\"), mdx(\"p\", null, \"Popular email testing scenarios\"), mdx(\"ul\", null, mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Did the intended recipient receive the email?\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Did \", mdx(\"em\", {\n    parentName: \"li\"\n  }, \"only\"), \" the intended recipient receive the email?\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Did the subject and body content come through complete, formatted, and localized (if applicable)?\")), mdx(\"p\", null, \"In addition, if one is testing parts of a site that includes user account creation it may require following a link in an email to complete the registration process.\"), mdx(\"h3\", {\n    \"id\": \"trouble-automating-these-email-scenarios\"\n  }, \"Trouble Automating These Email Scenarios\"), mdx(\"p\", null, \"Email tends to be under tight security policies within most organizations as well as the email services themselves. Corporate IT policy may have onerous restrictions on outbound email access or require valid accounts tied to specific employees which makes testing from a continuous build system impractical. In addition, testers shouldn't have to create logins on external platforms like Gmail or Yahoo to verify email delivery. What if someone leaves the company? Further, those systems usually have steps in place to prevent misuse by bots which means those tests can't be automated in your tests.\"), mdx(\"h3\", {\n    \"id\": \"mailtrap-email-sandbox-as-a-solution\"\n  }, \"MailTrap Email Sandbox as a Solution\"), mdx(\"p\", null, \"MailTrap let's one work around these sort of problems by providing an email sandbox one can programmatically check using their email API. MailTrap let's one create one or more inboxes to receive the emails on. One can replace their test environment's outbound email SMTP settings with the special ones provided by MailTrap that correlate to those inboxes. All outbound messages To and From the system under test will end up in the inbox to be verified with API calls to the MailTrap service (or web portal).\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-c#\"\n  }, \"// C# Example Sending Mail via MailTrap\\nstatic void Main(string[] args)\\n{\\n  var client = new SmtpClient(\\\"smtp.mailtrap.io\\\", 2525)\\n  {\\n    // The credentials correspond to your particular MailTrap inbox\\n    Credentials = new NetworkCredential(\\\"{username}\\\", \\\"{password}\\\"),\\n    EnableSsl = true\\n  };\\n  client.Send(\\\"from@example.com\\\", \\\"to@example.com\\\", \\\"Hello world\\\", \\\"testbody\\\");\\n}\\n\")), mdx(\"p\", null, \"So one can be as creative as they want using any From or To email address and it would end up in the MailTrap inbox associated with the provided network credentials. After, one can use the \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://mailtrap.docs.apiary.io/\"\n  }, \"MailTrap API\"), \" to programmatically check the emails for use in their tests.\"), mdx(\"p\", null, \"Using MailTrap as a stand-in or sandbox for one's actual corporate SMTP would bypass any network policy restrictions on access or email address validity in one's tests. The MailTrap API resolves the blocks against test automation (automated browser blocks, CAPTCHAs, 2FA requirements), or sharing and management of real email account credentials within the development team.\"), mdx(\"p\", null, \"This next section will cover how to put it all together with Nightwatch test assertions using the \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://www.npmjs.com/package/nightwatch-mailtrap\"\n  }, \"nightwatch-mailtrap\"), \" plugin so one doesn't have to work with the MailTrap API directly.\"), mdx(\"h2\", {\n    \"id\": \"email-assertions-using-the-nightwatch-mailtrap-plugin\"\n  }, \"Email Assertions using the Nightwatch MailTrap Plugin\"), mdx(\"p\", null, mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://www.npmjs.com/package/nightwatch-mailtrap\"\n  }, \"nightwatch-mailtrap\"), \" adds additional assertions to the native asserts in Nightwatch.\"), mdx(\"ul\", null, mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"emailSubjectContains\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"emailBodyContains\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"expectedInboxCount\")), mdx(\"p\", null, \"and a command that will return the first link out of the email body.\"), mdx(\"ul\", null, mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"getLinkFromEmail\")), mdx(\"p\", null, \"Here is an example test with email assertions\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-js\"\n  }, \"module.exports = {\\n  'should get messages': async (browser) => {\\n    const inboxId = browser.globals.mailtrap.mailboxId;\\n\\n    // Ability to grab the link URL out of the message body\\n    let url = await browser.getLinkFromEmail(inboxId);\\n    // Example navigating the browser to the URL from the message body link\\n    browser.url(url);\\n\\n    // Test inbox contains 5 emails\\n    browser.assert.expectedInboxCount(5, inboxId);\\n\\n    // Test inbox has 1 email if subject, to_email, or to_name matches \\\"hello\\\"\\n    browser.assert.expectedInboxCount(1, inboxId, 'hello');\\n\\n    // Test that the first email message containing Welcome has \\n    // \\\"Bienvenue \\xE0 Nightwatch\\\" in the message body\\n    browser.assert.emailBodyContains(\\n      'Bienvenue \\xE0 Nightwatch',\\n      inboxId,\\n      'Welcome'\\n    );\\n\\n    // Assert the text of the first email matching 'latest tests' matches \\n    // 'The latest tests delivered...'\\n    browser.assert.emailSubjectContains(\\n      'The latest tests delivered straight to your inbox',\\n      inboxId,\\n      'latest tests' // optional search filter\\n    );\\n  },\\n};\\n\")), mdx(\"h3\", {\n    \"id\": \"installation\"\n  }, \"Installation\"), mdx(\"ol\", null, mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Start from a \", mdx(\"a\", {\n    parentName: \"li\",\n    \"href\": \"https://nightwatchjs.org/guide/quickstarts/create-and-run-a-nightwatch-test.html\"\n  }, \"working Nightwatch 2.0 installation\")), mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"From a command line in the test project run \", mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"npm install nightwatch-mailtrap --save-dev\")), mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"In the \", mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"nightwatch.conf.js\"), \" file in your test project append \", mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"nightwatch-mailtrap\"), \" to the \", mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"plugins\"), \" property array.\")), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-js\"\n  }, \"// nightwatch.conf.js\\nmodule.exports = {\\n  ...\\n  src_folders: ['test'],\\n\\n  plugins: ['nightwatch-mailtrap'],\\n  ...\\n}\\n\")), mdx(\"ol\", {\n    \"start\": 4\n  }, mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Add your MailTrap account settings (from the MailTrap portal under your user settings) to the Nightwatch configuration inside \", mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"globals\"), \".\")), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\"\n  }, \"//nightwatch.conf.js\\ntest_settings: {\\n  default: {\\n    launch_url: 'http://localhost',\\n    desiredCapabilities: {\\n      browserName: 'chrome',\\n    },\\n\\n    globals: {\\n        mailtrap: {\\n          apiToken: '${MAILTRAP_API_TOKEN}',\\n          mailboxId: '${MAILTRAP_MAILBOX_ID}',\\n        },\\n    },\\n  }\\n}\\n\")), mdx(\"p\", null, \"This is what the plugin will use to authenticate with the MailTrap API. See the \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://github.com/reallymello/nightwatch-mailtrap#readme\"\n  }, \"README\"), \" for more detailed installation instructions.\"), mdx(\"h3\", {\n    \"id\": \"usage-ideas\"\n  }, \"Usage Ideas\"), mdx(\"p\", null, \"Here are some ideas where one might mix the nightwatch-mailtrap assertions in a Nightwatch test flow.\"), mdx(\"h4\", {\n    \"id\": \"user-registration-flow\"\n  }, \"User Registration Flow\"), mdx(\"p\", null, \"This test submits a new user registration form. The form generates an email with a link they must follow to complete the user registration. It uses \", mdx(\"em\", {\n    parentName: \"p\"\n  }, \"getLinkFromEmail\"), \" to get the link from the email, passes it back to Nightwatch to navigate to it, and then verifies the page it navigates to indicates registration was a success (thanks the user for signing up).\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-js\"\n  }, \"'Valid user registration is successful': async (browser) => {\\n  // Submit new user registration form\\n  browser.page.userRegistration()\\n    .submitNewUserForm('John', 'Doe');\\n  // Get link from generated new user verification email\\n  const url = await browser.getLinkFromEmail(\\n    browser.globals.mailtrap.mailboxId,\\n    'Verify your account');\\n  // Navigate to verification link from email in your browser\\n  browser.url(url);\\n  browser.expect.element('#message').to.equal('Thank you for signing up');\\n},\\n\")), mdx(\"h4\", {\n    \"id\": \"correct-recipients-received-email\"\n  }, \"Correct Recipients Received Email\"), mdx(\"p\", null, \"This test is verifying after finalizing a payroll in the UI for a salary group (SalaryGroup2), that an email with subject \\\"Your Pay Statement is Ready\\\" is sent to Mary and Ted @testco.com but not Brad because he is not in that salary group.\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-js\"\n  }, \"'Payroll emails sent to correct recipients': async (browser) => {\\n  // Complete Payroll\\n  browser.page.payroll()\\n    .finalizePayroll('SalaryGroup2');\\n  // Verify only users in SalaryGroup2 (Mary and Ted) received pay ready email\\n  browser.assert.expectedInboxCount(\\n    1,\\n    browser.globals.mailtrap.mailboxId,\\n    'mary@testco.com'\\n  );\\n  browser.assert.emailSubjectContains(\\n    'Your Pay Statement is Ready',\\n    browser.globals.mailtrap.mailboxId,\\n    'mary@testco.com'\\n  );\\n  browser.assert.expectedInboxCount(\\n    1,\\n    browser.globals.mailtrap.mailboxId,\\n    'ted@testco.com'\\n  );\\n  // Brad isn't in SalaryGroup2\\n  browser.assert.expectedInboxCount(\\n    0,\\n    browser.globals.mailtrap.mailboxId,\\n    'brad@testco.com'\\n  );\\n},\\n\")), mdx(\"p\", null, \"Example test output\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-sh\"\n  }, \"  Running Payroll emails sent to correct recipients:\\n\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\n  \\u221A Testing if inbox id \\\"*redacted*\\\" contains exactly 1 email\\n  with a subject, to_email, or to_name matching search\\n  mary@testco.com (222ms)\\n\")), mdx(\"h2\", {\n    \"id\": \"conclusion\"\n  }, \"Conclusion\"), mdx(\"p\", null, \"Mailtrap's ability to act as a sandbox or stand-in for one's real mail servers allows test engineers to be more creative in their email testing and test scenarios previously not fully coverable through test automation. By extending Nightwatch with the nightwatch-mailtrap plugin test engineers can leverage those capabilities directly inside their existing Nightwatch selenium tests. This allows them to use the MailTrap API in the context of familiar Nightwatch assertions and commands inside their tests.\"), mdx(\"p\", null, \"If you are a Nightwatch beginner, be sure to watch my \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://youtube.com/playlist?list=PLLS_Ef55N6hmkt3-JlW40GAGpXSlp8t_D\"\n  }, \"Software Testing Playlist\"), \".\"), mdx(\"p\", null, \"Please share the link to this article if you enjoyed it and if you have any questions or comments, please reach out through my social links below \\uD83D\\uDC47\"));\n}\n;\nMDXContent.isMDXComponent = true;","excerpt":"Nightwatch  is a browser automation test framework built on top of the Selenium Webdriver library popularly used for automating end-to-end…","fields":{"slug":"/email-testing-nightwatch-mailtrap/","type":"posts"},"headings":[{"value":"Email Test Assertion Scenarios","depth":2},{"value":"Trouble Automating These Email Scenarios","depth":3},{"value":"MailTrap Email Sandbox as a Solution","depth":3},{"value":"Email Assertions using the Nightwatch MailTrap Plugin","depth":2},{"value":"Installation","depth":3},{"value":"Usage Ideas","depth":3},{"value":"User Registration Flow","depth":4},{"value":"Correct Recipients Received Email","depth":4},{"value":"Conclusion","depth":2}],"frontmatter":{"title":"Automated Email Testing with Nightwatch and MailTrap","description":"This handy guide will show you how to perform email testing in your end-to-end test automation to verify email delivery, recipient, localized content, or follow links out of account verification/activation emails in your Nightwatch selenium test suite using the MailTrap email sandbox tool.","link":null,"date":"2022-09-27T00:00:00.000Z","tags":["quality assurance","software testing","email","nightwatchjs","nightwatch","post"],"draft":null,"hide":null,"cover":{"childImageSharp":{"fluid":{"base64":"data:image/jpeg;base64,/9j/2wBDAAMCAgMCAgMDAwMEAwMEBQgFBQQEBQoHBwYIDAoMDAsKCwsNDhIQDQ4RDgsLEBYQERMUFRUVDA8XGBYUGBIUFRT/2wBDAQMEBAUEBQkFBQkUDQsNFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBT/wgARCAAKABQDASIAAhEBAxEB/8QAGQAAAgMBAAAAAAAAAAAAAAAAAAYDBAUH/8QAFgEBAQEAAAAAAAAAAAAAAAAAAwUG/9oADAMBAAIQAxAAAAHHV1uWYXcyoZ9P/8QAGhAAAwEBAQEAAAAAAAAAAAAAAgQFAwEGFf/aAAgBAQABBQLlcVrrTj+1CKwPV3NT+jLMiuefxz0Q/8QAGREBAAIDAAAAAAAAAAAAAAAAAQACERIh/9oACAEDAQE/Ab8NpXKDP//EABsRAAEEAwAAAAAAAAAAAAAAAAEAAgMRISIx/9oACAECAQE/AWgDeuBT5lcV/8QAIBAAAgEDBQEBAAAAAAAAAAAAAQIDABESBBMhIjEFgf/aAAgBAQAGPwJfpMjyR5tKsB9HW35WknDbroT1zvgPLXqU6zTh5DISMBwo44qYZtbb8vUCFiULN1PnlXdFY5tyRX//xAAcEAEBAAIDAQEAAAAAAAAAAAABEQAxIUFRYXH/2gAIAQEAAT8htHtu3p0lb8D1cdHxnTc5+zzRrDWLLsMUle+fuHCBspDWVdIpV+cZyujXef/aAAwDAQACAAMAAAAQ3z//xAAcEQABAwUAAAAAAAAAAAAAAAAhAAERMUFRobH/2gAIAQMBAT8QHbhhY17pGyJZf//EABwRAQACAwADAAAAAAAAAAAAAAERIQAxQVGBwf/aAAgBAgEBPxBPYx3ZBx+9fO8MxVtCh6JrP//EABkQAQEBAQEBAAAAAAAAAAAAAAERIQBBMf/aAAgBAQABPxBQYhN0N1RlwwvCSajOLADC2tJHPBbAny6tUq+vAnggEnoLjwRomQAQVjO+LzZIkKnwM7//2Q==","aspectRatio":2,"src":"/static/dcc2af8b74e26455d20c3e150293fee2/60a5f/nightwatch-mailtrap-banner.jpg","srcSet":"/static/dcc2af8b74e26455d20c3e150293fee2/3e5eb/nightwatch-mailtrap-banner.jpg 192w,\n/static/dcc2af8b74e26455d20c3e150293fee2/2880b/nightwatch-mailtrap-banner.jpg 384w,\n/static/dcc2af8b74e26455d20c3e150293fee2/60a5f/nightwatch-mailtrap-banner.jpg 768w","srcWebp":"/static/dcc2af8b74e26455d20c3e150293fee2/25278/nightwatch-mailtrap-banner.webp","srcSetWebp":"/static/dcc2af8b74e26455d20c3e150293fee2/a278a/nightwatch-mailtrap-banner.webp 192w,\n/static/dcc2af8b74e26455d20c3e150293fee2/2474b/nightwatch-mailtrap-banner.webp 384w,\n/static/dcc2af8b74e26455d20c3e150293fee2/25278/nightwatch-mailtrap-banner.webp 768w","sizes":"(max-width: 768px) 100vw, 768px","presentationWidth":768,"presentationHeight":382}}}}},{"id":"cb4ce3b8-5fa1-558e-a8e2-5b9a62acd910","body":"var _excluded = [\"components\"];\nfunction _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }\nfunction _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }\nfunction _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }\n/* @jsxRuntime classic */\n/* @jsx mdx */\n\nvar _frontmatter = {\n  \"title\": \"Automated Visual Regression Testing Guide - Best visual tools, how, and when to use them\",\n  \"cover\": \"./visual-testing-meme.jpg\",\n  \"date\": \"2022-05-09T00:00:00.000Z\",\n  \"description\": \"Visual regression testing is a QA testing technique effective at catching look and feel or layout regression defects and catching bugs that you aren't explicitly looking for in your tests. These tests can be flaky or high maintenance without the right combination of visual testing tool, platform, and style so this guide will help you pick the best visual regression tool and write maintainable visual tests.\",\n  \"tags\": [\"quality assurance\", \"software testing\", \"visual testing\", \"nightwatchjs\", \"nightwatch\", \"post\"]\n};\nvar layoutProps = {\n  _frontmatter: _frontmatter\n};\nvar MDXLayout = \"wrapper\";\nreturn function MDXContent(_ref) {\n  var components = _ref.components,\n    props = _objectWithoutProperties(_ref, _excluded);\n  return mdx(MDXLayout, _extends({}, layoutProps, props, {\n    components: components,\n    mdxType: \"MDXLayout\"\n  }), mdx(\"p\", null, \"Visual regression testing is a software testing technique you can use to write automated test cases against your web applications to look for unexpected visual changes, visual regression defects. This guide will cover what visual testing is, when to use it, and how to create end-to-end visual tests in SauceLabs Visual and Nightwatch.js.\"), mdx(\"h2\", {\n    \"id\": \"advantages-of-visual-testing\"\n  }, \"Advantages of Visual Testing\"), mdx(\"p\", null, \"Visual testing can be more effective at testing for look and feel, visual, and layout changes in your UI than traditional functional automated test assertions that rely on checking against value or attribute changes.\"), mdx(\"p\", null, \"High level, visual tests compare a screenshot of the web application against a known baseline and report visual differences as errors. This is a kind of check-everything-at-once approach whereas functional tests require an explicit check of each field you are expecting a bug could appear.\"), mdx(\"blockquote\", null, mdx(\"p\", {\n    parentName: \"blockquote\"\n  }, \"A picture is worth 1000 asserts \\uD83D\\uDE43\")), mdx(\"p\", null, \"The need to write an explicit check for every area you want to cover can make a typical automated functional test more work to code than a visual one. For example, \\\"requiredness field checks\\\" on a form may cause \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"*required\"), \" labels to appear on 10 fields. You could write 10 assertions to verify they are there or just 1 visual test could cover that required-error-state with the bonus that they render where expected on screen.\"), mdx(\"p\", null, \"More importantly functional tests can miss areas not explicitly checked. There are an infinite number of future things that can go wrong with your application and trying to cover them all with explicit assertions is not possible. \"), mdx(\"p\", null, \"A perfect example is the infamous easter egg that a developer checked in to the ESPN website back in 2009 causing unicorns to appear all over the site. Legend has it this was not supposed to get deployed to production, but there were no tests to explicitly verify \", mdx(\"em\", {\n    parentName: \"p\"\n  }, \"unicorns did not appear on the home page\"), \" giving this surprise to users...\"), mdx(\"span\", {\n    \"className\": \"gatsby-resp-image-wrapper\",\n    \"style\": {\n      \"position\": \"relative\",\n      \"display\": \"block\",\n      \"marginLeft\": \"auto\",\n      \"marginRight\": \"auto\",\n      \"maxWidth\": \"667px\"\n    }\n  }, \"\\n      \", mdx(\"span\", {\n    parentName: \"span\",\n    \"className\": \"gatsby-resp-image-background-image\",\n    \"style\": {\n      \"paddingBottom\": \"73.17073170731707%\",\n      \"position\": \"relative\",\n      \"bottom\": \"0\",\n      \"left\": \"0\",\n      \"backgroundImage\": \"url('data:image/jpeg;base64,/9j/2wBDAAMCAgMCAgMDAwMEAwMEBQgFBQQEBQoHBwYIDAoMDAsKCwsNDhIQDQ4RDgsLEBYQERMUFRUVDA8XGBYUGBIUFRT/2wBDAQMEBAUEBQkFBQkUDQsNFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBT/wgARCAAPABQDASIAAhEBAxEB/8QAFwAAAwEAAAAAAAAAAAAAAAAAAAUHBP/EABUBAQEAAAAAAAAAAAAAAAAAAAEE/9oADAMBAAIQAxAAAAGsz+rtyh2Zxn//xAAbEAADAAIDAAAAAAAAAAAAAAACAwQBBQYRIf/aAAgBAQABBQLmOvfUUEjcRo8SCmdDGYikGCv/xAAYEQACAwAAAAAAAAAAAAAAAAABAgMQEf/aAAgBAwEBPwFZmUZX/8QAFREBAQAAAAAAAAAAAAAAAAAAEHH/2gAIAQIBAT8Bp//EACEQAAEEAQMFAAAAAAAAAAAAAAEAAgMRQRIichMhMXGB/9oACAEBAAY/AoJYXuGhp2i6dnCcJYJ3y0S9lu8+8KPiFv6nxwVaC7kQgD2rC//EAB4QAQACAgIDAQAAAAAAAAAAAAEAESFBMVFhcYGR/9oACAEBAAE/IT4/DyWUX06lxg0JQWmgyV+w0utl6ilVXgOJ9Y6qXuL4hJagVgn/2gAMAwEAAgADAAAAELgP/8QAGhEAAgIDAAAAAAAAAAAAAAAAAAEhQRExYf/aAAgBAwEBPxC51U66N5k//8QAGBEBAQADAAAAAAAAAAAAAAAAAREQofD/2gAIAQIBAT8QAtF7eP/EABwQAQEAAgMBAQAAAAAAAAAAAAERACExQVFxkf/aAAgBAQABPxALaqxMzlKObb8LlHizcdnAckV2JMV2qBtuPff3I/8ACgCBb9ODJuAiimxT5PzJQ4HmAECeZ//Z')\",\n      \"backgroundSize\": \"cover\",\n      \"display\": \"block\"\n    }\n  }), \"\\n  \", mdx(\"img\", {\n    parentName: \"span\",\n    \"className\": \"gatsby-resp-image-image\",\n    \"alt\": \"Unicorns superimposed on ESPN homepage (from BleacherReport article by Christopher Byrne)\",\n    \"title\": \"Unicorns superimposed on ESPN homepage (from BleacherReport article by Christopher Byrne)\",\n    \"src\": \"/static/7e91ef427b9f9024d53685120d599527/56505/espn-unicorn-defect-easter-egg.jpg\",\n    \"srcSet\": [\"/static/7e91ef427b9f9024d53685120d599527/bd2b6/espn-unicorn-defect-easter-egg.jpg 205w\", \"/static/7e91ef427b9f9024d53685120d599527/ceeba/espn-unicorn-defect-easter-egg.jpg 410w\", \"/static/7e91ef427b9f9024d53685120d599527/56505/espn-unicorn-defect-easter-egg.jpg 667w\"],\n    \"sizes\": \"(max-width: 667px) 100vw, 667px\",\n    \"style\": {\n      \"width\": \"100%\",\n      \"height\": \"100%\",\n      \"margin\": \"0\",\n      \"verticalAlign\": \"middle\",\n      \"position\": \"absolute\",\n      \"top\": \"0\",\n      \"left\": \"0\"\n    },\n    \"loading\": \"lazy\"\n  }), \"\\n    \"), mdx(\"sub\", null, \"A visual regression test would have caught this.\"), mdx(\"p\", null, \"With functional tests, it is hard to test rendered appearance on the screen. Your tests can check things like CSS attributes and values, but not if between releases a CSS defect is introduced that causes a weird rendering artifact in the user's browser or if an update to a browser or UI framework negatively changes the site appearance.\"), mdx(\"blockquote\", null, mdx(\"p\", {\n    parentName: \"blockquote\"\n  }, \"Functional tests can assert a class is applied, but not how it visually appears on screen.\")), mdx(\"p\", null, \"Lastly, with the right visual automation tools and platform you can quickly reuse your same visual tests to do cross-browser, cross OS, and mobile platform tests in parallel.\"), mdx(\"p\", null, \"Summary of Visual Testing Advantages\"), mdx(\"ul\", null, mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Functional tests are narrower, test only explicit areas, or find \", mdx(\"em\", {\n    parentName: \"li\"\n  }, \"expected\"), \" bugs\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Functional tests require more code, more maintenance\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Functional tests are weaker at finding cross-browser issues\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Visual tests can cover an entire page state in one assertion\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Visual tests can find unexpected bugs instead of just expected ones\")), mdx(\"p\", null, \"Visual testing a strong tool to use for covering visually heavy functionality, but before you go and replace all your test automation with visual tests there are some disadvantages with visual testing that will be covered in the next section.\"), mdx(\"h2\", {\n    \"id\": \"disadvantages-of-visual-testing\"\n  }, \"Disadvantages of Visual Testing\"), mdx(\"p\", null, \"Visual tests will cast a much wider net with less code alerting you to potential issues your functional tests may miss. The downside is they have the potential to be more flaky, slower, and they don't provide as much at-a-glance specifics about a failure than a functional test would. For example, a visual test will alert you that something visually changed vs a functional test which would be more specific like expected value quantity-on-hand to be 10, but got 11.\"), mdx(\"p\", null, \"On many platforms this may require you going into another system to see the failure or accept baselines or changes which doesn't feel as cohesive to me.\"), mdx(\"p\", null, \"Your experience with visual testing can vary widely depending on what automated visual testing tool you pick. Historically, I always considered them to be very fragile and required a lot of maintenance. This is because early tools would look at a baseline image you have stored and compare it the current snapshot of that screen the next time you run your test on a pixel-by-pixel.\"), mdx(\"p\", null, \"This led to the following problems.\"), mdx(\"ol\", null, mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Where and how do you store/manage your baseline images? That becomes a problem at scale.\"), mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Different operating systems or OS settings may render the page differently, polluting baselines or causing false positives--especially on a larger team with non-homogenous workstations.\"), mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Updates to site style, colors, or universal elements (like banners or logos) break all your tests, and require making new baselines\"), mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Vague reporting of what was \", mdx(\"em\", {\n    parentName: \"li\"\n  }, \"actually\"), \" different (the report may just provide a % different and not help you see where)\"), mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"No great way to deal with dynamic content\")), mdx(\"p\", null, \"Fortunately, newer tools have addressed most, if not all, of these issues making them viable as a useful tool for covering visually heavy areas of an application. One such tool is SauceLabs Visual (fka Screener.io) which I've started to evaluate for Visual end-to-end regression testing with Nightwatch.js.\"), mdx(\"h2\", {\n    \"id\": \"saucelabs-visual-with-nightwatch-js\"\n  }, \"SauceLabs Visual with Nightwatch js\"), mdx(\"p\", null, \"SauceLabs is a cloud testing service that allows you to execute tests, including visual tests, against their remote VMs. It works with popular automation frameworks including WebdriverIO, Nightwatch, Cypress, and Playwright that drive the tests to remotely control the browsers in their offsite VMs. For visual testing, it does require a selenium/webdriver-based automation platform. This excludes Playwright and Cypress.\"), mdx(\"p\", null, \"My automation suites already used Nightwatch for automated functional tests of the UI so I decided to use it for evaluating SauceLabs' visual testing tool capabilities as well. During my evaluation I found it to be the best visual testing tool overall for addressing my historical concerns with visual testing tools.\"), mdx(\"p\", null, \"To run visual assertions in SauceLabs you configure your test suite to run through their \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"hub.screener.io\"), \" proxy which listens for special commands you send through the browser you are controlling in your UI automation framework, like Nightwatch.\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-js\"\n  }, \"it('should take snapshot', () => {\\n  browser.url('https://screener.io');\\n  browser.execute('/*@visual.init*/', 'My Visual Test');\\n  browser.execute('/*@visual.snapshot*/', 'Home');\\n});\\n\")), mdx(\"sub\", null, \"This is ugly, but keep reading, it gets better\"), mdx(\"p\", null, \"Alternately, if you use Storybook and are interested in visual testing of your UI components SauceLabs Visual can integrate with Storybook as well.\"), mdx(\"p\", null, \"One of the nice things about Nightwatch is how extensible it is so I wrapped SauceLabs' visual testing implementation into custom assertions to allow you to perform visual testing in Nightwatch js. This is available on npm as \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://www.npmjs.com/package/nightwatch-saucelabs-visual\"\n  }, \"nightwatch-saucelabs-visual\"), \". This allows me to write much prettier tests like\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-js\"\n  }, \"'Content should not move around': (browser) => {\\n  browser.beginVisualTest('Shifting content example');\\n\\n  browser.page.shiftingContent().navigate();\\n  browser.page.shiftingContent().click('@example1Link');\\n\\n  browser.takeSnapshot('Menu page');\\n\\n  browser.assert.visuallyTheSame();\\n}\\n\")), mdx(\"p\", null, \"Examples are provided later in the guide.\"), mdx(\"h3\", {\n    \"id\": \"advantages-of-using-saucelabs-for-visual-testing\"\n  }, \"Advantages of using SauceLabs for Visual Testing\"), mdx(\"ul\", null, mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"The baseline screenshots and history are stored in their cloud\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Their VMs are static to your desired configuration so baselines won't be polluted by different developer workstations\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Ability to test browser and operating systems outside of what you have available on site.\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Combines knowledge of the DOM with the screen comparison\", mdx(\"ul\", {\n    parentName: \"li\"\n  }, mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Let's you ignore specific DOM elements (vs % change tolerances or regions)\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Highlights DOM elements where changes were detected\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Let's you accept changes at the DOM element level for faster baseline updating\")))), mdx(\"span\", {\n    \"className\": \"gatsby-resp-image-wrapper\",\n    \"style\": {\n      \"position\": \"relative\",\n      \"display\": \"block\",\n      \"marginLeft\": \"auto\",\n      \"marginRight\": \"auto\",\n      \"maxWidth\": \"367px\"\n    }\n  }, \"\\n      \", mdx(\"span\", {\n    parentName: \"span\",\n    \"className\": \"gatsby-resp-image-background-image\",\n    \"style\": {\n      \"paddingBottom\": \"80.97560975609755%\",\n      \"position\": \"relative\",\n      \"bottom\": \"0\",\n      \"left\": \"0\",\n      \"backgroundImage\": \"url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAQCAYAAAAWGF8bAAAACXBIWXMAABJ0AAASdAHeZh94AAACAElEQVQ4y52SsW7TUBSG/QhJ2aLEwcEhSiCIqVKEYCAUpQTxBsnAMzAUqaIDFS2iYip2OleAWsTGQlOxMbMwMGZCyPZ1HKepk9rxz72267qxLSGO9Omc63PPf39fm+N5HmkIgoBqtepRq9VQr9chiiIKhULqDJf0sFgsIp/Po1KpoNVaRafTQbvdRrfbRaPRQC6X8/b8s+C5KM8zfDf5Au87o6SJeYKlUglJCMJVXLteQ/n2PYg3llGuN1C+dQfizWXaKyJtjstkMkgim2VkkV26QvOSX3uZkgl6CXCyLKPX63mwOoYk+chSpJZT4XSdYDAYQNM0qKoKwzAwHBr43+BM04SiKB5MlBACTVUwpAcRutaJFmaGRg/VCeupmE2ncBwHtm2HcK7rYpG0cJNw55dmuYvdgZA9xfjLDvT9Zxh+fA7jwxrGn9YxPlzH6ecXARuYHKzBHvwIZucXr7zozJ1NYOw9hbZ5H+T1Ksh2K0Sn69HOY5hvn2C0vYKzn8eBnhN3GBXUpS7+bNyFsvnA52WT5ia0VysgWw9hvHkEc6tJBfuhYOgwJmjPYH1/j8nXXUz6Ek6O3mFMOTmScHrc87C+7cHq78L5/SuYnccFo6LnwbYZlo3RGWCymj6wFj/UwkzcIbsLdicB3oWzHCHsB86iohwSTrwknvRbsZzgjsVfAxqs2Y3QT6gAAAAASUVORK5CYII=')\",\n      \"backgroundSize\": \"cover\",\n      \"display\": \"block\"\n    }\n  }), \"\\n  \", mdx(\"img\", {\n    parentName: \"span\",\n    \"className\": \"gatsby-resp-image-image\",\n    \"alt\": \"Change highlight specific element\",\n    \"title\": \"Change highlight specific element\",\n    \"src\": \"/static/7ccc299b3986c429f5fbdd396389893d/c3d70/change-highlight.png\",\n    \"srcSet\": [\"/static/7ccc299b3986c429f5fbdd396389893d/ad4a5/change-highlight.png 205w\", \"/static/7ccc299b3986c429f5fbdd396389893d/c3d70/change-highlight.png 367w\"],\n    \"sizes\": \"(max-width: 367px) 100vw, 367px\",\n    \"style\": {\n      \"width\": \"100%\",\n      \"height\": \"100%\",\n      \"margin\": \"0\",\n      \"verticalAlign\": \"middle\",\n      \"position\": \"absolute\",\n      \"top\": \"0\",\n      \"left\": \"0\"\n    },\n    \"loading\": \"lazy\"\n  }), \"\\n    \"), mdx(\"sub\", null, \"Highlighting changed DOM element\"), mdx(\"h3\", {\n    \"id\": \"disadvantages-of-using-saucelabs-for-visual-testing\"\n  }, \"Disadvantages of using SauceLabs for Visual Testing\"), mdx(\"ul\", null, mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Their hub is slower to execute tests\", mdx(\"ul\", {\n    parentName: \"li\"\n  }, mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Snapshot and assertion checks take much longer than functional test steps\"))), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"You have to log in to their web portal to accept the baselines or see screen captures\", mdx(\"ul\", {\n    parentName: \"li\"\n  }, mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"As of writing the SauceLabs visual product doesn't feel as integrated into their main tools\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Extra login step\"))), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Limited number of tests allowed per month\")), mdx(\"h2\", {\n    \"id\": \"writing-a-nightwatch-visual-regression-test-with-saucelabs\"\n  }, \"Writing a Nightwatch Visual Regression Test with SauceLabs\"), mdx(\"p\", null, \"Most of the examples on SauceLabs' website are geared toward WebdriverIO so this will serve as a useful guide for getting started writing automated visual regression tests using Nightwatch and SauceLabs Visual.\"), mdx(\"p\", null, \"For brevity, these examples assume you are modifying an already working Nightwatch test suite to add visual testing. If you don't have one setup or would use the final product to follow along with, the code for this example can be found on GitHub under \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://github.com/reallymello/nightwatchTutorials/tree/master/sauceLabsVisualScreener\"\n  }, \"nightwatchTutorials/sauceLabsVisualScreener\"), \".\"), mdx(\"ol\", null, mdx(\"li\", {\n    parentName: \"ol\"\n  }, mdx(\"p\", {\n    parentName: \"li\"\n  }, \"In the root of your test project, run these commands to install the npm packages we will be using\"), mdx(\"ol\", {\n    parentName: \"li\"\n  }, mdx(\"li\", {\n    parentName: \"ol\"\n  }, mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"npm install nightwatch@latest -g\")), mdx(\"li\", {\n    parentName: \"ol\"\n  }, mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"npm install nightwatch-saucelabs-endsauce nightwatch-saucelabs-visual saucelabs --save-dev\")))), mdx(\"li\", {\n    parentName: \"ol\"\n  }, mdx(\"p\", {\n    parentName: \"li\"\n  }, \"Open \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"nightwatch.config.js\"), \" and add or append to \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"custom_commands_path\"), \" and \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"plugins\")), mdx(\"pre\", {\n    parentName: \"li\"\n  }, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-js\"\n  }, \"  custom_commands_path: [\\n    './node_modules/nightwatch-saucelabs-endsauce/commands',\\n  ],\\n  plugins: ['nightwatch-saucelabs-visual'],\\n\")), mdx(\"p\", {\n    parentName: \"li\"\n  }, \"This will allow Nightwatch to add the custom commands and assertions to run against SauceLabs and execute visual test assertions.\")), mdx(\"li\", {\n    parentName: \"ol\"\n  }, mdx(\"p\", {\n    parentName: \"li\"\n  }, \"Lower down in \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"nightwatch.config.js\"), \" append the SauceLabs communication and credential settings inside the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"test_settings\"), \" section. Note the use of \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"${SAUCE_*}\"), \" environment variables so you don't check in account credentials with your source code. You can replace these with your actual SauceLabs account values for local testing or see my other article where it covers \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"../how-to-use-nightwatch-with-saucelabs\"\n  }, \"setting environment variables for SauceLabs\"), \". This will allow the tests to run through the SauceLabs Visual hub using your account settings.\"), mdx(\"pre\", {\n    parentName: \"li\"\n  }, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-js\"\n  }, \"  test_settings: {\\n    default: {\\n      launch_url: 'https://www.davidmello.com',\\n      skip_testcases_on_fail: false,\\n      webdriver: {\\n        start_process: false,\\n      },\\n\\n      desiredCapabilities: {\\n        browserName: 'chrome',\\n        screenResolution: '1920x1080',\\n        browserVersion: 'latest',\\n        javascriptEnabled: true,\\n        acceptSslCerts: true,\\n        timeZone: 'New York',\\n        chromeOptions: {\\n          w3c: true,\\n        },\\n        'sauce:options': {\\n          username: '${SAUCE_USERNAME}',\\n          accessKey: '${SAUCE_ACCESS_KEY}',\\n        },\\n        'sauce:visual': {\\n          // API key found Account -> API Key from within Sauce Visual portal\\n          apiKey: '${SAUCE_VISUAL_API_KEY}',\\n          projectName: 'project name here',\\n          viewportSize: '1920x1080',\\n        },\\n      },\\n      selenium: {\\n        port: 443,\\n        host: 'hub.screener.io',\\n        protocol: 'https',\\n        path: '/wd/hub',\\n        start_process: false,\\n      },\\n\")), mdx(\"p\", {\n    parentName: \"li\"\n  }, \"After you configure Nightwatch with those steps you can write your first visual test. The example below uses a special \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"http://the-internet.herokuapp.com/shifting_content\"\n  }, \"automation practice website\"), \" that changes the layout of elements on the page on every visit making it a good for practicing visual tests (click here for more \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"../best-websites-for-practicing-test-automation/\"\n  }, \"websites for practicing test automation\"), \")\"))), mdx(\"p\", null, mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"shiftingContent()\"), \" in the code below is a page object part of the example repository under \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://github.com/reallymello/nightwatchTutorials/blob/master/sauceLabsVisualScreener/page-objects/shiftingContent.js\"\n  }, \"page-objects/shiftingContent.js\")), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-js\"\n  }, \"// /test/shiftingContent.js\\nmodule.exports = {\\n  before: (browser) => {\\n    // beginVisualTest needs to be called just once before you start taking snapshots\\n    browser.beginVisualTest('Shifting content example');\\n    browser.page.shiftingContent().navigate();\\n  },\\n  after: (browser) => {\\n    // endSauce will update the SauceLabs automated test run log with the test name and results\\n    browser.endSauce();\\n    browser.end();\\n  },\\n  'Content should not move around': (browser) => {\\n    browser.page.shiftingContent().click('@example1Link');\\n    // takeSnapshot can be run at each page or page state you want to ensure remains visually the same\\n    browser.takeSnapshot('Menu page');\\n    browser.back();\\n\\n    browser.page.shiftingContent().click('@example2Link');\\n    // takeSnapshot can be called multiple times per test\\n    browser.takeSnapshot('Image page');\\n    browser.back();\\n\\n    browser.page.shiftingContent().click('@example3Link');\\n    // As a second parameter you can pass a list of css selectors the test will ignore. Useful for dynamic or irrelevant content.\\n    browser.takeSnapshot('List page', '.adBanner,#logo');\\n\\n    // Run assert.visuallyTheSame() to check the snapshots taken against baselines and output pass or fail\\n    browser.assert.visuallyTheSame();\\n  },\\n};\\n\")), mdx(\"p\", null, \"As mentioned in the comments, you can pass a second parameter to either beginVisualTest or takeSnapshot of type string containing a comma separated list of CSS selectors to ignore globally for the test or on that snapshot respectively. This allows you to filter out duplicative content or highly changing elements like banners or ads to make your tests less flaky and require less maintenance.\"), mdx(\"p\", null, \"You can then run \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"nightwatch --test \\\".\\\\test\\\\shiftingContentTest.js\\\"\"), \" to execute the test and get some output similar to this\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\"\n  }, \"[Shifting Content Test] Test Suite\\n\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\n\\u2139 Connected to hub.screener.io on port 443 (8203ms).\\n  Using: chrome (101.0.4951.41) on WINDOWS.\\n\\n  \\u2139 Loaded url http://the-internet.herokuapp.com/shifting_content in 2595ms\\n\\n  Running Content should not move around:\\n\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500    \\n  \\xD7 Verifying the snapshot has no visual regressions in 5000ms - expected \\\"0 visual regressions'\\\" but got: \\\"1 visual regression found. Test failed.\\n\\nView report: https://screener.io/v2/dashboard/js1dhfkdh22fs1df8/(default)\\\" (18283ms)\\n    at Object.Content should not move around (C:\\\\Projects\\\\nightwatchTutorials\\\\sauceLabsVisualScreener\\\\test\\\\shiftingContentTest.js:33:20)\\n\\n\\nFAILED: 1 assertions failed (35.068s)\\n\")), mdx(\"p\", null, \"The test will fail the first time because there is no baseline. You follow the report URL in the output to navigate to your test run and accept the baselines as applicable the first time through. After, any following runs should pass unless visual regressions are detected.\"), mdx(\"span\", {\n    \"className\": \"gatsby-resp-image-wrapper\",\n    \"style\": {\n      \"position\": \"relative\",\n      \"display\": \"block\",\n      \"marginLeft\": \"auto\",\n      \"marginRight\": \"auto\",\n      \"maxWidth\": \"731px\"\n    }\n  }, \"\\n      \", mdx(\"span\", {\n    parentName: \"span\",\n    \"className\": \"gatsby-resp-image-background-image\",\n    \"style\": {\n      \"paddingBottom\": \"83.90243902439025%\",\n      \"position\": \"relative\",\n      \"bottom\": \"0\",\n      \"left\": \"0\",\n      \"backgroundImage\": \"url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAARCAYAAADdRIy+AAAACXBIWXMAABJ0AAASdAHeZh94AAACb0lEQVQ4y41U247TMBD1L/IFfAKPCB73BcTlAR4QQvsDXAQSCNBKgFiKVKEiHhaVBXVhL5Stkubi3B3HyWHGaUJLu4Kppk7s8cmZ4xkLrTVKpVAUBRS5Hemd5/M8t89N0/zTLU5ZQqRJgiAI4PsepJSIowhhGCJJUnjzOeI4thvquj7T2aqqsgTE3fc+zt2b4sXnEEUWQcaJZZmmKTIKyLKMwJONzjGtJ5YEx4r9vUMMD2JMXQJKYqTEUBXK0ud02fl5kzMAs+oI8Ciy4QCQLnQUQIcBStdBoyv8j0kZWkCWajabIaRRyONjxEqTCG1Qw261OVu3TtPOWD/OhEfBE9ooPNx/ih+jW8DPdy1oU/MfoGT7IXo2xvRAcRkjUlG/1s0L3n1zcAO3B3dwtPcc8tl5qMMdu1iWBQJ/boHYOnY23VjCl347Tx9ndrwmxs4Y13avI5x6+Dg+gjv5gOL1hTbQVFRfVV9rvMnU7WZ2Xeq1ehQTb4KtV1tIgwS5rHDybRfO20uLVP7oxCznVJe+59ladenwuFSWtWxTJtsebePqmyu4/+kBLr+8iO/ulz6VLkXeyGWRpDmSjMe076JVwMX78GSIJ+PHmEa/1sCWrawaZKpGadAfyErK9dLJdVbXpg8wpGOWpbYVuV/74jqjp0UPQoxMY9YCDIkfcX9TvytV2Erq6tHU6/EiJ6GL0IeuNJ2asjdPs0jXptylXZfIE4k0y9sTJ+YR9z51h1KlZW8LezB6hIOvOwhkAccLaaRLQZkVCf5m0c2xzecuHMfB7HRGN5YPQbgw9NOEURm610h0pWvLclPLdV3Rjau3Torfi7ogTsLWqoIAAAAASUVORK5CYII=')\",\n      \"backgroundSize\": \"cover\",\n      \"display\": \"block\"\n    }\n  }), \"\\n  \", mdx(\"img\", {\n    parentName: \"span\",\n    \"className\": \"gatsby-resp-image-image\",\n    \"alt\": \"Reviewing SauceLabs visual pass or fail\",\n    \"title\": \"Reviewing SauceLabs visual pass or fail\",\n    \"src\": \"/static/45cf62ed7f5be9542c945b4e0ec0ff06/9f2f1/saucelabs-visual-review-screen.png\",\n    \"srcSet\": [\"/static/45cf62ed7f5be9542c945b4e0ec0ff06/ad4a5/saucelabs-visual-review-screen.png 205w\", \"/static/45cf62ed7f5be9542c945b4e0ec0ff06/74ab4/saucelabs-visual-review-screen.png 410w\", \"/static/45cf62ed7f5be9542c945b4e0ec0ff06/9f2f1/saucelabs-visual-review-screen.png 731w\"],\n    \"sizes\": \"(max-width: 731px) 100vw, 731px\",\n    \"style\": {\n      \"width\": \"100%\",\n      \"height\": \"100%\",\n      \"margin\": \"0\",\n      \"verticalAlign\": \"middle\",\n      \"position\": \"absolute\",\n      \"top\": \"0\",\n      \"left\": \"0\"\n    },\n    \"loading\": \"lazy\"\n  }), \"\\n    \"), mdx(\"h2\", {\n    \"id\": \"final-thoughts\"\n  }, \"Final Thoughts\"), mdx(\"p\", null, \"Visual Regression testing fills an important coverage gap for testing the visual appearance of your site. Like any specialized tool it makes certain tasks much easier and is worth the investment if you get enough use out of it.\"), mdx(\"p\", null, \"I feel you'd need to use a managed platform such as SauceLabs or Applitools for it to scale well rather than rely on the built-in visual testing functionality some test automation frameworks like Playwright have included. At scale, managing snapshots and workstation variances polluting snapshot baselines become an issue otherwise. \"), mdx(\"p\", null, \"Due to their slower execution times and added cost I'd limit visual regression testing to verifying visually heavy functionality and critical areas of the site or where casting a wide net possibly finding more issues has more value than more quickly knowing the specifics of a failure at a glance.\"), mdx(\"p\", null, \"If you are a Nightwatch beginner, be sure to watch my \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://youtube.com/playlist?list=PLLS_Ef55N6hmkt3-JlW40GAGpXSlp8t_D\"\n  }, \"Software Testing Playlist\"), \".\"), mdx(\"p\", null, \"Please share the link to this article if you enjoyed it and if you have any questions or comments, please reach out through my social links below \\uD83D\\uDC47\"));\n}\n;\nMDXContent.isMDXComponent = true;","excerpt":"Visual regression testing is a software testing technique you can use to write automated test cases against your web applications to look…","fields":{"slug":"/visual-regression-testing/","type":"posts"},"headings":[{"value":"Advantages of Visual Testing","depth":2},{"value":"Disadvantages of Visual Testing","depth":2},{"value":"SauceLabs Visual with Nightwatch js","depth":2},{"value":"Advantages of using SauceLabs for Visual Testing","depth":3},{"value":"Disadvantages of using SauceLabs for Visual Testing","depth":3},{"value":"Writing a Nightwatch Visual Regression Test with SauceLabs","depth":2},{"value":"Final Thoughts","depth":2}],"frontmatter":{"title":"Automated Visual Regression Testing Guide - Best visual tools, how, and when to use them","description":"Visual regression testing is a QA testing technique effective at catching look and feel or layout regression defects and catching bugs that you aren't explicitly looking for in your tests. These tests can be flaky or high maintenance without the right combination of visual testing tool, platform, and style so this guide will help you pick the best visual regression tool and write maintainable visual tests.","link":null,"date":"2022-05-09T00:00:00.000Z","tags":["quality assurance","software testing","visual testing","nightwatchjs","nightwatch","post"],"draft":null,"hide":null,"cover":{"childImageSharp":{"fluid":{"base64":"data:image/jpeg;base64,/9j/2wBDAAMCAgMCAgMDAwMEAwMEBQgFBQQEBQoHBwYIDAoMDAsKCwsNDhIQDQ4RDgsLEBYQERMUFRUVDA8XGBYUGBIUFRT/2wBDAQMEBAUEBQkFBQkUDQsNFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBT/wgARCAALABQDASIAAhEBAxEB/8QAFgABAQEAAAAAAAAAAAAAAAAAAAQG/8QAFQEBAQAAAAAAAAAAAAAAAAAABAX/2gAMAwEAAhADEAAAAV9lEutoWLJL/8QAGRAAAwEBAQAAAAAAAAAAAAAAAwQFAQIU/9oACAEBAAEFApyb7eUuy+cLeaFI3YF57Zup0505Ff/EABgRAAMBAQAAAAAAAAAAAAAAAAACEQED/9oACAEDAQE/AeawZJsP/8QAGREAAwADAAAAAAAAAAAAAAAAABESAiEi/9oACAECAQE/AbvnIa0f/8QAIBAAAgIBAwUAAAAAAAAAAAAAAQIDEQASITEEEyJBYf/aAAgBAQAGPwIyLKop2oB7wOO5LJDHQ0GvIc5HXGkZII3ZBougc625XOw3J+4C0zsb9nP/xAAbEAEAAwEBAQEAAAAAAAAAAAABABEhMWFBgf/aAAgBAQABPyEXYRwhtNIb+ztUa1EVUNaZt2DUfzebPIVgAKAa7Fh3oVPD8hwgi1cn/9oADAMBAAIAAwAAABCH3//EABoRAAICAwAAAAAAAAAAAAAAAAABETFBgZH/2gAIAQMBAT8QS83juiTH/8QAGhEBAQACAwAAAAAAAAAAAAAAASEAETFBcf/aAAgBAgEBPxBkzyahOvcWqz//xAAaEAEBAQEBAQEAAAAAAAAAAAABESEAMUFh/9oACAEBAAE/ELGiuNEzUKIr0fnKrBRgJJiLYAcIutaikhjycTggAgsuG/hzQHPxN0bjMnFJxrAOFXzv/9k=","aspectRatio":1.7777777777777777,"src":"/static/e81e16a018f797292b8a0c5aa7f527de/60a5f/visual-testing-meme.jpg","srcSet":"/static/e81e16a018f797292b8a0c5aa7f527de/3e5eb/visual-testing-meme.jpg 192w,\n/static/e81e16a018f797292b8a0c5aa7f527de/2880b/visual-testing-meme.jpg 384w,\n/static/e81e16a018f797292b8a0c5aa7f527de/60a5f/visual-testing-meme.jpg 768w","srcWebp":"/static/e81e16a018f797292b8a0c5aa7f527de/25278/visual-testing-meme.webp","srcSetWebp":"/static/e81e16a018f797292b8a0c5aa7f527de/a278a/visual-testing-meme.webp 192w,\n/static/e81e16a018f797292b8a0c5aa7f527de/2474b/visual-testing-meme.webp 384w,\n/static/e81e16a018f797292b8a0c5aa7f527de/25278/visual-testing-meme.webp 768w","sizes":"(max-width: 768px) 100vw, 768px","presentationWidth":768,"presentationHeight":432}}}}},{"id":"8bb480fb-8fc8-51c2-ac7e-5217652360cf","body":"var _excluded = [\"components\"];\nfunction _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }\nfunction _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }\nfunction _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }\n/* @jsxRuntime classic */\n/* @jsx mdx */\n\nvar _frontmatter = {\n  \"title\": \"Using TypeScript to write Nightwatch.js Automated Tests\",\n  \"cover\": \"./nightwatch-typescript-banner.jpg\",\n  \"date\": \"2022-05-02T00:00:00.000Z\",\n  \"description\": \"Nightwatch supports TypeScript! Learn how to write your Nightwatch automated tests and page objects using TypeScript with the examples in this documentation. Get the benefits of intellisense and type checking in both your tests and page objects!\",\n  \"tags\": [\"quality assurance\", \"software testing\", \"nightwatchjs\", \"typescript\", \"nightwatch\", \"post\"]\n};\nvar makeShortcode = function makeShortcode(name) {\n  return function MDXDefaultShortcode(props) {\n    console.warn(\"Component \" + name + \" was not imported, exported, or provided by MDXProvider as global scope\");\n    return mdx(\"div\", props);\n  };\n};\nvar Embed = makeShortcode(\"Embed\");\nvar layoutProps = {\n  _frontmatter: _frontmatter\n};\nvar MDXLayout = \"wrapper\";\nreturn function MDXContent(_ref) {\n  var components = _ref.components,\n    props = _objectWithoutProperties(_ref, _excluded);\n  return mdx(MDXLayout, _extends({}, layoutProps, props, {\n    components: components,\n    mdxType: \"MDXLayout\"\n  }), mdx(\"p\", null, \"Nightwatch supports TypeScript! This post will provide documentation and provide examples of how to write your tests and page objects using Nightwatch and TypeScript leveraging the \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://www.npmjs.com/package/@types/nightwatch\"\n  }, \"@types/nightwatch\"), \" definitions and Nightwatch 2.0.\"), mdx(\"p\", null, \"There wasn't comprehensive guide that I found, while learning myself, that showed how to take the Nightwatch TypeScript type definitions and use them in a real-world UI test automation suite using the page object model, so...\"), mdx(\"blockquote\", null, mdx(\"p\", {\n    parentName: \"blockquote\"\n  }, \"This guide will show you step-by-step how to create a TypeScript-enabled Nightwatch automated test suite\")), mdx(\"p\", null, \"This should help you win over internal adoption of Nightwatch with engineers and test engineers who prefer using TypeScript over JavaScript. \"), mdx(\"p\", null, \"Tests within the same test project can be written in either JavaScript or TypeScript so you can slowly convert any existing tests over time or let your engineers work in either language they prefer. This is because \", mdx(\"em\", {\n    parentName: \"p\"\n  }, \"TypeScript transpiles into JavaScript\"), \".\"), mdx(\"p\", null, \"Continue reading or \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://youtu.be/fUPwk8bFVJM\"\n  }, \"watch the lesson below\"), \".\"), mdx(Embed, {\n    src: \"https://www.youtube.com/embed/fUPwk8bFVJM\",\n    mdxType: \"Embed\"\n  }), mdx(\"h2\", {\n    \"id\": \"creating-a-typescript-nightwatch-test-suite\"\n  }, \"Creating a TypeScript Nightwatch Test Suite\"), mdx(\"p\", null, \"The following steps will create an example Nightwatch test suite written in TypeScript. How to create Nightwatch tests using the page object model pattern with TypeScript will be covered here \", mdx(\"em\", {\n    parentName: \"p\"\n  }, \"clearly\"), \" as well (that was the missing piece for me).\"), mdx(\"p\", null, \"Let's start this Nightwatch TypeScript example by creating an empty suite.\"), mdx(\"ol\", null, mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Create a folder to hold your tests.\"), mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Create a source folder named \", mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"src\"), \". This will contain your tests and page objects in either TypeScript or JavaScript.\"), mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Create a \", mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"page-objects\"), \" folder and \", mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"tests\"), \" folder inside \", mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"src\"))), mdx(\"p\", null, \"Below is the example structure we are headed towards. \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"/distrib\"), \" is automatically created when we run the TypeScript transpiler in later steps.\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\"\n  }, \"/distrib\\n\\u2502\\n\\u2514\\u2500\\u2500\\u2500/src\\n    \\u2502\\n    \\u2514\\u2500\\u2500\\u2500nightwatch.conf.js\\n    \\u2514\\u2500\\u2500\\u2500/page-objects\\n    \\u2502   \\u2502\\n    \\u2502   \\u2514\\u2500\\u2500\\u2500testPage1.js\\n    \\u2502   \\u2514\\u2500\\u2500\\u2500testPage2.js\\n    \\u2502   \\u2514\\u2500\\u2500\\u2500existingPage.js\\n    \\u2502\\n    \\u2514\\u2500\\u2500\\u2500/tests \\n        \\u2502\\n        \\u2514\\u2500\\u2500\\u2500test1.js\\n        \\u2514\\u2500\\u2500\\u2500test2.js\\n        \\u2514\\u2500\\u2500\\u2500existingTest.js\\n    \\n/src\\n\\u2502\\n\\u2514\\u2500\\u2500\\u2500/page-objects\\n\\u2502   \\u2502\\n\\u2502   \\u2514\\u2500\\u2500\\u2500testPage1.ts\\n\\u2502   \\u2514\\u2500\\u2500\\u2500testPage2.ts\\n\\u2502   \\u2514\\u2500\\u2500\\u2500existingPage.js\\n\\u2502\\n\\u2514\\u2500\\u2500\\u2500/tests\\n    \\u2502\\n    \\u2514\\u2500\\u2500\\u2500test1.ts\\n    \\u2514\\u2500\\u2500\\u2500test2.ts\\n    \\u2514\\u2500\\u2500\\u2500existingTest.js\\n\")), mdx(\"ol\", {\n    \"start\": 4\n  }, mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Run \", mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"npm init -y\")), mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Run \", mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"npm install nightwatch -g\"), \" if you haven't already installed Nightwatch globally.\"), mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Run \", mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"npm install nightwatch typescript @types/nightwatch geckodriver chromedriver --save-dev\")), mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Run \", mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"nightwatch\"), \" (this will error out and create a nightwatch.conf.js file for you)\"), mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Open \", mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"nightwatch.conf.js\")), mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Change the \", mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"src_folders\"), \" setting to \", mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"./distrib/src/tests\")), mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Change the \", mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"page_objects_path\"), \" to \", mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"./distrib/src/page-objects\"))), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-js\"\n  }, \"// nightwatch.conf.js\\nmodule.exports = {\\n  //...\\n  src_folders: ['./distrib/src/tests'],\\n  page_objects_path: ['./distrib/src/page-objects'],\\n  //...\\n  //Optionally change the browser for the test from firefox to chrome\\n  test_settings: {\\n    //...\\n    default: {\\n      desiredCapabilities: {\\n        browserName : 'chrome'\\n      },\\n    }\\n  }\\n}\\n\")), mdx(\"p\", null, \"This tells Nightwatch to look for the transpiled versions of your test and page object code from \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"/src\"), \" in \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"/distrib/src\"), \" for actual execution by Nightwatch using the JavaScript versions of the files.\"), mdx(\"ol\", {\n    \"start\": 11\n  }, mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Run \", mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"npm install typescript -g\"), \" (to install TypeScript globally)\"), mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Run \", mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"tsc -init\"))), mdx(\"p\", null, \"This will create a default \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"tsconfig.json\"), \" file.\"), mdx(\"ol\", {\n    \"start\": 13\n  }, mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Open \", mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"tsconfig.json\")), mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Uncomment the \", mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"outDir\"), \" line to read \", mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"\\\"outDir\\\": \\\"./distrib\\\",\")), mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Uncomment the line \", mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"\\\"allowJS\\\": true,\"), \" so your JavaScript tests and page objects will work together.\"), mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Save and close tsconfig.json\")), mdx(\"p\", null, \"Now that we have the configuration set and packages downloaded let's write our first Nightwatch.js page object in TypeScript.\"), mdx(\"h3\", {\n    \"id\": \"writing-a-nightwatch-page-object-in-typescript\"\n  }, \"Writing a Nightwatch Page Object in TypeScript\"), mdx(\"p\", null, \"Using the \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"../nightwatch-page-object-model-with-commands\"\n  }, \"page object model\"), \" in your tests make them easier to understand and maintain. If you have an existing large project using JavaScript the TypeScript POM files can work alongside them seamlessly.\"), mdx(\"p\", null, \"A typical JavaScript page object in Nightwatch.js may look something like this\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-js\"\n  }, \"// googlePage.js\\nconst googleCommands = {\\n  clickSearch() {\\n    return this.waitForElementVisible('@submit', 10000)\\n      .click('@submit')\\n      .waitForElementNotPresent('@submit');\\n  },\\n};\\n\\nmodule.exports = {\\n  url: 'http://www.google.com',\\n  commands: [googleCommands],\\n  elements: {\\n    searchBar: {\\n      selector: 'input[type=text]',\\n    },\\n    submit: {\\n      selector: 'input[name=btnK]',\\n    },\\n  },\\n};\\n\")), mdx(\"p\", null, \"The TypeScript version is very similar, but we will be adding \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"types\"), \" to get the advantages of type checking and intellisense as we write our code. In addition, the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"module.exports\"), \" style will change slightly and we will be exporting the page object as an \", mdx(\"em\", {\n    parentName: \"p\"\n  }, \"interface\"), \" in TypeScript to use in our tests in the next section.\"), mdx(\"p\", null, \"In the following steps we will convert this JavaScript page object model to TypeScript for use in Nightwatch.\"), mdx(\"ol\", null, mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"In the \", mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"src/page-objects\"), \" folder create \", mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"googlePage.ts\"), \" using the contents of the example \", mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"googlePage.js\"), \" file above.\"), mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"At the top add an import statement to bring in the Nightwatch types from \", mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"@types/nightwatch\"))), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-js\"\n  }, \"// googlePage.ts\\nimport { PageObjectModel, EnhancedPageObject } from 'nightwatch';\\n\")), mdx(\"ol\", {\n    \"start\": 3\n  }, mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Change \", mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"module.exports =\"), \" to\")), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-js\"\n  }, \"//module.exports = {\\nconst googlePage: PageObjectModel = {\\n\")), mdx(\"p\", null, \"This creates googlePage as a Nightwatch PageObjectModel type in TypeScript which gives us type checking so we know what valid and invalid members are which is great because it helps prevent mistakes while coding and let's intellisense help you learn what members exist on it as you type.\"), mdx(\"span\", {\n    \"className\": \"gatsby-resp-image-wrapper\",\n    \"style\": {\n      \"position\": \"relative\",\n      \"display\": \"block\",\n      \"marginLeft\": \"auto\",\n      \"marginRight\": \"auto\",\n      \"maxWidth\": \"485px\"\n    }\n  }, \"\\n      \", mdx(\"span\", {\n    parentName: \"span\",\n    \"className\": \"gatsby-resp-image-background-image\",\n    \"style\": {\n      \"paddingBottom\": \"16.097560975609756%\",\n      \"position\": \"relative\",\n      \"bottom\": \"0\",\n      \"left\": \"0\",\n      \"backgroundImage\": \"url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAADCAYAAACTWi8uAAAACXBIWXMAABJ0AAASdAHeZh94AAAAyUlEQVQI1yWNWW7CQBBEfQIiIVACie14YRmBIowzXrFhbLMIBUVZFU7A/U/wMpiv6upXXW24TwLpRTimwEpK3FRhL1d4eYM5XvAoArxVjV/usYKU57jALxqsMKd3byNeAroPJp3ekLv+EMOZJajmg5lUjKKaabJl/KqYxE2r/nLd7v1Qc1kxCje3WTMvKG9eq7soWm90Bw4De8r6eCb7u5AefhDRtaxiImuS7Sf5/pts94Ws3on1c3U6Uxx/yXV286bvNL+yeXbgH/+EYMeRSy3fAAAAAElFTkSuQmCC')\",\n      \"backgroundSize\": \"cover\",\n      \"display\": \"block\"\n    }\n  }), \"\\n  \", mdx(\"img\", {\n    parentName: \"span\",\n    \"className\": \"gatsby-resp-image-image\",\n    \"alt\": \"Nightwatch TypeScript intellisense\",\n    \"title\": \"Nightwatch TypeScript intellisense\",\n    \"src\": \"/static/6f44b1437ee4cdfe3b5e29b6f1f4be53/bc09f/intellisense.png\",\n    \"srcSet\": [\"/static/6f44b1437ee4cdfe3b5e29b6f1f4be53/ad4a5/intellisense.png 205w\", \"/static/6f44b1437ee4cdfe3b5e29b6f1f4be53/74ab4/intellisense.png 410w\", \"/static/6f44b1437ee4cdfe3b5e29b6f1f4be53/bc09f/intellisense.png 485w\"],\n    \"sizes\": \"(max-width: 485px) 100vw, 485px\",\n    \"style\": {\n      \"width\": \"100%\",\n      \"height\": \"100%\",\n      \"margin\": \"0\",\n      \"verticalAlign\": \"middle\",\n      \"position\": \"absolute\",\n      \"top\": \"0\",\n      \"left\": \"0\"\n    },\n    \"loading\": \"lazy\"\n  }), \"\\n    \"), mdx(\"ol\", {\n    \"start\": 4\n  }, mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Add types to the page object commands\")), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-js\"\n  }, \"const googleCommands = {\\n  //clickSearch() {\\n  clickSearch(this: GooglePage) {\\n\")), mdx(\"ol\", {\n    \"start\": 5\n  }, mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Export your interface at the very bottom of the page object code\")), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-js\"\n  }, \"export default googlePage;\\n\\nexport interface GooglePage\\n  extends EnhancedPageObject<typeof googleCommands,\\n  typeof googlePage.elements> { }\\n\")), mdx(\"p\", null, mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"EnhancedPageObject\"), \" can take the types for your custom commands, elements, and optionally any \", mdx(\"em\", {\n    parentName: \"p\"\n  }, \"sections\"), \" you may optionally be using as a third parameter.\"), mdx(\"p\", null, \"This is what gives you intellisense and type checking for your page objects when you use them in your tests later.\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-js\"\n  }, \"// googlePage.ts\\nimport { PageObjectModel, EnhancedPageObject } from 'nightwatch';\\n\\nconst googleCommands = {\\n  clickSearch(this: GooglePage) {\\n    return this.waitForElementVisible('@submit', 10000)\\n      .click('@submit')\\n      .waitForElementNotPresent('@submit');\\n  },\\n};\\n\\nconst googlePage: PageObjectModel = {\\n  url: 'http://www.google.com',\\n  commands: [googleCommands],\\n  elements: {\\n    searchBar: {\\n      selector: 'input[type=text]',\\n    },\\n    submit: {\\n      selector: 'input[name=btnK]',\\n    },\\n  },\\n};\\n\\nexport default googlePage;\\n\\nexport interface GooglePage\\n  extends EnhancedPageObject<typeof googleCommands,\\n  typeof googlePage.elements> { }\\n\")), mdx(\"p\", null, \"So now that we have created a Nightwatch TypeScript page object model let's use it in a test also written in TypeScript.\"), mdx(\"h3\", {\n    \"id\": \"writing-a-nightwatch-test-in-typescript-using-the-page-object-model\"\n  }, \"Writing a Nightwatch Test in TypeScript using the Page Object Model\"), mdx(\"p\", null, \"In the next steps we'll use this example of a Nightwatch JavaScript test and convert it to TypeScript.\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-js\"\n  }, \"// googleTest.js\\nmodule.exports = {\\n  'Google search test': function (browser) {\\n    let google = browser.page.googlePage();\\n\\n    google\\n      .navigate()\\n      .assert.titleEquals('Google')\\n      .assert.visible('@searchBar')\\n      .setValue('@searchBar', 'nightwatch');\\n\\n    google\\n      .clickSearch()\\n      .expect.element('body')\\n      .text.to.contain(\\n        'Nightwatch.js | Node.js powered End-to-End testing framework'\\n      );\\n\\n    browser.end();\\n  },\\n};\\n\")), mdx(\"ol\", null, mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Create a file named \", mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"googleTest.ts\"), \" under \", mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"src/tests\"), \" and paste in the above example.\"), mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Add a line to import NightwatchTests and NightwatchBrowser from \", mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"@types/nightwatch\"), \" at the top of the test class. Also, import GooglePage.\"), mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Change module.exports to a named constant of type NightwatchTests and export it at the very bottom of the test class as shown below\")), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-js\"\n  }, \"// googleTest.ts\\nimport { NightwatchTests, NightwatchBrowser } from \\\"nightwatch\\\";\\nimport { GooglePage } from '../page-objects/googlePage';\\n//module.exports = {\\nconst googleTest: NightwatchTests = {\\n//...\\n}\\nexport default googleTest;\\n\")), mdx(\"ol\", {\n    \"start\": 4\n  }, mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Inside the test, change the untyped \", mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"browser\"), \" object to \", mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"NightwatchBrowser\"), \" and the \", mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"google\"), \" variable to our page object model type, \", mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"GooglePage\"), \", that we made in the prior section\")), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-js\"\n  }, \"//  'Google search test': function (browser) {\\n//    let google = browser.page.googlePage();\\n  'Google search test': function (browser: NightwatchBrowser) {\\n    const google: GooglePage = browser.page.googlePage();\\n\")), mdx(\"p\", null, \"Now that everything has types our IDE, like Visual Studio Code, will provide helpful intellisense when we hover over the methods exposed in the Nightwatch API or our page objects.\"), mdx(\"span\", {\n    \"className\": \"gatsby-resp-image-wrapper\",\n    \"style\": {\n      \"position\": \"relative\",\n      \"display\": \"block\",\n      \"marginLeft\": \"auto\",\n      \"marginRight\": \"auto\",\n      \"maxWidth\": \"755px\"\n    }\n  }, \"\\n      \", mdx(\"span\", {\n    parentName: \"span\",\n    \"className\": \"gatsby-resp-image-background-image\",\n    \"style\": {\n      \"paddingBottom\": \"34.146341463414636%\",\n      \"position\": \"relative\",\n      \"bottom\": \"0\",\n      \"left\": \"0\",\n      \"backgroundImage\": \"url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAHCAYAAAAIy204AAAACXBIWXMAABJ0AAASdAHeZh94AAABfElEQVQoz0WQWXObQBCEeXKsWBI60QVCiGuB5QZhK2WnnIck//8PfZkijvPQ1bOzMz0zbXhKE6UVkW5Jy164RtcDurqN+e7ljbK7EyY5SpcESUHQ3olvr8TlQFb1JEVLmJWsDxcMXwSzskMVDVX3Ip8N/f2NWkQDVdAM31BJSZ1nNGVGomKcKEP3Ulv3FO2AF6UsdzbLvYth+YqtGxLl9Tjdvip8lXGRIi8WDtMxPn/wbHNgurT4am5HTARP8p6t9yJ6xvj+6zf39x/kMsmyXR6mSx7nayaLDY/mesTElHi+5ct8w3S1H5tn6wPzzX98ClbtMypvuQY1oarIu4FI/LCdgOhaEHoFrlyRqCtJFnG4eKz2R8HpU+gfFjsHY+dFTC17NDaINV6YcI00liPNQc457XDEy1iH6CbGTwPc0MP2fdnI+bvh9jjy6GH3/hPVv+IVN1yBo2+c45i0DkU4GE+byZlPq6N4dZSTj5I7iZcnzO2JhSxjfmBhOfwBSkPDSKI0t7sAAAAASUVORK5CYII=')\",\n      \"backgroundSize\": \"cover\",\n      \"display\": \"block\"\n    }\n  }), \"\\n  \", mdx(\"img\", {\n    parentName: \"span\",\n    \"className\": \"gatsby-resp-image-image\",\n    \"alt\": \"Nightwatch intellisense for titleEquals\",\n    \"title\": \"Nightwatch intellisense for titleEquals\",\n    \"src\": \"/static/69e0323c9c23ad0c84d214f36e35c24d/9fe72/nightwatch-intellisense.png\",\n    \"srcSet\": [\"/static/69e0323c9c23ad0c84d214f36e35c24d/ad4a5/nightwatch-intellisense.png 205w\", \"/static/69e0323c9c23ad0c84d214f36e35c24d/74ab4/nightwatch-intellisense.png 410w\", \"/static/69e0323c9c23ad0c84d214f36e35c24d/9fe72/nightwatch-intellisense.png 755w\"],\n    \"sizes\": \"(max-width: 755px) 100vw, 755px\",\n    \"style\": {\n      \"width\": \"100%\",\n      \"height\": \"100%\",\n      \"margin\": \"0\",\n      \"verticalAlign\": \"middle\",\n      \"position\": \"absolute\",\n      \"top\": \"0\",\n      \"left\": \"0\"\n    },\n    \"loading\": \"lazy\"\n  }), \"\\n    \"), mdx(\"p\", null, \"Our custom commands in the page object model will also show up because of the use of \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"typeof\"), \" when we exported our page object model interface.\"), mdx(\"span\", {\n    \"className\": \"gatsby-resp-image-wrapper\",\n    \"style\": {\n      \"position\": \"relative\",\n      \"display\": \"block\",\n      \"marginLeft\": \"auto\",\n      \"marginRight\": \"auto\",\n      \"maxWidth\": \"521px\"\n    }\n  }, \"\\n      \", mdx(\"span\", {\n    parentName: \"span\",\n    \"className\": \"gatsby-resp-image-background-image\",\n    \"style\": {\n      \"paddingBottom\": \"24.878048780487806%\",\n      \"position\": \"relative\",\n      \"bottom\": \"0\",\n      \"left\": \"0\",\n      \"backgroundImage\": \"url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAFCAYAAABFA8wzAAAACXBIWXMAABJ0AAASdAHeZh94AAABA0lEQVQY06WNyU7CUBSG+wDEEWpVoJax3F460NKBYgUCkQhCMCYu3Lrz/defFzD4AC6+nPMPya85fowX57R6ktKVwVn59l9oTjInmq4p1u+Eiw2Wl9NQWO5IkZ9o/PpNb3zI/jqjU7bvaee6iRA+T+OCYrYi3X2Rfn4TLD9oDybY8UINbkgXb8SzLf7jimS+Yzjdkilv74eTV4Jijcye0S70GtdGlayQhIMAkc1wX7aE8yXBMEEOhlSbLfSqSfnOpHJf5+6hgV4zMerW8VdZRbG/2uVNnVLZQEYdolQSjVzcSCBcge31sOwOYS7pel3aTod+KAgSh66rdP+om6J90MK3+QHGvJYb+cAXiwAAAABJRU5ErkJggg==')\",\n      \"backgroundSize\": \"cover\",\n      \"display\": \"block\"\n    }\n  }), \"\\n  \", mdx(\"img\", {\n    parentName: \"span\",\n    \"className\": \"gatsby-resp-image-image\",\n    \"alt\": \"Nightwatch intellisense for titleEquals\",\n    \"title\": \"Nightwatch intellisense for titleEquals\",\n    \"src\": \"/static/f5f366de15cebf5dfa04531c78e0435e/ab3ab/pom-intellisense-autocomplete.png\",\n    \"srcSet\": [\"/static/f5f366de15cebf5dfa04531c78e0435e/ad4a5/pom-intellisense-autocomplete.png 205w\", \"/static/f5f366de15cebf5dfa04531c78e0435e/74ab4/pom-intellisense-autocomplete.png 410w\", \"/static/f5f366de15cebf5dfa04531c78e0435e/ab3ab/pom-intellisense-autocomplete.png 521w\"],\n    \"sizes\": \"(max-width: 521px) 100vw, 521px\",\n    \"style\": {\n      \"width\": \"100%\",\n      \"height\": \"100%\",\n      \"margin\": \"0\",\n      \"verticalAlign\": \"middle\",\n      \"position\": \"absolute\",\n      \"top\": \"0\",\n      \"left\": \"0\"\n    },\n    \"loading\": \"lazy\"\n  }), \"\\n    \"), mdx(\"p\", null, \"The final test Nightwatch test written in TypeScript should look like this\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-js\"\n  }, \"import { NightwatchTests, NightwatchBrowser } from 'nightwatch';\\nimport { GooglePage } from '../page-objects/googlePage';\\n\\nconst googleTest: NightwatchTests = {\\n  'Google search test': (browser: NightwatchBrowser) => {\\n    let google: GooglePage = browser.page.googlePage();\\n\\n    google\\n      .navigate()\\n      .assert.titleEquals('Google')\\n      .assert.visible('@searchBar')\\n      .setValue('@searchBar', 'nightwatch');\\n\\n    google\\n      .clickSearch()\\n      .expect.element('body')\\n      .text.to.contain(\\n        'Nightwatch.js | Node.js powered End-to-End testing framework'\\n      );\\n\\n    browser.end();\\n  },\\n};\\n\\nexport default googleTest;\\n\")), mdx(\"p\", null, \"Now that we have our page object model and automated test written we can run our test suite.\"), mdx(\"h3\", {\n    \"id\": \"how-to-run-a-nightwatch-test-in-typescript\"\n  }, \"How to Run a Nightwatch Test in TypeScript\"), mdx(\"p\", null, \"TypeScript needs to be transpiled, converted from TypeScript to JavaScript, using the TypeScript compiler, \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"tsc\"), \". The best workflow I've found for executing the TypeScript test suite is by modifying the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"npm test\"), \" command.\"), mdx(\"ol\", null, mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Open \", mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"package.json\")), mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Change the scripts section to the below example\")), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-json\"\n  }, \"  \\\"scripts\\\": {\\n    \\\"test\\\": \\\"tsc && nightwatch\\\"\\n  },\\n\")), mdx(\"p\", null, mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"tsc\"), \" will convert any .ts tests and page objects and move them over to the distrib folder along with any existing .js page objects and tests you have in the suite.\"), mdx(\"blockquote\", null, mdx(\"p\", {\n    parentName: \"blockquote\"\n  }, \"nightwatch will execute both your TypeScript and JavaScript tests in your suite when run this way \\uD83D\\uDE01\")), mdx(\"ol\", {\n    \"start\": 3\n  }, mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Run \", mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"npm test\"), \" from your command line to run your tests.\")), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-sh\"\n  }, \"> tsc && nightwatch\\n\\n\\n[Google Test] Test Suite\\n\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\n\\u2838 Starting ChromeDriver on port 9515...\\n\\n\\u2139 Connected to ChromeDriver on port 9515 (751ms).\\n  Using: chrome (101.0.4951.41) on WINDOWS.\\n\\n\\n  Running Google search test:\\n\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\n  \\u2139 Loaded url http://www.google.com in 1605ms\\n  \\u221A Testing if the page title equals 'Google' (6ms)\\n  \\u221A Testing if element <Element [name=@searchBar]> is visible (40ms)\\n  \\u221A Element <input[name=btnK]> was visible after 532 milliseconds.\\n  \\u221A Element <input[name=btnK]> was not present after 9 milliseconds.\\n  \\u221A Expected element <body> text to contain: \\\"Nightwatch.js | Node.js powered End-to-End testing framework\\\" (292ms)\\n\\nOK. 5 assertions passed. (4.149s)\\n\")), mdx(\"p\", null, \"From here you can add more automated tests in your test suite written in either JavaScript or TypeScript and npm run will execute them for you.\"), mdx(\"p\", null, \"The full source code for this example is \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://github.com/reallymello/nightwatchTutorials/tree/master/pomTypeScriptExample\"\n  }, \"on my GitHub\")), mdx(\"h2\", {\n    \"id\": \"final-thoughts\"\n  }, \"Final Thoughts\"), mdx(\"p\", null, \"I'm excited that I'm finally able to write my Nightwatch tests in TypeScript. If you are converting a large suite, like I am, you can slowly convert existing tests and page objects by swapping the file extension from .js to .ts using the patterns I showed above without needing to do a big-bang conversion on your entire project.\"), mdx(\"p\", null, \"The DefinitelyTyped @types/nightwatch project is community-driven and is continually being updated to include the remaining types in Nightwatch 2.0. If you have additions or improvements you can submit pull requests to \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/nightwatch\"\n  }, \"DefinitelyTyped/nightwatch\"), \". Special thanks to \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://github.com/vaibhavsingh97\"\n  }, \"vaibhavsingh97\"), \" who is a top contributor and answerer of questions for that project and \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://github.com/lukebickell\"\n  }, \"Luke Bickell\"), \" who helped figure out recursive page objects.\"), mdx(\"p\", null, \"If you are a Nightwatch beginner be sure to watch my \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://youtube.com/playlist?list=PLLS_Ef55N6hmkt3-JlW40GAGpXSlp8t_D\"\n  }, \"Software Testing Playlist\")), mdx(\"p\", null, \"Please share the link to this article if you enjoyed it and if you have any questions or comments please reach out through my social links below \\uD83D\\uDC47\"));\n}\n;\nMDXContent.isMDXComponent = true;","excerpt":"Nightwatch supports TypeScript! This post will provide documentation and provide examples of how to write your tests and page objects using…","fields":{"slug":"/using-nightwatch-with-typescript/","type":"posts"},"headings":[{"value":"Creating a TypeScript Nightwatch Test Suite","depth":2},{"value":"Writing a Nightwatch Page Object in TypeScript","depth":3},{"value":"Writing a Nightwatch Test in TypeScript using the Page Object Model","depth":3},{"value":"How to Run a Nightwatch Test in TypeScript","depth":3},{"value":"Final Thoughts","depth":2}],"frontmatter":{"title":"Using TypeScript to write Nightwatch.js Automated Tests","description":"Nightwatch supports TypeScript! Learn how to write your Nightwatch automated tests and page objects using TypeScript with the examples in this documentation. Get the benefits of intellisense and type checking in both your tests and page objects!","link":null,"date":"2022-05-02T00:00:00.000Z","tags":["quality assurance","software testing","nightwatchjs","typescript","nightwatch","post"],"draft":null,"hide":null,"cover":{"childImageSharp":{"fluid":{"base64":"data:image/jpeg;base64,/9j/2wBDAAMCAgMCAgMDAwMEAwMEBQgFBQQEBQoHBwYIDAoMDAsKCwsNDhIQDQ4RDgsLEBYQERMUFRUVDA8XGBYUGBIUFRT/2wBDAQMEBAUEBQkFBQkUDQsNFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBT/wgARCAAKABQDASIAAhEBAxEB/8QAFwAAAwEAAAAAAAAAAAAAAAAAAAUGA//EABYBAQEBAAAAAAAAAAAAAAAAAAUAAf/aAAwDAQACEAMQAAABiYrFawOwF5l//8QAGxAAAgIDAQAAAAAAAAAAAAAAAQIDBAUGEBL/2gAIAQEAAQUCt4itFqSsvmRyS9qY1uf/xAAZEQACAwEAAAAAAAAAAAAAAAAAEQECAzH/2gAIAQMBAT8Bro+wM//EABgRAAMBAQAAAAAAAAAAAAAAAAABAxEi/9oACAECAQE/AaSx8sxn/8QAJRAAAQIEBAcAAAAAAAAAAAAAAQIDAAQSIRARE1EiIyQxQYLw/9oACAEBAAY/ApeYDfUBdS3h5Sew+2ilLROW6BA5YT6iNMurLduCq2P/xAAaEAEBAAMBAQAAAAAAAAAAAAABMQARIRCB/9oACAEBAAE/ISRoRe8VLt1MSAqt+4yPNAsLNtFM57//2gAMAwEAAgADAAAAEHzv/8QAGREBAAIDAAAAAAAAAAAAAAAAAQARITFR/9oACAEDAQE/EHIlgrmohbqf/8QAGxEAAQQDAAAAAAAAAAAAAAAAAQARITFRkfD/2gAIAQIBAT8QC0QPObQEL7a//8QAHBABAQEBAAIDAAAAAAAAAAAAASERMQBREGHR/9oACAEBAAE/EDwhwHWlYGW4nO3Hg6fu1df0PXiSjkKmtnfW/XgtYCmiheRpJ8//2Q==","aspectRatio":2,"src":"/static/7b14821120634c832c64aaa32dbf83c2/60a5f/nightwatch-typescript-banner.jpg","srcSet":"/static/7b14821120634c832c64aaa32dbf83c2/3e5eb/nightwatch-typescript-banner.jpg 192w,\n/static/7b14821120634c832c64aaa32dbf83c2/2880b/nightwatch-typescript-banner.jpg 384w,\n/static/7b14821120634c832c64aaa32dbf83c2/60a5f/nightwatch-typescript-banner.jpg 768w","srcWebp":"/static/7b14821120634c832c64aaa32dbf83c2/25278/nightwatch-typescript-banner.webp","srcSetWebp":"/static/7b14821120634c832c64aaa32dbf83c2/a278a/nightwatch-typescript-banner.webp 192w,\n/static/7b14821120634c832c64aaa32dbf83c2/2474b/nightwatch-typescript-banner.webp 384w,\n/static/7b14821120634c832c64aaa32dbf83c2/25278/nightwatch-typescript-banner.webp 768w","sizes":"(max-width: 768px) 100vw, 768px","presentationWidth":768,"presentationHeight":382}}}}},{"id":"934dacf7-31f4-5ed0-aa3e-361bbd26d2eb","body":"var _excluded = [\"components\"];\nfunction _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }\nfunction _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }\nfunction _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }\n/* @jsxRuntime classic */\n/* @jsx mdx */\n\nvar _frontmatter = {\n  \"title\": \"Speed Test Comparison Unifi Wi-Fi 6 Pro vs UAP-AC-Pro access points\",\n  \"cover\": \"./u6-pro-us-vs-uap-ac-pro.jpg\",\n  \"date\": \"2022-02-23T00:00:00.000Z\",\n  \"description\": \"Ubiquiti released their new Wi-Fi 6 access points including their new U6-Pro-US so I decided to upgrade and test the speeds to see how the Unifi Wi-Fi 6 Pro compares to the UAP-AC-Pro and UAP-AC-HD using an iPhone 13 pro and iPerf3.\",\n  \"tags\": [\"networking\", \"smart home\", \"unifi\", \"ubiquiti\", \"post\"]\n};\nvar makeShortcode = function makeShortcode(name) {\n  return function MDXDefaultShortcode(props) {\n    console.warn(\"Component \" + name + \" was not imported, exported, or provided by MDXProvider as global scope\");\n    return mdx(\"div\", props);\n  };\n};\nvar Embed = makeShortcode(\"Embed\");\nvar layoutProps = {\n  _frontmatter: _frontmatter\n};\nvar MDXLayout = \"wrapper\";\nreturn function MDXContent(_ref) {\n  var components = _ref.components,\n    props = _objectWithoutProperties(_ref, _excluded);\n  return mdx(MDXLayout, _extends({}, layoutProps, props, {\n    components: components,\n    mdxType: \"MDXLayout\"\n  }), mdx(\"p\", null, \"Ubiquiti recently released their Wi-Fi 6 access points and I was curious to see if it would increase my iPhone 13 Pro Wi-Fi speeds over the existing UAP-AC-Pro and UAP-AC-HD access points I was using. So this comparison review will cover the Wi-Fi speed differences I noticed when upgrading from the UAP-AC-Pro and HD to the U6-Pro-US (Ubiquiti Access Point Wi-Fi 6 Pro).\"), mdx(Embed, {\n    src: \"https://www.youtube.com/embed/1SsPPYpfhV0\",\n    mdxType: \"Embed\"\n  }), mdx(\"h2\", {\n    \"id\": \"differences-between-u6-pro-us-and-uap-ac-pro\"\n  }, \"Differences between U6-Pro-US and UAP-AC-Pro\"), mdx(\"p\", null, \"The Ubiquiti Wi-Fi 6 Pro (U6-Pro-US) is the newer, Wi-Fi 6, version of the previous Wi-Fi 5 Pro access point, the UAP-AC-Pro. Below are some important specification differences between the two wifi access points.\"), mdx(\"h3\", {\n    \"id\": \"speed-differences-in-access-points-u6-pro-us-vs-uap-ac-pro\"\n  }, \"Speed differences in Access Points (U6-Pro-US vs UAP-AC-Pro)\"), mdx(\"table\", null, mdx(\"thead\", {\n    parentName: \"table\"\n  }, mdx(\"tr\", {\n    parentName: \"thead\"\n  }, mdx(\"th\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"Access Point\"), mdx(\"th\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"Throughput Speed 5ghz\"), mdx(\"th\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"Throughput Speed 2.4ghz\"))), mdx(\"tbody\", {\n    parentName: \"table\"\n  }, mdx(\"tr\", {\n    parentName: \"tbody\"\n  }, mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"U6-Pro-US\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"4800 mbps\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"574 mbps\")), mdx(\"tr\", {\n    parentName: \"tbody\"\n  }, mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"U6-LR-US\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"2400 mbps\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"600 mbps\")), mdx(\"tr\", {\n    parentName: \"tbody\"\n  }, mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"UAP-AC-Pro\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"1300 mbps\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"450 mbps\")))), mdx(\"p\", null, \"There is a theoretically huge speed performance increase of 3500mbps between the UAP-AC-Pro and the U6-Pro-US at 5ghz and a smaller increase of 124mbps at 2.4ghz.\"), mdx(\"h3\", {\n    \"id\": \"radio-changes\"\n  }, \"Radio changes\"), mdx(\"table\", null, mdx(\"thead\", {\n    parentName: \"table\"\n  }, mdx(\"tr\", {\n    parentName: \"thead\"\n  }, mdx(\"th\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"Access Point\"), mdx(\"th\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"5ghz MIMO\"), mdx(\"th\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"2.4ghz MIMO\"))), mdx(\"tbody\", {\n    parentName: \"table\"\n  }, mdx(\"tr\", {\n    parentName: \"tbody\"\n  }, mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"U6-Pro-US\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"4x4 MIMO\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"2x2 MIMO\")), mdx(\"tr\", {\n    parentName: \"tbody\"\n  }, mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"U6-LR-US\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"4x4 MIMO\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"4x4 MIMO\")), mdx(\"tr\", {\n    parentName: \"tbody\"\n  }, mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"UAP-AC-Pro\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"3x3 MIMO\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"3x3 MIMO\")))), mdx(\"p\", null, \"So the Ubiquiti Pro and LR Wi-Fi 6 APs increase from 3x3 to 4x4 MIMO on the 5ghz radio band. The Pro actually went down from 3x3 MIMO in the UAP-AC-Pro to 2x2 MIMO in the U6-Pro-US. The loss of 2 MIMO antennas in the 2.4ghz band may not materially affect performance because many 2.4ghz devices don't support higher levels of MIMO anyway.\"), mdx(\"p\", null, \"The Pro uses a Qualcomm chipset whereas the LR uses Mediatek.\"), mdx(\"h3\", {\n    \"id\": \"physical-differences\"\n  }, \"Physical differences\"), mdx(\"p\", null, \"The UAP-AC-Pro I have currently looks to be the exact same size, 197mm x 197mm x 35mm, as the new WiFi 6 Pro AP so I should be able to use the existing mounting bracket I have in the ceiling.\"), mdx(\"p\", null, \"Below is a picture of the back of each access point.\"), mdx(\"span\", {\n    \"className\": \"gatsby-resp-image-wrapper\",\n    \"style\": {\n      \"position\": \"relative\",\n      \"display\": \"block\",\n      \"marginLeft\": \"auto\",\n      \"marginRight\": \"auto\",\n      \"maxWidth\": \"820px\"\n    }\n  }, \"\\n      \", mdx(\"span\", {\n    parentName: \"span\",\n    \"className\": \"gatsby-resp-image-background-image\",\n    \"style\": {\n      \"paddingBottom\": \"65.85365853658536%\",\n      \"position\": \"relative\",\n      \"bottom\": \"0\",\n      \"left\": \"0\",\n      \"backgroundImage\": \"url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAANCAIAAAAmMtkJAAAACXBIWXMAAC4jAAAuIwF4pT92AAADJElEQVQozwEZA+b8AJ53HJNvH4tnI4ZcIIBYHnxWIXZSI3FRKW1OLGtKKWlJKWZHKGI+IV85G1w5HVs3HFszGFw2HVw4H1o0GACSbh6LYx6IYhyAdDV8iFp3iGNsa0xdRSVdQCBgRChgRChbOx5lWTltf3JxiZF4jZppb3JTPitPLBBVNRkAiGEfiW0lfZ1tcKqwernMebfKerjLb5efVEc2VDsgUTgeZ3BTfKy1fKjQga/XksLmirrgganDX1dJTSwPAIRmI3uid22pwn/Czn2/wnu5w3m2w4DA03ekr0k2JlpVN3yvtHieyn+ry32mw4CpyX2oxoOw1Ia0zFNCMAB9hlBppbWAwcx0sLRtmrV2qsNxqrtxp7eEw9dXampieGt5pcZ+qst4obt3mLx8osV/qMd5n72HuNxtf4QAdZV9crHGfsDEZpKreK/Fe7XEgbvLbJywfbXNaJCbY4WQd6DFf67Kc5i3e6HBfqbDgqzJeqK8fqfMep2vAHOUjHWzyHy4xGmYsHqxwoC5yInF0mqVqnyuzWaPnV+AkHegw4CvzHWcu4KoyoOryYKtyXifuX+mzHaYrAB0i4Z1tsmAusxzq8F8tsx/ts96s8dumLiFu9pWdX9NXmZ+rct9qMiArMx5oMF9o8R9psJ2l7eEsNdleYUAcGNVd7HHf7rPdqi+YIeVa5CjgnqKh5Ssf7vUMDM1LSMgdJm0f63QdJu4ZISebI2ofZzAjbbdf6vJQzcvAGE8HXN6gYLC3GmWp16BlHacvaGMnoafs0NaXDQjEj8sF1BJQHWWtHafvHCZt3Wmz4K35YWw0E1MSzsfCQBjQiNeOR5qY2Rzi51xnbyDpLt2eX01OTpBKxFeTiVnVzFcSSJDLiNgXWNmc4Rleo1dY2tELyRCIgxPLxcAYDsgXjkfVi4TUS8eTDEkWjUkVygSRB8JYkEhZkwgW0MhWUIiTS8XRyQMRiMMSiQMSSEIUSwRXzodZz0dAF03G1s3HFs4H1QvGUsmEFYqFF4tFk0pEFApEkkkDkIiDUwpElAtFU8uFlAvF1MwGFMvF1QvF1QwFYRSJyWGX/Q+kehuAAAAAElFTkSuQmCC')\",\n      \"backgroundSize\": \"cover\",\n      \"display\": \"block\"\n    }\n  }), \"\\n  \", mdx(\"img\", {\n    parentName: \"span\",\n    \"className\": \"gatsby-resp-image-image\",\n    \"alt\": \"UniFi Pro Access Point size comparison\",\n    \"title\": \"UniFi Pro Access Point size comparison\",\n    \"src\": \"/static/f76cde014cb5dcf9b3a76d4b9dc7d950/083f8/unifi-access-point-backs.png\",\n    \"srcSet\": [\"/static/f76cde014cb5dcf9b3a76d4b9dc7d950/ad4a5/unifi-access-point-backs.png 205w\", \"/static/f76cde014cb5dcf9b3a76d4b9dc7d950/74ab4/unifi-access-point-backs.png 410w\", \"/static/f76cde014cb5dcf9b3a76d4b9dc7d950/083f8/unifi-access-point-backs.png 820w\", \"/static/f76cde014cb5dcf9b3a76d4b9dc7d950/f1720/unifi-access-point-backs.png 1024w\"],\n    \"sizes\": \"(max-width: 820px) 100vw, 820px\",\n    \"style\": {\n      \"width\": \"100%\",\n      \"height\": \"100%\",\n      \"margin\": \"0\",\n      \"verticalAlign\": \"middle\",\n      \"position\": \"absolute\",\n      \"top\": \"0\",\n      \"left\": \"0\"\n    },\n    \"loading\": \"lazy\"\n  }), \"\\n    \"), mdx(\"p\", null, \"The new Wi-Fi 6 access point feels a lot more substantial between the two. The new Wi-Fi 6 version increased in weight by about 10 ounces over the previous Pro to ~20oz despite the same outer dimensions.\"), mdx(\"blockquote\", null, mdx(\"p\", {\n    parentName: \"blockquote\"\n  }, \"The secondary RJ45 port and USB ports were eliminated from the back of the U6-Pro-US\")), mdx(\"p\", null, \"Lastly, the new Wi-Fi 6 Pro requires PoE+ where the UAP-Pro could use either PoE or PoE+.\"), mdx(\"h2\", {\n    \"id\": \"real-world-throughput-speed-tests\"\n  }, \"Real-world Throughput Speed Tests\"), mdx(\"p\", null, \"I setup iPerf3 in server mode on a gigabit hardwired PC on my local network and used the iPerf mobile app with 5 streams to test the Wi-Fi speed of new U6-Pro-US, the UAP-AC-Pro, and the UAP-AC-HD using an iPhone 13 Pro (which supports 802.11ax and 2x2 MIMO). iPerf runs within the local network so it is unaffected by variances in your WAN internet speeds.\"), mdx(\"table\", null, mdx(\"thead\", {\n    parentName: \"table\"\n  }, mdx(\"tr\", {\n    parentName: \"thead\"\n  }, mdx(\"th\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"Access Point\"), mdx(\"th\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"Average\"), mdx(\"th\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"Max\"))), mdx(\"tbody\", {\n    parentName: \"table\"\n  }, mdx(\"tr\", {\n    parentName: \"tbody\"\n  }, mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"U6-Pro-US\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"778 Mbits/s\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"820 Mbits/s\")), mdx(\"tr\", {\n    parentName: \"tbody\"\n  }, mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"UAP-AC-Pro\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"508 Mbits/s\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"538 Mbits/s\")), mdx(\"tr\", {\n    parentName: \"tbody\"\n  }, mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"UAP-AC-HD\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"438 Mbits/s\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"598 Mbits/s\")))), mdx(\"p\", null, \"The U6-Pro-US access point has a 52% 5ghz wifi speed increase over the previous Unifi Pro based on the iPerf speed tests.\"), mdx(\"p\", null, \"These tests were conducted with 5ghz Wi-Fi channel width set to VHT80 on the the UAP access points and HE80 on the U6 so that speed shouldn't be constrained by wifi channel width.\"), mdx(\"span\", {\n    \"className\": \"gatsby-resp-image-wrapper\",\n    \"style\": {\n      \"position\": \"relative\",\n      \"display\": \"block\",\n      \"marginLeft\": \"auto\",\n      \"marginRight\": \"auto\",\n      \"maxWidth\": \"411px\"\n    }\n  }, \"\\n      \", mdx(\"span\", {\n    parentName: \"span\",\n    \"className\": \"gatsby-resp-image-background-image\",\n    \"style\": {\n      \"paddingBottom\": \"97.56097560975611%\",\n      \"position\": \"relative\",\n      \"bottom\": \"0\",\n      \"left\": \"0\",\n      \"backgroundImage\": \"url('data:image/jpeg;base64,/9j/2wBDAAMCAgMCAgMDAwMEAwMEBQgFBQQEBQoHBwYIDAoMDAsKCwsNDhIQDQ4RDgsLEBYQERMUFRUVDA8XGBYUGBIUFRT/2wBDAQMEBAUEBQkFBQkUDQsNFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBT/wgARCAAUABQDASIAAhEBAxEB/8QAGQABAQADAQAAAAAAAAAAAAAAAAQCAwUI/8QAFQEBAQAAAAAAAAAAAAAAAAAAAAH/2gAMAwEAAhADEAAAAfOE27AgBdYWcAS//8QAHBAAAwACAwEAAAAAAAAAAAAAAQIDBBIABRMU/9oACAEBAAEFAqEaoDSVp+Tst8mXz5Mxanq88x0kOzvwnY//xAAVEQEBAAAAAAAAAAAAAAAAAAAQAf/aAAgBAwEBPwEh/8QAFREBAQAAAAAAAAAAAAAAAAAAEAH/2gAIAQIBAT8BKf/EACQQAAEDAgQHAAAAAAAAAAAAAAEAAhESMQNhktEEFCIyQXFy/9oACAEBAAY/AjBw5zp2TWgtH1CpkH0g7l3OabO6igDw2JpKmKckG+LXK73a3bqTdf/EAB4QAAICAgIDAAAAAAAAAAAAAAERACExcUFRgdHw/9oACAEBAAE/IaDiHz2gRqckvRiEaLTZsTTRmWqZg8wC14himNEhNLcFOxaIrPB7mW/chykZWSZ//9oADAMBAAIAAwAAABAzyAD/xAAVEQEBAAAAAAAAAAAAAAAAAAAQMf/aAAgBAwEBPxAg/8QAFREBAQAAAAAAAAAAAAAAAAAAEDH/2gAIAQIBAT8QKP/EABwQAQADAQEBAQEAAAAAAAAAAAEAESExQXFhsf/aAAgBAQABPxChspysBUwvWVWyw7oQ4qNlT6tx4NDXBL7UDJJrT6Np0T7CmcArenH5dRSKC8FFVpfItmCphOAEPWHrDqmmabu1/ne4R9zVC1Xqz//Z')\",\n      \"backgroundSize\": \"cover\",\n      \"display\": \"block\"\n    }\n  }), \"\\n  \", mdx(\"img\", {\n    parentName: \"span\",\n    \"className\": \"gatsby-resp-image-image\",\n    \"alt\": \"screenshot of iPerf iPhone speed test on U6-Pro-US\",\n    \"title\": \"screenshot of iPerf iPhone speed test on U6-Pro-US\",\n    \"src\": \"/static/5fba0b67cf30309db05619131ff762f2/c460c/iperf-speed-test-U6-Pro-US.jpg\",\n    \"srcSet\": [\"/static/5fba0b67cf30309db05619131ff762f2/bd2b6/iperf-speed-test-U6-Pro-US.jpg 205w\", \"/static/5fba0b67cf30309db05619131ff762f2/ceeba/iperf-speed-test-U6-Pro-US.jpg 410w\", \"/static/5fba0b67cf30309db05619131ff762f2/c460c/iperf-speed-test-U6-Pro-US.jpg 411w\"],\n    \"sizes\": \"(max-width: 411px) 100vw, 411px\",\n    \"style\": {\n      \"width\": \"100%\",\n      \"height\": \"100%\",\n      \"margin\": \"0\",\n      \"verticalAlign\": \"middle\",\n      \"position\": \"absolute\",\n      \"top\": \"0\",\n      \"left\": \"0\"\n    },\n    \"loading\": \"lazy\"\n  }), \"\\n    \"), mdx(\"h2\", {\n    \"id\": \"final-thoughts\"\n  }, \"Final Thoughts\"), mdx(\"p\", null, \"The Wi-Fi 6 version of the Ubiquiti UniFi Pro router, U6-Pro-US, was a worthwhile upgrade over my previous generation UAP-AC-Pro and even HD access points. The 52% performance increase in Wi-Fi speed on 5ghz puts the new access point on par with wired devices on a gigabit network in terms of download speeds.\"), mdx(\"p\", null, \"Reddit user /u/mccanntech came up with this excellent specifications table showing the detailed differences of the UniFi omnidirectional access points. Visit \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://evanmccann.net/blog/ubiquiti/unifi-comparison-charts\"\n  }, \"their blog\"), \" for more comparison tables like this one below.\"), mdx(\"span\", {\n    \"className\": \"gatsby-resp-image-wrapper\",\n    \"style\": {\n      \"position\": \"relative\",\n      \"display\": \"block\",\n      \"marginLeft\": \"auto\",\n      \"marginRight\": \"auto\",\n      \"maxWidth\": \"820px\"\n    }\n  }, \"\\n      \", mdx(\"span\", {\n    parentName: \"span\",\n    \"className\": \"gatsby-resp-image-background-image\",\n    \"style\": {\n      \"paddingBottom\": \"66.34146341463415%\",\n      \"position\": \"relative\",\n      \"bottom\": \"0\",\n      \"left\": \"0\",\n      \"backgroundImage\": \"url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAANCAIAAAAmMtkJAAAACXBIWXMAAAsTAAALEwEAmpwYAAACtklEQVQozyWR60/aUBiH+31TUC4tvbe2XG1AwBgvMQqCIMxFy0XddIvDLVuWLXHKvZUW3aJL5JwWlGT/7E63kzfvt+f8Li/GMIwgiFJMiaRWwsllOZFWVtaU1ayyshNKboZTW/HVXHQ5Kyc2iMASE0jjviU6kCLxFEkksVgslkqlMo2rfWO6r9ml/hjt6JnFV2yxZglVS6pBtmLJtTFB/eLmb/2zt5zvjnTfMV4DCwaDyeTS5vn1TsfONUfZ5ijXAuG3gD6ArAoYFXIVSB9CXrW8hBlwDTwvbsh5wz8zINx9LBqNptOp7Y+tfG+Sb8NcG+Q7MPbOUUayQs2S6jZXteT6GKeQoIm7TdY/JD1D2nfjwMvpdO5Tq9S3Sx1QaI9e9WDsbCRW4EINiDUQPIJ8FQRrVoAZ8n6TcA94wqQ8Jovr2NK/l7loFvuTYgfk22Cvi2DkFgWGvPOFhZwvVCycGrIeE3cZSJlwG7RXxxRFicfj241mUUMw/A9HzxBm81UnMDLPol2xcQpVNfTNOjDuMh3bi4uLiUQi27gqa5NSFxba4FXPUt5D0UkLF+pW5MQW61aobpPsUCAMYu6GJ5CswRGa03YkEtlCyvrzbtfa7dp7PVs+tYmDMaXa5CHSt+mKzVXGPmKIzxjelwPk3D9r4C4NSyaT6+trmcZ1qT9Bhe22nYmcAg4dSQXsIeBVyKhAUCHKTM8ZfteA8TnJyXnNadvJ/OFHsWsXW4+F1mO5M1pEbasgVB+Fj0DkCIjV0YI6ImiD8Qxwt876B2j4QB9DZCIez1y0Xpt/9vWnsv50MHhWzp/4+lQ+eZJOnoNvpuLxs3w85aXfMvUgkvcSfS8zDyHuJ4bakiVpo94ofDOKX/XsF237sxZWNW5Pp4oaWdDwfJ/d0+SyLkeaUelaoC4F8lKgLyXm+19EEexwrCNBiQAAAABJRU5ErkJggg==')\",\n      \"backgroundSize\": \"cover\",\n      \"display\": \"block\"\n    }\n  }), \"\\n  \", mdx(\"img\", {\n    parentName: \"span\",\n    \"className\": \"gatsby-resp-image-image\",\n    \"alt\": \"UniFi Access Point Specification Table\",\n    \"title\": \"UniFi Access Point Specification Table\",\n    \"src\": \"/static/a0fc15db34218501f6e9fadfc3cf58b0/083f8/unifi-ap-specs-comparison-from-u-umccanntech.png\",\n    \"srcSet\": [\"/static/a0fc15db34218501f6e9fadfc3cf58b0/ad4a5/unifi-ap-specs-comparison-from-u-umccanntech.png 205w\", \"/static/a0fc15db34218501f6e9fadfc3cf58b0/74ab4/unifi-ap-specs-comparison-from-u-umccanntech.png 410w\", \"/static/a0fc15db34218501f6e9fadfc3cf58b0/083f8/unifi-ap-specs-comparison-from-u-umccanntech.png 820w\", \"/static/a0fc15db34218501f6e9fadfc3cf58b0/b3178/unifi-ap-specs-comparison-from-u-umccanntech.png 1230w\", \"/static/a0fc15db34218501f6e9fadfc3cf58b0/cb690/unifi-ap-specs-comparison-from-u-umccanntech.png 1640w\", \"/static/a0fc15db34218501f6e9fadfc3cf58b0/b577d/unifi-ap-specs-comparison-from-u-umccanntech.png 3768w\"],\n    \"sizes\": \"(max-width: 820px) 100vw, 820px\",\n    \"style\": {\n      \"width\": \"100%\",\n      \"height\": \"100%\",\n      \"margin\": \"0\",\n      \"verticalAlign\": \"middle\",\n      \"position\": \"absolute\",\n      \"top\": \"0\",\n      \"left\": \"0\"\n    },\n    \"loading\": \"lazy\"\n  }), \"\\n    \"));\n}\n;\nMDXContent.isMDXComponent = true;","excerpt":"Ubiquiti recently released their Wi-Fi 6 access points and I was curious to see if it would increase my iPhone 13 Pro Wi-Fi speeds over the…","fields":{"slug":"/speed-testing-ubiquiti-unifi-wifi-6-pro-vs-uap-ac-pro/","type":"posts"},"headings":[{"value":"Differences between U6-Pro-US and UAP-AC-Pro","depth":2},{"value":"Speed differences in Access Points (U6-Pro-US vs UAP-AC-Pro)","depth":3},{"value":"Radio changes","depth":3},{"value":"Physical differences","depth":3},{"value":"Real-world Throughput Speed Tests","depth":2},{"value":"Final Thoughts","depth":2}],"frontmatter":{"title":"Speed Test Comparison Unifi Wi-Fi 6 Pro vs UAP-AC-Pro access points","description":"Ubiquiti released their new Wi-Fi 6 access points including their new U6-Pro-US so I decided to upgrade and test the speeds to see how the Unifi Wi-Fi 6 Pro compares to the UAP-AC-Pro and UAP-AC-HD using an iPhone 13 pro and iPerf3.","link":null,"date":"2022-02-23T00:00:00.000Z","tags":["networking","smart home","unifi","ubiquiti","post"],"draft":null,"hide":null,"cover":{"childImageSharp":{"fluid":{"base64":"data:image/jpeg;base64,/9j/2wBDAAMCAgMCAgMDAwMEAwMEBQgFBQQEBQoHBwYIDAoMDAsKCwsNDhIQDQ4RDgsLEBYQERMUFRUVDA8XGBYUGBIUFRT/2wBDAQMEBAUEBQkFBQkUDQsNFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBT/wgARCAAKABQDASIAAhEBAxEB/8QAFwABAQEBAAAAAAAAAAAAAAAABgACB//EABUBAQEAAAAAAAAAAAAAAAAAAAQF/9oADAMBAAIQAxAAAAE9twSJMRXMo9j/xAAYEAADAQEAAAAAAAAAAAAAAAACAwQBBf/aAAgBAQABBQJYV0EkGZsk3U1PQARBRaL7HMXf/8QAGhEAAgIDAAAAAAAAAAAAAAAAAQIAAxETYf/aAAgBAwEBPwHVU1quFwRBX2f/xAAbEQACAwADAAAAAAAAAAAAAAABAgAEIRIjwf/aAAgBAgEBPwGp2VGRcB8M4qMn/8QAIxAAAgIBAgYDAAAAAAAAAAAAAQIAAxEhQQQSIiMxU3Oh0f/aAAgBAQAGPwI5YNXn1/UNT08rDUEbidisGv5F/ZgKAB4AiYOOracWiuyqtrAAHQT/xAAcEAEAAgIDAQAAAAAAAAAAAAABABEhMUFhcZH/2gAIAQEAAT8hWtmPMo2uB5Yp47ODyCs+xY19h1rOKDyP7NA2qyKj6eBegn//2gAMAwEAAgADAAAAEDwv/8QAGBEBAQEBAQAAAAAAAAAAAAAAAREAYUH/2gAIAQMBAT8QjHRE6Jw9ywz/xAAbEQEAAgIDAAAAAAAAAAAAAAABESEAMUFh4f/aAAgBAgEBPxBgKuIRwJcTsxTtXfmf/8QAHRABAQACAwADAAAAAAAAAAAAAREAMSFBYVGBof/aAAgBAQABPxCcluGUoi5FWl/MOhEEKyvZ2UpecXALFZbSEm/jvDxQAIfBr6xGcmiR5GbPMGGQRHADA8M//9k=","aspectRatio":2,"src":"/static/e6fef9249f650997c917648397a03118/60a5f/u6-pro-us-vs-uap-ac-pro.jpg","srcSet":"/static/e6fef9249f650997c917648397a03118/3e5eb/u6-pro-us-vs-uap-ac-pro.jpg 192w,\n/static/e6fef9249f650997c917648397a03118/2880b/u6-pro-us-vs-uap-ac-pro.jpg 384w,\n/static/e6fef9249f650997c917648397a03118/60a5f/u6-pro-us-vs-uap-ac-pro.jpg 768w","srcWebp":"/static/e6fef9249f650997c917648397a03118/25278/u6-pro-us-vs-uap-ac-pro.webp","srcSetWebp":"/static/e6fef9249f650997c917648397a03118/a278a/u6-pro-us-vs-uap-ac-pro.webp 192w,\n/static/e6fef9249f650997c917648397a03118/2474b/u6-pro-us-vs-uap-ac-pro.webp 384w,\n/static/e6fef9249f650997c917648397a03118/25278/u6-pro-us-vs-uap-ac-pro.webp 768w","sizes":"(max-width: 768px) 100vw, 768px","presentationWidth":768,"presentationHeight":382}}}}},{"id":"5154530f-459f-5dc0-9435-d56b4f5feecf","body":"var _excluded = [\"components\"];\nfunction _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }\nfunction _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }\nfunction _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }\n/* @jsxRuntime classic */\n/* @jsx mdx */\n\nvar _frontmatter = {\n  \"title\": \"UniFi Talk Review with UTP-Touch VoIP phone\",\n  \"cover\": \"./utp-touch-unifi-talk-review.jpg\",\n  \"date\": \"2022-02-16T00:00:00.000Z\",\n  \"description\": \"This Ubiquiti review covers my experience using UniFi Talk on the Ubiquiti Dream Machine to provide VoIP phone service for my home using the UTP-Touch VoIP phone.\",\n  \"tags\": [\"networking\", \"smart home\", \"unifi\", \"ubiquiti\", \"post\"]\n};\nvar makeShortcode = function makeShortcode(name) {\n  return function MDXDefaultShortcode(props) {\n    console.warn(\"Component \" + name + \" was not imported, exported, or provided by MDXProvider as global scope\");\n    return mdx(\"div\", props);\n  };\n};\nvar Embed = makeShortcode(\"Embed\");\nvar layoutProps = {\n  _frontmatter: _frontmatter\n};\nvar MDXLayout = \"wrapper\";\nreturn function MDXContent(_ref) {\n  var components = _ref.components,\n    props = _objectWithoutProperties(_ref, _excluded);\n  return mdx(MDXLayout, _extends({}, layoutProps, props, {\n    components: components,\n    mdxType: \"MDXLayout\"\n  }), mdx(\"p\", null, \"With the release of the UDM-Pro SE (Unifi Dream Machine Pro, special edition) I had my excuse to upgrade my USG + CloudKey combination to the latest Ubiquiti networking gear. Along with that comes access to UniFi Talk which allows me to provide VoIP phone service to my home at a cheaper cost than the pricing my ISP offers.\"), mdx(\"p\", null, \"This Unifi Talk review will cover features of the UniFi talk app, service, and setup--including number porting.\"), mdx(Embed, {\n    src: \"https://www.youtube.com/embed/01Vq2fnCWQ0\",\n    mdxType: \"Embed\"\n  }), mdx(\"h2\", {\n    \"id\": \"why-i-chose-unifi-talk\"\n  }, \"Why I chose UniFi Talk\"), mdx(\"blockquote\", null, mdx(\"p\", {\n    parentName: \"blockquote\"\n  }, \"$20 less for more features than I had before\")), mdx(\"p\", null, \"My ISP-provided VoIP service was reliable, but provided just basic phone service and was over $30/mo with taxes. UniFi talk provides additional office-level phone features for $9.99/mo USD per line. I felt if Ubiquiti's VoIP service could be as seamless \", mdx(\"em\", {\n    parentName: \"p\"\n  }, \"with even more features\"), \" it would make sense to port my phone service to UniFi Talk.\"), mdx(\"blockquote\", null, mdx(\"p\", {\n    parentName: \"blockquote\"\n  }, \"What was holding me back?\")), mdx(\"p\", null, \"The advantage my ISP had was the VoIP modem they provided backfed my home phone wiring to allow all my normal, non-VoIP, phones, including my DECT cordless phones, to work over VoIP using the normal phone line in my house. I didn't have to upgrade my phone wiring to CAT6 or use special VoIP phones.\"), mdx(\"p\", null, \"Until recently, Ubiquiti only provided service to their corded VoIP touch screen phones which use PoE over CAT6 which would have made it inconvenient for my situation since I still wanted to use my DECT cordless phones. This changed now that they started to offer (in early access) an ATA device that allows me to backfeed my normal house wiring the same way.\"), mdx(\"p\", null, \"So I jumped on getting the ATA device (analog phone adapter) to use UniFi talk with my older analog and DECT phones and also purchased the Ubiquiti UTP-Touch phone for my home office to use extra UniFi talk features.\"), mdx(\"h2\", {\n    \"id\": \"unifi-talk-features\"\n  }, \"Unifi Talk Features\"), mdx(\"p\", null, \"Below are my favorite 4 features in UniFi Talk.\"), mdx(\"ol\", null, mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Smart Assistant\")), mdx(\"p\", null, \"Smart assistant lets you setup a voice prompt/greeting tree that you can use to direct calls to numbers and extensions. The \", mdx(\"em\", {\n    parentName: \"p\"\n  }, \"hidden feature\"), \" I use it for is that this effectively \", mdx(\"em\", {\n    parentName: \"p\"\n  }, \"blocks most robocall spam calls\"), \". Most of the spamming robot autodialers trying to reach you for your car's extended warranty or duct cleaning don't have logic to press a number to continue so these calls do not ring through.\"), mdx(\"blockquote\", null, mdx(\"p\", {\n    parentName: \"blockquote\"\n  }, \"I've been able to block all robocalls since setting up UniFi Smart Assistant.\")), mdx(\"ol\", {\n    \"start\": 2\n  }, mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"UniFi Talk Pricing\")), mdx(\"p\", null, \"It is very competitive at $9.99 vs the ~$30 per month I was paying my ISP, but the same price as something like Ooma, but cheaper than RingCentral. To me, it has a better feature set than Ooma, but maybe not as many features as RingCentral. For home use, I don't need some of the more expensive features RingCentral offers.\"), mdx(\"p\", null, \"With everything getting more expensive every year finding ways like this to save $20 per month just made sense to me.\"), mdx(\"ol\", {\n    \"start\": 3\n  }, mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Call Recording\")), mdx(\"p\", null, \"Where legal to do so you can automatically record all your calls which is great to protect yourself or serve as something you can refer back to if you don't want to take notes. \"), mdx(\"ol\", {\n    \"start\": 4\n  }, mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Voicemail\")), mdx(\"p\", null, \"UniFi Talk voicemail can send you email notifications and even transcribe your voicemails allowing you to quickly read the contents instead of listening or have to rewind for something like a phone number spoken too quickly on the recording.\"), mdx(\"h3\", {\n    \"id\": \"unifi-talk-cons\"\n  }, \"UniFi Talk Cons\"), mdx(\"ul\", null, mdx(\"li\", {\n    parentName: \"ul\"\n  }, mdx(\"p\", {\n    parentName: \"li\"\n  }, \"Older alarm panels that use a landline to dial out to a central monitoring station, like ADT, often require a \", mdx(\"em\", {\n    parentName: \"p\"\n  }, \"Qualified Managed Facility Voice Network\"), \" which Unifi Talk does not qualify as. You'd need to use a dual path system that uses internet and cellular under Ubiquiti's VoIP offering. \")), mdx(\"li\", {\n    parentName: \"ul\"\n  }, mdx(\"p\", {\n    parentName: \"li\"\n  }, \"No UniFi talk mobile app.\"))), mdx(\"h2\", {\n    \"id\": \"unifi-talk-setup-with-utp-touch\"\n  }, \"UniFi Talk Setup with UTP-Touch\"), mdx(\"p\", null, \"Setup is pretty simple with a UDM-Pro or UDM-Pro SE. These are their Dream Machine gateways that allow you to run UniFi Network, Talk, and Protect on a central box along with an integrated 8 port switch. The SE version's switched ports are all PoE so I was able to use those to directly power the UTP-Touch.\"), mdx(\"p\", null, \"The steps consist of\"), mdx(\"ol\", null, mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Plugging in a UniFi Talk device to a PoE port on the dream machine (or PoE switch).\"), mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Enabling the UniFi talk app in the Unifi Management Portal and beginning the guided setup wizard.\")), mdx(\"p\", null, \"This starts a 15 day free trial that comes with 2 temporary phone numbers that you associate with your UTP-Touch phone or ATA device.\"), mdx(\"p\", null, \"After, if you want to port your number, you can start that process in the UniFi talk UI.\"), mdx(\"h3\", {\n    \"id\": \"porting-your-phone-number-to-unifi-talk\"\n  }, \"Porting your phone number to UniFi Talk\"), mdx(\"p\", null, \"Ubiquiti makes it pretty easy to initiate number porting, but it is a surprisingly manual process.\"), mdx(\"span\", {\n    \"className\": \"gatsby-resp-image-wrapper\",\n    \"style\": {\n      \"position\": \"relative\",\n      \"display\": \"block\",\n      \"marginLeft\": \"auto\",\n      \"marginRight\": \"auto\",\n      \"maxWidth\": \"745px\"\n    }\n  }, \"\\n      \", mdx(\"span\", {\n    parentName: \"span\",\n    \"className\": \"gatsby-resp-image-background-image\",\n    \"style\": {\n      \"paddingBottom\": \"33.170731707317074%\",\n      \"position\": \"relative\",\n      \"bottom\": \"0\",\n      \"left\": \"0\",\n      \"backgroundImage\": \"url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAHCAYAAAAIy204AAAACXBIWXMAAA7DAAAOwwHHb6hkAAAA5klEQVQoz5WPy27CMBBFvcNOSGzHz5A4WKhS9qCG//+y2zEGBCqLdnF0Zuw7Iw1L+Qszke4c0glxyhjnjPgPSt4fjmDbtuF8OeObvF2vWNcVKSUsy5FY/kzOGVOihW2n0fZEV9nLAb2y6JQhV2ptn/2j7t5qmqFZJjjHE8HBeWF39yd2L7z3ZQcLMcJ7jxADOSAEcvBwzsETITg4axGjrzn6H2mmzJVMjOPtrWCMAVNKQykFrcnEMOha05vSBsrNUDaRp9qX7CP/4oKUkk4WAr9pbueLvQTXC/hwIieItsfnfKVpGvwA6+mr1eMPPTkAAAAASUVORK5CYII=')\",\n      \"backgroundSize\": \"cover\",\n      \"display\": \"block\"\n    }\n  }), \"\\n  \", mdx(\"img\", {\n    parentName: \"span\",\n    \"className\": \"gatsby-resp-image-image\",\n    \"alt\": \"UniFi number porting screen\",\n    \"title\": \"UniFi number porting screen\",\n    \"src\": \"/static/49f1817941e86c85267aa0c2dc3886e2/6ce55/number-porting.png\",\n    \"srcSet\": [\"/static/49f1817941e86c85267aa0c2dc3886e2/ad4a5/number-porting.png 205w\", \"/static/49f1817941e86c85267aa0c2dc3886e2/74ab4/number-porting.png 410w\", \"/static/49f1817941e86c85267aa0c2dc3886e2/6ce55/number-porting.png 745w\"],\n    \"sizes\": \"(max-width: 745px) 100vw, 745px\",\n    \"style\": {\n      \"width\": \"100%\",\n      \"height\": \"100%\",\n      \"margin\": \"0\",\n      \"verticalAlign\": \"middle\",\n      \"position\": \"absolute\",\n      \"top\": \"0\",\n      \"left\": \"0\"\n    },\n    \"loading\": \"lazy\"\n  }), \"\\n    \"), mdx(\"p\", null, \"In UniFi Talk select Settings, Numbers & Subscriptions, and expand Number Porting Requests. Inside there is a button you click called Port Numbers and it walks you through the initial process. You need to provide proof of ownership of the current number. A bill with your phone number is usually recommended.\"), mdx(\"p\", null, \"The bill is uploaded during the guided setup during the porting request so have that handy. After, a letter of intent needs to be electronically signed.\"), mdx(\"p\", null, \"Ubiquiti support handles the processing with the outgoing provider. They informed me via email that they received the initial request and later when they had arranged the scheduled porting date. It took 14 days in my case to complete.\"), mdx(\"p\", null, \"The support rep from the porting team did a good job keeping me informed of the process.\"), mdx(\"h2\", {\n    \"id\": \"utp-touch\"\n  }, \"UTP-Touch\"), mdx(\"p\", null, \"The UTP-Touch and Touch Max are Ubiquiti's latest touchscreen corded desk phones. It has a familiar iPhone-like virtual touch pad for dialing.\"), mdx(\"span\", {\n    \"className\": \"gatsby-resp-image-wrapper\",\n    \"style\": {\n      \"position\": \"relative\",\n      \"display\": \"block\",\n      \"marginLeft\": \"auto\",\n      \"marginRight\": \"auto\",\n      \"maxWidth\": \"820px\"\n    }\n  }, \"\\n      \", mdx(\"span\", {\n    parentName: \"span\",\n    \"className\": \"gatsby-resp-image-background-image\",\n    \"style\": {\n      \"paddingBottom\": \"74.14634146341463%\",\n      \"position\": \"relative\",\n      \"bottom\": \"0\",\n      \"left\": \"0\",\n      \"backgroundImage\": \"url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAPCAYAAADkmO9VAAAACXBIWXMAAA7DAAAOwwHHb6hkAAAEOklEQVQ4yx2S6U+UVxSH30/V1LYqIjjMwjLAALMAAzKAODKOYBlUCqKjoEVRXNoibhEEcQVZLOKGjo6ICgjKIpZSTZBVsIIUBAVXTIlbayVp+g88fdsPN7/k3OS55zz3CM9+n2Dk+QuGnz3n0egYA4+G6fttiPv9A/Q+6Keju4e7HV20dnSK2cm93vvi6aG9vZ2W5mZqqquosNuprKyipr4BYfT1a4ZFYI8IaR8aoWPoMZ19D2lpbcN26Sr2y5Vcqa7lesMtauoaOWe/yNkLdmxi2svLKT9vo+7mTYbfT/L00z8IL96+ZeTlK+zX6znW+Avl3f1UtXViE0F6owWjZSXmuCSWrdpAwpqNLLGu5UB+IfnHT5IrZtmJUuqbf2bgzV8MTvyBMP7hA49fjrN1916s27PIPHORbQUlFJ46S3R8ClsyS9iaeZTYVWm46cLQhEdxuPhHWsSR7ZevYhOB1xpvcf/VG/pfv0N4Mj7OE7HD9T/swD/SQlxqOpHxa9lzsADzN8kkbs5mS84xlqdm4O4fzgxXDbGJydQ1/0Tp6TJKCwuprm+ia2ycX19MIDydmGDo+TMRkIdcE0LIoji0EbGkpmdiXb8N69b9pOWUkJSeg8qwEKk6lFCzhYprlRQUF1OUl8fVuibR/VN6R8cRuvofMjg2RtGpczi4qwk0xqDUR5KQ8h1pGZmEL/2WzdmlrN60j8AFFoIWLsUnOEL0eIS8giL25x6gvPoGrX2P6Bahwp2eXu4NDLJf9ObooUEbthCFXwhBC2Iwxcajmmtm5bZMEg7sQBsahT4yBoNpMfuOHCR7VwY7163GduECtzt6xO0YRGi4205hmZ3oOCuu2rl46uYiU6qRevjipgrAVW3AJ2wB/vFmXJQa8WMMrEhaQ8amJLJSEziUlkDZmVM0tdyhrbsbISBiEQpfPc7uKiReWhSevkikrsjdPAiZFynW/MW7/x7QIfFUI/fRE70ske1JFoq2LOP09uWUZW/gSukhbtVeQdAajISbFxMcYcRBpsRF4cociYTpDk5iR74oVIG4iQpcRLBEqcVJhAfrdVzabaQ130RbvpnOo1/zoCSZvhMpCGFRS9CEGpnj7o2DixuznJyZPnMWn02bxZQvHJkt80YiQhylShwlCqbOcCbF5EHX4QAeFIdxL1fH4Mko3tdm8GfDTgQ/Px+CfKREBSlwc/dgmoMzU79y4nMHGVO+dGTaTAmOMk+cpXK85BIiPGdzfqOeqgx/bBv9qNjsR12WmYna3Xxs2oOwK8nIiiVG0pbPxxCkw1GuwlnmjreXFyo/NU4eambLlf/X3FUaYszzqNiXyOhpC0PHTIwcNzF61sq7hiwmb+cgmOYHi6IDCAgJRa03oPDW4KUNxEcXiKtcRrivnKw4X+zfG8ixaojQSsVNCKZg7zo+1iQzeSOFT43pTN7J5e+2w/wLRgyu74ljQRAAAAAASUVORK5CYII=')\",\n      \"backgroundSize\": \"cover\",\n      \"display\": \"block\"\n    }\n  }), \"\\n  \", mdx(\"img\", {\n    parentName: \"span\",\n    \"className\": \"gatsby-resp-image-image\",\n    \"alt\": \"UTP-Touch phone\",\n    \"title\": \"UTP-Touch phone\",\n    \"src\": \"/static/54d83589498f88b1b6b53d953fa13cf7/083f8/utp-touch.png\",\n    \"srcSet\": [\"/static/54d83589498f88b1b6b53d953fa13cf7/ad4a5/utp-touch.png 205w\", \"/static/54d83589498f88b1b6b53d953fa13cf7/74ab4/utp-touch.png 410w\", \"/static/54d83589498f88b1b6b53d953fa13cf7/083f8/utp-touch.png 820w\", \"/static/54d83589498f88b1b6b53d953fa13cf7/85caa/utp-touch.png 852w\"],\n    \"sizes\": \"(max-width: 820px) 100vw, 820px\",\n    \"style\": {\n      \"width\": \"100%\",\n      \"height\": \"100%\",\n      \"margin\": \"0\",\n      \"verticalAlign\": \"middle\",\n      \"position\": \"absolute\",\n      \"top\": \"0\",\n      \"left\": \"0\"\n    },\n    \"loading\": \"lazy\"\n  }), \"\\n    \"), mdx(\"p\", null, \"The sound from the handset is good and it includes a speakerphone which also has nice sound quality. Nobody has complained on the other side about the sound clarity from the microphone even when on speaker so far. On the right side you have a USB-C port and place to plug in a headset for hands-free calling without using the speakerphone.\"), mdx(\"p\", null, \"The touch screen is responsive and the 5\\\" model I have is 720x1080 HD though the Touch Max has a 7\\\" screen in a horizontal 1280x800 HD resolution. Having these nice screens are important because\"), mdx(\"blockquote\", null, mdx(\"p\", {\n    parentName: \"blockquote\"\n  }, \"You can view UniFi Protect cameras on the phone screen\")), mdx(\"p\", null, \"When I add Ubiquiti security cameras I will be able to view their live feed on the UTP-Touch's screen.\"), mdx(\"p\", null, \"The phone draws around 3.02 watts of PoE power according to the switch's port status power monitoring.\"), mdx(\"h3\", {\n    \"id\": \"call-forwarding\"\n  }, \"Call Forwarding\"), mdx(\"p\", null, \"In the UTP-Touch you can set your status to Available, Do Not Disturb, or Redirect. When using Redirect you can choose to forward calls to another number like your cell phone.\"), mdx(\"span\", {\n    \"className\": \"gatsby-resp-image-wrapper\",\n    \"style\": {\n      \"position\": \"relative\",\n      \"display\": \"block\",\n      \"marginLeft\": \"auto\",\n      \"marginRight\": \"auto\",\n      \"maxWidth\": \"751px\"\n    }\n  }, \"\\n      \", mdx(\"span\", {\n    parentName: \"span\",\n    \"className\": \"gatsby-resp-image-background-image\",\n    \"style\": {\n      \"paddingBottom\": \"114.6341463414634%\",\n      \"position\": \"relative\",\n      \"bottom\": \"0\",\n      \"left\": \"0\",\n      \"backgroundImage\": \"url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAXCAYAAAALHW+jAAAACXBIWXMAAA7DAAAOwwHHb6hkAAAE+klEQVQ4y11V6VMaeRTUVNatpGrzZVObrFfUYFQuBxiO4QZBQO7hkBsBUQQNJiqK0U1IKruVqnzZ1Fb+3d5mktW4H7p+A8z0vNev+zExOTmJuflFqJZXsbi4jGcLz7G4tKxgfD0/t4D5+f/wjJjH7OwsZmamMT09jd+fPsX9+/cxMTGBqZ9/wsSYcGFRBZNog1ZnIATU6i3s7nbR758hkchAvaaD0SjCZDJBFEWYzWZYLCJsNouChw8fKoT37t3jScIxic3uhsksKbBKbkj87HT5YOdpsdhgtY5hhY2nzWb9Dgt/t+HRo0c/EPIiGI6iUm8jX6wjX9pBrlhDNl9GLl9CfnuMMrK5IuRMDum0jGQyycoTiEYjCp48+e0uYWgrjnb3CI12B639LprtA+y0Omi09nm2Ud/ZVWSoVBsol2soFisoFArIZmXIcoo6z90ljKVkxPlms02CaB23bYPd6YXbs/EdPrjdXgUeD+F289oFt8uBcCiI5WXVXUI5tw2LJMFMoWu1KnZ2Goquc7NLWHimIhaxsLCApaVvp2gWEQxsQMXPNj6j1WruEm6XyvAFNiEYDNhi+5FIHGqNQBut4MULDVZXtVCrNdBovkEUTcowdJo1ODgcQVi/S1iq1hFNpmGgLdweF5xOBzYC41a9fLuAlZUx6RpJ1dDr9dDptFhf19FOKzCLRtrJ8D/C2g4CkSiri+DTp494N7rGaPQW70cjxGIJvsCLMJ3gcrqp1zI9aeS001A9X8S6Xqt4cuznMRTCQqWGWDqHdZp3K5pidRGSp2mPHOKJFKKxOFKpDDY2gqxMgMEgwGRUs/o1GAS94sdxWm4Ic6U6MoUqze1AJpfhwym2b8aLcatMyZpGx8pWFP0EQSCpEQZrFhqtnt+tKnpOTU0pbSuEWRo5msrD7/fh69d/8OHDB/qsjMBGBIl4Bhl5G0lGUBQt1HOZeuqhl674QoFTn4Ek2fDgwYNbwlSmgEqzA8nlZUJqSKYLKFea2GZySuUGDd1Etd5k9SSmX1OyjEQyishWmEV44fW6b/KsEIap2/u//sbRyRv0ji9w0L9A++A1mnvHTE8PeweHODkboHd0hPZ+hzGtIZPPIRQOweGyw+N1Mc+/3BJuJWRUGgfKtvH6ArwpgGAojnAkifBWAsVyGZdvBshlM0yIGw6HnZ5dV7QTOCAjffn48a8/VBhLIVtocWIWDIenGF6e4+XxMU5PX6PVbOJieIYvXz4zx0W26GdFXlisZqU6q2QlJDzlXrwh9Acj2OsNeEMIm+EkK87T6AUk5DKS2SpK9V1cXl/jsH+Ml69O0Ou/ROewi3qzATmf5b1xzHDp3hA63D5cvf+Mg6MB9rqnaHVeUbs+ZeiiWO+g1urh5PycW6jN9ivIbueRSCURioQZ2Q34ggEsqVS3hE6PH9VGj/k1IbAZpW5RPiBzHxaV6rWUIsHJ+vmwnbF0ctNIdokduTjQLTg8HqyqtbeEktODYo1D0Yi4vhpicHaCU2IwGCAtZ7GmM7KaKAcVhMvD1eX18PQoRA6vD77NMNR6wy2hkftvr3cGq82v6Ojxx9hGkvmWGckiB1ZDa6+Lg8O+YqHdDvVr7aHIhZveriKWKUFrsNwSarj7BpcfcXz6B87ffMTF1Z84v/6Gk+EIZ5fvMLwe4fzqLV4PLjmUE3q0i/LOPtL8y4jKRQhmB7fNJP4FX9Ub/EY1qFAAAAAASUVORK5CYII=')\",\n      \"backgroundSize\": \"cover\",\n      \"display\": \"block\"\n    }\n  }), \"\\n  \", mdx(\"img\", {\n    parentName: \"span\",\n    \"className\": \"gatsby-resp-image-image\",\n    \"alt\": \"Call forwarding\",\n    \"title\": \"Call forwarding\",\n    \"src\": \"/static/7f53f48aaee7876bd24f2117068e72c8/c251d/unifi-call-forwarding.png\",\n    \"srcSet\": [\"/static/7f53f48aaee7876bd24f2117068e72c8/ad4a5/unifi-call-forwarding.png 205w\", \"/static/7f53f48aaee7876bd24f2117068e72c8/74ab4/unifi-call-forwarding.png 410w\", \"/static/7f53f48aaee7876bd24f2117068e72c8/c251d/unifi-call-forwarding.png 751w\"],\n    \"sizes\": \"(max-width: 751px) 100vw, 751px\",\n    \"style\": {\n      \"width\": \"100%\",\n      \"height\": \"100%\",\n      \"margin\": \"0\",\n      \"verticalAlign\": \"middle\",\n      \"position\": \"absolute\",\n      \"top\": \"0\",\n      \"left\": \"0\"\n    },\n    \"loading\": \"lazy\"\n  }), \"\\n    \"), mdx(\"p\", null, \"To get there, swipe down, press My Status, and select Redirect.\"), mdx(\"h3\", {\n    \"id\": \"other-fun-things\"\n  }, \"Other fun things\"), mdx(\"p\", null, \"You can customize from a long set of choices your ring and hold music. There is a 5mp camera at the top of the phone that currently isn't enabled, but I hope it can be used by UniFi Protect as another security camera or perhaps for video conferencing calls in Microsoft Teams or Zoom in the future.\"), mdx(\"h2\", {\n    \"id\": \"final-thoughts\"\n  }, \"Final Thoughts\"), mdx(\"p\", null, \"UniFi Talk is a good value for me and has been reliable so far. I love the side-effect of blocking robocalls because that was driving me crazy. The UTP-Touch desk phone is pretty cool and I'd like to see Ubiquiti release a wall mount version of it as well. I can't wait to use it with UniFi Protect.\"), mdx(\"p\", null, \"Ubiquiti has released 2 updates to the UniFi talk application in the month I've been using it so far so the roadmap seems to have active development. I'm hoping the UTP-Touch camera get's enabled soon for some sort of video capabilities and perhaps maybe SMS in the future?\"), mdx(\"p\", null, \"11/5/22 update, the Analog Telephone Adapter (ATA) that let's you use your existing traditional non-VOIP phones with Unifi Talk is now in general availability \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://amzn.to/3Td0tNL\"\n  }, \"for purchase\"), \" (paid link - As an Amazon Associate I earn from qualifying purchases from links marked paid).\"));\n}\n;\nMDXContent.isMDXComponent = true;","excerpt":"With the release of the UDM-Pro SE (Unifi Dream Machine Pro, special edition) I had my excuse to upgrade my USG + CloudKey combination to…","fields":{"slug":"/unifi-talk-utp-touch-review/","type":"posts"},"headings":[{"value":"Why I chose UniFi Talk","depth":2},{"value":"Unifi Talk Features","depth":2},{"value":"UniFi Talk Cons","depth":3},{"value":"UniFi Talk Setup with UTP-Touch","depth":2},{"value":"Porting your phone number to UniFi Talk","depth":3},{"value":"UTP-Touch","depth":2},{"value":"Call Forwarding","depth":3},{"value":"Other fun things","depth":3},{"value":"Final Thoughts","depth":2}],"frontmatter":{"title":"UniFi Talk Review with UTP-Touch VoIP phone","description":"This Ubiquiti review covers my experience using UniFi Talk on the Ubiquiti Dream Machine to provide VoIP phone service for my home using the UTP-Touch VoIP phone.","link":null,"date":"2022-02-16T00:00:00.000Z","tags":["networking","smart home","unifi","ubiquiti","post"],"draft":null,"hide":null,"cover":{"childImageSharp":{"fluid":{"base64":"data:image/jpeg;base64,/9j/2wBDAAMCAgMCAgMDAwMEAwMEBQgFBQQEBQoHBwYIDAoMDAsKCwsNDhIQDQ4RDgsLEBYQERMUFRUVDA8XGBYUGBIUFRT/2wBDAQMEBAUEBQkFBQkUDQsNFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBT/wgARCAAKABQDASIAAhEBAxEB/8QAFwAAAwEAAAAAAAAAAAAAAAAAAAUGB//EABUBAQEAAAAAAAAAAAAAAAAAAAQB/9oADAMBAAIQAxAAAAHNmsVodOxJcGb/xAAZEAADAQEBAAAAAAAAAAAAAAACAwQBBRL/2gAIAQEAAQUCPpz4HP1w0lMk9kWDAkStM1LD9//EAB0RAAICAQUAAAAAAAAAAAAAAAECAAPwEjFBUYH/2gAIAQMBAT8BelgNanbO4LGPAz2f/8QAGxEAAQQDAAAAAAAAAAAAAAAAAAIDBBITMWH/2gAIAQIBAT8Bck0Toz8P/8QAIBAAAgEDBAMAAAAAAAAAAAAAAQIAAwQREiExUSJh0f/aAAgBAQAGPwKtbImGzsR2G+TQbZnUb6HbYZmWooplwzKGbPJHuaqaKjdqMQeR47n/xAAdEAEAAwABBQAAAAAAAAAAAAABABEhQTFRYXGR/9oACAEBAAE/IVy5CdbPPsRFocSgc732ePsNTUXGpkmlYhT8gqTzH//aAAwDAQACAAMAAAAQky//xAAaEQEAAgMBAAAAAAAAAAAAAAABABEhMZFB/9oACAEDAQE/EGYw2EW8noK4wS3gx//EABsRAAICAwEAAAAAAAAAAAAAAAEhABFRscHR/9oACAECAQE/EEhuTg1wwItPJ//EABsQAQEAAgMBAAAAAAAAAAAAAAERACFBUWEx/9oACAEBAAE/ENEbkE0KRF91TPvw8eAw/UA7fK5QQgckOdU7wiGQIS7NS1yIDQxdKBTAQJTSOX3P/9k=","aspectRatio":2,"src":"/static/e70273d35e31f9ae23400edc2dd2ca0e/60a5f/utp-touch-unifi-talk-review.jpg","srcSet":"/static/e70273d35e31f9ae23400edc2dd2ca0e/3e5eb/utp-touch-unifi-talk-review.jpg 192w,\n/static/e70273d35e31f9ae23400edc2dd2ca0e/2880b/utp-touch-unifi-talk-review.jpg 384w,\n/static/e70273d35e31f9ae23400edc2dd2ca0e/60a5f/utp-touch-unifi-talk-review.jpg 768w","srcWebp":"/static/e70273d35e31f9ae23400edc2dd2ca0e/25278/utp-touch-unifi-talk-review.webp","srcSetWebp":"/static/e70273d35e31f9ae23400edc2dd2ca0e/a278a/utp-touch-unifi-talk-review.webp 192w,\n/static/e70273d35e31f9ae23400edc2dd2ca0e/2474b/utp-touch-unifi-talk-review.webp 384w,\n/static/e70273d35e31f9ae23400edc2dd2ca0e/25278/utp-touch-unifi-talk-review.webp 768w","sizes":"(max-width: 768px) 100vw, 768px","presentationWidth":768,"presentationHeight":382}}}}},{"id":"034d088f-3a4e-5a6a-b0c1-e53cd2f64a33","body":"var _excluded = [\"components\"];\nfunction _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }\nfunction _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }\nfunction _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }\n/* @jsxRuntime classic */\n/* @jsx mdx */\n\nvar _frontmatter = {\n  \"title\": \"Reviewing SwitchBot door contact and motion sensor home automation\",\n  \"cover\": \"./switchbot-motion-contact-banner.jpg\",\n  \"date\": \"2021-10-11T00:00:00.000Z\",\n  \"description\": \"In this review I'll use SwitchBot door contact and motion sensors to setup smart home automation routines using Amazon Alexa and the SwitchBot app to turn on lights when I open the door or shut lights off when occupancy isn't detected in the room.\",\n  \"tags\": [\"switchbot\", \"smart home\", \"iot\", \"post\"]\n};\nvar makeShortcode = function makeShortcode(name) {\n  return function MDXDefaultShortcode(props) {\n    console.warn(\"Component \" + name + \" was not imported, exported, or provided by MDXProvider as global scope\");\n    return mdx(\"div\", props);\n  };\n};\nvar Embed = makeShortcode(\"Embed\");\nvar layoutProps = {\n  _frontmatter: _frontmatter\n};\nvar MDXLayout = \"wrapper\";\nreturn function MDXContent(_ref) {\n  var components = _ref.components,\n    props = _objectWithoutProperties(_ref, _excluded);\n  return mdx(MDXLayout, _extends({}, layoutProps, props, {\n    components: components,\n    mdxType: \"MDXLayout\"\n  }), mdx(\"p\", null, \"SwitchBot added unique door contact and motion sensors to their smart home lineup and this article will go over how to use them to create smart home routines to control lights based on your doors opening or sensing room occupancy or the lack thereof. \"), mdx(\"p\", null, \"These sensors are unique because the door contact sensor, for example, includes a motion sensor and ambient light sensor in addition to the standard contact sensor.\"), mdx(\"blockquote\", null, mdx(\"p\", {\n    parentName: \"blockquote\"\n  }, \"This gives the contact sensor the ability to create more intelligent routines based on secondary criteria based on ambient light or if the room is occupied.\")), mdx(\"p\", null, \"I'm excited for the stand alone motion sensor because I can put them in bedrooms and have it work with my TP-Link Kasa light switches (or other smart switches) to shut the lights off when people forget to using Alexa routines.\"), mdx(\"p\", null, \"In the remainder of this article I'll go over the features of the SwitchBot door contact and motion sensors and how I set them up and installed them using Amazon Alexa and the SwitchBot app.\"), mdx(\"p\", null, \"If you prefer to see the video version of the \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://youtu.be/GtLAb6aMnvQ\"\n  }, \"SwitchBot review\"), \" you can watch that below or continue reading.\"), mdx(Embed, {\n    src: \"https://www.youtube.com/embed/GtLAb6aMnvQ\",\n    mdxType: \"Embed\"\n  }), mdx(\"p\", null, mdx(\"em\", {\n    parentName: \"p\"\n  }, \"As an Amazon Associate I earn from qualifying purchases made from links on the site which is a great way to support it if you are interested in purchasing any of the SwitchBot products mentioned.\")), mdx(\"h2\", {\n    \"id\": \"switchbot-contact-sensor\"\n  }, \"SwitchBot Contact Sensor\"), mdx(\"p\", null, \"The SwitchBot \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://amzn.to/2WtbSC7\"\n  }, \"contact sensors\"), \" can mount on things like doors, drawers, and cabinets so when they are opened and closed you can trigger home automation routines.\"), mdx(\"span\", {\n    \"className\": \"gatsby-resp-image-wrapper\",\n    \"style\": {\n      \"position\": \"relative\",\n      \"display\": \"block\",\n      \"marginLeft\": \"auto\",\n      \"marginRight\": \"auto\",\n      \"maxWidth\": \"640px\"\n    }\n  }, \"\\n      \", mdx(\"span\", {\n    parentName: \"span\",\n    \"className\": \"gatsby-resp-image-background-image\",\n    \"style\": {\n      \"paddingBottom\": \"75.1219512195122%\",\n      \"position\": \"relative\",\n      \"bottom\": \"0\",\n      \"left\": \"0\",\n      \"backgroundImage\": \"url('data:image/jpeg;base64,/9j/2wBDAAMCAgMCAgMDAwMEAwMEBQgFBQQEBQoHBwYIDAoMDAsKCwsNDhIQDQ4RDgsLEBYQERMUFRUVDA8XGBYUGBIUFRT/2wBDAQMEBAUEBQkFBQkUDQsNFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBT/wgARCAAPABQDASIAAhEBAxEB/8QAFwAAAwEAAAAAAAAAAAAAAAAAAAQGB//EABQBAQAAAAAAAAAAAAAAAAAAAAT/2gAMAwEAAhADEAAAAanPVUQO0kjgqP/EABoQAAIDAQEAAAAAAAAAAAAAAAQFAQMGAhb/2gAIAQEAAQUCQSMxDkOeXK7JHk0UaqPRPXNWhIC0JAtH/8QAGxEAAgIDAQAAAAAAAAAAAAAAAQIAAwQRMVH/2gAIAQMBAT8BU2rYQnDN5Hs//8QAGhEAAgIDAAAAAAAAAAAAAAAAAgMAARFCUv/aAAgBAgEBPwEqUSxI9ZhHM//EACEQAAIBBAICAwAAAAAAAAAAAAIDAQAEESETURIiMTJB/9oACAEBAAY/AnouURNwJYImbPE/uZptg+44Gx9JxouqkjwBQWMTNNvH+S0HG+ONx1SGpSSeHUNIvcq44afz3X//xAAdEAEAAgICAwAAAAAAAAAAAAABABEhcTFBYaHB/9oACAEBAAE/IXUs8TmS4+RU4W7I7aTMvgA/Z1EqOKFggS7ayjdl6eNw9SCbbKz/2gAMAwEAAgADAAAAEFTP/8QAGhEAAgIDAAAAAAAAAAAAAAAAASEAMVFhsf/aAAgBAwEBPxBJzVQOuQ4c/8QAGREAAgMBAAAAAAAAAAAAAAAAASEAMWGx/9oACAECAQE/EHgFHZG9mqf/xAAcEAEBAQEAAgMAAAAAAAAAAAABESEAMVFBYXH/2gAIAQEAAT8QGNXtgoSXUjkcDYZZcW1JBjfM0eT0VkWBj2rR+TeOqiokWBIro/v1wlLzKQzho+lHDhoVtSqre//Z')\",\n      \"backgroundSize\": \"cover\",\n      \"display\": \"block\"\n    }\n  }), \"\\n  \", mdx(\"img\", {\n    parentName: \"span\",\n    \"className\": \"gatsby-resp-image-image\",\n    \"alt\": \"Switchbot door contact sensor\",\n    \"title\": \"Switchbot door contact sensor\",\n    \"src\": \"/static/c85bb6fe777a9d78111946d09391d738/bbe0c/switchbot-door-contact.jpg\",\n    \"srcSet\": [\"/static/c85bb6fe777a9d78111946d09391d738/bd2b6/switchbot-door-contact.jpg 205w\", \"/static/c85bb6fe777a9d78111946d09391d738/ceeba/switchbot-door-contact.jpg 410w\", \"/static/c85bb6fe777a9d78111946d09391d738/bbe0c/switchbot-door-contact.jpg 640w\"],\n    \"sizes\": \"(max-width: 640px) 100vw, 640px\",\n    \"style\": {\n      \"width\": \"100%\",\n      \"height\": \"100%\",\n      \"margin\": \"0\",\n      \"verticalAlign\": \"middle\",\n      \"position\": \"absolute\",\n      \"top\": \"0\",\n      \"left\": \"0\"\n    },\n    \"loading\": \"lazy\"\n  }), \"\\n    \"), mdx(\"p\", null, \"If you are using various smart assistants like Alexa, Google Assistant, or Siri you can speak voice commands like\"), mdx(\"blockquote\", null, mdx(\"p\", {\n    parentName: \"blockquote\"\n  }, \"Hey \", \"[\", \" Alexa, Google, Siri \", \"]\", \" is the \", \"[\", \" name here \", \"]\", \" door opened?\")), mdx(\"p\", null, \"You can also integrate it with your other SwitchBot devices like their \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://amzn.to/3At6Nro\"\n  }, \"curtain-opener\"), \", \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"../smart-switch-no-neutral-using-switchbot/\"\n  }, \"bot (button pusher)\"), \", and indoor camera. You can use the contact sensor to initiate routines like closing the curtains when you leave the house or turn on a light when you open the door when entering your house.\"), mdx(\"p\", null, \"You can go a step further and setup a smart home routine with secondary criteria like\"), mdx(\"p\", null, mdx(\"em\", {\n    parentName: \"p\"\n  }, \"IF\"), \" the door is opened \", mdx(\"em\", {\n    parentName: \"p\"\n  }, \"AND\"), \" it's dark inside \", mdx(\"em\", {\n    parentName: \"p\"\n  }, \"THEN\"), \" turn on the light \", mdx(\"em\", {\n    parentName: \"p\"\n  }, \"ELSE\"), \" don't do anything\"), mdx(\"p\", null, \"because the SwitchBot contact sensor includes a light sensor so it can tell how bright it is in the room you place it.\"), mdx(\"p\", null, \"You can also help SwitchBot differentiate between a normal door open and close with leaving the house for example by using the physical button it has on the face. You can press that when you enter or leave the house to make an even more complex routine like\"), mdx(\"p\", null, mdx(\"em\", {\n    parentName: \"p\"\n  }, \"IF\"), \" the door is opened \", mdx(\"em\", {\n    parentName: \"p\"\n  }, \"AND\"), \" the contact button is pushed \", mdx(\"em\", {\n    parentName: \"p\"\n  }, \"AND\"), \" it's dark \", mdx(\"em\", {\n    parentName: \"p\"\n  }, \"THEN\"), \" turn on the outside lights \", mdx(\"em\", {\n    parentName: \"p\"\n  }, \"ELSE\"), \" don't do anything\"), mdx(\"p\", null, \"As far as mounting it is pretty flexible. It comes in two pieces; the sensor and contact magnet. You can stick them slightly apart on a fixed and moving surface like the door frame and door using 3m adhesive or screws.\"), mdx(\"h2\", {\n    \"id\": \"switchbot-motion-sensor\"\n  }, \"SwitchBot Motion Sensor\"), mdx(\"p\", null, \"I've always wanted my smart light switches to also include an occupancy or vacancy sensor, but at least in my case I found the idea is problematic because where my switches are located they don't have a good view of the room. Consequently, light switches with motion sensors fail to detect people in the room and shut the lights off on them.\"), mdx(\"blockquote\", null, mdx(\"p\", {\n    parentName: \"blockquote\"\n  }, \"The \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://amzn.to/38oreu0\"\n  }, \"SwitchBot motion sensor\"), \" allows for placement of the sensor in the room wherever it makes most sense for coverage.\")), mdx(\"p\", null, \"So now, I can put the SwitchBot motion sensor in a bedroom corner where it can pickup motion in the entire room, and setup an Alexa routine to switch off my \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"../tplink-kasa-light-switch-hs200/\"\n  }, \"TP-Link Kasa light switch\"), \" when no motion is detected after 5 minutes for example.\"), mdx(\"span\", {\n    \"className\": \"gatsby-resp-image-wrapper\",\n    \"style\": {\n      \"position\": \"relative\",\n      \"display\": \"block\",\n      \"marginLeft\": \"auto\",\n      \"marginRight\": \"auto\",\n      \"maxWidth\": \"640px\"\n    }\n  }, \"\\n      \", mdx(\"span\", {\n    parentName: \"span\",\n    \"className\": \"gatsby-resp-image-background-image\",\n    \"style\": {\n      \"paddingBottom\": \"75.1219512195122%\",\n      \"position\": \"relative\",\n      \"bottom\": \"0\",\n      \"left\": \"0\",\n      \"backgroundImage\": \"url('data:image/jpeg;base64,/9j/2wBDAAMCAgMCAgMDAwMEAwMEBQgFBQQEBQoHBwYIDAoMDAsKCwsNDhIQDQ4RDgsLEBYQERMUFRUVDA8XGBYUGBIUFRT/2wBDAQMEBAUEBQkFBQkUDQsNFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBT/wgARCAAPABQDASIAAhEBAxEB/8QAGAAAAgMAAAAAAAAAAAAAAAAAAAYEBQf/xAAXAQADAQAAAAAAAAAAAAAAAAAAAQID/9oADAMBAAIQAxAAAAFjmYswYO8EwI//xAAdEAABBAIDAAAAAAAAAAAAAAAEAAEDBQIUBhIT/9oACAEBAAEFAos+qzutaQzkewYHaSe9xNG5TlHOv//EABgRAAMBAQAAAAAAAAAAAAAAAAABEgMx/9oACAEDAQE/AdFXSUf/xAAXEQADAQAAAAAAAAAAAAAAAAAAAQIR/9oACAECAQE/AXKcYYf/xAAjEAACAQMDBAMAAAAAAAAAAAABAwIABBEFEiEUMkFRYZPB/9oACAEBAAY/AiR4GeKU26kxnO1YWs5Jx6FOadO1CO8jttj6A/KVJ14yKycEiots7ls1x5G4nMD8Vnqn/ZX/xAAbEAEBAAIDAQAAAAAAAAAAAAABEQAhMUFRof/aAAgBAQABPyFFUrwK5KY3IqsjeBzfLACEO44JsSCs81kWZQMMad9/cTpL7Wf/2gAMAwEAAgADAAAAEAjv/8QAFxEBAQEBAAAAAAAAAAAAAAAAAQARIf/aAAgBAwEBPxDekCM7f//EABoRAAICAwAAAAAAAAAAAAAAAAABESExUWH/2gAIAQIBAT8QsWHtZOz/xAAcEAEBAQEAAgMAAAAAAAAAAAABESEAMUFRodH/2gAIAQEAAT8QBEb1Keg9sXOtwITzZrbomTXlJJ2nQSg6+uIXcRheYC7PjgRai6VMaFsuizsNiItjxlsM8fvf/9k=')\",\n      \"backgroundSize\": \"cover\",\n      \"display\": \"block\"\n    }\n  }), \"\\n  \", mdx(\"img\", {\n    parentName: \"span\",\n    \"className\": \"gatsby-resp-image-image\",\n    \"alt\": \"Switchbot motion sensor\",\n    \"title\": \"Switchbot motion sensor\",\n    \"src\": \"/static/1f20e739601b1ba5321df20db3c9c3bf/bbe0c/switchbot-motion-sensor.jpg\",\n    \"srcSet\": [\"/static/1f20e739601b1ba5321df20db3c9c3bf/bd2b6/switchbot-motion-sensor.jpg 205w\", \"/static/1f20e739601b1ba5321df20db3c9c3bf/ceeba/switchbot-motion-sensor.jpg 410w\", \"/static/1f20e739601b1ba5321df20db3c9c3bf/bbe0c/switchbot-motion-sensor.jpg 640w\"],\n    \"sizes\": \"(max-width: 640px) 100vw, 640px\",\n    \"style\": {\n      \"width\": \"100%\",\n      \"height\": \"100%\",\n      \"margin\": \"0\",\n      \"verticalAlign\": \"middle\",\n      \"position\": \"absolute\",\n      \"top\": \"0\",\n      \"left\": \"0\"\n    },\n    \"loading\": \"lazy\"\n  }), \"\\n    \"), mdx(\"p\", null, \"Just like the door contact sensor, the SwitchBot motion sensor includes an ambient light sensor. This allowed me to setup a routine to turn on a light in the pass through when it is dark inside while also shutting it off if it doesn't sense any motion in the room for awhile.\"), mdx(\"p\", null, \"The motion sensor also has flexible mounting options using it's angle-adjustable magnetic pedestal base including\"), mdx(\"ul\", null, mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Stick on something magnetic\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Place on a shelf\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Stick on a wall with adhesive\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Mount with screws\")), mdx(\"p\", null, \"The motion sensor also has adjustments to reduce false positives from larger pets like dogs.\"), mdx(\"h2\", {\n    \"id\": \"installation\"\n  }, \"Installation\"), mdx(\"p\", null, \"Both sensors use AAA batteries for power which are included. To pair the sensors, once the batteries are installed, you simply open the SwitchBot app and push the + sign in the top right to add a device then select your motion or contact sensor from the list.\"), mdx(\"span\", {\n    \"className\": \"gatsby-resp-image-wrapper\",\n    \"style\": {\n      \"position\": \"relative\",\n      \"display\": \"block\",\n      \"marginLeft\": \"auto\",\n      \"marginRight\": \"auto\",\n      \"maxWidth\": \"370px\"\n    }\n  }, \"\\n      \", mdx(\"span\", {\n    parentName: \"span\",\n    \"className\": \"gatsby-resp-image-background-image\",\n    \"style\": {\n      \"paddingBottom\": \"173.17073170731706%\",\n      \"position\": \"relative\",\n      \"bottom\": \"0\",\n      \"left\": \"0\",\n      \"backgroundImage\": \"url('data:image/jpeg;base64,/9j/2wBDAAMCAgMCAgMDAwMEAwMEBQgFBQQEBQoHBwYIDAoMDAsKCwsNDhIQDQ4RDgsLEBYQERMUFRUVDA8XGBYUGBIUFRT/2wBDAQMEBAUEBQkFBQkUDQsNFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBT/wgARCAAjABQDASIAAhEBAxEB/8QAGAABAQEBAQAAAAAAAAAAAAAAAAECAwj/xAAVAQEBAAAAAAAAAAAAAAAAAAAAAf/aAAwDAQACEAMQAAAB9Q2dYyypZg0BQwD/xAAbEAADAQEAAwAAAAAAAAAAAAAAAQIREgMhMf/aAAgBAQABBQJPSPEoKeFdcor6t694JLqFNJrk00pn/8QAFBEBAAAAAAAAAAAAAAAAAAAAIP/aAAgBAwEBPwFf/8QAFBEBAAAAAAAAAAAAAAAAAAAAIP/aAAgBAgEBPwFf/8QAHRAAAQQDAQEAAAAAAAAAAAAAAAECESEQMWESIP/aAAgBAQAGPwJWlYTzE9zuo0OsQ7A9L3ciJ8f/xAAfEAEAAgMAAQUAAAAAAAAAAAABABEhMVFBYXGx0fH/2gAIAQEAAT8hRg6M36x1cMVtfmIsMAoqAaLMwwKH7jsrqN08jYWba32UL+cJGFijz5ggNBRDT2giIs//2gAMAwEAAgADAAAAEKzJDPPP/8QAFhEBAQEAAAAAAAAAAAAAAAAAESAh/9oACAEDAQE/EDGv/8QAFBEBAAAAAAAAAAAAAAAAAAAAIP/aAAgBAgEBPxBf/8QAHhABAQACAgMBAQAAAAAAAAAAAREAITFBUYGRYXH/2gAIAQEAAT8Qm4QYO46coRY01PZwKLJesapwtwzR47Rw411njNmp29uMHCBIm/j5gDlkfJ5znPgvW3H3eUXI+VXSnCesFeHA6DQYxWqAsLlSIfwMG168Z//Z')\",\n      \"backgroundSize\": \"cover\",\n      \"display\": \"block\"\n    }\n  }), \"\\n  \", mdx(\"img\", {\n    parentName: \"span\",\n    \"className\": \"gatsby-resp-image-image\",\n    \"alt\": \"Switchbot app\",\n    \"title\": \"Switchbot app\",\n    \"src\": \"/static/86c02e3832b697372db1df4ad7a716f8/a1654/switchbot-app.jpg\",\n    \"srcSet\": [\"/static/86c02e3832b697372db1df4ad7a716f8/bd2b6/switchbot-app.jpg 205w\", \"/static/86c02e3832b697372db1df4ad7a716f8/a1654/switchbot-app.jpg 370w\"],\n    \"sizes\": \"(max-width: 370px) 100vw, 370px\",\n    \"style\": {\n      \"width\": \"100%\",\n      \"height\": \"100%\",\n      \"margin\": \"0\",\n      \"verticalAlign\": \"middle\",\n      \"position\": \"absolute\",\n      \"top\": \"0\",\n      \"left\": \"0\"\n    },\n    \"loading\": \"lazy\"\n  }), \"\\n    \"), mdx(\"p\", null, \"The app walks you through the rest. I highly recommend getting the \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://amzn.to/3yyqzRh\"\n  }, \"SwitchBot hub\"), \" to allow external access to your SwitchBot devices otherwise you are limited to bluetooth range of your phone for control.\"), mdx(\"h2\", {\n    \"id\": \"setting-up-switchbot-scenes\"\n  }, \"Setting up SwitchBot Scenes\"), mdx(\"p\", null, \"To setup automation using your sensors head into the SwitchBot app and select the Scenes tab. From there you can select your sensors and the app will present you with criteria you can trigger applicable to what the device can sense like motion or ambient room brightness.\"), mdx(\"p\", null, \"Here I setup a scene to trigger my SwitchBot bot to click on the kitchen light switch \", mdx(\"em\", {\n    parentName: \"p\"\n  }, \"when\"), \" the SwitchBot motion sensor detects motion in the room \", mdx(\"em\", {\n    parentName: \"p\"\n  }, \"and\"), \" the light sensor detects it is dark inside the room.\"), mdx(\"span\", {\n    \"className\": \"gatsby-resp-image-wrapper\",\n    \"style\": {\n      \"position\": \"relative\",\n      \"display\": \"block\",\n      \"marginLeft\": \"auto\",\n      \"marginRight\": \"auto\",\n      \"maxWidth\": \"480px\"\n    }\n  }, \"\\n      \", mdx(\"span\", {\n    parentName: \"span\",\n    \"className\": \"gatsby-resp-image-background-image\",\n    \"style\": {\n      \"paddingBottom\": \"104.39024390243902%\",\n      \"position\": \"relative\",\n      \"bottom\": \"0\",\n      \"left\": \"0\",\n      \"backgroundImage\": \"url('data:image/jpeg;base64,/9j/2wBDAAMCAgMCAgMDAwMEAwMEBQgFBQQEBQoHBwYIDAoMDAsKCwsNDhIQDQ4RDgsLEBYQERMUFRUVDA8XGBYUGBIUFRT/2wBDAQMEBAUEBQkFBQkUDQsNFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBT/wgARCAAVABQDASIAAhEBAxEB/8QAGQABAAIDAAAAAAAAAAAAAAAAAAQHAwUI/8QAFwEAAwEAAAAAAAAAAAAAAAAAAQIDAP/aAAwDAQACEAMQAAABsnQXdLonP67BM/OSMAZv/8QAHBAAAgMBAAMAAAAAAAAAAAAAAQIAAwQRBRMh/9oACAEBAAEFAtnh0rwnDsaNjvSepeqF4qDRCvSPkoSxB//EABoRAAICAwAAAAAAAAAAAAAAAAABAhAREiH/2gAIAQMBAT8B1HDFN9P/xAAXEQADAQAAAAAAAAAAAAAAAAAAEBIT/9oACAECAQE/AbNH/8QAIRAAAQQBAwUAAAAAAAAAAAAAAQACAxESBDFxEDIzguH/2gAIAQEABj8ChlgEr5nVY3XglPoqdDIOWrsocBbIum0+JuhnWy+dHZyB5Juw2l//xAAfEAEAAgEDBQAAAAAAAAAAAAABABEhQWFxMVGBkaH/2gAIAQEAAT8ht5M4WcViCUm5RsQ1gQhgBHF6VG0EQp4qXEigSeFPSdmzslTQSyquh/f2f//aAAwDAQACAAMAAAAQbMj8/8QAGREBAAIDAAAAAAAAAAAAAAAAAQDBEBGh/9oACAEDAQE/EEMVaXpWHU0E/8QAGBEBAAMBAAAAAAAAAAAAAAAAAQAQEUH/2gAIAQIBAT8QMQDygMn/xAAdEAEBAAMAAwEBAAAAAAAAAAABEQAhMVFhkXGx/9oACAEBAAE/EOlioANOSwzr97gOu0NN+Y1N0jSu+ebhk+A0UQTZQ2bbrBQE1H+RrNGHlc4rsVd776wQqB0iPDqjd1yOcDxq+9ZCGMEmgCCqT9a8Z//Z')\",\n      \"backgroundSize\": \"cover\",\n      \"display\": \"block\"\n    }\n  }), \"\\n  \", mdx(\"img\", {\n    parentName: \"span\",\n    \"className\": \"gatsby-resp-image-image\",\n    \"alt\": \"Switchbot app\",\n    \"title\": \"Switchbot app\",\n    \"src\": \"/static/b38655352dfb1e8377cf8b9ec1cd07b1/55489/switchbot-scene.jpg\",\n    \"srcSet\": [\"/static/b38655352dfb1e8377cf8b9ec1cd07b1/bd2b6/switchbot-scene.jpg 205w\", \"/static/b38655352dfb1e8377cf8b9ec1cd07b1/ceeba/switchbot-scene.jpg 410w\", \"/static/b38655352dfb1e8377cf8b9ec1cd07b1/55489/switchbot-scene.jpg 480w\"],\n    \"sizes\": \"(max-width: 480px) 100vw, 480px\",\n    \"style\": {\n      \"width\": \"100%\",\n      \"height\": \"100%\",\n      \"margin\": \"0\",\n      \"verticalAlign\": \"middle\",\n      \"position\": \"absolute\",\n      \"top\": \"0\",\n      \"left\": \"0\"\n    },\n    \"loading\": \"lazy\"\n  }), \"\\n    \"), mdx(\"p\", null, \"So now when someone walks into the kitchen pass-through at a dark time of the night the lights will automatically turn on for them, but if the ambient brightness of the room is bright enough it won't unnecessarily turn on the lights.\"), mdx(\"p\", null, \"If you don't want to use their app the SwitchBot is compatible with Alexa, Google Assistant, Siri, IFTTT, SmartThings, and Clova.\"), mdx(\"p\", null, \"When integrating routines with devices made by other manufacturers, like TP-Link for example, I found it easier to use Alexa to create the routine.\"), mdx(\"h2\", {\n    \"id\": \"setting-up-alexa-routines\"\n  }, \"Setting up Alexa Routines\"), mdx(\"p\", null, \"I used Alexa to setup a routine to use the SwitchBot motion sensor as a trigger for my TP-Link \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://amzn.to/3iPjneu\"\n  }, \"HS200\"), \" light switch. To setup an Alexa routine.\"), mdx(\"ol\", null, mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Open the Alexa app\"), mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Select More on the bottom right\"), mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Press Routines\"), mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Press the + icon in the upper right\"), mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Provide a routine name\"), mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Click the + icon next to \", mdx(\"em\", {\n    parentName: \"li\"\n  }, \"When this happens\")), mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Select SmartHome\"), mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Select your SwitchBot device (motion sensor for example)\"), mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Set when \", mdx(\"em\", {\n    parentName: \"li\"\n  }, \"Motion is...\"), \" detected/not detected\"), mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Back at the routine screen press \", mdx(\"em\", {\n    parentName: \"li\"\n  }, \"Add Action\")), mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Select Smart Home > All Devices\"), mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Select the TP-Link light switch (or whatever you want to control)\"), mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Follow remaining prompts to save\")), mdx(\"h2\", {\n    \"id\": \"final-thoughts\"\n  }, \"Final Thoughts\"), mdx(\"p\", null, \"The SwitchBot motion and contact sensors allowed the extra input criteria to create some really useful automations with their existing product line up. For example, if you were using the \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://amzn.to/3oQk3nM\"\n  }, \"SwitchBot humidifier\"), \" for a bedroom you could use the motion sensor to turn it off when nobody is in the room.\"), mdx(\"p\", null, \"My favorite is the \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://amzn.to/38oreu0\"\n  }, \"motion sensor\"), \" because I can finally setup reliable vacancy-sensing routines to turn lights off around the house.\"), mdx(\"p\", null, \"Feel free to use my social links below with any questions and thanks for reading!\"));\n}\n;\nMDXContent.isMDXComponent = true;","excerpt":"SwitchBot added unique door contact and motion sensors to their smart home lineup and this article will go over how to use them to create…","fields":{"slug":"/switchbot-door-contact-and-motion-sensor-review/","type":"posts"},"headings":[{"value":"SwitchBot Contact Sensor","depth":2},{"value":"SwitchBot Motion Sensor","depth":2},{"value":"Installation","depth":2},{"value":"Setting up SwitchBot Scenes","depth":2},{"value":"Setting up Alexa Routines","depth":2},{"value":"Final Thoughts","depth":2}],"frontmatter":{"title":"Reviewing SwitchBot door contact and motion sensor home automation","description":"In this review I'll use SwitchBot door contact and motion sensors to setup smart home automation routines using Amazon Alexa and the SwitchBot app to turn on lights when I open the door or shut lights off when occupancy isn't detected in the room.","link":null,"date":"2021-10-11T00:00:00.000Z","tags":["switchbot","smart home","iot","post"],"draft":null,"hide":null,"cover":{"childImageSharp":{"fluid":{"base64":"data:image/jpeg;base64,/9j/2wBDAAMCAgMCAgMDAwMEAwMEBQgFBQQEBQoHBwYIDAoMDAsKCwsNDhIQDQ4RDgsLEBYQERMUFRUVDA8XGBYUGBIUFRT/2wBDAQMEBAUEBQkFBQkUDQsNFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBT/wgARCAAOABQDASIAAhEBAxEB/8QAGAAAAwEBAAAAAAAAAAAAAAAAAAUHBAb/xAAVAQEBAAAAAAAAAAAAAAAAAAACA//aAAwDAQACEAMQAAABV9/MnkngJGGX/8QAGhAAAwADAQAAAAAAAAAAAAAAAwQFAAEGAv/aAAgBAQABBQKEPSU72MhwnTHUyJSaczo6rHP7d6g5D//EABkRAQACAwAAAAAAAAAAAAAAAAEAERITQf/aAAgBAwEBPwHUpTMa7P/EABYRAQEBAAAAAAAAAAAAAAAAAAEAEv/aAAgBAgEBPwFSy3//xAAiEAACAgICAQUBAAAAAAAAAAABAgMRABIEMSIhI1JhcYH/2gAIAQEABj8Cd3BIHWo9auskj45jbVdlZ1on9xOQrwygr2i0PvOFFCyQx+WzFbYjvIwHE6z3fjrY/hwmH2E+C9Z//8QAHBABAAIDAAMAAAAAAAAAAAAAAQARITFRQZGx/9oACAEBAAE/IX1PizkRrKaf118jRAysCaa7cVAtMtDrdYt9wykANvFbBx4g/PYwZ//aAAwDAQACAAMAAAAQEA//xAAZEQADAQEBAAAAAAAAAAAAAAABESEAMZH/2gAIAQMBAT8QQkRFkw5N7v/EABoRAAICAwAAAAAAAAAAAAAAAAABESExkeH/2gAIAQIBAT8QeVT0UGXs/8QAGxABAQEAAwEBAAAAAAAAAAAAAREhADFBUWH/2gAIAQEAAT8QVcsHRkCFaLmQ5hjBcAyHe6mY/eML/vCSINDviNAW3SAgVEfiq4cf01egOATFWnZ2PThHvDUz29+8/9k=","aspectRatio":1.4222222222222223,"src":"/static/c352b32602dc70718cefc20a6f02df5b/60a5f/switchbot-motion-contact-banner.jpg","srcSet":"/static/c352b32602dc70718cefc20a6f02df5b/3e5eb/switchbot-motion-contact-banner.jpg 192w,\n/static/c352b32602dc70718cefc20a6f02df5b/2880b/switchbot-motion-contact-banner.jpg 384w,\n/static/c352b32602dc70718cefc20a6f02df5b/60a5f/switchbot-motion-contact-banner.jpg 768w","srcWebp":"/static/c352b32602dc70718cefc20a6f02df5b/25278/switchbot-motion-contact-banner.webp","srcSetWebp":"/static/c352b32602dc70718cefc20a6f02df5b/a278a/switchbot-motion-contact-banner.webp 192w,\n/static/c352b32602dc70718cefc20a6f02df5b/2474b/switchbot-motion-contact-banner.webp 384w,\n/static/c352b32602dc70718cefc20a6f02df5b/25278/switchbot-motion-contact-banner.webp 768w","sizes":"(max-width: 768px) 100vw, 768px","presentationWidth":768,"presentationHeight":538}}}}},{"id":"944244b4-1c36-5608-8191-8d75336e7754","body":"var _excluded = [\"components\"];\nfunction _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }\nfunction _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }\nfunction _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }\n/* @jsxRuntime classic */\n/* @jsx mdx */\n\nvar _frontmatter = {\n  \"title\": \"What to Buy your Model Y\",\n  \"cover\": \"./red-tesla-model-y.jpg\",\n  \"date\": \"2021-10-10T00:00:00.000Z\",\n  \"description\": \"There are a lot Tesla Model Y accessory options out there. Now that my model Y arrived here are my reviews for the best accessories for your Tesla Model Y that you'll want day 1. Benefit from my hindsight.\",\n  \"tags\": [\"tesla\", \"model y\", \"floor mat\", \"accessories\", \"smart home\", \"iot\", \"post\"]\n};\nvar makeShortcode = function makeShortcode(name) {\n  return function MDXDefaultShortcode(props) {\n    console.warn(\"Component \" + name + \" was not imported, exported, or provided by MDXProvider as global scope\");\n    return mdx(\"div\", props);\n  };\n};\nvar Embed = makeShortcode(\"Embed\");\nvar layoutProps = {\n  _frontmatter: _frontmatter\n};\nvar MDXLayout = \"wrapper\";\nreturn function MDXContent(_ref) {\n  var components = _ref.components,\n    props = _objectWithoutProperties(_ref, _excluded);\n  return mdx(MDXLayout, _extends({}, layoutProps, props, {\n    components: components,\n    mdxType: \"MDXLayout\"\n  }), mdx(\"p\", null, \"When I ordered my Tesla Model Y in June 2021 it had about a 3 month delivery lead time. I spent that time researching what accessories I felt I'd really need for my car, what are nice-to-haves, and what were probably a waste of money. The accessories I felt I needed \", mdx(\"em\", {\n    parentName: \"p\"\n  }, \"Day 1\"), \" came down to floor and cargo mats, screen protector, mud flaps (I'll explain), garage door opener (I'll explain again), and a way to cleanly mount my radar detector. With supply chain issues and the popularity of Tesla some of the accessories were backordered or threatening to go out of stock so I ordered them ahead of time.\"), mdx(\"p\", null, \"Now that the model Y arrived I had a chance to install these car accessories and thought I'd share my reviews of them to save like-minded people some time obsessing and pouring over forums and blogs like I did. As an Amazon Associate I earn from qualifying purchases made from links on the site which is a great way to support it if you are interested in any of the Tesla accessories I mention below.\"), mdx(\"h2\", {\n    \"id\": \"tesla-floor-and-cargo-mats\"\n  }, \"Tesla Floor and Cargo Mats\"), mdx(\"p\", null, \"While researching floor mats for the Model Y I realized how different regional preferences are on them. I've never felt the need to buy aftermarket floor mats in any previous car I've owned, but I am in Florida and it seems to be a bigger item of importance in places with more snow and mud.\"), mdx(\"p\", null, \"However, my last car had several unfortunate incidents with passengers spilling water bottles on the floor leading to unfortunate smells and I also wanted to protect the trunk cargo area from trips to the garden center for mulch and dirt. I figured trunk and cargo mats made sense to keep the model Y looking nice.\"), mdx(\"p\", null, \"There are a lot of options for floor mats for the Tesla Model Y. Ultimately, I chose ones from a company called Tuxmat because I felt they had the best look and function over the competitors. I go over my experience in this \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://youtu.be/9oRDz1gGrOo\"\n  }, \"Tesla Model Y Tuxmat floor mat review\"), \" below.\"), mdx(Embed, {\n    src: \"https://www.youtube.com/embed/9oRDz1gGrOo\",\n    mdxType: \"Embed\"\n  }), mdx(\"p\", null, \"Here is a breakdown of competing floor mats to show how I ended up choosing Tuxmat.\"), mdx(\"table\", null, mdx(\"thead\", {\n    parentName: \"table\"\n  }, mdx(\"tr\", {\n    parentName: \"thead\"\n  }, mdx(\"th\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"My Rank\"), mdx(\"th\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"Floor Mat\"), mdx(\"th\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"Pros\"), mdx(\"th\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"Cons\"))), mdx(\"tbody\", {\n    parentName: \"table\"\n  }, mdx(\"tr\", {\n    parentName: \"tbody\"\n  }, mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"1\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"Tuxmat\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"Looks good, good fit, waterproof\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"Higher price (worth it)\")), mdx(\"tr\", {\n    parentName: \"tbody\"\n  }, mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"2\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"Tesmanian\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"Good fit, waterproof\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"Plastic / cheap looking\")), mdx(\"tr\", {\n    parentName: \"tbody\"\n  }, mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"3\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, mdx(\"a\", {\n    parentName: \"td\",\n    \"href\": \"https://amzn.to/3uU0Kee\"\n  }, \"3d Maxspider\")), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"Well reviewed\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"Obnoxious logo/branding\")), mdx(\"tr\", {\n    parentName: \"tbody\"\n  }, mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"4\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, mdx(\"a\", {\n    parentName: \"td\",\n    \"href\": \"https://amzn.to/3BwEcmc\"\n  }, \"Autailors\")), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"Well reviewed\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"Not a fan of style\")))), mdx(\"p\", null, \"Tuxmat is going to give you the highest side-wall coverage, enhance the look of the car subtly (in my opinion), and not look like a plastic lunch tray on the floor.\"), mdx(\"span\", {\n    \"className\": \"gatsby-resp-image-wrapper\",\n    \"style\": {\n      \"position\": \"relative\",\n      \"display\": \"block\",\n      \"marginLeft\": \"auto\",\n      \"marginRight\": \"auto\",\n      \"maxWidth\": \"640px\"\n    }\n  }, \"\\n      \", mdx(\"span\", {\n    parentName: \"span\",\n    \"className\": \"gatsby-resp-image-background-image\",\n    \"style\": {\n      \"paddingBottom\": \"75.1219512195122%\",\n      \"position\": \"relative\",\n      \"bottom\": \"0\",\n      \"left\": \"0\",\n      \"backgroundImage\": \"url('data:image/jpeg;base64,/9j/2wBDAAMCAgMCAgMDAwMEAwMEBQgFBQQEBQoHBwYIDAoMDAsKCwsNDhIQDQ4RDgsLEBYQERMUFRUVDA8XGBYUGBIUFRT/2wBDAQMEBAUEBQkFBQkUDQsNFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBT/wgARCAAPABQDASIAAhEBAxEB/8QAGAAAAgMAAAAAAAAAAAAAAAAAAAcDBAb/xAAWAQEBAQAAAAAAAAAAAAAAAAAEAgP/2gAMAwEAAhADEAAAAVxYfuKxWEhJ/wD/xAAcEAACAgIDAAAAAAAAAAAAAAADBAEFAAIGEhT/2gAIAQEAAQUCIl3IlVG9O9RVJZYcVEmZxWUWyMBNP//EABkRAAIDAQAAAAAAAAAAAAAAAAASAQIDMv/aAAgBAwEBPwHetHbLmRZP/8QAFxEBAQEBAAAAAAAAAAAAAAAAAQARIf/aAAgBAgEBPwEeY2l//8QAIhAAAQMDAwUAAAAAAAAAAAAAAQACAxESQRMhIjEyM1Hh/9oACAEBAAY/AtSlzXeNwyU2KPrKdgUI5C+SSlXEPtFUHwjgQAWnP1RTYrwcO4H0r5Ijcctpuv/EAB0QAAMAAgMBAQAAAAAAAAAAAAERIQBhMVFxQbH/2gAIAQEAAT8hdM8qIfIjcimCRdnzPwdAwHWB3mBEQHXCjR7xXHI9I0vj3xifA2YtkeM//9oADAMBAAIAAwAAABATP//EABoRAAMAAwEAAAAAAAAAAAAAAAERMRAhYUH/2gAIAQMBAT8QbFNg6OHoj9T1MH//xAAWEQEBAQAAAAAAAAAAAAAAAAABEEH/2gAIAQIBAT8QY0Ev/8QAGxABAQEAAwEBAAAAAAAAAAAAAREhADFBUWH/2gAIAQEAAT8QseQAXKJrfUeM4cUEs0C/QAK+5x2DDxF2H0JlV/eLjssBQmSChhVDeR03ZEKB3BiHVabgULjOn5NPoZRTvn//2Q==')\",\n      \"backgroundSize\": \"cover\",\n      \"display\": \"block\"\n    }\n  }), \"\\n  \", mdx(\"img\", {\n    parentName: \"span\",\n    \"className\": \"gatsby-resp-image-image\",\n    \"alt\": \"Tuxmat Model Y floor mat driver side\",\n    \"title\": \"Tuxmat Model Y floor mat driver side\",\n    \"src\": \"/static/84fe204e26976f913040143ad227f3ef/bbe0c/tuxmat-tesla-floor-mat.jpg\",\n    \"srcSet\": [\"/static/84fe204e26976f913040143ad227f3ef/bd2b6/tuxmat-tesla-floor-mat.jpg 205w\", \"/static/84fe204e26976f913040143ad227f3ef/ceeba/tuxmat-tesla-floor-mat.jpg 410w\", \"/static/84fe204e26976f913040143ad227f3ef/bbe0c/tuxmat-tesla-floor-mat.jpg 640w\"],\n    \"sizes\": \"(max-width: 640px) 100vw, 640px\",\n    \"style\": {\n      \"width\": \"100%\",\n      \"height\": \"100%\",\n      \"margin\": \"0\",\n      \"verticalAlign\": \"middle\",\n      \"position\": \"absolute\",\n      \"top\": \"0\",\n      \"left\": \"0\"\n    },\n    \"loading\": \"lazy\"\n  }), \"\\n    \"), mdx(\"p\", null, \"I was considering the Tesmanian option for the cargo tray, but ultimately decided to stay with Tuxmat there to match. The Tesmanian cargo mat is two pieces which allows you to get at both sub trunks which I wished I could do with the Tuxmat where you can only easily get to the deeper one.\"), mdx(\"p\", null, \"Tuxmat's cargo mat also arrives folded in the shipping box and initially has a bit of a raised bump from it in the middle which I was concerned about. After I went to the garden center the next day and transported some mulch and fertilizer it flattened out. \"), mdx(\"p\", null, \"It stays in place using a rubber-textured bottom so no velcro to wear out the carpet with and you can still access the main sub trunk.\"), mdx(\"span\", {\n    \"className\": \"gatsby-resp-image-wrapper\",\n    \"style\": {\n      \"position\": \"relative\",\n      \"display\": \"block\",\n      \"marginLeft\": \"auto\",\n      \"marginRight\": \"auto\",\n      \"maxWidth\": \"640px\"\n    }\n  }, \"\\n      \", mdx(\"span\", {\n    parentName: \"span\",\n    \"className\": \"gatsby-resp-image-background-image\",\n    \"style\": {\n      \"paddingBottom\": \"75.1219512195122%\",\n      \"position\": \"relative\",\n      \"bottom\": \"0\",\n      \"left\": \"0\",\n      \"backgroundImage\": \"url('data:image/jpeg;base64,/9j/2wBDAAMCAgMCAgMDAwMEAwMEBQgFBQQEBQoHBwYIDAoMDAsKCwsNDhIQDQ4RDgsLEBYQERMUFRUVDA8XGBYUGBIUFRT/2wBDAQMEBAUEBQkFBQkUDQsNFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBT/wgARCAAPABQDASIAAhEBAxEB/8QAGAAAAwEBAAAAAAAAAAAAAAAAAAEGBAX/xAAWAQEBAQAAAAAAAAAAAAAAAAAEAgP/2gAMAwEAAhADEAAAAZNPjmfem4yv/8QAGhAAAwADAQAAAAAAAAAAAAAAAwQFAQITBv/aAAgBAQABBQKj0qXGfP8AJGM1lhJ8LCFMpz74hgACf//EAB0RAAEEAgMAAAAAAAAAAAAAAAEAAgMRBBIhgaH/2gAIAQMBAT8By8SYSXG4a9V6i9g4tf/EABoRAQACAwEAAAAAAAAAAAAAAAEAAhETIUH/2gAIAQIBAT8BO1AcTRV8n//EACMQAAIBAwMEAwAAAAAAAAAAAAECAwAEESEiQRIUUYEjYnH/2gAIAQEABj8ChtpCZNpJOMHHr9qa7W6mWaHd0sBhgPFK3cPGfAC0zwTGNtdfrxXy3LHPPNIrI2fVf//EAB0QAQACAwADAQAAAAAAAAAAAAERIQAxQVFhcYH/2gAIAQEAAT8hSwBiUMMmoyrvGPQh4UoAjAfuL2SuHaGbPeUz47lMi8fGgIpciufMNPlw0gjuf//aAAwDAQACAAMAAAAQ39//xAAcEQACAQUBAAAAAAAAAAAAAAABEQAhMUFRcWH/2gAIAQMBAT8QLrRIIIIhs3urfYTqnlnleOf/xAAXEQEBAQEAAAAAAAAAAAAAAAABEQBh/9oACAECAQE/EGXIcrlixv/EABoQAQEBAQEBAQAAAAAAAAAAAAERIQAxQVH/2gAIAQEAAT8QOouG0AIlBOIJLy94NXitCHU2qI8leej7A1KDN0zOMswSjegrA+Pjx/q6oEqgSDxBr+8JCpKhg8GYGd//2Q==')\",\n      \"backgroundSize\": \"cover\",\n      \"display\": \"block\"\n    }\n  }), \"\\n  \", mdx(\"img\", {\n    parentName: \"span\",\n    \"className\": \"gatsby-resp-image-image\",\n    \"alt\": \"Tuxmat Model Y floor mat driver side\",\n    \"title\": \"Tuxmat Model Y floor mat driver side\",\n    \"src\": \"/static/2569a204789fbd3a6c889b1502387bd2/bbe0c/tuxmat-tesla-cargo-mat.jpg\",\n    \"srcSet\": [\"/static/2569a204789fbd3a6c889b1502387bd2/bd2b6/tuxmat-tesla-cargo-mat.jpg 205w\", \"/static/2569a204789fbd3a6c889b1502387bd2/ceeba/tuxmat-tesla-cargo-mat.jpg 410w\", \"/static/2569a204789fbd3a6c889b1502387bd2/bbe0c/tuxmat-tesla-cargo-mat.jpg 640w\"],\n    \"sizes\": \"(max-width: 640px) 100vw, 640px\",\n    \"style\": {\n      \"width\": \"100%\",\n      \"height\": \"100%\",\n      \"margin\": \"0\",\n      \"verticalAlign\": \"middle\",\n      \"position\": \"absolute\",\n      \"top\": \"0\",\n      \"left\": \"0\"\n    },\n    \"loading\": \"lazy\"\n  }), \"\\n    \"), mdx(\"p\", null, \"The Tuxmats are still my favorite accessory purchase for the Tesla so far. It makes the Model Y look really nice which I wasn't expecting floor mats to do and I don't worry about people with dirty shoes getting in the car.\"), mdx(\"h2\", {\n    \"id\": \"tesla-infotainment-screen-protector\"\n  }, \"Tesla Infotainment Screen Protector\"), mdx(\"p\", null, \"When I test drove the Model Y I noticed some glare and fingerprints on the infotainment screen which I knew was going to bother me which got me looking into matte finish anti-glare screen protectors.\"), mdx(\"p\", null, \"I really don't like the look or feel of them on phones and I'm pretty bad at lining them up and getting them on without getting some spec of dust under it so I spent a lot of research finding the best screen protector for the Tesla Model Y.\"), mdx(\"p\", null, \"These are the qualities I felt the ideal screen protector needed to have\"), mdx(\"ul\", null, mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Reduces glare\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Reduces appearance of fingerprints\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Doesn't blur screen\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Doesn't diminish responsiveness\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Ease of installation (alignment, dust, bubbles)\")), mdx(\"p\", null, \"This narrowed things down to the \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://amzn.to/39CeJeH\"\n  }, \"Spigen GLAS.tR\"), \" and the Abstract Ocean tempered screen protector. I went with Spigen because I've had good experiences with their phone cases and their templated installation system seemed to be slightly better than Abstract Ocean's.\"), mdx(\"p\", null, \"Below is a video of me \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://youtu.be/AJdKZlzaQDA\"\n  }, \"installing the Spigen GLAS.tR screen protector\")), mdx(Embed, {\n    src: \"https://www.youtube.com/embed/AJdKZlzaQDA\",\n    mdxType: \"Embed\"\n  }), mdx(\"p\", null, \"I'm very happy with the screen protector and found it delivers on the promise of reduced glare and fingerprints. With the template frame getting it lined up perfectly for the install was super easy. Screen clarity does not seem to be impacted. There is a slight rainbow effect with polarized sunglasses, but it doesn't bother me.\"), mdx(\"p\", null, \"People seem to have similarly positive experiences with Abstract Ocean's version so you probably can't go wrong with either one.\"), mdx(\"h2\", {\n    \"id\": \"tesla-garage-door-opener\"\n  }, \"Tesla Garage Door Opener\"), mdx(\"p\", null, \"Hey so Tesla does not include that familar, standardized, and expected mirror HomeLink automatic garage door opener on it's cars. If you want that it costs another $325 and requires a separate service vist for installation. I didn't feel like paying that and had a smart garage door opener already. This seems to be where Tesla is pushing people towards in their reasoning behind not including one so I guess I was ahead of the curve.\"), mdx(Embed, {\n    src: \"https://www.youtube.com/embed/iBFTx9qcwPg\",\n    mdxType: \"Embed\"\n  }), mdx(\"p\", null, \"I found \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://amzn.to/3iwuvNf\"\n  }, \"NexxGarage\"), \" to be the best option for smart garage door openers for my Tesla Model Y. I have it set to automatically open the garage was I approach the house using GPS. When I leave I use a siri shortcut to close the garage door. Auto-open is nice when it works, but it isn't as reliable as I would hope.\"), mdx(\"p\", null, \"That being said smart openers just aren't as trouble-free as HomeLink since they involve both WiFi and a third party cloud service vs just line of sight radio signal directly to your opener.\"), mdx(\"p\", null, \"There are competing products from Chamberlain like \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://amzn.to/3FuZHGi\"\n  }, \"MyQ\"), \", but there is a monthly fee where NexxGarage does not. If Nexx's reliability doesn't get near perfect I may look into that one more.\"), mdx(\"h2\", {\n    \"id\": \"radar-detector-mount\"\n  }, \"Radar Detector Mount\"), mdx(\"p\", null, \"Though I don't drive reckless or speed I still have a radar detector for general awareness and to remind me not to. In my last car, I had my Valentine 1 gen2 radar detector mounted on the car mirror and I had tapped power by splicing into the wires in the headliner area for the dome lights. I did not want to risk that on the new Tesla.\"), mdx(\"p\", null, \"The Model Y has a convenient 12v adapter plug in the console arm rest I could power the radar detector with and I felt mounting the radar detector just above the Model Y's screen would be a cool look that wouldn't obstruct my view of the road.\"), mdx(\"p\", null, \"So I used the included Valentine1 12v power adapter and hid its' power cord under the seats and floor mats and up behind the screen. As far as mounting the radar detector, I used a \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://amzn.to/3uoajlm\"\n  }, \"retractable cell phone mount\"), \" that has a magnetic base and added a \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://amzn.to/3kUyIf2\"\n  }, \"magnetic plate\"), \" to the bottom of the radar detector. It came out really good. See how it came out in the below video.\"), mdx(Embed, {\n    src: \"https://www.youtube.com/embed/iF0OadDjIaw\",\n    mdxType: \"Embed\"\n  }), mdx(\"p\", null, \"This should work with other radar detectors and if yours doesn't come with a 12v adapter \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://amzn.to/3ldfYI6\"\n  }, \"this adapter\"), \" includes the cord and USB ports.\"), mdx(\"h2\", {\n    \"id\": \"mud-flaps\"\n  }, \"Mud flaps\"), mdx(\"p\", null, \"Reading forum posts it seemed like many people were complaining that the Model Y's flared back end made it easier for it to be impacted by road debris kicked up by the front tires. I think the likelihood or severity of this is probably more regional to areas with more gravel roads or where road salt use is common.\"), mdx(\"span\", {\n    \"className\": \"gatsby-resp-image-wrapper\",\n    \"style\": {\n      \"position\": \"relative\",\n      \"display\": \"block\",\n      \"marginLeft\": \"auto\",\n      \"marginRight\": \"auto\",\n      \"maxWidth\": \"640px\"\n    }\n  }, \"\\n      \", mdx(\"span\", {\n    parentName: \"span\",\n    \"className\": \"gatsby-resp-image-background-image\",\n    \"style\": {\n      \"paddingBottom\": \"75.1219512195122%\",\n      \"position\": \"relative\",\n      \"bottom\": \"0\",\n      \"left\": \"0\",\n      \"backgroundImage\": \"url('data:image/jpeg;base64,/9j/2wBDAAMCAgMCAgMDAwMEAwMEBQgFBQQEBQoHBwYIDAoMDAsKCwsNDhIQDQ4RDgsLEBYQERMUFRUVDA8XGBYUGBIUFRT/2wBDAQMEBAUEBQkFBQkUDQsNFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBT/wgARCAAPABQDASIAAhEBAxEB/8QAFwAAAwEAAAAAAAAAAAAAAAAAAAYHBf/EABYBAQEBAAAAAAAAAAAAAAAAAAIEBv/aAAwDAQACEAMQAAAB05u+vQSkW8h1P//EABsQAQEAAgMBAAAAAAAAAAAAAAMEBQYAAQIV/9oACAEBAAEFApXPGw7PQ1CFP7oJMWrnHoM6SfPk65//xAAbEQACAQUAAAAAAAAAAAAAAAAAAgEFEiExYf/aAAgBAwEBPwGcCV90WFs10//EABgRAAMBAQAAAAAAAAAAAAAAAAABEQRB/9oACAECAQE/AU66PJen/8QAIxAAAgEDAgcBAAAAAAAAAAAAAQIDABESFCEEEyIxMkJRYf/aAAgBAQAGPwLOGIEr4i3YfaSaYnnq3S35SyuoycX3pIExWeJtri+9Pr2MvESENkPWraVa/8QAHhABAAICAgMBAAAAAAAAAAAAAREhAFExQWFxoeH/2gAIAQEAAT8hh06iRNDDt0Z2wn7JKfIcNVAwXd5DhxRUHTxr8w+dJCUZr3ry4c0xsnP/2gAMAwEAAgADAAAAEBTf/8QAGBEBAAMBAAAAAAAAAAAAAAAAAQARgTH/2gAIAQMBAT8QFAOQY2oGMn//xAAZEQEAAgMAAAAAAAAAAAAAAAABABExwdH/2gAIAQIBAT8QslmCl37P/8QAGxABAQADAAMAAAAAAAAAAAAAAREAITFBYZH/2gAIAQEAAT8QhnACBAfLbodCxcLjr1CaEsQdu3RMYRlaEVo5tWe8N4LMgAoxUViynVyYrK2EB4AERoIdynCUE39W5//Z')\",\n      \"backgroundSize\": \"cover\",\n      \"display\": \"block\"\n    }\n  }), \"\\n  \", mdx(\"img\", {\n    parentName: \"span\",\n    \"className\": \"gatsby-resp-image-image\",\n    \"alt\": \"Tesla model y mud flaps\",\n    \"title\": \"Tesla model y mud flaps\",\n    \"src\": \"/static/d5d339308a1f0b82c15c2fcaa6029251/bbe0c/tesla-mud-flaps.jpg\",\n    \"srcSet\": [\"/static/d5d339308a1f0b82c15c2fcaa6029251/bd2b6/tesla-mud-flaps.jpg 205w\", \"/static/d5d339308a1f0b82c15c2fcaa6029251/ceeba/tesla-mud-flaps.jpg 410w\", \"/static/d5d339308a1f0b82c15c2fcaa6029251/bbe0c/tesla-mud-flaps.jpg 640w\"],\n    \"sizes\": \"(max-width: 640px) 100vw, 640px\",\n    \"style\": {\n      \"width\": \"100%\",\n      \"height\": \"100%\",\n      \"margin\": \"0\",\n      \"verticalAlign\": \"middle\",\n      \"position\": \"absolute\",\n      \"top\": \"0\",\n      \"left\": \"0\"\n    },\n    \"loading\": \"lazy\"\n  }), \"\\n    \"), mdx(\"p\", null, \"In addition, Tesla seems to have started to include them standard in those regions. To be on the safe side I purchased the \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://shop.tesla.com/product/model-y-mud-flaps\"\n  }, \"mud flaps\"), \" from the Tesla store. They installed really easy by just popping out the existing plastic rivets and pushing in the ones they supplied. Installation did not require drilling. They look fine to me and function well so far it seems. No rubbing going over speed bumps in the long range version.\"), mdx(\"p\", null, \"People who want to go with a set for the front and back seem to gravitate toward \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://amzn.to/3iLvuZS\"\n  }, \"basenor mud flaps\"), \" or ones from Tesmanian.\"), mdx(\"h2\", {\n    \"id\": \"final-thoughts\"\n  }, \"Final Thoughts\"), mdx(\"p\", null, \"It's easy to go overboard with car accessories when you purchase a new car. Some get really cheasy or you may find you don't really need in hindsight like aftermarket lights, carbon fiber accents, protector protectors, organizers, and so on. I hope you found that this article helps you find the best of the must-haves.\"));\n}\n;\nMDXContent.isMDXComponent = true;","excerpt":"When I ordered my Tesla Model Y in June 2021 it had about a 3 month delivery lead time. I spent that time researching what accessories I…","fields":{"slug":"/tesla-model-y-accessories-guide/","type":"posts"},"headings":[{"value":"Tesla Floor and Cargo Mats","depth":2},{"value":"Tesla Infotainment Screen Protector","depth":2},{"value":"Tesla Garage Door Opener","depth":2},{"value":"Radar Detector Mount","depth":2},{"value":"Mud flaps","depth":2},{"value":"Final Thoughts","depth":2}],"frontmatter":{"title":"What to Buy your Model Y","description":"There are a lot Tesla Model Y accessory options out there. Now that my model Y arrived here are my reviews for the best accessories for your Tesla Model Y that you'll want day 1. Benefit from my hindsight.","link":null,"date":"2021-10-10T00:00:00.000Z","tags":["tesla","model y","floor mat","accessories","smart home","iot","post"],"draft":null,"hide":null,"cover":{"childImageSharp":{"fluid":{"base64":"data:image/jpeg;base64,/9j/2wBDAAMCAgMCAgMDAwMEAwMEBQgFBQQEBQoHBwYIDAoMDAsKCwsNDhIQDQ4RDgsLEBYQERMUFRUVDA8XGBYUGBIUFRT/2wBDAQMEBAUEBQkFBQkUDQsNFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBT/wgARCAAKABQDASIAAhEBAxEB/8QAGQAAAgMBAAAAAAAAAAAAAAAAAAcDBAUG/8QAFgEBAQEAAAAAAAAAAAAAAAAABAEC/9oADAMBAAIQAxAAAAHH56wvzVkkYHH/xAAcEAACAgIDAAAAAAAAAAAAAAACBAMFAAEGFjP/2gAIAQEAAQUCavZVJm79I868dhHyvzS1o2BmkEf/xAAaEQACAwEBAAAAAAAAAAAAAAACAwABEQQj/9oACAEDAQE/AWKUyvQdg86RrKGf/8QAGhEAAgIDAAAAAAAAAAAAAAAAAQIAEgMTIf/aAAgBAgEBPwGq5FFhNRXgM//EACQQAAIBAgMJAAAAAAAAAAAAAAECABIhAwQRExQxQlFhcYHR/9oACAEBAAY/AtlmKndbUiwt6gO50P1BHyYWYvhh0BCnQRG5quMFQq8wAOwHYz//xAAcEAEBAAICAwAAAAAAAAAAAAABEQAxIVFBcZH/2gAIAQEAAT8hvoBpIKNNri9cGqC9xwoF09A9YTi8GHnrvIrfXJM1pTP/2gAMAwEAAgADAAAAEKP/AP/EABoRAQACAwEAAAAAAAAAAAAAAAEAEUFRcWH/2gAIAQMBAT8QaUoaHXM93AVYPZ//xAAaEQEAAwADAAAAAAAAAAAAAAABABExIUGR/9oACAECAQE/EGoSb7tPUCcMMKn/xAAbEAEAAwEBAQEAAAAAAAAAAAABABEhMVGBsf/aAAgBAQABPxB2HUspRGANiZnYpD7lkUom7WOQPnyHyywHr3Y6kyRkRYdUwkB8B+oa6YIHwZ//2Q==","aspectRatio":2,"src":"/static/13d8ff9e0ef22231191af976e62f694c/60a5f/red-tesla-model-y.jpg","srcSet":"/static/13d8ff9e0ef22231191af976e62f694c/3e5eb/red-tesla-model-y.jpg 192w,\n/static/13d8ff9e0ef22231191af976e62f694c/2880b/red-tesla-model-y.jpg 384w,\n/static/13d8ff9e0ef22231191af976e62f694c/60a5f/red-tesla-model-y.jpg 768w","srcWebp":"/static/13d8ff9e0ef22231191af976e62f694c/25278/red-tesla-model-y.webp","srcSetWebp":"/static/13d8ff9e0ef22231191af976e62f694c/a278a/red-tesla-model-y.webp 192w,\n/static/13d8ff9e0ef22231191af976e62f694c/2474b/red-tesla-model-y.webp 384w,\n/static/13d8ff9e0ef22231191af976e62f694c/25278/red-tesla-model-y.webp 768w","sizes":"(max-width: 768px) 100vw, 768px","presentationWidth":768,"presentationHeight":382}}}}},{"id":"41002bae-2dd2-5fce-8f7b-2352d46bac47","body":"var _excluded = [\"components\"];\nfunction _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }\nfunction _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }\nfunction _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }\n/* @jsxRuntime classic */\n/* @jsx mdx */\n\nvar _frontmatter = {\n  \"title\": \"Oupes 600w Portable Power Station Generator Review\",\n  \"cover\": \"./oupes-battery-power-station-600watt.jpg\",\n  \"date\": \"2021-07-21T00:00:00.000Z\",\n  \"description\": \"I've been researching battery-powered power stations or generators as hurricane-season approaches and this review goes over the features of the Oupes 600w, 595wh, inverter power versus similar models from Jackery and GoalZero.\",\n  \"tags\": [\"power station\", \"power\", \"battery\", \"smart home\", \"iot\", \"post\"]\n};\nvar makeShortcode = function makeShortcode(name) {\n  return function MDXDefaultShortcode(props) {\n    console.warn(\"Component \" + name + \" was not imported, exported, or provided by MDXProvider as global scope\");\n    return mdx(\"div\", props);\n  };\n};\nvar Embed = makeShortcode(\"Embed\");\nvar layoutProps = {\n  _frontmatter: _frontmatter\n};\nvar MDXLayout = \"wrapper\";\nreturn function MDXContent(_ref) {\n  var components = _ref.components,\n    props = _objectWithoutProperties(_ref, _excluded);\n  return mdx(MDXLayout, _extends({}, layoutProps, props, {\n    components: components,\n    mdxType: \"MDXLayout\"\n  }), mdx(\"p\", null, \"I've wanted a portable battery-powered inverter, power station, for years now in case we encounter a hurricane-caused blackout or other extended power outage in my area of Florida. With it, I could keep cell phones and computers charged in an emergency to keep aware of what is going on. However, the price and technology have held me back until this point. Older models were based around heavy unreliable lead-acid batteries and were quite expensive. Additionally, since the batteries only last for so long I wanted to be able to recharge or extend the runtime using a solar panel which used to be expensive as well.\"), mdx(\"p\", null, \"Oupes has released a portable power station based on lithium iron phosphate (LiFePO4) batteries which are much lighter and retain their charge in storage better than lead-acid batteries. It can also support charging from multiple sources; AC, car, solar. Its inverter has native USB ports and AC adapters so I can charge my cell phones and computers in an emergency like I wanted. That combined with prices for solar panels dropping meant it was a great time to try out a portable power station.\"), mdx(\"p\", null, \"At the bottom of the article I'll include a 10% discount code from Oupes to purchase the \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://amzn.to/3kAgiAt\"\n  }, \"portable power station\"), \". Full disclosure \", mdx(\"em\", {\n    parentName: \"p\"\n  }, \"as an Amazon Associate I earn from qualifying purchases made through links on this site\"), \" which helps support it, but doesn't add any cost to your purchase. \"), mdx(\"h2\", {\n    \"id\": \"features-of-the-600w-oupes-battery-generator--power-station\"\n  }, \"Features of the 600w Oupes battery generator / power station\"), mdx(\"p\", null, \"In the unboxing video below the the \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://amzn.to/3kAgiAt\"\n  }, \"Oupes power station\"), \" features are fully tested including powering phones, an iPad, tire compression, 42\\\" TV, and charging over AC, solar, and car cigarette adapter.\"), mdx(Embed, {\n    src: \"https://www.youtube.com/embed/vJP2yv5ZpaY\",\n    mdxType: \"Embed\"\n  }), mdx(\"p\", null, \"Oupes front charging ports (outlets)\"), mdx(\"ul\", null, mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"2x USB-A\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"1x USB-C\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"2x AC outlets\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"12v marine connector\")), mdx(\"p\", null, \"One of the nicest differentiators the Oupes power station has over competitors like Jackery or Goal Zero is the easy to read color LCD display that shows power draw, battery percent remaining, remaining runtime, and active outlets. In addition, the LiFePO4 is newer, allegedly safer, battery technology.\"), mdx(\"h2\", {\n    \"id\": \"charging\"\n  }, \"Charging\"), mdx(\"p\", null, \"Included chargers\"), mdx(\"ul\", null, mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"1x AC adapter to DC7909 connector\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"1x car charger (cigarette) to DC 7909 connector\")), mdx(\"p\", null, \"When charging the power station itself you can use the included DC7909 barrel-style connector on the side which accepts 12-30v at 100w max or it can charge at 60w through the USB-C.\"), mdx(\"h3\", {\n    \"id\": \"solar-generator-charging\"\n  }, \"Solar Generator Charging\"), mdx(\"p\", null, \"Most solar panels have a pair of + and - \", mdx(\"em\", {\n    parentName: \"p\"\n  }, \"MC4 connectors\"), \" coming from them. In order to connect a solar panel to recharge the Oupes battery you need to connect the MC4 to the DC7909 barrel connector.\"), mdx(\"p\", null, \"There are two ways of doing this:\"), mdx(\"p\", null, \"Option 1: Use a \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://amzn.to/3wVzCLb\"\n  }, \"MC4 to Female Cigarette Socket\"), \" and connect it to the male cigarette adapter provided with the Oupes unit\"), mdx(\"p\", null, mdx(\"em\", {\n    parentName: \"p\"\n  }, \"or\")), mdx(\"p\", null, \"Option 2: Use a \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://amzn.to/3rr1Ooe\"\n  }, \"MC4 to direct DC 7909 plug\"), \" to plug directly into the Oupes without the adapter\"), mdx(\"p\", null, \"I tried using battery clamps on a 30 watt panel and found it was only putting in about 5 input watts according to the display on the front of the Oupes power station. I'd recommend using a larger, \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://amzn.to/3BraXSm\"\n  }, \"100 watt solar panel\"), \", for faster charging with one of these adapters attached.\"), mdx(\"p\", null, \"It would probably take about 2 days to fully charge the 595wH battery using a 100 watt solar panel from empty.\"), mdx(\"p\", null, \"However, instead of letting it drain to empty before charging, you can extend the runtime by keeping the solar attached to it, charging, while using the power station's battery to power your devices.\"), mdx(\"h2\", {\n    \"id\": \"runtime\"\n  }, \"Runtime\"), mdx(\"p\", null, \"The Oupes' inverter inside the power station can output 600 watts and it has a capacity of 595 watt hours. What does that mean in real-world scenarios though?\"), mdx(\"p\", null, \"Idling, I found 2 iPhones, an iPad Pro, and Macbook Pro were using about 35 watts of power which would result in a 9.2 hour runtime. My AC-powered tire air compressor drew 118 watts and a 3.6 hour runtime. Last, my 42\\\" FireTV set drew 87 watts resulting in a 5.4 hour runtime. That is all really impressive.\"), mdx(\"p\", null, \"Here are examples from the manual:\"), mdx(\"ul\", null, mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"8 laptop recharges (assuming 60Wh laptop battery)\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"42 phone charges (3,110mAh/charge)\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"17 tablet recharges\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"50 hours of running a 10 watt LED bulb\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"8 drone charges\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"10 hours running a 50watt mini-fridge\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"17, (30wh) cordless drill recharges\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"CPAP machine (40w model) 12.6 hours\")), mdx(\"h2\", {\n    \"id\": \"portability\"\n  }, \"Portability\"), mdx(\"p\", null, \"The Oupes power station has a strong carry handle at the top. The LiFePO4 battery is lighter than lead acid so the entire unit only weighed in at about 15.6lbs though the manual says 8.6kg.\"), mdx(\"p\", null, \"It would be good for camping since it is carryable and not too heavy, but it only has an IP21 waterproof rating which means it is rated for condensation, but not rain, splashing, or dunking in water.\"), mdx(\"h2\", {\n    \"id\": \"competitors-like-jackery-and-goalzero\"\n  }, \"Competitors like Jackery and GoalZero\"), mdx(\"p\", null, \"Two close competitors to the Oupes power station would be the \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://amzn.to/3xPrptc\"\n  }, \"Jackery Explorer 500\"), \" and the \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://amzn.to/36OHeo5\"\n  }, \"GoalZero Yeti 400\"), \". These seem to be closest in specs and price to the Oupes 600 watt generator.\"), mdx(\"table\", null, mdx(\"thead\", {\n    parentName: \"table\"\n  }, mdx(\"tr\", {\n    parentName: \"thead\"\n  }, mdx(\"th\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"Model\"), mdx(\"th\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"Max output\"), mdx(\"th\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"Capacity (Wh)\"), mdx(\"th\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"USB-C\"), mdx(\"th\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"USB-A\"), mdx(\"th\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"AC Ports\"), mdx(\"th\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"Solar?\"), mdx(\"th\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"Battery Type\"), mdx(\"th\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"Weight\"))), mdx(\"tbody\", {\n    parentName: \"table\"\n  }, mdx(\"tr\", {\n    parentName: \"tbody\"\n  }, mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"Oupes 600\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"600 watts\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"595 Wh\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"1\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"2\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"2\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"Yes\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"Lithium Iron Phosphate\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"15.6lb\")), mdx(\"tr\", {\n    parentName: \"tbody\"\n  }, mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"Jackery Explorer 500\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"500 watts\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"518 Wh\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"0\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"3\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"1\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"Yes\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"Lithium\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"13.3lb\")), mdx(\"tr\", {\n    parentName: \"tbody\"\n  }, mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"Goal Zero Yeti 400\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"300 watts\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"396 Wh\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"0\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"2\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"2\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"Yes\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"Lead acid\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"29lb\")))), mdx(\"p\", null, \"So for about the same price the Oupes has better output and capacity and includes a much easier to read \", mdx(\"em\", {\n    parentName: \"p\"\n  }, \"color\"), \" LCD screen than Jackery or Goal Zero. The Jackery is 2.3 pounds lighter which can be an advantage when camping, but it does have less output and storage capacity.\"), mdx(\"p\", null, \"The Oupes has the latest advancements over the others which are admittedly older designs. I prefer the newer advancements.\"), mdx(\"h2\", {\n    \"id\": \"final-thoughts\"\n  }, \"Final Thoughts\"), mdx(\"p\", null, \"I hope this review of the Oupes portable power station and solar generator provided some meaning behind the technical specs. If you are interested in purchasing one please consider using my affiliate link \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://amzn.to/3kAgiAt\"\n  }, \"https://amzn.to/3kAgiAt\"), \" to do so. You can use coupon code B9DBUVZP for 10% off. Thanks!\"));\n}\n;\nMDXContent.isMDXComponent = true;","excerpt":"I've wanted a portable battery-powered inverter, power station, for years now in case we encounter a hurricane-caused blackout or other…","fields":{"slug":"/oupes-portable-power-station-generator-vs-jackery/","type":"posts"},"headings":[{"value":"Features of the 600w Oupes battery generator / power station","depth":2},{"value":"Charging","depth":2},{"value":"Solar Generator Charging","depth":3},{"value":"Runtime","depth":2},{"value":"Portability","depth":2},{"value":"Competitors like Jackery and GoalZero","depth":2},{"value":"Final Thoughts","depth":2}],"frontmatter":{"title":"Oupes 600w Portable Power Station Generator Review","description":"I've been researching battery-powered power stations or generators as hurricane-season approaches and this review goes over the features of the Oupes 600w, 595wh, inverter power versus similar models from Jackery and GoalZero.","link":null,"date":"2021-07-21T00:00:00.000Z","tags":["power station","power","battery","smart home","iot","post"],"draft":null,"hide":null,"cover":{"childImageSharp":{"fluid":{"base64":"data:image/jpeg;base64,/9j/2wBDAAMCAgMCAgMDAwMEAwMEBQgFBQQEBQoHBwYIDAoMDAsKCwsNDhIQDQ4RDgsLEBYQERMUFRUVDA8XGBYUGBIUFRT/2wBDAQMEBAUEBQkFBQkUDQsNFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBT/wgARCAAMABQDASIAAhEBAxEB/8QAFgABAQEAAAAAAAAAAAAAAAAABgAF/8QAFQEBAQAAAAAAAAAAAAAAAAAAAwT/2gAMAwEAAhADEAAAASpHSIhSusqGr//EABwQAAIDAAMBAAAAAAAAAAAAAAIFAQMEAAYSE//aAAgBAQABBQLsbS9fOxux5RsnTW/q+5HhGBDzAf/EABsRAAEEAwAAAAAAAAAAAAAAAAABAgQhAxIi/9oACAEDAQE/Acsdd6Hxels//8QAGxEAAQQDAAAAAAAAAAAAAAAAAgABAyEEETH/2gAIAQIBAT8BhmFw2TWhyK4v/8QAJBAAAgECBAcBAAAAAAAAAAAAAQIRAAMEEjFxEyEiIzORocH/2gAIAQEABj8CwVnDuVThZiF31Pqle5ezu6yCsTFKTYR2UAFideU/tYN5KdrL0715H+UoKI8CJZa//8QAHRABAQABBAMAAAAAAAAAAAAAAREAITFBYVGhwf/aAAgBAQABPyF4h2uLRDnEMKPYA1N8izzoqfDAMMuZCV13lFWHKvmGJSAlkz//2gAMAwEAAgADAAAAEN//AP/EABoRAQABBQAAAAAAAAAAAAAAAAEAESFBUeH/2gAIAQMBAT8QuTA4Qilnqf/EABkRAAMBAQEAAAAAAAAAAAAAAAERIQChwf/aAAgBAgEBPxAbYYoyU+aF97//xAAbEAEBAAMAAwAAAAAAAAAAAAABEQAhMUFh8P/aAAgBAQABPxADXjSXiERHdXkyR4hwZAvIomveJnQGkpQWEAFRJu4raqvnFASgEge9zornwwgs+uf/2Q==","aspectRatio":1.6271186440677967,"src":"/static/376a3f79d0ffb4ac73853a0ca54ccbf4/ccdac/oupes-battery-power-station-600watt.jpg","srcSet":"/static/376a3f79d0ffb4ac73853a0ca54ccbf4/3e5eb/oupes-battery-power-station-600watt.jpg 192w,\n/static/376a3f79d0ffb4ac73853a0ca54ccbf4/2880b/oupes-battery-power-station-600watt.jpg 384w,\n/static/376a3f79d0ffb4ac73853a0ca54ccbf4/ccdac/oupes-battery-power-station-600watt.jpg 622w","srcWebp":"/static/376a3f79d0ffb4ac73853a0ca54ccbf4/c32a3/oupes-battery-power-station-600watt.webp","srcSetWebp":"/static/376a3f79d0ffb4ac73853a0ca54ccbf4/a278a/oupes-battery-power-station-600watt.webp 192w,\n/static/376a3f79d0ffb4ac73853a0ca54ccbf4/2474b/oupes-battery-power-station-600watt.webp 384w,\n/static/376a3f79d0ffb4ac73853a0ca54ccbf4/c32a3/oupes-battery-power-station-600watt.webp 622w","sizes":"(max-width: 622px) 100vw, 622px","presentationWidth":622,"presentationHeight":382}}}}},{"id":"6906f41f-8c51-5a46-bde0-d7a909de1e73","body":"var _excluded = [\"components\"];\nfunction _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }\nfunction _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }\nfunction _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }\n/* @jsxRuntime classic */\n/* @jsx mdx */\n\nvar _frontmatter = {\n  \"title\": \"Testing the Flume 2 for wifi water monitoring and leak detection\",\n  \"cover\": \"./flume2-review.jpg\",\n  \"date\": \"2021-07-06T00:00:00.000Z\",\n  \"description\": \"The Flume 2 provides wifi whole-house water monitoring without requiring cutting plumbing or external power allowing you to see check water usage and get notified of leaks in near real time. Learn about how to install the Flume and my impressions after 3 months.\",\n  \"tags\": [\"water\", \"wifi\", \"leak detection\", \"smart home\", \"iot\", \"post\"]\n};\nvar makeShortcode = function makeShortcode(name) {\n  return function MDXDefaultShortcode(props) {\n    console.warn(\"Component \" + name + \" was not imported, exported, or provided by MDXProvider as global scope\");\n    return mdx(\"div\", props);\n  };\n};\nvar Embed = makeShortcode(\"Embed\");\nvar layoutProps = {\n  _frontmatter: _frontmatter\n};\nvar MDXLayout = \"wrapper\";\nreturn function MDXContent(_ref) {\n  var components = _ref.components,\n    props = _objectWithoutProperties(_ref, _excluded);\n  return mdx(MDXLayout, _extends({}, layoutProps, props, {\n    components: components,\n    mdxType: \"MDXLayout\"\n  }), mdx(\"h2\", {\n    \"id\": \"why-i-added-a-smart-water-meter-to-my-smart-home\"\n  }, \"Why I added a smart water meter to my smart home\"), mdx(\"p\", null, \"Last year I discovered, the hard way, an intermittent leak in a pipe run under the foundation was emitting through my bathroom wall. It left a pretty expensive mess to deal with and, due to the nature of it, was difficult to locate, since it seemed to trigger seemingly randomly, even with the help of leak detection experts.\"), mdx(\"span\", {\n    \"className\": \"gatsby-resp-image-wrapper\",\n    \"style\": {\n      \"position\": \"relative\",\n      \"display\": \"block\",\n      \"marginLeft\": \"auto\",\n      \"marginRight\": \"auto\",\n      \"maxWidth\": \"500px\"\n    }\n  }, \"\\n      \", mdx(\"span\", {\n    parentName: \"span\",\n    \"className\": \"gatsby-resp-image-background-image\",\n    \"style\": {\n      \"paddingBottom\": \"71.21951219512195%\",\n      \"position\": \"relative\",\n      \"bottom\": \"0\",\n      \"left\": \"0\",\n      \"backgroundImage\": \"url('data:image/jpeg;base64,/9j/2wBDAAMCAgMCAgMDAwMEAwMEBQgFBQQEBQoHBwYIDAoMDAsKCwsNDhIQDQ4RDgsLEBYQERMUFRUVDA8XGBYUGBIUFRT/2wBDAQMEBAUEBQkFBQkUDQsNFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBT/wgARCAAOABQDASIAAhEBAxEB/8QAGAAAAgMAAAAAAAAAAAAAAAAAAAcCBAb/xAAWAQEBAQAAAAAAAAAAAAAAAAAEAwX/2gAMAwEAAhADEAAAAVzPOtFM1WXTPR//xAAdEAACAQQDAAAAAAAAAAAAAAADBAIAAQUUBhMk/9oACAEBAAEFAuvdflgAFp3jc1j7vrEtZ9Nw0CM//8QAGREAAgMBAAAAAAAAAAAAAAAAAAECAxFR/9oACAEDAQE/Aa5a3ww//8QAGREAAgMBAAAAAAAAAAAAAAAAAAECAxEx/9oACAECAQE/Abq4qK3qFp//xAAjEAABAwMDBQEAAAAAAAAAAAABAgMRABITBCEiFDJRcXKB/9oACAEBAAY/Aitho9M2rH3QT+VAOM7ykco3qxRan6pFwtYBusbpvUY06ZmdsaiXD7NLlEgcUz4Ff//EAB4QAQABBAIDAAAAAAAAAAAAAAERACExQVGBYXGx/9oACAEBAAE/ISIeJyzxhj7SUQoMu2EjHOqZCdO036rMTXlmpZdJeIUgB2BV8I7CWL9jX//aAAwDAQACAAMAAAAQGB//xAAbEQEBAAEFAAAAAAAAAAAAAAABEQAhMUFR8P/aAAgBAwEBPxBgSppd5z2ecTdM/8QAGhEBAQADAQEAAAAAAAAAAAAAAREAIUExUf/aAAgBAgEBPxDzgUYUd6qx+13zmQCuf//EABoQAQEBAQEBAQAAAAAAAAAAAAERIQBhMUH/2gAIAQEAAT8QfT1xCs2qwthAdEC7jEHQ2NT4PSVhTNh8AEaiecSP0m2GxVdhVXd5sYFPrdNOi/o84/t8+Iq60X3v/9k=')\",\n      \"backgroundSize\": \"cover\",\n      \"display\": \"block\"\n    }\n  }), \"\\n  \", mdx(\"img\", {\n    parentName: \"span\",\n    \"className\": \"gatsby-resp-image-image\",\n    \"alt\": \"Plumbers finding the leak\",\n    \"title\": \"Plumbers finding the leak\",\n    \"src\": \"/static/84c4580c8758bb0b48100ab9468b78d3/d1f95/plumbers.jpg\",\n    \"srcSet\": [\"/static/84c4580c8758bb0b48100ab9468b78d3/bd2b6/plumbers.jpg 205w\", \"/static/84c4580c8758bb0b48100ab9468b78d3/ceeba/plumbers.jpg 410w\", \"/static/84c4580c8758bb0b48100ab9468b78d3/d1f95/plumbers.jpg 500w\"],\n    \"sizes\": \"(max-width: 500px) 100vw, 500px\",\n    \"style\": {\n      \"width\": \"100%\",\n      \"height\": \"100%\",\n      \"margin\": \"0\",\n      \"verticalAlign\": \"middle\",\n      \"position\": \"absolute\",\n      \"top\": \"0\",\n      \"left\": \"0\"\n    },\n    \"loading\": \"lazy\"\n  }), \"\\n    \"), mdx(\"p\", null, \"This left me paranoid and for peace of mind I started researching to see if there were any whole-house smart wifi water monitors or leak detectors I could install. In my research I found several promising ones which I'll go over in the remainder of this article, why I ultimately chose the Flume 2, and my experience with it in the 3 months since.\"), mdx(\"p\", null, \"As I go through these I want to mention that \", mdx(\"em\", {\n    parentName: \"p\"\n  }, \"as an Amazon Associate I earn from qualifying purchases made through links on this site\"), \" which helps support it, but doesn't add any cost to your purchase. At the end of the article, you can also use my Flume referral code for $20 off a Flume2.\"), mdx(\"h2\", {\n    \"id\": \"top-3-best-smart-water-leak-detectors-in-2021\"\n  }, \"Top 3 Best Smart Water Leak Detectors in 2021\"), mdx(\"p\", null, \"I narrowed my list down to the \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://amzn.to/2TEiOKX\"\n  }, \"Flume 2\"), \", \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://amzn.to/3e4dU19\"\n  }, \"Moen Flo\"), \", and \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://amzn.to/3hBP89s\"\n  }, \"Streamlabs\"), \". Unlike leak sensors you place under likely places a leak might occur like near a toilet or under a sink that alarm when water is detected, these all attach to the main water supply for your house allowing them to measure the water flow for your entire house. \"), mdx(\"p\", null, \"They have intelligence to alert you to unusual water usage. The Moen Flo even has an integrated shut off valve allowing you to remotely shut off water to your house in an emergency.\"), mdx(\"p\", null, \"The \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://amzn.to/3e4dU19\"\n  }, \"Moen Flo\"), \" is the most expensive, but the integrated water shut off is worth it in my opinion and the specifications claim it can detect up to 1 drop per minute or \\\"microleaks\\\". It can also detect high pressure and has a temperature sensor for freeze warnings.\"), mdx(\"p\", null, \"However, for the Moen Flo installation you need enough exposed pipe to cut your existing pipe and install the Flo and also have a nearby source of power to plug it into. The Streamlabs doesn't require plumbing, but it also needs power, and isn't as sensitive as either other option (it theoretically wouldn't be as good at finding small leaks).\"), mdx(\"p\", null, \"Florida houses like mine don't have power nearby the main shut off and there is no basement or crawl space to access more of the pipe than this.\"), mdx(\"span\", {\n    \"className\": \"gatsby-resp-image-wrapper\",\n    \"style\": {\n      \"position\": \"relative\",\n      \"display\": \"block\",\n      \"marginLeft\": \"auto\",\n      \"marginRight\": \"auto\",\n      \"maxWidth\": \"500px\"\n    }\n  }, \"\\n      \", mdx(\"span\", {\n    parentName: \"span\",\n    \"className\": \"gatsby-resp-image-background-image\",\n    \"style\": {\n      \"paddingBottom\": \"70.73170731707317%\",\n      \"position\": \"relative\",\n      \"bottom\": \"0\",\n      \"left\": \"0\",\n      \"backgroundImage\": \"url('data:image/jpeg;base64,/9j/2wBDAAMCAgMCAgMDAwMEAwMEBQgFBQQEBQoHBwYIDAoMDAsKCwsNDhIQDQ4RDgsLEBYQERMUFRUVDA8XGBYUGBIUFRT/2wBDAQMEBAUEBQkFBQkUDQsNFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBT/wgARCAAOABQDASIAAhEBAxEB/8QAGAAAAgMAAAAAAAAAAAAAAAAAAAYDBAf/xAAXAQADAQAAAAAAAAAAAAAAAAAAAQME/9oADAMBAAIQAxAAAAF7oR5xla6MAS//xAAZEAEAAgMAAAAAAAAAAAAAAAAEAQUCAwb/2gAIAQEAAQUCurBJTKWidtvDAuvm4HB1WzKUF6uzCb//xAAZEQADAAMAAAAAAAAAAAAAAAAAAQIREhP/2gAIAQMBAT8BqlnU5M//xAAZEQADAAMAAAAAAAAAAAAAAAAAAQISMVH/2gAIAQIBAT8BdLTMI6f/xAAhEAACAgICAQUAAAAAAAAAAAABAgMRABIEITEiMkFRsf/aAAgBAQAGPwLaF1TRS52W7r4xGbmRRwOnUgSgL8E48fJWVZPPtsEfYyNnTcM4Qj9xYV9MCHQJeRRJyNkA63W6z//EABsQAQACAwEBAAAAAAAAAAAAAAERIQAxQXFR/9oACAEBAAE/Ib410N2pwibyEljsNKXyvs4mCxMSWkCExSIgMKI7cqcZBE40Fe63muuMGJ1Ln//aAAwDAQACAAMAAAAQXw//xAAYEQACAwAAAAAAAAAAAAAAAAAAEQFB8P/aAAgBAwEBPxCEyzDP/8QAFxEBAQEBAAAAAAAAAAAAAAAAAQAhQf/aAAgBAgEBPxAOkL1f/8QAGhABAQADAQEAAAAAAAAAAAAAAREAIUExUf/aAAgBAQABPxBWCLAkBUKBw2hlbZuIBREnluxxMF9g+9SZIaT5NJMkMm0MB0YDuLla5CIZpfXNl1YGdnj38B3ITV+5/9k=')\",\n      \"backgroundSize\": \"cover\",\n      \"display\": \"block\"\n    }\n  }), \"\\n  \", mdx(\"img\", {\n    parentName: \"span\",\n    \"className\": \"gatsby-resp-image-image\",\n    \"alt\": \"Main water shut off - no space\",\n    \"title\": \"Main water shut off - no space\",\n    \"src\": \"/static/0136b476cdbd1e236a9a709d8a29df7c/d1f95/pipe.jpg\",\n    \"srcSet\": [\"/static/0136b476cdbd1e236a9a709d8a29df7c/bd2b6/pipe.jpg 205w\", \"/static/0136b476cdbd1e236a9a709d8a29df7c/ceeba/pipe.jpg 410w\", \"/static/0136b476cdbd1e236a9a709d8a29df7c/d1f95/pipe.jpg 500w\"],\n    \"sizes\": \"(max-width: 500px) 100vw, 500px\",\n    \"style\": {\n      \"width\": \"100%\",\n      \"height\": \"100%\",\n      \"margin\": \"0\",\n      \"verticalAlign\": \"middle\",\n      \"position\": \"absolute\",\n      \"top\": \"0\",\n      \"left\": \"0\"\n    },\n    \"loading\": \"lazy\"\n  }), \"\\n    \"), mdx(\"p\", null, \"This ultimately led me to the \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://amzn.to/2TEiOKX\"\n  }, \"Flume 2\"), \" because it non-invasively attaches to your water meter and is powered by replaceable lithium batteries that last for 2-4 years.\"), mdx(\"span\", {\n    \"className\": \"gatsby-resp-image-wrapper\",\n    \"style\": {\n      \"position\": \"relative\",\n      \"display\": \"block\",\n      \"marginLeft\": \"auto\",\n      \"marginRight\": \"auto\",\n      \"maxWidth\": \"334px\"\n    }\n  }, \"\\n      \", mdx(\"span\", {\n    parentName: \"span\",\n    \"className\": \"gatsby-resp-image-background-image\",\n    \"style\": {\n      \"paddingBottom\": \"65.36585365853658%\",\n      \"position\": \"relative\",\n      \"bottom\": \"0\",\n      \"left\": \"0\",\n      \"backgroundImage\": \"url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAANCAYAAACpUE5eAAAACXBIWXMAAA7DAAAOwwHHb6hkAAAC6klEQVQ4y2WT7WsUdxDHVwta+6aIUIqFWosgBcGW0r4otPgqKhrxOdyd3u5GWiEWTVWUSgWpBkREBCOCHrnUJ3J4d7ub3b295O4SjXchFxMv0Zioqc9aH8E/4ePsavSsLz7M/GZnvr8Z5reKOnSHqKBWbgc2NHyP0JX77xj+H1XfwkN339YFGoKiVonpl2+xp3eQpotl9gp/C/uKl9hXqqLYH8SaxO7sH2H9m/qAasGIdLa5fJVOO0VPLkNvd46+ngKDfReplItU+ktiSwyULtBfPE+v5LRlPTRpYmLCDwQbyyN4Topsp0O+q1PIkRfhru48Xed9CgE5iTvtaeKOHUz1nuDEIVy5wx9+h5k0nmdiWcmgyBIMM4lhnCOdfk1SMJJtxO129MFxohMjC4oqN2hD0uHN52wdGKXgGeRyDrZj8I/r4nW4ZAUv69DRmcF12zEsuSiVkA4dtGuPiMp06uC/REVLWTf2BHXgJuHmY2zNdtFdyJDPu7gZi1OWIcKm+O1vceRsiqBpJGlNJ9louGh9o6g3nqGOPUbZvmcHu/7cxIrvZhFes4zevh7yhayMbeO5ZjCyaaUwzVRgbVtigpPNcLL1BOt+nkfDr3Vs+msb+vbfUcI/fE30x9nUzP2Mn+bMJNZ8iDPxE7S0xDibOIudkfFdG9dzRMgi3trC8aNHaDsZZ3djA7/Mms7y+V9SO/8rar75AmWNe4GVsTMsWLKIhU0HiSRsNp9KsGrtalYurqFxYz0NGzR+U9ejh9ZSKzHtwGG2WB0sP9TMsg311MVOE+keINRRQtHH/kOX2aPy8n1fHRXGXzD72+/5SFGY8flMPp76CZOnTGPK1Gl8OnkStYkM2u2XaFKnS652/Yks52GwIMXfjE/wt/i+bEsfecDSIzEWH40TKV+nrnCJUPEKddkSS/fuJ9JTQRu+G+RG/Wdz+fWGgy0H/7Iv9oaJc/2NpwGa3/mI3H71Abp0UT/+HFWeSXVutf8KWr7rn6spKYYAAAAASUVORK5CYII=')\",\n      \"backgroundSize\": \"cover\",\n      \"display\": \"block\"\n    }\n  }), \"\\n  \", mdx(\"img\", {\n    parentName: \"span\",\n    \"className\": \"gatsby-resp-image-image\",\n    \"alt\": \"Flume 2 plumbing\",\n    \"title\": \"Flume 2 plumbing\",\n    \"src\": \"/static/b6e2af0c1a49acb9cd8e1df64344b054/78f35/flume2-plumbing.png\",\n    \"srcSet\": [\"/static/b6e2af0c1a49acb9cd8e1df64344b054/ad4a5/flume2-plumbing.png 205w\", \"/static/b6e2af0c1a49acb9cd8e1df64344b054/78f35/flume2-plumbing.png 334w\"],\n    \"sizes\": \"(max-width: 334px) 100vw, 334px\",\n    \"style\": {\n      \"width\": \"100%\",\n      \"height\": \"100%\",\n      \"margin\": \"0\",\n      \"verticalAlign\": \"middle\",\n      \"position\": \"absolute\",\n      \"top\": \"0\",\n      \"left\": \"0\"\n    },\n    \"loading\": \"lazy\"\n  }), \"\\n    \"), mdx(\"p\", null, \"Read on for more about my first hand experiences with the best \", mdx(\"em\", {\n    parentName: \"p\"\n  }, \"compatible\"), \" option for my house, the Flume 2.\"), mdx(\"h2\", {\n    \"id\": \"flume-2-installation\"\n  }, \"Flume 2 Installation\"), mdx(\"p\", null, \"The below how-to video goes over my experience installing the Flume water monitor on a Tesla4 water meter. Flume claims to be compatible with about 95% of water meters in the US, but you can use the \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://help.flumewater.com/en/articles/1618594-is-the-flume-device-compatible-with-all-water-meters\"\n  }, \"chat on their website\"), \" where they can help you confirm before purchasing.\"), mdx(\"p\", null, \"It's 5 minutes and worth your time, I promise \\uD83D\\uDC47\"), mdx(Embed, {\n    src: \"https://www.youtube.com/embed/-1y3UyRfWkE\",\n    mdxType: \"Embed\"\n  }), mdx(\"h3\", {\n    \"id\": \"installation-steps\"\n  }, \"Installation Steps\"), mdx(\"ol\", null, mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Download the Flume app to your phone, register\"), mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Locate the water meter in your yard\"), mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Strap Flume alongside your water meter\"), mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Plug the wireless bridge into power in your house that was wifi signal\"), mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Pair using the app prompts\"), mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Run some water from a faucet to calibrate when the app prompts you\")), mdx(\"p\", null, \"My installation was straightforward and everything detected right away.\"), mdx(\"h2\", {\n    \"id\": \"how-does-flume-work-does-it-work-well\"\n  }, \"How does Flume work? Does it work well?\"), mdx(\"p\", null, \"Somehow Flume reads the magnetic field of your water meter allowing it to detect, they claim, as low as .01 to .03 gallons per minute or around 1 gallon per hour which would be close to 152 drips (\", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://water.usgs.gov/edu/activity-drip.htm\"\n  }, \"assuming 15,140 drips in a gallon\"), \") of water per minute without needing to invasively cut and splice the Flume into your plumbing.\"), mdx(\"p\", null, \"The first thing I did was \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://www.youtube.com/watch?v=-1y3UyRfWkE&t=183s\"\n  }, \"test the flow rate\"), \" by filling up the known volume of a bucket with my garden hose and was shocked that it was exactly right. Flume also let's you check the accurancy through a tool on their website from your account there by comparing meter reads at least 24 hours apart.\"), mdx(\"h3\", {\n    \"id\": \"leak-detection\"\n  }, \"Leak detection\"), mdx(\"p\", null, \"There has been a few times since I've installed it where I've had Flume notify me by text and push notification that a leak was detected. I immediately panicked and started looking around the house.\"), mdx(\"span\", {\n    \"className\": \"gatsby-resp-image-wrapper\",\n    \"style\": {\n      \"position\": \"relative\",\n      \"display\": \"block\",\n      \"marginLeft\": \"auto\",\n      \"marginRight\": \"auto\",\n      \"maxWidth\": \"343px\"\n    }\n  }, \"\\n      \", mdx(\"span\", {\n    parentName: \"span\",\n    \"className\": \"gatsby-resp-image-background-image\",\n    \"style\": {\n      \"paddingBottom\": \"140%\",\n      \"position\": \"relative\",\n      \"bottom\": \"0\",\n      \"left\": \"0\",\n      \"backgroundImage\": \"url('data:image/jpeg;base64,/9j/2wBDAAMCAgMCAgMDAwMEAwMEBQgFBQQEBQoHBwYIDAoMDAsKCwsNDhIQDQ4RDgsLEBYQERMUFRUVDA8XGBYUGBIUFRT/2wBDAQMEBAUEBQkFBQkUDQsNFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBT/wgARCAAcABQDASIAAhEBAxEB/8QAGQAAAgMBAAAAAAAAAAAAAAAAAAcEBQYI/8QAFwEBAQEBAAAAAAAAAAAAAAAABAIAAf/aAAwDAQACEAMQAAABQ2pYFGoyENsI44p/NukuLwVIbf/EABsQAAMBAQEBAQAAAAAAAAAAAAIEBQMBBhEj/9oACAEBAAEFAoEJqltQ8uaGJAQ9wEAxbzX+7D+iZYt4BBYbK3qhPoT67crrHt67IF3pd//EABwRAAICAgMAAAAAAAAAAAAAAAABAhIRExQhQf/aAAgBAwEBPwG0mrI3GuUesnGXrP/EAB0RAAICAQUAAAAAAAAAAAAAAAACARIUAxMhQWH/2gAIAQIBAT8BlEVqSYfpv6bRaV5Mp+j/xAAlEAABBAAEBgMAAAAAAAAAAAABAAIDERITITEEFDJBUYEiJJH/2gAIAQEABj8CJZC0xbF8ugCefryO1Pwdd6KiDaAJDqFeU7Ea9o4ensm8RFK7Kf04R+hNc6UZZ3oVSfC18kuHqLvKJ4ad0d7jsfSLOZywdCY20USTZPcr/8QAHhABAQACAgMBAQAAAAAAAAAAAREAITFBUXGhgZH/2gAIAQEAAT8hlhC2O6S8vrESInBcEia86cUhDqY+7LdphD5i4umqt/M79t2yIFDAzyeEyYTrstpRf7xjR4EYPkcYV5me/etZposBL31+YrZ1Uquf/9oADAMBAAIAAwAAABDLD0//xAAbEQACAwADAAAAAAAAAAAAAAAAAREhMUGB0f/aAAgBAwEBPxB54KtoTWy4J7bPXh//xAAbEQEAAgMBAQAAAAAAAAAAAAABABEhMYFB0f/aAAgBAgEBPxAEKrLuSNHBv05mUYBXfs//xAAeEAEBAAICAgMAAAAAAAAAAAABEQAhMUFRkWGB4f/aAAgBAQABPxCqUwTAoKhpgfDBcUwyzhhFgb5nGJnSKWsDMjuANk4AfRjxLTG+ARbyERpA0C9LL6wXFSHt0TadJyTiI4+DbOJbAP4b5yUeonvoNCBLHeM6xKGDpIpOmU84kyiiHYN30YlGDhDaq8uf/9k=')\",\n      \"backgroundSize\": \"cover\",\n      \"display\": \"block\"\n    }\n  }), \"\\n  \", mdx(\"img\", {\n    parentName: \"span\",\n    \"className\": \"gatsby-resp-image-image\",\n    \"alt\": \"leak alert\",\n    \"title\": \"leak alert\",\n    \"src\": \"/static/0e0e412a18e665058100f1675d1edfc7/52c19/leak-detected.jpg\",\n    \"srcSet\": [\"/static/0e0e412a18e665058100f1675d1edfc7/bd2b6/leak-detected.jpg 205w\", \"/static/0e0e412a18e665058100f1675d1edfc7/52c19/leak-detected.jpg 343w\"],\n    \"sizes\": \"(max-width: 343px) 100vw, 343px\",\n    \"style\": {\n      \"width\": \"100%\",\n      \"height\": \"100%\",\n      \"margin\": \"0\",\n      \"verticalAlign\": \"middle\",\n      \"position\": \"absolute\",\n      \"top\": \"0\",\n      \"left\": \"0\"\n    },\n    \"loading\": \"lazy\"\n  }), \"\\n    \"), mdx(\"p\", null, \"I discovered the first time was a bathroom faucet not shut off all the way and the other two were when my temporary hose-end lawn sprinkler had a constant small drip from its O-ring after it shut off. The first time, their customer service even sent me a follow up message to make sure everything was ok!\"), mdx(\"p\", null, \"So leak detection does work, but it does require a constant detectable use for minimum 2 hours. If it is something under 1/100 of a gallon it may not pick it up automatically.\"), mdx(\"p\", null, \"The usage chart is good for picking up usage at odd times of day. For example, if nobody was using the water in the middle of the night and you see usage that isn't attributable to maybe your ice-maker or a flushing toilet it might mean something strange is going on.\"), mdx(\"span\", {\n    \"className\": \"gatsby-resp-image-wrapper\",\n    \"style\": {\n      \"position\": \"relative\",\n      \"display\": \"block\",\n      \"marginLeft\": \"auto\",\n      \"marginRight\": \"auto\",\n      \"maxWidth\": \"500px\"\n    }\n  }, \"\\n      \", mdx(\"span\", {\n    parentName: \"span\",\n    \"className\": \"gatsby-resp-image-background-image\",\n    \"style\": {\n      \"paddingBottom\": \"63.41463414634146%\",\n      \"position\": \"relative\",\n      \"bottom\": \"0\",\n      \"left\": \"0\",\n      \"backgroundImage\": \"url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAANCAYAAACpUE5eAAAACXBIWXMAAA7DAAAOwwHHb6hkAAABi0lEQVQ4y52TX0vDMBTF9/1f9iHER0VfVARf1AcF0T2IOBUF699u3dY0TdOk6fHkbowNncoCl0tOm19uck86uTJQukJZuXmYVWE9qlJDD15giwm0qSWinhcVw6JjTIXgPZZH+0v8PLxvYGuHTpyktcfDWEFPxqykmiLbdnWE8E2LwxEqwNu8xFlKmDGyy5/AFeGcnwLvVImrcYGLUQHNXdYBSoVuVmGfwMtM4TSdwKwLlApnwAd2+nKkcExgUloBhnWBPYI2nj5wk2vsv2XYSlIBNv8EhfmRZ8Dt5xTd2wR9NuaAwJ1kMAWG3+9ryWTzphC4+zJAt59Ip/ekwsHSkcV7C5Cox3vOrENGR3xWtWjzphy9DbH5+I5z2ubwdYjd50/ktkbTNOK3nB4tCVC0xD2v5eRjJP9e86p6WS52S43FiOAm+tBzkefiiq+lZrYUY27bQCiflNaYKIVhrlCwotJN/2tCAyc5SBYtAi0riGaumRcjaoa7as23XmgURUndwS18j3M7i6hpVvoFV030s1DRHIQAAAAASUVORK5CYII=')\",\n      \"backgroundSize\": \"cover\",\n      \"display\": \"block\"\n    }\n  }), \"\\n  \", mdx(\"img\", {\n    parentName: \"span\",\n    \"className\": \"gatsby-resp-image-image\",\n    \"alt\": \"Flume water use graph\",\n    \"title\": \"Flume water use graph\",\n    \"src\": \"/static/a5351c17ae59b587e9d22dc3f806f3fa/c6e3d/flume-chart.png\",\n    \"srcSet\": [\"/static/a5351c17ae59b587e9d22dc3f806f3fa/ad4a5/flume-chart.png 205w\", \"/static/a5351c17ae59b587e9d22dc3f806f3fa/74ab4/flume-chart.png 410w\", \"/static/a5351c17ae59b587e9d22dc3f806f3fa/c6e3d/flume-chart.png 500w\"],\n    \"sizes\": \"(max-width: 500px) 100vw, 500px\",\n    \"style\": {\n      \"width\": \"100%\",\n      \"height\": \"100%\",\n      \"margin\": \"0\",\n      \"verticalAlign\": \"middle\",\n      \"position\": \"absolute\",\n      \"top\": \"0\",\n      \"left\": \"0\"\n    },\n    \"loading\": \"lazy\"\n  }), \"\\n    \"), mdx(\"h2\", {\n    \"id\": \"cost-savings\"\n  }, \"Cost savings!\"), mdx(\"p\", null, \"When I was going over my policy with my agent earlier in the year they briefly mentioned something about credits being available for smart water monitoring devices. After I installed the Flume, I reached out to my insurance company, and they applied a credit to my policy that ended up paying for the cost of the Flume \\uD83D\\uDCA7\\uD83E\\uDD11!\"), mdx(\"p\", null, \"Beyond that, you realize how much water certain activities use which let's you be more aware which helps to conserve. For example, hand watering the lawn exceeded my household water usage on some days which I had no idea beforehand.\"), mdx(\"h2\", {\n    \"id\": \"final-thoughts\"\n  }, \"Final thoughts\"), mdx(\"p\", null, \"I've been really impressed with the Flume. It has been really accurate and reliable. It even notifies me when it loses connection to the internet--which has only happened when my ISP is out. Flume also provides access to your data through an API though I haven't tested that yet, but is important to many.\"), mdx(\"p\", null, \"Currently there is a referral promotion for -$20 off the price if you \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://flumewater.com?grsf=3vjucj\"\n  }, \"purchase through this link\"), \" or you can see if there is a better sale on \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://amzn.to/2TEiOKX\"\n  }, \"Flume 2 on Amazon\"), \".\"));\n}\n;\nMDXContent.isMDXComponent = true;","excerpt":"Why I added a smart water meter to my smart home Last year I discovered, the hard way, an intermittent leak in a pipe run under the…","fields":{"slug":"/flume2-wifi-leak-meter-review/","type":"posts"},"headings":[{"value":"Why I added a smart water meter to my smart home","depth":2},{"value":"Top 3 Best Smart Water Leak Detectors in 2021","depth":2},{"value":"Flume 2 Installation","depth":2},{"value":"Installation Steps","depth":3},{"value":"How does Flume work? Does it work well?","depth":2},{"value":"Leak detection","depth":3},{"value":"Cost savings!","depth":2},{"value":"Final thoughts","depth":2}],"frontmatter":{"title":"Testing the Flume 2 for wifi water monitoring and leak detection","description":"The Flume 2 provides wifi whole-house water monitoring without requiring cutting plumbing or external power allowing you to see check water usage and get notified of leaks in near real time. Learn about how to install the Flume and my impressions after 3 months.","link":null,"date":"2021-07-06T00:00:00.000Z","tags":["water","wifi","leak detection","smart home","iot","post"],"draft":null,"hide":null,"cover":{"childImageSharp":{"fluid":{"base64":"data:image/jpeg;base64,/9j/2wBDAAMCAgMCAgMDAwMEAwMEBQgFBQQEBQoHBwYIDAoMDAsKCwsNDhIQDQ4RDgsLEBYQERMUFRUVDA8XGBYUGBIUFRT/2wBDAQMEBAUEBQkFBQkUDQsNFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBT/wgARCAAOABQDASIAAhEBAxEB/8QAGAAAAwEBAAAAAAAAAAAAAAAAAAUGAwf/xAAWAQEBAQAAAAAAAAAAAAAAAAACAQP/2gAMAwEAAhADEAAAAeaXK3HFTIrHP//EABsQAAIDAAMAAAAAAAAAAAAAAAMFAgQGARIU/9oACAEBAAEFAs+j5MtGtrxG5BVlfy+w6CuN/eO0wEcf/8QAGxEAAgEFAAAAAAAAAAAAAAAAAAIDARESEyH/2gAIAQMBAT8B0yM1cZOFj//EABgRAAMBAQAAAAAAAAAAAAAAAAABAiER/9oACAECAQE/AXUpbOnT/8QAJBAAAgEDAwMFAAAAAAAAAAAAAQIDAAQSERMUITJhQlGRodH/2gAIAQEABj8C5EltiFcrk6DT7rIC1PgCMjT5pyvHUeyqn7SWhiI2x3L6vNTug4rIxQiEABqtt23jaRIghbEddCa//8QAHRABAAIDAAMBAAAAAAAAAAAAAQARITFBUXGBof/aAAgBAQABPyE+ZNtnbXTxLxEXJkLwVyQymtYyC1WdZVfVcy5UHk9ln4xVVps4hv1R8n//2gAMAwEAAgADAAAAEFcv/8QAGREBAAIDAAAAAAAAAAAAAAAAAQARITHx/9oACAEDAQE/EBowSwrkw2z/xAAaEQACAgMAAAAAAAAAAAAAAAAAAREhMVHw/9oACAECAQE/EEqF7dIq4P/EABsQAQEBAAMBAQAAAAAAAAAAAAERIQAxQVGh/9oACAEBAAE/EIE1HQAPkC1TzhlWAEEVcvYzrPd4QQEV8YNqgx8R0RSPNlJVqqsNUfzgpi0QPewzQii/eJ7S2I0ws3OwHhz/2Q==","aspectRatio":1.4222222222222223,"src":"/static/b86e864ecbfcfafc247fe378b55770b1/60a5f/flume2-review.jpg","srcSet":"/static/b86e864ecbfcfafc247fe378b55770b1/3e5eb/flume2-review.jpg 192w,\n/static/b86e864ecbfcfafc247fe378b55770b1/2880b/flume2-review.jpg 384w,\n/static/b86e864ecbfcfafc247fe378b55770b1/60a5f/flume2-review.jpg 768w","srcWebp":"/static/b86e864ecbfcfafc247fe378b55770b1/25278/flume2-review.webp","srcSetWebp":"/static/b86e864ecbfcfafc247fe378b55770b1/a278a/flume2-review.webp 192w,\n/static/b86e864ecbfcfafc247fe378b55770b1/2474b/flume2-review.webp 384w,\n/static/b86e864ecbfcfafc247fe378b55770b1/25278/flume2-review.webp 768w","sizes":"(max-width: 768px) 100vw, 768px","presentationWidth":768,"presentationHeight":541}}}}},{"id":"d37eb3db-1d02-5cf2-88e9-07e70af4e10d","body":"var _excluded = [\"components\"];\nfunction _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }\nfunction _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }\nfunction _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }\n/* @jsxRuntime classic */\n/* @jsx mdx */\n\nvar _frontmatter = {\n  \"title\": \"Nightwatch now supports TypeScript!\",\n  \"cover\": \"./nightwatch-typescript.jpg\",\n  \"date\": \"2021-04-07T00:00:00.000Z\",\n  \"link\": \"https://twitter.com/nightwatchjs/status/1375468959244378115?s=20\",\n  \"tags\": [\"link\", \"software testing\", \"nightwatch\", \"typescript\"]\n};\nvar layoutProps = {\n  _frontmatter: _frontmatter\n};\nvar MDXLayout = \"wrapper\";\nreturn function MDXContent(_ref) {\n  var components = _ref.components,\n    props = _objectWithoutProperties(_ref, _excluded);\n  return mdx(MDXLayout, _extends({}, layoutProps, props, {\n    components: components,\n    mdxType: \"MDXLayout\"\n  }), mdx(\"p\", null, \"Nightwatch.js recently released v1.6 which adds support TypeScript for writing your tests! The type definitions appear to be in the \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/nightwatch\"\n  }, \"DefinitelyTyped\"), \" project. Click here for my experience \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"./using-nightwatch-with-typescript/\"\n  }, \"using TypeScript with Nightwatch\"), \". I know a lot of test engineers were hoping to see this feature added \\uD83E\\uDD89\\uD83D\\uDC4D\\uD83D\\uDC4D\\uD83D\\uDC4D\"));\n}\n;\nMDXContent.isMDXComponent = true;","excerpt":"Nightwatch.js recently released v1.6 which adds support TypeScript for writing your tests! The type definitions appear to be in the…","fields":{"slug":"/links/nightwatch-typescript-release/","type":"links"},"headings":[],"frontmatter":{"title":"Nightwatch now supports TypeScript!","description":null,"link":"https://twitter.com/nightwatchjs/status/1375468959244378115?s=20","date":"2021-04-07T00:00:00.000Z","tags":["link","software testing","nightwatch","typescript"],"draft":null,"hide":null,"cover":{"childImageSharp":{"fluid":{"base64":"data:image/jpeg;base64,/9j/2wBDAAMCAgMCAgMDAwMEAwMEBQgFBQQEBQoHBwYIDAoMDAsKCwsNDhIQDQ4RDgsLEBYQERMUFRUVDA8XGBYUGBIUFRT/2wBDAQMEBAUEBQkFBQkUDQsNFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBT/wgARCAAKABQDASIAAhEBAxEB/8QAFwABAQEBAAAAAAAAAAAAAAAAAAQFB//EABUBAQEAAAAAAAAAAAAAAAAAAAIB/9oADAMBAAIQAxAAAAHjU0AOlmFP/8QAGxAAAgIDAQAAAAAAAAAAAAAAAgQBAwAFERL/2gAIAQEAAQUCZTTDWKtWIyy3YwYnPCKfOf/EABgRAQEBAQEAAAAAAAAAAAAAAAECABEx/9oACAEDAQE/AUmjiYmTw3//xAAYEQEBAQEBAAAAAAAAAAAAAAABAgARIf/aAAgBAgEBPwGWpey5un1d/8QAIhAAAQMDAwUAAAAAAAAAAAAAAgABAwQREhMhMRAiMlGx/9oACAEBAAY/AqOSOpyq5HLWiv4ekWmcW/OYsX1ZG4XtbtFmXLp9+n//xAAcEAEAAgEFAAAAAAAAAAAAAAABABFxEDFBsfD/2gAIAQEAAT8hD+YQUTHmFHuBSD0RsbWCUXdGApM6f//aAAwDAQACAAMAAAAQ9M//xAAWEQEBAQAAAAAAAAAAAAAAAAABABH/2gAIAQMBAT8QySSEwAv/xAAXEQEBAQEAAAAAAAAAAAAAAAABABFh/9oACAECAQE/ENNR5J6Fv//EABoQAQEBAQEBAQAAAAAAAAAAAAERIQAxEEH/2gAIAQEAAT8QZMW43rKJT9fGzmwVUCKEwQ6J4hgCpgF133zimSCG+jxjip6fP//Z","aspectRatio":1.9591836734693877,"src":"/static/d1d1aae41c48bfd7b7d1583cba92cdc2/7c84f/nightwatch-typescript.jpg","srcSet":"/static/d1d1aae41c48bfd7b7d1583cba92cdc2/3e5eb/nightwatch-typescript.jpg 192w,\n/static/d1d1aae41c48bfd7b7d1583cba92cdc2/2880b/nightwatch-typescript.jpg 384w,\n/static/d1d1aae41c48bfd7b7d1583cba92cdc2/7c84f/nightwatch-typescript.jpg 751w","srcWebp":"/static/d1d1aae41c48bfd7b7d1583cba92cdc2/efd58/nightwatch-typescript.webp","srcSetWebp":"/static/d1d1aae41c48bfd7b7d1583cba92cdc2/a278a/nightwatch-typescript.webp 192w,\n/static/d1d1aae41c48bfd7b7d1583cba92cdc2/2474b/nightwatch-typescript.webp 384w,\n/static/d1d1aae41c48bfd7b7d1583cba92cdc2/efd58/nightwatch-typescript.webp 751w","sizes":"(max-width: 751px) 100vw, 751px","presentationWidth":751,"presentationHeight":382}}}}},{"id":"e6cc274b-197a-51be-9c3b-b629c170c49d","body":"var _excluded = [\"components\"];\nfunction _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }\nfunction _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }\nfunction _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }\n/* @jsxRuntime classic */\n/* @jsx mdx */\n\nvar _frontmatter = {\n  \"title\": \"Best Variable Pump Speed for Pool Energy Savings\",\n  \"cover\": \"./pentair-superflo-pump-speed.jpg\",\n  \"date\": \"2021-03-29T00:00:00.000Z\",\n  \"description\": \"Learn how to calculate the best speed to run your variable speed pool pump at to efficiently keep your smart home's pool clean while saving energy and money by determining your gpm flow rate by calculating the total dynamic head without a flow meter.\",\n  \"tags\": [\"pool\", \"pentair\", \"smart home\", \"iot\", \"post'\"]\n};\nvar makeShortcode = function makeShortcode(name) {\n  return function MDXDefaultShortcode(props) {\n    console.warn(\"Component \" + name + \" was not imported, exported, or provided by MDXProvider as global scope\");\n    return mdx(\"div\", props);\n  };\n};\nvar Embed = makeShortcode(\"Embed\");\nvar layoutProps = {\n  _frontmatter: _frontmatter\n};\nvar MDXLayout = \"wrapper\";\nreturn function MDXContent(_ref) {\n  var components = _ref.components,\n    props = _objectWithoutProperties(_ref, _excluded);\n  return mdx(MDXLayout, _extends({}, layoutProps, props, {\n    components: components,\n    mdxType: \"MDXLayout\"\n  }), mdx(\"h2\", {\n    \"id\": \"calculating-your-optimum-variable-pool-pump-speed\"\n  }, \"Calculating your Optimum Variable Pool Pump Speed\"), mdx(\"p\", null, \"New smart pool pumps come with variable speed motors that you allow you to optimize your flow rate through speed control in order to keep your pool clean, run water features, and/or pump water to solar collectors on your roof while using less electricity to do so compared to a pool pump with a single speed motor.\"), mdx(\"p\", null, \"You can do this by adjusting the pump speed, through RPM settings, which increases the flow, measured in gallons per minute or GPM.\"), mdx(\"p\", null, \"Some pool pumps have built in flow meters that will tell you your gpm flow rate, but most only provide performance charts that let you determine your gpm at specific RPMs. However, to use these charts you need to know your Total Dynamic Head (TDH).\"), mdx(\"p\", null, \"This article will help you calculate your total dynamic head allowing you to determine your gpm flow rate at various RPM speeds letting you run your smart variable speed pool pump at the slowest, most energy efficient, settings to save money and run your pool pump quietly.\"), mdx(\"h2\", {\n    \"id\": \"calculating-total-dynamic-head-with-pressure-and-vacuum-gauges\"\n  }, \"Calculating Total Dynamic Head with Pressure and Vacuum Gauges\"), mdx(\"p\", null, \"You can calculate the total dynamic head (TDH) in your pool plumbing by placing a \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://amzn.to/31fVhjC\"\n  }, \"vacuum gauge\"), \" on the suction side of the pool pump and a \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://amzn.to/39apt4i\"\n  }, \"pressure gauge\"), \" at the pressure side. If your pool filter is clean and has a pressure gauge you can use that as well.\"), mdx(\"p\", null, \"The below video goes through installing the \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://amzn.to/31fVhjC\"\n  }, \"vacuum gauge\"), \" on the suction side of a \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://amzn.to/3lL2Rwr\"\n  }, \"Pentair SuperFlo pump\"), \" and explains the calculations in the remainder of this article.\"), mdx(Embed, {\n    src: \"https://www.youtube.com/embed/io9pSRM92FQ\",\n    mdxType: \"Embed\"\n  }), mdx(\"p\", null, \"After the gauges are installed, run your pool pump at the various RPM speeds that match the performance curves in your pool pump manual and record the vacuum gauge reading in Hg and the pressure gauge reading in psi.\"), mdx(\"p\", null, \"To calculate the TDH, total dynamic head, multiply the Hg reading by 1.13, the psi reading by 2.31 and add the two for the TDH.\"), mdx(\"h3\", {\n    \"id\": \"tdh-formula\"\n  }, \"TDH formula\"), mdx(\"ol\", null, mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Multiply Hg reading by 1.13\"), mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Multiply Psi reading by 2.31\"), mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Add products together for TDH\")), mdx(\"h2\", {\n    \"id\": \"using-total-dynamic-head-to-find-gpm-flow-rate\"\n  }, \"Using Total Dynamic Head to find GPM flow rate\"), mdx(\"p\", null, \"In your pool pump manual there should be a performance chart like this one from the Pentair SuperFlo below. Using the total dynamic head value you calculated paired with your RPM speed you can use this chart to determine the GPM flow rate.\"), mdx(\"span\", {\n    \"className\": \"gatsby-resp-image-wrapper\",\n    \"style\": {\n      \"position\": \"relative\",\n      \"display\": \"block\",\n      \"marginLeft\": \"auto\",\n      \"marginRight\": \"auto\",\n      \"maxWidth\": \"600px\"\n    }\n  }, \"\\n      \", mdx(\"span\", {\n    parentName: \"span\",\n    \"className\": \"gatsby-resp-image-background-image\",\n    \"style\": {\n      \"paddingBottom\": \"70.73170731707317%\",\n      \"position\": \"relative\",\n      \"bottom\": \"0\",\n      \"left\": \"0\",\n      \"backgroundImage\": \"url('data:image/jpeg;base64,/9j/2wBDAAMCAgMCAgMDAwMEAwMEBQgFBQQEBQoHBwYIDAoMDAsKCwsNDhIQDQ4RDgsLEBYQERMUFRUVDA8XGBYUGBIUFRT/2wBDAQMEBAUEBQkFBQkUDQsNFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBT/wgARCAAOABQDASIAAhEBAxEB/8QAGQAAAgMBAAAAAAAAAAAAAAAAAAIDBAUI/8QAFgEBAQEAAAAAAAAAAAAAAAAAAwEC/9oADAMBAAIQAxAAAAHoh4qbFqiFx//EABoQAQEAAgMAAAAAAAAAAAAAAAECAAMEEhP/2gAIAQEAAQUCNi4V6RMgL0NfIIjP/8QAFhEBAQEAAAAAAAAAAAAAAAAAABEh/9oACAEDAQE/Abiv/8QAFREBAQAAAAAAAAAAAAAAAAAAABH/2gAIAQIBAT8BiP/EAB0QAAEDBQEAAAAAAAAAAAAAAAABAiEQETJBcVH/2gAIAQEABj8CwcvDcnvS+kEbNP/EAB0QAQACAgIDAAAAAAAAAAAAAAEAESExQWFxgbH/2gAIAQEAAT8hyll5RX2GaUB4ZQj2UuqG6qjxFWNdzM//2gAMAwEAAgADAAAAEFTf/8QAFxEAAwEAAAAAAAAAAAAAAAAAABEhQf/aAAgBAwEBPxBMUU//xAAXEQEAAwAAAAAAAAAAAAAAAAABEBEh/9oACAECAQE/ELOjD//EABwQAQADAAMBAQAAAAAAAAAAAAEAESExQVFx4f/aAAgBAQABPxBvWuMWvAibzeBx1H06m6Dd6GBljgVob5/Ymrus1anG77hS/vs//9k=')\",\n      \"backgroundSize\": \"cover\",\n      \"display\": \"block\"\n    }\n  }), \"\\n  \", mdx(\"img\", {\n    parentName: \"span\",\n    \"className\": \"gatsby-resp-image-image\",\n    \"alt\": \"Pentair SuperFlo Performance Graph\",\n    \"title\": \"Pentair SuperFlo Performance Graph\",\n    \"src\": \"/static/ed8e30d9e31fc4ac77b8675cc956b52e/2a8be/pentair-flow-rate-chart.jpg\",\n    \"srcSet\": [\"/static/ed8e30d9e31fc4ac77b8675cc956b52e/bd2b6/pentair-flow-rate-chart.jpg 205w\", \"/static/ed8e30d9e31fc4ac77b8675cc956b52e/ceeba/pentair-flow-rate-chart.jpg 410w\", \"/static/ed8e30d9e31fc4ac77b8675cc956b52e/2a8be/pentair-flow-rate-chart.jpg 600w\"],\n    \"sizes\": \"(max-width: 600px) 100vw, 600px\",\n    \"style\": {\n      \"width\": \"100%\",\n      \"height\": \"100%\",\n      \"margin\": \"0\",\n      \"verticalAlign\": \"middle\",\n      \"position\": \"absolute\",\n      \"top\": \"0\",\n      \"left\": \"0\"\n    },\n    \"loading\": \"lazy\"\n  }), \"\\n    \"), mdx(\"p\", null, \"For example, let's pretend our TDH calculation came out to 55 when we were at 3000rpm. Start on the left axis of this chart labeled total dynamic head, draw a line to where it intersects the curve for 3000rpm, and then draw a line down to the bottom GPM axis.\"), mdx(\"p\", null, \"If you follow the red line in the chart above for our example you'd see our GPM is 45. At 3,000 RPM our pump would move 45 gallons of minute per water under that amount of dynamic head.\"), mdx(\"p\", null, mdx(\"em\", {\n    parentName: \"p\"\n  }, \"The TDH values change at different speeds so be sure to rerun the calculations for the different RPM curves to determine GPM at higher and lower speeds.\")), mdx(\"h2\", {\n    \"id\": \"using-gpm-to-optimize-runtime-and-speed\"\n  }, \"Using GPM to Optimize Runtime and Speed\"), mdx(\"p\", null, \"A variable speed pump will use the least amount of electricity at low speed, even if you need to run it longer, to filter the same volume of water in your pool than running it faster for less time.\"), mdx(\"p\", null, \"Some people run theirs for 24 hours a day at an extremely low speed. It saves money and keeping the water circulating keeps it more sanitary than if it were stagnant.\"), mdx(\"p\", null, \"However, there are several factors to consider when determining your run time.\"), mdx(\"ol\", null, mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Total gallons of water in your pool\"), mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"GPM requirements of pool features\"), mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"GPM requirements of suction-side pool cleaners\"), mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Solar collector flow rates\")), mdx(\"p\", null, \"A simple calculation without accounting for 2 through 4 would have you divide your pool volume in gallons by your GPM to determine how long you should run your pool for. \", mdx(\"em\", {\n    parentName: \"p\"\n  }, \"In most residential settings a 1x turnover of the pool volume is recommended\")), mdx(\"h3\", {\n    \"id\": \"example-for-a-20000-gallon-pool-w-50-gpm-flow-rate\"\n  }, \"Example for a 20,000 gallon pool w/ 50 GPM flow rate\"), mdx(\"ol\", null, mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"50gpm \", \"*\", \" 60min = 3000gph\"), mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"20,000 gallons / 3000gph = 6.67 hours of runtime needed\")), mdx(\"h3\", {\n    \"id\": \"considerations-for-solar\"\n  }, \"Considerations for Solar\"), mdx(\"p\", null, \"In my case, I have a \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://youtu.be/hHab_E1eD7w\"\n  }, \"suction side pool cleaner\"), \" and solar heat collectors on my roof. When my solar controller goes on there is more pressure so my TDH changes. In addition, the solar heater works best at a 32gpm flow rate.\"), mdx(\"p\", null, \"This means I have to run at a higher RPM to make up for it when heating the pool.\"), mdx(\"p\", null, \"My Pentair SuperFlo VS has 3 different speed programs it can automate per day so I paired the runtime and RPM to run at the slowest speeds that will filter through the total volume of water in my pool per day (19,000 gallon pool) during daylight hours while staying at flow rates good for solar and my pool cleaner.\"), mdx(\"ol\", null, mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"1900rpm @ 350 watts / 31gpm / 5 hours\"), mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"2600rpm @ 775 watts / 47gpm / 2 hours\"), mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"2200rpm @ 500 watts / 37gpm / 2 hours\")), mdx(\"p\", null, \"That gives me a 9 hour runtime and filters 19,380 gallons.\"), mdx(\"p\", null, \"I posted \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://drive.google.com/file/d/1TGU2pmePiY4UmnULn8OzMFYF9SJp-cTA/view?usp=sharing\"\n  }, \"my Pentair SuperFlo VS flow rate calculation spreadsheet\"), \" on drive if you want to use it for your own project.\"), mdx(\"p\", null, \"*\", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://floridasolardesigngroup.com/can-you-run-water-through-solar-pool-heating-panels-too-fast-causing-it-not-to-heat-long-enough/\"\n  }, \"Extra reading, \\\"Can you run water through solar pool heating panels too fast?\")), mdx(\"h2\", {\n    \"id\": \"final-thoughts\"\n  }, \"Final Thoughts\"), mdx(\"p\", null, \"I found this method very helpful for calculating my pool pump's flow rate. Alternative calculations either require a dedicated flow meter, that I didn't have the plumbing space to add, or error prone estimates based on length of pipe, diameters, and height.\"), mdx(\"p\", null, \"I hope you also found this useful if you were in a similar situation and trying to figure out how to best take advantage of your variable speed pump controls.\"), mdx(\"p\", null, mdx(\"em\", {\n    parentName: \"p\"\n  }, \"As an Amazon Associate I earn from qualifying purchases made through links on this site (which helps support it, thank you!)\")));\n}\n;\nMDXContent.isMDXComponent = true;","excerpt":"Calculating your Optimum Variable Pool Pump Speed New smart pool pumps come with variable speed motors that you allow you to optimize your…","fields":{"slug":"/calculate-gph-flow-rate-pool-pump-savings/","type":"posts"},"headings":[{"value":"Calculating your Optimum Variable Pool Pump Speed","depth":2},{"value":"Calculating Total Dynamic Head with Pressure and Vacuum Gauges","depth":2},{"value":"TDH formula","depth":3},{"value":"Using Total Dynamic Head to find GPM flow rate","depth":2},{"value":"Using GPM to Optimize Runtime and Speed","depth":2},{"value":"Example for a 20,000 gallon pool w/ 50 GPM flow rate","depth":3},{"value":"Considerations for Solar","depth":3},{"value":"Final Thoughts","depth":2}],"frontmatter":{"title":"Best Variable Pump Speed for Pool Energy Savings","description":"Learn how to calculate the best speed to run your variable speed pool pump at to efficiently keep your smart home's pool clean while saving energy and money by determining your gpm flow rate by calculating the total dynamic head without a flow meter.","link":null,"date":"2021-03-29T00:00:00.000Z","tags":["pool","pentair","smart home","iot","post'"],"draft":null,"hide":null,"cover":{"childImageSharp":{"fluid":{"base64":"data:image/jpeg;base64,/9j/2wBDAAMCAgMCAgMDAwMEAwMEBQgFBQQEBQoHBwYIDAoMDAsKCwsNDhIQDQ4RDgsLEBYQERMUFRUVDA8XGBYUGBIUFRT/2wBDAQMEBAUEBQkFBQkUDQsNFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBT/wgARCAAKABQDASIAAhEBAxEB/8QAGQAAAgMBAAAAAAAAAAAAAAAAAAQBAgUH/8QAFgEBAQEAAAAAAAAAAAAAAAAABQME/9oADAMBAAIQAxAAAAF9vMvQeTpplf8A/8QAGRAAAwEBAQAAAAAAAAAAAAAAAgMEAQUR/9oACAEBAAEFAoa6FMQIpqazpY7nlvhmSmomTXP/AP/EABkRAAIDAQAAAAAAAAAAAAAAAAABAgMEEv/aAAgBAwEBPwFb7FI7Z//EABgRAAMBAQAAAAAAAAAAAAAAAAABAhMR/9oACAECAQE/AcJa4Yyf/8QAIxAAAgEDAwQDAAAAAAAAAAAAAQIDABEhBBNBEhQiQjJh0f/aAAgBAQAGPwJoeodhLluSn7UzxKr6l/N9r2HJtUm3Dp9THfDboQj6IOb1Fk1dGKG/riopJ4kmkK/ORbmv/8QAHRAAAgIDAAMAAAAAAAAAAAAAAREAITFBUWFxgf/aAAgBAQABPyFW4CAHFladcFWYkbN0EWsncbmDK4dMAHqFuNXfEAqQssKlr3gB/pn/2gAMAwEAAgADAAAAENMv/8QAGREAAgMBAAAAAAAAAAAAAAAAAAERIdEx/9oACAEDAQE/EF2lE5omrp//xAAXEQEBAQEAAAAAAAAAAAAAAAABABFB/9oACAECAQE/EFZdkXMv/8QAGhABAAMBAQEAAAAAAAAAAAAAAQARITFBUf/aAAgBAQABPxAz1/TirHRDRqrNI7tHVYKEAMbDi7YzMNuMZqmiO5ZNkAjacrk7mJ1fCzyEeGF6KAmrRnZ//9k=","aspectRatio":2,"src":"/static/35862faad26406c076953869b10fbc9a/60a5f/pentair-superflo-pump-speed.jpg","srcSet":"/static/35862faad26406c076953869b10fbc9a/3e5eb/pentair-superflo-pump-speed.jpg 192w,\n/static/35862faad26406c076953869b10fbc9a/2880b/pentair-superflo-pump-speed.jpg 384w,\n/static/35862faad26406c076953869b10fbc9a/60a5f/pentair-superflo-pump-speed.jpg 768w","srcWebp":"/static/35862faad26406c076953869b10fbc9a/25278/pentair-superflo-pump-speed.webp","srcSetWebp":"/static/35862faad26406c076953869b10fbc9a/a278a/pentair-superflo-pump-speed.webp 192w,\n/static/35862faad26406c076953869b10fbc9a/2474b/pentair-superflo-pump-speed.webp 384w,\n/static/35862faad26406c076953869b10fbc9a/25278/pentair-superflo-pump-speed.webp 768w","sizes":"(max-width: 768px) 100vw, 768px","presentationWidth":768,"presentationHeight":382}}}}},{"id":"a48f549b-4852-5f26-8d38-3491e9207231","body":"var _excluded = [\"components\"];\nfunction _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }\nfunction _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }\nfunction _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }\n/* @jsxRuntime classic */\n/* @jsx mdx */\n\nvar _frontmatter = {\n  \"title\": \"Review and setup of Amcrest's AWC897 4K webcam\",\n  \"cover\": \"./amcrest-4k-awc897-webcam.jpg\",\n  \"date\": \"2021-02-17T00:00:00.000Z\",\n  \"description\": \"Amcrest has been releasing 1080p and now 4k webcams at budget-friendly prices throughout 2020 and 2021. This review will setup their latest 4k webcam and test the microphone audio, resolution, and color balance to see if they've improved over their previous webcam models.\",\n  \"tags\": [\"smart home\", \"camera\", \"amcrest\", \"iot\", \"post\"]\n};\nvar makeShortcode = function makeShortcode(name) {\n  return function MDXDefaultShortcode(props) {\n    console.warn(\"Component \" + name + \" was not imported, exported, or provided by MDXProvider as global scope\");\n    return mdx(\"div\", props);\n  };\n};\nvar Embed = makeShortcode(\"Embed\");\nvar layoutProps = {\n  _frontmatter: _frontmatter\n};\nvar MDXLayout = \"wrapper\";\nreturn function MDXContent(_ref) {\n  var components = _ref.components,\n    props = _objectWithoutProperties(_ref, _excluded);\n  return mdx(MDXLayout, _extends({}, layoutProps, props, {\n    components: components,\n    mdxType: \"MDXLayout\"\n  }), mdx(\"h2\", {\n    \"id\": \"new-features-in-the-amcrest-4k-awc897-webcam\"\n  }, \"New features in the Amcrest 4k AWC897 webcam\"), mdx(\"p\", null, \"Amcrest has released 7 different webcam models as of writing. I've \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"../tags/camera\"\n  }, \"reviewed\"), \" 4 of them and they all have something uniquely wrong with with either their microphone audio, blurry focus, fisheye distortion, or color balance.\"), mdx(\"p\", null, \"The specifications indicate Amcrest may have addressed these issues with this new \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://amzn.to/3s4BwY1\"\n  }, \"AWC897 UltraHD webcam\"), \" (affiliate link). It has an 8MP image sensor now and 75 degree field of view, which, when paired with the 1/3.2\\\" image sensor should eliminate the fish eye distortion.\"), mdx(\"p\", null, \"You video below goes over setup and reviews the webcam in greater detail.\"), mdx(Embed, {\n    src: \"https://www.youtube.com/embed/wHQytjyKT4E\",\n    mdxType: \"Embed\"\n  }), mdx(\"h2\", {\n    \"id\": \"amcrest-awc897-4k-ultrahd-webcam-tech-specs\"\n  }, \"Amcrest AWC897 4k UltraHD Webcam Tech Specs\"), mdx(\"ul\", null, mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"1/3.2\\\" 8MP CMOS image sensor\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"4k resolution (3840x2160) at 18fps\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"1440p (2560x1440) at 30fps\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"1080P (1920x1080) at 30fps\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"75 degree field of view\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Internal mic\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"USB 2.0 Plug and Play\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Physical privacy cover\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"5'9\\\" USB cord length (permanently attached)\")), mdx(\"p\", null, \"The CMOS sensor has much higher megapixel resolution than the AWC201-B and AWC195-B I've previously looked at which should lead to more detailed image, but the 1/3.2\\\" image sensor is smaller, about the same size as an iPhone 5.\"), mdx(\"p\", null, \"Though this camera is 4k it can only record or stream in that resolution at 18fps which is lower than the 24 or 30fps you'd need for smooth video. However, 30fps is supported at 1080p and 1440p which is what I'd be using anyway. Most of my video conferencing software is normally capped at 1080p or 720p so I hope to just get the highest quality when being used there.\"), mdx(\"p\", null, \"The upgraded image sensor should work well for Zoom meetings, Skype, Google Hangouts, and provide a clear image in normal room lighting.\"), mdx(\"p\", null, \"There should not be a fisheye effect in this camera. The image should be cropped to your head, shoulders, and immediate background at around 32\\\" away without distorting your face. There is no variable focus, it's fixed, so you need to position the camera about 30-34 inches away so you don't appear blurry.\"), mdx(\"p\", null, \"For the privacy conscious, there is a physical privacy cover included. \"), mdx(\"p\", null, \"Lastly, there is a 1/4\\\" tripod mount on the bottom of the webcam for tripod mounting if you don't want to use the clamp mount to perch it on your monitor or use it for other applications like 3D print monitoring with Octoprint/Octolapse.\"), mdx(\"h2\", {\n    \"id\": \"installation-and-setup\"\n  }, \"Installation and Setup\"), mdx(\"p\", null, \"This is a plug and play webcam so Windows 10 will detect and install drivers for the camera once you plug it into a free USB port on your computer. It will appear as \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"USB 2.0 Camera\"), \" under \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"Sound, video and game controllers\"), \" in your Device Manager.\"), mdx(\"p\", null, \"No additional software comes with the camera to adjust brightness or contrast. You can use the Windows 10 \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"Camera app\"), \", to adjust the brightness and resolution settings. Software like OBS (Open Broadcast Studio) let you manually adjust the resolution, brightness, contrast, and other image-related settings as well.\"), mdx(\"p\", null, \"For the most part though, the camera will automatically adjust and you just need to select the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"USB 2.0 Camera\"), \" as your video source when you are in your conferencing app (Zoom, Skype, MS Teams, Hangouts, etc.) to use it.\"), mdx(\"h2\", {\n    \"id\": \"video-and-audio-quality-and-performance\"\n  }, \"Video and Audio Quality and Performance\"), mdx(\"p\", null, \"In the \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://youtu.be/wHQytjyKT4E\"\n  }, \"Amcrest AWC897 webcam review\"), \" embedded at the top of the article you can see how improved the webcam is over the other models. The colors finally look normal. My skin tone is more natural and there is no more fisheye distortion with the lens they are using.\"), mdx(\"p\", null, \"The image is was sufficiently bright under normal room light. With supplemental light it was actually too bright, but for most home offices this should look really good. For best performance, with any camera, try to ensure you aren't backlit. For example, don't sit with a bright window behind you. You can usually improve things light unflattering shadows by adding a \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://amzn.to/3b6bPzg\"\n  }, \"front-facing light\"), \" (affiliate link) instead of relying on overhead lighting.\"), mdx(\"p\", null, \"Comparing the 1080p video against \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://www.davidmello.com/tags/camera\"\n  }, \"previous models\"), \", the AWC897 colors weren't washed out, but still wouldn't compare to something like an expensive DSLR or mirrorless camera's depth. The field of view was similar to my DSLR with a 24mm lens on.\"), mdx(\"p\", null, \"As improved as the video quality is--the audio is still awful. Amcrest has yet to pair their webcams with a decent microphone. Previous ones have sounded too quiet or muffled. This time they seem to have overcompensated by setting the gain and treble way too high making everything way too loud and blown out.\"), mdx(\"p\", null, \"You'd want to pair this with an \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://amzn.to/3alUM9O\"\n  }, \"external microphone\"), \" for professional sounding calls or meetings.\"), mdx(\"h2\", {\n    \"id\": \"final-thoughts\"\n  }, \"Final Thoughts\"), mdx(\"p\", null, \"The \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://amzn.to/3s4BwY1\"\n  }, \"AWC897 UltraHD webcam\"), \" is my favorite Amcrest webcam so far. They've improved on all my complaints on the older models in the areas of color, resolution, and focus. Unfortunately, they still can't get the microphone correct.\"), mdx(\"p\", null, \"Fortunately, the microphone issue can be worked around if you pair this with an inexpensive USB microphone like the \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://amzn.to/3rZLQ3q\"\n  }, \"Yeti Snowball\"), \" or \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://amzn.to/3b8eY1D\"\n  }, \"Samson USB Meteor mic\"), \".\"), mdx(\"p\", null, \"With the pairing of the webcam with an external USB mic you'd have a very good budget setup for remote work meetings or kids doing home schooling or remote learning.\"), mdx(\"p\", null, mdx(\"em\", {\n    parentName: \"p\"\n  }, \"As an Amazon Associate I earn from qualifying purchases made through links on this site (which helps support it, thank you!)\")));\n}\n;\nMDXContent.isMDXComponent = true;","excerpt":"New features in the Amcrest 4k AWC897 webcam Amcrest has released 7 different webcam models as of writing. I've  reviewed  4 of them and…","fields":{"slug":"/amcrest-4k-webcam-setup-review/","type":"posts"},"headings":[{"value":"New features in the Amcrest 4k AWC897 webcam","depth":2},{"value":"Amcrest AWC897 4k UltraHD Webcam Tech Specs","depth":2},{"value":"Installation and Setup","depth":2},{"value":"Video and Audio Quality and Performance","depth":2},{"value":"Final Thoughts","depth":2}],"frontmatter":{"title":"Review and setup of Amcrest's AWC897 4K webcam","description":"Amcrest has been releasing 1080p and now 4k webcams at budget-friendly prices throughout 2020 and 2021. This review will setup their latest 4k webcam and test the microphone audio, resolution, and color balance to see if they've improved over their previous webcam models.","link":null,"date":"2021-02-17T00:00:00.000Z","tags":["smart home","camera","amcrest","iot","post"],"draft":null,"hide":null,"cover":{"childImageSharp":{"fluid":{"base64":"data:image/jpeg;base64,/9j/2wBDAAMCAgMCAgMDAwMEAwMEBQgFBQQEBQoHBwYIDAoMDAsKCwsNDhIQDQ4RDgsLEBYQERMUFRUVDA8XGBYUGBIUFRT/2wBDAQMEBAUEBQkFBQkUDQsNFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBT/wgARCAALABQDASIAAhEBAxEB/8QAFwAAAwEAAAAAAAAAAAAAAAAAAAUHBv/EABYBAQEBAAAAAAAAAAAAAAAAAAMABP/aAAwDAQACEAMQAAABeZieL7RUSEgv/8QAGhAAAgMBAQAAAAAAAAAAAAAABAUBAwYCEv/aAAgBAQABBQIR+KvHZaN3JkCsOIyx1hDVofebfZomXr//xAAaEQEAAQUAAAAAAAAAAAAAAAABAAIDEyJS/9oACAEDAQE/AQ0a2ZrPM//EABkRAAMAAwAAAAAAAAAAAAAAAAABAhIhUf/aAAgBAgEBPwFbrEcX0//EACMQAAICAgECBwAAAAAAAAAAAAECAwQAIRESQRMiQlJxkaH/2gAIAQEABj8C1TsTHp58ijj9OR1FFas8npdACPsnALVZ7M3eSMpxkUciwunhk7gTsNdsV5mDsB7QM1bcfGs//8QAHBABAAICAwEAAAAAAAAAAAAAAQARIWExQVGh/9oACAEBAAE/IRNQoT0rmxlf5i1jsQbYJZ3RVnHcBeAHIZV7xRIM+jujMEwfyo+E/9oADAMBAAIAAwAAABALH//EABsRAAICAwEAAAAAAAAAAAAAAAERADGBkcHw/9oACAEDAQE/EGUIICyORFe7n//EABkRAQACAwAAAAAAAAAAAAAAAAEAITGR8P/aAAgBAgEBPxBpHNwKj3U//8QAHBABAQADAQADAAAAAAAAAAAAAREAITFBcYGh/9oACAEBAAE/EJdYcfYcR9YRlakxlko1G0hszWiZxqRxsGWHxlMU9J2EU2tr7cnnLNzvQQr61w7H5+cAz//Z","aspectRatio":1.7454545454545454,"src":"/static/d7f392ab5e01b4c8dbb7c243312cd8b6/60a5f/amcrest-4k-awc897-webcam.jpg","srcSet":"/static/d7f392ab5e01b4c8dbb7c243312cd8b6/3e5eb/amcrest-4k-awc897-webcam.jpg 192w,\n/static/d7f392ab5e01b4c8dbb7c243312cd8b6/2880b/amcrest-4k-awc897-webcam.jpg 384w,\n/static/d7f392ab5e01b4c8dbb7c243312cd8b6/60a5f/amcrest-4k-awc897-webcam.jpg 768w,\n/static/d7f392ab5e01b4c8dbb7c243312cd8b6/2f1b1/amcrest-4k-awc897-webcam.jpg 800w","srcWebp":"/static/d7f392ab5e01b4c8dbb7c243312cd8b6/25278/amcrest-4k-awc897-webcam.webp","srcSetWebp":"/static/d7f392ab5e01b4c8dbb7c243312cd8b6/a278a/amcrest-4k-awc897-webcam.webp 192w,\n/static/d7f392ab5e01b4c8dbb7c243312cd8b6/2474b/amcrest-4k-awc897-webcam.webp 384w,\n/static/d7f392ab5e01b4c8dbb7c243312cd8b6/25278/amcrest-4k-awc897-webcam.webp 768w,\n/static/d7f392ab5e01b4c8dbb7c243312cd8b6/ccdb5/amcrest-4k-awc897-webcam.webp 800w","sizes":"(max-width: 768px) 100vw, 768px","presentationWidth":768,"presentationHeight":440}}}}},{"id":"947ff704-f5b1-58a6-b788-7b5dce59a2fe","body":"var _excluded = [\"components\"];\nfunction _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }\nfunction _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }\nfunction _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }\n/* @jsxRuntime classic */\n/* @jsx mdx */\n\nvar _frontmatter = {\n  \"title\": \"Integrating TestRail with Nightwatch.js\",\n  \"cover\": \"./testrail-nightwatchjs-banner.jpg\",\n  \"date\": \"2021-02-07T00:00:00.000Z\",\n  \"description\": \"Test case management systems that aren't integrated with your automation framework are wasteful. Learn how to integrate Nightwatch automated test results into TestRail automatically in this tutorial with the help of testrail-nightwatch-updater.\",\n  \"tags\": [\"quality assurance\", \"software testing\", \"nightwatchjs\", \"testrail\", \"post'\"]\n};\nvar makeShortcode = function makeShortcode(name) {\n  return function MDXDefaultShortcode(props) {\n    console.warn(\"Component \" + name + \" was not imported, exported, or provided by MDXProvider as global scope\");\n    return mdx(\"div\", props);\n  };\n};\nvar Embed = makeShortcode(\"Embed\");\nvar layoutProps = {\n  _frontmatter: _frontmatter\n};\nvar MDXLayout = \"wrapper\";\nreturn function MDXContent(_ref) {\n  var components = _ref.components,\n    props = _objectWithoutProperties(_ref, _excluded);\n  return mdx(MDXLayout, _extends({}, layoutProps, props, {\n    components: components,\n    mdxType: \"MDXLayout\"\n  }), mdx(\"h2\", {\n    \"id\": \"making-test-case-management-systems-useful\"\n  }, \"Making Test Case Management Systems Useful\"), mdx(\"p\", null, \"If your tests suites are manual and scattered about in various spreadsheets, Google Docs, DropBox, notepad and the like then organizing them into a centralized Test Case Management System makes a lot of sense. Without the organization they afford you the tests tend to get lost to time and personnel changes. In addition, tracking test results and when they were run last is dicey. As a manager, reporting on quality is nerve-wracking.\"), mdx(\"p\", null, \"However, what if all your tests are automated into a continuous testing pipeline?\"), mdx(\"blockquote\", null, mdx(\"p\", {\n    parentName: \"blockquote\"\n  }, \"What benefit does a Test Case Management System offer for automated tests?\")), mdx(\"p\", null, \"To an automated test engineer, not a whole lot. Most continuous testing frameworks run the tests constantly and retain their pass/fail history already. Historically, the test case management systems that were implemented required a tester to manually update the test results in the TCMS after the automation ran which is a soul-crushing waste of time when your automation framework has it already. I've been critical of and fought, hard, against them based on prior bad experiences.\"), mdx(\"blockquote\", null, mdx(\"p\", {\n    parentName: \"blockquote\"\n  }, \"Most systems have a mix of both manual and automated tests, though.\")), mdx(\"p\", null, \"Rarely do most software companies have every single test automated. This means there will still be manual tests you need to keep track of and report on. The test case management system provides a central place to store the details of both the automated and manual tests along with their run data.\"), mdx(\"p\", null, \"Besides organization and centralized reporting, the Test Case Management System helps management present on test coverage. For non-technical users, it helps them see what a test is doing without looking in code. It also helps with auditing.\"), mdx(\"p\", null, \"Wouldn't it be great if we could get the utility of a Test Case Management System automatically without extra, especially manual, work for the test engineers?\"), mdx(\"p\", null, \"We recently did just that with TestRail and NightwatchJS on my latest test project.\"), mdx(Embed, {\n    src: \"https://www.youtube.com/embed/p01y9brwpBc\",\n    mdxType: \"Embed\"\n  }), mdx(\"h2\", {\n    \"id\": \"integrating-testrail-with-nightwatchjs\"\n  }, \"Integrating TestRail with Nightwatch.js\"), mdx(\"p\", null, \"My team and I were recently tasked with a project to write test automation to verify demo environment functionality prior to use. Previously, these tests were performed manually using a checklist in a Word document. It seemed like a good opportunity to move those tests into TestRail and then automate them using Nightwatch.\"), mdx(\"p\", null, \"However, if I automated the tests I didn't want to deal with manually updating the test case management system, TestRail. \"), mdx(\"blockquote\", null, mdx(\"p\", {\n    parentName: \"blockquote\"\n  }, \"Fortunately, TestRail provides an API that we leveraged to send the test results from Nightwatch back to the test run in TestRail.\")), mdx(\"p\", null, \"Using the combination of the two, our business users could see the test results, we didn't have to do any extra work (beyond the initial setup), and all the tests were centrally organized.\"), mdx(\"h3\", {\n    \"id\": \"how-to-update-testrail-with-nightwatch-test-results-automatically\"\n  }, \"How to Update TestRail with Nightwatch Test Results Automatically\"), mdx(\"p\", null, \"I've published an NPM package called \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://www.npmjs.com/package/nightwatch-testrail-updater\"\n  }, \"nightwatch-testrail-updater\"), \" that you can import into your Nightwatch test suite. It passes the results of the test case through the TestRail API when the test finishes executing.\"), mdx(\"p\", null, \"Outside of the overall pass status, the individual test steps are logged to the test case results and comments section as shown below.\"), mdx(\"span\", {\n    \"className\": \"gatsby-resp-image-wrapper\",\n    \"style\": {\n      \"position\": \"relative\",\n      \"display\": \"block\",\n      \"marginLeft\": \"auto\",\n      \"marginRight\": \"auto\",\n      \"maxWidth\": \"600px\"\n    }\n  }, \"\\n      \", mdx(\"span\", {\n    parentName: \"span\",\n    \"className\": \"gatsby-resp-image-background-image\",\n    \"style\": {\n      \"paddingBottom\": \"33.170731707317074%\",\n      \"position\": \"relative\",\n      \"bottom\": \"0\",\n      \"left\": \"0\",\n      \"backgroundImage\": \"url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAHCAIAAACHqfpvAAAACXBIWXMAAAsTAAALEwEAmpwYAAAA40lEQVQY001Q27KCMAzs/3+RD+cffOViaTmMQKCtOoNSQMQlcUZ3Mp1N2k02VYfj32N+3R7TMK33uPTOEXV977wPzgekPgQESkTU8wVAXXeua1U1RP5ak78M431atNbG2hMjyzKcpii0PllrNUPSPM+NMWrZtmn9BCajVJYlzoKB15CBoIsQ4UDbtgqeOdZx2cW4E6V02UcxqapKGokFPIgxKmg+8dzFcgFXaZrmDAyRYpIkUpR1mqb5iuNzG8ZZFpO/AfA3jgGCtGOAhBB227+ThzjDsv0B3JYMEPiXLVD/Z7wBqIh7Vr+/6OQAAAAASUVORK5CYII=')\",\n      \"backgroundSize\": \"cover\",\n      \"display\": \"block\"\n    }\n  }), \"\\n  \", mdx(\"img\", {\n    parentName: \"span\",\n    \"className\": \"gatsby-resp-image-image\",\n    \"alt\": \"Detailed TestRail test steps\",\n    \"title\": \"Detailed TestRail test steps\",\n    \"src\": \"/static/b23ec7945092ba9dc72daa0ad6675775/ff59c/testlog.png\",\n    \"srcSet\": [\"/static/b23ec7945092ba9dc72daa0ad6675775/ad4a5/testlog.png 205w\", \"/static/b23ec7945092ba9dc72daa0ad6675775/74ab4/testlog.png 410w\", \"/static/b23ec7945092ba9dc72daa0ad6675775/ff59c/testlog.png 600w\"],\n    \"sizes\": \"(max-width: 600px) 100vw, 600px\",\n    \"style\": {\n      \"width\": \"100%\",\n      \"height\": \"100%\",\n      \"margin\": \"0\",\n      \"verticalAlign\": \"middle\",\n      \"position\": \"absolute\",\n      \"top\": \"0\",\n      \"left\": \"0\"\n    },\n    \"loading\": \"lazy\"\n  }), \"\\n    \"), mdx(\"h3\", {\n    \"id\": \"installing-nightwatch-testrail-updater\"\n  }, \"Installing nightwatch-testrail-updater\"), mdx(\"p\", null, \"To add nightwatch-testrail-updater to your project, from a command line in your test suite, run\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-sh\"\n  }, \"npm install nightwatch-testrail-updater --save\\n\")), mdx(\"p\", null, \"After, in your Nightwatch configuration file, \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"nightwatch.json\"), \", add or append\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-json\"\n  }, \"\\\"custom_commands_path\\\": [\\\"./node_modules/nightwatch-testrail-updater/commands\\\"]\\n\")), mdx(\"p\", null, \"This will make \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"nightwatch-testrail-updater\"), \"'s updateTestRail() method available as a custom command in the Nightwatch browser object.\"), mdx(\"p\", null, \"Also in \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"nightwatch.json\"), \"'s \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"test_settings\"), \" section add a section called \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"testRail\"), \" with the host of your TestRail deployment and the runId of the TestRail suite containing your tests. The runId is the \\\"R\\\" number shown in the upper left of the test run in TestRail when you are viewing the test run details.\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-json\"\n  }, \"\\\"test_settings\\\": {\\n    \\\"default\\\": {\\n      \\\"globals\\\": {\\n            \\\"testRail\\\": {\\n                 \\\"host\\\": \\\"testrail.mycorp.com\\\",\\n                  \\\"runId\\\": \\\"12345\\\"\\n            }\\n        }\\n    }\\n}\\n\")), mdx(\"p\", null, \"The TestRail API will need your TestRail username and API Key. Create environment variables on the system named TESTRAIL_USERNAME and TESTRAIL_API_KEY to store your TestRail username and API key information in order to authenticate with the TestRail API (\", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://www.davidmello.com/how-to-use-nightwatch-with-saucelabs/\"\n  }, \"example setting environment variables\"), \".\"), mdx(\"h3\", {\n    \"id\": \"sending-nightwatch-test-results-to-testrail-using-nightwatch-testrail-updater\"\n  }, \"Sending Nightwatch Test Results to TestRail using nightwatch-testrail-updater\"), mdx(\"p\", null, \"In order to associate your automated tests to the entries in the TestRail test run you need to add the TestRail test ID in your test.\"), mdx(\"p\", null, \"So, in your test, add a line called browser.testId = \", mdx(\"em\", {\n    parentName: \"p\"\n  }, \"testIdHere\")), mdx(\"p\", null, \"Set the value equal to the numeric test case ID, the number prefixed by \\\"C\\\" when viewing the list of test cases in the TestRail Test Cases tab. This value is used in the API call to update the test result in TestRail.\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-js\"\n  }, \"'descriptive test case name': function (browser) {\\n        // For test case id C112233\\n        browser.testId = 112233;\\n\\n        // Test code here\\n    }\\n\")), mdx(\"p\", null, \"In the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"afterEach\"), \" test hook in your Nightwatch test suite add a call to .updateTestRail() so that it runs after each test finishes executing\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-js\"\n  }, \"module.exports = {\\n...\\n    afterEach: function (browser, done) {\\n        browser.updateTestRail(browser);\\n        browser.end();\\n    }\\n...\\n}\\n\")), mdx(\"p\", null, \"After each test case in the suite finishes the TestRail API will have the result posted to it. See also the \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://github.com/UltimateSoftware/nightwatch-testrail-updater#readme\"\n  }, \"GitHub readme for nightwatch-testrail-updater\"), \" or my \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://github.com/reallymello/nightwatchTutorials/tree/master/testRailExample\"\n  }, \"TestRail Nightwatch example project\"), \".\"), mdx(\"h2\", {\n    \"id\": \"final-thoughts\"\n  }, \"Final Thoughts\"), mdx(\"p\", null, \"Test Case Management System features typically benefit managers and business users to a higher degree than the engineers and test authors who are closer to the individual tests. To win over test engineers, the ones who will be tasked with getting the data into the TCMS, ensure your TCMS can integrate seamlessly with their test automation. Manual entry would be seen as redundant work. Test Case Management Systems that provide an API like TestRail paired with an extensible test framework like Nightwatch allow for that automatic integration to allow for successful adoption across your organization.\"), mdx(\"p\", null, \"Let me know what you think and if you end up using this where you work! Reach out through my social links below \\uD83D\\uDC47\"));\n}\n;\nMDXContent.isMDXComponent = true;","excerpt":"Making Test Case Management Systems Useful If your tests suites are manual and scattered about in various spreadsheets, Google Docs, DropBox…","fields":{"slug":"/testrail-with-nightwatchjs/","type":"posts"},"headings":[{"value":"Making Test Case Management Systems Useful","depth":2},{"value":"Integrating TestRail with Nightwatch.js","depth":2},{"value":"How to Update TestRail with Nightwatch Test Results Automatically","depth":3},{"value":"Installing nightwatch-testrail-updater","depth":3},{"value":"Sending Nightwatch Test Results to TestRail using nightwatch-testrail-updater","depth":3},{"value":"Final Thoughts","depth":2}],"frontmatter":{"title":"Integrating TestRail with Nightwatch.js","description":"Test case management systems that aren't integrated with your automation framework are wasteful. Learn how to integrate Nightwatch automated test results into TestRail automatically in this tutorial with the help of testrail-nightwatch-updater.","link":null,"date":"2021-02-07T00:00:00.000Z","tags":["quality assurance","software testing","nightwatchjs","testrail","post'"],"draft":null,"hide":null,"cover":{"childImageSharp":{"fluid":{"base64":"data:image/jpeg;base64,/9j/2wBDAAMCAgMCAgMDAwMEAwMEBQgFBQQEBQoHBwYIDAoMDAsKCwsNDhIQDQ4RDgsLEBYQERMUFRUVDA8XGBYUGBIUFRT/2wBDAQMEBAUEBQkFBQkUDQsNFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBT/wgARCAALABQDASIAAhEBAxEB/8QAGAAAAwEBAAAAAAAAAAAAAAAAAAIEBQf/xAAXAQADAQAAAAAAAAAAAAAAAAAAAQME/9oADAMBAAIQAxAAAAHh6YlmeiE4z//EABsQAAICAwEAAAAAAAAAAAAAAAEDAgQABREU/9oACAEBAAEFAkClBdbUV9jf8GIfODDZYcB6P//EABcRAAMBAAAAAAAAAAAAAAAAAAABERL/2gAIAQMBAT8BUNI//8QAGBEAAwEBAAAAAAAAAAAAAAAAAAIhARH/2gAIAQIBAT8BbW7Cn//EACIQAAEDAwMFAAAAAAAAAAAAAAEAAiEDETEEEpETIjJRYf/aAAgBAQAGPwICs117W7NmeF0dPXFJhw+uYEe15hAAw4zdNkT8CwOF/8QAHBABAAICAwEAAAAAAAAAAAAAAREhAEFRcZGB/9oACAEBAAE/IaQfSnFJdn7zmp9CqlYy4Io95rkgBn3CZ0L3V1kBPgz/2gAMAwEAAgADAAAAEFsP/8QAGhEAAgIDAAAAAAAAAAAAAAAAAAERIVGh8P/aAAgBAwEBPxCO32x4D//EABkRAQACAwAAAAAAAAAAAAAAAAEAESGh8P/aAAgBAgEBPxBFOagVyz//xAAcEAEBAAIDAQEAAAAAAAAAAAABEQAhMVFhkYH/2gAIAQEAAT8QIFQzw01QMdI9awUOVopGOA4hQ2zAk/UQ4fcr/oBYGcGfmGTislpRZ2y0S7+169z/2Q==","aspectRatio":1.7777777777777777,"src":"/static/f7ef9cb1fa133fd2a3dc14d5dc615c09/60a5f/testrail-nightwatchjs-banner.jpg","srcSet":"/static/f7ef9cb1fa133fd2a3dc14d5dc615c09/3e5eb/testrail-nightwatchjs-banner.jpg 192w,\n/static/f7ef9cb1fa133fd2a3dc14d5dc615c09/2880b/testrail-nightwatchjs-banner.jpg 384w,\n/static/f7ef9cb1fa133fd2a3dc14d5dc615c09/60a5f/testrail-nightwatchjs-banner.jpg 768w","srcWebp":"/static/f7ef9cb1fa133fd2a3dc14d5dc615c09/25278/testrail-nightwatchjs-banner.webp","srcSetWebp":"/static/f7ef9cb1fa133fd2a3dc14d5dc615c09/a278a/testrail-nightwatchjs-banner.webp 192w,\n/static/f7ef9cb1fa133fd2a3dc14d5dc615c09/2474b/testrail-nightwatchjs-banner.webp 384w,\n/static/f7ef9cb1fa133fd2a3dc14d5dc615c09/25278/testrail-nightwatchjs-banner.webp 768w","sizes":"(max-width: 768px) 100vw, 768px","presentationWidth":768,"presentationHeight":432}}}}},{"id":"23569601-41e1-55e7-9d9c-5de90cda6ca2","body":"var _excluded = [\"components\"];\nfunction _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }\nfunction _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }\nfunction _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }\n/* @jsxRuntime classic */\n/* @jsx mdx */\n\nvar _frontmatter = {\n  \"title\": \"Using Nightwatch global variables to write tests that work everywhere\",\n  \"cover\": \"./nightwatchjs-environment-globals.jpg\",\n  \"date\": \"2021-01-25T00:00:00.000Z\",\n  \"description\": \"Learn how to use Nightwatch.js global variables so you don't need to hardcode values in your test automation allowing them to work in multiple environments and reduce test maintenance costs.\",\n  \"tags\": [\"quality assurance\", \"software testing\", \"nightwatchjs\", \"post\"]\n};\nvar makeShortcode = function makeShortcode(name) {\n  return function MDXDefaultShortcode(props) {\n    console.warn(\"Component \" + name + \" was not imported, exported, or provided by MDXProvider as global scope\");\n    return mdx(\"div\", props);\n  };\n};\nvar Embed = makeShortcode(\"Embed\");\nvar layoutProps = {\n  _frontmatter: _frontmatter\n};\nvar MDXLayout = \"wrapper\";\nreturn function MDXContent(_ref) {\n  var components = _ref.components,\n    props = _objectWithoutProperties(_ref, _excluded);\n  return mdx(MDXLayout, _extends({}, layoutProps, props, {\n    components: components,\n    mdxType: \"MDXLayout\"\n  }), mdx(\"h2\", {\n    \"id\": \"why-use-global-variables-in-your-test-automation\"\n  }, \"Why Use Global Variables in your Test Automation?\"), mdx(\"p\", null, \"Nightwatch and other test automation frameworks allow you to use variables instead of hardcoding values in your tests. The benefits of this aren't always immediately obvious when you start writing a new test suite, but inevitably\"), mdx(\"ul\", null, mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"The dataset changes\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"You realize it's bad hardcoding sensitive data like login information in the test\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Someone asks if you can run the test against a different environment\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"You add translation or localization functionality\")), mdx(\"p\", null, \"and you are left with a lot of finding and replacing to do, duplicate code, or unnecessary rework.\"), mdx(\"p\", null, \"You can use Nightwatch global variables so that this information is read out of a config file instead of being hardcoded in your tests.\"), mdx(\"p\", null, \"If something changes you only have to make the change in one place similar to how the \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"../nightwatch-page-object-model-with-commands/\"\n  }, \"page object model\"), \" is a great pattern for the DOM.\"), mdx(\"p\", null, \"In addition, to the delight of your security team, you can safely store sensitive information like logins outside of your source code repository.\"), mdx(Embed, {\n    src: \"https://www.youtube.com/embed/CSwLBt_t4Vw\",\n    mdxType: \"Embed\"\n  }), mdx(\"h2\", {\n    \"id\": \"using-global-variables-in-your-tests\"\n  }, \"Using global variables in your tests\"), mdx(\"p\", null, \"The \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"nightwatch.json\"), \" config file has a test settings section that let's you store default and environment-based global variables to hold values unique to your test environments.\"), mdx(\"p\", null, \"For example, below we have a default and sandbox environment.\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-js\"\n  }, \"\\\"test_settings\\\": {\\n        \\\"default\\\": {\\n            \\\"launch_url\\\": \\\"http://localhost\\\",\\n            \\\"skip_testcases_on_fail\\\": false,\\n            \\\"desiredCapabilities\\\": {\\n                \\\"browserName\\\": \\\"chrome\\\"\\n            },\\n            \\\"globals\\\": {\\n                \\\"username\\\": \\\"nightwatchTutorial\\\",\\n                \\\"password\\\": \\\"${TUTORIAL_PASS}\\\",\\n                \\\"firstName\\\": \\\"Sharon\\\",\\n                \\\"lastName\\\": \\\"Trouble\\\",\\n                \\\"shirtSize\\\": \\\"L\\\"\\n            }\\n        },\\n        \\\"sandbox\\\": {\\n            \\\"launch_url\\\": \\\"https://sandbox.davidmello.com\\\",\\n            \\\"globals\\\": {\\n                \\\"username\\\": \\\"sandboxUser\\\",\\n                \\\"password\\\": \\\"${TUTORIAL_SANDBOX_PASS}\\\",\\n                \\\"firstName\\\": \\\"Aneeda\\\",\\n                \\\"lastName\\\": \\\"Showstopper\\\",\\n                \\\"shirtSize\\\": \\\"XL\\\"\\n            }\\n        }\\n    }\\n\")), mdx(\"p\", null, \"From your command line, when you run your test if you simply type \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"nightwatch\"), \" the test will run with the values from the default test_settings section.\"), mdx(\"p\", null, \"To run your test against a different environment all you need to do is pass the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"-e\"), \" flag. For example, \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"nightwatch -e sandbox\"), \" would run the test with the sandbox settings above. \"), mdx(\"p\", null, \"Any values specified in the non-default environment would override any entries with the same name from default. Conversely, anything not specified in the environment would use the value from default.\"), mdx(\"h2\", {\n    \"id\": \"refactoring-your-tests-to-use-global-environment-variables\"\n  }, \"Refactoring your tests to use global environment variables\"), mdx(\"p\", null, \"Nightwatch makes it pretty easy to use variables in place of hardcoded values in your tests. Here is an example, of a test suite not using global environment variables.\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-js\"\n  }, \"module.exports = {\\n    before: function(browser) {\\n        browser.url('https://test.davidmello.com')\\n        browser.page.login().loginAs('janna', 'i like testing and horses');\\n        browser.page.navigation().goToProfilePage();\\n    },\\n    \\\"profile page shows saved tshirt size\\\": function(browser) {\\n        browser.page.profile()\\n            .expect.element('@shirtSize').text.to.equal('Large');\\n    }\\n}\\n\")), mdx(\"p\", null, \"Now pretend you have hundreds of tests like this, your security team does an audit, and tells you need to stop posting the sandbox credentials to the public github repository \\uD83D\\uDE2D \\uD83D\\uDC80\"), mdx(\"p\", null, \"Also, what happens if the company adds language preferences and \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"Large\"), \" turns to \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"Largo\"), \" or product decides instead of using written out sizes like \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"Large\"), \" it will be abbreviated to \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"L\"), \".\"), mdx(\"p\", null, \"You'd have some rework on your hands.\"), mdx(\"p\", null, \"So let's refactor the tests to use global environment variables.\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-js\"\n  }, \"module.exports = {\\n    before: function(browser) {\\n        browser.page.login().navigate(); // Page object model will read launch_url from config\\n        browser.page.login()\\n            .loginAs(browser.globals.username, browser.globals.password);\\n        browser.page.navigation().goToProfilePage();\\n    },\\n    \\\"profile page shows saved tshirt size\\\": function(browser) {\\n        browser.page.profile()\\n            .expect.element('@shirtSize').text.to.equal(browser.globals.shirtSize);\\n    }\\n}\\n\")), mdx(\"p\", null, \"With this automation pattern Nightwatch will just read the value out of your config file and when you run the test it will grab the correct values from there so you don't need to maintain multiple copies of your tests for different environments or do expensive rewrites later on when data or data formats change.\"), mdx(\"p\", null, \"If the data changes you just change it in one place in your Nightwatch config file.\"), mdx(\"h2\", {\n    \"id\": \"organizing-global-variables-in-nightwatch\"\n  }, \"Organizing Global Variables in Nightwatch\"), mdx(\"p\", null, \"As a real example, my team and I were involved in a fun initiative to automate some manual environment smoke tests. It covered a large range of pages and rather than having a large list of key value pairs like this\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-js\"\n  }, \"\\\"sandbox\\\": {\\n            \\\"globals\\\": {\\n                \\\"someField1\\\": \\\"some value\\\",\\n                \\\"someField2\\\": \\\"some value\\\",\\n                ...\\n                \\\"someField42\\\": \\\"some value\\\"\\n            }\\n        }\\n\")), mdx(\"p\", null, \"We thought it would be better to use JSON nested objects to keep things logically organized together with the page. This prevents confusion when there are similarly named fields on different pages that contain different information.\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-js\"\n  }, \"\\\"sandbox\\\": {\\n            \\\"globals\\\": {\\n                \\\"profile\\\": {\\n                    \\\"firstName\\\": \\\"Janna\\\",\\n                    \\\"lastName\\\": \\\"Levtus\\\",\\n                    \\\"shirtSize\\\": \\\"L\\\"\\n                },\\n                \\\"emergencyContact\\\": {\\n                    \\\"firstName\\\": \\\"Rick\\\",\\n                    \\\"lastName\\\": \\\"Astley\\\"\\n                }\\n            }\\n        }\\n\")), mdx(\"p\", null, \"The data inside the variables can be accessed by drilling in through dot notation in your tests.\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-js\"\n  }, \"module.exports = {\\n    \\\"profile page shows saved tshirt size\\\": function(browser) {\\n        browser.page.navigation().goToProfilePage();\\n        browser.page.profile()\\n            .expect.element('@firstName').text\\n            .to.equal(browser.globals.profile.firstName);\\n        browser.page.profile()\\n            .expect.element('@lastName').text\\n            .to.equal(browser.globals.profile.lastName);\\n    },\\n    \\\"emergency contact page shows saved emergency contact\\\": function(browser) {\\n        browser.page.navigation().goToEmergencyContactPage();\\n        browser.page.emergencyContact()\\n            .expect.element('@lastName').text\\n            .to.equal(browser.globals.emergencyContact.lastName);\\n    }\\n}\\n\")), mdx(\"p\", null, \"This keeps things descriptive, well organized, and prevents name pollution when there are similarly named fields on different pages.\"), mdx(\"p\", null, \"I've posted the entire example for multi-environment testing with test globals on my GitHub\\n\\uD83D\\uDC49 \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://github.com/reallymello/nightwatchTutorials/tree/master/environmentVariables\"\n  }, \"Nightwatch.js Global Environment Variables Tutorial\"), \".\"), mdx(\"p\", null, \"If you are a Nightwatch beginner be sure to watch my \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://youtube.com/playlist?list=PLLS_Ef55N6hmkt3-JlW40GAGpXSlp8t_D\"\n  }, \"Software Testing Playlist\")), mdx(\"p\", null, \"If you have any questions or comments please reach out through my social links below \\uD83D\\uDC47\"));\n}\n;\nMDXContent.isMDXComponent = true;","excerpt":"Why Use Global Variables in your Test Automation? Nightwatch and other test automation frameworks allow you to use variables instead of…","fields":{"slug":"/nightwatch-global-environment-variables/","type":"posts"},"headings":[{"value":"Why Use Global Variables in your Test Automation?","depth":2},{"value":"Using global variables in your tests","depth":2},{"value":"Refactoring your tests to use global environment variables","depth":2},{"value":"Organizing Global Variables in Nightwatch","depth":2}],"frontmatter":{"title":"Using Nightwatch global variables to write tests that work everywhere","description":"Learn how to use Nightwatch.js global variables so you don't need to hardcode values in your test automation allowing them to work in multiple environments and reduce test maintenance costs.","link":null,"date":"2021-01-25T00:00:00.000Z","tags":["quality assurance","software testing","nightwatchjs","post"],"draft":null,"hide":null,"cover":{"childImageSharp":{"fluid":{"base64":"data:image/jpeg;base64,/9j/2wBDAAMCAgMCAgMDAwMEAwMEBQgFBQQEBQoHBwYIDAoMDAsKCwsNDhIQDQ4RDgsLEBYQERMUFRUVDA8XGBYUGBIUFRT/2wBDAQMEBAUEBQkFBQkUDQsNFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBT/wgARCAANABQDASIAAhEBAxEB/8QAFwABAQEBAAAAAAAAAAAAAAAABQAGB//EABcBAAMBAAAAAAAAAAAAAAAAAAACBAX/2gAMAwEAAhADEAAAAVn+bO4dyVg4X//EABsQAAMBAAMBAAAAAAAAAAAAAAECAwQAEhMj/9oACAEBAAEFAobgxk6MhAJyaPWlZ9l+2fn/xAAYEQACAwAAAAAAAAAAAAAAAAAAEgEDQf/aAAgBAwEBPwFa40c//8QAGREAAgMBAAAAAAAAAAAAAAAAAAEDERMh/9oACAECAQE/Abmb6jM//8QAIRAAAAUCBwAAAAAAAAAAAAAAAAECAyEREgQQE0FhgaH/2gAIAQEABj8CpT0HqPE2XJ5WyXYJtUpVAtbxC7NiVNB//8QAHBAAAgICAwAAAAAAAAAAAAAAAREAMSFhQVHx/9oACAEBAAE/IUARWGZ3AAQj8KhhLs8QOT1uLGZaIuqYdOf/2gAMAwEAAgADAAAAEFg//8QAGREBAAIDAAAAAAAAAAAAAAAAAQARQYHR/9oACAEDAQE/EAVhdPIjE//EABoRAAICAwAAAAAAAAAAAAAAAAARATFRceH/2gAIAQIBAT8QU0bjg2T/xAAcEAEAAgMAAwAAAAAAAAAAAAABESEAMVFxseH/2gAIAQEAAT8QAhIhU0Q19xkggAl7suOF5XNWnWSIqNw6OQ94krVKoIIW7JMB5eXBCky47B4z/9k=","aspectRatio":1.5737704918032787,"src":"/static/89d11ceee160bba89e4d9307141ac0a7/60a5f/nightwatchjs-environment-globals.jpg","srcSet":"/static/89d11ceee160bba89e4d9307141ac0a7/3e5eb/nightwatchjs-environment-globals.jpg 192w,\n/static/89d11ceee160bba89e4d9307141ac0a7/2880b/nightwatchjs-environment-globals.jpg 384w,\n/static/89d11ceee160bba89e4d9307141ac0a7/60a5f/nightwatchjs-environment-globals.jpg 768w,\n/static/89d11ceee160bba89e4d9307141ac0a7/2f1b1/nightwatchjs-environment-globals.jpg 800w","srcWebp":"/static/89d11ceee160bba89e4d9307141ac0a7/25278/nightwatchjs-environment-globals.webp","srcSetWebp":"/static/89d11ceee160bba89e4d9307141ac0a7/a278a/nightwatchjs-environment-globals.webp 192w,\n/static/89d11ceee160bba89e4d9307141ac0a7/2474b/nightwatchjs-environment-globals.webp 384w,\n/static/89d11ceee160bba89e4d9307141ac0a7/25278/nightwatchjs-environment-globals.webp 768w,\n/static/89d11ceee160bba89e4d9307141ac0a7/ccdb5/nightwatchjs-environment-globals.webp 800w","sizes":"(max-width: 768px) 100vw, 768px","presentationWidth":768,"presentationHeight":489}}}}},{"id":"1b1603c8-fc43-53fc-89e0-cf777edaed55","body":"var _excluded = [\"components\"];\nfunction _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }\nfunction _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }\nfunction _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }\n/* @jsxRuntime classic */\n/* @jsx mdx */\n\nvar _frontmatter = {\n  \"title\": \"Implementing a Minimum Accessibility Test Plan\",\n  \"cover\": \"./a11y-nightwatch-test-planning.png\",\n  \"date\": \"2021-01-11T00:00:00.000Z\",\n  \"description\": \"This tutorial will cover how to implement a minimum accessibility test plan against a website using Nightwatch.js and aXe.\",\n  \"tags\": [\"quality assurance\", \"accessibility testing\", \"software testing\", \"nightwatchjs\", \"post\"]\n};\nvar makeShortcode = function makeShortcode(name) {\n  return function MDXDefaultShortcode(props) {\n    console.warn(\"Component \" + name + \" was not imported, exported, or provided by MDXProvider as global scope\");\n    return mdx(\"div\", props);\n  };\n};\nvar Embed = makeShortcode(\"Embed\");\nvar layoutProps = {\n  _frontmatter: _frontmatter\n};\nvar MDXLayout = \"wrapper\";\nreturn function MDXContent(_ref) {\n  var components = _ref.components,\n    props = _objectWithoutProperties(_ref, _excluded);\n  return mdx(MDXLayout, _extends({}, layoutProps, props, {\n    components: components,\n    mdxType: \"MDXLayout\"\n  }), mdx(\"h2\", {\n    \"id\": \"implementing-a-test-plan-for-accessibility-testing\"\n  }, \"Implementing a Test Plan for Accessibility Testing\"), mdx(\"p\", null, \"Accessibility testing is similar to functional testing where you test defined expected behavior against the software under test. However, there are some unique challenges with automated accessibility testing.\"), mdx(\"p\", null, \"For one, accessibility for websites has not been a focus of many companies until recently. This means you have a testing model where the product is already built against different expected requirements and behaviors. At minimum this means there is going to be rework for the developers.\"), mdx(\"p\", null, \"I don't advocate for surprising developers with your \\\"gotcha\\\" test cases, the ones you bring out to find those \", mdx(\"em\", {\n    parentName: \"p\"\n  }, \"really awesome\"), \" defects at the last minute. It leads to having an adversarial relationship between developers and test engineers. Agreeing on the requirements, what, and how you will be testing before developers get started leads to more collaboration and less rework.\"), mdx(\"p\", null, \"As of writing, there are 90 rules in \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://dequeuniversity.com/rules/axe/4.1\"\n  }, \"aXe 4.1\"), \" and just testing against the full list isn't an efficient way to do this. You'd end up flooding your backlog with accessibility defects and probably stress out your developers and analysts.\"), mdx(\"p\", null, \"The solution I'd propose is to create a minimum accessibility test plan, get agreement that this is the minimum expected behavior of the software under test, and then automate those test cases to ensure the accessibility issues don't come back.\"), mdx(Embed, {\n    src: \"https://www.youtube.com/embed/lsv_lwxu2tI\",\n    mdxType: \"Embed\"\n  }), mdx(\"h2\", {\n    \"id\": \"creating-a-minimum-accessibility-test-plan\"\n  }, \"Creating a Minimum Accessibility Test Plan\"), mdx(\"p\", null, \"A minimum accessibility test plan will establish your foundation for making your website more accessible to users. You should have an end goal established such as eventually reaching \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://www.w3.org/TR/WCAG21/\"\n  }, \"WCAG 2.1 AA\"), \" compliance. The minimum accessibility test plan will be your first milestone along the way.\"), mdx(\"p\", null, \"I'm not a huge fan of formal test plans and strategies in an agile SDLC, lengthy formal documentation takes too long, but I believe your test plan should answer\"), mdx(\"ul\", null, mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Who the user(s) is/are (e.g., someone reliant on a screen-reader)\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"How they would use the system\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Scope of testing (entire site? or certain logical flows?)\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"How will the tests be run? (automated, manual, both)\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"How often will the tests be run?\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Who will run them? (pipeline, STE, developer?)\")), mdx(\"p\", null, \"Once you have the test plan you can iterate on it with the product team and developers to ensure common understanding and gain agreement. This is a healthier collaborative approach to testing that should lead to less rework.\"), mdx(\"p\", null, \"I'd recommend using the most impactful rules and guidance from the axe rule set. Have a goal like improving the navigation experience for users that rely on screen readers in mind.\"), mdx(\"h3\", {\n    \"id\": \"example-requirements-for-minimum-accessibility-test-plan\"\n  }, \"Example requirements for minimum accessibility test plan\"), mdx(\"ul\", null, mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Each page should have a relevant title (\", mdx(\"a\", {\n    parentName: \"li\",\n    \"href\": \"https://dequeuniversity.com/rules/axe/4.1/document-title\"\n  }, \"Axe rule 42\"), \")\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Each page should have relevant, to the content, section headers (\", mdx(\"a\", {\n    parentName: \"li\",\n    \"href\": \"https://dequeuniversity.com/rules/axe/4.1/page-has-heading-one\"\n  }, \"Axe rule 73\"), \")\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Verify text has proper color contrast (\", mdx(\"a\", {\n    parentName: \"li\",\n    \"href\": \"https://dequeuniversity.com/rules/axe/4.1/color-contrast\"\n  }, \"Axe rule 83\"), \")\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Verify site is navigable by keyboard alone (\", mdx(\"a\", {\n    parentName: \"li\",\n    \"href\": \"https://dequeuniversity.com/rules/axe/4.1/tabindex\"\n  }, \"Axe rule 46\"), \")\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Verify images have descriptive alternative text (\", mdx(\"a\", {\n    parentName: \"li\",\n    \"href\": \"https://dequeuniversity.com/rules/axe/4.1/image-alt\"\n  }, \"Axe rule 64\"), \")\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Ensure forms can be filled in by keyboard alone\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Ensure forms are not confusing when navigated with screen reading software\")), mdx(\"p\", null, \"Once you've established an agreed upon minimum accessibility test plan you can turn them into automated accessibility test cases.\"), mdx(\"h2\", {\n    \"id\": \"automating-your-accessibility-test-plan\"\n  }, \"Automating your Accessibility Test Plan\"), mdx(\"p\", null, \"Automating your accessibility tests is important because many accessibility violations are not as obvious as functional defects. For example, many of the accessibility features are not visible in the rendered markup of the page which is where automation can save you a lot of time. In addition, if you run your automation regularly your tests will ensure regression defects around your established accessibility requirements don't sneak in later.\"), mdx(\"p\", null, \"My favorite tool for implementing automating accessibility testing is Nightwatch.js combined with the nightwatch-axe-verbose NPM package. I cover installing them in my article \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"./accessibility-testing-with-nightwatchjs\"\n  }, \"accessibility testing with Nightwatch.js\"), \".\"), mdx(\"p\", null, \"The nightwatch-axe-verbose framework, unlike other accessibility assertion libraries, will not halt execution on the first rule failure. It will report on all rule violations you specify within the context of page you specify. This is great because it gives you a full picture of what needs to be remediated.\"), mdx(\"p\", null, \"Further, the combination of Nightwatch.js and the nightwatch-axe-verbose package allows you to run a subset of the 90 or so axe rules in your automated tests which allows it to conform to your minimum accessibility test plan requirements.\"), mdx(\"p\", null, \"You can accomplish running a subset of accessibility assertions using two different ways.\"), mdx(\"ul\", null, mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Disable specific tests from the axe ruleset\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Run only specific rules from the ruleset\")), mdx(\"p\", null, \"The former more aligns with the minimum accessibility test plan approach. Below are both examples.\"), mdx(\"h3\", {\n    \"id\": \"passing-rules-to-exclude-or-disable\"\n  }, \"Passing rules to exclude or disable\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-js\"\n  }, \"'Run everything except contrast and region': function (browser) {\\n        browser\\n            .url('@homePage')\\n            .axeInject()\\n            .axeRun('body', {\\n                rules: {\\n                    'color-contrast': {\\n                        enabled: false\\n                    },\\n                    'region': {\\n                        enabled: false\\n                    }\\n                }\\n            })\\n            .end();\\n    }\\n\")), mdx(\"h3\", {\n    \"id\": \"passing-specific-rules-to-include\"\n  }, \"Passing specific rules to include\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-js\"\n  }, \"'Run these rules only': function (browser) {\\n        browser\\n            .url('@homePage')\\n            .axeInject()\\n            .axeRun('body', {\\n                runOnly: ['color-contrast', 'image-alt']\\n            })\\n            .end();\\n    }\\n\")), mdx(\"p\", null, \"The full list of rule names can be found in the \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://github.com/dequelabs/axe-core/blob/develop/doc/rule-descriptions.md\"\n  }, \"axe core rule descriptions\"), \". To learn more about configuring your test project to run Nightwatch.js with nightwatch-axe-verbose watch this video on \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://youtu.be/nSodkqB-838\"\n  }, \"Nightwatch.js accessibility testing\"), \".\"), mdx(\"h3\", {\n    \"id\": \"test-suite-style-considerations\"\n  }, \"Test suite style considerations\"), mdx(\"p\", null, \"Good test suites should be readable, their intention clear, and allow for easy maintenance. To keep a test case's, intention clear I typically try to balance testing one thing per test where it makes sense balancing that with efficiency. With accessibility tests using the axe framework you can cover the entire page, subsections, or specific html elements in just one test.\"), mdx(\"p\", null, \"In addition, as shown above, you can run one or many tests against that element. In the examples above the test assertions were run against the body element of the page so all the axe rules are cascaded and run against all the html elements inside body, effectively running one test against the entire page.\"), mdx(\"p\", null, \"This provides you with a lot of assertion coverage using fewer tests.\"), mdx(\"p\", null, \"nightwatch-axe-verbose has good reporting so if you do go with that style it tells you how many elements in the page passed. \"), mdx(\"p\", null, \"It breaks out failures individually with the element identifier so you get a complete picture of each rule violation per element which is useful for remediation.\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-sh\"\n  }, \"\\u221A Passed [ok]: aXe rule: region (296 elements checked)\\n\\xD7 Failed [fail]: (aXe rule: region - All page content must be contained by landmarks\\n        In element: h1)\\n...\\n\")), mdx(\"p\", null, \"Still, this can be a lot of information so maybe it makes sense to write our automated minimum accessibility tests in a style like the one below where it is more clear what rules the tests are looking at by their test name and the tests themselves are run against more specific sections or elements of the page.\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-js\"\n  }, \"module.exports = {\\n        beforeEach: function (browser) {\\n            browser.page.login().navigate();\\n        },\\n        'Login page has descriptive title': function (browser) {\\n            browser\\n                .assert.title('Please login to BizCorp');\\n        },\\n        'Logo has alt text': function (browser) {\\n            browser\\n                .axeInject()\\n                .axeRun('#mainLogo', {\\n                    runOnly: ['image-alt']\\n                })\\n                .end();\\n        },\\n        'Login page has accessible headers': function (browser) {\\n            browser\\n                .axeInject()\\n                .axeRun('body', {\\n                    runOnly: ['empty-heading', 'heading-order', 'page-has-heading-one', \\n                             'p-as-heading']\\n                })\\n                .end();\\n        }\\n}\\n\")), mdx(\"p\", null, \"There probably isn't a one-size fits all approach to this. Weigh your current time constaints for implementing the automation against future costs of maintenance and who will be running the tests behind you to find the balance.\"), mdx(\"h3\", {\n    \"id\": \"dont-forget-state-changes\"\n  }, \"Don't forget state changes\"), mdx(\"p\", null, \"Most likely you have pages that have javascript that will update the look and feel of the page based on user interaction. You will want to ensure you test those scenarios too.\"), mdx(\"p\", null, \"For example, if you submit a form with invalid or missing information does a validation callout appear on screen? That page state should be tested as well.\"), mdx(\"p\", null, \"For example, is the error marked up appropriately where it makes sense to someone using a screen reader? Would someone know what field is in error from the message shown? An error stating \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"this field is required\"), \" without visual context may be confusing.\"), mdx(\"h2\", {\n    \"id\": \"workflow-considerations---do-i-file-a-defect\"\n  }, \"Workflow considerations - Do I file a defect?\"), mdx(\"p\", null, \"So what does one do if they are testing a legacy product, one without prior accessibility requirements, and find accessibility defects? I'd argue that if there was no accessibility requirement when product was developed it should be treated as a new feature request and not a bug.\"), mdx(\"p\", null, \"It makes more sense to me to play stories enhancing the accessibility of the product as a feature for legacy products instead of just burying the backlog with defects.\"), mdx(\"p\", null, \"This allows you and your team to work the story like any other piece of functionality. The requirements can come from the newly agreed upon accessibility test plan, coded, and verified by the tester.\"), mdx(\"p\", null, \"However, it should be considered a defect if the automated accessibility tests catch a regression after the requirements are in place.\"), mdx(\"p\", null, \"Any \", mdx(\"em\", {\n    parentName: \"p\"\n  }, \"new\"), \" features made after the minimum accessibility plan is in place should have this as an assumed requirement. Perhaps consider attaching the a11y requirements to stories as a reminder.\"), mdx(\"p\", null, \"A11y violations on these new features not caught in development should be treated as defects.\"), mdx(\"h2\", {\n    \"id\": \"final-thoughts\"\n  }, \"Final Thoughts\"), mdx(\"p\", null, \"Creating a minimum accessibility test plan is an important first step in making your website a11y compliant. Having a visible document your team can collaborate on will help with buy in. Many of the most impactful changes are easy to implement and make your site design better for all users.\"), mdx(\"p\", null, \"Use automated tests to efficiently scan your site for violations against your accessibility test plan and to prevent from backsliding with regression defects.\"), mdx(\"p\", null, \"Finally, find or be the accessibility advocate for your company and show how making your site more accessible leads to higher usage and protects the company from perils of non-compliance.\"), mdx(\"p\", null, \"I've posted the a working example of what I described in this article on my GitHub\\n\\uD83D\\uDC49 \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://github.com/reallymello/nightwatchTutorials/tree/master/a11yTestPlanExample\"\n  }, \"Implementing a minimum accessibility test plan using Nightwatch, aXe, and nightwatch-axe-verbose tutorial\")), mdx(\"p\", null, \"To stay in touch, subscribe to the \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://youtube.com/playlist?list=PLLS_Ef55N6hmkt3-JlW40GAGpXSlp8t_D\"\n  }, \"reallyMello channel on YouTube\"), \" and check our my social links below. Thanks!\"));\n}\n;\nMDXContent.isMDXComponent = true;","excerpt":"Implementing a Test Plan for Accessibility Testing Accessibility testing is similar to functional testing where you test defined expected…","fields":{"slug":"/implementing-a-minimum-accessibility-test-plan/","type":"posts"},"headings":[{"value":"Implementing a Test Plan for Accessibility Testing","depth":2},{"value":"Creating a Minimum Accessibility Test Plan","depth":2},{"value":"Example requirements for minimum accessibility test plan","depth":3},{"value":"Automating your Accessibility Test Plan","depth":2},{"value":"Passing rules to exclude or disable","depth":3},{"value":"Passing specific rules to include","depth":3},{"value":"Test suite style considerations","depth":3},{"value":"Don't forget state changes","depth":3},{"value":"Workflow considerations - Do I file a defect?","depth":2},{"value":"Final Thoughts","depth":2}],"frontmatter":{"title":"Implementing a Minimum Accessibility Test Plan","description":"This tutorial will cover how to implement a minimum accessibility test plan against a website using Nightwatch.js and aXe.","link":null,"date":"2021-01-11T00:00:00.000Z","tags":["quality assurance","accessibility testing","software testing","nightwatchjs","post"],"draft":null,"hide":null,"cover":{"childImageSharp":{"fluid":{"base64":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAYAAAB/Ca1DAAAACXBIWXMAAAsTAAALEwEAmpwYAAAB6UlEQVQoz41Ry24TQRA0VxAxznq3Z1+zL3udjR+x1zZ5YSIsRIQTgYQSXpeAEBcjDtxB4sABCSQk4MYlyk/wH3wAv8AnFNM7a8yJYKlc1TOj7qrtSuwS8iZhvKZ5wLpF6Dc0urE+31B6pM6HqdIJIXIIjmnBtSxIYSGwqeBK0yfcHgtM+wLXe4RJl7DbpqJmzbw/EriZC+z19N3+UGBrnbDT1gNCh/40rfAkdtKJCL1ET8+CpbtB6Zprvm/J5fuNMkVo/9WQBdv2yCoikKFY1aKu9AKGBp+51pILkIC0hWqmwJED2yonEFJfR97MCNsq0rWOrjkag3W//JYNr3RlVpGYK5D1K9oh/3FnKcziET9eD3UkbryInKeas1AviZdp11cxP3mET+9e49njI1i1qo4ceh6SbFxYdywBT4giikNLXmi+Y/iOg8iq4fTLB/z6+QNf37+FU72oHNIqgrANI7uLSCYIjUsI61WFlX8iMGuIL1/Atzdz8O/ji/uQNdUwjmKEs5egW68QzeZIp0dIbhyfi+b0GO7kHh48eY7vZ59x8PApbC9AJfQl0skdtGYnSHcP4bWvwu9uwe9sno/uNpy1AZygATcbQno+Kon0Mch3kO8doN8bIVFbDwwVScX+L5hqu2RoVov9DdaKQccPe8rEAAAAAElFTkSuQmCC","aspectRatio":1.7454545454545454,"src":"/static/7396df9c162627f5d65be9f98159410c/6caa6/a11y-nightwatch-test-planning.png","srcSet":"/static/7396df9c162627f5d65be9f98159410c/a4b17/a11y-nightwatch-test-planning.png 192w,\n/static/7396df9c162627f5d65be9f98159410c/1ef16/a11y-nightwatch-test-planning.png 384w,\n/static/7396df9c162627f5d65be9f98159410c/6caa6/a11y-nightwatch-test-planning.png 768w,\n/static/7396df9c162627f5d65be9f98159410c/3f078/a11y-nightwatch-test-planning.png 1152w,\n/static/7396df9c162627f5d65be9f98159410c/4fdf2/a11y-nightwatch-test-planning.png 1221w","srcWebp":"/static/7396df9c162627f5d65be9f98159410c/25278/a11y-nightwatch-test-planning.webp","srcSetWebp":"/static/7396df9c162627f5d65be9f98159410c/a278a/a11y-nightwatch-test-planning.webp 192w,\n/static/7396df9c162627f5d65be9f98159410c/2474b/a11y-nightwatch-test-planning.webp 384w,\n/static/7396df9c162627f5d65be9f98159410c/25278/a11y-nightwatch-test-planning.webp 768w,\n/static/7396df9c162627f5d65be9f98159410c/e7b8c/a11y-nightwatch-test-planning.webp 1152w,\n/static/7396df9c162627f5d65be9f98159410c/da99b/a11y-nightwatch-test-planning.webp 1221w","sizes":"(max-width: 768px) 100vw, 768px","presentationWidth":768,"presentationHeight":440}}}}},{"id":"42500c4f-ccb9-574d-a7ba-f0c02922ec6b","body":"var _excluded = [\"components\"];\nfunction _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }\nfunction _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }\nfunction _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }\n/* @jsxRuntime classic */\n/* @jsx mdx */\n\nvar _frontmatter = {\n  \"title\": \"TP-Link KL50 edison style kasa smart bulb reviewed\",\n  \"cover\": \"./tplink-kl50-smart-bulb-review.jpg\",\n  \"date\": \"2021-01-04T00:00:00.000Z\",\n  \"description\": \"TP-Link released a unicorn of a smart bulb for those looking for a smart LED edison filament style bulb perfect for people who like  vintage bulbs, but don't want to sacrifice energy savings and home automation features.\",\n  \"tags\": [\"tp-link\", \"kasa\", \"smart lights\", \"smart home\", \"iot\", \"post\"]\n};\nvar makeShortcode = function makeShortcode(name) {\n  return function MDXDefaultShortcode(props) {\n    console.warn(\"Component \" + name + \" was not imported, exported, or provided by MDXProvider as global scope\");\n    return mdx(\"div\", props);\n  };\n};\nvar Embed = makeShortcode(\"Embed\");\nvar layoutProps = {\n  _frontmatter: _frontmatter\n};\nvar MDXLayout = \"wrapper\";\nreturn function MDXContent(_ref) {\n  var components = _ref.components,\n    props = _objectWithoutProperties(_ref, _excluded);\n  return mdx(MDXLayout, _extends({}, layoutProps, props, {\n    components: components,\n    mdxType: \"MDXLayout\"\n  }), mdx(\"h2\", {\n    \"id\": \"vintage-bulbs-with-smart-features\"\n  }, \"Vintage bulbs with smart features\"), mdx(\"p\", null, \"Decorators love the look of warm glowing filament in clear bulbs housed in open light fixtures. The problem with these vintage reproduction edison bulbs is that they are still incandescent using more electrcity than LED bulbs and putting off more heat. In addition, they lack smart home features.\"), mdx(\"span\", {\n    \"className\": \"gatsby-resp-image-wrapper\",\n    \"style\": {\n      \"position\": \"relative\",\n      \"display\": \"block\",\n      \"marginLeft\": \"auto\",\n      \"marginRight\": \"auto\",\n      \"maxWidth\": \"401px\"\n    }\n  }, \"\\n      \", mdx(\"span\", {\n    parentName: \"span\",\n    \"className\": \"gatsby-resp-image-background-image\",\n    \"style\": {\n      \"paddingBottom\": \"69.75609756097562%\",\n      \"position\": \"relative\",\n      \"bottom\": \"0\",\n      \"left\": \"0\",\n      \"backgroundImage\": \"url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAOCAYAAAAvxDzwAAAACXBIWXMAAA7DAAAOwwHHb6hkAAADrUlEQVQ4yyWSy09jZRjG+xcYMhCYIGQyGTDOcNGipVx6paX0AvRG20N7elpKobSnV2g7LSXgMBcdBhiCYZiMC43EuHBhZnZm1iaTuHHiuNKFIS40rlzp7ucHrt58t9/7PM/7aTxzc8z7/ESiMiuZHPlihWqtSau1w/b2Xe7fe8Tp08959uxLjh8/4ejgmL2P97l35wHNRou8WkCJK/i8fhwzM2h8wQDBcIjFeIKVrCqAZQFssLW9y6aoZ8cHnH//ktcvX3B6eMjDB/uiyUN2RLN6vUkup5JQEgSDC7jdHjT+0AILixKxZJJVcZgvVdgQoGZzGzXu5Yuqwj+vXvDj2SHbaS87jQY7H91nUzioVhtkhYj4hULh0uVyo5n1eQlGIkjxOKnMGrlCifX6JkVVZXtrg59/es2/v77ijzc/cLK/S7NapCGa1W5vUSxXSa9kiMlxnAJmdwjL3gvLQmFEUUiuZsjki5SrTUrFErt3Wvx2fg5/vuHvv37n5OQxjZqIpN5ifaMhmldILKWRojFcnlmmLzMU+YXkGGEhW1lZJa3mKVRqwnaTcjrCUSnKL89PeX5Yp5SYo7a+Tmljk0K5RkYtIydSYgYRZlwe7NMOAZQiLAi7YRGsLIDLahFVWCnXxAQzGR5tlvju6T6f3b2NmlQoV1qopTprhQ2WM0WkmII3ELy0a7JY/geGEklCAiglUyRFyGvFdfICmi3WBDTLt58eUMvlxLoh1G+K4a2LvEvEU2sEwhLuOS9mq41JgwnNvBTFF4tzWcVfjCTTyOkciXSBknjsnwujHTGj0+rJ52vIywUkZRVJTuP1hrBa7RiNJj7QjTKo1aKxGceZNhtwWAx4bCb8M3bCbieLHheyFMc1K2Gy+hgc0mPSThLtHSD79i3S3e9SNrv4JJZCnXIRHjWwOKQTU9YPEpgYxjehJWzSEZsaY2nGRNxpw26ZYsQyj1Mu03tzFO1NLbPd/Sg9fYR6rlEM+PjmyQlfHR1xdrDP13t7aFK2EVamdaw69Kw5x8i6DVR8NkJC4ajNy4QjwIdGF/3Denr7h3l/aJi0086S3UZ6egp1dpqM0yLWQqFlHE1+ziA2jaiuCQoX1W9n2T+D3aCnq/cd2traaW+7QkfnVd5q7+HWjT5mDRMErWZidiuy3YxkHicwNoJ7ZACNZBgiZniPqFGLMqVDcYyLy2PoB/rp6+7iWkcb1zuvcKOrg76rnWiv9+KZ1BO2GolaJ5GtE8hmPZHJETyjQ/wHowRL7W2vUdkAAAAASUVORK5CYII=')\",\n      \"backgroundSize\": \"cover\",\n      \"display\": \"block\"\n    }\n  }), \"\\n  \", mdx(\"img\", {\n    parentName: \"span\",\n    \"className\": \"gatsby-resp-image-image\",\n    \"alt\": \"Vintage bulb\",\n    \"title\": \"Vintage bulb\",\n    \"src\": \"/static/5a82f6750369329f5c2ac1b3170bfac5/766a7/vintage-bulb.png\",\n    \"srcSet\": [\"/static/5a82f6750369329f5c2ac1b3170bfac5/ad4a5/vintage-bulb.png 205w\", \"/static/5a82f6750369329f5c2ac1b3170bfac5/766a7/vintage-bulb.png 401w\"],\n    \"sizes\": \"(max-width: 401px) 100vw, 401px\",\n    \"style\": {\n      \"width\": \"100%\",\n      \"height\": \"100%\",\n      \"margin\": \"0\",\n      \"verticalAlign\": \"middle\",\n      \"position\": \"absolute\",\n      \"top\": \"0\",\n      \"left\": \"0\"\n    },\n    \"loading\": \"lazy\"\n  }), \"\\n    \"), mdx(\"p\", null, \"On the other hand, smart LED bulbs offer the energy savings and smart home features, but lack the visual interest of filament bulbs.\"), mdx(\"span\", {\n    \"className\": \"gatsby-resp-image-wrapper\",\n    \"style\": {\n      \"position\": \"relative\",\n      \"display\": \"block\",\n      \"marginLeft\": \"auto\",\n      \"marginRight\": \"auto\",\n      \"maxWidth\": \"280px\"\n    }\n  }, \"\\n      \", mdx(\"span\", {\n    parentName: \"span\",\n    \"className\": \"gatsby-resp-image-background-image\",\n    \"style\": {\n      \"paddingBottom\": \"86.82926829268293%\",\n      \"position\": \"relative\",\n      \"bottom\": \"0\",\n      \"left\": \"0\",\n      \"backgroundImage\": \"url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAARCAYAAADdRIy+AAAACXBIWXMAAA7DAAAOwwHHb6hkAAADd0lEQVQ4y22U209cVRTG+T+MJj5pUm198cUm1qSPKEnRtMRW1KRGE6PYeHkwmlQjLzb6YhpjkJsNiXYiBTrcZgBxUhno3GAcJjNTEDoQBxhgyhTOZc7l5zrnzAyjce+cfVnf2muvb+21ThMNzZbuzrblzs5olFQsCxdxxLbtruq4WdI8zOkiaHJ2jpIrdGY57az18DhGWzvWi52orXkqPxsiN8WCp6tP3sRofQ2r5VvU89tUxh3cosm90LUuys7neGX8hhm8gvJVP4lXZwk8v876ow8xflU9/HAIc+QjDq8OEDn/B1PPbbL5uIIR0sXD/1A0DxyDbXJFG4qMAyHoOGfQ9wzsNFuYD+SgfVY0O3ggY9cIfNBsMPS0SvmSGKxRrMRnMdrfxH7pC7SXJ9H7Lon0axLi1OVPYOD0MPopJwTXhKIf/dY5wXv4vQSvv2Vy7YTNxgsVz8OKOU8l9B5qZzfJi9MEzhRZfUTBujHCtuBTEYnnZAfaNzeIXQgRPH3A/cf2sSbGKUhc+39R+LF5g78+3qvG0HpHhnZUAX3zQuEVnb5nVXaEma06Cq3yfUhZxr4AXGnR8J3SKF1wI0ppZwP/sI9C4W/PYNo6S9EUZ+2rpORd3v7c4NOTFpmnKpg7Kks8SanikPme8BG88b5N5wmT+2d0UCxy2QwzwRn3Hdy06Tc+Y9J8Ql7kSwm0yc0plettBRIXCyj7R3ynXmbBOin4dbbth/w0FOaHll3S7xYwdJVoeJ54LFY1KE0zNIljNV3lxdWDIsGJWyynlijt75JKJ6kY1ey3NMq7Gcb8g6zey1EqiW5ggpWVFc9gvTqccsBLn8LmJmO3/cRiEe7enSe5GEfTjuoVtb6yzvjoGNFolEhkgeTSIqqqHnvoVohtous6qqKQTaW4cydEIBhkcHCQrMTIMAwX15QjkrEo4bk5RkdHue33s7a2Vr+syatNL7lVVWFvb5fQ7AzJZJJ8Pk9XV1f9gKIcUixuMTMdJCd0M5kMPT098roFao41GLRZXv6ThYU5EvEo5XLZlReLRfk5SIWYBolE3KW4uJgQ40odb7TR5NH1BNPT0/h8PrKZrGuksTl7v9AbHh5hdWW1fuY4ZPYx5drGodbd3U0ul/uXYg1Pp9P09vaS38j/L+56WFvU2tbWFkY1RxoVa1468ap533iu1v4Byja/gS15yuAAAAAASUVORK5CYII=')\",\n      \"backgroundSize\": \"cover\",\n      \"display\": \"block\"\n    }\n  }), \"\\n  \", mdx(\"img\", {\n    parentName: \"span\",\n    \"className\": \"gatsby-resp-image-image\",\n    \"alt\": \"Ugly smart bulb\",\n    \"title\": \"Ugly smart bulb\",\n    \"src\": \"/static/1baee578cfdbee63afa23e1c19e2584d/f1fc5/ugly-smart-bulb.png\",\n    \"srcSet\": [\"/static/1baee578cfdbee63afa23e1c19e2584d/ad4a5/ugly-smart-bulb.png 205w\", \"/static/1baee578cfdbee63afa23e1c19e2584d/f1fc5/ugly-smart-bulb.png 280w\"],\n    \"sizes\": \"(max-width: 280px) 100vw, 280px\",\n    \"style\": {\n      \"width\": \"100%\",\n      \"height\": \"100%\",\n      \"margin\": \"0\",\n      \"verticalAlign\": \"middle\",\n      \"position\": \"absolute\",\n      \"top\": \"0\",\n      \"left\": \"0\"\n    },\n    \"loading\": \"lazy\"\n  }), \"\\n    \"), mdx(\"p\", null, \"That is where the \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://amzn.to/3nb3Ipc\"\n  }, \"TP-Link KL50\"), \" \", mdx(\"em\", {\n    parentName: \"p\"\n  }, \"(affiliate link)\"), \" smart bulb comes in. It looks like a vintage edison bulb, but has exposed LED filaments casting a warm light, \", mdx(\"em\", {\n    parentName: \"p\"\n  }, \"is dimmable\"), \", and has all the smart home features that come in the rest of the Kasa ecosystem.\"), mdx(\"p\", null, \"\\u2705 Vintage exposed filament looks\"), mdx(\"p\", null, \"\\u2705 Energy efficient LED\"), mdx(\"p\", null, \"\\u2705 Smart features; Alexa integration, Scheduling, Routines\"), mdx(\"p\", null, \"\\u2705 Smart dimmable controls (Kasa, Alexa, Google Assistant, IFTTT)\"), mdx(\"h2\", {\n    \"id\": \"review-of-the-tp-link-kl50-vintage-smart-light\"\n  }, \"Review of the TP-Link Kl50 Vintage Smart Light\"), mdx(\"p\", null, \"In the video review below I set out to replace the incandescent edison-style light bulb in the cool steampunk lamp holder shown above with the \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://youtu.be/ARXvCmpocxw\"\n  }, \"TP-Link KL50 smart filament light bulb\"), \". It goes over pairing with kasa, the kasa app, and voice commands with Alexa.\"), mdx(Embed, {\n    src: \"https://www.youtube.com/embed/ARXvCmpocxw\",\n    mdxType: \"Embed\"\n  }), mdx(\"h3\", {\n    \"id\": \"step-1---screw-in-the-light-bulb\"\n  }, \"Step 1 - Screw in the light bulb\"), mdx(\"p\", null, \"This normally only takes one person to do. \"), mdx(\"p\", null, \"Start by powering off your lamp holder then unscrew your old bulb and screw in the TP-Link filament bulb. Turn power on the the lamp base. The KL50 light should turn on then flash 3 times to indicate it is ready.\"), mdx(\"span\", {\n    \"className\": \"gatsby-resp-image-wrapper\",\n    \"style\": {\n      \"position\": \"relative\",\n      \"display\": \"block\",\n      \"marginLeft\": \"auto\",\n      \"marginRight\": \"auto\",\n      \"maxWidth\": \"800px\"\n    }\n  }, \"\\n      \", mdx(\"span\", {\n    parentName: \"span\",\n    \"className\": \"gatsby-resp-image-background-image\",\n    \"style\": {\n      \"paddingBottom\": \"56.09756097560976%\",\n      \"position\": \"relative\",\n      \"bottom\": \"0\",\n      \"left\": \"0\",\n      \"backgroundImage\": \"url('data:image/jpeg;base64,/9j/2wBDAAMCAgMCAgMDAwMEAwMEBQgFBQQEBQoHBwYIDAoMDAsKCwsNDhIQDQ4RDgsLEBYQERMUFRUVDA8XGBYUGBIUFRT/2wBDAQMEBAUEBQkFBQkUDQsNFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBT/wgARCAALABQDASIAAhEBAxEB/8QAFwAAAwEAAAAAAAAAAAAAAAAAAAUGB//EABQBAQAAAAAAAAAAAAAAAAAAAAX/2gAMAwEAAhADEAAAAaRXn08arqooDl//xAAaEAACAwEBAAAAAAAAAAAAAAADBAECBQYT/9oACAEBAAEFAg6SyCxdxkQzEk5MmfR3qLzTXowSR//EABkRAAMAAwAAAAAAAAAAAAAAAAABAwITIf/aAAgBAwEBPwHOetOj6KDP/8QAGhEAAgIDAAAAAAAAAAAAAAAAAAECExQxYf/aAAgBAgEBPwGDsSWjJh0//8QAJRAAAgAEBAcBAAAAAAAAAAAAAQIAAwQhERITYRQiMTJBUpGx/9oACAEBAAY/As9RWTaioPSllnm2iQ3F6Gv258D8MFphac3s9zADXs35GgLSZaqFTwIQZzgoCjYR/8QAGxABAAIDAQEAAAAAAAAAAAAAAQARITFhQeH/2gAIAQEAAT8hvQGkQL9GdR2kWyWU60TTmu4iIqUdrqIFwBYa872JFBl8DBP/2gAMAwEAAgADAAAAEOgv/8QAHREAAgIBBQAAAAAAAAAAAAAAAREAIUExUXGR8P/aAAgBAwEBPxApBYgLARwltr23EhYHuJ//xAAbEQACAgMBAAAAAAAAAAAAAAABEQAhMUHwgf/aAAgBAgEBPxAIVpsGyw7b7ERS57P/xAAbEAEBAAMBAQEAAAAAAAAAAAABEQAhQWExUf/aAAgBAQABPxC3J5OZAH0RCwjutMQXjJtg2oeV2lhh+xLwIfpCVZNTHxGW/ClOuvdXC3DuFhVHp9e46P7LEA8DP//Z')\",\n      \"backgroundSize\": \"cover\",\n      \"display\": \"block\"\n    }\n  }), \"\\n  \", mdx(\"img\", {\n    parentName: \"span\",\n    \"className\": \"gatsby-resp-image-image\",\n    \"alt\": \"KL50 vintage filament smart bulb\",\n    \"title\": \"KL50 vintage filament smart bulb\",\n    \"src\": \"/static/af52a6bf266c26e52a4522ba38fa6ff5/5fd6b/kasa-kl50-smart-bulb-install.jpg\",\n    \"srcSet\": [\"/static/af52a6bf266c26e52a4522ba38fa6ff5/bd2b6/kasa-kl50-smart-bulb-install.jpg 205w\", \"/static/af52a6bf266c26e52a4522ba38fa6ff5/ceeba/kasa-kl50-smart-bulb-install.jpg 410w\", \"/static/af52a6bf266c26e52a4522ba38fa6ff5/5fd6b/kasa-kl50-smart-bulb-install.jpg 800w\"],\n    \"sizes\": \"(max-width: 800px) 100vw, 800px\",\n    \"style\": {\n      \"width\": \"100%\",\n      \"height\": \"100%\",\n      \"margin\": \"0\",\n      \"verticalAlign\": \"middle\",\n      \"position\": \"absolute\",\n      \"top\": \"0\",\n      \"left\": \"0\"\n    },\n    \"loading\": \"lazy\"\n  }), \"\\n    \"), mdx(\"h3\", {\n    \"id\": \"step-2---pair-the-smart-light-bulb-with-kasa\"\n  }, \"Step 2 - Pair the smart light bulb with Kasa\"), mdx(\"p\", null, \"TP-Link uses the Kasa app to manage their smart home devices. It is one of the more feature rich and polished smart phone apps out there. If you already own another TP-Link smart device adding the KL50 smart light bulb can be done in moments. \"), mdx(\"p\", null, \"If you don't already have the app it is still a simple install from the app store and you just need to register an account.\"), mdx(\"p\", null, \"Once in Kasa, click the + sign in the upper right, and choose to add a device. From there, select Smart Light, then Filament LED bulb.\"), mdx(\"p\", null, \"The rest of the guided navigation will have you change your phone's wifi network to the wi-fi network of the KL50 smart bulb and return to the kasa app to finish the guided wizard.\"), mdx(\"p\", null, \"If you have the Alexa app on your phone it will automatically detect the light and add it to your Alexa-discovered devices.\"), mdx(\"p\", null, mdx(\"em\", {\n    parentName: \"p\"\n  }, \"It's important to note you will need to use a 2.4ghz network for most smart devices, including this one\")), mdx(\"h3\", {\n    \"id\": \"kasa-smart-light-features\"\n  }, \"Kasa Smart Light Features\"), mdx(\"p\", null, \"From the Kasa app you can customize and save 4 different preset brightness levels for the KL50 filament bulb. \"), mdx(\"p\", null, \"You can also use the Kasa app to specifically fine adjust your levels in 1% increments from 1-100%.\"), mdx(\"p\", null, \"At full brightness the bulb reaches 600 luments which will add a very bright warm glow to your space. At this brightness it will leave spots in your eyes if you stare at it. \"), mdx(\"p\", null, \"For visual interest I found 30% brightness or dimmer worked best. It is nice to have the option either way.\"), mdx(\"p\", null, \"Additionally, you can create multiple day of week schedules to control when to turn the light on and off.\"), mdx(\"h3\", {\n    \"id\": \"alexa-smart-home-integration\"\n  }, \"Alexa smart home integration\"), mdx(\"p\", null, \"During the Kasa pairing Alexa will add your device using the name you provided (for example \", mdx(\"em\", {\n    parentName: \"p\"\n  }, \"bedroom light\"), \"). \"), mdx(\"p\", null, \"To control the light using Alexa you can just say \"), mdx(\"p\", null, mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"Alexa turn [on/off] the [light name here]\")), mdx(\"p\", null, \"or\"), mdx(\"p\", null, mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"Alexa set the [light name here] brightness to [number] percent\"), \".\"), mdx(\"p\", null, \"Kasa and Alexa integrate very well together.\"), mdx(\"h2\", {\n    \"id\": \"final-thoughts\"\n  }, \"Final Thoughts\"), mdx(\"p\", null, \"The TP-Link KL50 smart filament light bulb has all the smart home features one would want from a dimmable LED bulb, but corrects the appearance downsides of a normal LED bulb adding the attractiveness of warm-glowing LED filament rods. \"), mdx(\"p\", null, \"The brightness dimming is really nice because it let's you adjust the brightness intensity from mood lighting the space to task lighting as needed. Since the bulb tops out at 600 luments it is powerful enough to do double duty.\"), mdx(\"p\", null, \"The bulb itself is only slightly more expensive than a non-smart LED bulb so this is a great value and recommended for fixtures that aren't already controlled by smart switches. You can purchase the \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://amzn.to/3nb3Ipc\"\n  }, \"TP-Link KL50 filament smart bulb\"), \" from the affiliate links in this review if you found this article helpful as it helps support the site. Thanks!\"), mdx(\"p\", null, mdx(\"em\", {\n    parentName: \"p\"\n  }, \"The steampunk looking lamp base can be found from seller NewWineOldBottles on Etsy\")));\n}\n;\nMDXContent.isMDXComponent = true;","excerpt":"Vintage bulbs with smart features Decorators love the look of warm glowing filament in clear bulbs housed in open light fixtures. The…","fields":{"slug":"/tplink-kl50-dimmable-filament-smart-bulb-review/","type":"posts"},"headings":[{"value":"Vintage bulbs with smart features","depth":2},{"value":"Review of the TP-Link Kl50 Vintage Smart Light","depth":2},{"value":"Step 1 - Screw in the light bulb","depth":3},{"value":"Step 2 - Pair the smart light bulb with Kasa","depth":3},{"value":"Kasa Smart Light Features","depth":3},{"value":"Alexa smart home integration","depth":3},{"value":"Final Thoughts","depth":2}],"frontmatter":{"title":"TP-Link KL50 edison style kasa smart bulb reviewed","description":"TP-Link released a unicorn of a smart bulb for those looking for a smart LED edison filament style bulb perfect for people who like  vintage bulbs, but don't want to sacrifice energy savings and home automation features.","link":null,"date":"2021-01-04T00:00:00.000Z","tags":["tp-link","kasa","smart lights","smart home","iot","post"],"draft":null,"hide":null,"cover":{"childImageSharp":{"fluid":{"base64":"data:image/jpeg;base64,/9j/2wBDAAMCAgMCAgMDAwMEAwMEBQgFBQQEBQoHBwYIDAoMDAsKCwsNDhIQDQ4RDgsLEBYQERMUFRUVDA8XGBYUGBIUFRT/2wBDAQMEBAUEBQkFBQkUDQsNFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBT/wgARCAALABQDASIAAhEBAxEB/8QAFgABAQEAAAAAAAAAAAAAAAAABgAH/8QAFQEBAQAAAAAAAAAAAAAAAAAAAwT/2gAMAwEAAhADEAAAAQhcdosik5lSr//EABwQAAICAgMAAAAAAAAAAAAAAAMEAQIFBhITIv/aAAgBAQABBQLWiodGPojRQjJZkfmqgRjWbrHP/8QAGBEAAgMAAAAAAAAAAAAAAAAAAAIDFCH/2gAIAQMBAT8BWPCup//EABoRAAICAwAAAAAAAAAAAAAAAAERAAQDEiH/2gAIAQIBAT8BsZ9OmC6w1P/EACEQAAIBAwMFAAAAAAAAAAAAAAECEQADBBIhQhMUMUGR/9oACAEBAAY/AnbICQytbdj5WRWccjIPdIo6AHIzQkD5Rj2d6sOttA5tTq0iaXbjX//EABoQAQEAAwEBAAAAAAAAAAAAAAERACExUfD/2gAIAQEAAT8h3xm+0Ci89xpklnSF5slymRCDJgioGBe5SjgNyG7Ljvk25//aAAwDAQACAAMAAAAQzP8A/8QAGREAAwADAAAAAAAAAAAAAAAAAAERITGR/9oACAEDAQE/ENRSEMRcP//EABkRAQACAwAAAAAAAAAAAAAAAAEAEWGR4f/aAAgBAgEBPxC2Aq5jFZvk/8QAGhABAQEBAQEBAAAAAAAAAAAAAREAITFRYf/aAAgBAQABPxBhgFdFWPFBJmaePvEdEKjJT90gsoQlXsPq4X1OiAs3V2CiwfEWt72uSkWPOY//2Q==","aspectRatio":1.7777777777777777,"src":"/static/07a10e9ca39f3300bdfb13553a36cc81/60a5f/tplink-kl50-smart-bulb-review.jpg","srcSet":"/static/07a10e9ca39f3300bdfb13553a36cc81/3e5eb/tplink-kl50-smart-bulb-review.jpg 192w,\n/static/07a10e9ca39f3300bdfb13553a36cc81/2880b/tplink-kl50-smart-bulb-review.jpg 384w,\n/static/07a10e9ca39f3300bdfb13553a36cc81/60a5f/tplink-kl50-smart-bulb-review.jpg 768w,\n/static/07a10e9ca39f3300bdfb13553a36cc81/2f1b1/tplink-kl50-smart-bulb-review.jpg 800w","srcWebp":"/static/07a10e9ca39f3300bdfb13553a36cc81/25278/tplink-kl50-smart-bulb-review.webp","srcSetWebp":"/static/07a10e9ca39f3300bdfb13553a36cc81/a278a/tplink-kl50-smart-bulb-review.webp 192w,\n/static/07a10e9ca39f3300bdfb13553a36cc81/2474b/tplink-kl50-smart-bulb-review.webp 384w,\n/static/07a10e9ca39f3300bdfb13553a36cc81/25278/tplink-kl50-smart-bulb-review.webp 768w,\n/static/07a10e9ca39f3300bdfb13553a36cc81/ccdb5/tplink-kl50-smart-bulb-review.webp 800w","sizes":"(max-width: 768px) 100vw, 768px","presentationWidth":768,"presentationHeight":432}}}}},{"id":"9f39ef30-b464-50b5-926e-aaf8911262a6","body":"var _excluded = [\"components\"];\nfunction _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }\nfunction _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }\nfunction _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }\n/* @jsxRuntime classic */\n/* @jsx mdx */\n\nvar _frontmatter = {\n  \"title\": \"How to use SauceLabs with Nightwatch.js\",\n  \"cover\": \"./nightwatch-saucelabs.png\",\n  \"date\": \"2020-09-28T00:00:00.000Z\",\n  \"description\": \"Learn how to configure Nightwatch.js to use SauceLabs for scalable, parallelized, multi-browser automated test execution in this software testing tutorial.\",\n  \"tags\": [\"quality assurance\", \"software testing\", \"nightwatchjs\", \"saucelabs\", \"post\"]\n};\nvar makeShortcode = function makeShortcode(name) {\n  return function MDXDefaultShortcode(props) {\n    console.warn(\"Component \" + name + \" was not imported, exported, or provided by MDXProvider as global scope\");\n    return mdx(\"div\", props);\n  };\n};\nvar Embed = makeShortcode(\"Embed\");\nvar layoutProps = {\n  _frontmatter: _frontmatter\n};\nvar MDXLayout = \"wrapper\";\nreturn function MDXContent(_ref) {\n  var components = _ref.components,\n    props = _objectWithoutProperties(_ref, _excluded);\n  return mdx(MDXLayout, _extends({}, layoutProps, props, {\n    components: components,\n    mdxType: \"MDXLayout\"\n  }), mdx(\"h2\", {\n    \"id\": \"using-saucelabs-to-run-your-nightwatchjs-tests\"\n  }, \"Using SauceLabs to run your Nightwatch.js Tests\"), mdx(\"p\", null, \"When running your automated tests on your local workstation or server you can quickly get resource constrained by the number of tests you have to run when you multiply them by platforms, browsers, and versions in-between.\"), mdx(\"p\", null, \"Nightwatch supports cloud testing platforms including SauceLabs which allows you to offload the running of the tests to their virtual machines instead of your own. \"), mdx(\"p\", null, \"There are many advantages which this approach\"), mdx(\"ul\", null, mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Nightwatch tests can be run in parallel across multiple virtual machines in the SauceLabs cloud\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"No need to maintain local test servers\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Access to different operating systems and current and past browser configurations\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Video recordings of test runs\")), mdx(\"p\", null, \"Disadvantages\"), mdx(\"ul\", null, mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Much slower single test execution time\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Slightly more complicated initial setup\")), mdx(\"p\", null, \"The video tutorial below show you how to configure Nightwatch to use SauceLabs to run your tests end to end.\"), mdx(Embed, {\n    src: \"https://www.youtube.com/embed/muMuP0DLbCQ\",\n    mdxType: \"Embed\"\n  }), mdx(\"h2\", {\n    \"id\": \"configuring-nightwatch-to-use-saucelabs\"\n  }, \"Configuring Nightwatch to use SauceLabs\"), mdx(\"p\", null, \"We will be using the \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://www.npmjs.com/package/saucelabs\"\n  }, \"saucelabs\"), \" and \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://www.npmjs.com/package/nightwatch-saucelabs-endsauce\"\n  }, \"nightwatch-saucelabs-endsauce\"), \" packages for Node.js to remotely execute our automated test commands and send our test results to SauceLabs respectively.\"), mdx(\"p\", null, mdx(\"em\", {\n    parentName: \"p\"\n  }, \"To follow along you can download the source code from GitHub from the \", mdx(\"a\", {\n    parentName: \"em\",\n    \"href\": \"https://github.com/reallymello/nightwatchTutorials/tree/master/sauceLabsExample\"\n  }, \"sauceLabsExample\"), \" in my Nightwatch tutorials project. See the README.md file there for installation instructions.\")), mdx(\"p\", null, \"In your \", mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"nightwatch.js.conf\"), \" (or nightwatch.json) configuration file the following entries will allow the two packages to connect to and authenticate with the remote SauceLabs cloud.\"), mdx(\"p\", null, mdx(\"em\", {\n    parentName: \"p\"\n  }, \"(partial section showing relevant parts)\")), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-js\"\n  }, \"{\\n...\\n    \\\"plugins\\\": [\\\"nightwatch-saucelabs-endsauce\\\"],\\n    \\\"test_settings\\\": {\\n        \\\"default\\\": {\\n            \\\"use_ssl\\\": true,\\n            \\\"silent\\\": true,\\n            \\\"selenium\\\": {\\n                \\\"port\\\": 443,\\n                \\\"host\\\": \\\"ondemand.saucelabs.com\\\"\\n            },\\n            \\\"webdriver\\\": {\\n                \\\"start_process\\\": false\\n            },\\n            \\\"desiredCapabilities\\\": {\\n                \\\"sauce:options\\\": {\\n                    \\\"username\\\": \\\"${SAUCE_USERNAME}\\\",\\n                    \\\"accessKey\\\": \\\"${SAUCE_ACCESS_KEY}\\\",\\n                    \\\"region\\\": \\\"us-west-1\\\", // or your region per your Sauce account if different\\n                    \\\"screenResolution\\\": \\\"1920x1080\\\"\\n                },\\n                \\\"browserName\\\": \\\"chrome\\\",\\n                \\\"platformName\\\": \\\"Windows 11\\\",\\n                \\\"browserVersion\\\": \\\"latest\\\",\\n                \\\"javascriptEnabled\\\": true,\\n                \\\"acceptSslCerts\\\": true,\\n                \\\"timeZone\\\": \\\"New York\\\" // optional\\n            }\\n        }\\n    }\\n...\\n}\\n\")), mdx(\"p\", null, \"The plugins property listed above will allow Nightwatch to find the nightwatch-saucelabs-endsauce package that will be called to send the test result information to SauceLabs on test end.\"), mdx(\"p\", null, \"Use \"), mdx(\"blockquote\", null, mdx(\"p\", {\n    parentName: \"blockquote\"\n  }, mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"npm install nightwatch-saucelabs-endsauce --save\"), \" \")), mdx(\"p\", null, \"to add it to your project.\"), mdx(\"p\", null, mdx(\"em\", {\n    parentName: \"p\"\n  }, \"(partial package.json)\")), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-js\"\n  }, \"{\\n   \\\"dependencies\\\": {\\n    \\\"nightwatch\\\": \\\"^2.4.2\\\",\\n    \\\"nightwatch-saucelabs-endsauce\\\": \\\"^2.0.0\\\",\\n    \\\"saucelabs\\\": \\\"^7.2.0\\\"\\n   }\\n}\\n\")), mdx(\"p\", null, \"The username and access key as shown will look for their values as environment variables. If you prefer to hardcode them you can replace the placeholder names in quotes with the username and access key from your SauceLabs account screen.\"), mdx(\"h3\", {\n    \"id\": \"setting-environment-variables-on-windows-10\"\n  }, \"Setting environment variables on Windows 10\"), mdx(\"ol\", null, mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Type \", mdx(\"em\", {\n    parentName: \"li\"\n  }, \"env\"), \" into your run bar and hit enter\"), mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Click \", mdx(\"em\", {\n    parentName: \"li\"\n  }, \"Environment Variables\"), \" toward the bottom of System Properties\"), mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"In user variables click \", mdx(\"em\", {\n    parentName: \"li\"\n  }, \"New\")), mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Type \", mdx(\"em\", {\n    parentName: \"li\"\n  }, \"SAUCE_USERNAME\"), \" for the Variable Name and enter your SauceLabs username for the variable value.\"), mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Repeat for SAUCE_ACCESS_KEY\")), mdx(\"h3\", {\n    \"id\": \"setting-environment-variables-on-mac-if-bash-is-your-default-terminal\"\n  }, \"Setting environment variables on Mac (if bash is your default terminal)\"), mdx(\"ol\", null, mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Type \", mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"sudo nano ~/.bash_profile\"), \" into a terminal\"), mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Type \", mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"export SAUCE_USERNAME=\"), \" followed by your SauceLabs username\"), mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Below that type \", mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"export SAUCE_ACCESS_KEY=\"), \" followed by your SauceLabs access key\"), mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Quit and save.\")), mdx(\"h3\", {\n    \"id\": \"setting-environment-variables-on-mac-if-zsh-is-your-default-terminal\"\n  }, \"Setting environment variables on Mac (if zsh is your default terminal)\"), mdx(\"ol\", null, mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Type \", mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"sudo nano ~/.zshrc\"), \" into a terminal\"), mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Type \", mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"export SAUCE_USERNAME=\\\"saucelabs-username-here\\\"\")), mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Below that type \", mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"export SAUCE_ACCESS_KEY=\\\"saucelabs-accesskey-here\\\"\")), mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Quit and save.\")), mdx(\"p\", null, \"In either operating system you'll need to reopen any terminal windows for them to read the change.\"), mdx(\"p\", null, \"Verify the settings are being read by your operating system by using \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"echo ${SAUCE_USERNAME}\")), mdx(\"h3\", {\n    \"id\": \"sauce_region\"\n  }, \"Sauce_region\"), mdx(\"p\", null, \"The last setting in the package.json file above is the sauce_region. This corresponds to your data center region for your account. Valid settings are us-west-1, eu-central-1, and us-east-1 so choose the appropriate value that matches your account.\"), mdx(\"h2\", {\n    \"id\": \"writing-a-nightwatchjs-test-that-runs-in-saucelabs\"\n  }, \"Writing a Nightwatch.js test that runs in SauceLabs\"), mdx(\"p\", null, \"The trickiest part, sending in the test results, is already solved by using the nightwatch-saucelabs-endsauce custom command. It just needs to be called in the afterEach test hook in your Nightwatch test suite.\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-js\"\n  }, \"module.exports = {\\n    beforeEach: function (browser) {\\n        browser.page.blog().navigate();\\n    },\\n    afterEach: function (browser) {\\n        browser.endSauce();\\n        browser.end();\\n    },\\n    'Can see homepage': function (browser) {\\n        browser.page.blog().assert.title('David Mello')\\n    },\\n    'Can navigate to Software Testing': function (browser) {\\n        browser.page.blog().click('@softwareTesting')\\n            .expect.element('h1').text.to.equal('Software Testing and Quality Assurance')\\n    }\\n}\\n\")), mdx(\"p\", null, \"SauceLabs relies on the browser session id in order to keep track of the individual tests so the browser must be closed and reopened between test cases to generate a new session id. \"), mdx(\"blockquote\", null, mdx(\"p\", {\n    parentName: \"blockquote\"\n  }, \"Starting with Nightwatch 2.0 the session id is cleared after the browser is closed so call \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"browser.endSauce()\"), \" before \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"browser.end()\"))), mdx(\"p\", null, \"I found that if you leave the browser open between test cases SauceLabs will treat the entire test suite as one test case--it needs a new session id per test.\"), mdx(\"p\", null, \"For the individual tests to properly record use the \", mdx(\"em\", {\n    parentName: \"p\"\n  }, \"afterEach\"), \" test hook which runs after each test. Inside there \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"browser.end()\"), \" is called which closes the browser.\"), mdx(\"p\", null, mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"browser.endSauce\"), \" then records the session id, test name, and pass/fail status back to SauceLabs.\"), mdx(\"p\", null, \"When \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"nightwatch\"), \" is run in the terminal of the root of the SauceLabs example project you'll see output similar to the following.\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\"\n  }, \"Running:  Can navigate to Software Testing\\n\\ni Connected to ondemand.saucelabs.com on port 443 (3427ms).\\n  Using: chrome (85.0.4183.83) on Windows platform.\\n\\n\\u221A Expected element <h1> text to equal: \\\"Software Testing and Quality Assurance\\\" (793ms)\\nSauceOnDemandSessionID=207dc6cf57824a72af4787dd49774aa8\\njob-name==Can navigate to Software Testing\\npassed=true\\n\")), mdx(\"p\", null, \"Nightwatch sends the commands over the saucelabs REST API to the remote environment and browser they have there in the cloud which then sends the results back to your local Nightwatch terminal window. The last 3 lines are the output from nightwatch-saucelabs-endsauce that sends the final test result back to SauceLabs.\"), mdx(\"span\", {\n    \"className\": \"gatsby-resp-image-wrapper\",\n    \"style\": {\n      \"position\": \"relative\",\n      \"display\": \"block\",\n      \"marginLeft\": \"auto\",\n      \"marginRight\": \"auto\",\n      \"maxWidth\": \"680px\"\n    }\n  }, \"\\n      \", mdx(\"span\", {\n    parentName: \"span\",\n    \"className\": \"gatsby-resp-image-background-image\",\n    \"style\": {\n      \"paddingBottom\": \"65.36585365853658%\",\n      \"position\": \"relative\",\n      \"bottom\": \"0\",\n      \"left\": \"0\",\n      \"backgroundImage\": \"url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAANCAYAAACpUE5eAAAACXBIWXMAABJ0AAASdAHeZh94AAACZ0lEQVQ4y5VTaU8TURR9bAItS5dpC13AtGUXERMERVDAD/DBRP6SiZ/8AWo0GDe2sEn8R5hYset09nkzx3dfaQ3RL77k5Nw5977Te29nmMt9VHQPVQHV9FHXDFQrFei6jnq9jlqthnK5LFgVqKJYLEqtqZumCe67gA95GOfCzOBQBaqGB003US6VoGmaNKwI88vLS2mgqipKIkca5UrFkmhAhes58H1fgrmOePBccNeBx114HpcJR+gEiuk0L1yPfVHvgXPeAnNdVwaWZV2JjQLqUK1r8sL/nJYh7cIwCIY0J6ZubduWOdopabboulFrSMicoctppGFzNDJpFlJMRu5VzpbPlmTSKGdZJixRT7HURWO+mIaRUFPF8sv0zzZ+kRZ+8aOAi0IB3yV+tvC3VmjpdIfRuLqmo/ireG0XpkldiM7tP7Cv2HFcOC4XzFvczDEa7+DgCG9ev8POzge83/ko+fOnXRwfneL87BtOT762cHZ6joP9I+ztHmJv71DyvuCT4zOZZ/R+pTN5MNaF9o4g2toDYG29CEeGEehTMDCYgBLLIBpLIxQeQiSaQmdXv6jvFuhpsLgTi2eQTGbB6GXN5WfQ0dmHQDCK3kBEIhBS8GRzE0+fbSOdH8fo2CTm793HnYVFKKmMRDwzgsTIKKLJNPqVuASjTyqbm5bdkVFPbxg3ugcRiqfw8sVzfHn7CkurjzA1N48Hj9ewvLaOheUVZKdmcHNyCuOzs0jlchhMDCE8nPy3YXdPCMGwgtm7S9ja2kZ2bAK56VuYuD2HxZVVPFzfQCSZQlCJoS8Wx0A8gdDQsDT9DcSPIN37H8gRAAAAAElFTkSuQmCC')\",\n      \"backgroundSize\": \"cover\",\n      \"display\": \"block\"\n    }\n  }), \"\\n  \", mdx(\"img\", {\n    parentName: \"span\",\n    \"className\": \"gatsby-resp-image-image\",\n    \"alt\": \"SauceLabs view\",\n    \"title\": \"SauceLabs view\",\n    \"src\": \"/static/0d592de83c0ca3f25d659a444d0e3d2f/bf608/sauceLabs-screenshot.png\",\n    \"srcSet\": [\"/static/0d592de83c0ca3f25d659a444d0e3d2f/ad4a5/sauceLabs-screenshot.png 205w\", \"/static/0d592de83c0ca3f25d659a444d0e3d2f/74ab4/sauceLabs-screenshot.png 410w\", \"/static/0d592de83c0ca3f25d659a444d0e3d2f/bf608/sauceLabs-screenshot.png 680w\"],\n    \"sizes\": \"(max-width: 680px) 100vw, 680px\",\n    \"style\": {\n      \"width\": \"100%\",\n      \"height\": \"100%\",\n      \"margin\": \"0\",\n      \"verticalAlign\": \"middle\",\n      \"position\": \"absolute\",\n      \"top\": \"0\",\n      \"left\": \"0\"\n    },\n    \"loading\": \"lazy\"\n  }), \"\\n    \"), mdx(\"h2\", {\n    \"id\": \"desired-capabilities-setting-the-browser\"\n  }, \"Desired Capabilities, setting the browser\"), mdx(\"p\", null, \"In the nightwatch.json file you can tell SauceLabs what browser and operating system you want your test automation to run under using the desired capabilities configuration section.\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-js\"\n  }, \"\\\"test_settings\\\": {\\n        \\\"default\\\": {\\n...\\n            \\\"desiredCapabilities\\\": {\\n                \\\"browserName\\\": \\\"chrome\\\",\\n                \\\"screenResolution\\\": \\\"1280x1024\\\",\\n                \\\"browserVersion\\\": \\\"latest\\\",\\n                \\\"javascriptEnabled\\\": true,\\n                \\\"acceptSslCerts\\\": true,\\n                \\\"timeZone\\\": \\\"New York\\\"\\n            }\\n        }\\n    }\\n\")), mdx(\"p\", null, \"The screen resolution is important to set a realistic window size for the test to run under. If you notice oddities around date and time ensure that your timeZone setting is set correctly here as well. I noticed javascript dates were wrong when running my test from the east coast while being on the west coast cloud before I explicitly set New York for the time zone here.\"), mdx(\"h2\", {\n    \"id\": \"parallelizing-nightwatch-saucelabs-tests\"\n  }, \"Parallelizing Nightwatch SauceLabs tests\"), mdx(\"p\", null, \"The biggest drawback when using SauceLabs instead of local test execution is that it is considerably slower for them to run a per test basis. However, the time is quickly made up if you take advantage of parallel test execution. \"), mdx(\"p\", null, \"Nightwatch has the capability to run your tests in parallel as long as they are in different .js files. It can spin up one test execution thread per .js file.\"), mdx(\"p\", null, \"With SauceLabs you can take advantage of this and use as many virtual machines to parallelize your tests as your account allocation permits.\"), mdx(\"p\", null, \"To enable parallel test execution in Nightwatch set the \", mdx(\"em\", {\n    parentName: \"p\"\n  }, \"enabled\"), \" flag equal to true under \", mdx(\"em\", {\n    parentName: \"p\"\n  }, \"test_workers\"), \" and configure the workers to auto or a set number of your choosing.\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-js\"\n  }, \"{\\n...\\n    \\\"page_objects_path\\\": \\\"./page-objects\\\",\\n    \\\"test_workers\\\": {\\n        \\\"enabled\\\": true,\\n        \\\"workers\\\": \\\"auto\\\"\\n    },\\n\")), mdx(\"h2\", {\n    \"id\": \"final-thoughts\"\n  }, \"Final Thoughts\"), mdx(\"p\", null, \"Initially it was a little hard to put together all the bits and pieces of tutorials out there to figure out how to get SauceLabs working with Nightwatch so I hope this article does a more comprehensive job for you. Nightwatch and SauceLabs seem to pair pretty well together so far and I've immediately appreciated the video recording capabilities SauceLabs offers as well as the parallel test execution offloaded from my local development machine.\"), mdx(\"p\", null, \"If you have any questions or comments please reach out through my social links below \\uD83D\\uDC47\"));\n}\n;\nMDXContent.isMDXComponent = true;","excerpt":"Using SauceLabs to run your Nightwatch.js Tests When running your automated tests on your local workstation or server you can quickly get…","fields":{"slug":"/how-to-use-nightwatch-with-saucelabs/","type":"posts"},"headings":[{"value":"Using SauceLabs to run your Nightwatch.js Tests","depth":2},{"value":"Configuring Nightwatch to use SauceLabs","depth":2},{"value":"Setting environment variables on Windows 10","depth":3},{"value":"Setting environment variables on Mac (if bash is your default terminal)","depth":3},{"value":"Setting environment variables on Mac (if zsh is your default terminal)","depth":3},{"value":"Sauce_region","depth":3},{"value":"Writing a Nightwatch.js test that runs in SauceLabs","depth":2},{"value":"Desired Capabilities, setting the browser","depth":2},{"value":"Parallelizing Nightwatch SauceLabs tests","depth":2},{"value":"Final Thoughts","depth":2}],"frontmatter":{"title":"How to use SauceLabs with Nightwatch.js","description":"Learn how to configure Nightwatch.js to use SauceLabs for scalable, parallelized, multi-browser automated test execution in this software testing tutorial.","link":null,"date":"2020-09-28T00:00:00.000Z","tags":["quality assurance","software testing","nightwatchjs","saucelabs","post"],"draft":null,"hide":null,"cover":{"childImageSharp":{"fluid":{"base64":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAYAAAB/Ca1DAAAACXBIWXMAAAsTAAALEwEAmpwYAAACgElEQVQoz12TTWgTURDH92JJrQeT7L63u0l2N9u1+TBp09QmadMkxtailLaWVlQq4jWKpaJV0IuoxZMggiBYEA8exIMHRQ9FkCK2tEVQwYtID6Ko+AVKrQr+nd1NS/DwY/a9ee8/M29mBV3hCMkcCY2jye9Bi7IBKSIi1tMec3y6wmrgNZCfcwSr2GcFgxwBWrRqfkydn8BkZY/D1LljtCeSj61dXhU0yBrVdZSCxnSGuCHBCjII4YCCjes9mBg7hEdzi7g7M48bdx7g+aslHD9SgbfBAzOoOEFXs1EkF5VxpC0J2aiIfFxES6NEgkEV3vo6XJk8jfmXrzF9awr3rl/Ci6V3uHz2FHTfOoyWRAx3eLG704v+dh8GMj6M0PdQzofBrA+dJNZqcQfBjpyxFOxPy1iYmwWe3Qae3sTi3BOMphUUIgqSFHl7yo/CZj+aTckR6IiJaNtkC4lOuc0md8oXuEiHEyEcaON4PH0ffwD8/gvMzzx09noTQYh+hgBzS7StTOWGVQ4rRI3UXCK6awVTCyEe8GGsO4r3b99geeUXln+u4NPHDxjviaHVoHej7hmq3Qy3IbrM0B7l2NLE0ZXgKCa5s05QlsKuvp3I53JIinW4euYovn/9jB/fvuAadbxNbYAqidDsLsv2CDESr8JZdVzs0WFrVghxCeOHKxgc3osL/TEsnihh9mQ3Lo60oG9gCL3lrdCZK2oRMSKuuNnWzuMqjmCBMiyXe9Dd1YliKo5iuhnlXAb5fAHbMllkuYjBgIx9AY6DQY4dqituZ2xnXjvsgkYbGr2RFQ7DNMKwrAgaTQumbq8NxDUNeYqcJpES0UNkiUa6rMn//zkc/wAVWW+f+ppWigAAAABJRU5ErkJggg==","aspectRatio":1.7777777777777777,"src":"/static/c621638c2d5abd6d6a88c13974465801/05b00/nightwatch-saucelabs.png","srcSet":"/static/c621638c2d5abd6d6a88c13974465801/a4b17/nightwatch-saucelabs.png 192w,\n/static/c621638c2d5abd6d6a88c13974465801/1ef16/nightwatch-saucelabs.png 384w,\n/static/c621638c2d5abd6d6a88c13974465801/05b00/nightwatch-saucelabs.png 700w","srcWebp":"/static/c621638c2d5abd6d6a88c13974465801/64998/nightwatch-saucelabs.webp","srcSetWebp":"/static/c621638c2d5abd6d6a88c13974465801/a278a/nightwatch-saucelabs.webp 192w,\n/static/c621638c2d5abd6d6a88c13974465801/2474b/nightwatch-saucelabs.webp 384w,\n/static/c621638c2d5abd6d6a88c13974465801/64998/nightwatch-saucelabs.webp 700w","sizes":"(max-width: 700px) 100vw, 700px","presentationWidth":700,"presentationHeight":395}}}}},{"id":"8b88d92f-07a9-51f2-9a17-87a7943c791d","body":"var _excluded = [\"components\"];\nfunction _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }\nfunction _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }\nfunction _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }\n/* @jsxRuntime classic */\n/* @jsx mdx */\n\nvar _frontmatter = {\n  \"title\": \"Using SwitchBot to install a smart switch without a neutral\",\n  \"cover\": \"./switchbot-setup-review.jpg\",\n  \"date\": \"2020-09-07T00:00:00.000Z\",\n  \"description\": \"Renters who can't replace their switches or homeowners without neutral wires needed for smart switches have trouble installing smart switches in their homes. Switchbot makes a unique product that allows you to convert your dumb switches into smart switches.\",\n  \"tags\": [\"smart home\", \"iot\", \"switchbot\", \"post\"]\n};\nvar makeShortcode = function makeShortcode(name) {\n  return function MDXDefaultShortcode(props) {\n    console.warn(\"Component \" + name + \" was not imported, exported, or provided by MDXProvider as global scope\");\n    return mdx(\"div\", props);\n  };\n};\nvar Embed = makeShortcode(\"Embed\");\nvar layoutProps = {\n  _frontmatter: _frontmatter\n};\nvar MDXLayout = \"wrapper\";\nreturn function MDXContent(_ref) {\n  var components = _ref.components,\n    props = _objectWithoutProperties(_ref, _excluded);\n  return mdx(MDXLayout, _extends({}, layoutProps, props, {\n    components: components,\n    mdxType: \"MDXLayout\"\n  }), mdx(\"h2\", {\n    \"id\": \"installing-smart-switches-without-a-neutral-wire\"\n  }, \"Installing smart switches without a neutral wire\"), mdx(\"p\", null, \"Unlike normal, or dumb switches, smart switches need to stay powered on all the time to be ready to respond to your commands. Smart switches rely on the neutral wire to provide that always on power, but many homeowners find their house wiring does not include it. Neutral wires weren't very common in houses built prior to the 1980s.\"), mdx(\"p\", null, \"This leaves you with a few different options to install a smart with in a house circuits without a neutral wire.\"), mdx(\"ul\", null, mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Have an electrician add a neutral wire\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Go with a smart switch like \", mdx(\"a\", {\n    parentName: \"li\",\n    \"href\": \"https://amzn.to/3lZdfQT\"\n  }, \"C by GE\"), \" that doesn't need a neutral (it uses capacitors to bank the energy)\")), mdx(\"p\", null, \"OR\"), mdx(\"p\", null, \"Use a robot like the \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://amzn.to/3bFY0aM\"\n  }, \"SwitchBot Switch\"), \" to eliminate the need for wiring anything which is what this article will cover in more detail.\"), mdx(\"h2\", {\n    \"id\": \"advantages-of-using-switchbot-to-control-your-lights-and-toggle-switches\"\n  }, \"Advantages of using SwitchBot to control your lights and toggle switches\"), mdx(\"p\", null, \"The SwitchBot is a novel battery-operated device that attaches under a decora light switch using 3M adhesive and uses a robotic finger to toggle on or off your light switches.\"), mdx(\"p\", null, \"It is a great idea and can upgrade anything that requires a physical button press to operate into a smart home device*.\"), mdx(\"p\", null, \"*As long as the SwitchBot can fit near it and its finger can reach.\"), mdx(\"p\", null, \"The strongest use cases I feel would be for\"), mdx(\"ul\", null, mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Renters who can't replace their light switches\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Homes without neutral wires in their light circuits\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Homeowners who don't like playing with electricity\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Complicated 3 and 4 way light switch circuits that are hard to find compatible smart switches for\")), mdx(\"p\", null, \"You can see my experiences with it in the below review of the Switchbot switch, hub mini, and hygrometer (humidity sensor)\"), mdx(Embed, {\n    src: \"https://www.youtube.com/embed/_tGab37bBp8\",\n    mdxType: \"Embed\"\n  }), mdx(\"h2\", {\n    \"id\": \"switchbot-installation\"\n  }, \"SwitchBot installation\"), mdx(\"p\", null, \"The Switchbot is pretty small and the white color matched my decora switch plates. It also comes in \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://amzn.to/322dnHh\"\n  }, \"black\"), \" to match black switch plates. The 3M adhesive paper backing peeled off easily and stuck just below the decora paddle switch.\"), mdx(\"p\", null, \"Switchbot includes extra 3M backings if you need to move the switch somewhere else later.\"), mdx(\"p\", null, \"The obvious question is if the finger pushes the switch in to turn it on or off how do you reverse it? SwitchBot also includes a lasso attachment that sticks onto the decora paddle and hooks into the SwitchBot's finger to pull the switch in the reverse direction.\"), mdx(\"p\", null, \"You sync this \\\"switch mode\\\" mode up in the SwitchBot app and you can invert the on/off, as well as the duration of the press, if you've installed the SwitchBot above or below your switch.\"), mdx(\"p\", null, \"The user-replaceable battery lasts for 600 days.\"), mdx(\"h2\", {\n    \"id\": \"good-uses-for-switchbot\"\n  }, \"Good uses for SwitchBot\"), mdx(\"p\", null, \"SwitchBot works best on normal light switches. Heavier press switches like garage door buttons are a little bit too wide and stiff to push without dislodging the SwitchBot.\"), mdx(\"p\", null, \"With the SwitchBot hub mini and thermostat hygrometer you could create routines in \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://ifttt.com/\"\n  }, \"IFTTT\"), \" or Alexa to a degree to have the SwitchBot toggle on a rocker switch for a fan if a room gets too hot or humid for example.\"), mdx(\"p\", null, \"Another cool feature of the \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://amzn.to/3i7aavJ\"\n  }, \"SwitchBot hub mini\"), \" is that it can control devices that use infrared like your TV so you could create a routine that turns on or off your TV and turns the room lights off at the same time with the SwitchBot switch for a movie.\"), mdx(\"h2\", {\n    \"id\": \"final-thoughts\"\n  }, \"Final Thoughts\"), mdx(\"p\", null, \"My light circuits do have neutrals and I have replaced many switches in my house with smart ones by \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"../tplink-kasa-light-switch-hs200\"\n  }, \"TP-Link\"), \" and \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://youtu.be/OW36oe8oTsY\"\n  }, \"Etekcity\"), \", but I am a bit wary of changing them all out due to how rapidly tech advances. On the remaining switches adding SwitchBot is so low effort and temporary, if need be, that it is well worth the price and if I change my mind it is easy to stop using them or stick them elsewhere.\"), mdx(\"p\", null, \"They are a great idea and well worth it.\"));\n}\n;\nMDXContent.isMDXComponent = true;","excerpt":"Installing smart switches without a neutral wire Unlike normal, or dumb switches, smart switches need to stay powered on all the time to be…","fields":{"slug":"/smart-switch-no-neutral-using-switchbot/","type":"posts"},"headings":[{"value":"Installing smart switches without a neutral wire","depth":2},{"value":"Advantages of using SwitchBot to control your lights and toggle switches","depth":2},{"value":"SwitchBot installation","depth":2},{"value":"Good uses for SwitchBot","depth":2},{"value":"Final Thoughts","depth":2}],"frontmatter":{"title":"Using SwitchBot to install a smart switch without a neutral","description":"Renters who can't replace their switches or homeowners without neutral wires needed for smart switches have trouble installing smart switches in their homes. Switchbot makes a unique product that allows you to convert your dumb switches into smart switches.","link":null,"date":"2020-09-07T00:00:00.000Z","tags":["smart home","iot","switchbot","post"],"draft":null,"hide":null,"cover":{"childImageSharp":{"fluid":{"base64":"data:image/jpeg;base64,/9j/2wBDAAMCAgMCAgMDAwMEAwMEBQgFBQQEBQoHBwYIDAoMDAsKCwsNDhIQDQ4RDgsLEBYQERMUFRUVDA8XGBYUGBIUFRT/2wBDAQMEBAUEBQkFBQkUDQsNFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBT/wgARCAALABQDASIAAhEBAxEB/8QAFwABAQEBAAAAAAAAAAAAAAAABwABBv/EABcBAAMBAAAAAAAAAAAAAAAAAAIDBAX/2gAMAwEAAhADEAAAAWMp4LI9AqkSJn//xAAZEAEBAQEBAQAAAAAAAAAAAAADBAUCARP/2gAIAQEAAQUC2fjNn7O+85Xwv7U1SmOMXPdFLoFP/8QAGhEAAgIDAAAAAAAAAAAAAAAAAQIAEQMEIv/aAAgBAwEBPwE4+LMXVRhdz//EABkRAAIDAQAAAAAAAAAAAAAAAAABAgMSEf/aAAgBAgEBPwFT1PI7Gnw//8QAIBAAAgIBBAMBAAAAAAAAAAAAAQIAEQMEEiFREzFBYv/aAAgBAQAGPwLSOAq8i9o/MyYNEfOua1dBVkRy2BkY+16i7XK31ELC9yPd/ZlRGIRTwJ//xAAaEAADAQEBAQAAAAAAAAAAAAAAARExIUGR/9oACAEBAAE/IVRk7usCCCrksGu4oMYBejoX2Zfr6UcVLXCJF00xRH//2gAMAwEAAgADAAAAEKz/AP/EABkRAQACAwAAAAAAAAAAAAAAAAEAESFh0f/aAAgBAwEBPxARtddgErOif//EABgRAAMBAQAAAAAAAAAAAAAAAAABETHR/9oACAECAQE/EGpVS8HaMP/EABwQAQEAAgMBAQAAAAAAAAAAAAERACExQVFhgf/aAAgBAQABPxBCCQRdF4K+zBCWSYxvuwBprfTJD2eDdCCSRI8OEcjWBHzkfmUz80h3C4Q3B6LULdVdZ//Z","aspectRatio":1.7777777777777777,"src":"/static/3ffc3723e23ab38b647739a8abbf665f/60a5f/switchbot-setup-review.jpg","srcSet":"/static/3ffc3723e23ab38b647739a8abbf665f/3e5eb/switchbot-setup-review.jpg 192w,\n/static/3ffc3723e23ab38b647739a8abbf665f/2880b/switchbot-setup-review.jpg 384w,\n/static/3ffc3723e23ab38b647739a8abbf665f/60a5f/switchbot-setup-review.jpg 768w,\n/static/3ffc3723e23ab38b647739a8abbf665f/a21ec/switchbot-setup-review.jpg 797w","srcWebp":"/static/3ffc3723e23ab38b647739a8abbf665f/25278/switchbot-setup-review.webp","srcSetWebp":"/static/3ffc3723e23ab38b647739a8abbf665f/a278a/switchbot-setup-review.webp 192w,\n/static/3ffc3723e23ab38b647739a8abbf665f/2474b/switchbot-setup-review.webp 384w,\n/static/3ffc3723e23ab38b647739a8abbf665f/25278/switchbot-setup-review.webp 768w,\n/static/3ffc3723e23ab38b647739a8abbf665f/c12ce/switchbot-setup-review.webp 797w","sizes":"(max-width: 768px) 100vw, 768px","presentationWidth":768,"presentationHeight":431}}}}},{"id":"0292500f-2a38-5dd6-b4b7-e9f9884ce36a","body":"var _excluded = [\"components\"];\nfunction _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }\nfunction _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }\nfunction _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }\n/* @jsxRuntime classic */\n/* @jsx mdx */\n\nvar _frontmatter = {\n  \"title\": \"Comparing the Amcrest ProStream AWC2198 webcam to other 1080p models\",\n  \"cover\": \"./amcrest-prostream-awc2198-on-monitor.jpg\",\n  \"date\": \"2020-08-25T00:00:00.000Z\",\n  \"description\": \"Amcrest generally puts out pretty decent quality security cameras at a reasonable price, but their webcams have been hit or miss. This review will see if the latest Amcrest ProStream model AWC2198 USB webcam gets it right.\",\n  \"tags\": [\"smart home\", \"camera\", \"iot\", \"post\"]\n};\nvar makeShortcode = function makeShortcode(name) {\n  return function MDXDefaultShortcode(props) {\n    console.warn(\"Component \" + name + \" was not imported, exported, or provided by MDXProvider as global scope\");\n    return mdx(\"div\", props);\n  };\n};\nvar Embed = makeShortcode(\"Embed\");\nvar layoutProps = {\n  _frontmatter: _frontmatter\n};\nvar MDXLayout = \"wrapper\";\nreturn function MDXContent(_ref) {\n  var components = _ref.components,\n    props = _objectWithoutProperties(_ref, _excluded);\n  return mdx(MDXLayout, _extends({}, layoutProps, props, {\n    components: components,\n    mdxType: \"MDXLayout\"\n  }), mdx(\"h2\", {\n    \"id\": \"another-amcrest-webcam-what-changed-this-time\"\n  }, \"Another Amcrest Webcam? What changed this time?\"), mdx(\"p\", null, \"In the span of a pandemic Amcrest has released at 5 different webcams. Some are honestly identical with just a different plastic housing. Others have slightly different CMOS sensors, lenses, microphones, and mounting options. So this review will do a side-by-side comparison review of the different webcam offerings to sort through the differences.\"), mdx(\"p\", null, \"The ProStream AWC2198 model webcam sits between the AWC201-B and the AWC195-B if you go by the specifications alone.\"), mdx(\"p\", null, \"In my review of the \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://www.davidmello.com/amcrest-hd-webcam-1080p-review/\"\n  }, \"AWC201-B webcam\"), \" I found the image quality OK though the colors were a bit washed out and the wide-angle lens probably won't fit everyone's needs. The big problem was the microphone included with it was terrible.\"), mdx(\"p\", null, \"The AWC2198 indicated a \", mdx(\"em\", {\n    parentName: \"p\"\n  }, \"high sensitivity dual mic\"), \" over the standard \", mdx(\"em\", {\n    parentName: \"p\"\n  }, \"built in microphone\"), \" indicated on the AWC201-B. So with the ProStream AWC2198 I was hoping Amcrest improved the microphone giving me a very decent budget webcam.\"), mdx(\"p\", null, \"I perform a side-by-side comparison of the ProStream AWC2198 against the AWC195-B and the AWC201-B in the video below or you can keep reading for more information.\"), mdx(Embed, {\n    src: \"https://www.youtube.com/embed/CZVrK1LwV_A\",\n    mdxType: \"Embed\"\n  }), mdx(\"h2\", {\n    \"id\": \"amcrest-awc2198-1080p-prostream-webcam-specifications\"\n  }, \"Amcrest AWC2198 1080P ProStream webcam specifications\"), mdx(\"ul\", null, mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"1/2.9\\\" CMOS image sensor (Slightly more imaging area than an iPhone 6)\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"1080P Full HD (1920x1080) at 30fps\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"3.6mm lens (wide)\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"90 degree field of view\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"\\\"High sensitivity\\\" dual mic\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"USB 2.0 Plug and Play\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Privacy cover\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"~6ft USB cable length\")), mdx(\"p\", null, \"As with the previous AWC201-B the specifications look like it would be sufficient for virtual lessons and classes for kids returning to school and when attending meetings on Microsoft Teams, Zoom, and so on. The webcam video output smoothly records 1080p resolution at 30 frames per second. For comparison, cinematic movies are 24fps, soap operas 30fps, action cameras are 60fps. \"), mdx(\"p\", null, \"The CMOS sensor is slightly larger than the AWC201-B so it should work better in low light by some degree.\"), mdx(\"p\", null, \"This model does not come with a physical privacy cover, but does include a tripod mount if you don't want to use the built-in clamp to place it on top of your monitor.\"), mdx(\"h2\", {\n    \"id\": \"installation-and-setup\"\n  }, \"Installation and Setup\"), mdx(\"p\", null, \"On a Windows computer you simply plug the webcam in and Windows will detect it as the Amcrest AWC2198 USB Webcam if you look for it under Sound, Video, and Game Controllers. No software is bundled with the webcam so adjustments to things like brightness, color, and contrast would be made in your video conference software like Microsoft Teams or Zoom.\"), mdx(\"p\", null, \"The Amcrest ProStream webcam will appear as a video source you can select in Microsoft Teams, Zoom, Hangouts, etc., and it will start streaming video automatically once selected.\"), mdx(\"h2\", {\n    \"id\": \"video-and-audio-quality-and-performance\"\n  }, \"Video and Audio Quality and Performance\"), mdx(\"p\", null, \"As you can see in the \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://youtu.be/CZVrK1LwV_A\"\n  }, \"Amcrest AWC2185 webcam review\"), \" embedded at the top of the article the microphone is better than the AWC201-B and AWC195-B, but the video is blurry. I was expecting it to be wide angle like the AWC201-B, but this one even distorts, noticeably, the center of the frame.\"), mdx(\"p\", null, \"It's like they fixed the microphone and color issues of the previous two models, but introduced an issue with the focus.\"), mdx(\"h2\", {\n    \"id\": \"final-thoughts\"\n  }, \"Final Thoughts\"), mdx(\"p\", null, \"If you were considering one of these three webcams I'd recommend the \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://amzn.to/3gCyepH\"\n  }, \"AWC195-B webcam\"), \" over this new ProStream model. The focus is too blurry. If you want to upgrade the microphone you can pair it with the \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://amzn.to/3alUM9O\"\n  }, \"Samson USB Meteor mic\"), \" for a huge upgrade.\"), mdx(\"p\", null, \"For a higher price point you can get better quality video in something like the \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://amzn.to/3ewG6YV\"\n  }, \"Logitech Brio\"), \".\"));\n}\n;\nMDXContent.isMDXComponent = true;","excerpt":"Another Amcrest Webcam? What changed this time? In the span of a pandemic Amcrest has released at 5 different webcams. Some are honestly…","fields":{"slug":"/amcrest-prostream-awc2198-usb-webcam-review/","type":"posts"},"headings":[{"value":"Another Amcrest Webcam? What changed this time?","depth":2},{"value":"Amcrest AWC2198 1080P ProStream webcam specifications","depth":2},{"value":"Installation and Setup","depth":2},{"value":"Video and Audio Quality and Performance","depth":2},{"value":"Final Thoughts","depth":2}],"frontmatter":{"title":"Comparing the Amcrest ProStream AWC2198 webcam to other 1080p models","description":"Amcrest generally puts out pretty decent quality security cameras at a reasonable price, but their webcams have been hit or miss. This review will see if the latest Amcrest ProStream model AWC2198 USB webcam gets it right.","link":null,"date":"2020-08-25T00:00:00.000Z","tags":["smart home","camera","iot","post"],"draft":null,"hide":null,"cover":{"childImageSharp":{"fluid":{"base64":"data:image/jpeg;base64,/9j/2wBDAAMCAgMCAgMDAwMEAwMEBQgFBQQEBQoHBwYIDAoMDAsKCwsNDhIQDQ4RDgsLEBYQERMUFRUVDA8XGBYUGBIUFRT/2wBDAQMEBAUEBQkFBQkUDQsNFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBT/wgARCAALABQDASIAAhEBAxEB/8QAFgABAQEAAAAAAAAAAAAAAAAABwAG/8QAFgEBAQEAAAAAAAAAAAAAAAAABAAD/9oADAMBAAIQAxAAAAHW581Q5BbMFsf/xAAZEAEBAAMBAAAAAAAAAAAAAAAFAgEDBAb/2gAIAQEAAQUCSWg4sX3DGo9lboXQErM+ZPjHQbv4dM3/AP/EABcRAQADAAAAAAAAAAAAAAAAAAIQEiL/2gAIAQMBAT8BumdR/8QAGBEAAwEBAAAAAAAAAAAAAAAAAAECESH/2gAIAQIBAT8BxS+Co//EACkQAAEDAwICCwAAAAAAAAAAAAECAxEABCEFIhIxFCNBQkRRYXKBwfD/2gAIAQEABj8Cuegr4yBtYSvt9IzTrt7ayFnqdihH2r9mnLpVgVrVzU40rNanq3j7Z0Bl09yfIcvmtPedlbriJWsnJ2moAMe81//EAB0QAQACAgIDAAAAAAAAAAAAAAERIQAxQXFRYeH/2gAIAQEAAT8hfGRkitw6JJoxUhE4B5NN8aK5GRMRCSgigaICsMDVVPmdJ6T7xXp/ddaZ7Mgbw19c/9oADAMBAAIAAwAAABDXD//EABcRAAMBAAAAAAAAAAAAAAAAAAABETH/2gAIAQMBAT8QaSsHKf/EABcRAAMBAAAAAAAAAAAAAAAAAAABETH/2gAIAQIBAT8Qd5dGQ//EABkQAQEBAQEBAAAAAAAAAAAAAAERIQAxQf/aAAgBAQABPxCBwLUcqMDd6EheAZ05SQteoEFdBDXuXUixCKU+KqrwLkjBPqq2uJ3By6Hpqum2wcfnSRwgga+d/9k=","aspectRatio":1.7777777777777777,"src":"/static/828b116bbd5216c507f96ee2be3b941c/60a5f/amcrest-prostream-awc2198-on-monitor.jpg","srcSet":"/static/828b116bbd5216c507f96ee2be3b941c/3e5eb/amcrest-prostream-awc2198-on-monitor.jpg 192w,\n/static/828b116bbd5216c507f96ee2be3b941c/2880b/amcrest-prostream-awc2198-on-monitor.jpg 384w,\n/static/828b116bbd5216c507f96ee2be3b941c/60a5f/amcrest-prostream-awc2198-on-monitor.jpg 768w,\n/static/828b116bbd5216c507f96ee2be3b941c/2f1b1/amcrest-prostream-awc2198-on-monitor.jpg 800w","srcWebp":"/static/828b116bbd5216c507f96ee2be3b941c/25278/amcrest-prostream-awc2198-on-monitor.webp","srcSetWebp":"/static/828b116bbd5216c507f96ee2be3b941c/a278a/amcrest-prostream-awc2198-on-monitor.webp 192w,\n/static/828b116bbd5216c507f96ee2be3b941c/2474b/amcrest-prostream-awc2198-on-monitor.webp 384w,\n/static/828b116bbd5216c507f96ee2be3b941c/25278/amcrest-prostream-awc2198-on-monitor.webp 768w,\n/static/828b116bbd5216c507f96ee2be3b941c/ccdb5/amcrest-prostream-awc2198-on-monitor.webp 800w","sizes":"(max-width: 768px) 100vw, 768px","presentationWidth":768,"presentationHeight":432}}}}},{"id":"8ec8adae-47b4-5bb7-9d71-5dac9fb3dbbf","body":"var _excluded = [\"components\"];\nfunction _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }\nfunction _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }\nfunction _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }\n/* @jsxRuntime classic */\n/* @jsx mdx */\n\nvar _frontmatter = {\n  \"title\": \"South Florida Test Automation Meetup presentation for Getting started with NightwatchJS\",\n  \"cover\": \"./nightwatchjs-test-automation-meetup.jpg\",\n  \"date\": \"2020-08-19T00:00:00.000Z\",\n  \"description\": \"Our local South Florida Test Automation chapter met virtually to go over the Nightwatch.js framework. It was our first virtual testing Meetup in an effort to adapt to the new normal.\",\n  \"tags\": [\"quality assurance\", \"qa\", \"software testing\", \"nightwatchjs\", \"post\"]\n};\nvar makeShortcode = function makeShortcode(name) {\n  return function MDXDefaultShortcode(props) {\n    console.warn(\"Component \" + name + \" was not imported, exported, or provided by MDXProvider as global scope\");\n    return mdx(\"div\", props);\n  };\n};\nvar Embed = makeShortcode(\"Embed\");\nvar layoutProps = {\n  _frontmatter: _frontmatter\n};\nvar MDXLayout = \"wrapper\";\nreturn function MDXContent(_ref) {\n  var components = _ref.components,\n    props = _objectWithoutProperties(_ref, _excluded);\n  return mdx(MDXLayout, _extends({}, layoutProps, props, {\n    components: components,\n    mdxType: \"MDXLayout\"\n  }), mdx(\"p\", null, \"I attend a local Meetup chapter for \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://www.meetup.com/South-Florida-Test-Automation/\"\n  }, \"South Florida Test Automation\"), \" which tries to meet about once a month. For the past year or so we've been meeting after hours in Weston, Florida on the campus of Ultimate Software. Local software testing professionals are encouraged to attend. \"), mdx(\"p\", null, \"Vlad, Philip, and Jairo manage to attract a wide range of speakers that present on things ranging from testing best practices, popular vendor tools, open source testing frameworks, and pizza \\uD83C\\uDF55.\"), mdx(\"p\", null, \"It is a great place to learn and network with fellow QA professionals and test engineers, but with COVID shutting things down we had to stop our in person meetings. This past July we decided to give a fully online presentation a go.\"), mdx(\"p\", null, \"I was asked to present on the Nightwatch.js testing framework and I feel it went really well. It was a bit awkward trying to guage audience feedback though while speaking when talking in a remote room I have to admit, but I think people got a lot of information out of it.\"), mdx(\"p\", null, \"In the beginning of the meeting we learned password protecting the meeting link is probably a good idea in the future since a contingent of folks (or bots?) decided to crash the meeting, but Philip managed to punt them before we got rolling.\"), mdx(\"p\", null, \"\\uD83C\\uDDE7\\uD83C\\uDDF7 \\uD83C\\uDF2D \\uD83D\\uDCA3\"), mdx(\"p\", null, \"Below you can find the Meetup recording for an Introduction to Nightwatch.js from 7/28/2020\"), mdx(Embed, {\n    src: \"https://www.youtube.com/embed/GkEhQAKgiDI\",\n    mdxType: \"Embed\"\n  }));\n}\n;\nMDXContent.isMDXComponent = true;","excerpt":"I attend a local Meetup chapter for  South Florida Test Automation  which tries to meet about once a month. For the past year or so we've…","fields":{"slug":"/south-florida-test-meetup-nightwatch-js/","type":"posts"},"headings":[],"frontmatter":{"title":"South Florida Test Automation Meetup presentation for Getting started with NightwatchJS","description":"Our local South Florida Test Automation chapter met virtually to go over the Nightwatch.js framework. It was our first virtual testing Meetup in an effort to adapt to the new normal.","link":null,"date":"2020-08-19T00:00:00.000Z","tags":["quality assurance","qa","software testing","nightwatchjs","post"],"draft":null,"hide":null,"cover":{"childImageSharp":{"fluid":{"base64":"data:image/jpeg;base64,/9j/2wBDAAMCAgMCAgMDAwMEAwMEBQgFBQQEBQoHBwYIDAoMDAsKCwsNDhIQDQ4RDgsLEBYQERMUFRUVDA8XGBYUGBIUFRT/2wBDAQMEBAUEBQkFBQkUDQsNFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBT/wgARCAALABQDASIAAhEBAxEB/8QAGAAAAwEBAAAAAAAAAAAAAAAAAAQFBgf/xAAUAQEAAAAAAAAAAAAAAAAAAAAC/9oADAMBAAIQAxAAAAFfj+UmhPEsR//EABwQAAICAgMAAAAAAAAAAAAAAAIDBAUAAQYTFf/aAAgBAQABBQKsh1zHci88r0+vRIuJ0bTnHIZn/8QAFhEBAQEAAAAAAAAAAAAAAAAAARAR/9oACAEDAQE/AV2f/8QAFhEBAQEAAAAAAAAAAAAAAAAAARAR/9oACAECAQE/AQyf/8QAJhAAAQMCAgsAAAAAAAAAAAAAAQIDBAAREkEFEBMUIiMxMjRRkv/aAAgBAQAGPwJlpWjI7jLfGp7Z9wtU3c4oMXHy8BKRawyrxz9VZqY+2PSXCKU44orWrqo56v/EAB0QAAICAgMBAAAAAAAAAAAAAAERACExQRBxkaH/2gAIAQEAAT8hxUUN5QNXuEbYvQYSrcAxIOnr5NnzSPHCU2ZrJcf/2gAMAwEAAgADAAAAEBfv/8QAFREBAQAAAAAAAAAAAAAAAAAAERD/2gAIAQMBAT8Qcz//xAAWEQEBAQAAAAAAAAAAAAAAAAABERD/2gAIAQIBAT8Qcy5//8QAGhABAQEBAQEBAAAAAAAAAAAAARExACFBgf/aAAgBAQABPxAeIKHNhMQq89UMlXyq0s5A5U+WtOg/ah/HX1ylpRMrJ6Izqt6SzKrude//2Q==","aspectRatio":1.7777777777777777,"src":"/static/8a18ea5fe18f6471ec8cf0e35f3546fd/596f3/nightwatchjs-test-automation-meetup.jpg","srcSet":"/static/8a18ea5fe18f6471ec8cf0e35f3546fd/3e5eb/nightwatchjs-test-automation-meetup.jpg 192w,\n/static/8a18ea5fe18f6471ec8cf0e35f3546fd/2880b/nightwatchjs-test-automation-meetup.jpg 384w,\n/static/8a18ea5fe18f6471ec8cf0e35f3546fd/596f3/nightwatchjs-test-automation-meetup.jpg 700w","srcWebp":"/static/8a18ea5fe18f6471ec8cf0e35f3546fd/64998/nightwatchjs-test-automation-meetup.webp","srcSetWebp":"/static/8a18ea5fe18f6471ec8cf0e35f3546fd/a278a/nightwatchjs-test-automation-meetup.webp 192w,\n/static/8a18ea5fe18f6471ec8cf0e35f3546fd/2474b/nightwatchjs-test-automation-meetup.webp 384w,\n/static/8a18ea5fe18f6471ec8cf0e35f3546fd/64998/nightwatchjs-test-automation-meetup.webp 700w","sizes":"(max-width: 700px) 100vw, 700px","presentationWidth":700,"presentationHeight":394}}}}},{"id":"cb264dc7-522a-523c-9761-eee03fcf4849","body":"var _excluded = [\"components\"];\nfunction _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }\nfunction _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }\nfunction _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }\n/* @jsxRuntime classic */\n/* @jsx mdx */\n\nvar _frontmatter = {\n  \"title\": \"Review of Crenova's XPE660 1080P HD LED projector\",\n  \"cover\": \"./crenova-xpe-660-review.jpg\",\n  \"date\": \"2020-08-18T00:00:00.000Z\",\n  \"description\": \"Crenova recently released a 1080p (upscaled from 1280x800) projector which makes a great companion for projecting streamed content from smart devices like an Amazon FireTV stick. In budget projectors brightness and sharpness are usually sacrificed. Let's see how it holds up in this review.\",\n  \"tags\": [\"smart home\", \"projector\", \"post\"]\n};\nvar makeShortcode = function makeShortcode(name) {\n  return function MDXDefaultShortcode(props) {\n    console.warn(\"Component \" + name + \" was not imported, exported, or provided by MDXProvider as global scope\");\n    return mdx(\"div\", props);\n  };\n};\nvar Embed = makeShortcode(\"Embed\");\nvar layoutProps = {\n  _frontmatter: _frontmatter\n};\nvar MDXLayout = \"wrapper\";\nreturn function MDXContent(_ref) {\n  var components = _ref.components,\n    props = _objectWithoutProperties(_ref, _excluded);\n  return mdx(MDXLayout, _extends({}, layoutProps, props, {\n    components: components,\n    mdxType: \"MDXLayout\"\n  }), mdx(\"h2\", {\n    \"id\": \"crenova-and-projectors\"\n  }, \"Crenova and projectors\"), mdx(\"p\", null, \"Crenova has a wide and deep selection of home theater products. When I was looking for a budget projector to do Halloween projected effects I ran across their \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://amzn.to/342N3OF\"\n  }, \"1080p-native model\"), \" which did an impressive job last Halloween season (when the world was normal and we still had holidays).\"), mdx(\"p\", null, \"Since then, I noticed they released the \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://amzn.to/2FwZ2d5\"\n  }, \"Crenova XPE600 projector\"), \" which is less expensive, has a newer design, but makes a sacrifice in native resolution at 1280x800. This resolution was fine for what I was planning on using it for as long as everything else like focus, brightness, and sound were solid. So I reviewed it in detail in both sunny day conditions, dark room, and night to see how clearly the projected video could be seen.\"), mdx(Embed, {\n    src: \"https://www.youtube.com/embed/T9V-3EUypUw\",\n    mdxType: \"Embed\"\n  }), mdx(\"h2\", {\n    \"id\": \"crenova-xpe600-specifications\"\n  }, \"Crenova XPE600 specifications\"), mdx(\"ul\", null, mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"1280x800 resolution\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"VGA (x1) and HDMI interface (x2) and USB (x2)\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"LED illuminated\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"140 watt power consumption\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"6000 lumens\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"6000:1 contrast ratio\")), mdx(\"h2\", {\n    \"id\": \"installation-and-setup\"\n  }, \"Installation and Setup\"), mdx(\"p\", null, \"The Crenova projector setup is simple. Just plug in the power cord, remove the lens cap, toggle on the back power switch and you are ready to go. The remote control will require two AAA batteries.\"), mdx(\"p\", null, \"To project content from something like an \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://amzn.to/3kSNr8s\"\n  }, \"Amazon FireTV stick\"), \" you plug the HDMI cord from the FireTV into one of the HDMI ports on the projector and the USB cord into one of the USB ports to supply power. The Crenova projector will autodetect the new video source and will project it on your screen or wall. From there, you can use the FireTV remote to navigate the menus.\"), mdx(\"p\", null, \"The one negative is there isn't a threaded tripod attachment to mount the projector to. You need to find a convenient platform or table to put it on at least 61 inches away from where you want to project.\"), mdx(\"h2\", {\n    \"id\": \"video-and-audio-quality-and-performance\"\n  }, \"Video and Audio Quality and Performance\"), mdx(\"p\", null, \"The audio quality was not bad, but it wasn't home theater quality. For a cinematic experience you'd want to use external speakers. The XPE660's speakers sound about as good as other projectors in this range. They are loud enough to hear and not muffled.\"), mdx(\"p\", null, \"The video quality, however, was very impressive for 1280x800. Text appeared very crisp and clear. I could get a good focus on the projected image. The projector does not output enough lumens to use outside during the day, but at night it looked fantastic. The projector would work great for movie night by the pool.\"), mdx(\"p\", null, \"This is not a short-throw projector so you need a good bit of distance to fill a 120\\\" projector screen. I needed to place the projector about 154-156 inches away from the screen.\"), mdx(\"h2\", {\n    \"id\": \"final-thoughts\"\n  }, \"Final Thoughts\"), mdx(\"p\", null, \"The \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://amzn.to/2FwZ2d5\"\n  }, \"Crenova XPE600 projector\"), \" would make a good budget projector for someone not needing native 1080p. Even at 1280x800 it was easy to read menus in the Alexa navigation and the video looked good. It isn't loud and is more compact than other options so it won't completely take over your room, but you do need a bit of distance between the projector and the screen to get a nice large projected image.\"));\n}\n;\nMDXContent.isMDXComponent = true;","excerpt":"Crenova and projectors Crenova has a wide and deep selection of home theater products. When I was looking for a budget projector to do…","fields":{"slug":"/crenova-xpe660-led-projector-review/","type":"posts"},"headings":[{"value":"Crenova and projectors","depth":2},{"value":"Crenova XPE600 specifications","depth":2},{"value":"Installation and Setup","depth":2},{"value":"Video and Audio Quality and Performance","depth":2},{"value":"Final Thoughts","depth":2}],"frontmatter":{"title":"Review of Crenova's XPE660 1080P HD LED projector","description":"Crenova recently released a 1080p (upscaled from 1280x800) projector which makes a great companion for projecting streamed content from smart devices like an Amazon FireTV stick. In budget projectors brightness and sharpness are usually sacrificed. Let's see how it holds up in this review.","link":null,"date":"2020-08-18T00:00:00.000Z","tags":["smart home","projector","post"],"draft":null,"hide":null,"cover":{"childImageSharp":{"fluid":{"base64":"data:image/jpeg;base64,/9j/2wBDAAMCAgMCAgMDAwMEAwMEBQgFBQQEBQoHBwYIDAoMDAsKCwsNDhIQDQ4RDgsLEBYQERMUFRUVDA8XGBYUGBIUFRT/2wBDAQMEBAUEBQkFBQkUDQsNFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBT/wgARCAALABQDASIAAhEBAxEB/8QAGQAAAgMBAAAAAAAAAAAAAAAAAAUCAwYH/8QAFgEBAQEAAAAAAAAAAAAAAAAAAwUG/9oADAMBAAIQAxAAAAFjHkl8rSPTLBv/AP/EAB0QAAIBBAMAAAAAAAAAAAAAAAMEAgAFBxMBBhL/2gAIAQEAAQUCTyMhbmOcngOVi7de3JR9oQRAKmBR2f/EABsRAAICAwEAAAAAAAAAAAAAAAECABEFFSFB/9oACAEDAQE/AVxgNNfJr18M/8QAGxEAAgEFAAAAAAAAAAAAAAAAAAIRAQUUIUH/2gAIAQIBAT8Ba4MsrGzPbtD/xAAkEAACAQEHBQEAAAAAAAAAAAABAgMABAUREiEiMTJBYXGR8P/aAAgBAQAGPwI2KOxLDd9nQJHqc7+hxWEUWbx+FOz3NvY5jp3+1HK26RmOJNFkiCsecK6RX//EABwQAAICAgMAAAAAAAAAAAAAAAERACExsUFRYf/aAAgBAQABPyGlB6nogpls9nNQtGQtbqChMH2kxZVdQTgy5kozm0lDPRP/2gAMAwEAAgADAAAAELAv/8QAHBEAAQQDAQAAAAAAAAAAAAAAAQARIUExUXGh/9oACAEDAQE/EMuSkxLUBR8W5bq//8QAHBEAAQMFAAAAAAAAAAAAAAAAAQARITFBUXHB/9oACAECAQE/ECtAgYfiFpfS/8QAHBABAAMAAwEBAAAAAAAAAAAAAQARITFRYUFx/9oACAEBAAE/EF6UAroA2wrViKoB24QoX9PW39gCgcey4AFrQA8JtCC2AOXKOowxrSRduvDwgRsAAu0//9k=","aspectRatio":1.7777777777777777,"src":"/static/8f26e353bee99c3611185677f737af95/60a5f/crenova-xpe-660-review.jpg","srcSet":"/static/8f26e353bee99c3611185677f737af95/3e5eb/crenova-xpe-660-review.jpg 192w,\n/static/8f26e353bee99c3611185677f737af95/2880b/crenova-xpe-660-review.jpg 384w,\n/static/8f26e353bee99c3611185677f737af95/60a5f/crenova-xpe-660-review.jpg 768w,\n/static/8f26e353bee99c3611185677f737af95/d9e57/crenova-xpe-660-review.jpg 1152w,\n/static/8f26e353bee99c3611185677f737af95/4b91a/crenova-xpe-660-review.jpg 1536w,\n/static/8f26e353bee99c3611185677f737af95/097fa/crenova-xpe-660-review.jpg 1920w","srcWebp":"/static/8f26e353bee99c3611185677f737af95/25278/crenova-xpe-660-review.webp","srcSetWebp":"/static/8f26e353bee99c3611185677f737af95/a278a/crenova-xpe-660-review.webp 192w,\n/static/8f26e353bee99c3611185677f737af95/2474b/crenova-xpe-660-review.webp 384w,\n/static/8f26e353bee99c3611185677f737af95/25278/crenova-xpe-660-review.webp 768w,\n/static/8f26e353bee99c3611185677f737af95/e7b8c/crenova-xpe-660-review.webp 1152w,\n/static/8f26e353bee99c3611185677f737af95/7dd62/crenova-xpe-660-review.webp 1536w,\n/static/8f26e353bee99c3611185677f737af95/30cf3/crenova-xpe-660-review.webp 1920w","sizes":"(max-width: 768px) 100vw, 768px","presentationWidth":768,"presentationHeight":432}}}}},{"id":"3758ba93-e1c9-548c-84f6-9b946c886b99","body":"var _excluded = [\"components\"];\nfunction _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }\nfunction _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }\nfunction _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }\n/* @jsxRuntime classic */\n/* @jsx mdx */\n\nvar _frontmatter = {\n  \"title\": \"Review and setup of Amcrest's AWC195-B 1080P HD webcam\",\n  \"cover\": \"./Amcrest-1080p-AWC195-B.jpg\",\n  \"date\": \"2020-05-31T00:00:00.000Z\",\n  \"description\": \"Amcrest, normally known for their security cameras, has recently released their second HD webcam, the AWC195-B to meet the demand from remote workers. Their first HD webcam had acceptable video quality, but terrible microphone. In this post I'll review their latest webcam and see if there are improvements to video and audio quality.\",\n  \"tags\": [\"smart home\", \"camera\", \"iot\", \"post\"]\n};\nvar makeShortcode = function makeShortcode(name) {\n  return function MDXDefaultShortcode(props) {\n    console.warn(\"Component \" + name + \" was not imported, exported, or provided by MDXProvider as global scope\");\n    return mdx(\"div\", props);\n  };\n};\nvar Embed = makeShortcode(\"Embed\");\nvar layoutProps = {\n  _frontmatter: _frontmatter\n};\nvar MDXLayout = \"wrapper\";\nreturn function MDXContent(_ref) {\n  var components = _ref.components,\n    props = _objectWithoutProperties(_ref, _excluded);\n  return mdx(MDXLayout, _extends({}, layoutProps, props, {\n    components: components,\n    mdxType: \"MDXLayout\"\n  }), mdx(\"h2\", {\n    \"id\": \"what-did-amcrest-change-in-the-awc195-b-webcam\"\n  }, \"What did Amcrest change in the AWC195-B webcam?\"), mdx(\"p\", null, \"Amcrest seemingly seized on the market's shortage of webcams, leveraged their knowledge of security cameras, and quickly put out the \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://www.davidmello.com/amcrest-hd-webcam-1080p-review/\"\n  }, \"AWC201-B HD webcam\"), \", but the microphone was terrible and the wide field of view distorted the image a bit too much for video chat.\"), mdx(\"p\", null, \"The \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://amzn.to/3gCyepH\"\n  }, \"AWC195-B HD webcam\"), \" is a newer release and I was curious to see what changes were made besides the obvious cosmetic ones. This webcam maintains the very affordable $50-60 price point for a 1080p camera, includes a slightly larger CMOS sensor than its predecessor, new lens, and has a stereo mic.\"), mdx(\"p\", null, \"I perform a side-by-side comparison of the AWC195-B and the AWC201-B in the review below.\"), mdx(Embed, {\n    src: \"https://www.youtube.com/embed/t-nfX4HFC74\",\n    mdxType: \"Embed\"\n  }), mdx(\"h2\", {\n    \"id\": \"amcrest-awc195-b-1080p-hd-webcam-tech-specs\"\n  }, \"Amcrest AWC195-B 1080P HD Webcam Tech Specs\"), mdx(\"ul\", null, mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"1/2.7\\\" CMOS image sensor\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"1080P Full HD (1920x1080) at 30fps\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"4.3mm lens\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"90 degree field of view\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Stereo mic\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"USB 2.0 Plug and Play\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Privacy cover\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"~6ft USB cord length\")), mdx(\"p\", null, \"As with the previous AWC201-B the specifications look pretty solid. The webcam video output smoothly records 1080p resolution at 30 frames per second. For comparison, cinematic movies are 24fps, soap operas 30fps, action cameras are 60fps. \"), mdx(\"p\", null, \"The CMOS sensor is slightly larger in the AWC195-B and should pickup enough detail for Zoom meetings, Skype, Google Hangouts, and so on especially if the space is lit well.\"), mdx(\"p\", null, \"Although the field of view is listed at 90 degrees it doesn't have the same fish eye effect from the previous webcam. The field of view isn't as wide so there wasn't obvious distortion around the edges.\"), mdx(\"p\", null, \"Lastly, Amcrest still includes a physical privacy cover to protect users against accidentally recording themselves and ending up in a viral video, but did not include a tripod in the box this time. You can still mount it on your monitor or a self-provided threaded tripod.\"), mdx(\"h2\", {\n    \"id\": \"installation-and-setup\"\n  }, \"Installation and Setup\"), mdx(\"p\", null, \"The camera is literally plug and play over USB 2.0 so just plug the camera into a free USB port on your computer. It will appear as \", mdx(\"em\", {\n    parentName: \"p\"\n  }, \"USB Camera\"), \" in \", mdx(\"em\", {\n    parentName: \"p\"\n  }, \"Sound Video and Game Controllers\"), \". All adjustments to video or audio settings would be done through your streaming or conferencing software.\"), mdx(\"p\", null, \"The Amcrest webcam will appear as a video source you can select in Microsoft Teams, Zoom, Hangouts, etc., and it will start streaming video automatically once selected. If you download apps like OBS Studio you can adjust the color settings, brightness, and contrast.\"), mdx(\"h2\", {\n    \"id\": \"video-and-audio-quality-and-performance\"\n  }, \"Video and Audio Quality and Performance\"), mdx(\"p\", null, \"The most obvious differences you can see in the \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://youtu.be/t-nfX4HFC74\"\n  }, \"Amcrest AWC195-B webcam review\"), \" embedded at the top of the article are the sound quality of the microphone, color palette, and field of view.\"), mdx(\"p\", null, \"The 1080p video is slightly improved over the previous model. The AWC195-B colors were less washed out compared to the AWC201-B, but nowhere near the DSLR color depth. The field of view was similar to my DSLR with a 24mm lens on.\"), mdx(\"p\", null, \"I did find the colors get weird on the edges of the frame. If I moved my body to the edges of the shot my skin turned green.\"), mdx(\"p\", null, \"The audio quality was an improvement from before, but a bit too treble heavy. The audio is a bit tinny as a result. If you want to more accurately reproduce your voice you can add an \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://amzn.to/3alUM9O\"\n  }, \"external microphone\"), \", but this should work OK for most meetings.\"), mdx(\"h2\", {\n    \"id\": \"final-thoughts\"\n  }, \"Final Thoughts\"), mdx(\"p\", null, \"Amcrest improved over AWC201-B, but the \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://amzn.to/3gCyepH\"\n  }, \"AWC195-B webcam\"), \" still has its flaws. The microphone is no longer a deal breaker, but could still use improvement and the colors are still a bit off, especially nearing on the edges of the frame.\"), mdx(\"p\", null, \"Those complaints aside, for $50-60 dollars it does give you a pretty decent 1080p HD image. The \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://amzn.to/3ewG6YV\"\n  }, \"Logitech Brio\"), \" has better reviewed microphone, but is also several times more expensive.\"), mdx(\"p\", null, \"If you do go the external microphone route I've been happy with the \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://amzn.to/3alUM9O\"\n  }, \"Samson USB Meteor mic\"), \".\"));\n}\n;\nMDXContent.isMDXComponent = true;","excerpt":"What did Amcrest change in the AWC195-B webcam? Amcrest seemingly seized on the market's shortage of webcams, leveraged their knowledge of…","fields":{"slug":"/amcrest-awc195-b-hd-webcam-1080p-review/","type":"posts"},"headings":[{"value":"What did Amcrest change in the AWC195-B webcam?","depth":2},{"value":"Amcrest AWC195-B 1080P HD Webcam Tech Specs","depth":2},{"value":"Installation and Setup","depth":2},{"value":"Video and Audio Quality and Performance","depth":2},{"value":"Final Thoughts","depth":2}],"frontmatter":{"title":"Review and setup of Amcrest's AWC195-B 1080P HD webcam","description":"Amcrest, normally known for their security cameras, has recently released their second HD webcam, the AWC195-B to meet the demand from remote workers. Their first HD webcam had acceptable video quality, but terrible microphone. In this post I'll review their latest webcam and see if there are improvements to video and audio quality.","link":null,"date":"2020-05-31T00:00:00.000Z","tags":["smart home","camera","iot","post"],"draft":null,"hide":null,"cover":{"childImageSharp":{"fluid":{"base64":"data:image/jpeg;base64,/9j/2wBDAAMCAgMCAgMDAwMEAwMEBQgFBQQEBQoHBwYIDAoMDAsKCwsNDhIQDQ4RDgsLEBYQERMUFRUVDA8XGBYUGBIUFRT/2wBDAQMEBAUEBQkFBQkUDQsNFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBT/wgARCAALABQDASIAAhEBAxEB/8QAGAAAAgMAAAAAAAAAAAAAAAAAAAYFBwj/xAAVAQEBAAAAAAAAAAAAAAAAAAADBP/aAAwDAQACEAMQAAABblfP1mz0yYvCB//EABoQAAIDAQEAAAAAAAAAAAAAAAQFAgMGEQH/2gAIAQEAAQUCP0MF8I60rQkOyW9Bz5wa0PXcpGY+8t//xAAYEQADAQEAAAAAAAAAAAAAAAAAARECEv/aAAgBAwEBPwFx6cOj/8QAGBEAAgMAAAAAAAAAAAAAAAAAACEBERL/2gAIAQIBAT8BpMzB/8QAIhAAAgEEAQQDAAAAAAAAAAAAAQIDAAQRIhITIUFhMTKB/9oACAEBAAY/AnuFR3t4W6cjmMqM+m+KVLDhDAPtJIhbvRWORGTHYrC1SJdXMkyRtxVCdR+VZhEjTkm2EG2vmkxgZXwPZr//xAAcEAADAQACAwAAAAAAAAAAAAABESEAMWFBUYH/2gAIAQEAAT8hoUym+HUHNwJUg6kgm8KUjcnr28f2VLCCggmWWDj9kX7oRQKAzr//2gAMAwEAAgADAAAAEIw//8QAGBEAAwEBAAAAAAAAAAAAAAAAAAERMbH/2gAIAQMBAT8QTAq8RR//xAAYEQACAwAAAAAAAAAAAAAAAAAAQQERIf/aAAgBAgEBPxDEGKEf/8QAGRABAQEBAQEAAAAAAAAAAAAAAREhADFB/9oACAEBAAE/EAnYFtAYdKlRvg3sA43Cw06ATK7HLwesW/kH1aAOvnPl7YnBMWfZX6vB/wB1ohRC39tN33ra6zBKKB7nf//Z","aspectRatio":1.7777777777777777,"src":"/static/453256d5317be73561564580d332c26c/596f3/Amcrest-1080p-AWC195-B.jpg","srcSet":"/static/453256d5317be73561564580d332c26c/3e5eb/Amcrest-1080p-AWC195-B.jpg 192w,\n/static/453256d5317be73561564580d332c26c/2880b/Amcrest-1080p-AWC195-B.jpg 384w,\n/static/453256d5317be73561564580d332c26c/596f3/Amcrest-1080p-AWC195-B.jpg 700w","srcWebp":"/static/453256d5317be73561564580d332c26c/64998/Amcrest-1080p-AWC195-B.webp","srcSetWebp":"/static/453256d5317be73561564580d332c26c/a278a/Amcrest-1080p-AWC195-B.webp 192w,\n/static/453256d5317be73561564580d332c26c/2474b/Amcrest-1080p-AWC195-B.webp 384w,\n/static/453256d5317be73561564580d332c26c/64998/Amcrest-1080p-AWC195-B.webp 700w","sizes":"(max-width: 700px) 100vw, 700px","presentationWidth":700,"presentationHeight":394}}}}},{"id":"231a51bf-7009-5b04-872c-1482953085f6","body":"var _excluded = [\"components\"];\nfunction _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }\nfunction _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }\nfunction _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }\n/* @jsxRuntime classic */\n/* @jsx mdx */\n\nvar _frontmatter = {\n  \"title\": \"Writing maintainable tests with Nightwatch.js page objects and commands\",\n  \"cover\": \"./nightwatch-page-object-model-testing.jpg\",\n  \"date\": \"2020-04-26T00:00:00.000Z\",\n  \"description\": \"Using page objects and commands for repetitive selectors and test code allows you to write maintainable automated test cases. Learn how in this Nightwatch automation tutorial.\",\n  \"tags\": [\"quality assurance\", \"software testing\", \"nightwatchjs\", \"post\"]\n};\nvar makeShortcode = function makeShortcode(name) {\n  return function MDXDefaultShortcode(props) {\n    console.warn(\"Component \" + name + \" was not imported, exported, or provided by MDXProvider as global scope\");\n    return mdx(\"div\", props);\n  };\n};\nvar Embed = makeShortcode(\"Embed\");\nvar layoutProps = {\n  _frontmatter: _frontmatter\n};\nvar MDXLayout = \"wrapper\";\nreturn function MDXContent(_ref) {\n  var components = _ref.components,\n    props = _objectWithoutProperties(_ref, _excluded);\n  return mdx(MDXLayout, _extends({}, layoutProps, props, {\n    components: components,\n    mdxType: \"MDXLayout\"\n  }), mdx(\"h2\", {\n    \"id\": \"nightwatchjs-page-object-model-and-commands-api\"\n  }, \"Nightwatch.js page object model and commands API\"), mdx(\"p\", null, \"The page object model in test automation allows you as a test engineer to write more readable and maintainable tests by placing the CSS or XPath selectors for the DOM elements into a single organized object. \"), mdx(\"blockquote\", null, mdx(\"p\", {\n    parentName: \"blockquote\"\n  }, \"The page object centralizes common selectors and logic for portions of the UI relevant to your tests. This page object can expose functions that hold repetitive logic so you don't need to repeat the same code all over your test classes.\")), mdx(\"p\", null, \"Tests become more readable because\"), mdx(\"ul\", null, mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"There is less code overall (less repetitive code)\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Selectors can change from something like \", mdx(\"strong\", {\n    parentName: \"li\"\n  }, \".click('div select option:checked')\"), \" to \", mdx(\"strong\", {\n    parentName: \"li\"\n  }, \".click('@friendlyName')\")), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"If the UI changes you only need to make the change in one place to fix all your tests\")), mdx(\"p\", null, \"Well-written test automation frameworks like Nightwatch.js support this using their Page Object API which we will cover in this tutorial.\"), mdx(Embed, {\n    src: \"https://www.youtube.com/embed/gJvAbGYP-H8\",\n    mdxType: \"Embed\"\n  }), mdx(\"h2\", {\n    \"id\": \"refactoring-tests-to-use-the-page-object-model\"\n  }, \"Refactoring tests to use the page object model\"), mdx(\"p\", null, \"The example software under test, SUT, in this course is a simple calculator. It supports addition, subtraction, multiplication, and division of two integers selectable through drop down select elements. The integer range is 0 to 5. Let's also assume for example sake say we need to test this at the UI layer.\"), mdx(\"span\", {\n    \"className\": \"gatsby-resp-image-wrapper\",\n    \"style\": {\n      \"position\": \"relative\",\n      \"display\": \"block\",\n      \"marginLeft\": \"auto\",\n      \"marginRight\": \"auto\",\n      \"maxWidth\": \"500px\"\n    }\n  }, \"\\n      \", mdx(\"span\", {\n    parentName: \"span\",\n    \"className\": \"gatsby-resp-image-background-image\",\n    \"style\": {\n      \"paddingBottom\": \"47.80487804878049%\",\n      \"position\": \"relative\",\n      \"bottom\": \"0\",\n      \"left\": \"0\",\n      \"backgroundImage\": \"url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAKCAYAAAC0VX7mAAAACXBIWXMAABJ0AAASdAHeZh94AAABXUlEQVQoz51S207CQBDt//+IUMoLkJL64CWACQgK0S29LjQUX2ixoC0PvRy3xa0ViYlOMpnZnZkzc2ZXIIRgNn1Ev9/H5GGMTruF3m0Pw/sxPM+Ds1xiNBpCM0wQ8oTpbIrxZAKb2siyDLlwm4ugKAqkhgip2YTclVGvXUCs1dFVLqGqKm6uryCKIospkOUOWu02GpKEweCuBPsGmCRJGUg/A2maIYljJEnK/PRYUCnikp25E3TdgMHo2LYNd/0CyzTwTNRiOsdZwaIUmjZnvsMaomjAlU9XpS7gD1Kd6BSoBKxSPpdU+Jw2jsxPgatWME0LlmWB0gV0XYfrrrBau1gwqnmMMkvZOgxdg8HyFstjnm1TzJklKkGw239NGIURoijC4XBAGIZ4Z8rPcfEwSaFVn5/5Xb7PEnCz2SAIAuxYF9/34Pk+Xrdb7Pdv+M9+hZ9/KStf8XS3vynP/QDkNvjp5dBPDwAAAABJRU5ErkJggg==')\",\n      \"backgroundSize\": \"cover\",\n      \"display\": \"block\"\n    }\n  }), \"\\n  \", mdx(\"img\", {\n    parentName: \"span\",\n    \"className\": \"gatsby-resp-image-image\",\n    \"alt\": \"Calculator\",\n    \"title\": \"Calculator\",\n    \"src\": \"/static/3f721583bf04f7011123ff7ca89014c2/c6e3d/mello-calculator.png\",\n    \"srcSet\": [\"/static/3f721583bf04f7011123ff7ca89014c2/ad4a5/mello-calculator.png 205w\", \"/static/3f721583bf04f7011123ff7ca89014c2/74ab4/mello-calculator.png 410w\", \"/static/3f721583bf04f7011123ff7ca89014c2/c6e3d/mello-calculator.png 500w\"],\n    \"sizes\": \"(max-width: 500px) 100vw, 500px\",\n    \"style\": {\n      \"width\": \"100%\",\n      \"height\": \"100%\",\n      \"margin\": \"0\",\n      \"verticalAlign\": \"middle\",\n      \"position\": \"absolute\",\n      \"top\": \"0\",\n      \"left\": \"0\"\n    },\n    \"loading\": \"lazy\"\n  }), \"\\n    \"), mdx(\"p\", null, \"A basic automated test for this might be to test adding 2+2=4 and look like the example below.\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-js\"\n  }, \"'two plus two is four': function (browser) {\\n        browser\\n            .url('http://localhost:3000')\\n            .setValue('#numList1', 2)\\n            .setValue('#operatorList', '+')\\n            .setValue('#numList2', 2)\\n            .click('#submit')\\n            .expect.element('#result').text.to.equal('4');\\n    }\\n\")), mdx(\"p\", null, \"One can imagine if we were to exhaustively test the calculator we could have 144 tests (6 integers \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"*\"), \" 6 integers \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"*\"), \" 4 math operations) plus any other business or UI requirements outside basic math. \"), mdx(\"p\", null, \"This would result in a large amount of tests with the same hardcoded CSS selectors and value setting logic.\"), mdx(\"p\", null, \"If the page interface changed in the future we'd have a test maintenance nightmare \\uD83D\\uDE30\"), mdx(\"h3\", {\n    \"id\": \"lets-refactor-to-use-page-objects\"\n  }, \"Let's refactor to use page objects\"), mdx(\"p\", null, \"Leveraging the Nightwatch Page Object API we can refactor the above test case to this example below.\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-js\"\n  }, \"'two plus two is four': function (browser) {\\n        let calcPage = browser.page.calculator();\\n\\n        calcPage\\n            .setValue('@firstNumber', 2)\\n            .setValue('@operator', '+')\\n            .setValue('@secondNumber', 2)\\n            .click('@submit')\\n            .expect.element('@result').text.to.equal('4');\\n    }\\n\")), mdx(\"p\", null, \"The CSS DOM selectors are moved to the page object called calculator.js. The CSS selectors in the test are replaced using \", mdx(\"em\", {\n    parentName: \"p\"\n  }, \"@elementNameHere\"), \" placeholders/pointers to the name of the property in the element collection and Nightwatch resolves it to the CSS selector defined in the page object module when the test runs.\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-js\"\n  }, \"module.exports = {\\n    url: 'http://localhost:3000',\\n    elements: {\\n        firstNumber: '#numList1',\\n        secondNumber: '#numList2',\\n        operator: '#operatorList',\\n        result: '#result',\\n        submit: '#submit'\\n    }\\n}\\n\")), mdx(\"p\", null, \"So now if the selectors on the page ever change you can just make the change in one place, the page object, calculator.js.\"), mdx(\"p\", null, \"This is better, but we still have a lot of repetitive logic here for setting the value...\"), mdx(\"h3\", {\n    \"id\": \"lets-refactor-to-use-page-object-commands\"\n  }, \"Let's refactor to use page object commands\"), mdx(\"p\", null, \"We can make the tests use less repetitive code by refactoring it into a command in the page object. \"), mdx(\"p\", null, \"As a bonus, by naming it something descriptive like \", mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"add(...)\"), \" it makes the intention of the test more understandable instead of a series of clicks and setValue.\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-js\"\n  }, \"'two plus two is four': function (browser) {\\n        browser.page.calculator()\\n            .add(2, 2)\\n            .expect.element('@result').text.to.equal('4');\\n    }\\n\")), mdx(\"p\", null, \"Considerably more readable and descriptive! \\uD83D\\uDE0C\"), mdx(\"p\", null, \"The logic for \", mdx(\"strong\", {\n    parentName: \"p\"\n  }, \".add(...)\"), \" gets added to the calculator page object and the object containing the custom commands gets wired into the page object by adding it to the \", mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"commands:\"), \" property as shown toward the bottom.\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-js\"\n  }, \"const calculatorCommands = {\\n    add: function (n1, n2) {\\n        return this.fillAndSubmit(n1, n2, '+');\\n    },\\n    fillAndSubmit: function (n1, n2, op) {\\n        return this\\n            .setValue('@operator', op)\\n            .setValue('@firstNumber', n1)\\n            .setValue('@secondNumber', n2)\\n            .click('@submit');\\n    }\\n    \\n...\\n\\nmodule.exports = {\\n    url: 'http://localhost:3000',\\n    commands: [calculatorCommands],\\n    elements: {\\n    \\n    ...\\n\")), mdx(\"p\", null, \"Moving the common logic to one place instead of scattered in all your test cases has the same advantage of selectors if the functionality of the page changes in the future. For example, maybe there is navigation to the page changes or we change from a drop down to a text box. Fixing the tests only has to be done in one place.\"), mdx(\"h3\", {\n    \"id\": \"further-refactoring\"\n  }, \"Further refactoring?\"), mdx(\"p\", null, \"So now we have something readable and maintainable like these series of tests.\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-js\"\n  }, \"    '0-5=-5': function (browser) {\\n        browser.page.calculator()\\n            .subtract(0, 5)\\n            .expect.element('@result')\\n            .text.to.equal('-5');\\n    },\\n    '2-2=0': function (browser) {\\n        browser.page.calculator()\\n            .subtract(2, 2)\\n            .expect.element('@result')\\n            .text.to.equal('0');\\n    },\\n    '5*2=10': function (browser) {\\n        browser.page.calculator()\\n            .multiply(5, 2)\\n            .expect.element('@result')\\n            .text.to.equal('10');\\n    },\\n    '5 divided by 0 is Infinity': function (browser) {\\n        browser.page.calculator()\\n            .divide(5, 0)\\n            .expect.element('@result')\\n            .text.to.equal('Infinity');\\n    }\\n\")), mdx(\"p\", null, \"You could further optimize as a matter of preference to something like the below example too.\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-js\"\n  }, \"    '0-5=-5': function (browser) {\\n        browser.page.calculator()\\n            .subtracting(5).from(0).equals(5)\\n    },\\n    '5 divided by 0 is Infinity': function (browser) {\\n        browser.page.calculator()\\n            .dividing(5).by(0).equals('Infinity')\\n    }\\n\")), mdx(\"h2\", {\n    \"id\": \"configuring-nightwatchjs-to-use-page-objects\"\n  }, \"Configuring Nightwatch.js to use page objects\"), mdx(\"p\", null, \"To allow Nightwatch to find your page objects you need to add the page_objects_path to your nightwatch.json file. My example puts it in \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"./page-objects/\"), \" and my tests themselves are in \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"./tests/\")), mdx(\"p\", null, \"nightwatch.json\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-json\"\n  }, \"{\\n    \\\"src_folders\\\": [\\\"test\\\"],\\n    \\\"page_objects_path\\\": \\\"page-objects\\\",\\n\\n    \\\"selenium\\\": {\\n        \\\"start_process\\\": false\\n    },\\n\\n    \\\"webdriver\\\": {\\n        \\\"start_process\\\": true,\\n        \\\"server_path\\\": \\\"node_modules/chromedriver/lib/chromedriver/chromedriver\\\",\\n        \\\"port\\\": 9515\\n    },\\n\\n    \\\"test_settings\\\": {\\n        \\\"default\\\": {\\n            \\\"desiredCapabilities\\\": {\\n                \\\"browserName\\\": \\\"chrome\\\"\\n            }\\n        }\\n    }\\n}\\n\")), mdx(\"p\", null, \"Nightwatch expects the page object at that path to be a .js file. The filename you give it will be how it is referenced in your test through the Nightwatch browser object passed through your tests.\"), mdx(\"p\", null, \"For example, you can access the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"\\\\page-objects\\\\calculator.js\"), \" page object in your test with syntax\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-js\"\n  }, \"'0-5=-5': function (browser) {\\n        browser.page.calculator().etc.etc;\\n}\\n\")), mdx(\"p\", null, \"I've posted the entire example for testing my calculator application on my GitHub\\n\\uD83D\\uDC49 \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://github.com/reallymello/nightwatchTutorials/tree/master/dropDowns\"\n  }, \"Nightwatch.js page objects and drop down select elements tutorial\")), mdx(\"p\", null, \"If you are a Nightwatch beginner and haven't seen my introduction on setting up your Nightwatch project and running tests you'll enjoy the tutorial below\"), mdx(Embed, {\n    src: \"https://www.youtube.com/embed/6Ufg6pPNVTs\",\n    mdxType: \"Embed\"\n  }), mdx(\"p\", null, \"If you have any questions or comments please reach out through my social links below \\uD83D\\uDC47\"));\n}\n;\nMDXContent.isMDXComponent = true;","excerpt":"Nightwatch.js page object model and commands API The page object model in test automation allows you as a test engineer to write more…","fields":{"slug":"/nightwatch-page-object-model-with-commands/","type":"posts"},"headings":[{"value":"Nightwatch.js page object model and commands API","depth":2},{"value":"Refactoring tests to use the page object model","depth":2},{"value":"Let's refactor to use page objects","depth":3},{"value":"Let's refactor to use page object commands","depth":3},{"value":"Further refactoring?","depth":3},{"value":"Configuring Nightwatch.js to use page objects","depth":2}],"frontmatter":{"title":"Writing maintainable tests with Nightwatch.js page objects and commands","description":"Using page objects and commands for repetitive selectors and test code allows you to write maintainable automated test cases. Learn how in this Nightwatch automation tutorial.","link":null,"date":"2020-04-26T00:00:00.000Z","tags":["quality assurance","software testing","nightwatchjs","post"],"draft":null,"hide":null,"cover":{"childImageSharp":{"fluid":{"base64":"data:image/jpeg;base64,/9j/2wBDAAMCAgMCAgMDAwMEAwMEBQgFBQQEBQoHBwYIDAoMDAsKCwsNDhIQDQ4RDgsLEBYQERMUFRUVDA8XGBYUGBIUFRT/2wBDAQMEBAUEBQkFBQkUDQsNFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBT/wgARCAALABQDASIAAhEBAxEB/8QAGAAAAwEBAAAAAAAAAAAAAAAAAAYHAgX/xAAUAQEAAAAAAAAAAAAAAAAAAAAE/9oADAMBAAIQAxAAAAGAdTDgJEXGMQf/xAAbEAACAwADAAAAAAAAAAAAAAADBAECBRESIf/aAAgBAQABBQJHPM6uDEtcV+e63mMGIkzKooP/AP/EABYRAQEBAAAAAAAAAAAAAAAAAAABEf/aAAgBAwEBPwGNf//EABcRAQEBAQAAAAAAAAAAAAAAAAEAAhH/2gAIAQIBAT8B08Y2X//EACIQAAEDAwMFAAAAAAAAAAAAAAIAAREDEiExQVEEIjKhwf/aAAgBAQAGPwIqoWiI4a99fSYqnUiBO3gwTE8p7tVRjk1EYx8R9u77r//EABwQAQACAgMBAAAAAAAAAAAAAAEAESExQVFhcf/aAAgBAQABPyEOOOG9cQbs7ST0IxmlpmfSLZT7FTFDD6wSFfZ2z//aAAwDAQACAAMAAAAQ4w//xAAWEQEBAQAAAAAAAAAAAAAAAAABIQD/2gAIAQMBAT8QoF1Xf//EABcRAQEBAQAAAAAAAAAAAAAAAAERAEH/2gAIAQIBAT8QoAd0ZDf/xAAZEAEBAQEBAQAAAAAAAAAAAAABEQAhMcH/2gAIAQEAAT8Q70xgR9BrnhVCp3exXMQEKeUGFFzaqusSIyaqIoQs5zkjRATq0zTw+lv/2Q==","aspectRatio":1.761467889908257,"src":"/static/66f0a129b389ddbfb6f2d718b126bc76/60a5f/nightwatch-page-object-model-testing.jpg","srcSet":"/static/66f0a129b389ddbfb6f2d718b126bc76/3e5eb/nightwatch-page-object-model-testing.jpg 192w,\n/static/66f0a129b389ddbfb6f2d718b126bc76/2880b/nightwatch-page-object-model-testing.jpg 384w,\n/static/66f0a129b389ddbfb6f2d718b126bc76/60a5f/nightwatch-page-object-model-testing.jpg 768w,\n/static/66f0a129b389ddbfb6f2d718b126bc76/72de9/nightwatch-page-object-model-testing.jpg 837w","srcWebp":"/static/66f0a129b389ddbfb6f2d718b126bc76/25278/nightwatch-page-object-model-testing.webp","srcSetWebp":"/static/66f0a129b389ddbfb6f2d718b126bc76/a278a/nightwatch-page-object-model-testing.webp 192w,\n/static/66f0a129b389ddbfb6f2d718b126bc76/2474b/nightwatch-page-object-model-testing.webp 384w,\n/static/66f0a129b389ddbfb6f2d718b126bc76/25278/nightwatch-page-object-model-testing.webp 768w,\n/static/66f0a129b389ddbfb6f2d718b126bc76/22d12/nightwatch-page-object-model-testing.webp 837w","sizes":"(max-width: 768px) 100vw, 768px","presentationWidth":768,"presentationHeight":434}}}}},{"id":"9a3ad948-e460-58d4-a9f7-8c14c96f1989","body":"var _excluded = [\"components\"];\nfunction _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }\nfunction _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }\nfunction _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }\n/* @jsxRuntime classic */\n/* @jsx mdx */\n\nvar _frontmatter = {\n  \"title\": \"Review and setup of Amcrest's 1080P HD webcam\",\n  \"cover\": \"./amcrest-hd-webcam-AWC201-B.jpg\",\n  \"date\": \"2020-04-20T00:00:00.000Z\",\n  \"description\": \"With so many people working from home, Amcrest, normally known for their security cameras, has entered the webcam space. Let's see how their first 1080p webcam performs in this review.\",\n  \"tags\": [\"smart home\", \"camera\", \"iot\", \"post\"]\n};\nvar makeShortcode = function makeShortcode(name) {\n  return function MDXDefaultShortcode(props) {\n    console.warn(\"Component \" + name + \" was not imported, exported, or provided by MDXProvider as global scope\");\n    return mdx(\"div\", props);\n  };\n};\nvar Embed = makeShortcode(\"Embed\");\nvar layoutProps = {\n  _frontmatter: _frontmatter\n};\nvar MDXLayout = \"wrapper\";\nreturn function MDXContent(_ref) {\n  var components = _ref.components,\n    props = _objectWithoutProperties(_ref, _excluded);\n  return mdx(MDXLayout, _extends({}, layoutProps, props, {\n    components: components,\n    mdxType: \"MDXLayout\"\n  }), mdx(\"h2\", {\n    \"id\": \"can-amcrest-leverage-its-security-camera-expertise-for-webcams\"\n  }, \"Can Amcrest leverage it's security camera expertise for webcam's?\"), mdx(\"p\", null, \"Amcrest seems to have found an opportunity to leverage their knowledge in the security camera space to produce an affordable USB webcam to fill the need of video conferencing for remote workers during the coronavirus lockdown.\"), mdx(\"p\", null, \"Primarily Amcrest is known for their affordable IP network security cameras. They are the first camera manufacturer I used that had the right balance between quality, price, and longevity. So it seems like they'd be able to repurpose some of their core competency in this area to produce a 1080p webcam at their $50-60 dollar price point. So let's take a closer look at the AWC201-B model of the \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://amzn.to/3ey6Yb1\"\n  }, \"Amcrest 1080P HD webcam\"), \".\"), mdx(Embed, {\n    src: \"https://www.youtube.com/embed/-3eOhLdu9u8\",\n    mdxType: \"Embed\"\n  }), mdx(\"h2\", {\n    \"id\": \"amcrest-1080p-hd-webcam-tech-specs\"\n  }, \"Amcrest 1080P HD Webcam Tech Specs\"), mdx(\"ul\", null, mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"1/3\\\" Sony CMOS image sensor\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"1080P Full HD (1920x1080) at 30fps\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"4mm lens\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"70 degree field of view\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"High sensitivity mic\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"USB 2.0 Plug and Play\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Privacy cover\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"6ft USB cord length\")), mdx(\"p\", null, \"On paper the specifications look pretty solid. The webcam video output should look nice and smooth at a 1080p resolution at 30 frames per second. For comparison, cinematic movies are 24fps, soap operas 30fps, action cameras are 60fps. \"), mdx(\"p\", null, \"The CMOS sensor should be large enough to pickup enough detail for Zoom meetings, Skype, Google Hangouts, and so on especially if the space is lit well.\"), mdx(\"p\", null, \"The lens gives you a wide field of view which will fit a lot in frame though it will lose some detail and distort around the edges (fisheye effect).\"), mdx(\"p\", null, \"Lastly, they've thought to add a physical privacy cover to protect users against accidentally leaving the web camera on as you walk to you morning meeting in your boxers or to protect against the potential for hackers spying your video feed in a worst case scenario.\"), mdx(\"h2\", {\n    \"id\": \"installation-and-setup\"\n  }, \"Installation and Setup\"), mdx(\"p\", null, \"The camera is literally plug and play over USB 2.0 so just plug the camera into a free USB port on your computer. It will appear as a \", mdx(\"em\", {\n    parentName: \"p\"\n  }, \"HD Webcam USB\"), \" in \", mdx(\"em\", {\n    parentName: \"p\"\n  }, \"Sound Video and Game Controllers\"), \". You don't get any kind of additional software with the camera for adjusting the image settings or microphone. You are dependent on whatever conferencing or video capture software you are using for that as a result.\"), mdx(\"p\", null, \"In your video conferencing software the Amcrest webcam will appear as a video source you can select and it will start streaming video automatically once selected. If you download apps like OBS Studio you can adjust the color settings, brightness, and contrast.\"), mdx(\"h2\", {\n    \"id\": \"video-and-audio-quality-and-performance\"\n  }, \"Video and Audio Quality and Performance\"), mdx(\"p\", null, \"I do a side-by-side comparison of the video and audio quality in the \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://youtu.be/-3eOhLdu9u8\"\n  }, \"Amcrest webcam review\"), \" embedded at the top of the article if you would like to see and hear first hand how this webcam compares to a DSLR with dedicated external microphone.\"), mdx(\"p\", null, \"The 1080p video was acceptable. The smaller CMOS sensor was grainy in lower light, but in a well lit room the video looked clear. The colors were washed out compared to the DSLR and the 70 degree field of view was able to capture a good portion of room.\"), mdx(\"p\", null, \"The audio quality was not as good. The audio sounded highly compressed, muffled, and distant. You'd have to pair this with an \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://amzn.to/3alUM9O\"\n  }, \"external microphone\"), \" to be able to be heard clearly in video conferencing. Across the webcam space it does tend to seem like microphones tend to be where they fall short.\"), mdx(\"h2\", {\n    \"id\": \"final-thoughts\"\n  }, \"Final Thoughts\"), mdx(\"p\", null, \"The video quality is decent especially at the $50 price point, but you'd have to add in the cost of an external microphone to make the Amcrest webcam useable in most situations. The \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://amzn.to/3ewG6YV\"\n  }, \"Logitech Brio\"), \" has better reviewed microphone, but is also several times more expensive.\"), mdx(\"p\", null, \"If you do go the external microphone route I've been happy with the \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://amzn.to/3alUM9O\"\n  }, \"Samson USB Meteor mic\"), \".\"));\n}\n;\nMDXContent.isMDXComponent = true;","excerpt":"Can Amcrest leverage it's security camera expertise for webcam's? Amcrest seems to have found an opportunity to leverage their knowledge in…","fields":{"slug":"/amcrest-hd-webcam-1080p-review/","type":"posts"},"headings":[{"value":"Can Amcrest leverage it's security camera expertise for webcam's?","depth":2},{"value":"Amcrest 1080P HD Webcam Tech Specs","depth":2},{"value":"Installation and Setup","depth":2},{"value":"Video and Audio Quality and Performance","depth":2},{"value":"Final Thoughts","depth":2}],"frontmatter":{"title":"Review and setup of Amcrest's 1080P HD webcam","description":"With so many people working from home, Amcrest, normally known for their security cameras, has entered the webcam space. Let's see how their first 1080p webcam performs in this review.","link":null,"date":"2020-04-20T00:00:00.000Z","tags":["smart home","camera","iot","post"],"draft":null,"hide":null,"cover":{"childImageSharp":{"fluid":{"base64":"data:image/jpeg;base64,/9j/2wBDAAMCAgMCAgMDAwMEAwMEBQgFBQQEBQoHBwYIDAoMDAsKCwsNDhIQDQ4RDgsLEBYQERMUFRUVDA8XGBYUGBIUFRT/2wBDAQMEBAUEBQkFBQkUDQsNFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBT/wgARCAAMABQDASIAAhEBAxEB/8QAGAAAAgMAAAAAAAAAAAAAAAAAAAYEBQf/xAAWAQEBAQAAAAAAAAAAAAAAAAADAQL/2gAMAwEAAhADEAAAAaVVis5Nlg4k3//EABoQAAIDAQEAAAAAAAAAAAAAAAIEAQMFBhT/2gAIAQEAAQUC63VlJjR19RBIQkB5qfZNzR6DdGcDwf/EABoRAAEFAQAAAAAAAAAAAAAAABEAAQIDMRL/2gAIAQMBAT8BewiMsTcDF//EABoRAAICAwAAAAAAAAAAAAAAAAABAjEREiL/2gAIAQIBAT8B1x0rG5J2f//EACQQAAEDAwMEAwAAAAAAAAAAAAECAxEAEiEEMUETFCIjUWLw/9oACAEBAAY/AmC0EqcdbtN4lO/P7iu1eflC/LpNiAfsaF2++a1evf8Ac8wUhsLykTzFL0j8LbKCqeZj5q9a1gjGIr//xAAeEAEAAgICAwEAAAAAAAAAAAABESEAMVFhQXGBsf/aAAgBAQABPyGPdShqwj8wpfSbtnoHjz6wzowUo3kLS+CV9E7y3VkNScVDESqAOTP/2gAMAwEAAgADAAAAECsv/8QAGBEBAQADAAAAAAAAAAAAAAAAAQBRoeH/2gAIAQMBAT8QQKXCaC78v//EABYRAQEBAAAAAAAAAAAAAAAAAAEAIf/aAAgBAgEBPxA3CKCf/8QAGhABAQADAQEAAAAAAAAAAAAAAREAITFBUf/aAAgBAQABPxBDeikIWD1vw00olTnTFTgFiIIsHQF9gLUUDd4+1AiBGnipSXo6yxkfkkdr0xq+FICR1ffuf//Z","aspectRatio":1.6271186440677967,"src":"/static/744fdd0b7590c2e300f8e27da79a01d6/0e005/amcrest-hd-webcam-AWC201-B.jpg","srcSet":"/static/744fdd0b7590c2e300f8e27da79a01d6/3e5eb/amcrest-hd-webcam-AWC201-B.jpg 192w,\n/static/744fdd0b7590c2e300f8e27da79a01d6/2880b/amcrest-hd-webcam-AWC201-B.jpg 384w,\n/static/744fdd0b7590c2e300f8e27da79a01d6/0e005/amcrest-hd-webcam-AWC201-B.jpg 637w","srcWebp":"/static/744fdd0b7590c2e300f8e27da79a01d6/31466/amcrest-hd-webcam-AWC201-B.webp","srcSetWebp":"/static/744fdd0b7590c2e300f8e27da79a01d6/a278a/amcrest-hd-webcam-AWC201-B.webp 192w,\n/static/744fdd0b7590c2e300f8e27da79a01d6/2474b/amcrest-hd-webcam-AWC201-B.webp 384w,\n/static/744fdd0b7590c2e300f8e27da79a01d6/31466/amcrest-hd-webcam-AWC201-B.webp 637w","sizes":"(max-width: 637px) 100vw, 637px","presentationWidth":637,"presentationHeight":391}}}}},{"id":"e039772f-7190-5884-9089-12ab8bf6f6fb","body":"var _excluded = [\"components\"];\nfunction _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }\nfunction _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }\nfunction _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }\n/* @jsxRuntime classic */\n/* @jsx mdx */\n\nvar _frontmatter = {\n  \"title\": \"How I turned into a Lockdown meme\",\n  \"cover\": \"./im-tiger-king.jpg\",\n  \"date\": \"2020-04-19T00:00:00.000Z\",\n  \"description\": \"My friend Bryan just blew my mind with a Lockdown Starter Pack fb post the other day that caused me to realize the pandemic lockdown has turned me into a meme. Let's explore how I won this unfortunate bingo prize.\",\n  \"tags\": [\"meme\", \"blog\", \"post\"]\n};\nvar makeShortcode = function makeShortcode(name) {\n  return function MDXDefaultShortcode(props) {\n    console.warn(\"Component \" + name + \" was not imported, exported, or provided by MDXProvider as global scope\");\n    return mdx(\"div\", props);\n  };\n};\nvar Embed = makeShortcode(\"Embed\");\nvar layoutProps = {\n  _frontmatter: _frontmatter\n};\nvar MDXLayout = \"wrapper\";\nreturn function MDXContent(_ref) {\n  var components = _ref.components,\n    props = _objectWithoutProperties(_ref, _excluded);\n  return mdx(MDXLayout, _extends({}, layoutProps, props, {\n    components: components,\n    mdxType: \"MDXLayout\"\n  }), mdx(\"h2\", {\n    \"id\": \"shared-pandemic-experiences-unite-us-all\"\n  }, \"Shared pandemic experiences unite us all\"), mdx(\"p\", null, \"We've all been going through the COVID-19 pandemic together and I consider myself fortunate counting my blessings that I've made through with just inconvenience so far.\"), mdx(\"p\", null, \"We all get through it in our unique way, but at least in my friend circle we heavily rely on the power of dark humor \", mdx(\"em\", {\n    parentName: \"p\"\n  }, \"and Tiger King\"), \". \"), mdx(\"p\", null, \"So over these past weeks we've been in lockdown, trying to flatten the curve, we've all been sharing what I thought were our own unique updates on how we've been getting by until my mind was blown by the starter pack meme my friend Bryan posted.\"), mdx(\"span\", {\n    \"className\": \"gatsby-resp-image-wrapper\",\n    \"style\": {\n      \"position\": \"relative\",\n      \"display\": \"block\",\n      \"marginLeft\": \"auto\",\n      \"marginRight\": \"auto\",\n      \"maxWidth\": \"800px\"\n    }\n  }, \"\\n      \", mdx(\"span\", {\n    parentName: \"span\",\n    \"className\": \"gatsby-resp-image-background-image\",\n    \"style\": {\n      \"paddingBottom\": \"100%\",\n      \"position\": \"relative\",\n      \"bottom\": \"0\",\n      \"left\": \"0\",\n      \"backgroundImage\": \"url('data:image/jpeg;base64,/9j/2wBDAAMCAgMCAgMDAwMEAwMEBQgFBQQEBQoHBwYIDAoMDAsKCwsNDhIQDQ4RDgsLEBYQERMUFRUVDA8XGBYUGBIUFRT/2wBDAQMEBAUEBQkFBQkUDQsNFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBT/wgARCAAUABQDASIAAhEBAxEB/8QAGgABAAIDAQAAAAAAAAAAAAAAAAQFAgMGCP/EABcBAAMBAAAAAAAAAAAAAAAAAAABAgP/2gAMAwEAAhADEAAAAfQlB0NPnp1DFcSNQ1LAf//EABwQAAMAAgMBAAAAAAAAAAAAAAIDBAEFEhMUFf/aAAgBAQABBQJ5rqc+0MTa6v3x8B+mhdKYNZk8xDIC2jIHWpQpD//EABcRAAMBAAAAAAAAAAAAAAAAAAABECH/2gAIAQMBAT8Bwd//xAAWEQADAAAAAAAAAAAAAAAAAAABESD/2gAIAQIBAT8BZj//xAAiEAACAQQCAQUAAAAAAAAAAAABAgMAERIhBDEyE0FCUXH/2gAIAQEABj8CnQcqaB4htVOOvun5Il5hxYAxq1JNgyX9n7qRSU8LhNX/AGuSTKvq3BEjWIC0hd1kJ3kvRqaVdPLbI00fwPY1ugi+I6r/xAAeEAACAgMBAAMAAAAAAAAAAAABEQAhMUFRYXGBof/aAAgBAQABPyF4ywjA0YsezJeKEA08Y3Bgw3HkKf5GXioPypnsG0CDVJsr7mge+ScEKArNYq0EJnjSgBpdTiQBwcn/2gAMAwEAAgADAAAAELAgPP/EABoRAQACAwEAAAAAAAAAAAAAAAERQQAQIeH/2gAIAQMBAT8QA1Ly/MEKb//EABoRAAEFAQAAAAAAAAAAAAAAAAEAEBEhMUH/2gAIAQIBAT8QlNZfEMf/xAAbEAEBAAMBAQEAAAAAAAAAAAABEQAhMUFRgf/aAAgBAQABPxAOrzxG3USxFNeYLKiZqbObFS+OrMlaW+itoo7DOOA9xWHcBYgVNdM9yboSQuwBOu/GJ015oMIXgIk3q4BBotK3zcCFvuGsCKyCVALQD8MBVBpk8gAAGf/Z')\",\n      \"backgroundSize\": \"cover\",\n      \"display\": \"block\"\n    }\n  }), \"\\n  \", mdx(\"img\", {\n    parentName: \"span\",\n    \"className\": \"gatsby-resp-image-image\",\n    \"alt\": \"Lockdown starter pack\",\n    \"title\": \"Lockdown starter pack\",\n    \"src\": \"/static/e3d2789aab49d38e4108ecaa1d9440ba/5fd6b/lockdown-starter-pack.jpg\",\n    \"srcSet\": [\"/static/e3d2789aab49d38e4108ecaa1d9440ba/bd2b6/lockdown-starter-pack.jpg 205w\", \"/static/e3d2789aab49d38e4108ecaa1d9440ba/ceeba/lockdown-starter-pack.jpg 410w\", \"/static/e3d2789aab49d38e4108ecaa1d9440ba/5fd6b/lockdown-starter-pack.jpg 800w\"],\n    \"sizes\": \"(max-width: 800px) 100vw, 800px\",\n    \"style\": {\n      \"width\": \"100%\",\n      \"height\": \"100%\",\n      \"margin\": \"0\",\n      \"verticalAlign\": \"middle\",\n      \"position\": \"absolute\",\n      \"top\": \"0\",\n      \"left\": \"0\"\n    },\n    \"loading\": \"lazy\"\n  }), \"\\n    \"), mdx(\"p\", null, \"Apparently I just filled the bingo card.\"), mdx(\"p\", null, \"My experience is not as unique as I thought so let's step through these for a laugh.\"), mdx(\"h2\", {\n    \"id\": \"everyone-baking-banana-and-sourdough-bread\"\n  }, \"Everyone Baking (Banana and Sourdough bread)\"), mdx(\"p\", null, \"When everyone was panic buying in the first weeks I thought I was super clever breaking out my old sourdough recipe I learned on Good Eats, but I guess so did everyone else \\uD83D\\uDE09\"), mdx(\"span\", {\n    \"className\": \"gatsby-resp-image-wrapper\",\n    \"style\": {\n      \"position\": \"relative\",\n      \"display\": \"block\",\n      \"marginLeft\": \"auto\",\n      \"marginRight\": \"auto\",\n      \"maxWidth\": \"637px\"\n    }\n  }, \"\\n      \", mdx(\"span\", {\n    parentName: \"span\",\n    \"className\": \"gatsby-resp-image-background-image\",\n    \"style\": {\n      \"paddingBottom\": \"75.1219512195122%\",\n      \"position\": \"relative\",\n      \"bottom\": \"0\",\n      \"left\": \"0\",\n      \"backgroundImage\": \"url('data:image/jpeg;base64,/9j/2wBDAAMCAgMCAgMDAwMEAwMEBQgFBQQEBQoHBwYIDAoMDAsKCwsNDhIQDQ4RDgsLEBYQERMUFRUVDA8XGBYUGBIUFRT/2wBDAQMEBAUEBQkFBQkUDQsNFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBT/wgARCAAPABQDASIAAhEBAxEB/8QAFgABAQEAAAAAAAAAAAAAAAAABwAG/8QAFAEBAAAAAAAAAAAAAAAAAAAABf/aAAwDAQACEAMQAAABy6YFqg6p3YubG//EABsQAQACAgMAAAAAAAAAAAAAAAQDBQIGARQV/9oACAEBAAEFAqethtU+CFBkM66dd2PCs4RuBiHkZnPJ/8QAGxEAAQQDAAAAAAAAAAAAAAAAAQACAyEEEYH/2gAIAQMBAT8BgGOIb6i926X/xAAbEQABBAMAAAAAAAAAAAAAAAABAAIEERMhgf/aAAgBAgEBPwGS6TnNcQYK2v/EACMQAAIBAwQBBQAAAAAAAAAAAAECAwAEERITISIUMkFCUWH/2gAIAQEABj8CuLy4YPGXLiMDGtvfNY2vGkHpmh6yJU8N1Ba3E0blTKdSav3rxWxOuUzlWFCRkYvjhR8qeVgCzksc/df/xAAdEAEBAAEFAQEAAAAAAAAAAAABEQAxQVFhcSGx/9oACAEBAAE/IZAnCUao2F0OcBfmGajpPxwag5jOiEK8y5UZeCLs9XPXg3eumMzqUtTXP//aAAwDAQACAAMAAAAQuz//xAAbEQACAgMBAAAAAAAAAAAAAAABEQBRMUFx8P/aAAgBAwEBPxAYBB2z6o4crXJ//8QAGhEAAgMBAQAAAAAAAAAAAAAAAREAMUEh8P/aAAgBAgEBPxApArAo+1xUvdu5/8QAGxABAQEBAQEBAQAAAAAAAAAAAREhQQAxUWH/2gAIAQEAAT8QVliDhwMolVVhr7qxrXZxM6CHLfB3LoJaPAjgrGpfGVdBq651foT+3nn2x7kUXQCsq8P3FOR3lKHyFYHA9//Z')\",\n      \"backgroundSize\": \"cover\",\n      \"display\": \"block\"\n    }\n  }), \"\\n  \", mdx(\"img\", {\n    parentName: \"span\",\n    \"className\": \"gatsby-resp-image-image\",\n    \"alt\": \"Sourdough\",\n    \"title\": \"Sourdough\",\n    \"src\": \"/static/9047c0318a446ae46c1d6383329c5f2e/60f36/sourdough.jpg\",\n    \"srcSet\": [\"/static/9047c0318a446ae46c1d6383329c5f2e/bd2b6/sourdough.jpg 205w\", \"/static/9047c0318a446ae46c1d6383329c5f2e/ceeba/sourdough.jpg 410w\", \"/static/9047c0318a446ae46c1d6383329c5f2e/60f36/sourdough.jpg 637w\"],\n    \"sizes\": \"(max-width: 637px) 100vw, 637px\",\n    \"style\": {\n      \"width\": \"100%\",\n      \"height\": \"100%\",\n      \"margin\": \"0\",\n      \"verticalAlign\": \"middle\",\n      \"position\": \"absolute\",\n      \"top\": \"0\",\n      \"left\": \"0\"\n    },\n    \"loading\": \"lazy\"\n  }), \"\\n    \"), mdx(\"p\", null, mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://www.foodnetwork.com/recipes/knead-not-sourdough-recipe-1939606\"\n  }, \"Sourdough recipe, Alton Brown is my hero\")), mdx(\"p\", null, \"Oh, and since I have been using Instacart for produce I tend to have too many or not enough bananas at the end of the week. This was a surplus week and I couldn't freeze anymore for smoothies so I thought I'd try banana bread, but so did everyone else.\"), mdx(\"span\", {\n    \"className\": \"gatsby-resp-image-wrapper\",\n    \"style\": {\n      \"position\": \"relative\",\n      \"display\": \"block\",\n      \"marginLeft\": \"auto\",\n      \"marginRight\": \"auto\",\n      \"maxWidth\": \"637px\"\n    }\n  }, \"\\n      \", mdx(\"span\", {\n    parentName: \"span\",\n    \"className\": \"gatsby-resp-image-background-image\",\n    \"style\": {\n      \"paddingBottom\": \"75.1219512195122%\",\n      \"position\": \"relative\",\n      \"bottom\": \"0\",\n      \"left\": \"0\",\n      \"backgroundImage\": \"url('data:image/jpeg;base64,/9j/2wBDAAMCAgMCAgMDAwMEAwMEBQgFBQQEBQoHBwYIDAoMDAsKCwsNDhIQDQ4RDgsLEBYQERMUFRUVDA8XGBYUGBIUFRT/2wBDAQMEBAUEBQkFBQkUDQsNFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBT/wgARCAAPABQDASIAAhEBAxEB/8QAFwABAQEBAAAAAAAAAAAAAAAABQAEBv/EABUBAQEAAAAAAAAAAAAAAAAAAAUE/9oADAMBAAIQAxAAAAE/OWmZfz8lJH//xAAaEAACAwEBAAAAAAAAAAAAAAACBAMFBgEA/9oACAEBAAEFAl6xwPPZ/swt0zrRyabsTRaCMQHdR1Bf/8QAGxEAAQQDAAAAAAAAAAAAAAAAAgABEiIEETH/2gAIAQMBAT8BnhDUmRQ3Ti//xAAcEQABBAMBAAAAAAAAAAAAAAACAAEREgQTIjH/2gAIAQIBAT8B15xPcCaEN479X//EACEQAAIBBAEFAQAAAAAAAAAAAAECAwAREiEEFCIxQWFC/9oACAEBAAY/AoR1L8eeNRZS5Jj14+Vcz5uN2kY0pTjxzhRjmJiPf2plZBM98mljuASd+6XNWxbxapIBx/1l3br/xAAbEAEBAAMBAQEAAAAAAAAAAAABEQAhQTFRcf/aAAgBAQABPyE954GHxRikoGS3pEE6+ZVMlsdOzrfNYNdhYFrSvcJXjuCOVIWlVsPn5n//2gAMAwEAAgADAAAAEPwf/8QAGREAAgMBAAAAAAAAAAAAAAAAATEAEUEh/9oACAEDAQE/EKAPvT16pUfRw1P/xAAbEQACAgMBAAAAAAAAAAAAAAABEQAxIUFhcf/aAAgBAgEBPxAkGCghXWJisBt7P//EABoQAQEAAwEBAAAAAAAAAAAAAAERACExQcH/2gAIAQEAAT8QmzhQvqgZQxYqjgyhjDWpybVm/mV5OgoiaLBVFUgcAccAUMGCMcmtawBMDbXwrTvoZsozQCDwAIGV73w//9k=')\",\n      \"backgroundSize\": \"cover\",\n      \"display\": \"block\"\n    }\n  }), \"\\n  \", mdx(\"img\", {\n    parentName: \"span\",\n    \"className\": \"gatsby-resp-image-image\",\n    \"alt\": \"Banana bread\",\n    \"title\": \"Banana bread\",\n    \"src\": \"/static/d532d27d2ee62cd28ac867c349e4fe02/60f36/banana-bread.jpg\",\n    \"srcSet\": [\"/static/d532d27d2ee62cd28ac867c349e4fe02/bd2b6/banana-bread.jpg 205w\", \"/static/d532d27d2ee62cd28ac867c349e4fe02/ceeba/banana-bread.jpg 410w\", \"/static/d532d27d2ee62cd28ac867c349e4fe02/60f36/banana-bread.jpg 637w\"],\n    \"sizes\": \"(max-width: 637px) 100vw, 637px\",\n    \"style\": {\n      \"width\": \"100%\",\n      \"height\": \"100%\",\n      \"margin\": \"0\",\n      \"verticalAlign\": \"middle\",\n      \"position\": \"absolute\",\n      \"top\": \"0\",\n      \"left\": \"0\"\n    },\n    \"loading\": \"lazy\"\n  }), \"\\n    \"), mdx(\"p\", null, \"I found the recipe for this one on \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://www.allrecipes.com/recipe/20144/banana-banana-bread/\"\n  }, \"AllRecipes\"), \". Came out pretty good as you can see.\"), mdx(\"span\", {\n    \"className\": \"gatsby-resp-image-wrapper\",\n    \"style\": {\n      \"position\": \"relative\",\n      \"display\": \"block\",\n      \"marginLeft\": \"auto\",\n      \"marginRight\": \"auto\",\n      \"maxWidth\": \"637px\"\n    }\n  }, \"\\n      \", mdx(\"span\", {\n    parentName: \"span\",\n    \"className\": \"gatsby-resp-image-background-image\",\n    \"style\": {\n      \"paddingBottom\": \"75.1219512195122%\",\n      \"position\": \"relative\",\n      \"bottom\": \"0\",\n      \"left\": \"0\",\n      \"backgroundImage\": \"url('data:image/jpeg;base64,/9j/2wBDAAMCAgMCAgMDAwMEAwMEBQgFBQQEBQoHBwYIDAoMDAsKCwsNDhIQDQ4RDgsLEBYQERMUFRUVDA8XGBYUGBIUFRT/2wBDAQMEBAUEBQkFBQkUDQsNFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBT/wgARCAAPABQDASIAAhEBAxEB/8QAFwABAQEBAAAAAAAAAAAAAAAABAAGB//EABcBAAMBAAAAAAAAAAAAAAAAAAACBQb/2gAMAwEAAhADEAAAAQN5oyFpNzCmP//EABkQAAMBAQEAAAAAAAAAAAAAAAMEBQIBE//aAAgBAQABBQJayqQas3d4+atFTjGO+idQwNBbdoZ//8QAGxEAAgEFAAAAAAAAAAAAAAAAAAECBBESFEH/2gAIAQMBAT8B2G3jIvTdif/EABsRAAEEAwAAAAAAAAAAAAAAAAABAgMEEiLx/9oACAECAQE/AXVsNoxHWE6f/8QAIxAAAQMCBQUAAAAAAAAAAAAAAgEDEQAEEhMhMTIUIkFxof/aAAgBAQAGPwIQRUZw7CaRThNi00wOkkMzWWV5ctInFB1SK4aR3IK10xG45b75WLDHuKV1i0bINpcWPnhK/8QAHBAAAgIDAQEAAAAAAAAAAAAAAREAMSFBYVHR/9oACAEBAAE/IUN9UvkDWGhHAG7LBNSG4JfpgY9DqDmy3SfQcBjoPwJUEgNCxmf/2gAMAwEAAgADAAAAENQ//8QAHBEAAQMFAAAAAAAAAAAAAAAAAQARIWFxgfDx/9oACAEDAQE/EBVFs7xPyAmy/8QAGhEAAgIDAAAAAAAAAAAAAAAAAREAkUFh0f/aAAgBAgEBPxAIZU+6uAk1Sf/EABsQAQEBAAMBAQAAAAAAAAAAAAERIQAxQWFx/9oACAEBAAE/EMFAjVkARo6wfPeV8ckN1gRZKuAsDOXvQWadcgTZeVS5DgzBajrJv5wWxKeT9XPsC+zmfMb9EDADJSzWqH//2Q==')\",\n      \"backgroundSize\": \"cover\",\n      \"display\": \"block\"\n    }\n  }), \"\\n  \", mdx(\"img\", {\n    parentName: \"span\",\n    \"className\": \"gatsby-resp-image-image\",\n    \"alt\": \"Banana bread\",\n    \"title\": \"Banana bread\",\n    \"src\": \"/static/99b9cc66de819463a2cd17f0eb1388d3/60f36/banana-bread-2.jpg\",\n    \"srcSet\": [\"/static/99b9cc66de819463a2cd17f0eb1388d3/bd2b6/banana-bread-2.jpg 205w\", \"/static/99b9cc66de819463a2cd17f0eb1388d3/ceeba/banana-bread-2.jpg 410w\", \"/static/99b9cc66de819463a2cd17f0eb1388d3/60f36/banana-bread-2.jpg 637w\"],\n    \"sizes\": \"(max-width: 637px) 100vw, 637px\",\n    \"style\": {\n      \"width\": \"100%\",\n      \"height\": \"100%\",\n      \"margin\": \"0\",\n      \"verticalAlign\": \"middle\",\n      \"position\": \"absolute\",\n      \"top\": \"0\",\n      \"left\": \"0\"\n    },\n    \"loading\": \"lazy\"\n  }), \"\\n    \"), mdx(\"p\", null, \"Also, for some reason you can't order Costco bagels through Instacart so I tried baking homemade bagels while under #stayAtHome. I went with this \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://www.sophisticatedgourmet.com/2009/10/new-york-style-bagel-recipe/\"\n  }, \"New York Style bagel recipe\"), \" and it came out good even mixing leftover white and wheat all purpose flour instead of bread flour. The family helped me film the process while my hands were covered in dough.\"), mdx(Embed, {\n    src: \"https://www.youtube.com/embed/NwmNtqSmtvQ\",\n    mdxType: \"Embed\"\n  }), mdx(\"h2\", {\n    \"id\": \"tiger-king\"\n  }, \"Tiger King\"), mdx(\"p\", null, \"Wow Netflix thanks for this distraction from reality and giving us something else to talk about.\"), mdx(\"p\", null, \"My favorite part how this is literally everyone's mental progression through the series.\"), mdx(\"span\", {\n    \"className\": \"gatsby-resp-image-wrapper\",\n    \"style\": {\n      \"position\": \"relative\",\n      \"display\": \"block\",\n      \"marginLeft\": \"auto\",\n      \"marginRight\": \"auto\",\n      \"maxWidth\": \"637px\"\n    }\n  }, \"\\n      \", mdx(\"span\", {\n    parentName: \"span\",\n    \"className\": \"gatsby-resp-image-background-image\",\n    \"style\": {\n      \"paddingBottom\": \"84.87804878048782%\",\n      \"position\": \"relative\",\n      \"bottom\": \"0\",\n      \"left\": \"0\",\n      \"backgroundImage\": \"url('data:image/jpeg;base64,/9j/2wBDAAMCAgMCAgMDAwMEAwMEBQgFBQQEBQoHBwYIDAoMDAsKCwsNDhIQDQ4RDgsLEBYQERMUFRUVDA8XGBYUGBIUFRT/2wBDAQMEBAUEBQkFBQkUDQsNFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBT/wgARCAARABQDASIAAhEBAxEB/8QAGQABAAMBAQAAAAAAAAAAAAAAAAQFBwMG/8QAGAEBAAMBAAAAAAAAAAAAAAAAAgABAwT/2gAMAwEAAhADEAAAAdQjeErBrs7LFCs5nWZ4M//EAB0QAAMAAgIDAAAAAAAAAAAAAAIDBAUGASQxMjT/2gAIAQEAAQUCpyvbyewmTJdhYCasgYNvc4+ZKjFNH1O8z+v/xAAcEQAABgMAAAAAAAAAAAAAAAAAAQIDEBESQlH/2gAIAQMBAT8BS1ewwPs//8QAGBEAAgMAAAAAAAAAAAAAAAAAAQIAECL/2gAIAQIBAT8BUMTNX//EACYQAAEDAwIFBQAAAAAAAAAAAAEAAiEDERITIgQjMnFyc4GhscH/2gAIAQEABj8Ce7RdkY2vgqllw0CQ4VcfZAaGPmZVZnVzCflM3XpmnabQmgOtaJCq+o77VPwH6j3X/8QAHhABAAICAgMBAAAAAAAAAAAAAQARIVEx8EFhcdH/2gAIAQEAAT8hRywtwl1wymcGwWfNOFxAiAff7xLm8fbY0tZ4x+wSTbUL7pznZqKWBKayd5tO03Hc9E//2gAMAwEAAgADAAAAEKTHP//EABkRAQEBAQEBAAAAAAAAAAAAAAERADFBof/aAAgBAwEBPxClEE7R+S3PgccM7//EABoRAQADAAMAAAAAAAAAAAAAAAEAITERcZH/2gAIAQIBAT8QBCgOLnVDc5Gj5CM//8QAHBABAQEAAgMBAAAAAAAAAAAAAREhADEgYZFB/9oACAEBAAE/EEcMCgA4SlAGTLN4KFgU1tQgAh6lB1nOMGa7ejb+fDlIU2QIQa6LBEgzTynmnSlFdzH0YvHawUrQFL0Ms9+C6Q5//9k=')\",\n      \"backgroundSize\": \"cover\",\n      \"display\": \"block\"\n    }\n  }), \"\\n  \", mdx(\"img\", {\n    parentName: \"span\",\n    \"className\": \"gatsby-resp-image-image\",\n    \"alt\": \"Carole Baskin\",\n    \"title\": \"Carole Baskin\",\n    \"src\": \"/static/0a6719039836052c0bd68a89a7904b45/60f36/carole-baskin.jpg\",\n    \"srcSet\": [\"/static/0a6719039836052c0bd68a89a7904b45/bd2b6/carole-baskin.jpg 205w\", \"/static/0a6719039836052c0bd68a89a7904b45/ceeba/carole-baskin.jpg 410w\", \"/static/0a6719039836052c0bd68a89a7904b45/60f36/carole-baskin.jpg 637w\"],\n    \"sizes\": \"(max-width: 637px) 100vw, 637px\",\n    \"style\": {\n      \"width\": \"100%\",\n      \"height\": \"100%\",\n      \"margin\": \"0\",\n      \"verticalAlign\": \"middle\",\n      \"position\": \"absolute\",\n      \"top\": \"0\",\n      \"left\": \"0\"\n    },\n    \"loading\": \"lazy\"\n  }), \"\\n    \"), mdx(\"p\", null, \"Also, thank you Snap Cam for making video calls so professional. I'm sure they are stealing all our data, but it is a small price to pay...\"), mdx(\"span\", {\n    \"className\": \"gatsby-resp-image-wrapper\",\n    \"style\": {\n      \"position\": \"relative\",\n      \"display\": \"block\",\n      \"marginLeft\": \"auto\",\n      \"marginRight\": \"auto\",\n      \"maxWidth\": \"630px\"\n    }\n  }, \"\\n      \", mdx(\"span\", {\n    parentName: \"span\",\n    \"className\": \"gatsby-resp-image-background-image\",\n    \"style\": {\n      \"paddingBottom\": \"92.68292682926828%\",\n      \"position\": \"relative\",\n      \"bottom\": \"0\",\n      \"left\": \"0\",\n      \"backgroundImage\": \"url('data:image/jpeg;base64,/9j/2wBDAAMCAgMCAgMDAwMEAwMEBQgFBQQEBQoHBwYIDAoMDAsKCwsNDhIQDQ4RDgsLEBYQERMUFRUVDA8XGBYUGBIUFRT/2wBDAQMEBAUEBQkFBQkUDQsNFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBT/wgARCAATABQDASIAAhEBAxEB/8QAGQABAAMBAQAAAAAAAAAAAAAAAAUGBwIE/8QAGQEAAQUAAAAAAAAAAAAAAAAAAwACBAUG/9oADAMBAAIQAxAAAAGN7z7UgybEx4g1WRNhV+gAd//EAB4QAAIBAwUAAAAAAAAAAAAAAAMEAgEFBhESFCIz/9oACAEBAAEFAjtVbLwGbccRC7MdaYneTsOAUhmJtB9ZtPsXAann/8QAGREAAwEBAQAAAAAAAAAAAAAAAAEDEgIR/9oACAEDAQE/AZzm59MyjTS8NM//xAAbEQEAAgIDAAAAAAAAAAAAAAABAAIDIRIxUf/aAAgBAgEBPwG7kLBN+ThV2kcdF6n/xAAjEAACAQQBAwUAAAAAAAAAAAABAgMABBEhEiIxQSRRYnGB/9oACAEBAAY/Ahb211G5k3zTOlpPVCWFtOqFta13oYzUM8ZW2lxwVQNMfapJbxzH1dCyY5sfzxWoQR8jWRojzQNzM0xXtyPaj91//8QAHxABAAICAgIDAAAAAAAAAAAAAQARIVExYUGBcaGx/9oACAEBAAE/Ia30B1suh+o5kWlbZJPhzC1GbYwG9U4Wr30QlgcW8rShXtiFmzSI/swWQWByMw4JzoAYT16J/9oADAMBAAIAAwAAABDI8P8A/8QAGREBAQEBAQEAAAAAAAAAAAAAAREAMUGB/9oACAEDAQE/EEKtHzn3CHTVCsZTxwJBd//EABsRAQACAgMAAAAAAAAAAAAAAAEAESExQYGR/9oACAECAQE/EHWYTnfUUQAqKaayXuogU+E//8QAHBABAQACAwEBAAAAAAAAAAAAAREAITFBUWGR/9oACAEBAAE/EFj8s5itMGB2d8FMrKTxgsMeGix0hniBYi/ueL5oEGtjXlHoygOwGGiaDQLtQKFzrk0vuhHxMtCa6AOkTdPcLforQUs+vby4AWBShw//2Q==')\",\n      \"backgroundSize\": \"cover\",\n      \"display\": \"block\"\n    }\n  }), \"\\n  \", mdx(\"img\", {\n    parentName: \"span\",\n    \"className\": \"gatsby-resp-image-image\",\n    \"alt\": \"I'm the tiger king\",\n    \"title\": \"I'm the tiger king\",\n    \"src\": \"/static/782412a231f403e0cee4be5c6043027a/83b6b/tigerking-hat.jpg\",\n    \"srcSet\": [\"/static/782412a231f403e0cee4be5c6043027a/bd2b6/tigerking-hat.jpg 205w\", \"/static/782412a231f403e0cee4be5c6043027a/ceeba/tigerking-hat.jpg 410w\", \"/static/782412a231f403e0cee4be5c6043027a/83b6b/tigerking-hat.jpg 630w\"],\n    \"sizes\": \"(max-width: 630px) 100vw, 630px\",\n    \"style\": {\n      \"width\": \"100%\",\n      \"height\": \"100%\",\n      \"margin\": \"0\",\n      \"verticalAlign\": \"middle\",\n      \"position\": \"absolute\",\n      \"top\": \"0\",\n      \"left\": \"0\"\n    },\n    \"loading\": \"lazy\"\n  }), \"\\n    \"), mdx(\"h2\", {\n    \"id\": \"exotic-travel\"\n  }, \"Exotic Travel\"), mdx(\"p\", null, \"Here I am in my last trip to Costco just before things got really crazy and you could still find things.\"), mdx(\"span\", {\n    \"className\": \"gatsby-resp-image-wrapper\",\n    \"style\": {\n      \"position\": \"relative\",\n      \"display\": \"block\",\n      \"marginLeft\": \"auto\",\n      \"marginRight\": \"auto\",\n      \"maxWidth\": \"375px\"\n    }\n  }, \"\\n      \", mdx(\"span\", {\n    parentName: \"span\",\n    \"className\": \"gatsby-resp-image-background-image\",\n    \"style\": {\n      \"paddingBottom\": \"133.1707317073171%\",\n      \"position\": \"relative\",\n      \"bottom\": \"0\",\n      \"left\": \"0\",\n      \"backgroundImage\": \"url('data:image/jpeg;base64,/9j/2wBDAAMCAgMCAgMDAwMEAwMEBQgFBQQEBQoHBwYIDAoMDAsKCwsNDhIQDQ4RDgsLEBYQERMUFRUVDA8XGBYUGBIUFRT/2wBDAQMEBAUEBQkFBQkUDQsNFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBT/wgARCAAbABQDASIAAhEBAxEB/8QAGAAAAwEBAAAAAAAAAAAAAAAAAAUHBgT/xAAWAQEBAQAAAAAAAAAAAAAAAAADBAX/2gAMAwEAAhADEAAAAe5UlApeicOqd1+b1ijNxxpgF//EAB0QAAICAwADAAAAAAAAAAAAAAIEAwUBBhIAITL/2gAIAQEAAQUC7EWgkFbLv3LtExyk23H5A3M1FsKuULOurgly3TEE25j1fUmeUrD21//EABwRAAEDBQAAAAAAAAAAAAAAAAEAAxEQEhMhUf/aAAgBAwEBPwG3UoMNkTlCHKf/xAAZEQADAQEBAAAAAAAAAAAAAAAAARECEkH/2gAIAQIBAT8B9jHqOcm9VJnR/8QAJxAAAgEDAgQHAQAAAAAAAAAAAQIRAAMSIUEEEyJRIzEyUlNxgfH/2gAIAQEABj8Ct8Rb1hhmszoavO+AbmFVUtFW+jmeGvVlG1coXTw5grIQDE7fdEl0d0l3xRfVHlMUt6/xoss4kLG35S2HOyl4q/DZ2ioQadv7RGYUdsa1+Jage4U1f//EAB4QAAIDAAIDAQAAAAAAAAAAAAERACExQXFRgaFh/9oACAEBAAE/IVtbZwoY8gw3qJH2MPMmMSN+kEsBvA2YH1+QI3jujDQXrrIF0wkmEQeBk1ZcQvV6iqBrNEFd9oMo7OL7ALuyb3DGIgESxNmf/9oADAMBAAIAAwAAABAACn7/xAAZEQADAQEBAAAAAAAAAAAAAAAAAREhQcH/2gAIAQMBAT8QVa57KLTKvI8FjYh//8QAGBEAAwEBAAAAAAAAAAAAAAAAAAERIWH/2gAIAQIBAT8Q1TQqUc6hrwo//8QAHBABAQEBAQEAAwAAAAAAAAAAAREhMQBhQYGx/9oACAEBAAE/EJZAVDwqLqxN4MfMKpwKLPzgWhKoG+MRwIxoaRyTu99W9QypRUBcFqhJPNkXQWNgF5kCze+ZN75CgNNE7m99y/Er7qbMfu88GIePeXqq0aZ976lxR7CqwgAq54MCVqtaP55c2talI67wPDvSVe+//9k=')\",\n      \"backgroundSize\": \"cover\",\n      \"display\": \"block\"\n    }\n  }), \"\\n  \", mdx(\"img\", {\n    parentName: \"span\",\n    \"className\": \"gatsby-resp-image-image\",\n    \"alt\": \"Costco\",\n    \"title\": \"Costco\",\n    \"src\": \"/static/283a18b5563649a0ea78340be9ca461e/f21ed/costco-line.jpg\",\n    \"srcSet\": [\"/static/283a18b5563649a0ea78340be9ca461e/bd2b6/costco-line.jpg 205w\", \"/static/283a18b5563649a0ea78340be9ca461e/f21ed/costco-line.jpg 375w\"],\n    \"sizes\": \"(max-width: 375px) 100vw, 375px\",\n    \"style\": {\n      \"width\": \"100%\",\n      \"height\": \"100%\",\n      \"margin\": \"0\",\n      \"verticalAlign\": \"middle\",\n      \"position\": \"absolute\",\n      \"top\": \"0\",\n      \"left\": \"0\"\n    },\n    \"loading\": \"lazy\"\n  }), \"\\n    \"), mdx(\"p\", null, \"This would be a totally different situation if Instacart hadn't developed their business model when they did. Seriously. These workers don't get enough credit.\"), mdx(\"h2\", {\n    \"id\": \"so-many-gardening-pictures\"\n  }, \"So Many Gardening Pictures\"), mdx(\"p\", null, \"Apparently pineapple season and lockdown season coincide this year. Not driving to work sure gives me more time to keep the garden tidied.\"), mdx(\"span\", {\n    \"className\": \"gatsby-resp-image-wrapper\",\n    \"style\": {\n      \"position\": \"relative\",\n      \"display\": \"block\",\n      \"marginLeft\": \"auto\",\n      \"marginRight\": \"auto\",\n      \"maxWidth\": \"375px\"\n    }\n  }, \"\\n      \", mdx(\"span\", {\n    parentName: \"span\",\n    \"className\": \"gatsby-resp-image-background-image\",\n    \"style\": {\n      \"paddingBottom\": \"133.1707317073171%\",\n      \"position\": \"relative\",\n      \"bottom\": \"0\",\n      \"left\": \"0\",\n      \"backgroundImage\": \"url('data:image/jpeg;base64,/9j/2wBDAAMCAgMCAgMDAwMEAwMEBQgFBQQEBQoHBwYIDAoMDAsKCwsNDhIQDQ4RDgsLEBYQERMUFRUVDA8XGBYUGBIUFRT/2wBDAQMEBAUEBQkFBQkUDQsNFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBT/wgARCAAbABQDASIAAhEBAxEB/8QAGgAAAgIDAAAAAAAAAAAAAAAAAAQCBgMFB//EABgBAAIDAAAAAAAAAAAAAAAAAAMEAQIG/9oADAMBAAIQAxAAAAGo7dGWQC4ICw5oK5Hi106UVj//xAAeEAACAwABBQAAAAAAAAAAAAADBAECBRQGERITFf/aAAgBAQABBQIkWHAJKe8tA7QvyBAkV8/11rEZVstNB+nz29uwTedmAdPLDJoO5CXK/8QAGxEAAgIDAQAAAAAAAAAAAAAAAQIAURAREiL/2gAIAQMBAT8BVCVL1gDZ5ueZ/8QAHhEAAQQBBQAAAAAAAAAAAAAAEQABAgMEEhMyUtH/2gAIAQIBAT8BGqLyW3R2T1mozcv4o5mMOC//xAAmEAACAgECBQQDAAAAAAAAAAABAgMRAAQhEhMxQVEUImGxcZLB/9oACAEBAAY/AozFJcJ+bLfnNNBE3vVGeuLYb39DF5j8bV1FYYeYkkJJYTefKn5zUnSI8bELGzE78Pf6GKImCrXdgCTnP1mojgajyxu7OPFf3J5IYbW/eE2vxtfTCpSQd9nrJZ5HZ5ZtLM0jM3WumaYMthoN/wBckHpozR7ref/EAB8QAAICAwADAQEAAAAAAAAAAAERACExQVFhcZGBwf/aAAgBAQABPyESRQMUALy2yfg1MjeIcsjVDJ+ieeCIQ+u4K6hFhA+SmD/Y2x7pFoRh2QiyFu8CRq4kqmDsHFV37lQnsOwHU75gKMgCKG8AIeo0bwBdMMBaUB9LEe5RjjKCiS6Z/9oADAMBAAIAAwAAABB4F7z/xAAbEQACAQUAAAAAAAAAAAAAAAAAAVERITFBof/aAAgBAwEBPxDSgVcoehHAVmOn/8QAHhEAAgICAgMAAAAAAAAAAAAAAREAIVGRMWHB0eH/2gAIAQIBAT8QBcwkD0yr3YXqAARTfitQDEy6QsaQLWGTnqIWe/s//8QAHBABAQADAAMBAAAAAAAAAAAAAREAITFBUWGh/9oACAEBAAE/EHFbGiSGwKE2AQM7C0UAXGwI34E3ox/C8lVWHAWXCWwuHOoa15Oog8YEYOVZjijrQS6d4V/q0Wq6KqSegfOPibMceXQIGF8eW7WwdlqoVhYjpTuKJAZcIe8tBwQ+5sfVBsOmBUAAFAx6+vUGIWPsP324YDxwEGbF2vX5wz//2Q==')\",\n      \"backgroundSize\": \"cover\",\n      \"display\": \"block\"\n    }\n  }), \"\\n  \", mdx(\"img\", {\n    parentName: \"span\",\n    \"className\": \"gatsby-resp-image-image\",\n    \"alt\": \"Pineapple time\",\n    \"title\": \"Pineapple time\",\n    \"src\": \"/static/0063b026a01d3740dd156a11889b126e/f21ed/pineapple-garden.jpg\",\n    \"srcSet\": [\"/static/0063b026a01d3740dd156a11889b126e/bd2b6/pineapple-garden.jpg 205w\", \"/static/0063b026a01d3740dd156a11889b126e/f21ed/pineapple-garden.jpg 375w\"],\n    \"sizes\": \"(max-width: 375px) 100vw, 375px\",\n    \"style\": {\n      \"width\": \"100%\",\n      \"height\": \"100%\",\n      \"margin\": \"0\",\n      \"verticalAlign\": \"middle\",\n      \"position\": \"absolute\",\n      \"top\": \"0\",\n      \"left\": \"0\"\n    },\n    \"loading\": \"lazy\"\n  }), \"\\n    \"), mdx(\"h2\", {\n    \"id\": \"drinking-all-the-time\"\n  }, \"Drinking All the Time\"), mdx(\"p\", null, \"All I'm saying is that the Parent Teacher Org Moms and Dad's seem to be posting the same themed pictures and we had a serious discussion on if grapes count as raisins and what sort of pairing would work with breakfast cereal.\"), mdx(\"span\", {\n    \"className\": \"gatsby-resp-image-wrapper\",\n    \"style\": {\n      \"position\": \"relative\",\n      \"display\": \"block\",\n      \"marginLeft\": \"auto\",\n      \"marginRight\": \"auto\",\n      \"maxWidth\": \"800px\"\n    }\n  }, \"\\n      \", mdx(\"span\", {\n    parentName: \"span\",\n    \"className\": \"gatsby-resp-image-background-image\",\n    \"style\": {\n      \"paddingBottom\": \"56.09756097560976%\",\n      \"position\": \"relative\",\n      \"bottom\": \"0\",\n      \"left\": \"0\",\n      \"backgroundImage\": \"url('data:image/jpeg;base64,/9j/2wBDAAMCAgMCAgMDAwMEAwMEBQgFBQQEBQoHBwYIDAoMDAsKCwsNDhIQDQ4RDgsLEBYQERMUFRUVDA8XGBYUGBIUFRT/2wBDAQMEBAUEBQkFBQkUDQsNFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBT/wgARCAALABQDASIAAhEBAxEB/8QAGQAAAgMBAAAAAAAAAAAAAAAAAAcDBQYI/8QAFgEBAQEAAAAAAAAAAAAAAAAABAID/9oADAMBAAIQAxAAAAHNXjzhA5FHQ5eP/8QAGRAAAwEBAQAAAAAAAAAAAAAAAwQFAQIA/9oACAEBAAEFAhs4u4Jg2s1GjcP0Z63EmsoAJSSUye//xAAZEQACAwEAAAAAAAAAAAAAAAAAAQIREjH/2gAIAQMBAT8B3F8RaP/EABgRAAIDAAAAAAAAAAAAAAAAAAABAhEi/9oACAECAQE/AakkaP/EACQQAAIBAwMDBQAAAAAAAAAAAAECAwAEERITMQUhIiMyM0GR/9oACAEBAAY/ArJ5IFi9H5Nv2t9GkLNHKs7gxswEmVPbg8cVMsNruxA+LJbBh+1bEQR6tUfkVye5Ga6bohRdcuGwvNAtbpxX/8QAHRABAAMAAgMBAAAAAAAAAAAAAQARITFBYXGBsf/aAAgBAQABPyEgXQwLOc5VLPTBOTJBCsO6zzEgJiKPjYxUDAB2juxwbxgpXDHWDjCvyf/aAAwDAQACAAMAAAAQB+//xAAbEQACAgMBAAAAAAAAAAAAAAABEQChITFB0f/aAAgBAwEBPxAkJDdD3O4C9NT/xAAaEQACAgMAAAAAAAAAAAAAAAAAASGhMUFR/9oACAECAQE/EMw+WyGlZ//EABwQAQEAAgMBAQAAAAAAAAAAAAERACExQVFxgf/aAAgBAQABPxCnzAIbaEKiRhdaXzlmvKBKwHC8qB9qkOqqG/OOsDIQMgQSEKKNjhxY/l3NnnnWA1MFda9I9z//2Q==')\",\n      \"backgroundSize\": \"cover\",\n      \"display\": \"block\"\n    }\n  }), \"\\n  \", mdx(\"img\", {\n    parentName: \"span\",\n    \"className\": \"gatsby-resp-image-image\",\n    \"alt\": \"Breakfast wine pairing\",\n    \"title\": \"Breakfast wine pairing\",\n    \"src\": \"/static/cd43965ffcd01d1e049b43de22c7d45d/5fd6b/wine-cereal-pairing.jpg\",\n    \"srcSet\": [\"/static/cd43965ffcd01d1e049b43de22c7d45d/bd2b6/wine-cereal-pairing.jpg 205w\", \"/static/cd43965ffcd01d1e049b43de22c7d45d/ceeba/wine-cereal-pairing.jpg 410w\", \"/static/cd43965ffcd01d1e049b43de22c7d45d/5fd6b/wine-cereal-pairing.jpg 800w\"],\n    \"sizes\": \"(max-width: 800px) 100vw, 800px\",\n    \"style\": {\n      \"width\": \"100%\",\n      \"height\": \"100%\",\n      \"margin\": \"0\",\n      \"verticalAlign\": \"middle\",\n      \"position\": \"absolute\",\n      \"top\": \"0\",\n      \"left\": \"0\"\n    },\n    \"loading\": \"lazy\"\n  }), \"\\n    \"), mdx(\"h2\", {\n    \"id\": \"botched-haircuts\"\n  }, \"Botched Haircuts\"), mdx(\"p\", null, \"This one is pretty funny. It is like all of us hit the same amount of weeks out where we couldn't handle it anymore and had the same herd thought to try out the buzzers to see how bad it could possibly be. I went from seeing no posts about haircuts to entire reddit forums for bad pandemic haircuts.\"), mdx(\"span\", {\n    \"className\": \"gatsby-resp-image-wrapper\",\n    \"style\": {\n      \"position\": \"relative\",\n      \"display\": \"block\",\n      \"marginLeft\": \"auto\",\n      \"marginRight\": \"auto\",\n      \"maxWidth\": \"637px\"\n    }\n  }, \"\\n      \", mdx(\"span\", {\n    parentName: \"span\",\n    \"className\": \"gatsby-resp-image-background-image\",\n    \"style\": {\n      \"paddingBottom\": \"61.46341463414634%\",\n      \"position\": \"relative\",\n      \"bottom\": \"0\",\n      \"left\": \"0\",\n      \"backgroundImage\": \"url('data:image/jpeg;base64,/9j/2wBDAAMCAgMCAgMDAwMEAwMEBQgFBQQEBQoHBwYIDAoMDAsKCwsNDhIQDQ4RDgsLEBYQERMUFRUVDA8XGBYUGBIUFRT/2wBDAQMEBAUEBQkFBQkUDQsNFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBT/wgARCAAMABQDASIAAhEBAxEB/8QAGAAAAgMAAAAAAAAAAAAAAAAAAAYDBQf/xAAWAQEBAQAAAAAAAAAAAAAAAAAEAwX/2gAMAwEAAhADEAAAAZ2FG1wmgrl2WF//xAAcEAABBAMBAAAAAAAAAAAAAAAEAAECBQMUFQb/2gAIAQEAAQUCAsulecWzHTUGwvFBQMsCsE4vhI1G/8QAGhEAAgIDAAAAAAAAAAAAAAAAAAECAxESUf/aAAgBAwEBPwGFG0E+mEf/xAAWEQEBAQAAAAAAAAAAAAAAAAACERD/2gAIAQIBAT8BSimf/8QAIRAAAgEDBAMBAAAAAAAAAAAAAQIDAAQSBREhMRMiQcH/2gAIAQEABj8Ck0yytLcMuXvKT8otexQeEsEBTg7n8otG0bDrg1qzlnjlB4kjbFhy32omN1cSBYEfB39S2/ZpliiRATke+6//xAAbEAEAAwEBAQEAAAAAAAAAAAABABEhMUFRYf/aAAgBAQABPyFa3P2daA/k1tKmtKGtu2fZiBtmzYieb7yg8GTSWIPiVa8luxyOl17P/9oADAMBAAIAAwAAABAIz//EABoRAAICAwAAAAAAAAAAAAAAAAEREFEhQdH/2gAIAQMBAT8QAdWBvT5UH//EABgRAAIDAAAAAAAAAAAAAAAAAAARASFB/9oACAECAQE/EG0MRZ//xAAbEAEBAQEBAQEBAAAAAAAAAAABESEAMUFRYf/aAAgBAQABPxAdE4fUG1Jofv8AN4jCvgRGRQVmhmPE3IpwD089LygmjBZNqRZr+9nOvCk4Qqa+QkeGhfnP0D9h3//Z')\",\n      \"backgroundSize\": \"cover\",\n      \"display\": \"block\"\n    }\n  }), \"\\n  \", mdx(\"img\", {\n    parentName: \"span\",\n    \"className\": \"gatsby-resp-image-image\",\n    \"alt\": \"Haircut\",\n    \"title\": \"Haircut\",\n    \"src\": \"/static/813022f57dabc5c74b9a771c0303559d/60f36/pandemic-haircut.jpg\",\n    \"srcSet\": [\"/static/813022f57dabc5c74b9a771c0303559d/bd2b6/pandemic-haircut.jpg 205w\", \"/static/813022f57dabc5c74b9a771c0303559d/ceeba/pandemic-haircut.jpg 410w\", \"/static/813022f57dabc5c74b9a771c0303559d/60f36/pandemic-haircut.jpg 637w\"],\n    \"sizes\": \"(max-width: 637px) 100vw, 637px\",\n    \"style\": {\n      \"width\": \"100%\",\n      \"height\": \"100%\",\n      \"margin\": \"0\",\n      \"verticalAlign\": \"middle\",\n      \"position\": \"absolute\",\n      \"top\": \"0\",\n      \"left\": \"0\"\n    },\n    \"loading\": \"lazy\"\n  }), \"\\n    \"), mdx(\"h2\", {\n    \"id\": \"runners-everywhere\"\n  }, \"Runners Everywhere\"), mdx(\"p\", null, \"It's great seeing more people out exercising to keep busy. Funny enough I was all about running 5k and 10k races (which are now all canceled) before the pandemic and have found myself running less and staying inside doing free weights and indoor cycling instead.\"), mdx(\"h2\", {\n    \"id\": \"what-sleep-schedule\"\n  }, \"What Sleep Schedule\"), mdx(\"p\", null, \"Right?\"), mdx(\"h2\", {\n    \"id\": \"bingo\"\n  }, \"Bingo\"), mdx(\"p\", null, \"So let's stay safe, listen to \", mdx(\"em\", {\n    parentName: \"p\"\n  }, \"doctor's advice\"), \", watch out for scammers (looking at you essential oils), and not forget the power of humor to get us through.\"));\n}\n;\nMDXContent.isMDXComponent = true;","excerpt":"Shared pandemic experiences unite us all We've all been going through the COVID-19 pandemic together and I consider myself fortunate…","fields":{"slug":"/how-lockdown-made-me-a-meme/","type":"posts"},"headings":[{"value":"Shared pandemic experiences unite us all","depth":2},{"value":"Everyone Baking (Banana and Sourdough bread)","depth":2},{"value":"Tiger King","depth":2},{"value":"Exotic Travel","depth":2},{"value":"So Many Gardening Pictures","depth":2},{"value":"Drinking All the Time","depth":2},{"value":"Botched Haircuts","depth":2},{"value":"Runners Everywhere","depth":2},{"value":"What Sleep Schedule","depth":2},{"value":"Bingo","depth":2}],"frontmatter":{"title":"How I turned into a Lockdown meme","description":"My friend Bryan just blew my mind with a Lockdown Starter Pack fb post the other day that caused me to realize the pandemic lockdown has turned me into a meme. Let's explore how I won this unfortunate bingo prize.","link":null,"date":"2020-04-19T00:00:00.000Z","tags":["meme","blog","post"],"draft":null,"hide":null,"cover":{"childImageSharp":{"fluid":{"base64":"data:image/jpeg;base64,/9j/2wBDAAMCAgMCAgMDAwMEAwMEBQgFBQQEBQoHBwYIDAoMDAsKCwsNDhIQDQ4RDgsLEBYQERMUFRUVDA8XGBYUGBIUFRT/2wBDAQMEBAUEBQkFBQkUDQsNFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBT/wgARCAAQABQDASIAAhEBAxEB/8QAGAAAAwEBAAAAAAAAAAAAAAAAAAUHAwb/xAAVAQEBAAAAAAAAAAAAAAAAAAAEA//aAAwDAQACEAMQAAABzZSmunp0AmCo/8QAGxAAAgMAAwAAAAAAAAAAAAAAAwQCBRMAARX/2gAIAQEAAQUCYtwCsAvTkmtdlyfUspPpq7c87bv/xAAeEQAABQUBAAAAAAAAAAAAAAAAAQMSIQIEESJRof/aAAgBAwEBPwGzNQkqnTt5GA7hD//EABsRAQACAgMAAAAAAAAAAAAAAAEAAgMEESEx/9oACAECAQE/AdoxlgDjqFVPZ//EACEQAAIBAwQDAQAAAAAAAAAAAAECEQADIRIiMkEUQ3GR/9oACAEBAAY/Arhl2ug8nMKPg7ikbKX33K0cMbf2gLl6XXBYQJpnC+VqaTpz1FJZ9qWVlvmMUdLXUCkrtyK//8QAHBABAAICAwEAAAAAAAAAAAAAAREhADFBcYFh/9oACAEBAAE/IaIvGqlIdrJOUaKU7vhS/k5MrWUlF7HrzE3qE5lmOYjbglUA22pgiOKnvIJcaARFuVJz/9oADAMBAAIAAwAAABAD3//EABkRAQEAAwEAAAAAAAAAAAAAAAERACExof/aAAgBAwEBPxBHaVKzkQCco+Yi6Zn/xAAbEQACAgMBAAAAAAAAAAAAAAABEQAhMVFhgf/aAAgBAgEBPxBaoEGibsnaXMwALeYn/8QAGhABAQEBAQEBAAAAAAAAAAAAAREhMQBBgf/aAAgBAQABPxAVHV80WR0JupTnjdb3G905QjcM3BCMJgZWhitDHg8QxL4YiDjAgAjgHk1EZYHP0OiICE8nVjOWQhrLDn77/9k=","aspectRatio":1.271523178807947,"src":"/static/2838c81c60b4e5e116abad487d48c74f/0e005/im-tiger-king.jpg","srcSet":"/static/2838c81c60b4e5e116abad487d48c74f/3e5eb/im-tiger-king.jpg 192w,\n/static/2838c81c60b4e5e116abad487d48c74f/2880b/im-tiger-king.jpg 384w,\n/static/2838c81c60b4e5e116abad487d48c74f/0e005/im-tiger-king.jpg 637w","srcWebp":"/static/2838c81c60b4e5e116abad487d48c74f/31466/im-tiger-king.webp","srcSetWebp":"/static/2838c81c60b4e5e116abad487d48c74f/a278a/im-tiger-king.webp 192w,\n/static/2838c81c60b4e5e116abad487d48c74f/2474b/im-tiger-king.webp 384w,\n/static/2838c81c60b4e5e116abad487d48c74f/31466/im-tiger-king.webp 637w","sizes":"(max-width: 637px) 100vw, 637px","presentationWidth":637,"presentationHeight":501}}}}},{"id":"41f674f7-7dea-565c-b97c-f627971f97f9","body":"var _excluded = [\"components\"];\nfunction _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }\nfunction _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }\nfunction _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }\n/* @jsxRuntime classic */\n/* @jsx mdx */\n\nvar _frontmatter = {\n  \"title\": \"Teckin SS31 wifi smart plug installation with Alexa\",\n  \"cover\": \"./teckin-ss31-smart plug-outlet.png\",\n  \"date\": \"2020-03-30T00:00:00.000Z\",\n  \"description\": \"If you are looking to upgrade your photocell timer plug to a smart outdoor plug this Teckin smart plug review is for you.\",\n  \"tags\": [\"teckin\", \"SmartLife\", \"smart home\", \"iot\", \"post\"]\n};\nvar makeShortcode = function makeShortcode(name) {\n  return function MDXDefaultShortcode(props) {\n    console.warn(\"Component \" + name + \" was not imported, exported, or provided by MDXProvider as global scope\");\n    return mdx(\"div\", props);\n  };\n};\nvar Embed = makeShortcode(\"Embed\");\nvar layoutProps = {\n  _frontmatter: _frontmatter\n};\nvar MDXLayout = \"wrapper\";\nreturn function MDXContent(_ref) {\n  var components = _ref.components,\n    props = _objectWithoutProperties(_ref, _excluded);\n  return mdx(MDXLayout, _extends({}, layoutProps, props, {\n    components: components,\n    mdxType: \"MDXLayout\"\n  }), mdx(\"p\", null, \"Traditionally I've used a photocell timer plug for controlling my holiday Christmas lights. These plugs have a light sensor that turn on for a desired amount of time after it detects darkness. Teckin recently released the \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://amzn.to/2ycHdMB\"\n  }, \"SS31 outdoor smart plug\"), \", outdoor smart outlet, that allows for greater control through scheduling and remote control with Alexa or the SmartLife app.\"), mdx(\"blockquote\", null, mdx(\"p\", {\n    parentName: \"blockquote\"\n  }, \"The Teckin outdoor smart plug includes two outlets you can independently control and has an IP44 \", mdx(\"em\", {\n    parentName: \"p\"\n  }, \"waterproof\"), \" rating\")), mdx(\"h2\", {\n    \"id\": \"installation-experience\"\n  }, \"Installation Experience\"), mdx(\"p\", null, \"I review and demonstrate installation instructions for the outdoor Teckin smart plug in this video below or continue reading on for more.\"), mdx(Embed, {\n    src: \"https://www.youtube.com/embed/6KQaPjbKrIo\",\n    mdxType: \"Embed\"\n  }), mdx(\"h3\", {\n    \"id\": \"whats-included\"\n  }, \"What's included\"), mdx(\"ul\", null, mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"SS31 2 outlet outdoor smart plug\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Instruction manual\")), mdx(\"h3\", {\n    \"id\": \"wifi-pairing\"\n  }, \"WiFi Pairing\"), mdx(\"p\", null, \"WiFi pairing was very fast compared to other smart devices I've looked at recently.\"), mdx(\"ol\", null, mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Download the SmartLife app onto your iPhone from the app store\"), mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Click the + sign to add a device in the upper right corner.\"), mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Select \\\"Electrician\\\" on the sidebar and then \\\"Socket (Wi-Fi)\\\" on the right list.\"), mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Enter pairing mode by holding down the power button the the Teckin plug for 5 seconds.\"), mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"The light will start flashing on the smart plug.\"), mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Click the \", mdx(\"em\", {\n    parentName: \"li\"\n  }, \"Confirm flashing\"), \" button in the SmartLife app.\")), mdx(\"p\", null, \"The app quickly located and configured the plug.\"), mdx(\"h3\", {\n    \"id\": \"ip44-waterproof-rating\"\n  }, \"IP44 Waterproof Rating\"), mdx(\"p\", null, \"IP stands for ingress protection and IP44 means this plug is rated to prevent ingress of objects over 1mm or splashing water at any angle. This would make it acceptable for most outdoor use under some basic cover. \"), mdx(\"p\", null, \"You would not want to use this smart plug where it could get submerged or receive heavy blasts of water. In the video above I tested this out by spraying the outlet with a hose for some time and it lived up to its claims.\"), mdx(\"h3\", {\n    \"id\": \"features-and-performance\"\n  }, \"Features and Performance\"), mdx(\"p\", null, \"I like that I can control the switched outlets independently which gives me more control than my current photocell timer. The SmartLife app let me create schedules on a 24 hour clock. This allows more control than a photocell timer and let's you vary the schedule on a per day basis if desired. There was no input lag when manually switching the outlets on and off from the app.\"), mdx(\"p\", null, \"Alexa automatically discovered the outlet and switches based on their names in the SmartLife app. Voice control through Alexa worked without issue.\"), mdx(\"p\", null, \"As far as Wi-Fi strength, the device is separated by a concrete stucco wall and some interior walls from the Wi-Fi access point, but still had a fairly strong Wi-Fi signal.\"), mdx(\"h3\", {\n    \"id\": \"summary\"\n  }, \"Summary\"), mdx(\"p\", null, \"The \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://amzn.to/2ycHdMB\"\n  }, \"Teckin SS31 outdoor smart plug\"), \" is very close to the price point of a standard photocell outdoor timer and would be a good pick if you would like independent control and scheduling of two outlets.\"));\n}\n;\nMDXContent.isMDXComponent = true;","excerpt":"Traditionally I've used a photocell timer plug for controlling my holiday Christmas lights. These plugs have a light sensor that turn on for…","fields":{"slug":"/teckin-outdoor-smart-plug-ss31/","type":"posts"},"headings":[{"value":"Installation Experience","depth":2},{"value":"What's included","depth":3},{"value":"WiFi Pairing","depth":3},{"value":"IP44 Waterproof Rating","depth":3},{"value":"Features and Performance","depth":3},{"value":"Summary","depth":3}],"frontmatter":{"title":"Teckin SS31 wifi smart plug installation with Alexa","description":"If you are looking to upgrade your photocell timer plug to a smart outdoor plug this Teckin smart plug review is for you.","link":null,"date":"2020-03-30T00:00:00.000Z","tags":["teckin","SmartLife","smart home","iot","post"],"draft":null,"hide":null,"cover":{"childImageSharp":{"fluid":{"base64":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAMCAYAAABiDJ37AAAACXBIWXMAABJ0AAASdAHeZh94AAADW0lEQVQozx3Q20/TBxiH8V7PsQGSydlWQY4t0FJqW1qktJWDFUo5lNKjFEuBVioQQaqIgBhFRTZkYSoQM51C5ghjmrBgJlsgbsk2dmOyGHfhDhdb4n/w7IcXz+0n7/sVXR9qZSxiY9Bdwq0hK+vzvTy928ez+RCrUy6udR3hZo+RIZeKKwENV4I6TjcpiDi0TEaqWF8YZGvlOp+Mh+kL2hH5rBra6jUEbCVMROqYGQvx8UQ/k9EAI9119NqVdFSJ8aj30FkeQ8gQQ68phvPWRIZbsok6C4h6immvLaTZLEV0stmE327B77TT7nXR0mDF3dKCt9WJp8mG12qi06blckcVS5fcfHsrxMadAU57j/Fwdpxfn95l68tpvvviKk9un0dUbdTTVHsMT3MjXgH1ttrxuV0E2oKEOiOEe85yKnKBSOQc/ad6Wbl5hpGwG4vFRqC9k1++meffHx/xz0+P+W/nCSKjMhu9IgeDVoGlsgJ7Q70AevB723G1+mi0tVBdWUONyUhmRgYRu5FQ4xHKdKUU5OczMRjizbM5Xq3P8XrjDiKzMouKogz0Mgka2UE08lxKVXIMeh0GnY6qssNUqGWE3bWUFGRzMVjP8qUAUW8ll7ttLE/1C9BtXgnY6+eLiMoKDrCbTipBnS9GmSsmIz0JnTyHoMOC83g5Zm0R/mYL+1NTmBrw8PfzOf76fp63Py/xducxb7bu8+f2A/7YFEDVoWRUOalopeJ3qFZ6gCxxEmXKPNoaqzGWqpBLc7FUaOmym9n8fIzfhfd2vrrBi/tj/LAwzPa9YTY/O8PGTI+wYV4aZmk6+hwB3sWzUxEnJ5B/SIJBo8RRo2c02MDqdB8v16aFrWZ5+fUNthejrE52MRO2ErXrOFEhpUYhQeQvzyNglNFxtIiTR+X4q4qxGxX0O808GD3B9vxZfns4yovFc6xd62Zh0MFVfyUdlQp2jylO34s0MZbM+D0cjHsP0ac9ddwbcvDooo8loZVxH2sTPlaFlkeczIaPM9igxqHJxJCVSKkkgcNpce+A7L3vk/dRDNJ9H1CUGkdRWjyiSb+ZcZeeC81qBuqKCZrycZdm0aiUYMpJQitOQJH8IbJ9MchTYlHvj0eVHk9hyi4Qh1xACgVMlhpLblIs/wN9CvuOFwoN0gAAAABJRU5ErkJggg==","aspectRatio":1.641025641025641,"src":"/static/5fd3b3097db6ad4086349994c14a1bcc/1d6bc/teckin-ss31-smart%20plug-outlet.png","srcSet":"/static/5fd3b3097db6ad4086349994c14a1bcc/a4b17/teckin-ss31-smart%20plug-outlet.png 192w,\n/static/5fd3b3097db6ad4086349994c14a1bcc/1ef16/teckin-ss31-smart%20plug-outlet.png 384w,\n/static/5fd3b3097db6ad4086349994c14a1bcc/1d6bc/teckin-ss31-smart%20plug-outlet.png 637w","srcWebp":"/static/5fd3b3097db6ad4086349994c14a1bcc/31466/teckin-ss31-smart%20plug-outlet.webp","srcSetWebp":"/static/5fd3b3097db6ad4086349994c14a1bcc/a278a/teckin-ss31-smart%20plug-outlet.webp 192w,\n/static/5fd3b3097db6ad4086349994c14a1bcc/2474b/teckin-ss31-smart%20plug-outlet.webp 384w,\n/static/5fd3b3097db6ad4086349994c14a1bcc/31466/teckin-ss31-smart%20plug-outlet.webp 637w","sizes":"(max-width: 637px) 100vw, 637px","presentationWidth":637,"presentationHeight":387}}}}},{"id":"f9904dde-87ea-5181-9001-4e3f5d800a68","body":"var _excluded = [\"components\"];\nfunction _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }\nfunction _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }\nfunction _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }\n/* @jsxRuntime classic */\n/* @jsx mdx */\n\nvar _frontmatter = {\n  \"title\": \"My photos made the news!\",\n  \"cover\": \"./ramblewood-bubble-run.jpg\",\n  \"date\": \"2020-03-06T00:00:00.000Z\",\n  \"link\": \"https://coralspringstalk.com/family-fun-bubble-run-raises-25647\",\n  \"tags\": [\"link\", \"photography\", \"drone\"]\n};\nvar layoutProps = {\n  _frontmatter: _frontmatter\n};\nvar MDXLayout = \"wrapper\";\nreturn function MDXContent(_ref) {\n  var components = _ref.components,\n    props = _objectWithoutProperties(_ref, _excluded);\n  return mdx(MDXLayout, _extends({}, layoutProps, props, {\n    components: components,\n    mdxType: \"MDXLayout\"\n  }), mdx(\"p\", null, \"I recently was able to get some great photos and drone footage of a PTO fundraiser for the local elementary school that made the local paper!\"));\n}\n;\nMDXContent.isMDXComponent = true;","excerpt":"I recently was able to get some great photos and drone footage of a PTO fundraiser for the local elementary school that made the local paper…","fields":{"slug":"/links/pto-fundraiser/","type":"links"},"headings":[],"frontmatter":{"title":"My photos made the news!","description":null,"link":"https://coralspringstalk.com/family-fun-bubble-run-raises-25647","date":"2020-03-06T00:00:00.000Z","tags":["link","photography","drone"],"draft":null,"hide":null,"cover":{"childImageSharp":{"fluid":{"base64":"data:image/jpeg;base64,/9j/2wBDAAMCAgMCAgMDAwMEAwMEBQgFBQQEBQoHBwYIDAoMDAsKCwsNDhIQDQ4RDgsLEBYQERMUFRUVDA8XGBYUGBIUFRT/2wBDAQMEBAUEBQkFBQkUDQsNFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBT/wgARCAANABQDASIAAhEBAxEB/8QAFwABAQEBAAAAAAAAAAAAAAAABgAEBf/EABYBAQEBAAAAAAAAAAAAAAAAAAUABP/aAAwDAQACEAMQAAABLojemwdKJyQ3/8QAGRAAAwEBAQAAAAAAAAAAAAAAAgMEBQEU/9oACAEBAAEFAs6x5Kov9EgPZmhnSAq3UCXOEKGI7//EAB0RAAEDBQEAAAAAAAAAAAAAAAEAAgMEESFBkeH/2gAIAQMBAT8BDpCSb4Tq+cHXPV//xAAdEQACAgEFAAAAAAAAAAAAAAABAgARBAMFIaHR/9oACAECAQE/AdTGVUsDmou1Y5Fm+vJ//8QAIRAAAgICAgEFAAAAAAAAAAAAAQIDEQASBCEiEzFRsfD/2gAIAQEABj8CeVCFWPva/rDK/mKREG3teLBE8dDI0JdoWU7JdX+vCeLxvTfbqQuSa+MdQ1+V995//8QAHBABAAICAwEAAAAAAAAAAAAAAQARITFRYXGh/9oACAEBAAE/Ie6RQPHUogbp0F+dSih7duXPPFSmaBjaBmrNoMk6BO3LHyVgirEWfSf/2gAMAwEAAgADAAAAEBAf/8QAGhEAAgIDAAAAAAAAAAAAAAAAAREAIUFhof/aAAgBAwEBPxAhBtVFpZ23DkDpH//EABoRAAICAwAAAAAAAAAAAAAAAAERACExQaH/2gAIAQIBAT8QBilJ2E32lAZo7vKP/8QAGxABAQEBAQADAAAAAAAAAAAAAREhADFBUWH/2gAIAQEAAT8Q9RE6kJNNYM81PGdi+6g+kytl/V3L22ae1R1ctUeLvzxByzwgEwBgffO1qp+E2oJBaDmVp9M1KjDA3w7/2Q==","aspectRatio":1.5,"src":"/static/c36da6a020d982a73c2f6e5930510971/60a5f/ramblewood-bubble-run.jpg","srcSet":"/static/c36da6a020d982a73c2f6e5930510971/3e5eb/ramblewood-bubble-run.jpg 192w,\n/static/c36da6a020d982a73c2f6e5930510971/2880b/ramblewood-bubble-run.jpg 384w,\n/static/c36da6a020d982a73c2f6e5930510971/60a5f/ramblewood-bubble-run.jpg 768w,\n/static/c36da6a020d982a73c2f6e5930510971/2f1b1/ramblewood-bubble-run.jpg 800w","srcWebp":"/static/c36da6a020d982a73c2f6e5930510971/25278/ramblewood-bubble-run.webp","srcSetWebp":"/static/c36da6a020d982a73c2f6e5930510971/a278a/ramblewood-bubble-run.webp 192w,\n/static/c36da6a020d982a73c2f6e5930510971/2474b/ramblewood-bubble-run.webp 384w,\n/static/c36da6a020d982a73c2f6e5930510971/25278/ramblewood-bubble-run.webp 768w,\n/static/c36da6a020d982a73c2f6e5930510971/ccdb5/ramblewood-bubble-run.webp 800w","sizes":"(max-width: 768px) 100vw, 768px","presentationWidth":768,"presentationHeight":511}}}}},{"id":"97742959-363f-533e-b722-62b6711b04f3","body":"var _excluded = [\"components\"];\nfunction _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }\nfunction _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }\nfunction _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }\n/* @jsxRuntime classic */\n/* @jsx mdx */\n\nvar _frontmatter = {\n  \"title\": \"Ecobee review 2 years later\",\n  \"cover\": \"./ecobee-box.png\",\n  \"date\": \"2020-02-23T00:00:00.000Z\",\n  \"description\": \"If you are still on the fence about smart thermostats I'm sharing my thoughts in this Ecobee smart thermostat review so you can determine if it is worth upgrading your thermostat.\",\n  \"tags\": [\"ecobee\", \"smart home\", \"iot\", \"post\"]\n};\nvar makeShortcode = function makeShortcode(name) {\n  return function MDXDefaultShortcode(props) {\n    console.warn(\"Component \" + name + \" was not imported, exported, or provided by MDXProvider as global scope\");\n    return mdx(\"div\", props);\n  };\n};\nvar Embed = makeShortcode(\"Embed\");\nvar layoutProps = {\n  _frontmatter: _frontmatter\n};\nvar MDXLayout = \"wrapper\";\nreturn function MDXContent(_ref) {\n  var components = _ref.components,\n    props = _objectWithoutProperties(_ref, _excluded);\n  return mdx(MDXLayout, _extends({}, layoutProps, props, {\n    components: components,\n    mdxType: \"MDXLayout\"\n  }), mdx(\"p\", null, \"For a long time I debated whether it was worth it to upgrade from a 7 day programmable thermostat to an \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://amzn.to/2Pi0Poq\"\n  }, \"ecobee smart thermostat\"), \". As my schedule became less predictable the ability of the Ecobee to dynamically adjust/detect occupancy and the ability to place sensors through the house for temperature balance became a stronger selling point and made me decide to give the Ecobee a try.\"), mdx(\"blockquote\", null, mdx(\"p\", {\n    parentName: \"blockquote\"\n  }, \"After owning the Ecobee for over two years at this point I thought it would be helpful to share some things I've learned about the Ecobee and tips and tricks so others can get a complete view of ownership\")), mdx(\"p\", null, \"In the video below I go over my initial Ecobee installation experience.\"), mdx(Embed, {\n    src: \"https://www.youtube.com/embed/ErW22bWhtuo\",\n    mdxType: \"Embed\"\n  }), mdx(\"h2\", {\n    \"id\": \"favorite-ecobee-features-after-2-years\"\n  }, \"Favorite Ecobee Features (after 2 years)\"), mdx(\"ul\", null, mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Occupancy sensor\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Remote room temperature sensors\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Ecobee app\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Alexa integration\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Humidity control (AC overcool max)\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Energy tracking\")), mdx(\"h3\", {\n    \"id\": \"occupancy-sensor\"\n  }, \"Occupancy Sensor\"), mdx(\"p\", null, \"The occupancy sensor has indeed provided tangible cost savings over a 7-day programmable thermostat. For people on more of a predictable schedule the benefits may be less, but the occupancy schedule in \", mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"my house\"), \" isn't that predictable. The Ecobee will set itself away automatically when it doesn't detect anyone home. It is accurate and hasn't set itself away when I have been home if you were curious.\"), mdx(\"h3\", {\n    \"id\": \"remote-room-temperature-sensors\"\n  }, \"Remote room temperature sensors\"), mdx(\"p\", null, \"The Ecobee version I purchased included 3 remote temperature sensors that run on coin batteries. The batteries seem to last about 10-12 months. One of the remote sensors developed a tendancy to drop and lose connection after about the first year of ownership. It hasn't bothered me enough to replace yet. Outside of this, it works great keeping the office where the extra heat of the computers tends to make it hotter than the rest of the house.\"), mdx(\"h3\", {\n    \"id\": \"ecobee-app-and-alexa-integration\"\n  }, \"Ecobee app and Alexa integration\"), mdx(\"p\", null, \"Ecobee frequently updated the app, especially in the first year of ownership, adding more and more features over time such as Apple Watch integration and access to most of the important A/C controls from the iPhone app. I have had good luck with connectivity with their servers. I once had an issue for a couple days where the weather service thought the outside temperature was freezing (when it was 80F out). This prevented the compressor shut off as a precaution which required me to override the safeguards for my A/C to run. Outside of that occurence I haven't had any other notable issues.\"), mdx(\"p\", null, \"The Alexa integration is fantastic. It is very convenient being able to change the temperate from bed or a remote room.\"), mdx(\"h3\", {\n    \"id\": \"humidity-controls\"\n  }, \"Humidity controls\"), mdx(\"p\", null, \"I discovered an Ecobee feature called AC Overcool Max which allows you to let Ecobee cool beyond your set point to lower the indoor humidity to your desired comfort level. This has been a great feature in Florida where it is always humid. You can see how to set your Ecobee to lower your indoor humidity in the below video.\"), mdx(Embed, {\n    src: \"https://www.youtube.com/embed/-vrwK5XKzDw\",\n    mdxType: \"Embed\"\n  }), mdx(\"h3\", {\n    \"id\": \"energy-savings-and-dashboard\"\n  }, \"Energy Savings and Dashboard\"), mdx(\"p\", null, \"Ecobee tracks your AC runtime and predicted savings on their portal. They've added features to it over time and I've found it useful for validating that the insulation I had blown in is actually saving me money. It was nice to have the usage history there to do the before and after comparison.\"), mdx(\"span\", {\n    \"className\": \"gatsby-resp-image-wrapper\",\n    \"style\": {\n      \"position\": \"relative\",\n      \"display\": \"block\",\n      \"marginLeft\": \"auto\",\n      \"marginRight\": \"auto\",\n      \"maxWidth\": \"820px\"\n    }\n  }, \"\\n      \", mdx(\"span\", {\n    parentName: \"span\",\n    \"className\": \"gatsby-resp-image-background-image\",\n    \"style\": {\n      \"paddingBottom\": \"44.87804878048781%\",\n      \"position\": \"relative\",\n      \"bottom\": \"0\",\n      \"left\": \"0\",\n      \"backgroundImage\": \"url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAJCAYAAAAywQxIAAAACXBIWXMAABJ0AAASdAHeZh94AAACNklEQVQoz1WSy08TURTGuzDKow+gFKbTec90prSlhUoQgiEmLkyMiWEhJoYEFybEGAi2PBSj0YSFD+LjHzAGMah/gdGFutG4VDeCCQpu5K/4eWaqCxdfzjnfvfe7537nxiw3wPOLuPk+HK+ALbUt0fWb9b+1kLO8ANPxBfloLURYp3uyLF+9xuMn68T8QonRkTGGh44wNjZOsVwlq5mU+qsMj4xydPwYg7Uh4SwCz8ezHLmgQKU6KHsraIbFodY460832Nn5QUzTLdKGTdx26LVddMNBt1y6MzkyGZ2utCpQhPMwTQdDhMMLFVWnN6uRk7OJVJr7Dx7y9t17YinNYDqp8OJAD2c75LBuklNtjk8c5sLCSRo3pzk/M0k2Z+OXqriFMl6hn3xfE2HuBWWKlRqV2jCxhAisJFV+tRgsKx5py0bN2pw4M0R9dZLVR3PMzE2h5Cz8YgU3KIloSYTKUQxrT6IjXjuSR09WxRdfPAlKFSwxPDQ9k5Hn5Fw03aUz3RsNpdmRCAXlpnDQFA75kAu7jbUnO2lpS0TGHmxpJ57skqmptLYnaYunaEukojyjaFiuj2a6UXT+/gpDvO3sVrh+4xabz18Sm52bp95YZP5yg4XFJS5emuXc1DSNhSXm6w3h6yxfWeHU6Ymo8/A7eeGUB2qU+wcwZFD/Tfnzl69R8m1ri5+7u3z4+ImNZ5vs7u2xvf094n/v73P77hpd0okuAtmcKTAi6NJxsqObO/fWePX6DX8Aorkz7OYVIhsAAAAASUVORK5CYII=')\",\n      \"backgroundSize\": \"cover\",\n      \"display\": \"block\"\n    }\n  }), \"\\n  \", mdx(\"img\", {\n    parentName: \"span\",\n    \"className\": \"gatsby-resp-image-image\",\n    \"alt\": \"Ecobee energy dashboard\",\n    \"title\": \"Ecobee energy dashboard\",\n    \"src\": \"/static/762b857f6639bcf5e61e862fbae53f72/083f8/ecobee-home-iq-dashboard.png\",\n    \"srcSet\": [\"/static/762b857f6639bcf5e61e862fbae53f72/ad4a5/ecobee-home-iq-dashboard.png 205w\", \"/static/762b857f6639bcf5e61e862fbae53f72/74ab4/ecobee-home-iq-dashboard.png 410w\", \"/static/762b857f6639bcf5e61e862fbae53f72/083f8/ecobee-home-iq-dashboard.png 820w\", \"/static/762b857f6639bcf5e61e862fbae53f72/b3178/ecobee-home-iq-dashboard.png 1230w\", \"/static/762b857f6639bcf5e61e862fbae53f72/d2c0c/ecobee-home-iq-dashboard.png 1503w\"],\n    \"sizes\": \"(max-width: 820px) 100vw, 820px\",\n    \"style\": {\n      \"width\": \"100%\",\n      \"height\": \"100%\",\n      \"margin\": \"0\",\n      \"verticalAlign\": \"middle\",\n      \"position\": \"absolute\",\n      \"top\": \"0\",\n      \"left\": \"0\"\n    },\n    \"loading\": \"lazy\"\n  }), \"\\n    \"), mdx(\"h2\", {\n    \"id\": \"would-i-recommend-the-ecobee\"\n  }, \"Would I recommend the Ecobee?\"), mdx(\"p\", null, \"Yes. There is a bit of an upfront cost, but I think the savings have a reasonable payback period and I have enjoyed the smart home integration more than I thought I would.\"), mdx(\"p\", null, \"If I've convinced you please consider supporting my site by using my affiliate link to purchase \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://amzn.to/2Pi0Poq\"\n  }, \"the ecobee smart thermostat\"), \".\"));\n}\n;\nMDXContent.isMDXComponent = true;","excerpt":"For a long time I debated whether it was worth it to upgrade from a 7 day programmable thermostat to an  ecobee smart thermostat . As my…","fields":{"slug":"/ecobee-2-years-hands-on-review/","type":"posts"},"headings":[{"value":"Favorite Ecobee Features (after 2 years)","depth":2},{"value":"Occupancy Sensor","depth":3},{"value":"Remote room temperature sensors","depth":3},{"value":"Ecobee app and Alexa integration","depth":3},{"value":"Humidity controls","depth":3},{"value":"Energy Savings and Dashboard","depth":3},{"value":"Would I recommend the Ecobee?","depth":2}],"frontmatter":{"title":"Ecobee review 2 years later","description":"If you are still on the fence about smart thermostats I'm sharing my thoughts in this Ecobee smart thermostat review so you can determine if it is worth upgrading your thermostat.","link":null,"date":"2020-02-23T00:00:00.000Z","tags":["ecobee","smart home","iot","post"],"draft":null,"hide":null,"cover":{"childImageSharp":{"fluid":{"base64":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAOCAYAAAAvxDzwAAAACXBIWXMAABJ0AAASdAHeZh94AAAD3UlEQVQ4yx2TW0zTZxjG/8lu1CDKoYqU2lJaeqQHeoLScqqlggVrEBRBmBCcDMZA6tiU0zxAAMF4GsIQpwFhy2Q6WHRRgcpcsmThwi1LlizuYhfbbpYsu9uW/PbNizffzfv88jzPm0+KDUT4dqafjcVpVu/eYn1hlmdz08RuX+eruQme373B13cusX7tHVYGm1kZbSc2cZZnU8OsTQzyZLyPL8+1sdQRZr7GjLR6ro6Nhcusf75I9NQIk5P3Gbs0z6iYrp7rNLScp7K+m9KqNgL7G9lTcYzi8gb8pUfwhaqx+cMcOlDNk+Eoiy3FSCuDDa+Aa/fvEalsoq39fU60DvDm2+epaTpFaP9x8kN1OAsqsXgrsOVX4AxEyC4sx+rbi8LswZ1bxOrkOEvv1iKtX2zgu49HWb4zjcu7F19hhOJQLQUltbh9B7B6yjA7gxicAfSOANrsQjTZ+aisXhQmN/EKHU5Pvqhnmidj3UhrFw7z/exZFq6Ooc/yYncX48gNiSmj6a1++oenaWrtxx+sxiigGqsfpTGH1EwHKRlW4nZqMFk9ovcZYjcuID3tqeDH+YssTt9EobGiNeeg0tp5/USUpxsv+WL9B67cXOZgXQdZrj3o7H7SxY5c50Au9hLkejIN2ax+OEbsknD49EwZv68ssnRvCVmamrQMC2qDi87Tg0zNP+LKrQecHpxiT7gOgz0frSVPvD52C+Du/12mZ6FSm/nkzHFi/TVIsd5S/tqI8akAxslSSU5NR2fxUHOs/VXkxrY+jjZ3sS9Sh8lRiMGWh8UpejR5yDC6UAlwugCOVflYOOpAetyZw2+P57g9M8emxJ3Ey9LIzHJQWFKBScSzewJ4C0oJhauwugpRiphqAUlTWzCKw5iseWTqsxkq0TAVViA9bLHy88wAI11dbEqQsTUpVUS24fYHSFFohTibDCHQGhwYsjwcrn+DobFrdER7yckrwZ0TwGbPo9cv54PQTqTl3laifhcexS62COD2pBSSdikFMIhcpUOZYUZncmEUNbiE+MVPv/Av8I+Yk9EecnOD+HxBegoUXC5IQMpLTeE1SUKbnCgcKYnfnsw2AU1V6VGojciVegxmJzaHD2dOMS9e/soff8OfgnphcJxAUZiSYIRuv5IrgSQkb4aSoeYGvpkV/3jqKsodKWyOSyRhh1wcSC1cGkjXWNAbnZjMbs4OXWb58XM+mvuMuiPNVEZqKQ9X0+lVMuqLR3o00sfD907yoKud6N5itm6OI26bjESZHNkulXCoQy2ABpNwafdiE4fweYMUFZRxoPwQ9TWNVB+sp8OnYci9hf8A7XY+BMzbtOAAAAAASUVORK5CYII=","aspectRatio":1.411764705882353,"src":"/static/bf126ae1acfebefb6bf69480209a78e9/6caa6/ecobee-box.png","srcSet":"/static/bf126ae1acfebefb6bf69480209a78e9/a4b17/ecobee-box.png 192w,\n/static/bf126ae1acfebefb6bf69480209a78e9/1ef16/ecobee-box.png 384w,\n/static/bf126ae1acfebefb6bf69480209a78e9/6caa6/ecobee-box.png 768w,\n/static/bf126ae1acfebefb6bf69480209a78e9/4c4ee/ecobee-box.png 821w","srcWebp":"/static/bf126ae1acfebefb6bf69480209a78e9/25278/ecobee-box.webp","srcSetWebp":"/static/bf126ae1acfebefb6bf69480209a78e9/a278a/ecobee-box.webp 192w,\n/static/bf126ae1acfebefb6bf69480209a78e9/2474b/ecobee-box.webp 384w,\n/static/bf126ae1acfebefb6bf69480209a78e9/25278/ecobee-box.webp 768w,\n/static/bf126ae1acfebefb6bf69480209a78e9/e4e51/ecobee-box.webp 821w","sizes":"(max-width: 768px) 100vw, 768px","presentationWidth":768,"presentationHeight":544}}}}},{"id":"56c70be0-7d61-5b04-b53a-c053ee5d01b0","body":"var _excluded = [\"components\"];\nfunction _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }\nfunction _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }\nfunction _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }\n/* @jsxRuntime classic */\n/* @jsx mdx */\n\nvar _frontmatter = {\n  \"title\": \"Automated database testing with Nightwatch.js\",\n  \"cover\": \"./nightwatch-database-testing.png\",\n  \"date\": \"2020-02-10T00:00:00.000Z\",\n  \"description\": \"When writing automated browser tests at the UI using frameworks like Selenium or Nightwatch.js it can be useful to validate your front-end interactions cause the desired changes in the backend database. This tutorial will show you how to use Nightwatch to write database tests against SQL databases.\",\n  \"tags\": [\"quality assurance\", \"database testing\", \"software testing\", \"nightwatchjs\", \"post\"]\n};\nvar makeShortcode = function makeShortcode(name) {\n  return function MDXDefaultShortcode(props) {\n    console.warn(\"Component \" + name + \" was not imported, exported, or provided by MDXProvider as global scope\");\n    return mdx(\"div\", props);\n  };\n};\nvar Embed = makeShortcode(\"Embed\");\nvar layoutProps = {\n  _frontmatter: _frontmatter\n};\nvar MDXLayout = \"wrapper\";\nreturn function MDXContent(_ref) {\n  var components = _ref.components,\n    props = _objectWithoutProperties(_ref, _excluded);\n  return mdx(MDXLayout, _extends({}, layoutProps, props, {\n    components: components,\n    mdxType: \"MDXLayout\"\n  }), mdx(\"h2\", {\n    \"id\": \"when-to-use-automated-database-testing\"\n  }, \"When to use automated database testing\"), mdx(\"p\", null, \"Sometimes the most effective way to validate the pass or fail criteria of automated tests written in the UI with frameworks such as Nightwatch.js or Selenium-webdriver is by writing assertions against the database in your test cases directly.\"), mdx(\"p\", null, \"For example, in a typical automated test case you may create a new account, try to login as the user, and verify the user profile page has the information you signed up with. \"), mdx(\"blockquote\", null, mdx(\"p\", {\n    parentName: \"blockquote\"\n  }, \"What if some of the information the requirements are supposed to validate aren't observable in the front end? \")), mdx(\"p\", null, \"You'd need a way to query the database and assert that the correct values made it there.\"), mdx(\"p\", null, \"In addition, navigating through the UI can be time consuming and may be immaterial to your test case. If you are only interested that the new user record is created after filling in a form you can assert against a database query immediately after your submit the form. This saves time on test execution and since there are less steps in the UI this pattern lends itself to being more reliable and repeatable.\"), mdx(Embed, {\n    src: \"https://www.youtube.com/embed/52BVA9cV2Mc\",\n    mdxType: \"Embed\"\n  }), mdx(\"h3\", {\n    \"id\": \"examples-of-when-to-use-database-test-assertions-from-the-webdriver\"\n  }, \"Examples of when to use database test assertions from the webdriver\"), mdx(\"ul\", null, mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Cases where the database logic is coded into the view\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"When results are not observable in the UI\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"When unit and integration tests can't be used\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"To save time and increase reliability\")), mdx(\"blockquote\", null, mdx(\"p\", {\n    parentName: \"blockquote\"\n  }, \"This database testing tutorial will teach database testing with Nightwatch.js custom assertions\")), mdx(\"h2\", {\n    \"id\": \"nightwatchjs-custom-assertions-for-mssql\"\n  }, \"Nightwatch.js custom assertions for MSSQL\"), mdx(\"p\", null, \"One way to verify the correct database records exist after your automated test actions is to verify a record count matching your WHERE clause. Using Nightwatch.js custom assertions you can assert database record counts as pass and fail criteria in your automated tests.\"), mdx(\"p\", null, \"On npm I published a package you can use in your Nightwatch tests called \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://www.npmjs.com/package/nightwatch-mssql-assertions\"\n  }, \"nightwatch-mssql-assertions\")), mdx(\"p\", null, \"This would allow you to database assertions in your test like the following\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-js\"\n  }, \"browser\\n    .assert\\n    .recordCountIs(2, tableName, whereClause)\\n\")), mdx(\"p\", null, \"A simple chained example would be asserting no records exist for a user, creating a new user in the UI, then verifying a record exists for that user.\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-js\"\n  }, \"let whereJohnWick = \\\"first_name = 'John' AND last_name = 'Wick'\\\";\\n        browser\\n            .assert\\n            .recordCountIs(0, \\\"people\\\", whereJohnWick)\\n            .url('http://foo.bar/createUser?fname=John&lName=Wick')\\n            .assert.recordCountIs(1, \\\"people\\\", whereJohnWick)\\n\")), mdx(\"h3\", {\n    \"id\": \"installing-nightwatch-mssql-assertions\"\n  }, \"Installing nightwatch-mssql-assertions\"), mdx(\"p\", null, \"In a command prompt at your Nightwatch test project\"), mdx(\"blockquote\", null, mdx(\"p\", {\n    parentName: \"blockquote\"\n  }, \"npm install nightwatch-mssql-assertions --save\")), mdx(\"p\", null, \"After, modify your nightwatch.json configuration file to add a custom assertions path to the nightwatch-mssql-assertions package\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-sh\"\n  }, \"\\\"custom_assertions_path\\\": [\\\"./node_modules/nightwatch-mssql-assertions/src/assertions\\\"],\\n\")), mdx(\"p\", null, \"Then, in the test settings section, add globals values that include the database credentials, address, port, and database name.\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-sh\"\n  }, \"\\\"test_settings\\\": {\\n        \\\"default\\\": {\\n            \\\"desiredCapabilities\\\": {\\n                \\\"browserName\\\": \\\"chrome\\\"\\n            },\\n            \\\"globals\\\": {\\n                \\\"dbUsername\\\": \\\"sa\\\",\\n                \\\"dbPassword\\\": \\\"ThisIsAStrongP@assword!SortOf\\\",\\n                \\\"dbAddress\\\": \\\"localhost\\\",\\n                \\\"dbPort\\\": 1433,\\n                \\\"dbName\\\": \\\"nightwatchDb\\\"\\n            }\\n        }\\n    }\\n\")), mdx(\"h2\", {\n    \"id\": \"running-nightwatch-database-tests\"\n  }, \"Running Nightwatch database tests\"), mdx(\"p\", null, \"Once the Nightwatch custom assertions package for MSSQL is downloaded you can execute your test automation by calling \", mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"nightwatch\"), \" at the command prompt and get output similar to this.\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-sh\"\n  }, \"Running:  Verify John Wick count\\n\\n\\u221A Testing if the record count (first_name = 'John' AND last_name = 'Wick') equals 0 (88ms)\\n\\u221A Testing if element <body> contains text 'Added john wick' (27ms)\\n\\u221A Testing if the record count (first_name = 'John' AND last_name = 'Wick') equals 3 (89ms)\\n\\u221A Testing if element <body> contains text 'Removed john wick' (21ms)\\n\\u221A Testing if the record count (first_name = 'John' AND last_name = 'Wick') equals 0 (93ms)\\n\\u221A Testing if the record count (first_name = 'Jane' AND last_name = 'Doe') equals 1 (86ms)\\n\\nOK. 6 assertions passed. (512ms)\\n\")), mdx(\"p\", null, \"As shown in the test output you can mix element assertions and database assertions in the same test case.\"), mdx(\"h2\", {\n    \"id\": \"full-database-testing-example\"\n  }, \"Full database testing example\"), mdx(\"p\", null, \"I found some tutorials for database testing with selenium, but not much with Nightwatch so created one and put the full source code on my GitHub to accompany this article. It covers this testing tutorial in greater detail with more examples.\"), mdx(\"p\", null, \"To make it an easier walk through it has a bundled docker image with Microsoft SQL server (mssql) preseeded with records to accompany the tests along with a simple node express app for adding and removing people to allow for more interesting example tests.\"), mdx(\"p\", null, mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://github.com/reallymello/nightwatchTutorials/tree/master/databaseExample\"\n  }, \"Database testing with Nightwatch.js on GitHub\")), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-js\"\n  }, \"module.exports = {\\n    '@tags': ['database'],\\n    afterEach: function (browser) {\\n        browser.url('http://localhost:8080/reset');\\n    },\\n    'Verify only one Jane Doe': function (browser) {\\n        browser\\n            .assert\\n            .recordCountIs(1, \\\"people\\\", \\\"first_name = 'Jane' AND last_name = 'Doe'\\\");\\n    },\\n    'Verify only one John Doe': function (browser) {\\n        browser\\n            .assert\\n            .recordCountIs(1, \\\"people\\\", \\\"first_name = 'John' AND last_name = 'Doe'\\\");\\n    },\\n    'Verify only 3 people exist': function (browser) {\\n        browser\\n            .assert\\n            .recordCountIs(3, \\\"people\\\");\\n    },\\n    'Verify John Wick count': function (browser) {\\n        let addJohnWick = 'http://localhost:8080/addPerson/john/wick';\\n        let removeJohnWick = 'http://localhost:8080/removePerson/john/wick';\\n        let whereJohnWick = \\\"first_name = 'John' AND last_name = 'Wick'\\\";\\n        browser\\n            .assert\\n            .recordCountIs(0, \\\"people\\\", whereJohnWick)\\n            .url(addJohnWick)\\n            .assert.containsText(\\\"body\\\", \\\"Added john wick\\\")\\n            .url(addJohnWick)\\n            .url(addJohnWick)\\n            .assert.recordCountIs(3, \\\"people\\\", whereJohnWick)\\n            .url(removeJohnWick)\\n            .assert.containsText(\\\"body\\\", \\\"Removed john wick\\\")\\n            .assert.recordCountIs(0, \\\"people\\\", whereJohnWick)\\n            .assert.recordCountIs(1, \\\"people\\\", \\\"first_name = 'Jane' AND last_name = 'Doe'\\\");\\n    }\\n}\\n\")), mdx(\"h2\", {\n    \"id\": \"final-considerations\"\n  }, \"Final considerations\"), mdx(\"p\", null, \"To make your tests repeatable you need to find a way of tearing down your data once your tests finish running. Otherwise the counts may change on the next test run.\"), mdx(\"p\", null, \"In my example, I built an endpoint that resets the table back to the original seeded data in the Nightwatch afterEach test hook that is run after each test. I've also seen custom DELETE queries used or database snapshot and restore as options to consider.\"), mdx(\"p\", null, \"I hope you found this Nightwatch tutorial helpful. Feel free to reach out on \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://twitter.com/reallymello\"\n  }, \"twitter\"), \" and share the article with friends. Thanks!\"));\n}\n;\nMDXContent.isMDXComponent = true;","excerpt":"When to use automated database testing Sometimes the most effective way to validate the pass or fail criteria of automated tests written in…","fields":{"slug":"/database-testing-with-nightwatchjs/","type":"posts"},"headings":[{"value":"When to use automated database testing","depth":2},{"value":"Examples of when to use database test assertions from the webdriver","depth":3},{"value":"Nightwatch.js custom assertions for MSSQL","depth":2},{"value":"Installing nightwatch-mssql-assertions","depth":3},{"value":"Running Nightwatch database tests","depth":2},{"value":"Full database testing example","depth":2},{"value":"Final considerations","depth":2}],"frontmatter":{"title":"Automated database testing with Nightwatch.js","description":"When writing automated browser tests at the UI using frameworks like Selenium or Nightwatch.js it can be useful to validate your front-end interactions cause the desired changes in the backend database. This tutorial will show you how to use Nightwatch to write database tests against SQL databases.","link":null,"date":"2020-02-10T00:00:00.000Z","tags":["quality assurance","database testing","software testing","nightwatchjs","post"],"draft":null,"hide":null,"cover":{"childImageSharp":{"fluid":{"base64":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAYAAAB/Ca1DAAAACXBIWXMAAAsTAAALEwEAmpwYAAAB1ElEQVQoz3VSS47TQBDNnq/i7nY7TtqxPYmT2EnsfEYeIMAMGiFASIg9G27AFdghdrBjj5A4AByA1VyAO7Bhw5LFo6o9dkykWTx3d3X186tX1THaReCpFlxwrO/uMWjtq/z9G6P370LfQ6cKUGLPsxhojYmRKCcC5VRgPZJYEbYJQyD2VUNck/E67OmK0JK5ErG4jrG4hoHoIg0VHi0dnC0cnOcOdikhEzil8wn95DipEPmqUdwQ+kogPQrx8d1bfP70AU9Od/DFTYyNvqJc1awDXSttEXpS4GTk4+L7F/z6+QOvn58hkDeQBBpHfYXEKMwCadUw6ZCIRhTn0qcBl0re9VqEoTG4EwlcfPuKv39+4+U6QhF38WAu8Xjl2DKfbbhsYS14uBB4uukij6W1IyN78pgbckkY0ef+vR3evNji/asSx8vMKmNV54VjvUyH3BhukMDdWeUfE84jiUWksIxcqkCTSiJkqeOhQY9Kl7dvNdKN51GSi0nAe23Rd7WdgtpP9pdL/m9seFNkM2wWmUWRTrFZzrElrOYZ8jTFiu7LdW7vynWBJDREJi9ntp7FyseOLx34sos+dbu+5GQGJ9TnZl4P1jYsYZvoMOEwXp+vAuf8A8z/NzxBn2p5AAAAAElFTkSuQmCC","aspectRatio":1.7777777777777777,"src":"/static/5bb340e3089ef21c93d324cf67896b05/6caa6/nightwatch-database-testing.png","srcSet":"/static/5bb340e3089ef21c93d324cf67896b05/a4b17/nightwatch-database-testing.png 192w,\n/static/5bb340e3089ef21c93d324cf67896b05/1ef16/nightwatch-database-testing.png 384w,\n/static/5bb340e3089ef21c93d324cf67896b05/6caa6/nightwatch-database-testing.png 768w,\n/static/5bb340e3089ef21c93d324cf67896b05/3f078/nightwatch-database-testing.png 1152w,\n/static/5bb340e3089ef21c93d324cf67896b05/bf181/nightwatch-database-testing.png 1346w","srcWebp":"/static/5bb340e3089ef21c93d324cf67896b05/25278/nightwatch-database-testing.webp","srcSetWebp":"/static/5bb340e3089ef21c93d324cf67896b05/a278a/nightwatch-database-testing.webp 192w,\n/static/5bb340e3089ef21c93d324cf67896b05/2474b/nightwatch-database-testing.webp 384w,\n/static/5bb340e3089ef21c93d324cf67896b05/25278/nightwatch-database-testing.webp 768w,\n/static/5bb340e3089ef21c93d324cf67896b05/e7b8c/nightwatch-database-testing.webp 1152w,\n/static/5bb340e3089ef21c93d324cf67896b05/89bf0/nightwatch-database-testing.webp 1346w","sizes":"(max-width: 768px) 100vw, 768px","presentationWidth":768,"presentationHeight":434}}}}},{"id":"471053fe-e245-5b51-986d-0074df12cb9c","body":"var _excluded = [\"components\"];\nfunction _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }\nfunction _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }\nfunction _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }\n/* @jsxRuntime classic */\n/* @jsx mdx */\n\nvar _frontmatter = {\n  \"title\": \"Yale smart door lock installation review\",\n  \"cover\": \"./yale-smart-lock.png\",\n  \"date\": \"2020-02-02T00:00:00.000Z\",\n  \"description\": \"The Yale smart lock I installed and reviewed a year ago is still the best smart door lock in 2020 in my opinion. See why as I cover installation and operation with Alexa in my post.\",\n  \"tags\": [\"yale\", \"smart home\", \"iot\", \"post\"]\n};\nvar makeShortcode = function makeShortcode(name) {\n  return function MDXDefaultShortcode(props) {\n    console.warn(\"Component \" + name + \" was not imported, exported, or provided by MDXProvider as global scope\");\n    return mdx(\"div\", props);\n  };\n};\nvar Embed = makeShortcode(\"Embed\");\nvar layoutProps = {\n  _frontmatter: _frontmatter\n};\nvar MDXLayout = \"wrapper\";\nreturn function MDXContent(_ref) {\n  var components = _ref.components,\n    props = _objectWithoutProperties(_ref, _excluded);\n  return mdx(MDXLayout, _extends({}, layoutProps, props, {\n    components: components,\n    mdxType: \"MDXLayout\"\n  }), mdx(\"p\", null, \"I installed my \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://amzn.to/2SbjEtT\"\n  }, \"Yale assure smart door lock\"), \" over a year ago with healthy skepticism, but the lock has been working without issues for over a year and still on the original set of batteries.\"), mdx(\"h2\", {\n    \"id\": \"why-to-replace-your-deadbolt-with-a-smart-door-lock\"\n  }, \"Why to replace your deadbolt with a smart door lock\"), mdx(\"p\", null, \"For a long time I resisted removing a perfectly fine working deadbolt with a smart device. I was skeptical about how well they worked and if the convenience was worth the money.\"), mdx(\"p\", null, \"I finally decided to replace my deadbolt with a smart lock because I was putting in a new front door that I couldn't rekey my other locks with. I didn't want to have to carry an extra set of keys so I started to look for locks I could open with a code or smart home integration.\"), mdx(\"p\", null, \"Other convenience factors to consider include\"), mdx(\"ul\", null, mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Generating temporary access codes instead of a key that could be duplicated to friends, tenants, contractors, house sitters.\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Fewer keys to carry\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Remote unlock / lock\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Smart home routine integration\")), mdx(\"h2\", {\n    \"id\": \"important-considerations-for-smart-door-locks-to-me\"\n  }, \"Important considerations for smart door locks (to me)\"), mdx(\"ul\", null, mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Not a toy, must have a decent ANSI grading\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Not require a separate hub\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Not super bulky or dumb looking\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Alexa integration\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Backup key in case of battery failure\")), mdx(\"p\", null, \"My requirements narrowed the field down quite a bit allowing me to quickly arrive at the \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://amzn.to/2SbjEtT\"\n  }, \"Yale assure smart door lock\"), \". \"), mdx(\"blockquote\", null, mdx(\"p\", {\n    parentName: \"blockquote\"\n  }, \"It has an ANSI Grade 2 rating (superior security for residential usage). \")), mdx(\"p\", null, \"ANSI Grade 3 would be minimum grade and ANSI Grade 1 would be found more in commercial heavy duty applications. \"), mdx(\"p\", null, \"I have an Amazon Echo \", mdx(\"em\", {\n    parentName: \"p\"\n  }, \"plus\"), \" which includes a hub. The Yale Assure model I have includes a zigbee module which will wirelessly connect to the Amazon Echo plus' built in hub for integration with my smart home. No need to purchase something like a Smart Things hub.\"), mdx(\"p\", null, \"Lastly, I don't want to worry about dead batteries locking me out of the house so this model also has a keyhole for backup.\"), mdx(\"h2\", {\n    \"id\": \"how-to-the-yale-assure-smart-door-lock\"\n  }, \"How to the Yale Assure smart door lock\"), mdx(\"p\", null, \"Detailed unboxing and instructions for the \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://amzn.to/2SbjEtT\"\n  }, \"Yale smart lock\"), \" are in this video tutorial below.\"), mdx(Embed, {\n    src: \"https://www.youtube.com/embed/aqIPF-8FOjE\",\n    mdxType: \"Embed\"\n  }), mdx(\"p\", null, \"Yale includes everything you need including batteries and mounting screws for the deadbolt. As shown in the above video, you just need to unscrew your old deadbolt and put the new lock hardware in place. If you are installing the Yale lock in a new door drilling templates are included as well.\"), mdx(\"blockquote\", null, mdx(\"p\", {\n    parentName: \"blockquote\"\n  }, \"Install the red zigbee module before installing your batteries.\")), mdx(\"p\", null, \"The zigbee module won't be detected if you install it after you place the batteries in the smart lock. Once, batteries are installed you just set your master code for programming and other code for your family.\"), mdx(\"h3\", {\n    \"id\": \"yale-and-alexa-pairing-and-programming\"\n  }, \"Yale and Alexa pairing and programming\"), mdx(\"p\", null, \"Getting the Yale lock and the Echo plus to work together for Alexa voice control is easy to do as shown in this video below.\"), mdx(Embed, {\n    src: \"https://www.youtube.com/embed/g-hz7aqKDVE\",\n    mdxType: \"Embed\"\n  }), mdx(\"ol\", null, mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Enter your master code\"), mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Press 7 on the keypad then 1.\"), mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Yell at Alexa to discover your devices\"), mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"The Yale lock will start beeping.\"), mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"In the Alexa app on your phone you should see your lock appear.\")), mdx(\"p\", null, \"You can now ask Alexa to lock or unlock your door as well as check the status of it. To unlock your door Alexa will have you set an audible code so a burglar can't just loudly shout to open your door.\"), mdx(\"h2\", {\n    \"id\": \"real-performance-after-a-year-of-ownership\"\n  }, \"Real performance after a year of ownership\"), mdx(\"p\", null, \"As mentioned at the top of the article the Yale Assure smart lock is still on it's original batteries. It gets daily usage and hasn't had any issues. \"), mdx(\"p\", null, \"The only problem I have with it is sometimes when I swing the door closed my hand might graze the keypad causing the door to accidentally lock itself as the door is swinging shut causing the deadbolt bar to slam on the door frame. Despite this, I haven't broken anything.\"), mdx(\"p\", null, \"If you are setup for Z-Wave for your smart home it might be worth looking at the Schlage \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://amzn.to/2tkiST4\"\n  }, \"Z-Wave lock\"), \" as well, but I am very satisfied with the Yale. \"), mdx(\"p\", null, \"If you are in the market, and found this article helpful, consider purchasing the Yale smart door lock through \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://amzn.to/2SbjEtT\"\n  }, \"my affiliate link\"), \" to help support my site. Thanks for reading!\"));\n}\n;\nMDXContent.isMDXComponent = true;","excerpt":"I installed my  Yale assure smart door lock  over a year ago with healthy skepticism, but the lock has been working without issues for over…","fields":{"slug":"/yale-smart-door-lock-installation/","type":"posts"},"headings":[{"value":"Why to replace your deadbolt with a smart door lock","depth":2},{"value":"Important considerations for smart door locks (to me)","depth":2},{"value":"How to the Yale Assure smart door lock","depth":2},{"value":"Yale and Alexa pairing and programming","depth":3},{"value":"Real performance after a year of ownership","depth":2}],"frontmatter":{"title":"Yale smart door lock installation review","description":"The Yale smart lock I installed and reviewed a year ago is still the best smart door lock in 2020 in my opinion. See why as I cover installation and operation with Alexa in my post.","link":null,"date":"2020-02-02T00:00:00.000Z","tags":["yale","smart home","iot","post"],"draft":null,"hide":null,"cover":{"childImageSharp":{"fluid":{"base64":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAANCAYAAACpUE5eAAAACXBIWXMAAAsTAAALEwEAmpwYAAADWUlEQVQ4y22T/U+bZRSG38IQ3MjYJiwaNxNjjNFsiwE10WUaF5MtZsFgMpQgjoxlXyyOsYUhyhiSgF9LNFmCuiHhayujpS0fLS1t37ZveftBW6DdYDAd3Ywm/hWXp+/UxMQfTp7n/PBcuc99n0dJaBrt589S+c4+Xt/zAm9W7OL9g/v5/usv+ePXNR6upMneWSIeUknqIXxTdmw3hvnqSi9NXX20X7Ezr6mkIzESoTmUz5oa2b75cZ59ZgdNZ05Ttm0rT5WWsP+13UzcGuHP7D2yyxl5FCSla4Rck2jeKOtLDjK+I6yFTxKfOUpGrcY1dg2lveUCiqJQ+FgRFeUVcjeRn7+BhprDWAb7+P3ebQGmmQ8HSEglwxpLsSTLqVW5+4irQ+heM7r7J3yTFpRvu3sM4D+VZzIZZ1fHZRzmQR7eXeTBSkZgKnFNJxUaIOk7Qzbdy+r8j6zEr5GJDLKsdxHxCrCjtdUAmHLK8vKl8oz+8qUOLMP9PFhOkRUfE4ZCnaDzOj5LLUm1k7DzcxKzjYSmLhH1dBOYGkL55MSJv5Xl/QvN9W0XP2V0oE+AC9y/nWI+5BcPQxLIkHhrYyXhZ3EuwKLuZ2HOyUKon6DLhXKq4ej/AlvOX2Dgh6sGcF08zKWcS9NpMeMYvUUq0EN0pkkCqUObPE1ippLxoV6Uho8/+i/Q9Gjk+to6rn7TI/4tGmuTg0VUD3NeN0G3TxR7iPqt6L5pOSfECitu2xhKfU21Acgl+ygYE0WFRewoK+WLthZ+u5vml3SSeNBHNOBlxjaKxz5OOpYST+NSURJajJiqoU47BfjhYQO0rXgjT27dzBPFmyiQPlcXzzayfmeB1WSMpHi4qAexDv2Muf+6+KYT8fuJqgGBBZjzePFOTKIcq6ul0CSpNjfzXWcnDdVV8mNeIk+A504d534mJeYHqT50gJdffJ6aqkNMmEdEcVjG9Qs0gO4PorlnmXU4UE7WH+GNinK6W9s4+NbblO/azYF9e9lYsIHj4u9aSr5UUGXvq69QumULH1S+i+PmMKrTLYqcsswu45weG8c2PILS1nyO53bu5OntZcboJZuKKSspoUAWvKbqPVlaTcZVxR8HwWk7s3YrLpuDsMdvqNLcHgJOFzNWC1PmG/wF2LSXqL/Htk8AAAAASUVORK5CYII=","aspectRatio":1.6,"src":"/static/4cd55645d1c4220ef923b896d6496667/6caa6/yale-smart-lock.png","srcSet":"/static/4cd55645d1c4220ef923b896d6496667/a4b17/yale-smart-lock.png 192w,\n/static/4cd55645d1c4220ef923b896d6496667/1ef16/yale-smart-lock.png 384w,\n/static/4cd55645d1c4220ef923b896d6496667/6caa6/yale-smart-lock.png 768w","srcWebp":"/static/4cd55645d1c4220ef923b896d6496667/25278/yale-smart-lock.webp","srcSetWebp":"/static/4cd55645d1c4220ef923b896d6496667/a278a/yale-smart-lock.webp 192w,\n/static/4cd55645d1c4220ef923b896d6496667/2474b/yale-smart-lock.webp 384w,\n/static/4cd55645d1c4220ef923b896d6496667/25278/yale-smart-lock.webp 768w","sizes":"(max-width: 768px) 100vw, 768px","presentationWidth":768,"presentationHeight":480}}}}},{"id":"a2b3f1ac-0f20-5bd0-b086-238fe1c6df02","body":"var _excluded = [\"components\"];\nfunction _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }\nfunction _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }\nfunction _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }\n/* @jsxRuntime classic */\n/* @jsx mdx */\n\nvar _frontmatter = {\n  \"title\": \"Nooie aurora smart light review\",\n  \"cover\": \"./nooie-aurora-smart-light.png\",\n  \"date\": \"2020-01-27T00:00:00.000Z\",\n  \"description\": \"The nooie aurora smart LED light bulb may be a solution for color controlled smart lights and a competitor to Philips Hue.\",\n  \"tags\": [\"nooie\", \"smart lights\", \"smart home\", \"iot\", \"post\"]\n};\nvar makeShortcode = function makeShortcode(name) {\n  return function MDXDefaultShortcode(props) {\n    console.warn(\"Component \" + name + \" was not imported, exported, or provided by MDXProvider as global scope\");\n    return mdx(\"div\", props);\n  };\n};\nvar Embed = makeShortcode(\"Embed\");\nvar layoutProps = {\n  _frontmatter: _frontmatter\n};\nvar MDXLayout = \"wrapper\";\nreturn function MDXContent(_ref) {\n  var components = _ref.components,\n    props = _objectWithoutProperties(_ref, _excluded);\n  return mdx(MDXLayout, _extends({}, layoutProps, props, {\n    components: components,\n    mdxType: \"MDXLayout\"\n  }), mdx(\"p\", null, \"The \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://amzn.to/2O5cAxR\"\n  }, \"nooie aurora smart LED bulbs\"), \" come in a 4 pack for around $56 dollars on Amazon which works out to $14 dollars per bulb. A similar bulb from Philips Hue would cost several times more and require a hub. \"), mdx(\"p\", null, \"The nooie bulbs can be controlled directly with the nooie app or through Amazon Alexa so they could be considered a good value for people wanting to add dimmable and color controlled smart lighting without a hub or replacing light switches.\"), mdx(Embed, {\n    src: \"https://www.youtube.com/embed/i-0di_GEgdk\",\n    mdxType: \"Embed\"\n  }), mdx(\"h3\", {\n    \"id\": \"nooie-lb126-led-specifications\"\n  }, \"nooie LB126 LED specifications\"), mdx(\"ul\", null, mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"800 lumens\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"2800-6000 K light range\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"10 watts per bulb\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"E26 base\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Dimmable in app or Alexa\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Voice control (Alexa)\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Scheduling\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"2.4ghz b/g/n Wi-Fi required\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"No hub needed\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"UL-listed\")), mdx(\"h3\", {\n    \"id\": \"pairing-process\"\n  }, \"Pairing process\"), mdx(\"p\", null, \"To use the nooie smart LED bulbs you need to pair them with your smart home's Wi-Fi connection. You start the pairing process by downloading the nooie home app from the App Store. Through a series of on-off switches the bulb will enter pairing mode. From there you follow the nooie home apps instructions to pair your phone and light and then save your Wi-Fi network credentials to the light.\"), mdx(\"blockquote\", null, mdx(\"p\", {\n    parentName: \"blockquote\"\n  }, \"Make sure your phone is at least temporarily on the same network as the bulb during pairing\")), mdx(\"p\", null, \"I have a \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"../secure-iot-with-ubiquiti-vlan-firewall-rules/\"\n  }, \"separate Wi-Fi network\"), \" for my IoT devices to keep them segmented off from the rest of my devices and it wasn't finding the bulb until I switched my phone over to the same network.\"), mdx(\"p\", null, \"Once the bulb is paired you have access to the dimming, color selection, and scheduling features in the nooie home app.\"), mdx(\"h3\", {\n    \"id\": \"alexa-pairing-for-voice-control\"\n  }, \"Alexa pairing for voice control\"), mdx(\"p\", null, \"Alexa pairing was much easier. In the Alexa app, look for the nooie home skill and enable that with your nooie account credentials. After, run Discover Devices and Alexa will find your smart bulb.\"), mdx(\"p\", null, \"When that is done you can issue Alexa voice commands to the name of your bulb for on and off, dimming, and color changes.\"), mdx(\"h3\", {\n    \"id\": \"overall-impressions\"\n  }, \"Overall impressions\"), mdx(\"p\", null, \"The light is very bright so a good value there. The white balance adjustments offer a good range. Picking colors in the color wheel didn't seem like the most accurate representation of the colors I was going for, but for a pleasing effect you can find lots of great colors to choose from. Alexa integration hasn't had any issues. It is easy to control the nooie smart bulbs with voice commands and is my preferred use case.\"), mdx(\"p\", null, \"It would make a good value for someone wanting inexpensive dimmable and/or color changing bulbs for a lamp and didn't want the added expense of a hub.\"), mdx(\"p\", null, \"Learn more about the \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://amzn.to/2O5cAxR\"\n  }, \"nooie aurora smart LED bulbs on amazon\"), \".\"));\n}\n;\nMDXContent.isMDXComponent = true;","excerpt":"The  nooie aurora smart LED bulbs  come in a 4 pack for around $56 dollars on Amazon which works out to $14 dollars per bulb. A similar bulb…","fields":{"slug":"/nooie-aurora-smart-light-review/","type":"posts"},"headings":[{"value":"nooie LB126 LED specifications","depth":3},{"value":"Pairing process","depth":3},{"value":"Alexa pairing for voice control","depth":3},{"value":"Overall impressions","depth":3}],"frontmatter":{"title":"Nooie aurora smart light review","description":"The nooie aurora smart LED light bulb may be a solution for color controlled smart lights and a competitor to Philips Hue.","link":null,"date":"2020-01-27T00:00:00.000Z","tags":["nooie","smart lights","smart home","iot","post"],"draft":null,"hide":null,"cover":{"childImageSharp":{"fluid":{"base64":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAANCAYAAACpUE5eAAAACXBIWXMAAAsTAAALEwEAmpwYAAADn0lEQVQ4y02Sa0ybVQCGPzN1IqOlN6DlWym1tKUUWlYu5aazwLiVMdmQzaFsyoJOGBl3FyaZDk2EuSlQYQJjAoXBhGnmDxPJFhUwcc64BSEZmxGmCVmiif42j8f98iRvzvlxznPe875H6jqyQefLq7S/9CMnDv1Eec40j0tdpKcsUbF3hZTkJcKkbrSPvUhkqJ/o8F3ow/PQK4oxhB5Eu6WdJ6SPeFSaJOSRIaTguQeMvX+fkZ41gv33OHrgOnWvLfP/UVP9M1uleqIVZcjqQnRhPiIEXCW9jkEVxBq3gs22iSzfRbrUv8lkYJ3xvjVmhtZorPmGi+c3HoL+/uOfh3Pfe/eFi2PI4aUYdYUkWfdil/dj1b2BST+LM/EeXu9fPGVZR5oO/MZU4BfG+la4NLxK69HPONU8x4P1P7l7+3duXF+lqiSAZkslsqpYAPPwZVWTk3qYopx24g1v446fJDPtC8zyKNLl878yPXiHYOAWMyO3aa0bo6G2i82NW9xZXmTx2izP5dei2VqArM0TeprI8HTUoakkmPdQ6mvmGU8zud4Oksy1SLPimZc/XmZq4AemR25wsnmU3KwqFuZn+f7bTxntfweP3U+EYicGTRYGbTp6rUcoGTkiFZMhG1mXzXZdDnpNOtLc0LIA3mRycImp4UV6TgfRhnmoO9LA1bkBKvzPo1N4BcxLlNojgG5x0EmUxkGUNoFItQOtwo5GYUOjtAqHwzdFdt8RHF5g/MLXtDX1Yrf4iIn24rBnoFI6RTY5GPVpRKpcqFTxqNV2AbISrftPNgG3iMvMRKhiRcsjC0wMfClanmHwrV78mWUUZJeQ5srGFZ9KnNFF9o5cPLY0wkMtOENsJIdZUT4ZS9g2E0qFiXCFEbXSiE65HWnizW7GWzsYb2phvLGV7kOvkO/yYDXaSTBZkTUxmHSx7JBtFEQncsySTIPbRnWiHZ/VQobJjNNgEntkUZZefJvjzUwL2GxbGyMNoq104c6diduZhDdFBB8VIyJIIC7GQZ7WQZPLzdkSD2eKUznrT6OnxMu7hVmcys+gIy8F6UpzO5+3tnD1RCNX2htoLN9HSqIbr8dDVrqX+LgE9JFmbDojVQ4HZ4qy6d/9LL27fXzg38mH/l2cK9pDn7+cQFkR0nxHC191HGf+ZD3XOl9ltr6YGp+NSpeSSuc2DiSGcDgjis6CGCYOJhN8wccn+/OEchmtzOVCRSkj5Qe5uK9arMv5FwrQGo6sAeB1AAAAAElFTkSuQmCC","aspectRatio":1.6,"src":"/static/f9bb8b3875ed7bacb37d5d6a7d7a4d9d/6caa6/nooie-aurora-smart-light.png","srcSet":"/static/f9bb8b3875ed7bacb37d5d6a7d7a4d9d/a4b17/nooie-aurora-smart-light.png 192w,\n/static/f9bb8b3875ed7bacb37d5d6a7d7a4d9d/1ef16/nooie-aurora-smart-light.png 384w,\n/static/f9bb8b3875ed7bacb37d5d6a7d7a4d9d/6caa6/nooie-aurora-smart-light.png 768w,\n/static/f9bb8b3875ed7bacb37d5d6a7d7a4d9d/5d2c5/nooie-aurora-smart-light.png 1000w","srcWebp":"/static/f9bb8b3875ed7bacb37d5d6a7d7a4d9d/25278/nooie-aurora-smart-light.webp","srcSetWebp":"/static/f9bb8b3875ed7bacb37d5d6a7d7a4d9d/a278a/nooie-aurora-smart-light.webp 192w,\n/static/f9bb8b3875ed7bacb37d5d6a7d7a4d9d/2474b/nooie-aurora-smart-light.webp 384w,\n/static/f9bb8b3875ed7bacb37d5d6a7d7a4d9d/25278/nooie-aurora-smart-light.webp 768w,\n/static/f9bb8b3875ed7bacb37d5d6a7d7a4d9d/36ebb/nooie-aurora-smart-light.webp 1000w","sizes":"(max-width: 768px) 100vw, 768px","presentationWidth":768,"presentationHeight":481}}}}},{"id":"dce55945-37a4-54cd-9760-44deaccb9a6e","body":"var _excluded = [\"components\"];\nfunction _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }\nfunction _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }\nfunction _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }\n/* @jsxRuntime classic */\n/* @jsx mdx */\n\nvar _frontmatter = {\n  \"title\": \"Automated web accessibility testing with Nightwatch.js and axe\",\n  \"cover\": \"./nightwatch-accessibility-testing.png\",\n  \"date\": \"2020-01-20T00:00:00.000Z\",\n  \"description\": \"There are quite a few web accessibility testing tools out there, but I thought I'd see if any could fit in with my existing automated Nightwatch tests.\",\n  \"tags\": [\"quality assurance\", \"accessibility testing\", \"software testing\", \"nightwatchjs\", \"post\"]\n};\nvar makeShortcode = function makeShortcode(name) {\n  return function MDXDefaultShortcode(props) {\n    console.warn(\"Component \" + name + \" was not imported, exported, or provided by MDXProvider as global scope\");\n    return mdx(\"div\", props);\n  };\n};\nvar Embed = makeShortcode(\"Embed\");\nvar layoutProps = {\n  _frontmatter: _frontmatter\n};\nvar MDXLayout = \"wrapper\";\nreturn function MDXContent(_ref) {\n  var components = _ref.components,\n    props = _objectWithoutProperties(_ref, _excluded);\n  return mdx(MDXLayout, _extends({}, layoutProps, props, {\n    components: components,\n    mdxType: \"MDXLayout\"\n  }), mdx(\"h2\", {\n    \"id\": \"web-content-accesibility-guidelines-and-the-ada\"\n  }, \"Web Content Accesibility Guidelines and the ADA\"), mdx(\"p\", null, \"Testing for compliance with web content accessibility guidelines or WCAG is becoming increasingly important. Legal precedent on how the Americans with Disabilities Act applies to websites and apps is finally catching up with the web. \"), mdx(\"p\", null, \"In the infamous Dominos Pizza case the 9th circuit court stated,\"), mdx(\"blockquote\", null, mdx(\"p\", {\n    parentName: \"blockquote\"\n  }, \"We agree with the district court in this case\\u2014and the many other district courts that have confronted this issue in similar contexts\\u2014that the ADA applies to Domino's website and app, which connect customers to the goods and services of Domino's physical restaurants.\")), mdx(\"p\", null, \"I'm strongly of the opinion that web content accessibility guidelines are good design principles and are good to follow regardless of the threat of legal implications. \"), mdx(\"p\", null, \"So what accessibility testing tools can we use to bake accessibility testing into our selenium test suites?\"), mdx(\"h2\", {\n    \"id\": \"nightwatchjs-and-axe-for-accessibility-testing\"\n  }, \"Nightwatch.js and Axe for Accessibility Testing\"), mdx(\"p\", null, \"Nightwatch is a great browser automation framework. Axe is an accessibility rule verification tool (it is even used Chrome Lighthouse). \"), mdx(\"p\", null, \"Wouldn't it be great if the two could be combined? Some people already thought of that so I built upon their work to do automated web accessibility testing in my existing automation suite.\"), mdx(\"p\", null, \"I found a couple npm packages that let you inject axe-core into Nightwatch as a custom command, but one didn't work with newer Chrome versions and the reporting on the other wasn't verbose enough for me.\"), mdx(\"p\", null, \"I built on that pattern with \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://www.npmjs.com/package/nightwatch-axe-verbose\"\n  }, \"nightwatch-axe-verbose\"), \" npm package.\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-sh\"\n  }, \"npm install nightwatch-axe-verbose -s\\n\")), mdx(Embed, {\n    src: \"https://www.youtube.com/embed/nSodkqB-838\",\n    mdxType: \"Embed\"\n  }), mdx(\"h2\", {\n    \"id\": \"writing-automated-accessibility-tests\"\n  }, \"Writing Automated Accessibility Tests\"), mdx(\"p\", null, \"To write Nightwatch tests to perform accessibility assertions with nightwatch-axe-verbose you need to add to your \", mdx(\"em\", {\n    parentName: \"p\"\n  }, \"nightwatch.json\"), \" file the location of the package inside the custom commands area\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-json\"\n  }, \"\\\"custom_commands_path\\\": [\\\"./node_modules/nightwatch-axe-verbose/src/commands\\\"]\\n\")), mdx(\"p\", null, \"This will allow you to use axe assertions in your test.\"), mdx(\"p\", null, \"With the accessibility testing package added as a custom command you can use it in your Nightwatch.js tests. Here are some example accessibility tests below.\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-js\"\n  }, \"module.exports = {\\n    '@tags': ['accessibility'],\\n    'Inaccessible site': function (browser) {\\n\\n        browser\\n            .url('https://www.w3.org/WAI/demos/bad/before/home.html')\\n            .assert.title('Welcome to CityLights! [Inaccessible Home Page]')\\n            .axeInject()\\n            .axeRun('body', {\\n                'color-contrast': { enabled: false },\\n            })\\n            .end();\\n    },\\n    'Accessible site': function (browser) {\\n\\n        browser\\n            .url('https://www.w3.org/WAI/demos/bad/after/home.html')\\n            .assert.title('Welcome to CityLights! [Accessible Home Page]')\\n            .axeInject()\\n            .axeRun('body', {\\n                rules: {}\\n            })\\n            .end();\\n    }\\n}\\n\")), mdx(\"p\", null, \".axeInject() makes the custom commands available and .axeRun() will execute all the tests in the axe-core ruleset unless customized. In the examples above, the first test provides an example of how to disable specific rules or you can specify specific rules *to run as well.\"), mdx(\"h3\", {\n    \"id\": \"useful-test-case-output\"\n  }, \"Useful test case output\"), mdx(\"p\", null, \"It is called -verbose because the package will report each passing accessibility rule run as a test case along with a count of how many controls the rule was run against. This is to ensure confidence in the results.\"), mdx(\"h4\", {\n    \"id\": \"passing-test-output\"\n  }, \"Passing test output\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-sh\"\n  }, \"\\u221A Passed [ok]: aXe rule: aria-allowed-role (2 elements checked)\\n\\u221A Passed [ok]: aXe rule: aria-hidden-body (1 elements checked)\\n\\u221A Passed [ok]: aXe rule: aria-hidden-focus (156 elements checked)\\n\\u221A Passed [ok]: aXe rule: aria-required-attr (2 elements checked)\\n\\u221A Passed [ok]: aXe rule: aria-required-children (2 elements checked)\\n...\\n\")), mdx(\"p\", null, \"Each failing test is reported per control per rule to ensure you get a complete picture of what is violating WCAG accessibility on your site so you can fix it all at once. \", mdx(\"em\", {\n    parentName: \"p\"\n  }, \"Other libraries I was looking would exit on first failure, hiding downstream issues.\")), mdx(\"h4\", {\n    \"id\": \"failing-test-output\"\n  }, \"Failing test output\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-sh\"\n  }, \"\\xD7 Failed [fail]: (aXe rule: heading-order - Heading levels should only increase by one  \\n        In element: h5\\n\\xD7 Failed [fail]: (aXe rule: image-alt - Images must have alternate text\\n        In element: .small-logo\\n\\xD7 Failed [fail]: (aXe rule: label - Form elements must have labels\\n        In element: #SearchInput)\\n\")), mdx(\"p\", null, mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://github.com/reallymello/nightwatchTutorials/tree/master/accessibilityExample\"\n  }, \"Nightwatch accessible test project on my GitHub\")));\n}\n;\nMDXContent.isMDXComponent = true;","excerpt":"Web Content Accesibility Guidelines and the ADA Testing for compliance with web content accessibility guidelines or WCAG is becoming…","fields":{"slug":"/accessibility-testing-with-nightwatchjs/","type":"posts"},"headings":[{"value":"Web Content Accesibility Guidelines and the ADA","depth":2},{"value":"Nightwatch.js and Axe for Accessibility Testing","depth":2},{"value":"Writing Automated Accessibility Tests","depth":2},{"value":"Useful test case output","depth":3},{"value":"Passing test output","depth":4},{"value":"Failing test output","depth":4}],"frontmatter":{"title":"Automated web accessibility testing with Nightwatch.js and axe","description":"There are quite a few web accessibility testing tools out there, but I thought I'd see if any could fit in with my existing automated Nightwatch tests.","link":null,"date":"2020-01-20T00:00:00.000Z","tags":["quality assurance","accessibility testing","software testing","nightwatchjs","post"],"draft":null,"hide":null,"cover":{"childImageSharp":{"fluid":{"base64":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAYAAAB/Ca1DAAAACXBIWXMAAAsTAAALEwEAmpwYAAAB30lEQVQoz3VSuY7UQBCdnFPjPnz2+JiDtT2H51gNyw5iF1ZcQkLkJPwBv0CGyCAjR0h8AHwA0f4A/0BCQkjwqHKPjTUSwXN1V5efqt6rnnE1Bp7qQCOiXKQ1wg44154JxrX1HO1ZIwk89JqL8T1EBOO5GIYKo0hhYixuDWSdy2OJnM7j6B9ZE2PftYQ1mZbIxFWMxRWEoo/FUOFh1ce9qYMHC4vTQuAkF7iT25gFTKTrZhgtYaAEimGCD2/f4NPH93h8tkMkrxOBwjyT2IwFFhSPJ4J+UvCVRrCXgGH2aAk9KXB7FODy22f8/PEdr56dw8hrGEZu3QWPdzRQSIiM9ePIcmShzcc+5zqEiTE4SQUuv37Bn9+/8GKVospo3JnEo6WDs5mDp2sHOxr5fO5QXuDJul93fUFSlImiMxuyJ0zpc/d0h9fPN3j3covjeWkNIPEvKgf3iaQgM5YjgdXIasjjM+E0lZilJE2qkRJhTKb2uNVxbODT6PLmjbZ143lUpMlh6zwj1C7p5tajm/1q8cjNurUuV2WO9aysURVHWM+n2BCW0xKLosCS3rerRf22XVWYJIbIpF23zh5yM71AOghkHyG53TxyMYMLmnu7rwexi5qwS3RYcJhv7v8D1/wFrnQ3UTrye2UAAAAASUVORK5CYII=","aspectRatio":1.7777777777777777,"src":"/static/bb735539796f771d95a8da1745266a2c/6caa6/nightwatch-accessibility-testing.png","srcSet":"/static/bb735539796f771d95a8da1745266a2c/a4b17/nightwatch-accessibility-testing.png 192w,\n/static/bb735539796f771d95a8da1745266a2c/1ef16/nightwatch-accessibility-testing.png 384w,\n/static/bb735539796f771d95a8da1745266a2c/6caa6/nightwatch-accessibility-testing.png 768w,\n/static/bb735539796f771d95a8da1745266a2c/3f078/nightwatch-accessibility-testing.png 1152w,\n/static/bb735539796f771d95a8da1745266a2c/bf181/nightwatch-accessibility-testing.png 1346w","srcWebp":"/static/bb735539796f771d95a8da1745266a2c/25278/nightwatch-accessibility-testing.webp","srcSetWebp":"/static/bb735539796f771d95a8da1745266a2c/a278a/nightwatch-accessibility-testing.webp 192w,\n/static/bb735539796f771d95a8da1745266a2c/2474b/nightwatch-accessibility-testing.webp 384w,\n/static/bb735539796f771d95a8da1745266a2c/25278/nightwatch-accessibility-testing.webp 768w,\n/static/bb735539796f771d95a8da1745266a2c/e7b8c/nightwatch-accessibility-testing.webp 1152w,\n/static/bb735539796f771d95a8da1745266a2c/89bf0/nightwatch-accessibility-testing.webp 1346w","sizes":"(max-width: 768px) 100vw, 768px","presentationWidth":768,"presentationHeight":434}}}}},{"id":"dea4c421-71f0-5306-9c1b-27f7c51b9416","body":"var _excluded = [\"components\"];\nfunction _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }\nfunction _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }\nfunction _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }\n/* @jsxRuntime classic */\n/* @jsx mdx */\n\nvar _frontmatter = {\n  \"title\": \"How to fix an unresponsive Unifi Cloud Key\",\n  \"cover\": \"./cloudkey.png\",\n  \"date\": \"2020-01-17T00:00:00.000Z\",\n  \"description\": \"If your Unifi cloud key has a white led, is unresponsive, or hanging at login read on to learn how to fix it.\",\n  \"tags\": [\"ubiquiti\", \"unifi\", \"networking\", \"post\"]\n};\nvar makeShortcode = function makeShortcode(name) {\n  return function MDXDefaultShortcode(props) {\n    console.warn(\"Component \" + name + \" was not imported, exported, or provided by MDXProvider as global scope\");\n    return mdx(\"div\", props);\n  };\n};\nvar Embed = makeShortcode(\"Embed\");\nvar layoutProps = {\n  _frontmatter: _frontmatter\n};\nvar MDXLayout = \"wrapper\";\nreturn function MDXContent(_ref) {\n  var components = _ref.components,\n    props = _objectWithoutProperties(_ref, _excluded);\n  return mdx(MDXLayout, _extends({}, layoutProps, props, {\n    components: components,\n    mdxType: \"MDXLayout\"\n  }), mdx(\"p\", null, mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"In general\"), \", I've been very happy with my \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://amzn.to/3fcuniL\"\n  }, \"Unifi cloud key\"), \" for managing my Unifi network, but there have been 2 occasions over the past couple years where the cloud key has encountered \", mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"unrecoverable failures\"), \".\"), mdx(\"p\", null, \"In both cases, the cloud key's status LED was showing a white LED light instead of the normal blue. \"), mdx(\"p\", null, \"When this occurs I'm unable to login to the unifi controller software's web interface. It hangs at login indefinetly after submitting my username and password.\"), mdx(\"blockquote\", null, mdx(\"p\", {\n    parentName: \"blockquote\"\n  }, \"The cloud key uses a mongo database that does not handle sudden shutdowns very well. \")), mdx(\"p\", null, \"This means any time there is a power loss you are at risk of corrupting the cloud key's mongo database \\uD83D\\uDE22\"), mdx(\"p\", null, \"The first time my cloud key failed my neighborhood was dealing with semi-frequent blackouts for 4 hours at a time before the power utility could restore power--some sort of underground powerline issue.\"), mdx(\"p\", null, \"As a workaround, I ended up buying an \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://amzn.to/3aT6Qj8\"\n  }, \"APC UPS 1500VA UPS\"), \" that let's you connect another battery to it which gave me a ridiculous uptime for my modem + USG + US-8-60W switch + UAP AC Pro + UC-CK cloud key at 8 hours. I might not have power, but I'll have the internet to keep me distracted all day \\uD83D\\uDE02\"), mdx(\"p\", null, \"The workaround worked well until a couple weeks ago when it randomly happened again without a power outage as the cause.\"), mdx(\"p\", null, \"So, for my future self, and the benefit of others, below are the steps to recover your UC-CK cloud key.\"), mdx(Embed, {\n    src: \"https://www.youtube.com/embed/OdyedKhqW8M\",\n    mdxType: \"Embed\"\n  }), mdx(\"h2\", {\n    \"id\": \"solution-1-try-restarting-the-unifi-service\"\n  }, \"Solution 1: Try restarting the Unifi Service\"), mdx(\"p\", null, \"Using \", mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"ssh\"), \", ssh to \", mdx(\"em\", {\n    parentName: \"p\"\n  }, \"yourUsername\"), \"@\", mdx(\"em\", {\n    parentName: \"p\"\n  }, \"theIpAddressOfYourCloudKey\")), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-sh\"\n  }, \"sudo service unifi restart\\n\")), mdx(\"p\", null, \"or\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-sh\"\n  }, \"/sbin/ubnt-systool reboot\\n\")), mdx(\"p\", null, \"If that doesn't work you'll need to go to solution 2, resetting to defaults and restoring from backup.\"), mdx(\"h2\", {\n    \"id\": \"solution-2-reset-the-cloud-key-to-defaults-and-restoring-settings-from-backup\"\n  }, \"Solution 2: Reset the cloud key to defaults and restoring settings from backup\"), mdx(\"blockquote\", null, mdx(\"p\", {\n    parentName: \"blockquote\"\n  }, \"This wipes out all the backups stored on your cloud key so move them off the cloud key before proceeding\")), mdx(\"ol\", null, mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Use a tool like WinSCP to connect to your cloud key's filesystem.\"), mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Change to \", mdx(\"strong\", {\n    parentName: \"li\"\n  }, \"/data/autobackup\")), mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Verify you have backups there. Download them somewhere safe--not on the cloud key.\"), mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Using \", mdx(\"strong\", {\n    parentName: \"li\"\n  }, \"ssh\"), \" login to ssh \", mdx(\"em\", {\n    parentName: \"li\"\n  }, \"yourUsername\"), \"@\", mdx(\"em\", {\n    parentName: \"li\"\n  }, \"theIpAddressOfYourCloudKey\")), mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Run reset2defaults\")), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-sh\"\n  }, \"/sbin/ubnt-systool reset2defaults\\n\")), mdx(\"ol\", {\n    \"start\": 6\n  }, mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"After a good amount of time your cloud key will restart and will likely have a different IP address.\"), mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Use the \", mdx(\"a\", {\n    parentName: \"li\",\n    \"href\": \"https://www.ui.com/download/utilities/default/default/ubiquiti-discovery-tool-chrome-extension/\"\n  }, \"ubiquiti device discovery tool - chrome extension\"), \" to find your cloud key's new ip address on the network.\"), mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Navigate to http://\", mdx(\"em\", {\n    parentName: \"li\"\n  }, \"theIpAddressOfYourCloudKey\"), \" and login as \", mdx(\"em\", {\n    parentName: \"li\"\n  }, \"root\"), \" / \", mdx(\"em\", {\n    parentName: \"li\"\n  }, \"ubnt\")), mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Select the option to restore from your backup and point to the saved backup from earlier.\")), mdx(\"p\", null, \"This \", mdx(\"em\", {\n    parentName: \"p\"\n  }, \"should\"), \" restore everything back like you had it. If you see oddities in the unifi dashboard like missing a USG just give it more time to catch up.\"), mdx(\"blockquote\", null, mdx(\"p\", {\n    parentName: \"blockquote\"\n  }, \"But what if my backups have a an newer, incompatible, firmware version than what the cloud key reset to? \\uD83D\\uDE31\")), mdx(\"p\", null, \"This happened to me. The reset put me on an older ubiquiti firmware version than the cloud key backups somehow. To use the backups you need to upgrade your cloud key firmware to the same version.\"), mdx(\"ol\", null, mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Using \", mdx(\"strong\", {\n    parentName: \"li\"\n  }, \"ssh\"), \" login to ssh://root@\", mdx(\"em\", {\n    parentName: \"li\"\n  }, \"theIpAddressOfYourCloudKey\"), \" password \", mdx(\"em\", {\n    parentName: \"li\"\n  }, \"ubnt\")), mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Navigate to the \", mdx(\"a\", {\n    parentName: \"li\",\n    \"href\": \"https://www.ui.com/download/unifi/\"\n  }, \"ubiquiti downloads site\"), \" and locate the firmware version that matches your backup.\"), mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Note the url path to the \", mdx(\"em\", {\n    parentName: \"li\"\n  }, \".deb\"), \" file of the firmware.\"), mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"In the ssh terminal\")), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-sh\"\n  }, \"cd /tmp\\nwget *path you copied to the .deb file here*\\ndpkg -i unifi_sysvinit_all.deb\\n\")), mdx(\"p\", null, \"Once that finishes installing\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-sh\"\n  }, \"rm unifi_sysvinit_all.deb\\n\")), mdx(\"ol\", {\n    \"start\": 5\n  }, mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Navigate to http://\", mdx(\"em\", {\n    parentName: \"li\"\n  }, \"theIpAddressOfYourCloudKey\"), \" and login as username: \", mdx(\"em\", {\n    parentName: \"li\"\n  }, \"root\"), \" password: \", mdx(\"em\", {\n    parentName: \"li\"\n  }, \"ubnt\")), mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Select the option to restore from your backup and point to the saved backup from earlier.\"), mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Login to the cloud key's website using your original credentials (now that it restored the backup) and change your ip address for the cloud key back to what it was originally.\")), mdx(\"p\", null, \"Your Unifi cloud key's LED light should now be back to blue from white, responsive, and working with your old configuration again. \"), mdx(\"h2\", {\n    \"id\": \"next-steps-best-solution\"\n  }, \"Next steps: Best Solution\"), mdx(\"p\", null, \"Since the original release Ubiquiti has put out the \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://amzn.to/2NwRUyz\"\n  }, \"Unifi Cloud Key Gen2\"), \" which largely solves this problem by including a backup battery that allows it to safely shut down when the power goes out.\"), mdx(\"p\", null, \"I'll be \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://amzn.to/2NwRUyz\"\n  }, \"upgrading\"), \" to that shortly and reporting back. Thanks for reading.\"));\n}\n;\nMDXContent.isMDXComponent = true;","excerpt":"In general , I've been very happy with my  Unifi cloud key  for managing my Unifi network, but there have been 2 occasions over the past…","fields":{"slug":"/unifi-cloud-key-fix/","type":"posts"},"headings":[{"value":"Solution 1: Try restarting the Unifi Service","depth":2},{"value":"Solution 2: Reset the cloud key to defaults and restoring settings from backup","depth":2},{"value":"Next steps: Best Solution","depth":2}],"frontmatter":{"title":"How to fix an unresponsive Unifi Cloud Key","description":"If your Unifi cloud key has a white led, is unresponsive, or hanging at login read on to learn how to fix it.","link":null,"date":"2020-01-17T00:00:00.000Z","tags":["ubiquiti","unifi","networking","post"],"draft":null,"hide":null,"cover":{"childImageSharp":{"fluid":{"base64":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAYAAAB/Ca1DAAAACXBIWXMAAAsTAAALEwEAmpwYAAABt0lEQVQoz31Sa2+CQBDs//81/UKTpmo19mE0VqhCVPCBYEStPEQQ8HB6ey1Wa9pNNrvZu5ubnd2bNE2RZRm22y3iOMZ+vxeeJAnCMEQUReKc7hV3f/v52U1RME1TPCZQilSbz+cYj8fI81x8kPBa8v24iOf5CZAYaZoG13XheZ5wYmcYBvr9PoAjcnYAcnL2nTMci9qRgR2+iF0BBkGA9XrNJQgxGo1g6ENMPyJUVRtPAwcvQweq7aKhL9GZriFzl2QL46UvPrgA9H0fuq7jsVoVOQHqwyFsN4bUMXGnWLjt2ChrDu6VGaYcxPrYQrU28MIYh0N2DTiZTNDt9bDb7UTLBOj4MZqGA9ny8fyu476pQeE52ZFl/7dM0yYnDYnhl4ZkOV7rNchvbfSUDh7LD7AsS5ykafb3UHw/gMeZFoCDwYCPBJAkCbKiYLlcIeKbYM5mKJVKqNfrF+tzCbjZiGEEHIwASU9ap263i0ajIdisVissFgsURu9qtZrIabWuGQY/DAmQNCWmlUpF7KSYPNeWmFPearXQbrevAVVVxYYzpMEUe3iuoW3bgi3pNuPtFpFqjLFTy5/ORDgDfvZsuQAAAABJRU5ErkJggg==","aspectRatio":1.7777777777777777,"src":"/static/fa9019a186d25f41129a7744c60b9b74/6748f/cloudkey.png","srcSet":"/static/fa9019a186d25f41129a7744c60b9b74/a4b17/cloudkey.png 192w,\n/static/fa9019a186d25f41129a7744c60b9b74/1ef16/cloudkey.png 384w,\n/static/fa9019a186d25f41129a7744c60b9b74/6748f/cloudkey.png 609w","srcWebp":"/static/fa9019a186d25f41129a7744c60b9b74/c9da5/cloudkey.webp","srcSetWebp":"/static/fa9019a186d25f41129a7744c60b9b74/a278a/cloudkey.webp 192w,\n/static/fa9019a186d25f41129a7744c60b9b74/2474b/cloudkey.webp 384w,\n/static/fa9019a186d25f41129a7744c60b9b74/c9da5/cloudkey.webp 609w","sizes":"(max-width: 609px) 100vw, 609px","presentationWidth":609,"presentationHeight":341}}}}},{"id":"8fcc97fd-4d8f-51e7-890a-eed506b3033f","body":"var _excluded = [\"components\"];\nfunction _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }\nfunction _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }\nfunction _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }\n/* @jsxRuntime classic */\n/* @jsx mdx */\n\nvar _frontmatter = {\n  \"title\": \"TP Link HS200 wifi smart switch installation with kasa\",\n  \"cover\": \"./hs200-kasa-switch-installation.png\",\n  \"date\": \"2020-01-16T00:00:00.000Z\",\n  \"description\": \"If you are tired of telling your kids to shut off the lights or like flexible outdoor light scheduling smart light switches are for you.\",\n  \"tags\": [\"tp-link\", \"kasa\", \"smart home\", \"iot\", \"post\"]\n};\nvar makeShortcode = function makeShortcode(name) {\n  return function MDXDefaultShortcode(props) {\n    console.warn(\"Component \" + name + \" was not imported, exported, or provided by MDXProvider as global scope\");\n    return mdx(\"div\", props);\n  };\n};\nvar Embed = makeShortcode(\"Embed\");\nvar Link = makeShortcode(\"Link\");\nvar layoutProps = {\n  _frontmatter: _frontmatter\n};\nvar MDXLayout = \"wrapper\";\nreturn function MDXContent(_ref) {\n  var components = _ref.components,\n    props = _objectWithoutProperties(_ref, _excluded);\n  return mdx(MDXLayout, _extends({}, layoutProps, props, {\n    components: components,\n    mdxType: \"MDXLayout\"\n  }), mdx(\"p\", null, \"To turn on and off my outside lights on a schedule I started with a simple GE switch with built in digital timer that you had to set by pushing buttons on the switch itself which wasn't very fun for the initial setup. \"), mdx(\"blockquote\", null, mdx(\"p\", {\n    parentName: \"blockquote\"\n  }, \"The GE switch worked fine for a little while, but every time there was a power outage it would lose it's programming which was very frustrating. That is what got me looking at Wi-Fi smart switches. \")), mdx(\"p\", null, \"After doing some research, I decided to try the \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://amzn.to/3633dET\"\n  }, \"TP-Link HS200 Kasa smart light switch\"), \" to control my outside house lights. Installation was straight forward like a typical switch with some additional steps required to connect it to my home Wi-Fi network and optionally pair the device with Alexa for voice control.\"), mdx(\"p\", null, \"The kasa app is designed very well and let's you schedule multiple on and off times on a 7 day programmable schedule. This has been working great for setting up my outdoor light timing and is easy to adjust when the seasons change and it gets darker earlier and so on.\"), mdx(\"p\", null, \"So I started with the one switch a couple years ago to control a side entry light and then decided to replace the switch controlling my front lights that was nested in a 4 gang switch plate. The wiring there was a little more complicated because the hot wires were daisy chained from switch to switch. \"), mdx(\"p\", null, \"You can learn how to install the HS200 in a multiple switch receptacle by watching the setup tutorial below. It also covers how to connect the HS200 to Alexa.\"), mdx(Embed, {\n    src: \"https://www.youtube.com/embed/kQSYotuwW2U\",\n    mdxType: \"Embed\"\n  }), mdx(\"p\", null, \"At the same time I've been slowly losing the battle, and sanity, of teaching my kids to turn their lights off when leaving their bedrooms since they were tall enough to reach them. I've surrendered and replaced their bedroom light switches with the kasa HS200 now as well so I can just shout \\\"Alexa turn off (child name here)'s lights!\\\" and am feeling pretty good about that.\"), mdx(\"p\", null, \"So I am up to 4 of them in my house. The longest one has been operating for about 2 years. They seem pretty reliable. If you are interested in buying one consider \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://amzn.to/3633dET\"\n  }, \"purchasing through my affiliate link\"), \" to help out my site.\"), mdx(\"p\", null, \"I would recommend segmenting smart switches like this onto their own Wi-Fi network with firewall rules restricting access to the rest of your network to be on the safe side. You can learn more about that in my other post \", mdx(Link, {\n    to: \"/secure-iot-with-ubiquiti-vlan-firewall-rules/\",\n    mdxType: \"Link\"\n  }, \"securing your IoT devices with Ubiquiti VLANs\")));\n}\n;\nMDXContent.isMDXComponent = true;","excerpt":"To turn on and off my outside lights on a schedule I started with a simple GE switch with built in digital timer that you had to set by…","fields":{"slug":"/tplink-kasa-light-switch-hs200/","type":"posts"},"headings":[],"frontmatter":{"title":"TP Link HS200 wifi smart switch installation with kasa","description":"If you are tired of telling your kids to shut off the lights or like flexible outdoor light scheduling smart light switches are for you.","link":null,"date":"2020-01-16T00:00:00.000Z","tags":["tp-link","kasa","smart home","iot","post"],"draft":null,"hide":null,"cover":{"childImageSharp":{"fluid":{"base64":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAMCAYAAABiDJ37AAAACXBIWXMAABJ0AAASdAHeZh94AAADG0lEQVQozy2TW2gcZRTHBx99EAQpaAUfBBGUKijivX0q3rBaUaslaEkhkKqVgFrSglhNWx8i6nqJQWgCVXFtTJS4aWqTbFKbbsmtcTd1s2lik+3esjvZncnMzm13f56ddeBwvjPznf/3O+d8o6hZHU21sIwqnlOj4lWpP+1nz3HXocO8+nU3R34b5ORkhHPxBJeS1/nHMBjJpli3XZKGRSKZoqBbbGgWSiGrkc/oGJonguA6DcGW0BD3d37KSjZPRjfIbZqoZRvDq+A2tlD0akRzRaILcVTNJCVrJZ8psi6URdXGNivYtuyuQXN4lAfHzviJ9fzK/+bKN1vMqNWwqjXS2U3yagndLDMbjaPkhDB9fYNURiOVNwXb9kX2RSa5fagf3ZFDRMl0K5SFzpKW1H1dVDqEK3sdeVc3o+ygJKNRSpkc5cIGWjqNurzE4sgwh375iROxy5Rszxc0HE/oar6VHbchVJG4uIHnSbuE1pVYme37gb8HT5M4+zvpyARr40MMfNJOd9c3LKkFEaoKXc0XdGgQWa7n+2I6w+rPP5JdXsGUuFQnvBLsZXF4iPnBEHP9fUyJBQMBTv0aJCaT1ATPlOxNX7AmZTYILRG4NjNF5s8BUmMh7Hyh3nqUPa/s5uX9LexpfYen39jHC60HeK/zMy7Eov4QNmVIujRMk9KtahVbSjNth3qnZ8bHWBroQZ8/TyY8jKXmUbY89Ag333MvN2y9jYefe5ZoPE6hpHJ5eoK+ngAr/65hyphV0/an7fqHOL6/ODVHzwcHWT1zmtLUBLlIGGX79id4bdczdLW3MdvzHXNfHCfW+y1/dLxP1/OPcyUcpizJ6fUiM9PzTP51iYWFhNDCYjLLseYmvtq7i9VQED0WQfn4QDPnuzsZ+bCNz596krfvuIUjd2/l+52PMtq0m6ujo2iSvF40Wby6xvK1jKzLFK0KCbn0H7Xup+OlncT7T5GbFsJwoINgWzPHdjxA05YbefPWm3h3250E9r7IyZbXiV0Yl6uD/FoOmoxZl1rrPq/bJHJ5vjx+lLceu4/QicPM9/XyHwSOL6UtRaH9AAAAAElFTkSuQmCC","aspectRatio":1.6991150442477876,"src":"/static/584e48ce8f033528ca837fe779eb02c2/6caa6/hs200-kasa-switch-installation.png","srcSet":"/static/584e48ce8f033528ca837fe779eb02c2/a4b17/hs200-kasa-switch-installation.png 192w,\n/static/584e48ce8f033528ca837fe779eb02c2/1ef16/hs200-kasa-switch-installation.png 384w,\n/static/584e48ce8f033528ca837fe779eb02c2/6caa6/hs200-kasa-switch-installation.png 768w,\n/static/584e48ce8f033528ca837fe779eb02c2/c9fd7/hs200-kasa-switch-installation.png 857w","srcWebp":"/static/584e48ce8f033528ca837fe779eb02c2/25278/hs200-kasa-switch-installation.webp","srcSetWebp":"/static/584e48ce8f033528ca837fe779eb02c2/a278a/hs200-kasa-switch-installation.webp 192w,\n/static/584e48ce8f033528ca837fe779eb02c2/2474b/hs200-kasa-switch-installation.webp 384w,\n/static/584e48ce8f033528ca837fe779eb02c2/25278/hs200-kasa-switch-installation.webp 768w,\n/static/584e48ce8f033528ca837fe779eb02c2/150fe/hs200-kasa-switch-installation.webp 857w","sizes":"(max-width: 768px) 100vw, 768px","presentationWidth":768,"presentationHeight":453}}}}},{"id":"5130cb8b-f915-5bcd-8a78-cfadac019a3c","body":"var _excluded = [\"components\"];\nfunction _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }\nfunction _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }\nfunction _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }\n/* @jsxRuntime classic */\n/* @jsx mdx */\n\nvar _frontmatter = {\n  \"title\": \"Securing your IoT devices with Ubiquiti VLANs\",\n  \"cover\": \"./oneway.png\",\n  \"date\": \"2020-01-14T00:00:00.000Z\",\n  \"description\": \"IoT or smart home devices such as lights, plugs, and security cameras haven't had to best track record for security so before you let them in behind your firewall let's look at how to secure them using VLANs.\",\n  \"tags\": [\"networking\", \"smart home\", \"iot\", \"security\", \"ubiquiti\", \"post\"]\n};\nvar makeShortcode = function makeShortcode(name) {\n  return function MDXDefaultShortcode(props) {\n    console.warn(\"Component \" + name + \" was not imported, exported, or provided by MDXProvider as global scope\");\n    return mdx(\"div\", props);\n  };\n};\nvar Embed = makeShortcode(\"Embed\");\nvar layoutProps = {\n  _frontmatter: _frontmatter\n};\nvar MDXLayout = \"wrapper\";\nreturn function MDXContent(_ref) {\n  var components = _ref.components,\n    props = _objectWithoutProperties(_ref, _excluded);\n  return mdx(MDXLayout, _extends({}, layoutProps, props, {\n    components: components,\n    mdxType: \"MDXLayout\"\n  }), mdx(\"p\", null, \"Firewalls protect you from unsolicited connections from outside your network into your private network. The problem arises when a trusted device behind your network is compromised opening outside access to attackers into your LAN.\"), mdx(\"p\", null, \"Historically smart devices like IoT gadgets, security cameras, lights, and switches have had their share of security issues so you wouldn't want them sitting on the same network as your trusted computers.\"), mdx(\"blockquote\", null, mdx(\"p\", {\n    parentName: \"blockquote\"\n  }, \"Microsegmentation through VLANs can provide your smart IoT devices access to the internet without opening holes into your private network.\")), mdx(\"p\", null, \"Prosumer networking devices, such as those from Ubiquiti, allow you to configure VLANs\"), mdx(\"p\", null, \"Roughly the steps you will need to do are\"), mdx(\"ol\", null, mdx(\"li\", {\n    parentName: \"ol\"\n  }, mdx(\"p\", {\n    parentName: \"li\"\n  }, \"Create a new Corporate network and assign it a VLAN ID and IP Address Range.\")), mdx(\"li\", {\n    parentName: \"ol\"\n  }, mdx(\"p\", {\n    parentName: \"li\"\n  }, \"Create a new Wi-Fi network and associate it to that LAN. In addition, you can tag wired ports with the VLAN ID for wired devices.\")), mdx(\"li\", {\n    parentName: \"ol\"\n  }, mdx(\"p\", {\n    parentName: \"li\"\n  }, \"Create firewall rules that block access from your VLAN into your private network, but allow your private network to call into your VLAN.\"))), mdx(\"p\", null, \"This will allow your IoT devices access to the internet, but not your internal private network in case they become compromised.\"), mdx(\"p\", null, \"This is a very high level view and the full walk through is covered in this VLAN tutorial video below.\"), mdx(Embed, {\n    src: \"https://www.youtube.com/embed/qxBIMYBJM1I\",\n    mdxType: \"Embed\"\n  }), mdx(\"p\", null, \"If you are interested in looking more into Ubiquiti hardware, \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://amzn.to/3aDL8B4\"\n  }, \"you can order it here\"), \"\\nOther manufacturers such as Asus have instructions for setting this up on their equipment as well. \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://www.asus.com/support/FAQ/1034018/\"\n  }, \"Asus VLAN for BRT series\")), mdx(\"p\", null, \"For further reading checkout this awesome explanation from \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://robpickering.com/ubiquiti-configure-micro-segmentation-for-iot-devices/\"\n  }, \"Rob Pickering\")));\n}\n;\nMDXContent.isMDXComponent = true;","excerpt":"Firewalls protect you from unsolicited connections from outside your network into your private network. The problem arises when a trusted…","fields":{"slug":"/secure-iot-with-ubiquiti-vlan-firewall-rules/","type":"posts"},"headings":[],"frontmatter":{"title":"Securing your IoT devices with Ubiquiti VLANs","description":"IoT or smart home devices such as lights, plugs, and security cameras haven't had to best track record for security so before you let them in behind your firewall let's look at how to secure them using VLANs.","link":null,"date":"2020-01-14T00:00:00.000Z","tags":["networking","smart home","iot","security","ubiquiti","post"],"draft":null,"hide":null,"cover":{"childImageSharp":{"fluid":{"base64":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAANCAYAAACpUE5eAAAACXBIWXMAAAsTAAALEwEAmpwYAAADlklEQVQ4y02Sa0wbVBiGGzZECuVWYJTalnZQoVjKKIONggwYFB1UpFLkEmEMWWGbY2BY3QgLMYxkZJsjbuqMGhcSjWYYdTGZMYtRE7dkLiP+kSmo0awglHsvXObjkfnDk7w/vpOc5/ve7z2Suz/9htvjZXbBx+yij/klP4veAEu+VZZ9a6z41/AK+f6n9Qdge6aandlZpG/XkBAbi0KlJVImQ3Lnx3GmPEssrPhZ9q6Kx+v4AusEVh9qdW3jP4k6sMaDv2FiYpKWejv1tmLyc82Y80swWkqJjohAcuv2Xf6c8uAPbOD1BZibX9iU3x94CFld2wRvbAiSOEtLy/S7jtLyfAUH6iopLcpnZ0ERhh05RIeHI7nx5Sf8+vsfzMzOMevxMP3XDPfdbubm5lkTQL9fWF/xMn5vnMnJX5hy38f1UitPFVtotJdTYd1DhikTjUZYDpMieef8Ma6OXOCHO7eFZb+Ybg739DSLYhKfz4dHgN3TM6Khh58FdEo06+/p4FRnM8fb69mdY0K5LZ6I0EeJkIYiefNsJ9dGr3Dr5td8982nXP/iPa5fG+Hy8CBXr7zOpaFTXHzj0qbdf/c3OXGP3pfbOdZaQ+f+arJNacgjI4gR+4sKC0Nyur+Byxe6GB15jc9H3+bjD87x0ftnGXQ5+fBiP+f6jtDbe5KxsTG+v/ktN776jO62WqrLLLQ3VJGZridRqSQpWb8JlQycbGPwRBPnB7p4a/gVhoeO4up6AWerg4MvOqirfRpbaSENNRW07bdxus/Ju0M92K1P0lRTKYApKFUqVGKHm6H0uZo5caiOumetPGevpLnRwd6SPLItO0jbZUJvSmVvfg6Fu804bGW86urgjOsgJaKuLi9Cr1OhUCiIj08gSipCOd7poMtZSZW1AMuuXHLMRvIsWVj25FBaXkCmOZ3HdRoy0lLQqpWYMww4xXepLM6j1VEh7pPRPRZDmjpSAEUoh1r2UVWaQXaqGm1CLJpt0SI1OcY0LcZULQa9jjh5DFqVkoS4GJLVCnqcDQx0t9J9oJYssUN9kniXGI5MJC3pPtJIXoYOa76BfSUG7DYzWUY1cdEytIoYFPJI5FEy4mMiBVCONjGWQvFVjClJKESDWHEXrVQTJJURvGUrksNOO031hRxusdKxvxBnS4GoxQ4NWspy0zFsV2HSJ5H9hB5dkkZYTifTkExoyCMEBwezNSSELUJBQUFIQ6X8A6u/XxXXFbc6AAAAAElFTkSuQmCC","aspectRatio":1.5,"src":"/static/a456087d8f4a998c71e0255d9330ee8e/6caa6/oneway.png","srcSet":"/static/a456087d8f4a998c71e0255d9330ee8e/a4b17/oneway.png 192w,\n/static/a456087d8f4a998c71e0255d9330ee8e/1ef16/oneway.png 384w,\n/static/a456087d8f4a998c71e0255d9330ee8e/6caa6/oneway.png 768w,\n/static/a456087d8f4a998c71e0255d9330ee8e/3f078/oneway.png 1152w,\n/static/a456087d8f4a998c71e0255d9330ee8e/c6da2/oneway.png 1350w","srcWebp":"/static/a456087d8f4a998c71e0255d9330ee8e/25278/oneway.webp","srcSetWebp":"/static/a456087d8f4a998c71e0255d9330ee8e/a278a/oneway.webp 192w,\n/static/a456087d8f4a998c71e0255d9330ee8e/2474b/oneway.webp 384w,\n/static/a456087d8f4a998c71e0255d9330ee8e/25278/oneway.webp 768w,\n/static/a456087d8f4a998c71e0255d9330ee8e/e7b8c/oneway.webp 1152w,\n/static/a456087d8f4a998c71e0255d9330ee8e/44d42/oneway.webp 1350w","sizes":"(max-width: 768px) 100vw, 768px","presentationWidth":768,"presentationHeight":512}}}}},{"id":"e96e8471-aff8-5735-a915-4f8c1082dbd2","body":"var _excluded = [\"components\"];\nfunction _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }\nfunction _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }\nfunction _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }\n/* @jsxRuntime classic */\n/* @jsx mdx */\n\nvar _frontmatter = {\n  \"title\": \"Review and installation of TP-Link KL430 smart light strip\",\n  \"cover\": \"./kl430-light-strip.jpg\",\n  \"date\": \"2020-01-13T00:00:00.000Z\",\n  \"description\": \"I needed some under cabinet lighting that didn't require a switch so I gave this TP-Link KL430 smart light strip a try.\",\n  \"tags\": [\"tp-link\", \"kasa\", \"smart lights\", \"smart home\", \"iot\", \"post\"]\n};\nvar makeShortcode = function makeShortcode(name) {\n  return function MDXDefaultShortcode(props) {\n    console.warn(\"Component \" + name + \" was not imported, exported, or provided by MDXProvider as global scope\");\n    return mdx(\"div\", props);\n  };\n};\nvar Embed = makeShortcode(\"Embed\");\nvar layoutProps = {\n  _frontmatter: _frontmatter\n};\nvar MDXLayout = \"wrapper\";\nreturn function MDXContent(_ref) {\n  var components = _ref.components,\n    props = _objectWithoutProperties(_ref, _excluded);\n  return mdx(MDXLayout, _extends({}, layoutProps, props, {\n    components: components,\n    mdxType: \"MDXLayout\"\n  }), mdx(\"p\", null, \"My office is \", mdx(\"em\", {\n    parentName: \"p\"\n  }, \"way too dark\"), \" and some recently added cabinets didn't help. I wanted to add strip lighting under the cabinets or behind the TV, but I didn't want to deal with a physical switch and a lot of the strip lights tend to be of dubious quality which left me procrastinating the decision.\"), mdx(\"p\", null, \"In December TP-Link released the \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://amzn.to/376poM1\"\n  }, \"KL430 smart light strip\"), \". I have been happy with their HS200 switches and smart plugs along with their Kasa app so I stopped debating and purchased it immediately to go behind the TV in the office.\"), mdx(\"p\", null, \"I found the strip itself to be a bit shorter than I'd hoped. It didn't wrap the entire perimeter of a 42\\\" TV (settled on left, bottom, and right sides). However, the light itself is extremely bright, the color representation is good, and the lighting effects are cool.\"), mdx(\"h2\", {\n    \"id\": \"specifications\"\n  }, \"Specifications\"), mdx(\"ul\", null, mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"6.6ft of light strip (2m)\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Max length of 33ft with \", mdx(\"a\", {\n    parentName: \"li\",\n    \"href\": \"https://amzn.to/2Z4XYos\"\n  }, \"KL430e light strip extenders\"), \" at 3.3ft (1m) per extender\")), mdx(\"h2\", {\n    \"id\": \"kasa-updates-as-of-5152020\"\n  }, \"Kasa updates (as of 5/15/2020)\"), mdx(\"p\", null, \"TP Link has been regularly updating their control app which recently added the ability to modify the lighting effects it comes with to control color, speed, and timing. No gentle wakeup or sunrise or sunset effects have been added.\"), mdx(\"h2\", {\n    \"id\": \"unboxing-setup-and-review\"\n  }, \"Unboxing, setup, and review\"), mdx(\"p\", null, \"See my unboxing and installation experience with the TP-Link KL430 light strip below. The video covers the features and how to connect the Kasa app to Alexa for control in your smart home.\"), mdx(\"p\", null, \"If you are interested in buying one consider \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://amzn.to/376poM1\"\n  }, \"purchasing through my affiliate link\"), \" to help out my site.\"), mdx(Embed, {\n    src: \"https://www.youtube.com/embed/stpOW9QZdB0\",\n    mdxType: \"Embed\"\n  }));\n}\n;\nMDXContent.isMDXComponent = true;","excerpt":"My office is  way too dark  and some recently added cabinets didn't help. I wanted to add strip lighting under the cabinets or behind the TV…","fields":{"slug":"/tp-link-kl430-light-strip/","type":"posts"},"headings":[{"value":"Specifications","depth":2},{"value":"Kasa updates (as of 5/15/2020)","depth":2},{"value":"Unboxing, setup, and review","depth":2}],"frontmatter":{"title":"Review and installation of TP-Link KL430 smart light strip","description":"I needed some under cabinet lighting that didn't require a switch so I gave this TP-Link KL430 smart light strip a try.","link":null,"date":"2020-01-13T00:00:00.000Z","tags":["tp-link","kasa","smart lights","smart home","iot","post"],"draft":null,"hide":null,"cover":{"childImageSharp":{"fluid":{"base64":"data:image/jpeg;base64,/9j/2wBDAAMCAgMCAgMDAwMEAwMEBQgFBQQEBQoHBwYIDAoMDAsKCwsNDhIQDQ4RDgsLEBYQERMUFRUVDA8XGBYUGBIUFRT/2wBDAQMEBAUEBQkFBQkUDQsNFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBT/wgARCAAIABQDASIAAhEBAxEB/8QAFwABAAMAAAAAAAAAAAAAAAAAAAQGB//EABYBAQEBAAAAAAAAAAAAAAAAAAQBAv/aAAwDAQACEAMQAAABwaxh0zxmf//EABkQAAIDAQAAAAAAAAAAAAAAAAMEAQIFEv/aAAgBAQABBQKCr2AknntjvmKdf//EABkRAQACAwAAAAAAAAAAAAAAAAEAAhESUv/aAAgBAwEBPwGrqTD0z//EABoRAAICAwAAAAAAAAAAAAAAAAABAgMRMlH/2gAIAQIBAT8Bl0d2Xqj/xAAhEAABAwIHAQAAAAAAAAAAAAABAAIDESEEEiMxQWKRwf/aAAgBAQAGPwKMGPO4b0KaHSOjxHLBe3i0pZQ3s2nxf//EABoQAQADAQEBAAAAAAAAAAAAAAEAESExQYH/2gAIAQEAAT8hoQnSl5OyiGaWz1sKgh0VX9E//9oADAMBAAIAAwAAABB77//EABoRAQEAAgMAAAAAAAAAAAAAAAERAEFhcYH/2gAIAQMBAT8QoMtJ1dnJrBSge5//xAAbEQEAAgIDAAAAAAAAAAAAAAABAGERUXGh4f/aAAgBAgEBPxBomjnmmoSPX7P/xAAaEAEBAAMBAQAAAAAAAAAAAAABEQAhMVGR/9oACAEBAAE/EB+hByolk+mHRVA/QYtG++czR3j25ghJNzt8z//Z","aspectRatio":2.430379746835443,"src":"/static/f8bca5110918921c0c3bb0a0bbb3d4ef/60a5f/kl430-light-strip.jpg","srcSet":"/static/f8bca5110918921c0c3bb0a0bbb3d4ef/3e5eb/kl430-light-strip.jpg 192w,\n/static/f8bca5110918921c0c3bb0a0bbb3d4ef/2880b/kl430-light-strip.jpg 384w,\n/static/f8bca5110918921c0c3bb0a0bbb3d4ef/60a5f/kl430-light-strip.jpg 768w,\n/static/f8bca5110918921c0c3bb0a0bbb3d4ef/d9e57/kl430-light-strip.jpg 1152w,\n/static/f8bca5110918921c0c3bb0a0bbb3d4ef/72798/kl430-light-strip.jpg 1464w","srcWebp":"/static/f8bca5110918921c0c3bb0a0bbb3d4ef/25278/kl430-light-strip.webp","srcSetWebp":"/static/f8bca5110918921c0c3bb0a0bbb3d4ef/a278a/kl430-light-strip.webp 192w,\n/static/f8bca5110918921c0c3bb0a0bbb3d4ef/2474b/kl430-light-strip.webp 384w,\n/static/f8bca5110918921c0c3bb0a0bbb3d4ef/25278/kl430-light-strip.webp 768w,\n/static/f8bca5110918921c0c3bb0a0bbb3d4ef/e7b8c/kl430-light-strip.webp 1152w,\n/static/f8bca5110918921c0c3bb0a0bbb3d4ef/eeaf7/kl430-light-strip.webp 1464w","sizes":"(max-width: 768px) 100vw, 768px","presentationWidth":768,"presentationHeight":315}}}}},{"id":"cd105a83-b583-5226-89ec-a58a912ddaba","body":"var _excluded = [\"components\"];\nfunction _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }\nfunction _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }\nfunction _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }\n/* @jsxRuntime classic */\n/* @jsx mdx */\n\nvar _frontmatter = {\n  \"title\": \"Getting started with NightwatchJS\",\n  \"cover\": \"./nightwatchjsBanner.png\",\n  \"date\": \"2020-01-12T00:00:00.000Z\",\n  \"description\": \"I've been looking at newer browser automation frameworks that might also be written in NodeJS and Nightwatch was a match.\",\n  \"tags\": [\"quality assurance\", \"qa\", \"software testing\", \"nightwatchjs\", \"post\"]\n};\nvar makeShortcode = function makeShortcode(name) {\n  return function MDXDefaultShortcode(props) {\n    console.warn(\"Component \" + name + \" was not imported, exported, or provided by MDXProvider as global scope\");\n    return mdx(\"div\", props);\n  };\n};\nvar Embed = makeShortcode(\"Embed\");\nvar layoutProps = {\n  _frontmatter: _frontmatter\n};\nvar MDXLayout = \"wrapper\";\nreturn function MDXContent(_ref) {\n  var components = _ref.components,\n    props = _objectWithoutProperties(_ref, _excluded);\n  return mdx(MDXLayout, _extends({}, layoutProps, props, {\n    components: components,\n    mdxType: \"MDXLayout\"\n  }), mdx(\"p\", null, \"I've been looking at newer browser automation frameworks that might also be written in NodeJS and Nightwatch was a match. So far I've found NightwatchJS to be very quick to get up and running. \"), mdx(\"blockquote\", null, mdx(\"p\", {\n    parentName: \"blockquote\"\n  }, \"NightwatchJS offers built in, easy to use, support for the page object model.\")), mdx(\"p\", null, \"I am a big fan of using the page object model and their use of basically a JSON object to hold the page elements with their selectors seems very organized and natural to me. Below is an example I used for automating a quick test for the Google home page.\"), mdx(\"p\", null, mdx(\"strong\", {\n    parentName: \"p\"\n  }, mdx(\"inlineCode\", {\n    parentName: \"strong\"\n  }, \"Page object model example:\"))), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-js\"\n  }, \"module.exports = {\\n    url: 'http://www.google.com',\\n    elements: {\\n        searchBar: {\\n            selector: 'input[type=text]'\\n        },\\n        submit: {\\n            selector: 'input[name=btnK]'\\n        }\\n    }\\n}\\n\")), mdx(\"p\", null, mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://github.com/reallymello/nightwatchExamples\"\n  }, \"Nightwatch example test starter on my GitHub\")), mdx(\"p\", null, \"I go through the Nightwatch installation and creation of my first test in the NightwatchJS tutorial below.\"), mdx(Embed, {\n    src: \"https://www.youtube.com/embed/6Ufg6pPNVTs\",\n    mdxType: \"Embed\"\n  }));\n}\n;\nMDXContent.isMDXComponent = true;","excerpt":"I've been looking at newer browser automation frameworks that might also be written in NodeJS and Nightwatch was a match. So far I've found…","fields":{"slug":"/nightwatchjs-getting-started/","type":"posts"},"headings":[],"frontmatter":{"title":"Getting started with NightwatchJS","description":"I've been looking at newer browser automation frameworks that might also be written in NodeJS and Nightwatch was a match.","link":null,"date":"2020-01-12T00:00:00.000Z","tags":["quality assurance","qa","software testing","nightwatchjs","post"],"draft":null,"hide":null,"cover":{"childImageSharp":{"fluid":{"base64":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAIAAADwazoUAAAACXBIWXMAAAsTAAALEwEAmpwYAAABdklEQVQoz5WSy0rDQBSG+wAWL8lck8wkmdpqbk0Ta2m1alUUFUFfQPAVfAV3utWdW+kLdO+yex9CXLl165mkLQVBKvwMZ4bznfkzfyqSEZA7lVxMLqeexSrlRnAmOJecLggD6du8gCmqoWodLUm8BiNn9//hZQLbGIU1//nhfvjydHG4h1aWXXABXhhxqF7L7rIWc7Y1zDHq1u3x6PXj/e32ciBRtSFozcYNSQIXK0tjLid1B8Nh4BKPa01gT4ieQuPR8Pvr8zpXqVrbj9FpZh4k5vmW0Q/NQaLrs9xIFTpOjcgjLUV8Cx6MV3yL9Xf7d1edx5teJ41hfOjik9Q8apqRh/N1BNoJzM4GShVOfNz0CaiAWcXjrOFJmyBqrMIwMCM5VzbddLlkTHLmUHgCWItQqPasP7uMCtxnUdhOk3YzzqJgO01AeRK3oiiLg26ewWE3b8EFguL5IDRsY9NChkOQpFhM5RWBl/Ws+3daGp5vkv/5yX4A+a5ataOUh5AAAAAASUVORK5CYII=","aspectRatio":1.7777777777777777,"src":"/static/981b5554d4aa826892d1758608eaf0bb/6caa6/nightwatchjsBanner.png","srcSet":"/static/981b5554d4aa826892d1758608eaf0bb/a4b17/nightwatchjsBanner.png 192w,\n/static/981b5554d4aa826892d1758608eaf0bb/1ef16/nightwatchjsBanner.png 384w,\n/static/981b5554d4aa826892d1758608eaf0bb/6caa6/nightwatchjsBanner.png 768w,\n/static/981b5554d4aa826892d1758608eaf0bb/3f078/nightwatchjsBanner.png 1152w,\n/static/981b5554d4aa826892d1758608eaf0bb/bf181/nightwatchjsBanner.png 1346w","srcWebp":"/static/981b5554d4aa826892d1758608eaf0bb/25278/nightwatchjsBanner.webp","srcSetWebp":"/static/981b5554d4aa826892d1758608eaf0bb/a278a/nightwatchjsBanner.webp 192w,\n/static/981b5554d4aa826892d1758608eaf0bb/2474b/nightwatchjsBanner.webp 384w,\n/static/981b5554d4aa826892d1758608eaf0bb/25278/nightwatchjsBanner.webp 768w,\n/static/981b5554d4aa826892d1758608eaf0bb/e7b8c/nightwatchjsBanner.webp 1152w,\n/static/981b5554d4aa826892d1758608eaf0bb/89bf0/nightwatchjsBanner.webp 1346w","sizes":"(max-width: 768px) 100vw, 768px","presentationWidth":768,"presentationHeight":434}}}}},{"id":"b1afb81e-457e-598b-9e2c-2484bd7f35fa","body":"var _excluded = [\"components\"];\nfunction _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }\nfunction _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }\nfunction _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }\n/* @jsxRuntime classic */\n/* @jsx mdx */\n\nvar _frontmatter = {\n  \"title\": \"Getting faster iPhone and iOS Wi-Fi speeds with Unifi UAP-AC-HD access point\",\n  \"cover\": \"./uap-ac-hd.jpg\",\n  \"date\": \"2020-01-10T00:00:00.000Z\",\n  \"description\": \"I realized my iPhone and iPad didn't seem to be transferring large files very fast so I set out to see if upgrading my Ubiquiti access point would help.\",\n  \"tags\": [\"ubiquiti\", \"wifi\", \"networking\", \"post\"]\n};\nvar makeShortcode = function makeShortcode(name) {\n  return function MDXDefaultShortcode(props) {\n    console.warn(\"Component \" + name + \" was not imported, exported, or provided by MDXProvider as global scope\");\n    return mdx(\"div\", props);\n  };\n};\nvar Embed = makeShortcode(\"Embed\");\nvar layoutProps = {\n  _frontmatter: _frontmatter\n};\nvar MDXLayout = \"wrapper\";\nreturn function MDXContent(_ref) {\n  var components = _ref.components,\n    props = _objectWithoutProperties(_ref, _excluded);\n  return mdx(MDXLayout, _extends({}, layoutProps, props, {\n    components: components,\n    mdxType: \"MDXLayout\"\n  }), mdx(\"p\", null, \"Over the winter break I got to spend some quality time with my electronics. I noticed that transferring large files from my NAS using my iPhone or iPad seemed a bit sluggish given their internal modems are supposed to be rated very fast though it was hard to find the exact mbps specifications online, but I was only getting 250-290mbps or around 36mb/s.\"), mdx(\"p\", null, \"I used this as an excuse to upgrade my Ubiquiti UAP-AC-Pro access point to the UAP-AC-HD which has more antennas and supports Wave2. I tested the before and after and found both before and after to have nearly the same results. \", mdx(\"em\", {\n    parentName: \"p\"\n  }, \"But keep reading there is more\")), mdx(\"blockquote\", null, mdx(\"p\", {\n    parentName: \"blockquote\"\n  }, \"Upgrading to the UAP-AC-HD made no speed difference for an iPhone 8 or 2018 iPad Pro.\")), mdx(\"p\", null, \"I go over the setup of the UAP-AC-HD and comparison to the UAP-AC-Pro in this video below where you can see the before and after.\"), mdx(Embed, {\n    src: \"https://www.youtube.com/embed/q83-EwJYreM\",\n    mdxType: \"Embed\"\n  }), mdx(\"h2\", {\n    \"id\": \"but-there-was-a-solution\"\n  }, \"But there was a solution.\"), mdx(\"p\", null, \"It turns out there is a channel width setting that, by default, ships at lower throughput channel size. This is what was limiting my throughput on both the \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://amzn.to/2NFxR0X\"\n  }, \"UAP-AC-Pro\"), \" and \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://amzn.to/2v1kwcG\"\n  }, \"UAP-AC-HD\"), \" access points. This is the VHT setting. It comes as VHT40, but can be configured to VHT80 to unlock higher speeds.\"), mdx(\"p\", null, \"There are considerations to running at the wider channel width though. This uses more spectrum. In installations with close neighbors with Wi-Fi it may be harder to find a channel on VHT80 without interference. In addition, when you have multiple access points it may be more challenging to find non-overlapping channels. This is why VHT40 is default. It allows for denser AP installs to blanket coverage at the lower more consistent speed which is fine for most users sending email and browsing the web etc.\"), mdx(\"p\", null, \"The VHT80 setting works great for larger file transfers over Wi-Fi in non-congested installs. See my Wi-FI results below\"), mdx(Embed, {\n    src: \"https://www.youtube.com/embed/jtK_lIw8We4\",\n    mdxType: \"Embed\"\n  }), mdx(\"p\", null, \"I was able to get to roughly 560mbps with the wider channel width of VHT80 on an iPhone 8.\"));\n}\n;\nMDXContent.isMDXComponent = true;","excerpt":"Over the winter break I got to spend some quality time with my electronics. I noticed that transferring large files from my NAS using my…","fields":{"slug":"/ubiquiti-access-point-speed-tuning-for-iphone-and-ios/","type":"posts"},"headings":[{"value":"But there was a solution.","depth":2}],"frontmatter":{"title":"Getting faster iPhone and iOS Wi-Fi speeds with Unifi UAP-AC-HD access point","description":"I realized my iPhone and iPad didn't seem to be transferring large files very fast so I set out to see if upgrading my Ubiquiti access point would help.","link":null,"date":"2020-01-10T00:00:00.000Z","tags":["ubiquiti","wifi","networking","post"],"draft":null,"hide":null,"cover":{"childImageSharp":{"fluid":{"base64":"data:image/jpeg;base64,/9j/2wBDAAMCAgMCAgMDAwMEAwMEBQgFBQQEBQoHBwYIDAoMDAsKCwsNDhIQDQ4RDgsLEBYQERMUFRUVDA8XGBYUGBIUFRT/2wBDAQMEBAUEBQkFBQkUDQsNFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBT/wgARCAAKABQDASIAAhEBAxEB/8QAGQAAAgMBAAAAAAAAAAAAAAAAAAcDBAUG/8QAFQEBAQAAAAAAAAAAAAAAAAAAAAH/2gAMAwEAAhADEAAAAVt16dktsGeH/8QAHRAAAgEEAwAAAAAAAAAAAAAAAgMBAAQFBhETFf/aAAgBAQABBQLyE9OQ13GL087RIzLC4J7STX//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/AT//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/AT//xAAlEAACAAQDCQAAAAAAAAAAAAABAgADESEEEjEFEBMUIiMzgYL/2gAIAQEABj8CBmJy7LhhNXNfimsbOxQ7OIZxnnalq1qPUeevzC9RtpeElGYxlqSVQmw3f//EAB0QAAICAwADAAAAAAAAAAAAAAERACExUXFBobH/2gAIAQEAAT8hFIPRWhDrFH1DGBFMGSGsK0opAfjfYSw6rRyFd+BJymQPEc//2gAMAwEAAgADAAAAEPPv/8QAFREBAQAAAAAAAAAAAAAAAAAAARD/2gAIAQMBAT8QSf/EABURAQEAAAAAAAAAAAAAAAAAAAEQ/9oACAECAQE/ECf/xAAbEAEBAQADAQEAAAAAAAAAAAABESEAMVFBcf/aAAgBAQABPxCw6B5LArDiRi/cpKZ6VAQWJE0vbwmJa4mKCoioDnVnzg4AoKzevB+c3ZXlAGxP1DeV68//2Q==","aspectRatio":1.92,"src":"/static/1a4efcd28a43f95d5fe2b3ccabcb4478/60a5f/uap-ac-hd.jpg","srcSet":"/static/1a4efcd28a43f95d5fe2b3ccabcb4478/3e5eb/uap-ac-hd.jpg 192w,\n/static/1a4efcd28a43f95d5fe2b3ccabcb4478/2880b/uap-ac-hd.jpg 384w,\n/static/1a4efcd28a43f95d5fe2b3ccabcb4478/60a5f/uap-ac-hd.jpg 768w,\n/static/1a4efcd28a43f95d5fe2b3ccabcb4478/d9e57/uap-ac-hd.jpg 1152w,\n/static/1a4efcd28a43f95d5fe2b3ccabcb4478/0ff54/uap-ac-hd.jpg 1200w","srcWebp":"/static/1a4efcd28a43f95d5fe2b3ccabcb4478/25278/uap-ac-hd.webp","srcSetWebp":"/static/1a4efcd28a43f95d5fe2b3ccabcb4478/a278a/uap-ac-hd.webp 192w,\n/static/1a4efcd28a43f95d5fe2b3ccabcb4478/2474b/uap-ac-hd.webp 384w,\n/static/1a4efcd28a43f95d5fe2b3ccabcb4478/25278/uap-ac-hd.webp 768w,\n/static/1a4efcd28a43f95d5fe2b3ccabcb4478/e7b8c/uap-ac-hd.webp 1152w,\n/static/1a4efcd28a43f95d5fe2b3ccabcb4478/9000d/uap-ac-hd.webp 1200w","sizes":"(max-width: 768px) 100vw, 768px","presentationWidth":768,"presentationHeight":402}}}}}]},"allSitePage":{"nodes":[{"path":"/dev-404-page/"},{"path":"/tags/quality-assurance/"},{"path":"/tags/software-testing/"},{"path":"/tags/nightwatchjs/"},{"path":"/tags/typescript/"},{"path":"/tags/nightwatch/"},{"path":"/tags/selenium/"},{"path":"/tags/post/"},{"path":"/tags/link/"},{"path":"/tags/email/"},{"path":"/tags/visual-testing/"},{"path":"/tags/networking/"},{"path":"/tags/smart-home/"},{"path":"/tags/unifi/"},{"path":"/tags/ubiquiti/"},{"path":"/tags/switchbot/"},{"path":"/tags/iot/"},{"path":"/tags/tesla/"},{"path":"/tags/model-y/"},{"path":"/tags/floor-mat/"},{"path":"/tags/accessories/"},{"path":"/tags/power-station/"},{"path":"/tags/power/"},{"path":"/tags/battery/"},{"path":"/tags/water/"},{"path":"/tags/wifi/"},{"path":"/tags/leak-detection/"},{"path":"/tags/pool/"},{"path":"/tags/pentair/"},{"path":"/tags/camera/"},{"path":"/tags/amcrest/"},{"path":"/tags/testrail/"},{"path":"/tags/accessibility-testing/"},{"path":"/tags/tp-link/"},{"path":"/tags/kasa/"},{"path":"/tags/smart-lights/"},{"path":"/tags/saucelabs/"},{"path":"/tags/qa/"},{"path":"/tags/projector/"},{"path":"/tags/meme/"},{"path":"/tags/blog/"},{"path":"/tags/teckin/"},{"path":"/tags/smart-life/"},{"path":"/tags/photography/"},{"path":"/tags/drone/"},{"path":"/tags/ecobee/"},{"path":"/tags/database-testing/"},{"path":"/tags/yale/"},{"path":"/tags/nooie/"},{"path":"/tags/security/"},{"path":"/links/nightwatch-webinar-vscode-a11y/"},{"path":"/links/nightwatch-typescript-release/"},{"path":"/links/pto-fundraiser/"},{"path":"/nightwatch-typescript-custom-command-extensibility/"},{"path":"/best-websites-for-practicing-test-automation/"},{"path":"/api-testing-with-nightwatch-supertest/"},{"path":"/testing-software-with-decision-tables/"},{"path":"/email-testing-nightwatch-mailtrap/"},{"path":"/visual-regression-testing/"},{"path":"/using-nightwatch-with-typescript/"},{"path":"/speed-testing-ubiquiti-unifi-wifi-6-pro-vs-uap-ac-pro/"},{"path":"/unifi-talk-utp-touch-review/"},{"path":"/switchbot-door-contact-and-motion-sensor-review/"},{"path":"/tesla-model-y-accessories-guide/"},{"path":"/oupes-portable-power-station-generator-vs-jackery/"},{"path":"/flume2-wifi-leak-meter-review/"},{"path":"/calculate-gph-flow-rate-pool-pump-savings/"},{"path":"/amcrest-4k-webcam-setup-review/"},{"path":"/testrail-with-nightwatchjs/"},{"path":"/nightwatch-global-environment-variables/"},{"path":"/implementing-a-minimum-accessibility-test-plan/"},{"path":"/tplink-kl50-dimmable-filament-smart-bulb-review/"},{"path":"/how-to-use-nightwatch-with-saucelabs/"},{"path":"/smart-switch-no-neutral-using-switchbot/"},{"path":"/amcrest-prostream-awc2198-usb-webcam-review/"},{"path":"/south-florida-test-meetup-nightwatch-js/"},{"path":"/crenova-xpe660-led-projector-review/"},{"path":"/amcrest-awc195-b-hd-webcam-1080p-review/"},{"path":"/nightwatch-page-object-model-with-commands/"},{"path":"/amcrest-hd-webcam-1080p-review/"},{"path":"/how-lockdown-made-me-a-meme/"},{"path":"/teckin-outdoor-smart-plug-ss31/"},{"path":"/ecobee-2-years-hands-on-review/"},{"path":"/database-testing-with-nightwatchjs/"},{"path":"/yale-smart-door-lock-installation/"},{"path":"/nooie-aurora-smart-light-review/"},{"path":"/accessibility-testing-with-nightwatchjs/"},{"path":"/unifi-cloud-key-fix/"},{"path":"/tplink-kasa-light-switch-hs200/"},{"path":"/secure-iot-with-ubiquiti-vlan-firewall-rules/"},{"path":"/tp-link-kl430-light-strip/"},{"path":"/nightwatchjs-getting-started/"},{"path":"/ubiquiti-access-point-speed-tuning-for-iphone-and-ios/"},{"path":"/404/"},{"path":"/tags/"},{"path":"/404.html"},{"path":"/"},{"path":"/smart-home-iot/"},{"path":"/software-testing/"}]}}}